Files
SingularityViewer/indra/llprimitive/llmodelloader.cpp

488 lines
12 KiB
C++

/**
* @file llmodelloader.cpp
* @brief LLModelLoader class implementation
*
* $LicenseInfo:firstyear=2004&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 "llmodelloader.h"
#include "llsdserialize.h"
#include "lljoint.h"
#include "llcallbacklist.h"
#include "llmatrix4a.h"
#include <boost/bind.hpp>
#include "apr_time.h"
std::list<LLModelLoader*> LLModelLoader::sActiveLoaderList;
void stretch_extents(LLModel* model, LLMatrix4a& mat, LLVector4a& min, LLVector4a& max, BOOL& first_transform)
{
LLVector4a box[] =
{
LLVector4a(-1, 1,-1),
LLVector4a(-1, 1, 1),
LLVector4a(-1,-1,-1),
LLVector4a(-1,-1, 1),
LLVector4a( 1, 1,-1),
LLVector4a( 1, 1, 1),
LLVector4a( 1,-1,-1),
LLVector4a( 1,-1, 1),
};
for (S32 j = 0; j < model->getNumVolumeFaces(); ++j)
{
const LLVolumeFace& face = model->getVolumeFace(j);
LLVector4a center;
center.setAdd(face.mExtents[0], face.mExtents[1]);
center.mul(0.5f);
LLVector4a size;
size.setSub(face.mExtents[1],face.mExtents[0]);
size.mul(0.5f);
for (U32 i = 0; i < 8; i++)
{
LLVector4a t;
t.setMul(size, box[i]);
t.add(center);
LLVector4a v;
mat.affineTransform(t, v);
if (first_transform)
{
first_transform = FALSE;
min = max = v;
}
else
{
update_min_max(min, max, v);
}
}
}
}
void stretch_extents(LLModel* model, LLMatrix4& mat, LLVector3& min, LLVector3& max, BOOL& first_transform)
{
LLVector4a mina, maxa;
LLMatrix4a mata;
mata.loadu(mat);
mina.load3(min.mV);
maxa.load3(max.mV);
stretch_extents(model, mata, mina, maxa, first_transform);
min.set(mina.getF32ptr());
max.set(maxa.getF32ptr());
}
//-----------------------------------------------------------------------------
// LLModelLoader
//-----------------------------------------------------------------------------
LLModelLoader::LLModelLoader(
std::string filename,
S32 lod,
load_callback_t load_cb,
joint_lookup_func_t joint_lookup_func,
texture_load_func_t texture_load_func,
state_callback_t state_cb,
void* opaque_userdata,
JointTransformMap& jointTransformMap,
JointNameSet& jointsFromNodes,
JointMap& legalJointNamesMap,
U32 maxJointsPerMesh)
: mJointList( jointTransformMap )
, mJointsFromNode( jointsFromNodes )
, LLThread("Model Loader")
, mFilename(filename)
, mLod(lod)
, mTrySLM(false)
, mFirstTransform(TRUE)
, mNumOfFetchingTextures(0)
, mLoadCallback(load_cb)
, mJointLookupFunc(joint_lookup_func)
, mTextureLoadFunc(texture_load_func)
, mStateCallback(state_cb)
, mOpaqueData(opaque_userdata)
, mRigValidJointUpload(true)
, mLegacyRigValid(true)
, mNoNormalize(false)
, mNoOptimize(false)
, mCacheOnlyHitIfRigged(false)
, mMaxJointsPerMesh(maxJointsPerMesh)
, mJointMap(legalJointNamesMap)
{
assert_main_thread();
sActiveLoaderList.push_back(this) ;
}
LLModelLoader::~LLModelLoader()
{
assert_main_thread();
sActiveLoaderList.remove(this);
}
void LLModelLoader::run()
{
doLoadModel();
doOnIdleOneTime(boost::bind(&LLModelLoader::loadModelCallback,this));
}
// static
bool LLModelLoader::getSLMFilename(const std::string& model_filename, std::string& slm_filename)
{
slm_filename = model_filename;
std::string::size_type i = model_filename.rfind(".");
if (i != std::string::npos)
{
slm_filename.replace(i, model_filename.size()-1, ".slm");
return true;
}
else
{
return false;
}
}
bool LLModelLoader::doLoadModel()
{
//first, look for a .slm file of the same name that was modified later
//than the .dae
if (mTrySLM)
{
std::string slm_filename;
if (getSLMFilename(mFilename, slm_filename))
{
llstat slm_status;
if (LLFile::stat(slm_filename, &slm_status) == 0)
{ //slm file exists
llstat dae_status;
if (LLFile::stat(mFilename, &dae_status) != 0 ||
dae_status.st_mtime < slm_status.st_mtime)
{
if (loadFromSLM(slm_filename))
{ //slm successfully loaded, if this fails, fall through and
//try loading from dae
mLod = -1; //successfully loading from an slm implicitly sets all
//LoDs
return true;
}
}
}
}
}
return OpenFile(mFilename);
}
void LLModelLoader::setLoadState(U32 state)
{
mStateCallback(state, mOpaqueData);
}
bool LLModelLoader::loadFromSLM(const std::string& filename)
{
//only need to populate mScene with data from slm
llstat stat;
if (LLFile::stat(filename, &stat))
{ //file does not exist
return false;
}
S32 file_size = (S32) stat.st_size;
llifstream ifstream(filename.c_str(), std::ifstream::in | std::ifstream::binary);
LLSD data;
LLSDSerialize::fromBinary(data, ifstream, file_size);
ifstream.close();
//build model list for each LoD
model_list model[LLModel::NUM_LODS];
if (data["version"].asInteger() != SLM_SUPPORTED_VERSION)
{ //unsupported version
return false;
}
LLSD& mesh = data["mesh"];
LLVolumeParams volume_params;
volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE);
for (S32 lod = 0; lod < LLModel::NUM_LODS; ++lod)
{
for (U32 i = 0; i < (U32)mesh.size(); ++i)
{
std::stringstream str(mesh[i].asString());
LLPointer<LLModel> loaded_model = new LLModel(volume_params, (F32) lod);
if (loaded_model->loadModel(str))
{
loaded_model->mLocalID = i;
model[lod].push_back(loaded_model);
if (lod == LLModel::LOD_HIGH)
{
if (!loaded_model->mSkinInfo.mJointNames.empty())
{
//check to see if rig is valid
critiqueRigForUploadApplicability( loaded_model->mSkinInfo.mJointNames );
}
else if (mCacheOnlyHitIfRigged)
{
return false;
}
}
}
}
}
if (model[LLModel::LOD_HIGH].empty())
{ //failed to load high lod
return false;
}
//load instance list
model_instance_list instance_list;
LLSD& instance = data["instance"];
for (U32 i = 0; i < (U32)instance.size(); ++i)
{
//deserialize instance list
instance_list.push_back(LLModelInstance(instance[i]));
//match up model instance pointers
S32 idx = instance_list[i].mLocalMeshID;
std::string instance_label = instance_list[i].mLabel;
for (U32 lod = 0; lod < LLModel::NUM_LODS; ++lod)
{
if (!model[lod].empty())
{
if (idx >= (S32)model[lod].size())
{
if (model[lod].size())
{
instance_list[i].mLOD[lod] = model[lod][0];
}
else
{
instance_list[i].mLOD[lod] = NULL;
}
continue;
}
if (model[lod][idx]
&& model[lod][idx]->mLabel.empty()
&& !instance_label.empty())
{
// restore model names
std::string name = instance_label;
switch (lod)
{
case LLModel::LOD_IMPOSTOR: name += "_LOD0"; break;
case LLModel::LOD_LOW: name += "_LOD1"; break;
case LLModel::LOD_MEDIUM: name += "_LOD2"; break;
case LLModel::LOD_PHYSICS: name += "_PHYS"; break;
case LLModel::LOD_HIGH: break;
}
model[lod][idx]->mLabel = name;
}
instance_list[i].mLOD[lod] = model[lod][idx];
}
}
if (!instance_list[i].mModel)
instance_list[i].mModel = model[LLModel::LOD_HIGH][idx];
}
// Set name for UI to use
std::string name = data["name"];
if (!name.empty())
{
model[LLModel::LOD_HIGH][0]->mRequestedLabel = name;
}
//convert instance_list to mScene
mFirstTransform = TRUE;
for (U32 i = 0; i < instance_list.size(); ++i)
{
LLModelInstance& cur_instance = instance_list[i];
mScene[cur_instance.mTransform].push_back(cur_instance);
stretch_extents(cur_instance.mModel, cur_instance.mTransform, mExtents[0], mExtents[1], mFirstTransform);
}
setLoadState( DONE );
return true;
}
//static
bool LLModelLoader::isAlive(LLModelLoader* loader)
{
if(!loader)
{
return false ;
}
std::list<LLModelLoader*>::iterator iter = sActiveLoaderList.begin() ;
for(; iter != sActiveLoaderList.end() && (*iter) != loader; ++iter) ;
return *iter == loader ;
}
void LLModelLoader::loadModelCallback()
{
mLoadCallback(mScene,mModelList,mLod, mOpaqueData);
while (!isStopped())
{ //wait until this thread is stopped before deleting self
apr_sleep(100);
}
//double check if "this" is valid before deleting it, in case it is aborted during running.
if(!isAlive(this))
{
return ;
}
delete this;
}
//-----------------------------------------------------------------------------
// critiqueRigForUploadApplicability()
//-----------------------------------------------------------------------------
void LLModelLoader::critiqueRigForUploadApplicability( const std::vector<std::string> &jointListFromAsset )
{
//Determines the following use cases for a rig:
//1. It is suitable for upload with skin weights & joint positions, or
//2. It is suitable for upload as standard av with just skin weights
bool isJointPositionUploadOK = isRigSuitableForJointPositionUpload( jointListFromAsset );
bool isRigLegacyOK = isRigLegacy( jointListFromAsset );
//It's OK that both could end up being true, both default to false
// Both start out as true and are forced to false if any mesh in
// the model file is not vald by that criterion. Note that a file
// can contain multiple meshes.
if ( !isJointPositionUploadOK )
{
// This starts out true, becomes false if false for any loaded
// mesh.
setRigValidForJointPositionUpload( false );
}
if ( !isRigLegacyOK)
{
// This starts out true, becomes false if false for any loaded
// mesh.
setLegacyRigValid( false );
}
}
//-----------------------------------------------------------------------------
// isRigLegacy()
//-----------------------------------------------------------------------------
bool LLModelLoader::isRigLegacy( const std::vector<std::string> &jointListFromAsset )
{
//No joints in asset
if ( jointListFromAsset.size() == 0 )
{
return false;
}
// Too many joints in asset
if (jointListFromAsset.size()>mMaxJointsPerMesh)
{
LL_WARNS() << "Rigged to " << jointListFromAsset.size() << " joints, max is " << mMaxJointsPerMesh << LL_ENDL;
LL_WARNS() << "Skinning disabled due to too many joints" << LL_ENDL;
return false;
}
// Unknown joints in asset
S32 unknown_joint_count = 0;
for (std::vector<std::string>::const_iterator it = jointListFromAsset.begin();
it != jointListFromAsset.end(); ++it)
{
if (mJointMap.find(*it)==mJointMap.end())
{
LL_WARNS() << "Rigged to unrecognized joint name " << *it << LL_ENDL;
unknown_joint_count++;
}
}
if (unknown_joint_count>0)
{
LL_WARNS() << "Skinning disabled due to unknown joints" << LL_ENDL;
return false;
}
return true;
}
//-----------------------------------------------------------------------------
// isRigSuitableForJointPositionUpload()
//-----------------------------------------------------------------------------
bool LLModelLoader::isRigSuitableForJointPositionUpload( const std::vector<std::string> &jointListFromAsset )
{
return true;
}
//called in the main thread
void LLModelLoader::loadTextures()
{
BOOL is_paused = isPaused() ;
pause() ; //pause the loader
for(scene::iterator iter = mScene.begin(); iter != mScene.end(); ++iter)
{
for(U32 i = 0 ; i < iter->second.size(); i++)
{
for(std::map<std::string, LLImportMaterial>::iterator j = iter->second[i].mMaterial.begin();
j != iter->second[i].mMaterial.end(); ++j)
{
LLImportMaterial& material = j->second;
if(!material.mDiffuseMapFilename.empty())
{
mNumOfFetchingTextures += mTextureLoadFunc(material, mOpaqueData);
}
}
}
}
if(!is_paused)
{
unpause() ;
}
}