Merge branch 'develop' into atlas-qaworkflow
commit
e4a25dc5b9
|
|
@ -218,8 +218,10 @@ jobs:
|
||||||
prefix=${ba[0]}
|
prefix=${ba[0]}
|
||||||
if [ "$prefix" == "project" ]; then
|
if [ "$prefix" == "project" ]; then
|
||||||
IFS='_' read -ra prj <<< "${ba[1]}"
|
IFS='_' read -ra prj <<< "${ba[1]}"
|
||||||
|
prj_str="${prj[*]}"
|
||||||
# uppercase first letter of each word
|
# 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" ]];
|
elif [[ "$prefix" == "release" || "$prefix" == "main" ]];
|
||||||
then
|
then
|
||||||
export viewer_channel="Second Life Release"
|
export viewer_channel="Second Life Release"
|
||||||
|
|
@ -455,7 +457,6 @@ jobs:
|
||||||
prerelease: true
|
prerelease: true
|
||||||
generate_release_notes: true
|
generate_release_notes: true
|
||||||
target_commitish: ${{ github.sha }}
|
target_commitish: ${{ github.sha }}
|
||||||
previous_tag: release
|
|
||||||
append_body: true
|
append_body: true
|
||||||
fail_on_unmatched_files: true
|
fail_on_unmatched_files: true
|
||||||
files: |
|
files: |
|
||||||
|
|
|
||||||
|
|
@ -2896,6 +2896,64 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
|
||||||
<key>version</key>
|
<key>version</key>
|
||||||
<string>1.0.9-5e8947c</string>
|
<string>1.0.9-5e8947c</string>
|
||||||
</map>
|
</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>
|
</map>
|
||||||
<key>package_description</key>
|
<key>package_description</key>
|
||||||
<map>
|
<map>
|
||||||
|
|
|
||||||
|
|
@ -103,13 +103,11 @@ Perform the testing procedure on both sets of cubes.
|
||||||
|
|
||||||
Ensure that debug setting `MediaFirstClickInteract` is set to `4`
|
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.
|
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.
|
|
||||||
|
|
||||||
**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)
|
### 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)
|
### 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.
|
Repeat test cases 1-6.
|
||||||
|
|
||||||
1. Test case 1 should fail
|
1. Test case 1 should fail
|
||||||
2. Test cases 2-6 should pass
|
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.
|
Repeat test cases 1-6, there is no pass/fail for this run.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ set(cmake_SOURCE_FILES
|
||||||
Copy3rdPartyLibs.cmake
|
Copy3rdPartyLibs.cmake
|
||||||
DBusGlib.cmake
|
DBusGlib.cmake
|
||||||
DeploySharedLibs.cmake
|
DeploySharedLibs.cmake
|
||||||
|
Discord.cmake
|
||||||
DragDrop.cmake
|
DragDrop.cmake
|
||||||
EXPAT.cmake
|
EXPAT.cmake
|
||||||
FindAutobuild.cmake
|
FindAutobuild.cmake
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,9 @@
|
||||||
|
|
||||||
include(CMakeCopyIfDifferent)
|
include(CMakeCopyIfDifferent)
|
||||||
include(Linking)
|
include(Linking)
|
||||||
|
if (USE_DISCORD)
|
||||||
|
include(Discord)
|
||||||
|
endif ()
|
||||||
include(OPENAL)
|
include(OPENAL)
|
||||||
|
|
||||||
# When we copy our dependent libraries, we almost always want to copy them to
|
# 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(ADDRESS_SIZE EQUAL 32)
|
||||||
endif (USE_BUGSPLAT)
|
endif (USE_BUGSPLAT)
|
||||||
|
|
||||||
|
if (TARGET ll::discord_sdk)
|
||||||
|
list(APPEND release_files discord_partner_sdk.dll)
|
||||||
|
endif ()
|
||||||
|
|
||||||
if (TARGET ll::openal)
|
if (TARGET ll::openal)
|
||||||
list(APPEND release_files openal32.dll alut.dll)
|
list(APPEND release_files openal32.dll alut.dll)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
@ -180,6 +187,10 @@ elseif(DARWIN)
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if (TARGET ll::discord_sdk)
|
||||||
|
list(APPEND release_files libdiscord_partner_sdk.dylib)
|
||||||
|
endif ()
|
||||||
|
|
||||||
if (TARGET ll::openal)
|
if (TARGET ll::openal)
|
||||||
list(APPEND release_files libalut.dylib libopenal.dylib)
|
list(APPEND release_files libalut.dylib libopenal.dylib)
|
||||||
endif ()
|
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
|
llavatarjoint.cpp
|
||||||
llavatarjointmesh.cpp
|
llavatarjointmesh.cpp
|
||||||
lldriverparam.cpp
|
lldriverparam.cpp
|
||||||
|
lljointdata.h
|
||||||
lllocaltextureobject.cpp
|
lllocaltextureobject.cpp
|
||||||
llpolyskeletaldistortion.cpp
|
llpolyskeletaldistortion.cpp
|
||||||
llpolymesh.cpp
|
llpolymesh.cpp
|
||||||
|
|
|
||||||
|
|
@ -29,16 +29,17 @@
|
||||||
#include "llavatarappearance.h"
|
#include "llavatarappearance.h"
|
||||||
#include "llavatarappearancedefines.h"
|
#include "llavatarappearancedefines.h"
|
||||||
#include "llavatarjointmesh.h"
|
#include "llavatarjointmesh.h"
|
||||||
|
#include "lljointdata.h"
|
||||||
#include "llstl.h"
|
#include "llstl.h"
|
||||||
#include "lldir.h"
|
#include "lldir.h"
|
||||||
#include "llpolymorph.h"
|
#include "llpolymorph.h"
|
||||||
#include "llpolymesh.h"
|
#include "llpolymesh.h"
|
||||||
#include "llpolyskeletaldistortion.h"
|
#include "llpolyskeletaldistortion.h"
|
||||||
#include "llstl.h"
|
|
||||||
#include "lltexglobalcolor.h"
|
#include "lltexglobalcolor.h"
|
||||||
#include "llwearabledata.h"
|
#include "llwearabledata.h"
|
||||||
#include "boost/bind.hpp"
|
#include "boost/bind.hpp"
|
||||||
#include "boost/tokenizer.hpp"
|
#include "boost/tokenizer.hpp"
|
||||||
|
#include "v4math.h"
|
||||||
|
|
||||||
using namespace LLAvatarAppearanceDefines;
|
using namespace LLAvatarAppearanceDefines;
|
||||||
|
|
||||||
|
|
@ -71,11 +72,13 @@ public:
|
||||||
mChildren.clear();
|
mChildren.clear();
|
||||||
}
|
}
|
||||||
bool parseXml(LLXmlTreeNode* node);
|
bool parseXml(LLXmlTreeNode* node);
|
||||||
|
glm::mat4 getJointMatrix();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string mName;
|
std::string mName;
|
||||||
std::string mSupport;
|
std::string mSupport;
|
||||||
std::string mAliases;
|
std::string mAliases;
|
||||||
|
std::string mGroup;
|
||||||
bool mIsJoint;
|
bool mIsJoint;
|
||||||
LLVector3 mPos;
|
LLVector3 mPos;
|
||||||
LLVector3 mEnd;
|
LLVector3 mEnd;
|
||||||
|
|
@ -105,11 +108,17 @@ public:
|
||||||
S32 getNumBones() const { return mNumBones; }
|
S32 getNumBones() const { return mNumBones; }
|
||||||
S32 getNumCollisionVolumes() const { return mNumCollisionVolumes; }
|
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:
|
private:
|
||||||
S32 mNumBones;
|
S32 mNumBones;
|
||||||
S32 mNumCollisionVolumes;
|
S32 mNumCollisionVolumes;
|
||||||
LLAvatarAppearance::joint_alias_map_t mJointAliasMap;
|
LLAvatarAppearance::joint_alias_map_t mJointAliasMap;
|
||||||
typedef std::vector<LLAvatarBoneInfo*> bone_info_list_t;
|
|
||||||
bone_info_list_t mBoneInfoList;
|
bone_info_list_t mBoneInfoList;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1598,6 +1607,15 @@ bool LLAvatarBoneInfo::parseXml(LLXmlTreeNode* node)
|
||||||
mSupport = "base";
|
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)
|
if (mIsJoint)
|
||||||
{
|
{
|
||||||
static LLStdStringHandle pivot_string = LLXmlTree::addAttributeString("pivot");
|
static LLStdStringHandle pivot_string = LLXmlTree::addAttributeString("pivot");
|
||||||
|
|
@ -1623,6 +1641,21 @@ bool LLAvatarBoneInfo::parseXml(LLXmlTreeNode* node)
|
||||||
return true;
|
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()
|
// LLAvatarSkeletonInfo::parseXml()
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
@ -1653,6 +1686,25 @@ bool LLAvatarSkeletonInfo::parseXml(LLXmlTreeNode* node)
|
||||||
return true;
|
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.
|
//Make aliases for joint and push to map.
|
||||||
void LLAvatarAppearance::makeJointAliases(LLAvatarBoneInfo *bone_info)
|
void LLAvatarAppearance::makeJointAliases(LLAvatarBoneInfo *bone_info)
|
||||||
{
|
{
|
||||||
|
|
@ -1714,6 +1766,16 @@ const LLAvatarAppearance::joint_alias_map_t& LLAvatarAppearance::getJointAliases
|
||||||
return mJointAliasMap;
|
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
|
// parseXmlSkeletonNode(): parses <skeleton> nodes from XML tree
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@
|
||||||
#include "lltexlayer.h"
|
#include "lltexlayer.h"
|
||||||
#include "llviewervisualparam.h"
|
#include "llviewervisualparam.h"
|
||||||
#include "llxmltree.h"
|
#include "llxmltree.h"
|
||||||
|
#include "v4math.h"
|
||||||
|
|
||||||
class LLTexLayerSet;
|
class LLTexLayerSet;
|
||||||
class LLTexGlobalColor;
|
class LLTexGlobalColor;
|
||||||
|
|
@ -41,6 +42,7 @@ class LLTexGlobalColorInfo;
|
||||||
class LLWearableData;
|
class LLWearableData;
|
||||||
class LLAvatarBoneInfo;
|
class LLAvatarBoneInfo;
|
||||||
class LLAvatarSkeletonInfo;
|
class LLAvatarSkeletonInfo;
|
||||||
|
class LLJointData;
|
||||||
|
|
||||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
// LLAvatarAppearance
|
// LLAvatarAppearance
|
||||||
|
|
@ -153,7 +155,9 @@ public:
|
||||||
const avatar_joint_list_t& getSkeleton() { return mSkeleton; }
|
const avatar_joint_list_t& getSkeleton() { return mSkeleton; }
|
||||||
typedef std::map<std::string, std::string, std::less<>> joint_alias_map_t;
|
typedef std::map<std::string, std::string, std::less<>> joint_alias_map_t;
|
||||||
const joint_alias_map_t& getJointAliases();
|
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:
|
protected:
|
||||||
static bool parseSkeletonFile(const std::string& filename, LLXmlTree& skeleton_xml_tree);
|
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())
|
if (!force_render && !hasMorph())
|
||||||
{
|
{
|
||||||
LL_DEBUGS() << "skipping renderMorphMasks for " << getUUID() << LL_ENDL;
|
LL_DEBUGS("Morph") << "skipping renderMorphMasks for " << getUUID() << LL_ENDL;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
LL_PROFILE_ZONE_SCOPED;
|
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 );
|
success &= param->render( x, y, width, height );
|
||||||
if (!success && !force_render)
|
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;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1365,7 +1365,7 @@ void LLTexLayer::renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLC
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LL_WARNS() << "Skipping rendering of " << getInfo()->mStaticImageFileName
|
LL_WARNS("Morph") << "Skipping rendering of " << getInfo()->mStaticImageFileName
|
||||||
<< "; expected 1 or 4 components." << LL_ENDL;
|
<< "; expected 1 or 4 components." << LL_ENDL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1404,8 +1404,8 @@ 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 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.
|
// 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
|
// clear out a slot if we have filled our cache
|
||||||
S32 max_cache_entries = getTexLayerSet()->getAvatarAppearance()->isSelf() ? 4 : 1;
|
S32 max_cache_entries = getTexLayerSet()->getAvatarAppearance()->isSelf() ? 4 : 1;
|
||||||
while ((S32)mAlphaCache.size() >= max_cache_entries)
|
while ((S32)mAlphaCache.size() >= max_cache_entries)
|
||||||
{
|
{
|
||||||
|
|
@ -1444,13 +1444,20 @@ 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);
|
glGetTexImage(LLTexUnit::getInternalType(LLTexUnit::TT_TEXTURE), 0, GL_RGBA, GL_UNSIGNED_BYTE, temp);
|
||||||
|
GLenum error = glGetError();
|
||||||
U8* alpha_cursor = alpha_data;
|
if (error != GL_NO_ERROR)
|
||||||
U8* pixel = temp;
|
|
||||||
for (int i = 0; i < pixels; i++)
|
|
||||||
{
|
{
|
||||||
*alpha_cursor++ = pixel[3];
|
LL_INFOS("Morph") << "GL Error while reading back morph texture. Error code: " << error << LL_ENDL;
|
||||||
pixel += 4;
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
U8* alpha_cursor = alpha_data;
|
||||||
|
U8* pixel = temp;
|
||||||
|
for (int i = 0; i < pixels; i++)
|
||||||
|
{
|
||||||
|
*alpha_cursor++ = pixel[3];
|
||||||
|
pixel += 4;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gGL.getTexUnit(0)->disable();
|
gGL.getTexUnit(0)->disable();
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@
|
||||||
|
|
||||||
#include "apr_portable.h"
|
#include "apr_portable.h"
|
||||||
|
|
||||||
|
#include "llapp.h"
|
||||||
#include "llthread.h"
|
#include "llthread.h"
|
||||||
#include "llmutex.h"
|
#include "llmutex.h"
|
||||||
|
|
||||||
|
|
@ -35,6 +36,7 @@
|
||||||
#include "lltrace.h"
|
#include "lltrace.h"
|
||||||
#include "lltracethreadrecorder.h"
|
#include "lltracethreadrecorder.h"
|
||||||
#include "llexception.h"
|
#include "llexception.h"
|
||||||
|
#include "workqueue.h"
|
||||||
|
|
||||||
#if LL_LINUX
|
#if LL_LINUX
|
||||||
#include <sched.h>
|
#include <sched.h>
|
||||||
|
|
@ -106,6 +108,27 @@ namespace
|
||||||
return s_thread_id;
|
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
|
} // anonymous namespace
|
||||||
|
|
||||||
LL_COMMON_API bool on_main_thread()
|
LL_COMMON_API bool on_main_thread()
|
||||||
|
|
@ -157,20 +180,11 @@ void LLThread::threadRun()
|
||||||
// Run the user supplied function
|
// Run the user supplied function
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
try
|
#ifdef LL_WINDOWS
|
||||||
{
|
sehHandle(); // Structured Exception Handling
|
||||||
run();
|
#else
|
||||||
}
|
tryRun();
|
||||||
catch (const LLContinueError &e)
|
#endif
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
} while (true);
|
} while (true);
|
||||||
|
|
@ -188,6 +202,69 @@ void LLThread::threadRun()
|
||||||
mStatus = STOPPED;
|
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) :
|
LLThread::LLThread(const std::string& name, apr_pool_t *poolp) :
|
||||||
mPaused(false),
|
mPaused(false),
|
||||||
mName(name),
|
mName(name),
|
||||||
|
|
|
||||||
|
|
@ -97,6 +97,11 @@ private:
|
||||||
|
|
||||||
// static function passed to APR thread creation routine
|
// static function passed to APR thread creation routine
|
||||||
void threadRun();
|
void threadRun();
|
||||||
|
void tryRun();
|
||||||
|
|
||||||
|
#ifdef LL_WINDOWS
|
||||||
|
void sehHandle();
|
||||||
|
#endif
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::string mName;
|
std::string mName;
|
||||||
|
|
|
||||||
|
|
@ -182,14 +182,22 @@ void LL::WorkQueueBase::callWork(const Work& work)
|
||||||
}
|
}
|
||||||
catch (...)
|
catch (...)
|
||||||
{
|
{
|
||||||
// Stash any other kind of uncaught exception to be rethrown by main thread.
|
if (getKey() != "mainloop")
|
||||||
LL_WARNS("LLCoros") << "Capturing and rethrowing uncaught exception in WorkQueueBase "
|
{
|
||||||
<< getKey() << LL_ENDL;
|
// Stash any other kind of uncaught exception to be rethrown by main thread.
|
||||||
|
LL_WARNS("LLCoros") << "Capturing and rethrowing uncaught exception in WorkQueueBase "
|
||||||
|
<< getKey() << LL_ENDL;
|
||||||
|
|
||||||
LL::WorkQueue::ptr_t main_queue = LL::WorkQueue::getInstance("mainloop");
|
LL::WorkQueue::ptr_t main_queue = LL::WorkQueue::getInstance("mainloop");
|
||||||
main_queue->post(
|
main_queue->post(
|
||||||
// Bind the current exception, rethrow it in main loop.
|
// Bind the current exception, rethrow it in main loop.
|
||||||
[exc = std::current_exception()]() { std::rethrow_exception(exc); });
|
[exc = std::current_exception()]() { std::rethrow_exception(exc); });
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// let main loop crash
|
||||||
|
throw;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif // else LL_WINDOWS
|
#endif // else LL_WINDOWS
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@ include(TinyGLTF)
|
||||||
|
|
||||||
set(llprimitive_SOURCE_FILES
|
set(llprimitive_SOURCE_FILES
|
||||||
lldaeloader.cpp
|
lldaeloader.cpp
|
||||||
llgltfloader.cpp
|
|
||||||
llgltfmaterial.cpp
|
llgltfmaterial.cpp
|
||||||
llmaterialid.cpp
|
llmaterialid.cpp
|
||||||
llmaterial.cpp
|
llmaterial.cpp
|
||||||
|
|
@ -32,7 +31,6 @@ set(llprimitive_SOURCE_FILES
|
||||||
set(llprimitive_HEADER_FILES
|
set(llprimitive_HEADER_FILES
|
||||||
CMakeLists.txt
|
CMakeLists.txt
|
||||||
lldaeloader.h
|
lldaeloader.h
|
||||||
llgltfloader.h
|
|
||||||
llgltfmaterial.h
|
llgltfmaterial.h
|
||||||
llgltfmaterial_templates.h
|
llgltfmaterial_templates.h
|
||||||
legacy_object_types.h
|
legacy_object_types.h
|
||||||
|
|
|
||||||
|
|
@ -204,12 +204,15 @@ LLModel::EModelStatus load_face_from_dom_triangles(
|
||||||
|
|
||||||
if (idx_stride <= 0
|
if (idx_stride <= 0
|
||||||
|| (pos_source && pos_offset >= idx_stride)
|
|| (pos_source && pos_offset >= idx_stride)
|
||||||
|
|| (pos_source && pos_offset < 0)
|
||||||
|| (tc_source && tc_offset >= idx_stride)
|
|| (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
|
// Looks like these offsets should fit inside idx_stride
|
||||||
// Might be good idea to also check idx.getCount()%idx_stride != 0
|
// 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;
|
return LLModel::BAD_ELEMENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -883,6 +886,7 @@ LLDAELoader::LLDAELoader(
|
||||||
std::map<std::string, std::string, std::less<>>& jointAliasMap,
|
std::map<std::string, std::string, std::less<>>& jointAliasMap,
|
||||||
U32 maxJointsPerMesh,
|
U32 maxJointsPerMesh,
|
||||||
U32 modelLimit,
|
U32 modelLimit,
|
||||||
|
U32 debugMode,
|
||||||
bool preprocess)
|
bool preprocess)
|
||||||
: LLModelLoader(
|
: LLModelLoader(
|
||||||
filename,
|
filename,
|
||||||
|
|
@ -895,8 +899,9 @@ LLDAELoader::LLDAELoader(
|
||||||
jointTransformMap,
|
jointTransformMap,
|
||||||
jointsFromNodes,
|
jointsFromNodes,
|
||||||
jointAliasMap,
|
jointAliasMap,
|
||||||
maxJointsPerMesh),
|
maxJointsPerMesh,
|
||||||
mGeneratedModelLimit(modelLimit),
|
modelLimit,
|
||||||
|
debugMode),
|
||||||
mPreprocessDAE(preprocess)
|
mPreprocessDAE(preprocess)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
@ -1680,6 +1685,7 @@ void LLDAELoader::processDomModel(LLModel* model, DAE* dae, daeElement* root, do
|
||||||
{
|
{
|
||||||
materials[model->mMaterialList[i]] = LLImportMaterial();
|
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));
|
mScene[transformation].push_back(LLModelInstance(model, model->mLabel, transformation, materials));
|
||||||
stretch_extents(model, transformation);
|
stretch_extents(model, transformation);
|
||||||
}
|
}
|
||||||
|
|
@ -2412,7 +2418,7 @@ std::string LLDAELoader::getElementLabel(daeElement *element)
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// 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))
|
if ((label.find("_LOD") != -1) || (label.find("_PHYS") != -1))
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,7 @@ public:
|
||||||
std::map<std::string, std::string, std::less<>>& jointAliasMap,
|
std::map<std::string, std::string, std::less<>>& jointAliasMap,
|
||||||
U32 maxJointsPerMesh,
|
U32 maxJointsPerMesh,
|
||||||
U32 modelLimit,
|
U32 modelLimit,
|
||||||
|
U32 debugMode,
|
||||||
bool preprocess);
|
bool preprocess);
|
||||||
virtual ~LLDAELoader() ;
|
virtual ~LLDAELoader() ;
|
||||||
|
|
||||||
|
|
@ -97,13 +98,12 @@ protected:
|
||||||
bool loadModelsFromDomMesh(domMesh* mesh, std::vector<LLModel*>& models_out, U32 submodel_limit);
|
bool loadModelsFromDomMesh(domMesh* mesh, std::vector<LLModel*>& models_out, U32 submodel_limit);
|
||||||
|
|
||||||
static std::string getElementLabel(daeElement *element);
|
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 getLodlessLabel(daeElement *element);
|
||||||
|
|
||||||
static std::string preprocessDAE(std::string filename);
|
static std::string preprocessDAE(std::string filename);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
U32 mGeneratedModelLimit; // Attempt to limit amount of generated submodels
|
|
||||||
bool mPreprocessDAE;
|
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
|
void LLModel::getNormalizedScaleTranslation(LLVector3& scale_out, LLVector3& translation_out) const
|
||||||
{
|
{
|
||||||
scale_out = mNormalizedScale;
|
scale_out = mNormalizedScale;
|
||||||
|
|
@ -662,7 +818,7 @@ LLSD LLModel::writeModel(
|
||||||
bool upload_skin,
|
bool upload_skin,
|
||||||
bool upload_joints,
|
bool upload_joints,
|
||||||
bool lock_scale_if_joint_position,
|
bool lock_scale_if_joint_position,
|
||||||
bool nowrite,
|
EWriteModelMode write_mode,
|
||||||
bool as_slm,
|
bool as_slm,
|
||||||
int submodel_id)
|
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;
|
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);
|
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];
|
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 j = 0; j < 4; j++)
|
||||||
{
|
{
|
||||||
for (U32 k = 0; k < 4; k++)
|
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)
|
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 j = 0; j < 4; j++)
|
||||||
{
|
{
|
||||||
for (U32 k = 0; k < 4; k++)
|
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 loadSkinInfo(LLSD& header, std::istream& is);
|
||||||
bool loadDecomposition(LLSD& header, std::istream& is);
|
bool loadDecomposition(LLSD& header, std::istream& is);
|
||||||
|
|
||||||
|
enum EWriteModelMode
|
||||||
|
{
|
||||||
|
WRITE_NO = 0,
|
||||||
|
WRITE_BINARY,
|
||||||
|
WRITE_HUMAN,
|
||||||
|
};
|
||||||
static LLSD writeModel(
|
static LLSD writeModel(
|
||||||
std::ostream& ostr,
|
std::ostream& ostr,
|
||||||
LLModel* physics,
|
LLModel* physics,
|
||||||
|
|
@ -171,14 +177,14 @@ public:
|
||||||
bool upload_skin,
|
bool upload_skin,
|
||||||
bool upload_joints,
|
bool upload_joints,
|
||||||
bool lock_scale_if_joint_position,
|
bool lock_scale_if_joint_position,
|
||||||
bool nowrite = false,
|
EWriteModelMode write_mode = WRITE_BINARY,
|
||||||
bool as_slm = false,
|
bool as_slm = false,
|
||||||
int submodel_id = 0);
|
int submodel_id = 0);
|
||||||
|
|
||||||
static LLSD writeModelToStream(
|
static LLSD writeModelToStream(
|
||||||
std::ostream& ostr,
|
std::ostream& ostr,
|
||||||
LLSD& mdl,
|
LLSD& mdl,
|
||||||
bool nowrite = false, bool as_slm = false);
|
EWriteModelMode write_mode = WRITE_BINARY, bool as_slm = false);
|
||||||
|
|
||||||
void ClearFacesAndMaterials() { mVolumeFaces.clear(); mMaterialList.clear(); }
|
void ClearFacesAndMaterials() { mVolumeFaces.clear(); mMaterialList.clear(); }
|
||||||
|
|
||||||
|
|
@ -202,6 +208,7 @@ public:
|
||||||
|
|
||||||
void sortVolumeFacesByMaterialName();
|
void sortVolumeFacesByMaterialName();
|
||||||
void normalizeVolumeFaces();
|
void normalizeVolumeFaces();
|
||||||
|
void normalizeVolumeFacesAndWeights();
|
||||||
void trimVolumeFacesToSize(U32 new_count = LL_SCULPT_MESH_MAX_FACES, LLVolume::face_list_t* remainder = NULL);
|
void trimVolumeFacesToSize(U32 new_count = LL_SCULPT_MESH_MAX_FACES, LLVolume::face_list_t* remainder = NULL);
|
||||||
void remapVolumeFaces();
|
void remapVolumeFaces();
|
||||||
void optimizeVolumeFaces();
|
void optimizeVolumeFaces();
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@
|
||||||
|
|
||||||
#include "llmatrix4a.h"
|
#include "llmatrix4a.h"
|
||||||
#include <boost/bind.hpp>
|
#include <boost/bind.hpp>
|
||||||
|
#include <boost/exception/diagnostic_information.hpp>
|
||||||
|
|
||||||
std::list<LLModelLoader*> LLModelLoader::sActiveLoaderList;
|
std::list<LLModelLoader*> LLModelLoader::sActiveLoaderList;
|
||||||
|
|
||||||
|
|
@ -113,7 +114,9 @@ LLModelLoader::LLModelLoader(
|
||||||
JointTransformMap& jointTransformMap,
|
JointTransformMap& jointTransformMap,
|
||||||
JointNameSet& jointsFromNodes,
|
JointNameSet& jointsFromNodes,
|
||||||
JointMap& legalJointNamesMap,
|
JointMap& legalJointNamesMap,
|
||||||
U32 maxJointsPerMesh)
|
U32 maxJointsPerMesh,
|
||||||
|
U32 modelLimit,
|
||||||
|
U32 debugMode)
|
||||||
: mJointList( jointTransformMap )
|
: mJointList( jointTransformMap )
|
||||||
, mJointsFromNode( jointsFromNodes )
|
, mJointsFromNode( jointsFromNodes )
|
||||||
, LLThread("Model Loader")
|
, LLThread("Model Loader")
|
||||||
|
|
@ -121,7 +124,6 @@ LLModelLoader::LLModelLoader(
|
||||||
, mLod(lod)
|
, mLod(lod)
|
||||||
, mTrySLM(false)
|
, mTrySLM(false)
|
||||||
, mFirstTransform(true)
|
, mFirstTransform(true)
|
||||||
, mNumOfFetchingTextures(0)
|
|
||||||
, mLoadCallback(load_cb)
|
, mLoadCallback(load_cb)
|
||||||
, mJointLookupFunc(joint_lookup_func)
|
, mJointLookupFunc(joint_lookup_func)
|
||||||
, mTextureLoadFunc(texture_load_func)
|
, mTextureLoadFunc(texture_load_func)
|
||||||
|
|
@ -132,7 +134,10 @@ LLModelLoader::LLModelLoader(
|
||||||
, mNoNormalize(false)
|
, mNoNormalize(false)
|
||||||
, mNoOptimize(false)
|
, mNoOptimize(false)
|
||||||
, mCacheOnlyHitIfRigged(false)
|
, mCacheOnlyHitIfRigged(false)
|
||||||
|
, mTexturesNeedScaling(false)
|
||||||
, mMaxJointsPerMesh(maxJointsPerMesh)
|
, mMaxJointsPerMesh(maxJointsPerMesh)
|
||||||
|
, mGeneratedModelLimit(modelLimit)
|
||||||
|
, mDebugMode(debugMode)
|
||||||
, mJointMap(legalJointNamesMap)
|
, mJointMap(legalJointNamesMap)
|
||||||
{
|
{
|
||||||
assert_main_thread();
|
assert_main_thread();
|
||||||
|
|
@ -149,7 +154,44 @@ LLModelLoader::~LLModelLoader()
|
||||||
void LLModelLoader::run()
|
void LLModelLoader::run()
|
||||||
{
|
{
|
||||||
mWarningsArray.clear();
|
mWarningsArray.clear();
|
||||||
doLoadModel();
|
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));
|
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)
|
void LLModelLoader::setLoadState(U32 state)
|
||||||
|
|
@ -466,6 +510,148 @@ bool LLModelLoader::isRigSuitableForJointPositionUpload( const std::vector<std::
|
||||||
return true;
|
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
|
//called in the main thread
|
||||||
void LLModelLoader::loadTextures()
|
void LLModelLoader::loadTextures()
|
||||||
|
|
@ -484,7 +670,7 @@ void LLModelLoader::loadTextures()
|
||||||
|
|
||||||
if(!material.mDiffuseMapFilename.empty())
|
if(!material.mDiffuseMapFilename.empty())
|
||||||
{
|
{
|
||||||
mNumOfFetchingTextures += mTextureLoadFunc(material, mOpaqueData);
|
mTextureLoadFunc(material, mOpaqueData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -109,8 +109,10 @@ public:
|
||||||
|
|
||||||
bool mTrySLM;
|
bool mTrySLM;
|
||||||
bool mCacheOnlyHitIfRigged; // ignore cached SLM if it does not contain rig info (and we want rig info)
|
bool mCacheOnlyHitIfRigged; // ignore cached SLM if it does not contain rig info (and we want rig info)
|
||||||
|
bool mTexturesNeedScaling;
|
||||||
|
|
||||||
model_list mModelList;
|
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;
|
scene mScene;
|
||||||
|
|
||||||
typedef std::queue<LLPointer<LLModel> > model_queue;
|
typedef std::queue<LLPointer<LLModel> > model_queue;
|
||||||
|
|
@ -119,10 +121,16 @@ public:
|
||||||
model_queue mPhysicsQ;
|
model_queue mPhysicsQ;
|
||||||
|
|
||||||
//map of avatar joints as named in COLLADA assets to internal joint names
|
//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;
|
JointMap mJointMap;
|
||||||
|
|
||||||
|
// The joint list is what you want to use to actually setup the specific joint transformations.
|
||||||
JointTransformMap& mJointList;
|
JointTransformMap& mJointList;
|
||||||
JointNameSet& mJointsFromNode;
|
JointNameSet& mJointsFromNode;
|
||||||
|
|
||||||
|
|
||||||
U32 mMaxJointsPerMesh;
|
U32 mMaxJointsPerMesh;
|
||||||
|
U32 mDebugMode; // see dumDebugData() for details
|
||||||
|
|
||||||
LLModelLoader(
|
LLModelLoader(
|
||||||
std::string filename,
|
std::string filename,
|
||||||
|
|
@ -135,7 +143,9 @@ public:
|
||||||
JointTransformMap& jointTransformMap,
|
JointTransformMap& jointTransformMap,
|
||||||
JointNameSet& jointsFromNodes,
|
JointNameSet& jointsFromNodes,
|
||||||
JointMap& legalJointNamesMap,
|
JointMap& legalJointNamesMap,
|
||||||
U32 maxJointsPerMesh);
|
U32 maxJointsPerMesh,
|
||||||
|
U32 modelLimit,
|
||||||
|
U32 debugMode);
|
||||||
virtual ~LLModelLoader();
|
virtual ~LLModelLoader();
|
||||||
|
|
||||||
virtual void setNoNormalize() { mNoNormalize = true; }
|
virtual void setNoNormalize() { mNoNormalize = true; }
|
||||||
|
|
@ -161,9 +171,6 @@ public:
|
||||||
|
|
||||||
void stretch_extents(const LLModel* model, const LLMatrix4& mat);
|
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 );
|
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)
|
//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; }
|
const LLSD logOut() const { return mWarningsArray; }
|
||||||
void clearLog() { mWarningsArray.clear(); }
|
void clearLog() { mWarningsArray.clear(); }
|
||||||
|
void dumpDebugData();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
|
|
@ -203,6 +211,7 @@ protected:
|
||||||
|
|
||||||
bool mRigValidJointUpload;
|
bool mRigValidJointUpload;
|
||||||
U32 mLegacyRigFlags;
|
U32 mLegacyRigFlags;
|
||||||
|
U32 mGeneratedModelLimit; // Attempt to limit amount of generated submodels
|
||||||
|
|
||||||
bool mNoNormalize;
|
bool mNoNormalize;
|
||||||
bool mNoOptimize;
|
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);
|
glGetTexLevelParameteriv(mTarget, gl_discard, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, (GLint*)&glbytes);
|
||||||
if(!imageraw->allocateDataSize(width, height, ncomponents, glbytes))
|
if(!imageraw->allocateDataSize(width, height, ncomponents, glbytes))
|
||||||
{
|
{
|
||||||
LL_WARNS() << "Memory allocation failed for reading back texture. Size is: " << glbytes << LL_ENDL ;
|
constexpr S64 MAX_GL_BYTES = 2048 * 2048;
|
||||||
LL_WARNS() << "width: " << width << "height: " << height << "components: " << ncomponents << LL_ENDL ;
|
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 ;
|
return false ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1882,8 +1891,18 @@ bool LLImageGL::readBackRaw(S32 discard_level, LLImageRaw* imageraw, bool compre
|
||||||
{
|
{
|
||||||
if(!imageraw->allocateDataSize(width, height, ncomponents))
|
if(!imageraw->allocateDataSize(width, height, ncomponents))
|
||||||
{
|
{
|
||||||
LL_WARNS() << "Memory allocation failed for reading back texture." << LL_ENDL ;
|
constexpr F32 MAX_IMAGE_SIZE = 2048 * 2048;
|
||||||
LL_WARNS() << "width: " << width << "height: " << height << "components: " << ncomponents << LL_ENDL ;
|
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 ;
|
return false ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,9 @@ include(CMakeCopyIfDifferent)
|
||||||
include(CubemapToEquirectangularJS)
|
include(CubemapToEquirectangularJS)
|
||||||
include(DBusGlib)
|
include(DBusGlib)
|
||||||
include(DragDrop)
|
include(DragDrop)
|
||||||
|
if (USE_DISCORD)
|
||||||
|
include(Discord)
|
||||||
|
endif ()
|
||||||
include(EXPAT)
|
include(EXPAT)
|
||||||
include(Hunspell)
|
include(Hunspell)
|
||||||
include(JPEGEncoderBasic)
|
include(JPEGEncoderBasic)
|
||||||
|
|
@ -76,6 +79,7 @@ set(viewer_SOURCE_FILES
|
||||||
gltf/accessor.cpp
|
gltf/accessor.cpp
|
||||||
gltf/primitive.cpp
|
gltf/primitive.cpp
|
||||||
gltf/animation.cpp
|
gltf/animation.cpp
|
||||||
|
gltf/llgltfloader.cpp
|
||||||
groupchatlistener.cpp
|
groupchatlistener.cpp
|
||||||
llaccountingcostmanager.cpp
|
llaccountingcostmanager.cpp
|
||||||
llaisapi.cpp
|
llaisapi.cpp
|
||||||
|
|
@ -178,11 +182,11 @@ set(viewer_SOURCE_FILES
|
||||||
llflexibleobject.cpp
|
llflexibleobject.cpp
|
||||||
llfloater360capture.cpp
|
llfloater360capture.cpp
|
||||||
llfloaterabout.cpp
|
llfloaterabout.cpp
|
||||||
|
llfloateravatarwelcomepack.cpp
|
||||||
llfloaterbvhpreview.cpp
|
llfloaterbvhpreview.cpp
|
||||||
llfloateraddpaymentmethod.cpp
|
llfloateraddpaymentmethod.cpp
|
||||||
llfloaterauction.cpp
|
llfloaterauction.cpp
|
||||||
llfloaterautoreplacesettings.cpp
|
llfloaterautoreplacesettings.cpp
|
||||||
llfloateravatar.cpp
|
|
||||||
llfloateravatarpicker.cpp
|
llfloateravatarpicker.cpp
|
||||||
llfloateravatarrendersettings.cpp
|
llfloateravatarrendersettings.cpp
|
||||||
llfloateravatartextures.cpp
|
llfloateravatartextures.cpp
|
||||||
|
|
@ -746,6 +750,7 @@ set(viewer_HEADER_FILES
|
||||||
gltf/buffer_util.h
|
gltf/buffer_util.h
|
||||||
gltf/primitive.h
|
gltf/primitive.h
|
||||||
gltf/animation.h
|
gltf/animation.h
|
||||||
|
gltf/llgltfloader.h
|
||||||
llaccountingcost.h
|
llaccountingcost.h
|
||||||
llaccountingcostmanager.h
|
llaccountingcostmanager.h
|
||||||
llaisapi.h
|
llaisapi.h
|
||||||
|
|
@ -849,11 +854,11 @@ set(viewer_HEADER_FILES
|
||||||
llflexibleobject.h
|
llflexibleobject.h
|
||||||
llfloater360capture.h
|
llfloater360capture.h
|
||||||
llfloaterabout.h
|
llfloaterabout.h
|
||||||
|
llfloateravatarwelcomepack.h
|
||||||
llfloaterbvhpreview.h
|
llfloaterbvhpreview.h
|
||||||
llfloateraddpaymentmethod.h
|
llfloateraddpaymentmethod.h
|
||||||
llfloaterauction.h
|
llfloaterauction.h
|
||||||
llfloaterautoreplacesettings.h
|
llfloaterautoreplacesettings.h
|
||||||
llfloateravatar.h
|
|
||||||
llfloateravatarpicker.h
|
llfloateravatarpicker.h
|
||||||
llfloateravatarrendersettings.h
|
llfloateravatarrendersettings.h
|
||||||
llfloateravatartextures.h
|
llfloateravatartextures.h
|
||||||
|
|
@ -1782,6 +1787,12 @@ if (WINDOWS)
|
||||||
)
|
)
|
||||||
endif (ADDRESS_SIZE EQUAL 64)
|
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)
|
if (TARGET ll::openal)
|
||||||
list(APPEND COPY_INPUT_DEPENDENCIES
|
list(APPEND COPY_INPUT_DEPENDENCIES
|
||||||
${SHARED_LIB_STAGING_DIR}/OpenAL32.dll
|
${SHARED_LIB_STAGING_DIR}/OpenAL32.dll
|
||||||
|
|
@ -1798,6 +1809,7 @@ if (WINDOWS)
|
||||||
--arch=${ARCH}
|
--arch=${ARCH}
|
||||||
--artwork=${ARTWORK_DIR}
|
--artwork=${ARTWORK_DIR}
|
||||||
"--bugsplat=${BUGSPLAT_DB}"
|
"--bugsplat=${BUGSPLAT_DB}"
|
||||||
|
"--discord=${USE_DISCORD}"
|
||||||
"--openal=${USE_OPENAL}"
|
"--openal=${USE_OPENAL}"
|
||||||
"--tracy=${USE_TRACY}"
|
"--tracy=${USE_TRACY}"
|
||||||
--build=${CMAKE_CURRENT_BINARY_DIR}
|
--build=${CMAKE_CURRENT_BINARY_DIR}
|
||||||
|
|
@ -1836,6 +1848,7 @@ if (WINDOWS)
|
||||||
--arch=${ARCH}
|
--arch=${ARCH}
|
||||||
--artwork=${ARTWORK_DIR}
|
--artwork=${ARTWORK_DIR}
|
||||||
"--bugsplat=${BUGSPLAT_DB}"
|
"--bugsplat=${BUGSPLAT_DB}"
|
||||||
|
"--discord=${USE_DISCORD}"
|
||||||
"--openal=${USE_OPENAL}"
|
"--openal=${USE_OPENAL}"
|
||||||
"--tracy=${USE_TRACY}"
|
"--tracy=${USE_TRACY}"
|
||||||
--build=${CMAKE_CURRENT_BINARY_DIR}
|
--build=${CMAKE_CURRENT_BINARY_DIR}
|
||||||
|
|
@ -1900,6 +1913,7 @@ if (WINDOWS)
|
||||||
--arch=${ARCH}
|
--arch=${ARCH}
|
||||||
--artwork=${ARTWORK_DIR}
|
--artwork=${ARTWORK_DIR}
|
||||||
"--bugsplat=${BUGSPLAT_DB}"
|
"--bugsplat=${BUGSPLAT_DB}"
|
||||||
|
"--discord=${USE_DISCORD}"
|
||||||
"--openal=${USE_OPENAL}"
|
"--openal=${USE_OPENAL}"
|
||||||
"--tracy=${USE_TRACY}"
|
"--tracy=${USE_TRACY}"
|
||||||
--build=${CMAKE_CURRENT_BINARY_DIR}
|
--build=${CMAKE_CURRENT_BINARY_DIR}
|
||||||
|
|
@ -1995,6 +2009,10 @@ target_link_libraries(${VIEWER_BINARY_NAME}
|
||||||
ll::openxr
|
ll::openxr
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (USE_DISCORD)
|
||||||
|
target_link_libraries(${VIEWER_BINARY_NAME} ll::discord_sdk )
|
||||||
|
endif ()
|
||||||
|
|
||||||
if( TARGET ll::intel_memops )
|
if( TARGET ll::intel_memops )
|
||||||
target_link_libraries(${VIEWER_BINARY_NAME} ll::intel_memops )
|
target_link_libraries(${VIEWER_BINARY_NAME} ll::intel_memops )
|
||||||
endif()
|
endif()
|
||||||
|
|
@ -2051,6 +2069,7 @@ if (LINUX)
|
||||||
--arch=${ARCH}
|
--arch=${ARCH}
|
||||||
--artwork=${ARTWORK_DIR}
|
--artwork=${ARTWORK_DIR}
|
||||||
"--bugsplat=${BUGSPLAT_DB}"
|
"--bugsplat=${BUGSPLAT_DB}"
|
||||||
|
"--discord=${USE_DISCORD}"
|
||||||
"--openal=${USE_OPENAL}"
|
"--openal=${USE_OPENAL}"
|
||||||
"--tracy=${USE_TRACY}"
|
"--tracy=${USE_TRACY}"
|
||||||
--build=${CMAKE_CURRENT_BINARY_DIR}
|
--build=${CMAKE_CURRENT_BINARY_DIR}
|
||||||
|
|
@ -2079,6 +2098,7 @@ if (LINUX)
|
||||||
--arch=${ARCH}
|
--arch=${ARCH}
|
||||||
--artwork=${ARTWORK_DIR}
|
--artwork=${ARTWORK_DIR}
|
||||||
"--bugsplat=${BUGSPLAT_DB}"
|
"--bugsplat=${BUGSPLAT_DB}"
|
||||||
|
"--discord=${USE_DISCORD}"
|
||||||
"--openal=${USE_OPENAL}"
|
"--openal=${USE_OPENAL}"
|
||||||
"--tracy=${USE_TRACY}"
|
"--tracy=${USE_TRACY}"
|
||||||
--build=${CMAKE_CURRENT_BINARY_DIR}
|
--build=${CMAKE_CURRENT_BINARY_DIR}
|
||||||
|
|
@ -2155,6 +2175,7 @@ if (DARWIN)
|
||||||
--arch=${ARCH}
|
--arch=${ARCH}
|
||||||
--artwork=${ARTWORK_DIR}
|
--artwork=${ARTWORK_DIR}
|
||||||
"--bugsplat=${BUGSPLAT_DB}"
|
"--bugsplat=${BUGSPLAT_DB}"
|
||||||
|
"--discord=${USE_DISCORD}"
|
||||||
"--openal=${USE_OPENAL}"
|
"--openal=${USE_OPENAL}"
|
||||||
"--tracy=${USE_TRACY}"
|
"--tracy=${USE_TRACY}"
|
||||||
--build=${CMAKE_CURRENT_BINARY_DIR}
|
--build=${CMAKE_CURRENT_BINARY_DIR}
|
||||||
|
|
@ -2190,6 +2211,7 @@ if (DARWIN)
|
||||||
--arch=${ARCH}
|
--arch=${ARCH}
|
||||||
--artwork=${ARTWORK_DIR}
|
--artwork=${ARTWORK_DIR}
|
||||||
"--bugsplat=${BUGSPLAT_DB}"
|
"--bugsplat=${BUGSPLAT_DB}"
|
||||||
|
"--discord=${USE_DISCORD}"
|
||||||
"--openal=${USE_OPENAL}"
|
"--openal=${USE_OPENAL}"
|
||||||
"--tracy=${USE_TRACY}"
|
"--tracy=${USE_TRACY}"
|
||||||
--build=${CMAKE_CURRENT_BINARY_DIR}
|
--build=${CMAKE_CURRENT_BINARY_DIR}
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
7.1.14
|
7.2.0
|
||||||
|
|
|
||||||
|
|
@ -26,9 +26,9 @@
|
||||||
label_ref="Command_Avatar_Label"
|
label_ref="Command_Avatar_Label"
|
||||||
tooltip_ref="Command_Avatar_Tooltip"
|
tooltip_ref="Command_Avatar_Tooltip"
|
||||||
execute_function="Floater.ToggleOrBringToFront"
|
execute_function="Floater.ToggleOrBringToFront"
|
||||||
execute_parameters="avatar"
|
execute_parameters="avatar_welcome_pack"
|
||||||
is_running_function="Floater.IsOpen"
|
is_running_function="Floater.IsOpen"
|
||||||
is_running_parameters="avatar"
|
is_running_parameters="avatar_welcome_pack"
|
||||||
/>
|
/>
|
||||||
<command name="build"
|
<command name="build"
|
||||||
available_in_toybox="true"
|
available_in_toybox="true"
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
<llsd xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
<llsd xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xsi:noNamespaceSchemaLocation="llsd.xsd">
|
xsi:noNamespaceSchemaLocation="llsd.xsd">
|
||||||
<map>
|
<map>
|
||||||
<key>ImporterDebug</key>
|
<key>ImporterDebugVerboseLogging</key>
|
||||||
<map>
|
<map>
|
||||||
<key>Comment</key>
|
<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>
|
<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>
|
<key>ImporterModelLimit</key>
|
||||||
<map>
|
<map>
|
||||||
<key>Comment</key>
|
<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>
|
<key>Persist</key>
|
||||||
<integer>1</integer>
|
<integer>1</integer>
|
||||||
<key>Type</key>
|
<key>Type</key>
|
||||||
|
|
@ -35,6 +35,17 @@
|
||||||
<key>Value</key>
|
<key>Value</key>
|
||||||
<integer>768</integer>
|
<integer>768</integer>
|
||||||
</map>
|
</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>
|
<key>ImporterPreprocessDAE</key>
|
||||||
<map>
|
<map>
|
||||||
<key>Comment</key>
|
<key>Comment</key>
|
||||||
|
|
@ -621,16 +632,16 @@
|
||||||
<key>Value</key>
|
<key>Value</key>
|
||||||
<real>16.0</real>
|
<real>16.0</real>
|
||||||
</map>
|
</map>
|
||||||
<key>AvatarPickerURL</key>
|
<key>AvatarWelcomePack</key>
|
||||||
<map>
|
<map>
|
||||||
<key>Comment</key>
|
<key>Comment</key>
|
||||||
<string>Avatar picker contents</string>
|
<string>Avatar Welcome Pack contents</string>
|
||||||
<key>Persist</key>
|
<key>Persist</key>
|
||||||
<integer>1</integer>
|
<integer>1</integer>
|
||||||
<key>Type</key>
|
<key>Type</key>
|
||||||
<string>String</string>
|
<string>String</string>
|
||||||
<key>Value</key>
|
<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>
|
</map>
|
||||||
<!--AvatarBakedTextureUploadTimeout is in use by QA-->
|
<!--AvatarBakedTextureUploadTimeout is in use by QA-->
|
||||||
<key>AvatarBakedTextureUploadTimeout</key>
|
<key>AvatarBakedTextureUploadTimeout</key>
|
||||||
|
|
@ -1139,6 +1150,39 @@
|
||||||
<key>Value</key>
|
<key>Value</key>
|
||||||
<integer>1</integer>
|
<integer>1</integer>
|
||||||
</map>
|
</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>
|
<key>EnableDiskCacheDebugInfo</key>
|
||||||
<map>
|
<map>
|
||||||
<key>Comment</key>
|
<key>Comment</key>
|
||||||
|
|
@ -16237,13 +16281,13 @@
|
||||||
<key>MediaFirstClickInteract</key>
|
<key>MediaFirstClickInteract</key>
|
||||||
<map>
|
<map>
|
||||||
<key>Comment</key>
|
<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>
|
<key>Persist</key>
|
||||||
<integer>1</integer>
|
<integer>1</integer>
|
||||||
<key>Type</key>
|
<key>Type</key>
|
||||||
<string>S32</string>
|
<string>S32</string>
|
||||||
<key>Value</key>
|
<key>Value</key>
|
||||||
<integer>1</integer>
|
<integer>31</integer>
|
||||||
</map>
|
</map>
|
||||||
<key>EnableSelectionHints</key>
|
<key>EnableSelectionHints</key>
|
||||||
<map>
|
<map>
|
||||||
|
|
|
||||||
|
|
@ -159,7 +159,7 @@ bool Buffer::prep(Asset& asset)
|
||||||
std::string dir = gDirUtilp->getDirName(asset.mFilename);
|
std::string dir = gDirUtilp->getDirName(asset.mFilename);
|
||||||
std::string bin_file = dir + gDirUtilp->getDirDelimiter() + mUri;
|
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())
|
if (!file.is_open())
|
||||||
{
|
{
|
||||||
LL_WARNS("GLTF") << "Failed to open file: " << bin_file << LL_ENDL;
|
LL_WARNS("GLTF") << "Failed to open file: " << bin_file << LL_ENDL;
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,10 @@ namespace LL
|
||||||
"KHR_texture_transform"
|
"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)
|
Material::AlphaMode gltf_alpha_mode_to_enum(const std::string& alpha_mode)
|
||||||
{
|
{
|
||||||
if (alpha_mode == "OPAQUE")
|
if (alpha_mode == "OPAQUE")
|
||||||
|
|
@ -472,11 +476,14 @@ void Asset::update()
|
||||||
|
|
||||||
for (auto& image : mImages)
|
for (auto& image : mImages)
|
||||||
{
|
{
|
||||||
if (image.mTexture.notNull())
|
if (image.mLoadIntoTexturePipe)
|
||||||
{ // HACK - force texture to be loaded full rez
|
{
|
||||||
// TODO: calculate actual vsize
|
if (image.mTexture.notNull())
|
||||||
image.mTexture->addTextureStats(2048.f * 2048.f);
|
{ // HACK - force texture to be loaded full rez
|
||||||
image.mTexture->setBoostLevel(LLViewerTexture::BOOST_HIGH);
|
// TODO: calculate actual vsize
|
||||||
|
image.mTexture->addTextureStats(2048.f * 2048.f);
|
||||||
|
image.mTexture->setBoostLevel(LLViewerTexture::BOOST_HIGH);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -486,18 +493,23 @@ void Asset::update()
|
||||||
bool Asset::prep()
|
bool Asset::prep()
|
||||||
{
|
{
|
||||||
LL_PROFILE_ZONE_SCOPED_CATEGORY_GLTF;
|
LL_PROFILE_ZONE_SCOPED_CATEGORY_GLTF;
|
||||||
// check required extensions and fail if not supported
|
// check required extensions
|
||||||
bool unsupported = false;
|
|
||||||
for (auto& extension : mExtensionsRequired)
|
for (auto& extension : mExtensionsRequired)
|
||||||
{
|
{
|
||||||
if (ExtensionsSupported.find(extension) == ExtensionsSupported.end())
|
if (ExtensionsSupported.find(extension) == ExtensionsSupported.end())
|
||||||
{
|
{
|
||||||
LL_WARNS() << "Unsupported extension: " << extension << LL_ENDL;
|
if (ExtensionsIgnored.find(extension) == ExtensionsIgnored.end())
|
||||||
unsupported = true;
|
{
|
||||||
|
LL_WARNS() << "Unsupported extension: " << extension << LL_ENDL;
|
||||||
|
mUnsupportedExtensions.push_back(extension);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mIgnoredExtensions.push_back(extension);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (mUnsupportedExtensions.size() > 0)
|
||||||
if (unsupported)
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -513,7 +525,7 @@ bool Asset::prep()
|
||||||
|
|
||||||
for (auto& image : mImages)
|
for (auto& image : mImages)
|
||||||
{
|
{
|
||||||
if (!image.prep(*this))
|
if (!image.prep(*this, mLoadIntoVRAM))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -542,102 +554,106 @@ bool Asset::prep()
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (mLoadIntoVRAM)
|
||||||
// prepare vertex buffers
|
|
||||||
|
|
||||||
// material count is number of materials + 1 for default material
|
|
||||||
U32 mat_count = (U32) mMaterials.size() + 1;
|
|
||||||
|
|
||||||
if (LLGLSLShader::sCurBoundShaderPtr == nullptr)
|
|
||||||
{ // make sure a shader is bound to satisfy mVertexBuffer->setBuffer
|
|
||||||
gDebugProgram.bind();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (S32 double_sided = 0; double_sided < 2; ++double_sided)
|
|
||||||
{
|
{
|
||||||
RenderData& rd = mRenderData[double_sided];
|
// prepare vertex buffers
|
||||||
for (U32 i = 0; i < LLGLSLShader::NUM_GLTF_VARIANTS; ++i)
|
|
||||||
{
|
// material count is number of materials + 1 for default material
|
||||||
rd.mBatches[i].resize(mat_count);
|
U32 mat_count = (U32) mMaterials.size() + 1;
|
||||||
|
|
||||||
|
if (LLGLSLShader::sCurBoundShaderPtr == nullptr)
|
||||||
|
{ // make sure a shader is bound to satisfy mVertexBuffer->setBuffer
|
||||||
|
gDebugProgram.bind();
|
||||||
}
|
}
|
||||||
|
|
||||||
// for each material
|
for (S32 double_sided = 0; double_sided < 2; ++double_sided)
|
||||||
for (S32 mat_id = -1; mat_id < (S32)mMaterials.size(); ++mat_id)
|
|
||||||
{
|
{
|
||||||
// for each shader variant
|
RenderData& rd = mRenderData[double_sided];
|
||||||
U32 vertex_count[LLGLSLShader::NUM_GLTF_VARIANTS] = { 0 };
|
for (U32 i = 0; i < LLGLSLShader::NUM_GLTF_VARIANTS; ++i)
|
||||||
U32 index_count[LLGLSLShader::NUM_GLTF_VARIANTS] = { 0 };
|
|
||||||
|
|
||||||
S32 ds_mat = mat_id == -1 ? 0 : mMaterials[mat_id].mDoubleSided;
|
|
||||||
if (ds_mat != double_sided)
|
|
||||||
{
|
{
|
||||||
continue;
|
rd.mBatches[i].resize(mat_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (U32 variant = 0; variant < LLGLSLShader::NUM_GLTF_VARIANTS; ++variant)
|
// for each material
|
||||||
|
for (S32 mat_id = -1; mat_id < (S32)mMaterials.size(); ++mat_id)
|
||||||
{
|
{
|
||||||
U32 attribute_mask = 0;
|
// for each shader variant
|
||||||
// for each mesh
|
U32 vertex_count[LLGLSLShader::NUM_GLTF_VARIANTS] = { 0 };
|
||||||
for (auto& mesh : mMeshes)
|
U32 index_count[LLGLSLShader::NUM_GLTF_VARIANTS] = { 0 };
|
||||||
|
|
||||||
|
S32 ds_mat = mat_id == -1 ? 0 : mMaterials[mat_id].mDoubleSided;
|
||||||
|
if (ds_mat != double_sided)
|
||||||
{
|
{
|
||||||
// for each primitive
|
continue;
|
||||||
for (auto& primitive : mesh.mPrimitives)
|
|
||||||
{
|
|
||||||
if (primitive.mMaterial == mat_id && primitive.mShaderVariant == variant)
|
|
||||||
{
|
|
||||||
// accumulate vertex and index counts
|
|
||||||
primitive.mVertexOffset = vertex_count[variant];
|
|
||||||
primitive.mIndexOffset = index_count[variant];
|
|
||||||
|
|
||||||
vertex_count[variant] += primitive.getVertexCount();
|
|
||||||
index_count[variant] += primitive.getIndexCount();
|
|
||||||
|
|
||||||
// all primitives of a given variant and material should all have the same attribute mask
|
|
||||||
llassert(attribute_mask == 0 || primitive.mAttributeMask == attribute_mask);
|
|
||||||
attribute_mask |= primitive.mAttributeMask;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// allocate vertex buffer and pack it
|
for (U32 variant = 0; variant < LLGLSLShader::NUM_GLTF_VARIANTS; ++variant)
|
||||||
if (vertex_count[variant] > 0)
|
|
||||||
{
|
{
|
||||||
U32 mat_idx = mat_id + 1;
|
U32 attribute_mask = 0;
|
||||||
LLVertexBuffer* vb = new LLVertexBuffer(attribute_mask);
|
// for each mesh
|
||||||
|
|
||||||
rd.mBatches[variant][mat_idx].mVertexBuffer = vb;
|
|
||||||
vb->allocateBuffer(vertex_count[variant],
|
|
||||||
index_count[variant] * 2); // hack double index count... TODO: find a better way to indicate 32-bit indices will be used
|
|
||||||
vb->setBuffer();
|
|
||||||
|
|
||||||
for (auto& mesh : mMeshes)
|
for (auto& mesh : mMeshes)
|
||||||
{
|
{
|
||||||
|
// for each primitive
|
||||||
for (auto& primitive : mesh.mPrimitives)
|
for (auto& primitive : mesh.mPrimitives)
|
||||||
{
|
{
|
||||||
if (primitive.mMaterial == mat_id && primitive.mShaderVariant == variant)
|
if (primitive.mMaterial == mat_id && primitive.mShaderVariant == variant)
|
||||||
{
|
{
|
||||||
primitive.upload(vb);
|
// accumulate vertex and index counts
|
||||||
|
primitive.mVertexOffset = vertex_count[variant];
|
||||||
|
primitive.mIndexOffset = index_count[variant];
|
||||||
|
|
||||||
|
vertex_count[variant] += primitive.getVertexCount();
|
||||||
|
index_count[variant] += primitive.getIndexCount();
|
||||||
|
|
||||||
|
// all primitives of a given variant and material should all have the same attribute mask
|
||||||
|
llassert(attribute_mask == 0 || primitive.mAttributeMask == attribute_mask);
|
||||||
|
attribute_mask |= primitive.mAttributeMask;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vb->unmapBuffer();
|
// allocate vertex buffer and pack it
|
||||||
|
if (vertex_count[variant] > 0)
|
||||||
|
{
|
||||||
|
U32 mat_idx = mat_id + 1;
|
||||||
|
#if 0
|
||||||
|
LLVertexBuffer* vb = new LLVertexBuffer(attribute_mask);
|
||||||
|
|
||||||
vb->unbind();
|
rd.mBatches[variant][mat_idx].mVertexBuffer = vb;
|
||||||
|
vb->allocateBuffer(vertex_count[variant],
|
||||||
|
index_count[variant] * 2); // hack double index count... TODO: find a better way to indicate 32-bit indices will be used
|
||||||
|
vb->setBuffer();
|
||||||
|
|
||||||
|
for (auto& mesh : mMeshes)
|
||||||
|
{
|
||||||
|
for (auto& primitive : mesh.mPrimitives)
|
||||||
|
{
|
||||||
|
if (primitive.mMaterial == mat_id && primitive.mShaderVariant == variant)
|
||||||
|
{
|
||||||
|
primitive.upload(vb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vb->unmapBuffer();
|
||||||
|
|
||||||
|
vb->unbind();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// sanity check that all primitives have a vertex buffer
|
// sanity check that all primitives have a vertex buffer
|
||||||
for (auto& mesh : mMeshes)
|
for (auto& mesh : mMeshes)
|
||||||
{
|
|
||||||
for (auto& primitive : mesh.mPrimitives)
|
|
||||||
{
|
{
|
||||||
llassert(primitive.mVertexBuffer.notNull());
|
for (auto& primitive : mesh.mPrimitives)
|
||||||
|
{
|
||||||
|
//llassert(primitive.mVertexBuffer.notNull());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if 0
|
||||||
// build render batches
|
// build render batches
|
||||||
for (S32 node_id = 0; node_id < mNodes.size(); ++node_id)
|
for (S32 node_id = 0; node_id < mNodes.size(); ++node_id)
|
||||||
{
|
{
|
||||||
|
|
@ -664,6 +680,7 @@ bool Asset::prep()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -672,13 +689,14 @@ Asset::Asset(const Value& src)
|
||||||
*this = 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;
|
LL_PROFILE_ZONE_SCOPED_CATEGORY_GLTF;
|
||||||
|
mLoadIntoVRAM = loadIntoVRAM;
|
||||||
mFilename = filename;
|
mFilename = filename;
|
||||||
std::string ext = gDirUtilp->getExtension(mFilename);
|
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())
|
if (file.is_open())
|
||||||
{
|
{
|
||||||
std::string str((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
|
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")
|
else if (ext == "glb")
|
||||||
{
|
{
|
||||||
return loadBinary(str);
|
return loadBinary(str, mLoadIntoVRAM);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -709,8 +727,9 @@ bool Asset::load(std::string_view filename)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Asset::loadBinary(const std::string& data)
|
bool Asset::loadBinary(const std::string& data, bool loadIntoVRAM)
|
||||||
{
|
{
|
||||||
|
mLoadIntoVRAM = loadIntoVRAM;
|
||||||
// load from binary gltf
|
// load from binary gltf
|
||||||
const U8* ptr = (const U8*)data.data();
|
const U8* ptr = (const U8*)data.data();
|
||||||
const U8* end = ptr + data.size();
|
const U8* end = ptr + data.size();
|
||||||
|
|
@ -935,8 +954,9 @@ void Asset::eraseBufferView(S32 bufferView)
|
||||||
|
|
||||||
LLViewerFetchedTexture* fetch_texture(const LLUUID& id);
|
LLViewerFetchedTexture* fetch_texture(const LLUUID& id);
|
||||||
|
|
||||||
bool Image::prep(Asset& asset)
|
bool Image::prep(Asset& asset, bool loadIntoVRAM)
|
||||||
{
|
{
|
||||||
|
mLoadIntoTexturePipe = loadIntoVRAM;
|
||||||
LLUUID id;
|
LLUUID id;
|
||||||
if (mUri.size() == UUID_STR_SIZE && LLUUID::parseUUID(mUri, &id) && id.notNull())
|
if (mUri.size() == UUID_STR_SIZE && LLUUID::parseUUID(mUri, &id) && id.notNull())
|
||||||
{ // loaded from an asset, fetch the texture from the asset system
|
{ // 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
|
{ // embedded in a buffer, load the texture from the buffer
|
||||||
BufferView& bufferView = asset.mBufferViews[mBufferView];
|
BufferView& bufferView = asset.mBufferViews[mBufferView];
|
||||||
Buffer& buffer = asset.mBuffers[bufferView.mBuffer];
|
Buffer& buffer = asset.mBuffers[bufferView.mBuffer];
|
||||||
|
if (mLoadIntoTexturePipe)
|
||||||
U8* data = buffer.mData.data() + bufferView.mByteOffset;
|
{
|
||||||
|
U8* data = buffer.mData.data() + bufferView.mByteOffset;
|
||||||
mTexture = LLViewerTextureManager::getFetchedTextureFromMemory(data, bufferView.mByteLength, mMimeType);
|
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") << "Failed to load image from buffer:" << LL_ENDL;
|
||||||
LL_WARNS("GLTF") << " image: " << mName << 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;
|
std::string img_file = dir + gDirUtilp->getDirDelimiter() + mUri;
|
||||||
|
|
||||||
LLUUID tracking_id = LLLocalBitmapMgr::getInstance()->addUnit(img_file);
|
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);
|
LLUUID world_id = LLLocalBitmapMgr::getInstance()->getWorldID(tracking_id);
|
||||||
mTexture = LLViewerTextureManager::getFetchedTexture(world_id);
|
mTexture = LLViewerTextureManager::getFetchedTexture(world_id);
|
||||||
}
|
}
|
||||||
else
|
else if (mLoadIntoTexturePipe)
|
||||||
{
|
{
|
||||||
LL_WARNS("GLTF") << "Failed to load image from file:" << LL_ENDL;
|
LL_WARNS("GLTF") << "Failed to load image from file:" << LL_ENDL;
|
||||||
LL_WARNS("GLTF") << " image: " << mName << LL_ENDL;
|
LL_WARNS("GLTF") << " image: " << mName << LL_ENDL;
|
||||||
|
|
@ -991,7 +1011,7 @@ bool Image::prep(Asset& asset)
|
||||||
return false;
|
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
|
{ // 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->setBoostLevel(LLViewerTexture::BOOST_PREVIEW);
|
||||||
mTexture->forceToSaveRawImage(0, F32_MAX);
|
mTexture->forceToSaveRawImage(0, F32_MAX);
|
||||||
|
|
|
||||||
|
|
@ -286,6 +286,7 @@ namespace LL
|
||||||
void serialize(boost::json::object& dst) const;
|
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
|
class Image
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
@ -301,6 +302,8 @@ namespace LL
|
||||||
S32 mBits = -1;
|
S32 mBits = -1;
|
||||||
S32 mPixelType = -1;
|
S32 mPixelType = -1;
|
||||||
|
|
||||||
|
bool mLoadIntoTexturePipe = false;
|
||||||
|
|
||||||
LLPointer<LLViewerFetchedTexture> mTexture;
|
LLPointer<LLViewerFetchedTexture> mTexture;
|
||||||
|
|
||||||
const Image& operator=(const Value& src);
|
const Image& operator=(const Value& src);
|
||||||
|
|
@ -316,7 +319,7 @@ namespace LL
|
||||||
// preserve only uri and name
|
// preserve only uri and name
|
||||||
void clearData(Asset& asset);
|
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
|
// Render Batch -- vertex buffer and list of primitives to render using
|
||||||
|
|
@ -391,6 +394,10 @@ namespace LL
|
||||||
|
|
||||||
// UBO for storing material data
|
// UBO for storing material data
|
||||||
U32 mMaterialsUBO = 0;
|
U32 mMaterialsUBO = 0;
|
||||||
|
bool mLoadIntoVRAM = false;
|
||||||
|
|
||||||
|
std::vector<std::string> mUnsupportedExtensions;
|
||||||
|
std::vector<std::string> mIgnoredExtensions;
|
||||||
|
|
||||||
// prepare for first time use
|
// prepare for first time use
|
||||||
bool prep();
|
bool prep();
|
||||||
|
|
@ -428,12 +435,12 @@ namespace LL
|
||||||
// accepts .gltf and .glb files
|
// accepts .gltf and .glb files
|
||||||
// Any existing data will be lost
|
// Any existing data will be lost
|
||||||
// returns result of prep() on success
|
// 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
|
// load .glb contents from memory
|
||||||
// data - binary contents of .glb file
|
// data - binary contents of .glb file
|
||||||
// returns result of prep() on success
|
// 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);
|
const Asset& operator=(const Value& src);
|
||||||
void serialize(boost::json::object& dst) const;
|
void serialize(boost::json::object& dst) const;
|
||||||
|
|
|
||||||
|
|
@ -158,6 +158,12 @@ namespace LL
|
||||||
dst.load3(src);
|
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<>
|
template<>
|
||||||
inline void copyVec3<U16, LLColor4U>(U16* src, LLColor4U& dst)
|
inline void copyVec3<U16, LLColor4U>(U16* src, LLColor4U& dst)
|
||||||
{
|
{
|
||||||
|
|
@ -369,6 +375,11 @@ namespace LL
|
||||||
template<class T>
|
template<class T>
|
||||||
inline void copy(Asset& asset, Accessor& accessor, LLStrider<T>& dst)
|
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 BufferView& bufferView = asset.mBufferViews[accessor.mBufferView];
|
||||||
const Buffer& buffer = asset.mBuffers[bufferView.mBuffer];
|
const Buffer& buffer = asset.mBuffers[bufferView.mBuffer];
|
||||||
const U8* src = buffer.mData.data() + bufferView.mByteOffset + accessor.mByteOffset;
|
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>();
|
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
|
gDebugProgram.bind(); // bind a shader to satisfy LLVertexBuffer assertions
|
||||||
asset->updateTransforms();
|
asset->updateTransforms();
|
||||||
|
|
|
||||||
|
|
@ -268,6 +268,16 @@ using namespace LL;
|
||||||
#include "glib.h"
|
#include "glib.h"
|
||||||
#endif // (LL_LINUX) && LL_GTK
|
#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);
|
static LLAppViewerListener sAppViewerListener(LLAppViewer::instance);
|
||||||
|
|
||||||
////// Windows-specific includes to the bottom - nasty defines in these pollute the preprocessor
|
////// Windows-specific includes to the bottom - nasty defines in these pollute the preprocessor
|
||||||
|
|
@ -1319,6 +1329,13 @@ bool LLAppViewer::frame()
|
||||||
|
|
||||||
bool LLAppViewer::doFrame()
|
bool LLAppViewer::doFrame()
|
||||||
{
|
{
|
||||||
|
#ifdef LL_DISCORD
|
||||||
|
{
|
||||||
|
LL_PROFILE_ZONE_NAMED("discord_callbacks");
|
||||||
|
discordpp::RunCallbacks();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
LL_RECORD_BLOCK_TIME(FTM_FRAME);
|
LL_RECORD_BLOCK_TIME(FTM_FRAME);
|
||||||
{
|
{
|
||||||
// and now adjust the visuals from previous 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
|
// Callback for LLError::LLUserWarningMsg
|
||||||
void errorHandler(const std::string& title_string, const std::string& message_string, S32 code)
|
void errorHandler(const std::string& title_string, const std::string& message_string, S32 code)
|
||||||
{
|
{
|
||||||
if (!message_string.empty())
|
// message is going to hang viewer, create marker first
|
||||||
{
|
|
||||||
OSMessageBox(message_string, title_string.empty() ? LLTrans::getString("MBFatalError") : title_string, OSMB_OK);
|
|
||||||
}
|
|
||||||
switch (code)
|
switch (code)
|
||||||
{
|
{
|
||||||
case LLError::LLUserWarningMsg::ERROR_OTHER:
|
case LLError::LLUserWarningMsg::ERROR_OTHER:
|
||||||
|
|
@ -2256,6 +2270,10 @@ void errorHandler(const std::string& title_string, const std::string& message_st
|
||||||
break;
|
break;
|
||||||
case LLError::LLUserWarningMsg::ERROR_BAD_ALLOC:
|
case LLError::LLUserWarningMsg::ERROR_BAD_ALLOC:
|
||||||
LLAppViewer::instance()->createErrorMarker(LAST_EXEC_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;
|
break;
|
||||||
case LLError::LLUserWarningMsg::ERROR_MISSING_FILES:
|
case LLError::LLUserWarningMsg::ERROR_MISSING_FILES:
|
||||||
LLAppViewer::instance()->createErrorMarker(LAST_EXEC_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:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (!message_string.empty())
|
||||||
|
{
|
||||||
|
OSMessageBox(message_string, title_string.empty() ? LLTrans::getString("MBFatalError") : title_string, OSMB_OK);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LLAppViewer::initLoggingAndGetLastDuration()
|
void LLAppViewer::initLoggingAndGetLastDuration()
|
||||||
|
|
@ -5687,6 +5709,28 @@ void LLAppViewer::forceErrorThreadCrash()
|
||||||
thread->start();
|
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)
|
void LLAppViewer::initMainloopTimeout(std::string_view state, F32 secs)
|
||||||
{
|
{
|
||||||
if (!mMainloopTimeout)
|
if (!mMainloopTimeout)
|
||||||
|
|
@ -5862,3 +5906,180 @@ void LLAppViewer::metricsSend(bool enable_reporting)
|
||||||
gViewerAssetStats->restart();
|
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 forceErrorCoroprocedureCrash();
|
||||||
virtual void forceErrorWorkQueueCrash();
|
virtual void forceErrorWorkQueueCrash();
|
||||||
virtual void forceErrorThreadCrash();
|
virtual void forceErrorThreadCrash();
|
||||||
|
virtual void forceExceptionThreadCrash();
|
||||||
|
|
||||||
// The list is found in app_settings/settings_files.xml
|
// The list is found in app_settings/settings_files.xml
|
||||||
// but since they are used explicitly in code,
|
// but since they are used explicitly in code,
|
||||||
|
|
@ -250,6 +251,14 @@ public:
|
||||||
// Note: mQuitRequested can be aborted by user.
|
// Note: mQuitRequested can be aborted by user.
|
||||||
void outOfMemorySoftQuit();
|
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:
|
protected:
|
||||||
virtual bool initWindow(); // Initialize the viewer's window.
|
virtual bool initWindow(); // Initialize the viewer's window.
|
||||||
virtual void initLoggingAndGetLastDuration(); // Initialize log files, logging system
|
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 XML_FILTER L"XML files (*.xml)\0*.xml\0"
|
||||||
#define SLOBJECT_FILTER L"Objects (*.slobject)\0*.slobject\0"
|
#define SLOBJECT_FILTER L"Objects (*.slobject)\0*.slobject\0"
|
||||||
#define RAW_FILTER L"RAW files (*.raw)\0*.raw\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 MATERIAL_FILTER L"GLTF Files (*.gltf; *.glb)\0*.gltf;*.glb\0"
|
||||||
#define HDRI_FILTER L"HDRI Files (*.exr)\0*.exr\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"
|
#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;
|
break;
|
||||||
case FFLOAD_MODEL:
|
case FFLOAD_MODEL:
|
||||||
mOFN.lpstrFilter = MODEL_FILTER \
|
mOFN.lpstrFilter = MODEL_FILTER \
|
||||||
|
COLLADA_FILTER \
|
||||||
|
MATERIAL_FILTER \
|
||||||
L"\0";
|
L"\0";
|
||||||
break;
|
break;
|
||||||
case FFLOAD_MATERIAL:
|
case FFLOAD_MATERIAL:
|
||||||
|
|
@ -671,6 +673,8 @@ std::unique_ptr<std::vector<std::string>> LLFilePicker::navOpenFilterProc(ELoadF
|
||||||
case FFLOAD_HDRI:
|
case FFLOAD_HDRI:
|
||||||
allowedv->push_back("exr");
|
allowedv->push_back("exr");
|
||||||
case FFLOAD_MODEL:
|
case FFLOAD_MODEL:
|
||||||
|
allowedv->push_back("gltf");
|
||||||
|
allowedv->push_back("glb");
|
||||||
case FFLOAD_COLLADA:
|
case FFLOAD_COLLADA:
|
||||||
allowedv->push_back("dae");
|
allowedv->push_back("dae");
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
/**
|
/**
|
||||||
* @file llfloateravatar.h
|
* @file llfloateravatarwelcomepack.cpp
|
||||||
* @author Leyla Farazha
|
* @author Callum Prentice (callum@lindenlab.com)
|
||||||
* @brief floater for the avatar changer
|
* @brief Floater container for the Avatar Welcome Pack we app
|
||||||
*
|
*
|
||||||
* $LicenseInfo:firstyear=2011&license=viewerlgpl$
|
* $LicenseInfo:firstyear=2011&license=viewerlgpl$
|
||||||
* Second Life Viewer Source Code
|
* Second Life Viewer Source Code
|
||||||
|
|
@ -27,17 +27,16 @@
|
||||||
|
|
||||||
#include "llviewerprecompiledheaders.h"
|
#include "llviewerprecompiledheaders.h"
|
||||||
|
|
||||||
#include "llfloateravatar.h"
|
#include "llfloateravatarwelcomepack.h"
|
||||||
#include "lluictrlfactory.h"
|
#include "lluictrlfactory.h"
|
||||||
#include "llmediactrl.h"
|
#include "llmediactrl.h"
|
||||||
|
|
||||||
|
LLFloaterAvatarWelcomePack::LLFloaterAvatarWelcomePack(const LLSD& key)
|
||||||
LLFloaterAvatar::LLFloaterAvatar(const LLSD& key)
|
|
||||||
: LLFloater(key)
|
: LLFloater(key)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
LLFloaterAvatar::~LLFloaterAvatar()
|
LLFloaterAvatarWelcomePack::~LLFloaterAvatarWelcomePack()
|
||||||
{
|
{
|
||||||
if (mAvatarPicker)
|
if (mAvatarPicker)
|
||||||
{
|
{
|
||||||
|
|
@ -47,15 +46,13 @@ LLFloaterAvatar::~LLFloaterAvatar()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LLFloaterAvatar::postBuild()
|
bool LLFloaterAvatarWelcomePack::postBuild()
|
||||||
{
|
{
|
||||||
mAvatarPicker = findChild<LLMediaCtrl>("avatar_picker_contents");
|
mAvatarPicker = findChild<LLMediaCtrl>("avatar_picker_contents");
|
||||||
if (mAvatarPicker)
|
if (mAvatarPicker)
|
||||||
{
|
{
|
||||||
mAvatarPicker->clearCache();
|
mAvatarPicker->clearCache();
|
||||||
}
|
}
|
||||||
enableResizeCtrls(true, true, false);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
/**
|
/**
|
||||||
* @file llfloateravatar.h
|
* @file llfloateravatarwelcomepack.h
|
||||||
* @author Leyla Farazha
|
* @author Callum Prentice (callum@lindenlab.com)
|
||||||
* @brief floater for the avatar changer
|
* @brief Floater container for the Avatar Welcome Pack we app
|
||||||
*
|
*
|
||||||
* $LicenseInfo:firstyear=2011&license=viewerlgpl$
|
* $LicenseInfo:firstyear=2011&license=viewerlgpl$
|
||||||
* Second Life Viewer Source Code
|
* Second Life Viewer Source Code
|
||||||
|
|
@ -25,22 +25,21 @@
|
||||||
* $/LicenseInfo$
|
* $/LicenseInfo$
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef LL_FLOATER_AVATAR_H
|
#pragma once
|
||||||
#define LL_FLOATER_AVATAR_H
|
|
||||||
|
|
||||||
#include "llfloater.h"
|
#include "llfloater.h"
|
||||||
|
|
||||||
class LLMediaCtrl;
|
class LLMediaCtrl;
|
||||||
|
|
||||||
class LLFloaterAvatar:
|
class LLFloaterAvatarWelcomePack:
|
||||||
public LLFloater
|
public LLFloater
|
||||||
{
|
{
|
||||||
friend class LLFloaterReg;
|
friend class LLFloaterReg;
|
||||||
private:
|
|
||||||
LLFloaterAvatar(const LLSD& key);
|
|
||||||
~LLFloaterAvatar();
|
|
||||||
bool postBuild() override;
|
|
||||||
|
|
||||||
LLMediaCtrl* mAvatarPicker;
|
private:
|
||||||
|
LLFloaterAvatarWelcomePack(const LLSD& key);
|
||||||
|
~LLFloaterAvatarWelcomePack();
|
||||||
|
bool postBuild() override;
|
||||||
|
|
||||||
|
LLMediaCtrl* mAvatarPicker;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -64,6 +64,7 @@
|
||||||
#include "llcallbacklist.h"
|
#include "llcallbacklist.h"
|
||||||
#include "llviewertexteditor.h"
|
#include "llviewertexteditor.h"
|
||||||
#include "llviewernetwork.h"
|
#include "llviewernetwork.h"
|
||||||
|
#include "llmaterialeditor.h"
|
||||||
|
|
||||||
|
|
||||||
//static
|
//static
|
||||||
|
|
@ -164,7 +165,7 @@ bool LLFloaterModelPreview::postBuild()
|
||||||
for (S32 lod = 0; lod <= LLModel::LOD_HIGH; ++lod)
|
for (S32 lod = 0; lod <= LLModel::LOD_HIGH; ++lod)
|
||||||
{
|
{
|
||||||
LLComboBox* lod_source_combo = getChild<LLComboBox>("lod_source_" + lod_name[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]);
|
lod_source_combo->setCurrentByIndex(mLODMode[lod]);
|
||||||
|
|
||||||
getChild<LLButton>("lod_browse_" + lod_name[lod])->setCommitCallback(boost::bind(&LLFloaterModelPreview::onBrowseLOD, this, 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");
|
LLPanel *panel = mTabContainer->getPanelByName("rigging_panel");
|
||||||
LLScrollListCtrl *joints_list = panel->getChild<LLScrollListCtrl>("joints_list");
|
LLScrollListCtrl *joints_list = panel->getChild<LLScrollListCtrl>("joints_list");
|
||||||
LLScrollListCtrl *joints_pos = panel->getChild<LLScrollListCtrl>("pos_overrides_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");
|
LLTextBox *joint_pos_descr = panel->getChild<LLTextBox>("pos_overrides_descr");
|
||||||
|
|
||||||
joints_pos->deleteAllItems();
|
joints_pos->deleteAllItems();
|
||||||
joints_scale->deleteAllItems();
|
|
||||||
|
|
||||||
LLScrollListItem *selected = joints_list->getFirstSelected();
|
LLScrollListItem *selected = joints_list->getFirstSelected();
|
||||||
if (selected)
|
if (selected)
|
||||||
|
|
@ -757,7 +756,7 @@ void LLFloaterModelPreview::onLODParamCommit(S32 lod, bool enforce_tri_limit)
|
||||||
mModelPreview->onLODMeshOptimizerParamCommit(lod, enforce_tri_limit, mode);
|
mModelPreview->onLODMeshOptimizerParamCommit(lod, enforce_tri_limit, mode);
|
||||||
break;
|
break;
|
||||||
default:
|
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;
|
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]);
|
LLComboBox* lod_source_combo = getChild<LLComboBox>("lod_source_" + lod_name[i]);
|
||||||
if (lod_source_combo->getCurrentIndex() == LLModelPreview::USE_LOD_ABOVE)
|
if (lod_source_combo->getCurrentIndex() == LLModelPreview::USE_LOD_ABOVE)
|
||||||
{
|
{
|
||||||
onLoDSourceCommit(i);
|
onLoDSourceCommit(i, false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -1341,26 +1340,26 @@ void LLFloaterModelPreview::addStringToLog(const std::string& message, const LLS
|
||||||
{
|
{
|
||||||
std::string str;
|
std::string str;
|
||||||
switch (lod)
|
switch (lod)
|
||||||
{
|
{
|
||||||
case LLModel::LOD_IMPOSTOR: str = "LOD0 "; break;
|
case LLModel::LOD_IMPOSTOR: str = "LOD0 "; break;
|
||||||
case LLModel::LOD_LOW: str = "LOD1 "; break;
|
case LLModel::LOD_LOW: str = "LOD1 "; break;
|
||||||
case LLModel::LOD_MEDIUM: str = "LOD2 "; break;
|
case LLModel::LOD_MEDIUM: str = "LOD2 "; break;
|
||||||
case LLModel::LOD_PHYSICS: str = "PHYS "; break;
|
case LLModel::LOD_PHYSICS: str = "PHYS "; break;
|
||||||
case LLModel::LOD_HIGH: str = "LOD3 "; break;
|
case LLModel::LOD_HIGH: str = "LOD3 "; break;
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
|
|
||||||
LLStringUtil::format_map_t args_msg;
|
LLStringUtil::format_map_t args_msg;
|
||||||
LLSD::map_const_iterator iter = args.beginMap();
|
LLSD::map_const_iterator iter = args.beginMap();
|
||||||
LLSD::map_const_iterator end = args.endMap();
|
LLSD::map_const_iterator end = args.endMap();
|
||||||
for (; iter != end; ++iter)
|
for (; iter != end; ++iter)
|
||||||
{
|
{
|
||||||
args_msg[iter->first] = iter->second.asString();
|
args_msg[iter->first] = iter->second.asString();
|
||||||
}
|
}
|
||||||
str += sInstance->getString(message, args_msg);
|
str += sInstance->getString(message, args_msg);
|
||||||
sInstance->addStringToLogTab(str, flash);
|
sInstance->addStringToLogTab(str, flash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
void LLFloaterModelPreview::addStringToLog(const std::string& str, bool flash)
|
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);
|
mModelPreview->updateLodControls(lod);
|
||||||
|
|
||||||
|
|
@ -1770,9 +1769,17 @@ void LLFloaterModelPreview::onLoDSourceCommit(S32 lod)
|
||||||
if (index == LLModelPreview::MESH_OPTIMIZER_AUTO
|
if (index == LLModelPreview::MESH_OPTIMIZER_AUTO
|
||||||
|| index == LLModelPreview::MESH_OPTIMIZER_SLOPPY
|
|| index == LLModelPreview::MESH_OPTIMIZER_SLOPPY
|
||||||
|| index == LLModelPreview::MESH_OPTIMIZER_PRECISE)
|
|| index == LLModelPreview::MESH_OPTIMIZER_PRECISE)
|
||||||
{ //rebuild LoD to update triangle counts
|
{
|
||||||
|
// rebuild LoD to update triangle counts
|
||||||
onLODParamCommit(lod, true);
|
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()
|
void LLFloaterModelPreview::resetDisplayOptions()
|
||||||
|
|
|
||||||
|
|
@ -208,7 +208,7 @@ private:
|
||||||
void onClickCalculateBtn();
|
void onClickCalculateBtn();
|
||||||
void onJointListSelection();
|
void onJointListSelection();
|
||||||
|
|
||||||
void onLoDSourceCommit(S32 lod);
|
void onLoDSourceCommit(S32 lod, bool refresh_ui);
|
||||||
|
|
||||||
void modelUpdated(bool calculate_visible);
|
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.ClearLog", boost::bind(&LLConversationLog::onClearLog, &LLConversationLog::instance()));
|
||||||
mCommitCallbackRegistrar.add("Pref.DeleteTranscripts", boost::bind(&LLFloaterPreference::onDeleteTranscripts, this));
|
mCommitCallbackRegistrar.add("Pref.DeleteTranscripts", boost::bind(&LLFloaterPreference::onDeleteTranscripts, this));
|
||||||
mCommitCallbackRegistrar.add("UpdateFilter", boost::bind(&LLFloaterPreference::onUpdateFilterTerm, this, false)); // <FS:ND/> Hook up for filtering
|
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 )
|
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);
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -371,6 +371,7 @@ LLFloaterWorldMap::LLFloaterWorldMap(const LLSD& key)
|
||||||
mWaitingForTracker(false),
|
mWaitingForTracker(false),
|
||||||
mIsClosing(false),
|
mIsClosing(false),
|
||||||
mSetToUserPosition(true),
|
mSetToUserPosition(true),
|
||||||
|
mProcessingSearchUpdate(false),
|
||||||
mTrackedLocation(0.0,0.0,0.0),
|
mTrackedLocation(0.0,0.0,0.0),
|
||||||
mTrackedStatus(LLTracker::TRACKING_NOTHING),
|
mTrackedStatus(LLTracker::TRACKING_NOTHING),
|
||||||
mParcelInfoObserver(nullptr),
|
mParcelInfoObserver(nullptr),
|
||||||
|
|
@ -384,7 +385,7 @@ LLFloaterWorldMap::LLFloaterWorldMap(const LLSD& key)
|
||||||
mCommitCallbackRegistrar.add("WMap.Location", boost::bind(&LLFloaterWorldMap::onLocationCommit, this));
|
mCommitCallbackRegistrar.add("WMap.Location", boost::bind(&LLFloaterWorldMap::onLocationCommit, this));
|
||||||
mCommitCallbackRegistrar.add("WMap.AvatarCombo", boost::bind(&LLFloaterWorldMap::onAvatarComboCommit, this));
|
mCommitCallbackRegistrar.add("WMap.AvatarCombo", boost::bind(&LLFloaterWorldMap::onAvatarComboCommit, this));
|
||||||
mCommitCallbackRegistrar.add("WMap.Landmark", boost::bind(&LLFloaterWorldMap::onLandmarkComboCommit, 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.GoHome", boost::bind(&LLFloaterWorldMap::onGoHome, this));
|
||||||
mCommitCallbackRegistrar.add("WMap.Teleport", boost::bind(&LLFloaterWorldMap::onClickTeleportBtn, this));
|
mCommitCallbackRegistrar.add("WMap.Teleport", boost::bind(&LLFloaterWorldMap::onClickTeleportBtn, this));
|
||||||
mCommitCallbackRegistrar.add("WMap.ShowTarget", boost::bind(&LLFloaterWorldMap::onShowTargetBtn, 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)
|
void LLFloaterWorldMap::trackLocation(const LLVector3d& pos_global)
|
||||||
{
|
{
|
||||||
|
mProcessingSearchUpdate = false;
|
||||||
LLSimInfo* sim_info = LLWorldMap::getInstance()->simInfoFromPosGlobal(pos_global);
|
LLSimInfo* sim_info = LLWorldMap::getInstance()->simInfoFromPosGlobal(pos_global);
|
||||||
if (!sim_info)
|
if (!sim_info)
|
||||||
{
|
{
|
||||||
|
|
@ -968,7 +970,10 @@ void LLFloaterWorldMap::updateLocation()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mLocationEditor->setValue(sim_name);
|
if (!mProcessingSearchUpdate)
|
||||||
|
{
|
||||||
|
mLocationEditor->setValue(sim_name);
|
||||||
|
}
|
||||||
|
|
||||||
// refresh coordinate display to reflect where user clicked.
|
// refresh coordinate display to reflect where user clicked.
|
||||||
LLVector3d coord_pos = LLTracker::getTrackedPositionGlobal();
|
LLVector3d coord_pos = LLTracker::getTrackedPositionGlobal();
|
||||||
|
|
@ -1242,6 +1247,7 @@ void LLFloaterWorldMap::onGoHome()
|
||||||
{
|
{
|
||||||
gAgent.teleportHome();
|
gAgent.teleportHome();
|
||||||
closeFloater();
|
closeFloater();
|
||||||
|
mProcessingSearchUpdate = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1411,6 +1417,7 @@ void LLFloaterWorldMap::onLocationCommit()
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
mProcessingSearchUpdate = true;
|
||||||
|
|
||||||
LLStringUtil::toLower(str);
|
LLStringUtil::toLower(str);
|
||||||
mCompletingRegionName = str;
|
mCompletingRegionName = str;
|
||||||
|
|
@ -1432,6 +1439,7 @@ void LLFloaterWorldMap::onCoordinatesCommit()
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
mProcessingSearchUpdate = false;
|
||||||
|
|
||||||
S32 x_coord = (S32)mTeleportCoordSpinX->getValue().asReal();
|
S32 x_coord = (S32)mTeleportCoordSpinX->getValue().asReal();
|
||||||
S32 y_coord = (S32)mTeleportCoordSpinY->getValue().asReal();
|
S32 y_coord = (S32)mTeleportCoordSpinY->getValue().asReal();
|
||||||
|
|
@ -1445,6 +1453,7 @@ void LLFloaterWorldMap::onCoordinatesCommit()
|
||||||
void LLFloaterWorldMap::onClearBtn()
|
void LLFloaterWorldMap::onClearBtn()
|
||||||
{
|
{
|
||||||
mTrackedStatus = LLTracker::TRACKING_NOTHING;
|
mTrackedStatus = LLTracker::TRACKING_NOTHING;
|
||||||
|
mProcessingSearchUpdate = false;
|
||||||
LLTracker::stopTracking(true);
|
LLTracker::stopTracking(true);
|
||||||
LLWorldMap::getInstance()->cancelTracking();
|
LLWorldMap::getInstance()->cancelTracking();
|
||||||
mSLURL = LLSLURL(); // Clear the SLURL since it's invalid
|
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
|
mMapView->setPanWithInterpTime(0, 0, false, 0.1f); // false == animate
|
||||||
// Set flag so user's location will be displayed if not tracking anything else
|
// Set flag so user's location will be displayed if not tracking anything else
|
||||||
mSetToUserPosition = true;
|
mSetToUserPosition = true;
|
||||||
|
mProcessingSearchUpdate = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LLFloaterWorldMap::onClickTeleportBtn()
|
void LLFloaterWorldMap::onClickTeleportBtn()
|
||||||
|
|
@ -1616,6 +1626,12 @@ void LLFloaterWorldMap::teleport()
|
||||||
gAgent.teleportViaLocation( pos_global );
|
gAgent.teleportViaLocation( pos_global );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mProcessingSearchUpdate)
|
||||||
|
{
|
||||||
|
mProcessingSearchUpdate = false;
|
||||||
|
mTrackedSimName.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LLFloaterWorldMap::flyToLandmark()
|
void LLFloaterWorldMap::flyToLandmark()
|
||||||
|
|
@ -1741,18 +1757,20 @@ void LLFloaterWorldMap::updateSims(bool found_null_sim)
|
||||||
{
|
{
|
||||||
mSearchResults->selectByValue(match);
|
mSearchResults->selectByValue(match);
|
||||||
mSearchResults->setFocus(true);
|
mSearchResults->setFocus(true);
|
||||||
onCommitSearchResult();
|
onCommitSearchResult(false /*fully commit the only option*/);
|
||||||
}
|
}
|
||||||
// else let user decide
|
// else let user decide
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
mSearchResults->operateOnAll(LLCtrlListInterface::OP_DESELECT);
|
mSearchResults->selectFirstItem();
|
||||||
mSearchResults->setFocus(true);
|
mSearchResults->setFocus(true);
|
||||||
|
onCommitSearchResult(true /*don't update text field*/);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// if we found nothing, say "none"
|
// if we found nothing, say "none"
|
||||||
|
mProcessingSearchUpdate = false;
|
||||||
mSearchResults->setCommentText(LLTrans::getString("worldmap_results_none_found"));
|
mSearchResults->setCommentText(LLTrans::getString("worldmap_results_none_found"));
|
||||||
mSearchResults->operateOnAll(LLCtrlListInterface::OP_DESELECT);
|
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();
|
std::string sim_name = mSearchResults->getSelectedValue().asString();
|
||||||
if (sim_name.empty())
|
if (sim_name.empty())
|
||||||
|
|
@ -1797,8 +1815,14 @@ void LLFloaterWorldMap::onCommitSearchResult()
|
||||||
pos_global.mdV[VY] += (F64)pos_local.mV[VY];
|
pos_global.mdV[VY] += (F64)pos_local.mV[VY];
|
||||||
pos_global.mdV[VZ] = (F64)pos_local.mV[VZ];
|
pos_global.mdV[VZ] = (F64)pos_local.mV[VZ];
|
||||||
|
|
||||||
mLocationEditor->setValue(sim_name);
|
// 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);
|
trackLocation(pos_global);
|
||||||
|
mProcessingSearchUpdate = from_search;
|
||||||
mTrackCtrlsPanel->setDefaultBtn(mTeleportButton);
|
mTrackCtrlsPanel->setDefaultBtn(mTeleportButton);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -176,7 +176,7 @@ protected:
|
||||||
void onLocationFocusChanged( LLFocusableElement* ctrl );
|
void onLocationFocusChanged( LLFocusableElement* ctrl );
|
||||||
void onLocationCommit();
|
void onLocationCommit();
|
||||||
void onCoordinatesCommit();
|
void onCoordinatesCommit();
|
||||||
void onCommitSearchResult();
|
void onCommitSearchResult(bool from_search);
|
||||||
|
|
||||||
void onTeleportFinished();
|
void onTeleportFinished();
|
||||||
|
|
||||||
|
|
@ -213,6 +213,7 @@ private:
|
||||||
|
|
||||||
bool mIsClosing;
|
bool mIsClosing;
|
||||||
bool mSetToUserPosition;
|
bool mSetToUserPosition;
|
||||||
|
bool mProcessingSearchUpdate; // Don't update search string from what user set it to
|
||||||
|
|
||||||
LLVector3d mTrackedLocation;
|
LLVector3d mTrackedLocation;
|
||||||
LLTracker::ETrackingStatus mTrackedStatus;
|
LLTracker::ETrackingStatus mTrackedStatus;
|
||||||
|
|
|
||||||
|
|
@ -2682,6 +2682,7 @@ bool LLInventoryModel::loadSkeleton(
|
||||||
LL_PROFILE_ZONE_SCOPED;
|
LL_PROFILE_ZONE_SCOPED;
|
||||||
LL_DEBUGS(LOG_INV) << "importing inventory skeleton for " << owner_id << LL_ENDL;
|
LL_DEBUGS(LOG_INV) << "importing inventory skeleton for " << owner_id << LL_ENDL;
|
||||||
|
|
||||||
|
LLTimer timer;
|
||||||
typedef std::set<LLPointer<LLViewerInventoryCategory>, InventoryIDPtrLess> cat_set_t;
|
typedef std::set<LLPointer<LLViewerInventoryCategory>, InventoryIDPtrLess> cat_set_t;
|
||||||
cat_set_t temp_cats;
|
cat_set_t temp_cats;
|
||||||
bool rv = true;
|
bool rv = true;
|
||||||
|
|
@ -2966,7 +2967,8 @@ bool LLInventoryModel::loadSkeleton(
|
||||||
}
|
}
|
||||||
|
|
||||||
LL_INFOS(LOG_INV) << "Successfully loaded " << cached_category_count
|
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;
|
<< LL_ENDL;
|
||||||
|
|
||||||
return rv;
|
return rv;
|
||||||
|
|
|
||||||
|
|
@ -364,9 +364,28 @@ void LLInventoryPanel::initializeViewBuilding()
|
||||||
if (mInventory->isInventoryUsable()
|
if (mInventory->isInventoryUsable()
|
||||||
&& LLStartUp::getStartupState() <= STATE_WEARABLES_WAIT)
|
&& 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
|
// 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;
|
const F64 max_time = 20.f;
|
||||||
initializeViews(max_time);
|
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
|
else
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -137,7 +137,8 @@ LLFloaterComboOptions* LLFloaterComboOptions::showUI(
|
||||||
{
|
{
|
||||||
combo_picker->mComboOptions->addSimpleElement(*iter);
|
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->openFloater(LLSD(title));
|
||||||
combo_picker->setFocus(true);
|
combo_picker->setFocus(true);
|
||||||
|
|
@ -1332,15 +1333,6 @@ const std::string LLMaterialEditor::buildMaterialDescription()
|
||||||
desc << mNormalName;
|
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
|
// 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
|
// 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
|
// 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();
|
std::string inv_desc = desc.str();
|
||||||
LLInventoryObject::correctInventoryName(inv_desc);
|
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;
|
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,
|
pack_textures(base_color_img, normal_img, mr_img, emissive_img, occlusion_img,
|
||||||
mBaseColorJ2C, mNormalJ2C, mMetallicRoughnessJ2C, mEmissiveJ2C);
|
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;
|
LLUUID base_color_id;
|
||||||
if (mBaseColorFetched.notNull())
|
if (mBaseColorFetched.notNull())
|
||||||
{
|
{
|
||||||
|
|
@ -2684,10 +2721,8 @@ const std::string LLMaterialEditor::getImageNameFromUri(std::string image_uri, c
|
||||||
// so we can include everything
|
// so we can include everything
|
||||||
if (stripped_uri.length() > 0)
|
if (stripped_uri.length() > 0)
|
||||||
{
|
{
|
||||||
// example "DamagedHelmet: base layer"
|
// example "base layer"
|
||||||
return STRINGIZE(
|
return STRINGIZE(
|
||||||
mMaterialNameShort <<
|
|
||||||
": " <<
|
|
||||||
stripped_uri <<
|
stripped_uri <<
|
||||||
" (" <<
|
" (" <<
|
||||||
texture_type <<
|
texture_type <<
|
||||||
|
|
@ -2696,28 +2731,17 @@ const std::string LLMaterialEditor::getImageNameFromUri(std::string image_uri, c
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
// uri doesn't include the type (because the uri is empty)
|
// uri doesn't include the type (because the uri is empty)
|
||||||
// so we must reorganize the string a bit to include the name
|
// include an explicit name type
|
||||||
// and an explicit name type
|
|
||||||
{
|
{
|
||||||
// example "DamagedHelmet: (Emissive)"
|
// example "Emissive"
|
||||||
return STRINGIZE(
|
return texture_type;
|
||||||
mMaterialNameShort <<
|
|
||||||
" (" <<
|
|
||||||
texture_type <<
|
|
||||||
")"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
// uri includes the type so just use it directly with the
|
// uri includes the type so just use it directly
|
||||||
// name of the material
|
|
||||||
{
|
{
|
||||||
return STRINGIZE(
|
// example: "normal_layer"
|
||||||
// example: AlienBust: normal_layer
|
return stripped_uri;
|
||||||
mMaterialNameShort <<
|
|
||||||
": " <<
|
|
||||||
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
|
// might be good idea to turn mesh into pointer to avoid making a copy
|
||||||
mesh.mVolume = NULL;
|
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;
|
return MESH_OK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2700,10 +2705,14 @@ void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, bool include_textures)
|
||||||
|
|
||||||
S32 instance_num = 0;
|
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;
|
LLMeshUploadData data;
|
||||||
data.mBaseModel = iter->first;
|
data.mBaseModel = iter.first;
|
||||||
|
|
||||||
if (data.mBaseModel->mSubmodelID)
|
if (data.mBaseModel->mSubmodelID)
|
||||||
{
|
{
|
||||||
|
|
@ -2712,7 +2721,7 @@ void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, bool include_textures)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
LLModelInstance& first_instance = *(iter->second.begin());
|
LLModelInstance& first_instance = *(iter.second.begin());
|
||||||
for (S32 i = 0; i < 5; i++)
|
for (S32 i = 0; i < 5; i++)
|
||||||
{
|
{
|
||||||
data.mModel[i] = first_instance.mLOD[i];
|
data.mModel[i] = first_instance.mLOD[i];
|
||||||
|
|
@ -2746,7 +2755,7 @@ void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, bool include_textures)
|
||||||
mUploadSkin,
|
mUploadSkin,
|
||||||
mUploadJoints,
|
mUploadJoints,
|
||||||
mLockScaleIfJointPosition,
|
mLockScaleIfJointPosition,
|
||||||
false,
|
LLModel::WRITE_BINARY,
|
||||||
false,
|
false,
|
||||||
data.mBaseModel->mSubmodelID);
|
data.mBaseModel->mSubmodelID);
|
||||||
|
|
||||||
|
|
@ -2759,8 +2768,8 @@ void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, bool include_textures)
|
||||||
}
|
}
|
||||||
|
|
||||||
// For all instances that use this model
|
// For all instances that use this model
|
||||||
for (instance_list::iterator instance_iter = iter->second.begin();
|
for (instance_list::iterator instance_iter = iter.second.begin();
|
||||||
instance_iter != iter->second.end();
|
instance_iter != iter.second.end();
|
||||||
++instance_iter)
|
++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;
|
LLMeshUploadData data;
|
||||||
data.mBaseModel = iter->first;
|
data.mBaseModel = iter.first;
|
||||||
|
|
||||||
if (!data.mBaseModel->mSubmodelID)
|
if (!data.mBaseModel->mSubmodelID)
|
||||||
{
|
{
|
||||||
|
|
@ -2870,7 +2880,7 @@ void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, bool include_textures)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
LLModelInstance& first_instance = *(iter->second.begin());
|
LLModelInstance& first_instance = *(iter.second.begin());
|
||||||
for (S32 i = 0; i < 5; i++)
|
for (S32 i = 0; i < 5; i++)
|
||||||
{
|
{
|
||||||
data.mModel[i] = first_instance.mLOD[i];
|
data.mModel[i] = first_instance.mLOD[i];
|
||||||
|
|
@ -2904,7 +2914,7 @@ void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, bool include_textures)
|
||||||
mUploadSkin,
|
mUploadSkin,
|
||||||
mUploadJoints,
|
mUploadJoints,
|
||||||
mLockScaleIfJointPosition,
|
mLockScaleIfJointPosition,
|
||||||
false,
|
LLModel::WRITE_BINARY,
|
||||||
false,
|
false,
|
||||||
data.mBaseModel->mSubmodelID);
|
data.mBaseModel->mSubmodelID);
|
||||||
|
|
||||||
|
|
@ -2917,8 +2927,8 @@ void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, bool include_textures)
|
||||||
}
|
}
|
||||||
|
|
||||||
// For all instances that use this model
|
// For all instances that use this model
|
||||||
for (instance_list::iterator instance_iter = iter->second.begin();
|
for (instance_list::iterator instance_iter = iter.second.begin();
|
||||||
instance_iter != iter->second.end();
|
instance_iter != iter.second.end();
|
||||||
++instance_iter)
|
++instance_iter)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -674,7 +674,22 @@ public:
|
||||||
typedef std::vector<LLModelInstance> instance_list;
|
typedef std::vector<LLModelInstance> instance_list;
|
||||||
instance_list mInstanceList;
|
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;
|
instance_map mInstance;
|
||||||
|
|
||||||
LLMutex* mMutex;
|
LLMutex* mMutex;
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@
|
||||||
|
|
||||||
#include "llmodelloader.h"
|
#include "llmodelloader.h"
|
||||||
#include "lldaeloader.h"
|
#include "lldaeloader.h"
|
||||||
#include "llgltfloader.h"
|
#include "gltf/llgltfloader.h"
|
||||||
#include "llfloatermodelpreview.h"
|
#include "llfloatermodelpreview.h"
|
||||||
|
|
||||||
#include "llagent.h"
|
#include "llagent.h"
|
||||||
|
|
@ -40,6 +40,7 @@
|
||||||
#include "lldrawable.h"
|
#include "lldrawable.h"
|
||||||
#include "llface.h"
|
#include "llface.h"
|
||||||
#include "lliconctrl.h"
|
#include "lliconctrl.h"
|
||||||
|
#include "lljointdata.h"
|
||||||
#include "llmatrix4a.h"
|
#include "llmatrix4a.h"
|
||||||
#include "llmeshrepository.h"
|
#include "llmeshrepository.h"
|
||||||
#include "llmeshoptimizer.h"
|
#include "llmeshoptimizer.h"
|
||||||
|
|
@ -163,10 +164,14 @@ LLModelPreview::LLModelPreview(S32 width, S32 height, LLFloater* fmp)
|
||||||
, mPhysicsSearchLOD(LLModel::LOD_PHYSICS)
|
, mPhysicsSearchLOD(LLModel::LOD_PHYSICS)
|
||||||
, mResetJoints(false)
|
, mResetJoints(false)
|
||||||
, mModelNoErrors(true)
|
, mModelNoErrors(true)
|
||||||
|
, mLoading(false)
|
||||||
|
, mModelLoader(nullptr)
|
||||||
, mLastJointUpdate(false)
|
, mLastJointUpdate(false)
|
||||||
, mFirstSkinUpdate(true)
|
, mFirstSkinUpdate(true)
|
||||||
, mHasDegenerate(false)
|
, mHasDegenerate(false)
|
||||||
, mImporterDebug(LLCachedControl<bool>(gSavedSettings, "ImporterDebug", false))
|
, mNumOfFetchingTextures(0)
|
||||||
|
, mTexturesNeedScaling(false)
|
||||||
|
, mImporterDebug(LLCachedControl<bool>(gSavedSettings, "ImporterDebugVerboseLogging", false))
|
||||||
{
|
{
|
||||||
mNeedsUpdate = true;
|
mNeedsUpdate = true;
|
||||||
mCameraDistance = 0.f;
|
mCameraDistance = 0.f;
|
||||||
|
|
@ -175,11 +180,9 @@ LLModelPreview::LLModelPreview(S32 width, S32 height, LLFloater* fmp)
|
||||||
mCameraZoom = 1.f;
|
mCameraZoom = 1.f;
|
||||||
mTextureName = 0;
|
mTextureName = 0;
|
||||||
mPreviewLOD = 0;
|
mPreviewLOD = 0;
|
||||||
mModelLoader = NULL;
|
|
||||||
mMaxTriangleLimit = 0;
|
mMaxTriangleLimit = 0;
|
||||||
mDirty = false;
|
mDirty = false;
|
||||||
mGenLOD = false;
|
mGenLOD = false;
|
||||||
mLoading = false;
|
|
||||||
mLookUpLodFiles = false;
|
mLookUpLodFiles = false;
|
||||||
mLoadState = LLModelLoader::STARTING;
|
mLoadState = LLModelLoader::STARTING;
|
||||||
mGroup = 0;
|
mGroup = 0;
|
||||||
|
|
@ -211,6 +214,7 @@ LLModelPreview::~LLModelPreview()
|
||||||
{
|
{
|
||||||
mModelLoader->shutdown();
|
mModelLoader->shutdown();
|
||||||
mModelLoader = NULL;
|
mModelLoader = NULL;
|
||||||
|
mLoading = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mPreviewAvatar)
|
if (mPreviewAvatar)
|
||||||
|
|
@ -557,10 +561,7 @@ void LLModelPreview::rebuildUploadData()
|
||||||
texture->setLoadedCallback(LLModelPreview::textureLoadedCallback, 0, true, false, new LLHandle<LLModelPreview>(getHandle()), &mCallbackTextureList, false);
|
texture->setLoadedCallback(LLModelPreview::textureLoadedCallback, 0, true, false, new LLHandle<LLModelPreview>(getHandle()), &mCallbackTextureList, false);
|
||||||
texture->forceToSaveRawImage(0, F32_MAX);
|
texture->forceToSaveRawImage(0, F32_MAX);
|
||||||
texture->updateFetch();
|
texture->updateFetch();
|
||||||
if (mModelLoader)
|
mNumOfFetchingTextures++;
|
||||||
{
|
|
||||||
mModelLoader->mNumOfFetchingTextures++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -691,7 +692,7 @@ void LLModelPreview::saveUploadData(const std::string& filename,
|
||||||
save_skinweights,
|
save_skinweights,
|
||||||
save_joint_positions,
|
save_joint_positions,
|
||||||
lock_scale_if_joint_position,
|
lock_scale_if_joint_position,
|
||||||
false, true, instance.mModel->mSubmodelID);
|
LLModel::WRITE_BINARY, true, instance.mModel->mSubmodelID);
|
||||||
|
|
||||||
data["mesh"][instance.mModel->mLocalID] = str.str();
|
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;
|
LL_WARNS() << out.str() << LL_ENDL;
|
||||||
LLFloaterModelPreview::addStringToLog(out, true);
|
LLFloaterModelPreview::addStringToLog(out, true);
|
||||||
assert(lod >= LLModel::LOD_IMPOSTOR && lod < LLModel::NUM_LODS);
|
assert(lod >= LLModel::LOD_IMPOSTOR && lod < LLModel::NUM_LODS);
|
||||||
|
if (mModelLoader == nullptr)
|
||||||
|
{
|
||||||
|
mLoading = false;
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -806,10 +811,14 @@ void LLModelPreview::loadModel(std::string filename, S32 lod, bool force_disable
|
||||||
joint_alias_map,
|
joint_alias_map,
|
||||||
LLSkinningUtil::getMaxJointCount(),
|
LLSkinningUtil::getMaxJointCount(),
|
||||||
gSavedSettings.getU32("ImporterModelLimit"),
|
gSavedSettings.getU32("ImporterModelLimit"),
|
||||||
|
gSavedSettings.getU32("ImporterDebugMode"),
|
||||||
gSavedSettings.getBOOL("ImporterPreprocessDAE"));
|
gSavedSettings.getBOOL("ImporterPreprocessDAE"));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
LLVOAvatar* av = getPreviewAvatar();
|
||||||
|
std::vector<LLJointData> viewer_skeleton;
|
||||||
|
av->getJointMatricesAndHierarhy(viewer_skeleton);
|
||||||
mModelLoader = new LLGLTFLoader(
|
mModelLoader = new LLGLTFLoader(
|
||||||
filename,
|
filename,
|
||||||
lod,
|
lod,
|
||||||
|
|
@ -822,7 +831,9 @@ void LLModelPreview::loadModel(std::string filename, S32 lod, bool force_disable
|
||||||
mJointsFromNode,
|
mJointsFromNode,
|
||||||
joint_alias_map,
|
joint_alias_map,
|
||||||
LLSkinningUtil::getMaxJointCount(),
|
LLSkinningUtil::getMaxJointCount(),
|
||||||
gSavedSettings.getU32("ImporterModelLimit"));
|
gSavedSettings.getU32("ImporterModelLimit"),
|
||||||
|
gSavedSettings.getU32("ImporterDebugMode"),
|
||||||
|
viewer_skeleton);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (force_disable_slm)
|
if (force_disable_slm)
|
||||||
|
|
@ -985,7 +996,9 @@ void LLModelPreview::loadModelCallback(S32 loaded_lod)
|
||||||
setRigValidForJointPositionUpload(mModelLoader->isRigValidForJointPositionUpload());
|
setRigValidForJointPositionUpload(mModelLoader->isRigValidForJointPositionUpload());
|
||||||
setLegacyRigFlags(mModelLoader->getLegacyRigFlags());
|
setLegacyRigFlags(mModelLoader->getLegacyRigFlags());
|
||||||
|
|
||||||
|
mTexturesNeedScaling |= mModelLoader->mTexturesNeedScaling;
|
||||||
mModelLoader->loadTextures();
|
mModelLoader->loadTextures();
|
||||||
|
warnTextureScaling();
|
||||||
|
|
||||||
if (loaded_lod == -1)
|
if (loaded_lod == -1)
|
||||||
{ //populate all LoDs from model loader scene
|
{ //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)
|
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
|
// Allow LoD from -1 to LLModel::LOD_PHYSICS
|
||||||
if (which_lod < -1 || which_lod > LLModel::NUM_LODS - 1)
|
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;
|
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
|
// Build models
|
||||||
|
|
||||||
S32 start = LLModel::LOD_HIGH;
|
S32 start = LLModel::LOD_HIGH;
|
||||||
|
|
@ -1893,7 +1912,7 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d
|
||||||
{
|
{
|
||||||
start = which_lod;
|
start = which_lod;
|
||||||
end = which_lod;
|
end = which_lod;
|
||||||
}
|
};
|
||||||
|
|
||||||
for (S32 lod = start; lod >= end; --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);
|
const LLVolumeFace &face = base->getVolumeFace(face_idx);
|
||||||
LLVolumeFace &new_face = target_model->getVolumeFace(face_idx);
|
LLVolumeFace &new_face = target_model->getVolumeFace(face_idx);
|
||||||
new_face = face;
|
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)
|
if (genMeshOptimizerPerFace(base, target_model, face_idx, indices_decimator, lod_error_threshold, MESH_OPTIMIZER_NO_TOPOLOGY) < 0)
|
||||||
{
|
{
|
||||||
// Sloppy failed and returned an invalid model
|
// 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);
|
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
|
<< " lod " << which_lod
|
||||||
<< " resulting ratio " << precise_ratio
|
<< " resulting ratio " << precise_ratio
|
||||||
<< " simplified using per model method." << LL_ENDL;
|
<< " simplified using per model method." << LL_ENDL;
|
||||||
|
meshes_simplified++;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LL_INFOS() << "Model " << target_model->getName()
|
LL_DEBUGS("Upload") << "Model " << target_model->getName()
|
||||||
<< " lod " << which_lod
|
<< " lod " << which_lod
|
||||||
<< " resulting ratio " << sloppy_ratio
|
<< " resulting ratio " << sloppy_ratio
|
||||||
<< " sloppily simplified using per model method." << LL_ENDL;
|
<< " sloppily simplified using per model method." << LL_ENDL;
|
||||||
|
meshes_sloppy_simplified++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LL_INFOS() << "Model " << target_model->getName()
|
LL_DEBUGS("Upload") << "Model " << target_model->getName()
|
||||||
<< " lod " << which_lod
|
<< " lod " << which_lod
|
||||||
<< " resulting ratio " << precise_ratio
|
<< " resulting ratio " << precise_ratio
|
||||||
<< " simplified using per model method." << LL_ENDL;
|
<< " 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
|
//copy material list
|
||||||
target_model->mMaterialList = base->mMaterialList;
|
target_model->mMaterialList = base->mMaterialList;
|
||||||
|
|
||||||
|
meshes_processed++;
|
||||||
|
|
||||||
if (!validate_model(target_model))
|
if (!validate_model(target_model))
|
||||||
{
|
{
|
||||||
LL_ERRS() << "Invalid model generated when creating LODs" << LL_ENDL;
|
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()
|
void LLModelPreview::updateStatusMessages()
|
||||||
|
|
@ -2466,7 +2511,7 @@ void LLModelPreview::updateStatusMessages()
|
||||||
LLMutexLock lock(this);
|
LLMutexLock lock(this);
|
||||||
if (mModelLoader)
|
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
|
// Some textures are still loading, prevent upload until they are done
|
||||||
mModelNoErrors = false;
|
mModelNoErrors = false;
|
||||||
|
|
@ -3039,9 +3084,12 @@ void LLModelPreview::loadedCallback(
|
||||||
S32 lod,
|
S32 lod,
|
||||||
void* opaque)
|
void* opaque)
|
||||||
{
|
{
|
||||||
|
if(LLModelPreview::sIgnoreLoadedCallback)
|
||||||
|
return;
|
||||||
|
|
||||||
LLModelPreview* pPreview = static_cast<LLModelPreview*>(opaque);
|
LLModelPreview* pPreview = static_cast<LLModelPreview*>(opaque);
|
||||||
LLMutexLock lock(pPreview);
|
LLMutexLock lock(pPreview);
|
||||||
if (pPreview && pPreview->mModelLoader && !LLModelPreview::sIgnoreLoadedCallback)
|
if (pPreview && pPreview->mModelLoader)
|
||||||
{
|
{
|
||||||
// Load loader's warnings into floater's log tab
|
// Load loader's warnings into floater's log tab
|
||||||
const LLSD out = pPreview->mModelLoader->logOut();
|
const LLSD out = pPreview->mModelLoader->logOut();
|
||||||
|
|
@ -3090,25 +3138,48 @@ void LLModelPreview::lookupLODModelFiles(S32 lod)
|
||||||
S32 next_lod = (lod - 1 >= LLModel::LOD_IMPOSTOR) ? lod - 1 : LLModel::LOD_PHYSICS;
|
S32 next_lod = (lod - 1 >= LLModel::LOD_IMPOSTOR) ? lod - 1 : LLModel::LOD_PHYSICS;
|
||||||
|
|
||||||
std::string lod_filename = mLODFile[LLModel::LOD_HIGH];
|
std::string lod_filename = mLODFile[LLModel::LOD_HIGH];
|
||||||
std::string ext = ".dae";
|
|
||||||
std::string lod_filename_lower(lod_filename);
|
std::string lod_filename_lower(lod_filename);
|
||||||
LLStringUtil::toLower(lod_filename_lower);
|
LLStringUtil::toLower(lod_filename_lower);
|
||||||
std::string::size_type i = lod_filename_lower.rfind(ext);
|
|
||||||
if (i != std::string::npos)
|
// 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)
|
||||||
{
|
{
|
||||||
lod_filename.replace(i, lod_filename.size() - ext.size(), getLodSuffix(next_lod) + ext);
|
std::string::size_type i = lod_filename_lower.rfind(ext);
|
||||||
}
|
if (i != std::string::npos)
|
||||||
if (gDirUtilp->fileExists(lod_filename))
|
|
||||||
{
|
|
||||||
LLFloaterModelPreview* fmp = LLFloaterModelPreview::sInstance;
|
|
||||||
if (fmp)
|
|
||||||
{
|
{
|
||||||
fmp->setCtrlLoadFromFile(next_lod);
|
ext_pos = i;
|
||||||
|
found_ext = ext;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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_file_to_check, next_lod);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lookupLODModelFiles(next_lod);
|
||||||
}
|
}
|
||||||
loadModel(lod_filename, next_lod);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// No recognized extension found, continue with next LOD
|
||||||
lookupLODModelFiles(next_lod);
|
lookupLODModelFiles(next_lod);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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->setLoadedCallback(LLModelPreview::textureLoadedCallback, 0, true, false, new LLHandle<LLModelPreview>(handle), &preview->mCallbackTextureList, false);
|
||||||
tex->forceToSaveRawImage(0, F32_MAX);
|
tex->forceToSaveRawImage(0, F32_MAX);
|
||||||
material.setDiffuseMap(tex->getID()); // record tex ID
|
material.setDiffuseMap(tex->getID()); // record tex ID
|
||||||
|
preview->mNumOfFetchingTextures++;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3993,6 +4065,18 @@ void LLModelPreview::setPreviewLOD(S32 lod)
|
||||||
updateStatusMessages();
|
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
|
//static
|
||||||
void LLModelPreview::textureLoadedCallback(
|
void LLModelPreview::textureLoadedCallback(
|
||||||
bool success,
|
bool success,
|
||||||
|
|
@ -4013,11 +4097,19 @@ void LLModelPreview::textureLoadedCallback(
|
||||||
LLModelPreview* preview = static_cast<LLModelPreview*>(handle->get());
|
LLModelPreview* preview = static_cast<LLModelPreview*>(handle->get());
|
||||||
preview->refresh();
|
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> mLodsQuery;
|
||||||
std::vector<S32> mLodsWithParsingError;
|
std::vector<S32> mLodsWithParsingError;
|
||||||
bool mHasDegenerate;
|
bool mHasDegenerate;
|
||||||
|
bool areTexturesReady() { return !mNumOfFetchingTextures; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
|
|
@ -213,6 +214,7 @@ protected:
|
||||||
static LLJoint* lookupJointByName(const std::string&, void* opaque);
|
static LLJoint* lookupJointByName(const std::string&, void* opaque);
|
||||||
static U32 loadTextures(LLImportMaterial& material, LLHandle<LLModelPreview> handle);
|
static U32 loadTextures(LLImportMaterial& material, LLHandle<LLModelPreview> handle);
|
||||||
|
|
||||||
|
void warnTextureScaling();
|
||||||
void lookupLODModelFiles(S32 lod);
|
void lookupLODModelFiles(S32 lod);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
@ -242,6 +244,9 @@ private:
|
||||||
/// Not read unless mWarnOfUnmatchedPhyicsMeshes is true.
|
/// Not read unless mWarnOfUnmatchedPhyicsMeshes is true.
|
||||||
LLPointer<LLModel> mDefaultPhysicsShapeP;
|
LLPointer<LLModel> mDefaultPhysicsShapeP;
|
||||||
|
|
||||||
|
S32 mNumOfFetchingTextures;
|
||||||
|
bool mTexturesNeedScaling;
|
||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
MESH_OPTIMIZER_FULL,
|
MESH_OPTIMIZER_FULL,
|
||||||
|
|
|
||||||
|
|
@ -843,6 +843,10 @@ void LLPanelPeople::updateNearbyList()
|
||||||
|
|
||||||
LLWorld::getInstance()->getAvatars(&mNearbyList->getIDs(), &positions, gAgent.getPositionGlobal(), gSavedSettings.getF32("NearMeRange"));
|
LLWorld::getInstance()->getAvatars(&mNearbyList->getIDs(), &positions, gAgent.getPositionGlobal(), gSavedSettings.getF32("NearMeRange"));
|
||||||
mNearbyList->setDirty();
|
mNearbyList->setDirty();
|
||||||
|
#ifdef LL_DISCORD
|
||||||
|
if (gSavedSettings.getBOOL("EnableDiscord"))
|
||||||
|
LLAppViewer::updateDiscordPartyMaxSize((S32)mNearbyList->getIDs().size());
|
||||||
|
#endif
|
||||||
|
|
||||||
DISTANCE_COMPARATOR.updateAvatarsPositions(positions, mNearbyList->getIDs());
|
DISTANCE_COMPARATOR.updateAvatarsPositions(positions, mNearbyList->getIDs());
|
||||||
LLActiveSpeakerMgr::instance().update(true);
|
LLActiveSpeakerMgr::instance().update(true);
|
||||||
|
|
|
||||||
|
|
@ -154,11 +154,15 @@ void PeopleContextMenu::buildContextMenu(class LLMenuGL& menu, U32 flags)
|
||||||
|
|
||||||
bool PeopleContextMenu::enableContextMenuItem(const LLSD& userdata)
|
bool PeopleContextMenu::enableContextMenuItem(const LLSD& userdata)
|
||||||
{
|
{
|
||||||
|
std::string item = userdata.asString();
|
||||||
if(gAgent.getID() == mUUIDs.front())
|
if(gAgent.getID() == mUUIDs.front())
|
||||||
{
|
{
|
||||||
|
if (item == std::string("can_zoom_in"))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
std::string item = userdata.asString();
|
|
||||||
|
|
||||||
// Note: can_block and can_delete is used only for one person selected menu
|
// Note: can_block and can_delete is used only for one person selected menu
|
||||||
// so we don't need to go over all uuids.
|
// so we don't need to go over all uuids.
|
||||||
|
|
|
||||||
|
|
@ -177,7 +177,7 @@ void LLReflectionMap::autoAdjustOrigin()
|
||||||
mPriority = 1;
|
mPriority = 1;
|
||||||
mOrigin.load3(mViewerObject->getPositionAgent().mV);
|
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));
|
LLVector3 s = mViewerObject->getScale().scaledVec(LLVector3(0.5f, 0.5f, 0.5f));
|
||||||
mRadius = s.magVec();
|
mRadius = s.magVec();
|
||||||
|
|
|
||||||
|
|
@ -124,7 +124,7 @@ public:
|
||||||
LLSpatialGroup* mGroup = nullptr;
|
LLSpatialGroup* mGroup = nullptr;
|
||||||
|
|
||||||
// viewer object this probe is tracking (if any)
|
// 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)
|
// what priority should this probe have (higher is higher priority)
|
||||||
// currently only 0 or 1
|
// currently only 0 or 1
|
||||||
|
|
|
||||||
|
|
@ -1146,7 +1146,7 @@ void LLReflectionMapManager::updateUniforms()
|
||||||
{
|
{
|
||||||
if (refmap->mViewerObject && refmap->mViewerObject->getVolume())
|
if (refmap->mViewerObject && refmap->mViewerObject->getVolume())
|
||||||
{ // have active manual probes live-track the object they're associated with
|
{ // 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);
|
refmap->mOrigin.load3(vobj->getPositionAgent().mV);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -135,6 +135,12 @@ void LLSkinningUtil::initSkinningMatrixPalette(
|
||||||
|
|
||||||
initJointNums(const_cast<LLMeshSkinInfo*>(skin), avatar);
|
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];
|
LLMatrix4a world[LL_CHARACTER_MAX_ANIMATED_JOINTS];
|
||||||
|
|
||||||
for (S32 j = 0; j < count; ++j)
|
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);
|
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;
|
LLVector4a pos_joint_space;
|
||||||
|
|
||||||
mat.affineTransform(pos, pos_joint_space);
|
mat.affineTransform(pos, pos_joint_space);
|
||||||
|
|
|
||||||
|
|
@ -1026,6 +1026,10 @@ void LLLocalSpeakerMgr::updateSpeakerList()
|
||||||
uuid_vec_t avatar_ids;
|
uuid_vec_t avatar_ids;
|
||||||
std::vector<LLVector3d> positions;
|
std::vector<LLVector3d> positions;
|
||||||
LLWorld::getInstance()->getAvatars(&avatar_ids, &positions, gAgent.getPositionGlobal(), CHAT_NORMAL_RADIUS);
|
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++)
|
for(U32 i=0; i<avatar_ids.size(); i++)
|
||||||
{
|
{
|
||||||
setSpeaker(avatar_ids[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;
|
LL_WARNS("AppInit") << "Unreliable timers detected (may be bad PCI chipset)!!" << LL_ENDL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef LL_DISCORD
|
||||||
|
LLAppViewer::initDiscordSocial();
|
||||||
|
#endif
|
||||||
|
|
||||||
//
|
//
|
||||||
// Log on to system
|
// Log on to system
|
||||||
//
|
//
|
||||||
|
|
@ -2103,9 +2107,6 @@ bool idle_startup()
|
||||||
|
|
||||||
do_startup_frame();
|
do_startup_frame();
|
||||||
|
|
||||||
// We're successfully logged in.
|
|
||||||
gSavedSettings.setBOOL("FirstLoginThisInstall", false);
|
|
||||||
|
|
||||||
LLFloaterReg::showInitialVisibleInstances();
|
LLFloaterReg::showInitialVisibleInstances();
|
||||||
|
|
||||||
LLFloaterGridStatus::getInstance()->startGridStatusTimer();
|
LLFloaterGridStatus::getInstance()->startGridStatusTimer();
|
||||||
|
|
@ -2451,6 +2452,27 @@ bool idle_startup()
|
||||||
|
|
||||||
LLPerfStats::StatsRecorder::setAutotuneInit();
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1541,12 +1541,6 @@ bool LLToolPie::shouldAllowFirstMediaInteraction(const LLPickInfo& pick, bool mo
|
||||||
return false;
|
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
|
// HUD attachments
|
||||||
if((FirstClickPref & MEDIA_FIRST_CLICK_HUD) && object->isHUDAttachment())
|
if((FirstClickPref & MEDIA_FIRST_CLICK_HUD) && object->isHUDAttachment())
|
||||||
{
|
{
|
||||||
|
|
@ -1569,21 +1563,29 @@ bool LLToolPie::shouldAllowFirstMediaInteraction(const LLPickInfo& pick, bool mo
|
||||||
return false;
|
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
|
// Check if the object is owned by a friend of the agent
|
||||||
if(FirstClickPref & MEDIA_FIRST_CLICK_FRIEND)
|
if(FirstClickPref & MEDIA_FIRST_CLICK_FRIEND)
|
||||||
{
|
{
|
||||||
LL_DEBUGS_ONCE() << "FirstClickPref & MEDIA_FIRST_CLICK_FRIEND. id: " << owner_id << LL_ENDL;
|
if(LLAvatarTracker::instance().isBuddy(owner_id))
|
||||||
return LLAvatarTracker::instance().isBuddy(owner_id);
|
{
|
||||||
|
LL_DEBUGS_ONCE() << "FirstClickPref & MEDIA_FIRST_CLICK_FRIEND. id: " << owner_id << LL_ENDL;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for objects set to or owned by the active group
|
// Check for objects set to or owned by the active group
|
||||||
if(FirstClickPref & MEDIA_FIRST_CLICK_GROUP)
|
if(FirstClickPref & MEDIA_FIRST_CLICK_GROUP)
|
||||||
{
|
{
|
||||||
// Get our active group
|
if(gAgent.isInGroup(group_id) || gAgent.isInGroup(owner_id))
|
||||||
LLUUID active_group = gAgent.getGroupID();
|
|
||||||
if(active_group.notNull() && (active_group == group_id || active_group == 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;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -99,10 +99,10 @@ private:
|
||||||
MEDIA_FIRST_CLICK_LAND = 1 << 4, // 0b00010000 (16)
|
MEDIA_FIRST_CLICK_LAND = 1 << 4, // 0b00010000 (16)
|
||||||
|
|
||||||
// Covers any object with PRIM_MEDIA_FIRST_CLICK_INTERACT (combines all previous flags)
|
// 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
|
// 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 shouldAllowFirstMediaInteraction(const LLPickInfo& info, bool moap_flag);
|
||||||
bool handleMediaClick(const LLPickInfo& info);
|
bool handleMediaClick(const LLPickInfo& info);
|
||||||
|
|
|
||||||
|
|
@ -38,8 +38,8 @@
|
||||||
#include "llfloateraddpaymentmethod.h"
|
#include "llfloateraddpaymentmethod.h"
|
||||||
#include "llfloaterauction.h"
|
#include "llfloaterauction.h"
|
||||||
#include "llfloaterautoreplacesettings.h"
|
#include "llfloaterautoreplacesettings.h"
|
||||||
#include "llfloateravatar.h"
|
|
||||||
#include "llfloateravatarpicker.h"
|
#include "llfloateravatarpicker.h"
|
||||||
|
#include "llfloateravatarwelcomepack.h"
|
||||||
#include "llfloateravatarrendersettings.h"
|
#include "llfloateravatarrendersettings.h"
|
||||||
#include "llfloateravatartextures.h"
|
#include "llfloateravatartextures.h"
|
||||||
#include "llfloaterbanduration.h"
|
#include "llfloaterbanduration.h"
|
||||||
|
|
@ -331,8 +331,8 @@ void LLViewerFloaterReg::registerFloaters()
|
||||||
LLFloaterReg::add("appearance", "floater_my_appearance.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSidePanelContainer>);
|
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("associate_listing", "floater_associate_listing.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterAssociateListing>);
|
||||||
LLFloaterReg::add("auction", "floater_auction.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterAuction>);
|
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_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_render_settings", "floater_avatar_render_settings.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterAvatarRenderSettings>);
|
||||||
LLFloaterReg::add("avatar_textures", "floater_avatar_textures.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterAvatarTextures>);
|
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)
|
if(!mMediaPlugin)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!url.empty()) {
|
if (!url.empty())
|
||||||
|
{
|
||||||
LL_INFOS() << "Starting internet stream: " << url << LL_ENDL;
|
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();
|
mMediaPlugin->start();
|
||||||
LL_INFOS() << "Playing stream..." << LL_ENDL;
|
LL_INFOS() << "Playing stream..." << LL_ENDL;
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
LL_INFOS() << "setting stream to NULL"<< LL_ENDL;
|
LL_INFOS() << "setting stream to NULL"<< LL_ENDL;
|
||||||
mURL.clear();
|
mURL.clear();
|
||||||
mMediaPlugin->stop();
|
mMediaPlugin->stop();
|
||||||
|
|
|
||||||
|
|
@ -289,6 +289,7 @@ void force_error_coroutine_crash();
|
||||||
void force_error_coroprocedure_crash();
|
void force_error_coroprocedure_crash();
|
||||||
void force_error_work_queue_crash();
|
void force_error_work_queue_crash();
|
||||||
void force_error_thread_crash();
|
void force_error_thread_crash();
|
||||||
|
void force_exception_thread_crash();
|
||||||
|
|
||||||
void handle_force_delete();
|
void handle_force_delete();
|
||||||
void print_object_info();
|
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
|
class LLAdvancedForceErrorDisconnectViewer : public view_listener_t
|
||||||
{
|
{
|
||||||
bool handleEvent(const LLSD& userdata)
|
bool handleEvent(const LLSD& userdata)
|
||||||
|
|
@ -8696,6 +8706,11 @@ void force_error_thread_crash()
|
||||||
LLAppViewer::instance()->forceErrorThreadCrash();
|
LLAppViewer::instance()->forceErrorThreadCrash();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void force_exception_thread_crash()
|
||||||
|
{
|
||||||
|
LLAppViewer::instance()->forceExceptionThreadCrash();
|
||||||
|
}
|
||||||
|
|
||||||
class LLToolsUseSelectionForGrid : public view_listener_t
|
class LLToolsUseSelectionForGrid : public view_listener_t
|
||||||
{
|
{
|
||||||
bool handleEvent(const LLSD& userdata)
|
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 LLAdvancedForceErrorCoroprocedureCrash(), "Advanced.ForceErrorCoroprocedureCrash");
|
||||||
view_listener_t::addMenu(new LLAdvancedForceErrorWorkQueueCrash(), "Advanced.ForceErrorWorkQueueCrash");
|
view_listener_t::addMenu(new LLAdvancedForceErrorWorkQueueCrash(), "Advanced.ForceErrorWorkQueueCrash");
|
||||||
view_listener_t::addMenu(new LLAdvancedForceErrorThreadCrash(), "Advanced.ForceErrorThreadCrash");
|
view_listener_t::addMenu(new LLAdvancedForceErrorThreadCrash(), "Advanced.ForceErrorThreadCrash");
|
||||||
|
view_listener_t::addMenu(new LLAdvancedForceExceptionThreadCrash(), "Advanced.ForceExceptionThreadCrash");
|
||||||
view_listener_t::addMenu(new LLAdvancedForceErrorDisconnectViewer(), "Advanced.ForceErrorDisconnectViewer");
|
view_listener_t::addMenu(new LLAdvancedForceErrorDisconnectViewer(), "Advanced.ForceErrorDisconnectViewer");
|
||||||
|
|
||||||
// Advanced (toplevel)
|
// Advanced (toplevel)
|
||||||
|
|
|
||||||
|
|
@ -95,7 +95,7 @@ class LLFileEnableUploadModel : public view_listener_t
|
||||||
bool handleEvent(const LLSD& userdata)
|
bool handleEvent(const LLSD& userdata)
|
||||||
{
|
{
|
||||||
LLFloaterModelPreview* fmp = (LLFloaterModelPreview*) LLFloaterReg::findInstance("upload_model");
|
LLFloaterModelPreview* fmp = (LLFloaterModelPreview*) LLFloaterReg::findInstance("upload_model");
|
||||||
if (fmp && fmp->isModelLoading())
|
if (fmp && !fmp->isDead() && fmp->isModelLoading())
|
||||||
{
|
{
|
||||||
return false;
|
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) )
|
if ( LLTracker::isTracking(NULL) )
|
||||||
{
|
{
|
||||||
// Check distance to beacon, if < 5m, remove beacon
|
// 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_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.");
|
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");
|
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)
|
if (gFrameCount && mLastTimeDiff > (F64Seconds)0.0)
|
||||||
{
|
{
|
||||||
|
mTotalTime += time_diff;
|
||||||
sample(LLStatViewer::FRAMETIME, time_diff);
|
sample(LLStatViewer::FRAMETIME, time_diff);
|
||||||
// old stats that were never really used
|
// old stats that were never really used
|
||||||
F64Seconds jit = (F64Seconds)std::fabs((mLastTimeDiff - time_diff));
|
F64Seconds jit = (F64Seconds)std::fabs((mLastTimeDiff - time_diff));
|
||||||
sample(LLStatViewer::FRAMETIME_JITTER, jit);
|
sample(LLStatViewer::FRAMETIME_JITTER, jit);
|
||||||
mTotalFrametimeJitter += jit;
|
mTotalFrametimeJitter += jit;
|
||||||
sample(LLStatViewer::FRAMETIME_JITTER_CUMULATIVE, mTotalFrametimeJitter);
|
sample(LLStatViewer::FRAMETIME_JITTER_CUMULATIVE, mTotalFrametimeJitter);
|
||||||
|
sample(LLStatViewer::NOTRMALIZED_FRAMETIME_JITTER_SESSION, mTotalFrametimeJitter / mTotalTime);
|
||||||
|
|
||||||
static LLCachedControl<F32> frameTimeEventThreshold(gSavedSettings, "StatsFrametimeEventThreshold", 0.1f);
|
static LLCachedControl<F32> frameTimeEventThreshold(gSavedSettings, "StatsFrametimeEventThreshold", 0.1f);
|
||||||
|
|
||||||
if (time_diff - mLastTimeDiff > mLastTimeDiff * frameTimeEventThreshold())
|
if (time_diff - mLastTimeDiff > mLastTimeDiff * frameTimeEventThreshold())
|
||||||
{
|
{
|
||||||
sample(LLStatViewer::FRAMETIME_JITTER_EVENTS, mFrameJitterEvents++);
|
sample(LLStatViewer::FRAMETIME_JITTER_EVENTS, mFrameJitterEvents++);
|
||||||
|
mFrameJitterEventsLastMinute++;
|
||||||
}
|
}
|
||||||
|
|
||||||
mFrameTimes.push_back(time_diff);
|
mFrameTimes.push_back(time_diff);
|
||||||
mFrameTimesJitter.push_back(jit);
|
mFrameTimesJitter.push_back(jit);
|
||||||
|
|
||||||
mLastFrameTimeSample += time_diff;
|
mLastFrameTimeSample += time_diff;
|
||||||
|
mTimeSinceLastEventSample += time_diff;
|
||||||
|
|
||||||
static LLCachedControl<S32> frameTimeSampleSeconds(gSavedSettings, "StatsFrametimeSampleSeconds", 5);
|
static LLCachedControl<S32> frameTimeSampleSeconds(gSavedSettings, "StatsFrametimeSampleSeconds", 5);
|
||||||
|
|
||||||
|
|
@ -356,6 +364,17 @@ void LLViewerStats::updateFrameStats(const F64Seconds time_diff)
|
||||||
mFrameTimesJitter.clear();
|
mFrameTimesJitter.clear();
|
||||||
mLastFrameTimeSample = F64Seconds(0);
|
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;
|
mLastTimeDiff = time_diff;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -277,9 +277,13 @@ private:
|
||||||
F64Seconds mLastTimeDiff; // used for time stat updates
|
F64Seconds mLastTimeDiff; // used for time stat updates
|
||||||
F64Seconds mTotalFrametimeJitter;
|
F64Seconds mTotalFrametimeJitter;
|
||||||
|
|
||||||
U32 mFrameJitterEvents;
|
U32 mFrameJitterEvents = 0;
|
||||||
|
U32 mFrameJitterEventsLastMinute = 0;
|
||||||
|
U32 mEventMinutes = 0;
|
||||||
|
F64Seconds mTotalTime;
|
||||||
|
|
||||||
F64Seconds mLastFrameTimeSample; // used for frame time stats
|
F64Seconds mLastFrameTimeSample; // used for frame time stats
|
||||||
|
F64Seconds mTimeSinceLastEventSample;
|
||||||
std::vector<F64Seconds> mFrameTimes; // used for frame time stats
|
std::vector<F64Seconds> mFrameTimes; // used for frame time stats
|
||||||
std::vector<F64Seconds> mFrameTimesJitter; // used for frame time jitter stats
|
std::vector<F64Seconds> mFrameTimesJitter; // used for frame time jitter stats
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -2291,13 +2291,13 @@ void LLViewerWindow::initWorldUI()
|
||||||
url = LLWeb::expandURLSubstitutions(url, LLSD());
|
url = LLWeb::expandURLSubstitutions(url, LLSD());
|
||||||
destinations->navigateTo(url, HTTP_CONTENT_TEXT_HTML);
|
destinations->navigateTo(url, HTTP_CONTENT_TEXT_HTML);
|
||||||
}
|
}
|
||||||
LLMediaCtrl* avatar_picker = LLFloaterReg::getInstance("avatar")->findChild<LLMediaCtrl>("avatar_picker_contents");
|
LLMediaCtrl* avatar_welcome_pack = LLFloaterReg::getInstance("avatar_welcome_pack")->findChild<LLMediaCtrl>("avatar_picker_contents");
|
||||||
if (avatar_picker)
|
if (avatar_welcome_pack)
|
||||||
{
|
{
|
||||||
avatar_picker->setErrorPageURL(gSavedSettings.getString("GenericErrorPageURL"));
|
avatar_welcome_pack->setErrorPageURL(gSavedSettings.getString("GenericErrorPageURL"));
|
||||||
std::string url = gSavedSettings.getString("AvatarPickerURL");
|
std::string url = gSavedSettings.getString("AvatarWelcomePack");
|
||||||
url = LLWeb::expandURLSubstitutions(url, LLSD());
|
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" 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="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_Off" file_name="widgets/Stepper_Down_Off.png" preload="false" />
|
||||||
<texture name="Stepper_Down_Press" file_name="widgets/Stepper_Down_Press.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">
|
legacy_header_height="25">
|
||||||
|
|
||||||
<string name="status_idle"></string>
|
<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_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_material_mismatch">Error: Material of model is not a subset of reference model.</string>
|
||||||
<string name="status_reading_file">Loading...</string>
|
<string name="status_reading_file">Loading...</string>
|
||||||
|
|
@ -39,12 +39,14 @@
|
||||||
<string name="decomposing">Analyzing...</string>
|
<string name="decomposing">Analyzing...</string>
|
||||||
<string name="simplifying">Simplifying...</string>
|
<string name="simplifying">Simplifying...</string>
|
||||||
<string name="tbd">TBD</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-->
|
<!-- Warnings and info from model loader-->
|
||||||
<string name="TooManyJoint">Skinning disabled due to too many joints: [JOINTS], maximum: [MAX]</string>
|
<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="UnrecognizedJoint">Rigged to unrecognized joint name [NAME]</string>
|
||||||
<string name="UnknownJoints">Skinning disabled due to [COUNT] unknown joints</string>
|
<string name="UnknownJoints">Skinning disabled due to [COUNT] unknown joints</string>
|
||||||
<string name="ModelLoaded">Model [MODEL_NAME] loaded</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="IncompleteTC">Texture coordinates data is not complete.</string>
|
||||||
<string name="PositionNaN">Found NaN while loading position data from DAE-Model, invalid model.</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="ParsingErrorNoRoot">Document has no root</string>
|
||||||
<string name="ParsingErrorNoScene">Document has no visual_scene</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="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
|
<panel
|
||||||
follows="top|left"
|
follows="top|left"
|
||||||
|
|
@ -1404,7 +1427,7 @@
|
||||||
word_wrap="true">
|
word_wrap="true">
|
||||||
</text_editor>
|
</text_editor>
|
||||||
<check_box
|
<check_box
|
||||||
control_name="ImporterDebug"
|
control_name="ImporterDebugVerboseLogging"
|
||||||
follows="top|left"
|
follows="top|left"
|
||||||
top_pad="9"
|
top_pad="9"
|
||||||
left="6"
|
left="6"
|
||||||
|
|
@ -1706,7 +1729,6 @@ Analysed:
|
||||||
height="408"/>
|
height="408"/>
|
||||||
<panel
|
<panel
|
||||||
follows="right|bottom"
|
follows="right|bottom"
|
||||||
can_resize="false"
|
|
||||||
height="140"
|
height="140"
|
||||||
layout="topleft"
|
layout="topleft"
|
||||||
name="right_panel"
|
name="right_panel"
|
||||||
|
|
|
||||||
|
|
@ -54,38 +54,19 @@
|
||||||
label="jitter"
|
label="jitter"
|
||||||
decimal_digits="1"
|
decimal_digits="1"
|
||||||
stat="frametimejitter"/>
|
stat="frametimejitter"/>
|
||||||
<stat_bar name="framet_cumulative"
|
<stat_bar name="normalized_cumulative_frametime"
|
||||||
label="jitter cumulative"
|
label="normalized sess. jitter"
|
||||||
decimal_digits="1"
|
decimal_digits="4"
|
||||||
stat="frametimejitcumulative"/>
|
stat="normalizedframetimejitter"/>
|
||||||
<stat_bar name="framet_jitter_99th"
|
<stat_bar name="frame_events_per_minute"
|
||||||
label="jitter 99th percentile"
|
label="frame events/minute"
|
||||||
decimal_digits="1"
|
decimal_digits="2"
|
||||||
stat="frametimejitter99"/>
|
stat="frametimeeventspm"/>
|
||||||
<stat_bar name="framet_jitter_95th"
|
<stat_bar name="frame_events_last_minute"
|
||||||
label="jitter 95th percentile"
|
label="frame events last min."
|
||||||
decimal_digits="1"
|
decimal_digits="0"
|
||||||
stat="frametimejitter95"/>
|
stat="frametimeeventslastmin"/>
|
||||||
<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="bandwidth"
|
<stat_bar name="bandwidth"
|
||||||
label="UDP Data Received"
|
label="UDP Data Received"
|
||||||
stat="activemessagedatareceived"
|
stat="activemessagedatareceived"
|
||||||
|
|
@ -106,6 +87,38 @@
|
||||||
<stat_view name="render"
|
<stat_view name="render"
|
||||||
label="Render"
|
label="Render"
|
||||||
setting="OpenDebugStatRender">
|
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"
|
<stat_bar name="ktrisframe"
|
||||||
label="KTris per Frame"
|
label="KTris per Frame"
|
||||||
unit_label="ktris/fr"
|
unit_label="ktris/fr"
|
||||||
|
|
|
||||||
|
|
@ -411,11 +411,11 @@
|
||||||
</menu_item_call>
|
</menu_item_call>
|
||||||
<menu_item_separator/>
|
<menu_item_separator/>
|
||||||
<menu_item_call
|
<menu_item_call
|
||||||
label="Complete avatars..."
|
label="Avatar Welcome Pack..."
|
||||||
name="Avatar Picker">
|
name="Avatar Welcome Pack">
|
||||||
<menu_item_call.on_click
|
<menu_item_call.on_click
|
||||||
function="Floater.ToggleOrBringToFront"
|
function="Floater.ToggleOrBringToFront"
|
||||||
parameter="avatar" />
|
parameter="avatar_welcome_pack" />
|
||||||
</menu_item_call>
|
</menu_item_call>
|
||||||
<menu_item_separator/>
|
<menu_item_separator/>
|
||||||
|
|
||||||
|
|
@ -2808,11 +2808,17 @@ function="World.EnvPreset"
|
||||||
function="Advanced.ForceErrorWorkQueueCrash" />
|
function="Advanced.ForceErrorWorkQueueCrash" />
|
||||||
</menu_item_call>
|
</menu_item_call>
|
||||||
<menu_item_call
|
<menu_item_call
|
||||||
label="Force a Crash in a Thread"
|
label="Force an LLError Crash in a Thread"
|
||||||
name="Force a Crash in a Thread">
|
name="Force an LLError Crash in a Thread">
|
||||||
<menu_item_call.on_click
|
<menu_item_call.on_click
|
||||||
function="Advanced.ForceErrorThreadCrash" />
|
function="Advanced.ForceErrorThreadCrash" />
|
||||||
</menu_item_call>
|
</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
|
<menu_item_call
|
||||||
label="Force Disconnect Viewer"
|
label="Force Disconnect Viewer"
|
||||||
name="Force Disconnect Viewer">
|
name="Force Disconnect Viewer">
|
||||||
|
|
|
||||||
|
|
@ -7135,6 +7135,20 @@ You don't have permission to view this notecard.
|
||||||
<tag>fail</tag>
|
<tag>fail</tag>
|
||||||
</notification>
|
</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
|
<notification
|
||||||
icon="notifytip.tga"
|
icon="notifytip.tga"
|
||||||
name="RezItemNoPermissions"
|
name="RezItemNoPermissions"
|
||||||
|
|
@ -9453,8 +9467,11 @@ Unable to upload texture: '[NAME]'
|
||||||
icon="alertmodal.tga"
|
icon="alertmodal.tga"
|
||||||
name="CannotUploadMaterial"
|
name="CannotUploadMaterial"
|
||||||
type="alertmodal">
|
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>
|
<tag>fail</tag>
|
||||||
|
<usetemplate
|
||||||
|
name="okbutton"
|
||||||
|
yestext="OK"/>
|
||||||
</notification>
|
</notification>
|
||||||
|
|
||||||
<notification
|
<notification
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,31 @@
|
||||||
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||||
<panel
|
<panel
|
||||||
border="true"
|
border="true"
|
||||||
follows="left|top|right|bottom"
|
follows="left|top|right|bottom"
|
||||||
height="408"
|
height="438"
|
||||||
label="Communication"
|
label="Privacy"
|
||||||
layout="topleft"
|
layout="topleft"
|
||||||
left="102"
|
left="102"
|
||||||
name="im"
|
name="Privacy panel"
|
||||||
top="1"
|
top="1"
|
||||||
width="517">
|
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
|
<panel.string
|
||||||
name="log_in_to_change">
|
name="log_in_to_change">
|
||||||
log in to change
|
log in to change
|
||||||
|
|
@ -134,3 +150,48 @@
|
||||||
(People and/or Objects you have blocked)
|
(People and/or Objects you have blocked)
|
||||||
</text>
|
</text>
|
||||||
</panel>
|
</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
|
<item
|
||||||
label="Anyone's objects"
|
label="Anyone's objects"
|
||||||
name="media_first_interact_any"
|
name="media_first_interact_any"
|
||||||
value="1073741823"/>
|
value="32767"/>
|
||||||
<item
|
<item
|
||||||
label="All MOAP"
|
label="All MOAP"
|
||||||
name="media_first_click_all"
|
name="media_first_click_all"
|
||||||
value="2147483647"/>
|
value="65535"/>
|
||||||
</combo_box>
|
</combo_box>
|
||||||
<check_box
|
<check_box
|
||||||
name="media_show_on_others_btn"
|
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>
|
name="Command_360_Capture_Label">360 snapshot</string>
|
||||||
<string name="Command_AboutLand_Label">About land</string>
|
<string name="Command_AboutLand_Label">About land</string>
|
||||||
<string name="Command_Appearance_Label">Outfits</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_Build_Label">Build</string>
|
||||||
<string name="Command_Chat_Label">Chat</string>
|
<string name="Command_Chat_Label">Chat</string>
|
||||||
<string name="Command_Conversations_Label">Conversations</string>
|
<string name="Command_Conversations_Label">Conversations</string>
|
||||||
|
|
|
||||||
|
|
@ -556,6 +556,9 @@ class Windows_x86_64_Manifest(ViewerManifest):
|
||||||
):
|
):
|
||||||
self.path(libfile)
|
self.path(libfile)
|
||||||
|
|
||||||
|
if self.args['discord'] == 'ON':
|
||||||
|
self.path("discord_partner_sdk.dll")
|
||||||
|
|
||||||
if self.args['openal'] == 'ON':
|
if self.args['openal'] == 'ON':
|
||||||
# Get openal dll
|
# Get openal dll
|
||||||
self.path("OpenAL32.dll")
|
self.path("OpenAL32.dll")
|
||||||
|
|
@ -1018,6 +1021,13 @@ class Darwin_x86_64_Manifest(ViewerManifest):
|
||||||
):
|
):
|
||||||
self.path2basename(relpkgdir, libfile)
|
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
|
# OpenAL dylibs
|
||||||
if self.args['openal'] == 'ON':
|
if self.args['openal'] == 'ON':
|
||||||
for libfile in (
|
for libfile in (
|
||||||
|
|
@ -1384,6 +1394,7 @@ if __name__ == "__main__":
|
||||||
extra_arguments = [
|
extra_arguments = [
|
||||||
dict(name='bugsplat', description="""BugSplat database to which to post crashes,
|
dict(name='bugsplat', description="""BugSplat database to which to post crashes,
|
||||||
if BugSplat crash reporting is desired""", default=''),
|
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='openal', description="""Indication openal libraries are needed""", default='OFF'),
|
||||||
dict(name='tracy', description="""Indication tracy profiler is enabled""", default='OFF'),
|
dict(name='tracy', description="""Indication tracy profiler is enabled""", default='OFF'),
|
||||||
]
|
]
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue