summaryrefslogtreecommitdiff
path: root/indra/llmath/llvolume.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llmath/llvolume.cpp')
-rw-r--r--indra/llmath/llvolume.cpp652
1 files changed, 645 insertions, 7 deletions
diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp
index 34348230b6..52a3fb2195 100644
--- a/indra/llmath/llvolume.cpp
+++ b/indra/llmath/llvolume.cpp
@@ -46,6 +46,9 @@
#include "lldarray.h"
#include "llvolume.h"
#include "llstl.h"
+#include "llsdserialize.h"
+#include "zlib/zlib.h"
+
#define DEBUG_SILHOUETTE_BINORMALS 0
#define DEBUG_SILHOUETTE_NORMALS 0 // TomY: Use this to display normals using the silhouette
@@ -1673,7 +1676,9 @@ LLVolume::LLVolume(const LLVolumeParams &params, const F32 detail, const BOOL ge
mFaceMask = 0x0;
mDetail = detail;
mSculptLevel = -2;
-
+ mIsTetrahedron = FALSE;
+ mLODScaleBias.setVec(1,1,1);
+
// set defaults
if (mParams.getPathParams().getCurveType() == LL_PCODE_PATH_FLEXIBLE)
{
@@ -1688,7 +1693,8 @@ LLVolume::LLVolume(const LLVolumeParams &params, const F32 detail, const BOOL ge
mGenerateSingleFace = generate_single_face;
generate();
- if (mParams.getSculptID().isNull())
+
+ if (mParams.getSculptID().isNull() && mParams.getSculptType() == LL_SCULPT_TYPE_NONE)
{
createVolumeFaces();
}
@@ -1839,6 +1845,482 @@ BOOL LLVolume::generate()
return FALSE;
}
+bool LLVolumeFace::VertexData::operator<(const LLVolumeFace::VertexData& rhs)const
+{
+ const U8* l = (const U8*) this;
+ const U8* r = (const U8*) &rhs;
+
+ for (U32 i = 0; i < sizeof(VertexData); ++i)
+ {
+ if (l[i] != r[i])
+ {
+ return r[i] < l[i];
+ }
+ }
+
+ return false;
+}
+
+bool LLVolumeFace::VertexData::operator==(const LLVolumeFace::VertexData& rhs)const
+{
+ const U8* l = (const U8*) this;
+ const U8* r = (const U8*) &rhs;
+
+ for (U32 i = 0; i < sizeof(VertexData); ++i)
+ {
+ if (l[i] != r[i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool LLVolumeFace::VertexData::compareNormal(const LLVolumeFace::VertexData& rhs, F32 angle_cutoff) const
+{
+ bool retval = false;
+ if (rhs.mPosition == mPosition && rhs.mTexCoord == mTexCoord)
+ {
+ if (angle_cutoff > 1.f)
+ {
+ retval = (mNormal == rhs.mNormal);
+ }
+ else
+ {
+ F32 cur_angle = rhs.mNormal*mNormal;
+ retval = cur_angle > angle_cutoff;
+ }
+ }
+
+ return retval;
+}
+
+BOOL LLVolume::createVolumeFacesFromFile(const std::string& file_name)
+{
+ std::ifstream is;
+
+ is.open(file_name.c_str(), std::ifstream::in | std::ifstream::binary);
+
+ BOOL success = createVolumeFacesFromStream(is);
+
+ is.close();
+
+ return success;
+}
+
+BOOL LLVolume::createVolumeFacesFromStream(std::istream& is)
+{
+ mSculptLevel = -1; // default is an error occured
+
+ LLSD header;
+ {
+ if (!LLSDSerialize::deserialize(header, is, 1024*1024*1024))
+ {
+ llwarns << "Mesh header parse error. Not a valid mesh asset!" << llendl;
+ return FALSE;
+ }
+ }
+
+ std::string nm[] =
+ {
+ "lowest_lod",
+ "low_lod",
+ "medium_lod",
+ "high_lod"
+ };
+
+ S32 lod = llclamp((S32) mDetail, 0, 3);
+
+ while (lod < 4 &&
+ (header[nm[lod]]["offset"].asInteger() == -1 ||
+ header[nm[lod]]["size"].asInteger() == 0 ))
+ {
+ ++lod;
+ }
+
+ if (lod >= 4)
+ {
+ lod = llclamp((S32) mDetail, 0, 3);
+
+ while (lod >= 0 &&
+ (header[nm[lod]]["offset"].asInteger() == -1 ||
+ header[nm[lod]]["size"].asInteger() == 0) )
+ {
+ --lod;
+ }
+
+ if (lod < 0)
+ {
+ llwarns << "Mesh header missing LOD offsets. Not a valid mesh asset!" << llendl;
+ return FALSE;
+ }
+ }
+
+ is.seekg(header[nm[lod]]["offset"].asInteger(), std::ios_base::cur);
+
+ return unpackVolumeFaces(is, header[nm[lod]]["size"].asInteger());
+}
+
+bool LLVolume::unpackVolumeFaces(std::istream& is, S32 size)
+{
+ U8* result = NULL;
+ U32 cur_size = 0;
+
+ {
+ //input stream is now pointing at a zlib compressed block of LLSD
+ //decompress block
+ z_stream strm;
+
+ const U32 CHUNK = 65536;
+
+ U8 *in = new U8[size];
+ is.read((char*) in, size);
+
+ U8 out[CHUNK];
+
+ strm.zalloc = Z_NULL;
+ strm.zfree = Z_NULL;
+ strm.opaque = Z_NULL;
+ strm.avail_in = size;
+ strm.next_in = in;
+
+ S32 ret = inflateInit(&strm);
+
+ if (ret != Z_OK)
+ {
+ llerrs << "WTF?" << llendl;
+ }
+
+ do
+ {
+ strm.avail_out = CHUNK;
+ strm.next_out = out;
+ ret = inflate(&strm, Z_NO_FLUSH);
+ if (ret == Z_STREAM_ERROR)
+ {
+ inflateEnd(&strm);
+ free(result);
+ delete [] in;
+ return false;
+ }
+
+ switch (ret)
+ {
+ case Z_NEED_DICT:
+ ret = Z_DATA_ERROR;
+ case Z_DATA_ERROR:
+ case Z_MEM_ERROR:
+ inflateEnd(&strm);
+ free(result);
+ delete [] in;
+ return false;
+ break;
+ }
+
+ U32 have = CHUNK-strm.avail_out;
+
+ result = (U8*) realloc(result, cur_size + have);
+ memcpy(result+cur_size, out, have);
+ cur_size += have;
+
+ } while (strm.avail_out == 0);
+
+ inflateEnd(&strm);
+ delete [] in;
+
+ if (ret != Z_STREAM_END)
+ {
+ free(result);
+ return false;
+ }
+ }
+
+ //result now points to the decompressed LLSD block
+
+ LLSD mdl;
+
+ {
+ std::string res_str((char*) result, cur_size);
+ std::istringstream istr(res_str);
+
+ if (!LLSDSerialize::deserialize(mdl, istr, cur_size))
+ {
+ llwarns << "not a valid mesh asset!" << llendl;
+ return false;
+ }
+ }
+
+
+ free(result);
+
+
+ {
+ U32 face_count = mdl.size();
+
+ if (face_count == 0)
+ {
+ llerrs << "WTF?" << llendl;
+ }
+
+ mVolumeFaces.resize(face_count);
+
+ for (U32 i = 0; i < face_count; ++i)
+ {
+ LLSD::Binary pos = mdl[i]["Position"];
+ LLSD::Binary norm = mdl[i]["Normal"];
+ LLSD::Binary tc = mdl[i]["TexCoord0"];
+ LLSD::Binary idx = mdl[i]["TriangleList"];
+
+ LLVolumeFace& face = mVolumeFaces[i];
+
+ face.mHasBinormals = false;
+
+ //copy out indices
+ face.mIndices.resize(idx.size()/2);
+ if (idx.empty() || face.mIndices.size() < 3)
+ { //why is there an empty index list?
+ llerrs <<"WTF?" << llendl;
+ continue;
+ }
+
+ U16* indices = (U16*) &(idx[0]);
+ for (U32 j = 0; j < idx.size()/2; ++j)
+ {
+ face.mIndices[j] = indices[j];
+ }
+
+ //copy out vertices
+ U32 num_verts = pos.size()/(3*2);
+ face.mVertices.resize(num_verts);
+
+ LLVector3 min_pos;
+ LLVector3 max_pos;
+ LLVector2 min_tc;
+ LLVector2 max_tc;
+
+ min_pos.setValue(mdl[i]["PositionDomain"]["Min"]);
+ max_pos.setValue(mdl[i]["PositionDomain"]["Max"]);
+ min_tc.setValue(mdl[i]["TexCoord0Domain"]["Min"]);
+ max_tc.setValue(mdl[i]["TexCoord0Domain"]["Max"]);
+
+ LLVector3 pos_range = max_pos - min_pos;
+ LLVector2 tc_range = max_tc - min_tc;
+
+ LLVector3& min = face.mExtents[0];
+ LLVector3& max = face.mExtents[1];
+
+ min = max = LLVector3(0,0,0);
+
+ for (U32 j = 0; j < num_verts; ++j)
+ {
+ U16* v = (U16*) &(pos[j*3*2]);
+
+ face.mVertices[j].mPosition.setVec(
+ (F32) v[0] / 65535.f * pos_range.mV[0] + min_pos.mV[0],
+ (F32) v[1] / 65535.f * pos_range.mV[1] + min_pos.mV[1],
+ (F32) v[2] / 65535.f * pos_range.mV[2] + min_pos.mV[2]);
+
+ if (j == 0)
+ {
+ min = max = face.mVertices[j].mPosition;
+ }
+ else
+ {
+ update_min_max(min,max,face.mVertices[j].mPosition);
+ }
+
+ U16* n = (U16*) &(norm[j*3*2]);
+
+ face.mVertices[j].mNormal.setVec(
+ (F32) n[0] / 65535.f * 2.f - 1.f,
+ (F32) n[1] / 65535.f * 2.f - 1.f,
+ (F32) n[2] / 65535.f * 2.f - 1.f);
+
+ U16* t = (U16*) &(tc[j*2*2]);
+
+ face.mVertices[j].mTexCoord.setVec(
+ (F32) t[0] / 65535.f * tc_range.mV[0] + min_tc.mV[0],
+ (F32) t[1] / 65535.f * tc_range.mV[1] + min_tc.mV[1]);
+ }
+
+
+ // modifier flags?
+ bool do_mirror = (mParams.getSculptType() & LL_SCULPT_FLAG_MIRROR);
+ bool do_invert = (mParams.getSculptType() &LL_SCULPT_FLAG_INVERT);
+
+
+ // translate to actions:
+ bool do_reflect_x = false;
+ bool do_reverse_triangles = false;
+ bool do_invert_normals = false;
+
+ if (do_mirror)
+ {
+ do_reflect_x = true;
+ do_reverse_triangles = !do_reverse_triangles;
+ }
+
+ if (do_invert)
+ {
+ do_invert_normals = true;
+ do_reverse_triangles = !do_reverse_triangles;
+ }
+
+ // now do the work
+
+ if (do_reflect_x)
+ {
+ for (S32 i = 0; i < face.mVertices.size(); i++)
+ {
+ face.mVertices[i].mPosition.mV[VX] *= -1.0f;
+ face.mVertices[i].mNormal.mV[VX] *= -1.0f;
+ }
+ }
+
+ if (do_invert_normals)
+ {
+ for (S32 i = 0; i < face.mVertices.size(); i++)
+ {
+ face.mVertices[i].mNormal *= -1.0f;
+ }
+ }
+
+ if (do_reverse_triangles)
+ {
+ for (U32 j = 0; j < face.mIndices.size(); j += 3)
+ {
+ // swap the 2nd and 3rd index
+ S32 swap = face.mIndices[j+1];
+ face.mIndices[j+1] = face.mIndices[j+2];
+ face.mIndices[j+2] = swap;
+ }
+ }
+
+ }
+ }
+
+ mSculptLevel = 0; // success!
+ return true;
+}
+
+void tetrahedron_set_normal(LLVolumeFace::VertexData* cv)
+{
+ LLVector3 nrm = (cv[1].mPosition-cv[0].mPosition)%(cv[2].mPosition-cv[0].mPosition);
+
+ nrm.normVec();
+
+ cv[0].mNormal = nrm;
+ cv[1].mNormal = nrm;
+ cv[2].mNormal = nrm;
+}
+
+BOOL LLVolume::isTetrahedron()
+{
+ return mIsTetrahedron;
+}
+
+void LLVolume::makeTetrahedron()
+{
+ mVolumeFaces.clear();
+
+ LLVolumeFace face;
+
+ F32 x = 0.25f;
+ LLVector3 p[] =
+ { //unit tetrahedron corners
+ LLVector3(x,x,x),
+ LLVector3(-x,-x,x),
+ LLVector3(-x,x,-x),
+ LLVector3(x,-x,-x)
+ };
+
+ face.mExtents[0].setVec(-x,-x,-x);
+ face.mExtents[1].setVec(x,x,x);
+
+ LLVolumeFace::VertexData cv[3];
+
+ //set texture coordinates
+ cv[0].mTexCoord = LLVector2(0,0);
+ cv[1].mTexCoord = LLVector2(1,0);
+ cv[2].mTexCoord = LLVector2(0.5f, 0.5f*F_SQRT3);
+
+
+ //side 1
+ cv[0].mPosition = p[1];
+ cv[1].mPosition = p[0];
+ cv[2].mPosition = p[2];
+
+ tetrahedron_set_normal(cv);
+
+ face.mVertices.push_back(cv[0]);
+ face.mVertices.push_back(cv[1]);
+ face.mVertices.push_back(cv[2]);
+
+ //side 2
+ cv[0].mPosition = p[3];
+ cv[1].mPosition = p[0];
+ cv[2].mPosition = p[1];
+
+ tetrahedron_set_normal(cv);
+
+ face.mVertices.push_back(cv[0]);
+ face.mVertices.push_back(cv[1]);
+ face.mVertices.push_back(cv[2]);
+
+ //side 3
+ cv[0].mPosition = p[3];
+ cv[1].mPosition = p[1];
+ cv[2].mPosition = p[2];
+
+ tetrahedron_set_normal(cv);
+
+ face.mVertices.push_back(cv[0]);
+ face.mVertices.push_back(cv[1]);
+ face.mVertices.push_back(cv[2]);
+
+ //side 4
+ cv[0].mPosition = p[2];
+ cv[1].mPosition = p[0];
+ cv[2].mPosition = p[3];
+
+ tetrahedron_set_normal(cv);
+
+ face.mVertices.push_back(cv[0]);
+ face.mVertices.push_back(cv[1]);
+ face.mVertices.push_back(cv[2]);
+
+ //set index buffer
+ for (U32 i = 0; i < 12; i++)
+ {
+ face.mIndices.push_back(i);
+ }
+
+ mVolumeFaces.push_back(face);
+ mSculptLevel = 0;
+ mIsTetrahedron = TRUE;
+}
+
+void LLVolume::copyVolumeFaces(LLVolume* volume)
+{
+ mVolumeFaces = volume->mVolumeFaces;
+ mSculptLevel = 0;
+ mIsTetrahedron = FALSE;
+}
+
+
+S32 LLVolume::getNumFaces() const
+{
+ U8 sculpt_type = (mParams.getSculptType() & LL_SCULPT_TYPE_MASK);
+
+ if (sculpt_type == LL_SCULPT_TYPE_MESH)
+ {
+ return LL_SCULPT_MESH_MAX_FACES;
+ }
+
+ return (S32)mProfilep->mFaces.size();
+}
+
void LLVolume::createVolumeFaces()
{
@@ -1864,6 +2346,11 @@ void LLVolume::createVolumeFaces()
LLProfile::Face& face = mProfilep->mFaces[i];
vf.mBeginS = face.mIndex;
vf.mNumS = face.mCount;
+ if (vf.mNumS < 0)
+ {
+ llerrs << "Volume face corruption detected." << llendl;
+ }
+
vf.mBeginT = 0;
vf.mNumT= getPath().mPath.size();
vf.mID = i;
@@ -1907,6 +2394,10 @@ void LLVolume::createVolumeFaces()
if (face.mFlat && vf.mNumS > 2)
{ //flat inner faces have to copy vert normals
vf.mNumS = vf.mNumS*2;
+ if (vf.mNumS < 0)
+ {
+ llerrs << "Volume face corruption detected." << llendl;
+ }
}
}
else
@@ -2198,6 +2689,11 @@ void LLVolume::sculpt(U16 sculpt_width, U16 sculpt_height, S8 sculpt_components,
LLMemType m1(LLMemType::MTYPE_VOLUME);
U8 sculpt_type = mParams.getSculptType();
+ if (sculpt_type & LL_SCULPT_TYPE_MASK == LL_SCULPT_TYPE_MESH)
+ {
+ llerrs << "WTF?" << llendl;
+ }
+
BOOL data_is_empty = FALSE;
if (sculpt_width == 0 || sculpt_height == 0 || sculpt_components < 3 || sculpt_data == NULL)
@@ -2309,7 +2805,6 @@ bool LLVolumeParams::operator<(const LLVolumeParams &params) const
return mSculptID < params.mSculptID;
}
-
return mSculptType < params.mSculptType;
@@ -3367,6 +3862,20 @@ S32 LLVolume::getNumTriangleIndices() const
return count;
}
+
+S32 LLVolume::getNumTriangles() const
+{
+ U32 triangle_count = 0;
+
+ for (S32 i = 0; i < getNumVolumeFaces(); ++i)
+ {
+ triangle_count += getVolumeFace(i).mIndices.size()/3;
+ }
+
+ return triangle_count;
+}
+
+
//-----------------------------------------------------------------------------
// generateSilhouetteVertices()
//-----------------------------------------------------------------------------
@@ -3379,22 +3888,29 @@ void LLVolume::generateSilhouetteVertices(std::vector<LLVector3> &vertices,
S32 face_mask)
{
LLMemType m1(LLMemType::MTYPE_VOLUME);
-
+
vertices.clear();
normals.clear();
segments.clear();
+ if ((mParams.getSculptType() & LL_SCULPT_TYPE_MASK) == LL_SCULPT_TYPE_MESH)
+ {
+ return;
+ }
+
S32 cur_index = 0;
//for each face
for (face_list_t::iterator iter = mVolumeFaces.begin();
iter != mVolumeFaces.end(); ++iter)
{
- const LLVolumeFace& face = *iter;
+ LLVolumeFace& face = *iter;
- if (!(face_mask & (0x1 << cur_index++)))
+ if (!(face_mask & (0x1 << cur_index++)) ||
+ face.mIndices.empty() || face.mEdge.empty())
{
continue;
}
+
if (face.mTypeMask & (LLVolumeFace::CAP_MASK)) {
}
@@ -3594,6 +4110,8 @@ S32 LLVolume::lineSegmentIntersect(const LLVector3& start, const LLVector3& end,
F32 closest_t = 2.f; // must be larger than 1
+ end_face = llmin(end_face, getNumVolumeFaces()-1);
+
for (S32 i = start_face; i <= end_face; i++)
{
const LLVolumeFace &face = getVolumeFace((U32)i);
@@ -4104,11 +4622,28 @@ BOOL LLVolumeParams::exportLegacyStream(std::ostream& output_stream) const
return TRUE;
}
+LLSD LLVolumeParams::sculptAsLLSD() const
+{
+ LLSD sd = LLSD();
+ sd["id"] = getSculptID();
+ sd["type"] = getSculptType();
+
+ return sd;
+}
+
+bool LLVolumeParams::sculptFromLLSD(LLSD& sd)
+{
+ setSculptID(sd["id"].asUUID(), (U8)sd["type"].asInteger());
+ return true;
+}
+
LLSD LLVolumeParams::asLLSD() const
{
LLSD sd = LLSD();
sd["path"] = mPathParams;
sd["profile"] = mProfileParams;
+ sd["sculpt"] = sculptAsLLSD();
+
return sd;
}
@@ -4116,6 +4651,8 @@ bool LLVolumeParams::fromLLSD(LLSD& sd)
{
mPathParams.fromLLSD(sd["path"]);
mProfileParams.fromLLSD(sd["profile"]);
+ sculptFromLLSD(sd["sculpt"]);
+
return true;
}
@@ -4158,6 +4695,12 @@ const F32 MIN_CONCAVE_PATH_WEDGE = 0.111111f; // 1/9 unity
// for collison purposes
BOOL LLVolumeParams::isConvex() const
{
+ if (!getSculptID().isNull())
+ {
+ // can't determine, be safe and say no:
+ return FALSE;
+ }
+
F32 path_length = mPathParams.getEnd() - mPathParams.getBegin();
F32 hollow = mProfileParams.getHollow();
@@ -4416,6 +4959,61 @@ BOOL LLVolumeFace::create(LLVolume* volume, BOOL partial_build)
}
}
+void LLVolumeFace::optimize(F32 angle_cutoff)
+{
+ LLVolumeFace new_face;
+
+ VertexMapData::PointMap point_map;
+
+ //remove redundant vertices
+ for (U32 i = 0; i < mIndices.size(); ++i)
+ {
+ U16 index = mIndices[i];
+
+ LLVolumeFace::VertexData cv = mVertices[index];
+
+ BOOL found = FALSE;
+ VertexMapData::PointMap::iterator point_iter = point_map.find(cv.mPosition);
+ if (point_iter != point_map.end())
+ { //duplicate point might exist
+ for (U32 j = 0; j < point_iter->second.size(); ++j)
+ {
+ LLVolumeFace::VertexData& tv = (point_iter->second)[j];
+ if (tv.compareNormal(cv, angle_cutoff))
+ {
+ found = TRUE;
+ new_face.mIndices.push_back((point_iter->second)[j].mIndex);
+ break;
+ }
+ }
+ }
+
+ if (!found)
+ {
+ new_face.mVertices.push_back(cv);
+ U16 index = (U16) new_face.mVertices.size()-1;
+ new_face.mIndices.push_back(index);
+
+ VertexMapData d;
+ d.mPosition = cv.mPosition;
+ d.mTexCoord = cv.mTexCoord;
+ d.mNormal = cv.mNormal;
+ d.mIndex = index;
+ if (point_iter != point_map.end())
+ {
+ point_iter->second.push_back(d);
+ }
+ else
+ {
+ point_map[d.mPosition].push_back(d);
+ }
+ }
+ }
+
+ mVertices = new_face.mVertices;
+ mIndices = new_face.mIndices;
+}
+
void LerpPlanarVertex(LLVolumeFace::VertexData& v0,
LLVolumeFace::VertexData& v1,
LLVolumeFace::VertexData& v2,
@@ -5074,6 +5672,42 @@ void LLVolumeFace::createBinormals()
}
}
+void LLVolumeFace::appendFace(const LLVolumeFace& face, LLMatrix4& mat, LLMatrix4& norm_mat)
+{
+ U16 offset = mVertices.size();
+
+ if (face.mVertices.size() + mVertices.size() > 65536)
+ {
+ llerrs << "Cannot append face -- 16-bit overflow will occur." << llendl;
+ }
+
+ for (U32 i = 0; i < face.mVertices.size(); ++i)
+ {
+ VertexData v = face.mVertices[i];
+ v.mPosition = v.mPosition*mat;
+ v.mNormal = v.mNormal * norm_mat;
+
+ v.mNormal.normalize();
+
+ mVertices.push_back(v);
+
+ if (offset == 0 && i == 0)
+ {
+ mExtents[0] = mExtents[1] = v.mPosition;
+ }
+ else
+ {
+ update_min_max(mExtents[0], mExtents[1], v.mPosition);
+ }
+ }
+
+
+ for (U32 i = 0; i < face.mIndices.size(); ++i)
+ {
+ mIndices.push_back(face.mIndices[i]+offset);
+ }
+}
+
BOOL LLVolumeFace::createSide(LLVolume* volume, BOOL partial_build)
{
LLMemType m1(LLMemType::MTYPE_VOLUME);
@@ -5105,7 +5739,11 @@ BOOL LLVolumeFace::createSide(LLVolume* volume, BOOL partial_build)
if (!partial_build)
{
mIndices.resize(num_indices);
- mEdge.resize(num_indices);
+
+ if ((volume->getParams().getSculptType() & LL_SCULPT_TYPE_MASK) != LL_SCULPT_TYPE_MESH)
+ {
+ mEdge.resize(num_indices);
+ }
}
else
{