From f5d66e79eec07384ef6f4fd3f516f7a9d010fb9e Mon Sep 17 00:00:00 2001
From: Dave Parks <davep@lindenlab.com>
Date: Wed, 15 Jun 2022 17:03:38 -0500
Subject: [PATCH] SL-17605 WIP - Upload->Material now lets you pick a GLTF file
 and imports the first material in the GLTF file to the Material Editor

---
 indra/newview/llfilepicker.cpp                |   5 +
 indra/newview/llfilepicker.h                  |   3 +-
 indra/newview/llmaterialeditor.cpp            | 241 ++++++++++++++++++
 indra/newview/llmaterialeditor.h              |  23 ++
 indra/newview/llviewermenufile.cpp            |  34 ++-
 .../skins/default/xui/en/menu_viewer.xml      |  10 +
 6 files changed, 314 insertions(+), 2 deletions(-)

diff --git a/indra/newview/llfilepicker.cpp b/indra/newview/llfilepicker.cpp
index 4e2cc342077..2809988ba05 100644
--- a/indra/newview/llfilepicker.cpp
+++ b/indra/newview/llfilepicker.cpp
@@ -60,6 +60,7 @@ LLFilePicker LLFilePicker::sInstance;
 #define SLOBJECT_FILTER L"Objects (*.slobject)\0*.slobject\0"
 #define RAW_FILTER L"RAW files (*.raw)\0*.raw\0"
 #define MODEL_FILTER L"Model files (*.dae; *.gltf; *.glb)\0*.dae;*.gltf;*.glb\0"
+#define MATERIAL_FILTER L"GLTF Files (*.gltf; *.glb)\0*.gltf;*.glb\0"
 #define SCRIPT_FILTER L"Script files (*.lsl)\0*.lsl\0"
 #define DICTIONARY_FILTER L"Dictionary files (*.dic; *.xcu)\0*.dic;*.xcu\0"
 #endif
@@ -215,6 +216,10 @@ BOOL LLFilePicker::setupFilter(ELoadFilter filter)
 		mOFN.lpstrFilter = MODEL_FILTER \
 			L"\0";
 		break;
+    case FFLOAD_MATERIAL:
+        mOFN.lpstrFilter = MATERIAL_FILTER \
+            L"\0";
+        break;
 	case FFLOAD_SCRIPT:
 		mOFN.lpstrFilter = SCRIPT_FILTER \
 			L"\0";
diff --git a/indra/newview/llfilepicker.h b/indra/newview/llfilepicker.h
index a314207da64..cb57c8437dc 100644
--- a/indra/newview/llfilepicker.h
+++ b/indra/newview/llfilepicker.h
@@ -86,7 +86,8 @@ class LLFilePicker
 		FFLOAD_SCRIPT = 11,
 		FFLOAD_DICTIONARY = 12,
         FFLOAD_DIRECTORY = 13,   // To call from lldirpicker.
-        FFLOAD_EXE = 14          // Note: EXE will be treated as ALL on Windows and Linux but not on Darwin
+        FFLOAD_EXE = 14,          // Note: EXE will be treated as ALL on Windows and Linux but not on Darwin
+        FFLOAD_MATERIAL = 15
 	};
 
 	enum ESaveFilter
diff --git a/indra/newview/llmaterialeditor.cpp b/indra/newview/llmaterialeditor.cpp
index 6e7f7adfed5..f23564c51b9 100644
--- a/indra/newview/llmaterialeditor.cpp
+++ b/indra/newview/llmaterialeditor.cpp
@@ -28,6 +28,9 @@
 
 #include "llmaterialeditor.h"
 #include "llcombobox.h"
+#include "llviewermenufile.h"
+#include "llappviewer.h"
+#include "llviewertexture.h"
 
 #include "tinygltf/tiny_gltf.h"
 
@@ -52,6 +55,11 @@ LLUUID LLMaterialEditor::getAlbedoId()
     return childGetValue("albedo texture").asUUID();
 }
 
+void LLMaterialEditor::setAlbedoId(const LLUUID& id)
+{
+    childSetValue("albedo texture", id);
+}
+
 LLColor4 LLMaterialEditor::getAlbedoColor()
 {
     LLColor4 ret = LLColor4(childGetValue("albedo color"));
@@ -60,6 +68,12 @@ LLColor4 LLMaterialEditor::getAlbedoColor()
 }
 
 
+void LLMaterialEditor::setAlbedoColor(const LLColor4& color)
+{
+    childSetValue("albedo color", color.getValue());
+    childSetValue("transparency", color.mV[3]);
+}
+
 F32 LLMaterialEditor::getTransparency()
 {
     return childGetValue("transparency").asReal();
@@ -70,46 +84,91 @@ std::string LLMaterialEditor::getAlphaMode()
     return childGetValue("alpha mode").asString();
 }
 
+void LLMaterialEditor::setAlphaMode(const std::string& alpha_mode)
+{
+    childSetValue("alpha mode", alpha_mode);
+}
+
 F32 LLMaterialEditor::getAlphaCutoff()
 {
     return childGetValue("alpha cutoff").asReal();
 }
 
+void LLMaterialEditor::setAlphaCutoff(F32 alpha_cutoff)
+{
+    childSetValue("alpha cutoff", alpha_cutoff);
+}
+
 LLUUID LLMaterialEditor::getMetallicRoughnessId()
 {
     return childGetValue("metallic-roughness texture").asUUID();
 }
 
+void LLMaterialEditor::setMetallicRoughnessId(const LLUUID& id)
+{
+    childSetValue("metallic-roughness texture", id);
+}
+
 F32 LLMaterialEditor::getMetalnessFactor()
 {
     return childGetValue("metalness factor").asReal();
 }
 
+void LLMaterialEditor::setMetalnessFactor(F32 factor)
+{
+    childSetValue("metalness factor", factor);
+}
+
 F32 LLMaterialEditor::getRoughnessFactor()
 {
     return childGetValue("roughness factor").asReal();
 }
 
+void LLMaterialEditor::setRoughnessFactor(F32 factor)
+{
+    childSetValue("roughness factor", factor);
+}
+
 LLUUID LLMaterialEditor::getEmissiveId()
 {
     return childGetValue("emissive texture").asUUID();
 }
 
+void LLMaterialEditor::setEmissiveId(const LLUUID& id)
+{
+    childSetValue("emissive texture", id);
+}
+
 LLColor4 LLMaterialEditor::getEmissiveColor()
 {
     return LLColor4(childGetValue("emissive color"));
 }
 
+void LLMaterialEditor::setEmissiveColor(const LLColor4& color)
+{
+    childSetValue("emissive color", color.getValue());
+}
+
 LLUUID LLMaterialEditor::getNormalId()
 {
     return childGetValue("normal texture").asUUID();
 }
 
+void LLMaterialEditor::setNormalId(const LLUUID& id)
+{
+    childSetValue("normal texture", id);
+}
+
 bool LLMaterialEditor::getDoubleSided()
 {
     return childGetValue("double sided").asBoolean();
 }
 
+void LLMaterialEditor::setDoubleSided(bool double_sided)
+{
+    childSetValue("double sided", double_sided);
+}
+
 
 static void write_color(const LLColor4& color, std::vector<double>& c)
 {
@@ -205,3 +264,185 @@ void LLMaterialEditor::onClickSave()
     LL_INFOS() << dump << LL_ENDL;
 }
 
+class LLMaterialFilePicker : public LLFilePickerThread
+{
+public:
+    LLMaterialFilePicker(LLMaterialEditor* me);
+    virtual void notify(const std::vector<std::string>& filenames);
+    void loadMaterial(const std::string& filename);
+    static void	textureLoadedCallback(BOOL success, LLViewerFetchedTexture* src_vi, LLImageRaw* src, LLImageRaw* src_aux, S32 discard_level, BOOL final, void* userdata);
+private:
+    LLMaterialEditor* mME;
+};
+
+LLMaterialFilePicker::LLMaterialFilePicker(LLMaterialEditor* me)
+    : LLFilePickerThread(LLFilePicker::FFLOAD_MODEL)
+{
+    mME = me;
+}
+
+void LLMaterialFilePicker::notify(const std::vector<std::string>& filenames)
+{
+    if (LLAppViewer::instance()->quitRequested())
+    {
+        return;
+    }
+
+    
+    if (filenames.size() > 0)
+    {
+        loadMaterial(filenames[0]);
+    }
+}
+
+static std::string get_texture_uri(const tinygltf::Model& model, S32 texture_index)
+{
+    std::string ret;
+
+    if (texture_index >= 0)
+    {
+        S32 source_idx = model.textures[texture_index].source;
+        if (source_idx >= 0)
+        {
+            ret = model.images[source_idx].uri;
+        }
+    }
+
+    return ret;
+}
+
+static LLViewerFetchedTexture* get_texture(const std::string& folder, const tinygltf::Model& model, S32 texture_index)
+{
+    LLViewerFetchedTexture* ret = nullptr;
+    std::string file = get_texture_uri(model, texture_index);
+    if (!file.empty())
+    {
+        std::string uri = folder;
+        gDirUtilp->append(uri, file);
+
+        ret = LLViewerTextureManager::getFetchedTextureFromUrl("file://" + LLURI::unescape(uri), FTT_LOCAL_FILE, TRUE, LLGLTexture::BOOST_PREVIEW);
+        //ret->setLoadedCallback(LLMaterialFilePicker::textureLoadedCallback, 0, TRUE, FALSE, opaque, NULL, FALSE);
+        ret->forceToSaveRawImage(0, F32_MAX);
+    }
+    return ret;
+}
+
+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)
+{
+}
+
+
+void LLMaterialFilePicker::loadMaterial(const std::string& filename)
+{
+    tinygltf::TinyGLTF loader;
+    std::string        error_msg;
+    std::string        warn_msg;
+
+    bool loaded = false;
+    tinygltf::Model model_in;
+
+    std::string filename_lc = filename;
+    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
+        loaded = loader.LoadBinaryFromFile(&model_in, &error_msg, &warn_msg, filename);
+    }
+    else
+    {  // file is ascii
+        loaded = loader.LoadASCIIFromFile(&model_in, &error_msg, &warn_msg, filename);
+    }
+
+    if (!loaded)
+    {
+        // TODO: show error_msg to user
+        return;
+    }
+
+    if (model_in.materials.empty())
+    {
+        // TODO: show error message that materials are missing
+        return;
+    }
+
+    std::string folder = gDirUtilp->getDirName(filename);
+
+
+    tinygltf::Material material_in = model_in.materials[0];
+
+    tinygltf::Model  model_out;
+    model_out.asset.version = "2.0";
+    model_out.materials.resize(1);
+
+    // get albedo texture
+    LLPointer<LLViewerFetchedTexture> albedo_tex = get_texture(folder, model_in, material_in.pbrMetallicRoughness.baseColorTexture.index);
+
+    LLUUID albedo_id;
+    if (albedo_tex != nullptr)
+    {
+        albedo_id = albedo_tex->getID();
+    }
+
+    // get metallic-roughness texture
+    LLPointer<LLViewerFetchedTexture> mr_tex = get_texture(folder, model_in, material_in.pbrMetallicRoughness.metallicRoughnessTexture.index);
+
+    LLUUID mr_id;
+    if (mr_tex != nullptr)
+    {
+        mr_id = mr_tex->getID();
+    }
+
+    // get emissive texture
+    LLPointer<LLViewerFetchedTexture> emissive_tex = get_texture(folder, model_in, material_in.emissiveTexture.index);
+
+    LLUUID emissive_id;
+    if (emissive_tex != nullptr)
+    {
+        emissive_id = emissive_tex->getID();
+    }
+
+    // get normal map
+    LLPointer<LLViewerFetchedTexture> normal_tex = get_texture(folder, model_in, material_in.normalTexture.index);
+
+    LLUUID normal_id;
+    if (normal_tex != nullptr)
+    {
+        normal_id = normal_tex->getID();
+    }
+
+    mME->setAlbedoId(albedo_id);
+    mME->setMetallicRoughnessId(mr_id);
+    mME->setEmissiveId(emissive_id);
+    mME->setNormalId(normal_id);
+
+    mME->setAlphaMode(material_in.alphaMode);
+    mME->setAlphaCutoff(material_in.alphaCutoff);
+    
+    mME->setAlbedoColor(get_color(material_in.pbrMetallicRoughness.baseColorFactor));
+    mME->setEmissiveColor(get_color(material_in.emissiveFactor));
+    
+    mME->setMetalnessFactor(material_in.pbrMetallicRoughness.metallicFactor);
+    mME->setRoughnessFactor(material_in.pbrMetallicRoughness.roughnessFactor);
+    
+    mME->setDoubleSided(material_in.doubleSided);
+
+    mME->openFloater();
+}
+
+void LLMaterialEditor::importMaterial()
+{
+    (new LLMaterialFilePicker(this))->getFile();
+}
\ No newline at end of file
diff --git a/indra/newview/llmaterialeditor.h b/indra/newview/llmaterialeditor.h
index febdb3bbc1d..f0c5dca44d0 100644
--- a/indra/newview/llmaterialeditor.h
+++ b/indra/newview/llmaterialeditor.h
@@ -33,26 +33,49 @@ class LLMaterialEditor : public LLFloater
 public:
 	LLMaterialEditor(const LLSD& key);
 
+    // open a file dialog and select a gltf/glb file for import
+    void importMaterial();
+
     void onClickSave();
 
 	// llpanel
 	BOOL postBuild() override;
 
     LLUUID getAlbedoId();
+    void setAlbedoId(const LLUUID& id);
+
     LLColor4 getAlbedoColor();
+
+    // sets both albedo color and transparency
+    void    setAlbedoColor(const LLColor4& color);
+
     F32     getTransparency();
+    
     std::string getAlphaMode();
+    void setAlphaMode(const std::string& alpha_mode);
+
     F32 getAlphaCutoff();
+    void setAlphaCutoff(F32 alpha_cutoff);
 
     LLUUID getMetallicRoughnessId();
+    void setMetallicRoughnessId(const LLUUID& id);
+
     F32 getMetalnessFactor();
+    void setMetalnessFactor(F32 factor);
+
     F32 getRoughnessFactor();
+    void setRoughnessFactor(F32 factor);
 
     LLUUID getEmissiveId();
+    void setEmissiveId(const LLUUID& id);
+
     LLColor4 getEmissiveColor();
+    void setEmissiveColor(const LLColor4& color);
 
     LLUUID getNormalId();
+    void setNormalId(const LLUUID& id);
 
     bool getDoubleSided();
+    void setDoubleSided(bool double_sided);
 };
 
diff --git a/indra/newview/llviewermenufile.cpp b/indra/newview/llviewermenufile.cpp
index 32fdfe282d9..975e3b97ec2 100644
--- a/indra/newview/llviewermenufile.cpp
+++ b/indra/newview/llviewermenufile.cpp
@@ -37,6 +37,7 @@
 #include "llbuycurrencyhtml.h"
 #include "llfloatermap.h"
 #include "llfloatermodelpreview.h"
+#include "llmaterialeditor.h"
 #include "llfloatersnapshot.h"
 #include "llfloateroutfitsnapshot.h"
 #include "llimage.h"
@@ -101,6 +102,20 @@ class LLFileEnableUploadModel : public view_listener_t
 	}
 };
 
+class LLFileEnableUploadMaterial : public view_listener_t
+{
+    bool handleEvent(const LLSD& userdata)
+    {
+        LLMaterialEditor* me = (LLMaterialEditor*)LLFloaterReg::findInstance("material_editor");
+        if (me && me->isShown())
+        {
+            return false;
+        }
+
+        return true;
+    }
+};
+
 class LLMeshEnabled : public view_listener_t
 {
 	bool handleEvent(const LLSD& userdata)
@@ -282,6 +297,7 @@ static std::string SLOBJECT_EXTENSIONS = "slobject";
 #endif
 static std::string ALL_FILE_EXTENSIONS = "*.*";
 static std::string MODEL_EXTENSIONS = "dae";
+static std::string MATERIAL_EXTENSIONS = "gltf glb";
 
 std::string build_extensions_string(LLFilePicker::ELoadFilter filter)
 {
@@ -298,6 +314,8 @@ std::string build_extensions_string(LLFilePicker::ELoadFilter filter)
 		return SLOBJECT_EXTENSIONS;
 	case LLFilePicker::FFLOAD_MODEL:
 		return MODEL_EXTENSIONS;
+    case LLFilePicker::FFLOAD_MATERIAL:
+        return MATERIAL_EXTENSIONS;
 	case LLFilePicker::FFLOAD_XML:
 	    return XML_EXTENSIONS;
     case LLFilePicker::FFLOAD_ALL:
@@ -566,7 +584,20 @@ class LLFileUploadModel : public view_listener_t
         return TRUE;
 	}
 };
-	
+
+class LLFileUploadMaterial : public view_listener_t
+{
+    bool handleEvent(const LLSD& userdata)
+    {
+        LLMaterialEditor* me = (LLMaterialEditor*)LLFloaterReg::getInstance("material_editor");
+        if (me)
+        {
+            me->importMaterial();
+        }
+        return TRUE;
+    }
+};
+
 class LLFileUploadSound : public view_listener_t
 {
 	bool handleEvent(const LLSD& userdata)
@@ -1097,6 +1128,7 @@ void init_menu_file()
 	view_listener_t::addCommit(new LLFileUploadSound(), "File.UploadSound");
 	view_listener_t::addCommit(new LLFileUploadAnim(), "File.UploadAnim");
 	view_listener_t::addCommit(new LLFileUploadModel(), "File.UploadModel");
+    view_listener_t::addCommit(new LLFileUploadMaterial(), "File.UploadMaterial");
 	view_listener_t::addCommit(new LLFileUploadBulk(), "File.UploadBulk");
 	view_listener_t::addCommit(new LLFileCloseWindow(), "File.CloseWindow");
 	view_listener_t::addCommit(new LLFileCloseAllWindows(), "File.CloseAllWindows");
diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml
index 623d0d88eb5..4b3c61d7f08 100644
--- a/indra/newview/skins/default/xui/en/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/en/menu_viewer.xml
@@ -1583,6 +1583,16 @@ function="World.EnvPreset"
             <menu_item_call.on_visible
             function="File.VisibleUploadModel"/>
             </menu_item_call>
+          <menu_item_call
+           label="Material..."
+           layout="topleft"
+           name="Upload Material">
+            <menu_item_call.on_click
+             function="File.UploadMaterial"
+             parameter="" />
+            <menu_item_call.on_enable
+             function="File.EnableUploadMaterial" />
+          </menu_item_call>
 	   <menu_item_call
              label="Bulk..."
              layout="topleft"
-- 
GitLab