summaryrefslogtreecommitdiff
path: root/indra/llmath
diff options
context:
space:
mode:
authorBrad Kittenbrink <brad@lindenlab.com>2007-05-23 21:17:34 +0000
committerBrad Kittenbrink <brad@lindenlab.com>2007-05-23 21:17:34 +0000
commit029130bf9c76139fa836117987b60e801ac7ec7c (patch)
treeee320f7737ad3edc74a2401b5bd20a027b670487 /indra/llmath
parent0aac2f674e4bd2fc73025ec8b649739cf7be3e4c (diff)
svn merge svn+ssh://svn.lindenlab.com/svn/linden/release@62339 svn+ssh://svn.lindenlab.com/svn/linden/branches/release-candidate62341 -> release
Diffstat (limited to 'indra/llmath')
-rw-r--r--indra/llmath/llvolume.cpp278
-rw-r--r--indra/llmath/llvolume.h44
2 files changed, 282 insertions, 40 deletions
diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp
index 20d07064e3..c779a8b714 100644
--- a/indra/llmath/llvolume.cpp
+++ b/indra/llmath/llvolume.cpp
@@ -434,9 +434,18 @@ LLProfile::Face* LLProfile::addHole(BOOL flat, F32 sides, F32 offset, F32 box_ho
return face;
}
-BOOL LLProfile::generate(BOOL path_open,F32 detail, S32 split)
+
+F32 next_power_of_two(F32 value)
{
- if (!mDirty)
+ S32 power = (S32)llceil((F32)log((double)value)/(F32)log(2.0));
+ return pow(2.0f, power);
+}
+
+
+
+BOOL LLProfile::generate(BOOL path_open,F32 detail, S32 split, BOOL is_sculpted)
+{
+ if ((!mDirty) && (!is_sculpted))
{
return FALSE;
}
@@ -572,10 +581,14 @@ BOOL LLProfile::generate(BOOL path_open,F32 detail, S32 split)
circle_detail = llceil(circle_detail / 4.0f) * 4.0f;
}
}
+
+ S32 sides = (S32)circle_detail;
+
+ if (is_sculpted)
+ sides = (S32)next_power_of_two((F32)sides);
+
+ genNGon(sides);
- //llinfos << "(CIRCLE) detail: " << detail << "; genNGon("
- // << llfloor(circle_detail) << ")" << llendl;
- genNGon(llfloor(circle_detail));
if (path_open)
{
addCap (LL_FACE_PATH_BEGIN);
@@ -824,7 +837,7 @@ BOOL LLProfileParams::importLegacyStream(std::istream& input_stream)
}
else
{
- llwarns << "unknown keyword " << keyword << " in profile import" << llendl;
+ llwarns << "unknown keyword " << keyword << " in profile import" << llendl;
}
}
@@ -1054,9 +1067,9 @@ const LLVector2 LLPathParams::getEndScale() const
return end_scale;
}
-BOOL LLPath::generate(F32 detail, S32 split)
+BOOL LLPath::generate(F32 detail, S32 split, BOOL is_sculpted)
{
- if (!mDirty)
+ if ((!mDirty) && (!is_sculpted))
{
return FALSE;
}
@@ -1111,7 +1124,13 @@ BOOL LLPath::generate(F32 detail, S32 split)
{
// Increase the detail as the revolutions and twist increase.
F32 twist_mag = fabs(mParams.getTwistBegin() - mParams.getTwist());
- genNGon(llfloor(llfloor((MIN_DETAIL_FACES * detail + twist_mag * 3.5f * (detail-0.5f))) * mParams.getRevolutions()));
+
+ S32 sides = (S32)llfloor(llfloor((MIN_DETAIL_FACES * detail + twist_mag * 3.5f * (detail-0.5f))) * mParams.getRevolutions());
+
+ if (is_sculpted)
+ sides = (S32)next_power_of_two((F32)sides);
+
+ genNGon(sides);
}
break;
@@ -1174,7 +1193,7 @@ BOOL LLPath::generate(F32 detail, S32 split)
return TRUE;
}
-BOOL LLDynamicPath::generate(F32 detail, S32 split)
+BOOL LLDynamicPath::generate(F32 detail, S32 split, BOOL is_sculpted)
{
mOpen = TRUE; // Draw end caps
if (getPathLength() == 0)
@@ -1535,13 +1554,15 @@ LLProfile::~LLProfile()
}
-S32 LLVolume::mNumMeshPoints = 0;
+S32 LLVolume::sNumMeshPoints = 0;
LLVolume::LLVolume(const LLVolumeParams &params, const F32 detail, const BOOL generate_single_face, const BOOL is_unique) : mParams(params)
{
mUnique = is_unique;
mFaceMask = 0x0;
mDetail = detail;
+ mSculptLevel = -2;
+
// set defaults
if (mParams.getPathParams().getCurveType() == LL_PCODE_PATH_FLEXIBLE)
{
@@ -1558,7 +1579,10 @@ LLVolume::LLVolume(const LLVolumeParams &params, const F32 detail, const BOOL ge
mGenerateSingleFace = generate_single_face;
generate();
- createVolumeFaces();
+ if (mParams.getSculptID().isNull())
+ {
+ createVolumeFaces();
+ }
}
void LLVolume::regen()
@@ -1569,7 +1593,7 @@ void LLVolume::regen()
LLVolume::~LLVolume()
{
- mNumMeshPoints -= mMesh.size();
+ sNumMeshPoints -= mMesh.size();
delete mPathp;
delete mProfilep;
delete[] mVolumeFaces;
@@ -1620,9 +1644,9 @@ BOOL LLVolume::generate()
if (regenPath || regenProf )
{
- mNumMeshPoints -= mMesh.size();
+ sNumMeshPoints -= mMesh.size();
mMesh.resize(mProfilep->mProfile.size() * mPathp->mPath.size());
- mNumMeshPoints += mMesh.size();
+ sNumMeshPoints += mMesh.size();
S32 s = 0, t=0;
S32 sizeS = mPathp->mPath.size();
@@ -1752,6 +1776,128 @@ void LLVolume::createVolumeFaces()
}
+// sculpt replaces generate() for sculpted surfaces
+void LLVolume::sculpt(U16 sculpt_width, U16 sculpt_height, S8 sculpt_components, const U8* sculpt_data, S32 sculpt_level)
+{
+ BOOL data_is_empty = FALSE;
+
+ if (sculpt_width == 0 || sculpt_height == 0 || sculpt_data == NULL)
+ {
+ sculpt_level = -1;
+ data_is_empty = TRUE;
+ }
+
+ mPathp->generate(mDetail, 0, TRUE);
+ mProfilep->generate(mPathp->isOpen(), mDetail, 0, TRUE);
+
+
+ S32 sizeS = mPathp->mPath.size();
+ S32 sizeT = mProfilep->mProfile.size();
+
+ sNumMeshPoints -= mMesh.size();
+ mMesh.resize(sizeS * sizeT);
+ sNumMeshPoints += mMesh.size();
+
+ S32 vertex_change = 0;
+ // first test to see if image has enough variation to create geometry
+ if (!data_is_empty)
+ {
+ S32 last_index = 0;
+ for (S32 s = 0; s < sizeS; s++)
+ for (S32 t = 0; t < sizeT; t++)
+ {
+ U32 x = (U32) ((F32)s/(sizeS-1) * (F32) sculpt_width);
+ U32 y = (U32) ((F32)t/(sizeT-1) * (F32) sculpt_height);
+
+ if (y == sculpt_height) // clamp to bottom row
+ y = sculpt_height - 1;
+
+ if (x == sculpt_width) // stitch sides
+ x = 0;
+
+ if ((y == 0) || (y == sculpt_height-1)) // stitch top and bottom
+ x = sculpt_width / 2;
+
+ U32 index = (x + y * sculpt_width) * sculpt_components;
+
+ if (fabs((F32)(sculpt_data[index] - sculpt_data[last_index])) +
+ fabs((F32)(sculpt_data[index+1] - sculpt_data[last_index+1])) +
+ fabs((F32)(sculpt_data[index+2] - sculpt_data[last_index+2])) > 256 * 0.02)
+ vertex_change++;
+
+ last_index = index;
+ }
+ }
+
+
+ if ((F32)vertex_change / sizeS / sizeT < 0.05) // less than 5%
+ data_is_empty = TRUE;
+
+
+ //generate vertex positions
+ // Run along the path.
+ S32 s = 0, t = 0;
+ S32 line = 0;
+ while (s < sizeS)
+ {
+ t = 0;
+ // Run along the profile.
+ while (t < sizeT)
+ {
+ S32 i = t + line;
+ Point& pt = mMesh[i];
+
+ U32 x = (U32) ((F32)t/(sizeT-1) * (F32) sculpt_width);
+ U32 y = (U32) ((F32)s/(sizeS-1) * (F32) sculpt_height);
+
+ if (y == sculpt_height) // clamp to bottom row
+ y = sculpt_height - 1;
+
+ if (x == sculpt_width) // stitch sides
+ x = 0;
+
+ if ((y == 0) || (y == sculpt_height-1)) // stitch top and bottom
+ x = sculpt_width / 2;
+
+
+ if (data_is_empty) // if empty, make a sphere
+ {
+ F32 u = (F32)s/(sizeS-1);
+ F32 v = (F32)t/(sizeT-1);
+
+ const F32 RADIUS = (F32) 0.3;
+
+ pt.mPos.mV[0] = (F32)(sin(F_PI * v) * cos(2.0 * F_PI * u) * RADIUS);
+ pt.mPos.mV[1] = (F32)(sin(F_PI * v) * sin(2.0 * F_PI * u) * RADIUS);
+ pt.mPos.mV[2] = (F32)(cos(F_PI * v) * RADIUS);
+ }
+
+ else
+ {
+ U32 index = (x + y * sculpt_width) * sculpt_components;
+ pt.mPos.mV[0] = sculpt_data[index ] / 256.f - 0.5f;
+ pt.mPos.mV[1] = sculpt_data[index+1] / 256.f - 0.5f;
+ pt.mPos.mV[2] = sculpt_data[index+2] / 256.f - 0.5f;
+ }
+
+ t++;
+ }
+ line += sizeT;
+ s++;
+ }
+
+ for (S32 i = 0; i < (S32)mProfilep->mFaces.size(); i++)
+ {
+ mFaceMask |= mProfilep->mFaces[i].mFaceID;
+ }
+
+ mSculptLevel = sculpt_level;
+ createVolumeFaces();
+}
+
+
+
+
BOOL LLVolume::isCap(S32 face)
{
return mProfilep->mFaces[face].mCap;
@@ -1765,14 +1911,18 @@ BOOL LLVolume::isFlat(S32 face)
bool LLVolumeParams::operator==(const LLVolumeParams &params) const
{
- return (getPathParams() == params.getPathParams()) &&
- (getProfileParams() == params.getProfileParams());
+ return ( (getPathParams() == params.getPathParams()) &&
+ (getProfileParams() == params.getProfileParams()) &&
+ (mSculptID == params.mSculptID) &&
+ (mSculptType == params.mSculptType) );
}
bool LLVolumeParams::operator!=(const LLVolumeParams &params) const
{
- return (getPathParams() != params.getPathParams()) ||
- (getProfileParams() != params.getProfileParams());
+ return ( (getPathParams() != params.getPathParams()) ||
+ (getProfileParams() != params.getProfileParams()) ||
+ (mSculptID != params.mSculptID) ||
+ (mSculptType != params.mSculptType) );
}
bool LLVolumeParams::operator<(const LLVolumeParams &params) const
@@ -1781,16 +1931,29 @@ bool LLVolumeParams::operator<(const LLVolumeParams &params) const
{
return getPathParams() < params.getPathParams();
}
- else
+
+ if (getProfileParams() != params.getProfileParams())
{
return getProfileParams() < params.getProfileParams();
}
+
+ if (mSculptID != params.mSculptID)
+ {
+ return mSculptID < params.mSculptID;
+ }
+
+
+ return mSculptType < params.mSculptType;
+
+
}
void LLVolumeParams::copyParams(const LLVolumeParams &params)
{
mProfileParams.copyParams(params.mProfileParams);
mPathParams.copyParams(params.mPathParams);
+ mSculptID = params.getSculptID();
+ mSculptType = params.getSculptType();
}
// Less restricitve approx 0 for volumes
@@ -2050,6 +2213,13 @@ bool LLVolumeParams::setSkew(const F32 skew_value)
return valid;
}
+bool LLVolumeParams::setSculptID(const LLUUID sculpt_id, U8 sculpt_type)
+{
+ mSculptID = sculpt_id;
+ mSculptType = sculpt_type;
+ return true;
+}
+
bool LLVolumeParams::setType(U8 profile, U8 path)
{
bool result = true;
@@ -2809,7 +2979,7 @@ void LLVolume::generateSilhouetteVertices(std::vector<LLVector3> &vertices,
segments.clear();
//for each face
- for (S32 i = 0; i < getNumFaces(); i++) {
+ for (S32 i = 0; i < getNumVolumeFaces(); i++) {
LLVolumeFace face = this->getVolumeFace(i);
if (face.mTypeMask & (LLVolumeFace::CAP_MASK)) {
@@ -4416,6 +4586,9 @@ BOOL LLVolumeFace::createSide()
}
}
+ BOOL s_bottom_converges = ((mVertices[0].mPosition - mVertices[mNumS*(mNumT-2)].mPosition).magVecSquared() < 0.000001f);
+ BOOL s_top_converges = ((mVertices[mNumS-1].mPosition - mVertices[mNumS*(mNumT-2)+mNumS-1].mPosition).magVecSquared() < 0.000001f);
+
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;
@@ -4424,30 +4597,73 @@ BOOL LLVolumeFace::createSide()
}
}
- 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->getProfile().isOpen() == FALSE) &&
+ !(s_bottom_converges))
+ { //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)
+ if (mVolumep->getPathType() == LL_PCODE_PATH_CIRCLE &&
+ ((mVolumep->getProfileType() & LL_PCODE_PROFILE_MASK) == LL_PCODE_PROFILE_CIRCLE_HALF))
+ {
+ if (s_bottom_converges)
{ //all lower S have same normal
for (S32 i = 0; i < mNumT; i++) {
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
+ if (s_top_converges)
+ { //all upper S have same normal
for (S32 i = 0; i < mNumT; i++) {
mVertices[mNumS*i+mNumS-1].mNormal = LLVector3(-1,0,0);
}
}
}
+ U8 sculpt_type = mVolumep->getParams().getSculptType();
+
+ if (sculpt_type == LL_SCULPT_TYPE_SPHERE)
+ {
+ // average normals for north pole
+
+ LLVector3 average(0.0, 0.0, 0.0);
+ for (S32 i = 0; i < mNumS; i++)
+ {
+ average += mVertices[i].mNormal;
+ }
+
+ // set average
+ for (S32 i = 0; i < mNumS; i++)
+ {
+ mVertices[i].mNormal = average;
+ }
+ }
+
+
+ if (sculpt_type == LL_SCULPT_TYPE_SPHERE)
+ {
+ // average normals for south pole
+
+ LLVector3 average(0.0, 0.0, 0.0);
+ for (S32 i = 0; i < mNumS; i++)
+ {
+ average += mVertices[i + mNumS * (mNumT - 1)].mNormal;
+ }
+
+ // set average
+ for (S32 i = 0; i < mNumS; i++)
+ {
+ mVertices[i + mNumS * (mNumT - 1)].mNormal = average;
+ }
+ }
+
+
+
//normalize normals and binormals here so the meshes that reference
//this volume data don't have to
for (U32 i = 0; i < mVertices.size(); i++)
diff --git a/indra/llmath/llvolume.h b/indra/llmath/llvolume.h
index 22742e09da..3ce3058887 100644
--- a/indra/llmath/llvolume.h
+++ b/indra/llmath/llvolume.h
@@ -149,6 +149,16 @@ const LLFaceID LL_FACE_OUTER_SIDE_3 = 0x1 << 8;
//============================================================================
+// sculpt types
+
+const U8 LL_SCULPT_TYPE_NONE = 0;
+const U8 LL_SCULPT_TYPE_SPHERE = 1;
+const U8 LL_SCULPT_TYPE_TORUS = 2;
+const U8 LL_SCULPT_TYPE_PLAIN = 3;
+const U8 LL_SCULPT_TYPE_CYLINDER = 4;
+
+
+
class LLProfileParams
{
public:
@@ -492,8 +502,9 @@ public:
{
}
- LLVolumeParams(LLProfileParams &profile, LLPathParams &path)
- : mProfileParams(profile), mPathParams(path)
+ 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)
{
}
@@ -544,6 +555,7 @@ public:
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,
@@ -571,6 +583,8 @@ public:
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 isConvex() const;
@@ -593,6 +607,8 @@ public:
protected:
LLProfileParams mProfileParams;
LLPathParams mPathParams;
+ LLUUID mSculptID;
+ U8 mSculptType;
};
@@ -615,7 +631,7 @@ public:
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 generate(BOOL path_open, F32 detail = 1.0f, S32 split = 0, BOOL is_sculpted = FALSE);
BOOL isConcave() const { return mConcave; }
public:
const LLProfileParams &mParams;
@@ -684,7 +700,7 @@ public:
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);
+ virtual BOOL generate(F32 detail=1.0f, S32 split = 0, BOOL is_sculpted = FALSE);
BOOL isOpen() const { return mOpen; }
F32 getStep() const { return mStep; }
@@ -711,7 +727,7 @@ class LLDynamicPath : public LLPath
{
public:
LLDynamicPath(const LLPathParams &params) : LLPath(params) { }
- BOOL generate(F32 detail=1.0f, S32 split = 0);
+ BOOL generate(F32 detail=1.0f, S32 split = 0, BOOL is_sculpted = FALSE);
};
// Yet another "face" class - caches volume-specific, but not instance-specific data for faces)
@@ -801,7 +817,7 @@ public:
U8 getProfileType() const { return mProfilep->mParams.getCurveType(); }
U8 getPathType() const { return mPathp->mParams.getCurveType(); }
S32 getNumFaces() const { return (S32)mProfilep->mFaces.size(); }
-
+ S32 getNumVolumeFaces() const { return mNumVolumeFaces; }
const F32 getDetail() const { return mDetail; }
const LLVolumeParams & getParams() const { return mParams; }
LLVolumeParams getCopyOfParams() const { return mParams; }
@@ -819,6 +835,11 @@ public:
BOOL isFlat(S32 face);
BOOL isUnique() const { return mUnique; }
+ S32 getSculptLevel() const { return mSculptLevel; }
+ void setSculptLevel(S32 level) { mSculptLevel = level; }
+
+ U8 getSculptType() const { return mSculptType; }
+
S32 *getTriangleIndices(U32 &num_indices) const;
void generateSilhouetteVertices(std::vector<LLVector3> &vertices, std::vector<LLVector3> &normals, std::vector<S32> &segments, const LLVector3& view_vec,
const LLMatrix4& mat,
@@ -843,7 +864,7 @@ public:
LLFaceID generateFaceMask();
BOOL isFaceMaskValid(LLFaceID face_mask);
- static S32 mNumMeshPoints;
+ 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
@@ -852,14 +873,19 @@ public:
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);
+
protected:
BOOL generate();
void createVolumeFaces();
-protected:
+ protected:
BOOL mUnique;
F32 mDetail;
+ S32 mSculptLevel;
+ U8 mSculptType;
+
LLVolumeParams mParams;
LLPath *mPathp;
LLProfile *mProfilep;