Merge branch 'develop' into atlas-qaworkflow
commit
e4a25dc5b9
|
|
@ -218,8 +218,10 @@ jobs:
|
|||
prefix=${ba[0]}
|
||||
if [ "$prefix" == "project" ]; then
|
||||
IFS='_' read -ra prj <<< "${ba[1]}"
|
||||
prj_str="${prj[*]}"
|
||||
# uppercase first letter of each word
|
||||
export viewer_channel="Second Life Project ${prj[*]^}"
|
||||
capitalized=$(echo "$prj_str" | awk '{for (i=1; i<=NF; i++) $i = toupper(substr($i,1,1)) substr($i,2); print}')
|
||||
export viewer_channel="Second Life Project $capitalized"
|
||||
elif [[ "$prefix" == "release" || "$prefix" == "main" ]];
|
||||
then
|
||||
export viewer_channel="Second Life Release"
|
||||
|
|
@ -455,7 +457,6 @@ jobs:
|
|||
prerelease: true
|
||||
generate_release_notes: true
|
||||
target_commitish: ${{ github.sha }}
|
||||
previous_tag: release
|
||||
append_body: true
|
||||
fail_on_unmatched_files: true
|
||||
files: |
|
||||
|
|
|
|||
|
|
@ -2896,6 +2896,64 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
|
|||
<key>version</key>
|
||||
<string>1.0.9-5e8947c</string>
|
||||
</map>
|
||||
<key>discord_sdk</key>
|
||||
<map>
|
||||
<key>platforms</key>
|
||||
<map>
|
||||
<key>windows64</key>
|
||||
<map>
|
||||
<key>archive</key>
|
||||
<map>
|
||||
<key>creds</key>
|
||||
<string>github</string>
|
||||
<key>hash</key>
|
||||
<string>e11571bf76b27d15c244069988ae372eaa5afae9</string>
|
||||
<key>hash_algorithm</key>
|
||||
<string>sha1</string>
|
||||
<key>url</key>
|
||||
<string>https://api.github.com/repos/secondlife/3p-discord-sdk/releases/assets/279333720</string>
|
||||
</map>
|
||||
<key>name</key>
|
||||
<string>windows64</string>
|
||||
</map>
|
||||
<key>darwin64</key>
|
||||
<map>
|
||||
<key>archive</key>
|
||||
<map>
|
||||
<key>creds</key>
|
||||
<string>github</string>
|
||||
<key>hash</key>
|
||||
<string>dc21df8b051c425163acf3eff8f06e32f407c9e0</string>
|
||||
<key>hash_algorithm</key>
|
||||
<string>sha1</string>
|
||||
<key>url</key>
|
||||
<string>https://api.github.com/repos/secondlife/3p-discord-sdk/releases/assets/279333706</string>
|
||||
</map>
|
||||
<key>name</key>
|
||||
<string>darwin64</string>
|
||||
</map>
|
||||
</map>
|
||||
<key>license</key>
|
||||
<string>discord_sdk</string>
|
||||
<key>license_file</key>
|
||||
<string>LICENSES/discord_sdk.txt</string>
|
||||
<key>copyright</key>
|
||||
<string>Discord Inc.</string>
|
||||
<key>version</key>
|
||||
<string>1.4.9649.16733550144</string>
|
||||
<key>name</key>
|
||||
<string>discord_sdk</string>
|
||||
<key>vcs_branch</key>
|
||||
<string>main</string>
|
||||
<key>vcs_revision</key>
|
||||
<string>ef5c7c4a490ceac2df2b2f046788b1daf1bbb392</string>
|
||||
<key>vcs_url</key>
|
||||
<string>https://github.com/secondlife/3p-discord-sdk</string>
|
||||
<key>canonical_repo</key>
|
||||
<string>https://github.com/secondlife/3p-discord-sdk</string>
|
||||
<key>description</key>
|
||||
<string>Discord Social SDK</string>
|
||||
</map>
|
||||
</map>
|
||||
<key>package_description</key>
|
||||
<map>
|
||||
|
|
|
|||
|
|
@ -103,13 +103,11 @@ Perform the testing procedure on both sets of cubes.
|
|||
|
||||
Ensure that debug setting `MediaFirstClickInteract` is set to `4`
|
||||
|
||||
This test case requires two pairs of cubes, and the second pair must be deeded or set to a group that your testing account is a member of, but does not have set as active at the beginning of the test. As long as the second set of cubes is set to a group that your primary test account is a member of, the avatar that owns them does not matter.
|
||||
This test case requires two cubes, and the second cube must be deeded or set to a group that your testing account is a member of. As long as the second set of cubes is set to a group that your test account is a member of, the avatar that owns them does not matter.
|
||||
|
||||
1. Perform the testing procedure on both sets of cubes.
|
||||
2. Activate the group that the second set of cubes is set / deeded to
|
||||
3. Perform the testing procedure on both sets of cubes once more.
|
||||
Perform the testing procedure on both sets of cubes.
|
||||
|
||||
**Expected observations:** Both cubes owned by your primary testing account will not react to mouse cursor hover events and clicks without needing a focus click. Cube A set to group will react to mouse cursor hover events and clicks without needing a focus click, but Cube B will not.
|
||||
**Expected observations:** The cube owned by your primary account will not react to mouse cursor hover events and clicks without needing a focus click. The cube set to group will react to mouse cursor hover events and clicks without needing a focus click.
|
||||
|
||||
### Case 5 (MEDIA_FIRST_CLICK_FRIEND)
|
||||
|
||||
|
|
@ -144,16 +142,16 @@ Note: This requires the avatar that is performing the tests to physically be in
|
|||
|
||||
### Case 7 (MEDIA_FIRST_CLICK_ANY) (optional)
|
||||
|
||||
Ensure that debug setting `MediaFirstClickInteract` is set to `31`
|
||||
Ensure that debug setting `MediaFirstClickInteract` is set to `32767`
|
||||
|
||||
Repeat test cases 1-6.
|
||||
|
||||
1. Test case 1 should fail
|
||||
2. Test cases 2-6 should pass
|
||||
|
||||
### Case 8 (MEDIA_FIRST_CLICK_ALL) (optional)
|
||||
### Case 8 (MEDIA_FIRST_CLICK_BYPASS_MOAP_FLAG) (optional)
|
||||
|
||||
Ensure that debug setting `MediaFirstClickInteract` is set to `1073741824`
|
||||
Ensure that debug setting `MediaFirstClickInteract` is set to `65535`
|
||||
|
||||
Repeat test cases 1-6, there is no pass/fail for this run.
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ set(cmake_SOURCE_FILES
|
|||
Copy3rdPartyLibs.cmake
|
||||
DBusGlib.cmake
|
||||
DeploySharedLibs.cmake
|
||||
Discord.cmake
|
||||
DragDrop.cmake
|
||||
EXPAT.cmake
|
||||
FindAutobuild.cmake
|
||||
|
|
|
|||
|
|
@ -6,6 +6,9 @@
|
|||
|
||||
include(CMakeCopyIfDifferent)
|
||||
include(Linking)
|
||||
if (USE_DISCORD)
|
||||
include(Discord)
|
||||
endif ()
|
||||
include(OPENAL)
|
||||
|
||||
# When we copy our dependent libraries, we almost always want to copy them to
|
||||
|
|
@ -75,6 +78,10 @@ if(WINDOWS)
|
|||
endif(ADDRESS_SIZE EQUAL 32)
|
||||
endif (USE_BUGSPLAT)
|
||||
|
||||
if (TARGET ll::discord_sdk)
|
||||
list(APPEND release_files discord_partner_sdk.dll)
|
||||
endif ()
|
||||
|
||||
if (TARGET ll::openal)
|
||||
list(APPEND release_files openal32.dll alut.dll)
|
||||
endif ()
|
||||
|
|
@ -180,6 +187,10 @@ elseif(DARWIN)
|
|||
)
|
||||
endif()
|
||||
|
||||
if (TARGET ll::discord_sdk)
|
||||
list(APPEND release_files libdiscord_partner_sdk.dylib)
|
||||
endif ()
|
||||
|
||||
if (TARGET ll::openal)
|
||||
list(APPEND release_files libalut.dylib libopenal.dylib)
|
||||
endif ()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
include(Prebuilt)
|
||||
|
||||
include_guard()
|
||||
|
||||
add_library(ll::discord_sdk INTERFACE IMPORTED)
|
||||
target_compile_definitions(ll::discord_sdk INTERFACE LL_DISCORD=1)
|
||||
|
||||
use_prebuilt_binary(discord_sdk)
|
||||
|
||||
target_include_directories(ll::discord_sdk SYSTEM INTERFACE ${LIBS_PREBUILT_DIR}/include/discord_sdk)
|
||||
target_link_libraries(ll::discord_sdk INTERFACE discord_partner_sdk)
|
||||
|
|
@ -14,6 +14,7 @@ set(llappearance_SOURCE_FILES
|
|||
llavatarjoint.cpp
|
||||
llavatarjointmesh.cpp
|
||||
lldriverparam.cpp
|
||||
lljointdata.h
|
||||
lllocaltextureobject.cpp
|
||||
llpolyskeletaldistortion.cpp
|
||||
llpolymesh.cpp
|
||||
|
|
|
|||
|
|
@ -29,16 +29,17 @@
|
|||
#include "llavatarappearance.h"
|
||||
#include "llavatarappearancedefines.h"
|
||||
#include "llavatarjointmesh.h"
|
||||
#include "lljointdata.h"
|
||||
#include "llstl.h"
|
||||
#include "lldir.h"
|
||||
#include "llpolymorph.h"
|
||||
#include "llpolymesh.h"
|
||||
#include "llpolyskeletaldistortion.h"
|
||||
#include "llstl.h"
|
||||
#include "lltexglobalcolor.h"
|
||||
#include "llwearabledata.h"
|
||||
#include "boost/bind.hpp"
|
||||
#include "boost/tokenizer.hpp"
|
||||
#include "v4math.h"
|
||||
|
||||
using namespace LLAvatarAppearanceDefines;
|
||||
|
||||
|
|
@ -71,11 +72,13 @@ public:
|
|||
mChildren.clear();
|
||||
}
|
||||
bool parseXml(LLXmlTreeNode* node);
|
||||
glm::mat4 getJointMatrix();
|
||||
|
||||
private:
|
||||
std::string mName;
|
||||
std::string mSupport;
|
||||
std::string mAliases;
|
||||
std::string mGroup;
|
||||
bool mIsJoint;
|
||||
LLVector3 mPos;
|
||||
LLVector3 mEnd;
|
||||
|
|
@ -105,11 +108,17 @@ public:
|
|||
S32 getNumBones() const { return mNumBones; }
|
||||
S32 getNumCollisionVolumes() const { return mNumCollisionVolumes; }
|
||||
|
||||
private:
|
||||
typedef std::vector<LLAvatarBoneInfo*> bone_info_list_t;
|
||||
static void getJointMatricesAndHierarhy(
|
||||
LLAvatarBoneInfo* bone_info,
|
||||
LLJointData& data,
|
||||
const glm::mat4& parent_mat);
|
||||
|
||||
private:
|
||||
S32 mNumBones;
|
||||
S32 mNumCollisionVolumes;
|
||||
LLAvatarAppearance::joint_alias_map_t mJointAliasMap;
|
||||
typedef std::vector<LLAvatarBoneInfo*> bone_info_list_t;
|
||||
bone_info_list_t mBoneInfoList;
|
||||
};
|
||||
|
||||
|
|
@ -1598,6 +1607,15 @@ bool LLAvatarBoneInfo::parseXml(LLXmlTreeNode* node)
|
|||
mSupport = "base";
|
||||
}
|
||||
|
||||
// Skeleton has 133 bones, but shader only allows 110 (LL_MAX_JOINTS_PER_MESH_OBJECT)
|
||||
// Groups can be used by importer to cut out unused groups of joints
|
||||
static LLStdStringHandle group_string = LLXmlTree::addAttributeString("group");
|
||||
if (!node->getFastAttributeString(group_string, mGroup))
|
||||
{
|
||||
LL_WARNS() << "Bone without group " << mName << LL_ENDL;
|
||||
mGroup = "global";
|
||||
}
|
||||
|
||||
if (mIsJoint)
|
||||
{
|
||||
static LLStdStringHandle pivot_string = LLXmlTree::addAttributeString("pivot");
|
||||
|
|
@ -1623,6 +1641,21 @@ bool LLAvatarBoneInfo::parseXml(LLXmlTreeNode* node)
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
glm::mat4 LLAvatarBoneInfo::getJointMatrix()
|
||||
{
|
||||
glm::mat4 mat(1.0f);
|
||||
// 1. Scaling
|
||||
mat = glm::scale(mat, glm::vec3(mScale[0], mScale[1], mScale[2]));
|
||||
// 2. Rotation (Euler angles rad)
|
||||
mat = glm::rotate(mat, mRot[0], glm::vec3(1, 0, 0));
|
||||
mat = glm::rotate(mat, mRot[1], glm::vec3(0, 1, 0));
|
||||
mat = glm::rotate(mat, mRot[2], glm::vec3(0, 0, 1));
|
||||
// 3. Position
|
||||
mat = glm::translate(mat, glm::vec3(mPos[0], mPos[1], mPos[2]));
|
||||
return mat;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// LLAvatarSkeletonInfo::parseXml()
|
||||
//-----------------------------------------------------------------------------
|
||||
|
|
@ -1653,6 +1686,25 @@ bool LLAvatarSkeletonInfo::parseXml(LLXmlTreeNode* node)
|
|||
return true;
|
||||
}
|
||||
|
||||
void LLAvatarSkeletonInfo::getJointMatricesAndHierarhy(
|
||||
LLAvatarBoneInfo* bone_info,
|
||||
LLJointData& data,
|
||||
const glm::mat4& parent_mat)
|
||||
{
|
||||
data.mName = bone_info->mName;
|
||||
data.mJointMatrix = bone_info->getJointMatrix();
|
||||
data.mScale = glm::vec3(bone_info->mScale[0], bone_info->mScale[1], bone_info->mScale[2]);
|
||||
data.mRotation = bone_info->mRot;
|
||||
data.mRestMatrix = parent_mat * data.mJointMatrix;
|
||||
data.mIsJoint = bone_info->mIsJoint;
|
||||
data.mGroup = bone_info->mGroup;
|
||||
for (LLAvatarBoneInfo* child_info : bone_info->mChildren)
|
||||
{
|
||||
LLJointData& child_data = data.mChildren.emplace_back();
|
||||
getJointMatricesAndHierarhy(child_info, child_data, data.mRestMatrix);
|
||||
}
|
||||
}
|
||||
|
||||
//Make aliases for joint and push to map.
|
||||
void LLAvatarAppearance::makeJointAliases(LLAvatarBoneInfo *bone_info)
|
||||
{
|
||||
|
|
@ -1714,6 +1766,16 @@ const LLAvatarAppearance::joint_alias_map_t& LLAvatarAppearance::getJointAliases
|
|||
return mJointAliasMap;
|
||||
}
|
||||
|
||||
void LLAvatarAppearance::getJointMatricesAndHierarhy(std::vector<LLJointData> &data) const
|
||||
{
|
||||
glm::mat4 identity(1.f);
|
||||
for (LLAvatarBoneInfo* bone_info : sAvatarSkeletonInfo->mBoneInfoList)
|
||||
{
|
||||
LLJointData& child_data = data.emplace_back();
|
||||
LLAvatarSkeletonInfo::getJointMatricesAndHierarhy(bone_info, child_data, identity);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// parseXmlSkeletonNode(): parses <skeleton> nodes from XML tree
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@
|
|||
#include "lltexlayer.h"
|
||||
#include "llviewervisualparam.h"
|
||||
#include "llxmltree.h"
|
||||
#include "v4math.h"
|
||||
|
||||
class LLTexLayerSet;
|
||||
class LLTexGlobalColor;
|
||||
|
|
@ -41,6 +42,7 @@ class LLTexGlobalColorInfo;
|
|||
class LLWearableData;
|
||||
class LLAvatarBoneInfo;
|
||||
class LLAvatarSkeletonInfo;
|
||||
class LLJointData;
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// LLAvatarAppearance
|
||||
|
|
@ -153,7 +155,9 @@ public:
|
|||
const avatar_joint_list_t& getSkeleton() { return mSkeleton; }
|
||||
typedef std::map<std::string, std::string, std::less<>> joint_alias_map_t;
|
||||
const joint_alias_map_t& getJointAliases();
|
||||
|
||||
typedef std::map<std::string, std::string> joint_parent_map_t; // matrix plus parent
|
||||
typedef std::map<std::string, glm::mat4> joint_rest_map_t;
|
||||
void getJointMatricesAndHierarhy(std::vector<LLJointData> &data) const;
|
||||
|
||||
protected:
|
||||
static bool parseSkeletonFile(const std::string& filename, LLXmlTree& skeleton_xml_tree);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,66 @@
|
|||
/**
|
||||
* @file lljointdata.h
|
||||
* @brief LLJointData class for holding individual joint data and skeleton
|
||||
*
|
||||
* $LicenseInfo:firstyear=2025&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2025, 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$
|
||||
*/
|
||||
|
||||
#ifndef LL_LLJOINTDATA_H
|
||||
#define LL_LLJOINTDATA_H
|
||||
|
||||
#include "v4math.h"
|
||||
|
||||
// may be just move LLAvatarBoneInfo
|
||||
class LLJointData
|
||||
{
|
||||
public:
|
||||
std::string mName;
|
||||
std::string mGroup;
|
||||
glm::mat4 mJointMatrix;
|
||||
glm::mat4 mRestMatrix;
|
||||
glm::vec3 mScale;
|
||||
LLVector3 mRotation;
|
||||
|
||||
typedef std::vector<LLJointData> bones_t;
|
||||
bones_t mChildren;
|
||||
|
||||
bool mIsJoint; // if not, collision_volume
|
||||
enum SupportCategory
|
||||
{
|
||||
SUPPORT_BASE,
|
||||
SUPPORT_EXTENDED
|
||||
};
|
||||
SupportCategory mSupport;
|
||||
void setSupport(const std::string& support)
|
||||
{
|
||||
if (support == "extended")
|
||||
{
|
||||
mSupport = SUPPORT_EXTENDED;
|
||||
}
|
||||
else
|
||||
{
|
||||
mSupport = SUPPORT_BASE;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif //LL_LLJOINTDATA_H
|
||||
|
|
@ -1293,7 +1293,7 @@ void LLTexLayer::renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLC
|
|||
{
|
||||
if (!force_render && !hasMorph())
|
||||
{
|
||||
LL_DEBUGS() << "skipping renderMorphMasks for " << getUUID() << LL_ENDL;
|
||||
LL_DEBUGS("Morph") << "skipping renderMorphMasks for " << getUUID() << LL_ENDL;
|
||||
return;
|
||||
}
|
||||
LL_PROFILE_ZONE_SCOPED;
|
||||
|
|
@ -1325,7 +1325,7 @@ void LLTexLayer::renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLC
|
|||
success &= param->render( x, y, width, height );
|
||||
if (!success && !force_render)
|
||||
{
|
||||
LL_DEBUGS() << "Failed to render param " << param->getID() << " ; skipping morph mask." << LL_ENDL;
|
||||
LL_DEBUGS("Morph") << "Failed to render param " << param->getID() << " ; skipping morph mask." << LL_ENDL;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -1365,7 +1365,7 @@ void LLTexLayer::renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLC
|
|||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS() << "Skipping rendering of " << getInfo()->mStaticImageFileName
|
||||
LL_WARNS("Morph") << "Skipping rendering of " << getInfo()->mStaticImageFileName
|
||||
<< "; expected 1 or 4 components." << LL_ENDL;
|
||||
}
|
||||
}
|
||||
|
|
@ -1404,7 +1404,7 @@ void LLTexLayer::renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLC
|
|||
// We can get bad morph masks during login, on minimize, and occasional gl errors.
|
||||
// We should only be doing this when we believe something has changed with respect to the user's appearance.
|
||||
{
|
||||
LL_DEBUGS("Avatar") << "gl alpha cache of morph mask not found, doing readback: " << getName() << LL_ENDL;
|
||||
LL_DEBUGS("Morph") << "gl alpha cache of morph mask not found, doing readback: " << getName() << LL_ENDL;
|
||||
// clear out a slot if we have filled our cache
|
||||
S32 max_cache_entries = getTexLayerSet()->getAvatarAppearance()->isSelf() ? 4 : 1;
|
||||
while ((S32)mAlphaCache.size() >= max_cache_entries)
|
||||
|
|
@ -1444,7 +1444,13 @@ void LLTexLayer::renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLC
|
|||
}
|
||||
|
||||
glGetTexImage(LLTexUnit::getInternalType(LLTexUnit::TT_TEXTURE), 0, GL_RGBA, GL_UNSIGNED_BYTE, temp);
|
||||
|
||||
GLenum error = glGetError();
|
||||
if (error != GL_NO_ERROR)
|
||||
{
|
||||
LL_INFOS("Morph") << "GL Error while reading back morph texture. Error code: " << error << LL_ENDL;
|
||||
}
|
||||
else
|
||||
{
|
||||
U8* alpha_cursor = alpha_data;
|
||||
U8* pixel = temp;
|
||||
for (int i = 0; i < pixels; i++)
|
||||
|
|
@ -1452,6 +1458,7 @@ void LLTexLayer::renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLC
|
|||
*alpha_cursor++ = pixel[3];
|
||||
pixel += 4;
|
||||
}
|
||||
}
|
||||
|
||||
gGL.getTexUnit(0)->disable();
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
|
||||
#include "apr_portable.h"
|
||||
|
||||
#include "llapp.h"
|
||||
#include "llthread.h"
|
||||
#include "llmutex.h"
|
||||
|
||||
|
|
@ -35,6 +36,7 @@
|
|||
#include "lltrace.h"
|
||||
#include "lltracethreadrecorder.h"
|
||||
#include "llexception.h"
|
||||
#include "workqueue.h"
|
||||
|
||||
#if LL_LINUX
|
||||
#include <sched.h>
|
||||
|
|
@ -106,6 +108,27 @@ namespace
|
|||
return s_thread_id;
|
||||
}
|
||||
|
||||
#if LL_WINDOWS
|
||||
|
||||
static const U32 STATUS_MSC_EXCEPTION = 0xE06D7363; // compiler specific
|
||||
|
||||
U32 exception_filter(U32 code, struct _EXCEPTION_POINTERS* exception_infop)
|
||||
{
|
||||
if (LLApp::instance()->reportCrashToBugsplat((void*)exception_infop))
|
||||
{
|
||||
// Handled
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
else if (code == STATUS_MSC_EXCEPTION)
|
||||
{
|
||||
// C++ exception, go on
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
// handle it, convert to std::exception
|
||||
return EXCEPTION_EXECUTE_HANDLER;
|
||||
}
|
||||
#endif // LL_WINDOWS
|
||||
} // anonymous namespace
|
||||
|
||||
LL_COMMON_API bool on_main_thread()
|
||||
|
|
@ -157,20 +180,11 @@ void LLThread::threadRun()
|
|||
// Run the user supplied function
|
||||
do
|
||||
{
|
||||
try
|
||||
{
|
||||
run();
|
||||
}
|
||||
catch (const LLContinueError &e)
|
||||
{
|
||||
LL_WARNS("THREAD") << "ContinueException on thread '" << mName <<
|
||||
"' reentering run(). Error what is: '" << e.what() << "'" << LL_ENDL;
|
||||
//output possible call stacks to log file.
|
||||
LLError::LLCallStacks::print();
|
||||
|
||||
LOG_UNHANDLED_EXCEPTION("LLThread");
|
||||
continue;
|
||||
}
|
||||
#ifdef LL_WINDOWS
|
||||
sehHandle(); // Structured Exception Handling
|
||||
#else
|
||||
tryRun();
|
||||
#endif
|
||||
break;
|
||||
|
||||
} while (true);
|
||||
|
|
@ -188,6 +202,69 @@ void LLThread::threadRun()
|
|||
mStatus = STOPPED;
|
||||
}
|
||||
|
||||
void LLThread::tryRun()
|
||||
{
|
||||
try
|
||||
{
|
||||
run();
|
||||
}
|
||||
catch (const LLContinueError& e)
|
||||
{
|
||||
LL_WARNS("THREAD") << "ContinueException on thread '" << mName <<
|
||||
"'. Error what is: '" << e.what() << "'" << LL_ENDL;
|
||||
LLError::LLCallStacks::print();
|
||||
|
||||
LOG_UNHANDLED_EXCEPTION("LLThread");
|
||||
}
|
||||
catch (std::bad_alloc&)
|
||||
{
|
||||
// Todo: improve this, this is going to have a different callstack
|
||||
// instead of showing where it crashed
|
||||
LL_WARNS("THREAD") << "Out of memory in a thread: " << mName << LL_ENDL;
|
||||
|
||||
LL::WorkQueue::ptr_t main_queue = LL::WorkQueue::getInstance("mainloop");
|
||||
main_queue->post(
|
||||
// Bind the current exception, rethrow it in main loop.
|
||||
[]() {
|
||||
LLError::LLUserWarningMsg::showOutOfMemory();
|
||||
LL_ERRS("THREAD") << "Out of memory in a thread" << LL_ENDL;
|
||||
});
|
||||
}
|
||||
#ifndef LL_WINDOWS
|
||||
catch (...)
|
||||
{
|
||||
// Stash any other kind of uncaught exception to be rethrown by main thread.
|
||||
LL_WARNS("THREAD") << "Capturing and rethrowing uncaught exception in LLThread "
|
||||
<< mName << LL_ENDL;
|
||||
|
||||
LL::WorkQueue::ptr_t main_queue = LL::WorkQueue::getInstance("mainloop");
|
||||
main_queue->post(
|
||||
// Bind the current exception, rethrow it in main loop.
|
||||
[exc = std::current_exception()]() { std::rethrow_exception(exc); });
|
||||
}
|
||||
#endif // else LL_WINDOWS
|
||||
}
|
||||
|
||||
#ifdef LL_WINDOWS
|
||||
void LLThread::sehHandle()
|
||||
{
|
||||
__try
|
||||
{
|
||||
// handle stop and continue exceptions first
|
||||
tryRun();
|
||||
}
|
||||
__except (exception_filter(GetExceptionCode(), GetExceptionInformation()))
|
||||
{
|
||||
// convert to C++ styled exception
|
||||
// Note: it might be better to use _se_set_translator
|
||||
// if you want exception to inherit full callstack
|
||||
char integer_string[512];
|
||||
sprintf(integer_string, "SEH, code: %lu\n", GetExceptionCode());
|
||||
throw std::exception(integer_string);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
LLThread::LLThread(const std::string& name, apr_pool_t *poolp) :
|
||||
mPaused(false),
|
||||
mName(name),
|
||||
|
|
|
|||
|
|
@ -97,6 +97,11 @@ private:
|
|||
|
||||
// static function passed to APR thread creation routine
|
||||
void threadRun();
|
||||
void tryRun();
|
||||
|
||||
#ifdef LL_WINDOWS
|
||||
void sehHandle();
|
||||
#endif
|
||||
|
||||
protected:
|
||||
std::string mName;
|
||||
|
|
|
|||
|
|
@ -181,6 +181,8 @@ void LL::WorkQueueBase::callWork(const Work& work)
|
|||
LOG_UNHANDLED_EXCEPTION(getKey());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
if (getKey() != "mainloop")
|
||||
{
|
||||
// Stash any other kind of uncaught exception to be rethrown by main thread.
|
||||
LL_WARNS("LLCoros") << "Capturing and rethrowing uncaught exception in WorkQueueBase "
|
||||
|
|
@ -191,6 +193,12 @@ void LL::WorkQueueBase::callWork(const Work& work)
|
|||
// Bind the current exception, rethrow it in main loop.
|
||||
[exc = std::current_exception()]() { std::rethrow_exception(exc); });
|
||||
}
|
||||
else
|
||||
{
|
||||
// let main loop crash
|
||||
throw;
|
||||
}
|
||||
}
|
||||
#endif // else LL_WINDOWS
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ include(TinyGLTF)
|
|||
|
||||
set(llprimitive_SOURCE_FILES
|
||||
lldaeloader.cpp
|
||||
llgltfloader.cpp
|
||||
llgltfmaterial.cpp
|
||||
llmaterialid.cpp
|
||||
llmaterial.cpp
|
||||
|
|
@ -32,7 +31,6 @@ set(llprimitive_SOURCE_FILES
|
|||
set(llprimitive_HEADER_FILES
|
||||
CMakeLists.txt
|
||||
lldaeloader.h
|
||||
llgltfloader.h
|
||||
llgltfmaterial.h
|
||||
llgltfmaterial_templates.h
|
||||
legacy_object_types.h
|
||||
|
|
|
|||
|
|
@ -204,12 +204,15 @@ LLModel::EModelStatus load_face_from_dom_triangles(
|
|||
|
||||
if (idx_stride <= 0
|
||||
|| (pos_source && pos_offset >= idx_stride)
|
||||
|| (pos_source && pos_offset < 0)
|
||||
|| (tc_source && tc_offset >= idx_stride)
|
||||
|| (norm_source && norm_offset >= idx_stride))
|
||||
|| (tc_source && tc_offset < 0)
|
||||
|| (norm_source && norm_offset >= idx_stride)
|
||||
|| (norm_source && norm_offset < 0))
|
||||
{
|
||||
// Looks like these offsets should fit inside idx_stride
|
||||
// Might be good idea to also check idx.getCount()%idx_stride != 0
|
||||
LL_WARNS() << "Invalid pos_offset " << pos_offset << ", tc_offset " << tc_offset << " or norm_offset " << norm_offset << LL_ENDL;
|
||||
LL_WARNS() << "Invalid idx_stride " << idx_stride << ", pos_offset " << pos_offset << ", tc_offset " << tc_offset << " or norm_offset " << norm_offset << LL_ENDL;
|
||||
return LLModel::BAD_ELEMENT;
|
||||
}
|
||||
|
||||
|
|
@ -883,6 +886,7 @@ LLDAELoader::LLDAELoader(
|
|||
std::map<std::string, std::string, std::less<>>& jointAliasMap,
|
||||
U32 maxJointsPerMesh,
|
||||
U32 modelLimit,
|
||||
U32 debugMode,
|
||||
bool preprocess)
|
||||
: LLModelLoader(
|
||||
filename,
|
||||
|
|
@ -895,8 +899,9 @@ LLDAELoader::LLDAELoader(
|
|||
jointTransformMap,
|
||||
jointsFromNodes,
|
||||
jointAliasMap,
|
||||
maxJointsPerMesh),
|
||||
mGeneratedModelLimit(modelLimit),
|
||||
maxJointsPerMesh,
|
||||
modelLimit,
|
||||
debugMode),
|
||||
mPreprocessDAE(preprocess)
|
||||
{
|
||||
}
|
||||
|
|
@ -1680,6 +1685,7 @@ void LLDAELoader::processDomModel(LLModel* model, DAE* dae, daeElement* root, do
|
|||
{
|
||||
materials[model->mMaterialList[i]] = LLImportMaterial();
|
||||
}
|
||||
// todo: likely a bug here, shouldn't be using suffixed label, see how it gets used in other places.
|
||||
mScene[transformation].push_back(LLModelInstance(model, model->mLabel, transformation, materials));
|
||||
stretch_extents(model, transformation);
|
||||
}
|
||||
|
|
@ -2412,7 +2418,7 @@ std::string LLDAELoader::getElementLabel(daeElement *element)
|
|||
}
|
||||
|
||||
// static
|
||||
size_t LLDAELoader::getSuffixPosition(std::string label)
|
||||
size_t LLDAELoader::getSuffixPosition(const std::string &label)
|
||||
{
|
||||
if ((label.find("_LOD") != -1) || (label.find("_PHYS") != -1))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ public:
|
|||
std::map<std::string, std::string, std::less<>>& jointAliasMap,
|
||||
U32 maxJointsPerMesh,
|
||||
U32 modelLimit,
|
||||
U32 debugMode,
|
||||
bool preprocess);
|
||||
virtual ~LLDAELoader() ;
|
||||
|
||||
|
|
@ -97,13 +98,12 @@ protected:
|
|||
bool loadModelsFromDomMesh(domMesh* mesh, std::vector<LLModel*>& models_out, U32 submodel_limit);
|
||||
|
||||
static std::string getElementLabel(daeElement *element);
|
||||
static size_t getSuffixPosition(std::string label);
|
||||
static size_t getSuffixPosition(const std::string& label);
|
||||
static std::string getLodlessLabel(daeElement *element);
|
||||
|
||||
static std::string preprocessDAE(std::string filename);
|
||||
|
||||
private:
|
||||
U32 mGeneratedModelLimit; // Attempt to limit amount of generated submodels
|
||||
bool mPreprocessDAE;
|
||||
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,404 +0,0 @@
|
|||
/**
|
||||
* @file LLGLTFLoader.cpp
|
||||
* @brief LLGLTFLoader class implementation
|
||||
*
|
||||
* $LicenseInfo:firstyear=2022&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$
|
||||
*/
|
||||
|
||||
#include "llgltfloader.h"
|
||||
|
||||
// Import & define single-header gltf import/export lib
|
||||
#define TINYGLTF_IMPLEMENTATION
|
||||
#define TINYGLTF_USE_CPP14 // default is C++ 11
|
||||
|
||||
// tinygltf by default loads image files using STB
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
// to use our own image loading:
|
||||
// 1. replace this definition with TINYGLTF_NO_STB_IMAGE
|
||||
// 2. provide image loader callback with TinyGLTF::SetImageLoader(LoadimageDataFunction LoadImageData, void *user_data)
|
||||
|
||||
// tinygltf saves image files using STB
|
||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
// similarly, can override with TINYGLTF_NO_STB_IMAGE_WRITE and TinyGLTF::SetImageWriter(fxn, data)
|
||||
|
||||
// Additionally, disable inclusion of STB header files entirely with
|
||||
// TINYGLTF_NO_INCLUDE_STB_IMAGE
|
||||
// TINYGLTF_NO_INCLUDE_STB_IMAGE_WRITE
|
||||
#include "tinygltf/tiny_gltf.h"
|
||||
|
||||
|
||||
// TODO: includes inherited from dae loader. Validate / prune
|
||||
|
||||
#include "llsdserialize.h"
|
||||
#include "lljoint.h"
|
||||
|
||||
#include "llmatrix4a.h"
|
||||
|
||||
#include <boost/regex.hpp>
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
|
||||
static const std::string lod_suffix[LLModel::NUM_LODS] =
|
||||
{
|
||||
"_LOD0",
|
||||
"_LOD1",
|
||||
"_LOD2",
|
||||
"",
|
||||
"_PHYS",
|
||||
};
|
||||
|
||||
|
||||
LLGLTFLoader::LLGLTFLoader(std::string filename,
|
||||
S32 lod,
|
||||
LLModelLoader::load_callback_t load_cb,
|
||||
LLModelLoader::joint_lookup_func_t joint_lookup_func,
|
||||
LLModelLoader::texture_load_func_t texture_load_func,
|
||||
LLModelLoader::state_callback_t state_cb,
|
||||
void * opaque_userdata,
|
||||
JointTransformMap & jointTransformMap,
|
||||
JointNameSet & jointsFromNodes,
|
||||
std::map<std::string, std::string, std::less<>> & jointAliasMap,
|
||||
U32 maxJointsPerMesh,
|
||||
U32 modelLimit) //,
|
||||
//bool preprocess)
|
||||
: LLModelLoader( filename,
|
||||
lod,
|
||||
load_cb,
|
||||
joint_lookup_func,
|
||||
texture_load_func,
|
||||
state_cb,
|
||||
opaque_userdata,
|
||||
jointTransformMap,
|
||||
jointsFromNodes,
|
||||
jointAliasMap,
|
||||
maxJointsPerMesh ),
|
||||
//mPreprocessGLTF(preprocess),
|
||||
mMeshesLoaded(false),
|
||||
mMaterialsLoaded(false)
|
||||
{
|
||||
}
|
||||
|
||||
LLGLTFLoader::~LLGLTFLoader() {}
|
||||
|
||||
bool LLGLTFLoader::OpenFile(const std::string &filename)
|
||||
{
|
||||
tinygltf::TinyGLTF loader;
|
||||
std::string error_msg;
|
||||
std::string warn_msg;
|
||||
std::string filename_lc(filename);
|
||||
LLStringUtil::toLower(filename_lc);
|
||||
|
||||
// Load a tinygltf model fom a file. Assumes that the input filename has already been
|
||||
// been sanitized to one of (.gltf , .glb) extensions, so does a simple find to distinguish.
|
||||
if (std::string::npos == filename_lc.rfind(".gltf"))
|
||||
{ // file is binary
|
||||
mGltfLoaded = loader.LoadBinaryFromFile(&mGltfModel, &error_msg, &warn_msg, filename);
|
||||
}
|
||||
else
|
||||
{ // file is ascii
|
||||
mGltfLoaded = loader.LoadASCIIFromFile(&mGltfModel, &error_msg, &warn_msg, filename);
|
||||
}
|
||||
|
||||
if (!mGltfLoaded)
|
||||
{
|
||||
if (!warn_msg.empty())
|
||||
LL_WARNS("GLTF_IMPORT") << "gltf load warning: " << warn_msg.c_str() << LL_ENDL;
|
||||
if (!error_msg.empty())
|
||||
LL_WARNS("GLTF_IMPORT") << "gltf load error: " << error_msg.c_str() << LL_ENDL;
|
||||
return false;
|
||||
}
|
||||
|
||||
mMeshesLoaded = parseMeshes();
|
||||
if (mMeshesLoaded) uploadMeshes();
|
||||
|
||||
mMaterialsLoaded = parseMaterials();
|
||||
if (mMaterialsLoaded) uploadMaterials();
|
||||
|
||||
return (mMeshesLoaded || mMaterialsLoaded);
|
||||
}
|
||||
|
||||
bool LLGLTFLoader::parseMeshes()
|
||||
{
|
||||
if (!mGltfLoaded) return false;
|
||||
|
||||
// 2022-04 DJH Volume params from dae example. TODO understand PCODE
|
||||
LLVolumeParams volume_params;
|
||||
volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE);
|
||||
|
||||
for (tinygltf::Mesh mesh : mGltfModel.meshes)
|
||||
{
|
||||
LLModel *pModel = new LLModel(volume_params, 0.f);
|
||||
|
||||
if (populateModelFromMesh(pModel, mesh) &&
|
||||
(LLModel::NO_ERRORS == pModel->getStatus()) &&
|
||||
validate_model(pModel))
|
||||
{
|
||||
mModelList.push_back(pModel);
|
||||
}
|
||||
else
|
||||
{
|
||||
setLoadState(ERROR_MODEL + pModel->getStatus());
|
||||
delete(pModel);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const tinygltf::Mesh &mesh)
|
||||
{
|
||||
pModel->mLabel = mesh.name;
|
||||
int pos_idx;
|
||||
tinygltf::Accessor indices_a, positions_a, normals_a, uv0_a, color0_a;
|
||||
|
||||
auto prims = mesh.primitives;
|
||||
for (auto prim : prims)
|
||||
{
|
||||
if (prim.indices >= 0) indices_a = mGltfModel.accessors[prim.indices];
|
||||
|
||||
pos_idx = (prim.attributes.count("POSITION") > 0) ? prim.attributes.at("POSITION") : -1;
|
||||
if (pos_idx >= 0)
|
||||
{
|
||||
positions_a = mGltfModel.accessors[pos_idx];
|
||||
if (TINYGLTF_COMPONENT_TYPE_FLOAT != positions_a.componentType)
|
||||
continue;
|
||||
auto positions_bv = mGltfModel.bufferViews[positions_a.bufferView];
|
||||
auto positions_buf = mGltfModel.buffers[positions_bv.buffer];
|
||||
//auto type = positions_vb.
|
||||
//if (positions_buf.name
|
||||
}
|
||||
|
||||
#if 0
|
||||
int norm_idx, tan_idx, uv0_idx, uv1_idx, color0_idx, color1_idx;
|
||||
norm_idx = (prim.attributes.count("NORMAL") > 0) ? prim.attributes.at("NORMAL") : -1;
|
||||
tan_idx = (prim.attributes.count("TANGENT") > 0) ? prim.attributes.at("TANGENT") : -1;
|
||||
uv0_idx = (prim.attributes.count("TEXCOORDS_0") > 0) ? prim.attributes.at("TEXCOORDS_0") : -1;
|
||||
uv1_idx = (prim.attributes.count("TEXCOORDS_1") > 0) ? prim.attributes.at("TEXCOORDS_1") : -1;
|
||||
color0_idx = (prim.attributes.count("COLOR_0") > 0) ? prim.attributes.at("COLOR_0") : -1;
|
||||
color1_idx = (prim.attributes.count("COLOR_1") > 0) ? prim.attributes.at("COLOR_1") : -1;
|
||||
#endif
|
||||
|
||||
if (prim.mode == TINYGLTF_MODE_TRIANGLES)
|
||||
{
|
||||
//auto pos = mesh. TODO resume here DJH 2022-04
|
||||
}
|
||||
}
|
||||
|
||||
//pModel->addFace()
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LLGLTFLoader::parseMaterials()
|
||||
{
|
||||
if (!mGltfLoaded) return false;
|
||||
|
||||
// fill local texture data structures
|
||||
mSamplers.clear();
|
||||
for (auto in_sampler : mGltfModel.samplers)
|
||||
{
|
||||
gltf_sampler sampler;
|
||||
sampler.magFilter = in_sampler.magFilter > 0 ? in_sampler.magFilter : GL_LINEAR;
|
||||
sampler.minFilter = in_sampler.minFilter > 0 ? in_sampler.minFilter : GL_LINEAR;;
|
||||
sampler.wrapS = in_sampler.wrapS;
|
||||
sampler.wrapT = in_sampler.wrapT;
|
||||
sampler.name = in_sampler.name; // unused
|
||||
mSamplers.push_back(sampler);
|
||||
}
|
||||
|
||||
mImages.clear();
|
||||
for (auto in_image : mGltfModel.images)
|
||||
{
|
||||
gltf_image image;
|
||||
image.numChannels = in_image.component;
|
||||
image.bytesPerChannel = in_image.bits >> 3; // Convert bits to bytes
|
||||
image.pixelType = in_image.pixel_type; // Maps exactly, i.e. TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE == GL_UNSIGNED_BYTE, etc
|
||||
image.size = static_cast<U32>(in_image.image.size());
|
||||
image.height = in_image.height;
|
||||
image.width = in_image.width;
|
||||
image.data = in_image.image.data();
|
||||
|
||||
if (in_image.as_is)
|
||||
{
|
||||
LL_WARNS("GLTF_IMPORT") << "Unsupported image encoding" << LL_ENDL;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (image.size != image.height * image.width * image.numChannels * image.bytesPerChannel)
|
||||
{
|
||||
LL_WARNS("GLTF_IMPORT") << "Image size error" << LL_ENDL;
|
||||
return false;
|
||||
}
|
||||
|
||||
mImages.push_back(image);
|
||||
}
|
||||
|
||||
mTextures.clear();
|
||||
for (auto in_tex : mGltfModel.textures)
|
||||
{
|
||||
gltf_texture tex;
|
||||
tex.imageIdx = in_tex.source;
|
||||
tex.samplerIdx = in_tex.sampler;
|
||||
tex.imageUuid.setNull();
|
||||
|
||||
if (tex.imageIdx >= mImages.size() || tex.samplerIdx >= mSamplers.size())
|
||||
{
|
||||
LL_WARNS("GLTF_IMPORT") << "Texture sampler/image index error" << LL_ENDL;
|
||||
return false;
|
||||
}
|
||||
|
||||
mTextures.push_back(tex);
|
||||
}
|
||||
|
||||
// parse each material
|
||||
for (tinygltf::Material gltf_material : mGltfModel.materials)
|
||||
{
|
||||
gltf_render_material mat;
|
||||
mat.name = gltf_material.name;
|
||||
|
||||
tinygltf::PbrMetallicRoughness& pbr = gltf_material.pbrMetallicRoughness;
|
||||
mat.hasPBR = true; // Always true, for now
|
||||
|
||||
mat.baseColor.set(pbr.baseColorFactor.data());
|
||||
mat.hasBaseTex = pbr.baseColorTexture.index >= 0;
|
||||
mat.baseColorTexIdx = pbr.baseColorTexture.index;
|
||||
mat.baseColorTexCoords = pbr.baseColorTexture.texCoord;
|
||||
|
||||
mat.metalness = pbr.metallicFactor;
|
||||
mat.roughness = pbr.roughnessFactor;
|
||||
mat.hasMRTex = pbr.metallicRoughnessTexture.index >= 0;
|
||||
mat.metalRoughTexIdx = pbr.metallicRoughnessTexture.index;
|
||||
mat.metalRoughTexCoords = pbr.metallicRoughnessTexture.texCoord;
|
||||
|
||||
mat.normalScale = gltf_material.normalTexture.scale;
|
||||
mat.hasNormalTex = gltf_material.normalTexture.index >= 0;
|
||||
mat.normalTexIdx = gltf_material.normalTexture.index;
|
||||
mat.normalTexCoords = gltf_material.normalTexture.texCoord;
|
||||
|
||||
mat.occlusionScale = gltf_material.occlusionTexture.strength;
|
||||
mat.hasOcclusionTex = gltf_material.occlusionTexture.index >= 0;
|
||||
mat.occlusionTexIdx = gltf_material.occlusionTexture.index;
|
||||
mat.occlusionTexCoords = gltf_material.occlusionTexture.texCoord;
|
||||
|
||||
mat.emissiveColor.set(gltf_material.emissiveFactor.data());
|
||||
mat.hasEmissiveTex = gltf_material.emissiveTexture.index >= 0;
|
||||
mat.emissiveTexIdx = gltf_material.emissiveTexture.index;
|
||||
mat.emissiveTexCoords = gltf_material.emissiveTexture.texCoord;
|
||||
|
||||
mat.alphaMode = gltf_material.alphaMode;
|
||||
mat.alphaMask = gltf_material.alphaCutoff;
|
||||
|
||||
if ((mat.hasNormalTex && (mat.normalTexIdx >= mTextures.size())) ||
|
||||
(mat.hasOcclusionTex && (mat.occlusionTexIdx >= mTextures.size())) ||
|
||||
(mat.hasEmissiveTex && (mat.emissiveTexIdx >= mTextures.size())) ||
|
||||
(mat.hasBaseTex && (mat.baseColorTexIdx >= mTextures.size())) ||
|
||||
(mat.hasMRTex && (mat.metalRoughTexIdx >= mTextures.size())))
|
||||
{
|
||||
LL_WARNS("GLTF_IMPORT") << "Texture resource index error" << LL_ENDL;
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((mat.hasNormalTex && (mat.normalTexCoords > 2)) || // mesh can have up to 3 sets of UV
|
||||
(mat.hasOcclusionTex && (mat.occlusionTexCoords > 2)) ||
|
||||
(mat.hasEmissiveTex && (mat.emissiveTexCoords > 2)) ||
|
||||
(mat.hasBaseTex && (mat.baseColorTexCoords > 2)) ||
|
||||
(mat.hasMRTex && (mat.metalRoughTexCoords > 2)))
|
||||
{
|
||||
LL_WARNS("GLTF_IMPORT") << "Image texcoord index error" << LL_ENDL;
|
||||
return false;
|
||||
}
|
||||
|
||||
mMaterials.push_back(mat);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: convert raw vertex buffers to UUIDs
|
||||
void LLGLTFLoader::uploadMeshes()
|
||||
{
|
||||
llassert(0);
|
||||
}
|
||||
|
||||
// convert raw image buffers to texture UUIDs & assemble into a render material
|
||||
void LLGLTFLoader::uploadMaterials()
|
||||
{
|
||||
for (gltf_render_material mat : mMaterials) // Initially 1 material per gltf file, but design for multiple
|
||||
{
|
||||
if (mat.hasBaseTex)
|
||||
{
|
||||
gltf_texture& gtex = mTextures[mat.baseColorTexIdx];
|
||||
if (gtex.imageUuid.isNull())
|
||||
{
|
||||
gtex.imageUuid = imageBufferToTextureUUID(gtex);
|
||||
}
|
||||
}
|
||||
|
||||
if (mat.hasMRTex)
|
||||
{
|
||||
gltf_texture& gtex = mTextures[mat.metalRoughTexIdx];
|
||||
if (gtex.imageUuid.isNull())
|
||||
{
|
||||
gtex.imageUuid = imageBufferToTextureUUID(gtex);
|
||||
}
|
||||
}
|
||||
|
||||
if (mat.hasNormalTex)
|
||||
{
|
||||
gltf_texture& gtex = mTextures[mat.normalTexIdx];
|
||||
if (gtex.imageUuid.isNull())
|
||||
{
|
||||
gtex.imageUuid = imageBufferToTextureUUID(gtex);
|
||||
}
|
||||
}
|
||||
|
||||
if (mat.hasOcclusionTex)
|
||||
{
|
||||
gltf_texture& gtex = mTextures[mat.occlusionTexIdx];
|
||||
if (gtex.imageUuid.isNull())
|
||||
{
|
||||
gtex.imageUuid = imageBufferToTextureUUID(gtex);
|
||||
}
|
||||
}
|
||||
|
||||
if (mat.hasEmissiveTex)
|
||||
{
|
||||
gltf_texture& gtex = mTextures[mat.emissiveTexIdx];
|
||||
if (gtex.imageUuid.isNull())
|
||||
{
|
||||
gtex.imageUuid = imageBufferToTextureUUID(gtex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LLUUID LLGLTFLoader::imageBufferToTextureUUID(const gltf_texture& tex)
|
||||
{
|
||||
//gltf_image& image = mImages[tex.imageIdx];
|
||||
//gltf_sampler& sampler = mSamplers[tex.samplerIdx];
|
||||
|
||||
// fill an LLSD container with image+sampler data
|
||||
|
||||
// upload texture
|
||||
|
||||
// retrieve UUID
|
||||
|
||||
return LLUUID::null;
|
||||
}
|
||||
|
|
@ -1,206 +0,0 @@
|
|||
/**
|
||||
* @file LLGLTFLoader.h
|
||||
* @brief LLGLTFLoader class definition
|
||||
*
|
||||
* $LicenseInfo:firstyear=2022&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$
|
||||
*/
|
||||
|
||||
#ifndef LL_LLGLTFLoader_H
|
||||
#define LL_LLGLTFLoader_H
|
||||
|
||||
#include "tinygltf/tiny_gltf.h"
|
||||
|
||||
#include "llglheaders.h"
|
||||
#include "llmodelloader.h"
|
||||
|
||||
// gltf_* structs are temporary, used to organize the subset of data that eventually goes into the material LLSD
|
||||
|
||||
class gltf_sampler
|
||||
{
|
||||
public:
|
||||
// Uses GL enums
|
||||
S32 minFilter; // GL_NEAREST, GL_LINEAR, GL_NEAREST_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_NEAREST, GL_NEAREST_MIPMAP_LINEAR or GL_LINEAR_MIPMAP_LINEAR
|
||||
S32 magFilter; // GL_NEAREST or GL_LINEAR
|
||||
S32 wrapS; // GL_CLAMP_TO_EDGE, GL_MIRRORED_REPEAT or GL_REPEAT
|
||||
S32 wrapT; // GL_CLAMP_TO_EDGE, GL_MIRRORED_REPEAT or GL_REPEAT
|
||||
//S32 wrapR; // Found in some sample files, but not part of glTF 2.0 spec. Ignored.
|
||||
std::string name; // optional, currently unused
|
||||
// extensions and extras are sampler optional fields that we don't support - at least initially
|
||||
};
|
||||
|
||||
class gltf_image
|
||||
{
|
||||
public:// Note that glTF images are defined with row 0 at the top (opposite of OpenGL)
|
||||
U8* data; // ptr to decoded image data
|
||||
U32 size; // in bytes, regardless of channel width
|
||||
U32 width;
|
||||
U32 height;
|
||||
U32 numChannels; // range 1..4
|
||||
U32 bytesPerChannel; // converted from gltf "bits", expects only 8, 16 or 32 as input
|
||||
U32 pixelType; // one of (TINYGLTF_COMPONENT_TYPE)_UNSIGNED_BYTE, _UNSIGNED_SHORT, _UNSIGNED_INT, or _FLOAT
|
||||
};
|
||||
|
||||
class gltf_texture
|
||||
{
|
||||
public:
|
||||
U32 imageIdx;
|
||||
U32 samplerIdx;
|
||||
LLUUID imageUuid = LLUUID::null;
|
||||
};
|
||||
|
||||
class gltf_render_material
|
||||
{
|
||||
public:
|
||||
std::string name;
|
||||
|
||||
// scalar values
|
||||
LLColor4 baseColor; // linear encoding. Multiplied with vertex color, if present.
|
||||
double metalness;
|
||||
double roughness;
|
||||
double normalScale; // scale applies only to X,Y components of normal
|
||||
double occlusionScale; // strength multiplier for occlusion
|
||||
LLColor4 emissiveColor; // emissive mulitiplier, assumed linear encoding (spec 2.0 is silent)
|
||||
std::string alphaMode; // "OPAQUE", "MASK" or "BLEND"
|
||||
double alphaMask; // alpha cut-off
|
||||
|
||||
// textures
|
||||
U32 baseColorTexIdx; // always sRGB encoded
|
||||
U32 metalRoughTexIdx; // always linear, roughness in G channel, metalness in B channel
|
||||
U32 normalTexIdx; // linear, valid range R[0-1], G[0-1], B[0.5-1]. Normal = texel * 2 - vec3(1.0)
|
||||
U32 occlusionTexIdx; // linear, occlusion in R channel, 0 meaning fully occluded, 1 meaning not occluded
|
||||
U32 emissiveTexIdx; // always stored as sRGB, in nits (candela / meter^2)
|
||||
|
||||
// texture coordinates
|
||||
U32 baseColorTexCoords;
|
||||
U32 metalRoughTexCoords;
|
||||
U32 normalTexCoords;
|
||||
U32 occlusionTexCoords;
|
||||
U32 emissiveTexCoords;
|
||||
|
||||
// TODO: Add traditional (diffuse, normal, specular) UUIDs here, or add this struct to LL_TextureEntry??
|
||||
|
||||
bool hasPBR;
|
||||
bool hasBaseTex, hasMRTex, hasNormalTex, hasOcclusionTex, hasEmissiveTex;
|
||||
|
||||
// This field is populated after upload
|
||||
LLUUID material_uuid = LLUUID::null;
|
||||
|
||||
};
|
||||
|
||||
class gltf_mesh
|
||||
{
|
||||
public:
|
||||
std::string name;
|
||||
|
||||
// TODO add mesh import DJH 2022-04
|
||||
|
||||
};
|
||||
|
||||
class LLGLTFLoader : public LLModelLoader
|
||||
{
|
||||
public:
|
||||
typedef std::map<std::string, LLImportMaterial> material_map;
|
||||
|
||||
LLGLTFLoader(std::string filename,
|
||||
S32 lod,
|
||||
LLModelLoader::load_callback_t load_cb,
|
||||
LLModelLoader::joint_lookup_func_t joint_lookup_func,
|
||||
LLModelLoader::texture_load_func_t texture_load_func,
|
||||
LLModelLoader::state_callback_t state_cb,
|
||||
void * opaque_userdata,
|
||||
JointTransformMap & jointTransformMap,
|
||||
JointNameSet & jointsFromNodes,
|
||||
std::map<std::string, std::string,std::less<>> &jointAliasMap,
|
||||
U32 maxJointsPerMesh,
|
||||
U32 modelLimit); //,
|
||||
//bool preprocess );
|
||||
virtual ~LLGLTFLoader();
|
||||
|
||||
virtual bool OpenFile(const std::string &filename);
|
||||
|
||||
protected:
|
||||
tinygltf::Model mGltfModel;
|
||||
bool mGltfLoaded;
|
||||
bool mMeshesLoaded;
|
||||
bool mMaterialsLoaded;
|
||||
|
||||
std::vector<gltf_mesh> mMeshes;
|
||||
std::vector<gltf_render_material> mMaterials;
|
||||
|
||||
std::vector<gltf_texture> mTextures;
|
||||
std::vector<gltf_image> mImages;
|
||||
std::vector<gltf_sampler> mSamplers;
|
||||
|
||||
private:
|
||||
bool parseMeshes();
|
||||
void uploadMeshes();
|
||||
bool parseMaterials();
|
||||
void uploadMaterials();
|
||||
bool populateModelFromMesh(LLModel* pModel, const tinygltf::Mesh &mesh);
|
||||
LLUUID imageBufferToTextureUUID(const gltf_texture& tex);
|
||||
|
||||
// bool mPreprocessGLTF;
|
||||
|
||||
/* Below inherited from dae loader - unknown if/how useful here
|
||||
|
||||
void processElement(gltfElement *element, bool &badElement, GLTF *gltf);
|
||||
void processGltfModel(LLModel *model, GLTF *gltf, gltfElement *pRoot, gltfMesh *mesh, gltfSkin *skin);
|
||||
|
||||
material_map getMaterials(LLModel *model, gltfInstance_geometry *instance_geo, GLTF *gltf);
|
||||
LLImportMaterial profileToMaterial(gltfProfile_COMMON *material, GLTF *gltf);
|
||||
LLColor4 getGltfColor(gltfElement *element);
|
||||
|
||||
gltfElement *getChildFromElement(gltfElement *pElement, std::string const &name);
|
||||
|
||||
bool isNodeAJoint(gltfNode *pNode);
|
||||
void processJointNode(gltfNode *pNode, std::map<std::string, LLMatrix4> &jointTransforms);
|
||||
void extractTranslation(gltfTranslate *pTranslate, LLMatrix4 &transform);
|
||||
void extractTranslationViaElement(gltfElement *pTranslateElement, LLMatrix4 &transform);
|
||||
void extractTranslationViaSID(gltfElement *pElement, LLMatrix4 &transform);
|
||||
void buildJointToNodeMappingFromScene(gltfElement *pRoot);
|
||||
void processJointToNodeMapping(gltfNode *pNode);
|
||||
void processChildJoints(gltfNode *pParentNode);
|
||||
|
||||
bool verifyCount(int expected, int result);
|
||||
|
||||
// Verify that a controller matches vertex counts
|
||||
bool verifyController(gltfController *pController);
|
||||
|
||||
static bool addVolumeFacesFromGltfMesh(LLModel *model, gltfMesh *mesh, LLSD &log_msg);
|
||||
static bool createVolumeFacesFromGltfMesh(LLModel *model, gltfMesh *mesh);
|
||||
|
||||
static LLModel *loadModelFromGltfMesh(gltfMesh *mesh);
|
||||
|
||||
// Loads a mesh breaking it into one or more models as necessary
|
||||
// to get around volume face limitations while retaining >8 materials
|
||||
//
|
||||
bool loadModelsFromGltfMesh(gltfMesh *mesh, std::vector<LLModel *> &models_out, U32 submodel_limit);
|
||||
|
||||
static std::string getElementLabel(gltfElement *element);
|
||||
static size_t getSuffixPosition(std::string label);
|
||||
static std::string getLodlessLabel(gltfElement *element);
|
||||
|
||||
static std::string preprocessGLTF(std::string filename);
|
||||
*/
|
||||
|
||||
};
|
||||
#endif // LL_LLGLTFLLOADER_H
|
||||
|
|
@ -334,6 +334,162 @@ void LLModel::normalizeVolumeFaces()
|
|||
}
|
||||
}
|
||||
|
||||
void LLModel::normalizeVolumeFacesAndWeights()
|
||||
{
|
||||
if (!mVolumeFaces.empty())
|
||||
{
|
||||
LLVector4a min, max;
|
||||
|
||||
// For all of the volume faces
|
||||
// in the model, loop over
|
||||
// them and see what the extents
|
||||
// of the volume along each axis.
|
||||
min = mVolumeFaces[0].mExtents[0];
|
||||
max = mVolumeFaces[0].mExtents[1];
|
||||
|
||||
for (U32 i = 1; i < mVolumeFaces.size(); ++i)
|
||||
{
|
||||
LLVolumeFace& face = mVolumeFaces[i];
|
||||
|
||||
update_min_max(min, max, face.mExtents[0]);
|
||||
update_min_max(min, max, face.mExtents[1]);
|
||||
|
||||
if (face.mTexCoords)
|
||||
{
|
||||
LLVector2& min_tc = face.mTexCoordExtents[0];
|
||||
LLVector2& max_tc = face.mTexCoordExtents[1];
|
||||
|
||||
min_tc = face.mTexCoords[0];
|
||||
max_tc = face.mTexCoords[0];
|
||||
|
||||
for (S32 j = 1; j < face.mNumVertices; ++j)
|
||||
{
|
||||
update_min_max(min_tc, max_tc, face.mTexCoords[j]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
face.mTexCoordExtents[0].set(0, 0);
|
||||
face.mTexCoordExtents[1].set(1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Now that we have the extents of the model
|
||||
// we can compute the offset needed to center
|
||||
// the model at the origin.
|
||||
|
||||
// Compute center of the model
|
||||
// and make it negative to get translation
|
||||
// needed to center at origin.
|
||||
LLVector4a trans;
|
||||
trans.setAdd(min, max);
|
||||
trans.mul(-0.5f);
|
||||
|
||||
// Compute the total size along all
|
||||
// axes of the model.
|
||||
LLVector4a size;
|
||||
size.setSub(max, min);
|
||||
|
||||
// Prevent division by zero.
|
||||
F32 x = size[0];
|
||||
F32 y = size[1];
|
||||
F32 z = size[2];
|
||||
F32 w = size[3];
|
||||
if (fabs(x) < F_APPROXIMATELY_ZERO)
|
||||
{
|
||||
x = 1.0;
|
||||
}
|
||||
if (fabs(y) < F_APPROXIMATELY_ZERO)
|
||||
{
|
||||
y = 1.0;
|
||||
}
|
||||
if (fabs(z) < F_APPROXIMATELY_ZERO)
|
||||
{
|
||||
z = 1.0;
|
||||
}
|
||||
size.set(x, y, z, w);
|
||||
|
||||
// Compute scale as reciprocal of size
|
||||
LLVector4a scale;
|
||||
scale.splat(1.f);
|
||||
scale.div(size);
|
||||
|
||||
LLVector4a inv_scale(1.f);
|
||||
inv_scale.div(scale);
|
||||
|
||||
for (U32 i = 0; i < mVolumeFaces.size(); ++i)
|
||||
{
|
||||
LLVolumeFace& face = mVolumeFaces[i];
|
||||
|
||||
// We shrink the extents so
|
||||
// that they fall within
|
||||
// the unit cube.
|
||||
// VFExtents change
|
||||
face.mExtents[0].add(trans);
|
||||
face.mExtents[0].mul(scale);
|
||||
|
||||
face.mExtents[1].add(trans);
|
||||
face.mExtents[1].mul(scale);
|
||||
|
||||
// For all the positions, we scale
|
||||
// the positions to fit within the unit cube.
|
||||
LLVector4a* pos = (LLVector4a*)face.mPositions;
|
||||
LLVector4a* norm = (LLVector4a*)face.mNormals;
|
||||
LLVector4a* t = (LLVector4a*)face.mTangents;
|
||||
|
||||
for (S32 j = 0; j < face.mNumVertices; ++j)
|
||||
{
|
||||
pos[j].add(trans);
|
||||
pos[j].mul(scale);
|
||||
if (norm && !norm[j].equals3(LLVector4a::getZero()))
|
||||
{
|
||||
norm[j].mul(inv_scale);
|
||||
norm[j].normalize3();
|
||||
}
|
||||
|
||||
if (t)
|
||||
{
|
||||
F32 w = t[j].getF32ptr()[3];
|
||||
t[j].mul(inv_scale);
|
||||
t[j].normalize3();
|
||||
t[j].getF32ptr()[3] = w;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
weight_map old_weights = mSkinWeights;
|
||||
mSkinWeights.clear();
|
||||
mPosition.clear();
|
||||
|
||||
for (auto& weights : old_weights)
|
||||
{
|
||||
LLVector4a pos(weights.first.mV[VX], weights.first.mV[VY], weights.first.mV[VZ]);
|
||||
pos.add(trans);
|
||||
pos.mul(scale);
|
||||
LLVector3 scaled_pos(pos.getF32ptr());
|
||||
mPosition.push_back(scaled_pos);
|
||||
mSkinWeights[scaled_pos] = weights.second;
|
||||
}
|
||||
|
||||
// mNormalizedScale is the scale at which
|
||||
// we would need to multiply the model
|
||||
// by to get the original size of the
|
||||
// model instead of the normalized size.
|
||||
LLVector4a normalized_scale;
|
||||
normalized_scale.splat(1.f);
|
||||
normalized_scale.div(scale);
|
||||
mNormalizedScale.set(normalized_scale.getF32ptr());
|
||||
mNormalizedTranslation.set(trans.getF32ptr());
|
||||
mNormalizedTranslation *= -1.f;
|
||||
|
||||
// remember normalized scale so original dimensions can be recovered for mesh processing (i.e. tangent generation)
|
||||
for (auto& face : mVolumeFaces)
|
||||
{
|
||||
face.mNormalizedScale = mNormalizedScale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LLModel::getNormalizedScaleTranslation(LLVector3& scale_out, LLVector3& translation_out) const
|
||||
{
|
||||
scale_out = mNormalizedScale;
|
||||
|
|
@ -662,7 +818,7 @@ LLSD LLModel::writeModel(
|
|||
bool upload_skin,
|
||||
bool upload_joints,
|
||||
bool lock_scale_if_joint_position,
|
||||
bool nowrite,
|
||||
EWriteModelMode write_mode,
|
||||
bool as_slm,
|
||||
int submodel_id)
|
||||
{
|
||||
|
|
@ -941,10 +1097,10 @@ LLSD LLModel::writeModel(
|
|||
}
|
||||
}
|
||||
|
||||
return writeModelToStream(ostr, mdl, nowrite, as_slm);
|
||||
return writeModelToStream(ostr, mdl, write_mode, as_slm);
|
||||
}
|
||||
|
||||
LLSD LLModel::writeModelToStream(std::ostream& ostr, LLSD& mdl, bool nowrite, bool as_slm)
|
||||
LLSD LLModel::writeModelToStream(std::ostream& ostr, LLSD& mdl, EWriteModelMode write_mode, bool as_slm)
|
||||
{
|
||||
std::string::size_type cur_offset = 0;
|
||||
|
||||
|
|
@ -1006,7 +1162,11 @@ LLSD LLModel::writeModelToStream(std::ostream& ostr, LLSD& mdl, bool nowrite, bo
|
|||
}
|
||||
}
|
||||
|
||||
if (!nowrite)
|
||||
if (write_mode == WRITE_HUMAN)
|
||||
{
|
||||
ostr << mdl;
|
||||
}
|
||||
else if (write_mode == WRITE_BINARY)
|
||||
{
|
||||
LLSDSerialize::toBinary(header, ostr);
|
||||
|
||||
|
|
@ -1561,11 +1721,21 @@ LLSD LLMeshSkinInfo::asLLSD(bool include_joints, bool lock_scale_if_joint_positi
|
|||
{
|
||||
ret["joint_names"][i] = mJointNames[i];
|
||||
|
||||
// For model to work at all there must be a matching bind matrix,
|
||||
// so supply an indentity one if it isn't true
|
||||
// Note: can build an actual bind matrix from joints
|
||||
const LLMatrix4a& inv_bind = mInvBindMatrix.size() > i ? mInvBindMatrix[i] : LLMatrix4a::identity();
|
||||
if (i >= mInvBindMatrix.size())
|
||||
{
|
||||
LL_WARNS("MESHSKININFO") << "Joint index " << i << " (" << mJointNames[i] << ") exceeds inverse bind matrix size "
|
||||
<< mInvBindMatrix.size() << LL_ENDL;
|
||||
}
|
||||
|
||||
for (U32 j = 0; j < 4; j++)
|
||||
{
|
||||
for (U32 k = 0; k < 4; k++)
|
||||
{
|
||||
ret["inverse_bind_matrix"][i][j*4+k] = mInvBindMatrix[i].mMatrix[j][k];
|
||||
ret["inverse_bind_matrix"][i][j * 4 + k] = inv_bind.mMatrix[j][k];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1578,15 +1748,25 @@ LLSD LLMeshSkinInfo::asLLSD(bool include_joints, bool lock_scale_if_joint_positi
|
|||
}
|
||||
}
|
||||
|
||||
if ( include_joints && mAlternateBindMatrix.size() > 0 )
|
||||
// optional 'joint overrides'
|
||||
if (include_joints && mAlternateBindMatrix.size() > 0)
|
||||
{
|
||||
for (U32 i = 0; i < mJointNames.size(); ++i)
|
||||
{
|
||||
// If there is not enough to match mJointNames,
|
||||
// either supply no alternate matrixes at all or supply
|
||||
// replacements
|
||||
const LLMatrix4a& alt_bind = mAlternateBindMatrix.size() > i ? mAlternateBindMatrix[i] : LLMatrix4a::identity();
|
||||
if (i >= mAlternateBindMatrix.size())
|
||||
{
|
||||
LL_WARNS("MESHSKININFO") << "Joint index " << i << " (" << mJointNames[i] << ") exceeds alternate bind matrix size "
|
||||
<< mAlternateBindMatrix.size() << LL_ENDL;
|
||||
}
|
||||
for (U32 j = 0; j < 4; j++)
|
||||
{
|
||||
for (U32 k = 0; k < 4; k++)
|
||||
{
|
||||
ret["alt_inverse_bind_matrix"][i][j*4+k] = mAlternateBindMatrix[i].mMatrix[j][k];
|
||||
ret["alt_inverse_bind_matrix"][i][j * 4 + k] = alt_bind.mMatrix[j][k];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -160,6 +160,12 @@ public:
|
|||
bool loadSkinInfo(LLSD& header, std::istream& is);
|
||||
bool loadDecomposition(LLSD& header, std::istream& is);
|
||||
|
||||
enum EWriteModelMode
|
||||
{
|
||||
WRITE_NO = 0,
|
||||
WRITE_BINARY,
|
||||
WRITE_HUMAN,
|
||||
};
|
||||
static LLSD writeModel(
|
||||
std::ostream& ostr,
|
||||
LLModel* physics,
|
||||
|
|
@ -171,14 +177,14 @@ public:
|
|||
bool upload_skin,
|
||||
bool upload_joints,
|
||||
bool lock_scale_if_joint_position,
|
||||
bool nowrite = false,
|
||||
EWriteModelMode write_mode = WRITE_BINARY,
|
||||
bool as_slm = false,
|
||||
int submodel_id = 0);
|
||||
|
||||
static LLSD writeModelToStream(
|
||||
std::ostream& ostr,
|
||||
LLSD& mdl,
|
||||
bool nowrite = false, bool as_slm = false);
|
||||
EWriteModelMode write_mode = WRITE_BINARY, bool as_slm = false);
|
||||
|
||||
void ClearFacesAndMaterials() { mVolumeFaces.clear(); mMaterialList.clear(); }
|
||||
|
||||
|
|
@ -202,6 +208,7 @@ public:
|
|||
|
||||
void sortVolumeFacesByMaterialName();
|
||||
void normalizeVolumeFaces();
|
||||
void normalizeVolumeFacesAndWeights();
|
||||
void trimVolumeFacesToSize(U32 new_count = LL_SCULPT_MESH_MAX_FACES, LLVolume::face_list_t* remainder = NULL);
|
||||
void remapVolumeFaces();
|
||||
void optimizeVolumeFaces();
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@
|
|||
|
||||
#include "llmatrix4a.h"
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/exception/diagnostic_information.hpp>
|
||||
|
||||
std::list<LLModelLoader*> LLModelLoader::sActiveLoaderList;
|
||||
|
||||
|
|
@ -113,7 +114,9 @@ LLModelLoader::LLModelLoader(
|
|||
JointTransformMap& jointTransformMap,
|
||||
JointNameSet& jointsFromNodes,
|
||||
JointMap& legalJointNamesMap,
|
||||
U32 maxJointsPerMesh)
|
||||
U32 maxJointsPerMesh,
|
||||
U32 modelLimit,
|
||||
U32 debugMode)
|
||||
: mJointList( jointTransformMap )
|
||||
, mJointsFromNode( jointsFromNodes )
|
||||
, LLThread("Model Loader")
|
||||
|
|
@ -121,7 +124,6 @@ LLModelLoader::LLModelLoader(
|
|||
, mLod(lod)
|
||||
, mTrySLM(false)
|
||||
, mFirstTransform(true)
|
||||
, mNumOfFetchingTextures(0)
|
||||
, mLoadCallback(load_cb)
|
||||
, mJointLookupFunc(joint_lookup_func)
|
||||
, mTextureLoadFunc(texture_load_func)
|
||||
|
|
@ -132,7 +134,10 @@ LLModelLoader::LLModelLoader(
|
|||
, mNoNormalize(false)
|
||||
, mNoOptimize(false)
|
||||
, mCacheOnlyHitIfRigged(false)
|
||||
, mTexturesNeedScaling(false)
|
||||
, mMaxJointsPerMesh(maxJointsPerMesh)
|
||||
, mGeneratedModelLimit(modelLimit)
|
||||
, mDebugMode(debugMode)
|
||||
, mJointMap(legalJointNamesMap)
|
||||
{
|
||||
assert_main_thread();
|
||||
|
|
@ -149,7 +154,44 @@ LLModelLoader::~LLModelLoader()
|
|||
void LLModelLoader::run()
|
||||
{
|
||||
mWarningsArray.clear();
|
||||
try
|
||||
{
|
||||
doLoadModel();
|
||||
}
|
||||
// Model loader isn't mission critical, so we just log all exceptions
|
||||
catch (const LLException& e)
|
||||
{
|
||||
LL_WARNS("THREAD") << "LLException in model loader: " << e.what() << "" << LL_ENDL;
|
||||
LLSD args;
|
||||
args["Message"] = "UnknownException";
|
||||
args["FILENAME"] = mFilename;
|
||||
args["EXCEPTION"] = e.what();
|
||||
mWarningsArray.append(args);
|
||||
setLoadState(ERROR_PARSING);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
LL_WARNS() << "Exception in LLModelLoader::run: " << e.what() << LL_ENDL;
|
||||
LLSD args;
|
||||
args["Message"] = "UnknownException";
|
||||
args["FILENAME"] = mFilename;
|
||||
args["EXCEPTION"] = e.what();
|
||||
mWarningsArray.append(args);
|
||||
setLoadState(ERROR_PARSING);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LOG_UNHANDLED_EXCEPTION("LLModelLoader");
|
||||
LLSD args;
|
||||
args["Message"] = "UnknownException";
|
||||
args["FILENAME"] = mFilename;
|
||||
args["EXCEPTION"] = boost::current_exception_diagnostic_information();
|
||||
mWarningsArray.append(args);
|
||||
setLoadState(ERROR_PARSING);
|
||||
}
|
||||
|
||||
// todo: we are inside of a thread, push this into main thread worker,
|
||||
// not into doOnIdleOneTime that laks tread safety
|
||||
doOnIdleOneTime(boost::bind(&LLModelLoader::loadModelCallback,this));
|
||||
}
|
||||
|
||||
|
|
@ -201,7 +243,9 @@ bool LLModelLoader::doLoadModel()
|
|||
}
|
||||
}
|
||||
|
||||
return OpenFile(mFilename);
|
||||
bool res = OpenFile(mFilename);
|
||||
dumpDebugData(); // conditional on mDebugMode
|
||||
return res;
|
||||
}
|
||||
|
||||
void LLModelLoader::setLoadState(U32 state)
|
||||
|
|
@ -466,6 +510,148 @@ bool LLModelLoader::isRigSuitableForJointPositionUpload( const std::vector<std::
|
|||
return true;
|
||||
}
|
||||
|
||||
void LLModelLoader::dumpDebugData()
|
||||
{
|
||||
if (mDebugMode == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::string log_file = mFilename + "_importer.txt";
|
||||
LLStringUtil::toLower(log_file);
|
||||
llofstream file;
|
||||
file.open(log_file.c_str());
|
||||
if (!file)
|
||||
{
|
||||
LL_WARNS() << "dumpDebugData failed to open file " << log_file << LL_ENDL;
|
||||
return;
|
||||
}
|
||||
file << "Importing: " << mFilename << "\n";
|
||||
|
||||
std::map<std::string, LLMatrix4a> inv_bind;
|
||||
std::map<std::string, LLMatrix4a> alt_bind;
|
||||
for (LLPointer<LLModel>& mdl : mModelList)
|
||||
{
|
||||
|
||||
file << "Model name: " << mdl->mLabel << "\n";
|
||||
const LLMeshSkinInfo& skin_info = mdl->mSkinInfo;
|
||||
file << "Shape Bind matrix: " << skin_info.mBindShapeMatrix << "\n";
|
||||
file << "Skin Weights count: " << (S32)mdl->mSkinWeights.size() << "\n";
|
||||
|
||||
// some objects might have individual bind matrices,
|
||||
// but for now it isn't accounted for
|
||||
size_t joint_count = skin_info.mJointNames.size();
|
||||
for (size_t i = 0; i< joint_count;i++)
|
||||
{
|
||||
const std::string& joint = skin_info.mJointNames[i];
|
||||
if (skin_info.mInvBindMatrix.size() > i)
|
||||
{
|
||||
inv_bind[joint] = skin_info.mInvBindMatrix[i];
|
||||
}
|
||||
if (skin_info.mAlternateBindMatrix.size() > i)
|
||||
{
|
||||
alt_bind[joint] = skin_info.mAlternateBindMatrix[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
file << "\nInv Bind matrices.\n";
|
||||
for (auto& bind : inv_bind)
|
||||
{
|
||||
file << "Joint: " << bind.first << " Matrix: " << bind.second << "\n";
|
||||
}
|
||||
|
||||
file << "\nAlt Bind matrices.\n";
|
||||
for (auto& bind : alt_bind)
|
||||
{
|
||||
file << "Joint: " << bind.first << " Matrix: " << bind.second << "\n";
|
||||
}
|
||||
|
||||
if (mDebugMode == 2)
|
||||
{
|
||||
S32 model_count = 0;
|
||||
for (LLPointer<LLModel>& mdl : mModelList)
|
||||
{
|
||||
const LLVolume::face_list_t &face_list = mdl->getVolumeFaces();
|
||||
for (S32 face = 0; face < face_list.size(); face++)
|
||||
{
|
||||
const LLVolumeFace& vf = face_list[face];
|
||||
file << "\nModel: " << mdl->mLabel
|
||||
<< " face " << face
|
||||
<< " has " << vf.mNumVertices
|
||||
<< " vertices and " << vf.mNumIndices
|
||||
<< " indices " << "\n";
|
||||
|
||||
file << "\nPositions for model: " << mdl->mLabel << " face " << face << "\n";
|
||||
|
||||
for (S32 pos = 0; pos < vf.mNumVertices; ++pos)
|
||||
{
|
||||
file << vf.mPositions[pos] << " ";
|
||||
}
|
||||
|
||||
file << "\n\nIndices for model: " << mdl->mLabel << " face " << face << "\n";
|
||||
|
||||
for (S32 ind = 0; ind < vf.mNumIndices; ++ind)
|
||||
{
|
||||
file << vf.mIndices[ind] << " ";
|
||||
}
|
||||
}
|
||||
|
||||
file << "\n\nWeights for model: " << mdl->mLabel;
|
||||
for (auto& weights : mdl->mSkinWeights)
|
||||
{
|
||||
file << "\nVertex: " << weights.first << " Weights: ";
|
||||
for (auto& weight : weights.second)
|
||||
{
|
||||
file << weight.mJointIdx << ":" << weight.mWeight << " ";
|
||||
}
|
||||
}
|
||||
|
||||
file << "\n";
|
||||
model_count++;
|
||||
if (model_count == 5)
|
||||
{
|
||||
file << "Too many models, stopping at 5.\n";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (mDebugMode > 2)
|
||||
{
|
||||
file << "\nModel LLSDs\n";
|
||||
S32 model_count = 0;
|
||||
// some files contain too many models, so stop at 5.
|
||||
for (LLPointer<LLModel>& mdl : mModelList)
|
||||
{
|
||||
const LLMeshSkinInfo& skin_info = mdl->mSkinInfo;
|
||||
size_t joint_count = skin_info.mJointNames.size();
|
||||
size_t alt_count = skin_info.mAlternateBindMatrix.size();
|
||||
|
||||
LLModel::writeModel(
|
||||
file,
|
||||
nullptr,
|
||||
mdl,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
mdl->mPhysics,
|
||||
joint_count > 0,
|
||||
alt_count > 0,
|
||||
false,
|
||||
LLModel::WRITE_HUMAN,
|
||||
false,
|
||||
mdl->mSubmodelID);
|
||||
|
||||
file << "\n";
|
||||
model_count++;
|
||||
if (model_count == 5)
|
||||
{
|
||||
file << "Too many models, stopping at 5.\n";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//called in the main thread
|
||||
void LLModelLoader::loadTextures()
|
||||
|
|
@ -484,7 +670,7 @@ void LLModelLoader::loadTextures()
|
|||
|
||||
if(!material.mDiffuseMapFilename.empty())
|
||||
{
|
||||
mNumOfFetchingTextures += mTextureLoadFunc(material, mOpaqueData);
|
||||
mTextureLoadFunc(material, mOpaqueData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -109,8 +109,10 @@ public:
|
|||
|
||||
bool mTrySLM;
|
||||
bool mCacheOnlyHitIfRigged; // ignore cached SLM if it does not contain rig info (and we want rig info)
|
||||
bool mTexturesNeedScaling;
|
||||
|
||||
model_list mModelList;
|
||||
// The scene is pretty much what ends up getting loaded for upload. Basically assign things to this guy if you want something uploaded.
|
||||
scene mScene;
|
||||
|
||||
typedef std::queue<LLPointer<LLModel> > model_queue;
|
||||
|
|
@ -119,10 +121,16 @@ public:
|
|||
model_queue mPhysicsQ;
|
||||
|
||||
//map of avatar joints as named in COLLADA assets to internal joint names
|
||||
// Do not use this for anything other than looking up the name of a joint. This is populated elsewhere.
|
||||
JointMap mJointMap;
|
||||
|
||||
// The joint list is what you want to use to actually setup the specific joint transformations.
|
||||
JointTransformMap& mJointList;
|
||||
JointNameSet& mJointsFromNode;
|
||||
|
||||
|
||||
U32 mMaxJointsPerMesh;
|
||||
U32 mDebugMode; // see dumDebugData() for details
|
||||
|
||||
LLModelLoader(
|
||||
std::string filename,
|
||||
|
|
@ -135,7 +143,9 @@ public:
|
|||
JointTransformMap& jointTransformMap,
|
||||
JointNameSet& jointsFromNodes,
|
||||
JointMap& legalJointNamesMap,
|
||||
U32 maxJointsPerMesh);
|
||||
U32 maxJointsPerMesh,
|
||||
U32 modelLimit,
|
||||
U32 debugMode);
|
||||
virtual ~LLModelLoader();
|
||||
|
||||
virtual void setNoNormalize() { mNoNormalize = true; }
|
||||
|
|
@ -161,9 +171,6 @@ public:
|
|||
|
||||
void stretch_extents(const LLModel* model, const LLMatrix4& mat);
|
||||
|
||||
S32 mNumOfFetchingTextures ; // updated in the main thread
|
||||
bool areTexturesReady() { return !mNumOfFetchingTextures; } // called in the main thread.
|
||||
|
||||
bool verifyCount( int expected, int result );
|
||||
|
||||
//Determines the viability of an asset to be used as an avatar rig (w or w/o joint upload caps)
|
||||
|
|
@ -192,6 +199,7 @@ public:
|
|||
|
||||
const LLSD logOut() const { return mWarningsArray; }
|
||||
void clearLog() { mWarningsArray.clear(); }
|
||||
void dumpDebugData();
|
||||
|
||||
protected:
|
||||
|
||||
|
|
@ -203,6 +211,7 @@ protected:
|
|||
|
||||
bool mRigValidJointUpload;
|
||||
U32 mLegacyRigFlags;
|
||||
U32 mGeneratedModelLimit; // Attempt to limit amount of generated submodels
|
||||
|
||||
bool mNoNormalize;
|
||||
bool mNoOptimize;
|
||||
|
|
|
|||
|
|
@ -1870,8 +1870,17 @@ bool LLImageGL::readBackRaw(S32 discard_level, LLImageRaw* imageraw, bool compre
|
|||
glGetTexLevelParameteriv(mTarget, gl_discard, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, (GLint*)&glbytes);
|
||||
if(!imageraw->allocateDataSize(width, height, ncomponents, glbytes))
|
||||
{
|
||||
LL_WARNS() << "Memory allocation failed for reading back texture. Size is: " << glbytes << LL_ENDL ;
|
||||
LL_WARNS() << "width: " << width << "height: " << height << "components: " << ncomponents << LL_ENDL ;
|
||||
constexpr S64 MAX_GL_BYTES = 2048 * 2048;
|
||||
if (glbytes > 0 && glbytes <= MAX_GL_BYTES)
|
||||
{
|
||||
LLError::LLUserWarningMsg::showOutOfMemory();
|
||||
LL_ERRS() << "Memory allocation failed for reading back texture. Data size: " << glbytes << LL_ENDL;
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS() << "Memory allocation failed for reading back texture. Data size is: " << glbytes << LL_ENDL;
|
||||
LL_WARNS() << "width: " << width << "height: " << height << "components: " << ncomponents << LL_ENDL;
|
||||
}
|
||||
return false ;
|
||||
}
|
||||
|
||||
|
|
@ -1882,8 +1891,18 @@ bool LLImageGL::readBackRaw(S32 discard_level, LLImageRaw* imageraw, bool compre
|
|||
{
|
||||
if(!imageraw->allocateDataSize(width, height, ncomponents))
|
||||
{
|
||||
LL_WARNS() << "Memory allocation failed for reading back texture." << LL_ENDL ;
|
||||
LL_WARNS() << "width: " << width << "height: " << height << "components: " << ncomponents << LL_ENDL ;
|
||||
constexpr F32 MAX_IMAGE_SIZE = 2048 * 2048;
|
||||
F32 size = (F32)width * (F32)height * (F32)ncomponents;
|
||||
if (size > 0 && size <= MAX_IMAGE_SIZE)
|
||||
{
|
||||
LLError::LLUserWarningMsg::showOutOfMemory();
|
||||
LL_ERRS() << "Memory allocation failed for reading back texture. Data size: " << size << LL_ENDL;
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS() << "Memory allocation failed for reading back texture." << LL_ENDL;
|
||||
LL_WARNS() << "width: " << width << "height: " << height << "components: " << ncomponents << LL_ENDL;
|
||||
}
|
||||
return false ;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,9 @@ include(CMakeCopyIfDifferent)
|
|||
include(CubemapToEquirectangularJS)
|
||||
include(DBusGlib)
|
||||
include(DragDrop)
|
||||
if (USE_DISCORD)
|
||||
include(Discord)
|
||||
endif ()
|
||||
include(EXPAT)
|
||||
include(Hunspell)
|
||||
include(JPEGEncoderBasic)
|
||||
|
|
@ -76,6 +79,7 @@ set(viewer_SOURCE_FILES
|
|||
gltf/accessor.cpp
|
||||
gltf/primitive.cpp
|
||||
gltf/animation.cpp
|
||||
gltf/llgltfloader.cpp
|
||||
groupchatlistener.cpp
|
||||
llaccountingcostmanager.cpp
|
||||
llaisapi.cpp
|
||||
|
|
@ -178,11 +182,11 @@ set(viewer_SOURCE_FILES
|
|||
llflexibleobject.cpp
|
||||
llfloater360capture.cpp
|
||||
llfloaterabout.cpp
|
||||
llfloateravatarwelcomepack.cpp
|
||||
llfloaterbvhpreview.cpp
|
||||
llfloateraddpaymentmethod.cpp
|
||||
llfloaterauction.cpp
|
||||
llfloaterautoreplacesettings.cpp
|
||||
llfloateravatar.cpp
|
||||
llfloateravatarpicker.cpp
|
||||
llfloateravatarrendersettings.cpp
|
||||
llfloateravatartextures.cpp
|
||||
|
|
@ -746,6 +750,7 @@ set(viewer_HEADER_FILES
|
|||
gltf/buffer_util.h
|
||||
gltf/primitive.h
|
||||
gltf/animation.h
|
||||
gltf/llgltfloader.h
|
||||
llaccountingcost.h
|
||||
llaccountingcostmanager.h
|
||||
llaisapi.h
|
||||
|
|
@ -849,11 +854,11 @@ set(viewer_HEADER_FILES
|
|||
llflexibleobject.h
|
||||
llfloater360capture.h
|
||||
llfloaterabout.h
|
||||
llfloateravatarwelcomepack.h
|
||||
llfloaterbvhpreview.h
|
||||
llfloateraddpaymentmethod.h
|
||||
llfloaterauction.h
|
||||
llfloaterautoreplacesettings.h
|
||||
llfloateravatar.h
|
||||
llfloateravatarpicker.h
|
||||
llfloateravatarrendersettings.h
|
||||
llfloateravatartextures.h
|
||||
|
|
@ -1782,6 +1787,12 @@ if (WINDOWS)
|
|||
)
|
||||
endif (ADDRESS_SIZE EQUAL 64)
|
||||
|
||||
if (TARGET ll::discord_sdk)
|
||||
list(APPEND COPY_INPUT_DEPENDENCIES
|
||||
${SHARED_LIB_STAGING_DIR}/discord_partner_sdk.dll
|
||||
)
|
||||
endif ()
|
||||
|
||||
if (TARGET ll::openal)
|
||||
list(APPEND COPY_INPUT_DEPENDENCIES
|
||||
${SHARED_LIB_STAGING_DIR}/OpenAL32.dll
|
||||
|
|
@ -1798,6 +1809,7 @@ if (WINDOWS)
|
|||
--arch=${ARCH}
|
||||
--artwork=${ARTWORK_DIR}
|
||||
"--bugsplat=${BUGSPLAT_DB}"
|
||||
"--discord=${USE_DISCORD}"
|
||||
"--openal=${USE_OPENAL}"
|
||||
"--tracy=${USE_TRACY}"
|
||||
--build=${CMAKE_CURRENT_BINARY_DIR}
|
||||
|
|
@ -1836,6 +1848,7 @@ if (WINDOWS)
|
|||
--arch=${ARCH}
|
||||
--artwork=${ARTWORK_DIR}
|
||||
"--bugsplat=${BUGSPLAT_DB}"
|
||||
"--discord=${USE_DISCORD}"
|
||||
"--openal=${USE_OPENAL}"
|
||||
"--tracy=${USE_TRACY}"
|
||||
--build=${CMAKE_CURRENT_BINARY_DIR}
|
||||
|
|
@ -1900,6 +1913,7 @@ if (WINDOWS)
|
|||
--arch=${ARCH}
|
||||
--artwork=${ARTWORK_DIR}
|
||||
"--bugsplat=${BUGSPLAT_DB}"
|
||||
"--discord=${USE_DISCORD}"
|
||||
"--openal=${USE_OPENAL}"
|
||||
"--tracy=${USE_TRACY}"
|
||||
--build=${CMAKE_CURRENT_BINARY_DIR}
|
||||
|
|
@ -1995,6 +2009,10 @@ target_link_libraries(${VIEWER_BINARY_NAME}
|
|||
ll::openxr
|
||||
)
|
||||
|
||||
if (USE_DISCORD)
|
||||
target_link_libraries(${VIEWER_BINARY_NAME} ll::discord_sdk )
|
||||
endif ()
|
||||
|
||||
if( TARGET ll::intel_memops )
|
||||
target_link_libraries(${VIEWER_BINARY_NAME} ll::intel_memops )
|
||||
endif()
|
||||
|
|
@ -2051,6 +2069,7 @@ if (LINUX)
|
|||
--arch=${ARCH}
|
||||
--artwork=${ARTWORK_DIR}
|
||||
"--bugsplat=${BUGSPLAT_DB}"
|
||||
"--discord=${USE_DISCORD}"
|
||||
"--openal=${USE_OPENAL}"
|
||||
"--tracy=${USE_TRACY}"
|
||||
--build=${CMAKE_CURRENT_BINARY_DIR}
|
||||
|
|
@ -2079,6 +2098,7 @@ if (LINUX)
|
|||
--arch=${ARCH}
|
||||
--artwork=${ARTWORK_DIR}
|
||||
"--bugsplat=${BUGSPLAT_DB}"
|
||||
"--discord=${USE_DISCORD}"
|
||||
"--openal=${USE_OPENAL}"
|
||||
"--tracy=${USE_TRACY}"
|
||||
--build=${CMAKE_CURRENT_BINARY_DIR}
|
||||
|
|
@ -2155,6 +2175,7 @@ if (DARWIN)
|
|||
--arch=${ARCH}
|
||||
--artwork=${ARTWORK_DIR}
|
||||
"--bugsplat=${BUGSPLAT_DB}"
|
||||
"--discord=${USE_DISCORD}"
|
||||
"--openal=${USE_OPENAL}"
|
||||
"--tracy=${USE_TRACY}"
|
||||
--build=${CMAKE_CURRENT_BINARY_DIR}
|
||||
|
|
@ -2190,6 +2211,7 @@ if (DARWIN)
|
|||
--arch=${ARCH}
|
||||
--artwork=${ARTWORK_DIR}
|
||||
"--bugsplat=${BUGSPLAT_DB}"
|
||||
"--discord=${USE_DISCORD}"
|
||||
"--openal=${USE_OPENAL}"
|
||||
"--tracy=${USE_TRACY}"
|
||||
--build=${CMAKE_CURRENT_BINARY_DIR}
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
7.1.14
|
||||
7.2.0
|
||||
|
|
|
|||
|
|
@ -26,9 +26,9 @@
|
|||
label_ref="Command_Avatar_Label"
|
||||
tooltip_ref="Command_Avatar_Tooltip"
|
||||
execute_function="Floater.ToggleOrBringToFront"
|
||||
execute_parameters="avatar"
|
||||
execute_parameters="avatar_welcome_pack"
|
||||
is_running_function="Floater.IsOpen"
|
||||
is_running_parameters="avatar"
|
||||
is_running_parameters="avatar_welcome_pack"
|
||||
/>
|
||||
<command name="build"
|
||||
available_in_toybox="true"
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<llsd xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="llsd.xsd">
|
||||
<map>
|
||||
<key>ImporterDebug</key>
|
||||
<key>ImporterDebugVerboseLogging</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
<string>Enable debug output to more precisely identify sources of import errors. Warning: the output can slow down import on many machines.</string>
|
||||
|
|
@ -27,7 +27,7 @@
|
|||
<key>ImporterModelLimit</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
<string>Limits amount of importer generated models for dae files</string>
|
||||
<string>Limits amount of importer generated (when over 8 faces) models for dae and gltf files</string>
|
||||
<key>Persist</key>
|
||||
<integer>1</integer>
|
||||
<key>Type</key>
|
||||
|
|
@ -35,6 +35,17 @@
|
|||
<key>Value</key>
|
||||
<integer>768</integer>
|
||||
</map>
|
||||
<key>ImporterDebugMode</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
<string>At 0 does nothing, at 1 dumps skinning data near orifinal file, at 2 dumps skining data and positions/weights of first 5 models, at 3 dumps skinning data and models as llsd</string>
|
||||
<key>Persist</key>
|
||||
<integer>1</integer>
|
||||
<key>Type</key>
|
||||
<string>U32</string>
|
||||
<key>Value</key>
|
||||
<integer>0</integer>
|
||||
</map>
|
||||
<key>ImporterPreprocessDAE</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
|
|
@ -621,16 +632,16 @@
|
|||
<key>Value</key>
|
||||
<real>16.0</real>
|
||||
</map>
|
||||
<key>AvatarPickerURL</key>
|
||||
<key>AvatarWelcomePack</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
<string>Avatar picker contents</string>
|
||||
<string>Avatar Welcome Pack contents</string>
|
||||
<key>Persist</key>
|
||||
<integer>1</integer>
|
||||
<key>Type</key>
|
||||
<string>String</string>
|
||||
<key>Value</key>
|
||||
<string>http://lecs-viewer-web-components.s3.amazonaws.com/v3.0/[GRID_LOWERCASE]/avatars.html</string>
|
||||
<string>http://lecs-viewer-web-components.s3.amazonaws.com/v3.0/[GRID_LOWERCASE]/vawp/index.html</string>
|
||||
</map>
|
||||
<!--AvatarBakedTextureUploadTimeout is in use by QA-->
|
||||
<key>AvatarBakedTextureUploadTimeout</key>
|
||||
|
|
@ -1139,6 +1150,39 @@
|
|||
<key>Value</key>
|
||||
<integer>1</integer>
|
||||
</map>
|
||||
<key>EnableDiscord</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
<string>When set, connect to Discord to enable Rich Presence</string>
|
||||
<key>Persist</key>
|
||||
<integer>1</integer>
|
||||
<key>Type</key>
|
||||
<string>Boolean</string>
|
||||
<key>Value</key>
|
||||
<integer>0</integer>
|
||||
</map>
|
||||
<key>ShowDiscordActivityDetails</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
<string>When set, show avatar name on Discord Rich Presence</string>
|
||||
<key>Persist</key>
|
||||
<integer>1</integer>
|
||||
<key>Type</key>
|
||||
<string>Boolean</string>
|
||||
<key>Value</key>
|
||||
<integer>0</integer>
|
||||
</map>
|
||||
<key>ShowDiscordActivityState</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
<string>When set, show location on Discord Rich Presence</string>
|
||||
<key>Persist</key>
|
||||
<integer>1</integer>
|
||||
<key>Type</key>
|
||||
<string>Boolean</string>
|
||||
<key>Value</key>
|
||||
<integer>0</integer>
|
||||
</map>
|
||||
<key>EnableDiskCacheDebugInfo</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
|
|
@ -16237,13 +16281,13 @@
|
|||
<key>MediaFirstClickInteract</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
<string>This setting controls which media (once loaded) does not require a first click to focus before interaction can begin. This allows clicks to be passed directly to media bypassing the focus click requirement. This setting is a bitfield, precomputed values are as follows: Disabled=0; Worn HUDs only=1; Owned objects=3; Friend objects=7; Group objects=15; Landowner objects=31; Any object=31; All MOAP=1073741824. For complete details see lltoolpie.h enum MediaFirstClickTypes.</string>
|
||||
<string>This setting controls which media (once loaded) does not require a first click to focus before interaction can begin. This allows clicks to be passed directly to media bypassing the focus click requirement. This setting is a bitfield, precomputed values are as follows: Disabled=0; Worn HUDs only=1; Owned objects=2; Friend objects=4; Group objects=8; Landowner objects=16; Any object=32767; All MOAP=32768. For complete details see lltoolpie.h enum MediaFirstClickTypes.</string>
|
||||
<key>Persist</key>
|
||||
<integer>1</integer>
|
||||
<key>Type</key>
|
||||
<string>S32</string>
|
||||
<key>Value</key>
|
||||
<integer>1</integer>
|
||||
<integer>31</integer>
|
||||
</map>
|
||||
<key>EnableSelectionHints</key>
|
||||
<map>
|
||||
|
|
|
|||
|
|
@ -159,7 +159,7 @@ bool Buffer::prep(Asset& asset)
|
|||
std::string dir = gDirUtilp->getDirName(asset.mFilename);
|
||||
std::string bin_file = dir + gDirUtilp->getDirDelimiter() + mUri;
|
||||
|
||||
std::ifstream file(bin_file, std::ios::binary);
|
||||
llifstream file(bin_file.c_str(), std::ios::binary);
|
||||
if (!file.is_open())
|
||||
{
|
||||
LL_WARNS("GLTF") << "Failed to open file: " << bin_file << LL_ENDL;
|
||||
|
|
|
|||
|
|
@ -50,6 +50,10 @@ namespace LL
|
|||
"KHR_texture_transform"
|
||||
};
|
||||
|
||||
static std::unordered_set<std::string> ExtensionsIgnored = {
|
||||
"KHR_materials_pbrSpecularGlossiness"
|
||||
};
|
||||
|
||||
Material::AlphaMode gltf_alpha_mode_to_enum(const std::string& alpha_mode)
|
||||
{
|
||||
if (alpha_mode == "OPAQUE")
|
||||
|
|
@ -471,6 +475,8 @@ void Asset::update()
|
|||
LL_PROFILE_ZONE_NAMED_CATEGORY_GLTF("gltf - addTextureStats");
|
||||
|
||||
for (auto& image : mImages)
|
||||
{
|
||||
if (image.mLoadIntoTexturePipe)
|
||||
{
|
||||
if (image.mTexture.notNull())
|
||||
{ // HACK - force texture to be loaded full rez
|
||||
|
|
@ -481,23 +487,29 @@ void Asset::update()
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Asset::prep()
|
||||
{
|
||||
LL_PROFILE_ZONE_SCOPED_CATEGORY_GLTF;
|
||||
// check required extensions and fail if not supported
|
||||
bool unsupported = false;
|
||||
// check required extensions
|
||||
for (auto& extension : mExtensionsRequired)
|
||||
{
|
||||
if (ExtensionsSupported.find(extension) == ExtensionsSupported.end())
|
||||
{
|
||||
if (ExtensionsIgnored.find(extension) == ExtensionsIgnored.end())
|
||||
{
|
||||
LL_WARNS() << "Unsupported extension: " << extension << LL_ENDL;
|
||||
unsupported = true;
|
||||
mUnsupportedExtensions.push_back(extension);
|
||||
}
|
||||
else
|
||||
{
|
||||
mIgnoredExtensions.push_back(extension);
|
||||
}
|
||||
}
|
||||
|
||||
if (unsupported)
|
||||
}
|
||||
if (mUnsupportedExtensions.size() > 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
@ -513,7 +525,7 @@ bool Asset::prep()
|
|||
|
||||
for (auto& image : mImages)
|
||||
{
|
||||
if (!image.prep(*this))
|
||||
if (!image.prep(*this, mLoadIntoVRAM))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
@ -542,7 +554,8 @@ bool Asset::prep()
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (mLoadIntoVRAM)
|
||||
{
|
||||
// prepare vertex buffers
|
||||
|
||||
// material count is number of materials + 1 for default material
|
||||
|
|
@ -603,6 +616,7 @@ bool Asset::prep()
|
|||
if (vertex_count[variant] > 0)
|
||||
{
|
||||
U32 mat_idx = mat_id + 1;
|
||||
#if 0
|
||||
LLVertexBuffer* vb = new LLVertexBuffer(attribute_mask);
|
||||
|
||||
rd.mBatches[variant][mat_idx].mVertexBuffer = vb;
|
||||
|
|
@ -624,6 +638,7 @@ bool Asset::prep()
|
|||
vb->unmapBuffer();
|
||||
|
||||
vb->unbind();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -634,10 +649,11 @@ bool Asset::prep()
|
|||
{
|
||||
for (auto& primitive : mesh.mPrimitives)
|
||||
{
|
||||
llassert(primitive.mVertexBuffer.notNull());
|
||||
//llassert(primitive.mVertexBuffer.notNull());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
#if 0
|
||||
// build render batches
|
||||
for (S32 node_id = 0; node_id < mNodes.size(); ++node_id)
|
||||
{
|
||||
|
|
@ -664,6 +680,7 @@ bool Asset::prep()
|
|||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -672,13 +689,14 @@ Asset::Asset(const Value& src)
|
|||
*this = src;
|
||||
}
|
||||
|
||||
bool Asset::load(std::string_view filename)
|
||||
bool Asset::load(std::string_view filename, bool loadIntoVRAM)
|
||||
{
|
||||
LL_PROFILE_ZONE_SCOPED_CATEGORY_GLTF;
|
||||
mLoadIntoVRAM = loadIntoVRAM;
|
||||
mFilename = filename;
|
||||
std::string ext = gDirUtilp->getExtension(mFilename);
|
||||
|
||||
std::ifstream file(filename.data(), std::ios::binary);
|
||||
llifstream file(filename.data(), std::ios::binary);
|
||||
if (file.is_open())
|
||||
{
|
||||
std::string str((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
|
||||
|
|
@ -692,7 +710,7 @@ bool Asset::load(std::string_view filename)
|
|||
}
|
||||
else if (ext == "glb")
|
||||
{
|
||||
return loadBinary(str);
|
||||
return loadBinary(str, mLoadIntoVRAM);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -709,8 +727,9 @@ bool Asset::load(std::string_view filename)
|
|||
return false;
|
||||
}
|
||||
|
||||
bool Asset::loadBinary(const std::string& data)
|
||||
bool Asset::loadBinary(const std::string& data, bool loadIntoVRAM)
|
||||
{
|
||||
mLoadIntoVRAM = loadIntoVRAM;
|
||||
// load from binary gltf
|
||||
const U8* ptr = (const U8*)data.data();
|
||||
const U8* end = ptr + data.size();
|
||||
|
|
@ -935,8 +954,9 @@ void Asset::eraseBufferView(S32 bufferView)
|
|||
|
||||
LLViewerFetchedTexture* fetch_texture(const LLUUID& id);
|
||||
|
||||
bool Image::prep(Asset& asset)
|
||||
bool Image::prep(Asset& asset, bool loadIntoVRAM)
|
||||
{
|
||||
mLoadIntoTexturePipe = loadIntoVRAM;
|
||||
LLUUID id;
|
||||
if (mUri.size() == UUID_STR_SIZE && LLUUID::parseUUID(mUri, &id) && id.notNull())
|
||||
{ // loaded from an asset, fetch the texture from the asset system
|
||||
|
|
@ -951,12 +971,12 @@ bool Image::prep(Asset& asset)
|
|||
{ // embedded in a buffer, load the texture from the buffer
|
||||
BufferView& bufferView = asset.mBufferViews[mBufferView];
|
||||
Buffer& buffer = asset.mBuffers[bufferView.mBuffer];
|
||||
|
||||
if (mLoadIntoTexturePipe)
|
||||
{
|
||||
U8* data = buffer.mData.data() + bufferView.mByteOffset;
|
||||
|
||||
mTexture = LLViewerTextureManager::getFetchedTextureFromMemory(data, bufferView.mByteLength, mMimeType);
|
||||
|
||||
if (mTexture.isNull())
|
||||
}
|
||||
else if (mTexture.isNull() && mLoadIntoTexturePipe)
|
||||
{
|
||||
LL_WARNS("GLTF") << "Failed to load image from buffer:" << LL_ENDL;
|
||||
LL_WARNS("GLTF") << " image: " << mName << LL_ENDL;
|
||||
|
|
@ -971,12 +991,12 @@ bool Image::prep(Asset& asset)
|
|||
std::string img_file = dir + gDirUtilp->getDirDelimiter() + mUri;
|
||||
|
||||
LLUUID tracking_id = LLLocalBitmapMgr::getInstance()->addUnit(img_file);
|
||||
if (tracking_id.notNull())
|
||||
if (tracking_id.notNull() && mLoadIntoTexturePipe)
|
||||
{
|
||||
LLUUID world_id = LLLocalBitmapMgr::getInstance()->getWorldID(tracking_id);
|
||||
mTexture = LLViewerTextureManager::getFetchedTexture(world_id);
|
||||
}
|
||||
else
|
||||
else if (mLoadIntoTexturePipe)
|
||||
{
|
||||
LL_WARNS("GLTF") << "Failed to load image from file:" << LL_ENDL;
|
||||
LL_WARNS("GLTF") << " image: " << mName << LL_ENDL;
|
||||
|
|
@ -991,7 +1011,7 @@ bool Image::prep(Asset& asset)
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!asset.mFilename.empty())
|
||||
if (!asset.mFilename.empty() && mLoadIntoTexturePipe)
|
||||
{ // local preview, boost image so it doesn't discard and force to save raw image in case we save out or upload
|
||||
mTexture->setBoostLevel(LLViewerTexture::BOOST_PREVIEW);
|
||||
mTexture->forceToSaveRawImage(0, F32_MAX);
|
||||
|
|
|
|||
|
|
@ -286,6 +286,7 @@ namespace LL
|
|||
void serialize(boost::json::object& dst) const;
|
||||
};
|
||||
|
||||
// Image is for images that we want to load for the given asset. This acts as an interface into the viewer's texture pipe.
|
||||
class Image
|
||||
{
|
||||
public:
|
||||
|
|
@ -301,6 +302,8 @@ namespace LL
|
|||
S32 mBits = -1;
|
||||
S32 mPixelType = -1;
|
||||
|
||||
bool mLoadIntoTexturePipe = false;
|
||||
|
||||
LLPointer<LLViewerFetchedTexture> mTexture;
|
||||
|
||||
const Image& operator=(const Value& src);
|
||||
|
|
@ -316,7 +319,7 @@ namespace LL
|
|||
// preserve only uri and name
|
||||
void clearData(Asset& asset);
|
||||
|
||||
bool prep(Asset& asset);
|
||||
bool prep(Asset& asset, bool loadIntoVRAM);
|
||||
};
|
||||
|
||||
// Render Batch -- vertex buffer and list of primitives to render using
|
||||
|
|
@ -391,6 +394,10 @@ namespace LL
|
|||
|
||||
// UBO for storing material data
|
||||
U32 mMaterialsUBO = 0;
|
||||
bool mLoadIntoVRAM = false;
|
||||
|
||||
std::vector<std::string> mUnsupportedExtensions;
|
||||
std::vector<std::string> mIgnoredExtensions;
|
||||
|
||||
// prepare for first time use
|
||||
bool prep();
|
||||
|
|
@ -428,12 +435,12 @@ namespace LL
|
|||
// accepts .gltf and .glb files
|
||||
// Any existing data will be lost
|
||||
// returns result of prep() on success
|
||||
bool load(std::string_view filename);
|
||||
bool load(std::string_view filename, bool loadIntoVRAM);
|
||||
|
||||
// load .glb contents from memory
|
||||
// data - binary contents of .glb file
|
||||
// returns result of prep() on success
|
||||
bool loadBinary(const std::string& data);
|
||||
bool loadBinary(const std::string& data, bool loadIntoVRAM);
|
||||
|
||||
const Asset& operator=(const Value& src);
|
||||
void serialize(boost::json::object& dst) const;
|
||||
|
|
|
|||
|
|
@ -158,6 +158,12 @@ namespace LL
|
|||
dst.load3(src);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline void copyVec3<F32, LLColor4U>(F32* src, LLColor4U& dst)
|
||||
{
|
||||
dst.set((U8)(src[0] * 255.f), (U8)(src[1] * 255.f), (U8)(src[2] * 255.f), 255);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline void copyVec3<U16, LLColor4U>(U16* src, LLColor4U& dst)
|
||||
{
|
||||
|
|
@ -369,6 +375,11 @@ namespace LL
|
|||
template<class T>
|
||||
inline void copy(Asset& asset, Accessor& accessor, LLStrider<T>& dst)
|
||||
{
|
||||
if (accessor.mBufferView == INVALID_INDEX)
|
||||
{
|
||||
LL_WARNS("GLTF") << "Invalid buffer" << LL_ENDL;
|
||||
return;
|
||||
}
|
||||
const BufferView& bufferView = asset.mBufferViews[accessor.mBufferView];
|
||||
const Buffer& buffer = asset.mBuffers[bufferView.mBuffer];
|
||||
const U8* src = buffer.mData.data() + bufferView.mByteOffset + accessor.mByteOffset;
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,216 @@
|
|||
/**
|
||||
* @file LLGLTFLoader.h
|
||||
* @brief LLGLTFLoader class definition
|
||||
*
|
||||
* $LicenseInfo:firstyear=2022&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$
|
||||
*/
|
||||
|
||||
#ifndef LL_LLGLTFLoader_H
|
||||
#define LL_LLGLTFLoader_H
|
||||
|
||||
#include "tinygltf/tiny_gltf.h"
|
||||
|
||||
#include "asset.h"
|
||||
|
||||
#include "llglheaders.h"
|
||||
#include "lljointdata.h"
|
||||
#include "llmodelloader.h"
|
||||
|
||||
class LLGLTFLoader : public LLModelLoader
|
||||
{
|
||||
public:
|
||||
typedef std::map<std::string, LLImportMaterial> material_map;
|
||||
typedef std::map<std::string, std::string> joint_viewer_parent_map_t;
|
||||
typedef std::map<std::string, glm::mat4> joint_viewer_rest_map_t;
|
||||
typedef std::map<S32, glm::mat4> joint_node_mat4_map_t;
|
||||
|
||||
struct JointNodeData
|
||||
{
|
||||
JointNodeData()
|
||||
: mJointListIdx(-1)
|
||||
, mNodeIdx(-1)
|
||||
, mParentNodeIdx(-1)
|
||||
, mIsValidViewerJoint(false)
|
||||
, mIsParentValidViewerJoint(false)
|
||||
, mIsOverrideValid(false)
|
||||
{
|
||||
|
||||
}
|
||||
S32 mJointListIdx;
|
||||
S32 mNodeIdx;
|
||||
S32 mParentNodeIdx;
|
||||
glm::mat4 mGltfRestMatrix;
|
||||
glm::mat4 mViewerRestMatrix;
|
||||
glm::mat4 mOverrideRestMatrix;
|
||||
glm::mat4 mGltfMatrix;
|
||||
glm::mat4 mOverrideMatrix;
|
||||
std::string mName;
|
||||
bool mIsValidViewerJoint;
|
||||
bool mIsParentValidViewerJoint;
|
||||
bool mIsOverrideValid;
|
||||
};
|
||||
typedef std::map <S32, JointNodeData> joints_data_map_t;
|
||||
typedef std::map <std::string, S32> joints_name_to_node_map_t;
|
||||
|
||||
class LLGLTFImportMaterial : public LLImportMaterial
|
||||
{
|
||||
public:
|
||||
std::string name;
|
||||
LLGLTFImportMaterial() = default;
|
||||
LLGLTFImportMaterial(const LLImportMaterial& mat, const std::string& n) : LLImportMaterial(mat), name(n) {}
|
||||
};
|
||||
|
||||
LLGLTFLoader(std::string filename,
|
||||
S32 lod,
|
||||
LLModelLoader::load_callback_t load_cb,
|
||||
LLModelLoader::joint_lookup_func_t joint_lookup_func,
|
||||
LLModelLoader::texture_load_func_t texture_load_func,
|
||||
LLModelLoader::state_callback_t state_cb,
|
||||
void * opaque_userdata,
|
||||
JointTransformMap & jointTransformMap,
|
||||
JointNameSet & jointsFromNodes,
|
||||
std::map<std::string, std::string, std::less<>> & jointAliasMap,
|
||||
U32 maxJointsPerMesh,
|
||||
U32 modelLimit,
|
||||
U32 debugMode,
|
||||
std::vector<LLJointData> viewer_skeleton); //,
|
||||
//bool preprocess );
|
||||
virtual ~LLGLTFLoader();
|
||||
|
||||
virtual bool OpenFile(const std::string &filename);
|
||||
|
||||
struct GLTFVertex
|
||||
{
|
||||
glm::vec3 position;
|
||||
glm::vec3 normal;
|
||||
glm::vec2 uv0;
|
||||
glm::u16vec4 joints;
|
||||
glm::vec4 weights;
|
||||
};
|
||||
|
||||
protected:
|
||||
LL::GLTF::Asset mGLTFAsset;
|
||||
tinygltf::Model mGltfModel;
|
||||
bool mGltfLoaded = false;
|
||||
bool mApplyXYRotation = false;
|
||||
|
||||
// GLTF isn't aware of viewer's skeleton and uses it's own,
|
||||
// so need to take viewer's joints and use them to
|
||||
// recalculate iverse bind matrices
|
||||
std::vector<LLJointData> mViewerJointData;
|
||||
|
||||
// vector of vectors because of a posibility of having more than one skin
|
||||
typedef std::vector<LLMeshSkinInfo::matrix_list_t> bind_matrices_t;
|
||||
typedef std::vector<std::vector<std::string> > joint_names_t;
|
||||
bind_matrices_t mInverseBindMatrices;
|
||||
bind_matrices_t mAlternateBindMatrices;
|
||||
joint_names_t mJointNames; // empty string when no legal name for a given idx
|
||||
std::vector<std::vector<S32>> mJointUsage; // detect and warn about unsed joints
|
||||
|
||||
// what group a joint belongs to.
|
||||
// For purpose of stripping unused groups when joints are over limit.
|
||||
struct JointGroups
|
||||
{
|
||||
std::string mGroup;
|
||||
std::string mParentGroup;
|
||||
};
|
||||
typedef std::map<std::string, JointGroups, std::less<> > joint_to_group_map_t;
|
||||
joint_to_group_map_t mJointGroups;
|
||||
|
||||
// per skin joint count, needs to be tracked for the sake of limits check.
|
||||
std::vector<S32> mValidJointsCount;
|
||||
|
||||
// Cached material information
|
||||
typedef std::map<S32, LLGLTFImportMaterial> MaterialCache;
|
||||
MaterialCache mMaterialCache;
|
||||
|
||||
private:
|
||||
bool parseMeshes();
|
||||
void computeCombinedNodeTransform(const LL::GLTF::Asset& asset, S32 node_index, glm::mat4& combined_transform) const;
|
||||
void processNodeHierarchy(S32 node_idx, std::map<std::string, S32>& mesh_name_counts, U32 submodel_limit, const LLVolumeParams& volume_params);
|
||||
bool addJointToModelSkin(LLMeshSkinInfo& skin_info, S32 gltf_skin_idx, size_t gltf_joint_idx);
|
||||
LLGLTFImportMaterial processMaterial(S32 material_index, S32 fallback_index);
|
||||
std::string processTexture(S32 texture_index, const std::string& texture_type, const std::string& material_name);
|
||||
bool validateTextureIndex(S32 texture_index, S32& source_index);
|
||||
std::string generateMaterialName(S32 material_index, S32 fallback_index = -1);
|
||||
bool populateModelFromMesh(LLModel* pModel, const std::string& base_name, const LL::GLTF::Mesh &mesh, const LL::GLTF::Node &node, material_map& mats);
|
||||
void populateJointsFromSkin(S32 skin_idx);
|
||||
void populateJointGroups();
|
||||
void addModelToScene(LLModel* pModel, const std::string& model_name, U32 submodel_limit, const LLMatrix4& transformation, const LLVolumeParams& volume_params, const material_map& mats);
|
||||
void buildJointGroup(LLJointData& viewer_data, const std::string& parent_group);
|
||||
void buildOverrideMatrix(LLJointData& data, joints_data_map_t &gltf_nodes, joints_name_to_node_map_t &names_to_nodes, glm::mat4& parent_rest, glm::mat4& support_rest) const;
|
||||
glm::mat4 buildGltfRestMatrix(S32 joint_node_index, const LL::GLTF::Skin& gltf_skin) const;
|
||||
glm::mat4 buildGltfRestMatrix(S32 joint_node_index, const joints_data_map_t& joint_data) const;
|
||||
glm::mat4 computeGltfToViewerSkeletonTransform(const joints_data_map_t& joints_data_map, S32 gltf_node_index, const std::string& joint_name) const;
|
||||
bool checkForXYrotation(const LL::GLTF::Skin& gltf_skin, S32 joint_idx, S32 bind_indx);
|
||||
void checkForXYrotation(const LL::GLTF::Skin& gltf_skin);
|
||||
void checkGlobalJointUsage();
|
||||
|
||||
std::string extractTextureToTempFile(S32 textureIndex, const std::string& texture_type);
|
||||
|
||||
void notifyUnsupportedExtension(bool unsupported);
|
||||
|
||||
static size_t getSuffixPosition(const std::string& label);
|
||||
static std::string getLodlessLabel(const LL::GLTF::Mesh& mesh);
|
||||
|
||||
// bool mPreprocessGLTF;
|
||||
|
||||
/* Below inherited from dae loader - unknown if/how useful here
|
||||
|
||||
void processElement(gltfElement *element, bool &badElement, GLTF *gltf);
|
||||
void processGltfModel(LLModel *model, GLTF *gltf, gltfElement *pRoot, gltfMesh *mesh, gltfSkin *skin);
|
||||
|
||||
material_map getMaterials(LLModel *model, gltfInstance_geometry *instance_geo, GLTF *gltf);
|
||||
LLImportMaterial profileToMaterial(gltfProfile_COMMON *material, GLTF *gltf);
|
||||
LLColor4 getGltfColor(gltfElement *element);
|
||||
|
||||
gltfElement *getChildFromElement(gltfElement *pElement, std::string const &name);
|
||||
|
||||
bool isNodeAJoint(gltfNode *pNode);
|
||||
void processJointNode(gltfNode *pNode, std::map<std::string, LLMatrix4> &jointTransforms);
|
||||
void extractTranslation(gltfTranslate *pTranslate, LLMatrix4 &transform);
|
||||
void extractTranslationViaElement(gltfElement *pTranslateElement, LLMatrix4 &transform);
|
||||
void extractTranslationViaSID(gltfElement *pElement, LLMatrix4 &transform);
|
||||
void buildJointToNodeMappingFromScene(gltfElement *pRoot);
|
||||
void processJointToNodeMapping(gltfNode *pNode);
|
||||
void processChildJoints(gltfNode *pParentNode);
|
||||
|
||||
bool verifyCount(int expected, int result);
|
||||
|
||||
// Verify that a controller matches vertex counts
|
||||
bool verifyController(gltfController *pController);
|
||||
|
||||
static bool addVolumeFacesFromGltfMesh(LLModel *model, gltfMesh *mesh, LLSD &log_msg);
|
||||
static bool createVolumeFacesFromGltfMesh(LLModel *model, gltfMesh *mesh);
|
||||
|
||||
static LLModel *loadModelFromGltfMesh(gltfMesh *mesh);
|
||||
|
||||
// Loads a mesh breaking it into one or more models as necessary
|
||||
// to get around volume face limitations while retaining >8 materials
|
||||
//
|
||||
bool loadModelsFromGltfMesh(gltfMesh *mesh, std::vector<LLModel *> &models_out, U32 submodel_limit);
|
||||
|
||||
static std::string preprocessGLTF(std::string filename);
|
||||
*/
|
||||
|
||||
};
|
||||
#endif // LL_LLGLTFLLOADER_H
|
||||
|
|
@ -317,7 +317,7 @@ void GLTFSceneManager::load(const std::string& filename)
|
|||
{
|
||||
std::shared_ptr<Asset> asset = std::make_shared<Asset>();
|
||||
|
||||
if (asset->load(filename))
|
||||
if (asset->load(filename, true))
|
||||
{
|
||||
gDebugProgram.bind(); // bind a shader to satisfy LLVertexBuffer assertions
|
||||
asset->updateTransforms();
|
||||
|
|
|
|||
|
|
@ -268,6 +268,16 @@ using namespace LL;
|
|||
#include "glib.h"
|
||||
#endif // (LL_LINUX) && LL_GTK
|
||||
|
||||
#ifdef LL_DISCORD
|
||||
#define DISCORDPP_IMPLEMENTATION
|
||||
#include <discordpp.h>
|
||||
static std::shared_ptr<discordpp::Client> gDiscordClient;
|
||||
static uint64_t gDiscordTimestampsStart;
|
||||
static std::string gDiscordActivityDetails;
|
||||
static int32_t gDiscordPartyCurrentSize;
|
||||
static int32_t gDiscordPartyMaxSize;
|
||||
#endif
|
||||
|
||||
static LLAppViewerListener sAppViewerListener(LLAppViewer::instance);
|
||||
|
||||
////// Windows-specific includes to the bottom - nasty defines in these pollute the preprocessor
|
||||
|
|
@ -1319,6 +1329,13 @@ bool LLAppViewer::frame()
|
|||
|
||||
bool LLAppViewer::doFrame()
|
||||
{
|
||||
#ifdef LL_DISCORD
|
||||
{
|
||||
LL_PROFILE_ZONE_NAMED("discord_callbacks");
|
||||
discordpp::RunCallbacks();
|
||||
}
|
||||
#endif
|
||||
|
||||
LL_RECORD_BLOCK_TIME(FTM_FRAME);
|
||||
{
|
||||
// and now adjust the visuals from previous frame.
|
||||
|
|
@ -2245,10 +2262,7 @@ void errorCallback(LLError::ELevel level, const std::string &error_string)
|
|||
// Callback for LLError::LLUserWarningMsg
|
||||
void errorHandler(const std::string& title_string, const std::string& message_string, S32 code)
|
||||
{
|
||||
if (!message_string.empty())
|
||||
{
|
||||
OSMessageBox(message_string, title_string.empty() ? LLTrans::getString("MBFatalError") : title_string, OSMB_OK);
|
||||
}
|
||||
// message is going to hang viewer, create marker first
|
||||
switch (code)
|
||||
{
|
||||
case LLError::LLUserWarningMsg::ERROR_OTHER:
|
||||
|
|
@ -2256,6 +2270,10 @@ void errorHandler(const std::string& title_string, const std::string& message_st
|
|||
break;
|
||||
case LLError::LLUserWarningMsg::ERROR_BAD_ALLOC:
|
||||
LLAppViewer::instance()->createErrorMarker(LAST_EXEC_BAD_ALLOC);
|
||||
// When system run out of memory and errorHandler gets called from a thread,
|
||||
// main thread might keep going while OSMessageBox freezes the caller.
|
||||
// Todo: handle it better, but for now disconnect to avoid making things worse
|
||||
gDisconnected = true;
|
||||
break;
|
||||
case LLError::LLUserWarningMsg::ERROR_MISSING_FILES:
|
||||
LLAppViewer::instance()->createErrorMarker(LAST_EXEC_MISSING_FILES);
|
||||
|
|
@ -2263,6 +2281,10 @@ void errorHandler(const std::string& title_string, const std::string& message_st
|
|||
default:
|
||||
break;
|
||||
}
|
||||
if (!message_string.empty())
|
||||
{
|
||||
OSMessageBox(message_string, title_string.empty() ? LLTrans::getString("MBFatalError") : title_string, OSMB_OK);
|
||||
}
|
||||
}
|
||||
|
||||
void LLAppViewer::initLoggingAndGetLastDuration()
|
||||
|
|
@ -5687,6 +5709,28 @@ void LLAppViewer::forceErrorThreadCrash()
|
|||
thread->start();
|
||||
}
|
||||
|
||||
void LLAppViewer::forceExceptionThreadCrash()
|
||||
{
|
||||
class LLCrashTestThread : public LLThread
|
||||
{
|
||||
public:
|
||||
|
||||
LLCrashTestThread() : LLThread("Crash logging test thread")
|
||||
{
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
const std::string exception_text = "This is a deliberate exception in a thread";
|
||||
throw std::runtime_error(exception_text);
|
||||
}
|
||||
};
|
||||
|
||||
LL_WARNS() << "This is a deliberate exception in a thread" << LL_ENDL;
|
||||
LLCrashTestThread* thread = new LLCrashTestThread();
|
||||
thread->start();
|
||||
}
|
||||
|
||||
void LLAppViewer::initMainloopTimeout(std::string_view state, F32 secs)
|
||||
{
|
||||
if (!mMainloopTimeout)
|
||||
|
|
@ -5862,3 +5906,180 @@ void LLAppViewer::metricsSend(bool enable_reporting)
|
|||
gViewerAssetStats->restart();
|
||||
}
|
||||
|
||||
#ifdef LL_DISCORD
|
||||
|
||||
void LLAppViewer::initDiscordSocial()
|
||||
{
|
||||
gDiscordPartyCurrentSize = 1;
|
||||
gDiscordPartyMaxSize = 0;
|
||||
gDiscordTimestampsStart = time(nullptr);
|
||||
gDiscordClient = std::make_shared<discordpp::Client>();
|
||||
gDiscordClient->SetStatusChangedCallback([](discordpp::Client::Status status, discordpp::Client::Error, int32_t) {
|
||||
if (status == discordpp::Client::Status::Ready)
|
||||
{
|
||||
updateDiscordActivity();
|
||||
}
|
||||
});
|
||||
if (gSavedSettings.getBOOL("EnableDiscord"))
|
||||
{
|
||||
auto credential = gSecAPIHandler->loadCredential("Discord");
|
||||
if (credential.notNull())
|
||||
{
|
||||
gDiscordClient->UpdateToken(discordpp::AuthorizationTokenType::Bearer, credential->getAuthenticator()["token"].asString(), [](discordpp::ClientResult result) {
|
||||
if (result.Successful())
|
||||
gDiscordClient->Connect();
|
||||
else
|
||||
LL_WARNS("Discord") << result.Error() << LL_ENDL;
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS("Discord") << "Integration was enabled, but no credentials. Disabling integration." << LL_ENDL;
|
||||
gSavedSettings.setBOOL("EnableDiscord", false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LLAppViewer::toggleDiscordIntegration(const LLSD& value)
|
||||
{
|
||||
static const uint64_t APPLICATION_ID = 1394782217405862001;
|
||||
if (value.asBoolean())
|
||||
{
|
||||
discordpp::AuthorizationArgs args{};
|
||||
args.SetClientId(APPLICATION_ID);
|
||||
args.SetScopes(discordpp::Client::GetDefaultPresenceScopes());
|
||||
auto codeVerifier = gDiscordClient->CreateAuthorizationCodeVerifier();
|
||||
args.SetCodeChallenge(codeVerifier.Challenge());
|
||||
gDiscordClient->Authorize(args, [codeVerifier](auto result, auto code, auto redirectUri) {
|
||||
if (result.Successful())
|
||||
{
|
||||
gDiscordClient->GetToken(APPLICATION_ID, code, codeVerifier.Verifier(), redirectUri, [](discordpp::ClientResult result, std::string accessToken, std::string, discordpp::AuthorizationTokenType, int32_t, std::string) {
|
||||
if (result.Successful())
|
||||
{
|
||||
gDiscordClient->UpdateToken(discordpp::AuthorizationTokenType::Bearer, accessToken, [accessToken](discordpp::ClientResult result) {
|
||||
if (result.Successful())
|
||||
{
|
||||
LLSD authenticator = LLSD::emptyMap();
|
||||
authenticator["token"] = accessToken;
|
||||
gSecAPIHandler->saveCredential(gSecAPIHandler->createCredential("Discord", LLSD::emptyMap(), authenticator), true);
|
||||
gDiscordClient->Connect();
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS("Discord") << result.Error() << LL_ENDL;
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS("Discord") << result.Error() << LL_ENDL;
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS("Discord") << result.Error() << LL_ENDL;
|
||||
gSavedSettings.setBOOL("EnableDiscord", false);
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
gDiscordClient->Disconnect();
|
||||
auto credential = gSecAPIHandler->loadCredential("Discord");
|
||||
if (credential.notNull())
|
||||
{
|
||||
gDiscordClient->RevokeToken(APPLICATION_ID, credential->getAuthenticator()["token"].asString(), [](discordpp::ClientResult result) {
|
||||
if (result.Successful())
|
||||
LL_INFOS("Discord") << "Access token successfully revoked." << LL_ENDL;
|
||||
else
|
||||
LL_WARNS("Discord") << "No access token to revoke." << LL_ENDL;
|
||||
});
|
||||
auto cred = new LLCredential("Discord");
|
||||
gSecAPIHandler->deleteCredential(cred);
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS("Discord") << "Credentials are already nonexistent." << LL_ENDL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LLAppViewer::updateDiscordActivity()
|
||||
{
|
||||
LL_PROFILE_ZONE_SCOPED;
|
||||
discordpp::Activity activity;
|
||||
activity.SetType(discordpp::ActivityTypes::Playing);
|
||||
discordpp::ActivityTimestamps timestamps;
|
||||
timestamps.SetStart(gDiscordTimestampsStart);
|
||||
activity.SetTimestamps(timestamps);
|
||||
|
||||
if (gAgent.getID() == LLUUID::null)
|
||||
{
|
||||
gDiscordClient->UpdateRichPresence(activity, [](discordpp::ClientResult) {});
|
||||
return;
|
||||
}
|
||||
|
||||
static LLCachedControl<bool> show_details(gSavedSettings, "ShowDiscordActivityDetails", false);
|
||||
if (show_details)
|
||||
{
|
||||
if (gDiscordActivityDetails.empty())
|
||||
{
|
||||
LLAvatarName av_name;
|
||||
LLAvatarNameCache::get(gAgent.getID(), &av_name);
|
||||
gDiscordActivityDetails = av_name.getUserName();
|
||||
auto displayName = av_name.getDisplayName();
|
||||
if (gDiscordActivityDetails != displayName)
|
||||
gDiscordActivityDetails = displayName + " (" + gDiscordActivityDetails + ")";
|
||||
}
|
||||
activity.SetDetails(gDiscordActivityDetails);
|
||||
}
|
||||
|
||||
static LLCachedControl<bool> show_state(gSavedSettings, "ShowDiscordActivityState", false);
|
||||
if (show_state)
|
||||
{
|
||||
auto agent_pos_region = gAgent.getPositionAgent();
|
||||
S32 pos_x = S32(agent_pos_region.mV[VX] + 0.5f);
|
||||
S32 pos_y = S32(agent_pos_region.mV[VY] + 0.5f);
|
||||
S32 pos_z = S32(agent_pos_region.mV[VZ] + 0.5f);
|
||||
F32 velocity_mag_sq = gAgent.getVelocity().magVecSquared();
|
||||
const F32 FLY_CUTOFF = 6.f;
|
||||
const F32 FLY_CUTOFF_SQ = FLY_CUTOFF * FLY_CUTOFF;
|
||||
const F32 WALK_CUTOFF = 1.5f;
|
||||
const F32 WALK_CUTOFF_SQ = WALK_CUTOFF * WALK_CUTOFF;
|
||||
if (velocity_mag_sq > FLY_CUTOFF_SQ)
|
||||
{
|
||||
pos_x -= pos_x % 4;
|
||||
pos_y -= pos_y % 4;
|
||||
}
|
||||
else if (velocity_mag_sq > WALK_CUTOFF_SQ)
|
||||
{
|
||||
pos_x -= pos_x % 2;
|
||||
pos_y -= pos_y % 2;
|
||||
}
|
||||
auto location = llformat("%s (%d, %d, %d)", gAgent.getRegion()->getName().c_str(), pos_x, pos_y, pos_z);
|
||||
activity.SetState(location);
|
||||
|
||||
discordpp::ActivityParty party;
|
||||
party.SetId(location);
|
||||
party.SetCurrentSize(gDiscordPartyCurrentSize);
|
||||
party.SetMaxSize(gDiscordPartyMaxSize);
|
||||
activity.SetParty(party);
|
||||
}
|
||||
|
||||
gDiscordClient->UpdateRichPresence(activity, [](discordpp::ClientResult) {});
|
||||
}
|
||||
|
||||
void LLAppViewer::updateDiscordPartyCurrentSize(int32_t size)
|
||||
{
|
||||
gDiscordPartyCurrentSize = size;
|
||||
updateDiscordActivity();
|
||||
}
|
||||
|
||||
void LLAppViewer::updateDiscordPartyMaxSize(int32_t size)
|
||||
{
|
||||
gDiscordPartyMaxSize = size;
|
||||
updateDiscordActivity();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -175,6 +175,7 @@ public:
|
|||
virtual void forceErrorCoroprocedureCrash();
|
||||
virtual void forceErrorWorkQueueCrash();
|
||||
virtual void forceErrorThreadCrash();
|
||||
virtual void forceExceptionThreadCrash();
|
||||
|
||||
// The list is found in app_settings/settings_files.xml
|
||||
// but since they are used explicitly in code,
|
||||
|
|
@ -250,6 +251,14 @@ public:
|
|||
// Note: mQuitRequested can be aborted by user.
|
||||
void outOfMemorySoftQuit();
|
||||
|
||||
#ifdef LL_DISCORD
|
||||
static void initDiscordSocial();
|
||||
static void toggleDiscordIntegration(const LLSD& value);
|
||||
static void updateDiscordActivity();
|
||||
static void updateDiscordPartyCurrentSize(int32_t size);
|
||||
static void updateDiscordPartyMaxSize(int32_t size);
|
||||
#endif
|
||||
|
||||
protected:
|
||||
virtual bool initWindow(); // Initialize the viewer's window.
|
||||
virtual void initLoggingAndGetLastDuration(); // Initialize log files, logging system
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ LLFilePicker LLFilePicker::sInstance;
|
|||
#define XML_FILTER L"XML files (*.xml)\0*.xml\0"
|
||||
#define SLOBJECT_FILTER L"Objects (*.slobject)\0*.slobject\0"
|
||||
#define RAW_FILTER L"RAW files (*.raw)\0*.raw\0"
|
||||
#define MODEL_FILTER L"Model files (*.dae)\0*.dae\0"
|
||||
#define MODEL_FILTER L"Model files (*.dae, *.gltf, *.glb)\0*.dae;*.gltf;*.glb\0"
|
||||
#define MATERIAL_FILTER L"GLTF Files (*.gltf; *.glb)\0*.gltf;*.glb\0"
|
||||
#define HDRI_FILTER L"HDRI Files (*.exr)\0*.exr\0"
|
||||
#define MATERIAL_TEXTURES_FILTER L"GLTF Import (*.gltf; *.glb; *.tga; *.bmp; *.jpg; *.jpeg; *.png)\0*.gltf;*.glb;*.tga;*.bmp;*.jpg;*.jpeg;*.png\0"
|
||||
|
|
@ -217,6 +217,8 @@ bool LLFilePicker::setupFilter(ELoadFilter filter)
|
|||
break;
|
||||
case FFLOAD_MODEL:
|
||||
mOFN.lpstrFilter = MODEL_FILTER \
|
||||
COLLADA_FILTER \
|
||||
MATERIAL_FILTER \
|
||||
L"\0";
|
||||
break;
|
||||
case FFLOAD_MATERIAL:
|
||||
|
|
@ -671,6 +673,8 @@ std::unique_ptr<std::vector<std::string>> LLFilePicker::navOpenFilterProc(ELoadF
|
|||
case FFLOAD_HDRI:
|
||||
allowedv->push_back("exr");
|
||||
case FFLOAD_MODEL:
|
||||
allowedv->push_back("gltf");
|
||||
allowedv->push_back("glb");
|
||||
case FFLOAD_COLLADA:
|
||||
allowedv->push_back("dae");
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* @file llfloateravatar.h
|
||||
* @author Leyla Farazha
|
||||
* @brief floater for the avatar changer
|
||||
* @file llfloateravatarwelcomepack.cpp
|
||||
* @author Callum Prentice (callum@lindenlab.com)
|
||||
* @brief Floater container for the Avatar Welcome Pack we app
|
||||
*
|
||||
* $LicenseInfo:firstyear=2011&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
|
|
@ -27,17 +27,16 @@
|
|||
|
||||
#include "llviewerprecompiledheaders.h"
|
||||
|
||||
#include "llfloateravatar.h"
|
||||
#include "llfloateravatarwelcomepack.h"
|
||||
#include "lluictrlfactory.h"
|
||||
#include "llmediactrl.h"
|
||||
|
||||
|
||||
LLFloaterAvatar::LLFloaterAvatar(const LLSD& key)
|
||||
LLFloaterAvatarWelcomePack::LLFloaterAvatarWelcomePack(const LLSD& key)
|
||||
: LLFloater(key)
|
||||
{
|
||||
}
|
||||
|
||||
LLFloaterAvatar::~LLFloaterAvatar()
|
||||
LLFloaterAvatarWelcomePack::~LLFloaterAvatarWelcomePack()
|
||||
{
|
||||
if (mAvatarPicker)
|
||||
{
|
||||
|
|
@ -47,15 +46,13 @@ LLFloaterAvatar::~LLFloaterAvatar()
|
|||
}
|
||||
}
|
||||
|
||||
bool LLFloaterAvatar::postBuild()
|
||||
bool LLFloaterAvatarWelcomePack::postBuild()
|
||||
{
|
||||
mAvatarPicker = findChild<LLMediaCtrl>("avatar_picker_contents");
|
||||
if (mAvatarPicker)
|
||||
{
|
||||
mAvatarPicker->clearCache();
|
||||
}
|
||||
enableResizeCtrls(true, true, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* @file llfloateravatar.h
|
||||
* @author Leyla Farazha
|
||||
* @brief floater for the avatar changer
|
||||
* @file llfloateravatarwelcomepack.h
|
||||
* @author Callum Prentice (callum@lindenlab.com)
|
||||
* @brief Floater container for the Avatar Welcome Pack we app
|
||||
*
|
||||
* $LicenseInfo:firstyear=2011&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
|
|
@ -25,22 +25,21 @@
|
|||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#ifndef LL_FLOATER_AVATAR_H
|
||||
#define LL_FLOATER_AVATAR_H
|
||||
#pragma once
|
||||
|
||||
#include "llfloater.h"
|
||||
|
||||
class LLMediaCtrl;
|
||||
|
||||
class LLFloaterAvatar:
|
||||
class LLFloaterAvatarWelcomePack:
|
||||
public LLFloater
|
||||
{
|
||||
friend class LLFloaterReg;
|
||||
private:
|
||||
LLFloaterAvatar(const LLSD& key);
|
||||
~LLFloaterAvatar();
|
||||
|
||||
private:
|
||||
LLFloaterAvatarWelcomePack(const LLSD& key);
|
||||
~LLFloaterAvatarWelcomePack();
|
||||
bool postBuild() override;
|
||||
|
||||
LLMediaCtrl* mAvatarPicker;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -64,6 +64,7 @@
|
|||
#include "llcallbacklist.h"
|
||||
#include "llviewertexteditor.h"
|
||||
#include "llviewernetwork.h"
|
||||
#include "llmaterialeditor.h"
|
||||
|
||||
|
||||
//static
|
||||
|
|
@ -164,7 +165,7 @@ bool LLFloaterModelPreview::postBuild()
|
|||
for (S32 lod = 0; lod <= LLModel::LOD_HIGH; ++lod)
|
||||
{
|
||||
LLComboBox* lod_source_combo = getChild<LLComboBox>("lod_source_" + lod_name[lod]);
|
||||
lod_source_combo->setCommitCallback(boost::bind(&LLFloaterModelPreview::onLoDSourceCommit, this, lod));
|
||||
lod_source_combo->setCommitCallback(boost::bind(&LLFloaterModelPreview::onLoDSourceCommit, this, lod, true));
|
||||
lod_source_combo->setCurrentByIndex(mLODMode[lod]);
|
||||
|
||||
getChild<LLButton>("lod_browse_" + lod_name[lod])->setCommitCallback(boost::bind(&LLFloaterModelPreview::onBrowseLOD, this, lod));
|
||||
|
|
@ -619,11 +620,9 @@ void LLFloaterModelPreview::onJointListSelection()
|
|||
LLPanel *panel = mTabContainer->getPanelByName("rigging_panel");
|
||||
LLScrollListCtrl *joints_list = panel->getChild<LLScrollListCtrl>("joints_list");
|
||||
LLScrollListCtrl *joints_pos = panel->getChild<LLScrollListCtrl>("pos_overrides_list");
|
||||
LLScrollListCtrl *joints_scale = panel->getChild<LLScrollListCtrl>("scale_overrides_list");
|
||||
LLTextBox *joint_pos_descr = panel->getChild<LLTextBox>("pos_overrides_descr");
|
||||
|
||||
joints_pos->deleteAllItems();
|
||||
joints_scale->deleteAllItems();
|
||||
|
||||
LLScrollListItem *selected = joints_list->getFirstSelected();
|
||||
if (selected)
|
||||
|
|
@ -757,7 +756,7 @@ void LLFloaterModelPreview::onLODParamCommit(S32 lod, bool enforce_tri_limit)
|
|||
mModelPreview->onLODMeshOptimizerParamCommit(lod, enforce_tri_limit, mode);
|
||||
break;
|
||||
default:
|
||||
LL_ERRS() << "Only supposed to be called to generate models, val: " << mode << LL_ENDL;
|
||||
LL_ERRS() << "Only supposed to be called to generate models" << LL_ENDL;
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -767,7 +766,7 @@ void LLFloaterModelPreview::onLODParamCommit(S32 lod, bool enforce_tri_limit)
|
|||
LLComboBox* lod_source_combo = getChild<LLComboBox>("lod_source_" + lod_name[i]);
|
||||
if (lod_source_combo->getCurrentIndex() == LLModelPreview::USE_LOD_ABOVE)
|
||||
{
|
||||
onLoDSourceCommit(i);
|
||||
onLoDSourceCommit(i, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -1341,26 +1340,26 @@ void LLFloaterModelPreview::addStringToLog(const std::string& message, const LLS
|
|||
{
|
||||
std::string str;
|
||||
switch (lod)
|
||||
{
|
||||
{
|
||||
case LLModel::LOD_IMPOSTOR: str = "LOD0 "; break;
|
||||
case LLModel::LOD_LOW: str = "LOD1 "; break;
|
||||
case LLModel::LOD_MEDIUM: str = "LOD2 "; break;
|
||||
case LLModel::LOD_PHYSICS: str = "PHYS "; break;
|
||||
case LLModel::LOD_HIGH: str = "LOD3 "; break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
LLStringUtil::format_map_t args_msg;
|
||||
LLSD::map_const_iterator iter = args.beginMap();
|
||||
LLSD::map_const_iterator end = args.endMap();
|
||||
for (; iter != end; ++iter)
|
||||
{
|
||||
{
|
||||
args_msg[iter->first] = iter->second.asString();
|
||||
}
|
||||
str += sInstance->getString(message, args_msg);
|
||||
sInstance->addStringToLogTab(str, flash);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
void LLFloaterModelPreview::addStringToLog(const std::string& str, bool flash)
|
||||
|
|
@ -1761,7 +1760,7 @@ void LLFloaterModelPreview::toggleCalculateButton(bool visible)
|
|||
}
|
||||
}
|
||||
|
||||
void LLFloaterModelPreview::onLoDSourceCommit(S32 lod)
|
||||
void LLFloaterModelPreview::onLoDSourceCommit(S32 lod, bool refresh_ui)
|
||||
{
|
||||
mModelPreview->updateLodControls(lod);
|
||||
|
||||
|
|
@ -1770,9 +1769,17 @@ void LLFloaterModelPreview::onLoDSourceCommit(S32 lod)
|
|||
if (index == LLModelPreview::MESH_OPTIMIZER_AUTO
|
||||
|| index == LLModelPreview::MESH_OPTIMIZER_SLOPPY
|
||||
|| index == LLModelPreview::MESH_OPTIMIZER_PRECISE)
|
||||
{ //rebuild LoD to update triangle counts
|
||||
{
|
||||
// rebuild LoD to update triangle counts
|
||||
onLODParamCommit(lod, true);
|
||||
}
|
||||
else if (refresh_ui && index == LLModelPreview::USE_LOD_ABOVE)
|
||||
{
|
||||
// Update mUploadData for updateStatusMessages
|
||||
mModelPreview->rebuildUploadData();
|
||||
// Update UI with new triangle values
|
||||
mModelPreview->updateStatusMessages();
|
||||
}
|
||||
}
|
||||
|
||||
void LLFloaterModelPreview::resetDisplayOptions()
|
||||
|
|
|
|||
|
|
@ -208,7 +208,7 @@ private:
|
|||
void onClickCalculateBtn();
|
||||
void onJointListSelection();
|
||||
|
||||
void onLoDSourceCommit(S32 lod);
|
||||
void onLoDSourceCommit(S32 lod, bool refresh_ui);
|
||||
|
||||
void modelUpdated(bool calculate_visible);
|
||||
|
||||
|
|
|
|||
|
|
@ -366,6 +366,11 @@ LLFloaterPreference::LLFloaterPreference(const LLSD& key)
|
|||
mCommitCallbackRegistrar.add("Pref.ClearLog", boost::bind(&LLConversationLog::onClearLog, &LLConversationLog::instance()));
|
||||
mCommitCallbackRegistrar.add("Pref.DeleteTranscripts", boost::bind(&LLFloaterPreference::onDeleteTranscripts, this));
|
||||
mCommitCallbackRegistrar.add("UpdateFilter", boost::bind(&LLFloaterPreference::onUpdateFilterTerm, this, false)); // <FS:ND/> Hook up for filtering
|
||||
#ifdef LL_DISCORD
|
||||
gSavedSettings.getControl("EnableDiscord")->getCommitSignal()->connect(boost::bind(&LLAppViewer::toggleDiscordIntegration, _2));
|
||||
gSavedSettings.getControl("ShowDiscordActivityDetails")->getCommitSignal()->connect(boost::bind(&LLAppViewer::updateDiscordActivity));
|
||||
gSavedSettings.getControl("ShowDiscordActivityState")->getCommitSignal()->connect(boost::bind(&LLAppViewer::updateDiscordActivity));
|
||||
#endif
|
||||
}
|
||||
|
||||
void LLFloaterPreference::processProperties( void* pData, EAvatarProcessorType type )
|
||||
|
|
@ -523,6 +528,10 @@ bool LLFloaterPreference::postBuild()
|
|||
getChild<LLComboBox>("language_combobox")->add("System default", LLSD("default"), ADD_TOP, true);
|
||||
}
|
||||
|
||||
#ifndef LL_DISCORD
|
||||
getChild<LLTabContainer>("privacy_tab_container")->childDisable("privacy_preferences_discord");
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -371,6 +371,7 @@ LLFloaterWorldMap::LLFloaterWorldMap(const LLSD& key)
|
|||
mWaitingForTracker(false),
|
||||
mIsClosing(false),
|
||||
mSetToUserPosition(true),
|
||||
mProcessingSearchUpdate(false),
|
||||
mTrackedLocation(0.0,0.0,0.0),
|
||||
mTrackedStatus(LLTracker::TRACKING_NOTHING),
|
||||
mParcelInfoObserver(nullptr),
|
||||
|
|
@ -384,7 +385,7 @@ LLFloaterWorldMap::LLFloaterWorldMap(const LLSD& key)
|
|||
mCommitCallbackRegistrar.add("WMap.Location", boost::bind(&LLFloaterWorldMap::onLocationCommit, this));
|
||||
mCommitCallbackRegistrar.add("WMap.AvatarCombo", boost::bind(&LLFloaterWorldMap::onAvatarComboCommit, this));
|
||||
mCommitCallbackRegistrar.add("WMap.Landmark", boost::bind(&LLFloaterWorldMap::onLandmarkComboCommit, this));
|
||||
mCommitCallbackRegistrar.add("WMap.SearchResult", boost::bind(&LLFloaterWorldMap::onCommitSearchResult, this));
|
||||
mCommitCallbackRegistrar.add("WMap.SearchResult", [this](LLUICtrl* ctrl, const LLSD& data) { LLFloaterWorldMap::onCommitSearchResult(false); });
|
||||
mCommitCallbackRegistrar.add("WMap.GoHome", boost::bind(&LLFloaterWorldMap::onGoHome, this));
|
||||
mCommitCallbackRegistrar.add("WMap.Teleport", boost::bind(&LLFloaterWorldMap::onClickTeleportBtn, this));
|
||||
mCommitCallbackRegistrar.add("WMap.ShowTarget", boost::bind(&LLFloaterWorldMap::onShowTargetBtn, this));
|
||||
|
|
@ -829,6 +830,7 @@ void LLFloaterWorldMap::trackGenericItem(const LLItemInfo &item)
|
|||
|
||||
void LLFloaterWorldMap::trackLocation(const LLVector3d& pos_global)
|
||||
{
|
||||
mProcessingSearchUpdate = false;
|
||||
LLSimInfo* sim_info = LLWorldMap::getInstance()->simInfoFromPosGlobal(pos_global);
|
||||
if (!sim_info)
|
||||
{
|
||||
|
|
@ -968,7 +970,10 @@ void LLFloaterWorldMap::updateLocation()
|
|||
}
|
||||
}
|
||||
|
||||
if (!mProcessingSearchUpdate)
|
||||
{
|
||||
mLocationEditor->setValue(sim_name);
|
||||
}
|
||||
|
||||
// refresh coordinate display to reflect where user clicked.
|
||||
LLVector3d coord_pos = LLTracker::getTrackedPositionGlobal();
|
||||
|
|
@ -1242,6 +1247,7 @@ void LLFloaterWorldMap::onGoHome()
|
|||
{
|
||||
gAgent.teleportHome();
|
||||
closeFloater();
|
||||
mProcessingSearchUpdate = false;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1411,6 +1417,7 @@ void LLFloaterWorldMap::onLocationCommit()
|
|||
{
|
||||
return;
|
||||
}
|
||||
mProcessingSearchUpdate = true;
|
||||
|
||||
LLStringUtil::toLower(str);
|
||||
mCompletingRegionName = str;
|
||||
|
|
@ -1432,6 +1439,7 @@ void LLFloaterWorldMap::onCoordinatesCommit()
|
|||
{
|
||||
return;
|
||||
}
|
||||
mProcessingSearchUpdate = false;
|
||||
|
||||
S32 x_coord = (S32)mTeleportCoordSpinX->getValue().asReal();
|
||||
S32 y_coord = (S32)mTeleportCoordSpinY->getValue().asReal();
|
||||
|
|
@ -1445,6 +1453,7 @@ void LLFloaterWorldMap::onCoordinatesCommit()
|
|||
void LLFloaterWorldMap::onClearBtn()
|
||||
{
|
||||
mTrackedStatus = LLTracker::TRACKING_NOTHING;
|
||||
mProcessingSearchUpdate = false;
|
||||
LLTracker::stopTracking(true);
|
||||
LLWorldMap::getInstance()->cancelTracking();
|
||||
mSLURL = LLSLURL(); // Clear the SLURL since it's invalid
|
||||
|
|
@ -1461,6 +1470,7 @@ void LLFloaterWorldMap::onShowAgentBtn()
|
|||
mMapView->setPanWithInterpTime(0, 0, false, 0.1f); // false == animate
|
||||
// Set flag so user's location will be displayed if not tracking anything else
|
||||
mSetToUserPosition = true;
|
||||
mProcessingSearchUpdate = false;
|
||||
}
|
||||
|
||||
void LLFloaterWorldMap::onClickTeleportBtn()
|
||||
|
|
@ -1616,6 +1626,12 @@ void LLFloaterWorldMap::teleport()
|
|||
gAgent.teleportViaLocation( pos_global );
|
||||
}
|
||||
}
|
||||
|
||||
if (mProcessingSearchUpdate)
|
||||
{
|
||||
mProcessingSearchUpdate = false;
|
||||
mTrackedSimName.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void LLFloaterWorldMap::flyToLandmark()
|
||||
|
|
@ -1741,18 +1757,20 @@ void LLFloaterWorldMap::updateSims(bool found_null_sim)
|
|||
{
|
||||
mSearchResults->selectByValue(match);
|
||||
mSearchResults->setFocus(true);
|
||||
onCommitSearchResult();
|
||||
onCommitSearchResult(false /*fully commit the only option*/);
|
||||
}
|
||||
// else let user decide
|
||||
else
|
||||
{
|
||||
mSearchResults->operateOnAll(LLCtrlListInterface::OP_DESELECT);
|
||||
mSearchResults->selectFirstItem();
|
||||
mSearchResults->setFocus(true);
|
||||
onCommitSearchResult(true /*don't update text field*/);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// if we found nothing, say "none"
|
||||
mProcessingSearchUpdate = false;
|
||||
mSearchResults->setCommentText(LLTrans::getString("worldmap_results_none_found"));
|
||||
mSearchResults->operateOnAll(LLCtrlListInterface::OP_DESELECT);
|
||||
}
|
||||
|
|
@ -1766,7 +1784,7 @@ void LLFloaterWorldMap::onTeleportFinished()
|
|||
}
|
||||
}
|
||||
|
||||
void LLFloaterWorldMap::onCommitSearchResult()
|
||||
void LLFloaterWorldMap::onCommitSearchResult(bool from_search)
|
||||
{
|
||||
std::string sim_name = mSearchResults->getSelectedValue().asString();
|
||||
if (sim_name.empty())
|
||||
|
|
@ -1797,8 +1815,14 @@ void LLFloaterWorldMap::onCommitSearchResult()
|
|||
pos_global.mdV[VY] += (F64)pos_local.mV[VY];
|
||||
pos_global.mdV[VZ] = (F64)pos_local.mV[VZ];
|
||||
|
||||
// Commiting search string automatically selects first item in the search list,
|
||||
// in such case onCommitSearchResult shouldn't modify search string
|
||||
if (!from_search)
|
||||
{
|
||||
mLocationEditor->setValue(sim_name);
|
||||
}
|
||||
trackLocation(pos_global);
|
||||
mProcessingSearchUpdate = from_search;
|
||||
mTrackCtrlsPanel->setDefaultBtn(mTeleportButton);
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -176,7 +176,7 @@ protected:
|
|||
void onLocationFocusChanged( LLFocusableElement* ctrl );
|
||||
void onLocationCommit();
|
||||
void onCoordinatesCommit();
|
||||
void onCommitSearchResult();
|
||||
void onCommitSearchResult(bool from_search);
|
||||
|
||||
void onTeleportFinished();
|
||||
|
||||
|
|
@ -213,6 +213,7 @@ private:
|
|||
|
||||
bool mIsClosing;
|
||||
bool mSetToUserPosition;
|
||||
bool mProcessingSearchUpdate; // Don't update search string from what user set it to
|
||||
|
||||
LLVector3d mTrackedLocation;
|
||||
LLTracker::ETrackingStatus mTrackedStatus;
|
||||
|
|
|
|||
|
|
@ -2682,6 +2682,7 @@ bool LLInventoryModel::loadSkeleton(
|
|||
LL_PROFILE_ZONE_SCOPED;
|
||||
LL_DEBUGS(LOG_INV) << "importing inventory skeleton for " << owner_id << LL_ENDL;
|
||||
|
||||
LLTimer timer;
|
||||
typedef std::set<LLPointer<LLViewerInventoryCategory>, InventoryIDPtrLess> cat_set_t;
|
||||
cat_set_t temp_cats;
|
||||
bool rv = true;
|
||||
|
|
@ -2966,7 +2967,8 @@ bool LLInventoryModel::loadSkeleton(
|
|||
}
|
||||
|
||||
LL_INFOS(LOG_INV) << "Successfully loaded " << cached_category_count
|
||||
<< " categories and " << cached_item_count << " items from cache."
|
||||
<< " categories and " << cached_item_count << " items from cache"
|
||||
<< " after " << timer.getElapsedTimeF32() << " seconds."
|
||||
<< LL_ENDL;
|
||||
|
||||
return rv;
|
||||
|
|
|
|||
|
|
@ -364,9 +364,28 @@ void LLInventoryPanel::initializeViewBuilding()
|
|||
if (mInventory->isInventoryUsable()
|
||||
&& LLStartUp::getStartupState() <= STATE_WEARABLES_WAIT)
|
||||
{
|
||||
LLTimer timer;
|
||||
// Usually this happens on login, so we have less time constraits, but too long and we can cause a disconnect
|
||||
const F64 max_time = 20.f;
|
||||
initializeViews(max_time);
|
||||
|
||||
if (mViewsInitialized == VIEWS_INITIALIZED)
|
||||
{
|
||||
LL_INFOS("Inventory")
|
||||
<< "Fully initialized inventory panel " << getName()
|
||||
<< " with " << (S32)mItemMap.size()
|
||||
<< " views in " << timer.getElapsedTimeF32() << " seconds."
|
||||
<< LL_ENDL;
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_INFOS("Inventory")
|
||||
<< "Partially initialized inventory panel " << getName()
|
||||
<< " with " << (S32)mItemMap.size()
|
||||
<< " views in " << timer.getElapsedTimeF32()
|
||||
<< " seconds. Pending known views: " << (S32)mBuildViewsQueue.size()
|
||||
<< LL_ENDL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -137,7 +137,8 @@ LLFloaterComboOptions* LLFloaterComboOptions::showUI(
|
|||
{
|
||||
combo_picker->mComboOptions->addSimpleElement(*iter);
|
||||
}
|
||||
combo_picker->mComboOptions->selectFirstItem();
|
||||
// select 'Bulk Upload All' option
|
||||
combo_picker->mComboOptions->selectNthItem((S32)options.size() - 1);
|
||||
|
||||
combo_picker->openFloater(LLSD(title));
|
||||
combo_picker->setFocus(true);
|
||||
|
|
@ -1332,15 +1333,6 @@ const std::string LLMaterialEditor::buildMaterialDescription()
|
|||
desc << mNormalName;
|
||||
}
|
||||
|
||||
// trim last char if it's a ',' in case there is no normal texture
|
||||
// present and the code above inserts one
|
||||
// (no need to check for string length - always has initial string)
|
||||
std::string::iterator iter = desc.str().end() - 1;
|
||||
if (*iter == ',')
|
||||
{
|
||||
desc.str().erase(iter);
|
||||
}
|
||||
|
||||
// sanitize the material description so that it's compatible with the inventory
|
||||
// note: split this up because clang doesn't like operating directly on the
|
||||
// str() - error: lvalue reference to type 'basic_string<...>' cannot bind to a
|
||||
|
|
@ -1348,6 +1340,15 @@ const std::string LLMaterialEditor::buildMaterialDescription()
|
|||
std::string inv_desc = desc.str();
|
||||
LLInventoryObject::correctInventoryName(inv_desc);
|
||||
|
||||
// trim last char if it's a ',' in case there is no normal texture
|
||||
// present and the code above inserts one
|
||||
// (no need to check for string length - always has initial string)
|
||||
std::string::iterator iter = inv_desc.end() - 1;
|
||||
if (*iter == ',')
|
||||
{
|
||||
inv_desc.erase(iter);
|
||||
}
|
||||
|
||||
return inv_desc;
|
||||
}
|
||||
|
||||
|
|
@ -2478,6 +2479,42 @@ void LLMaterialEditor::loadMaterial(const tinygltf::Model &model_in, const std::
|
|||
pack_textures(base_color_img, normal_img, mr_img, emissive_img, occlusion_img,
|
||||
mBaseColorJ2C, mNormalJ2C, mMetallicRoughnessJ2C, mEmissiveJ2C);
|
||||
|
||||
if (open_floater)
|
||||
{
|
||||
bool textures_scaled = false;
|
||||
if (mBaseColorFetched && mBaseColorJ2C
|
||||
&& (mBaseColorFetched->getWidth() != mBaseColorJ2C->getWidth()
|
||||
|| mBaseColorFetched->getHeight() != mBaseColorJ2C->getHeight()))
|
||||
{
|
||||
textures_scaled = true;
|
||||
}
|
||||
else if (mNormalFetched && mNormalJ2C
|
||||
&& (mNormalFetched->getWidth() != mNormalJ2C->getWidth()
|
||||
|| mNormalFetched->getHeight() != mNormalJ2C->getHeight()))
|
||||
{
|
||||
textures_scaled = true;
|
||||
}
|
||||
else if (mMetallicRoughnessFetched && mMetallicRoughnessJ2C
|
||||
&& (mMetallicRoughnessFetched->getWidth() != mMetallicRoughnessJ2C->getWidth()
|
||||
|| mMetallicRoughnessFetched->getHeight() != mMetallicRoughnessJ2C->getHeight()))
|
||||
{
|
||||
textures_scaled = true;
|
||||
}
|
||||
else if (mEmissiveFetched && mEmissiveJ2C
|
||||
&& (mEmissiveFetched->getWidth() != mEmissiveJ2C->getWidth()
|
||||
|| mEmissiveFetched->getHeight() != mEmissiveJ2C->getHeight()))
|
||||
{
|
||||
textures_scaled = true;
|
||||
}
|
||||
|
||||
if (textures_scaled)
|
||||
{
|
||||
LLSD args;
|
||||
args["MAX_SIZE"] = LLViewerTexture::MAX_IMAGE_SIZE_DEFAULT;
|
||||
LLNotificationsUtil::add("MaterialImagesWereScaled", args);
|
||||
}
|
||||
}
|
||||
|
||||
LLUUID base_color_id;
|
||||
if (mBaseColorFetched.notNull())
|
||||
{
|
||||
|
|
@ -2684,10 +2721,8 @@ const std::string LLMaterialEditor::getImageNameFromUri(std::string image_uri, c
|
|||
// so we can include everything
|
||||
if (stripped_uri.length() > 0)
|
||||
{
|
||||
// example "DamagedHelmet: base layer"
|
||||
// example "base layer"
|
||||
return STRINGIZE(
|
||||
mMaterialNameShort <<
|
||||
": " <<
|
||||
stripped_uri <<
|
||||
" (" <<
|
||||
texture_type <<
|
||||
|
|
@ -2696,28 +2731,17 @@ const std::string LLMaterialEditor::getImageNameFromUri(std::string image_uri, c
|
|||
}
|
||||
else
|
||||
// uri doesn't include the type (because the uri is empty)
|
||||
// so we must reorganize the string a bit to include the name
|
||||
// and an explicit name type
|
||||
// include an explicit name type
|
||||
{
|
||||
// example "DamagedHelmet: (Emissive)"
|
||||
return STRINGIZE(
|
||||
mMaterialNameShort <<
|
||||
" (" <<
|
||||
texture_type <<
|
||||
")"
|
||||
);
|
||||
// example "Emissive"
|
||||
return texture_type;
|
||||
}
|
||||
}
|
||||
else
|
||||
// uri includes the type so just use it directly with the
|
||||
// name of the material
|
||||
// uri includes the type so just use it directly
|
||||
{
|
||||
return STRINGIZE(
|
||||
// example: AlienBust: normal_layer
|
||||
mMaterialNameShort <<
|
||||
": " <<
|
||||
stripped_uri
|
||||
);
|
||||
// example: "normal_layer"
|
||||
return stripped_uri;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2401,6 +2401,11 @@ EMeshProcessingResult LLMeshRepoThread::lodReceived(const LLVolumeParams& mesh_p
|
|||
// might be good idea to turn mesh into pointer to avoid making a copy
|
||||
mesh.mVolume = NULL;
|
||||
}
|
||||
{
|
||||
// make sure skin info is not removed from list while we are decreasing reference count
|
||||
LLMutexLock lock(mSkinMapMutex);
|
||||
skin_info = nullptr;
|
||||
}
|
||||
return MESH_OK;
|
||||
}
|
||||
}
|
||||
|
|
@ -2700,10 +2705,14 @@ void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, bool include_textures)
|
|||
|
||||
S32 instance_num = 0;
|
||||
|
||||
for (instance_map::iterator iter = mInstance.begin(); iter != mInstance.end(); ++iter)
|
||||
// Handle models, ignore submodels for now.
|
||||
// Probably should pre-sort by mSubmodelID instead of running twice.
|
||||
// Note: mInstance should be sorted by model name for the sake of
|
||||
// deterministic order.
|
||||
for (auto& iter : mInstance)
|
||||
{
|
||||
LLMeshUploadData data;
|
||||
data.mBaseModel = iter->first;
|
||||
data.mBaseModel = iter.first;
|
||||
|
||||
if (data.mBaseModel->mSubmodelID)
|
||||
{
|
||||
|
|
@ -2712,7 +2721,7 @@ void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, bool include_textures)
|
|||
continue;
|
||||
}
|
||||
|
||||
LLModelInstance& first_instance = *(iter->second.begin());
|
||||
LLModelInstance& first_instance = *(iter.second.begin());
|
||||
for (S32 i = 0; i < 5; i++)
|
||||
{
|
||||
data.mModel[i] = first_instance.mLOD[i];
|
||||
|
|
@ -2746,7 +2755,7 @@ void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, bool include_textures)
|
|||
mUploadSkin,
|
||||
mUploadJoints,
|
||||
mLockScaleIfJointPosition,
|
||||
false,
|
||||
LLModel::WRITE_BINARY,
|
||||
false,
|
||||
data.mBaseModel->mSubmodelID);
|
||||
|
||||
|
|
@ -2759,8 +2768,8 @@ void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, bool include_textures)
|
|||
}
|
||||
|
||||
// For all instances that use this model
|
||||
for (instance_list::iterator instance_iter = iter->second.begin();
|
||||
instance_iter != iter->second.end();
|
||||
for (instance_list::iterator instance_iter = iter.second.begin();
|
||||
instance_iter != iter.second.end();
|
||||
++instance_iter)
|
||||
{
|
||||
|
||||
|
|
@ -2858,10 +2867,11 @@ void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, bool include_textures)
|
|||
}
|
||||
}
|
||||
|
||||
for (instance_map::iterator iter = mInstance.begin(); iter != mInstance.end(); ++iter)
|
||||
// Now handle the submodels.
|
||||
for (auto& iter : mInstance)
|
||||
{
|
||||
LLMeshUploadData data;
|
||||
data.mBaseModel = iter->first;
|
||||
data.mBaseModel = iter.first;
|
||||
|
||||
if (!data.mBaseModel->mSubmodelID)
|
||||
{
|
||||
|
|
@ -2870,7 +2880,7 @@ void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, bool include_textures)
|
|||
continue;
|
||||
}
|
||||
|
||||
LLModelInstance& first_instance = *(iter->second.begin());
|
||||
LLModelInstance& first_instance = *(iter.second.begin());
|
||||
for (S32 i = 0; i < 5; i++)
|
||||
{
|
||||
data.mModel[i] = first_instance.mLOD[i];
|
||||
|
|
@ -2904,7 +2914,7 @@ void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, bool include_textures)
|
|||
mUploadSkin,
|
||||
mUploadJoints,
|
||||
mLockScaleIfJointPosition,
|
||||
false,
|
||||
LLModel::WRITE_BINARY,
|
||||
false,
|
||||
data.mBaseModel->mSubmodelID);
|
||||
|
||||
|
|
@ -2917,8 +2927,8 @@ void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, bool include_textures)
|
|||
}
|
||||
|
||||
// For all instances that use this model
|
||||
for (instance_list::iterator instance_iter = iter->second.begin();
|
||||
instance_iter != iter->second.end();
|
||||
for (instance_list::iterator instance_iter = iter.second.begin();
|
||||
instance_iter != iter.second.end();
|
||||
++instance_iter)
|
||||
{
|
||||
|
||||
|
|
|
|||
|
|
@ -674,7 +674,22 @@ public:
|
|||
typedef std::vector<LLModelInstance> instance_list;
|
||||
instance_list mInstanceList;
|
||||
|
||||
typedef std::map<LLPointer<LLModel>, instance_list> instance_map;
|
||||
// Upload should happen in deterministic order, so sort instances by model name.
|
||||
struct LLUploadModelInstanceLess
|
||||
{
|
||||
inline bool operator()(const LLPointer<LLModel>& a, const LLPointer<LLModel>& b) const
|
||||
{
|
||||
if (a.isNull() || b.isNull())
|
||||
{
|
||||
llassert(false); // We are uploading these models, they shouldn't be null.
|
||||
return true;
|
||||
}
|
||||
// Note: probably can sort by mBaseModel->mSubmodelID here as well to avoid
|
||||
// running over the list twice in wholeModelToLLSD.
|
||||
return a->mLabel < b->mLabel;
|
||||
}
|
||||
};
|
||||
typedef std::map<LLPointer<LLModel>, instance_list, LLUploadModelInstanceLess> instance_map;
|
||||
instance_map mInstance;
|
||||
|
||||
LLMutex* mMutex;
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@
|
|||
|
||||
#include "llmodelloader.h"
|
||||
#include "lldaeloader.h"
|
||||
#include "llgltfloader.h"
|
||||
#include "gltf/llgltfloader.h"
|
||||
#include "llfloatermodelpreview.h"
|
||||
|
||||
#include "llagent.h"
|
||||
|
|
@ -40,6 +40,7 @@
|
|||
#include "lldrawable.h"
|
||||
#include "llface.h"
|
||||
#include "lliconctrl.h"
|
||||
#include "lljointdata.h"
|
||||
#include "llmatrix4a.h"
|
||||
#include "llmeshrepository.h"
|
||||
#include "llmeshoptimizer.h"
|
||||
|
|
@ -163,10 +164,14 @@ LLModelPreview::LLModelPreview(S32 width, S32 height, LLFloater* fmp)
|
|||
, mPhysicsSearchLOD(LLModel::LOD_PHYSICS)
|
||||
, mResetJoints(false)
|
||||
, mModelNoErrors(true)
|
||||
, mLoading(false)
|
||||
, mModelLoader(nullptr)
|
||||
, mLastJointUpdate(false)
|
||||
, mFirstSkinUpdate(true)
|
||||
, mHasDegenerate(false)
|
||||
, mImporterDebug(LLCachedControl<bool>(gSavedSettings, "ImporterDebug", false))
|
||||
, mNumOfFetchingTextures(0)
|
||||
, mTexturesNeedScaling(false)
|
||||
, mImporterDebug(LLCachedControl<bool>(gSavedSettings, "ImporterDebugVerboseLogging", false))
|
||||
{
|
||||
mNeedsUpdate = true;
|
||||
mCameraDistance = 0.f;
|
||||
|
|
@ -175,11 +180,9 @@ LLModelPreview::LLModelPreview(S32 width, S32 height, LLFloater* fmp)
|
|||
mCameraZoom = 1.f;
|
||||
mTextureName = 0;
|
||||
mPreviewLOD = 0;
|
||||
mModelLoader = NULL;
|
||||
mMaxTriangleLimit = 0;
|
||||
mDirty = false;
|
||||
mGenLOD = false;
|
||||
mLoading = false;
|
||||
mLookUpLodFiles = false;
|
||||
mLoadState = LLModelLoader::STARTING;
|
||||
mGroup = 0;
|
||||
|
|
@ -211,6 +214,7 @@ LLModelPreview::~LLModelPreview()
|
|||
{
|
||||
mModelLoader->shutdown();
|
||||
mModelLoader = NULL;
|
||||
mLoading = false;
|
||||
}
|
||||
|
||||
if (mPreviewAvatar)
|
||||
|
|
@ -557,10 +561,7 @@ void LLModelPreview::rebuildUploadData()
|
|||
texture->setLoadedCallback(LLModelPreview::textureLoadedCallback, 0, true, false, new LLHandle<LLModelPreview>(getHandle()), &mCallbackTextureList, false);
|
||||
texture->forceToSaveRawImage(0, F32_MAX);
|
||||
texture->updateFetch();
|
||||
if (mModelLoader)
|
||||
{
|
||||
mModelLoader->mNumOfFetchingTextures++;
|
||||
}
|
||||
mNumOfFetchingTextures++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -691,7 +692,7 @@ void LLModelPreview::saveUploadData(const std::string& filename,
|
|||
save_skinweights,
|
||||
save_joint_positions,
|
||||
lock_scale_if_joint_position,
|
||||
false, true, instance.mModel->mSubmodelID);
|
||||
LLModel::WRITE_BINARY, true, instance.mModel->mSubmodelID);
|
||||
|
||||
data["mesh"][instance.mModel->mLocalID] = str.str();
|
||||
}
|
||||
|
|
@ -753,6 +754,10 @@ void LLModelPreview::loadModel(std::string filename, S32 lod, bool force_disable
|
|||
LL_WARNS() << out.str() << LL_ENDL;
|
||||
LLFloaterModelPreview::addStringToLog(out, true);
|
||||
assert(lod >= LLModel::LOD_IMPOSTOR && lod < LLModel::NUM_LODS);
|
||||
if (mModelLoader == nullptr)
|
||||
{
|
||||
mLoading = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -806,10 +811,14 @@ void LLModelPreview::loadModel(std::string filename, S32 lod, bool force_disable
|
|||
joint_alias_map,
|
||||
LLSkinningUtil::getMaxJointCount(),
|
||||
gSavedSettings.getU32("ImporterModelLimit"),
|
||||
gSavedSettings.getU32("ImporterDebugMode"),
|
||||
gSavedSettings.getBOOL("ImporterPreprocessDAE"));
|
||||
}
|
||||
else
|
||||
{
|
||||
LLVOAvatar* av = getPreviewAvatar();
|
||||
std::vector<LLJointData> viewer_skeleton;
|
||||
av->getJointMatricesAndHierarhy(viewer_skeleton);
|
||||
mModelLoader = new LLGLTFLoader(
|
||||
filename,
|
||||
lod,
|
||||
|
|
@ -822,7 +831,9 @@ void LLModelPreview::loadModel(std::string filename, S32 lod, bool force_disable
|
|||
mJointsFromNode,
|
||||
joint_alias_map,
|
||||
LLSkinningUtil::getMaxJointCount(),
|
||||
gSavedSettings.getU32("ImporterModelLimit"));
|
||||
gSavedSettings.getU32("ImporterModelLimit"),
|
||||
gSavedSettings.getU32("ImporterDebugMode"),
|
||||
viewer_skeleton);
|
||||
}
|
||||
|
||||
if (force_disable_slm)
|
||||
|
|
@ -985,7 +996,9 @@ void LLModelPreview::loadModelCallback(S32 loaded_lod)
|
|||
setRigValidForJointPositionUpload(mModelLoader->isRigValidForJointPositionUpload());
|
||||
setLegacyRigFlags(mModelLoader->getLegacyRigFlags());
|
||||
|
||||
mTexturesNeedScaling |= mModelLoader->mTexturesNeedScaling;
|
||||
mModelLoader->loadTextures();
|
||||
warnTextureScaling();
|
||||
|
||||
if (loaded_lod == -1)
|
||||
{ //populate all LoDs from model loader scene
|
||||
|
|
@ -1807,7 +1820,7 @@ F32 LLModelPreview::genMeshOptimizerPerFace(LLModel *base_model, LLModel *target
|
|||
|
||||
void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 decimation, bool enforce_tri_limit)
|
||||
{
|
||||
LL_INFOS() << "Generating lod " << which_lod << " using meshoptimizer" << LL_ENDL;
|
||||
LL_DEBUGS("Upload") << "Generating lod " << which_lod << " using meshoptimizer" << LL_ENDL;
|
||||
// Allow LoD from -1 to LLModel::LOD_PHYSICS
|
||||
if (which_lod < -1 || which_lod > LLModel::NUM_LODS - 1)
|
||||
{
|
||||
|
|
@ -1884,6 +1897,12 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d
|
|||
|
||||
mMaxTriangleLimit = base_triangle_count;
|
||||
|
||||
// For logging purposes
|
||||
S32 meshes_processed = 0;
|
||||
S32 meshes_simplified = 0;
|
||||
S32 meshes_sloppy_simplified = 0;
|
||||
S32 meshes_fail_count = 0;
|
||||
|
||||
// Build models
|
||||
|
||||
S32 start = LLModel::LOD_HIGH;
|
||||
|
|
@ -1893,7 +1912,7 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d
|
|||
{
|
||||
start = which_lod;
|
||||
end = which_lod;
|
||||
}
|
||||
};
|
||||
|
||||
for (S32 lod = start; lod >= end; --lod)
|
||||
{
|
||||
|
|
@ -1956,6 +1975,11 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d
|
|||
const LLVolumeFace &face = base->getVolumeFace(face_idx);
|
||||
LLVolumeFace &new_face = target_model->getVolumeFace(face_idx);
|
||||
new_face = face;
|
||||
meshes_fail_count++;
|
||||
}
|
||||
else
|
||||
{
|
||||
meshes_simplified++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1968,7 +1992,18 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d
|
|||
if (genMeshOptimizerPerFace(base, target_model, face_idx, indices_decimator, lod_error_threshold, MESH_OPTIMIZER_NO_TOPOLOGY) < 0)
|
||||
{
|
||||
// Sloppy failed and returned an invalid model
|
||||
genMeshOptimizerPerFace(base, target_model, face_idx, indices_decimator, lod_error_threshold, MESH_OPTIMIZER_FULL);
|
||||
if (genMeshOptimizerPerFace(base, target_model, face_idx, indices_decimator, lod_error_threshold, MESH_OPTIMIZER_FULL) < 0)
|
||||
{
|
||||
meshes_fail_count++;
|
||||
}
|
||||
else
|
||||
{
|
||||
meshes_simplified++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
meshes_sloppy_simplified++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2068,25 +2103,28 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d
|
|||
precise_ratio = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, MESH_OPTIMIZER_FULL);
|
||||
}
|
||||
|
||||
LL_INFOS() << "Model " << target_model->getName()
|
||||
LL_DEBUGS("Upload") << "Model " << target_model->getName()
|
||||
<< " lod " << which_lod
|
||||
<< " resulting ratio " << precise_ratio
|
||||
<< " simplified using per model method." << LL_ENDL;
|
||||
meshes_simplified++;
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_INFOS() << "Model " << target_model->getName()
|
||||
LL_DEBUGS("Upload") << "Model " << target_model->getName()
|
||||
<< " lod " << which_lod
|
||||
<< " resulting ratio " << sloppy_ratio
|
||||
<< " sloppily simplified using per model method." << LL_ENDL;
|
||||
meshes_sloppy_simplified++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_INFOS() << "Model " << target_model->getName()
|
||||
LL_DEBUGS("Upload") << "Model " << target_model->getName()
|
||||
<< " lod " << which_lod
|
||||
<< " resulting ratio " << precise_ratio
|
||||
<< " simplified using per model method." << LL_ENDL;
|
||||
meshes_simplified++;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2100,6 +2138,8 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d
|
|||
//copy material list
|
||||
target_model->mMaterialList = base->mMaterialList;
|
||||
|
||||
meshes_processed++;
|
||||
|
||||
if (!validate_model(target_model))
|
||||
{
|
||||
LL_ERRS() << "Invalid model generated when creating LODs" << LL_ENDL;
|
||||
|
|
@ -2129,6 +2169,11 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
LL_INFOS("Upload") << "LOD " << which_lod << ", Mesh optimizer processed meshes : " << meshes_processed
|
||||
<<" simplified: " << meshes_simplified
|
||||
<< ", slopily simplified: " << meshes_sloppy_simplified
|
||||
<< ", failures: " << meshes_fail_count << LL_ENDL;
|
||||
}
|
||||
|
||||
void LLModelPreview::updateStatusMessages()
|
||||
|
|
@ -2466,7 +2511,7 @@ void LLModelPreview::updateStatusMessages()
|
|||
LLMutexLock lock(this);
|
||||
if (mModelLoader)
|
||||
{
|
||||
if (!mModelLoader->areTexturesReady() && mFMP->childGetValue("upload_textures").asBoolean())
|
||||
if (!areTexturesReady() && mFMP->childGetValue("upload_textures").asBoolean())
|
||||
{
|
||||
// Some textures are still loading, prevent upload until they are done
|
||||
mModelNoErrors = false;
|
||||
|
|
@ -3039,9 +3084,12 @@ void LLModelPreview::loadedCallback(
|
|||
S32 lod,
|
||||
void* opaque)
|
||||
{
|
||||
if(LLModelPreview::sIgnoreLoadedCallback)
|
||||
return;
|
||||
|
||||
LLModelPreview* pPreview = static_cast<LLModelPreview*>(opaque);
|
||||
LLMutexLock lock(pPreview);
|
||||
if (pPreview && pPreview->mModelLoader && !LLModelPreview::sIgnoreLoadedCallback)
|
||||
if (pPreview && pPreview->mModelLoader)
|
||||
{
|
||||
// Load loader's warnings into floater's log tab
|
||||
const LLSD out = pPreview->mModelLoader->logOut();
|
||||
|
|
@ -3090,27 +3138,50 @@ void LLModelPreview::lookupLODModelFiles(S32 lod)
|
|||
S32 next_lod = (lod - 1 >= LLModel::LOD_IMPOSTOR) ? lod - 1 : LLModel::LOD_PHYSICS;
|
||||
|
||||
std::string lod_filename = mLODFile[LLModel::LOD_HIGH];
|
||||
std::string ext = ".dae";
|
||||
std::string lod_filename_lower(lod_filename);
|
||||
LLStringUtil::toLower(lod_filename_lower);
|
||||
|
||||
// Check for each supported file extension
|
||||
std::vector<std::string> supported_exts = { ".dae", ".gltf", ".glb" };
|
||||
std::string found_ext;
|
||||
std::string::size_type ext_pos = std::string::npos;
|
||||
|
||||
for (const auto& ext : supported_exts)
|
||||
{
|
||||
std::string::size_type i = lod_filename_lower.rfind(ext);
|
||||
if (i != std::string::npos)
|
||||
{
|
||||
lod_filename.replace(i, lod_filename.size() - ext.size(), getLodSuffix(next_lod) + ext);
|
||||
ext_pos = i;
|
||||
found_ext = ext;
|
||||
break;
|
||||
}
|
||||
if (gDirUtilp->fileExists(lod_filename))
|
||||
}
|
||||
|
||||
if (ext_pos != std::string::npos)
|
||||
{
|
||||
// Replace extension with LOD suffix + original extension
|
||||
std::string lod_file_to_check = lod_filename;
|
||||
lod_file_to_check.replace(ext_pos, found_ext.size(), getLodSuffix(next_lod) + found_ext);
|
||||
|
||||
if (gDirUtilp->fileExists(lod_file_to_check))
|
||||
{
|
||||
LLFloaterModelPreview* fmp = LLFloaterModelPreview::sInstance;
|
||||
if (fmp)
|
||||
{
|
||||
fmp->setCtrlLoadFromFile(next_lod);
|
||||
}
|
||||
loadModel(lod_filename, next_lod);
|
||||
loadModel(lod_file_to_check, next_lod);
|
||||
}
|
||||
else
|
||||
{
|
||||
lookupLODModelFiles(next_lod);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// No recognized extension found, continue with next LOD
|
||||
lookupLODModelFiles(next_lod);
|
||||
}
|
||||
}
|
||||
|
||||
void LLModelPreview::stateChangedCallback(U32 state, void* opaque)
|
||||
|
|
@ -3149,6 +3220,7 @@ U32 LLModelPreview::loadTextures(LLImportMaterial& material, LLHandle<LLModelPre
|
|||
tex->setLoadedCallback(LLModelPreview::textureLoadedCallback, 0, true, false, new LLHandle<LLModelPreview>(handle), &preview->mCallbackTextureList, false);
|
||||
tex->forceToSaveRawImage(0, F32_MAX);
|
||||
material.setDiffuseMap(tex->getID()); // record tex ID
|
||||
preview->mNumOfFetchingTextures++;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
@ -3993,6 +4065,18 @@ void LLModelPreview::setPreviewLOD(S32 lod)
|
|||
updateStatusMessages();
|
||||
}
|
||||
|
||||
void LLModelPreview::warnTextureScaling()
|
||||
{
|
||||
if (areTexturesReady() && mTexturesNeedScaling)
|
||||
{
|
||||
std::ostringstream out;
|
||||
out << "One or more textures in this model were scaled to be within the allowed limits.";
|
||||
LL_INFOS() << out.str() << LL_ENDL;
|
||||
LLSD args;
|
||||
LLFloaterModelPreview::addStringToLog("ModelTextureScaling", args, true, -1);
|
||||
}
|
||||
}
|
||||
|
||||
//static
|
||||
void LLModelPreview::textureLoadedCallback(
|
||||
bool success,
|
||||
|
|
@ -4013,11 +4097,19 @@ void LLModelPreview::textureLoadedCallback(
|
|||
LLModelPreview* preview = static_cast<LLModelPreview*>(handle->get());
|
||||
preview->refresh();
|
||||
|
||||
if (final && preview->mModelLoader)
|
||||
if (final)
|
||||
{
|
||||
if (preview->mModelLoader->mNumOfFetchingTextures > 0)
|
||||
if (src_vi
|
||||
&& (src_vi->getOriginalWidth() > LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT
|
||||
|| src_vi->getOriginalHeight() > LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT))
|
||||
{
|
||||
preview->mModelLoader->mNumOfFetchingTextures--;
|
||||
preview->mTexturesNeedScaling = true;
|
||||
}
|
||||
|
||||
if (preview->mNumOfFetchingTextures > 0)
|
||||
{
|
||||
preview->mNumOfFetchingTextures--;
|
||||
preview->warnTextureScaling();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -204,6 +204,7 @@ public:
|
|||
std::vector<S32> mLodsQuery;
|
||||
std::vector<S32> mLodsWithParsingError;
|
||||
bool mHasDegenerate;
|
||||
bool areTexturesReady() { return !mNumOfFetchingTextures; }
|
||||
|
||||
protected:
|
||||
|
||||
|
|
@ -213,6 +214,7 @@ protected:
|
|||
static LLJoint* lookupJointByName(const std::string&, void* opaque);
|
||||
static U32 loadTextures(LLImportMaterial& material, LLHandle<LLModelPreview> handle);
|
||||
|
||||
void warnTextureScaling();
|
||||
void lookupLODModelFiles(S32 lod);
|
||||
|
||||
private:
|
||||
|
|
@ -242,6 +244,9 @@ private:
|
|||
/// Not read unless mWarnOfUnmatchedPhyicsMeshes is true.
|
||||
LLPointer<LLModel> mDefaultPhysicsShapeP;
|
||||
|
||||
S32 mNumOfFetchingTextures;
|
||||
bool mTexturesNeedScaling;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
MESH_OPTIMIZER_FULL,
|
||||
|
|
|
|||
|
|
@ -843,6 +843,10 @@ void LLPanelPeople::updateNearbyList()
|
|||
|
||||
LLWorld::getInstance()->getAvatars(&mNearbyList->getIDs(), &positions, gAgent.getPositionGlobal(), gSavedSettings.getF32("NearMeRange"));
|
||||
mNearbyList->setDirty();
|
||||
#ifdef LL_DISCORD
|
||||
if (gSavedSettings.getBOOL("EnableDiscord"))
|
||||
LLAppViewer::updateDiscordPartyMaxSize((S32)mNearbyList->getIDs().size());
|
||||
#endif
|
||||
|
||||
DISTANCE_COMPARATOR.updateAvatarsPositions(positions, mNearbyList->getIDs());
|
||||
LLActiveSpeakerMgr::instance().update(true);
|
||||
|
|
|
|||
|
|
@ -154,11 +154,15 @@ void PeopleContextMenu::buildContextMenu(class LLMenuGL& menu, U32 flags)
|
|||
|
||||
bool PeopleContextMenu::enableContextMenuItem(const LLSD& userdata)
|
||||
{
|
||||
std::string item = userdata.asString();
|
||||
if(gAgent.getID() == mUUIDs.front())
|
||||
{
|
||||
if (item == std::string("can_zoom_in"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
std::string item = userdata.asString();
|
||||
|
||||
// Note: can_block and can_delete is used only for one person selected menu
|
||||
// so we don't need to go over all uuids.
|
||||
|
|
|
|||
|
|
@ -177,7 +177,7 @@ void LLReflectionMap::autoAdjustOrigin()
|
|||
mPriority = 1;
|
||||
mOrigin.load3(mViewerObject->getPositionAgent().mV);
|
||||
|
||||
if (mViewerObject->getVolume() && ((LLVOVolume*)mViewerObject)->getReflectionProbeIsBox())
|
||||
if (mViewerObject->getVolume() && ((LLVOVolume*)mViewerObject.get())->getReflectionProbeIsBox())
|
||||
{
|
||||
LLVector3 s = mViewerObject->getScale().scaledVec(LLVector3(0.5f, 0.5f, 0.5f));
|
||||
mRadius = s.magVec();
|
||||
|
|
|
|||
|
|
@ -124,7 +124,7 @@ public:
|
|||
LLSpatialGroup* mGroup = nullptr;
|
||||
|
||||
// viewer object this probe is tracking (if any)
|
||||
LLViewerObject* mViewerObject = nullptr;
|
||||
LLPointer<LLViewerObject> mViewerObject = nullptr;
|
||||
|
||||
// what priority should this probe have (higher is higher priority)
|
||||
// currently only 0 or 1
|
||||
|
|
|
|||
|
|
@ -1146,7 +1146,7 @@ void LLReflectionMapManager::updateUniforms()
|
|||
{
|
||||
if (refmap->mViewerObject && refmap->mViewerObject->getVolume())
|
||||
{ // have active manual probes live-track the object they're associated with
|
||||
LLVOVolume* vobj = (LLVOVolume*)refmap->mViewerObject;
|
||||
LLVOVolume* vobj = (LLVOVolume*)refmap->mViewerObject.get();
|
||||
|
||||
refmap->mOrigin.load3(vobj->getPositionAgent().mV);
|
||||
|
||||
|
|
|
|||
|
|
@ -135,6 +135,12 @@ void LLSkinningUtil::initSkinningMatrixPalette(
|
|||
|
||||
initJointNums(const_cast<LLMeshSkinInfo*>(skin), avatar);
|
||||
|
||||
if (skin->mInvBindMatrix.size() < count )
|
||||
{
|
||||
// faulty model? mInvBindMatrix.size() should have matched mJointNames.size()
|
||||
return;
|
||||
}
|
||||
|
||||
LLMatrix4a world[LL_CHARACTER_MAX_ANIMATED_JOINTS];
|
||||
|
||||
for (S32 j = 0; j < count; ++j)
|
||||
|
|
@ -354,7 +360,8 @@ void LLSkinningUtil::updateRiggingInfo(const LLMeshSkinInfo* skin, LLVOAvatar *a
|
|||
{
|
||||
rig_info_tab[joint_num].setIsRiggedTo(true);
|
||||
|
||||
const LLMatrix4a& mat = skin->mBindPoseMatrix[joint_index];
|
||||
size_t bind_poses_size = skin->mBindPoseMatrix.size();
|
||||
const LLMatrix4a& mat = bind_poses_size > joint_index ? skin->mBindPoseMatrix[joint_index] : LLMatrix4a::identity();
|
||||
LLVector4a pos_joint_space;
|
||||
|
||||
mat.affineTransform(pos, pos_joint_space);
|
||||
|
|
|
|||
|
|
@ -1026,6 +1026,10 @@ void LLLocalSpeakerMgr::updateSpeakerList()
|
|||
uuid_vec_t avatar_ids;
|
||||
std::vector<LLVector3d> positions;
|
||||
LLWorld::getInstance()->getAvatars(&avatar_ids, &positions, gAgent.getPositionGlobal(), CHAT_NORMAL_RADIUS);
|
||||
#ifdef LL_DISCORD
|
||||
if (gSavedSettings.getBOOL("EnableDiscord"))
|
||||
LLAppViewer::updateDiscordPartyCurrentSize((S32)avatar_ids.size());
|
||||
#endif
|
||||
for(U32 i=0; i<avatar_ids.size(); i++)
|
||||
{
|
||||
setSpeaker(avatar_ids[i]);
|
||||
|
|
|
|||
|
|
@ -724,6 +724,10 @@ bool idle_startup()
|
|||
LL_WARNS("AppInit") << "Unreliable timers detected (may be bad PCI chipset)!!" << LL_ENDL;
|
||||
}
|
||||
|
||||
#ifdef LL_DISCORD
|
||||
LLAppViewer::initDiscordSocial();
|
||||
#endif
|
||||
|
||||
//
|
||||
// Log on to system
|
||||
//
|
||||
|
|
@ -2103,9 +2107,6 @@ bool idle_startup()
|
|||
|
||||
do_startup_frame();
|
||||
|
||||
// We're successfully logged in.
|
||||
gSavedSettings.setBOOL("FirstLoginThisInstall", false);
|
||||
|
||||
LLFloaterReg::showInitialVisibleInstances();
|
||||
|
||||
LLFloaterGridStatus::getInstance()->startGridStatusTimer();
|
||||
|
|
@ -2451,6 +2452,27 @@ bool idle_startup()
|
|||
|
||||
LLPerfStats::StatsRecorder::setAutotuneInit();
|
||||
|
||||
// Display Avatar Welcome Pack the first time a user logs in
|
||||
// (or clears their settings....)
|
||||
if (gSavedSettings.getBOOL("FirstLoginThisInstall"))
|
||||
{
|
||||
LLFloater* avatar_welcome_pack_floater = LLFloaterReg::findInstance("avatar_welcome_pack");
|
||||
if (avatar_welcome_pack_floater != nullptr)
|
||||
{
|
||||
// There is a (very - 1 in ~50 times) hard to repro bug where the login
|
||||
// page is not hidden when the AWP floater is presented. This (agressive)
|
||||
// approach to always close it seems like the best fix for now.
|
||||
LLPanelLogin::closePanel();
|
||||
|
||||
avatar_welcome_pack_floater->setVisible(true);
|
||||
}
|
||||
}
|
||||
|
||||
//// We're successfully logged in.
|
||||
// 2025-06 Moved lower down in the state machine so the Avatar Welcome Pack
|
||||
// floater display can be triggered correctly.
|
||||
gSavedSettings.setBOOL("FirstLoginThisInstall", false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1541,12 +1541,6 @@ bool LLToolPie::shouldAllowFirstMediaInteraction(const LLPickInfo& pick, bool mo
|
|||
return false;
|
||||
}
|
||||
|
||||
// Own objects
|
||||
if((FirstClickPref & MEDIA_FIRST_CLICK_OWN) && object->permYouOwner())
|
||||
{
|
||||
LL_DEBUGS_ONCE() << "FirstClickPref & MEDIA_FIRST_CLICK_OWN" << LL_ENDL;
|
||||
return true;
|
||||
}
|
||||
// HUD attachments
|
||||
if((FirstClickPref & MEDIA_FIRST_CLICK_HUD) && object->isHUDAttachment())
|
||||
{
|
||||
|
|
@ -1569,21 +1563,29 @@ bool LLToolPie::shouldAllowFirstMediaInteraction(const LLPickInfo& pick, bool mo
|
|||
return false;
|
||||
}
|
||||
|
||||
// Own objects
|
||||
if((FirstClickPref & MEDIA_FIRST_CLICK_OWN) && owner_id == gAgent.getID())
|
||||
{
|
||||
LL_DEBUGS_ONCE() << "FirstClickPref & MEDIA_FIRST_CLICK_OWN" << LL_ENDL;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if the object is owned by a friend of the agent
|
||||
if(FirstClickPref & MEDIA_FIRST_CLICK_FRIEND)
|
||||
{
|
||||
if(LLAvatarTracker::instance().isBuddy(owner_id))
|
||||
{
|
||||
LL_DEBUGS_ONCE() << "FirstClickPref & MEDIA_FIRST_CLICK_FRIEND. id: " << owner_id << LL_ENDL;
|
||||
return LLAvatarTracker::instance().isBuddy(owner_id);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for objects set to or owned by the active group
|
||||
if(FirstClickPref & MEDIA_FIRST_CLICK_GROUP)
|
||||
{
|
||||
// Get our active group
|
||||
LLUUID active_group = gAgent.getGroupID();
|
||||
if(active_group.notNull() && (active_group == group_id || active_group == owner_id))
|
||||
if(gAgent.isInGroup(group_id) || gAgent.isInGroup(owner_id))
|
||||
{
|
||||
LL_DEBUGS_ONCE() << "FirstClickPref & MEDIA_FIRST_CLICK_GROUP.Active group: " << active_group << ", group_id:" << group_id << ", owner_id: " << owner_id << LL_ENDL;
|
||||
LL_DEBUGS_ONCE() << "FirstClickPref & MEDIA_FIRST_CLICK_GROUP. group_id:" << group_id << ", owner_id: " << owner_id << LL_ENDL;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -99,10 +99,10 @@ private:
|
|||
MEDIA_FIRST_CLICK_LAND = 1 << 4, // 0b00010000 (16)
|
||||
|
||||
// Covers any object with PRIM_MEDIA_FIRST_CLICK_INTERACT (combines all previous flags)
|
||||
MEDIA_FIRST_CLICK_ANY = ~(3<<30), // 0b00111111111111111111111111111111
|
||||
MEDIA_FIRST_CLICK_ANY = (1 << 15) - 1, // 0b0111111111111111 (32767)
|
||||
|
||||
// Covers all media regardless of other rules or PRIM_MEDIA_FIRST_CLICK_INTERACT
|
||||
MEDIA_FIRST_CLICK_BYPASS_MOAP_FLAG = 1 << 30 // 0b01000000000000000000000000000000 (1073741824)
|
||||
MEDIA_FIRST_CLICK_BYPASS_MOAP_FLAG = 1 << 15 // 0b10000000000000000 (32768)
|
||||
};
|
||||
bool shouldAllowFirstMediaInteraction(const LLPickInfo& info, bool moap_flag);
|
||||
bool handleMediaClick(const LLPickInfo& info);
|
||||
|
|
|
|||
|
|
@ -38,8 +38,8 @@
|
|||
#include "llfloateraddpaymentmethod.h"
|
||||
#include "llfloaterauction.h"
|
||||
#include "llfloaterautoreplacesettings.h"
|
||||
#include "llfloateravatar.h"
|
||||
#include "llfloateravatarpicker.h"
|
||||
#include "llfloateravatarwelcomepack.h"
|
||||
#include "llfloateravatarrendersettings.h"
|
||||
#include "llfloateravatartextures.h"
|
||||
#include "llfloaterbanduration.h"
|
||||
|
|
@ -331,8 +331,8 @@ void LLViewerFloaterReg::registerFloaters()
|
|||
LLFloaterReg::add("appearance", "floater_my_appearance.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSidePanelContainer>);
|
||||
LLFloaterReg::add("associate_listing", "floater_associate_listing.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterAssociateListing>);
|
||||
LLFloaterReg::add("auction", "floater_auction.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterAuction>);
|
||||
LLFloaterReg::add("avatar", "floater_avatar.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterAvatar>);
|
||||
LLFloaterReg::add("avatar_picker", "floater_avatar_picker.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterAvatarPicker>);
|
||||
LLFloaterReg::add("avatar_welcome_pack", "floater_avatar_welcome_pack.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterAvatarWelcomePack>);
|
||||
LLFloaterReg::add("avatar_render_settings", "floater_avatar_render_settings.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterAvatarRenderSettings>);
|
||||
LLFloaterReg::add("avatar_textures", "floater_avatar_textures.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterAvatarTextures>);
|
||||
|
||||
|
|
|
|||
|
|
@ -60,13 +60,26 @@ void LLStreamingAudio_MediaPlugins::start(const std::string& url)
|
|||
if(!mMediaPlugin)
|
||||
return;
|
||||
|
||||
if (!url.empty()) {
|
||||
if (!url.empty())
|
||||
{
|
||||
LL_INFOS() << "Starting internet stream: " << url << LL_ENDL;
|
||||
mURL = url;
|
||||
mMediaPlugin->loadURI ( url );
|
||||
|
||||
mURL = url; // keep original url here for comparison purposes
|
||||
std::string snt_url = url;
|
||||
LLStringUtil::trim(snt_url);
|
||||
size_t pos = snt_url.find(' ');
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
// fmod permited having names after the url and people were using it.
|
||||
// People label their streams this way, ignore the 'label'.
|
||||
snt_url = snt_url.substr(0, pos);
|
||||
}
|
||||
mMediaPlugin->loadURI(snt_url);
|
||||
mMediaPlugin->start();
|
||||
LL_INFOS() << "Playing stream..." << LL_ENDL;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_INFOS() << "setting stream to NULL"<< LL_ENDL;
|
||||
mURL.clear();
|
||||
mMediaPlugin->stop();
|
||||
|
|
|
|||
|
|
@ -289,6 +289,7 @@ void force_error_coroutine_crash();
|
|||
void force_error_coroprocedure_crash();
|
||||
void force_error_work_queue_crash();
|
||||
void force_error_thread_crash();
|
||||
void force_exception_thread_crash();
|
||||
|
||||
void handle_force_delete();
|
||||
void print_object_info();
|
||||
|
|
@ -2663,6 +2664,15 @@ class LLAdvancedForceErrorThreadCrash : public view_listener_t
|
|||
}
|
||||
};
|
||||
|
||||
class LLAdvancedForceExceptionThreadCrash : public view_listener_t
|
||||
{
|
||||
bool handleEvent(const LLSD& userdata)
|
||||
{
|
||||
force_exception_thread_crash();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class LLAdvancedForceErrorDisconnectViewer : public view_listener_t
|
||||
{
|
||||
bool handleEvent(const LLSD& userdata)
|
||||
|
|
@ -8696,6 +8706,11 @@ void force_error_thread_crash()
|
|||
LLAppViewer::instance()->forceErrorThreadCrash();
|
||||
}
|
||||
|
||||
void force_exception_thread_crash()
|
||||
{
|
||||
LLAppViewer::instance()->forceExceptionThreadCrash();
|
||||
}
|
||||
|
||||
class LLToolsUseSelectionForGrid : public view_listener_t
|
||||
{
|
||||
bool handleEvent(const LLSD& userdata)
|
||||
|
|
@ -9898,6 +9913,7 @@ void initialize_menus()
|
|||
view_listener_t::addMenu(new LLAdvancedForceErrorCoroprocedureCrash(), "Advanced.ForceErrorCoroprocedureCrash");
|
||||
view_listener_t::addMenu(new LLAdvancedForceErrorWorkQueueCrash(), "Advanced.ForceErrorWorkQueueCrash");
|
||||
view_listener_t::addMenu(new LLAdvancedForceErrorThreadCrash(), "Advanced.ForceErrorThreadCrash");
|
||||
view_listener_t::addMenu(new LLAdvancedForceExceptionThreadCrash(), "Advanced.ForceExceptionThreadCrash");
|
||||
view_listener_t::addMenu(new LLAdvancedForceErrorDisconnectViewer(), "Advanced.ForceErrorDisconnectViewer");
|
||||
|
||||
// Advanced (toplevel)
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ class LLFileEnableUploadModel : public view_listener_t
|
|||
bool handleEvent(const LLSD& userdata)
|
||||
{
|
||||
LLFloaterModelPreview* fmp = (LLFloaterModelPreview*) LLFloaterReg::findInstance("upload_model");
|
||||
if (fmp && fmp->isModelLoading())
|
||||
if (fmp && !fmp->isDead() && fmp->isModelLoading())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3052,6 +3052,11 @@ void process_agent_movement_complete(LLMessageSystem* msg, void**)
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef LL_DISCORD
|
||||
if (gSavedSettings.getBOOL("EnableDiscord"))
|
||||
LLAppViewer::updateDiscordActivity();
|
||||
#endif
|
||||
|
||||
if ( LLTracker::isTracking(NULL) )
|
||||
{
|
||||
// Check distance to beacon, if < 5m, remove beacon
|
||||
|
|
|
|||
|
|
@ -229,7 +229,11 @@ LLTrace::SampleStatHandle<F64Milliseconds > FRAMETIME_JITTER("frametimejitter",
|
|||
FRAMETIME_JITTER_STDDEV("frametimejitterstddev", "Standard deviation of frametime jitter in a 5 second period."),
|
||||
FRAMETIME_STDDEV("frametimestddev", "Standard deviation of frametime in a 5 second period.");
|
||||
|
||||
LLTrace::SampleStatHandle<U32> FRAMETIME_JITTER_EVENTS("frametimeevents", "Number of frametime events in the session. Applies when jitter exceeds 10% of the previous frame.");
|
||||
LLTrace::SampleStatHandle<U32> FRAMETIME_JITTER_EVENTS("frametimeevents", "Number of frametime events in the session. Applies when jitter exceeds 10% of the previous frame."),
|
||||
FRAMETIME_JITTER_EVENTS_PER_MINUTE("frametimeeventspm", "Average number of frametime events per minute."),
|
||||
FRAMETIME_JITTER_EVENTS_LAST_MINUTE("frametimeeventslastmin", "Number of frametime events in the last minute.");
|
||||
|
||||
LLTrace::SampleStatHandle<F64> NOTRMALIZED_FRAMETIME_JITTER_SESSION("normalizedframetimejitter", "Normalized frametime jitter over the session.");
|
||||
|
||||
LLTrace::EventStatHandle<LLUnit<F64, LLUnits::Meters> > AGENT_POSITION_SNAP("agentpositionsnap", "agent position corrections");
|
||||
|
||||
|
|
@ -309,24 +313,28 @@ void LLViewerStats::updateFrameStats(const F64Seconds time_diff)
|
|||
{
|
||||
if (gFrameCount && mLastTimeDiff > (F64Seconds)0.0)
|
||||
{
|
||||
mTotalTime += time_diff;
|
||||
sample(LLStatViewer::FRAMETIME, time_diff);
|
||||
// old stats that were never really used
|
||||
F64Seconds jit = (F64Seconds)std::fabs((mLastTimeDiff - time_diff));
|
||||
sample(LLStatViewer::FRAMETIME_JITTER, jit);
|
||||
mTotalFrametimeJitter += jit;
|
||||
sample(LLStatViewer::FRAMETIME_JITTER_CUMULATIVE, mTotalFrametimeJitter);
|
||||
sample(LLStatViewer::NOTRMALIZED_FRAMETIME_JITTER_SESSION, mTotalFrametimeJitter / mTotalTime);
|
||||
|
||||
static LLCachedControl<F32> frameTimeEventThreshold(gSavedSettings, "StatsFrametimeEventThreshold", 0.1f);
|
||||
|
||||
if (time_diff - mLastTimeDiff > mLastTimeDiff * frameTimeEventThreshold())
|
||||
{
|
||||
sample(LLStatViewer::FRAMETIME_JITTER_EVENTS, mFrameJitterEvents++);
|
||||
mFrameJitterEventsLastMinute++;
|
||||
}
|
||||
|
||||
mFrameTimes.push_back(time_diff);
|
||||
mFrameTimesJitter.push_back(jit);
|
||||
|
||||
mLastFrameTimeSample += time_diff;
|
||||
mTimeSinceLastEventSample += time_diff;
|
||||
|
||||
static LLCachedControl<S32> frameTimeSampleSeconds(gSavedSettings, "StatsFrametimeSampleSeconds", 5);
|
||||
|
||||
|
|
@ -356,6 +364,17 @@ void LLViewerStats::updateFrameStats(const F64Seconds time_diff)
|
|||
mFrameTimesJitter.clear();
|
||||
mLastFrameTimeSample = F64Seconds(0);
|
||||
}
|
||||
|
||||
if (mTimeSinceLastEventSample >= 60)
|
||||
{
|
||||
mEventMinutes++;
|
||||
// Calculate average events per minute
|
||||
U64 frame_time_events_per_minute = (U64)mFrameJitterEvents / mEventMinutes;
|
||||
sample(LLStatViewer::FRAMETIME_JITTER_EVENTS_PER_MINUTE, frame_time_events_per_minute);
|
||||
sample(LLStatViewer::FRAMETIME_JITTER_EVENTS_LAST_MINUTE, mFrameJitterEventsLastMinute);
|
||||
mFrameJitterEventsLastMinute = 0;
|
||||
mTimeSinceLastEventSample = F64Seconds(0);
|
||||
}
|
||||
}
|
||||
mLastTimeDiff = time_diff;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -277,9 +277,13 @@ private:
|
|||
F64Seconds mLastTimeDiff; // used for time stat updates
|
||||
F64Seconds mTotalFrametimeJitter;
|
||||
|
||||
U32 mFrameJitterEvents;
|
||||
U32 mFrameJitterEvents = 0;
|
||||
U32 mFrameJitterEventsLastMinute = 0;
|
||||
U32 mEventMinutes = 0;
|
||||
F64Seconds mTotalTime;
|
||||
|
||||
F64Seconds mLastFrameTimeSample; // used for frame time stats
|
||||
F64Seconds mTimeSinceLastEventSample;
|
||||
std::vector<F64Seconds> mFrameTimes; // used for frame time stats
|
||||
std::vector<F64Seconds> mFrameTimesJitter; // used for frame time jitter stats
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2291,13 +2291,13 @@ void LLViewerWindow::initWorldUI()
|
|||
url = LLWeb::expandURLSubstitutions(url, LLSD());
|
||||
destinations->navigateTo(url, HTTP_CONTENT_TEXT_HTML);
|
||||
}
|
||||
LLMediaCtrl* avatar_picker = LLFloaterReg::getInstance("avatar")->findChild<LLMediaCtrl>("avatar_picker_contents");
|
||||
if (avatar_picker)
|
||||
LLMediaCtrl* avatar_welcome_pack = LLFloaterReg::getInstance("avatar_welcome_pack")->findChild<LLMediaCtrl>("avatar_picker_contents");
|
||||
if (avatar_welcome_pack)
|
||||
{
|
||||
avatar_picker->setErrorPageURL(gSavedSettings.getString("GenericErrorPageURL"));
|
||||
std::string url = gSavedSettings.getString("AvatarPickerURL");
|
||||
avatar_welcome_pack->setErrorPageURL(gSavedSettings.getString("GenericErrorPageURL"));
|
||||
std::string url = gSavedSettings.getString("AvatarWelcomePack");
|
||||
url = LLWeb::expandURLSubstitutions(url, LLSD());
|
||||
avatar_picker->navigateTo(url, HTTP_CONTENT_TEXT_HTML);
|
||||
avatar_welcome_pack->navigateTo(url, HTTP_CONTENT_TEXT_HTML);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -655,7 +655,6 @@ with the same filename but different name
|
|||
|
||||
<texture name="login_sl_logo" file_name="windows/login_sl_logo.png" preload="true" />
|
||||
<texture name="login_sl_logo_small" file_name="windows/login_sl_logo_small.png" preload="true" />
|
||||
<texture name="first_login_image" file_name="windows/first_login_image.jpg" preload="true" />
|
||||
|
||||
<texture name="Stepper_Down_Off" file_name="widgets/Stepper_Down_Off.png" preload="false" />
|
||||
<texture name="Stepper_Down_Press" file_name="widgets/Stepper_Down_Press.png" preload="false" />
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||
<floater
|
||||
positioning="cascading"
|
||||
legacy_header_height="225"
|
||||
can_minimize="true"
|
||||
can_close="true"
|
||||
can_resize="false"
|
||||
min_height="438"
|
||||
min_width="530"
|
||||
height="438"
|
||||
layout="topleft"
|
||||
name="Avatar Welcome Pack"
|
||||
single_instance="true"
|
||||
save_rect="true"
|
||||
save_visibility="true"
|
||||
title="AVATAR WELCOME PACK"
|
||||
width="530">
|
||||
<web_browser
|
||||
top="25"
|
||||
height="438"
|
||||
width="530"
|
||||
follows="all"
|
||||
name="avatar_picker_contents"
|
||||
trusted_content="true"/>
|
||||
</floater>
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
legacy_header_height="25">
|
||||
|
||||
<string name="status_idle"></string>
|
||||
<string name="status_parse_error">Error: Dae parsing issue - see log for details.</string>
|
||||
<string name="status_parse_error">Error: Model parsing issue - see log for details.</string>
|
||||
<string name="status_bind_shape_orientation">Warning: bind shape matrix is not in standard X-forward orientation.</string>
|
||||
<string name="status_material_mismatch">Error: Material of model is not a subset of reference model.</string>
|
||||
<string name="status_reading_file">Loading...</string>
|
||||
|
|
@ -39,12 +39,14 @@
|
|||
<string name="decomposing">Analyzing...</string>
|
||||
<string name="simplifying">Simplifying...</string>
|
||||
<string name="tbd">TBD</string>
|
||||
<string name="ModelTextureScaling">One or more textures in this model were scaled to be within the allowed limits.</string>
|
||||
|
||||
<!-- Warnings and info from model loader-->
|
||||
<string name="TooManyJoint">Skinning disabled due to too many joints: [JOINTS], maximum: [MAX]</string>
|
||||
<string name="UnrecognizedJoint">Rigged to unrecognized joint name [NAME]</string>
|
||||
<string name="UnknownJoints">Skinning disabled due to [COUNT] unknown joints</string>
|
||||
<string name="ModelLoaded">Model [MODEL_NAME] loaded</string>
|
||||
<string name="InvBindCountMismatch">Bind matrices count mismatch joints count</string>
|
||||
|
||||
<string name="IncompleteTC">Texture coordinates data is not complete.</string>
|
||||
<string name="PositionNaN">Found NaN while loading position data from DAE-Model, invalid model.</string>
|
||||
|
|
@ -60,6 +62,27 @@
|
|||
<string name="ParsingErrorNoRoot">Document has no root</string>
|
||||
<string name="ParsingErrorNoScene">Document has no visual_scene</string>
|
||||
<string name="ParsingErrorPositionInvalidModel">Unable to process mesh without position data. Invalid model.</string>
|
||||
<string name="UnknownException">Importer crashed while processing [FILENAME], if you encounter this and file is valid, please report the issue to Second Life Support. Exception: [EXCEPTION].</string>
|
||||
|
||||
<!-- GLTF specific messages -->
|
||||
<string name="NoScenesFound">No scenes defined in GLTF file</string>
|
||||
<string name="InvalidMeshReference">Node [NODE_NAME] references invalid mesh [MESH_INDEX] (total meshes: [TOTAL_MESHES])</string>
|
||||
<string name="InvalidGeometryNonTriangulated">Mesh [MESH_NAME] primitive [PRIMITIVE_INDEX]: Invalid geometry with [INDEX_COUNT] indices (must be triangulated)</string>
|
||||
<string name="EmptyVertexArray">Mesh [MESH_NAME] primitive [PRIMITIVE_INDEX]: Empty vertex array</string>
|
||||
<string name="ErrorIndexLimit">Unable to process mesh [MESH_NAME] due to 65,534 vertex limit. Vertex count: [VERTEX_COUNT]</string>
|
||||
<string name="TextureFound">Found texture: [TEXTURE_NAME] for material: [MATERIAL_NAME]</string>
|
||||
<string name="IgnoredExtension">Model uses unsupported extension: [EXT], related material properties are ignored</string>
|
||||
<string name="UnsupportedExtension">Unable to load model, unsupported extension: [EXT]</string>
|
||||
<string name="FailedToCreateTempFile">Failed to create temporary file for embedded [TEXTURE_TYPE] texture [TEXTURE_INDEX]: [TEMP_FILE]</string>
|
||||
<string name="SkinJointsOverLimit">Skin [SKIN_INDEX] defines [JOINT_COUNT] compatible joints, maximum is: [MAX]. Unused joints will be stripped on per model basis.</string>
|
||||
<string name="SkinUsupportedJoints">Skin [SKIN_INDEX] defines [JOINT_COUNT] joints, but only [LEGAL_COUNT] were recognized and are compatible</string>
|
||||
<string name="SkinUnusedJoints">Skin [SKIN_INDEX] defines [JOINT_COUNT] compatible joints, of them only [USED_COUNT] were used</string>
|
||||
<string name="ModelTooManyJoints">Model [MODEL_NAME] uses [JOINT_COUNT], maximum: [MAX], upload might fail</string>
|
||||
<string name="ModelSplitPrimitive">Too many vertices in primitive [MODEL_NAME], it was split into [FACE_COUNT] faces</string>
|
||||
<string name="ModelTooManySubmodels">Model [MODEL_NAME] contains [SUBMODEL_COUNT] generated mesh parts, parts were trimmed to [SUBMODEL_LIMIT]</string>
|
||||
<string name="ParsingErrorMissingBuffer">Buffer is either missing or empty [BUFFER_NAME].</string>
|
||||
<string name="ParsingErrorMissingBufferBin">Buffer is either missing or empty. Check presence of [BUFFER_URI] file.</string>
|
||||
<string name="ParsingErrorException">Parser failed to process [FILENAME], file might be corrupt, incomplete or protected from reading. Exception: [EXCEPTION].</string>
|
||||
|
||||
<panel
|
||||
follows="top|left"
|
||||
|
|
@ -1404,7 +1427,7 @@
|
|||
word_wrap="true">
|
||||
</text_editor>
|
||||
<check_box
|
||||
control_name="ImporterDebug"
|
||||
control_name="ImporterDebugVerboseLogging"
|
||||
follows="top|left"
|
||||
top_pad="9"
|
||||
left="6"
|
||||
|
|
@ -1706,7 +1729,6 @@ Analysed:
|
|||
height="408"/>
|
||||
<panel
|
||||
follows="right|bottom"
|
||||
can_resize="false"
|
||||
height="140"
|
||||
layout="topleft"
|
||||
name="right_panel"
|
||||
|
|
|
|||
|
|
@ -54,38 +54,19 @@
|
|||
label="jitter"
|
||||
decimal_digits="1"
|
||||
stat="frametimejitter"/>
|
||||
<stat_bar name="framet_cumulative"
|
||||
label="jitter cumulative"
|
||||
decimal_digits="1"
|
||||
stat="frametimejitcumulative"/>
|
||||
<stat_bar name="framet_jitter_99th"
|
||||
label="jitter 99th percentile"
|
||||
decimal_digits="1"
|
||||
stat="frametimejitter99"/>
|
||||
<stat_bar name="framet_jitter_95th"
|
||||
label="jitter 95th percentile"
|
||||
decimal_digits="1"
|
||||
stat="frametimejitter95"/>
|
||||
<stat_bar name="framet_jitter_stddev"
|
||||
label="frametime jitter standard deviation"
|
||||
decimal_digits="1"
|
||||
stat="frametimejitterstddev"/>
|
||||
<stat_bar name="framet_99th"
|
||||
label="frametime 99th percentile"
|
||||
decimal_digits="1"
|
||||
stat="frametime99"/>
|
||||
<stat_bar name="framet_95th"
|
||||
label="frametime 95th percentile"
|
||||
decimal_digits="1"
|
||||
stat="frametime95"/>
|
||||
<stat_bar name="framet_stddev"
|
||||
label="frametime standard deviation"
|
||||
decimal_digits="1"
|
||||
stat="frametimestddev"/>
|
||||
<stat_bar name="framet_events"
|
||||
label="frametime events"
|
||||
decimal_digits="1"
|
||||
stat="frametimeevents"/>
|
||||
<stat_bar name="normalized_cumulative_frametime"
|
||||
label="normalized sess. jitter"
|
||||
decimal_digits="4"
|
||||
stat="normalizedframetimejitter"/>
|
||||
<stat_bar name="frame_events_per_minute"
|
||||
label="frame events/minute"
|
||||
decimal_digits="2"
|
||||
stat="frametimeeventspm"/>
|
||||
<stat_bar name="frame_events_last_minute"
|
||||
label="frame events last min."
|
||||
decimal_digits="0"
|
||||
stat="frametimeeventslastmin"/>
|
||||
|
||||
<stat_bar name="bandwidth"
|
||||
label="UDP Data Received"
|
||||
stat="activemessagedatareceived"
|
||||
|
|
@ -106,6 +87,38 @@
|
|||
<stat_view name="render"
|
||||
label="Render"
|
||||
setting="OpenDebugStatRender">
|
||||
<stat_bar name="framet_cumulative"
|
||||
label="jitter cumulative"
|
||||
decimal_digits="1"
|
||||
stat="frametimejitcumulative"/>
|
||||
<stat_bar name="framet_jitter_99th"
|
||||
label="jitter 99th percentile"
|
||||
decimal_digits="1"
|
||||
stat="frametimejitter99"/>
|
||||
<stat_bar name="framet_jitter_95th"
|
||||
label="jitter 95th percentile"
|
||||
decimal_digits="1"
|
||||
stat="frametimejitter95"/>
|
||||
<stat_bar name="framet_jitter_stddev"
|
||||
label="frametime jitter std dev"
|
||||
decimal_digits="1"
|
||||
stat="frametimejitterstddev"/>
|
||||
<stat_bar name="framet_99th"
|
||||
label="frametime 99th percentile"
|
||||
decimal_digits="1"
|
||||
stat="frametime99"/>
|
||||
<stat_bar name="framet_95th"
|
||||
label="frametime 95th percentile"
|
||||
decimal_digits="1"
|
||||
stat="frametime95"/>
|
||||
<stat_bar name="framet_stddev"
|
||||
label="frametime std dev"
|
||||
decimal_digits="1"
|
||||
stat="frametimestddev"/>
|
||||
<stat_bar name="framet_events"
|
||||
label="frametime events"
|
||||
decimal_digits="0"
|
||||
stat="frametimeevents"/>
|
||||
<stat_bar name="ktrisframe"
|
||||
label="KTris per Frame"
|
||||
unit_label="ktris/fr"
|
||||
|
|
|
|||
|
|
@ -411,11 +411,11 @@
|
|||
</menu_item_call>
|
||||
<menu_item_separator/>
|
||||
<menu_item_call
|
||||
label="Complete avatars..."
|
||||
name="Avatar Picker">
|
||||
label="Avatar Welcome Pack..."
|
||||
name="Avatar Welcome Pack">
|
||||
<menu_item_call.on_click
|
||||
function="Floater.ToggleOrBringToFront"
|
||||
parameter="avatar" />
|
||||
parameter="avatar_welcome_pack" />
|
||||
</menu_item_call>
|
||||
<menu_item_separator/>
|
||||
|
||||
|
|
@ -2808,11 +2808,17 @@ function="World.EnvPreset"
|
|||
function="Advanced.ForceErrorWorkQueueCrash" />
|
||||
</menu_item_call>
|
||||
<menu_item_call
|
||||
label="Force a Crash in a Thread"
|
||||
name="Force a Crash in a Thread">
|
||||
label="Force an LLError Crash in a Thread"
|
||||
name="Force an LLError Crash in a Thread">
|
||||
<menu_item_call.on_click
|
||||
function="Advanced.ForceErrorThreadCrash" />
|
||||
</menu_item_call>
|
||||
<menu_item_call
|
||||
label="Force an Exception Crash in a Thread"
|
||||
name="Force an Exception Crash in a Thread">
|
||||
<menu_item_call.on_click
|
||||
function="Advanced.ForceExceptionThreadCrash" />
|
||||
</menu_item_call>
|
||||
<menu_item_call
|
||||
label="Force Disconnect Viewer"
|
||||
name="Force Disconnect Viewer">
|
||||
|
|
|
|||
|
|
@ -7135,6 +7135,20 @@ You don't have permission to view this notecard.
|
|||
<tag>fail</tag>
|
||||
</notification>
|
||||
|
||||
<notification
|
||||
icon="alertmodal.tga"
|
||||
name="MaterialImagesWereScaled"
|
||||
type="alertmodal">
|
||||
One or more textures in this material were scaled to be within the allowed limits.
|
||||
Textures must have power of two dimensions and must not exceed [MAX_SIZE]x[MAX_SIZE] pixels.
|
||||
<unique/>
|
||||
<tag>confirm</tag>
|
||||
<usetemplate
|
||||
ignoretext="Warn if textures will be scaled during upload."
|
||||
name="okignore"
|
||||
yestext="OK"/>
|
||||
</notification>
|
||||
|
||||
<notification
|
||||
icon="notifytip.tga"
|
||||
name="RezItemNoPermissions"
|
||||
|
|
@ -9453,8 +9467,11 @@ Unable to upload texture: '[NAME]'
|
|||
icon="alertmodal.tga"
|
||||
name="CannotUploadMaterial"
|
||||
type="alertmodal">
|
||||
There was a problem uploading the file
|
||||
Unable to upload material file. The file may be corrupted, in an unsupported format, or contain invalid data. Please check that you're using a valid GLTF/GLB file with proper material definitions.
|
||||
<tag>fail</tag>
|
||||
<usetemplate
|
||||
name="okbutton"
|
||||
yestext="OK"/>
|
||||
</notification>
|
||||
|
||||
<notification
|
||||
|
|
|
|||
|
|
@ -1,15 +1,31 @@
|
|||
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||
<panel
|
||||
<panel
|
||||
border="true"
|
||||
follows="left|top|right|bottom"
|
||||
height="408"
|
||||
label="Communication"
|
||||
height="438"
|
||||
label="Privacy"
|
||||
layout="topleft"
|
||||
left="102"
|
||||
name="im"
|
||||
name="Privacy panel"
|
||||
top="1"
|
||||
width="517">
|
||||
|
||||
<tab_container
|
||||
top_pad="0"
|
||||
enabled="true"
|
||||
follows="left|top"
|
||||
height="430"
|
||||
width="517"
|
||||
left_delta="0"
|
||||
name="privacy_tab_container"
|
||||
tab_position="top"
|
||||
tab_stop="false">
|
||||
<panel
|
||||
label="General"
|
||||
name="privacy_preferences_general"
|
||||
layout="topleft"
|
||||
follows="top|left">
|
||||
|
||||
<panel.string
|
||||
name="log_in_to_change">
|
||||
log in to change
|
||||
|
|
@ -134,3 +150,48 @@
|
|||
(People and/or Objects you have blocked)
|
||||
</text>
|
||||
</panel>
|
||||
|
||||
<panel
|
||||
label="Discord"
|
||||
name="privacy_preferences_discord"
|
||||
layout="topleft"
|
||||
follows="top|left">
|
||||
|
||||
<check_box
|
||||
control_name="EnableDiscord"
|
||||
height="16"
|
||||
enabled="true"
|
||||
label="Enable Discord integration"
|
||||
layout="topleft"
|
||||
left="30"
|
||||
name="enable_discord"
|
||||
top_pad="20"
|
||||
width="350" />
|
||||
|
||||
<check_box
|
||||
enabled_control="EnableDiscord"
|
||||
control_name="ShowDiscordActivityDetails"
|
||||
height="16"
|
||||
enabled="true"
|
||||
label="Show avatar name"
|
||||
layout="topleft"
|
||||
left="30"
|
||||
name="show_name"
|
||||
top_pad="20"
|
||||
width="350" />
|
||||
|
||||
<check_box
|
||||
enabled_control="EnableDiscord"
|
||||
control_name="ShowDiscordActivityState"
|
||||
height="16"
|
||||
enabled="false"
|
||||
label="Show location"
|
||||
layout="topleft"
|
||||
left="30"
|
||||
name="show_location"
|
||||
top_pad="20"
|
||||
width="350" />
|
||||
</panel>
|
||||
</tab_container>
|
||||
|
||||
</panel>
|
||||
|
|
|
|||
|
|
@ -422,11 +422,11 @@
|
|||
<item
|
||||
label="Anyone's objects"
|
||||
name="media_first_interact_any"
|
||||
value="1073741823"/>
|
||||
value="32767"/>
|
||||
<item
|
||||
label="All MOAP"
|
||||
name="media_first_click_all"
|
||||
value="2147483647"/>
|
||||
value="65535"/>
|
||||
</combo_box>
|
||||
<check_box
|
||||
name="media_show_on_others_btn"
|
||||
|
|
|
|||
|
|
@ -4186,7 +4186,7 @@ Try enclosing path to the editor with double quotes.
|
|||
name="Command_360_Capture_Label">360 snapshot</string>
|
||||
<string name="Command_AboutLand_Label">About land</string>
|
||||
<string name="Command_Appearance_Label">Outfits</string>
|
||||
<string name="Command_Avatar_Label">Complete avatars</string>
|
||||
<string name="Command_Avatar_Label">Avatar Welcome Pack</string>
|
||||
<string name="Command_Build_Label">Build</string>
|
||||
<string name="Command_Chat_Label">Chat</string>
|
||||
<string name="Command_Conversations_Label">Conversations</string>
|
||||
|
|
|
|||
|
|
@ -556,6 +556,9 @@ class Windows_x86_64_Manifest(ViewerManifest):
|
|||
):
|
||||
self.path(libfile)
|
||||
|
||||
if self.args['discord'] == 'ON':
|
||||
self.path("discord_partner_sdk.dll")
|
||||
|
||||
if self.args['openal'] == 'ON':
|
||||
# Get openal dll
|
||||
self.path("OpenAL32.dll")
|
||||
|
|
@ -1018,6 +1021,13 @@ class Darwin_x86_64_Manifest(ViewerManifest):
|
|||
):
|
||||
self.path2basename(relpkgdir, libfile)
|
||||
|
||||
# Discord social SDK
|
||||
if self.args['discord'] == 'ON':
|
||||
for libfile in (
|
||||
"libdiscord_partner_sdk.dylib",
|
||||
):
|
||||
self.path2basename(relpkgdir, libfile)
|
||||
|
||||
# OpenAL dylibs
|
||||
if self.args['openal'] == 'ON':
|
||||
for libfile in (
|
||||
|
|
@ -1384,6 +1394,7 @@ if __name__ == "__main__":
|
|||
extra_arguments = [
|
||||
dict(name='bugsplat', description="""BugSplat database to which to post crashes,
|
||||
if BugSplat crash reporting is desired""", default=''),
|
||||
dict(name='discord', description="""Indication discord social sdk libraries are needed""", default='OFF'),
|
||||
dict(name='openal', description="""Indication openal libraries are needed""", default='OFF'),
|
||||
dict(name='tracy', description="""Indication tracy profiler is enabled""", default='OFF'),
|
||||
]
|
||||
|
|
|
|||
Loading…
Reference in New Issue