summaryrefslogtreecommitdiff
path: root/indra/newview
diff options
context:
space:
mode:
authorRunitaiLinden <davep@lindenlab.com>2024-04-30 21:57:42 -0500
committerRunitaiLinden <davep@lindenlab.com>2024-04-30 21:57:42 -0500
commit170765fd3505410dced83b342f87030fd9151e35 (patch)
treef3665586f2d731a04bf645bc1155bbddc6c5f7e8 /indra/newview
parent5e2bac01cb6e8d3de3cc0e496d94a729e4740247 (diff)
#1357 Proof of concept on decomposing a GLTF scene into its component parts
Diffstat (limited to 'indra/newview')
-rw-r--r--indra/newview/gltf/accessor.cpp18
-rw-r--r--indra/newview/gltf/accessor.h4
-rw-r--r--indra/newview/gltf/asset.cpp102
-rw-r--r--indra/newview/gltf/asset.h11
-rw-r--r--indra/newview/gltfscenemanager.cpp45
-rw-r--r--indra/newview/gltfscenemanager.h4
-rw-r--r--indra/newview/llviewermenu.cpp11
-rw-r--r--indra/newview/skins/default/xui/en/menu_viewer.xml6
8 files changed, 199 insertions, 2 deletions
diff --git a/indra/newview/gltf/accessor.cpp b/indra/newview/gltf/accessor.cpp
index 55d36b7a32..9bfdc2afa6 100644
--- a/indra/newview/gltf/accessor.cpp
+++ b/indra/newview/gltf/accessor.cpp
@@ -30,6 +30,24 @@
using namespace LL::GLTF;
+void Buffer::erase(Asset& asset, S32 offset, S32 length)
+{
+ S32 idx = this - &asset.mBuffers[0];
+
+ mData.erase(mData.begin() + offset, mData.begin() + offset + length);
+
+ for (BufferView& view : asset.mBufferViews)
+ {
+ if (view.mBuffer == idx)
+ {
+ if (view.mByteOffset >= offset)
+ {
+ view.mByteOffset -= length;
+ }
+ }
+ }
+}
+
const Buffer& Buffer::operator=(const tinygltf::Buffer& src)
{
mData = src.data;
diff --git a/indra/newview/gltf/accessor.h b/indra/newview/gltf/accessor.h
index 9b8265d8da..6849cd8609 100644
--- a/indra/newview/gltf/accessor.h
+++ b/indra/newview/gltf/accessor.h
@@ -45,6 +45,10 @@ namespace LL
std::string mName;
std::string mUri;
+ // erase the given range from this buffer.
+ // also updates all buffer views in given asset that reference this buffer
+ void erase(Asset& asset, S32 offset, S32 length);
+
const Buffer& operator=(const tinygltf::Buffer& src);
};
diff --git a/indra/newview/gltf/asset.cpp b/indra/newview/gltf/asset.cpp
index 233faac545..475cbcb6e5 100644
--- a/indra/newview/gltf/asset.cpp
+++ b/indra/newview/gltf/asset.cpp
@@ -30,9 +30,11 @@
#include "llvolumeoctree.h"
#include "../llviewershadermgr.h"
#include "../llviewercontrol.h"
+#include "../llviewertexturelist.h"
using namespace LL::GLTF;
+#pragma optimize("", off)
namespace LL
{
@@ -821,6 +823,106 @@ void Asset::save(tinygltf::Model& dst)
LL::GLTF::copy(*this, dst);
}
+void Asset::decompose(const std::string& filename)
+{
+ // get folder path
+ std::string folder = gDirUtilp->getDirName(filename);
+
+ // decompose images
+ for (auto& image : mImages)
+ {
+ image.decompose(*this, folder);
+ }
+}
+
+void Asset::eraseBufferView(S32 bufferView)
+{
+ mBufferViews.erase(mBufferViews.begin() + bufferView);
+
+ for (auto& accessor : mAccessors)
+ {
+ if (accessor.mBufferView > bufferView)
+ {
+ accessor.mBufferView--;
+ }
+ }
+
+ for (auto& image : mImages)
+ {
+ if (image.mBufferView > bufferView)
+ {
+ image.mBufferView--;
+ }
+ }
+
+}
+
+void Image::decompose(Asset& asset, const std::string& folder)
+{
+ std::string name = mName;
+ if (name.empty())
+ {
+ S32 idx = this - asset.mImages.data();
+ name = llformat("image_%d", idx);
+ }
+
+ if (mBufferView != INVALID_INDEX)
+ {
+ // save original image
+ BufferView& bufferView = asset.mBufferViews[mBufferView];
+ Buffer& buffer = asset.mBuffers[bufferView.mBuffer];
+
+ std::string extension;
+
+ if (mMimeType == "image/jpeg")
+ {
+ extension = ".jpg";
+ }
+ else if (mMimeType == "image/png")
+ {
+ extension = ".png";
+ }
+ else
+ {
+ extension = ".bin";
+ }
+
+ std::string filename = folder + "/" + name + "." + extension;
+
+ // set URI to non-j2c file for now, but later we'll want to reference the j2c hash
+ mUri = name + "." + extension;
+
+ std::ofstream file(filename, std::ios::binary);
+ file.write((const char*)buffer.mData.data() + bufferView.mByteOffset, bufferView.mByteLength);
+
+ buffer.erase(asset, bufferView.mByteOffset, bufferView.mByteLength);
+
+ asset.eraseBufferView(mBufferView);
+ }
+
+ if (!mData.empty())
+ {
+ // save j2c image
+ std::string filename = folder + "/" + name + ".j2c";
+
+ LLPointer<LLImageRaw> raw = new LLImageRaw(mWidth, mHeight, mComponent);
+ U8* data = raw->allocateData();
+ llassert(mData.size() == raw->getDataSize());
+ memcpy(data, mData.data(), mData.size());
+
+ LLViewerTextureList::createUploadFile(raw, filename, 4096);
+
+ mData.clear();
+ }
+
+ mWidth = -1;
+ mHeight = -1;
+ mComponent = -1;
+ mBits = -1;
+ mPixelType = -1;
+ mMimeType = "";
+
+}
const Material::TextureInfo& Material::TextureInfo::operator=(const tinygltf::TextureInfo& src)
{
diff --git a/indra/newview/gltf/asset.h b/indra/newview/gltf/asset.h
index b8300c2d8a..cb28c4a572 100644
--- a/indra/newview/gltf/asset.h
+++ b/indra/newview/gltf/asset.h
@@ -254,6 +254,9 @@ namespace LL
return *this;
}
+ // save image clear local data, and set uri
+ void decompose(Asset& asset, const std::string& filename);
+
void allocateGLResources()
{
// allocate texture
@@ -322,7 +325,13 @@ namespace LL
// save the asset to a tinygltf model
void save(tinygltf::Model& dst);
-
+
+ // decompose the asset to the given .gltf file
+ void decompose(const std::string& filename);
+
+ // remove the bufferview at the given index
+ // updates all bufferview indices in this Asset as needed
+ void eraseBufferView(S32 bufferView);
};
}
}
diff --git a/indra/newview/gltfscenemanager.cpp b/indra/newview/gltfscenemanager.cpp
index f9f1240469..0aedcd653d 100644
--- a/indra/newview/gltfscenemanager.cpp
+++ b/indra/newview/gltfscenemanager.cpp
@@ -100,6 +100,51 @@ void GLTFSceneManager::saveAs()
}
}
+void GLTFSceneManager::decomposeSelection()
+{
+ LLViewerObject* obj = LLSelectMgr::instance().getSelection()->getFirstRootObject();
+ if (obj && obj->mGLTFAsset.notNull())
+ {
+ LLFilePickerReplyThread::startPicker(
+ [](const std::vector<std::string>& filenames, LLFilePicker::ELoadFilter load_filter, LLFilePicker::ESaveFilter save_filter)
+ {
+ if (LLAppViewer::instance()->quitRequested())
+ {
+ return;
+ }
+ if (filenames.size() > 0)
+ {
+ GLTFSceneManager::instance().decomposeSelection(filenames[0]);
+ }
+ },
+ LLFilePicker::FFSAVE_GLTF,
+ "scene.gltf");
+ }
+ else
+ {
+ LLNotificationsUtil::add("GLTFSaveSelection");
+ }
+}
+
+void GLTFSceneManager::decomposeSelection(const std::string& filename)
+{
+ LLViewerObject* obj = LLSelectMgr::instance().getSelection()->getFirstRootObject();
+ if (obj && obj->mGLTFAsset.notNull())
+ {
+ // copy asset out for decomposition
+ Asset asset = *obj->mGLTFAsset;
+
+ // decompose the asset into component parts
+ asset.decompose(filename);
+
+ // copy decomposed asset into tinygltf for serialization
+ tinygltf::Model model;
+ asset.save(model);
+
+ LLTinyGLTFHelper::saveModel(filename, model);
+ }
+}
+
void GLTFSceneManager::save(const std::string& filename)
{
LLViewerObject* obj = LLSelectMgr::instance().getSelection()->getFirstRootObject();
diff --git a/indra/newview/gltfscenemanager.h b/indra/newview/gltfscenemanager.h
index ec50a0952f..57d9e019a5 100644
--- a/indra/newview/gltfscenemanager.h
+++ b/indra/newview/gltfscenemanager.h
@@ -41,7 +41,9 @@ namespace LL
void load(const std::string& filename); // load asset from filename
void saveAs(); // open filepicker and choose file to save selected asset to
- void save(const std::string& filename); // save selected asset to filename
+ void save(const std::string& filename); // save selected asset to filename (suitable for use in external programs)
+ void decomposeSelection(); // open file picker and choose a location to decompose to
+ void decomposeSelection(const std::string& filename); // decompose selected asset into simulator-ready .gltf, .bin, and .j2c files
void update();
void render(bool opaque, bool rigged = false);
diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index fcda8fa767..631e1b57d9 100644
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -8000,6 +8000,16 @@ class LLAdvancedClickGLTFSaveAs : public view_listener_t
}
};
+class LLAdvancedClickGLTFDecompose : public view_listener_t
+{
+ bool handleEvent(const LLSD& userdata)
+ {
+ // open personal lighting floater when previewing an HDRI (keeps HDRI from implicitly unloading when opening build tools)
+ LL::GLTFSceneManager::instance().decomposeSelection();
+ return true;
+ }
+};
+
// these are used in the gl menus to set control values that require shader recompilation
class LLToggleShaderControl : public view_listener_t
{
@@ -9649,6 +9659,7 @@ void initialize_menus()
view_listener_t::addMenu(new LLAdvancedClickHDRIPreview(), "Advanced.ClickHDRIPreview");
view_listener_t::addMenu(new LLAdvancedClickGLTFOpen(), "Advanced.ClickGLTFOpen");
view_listener_t::addMenu(new LLAdvancedClickGLTFSaveAs(), "Advanced.ClickGLTFSaveAs");
+ view_listener_t::addMenu(new LLAdvancedClickGLTFDecompose(), "Advanced.ClickGLTFDecompose");
view_listener_t::addMenu(new LLAdvancedPurgeShaderCache(), "Advanced.ClearShaderCache");
view_listener_t::addMenu(new LLAdvancedRebuildTerrain(), "Advanced.RebuildTerrain");
diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml
index 9544a926f3..6238efe688 100644
--- a/indra/newview/skins/default/xui/en/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/en/menu_viewer.xml
@@ -2861,6 +2861,12 @@ function="World.EnvPreset"
<menu_item_call.on_click
function="Advanced.ClickGLTFSaveAs" />
</menu_item_call>
+ <menu_item_call
+ label="Decompose..."
+ name="Decompose...">
+ <menu_item_call.on_click
+ function="Advanced.ClickGLTFDecompose" />
+ </menu_item_call>
</menu>
<menu
create_jump_keys="true"