summaryrefslogtreecommitdiff
path: root/indra/llprimitive
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llprimitive')
-rw-r--r--indra/llprimitive/CMakeLists.txt9
-rw-r--r--indra/llprimitive/lldaeloader.cpp45
-rw-r--r--indra/llprimitive/lldaeloader.h3
-rw-r--r--indra/llprimitive/llgltfloader.cpp402
-rw-r--r--indra/llprimitive/llgltfloader.h206
-rw-r--r--indra/llprimitive/llgltfmaterial.cpp557
-rw-r--r--indra/llprimitive/llgltfmaterial.h193
-rw-r--r--indra/llprimitive/llmaterial.cpp11
-rw-r--r--indra/llprimitive/llmaterial.h6
-rw-r--r--indra/llprimitive/llmaterialid.cpp7
-rw-r--r--indra/llprimitive/llmaterialid.h1
-rw-r--r--indra/llprimitive/llmodel.cpp45
-rw-r--r--indra/llprimitive/llmodelloader.cpp13
-rw-r--r--indra/llprimitive/llprimitive.cpp264
-rw-r--r--indra/llprimitive/llprimitive.h79
-rw-r--r--indra/llprimitive/lltextureentry.cpp19
-rw-r--r--indra/llprimitive/lltextureentry.h25
-rw-r--r--indra/llprimitive/tests/llmessagesystem_stub.cpp2
-rw-r--r--indra/llprimitive/tests/llprimitive_test.cpp40
19 files changed, 1867 insertions, 60 deletions
diff --git a/indra/llprimitive/CMakeLists.txt b/indra/llprimitive/CMakeLists.txt
index 7b6d04b096..328b22f900 100644
--- a/indra/llprimitive/CMakeLists.txt
+++ b/indra/llprimitive/CMakeLists.txt
@@ -10,15 +10,19 @@ include(LLCoreHttp)
include(LLXML)
include(LLPhysicsExtensions)
include(LLCharacter)
+include(LLRender)
+include(TinyGLTF)
include_directories(
${LLCOMMON_INCLUDE_DIRS}
${LLMATH_INCLUDE_DIRS}
${LLMESSAGE_INCLUDE_DIRS}
${LLXML_INCLUDE_DIRS}
+ ${LLRENDER_INCLUDE_DIRS}
${LIBS_PREBUILT_DIR}/include/collada
${LIBS_PREBUILT_DIR}/include/collada/1.4
${LLCHARACTER_INCLUDE_DIRS}
+ ${TINYGLTF_INCLUDE_DIR}
)
include_directories(SYSTEM
${LLCOMMON_SYSTEM_INCLUDE_DIRS}
@@ -28,6 +32,8 @@ include_directories(SYSTEM
set(llprimitive_SOURCE_FILES
lldaeloader.cpp
+ llgltfloader.cpp
+ llgltfmaterial.cpp
llmaterialid.cpp
llmaterial.cpp
llmaterialtable.cpp
@@ -46,6 +52,8 @@ set(llprimitive_SOURCE_FILES
set(llprimitive_HEADER_FILES
CMakeLists.txt
lldaeloader.h
+ llgltfloader.h
+ llgltfmaterial.h
legacy_object_types.h
llmaterial.h
llmaterialid.h
@@ -90,6 +98,7 @@ if (LL_TESTS)
INCLUDE(LLAddBuildTest)
SET(llprimitive_TEST_SOURCE_FILES
llmediaentry.cpp
+ llprimitive.cpp
)
LL_ADD_PROJECT_UNIT_TESTS(llprimitive "${llprimitive_TEST_SOURCE_FILES}")
endif (LL_TESTS)
diff --git a/indra/llprimitive/lldaeloader.cpp b/indra/llprimitive/lldaeloader.cpp
index 68654486a4..73b97cec08 100644
--- a/indra/llprimitive/lldaeloader.cpp
+++ b/indra/llprimitive/lldaeloader.cpp
@@ -2504,20 +2504,6 @@ bool LLDAELoader::addVolumeFacesFromDomMesh(LLModel* pModel,domMesh* mesh, LLSD&
return (status == LLModel::NO_ERRORS);
}
-//static
-LLModel* LLDAELoader::loadModelFromDomMesh(domMesh *mesh)
-{
- LLVolumeParams volume_params;
- volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE);
- LLModel* ret = new LLModel(volume_params, 0.f);
- createVolumeFacesFromDomMesh(ret, mesh);
- if (ret->mLabel.empty())
- {
- ret->mLabel = getElementLabel(mesh);
- }
- return ret;
-}
-
//static diff version supports creating multiple models when material counts spill
// over the 8 face server-side limit
//
@@ -2575,6 +2561,7 @@ bool LLDAELoader::loadModelsFromDomMesh(domMesh* mesh, std::vector<LLModel*>& mo
ret->trimVolumeFacesToSize(LL_SCULPT_MESH_MAX_FACES, &remainder);
+ // remove unused/redundant vertices after normalizing
if (!mNoOptimize)
{
ret->remapVolumeFaces();
@@ -2594,7 +2581,7 @@ bool LLDAELoader::loadModelsFromDomMesh(domMesh* mesh, std::vector<LLModel*>& mo
next->mLabel = model_name + (char)((int)'a' + next->mSubmodelID) + lod_suffix[mLod];
next->getVolumeFaces() = remainder;
next->mNormalizedScale = ret->mNormalizedScale;
- next->mNormalizedTranslation = ret->mNormalizedTranslation;
+
if ( ret->mMaterialList.size() > LL_SCULPT_MESH_MAX_FACES)
{
next->mMaterialList.assign(ret->mMaterialList.begin() + LL_SCULPT_MESH_MAX_FACES, ret->mMaterialList.end());
@@ -2608,31 +2595,3 @@ bool LLDAELoader::loadModelsFromDomMesh(domMesh* mesh, std::vector<LLModel*>& mo
return true;
}
-
-bool LLDAELoader::createVolumeFacesFromDomMesh(LLModel* pModel, domMesh* mesh)
-{
- if (mesh)
- {
- pModel->ClearFacesAndMaterials();
-
- LLSD placeholder;
- addVolumeFacesFromDomMesh(pModel, mesh, placeholder);
-
- if (pModel->getNumVolumeFaces() > 0)
- {
- pModel->normalizeVolumeFaces();
- pModel->optimizeVolumeFaces();
-
- if (pModel->getNumVolumeFaces() > 0)
- {
- return true;
- }
- }
- }
- else
- {
- LL_WARNS() << "no mesh found" << LL_ENDL;
- }
-
- return false;
-}
diff --git a/indra/llprimitive/lldaeloader.h b/indra/llprimitive/lldaeloader.h
index 2b211343e1..52ad908870 100644
--- a/indra/llprimitive/lldaeloader.h
+++ b/indra/llprimitive/lldaeloader.h
@@ -90,9 +90,6 @@ protected:
bool verifyController( domController* pController );
static bool addVolumeFacesFromDomMesh(LLModel* model, domMesh* mesh, LLSD& log_msg);
- static bool createVolumeFacesFromDomMesh(LLModel* model, domMesh *mesh);
-
- static LLModel* loadModelFromDomMesh(domMesh* mesh);
// Loads a mesh breaking it into one or more models as necessary
// to get around volume face limitations while retaining >8 materials
diff --git a/indra/llprimitive/llgltfloader.cpp b/indra/llprimitive/llgltfloader.cpp
new file mode 100644
index 0000000000..6041c9c273
--- /dev/null
+++ b/indra/llprimitive/llgltfloader.cpp
@@ -0,0 +1,402 @@
+/**
+ * @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 <boost/lexical_cast.hpp>
+
+#include "llsdserialize.h"
+#include "lljoint.h"
+
+#include "glh/glh_linear.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> &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;
+
+ // 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.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, norm_idx, tan_idx, uv0_idx, uv1_idx, color0_idx, color1_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
+ }
+
+ 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;
+
+ 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 = 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;
+}
diff --git a/indra/llprimitive/llgltfloader.h b/indra/llprimitive/llgltfloader.h
new file mode 100644
index 0000000000..b4d6ca1940
--- /dev/null
+++ b/indra/llprimitive/llgltfloader.h
@@ -0,0 +1,206 @@
+/**
+ * @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> &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
diff --git a/indra/llprimitive/llgltfmaterial.cpp b/indra/llprimitive/llgltfmaterial.cpp
new file mode 100644
index 0000000000..7b6612b90a
--- /dev/null
+++ b/indra/llprimitive/llgltfmaterial.cpp
@@ -0,0 +1,557 @@
+/**
+ * @file llgltfmaterial.cpp
+ * @brief Material 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$
+ */
+
+#include "linden_common.h"
+
+#include "llgltfmaterial.h"
+
+#include "tinygltf/tiny_gltf.h"
+
+LLGLTFMaterial::LLGLTFMaterial(const LLGLTFMaterial& rhs)
+{
+ *this = rhs;
+}
+
+LLGLTFMaterial& LLGLTFMaterial::operator=(const LLGLTFMaterial& rhs)
+{
+ //have to do a manual operator= because of LLRefCount
+ mBaseColorId = rhs.mBaseColorId;
+ mNormalId = rhs.mNormalId;
+ mMetallicRoughnessId = rhs.mMetallicRoughnessId;
+ mEmissiveId = rhs.mEmissiveId;
+
+ mBaseColor = rhs.mBaseColor;
+ mEmissiveColor = rhs.mEmissiveColor;
+
+ mMetallicFactor = rhs.mMetallicFactor;
+ mRoughnessFactor = rhs.mRoughnessFactor;
+ mAlphaCutoff = rhs.mAlphaCutoff;
+
+ mDoubleSided = rhs.mDoubleSided;
+ mAlphaMode = rhs.mAlphaMode;
+
+ mTextureTransform = rhs.mTextureTransform;
+
+ return *this;
+}
+
+bool LLGLTFMaterial::fromJSON(const std::string& json, std::string& warn_msg, std::string& error_msg)
+{
+#if 1
+ tinygltf::TinyGLTF gltf;
+
+ tinygltf::Model model_in;
+
+ if (gltf.LoadASCIIFromString(&model_in, &error_msg, &warn_msg, json.c_str(), json.length(), ""))
+ {
+ setFromModel(model_in, 0);
+
+ //DEBUG generate json and print
+ LL_INFOS() << asJSON(true) << LL_ENDL;
+
+ return true;
+ }
+#endif
+ return false;
+}
+
+std::string LLGLTFMaterial::asJSON(bool prettyprint) const
+{
+#if 1
+ tinygltf::TinyGLTF gltf;
+ tinygltf::Model model_out;
+
+ std::ostringstream str;
+
+ writeToModel(model_out, 0);
+
+ gltf.WriteGltfSceneToStream(&model_out, str, prettyprint, false);
+
+ return str.str();
+#else
+ return "";
+#endif
+}
+
+void LLGLTFMaterial::setFromModel(const tinygltf::Model& model, S32 mat_index)
+{
+ if (model.materials.size() <= mat_index)
+ {
+ return;
+ }
+
+ const tinygltf::Material& material_in = model.materials[mat_index];
+
+ // get base color texture
+ S32 tex_index = material_in.pbrMetallicRoughness.baseColorTexture.index;
+ if (tex_index >= 0)
+ {
+ mBaseColorId.set(model.images[tex_index].uri);
+ }
+ else
+ {
+ mBaseColorId.setNull();
+ }
+
+ // get normal map
+ tex_index = material_in.normalTexture.index;
+ if (tex_index >= 0)
+ {
+ mNormalId.set(model.images[tex_index].uri);
+ }
+ else
+ {
+ mNormalId.setNull();
+ }
+
+ // get metallic-roughness texture
+ tex_index = material_in.pbrMetallicRoughness.metallicRoughnessTexture.index;
+ if (tex_index >= 0)
+ {
+ mMetallicRoughnessId.set(model.images[tex_index].uri);
+ }
+ else
+ {
+ mMetallicRoughnessId.setNull();
+ }
+
+ // get emissive texture
+ tex_index = material_in.emissiveTexture.index;
+ if (tex_index >= 0)
+ {
+ mEmissiveId.set(model.images[tex_index].uri);
+ }
+ else
+ {
+ mEmissiveId.setNull();
+ }
+
+ setAlphaMode(material_in.alphaMode);
+ mAlphaCutoff = llclamp((F32)material_in.alphaCutoff, 0.f, 1.f);
+
+ mBaseColor.set(material_in.pbrMetallicRoughness.baseColorFactor);
+ mEmissiveColor.set(material_in.emissiveFactor);
+
+ mMetallicFactor = llclamp((F32)material_in.pbrMetallicRoughness.metallicFactor, 0.f, 1.f);
+ mRoughnessFactor = llclamp((F32)material_in.pbrMetallicRoughness.roughnessFactor, 0.f, 1.f);
+
+ mDoubleSided = material_in.doubleSided;
+}
+
+void LLGLTFMaterial::writeToModel(tinygltf::Model& model, S32 mat_index) const
+{
+ if (model.materials.size() < mat_index + 1)
+ {
+ model.materials.resize(mat_index + 1);
+ }
+
+ tinygltf::Material& material_out = model.materials[mat_index];
+
+ // set base color texture
+ if (mBaseColorId.notNull())
+ {
+ U32 idx = model.images.size();
+ model.images.resize(idx + 1);
+ model.textures.resize(idx + 1);
+
+ material_out.pbrMetallicRoughness.baseColorTexture.index = idx;
+ model.textures[idx].source = idx;
+ model.images[idx].uri = mBaseColorId.asString();
+ }
+
+ // set normal texture
+ if (mNormalId.notNull())
+ {
+ U32 idx = model.images.size();
+ model.images.resize(idx + 1);
+ model.textures.resize(idx + 1);
+
+ material_out.normalTexture.index = idx;
+ model.textures[idx].source = idx;
+ model.images[idx].uri = mNormalId.asString();
+ }
+
+ // set metallic-roughness texture
+ if (mMetallicRoughnessId.notNull())
+ {
+ U32 idx = model.images.size();
+ model.images.resize(idx + 1);
+ model.textures.resize(idx + 1);
+
+ material_out.pbrMetallicRoughness.metallicRoughnessTexture.index = idx;
+ model.textures[idx].source = idx;
+ model.images[idx].uri = mMetallicRoughnessId.asString();
+ }
+
+ // set emissive texture
+ if (mEmissiveId.notNull())
+ {
+ U32 idx = model.images.size();
+ model.images.resize(idx + 1);
+ model.textures.resize(idx + 1);
+
+ material_out.emissiveTexture.index = idx;
+ model.textures[idx].source = idx;
+ model.images[idx].uri = mEmissiveId.asString();
+ }
+
+ material_out.alphaMode = getAlphaMode();
+ material_out.alphaCutoff = mAlphaCutoff;
+
+ mBaseColor.write(material_out.pbrMetallicRoughness.baseColorFactor);
+ mEmissiveColor.write(material_out.emissiveFactor);
+
+ material_out.pbrMetallicRoughness.metallicFactor = mMetallicFactor;
+ material_out.pbrMetallicRoughness.roughnessFactor = mRoughnessFactor;
+
+ material_out.doubleSided = mDoubleSided;
+
+ model.asset.version = "2.0";
+}
+
+
+void LLGLTFMaterial::setBaseColorId(const LLUUID& id)
+{
+ mBaseColorId = id;
+}
+
+void LLGLTFMaterial::setNormalId(const LLUUID& id)
+{
+ mNormalId = id;
+}
+
+void LLGLTFMaterial::setMetallicRoughnessId(const LLUUID& id)
+{
+ mMetallicRoughnessId = id;
+}
+
+void LLGLTFMaterial::setEmissiveId(const LLUUID& id)
+{
+ mEmissiveId = id;
+}
+
+void LLGLTFMaterial::setBaseColorFactor(const LLColor3& baseColor, F32 transparency)
+{
+ mBaseColor.set(baseColor, transparency);
+ mBaseColor.clamp();
+}
+
+void LLGLTFMaterial::setAlphaCutoff(F32 cutoff)
+{
+ mAlphaCutoff = llclamp(cutoff, 0.f, 1.f);
+}
+
+void LLGLTFMaterial::setEmissiveColorFactor(const LLColor3& emissiveColor)
+{
+ mEmissiveColor = emissiveColor;
+ mEmissiveColor.clamp();
+}
+
+void LLGLTFMaterial::setMetallicFactor(F32 metallic)
+{
+ mMetallicFactor = llclamp(metallic, 0.f, 1.f);
+}
+
+void LLGLTFMaterial::setRoughnessFactor(F32 roughness)
+{
+ mRoughnessFactor = llclamp(roughness, 0.f, 1.f);
+}
+
+void LLGLTFMaterial::setAlphaMode(S32 mode)
+{
+ mAlphaMode = (AlphaMode) llclamp(mode, (S32) ALPHA_MODE_OPAQUE, (S32) ALPHA_MODE_MASK);
+}
+
+void LLGLTFMaterial::setDoubleSided(bool double_sided)
+{
+ // sure, no clamping will ever be needed for a bool, but include the
+ // setter for consistency with the clamping API
+ mDoubleSided = double_sided;
+}
+
+void LLGLTFMaterial::setTextureOffset(TextureInfo texture_info, const LLVector2& offset)
+{
+ mTextureTransform[texture_info].mOffset = offset;
+}
+
+void LLGLTFMaterial::setTextureScale(TextureInfo texture_info, const LLVector2& scale)
+{
+ mTextureTransform[texture_info].mScale = scale;
+}
+
+void LLGLTFMaterial::setTextureRotation(TextureInfo texture_info, float rotation)
+{
+ mTextureTransform[texture_info].mRotation = rotation;
+}
+
+// Default value accessors
+
+LLUUID LLGLTFMaterial::getDefaultBaseColorId()
+{
+ return LLUUID::null;
+}
+
+LLUUID LLGLTFMaterial::getDefaultNormalId()
+{
+ return LLUUID::null;
+}
+
+LLUUID LLGLTFMaterial::getDefaultEmissiveId()
+{
+ return LLUUID::null;
+}
+
+LLUUID LLGLTFMaterial::getDefaultMetallicRoughnessId()
+{
+ return LLUUID::null;
+}
+
+F32 LLGLTFMaterial::getDefaultAlphaCutoff()
+{
+ return 0.f;
+}
+
+S32 LLGLTFMaterial::getDefaultAlphaMode()
+{
+ return (S32) ALPHA_MODE_OPAQUE;
+}
+
+F32 LLGLTFMaterial::getDefaultMetallicFactor()
+{
+ return 0.f;
+}
+
+F32 LLGLTFMaterial::getDefaultRoughnessFactor()
+{
+ return 0.f;
+}
+
+LLColor4 LLGLTFMaterial::getDefaultBaseColor()
+{
+ return LLColor4::white;
+}
+
+LLColor3 LLGLTFMaterial::getDefaultEmissiveColor()
+{
+ return LLColor3::black;
+}
+
+bool LLGLTFMaterial::getDefaultDoubleSided()
+{
+ return false;
+}
+
+LLVector2 LLGLTFMaterial::getDefaultTextureOffset()
+{
+ return LLVector2(0.f, 0.f);
+}
+
+LLVector2 LLGLTFMaterial::getDefaultTextureScale()
+{
+ return LLVector2(1.f, 1.f);
+}
+
+F32 LLGLTFMaterial::getDefaultTextureRotation()
+{
+ return 0.f;
+}
+
+void LLGLTFMaterial::writeOverridesToModel(tinygltf::Model& model, S32 mat_index, LLGLTFMaterial const* base_material) const
+{
+ if (model.materials.size() < mat_index + 1)
+ {
+ model.materials.resize(mat_index + 1);
+ }
+
+ tinygltf::Material& material_out = model.materials[mat_index];
+
+ // TODO - fix handling of resetting to null/default values
+
+ // set base color texture
+ if (mBaseColorId.notNull() && mBaseColorId != base_material->mBaseColorId)
+ {
+ U32 idx = model.images.size();
+ model.images.resize(idx + 1);
+ model.textures.resize(idx + 1);
+
+ material_out.pbrMetallicRoughness.baseColorTexture.index = idx;
+ model.textures[idx].source = idx;
+ model.images[idx].uri = mBaseColorId.asString();
+ }
+
+ // set normal texture
+ if (mNormalId.notNull() && mNormalId != base_material->mNormalId)
+ {
+ U32 idx = model.images.size();
+ model.images.resize(idx + 1);
+ model.textures.resize(idx + 1);
+
+ material_out.normalTexture.index = idx;
+ model.textures[idx].source = idx;
+ model.images[idx].uri = mNormalId.asString();
+ }
+
+ // set metallic-roughness texture
+ if (mMetallicRoughnessId.notNull() && mMetallicRoughnessId != base_material->mMetallicRoughnessId)
+ {
+ U32 idx = model.images.size();
+ model.images.resize(idx + 1);
+ model.textures.resize(idx + 1);
+
+ material_out.pbrMetallicRoughness.metallicRoughnessTexture.index = idx;
+ model.textures[idx].source = idx;
+ model.images[idx].uri = mMetallicRoughnessId.asString();
+ }
+
+ // set emissive texture
+ if (mEmissiveId.notNull() && mEmissiveId != base_material->mEmissiveId)
+ {
+ U32 idx = model.images.size();
+ model.images.resize(idx + 1);
+ model.textures.resize(idx + 1);
+
+ material_out.emissiveTexture.index = idx;
+ model.textures[idx].source = idx;
+ model.images[idx].uri = mEmissiveId.asString();
+ }
+
+ if (mAlphaMode != base_material->mAlphaMode)
+ {
+ material_out.alphaMode = getAlphaMode();
+ }
+
+ if (mAlphaCutoff != base_material->mAlphaCutoff)
+ {
+ material_out.alphaCutoff = mAlphaCutoff;
+ }
+
+ if (mBaseColor != base_material->mBaseColor)
+ {
+ mBaseColor.write(material_out.pbrMetallicRoughness.baseColorFactor);
+ }
+
+ if (mEmissiveColor != base_material->mEmissiveColor)
+ {
+ mEmissiveColor.write(material_out.emissiveFactor);
+ }
+
+ if (mMetallicFactor != base_material->mMetallicFactor)
+ {
+ material_out.pbrMetallicRoughness.metallicFactor = mMetallicFactor;
+ }
+
+ if (mRoughnessFactor != base_material->mRoughnessFactor)
+ {
+ material_out.pbrMetallicRoughness.roughnessFactor = mRoughnessFactor;
+ }
+
+ if (mDoubleSided != base_material->mDoubleSided)
+ {
+ material_out.doubleSided = mDoubleSided;
+ }
+}
+
+void LLGLTFMaterial::applyOverride(const LLGLTFMaterial& override_mat)
+{
+ // TODO: potentially reimplement this with a more general purpose JSON merge
+
+ if (override_mat.mBaseColorId != getDefaultBaseColorId())
+ {
+ mBaseColorId = override_mat.mBaseColorId;
+ }
+
+ if (override_mat.mNormalId != getDefaultNormalId())
+ {
+ mNormalId = override_mat.mNormalId;
+ }
+
+ if (override_mat.mMetallicRoughnessId != getDefaultMetallicRoughnessId())
+ {
+ mMetallicRoughnessId = override_mat.mMetallicRoughnessId;
+ }
+
+ if (override_mat.mEmissiveId != getDefaultEmissiveId())
+ {
+ mEmissiveId = override_mat.mEmissiveId;
+ }
+
+ if (override_mat.mBaseColor != getDefaultBaseColor())
+ {
+ mBaseColor = override_mat.mBaseColor;
+ }
+
+ if (override_mat.mEmissiveColor != getDefaultEmissiveColor())
+ {
+ mEmissiveColor = override_mat.mEmissiveColor;
+ }
+
+ if (override_mat.mMetallicFactor != getDefaultMetallicFactor())
+ {
+ mMetallicFactor = override_mat.mMetallicFactor;
+ }
+
+ if (override_mat.mRoughnessFactor != getDefaultRoughnessFactor())
+ {
+ mRoughnessFactor = override_mat.mRoughnessFactor;
+ }
+
+ if (override_mat.mAlphaMode != getDefaultAlphaMode())
+ {
+ mAlphaMode = override_mat.mAlphaMode;
+ }
+ if (override_mat.mAlphaCutoff != getDefaultAlphaCutoff())
+ {
+ mAlphaCutoff = override_mat.mAlphaCutoff;
+ }
+
+ if (override_mat.mDoubleSided != getDefaultDoubleSided())
+ {
+ mDoubleSided = override_mat.mDoubleSided;
+ }
+
+ for (int i = 0; i < GLTF_TEXTURE_INFO_COUNT; ++i)
+ {
+ if (override_mat.mTextureTransform[i].mOffset != getDefaultTextureOffset())
+ {
+ mTextureTransform[i].mOffset = override_mat.mTextureTransform[i].mOffset;
+ }
+
+ if (override_mat.mTextureTransform[i].mScale != getDefaultTextureScale())
+ {
+ mTextureTransform[i].mScale = override_mat.mTextureTransform[i].mScale;
+ }
+
+ if (override_mat.mTextureTransform[i].mScale != getDefaultTextureScale())
+ {
+ mTextureTransform[i].mScale = override_mat.mTextureTransform[i].mScale;
+ }
+
+ if (override_mat.mTextureTransform[i].mRotation != getDefaultTextureRotation())
+ {
+ mTextureTransform[i].mRotation = override_mat.mTextureTransform[i].mRotation;
+ }
+ }
+}
diff --git a/indra/llprimitive/llgltfmaterial.h b/indra/llprimitive/llgltfmaterial.h
new file mode 100644
index 0000000000..ea7e402805
--- /dev/null
+++ b/indra/llprimitive/llgltfmaterial.h
@@ -0,0 +1,193 @@
+/**
+ * @file llgltfmaterial.h
+ * @brief Material 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$
+ */
+
+#pragma once
+
+#include "llrefcount.h"
+#include "llmemory.h"
+#include "v4color.h"
+#include "v3color.h"
+#include "v2math.h"
+#include "lluuid.h"
+#include "llmd5.h"
+
+#include <string>
+
+namespace tinygltf
+{
+ class Model;
+}
+
+class LLGLTFMaterial : public LLRefCount
+{
+public:
+
+ struct TextureTransform
+ {
+ LLVector2 mOffset = { 0.f, 0.f };
+ LLVector2 mScale = { 1.f, 1.f };
+ F32 mRotation = 0.f;
+ };
+
+ enum AlphaMode
+ {
+ ALPHA_MODE_OPAQUE = 0,
+ ALPHA_MODE_BLEND,
+ ALPHA_MODE_MASK
+ };
+
+ LLGLTFMaterial() {}
+ LLGLTFMaterial(const LLGLTFMaterial& rhs);
+
+ LLGLTFMaterial& operator=(const LLGLTFMaterial& rhs);
+
+ LLUUID mBaseColorId;
+ LLUUID mNormalId;
+ LLUUID mMetallicRoughnessId;
+ LLUUID mEmissiveId;
+
+ LLColor4 mBaseColor = LLColor4(1, 1, 1, 1);
+ LLColor3 mEmissiveColor = LLColor3(0, 0, 0);
+
+ F32 mMetallicFactor = 0.f;
+ F32 mRoughnessFactor = 0.f;
+ F32 mAlphaCutoff = 0.f;
+
+ bool mDoubleSided = false;
+ AlphaMode mAlphaMode = ALPHA_MODE_OPAQUE;
+
+ // get a UUID based on a hash of this LLGLTFMaterial
+ LLUUID getHash() const
+ {
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
+ LLMD5 md5;
+ md5.update((unsigned char*)this, sizeof(this));
+ md5.finalize();
+ LLUUID id;
+ md5.raw_digest(id.mData);
+ // *TODO: Hash the overrides
+ return id;
+ }
+
+ enum TextureInfo : U32
+ {
+ GLTF_TEXTURE_INFO_BASE_COLOR,
+ GLTF_TEXTURE_INFO_NORMAL,
+ GLTF_TEXTURE_INFO_METALLIC_ROUGHNESS,
+ GLTF_TEXTURE_INFO_EMISSIVE,
+
+ GLTF_TEXTURE_INFO_COUNT
+ };
+
+ std::array<TextureTransform, GLTF_TEXTURE_INFO_COUNT> mTextureTransform;
+
+ //setters for various members (will clamp to acceptable ranges)
+
+ void setBaseColorId(const LLUUID& id);
+ void setNormalId(const LLUUID& id);
+ void setMetallicRoughnessId(const LLUUID& id);
+ void setEmissiveId(const LLUUID& id);
+
+ void setBaseColorFactor(const LLColor3& baseColor, F32 transparency);
+ void setAlphaCutoff(F32 cutoff);
+ void setEmissiveColorFactor(const LLColor3& emissiveColor);
+ void setMetallicFactor(F32 metallic);
+ void setRoughnessFactor(F32 roughness);
+ void setAlphaMode(S32 mode);
+ void setDoubleSided(bool double_sided);
+ void setTextureOffset(TextureInfo texture_info, const LLVector2& offset);
+ void setTextureScale(TextureInfo texture_info, const LLVector2& scale);
+ void setTextureRotation(TextureInfo texture_info, float rotation);
+
+ // Default value accessors
+ static LLUUID getDefaultBaseColorId();
+ static LLUUID getDefaultNormalId();
+ static LLUUID getDefaultEmissiveId();
+ static LLUUID getDefaultMetallicRoughnessId();
+ static F32 getDefaultAlphaCutoff();
+ static S32 getDefaultAlphaMode();
+ static F32 getDefaultMetallicFactor();
+ static F32 getDefaultRoughnessFactor();
+ static LLColor4 getDefaultBaseColor();
+ static LLColor3 getDefaultEmissiveColor();
+ static bool getDefaultDoubleSided();
+ static LLVector2 getDefaultTextureOffset();
+ static LLVector2 getDefaultTextureScale();
+ static F32 getDefaultTextureRotation();
+
+ // set mAlphaMode from string.
+ // Anything otherthan "MASK" or "BLEND" sets mAlphaMode to ALPHA_MODE_OPAQUE
+ void setAlphaMode(const std::string& mode)
+ {
+ if (mode == "MASK")
+ {
+ mAlphaMode = ALPHA_MODE_MASK;
+ }
+ else if (mode == "BLEND")
+ {
+ mAlphaMode = ALPHA_MODE_BLEND;
+ }
+ else
+ {
+ mAlphaMode = ALPHA_MODE_OPAQUE;
+ }
+ }
+
+ const char* getAlphaMode() const
+ {
+ switch (mAlphaMode)
+ {
+ case ALPHA_MODE_MASK: return "MASK";
+ case ALPHA_MODE_BLEND: return "BLEND";
+ default: return "OPAQUE";
+ }
+ }
+
+ // set the contents of this LLGLTFMaterial from the given json
+ // returns true if successful
+ // json - the json text to load from
+ // warn_msg - warning message from TinyGLTF if any
+ // error_msg - error_msg from TinyGLTF if any
+ bool fromJSON(const std::string& json, std::string& warn_msg, std::string& error_msg);
+
+ // get the contents of this LLGLTFMaterial as a json string
+ std::string asJSON(bool prettyprint = false) const;
+
+ // initialize from given tinygltf::Model
+ // model - the model to reference
+ // mat_index - index of material in model's material array
+ void setFromModel(const tinygltf::Model& model, S32 mat_index);
+
+ // write to given tinygltf::Model
+ void writeToModel(tinygltf::Model& model, S32 mat_index) const;
+
+ // calculate the fields in this material that differ from a base material and write them out to a given tinygltf::Model
+ void writeOverridesToModel(tinygltf::Model& model, S32 mat_index, LLGLTFMaterial const* base_material) const;
+
+ void applyOverride(const LLGLTFMaterial& override_mat);
+
+};
+
diff --git a/indra/llprimitive/llmaterial.cpp b/indra/llprimitive/llmaterial.cpp
index a219ac1450..f546ac1645 100644
--- a/indra/llprimitive/llmaterial.cpp
+++ b/indra/llprimitive/llmaterial.cpp
@@ -331,6 +331,17 @@ void LLMaterial::setAlphaMaskCutoff(U8 cutoff)
mAlphaMaskCutoff = cutoff;
}
+LLUUID LLMaterial::getMaterialID() const
+{
+ // TODO - not null
+ return LLUUID::null;
+}
+
+void LLMaterial::setMaterialID(const LLUUID &material_id)
+{
+ // TODO - set
+}
+
LLSD LLMaterial::asLLSD() const
{
LLSD material_data;
diff --git a/indra/llprimitive/llmaterial.h b/indra/llprimitive/llmaterial.h
index d58b7ee812..2f8aafc2cf 100644
--- a/indra/llprimitive/llmaterial.h
+++ b/indra/llprimitive/llmaterial.h
@@ -27,8 +27,6 @@
#ifndef LL_LLMATERIAL_H
#define LL_LLMATERIAL_H
-#include <boost/shared_ptr.hpp>
-
#include "llmaterialid.h"
#include "llsd.h"
#include "v4coloru.h"
@@ -54,8 +52,6 @@ public:
ALPHA_SHADER_COUNT = 4
} eShaderCount;
-
-
static const U8 DEFAULT_SPECULAR_LIGHT_EXPONENT = ((U8)(0.2f * 255));
static const LLColor4U DEFAULT_SPECULAR_LIGHT_COLOR;
static const U8 DEFAULT_ENV_INTENSITY = 0;
@@ -119,6 +115,8 @@ public:
void setDiffuseAlphaMode(U8 alpha_mode);
U8 getAlphaMaskCutoff() const;
void setAlphaMaskCutoff(U8 cutoff);
+ LLUUID getMaterialID() const;
+ void setMaterialID(LLUUID const & material_id);
bool isNull() const;
static const LLMaterial null;
diff --git a/indra/llprimitive/llmaterialid.cpp b/indra/llprimitive/llmaterialid.cpp
index f88a607c4f..340a83801c 100644
--- a/indra/llprimitive/llmaterialid.cpp
+++ b/indra/llprimitive/llmaterialid.cpp
@@ -155,6 +155,13 @@ std::string LLMaterialID::asString() const
return materialIDString;
}
+LLUUID LLMaterialID::asUUID() const
+{
+ LLUUID ret;
+ memcpy(ret.mData, mID, MATERIAL_ID_SIZE);
+ return ret;
+}
+
std::ostream& operator<<(std::ostream& s, const LLMaterialID &material_id)
{
s << material_id.asString();
diff --git a/indra/llprimitive/llmaterialid.h b/indra/llprimitive/llmaterialid.h
index ee663f8f99..e6165dfc91 100644
--- a/indra/llprimitive/llmaterialid.h
+++ b/indra/llprimitive/llmaterialid.h
@@ -61,6 +61,7 @@ public:
LLSD asLLSD() const;
std::string asString() const;
+ LLUUID asUUID() const;
friend std::ostream& operator<<(std::ostream& s, const LLMaterialID &material_id);
diff --git a/indra/llprimitive/llmodel.cpp b/indra/llprimitive/llmodel.cpp
index 805af55299..206142fdd5 100644
--- a/indra/llprimitive/llmodel.cpp
+++ b/indra/llprimitive/llmodel.cpp
@@ -53,7 +53,6 @@ const int MODEL_NAMES_LENGTH = sizeof(model_names) / sizeof(std::string);
LLModel::LLModel(LLVolumeParams& params, F32 detail)
: LLVolume(params, detail),
mNormalizedScale(1,1,1),
- mNormalizedTranslation(0,0,0),
mPelvisOffset( 0.0f ),
mStatus(NO_ERRORS),
mSubmodelID(0)
@@ -296,6 +295,7 @@ void LLModel::normalizeVolumeFaces()
// the positions to fit within the unit cube.
LLVector4a* pos = (LLVector4a*) face.mPositions;
LLVector4a* norm = (LLVector4a*) face.mNormals;
+ LLVector4a* t = (LLVector4a*)face.mTangents;
for (U32 j = 0; j < face.mNumVertices; ++j)
{
@@ -306,6 +306,14 @@ void LLModel::normalizeVolumeFaces()
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;
+ }
}
}
@@ -319,6 +327,12 @@ void LLModel::normalizeVolumeFaces()
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;
+ }
}
}
@@ -728,10 +742,12 @@ LLSD LLModel::writeModel(
LLSD::Binary verts(face.mNumVertices*3*2);
LLSD::Binary tc(face.mNumVertices*2*2);
LLSD::Binary normals(face.mNumVertices*3*2);
+ LLSD::Binary tangents(face.mNumVertices * 4 * 2);
LLSD::Binary indices(face.mNumIndices*2);
U32 vert_idx = 0;
U32 norm_idx = 0;
+ //U32 tan_idx = 0;
U32 tc_idx = 0;
LLVector2* ftc = (LLVector2*) face.mTexCoords;
@@ -784,6 +800,24 @@ LLSD LLModel::writeModel(
normals[norm_idx++] = buff[1];
}
}
+
+#if 0 // keep this code for now in case we want to support transporting tangents with mesh assets
+ if (face.mTangents)
+ { //normals
+ F32* tangent = face.mTangents[j].getF32ptr();
+
+ for (U32 k = 0; k < 4; ++k)
+ { //for each component
+ //convert to 16-bit normalized
+ U16 val = (U16)((tangent[k] + 1.f) * 0.5f * 65535);
+ U8* buff = (U8*)&val;
+
+ //write to binary buffer
+ tangents[tan_idx++] = buff[0];
+ tangents[tan_idx++] = buff[1];
+ }
+ }
+#endif
//texcoord
if (face.mTexCoords)
@@ -814,6 +848,8 @@ LLSD LLModel::writeModel(
//write out face data
mdl[model_names[idx]][i]["PositionDomain"]["Min"] = min_pos.getValue();
mdl[model_names[idx]][i]["PositionDomain"]["Max"] = max_pos.getValue();
+ mdl[model_names[idx]][i]["NormalizedScale"] = face.mNormalizedScale.getValue();
+
mdl[model_names[idx]][i]["Position"] = verts;
if (face.mNormals)
@@ -821,6 +857,13 @@ LLSD LLModel::writeModel(
mdl[model_names[idx]][i]["Normal"] = normals;
}
+#if 0 // keep this code for now in case we decide to transport tangents with mesh assets
+ if (face.mTangents)
+ {
+ mdl[model_names[idx]][i]["Tangent"] = tangents;
+ }
+#endif
+
if (face.mTexCoords)
{
mdl[model_names[idx]][i]["TexCoord0Domain"]["Min"] = min_tc.getValue();
diff --git a/indra/llprimitive/llmodelloader.cpp b/indra/llprimitive/llmodelloader.cpp
index 5171621007..554ed54de1 100644
--- a/indra/llprimitive/llmodelloader.cpp
+++ b/indra/llprimitive/llmodelloader.cpp
@@ -160,7 +160,8 @@ bool LLModelLoader::getSLMFilename(const std::string& model_filename, std::strin
std::string::size_type i = model_filename.rfind(".");
if (i != std::string::npos)
{
- slm_filename.replace(i, model_filename.size()-1, ".slm");
+ slm_filename.resize(i, '\0');
+ slm_filename.append(".slm");
return true;
}
else
@@ -172,7 +173,7 @@ bool LLModelLoader::getSLMFilename(const std::string& model_filename, std::strin
bool LLModelLoader::doLoadModel()
{
//first, look for a .slm file of the same name that was modified later
- //than the .dae
+ //than the specified model file
if (mTrySLM)
{
@@ -182,13 +183,13 @@ bool LLModelLoader::doLoadModel()
llstat slm_status;
if (LLFile::stat(slm_filename, &slm_status) == 0)
{ //slm file exists
- llstat dae_status;
- if (LLFile::stat(mFilename, &dae_status) != 0 ||
- dae_status.st_mtime < slm_status.st_mtime)
+ llstat model_file_status;
+ if (LLFile::stat(mFilename, &model_file_status) != 0 ||
+ model_file_status.st_mtime < slm_status.st_mtime)
{
if (loadFromSLM(slm_filename))
{ //slm successfully loaded, if this fails, fall through and
- //try loading from dae
+ //try loading from model file
mLod = -1; //successfully loading from an slm implicitly sets all
//LoDs
diff --git a/indra/llprimitive/llprimitive.cpp b/indra/llprimitive/llprimitive.cpp
index 67c225d25d..8b470d235c 100644
--- a/indra/llprimitive/llprimitive.cpp
+++ b/indra/llprimitive/llprimitive.cpp
@@ -39,6 +39,7 @@
#include "llsdutil_math.h"
#include "llprimtexturelist.h"
#include "llmaterialid.h"
+#include "llsdutil.h"
/**
* exported constants
@@ -80,6 +81,14 @@ const F32 LIGHT_MIN_CUTOFF = 0.0f;
const F32 LIGHT_DEFAULT_CUTOFF = 0.0f;
const F32 LIGHT_MAX_CUTOFF = 180.f;
+// reflection probes
+const F32 REFLECTION_PROBE_MIN_AMBIANCE = 0.f;
+const F32 REFLECTION_PROBE_MAX_AMBIANCE = 1.f;
+const F32 REFLECTION_PROBE_DEFAULT_AMBIANCE = 0.f;
+const F32 REFLECTION_PROBE_MIN_CLIP_DISTANCE = 0.f;
+const F32 REFLECTION_PROBE_MAX_CLIP_DISTANCE = 1024.f;
+const F32 REFLECTION_PROBE_DEFAULT_CLIP_DISTANCE = 0.f;
+
// "Tension" => [0,10], increments of 0.1
const F32 FLEXIBLE_OBJECT_MIN_TENSION = 0.0f;
const F32 FLEXIBLE_OBJECT_DEFAULT_TENSION = 1.0f;
@@ -1690,6 +1699,10 @@ BOOL LLNetworkData::isValid(U16 param_type, U32 size)
return (size == 28);
case PARAMS_EXTENDED_MESH:
return (size == 4);
+ case PARAMS_RENDER_MATERIAL:
+ return (size > 1);
+ case PARAMS_REFLECTION_PROBE:
+ return (size == 9);
}
return FALSE;
@@ -1808,6 +1821,118 @@ bool LLLightParams::fromLLSD(LLSD& sd)
//============================================================================
+//============================================================================
+
+LLReflectionProbeParams::LLReflectionProbeParams()
+{
+ mType = PARAMS_REFLECTION_PROBE;
+}
+
+BOOL LLReflectionProbeParams::pack(LLDataPacker &dp) const
+{
+ dp.packF32(mAmbiance, "ambiance");
+ dp.packF32(mClipDistance, "clip_distance");
+ dp.packU8(mFlags, "flags");
+ return TRUE;
+}
+
+BOOL LLReflectionProbeParams::unpack(LLDataPacker &dp)
+{
+ F32 ambiance;
+ F32 clip_distance;
+
+ dp.unpackF32(ambiance, "ambiance");
+ setAmbiance(ambiance);
+
+ dp.unpackF32(clip_distance, "clip_distance");
+ setClipDistance(clip_distance);
+
+ dp.unpackU8(mFlags, "flags");
+
+ return TRUE;
+}
+
+bool LLReflectionProbeParams::operator==(const LLNetworkData& data) const
+{
+ if (data.mType != PARAMS_REFLECTION_PROBE)
+ {
+ return false;
+ }
+ const LLReflectionProbeParams *param = (const LLReflectionProbeParams*)&data;
+ if (param->mAmbiance != mAmbiance)
+ {
+ return false;
+ }
+ if (param->mClipDistance != mClipDistance)
+ {
+ return false;
+ }
+ if (param->mFlags != mFlags)
+ {
+ return false;
+ }
+ return true;
+}
+
+void LLReflectionProbeParams::copy(const LLNetworkData& data)
+{
+ const LLReflectionProbeParams *param = (LLReflectionProbeParams*)&data;
+ mType = param->mType;
+ mAmbiance = param->mAmbiance;
+ mClipDistance = param->mClipDistance;
+ mFlags = param->mFlags;
+}
+
+LLSD LLReflectionProbeParams::asLLSD() const
+{
+ LLSD sd;
+ sd["ambiance"] = getAmbiance();
+ sd["clip_distance"] = getClipDistance();
+ sd["flags"] = mFlags;
+ return sd;
+}
+
+bool LLReflectionProbeParams::fromLLSD(LLSD& sd)
+{
+ if (!sd.has("ambiance") ||
+ !sd.has("clip_distance") ||
+ !sd.has("flags"))
+ {
+ return false;
+ }
+
+ setAmbiance((F32)sd["ambiance"].asReal());
+ setClipDistance((F32)sd["clip_distance"].asReal());
+ mFlags = (U8) sd["flags"].asInteger();
+
+ return true;
+}
+
+void LLReflectionProbeParams::setIsBox(bool is_box)
+{
+ if (is_box)
+ {
+ mFlags |= FLAG_BOX_VOLUME;
+ }
+ else
+ {
+ mFlags &= ~FLAG_BOX_VOLUME;
+ }
+}
+
+void LLReflectionProbeParams::setIsDynamic(bool is_dynamic)
+{
+ if (is_dynamic)
+ {
+ mFlags |= FLAG_DYNAMIC;
+ }
+ else
+ {
+ mFlags &= ~FLAG_DYNAMIC;
+ }
+}
+
+//============================================================================
LLFlexibleObjectData::LLFlexibleObjectData()
{
mSimulateLOD = FLEXIBLE_OBJECT_DEFAULT_NUM_SECTIONS;
@@ -2181,3 +2306,142 @@ bool LLExtendedMeshParams::fromLLSD(LLSD& sd)
return false;
}
+
+//============================================================================
+
+LLRenderMaterialParams::LLRenderMaterialParams()
+{
+ mType = PARAMS_RENDER_MATERIAL;
+}
+
+BOOL LLRenderMaterialParams::pack(LLDataPacker& dp) const
+{
+ U8 count = (U8)llmin((S32)mEntries.size(), 14); //limited to 255 bytes, no more than 14 material ids
+
+ dp.packU8(count, "count");
+ for (auto& entry : mEntries)
+ {
+ dp.packU8(entry.te_idx, "te_idx");
+ dp.packUUID(entry.id, "id");
+ }
+
+ return TRUE;
+}
+
+BOOL LLRenderMaterialParams::unpack(LLDataPacker& dp)
+{
+ U8 count;
+ dp.unpackU8(count, "count");
+ mEntries.resize(count);
+ for (auto& entry : mEntries)
+ {
+ dp.unpackU8(entry.te_idx, "te_idx");
+ dp.unpackUUID(entry.id, "te_id");
+ }
+
+ return TRUE;
+}
+
+bool LLRenderMaterialParams::operator==(const LLNetworkData& data) const
+{
+ if (data.mType != PARAMS_RENDER_MATERIAL)
+ {
+ return false;
+ }
+
+ const LLRenderMaterialParams& param = static_cast<const LLRenderMaterialParams&>(data);
+
+ if (param.mEntries.size() != mEntries.size())
+ {
+ return false;
+ }
+
+ for (auto& entry : mEntries)
+ {
+ if (param.getMaterial(entry.te_idx) != entry.id)
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void LLRenderMaterialParams::copy(const LLNetworkData& data)
+{
+ llassert_always(data.mType == PARAMS_RENDER_MATERIAL);
+ const LLRenderMaterialParams& param = static_cast<const LLRenderMaterialParams&>(data);
+ mEntries = param.mEntries;
+}
+
+LLSD LLRenderMaterialParams::asLLSD() const
+{
+ LLSD ret;
+
+ for (int i = 0; i < mEntries.size(); ++i)
+ {
+ ret[i]["te_idx"] = mEntries[i].te_idx;
+ ret[i]["id"] = mEntries[i].id;
+ }
+
+ return ret;
+}
+
+bool LLRenderMaterialParams::fromLLSD(LLSD& sd)
+{
+ if (sd.isArray())
+ {
+ mEntries.resize(sd.size());
+ for (int i = 0; i < sd.size(); ++i)
+ {
+ if (sd[i].has("te_idx") && sd.has("id"))
+ {
+ mEntries[i].te_idx = sd[i]["te_idx"].asInteger();
+ mEntries[i].id = sd[i]["id"].asUUID();
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+void LLRenderMaterialParams::setMaterial(U8 te, const LLUUID& id)
+{
+ for (int i = 0; i < mEntries.size(); ++i)
+ {
+ if (mEntries[i].te_idx == te)
+ {
+ if (id.isNull())
+ {
+ mEntries.erase(mEntries.begin() + i);
+ }
+ else
+ {
+ mEntries[i].id = id;
+ }
+ return;
+ }
+ }
+
+ mEntries.push_back({ te, id });
+}
+
+const LLUUID& LLRenderMaterialParams::getMaterial(U8 te) const
+{
+ for (int i = 0; i < mEntries.size(); ++i)
+ {
+ if (mEntries[i].te_idx == te)
+ {
+ return mEntries[i].id;
+ }
+ }
+
+ return LLUUID::null;
+}
+
diff --git a/indra/llprimitive/llprimitive.h b/indra/llprimitive/llprimitive.h
index 309b18faa9..d2adfa4a3d 100644
--- a/indra/llprimitive/llprimitive.h
+++ b/indra/llprimitive/llprimitive.h
@@ -107,6 +107,8 @@ public:
PARAMS_RESERVED = 0x50, // Used on server-side
PARAMS_MESH = 0x60,
PARAMS_EXTENDED_MESH = 0x70,
+ PARAMS_RENDER_MATERIAL = 0x80,
+ PARAMS_REFLECTION_PROBE = 0x90,
};
public:
@@ -170,6 +172,50 @@ public:
F32 getCutoff() const { return mCutoff; }
};
+extern const F32 REFLECTION_PROBE_MIN_AMBIANCE;
+extern const F32 REFLECTION_PROBE_MAX_AMBIANCE;
+extern const F32 REFLECTION_PROBE_DEFAULT_AMBIANCE;
+extern const F32 REFLECTION_PROBE_MIN_CLIP_DISTANCE;
+extern const F32 REFLECTION_PROBE_MAX_CLIP_DISTANCE;
+extern const F32 REFLECTION_PROBE_DEFAULT_CLIP_DISTANCE;
+
+class LLReflectionProbeParams : public LLNetworkData
+{
+public:
+ enum EFlags : U8
+ {
+ FLAG_BOX_VOLUME = 0x01, // use a box influence volume
+ FLAG_DYNAMIC = 0x02, // render dynamic objects (avatars) into this Reflection Probe
+ };
+
+protected:
+ F32 mAmbiance = REFLECTION_PROBE_DEFAULT_AMBIANCE;
+ F32 mClipDistance = REFLECTION_PROBE_DEFAULT_CLIP_DISTANCE;
+ U8 mFlags = 0;
+
+public:
+ LLReflectionProbeParams();
+ /*virtual*/ BOOL pack(LLDataPacker& dp) const;
+ /*virtual*/ BOOL unpack(LLDataPacker& dp);
+ /*virtual*/ bool operator==(const LLNetworkData& data) const;
+ /*virtual*/ void copy(const LLNetworkData& data);
+ // LLSD implementations here are provided by Eddy Stryker.
+ // NOTE: there are currently unused in protocols
+ LLSD asLLSD() const;
+ operator LLSD() const { return asLLSD(); }
+ bool fromLLSD(LLSD& sd);
+
+ void setAmbiance(F32 ambiance) { mAmbiance = llclamp(ambiance, REFLECTION_PROBE_MIN_AMBIANCE, REFLECTION_PROBE_MAX_AMBIANCE); }
+ void setClipDistance(F32 distance) { mClipDistance = llclamp(distance, REFLECTION_PROBE_MIN_CLIP_DISTANCE, REFLECTION_PROBE_MAX_CLIP_DISTANCE); }
+ void setIsBox(bool is_box);
+ void setIsDynamic(bool is_dynamic);
+
+ F32 getAmbiance() const { return mAmbiance; }
+ F32 getClipDistance() const { return mClipDistance; }
+ bool getIsBox() const { return (mFlags & FLAG_BOX_VOLUME) != 0; }
+ bool getIsDynamic() const { return (mFlags & FLAG_DYNAMIC) != 0; }
+};
+
//-------------------------------------------------
// This structure is also used in the part of the
// code that creates new flexible objects.
@@ -320,6 +366,33 @@ public:
};
+class LLRenderMaterialParams : public LLNetworkData
+{
+private:
+ struct Entry
+ {
+ U8 te_idx;
+ LLUUID id;
+ };
+ std::vector< Entry > mEntries;
+
+public:
+ LLRenderMaterialParams();
+ BOOL pack(LLDataPacker& dp) const override;
+ BOOL unpack(LLDataPacker& dp) override;
+ bool operator==(const LLNetworkData& data) const override;
+ void copy(const LLNetworkData& data) override;
+ LLSD asLLSD() const;
+ operator LLSD() const { return asLLSD(); }
+ bool fromLLSD(LLSD& sd);
+
+ void setMaterial(U8 te_idx, const LLUUID& id);
+ const LLUUID& getMaterial(U8 te_idx) const;
+
+ bool isEmpty() { return mEntries.empty(); }
+};
+
+
// This code is not naming-standards compliant. Leaving it like this for
// now to make the connection to code in
// BOOL packTEMessage(LLDataPacker &dp) const;
@@ -415,12 +488,12 @@ public:
virtual S32 setTEMediaFlags(const U8 te, const U8 flags);
virtual S32 setTEGlow(const U8 te, const F32 glow);
virtual S32 setTEMaterialID(const U8 te, const LLMaterialID& pMaterialID);
- virtual S32 setTEMaterialParams(const U8 index, const LLMaterialPtr pMaterialParams);
+ virtual S32 setTEMaterialParams(const U8 index, const LLMaterialPtr pMaterialParams);
virtual BOOL setMaterial(const U8 material); // returns TRUE if material changed
virtual void setTESelected(const U8 te, bool sel);
LLMaterialPtr getTEMaterialParams(const U8 index);
-
+
void copyTEs(const LLPrimitive *primitive);
S32 packTEField(U8 *cur_ptr, U8 *data_ptr, U8 data_size, U8 last_face_index, EMsgVariableType type) const;
BOOL packTEMessage(LLMessageSystem *mesgsys) const;
@@ -498,6 +571,8 @@ public:
static LLPCode legacyToPCode(const U8 legacy);
static U8 pCodeToLegacy(const LLPCode pcode);
static bool getTESTAxes(const U8 face, U32* s_axis, U32* t_axis);
+
+ BOOL hasRenderMaterialParams() const;
inline static BOOL isPrimitive(const LLPCode pcode);
inline static BOOL isApp(const LLPCode pcode);
diff --git a/indra/llprimitive/lltextureentry.cpp b/indra/llprimitive/lltextureentry.cpp
index 284dfc15f4..e185404ada 100644
--- a/indra/llprimitive/lltextureentry.cpp
+++ b/indra/llprimitive/lltextureentry.cpp
@@ -491,6 +491,25 @@ S32 LLTextureEntry::setBumpShiny(U8 bump_shiny)
return TEM_CHANGE_NONE;
}
+void LLTextureEntry::setGLTFMaterial(LLGLTFMaterial* material)
+{
+ mGLTFMaterial = material;
+ if (mGLTFMaterial == nullptr)
+ {
+ setGLTFRenderMaterial(nullptr);
+ }
+}
+
+S32 LLTextureEntry::setGLTFRenderMaterial(LLGLTFMaterial* mat)
+{
+ if (mGLTFRenderMaterial != mat)
+ {
+ mGLTFRenderMaterial = mat;
+ return TEM_CHANGE_TEXTURE;
+ }
+ return TEM_CHANGE_NONE;
+}
+
S32 LLTextureEntry::setMediaFlags(U8 media_flags)
{
media_flags &= TEM_MEDIA_MASK;
diff --git a/indra/llprimitive/lltextureentry.h b/indra/llprimitive/lltextureentry.h
index dc2e201044..8dc3434a01 100644
--- a/indra/llprimitive/lltextureentry.h
+++ b/indra/llprimitive/lltextureentry.h
@@ -32,6 +32,7 @@
#include "llsd.h"
#include "llmaterialid.h"
#include "llmaterial.h"
+#include "llgltfmaterial.h"
// These bits are used while unpacking TEM messages to tell which aspects of
// the texture entry changed.
@@ -193,6 +194,19 @@ public:
// Media flags
enum { MF_NONE = 0x0, MF_HAS_MEDIA = 0x1 };
+ // GLTF asset
+ void setGLTFMaterial(LLGLTFMaterial* material);
+ LLGLTFMaterial* getGLTFMaterial() const { return mGLTFMaterial; }
+
+ // GLTF override
+ LLGLTFMaterial* getGLTFMaterialOverride() { return mGLTFMaterialOverrides; }
+ void setGLTFMaterialOverride(LLGLTFMaterial* mat) { mGLTFMaterialOverrides = mat; }
+
+ // GLTF render material
+ // nuanced behavior here -- if there is no render material, fall back to getGLTFMaterial, but ONLY for the getter, not the setter
+ LLGLTFMaterial* getGLTFRenderMaterial() const { return mGLTFRenderMaterial.notNull() ? mGLTFRenderMaterial.get() : getGLTFMaterial(); }
+ S32 setGLTFRenderMaterial(LLGLTFMaterial* mat);
+
public:
F32 mScaleS; // S, T offset
F32 mScaleT; // S, T offset
@@ -219,6 +233,17 @@ protected:
bool mMaterialUpdatePending;
LLMaterialID mMaterialID;
LLMaterialPtr mMaterial;
+
+ // Reference to GLTF material asset state
+ // On the viewer, this should be the same LLGLTFMaterial instance that exists in LLGLTFMaterialList
+ LLPointer<LLGLTFMaterial> mGLTFMaterial;
+
+ // GLTF material parameter overrides -- the viewer will use this data to override material parameters
+ // set by the asset and store the results in mRenderGLTFMaterial
+ LLPointer<LLGLTFMaterial> mGLTFMaterialOverrides;
+
+ // GLTF material to use for rendering -- will always be an LLFetchedGLTFMaterial
+ LLPointer<LLGLTFMaterial> mGLTFRenderMaterial;
// Note the media data is not sent via the same message structure as the rest of the TE
LLMediaEntry* mMediaEntry; // The media data for the face
diff --git a/indra/llprimitive/tests/llmessagesystem_stub.cpp b/indra/llprimitive/tests/llmessagesystem_stub.cpp
index 04e70945c4..9006833054 100644
--- a/indra/llprimitive/tests/llmessagesystem_stub.cpp
+++ b/indra/llprimitive/tests/llmessagesystem_stub.cpp
@@ -25,7 +25,7 @@
#include "linden_common.h"
-char * _PREHASH_TextureEntry;
+const char * const _PREHASH_TextureEntry = "TextureEntry";
S32 LLMessageSystem::getSizeFast(char const*, char const*) const
{
diff --git a/indra/llprimitive/tests/llprimitive_test.cpp b/indra/llprimitive/tests/llprimitive_test.cpp
index 0d60c7cd15..0ff0795fdc 100644
--- a/indra/llprimitive/tests/llprimitive_test.cpp
+++ b/indra/llprimitive/tests/llprimitive_test.cpp
@@ -71,6 +71,46 @@ private:
S32 mCurrDetailTest;
};
+LLMaterialID::LLMaterialID() {}
+LLMaterialID::LLMaterialID(LLMaterialID const &m) = default;
+LLMaterialID::~LLMaterialID() {}
+void LLMaterialID::set(void const*) { }
+U8 const * LLMaterialID::get() const { return mID; }
+
+LLPrimTextureList::LLPrimTextureList() { }
+LLPrimTextureList::~LLPrimTextureList() { }
+S32 LLPrimTextureList::setBumpMap(const U8 index, const U8 bump) { return TEM_CHANGE_NONE; }
+S32 LLPrimTextureList::setOffsetS(const U8 index, const F32 s) { return TEM_CHANGE_NONE; }
+S32 LLPrimTextureList::setOffsetT(const U8 index, const F32 t) { return TEM_CHANGE_NONE; }
+S32 LLPrimTextureList::copyTexture(const U8 index, const LLTextureEntry &te) { return TEM_CHANGE_NONE; }
+S32 LLPrimTextureList::setRotation(const U8 index, const F32 r) { return TEM_CHANGE_NONE; }
+S32 LLPrimTextureList::setBumpShiny(const U8 index, const U8 bump_shiny) { return TEM_CHANGE_NONE; }
+S32 LLPrimTextureList::setFullbright(const U8 index, const U8 t) { return TEM_CHANGE_NONE; }
+S32 LLPrimTextureList::setMaterialID(const U8 index, const LLMaterialID& pMaterialID) { return TEM_CHANGE_NONE; }
+S32 LLPrimTextureList::setMediaFlags(const U8 index, const U8 media_flags) { return TEM_CHANGE_NONE; }
+S32 LLPrimTextureList::setMediaTexGen(const U8 index, const U8 media) { return TEM_CHANGE_NONE; }
+S32 LLPrimTextureList::setMaterialParams(const U8 index, const LLMaterialPtr pMaterialParams) { return TEM_CHANGE_NONE; }
+S32 LLPrimTextureList::setBumpShinyFullbright(const U8 index, const U8 bump) { return TEM_CHANGE_NONE; }
+S32 LLPrimTextureList::setID(const U8 index, const LLUUID& id) { return TEM_CHANGE_NONE; }
+S32 LLPrimTextureList::setGlow(const U8 index, const F32 glow) { return TEM_CHANGE_NONE; }
+S32 LLPrimTextureList::setAlpha(const U8 index, const F32 alpha) { return TEM_CHANGE_NONE; }
+S32 LLPrimTextureList::setColor(const U8 index, const LLColor3& color) { return TEM_CHANGE_NONE; }
+S32 LLPrimTextureList::setColor(const U8 index, const LLColor4& color) { return TEM_CHANGE_NONE; }
+S32 LLPrimTextureList::setScale(const U8 index, const F32 s, const F32 t) { return TEM_CHANGE_NONE; }
+S32 LLPrimTextureList::setScaleS(const U8 index, const F32 s) { return TEM_CHANGE_NONE; }
+S32 LLPrimTextureList::setScaleT(const U8 index, const F32 t) { return TEM_CHANGE_NONE; }
+S32 LLPrimTextureList::setShiny(const U8 index, const U8 shiny) { return TEM_CHANGE_NONE; }
+S32 LLPrimTextureList::setOffset(const U8 index, const F32 s, const F32 t) { return TEM_CHANGE_NONE; }
+S32 LLPrimTextureList::setTexGen(const U8 index, const U8 texgen) { return TEM_CHANGE_NONE; }
+
+LLMaterialPtr LLPrimTextureList::getMaterialParams(const U8 index) { return LLMaterialPtr(); }
+void LLPrimTextureList::copy(LLPrimTextureList const & ptl) { mEntryList = ptl.mEntryList; } // do we need to call getTexture()->newCopy()?
+void LLPrimTextureList::take(LLPrimTextureList &other_list) { }
+void LLPrimTextureList::setSize(S32 new_size) { mEntryList.resize(new_size); }
+void LLPrimTextureList::setAllIDs(const LLUUID &id) { }
+LLTextureEntry * LLPrimTextureList::getTexture(const U8 index) const { return nullptr; }
+S32 LLPrimTextureList::size() const { return mEntryList.size(); }
+
class PRIMITIVE_TEST_SETUP
{
public: