Add Advanced --> Character --> Meshes and morphs...

Allows to save the .llm of all mesh objects, and
all morphs of each as .obj (Wavefront OBJ File),
for import in, for example, blender.

You can also load .obj files, but of course they
will only affect what you see locally.
This commit is contained in:
Aleric Inglewood
2011-05-16 19:03:48 +02:00
parent 00f8c35614
commit 10af185abc
8 changed files with 1532 additions and 25 deletions

View File

@@ -2324,6 +2324,8 @@ void LLMenuGL::arrange( void )
(*item_iter)->buildDrawLabel();
}
}
cleanupSpilloverBranch();
}
if (mKeepFixedSize)
{
@@ -2540,6 +2542,11 @@ BOOL LLMenuGL::append( LLMenuItemGL* item )
// Its added as a fix to a viewer 1.23 bug that has already been address by skinning work.
BOOL LLMenuGL::appendNoArrange( LLMenuItemGL* item )
{
if (mSpilloverMenu)
{
return mSpilloverMenu->append(item);
}
mItems.push_back( item );
addChild( item );
return TRUE;
@@ -4411,6 +4418,9 @@ BOOL LLMenuHolderGL::hideMenus()
{
return FALSE;
}
sItemActivationTimer.stop();
BOOL menu_visible = hasVisibleMenu();
if (menu_visible)
{

View File

@@ -863,6 +863,687 @@ LLPolyMesh *LLPolyMesh::getMesh(const std::string &name, LLPolyMesh* reference_m
return poly_mesh;
}
//-----------------------------------------------------------------------------
// LLPolyMesh::getMeshData()
//-----------------------------------------------------------------------------
LLPolyMeshSharedData *LLPolyMesh::getMeshData(const std::string &name)
{
//-------------------------------------------------------------------------
// search for an existing mesh by this name
//-------------------------------------------------------------------------
LLPolyMeshSharedData* mesh_shared_data = get_if_there(sGlobalSharedMeshList, name, (LLPolyMeshSharedData*)NULL);
return mesh_shared_data;
}
//-----------------------------------------------------------------------------
// LLPolyMesh::saveLLM()
//-----------------------------------------------------------------------------
BOOL LLPolyMesh::saveLLM(LLFILE *fp)
{
if (!fp)
return FALSE;
//-------------------------------------------------------------------------
// Write a header
//-------------------------------------------------------------------------
if (fwrite(HEADER_BINARY, 1, strlen(HEADER_BINARY), fp) != strlen(HEADER_BINARY))
{
llwarns << "Short write" << llendl;
}
if (strlen(HEADER_BINARY) < 24)
{
char padding[24] = {}; // zeroes
int pad = 24 - strlen(HEADER_BINARY);
if (fwrite(&padding, 1, pad, fp) != pad)
{
llwarns << "Short write" << llendl;
}
}
//----------------------------------------------------------------
// HasWeights
//----------------------------------------------------------------
U8 hasWeights = (U8) mSharedData->mHasWeights;
if (fwrite(&hasWeights, sizeof(U8), 1, fp) != 1)
{
llwarns << "Short write" << llendl;
}
//----------------------------------------------------------------
// HasDetailTexCoords
//----------------------------------------------------------------
U8 hasDetailTexCoords = (U8) mSharedData->mHasDetailTexCoords;
if (fwrite(&hasDetailTexCoords, sizeof(U8), 1, fp) != 1)
{
llwarns << "Short write" << llendl;
}
//----------------------------------------------------------------
// Position
//----------------------------------------------------------------
LLVector3 position = mSharedData->mPosition;
llendianswizzle(position.mV, sizeof(float), 3);
if (fwrite(position.mV, sizeof(float), 3, fp) != 3)
{
llwarns << "Short write" << llendl;
}
//----------------------------------------------------------------
// Rotation
//----------------------------------------------------------------
LLQuaternion rotation = mSharedData->mRotation;
F32 roll;
F32 pitch;
F32 yaw;
rotation.getEulerAngles(&roll, &pitch, &yaw);
roll *= RAD_TO_DEG;
pitch *= RAD_TO_DEG;
yaw *= RAD_TO_DEG;
LLVector3 rotationAngles (roll, pitch, yaw);
llendianswizzle(rotationAngles.mV, sizeof(float), 3);
if (fwrite(rotationAngles.mV, sizeof(float), 3, fp) != 3)
{
llwarns << "Short write" << llendl;
}
U8 rotationOrder = 0;
if (fwrite(&rotationOrder, sizeof(U8), 1, fp) != 1)
{
llwarns << "Short write" << llendl;
}
//----------------------------------------------------------------
// Scale
//----------------------------------------------------------------
LLVector3 scale = mSharedData->mScale;
llendianswizzle(scale.mV, sizeof(float), 3);
if (fwrite(scale.mV, sizeof(float), 3, fp) != 3)
{
llwarns << "Short write" << llendl;
}
//----------------------------------------------------------------
// NumVertices
//----------------------------------------------------------------
U16 numVertices = mSharedData->mNumVertices;
if (!isLOD())
{
llendianswizzle(&numVertices, sizeof(U16), 1);
if (fwrite(&numVertices, sizeof(U16), 1, fp) != 1)
{
llwarns << "Short write" << llendl;
}
numVertices = mSharedData->mNumVertices; // without the swizzle again
//----------------------------------------------------------------
// Coords
//----------------------------------------------------------------
LLVector3* coords = mSharedData->mBaseCoords;
llendianswizzle(coords, sizeof(float), 3*numVertices);
if (fwrite(coords, 3*sizeof(float), numVertices, fp) != numVertices)
{
llwarns << "Short write" << llendl;
}
llendianswizzle(coords, sizeof(float), 3*numVertices);
//----------------------------------------------------------------
// Normals
//----------------------------------------------------------------
LLVector3* normals = mSharedData->mBaseNormals;
llendianswizzle(normals, sizeof(float), 3*numVertices);
if (fwrite(normals, 3*sizeof(float), numVertices, fp) != numVertices)
{
llwarns << "Short write" << llendl;
}
llendianswizzle(normals, sizeof(float), 3*numVertices);
//----------------------------------------------------------------
// Binormals
//----------------------------------------------------------------
LLVector3* binormals = mSharedData->mBaseBinormals;
llendianswizzle(binormals, sizeof(float), 3*numVertices);
if (fwrite(binormals, 3*sizeof(float), numVertices, fp) != numVertices)
{
llwarns << "Short write" << llendl;
}
llendianswizzle(binormals, sizeof(float), 3*numVertices);
//----------------------------------------------------------------
// TexCoords
//----------------------------------------------------------------
LLVector2* tex = mSharedData->mTexCoords;
llendianswizzle(tex, sizeof(float), 2*numVertices);
if (fwrite(tex, 2*sizeof(float), numVertices, fp) != numVertices)
{
llwarns << "Short write" << llendl;
}
llendianswizzle(tex, sizeof(float), 2*numVertices);
//----------------------------------------------------------------
// DetailTexCoords
//----------------------------------------------------------------
if (hasDetailTexCoords)
{
LLVector2* detail = mSharedData->mDetailTexCoords;
llendianswizzle(detail, sizeof(float), 2*numVertices);
if (fwrite(detail, 2*sizeof(float), numVertices, fp) != numVertices)
{
llwarns << "Short write" << llendl;
}
llendianswizzle(detail, sizeof(float), 2*numVertices);
}
//----------------------------------------------------------------
// Weights
//----------------------------------------------------------------
if (hasWeights)
{
F32* weights = mSharedData->mWeights;
llendianswizzle(weights, sizeof(float), numVertices);
if (fwrite(weights, sizeof(float), numVertices, fp) != numVertices)
{
llwarns << "Short write" << llendl;
}
llendianswizzle(weights, sizeof(float), numVertices);
}
}
//----------------------------------------------------------------
// NumFaces
//----------------------------------------------------------------
U16 numFaces = mSharedData->mNumFaces;
llendianswizzle(&numFaces, sizeof(U16), 1);
if (fwrite(&numFaces, sizeof(U16), 1, fp) != 1)
{
llwarns << "Short write" << llendl;
}
numFaces = mSharedData->mNumFaces; // without the swizzle again
//----------------------------------------------------------------
// Faces
//----------------------------------------------------------------
LLPolyFace* faces = mSharedData->mFaces;
S16 face[3];
U32 i;
for (i = 0; i < numFaces; i++)
{
face[0] = faces[i][0];
face[1] = faces[i][1];
face[2] = faces[i][2];
llendianswizzle(face, sizeof(U16), 3);
if (fwrite(face, sizeof(U16), 3, fp) != 3)
{
llwarns << "Short write" << llendl;
}
}
//----------------------------------------------------------------
// NumSkinJoints
//----------------------------------------------------------------
// When reading LOD mesh files, we stop here, but the actual Linden
// .llm files have data with zero items for these, so we do the same.
U16 numSkinJoints = mSharedData->mNumJointNames;
// At least one element is always allocated but it may be empty
std::string *jn = &mSharedData->mJointNames[0];
if ((numSkinJoints == 1)
&& (jn->length() == 0))
{
numSkinJoints = 0;
}
if ( hasWeights )
{
llendianswizzle(&numSkinJoints, sizeof(U16), 1);
if (fwrite(&numSkinJoints, sizeof(U16), 1, fp) != 1)
{
llwarns << "Short write" << llendl;
}
llendianswizzle(&numSkinJoints, sizeof(U16), 1);
//----------------------------------------------------------------
// SkinJoints
//----------------------------------------------------------------
char padding[64] = {}; // zeroes
for (i=0; i < numSkinJoints; i++)
{
jn = &mSharedData->mJointNames[i];
if (fwrite(jn->c_str(), 1, jn->length(), fp) != jn->length())
{
llwarns << "Short write" << llendl;
}
if (jn->length() < 64)
{
int pad = 64 - jn->length();
if (fwrite(&padding, 1, pad, fp) != pad)
{
llwarns << "Short write" << llendl;
}
}
}
}
//-------------------------------------------------------------------------
// look for morph section
//-------------------------------------------------------------------------
LLPolyMeshSharedData::morphdata_list_t::iterator iter = mSharedData->mMorphData.begin();
LLPolyMeshSharedData::morphdata_list_t::iterator end = mSharedData->mMorphData.end();
// Sort them
morph_list_t morph_list;
for (; iter != end; ++iter)
{
LLPolyMorphData *morph_data = *iter;
std::string morph_name = morph_data->getName();
morph_list.insert(std::pair<std::string,LLPolyMorphData*>(morph_name, morph_data));
}
char padding[64] = {}; // zeroes
for (morph_list_t::iterator morph_iter = morph_list.begin();
morph_iter != morph_list.end(); ++morph_iter)
{
const std::string& morph_name = morph_iter->first;
LLPolyMorphData* morph_data = morph_iter->second;
if (fwrite(morph_name.c_str(), 1, morph_name.length(), fp) != morph_name.length())
{
llwarns << "Short write" << llendl;
}
if (morph_name.length() < 64)
{
int pad = 64 - morph_name.length();
if (fwrite(&padding, 1, pad, fp) != pad)
{
llwarns << "Short write" << llendl;
}
}
if (!morph_data->saveLLM(fp))
{
llwarns << "Problem writing morph" << llendl;
}
}
char end_morphs[64] = "End Morphs"; // padded with zeroes
if (fwrite(end_morphs, sizeof(char), 64, fp) != 64)
{
llwarns << "Short write" << llendl;
}
//-------------------------------------------------------------------------
// Remaps
//-------------------------------------------------------------------------
S32 numRemaps = mSharedData->mSharedVerts.size();
llendianswizzle(&numRemaps, sizeof(S32), 1);
if (fwrite(&numRemaps, sizeof(S32), 1, fp) != 1)
{
llwarns << "Short write" << llendl;
}
std::map<S32, S32>::iterator remap_iter = mSharedData->mSharedVerts.begin();
std::map<S32, S32>::iterator remap_end = mSharedData->mSharedVerts.end();
for (; remap_iter != remap_end; ++remap_iter)
{
S32 remapSrc = remap_iter->first;
llendianswizzle(&remapSrc, sizeof(S32), 1);
if (fwrite(&remapSrc, sizeof(S32), 1, fp) != 1)
{
llwarns << "Short write" << llendl;
}
S32 remapDst = remap_iter->second;
llendianswizzle(&remapDst, sizeof(S32), 1);
if (fwrite(&remapDst, sizeof(S32), 1, fp) != 1)
{
llwarns << "Short write" << llendl;
}
}
return TRUE;
}
//-----------------------------------------------------------------------------
// LLPolyMesh::saveOBJ()
//-----------------------------------------------------------------------------
BOOL LLPolyMesh::saveOBJ(LLFILE *fp)
{
if (!fp)
return FALSE;
// If it's an LOD mesh, the LOD vertices are usually at the start of the
// list of vertices, so the number of vertices is just that subset.
// We could also write out the rest of the vertices in case someone wants
// to choose new vertices for the LOD mesh, but that may confuse some people.
int nverts = mSharedData->mNumVertices;
int nfaces = mSharedData->mNumFaces;
int i;
LLVector3* coords = getWritableCoords();
for ( i=0; i<nverts; i++) {
std::string outstring = llformat("v %f %f %f\n",
coords[i][0],
coords[i][1],
coords[i][2]);
if (fwrite(outstring.c_str(), 1, outstring.length(), fp) != outstring.length())
{
llwarns << "Short write" << llendl;
}
}
LLVector3* normals = getWritableNormals();
for ( i=0; i<nverts; i++) {
std::string outstring = llformat("vn %f %f %f\n",
normals[i][0],
normals[i][1],
normals[i][2]);
if (fwrite(outstring.c_str(), 1, outstring.length(), fp) != outstring.length())
{
llwarns << "Short write" << llendl;
}
}
LLVector2* tex = getWritableTexCoords();
for ( i=0; i<nverts; i++) {
std::string outstring = llformat("vt %f %f\n",
tex[i][0],
tex[i][1]);
if (fwrite(outstring.c_str(), 1, outstring.length(), fp) != outstring.length())
{
llwarns << "Short write" << llendl;
}
}
LLPolyFace* faces = getFaces();
for ( i=0; i<nfaces; i++) {
S32 f1 = faces[i][0] + 1;
S32 f2 = faces[i][1] + 1;
S32 f3 = faces[i][2] + 1;
std::string outstring = llformat("f %d/%d/%d %d/%d/%d %d/%d/%d\n",
f1, f1, f1,
f2, f2, f2,
f3, f3, f3);
if (fwrite(outstring.c_str(), 1, outstring.length(), fp) != outstring.length())
{
llwarns << "Short write" << llendl;
}
}
return TRUE;
}
//-----------------------------------------------------------------------------
// LLPolyMesh::loadOBJ()
//-----------------------------------------------------------------------------
BOOL LLPolyMesh::loadOBJ(LLFILE *fp)
{
if (!fp)
return FALSE;
int nverts = mSharedData->mNumVertices;
int ntris = mSharedData->mNumFaces;
int nfaces = 0;
int ncoords = 0;
int nnormals = 0;
int ntexcoords = 0;
LLVector3* coords = getWritableCoords();
LLVector3* normals = getWritableNormals();
LLVector3* binormals = getWritableBinormals();
LLVector2* tex = getWritableTexCoords();
LLPolyFace* faces = getFaces();
const S32 BUFSIZE = 16384;
char buffer[BUFSIZE];
// *NOTE: changing the size or type of these buffers will require
// changing the sscanf below.
char keyword[256];
keyword[0] = 0;
F32 tempX;
F32 tempY;
F32 tempZ;
S32 values;
while (!feof(fp))
{
if (fgets(buffer, BUFSIZE, fp) == NULL)
{
buffer[0] = '\0';
}
if (sscanf (buffer," %255s", keyword) != 1)
{
// blank line
continue;
}
if (!strcmp("v", keyword))
{
values = sscanf (buffer," %255s %f %f %f", keyword, &tempX, &tempY, &tempZ);
if (values != 4)
{
llwarns << "Expecting v x y z, but found: " << buffer <<llendl;
continue;
}
if (ncoords == nverts)
{
llwarns << "Too many vertices. Ignoring from: " << buffer <<llendl;
}
if (ncoords < nverts)
{
coords[ncoords].set (tempX, tempY, tempZ);
}
ncoords++;
}
else if (!strcmp("vn",keyword))
{
values = sscanf (buffer," %255s %f %f %f", keyword, &tempX, &tempY, &tempZ);
if (values != 4)
{
llwarns << "Expecting vn x y z, but found: " << buffer <<llendl;
continue;
}
if (nnormals == nverts)
{
llwarns << "Too many normals. Ignoring from: " << buffer <<llendl;
}
if (nnormals < nverts)
{
normals[nnormals].set (tempX, tempY, tempZ);
}
nnormals++;
}
else if (!strcmp("vt", keyword))
{
values = sscanf (buffer," %255s %f %f", keyword, &tempX, &tempY);
if (values != 3)
{
llwarns << "Expecting vt x y, but found: " << buffer <<llendl;
continue;
}
if (ntexcoords == nverts)
{
llwarns << "Too many texture vertices. Ignoring from: " << buffer <<llendl;
}
if (ntexcoords < nverts)
{
tex[ntexcoords].set (tempX, tempY);
}
ntexcoords++;
}
else if (!strcmp("f",keyword))
{
if (nfaces == 0)
{
llwarns << "Ignoring face keywords for now." <<llendl;
}
nfaces++;
}
else
{
llinfos << "Unrecognized keyword. Ignoring: " << buffer << llendl;
}
}
// Compute the binormals
// This computation is close, but not exactly what is being used in the
// original meshes.
int v;
for ( v=0; v<nverts; v++)
{
binormals[v].setZero();
}
int f;
for ( f=0; f<ntris; f++)
{
S32 f0 = faces[f][0];
S32 f1 = faces[f][1];
S32 f2 = faces[f][2];
LLVector3& v0 = coords[f0];
LLVector3& v1 = coords[f1];
LLVector3& v2 = coords[f2];
LLVector2& t0 = tex[f0];
LLVector2& t1 = tex[f1];
LLVector2& t2 = tex[f2];
LLVector3 binorm = calc_binormal_from_triangle(v0, t0, v1, t1, v2, t2);
binorm.normVec();
binormals[f0] += binorm;
binormals[f1] += binorm;
binormals[f2] += binorm;
}
for ( v=0; v<nverts; v++)
{
binormals[v].normVec();
}
return TRUE;
}
//-----------------------------------------------------------------------------
// LLPolyMesh::setSharedFromCurrent()
//-----------------------------------------------------------------------------
BOOL LLPolyMesh::setSharedFromCurrent()
{
// Because meshes are set by continually updating morph weights
// there is no easy way to reapply the morphs, so we just compute
// the change in the base mesh and apply that.
LLPolyMesh delta(mSharedData, NULL);
U32 nverts = delta.getNumVertices();
LLVector3 *delta_coords = delta.getWritableCoords();
LLVector3 *delta_normals = delta.getWritableNormals();
LLVector3 *delta_binormals = delta.getWritableBinormals();
LLVector2 *delta_tex_coords = delta.getWritableTexCoords();
U32 vert_index;
for( vert_index = 0; vert_index < nverts; vert_index++)
{
delta_coords[vert_index] -= mCoords[vert_index];
delta_normals[vert_index] -= mNormals[vert_index];
delta_binormals[vert_index] -= mBinormals[vert_index];
delta_tex_coords[vert_index] -= mTexCoords[vert_index];
}
// Now copy the new base mesh
memcpy(mSharedData->mBaseCoords, mCoords, sizeof(LLVector3) * mSharedData->mNumVertices);
memcpy(mSharedData->mBaseNormals, mNormals, sizeof(LLVector3) * mSharedData->mNumVertices);
memcpy(mSharedData->mBaseBinormals, mBinormals, sizeof(LLVector3) * mSharedData->mNumVertices);
memcpy(mSharedData->mTexCoords, mTexCoords, sizeof(LLVector2) * mSharedData->mNumVertices);
// Update all avatars by applying the delta
std::vector< LLCharacter* >::iterator avatar_it;
for(avatar_it = LLCharacter::sInstances.begin(); avatar_it != LLCharacter::sInstances.end(); ++avatar_it)
{
LLVOAvatar* avatarp = (LLVOAvatar*)*avatar_it;
LLPolyMesh* mesh = avatarp->getMesh(mSharedData);
if (mesh)
{
LLVector3 *mesh_coords = mesh->getWritableCoords();
LLVector3 *mesh_normals = mesh->getWritableNormals();
LLVector3 *mesh_binormals = mesh->getWritableBinormals();
LLVector2 *mesh_tex_coords = mesh->getWritableTexCoords();
LLVector3 *mesh_scaled_normals = mesh->getScaledNormals();
LLVector3 *mesh_scaled_binormals = mesh->getScaledBinormals();
for( vert_index = 0; vert_index < nverts; vert_index++)
{
mesh_coords[vert_index] -= delta_coords[vert_index];
mesh_tex_coords[vert_index] -= delta_tex_coords[vert_index];
mesh_scaled_normals[vert_index] -= delta_normals[vert_index];
LLVector3 normalized_normal = mesh_scaled_normals[vert_index];
normalized_normal.normVec();
mesh_normals[vert_index] = normalized_normal;
mesh_scaled_binormals[vert_index] -= delta_binormals[vert_index];
LLVector3 tangent = mesh_scaled_binormals[vert_index] % normalized_normal;
LLVector3 normalized_binormal = normalized_normal % tangent;
normalized_binormal.normVec();
mesh_binormals[vert_index] = normalized_binormal;
}
avatarp->dirtyMesh();
}
}
return TRUE;
}
//-----------------------------------------------------------------------------
// LLPolyMesh::getSharedMeshName()
//-----------------------------------------------------------------------------
std::string const* LLPolyMesh::getSharedMeshName(LLPolyMeshSharedData* shared)
{
//-------------------------------------------------------------------------
// search for an existing mesh with this shared data
//-------------------------------------------------------------------------
for(LLPolyMeshSharedDataTable::iterator iter = sGlobalSharedMeshList.begin(); iter != sGlobalSharedMeshList.end(); ++iter)
{
std::string const* mesh_name = &iter->first;
LLPolyMeshSharedData* mesh = iter->second;
if (mesh == shared)
return mesh_name;
}
return NULL;
}
//-----------------------------------------------------------------------------
// LLPolyMesh::freeAllMeshes()
//-----------------------------------------------------------------------------
@@ -882,7 +1563,7 @@ LLPolyMeshSharedData *LLPolyMesh::getSharedData() const
//--------------------------------------------------------------------
// LLPolyMesh::dumpDiagInfo()
//--------------------------------------------------------------------
void LLPolyMesh::dumpDiagInfo()
void LLPolyMesh::dumpDiagInfo(void*)
{
// keep track of totals
U32 total_verts = 0;
@@ -893,7 +1574,7 @@ void LLPolyMesh::dumpDiagInfo()
llinfos << "-----------------------------------------------------" << llendl;
llinfos << " Global PolyMesh Table (DEBUG only)" << llendl;
llinfos << " Verts Faces Mem(KB) Name" << llendl;
llinfos << " Verts Faces Mem(KB) Type Name" << llendl;
llinfos << "-----------------------------------------------------" << llendl;
// print each loaded mesh, and it's memory usage
@@ -907,7 +1588,14 @@ void LLPolyMesh::dumpDiagInfo()
S32 num_faces = mesh->mNumFaces;
U32 num_kb = mesh->getNumKB();
buf = llformat("%8d %8d %8d %s", num_verts, num_faces, num_kb, mesh_name.c_str());
std::string type;
if (mesh->isLOD()) {
type = "LOD ";
} else {
type = "base";
}
buf = llformat("%8d %8d %8d %s %s", num_verts, num_faces, num_kb, type.c_str(), mesh_name.c_str());
llinfos << buf << llendl;
total_verts += num_verts;
@@ -996,6 +1684,32 @@ void LLPolyMesh::initializeForMorph()
memset(mClothingWeights, 0, sizeof(LLVector4) * mSharedData->mNumVertices);
}
//-----------------------------------------------------------------------------
// getMorphList()
//-----------------------------------------------------------------------------
void LLPolyMesh::getMorphList (const std::string& mesh_name, morph_list_t* morph_list)
{
if (!morph_list)
return;
LLPolyMeshSharedData* mesh_shared_data = get_if_there(sGlobalSharedMeshList, mesh_name, (LLPolyMeshSharedData*)NULL);
if (!mesh_shared_data)
return;
LLPolyMeshSharedData::morphdata_list_t::iterator iter = mesh_shared_data->mMorphData.begin();
LLPolyMeshSharedData::morphdata_list_t::iterator end = mesh_shared_data->mMorphData.end();
for (; iter != end; ++iter)
{
LLPolyMorphData *morph_data = *iter;
std::string morph_name = morph_data->getName();
morph_list->insert(std::pair<std::string,LLPolyMorphData*>(morph_name, morph_data));
}
return;
}
//-----------------------------------------------------------------------------
// getMorphData()
//-----------------------------------------------------------------------------

View File

@@ -174,6 +174,24 @@ public:
// otherwise it is loaded from file, added to the table, and returned.
static LLPolyMesh *getMesh( const std::string &name, LLPolyMesh* reference_mesh = NULL);
// Saves the mesh information as a binary Linden Lab Mesh file.
BOOL saveLLM(LLFILE *fp);
// Saves the mesh information as an OBJ file.
BOOL saveOBJ(LLFILE *fp);
// Loads new mesh information from an OBJ file.
BOOL loadOBJ(LLFILE *fp);
// Copies the current mesh to the base mesh.
BOOL setSharedFromCurrent();
// Gets the name of the mesh corresponding to the shared data
static const std::string* getSharedMeshName(LLPolyMeshSharedData* shared);
// Requests mesh data by name. Returns null if not found.
static LLPolyMeshSharedData *getMeshData( const std::string &name );
// Frees all loaded meshes.
// This should only be called once you know there are no outstanding
// references to these objects. Generally, upon exit of the application.
@@ -312,6 +330,9 @@ public:
return mSharedData->mJointNames;
}
typedef std::map<std::string,LLPolyMorphData*> morph_list_t;
static void getMorphList (const std::string& mesh_name, morph_list_t* morph_list);
LLPolyMorphData* getMorphData(const std::string& morph_name);
// void removeMorphData(LLPolyMorphData *morph_target);
// void deleteAllMorphData();
@@ -334,11 +355,12 @@ public:
U32 mFaceIndexOffset;
U32 mFaceIndexCount;
U32 mCurVertexCount;
private:
void initializeForMorph();
// Dumps diagnostic information about the global mesh table
static void dumpDiagInfo();
static void dumpDiagInfo(void*);
private:
void initializeForMorph();
protected:
// mesh data shared across all instances of a given mesh

View File

@@ -43,6 +43,7 @@
//#include "../tools/imdebug/imdebug.h"
const F32 NORMAL_SOFTEN_FACTOR = 0.65f;
const F32 SIGNIFICANT_DELTA = 0.0001f;
//-----------------------------------------------------------------------------
// LLPolyMorphData()
@@ -210,6 +211,297 @@ BOOL LLPolyMorphData::loadBinary(LLFILE *fp, LLPolyMeshSharedData *mesh)
return TRUE;
}
//-----------------------------------------------------------------------------
// LLPolyMesh::saveLLM()
//-----------------------------------------------------------------------------
BOOL LLPolyMorphData::saveLLM(LLFILE *fp)
{
if (!fp)
return FALSE;
S32 numVertices = mNumIndices;
llendianswizzle(&numVertices, sizeof(S32), 1);
if (fwrite(&numVertices, sizeof(S32), 1, fp) != 1)
{
llwarns << "Short write" << llendl;
}
numVertices = mNumIndices; // without the swizzle again
//-------------------------------------------------------------------------
// write vertices
//-------------------------------------------------------------------------
for(S32 v = 0; v < numVertices; v++)
{
llendianswizzle(&mVertexIndices[v], sizeof(U32), 1);
if (fwrite(&mVertexIndices[v], sizeof(U32), 1, fp) != 1)
{
llwarns << "Short write" << llendl;
}
llendianswizzle(&mVertexIndices[v], sizeof(U32), 1);
llendianswizzle(&mCoords[v].mV, sizeof(F32), 3);
if (fwrite(&mCoords[v].mV, sizeof(F32), 3, fp) != 3)
{
llwarns << "Short write" << llendl;
}
llendianswizzle(&mCoords[v].mV, sizeof(F32), 3);
llendianswizzle(&mNormals[v].mV, sizeof(F32), 3);
if (fwrite(&mNormals[v].mV, sizeof(F32), 3, fp) != 3)
{
llwarns << "Short write" << llendl;
}
llendianswizzle(&mNormals[v].mV, sizeof(F32), 3);
llendianswizzle(&mBinormals[v].mV, sizeof(F32), 3);
if (fwrite(&mBinormals[v].mV, sizeof(F32), 3, fp) != 3)
{
llwarns << "Short write" << llendl;
}
llendianswizzle(&mBinormals[v].mV, sizeof(F32), 3);
llendianswizzle(&mTexCoords[v].mV, sizeof(F32), 2);
if (fwrite(&mTexCoords[v].mV, sizeof(F32), 2, fp) != 2)
{
llwarns << "Short write" << llendl;
}
llendianswizzle(&mTexCoords[v].mV, sizeof(F32), 2);
}
return TRUE;
}
//-----------------------------------------------------------------------------
// saveOBJ()
//-----------------------------------------------------------------------------
BOOL LLPolyMorphData::saveOBJ(LLFILE *fp)
{
if (!fp)
return FALSE;
LLPolyMesh mesh(mMesh, NULL);
LLVector3 *coords = mesh.getWritableCoords();
LLVector3 *normals = mesh.getWritableNormals();
LLVector2 *tex_coords = mesh.getWritableTexCoords();
for(U32 vert_index_morph = 0; vert_index_morph < mNumIndices; vert_index_morph++)
{
S32 vert_index_mesh = mVertexIndices[vert_index_morph];
coords[vert_index_mesh] += mCoords[vert_index_morph];
normals[vert_index_mesh] += mNormals[vert_index_morph];
normals[vert_index_mesh].normVec();
tex_coords[vert_index_mesh] += mTexCoords[vert_index_morph];
}
return mesh.saveOBJ(fp);
}
//-----------------------------------------------------------------------------
// setMorphFromMesh()
//-----------------------------------------------------------------------------
BOOL LLPolyMorphData::setMorphFromMesh(LLPolyMesh *morph)
{
if (!morph)
return FALSE;
LLVector3 *morph_coords = morph->getWritableCoords();
LLVector3 *morph_normals = morph->getWritableNormals();
LLVector3 *morph_binormals = morph->getWritableBinormals();
LLVector2 *morph_tex_coords = morph->getWritableTexCoords();
// We now have the morph loaded as a mesh. We have to subtract the
// base mesh to get the delta morph.
LLPolyMesh delta(mMesh, NULL);
U32 nverts = delta.getNumVertices();
LLVector3 *delta_coords = delta.getWritableCoords();
LLVector3 *delta_normals = delta.getWritableNormals();
LLVector3 *delta_binormals = delta.getWritableBinormals();
LLVector2 *delta_tex_coords = delta.getWritableTexCoords();
U32 num_significant = 0;
U32 vert_index;
for( vert_index = 0; vert_index < nverts; vert_index++)
{
delta_coords[vert_index] = morph_coords[vert_index] - delta_coords[vert_index];
delta_normals[vert_index] = morph_normals[vert_index] - delta_normals[vert_index];
delta_binormals[vert_index] = morph_binormals[vert_index] - delta_binormals[vert_index];
delta_tex_coords[vert_index] = morph_tex_coords[vert_index] - delta_tex_coords[vert_index];
// For the normals and binormals, we really want the deltas
// to be perpendicular to the mesh (bi)normals in the plane
// that contains both the mesh and morph (bi)normals, such
// that the morph (bi)normals form the hypotenuses of right
// triangles. Right now, we just compute the difference vector.
if (delta_coords[vert_index].length() > SIGNIFICANT_DELTA
|| delta_normals[vert_index].length() > SIGNIFICANT_DELTA
|| delta_binormals[vert_index].length() > SIGNIFICANT_DELTA
|| delta_tex_coords[vert_index].length() > SIGNIFICANT_DELTA)
{
num_significant++;
}
}
//-------------------------------------------------------------------------
// compute new morph
//-------------------------------------------------------------------------
// If the morph matches the base mesh, we store one vertex to prevent
// zero length vectors.
U32 nindices = num_significant;
if (num_significant == 0)
nindices = 1;
LLVector3* new_coords = new LLVector3[nindices];
LLVector3* new_normals = new LLVector3[nindices];
LLVector3* new_binormals = new LLVector3[nindices];
LLVector2* new_tex_coords = new LLVector2[nindices];
U32* new_vertex_indices = new U32[nindices];
// We'll set the distortion directly
mTotalDistortion = 0.f;
mMaxDistortion = 0.f;
mAvgDistortion.zeroVec();
U32 morph_index = 0;
for( vert_index = 0; vert_index < nverts; vert_index++)
{
if (delta_coords[vert_index].length() > SIGNIFICANT_DELTA
|| delta_normals[vert_index].length() > SIGNIFICANT_DELTA
|| delta_binormals[vert_index].length() > SIGNIFICANT_DELTA
|| delta_tex_coords[vert_index].length() > SIGNIFICANT_DELTA
|| num_significant == 0)
{
new_vertex_indices[morph_index] = vert_index;
new_coords[morph_index] = delta_coords[vert_index];
new_normals[morph_index] = delta_normals[vert_index];
new_binormals[morph_index] = delta_binormals[vert_index];
new_tex_coords[morph_index] = delta_tex_coords[vert_index];
F32 magnitude = new_coords[morph_index].magVec();
mTotalDistortion += magnitude;
mAvgDistortion.mV[VX] += fabs(new_coords[morph_index].mV[VX]);
mAvgDistortion.mV[VY] += fabs(new_coords[morph_index].mV[VY]);
mAvgDistortion.mV[VZ] += fabs(new_coords[morph_index].mV[VZ]);
if (magnitude > mMaxDistortion)
{
mMaxDistortion = magnitude;
}
morph_index++;
num_significant = 1;
}
}
mAvgDistortion = mAvgDistortion * (1.f/(F32)nindices);
mAvgDistortion.normVec();
//-------------------------------------------------------------------------
// compute the change in the morph
//-------------------------------------------------------------------------
// Because meshes are set by continually updating morph weights
// there is no easy way to reapply the morphs, so we just compute
// the change in this morph and apply that appropriately weighted.
for( morph_index = 0; morph_index < mNumIndices; morph_index++)
{
vert_index = mVertexIndices[morph_index];
delta_coords[vert_index] -= mCoords[morph_index];
delta_normals[vert_index] -= mNormals[morph_index];
delta_binormals[vert_index] -= mBinormals[morph_index];
delta_tex_coords[vert_index] -= mTexCoords[morph_index];
}
//-------------------------------------------------------------------------
// Update all avatars
//-------------------------------------------------------------------------
std::vector< LLCharacter* >::iterator avatar_it;
for(avatar_it = LLCharacter::sInstances.begin(); avatar_it != LLCharacter::sInstances.end(); ++avatar_it)
{
LLVOAvatar* avatarp = (LLVOAvatar*)*avatar_it;
LLPolyMorphTarget* param = (LLPolyMorphTarget*) avatarp->getVisualParam(mName.c_str());
if (!param)
{
continue;
}
F32 weight = param->getLastWeight();
if (weight == 0.0f)
{
continue;
}
LLPolyMesh* mesh = avatarp->getMesh(mMesh);
if (!mesh)
{
continue;
}
// If we have a vertex mask, just remove it. It will be recreated.
if (param->undoMask(TRUE))
{
continue;
}
LLVector3 *mesh_coords = mesh->getWritableCoords();
LLVector3 *mesh_normals = mesh->getWritableNormals();
LLVector3 *mesh_binormals = mesh->getWritableBinormals();
LLVector2 *mesh_tex_coords = mesh->getWritableTexCoords();
LLVector3 *mesh_scaled_normals = mesh->getScaledNormals();
LLVector3 *mesh_scaled_binormals = mesh->getScaledBinormals();
for( vert_index = 0; vert_index < nverts; vert_index++)
{
mesh_coords[vert_index] += delta_coords[vert_index] * weight;
mesh_tex_coords[vert_index] += delta_tex_coords[vert_index] * weight;
mesh_scaled_normals[vert_index] += delta_normals[vert_index] * weight * NORMAL_SOFTEN_FACTOR;
LLVector3 normalized_normal = mesh_scaled_normals[vert_index];
normalized_normal.normVec();
mesh_normals[vert_index] = normalized_normal;
mesh_scaled_binormals[vert_index] += delta_binormals[vert_index] * weight * NORMAL_SOFTEN_FACTOR;
LLVector3 tangent = mesh_scaled_binormals[vert_index] % normalized_normal;
LLVector3 normalized_binormal = normalized_normal % tangent;
normalized_binormal.normVec();
mesh_binormals[vert_index] = normalized_binormal;
}
avatarp->dirtyMesh();
}
//-------------------------------------------------------------------------
// reallocate vertices
//-------------------------------------------------------------------------
delete [] mVertexIndices;
delete [] mCoords;
delete [] mNormals;
delete [] mBinormals;
delete [] mTexCoords;
mVertexIndices = new_vertex_indices;
mCoords = new_coords;
mNormals = new_normals;
mBinormals = new_binormals;
mTexCoords = new_tex_coords;
mNumIndices = nindices;
return TRUE;
}
//-----------------------------------------------------------------------------
// LLPolyMorphTargetInfo()
//-----------------------------------------------------------------------------
@@ -588,10 +880,32 @@ void LLPolyMorphTarget::applyMask(U8 *maskTextureData, S32 width, S32 height, S3
else
{
// remove effect of previous mask
F32 *maskWeights = (mVertMask) ? mVertMask->getMorphMaskWeights() : NULL;
undoMask(FALSE);
}
if (maskWeights)
mLastWeight = 0.f;
mVertMask->generateMask(maskTextureData, width, height, num_components, invert, clothing_weights);
apply(mLastSex);
}
//-----------------------------------------------------------------------------
// undoMask()
//-----------------------------------------------------------------------------
BOOL LLPolyMorphTarget::undoMask(BOOL delete_mask)
{
if (!mVertMask)
{
return FALSE;
}
// remove effect of previous mask
LLVector4 *clothing_weights = getInfo()->mIsClothingMorph ? mMesh->getWritableClothingWeights() : NULL;
F32 *mask_weights = mVertMask->getMorphMaskWeights();
LLVector3 *coords = mMesh->getWritableCoords();
LLVector3 *scaled_normals = mMesh->getScaledNormals();
LLVector3 *scaled_binormals = mMesh->getScaledBinormals();
@@ -599,33 +913,42 @@ void LLPolyMorphTarget::applyMask(U8 *maskTextureData, S32 width, S32 height, S3
for(U32 vert = 0; vert < mMorphData->mNumIndices; vert++)
{
F32 lastMaskWeight = mLastWeight * maskWeights[vert];
F32 mask_weight = 1.f;
if (mask_weights)
{
mask_weight = mask_weights[vert];
}
F32 last_mask_weight = mLastWeight * mask_weight;
S32 out_vert = mMorphData->mVertexIndices[vert];
// remove effect of existing masked morph
coords[out_vert] -= mMorphData->mCoords[vert] * lastMaskWeight;
scaled_normals[out_vert] -= mMorphData->mNormals[vert] * lastMaskWeight * NORMAL_SOFTEN_FACTOR;
scaled_binormals[out_vert] -= mMorphData->mBinormals[vert] * lastMaskWeight * NORMAL_SOFTEN_FACTOR;
tex_coords[out_vert] -= mMorphData->mTexCoords[vert] * lastMaskWeight;
coords[out_vert] -= mMorphData->mCoords[vert] * last_mask_weight;
scaled_normals[out_vert] -= mMorphData->mNormals[vert] * last_mask_weight * NORMAL_SOFTEN_FACTOR;
scaled_binormals[out_vert] -= mMorphData->mBinormals[vert] * last_mask_weight * NORMAL_SOFTEN_FACTOR;
tex_coords[out_vert] -= mMorphData->mTexCoords[vert] * last_mask_weight;
if (clothing_weights)
{
LLVector3 clothing_offset = mMorphData->mCoords[vert] * lastMaskWeight;
LLVector3 clothing_offset = mMorphData->mCoords[vert] * last_mask_weight;
LLVector4* clothing_weight = &clothing_weights[out_vert];
clothing_weight->mV[VX] -= clothing_offset.mV[VX];
clothing_weight->mV[VY] -= clothing_offset.mV[VY];
clothing_weight->mV[VZ] -= clothing_offset.mV[VZ];
}
}
}
}
// set last weight to 0, since we've removed the effect of this morph
mLastWeight = 0.f;
mVertMask->generateMask(maskTextureData, width, height, num_components, invert, clothing_weights);
if (delete_mask)
{
delete mVertMask;
mVertMask = NULL;
addPendingMorphMask();
}
apply(mLastSex);
return TRUE;
}

View File

@@ -56,6 +56,10 @@ public:
BOOL loadBinary(LLFILE* fp, LLPolyMeshSharedData *mesh);
const std::string& getName() { return mName; }
BOOL saveLLM(LLFILE *fp);
BOOL saveOBJ(LLFILE *fp);
BOOL setMorphFromMesh(LLPolyMesh *morph);
public:
std::string mName;
@@ -167,6 +171,7 @@ public:
/*virtual*/ const LLVector3* getNextDistortion(U32 *index, LLPolyMesh **poly_mesh);
void applyMask(U8 *maskData, S32 width, S32 height, S32 num_components, BOOL invert);
BOOL undoMask(BOOL delete_mask);
void addPendingMorphMask() { mNumMorphMasksPending++; }
protected:

View File

@@ -182,7 +182,7 @@
#include "llparcel.h"
#include "llpolymesh.h"
#include "llprimitive.h"
#include "llresmgr.h"
#include "llselectmgr.h"
@@ -572,6 +572,13 @@ void handle_toggle_pg(void*);
void handle_dump_attachments(void *);
void handle_show_overlay_title(void*);
void handle_dump_avatar_local_textures(void*);
void handle_meshes_and_morphs(void*);
void handle_mesh_save_llm(void* data);
void handle_mesh_save_current_obj(void*);
void handle_mesh_save_obj(void*);
void handle_mesh_load_obj(void*);
void handle_morph_save_obj(void*);
void handle_morph_load_obj(void*);
void handle_debug_avatar_textures(void*);
void handle_grab_texture(void*);
BOOL enable_grab_texture(void*);
@@ -1626,6 +1633,11 @@ void init_debug_avatar_menu(LLMenuGL* menu)
// <edit>
//#endif
// </edit>
LLMenuItemCallGL* mesh_item = new LLMenuItemCallGL("Meshes And Morphs...", handle_meshes_and_morphs);
mesh_item->setUserData((void*)mesh_item); // So we can remove it later
menu->append(mesh_item);
menu->createJumpKeys();
}
@@ -8391,6 +8403,410 @@ void handle_dump_avatar_local_textures(void*)
}
}
void handle_meshes_and_morphs(void* menu_item)
{
LLMenuItemCallGL* item = (LLMenuItemCallGL*) menu_item;
LLMenuGL* parent_menu = (LLMenuGL*) item->getParent();
parent_menu->remove(item);
LLMenuGL* menu = new LLMenuGL("Meshes And Morphs");
menu->append(new LLMenuItemCallGL("Dump Avatar Mesh Info", &LLPolyMesh::dumpDiagInfo));
menu->appendSeparator();
LLVOAvatar::mesh_info_t mesh_info;
LLVOAvatar::getMeshInfo(&mesh_info);
for(LLVOAvatar::mesh_info_t::iterator info_iter = mesh_info.begin();
info_iter != mesh_info.end(); ++info_iter)
{
const std::string& type = info_iter->first;
LLVOAvatar::lod_mesh_map_t& lod_mesh = info_iter->second;
LLMenuGL* type_menu = new LLMenuGL(type);
for(LLVOAvatar::lod_mesh_map_t::iterator lod_iter = lod_mesh.begin();
lod_iter != lod_mesh.end(); ++lod_iter)
{
S32 lod = lod_iter->first;
std::string& mesh = lod_iter->second;
std::string caption = llformat ("%s LOD %d", type.c_str(), lod);
if (lod == 0)
{
caption = type;
}
LLPolyMeshSharedData* mesh_shared = LLPolyMesh::getMeshData(mesh);
LLPolyMesh::morph_list_t morph_list;
LLPolyMesh::getMorphList(mesh, &morph_list);
LLMenuGL* lod_menu = new LLMenuGL(caption);
lod_menu->append(new LLMenuItemCallGL("Save LLM", handle_mesh_save_llm, NULL, (void*) mesh_shared));
LLMenuGL* action_menu = new LLMenuGL("Base Mesh");
action_menu->append(new LLMenuItemCallGL("Save OBJ", handle_mesh_save_obj, NULL, (void*) mesh_shared));
if (lod == 0)
{
// Since an LOD mesh has only faces, we won't enable this for
// LOD meshes until we add code for processing the face commands.
action_menu->append(new LLMenuItemCallGL("Load OBJ", handle_mesh_load_obj, NULL, (void*) mesh_shared));
}
action_menu->createJumpKeys();
lod_menu->appendMenu(action_menu);
action_menu = new LLMenuGL("Current Mesh");
action_menu->append(new LLMenuItemCallGL("Save OBJ", handle_mesh_save_current_obj, NULL, (void*) mesh_shared));
action_menu->createJumpKeys();
lod_menu->appendMenu(action_menu);
lod_menu->appendSeparator();
for(LLPolyMesh::morph_list_t::iterator morph_iter = morph_list.begin();
morph_iter != morph_list.end(); ++morph_iter)
{
std::string const& morph_name = morph_iter->first;
LLPolyMorphData* morph_data = morph_iter->second;
action_menu = new LLMenuGL(morph_name);
action_menu->append(new LLMenuItemCallGL("Save OBJ", handle_morph_save_obj, NULL, (void*) morph_data));
action_menu->append(new LLMenuItemCallGL("Load OBJ", handle_morph_load_obj, NULL, (void*) morph_data));
action_menu->createJumpKeys();
lod_menu->appendMenu(action_menu);
}
lod_menu->createJumpKeys();
type_menu->appendMenu(lod_menu);
}
type_menu->createJumpKeys();
menu->appendMenu(type_menu);
}
menu->createJumpKeys();
menu->updateParent(LLMenuGL::sMenuContainer);
parent_menu->appendMenu(menu);
LLMenuGL::sMenuContainer->hideMenus();
LLFloater* tear_off_menu = LLTearOffMenu::create(menu);
tear_off_menu->setFocus(TRUE);
}
static void handle_mesh_save_llm_continued(void* data, AIFilePicker* filepicker);
void handle_mesh_save_llm(void* data)
{
LLPolyMeshSharedData* mesh_shared = (LLPolyMeshSharedData*) data;
std::string const* mesh_name = LLPolyMesh::getSharedMeshName(mesh_shared);
std::string default_path = gDirUtilp->getExpandedFilename(LL_PATH_CHARACTER, "");
if (!mesh_name)
{
llwarns << "LPolyMesh::getSharedMeshName returned NULL" << llendl;
return;
}
AIFilePicker* filepicker = new AIFilePicker;
filepicker->open(*mesh_name, FFSAVE_ALL, default_path, "mesh_llm");
filepicker->run(boost::bind(&handle_mesh_save_llm_continued, data, filepicker));
}
static void handle_mesh_save_llm_continued(void* data, AIFilePicker* filepicker)
{
if (!filepicker->hasFilename())
{
llwarns << "No file" << llendl;
return;
}
std::string selected_filename = filepicker->getFilename();
LLPolyMeshSharedData* mesh_shared = (LLPolyMeshSharedData*) data;
std::string const* mesh_name = LLPolyMesh::getSharedMeshName(mesh_shared);
llinfos << "Selected " << selected_filename << " for mesh " << *mesh_name <<llendl;
std::string bak_filename = selected_filename + ".bak";
llstat stat_selected;
llstat stat_bak;
if ((LLFile::stat(selected_filename, &stat_selected) == 0)
&& (LLFile::stat(bak_filename, &stat_bak) != 0))
{
// NB: stat returns non-zero if it can't read the file, for example
// if it doesn't exist. LLFile has no better abstraction for
// testing for file existence.
// The selected file exists, but there is no backup yet, so make one.
if (LLFile::rename(selected_filename, bak_filename) != 0 )
{
llerrs << "can't rename: " << selected_filename << llendl;
return;
}
}
LLFILE* fp = LLFile::fopen(selected_filename, "wb");
if (!fp)
{
llerrs << "can't open: " << selected_filename << llendl;
if ((LLFile::stat(bak_filename, &stat_bak) == 0)
&& (LLFile::stat(selected_filename, &stat_selected) != 0) )
{
// Rename the backup to its original name
if (LLFile::rename(bak_filename, selected_filename) != 0 )
{
llerrs << "can't rename: " << bak_filename << " back to " << selected_filename << llendl;
return;
}
}
return;
}
LLPolyMesh mesh(mesh_shared,NULL);
mesh.saveLLM(fp);
fclose(fp);
}
static void handle_mesh_save_current_obj_continued(void* data, AIFilePicker* filepicker);
void handle_mesh_save_current_obj(void* data)
{
LLPolyMeshSharedData* mesh_shared = (LLPolyMeshSharedData*) data;
std::string const* mesh_name = LLPolyMesh::getSharedMeshName(mesh_shared);
if (!mesh_name)
{
llwarns << "LPolyMesh::getSharedMeshName returned NULL" << llendl;
return;
}
std::string file_name = *mesh_name + "_current.obj";
std::string default_path = gDirUtilp->getExpandedFilename(LL_PATH_CHARACTER, "");
AIFilePicker* filepicker = new AIFilePicker;
filepicker->open(file_name, FFSAVE_ALL, default_path, "mesh_obj");
filepicker->run(boost::bind(&handle_mesh_save_current_obj_continued, data, filepicker));
}
static void handle_mesh_save_current_obj_continued(void* data, AIFilePicker* filepicker)
{
if(!filepicker->hasFilename())
{
llwarns << "No file" << llendl;
return;
}
std::string selected_filename = filepicker->getFilename();
LLPolyMeshSharedData* mesh_shared = (LLPolyMeshSharedData*)data;
std::string const* mesh_name = LLPolyMesh::getSharedMeshName(mesh_shared);
llinfos << "Selected " << selected_filename << " for mesh " << *mesh_name <<llendl;
LLFILE* fp = LLFile::fopen(selected_filename, "wb"); /*Flawfinder: ignore*/
if (!fp)
{
llerrs << "can't open: " << selected_filename << llendl;
return;
}
LLVOAvatar* avatar = gAgent.getAvatarObject();
if ( avatar )
{
LLPolyMesh* mesh = avatar->getMesh (mesh_shared);
mesh->saveOBJ(fp);
}
fclose(fp);
}
static void handle_mesh_save_obj_continued(void* data, AIFilePicker* filepicker);
void handle_mesh_save_obj(void* data)
{
LLPolyMeshSharedData* mesh_shared = (LLPolyMeshSharedData*) data;
std::string const* mesh_name = LLPolyMesh::getSharedMeshName(mesh_shared);
if (!mesh_name)
{
llwarns << "LPolyMesh::getSharedMeshName returned NULL" << llendl;
return;
}
std::string file_name = *mesh_name + ".obj";
std::string default_path = gDirUtilp->getExpandedFilename(LL_PATH_CHARACTER, "");
AIFilePicker* filepicker = new AIFilePicker;
filepicker->open(file_name, FFSAVE_ALL, default_path, "mesh_obj");
filepicker->run(boost::bind(&handle_mesh_save_obj_continued, data, filepicker));
}
static void handle_mesh_save_obj_continued(void* data, AIFilePicker* filepicker)
{
if(!filepicker->hasFilename())
{
llwarns << "No file" << llendl;
return;
}
std::string selected_filename = filepicker->getFilename();
LLPolyMeshSharedData* mesh_shared = (LLPolyMeshSharedData*) data;
std::string const* mesh_name = LLPolyMesh::getSharedMeshName(mesh_shared);
llinfos << "Selected " << selected_filename << " for mesh " << *mesh_name <<llendl;
LLFILE* fp = LLFile::fopen(selected_filename, "wb"); /*Flawfinder: ignore*/
if (!fp)
{
llerrs << "can't open: " << selected_filename << llendl;
return;
}
LLPolyMesh mesh(mesh_shared,NULL);
mesh.saveOBJ(fp);
fclose(fp);
}
static void handle_mesh_load_obj_continued(void* data, AIFilePicker* filepicker);
void handle_mesh_load_obj(void* data)
{
LLPolyMeshSharedData* mesh_shared = (LLPolyMeshSharedData*) data;
std::string const* mesh_name = LLPolyMesh::getSharedMeshName(mesh_shared);
std::string default_path = gDirUtilp->getExpandedFilename(LL_PATH_CHARACTER, "");
if (!mesh_name)
{
llwarns << "LPolyMesh::getSharedMeshName returned NULL" << llendl;
return;
}
AIFilePicker* filepicker = new AIFilePicker;
filepicker->open(FFLOAD_ALL, default_path, "mesh_obj");
filepicker->run(boost::bind(&handle_mesh_load_obj_continued, data, filepicker));
}
static void handle_mesh_load_obj_continued(void* data, AIFilePicker* filepicker)
{
if(!filepicker->hasFilename())
{
llwarns << "No file" << llendl;
return;
}
std::string selected_filename = filepicker->getFilename();
LLPolyMeshSharedData* mesh_shared = (LLPolyMeshSharedData*) data;
std::string const* mesh_name = LLPolyMesh::getSharedMeshName(mesh_shared);
llinfos << "Selected " << selected_filename << " for mesh " << *mesh_name <<llendl;
LLFILE* fp = LLFile::fopen(selected_filename, "rb"); /*Flawfinder: ignore*/
if (!fp)
{
llerrs << "can't open: " << selected_filename << llendl;
return;
}
LLPolyMesh mesh(mesh_shared,NULL);
mesh.loadOBJ(fp);
mesh.setSharedFromCurrent();
fclose(fp);
}
static void handle_morph_save_obj_continued(void* data, AIFilePicker* filepicker);
void handle_morph_save_obj(void* data)
{
LLPolyMorphData* morph_data = (LLPolyMorphData*) data;
LLPolyMeshSharedData* mesh_shared = morph_data->mMesh;
std::string const* mesh_name = LLPolyMesh::getSharedMeshName(mesh_shared);
std::string const& morph_name = morph_data->getName();
if (!mesh_name)
{
llwarns << "LPolyMesh::getSharedMeshName returned NULL" << llendl;
return;
}
llinfos << "Save morph OBJ " << morph_name << " of mesh " << *mesh_name <<llendl;
std::string file_name = *mesh_name + "." + morph_name + ".obj";
std::string default_path = gDirUtilp->getExpandedFilename(LL_PATH_CHARACTER, "");
AIFilePicker* filepicker = new AIFilePicker;
filepicker->open(file_name, FFSAVE_ALL, default_path, "mesh_obj");
filepicker->run(boost::bind(&handle_morph_save_obj_continued, data, filepicker));
}
static void handle_morph_save_obj_continued(void* data, AIFilePicker* filepicker)
{
if (!filepicker->hasFilename())
{
llwarns << "No file" << llendl;
return;
}
std::string selected_filename = filepicker->getFilename();
LLPolyMorphData* morph_data = (LLPolyMorphData*)data;
llinfos << "Selected " << selected_filename << llendl;
LLFILE* fp = LLFile::fopen(selected_filename, "wb"); /*Flawfinder: ignore*/
if (!fp)
{
llerrs << "can't open: " << selected_filename << llendl;
return;
}
morph_data->saveOBJ(fp);
fclose(fp);
}
static void handle_morph_load_obj_continued(void* data, AIFilePicker* filepicker);
void handle_morph_load_obj(void* data)
{
LLPolyMorphData* morph_data = (LLPolyMorphData*) data;
LLPolyMeshSharedData* mesh_shared = morph_data->mMesh;
std::string const* mesh_name = LLPolyMesh::getSharedMeshName(mesh_shared);
std::string const& morph_name = morph_data->getName();
std::string default_path = gDirUtilp->getExpandedFilename(LL_PATH_CHARACTER, "");
if (!mesh_name)
{
llwarns << "LPolyMesh::getSharedMeshName returned NULL" << llendl;
return;
}
llinfos << "Load morph OBJ " << morph_name << " of mesh " << *mesh_name <<llendl;
AIFilePicker* filepicker = new AIFilePicker;
filepicker->open(FFLOAD_ALL, default_path, "mesh_obj");
filepicker->run(boost::bind(&handle_morph_load_obj_continued, data, filepicker));
}
static void handle_morph_load_obj_continued(void* data, AIFilePicker* filepicker)
{
if(!filepicker->hasFilename())
{
llwarns << "No file" << llendl;
return;
}
std::string selected_filename = filepicker->getFilename();
LLPolyMorphData* morph_data = (LLPolyMorphData*) data;
LLPolyMeshSharedData* mesh_shared = morph_data->mMesh;
llinfos << "Selected " << selected_filename <<llendl;
LLFILE* fp = LLFile::fopen(selected_filename, "rb"); /*Flawfinder: ignore*/
if (!fp)
{
llerrs << "can't open: " << selected_filename << llendl;
return;
}
LLPolyMesh mesh(mesh_shared,NULL);
mesh.loadOBJ(fp);
fclose(fp);
morph_data->setMorphFromMesh(&mesh);
}
void handle_debug_avatar_textures(void*)
{
LLViewerObject* objectp = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject();

View File

@@ -1182,7 +1182,6 @@ void LLVOAvatar::getMeshInfo (mesh_info_t* mesh_info)
return;
}
// static
void LLVOAvatar::dumpBakedStatus()
{
@@ -6852,6 +6851,22 @@ void LLVOAvatar::hideSkirt()
mMeshLOD[MESH_ID_SKIRT]->setVisible(FALSE, TRUE);
}
//-----------------------------------------------------------------------------
// getMesh( LLPolyMeshSharedData *shared_data )
//-----------------------------------------------------------------------------
LLPolyMesh* LLVOAvatar::getMesh( LLPolyMeshSharedData *shared_data )
{
for (polymesh_map_t::iterator i = mMeshes.begin(); i != mMeshes.end(); ++i)
{
LLPolyMesh* mesh = i->second;
if (mesh->getSharedData() == shared_data)
{
return mesh;
}
}
return NULL;
}
//-----------------------------------------------------------------------------
// requestLayerSetUpdate()
//-----------------------------------------------------------------------------

View File

@@ -600,11 +600,6 @@ public:
void setLocTexTE( U8 te, LLViewerTexture* image, BOOL set_by_user );
void setupComposites();
typedef std::map<S32,std::string> lod_mesh_map_t;
typedef std::map<std::string,lod_mesh_map_t> mesh_info_t;
static void getMeshInfo (mesh_info_t* mesh_info);
/** Textures
** **
*******************************************************************************/
@@ -651,6 +646,7 @@ public:
void processAvatarAppearance(LLMessageSystem* mesgsys);
void hideSkirt();
void startAppearanceAnimation(BOOL set_by_user, BOOL play_sound);
LLPolyMesh* getMesh(LLPolyMeshSharedData* shared_data);
//--------------------------------------------------------------------
// Appearance morphing
@@ -663,6 +659,12 @@ private:
F32 mLastAppearanceBlendTime;
BOOL mAppearanceAnimSetByUser; //1.23
public:
typedef std::map<S32, std::string> lod_mesh_map_t;
typedef std::map<std::string, lod_mesh_map_t> mesh_info_t;
static void getMeshInfo(mesh_info_t* mesh_info);
//--------------------------------------------------------------------
// Clothing colors (convenience functions to access visual parameters)
//--------------------------------------------------------------------