SL-17653 Basic local gltf materials
parent
15cd6c1cb7
commit
4bb419031c
|
|
@ -394,6 +394,7 @@ set(viewer_SOURCE_FILES
|
|||
lllistcontextmenu.cpp
|
||||
lllistview.cpp
|
||||
lllocalbitmaps.cpp
|
||||
lllocalrendermaterials.cpp
|
||||
lllocationhistory.cpp
|
||||
lllocationinputctrl.cpp
|
||||
lllogchat.cpp
|
||||
|
|
@ -607,6 +608,7 @@ set(viewer_SOURCE_FILES
|
|||
lltextureinfodetails.cpp
|
||||
lltexturestats.cpp
|
||||
lltextureview.cpp
|
||||
lltinygltfhelper.cpp
|
||||
lltoast.cpp
|
||||
lltoastalertpanel.cpp
|
||||
lltoastgroupnotifypanel.cpp
|
||||
|
|
@ -1036,6 +1038,7 @@ set(viewer_HEADER_FILES
|
|||
lllistcontextmenu.h
|
||||
lllistview.h
|
||||
lllocalbitmaps.h
|
||||
lllocalrendermaterials.h
|
||||
lllocationhistory.h
|
||||
lllocationinputctrl.h
|
||||
lllogchat.h
|
||||
|
|
@ -1240,6 +1243,7 @@ set(viewer_HEADER_FILES
|
|||
lltextureinfodetails.h
|
||||
lltexturestats.h
|
||||
lltextureview.h
|
||||
lltinygltfhelper.h
|
||||
lltoast.h
|
||||
lltoastalertpanel.h
|
||||
lltoastgroupnotifypanel.h
|
||||
|
|
|
|||
|
|
@ -30,86 +30,13 @@
|
|||
#include "llassetstorage.h"
|
||||
#include "llfilesystem.h"
|
||||
#include "llsdserialize.h"
|
||||
#include "lltinygltfhelper.h"
|
||||
|
||||
#include "tinygltf/tiny_gltf.h"
|
||||
#include <strstream>
|
||||
|
||||
LLGLTFMaterialList gGLTFMaterialList;
|
||||
|
||||
static LLColor4 get_color(const std::vector<double>& in)
|
||||
{
|
||||
LLColor4 out;
|
||||
for (S32 i = 0; i < llmin((S32)in.size(), 4); ++i)
|
||||
{
|
||||
out.mV[i] = in[i];
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
static void set_from_model(LLGLTFMaterial* mat, tinygltf::Model& model)
|
||||
{
|
||||
|
||||
S32 index;
|
||||
|
||||
auto& material_in = model.materials[0];
|
||||
|
||||
// get albedo texture
|
||||
index = material_in.pbrMetallicRoughness.baseColorTexture.index;
|
||||
if (index >= 0)
|
||||
{
|
||||
mat->mAlbedoId.set(model.images[index].uri);
|
||||
}
|
||||
else
|
||||
{
|
||||
mat->mAlbedoId.setNull();
|
||||
}
|
||||
|
||||
// get normal map
|
||||
index = material_in.normalTexture.index;
|
||||
if (index >= 0)
|
||||
{
|
||||
mat->mNormalId.set(model.images[index].uri);
|
||||
}
|
||||
else
|
||||
{
|
||||
mat->mNormalId.setNull();
|
||||
}
|
||||
|
||||
// get metallic-roughness texture
|
||||
index = material_in.pbrMetallicRoughness.metallicRoughnessTexture.index;
|
||||
if (index >= 0)
|
||||
{
|
||||
mat->mMetallicRoughnessId.set(model.images[index].uri);
|
||||
}
|
||||
else
|
||||
{
|
||||
mat->mMetallicRoughnessId.setNull();
|
||||
}
|
||||
|
||||
// get emissive texture
|
||||
index = material_in.emissiveTexture.index;
|
||||
if (index >= 0)
|
||||
{
|
||||
mat->mEmissiveId.set(model.images[index].uri);
|
||||
}
|
||||
else
|
||||
{
|
||||
mat->mEmissiveId.setNull();
|
||||
}
|
||||
|
||||
mat->setAlphaMode(material_in.alphaMode);
|
||||
mat->mAlphaCutoff = llclamp((F32)material_in.alphaCutoff, 0.f, 1.f);
|
||||
|
||||
mat->mAlbedoColor = get_color(material_in.pbrMetallicRoughness.baseColorFactor);
|
||||
mat->mEmissiveColor = get_color(material_in.emissiveFactor);
|
||||
|
||||
mat->mMetallicFactor = llclamp((F32)material_in.pbrMetallicRoughness.metallicFactor, 0.f, 1.f);
|
||||
mat->mRoughnessFactor = llclamp((F32)material_in.pbrMetallicRoughness.roughnessFactor, 0.f, 1.f);
|
||||
|
||||
mat->mDoubleSided = material_in.doubleSided;
|
||||
}
|
||||
|
||||
LLGLTFMaterial* LLGLTFMaterialList::getMaterial(const LLUUID& id)
|
||||
{
|
||||
List::iterator iter = mList.find(id);
|
||||
|
|
@ -158,7 +85,7 @@ LLGLTFMaterial* LLGLTFMaterialList::getMaterial(const LLUUID& id)
|
|||
|
||||
if (loader.LoadASCIIFromString(&model_in, &error_msg, &warn_msg, data.c_str(), data.length(), ""))
|
||||
{
|
||||
set_from_model(mat, model_in);
|
||||
LLTinyGLTFHelper::setFromModel(mat, model_in);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -184,3 +111,13 @@ LLGLTFMaterial* LLGLTFMaterialList::getMaterial(const LLUUID& id)
|
|||
return iter->second;
|
||||
}
|
||||
|
||||
void LLGLTFMaterialList::addMaterial(const LLUUID& id, LLGLTFMaterial* material)
|
||||
{
|
||||
mList[id] = material;
|
||||
}
|
||||
|
||||
void LLGLTFMaterialList::removeMaterial(const LLUUID& id)
|
||||
{
|
||||
mList.erase(id);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -41,6 +41,9 @@ public:
|
|||
|
||||
LLGLTFMaterial* getMaterial(const LLUUID& id);
|
||||
|
||||
void addMaterial(const LLUUID& id, LLGLTFMaterial* material);
|
||||
void removeMaterial(const LLUUID& id);
|
||||
|
||||
};
|
||||
|
||||
extern LLGLTFMaterialList gGLTFMaterialList;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,734 @@
|
|||
/**
|
||||
* @file lllocalrendermaterials.cpp
|
||||
* @brief Local GLTF materials source
|
||||
*
|
||||
* $LicenseInfo:firstyear=2022&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2022, 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$
|
||||
*/
|
||||
|
||||
/* precompiled headers */
|
||||
#include "llviewerprecompiledheaders.h"
|
||||
|
||||
/* own header */
|
||||
#include "lllocalrendermaterials.h"
|
||||
|
||||
/* boost: will not compile unless equivalent is undef'd, beware. */
|
||||
#include "fix_macros.h"
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
/* time headers */
|
||||
#include <time.h>
|
||||
#include <ctime>
|
||||
|
||||
/* misc headers */
|
||||
#include "llagentwearables.h"
|
||||
#include "llface.h"
|
||||
#include "llfilepicker.h"
|
||||
#include "llgltfmateriallist.h"
|
||||
#include "llimagedimensionsinfo.h"
|
||||
#include "lllocaltextureobject.h"
|
||||
#include "llmaterialmgr.h"
|
||||
#include "llnotificationsutil.h"
|
||||
#include "llscrolllistctrl.h"
|
||||
#include "lltexlayerparams.h"
|
||||
#include "lltinygltfhelper.h"
|
||||
#include "lltrans.h"
|
||||
#include "llviewercontrol.h"
|
||||
#include "llviewerdisplay.h"
|
||||
#include "llviewerobjectlist.h"
|
||||
#include "llviewerobject.h"
|
||||
#include "llviewerwearable.h"
|
||||
#include "llvoavatarself.h"
|
||||
#include "llvovolume.h"
|
||||
#include "pipeline.h"
|
||||
#include "tinygltf/tiny_gltf.h"
|
||||
|
||||
/*=======================================*/
|
||||
/* Formal declarations, constants, etc. */
|
||||
/*=======================================*/
|
||||
|
||||
static const F32 LL_LOCAL_TIMER_HEARTBEAT = 3.0;
|
||||
static const BOOL LL_LOCAL_USE_MIPMAPS = true;
|
||||
static const S32 LL_LOCAL_DISCARD_LEVEL = 0;
|
||||
static const bool LL_LOCAL_SLAM_FOR_DEBUG = true;
|
||||
static const bool LL_LOCAL_REPLACE_ON_DEL = true;
|
||||
static const S32 LL_LOCAL_UPDATE_RETRIES = 5;
|
||||
|
||||
/*=======================================*/
|
||||
/* LLLocalGLTFMaterial: unit class */
|
||||
/*=======================================*/
|
||||
LLLocalGLTFMaterial::LLLocalGLTFMaterial(std::string filename)
|
||||
: mFilename(filename)
|
||||
, mShortName(gDirUtilp->getBaseFileName(filename, true))
|
||||
, mValid(false)
|
||||
, mLastModified()
|
||||
, mLinkStatus(LS_ON)
|
||||
, mUpdateRetries(LL_LOCAL_UPDATE_RETRIES)
|
||||
{
|
||||
mTrackingID.generate();
|
||||
|
||||
/* extension */
|
||||
std::string temp_exten = gDirUtilp->getExtension(mFilename);
|
||||
|
||||
if (temp_exten == "gltf")
|
||||
{
|
||||
mExtension = ET_MATERIAL_GLTF;
|
||||
}
|
||||
else if (temp_exten == "glb")
|
||||
{
|
||||
mExtension = ET_MATERIAL_GLB;
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS() << "File of no valid extension given, local material creation aborted." << "\n"
|
||||
<< "Filename: " << mFilename << LL_ENDL;
|
||||
return; // no valid extension.
|
||||
}
|
||||
|
||||
/* next phase of unit creation is nearly the same as an update cycle.
|
||||
we're running updateSelf as a special case with the optional UT_FIRSTUSE
|
||||
which omits the parts associated with removing the outdated texture */
|
||||
mValid = updateSelf(UT_FIRSTUSE);
|
||||
}
|
||||
|
||||
LLLocalGLTFMaterial::~LLLocalGLTFMaterial()
|
||||
{
|
||||
// replace IDs with defaults, if set to do so.
|
||||
if(LL_LOCAL_REPLACE_ON_DEL && mValid && gAgentAvatarp) // fix for STORM-1837
|
||||
{
|
||||
replaceIDs(mWorldID, IMG_DEFAULT);
|
||||
LLLocalGLTFMaterialMgr::getInstance()->doRebake();
|
||||
}
|
||||
|
||||
// delete self from material list
|
||||
gGLTFMaterialList.removeMaterial(mWorldID);
|
||||
}
|
||||
|
||||
/* accessors */
|
||||
std::string LLLocalGLTFMaterial::getFilename()
|
||||
{
|
||||
return mFilename;
|
||||
}
|
||||
|
||||
std::string LLLocalGLTFMaterial::getShortName()
|
||||
{
|
||||
return mShortName;
|
||||
}
|
||||
|
||||
LLUUID LLLocalGLTFMaterial::getTrackingID()
|
||||
{
|
||||
return mTrackingID;
|
||||
}
|
||||
|
||||
LLUUID LLLocalGLTFMaterial::getWorldID()
|
||||
{
|
||||
return mWorldID;
|
||||
}
|
||||
|
||||
bool LLLocalGLTFMaterial::getValid()
|
||||
{
|
||||
return mValid;
|
||||
}
|
||||
|
||||
/* update functions */
|
||||
bool LLLocalGLTFMaterial::updateSelf(EUpdateType optional_firstupdate)
|
||||
{
|
||||
bool updated = false;
|
||||
|
||||
if (mLinkStatus == LS_ON)
|
||||
{
|
||||
// verifying that the file exists
|
||||
if (gDirUtilp->fileExists(mFilename))
|
||||
{
|
||||
// verifying that the file has indeed been modified
|
||||
|
||||
#ifndef LL_WINDOWS
|
||||
const std::time_t temp_time = boost::filesystem::last_write_time(boost::filesystem::path(mFilename));
|
||||
#else
|
||||
const std::time_t temp_time = boost::filesystem::last_write_time(boost::filesystem::path(utf8str_to_utf16str(mFilename)));
|
||||
#endif
|
||||
LLSD new_last_modified = asctime(localtime(&temp_time));
|
||||
|
||||
if (mLastModified.asString() != new_last_modified.asString())
|
||||
{
|
||||
LLPointer<LLGLTFMaterial> raw_material = new LLGLTFMaterial();
|
||||
if (loadMaterial(raw_material))
|
||||
{
|
||||
// decode is successful, we can safely proceed.
|
||||
LLUUID old_id = LLUUID::null;
|
||||
if ((optional_firstupdate != UT_FIRSTUSE) && !mWorldID.isNull())
|
||||
{
|
||||
old_id = mWorldID;
|
||||
}
|
||||
mWorldID.generate();
|
||||
mLastModified = new_last_modified;
|
||||
|
||||
gGLTFMaterialList.addMaterial(mWorldID, raw_material);
|
||||
|
||||
if (optional_firstupdate != UT_FIRSTUSE)
|
||||
{
|
||||
// seek out everything old_id uses and replace it with mWorldID
|
||||
replaceIDs(old_id, mWorldID);
|
||||
|
||||
// remove old_id from material list
|
||||
gGLTFMaterialList.removeMaterial(old_id);
|
||||
}
|
||||
|
||||
mUpdateRetries = LL_LOCAL_UPDATE_RETRIES;
|
||||
updated = true;
|
||||
}
|
||||
|
||||
// if decoding failed, we get here and it will attempt to decode it in the next cycles
|
||||
// until mUpdateRetries runs out. this is done because some software lock the material while writing to it
|
||||
else
|
||||
{
|
||||
if (mUpdateRetries)
|
||||
{
|
||||
mUpdateRetries--;
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS() << "During the update process the following file was found" << "\n"
|
||||
<< "but could not be opened or decoded for " << LL_LOCAL_UPDATE_RETRIES << " attempts." << "\n"
|
||||
<< "Filename: " << mFilename << "\n"
|
||||
<< "Disabling further update attempts for this file." << LL_ENDL;
|
||||
|
||||
LLSD notif_args;
|
||||
notif_args["FNAME"] = mFilename;
|
||||
notif_args["NRETRIES"] = LL_LOCAL_UPDATE_RETRIES;
|
||||
LLNotificationsUtil::add("LocalBitmapsUpdateFailedFinal", notif_args);
|
||||
|
||||
mLinkStatus = LS_BROKEN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // end if file exists
|
||||
|
||||
else
|
||||
{
|
||||
LL_WARNS() << "During the update process, the following file was not found." << "\n"
|
||||
<< "Filename: " << mFilename << "\n"
|
||||
<< "Disabling further update attempts for this file." << LL_ENDL;
|
||||
|
||||
LLSD notif_args;
|
||||
notif_args["FNAME"] = mFilename;
|
||||
LLNotificationsUtil::add("LocalBitmapsUpdateFileNotFound", notif_args);
|
||||
|
||||
mLinkStatus = LS_BROKEN;
|
||||
}
|
||||
}
|
||||
|
||||
return updated;
|
||||
}
|
||||
|
||||
bool LLLocalGLTFMaterial::loadMaterial(LLPointer<LLGLTFMaterial> mat)
|
||||
{
|
||||
bool decode_successful = false;
|
||||
|
||||
switch (mExtension)
|
||||
{
|
||||
case ET_MATERIAL_GLTF:
|
||||
case ET_MATERIAL_GLB:
|
||||
{
|
||||
tinygltf::TinyGLTF loader;
|
||||
std::string error_msg;
|
||||
std::string warn_msg;
|
||||
|
||||
tinygltf::Model model_in;
|
||||
|
||||
std::string filename_lc = mFilename;
|
||||
LLStringUtil::toLower(filename_lc);
|
||||
|
||||
// Load a tinygltf model fom a file. Assumes that the input filename has already been
|
||||
// been sanitized to one of (.gltf , .glb) extensions, so does a simple find to distinguish.
|
||||
if (std::string::npos == filename_lc.rfind(".gltf"))
|
||||
{ // file is binary
|
||||
decode_successful = loader.LoadBinaryFromFile(&model_in, &error_msg, &warn_msg, filename_lc);
|
||||
}
|
||||
else
|
||||
{ // file is ascii
|
||||
decode_successful = loader.LoadASCIIFromFile(&model_in, &error_msg, &warn_msg, filename_lc);
|
||||
}
|
||||
|
||||
if (!decode_successful)
|
||||
{
|
||||
LL_WARNS() << "Cannot Upload Material, error: " << error_msg
|
||||
<< ", warning:" << warn_msg
|
||||
<< " file: " << mFilename
|
||||
<< LL_ENDL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (model_in.materials.empty())
|
||||
{
|
||||
// materials are missing
|
||||
LL_WARNS() << "Cannot Upload Material, Material missing, " << mFilename << LL_ENDL;
|
||||
decode_successful = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// sets everything, but textures will have inaccurate ids
|
||||
LLTinyGLTFHelper::setFromModel(mat, model_in);
|
||||
|
||||
std::string folder = gDirUtilp->getDirName(filename_lc);
|
||||
tinygltf::Material material_in = model_in.materials[0];
|
||||
|
||||
// get albedo texture
|
||||
LLPointer<LLImageRaw> albedo_img = LLTinyGLTFHelper::getTexture(folder, model_in, material_in.pbrMetallicRoughness.baseColorTexture.index);
|
||||
// get normal map
|
||||
LLPointer<LLImageRaw> normal_img = LLTinyGLTFHelper::getTexture(folder, model_in, material_in.normalTexture.index);
|
||||
// get metallic-roughness texture
|
||||
LLPointer<LLImageRaw> mr_img = LLTinyGLTFHelper::getTexture(folder, model_in, material_in.pbrMetallicRoughness.metallicRoughnessTexture.index);
|
||||
// get emissive texture
|
||||
LLPointer<LLImageRaw> emissive_img = LLTinyGLTFHelper::getTexture(folder, model_in, material_in.emissiveTexture.index);
|
||||
// get occlusion map if needed
|
||||
LLPointer<LLImageRaw> occlusion_img;
|
||||
if (material_in.occlusionTexture.index != material_in.pbrMetallicRoughness.metallicRoughnessTexture.index)
|
||||
{
|
||||
occlusion_img = LLTinyGLTFHelper::getTexture(folder, model_in, material_in.occlusionTexture.index);
|
||||
}
|
||||
|
||||
// todo: pass it into local bitmaps?
|
||||
LLTinyGLTFHelper::initFetchedTextures(material_in,
|
||||
albedo_img, normal_img, mr_img, emissive_img, occlusion_img,
|
||||
mAlbedoFetched, mNormalFetched, mMRFetched, mEmissiveFetched);
|
||||
|
||||
mat->mAlbedoId = mAlbedoFetched->getID();
|
||||
mat->mNormalId = mNormalFetched->getID();
|
||||
mat->mMetallicRoughnessId = mMRFetched->getID();
|
||||
mat->mEmissiveId = mEmissiveFetched->getID();
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
// separating this into -several- LL_WARNS() calls because in the extremely unlikely case that this happens
|
||||
// accessing mFilename and any other object properties might very well crash the viewer.
|
||||
// getting here should be impossible, or there's been a pretty serious bug.
|
||||
|
||||
LL_WARNS() << "During a decode attempt, the following local material had no properly assigned extension." << LL_ENDL;
|
||||
LL_WARNS() << "Filename: " << mFilename << LL_ENDL;
|
||||
LL_WARNS() << "Disabling further update attempts for this file." << LL_ENDL;
|
||||
mLinkStatus = LS_BROKEN;
|
||||
}
|
||||
}
|
||||
|
||||
return decode_successful;
|
||||
}
|
||||
|
||||
void LLLocalGLTFMaterial::replaceIDs(LLUUID old_id, LLUUID new_id)
|
||||
{
|
||||
// checking for misuse.
|
||||
if (old_id == new_id)
|
||||
{
|
||||
LL_INFOS() << "An attempt was made to replace a texture with itself. (matching UUIDs)" << "\n"
|
||||
<< "Texture UUID: " << old_id.asString() << LL_ENDL;
|
||||
return;
|
||||
}
|
||||
|
||||
// processing updates per channel; makes the process scalable.
|
||||
// the only actual difference is in SetTE* call i.e. SetTETexture, SetTENormal, etc.
|
||||
updateUserPrims(old_id, new_id, LLRender::DIFFUSE_MAP);
|
||||
updateUserPrims(old_id, new_id, LLRender::NORMAL_MAP);
|
||||
updateUserPrims(old_id, new_id, LLRender::SPECULAR_MAP);
|
||||
|
||||
// default safeguard image for layers
|
||||
if (new_id == IMG_DEFAULT)
|
||||
{
|
||||
new_id = IMG_DEFAULT_AVATAR;
|
||||
}
|
||||
}
|
||||
|
||||
// this function sorts the faces from a getFaceList[getNumFaces] into a list of objects
|
||||
// in order to prevent multiple sendTEUpdate calls per object during updateUserPrims
|
||||
std::vector<LLViewerObject*> LLLocalGLTFMaterial::prepUpdateObjects(LLUUID old_id, U32 channel)
|
||||
{
|
||||
std::vector<LLViewerObject*> obj_list;
|
||||
// todo: find a way to update materials
|
||||
/*
|
||||
LLGLTFMaterial* old_material = gGLTFMaterialList.getMaterial(old_id);
|
||||
|
||||
for(U32 face_iterator = 0; face_iterator < old_texture->getNumFaces(channel); face_iterator++)
|
||||
{
|
||||
// getting an object from a face
|
||||
LLFace* face_to_object = (*old_texture->getFaceList(channel))[face_iterator];
|
||||
|
||||
if(face_to_object)
|
||||
{
|
||||
LLViewerObject* affected_object = face_to_object->getViewerObject();
|
||||
|
||||
if(affected_object)
|
||||
{
|
||||
|
||||
// we have an object, we'll take it's UUID and compare it to
|
||||
// whatever we already have in the returnable object list.
|
||||
// if there is a match - we do not add (to prevent duplicates)
|
||||
LLUUID mainlist_obj_id = affected_object->getID();
|
||||
bool add_object = true;
|
||||
|
||||
// begin looking for duplicates
|
||||
std::vector<LLViewerObject*>::iterator objlist_iter = obj_list.begin();
|
||||
for(; (objlist_iter != obj_list.end()) && add_object; objlist_iter++)
|
||||
{
|
||||
LLViewerObject* obj = *objlist_iter;
|
||||
if (obj->getID() == mainlist_obj_id)
|
||||
{
|
||||
add_object = false; // duplicate found.
|
||||
}
|
||||
}
|
||||
// end looking for duplicates
|
||||
|
||||
if(add_object)
|
||||
{
|
||||
obj_list.push_back(affected_object);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // end of face-iterating for()
|
||||
|
||||
*/
|
||||
return obj_list;
|
||||
}
|
||||
|
||||
void LLLocalGLTFMaterial::updateUserPrims(LLUUID old_id, LLUUID new_id, U32 channel)
|
||||
{
|
||||
/*std::vector<LLViewerObject*> objectlist = prepUpdateObjects(old_id, channel);
|
||||
for(std::vector<LLViewerObject*>::iterator object_iterator = objectlist.begin();
|
||||
object_iterator != objectlist.end(); object_iterator++)
|
||||
{
|
||||
LLViewerObject* object = *object_iterator;
|
||||
|
||||
if(object)
|
||||
{
|
||||
bool update_tex = false;
|
||||
bool update_mat = false;
|
||||
S32 num_faces = object->getNumFaces();
|
||||
|
||||
for (U8 face_iter = 0; face_iter < num_faces; face_iter++)
|
||||
{
|
||||
if (object->mDrawable)
|
||||
{
|
||||
LLFace* face = object->mDrawable->getFace(face_iter);
|
||||
if (face && face->getTexture(channel) && face->getTexture(channel)->getID() == old_id)
|
||||
{
|
||||
// these things differ per channel, unless there already is a universal
|
||||
// texture setting function to setTE that takes channel as a param?
|
||||
// p.s.: switch for now, might become if - if an extra test is needed to verify before touching normalmap/specmap
|
||||
switch(channel)
|
||||
{
|
||||
case LLRender::DIFFUSE_MAP:
|
||||
{
|
||||
object->setTETexture(face_iter, new_id);
|
||||
update_tex = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case LLRender::NORMAL_MAP:
|
||||
{
|
||||
object->setTENormalMap(face_iter, new_id);
|
||||
update_mat = true;
|
||||
update_tex = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case LLRender::SPECULAR_MAP:
|
||||
{
|
||||
object->setTESpecularMap(face_iter, new_id);
|
||||
update_mat = true;
|
||||
update_tex = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// end switch
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (update_tex)
|
||||
{
|
||||
object->sendTEUpdate();
|
||||
}
|
||||
|
||||
if (update_mat)
|
||||
{
|
||||
object->mDrawable->getVOVolume()->faceMappingChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
/*=======================================*/
|
||||
/* LLLocalGLTFMaterialTimer: timer class */
|
||||
/*=======================================*/
|
||||
LLLocalGLTFMaterialTimer::LLLocalGLTFMaterialTimer() : LLEventTimer(LL_LOCAL_TIMER_HEARTBEAT)
|
||||
{
|
||||
}
|
||||
|
||||
LLLocalGLTFMaterialTimer::~LLLocalGLTFMaterialTimer()
|
||||
{
|
||||
}
|
||||
|
||||
void LLLocalGLTFMaterialTimer::startTimer()
|
||||
{
|
||||
mEventTimer.start();
|
||||
}
|
||||
|
||||
void LLLocalGLTFMaterialTimer::stopTimer()
|
||||
{
|
||||
mEventTimer.stop();
|
||||
}
|
||||
|
||||
bool LLLocalGLTFMaterialTimer::isRunning()
|
||||
{
|
||||
return mEventTimer.getStarted();
|
||||
}
|
||||
|
||||
BOOL LLLocalGLTFMaterialTimer::tick()
|
||||
{
|
||||
// todo: do on idle? No point in timer
|
||||
LLLocalGLTFMaterialMgr::getInstance()->doUpdates();
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*=======================================*/
|
||||
/* LLLocalGLTFMaterialMgr: manager class */
|
||||
/*=======================================*/
|
||||
LLLocalGLTFMaterialMgr::LLLocalGLTFMaterialMgr()
|
||||
{
|
||||
}
|
||||
|
||||
LLLocalGLTFMaterialMgr::~LLLocalGLTFMaterialMgr()
|
||||
{
|
||||
std::for_each(mMaterialList.begin(), mMaterialList.end(), DeletePointer());
|
||||
mMaterialList.clear();
|
||||
}
|
||||
|
||||
bool LLLocalGLTFMaterialMgr::addUnit()
|
||||
{
|
||||
bool add_successful = false;
|
||||
|
||||
LLFilePicker& picker = LLFilePicker::instance();
|
||||
if (picker.getMultipleOpenFiles(LLFilePicker::FFLOAD_IMAGE))
|
||||
{
|
||||
mTimer.stopTimer();
|
||||
|
||||
std::string filename = picker.getFirstFile();
|
||||
while (!filename.empty())
|
||||
{
|
||||
if (!checkTextureDimensions(filename))
|
||||
{
|
||||
filename = picker.getNextFile();
|
||||
continue;
|
||||
}
|
||||
|
||||
LLLocalGLTFMaterial* unit = new LLLocalGLTFMaterial(filename);
|
||||
|
||||
if (unit->getValid())
|
||||
{
|
||||
mMaterialList.push_back(unit);
|
||||
add_successful = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS() << "Attempted to add invalid or unreadable image file, attempt cancelled.\n"
|
||||
<< "Filename: " << filename << LL_ENDL;
|
||||
|
||||
LLSD notif_args;
|
||||
notif_args["FNAME"] = filename;
|
||||
LLNotificationsUtil::add("LocalBitmapsVerifyFail", notif_args);
|
||||
|
||||
delete unit;
|
||||
unit = NULL;
|
||||
}
|
||||
|
||||
filename = picker.getNextFile();
|
||||
}
|
||||
|
||||
mTimer.startTimer();
|
||||
}
|
||||
|
||||
return add_successful;
|
||||
}
|
||||
|
||||
bool LLLocalGLTFMaterialMgr::checkTextureDimensions(std::string filename)
|
||||
{
|
||||
std::string exten = gDirUtilp->getExtension(filename);
|
||||
U32 codec = LLImageBase::getCodecFromExtension(exten);
|
||||
std::string mImageLoadError;
|
||||
LLImageDimensionsInfo image_info;
|
||||
if (!image_info.load(filename, codec))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
S32 max_width = gSavedSettings.getS32("max_texture_dimension_X");
|
||||
S32 max_height = gSavedSettings.getS32("max_texture_dimension_Y");
|
||||
|
||||
if ((image_info.getWidth() > max_width) || (image_info.getHeight() > max_height))
|
||||
{
|
||||
LLStringUtil::format_map_t args;
|
||||
args["WIDTH"] = llformat("%d", max_width);
|
||||
args["HEIGHT"] = llformat("%d", max_height);
|
||||
mImageLoadError = LLTrans::getString("texture_load_dimensions_error", args);
|
||||
|
||||
LLSD notif_args;
|
||||
notif_args["REASON"] = mImageLoadError;
|
||||
LLNotificationsUtil::add("CannotUploadTexture", notif_args);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void LLLocalGLTFMaterialMgr::delUnit(LLUUID tracking_id)
|
||||
{
|
||||
if (!mMaterialList.empty())
|
||||
{
|
||||
std::vector<LLLocalGLTFMaterial*> to_delete;
|
||||
for (local_list_iter iter = mMaterialList.begin(); iter != mMaterialList.end(); iter++)
|
||||
{ /* finding which ones we want deleted and making a separate list */
|
||||
LLLocalGLTFMaterial* unit = *iter;
|
||||
if (unit->getTrackingID() == tracking_id)
|
||||
{
|
||||
to_delete.push_back(unit);
|
||||
}
|
||||
}
|
||||
|
||||
for (std::vector<LLLocalGLTFMaterial*>::iterator del_iter = to_delete.begin();
|
||||
del_iter != to_delete.end(); del_iter++)
|
||||
{ /* iterating over a temporary list, hence preserving the iterator validity while deleting. */
|
||||
LLLocalGLTFMaterial* unit = *del_iter;
|
||||
mMaterialList.remove(unit);
|
||||
delete unit;
|
||||
unit = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LLUUID LLLocalGLTFMaterialMgr::getWorldID(LLUUID tracking_id)
|
||||
{
|
||||
LLUUID world_id = LLUUID::null;
|
||||
|
||||
for (local_list_iter iter = mMaterialList.begin(); iter != mMaterialList.end(); iter++)
|
||||
{
|
||||
LLLocalGLTFMaterial* unit = *iter;
|
||||
if (unit->getTrackingID() == tracking_id)
|
||||
{
|
||||
world_id = unit->getWorldID();
|
||||
}
|
||||
}
|
||||
|
||||
return world_id;
|
||||
}
|
||||
|
||||
bool LLLocalGLTFMaterialMgr::isLocal(const LLUUID world_id)
|
||||
{
|
||||
for (local_list_iter iter = mMaterialList.begin(); iter != mMaterialList.end(); iter++)
|
||||
{
|
||||
LLLocalGLTFMaterial* unit = *iter;
|
||||
if (unit->getWorldID() == world_id)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string LLLocalGLTFMaterialMgr::getFilename(LLUUID tracking_id)
|
||||
{
|
||||
std::string filename = "";
|
||||
|
||||
for (local_list_iter iter = mMaterialList.begin(); iter != mMaterialList.end(); iter++)
|
||||
{
|
||||
LLLocalGLTFMaterial* unit = *iter;
|
||||
if (unit->getTrackingID() == tracking_id)
|
||||
{
|
||||
filename = unit->getFilename();
|
||||
}
|
||||
}
|
||||
|
||||
return filename;
|
||||
}
|
||||
|
||||
void LLLocalGLTFMaterialMgr::feedScrollList(LLScrollListCtrl* ctrl)
|
||||
{
|
||||
if (ctrl)
|
||||
{
|
||||
ctrl->clearRows();
|
||||
|
||||
if (!mMaterialList.empty())
|
||||
{
|
||||
for (local_list_iter iter = mMaterialList.begin();
|
||||
iter != mMaterialList.end(); iter++)
|
||||
{
|
||||
LLSD element;
|
||||
element["columns"][0]["column"] = "unit_name";
|
||||
element["columns"][0]["type"] = "text";
|
||||
element["columns"][0]["value"] = (*iter)->getShortName();
|
||||
|
||||
element["columns"][1]["column"] = "unit_id_HIDDEN";
|
||||
element["columns"][1]["type"] = "text";
|
||||
element["columns"][1]["value"] = (*iter)->getTrackingID();
|
||||
|
||||
ctrl->addElement(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void LLLocalGLTFMaterialMgr::doUpdates()
|
||||
{
|
||||
// preventing theoretical overlap in cases with huge number of loaded images.
|
||||
mTimer.stopTimer();
|
||||
mNeedsRebake = false;
|
||||
|
||||
for (local_list_iter iter = mMaterialList.begin(); iter != mMaterialList.end(); iter++)
|
||||
{
|
||||
(*iter)->updateSelf();
|
||||
}
|
||||
|
||||
doRebake();
|
||||
mTimer.startTimer();
|
||||
}
|
||||
|
||||
void LLLocalGLTFMaterialMgr::setNeedsRebake()
|
||||
{
|
||||
mNeedsRebake = true;
|
||||
}
|
||||
|
||||
void LLLocalGLTFMaterialMgr::doRebake()
|
||||
{ /* separated that from doUpdates to insure a rebake can be called separately during deletion */
|
||||
if (mNeedsRebake)
|
||||
{
|
||||
gAgentAvatarp->forceBakeAllTextures(LL_LOCAL_SLAM_FOR_DEBUG);
|
||||
mNeedsRebake = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,136 @@
|
|||
/**
|
||||
* @file lllocalrendermaterials.h
|
||||
* @brief Local GLTF materials header
|
||||
*
|
||||
* $LicenseInfo:firstyear=2022&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2022, 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_LOCALRENDERMATERIALS_H
|
||||
#define LL_LOCALRENDERMATERIALS_H
|
||||
|
||||
#include "lleventtimer.h"
|
||||
#include "llpointer.h"
|
||||
|
||||
class LLScrollListCtrl;
|
||||
class LLGLTFMaterial;
|
||||
class LLViewerObject;
|
||||
class LLViewerFetchedTexture;
|
||||
|
||||
class LLLocalGLTFMaterial
|
||||
{
|
||||
public: /* main */
|
||||
LLLocalGLTFMaterial(std::string filename);
|
||||
~LLLocalGLTFMaterial();
|
||||
|
||||
public: /* accessors */
|
||||
std::string getFilename();
|
||||
std::string getShortName();
|
||||
LLUUID getTrackingID();
|
||||
LLUUID getWorldID();
|
||||
bool getValid();
|
||||
|
||||
public: /* self update public section */
|
||||
enum EUpdateType
|
||||
{
|
||||
UT_FIRSTUSE,
|
||||
UT_REGUPDATE
|
||||
};
|
||||
|
||||
bool updateSelf(EUpdateType = UT_REGUPDATE);
|
||||
|
||||
private: /* self update private section */
|
||||
bool loadMaterial(LLPointer<LLGLTFMaterial> raw);
|
||||
void replaceIDs(LLUUID old_id, LLUUID new_id);
|
||||
std::vector<LLViewerObject*> prepUpdateObjects(LLUUID old_id, U32 channel);
|
||||
void updateUserPrims(LLUUID old_id, LLUUID new_id, U32 channel);
|
||||
|
||||
private: /* private enums */
|
||||
enum ELinkStatus
|
||||
{
|
||||
LS_ON,
|
||||
LS_BROKEN,
|
||||
};
|
||||
|
||||
enum EExtension
|
||||
{
|
||||
ET_MATERIAL_GLTF,
|
||||
ET_MATERIAL_GLB,
|
||||
};
|
||||
|
||||
private: /* members */
|
||||
std::string mFilename;
|
||||
std::string mShortName;
|
||||
LLUUID mTrackingID;
|
||||
LLUUID mWorldID;
|
||||
bool mValid;
|
||||
LLSD mLastModified;
|
||||
EExtension mExtension;
|
||||
ELinkStatus mLinkStatus;
|
||||
S32 mUpdateRetries;
|
||||
|
||||
// material needs to maintain textures
|
||||
LLPointer<LLViewerFetchedTexture> mAlbedoFetched;
|
||||
LLPointer<LLViewerFetchedTexture> mNormalFetched;
|
||||
LLPointer<LLViewerFetchedTexture> mMRFetched;
|
||||
LLPointer<LLViewerFetchedTexture> mEmissiveFetched;
|
||||
};
|
||||
|
||||
class LLLocalGLTFMaterialTimer : public LLEventTimer
|
||||
{
|
||||
public:
|
||||
LLLocalGLTFMaterialTimer();
|
||||
~LLLocalGLTFMaterialTimer();
|
||||
|
||||
public:
|
||||
void startTimer();
|
||||
void stopTimer();
|
||||
bool isRunning();
|
||||
BOOL tick();
|
||||
};
|
||||
|
||||
class LLLocalGLTFMaterialMgr : public LLSingleton<LLLocalGLTFMaterialMgr>
|
||||
{
|
||||
LLSINGLETON(LLLocalGLTFMaterialMgr);
|
||||
~LLLocalGLTFMaterialMgr();
|
||||
public:
|
||||
bool addUnit();
|
||||
void delUnit(LLUUID tracking_id);
|
||||
bool checkTextureDimensions(std::string filename);
|
||||
|
||||
LLUUID getWorldID(LLUUID tracking_id);
|
||||
bool isLocal(LLUUID world_id);
|
||||
std::string getFilename(LLUUID tracking_id);
|
||||
|
||||
void feedScrollList(LLScrollListCtrl* ctrl);
|
||||
void doUpdates();
|
||||
void setNeedsRebake();
|
||||
void doRebake();
|
||||
|
||||
private:
|
||||
std::list<LLLocalGLTFMaterial*> mMaterialList;
|
||||
LLLocalGLTFMaterialTimer mTimer;
|
||||
bool mNeedsRebake;
|
||||
typedef std::list<LLLocalGLTFMaterial*>::iterator local_list_iter;
|
||||
};
|
||||
|
||||
#endif // LL_LOCALRENDERMATERIALS_H
|
||||
|
||||
|
|
@ -55,6 +55,7 @@
|
|||
#include "llfloaterperms.h"
|
||||
|
||||
#include "tinygltf/tiny_gltf.h"
|
||||
#include "lltinygltfhelper.h"
|
||||
#include <strstream>
|
||||
|
||||
|
||||
|
|
@ -1046,38 +1047,6 @@ void LLMaterialFilePicker::notify(const std::vector<std::string>& filenames)
|
|||
}
|
||||
}
|
||||
|
||||
const tinygltf::Image* get_image_from_texture_index(const tinygltf::Model& model, S32 texture_index)
|
||||
{
|
||||
if (texture_index >= 0)
|
||||
{
|
||||
S32 source_idx = model.textures[texture_index].source;
|
||||
if (source_idx >= 0)
|
||||
{
|
||||
return &(model.images[source_idx]);
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static LLImageRaw* get_texture(const std::string& folder, const tinygltf::Model& model, S32 texture_index, std::string& name)
|
||||
{
|
||||
const tinygltf::Image* image = get_image_from_texture_index(model, texture_index);
|
||||
LLImageRaw* rawImage = nullptr;
|
||||
|
||||
if (image != nullptr &&
|
||||
image->bits == 8 &&
|
||||
!image->image.empty() &&
|
||||
image->component <= 4)
|
||||
{
|
||||
name = image->name;
|
||||
rawImage = new LLImageRaw(&image->image[0], image->width, image->height, image->component);
|
||||
rawImage->verticalFlip();
|
||||
}
|
||||
|
||||
return rawImage;
|
||||
}
|
||||
|
||||
static void strip_alpha_channel(LLPointer<LLImageRaw>& img)
|
||||
{
|
||||
if (img->getComponents() == 4)
|
||||
|
|
@ -1108,16 +1077,12 @@ static void copy_red_channel(LLPointer<LLImageRaw>& src_img, LLPointer<LLImageRa
|
|||
}
|
||||
}
|
||||
|
||||
static void pack_textures(tinygltf::Model& model, tinygltf::Material& material,
|
||||
static void pack_textures(
|
||||
LLPointer<LLImageRaw>& albedo_img,
|
||||
LLPointer<LLImageRaw>& normal_img,
|
||||
LLPointer<LLImageRaw>& mr_img,
|
||||
LLPointer<LLImageRaw>& emissive_img,
|
||||
LLPointer<LLImageRaw>& occlusion_img,
|
||||
LLPointer<LLViewerFetchedTexture>& albedo_tex,
|
||||
LLPointer<LLViewerFetchedTexture>& normal_tex,
|
||||
LLPointer<LLViewerFetchedTexture>& mr_tex,
|
||||
LLPointer<LLViewerFetchedTexture>& emissive_tex,
|
||||
LLPointer<LLImageJ2C>& albedo_j2c,
|
||||
LLPointer<LLImageJ2C>& normal_j2c,
|
||||
LLPointer<LLImageJ2C>& mr_j2c,
|
||||
|
|
@ -1125,70 +1090,25 @@ static void pack_textures(tinygltf::Model& model, tinygltf::Material& material,
|
|||
{
|
||||
if (albedo_img)
|
||||
{
|
||||
albedo_tex = LLViewerTextureManager::getFetchedTexture(albedo_img, FTType::FTT_LOCAL_FILE, true);
|
||||
albedo_j2c = LLViewerTextureList::convertToUploadFile(albedo_img);
|
||||
}
|
||||
|
||||
if (normal_img)
|
||||
{
|
||||
strip_alpha_channel(normal_img);
|
||||
normal_tex = LLViewerTextureManager::getFetchedTexture(normal_img, FTType::FTT_LOCAL_FILE, true);
|
||||
normal_j2c = LLViewerTextureList::convertToUploadFile(normal_img);
|
||||
}
|
||||
|
||||
if (mr_img)
|
||||
{
|
||||
strip_alpha_channel(mr_img);
|
||||
|
||||
if (occlusion_img && material.pbrMetallicRoughness.metallicRoughnessTexture.index != material.occlusionTexture.index)
|
||||
{
|
||||
// occlusion is a distinct texture from pbrMetallicRoughness
|
||||
// pack into mr red channel
|
||||
int occlusion_idx = material.occlusionTexture.index;
|
||||
int mr_idx = material.pbrMetallicRoughness.metallicRoughnessTexture.index;
|
||||
if (occlusion_idx != mr_idx)
|
||||
{
|
||||
//scale occlusion image to match resolution of mr image
|
||||
occlusion_img->scale(mr_img->getWidth(), mr_img->getHeight());
|
||||
|
||||
copy_red_channel(occlusion_img, mr_img);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (occlusion_img)
|
||||
{
|
||||
//no mr but occlusion exists, make a white mr_img and copy occlusion red channel over
|
||||
mr_img = new LLImageRaw(occlusion_img->getWidth(), occlusion_img->getHeight(), 3);
|
||||
mr_img->clear(255, 255, 255);
|
||||
copy_red_channel(occlusion_img, mr_img);
|
||||
|
||||
}
|
||||
|
||||
if (mr_img)
|
||||
{
|
||||
mr_tex = LLViewerTextureManager::getFetchedTexture(mr_img, FTType::FTT_LOCAL_FILE, true);
|
||||
mr_j2c = LLViewerTextureList::convertToUploadFile(mr_img);
|
||||
}
|
||||
|
||||
if (emissive_img)
|
||||
{
|
||||
strip_alpha_channel(emissive_img);
|
||||
emissive_tex = LLViewerTextureManager::getFetchedTexture(emissive_img, FTType::FTT_LOCAL_FILE, true);
|
||||
emissive_j2c = LLViewerTextureList::convertToUploadFile(emissive_img);
|
||||
}
|
||||
}
|
||||
|
||||
static LLColor4 get_color(const std::vector<double>& in)
|
||||
{
|
||||
LLColor4 out;
|
||||
for (S32 i = 0; i < llmin((S32) in.size(), 4); ++i)
|
||||
{
|
||||
out.mV[i] = in[i];
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void LLMaterialFilePicker::textureLoadedCallback(BOOL success, LLViewerFetchedTexture* src_vi, LLImageRaw* src, LLImageRaw* src_aux, S32 discard_level, BOOL final, void* userdata)
|
||||
{
|
||||
}
|
||||
|
|
@ -1240,23 +1160,24 @@ void LLMaterialFilePicker::loadMaterial(const std::string& filename)
|
|||
model_out.materials.resize(1);
|
||||
|
||||
// get albedo texture
|
||||
LLPointer<LLImageRaw> albedo_img = get_texture(folder, model_in, material_in.pbrMetallicRoughness.baseColorTexture.index, mME->mAlbedoName);
|
||||
LLPointer<LLImageRaw> albedo_img = LLTinyGLTFHelper::getTexture(folder, model_in, material_in.pbrMetallicRoughness.baseColorTexture.index, mME->mAlbedoName);
|
||||
// get normal map
|
||||
LLPointer<LLImageRaw> normal_img = get_texture(folder, model_in, material_in.normalTexture.index, mME->mNormalName);
|
||||
LLPointer<LLImageRaw> normal_img = LLTinyGLTFHelper::getTexture(folder, model_in, material_in.normalTexture.index, mME->mNormalName);
|
||||
// get metallic-roughness texture
|
||||
LLPointer<LLImageRaw> mr_img = get_texture(folder, model_in, material_in.pbrMetallicRoughness.metallicRoughnessTexture.index, mME->mMetallicRoughnessName);
|
||||
LLPointer<LLImageRaw> mr_img = LLTinyGLTFHelper::getTexture(folder, model_in, material_in.pbrMetallicRoughness.metallicRoughnessTexture.index, mME->mMetallicRoughnessName);
|
||||
// get emissive texture
|
||||
LLPointer<LLImageRaw> emissive_img = get_texture(folder, model_in, material_in.emissiveTexture.index, mME->mEmissiveName);
|
||||
LLPointer<LLImageRaw> emissive_img = LLTinyGLTFHelper::getTexture(folder, model_in, material_in.emissiveTexture.index, mME->mEmissiveName);
|
||||
// get occlusion map if needed
|
||||
LLPointer<LLImageRaw> occlusion_img;
|
||||
if (material_in.occlusionTexture.index != material_in.pbrMetallicRoughness.metallicRoughnessTexture.index)
|
||||
{
|
||||
std::string tmp;
|
||||
occlusion_img = get_texture(folder, model_in, material_in.occlusionTexture.index, tmp);
|
||||
occlusion_img = LLTinyGLTFHelper::getTexture(folder, model_in, material_in.occlusionTexture.index, tmp);
|
||||
}
|
||||
|
||||
pack_textures(model_in, material_in, albedo_img, normal_img, mr_img, emissive_img, occlusion_img,
|
||||
mME->mAlbedoFetched, mME->mNormalFetched, mME->mMetallicRoughnessFetched, mME->mEmissiveFetched,
|
||||
LLTinyGLTFHelper::initFetchedTextures(material_in, albedo_img, normal_img, mr_img, emissive_img, occlusion_img,
|
||||
mME->mAlbedoFetched, mME->mNormalFetched, mME->mMetallicRoughnessFetched, mME->mEmissiveFetched);
|
||||
pack_textures(albedo_img, normal_img, mr_img, emissive_img, occlusion_img,
|
||||
mME->mAlbedoJ2C, mME->mNormalJ2C, mME->mMetallicRoughnessJ2C, mME->mEmissiveJ2C);
|
||||
|
||||
LLUUID albedo_id;
|
||||
|
|
@ -1389,8 +1310,8 @@ bool LLMaterialEditor::setFromGltfModel(tinygltf::Model& model, bool set_texture
|
|||
setAlphaMode(material_in.alphaMode);
|
||||
setAlphaCutoff(material_in.alphaCutoff);
|
||||
|
||||
setAlbedoColor(get_color(material_in.pbrMetallicRoughness.baseColorFactor));
|
||||
setEmissiveColor(get_color(material_in.emissiveFactor));
|
||||
setAlbedoColor(LLTinyGLTFHelper::getColor(material_in.pbrMetallicRoughness.baseColorFactor));
|
||||
setEmissiveColor(LLTinyGLTFHelper::getColor(material_in.emissiveFactor));
|
||||
|
||||
setMetalnessFactor(material_in.pbrMetallicRoughness.metallicFactor);
|
||||
setRoughnessFactor(material_in.pbrMetallicRoughness.roughnessFactor);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,245 @@
|
|||
/**
|
||||
* @file lltinygltfhelper.cpp
|
||||
*
|
||||
* $LicenseInfo:firstyear=2022&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2022, 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 "lltinygltfhelper.h"
|
||||
|
||||
#include "llimage.h"
|
||||
#include "llviewertexture.h"
|
||||
#include "llviewertexturelist.h"
|
||||
|
||||
void strip_alpha_channel(LLPointer<LLImageRaw>& img)
|
||||
{
|
||||
if (img->getComponents() == 4)
|
||||
{
|
||||
LLImageRaw* tmp = new LLImageRaw(img->getWidth(), img->getHeight(), 3);
|
||||
tmp->copyUnscaled4onto3(img);
|
||||
img = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
// copy red channel from src_img to dst_img
|
||||
// PRECONDITIONS:
|
||||
// dst_img must be 3 component
|
||||
// src_img and dst_image must have the same dimensions
|
||||
void copy_red_channel(LLPointer<LLImageRaw>& src_img, LLPointer<LLImageRaw>& dst_img)
|
||||
{
|
||||
llassert(src_img->getWidth() == dst_img->getWidth() && src_img->getHeight() == dst_img->getHeight());
|
||||
llassert(dst_img->getComponents() == 3);
|
||||
|
||||
U32 pixel_count = dst_img->getWidth() * dst_img->getHeight();
|
||||
U8* src = src_img->getData();
|
||||
U8* dst = dst_img->getData();
|
||||
S8 src_components = src_img->getComponents();
|
||||
|
||||
for (U32 i = 0; i < pixel_count; ++i)
|
||||
{
|
||||
dst[i * 3] = src[i * src_components];
|
||||
}
|
||||
}
|
||||
|
||||
void LLTinyGLTFHelper::initFetchedTextures(tinygltf::Material& material,
|
||||
LLPointer<LLImageRaw>& albedo_img,
|
||||
LLPointer<LLImageRaw>& normal_img,
|
||||
LLPointer<LLImageRaw>& mr_img,
|
||||
LLPointer<LLImageRaw>& emissive_img,
|
||||
LLPointer<LLImageRaw>& occlusion_img,
|
||||
LLPointer<LLViewerFetchedTexture>& albedo_tex,
|
||||
LLPointer<LLViewerFetchedTexture>& normal_tex,
|
||||
LLPointer<LLViewerFetchedTexture>& mr_tex,
|
||||
LLPointer<LLViewerFetchedTexture>& emissive_tex)
|
||||
{
|
||||
if (albedo_img)
|
||||
{
|
||||
albedo_tex = LLViewerTextureManager::getFetchedTexture(albedo_img, FTType::FTT_LOCAL_FILE, true);
|
||||
}
|
||||
|
||||
if (normal_img)
|
||||
{
|
||||
strip_alpha_channel(normal_img);
|
||||
normal_tex = LLViewerTextureManager::getFetchedTexture(normal_img, FTType::FTT_LOCAL_FILE, true);
|
||||
}
|
||||
|
||||
if (mr_img)
|
||||
{
|
||||
strip_alpha_channel(mr_img);
|
||||
|
||||
if (occlusion_img && material.pbrMetallicRoughness.metallicRoughnessTexture.index != material.occlusionTexture.index)
|
||||
{
|
||||
// occlusion is a distinct texture from pbrMetallicRoughness
|
||||
// pack into mr red channel
|
||||
int occlusion_idx = material.occlusionTexture.index;
|
||||
int mr_idx = material.pbrMetallicRoughness.metallicRoughnessTexture.index;
|
||||
if (occlusion_idx != mr_idx)
|
||||
{
|
||||
//scale occlusion image to match resolution of mr image
|
||||
occlusion_img->scale(mr_img->getWidth(), mr_img->getHeight());
|
||||
|
||||
copy_red_channel(occlusion_img, mr_img);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (occlusion_img)
|
||||
{
|
||||
//no mr but occlusion exists, make a white mr_img and copy occlusion red channel over
|
||||
mr_img = new LLImageRaw(occlusion_img->getWidth(), occlusion_img->getHeight(), 3);
|
||||
mr_img->clear(255, 255, 255);
|
||||
copy_red_channel(occlusion_img, mr_img);
|
||||
}
|
||||
|
||||
if (mr_img)
|
||||
{
|
||||
mr_tex = LLViewerTextureManager::getFetchedTexture(mr_img, FTType::FTT_LOCAL_FILE, true);
|
||||
}
|
||||
|
||||
if (emissive_img)
|
||||
{
|
||||
strip_alpha_channel(emissive_img);
|
||||
emissive_tex = LLViewerTextureManager::getFetchedTexture(emissive_img, FTType::FTT_LOCAL_FILE, true);
|
||||
}
|
||||
}
|
||||
|
||||
void LLTinyGLTFHelper::setFromModel(LLGLTFMaterial* mat, tinygltf::Model& model)
|
||||
{
|
||||
S32 index;
|
||||
|
||||
auto& material_in = model.materials[0];
|
||||
|
||||
// get albedo texture
|
||||
index = material_in.pbrMetallicRoughness.baseColorTexture.index;
|
||||
if (index >= 0)
|
||||
{
|
||||
mat->mAlbedoId.set(model.images[index].uri);
|
||||
}
|
||||
else
|
||||
{
|
||||
mat->mAlbedoId.setNull();
|
||||
}
|
||||
|
||||
// get normal map
|
||||
index = material_in.normalTexture.index;
|
||||
if (index >= 0)
|
||||
{
|
||||
mat->mNormalId.set(model.images[index].uri);
|
||||
}
|
||||
else
|
||||
{
|
||||
mat->mNormalId.setNull();
|
||||
}
|
||||
|
||||
// get metallic-roughness texture
|
||||
index = material_in.pbrMetallicRoughness.metallicRoughnessTexture.index;
|
||||
if (index >= 0)
|
||||
{
|
||||
mat->mMetallicRoughnessId.set(model.images[index].uri);
|
||||
}
|
||||
else
|
||||
{
|
||||
mat->mMetallicRoughnessId.setNull();
|
||||
}
|
||||
|
||||
// get emissive texture
|
||||
index = material_in.emissiveTexture.index;
|
||||
if (index >= 0)
|
||||
{
|
||||
mat->mEmissiveId.set(model.images[index].uri);
|
||||
}
|
||||
else
|
||||
{
|
||||
mat->mEmissiveId.setNull();
|
||||
}
|
||||
|
||||
mat->setAlphaMode(material_in.alphaMode);
|
||||
mat->mAlphaCutoff = llclamp((F32)material_in.alphaCutoff, 0.f, 1.f);
|
||||
|
||||
mat->mAlbedoColor = getColor(material_in.pbrMetallicRoughness.baseColorFactor);
|
||||
mat->mEmissiveColor = getColor(material_in.emissiveFactor);
|
||||
|
||||
mat->mMetallicFactor = llclamp((F32)material_in.pbrMetallicRoughness.metallicFactor, 0.f, 1.f);
|
||||
mat->mRoughnessFactor = llclamp((F32)material_in.pbrMetallicRoughness.roughnessFactor, 0.f, 1.f);
|
||||
|
||||
mat->mDoubleSided = material_in.doubleSided;
|
||||
}
|
||||
|
||||
LLColor4 LLTinyGLTFHelper::getColor(const std::vector<double>& in)
|
||||
{
|
||||
LLColor4 out;
|
||||
for (S32 i = 0; i < llmin((S32)in.size(), 4); ++i)
|
||||
{
|
||||
out.mV[i] = in[i];
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
const tinygltf::Image * LLTinyGLTFHelper::getImageFromTextureIndex(const tinygltf::Model & model, S32 texture_index)
|
||||
{
|
||||
if (texture_index >= 0)
|
||||
{
|
||||
S32 source_idx = model.textures[texture_index].source;
|
||||
if (source_idx >= 0)
|
||||
{
|
||||
return &(model.images[source_idx]);
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
LLImageRaw * LLTinyGLTFHelper::getTexture(const std::string & folder, const tinygltf::Model & model, S32 texture_index, std::string & name)
|
||||
{
|
||||
const tinygltf::Image* image = getImageFromTextureIndex(model, texture_index);
|
||||
LLImageRaw* rawImage = nullptr;
|
||||
|
||||
if (image != nullptr &&
|
||||
image->bits == 8 &&
|
||||
!image->image.empty() &&
|
||||
image->component <= 4)
|
||||
{
|
||||
name = image->name;
|
||||
rawImage = new LLImageRaw(&image->image[0], image->width, image->height, image->component);
|
||||
rawImage->verticalFlip();
|
||||
}
|
||||
|
||||
return rawImage;
|
||||
}
|
||||
|
||||
LLImageRaw * LLTinyGLTFHelper::getTexture(const std::string & folder, const tinygltf::Model & model, S32 texture_index)
|
||||
{
|
||||
const tinygltf::Image* image = getImageFromTextureIndex(model, texture_index);
|
||||
LLImageRaw* rawImage = nullptr;
|
||||
|
||||
if (image != nullptr &&
|
||||
image->bits == 8 &&
|
||||
!image->image.empty() &&
|
||||
image->component <= 4)
|
||||
{
|
||||
rawImage = new LLImageRaw(&image->image[0], image->width, image->height, image->component);
|
||||
rawImage->verticalFlip();
|
||||
}
|
||||
|
||||
return rawImage;
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
/**
|
||||
* @file lltinygltfhelper.h
|
||||
*
|
||||
* $LicenseInfo:firstyear=2022&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2022, 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$
|
||||
*/
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "llgltfmaterial.h"
|
||||
#include "llpointer.h"
|
||||
#include "tinygltf/tiny_gltf.h"
|
||||
|
||||
class LLImageRaw;
|
||||
class LLViewerFetchedTexture;
|
||||
|
||||
namespace LLTinyGLTFHelper
|
||||
{
|
||||
void setFromModel(LLGLTFMaterial* mat, tinygltf::Model& model);
|
||||
LLColor4 getColor(const std::vector<double>& in);
|
||||
const tinygltf::Image* getImageFromTextureIndex(const tinygltf::Model& model, S32 texture_index);
|
||||
LLImageRaw* getTexture(const std::string& folder, const tinygltf::Model& model, S32 texture_index, std::string& name);
|
||||
LLImageRaw* getTexture(const std::string& folder, const tinygltf::Model& model, S32 texture_index);
|
||||
|
||||
void initFetchedTextures(tinygltf::Material& material,
|
||||
LLPointer<LLImageRaw>& albedo_img,
|
||||
LLPointer<LLImageRaw>& normal_img,
|
||||
LLPointer<LLImageRaw>& mr_img,
|
||||
LLPointer<LLImageRaw>& emissive_img,
|
||||
LLPointer<LLImageRaw>& occlusion_img,
|
||||
LLPointer<LLViewerFetchedTexture>& albedo_tex,
|
||||
LLPointer<LLViewerFetchedTexture>& normal_tex,
|
||||
LLPointer<LLViewerFetchedTexture>& mr_tex,
|
||||
LLPointer<LLViewerFetchedTexture>& emissive_tex);
|
||||
}
|
||||
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
/**
|
||||
* @file llviewertexturelinumimagest.h
|
||||
* @file llviewertexturelist.h
|
||||
* @brief Object for managing the list of images within a region
|
||||
*
|
||||
* $LicenseInfo:firstyear=2000&license=viewerlgpl$
|
||||
* $LicenseInfo:firstyear=2022&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2010, Linden Research, Inc.
|
||||
* Copyright (C) 2022, 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
|
||||
|
|
|
|||
Loading…
Reference in New Issue