summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDave Parks <davep@lindenlab.com>2024-06-10 16:57:31 -0500
committerGitHub <noreply@github.com>2024-06-10 16:57:31 -0500
commit4522f33d2bad2cc0f67e10a0b0ad3cc7c1b43fbd (patch)
treef2e020507f3240208e8d0b105f7e100a1e7aaf36
parent4705362a332a2347510c81c8d00a1f607ad88af8 (diff)
#1677 Add GLTF extensions serialization and support for KHR_materials_unlit (#1686)
-rw-r--r--indra/llmath/llvolumeoctree.cpp6
-rw-r--r--indra/newview/gltf/asset.cpp40
-rw-r--r--indra/newview/gltf/asset.h19
-rw-r--r--indra/newview/gltf/buffer_util.h98
-rw-r--r--indra/newview/gltf/primitive.cpp24
5 files changed, 176 insertions, 11 deletions
diff --git a/indra/llmath/llvolumeoctree.cpp b/indra/llmath/llvolumeoctree.cpp
index 7a4d313fb1..faa169e295 100644
--- a/indra/llmath/llvolumeoctree.cpp
+++ b/indra/llmath/llvolumeoctree.cpp
@@ -151,7 +151,7 @@ void LLOctreeTriangleRayIntersect::visit(const LLOctreeNode<LLVolumeTriangle, LL
U32 idx1 = tri->mIndex[1];
U32 idx2 = tri->mIndex[2];
- if (mTexCoord != NULL)
+ if (mTexCoord != NULL && mFace->mTexCoords)
{
LLVector2* tc = (LLVector2*) mFace->mTexCoords;
*mTexCoord = ((1.f - a - b) * tc[idx0] +
@@ -160,7 +160,7 @@ void LLOctreeTriangleRayIntersect::visit(const LLOctreeNode<LLVolumeTriangle, LL
}
- if (mNormal != NULL)
+ if (mNormal != NULL && mFace->mNormals)
{
LLVector4a* norm = mFace->mNormals;
@@ -180,7 +180,7 @@ void LLOctreeTriangleRayIntersect::visit(const LLOctreeNode<LLVolumeTriangle, LL
*mNormal = n1;
}
- if (mTangent != NULL)
+ if (mTangent != NULL && mFace->mTangents)
{
LLVector4a* tangents = mFace->mTangents;
diff --git a/indra/newview/gltf/asset.cpp b/indra/newview/gltf/asset.cpp
index 7d379c2528..a4efb25860 100644
--- a/indra/newview/gltf/asset.cpp
+++ b/indra/newview/gltf/asset.cpp
@@ -39,10 +39,15 @@
using namespace LL::GLTF;
using namespace boost::json;
+
namespace LL
{
namespace GLTF
{
+ static std::unordered_set<std::string> ExtensionsSupported = {
+ "KHR_materials_unlit"
+ };
+
Material::AlphaMode gltf_alpha_mode_to_enum(const std::string& alpha_mode)
{
if (alpha_mode == "OPAQUE")
@@ -382,6 +387,22 @@ void Asset::update()
bool Asset::prep()
{
+ // check required extensions and fail if not supported
+ bool unsupported = false;
+ for (auto& extension : mExtensionsRequired)
+ {
+ if (ExtensionsSupported.find(extension) == ExtensionsSupported.end())
+ {
+ LL_WARNS() << "Unsupported extension: " << extension << LL_ENDL;
+ unsupported = true;
+ }
+ }
+
+ if (unsupported)
+ {
+ return false;
+ }
+
// do buffers first as other resources depend on them
for (auto& buffer : mBuffers)
{
@@ -600,6 +621,8 @@ const Asset& Asset::operator=(const Value& src)
copy(obj, "accessors", mAccessors);
copy(obj, "animations", mAnimations);
copy(obj, "skins", mSkins);
+ copy(obj, "extensionsUsed", mExtensionsUsed);
+ copy(obj, "extensionsRequired", mExtensionsRequired);
}
return *this;
@@ -628,6 +651,8 @@ void Asset::serialize(object& dst) const
write(mAccessors, "accessors", dst);
write(mAnimations, "animations", dst);
write(mSkins, "skins", dst);
+ write(mExtensionsUsed, "extensionsUsed", dst);
+ write(mExtensionsRequired, "extensionsRequired", dst);
}
bool Asset::save(const std::string& filename)
@@ -979,6 +1004,17 @@ bool Material::PbrMetallicRoughness::operator!=(const Material::PbrMetallicRough
return !(*this == rhs);
}
+const Material::Unlit& Material::Unlit::operator=(const Value& src)
+{
+ mPresent = true;
+ return *this;
+}
+
+void Material::Unlit::serialize(object& dst) const
+{
+ // no members and object has already been created, nothing to do
+}
+
void Material::serialize(object& dst) const
{
write(mName, "name", dst);
@@ -990,6 +1026,7 @@ void Material::serialize(object& dst) const
write(mAlphaMode, "alphaMode", dst, Material::AlphaMode::OPAQUE);
write(mAlphaCutoff, "alphaCutoff", dst, 0.5f);
write(mDoubleSided, "doubleSided", dst, false);
+ write_extensions(dst, &mUnlit, "KHR_materials_unlit");
}
const Material& Material::operator=(const Value& src)
@@ -1005,6 +1042,8 @@ const Material& Material::operator=(const Value& src)
copy(src, "alphaMode", mAlphaMode);
copy(src, "alphaCutoff", mAlphaCutoff);
copy(src, "doubleSided", mDoubleSided);
+ copy_extensions(src,
+ "KHR_materials_unlit", &mUnlit );
}
return *this;
}
@@ -1027,7 +1066,6 @@ const Mesh& Mesh::operator=(const Value& src)
}
return *this;
-
}
bool Mesh::prep(Asset& asset)
diff --git a/indra/newview/gltf/asset.h b/indra/newview/gltf/asset.h
index fd7ea17f05..8f28e5905f 100644
--- a/indra/newview/gltf/asset.h
+++ b/indra/newview/gltf/asset.h
@@ -49,10 +49,26 @@ namespace LL
{
class Asset;
+ class Extension
+ {
+ public:
+ // true if this extension is present in the gltf file
+ // otherwise false
+ bool mPresent = false;
+ };
+
+
class Material
{
public:
+ class Unlit : public Extension // KHR_materials_unlit implementation
+ {
+ public:
+ const Unlit& operator=(const Value& src);
+ void serialize(boost::json::object& dst) const;
+ };
+
enum class AlphaMode
{
OPAQUE,
@@ -117,6 +133,7 @@ namespace LL
AlphaMode mAlphaMode = AlphaMode::OPAQUE;
F32 mAlphaCutoff = 0.5f;
bool mDoubleSided = false;
+ Unlit mUnlit;
const Material& operator=(const Value& src);
void serialize(boost::json::object& dst) const;
@@ -300,6 +317,8 @@ namespace LL
std::vector<Accessor> mAccessors;
std::vector<Animation> mAnimations;
std::vector<Skin> mSkins;
+ std::vector<std::string> mExtensionsUsed;
+ std::vector<std::string> mExtensionsRequired;
std::string mVersion;
std::string mGenerator;
diff --git a/indra/newview/gltf/buffer_util.h b/indra/newview/gltf/buffer_util.h
index bd43f60d19..c26752a6b6 100644
--- a/indra/newview/gltf/buffer_util.h
+++ b/indra/newview/gltf/buffer_util.h
@@ -519,6 +519,104 @@ namespace LL
return true;
}
+
+ // to/from extension
+
+ // for internal use only, use copy_extensions instead
+ template<typename T>
+ inline bool _copy_extension(const boost::json::object& extensions, std::string_view member, T* dst)
+ {
+ if (extensions.contains(member))
+ {
+ return copy(extensions.at(member), *dst);
+ }
+
+ return false;
+ }
+
+ // Copy all extensions from src.extensions to provided destinations
+ // Usage:
+ // copy_extensions(src,
+ // "KHR_materials_unlit", &mUnlit,
+ // "KHR_materials_pbrSpecularGlossiness", &mPbrSpecularGlossiness);
+ // returns true if any of the extensions are copied
+ template<class... Types>
+ inline bool copy_extensions(const boost::json::value& src, Types... args)
+ {
+ // extract the extensions object (don't assume it exists and verify that it is an object)
+ if (src.is_object())
+ {
+ boost::json::object obj = src.get_object();
+ if (obj.contains("extensions"))
+ {
+ const boost::json::value& extensions = obj.at("extensions");
+ if (extensions.is_object())
+ {
+ const boost::json::object& ext_obj = extensions.as_object();
+ bool success = false;
+ // copy each extension, return true if any of them succeed, do not short circuit on success
+ U32 count = sizeof...(args);
+ for (U32 i = 0; i < count; i += 2)
+ {
+ if (_copy_extension(ext_obj, args...))
+ {
+ success = true;
+ }
+ }
+ return success;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ // internal use aonly, use write_extensions instead
+ template<typename T>
+ inline bool _write_extension(boost::json::object& extensions, const T* src, string_view member)
+ {
+ if (src->mPresent)
+ {
+ Value v;
+ if (write(*src, v))
+ {
+ extensions[member] = v;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // Write all extensions to dst.extensions
+ // Usage:
+ // write_extensions(dst,
+ // "KHR_materials_unlit", mUnlit,
+ // "KHR_materials_pbrSpecularGlossiness", mPbrSpecularGlossiness);
+ // returns true if any of the extensions are written
+ template<class... Types>
+ inline bool write_extensions(boost::json::object& dst, Types... args)
+ {
+ bool success = false;
+
+ boost::json::object extensions;
+ U32 count = sizeof...(args) - 1;
+
+ for (U32 i = 0; i < count; i += 2)
+ {
+ if (_write_extension(extensions, args...))
+ {
+ success = true;
+ }
+ }
+
+ if (success)
+ {
+ dst["extensions"] = extensions;
+ }
+
+ return success;
+ }
+
// conditionally write a member to an object if the member
// is not the default value
template<typename T>
diff --git a/indra/newview/gltf/primitive.cpp b/indra/newview/gltf/primitive.cpp
index 8d9880951b..bc333aff69 100644
--- a/indra/newview/gltf/primitive.cpp
+++ b/indra/newview/gltf/primitive.cpp
@@ -313,6 +313,10 @@ bool Primitive::prep(Asset& asset)
// TODO: support colorless vertex buffers
mask |= LLVertexBuffer::MAP_COLOR;
+ mShaderVariant = 0;
+
+ bool unlit = false;
+
// bake material basecolor into color array
if (mMaterial != INVALID_INDEX)
{
@@ -322,11 +326,15 @@ bool Primitive::prep(Asset& asset)
{
dst = LLColor4U(baseColor * LLColor4(dst));
}
- }
- mShaderVariant = 0;
+ if (material.mUnlit.mPresent)
+ { // material uses KHR_materials_unlit
+ mShaderVariant |= LLGLSLShader::GLTFVariant::UNLIT;
+ unlit = true;
+ }
+ }
- if (mNormals.empty())
+ if (mNormals.empty() && !unlit)
{
mTangents.clear();
@@ -334,6 +342,7 @@ bool Primitive::prep(Asset& asset)
{ //no normals and no surfaces, this primitive is unlit
mTangents.clear();
mShaderVariant |= LLGLSLShader::GLTFVariant::UNLIT;
+ unlit = true;
}
else
{
@@ -350,8 +359,6 @@ bool Primitive::prep(Asset& asset)
}
}
- bool unlit = (mShaderVariant & LLGLSLShader::GLTFVariant::UNLIT) != 0;
-
if (mTangents.empty() && !unlit)
{ // NOTE: must be done last because tangent generation rewrites the other arrays
// adapted from usage of Mikktspace in llvolume.cpp
@@ -388,10 +395,13 @@ bool Primitive::prep(Asset& asset)
}
}
-
- if (!unlit)
+ if (!mNormals.empty())
{
mask |= LLVertexBuffer::MAP_NORMAL;
+ }
+
+ if (!mTangents.empty())
+ {
mask |= LLVertexBuffer::MAP_TANGENT;
}