Merge branch 'project/gltf_development' of https://github.com/secondlife/viewer

# Conflicts:
#	indra/cmake/Copy3rdPartyLibs.cmake
#	indra/llappearance/llavatarappearance.cpp
#	indra/llrender/llvertexbuffer.cpp
#	indra/newview/llpanelface.cpp
#	indra/newview/llviewermenu.cpp
#	indra/newview/llviewertexturelist.cpp
#	indra/newview/llvocache.cpp
master
Ansariel 2024-06-11 14:38:20 +02:00
commit 1bbe697db7
106 changed files with 4847 additions and 2544 deletions

View File

@ -3240,11 +3240,11 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
<key>archive</key>
<map>
<key>hash</key>
<string>9e0092c6a3aed1cb40a9e26df689c42c68142c9d</string>
<string>8278a2368136cb12319ca00e7aceb2829bf3ebd8</string>
<key>hash_algorithm</key>
<string>sha1</string>
<key>url</key>
<string>https://github.com/secondlife/3p-tinyexr/releases/download/v1.0.8-r1/tinyexr-v1.0.8-common-8755737750.tar.zst</string>
<string>https://github.com/secondlife/3p-tinyexr/releases/download/v1.0.8-ba4bc64/tinyexr-v1.0.8-common-9373975608.tar.zst</string>
</map>
<key>name</key>
<string>common</string>

View File

@ -248,7 +248,7 @@ Ansariel Hiller
SL-4126
SL-20224
SL-20524
https://github.com/secondlife/viewer/issues/1051
secondlife/viewer#1051
Aralara Rajal
Arare Chantilly
CHUIBUG-191

View File

@ -124,7 +124,6 @@ if(WINDOWS)
MESSAGE(WARNING "New MSVC_VERSION ${MSVC_VERSION} of MSVC: adapt Copy3rdPartyLibs.cmake")
endif (MSVC80)
# <FS:Ansariel> Try using the VC runtime redistributables that came with the VS installation first
if (MSVC_TOOLSET_VER AND DEFINED ENV{VCTOOLSREDISTDIR})
if(ADDRESS_SIZE EQUAL 32)
set(redist_find_path "$ENV{VCTOOLSREDISTDIR}x86\\Microsoft.VC${MSVC_TOOLSET_VER}.CRT")
@ -134,7 +133,6 @@ if(WINDOWS)
get_filename_component(redist_path "${redist_find_path}" ABSOLUTE)
MESSAGE(STATUS "VC Runtime redist path: ${redist_path}")
endif (MSVC_TOOLSET_VER AND DEFINED ENV{VCTOOLSREDISTDIR})
# </FS:Ansariel>
if(ADDRESS_SIZE EQUAL 32)
# this folder contains the 32bit DLLs.. (yes really!)
@ -158,14 +156,12 @@ if(WINDOWS)
vcruntime${MSVC_VER}.dll
vcruntime${MSVC_VER}_1.dll
)
# <FS:Ansariel> Try using the VC runtime redistributables that came with the VS installation first
if(redist_path AND EXISTS "${redist_path}/${release_msvc_file}")
MESSAGE(STATUS "Copying redist file from ${redist_path}/${release_msvc_file}")
to_staging_dirs(
${redist_path}
third_party_targets
${release_msvc_file})
# </FS:Ansariel>
elseif(EXISTS "${registry_path}/${release_msvc_file}")
MESSAGE(STATUS "Copying redist file from ${registry_path}/${release_msvc_file}")
to_staging_dirs(

View File

@ -496,21 +496,69 @@ void LLAvatarAppearance::computeBodySize()
mCurrBodySizeState["mAnkleLeft scale"] = mAnkleLeftp->getScale();
mCurrBodySizeState["mFootLeft pos"] = mFootLeftp->getPosition();
F32 old_height = mBodySize.mV[VZ];
LLVector3 pelvis_scale = mPelvisp->getScale();
// some of the joints have not been cached
LLVector3 skull = mSkullp->getPosition();
//LLVector3 skull_scale = mSkullp->getScale();
LLVector3 neck = mNeckp->getPosition();
LLVector3 neck_scale = mNeckp->getScale();
LLVector3 chest = mChestp->getPosition();
LLVector3 chest_scale = mChestp->getScale();
// the rest of the joints have been cached
LLVector3 head = mHeadp->getPosition();
LLVector3 head_scale = mHeadp->getScale();
LLVector3 torso = mTorsop->getPosition();
LLVector3 torso_scale = mTorsop->getScale();
LLVector3 hip = mHipLeftp->getPosition();
LLVector3 hip_scale = mHipLeftp->getScale();
LLVector3 knee = mKneeLeftp->getPosition();
LLVector3 knee_scale = mKneeLeftp->getScale();
LLVector3 ankle = mAnkleLeftp->getPosition();
LLVector3 ankle_scale = mAnkleLeftp->getScale();
LLVector3 foot = mFootLeftp->getPosition();
F32 old_offset = mAvatarOffset.mV[VZ];
// TODO: Measure the real depth and width
mPelvisToFoot = computePelvisToFoot();
F32 new_height = computeBodyHeight();
mBodySize.set(DEFAULT_AGENT_DEPTH, DEFAULT_AGENT_WIDTH, new_height);
// [RLVa:KB] - Checked: 2013-03-03 (RLVa-1.4.8)
F32 new_offset = getAvatarOffset();
mAvatarOffset.mV[VZ] = getAvatarOffset();
// [/RLVa:KB]
// F32 new_offset = getVisualParamWeight(AVATAR_HOVER);
mAvatarOffset.set(0, 0, new_offset);
// mAvatarOffset.mV[VZ] = getVisualParamWeight(AVATAR_HOVER);
if (mBodySize.mV[VZ] != old_height || new_offset != old_offset)
mPelvisToFoot = hip.mV[VZ] * pelvis_scale.mV[VZ] -
knee.mV[VZ] * hip_scale.mV[VZ] -
ankle.mV[VZ] * knee_scale.mV[VZ] -
foot.mV[VZ] * ankle_scale.mV[VZ];
LLVector3 new_body_size;
new_body_size.mV[VZ] = mPelvisToFoot +
// the sqrt(2) correction below is an approximate
// correction to get to the top of the head
F_SQRT2 * (skull.mV[VZ] * head_scale.mV[VZ]) +
head.mV[VZ] * neck_scale.mV[VZ] +
neck.mV[VZ] * chest_scale.mV[VZ] +
chest.mV[VZ] * torso_scale.mV[VZ] +
torso.mV[VZ] * pelvis_scale.mV[VZ];
// TODO -- measure the real depth and width
new_body_size.mV[VX] = DEFAULT_AGENT_DEPTH;
new_body_size.mV[VY] = DEFAULT_AGENT_WIDTH;
mAvatarOffset.mV[VX] = 0.0f;
mAvatarOffset.mV[VY] = 0.0f;
if (new_body_size != mBodySize || old_offset != mAvatarOffset.mV[VZ])
{
mBodySize = new_body_size;
// <FS:Ansariel> [Legacy Bake]
bodySizeChanged();
@ -518,29 +566,6 @@ void LLAvatarAppearance::computeBodySize()
}
}
F32 LLAvatarAppearance::computeBodyHeight()
{
F32 result = mPelvisToFoot +
// all these relative positions usually are positive
mPelvisp->getScale().mV[VZ] * mTorsop->getPosition().mV[VZ] +
mTorsop->getScale().mV[VZ] * mChestp->getPosition().mV[VZ] +
mChestp->getScale().mV[VZ] * mNeckp->getPosition().mV[VZ] +
mNeckp->getScale().mV[VZ] * mHeadp->getPosition().mV[VZ] +
mHeadp->getScale().mV[VZ] * mSkullp->getPosition().mV[VZ] * 2;
return result;
}
F32 LLAvatarAppearance::computePelvisToFoot()
{
F32 result =
// all these relative positions usually are negative
mPelvisp->getScale().mV[VZ] * mHipLeftp->getPosition().mV[VZ] +
mHipLeftp->getScale().mV[VZ] * mKneeLeftp->getPosition().mV[VZ] +
mKneeLeftp->getScale().mV[VZ] * mAnkleLeftp->getPosition().mV[VZ] +
mAnkleLeftp->getScale().mV[VZ] * mFootLeftp->getPosition().mV[VZ] / 2;
return -result;
}
// [RLVa:KB] - Checked: 2013-03-03 (RLVa-1.4.8)
F32 LLAvatarAppearance::getAvatarOffset() /*const*/
{

View File

@ -153,8 +153,6 @@ public:
void compareJointStateMaps(joint_state_map_t& last_state,
joint_state_map_t& curr_state);
void computeBodySize();
F32 computeBodyHeight();
F32 computePelvisToFoot();
public:
typedef std::vector<LLAvatarJoint*> avatar_joint_list_t;

View File

@ -592,9 +592,10 @@ void default_unix_signal_handler(int signum, siginfo_t *info, void *)
switch (signum)
{
case SIGCHLD:
case SIGHUP:
if (LLApp::sLogInSignal)
{
LL_INFOS() << "Signal handler - Got SIGCHLD from " << info->si_pid << LL_ENDL;
LL_INFOS() << "Signal handler - Got SIGCHLD or SIGHUP from " << info->si_pid << LL_ENDL;
}
return;
@ -609,11 +610,10 @@ void default_unix_signal_handler(int signum, siginfo_t *info, void *)
raise(signum);
return;
case SIGINT:
case SIGHUP:
case SIGTERM:
if (LLApp::sLogInSignal)
{
LL_WARNS() << "Signal handler - Got SIGINT, HUP, or TERM, exiting gracefully" << LL_ENDL;
LL_WARNS() << "Signal handler - Got SIGINT, or TERM, exiting gracefully" << LL_ENDL;
}
// Graceful exit
// Just set our state to quitting, not error

View File

@ -18,9 +18,6 @@
#include <algorithm>
// std headers
// external library headers
#include <boost/bind.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/tokenizer.hpp>
// other Linden headers
#include "llerror.h"
#include "llstring.h"
@ -64,7 +61,9 @@ public:
// Pass it a callback to our connect() method, so it can send events
// from a particular LLEventPump to the plugin without having to know
// this class or method name.
mListener(new LLLeapListener(boost::bind(&LLLeapImpl::connect, this, _1, _2)))
mListener(new LLLeapListener(
[this](LLEventPump& pump, const std::string& listener)
{ return connect(pump, listener); }))
{
// Rule out unpopulated Params block
if (! cparams.executable.isProvided())
@ -93,7 +92,7 @@ public:
}
// Listen for child "termination" right away to catch launch errors.
mDonePump.listen("LLLeap", boost::bind(&LLLeapImpl::bad_launch, this, _1));
mDonePump.listen("LLLeap", [this](const LLSD& data){ return bad_launch(data); });
// Okay, launch child.
// Get a modifiable copy of params block to set files and postend.
@ -113,7 +112,7 @@ public:
// Okay, launch apparently worked. Change our mDonePump listener.
mDonePump.stopListening("LLLeap");
mDonePump.listen("LLLeap", boost::bind(&LLLeapImpl::done, this, _1));
mDonePump.listen("LLLeap", [this](const LLSD& data){ return done(data); });
// Child might pump large volumes of data through either stdout or
// stderr. Don't bother copying all that data into notification event.
@ -128,13 +127,9 @@ public:
// Listening on stdout is stateful. In general, we're either waiting
// for the length prefix or waiting for the specified length of data.
// We address that with two different listener methods -- one of which
// is blocked at any given time.
mReadPrefix = true;
mStdoutConnection = childout.getPump()
.listen("prefix", boost::bind(&LLLeapImpl::rstdout, this, _1));
mStdoutDataConnection = childout.getPump()
.listen("data", boost::bind(&LLLeapImpl::rstdoutData, this, _1));
mBlocker.reset(new LLEventPump::Blocker(mStdoutDataConnection));
.listen("LLLeap", [this](const LLSD& data){ return rstdout(data); });
// Log anything sent up through stderr. When a typical program
// encounters an error, it writes its error message to stderr and
@ -142,7 +137,7 @@ public:
// interpreter behaves that way. More generally, though, a plugin
// author can log whatever s/he wants to the viewer log using stderr.
mStderrConnection = childerr.getPump()
.listen("LLLeap", boost::bind(&LLLeapImpl::rstderr, this, _1));
.listen("LLLeap", [this](const LLSD& data){ return rstderr(data); });
// For our lifespan, intercept any LL_ERRS so we can notify plugin
mRecorder = LLError::addGenericRecorder(
@ -255,120 +250,120 @@ public:
return false;
}
// Initial state of stateful listening on child stdout: wait for a length
// prefix, followed by ':'.
bool rstdout(const LLSD& data)
// Stateful listening on child stdout:
// wait for a length prefix, followed by ':'.
bool rstdout(const LLSD&)
{
LLProcess::ReadPipe& childout(mChild->getReadPipe(LLProcess::STDOUT));
// It's possible we got notified of a couple digit characters without
// seeing the ':' -- unlikely, but still. Until we see ':', keep
// waiting.
if (childout.contains(':'))
while (childout.size())
{
std::istream& childstream(childout.get_istream());
// Saw ':', read length prefix and store in mExpect.
size_t expect;
childstream >> expect;
int colon(childstream.get());
if (colon != ':')
/*----------------- waiting for length prefix ------------------*/
if (mReadPrefix)
{
// Protocol failure. Clear out the rest of the pending data in
// childout (well, up to a max length) to log what was wrong.
LLProcess::ReadPipe::size_type
readlen((std::min)(childout.size(), LLProcess::ReadPipe::size_type(80)));
bad_protocol(STRINGIZE(expect << char(colon) << childout.read(readlen)));
}
else
{
// Saw length prefix, saw colon, life is good. Now wait for
// that length of data to arrive.
mExpect = expect;
LL_DEBUGS("LLLeap") << "got length, waiting for "
<< mExpect << " bytes of data" << LL_ENDL;
// Block calls to this method; resetting mBlocker unblocks
// calls to the other method.
mBlocker.reset(new LLEventPump::Blocker(mStdoutConnection));
// Go check if we've already received all the advertised data.
if (childout.size())
// It's possible we got notified of a couple digit characters without
// seeing the ':' -- unlikely, but still. Until we see ':', keep
// waiting.
if (! childout.contains(':'))
{
LLSD updata(data);
updata["len"] = LLSD::Integer(childout.size());
rstdoutData(updata);
if (childout.contains('\n'))
{
// Since this is the initial listening state, this is where we'd
// arrive if the child isn't following protocol at all -- say
// because the user specified 'ls' or some darn thing.
bad_protocol(childout.getline());
}
// Either way, stop looping.
break;
}
}
}
else if (childout.contains('\n'))
{
// Since this is the initial listening state, this is where we'd
// arrive if the child isn't following protocol at all -- say
// because the user specified 'ls' or some darn thing.
bad_protocol(childout.getline());
}
return false;
}
// State in which we listen on stdout for the specified length of data to
// arrive.
bool rstdoutData(const LLSD& data)
{
LLProcess::ReadPipe& childout(mChild->getReadPipe(LLProcess::STDOUT));
// Until we've accumulated the promised length of data, keep waiting.
if (childout.size() >= mExpect)
{
// Ready to rock and roll.
LL_DEBUGS("LLLeap") << "needed " << mExpect << " bytes, got "
<< childout.size() << ", parsing LLSD" << LL_ENDL;
LLSD data;
#if 1
// specifically require notation LLSD from child
LLPointer<LLSDParser> parser(new LLSDNotationParser());
S32 parse_status(parser->parse(childout.get_istream(), data, mExpect));
if (parse_status == LLSDParser::PARSE_FAILURE)
#else
// SL-18330: accept any valid LLSD serialization format from child
// Unfortunately this runs into trouble we have not yet debugged.
bool parse_status(LLSDSerialize::deserialize(data, childout.get_istream(), mExpect));
if (! parse_status)
#endif
{
bad_protocol("unparseable LLSD data");
}
else if (! (data.isMap() && data["pump"].isString() && data.has("data")))
{
// we got an LLSD object, but it lacks required keys
bad_protocol("missing 'pump' or 'data'");
// Saw ':', read length prefix and store in mExpect.
std::istream& childstream(childout.get_istream());
size_t expect;
childstream >> expect;
int colon(childstream.get());
if (colon != ':')
{
// Protocol failure. Clear out the rest of the pending data in
// childout (well, up to a max length) to log what was wrong.
LLProcess::ReadPipe::size_type
readlen((std::min)(childout.size(),
LLProcess::ReadPipe::size_type(80)));
bad_protocol(stringize(expect, char(colon), childout.read(readlen)));
break;
}
else
{
// Saw length prefix, saw colon, life is good. Now wait for
// that length of data to arrive.
mExpect = expect;
LL_DEBUGS("LLLeap") << "got length, waiting for "
<< mExpect << " bytes of data" << LL_ENDL;
// Transition to "read data" mode and loop back to check
// if we've already received all the advertised data.
mReadPrefix = false;
continue;
}
}
/*----------------- saw prefix, wait for data ------------------*/
else
{
try
// Until we've accumulated the promised length of data, keep waiting.
if (childout.size() < mExpect)
{
// The LLSD object we got from our stream contains the
// keys we need.
LLEventPumps::instance().obtain(data["pump"]).post(data["data"]);
break;
}
catch (const std::exception& err)
// We have the data we were told to expect! Ready to rock and roll.
LL_DEBUGS("LLLeap") << "needed " << mExpect << " bytes, got "
<< childout.size() << ", parsing LLSD" << LL_ENDL;
LLSD data;
#if 1
// specifically require notation LLSD from child
LLPointer<LLSDParser> parser(new LLSDNotationParser());
S32 parse_status(parser->parse(childout.get_istream(), data, mExpect));
if (parse_status == LLSDParser::PARSE_FAILURE)
#else
// SL-18330: accept any valid LLSD serialization format from child
// Unfortunately this runs into trouble we have not yet debugged.
bool parse_status(LLSDSerialize::deserialize(data, childout.get_istream(), mExpect));
if (! parse_status)
#endif
{
// No plugin should be allowed to crash the viewer by
// driving an exception -- intentionally or not.
LOG_UNHANDLED_EXCEPTION(stringize("handling request ", data));
// Whether or not the plugin added a "reply" key to the
// request, send a reply. We happen to know who originated
// this request, and the reply LLEventPump of interest.
// Not our problem if the plugin ignores the reply event.
data["reply"] = mReplyPump.getName();
sendReply(llsd::map("error",
stringize(LLError::Log::classname(err), ": ", err.what())),
data);
bad_protocol("unparseable LLSD data");
break;
}
// Block calls to this method; resetting mBlocker unblocks
// calls to the other method.
mBlocker.reset(new LLEventPump::Blocker(mStdoutDataConnection));
// Go check for any more pending events in the buffer.
if (childout.size())
else if (! (data.isMap() && data["pump"].isString() && data.has("data")))
{
LLSD updata(data);
data["len"] = LLSD::Integer(childout.size());
rstdout(updata);
// we got an LLSD object, but it lacks required keys
bad_protocol("missing 'pump' or 'data'");
break;
}
else
{
try
{
// The LLSD object we got from our stream contains the
// keys we need.
LLEventPumps::instance().obtain(data["pump"]).post(data["data"]);
}
catch (const std::exception& err)
{
// No plugin should be allowed to crash the viewer by
// driving an exception -- intentionally or not.
LOG_UNHANDLED_EXCEPTION(stringize("handling request ", data));
// Whether or not the plugin added a "reply" key to the
// request, send a reply. We happen to know who originated
// this request, and the reply LLEventPump of interest.
// Not our problem if the plugin ignores the reply event.
data["reply"] = mReplyPump.getName();
sendReply(llsd::map("error",
stringize(LLError::Log::classname(err), ": ", err.what())),
data);
}
// Transition to "read prefix" mode and go check for any
// more pending events in the buffer.
mReadPrefix = true;
continue;
}
}
}
@ -453,7 +448,8 @@ private:
// child's stdin, suitably enriched with the pump name on which it was
// received.
return pump.listen(listener,
boost::bind(&LLLeapImpl::wstdin, this, pump.getName(), _1));
[this, name=pump.getName()](const LLSD& data)
{ return wstdin(name, data); });
}
std::string mDesc;
@ -461,11 +457,11 @@ private:
LLEventStream mReplyPump;
LLProcessPtr mChild;
LLTempBoundListener
mStdinConnection, mStdoutConnection, mStdoutDataConnection, mStderrConnection;
std::unique_ptr<LLEventPump::Blocker> mBlocker;
mStdinConnection, mStdoutConnection, mStderrConnection;
LLProcess::ReadPipe::size_type mExpect;
LLError::RecorderPtr mRecorder;
std::unique_ptr<LLLeapListener> mListener;
bool mReadPrefix;
};
// These must follow the declaration of LLLeapImpl, so they may as well be last.

View File

@ -237,7 +237,7 @@ namespace LLProfiler
#define LL_LABEL_OBJECT_GL(type, name, length, label)
#if LL_PROFILER_CONFIGURATION > 1
#if !LL_DARWIN && LL_PROFILER_CONFIGURATION > 1
#define LL_PROFILE_ALLOC(ptr, size) TracyAlloc(ptr, size)
#define LL_PROFILE_FREE(ptr) TracyFree(ptr)
#else

View File

@ -830,7 +830,7 @@ std::string utf8str_showBytesUTF8(const std::string& utf8str)
}
// Search for any emoji symbol, return true if found
bool wstring_has_emoji(const LLWString& wstr)
bool wstring_has_emoji(LLWStringView wstr)
{
for (const llwchar& wch : wstr)
{

View File

@ -456,6 +456,7 @@ template<class T> std::string LLStringUtilBase<T>::sLocale;
typedef LLStringUtilBase<char> LLStringUtil;
typedef LLStringUtilBase<llwchar> LLWStringUtil;
typedef std::basic_string<llwchar> LLWString;
typedef std::basic_string_view<llwchar> LLWStringView;
//@ Use this where we want to disallow input in the form of "foo"
// This is used to catch places where english text is embedded in the code
@ -762,7 +763,7 @@ LL_COMMON_API llwchar utf8str_to_wchar(const std::string& utf8str, size_t offset
LL_COMMON_API std::string utf8str_showBytesUTF8(const std::string& utf8str);
LL_COMMON_API bool wstring_has_emoji(const LLWString& wstr);
LL_COMMON_API bool wstring_has_emoji(LLWStringView wstr);
LL_COMMON_API bool wstring_remove_emojis(LLWString& wstr);

View File

@ -37,8 +37,8 @@ class LLMutex;
const S32 UUID_BYTES = 16;
const S32 UUID_WORDS = 4;
const S32 UUID_STR_LENGTH = 37; // actually wrong, should be 36 and use size below
const S32 UUID_STR_SIZE = 37;
const S32 UUID_STR_LENGTH = 37; // number of bytes needed to store a UUID as a string
const S32 UUID_STR_SIZE = 36; // .size() of a UUID in a std::string
const S32 UUID_BASE85_LENGTH = 21; // including the trailing NULL.
struct uuid_time_t {

View File

@ -47,11 +47,11 @@ namespace tut
// the timestamp for each one -- but since we're passing explicit
// timestamps, make the queue reorder them.
auto now{ Queue::Clock::now() };
queue.push(Queue::TimeTuple(now + 200ms, "ghi"));
queue.push(Queue::TimeTuple(now + 200ms, "ghi"s));
// Given the various push() overloads, you have to match the type
// exactly: conversions are ambiguous.
queue.push("abc"s);
queue.push(now + 100ms, "def");
queue.push(now, "abc"s);
queue.push(now + 100ms, "def"s);
queue.close();
auto entry = queue.pop();
ensure_equals("failed to pop first", std::get<0>(entry), "abc"s);

View File

@ -2274,6 +2274,61 @@ LLImageFormatted* LLImageFormatted::createFromType(S8 codec)
return image;
}
// static
S8 LLImageFormatted::getCodecFromMimeType(std::string_view mimetype)
{
if (mimetype == "image/bmp")
{
return IMG_CODEC_BMP;
}
else if (mimetype == "image/tga")
{
return IMG_CODEC_TGA;
}
else if (mimetype == "image/jpeg")
{
return IMG_CODEC_JPEG;
}
else if (mimetype == "image/png")
{
return IMG_CODEC_PNG;
}
else if (mimetype == "image/j2c")
{
return IMG_CODEC_J2C;
}
else if (mimetype == "image/dxt")
{
return IMG_CODEC_DXT;
}
return IMG_CODEC_INVALID;
}
// static
LLImageFormatted* LLImageFormatted::createFromMimeType(std::string_view mimetype)
{
S8 codec = getCodecFromMimeType(mimetype);
return createFromType(codec);
}
// static
LLImageFormatted* LLImageFormatted::loadFromMemory(const U8* data_in, U32 size, std::string_view mimetype)
{
LLImageFormatted* image = createFromMimeType(mimetype);
if (image)
{
U8* data = image->allocateData(size);
memcpy(data, data_in, size);
if (!image->updateData())
{
delete image;
image = NULL;
}
}
return image;
}
// static
LLImageFormatted* LLImageFormatted::createFromExtension(const std::string& instring)
{
@ -2457,6 +2512,7 @@ void LLImageFormatted::appendData(U8 *data, S32 size)
//----------------------------------------------------------------------------
bool LLImageFormatted::load(const std::string &filename, int load_size)
{
resetLastError();

View File

@ -332,7 +332,10 @@ class LLImageFormatted : public LLImageBase
{
public:
static LLImageFormatted* createFromType(S8 codec);
static LLImageFormatted* loadFromMemory(const U8* data, U32 size, std::string_view mimetype);
static LLImageFormatted* createFromExtension(const std::string& instring);
static LLImageFormatted* createFromMimeType(std::string_view mimetype);
static S8 getCodecFromMimeType(std::string_view mimetype);
protected:
/*virtual*/ ~LLImageFormatted();

View File

@ -270,36 +270,23 @@ public:
inline bool allowModifyBy(const LLUUID &agent_id) const;
inline bool allowCopyBy(const LLUUID& agent_id) const;
inline bool allowMoveBy(const LLUUID& agent_id) const;
inline bool allowModifyBy(const LLUUID &agent_id, const LLUUID& group) const;
inline bool allowCopyBy(const LLUUID& agent_id, const LLUUID& group) const;
inline bool allowMoveBy(const LLUUID &agent_id, const LLUUID &group) const;
inline bool allowExportBy(const LLUUID& agent_id) const; // <FS:CR> OpenSim export permission
// This somewhat specialized function is meant for testing if the
// current owner is allowed to transfer to the specified agent id.
inline bool allowTransferTo(const LLUUID &agent_id) const;
//
// DEPRECATED.
//
// These return true if the given agent can perform the function.
// They also return true if the object isn't owned, or the
// requesting agent is a system agent. See llpermissionsflags.h
// for bits.
//bool allowDeleteBy(const LLUUID& agent_id) const { return allowModifyBy(agent_id); }
//bool allowEditBy(const LLUUID& agent_id) const { return allowModifyBy(agent_id); }
// saves last owner and sets current owner
//bool setOwner(const LLUUID& agent, const LLUUID& owner);
// This method saves the last owner, sets the current owner to the
// one provided, and sets the base mask as indicated.
//bool setOwner(const LLUUID& agent, const LLUUID& owner, U32 new_base_mask);
// Attempt to set or clear the given bitmask. Returns true if you
// are allowed to modify the permissions. If you attempt to turn
// on bits not allowed by the base bits, the function will return
// true, but those bits will not be set.
//bool setGroupBits( const LLUUID& agent, bool set, PermissionMask bits);
//bool setEveryoneBits(const LLUUID& agent, bool set, PermissionMask bits);
// Returns true if the object can exported by the given agent
// (e.g. saved as a local .gltf file)
// The current test should return true if the agent is the owner
// AND the creator of the object.
inline bool allowExportBy(const LLUUID& agent_id) const;
#ifdef OPENSIM
inline bool allowOpenSimExportBy(const LLUUID& agent_id) const; // <FS:CR> OpenSim export permission
#endif
//
// MISC METHODS and OPERATORS
@ -354,6 +341,20 @@ bool LLPermissions::allowMoveBy(const LLUUID& agent) const
return allowOperationBy(PERM_MOVE, agent, LLUUID::null);
}
bool LLPermissions::allowExportBy(const LLUUID& agent) const
{
return agent == mOwner && agent == mCreator;
}
// <FS:CR> Opensim Export Permissions
#ifdef OPENSIM
bool LLPermissions::allowOpenSimExportBy(const LLUUID& agent) const
{
return ((mCreator == agent) ? true : (allowOperationBy(PERM_EXPORT, agent, LLUUID::null)));
}
#endif
// </FS:CR>
bool LLPermissions::allowTransferTo(const LLUUID &agent_id) const
{
if (mIsGroupOwned)
@ -366,13 +367,6 @@ bool LLPermissions::allowTransferTo(const LLUUID &agent_id) const
}
}
// <FS:CR> Opensim Export Permissions
bool LLPermissions::allowExportBy(const LLUUID& agent) const
{
return ((mCreator == agent) ? true : (allowOperationBy(PERM_EXPORT, agent, LLUUID::null)));
}
// </FS:CR>
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Class LLAggregatePermissions
//

View File

@ -127,6 +127,39 @@ public:
return !(*this == rhs);
}
const LLVector4a& operator+=(const LLVector4a& rhs)
{
add(rhs);
return *this;
}
const LLVector4a& operator-=(const LLVector4a& rhs)
{
sub(rhs);
return *this;
}
LLVector4a operator+(const LLVector4a& rhs) const
{
LLVector4a result = *this;
result.add(rhs);
return result;
}
LLVector4a operator-(const LLVector4a& rhs) const
{
LLVector4a result = *this;
result.sub(rhs);
return result;
}
LLVector4a cross3(const LLVector4a& b) const
{
LLVector4a result;
result.setCross3(*this, b);
return result;
}
////////////////////////////////////
// LOAD/STORE
////////////////////////////////////

View File

@ -151,7 +151,7 @@ void LLOctreeTriangleRayIntersect::visit(const LLOctreeNode<LLVolumeTriangle, LL
U32 idx1 = tri->mIndex[1];
U32 idx2 = tri->mIndex[2];
if (mTexCoord != NULL)
if (mTexCoord != NULL && mFace->mTexCoords)
{
LLVector2* tc = (LLVector2*) mFace->mTexCoords;
*mTexCoord = ((1.f - a - b) * tc[idx0] +
@ -160,7 +160,7 @@ void LLOctreeTriangleRayIntersect::visit(const LLOctreeNode<LLVolumeTriangle, LL
}
if (mNormal != NULL)
if (mNormal != NULL && mFace->mNormals)
{
LLVector4a* norm = mFace->mNormals;
@ -180,7 +180,7 @@ void LLOctreeTriangleRayIntersect::visit(const LLOctreeNode<LLVolumeTriangle, LL
*mNormal = n1;
}
if (mTangent != NULL)
if (mTangent != NULL && mFace->mTangents)
{
LLVector4a* tangents = mFace->mTangents;

View File

@ -681,7 +681,7 @@ void LLGLTFMaterial::applyOverride(const LLGLTFMaterial& override_mat)
}
}
void LLGLTFMaterial::getOverrideLLSD(const LLGLTFMaterial& override_mat, LLSD& data)
void LLGLTFMaterial::getOverrideLLSD(const LLGLTFMaterial& override_mat, LLSD& data) const
{
LL_PROFILE_ZONE_SCOPED;
llassert(data.isUndefined());
@ -690,7 +690,7 @@ void LLGLTFMaterial::getOverrideLLSD(const LLGLTFMaterial& override_mat, LLSD& d
for (U32 i = 0; i < GLTF_TEXTURE_INFO_COUNT; ++i)
{
LLUUID& texture_id = mTextureId[i];
const LLUUID& texture_id = mTextureId[i];
const LLUUID& override_texture_id = override_mat.mTextureId[i];
if (override_texture_id.notNull() && override_texture_id != texture_id)
{

View File

@ -202,7 +202,7 @@ public:
// Get the given override on this LLGLTFMaterial as LLSD
// override_mat -- the override source data
// data -- output LLSD object (should be passed in empty)
void getOverrideLLSD(const LLGLTFMaterial& override_mat, LLSD& data);
void getOverrideLLSD(const LLGLTFMaterial& override_mat, LLSD& data) const;
// For base materials only (i.e. assets). Clears transforms to
// default since they're not supported in assets yet.

View File

@ -1274,6 +1274,11 @@ bool LLGLManager::initGL()
glGetIntegerv(GL_MAX_INTEGER_SAMPLES, &mMaxIntegerSamples);
glGetIntegerv(GL_MAX_SAMPLE_MASK_WORDS, &mMaxSampleMaskWords);
glGetIntegerv(GL_MAX_SAMPLES, &mMaxSamples);
glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &mMaxUniformBlockSize);
// sanity clamp max uniform block size to 64k just in case
// there's some implementation that reports a crazy value
mMaxUniformBlockSize = llmin(mMaxUniformBlockSize, 65536);
if (mGLVersion >= 4.59f)
{

View File

@ -99,6 +99,7 @@ public:
S32 mGLMaxIndexRange;
S32 mGLMaxTextureSize;
F32 mMaxAnisotropy = 0.f;
S32 mMaxUniformBlockSize = 0;
// GL 4.x capabilities
bool mHasCubeMapArray = false;
@ -169,13 +170,18 @@ void assert_glerror();
void clear_glerror();
//#if LL_DEBUG
# define stop_glerror() assert_glerror()
# define llglassertok() assert_glerror()
//#else
//# define stop_glerror()
//# define llglassertok()
//#endif
// stop_glerror is still needed on OS X but has performance implications
// use macro below to conditionally add stop_glerror to non-release builds
// on OS X
#if LL_DARWIN && !LL_RELEASE_FOR_DOWNLOAD
#define STOP_GLERROR stop_glerror()
#else
#define STOP_GLERROR
#endif
#define llglassertok_always() assert_glerror()

View File

@ -381,10 +381,7 @@ void LLGLSLShader::unloadInternal()
stop_glerror();
}
bool LLGLSLShader::createShader(std::vector<LLStaticHashedString>* attributes,
std::vector<LLStaticHashedString>* uniforms,
U32 varying_count,
const char** varyings)
bool LLGLSLShader::createShader()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
@ -454,11 +451,11 @@ bool LLGLSLShader::createShader(std::vector<LLStaticHashedString>* attributes,
// Map attributes and uniforms
if (success)
{
success = mapAttributes(attributes);
success = mapAttributes();
}
if (success)
{
success = mapUniforms(uniforms);
success = mapUniforms();
}
if (!success)
{
@ -469,7 +466,7 @@ bool LLGLSLShader::createShader(std::vector<LLStaticHashedString>* attributes,
{
LL_SHADER_LOADING_WARNS() << "Failed to link using shader level " << mShaderLevel << " trying again using shader level " << (mShaderLevel - 1) << LL_ENDL;
mShaderLevel--;
return createShader(attributes, uniforms);
return createShader();
}
else
{
@ -602,7 +599,7 @@ void LLGLSLShader::attachObjects(GLuint* objects, S32 count)
}
}
bool LLGLSLShader::mapAttributes(const std::vector<LLStaticHashedString>* attributes)
bool LLGLSLShader::mapAttributes()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
@ -621,11 +618,10 @@ bool LLGLSLShader::mapAttributes(const std::vector<LLStaticHashedString>* attrib
}
mAttribute.clear();
U32 numAttributes = (attributes == NULL) ? 0 : attributes->size();
#if LL_RELEASE_WITH_DEBUG_INFO
mAttribute.resize(LLShaderMgr::instance()->mReservedAttribs.size() + numAttributes, { -1, NULL });
mAttribute.resize(LLShaderMgr::instance()->mReservedAttribs.size(), { -1, NULL });
#else
mAttribute.resize(LLShaderMgr::instance()->mReservedAttribs.size() + numAttributes, -1);
mAttribute.resize(LLShaderMgr::instance()->mReservedAttribs.size(), -1);
#endif
if (res)
@ -649,19 +645,6 @@ bool LLGLSLShader::mapAttributes(const std::vector<LLStaticHashedString>* attrib
LL_DEBUGS("ShaderUniform") << "Attribute " << name << " assigned to channel " << index << LL_ENDL;
}
}
if (attributes != NULL)
{
for (U32 i = 0; i < numAttributes; i++)
{
const char* name = (*attributes)[i].String().c_str();
S32 index = glGetAttribLocation(mProgramObject, name);
if (index != -1)
{
mAttribute[LLShaderMgr::instance()->mReservedAttribs.size() + i] = index;
LL_DEBUGS("ShaderUniform") << "Attribute " << name << " assigned to channel " << index << LL_ENDL;
}
}
}
return true;
}
@ -669,7 +652,7 @@ bool LLGLSLShader::mapAttributes(const std::vector<LLStaticHashedString>* attrib
return false;
}
void LLGLSLShader::mapUniform(GLint index, const vector<LLStaticHashedString>* uniforms)
void LLGLSLShader::mapUniform(GLint index)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
@ -756,21 +739,6 @@ void LLGLSLShader::mapUniform(GLint index, const vector<LLStaticHashedString>* u
return;
}
}
if (uniforms != NULL)
{
for (U32 i = 0; i < uniforms->size(); i++)
{
if ((mUniform[i + LLShaderMgr::instance()->mReservedUniforms.size()] == -1)
&& ((*uniforms)[i].String() == name))
{
//found it
mUniform[i + LLShaderMgr::instance()->mReservedUniforms.size()] = location;
mTexture[i + LLShaderMgr::instance()->mReservedUniforms.size()] = mapUniformTextureChannel(location, type, size);
return;
}
}
}
}
}
@ -830,7 +798,7 @@ GLint LLGLSLShader::mapUniformTextureChannel(GLint location, GLenum type, GLint
return -1;
}
bool LLGLSLShader::mapUniforms(const vector<LLStaticHashedString>* uniforms)
bool LLGLSLShader::mapUniforms()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
@ -843,9 +811,8 @@ bool LLGLSLShader::mapUniforms(const vector<LLStaticHashedString>* uniforms)
mTexture.clear();
mValue.clear();
//initialize arrays
U32 numUniforms = (uniforms == NULL) ? 0 : uniforms->size();
mUniform.resize(numUniforms + LLShaderMgr::instance()->mReservedUniforms.size(), -1);
mTexture.resize(numUniforms + LLShaderMgr::instance()->mReservedUniforms.size(), -1);
mUniform.resize(LLShaderMgr::instance()->mReservedUniforms.size(), -1);
mTexture.resize(LLShaderMgr::instance()->mReservedUniforms.size(), -1);
bind();
@ -946,26 +913,26 @@ bool LLGLSLShader::mapUniforms(const vector<LLStaticHashedString>* uniforms)
if (specularDiff || bumpLessDiff || envLessDiff || refLessDiff)
{
mapUniform(diffuseMap, uniforms);
mapUniform(diffuseMap);
skip_index.insert(diffuseMap);
if (-1 != specularMap) {
mapUniform(specularMap, uniforms);
mapUniform(specularMap);
skip_index.insert(specularMap);
}
if (-1 != bumpMap) {
mapUniform(bumpMap, uniforms);
mapUniform(bumpMap);
skip_index.insert(bumpMap);
}
if (-1 != environmentMap) {
mapUniform(environmentMap, uniforms);
mapUniform(environmentMap);
skip_index.insert(environmentMap);
}
if (-1 != reflectionMap) {
mapUniform(reflectionMap, uniforms);
mapUniform(reflectionMap);
skip_index.insert(reflectionMap);
}
}
@ -979,21 +946,29 @@ bool LLGLSLShader::mapUniforms(const vector<LLStaticHashedString>* uniforms)
if (skip_index.end() != skip_index.find(i)) continue;
//........................................................................................
mapUniform(i, uniforms);
mapUniform(i);
}
//........................................................................................................................................
if (mFeatures.hasReflectionProbes) // Set up block binding, in a way supported by Apple (rather than binding = 1 in .glsl).
{ // See slide 35 and more of https://docs.huihoo.com/apple/wwdc/2011/session_420__advances_in_opengl_for_mac_os_x_lion.pdf
static const GLuint BLOCKBINDING = 1; //picked by us
//Get the index, similar to a uniform location
GLuint UBOBlockIndex = glGetUniformBlockIndex(mProgramObject, "ReflectionProbes");
// Set up block binding, in a way supported by Apple (rather than binding = 1 in .glsl).
// See slide 35 and more of https://docs.huihoo.com/apple/wwdc/2011/session_420__advances_in_opengl_for_mac_os_x_lion.pdf
const char* ubo_names[] =
{
"ReflectionProbes", // UB_REFLECTION_PROBES
"GLTFJoints", // UB_GLTF_JOINTS
};
llassert(LL_ARRAY_SIZE(ubo_names) == NUM_UNIFORM_BLOCKS);
for (U32 i = 0; i < NUM_UNIFORM_BLOCKS; ++i)
{
GLuint UBOBlockIndex = glGetUniformBlockIndex(mProgramObject, ubo_names[i]);
if (UBOBlockIndex != GL_INVALID_INDEX)
{
//Set this index to a binding index
glUniformBlockBinding(mProgramObject, UBOBlockIndex, BLOCKBINDING);
glUniformBlockBinding(mProgramObject, UBOBlockIndex, i);
}
}
unbind();
LL_DEBUGS("ShaderUniform") << "Total Uniform Size: " << mTotalUniformSize << LL_ENDL;
@ -1049,6 +1024,13 @@ void LLGLSLShader::bind()
}
}
void LLGLSLShader::bind(U8 variant)
{
llassert(mGLTFVariants.size() == LLGLSLShader::NUM_GLTF_VARIANTS);
llassert(variant < LLGLSLShader::NUM_GLTF_VARIANTS);
mGLTFVariants[variant].bind();
}
void LLGLSLShader::bind(bool rigged)
{
if (rigged)

View File

@ -44,6 +44,7 @@ public:
bool hasTransport = false; // implies no lighting (it's possible to have neither though)
bool hasSkinning = false;
bool hasObjectSkinning = false;
bool mGLTF = false;
bool hasAtmospherics = false;
bool hasGamma = false;
bool hasShadows = false;
@ -145,6 +146,14 @@ public:
SG_COUNT
} eGroup;
enum UniformBlock : GLuint
{
UB_REFLECTION_PROBES,
UB_GLTF_JOINTS,
NUM_UNIFORM_BLOCKS
};
static std::set<LLGLSLShader*> sInstances;
static bool sProfileEnabled;
@ -175,17 +184,14 @@ public:
// If force_read is true, will force an immediate readback (severe performance penalty)
bool readProfileQuery(bool for_runtime = false, bool force_read = false);
bool createShader(std::vector<LLStaticHashedString>* attributes,
std::vector<LLStaticHashedString>* uniforms,
U32 varying_count = 0,
const char** varyings = NULL);
bool createShader();
bool attachFragmentObject(std::string object);
bool attachVertexObject(std::string object);
void attachObject(GLuint object);
void attachObjects(GLuint* objects = NULL, S32 count = 0);
bool mapAttributes(const std::vector<LLStaticHashedString>* attributes);
bool mapUniforms(const std::vector<LLStaticHashedString>*);
void mapUniform(GLint index, const std::vector<LLStaticHashedString>*);
bool mapAttributes();
bool mapUniforms();
void mapUniform(GLint index);
void uniform1i(U32 index, GLint i);
void uniform1f(U32 index, GLfloat v);
void fastUniform1f(U32 index, GLfloat v);
@ -318,6 +324,24 @@ public:
// this pointer should be set to whichever shader represents this shader's rigged variant
LLGLSLShader* mRiggedVariant = nullptr;
// variants for use by GLTF renderer
// bit 0 = alpha mode blend (1) or opaque (0)
// bit 1 = rigged (1) or static (0)
// bit 2 = unlit (1) or lit (0)
struct GLTFVariant
{
constexpr static U8 ALPHA_BLEND = 1;
constexpr static U8 RIGGED = 2;
constexpr static U8 UNLIT = 4;
};
constexpr static U8 NUM_GLTF_VARIANTS = 8;
std::vector<LLGLSLShader> mGLTFVariants;
//helper to bind GLTF variant
void bind(U8 variant);
// hacky flag used for optimization in LLDrawPoolAlpha
bool mCanBindFast = false;

View File

@ -49,6 +49,10 @@ LLGLTexture::LLGLTexture(const LLImageRaw* raw, bool usemipmaps)
mUseMipMaps = usemipmaps ;
// Create an empty image of the specified size and width
mGLTexturep = new LLImageGL(raw, usemipmaps) ;
mFullWidth = mGLTexturep->getWidth();
mFullHeight = mGLTexturep->getHeight();
mComponents = mGLTexturep->getComponents();
setTexelsPerImage();
}
LLGLTexture::~LLGLTexture()

View File

@ -1012,6 +1012,7 @@ void LLRender::syncLightState()
void LLRender::syncMatrices()
{
STOP_GLERROR;
static const U32 name[] =
{
LLShaderMgr::MODELVIEW_MATRIX,
@ -1034,8 +1035,6 @@ void LLRender::syncMatrices()
if (shader)
{
//llassert(shader);
bool mvp_done = false;
U32 i = MM_MODELVIEW;
@ -1156,6 +1155,7 @@ void LLRender::syncMatrices()
syncLightState();
}
}
STOP_GLERROR;
}
void LLRender::translatef(const GLfloat& x, const GLfloat& y, const GLfloat& z)
@ -1628,6 +1628,7 @@ void LLRender::end()
}
void LLRender::flush()
{
STOP_GLERROR;
if (mCount > 0)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_PIPELINE;
@ -1738,6 +1739,9 @@ void LLRender::flush()
vb->setColorData(mColorsp.get());
}
#if LL_DARWIN
vb->unmapBuffer();
#endif
vb->unbind();
sVBCache[vhash] = { vb , std::chrono::steady_clock::now() };

View File

@ -1163,6 +1163,7 @@ void LLShaderMgr::initAttribsAndUniforms()
mReservedAttribs.push_back("weight");
mReservedAttribs.push_back("weight4");
mReservedAttribs.push_back("clothing");
mReservedAttribs.push_back("joint");
mReservedAttribs.push_back("texture_index");
//matrix state
@ -1231,6 +1232,9 @@ void LLShaderMgr::initAttribsAndUniforms()
mReservedUniforms.push_back("diffuseMap");
mReservedUniforms.push_back("altDiffuseMap");
mReservedUniforms.push_back("specularMap");
mReservedUniforms.push_back("metallicRoughnessMap");
mReservedUniforms.push_back("normalMap");
mReservedUniforms.push_back("occlusionMap");
mReservedUniforms.push_back("emissiveMap");
mReservedUniforms.push_back("bumpMap");
mReservedUniforms.push_back("bumpMap2");
@ -1242,7 +1246,6 @@ void LLShaderMgr::initAttribsAndUniforms()
mReservedUniforms.push_back("heroProbes");
mReservedUniforms.push_back("cloud_noise_texture");
mReservedUniforms.push_back("cloud_noise_texture_next");
mReservedUniforms.push_back("fullbright");
mReservedUniforms.push_back("lightnorm");
mReservedUniforms.push_back("sunlight_color");
mReservedUniforms.push_back("ambient_color");
@ -1354,7 +1357,6 @@ void LLShaderMgr::initAttribsAndUniforms()
llassert(mReservedUniforms.size() == LLShaderMgr::DEFERRED_SHADOW5+1);
mReservedUniforms.push_back("normalMap");
mReservedUniforms.push_back("positionMap");
mReservedUniforms.push_back("diffuseRect");
mReservedUniforms.push_back("specularRect");
@ -1367,7 +1369,6 @@ void LLShaderMgr::initAttribsAndUniforms()
mReservedUniforms.push_back("bloomMap");
mReservedUniforms.push_back("projectionMap");
mReservedUniforms.push_back("norm_mat");
mReservedUniforms.push_back("texture_gamma");
mReservedUniforms.push_back("specular_color");
mReservedUniforms.push_back("env_intensity");

View File

@ -93,6 +93,9 @@ public:
DIFFUSE_MAP, // "diffuseMap"
ALTERNATE_DIFFUSE_MAP, // "altDiffuseMap"
SPECULAR_MAP, // "specularMap"
METALLIC_ROUGHNESS_MAP, // "metallicRoughnessMap"
NORMAL_MAP, // "normalMap"
OCCLUSION_MAP, // "occlusionMap"
EMISSIVE_MAP, // "emissiveMap"
BUMP_MAP, // "bumpMap"
BUMP_MAP2, // "bumpMap2"
@ -104,7 +107,6 @@ public:
HERO_PROBE, // "heroProbes"
CLOUD_NOISE_MAP, // "cloud_noise_texture"
CLOUD_NOISE_MAP_NEXT, // "cloud_noise_texture_next"
FULLBRIGHT, // "fullbright"
LIGHTNORM, // "lightnorm"
SUNLIGHT_COLOR, // "sunlight_color"
AMBIENT, // "ambient_color"
@ -202,7 +204,6 @@ public:
DEFERRED_SHADOW3, // "shadowMap3"
DEFERRED_SHADOW4, // "shadowMap4"
DEFERRED_SHADOW5, // "shadowMap5"
DEFERRED_NORMAL, // "normalMap"
DEFERRED_POSITION, // "positionMap"
DEFERRED_DIFFUSE, // "diffuseRect"
DEFERRED_SPECULAR, // "specularRect"
@ -215,7 +216,6 @@ public:
DEFERRED_BLOOM, // "bloomMap"
DEFERRED_PROJECTION, // "projectionMap"
DEFERRED_NORM_MATRIX, // "norm_mat"
TEXTURE_GAMMA, // "texture_gamma"
SPECULAR_COLOR, // "specular_color"
ENVIRONMENT_INTENSITY, // "env_intensity"

View File

@ -290,6 +290,62 @@ static GLuint gen_buffer()
#define ANALYZE_VBO_POOL 0
#if LL_DARWIN
// experimental -- disable VBO pooling on OS X and use glMapBuffer
class LLVBOPool
{
public:
U64 mAllocated = 0;
U64 getVramBytesUsed()
{
return mAllocated;
}
void allocate(GLenum type, U32 size, GLuint& name, U8*& data)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_VERTEX;
STOP_GLERROR;
llassert(type == GL_ARRAY_BUFFER || type == GL_ELEMENT_ARRAY_BUFFER);
llassert(name == 0); // non zero name indicates a gl name that wasn't freed
llassert(data == nullptr); // non null data indicates a buffer that wasn't freed
llassert(size >= 2); // any buffer size smaller than a single index is nonsensical
mAllocated += size;
{ //allocate a new buffer
LL_PROFILE_GPU_ZONE("vbo alloc");
// ON OS X, we don't allocate a VBO until the last possible moment
// in unmapBuffer
data = (U8*) ll_aligned_malloc_16(size);
STOP_GLERROR;
}
}
void free(GLenum type, U32 size, GLuint name, U8* data)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_VERTEX;
llassert(type == GL_ARRAY_BUFFER || type == GL_ELEMENT_ARRAY_BUFFER);
llassert(size >= 2);
if (data)
{
ll_aligned_free_16(data);
}
mAllocated -= size;
STOP_GLERROR;
if (name)
{
glDeleteBuffers(1, &name);
}
STOP_GLERROR;
}
};
#else
class LLVBOPool
{
public:
@ -509,9 +565,8 @@ public:
mIBOPool.clear();
mVBOPool.clear();
}
};
#endif
static LLVBOPool* sVBOPool = nullptr;
@ -545,6 +600,7 @@ const U32 LLVertexBuffer::sTypeSize[LLVertexBuffer::TYPE_MAX] =
sizeof(F32), // TYPE_WEIGHT,
sizeof(LLVector4), // TYPE_WEIGHT4,
sizeof(LLVector4), // TYPE_CLOTHWEIGHT,
sizeof(U64), // TYPE_JOINT,
sizeof(LLVector4), // TYPE_TEXTURE_INDEX (actually exists as position.w), no extra data, but stride is 16 bytes
};
@ -562,6 +618,7 @@ static const std::string vb_type_name[] =
"TYPE_WEIGHT",
"TYPE_WEIGHT4",
"TYPE_CLOTHWEIGHT",
"TYPE_JOINT"
"TYPE_TEXTURE_INDEX",
"TYPE_MAX",
"TYPE_INDEX",
@ -645,6 +702,8 @@ void LLVertexBuffer::drawElements(U32 mode, const LLVector4a* pos, const LLVecto
}
// </FS:Ansariel>
STOP_GLERROR;
gGL.syncMatrices();
U32 mask = LLVertexBuffer::MAP_VERTEX;
@ -703,6 +762,7 @@ bool LLVertexBuffer::validateRange(U32 start, U32 end, U32 count, U32 indices_of
}
{
#if 0 // not a reliable test for VBOs that are not backed by a CPU buffer
U16* idx = (U16*) mMappedIndexData+indices_offset;
for (U32 i = 0; i < count; ++i)
{
@ -740,6 +800,7 @@ bool LLVertexBuffer::validateRange(U32 start, U32 end, U32 count, U32 indices_of
}
}
}
#endif
}
return true;
@ -757,8 +818,10 @@ void LLVertexBuffer::drawRange(U32 mode, U32 start, U32 end, U32 count, U32 indi
llassert(mGLBuffer == sGLRenderBuffer);
llassert(mGLIndices == sGLRenderIndices);
gGL.syncMatrices();
STOP_GLERROR;
glDrawRangeElements(sGLMode[mode], start, end, count, mIndicesType,
(GLvoid*) (indices_offset * (size_t) mIndicesStride));
STOP_GLERROR;
}
void LLVertexBuffer::draw(U32 mode, U32 count, U32 indices_offset) const
@ -774,7 +837,9 @@ void LLVertexBuffer::drawArrays(U32 mode, U32 first, U32 count) const
llassert(mGLIndices == sGLRenderIndices);
gGL.syncMatrices();
STOP_GLERROR;
glDrawArrays(sGLMode[mode], first, count);
STOP_GLERROR;
}
//static
@ -797,9 +862,10 @@ void LLVertexBuffer::initClass(LLWindow* window)
//static
void LLVertexBuffer::unbind()
{
STOP_GLERROR;
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
STOP_GLERROR;
sGLRenderBuffer = 0;
sGLRenderIndices = 0;
}
@ -1052,8 +1118,7 @@ bool LLVertexBuffer::updateNumIndices(U32 nindices)
bool LLVertexBuffer::allocateBuffer(U32 nverts, U32 nindices)
{
if (nverts < 0 || nindices < 0 ||
nverts > 65536)
if (nverts < 0 || nindices < 0)
{
LL_ERRS() << "Bad vertex buffer allocation: " << nverts << " : " << nindices << LL_ENDL;
}
@ -1096,6 +1161,7 @@ U8* LLVertexBuffer::mapVertexBuffer(LLVertexBuffer::AttributeType type, U32 inde
count = mNumVerts - index;
}
#if !LL_DARWIN
U32 start = mOffsets[type] + sTypeSize[type] * index;
U32 end = start + sTypeSize[type] * count-1;
@ -1116,7 +1182,7 @@ U8* LLVertexBuffer::mapVertexBuffer(LLVertexBuffer::AttributeType type, U32 inde
//didn't expand an existing region, make a new one
mMappedVertexRegions.push_back({ start, end });
}
#endif
return mMappedData+mOffsets[type]+sTypeSize[type]*index;
}
@ -1130,6 +1196,7 @@ U8* LLVertexBuffer::mapIndexBuffer(U32 index, S32 count)
count = mNumIndices-index;
}
#if !LL_DARWIN
U32 start = sizeof(U16) * index;
U32 end = start + sizeof(U16) * count-1;
@ -1150,6 +1217,7 @@ U8* LLVertexBuffer::mapIndexBuffer(U32 index, S32 count)
//didn't expand an existing region, make a new one
mMappedIndexRegions.push_back({ start, end });
}
#endif
return mMappedIndexData + sizeof(U16)*index;
}
@ -1158,9 +1226,17 @@ U8* LLVertexBuffer::mapIndexBuffer(U32 index, S32 count)
// target -- "target" parameter for glBufferSubData
// start -- first byte to copy
// end -- last byte to copy (NOT last byte + 1)
// data -- mMappedData or mMappedIndexData
static void flush_vbo(GLenum target, U32 start, U32 end, void* data)
// data -- data to be flushed
// dst -- mMappedData or mMappedIndexData
static void flush_vbo(GLenum target, U32 start, U32 end, void* data, U8* dst)
{
#if LL_DARWIN
LL_PROFILE_ZONE_NAMED_CATEGORY_VERTEX("vb memcpy");
STOP_GLERROR;
// copy into mapped buffer
memcpy(dst+start, data, end-start+1);
#else
// skip mapped data and stream to GPU via glBufferSubData
if (end != 0)
{
LL_PROFILE_ZONE_NAMED_CATEGORY_VERTEX("glBufferSubData");
@ -1179,10 +1255,12 @@ static void flush_vbo(GLenum target, U32 start, U32 end, void* data)
glBufferSubData(target, i, size, (U8*) data + (i-start));
}
}
#endif
}
void LLVertexBuffer::unmapBuffer()
{
STOP_GLERROR;
struct SortMappedRegion
{
bool operator()(const MappedRegion& lhs, const MappedRegion& rhs)
@ -1191,9 +1269,51 @@ void LLVertexBuffer::unmapBuffer()
}
};
#if LL_DARWIN
STOP_GLERROR;
if (mMappedData)
{
if (mGLBuffer)
{
glDeleteBuffers(1, &mGLBuffer);
}
mGLBuffer = gen_buffer();
glBindBuffer(GL_ARRAY_BUFFER, mGLBuffer);
sGLRenderBuffer = mGLBuffer;
glBufferData(GL_ARRAY_BUFFER, mSize, mMappedData, GL_STATIC_DRAW);
}
else if (mGLBuffer != sGLRenderBuffer)
{
glBindBuffer(GL_ARRAY_BUFFER, mGLBuffer);
sGLRenderBuffer = mGLBuffer;
}
STOP_GLERROR;
if (mMappedIndexData)
{
if (mGLIndices)
{
glDeleteBuffers(1, &mGLIndices);
}
mGLIndices = gen_buffer();
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mGLIndices);
sGLRenderIndices = mGLIndices;
glBufferData(GL_ELEMENT_ARRAY_BUFFER, mIndicesSize, mMappedIndexData, GL_STATIC_DRAW);
}
else if (mGLIndices != sGLRenderIndices)
{
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mGLIndices);
sGLRenderIndices = mGLIndices;
}
STOP_GLERROR;
#else
if (!mMappedVertexRegions.empty())
{
LL_PROFILE_ZONE_NAMED_CATEGORY_VERTEX("unmapBuffer - vertex");
if (sGLRenderBuffer != mGLBuffer)
{
glBindBuffer(GL_ARRAY_BUFFER, mGLBuffer);
@ -1214,14 +1334,13 @@ void LLVertexBuffer::unmapBuffer()
}
else
{
flush_vbo(GL_ARRAY_BUFFER, start, end, (U8*)mMappedData + start);
flush_vbo(GL_ARRAY_BUFFER, start, end, (U8*)mMappedData + start, mMappedData);
start = region.mStart;
end = region.mEnd;
}
}
flush_vbo(GL_ARRAY_BUFFER, start, end, (U8*)mMappedData + start);
flush_vbo(GL_ARRAY_BUFFER, start, end, (U8*)mMappedData + start, mMappedData);
mMappedVertexRegions.clear();
}
@ -1248,16 +1367,16 @@ void LLVertexBuffer::unmapBuffer()
}
else
{
flush_vbo(GL_ELEMENT_ARRAY_BUFFER, start, end, (U8*)mMappedIndexData + start);
flush_vbo(GL_ELEMENT_ARRAY_BUFFER, start, end, (U8*)mMappedIndexData + start, mMappedIndexData);
start = region.mStart;
end = region.mEnd;
}
}
flush_vbo(GL_ELEMENT_ARRAY_BUFFER, start, end, (U8*)mMappedIndexData + start);
flush_vbo(GL_ELEMENT_ARRAY_BUFFER, start, end, (U8*)mMappedIndexData + start, mMappedIndexData);
mMappedIndexRegions.clear();
}
#endif
}
//----------------------------------------------------------------------------
@ -1389,6 +1508,13 @@ bool LLVertexBuffer::getClothWeightStrider(LLStrider<LLVector4a>& strider, U32 i
// Set for rendering
void LLVertexBuffer::setBuffer()
{
STOP_GLERROR;
#if LL_DARWIN
if (!mGLBuffer)
{ // OS X doesn't allocate a buffer until we call unmapBuffer
return;
}
#endif
// no data may be pending
llassert(mMappedVertexRegions.empty());
llassert(mMappedIndexRegions.empty());
@ -1421,12 +1547,15 @@ void LLVertexBuffer::setBuffer()
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mGLIndices);
sGLRenderIndices = mGLIndices;
}
STOP_GLERROR;
}
// virtual (default)
void LLVertexBuffer::setupVertexBuffer()
{
STOP_GLERROR;
U8* base = nullptr;
U32 data_mask = LLGLSLShader::sCurBoundShaderPtr->mAttributeMask;
@ -1498,6 +1627,12 @@ void LLVertexBuffer::setupVertexBuffer()
void* ptr = (void*)(base + mOffsets[TYPE_WEIGHT4]);
glVertexAttribPointer(loc, 4, GL_FLOAT, GL_FALSE, LLVertexBuffer::sTypeSize[TYPE_WEIGHT4], ptr);
}
if (data_mask & MAP_JOINT)
{
AttributeType loc = TYPE_JOINT;
void* ptr = (void*)(base + mOffsets[TYPE_JOINT]);
glVertexAttribIPointer(loc, 4, GL_UNSIGNED_SHORT, LLVertexBuffer::sTypeSize[TYPE_JOINT], ptr);
}
if (data_mask & MAP_CLOTHWEIGHT)
{
AttributeType loc = TYPE_CLOTHWEIGHT;
@ -1516,59 +1651,84 @@ void LLVertexBuffer::setupVertexBuffer()
void* ptr = (void*)(base + mOffsets[TYPE_VERTEX]);
glVertexAttribPointer(loc, 3, GL_FLOAT, GL_FALSE, LLVertexBuffer::sTypeSize[TYPE_VERTEX], ptr);
}
STOP_GLERROR;
}
void LLVertexBuffer::setPositionData(const LLVector4a* data)
{
#if !LL_DARWIN
llassert(sGLRenderBuffer == mGLBuffer);
flush_vbo(GL_ARRAY_BUFFER, 0, sizeof(LLVector4a) * getNumVerts()-1, (U8*) data);
#endif
flush_vbo(GL_ARRAY_BUFFER, 0, sizeof(LLVector4a) * getNumVerts()-1, (U8*) data, mMappedData);
}
void LLVertexBuffer::setTexCoordData(const LLVector2* data)
{
#if !LL_DARWIN
llassert(sGLRenderBuffer == mGLBuffer);
flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_TEXCOORD0], mOffsets[TYPE_TEXCOORD0] + sTypeSize[TYPE_TEXCOORD0] * getNumVerts() - 1, (U8*)data);
#endif
flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_TEXCOORD0], mOffsets[TYPE_TEXCOORD0] + sTypeSize[TYPE_TEXCOORD0] * getNumVerts() - 1, (U8*)data, mMappedData);
}
void LLVertexBuffer::setColorData(const LLColor4U* data)
{
#if !LL_DARWIN
llassert(sGLRenderBuffer == mGLBuffer);
flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_COLOR], mOffsets[TYPE_COLOR] + sTypeSize[TYPE_COLOR] * getNumVerts() - 1, (U8*) data);
#endif
flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_COLOR], mOffsets[TYPE_COLOR] + sTypeSize[TYPE_COLOR] * getNumVerts() - 1, (U8*) data, mMappedData);
}
void LLVertexBuffer::setNormalData(const LLVector4a* data)
{
#if !LL_DARWIN
llassert(sGLRenderBuffer == mGLBuffer);
flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_NORMAL], mOffsets[TYPE_NORMAL] + sTypeSize[TYPE_NORMAL] * getNumVerts() - 1, (U8*) data);
#endif
flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_NORMAL], mOffsets[TYPE_NORMAL] + sTypeSize[TYPE_NORMAL] * getNumVerts() - 1, (U8*) data, mMappedData);
}
void LLVertexBuffer::setTangentData(const LLVector4a* data)
{
#if !LL_DARWIN
llassert(sGLRenderBuffer == mGLBuffer);
flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_TANGENT], mOffsets[TYPE_TANGENT] + sTypeSize[TYPE_TANGENT] * getNumVerts() - 1, (U8*) data);
#endif
flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_TANGENT], mOffsets[TYPE_TANGENT] + sTypeSize[TYPE_TANGENT] * getNumVerts() - 1, (U8*) data, mMappedData);
}
void LLVertexBuffer::setWeight4Data(const LLVector4a* data)
{
#if !LL_DARWIN
llassert(sGLRenderBuffer == mGLBuffer);
flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_WEIGHT4], mOffsets[TYPE_WEIGHT4] + sTypeSize[TYPE_WEIGHT4] * getNumVerts() - 1, (U8*) data);
#endif
flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_WEIGHT4], mOffsets[TYPE_WEIGHT4] + sTypeSize[TYPE_WEIGHT4] * getNumVerts() - 1, (U8*) data, mMappedData);
}
void LLVertexBuffer::setJointData(const U64* data)
{
#if !LL_DARWIN
llassert(sGLRenderBuffer == mGLBuffer);
#endif
flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_JOINT], mOffsets[TYPE_JOINT] + sTypeSize[TYPE_JOINT] * getNumVerts() - 1, (U8*) data, mMappedData);
}
void LLVertexBuffer::setIndexData(const U16* data)
{
#if !LL_DARWIN
llassert(sGLRenderIndices == mGLIndices);
flush_vbo(GL_ELEMENT_ARRAY_BUFFER, 0, sizeof(U16) * getNumIndices() - 1, (U8*) data);
#endif
flush_vbo(GL_ELEMENT_ARRAY_BUFFER, 0, sizeof(U16) * getNumIndices() - 1, (U8*) data, mMappedIndexData);
}
void LLVertexBuffer::setIndexData(const U32* data)
{
#if !LL_DARWIN
llassert(sGLRenderIndices == mGLIndices);
#endif
if (mIndicesType != GL_UNSIGNED_INT)
{ // HACK -- vertex buffers are initialized as 16-bit indices, but can be switched to 32-bit indices
mIndicesType = GL_UNSIGNED_INT;
mIndicesStride = 4;
mNumIndices /= 2;
}
flush_vbo(GL_ELEMENT_ARRAY_BUFFER, 0, sizeof(U32) * getNumIndices() - 1, (U8*)data);
flush_vbo(GL_ELEMENT_ARRAY_BUFFER, 0, sizeof(U32) * getNumIndices() - 1, (U8*)data, mMappedIndexData);
}

View File

@ -110,6 +110,7 @@ public:
TYPE_WEIGHT, // "weight"
TYPE_WEIGHT4, // "weight4"
TYPE_CLOTHWEIGHT, // "clothing"
TYPE_JOINT, // "joint"
TYPE_TEXTURE_INDEX, // "texture_index"
TYPE_MAX, // TYPE_MAX is the size/boundary marker for attributes that go in the vertex buffer
TYPE_INDEX, // TYPE_INDEX is beyond _MAX because it lives in a separate (index) buffer
@ -129,6 +130,7 @@ public:
MAP_WEIGHT = (1<<TYPE_WEIGHT),
MAP_WEIGHT4 = (1<<TYPE_WEIGHT4),
MAP_CLOTHWEIGHT = (1<<TYPE_CLOTHWEIGHT),
MAP_JOINT = (1<<TYPE_JOINT),
MAP_TEXTURE_INDEX = (1<<TYPE_TEXTURE_INDEX),
};
@ -197,6 +199,7 @@ public:
void setNormalData(const LLVector4a* data);
void setTangentData(const LLVector4a* data);
void setWeight4Data(const LLVector4a* data);
void setJointData(const U64* data);
void setTexCoordData(const LLVector2* data);
void setColorData(const LLColor4U* data);
void setIndexData(const U16* data);

View File

@ -361,14 +361,8 @@ struct LLWindowWin32::LLWindowWin32Thread : public LL::ThreadPool
mGLReady = true;
}
// initialzie DXGI adapter (for querying available VRAM)
void initDX();
// initialize D3D (if DXGI cannot be used)
void initD3D();
//clean up DXGI/D3D resources
void cleanupDX();
// Use DXGI to check memory (because WMI doesn't report more than 4Gb)
void checkDXMem();
/// called by main thread to post work to this window thread
template <typename CALLABLE>
@ -417,12 +411,9 @@ struct LLWindowWin32::LLWindowWin32Thread : public LL::ThreadPool
// *HACK: Attempt to prevent startup crashes by deferring memory accounting
// until after some graphics setup. See SL-20177. -Cosmic,2023-09-18
bool mGLReady = false;
bool mGotGLBuffer = false;
U32 mMaxVRAM = 0; // maximum amount of vram to allow in the "budget", or 0 for no maximum (see updateVRAMUsage)
IDXGIAdapter3* mDXGIAdapter = nullptr;
LPDIRECT3D9 mD3D = nullptr;
LPDIRECT3DDEVICE9 mD3DDevice = nullptr;
};
@ -4741,39 +4732,55 @@ private:
std::string mPrev;
};
// Print hardware debug info about available graphics adapters in ordinal order
void debugEnumerateGraphicsAdapters()
void LLWindowWin32::LLWindowWin32Thread::checkDXMem()
{
LL_INFOS("Window") << "Enumerating graphics adapters..." << LL_ENDL;
if (!mGLReady || mGotGLBuffer) { return; }
IDXGIFactory1* factory;
HRESULT res = CreateDXGIFactory1(__uuidof(IDXGIFactory1), (void**)&factory);
if (FAILED(res) || !factory)
IDXGIFactory4* p_factory = nullptr;
HRESULT res = CreateDXGIFactory1(__uuidof(IDXGIFactory4), (void**)&p_factory);
if (FAILED(res))
{
LL_WARNS() << "CreateDXGIFactory1 failed: 0x" << std::hex << res << LL_ENDL;
}
else
{
IDXGIAdapter3* p_dxgi_adapter = nullptr;
UINT graphics_adapter_index = 0;
IDXGIAdapter3* dxgi_adapter;
while (true)
{
res = factory->EnumAdapters(graphics_adapter_index, reinterpret_cast<IDXGIAdapter**>(&dxgi_adapter));
res = p_factory->EnumAdapters(graphics_adapter_index, reinterpret_cast<IDXGIAdapter**>(&p_dxgi_adapter));
if (FAILED(res))
{
if (graphics_adapter_index == 0)
{
LL_WARNS() << "EnumAdapters failed: 0x" << std::hex << res << LL_ENDL;
}
else
{
LL_INFOS("Window") << "Done enumerating graphics adapters" << LL_ENDL;
}
}
else
{
if (graphics_adapter_index == 0) // Should it check largest one isntead of first?
{
DXGI_QUERY_VIDEO_MEMORY_INFO info;
p_dxgi_adapter->QueryVideoMemoryInfo(0, DXGI_MEMORY_SEGMENT_GROUP_LOCAL, &info);
// Alternatively use GetDesc from below to get adapter's memory
UINT64 budget_mb = info.Budget / (1024 * 1024);
if (gGLManager.mVRAM < (S32)budget_mb)
{
gGLManager.mVRAM = (S32)budget_mb;
LL_INFOS("RenderInit") << "New VRAM Budget (DX9): " << gGLManager.mVRAM << " MB" << LL_ENDL;
}
else
{
LL_INFOS("RenderInit") << "VRAM Budget (DX9): " << budget_mb
<< " MB, current (WMI): " << gGLManager.mVRAM << " MB" << LL_ENDL;
}
}
DXGI_ADAPTER_DESC desc;
dxgi_adapter->GetDesc(&desc);
p_dxgi_adapter->GetDesc(&desc);
std::wstring description_w((wchar_t*)desc.Description);
std::string description(description_w.begin(), description_w.end());
LL_INFOS("Window") << "Graphics adapter index: " << graphics_adapter_index << ", "
@ -4786,10 +4793,10 @@ void debugEnumerateGraphicsAdapters()
<< "SharedSystemMemory: " << desc.SharedSystemMemory / 1024 / 1024 << LL_ENDL;
}
if (dxgi_adapter)
if (p_dxgi_adapter)
{
dxgi_adapter->Release();
dxgi_adapter = NULL;
p_dxgi_adapter->Release();
p_dxgi_adapter = NULL;
}
else
{
@ -4800,95 +4807,12 @@ void debugEnumerateGraphicsAdapters()
}
}
if (factory)
if (p_factory)
{
factory->Release();
}
}
void LLWindowWin32::LLWindowWin32Thread::initDX()
{
if (!mGLReady) { return; }
if (mDXGIAdapter == NULL)
{
debugEnumerateGraphicsAdapters();
IDXGIFactory4* pFactory = nullptr;
HRESULT res = CreateDXGIFactory1(__uuidof(IDXGIFactory4), (void**)&pFactory);
if (FAILED(res))
{
LL_WARNS() << "CreateDXGIFactory1 failed: 0x" << std::hex << res << LL_ENDL;
}
else
{
res = pFactory->EnumAdapters(0, reinterpret_cast<IDXGIAdapter**>(&mDXGIAdapter));
if (FAILED(res))
{
LL_WARNS() << "EnumAdapters failed: 0x" << std::hex << res << LL_ENDL;
}
else
{
LL_INFOS() << "EnumAdapters success" << LL_ENDL;
}
}
if (pFactory)
{
pFactory->Release();
}
}
}
void LLWindowWin32::LLWindowWin32Thread::initD3D()
{
if (!mGLReady) { return; }
if (mDXGIAdapter == NULL && mD3DDevice == NULL && mWindowHandleThrd != 0)
{
mD3D = Direct3DCreate9(D3D_SDK_VERSION);
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
HRESULT res = mD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, mWindowHandleThrd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &mD3DDevice);
if (FAILED(res))
{
LL_WARNS() << "(fallback) CreateDevice failed: 0x" << std::hex << res << LL_ENDL;
}
else
{
LL_INFOS() << "(fallback) CreateDevice success" << LL_ENDL;
}
}
}
void LLWindowWin32::LLWindowWin32Thread::cleanupDX()
{
//clean up DXGI/D3D resources
if (mDXGIAdapter)
{
mDXGIAdapter->Release();
mDXGIAdapter = nullptr;
p_factory->Release();
}
if (mD3DDevice)
{
mD3DDevice->Release();
mD3DDevice = nullptr;
}
if (mD3D)
{
mD3D->Release();
mD3D = nullptr;
}
mGotGLBuffer = true;
}
void LLWindowWin32::LLWindowWin32Thread::run()
@ -4907,15 +4831,11 @@ void LLWindowWin32::LLWindowWin32Thread::run()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_WIN32;
// lazily call initD3D inside this loop to catch when mGLReady has been set to true
initDX();
// Check memory budget using DirectX
checkDXMem();
if (mWindowHandleThrd != 0)
{
// lazily call initD3D inside this loop to catch when mWindowHandle has been set, and mGLReady has been set to true
// *TODO: Shutdown if this fails when mWindowHandle exists
initD3D();
MSG msg;
BOOL status;
if (mhDCThrd == 0)
@ -4955,8 +4875,6 @@ void LLWindowWin32::LLWindowWin32Thread::run()
}
#endif
}
cleanupDX();
}
void LLWindowWin32::LLWindowWin32Thread::wakeAndDestroy()
@ -5056,7 +4974,6 @@ void LLWindowWin32::LLWindowWin32Thread::wakeAndDestroy()
// very unsafe
TerminateThread(pair.second.native_handle(), 0);
pair.second.detach();
cleanupDX();
}
}
LL_DEBUGS("Window") << "thread pool shutdown complete" << LL_ENDL;

View File

@ -618,6 +618,7 @@ set(viewer_SOURCE_FILES
llpathfindingobject.cpp
llpathfindingobjectlist.cpp
llpathfindingpathtool.cpp
llpbrterrainfeatures.cpp
llpersistentnotificationstorage.cpp
llphysicsmotion.cpp
llphysicsshapebuilderutil.cpp
@ -1406,6 +1407,7 @@ set(viewer_HEADER_FILES
llpathfindingobject.h
llpathfindingobjectlist.h
llpathfindingpathtool.h
llpbrterrainfeatures.h
llpersistentnotificationstorage.h
llphysicsmotion.h
llphysicsshapebuilderutil.h

View File

@ -1 +1 @@
7.1.8
7.1.9

View File

@ -12816,6 +12816,17 @@ Change of this parameter will affect the layout of buttons in notification toast
<key>Value</key>
<real>8.0</real>
</map>
<key>RenderTerrainPBRTransformsEnabled</key>
<map>
<key>Comment</key>
<string>EXPERIMENTAL: Enable PBR Terrain texture transforms.</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>0</integer>
</map>
<key>RenderTerrainPBRNormalsEnabled</key>
<map>
<key>Comment</key>
@ -22832,7 +22843,7 @@ Change of this parameter will affect the layout of buttons in notification toast
<key>GLTFEnabled</key>
<map>
<key>Comment</key>
<string>Enable GLTF support. Set by SimulatorFeatures</string>
<string>Enable GLTF support. Set to true by simulator if the simulator you are connected to supports GLTF Asset upload. WARNING: Manually setting this to true will enable buttons that can drain your L$ balance by implicitly uploading textures without asking.</string>
<key>Persist</key>
<integer>0</integer>
<key>Type</key>

View File

@ -142,18 +142,9 @@ vec2 getScreenCoordinate(vec2 screenpos)
}
vec4 getNorm(vec2 screenpos)
{
return texture(normalMap, screenpos.xy);
}
// return packedNormalEnvIntensityFlags since GBUFFER_FLAG_HAS_PBR needs .w
// See: C++: addDeferredAttachments(), GLSL: softenLightF
vec4 getNormalEnvIntensityFlags(vec2 screenpos, out vec3 n, out float envIntensity)
{
vec4 norm = texture(normalMap, screenpos.xy);
n = norm.xyz;
envIntensity = texture(emissiveRect, screenpos.xy).r;
norm.xyz = normalize(norm.xyz);
return norm;
}
@ -496,6 +487,43 @@ vec3 pbrPunctual(vec3 diffuseColor, vec3 specularColor,
return clamp(color, vec3(0), vec3(10));
}
vec3 pbrCalcPointLightOrSpotLight(vec3 diffuseColor, vec3 specularColor,
float perceptualRoughness,
float metallic,
vec3 n, // normal
vec3 p, // pixel position
vec3 v, // view vector (negative normalized pixel position)
vec3 lp, // light position
vec3 ld, // light direction (for spotlights)
vec3 lightColor,
float lightSize, float falloff, float is_pointlight, float ambiance)
{
vec3 color = vec3(0,0,0);
vec3 lv = lp.xyz - p;
float lightDist = length(lv);
float dist = lightDist / lightSize;
if (dist <= 1.0)
{
lv /= lightDist;
float dist_atten = calcLegacyDistanceAttenuation(dist, falloff);
// spotlight coefficient.
float spot = max(dot(-ld, lv), is_pointlight);
// spot*spot => GL_SPOT_EXPONENT=2
float spot_atten = spot*spot;
vec3 intensity = spot_atten * dist_atten * lightColor * 3.0; //magic number to balance with legacy materials
color = intensity*pbrPunctual(diffuseColor, specularColor, perceptualRoughness, metallic, n.xyz, v, lv);
}
return color;
}
void calcDiffuseSpecular(vec3 baseColor, float metallic, inout vec3 diffuseColor, inout vec3 specularColor)
{
vec3 f0 = vec3(0.04);

View File

@ -51,8 +51,6 @@ uniform vec4[2] texture_emissive_transform;
out vec3 vary_fragcoord;
uniform float near_clip;
in vec3 position;
in vec4 diffuse_color;
in vec3 normal;
@ -88,7 +86,7 @@ void main()
#endif
gl_Position = vert;
vary_fragcoord.xyz = vert.xyz + vec3(0,0,near_clip);
vary_fragcoord.xyz = vert.xyz;
base_color_texcoord = texture_transform(texcoord0, texture_base_color_transform, texture_matrix0);
normal_texcoord = texture_transform(texcoord0, texture_normal_transform, texture_matrix0);

View File

@ -193,7 +193,7 @@ void main()
rm_factors[3] = vec2(roughnessFactors.w, metallicFactors.w);
#endif
PBRMix mix = init_pbr_mix();
PBRMix pbr_mix = init_pbr_mix();
PBRMix mix2;
TerrainCoord terrain_texcoord;
switch (tm.type & MIX_X)
@ -233,7 +233,7 @@ void main()
#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL)
mix2.vNt = mikktspace(mix2.vNt, vary_tangents[0]);
#endif
mix = mix_pbr(mix, mix2, tm.weight.x);
pbr_mix = mix_pbr(pbr_mix, mix2, tm.weight.x);
break;
default:
break;
@ -275,7 +275,7 @@ void main()
#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL)
mix2.vNt = mikktspace(mix2.vNt, vary_tangents[1]);
#endif
mix = mix_pbr(mix, mix2, tm.weight.y);
pbr_mix = mix_pbr(pbr_mix, mix2, tm.weight.y);
break;
default:
break;
@ -317,7 +317,7 @@ void main()
#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL)
mix2.vNt = mikktspace(mix2.vNt, vary_tangents[2]);
#endif
mix = mix_pbr(mix, mix2, tm.weight.z);
pbr_mix = mix_pbr(pbr_mix, mix2, tm.weight.z);
break;
default:
break;
@ -359,21 +359,21 @@ void main()
#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL)
mix2.vNt = mikktspace(mix2.vNt, vary_tangents[3]);
#endif
mix = mix_pbr(mix, mix2, tm.weight.w);
pbr_mix = mix_pbr(pbr_mix, mix2, tm.weight.w);
break;
default:
break;
}
float minimum_alpha = terrain_mix(tm, minimum_alphas);
if (mix.col.a < minimum_alpha)
if (pbr_mix.col.a < minimum_alpha)
{
discard;
}
float base_color_factor_alpha = terrain_mix(tm, vec4(baseColorFactors[0].z, baseColorFactors[1].z, baseColorFactors[2].z, baseColorFactors[3].z));
#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL)
vec3 tnorm = normalize(mix.vNt);
vec3 tnorm = normalize(pbr_mix.vNt);
#else
vec3 tnorm = vary_normal;
#endif
@ -381,21 +381,21 @@ void main()
#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_EMISSIVE)
#define emissive mix.emissive
#define mix_emissive pbr_mix.emissive
#else
#define emissive vec3(0)
#define mix_emissive vec3(0)
#endif
#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_OCCLUSION)
#define orm mix.orm
#define mix_orm pbr_mix.orm
#elif (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS)
#define orm vec3(1.0, mix.rm)
#define mix_orm vec3(1.0, pbr_mix.rm)
#else
// Matte plastic potato terrain
#define orm vec3(1.0, 1.0, 0.0)
#define mix_orm vec3(1.0, 1.0, 0.0)
#endif
frag_data[0] = max(vec4(mix.col.xyz, 0.0), vec4(0)); // Diffuse
frag_data[1] = max(vec4(orm.rgb, base_color_factor_alpha), vec4(0)); // PBR linear packed Occlusion, Roughness, Metal.
frag_data[0] = max(vec4(pbr_mix.col.xyz, 0.0), vec4(0)); // Diffuse
frag_data[1] = max(vec4(mix_orm.rgb, base_color_factor_alpha), vec4(0)); // PBR linear packed Occlusion, Roughness, Metal.
frag_data[2] = vec4(tnorm, GBUFFER_FLAG_HAS_PBR); // normal, flags
frag_data[3] = max(vec4(emissive,0), vec4(0)); // PBR sRGB Emissive
frag_data[3] = max(vec4(mix_emissive,0), vec4(0)); // PBR sRGB Emissive
}

View File

@ -57,7 +57,7 @@ void main()
outColor.a = 0.0; // yes, downstream atmospherics
frag_data[0] = outColor;
frag_data[0] = max(outColor, vec4(0));
frag_data[1] = vec4(0.0,0.0,0.0,-1.0);
vec3 nvn = normalize(vary_normal);
frag_data[2] = vec4(nvn.xyz, GBUFFER_FLAG_HAS_ATMOS);

View File

@ -47,6 +47,8 @@ vec2 texgen_object(vec4 vpos, mat4 mat, vec4 tp0, vec4 tp1)
tcoord.x = dot(vpos, tp0);
tcoord.y = dot(vpos, tp1);
tcoord.z = 0;
tcoord.w = 1;
tcoord = mat * tcoord;

View File

@ -23,8 +23,6 @@
* $/LicenseInfo$
*/
uniform sampler2D exposureMap;
vec3 srgb_to_linear(vec3 cs)
{
vec3 low_range = cs / vec3(12.92);

View File

@ -0,0 +1,300 @@
/**
* @file pbrmetallicroughnessF.glsl
*
* $LicenseInfo:firstyear=2024&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$
*/
/*[EXTRA_CODE_HERE]*/
// GLTF pbrMetallicRoughness implementation
// ==================================
// needed by all variants
// ==================================
uniform sampler2D diffuseMap; //always in sRGB space
uniform sampler2D emissiveMap;
uniform vec3 emissiveColor;
in vec3 vary_position;
in vec4 vertex_color;
in vec2 base_color_texcoord;
in vec2 emissive_texcoord;
uniform float minimum_alpha;
void mirrorClip(vec3 pos);
vec3 linear_to_srgb(vec3 c);
vec3 srgb_to_linear(vec3 c);
// ==================================
// ==================================
// needed by all lit variants
// ==================================
#ifndef UNLIT
uniform sampler2D normalMap;
uniform sampler2D metallicRoughnessMap;
uniform sampler2D occlusionMap;
uniform float metallicFactor;
uniform float roughnessFactor;
in vec3 vary_normal;
in vec3 vary_tangent;
flat in float vary_sign;
in vec2 normal_texcoord;
in vec2 metallic_roughness_texcoord;
#endif
// ==================================
// ==================================
// needed by all alpha variants
// ==================================
#ifdef ALPHA_BLEND
in vec3 vary_fragcoord;
uniform vec4 clipPlane;
uniform float clipSign;
void waterClip(vec3 pos);
void calcAtmosphericVarsLinear(vec3 inPositionEye, vec3 norm, vec3 light_dir, out vec3 sunlit, out vec3 amblit, out vec3 atten, out vec3 additive);
vec4 applySkyAndWaterFog(vec3 pos, vec3 additive, vec3 atten, vec4 color);
#endif
// ==================================
// ==================================
// needed by lit alpha
// ==================================
#if defined(ALPHA_BLEND) && !defined(UNLIT)
#ifdef HAS_SUN_SHADOW
uniform sampler2D lightMap;
uniform vec2 screen_res;
#endif
// Lights
// See: LLRender::syncLightState()
uniform vec4 light_position[8];
uniform vec3 light_direction[8]; // spot direction
uniform vec4 light_attenuation[8]; // linear, quadratic, is omni, unused, See: LLPipeline::setupHWLights() and syncLightState()
uniform vec3 light_diffuse[8];
uniform vec2 light_deferred_attenuation[8]; // light size and falloff
uniform int sun_up_factor;
uniform vec3 sun_dir;
uniform vec3 moon_dir;
void calcHalfVectors(vec3 lv, vec3 n, vec3 v, out vec3 h, out vec3 l, out float nh, out float nl, out float nv, out float vh, out float lightDist);
float calcLegacyDistanceAttenuation(float distance, float falloff);
float sampleDirectionalShadow(vec3 pos, vec3 norm, vec2 pos_screen);
void sampleReflectionProbes(inout vec3 ambenv, inout vec3 glossenv,
vec2 tc, vec3 pos, vec3 norm, float glossiness, bool transparent, vec3 amblit_linear);
void calcDiffuseSpecular(vec3 baseColor, float metallic, inout vec3 diffuseColor, inout vec3 specularColor);
vec3 pbrBaseLight(vec3 diffuseColor,
vec3 specularColor,
float metallic,
vec3 pos,
vec3 norm,
float perceptualRoughness,
vec3 light_dir,
vec3 sunlit,
float scol,
vec3 radiance,
vec3 irradiance,
vec3 colorEmissive,
float ao,
vec3 additive,
vec3 atten);
vec3 pbrCalcPointLightOrSpotLight(vec3 diffuseColor, vec3 specularColor,
float perceptualRoughness,
float metallic,
vec3 n, // normal
vec3 p, // pixel position
vec3 v, // view vector (negative normalized pixel position)
vec3 lp, // light position
vec3 ld, // light direction (for spotlights)
vec3 lightColor,
float lightSize, float falloff, float is_pointlight, float ambiance);
#endif
// ==================================
// ==================================
// output definition
// ==================================
#if defined(ALPHA_BLEND) || defined(UNLIT)
out vec4 frag_color;
#else
out vec4 frag_data[4];
#endif
// ==================================
void main()
{
// ==================================
// all variants
// mirror clip
// base color
// masking
// emissive
// ==================================
vec3 pos = vary_position;
mirrorClip(pos);
vec4 basecolor = texture(diffuseMap, base_color_texcoord.xy).rgba;
basecolor.rgb = srgb_to_linear(basecolor.rgb);
basecolor *= vertex_color;
if (basecolor.a < minimum_alpha)
{
discard;
}
vec3 emissive = emissiveColor;
emissive *= srgb_to_linear(texture(emissiveMap, emissive_texcoord.xy).rgb);
// ==================================
// ==================================
// all lit variants
// prepare norm
// prepare orm
// ==================================
#ifndef UNLIT
// from mikktspace.com
vec3 vNt = texture(normalMap, normal_texcoord.xy).xyz*2.0-1.0;
float sign = vary_sign;
vec3 vN = vary_normal;
vec3 vT = vary_tangent.xyz;
vec3 vB = sign * cross(vN, vT);
vec3 norm = normalize( vNt.x * vT + vNt.y * vB + vNt.z * vN );
norm *= gl_FrontFacing ? 1.0 : -1.0;
// RGB = Occlusion, Roughness, Metal
// default values, see LLViewerTexture::sDefaultPBRORMImagep
// occlusion 1.0
// roughness 0.0
// metal 0.0
vec3 orm = texture(metallicRoughnessMap, metallic_roughness_texcoord.xy).rgb;
orm.r = texture(occlusionMap, metallic_roughness_texcoord.xy).r;
orm.g *= roughnessFactor;
orm.b *= metallicFactor;
#endif
// ==================================
// ==================================
// non alpha output
// ==================================
#ifndef ALPHA_BLEND
#ifdef UNLIT
vec4 color = basecolor;
color.rgb += emissive.rgb;
frag_color = color;
#else
frag_data[0] = max(vec4(basecolor.rgb, 0.0), vec4(0));
frag_data[1] = max(vec4(orm.rgb,0.0), vec4(0));
frag_data[2] = vec4(norm, GBUFFER_FLAG_HAS_PBR);
frag_data[3] = max(vec4(emissive,0), vec4(0));
#endif
#endif
// ==================================
// alpha implementation
// ==================================
#ifdef ALPHA_BLEND
float scol = 1.0;
vec3 sunlit;
vec3 amblit;
vec3 additive;
vec3 atten;
vec3 light_dir;
#ifdef UNLIT
light_dir = vec3(0,0,1);
vec3 norm = vec3(0,0,1);
#else
light_dir = (sun_up_factor == 1) ? sun_dir : moon_dir;
#endif
calcAtmosphericVarsLinear(pos.xyz, norm, light_dir, sunlit, amblit, additive, atten);
#ifndef UNLIT
vec3 sunlit_linear = srgb_to_linear(sunlit);
vec2 frag = vary_fragcoord.xy/vary_fragcoord.z*0.5+0.5;
#ifdef HAS_SUN_SHADOW
scol = sampleDirectionalShadow(pos.xyz, norm.xyz, frag);
#endif
float perceptualRoughness = orm.g * roughnessFactor;
float metallic = orm.b * metallicFactor;
// PBR IBL
float gloss = 1.0 - perceptualRoughness;
vec3 irradiance = vec3(0);
vec3 radiance = vec3(0);
sampleReflectionProbes(irradiance, radiance, vary_position.xy*0.5+0.5, pos.xyz, norm.xyz, gloss, true, amblit);
vec3 diffuseColor;
vec3 specularColor;
calcDiffuseSpecular(basecolor.rgb, metallic, diffuseColor, specularColor);
vec3 v = -normalize(pos.xyz);
vec3 color = pbrBaseLight(diffuseColor, specularColor, metallic, v, norm.xyz, perceptualRoughness, light_dir, sunlit_linear, scol, radiance, irradiance, emissive, orm.r, additive, atten);
vec3 light = vec3(0);
// Punctual lights
#define LIGHT_LOOP(i) light += pbrCalcPointLightOrSpotLight(diffuseColor, specularColor, perceptualRoughness, metallic, norm.xyz, pos.xyz, v, light_position[i].xyz, light_direction[i].xyz, light_diffuse[i].rgb, light_deferred_attenuation[i].x, light_deferred_attenuation[i].y, light_attenuation[i].z, light_attenuation[i].w);
LIGHT_LOOP(1)
LIGHT_LOOP(2)
LIGHT_LOOP(3)
LIGHT_LOOP(4)
LIGHT_LOOP(5)
LIGHT_LOOP(6)
LIGHT_LOOP(7)
color.rgb += light.rgb;
color.rgb = applySkyAndWaterFog(pos.xyz, additive, atten, vec4(color, 1.0)).rgb;
float a = basecolor.a*vertex_color.a;
frag_color = max(vec4(color.rgb,a), vec4(0));
#else // UNLIT
vec4 color = basecolor;
color.rgb += emissive.rgb;
frag_color = color;
#endif
#endif // ALPHA_BLEND
}

View File

@ -0,0 +1,171 @@
/**
* @file pbrmetallicroughnessV.glsl
*
* $LicenseInfo:firstyear=2024&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$
*/
// GLTF pbrMetallicRoughness implementation
uniform mat4 modelview_matrix;
#ifdef HAS_SKIN
uniform mat4 projection_matrix;
#else
uniform mat3 normal_matrix;
uniform mat4 modelview_projection_matrix;
#endif
uniform mat4 texture_matrix0;
uniform vec4[2] texture_base_color_transform;
uniform vec4[2] texture_normal_transform;
uniform vec4[2] texture_metallic_roughness_transform;
uniform vec4[2] texture_emissive_transform;
in vec3 position;
in vec4 diffuse_color;
in vec2 texcoord0;
out vec2 base_color_texcoord;
out vec2 emissive_texcoord;
out vec4 vertex_color;
out vec3 vary_position;
#ifndef UNLIT
in vec3 normal;
in vec4 tangent;
out vec2 normal_texcoord;
out vec2 metallic_roughness_texcoord;
out vec3 vary_tangent;
flat out float vary_sign;
out vec3 vary_normal;
vec3 tangent_space_transform(vec4 vertex_tangent, vec3 vertex_normal, vec4[2] khr_gltf_transform, mat4 sl_animation_transform);
#endif
vec2 texture_transform(vec2 vertex_texcoord, vec4[2] khr_gltf_transform, mat4 sl_animation_transform);
#ifdef ALPHA_BLEND
out vec3 vary_fragcoord;
#endif
#ifdef HAS_SKIN
in uvec4 joint;
in vec4 weight4;
layout (std140) uniform GLTFJoints
{
// list of OBBs for user override probes
mat3x4 gltf_joints[MAX_JOINTS_PER_GLTF_OBJECT];
};
mat4 getGLTFSkinTransform()
{
int i;
vec4 w = weight4;
uint i1 = joint.x;
uint i2 = joint.y;
uint i3 = joint.z;
uint i4 = joint.w;
mat3 mat = mat3(gltf_joints[i1])*w.x;
mat += mat3(gltf_joints[i2])*w.y;
mat += mat3(gltf_joints[i3])*w.z;
mat += mat3(gltf_joints[i4])*w.w;
vec3 trans = vec3(gltf_joints[i1][0].w,gltf_joints[i1][1].w,gltf_joints[i1][2].w)*w.x;
trans += vec3(gltf_joints[i2][0].w,gltf_joints[i2][1].w,gltf_joints[i2][2].w)*w.y;
trans += vec3(gltf_joints[i3][0].w,gltf_joints[i3][1].w,gltf_joints[i3][2].w)*w.z;
trans += vec3(gltf_joints[i4][0].w,gltf_joints[i4][1].w,gltf_joints[i4][2].w)*w.w;
mat4 ret;
ret[0] = vec4(mat[0], 0);
ret[1] = vec4(mat[1], 0);
ret[2] = vec4(mat[2], 0);
ret[3] = vec4(trans, 1.0);
return ret;
#ifdef IS_AMD_CARD
// If it's AMD make sure the GLSL compiler sees the arrays referenced once by static index. Otherwise it seems to optimise the storage awawy which leads to unfun crashes and artifacts.
mat3x4 dummy1 = gltf_joints[0];
mat3x4 dummy2 = gltf_joints[MAX_JOINTS_PER_GLTF_OBJECT-1];
#endif
}
#endif
void main()
{
#ifdef HAS_SKIN
mat4 mat = getGLTFSkinTransform();
mat = modelview_matrix * mat;
vec3 pos = (mat*vec4(position.xyz,1.0)).xyz;
vary_position = pos;
vec4 vert = projection_matrix * vec4(pos, 1.0);
gl_Position = vert;
#else
vary_position = (modelview_matrix*vec4(position.xyz, 1.0)).xyz;
//transform vertex
vec4 vert = modelview_projection_matrix * vec4(position.xyz, 1.0);
gl_Position = vert;
#endif
base_color_texcoord = texture_transform(texcoord0, texture_base_color_transform, texture_matrix0);
emissive_texcoord = texture_transform(texcoord0, texture_emissive_transform, texture_matrix0);
#ifndef UNLIT
normal_texcoord = texture_transform(texcoord0, texture_normal_transform, texture_matrix0);
metallic_roughness_texcoord = texture_transform(texcoord0, texture_metallic_roughness_transform, texture_matrix0);
#endif
#ifndef UNLIT
#ifdef HAS_SKIN
vec3 n = (mat*vec4(normal.xyz+position.xyz,1.0)).xyz-pos.xyz;
vec3 t = (mat*vec4(tangent.xyz+position.xyz,1.0)).xyz-pos.xyz;
#else //HAS_SKIN
vec3 n = normal_matrix * normal;
vec3 t = normal_matrix * tangent.xyz;
#endif
n = normalize(n);
vary_tangent = normalize(tangent_space_transform(vec4(t, tangent.w), n, texture_normal_transform, texture_matrix0));
vary_sign = tangent.w;
vary_normal = n;
#endif
vertex_color = diffuse_color;
#ifdef ALPHA_BLEND
vary_fragcoord = vert.xyz;
#endif
}

View File

@ -118,7 +118,7 @@ vec3 pbrPunctual(vec3 diffuseColor, vec3 specularColor,
vec3 v, // surface point to camera
vec3 l); //surface point to light
vec3 calcPointLightOrSpotLight(vec3 diffuseColor, vec3 specularColor,
vec3 pbrCalcPointLightOrSpotLight(vec3 diffuseColor, vec3 specularColor,
float perceptualRoughness,
float metallic,
vec3 n, // normal
@ -127,33 +127,7 @@ vec3 calcPointLightOrSpotLight(vec3 diffuseColor, vec3 specularColor,
vec3 lp, // light position
vec3 ld, // light direction (for spotlights)
vec3 lightColor,
float lightSize, float falloff, float is_pointlight, float ambiance)
{
vec3 color = vec3(0,0,0);
vec3 lv = lp.xyz - p;
float lightDist = length(lv);
float dist = lightDist / lightSize;
if (dist <= 1.0)
{
lv /= lightDist;
float dist_atten = calcLegacyDistanceAttenuation(dist, falloff);
// spotlight coefficient.
float spot = max(dot(-ld, lv), is_pointlight);
// spot*spot => GL_SPOT_EXPONENT=2
float spot_atten = spot*spot;
vec3 intensity = spot_atten * dist_atten * lightColor * 3.0; //magic number to balance with legacy materials
color = intensity*pbrPunctual(diffuseColor, specularColor, perceptualRoughness, metallic, n.xyz, v, lv);
}
return color;
}
float lightSize, float falloff, float is_pointlight, float ambiance);
void main()
{
@ -230,7 +204,7 @@ void main()
vec3 light = vec3(0);
// Punctual lights
#define LIGHT_LOOP(i) light += calcPointLightOrSpotLight(diffuseColor, specularColor, perceptualRoughness, metallic, norm.xyz, pos.xyz, v, light_position[i].xyz, light_direction[i].xyz, light_diffuse[i].rgb, light_deferred_attenuation[i].x, light_deferred_attenuation[i].y, light_attenuation[i].z, light_attenuation[i].w);
#define LIGHT_LOOP(i) light += pbrCalcPointLightOrSpotLight(diffuseColor, specularColor, perceptualRoughness, metallic, norm.xyz, pos.xyz, v, light_position[i].xyz, light_direction[i].xyz, light_diffuse[i].rgb, light_deferred_attenuation[i].x, light_deferred_attenuation[i].y, light_attenuation[i].z, light_attenuation[i].w);
LIGHT_LOOP(1)
LIGHT_LOOP(2)

View File

@ -77,7 +77,6 @@ uniform float is_mirror;
uniform vec3 sun_dir;
uniform vec3 moon_dir;
in vec2 vary_fragcoord;
uniform mat4 proj_mat;
uniform mat4 inv_proj;

View File

@ -68,7 +68,7 @@ bool FSExportPermsCheck::canExportNode(LLSelectNode* node, bool dae)
{
case EXPORT_ALLOWED:
{
exportable = node->mPermissions->allowExportBy(gAgent.getID());
exportable = node->mPermissions->allowOpenSimExportBy(gAgentID);
break;
}
/// TODO: Once enough grids adopt a version supporting exports, get consensus

View File

@ -28,6 +28,7 @@
#include "asset.h"
#include "buffer_util.h"
#include "llfilesystem.h"
using namespace LL::GLTF;
using namespace boost::json;
@ -107,6 +108,8 @@ void Buffer::erase(Asset& asset, S32 offset, S32 length)
mData.erase(mData.begin() + offset, mData.begin() + offset + length);
mByteLength = mData.size();
for (BufferView& view : asset.mBufferViews)
{
if (view.mBuffer == idx)
@ -119,6 +122,103 @@ void Buffer::erase(Asset& asset, S32 offset, S32 length)
}
}
bool Buffer::prep(Asset& asset)
{
if (mByteLength == 0)
{
return false;
}
LLUUID id;
if (mUri.size() == UUID_STR_SIZE && LLUUID::parseUUID(mUri, &id) && id.notNull())
{ // loaded from an asset, fetch the buffer data from the asset store
LLFileSystem file(id, LLAssetType::AT_GLTF_BIN, LLFileSystem::READ);
if (mByteLength > file.getSize())
{
LL_WARNS("GLTF") << "Unexpected glbin size: " << id << " is " << file.getSize() << " bytes, expected " << mByteLength << LL_ENDL;
return false;
}
mData.resize(mByteLength);
if (!file.read((U8*)mData.data(), mData.size()))
{
LL_WARNS("GLTF") << "Failed to load buffer data from asset: " << id << LL_ENDL;
return false;
}
}
else if (mUri.find("data:") == 0)
{ // loaded from a data URI, load the texture from the data
LL_WARNS() << "Data URIs not yet supported" << LL_ENDL;
return false;
}
else if (!asset.mFilename.empty() &&
!mUri.empty()) // <-- uri could be empty if we're loading from .glb
{
std::string dir = gDirUtilp->getDirName(asset.mFilename);
std::string bin_file = dir + gDirUtilp->getDirDelimiter() + mUri;
std::ifstream file(bin_file, std::ios::binary);
if (!file.is_open())
{
LL_WARNS("GLTF") << "Failed to open file: " << bin_file << LL_ENDL;
return false;
}
file.seekg(0, std::ios::end);
if (mByteLength > file.tellg())
{
LL_WARNS("GLTF") << "Unexpected file size: " << bin_file << " is " << file.tellg() << " bytes, expected " << mByteLength << LL_ENDL;
return false;
}
file.seekg(0, std::ios::beg);
mData.resize(mByteLength);
file.read((char*)mData.data(), mData.size());
}
// POSTCONDITION: on success, mData.size == mByteLength
llassert(mData.size() == mByteLength);
return true;
}
bool Buffer::save(Asset& asset, const std::string& folder)
{
if (mUri.substr(0, 5) == "data:")
{
LL_WARNS("GLTF") << "Data URIs not yet supported" << LL_ENDL;
return false;
}
std::string bin_file = folder + gDirUtilp->getDirDelimiter();
if (mUri.empty())
{
if (mName.empty())
{
S32 idx = this - &asset.mBuffers[0];
mUri = llformat("buffer_%d.bin", idx);
}
else
{
mUri = mName + ".bin";
}
}
bin_file += mUri;
std::ofstream file(bin_file, std::ios::binary);
if (!file.is_open())
{
LL_WARNS("GLTF") << "Failed to open file: " << bin_file << LL_ENDL;
return false;
}
file.write((char*)mData.data(), mData.size());
return true;
}
void Buffer::serialize(object& dst) const
{
write(mName, "name", dst);
@ -132,6 +232,7 @@ const Buffer& Buffer::operator=(const Value& src)
{
copy(src, "name", mName);
copy(src, "uri", mUri);
copy(src, "byteLength", mByteLength);
// NOTE: DO NOT attempt to handle the uri here.
// The uri is a reference to a file that is not loaded until
@ -140,15 +241,6 @@ const Buffer& Buffer::operator=(const Value& src)
return *this;
}
const Buffer& Buffer::operator=(const tinygltf::Buffer& src)
{
mData = src.data;
mName = src.name;
mUri = src.uri;
return *this;
}
void BufferView::serialize(object& dst) const
{
write_always(mBuffer, "buffer", dst);
@ -173,43 +265,6 @@ const BufferView& BufferView::operator=(const Value& src)
return *this;
}
const BufferView& BufferView::operator=(const tinygltf::BufferView& src)
{
mBuffer = src.buffer;
mByteLength = src.byteLength;
mByteOffset = src.byteOffset;
mByteStride = src.byteStride;
mTarget = src.target;
mName = src.name;
return *this;
}
Accessor::Type tinygltf_type_to_enum(S32 type)
{
switch (type)
{
case TINYGLTF_TYPE_SCALAR:
return Accessor::Type::SCALAR;
case TINYGLTF_TYPE_VEC2:
return Accessor::Type::VEC2;
case TINYGLTF_TYPE_VEC3:
return Accessor::Type::VEC3;
case TINYGLTF_TYPE_VEC4:
return Accessor::Type::VEC4;
case TINYGLTF_TYPE_MAT2:
return Accessor::Type::MAT2;
case TINYGLTF_TYPE_MAT3:
return Accessor::Type::MAT3;
case TINYGLTF_TYPE_MAT4:
return Accessor::Type::MAT4;
}
LL_WARNS("GLTF") << "Unknown tinygltf accessor type: " << type << LL_ENDL;
llassert(false);
return Accessor::Type::SCALAR;
}
void Accessor::serialize(object& dst) const
{
write(mName, "name", dst);
@ -240,18 +295,3 @@ const Accessor& Accessor::operator=(const Value& src)
return *this;
}
const Accessor& Accessor::operator=(const tinygltf::Accessor& src)
{
mBufferView = src.bufferView;
mByteOffset = src.byteOffset;
mComponentType = src.componentType;
mCount = src.count;
mType = tinygltf_type_to_enum(src.type);
mNormalized = src.normalized;
mName = src.name;
mMax = src.maxValues;
mMin = src.minValues;
return *this;
}

View File

@ -26,7 +26,6 @@
* $/LicenseInfo$
*/
#include "../lltinygltfhelper.h"
#include "llstrider.h"
#include "boost/json.hpp"
@ -51,9 +50,12 @@ namespace LL
// also updates all buffer views in given asset that reference this buffer
void erase(Asset& asset, S32 offset, S32 length);
bool prep(Asset& asset);
void serialize(boost::json::object& obj) const;
const Buffer& operator=(const Value& value);
const Buffer& operator=(const tinygltf::Buffer& src);
bool save(Asset& asset, const std::string& folder);
};
class BufferView
@ -69,37 +71,44 @@ namespace LL
void serialize(boost::json::object& obj) const;
const BufferView& operator=(const Value& value);
const BufferView& operator=(const tinygltf::BufferView& src);
};
class Accessor
{
public:
S32 mBufferView = INVALID_INDEX;
S32 mByteOffset = 0;
S32 mComponentType = 0;
S32 mCount = 0;
std::vector<double> mMax;
std::vector<double> mMin;
enum class Type : S32
enum class Type : U8
{
SCALAR = TINYGLTF_TYPE_SCALAR,
VEC2 = TINYGLTF_TYPE_VEC2,
VEC3 = TINYGLTF_TYPE_VEC3,
VEC4 = TINYGLTF_TYPE_VEC4,
MAT2 = TINYGLTF_TYPE_MAT2,
MAT3 = TINYGLTF_TYPE_MAT3,
MAT4 = TINYGLTF_TYPE_MAT4
SCALAR,
VEC2,
VEC3,
VEC4,
MAT2,
MAT3,
MAT4
};
enum class ComponentType : U32
{
BYTE = 5120,
UNSIGNED_BYTE = 5121,
SHORT = 5122,
UNSIGNED_SHORT = 5123,
UNSIGNED_INT = 5125,
FLOAT = 5126
};
std::vector<double> mMax;
std::vector<double> mMin;
std::string mName;
S32 mBufferView = INVALID_INDEX;
S32 mByteOffset = 0;
ComponentType mComponentType = ComponentType::BYTE;
S32 mCount = 0;
Type mType = Type::SCALAR;
bool mNormalized = false;
std::string mName;
void serialize(boost::json::object& obj) const;
const Accessor& operator=(const Value& value);
const Accessor& operator=(const tinygltf::Accessor& src);
};
// convert from "SCALAR", "VEC2", etc to Accessor::Type

View File

@ -28,11 +28,12 @@
#include "asset.h"
#include "buffer_util.h"
#include "../llskinningutil.h"
using namespace LL::GLTF;
using namespace boost::json;
void Animation::allocateGLResources(Asset& asset)
bool Animation::prep(Asset& asset)
{
if (!mSamplers.empty())
{
@ -40,7 +41,10 @@ void Animation::allocateGLResources(Asset& asset)
mMaxTime = -FLT_MAX;
for (auto& sampler : mSamplers)
{
sampler.allocateGLResources(asset);
if (!sampler.prep(asset))
{
return false;
}
mMinTime = llmin(sampler.mMinTime, mMinTime);
mMaxTime = llmax(sampler.mMaxTime, mMaxTime);
}
@ -52,13 +56,21 @@ void Animation::allocateGLResources(Asset& asset)
for (auto& channel : mRotationChannels)
{
channel.allocateGLResources(asset, mSamplers[channel.mSampler]);
if (!channel.prep(asset, mSamplers[channel.mSampler]))
{
return false;
}
}
for (auto& channel : mTranslationChannels)
{
channel.allocateGLResources(asset, mSamplers[channel.mSampler]);
if (!channel.prep(asset, mSamplers[channel.mSampler]))
{
return false;
}
}
return true;
}
void Animation::update(Asset& asset, F32 dt)
@ -85,7 +97,7 @@ void Animation::apply(Asset& asset, float time)
}
};
void Animation::Sampler::allocateGLResources(Asset& asset)
bool Animation::Sampler::prep(Asset& asset)
{
Accessor& accessor = asset.mAccessors[mInput];
mMinTime = accessor.mMin[0];
@ -95,6 +107,8 @@ void Animation::Sampler::allocateGLResources(Asset& asset)
LLStrider<F32> frame_times = mFrameTimes.data();
copy(asset, accessor, frame_times);
return true;
}
@ -120,16 +134,6 @@ const Animation::Sampler& Animation::Sampler::operator=(const Value& src)
return *this;
}
const Animation::Sampler& Animation::Sampler::operator=(const tinygltf::AnimationSampler& src)
{
mInput = src.input;
mOutput = src.output;
mInterpolation = src.interpolation;
return *this;
}
bool Animation::Channel::Target::operator==(const Channel::Target& rhs) const
{
return mNode == rhs.mNode && mPath == rhs.mPath;
@ -172,17 +176,6 @@ const Animation::Channel& Animation::Channel::operator=(const Value& src)
return *this;
}
const Animation::Channel& Animation::Channel::operator=(const tinygltf::AnimationChannel& src)
{
mSampler = src.sampler;
mTarget.mNode = src.target_node;
mTarget.mPath = src.target_path;
return *this;
}
void Animation::Sampler::getFrameInfo(Asset& asset, F32 time, U32& frameIndex, F32& t)
{
LL_PROFILE_ZONE_SCOPED;
@ -223,11 +216,13 @@ void Animation::Sampler::getFrameInfo(Asset& asset, F32 time, U32& frameIndex, F
}
}
void Animation::RotationChannel::allocateGLResources(Asset& asset, Animation::Sampler& sampler)
bool Animation::RotationChannel::prep(Asset& asset, Animation::Sampler& sampler)
{
Accessor& accessor = asset.mAccessors[sampler.mOutput];
copy(asset, accessor, mRotations);
return true;
}
void Animation::RotationChannel::apply(Asset& asset, Sampler& sampler, F32 time)
@ -254,11 +249,13 @@ void Animation::RotationChannel::apply(Asset& asset, Sampler& sampler, F32 time)
}
}
void Animation::TranslationChannel::allocateGLResources(Asset& asset, Animation::Sampler& sampler)
bool Animation::TranslationChannel::prep(Asset& asset, Animation::Sampler& sampler)
{
Accessor& accessor = asset.mAccessors[sampler.mOutput];
copy(asset, accessor, mTranslations);
return true;
}
void Animation::TranslationChannel::apply(Asset& asset, Sampler& sampler, F32 time)
@ -286,11 +283,13 @@ void Animation::TranslationChannel::apply(Asset& asset, Sampler& sampler, F32 ti
}
}
void Animation::ScaleChannel::allocateGLResources(Asset& asset, Animation::Sampler& sampler)
bool Animation::ScaleChannel::prep(Asset& asset, Animation::Sampler& sampler)
{
Accessor& accessor = asset.mAccessors[sampler.mOutput];
copy(asset, accessor, mScales);
return true;
}
void Animation::ScaleChannel::apply(Asset& asset, Sampler& sampler, F32 time)
@ -364,47 +363,80 @@ const Animation& Animation::operator=(const Value& src)
return *this;
}
const Animation& Animation::operator=(const tinygltf::Animation& src)
Skin::~Skin()
{
mName = src.name;
mSamplers.resize(src.samplers.size());
for (U32 i = 0; i < src.samplers.size(); ++i)
if (mUBO)
{
mSamplers[i] = src.samplers[i];
glDeleteBuffers(1, &mUBO);
}
for (U32 i = 0; i < src.channels.size(); ++i)
{
if (src.channels[i].target_path == "rotation")
{
mRotationChannels.push_back(RotationChannel());
mRotationChannels.back() = src.channels[i];
}
if (src.channels[i].target_path == "translation")
{
mTranslationChannels.push_back(TranslationChannel());
mTranslationChannels.back() = src.channels[i];
}
if (src.channels[i].target_path == "scale")
{
mScaleChannels.push_back(ScaleChannel());
mScaleChannels.back() = src.channels[i];
}
}
return *this;
}
void Skin::allocateGLResources(Asset& asset)
void Skin::uploadMatrixPalette(Asset& asset)
{
// prepare matrix palette
U32 max_joints = LLSkinningUtil::getMaxGLTFJointCount();
if (mUBO == 0)
{
glGenBuffers(1, &mUBO);
}
U32 joint_count = llmin(max_joints, mJoints.size());
std::vector<mat4> t_mp;
t_mp.resize(joint_count);
for (U32 i = 0; i < joint_count; ++i)
{
Node& joint = asset.mNodes[mJoints[i]];
// build matrix palette in asset space
t_mp[i] = joint.mAssetMatrix * mInverseBindMatricesData[i];
}
std::vector<F32> glmp;
glmp.resize(joint_count * 12);
F32* mp = glmp.data();
for (U32 i = 0; i < joint_count; ++i)
{
F32* m = glm::value_ptr(t_mp[i]);
U32 idx = i * 12;
mp[idx + 0] = m[0];
mp[idx + 1] = m[1];
mp[idx + 2] = m[2];
mp[idx + 3] = m[12];
mp[idx + 4] = m[4];
mp[idx + 5] = m[5];
mp[idx + 6] = m[6];
mp[idx + 7] = m[13];
mp[idx + 8] = m[8];
mp[idx + 9] = m[9];
mp[idx + 10] = m[10];
mp[idx + 11] = m[14];
}
glBindBuffer(GL_UNIFORM_BUFFER, mUBO);
glBufferData(GL_UNIFORM_BUFFER, glmp.size() * sizeof(F32), glmp.data(), GL_STREAM_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
}
bool Skin::prep(Asset& asset)
{
if (mInverseBindMatrices != INVALID_INDEX)
{
Accessor& accessor = asset.mAccessors[mInverseBindMatrices];
copy(asset, accessor, mInverseBindMatricesData);
}
return true;
}
const Skin& Skin::operator=(const Value& src)
@ -419,16 +451,6 @@ const Skin& Skin::operator=(const Value& src)
return *this;
}
const Skin& Skin::operator=(const tinygltf::Skin& src)
{
mName = src.name;
mSkeleton = src.skeleton;
mInverseBindMatrices = src.inverseBindMatrices;
mJoints = src.joints;
return *this;
}
void Skin::serialize(object& obj) const
{
write(mInverseBindMatrices, "inverseBindMatrices", obj, INVALID_INDEX);

View File

@ -49,12 +49,10 @@ namespace LL
S32 mOutput = INVALID_INDEX;
std::string mInterpolation;
void allocateGLResources(Asset& asset);
bool prep(Asset& asset);
void serialize(boost::json::object& dst) const;
const Sampler& operator=(const Value& value);
const Sampler& operator=(const tinygltf::AnimationSampler& src);
// get the frame index and time for the specified time
// asset -- the asset to reference for Accessors
@ -85,7 +83,6 @@ namespace LL
void serialize(boost::json::object& dst) const;
const Channel& operator=(const Value& value);
const Channel& operator=(const tinygltf::AnimationChannel& src);
};
class RotationChannel : public Channel
@ -96,16 +93,10 @@ namespace LL
std::vector<quat> mRotations;
const RotationChannel& operator=(const tinygltf::AnimationChannel& src)
{
Channel::operator=(src);
return *this;
}
// prepare data needed for rendering
// asset -- asset to reference for Accessors
// sampler -- Sampler associated with this channel
void allocateGLResources(Asset& asset, Sampler& sampler);
bool prep(Asset& asset, Sampler& sampler);
void apply(Asset& asset, Sampler& sampler, F32 time);
};
@ -118,16 +109,10 @@ namespace LL
std::vector<vec3> mTranslations;
const TranslationChannel& operator=(const tinygltf::AnimationChannel& src)
{
Channel::operator=(src);
return *this;
}
// prepare data needed for rendering
// asset -- asset to reference for Accessors
// sampler -- Sampler associated with this channel
void allocateGLResources(Asset& asset, Sampler& sampler);
bool prep(Asset& asset, Sampler& sampler);
void apply(Asset& asset, Sampler& sampler, F32 time);
};
@ -140,16 +125,10 @@ namespace LL
std::vector<vec3> mScales;
const ScaleChannel& operator=(const tinygltf::AnimationChannel& src)
{
Channel::operator=(src);
return *this;
}
// prepare data needed for rendering
// asset -- asset to reference for Accessors
// sampler -- Sampler associated with this channel
void allocateGLResources(Asset& asset, Sampler& sampler);
bool prep(Asset& asset, Sampler& sampler);
void apply(Asset& asset, Sampler& sampler, F32 time);
};
@ -170,9 +149,8 @@ namespace LL
void serialize(boost::json::object& dst) const;
const Animation& operator=(const Value& value);
const Animation& operator=(const tinygltf::Animation& src);
void allocateGLResources(Asset& asset);
bool prep(Asset& asset);
void update(Asset& asset, float dt);

File diff suppressed because it is too large Load Diff

View File

@ -28,12 +28,12 @@
#include "llvertexbuffer.h"
#include "llvolumeoctree.h"
#include "../lltinygltfhelper.h"
#include "accessor.h"
#include "primitive.h"
#include "animation.h"
#include "boost/json.hpp"
#include "common.h"
#include "../llviewertexture.h"
extern F32SecondsImplicit gFrameTimeSeconds;
@ -49,10 +49,26 @@ namespace LL
{
class Asset;
class Extension
{
public:
// true if this extension is present in the gltf file
// otherwise false
bool mPresent = false;
};
class Material
{
public:
class Unlit : public Extension // KHR_materials_unlit implementation
{
public:
const Unlit& operator=(const Value& src);
void serialize(boost::json::object& dst) const;
};
enum class AlphaMode
{
OPAQUE,
@ -69,7 +85,6 @@ namespace LL
bool operator==(const TextureInfo& rhs) const;
bool operator!=(const TextureInfo& rhs) const;
const TextureInfo& operator=(const tinygltf::TextureInfo& src);
const TextureInfo& operator=(const Value& src);
void serialize(boost::json::object& dst) const;
};
@ -79,7 +94,6 @@ namespace LL
public:
F32 mScale = 1.0f;
const NormalTextureInfo& operator=(const tinygltf::NormalTextureInfo& src);
const NormalTextureInfo& operator=(const Value& src);
void serialize(boost::json::object& dst) const;
};
@ -89,7 +103,6 @@ namespace LL
public:
F32 mStrength = 1.0f;
const OcclusionTextureInfo& operator=(const tinygltf::OcclusionTextureInfo& src);
const OcclusionTextureInfo& operator=(const Value& src);
void serialize(boost::json::object& dst) const;
};
@ -105,35 +118,25 @@ namespace LL
bool operator==(const PbrMetallicRoughness& rhs) const;
bool operator!=(const PbrMetallicRoughness& rhs) const;
const PbrMetallicRoughness& operator=(const tinygltf::PbrMetallicRoughness& src);
const PbrMetallicRoughness& operator=(const Value& src);
void serialize(boost::json::object& dst) const;
};
// 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;
vec3 mEmissiveFactor = vec3(0.f, 0.f, 0.f);
AlphaMode mAlphaMode = AlphaMode::OPAQUE;
F32 mAlphaCutoff = 0.5f;
bool mDoubleSided = false;
Unlit mUnlit;
// bind for rendering
void bind(Asset& asset);
const Material& operator=(const tinygltf::Material& src);
const Material& operator=(const Value& src);
void serialize(boost::json::object& dst) const;
void allocateGLResources(Asset& asset);
};
class Mesh
@ -143,11 +146,10 @@ namespace LL
std::vector<double> mWeights;
std::string mName;
const Mesh& operator=(const tinygltf::Mesh& src);
const Mesh& operator=(const Value& src);
void serialize(boost::json::object& dst) const;
void allocateGLResources(Asset& asset);
bool prep(Asset& asset);
};
class Node
@ -178,7 +180,6 @@ namespace LL
std::string mName;
const Node& operator=(const tinygltf::Node& src);
const Node& operator=(const Value& src);
void serialize(boost::json::object& dst) const;
@ -211,16 +212,19 @@ namespace LL
class Skin
{
public:
~Skin();
S32 mInverseBindMatrices = INVALID_INDEX;
S32 mSkeleton = INVALID_INDEX;
U32 mUBO = 0;
std::vector<S32> mJoints;
std::string mName;
std::vector<mat4> mInverseBindMatricesData;
void allocateGLResources(Asset& asset);
void uploadMatrixPalette(Asset& asset, Node& node);
bool prep(Asset& asset);
void uploadMatrixPalette(Asset& asset);
const Skin& operator=(const tinygltf::Skin& src);
const Skin& operator=(const Value& src);
void serialize(boost::json::object& dst) const;
};
@ -231,7 +235,6 @@ namespace LL
std::vector<S32> mNodes;
std::string mName;
const Scene& operator=(const tinygltf::Scene& src);
const Scene& operator=(const Value& src);
void serialize(boost::json::object& dst) const;
@ -246,7 +249,6 @@ namespace LL
S32 mSource = INVALID_INDEX;
std::string mName;
const Texture& operator=(const tinygltf::Texture& src);
const Texture& operator=(const Value& src);
void serialize(boost::json::object& dst) const;
};
@ -260,7 +262,6 @@ namespace LL
S32 mWrapT = REPEAT;
std::string mName;
const Sampler& operator=(const tinygltf::Sampler& src);
const Sampler& operator=(const Value& src);
void serialize(boost::json::object& dst) const;
};
@ -274,7 +275,6 @@ namespace LL
S32 mBufferView = INVALID_INDEX;
std::vector<U8> mData;
S32 mWidth = -1;
S32 mHeight = -1;
S32 mComponent = -1;
@ -283,19 +283,20 @@ namespace LL
LLPointer<LLViewerFetchedTexture> mTexture;
const Image& operator=(const tinygltf::Image& src);
const Image& operator=(const Value& src);
void serialize(boost::json::object& dst) const;
// save image clear local data, and set uri
void decompose(Asset& asset, const std::string& filename);
// save image to disk
// may remove image data from bufferviews and convert to
// file uri if necessary
bool save(Asset& asset, const std::string& filename);
// erase the buffer view associated with this image
// free any associated resources
// free any associated GLTF resources
// preserve only uri and name
void clearData(Asset& asset);
void allocateGLResources();
bool prep(Asset& asset);
};
// C++ representation of a GLTF Asset
@ -316,22 +317,28 @@ namespace LL
std::vector<Accessor> mAccessors;
std::vector<Animation> mAnimations;
std::vector<Skin> mSkins;
std::vector<std::string> mExtensionsUsed;
std::vector<std::string> mExtensionsRequired;
std::string mVersion;
std::string mGenerator;
std::string mMinVersion;
std::string mCopyright;
S32 mDefaultScene = INVALID_INDEX;
S32 mScene = INVALID_INDEX;
Value mExtras;
U32 mPendingBuffers = 0;
// local file this asset was loaded from (if any)
std::string mFilename;
// the last time update() was called according to gFrameTimeSeconds
F32 mLastUpdateTime = gFrameTimeSeconds;
// prepare the asset for rendering
void allocateGLResources(const std::string& filename = "", const tinygltf::Model& model = tinygltf::Model());
// prepare for first time use
bool prep();
// Called periodically (typically once per frame)
// Any ongoing work (such as animations) should be handled here
@ -346,10 +353,6 @@ namespace LL
// update node render transforms
void updateRenderTransforms(const mat4& modelview);
void render(bool opaque, bool rigged = false);
void renderOpaque();
void renderTransparent();
// return the index of the node that the line segment intersects with, or -1 if no hit
// input and output values must be in this asset's local coordinate frame
S32 lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end,
@ -361,22 +364,33 @@ namespace LL
);
Asset() = default;
Asset(const tinygltf::Model& src);
Asset(const Value& src);
const Asset& operator=(const tinygltf::Model& src);
// load from given file
// accepts .gltf and .glb files
// Any existing data will be lost
// returns result of prep() on success
bool load(std::string_view filename);
// load .glb contents from memory
// data - binary contents of .glb file
// returns result of prep() on success
bool loadBinary(const std::string& data);
const Asset& operator=(const Value& src);
void serialize(boost::json::object& dst) const;
// 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);
// save the asset to the given .gltf file
// saves images and bins alongside the gltf file
bool save(const std::string& filename);
// remove the bufferview at the given index
// updates all bufferview indices in this Asset as needed
void eraseBufferView(S32 bufferView);
// return true if this Asset has been loaded as a local preview
// Local previews may be uploaded or exported to disk
bool isLocalPreview() { return !mFilename.empty(); }
};
Material::AlphaMode gltf_alpha_mode_to_enum(const std::string& alpha_mode);

View File

@ -170,6 +170,16 @@ namespace LL
dst.set(src[0], src[1], src[2], src[3]);
}
template<>
inline void copyVec4<U16, U64>(U16* src, U64& dst)
{
U16* data = (U16*)&dst;
data[0] = src[0];
data[1] = src[1];
data[2] = src[2];
data[3] = src[3];
}
template<>
inline void copyVec4<U16, LLColor4U>(U16* src, LLColor4U& dst)
{
@ -353,37 +363,29 @@ namespace LL
const Buffer& buffer = asset.mBuffers[bufferView.mBuffer];
const U8* src = buffer.mData.data() + bufferView.mByteOffset + accessor.mByteOffset;
if (accessor.mComponentType == TINYGLTF_COMPONENT_TYPE_FLOAT)
switch (accessor.mComponentType)
{
LL::GLTF::copy(asset, accessor, (const F32*)src, dst, bufferView.mByteStride);
}
else if (accessor.mComponentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT)
{
LL::GLTF::copy(asset, accessor, (const U16*)src, dst, bufferView.mByteStride);
}
else if (accessor.mComponentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT)
{
LL::GLTF::copy(asset, accessor, (const U32*)src, dst, bufferView.mByteStride);
}
else if (accessor.mComponentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE)
{
LL::GLTF::copy(asset, accessor, (const U8*)src, dst, bufferView.mByteStride);
}
else if (accessor.mComponentType == TINYGLTF_COMPONENT_TYPE_SHORT)
{
LL::GLTF::copy(asset, accessor, (const S16*)src, dst, bufferView.mByteStride);
}
else if (accessor.mComponentType == TINYGLTF_COMPONENT_TYPE_BYTE)
{
LL::GLTF::copy(asset, accessor, (const S8*)src, dst, bufferView.mByteStride);
}
else if (accessor.mComponentType == TINYGLTF_COMPONENT_TYPE_DOUBLE)
{
LL::GLTF::copy(asset, accessor, (const F64*)src, dst, bufferView.mByteStride);
}
else
{
LL_ERRS("GLTF") << "Unsupported component type" << LL_ENDL;
case Accessor::ComponentType::FLOAT:
copy(asset, accessor, (const F32*)src, dst, bufferView.mByteStride);
break;
case Accessor::ComponentType::UNSIGNED_INT:
copy(asset, accessor, (const U32*)src, dst, bufferView.mByteStride);
break;
case Accessor::ComponentType::SHORT:
copy(asset, accessor, (const S16*)src, dst, bufferView.mByteStride);
break;
case Accessor::ComponentType::UNSIGNED_SHORT:
copy(asset, accessor, (const U16*)src, dst, bufferView.mByteStride);
break;
case Accessor::ComponentType::BYTE:
copy(asset, accessor, (const S8*)src, dst, bufferView.mByteStride);
break;
case Accessor::ComponentType::UNSIGNED_BYTE:
copy(asset, accessor, (const U8*)src, dst, bufferView.mByteStride);
break;
default:
LL_ERRS("GLTF") << "Invalid component type" << LL_ENDL;
break;
}
}
@ -517,6 +519,104 @@ namespace LL
return true;
}
// to/from extension
// for internal use only, use copy_extensions instead
template<typename T>
inline bool _copy_extension(const boost::json::object& extensions, std::string_view member, T* dst)
{
if (extensions.contains(member))
{
return copy(extensions.at(member), *dst);
}
return false;
}
// Copy all extensions from src.extensions to provided destinations
// Usage:
// copy_extensions(src,
// "KHR_materials_unlit", &mUnlit,
// "KHR_materials_pbrSpecularGlossiness", &mPbrSpecularGlossiness);
// returns true if any of the extensions are copied
template<class... Types>
inline bool copy_extensions(const boost::json::value& src, Types... args)
{
// extract the extensions object (don't assume it exists and verify that it is an object)
if (src.is_object())
{
boost::json::object obj = src.get_object();
if (obj.contains("extensions"))
{
const boost::json::value& extensions = obj.at("extensions");
if (extensions.is_object())
{
const boost::json::object& ext_obj = extensions.as_object();
bool success = false;
// copy each extension, return true if any of them succeed, do not short circuit on success
U32 count = sizeof...(args);
for (U32 i = 0; i < count; i += 2)
{
if (_copy_extension(ext_obj, args...))
{
success = true;
}
}
return success;
}
}
}
return false;
}
// internal use aonly, use write_extensions instead
template<typename T>
inline bool _write_extension(boost::json::object& extensions, const T* src, string_view member)
{
if (src->mPresent)
{
Value v;
if (write(*src, v))
{
extensions[member] = v;
return true;
}
}
return false;
}
// Write all extensions to dst.extensions
// Usage:
// write_extensions(dst,
// "KHR_materials_unlit", mUnlit,
// "KHR_materials_pbrSpecularGlossiness", mPbrSpecularGlossiness);
// returns true if any of the extensions are written
template<class... Types>
inline bool write_extensions(boost::json::object& dst, Types... args)
{
bool success = false;
boost::json::object extensions;
U32 count = sizeof...(args) - 1;
for (U32 i = 0; i < count; i += 2)
{
if (_write_extension(extensions, args...))
{
success = true;
}
}
if (success)
{
dst["extensions"] = extensions;
}
return success;
}
// conditionally write a member to an object if the member
// is not the default value
template<typename T>
@ -571,6 +671,44 @@ namespace LL
return false;
}
// Accessor::ComponentType
template<>
inline bool copy(const Value& src, Accessor::ComponentType& dst)
{
if (src.is_int64())
{
dst = (Accessor::ComponentType)src.get_int64();
return true;
}
return false;
}
template<>
inline bool write(const Accessor::ComponentType& src, Value& dst)
{
dst = (S32)src;
return true;
}
//Primitive::Mode
template<>
inline bool copy(const Value& src, Primitive::Mode& dst)
{
if (src.is_int64())
{
dst = (Primitive::Mode)src.get_int64();
return true;
}
return false;
}
template<>
inline bool write(const Primitive::Mode& src, Value& dst)
{
dst = (S32)src;
return true;
}
// vec4
template<>
inline bool copy(const Value& src, vec4& dst)
@ -580,14 +718,17 @@ namespace LL
const boost::json::array& arr = src.as_array();
if (arr.size() == 4)
{
if (arr[0].is_double() &&
arr[1].is_double() &&
arr[2].is_double() &&
arr[3].is_double())
{
dst = vec4(arr[0].get_double(), arr[1].get_double(), arr[2].get_double(), arr[3].get_double());
return true;
}
vec4 v;
std::error_code ec;
v.x = arr[0].to_number<F32>(ec); if (ec) return false;
v.y = arr[1].to_number<F32>(ec); if (ec) return false;
v.z = arr[2].to_number<F32>(ec); if (ec) return false;
v.w = arr[3].to_number<F32>(ec); if (ec) return false;
dst = v;
return true;
}
}
return false;
@ -615,17 +756,13 @@ namespace LL
const boost::json::array& arr = src.as_array();
if (arr.size() == 4)
{
if (arr[0].is_double() &&
arr[1].is_double() &&
arr[2].is_double() &&
arr[3].is_double())
{
dst.x = arr[0].get_double();
dst.y = arr[1].get_double();
dst.z = arr[2].get_double();
dst.w = arr[3].get_double();
return true;
}
std::error_code ec;
dst.x = arr[0].to_number<F32>(ec); if (ec) return false;
dst.y = arr[1].to_number<F32>(ec); if (ec) return false;
dst.z = arr[2].to_number<F32>(ec); if (ec) return false;
dst.w = arr[3].to_number<F32>(ec); if (ec) return false;
return true;
}
}
return false;
@ -654,12 +791,13 @@ namespace LL
const boost::json::array& arr = src.as_array();
if (arr.size() == 3)
{
if (arr[0].is_double() &&
arr[1].is_double() &&
arr[2].is_double())
{
dst = vec3(arr[0].get_double(), arr[1].get_double(), arr[2].get_double());
}
std::error_code ec;
vec3 t;
t.x = arr[0].to_number<F32>(ec); if (ec) return false;
t.y = arr[1].to_number<F32>(ec); if (ec) return false;
t.z = arr[2].to_number<F32>(ec); if (ec) return false;
dst = t;
return true;
}
}
@ -701,12 +839,10 @@ namespace LL
template<>
inline bool copy(const Value& src, F32& dst)
{
if (src.is_double())
{
dst = src.get_double();
return true;
}
return false;
std::error_code ec;
F32 t = src.to_number<F32>(ec); if (ec) return false;
dst = t;
return true;
}
template<>
@ -740,12 +876,10 @@ namespace LL
template<>
inline bool copy(const Value& src, F64& dst)
{
if (src.is_double())
{
dst = src.get_double();
return true;
}
return false;
std::error_code ec;
F64 t = src.to_number<F64>(ec); if (ec) return false;
dst = t;
return true;
}
template<>
@ -830,11 +964,9 @@ namespace LL
for (U32 i = 0; i < arr.size(); ++i)
{
if (arr[i].is_double())
{
p[i] = arr[i].get_double();
}
else
std::error_code ec;
p[i] = arr[i].to_number<F32>(ec);
if (ec)
{
return false;
}

View File

@ -36,6 +36,7 @@
#include "glm/ext/quaternion_float.hpp"
#include "glm/gtx/quaternion.hpp"
#include "glm/gtx/matrix_decompose.hpp"
#include <boost/json.hpp>
// Common types and constants used in the GLTF implementation
namespace LL
@ -60,7 +61,23 @@ namespace LL
constexpr S32 MIRRORED_REPEAT = 33648;
constexpr S32 REPEAT = 10497;
class Asset;
class Material;
class Mesh;
class Node;
class Scene;
class Texture;
class Sampler;
class Image;
class Animation;
class Skin;
class Camera;
class Light;
class Primitive;
class Accessor;
class BufferView;
class Buffer;
}
}

View File

@ -30,12 +30,205 @@
#include "buffer_util.h"
#include "../llviewershadermgr.h"
#include "../lltinygltfhelper.h"
#include "mikktspace/mikktspace.hh"
#include "meshoptimizer/meshoptimizer.h"
using namespace LL::GLTF;
using namespace boost::json;
void Primitive::allocateGLResources(Asset& asset)
// Mesh data useful for Mikktspace tangent generation (and flat normal generation)
struct MikktMesh
{
std::vector<LLVector3> p;
std::vector<LLVector3> n;
std::vector<LLVector2> tc;
std::vector<LLVector4> w;
std::vector<LLVector4> t;
std::vector<LLColor4U> c;
std::vector<U64> j;
// initialize from src primitive and make an unrolled triangle list
// returns false if the Primitive cannot be converted to a triangle list
bool copy(const Primitive* prim)
{
bool indexed = !prim->mIndexArray.empty();
U32 vert_count = indexed ? prim->mIndexArray.size() : prim->mPositions.size();
if (prim->mMode != Primitive::Mode::TRIANGLES)
{
LL_WARNS("GLTF") << "Unsupported primitive mode for conversion to triangles: " << (S32) prim->mMode << LL_ENDL;
return false;
}
p.resize(vert_count);
n.resize(vert_count);
tc.resize(vert_count);
c.resize(vert_count);
bool has_normals = !prim->mNormals.empty();
if (has_normals)
{
n.resize(vert_count);
}
bool has_tangents = !prim->mTangents.empty();
if (has_tangents)
{
t.resize(vert_count);
}
bool rigged = !prim->mWeights.empty();
if (rigged)
{
w.resize(vert_count);
j.resize(vert_count);
}
for (int i = 0; i < vert_count; ++i)
{
U32 idx = indexed ? prim->mIndexArray[i] : i;
p[i].set(prim->mPositions[idx].getF32ptr());
tc[i].set(prim->mTexCoords[idx]);
c[i] = prim->mColors[idx];
if (has_normals)
{
n[i].set(prim->mNormals[idx].getF32ptr());
}
if (rigged)
{
w[i].set(prim->mWeights[idx].getF32ptr());
j[i] = prim->mJoints[idx];
}
}
return true;
}
void genNormals()
{
U32 tri_count = p.size() / 3;
for (U32 i = 0; i < tri_count; ++i)
{
LLVector3 v0 = p[i * 3];
LLVector3 v1 = p[i * 3 + 1];
LLVector3 v2 = p[i * 3 + 2];
LLVector3 normal = (v1 - v0) % (v2 - v0);
normal.normalize();
n[i * 3] = normal;
n[i * 3 + 1] = normal;
n[i * 3 + 2] = normal;
}
}
void genTangents()
{
t.resize(p.size());
mikk::Mikktspace ctx(*this);
ctx.genTangSpace();
}
// write to target primitive as an indexed triangle list
// Only modifies runtime data, does not modify the original GLTF data
void write(Primitive* prim) const
{
//re-weld
meshopt_Stream mos[] =
{
{ &p[0], sizeof(LLVector3), sizeof(LLVector3) },
{ &n[0], sizeof(LLVector3), sizeof(LLVector3) },
{ &t[0], sizeof(LLVector4), sizeof(LLVector4) },
{ &tc[0], sizeof(LLVector2), sizeof(LLVector2) },
{ &c[0], sizeof(LLColor4U), sizeof(LLColor4U) },
{ w.empty() ? nullptr : &w[0], sizeof(LLVector4), sizeof(LLVector4) },
{ j.empty() ? nullptr : &j[0], sizeof(U64), sizeof(U64) }
};
std::vector<U32> remap;
remap.resize(p.size());
U32 stream_count = w.empty() ? 5 : 7;
size_t vert_count = meshopt_generateVertexRemapMulti(&remap[0], nullptr, p.size(), p.size(), mos, stream_count);
prim->mTexCoords.resize(vert_count);
prim->mNormals.resize(vert_count);
prim->mTangents.resize(vert_count);
prim->mPositions.resize(vert_count);
prim->mColors.resize(vert_count);
if (!w.empty())
{
prim->mWeights.resize(vert_count);
prim->mJoints.resize(vert_count);
}
prim->mIndexArray.resize(remap.size());
for (int i = 0; i < remap.size(); ++i)
{
U32 src_idx = i;
U32 dst_idx = remap[i];
prim->mIndexArray[i] = dst_idx;
prim->mPositions[dst_idx].load3(p[src_idx].mV);
prim->mNormals[dst_idx].load3(n[src_idx].mV);
prim->mTexCoords[dst_idx] = tc[src_idx];
prim->mTangents[dst_idx].loadua(t[src_idx].mV);
prim->mColors[dst_idx] = c[src_idx];
if (!w.empty())
{
prim->mWeights[dst_idx].loadua(w[src_idx].mV);
prim->mJoints[dst_idx] = j[src_idx];
}
}
prim->mGLMode = LLRender::TRIANGLES;
}
uint32_t GetNumFaces()
{
return uint32_t(p.size()/3);
}
uint32_t GetNumVerticesOfFace(const uint32_t face_num)
{
return 3;
}
mikk::float3 GetPosition(const uint32_t face_num, const uint32_t vert_num)
{
F32* v = p[face_num * 3 + vert_num].mV;
return mikk::float3(v);
}
mikk::float3 GetTexCoord(const uint32_t face_num, const uint32_t vert_num)
{
F32* uv = tc[face_num * 3 + vert_num].mV;
return mikk::float3(uv[0], uv[1], 1.0f);
}
mikk::float3 GetNormal(const uint32_t face_num, const uint32_t vert_num)
{
F32* normal = n[face_num * 3 + vert_num].mV;
return mikk::float3(normal);
}
void SetTangentSpace(const uint32_t face_num, const uint32_t vert_num, mikk::float3 T, bool orientation)
{
S32 i = face_num * 3 + vert_num;
t[i].set(T.x, T.y, T.z, orientation ? 1.0f : -1.0f);
}
};
bool Primitive::prep(Asset& asset)
{
// allocate vertex buffer
// We diverge from the intent of the GLTF format here to work with our existing render pipeline
@ -85,28 +278,23 @@ void Primitive::allocateGLResources(Asset& asset)
{
Accessor& accessor = asset.mAccessors[mIndices];
copy(asset, accessor, mIndexArray);
for (auto& idx : mIndexArray)
{
if (idx >= mPositions.size())
{
LL_WARNS("GLTF") << "Invalid index array" << LL_ENDL;
return false;
}
}
}
U32 mask = ATTRIBUTE_MASK;
U32 mask = LLVertexBuffer::MAP_VERTEX;
if (!mWeights.empty())
{
mask |= LLVertexBuffer::MAP_WEIGHT4;
}
if (LLGLSLShader::sCurBoundShaderPtr == nullptr)
{ // make sure a shader is bound to satisfy mVertexBuffer->setBuffer
gDebugProgram.bind();
}
mVertexBuffer = new LLVertexBuffer(mask);
mVertexBuffer->allocateBuffer(mPositions.size(), mIndexArray.size()*2); // double the size of the index buffer for 32-bit indices
mVertexBuffer->setBuffer();
mVertexBuffer->setPositionData(mPositions.data());
if (!mIndexArray.empty())
{
mVertexBuffer->setIndexData(mIndexArray.data());
mask |= LLVertexBuffer::MAP_JOINT;
}
if (mTexCoords.empty())
@ -114,23 +302,21 @@ void Primitive::allocateGLResources(Asset& asset)
mTexCoords.resize(mPositions.size());
}
// flip texcoord y, upload, then flip back (keep the off-spec data in vram only)
for (auto& tc : mTexCoords)
{
tc[1] = 1.f - tc[1];
}
mVertexBuffer->setTexCoordData(mTexCoords.data());
for (auto& tc : mTexCoords)
{
tc[1] = 1.f - tc[1];
}
// TODO: support more than one texcoord set (or no texcoords)
mask |= LLVertexBuffer::MAP_TEXCOORD0;
if (mColors.empty())
{
mColors.resize(mPositions.size(), LLColor4U::white);
}
// TODO: support colorless vertex buffers
mask |= LLVertexBuffer::MAP_COLOR;
mShaderVariant = 0;
bool unlit = false;
// bake material basecolor into color array
if (mMaterial != INVALID_INDEX)
{
@ -140,45 +326,140 @@ void Primitive::allocateGLResources(Asset& asset)
{
dst = LLColor4U(baseColor * LLColor4(dst));
}
if (material.mUnlit.mPresent)
{ // material uses KHR_materials_unlit
mShaderVariant |= LLGLSLShader::GLTFVariant::UNLIT;
unlit = true;
}
}
if (mNormals.empty() && !unlit)
{
mTangents.clear();
if (mMode == Mode::POINTS || mMode == Mode::LINES || mMode == Mode::LINE_LOOP || mMode == Mode::LINE_STRIP)
{ //no normals and no surfaces, this primitive is unlit
mTangents.clear();
mShaderVariant |= LLGLSLShader::GLTFVariant::UNLIT;
unlit = true;
}
else
{
// unroll into non-indexed array of flat shaded triangles
MikktMesh data;
if (!data.copy(this))
{
return false;
}
data.genNormals();
data.genTangents();
data.write(this);
}
}
if (mTangents.empty() && !unlit)
{ // NOTE: must be done last because tangent generation rewrites the other arrays
// adapted from usage of Mikktspace in llvolume.cpp
if (mMode == Mode::POINTS || mMode == Mode::LINES || mMode == Mode::LINE_LOOP || mMode == Mode::LINE_STRIP)
{
// for points and lines, just make sure tangent is perpendicular to normal
mTangents.resize(mNormals.size());
LLVector4a up(0.f, 0.f, 1.f, 0.f);
LLVector4a left(1.f, 0.f, 0.f, 0.f);
for (U32 i = 0; i < mNormals.size(); ++i)
{
if (fabsf(mNormals[i].getF32ptr()[2]) < 0.999f)
{
mTangents[i] = up.cross3(mNormals[i]);
}
else
{
mTangents[i] = left.cross3(mNormals[i]);
}
mTangents[i].getF32ptr()[3] = 1.f;
}
}
else
{
MikktMesh data;
if (!data.copy(this))
{
return false;
}
data.genTangents();
data.write(this);
}
}
if (!mNormals.empty())
{
mask |= LLVertexBuffer::MAP_NORMAL;
}
if (!mTangents.empty())
{
mask |= LLVertexBuffer::MAP_TANGENT;
}
if (LLGLSLShader::sCurBoundShaderPtr == nullptr)
{ // make sure a shader is bound to satisfy mVertexBuffer->setBuffer
gDebugProgram.bind();
}
mVertexBuffer = new LLVertexBuffer(mask);
mVertexBuffer->allocateBuffer(mPositions.size(), mIndexArray.size() * 2); // double the size of the index buffer for 32-bit indices
mVertexBuffer->setBuffer();
mVertexBuffer->setPositionData(mPositions.data());
mVertexBuffer->setColorData(mColors.data());
if (mNormals.empty())
if (!mNormals.empty())
{
mNormals.resize(mPositions.size(), LLVector4a(0, 0, 1, 0));
mVertexBuffer->setNormalData(mNormals.data());
}
mVertexBuffer->setNormalData(mNormals.data());
if (mTangents.empty())
if (!mTangents.empty())
{
// TODO: generate tangents if needed
mTangents.resize(mPositions.size(), LLVector4a(1, 0, 0, 1));
mVertexBuffer->setTangentData(mTangents.data());
}
mVertexBuffer->setTangentData(mTangents.data());
if (!mWeights.empty())
{
std::vector<LLVector4a> weight_data;
weight_data.resize(mWeights.size());
mShaderVariant |= LLGLSLShader::GLTFVariant::RIGGED;
mVertexBuffer->setWeight4Data(mWeights.data());
mVertexBuffer->setJointData(mJoints.data());
}
F32 max_weight = 1.f - FLT_EPSILON*100.f;
LLVector4a maxw(max_weight, max_weight, max_weight, max_weight);
for (U32 i = 0; i < mWeights.size(); ++i)
{
LLVector4a& w = weight_data[i];
w.setMin(mWeights[i], maxw);
w.add(mJoints[i]);
};
// flip texcoord y, upload, then flip back (keep the off-spec data in vram only)
for (auto& tc : mTexCoords)
{
tc[1] = 1.f - tc[1];
}
mVertexBuffer->setTexCoordData(mTexCoords.data());
for (auto& tc : mTexCoords)
{
tc[1] = 1.f - tc[1];
}
mVertexBuffer->setWeight4Data(weight_data.data());
if (!mIndexArray.empty())
{
mVertexBuffer->setIndexData(mIndexArray.data());
}
createOctree();
mVertexBuffer->unbind();
Material& material = asset.mMaterials[mMaterial];
if (material.mAlphaMode == Material::AlphaMode::BLEND)
{
mShaderVariant |= LLGLSLShader::GLTFVariant::ALPHA_BLEND;
}
return true;
}
void initOctreeTriangle(LLVolumeTriangle* tri, F32 scaler, S32 i0, S32 i1, S32 i2, const LLVector4a& v0, const LLVector4a& v1, const LLVector4a& v2)
@ -224,7 +505,7 @@ void Primitive::createOctree()
F32 scaler = 0.25f;
if (mMode == TINYGLTF_MODE_TRIANGLES)
if (mMode == Mode::TRIANGLES)
{
const U32 num_triangles = mVertexBuffer->getNumIndices() / 3;
// Initialize all the triangles we need
@ -248,7 +529,7 @@ void Primitive::createOctree()
mOctree->insert(tri);
}
}
else if (mMode == TINYGLTF_MODE_TRIANGLE_STRIP)
else if (mMode == Mode::TRIANGLE_STRIP)
{
const U32 num_triangles = mVertexBuffer->getNumIndices() - 2;
// Initialize all the triangles we need
@ -272,7 +553,7 @@ void Primitive::createOctree()
mOctree->insert(tri);
}
}
else if (mMode == TINYGLTF_MODE_TRIANGLE_FAN)
else if (mMode == Mode::TRIANGLE_FAN)
{
const U32 num_triangles = mVertexBuffer->getNumIndices() - 2;
// Initialize all the triangles we need
@ -296,10 +577,10 @@ void Primitive::createOctree()
mOctree->insert(tri);
}
}
else if (mMode == TINYGLTF_MODE_POINTS ||
mMode == TINYGLTF_MODE_LINE ||
mMode == TINYGLTF_MODE_LINE_LOOP ||
mMode == TINYGLTF_MODE_LINE_STRIP)
else if (mMode == Mode::POINTS ||
mMode == Mode::LINES ||
mMode == Mode::LINE_LOOP ||
mMode == Mode::LINE_STRIP)
{
// nothing to do, no volume... maybe add some collision geometry around these primitive types?
}
@ -357,23 +638,23 @@ Primitive::~Primitive()
mOctree = nullptr;
}
U32 gltf_mode_to_gl_mode(U32 mode)
LLRender::eGeomModes gltf_mode_to_gl_mode(Primitive::Mode mode)
{
switch (mode)
{
case TINYGLTF_MODE_POINTS:
case Primitive::Mode::POINTS:
return LLRender::POINTS;
case TINYGLTF_MODE_LINE:
case Primitive::Mode::LINES:
return LLRender::LINES;
case TINYGLTF_MODE_LINE_LOOP:
case Primitive::Mode::LINE_LOOP:
return LLRender::LINE_LOOP;
case TINYGLTF_MODE_LINE_STRIP:
case Primitive::Mode::LINE_STRIP:
return LLRender::LINE_STRIP;
case TINYGLTF_MODE_TRIANGLES:
case Primitive::Mode::TRIANGLES:
return LLRender::TRIANGLES;
case TINYGLTF_MODE_TRIANGLE_STRIP:
case Primitive::Mode::TRIANGLE_STRIP:
return LLRender::TRIANGLE_STRIP;
case TINYGLTF_MODE_TRIANGLE_FAN:
case Primitive::Mode::TRIANGLE_FAN:
return LLRender::TRIANGLE_FAN;
default:
return LLRender::TRIANGLES;
@ -383,7 +664,7 @@ U32 gltf_mode_to_gl_mode(U32 mode)
void Primitive::serialize(boost::json::object& dst) const
{
write(mMaterial, "material", dst, -1);
write(mMode, "mode", dst, TINYGLTF_MODE_TRIANGLES);
write(mMode, "mode", dst, Primitive::Mode::TRIANGLES);
write(mIndices, "indices", dst, INVALID_INDEX);
write(mAttributes, "attributes", dst);
}
@ -402,24 +683,3 @@ const Primitive& Primitive::operator=(const Value& src)
return *this;
}
const Primitive& Primitive::operator=(const tinygltf::Primitive& src)
{
// load material
mMaterial = src.material;
// load mode
mMode = src.mode;
// load indices
mIndices = src.indices;
// load attributes
for (auto& it : src.attributes)
{
mAttributes[it.first] = it.second;
}
mGLMode = gltf_mode_to_gl_mode(mMode);
return *this;
}

View File

@ -38,27 +38,31 @@ namespace LL
using Value = boost::json::value;
class Asset;
constexpr U32 ATTRIBUTE_MASK =
LLVertexBuffer::MAP_VERTEX |
LLVertexBuffer::MAP_NORMAL |
LLVertexBuffer::MAP_TEXCOORD0 |
LLVertexBuffer::MAP_TANGENT |
LLVertexBuffer::MAP_COLOR;
class Primitive
{
public:
enum class Mode : U8
{
POINTS,
LINES,
LINE_LOOP,
LINE_STRIP,
TRIANGLES,
TRIANGLE_STRIP,
TRIANGLE_FAN
};
~Primitive();
// GPU copy of mesh data
LLPointer<LLVertexBuffer> mVertexBuffer;
// CPU copy of mesh data
// CPU copy of mesh data, keep these as LLVector types for compatibility with raycasting code
std::vector<LLVector2> mTexCoords;
std::vector<LLVector4a> mNormals;
std::vector<LLVector4a> mTangents;
std::vector<LLVector4a> mPositions;
std::vector<LLVector4a> mJoints;
std::vector<U64> mJoints;
std::vector<LLVector4a> mWeights;
std::vector<LLColor4U> mColors;
std::vector<U32> mIndexArray;
@ -68,9 +72,13 @@ namespace LL
std::vector<LLVolumeTriangle> mOctreeTriangles;
S32 mMaterial = -1;
S32 mMode = TINYGLTF_MODE_TRIANGLES; // default to triangles
U32 mGLMode = LLRender::TRIANGLES;
Mode mMode = Mode::TRIANGLES; // default to triangles
LLRender::eGeomModes mGLMode = LLRender::TRIANGLES; // for use with LLRender
S32 mIndices = -1;
// shader variant according to LLGLSLShader::GLTFVariant flags
U8 mShaderVariant = 0;
std::unordered_map<std::string, S32> mAttributes;
// create octree based on vertex buffer
@ -89,9 +97,8 @@ namespace LL
void serialize(boost::json::object& obj) const;
const Primitive& operator=(const Value& src);
const Primitive& operator=(const tinygltf::Primitive& src);
void allocateGLResources(Asset& asset);
bool prep(Asset& asset);
};
}
}

View File

@ -107,32 +107,6 @@ void GLTFSceneManager::saveAs()
}
}
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::uploadSelection()
{
if (mUploadingAsset)
@ -153,77 +127,86 @@ void GLTFSceneManager::uploadSelection()
for (auto& image : asset.mImages)
{
if (!image.mData.empty())
if (image.mTexture.notNull())
{
mPendingImageUploads++;
LLPointer<LLImageRaw> raw = new LLImageRaw(image.mWidth, image.mHeight, image.mComponent);
U8* data = raw->allocateData();
llassert_always(image.mData.size() == raw->getDataSize());
memcpy(data, image.mData.data(), image.mData.size());
LLPointer<LLImageRaw> raw;
// for GLTF native content, store image in GLTF orientation
raw->verticalFlip();
LLPointer<LLImageJ2C> j2c = LLViewerTextureList::convertToUploadFile(raw);
std::string buffer;
buffer.assign((const char*)j2c->getData(), j2c->getDataSize());
LLUUID asset_id = LLUUID::generateNewID();
std::string name;
S32 idx = (S32)(&image - &asset.mImages[0]);
if (image.mName.empty())
if (image.mBufferView != INVALID_INDEX)
{
BufferView& view = asset.mBufferViews[image.mBufferView];
Buffer& buffer = asset.mBuffers[view.mBuffer];
name = llformat("Image_%d", idx);
raw = LLViewerTextureManager::getRawImageFromMemory(buffer.mData.data() + view.mByteOffset, view.mByteLength, image.mMimeType);
image.clearData(asset);
}
else
{
name = image.mName;
raw = image.mTexture->getCachedRawImage();
}
LLNewBufferedResourceUploadInfo::uploadFailure_f failure = [this](LLUUID assetId, LLSD response, std::string reason)
{
// TODO: handle failure
mPendingImageUploads--;
return false;
};
if (raw.notNull())
{
LLPointer<LLImageJ2C> j2c = LLViewerTextureList::convertToUploadFile(raw);
std::string buffer;
buffer.assign((const char*)j2c->getData(), j2c->getDataSize());
LLNewBufferedResourceUploadInfo::uploadFinish_f finish = [this, idx, raw, j2c](LLUUID assetId, LLSD response)
LLUUID asset_id = LLUUID::generateNewID();
std::string name;
S32 idx = (S32)(&image - &asset.mImages[0]);
if (image.mName.empty())
{
if (mUploadingAsset && mUploadingAsset->mImages.size() > idx)
name = llformat("Image_%d", idx);
}
else
{
name = image.mName;
}
LLNewBufferedResourceUploadInfo::uploadFailure_f failure = [this](LLUUID assetId, LLSD response, std::string reason)
{
mUploadingAsset->mImages[idx].mUri = assetId.asString();
// TODO: handle failure
mPendingImageUploads--;
}
};
return false;
};
S32 expected_upload_cost = LLAgentBenefitsMgr::current().getTextureUploadCost(j2c);
LLResourceUploadInfo::ptr_t uploadInfo(std::make_shared<LLNewBufferedResourceUploadInfo>(
buffer,
asset_id,
name,
name,
0,
LLFolderType::FT_TEXTURE,
LLInventoryType::IT_TEXTURE,
LLAssetType::AT_TEXTURE,
LLFloaterPerms::getNextOwnerPerms("Uploads"),
LLFloaterPerms::getGroupPerms("Uploads"),
LLFloaterPerms::getEveryonePerms("Uploads"),
expected_upload_cost,
false,
finish,
failure));
LLNewBufferedResourceUploadInfo::uploadFinish_f finish = [this, idx, raw, j2c](LLUUID assetId, LLSD response)
{
if (mUploadingAsset && mUploadingAsset->mImages.size() > idx)
{
mUploadingAsset->mImages[idx].mUri = assetId.asString();
mPendingImageUploads--;
}
};
upload_new_resource(uploadInfo);
S32 expected_upload_cost = LLAgentBenefitsMgr::current().getTextureUploadCost(j2c);
image.clearData(asset);
LLResourceUploadInfo::ptr_t uploadInfo(std::make_shared<LLNewBufferedResourceUploadInfo>(
buffer,
asset_id,
name,
name,
0,
LLFolderType::FT_TEXTURE,
LLInventoryType::IT_TEXTURE,
LLAssetType::AT_TEXTURE,
LLFloaterPerms::getNextOwnerPerms("Uploads"),
LLFloaterPerms::getGroupPerms("Uploads"),
LLFloaterPerms::getEveryonePerms("Uploads"),
expected_upload_cost,
false,
finish,
failure));
upload_new_resource(uploadInfo);
}
}
}
@ -297,62 +280,45 @@ void GLTFSceneManager::uploadSelection()
}
}
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);
if (!asset->save(filename))
{
LLNotificationsUtil::add("GLTFSaveFailed");
}
}
}
void GLTFSceneManager::load(const std::string& filename)
{
tinygltf::Model model;
LLTinyGLTFHelper::loadModel(filename, model);
std::shared_ptr<Asset> asset = std::make_shared<Asset>();
*asset = model;
gDebugProgram.bind(); // bind a shader to satisfy LLVertexBuffer assertions
asset->allocateGLResources(filename, model);
asset->updateTransforms();
if (asset->load(filename))
{
gDebugProgram.bind(); // bind a shader to satisfy LLVertexBuffer assertions
asset->updateTransforms();
// hang the asset off the currently selected object, or off of the avatar if no object is selected
LLViewerObject* obj = LLSelectMgr::instance().getSelection()->getFirstRootObject();
// hang the asset off the currently selected object, or off of the avatar if no object is selected
LLViewerObject* obj = LLSelectMgr::instance().getSelection()->getFirstRootObject();
if (obj)
{ // assign to self avatar
obj->mGLTFAsset = asset;
obj->markForUpdate();
if (std::find(mObjects.begin(), mObjects.end(), obj) == mObjects.end())
{
mObjects.push_back(obj);
if (obj)
{ // assign to self avatar
obj->mGLTFAsset = asset;
obj->markForUpdate();
if (std::find(mObjects.begin(), mObjects.end(), obj) == mObjects.end())
{
mObjects.push_back(obj);
}
}
}
else
{
LLNotificationsUtil::add("GLTFLoadFailed");
}
}
GLTFSceneManager::~GLTFSceneManager()
@ -392,32 +358,26 @@ void GLTFSceneManager::onGLTFBinLoadComplete(const LLUUID& id, LLAssetType::ETyp
// find the Buffer with the given id in the asset
if (obj->mGLTFAsset)
{
for (auto& buffer : obj->mGLTFAsset->mBuffers)
obj->mGLTFAsset->mPendingBuffers--;
if (obj->mGLTFAsset->mPendingBuffers == 0)
{
LLUUID buffer_id;
if (LLUUID::parseUUID(buffer.mUri, &buffer_id) && buffer_id == id)
if (obj->mGLTFAsset->prep())
{
LLFileSystem file(id, asset_type, LLFileSystem::READ);
buffer.mData.resize(file.getSize());
file.read((U8*)buffer.mData.data(), buffer.mData.size());
obj->mGLTFAsset->mPendingBuffers--;
if (obj->mGLTFAsset->mPendingBuffers == 0)
GLTFSceneManager& mgr = GLTFSceneManager::instance();
if (std::find(mgr.mObjects.begin(), mgr.mObjects.end(), obj) == mgr.mObjects.end())
{
obj->mGLTFAsset->allocateGLResources();
GLTFSceneManager& mgr = GLTFSceneManager::instance();
if (std::find(mgr.mObjects.begin(), mgr.mObjects.end(), obj) == mgr.mObjects.end())
{
GLTFSceneManager::instance().mObjects.push_back(obj);
}
GLTFSceneManager::instance().mObjects.push_back(obj);
}
}
else
{
LL_WARNS("GLTF") << "Failed to prepare GLTF asset: " << id << LL_ENDL;
obj->mGLTFAsset = nullptr;
}
}
}
}
}
else
@ -492,30 +452,9 @@ void GLTFSceneManager::update()
{
if (mPendingImageUploads == 0 && mPendingBinaryUploads == 0)
{
std::string filename(gDirUtilp->getTempDir() + "/upload.gltf");
#if 0
tinygltf::Model model;
mUploadingAsset->save(model);
tinygltf::TinyGLTF writer;
writer.WriteGltfSceneToFile(&model, filename, false, false, true, false);
#else
boost::json::object obj;
mUploadingAsset->serialize(obj);
std::string json = boost::json::serialize(obj, {});
{
std::ofstream o(filename);
o << json;
}
#endif
std::ifstream t(filename);
std::stringstream str;
str << t.rdbuf();
std::string buffer = str.str();
std::string buffer = boost::json::serialize(obj, {});
LLNewBufferedResourceUploadInfo::uploadFailure_f failure = [this](LLUUID assetId, LLSD response, std::string reason)
{
@ -589,7 +528,26 @@ void GLTFSceneManager::update()
}
}
void GLTFSceneManager::render(bool opaque, bool rigged)
void GLTFSceneManager::render(bool opaque, bool rigged, bool unlit)
{
U8 variant = 0;
if (rigged)
{
variant |= LLGLSLShader::GLTFVariant::RIGGED;
}
if (!opaque)
{
variant |= LLGLSLShader::GLTFVariant::ALPHA_BLEND;
}
if (unlit)
{
variant |= LLGLSLShader::GLTFVariant::UNLIT;
}
render(variant);
}
void GLTFSceneManager::render(U8 variant)
{
// for debugging, just render the whole scene as opaque
// by traversing the whole scenegraph
@ -598,6 +556,8 @@ void GLTFSceneManager::render(bool opaque, bool rigged)
gGL.matrixMode(LLRender::MM_MODELVIEW);
bool rigged = variant & LLGLSLShader::GLTFVariant::RIGGED;
for (U32 i = 0; i < mObjects.size(); ++i)
{
if (mObjects[i]->isDead() || mObjects[i]->mGLTFAsset == nullptr)
@ -608,7 +568,6 @@ void GLTFSceneManager::render(bool opaque, bool rigged)
}
Asset* asset = mObjects[i]->mGLTFAsset.get();
gGL.pushMatrix();
LLMatrix4a mat = mObjects[i]->getGLTFAssetToAgentTransform();
@ -620,12 +579,172 @@ void GLTFSceneManager::render(bool opaque, bool rigged)
mat4 mdv = glm::make_mat4(modelview.getF32ptr());
asset->updateRenderTransforms(mdv);
asset->render(opaque, rigged);
if (rigged)
{ // provide a modelview matrix that goes from asset to camera space for rigged render passes
// (matrix palettes are in asset space)
gGL.loadMatrix(glm::value_ptr(mdv));
}
render(*asset, variant);
gGL.popMatrix();
}
}
void GLTFSceneManager::render(Asset& asset, U8 variant)
{
bool opaque = !(variant & LLGLSLShader::GLTFVariant::ALPHA_BLEND);
bool rigged = variant & LLGLSLShader::GLTFVariant::RIGGED;
if (opaque)
{
gGLTFPBRMetallicRoughnessProgram.bind(variant);
}
else
{ // alpha shaders need all the shadow map setup etc
gPipeline.bindDeferredShader(gGLTFPBRMetallicRoughnessProgram.mGLTFVariants[variant]);
}
for (auto& node : asset.mNodes)
{
if (node.mSkin != INVALID_INDEX)
{
if (rigged)
{
Skin& skin = asset.mSkins[node.mSkin];
glBindBufferBase(GL_UNIFORM_BUFFER, LLGLSLShader::UB_GLTF_JOINTS, skin.mUBO);
}
}
if (node.mMesh != INVALID_INDEX)
{
Mesh& mesh = asset.mMeshes[node.mMesh];
for (auto& primitive : mesh.mPrimitives)
{
if (primitive.mShaderVariant != variant)
{
continue;
}
if (!rigged)
{
gGL.loadMatrix((F32*)glm::value_ptr(node.mRenderMatrix));
}
bool cull = true;
if (primitive.mMaterial != INVALID_INDEX)
{
Material& material = asset.mMaterials[primitive.mMaterial];
bind(asset, material);
cull = !material.mDoubleSided;
}
else
{
LLFetchedGLTFMaterial::sDefault.bind();
}
LLGLDisable cull_face(!cull ? GL_CULL_FACE : 0);
primitive.mVertexBuffer->setBuffer();
if (primitive.mVertexBuffer->getNumIndices() > 0)
{
primitive.mVertexBuffer->draw(primitive.mGLMode, primitive.mVertexBuffer->getNumIndices(), 0);
}
else
{
primitive.mVertexBuffer->drawArrays(primitive.mGLMode, 0, primitive.mVertexBuffer->getNumVerts());
}
}
}
}
}
static void bindTexture(Asset& asset, S32 uniform, Material::TextureInfo& info, LLViewerTexture* fallback)
{
if (info.mIndex != INVALID_INDEX)
{
LLViewerTexture* tex = asset.mImages[asset.mTextures[info.mIndex].mSource].mTexture;
if (tex)
{
tex->addTextureStats(2048.f * 2048.f);
LLGLSLShader::sCurBoundShaderPtr->bindTexture(uniform, tex);
}
else
{
LLGLSLShader::sCurBoundShaderPtr->bindTexture(uniform, fallback);
}
}
else
{
LLGLSLShader::sCurBoundShaderPtr->bindTexture(uniform, fallback);
}
}
void GLTFSceneManager::bind(Asset& asset, Material& material)
{
// bind for rendering (derived from LLFetchedGLTFMaterial::bind)
// glTF 2.0 Specification 3.9.4. Alpha Coverage
// mAlphaCutoff is only valid for LLGLTFMaterial::ALPHA_MODE_MASK
F32 min_alpha = -1.0;
LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr;
if (!LLPipeline::sShadowRender || (material.mAlphaMode == Material::AlphaMode::BLEND))
{
if (material.mAlphaMode == Material::AlphaMode::MASK)
{
// dividing the alpha cutoff by transparency here allows the shader to compare against
// the alpha value of the texture without needing the transparency value
if (material.mPbrMetallicRoughness.mBaseColorFactor.a > 0.f)
{
min_alpha = material.mAlphaCutoff / material.mPbrMetallicRoughness.mBaseColorFactor.a;
}
else
{
min_alpha = 1024.f;
}
}
shader->uniform1f(LLShaderMgr::MINIMUM_ALPHA, min_alpha);
}
bindTexture(asset, LLShaderMgr::DIFFUSE_MAP, material.mPbrMetallicRoughness.mBaseColorTexture, LLViewerFetchedTexture::sWhiteImagep);
F32 base_color_packed[8];
//mTextureTransform[GLTF_TEXTURE_INFO_BASE_COLOR].getPacked(base_color_packed);
LLGLTFMaterial::sDefault.mTextureTransform[LLGLTFMaterial::GLTF_TEXTURE_INFO_BASE_COLOR].getPacked(base_color_packed);
shader->uniform4fv(LLShaderMgr::TEXTURE_BASE_COLOR_TRANSFORM, 2, (F32*)base_color_packed);
if (!LLPipeline::sShadowRender)
{
bindTexture(asset, LLShaderMgr::NORMAL_MAP, material.mNormalTexture, LLViewerFetchedTexture::sFlatNormalImagep);
bindTexture(asset, LLShaderMgr::METALLIC_ROUGHNESS_MAP, material.mPbrMetallicRoughness.mMetallicRoughnessTexture, LLViewerFetchedTexture::sWhiteImagep);
bindTexture(asset, LLShaderMgr::OCCLUSION_MAP, material.mOcclusionTexture, LLViewerFetchedTexture::sWhiteImagep);
bindTexture(asset, LLShaderMgr::EMISSIVE_MAP, material.mEmissiveTexture, LLViewerFetchedTexture::sWhiteImagep);
// NOTE: base color factor is baked into vertex stream
shader->uniform1f(LLShaderMgr::ROUGHNESS_FACTOR, material.mPbrMetallicRoughness.mRoughnessFactor);
shader->uniform1f(LLShaderMgr::METALLIC_FACTOR, material.mPbrMetallicRoughness.mMetallicFactor);
shader->uniform3fv(LLShaderMgr::EMISSIVE_COLOR, 1, glm::value_ptr(material.mEmissiveFactor));
F32 normal_packed[8];
//mTextureTransform[GLTF_TEXTURE_INFO_NORMAL].getPacked(normal_packed);
LLGLTFMaterial::sDefault.mTextureTransform[LLGLTFMaterial::GLTF_TEXTURE_INFO_NORMAL].getPacked(normal_packed);
shader->uniform4fv(LLShaderMgr::TEXTURE_NORMAL_TRANSFORM, 2, (F32*)normal_packed);
F32 metallic_roughness_packed[8];
//mTextureTransform[GLTF_TEXTURE_INFO_METALLIC_ROUGHNESS].getPacked(metallic_roughness_packed);
LLGLTFMaterial::sDefault.mTextureTransform[LLGLTFMaterial::GLTF_TEXTURE_INFO_METALLIC_ROUGHNESS].getPacked(metallic_roughness_packed);
shader->uniform4fv(LLShaderMgr::TEXTURE_METALLIC_ROUGHNESS_TRANSFORM, 2, (F32*)metallic_roughness_packed);
F32 emissive_packed[8];
//mTextureTransform[GLTF_TEXTURE_INFO_EMISSIVE].getPacked(emissive_packed);
LLGLTFMaterial::sDefault.mTextureTransform[LLGLTFMaterial::GLTF_TEXTURE_INFO_EMISSIVE].getPacked(emissive_packed);
shader->uniform4fv(LLShaderMgr::TEXTURE_EMISSIVE_TRANSFORM, 2, (F32*)emissive_packed);
}
}
LLMatrix4a inverse(const LLMatrix4a& mat)
{
glh::matrix4f m((F32*)mat.mMatrix);

View File

@ -28,17 +28,11 @@
#include "llsingleton.h"
#include "llviewerobject.h"
#include "gltf/common.h"
class LLVOVolume;
class LLDrawable;
namespace LL
{
namespace GLTF
{
class Asset;
}
}
namespace LL
{
class GLTFSceneManager : public LLSimpleton<GLTFSceneManager>
@ -52,12 +46,20 @@ namespace LL
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 uploadSelection(); // decompose selected asset and upload to simulator
void update();
void render(bool opaque, bool rigged = false);
void render(bool opaque, bool rigged = false, bool unlit = false);
// render the given variant of all assets
// variant - bitmask according to LLGLSLShader::GLTFVariant flags
void render(U8 variant);
void render(LL::GLTF::Asset& asset, U8 variant);
// bind the given material for rendering
void bind(LL::GLTF::Asset& asset, LL::GLTF::Material& material);
void renderOpaque();
void renderAlpha();
@ -73,7 +75,7 @@ namespace LL
LLVector4a* normal, // return the surface normal at the intersection point
LLVector4a* tangent); // return the surface tangent at the intersection point
bool lineSegmentIntersect(LLVOVolume* obj, GLTF::Asset* asset, const LLVector4a& start, const LLVector4a& end, S32 face, bool pick_transparent, bool pick_rigged, bool pick_unselectable, S32* face_hitp, S32* primitive_hitp,
bool lineSegmentIntersect(LLVOVolume* obj, LL::GLTF::Asset* asset, const LLVector4a& start, const LLVector4a& end, S32 face, bool pick_transparent, bool pick_rigged, bool pick_unselectable, S32* face_hitp, S32* primitive_hitp,
LLVector4a* intersection, LLVector2* tex_coord, LLVector4a* normal, LLVector4a* tangent);
void renderDebug();
@ -90,6 +92,8 @@ namespace LL
U32 mPendingImageUploads = 0;
U32 mPendingBinaryUploads = 0;
U32 mPendingGLTFUploads = 0;
U32 mJointUBO = 0;
};
}

View File

@ -247,7 +247,7 @@ S32 LLAgentBenefits::getTextureUploadCost(const LLViewerTexture* tex) const
return getTextureUploadCost();
}
}
return getTextureUploadCost();
return 0;
}
S32 LLAgentBenefits::getTextureUploadCost(const LLImageBase* tex) const

View File

@ -94,7 +94,7 @@ S32 LLDrawPoolAlpha::getNumPostDeferredPasses()
}
// set some common parameters on the given shader to prepare for alpha rendering
static void prepare_alpha_shader(LLGLSLShader* shader, bool textureGamma, bool deferredEnvironment, F32 water_sign)
static void prepare_alpha_shader(LLGLSLShader* shader, bool deferredEnvironment, F32 water_sign)
{
static LLCachedControl<F32> displayGamma(gSavedSettings, "RenderDeferredDisplayGamma");
F32 gamma = displayGamma;
@ -133,15 +133,11 @@ static void prepare_alpha_shader(LLGLSLShader* shader, bool textureGamma, bool d
{
shader->setMinimumAlpha(MINIMUM_ALPHA);
}
if (textureGamma)
{
shader->uniform1f(LLShaderMgr::TEXTURE_GAMMA, 2.2f);
}
//also prepare rigged variant
if (shader->mRiggedVariant && shader->mRiggedVariant != shader)
{
prepare_alpha_shader(shader->mRiggedVariant, textureGamma, deferredEnvironment, water_sign);
prepare_alpha_shader(shader->mRiggedVariant, deferredEnvironment, water_sign);
}
}
@ -172,36 +168,36 @@ void LLDrawPoolAlpha::renderPostDeferred(S32 pass)
llassert(LLPipeline::sRenderDeferred);
emissive_shader = &gDeferredEmissiveProgram;
prepare_alpha_shader(emissive_shader, true, false, water_sign);
prepare_alpha_shader(emissive_shader, false, water_sign);
pbr_emissive_shader = &gPBRGlowProgram;
prepare_alpha_shader(pbr_emissive_shader, true, false, water_sign);
prepare_alpha_shader(pbr_emissive_shader, false, water_sign);
fullbright_shader =
(LLPipeline::sImpostorRender) ? &gDeferredFullbrightAlphaMaskProgram :
(LLPipeline::sRenderingHUDs) ? &gHUDFullbrightAlphaMaskAlphaProgram :
&gDeferredFullbrightAlphaMaskAlphaProgram;
prepare_alpha_shader(fullbright_shader, true, true, water_sign);
prepare_alpha_shader(fullbright_shader, true, water_sign);
simple_shader =
(LLPipeline::sImpostorRender) ? &gDeferredAlphaImpostorProgram :
(LLPipeline::sRenderingHUDs) ? &gHUDAlphaProgram :
&gDeferredAlphaProgram;
prepare_alpha_shader(simple_shader, false, true, water_sign); //prime simple shader (loads shadow relevant uniforms)
prepare_alpha_shader(simple_shader, true, water_sign); //prime simple shader (loads shadow relevant uniforms)
LLGLSLShader* materialShader = gDeferredMaterialProgram;
for (int i = 0; i < LLMaterial::SHADER_COUNT*2; ++i)
{
prepare_alpha_shader(&materialShader[i], false, true, water_sign);
prepare_alpha_shader(&materialShader[i], true, water_sign);
}
pbr_shader =
(LLPipeline::sRenderingHUDs) ? &gHUDPBRAlphaProgram :
&gDeferredPBRAlphaProgram;
prepare_alpha_shader(pbr_shader, false, true, water_sign);
prepare_alpha_shader(pbr_shader, true, water_sign);
// explicitly unbind here so render loop doesn't make assumptions about the last shader
// already being setup for rendering
@ -264,11 +260,10 @@ void LLDrawPoolAlpha::forwardRender(bool rigged)
if (rigged)
{ // draw GLTF scene to depth buffer before rigged alpha
gPipeline.bindDeferredShader(gDeferredPBRAlphaProgram);
LL::GLTFSceneManager::instance().render(false, false);
gPipeline.bindDeferredShader(*gDeferredPBRAlphaProgram.mRiggedVariant);
LL::GLTFSceneManager::instance().render(false, true);
LL::GLTFSceneManager::instance().render(false, false, true);
LL::GLTFSceneManager::instance().render(false, true, true);
}
// If the face is more than 90% transparent, then don't update the Depth buffer for Dof

View File

@ -564,7 +564,7 @@ void LLDrawPoolAvatar::beginDeferredImpostor()
sVertexProgram = &gDeferredImpostorProgram;
specular_channel = sVertexProgram->enableTexture(LLViewerShaderMgr::SPECULAR_MAP);
normal_channel = sVertexProgram->enableTexture(LLViewerShaderMgr::DEFERRED_NORMAL);
normal_channel = sVertexProgram->enableTexture(LLViewerShaderMgr::NORMAL_MAP);
sDiffuseChannel = sVertexProgram->enableTexture(LLViewerShaderMgr::DIFFUSE_MAP);
sVertexProgram->bind();
sVertexProgram->setMinimumAlpha(0.01f);
@ -575,7 +575,7 @@ void LLDrawPoolAvatar::endDeferredImpostor()
LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR
sShaderLevel = mShaderLevel;
sVertexProgram->disableTexture(LLViewerShaderMgr::DEFERRED_NORMAL);
sVertexProgram->disableTexture(LLViewerShaderMgr::NORMAL_MAP);
sVertexProgram->disableTexture(LLViewerShaderMgr::SPECULAR_MAP);
sVertexProgram->disableTexture(LLViewerShaderMgr::DIFFUSE_MAP);
gPipeline.unbindDeferredShader(*sVertexProgram);

View File

@ -54,11 +54,10 @@ void LLDrawPoolGLTFPBR::renderDeferred(S32 pass)
{
llassert(!LLPipeline::sRenderingHUDs);
gDeferredPBROpaqueProgram.bind();
LL::GLTFSceneManager::instance().renderOpaque();
pushGLTFBatches(mRenderType);
gDeferredPBROpaqueProgram.bind();
pushGLTFBatches(mRenderType);
gDeferredPBROpaqueProgram.bind(true);
LL::GLTFSceneManager::instance().render(true, true);

View File

@ -36,43 +36,12 @@
#include "llspatialpartition.h"
#include "llviewershadermgr.h"
#include "llrender.h"
#include "gltfscenemanager.h"
static LLTrace::BlockTimerStatHandle FTM_RENDER_SIMPLE_DEFERRED("Deferred Simple");
static LLTrace::BlockTimerStatHandle FTM_RENDER_GRASS_DEFERRED("Deferred Grass");
static void setup_simple_shader(LLGLSLShader* shader)
{
shader->bind();
}
static void setup_glow_shader(LLGLSLShader* shader)
{
setup_simple_shader(shader);
if (LLPipeline::sRenderDeferred && !LLPipeline::sRenderingHUDs)
{
shader->uniform1f(LLShaderMgr::TEXTURE_GAMMA, 2.2f);
}
else
{
shader->uniform1f(LLShaderMgr::TEXTURE_GAMMA, 1.f);
}
}
static void setup_fullbright_shader(LLGLSLShader* shader)
{
setup_glow_shader(shader);
S32 channel = shader->enableTexture(LLShaderMgr::EXPOSURE_MAP);
if (channel > -1)
{
gGL.getTexUnit(channel)->bind(&gPipeline.mExposureMap);
}
shader->uniform1f(LLViewerShaderMgr::FULLBRIGHT, 1.f);
}
void LLDrawPoolGlow::renderPostDeferred(S32 pass)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL;
@ -89,12 +58,12 @@ void LLDrawPoolGlow::renderPostDeferred(S32 pass)
gGL.setColorMask(false, true);
//first pass -- static objects
setup_glow_shader(shader);
shader->bind();
pushBatches(LLRenderPass::PASS_GLOW, true, true);
// second pass -- rigged objects
shader = shader->mRiggedVariant;
setup_glow_shader(shader);
shader->bind();
pushRiggedBatches(LLRenderPass::PASS_GLOW_RIGGED, true, true);
gGL.setColorMask(true, false);
@ -133,11 +102,11 @@ void LLDrawPoolSimple::renderDeferred(S32 pass)
LLGLDisable blend(GL_BLEND);
//render static
setup_simple_shader(&gDeferredDiffuseProgram);
gDeferredDiffuseProgram.bind();
pushBatches(LLRenderPass::PASS_SIMPLE, true, true);
//render rigged
setup_simple_shader(gDeferredDiffuseProgram.mRiggedVariant);
gDeferredDiffuseProgram.bind(true);
pushRiggedBatches(LLRenderPass::PASS_SIMPLE_RIGGED, true, true);
}
@ -150,11 +119,11 @@ void LLDrawPoolAlphaMask::renderDeferred(S32 pass)
LLGLSLShader* shader = &gDeferredDiffuseAlphaMaskProgram;
//render static
setup_simple_shader(shader);
shader->bind();
pushMaskBatches(LLRenderPass::PASS_ALPHA_MASK, true, true);
//render rigged
setup_simple_shader(shader->mRiggedVariant);
shader->bind(true);
pushRiggedMaskBatches(LLRenderPass::PASS_ALPHA_MASK_RIGGED, true, true);
}
@ -201,13 +170,13 @@ void LLDrawPoolFullbright::renderPostDeferred(S32 pass)
gGL.setSceneBlendType(LLRender::BT_ALPHA);
// render static
setup_fullbright_shader(shader);
shader->bind();
pushBatches(LLRenderPass::PASS_FULLBRIGHT, true, true);
if (!LLPipeline::sRenderingHUDs)
{
// render rigged
setup_fullbright_shader(shader->mRiggedVariant);
shader->bind(true);
pushRiggedBatches(LLRenderPass::PASS_FULLBRIGHT_RIGGED, true, true);
}
}
@ -216,6 +185,10 @@ void LLDrawPoolFullbrightAlphaMask::renderPostDeferred(S32 pass)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL; //LL_RECORD_BLOCK_TIME(FTM_RENDER_FULLBRIGHT);
// render unrigged unlit GLTF
LL::GLTFSceneManager::instance().render(true, false, true);
LL::GLTFSceneManager::instance().render(true, true, true);
LLGLSLShader* shader = nullptr;
if (LLPipeline::sRenderingHUDs)
{
@ -229,13 +202,13 @@ void LLDrawPoolFullbrightAlphaMask::renderPostDeferred(S32 pass)
LLGLDisable blend(GL_BLEND);
// render static
setup_fullbright_shader(shader);
shader->bind();
pushMaskBatches(LLRenderPass::PASS_FULLBRIGHT_ALPHA_MASK, true, true);
if (!LLPipeline::sRenderingHUDs)
{
// render rigged
setup_fullbright_shader(shader->mRiggedVariant);
shader->bind(true);
pushRiggedMaskBatches(LLRenderPass::PASS_FULLBRIGHT_ALPHA_MASK_RIGGED, true, true);
}
}

View File

@ -1815,15 +1815,15 @@ void LLEnvironment::update(const LLViewerCamera * cam)
end_shaders = LLViewerShaderMgr::instance()->endShaders();
for (shaders_iter = LLViewerShaderMgr::instance()->beginShaders(); shaders_iter != end_shaders; ++shaders_iter)
{
if ((shaders_iter->mProgramObject != 0)
&& (gPipeline.canUseWindLightShaders()
|| shaders_iter->mShaderGroup == LLGLSLShader::SG_WATER))
shaders_iter->mUniformsDirty = true;
if (shaders_iter->mRiggedVariant)
{
shaders_iter->mUniformsDirty = true;
if (shaders_iter->mRiggedVariant)
{
shaders_iter->mRiggedVariant->mUniformsDirty = true;
}
shaders_iter->mRiggedVariant->mUniformsDirty = true;
}
for (auto& variant : shaders_iter->mGLTFVariants)
{
variant.mUniformsDirty = true;
}
}
}

View File

@ -657,13 +657,6 @@ void LLFace::renderOneWireframe(const LLColor4 &color, F32 fogCfx, bool wirefram
{
LLGLDisable depth(wireframe_selection ? 0 : GL_BLEND);
//LLGLEnable stencil(wireframe_selection ? 0 : GL_STENCIL_TEST);
if (!wireframe_selection)
{ //modify wireframe into outline selection mode
glStencilFunc(GL_NOTEQUAL, 2, 0xffff);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
}
LLGLEnable offset(GL_POLYGON_OFFSET_LINE);
glPolygonOffset(3.f, 3.f);

View File

@ -541,11 +541,11 @@ bool LLFilePicker::getSaveFile(ESaveFilter filter, const std::string& filename,
case FFSAVE_GLTF:
if (filename.empty())
{
wcsncpy( mFilesW,L"untitled.glb", FILENAME_BUFFER_SIZE); /*Flawfinder: ignore*/
wcsncpy( mFilesW,L"untitled.gltf", FILENAME_BUFFER_SIZE); /*Flawfinder: ignore*/
}
mOFN.lpstrDefExt = L"glb";
mOFN.lpstrDefExt = L"gltf";
mOFN.lpstrFilter =
L"glTF Asset File (*.gltf *.glb)\0*.gltf;*.glb\0" \
L"glTF Asset File (*.gltf)\0*.gltf\0" \
L"\0";
break;
case FFSAVE_XML:
@ -860,7 +860,7 @@ void set_nav_save_data(LLFilePicker::ESaveFilter filter, std::string &extension,
case LLFilePicker::FFSAVE_GLTF:
type = "\?\?\?\?";
creator = "\?\?\?\?";
extension = "glb,gltf";
extension = "gltf";
break;
// <FS:TS> Compile fix

View File

@ -589,13 +589,13 @@ void LLFloaterIMSessionTab::appendMessage(const LLChat& chat, const LLSD& args)
mChatHistory->appendMessage(chat, chat_args);
}
void LLFloaterIMSessionTab::updateUsedEmojis(LLWString text)
void LLFloaterIMSessionTab::updateUsedEmojis(LLWStringView text)
{
LLEmojiDictionary* dictionary = LLEmojiDictionary::getInstance();
llassert_always(dictionary);
bool emojiSent = false;
for (llwchar& c : text)
for (const llwchar& c : text)
{
if (dictionary->isEmoji(c))
{

View File

@ -149,7 +149,7 @@ protected:
std::string appendTime();
void assignResizeLimits();
void updateUsedEmojis(LLWString text);
void updateUsedEmojis(LLWStringView text);
S32 mFloaterExtraWidth;

View File

@ -68,6 +68,7 @@
#include "llnamelistctrl.h"
#include "llnotifications.h"
#include "llnotificationsutil.h"
#include "llpbrterrainfeatures.h"
#include "llregioninfomodel.h"
#include "llscrolllistitem.h"
#include "llsliderctrl.h"
@ -282,7 +283,16 @@ bool LLFloaterRegionInfo::postBuild()
panel = new LLPanelRegionTerrainInfo;
mInfoPanels.push_back(panel);
panel->buildFromFile("panel_region_terrain.xml");
static LLCachedControl<bool> feature_pbr_terrain_enabled(gSavedSettings, "RenderTerrainPBREnabled", false);
static LLCachedControl<bool> feature_pbr_terrain_transforms_enabled(gSavedSettings, "RenderTerrainPBRTransformsEnabled", false);
if (!feature_pbr_terrain_transforms_enabled || !feature_pbr_terrain_enabled)
{
panel->buildFromFile("panel_region_terrain.xml");
}
else
{
panel->buildFromFile("panel_region_terrain_texture_transform.xml");
}
mTab->addTabPanel(panel);
mEnvironmentPanel = new LLPanelRegionEnvironment;
@ -601,6 +611,20 @@ void LLFloaterRegionInfo::processRegionInfo(LLMessageSystem* msg)
} // else will rerequest on onOpen either way
}
// static
void LLFloaterRegionInfo::sRefreshFromRegion(LLViewerRegion* region)
{
if (region != gAgent.getRegion()) { return; }
LLFloaterRegionInfo* floater = LLFloaterReg::getTypedInstance<LLFloaterRegionInfo>("region_info");
if (!floater) { return; }
if (floater->getVisible() && region == gAgent.getRegion())
{
floater->refreshFromRegion(region);
}
}
// static
LLPanelEstateInfo* LLFloaterRegionInfo::getPanelEstate()
{
@ -885,6 +909,13 @@ void LLPanelRegionInfo::initCtrl(const std::string& name)
getChild<LLUICtrl>(name)->setCommitCallback(boost::bind(&LLPanelRegionInfo::onChangeAnything, this));
}
template<typename CTRL>
void LLPanelRegionInfo::initAndSetCtrl(CTRL*& ctrl, const std::string& name)
{
initCtrl(name);
ctrl = findChild<CTRL>(name);
}
void LLPanelRegionInfo::onClickManageTelehub()
{
LLFloaterReg::hideInstance("region_info");
@ -1680,11 +1711,17 @@ LLPanelRegionTerrainInfo::LLPanelRegionTerrainInfo()
const LLUUID (&default_textures)[LLVLComposition::ASSET_COUNT] = LLVLComposition::getDefaultTextures();
for (S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i)
{
mTextureDetailCtrl[i] = nullptr;
mMaterialDetailCtrl[i] = nullptr;
mLastSetTextures[i] = default_textures[i];
}
for (S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i)
{
mLastSetMaterials[i] = BLANK_MATERIAL_ASSET_ID;
mMaterialScaleUCtrl[i] = nullptr;
mMaterialScaleVCtrl[i] = nullptr;
mMaterialRotationCtrl[i] = nullptr;
mMaterialOffsetUCtrl[i] = nullptr;
mMaterialOffsetVCtrl[i] = nullptr;
}
}
@ -1705,15 +1742,18 @@ bool LLPanelRegionTerrainInfo::postBuild()
for(S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i)
{
buffer = llformat("texture_detail_%d", i);
initCtrl(buffer);
mTextureDetailCtrl[i] = findChild<LLTextureCtrl>(buffer);
}
for(S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i)
{
buffer = llformat("material_detail_%d", i);
initCtrl(buffer);
mMaterialDetailCtrl[i] = findChild<LLTextureCtrl>(buffer);
initAndSetCtrl(mTextureDetailCtrl[i], llformat("texture_detail_%d", i));
if (mTextureDetailCtrl[i])
{
mTextureDetailCtrl[i]->setBakeTextureEnabled(false);
}
initAndSetCtrl(mMaterialDetailCtrl[i], llformat("material_detail_%d", i));
initAndSetCtrl(mMaterialScaleUCtrl[i], llformat("terrain%dScaleU", i));
initAndSetCtrl(mMaterialScaleVCtrl[i], llformat("terrain%dScaleV", i));
initAndSetCtrl(mMaterialRotationCtrl[i], llformat("terrain%dRotation", i));
initAndSetCtrl(mMaterialOffsetUCtrl[i], llformat("terrain%dOffsetU", i));
initAndSetCtrl(mMaterialOffsetVCtrl[i], llformat("terrain%dOffsetV", i));
}
for(S32 i = 0; i < CORNER_COUNT; ++i)
@ -1765,6 +1805,17 @@ void LLPanelRegionTerrainInfo::updateForMaterialType()
}
}
// Toggle visibility of terrain tabs
LLTabContainer* terrain_tabs = findChild<LLTabContainer>("terrain_tabs");
if (terrain_tabs)
{
LLPanel* pbr_terrain_repeats_tab = findChild<LLPanel>("terrain_transform_panel");
if (pbr_terrain_repeats_tab)
{
terrain_tabs->setTabVisibility(pbr_terrain_repeats_tab, show_material_controls);
}
}
// Toggle visibility of labels
LLUICtrl* texture_label = findChild<LLUICtrl>("detail_texture_text");
if (texture_label) { texture_label->setVisible(show_texture_controls); }
@ -1893,6 +1944,21 @@ bool LLPanelRegionTerrainInfo::refreshFromRegion(LLViewerRegion* region)
}
}
for(S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i)
{
if (!mMaterialScaleUCtrl[i] || !mMaterialScaleVCtrl[i] || !mMaterialRotationCtrl[i] || !mMaterialOffsetUCtrl[i] || !mMaterialOffsetVCtrl[i]) { continue; }
const LLGLTFMaterial* mat_override = compp->getMaterialOverride(i);
if (!mat_override) { mat_override = &LLGLTFMaterial::sDefault; }
// Assume all texture transforms have the same value
const LLGLTFMaterial::TextureTransform& transform = mat_override->mTextureTransform[LLGLTFMaterial::GLTF_TEXTURE_INFO_BASE_COLOR];
mMaterialScaleUCtrl[i]->setValue(transform.mScale.mV[VX]);
mMaterialScaleVCtrl[i]->setValue(transform.mScale.mV[VY]);
mMaterialRotationCtrl[i]->setValue(transform.mRotation * RAD_TO_DEG);
mMaterialOffsetUCtrl[i]->setValue(transform.mOffset.mV[VX]);
mMaterialOffsetVCtrl[i]->setValue(transform.mOffset.mV[VY]);
}
std::string buffer;
for(S32 i = 0; i < CORNER_COUNT; ++i)
{
@ -1922,7 +1988,14 @@ bool LLPanelRegionTerrainInfo::refreshFromRegion(LLViewerRegion* region)
// virtual
bool LLPanelRegionTerrainInfo::sendUpdate()
{
LL_INFOS() << "LLPanelRegionTerrainInfo::sendUpdate" << LL_ENDL;
LL_INFOS() << __FUNCTION__ << LL_ENDL;
LLUICtrl* apply_btn = getChild<LLUICtrl>("apply_btn");
if (apply_btn && !apply_btn->getEnabled())
{
LL_WARNS() << "Duplicate update, ignored" << LL_ENDL;
return false;
}
// Make sure user hasn't chosen wacky textures unless we're on Aurora-sim.
// <FS:CR> Aurora Sim - Region Settings Console
@ -2032,6 +2105,51 @@ bool LLPanelRegionTerrainInfo::sendUpdate()
sendEstateOwnerMessage(msg, "texturecommit", invoice, strings);
// ========================================
// POST to ModifyRegion endpoint, if enabled
static LLCachedControl<bool> feature_pbr_terrain_transforms_enabled(gSavedSettings, "RenderTerrainPBRTransformsEnabled", false);
if (material_type == LLTerrainMaterials::Type::PBR && feature_pbr_terrain_transforms_enabled)
{
LLTerrainMaterials composition;
for (S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i)
{
LLPointer<LLGLTFMaterial> mat_override = new LLGLTFMaterial();
const bool transform_controls_valid = mMaterialScaleUCtrl[i] && mMaterialScaleVCtrl[i] && mMaterialRotationCtrl[i] && mMaterialOffsetUCtrl[i] && mMaterialOffsetVCtrl[i];
if (transform_controls_valid)
{
// Set texture transforms for all texture infos to the same value,
// because the PBR terrain shader doesn't currently support
// different transforms per texture info. See also
// LLDrawPoolTerrain::renderFullShaderPBR .
for (U32 tt = 0; tt < LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT; ++tt)
{
LLGLTFMaterial::TextureTransform& transform = mat_override->mTextureTransform[tt];
transform.mScale.mV[VX] = mMaterialScaleUCtrl[i]->getValue().asReal();
transform.mScale.mV[VY] = mMaterialScaleVCtrl[i]->getValue().asReal();
transform.mRotation = mMaterialRotationCtrl[i]->getValue().asReal() * DEG_TO_RAD;
transform.mOffset.mV[VX] = mMaterialOffsetUCtrl[i]->getValue().asReal();
transform.mOffset.mV[VY] = mMaterialOffsetVCtrl[i]->getValue().asReal();
}
}
if (*mat_override == LLGLTFMaterial::sDefault) { mat_override = nullptr; }
composition.setMaterialOverride(i, mat_override.get());
}
// queueModify leads to a few messages being sent back and forth:
// viewer: POST ModifyRegion
// simulator: RegionHandshake
// viewer: GET ModifyRegion
LLViewerRegion* region = gAgent.getRegion();
llassert(region);
if (region)
{
LLPBRTerrainFeatures::queueModify(*region, composition);
}
}
return true;
}

View File

@ -88,6 +88,7 @@ public:
// get and process region info if necessary.
static void processRegionInfo(LLMessageSystem* msg);
static void sRefreshFromRegion(LLViewerRegion* region);
static const LLUUID& getLastInvoice() { return sRequestInvoice; }
static void nextInvoice() { sRequestInvoice.generate(); }
@ -162,6 +163,7 @@ public:
protected:
void initCtrl(const std::string& name);
template<typename CTRL> void initAndSetCtrl(CTRL*& ctrl, const std::string& name);
// Returns true if update sent and apply button should be
// disabled.
@ -307,8 +309,15 @@ private:
LLCheckBoxCtrl* mMaterialTypeCtrl = nullptr;
LLTextureCtrl* mTextureDetailCtrl[LLTerrainMaterials::ASSET_COUNT];
LLTextureCtrl* mMaterialDetailCtrl[LLTerrainMaterials::ASSET_COUNT];
LLUUID mLastSetTextures[LLTerrainMaterials::ASSET_COUNT];
LLUUID mLastSetMaterials[LLTerrainMaterials::ASSET_COUNT];
LLSpinCtrl* mMaterialScaleUCtrl[LLTerrainMaterials::ASSET_COUNT];
LLSpinCtrl* mMaterialScaleVCtrl[LLTerrainMaterials::ASSET_COUNT];
LLSpinCtrl* mMaterialRotationCtrl[LLTerrainMaterials::ASSET_COUNT];
LLSpinCtrl* mMaterialOffsetUCtrl[LLTerrainMaterials::ASSET_COUNT];
LLSpinCtrl* mMaterialOffsetVCtrl[LLTerrainMaterials::ASSET_COUNT];
};
/////////////////////////////////////////////////////////////////////////////

View File

@ -1159,7 +1159,7 @@ F32 gpu_benchmark()
gBenchmarkProgram.mShaderFiles.push_back(std::make_pair("interface/benchmarkV.glsl", GL_VERTEX_SHADER));
gBenchmarkProgram.mShaderFiles.push_back(std::make_pair("interface/benchmarkF.glsl", GL_FRAGMENT_SHADER));
gBenchmarkProgram.mShaderLevel = 1;
if (!gBenchmarkProgram.createShader(NULL, NULL))
if (!gBenchmarkProgram.createShader())
{
return -1.f;
}

View File

@ -75,7 +75,7 @@ LLHeroProbeManager::~LLHeroProbeManager()
// helper class to seed octree with probes
void LLHeroProbeManager::update()
{
if (!LLPipeline::RenderMirrors || gTeleportDisplay || LLStartUp::getStartupState() < STATE_PRECACHE)
if (!LLPipeline::RenderMirrors || !LLPipeline::sReflectionProbesEnabled || gTeleportDisplay || LLStartUp::getStartupState() < STATE_PRECACHE)
{
return;
}
@ -112,7 +112,6 @@ void LLHeroProbeManager::update()
LLVector4a probe_pos;
LLVector3 camera_pos = LLViewerCamera::instance().mOrigin;
F32 near_clip = 0.1f;
bool probe_present = false;
LLQuaternion cameraOrientation = LLViewerCamera::instance().getQuaternion();
LLVector3 cameraDirection = LLVector3::z_axis * cameraOrientation;
@ -124,7 +123,7 @@ void LLHeroProbeManager::update()
float camera_center_distance = 99999.f;
for (auto vo : mHeroVOList)
{
if (vo && !vo->isDead() && vo->mDrawable.notNull())
if (vo && !vo->isDead() && vo->mDrawable.notNull() && vo->isReflectionProbe() && vo->getReflectionProbeIsBox())
{
float distance = (LLViewerCamera::instance().getOrigin() - vo->getPositionAgent()).magVec();
float center_distance = cameraDirection * (vo->getPositionAgent() - camera_pos);
@ -192,20 +191,15 @@ void LLHeroProbeManager::update()
// Iterate through each face of the cube
for (int i = 0; i < 6; i++)
{
float cube_facing = fmax(-1, fmin(1.0f, cameraDirection * cubeFaces[i])) * 0.6 + 0.4;
float cube_facing = fmax(-1, fmin(1.0f, cameraDirection * cubeFaces[i]));
float updateRate;
if (cube_facing < 0.1f)
{
updateRate = 0;
}
else
{
updateRate = ceilf(cube_facing * gPipeline.RenderHeroProbeConservativeUpdateMultiplier);
cube_facing = 1 - cube_facing;
mFaceUpdateList[i] = ceilf(cube_facing * gPipeline.RenderHeroProbeConservativeUpdateMultiplier);
}
mFaceUpdateList[i] = updateRate;
}
mProbes[0]->mOrigin = probe_pos;
}
else
{
@ -214,20 +208,24 @@ void LLHeroProbeManager::update()
mHeroProbeStrength = 1;
}
else
{
probe_pos.load3(camera_pos.mV);
}
void LLHeroProbeManager::renderProbes()
{
if (!LLPipeline::RenderMirrors || !LLPipeline::sReflectionProbesEnabled || gTeleportDisplay ||
LLStartUp::getStartupState() < STATE_PRECACHE)
{
return;
}
static LLCachedControl<S32> sDetail(gSavedSettings, "RenderHeroReflectionProbeDetail", -1);
static LLCachedControl<S32> sLevel(gSavedSettings, "RenderHeroReflectionProbeLevel", 3);
if (mNearestHero != nullptr)
F32 near_clip = 0.01f;
if (mNearestHero != nullptr && (gPipeline.RenderHeroProbeUpdateRate == 0 || (gFrameCount % gPipeline.RenderHeroProbeUpdateRate) == 0) &&
!gTeleportDisplay && !gDisconnected && !LLAppViewer::instance()->logoutRequestSent())
{
LL_PROFILE_ZONE_NAMED_CATEGORY_DISPLAY("hpmu - realtime");
// Probe 0 is always our mirror probe.
mProbes[0]->mOrigin = probe_pos;
bool radiance_pass = gPipeline.mReflectionMapManager.isRadiancePass();
@ -585,8 +583,6 @@ void LLHeroProbeManager::cleanup()
mDefaultProbe = nullptr;
mUpdatingProbe = nullptr;
/*
*/
}
void LLHeroProbeManager::doOcclusion()

View File

@ -68,6 +68,8 @@ public:
// maintain reflection probes
void update();
void renderProbes();
// debug display, called from llspatialpartition if reflection
// probe debug display is active
void renderDebug();
@ -152,5 +154,6 @@ private:
std::vector<LLPointer<LLVOVolume>> mHeroVOList;
LLPointer<LLVOVolume> mNearestHero;
};

View File

@ -1100,8 +1100,9 @@ bool LLLocalBitmapMgr::checkTextureDimensions(std::string filename)
return false;
}
S32 max_width = gSavedSettings.getS32("max_texture_dimension_X");
S32 max_height = gSavedSettings.getS32("max_texture_dimension_Y");
// allow loading up to 4x max rez but implicitly downrez to max rez before upload
S32 max_width = gSavedSettings.getS32("max_texture_dimension_X")*4;
S32 max_height = gSavedSettings.getS32("max_texture_dimension_Y")*4;
if ((image_info.getWidth() > max_width) || (image_info.getHeight() > max_height))
{
@ -1146,6 +1147,20 @@ void LLLocalBitmapMgr::delUnit(LLUUID tracking_id)
}
}
LLUUID LLLocalBitmapMgr::getTrackingID(const LLUUID& world_id) const
{
for (local_list_citer iter = mBitmapList.begin(); iter != mBitmapList.end(); iter++)
{
LLLocalBitmap* unit = *iter;
if (unit->getWorldID() == world_id)
{
return unit->getTrackingID();
}
}
return LLUUID::null;
}
LLUUID LLLocalBitmapMgr::getWorldID(const LLUUID &tracking_id) const
{
LLUUID world_id = LLUUID::null;

View File

@ -135,6 +135,7 @@ public:
void delUnit(LLUUID tracking_id);
bool checkTextureDimensions(std::string filename);
LLUUID getTrackingID(const LLUUID& world_id) const;
LLUUID getWorldID(const LLUUID &tracking_id) const;
bool isLocal(const LLUUID& world_id) const;
std::string getFilename(const LLUUID &tracking_id) const;

View File

@ -177,6 +177,8 @@ bool LLLocalGLTFMaterial::updateSelf()
}
}
materialBegin();
materialComplete(true);
updated = true;
}
@ -201,6 +203,8 @@ bool LLLocalGLTFMaterial::updateSelf()
LLNotificationsUtil::add("LocalBitmapsUpdateFailedFinal", notif_args);
mLinkStatus = LS_BROKEN;
materialBegin();
materialComplete(false);
}
}
}
@ -218,6 +222,8 @@ bool LLLocalGLTFMaterial::updateSelf()
LLNotificationsUtil::add("LocalBitmapsUpdateFileNotFound", notif_args);
mLinkStatus = LS_BROKEN;
materialBegin();
materialComplete(false);
}
}

View File

@ -492,10 +492,7 @@ bool LLMaterialEditor::postBuild()
}
else
{
getChild<LLUICtrl>("base_color_upload_fee")->setTextArg("[FEE]", llformat("%d", LLAgentBenefitsMgr::current().getTextureUploadCost(mBaseColorFetched)));
getChild<LLUICtrl>("metallic_upload_fee")->setTextArg("[FEE]", llformat("%d", LLAgentBenefitsMgr::current().getTextureUploadCost(mMetallicRoughnessFetched)));
getChild<LLUICtrl>("emissive_upload_fee")->setTextArg("[FEE]", llformat("%d", LLAgentBenefitsMgr::current().getTextureUploadCost(mEmissiveFetched)));
getChild<LLUICtrl>("normal_upload_fee")->setTextArg("[FEE]", llformat("%d", LLAgentBenefitsMgr::current().getTextureUploadCost(mNormalFetched)));
refreshUploadCost();
}
boost::function<void(LLUICtrl*, void*)> changes_callback = [this](LLUICtrl * ctrl, void* userData)
@ -814,6 +811,37 @@ void LLMaterialEditor::resetUnsavedChanges()
}
}
void LLMaterialEditor::refreshUploadCost()
{
mExpectedUploadCost = 0;
if (mBaseColorTextureUploadId.notNull() && mBaseColorTextureUploadId == getBaseColorId() && mBaseColorFetched)
{
S32 upload_cost = LLAgentBenefitsMgr::current().getTextureUploadCost(mBaseColorFetched);
mExpectedUploadCost += upload_cost;
getChild<LLUICtrl>("base_color_upload_fee")->setTextArg("[FEE]", llformat("%d", upload_cost));
}
if (mMetallicTextureUploadId.notNull() && mMetallicTextureUploadId == getMetallicRoughnessId() && mMetallicRoughnessFetched)
{
S32 upload_cost = LLAgentBenefitsMgr::current().getTextureUploadCost(mMetallicRoughnessFetched);
mExpectedUploadCost += upload_cost;
getChild<LLUICtrl>("metallic_upload_fee")->setTextArg("[FEE]", llformat("%d", upload_cost));
}
if (mEmissiveTextureUploadId.notNull() && mEmissiveTextureUploadId == getEmissiveId() && mEmissiveFetched)
{
S32 upload_cost = LLAgentBenefitsMgr::current().getTextureUploadCost(mEmissiveFetched);
mExpectedUploadCost += upload_cost;
getChild<LLUICtrl>("emissive_upload_fee")->setTextArg("[FEE]", llformat("%d", upload_cost));
}
if (mNormalTextureUploadId.notNull() && mNormalTextureUploadId == getNormalId() && mNormalFetched)
{
S32 upload_cost = LLAgentBenefitsMgr::current().getTextureUploadCost(mNormalFetched);
mExpectedUploadCost += upload_cost;
getChild<LLUICtrl>("normal_upload_fee")->setTextArg("[FEE]", llformat("%d", upload_cost));
}
getChild<LLUICtrl>("total_upload_fee")->setTextArg("[FEE]", llformat("%d", mExpectedUploadCost));
}
void LLMaterialEditor::markChangesUnsaved(U32 dirty_flag)
{
mUnsavedChanges |= dirty_flag;
@ -844,25 +872,15 @@ void LLMaterialEditor::markChangesUnsaved(U32 dirty_flag)
setCanSave(false);
}
mExpectedUploadCost = 0;
if (mBaseColorTextureUploadId.notNull() && mBaseColorTextureUploadId == getBaseColorId() && mBaseColorFetched)
if ((dirty_flag & MATERIAL_BASE_COLOR_TEX_DIRTY)
|| (dirty_flag & MATERIAL_NORMAL_TEX_DIRTY)
|| (dirty_flag & MATERIAL_METALLIC_ROUGHTNESS_TEX_DIRTY)
|| (dirty_flag & MATERIAL_EMISIVE_TEX_DIRTY)
|| (dirty_flag == 0)
|| (dirty_flag == U32_MAX))
{
mExpectedUploadCost += LLAgentBenefitsMgr::current().getTextureUploadCost(mBaseColorFetched);
refreshUploadCost();
}
if (mMetallicTextureUploadId.notNull() && mMetallicTextureUploadId == getMetallicRoughnessId() && mMetallicRoughnessFetched)
{
mExpectedUploadCost += LLAgentBenefitsMgr::current().getTextureUploadCost(mMetallicRoughnessFetched);
}
if (mEmissiveTextureUploadId.notNull() && mEmissiveTextureUploadId == getEmissiveId() && mEmissiveFetched)
{
mExpectedUploadCost += LLAgentBenefitsMgr::current().getTextureUploadCost(mEmissiveFetched);
}
if (mNormalTextureUploadId.notNull() && mNormalTextureUploadId == getNormalId() && mNormalFetched)
{
mExpectedUploadCost += LLAgentBenefitsMgr::current().getTextureUploadCost(mNormalFetched);
}
getChild<LLUICtrl>("total_upload_fee")->setTextArg("[FEE]", llformat("%d", mExpectedUploadCost));
}
void LLMaterialEditor::setCanSaveAs(bool value)

View File

@ -288,6 +288,7 @@ private:
// utility function for building a description of the imported material
// based on what we know about it.
const std::string buildMaterialDescription();
void refreshUploadCost();
void resetUnsavedChanges();
void markChangesUnsaved(U32 dirty_flag);

View File

@ -1362,27 +1362,35 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/)
// Texture
{
mIsAlpha = false;
LLGLenum image_format = GL_RGB;
bool identical_image_format = false;
LLSelectedTE::getImageFormat(image_format, identical_image_format);
bool missing_asset = false;
LLSelectedTE::getImageFormat(image_format, identical_image_format, missing_asset);
mIsAlpha = false;
switch (image_format)
if (!missing_asset)
{
mIsAlpha = false;
switch (image_format)
{
case GL_RGBA:
case GL_ALPHA:
{
mIsAlpha = true;
}
break;
{
mIsAlpha = true;
}
break;
case GL_RGB: break;
default:
{
LL_WARNS() << "Unexpected tex format in LLPanelFace...resorting to no alpha" << LL_ENDL;
{
LL_WARNS() << "Unexpected tex format in LLPanelFace...resorting to no alpha" << LL_ENDL;
}
break;
}
break;
}
else
{
// Don't know image's properties, use material's mode value
mIsAlpha = true;
}
if (LLViewerMedia::getInstance()->textureHasMedia(id))
@ -1428,10 +1436,12 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/)
mTextureCtrl->setTentative(false);
mTextureCtrl->setEnabled(editable && !has_pbr_material);
mTextureCtrl->setImageAssetID(id);
getChildView("combobox alphamode")->setEnabled(editable && mIsAlpha && transparency <= 0.f && !has_pbr_material);
getChildView("label alphamode")->setEnabled(editable && mIsAlpha && !has_pbr_material);
getChildView("maskcutoff")->setEnabled(editable && mIsAlpha && !has_pbr_material);
getChildView("label maskcutoff")->setEnabled(editable && mIsAlpha && !has_pbr_material);
bool can_change_alpha = editable && mIsAlpha && !missing_asset && !has_pbr_material;
getChildView("combobox alphamode")->setEnabled(can_change_alpha && transparency <= 0.f);
getChildView("label alphamode")->setEnabled(can_change_alpha);
getChildView("maskcutoff")->setEnabled(can_change_alpha);
getChildView("label maskcutoff")->setEnabled(can_change_alpha);
mTextureCtrl->setBakeTextureEnabled(true);
}
@ -1454,10 +1464,12 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/)
mTextureCtrl->setTentative(true);
mTextureCtrl->setEnabled(editable && !has_pbr_material);
mTextureCtrl->setImageAssetID(id);
getChildView("combobox alphamode")->setEnabled(editable && mIsAlpha && transparency <= 0.f && !has_pbr_material);
getChildView("label alphamode")->setEnabled(editable && mIsAlpha && !has_pbr_material);
getChildView("maskcutoff")->setEnabled(editable && mIsAlpha && !has_pbr_material);
getChildView("label maskcutoff")->setEnabled(editable && mIsAlpha && !has_pbr_material);
bool can_change_alpha = editable && mIsAlpha && !missing_asset && !has_pbr_material;
getChildView("combobox alphamode")->setEnabled(can_change_alpha && transparency <= 0.f);
getChildView("label alphamode")->setEnabled(can_change_alpha);
getChildView("maskcutoff")->setEnabled(can_change_alpha);
getChildView("label maskcutoff")->setEnabled(can_change_alpha);
mTextureCtrl->setBakeTextureEnabled(true);
}
@ -3508,13 +3520,14 @@ void LLPanelFace::onSelectTexture(const LLSD& data)
LLGLenum image_format;
bool identical_image_format = false;
LLSelectedTE::getImageFormat(image_format, identical_image_format);
bool missing_asset = false;
LLSelectedTE::getImageFormat(image_format, identical_image_format, missing_asset);
LLCtrlSelectionInterface* combobox_alphamode =
childGetSelectionInterface("combobox alphamode");
U32 alpha_mode = LLMaterial::DIFFUSE_ALPHA_MODE_NONE;
if (combobox_alphamode)
if (combobox_alphamode && !missing_asset)
{
switch (image_format)
{
@ -5439,19 +5452,51 @@ void LLPanelFace::LLSelectedTE::getFace(LLFace*& face_to_return, bool& identical
identical_face = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue(&get_te_face_func, face_to_return, false, (LLFace*)nullptr);
}
void LLPanelFace::LLSelectedTE::getImageFormat(LLGLenum& image_format_to_return, bool& identical_face)
void LLPanelFace::LLSelectedTE::getImageFormat(LLGLenum& image_format_to_return, bool& identical_face, bool& missing_asset)
{
LLGLenum image_format{ GL_RGB };
struct LLSelectedTEGetImageFormat : public LLSelectedTEGetFunctor<LLGLenum>
struct LLSelectedTEGetmatId : public LLSelectedTEFunctor
{
LLGLenum get(LLViewerObject* object, S32 te_index)
LLSelectedTEGetmatId()
: mImageFormat(GL_RGB)
, mIdentical(true)
, mMissingAsset(false)
, mFirstRun(true)
{
LLViewerTexture* image = object->getTEImage(te_index);
return image ? image->getPrimaryFormat() : GL_RGB;
}
} get_glenum;
identical_face = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue(&get_glenum, image_format);
image_format_to_return = image_format;
bool apply(LLViewerObject* object, S32 te_index) override
{
LLViewerTexture* image = object ? object->getTEImage(te_index) : nullptr;
LLGLenum format = GL_RGB;
bool missing = false;
if (image)
{
format = image->getPrimaryFormat();
missing = image->isMissingAsset();
}
if (mFirstRun)
{
mFirstRun = false;
mImageFormat = format;
mMissingAsset = missing;
}
else
{
mIdentical &= (mImageFormat == format);
mIdentical &= (mMissingAsset == missing);
}
return true;
}
LLGLenum mImageFormat;
bool mIdentical;
bool mMissingAsset;
bool mFirstRun;
} func;
LLSelectMgr::getInstance()->getSelection()->applyToTEs(&func);
image_format_to_return = func.mImageFormat;
identical_face = func.mIdentical;
missing_asset = func.mMissingAsset;
}
void LLPanelFace::LLSelectedTE::getTexId(LLUUID& id, bool& identical)

View File

@ -665,7 +665,7 @@ public:
{
public:
static void getFace(class LLFace*& face_to_return, bool& identical_face);
static void getImageFormat(LLGLenum& image_format_to_return, bool& identical_face);
static void getImageFormat(LLGLenum& image_format_to_return, bool& identical_face, bool& missing_asset);
static void getTexId(LLUUID& id, bool& identical);
static void getPbrMaterialId(LLUUID& id, bool& identical, bool& has_pbr, bool& has_faces_without_pbr);
static void getObjectScaleS(F32& scale_s, bool& identical);

View File

@ -391,6 +391,7 @@ void LLPanelVolume::getState( )
// Reflection Probe
bool is_probe = volobjp && volobjp->isReflectionProbe();
bool is_mirror = volobjp && volobjp->getReflectionProbeIsMirror();
getChild<LLUICtrl>("Reflection Probe")->setValue(is_probe);
getChildView("Reflection Probe")->setEnabled(editable && single_volume && volobjp && !volobjp->isMesh());
@ -404,9 +405,9 @@ void LLPanelVolume::getState( )
getChildView("Probe Dynamic")->setEnabled(probe_enabled);
getChildView("Probe Update Type")->setEnabled(probe_enabled);
getChildView("Probe Volume Type")->setEnabled(probe_enabled);
getChildView("Probe Ambiance")->setEnabled(probe_enabled);
getChildView("Probe Near Clip")->setEnabled(probe_enabled);
getChildView("Probe Volume Type")->setEnabled(probe_enabled && !is_mirror);
getChildView("Probe Ambiance")->setEnabled(probe_enabled && !is_mirror);
getChildView("Probe Near Clip")->setEnabled(probe_enabled && !is_mirror);
getChildView("Probe Update Label")->setEnabled(probe_enabled);
if (!probe_enabled)
@ -445,9 +446,6 @@ void LLPanelVolume::getState( )
update_type = "Dynamic Mirror";
}
getChildView("Probe Ambiance")->setEnabled(update_type != "Mirror");
getChildView("Probe Near Clip")->setEnabled(update_type != "Mirror");
getChild<LLComboBox>("Probe Volume Type", true)->setValue(volume_type);
getChild<LLSpinCtrl>("Probe Ambiance", true)->setValue(volobjp->getReflectionProbeAmbiance());
getChild<LLSpinCtrl>("Probe Near Clip", true)->setValue(volobjp->getReflectionProbeNearClip());
@ -1494,6 +1492,8 @@ void LLPanelVolume::onCommitProbe(LLUICtrl* ctrl, void* userdata)
bool is_mirror = update_type.find("Mirror") != std::string::npos;
self->getChildView("Probe Volume Type")->setEnabled(!is_mirror);
volobjp->setReflectionProbeIsDynamic(update_type.find("Dynamic") != std::string::npos);
volobjp->setReflectionProbeIsMirror(is_mirror);
@ -1502,7 +1502,7 @@ void LLPanelVolume::onCommitProbe(LLUICtrl* ctrl, void* userdata)
std::string shape_type = self->getChild<LLUICtrl>("Probe Volume Type")->getValue().asString();
bool is_box = shape_type == "Box";
bool is_box = shape_type == "Box" || is_mirror;
if (volobjp->setReflectionProbeIsBox(is_box))
{

View File

@ -0,0 +1,198 @@
/**
* @file llpbrterrainfeatures.cpp
*
* $LicenseInfo:firstyear=2024&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2024, 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 "llpbrterrainfeatures.h"
#include "llappviewer.h"
#include "llgltfmaterial.h"
#include "llviewerregion.h"
#include "llvlcomposition.h"
LLPBRTerrainFeatures gPBRTerrainFeatures;
// static
void LLPBRTerrainFeatures::queueQuery(LLViewerRegion& region, void(*done_callback)(LLUUID, bool, const LLModifyRegion&))
{
llassert(on_main_thread());
llassert(LLCoros::on_main_coro());
LLUUID region_id = region.getRegionID();
LLCoros::instance().launch("queryRegionCoro",
std::bind(&LLPBRTerrainFeatures::queryRegionCoro,
region.getCapability("ModifyRegion"),
region_id,
done_callback));
}
// static
void LLPBRTerrainFeatures::queueModify(LLViewerRegion& region, const LLModifyRegion& composition)
{
llassert(on_main_thread());
llassert(LLCoros::on_main_coro());
LLSD updates = LLSD::emptyMap();
LLSD override_updates = LLSD::emptyArray();
for (S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i)
{
const LLGLTFMaterial* material_override = composition.getMaterialOverride(i);
LLSD override_update;
if (material_override)
{
LLGLTFMaterial::sDefault.getOverrideLLSD(*material_override, override_update);
}
else
{
override_update = LLSD::emptyMap();
}
override_updates.append(override_update);
}
updates["overrides"] = override_updates;
LLCoros::instance().launch("modifyRegionCoro",
std::bind(&LLPBRTerrainFeatures::modifyRegionCoro,
region.getCapability("ModifyRegion"),
updates,
nullptr));
}
// static
void LLPBRTerrainFeatures::queryRegionCoro(std::string cap_url, LLUUID region_id, void(*done_callback)(LLUUID, bool, const LLModifyRegion&) )
{
LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID);
LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t
httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("queryRegionCoro", httpPolicy));
LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest);
LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions);
LLCore::HttpHeaders::ptr_t httpHeaders;
httpOpts->setFollowRedirects(true);
LL_DEBUGS("GLTF") << "Querying features via ModifyRegion endpoint" << LL_ENDL;
LLSD result = httpAdapter->getAndSuspend(httpRequest, cap_url, httpOpts, httpHeaders);
LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
bool success = true;
if (!status || !result["success"].asBoolean())
{
if (result["message"].isUndefined())
{
LL_WARNS("PBRTerrain") << "Failed to query PBR terrain features." << LL_ENDL;
}
else
{
LL_WARNS("PBRTerrain") << "Failed to query PBR terrain features: " << result["message"] << LL_ENDL;
}
success = false;
}
LLTerrainMaterials* composition = new LLTerrainMaterials();
if (success)
{
const LLSD& overrides = result["overrides"];
if (!overrides.isArray() || overrides.size() < LLTerrainMaterials::ASSET_COUNT)
{
LL_WARNS("PBRTerrain") << "Invalid composition format: Missing/invalid overrides" << LL_ENDL;
success = false;
}
else
{
for (S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i)
{
const LLSD& override_llsd = overrides[i];
LLPointer<LLGLTFMaterial> material_override = new LLGLTFMaterial();
material_override->applyOverrideLLSD(override_llsd);
if (*material_override == LLGLTFMaterial::sDefault)
{
material_override = nullptr;
}
composition->setMaterialOverride(i, material_override.get());
}
}
}
if (done_callback)
{
LLAppViewer::instance()->postToMainCoro([=]()
{
done_callback(region_id, success, *composition);
delete composition;
});
}
else
{
delete composition;
}
}
// static
void LLPBRTerrainFeatures::modifyRegionCoro(std::string cap_url, LLSD updates, void(*done_callback)(bool) )
{
LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID);
LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t
httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("modifyRegionCoro", httpPolicy));
LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest);
LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions);
LLCore::HttpHeaders::ptr_t httpHeaders;
httpOpts->setFollowRedirects(true);
LL_DEBUGS("GLTF") << "Applying features via ModifyRegion endpoint: " << updates << LL_ENDL;
LLSD result = httpAdapter->postAndSuspend(httpRequest, cap_url, updates, httpOpts, httpHeaders);
LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
bool success = true;
if (!status || !result["success"].asBoolean())
{
if (result["message"].isUndefined())
{
LL_WARNS("PBRTerrain") << "Failed to modify PBR terrain features." << LL_ENDL;
}
else
{
LL_WARNS("PBRTerrain") << "Failed to modify PBR terrain features: " << result["message"] << LL_ENDL;
}
success = false;
}
if (done_callback)
{
LLAppViewer::instance()->postToMainCoro([=]()
{
done_callback(success);
});
}
}

View File

@ -0,0 +1,48 @@
/**
* @file llpbrterrainfeatures.h
*
* $LicenseInfo:firstyear=2024&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2024, 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 <string>
class LLViewerRegion;
class LLMessageSystem;
class LLModifyRegion;
// Queries/modifies PBR terrain repeats, possibly other features in the future
class LLPBRTerrainFeatures
{
public:
static void queueQuery(LLViewerRegion& region, void(*done_callback)(LLUUID, bool, const LLModifyRegion&));
static void queueModify(LLViewerRegion& region, const LLModifyRegion& composition);
private:
static void queryRegionCoro(std::string cap_url, LLUUID region_id, void(*done_callback)(LLUUID, bool, const LLModifyRegion&) );
static void modifyRegionCoro(std::string cap_url, LLSD updates, void(*done_callback)(bool) );
};
extern LLPBRTerrainFeatures gPBRTerrainFeatures;

View File

@ -1266,7 +1266,7 @@ void LLReflectionMapManager::setUniforms()
{
updateUniforms();
}
glBindBufferBase(GL_UNIFORM_BUFFER, 1, mUBO);
glBindBufferBase(GL_UNIFORM_BUFFER, LLGLSLShader::UB_REFLECTION_PROBES, mUBO);
}

View File

@ -102,7 +102,6 @@ static std::string click_action_to_string_value(U8 click_action)
default:
return "Touch";
}
return "Touch";
}
// Default constructor

View File

@ -100,6 +100,12 @@ U32 LLSkinningUtil::getMeshJointCount(const LLMeshSkinInfo *skin)
return llmin((U32)getMaxJointCount(), (U32)skin->mJointNames.size());
}
S32 LLSkinningUtil::getMaxGLTFJointCount()
{
// this is the maximum number of 3x4 matrices than can fit in a UBO
return gGLManager.mMaxUniformBlockSize / 48;
}
void LLSkinningUtil::scrubInvalidJoints(LLVOAvatar *avatar, LLMeshSkinInfo* skin)
{
if (skin->mInvalidJointsScrubbed)

View File

@ -40,6 +40,7 @@ class LLJointRiggingInfoTab;
namespace LLSkinningUtil
{
S32 getMaxJointCount();
S32 getMaxGLTFJointCount();
U32 getMeshJointCount(const LLMeshSkinInfo *skin);
void scrubInvalidJoints(LLVOAvatar *avatar, LLMeshSkinInfo* skin);
void initSkinningMatrixPalette(LLMatrix4a* mat, S32 count, const LLMeshSkinInfo* skin, LLVOAvatar *avatar);

View File

@ -703,7 +703,7 @@ bool LLFloaterTexturePicker::postBuild()
getChild<LLComboBox>("l_bake_use_texture_combo_box")->setCommitCallback(onBakeTextureSelect, this);
setBakeTextureEnabled(true);
setBakeTextureEnabled(mInventoryPickType != PICK_MATERIAL);
return true;
}
@ -2058,7 +2058,7 @@ void LLTextureCtrl::showPicker(bool take_focus)
{
texture_floaterp->setSetImageAssetIDCallback(boost::bind(&LLTextureCtrl::setImageAssetID, this, _1));
texture_floaterp->setBakeTextureEnabled(mBakeTextureEnabled);
texture_floaterp->setBakeTextureEnabled(mBakeTextureEnabled && mInventoryPickType != PICK_MATERIAL);
}
LLFloater* root_floater = gFloaterView->getParentFloater(this);
@ -2300,7 +2300,7 @@ void LLTextureCtrl::setBakeTextureEnabled(bool enabled)
LLFloaterTexturePicker* floaterp = (LLFloaterTexturePicker*)mFloaterHandle.get();
if (floaterp)
{
floaterp->setBakeTextureEnabled(enabled);
floaterp->setBakeTextureEnabled(enabled && mInventoryPickType != PICK_MATERIAL);
}
}

View File

@ -770,10 +770,11 @@ void display(bool rebuild, F32 zoom_factor, int subfield, bool for_snapshot)
{
// Render mirrors and associated hero probes before we render the rest of the scene.
// This ensures the scene state in the hero probes are exactly the same as the rest of the scene before we render it.
if (gPipeline.RenderMirrors && !gSnapshot && (gPipeline.RenderHeroProbeUpdateRate == 0 || (gFrameCount % gPipeline.RenderHeroProbeUpdateRate) == 0))
if (gPipeline.RenderMirrors && !gSnapshot)
{
LL_PROFILE_ZONE_NAMED_CATEGORY_DISPLAY("Update hero probes");
gPipeline.mHeroProbeManager.update();
gPipeline.mHeroProbeManager.renderProbes();
}
LL_PROFILE_ZONE_NAMED_CATEGORY_DISPLAY("display - 1");

View File

@ -147,6 +147,7 @@
#include "llcleanup.h"
#include "llviewershadermgr.h"
#include "gltfscenemanager.h"
#include "gltf/asset.h"
// [RLVa:KB] - Checked: 2011-05-22 (RLVa-1.3.1a)
#include "fsavatarrenderpersistence.h"
#include "rlvactions.h"
@ -4016,6 +4017,33 @@ bool enable_gltf()
return enablegltf;
}
bool enable_gltf_save_as()
{
if (enable_gltf())
{
LLViewerObject* obj = LLSelectMgr::getInstance()->getSelection()->getFirstRootObject();
if (obj)
{
if (obj->mGLTFAsset && obj->mGLTFAsset->isLocalPreview())
{
return true;
}
LLPermissions* permissions = LLSelectMgr::getInstance()->findObjectPermissions(obj);
if (permissions)
{
return permissions->allowExportBy(gAgent.getID());
}
}
}
return false;
}
bool enable_gltf_upload()
{
return enable_gltf_save_as();
}
class LLSelfRemoveAllAttachments : public view_listener_t
{
bool handleEvent(const LLSD& userdata)
@ -10091,15 +10119,6 @@ class LLAdvancedClickGLTFSaveAs : public view_listener_t
}
};
class LLAdvancedClickGLTFDecompose : public view_listener_t
{
bool handleEvent(const LLSD& userdata)
{
LL::GLTFSceneManager::instance().decomposeSelection();
return true;
}
};
class LLAdvancedClickGLTFUpload: public view_listener_t
{
bool handleEvent(const LLSD& userdata)
@ -12583,7 +12602,6 @@ 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 LLAdvancedClickGLTFUpload(), "Advanced.ClickGLTFUpload");
view_listener_t::addMenu(new LLAdvancedClickResizeWindow(), "Advanced.ClickResizeWindow");
view_listener_t::addMenu(new LLAdvancedPurgeShaderCache(), "Advanced.ClearShaderCache");
@ -12948,6 +12966,8 @@ void initialize_menus()
enable.add("EnableSelectInPathfindingCharacters", boost::bind(&enable_object_select_in_pathfinding_characters));
enable.add("Advanced.EnableErrorOSException", boost::bind(&enable_os_exception));
enable.add("EnableGLTF", boost::bind(&enable_gltf));
enable.add("EnableGLTFSaveAs", boost::bind(&enable_gltf_save_as));
enable.add("EnableGLTFUpload", boost::bind(&enable_gltf_upload));
enable.add("EnableBridgeFunction", boost::bind(&enable_bridge_function)); // <FS:CR>
view_listener_t::addMenu(new LLFloaterVisible(), "FloaterVisible");

View File

@ -590,192 +590,6 @@ void process_layer_data(LLMessageSystem *mesgsys, void **user_data)
}
}
// S32 exported_object_count = 0;
// S32 exported_image_count = 0;
// S32 current_object_count = 0;
// S32 current_image_count = 0;
// extern LLNotifyBox *gExporterNotify;
// extern LLUUID gExporterRequestID;
// extern std::string gExportDirectory;
// extern LLUploadDialog *gExportDialog;
// std::string gExportedFile;
// std::map<LLUUID, std::string> gImageChecksums;
// void export_complete()
// {
// LLUploadDialog::modalUploadFinished();
// gExporterRequestID.setNull();
// gExportDirectory = "";
// LLFILE* fXML = LLFile::fopen(gExportedFile, "rb"); /* Flawfinder: ignore */
// fseek(fXML, 0, SEEK_END);
// long length = ftell(fXML);
// fseek(fXML, 0, SEEK_SET);
// U8 *buffer = new U8[length + 1];
// size_t nread = fread(buffer, 1, length, fXML);
// if (nread < (size_t) length)
// {
// LL_WARNS("Messaging") << "Short read" << LL_ENDL;
// }
// buffer[nread] = '\0';
// fclose(fXML);
// char *pos = (char *)buffer;
// while ((pos = strstr(pos+1, "<sl:image ")) != 0)
// {
// char *pos_check = strstr(pos, "checksum=\"");
// if (pos_check)
// {
// char *pos_uuid = strstr(pos_check, "\">");
// if (pos_uuid)
// {
// char image_uuid_str[UUID_STR_SIZE]; /* Flawfinder: ignore */
// memcpy(image_uuid_str, pos_uuid+2, UUID_STR_SIZE-1); /* Flawfinder: ignore */
// image_uuid_str[UUID_STR_SIZE-1] = 0;
// LLUUID image_uuid(image_uuid_str);
// LL_INFOS("Messaging") << "Found UUID: " << image_uuid << LL_ENDL;
// std::map<LLUUID, std::string>::iterator itor = gImageChecksums.find(image_uuid);
// if (itor != gImageChecksums.end())
// {
// LL_INFOS("Messaging") << "Replacing with checksum: " << itor->second << LL_ENDL;
// if (!itor->second.empty())
// {
// memcpy(&pos_check[10], itor->second.c_str(), 32); /* Flawfinder: ignore */
// }
// }
// }
// }
// }
// LLFILE* fXMLOut = LLFile::fopen(gExportedFile, "wb"); /* Flawfinder: ignore */
// if (fwrite(buffer, 1, length, fXMLOut) != length)
// {
// LL_WARNS("Messaging") << "Short write" << LL_ENDL;
// }
// fclose(fXMLOut);
// delete [] buffer;
// }
// void exported_item_complete(const LLTSCode status, void *user_data)
// {
// //std::string *filename = (std::string *)user_data;
// if (status < LLTS_OK)
// {
// LL_WARNS("Messaging") << "Export failed!" << LL_ENDL;
// }
// else
// {
// ++current_object_count;
// if (current_image_count == exported_image_count && current_object_count == exported_object_count)
// {
// LL_INFOS("Messaging") << "*** Export complete ***" << LL_ENDL;
// export_complete();
// }
// else
// {
// gExportDialog->setMessage(llformat("Exported %d/%d object files, %d/%d textures.", current_object_count, exported_object_count, current_image_count, exported_image_count));
// }
// }
// }
// struct exported_image_info
// {
// LLUUID image_id;
// std::string filename;
// U32 image_num;
// };
// void exported_j2c_complete(const LLTSCode status, void *user_data)
// {
// exported_image_info *info = (exported_image_info *)user_data;
// LLUUID image_id = info->image_id;
// U32 image_num = info->image_num;
// std::string filename = info->filename;
// delete info;
// if (status < LLTS_OK)
// {
// LL_WARNS("Messaging") << "Image download failed!" << LL_ENDL;
// }
// else
// {
// LLFILE* fIn = LLFile::fopen(filename, "rb"); /* Flawfinder: ignore */
// if (fIn)
// {
// LLPointer<LLImageJ2C> ImageUtility = new LLImageJ2C;
// LLPointer<LLImageTGA> TargaUtility = new LLImageTGA;
// fseek(fIn, 0, SEEK_END);
// S32 length = ftell(fIn);
// fseek(fIn, 0, SEEK_SET);
// U8 *buffer = ImageUtility->allocateData(length);
// if (fread(buffer, 1, length, fIn) != length)
// {
// LL_WARNS("Messaging") << "Short read" << LL_ENDL;
// }
// fclose(fIn);
// LLFile::remove(filename);
// // Convert to TGA
// LLPointer<LLImageRaw> image = new LLImageRaw();
// ImageUtility->updateData();
// ImageUtility->decode(image, 100000.0f);
// TargaUtility->encode(image);
// U8 *data = TargaUtility->getData();
// S32 data_size = TargaUtility->getDataSize();
// std::string file_path = gDirUtilp->getDirName(filename);
// std::string output_file = llformat("%s/image-%03d.tga", file_path.c_str(), image_num);//filename;
// //S32 name_len = output_file.length();
// //strcpy(&output_file[name_len-3], "tga");
// LLFILE* fOut = LLFile::fopen(output_file, "wb"); /* Flawfinder: ignore */
// char md5_hash_string[33]; /* Flawfinder: ignore */
// strcpy(md5_hash_string, "00000000000000000000000000000000"); /* Flawfinder: ignore */
// if (fOut)
// {
// if (fwrite(data, 1, data_size, fOut) != data_size)
// {
// LL_WARNS("Messaging") << "Short write" << LL_ENDL;
// }
// fseek(fOut, 0, SEEK_SET);
// fclose(fOut);
// fOut = LLFile::fopen(output_file, "rb"); /* Flawfinder: ignore */
// LLMD5 my_md5_hash(fOut);
// my_md5_hash.hex_digest(md5_hash_string);
// }
// gImageChecksums.insert(std::pair<LLUUID, std::string>(image_id, md5_hash_string));
// }
// }
// ++current_image_count;
// if (current_image_count == exported_image_count && current_object_count == exported_object_count)
// {
// LL_INFOS("Messaging") << "*** Export textures complete ***" << LL_ENDL;
// export_complete();
// }
// else
// {
// gExportDialog->setMessage(llformat("Exported %d/%d object files, %d/%d textures.", current_object_count, exported_object_count, current_image_count, exported_image_count));
// }
//}
void process_derez_ack(LLMessageSystem*, void**)
{
if(gViewerWindow) gViewerWindow->getWindow()->decBusyCount();

View File

@ -56,6 +56,7 @@
#include "llfloaterregioninfo.h"
#include "llgltfmateriallist.h"
#include "llhttpnode.h"
#include "llpbrterrainfeatures.h"
#include "llregioninfomodel.h"
#include "llsdutil.h"
#include "llstartup.h"
@ -2628,6 +2629,26 @@ void LLViewerRegion::setSimulatorFeatures(const LLSD& sim_features)
gSavedSettings.setS32("max_texture_dimension_Y", 1024);
}
if (features.has("PBRTerrainEnabled"))
{
bool enabled = features["PBRTerrainEnabled"];
gSavedSettings.setBOOL("RenderTerrainPBREnabled", enabled);
}
else
{
gSavedSettings.setBOOL("RenderTerrainPBREnabled", false);
}
if (features.has("PBRMaterialSwatchEnabled"))
{
bool enabled = features["PBRMaterialSwatchEnabled"];
gSavedSettings.setBOOL("UIPreviewMaterial", enabled);
}
else
{
gSavedSettings.setBOOL("UIPreviewMaterial", false);
}
if (features.has("GLTFEnabled"))
{
bool enabled = features["GLTFEnabled"];
@ -2637,6 +2658,16 @@ void LLViewerRegion::setSimulatorFeatures(const LLSD& sim_features)
{
gSavedSettings.setBOOL("GLTFEnabled", false);
}
if (features.has("PBRTerrainTransformsEnabled"))
{
bool enabled = features["PBRTerrainTransformsEnabled"];
gSavedSettings.setBOOL("RenderTerrainTransformsPBREnabled", enabled);
}
else
{
gSavedSettings.setBOOL("RenderTerrainTransformsPBREnabled", false);
}
};
@ -3342,6 +3373,17 @@ void LLViewerRegion::unpackRegionHandshake()
{
compp->setParamsReady();
}
LLPBRTerrainFeatures::queueQuery(*this, [](LLUUID region_id, bool success, const LLModifyRegion& composition_changes)
{
if (!success) { return; }
LLViewerRegion* region = LLWorld::getInstance()->getRegionFromID(region_id);
if (!region) { return; }
LLVLComposition* compp = region->getComposition();
if (!compp) { return; }
compp->apply(composition_changes);
LLFloaterRegionInfo::sRefreshFromRegion(region);
});
}
@ -3451,6 +3493,7 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames)
capabilityNames.append("MapLayerGod");
capabilityNames.append("MeshUploadFlag");
capabilityNames.append("ModifyMaterialParams");
capabilityNames.append("ModifyRegion");
capabilityNames.append("NavMeshGenerationStatus");
capabilityNames.append("NewFileAgentInventory");
capabilityNames.append("ObjectAnimation");

File diff suppressed because it is too large Load Diff

View File

@ -291,6 +291,9 @@ extern LLGLSLShader gDeferredPBROpaqueProgram;
extern LLGLSLShader gDeferredPBRAlphaProgram;
extern LLGLSLShader gHUDPBRAlphaProgram;
// GLTF shaders
extern LLGLSLShader gGLTFPBRMetallicRoughnessProgram;
// Encodes detail level for dropping textures, in accordance with the GLTF spec where possible
// 0 is highest detail, -1 drops emissive, etc
// Dropping metallic roughness is off-spec - Reserve for potato machines as needed

View File

@ -349,6 +349,18 @@ LLViewerFetchedTexture* LLViewerTextureManager::getFetchedTextureFromUrl(const s
return gTextureList.getImageFromUrl(url, f_type, usemipmaps, boost_priority, texture_type, internal_format, primary_format, force_id);
}
//static
LLImageRaw* LLViewerTextureManager::getRawImageFromMemory(const U8* data, U32 size, std::string_view mimetype)
{
return gTextureList.getRawImageFromMemory(data, size, mimetype);
}
//static
LLViewerFetchedTexture* LLViewerTextureManager::getFetchedTextureFromMemory(const U8* data, U32 size, std::string_view mimetype)
{
return gTextureList.getImageFromMemory(data, size, mimetype);
}
LLViewerFetchedTexture* LLViewerTextureManager::getFetchedTextureFromHost(const LLUUID& image_id, FTType f_type, LLHost host)
{
return gTextureList.getImageFromHost(image_id, f_type, host);

View File

@ -476,8 +476,6 @@ private:
bool mForceCallbackFetch;
protected:
std::string mLocalFileName;
S32 mOrigWidth;
S32 mOrigHeight;
@ -732,6 +730,14 @@ public:
static LLViewerFetchedTexture* getFetchedTextureFromHost(const LLUUID& image_id, FTType f_type, LLHost host) ;
// decode a given image data according to given mime type
// WARNING: caller is responsible for deleting the returned raw image
static LLImageRaw* getRawImageFromMemory(const U8* data, U32 size, std::string_view mimetype);
// decode given image data according to given mime type
// WARNING: caller is responsible for deleting the returned image
static LLViewerFetchedTexture* getFetchedTextureFromMemory(const U8* data, U32 size, std::string_view mimetype);
static void init() ;
static void cleanup() ;
};

View File

@ -368,7 +368,7 @@ void LLViewerTextureList::shutdown()
mImageList.clear();
mInitialized = false; //prevent loading textures again.
mInitialized = false ; //prevent loading textures again.
}
void LLViewerTextureList::dump()
@ -526,6 +526,39 @@ LLViewerFetchedTexture* LLViewerTextureList::getImageFromUrl(const std::string&
return imagep;
}
LLImageRaw* LLViewerTextureList::getRawImageFromMemory(const U8* data, U32 size, std::string_view mimetype)
{
LLPointer<LLImageFormatted> image = LLImageFormatted::loadFromMemory(data, size, mimetype);
if (image)
{
LLImageRaw* raw_image = new LLImageRaw();
image->decode(raw_image, 0.f);
return raw_image;
}
else
{
return nullptr;
}
}
LLViewerFetchedTexture* LLViewerTextureList::getImageFromMemory(const U8* data, U32 size, std::string_view mimetype)
{
LLPointer<LLImageRaw> raw_image = getRawImageFromMemory(data, size, mimetype);
if (raw_image.notNull())
{
LLViewerFetchedTexture* imagep = new LLViewerFetchedTexture(raw_image, FTT_LOCAL_FILE, true);
addImage(imagep, TEX_LIST_STANDARD);
imagep->dontDiscard();
imagep->setBoostLevel(LLViewerFetchedTexture::BOOST_PREVIEW);
return imagep;
}
else
{
return nullptr;
}
}
LLViewerFetchedTexture* LLViewerTextureList::getImage(const LLUUID &image_id,
FTType f_type,
@ -1326,63 +1359,63 @@ bool LLViewerTextureList::createUploadFile(const std::string& filename,
LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
try
{
// Load the image
LLPointer<LLImageFormatted> image = LLImageFormatted::createFromType(codec);
if (image.isNull())
{
LL_WARNS() << "Couldn't open the image to be uploaded." << LL_ENDL;
return false;
}
if (!image->load(filename))
{
image->setLastError("Couldn't load the image to be uploaded.");
return false;
}
// Decompress or expand it in a raw image structure
LLPointer<LLImageRaw> raw_image = new LLImageRaw;
if (!image->decode(raw_image, 0.0f))
{
image->setLastError("Couldn't decode the image to be uploaded.");
return false;
}
// Check the image constraints
if ((image->getComponents() != 3) && (image->getComponents() != 4))
{
image->setLastError("Image files with less than 3 or more than 4 components are not supported.");
return false;
}
if (image->getWidth() < min_image_dimentions || image->getHeight() < min_image_dimentions)
{
std::string reason = llformat("Images below %d x %d pixels are not allowed. Actual size: %d x %dpx",
min_image_dimentions,
min_image_dimentions,
image->getWidth(),
image->getHeight());
image->setLastError(reason);
return false;
}
// Convert to j2c (JPEG2000) and save the file locally
LLPointer<LLImageJ2C> compressedImage = convertToUploadFile(raw_image, max_image_dimentions, force_square);
if (compressedImage.isNull())
{
image->setLastError("Couldn't convert the image to jpeg2000.");
LL_INFOS() << "Couldn't convert to j2c, file : " << filename << LL_ENDL;
return false;
}
if (!compressedImage->save(out_filename))
{
image->setLastError("Couldn't create the jpeg2000 image for upload.");
LL_INFOS() << "Couldn't create output file : " << out_filename << LL_ENDL;
return false;
}
// Test to see if the encode and save worked
LLPointer<LLImageJ2C> integrity_test = new LLImageJ2C;
if (!integrity_test->loadAndValidate(out_filename))
{
image->setLastError("The created jpeg2000 image is corrupt.");
LL_INFOS() << "Image file : " << out_filename << " is corrupt" << LL_ENDL;
return false;
}
// Load the image
LLPointer<LLImageFormatted> image = LLImageFormatted::createFromType(codec);
if (image.isNull())
{
LL_WARNS() << "Couldn't open the image to be uploaded." << LL_ENDL;
return false;
}
if (!image->load(filename))
{
image->setLastError("Couldn't load the image to be uploaded.");
return false;
}
// Decompress or expand it in a raw image structure
LLPointer<LLImageRaw> raw_image = new LLImageRaw;
if (!image->decode(raw_image, 0.0f))
{
image->setLastError("Couldn't decode the image to be uploaded.");
return false;
}
// Check the image constraints
if ((image->getComponents() != 3) && (image->getComponents() != 4))
{
image->setLastError("Image files with less than 3 or more than 4 components are not supported.");
return false;
}
if (image->getWidth() < min_image_dimentions || image->getHeight() < min_image_dimentions)
{
std::string reason = llformat("Images below %d x %d pixels are not allowed. Actual size: %d x %dpx",
min_image_dimentions,
min_image_dimentions,
image->getWidth(),
image->getHeight());
image->setLastError(reason);
return false;
}
// Convert to j2c (JPEG2000) and save the file locally
LLPointer<LLImageJ2C> compressedImage = convertToUploadFile(raw_image, max_image_dimentions, force_square);
if (compressedImage.isNull())
{
image->setLastError("Couldn't convert the image to jpeg2000.");
LL_INFOS() << "Couldn't convert to j2c, file : " << filename << LL_ENDL;
return false;
}
if (!compressedImage->save(out_filename))
{
image->setLastError("Couldn't create the jpeg2000 image for upload.");
LL_INFOS() << "Couldn't create output file : " << out_filename << LL_ENDL;
return false;
}
// Test to see if the encode and save worked
LLPointer<LLImageJ2C> integrity_test = new LLImageJ2C;
if (!integrity_test->loadAndValidate( out_filename ))
{
image->setLastError("The created jpeg2000 image is corrupt.");
LL_INFOS() << "Image file : " << out_filename << " is corrupt" << LL_ENDL;
return false;
}
}
catch (...)
{

View File

@ -198,6 +198,9 @@ private: // PoundLife - Improved Object Inspect
const LLUUID& force_id = LLUUID::null
);
LLImageRaw* getRawImageFromMemory(const U8* data, U32 size, std::string_view mimetype);
LLViewerFetchedTexture* getImageFromMemory(const U8* data, U32 size, std::string_view mimetype);
LLViewerFetchedTexture* createImage(const LLUUID &image_id,
FTType f_type,
bool usemipmap = true,

View File

@ -115,6 +115,16 @@ LLTerrainMaterials::~LLTerrainMaterials()
unboost();
}
void LLTerrainMaterials::apply(const LLModifyRegion& other)
{
for (S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i)
{
const LLGLTFMaterial* other_override = other.getMaterialOverride(i);
LLGLTFMaterial* material_override = other_override ? new LLGLTFMaterial(*other_override) : nullptr;
setMaterialOverride(i, material_override);
}
}
bool LLTerrainMaterials::generateMaterials()
{
if (texturesReady(true, true))
@ -192,7 +202,7 @@ void LLTerrainMaterials::setDetailAssetID(S32 asset, const LLUUID& id)
mMaterialTexturesSet[asset] = false;
}
const LLGLTFMaterial* LLTerrainMaterials::getMaterialOverride(S32 asset)
const LLGLTFMaterial* LLTerrainMaterials::getMaterialOverride(S32 asset) const
{
return mDetailMaterialOverrides[asset];
}

View File

@ -38,7 +38,13 @@ class LLViewerFetchedTexture;
class LLGLTFMaterial;
class LLFetchedGLTFMaterial;
class LLTerrainMaterials
class LLModifyRegion
{
public:
virtual const LLGLTFMaterial* getMaterialOverride(S32 asset) const = 0;
};
class LLTerrainMaterials : public LLModifyRegion
{
public:
friend class LLDrawPoolTerrain;
@ -46,6 +52,8 @@ public:
LLTerrainMaterials();
virtual ~LLTerrainMaterials();
void apply(const LLModifyRegion& other);
// Heights map into textures (or materials) as 0-1 = first, 1-2 = second, etc.
// So we need to compress heights into this range.
static const S32 ASSET_COUNT = 4;
@ -63,7 +71,7 @@ public:
virtual LLUUID getDetailAssetID(S32 asset);
virtual void setDetailAssetID(S32 asset, const LLUUID& id);
virtual const LLGLTFMaterial* getMaterialOverride(S32 asset);
const LLGLTFMaterial* getMaterialOverride(S32 asset) const override;
virtual void setMaterialOverride(S32 asset, LLGLTFMaterial* mat_override);
Type getMaterialType();
bool texturesReady(bool boost, bool strict);

View File

@ -548,8 +548,19 @@ F32 LLVOCacheEntry::getSquaredPixelThreshold(bool is_front)
return projection_threshold;
}
extern bool gCubeSnapshot;
bool LLVOCacheEntry::isAnyVisible(const LLVector4a& camera_origin, const LLVector4a& local_camera_origin, F32 dist_threshold)
{
#if 0
// this is ill-conceived and should be removed pending QA
// In the name of saving memory, we evict objects that are still within view distance from memory
// This results in constant paging of objects in and out of memory, leading to poor performance
// and many unacceptable visual glitches when rotating the camera
// Honestly, the entire VOCache partition system needs to be removed since it doubles the overhead of
// the spatial partition system and is redundant to the object cache, but this is a start
// - davep 2024.06.07
if( gAgent.getFSAreaSearchActive() ) { return true; } // <FS:Beq/> FIRE-32688 Area Search improvements
LLOcclusionCullingGroup* group = (LLOcclusionCullingGroup*)getGroup();
if(!group)
@ -587,6 +598,9 @@ bool LLVOCacheEntry::isAnyVisible(const LLVector4a& camera_origin, const LLVecto
}
return vis;
#else
return true;
#endif
}
void LLVOCacheEntry::calcSceneContribution(const LLVector4a& camera_origin, bool needs_update, U32 last_update, F32 max_dist)

View File

@ -1988,6 +1988,11 @@ void LLPipeline::updateMovedList(LLDrawable::drawable_vector_t& moved_list)
{
LLDrawable::drawable_vector_t::iterator curiter = iter++;
LLDrawable *drawablep = *curiter;
if (!drawablep)
{
iter = moved_list.erase(curiter);
continue;
}
bool done = true;
if (!drawablep->isDead() && (!drawablep->isState(LLDrawable::EARLY_MOVE)))
{
@ -6961,7 +6966,7 @@ void LLPipeline::generateLuminance(LLRenderTarget* src, LLRenderTarget* dst)
mGlow[1].bindTexture(0, channel);
}
channel = gLuminanceProgram.enableTexture(LLShaderMgr::DEFERRED_NORMAL);
channel = gLuminanceProgram.enableTexture(LLShaderMgr::NORMAL_MAP);
if (channel > -1)
{
// bind the normal map to get the environment mask
@ -7841,7 +7846,7 @@ void LLPipeline::bindDeferredShader(LLGLSLShader& shader, LLRenderTarget* light_
gGL.getTexUnit(channel)->setTextureAddressMode(LLTexUnit::TAM_CLAMP);
}
channel = shader.enableTexture(LLShaderMgr::DEFERRED_NORMAL, deferred_target->getUsage());
channel = shader.enableTexture(LLShaderMgr::NORMAL_MAP, deferred_target->getUsage());
if (channel > -1)
{
deferred_target->bindTexture(2, channel, LLTexUnit::TFO_POINT); // frag_data[2]
@ -8876,7 +8881,7 @@ void LLPipeline::unbindDeferredShader(LLGLSLShader &shader)
LLRenderTarget* deferred_light_target = &mRT->deferredLight;
stop_glerror();
shader.disableTexture(LLShaderMgr::DEFERRED_NORMAL, deferred_target->getUsage());
shader.disableTexture(LLShaderMgr::NORMAL_MAP, deferred_target->getUsage());
shader.disableTexture(LLShaderMgr::DEFERRED_DIFFUSE, deferred_target->getUsage());
shader.disableTexture(LLShaderMgr::DEFERRED_SPECULAR, deferred_target->getUsage());
shader.disableTexture(LLShaderMgr::DEFERRED_EMISSIVE, deferred_target->getUsage());

Some files were not shown because too many files have changed in this diff Show More