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.cppmaster
commit
1bbe697db7
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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*/
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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
|
||||
//
|
||||
|
|
|
|||
|
|
@ -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
|
||||
////////////////////////////////////
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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() };
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
7.1.8
|
||||
7.1.9
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -23,8 +23,6 @@
|
|||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
uniform sampler2D exposureMap;
|
||||
|
||||
vec3 srgb_to_linear(vec3 cs)
|
||||
{
|
||||
vec3 low_range = cs / vec3(12.92);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -247,7 +247,7 @@ S32 LLAgentBenefits::getTextureUploadCost(const LLViewerTexture* tex) const
|
|||
return getTextureUploadCost();
|
||||
}
|
||||
}
|
||||
return getTextureUploadCost();
|
||||
return 0;
|
||||
}
|
||||
|
||||
S32 LLAgentBenefits::getTextureUploadCost(const LLImageBase* tex) const
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -149,7 +149,7 @@ protected:
|
|||
std::string appendTime();
|
||||
void assignResizeLimits();
|
||||
|
||||
void updateUsedEmojis(LLWString text);
|
||||
void updateUsedEmojis(LLWStringView text);
|
||||
|
||||
S32 mFloaterExtraWidth;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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;
|
||||
|
||||
|
|
@ -1266,7 +1266,7 @@ void LLReflectionMapManager::setUniforms()
|
|||
{
|
||||
updateUniforms();
|
||||
}
|
||||
glBindBufferBase(GL_UNIFORM_BUFFER, 1, mUBO);
|
||||
glBindBufferBase(GL_UNIFORM_BUFFER, LLGLSLShader::UB_REFLECTION_PROBES, mUBO);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -102,7 +102,6 @@ static std::string click_action_to_string_value(U8 click_action)
|
|||
default:
|
||||
return "Touch";
|
||||
}
|
||||
return "Touch";
|
||||
}
|
||||
|
||||
// Default constructor
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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() ;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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 (...)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Reference in New Issue