Bulk of mesh. Excluded via #if MESH_ENABLED. LLModel still needs updated for strided vbos, Collada SDK needs to be wrangled, and misc pieces need to be found and brought over. Skipping inventory stuff until meshes are at least displayable.

This commit is contained in:
Shyotl
2011-07-23 03:26:30 -05:00
parent 179193d173
commit 1e92e734d8
29 changed files with 10133 additions and 92 deletions

View File

@@ -2079,6 +2079,14 @@ LLVolume::LLVolume(const LLVolumeParams &params, const F32 detail, const BOOL ge
mFaceMask = 0x0;
mDetail = detail;
mSculptLevel = -2;
#if MESH_ENABLED
mIsTetrahedron = FALSE;
mLODScaleBias.setVec(1,1,1);
mHullPoints = NULL;
mHullIndices = NULL;
mNumHullPoints = 0;
mNumHullIndices = 0;
#endif //MESH_ENABLED
// set defaults
if (mParams.getPathParams().getCurveType() == LL_PCODE_PATH_FLEXIBLE)
@@ -2386,9 +2394,461 @@ bool LLVolumeFace::VertexData::compareNormal(const LLVolumeFace::VertexData& rhs
return retval;
}
#if MESH_ENABLED
bool LLVolume::unpackVolumeFaces(std::istream& is, S32 size)
{
//input stream is now pointing at a zlib compressed block of LLSD
//decompress block
LLSD mdl;
if (!unzip_llsd(mdl, is, size))
{
llwarns << "not a valid mesh asset!" << llendl;
return false;
}
{
U32 face_count = mdl.size();
if (face_count == 0)
{ //no faces unpacked, treat as failed decode
llwarns << "found no faces!" << llendl;
return false;
}
mVolumeFaces.resize(face_count);
for (U32 i = 0; i < face_count; ++i)
{
LLVolumeFace& face = mVolumeFaces[i];
if (mdl[i].has("NoGeometry"))
{ //face has no geometry, continue
face.resizeIndices(3);
face.resizeVertices(1);
memset(face.mPositions, 0, sizeof(LLVector4a));
memset(face.mNormals, 0, sizeof(LLVector4a));
memset(face.mTexCoords, 0, sizeof(LLVector2));
memset(face.mIndices, 0, sizeof(U16)*3);
continue;
}
LLSD::Binary pos = mdl[i]["Position"];
LLSD::Binary norm = mdl[i]["Normal"];
LLSD::Binary tc = mdl[i]["TexCoord0"];
LLSD::Binary idx = mdl[i]["TriangleList"];
//copy out indices
face.resizeIndices(idx.size()/2);
if (idx.empty() || face.mNumIndices < 3)
{ //why is there an empty index list?
llwarns <<"Empty face present!" << llendl;
continue;
}
U16* indices = (U16*) &(idx[0]);
U32 count = idx.size()/2;
for (U32 j = 0; j < count; ++j)
{
face.mIndices[j] = indices[j];
}
//copy out vertices
U32 num_verts = pos.size()/(3*2);
face.resizeVertices(num_verts);
LLVector3 minp;
LLVector3 maxp;
LLVector2 min_tc;
LLVector2 max_tc;
minp.setValue(mdl[i]["PositionDomain"]["Min"]);
maxp.setValue(mdl[i]["PositionDomain"]["Max"]);
LLVector4a min_pos, max_pos;
min_pos.load3(minp.mV);
max_pos.load3(maxp.mV);
min_tc.setValue(mdl[i]["TexCoord0Domain"]["Min"]);
max_tc.setValue(mdl[i]["TexCoord0Domain"]["Max"]);
LLVector4a pos_range;
pos_range.setSub(max_pos, min_pos);
LLVector2 tc_range2 = max_tc - min_tc;
LLVector4a tc_range;
tc_range.set(tc_range2[0], tc_range2[1], tc_range2[0], tc_range2[1]);
LLVector4a min_tc4(min_tc[0], min_tc[1], min_tc[0], min_tc[1]);
LLVector4a* pos_out = face.mPositions;
LLVector4a* norm_out = face.mNormals;
LLVector4a* tc_out = (LLVector4a*) face.mTexCoords;
{
U16* v = (U16*) &(pos[0]);
for (U32 j = 0; j < num_verts; ++j)
{
pos_out->set((F32) v[0], (F32) v[1], (F32) v[2]);
pos_out->div(65535.f);
pos_out->mul(pos_range);
pos_out->add(min_pos);
pos_out++;
v += 3;
}
}
{
U16* n = (U16*) &(norm[0]);
if(n)
{
for (U32 j = 0; j < num_verts; ++j)
{
norm_out->set((F32) n[0], (F32) n[1], (F32) n[2]);
norm_out->div(65535.f);
norm_out->mul(2.f);
norm_out->sub(1.f);
norm_out++;
n += 3;
}
}
}
{
U16* t = (U16*) &(tc[0]);
if(t)
{
for (U32 j = 0; j < num_verts; j+=2)
{
if (j < num_verts-1)
{
tc_out->set((F32) t[0], (F32) t[1], (F32) t[2], (F32) t[3]);
}
else
{
tc_out->set((F32) t[0], (F32) t[1], 0.f, 0.f);
}
t += 4;
tc_out->div(65535.f);
tc_out->mul(tc_range);
tc_out->add(min_tc4);
tc_out++;
}
}
}
if (mdl[i].has("Weights"))
{
face.allocateWeights(num_verts);
LLSD::Binary weights = mdl[i]["Weights"];
U32 idx = 0;
U32 cur_vertex = 0;
while (idx < weights.size() && cur_vertex < num_verts)
{
const U8 END_INFLUENCES = 0xFF;
U8 joint = weights[idx++];
U32 cur_influence = 0;
LLVector4 wght(0,0,0,0);
while (joint != END_INFLUENCES && idx < weights.size())
{
U16 influence = weights[idx++];
influence |= ((U16) weights[idx++] << 8);
F32 w = llclamp((F32) influence / 65535.f, 0.f, 0.99999f);
wght.mV[cur_influence++] = (F32) joint + w;
if (cur_influence >= 4)
{
joint = END_INFLUENCES;
}
else
{
joint = weights[idx++];
}
}
face.mWeights[cur_vertex].loadua(wght.mV);
cur_vertex++;
}
if (cur_vertex != num_verts || idx != weights.size())
{
llwarns << "Vertex weight count does not match vertex count!" << llendl;
}
}
// 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)
{
LLVector4a* p = (LLVector4a*) face.mPositions;
LLVector4a* n = (LLVector4a*) face.mNormals;
for (S32 i = 0; i < face.mNumVertices; i++)
{
p[i].mul(-1.0f);
n[i].mul(-1.0f);
}
}
if (do_invert_normals)
{
LLVector4a* n = (LLVector4a*) face.mNormals;
for (S32 i = 0; i < face.mNumVertices; i++)
{
n[i].mul(-1.0f);
}
}
if (do_reverse_triangles)
{
for (U32 j = 0; j < face.mNumIndices; 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;
}
}
//calculate bounding box
LLVector4a& min = face.mExtents[0];
LLVector4a& max = face.mExtents[1];
if (face.mNumVertices < 3)
{ //empty face, use a dummy 1cm (at 1m scale) bounding box
min.splat(-0.005f);
max.splat(0.005f);
}
else
{
min = max = face.mPositions[0];
for (S32 i = 1; i < face.mNumVertices; ++i)
{
min.setMin(min, face.mPositions[i]);
max.setMax(max, face.mPositions[i]);
}
}
}
}
mSculptLevel = 0; // success!
cacheOptimize();
return true;
}
void tetrahedron_set_normal(LLVolumeFace::VertexData* cv)
{
LLVector4a v0;
v0.setSub(cv[1].getPosition(), cv[0].getNormal());
LLVector4a v1;
v1.setSub(cv[2].getNormal(), cv[0].getPosition());
cv[0].getNormal().setCross3(v0,v1);
cv[0].getNormal().normalize3fast();
cv[1].setNormal(cv[0].getNormal());
cv[2].setNormal(cv[1].getNormal());
}
BOOL LLVolume::isTetrahedron()
{
return mIsTetrahedron;
}
void LLVolume::makeTetrahedron()
{
mVolumeFaces.clear();
LLVolumeFace face;
F32 x = 0.25f;
LLVector4a p[] =
{ //unit tetrahedron corners
LLVector4a(x,x,x),
LLVector4a(-x,-x,x),
LLVector4a(-x,x,-x),
LLVector4a(x,-x,-x)
};
face.mExtents[0].splat(-x);
face.mExtents[1].splat(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].setPosition(p[1]);
cv[1].setPosition(p[0]);
cv[2].setPosition(p[2]);
tetrahedron_set_normal(cv);
face.resizeVertices(12);
face.resizeIndices(12);
LLVector4a* v = (LLVector4a*) face.mPositions;
LLVector4a* n = (LLVector4a*) face.mNormals;
LLVector2* tc = (LLVector2*) face.mTexCoords;
v[0] = cv[0].getPosition();
v[1] = cv[1].getPosition();
v[2] = cv[2].getPosition();
v += 3;
n[0] = cv[0].getNormal();
n[1] = cv[1].getNormal();
n[2] = cv[2].getNormal();
n += 3;
tc[0] = cv[0].mTexCoord;
tc[1] = cv[1].mTexCoord;
tc[2] = cv[2].mTexCoord;
tc += 3;
//side 2
cv[0].setPosition(p[3]);
cv[1].setPosition(p[0]);
cv[2].setPosition(p[1]);
tetrahedron_set_normal(cv);
v[0] = cv[0].getPosition();
v[1] = cv[1].getPosition();
v[2] = cv[2].getPosition();
v += 3;
n[0] = cv[0].getNormal();
n[1] = cv[1].getNormal();
n[2] = cv[2].getNormal();
n += 3;
tc[0] = cv[0].mTexCoord;
tc[1] = cv[1].mTexCoord;
tc[2] = cv[2].mTexCoord;
tc += 3;
//side 3
cv[0].setPosition(p[3]);
cv[1].setPosition(p[1]);
cv[2].setPosition(p[2]);
tetrahedron_set_normal(cv);
v[0] = cv[0].getPosition();
v[1] = cv[1].getPosition();
v[2] = cv[2].getPosition();
v += 3;
n[0] = cv[0].getNormal();
n[1] = cv[1].getNormal();
n[2] = cv[2].getNormal();
n += 3;
tc[0] = cv[0].mTexCoord;
tc[1] = cv[1].mTexCoord;
tc[2] = cv[2].mTexCoord;
tc += 3;
//side 4
cv[0].setPosition(p[2]);
cv[1].setPosition(p[0]);
cv[2].setPosition(p[3]);
tetrahedron_set_normal(cv);
v[0] = cv[0].getPosition();
v[1] = cv[1].getPosition();
v[2] = cv[2].getPosition();
v += 3;
n[0] = cv[0].getNormal();
n[1] = cv[1].getNormal();
n[2] = cv[2].getNormal();
n += 3;
tc[0] = cv[0].mTexCoord;
tc[1] = cv[1].mTexCoord;
tc[2] = cv[2].mTexCoord;
tc += 3;
//set index buffer
for (U16 i = 0; i < 12; i++)
{
face.mIndices[i] = i;
}
mVolumeFaces.push_back(face);
mSculptLevel = 0;
mIsTetrahedron = TRUE;
}
void LLVolume::copyVolumeFaces(const LLVolume* volume)
{
mVolumeFaces = volume->mVolumeFaces;
mSculptLevel = 0;
mIsTetrahedron = FALSE;
}
void LLVolume::cacheOptimize()
{
for (S32 i = 0; i < mVolumeFaces.size(); ++i)
{
mVolumeFaces[i].cacheOptimize();
}
}
#endif //MESH_ENABLED
S32 LLVolume::getNumFaces() const
{
#if MESH_ENABLED
U8 sculpt_type = (mParams.getSculptType() & LL_SCULPT_TYPE_MASK);
if (sculpt_type == LL_SCULPT_TYPE_MESH)
{
return LL_SCULPT_MESH_MAX_FACES;
}
#endif //MESH_ENABLED
return (S32)mProfilep->mFaces.size();
}
@@ -2854,6 +3314,13 @@ bool LLVolumeParams::isSculpt() const
return mSculptID.notNull();
}
#if MESH_ENABLED
bool LLVolumeParams::isMeshSculpt() const
{
return isSculpt() && ((mSculptType & LL_SCULPT_TYPE_MASK) == LL_SCULPT_TYPE_MESH);
}
#endif //MESH_ENABLED
bool LLVolumeParams::operator==(const LLVolumeParams &params) const
{
return ( (getPathParams() == params.getPathParams()) &&
@@ -3999,6 +4466,13 @@ void LLVolume::generateSilhouetteVertices(std::vector<LLVector3> &vertices,
vertices.clear();
normals.clear();
#if MESH_ENABLED
if ((mParams.getSculptType() & LL_SCULPT_TYPE_MASK) == LL_SCULPT_TYPE_MESH)
{
return;
}
#endif //MESH_ENABLED
S32 cur_index = 0;
//for each face
for (face_list_t::iterator iter = mVolumeFaces.begin();
@@ -4191,7 +4665,6 @@ void LLVolume::generateSilhouetteVertices(std::vector<LLVector3> &vertices,
norm_mat.rotate(n[v2], t);
t.normalize3fast();
normals.push_back(LLVector3(t[0], t[1], t[2]));
}
}
}
@@ -5072,6 +5545,9 @@ LLVolumeFace::LLVolumeFace(const LLVolumeFace& src)
mBinormals(NULL),
mTexCoords(NULL),
mIndices(NULL),
#if MESH_ENABLED
mWeights(NULL),
#endif //MESH_ENABLED
mOctree(NULL)
{
mExtents = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*3);
@@ -5128,6 +5604,18 @@ LLVolumeFace& LLVolumeFace::operator=(const LLVolumeFace& src)
mBinormals = NULL;
}
#if MESH_ENABLED
if (src.mWeights)
{
allocateWeights(src.mNumVertices);
LLVector4a::memcpyNonAliased16((F32*) mWeights, (F32*) src.mWeights, vert_size);
}
else
{
ll_aligned_free_16(mWeights);
mWeights = NULL;
}
#endif //MESH_ENABLED
}
if (mNumIndices)
{
@@ -5160,6 +5648,10 @@ void LLVolumeFace::freeData()
mIndices = NULL;
ll_aligned_free_16(mBinormals);
mBinormals = NULL;
#if MESH_ENABLED
ll_aligned_free_16(mWeights);
mWeights = NULL;
#endif //MESH_ENABLED
delete mOctree;
mOctree = NULL;
@@ -5685,6 +6177,13 @@ void LLVolumeFace::cacheOptimize()
S32 size = ((num_verts*sizeof(LLVector2)) + 0xF) & ~0xF;
LLVector2* tc = (LLVector2*) ll_aligned_malloc_16(size);
#if MESH_ENABLED
LLVector4a* wght = NULL;
if (mWeights)
{
wght = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*num_verts);
}
#endif //MESH_ENABLED
LLVector4a* binorm = NULL;
if (mBinormals)
@@ -5708,6 +6207,12 @@ void LLVolumeFace::cacheOptimize()
pos[cur_idx] = mPositions[idx];
norm[cur_idx] = mNormals[idx];
tc[cur_idx] = mTexCoords[idx];
#if MESH_ENABLED
if (mWeights)
{
wght[cur_idx] = mWeights[idx];
}
#endif //MESH_ENABLED
if (mBinormals)
{
binorm[cur_idx] = mBinormals[idx];
@@ -5725,11 +6230,17 @@ void LLVolumeFace::cacheOptimize()
ll_aligned_free_16(mPositions);
ll_aligned_free_16(mNormals);
ll_aligned_free_16(mTexCoords);
#if MESH_ENABLED
ll_aligned_free_16(mWeights);
#endif //MESH_ENABLED
ll_aligned_free_16(mBinormals);
mPositions = pos;
mNormals = norm;
mTexCoords = tc;
#if MESH_ENABLED
mWeights = wght;
#endif //MESH_ENABLED
mBinormals = binorm;
//std::string result = llformat("ACMR pre/post: %.3f/%.3f -- %d triangles %d breaks", pre_acmr, post_acmr, mNumIndices/3, breaks);
@@ -6512,6 +7023,14 @@ void LLVolumeFace::allocateBinormals(S32 num_verts)
mBinormals = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*num_verts);
}
#if MESH_ENABLED
void LLVolumeFace::allocateWeights(S32 num_verts)
{
ll_aligned_free_16(mWeights);
mWeights = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*num_verts);
}
#endif //MESH_ENABLED
void LLVolumeFace::resizeIndices(S32 num_indices)
{
ll_aligned_free_16(mIndices);
@@ -6673,8 +7192,9 @@ BOOL LLVolumeFace::createSide(LLVolume* volume, BOOL partial_build)
{
resizeVertices(num_vertices);
resizeIndices(num_indices);
//if ((volume->getParams().getSculptType() & LL_SCULPT_TYPE_MASK) != LL_SCULPT_TYPE_MESH)
#if MESH_ENABLED
if ((volume->getParams().getSculptType() & LL_SCULPT_TYPE_MASK) != LL_SCULPT_TYPE_MESH)
#endif //MESH_ENABLED
{
mEdge.resize(num_indices);
}

View File

@@ -191,12 +191,21 @@ const U8 LL_SCULPT_TYPE_SPHERE = 1;
const U8 LL_SCULPT_TYPE_TORUS = 2;
const U8 LL_SCULPT_TYPE_PLANE = 3;
const U8 LL_SCULPT_TYPE_CYLINDER = 4;
#if MESH_ENABLED
const U8 LL_SCULPT_TYPE_MESH = 5;
const 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;
#endif //MESH_ENABLED
#if !MESH_ENABLED
const U8 LL_SCULPT_TYPE_MASK = LL_SCULPT_TYPE_SPHERE | LL_SCULPT_TYPE_TORUS | LL_SCULPT_TYPE_PLANE | LL_SCULPT_TYPE_CYLINDER;
#endif //!MESH_ENABLED
const U8 LL_SCULPT_FLAG_INVERT = 64;
const U8 LL_SCULPT_FLAG_MIRROR = 128;
#if MESH_ENABLED
const S32 LL_SCULPT_MESH_MAX_FACES = 8;
#endif //MESH_ENABLED
class LLProfileParams
{
@@ -646,6 +655,9 @@ public:
const LLUUID& getSculptID() const { return mSculptID; }
const U8& getSculptType() const { return mSculptType; }
bool isSculpt() const;
#if MESH_ENABLED
bool isMeshSculpt() const;
#endif //MESH_ENABLED
BOOL isConvex() const;
// 'begin' and 'end' should be in range [0, 1] (they will be clamped)
@@ -853,6 +865,9 @@ public:
void resizeVertices(S32 num_verts);
void allocateBinormals(S32 num_verts);
#if MESH_ENABLED
void allocateWeights(S32 num_verts);
#endif //MESH_ENABLED
void resizeIndices(S32 num_indices);
void fillFromLegacyData(std::vector<LLVolumeFace::VertexData>& v, std::vector<U16>& idx);
@@ -923,6 +938,14 @@ public:
U16* mIndices;
std::vector<S32> mEdge;
#if MESH_ENABLED
//list of skin weights for rigged volumes
// format is mWeights[vertex_index].mV[influence] = <joint_index>.<weight>
// mWeights.size() should be empty or match mVertices.size()
LLVector4a* mWeights;
#endif //MESH_ENABLED
LLOctreeNode<LLVolumeTriangle>* mOctree;
private:
@@ -1053,11 +1076,20 @@ private:
protected:
BOOL generate();
void createVolumeFaces();
#if MESH_ENABLED
public:
virtual bool unpackVolumeFaces(std::istream& is, S32 size);
virtual void makeTetrahedron();
virtual BOOL isTetrahedron();
#endif //MESH_ENABLED
protected:
BOOL mUnique;
F32 mDetail;
S32 mSculptLevel;
#if MESH_ENABLED
BOOL mIsTetrahedron;
#endif //MESH_ENABLED
LLVolumeParams mParams;
LLPath *mPathp;
@@ -1067,6 +1099,14 @@ protected:
BOOL mGenerateSingleFace;
typedef std::vector<LLVolumeFace> face_list_t;
face_list_t mVolumeFaces;
#if MESH_ENABLED
public:
LLVector4a* mHullPoints;
U16* mHullIndices;
S32 mNumHullPoints;
S32 mNumHullIndices;
#endif //MESH_ENABLED
};
std::ostream& operator<<(std::ostream &s, const LLVolumeParams &volume_params);

View File

@@ -39,6 +39,9 @@
const F32 MAX_OBJECT_Z = 4096.f; // should match REGION_HEIGHT_METERS, Pre-havok4: 768.f
const F32 MIN_OBJECT_Z = -256.f;
const F32 DEFAULT_MAX_PRIM_SCALE = 256.f;
#if MESH_ENABLED
const F32 DEFAULT_MAX_PRIM_SCALE_NO_MESH = DEFAULT_MAX_PRIM_SCALE;
#endif //MESH_ENABLED
const F32 MIN_PRIM_SCALE = 0.01f;
const F32 MAX_PRIM_SCALE = 65536.f; // something very high but not near FLT_MAX

View File

@@ -17,6 +17,7 @@ include_directories(
set(llprimitive_SOURCE_FILES
llmaterialtable.cpp
#llmodel.cpp
llprimitive.cpp
llprimtexturelist.cpp
lltextureanim.cpp
@@ -32,6 +33,7 @@ set(llprimitive_HEADER_FILES
legacy_object_types.h
llmaterialtable.h
#llmodel.h
llprimitive.h
llprimtexturelist.h
lltextureanim.h

File diff suppressed because it is too large Load Diff

257
indra/llprimitive/llmodel.h Normal file
View File

@@ -0,0 +1,257 @@
/**
* @file llmodel.h
* @brief Model handling class definitions
*
* $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_LLMODEL_H
#define LL_LLMODEL_H
#include "llpointer.h"
#include "llvolume.h"
#include "v4math.h"
#include "m4math.h"
class daeElement;
class domMesh;
#define MAX_MODEL_FACES 8
class LLMeshSkinInfo
{
public:
LLUUID mMeshID;
std::vector<std::string> mJointNames;
std::vector<LLMatrix4> mInvBindMatrix;
std::vector<LLMatrix4> mAlternateBindMatrix;
std::map<std::string, U32> mJointMap;
LLMeshSkinInfo() { }
LLMeshSkinInfo(LLSD& data);
void fromLLSD(LLSD& data);
LLSD asLLSD(bool include_joints) const;
LLMatrix4 mBindShapeMatrix;
float mPelvisOffset;
};
class LLModel : public LLVolume
{
public:
enum
{
LOD_IMPOSTOR = 0,
LOD_LOW,
LOD_MEDIUM,
LOD_HIGH,
LOD_PHYSICS,
NUM_LODS
};
enum EModelStatus
{
NO_ERRORS = 0,
VERTEX_NUMBER_OVERFLOW, //vertex number is >= 65535.
BAD_ELEMENT,
INVALID_STATUS
} ;
//convex_hull_decomposition is a vector of convex hulls
//each convex hull is a set of points
typedef std::vector<std::vector<LLVector3> > convex_hull_decomposition;
typedef std::vector<LLVector3> hull;
class PhysicsMesh
{
public:
std::vector<LLVector3> mPositions;
std::vector<LLVector3> mNormals;
void clear()
{
mPositions.clear();
mNormals.clear();
}
bool empty() const
{
return mPositions.empty();
}
};
class Decomposition
{
public:
Decomposition() { }
Decomposition(LLSD& data);
void fromLLSD(LLSD& data);
LLSD asLLSD() const;
bool hasHullList() const;
void merge(const Decomposition* rhs);
LLUUID mMeshID;
LLModel::convex_hull_decomposition mHull;
LLModel::hull mBaseHull;
std::vector<LLModel::PhysicsMesh> mMesh;
LLModel::PhysicsMesh mBaseHullMesh;
LLModel::PhysicsMesh mPhysicsShapeMesh;
};
LLModel(LLVolumeParams& params, F32 detail);
~LLModel();
bool loadModel(std::istream& is);
bool loadSkinInfo(LLSD& header, std::istream& is);
bool loadDecomposition(LLSD& header, std::istream& is);
static LLSD writeModel(
std::ostream& ostr,
LLModel* physics,
LLModel* high,
LLModel* medium,
LLModel* low,
LLModel* imposotr,
const LLModel::Decomposition& decomp,
BOOL upload_skin,
BOOL upload_joints,
BOOL nowrite = FALSE);
static LLSD writeModelToStream(
std::ostream& ostr,
LLSD& mdl,
BOOL nowrite = FALSE);
static LLModel* loadModelFromDomMesh(domMesh* mesh);
static std::string getElementLabel(daeElement* element);
std::string getName() const;
EModelStatus getStatus() const {return mStatus;}
static std::string getStatusString(U32 status) ;
void appendFaces(LLModel* model, LLMatrix4& transform, LLMatrix4& normal_transform);
void appendFace(const LLVolumeFace& src_face, std::string src_material, LLMatrix4& mat, LLMatrix4& norm_mat);
void setNumVolumeFaces(S32 count);
void setVolumeFaceData(
S32 f,
LLStrider<LLVector3> pos,
LLStrider<LLVector3> norm,
LLStrider<LLVector2> tc,
LLStrider<U16> ind,
U32 num_verts,
U32 num_indices);
void generateNormals(F32 angle_cutoff);
void addFace(const LLVolumeFace& face);
void normalizeVolumeFaces();
void optimizeVolumeFaces();
void offsetMesh( const LLVector3& pivotPoint );
void getNormalizedScaleTranslation(LLVector3& scale_out, LLVector3& translation_out);
std::vector<std::string> mMaterialList;
//data used for skin weights
class JointWeight
{
public:
S32 mJointIdx;
F32 mWeight;
JointWeight()
{
mJointIdx = 0;
mWeight = 0.f;
}
JointWeight(S32 idx, F32 weight)
: mJointIdx(idx), mWeight(weight)
{
}
bool operator<(const JointWeight& rhs) const
{
if (mWeight == rhs.mWeight)
{
return mJointIdx < rhs.mJointIdx;
}
return mWeight < rhs.mWeight;
}
};
struct CompareWeightGreater
{
bool operator()(const JointWeight& lhs, const JointWeight& rhs)
{
return rhs < lhs; // strongest = first
}
};
//copy of position array for this model -- mPosition[idx].mV[X,Y,Z]
std::vector<LLVector3> mPosition;
//map of positions to skin weights --- mSkinWeights[pos].mV[0..4] == <joint_index>.<weight>
//joint_index corresponds to mJointList
typedef std::vector<JointWeight> weight_list;
typedef std::map<LLVector3, weight_list > weight_map;
weight_map mSkinWeights;
//get list of weight influences closest to given position
weight_list& getJointInfluences(const LLVector3& pos);
LLMeshSkinInfo mSkinInfo;
std::string mRequestedLabel; // name requested in UI, if any.
std::string mLabel; // name computed from dae.
LLVector3 mNormalizedScale;
LLVector3 mNormalizedTranslation;
float mPelvisOffset;
// convex hull decomposition
S32 mDecompID;
void setConvexHullDecomposition(
const convex_hull_decomposition& decomp);
void updateHullCenters();
LLVector3 mCenterOfHullCenters;
std::vector<LLVector3> mHullCenter;
U32 mHullPoints;
//ID for storing this model in a .slm file
S32 mLocalID;
Decomposition mPhysics;
EModelStatus mStatus ;
protected:
void addVolumeFacesFromDomMesh(domMesh* mesh);
virtual BOOL createVolumeFacesFromDomMesh(domMesh *mesh);
};
#endif //LL_LLMODEL_H

View File

@@ -573,7 +573,9 @@ BOOL LLAgentCamera::calcCameraMinDistance(F32 &obj_min_distance)
BOOL soft_limit = FALSE; // is the bounding box to be treated literally (volumes) or as an approximation (avatars)
if (!mFocusObject || mFocusObject->isDead() ||
//mFocusObject->isMesh() ||
#if MESH_ENABLED
mFocusObject->isMesh() ||
#endif //MESH_ENABLED
gSavedSettings.getBOOL("DisableCameraConstraints"))
{
obj_min_distance = 0.f;

View File

@@ -52,6 +52,9 @@
#include "llwindow.h"
#include "llviewerstats.h"
#include "llmd5.h"
#if MESH_ENABLED
#include "llmeshrepository.h"
#endif //MESH_ENABLED
#include "llpumpio.h"
#include "llimpanel.h"
#include "llmimetypes.h"
@@ -1110,6 +1113,9 @@ bool LLAppViewer::mainLoop()
break;
}
}
#if MESH_ENABLED
gMeshRepo.update() ;
#endif //MESH_ENABLED
if ((LLStartUp::getStartupState() >= STATE_CLEANUP) &&
(frameTimer.getElapsedTimeF64() > FRAME_STALL_THRESHOLD))
{
@@ -1233,6 +1239,11 @@ bool LLAppViewer::cleanup()
llinfos << "Cleaning Up" << llendflush;
#if MESH_ENABLED
// shut down mesh streamer
gMeshRepo.shutdown();
#endif //MESH_ENABLED
// Must clean up texture references before viewer window is destroyed.
if(LLHUDManager::instanceExists())
{
@@ -1655,6 +1666,11 @@ bool LLAppViewer::initThreads()
LLAppViewer::sTextureFetch = new LLTextureFetch(LLAppViewer::getTextureCache(), sImageDecodeThread, enable_threads && true);
LLImage::initClass();
#if MESH_ENABLED
// Mesh streaming and caching
gMeshRepo.init();
#endif //MESH_ENABLED
// *FIX: no error handling here!
return true;
}

View File

@@ -269,6 +269,9 @@ public:
REBUILD_GEOMETRY= REBUILD_POSITION|REBUILD_TCOORD|REBUILD_COLOR,
REBUILD_MATERIAL= REBUILD_TCOORD|REBUILD_COLOR,
REBUILD_ALL = REBUILD_GEOMETRY|REBUILD_VOLUME,
#if MESH_ENABLED
REBUILD_RIGGED = 0x00008000,
#endif //MESH_ENABLED
ON_SHIFT_LIST = 0x00010000,
BLOCKER = 0x00020000,
ACTIVE = 0x00040000,
@@ -280,6 +283,9 @@ public:
CLEAR_INVISIBLE = 0x01000000, // clear FORCE_INVISIBLE next draw frame
REBUILD_SHADOW = 0x02000000,
HAS_ALPHA = 0x04000000,
#if MESH_ENABLED
RIGGED = 0x08000000,
#endif //MESH_ENABLED
PARTITION_MOVE = 0x10000000,
} EDrawableFlags;

File diff suppressed because it is too large Load Diff

View File

@@ -36,6 +36,12 @@
#include "lldrawpool.h"
class LLVOAvatar;
class LLGLSLShader;
class LLFace;
class LLMeshSkinInfo;
class LLVolume;
class LLVolumeFace;
class LLDrawPoolAvatar : public LLFacePool
{
@@ -102,6 +108,105 @@ public:
void endDeferredImpostor();
void endDeferredRigid();
void endDeferredSkinned();
void beginPostDeferredAlpha();
void endPostDeferredAlpha();
#if MESH_ENABLED
void beginRiggedSimple();
void beginRiggedFullbright();
void beginRiggedFullbrightShiny();
void beginRiggedShinySimple();
void beginRiggedAlpha();
void beginRiggedFullbrightAlpha();
void beginRiggedGlow();
void beginDeferredRiggedAlpha();
void endRiggedSimple();
void endRiggedFullbright();
void endRiggedFullbrightShiny();
void endRiggedShinySimple();
void endRiggedAlpha();
void endRiggedFullbrightAlpha();
void endRiggedGlow();
void endDeferredRiggedAlpha();
void beginDeferredRiggedSimple();
void beginDeferredRiggedBump();
void endDeferredRiggedSimple();
void endDeferredRiggedBump();
void updateRiggedFaceVertexBuffer(LLVOAvatar* avatar,
LLFace* facep,
const LLMeshSkinInfo* skin,
LLVolume* volume,
const LLVolumeFace& vol_face);
void renderRigged(LLVOAvatar* avatar, U32 type, bool glow = false);
void renderRiggedSimple(LLVOAvatar* avatar);
void renderRiggedAlpha(LLVOAvatar* avatar);
void renderRiggedFullbrightAlpha(LLVOAvatar* avatar);
void renderRiggedFullbright(LLVOAvatar* avatar);
void renderRiggedShinySimple(LLVOAvatar* avatar);
void renderRiggedFullbrightShiny(LLVOAvatar* avatar);
void renderRiggedGlow(LLVOAvatar* avatar);
void renderDeferredRiggedSimple(LLVOAvatar* avatar);
void renderDeferredRiggedBump(LLVOAvatar* avatar);
typedef enum
{
RIGGED_SIMPLE = 0,
RIGGED_FULLBRIGHT,
RIGGED_SHINY,
RIGGED_FULLBRIGHT_SHINY,
RIGGED_GLOW,
RIGGED_ALPHA,
RIGGED_FULLBRIGHT_ALPHA,
RIGGED_DEFERRED_BUMP,
RIGGED_DEFERRED_SIMPLE,
NUM_RIGGED_PASSES,
RIGGED_UNKNOWN,
} eRiggedPass;
typedef enum
{
RIGGED_SIMPLE_MASK = LLVertexBuffer::MAP_VERTEX |
LLVertexBuffer::MAP_NORMAL |
LLVertexBuffer::MAP_TEXCOORD0 |
LLVertexBuffer::MAP_COLOR |
LLVertexBuffer::MAP_WEIGHT4,
RIGGED_FULLBRIGHT_MASK = LLVertexBuffer::MAP_VERTEX |
LLVertexBuffer::MAP_TEXCOORD0 |
LLVertexBuffer::MAP_COLOR |
LLVertexBuffer::MAP_WEIGHT4,
RIGGED_SHINY_MASK = RIGGED_SIMPLE_MASK,
RIGGED_FULLBRIGHT_SHINY_MASK = RIGGED_SIMPLE_MASK,
RIGGED_GLOW_MASK = LLVertexBuffer::MAP_VERTEX |
LLVertexBuffer::MAP_TEXCOORD0 |
LLVertexBuffer::MAP_WEIGHT4,
RIGGED_ALPHA_MASK = RIGGED_SIMPLE_MASK,
RIGGED_FULLBRIGHT_ALPHA_MASK = RIGGED_FULLBRIGHT_MASK,
RIGGED_DEFERRED_BUMP_MASK = LLVertexBuffer::MAP_VERTEX |
LLVertexBuffer::MAP_NORMAL |
LLVertexBuffer::MAP_TEXCOORD0 |
LLVertexBuffer::MAP_BINORMAL |
LLVertexBuffer::MAP_COLOR |
LLVertexBuffer::MAP_WEIGHT4,
RIGGED_DEFERRED_SIMPLE_MASK = LLVertexBuffer::MAP_VERTEX |
LLVertexBuffer::MAP_NORMAL |
LLVertexBuffer::MAP_TEXCOORD0 |
LLVertexBuffer::MAP_COLOR |
LLVertexBuffer::MAP_WEIGHT4,
} eRiggedDataMask;
void addRiggedFace(LLFace* facep, U32 type);
void removeRiggedFace(LLFace* facep);
std::vector<LLFace*> mRiggedFace[NUM_RIGGED_PASSES];
#endif //MESH_ENABLED
/*virtual*/ LLViewerTexture *getDebugTexture();
/*virtual*/ LLColor3 getDebugColor() const; // For AGP debug display
@@ -110,6 +215,9 @@ public:
static BOOL sSkipOpaque;
static BOOL sSkipTransparent;
static S32 sDiffuseChannel;
static LLGLSLShader* sVertexProgram;
};
class LLVertexBufferAvatar : public LLVertexBuffer

View File

@@ -205,7 +205,17 @@ void LLFace::destroy()
if (mDrawPoolp)
{
mDrawPoolp->removeFace(this);
#if MESH_ENABLED
if (this->isState(LLFace::RIGGED) && mDrawPoolp->getType() == LLDrawPool::POOL_AVATAR)
{
((LLDrawPoolAvatar*) mDrawPoolp)->removeRiggedFace(this);
}
else
#endif //MESH_ENABLED
{
mDrawPoolp->removeFace(this);
}
mDrawPoolp = NULL;
}
@@ -550,11 +560,40 @@ void LLFace::renderSelected(LLViewerTexture *imagep, const LLColor4& color)
}
glColor4fv(color.mV);
mVertexBuffer->setBuffer(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0);
#if !LL_RELEASE_FOR_DOWNLOAD
LLGLState::checkClientArrays("", LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0);
#endif
mVertexBuffer->draw(LLRender::TRIANGLES, mIndicesCount, mIndicesIndex);
#if MESH_ENABLED
if (mDrawablep->isState(LLDrawable::RIGGED))
{
LLVOVolume* volume = mDrawablep->getVOVolume();
if (volume)
{
LLRiggedVolume* rigged = volume->getRiggedVolume();
if (rigged)
{
LLGLEnable offset(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(-1.f, -1.f);
glMultMatrixf((F32*) volume->getRelativeXform().mMatrix);
const LLVolumeFace& vol_face = rigged->getVolumeFace(getTEOffset());
LLVertexBuffer::unbind();
glVertexPointer(3, GL_FLOAT, 16, vol_face.mPositions);
if (vol_face.mTexCoords)
{
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(2, GL_FLOAT, 8, vol_face.mTexCoords);
}
glDrawElements(GL_TRIANGLES, vol_face.mNumIndices, GL_UNSIGNED_SHORT, vol_face.mIndices);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}
}
}
else
#endif //MESH_ENABLED
{
mVertexBuffer->setBuffer(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0);
#if !LL_RELEASE_FOR_DOWNLOAD
LLGLState::checkClientArrays("", LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0);
#endif
mVertexBuffer->draw(LLRender::TRIANGLES, mIndicesCount, mIndicesIndex);
}
gGL.popMatrix();
}
@@ -733,7 +772,11 @@ BOOL LLFace::genVolumeBBoxes(const LLVolume &volume, S32 f,
LLMemType mt1(LLMemType::MTYPE_DRAWABLE);
//get bounding box
if (mDrawablep->isState(LLDrawable::REBUILD_VOLUME | LLDrawable::REBUILD_POSITION))
if (mDrawablep->isState(LLDrawable::REBUILD_VOLUME | LLDrawable::REBUILD_POSITION
#if MESH_ENABLED
| LLDrawable::REBUILD_RIGGED
#endif //MESH_ENABLED
))
{
//VECTORIZE THIS
LLMatrix4a mat_vert;
@@ -1096,6 +1139,9 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume,
LLStrider<LLColor4U> colors;
LLStrider<LLVector3> binormals;
LLStrider<U16> indicesp;
#if MESH_ENABLED
LLStrider<LLVector3> weights;
#endif //MESH_ENABLED
BOOL full_rebuild = force_rebuild || mDrawablep->isState(LLDrawable::REBUILD_VOLUME);
@@ -1115,7 +1161,9 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume,
bool rebuild_tcoord = full_rebuild || mDrawablep->isState(LLDrawable::REBUILD_TCOORD);
bool rebuild_normal = rebuild_pos && mVertexBuffer->hasDataType(LLVertexBuffer::TYPE_NORMAL);
bool rebuild_binormal = rebuild_pos && mVertexBuffer->hasDataType(LLVertexBuffer::TYPE_BINORMAL);
#if MESH_ENABLED
bool rebuild_weights = rebuild_pos && mVertexBuffer->hasDataType(LLVertexBuffer::TYPE_WEIGHT4);
#endif //MESH_ENABLED
const LLTextureEntry *tep = mVObjp->getTE(f);
if (!tep) rebuild_color = FALSE; // can't get color when tep is NULL
@@ -1133,6 +1181,12 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume,
{
mVertexBuffer->getBinormalStrider(binormals, mGeomIndex);
}
#if MESH_ENABLED
if (rebuild_weights)
{
mVertexBuffer->getWeight4Strider(weights, mGeomIndex);
}
#endif //MESH_ENABLED
if (rebuild_tcoord)
{
@@ -1538,6 +1592,15 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume,
}
}
#if MESH_ENABLED
if (rebuild_weights && vf.mWeights)
{
for (S32 i = 0; i < num_vertices; i++)
{
weights[i].set(weights.getF32ptr());
}
}
#endif //MESH_ENABLED
if (rebuild_color)
{
@@ -1959,3 +2022,68 @@ void LLFace::clearVertexBuffer()
mLastVertexBuffer = NULL;
}
#if MESH_ENABLED
//static
U32 LLFace::getRiggedDataMask(U32 type)
{
static const U32 rigged_data_mask[] = {
LLDrawPoolAvatar::RIGGED_SIMPLE_MASK,
LLDrawPoolAvatar::RIGGED_FULLBRIGHT_MASK,
LLDrawPoolAvatar::RIGGED_SHINY_MASK,
LLDrawPoolAvatar::RIGGED_FULLBRIGHT_SHINY_MASK,
LLDrawPoolAvatar::RIGGED_GLOW_MASK,
LLDrawPoolAvatar::RIGGED_ALPHA_MASK,
LLDrawPoolAvatar::RIGGED_FULLBRIGHT_ALPHA_MASK,
LLDrawPoolAvatar::RIGGED_DEFERRED_BUMP_MASK,
LLDrawPoolAvatar::RIGGED_DEFERRED_SIMPLE_MASK,
};
llassert(type < sizeof(rigged_data_mask)/sizeof(U32));
return rigged_data_mask[type];
}
U32 LLFace::getRiggedVertexBufferDataMask() const
{
U32 data_mask = 0;
for (U32 i = 0; i < mRiggedIndex.size(); ++i)
{
if (mRiggedIndex[i] > -1)
{
data_mask |= LLFace::getRiggedDataMask(i);
}
}
return data_mask;
}
S32 LLFace::getRiggedIndex(U32 type) const
{
if (mRiggedIndex.empty())
{
return -1;
}
llassert(type < mRiggedIndex.size());
return mRiggedIndex[type];
}
void LLFace::setRiggedIndex(U32 type, S32 index)
{
if (mRiggedIndex.empty())
{
mRiggedIndex.resize(LLDrawPoolAvatar::NUM_RIGGED_PASSES);
for (U32 i = 0; i < mRiggedIndex.size(); ++i)
{
mRiggedIndex[i] = -1;
}
}
llassert(type < mRiggedIndex.size());
mRiggedIndex[type] = index;
}
#endif //MESH_ENABLED

View File

@@ -83,6 +83,9 @@ public:
HUD_RENDER = 0x0008,
USE_FACE_COLOR = 0x0010,
TEXTURE_ANIM = 0x0020,
#if MESH_ENABLED
RIGGED = 0x0040,
#endif //MESH_ENABLED
};
static void initClass();
@@ -212,6 +215,13 @@ public:
void setVertexBuffer(LLVertexBuffer* buffer);
void clearVertexBuffer(); //sets mVertexBuffer and mLastVertexBuffer to NULL
LLVertexBuffer* getVertexBuffer() const { return mVertexBuffer; }
#if MESH_ENABLED
U32 getRiggedVertexBufferDataMask() const;
S32 getRiggedIndex(U32 type) const;
void setRiggedIndex(U32 type, S32 index);
static U32 getRiggedDataMask(U32 type);
#endif //MESH_ENABLED
public: //aligned members
LLVector4a mExtents[2];
@@ -264,6 +274,10 @@ private:
S32 mTEOffset;
S32 mReferenceIndex;
#if MESH_ENABLED
std::vector<S32> mRiggedIndex;
#endif //MESH_ENABLED
F32 mVSize;
F32 mPixelArea;

View File

@@ -91,6 +91,28 @@ const LLManip::EManipPart MANIPULATOR_IDS[NUM_MANIPULATORS] =
};
F32 get_default_max_prim_scale(bool is_flora)
{
// a bit of a hack, but if it's foilage, we don't want to use the
// new larger scale which would result in giant trees and grass
#if MESH_ENABLED
if (gSavedSettings.getBOOL("MeshEnabled") &&
gAgent.getRegion() &&
!gAgent.getRegion()->getCapability("GetMesh").empty() &&
!gAgent.getRegion()->getCapability("ObjectAdd").empty() &&
!is_flora)
{
return DEFAULT_MAX_PRIM_SCALE;
}
else
{
return DEFAULT_MAX_PRIM_SCALE_NO_MESH;
}
#endif //MESH_ENABLED
#if !MESH_ENABLED
return DEFAULT_MAX_PRIM_SCALE;
#endif //!MESH_ENABLED
}
// static
void LLManipScale::setUniform(BOOL b)
@@ -953,8 +975,8 @@ void LLManipScale::dragCorner( S32 x, S32 y )
mInSnapRegime = FALSE;
}
F32 max_scale_factor = DEFAULT_MAX_PRIM_SCALE / MIN_PRIM_SCALE;
F32 min_scale_factor = MIN_PRIM_SCALE / DEFAULT_MAX_PRIM_SCALE;
F32 max_scale_factor = get_default_max_prim_scale() / MIN_PRIM_SCALE;
F32 min_scale_factor = MIN_PRIM_SCALE / get_default_max_prim_scale();
// find max and min scale factors that will make biggest object hit max absolute scale and smallest object hit min absolute scale
for (LLObjectSelection::iterator iter = mObjectSelection->begin();
@@ -966,7 +988,7 @@ void LLManipScale::dragCorner( S32 x, S32 y )
{
const LLVector3& scale = selectNode->mSavedScale;
F32 cur_max_scale_factor = llmin( DEFAULT_MAX_PRIM_SCALE / scale.mV[VX], DEFAULT_MAX_PRIM_SCALE / scale.mV[VY], DEFAULT_MAX_PRIM_SCALE / scale.mV[VZ] );
F32 cur_max_scale_factor = llmin( get_default_max_prim_scale(LLPickInfo::isFlora(cur)) / scale.mV[VX], get_default_max_prim_scale(LLPickInfo::isFlora(cur)) / scale.mV[VY], get_default_max_prim_scale(LLPickInfo::isFlora(cur)) / scale.mV[VZ] );
max_scale_factor = llmin( max_scale_factor, cur_max_scale_factor );
F32 cur_min_scale_factor = llmax( MIN_PRIM_SCALE / scale.mV[VX], MIN_PRIM_SCALE / scale.mV[VY], MIN_PRIM_SCALE / scale.mV[VZ] );
@@ -1263,7 +1285,7 @@ void LLManipScale::stretchFace( const LLVector3& drag_start_agent, const LLVecto
F32 denom = axis * dir_local;
F32 desired_delta_size = is_approx_zero(denom) ? 0.f : (delta_local_mag / denom); // in meters
F32 desired_scale = llclamp(selectNode->mSavedScale.mV[axis_index] + desired_delta_size, MIN_PRIM_SCALE, DEFAULT_MAX_PRIM_SCALE);
F32 desired_scale = llclamp(selectNode->mSavedScale.mV[axis_index] + desired_delta_size, MIN_PRIM_SCALE, get_default_max_prim_scale(LLPickInfo::isFlora(cur)));
// propagate scale constraint back to position offset
desired_delta_size = desired_scale - selectNode->mSavedScale.mV[axis_index]; // propagate constraint back to position
@@ -1964,7 +1986,7 @@ F32 LLManipScale::partToMaxScale( S32 part, const LLBBox &bbox ) const
max_extent = bbox_extents.mV[i];
}
}
max_scale_factor = bbox_extents.magVec() * DEFAULT_MAX_PRIM_SCALE / max_extent;
max_scale_factor = bbox_extents.magVec() * get_default_max_prim_scale() / max_extent;
if (getUniform())
{
@@ -1979,7 +2001,7 @@ F32 LLManipScale::partToMinScale( S32 part, const LLBBox &bbox ) const
{
LLVector3 bbox_extents = unitVectorToLocalBBoxExtent( partToUnitVector( part ), bbox );
bbox_extents.abs();
F32 min_extent = DEFAULT_MAX_PRIM_SCALE;
F32 min_extent = get_default_max_prim_scale();
for (U32 i = VX; i <= VZ; i++)
{
if (bbox_extents.mV[i] > 0.f && bbox_extents.mV[i] < min_extent)

View File

@@ -45,6 +45,9 @@
#include "llviewerobject.h"
#include "llbbox.h"
F32 get_default_max_prim_scale(bool is_flora = false);
class LLToolComposite;
class LLColor4;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,552 @@
/**
* @file llmeshrepository.h
* @brief Client-side repository of mesh assets.
*
* $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_MESH_REPOSITORY_H
#define LL_MESH_REPOSITORY_H
#include "llassettype.h"
#include "llmodel.h"
#include "lluuid.h"
#include "llviewertexture.h"
#include "llvolume.h"
#define LLCONVEXDECOMPINTER_STATIC 1
#include "llconvexdecomposition.h"
class LLVOVolume;
class LLMeshResponder;
class LLCurlRequest;
class LLMutex;
class LLCondition;
class LLVFS;
class LLMeshRepository;
class LLMeshUploadData
{
public:
LLPointer<LLModel> mBaseModel;
LLPointer<LLModel> mModel[5];
LLUUID mUUID;
U32 mRetries;
std::string mRSVP;
std::string mAssetData;
LLSD mPostData;
LLMeshUploadData()
{
mRetries = 0;
}
};
class LLTextureUploadData
{
public:
LLViewerFetchedTexture* mTexture;
LLUUID mUUID;
std::string mRSVP;
std::string mLabel;
U32 mRetries;
std::string mAssetData;
LLSD mPostData;
LLTextureUploadData()
{
mRetries = 0;
}
LLTextureUploadData(LLViewerFetchedTexture* texture, std::string& label)
: mTexture(texture), mLabel(label)
{
mRetries = 0;
}
};
class LLImportMaterial
{
public:
LLPointer<LLViewerFetchedTexture> mDiffuseMap;
std::string mDiffuseMapFilename;
std::string mDiffuseMapLabel;
LLColor4 mDiffuseColor;
bool mFullbright;
bool operator<(const LLImportMaterial &params) const;
LLImportMaterial()
: mFullbright(false)
{
mDiffuseColor.set(1,1,1,1);
}
LLImportMaterial(LLSD& data);
LLSD asLLSD();
};
class LLModelInstance
{
public:
LLPointer<LLModel> mModel;
LLPointer<LLModel> mLOD[5];
std::string mLabel;
LLUUID mMeshID;
S32 mLocalMeshID;
LLMatrix4 mTransform;
std::vector<LLImportMaterial> mMaterial;
LLModelInstance(LLModel* model, const std::string& label, LLMatrix4& transform, std::vector<LLImportMaterial>& materials)
: mModel(model), mLabel(label), mTransform(transform), mMaterial(materials)
{
mLocalMeshID = -1;
}
LLModelInstance(LLSD& data);
LLSD asLLSD();
};
class LLPhysicsDecomp : public LLThread
{
public:
typedef std::map<std::string, LLSD> decomp_params;
class Request : public LLRefCount
{
public:
//input params
S32* mDecompID;
std::string mStage;
std::vector<LLVector3> mPositions;
std::vector<U16> mIndices;
decomp_params mParams;
//output state
std::string mStatusMessage;
std::vector<LLModel::PhysicsMesh> mHullMesh;
LLModel::convex_hull_decomposition mHull;
//status message callback, called from decomposition thread
virtual S32 statusCallback(const char* status, S32 p1, S32 p2) = 0;
//completed callback, called from the main thread
virtual void completed() = 0;
virtual void setStatusMessage(const std::string& msg);
};
LLCondition* mSignal;
LLMutex* mMutex;
bool mInited;
bool mQuitting;
bool mDone;
LLPhysicsDecomp();
~LLPhysicsDecomp();
void shutdown();
void submitRequest(Request* request);
static S32 llcdCallback(const char*, S32, S32);
void cancel();
void setMeshData(LLCDMeshData& mesh);
void doDecomposition();
void doDecompositionSingleHull();
virtual void run();
void completeCurrent();
void notifyCompleted();
std::map<std::string, S32> mStageID;
typedef std::queue<LLPointer<Request> > request_queue;
request_queue mRequestQ;
LLPointer<Request> mCurRequest;
std::queue<LLPointer<Request> > mCompletedQ;
};
class LLMeshRepoThread : public LLThread
{
public:
static S32 sActiveHeaderRequests;
static S32 sActiveLODRequests;
static U32 sMaxConcurrentRequests;
LLCurlRequest* mCurlRequest;
LLMutex* mMutex;
LLMutex* mHeaderMutex;
LLCondition* mSignal;
bool mWaiting;
//map of known mesh headers
typedef std::map<LLUUID, LLSD> mesh_header_map;
mesh_header_map mMeshHeader;
std::map<LLUUID, U32> mMeshHeaderSize;
std::map<LLUUID, U32> mMeshResourceCost;
class HeaderRequest
{
public:
const LLVolumeParams mMeshParams;
HeaderRequest(const LLVolumeParams& mesh_params)
: mMeshParams(mesh_params)
{
}
bool operator<(const HeaderRequest& rhs) const
{
return mMeshParams < rhs.mMeshParams;
}
};
class LODRequest
{
public:
LLVolumeParams mMeshParams;
S32 mLOD;
F32 mScore;
LODRequest(const LLVolumeParams& mesh_params, S32 lod)
: mMeshParams(mesh_params), mLOD(lod), mScore(0.f)
{
}
};
struct CompareScoreGreater
{
bool operator()(const LODRequest& lhs, const LODRequest& rhs)
{
return lhs.mScore > rhs.mScore; // greatest = first
}
};
class LoadedMesh
{
public:
LLPointer<LLVolume> mVolume;
LLVolumeParams mMeshParams;
S32 mLOD;
LoadedMesh(LLVolume* volume, const LLVolumeParams& mesh_params, S32 lod)
: mVolume(volume), mMeshParams(mesh_params), mLOD(lod)
{
}
};
//set of requested skin info
std::set<LLUUID> mSkinRequests;
//queue of completed skin info requests
std::queue<LLMeshSkinInfo> mSkinInfoQ;
//set of requested decompositions
std::set<LLUUID> mDecompositionRequests;
//set of requested physics shapes
std::set<LLUUID> mPhysicsShapeRequests;
//queue of completed Decomposition info requests
std::queue<LLModel::Decomposition*> mDecompositionQ;
//queue of requested headers
std::queue<HeaderRequest> mHeaderReqQ;
//queue of requested LODs
std::queue<LODRequest> mLODReqQ;
//queue of unavailable LODs (either asset doesn't exist or asset doesn't have desired LOD)
std::queue<LODRequest> mUnavailableQ;
//queue of successfully loaded meshes
std::queue<LoadedMesh> mLoadedQ;
//map of pending header requests and currently desired LODs
typedef std::map<LLVolumeParams, std::vector<S32> > pending_lod_map;
pending_lod_map mPendingLOD;
static std::string constructUrl(LLUUID mesh_id);
LLMeshRepoThread();
~LLMeshRepoThread();
virtual void run();
void loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod);
bool fetchMeshHeader(const LLVolumeParams& mesh_params);
bool fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod);
bool headerReceived(const LLVolumeParams& mesh_params, U8* data, S32 data_size);
bool lodReceived(const LLVolumeParams& mesh_params, S32 lod, U8* data, S32 data_size);
bool skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 data_size);
bool decompositionReceived(const LLUUID& mesh_id, U8* data, S32 data_size);
bool physicsShapeReceived(const LLUUID& mesh_id, U8* data, S32 data_size);
LLSD& getMeshHeader(const LLUUID& mesh_id);
void notifyLoadedMeshes();
S32 getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lod);
U32 getResourceCost(const LLUUID& mesh_params);
void loadMeshSkinInfo(const LLUUID& mesh_id);
void loadMeshDecomposition(const LLUUID& mesh_id);
void loadMeshPhysicsShape(const LLUUID& mesh_id);
//send request for skin info, returns true if header info exists
// (should hold onto mesh_id and try again later if header info does not exist)
bool fetchMeshSkinInfo(const LLUUID& mesh_id);
//send request for decomposition, returns true if header info exists
// (should hold onto mesh_id and try again later if header info does not exist)
bool fetchMeshDecomposition(const LLUUID& mesh_id);
//send request for PhysicsShape, returns true if header info exists
// (should hold onto mesh_id and try again later if header info does not exist)
bool fetchMeshPhysicsShape(const LLUUID& mesh_id);
};
class LLMeshUploadThread : public LLThread
{
public:
class DecompRequest : public LLPhysicsDecomp::Request
{
public:
LLPointer<LLModel> mModel;
LLPointer<LLModel> mBaseModel;
LLMeshUploadThread* mThread;
DecompRequest(LLModel* mdl, LLModel* base_model, LLMeshUploadThread* thread);
S32 statusCallback(const char* status, S32 p1, S32 p2) { return 1; }
void completed();
};
LLPointer<DecompRequest> mFinalDecomp;
bool mPhysicsComplete;
typedef std::map<LLPointer<LLModel>, std::vector<LLVector3> > hull_map;
hull_map mHullMap;
typedef std::vector<LLModelInstance> instance_list;
instance_list mInstanceList;
typedef std::map<LLPointer<LLModel>, instance_list> instance_map;
instance_map mInstance;
LLMutex* mMutex;
LLCurlRequest* mCurlRequest;
S32 mPendingConfirmations;
S32 mPendingUploads;
S32 mPendingCost;
LLVector3 mOrigin;
bool mFinished;
bool mUploadTextures;
bool mUploadSkin;
bool mUploadJoints;
BOOL mDiscarded ;
LLHost mHost;
std::string mUploadObjectAssetCapability;
std::string mNewInventoryCapability;
std::string mWholeModelFeeCapability;
std::string mWholeModelUploadURL;
std::queue<LLMeshUploadData> mUploadQ;
std::queue<LLMeshUploadData> mConfirmedQ;
std::queue<LLModelInstance> mInstanceQ;
std::queue<LLTextureUploadData> mTextureQ;
std::queue<LLTextureUploadData> mConfirmedTextureQ;
std::map<LLViewerFetchedTexture*, LLTextureUploadData> mTextureMap;
LLMeshUploadThread(instance_list& data, LLVector3& scale, bool upload_textures,
bool upload_skin, bool upload_joints);
~LLMeshUploadThread();
void uploadTexture(LLTextureUploadData& data);
void doUploadTexture(LLTextureUploadData& data);
void sendCostRequest(LLTextureUploadData& data);
void priceResult(LLTextureUploadData& data, const LLSD& content);
void onTextureUploaded(LLTextureUploadData& data);
void uploadModel(LLMeshUploadData& data);
void sendCostRequest(LLMeshUploadData& data);
void doUploadModel(LLMeshUploadData& data);
void onModelUploaded(LLMeshUploadData& data);
void createObjects(LLMeshUploadData& data);
LLSD createObject(LLModelInstance& instance);
void priceResult(LLMeshUploadData& data, const LLSD& content);
bool finished() { return mFinished; }
virtual void run();
void preStart();
void discard() ;
BOOL isDiscarded();
void doWholeModelUpload();
void doIterativeUpload();
void wholeModelToLLSD(LLSD& dest, bool include_textures);
void decomposeMeshMatrix(LLMatrix4& transformation,
LLVector3& result_pos,
LLQuaternion& result_rot,
LLVector3& result_scale);
};
class LLMeshRepository
{
public:
//metrics
static U32 sBytesReceived;
static U32 sHTTPRequestCount;
static U32 sHTTPRetryCount;
static U32 sCacheBytesRead;
static U32 sCacheBytesWritten;
static U32 sPeakKbps;
static F32 getStreamingCost(LLSD& header, F32 radius, S32* bytes = NULL, S32* visible_bytes = NULL, S32 detail = -1);
LLMeshRepository();
void init();
void shutdown();
S32 update() ;
//mesh management functions
S32 loadMesh(LLVOVolume* volume, const LLVolumeParams& mesh_params, S32 detail = 0, S32 last_lod = -1);
void notifyLoadedMeshes();
void notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVolume* volume);
void notifyMeshUnavailable(const LLVolumeParams& mesh_params, S32 lod);
void notifySkinInfoReceived(LLMeshSkinInfo& info);
void notifyDecompositionReceived(LLModel::Decomposition* info);
S32 getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lod);
static S32 getActualMeshLOD(LLSD& header, S32 lod);
U32 calcResourceCost(LLSD& header);
U32 getResourceCost(const LLUUID& mesh_params);
const LLMeshSkinInfo* getSkinInfo(const LLUUID& mesh_id);
LLModel::Decomposition* getDecomposition(const LLUUID& mesh_id);
void fetchPhysicsShape(const LLUUID& mesh_id);
bool hasPhysicsShape(const LLUUID& mesh_id);
void buildHull(const LLVolumeParams& params, S32 detail);
void buildPhysicsMesh(LLModel::Decomposition& decomp);
LLSD& getMeshHeader(const LLUUID& mesh_id);
void uploadModel(std::vector<LLModelInstance>& data, LLVector3& scale, bool upload_textures,
bool upload_skin, bool upload_joints);
S32 getMeshSize(const LLUUID& mesh_id, S32 lod);
typedef std::map<LLVolumeParams, std::set<LLUUID> > mesh_load_map;
mesh_load_map mLoadingMeshes[4];
typedef std::map<LLUUID, LLMeshSkinInfo> skin_map;
skin_map mSkinMap;
typedef std::map<LLUUID, LLModel::Decomposition*> decomposition_map;
decomposition_map mDecompositionMap;
LLMutex* mMeshMutex;
std::vector<LLMeshRepoThread::LODRequest> mPendingRequests;
//list of mesh ids awaiting skin info
std::set<LLUUID> mLoadingSkins;
//list of mesh ids that need to send skin info fetch requests
std::queue<LLUUID> mPendingSkinRequests;
//list of mesh ids awaiting decompositions
std::set<LLUUID> mLoadingDecompositions;
//list of mesh ids that need to send decomposition fetch requests
std::queue<LLUUID> mPendingDecompositionRequests;
//list of mesh ids awaiting physics shapes
std::set<LLUUID> mLoadingPhysicsShapes;
//list of mesh ids that need to send physics shape fetch requests
std::queue<LLUUID> mPendingPhysicsShapeRequests;
U32 mMeshThreadCount;
void cacheOutgoingMesh(LLMeshUploadData& data, LLSD& header);
LLMeshRepoThread* mThread;
std::vector<LLMeshUploadThread*> mUploads;
std::vector<LLMeshUploadThread*> mUploadWaitList;
LLPhysicsDecomp* mDecompThread;
class inventory_data
{
public:
LLSD mPostData;
LLSD mResponse;
inventory_data(const LLSD& data, const LLSD& content)
: mPostData(data), mResponse(content)
{
}
};
std::queue<inventory_data> mInventoryQ;
std::queue<LLSD> mUploadErrorQ;
void uploadError(LLSD& args);
void updateInventory(inventory_data data);
std::string mGetMeshCapability;
};
extern LLMeshRepository gMeshRepo;
#endif

View File

@@ -0,0 +1,210 @@
/**
* @file llphysicsshapebuilder.cpp
* @brief Generic system to convert LL(Physics)VolumeParams to physics shapes
*
* $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$
*/
#include "llviewerprecompiledheaders.h"
#include "llphysicsshapebuilderutil.h"
/* static */
void LLPhysicsShapeBuilderUtil::determinePhysicsShape( const LLPhysicsVolumeParams& volume_params, const LLVector3& scale, PhysicsShapeSpecification& specOut )
{
const LLProfileParams& profile_params = volume_params.getProfileParams();
const LLPathParams& path_params = volume_params.getPathParams();
specOut.mScale = scale;
const F32 avgScale = ( scale[VX] + scale[VY] + scale[VZ] )/3.0f;
// count the scale elements that are small
S32 min_size_counts = 0;
for (S32 i = 0; i < 3; ++i)
{
if (scale[i] < SHAPE_BUILDER_CONVEXIFICATION_SIZE)
{
++min_size_counts;
}
}
const bool profile_complete = ( profile_params.getBegin() <= SHAPE_BUILDER_IMPLICIT_THRESHOLD_PATH_CUT/avgScale ) &&
( profile_params.getEnd() >= (1.0f - SHAPE_BUILDER_IMPLICIT_THRESHOLD_PATH_CUT/avgScale) );
const bool path_complete = ( path_params.getBegin() <= SHAPE_BUILDER_IMPLICIT_THRESHOLD_PATH_CUT/avgScale ) &&
( path_params.getEnd() >= (1.0f - SHAPE_BUILDER_IMPLICIT_THRESHOLD_PATH_CUT/avgScale) );
const bool simple_params = ( volume_params.getHollow() <= SHAPE_BUILDER_IMPLICIT_THRESHOLD_HOLLOW/avgScale )
&& ( fabs(path_params.getShearX()) <= SHAPE_BUILDER_IMPLICIT_THRESHOLD_SHEAR/avgScale )
&& ( fabs(path_params.getShearY()) <= SHAPE_BUILDER_IMPLICIT_THRESHOLD_SHEAR/avgScale )
&& ( !volume_params.isMeshSculpt() && !volume_params.isSculpt() );
if (simple_params && profile_complete)
{
// Try to create an implicit shape or convexified
bool no_taper = ( fabs(path_params.getScaleX() - 1.0f) <= SHAPE_BUILDER_IMPLICIT_THRESHOLD_TAPER/avgScale )
&& ( fabs(path_params.getScaleY() - 1.0f) <= SHAPE_BUILDER_IMPLICIT_THRESHOLD_TAPER/avgScale );
bool no_twist = ( fabs(path_params.getTwistBegin()) <= SHAPE_BUILDER_IMPLICIT_THRESHOLD_TWIST/avgScale )
&& ( fabs(path_params.getTwistEnd()) <= SHAPE_BUILDER_IMPLICIT_THRESHOLD_TWIST/avgScale);
// Box
if(
( profile_params.getCurveType() == LL_PCODE_PROFILE_SQUARE )
&& ( path_params.getCurveType() == LL_PCODE_PATH_LINE )
&& no_taper
&& no_twist
)
{
specOut.mType = PhysicsShapeSpecification::BOX;
if ( path_complete )
{
return;
}
else
{
// Side lengths
specOut.mScale[VX] = llmax( scale[VX], SHAPE_BUILDER_MIN_GEOMETRY_SIZE );
specOut.mScale[VY] = llmax( scale[VY], SHAPE_BUILDER_MIN_GEOMETRY_SIZE );
specOut.mScale[VZ] = llmax( scale[VZ] * (path_params.getEnd() - path_params.getBegin()), SHAPE_BUILDER_MIN_GEOMETRY_SIZE );
specOut.mCenter.set( 0.f, 0.f, 0.5f * scale[VZ] * ( path_params.getEnd() + path_params.getBegin() - 1.0f ) );
return;
}
}
// Sphere
if( path_complete
&& ( profile_params.getCurveType() == LL_PCODE_PROFILE_CIRCLE_HALF )
&& ( path_params.getCurveType() == LL_PCODE_PATH_CIRCLE )
&& ( fabs(volume_params.getTaper()) <= SHAPE_BUILDER_IMPLICIT_THRESHOLD_TAPER/avgScale )
&& no_twist
)
{
if ( ( scale[VX] == scale[VZ] )
&& ( scale[VY] == scale[VZ] ) )
{
// perfect sphere
specOut.mType = PhysicsShapeSpecification::SPHERE;
specOut.mScale = scale;
return;
}
else if (min_size_counts > 1)
{
// small or narrow sphere -- we can boxify
for (S32 i=0; i<3; ++i)
{
if (specOut.mScale[i] < SHAPE_BUILDER_CONVEXIFICATION_SIZE)
{
// reduce each small dimension size to split the approximation errors
specOut.mScale[i] *= 0.75f;
}
}
specOut.mType = PhysicsShapeSpecification::BOX;
return;
}
}
// Cylinder
if( (scale[VX] == scale[VY])
&& ( profile_params.getCurveType() == LL_PCODE_PROFILE_CIRCLE )
&& ( path_params.getCurveType() == LL_PCODE_PATH_LINE )
&& ( volume_params.getBeginS() <= SHAPE_BUILDER_IMPLICIT_THRESHOLD_PATH_CUT/avgScale )
&& ( volume_params.getEndS() >= (1.0f - SHAPE_BUILDER_IMPLICIT_THRESHOLD_PATH_CUT/avgScale) )
&& no_taper
)
{
if (min_size_counts > 1)
{
// small or narrow sphere -- we can boxify
for (S32 i=0; i<3; ++i)
{
if (specOut.mScale[i] < SHAPE_BUILDER_CONVEXIFICATION_SIZE)
{
// reduce each small dimension size to split the approximation errors
specOut.mScale[i] *= 0.75f;
}
}
specOut.mType = PhysicsShapeSpecification::BOX;
}
else
{
specOut.mType = PhysicsShapeSpecification::CYLINDER;
F32 length = (volume_params.getPathParams().getEnd() - volume_params.getPathParams().getBegin()) * scale[VZ];
specOut.mScale[VY] = specOut.mScale[VX];
specOut.mScale[VZ] = length;
// The minus one below fixes the fact that begin and end range from 0 to 1 not -1 to 1.
specOut.mCenter.set( 0.f, 0.f, 0.5f * (volume_params.getPathParams().getBegin() + volume_params.getPathParams().getEnd() - 1.f) * scale[VZ] );
}
return;
}
}
if ( (min_size_counts == 3 )
// Possible dead code here--who wants to take it out?
|| (path_complete
&& profile_complete
&& ( path_params.getCurveType() == LL_PCODE_PATH_LINE )
&& (min_size_counts > 1 ) )
)
{
// it isn't simple but
// we might be able to convexify this shape if the path and profile are complete
// or the path is linear and both path and profile are complete --> we can boxify it
specOut.mType = PhysicsShapeSpecification::BOX;
specOut.mScale = scale;
return;
}
// Special case for big, very thin objects - bump the small dimensions up to the COLLISION_TOLERANCE
if (min_size_counts == 1 // One dimension is small
&& avgScale > 3.f) // ... but others are fairly large
{
for (S32 i = 0; i < 3; ++i)
{
specOut.mScale[i] = llmax( specOut.mScale[i], COLLISION_TOLERANCE );
}
}
if ( volume_params.shouldForceConvex() )
{
specOut.mType = PhysicsShapeSpecification::USER_CONVEX;
}
// Make a simpler convex shape if we can.
else if (volume_params.isConvex() // is convex
|| min_size_counts > 1 ) // two or more small dimensions
{
specOut.mType = PhysicsShapeSpecification::PRIM_CONVEX;
}
else if ( volume_params.isSculpt() ) // Is a sculpt of any kind (mesh or legacy)
{
specOut.mType = volume_params.isMeshSculpt() ? PhysicsShapeSpecification::USER_MESH : PhysicsShapeSpecification::SCULPT;
}
else // Resort to mesh
{
specOut.mType = PhysicsShapeSpecification::PRIM_MESH;
}
}

View File

@@ -0,0 +1,138 @@
/**
* @file llphysicsshapebuilder.h
* @brief Generic system to convert LL(Physics)VolumeParams to physics shapes
*
* $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_PHYSICS_SHAPE_BUILDER_H
#define LL_PHYSICS_SHAPE_BUILDER_H
#include "indra_constants.h"
#include "llvolume.h"
#define USE_SHAPE_QUANTIZATION 0
#define SHAPE_BUILDER_DEFAULT_VOLUME_DETAIL 1
#define SHAPE_BUILDER_IMPLICIT_THRESHOLD_HOLLOW 0.10f
#define SHAPE_BUILDER_IMPLICIT_THRESHOLD_HOLLOW_SPHERES 0.90f
#define SHAPE_BUILDER_IMPLICIT_THRESHOLD_PATH_CUT 0.05f
#define SHAPE_BUILDER_IMPLICIT_THRESHOLD_TAPER 0.05f
#define SHAPE_BUILDER_IMPLICIT_THRESHOLD_TWIST 0.09f
#define SHAPE_BUILDER_IMPLICIT_THRESHOLD_SHEAR 0.05f
const F32 SHAPE_BUILDER_ENTRY_SNAP_SCALE_BIN_SIZE = 0.15f;
const F32 SHAPE_BUILDER_ENTRY_SNAP_PARAMETER_BIN_SIZE = 0.010f;
const F32 SHAPE_BUILDER_CONVEXIFICATION_SIZE = 2.f * COLLISION_TOLERANCE;
const F32 SHAPE_BUILDER_MIN_GEOMETRY_SIZE = 0.5f * COLLISION_TOLERANCE;
class LLPhysicsVolumeParams : public LLVolumeParams
{
public:
LLPhysicsVolumeParams( const LLVolumeParams& params, bool forceConvex ) :
LLVolumeParams( params ),
mForceConvex(forceConvex) {}
bool operator==(const LLPhysicsVolumeParams &params) const
{
return ( LLVolumeParams::operator==(params) && (mForceConvex == params.mForceConvex) );
}
bool operator!=(const LLPhysicsVolumeParams &params) const
{
return !operator==(params);
}
bool operator<(const LLPhysicsVolumeParams &params) const
{
if ( LLVolumeParams::operator!=(params) )
{
return LLVolumeParams::operator<(params);
}
return (params.mForceConvex == false) && (mForceConvex == true);
}
bool shouldForceConvex() const { return mForceConvex; }
private:
bool mForceConvex;
};
class LLPhysicsShapeBuilderUtil
{
public:
class PhysicsShapeSpecification
{
public:
enum ShapeType
{
// Primitive types
BOX,
SPHERE,
CYLINDER,
USER_CONVEX, // User specified they wanted the convex hull of the volume
PRIM_CONVEX, // Either a volume that is inherently convex but not a primitive type, or a shape
// with dimensions such that will convexify it anyway.
SCULPT, // Special case for traditional sculpts--they are the convex hull of a single particular set of volume params
USER_MESH, // A user mesh. May or may not contain a convex decomposition.
PRIM_MESH, // A non-convex volume which we have to represent accurately
INVALID
};
PhysicsShapeSpecification() :
mType( INVALID ),
mScale( 0.f, 0.f, 0.f ),
mCenter( 0.f, 0.f, 0.f ) {}
bool isConvex() { return (mType != USER_MESH && mType != PRIM_MESH && mType != INVALID); }
bool isMesh() { return (mType == USER_MESH) || (mType == PRIM_MESH); }
ShapeType getType() { return mType; }
const LLVector3& getScale() { return mScale; }
const LLVector3& getCenter() { return mCenter; }
private:
friend class LLPhysicsShapeBuilderUtil;
ShapeType mType;
// Dimensions of an AABB around the shape
LLVector3 mScale;
// Offset of shape from origin of primitive's reference frame
LLVector3 mCenter;
};
static void determinePhysicsShape( const LLPhysicsVolumeParams& volume_params, const LLVector3& scale, PhysicsShapeSpecification& specOut );
};
#endif //LL_PHYSICS_SHAPE_BUILDER_H

View File

@@ -5457,6 +5457,111 @@ BOOL LLSelectNode::allowOperationOnNode(PermissionBit op, U64 group_proxy_power)
return (mPermissions->allowOperationBy(op, proxy_agent_id, group_id));
}
#if MESH_ENABLED
void pushWireframe(LLDrawable* drawable)
{
if (drawable->isState(LLDrawable::RIGGED))
{ //render straight from rigged volume if this is a rigged attachment
LLVOVolume* vobj = drawable->getVOVolume();
if (vobj)
{
vobj->updateRiggedVolume();
LLRiggedVolume* rigged_volume = vobj->getRiggedVolume();
if (rigged_volume)
{
LLVertexBuffer::unbind();
gGL.pushMatrix();
glMultMatrixf((F32*) vobj->getRelativeXform().mMatrix);
for (S32 i = 0; i < rigged_volume->getNumVolumeFaces(); ++i)
{
const LLVolumeFace& face = rigged_volume->getVolumeFace(i);
glVertexPointer(3, GL_FLOAT, 16, face.mPositions);
glDrawElements(GL_TRIANGLES, face.mNumIndices, GL_UNSIGNED_SHORT, face.mIndices);
}
gGL.popMatrix();
}
}
}
else
{
for (S32 i = 0; i < drawable->getNumFaces(); ++i)
{
LLFace* face = drawable->getFace(i);
if (face->verify())
{
pushVerts(face, LLVertexBuffer::MAP_VERTEX);
}
}
}
}
void LLSelectNode::renderOneWireframe(const LLColor4& color)
{
LLViewerObject* objectp = getObject();
if (!objectp)
{
return;
}
LLDrawable* drawable = objectp->mDrawable;
if(!drawable)
{
return;
}
glMatrixMode(GL_MODELVIEW);
gGL.pushMatrix();
BOOL is_hud_object = objectp->isHUDAttachment();
if (drawable->isActive())
{
glLoadMatrixd(gGLModelView);
glMultMatrixf((F32*) objectp->getRenderMatrix().mMatrix);
}
else if (!is_hud_object)
{
glLoadIdentity();
glMultMatrixd(gGLModelView);
LLVector3 trans = objectp->getRegion()->getOriginAgent();
glTranslatef(trans.mV[0], trans.mV[1], trans.mV[2]);
}
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
if (LLSelectMgr::sRenderHiddenSelections) // && gFloaterTools && gFloaterTools->getVisible())
{
gGL.blendFunc(LLRender::BF_SOURCE_COLOR, LLRender::BF_ONE);
LLGLEnable fog(GL_FOG);
glFogi(GL_FOG_MODE, GL_LINEAR);
float d = (LLViewerCamera::getInstance()->getPointOfInterest()-LLViewerCamera::getInstance()->getOrigin()).magVec();
LLColor4 fogCol = color * (F32)llclamp((LLSelectMgr::getInstance()->getSelectionCenterGlobal()-gAgentCamera.getCameraPositionGlobal()).magVec()/(LLSelectMgr::getInstance()->getBBoxOfSelection().getExtentLocal().magVec()*4), 0.0, 1.0);
glFogf(GL_FOG_START, d);
glFogf(GL_FOG_END, d*(1 + (LLViewerCamera::getInstance()->getView() / LLViewerCamera::getInstance()->getDefaultFOV())));
glFogfv(GL_FOG_COLOR, fogCol.mV);
LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE, GL_GEQUAL);
gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT);
{
glColor4f(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE], 0.4f);
pushWireframe(drawable);
}
}
gGL.flush();
gGL.setSceneBlendType(LLRender::BT_ALPHA);
glColor4f(color.mV[VRED]*2, color.mV[VGREEN]*2, color.mV[VBLUE]*2, LLSelectMgr::sHighlightAlpha*2);
LLGLEnable offset(GL_POLYGON_OFFSET_LINE);
glPolygonOffset(3.f, 3.f);
glLineWidth(3.f);
pushWireframe(drawable);
glLineWidth(1.f);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
gGL.popMatrix();
}
#endif MESH_ENABLED
//-----------------------------------------------------------------------------
// renderOneSilhouette()
//-----------------------------------------------------------------------------
@@ -5474,6 +5579,15 @@ void LLSelectNode::renderOneSilhouette(const LLColor4 &color)
return;
}
#if MESH_ENABLED
LLVOVolume* vobj = drawable->getVOVolume();
if (vobj && vobj->isMesh())
{
renderOneWireframe(color);
return;
}
#endif //MESH_ENABLED
if (!mSilhouetteExists)
{
return;

View File

@@ -139,6 +139,9 @@ public:
BOOL isTESelected(S32 te_index);
S32 getLastSelectedTE();
S32 getTESelectMask() { return mTESelectMask; }
#if MESH_ENABLED
void renderOneWireframe(const LLColor4& color);
#endif
void renderOneSilhouette(const LLColor4 &color);
void setTransient(BOOL transient) { mTransient = transient; }
BOOL isTransient() { return mTransient; }

View File

@@ -2999,7 +2999,12 @@ void LLViewerObject::boostTexturePriority(BOOL boost_children /* = TRUE */)
getTEImage(i)->setBoostLevel(LLViewerTexture::BOOST_SELECTED);
}
if (isSculpted())
if (isSculpted()
#if MESH_ENABLED
&& !isMesh()
#endif //MESH_ENABLED
)
{
LLSculptParams *sculpt_params = (LLSculptParams *)getParameterEntry(LLNetworkData::PARAMS_SCULPT);
LLUUID sculpt_id = sculpt_params->getSculptTexture();
@@ -3239,6 +3244,16 @@ const LLVector3 LLViewerObject::getPositionEdit() const
const LLVector3 LLViewerObject::getRenderPosition() const
{
#if MESH_ENABLED
if (mDrawable.notNull() && mDrawable->isState(LLDrawable::RIGGED))
{
LLVOAvatar* avatar = getAvatar();
if (avatar)
{
return avatar->getPositionAgent();
}
}
#endif //MESH_ENABLED
if (mDrawable.isNull() || mDrawable->getGeneration() < 0)
{
return getPositionAgent();
@@ -3257,6 +3272,12 @@ const LLVector3 LLViewerObject::getPivotPositionAgent() const
const LLQuaternion LLViewerObject::getRenderRotation() const
{
LLQuaternion ret;
#if MESH_ENABLED
if (mDrawable.notNull() && mDrawable->isState(LLDrawable::RIGGED))
{
return ret;
}
#endif //MESH_ENABLED
if (mDrawable.isNull() || mDrawable->isStatic())
{
ret = getRotationEdit();

View File

@@ -1495,6 +1495,11 @@ void LLViewerRegion::setSeedCapability(const std::string& url)
}
capabilityNames.append("GetDisplayNames");
capabilityNames.append("GetTexture");
#if MESH_ENABLED
capabilityNames.append("GetMesh");
capabilityNames.append("GetObjectCost");
capabilityNames.append("GetObjectPhysicsData");
#endif //MESH_ENABLED
capabilityNames.append("GroupProposalBallot");
capabilityNames.append("HomeLocation");

View File

@@ -39,6 +39,9 @@
#include "llagent.h"
#include "llagentcamera.h"
#if MESH_ENABLED
#include "llmeshrepository.h"
#endif //MESH_ENABLED
#include "llpanellogin.h"
#include "llviewerkeyboard.h"
#include "llviewerwindow.h"
@@ -507,6 +510,24 @@ public:
ypos += y_inc;
#if MESH_ENABLED
if (gSavedSettings.getBOOL("MeshEnabled"))
{
addText(xpos, ypos, llformat("%.3f MB Mesh Data Received", LLMeshRepository::sBytesReceived/(1024.f*1024.f)));
ypos += y_inc;
addText(xpos, ypos, llformat("%d/%d Mesh HTTP Requests/Retries", LLMeshRepository::sHTTPRequestCount,
LLMeshRepository::sHTTPRetryCount));
ypos += y_inc;
addText(xpos, ypos, llformat("%.3f/%.3f MB Mesh Cache Read/Write ", LLMeshRepository::sCacheBytesRead/(1024.f*1024.f), LLMeshRepository::sCacheBytesWritten/(1024.f*1024.f)));
ypos += y_inc;
}
#endif //MESH_ENABLED
LLVertexBuffer::sBindCount = LLImageGL::sBindCount =
LLVertexBuffer::sSetCount = LLImageGL::sUniqueCount =
gPipeline.mNumVisibleNodes = LLPipeline::sVisibleLightCount = 0;

View File

@@ -63,6 +63,10 @@
#include "llkeyframefallmotion.h"
#include "llkeyframestandmotion.h"
#include "llkeyframewalkmotion.h"
#if MESH_ENABLED
#include "llmanipscale.h" // for get_default_max_prim_scale()
#include "llmeshrepository.h"
#endif //MESH_ENABLED
#include "llmutelist.h"
#include "llnotify.h"
#include "llquantize.h"
@@ -1618,7 +1622,11 @@ void LLVOAvatar::getSpatialExtents(LLVector4a& newMin, LLVector4a& newMax)
if (attached_object && !attached_object->isHUDAttachment())
{
LLDrawable* drawable = attached_object->mDrawable;
if (drawable)
if (drawable
#if MESH_ENABLED
&& !drawable->isState(LLDrawable::RIGGED))
#endif //MESH_ENABLED
)
{
LLSpatialBridge* bridge = drawable->getSpatialBridge();
if (bridge)
@@ -4943,6 +4951,7 @@ bool LLVOAvatar::shouldAlphaMask()
}
//------------------------------------------------------------------------
// needsRenderBeam()
//------------------------------------------------------------------------
@@ -7162,6 +7171,56 @@ void LLVOAvatar::resetHUDAttachments()
}
}
#if MESH_ENABLED
void LLVOAvatar::rebuildRiggedAttachments( void )
{
for ( attachment_map_t::iterator iter = mAttachmentPoints.begin(); iter != mAttachmentPoints.end(); ++iter )
{
LLViewerJointAttachment* pAttachment = iter->second;
LLViewerJointAttachment::attachedobjs_vec_t::iterator attachmentIterEnd = pAttachment->mAttachedObjects.end();
for ( LLViewerJointAttachment::attachedobjs_vec_t::iterator attachmentIter = pAttachment->mAttachedObjects.begin();
attachmentIter != attachmentIterEnd; ++attachmentIter)
{
const LLViewerObject* pAttachedObject = *attachmentIter;
if ( pAttachment && pAttachedObject->mDrawable.notNull() )
{
gPipeline.markRebuild(pAttachedObject->mDrawable);
}
}
}
}
//-----------------------------------------------------------------------------
// cleanupAttachedMesh()
//-----------------------------------------------------------------------------
void LLVOAvatar::cleanupAttachedMesh( LLViewerObject* pVO )
{
//If a VO has a skin that we'll reset the joint positions to their default
if ( pVO && pVO->mDrawable )
{
LLVOVolume* pVObj = pVO->mDrawable->getVOVolume();
if ( pVObj )
{
const LLMeshSkinInfo* pSkinData = gMeshRepo.getSkinInfo( pVObj->getVolume()->getParams().getSculptID() );
if ( pSkinData )
{
const int jointCnt = pSkinData->mJointNames.size();
bool fullRig = ( jointCnt>=20 ) ? true : false;
if ( fullRig )
{
const int bindCnt = pSkinData->mAlternateBindMatrix.size();
if ( bindCnt > 0 )
{
LLVOAvatar::resetJointPositionsToDefault();
}
}
}
}
}
}
#endif //MESH_ENABLED
//-----------------------------------------------------------------------------
// detachObject()
//-----------------------------------------------------------------------------
@@ -10508,6 +10567,13 @@ BOOL LLVOAvatar::updateLOD()
return res;
}
#if MESH_ENABLED
void LLVOAvatar::updateLODRiggedAttachments( void )
{
updateLOD();
rebuildRiggedAttachments();
}
#endif //MESH_ENABLED
U32 LLVOAvatar::getPartitionType() const
{
// Avatars merely exist as drawables in the bridge partition

View File

@@ -124,6 +124,9 @@ public:
/*virtual*/ BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time);
virtual BOOL updateLOD();
BOOL updateJointLODs();
#if MESH_ENABLED
void updateLODRiggedAttachments( void );
#endif //MESH_ENABLED
/*virtual*/ BOOL isActive() const; // Whether this object needs to do an idleUpdate.
/*virtual*/ void updateTextures();
/*virtual*/ S32 setTETexture(const U8 te, const LLUUID& uuid); // If setting a baked texture, need to request it from a non-local sim.
@@ -722,12 +725,18 @@ public:
void clampAttachmentPositions();
BOOL attachObject(LLViewerObject *viewer_object);
BOOL detachObject(LLViewerObject *viewer_object);
#if MESH_ENABLED
void cleanupAttachedMesh( LLViewerObject* pVO );
#endif //MESH_ENABLED
static LLVOAvatar* findAvatarFromAttachment( LLViewerObject* obj );
protected:
// [RLVa:KB] - Checked: 2009-12-18 (RLVa-1.1.0i) | Added: RLVa-1.1.0i
LLViewerJointAttachment* getTargetAttachmentPoint(const LLViewerObject* viewer_object) const;
// [/RLVa:KB]
void lazyAttach();
#if MESH_ENABLED
void rebuildRiggedAttachments( void );
#endif //MESH_ENABLED
//--------------------------------------------------------------------
// Map of attachment points, by ID

View File

@@ -42,12 +42,13 @@
#include "llmaterialtable.h"
#include "llprimitive.h"
#include "llvolume.h"
#include "llvolumeoctree.h"
#include "llvolumemgr.h"
#include "llvolumemessage.h"
#include "material_codes.h"
#include "message.h"
#include "object_flags.h"
#include "llagent.h"
#include "llagentconstants.h"
#include "lldrawable.h"
#include "lldrawpoolbump.h"
#include "llface.h"
@@ -59,11 +60,21 @@
#include "llvector4a.h"
#include "llviewercamera.h"
#include "llviewertexturelist.h"
#include "llviewerobjectlist.h"
#include "llviewerregion.h"
#include "llviewertextureanim.h"
#include "llworld.h"
#include "llselectmgr.h"
#include "pipeline.h"
#include "llsdutil.h"
#include "llmatrix4a.h"
#include "llagent.h"
#if MESH_ENABLED
#include "lldrawpoolavatar.h
#include "llmeshrepository.h"
#include "lldatapacker.h"
#include "llvoavatar.h"
#endif //MESH_ENABLED
#include "llvocache.h"
// [RLVa:KB] - Checked: 2010-04-04 (RLVa-1.2.0d)
@@ -730,8 +741,35 @@ LLDrawable *LLVOVolume::createDrawable(LLPipeline *pipeline)
return mDrawable;
}
BOOL LLVOVolume::setVolume(const LLVolumeParams &volume_params, const S32 detail, bool unique_volume)
BOOL LLVOVolume::setVolume(const LLVolumeParams &params_in, const S32 detail, bool unique_volume)
{
LLVolumeParams volume_params = params_in;
S32 lod = mLOD;
#if MESH_ENABLED
S32 last_lod = mVolumep.notNull() ? LLVolumeLODGroup::getVolumeDetailFromScale(mVolumep->getDetail()) : -1;
BOOL is404 = FALSE;
if (isSculpted())
{
// if it's a mesh
if ((volume_params.getSculptType() & LL_SCULPT_TYPE_MASK) == LL_SCULPT_TYPE_MESH)
{ //meshes might not have all LODs, get the force detail to best existing LOD
LLUUID mesh_id = volume_params.getSculptID();
//profile and path params don't matter for meshes
volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE);
lod = gMeshRepo.getActualMeshLOD(volume_params, lod);
if (lod == -1)
{
is404 = TRUE;
lod = 0;
}
}
}
#endif //MESH_ENABLED
// Check if we need to change implementations
bool is_flexible = (volume_params.getPathParams().getCurveType() == LL_PCODE_PATH_FLEXIBLE);
if (is_flexible)
@@ -759,13 +797,19 @@ BOOL LLVOVolume::setVolume(const LLVolumeParams &volume_params, const S32 detail
}
}
if ((LLPrimitive::setVolume(volume_params, mLOD, (mVolumeImpl && mVolumeImpl->isVolumeUnique()))) || mSculptChanged)
#if MESH_ENABLED
if (is404)
{
setIcon(LLViewerTextureManager::getFetchedTextureFromFile("icons/Inv_Mesh.png", TRUE, LLViewerTexture::BOOST_UI));
}
#endif //MESH_ENABLED
if ((LLPrimitive::setVolume(volume_params, lod, (mVolumeImpl && mVolumeImpl->isVolumeUnique()))) || mSculptChanged)
{
mFaceMappingChanged = TRUE;
if (mVolumeImpl)
{
mVolumeImpl->onSetVolume(volume_params, detail);
mVolumeImpl->onSetVolume(volume_params, detail); //mLOD ?
}
updateSculptTexture();
@@ -773,10 +817,31 @@ BOOL LLVOVolume::setVolume(const LLVolumeParams &volume_params, const S32 detail
if (isSculpted())
{
updateSculptTexture();
if (mSculptTexture.notNull())
#if MESH_ENABLED
// if it's a mesh
if ((volume_params.getSculptType() & LL_SCULPT_TYPE_MASK) == LL_SCULPT_TYPE_MESH)
{
sculpt();
if (getVolume()->getNumVolumeFaces() == 0 || getVolume()->isTetrahedron())
{
//load request not yet issued, request pipeline load this mesh
LLUUID asset_id = volume_params.getSculptID();
S32 available_lod = gMeshRepo.loadMesh(this, volume_params, lod, last_lod);
if (available_lod != lod)
{
LLPrimitive::setVolume(volume_params, available_lod);
}
}
}
else // otherwise is sculptie
#endif //MESH_ENABLED
{
if (mSculptTexture.notNull())
{
sculpt();
}
}
}
@@ -789,7 +854,11 @@ void LLVOVolume::updateSculptTexture()
{
LLPointer<LLViewerFetchedTexture> old_sculpt = mSculptTexture;
if (isSculpted())
if (isSculpted()
#if MESH_ENABLED
&& !isMesh()
#endif //MESH_ENABLED
)
{
LLSculptParams *sculpt_params = (LLSculptParams *)getParameterEntry(LLNetworkData::PARAMS_SCULPT);
LLUUID id = sculpt_params->getSculptTexture();
@@ -814,8 +883,17 @@ void LLVOVolume::updateSculptTexture()
mSculptTexture->addVolume(this);
}
}
}
#if MESH_ENABLED
void LLVOVolume::notifyMeshLoaded()
{
mSculptChanged = TRUE;
gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_GEOMETRY, TRUE);
}
#endif //MESH_ENABLED
// sculpt replaces generate() for sculpted surfaces
void LLVOVolume::sculpt()
{
@@ -907,8 +985,24 @@ BOOL LLVOVolume::calcLOD()
S32 cur_detail = 0;
F32 radius = getVolume()->mLODScaleBias.scaledVec(getScale()).length();
F32 distance = mDrawable->mDistanceWRTCamera;
F32 radius;
F32 distance;
#if MESH_ENABLED
if (mDrawable->isState(LLDrawable::RIGGED))
{
LLVOAvatar* avatar = getAvatar();
distance = avatar->mDrawable->mDistanceWRTCamera;
radius = avatar->getBinRadius();
}
else
#endif //MESH_ENABLED
{
distance = mDrawable->mDistanceWRTCamera;
radius = getVolume()->mLODScaleBias.scaledVec(getScale()).length();
}
distance *= sDistanceFactor;
F32 rampDist = LLVOVolume::sLODFactor * 2;
@@ -1072,9 +1166,22 @@ BOOL LLVOVolume::genBBoxes(BOOL force_global)
min.clear();
max.clear();
BOOL rebuild = mDrawable->isState(LLDrawable::REBUILD_VOLUME | LLDrawable::REBUILD_POSITION);
BOOL rebuild = mDrawable->isState(LLDrawable::REBUILD_VOLUME | LLDrawable::REBUILD_POSITION
#if MESH_ENABLED
| LLDrawable::REBUILD_RIGGED
#endif //MESH_ENABLED
);
#if MESH_ENABLED
LLVolume* volume = mRiggedVolume;
if (!volume)
{
volume = getVolume();
}
#endif //MESH_ENABLED
#if !MESH_ENABLED
LLVolume* volume = getVolume();
#endif //!MESH_ENABLED
for (S32 i = 0; i < getVolume()->getNumFaces(); i++)
{
LLFace *face = mDrawable->getFace(i);
@@ -1132,8 +1239,27 @@ void LLVOVolume::updateRelativeXform()
}
LLDrawable* drawable = mDrawable;
#if MESH_ENABLED
if (drawable->isState(LLDrawable::RIGGED) && mRiggedVolume.notNull())
{ //rigged volume (which is in agent space) is used for generating bounding boxes etc
//inverse of render matrix should go to partition space
mRelativeXform = getRenderMatrix();
F32* dst = (F32*) mRelativeXformInvTrans.mMatrix;
F32* src = (F32*) mRelativeXform.mMatrix;
dst[0] = src[0]; dst[1] = src[1]; dst[2] = src[2];
dst[3] = src[4]; dst[4] = src[5]; dst[5] = src[6];
dst[6] = src[8]; dst[7] = src[9]; dst[8] = src[10];
mRelativeXform.invert();
mRelativeXformInvTrans.transpose();
}
else if (drawable->isActive())
#endif //MESH_ENABLED
#if !MESH_ENABLED
if (drawable->isActive())
#endif //!MESH_ENABLED
{
// setup relative transforms
LLQuaternion delta_rot;
@@ -1217,6 +1343,17 @@ BOOL LLVOVolume::updateGeometry(LLDrawable *drawable)
{
LLFastTimer t(LLFastTimer::FTM_UPDATE_PRIMITIVES);
#if MESH_ENABLED
if (mDrawable->isState(LLDrawable::REBUILD_RIGGED))
{
{
LLFastTimer t(FTM_UPDATE_RIGGED_VOLUME);
updateRiggedVolume();
}
genBBoxes(FALSE);
mDrawable->clearState(LLDrawable::REBUILD_RIGGED);
}
#endif //MESH_ENABLED
if (mVolumeImpl != NULL)
{
BOOL res;
@@ -1882,6 +2019,24 @@ BOOL LLVOVolume::isSculpted() const
return FALSE;
}
#if MESH_ENABLED
BOOL LLVOVolume::isMesh() const
{
if (isSculpted())
{
LLSculptParams *sculpt_params = (LLSculptParams *)getParameterEntry(LLNetworkData::PARAMS_SCULPT);
U8 sculpt_type = sculpt_params->getSculptType();
if ((sculpt_type & LL_SCULPT_TYPE_MASK) == LL_SCULPT_TYPE_MESH)
// mesh is a mesh
{
return TRUE;
}
}
return FALSE;
}
#endif //MESH_ENABLED
BOOL LLVOVolume::hasLightTexture() const
{
if (getParameterEntryInUse(LLNetworkData::PARAMS_LIGHT_IMAGE))
@@ -1898,6 +2053,12 @@ BOOL LLVOVolume::isVolumeGlobal() const
{
return mVolumeImpl->isVolumeGlobal() ? TRUE : FALSE;
}
#if MESH_ENABLED
else if (mRiggedVolume.notNull())
{
return TRUE;
}
#endif //MESH_ENABLED
return FALSE;
}
@@ -1982,7 +2143,7 @@ void LLVOVolume::generateSilhouette(LLSelectNode* nodep, const LLVector3& view_p
trans_mat.translate(getRegion()->getOriginAgent());
}
volume->generateSilhouetteVertices(nodep->mSilhouetteVertices, nodep->mSilhouetteNormals, nodep->mSilhouetteSegments, view_vector, trans_mat, mRelativeXformInvTrans, nodep->getTESelectMask());
volume->generateSilhouetteVertices(nodep->mSilhouetteVertices, nodep->mSilhouetteNormals, view_vector, trans_mat, mRelativeXformInvTrans, nodep->getTESelectMask());
nodep->mSilhouetteExists = TRUE;
}
@@ -2060,7 +2221,8 @@ U32 LLVOVolume::getHighLODTriangleCount()
ret = ref->getNumTriangles();
LLPrimitive::getVolumeManager()->unrefVolume(ref);
}
/*else if (isMesh())
#if MESH_ENABLED
else if (isMesh())
{
LLVolume* ref = LLPrimitive::getVolumeManager()->refVolume(volume->getParams(), 3);
if (ref->isTetrahedron() || ref->getNumVolumeFaces() == 0)
@@ -2069,7 +2231,8 @@ U32 LLVOVolume::getHighLODTriangleCount()
}
ret = ref->getNumTriangles();
LLPrimitive::getVolumeManager()->unrefVolume(ref);
}*/
}
#endif //MESH_ENABLED
else
{ //default sculpts have a constant number of triangles
ret = 31*2*31; //31 rows of 31 columns of quads for a 32x32 vertex patch
@@ -2283,12 +2446,38 @@ BOOL LLVOVolume::lineSegmentIntersect(const LLVector3& start, const LLVector3& e
BOOL ret = FALSE;
LLVolume* volume = getVolume();
bool transform = true;
#if MESH_ENABLED
if (mDrawable->isState(LLDrawable::RIGGED))
{
if (LLFloater::isVisible(gFloaterTools) && getAvatar()->isSelf())
{
gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_RIGGED, TRUE);
volume = mRiggedVolume;
transform = false;
}
else
{ //cannot pick rigged attachments on other avatars or when not in build mode
return FALSE;
}
}
#endif //MESH_ENABLED
if (volume)
{
LLVector3 v_start, v_end, v_dir;
v_start = agentPositionToVolume(start);
v_end = agentPositionToVolume(end);
if (transform)
{
v_start = agentPositionToVolume(start);
v_end = agentPositionToVolume(end);
}
else
{
v_start = start;
v_end = end;
}
LLVector3 p;
LLVector3 n;
@@ -2348,18 +2537,40 @@ BOOL LLVOVolume::lineSegmentIntersect(const LLVector3& start, const LLVector3& e
if (intersection != NULL)
{
*intersection = volumePositionToAgent(p); // must map back to agent space
if (transform)
{
*intersection = volumePositionToAgent(p); // must map back to agent space
}
else
{
*intersection = p;
}
}
if (normal != NULL)
{
*normal = volumeDirectionToAgent(n);
if (transform)
{
*normal = volumeDirectionToAgent(n);
}
else
{
*normal = n;
}
(*normal).normVec();
}
if (bi_normal != NULL)
{
*bi_normal = volumeDirectionToAgent(bn);
if (transform)
{
*bi_normal = volumeDirectionToAgent(bn);
}
else
{
*bi_normal = bn;
}
(*bi_normal).normVec();
}
@@ -2377,6 +2588,203 @@ BOOL LLVOVolume::lineSegmentIntersect(const LLVector3& start, const LLVector3& e
return ret;
}
#if MESH_ENABLED
bool LLVOVolume::treatAsRigged()
{
return LLFloater::isVisible(gFloaterTools) &&
isAttachment() &&
getAvatar() &&
getAvatar()->isSelf() &&
mDrawable.notNull() &&
mDrawable->isState(LLDrawable::RIGGED);
}
LLRiggedVolume* LLVOVolume::getRiggedVolume()
{
return mRiggedVolume;
}
void LLVOVolume::clearRiggedVolume()
{
if (mRiggedVolume.notNull())
{
mRiggedVolume = NULL;
updateRelativeXform();
}
}
void LLVOVolume::updateRiggedVolume()
{
//Update mRiggedVolume to match current animation frame of avatar.
//Also update position/size in octree.
if (!treatAsRigged())
{
clearRiggedVolume();
return;
}
LLVolume* volume = getVolume();
const LLMeshSkinInfo* skin = gMeshRepo.getSkinInfo(volume->getParams().getSculptID());
if (!skin)
{
clearRiggedVolume();
return;
}
LLVOAvatar* avatar = getAvatar();
if (!avatar)
{
clearRiggedVolume();
return;
}
if (!mRiggedVolume)
{
LLVolumeParams p;
mRiggedVolume = new LLRiggedVolume(p);
updateRelativeXform();
}
mRiggedVolume->update(skin, avatar, volume);
}
static LLFastTimer::DeclareTimer FTM_SKIN_RIGGED("Skin");
static LLFastTimer::DeclareTimer FTM_RIGGED_OCTREE("Octree");
void LLRiggedVolume::update(const LLMeshSkinInfo* skin, LLVOAvatar* avatar, const LLVolume* volume)
{
bool copy = false;
if (volume->getNumVolumeFaces() != getNumVolumeFaces())
{
copy = true;
}
for (S32 i = 0; i < volume->getNumVolumeFaces() && !copy; ++i)
{
const LLVolumeFace& src_face = volume->getVolumeFace(i);
const LLVolumeFace& dst_face = getVolumeFace(i);
if (src_face.mNumIndices != dst_face.mNumIndices ||
src_face.mNumVertices != dst_face.mNumVertices)
{
copy = true;
}
}
if (copy)
{
copyVolumeFaces(volume);
}
//build matrix palette
LLMatrix4a mp[64];
LLMatrix4* mat = (LLMatrix4*) mp;
for (U32 j = 0; j < skin->mJointNames.size(); ++j)
{
LLJoint* joint = avatar->getJoint(skin->mJointNames[j]);
if (joint)
{
mat[j] = skin->mInvBindMatrix[j];
mat[j] *= joint->getWorldMatrix();
}
}
for (S32 i = 0; i < volume->getNumVolumeFaces(); ++i)
{
const LLVolumeFace& vol_face = volume->getVolumeFace(i);
LLVolumeFace& dst_face = mVolumeFaces[i];
LLVector4a* weight = vol_face.mWeights;
LLMatrix4a bind_shape_matrix;
bind_shape_matrix.loadu(skin->mBindShapeMatrix);
LLVector4a* pos = dst_face.mPositions;
{
LLFastTimer t(FTM_SKIN_RIGGED);
for (U32 j = 0; j < dst_face.mNumVertices; ++j)
{
LLMatrix4a final_mat;
final_mat.clear();
S32 idx[4];
LLVector4 wght;
F32 scale = 0.f;
for (U32 k = 0; k < 4; k++)
{
F32 w = weight[j][k];
idx[k] = (S32) floorf(w);
wght[k] = w - floorf(w);
scale += wght[k];
}
wght *= 1.f/scale;
for (U32 k = 0; k < 4; k++)
{
F32 w = wght[k];
LLMatrix4a src;
src.setMul(mp[idx[k]], w);
final_mat.add(src);
}
LLVector4a& v = vol_face.mPositions[j];
LLVector4a t;
LLVector4a dst;
bind_shape_matrix.affineTransform(v, t);
final_mat.affineTransform(t, dst);
pos[j] = dst;
}
//update bounding box
LLVector4a& min = dst_face.mExtents[0];
LLVector4a& max = dst_face.mExtents[1];
min = pos[0];
max = pos[1];
for (U32 j = 1; j < dst_face.mNumVertices; ++j)
{
min.setMin(min, pos[j]);
max.setMax(max, pos[j]);
}
dst_face.mCenter->setAdd(dst_face.mExtents[0], dst_face.mExtents[1]);
dst_face.mCenter->mul(0.5f);
}
{
LLFastTimer t(FTM_RIGGED_OCTREE);
delete dst_face.mOctree;
dst_face.mOctree = NULL;
LLVector4a size;
size.setSub(dst_face.mExtents[1], dst_face.mExtents[0]);
size.splat(size.getLength3().getF32()*0.5f);
dst_face.createOctree(1.f);
}
}
}
#endif //MESH_ENABLED
U32 LLVOVolume::getPartitionType() const
{
if (isHUDAttachment())
@@ -2525,6 +2933,35 @@ void LLVolumeGeometryManager::getGeometry(LLSpatialGroup* group)
}
#if MESH_ENABLED
static LLDrawPoolAvatar* get_avatar_drawpool(LLViewerObject* vobj)
{
LLVOAvatar* avatar = vobj->getAvatar();
if (avatar)
{
LLDrawable* drawable = avatar->mDrawable;
if (drawable && drawable->getNumFaces() > 0)
{
LLFace* face = drawable->getFace(0);
if (face)
{
LLDrawPool* drawpool = face->getPool();
if (drawpool)
{
if (drawpool->getType() == LLDrawPool::POOL_AVATAR)
{
return (LLDrawPoolAvatar*) drawpool;
}
}
}
}
}
return NULL;
}
#endif //MESH_ENABLED
void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
{
if (LLPipeline::sSkipUpdate)
@@ -2591,12 +3028,28 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
}
LLVOVolume* vobj = drawablep->getVOVolume();
#if MESH_ENABLED
if (vobj->getVolume() && vobj->getVolume()->isTetrahedron())
{
continue;
}
#endif //MESH_ENABLED
llassert_always(vobj);
vobj->updateTextureVirtualSize();
vobj->preRebuild();
drawablep->clearState(LLDrawable::HAS_ALPHA);
#if MESH_ENABLED
bool rigged = vobj->isAttachment() &&
vobj->isMesh() &&
gMeshRepo.getSkinInfo(vobj->getVolume()->getParams().getSculptID());
bool bake_sunlight = LLPipeline::sBakeSunlight && drawablep->isStatic();
bool is_rigged = false;
#endif //MESH_ENABLED
//for each face
for (S32 i = 0; i < drawablep->getNumFaces(); i++)
{
@@ -2608,6 +3061,170 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
//sum up face verts and indices
drawablep->updateFaceSize(i);
#if MESH_ENABLED
if (rigged)
{
if (!facep->isState(LLFace::RIGGED))
{ //completely reset vertex buffer
facep->clearVertexBuffer();
}
facep->setState(LLFace::RIGGED);
is_rigged = true;
//get drawpool of avatar with rigged face
LLDrawPoolAvatar* pool = get_avatar_drawpool(vobj);
//Determine if we've received skininfo that contains an
//alternate bind matrix - if it does then apply the translational component
//to the joints of the avatar.
LLVOAvatar* pAvatarVO = vobj->getAvatar();
bool pelvisGotSet = false;
if ( pAvatarVO )
{
LLUUID currentId = vobj->getVolume()->getParams().getSculptID();
const LLMeshSkinInfo* pSkinData = gMeshRepo.getSkinInfo( currentId );
if ( pSkinData )
{
const int bindCnt = pSkinData->mAlternateBindMatrix.size();
if ( bindCnt > 0 )
{
const int jointCnt = pSkinData->mJointNames.size();
const F32 pelvisZOffset = pSkinData->mPelvisOffset;
bool fullRig = (jointCnt>=20) ? true : false;
if ( fullRig )
{
for ( int i=0; i<jointCnt; ++i )
{
std::string lookingForJoint = pSkinData->mJointNames[i].c_str();
//llinfos<<"joint name "<<lookingForJoint.c_str()<<llendl;
LLJoint* pJoint = pAvatarVO->getJoint( lookingForJoint );
if ( pJoint && pJoint->getId() != currentId )
{
pJoint->setId( currentId );
const LLVector3& jointPos = pSkinData->mAlternateBindMatrix[i].getTranslation();
//Set the joint position
pJoint->storeCurrentXform( jointPos );
//If joint is a pelvis then handle old/new pelvis to foot values
if ( lookingForJoint == "mPelvis" )
{
pJoint->storeCurrentXform( jointPos );
if ( !pAvatarVO->hasPelvisOffset() )
{
pAvatarVO->setPelvisOffset( true, jointPos, pelvisZOffset );
//Trigger to rebuild viewer AV
pelvisGotSet = true;
}
}
}
}
}
}
}
}
//If we've set the pelvis to a new position we need to also rebuild some information that the
//viewer does at launch (e.g. body size etc.)
if ( pelvisGotSet )
{
pAvatarVO->postPelvisSetRecalc();
}
if (pool)
{
const LLTextureEntry* te = facep->getTextureEntry();
//remove face from old pool if it exists
LLDrawPool* old_pool = facep->getPool();
if (old_pool && old_pool->getType() == LLDrawPool::POOL_AVATAR)
{
((LLDrawPoolAvatar*) old_pool)->removeRiggedFace(facep);
}
//add face to new pool
LLViewerTexture* tex = facep->getTexture();
U32 type = gPipeline.getPoolTypeFromTE(te, tex);
if (type == LLDrawPool::POOL_ALPHA)
{
if (te->getFullbright())
{
pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_FULLBRIGHT_ALPHA);
}
else
{
pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_ALPHA);
}
}
else if (te->getShiny())
{
if (te->getFullbright())
{
pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_FULLBRIGHT_SHINY);
}
else
{
if (LLPipeline::sRenderDeferred)
{
pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_SIMPLE);
}
else
{
pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_SHINY);
}
}
}
else
{
if (te->getFullbright())
{
pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_FULLBRIGHT);
}
else
{
pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_SIMPLE);
}
}
if (te->getGlow())
{
pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_GLOW);
}
if (LLPipeline::sRenderDeferred)
{
if (type != LLDrawPool::POOL_ALPHA && !te->getFullbright())
{
if (te->getBumpmap())
{
pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_DEFERRED_BUMP);
}
else
{
pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_DEFERRED_SIMPLE);
}
}
}
}
continue;
}
else
{
if (facep->isState(LLFace::RIGGED))
{ //face is not rigged but used to be, remove from rigged face pool
LLDrawPoolAvatar* pool = (LLDrawPoolAvatar*) facep->getPool();
if (pool)
{
pool->removeRiggedFace(facep);
}
facep->clearState(LLFace::RIGGED);
}
}
#endif //MESH_ENABLED
if (cur_total > max_total || facep->getIndicesCount() <= 0 || facep->getGeomCount() <= 0)
{
facep->clearVertexBuffer();
@@ -2718,6 +3335,17 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
facep->clearVertexBuffer();
}
}
#if MESH_ENABLED
if (is_rigged)
{
drawablep->setState(LLDrawable::RIGGED);
}
else
{
drawablep->clearState(LLDrawable::RIGGED);
}
#endif //MESH_ENABLED
}
group->mBufferUsage = useage;

View File

@@ -44,12 +44,27 @@
class LLViewerTextureAnim;
class LLDrawPool;
class LLSelectNode;
class LLVOAvatar;
class LLMeshSkinInfo;
enum LLVolumeInterfaceType
{
INTERFACE_FLEXIBLE = 1,
};
#if MESH_ENABLED
class LLRiggedVolume : public LLVolume
{
public:
LLRiggedVolume(const LLVolumeParams& params)
: LLVolume(params, 0.f)
{
}
void update(const LLMeshSkinInfo* skin, LLVOAvatar* avatar, const LLVolume* src_volume);
};
#endif //MESH_ENABLED
// Base class for implementations of the volume - Primitive, Flexible Object, etc.
class LLVolumeInterface
{
@@ -181,6 +196,12 @@ public:
void updateSculptTexture();
void setIndexInTex(S32 index) { mIndexInTex = index ;}
void sculpt();
#if MESH_ENABLED
static void rebuildMeshAssetCallback(LLVFS *vfs,
const LLUUID& asset_uuid,
LLAssetType::EType type,
void* user_data, S32 status, LLExtStat ext_status);
#endif //MESH_ENABLED
void updateRelativeXform();
/*virtual*/ BOOL updateGeometry(LLDrawable *drawable);
/*virtual*/ void updateFaceSize(S32 idx);
@@ -227,6 +248,9 @@ public:
U32 getVolumeInterfaceID() const;
virtual BOOL isFlexible() const;
virtual BOOL isSculpted() const;
#if MESH_ENABLED
virtual BOOL isMesh() const;
#endif //MESH_ENABLED
virtual BOOL hasLightTexture() const;
BOOL isVolumeGlobal() const;
@@ -236,6 +260,35 @@ public:
// tag: vaa emerald local_asset_browser
void setSculptChanged(BOOL has_changed) { mSculptChanged = has_changed; }
#if MESH_ENABLED
void notifyMeshLoaded();
// Returns 'true' iff the media data for this object is in flight
bool isMediaDataBeingFetched() const;
// Returns the "last fetched" media version, or -1 if not fetched yet
S32 getLastFetchedMediaVersion() const { return mLastFetchedMediaVersion; }
void addMDCImpl() { ++mMDCImplCount; }
void removeMDCImpl() { --mMDCImplCount; }
S32 getMDCImplCount() { return mMDCImplCount; }
//rigged volume update (for raycasting)
void updateRiggedVolume();
LLRiggedVolume* getRiggedVolume();
//returns true if volume should be treated as a rigged volume
// - Build tools are open
// - object is an attachment
// - object is attached to self
// - object is rendered as rigged
bool treatAsRigged();
//clear out rigged volume and revert back to non-rigged state for picking/LOD/distance updates
void clearRiggedVolume();
#endif //MESH_ENABLED
protected:
S32 computeLODDetail(F32 distance, F32 radius);
BOOL calcLOD();
@@ -262,7 +315,9 @@ private:
LLPointer<LLViewerFetchedTexture> mSculptTexture;
LLPointer<LLViewerFetchedTexture> mLightTexture;
S32 mIndexInTex;
#if MESH_ENABLED
LLPointer<LLRiggedVolume> mRiggedVolume;
#endif //MESH_ENABLED
// statics
public:
static F32 sLODSlopDistanceFactor;// Changing this to zero, effectively disables the LOD transition slop

View File

@@ -70,6 +70,9 @@
#include "llgldbg.h"
#include "llhudmanager.h"
#include "lllightconstants.h"
#if MESH_ENABLED
#include "llmeshrepository.h"
#endif //MESH_ENABLED
#include "llresmgr.h"
#include "llselectmgr.h"
#include "llsky.h"
@@ -2108,6 +2111,9 @@ void LLPipeline::rebuildPriorityGroups()
assertInitialized();
#if MESH_ENABLED
gMeshRepo.notifyLoadedMeshes();
#endif //MESH_ENABLED
mGroupQ1Locked = true;
// Iterate through all drawables on the priority build queue,