summaryrefslogtreecommitdiff
path: root/indra
diff options
context:
space:
mode:
Diffstat (limited to 'indra')
-rw-r--r--indra/llcommon/llcommon.cpp4
-rw-r--r--indra/llcommon/llcoros.cpp17
-rw-r--r--indra/llcommon/llcoros.h10
-rw-r--r--indra/llcommon/llerror.cpp12
-rw-r--r--indra/llcommon/llevents.cpp10
-rw-r--r--indra/llcommon/llevents.h3
-rw-r--r--indra/llcommon/llfixedbuffer.h3
-rw-r--r--indra/llcommon/llmutex.cpp18
-rw-r--r--indra/llcommon/llrefcount.h7
-rw-r--r--indra/llcommon/llthread.cpp24
-rw-r--r--indra/llmath/llvolumeoctree.h6
-rw-r--r--indra/llmessage/llavatarnamecache.cpp32
-rw-r--r--indra/llplugin/llpluginprocessparent.cpp17
-rw-r--r--indra/llplugin/llpluginprocessparent.h2
-rw-r--r--indra/llui/llconsole.cpp2
-rw-r--r--indra/newview/gltf/accessor.cpp18
-rw-r--r--indra/newview/gltf/accessor.h4
-rw-r--r--indra/newview/gltf/animation.h2
-rw-r--r--indra/newview/gltf/asset.cpp434
-rw-r--r--indra/newview/gltf/asset.h81
-rw-r--r--indra/newview/gltfscenemanager.cpp107
-rw-r--r--indra/newview/gltfscenemanager.h15
-rw-r--r--indra/newview/llappviewer.cpp5
-rw-r--r--indra/newview/llappviewer.h3
-rw-r--r--indra/newview/llavatarrenderinfoaccountant.cpp2
-rw-r--r--indra/newview/lldrawpoolbump.cpp2
-rw-r--r--indra/newview/llfilepicker.cpp2
-rw-r--r--indra/newview/llstartup.cpp14
-rw-r--r--indra/newview/lltinygltfhelper.cpp37
-rw-r--r--indra/newview/lltinygltfhelper.h1
-rw-r--r--indra/newview/llviewermedia.cpp4
-rw-r--r--indra/newview/llviewermedia.h4
-rw-r--r--indra/newview/llviewermenu.cpp26
-rw-r--r--indra/newview/llviewerobject.cpp11
-rw-r--r--indra/newview/llviewerobject.h11
-rwxr-xr-xindra/newview/llviewerregion.cpp7
-rw-r--r--indra/newview/llvoicevivox.cpp50
-rw-r--r--indra/newview/skins/default/xui/en/menu_viewer.xml30
-rw-r--r--indra/newview/skins/default/xui/en/notifications.xml13
39 files changed, 900 insertions, 150 deletions
diff --git a/indra/llcommon/llcommon.cpp b/indra/llcommon/llcommon.cpp
index 6e988260a9..30aefa3134 100644
--- a/indra/llcommon/llcommon.cpp
+++ b/indra/llcommon/llcommon.cpp
@@ -128,8 +128,7 @@ void LLCommon::initClass()
sAprInitialized = TRUE;
}
LLTimer::initClass();
- LLThreadSafeRefCount::initThreadSafeRefCount();
- assert_main_thread(); // Make sure we record the main thread
+ assert_main_thread(); // Make sure we record the main thread
if (!sMasterThreadRecorder)
{
sMasterThreadRecorder = new LLTrace::ThreadRecorder();
@@ -143,7 +142,6 @@ void LLCommon::cleanupClass()
delete sMasterThreadRecorder;
sMasterThreadRecorder = NULL;
LLTrace::set_master_thread_recorder(NULL);
- LLThreadSafeRefCount::cleanupThreadSafeRefCount();
SUBSYSTEM_CLEANUP_DBG(LLTimer);
if (sAprInitialized)
{
diff --git a/indra/llcommon/llcoros.cpp b/indra/llcommon/llcoros.cpp
index c13900f74a..219c65fbb8 100644
--- a/indra/llcommon/llcoros.cpp
+++ b/indra/llcommon/llcoros.cpp
@@ -61,6 +61,23 @@
#include <excpt.h>
#endif
+// static
+bool LLCoros::on_main_coro()
+{
+ if (!LLCoros::instanceExists() || LLCoros::getName().empty())
+ {
+ return true;
+ }
+
+ return false;
+}
+
+// static
+bool LLCoros::on_main_thread_main_coro()
+{
+ return on_main_coro() && on_main_thread();
+}
+
// static
LLCoros::CoroData& LLCoros::get_CoroData(const std::string& caller)
{
diff --git a/indra/llcommon/llcoros.h b/indra/llcommon/llcoros.h
index fd878f20ad..00650a2454 100644
--- a/indra/llcommon/llcoros.h
+++ b/indra/llcommon/llcoros.h
@@ -94,6 +94,16 @@ class LL_COMMON_API LLCoros: public LLSingleton<LLCoros>
void cleanupSingleton() override;
public:
+ // For debugging, return true if on the main coroutine for the current thread
+ // Code that should not be executed from a coroutine should be protected by
+ // llassert(LLCoros::on_main_coro())
+ static bool on_main_coro();
+
+ // For debugging, return true if on the main thread and not in a coroutine
+ // Non-thread-safe code in the main loop should be protected by
+ // llassert(LLCoros::on_main_thread_main_coro())
+ static bool on_main_thread_main_coro();
+
/// The viewer's use of the term "coroutine" became deeply embedded before
/// the industry term "fiber" emerged to distinguish userland threads from
/// simpler, more transient kinds of coroutines. Semantically they've
diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp
index 0f48ce16b2..74b138a53b 100644
--- a/indra/llcommon/llerror.cpp
+++ b/indra/llcommon/llerror.cpp
@@ -506,7 +506,7 @@ namespace
LLError::TimeFunction mTimeFunction;
Recorders mRecorders;
- LLMutex mRecorderMutex;
+ LLCoros::Mutex mRecorderMutex;
int mShouldLogCallCounter;
@@ -1044,7 +1044,7 @@ namespace LLError
return;
}
SettingsConfigPtr s = Globals::getInstance()->getSettingsConfig();
- LLMutexLock lock(&s->mRecorderMutex);
+ LLCoros::LockType lock(s->mRecorderMutex);
s->mRecorders.push_back(recorder);
}
@@ -1055,7 +1055,7 @@ namespace LLError
return;
}
SettingsConfigPtr s = Globals::getInstance()->getSettingsConfig();
- LLMutexLock lock(&s->mRecorderMutex);
+ LLCoros::LockType lock(s->mRecorderMutex);
s->mRecorders.erase(std::remove(s->mRecorders.begin(), s->mRecorders.end(), recorder),
s->mRecorders.end());
}
@@ -1104,7 +1104,7 @@ namespace LLError
std::shared_ptr<RECORDER> findRecorder()
{
SettingsConfigPtr s = Globals::getInstance()->getSettingsConfig();
- LLMutexLock lock(&s->mRecorderMutex);
+ LLCoros::LockType lock(s->mRecorderMutex);
return findRecorderPos<RECORDER>(s).first;
}
@@ -1115,7 +1115,7 @@ namespace LLError
bool removeRecorder()
{
SettingsConfigPtr s = Globals::getInstance()->getSettingsConfig();
- LLMutexLock lock(&s->mRecorderMutex);
+ LLCoros::LockType lock(s->mRecorderMutex);
auto found = findRecorderPos<RECORDER>(s);
if (found.first)
{
@@ -1221,7 +1221,7 @@ namespace
std::string escaped_message;
- LLMutexLock lock(&s->mRecorderMutex);
+ LLCoros::LockType lock(s->mRecorderMutex);
for (LLError::RecorderPtr& r : s->mRecorders)
{
if (!r->enabled())
diff --git a/indra/llcommon/llevents.cpp b/indra/llcommon/llevents.cpp
index 70931f3a65..8006f9d059 100644
--- a/indra/llcommon/llevents.cpp
+++ b/indra/llcommon/llevents.cpp
@@ -382,7 +382,7 @@ std::string LLEventPump::inventName(const std::string& pfx)
void LLEventPump::clear()
{
- LLMutexLock lock(&mConnectionListMutex);
+ LLCoros::LockType lock(mConnectionListMutex);
// Destroy the original LLStandardSignal instance, replacing it with a
// whole new one.
mSignal = std::make_shared<LLStandardSignal>();
@@ -394,7 +394,7 @@ void LLEventPump::reset()
{
// Resetting mSignal is supposed to disconnect everything on its own
// But due to crash on 'reset' added explicit cleanup to get more data
- LLMutexLock lock(&mConnectionListMutex);
+ LLCoros::LockType lock(mConnectionListMutex);
ConnectionMap::const_iterator iter = mConnections.begin();
ConnectionMap::const_iterator end = mConnections.end();
while (iter!=end)
@@ -419,7 +419,7 @@ LLBoundListener LLEventPump::listen_impl(const std::string& name, const LLEventL
return LLBoundListener();
}
- LLMutexLock lock(&mConnectionListMutex);
+ LLCoros::LockType lock(mConnectionListMutex);
float nodePosition = 1.0;
@@ -582,7 +582,7 @@ LLBoundListener LLEventPump::listen_impl(const std::string& name, const LLEventL
LLBoundListener LLEventPump::getListener(const std::string& name)
{
- LLMutexLock lock(&mConnectionListMutex);
+ LLCoros::LockType lock(mConnectionListMutex);
ConnectionMap::const_iterator found = mConnections.find(name);
if (found != mConnections.end())
{
@@ -594,7 +594,7 @@ LLBoundListener LLEventPump::getListener(const std::string& name)
void LLEventPump::stopListening(const std::string& name)
{
- LLMutexLock lock(&mConnectionListMutex);
+ LLCoros::LockType lock(mConnectionListMutex);
ConnectionMap::iterator found = mConnections.find(name);
if (found != mConnections.end())
{
diff --git a/indra/llcommon/llevents.h b/indra/llcommon/llevents.h
index bebcfacdcb..df54a6546d 100644
--- a/indra/llcommon/llevents.h
+++ b/indra/llcommon/llevents.h
@@ -61,6 +61,7 @@
#include "llstl.h"
#include "llexception.h"
#include "llhandle.h"
+#include "llcoros.h"
/*==========================================================================*|
// override this to allow binding free functions with more parameters
@@ -601,7 +602,7 @@ private:
LLHandle<LLEventPumps> mRegistry;
std::string mName;
- LLMutex mConnectionListMutex;
+ LLCoros::Mutex mConnectionListMutex;
protected:
virtual LLBoundListener listen_impl(const std::string& name, const LLEventListener&,
diff --git a/indra/llcommon/llfixedbuffer.h b/indra/llcommon/llfixedbuffer.h
index 554cf48a4c..b3bef7520b 100644
--- a/indra/llcommon/llfixedbuffer.h
+++ b/indra/llcommon/llfixedbuffer.h
@@ -33,6 +33,7 @@
#include "llstring.h"
#include "llthread.h"
#include "llerrorcontrol.h"
+#include "llcoros.h"
// fixed buffer implementation
class LL_COMMON_API LLFixedBuffer : public LLLineBuffer
@@ -58,7 +59,7 @@ protected:
void addWLine(const LLWString& line);
protected:
- LLMutex mMutex ;
+ LLCoros::Mutex mMutex ;
};
#endif //LL_FIXED_BUFFER_H
diff --git a/indra/llcommon/llmutex.cpp b/indra/llcommon/llmutex.cpp
index 0273dd5970..5d93d6e72b 100644
--- a/indra/llcommon/llmutex.cpp
+++ b/indra/llcommon/llmutex.cpp
@@ -28,6 +28,7 @@
#include "llmutex.h"
#include "llthread.h"
#include "lltimer.h"
+#include "llcoros.h"
//============================================================================
@@ -44,7 +45,17 @@ LLMutex::~LLMutex()
void LLMutex::lock()
{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD;
+
+ // LLMutex is not coroutine aware and should not be used from a coroutine
+ // If your code is running in a coroutine, you should use LLCoros::Mutex instead
+ // NOTE: If the stack trace you're staring at contains non-thread-safe code,
+ // you should use LLAppViewer::instance().postToMainThread() to shuttle execution
+ // back to the main loop.
+ // NOTE: If you got here from seeing this assert in your log and you're not seeing
+ // a stack trace that points here, put a breakpoint in on_main_coro and try again.
+ llassert(LLCoros::on_main_coro());
+
if(isSelfLocked())
{ //redundant lock
mCount++;
@@ -66,7 +77,8 @@ void LLMutex::lock()
void LLMutex::unlock()
{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD;
+
if (mCount > 0)
{ //not the root unlock
mCount--;
@@ -111,7 +123,7 @@ LLThread::id_t LLMutex::lockingThread() const
bool LLMutex::trylock()
{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD;
if(isSelfLocked())
{ //redundant lock
mCount++;
diff --git a/indra/llcommon/llrefcount.h b/indra/llcommon/llrefcount.h
index 15e7175fc8..109c29c0c9 100644
--- a/indra/llcommon/llrefcount.h
+++ b/indra/llcommon/llrefcount.h
@@ -89,13 +89,6 @@ private:
class LL_COMMON_API LLThreadSafeRefCount
{
-public:
- static void initThreadSafeRefCount(); // creates sMutex
- static void cleanupThreadSafeRefCount(); // destroys sMutex
-
-private:
- static LLMutex* sMutex;
-
protected:
virtual ~LLThreadSafeRefCount(); // use unref()
diff --git a/indra/llcommon/llthread.cpp b/indra/llcommon/llthread.cpp
index cd4975d9d3..ef66c36827 100644
--- a/indra/llcommon/llthread.cpp
+++ b/indra/llcommon/llthread.cpp
@@ -421,30 +421,6 @@ void LLThread::unlockData()
//============================================================================
-//----------------------------------------------------------------------------
-
-//static
-LLMutex* LLThreadSafeRefCount::sMutex = 0;
-
-//static
-void LLThreadSafeRefCount::initThreadSafeRefCount()
-{
- if (!sMutex)
- {
- sMutex = new LLMutex();
- }
-}
-
-//static
-void LLThreadSafeRefCount::cleanupThreadSafeRefCount()
-{
- delete sMutex;
- sMutex = NULL;
-}
-
-
-//----------------------------------------------------------------------------
-
LLThreadSafeRefCount::LLThreadSafeRefCount() :
mRef(0)
{
diff --git a/indra/llmath/llvolumeoctree.h b/indra/llmath/llvolumeoctree.h
index 0bbb793896..1df2bf5390 100644
--- a/indra/llmath/llvolumeoctree.h
+++ b/indra/llmath/llvolumeoctree.h
@@ -48,12 +48,6 @@ public:
*this = rhs;
}
- const LLVolumeTriangle& operator=(const LLVolumeTriangle& rhs)
- {
- LL_ERRS() << "Illegal operation!" << LL_ENDL;
- return *this;
- }
-
~LLVolumeTriangle()
{
diff --git a/indra/llmessage/llavatarnamecache.cpp b/indra/llmessage/llavatarnamecache.cpp
index f7a9f55685..314e7f0217 100644
--- a/indra/llmessage/llavatarnamecache.cpp
+++ b/indra/llmessage/llavatarnamecache.cpp
@@ -45,6 +45,7 @@
#include "llcorehttputil.h"
#include "llexception.h"
#include "stringize.h"
+#include "workqueue.h"
#include <map>
#include <set>
@@ -179,19 +180,26 @@ void LLAvatarNameCache::requestAvatarNameCache_(std::string url, std::vector<LLU
if (LLAvatarNameCache::instanceExists())
{
- if (!success)
- { // on any sort of failure add dummy records for any agent IDs
- // in this request that we do not have cached already
- std::vector<LLUUID>::const_iterator it = agentIds.begin();
- for (; it != agentIds.end(); ++it)
- {
- const LLUUID& agent_id = *it;
- LLAvatarNameCache::getInstance()->handleAgentError(agent_id);
- }
- return;
- }
+ // dispatch handler execution back to mainloop
+ auto workqueue = LL::WorkQueue::getInstance("mainloop");
- LLAvatarNameCache::getInstance()->handleAvNameCacheSuccess(results, httpResults);
+ if (workqueue)
+ {
+ workqueue->post([=]()
+ {
+ if (!success)
+ { // on any sort of failure add dummy records for any agent IDs
+ // in this request that we do not have cached already
+ for (const auto& agent_id : agentIds)
+ {
+ LLAvatarNameCache::getInstance()->handleAgentError(agent_id);
+ }
+ return;
+ }
+
+ LLAvatarNameCache::getInstance()->handleAvNameCacheSuccess(results, httpResults);
+ });
+ }
}
}
catch (const LLCoros::Stop&)
diff --git a/indra/llplugin/llpluginprocessparent.cpp b/indra/llplugin/llpluginprocessparent.cpp
index f5966b71de..a28f6c648f 100644
--- a/indra/llplugin/llpluginprocessparent.cpp
+++ b/indra/llplugin/llpluginprocessparent.cpp
@@ -34,7 +34,6 @@
#include "llpluginmessageclasses.h"
#include "llsdserialize.h"
#include "stringize.h"
-
#include "llapr.h"
//virtual
@@ -46,7 +45,7 @@ LLPluginProcessParentOwner::~LLPluginProcessParentOwner()
bool LLPluginProcessParent::sUseReadThread = false;
apr_pollset_t *LLPluginProcessParent::sPollSet = NULL;
bool LLPluginProcessParent::sPollsetNeedsRebuild = false;
-LLMutex *LLPluginProcessParent::sInstancesMutex;
+LLCoros::Mutex *LLPluginProcessParent::sInstancesMutex;
LLPluginProcessParent::mapInstances_t LLPluginProcessParent::sInstances;
LLThread *LLPluginProcessParent::sReadThread = NULL;
@@ -106,7 +105,7 @@ LLPluginProcessParent::LLPluginProcessParent(LLPluginProcessParentOwner *owner):
{
if(!sInstancesMutex)
{
- sInstancesMutex = new LLMutex();
+ sInstancesMutex = new LLCoros::Mutex();
}
mOwner = owner;
@@ -176,7 +175,7 @@ LLPluginProcessParent::ptr_t LLPluginProcessParent::create(LLPluginProcessParent
// Don't add to the global list until fully constructed.
{
- LLMutexLock lock(sInstancesMutex);
+ LLCoros::LockType lock(*sInstancesMutex);
sInstances.insert(mapInstances_t::value_type(that.get(), that));
}
@@ -186,7 +185,7 @@ LLPluginProcessParent::ptr_t LLPluginProcessParent::create(LLPluginProcessParent
/*static*/
void LLPluginProcessParent::shutdown()
{
- LLMutexLock lock(sInstancesMutex);
+ LLCoros::LockType lock(*sInstancesMutex);
mapInstances_t::iterator it;
for (it = sInstances.begin(); it != sInstances.end(); ++it)
@@ -244,7 +243,7 @@ bool LLPluginProcessParent::pollTick()
{
// this grabs a copy of the smart pointer to ourselves to ensure that we do not
// get destroyed until after this method returns.
- LLMutexLock lock(sInstancesMutex);
+ LLCoros::LockType lock(*sInstancesMutex);
mapInstances_t::iterator it = sInstances.find(this);
if (it != sInstances.end())
that = (*it).second;
@@ -263,7 +262,7 @@ void LLPluginProcessParent::removeFromProcessing()
// Remove from the global list before beginning destruction.
{
// Make sure to get the global mutex _first_ here, to avoid a possible deadlock against LLPluginProcessParent::poll()
- LLMutexLock lock(sInstancesMutex);
+ LLCoros::LockType lock(*sInstancesMutex);
{
LLMutexLock lock2(&mIncomingQueueMutex);
sInstances.erase(this);
@@ -845,7 +844,7 @@ void LLPluginProcessParent::updatePollset()
return;
}
- LLMutexLock lock(sInstancesMutex);
+ LLCoros::LockType lock(*sInstancesMutex);
if(sPollSet)
{
@@ -968,7 +967,7 @@ void LLPluginProcessParent::poll(F64 timeout)
mapInstances_t::iterator it;
{
- LLMutexLock lock(sInstancesMutex);
+ LLCoros::LockType lock(*sInstancesMutex);
it = sInstances.find(thatId);
if (it != sInstances.end())
that = (*it).second;
diff --git a/indra/llplugin/llpluginprocessparent.h b/indra/llplugin/llpluginprocessparent.h
index 01627925d7..2f9dc921ea 100644
--- a/indra/llplugin/llpluginprocessparent.h
+++ b/indra/llplugin/llpluginprocessparent.h
@@ -207,7 +207,7 @@ private:
apr_pollfd_t mPollFD;
static apr_pollset_t *sPollSet;
static bool sPollsetNeedsRebuild;
- static LLMutex *sInstancesMutex;
+ static LLCoros::Mutex *sInstancesMutex;
static mapInstances_t sInstances;
static void dirtyPollSet();
static void updatePollset();
diff --git a/indra/llui/llconsole.cpp b/indra/llui/llconsole.cpp
index 8fc2978bdd..6a44108741 100644
--- a/indra/llui/llconsole.cpp
+++ b/indra/llui/llconsole.cpp
@@ -380,7 +380,7 @@ void LLConsole::updateClass()
void LLConsole::update()
{
{
- LLMutexLock lock(&mMutex);
+ LLCoros::LockType lock(mMutex);
while (!mLines.empty())
{
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/animation.h b/indra/newview/gltf/animation.h
index 869eae963a..cc2045ebb2 100644
--- a/indra/newview/gltf/animation.h
+++ b/indra/newview/gltf/animation.h
@@ -81,8 +81,6 @@ namespace LL
S32 mSampler = INVALID_INDEX;
Target mTarget;
- std::string mTargetPath;
- std::string mName;
const Channel& operator=(const tinygltf::AnimationChannel& src)
{
diff --git a/indra/newview/gltf/asset.cpp b/indra/newview/gltf/asset.cpp
index 313e82bf01..42f064699c 100644
--- a/indra/newview/gltf/asset.cpp
+++ b/indra/newview/gltf/asset.cpp
@@ -30,9 +30,271 @@
#include "llvolumeoctree.h"
#include "../llviewershadermgr.h"
#include "../llviewercontrol.h"
+#include "../llviewertexturelist.h"
using namespace LL::GLTF;
+namespace LL
+{
+ namespace GLTF
+ {
+ template <typename T, typename U>
+ void copy(const std::vector<T>& src, std::vector<U>& dst)
+ {
+ dst.resize(src.size());
+ for (U32 i = 0; i < src.size(); ++i)
+ {
+ copy(src[i], dst[i]);
+ }
+ }
+
+ void copy(const Node& src, tinygltf::Node& dst)
+ {
+ if (src.mMatrixValid)
+ {
+ if (!src.mMatrix.asMatrix4().isIdentity())
+ {
+ dst.matrix.resize(16);
+ for (U32 i = 0; i < 16; ++i)
+ {
+ dst.matrix[i] = src.mMatrix.getF32ptr()[i];
+ }
+ }
+ }
+ else if (src.mTRSValid)
+ {
+ if (!src.mRotation.equals(glh::quaternionf::identity(), FLT_EPSILON))
+ {
+ dst.rotation.resize(4);
+ for (U32 i = 0; i < 4; ++i)
+ {
+ dst.rotation[i] = src.mRotation.get_value()[i];
+ }
+ }
+
+ if (src.mTranslation != glh::vec3f(0.f, 0.f, 0.f))
+ {
+ dst.translation.resize(3);
+ for (U32 i = 0; i < 3; ++i)
+ {
+ dst.translation[i] = src.mTranslation.v[i];
+ }
+ }
+
+ if (src.mScale != glh::vec3f(1.f, 1.f, 1.f))
+ {
+ dst.scale.resize(3);
+ for (U32 i = 0; i < 3; ++i)
+ {
+ dst.scale[i] = src.mScale.v[i];
+ }
+ }
+ }
+
+ dst.children = src.mChildren;
+ dst.mesh = src.mMesh;
+ dst.skin = src.mSkin;
+ dst.name = src.mName;
+ }
+
+ void copy(const Scene& src, tinygltf::Scene& dst)
+ {
+ dst.nodes = src.mNodes;
+ dst.name = src.mName;
+ }
+
+ void copy(const Primitive& src, tinygltf::Primitive& dst)
+ {
+ for (auto& attrib : src.mAttributes)
+ {
+ dst.attributes[attrib.first] = attrib.second;
+ }
+ dst.indices = src.mIndices;
+ dst.material = src.mMaterial;
+ dst.mode = src.mMode;
+ }
+
+ void copy(const Mesh& src, tinygltf::Mesh& mesh)
+ {
+ copy(src.mPrimitives, mesh.primitives);
+ mesh.weights = src.mWeights;
+ mesh.name = src.mName;
+ }
+
+ void copy(const Material::TextureInfo& src, tinygltf::TextureInfo& dst)
+ {
+ dst.index = src.mIndex;
+ dst.texCoord = src.mTexCoord;
+ }
+
+ void copy(const Material::OcclusionTextureInfo& src, tinygltf::OcclusionTextureInfo& dst)
+ {
+ dst.index = src.mIndex;
+ dst.texCoord = src.mTexCoord;
+ dst.strength = src.mStrength;
+ }
+
+ void copy(const Material::NormalTextureInfo& src, tinygltf::NormalTextureInfo& dst)
+ {
+ dst.index = src.mIndex;
+ dst.texCoord = src.mTexCoord;
+ dst.scale = src.mScale;
+ }
+
+ void copy(const Material::PbrMetallicRoughness& src, tinygltf::PbrMetallicRoughness& dst)
+ {
+ dst.baseColorFactor = { src.mBaseColorFactor.v[0], src.mBaseColorFactor.v[1], src.mBaseColorFactor.v[2], src.mBaseColorFactor.v[3] };
+ copy(src.mBaseColorTexture, dst.baseColorTexture);
+ dst.metallicFactor = src.mMetallicFactor;
+ dst.roughnessFactor = src.mRoughnessFactor;
+ copy(src.mMetallicRoughnessTexture, dst.metallicRoughnessTexture);
+ }
+
+ void copy(const Material& src, tinygltf::Material& material)
+ {
+ material.name = src.mName;
+
+ material.emissiveFactor = { src.mEmissiveFactor.v[0], src.mEmissiveFactor.v[1], src.mEmissiveFactor.v[2] };
+ copy(src.mPbrMetallicRoughness, material.pbrMetallicRoughness);
+ copy(src.mNormalTexture, material.normalTexture);
+ copy(src.mEmissiveTexture, material.emissiveTexture);
+ }
+
+ void copy(const Texture& src, tinygltf::Texture& texture)
+ {
+ texture.sampler = src.mSampler;
+ texture.source = src.mSource;
+ texture.name = src.mName;
+ }
+
+ void copy(const Sampler& src, tinygltf::Sampler& sampler)
+ {
+ sampler.magFilter = src.mMagFilter;
+ sampler.minFilter = src.mMinFilter;
+ sampler.wrapS = src.mWrapS;
+ sampler.wrapT = src.mWrapT;
+ sampler.name = src.mName;
+ }
+
+ void copy(const Skin& src, tinygltf::Skin& skin)
+ {
+ skin.joints = src.mJoints;
+ skin.inverseBindMatrices = src.mInverseBindMatrices;
+ skin.skeleton = src.mSkeleton;
+ skin.name = src.mName;
+ }
+
+ void copy(const Accessor& src, tinygltf::Accessor& accessor)
+ {
+ accessor.bufferView = src.mBufferView;
+ accessor.byteOffset = src.mByteOffset;
+ accessor.componentType = src.mComponentType;
+ accessor.minValues = src.mMin;
+ accessor.maxValues = src.mMax;
+
+ accessor.count = src.mCount;
+ accessor.type = src.mType;
+ accessor.normalized = src.mNormalized;
+ accessor.name = src.mName;
+ }
+
+ void copy(const Animation::Sampler& src, tinygltf::AnimationSampler& sampler)
+ {
+ sampler.input = src.mInput;
+ sampler.output = src.mOutput;
+ sampler.interpolation = src.mInterpolation;
+ }
+
+ void copy(const Animation::Channel& src, tinygltf::AnimationChannel& channel)
+ {
+ channel.sampler = src.mSampler;
+ channel.target_node = src.mTarget.mNode;
+ channel.target_path = src.mTarget.mPath;
+ }
+
+ void copy(const Animation& src, tinygltf::Animation& animation)
+ {
+ animation.name = src.mName;
+
+ copy(src.mSamplers, animation.samplers);
+
+ U32 channel_count = src.mRotationChannels.size() + src.mTranslationChannels.size() + src.mScaleChannels.size();
+
+ animation.channels.resize(channel_count);
+
+ U32 idx = 0;
+ for (U32 i = 0; i < src.mTranslationChannels.size(); ++i)
+ {
+ copy(src.mTranslationChannels[i], animation.channels[idx++]);
+ }
+
+ for (U32 i = 0; i < src.mRotationChannels.size(); ++i)
+ {
+ copy(src.mRotationChannels[i], animation.channels[idx++]);
+ }
+
+ for (U32 i = 0; i < src.mScaleChannels.size(); ++i)
+ {
+ copy(src.mScaleChannels[i], animation.channels[idx++]);
+ }
+ }
+
+ void copy(const Buffer& src, tinygltf::Buffer& buffer)
+ {
+ buffer.uri = src.mUri;
+ buffer.data = src.mData;
+ buffer.name = src.mName;
+ }
+
+ void copy(const BufferView& src, tinygltf::BufferView& bufferView)
+ {
+ bufferView.buffer = src.mBuffer;
+ bufferView.byteOffset = src.mByteOffset;
+ bufferView.byteLength = src.mByteLength;
+ bufferView.byteStride = src.mByteStride;
+ bufferView.target = src.mTarget;
+ bufferView.name = src.mName;
+ }
+
+ void copy(const Image& src, tinygltf::Image& image)
+ {
+ image.name = src.mName;
+ image.width = src.mWidth;
+ image.height = src.mHeight;
+ image.component = src.mComponent;
+ image.bits = src.mBits;
+ image.pixel_type = src.mPixelType;
+
+ image.image = src.mData;
+ image.bufferView = src.mBufferView;
+ image.mimeType = src.mMimeType;
+ image.uri = src.mUri;
+ }
+
+ void copy(const Asset & src, tinygltf::Model& dst)
+ {
+ dst.defaultScene = src.mDefaultScene;
+ dst.asset.copyright = src.mCopyright;
+ dst.asset.version = src.mVersion;
+ dst.asset.minVersion = src.mMinVersion;
+ dst.asset.generator = "Linden Lab Experimental GLTF Export";
+ dst.asset.extras = src.mExtras;
+
+ copy(src.mScenes, dst.scenes);
+ copy(src.mNodes, dst.nodes);
+ copy(src.mMeshes, dst.meshes);
+ copy(src.mMaterials, dst.materials);
+ copy(src.mBuffers, dst.buffers);
+ copy(src.mBufferViews, dst.bufferViews);
+ copy(src.mTextures, dst.textures);
+ copy(src.mSamplers, dst.samplers);
+ copy(src.mImages, dst.images);
+ copy(src.mAccessors, dst.accessors);
+ copy(src.mAnimations, dst.animations);
+ copy(src.mSkins, dst.skins);
+ }
+ }
+}
void Scene::updateTransforms(Asset& asset)
{
LLMatrix4a identity;
@@ -237,6 +499,8 @@ void Node::makeMatrixValid()
mMatrix.loadu(t.m);
mMatrixValid = true;
}
+
+ llassert(mMatrixValid);
}
void Node::makeTRSValid()
@@ -252,6 +516,8 @@ void Node::makeTRSValid()
mRotation.set_value(t);
mTRSValid = true;
}
+
+ llassert(mTRSValid);
}
void Node::setRotation(const glh::quaternionf& q)
@@ -318,6 +584,7 @@ const Node& Node::operator=(const tinygltf::Node& src)
{
// node specifies no transformation, set to identity
mMatrix.setIdentity();
+ mMatrixValid = true;
}
mChildren = src.children;
@@ -467,6 +734,15 @@ void Asset::allocateGLResources(const std::string& filename, const tinygltf::Mod
const Asset& Asset::operator=(const tinygltf::Model& src)
{
+ mVersion = src.asset.version;
+ mMinVersion = src.asset.minVersion;
+ mGenerator = src.asset.generator;
+ mCopyright = src.asset.copyright;
+ mExtras = src.asset.extras;
+
+ mDefaultScene = src.defaultScene;
+
+
mScenes.resize(src.scenes.size());
for (U32 i = 0; i < src.scenes.size(); ++i)
{
@@ -542,9 +818,167 @@ const Asset& Asset::operator=(const tinygltf::Model& src)
return *this;
}
+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)
+{
+ mIndex = src.index;
+ mTexCoord = src.texCoord;
+ return *this;
+}
+
+const Material::OcclusionTextureInfo& Material::OcclusionTextureInfo::operator=(const tinygltf::OcclusionTextureInfo& src)
+{
+ mIndex = src.index;
+ mTexCoord = src.texCoord;
+ mStrength = src.strength;
+ return *this;
+}
+
+const Material::NormalTextureInfo& Material::NormalTextureInfo::operator=(const tinygltf::NormalTextureInfo& src)
+{
+ mIndex = src.index;
+ mTexCoord = src.texCoord;
+ mScale = src.scale;
+ return *this;
+}
+
+const Material::PbrMetallicRoughness& Material::PbrMetallicRoughness::operator=(const tinygltf::PbrMetallicRoughness& src)
+{
+ if (src.baseColorFactor.size() == 4)
+ {
+ mBaseColorFactor.set_value(src.baseColorFactor[0], src.baseColorFactor[1], src.baseColorFactor[2], src.baseColorFactor[3]);
+ }
+
+ mBaseColorTexture = src.baseColorTexture;
+ mMetallicFactor = src.metallicFactor;
+ mRoughnessFactor = src.roughnessFactor;
+ mMetallicRoughnessTexture = src.metallicRoughnessTexture;
+
+ return *this;
+}
const Material& Material::operator=(const tinygltf::Material& src)
{
mName = src.name;
+
+ if (src.emissiveFactor.size() == 3)
+ {
+ mEmissiveFactor.set_value(src.emissiveFactor[0], src.emissiveFactor[1], src.emissiveFactor[2]);
+ }
+
+ mPbrMetallicRoughness = src.pbrMetallicRoughness;
+ mNormalTexture = src.normalTexture;
+ mOcclusionTexture = src.occlusionTexture;
+ mEmissiveTexture = src.emissiveTexture;
+
+ mAlphaMode = src.alphaMode;
+ mAlphaCutoff = src.alphaCutoff;
+ mDoubleSided = src.doubleSided;
+
return *this;
}
diff --git a/indra/newview/gltf/asset.h b/indra/newview/gltf/asset.h
index 6e576a1ffe..5a62313705 100644
--- a/indra/newview/gltf/asset.h
+++ b/indra/newview/gltf/asset.h
@@ -45,11 +45,59 @@ namespace LL
class Material
{
public:
+ class TextureInfo
+ {
+ public:
+ S32 mIndex = INVALID_INDEX;
+ S32 mTexCoord = 0;
+
+ const TextureInfo& operator=(const tinygltf::TextureInfo& src);
+ };
+
+ class NormalTextureInfo : public TextureInfo
+ {
+ public:
+ F32 mScale = 1.0f;
+
+ const NormalTextureInfo& operator=(const tinygltf::NormalTextureInfo& src);
+ };
+
+ class OcclusionTextureInfo : public TextureInfo
+ {
+ public:
+ F32 mStrength = 1.0f;
+
+ const OcclusionTextureInfo& operator=(const tinygltf::OcclusionTextureInfo& src);
+ };
+
+ class PbrMetallicRoughness
+ {
+ public:
+ glh::vec4f mBaseColorFactor = glh::vec4f(1.f,1.f,1.f,1.f);
+ TextureInfo mBaseColorTexture;
+ F32 mMetallicFactor = 1.0f;
+ F32 mRoughnessFactor = 1.0f;
+ TextureInfo mMetallicRoughnessTexture;
+ const PbrMetallicRoughness& operator=(const tinygltf::PbrMetallicRoughness& src);
+ };
+
+
// use LLFetchedGLTFMaterial for now, but eventually we'll want to use
// a more flexible GLTF material implementation instead of the fixed packing
// version we use for sharable GLTF material assets
LLPointer<LLFetchedGLTFMaterial> mMaterial;
+ PbrMetallicRoughness mPbrMetallicRoughness;
+ NormalTextureInfo mNormalTexture;
+ OcclusionTextureInfo mOcclusionTexture;
+ TextureInfo mEmissiveTexture;
+
+
std::string mName;
+ glh::vec3f mEmissiveFactor = glh::vec3f(0.f, 0.f, 0.f);
+ std::string mAlphaMode = "OPAQUE";
+ F32 mAlphaCutoff = 0.5f;
+ bool mDoubleSided = false;
+
const Material& operator=(const tinygltf::Material& src);
@@ -179,11 +227,16 @@ namespace LL
std::string mName;
std::string mUri;
std::string mMimeType;
+
+ S32 mBufferView = INVALID_INDEX;
+
std::vector<U8> mData;
S32 mWidth;
S32 mHeight;
S32 mComponent;
S32 mBits;
+ S32 mPixelType;
+
LLPointer<LLViewerFetchedTexture> mTexture;
const Image& operator=(const tinygltf::Image& src)
@@ -196,10 +249,14 @@ namespace LL
mHeight = src.height;
mComponent = src.component;
mBits = src.bits;
-
+ mBufferView = src.bufferView;
+ mPixelType = src.pixel_type;
return *this;
}
+ // save image clear local data, and set uri
+ void decompose(Asset& asset, const std::string& filename);
+
void allocateGLResources()
{
// allocate texture
@@ -208,7 +265,7 @@ namespace LL
};
// C++ representation of a GLTF Asset
- class Asset : public LLRefCount
+ class Asset
{
public:
std::vector<Scene> mScenes;
@@ -224,6 +281,15 @@ namespace LL
std::vector<Animation> mAnimations;
std::vector<Skin> mSkins;
+ std::string mVersion;
+ std::string mGenerator;
+ std::string mMinVersion;
+ std::string mCopyright;
+
+ S32 mDefaultScene = INVALID_INDEX;
+ tinygltf::Value mExtras;
+
+
// the last time update() was called according to gFrameTimeSeconds
F32 mLastUpdateTime = gFrameTimeSeconds;
@@ -258,7 +324,16 @@ namespace LL
);
const Asset& operator=(const tinygltf::Model& src);
-
+
+ // 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 4e3439ea5c..6533f283e2 100644
--- a/indra/newview/gltfscenemanager.cpp
+++ b/indra/newview/gltfscenemanager.cpp
@@ -66,11 +66,95 @@ void GLTFSceneManager::load()
}
},
LLFilePicker::FFLOAD_GLTF,
- true);
+ false);
}
else
{
- LLNotificationsUtil::add("GLTFPreviewSelection");
+ LLNotificationsUtil::add("GLTFOpenSelection");
+ }
+}
+
+void GLTFSceneManager::saveAs()
+{
+ LLViewerObject* obj = LLSelectMgr::instance().getSelection()->getFirstRootObject();
+ if (obj && obj->mGLTFAsset)
+ {
+ 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().save(filenames[0]);
+ }
+ },
+ LLFilePicker::FFSAVE_GLTF,
+ "scene.gltf");
+ }
+ else
+ {
+ LLNotificationsUtil::add("GLTFSaveSelection");
+ }
+}
+
+void GLTFSceneManager::decomposeSelection()
+{
+ LLViewerObject* obj = LLSelectMgr::instance().getSelection()->getFirstRootObject();
+ if (obj && obj->mGLTFAsset)
+ {
+ 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)
+ {
+ // 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();
+ if (obj && obj->mGLTFAsset)
+ {
+ Asset* asset = obj->mGLTFAsset.get();
+ tinygltf::Model model;
+ asset->save(model);
+
+ LLTinyGLTFHelper::saveModel(filename, model);
}
}
@@ -79,7 +163,7 @@ void GLTFSceneManager::load(const std::string& filename)
tinygltf::Model model;
LLTinyGLTFHelper::loadModel(filename, model);
- LLPointer<Asset> asset = new Asset();
+ std::shared_ptr<Asset> asset = std::make_shared<Asset>();
*asset = model;
gDebugProgram.bind(); // bind a shader to satisfy LLVertexBuffer assertions
@@ -126,10 +210,7 @@ void GLTFSceneManager::update()
continue;
}
- Asset* asset = mObjects[i]->mGLTFAsset;
-
- asset->update();
-
+ mObjects[i]->mGLTFAsset->update();
}
}
@@ -151,7 +232,7 @@ void GLTFSceneManager::render(bool opaque, bool rigged)
continue;
}
- Asset* asset = mObjects[i]->mGLTFAsset;
+ Asset* asset = mObjects[i]->mGLTFAsset.get();
gGL.pushMatrix();
@@ -298,7 +379,7 @@ LLDrawable* GLTFSceneManager::lineSegmentIntersect(const LLVector4a& start, cons
}
// temporary debug -- always double check objects that have GLTF scenes hanging off of them even if the ray doesn't intersect the object bounds
- if (lineSegmentIntersect((LLVOVolume*) mObjects[i].get(), mObjects[i]->mGLTFAsset, start, local_end, -1, pick_transparent, pick_rigged, pick_unselectable, node_hit, primitive_hit, &position, tex_coord, normal, tangent))
+ if (lineSegmentIntersect((LLVOVolume*) mObjects[i].get(), mObjects[i]->mGLTFAsset.get(), start, local_end, -1, pick_transparent, pick_rigged, pick_unselectable, node_hit, primitive_hit, &position, tex_coord, normal, tangent))
{
local_end = position;
if (intersection)
@@ -425,7 +506,7 @@ void GLTFSceneManager::renderDebug()
matMul(mat, modelview, modelview);
- Asset* asset = obj->mGLTFAsset;
+ Asset* asset = obj->mGLTFAsset.get();
for (auto& node : asset->mNodes)
{
@@ -440,7 +521,7 @@ void GLTFSceneManager::renderDebug()
continue;
}
- Asset* asset = obj->mGLTFAsset;
+ Asset* asset = obj->mGLTFAsset.get();
LLMatrix4a mat = obj->getGLTFAssetToAgentTransform();
@@ -477,7 +558,7 @@ void GLTFSceneManager::renderDebug()
matMul(mat, modelview, modelview);
- Asset* asset = obj->mGLTFAsset;
+ Asset* asset = obj->mGLTFAsset.get();
for (auto& node : asset->mNodes)
{
@@ -538,7 +619,7 @@ void GLTFSceneManager::renderDebug()
if (drawable)
{
gGL.pushMatrix();
- Asset* asset = drawable->getVObj()->mGLTFAsset;
+ Asset* asset = drawable->getVObj()->mGLTFAsset.get();
Node* node = &asset->mNodes[node_hit];
Primitive* primitive = &asset->mMeshes[node->mMesh].mPrimitives[primitive_hit];
diff --git a/indra/newview/gltfscenemanager.h b/indra/newview/gltfscenemanager.h
index d286f335e4..e380be7e3c 100644
--- a/indra/newview/gltfscenemanager.h
+++ b/indra/newview/gltfscenemanager.h
@@ -28,6 +28,16 @@
#include "llsingleton.h"
#include "llviewerobject.h"
+class LLVOVolume;
+class LLDrawable;
+
+namespace LL
+{
+ namespace GLTF
+ {
+ class Asset;
+ }
+}
namespace LL
{
@@ -40,6 +50,11 @@ namespace LL
void load(); // open filepicker to choose asset
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 (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);
void renderOpaque();
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index 90f9b38f2d..c966f9cbcc 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -5200,6 +5200,11 @@ void LLAppViewer::updateNameLookupUrl(const LLViewerRegion * regionp)
}
}
+void LLAppViewer::postToMainCoro(const LL::WorkQueue::Work& work)
+{
+ gMainloopWork.post(work);
+}
+
void LLAppViewer::idleNameCache()
{
// Neither old nor new name cache can function before agent has a region
diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h
index 9352dba06d..92976a70af 100644
--- a/indra/newview/llappviewer.h
+++ b/indra/newview/llappviewer.h
@@ -226,6 +226,9 @@ public:
void updateNameLookupUrl(const LLViewerRegion* regionp);
+ // post given work to the "mainloop" work queue for handling on the main thread
+ void postToMainCoro(const LL::WorkQueue::Work& work);
+
protected:
virtual bool initWindow(); // Initialize the viewer's window.
virtual void initLoggingAndGetLastDuration(); // Initialize log files, logging system
diff --git a/indra/newview/llavatarrenderinfoaccountant.cpp b/indra/newview/llavatarrenderinfoaccountant.cpp
index b95b971890..ba9ba4dc64 100644
--- a/indra/newview/llavatarrenderinfoaccountant.cpp
+++ b/indra/newview/llavatarrenderinfoaccountant.cpp
@@ -236,8 +236,6 @@ void LLAvatarRenderInfoAccountant::avatarRenderInfoReportCoro(std::string url, U
!avatar->isControlAvatar() && // Not part of an animated object
avatar->getObjectHost() == regionp->getHost()) // Ensure it's on the same region
{
- avatar->calculateUpdateRenderComplexity(); // Make sure the numbers are up-to-date
-
LLSD info = LLSD::emptyMap();
U32 avatar_complexity = avatar->getVisualComplexity();
if (avatar_complexity > 0)
diff --git a/indra/newview/lldrawpoolbump.cpp b/indra/newview/lldrawpoolbump.cpp
index 24df11c158..518310476b 100644
--- a/indra/newview/lldrawpoolbump.cpp
+++ b/indra/newview/lldrawpoolbump.cpp
@@ -699,6 +699,8 @@ void LLBumpImageList::addTextureStats(U8 bump, const LLUUID& base_image_id, F32
void LLBumpImageList::updateImages()
{
+ llassert(LLCoros::on_main_thread_main_coro()); // This code is not thread safe
+
for (bump_image_map_t::iterator iter = mBrightnessEntries.begin(); iter != mBrightnessEntries.end(); )
{
LLViewerTexture* image = iter->second;
diff --git a/indra/newview/llfilepicker.cpp b/indra/newview/llfilepicker.cpp
index b82172c506..23d9b25344 100644
--- a/indra/newview/llfilepicker.cpp
+++ b/indra/newview/llfilepicker.cpp
@@ -790,7 +790,7 @@ void set_nav_save_data(LLFilePicker::ESaveFilter filter, std::string &extension,
case LLFilePicker::FFSAVE_GLTF:
type = "\?\?\?\?";
creator = "\?\?\?\?";
- extension = "glb";
+ extension = "glb,gltf";
break;
case LLFilePicker::FFSAVE_XML:
diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp
index a0324ca82a..486b652f07 100644
--- a/indra/newview/llstartup.cpp
+++ b/indra/newview/llstartup.cpp
@@ -334,10 +334,18 @@ void update_texture_fetch()
void set_flags_and_update_appearance()
{
- LLAppearanceMgr::instance().setAttachmentInvLinkEnable(true);
- LLAppearanceMgr::instance().updateAppearanceFromCOF(true, true, no_op);
+ // this may be called from a coroutine but has many side effects
+ // in non-thread-safe classes, post to main loop
+ auto work = []()
+ {
+ LLAppearanceMgr::instance().setAttachmentInvLinkEnable(true);
+ LLAppearanceMgr::instance().updateAppearanceFromCOF(true, true, no_op);
+
+ LLInventoryModelBackgroundFetch::instance().start();
+ };
+
+ LLAppViewer::instance()->postToMainCoro(work);
- LLInventoryModelBackgroundFetch::instance().start();
}
// Returns false to skip other idle processing. Should only return
diff --git a/indra/newview/lltinygltfhelper.cpp b/indra/newview/lltinygltfhelper.cpp
index 4e41f9959a..f71c9dd14a 100644
--- a/indra/newview/lltinygltfhelper.cpp
+++ b/indra/newview/lltinygltfhelper.cpp
@@ -248,6 +248,43 @@ bool LLTinyGLTFHelper::loadModel(const std::string& filename, tinygltf::Model& m
return false;
}
+bool LLTinyGLTFHelper::saveModel(const std::string& filename, tinygltf::Model& model_in)
+{
+ std::string exten = gDirUtilp->getExtension(filename);
+
+ bool success = false;
+
+ if (exten == "gltf" || exten == "glb")
+ {
+ tinygltf::TinyGLTF writer;
+
+ std::string filename_lc = filename;
+ LLStringUtil::toLower(filename_lc);
+
+
+ bool embed_images = false;
+ bool embed_buffers = false;
+ bool pretty_print = true;
+ bool write_binary = false;
+
+
+ if (std::string::npos == filename_lc.rfind(".gltf"))
+ { // file is binary
+ embed_images = embed_buffers = write_binary = true;
+ }
+
+ success = writer.WriteGltfSceneToFile(&model_in, filename, embed_images, embed_buffers, pretty_print, write_binary);
+
+ if (!success)
+ {
+ LL_WARNS("GLTF") << "Failed to save" << LL_ENDL;
+ return false;
+ }
+ }
+
+ return success;
+}
+
bool LLTinyGLTFHelper::getMaterialFromModel(
const std::string& filename,
const tinygltf::Model& model_in,
diff --git a/indra/newview/lltinygltfhelper.h b/indra/newview/lltinygltfhelper.h
index da505b41e9..a259609404 100644
--- a/indra/newview/lltinygltfhelper.h
+++ b/indra/newview/lltinygltfhelper.h
@@ -42,6 +42,7 @@ namespace LLTinyGLTFHelper
LLImageRaw* getTexture(const std::string& folder, const tinygltf::Model& model, S32 texture_index, bool flip = true);
bool loadModel(const std::string& filename, tinygltf::Model& model_out);
+ bool saveModel(const std::string& filename, tinygltf::Model& model_in);
bool getMaterialFromModel(
const std::string& filename,
diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp
index 02108e861a..85891744ef 100644
--- a/indra/newview/llviewermedia.cpp
+++ b/indra/newview/llviewermedia.cpp
@@ -1667,7 +1667,7 @@ void LLViewerMediaImpl::destroyMediaSource()
cancelMimeTypeProbe();
{
- LLMutexLock lock(&mLock); // Delay tear-down while bg thread is updating
+ LLCoros::LockType lock(mLock); // Delay tear-down while bg thread is updating
if(mMediaSource)
{
mMediaSource->setDeleteOK(true) ;
@@ -2968,7 +2968,7 @@ bool LLViewerMediaImpl::preMediaTexUpdate(LLViewerMediaTexture*& media_tex, U8*&
void LLViewerMediaImpl::doMediaTexUpdate(LLViewerMediaTexture* media_tex, U8* data, S32 data_width, S32 data_height, S32 x_pos, S32 y_pos, S32 width, S32 height, bool sync)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_MEDIA;
- LLMutexLock lock(&mLock); // don't allow media source tear-down during update
+ LLCoros::LockType lock(mLock); // don't allow media source tear-down during update
// wrap "data" in an LLImageRaw but do NOT make a copy
LLPointer<LLImageRaw> raw = new LLImageRaw(data, media_tex->getWidth(), media_tex->getHeight(), media_tex->getComponents(), true);
diff --git a/indra/newview/llviewermedia.h b/indra/newview/llviewermedia.h
index 03899b6b8f..378297bf91 100644
--- a/indra/newview/llviewermedia.h
+++ b/indra/newview/llviewermedia.h
@@ -182,7 +182,7 @@ private:
// Implementation functions not exported into header file
class LLViewerMediaImpl
- : public LLMouseHandler, public LLRefCount, public LLPluginClassMediaOwner, public LLViewerMediaEventEmitter, public LLEditMenuHandler
+ : public LLMouseHandler, public LLThreadSafeRefCount, public LLPluginClassMediaOwner, public LLViewerMediaEventEmitter, public LLEditMenuHandler
{
LOG_CLASS(LLViewerMediaImpl);
public:
@@ -432,7 +432,7 @@ private:
private:
// a single media url with some data and an impl.
std::shared_ptr<LLPluginClassMedia> mMediaSource;
- LLMutex mLock;
+ LLCoros::Mutex mLock;
F64 mZoomFactor;
LLUUID mTextureId;
bool mMovieImageHasMips;
diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index c1a8c5df9e..631e1b57d9 100644
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -7980,7 +7980,7 @@ class LLAdvancedClickHDRIPreview: public view_listener_t
};
-class LLAdvancedClickGLTFScenePreview : public view_listener_t
+class LLAdvancedClickGLTFOpen: public view_listener_t
{
bool handleEvent(const LLSD& userdata)
{
@@ -7990,6 +7990,26 @@ class LLAdvancedClickGLTFScenePreview : public view_listener_t
}
};
+class LLAdvancedClickGLTFSaveAs : 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().saveAs();
+ return true;
+ }
+};
+
+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
{
@@ -9637,7 +9657,9 @@ void initialize_menus()
view_listener_t::addMenu(new LLAdvancedClickRenderProfile(), "Advanced.ClickRenderProfile");
view_listener_t::addMenu(new LLAdvancedClickRenderBenchmark(), "Advanced.ClickRenderBenchmark");
view_listener_t::addMenu(new LLAdvancedClickHDRIPreview(), "Advanced.ClickHDRIPreview");
- view_listener_t::addMenu(new LLAdvancedClickGLTFScenePreview(), "Advanced.ClickGLTFScenePreview");
+ 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/llviewerobject.cpp b/indra/newview/llviewerobject.cpp
index b66d4b1dab..cc6f4403dd 100644
--- a/indra/newview/llviewerobject.cpp
+++ b/indra/newview/llviewerobject.cpp
@@ -107,6 +107,7 @@
#include "llmeshrepository.h"
#include "llgltfmateriallist.h"
#include "llgl.h"
+#include "gltf/asset.h"
//#define DEBUG_UPDATE_TYPE
@@ -4379,7 +4380,7 @@ LLMatrix4a LLViewerObject::getGLTFAssetToAgentTransform() const
LLMatrix4 root;
root.initScale(getScale());
root.rotate(getRenderRotation());
- root.translate(getPositionAgent());
+ root.translate(getRenderPosition());
LLMatrix4a mat;
mat.loadu((F32*)root.mMatrix);
@@ -4403,7 +4404,7 @@ LLMatrix4a LLViewerObject::getAgentToGLTFAssetTransform() const
scale.mV[1] = 1.f / scale.mV[1];
scale.mV[2] = 1.f / scale.mV[2];
- root.translate(-getPositionAgent());
+ root.translate(-getRenderPosition());
root.rotate(~getRenderRotation());
LLMatrix4 scale_mat;
@@ -4420,7 +4421,7 @@ LLMatrix4a LLViewerObject::getGLTFNodeTransformAgent(S32 node_index) const
{
LLMatrix4a mat;
- if (mGLTFAsset.notNull() && node_index >= 0 && node_index < mGLTFAsset->mNodes.size())
+ if (mGLTFAsset && node_index >= 0 && node_index < mGLTFAsset->mNodes.size())
{
auto& node = mGLTFAsset->mNodes[node_index];
@@ -4474,7 +4475,7 @@ void decomposeMatrix(const LLMatrix4a& mat, LLVector3& position, LLQuaternion& r
void LLViewerObject::setGLTFNodeRotationAgent(S32 node_index, const LLQuaternion& rotation)
{
- if (mGLTFAsset.notNull() && node_index >= 0 && node_index < mGLTFAsset->mNodes.size())
+ if (mGLTFAsset && node_index >= 0 && node_index < mGLTFAsset->mNodes.size())
{
auto& node = mGLTFAsset->mNodes[node_index];
@@ -4506,7 +4507,7 @@ void LLViewerObject::setGLTFNodeRotationAgent(S32 node_index, const LLQuaternion
void LLViewerObject::moveGLTFNode(S32 node_index, const LLVector3& offset)
{
- if (mGLTFAsset.notNull() && node_index >= 0 && node_index < mGLTFAsset->mNodes.size())
+ if (mGLTFAsset && node_index >= 0 && node_index < mGLTFAsset->mNodes.size())
{
auto& node = mGLTFAsset->mNodes[node_index];
diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h
index 64c1ee6633..e314992532 100644
--- a/indra/newview/llviewerobject.h
+++ b/indra/newview/llviewerobject.h
@@ -45,7 +45,14 @@
#include "llbbox.h"
#include "llrigginginfo.h"
#include "llreflectionmap.h"
-#include "gltf/asset.h"
+
+namespace LL
+{
+ namespace GLTF
+ {
+ class Asset;
+ }
+}
class LLAgent; // TODO: Get rid of this.
class LLAudioSource;
@@ -736,7 +743,7 @@ public:
F32 mPhysicsRestitution;
// Associated GLTF Asset
- LLPointer<LL::GLTF::Asset> mGLTFAsset;
+ std::shared_ptr<LL::GLTF::Asset> mGLTFAsset;
// Pipeline classes
LLPointer<LLDrawable> mDrawable;
diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp
index 29dce088f5..210d5f84bd 100755
--- a/indra/newview/llviewerregion.cpp
+++ b/indra/newview/llviewerregion.cpp
@@ -2498,11 +2498,8 @@ void LLViewerRegion::setSimulatorFeatures(const LLSD& sim_features)
}
};
- auto workqueue = LL::WorkQueue::getInstance("mainloop");
- if (workqueue)
- {
- LL::WorkQueue::postMaybe(workqueue, work);
- }
+
+ LLAppViewer::instance()->postToMainCoro(work);
}
//this is called when the parent is not cacheable.
diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp
index 310c9ee297..9a0aae944c 100644
--- a/indra/newview/llvoicevivox.cpp
+++ b/indra/newview/llvoicevivox.cpp
@@ -6437,37 +6437,43 @@ void LLVivoxVoiceClient::notifyStatusObservers(LLVoiceClientStatusObserver::ESta
}
}
}
-
+
LL_DEBUGS("Voice")
<< " " << LLVoiceClientStatusObserver::status2string(status)
<< ", session URI " << getAudioSessionURI()
<< ", proximal is " << inSpatialChannel()
<< LL_ENDL;
- for (status_observer_set_t::iterator it = mStatusObservers.begin();
- it != mStatusObservers.end();
- )
- {
- LLVoiceClientStatusObserver* observer = *it;
- observer->onChange(status, getAudioSessionURI(), inSpatialChannel());
- // In case onError() deleted an entry.
- it = mStatusObservers.upper_bound(observer);
- }
+ // this function is called from a coroutine, shuttle application hook back to main loop
+ auto work = [=]()
+ {
+ for (status_observer_set_t::iterator it = mStatusObservers.begin();
+ it != mStatusObservers.end();
+ )
+ {
+ LLVoiceClientStatusObserver* observer = *it;
+ observer->onChange(status, getAudioSessionURI(), inSpatialChannel());
+ // In case onError() deleted an entry.
+ it = mStatusObservers.upper_bound(observer);
+ }
- // skipped to avoid speak button blinking
- if ( status != LLVoiceClientStatusObserver::STATUS_JOINING
- && status != LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL
- && status != LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED)
- {
- bool voice_status = LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking();
+ // skipped to avoid speak button blinking
+ if (status != LLVoiceClientStatusObserver::STATUS_JOINING
+ && status != LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL
+ && status != LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED)
+ {
+ bool voice_status = LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking();
- gAgent.setVoiceConnected(voice_status);
+ gAgent.setVoiceConnected(voice_status);
- if (voice_status)
- {
- LLFirstUse::speak(true);
- }
- }
+ if (voice_status)
+ {
+ LLFirstUse::speak(true);
+ }
+ }
+ };
+
+ LLAppViewer::instance()->postToMainCoro(work);
}
void LLVivoxVoiceClient::addObserver(LLFriendObserver* observer)
diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml
index 644553e64c..6238efe688 100644
--- a/indra/newview/skins/default/xui/en/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/en/menu_viewer.xml
@@ -2844,6 +2844,30 @@ function="World.EnvPreset"
parameter="mem_leaking" />
</menu_item_call>
</menu>
+ <menu
+ create_jump_keys="true"
+ label="GLTF"
+ name="GLTF"
+ tear_off="true">
+ <menu_item_call
+ label="Open..."
+ name="Open...">
+ <menu_item_call.on_click
+ function="Advanced.ClickGLTFOpen" />
+ </menu_item_call>
+ <menu_item_call
+ label="Save As..."
+ name="Save As...">
+ <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"
label="Render Tests"
@@ -2896,12 +2920,6 @@ function="World.EnvPreset"
<menu_item_call.on_click
function="Advanced.ClickHDRIPreview" />
</menu_item_call>
- <menu_item_call
- label="GLTF Scene Preview"
- name="GLTF Scene Preview">
- <menu_item_call.on_click
- function="Advanced.ClickGLTFScenePreview" />
- </menu_item_call>
</menu>
<menu
create_jump_keys="true"
diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml
index ad1f12002e..7d7ea986dd 100644
--- a/indra/newview/skins/default/xui/en/notifications.xml
+++ b/indra/newview/skins/default/xui/en/notifications.xml
@@ -12408,7 +12408,7 @@ are wearing now.
<notification
icon="alertmodal.tga"
- name="GLTFPreviewSelection"
+ name="GLTFOpenSelection"
type="alert">
You must select an object to act as a handle to the GLTF asset you are previewing.
<tag>fail</tag>
@@ -12417,4 +12417,15 @@ are wearing now.
yestext="OK"/>
</notification>
+ <notification
+ icon="alertmodal.tga"
+ name="GLTFSaveSelection"
+ type="alert">
+ You must select an object that has a GLTF asset associated with it.
+ <tag>fail</tag>
+ <usetemplate
+ name="okbutton"
+ yestext="OK"/>
+ </notification>
+
</notifications>