summaryrefslogtreecommitdiff
path: root/indra/newview
diff options
context:
space:
mode:
authorJonathan "Geenz" Goodman <geenz@geenzo.com>2025-04-08 13:51:21 -0400
committerJonathan "Geenz" Goodman <geenz@geenzo.com>2025-04-08 13:51:21 -0400
commitc8499b7f01ac3f46f0764ea8195c30a4a2ec27a8 (patch)
treef1175d186cbdd87c0afe13498ba01d46ae56bf92 /indra/newview
parent3c9bb9bfe5aa178141c381cfac066fd31a7252b4 (diff)
GLTF WIP. Still working on getting transforms working proper and need to figure out our indices.
Diffstat (limited to 'indra/newview')
-rw-r--r--indra/newview/CMakeLists.txt2
-rw-r--r--indra/newview/gltf/asset.cpp20
-rw-r--r--indra/newview/gltf/asset.h3
-rw-r--r--indra/newview/gltf/llgltfloader.cpp431
-rw-r--r--indra/newview/gltf/llgltfloader.h209
-rw-r--r--indra/newview/llfilepicker.cpp2
-rw-r--r--indra/newview/llmodelpreview.cpp2
7 files changed, 660 insertions, 9 deletions
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index ed29911a43..7dfe01898a 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -76,6 +76,7 @@ set(viewer_SOURCE_FILES
gltf/accessor.cpp
gltf/primitive.cpp
gltf/animation.cpp
+ gltf/llgltfloader.cpp
groupchatlistener.cpp
llaccountingcostmanager.cpp
llaisapi.cpp
@@ -744,6 +745,7 @@ set(viewer_HEADER_FILES
gltf/buffer_util.h
gltf/primitive.h
gltf/animation.h
+ gltf/llgltfloader.h
llaccountingcost.h
llaccountingcostmanager.h
llaisapi.h
diff --git a/indra/newview/gltf/asset.cpp b/indra/newview/gltf/asset.cpp
index c210b9c61d..beccb02bd4 100644
--- a/indra/newview/gltf/asset.cpp
+++ b/indra/newview/gltf/asset.cpp
@@ -472,11 +472,14 @@ void Asset::update()
for (auto& image : mImages)
{
- if (image.mTexture.notNull())
- { // HACK - force texture to be loaded full rez
- // TODO: calculate actual vsize
- image.mTexture->addTextureStats(2048.f * 2048.f);
- image.mTexture->setBoostLevel(LLViewerTexture::BOOST_HIGH);
+ if (image.mLoadIntoTexturePipe)
+ {
+ if (image.mTexture.notNull())
+ { // HACK - force texture to be loaded full rez
+ // TODO: calculate actual vsize
+ image.mTexture->addTextureStats(2048.f * 2048.f);
+ image.mTexture->setBoostLevel(LLViewerTexture::BOOST_HIGH);
+ }
}
}
}
@@ -603,6 +606,7 @@ bool Asset::prep()
if (vertex_count[variant] > 0)
{
U32 mat_idx = mat_id + 1;
+ #if 0
LLVertexBuffer* vb = new LLVertexBuffer(attribute_mask);
rd.mBatches[variant][mat_idx].mVertexBuffer = vb;
@@ -624,6 +628,7 @@ bool Asset::prep()
vb->unmapBuffer();
vb->unbind();
+ #endif
}
}
}
@@ -634,10 +639,10 @@ bool Asset::prep()
{
for (auto& primitive : mesh.mPrimitives)
{
- llassert(primitive.mVertexBuffer.notNull());
+ //llassert(primitive.mVertexBuffer.notNull());
}
}
-
+ #if 0
// build render batches
for (S32 node_id = 0; node_id < mNodes.size(); ++node_id)
{
@@ -664,6 +669,7 @@ bool Asset::prep()
}
}
}
+ #endif
return true;
}
diff --git a/indra/newview/gltf/asset.h b/indra/newview/gltf/asset.h
index 27821659db..e70fffa986 100644
--- a/indra/newview/gltf/asset.h
+++ b/indra/newview/gltf/asset.h
@@ -286,6 +286,7 @@ namespace LL
void serialize(boost::json::object& dst) const;
};
+ // Image is for images that we want to load for the given asset. This acts as an interface into the viewer's texture pipe.
class Image
{
public:
@@ -301,6 +302,8 @@ namespace LL
S32 mBits = -1;
S32 mPixelType = -1;
+ bool mLoadIntoTexturePipe = false;
+
LLPointer<LLViewerFetchedTexture> mTexture;
const Image& operator=(const Value& src);
diff --git a/indra/newview/gltf/llgltfloader.cpp b/indra/newview/gltf/llgltfloader.cpp
new file mode 100644
index 0000000000..106c20d4d3
--- /dev/null
+++ b/indra/newview/gltf/llgltfloader.cpp
@@ -0,0 +1,431 @@
+/**
+ * @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> &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);
+
+ mGltfLoaded = mGLTFAsset.load(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();
+ */
+
+ setLoadState(DONE);
+
+ return (mMeshesLoaded);
+}
+
+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 (auto node : mGLTFAsset.mNodes)
+ {
+ LLMatrix4 transform;
+ material_map mats;
+ auto meshidx = node.mMesh;
+
+ if (meshidx >= 0)
+ {
+ LLModel* pModel = new LLModel(volume_params, 0.f);
+ auto mesh = mGLTFAsset.mMeshes[meshidx];
+
+ if (populateModelFromMesh(pModel, mesh, mats) && (LLModel::NO_ERRORS == pModel->getStatus()) && validate_model(pModel))
+ {
+ mModelList.push_back(pModel);
+ LLVector3 mesh_scale_vector;
+ LLVector3 mesh_translation_vector;
+ pModel->getNormalizedScaleTranslation(mesh_scale_vector, mesh_translation_vector);
+
+ LLMatrix4 mesh_translation;
+ mesh_translation.setTranslation(mesh_translation_vector);
+ mesh_translation *= transform;
+ transform = mesh_translation;
+
+ LLMatrix4 mesh_scale;
+ mesh_scale.initScale(mesh_scale_vector);
+ mesh_scale *= transform;
+ transform = mesh_scale;
+
+
+ mScene[transform].push_back(LLModelInstance(pModel, node.mName, transform, mats));
+ }
+ else
+ {
+ setLoadState(ERROR_MODEL + pModel->getStatus());
+ delete (pModel);
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh &mesh, material_map &mats)
+{
+ pModel->mLabel = mesh.mName;
+ pModel->ClearFacesAndMaterials();
+
+ auto prims = mesh.mPrimitives;
+ for (auto prim : prims)
+ {
+ // So primitives already have all of the data we need for a given face in SL land.
+ // Primitives may only ever have a single material assigned to them - as the relation is 1:1 in terms of intended draw call count.
+ // Just go ahead and populate faces direct from the GLTF primitives here.
+ // -Geenz 2025-04-07
+ LLVolumeFace face;
+ LLVolumeFace::VertexMapData::PointMap point_map;
+
+ std::vector<LLVolumeFace::VertexData> vertices;
+ std::vector<U16> indices;
+
+ LLImportMaterial impMat;
+
+ auto material = mGLTFAsset.mMaterials[prim.mMaterial];
+
+ impMat.mDiffuseColor = LLColor4::white;
+
+
+ for (U32 i = 0; i < prim.getVertexCount(); i++)
+ {
+ LLVolumeFace::VertexData vert;
+ vert.setPosition(prim.mPositions[i]);
+ vert.setNormal(prim.mNormals[i]);
+ vert.mTexCoord = prim.mTexCoords0[i];
+ vertices.push_back(vert);
+ }
+
+ for (S32 i = 0; i < prim.mIndexArray.size(); i++)
+ {
+ indices.push_back(prim.mIndexArray[i]);
+ }
+
+ face.fillFromLegacyData(vertices, indices);
+
+ pModel->getVolumeFaces().push_back(face);
+ pModel->getMaterialList().push_back("mat" + std::to_string(prim.mMaterial));
+ mats["mat" + std::to_string(prim.mMaterial)] = impMat;
+ }
+
+ return true;
+}
+
+bool LLGLTFLoader::parseMaterials()
+{
+ return true;
+ /*
+ 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;
+}
diff --git a/indra/newview/gltf/llgltfloader.h b/indra/newview/gltf/llgltfloader.h
new file mode 100644
index 0000000000..3b7147f588
--- /dev/null
+++ b/indra/newview/gltf/llgltfloader.h
@@ -0,0 +1,209 @@
+/**
+ * @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 "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:
+ LL::GLTF::Asset mGLTFAsset;
+ 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 LL::GLTF::Mesh &mesh, material_map& mats);
+ 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/newview/llfilepicker.cpp b/indra/newview/llfilepicker.cpp
index 716e6cd9e3..09a2206cd7 100644
--- a/indra/newview/llfilepicker.cpp
+++ b/indra/newview/llfilepicker.cpp
@@ -59,7 +59,7 @@ LLFilePicker LLFilePicker::sInstance;
#define XML_FILTER L"XML files (*.xml)\0*.xml\0"
#define SLOBJECT_FILTER L"Objects (*.slobject)\0*.slobject\0"
#define RAW_FILTER L"RAW files (*.raw)\0*.raw\0"
-#define MODEL_FILTER L"Model files (*.dae)\0*.dae\0"
+#define MODEL_FILTER L"Model files (*.dae, *.gltf, *.glb)\0*.dae;*.gltf;*.glb\0"
#define MATERIAL_FILTER L"GLTF Files (*.gltf; *.glb)\0*.gltf;*.glb\0"
#define HDRI_FILTER L"HDRI Files (*.exr)\0*.exr\0"
#define MATERIAL_TEXTURES_FILTER L"GLTF Import (*.gltf; *.glb; *.tga; *.bmp; *.jpg; *.jpeg; *.png)\0*.gltf;*.glb;*.tga;*.bmp;*.jpg;*.jpeg;*.png\0"
diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp
index 5a8fd299bf..14d23b73c9 100644
--- a/indra/newview/llmodelpreview.cpp
+++ b/indra/newview/llmodelpreview.cpp
@@ -30,7 +30,7 @@
#include "llmodelloader.h"
#include "lldaeloader.h"
-#include "llgltfloader.h"
+#include "gltf/llgltfloader.h"
#include "llfloatermodelpreview.h"
#include "llagent.h"