SL-17653 Basic local gltf materials

meow-7.2.2
Andrey Kleshchev 2022-08-19 01:45:47 +03:00
parent 15cd6c1cb7
commit 4bb419031c
9 changed files with 1204 additions and 169 deletions

View File

@ -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

View File

@ -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);
}

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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

View File

@ -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);

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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