Merge pull request #354 from secondlife/SL-20225

SL-20225: Update LLGLTFMaterial
meow-7.2.2
cosmic-linden 2023-10-09 16:18:06 -07:00 committed by GitHub
commit 87b34fb742
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 197 additions and 145 deletions

View File

@ -34,6 +34,7 @@ set(llprimitive_HEADER_FILES
lldaeloader.h
llgltfloader.h
llgltfmaterial.h
llgltfmaterial_templates.h
legacy_object_types.h
llmaterial.h
llmaterialid.h

View File

@ -24,25 +24,28 @@
* $/LicenseInfo$
*/
#include "linden_common.h"
#include "llgltfmaterial.h"
#include "llsdserialize.h"
// NOTE -- this should be the one and only place tiny_gltf.h is included
#include "tinygltf/tiny_gltf.h"
#include "llgltfmaterial_templates.h"
const char* const LLGLTFMaterial::ASSET_VERSION = "1.1";
const char* const LLGLTFMaterial::ASSET_TYPE = "GLTF 2.0";
const std::array<std::string, 2> LLGLTFMaterial::ACCEPTED_ASSET_VERSIONS = { "1.0", "1.1" };
const char* const GLTF_FILE_EXTENSION_TRANSFORM = "KHR_texture_transform";
const char* const GLTF_FILE_EXTENSION_TRANSFORM_SCALE = "scale";
const char* const GLTF_FILE_EXTENSION_TRANSFORM_OFFSET = "offset";
const char* const GLTF_FILE_EXTENSION_TRANSFORM_ROTATION = "rotation";
const char* const LLGLTFMaterial::GLTF_FILE_EXTENSION_TRANSFORM = "KHR_texture_transform";
const char* const LLGLTFMaterial::GLTF_FILE_EXTENSION_TRANSFORM_SCALE = "scale";
const char* const LLGLTFMaterial::GLTF_FILE_EXTENSION_TRANSFORM_OFFSET = "offset";
const char* const LLGLTFMaterial::GLTF_FILE_EXTENSION_TRANSFORM_ROTATION = "rotation";
// special UUID that indicates a null UUID in override data
static const LLUUID GLTF_OVERRIDE_NULL_UUID = LLUUID("ffffffff-ffff-ffff-ffff-ffffffffffff");
const LLUUID LLGLTFMaterial::GLTF_OVERRIDE_NULL_UUID = LLUUID("ffffffff-ffff-ffff-ffff-ffffffffffff");
void LLGLTFMaterial::TextureTransform::getPacked(F32 (&packed)[8]) const
{
@ -68,14 +71,14 @@ LLGLTFMaterial::LLGLTFMaterial(const LLGLTFMaterial& rhs)
LLGLTFMaterial& LLGLTFMaterial::operator=(const LLGLTFMaterial& rhs)
{
//have to do a manual operator= because of LLRefCount
//have to do a manual operator= because of LLRefCount
mTextureId = rhs.mTextureId;
mTextureTransform = rhs.mTextureTransform;
mBaseColor = rhs.mBaseColor;
mEmissiveColor = rhs.mEmissiveColor;
mMetallicFactor = rhs.mMetallicFactor;
mRoughnessFactor = rhs.mRoughnessFactor;
mAlphaCutoff = rhs.mAlphaCutoff;
@ -97,7 +100,7 @@ bool LLGLTFMaterial::operator==(const LLGLTFMaterial& rhs) const
mBaseColor == rhs.mBaseColor &&
mEmissiveColor == rhs.mEmissiveColor &&
mMetallicFactor == rhs.mMetallicFactor &&
mRoughnessFactor == rhs.mRoughnessFactor &&
mAlphaCutoff == rhs.mAlphaCutoff &&
@ -122,6 +125,7 @@ bool LLGLTFMaterial::fromJSON(const std::string& json, std::string& warn_msg, st
return true;
}
return false;
}
@ -190,7 +194,8 @@ void LLGLTFMaterial::setFromModel(const tinygltf::Model& model, S32 mat_index)
}
}
LLVector2 vec2_from_json(const tinygltf::Value::Object& object, const char* key, const LLVector2& default_value)
// static
LLVector2 LLGLTFMaterial::vec2FromJson(const tinygltf::Value::Object& object, const char* key, const LLVector2& default_value)
{
const auto it = object.find(key);
if (it == object.end())
@ -215,7 +220,8 @@ LLVector2 vec2_from_json(const tinygltf::Value::Object& object, const char* key,
return value;
}
F32 float_from_json(const tinygltf::Value::Object& object, const char* key, const F32 default_value)
// static
F32 LLGLTFMaterial::floatFromJson(const tinygltf::Value::Object& object, const char* key, const F32 default_value)
{
const auto it = object.find(key);
if (it == object.end())
@ -230,52 +236,6 @@ F32 float_from_json(const tinygltf::Value::Object& object, const char* key, cons
return (F32)real_json.GetNumberAsDouble();
}
template<typename T>
std::string gltf_get_texture_image(const tinygltf::Model& model, const T& texture_info)
{
const S32 texture_idx = texture_info.index;
if (texture_idx < 0 || texture_idx >= model.textures.size())
{
return "";
}
const tinygltf::Texture& texture = model.textures[texture_idx];
// Ignore texture.sampler for now
const S32 image_idx = texture.source;
if (image_idx < 0 || image_idx >= model.images.size())
{
return "";
}
const tinygltf::Image& image = model.images[image_idx];
return image.uri;
}
// *NOTE: Use template here as workaround for the different similar texture info classes
template<typename T>
void LLGLTFMaterial::setFromTexture(const tinygltf::Model& model, const T& texture_info, TextureInfo texture_info_id)
{
LL_PROFILE_ZONE_SCOPED;
const std::string uri = gltf_get_texture_image(model, texture_info);
mTextureId[texture_info_id].set(uri);
const tinygltf::Value::Object& extensions_object = texture_info.extensions;
const auto transform_it = extensions_object.find(GLTF_FILE_EXTENSION_TRANSFORM);
if (transform_it != extensions_object.end())
{
const tinygltf::Value& transform_json = std::get<1>(*transform_it);
if (transform_json.IsObject())
{
const tinygltf::Value::Object& transform_object = transform_json.Get<tinygltf::Value::Object>();
TextureTransform& transform = mTextureTransform[texture_info_id];
transform.mOffset = vec2_from_json(transform_object, GLTF_FILE_EXTENSION_TRANSFORM_OFFSET, getDefaultTextureOffset());
transform.mScale = vec2_from_json(transform_object, GLTF_FILE_EXTENSION_TRANSFORM_SCALE, getDefaultTextureScale());
transform.mRotation = float_from_json(transform_object, GLTF_FILE_EXTENSION_TRANSFORM_ROTATION, getDefaultTextureRotation());
}
}
}
void LLGLTFMaterial::writeToModel(tinygltf::Model& model, S32 mat_index) const
{
LL_PROFILE_ZONE_SCOPED;
@ -302,7 +262,7 @@ void LLGLTFMaterial::writeToModel(tinygltf::Model& model, S32 mat_index) const
material_out.alphaMode = getAlphaMode();
material_out.alphaCutoff = mAlphaCutoff;
mBaseColor.write(material_out.pbrMetallicRoughness.baseColorFactor);
if (mEmissiveColor != LLGLTFMaterial::getDefaultEmissiveColor())
@ -320,7 +280,7 @@ void LLGLTFMaterial::writeToModel(tinygltf::Model& model, S32 mat_index) const
tinygltf::Value::Object extras;
bool write_extras = false;
if (mOverrideAlphaMode && mAlphaMode == getDefaultAlphaMode())
{
{
extras["override_alpha_mode"] = tinygltf::Value(mOverrideAlphaMode);
write_extras = true;
}
@ -339,57 +299,6 @@ void LLGLTFMaterial::writeToModel(tinygltf::Model& model, S32 mat_index) const
model.asset.version = "2.0";
}
template<typename T>
void gltf_allocate_texture_image(tinygltf::Model& model, T& texture_info, const std::string& uri)
{
const S32 image_idx = model.images.size();
model.images.emplace_back();
model.images[image_idx].uri = uri;
// The texture, not to be confused with the texture info
const S32 texture_idx = model.textures.size();
model.textures.emplace_back();
tinygltf::Texture& texture = model.textures[texture_idx];
texture.source = image_idx;
texture_info.index = texture_idx;
}
template<typename T>
void LLGLTFMaterial::writeToTexture(tinygltf::Model& model, T& texture_info, TextureInfo texture_info_id, bool force_write) const
{
LL_PROFILE_ZONE_SCOPED;
const LLUUID& texture_id = mTextureId[texture_info_id];
const TextureTransform& transform = mTextureTransform[texture_info_id];
const bool is_blank_transform = transform == sDefault.mTextureTransform[0];
// Check if this material matches all the fallback values, and if so, then
// skip including it to reduce material size
if (!force_write && texture_id.isNull() && is_blank_transform)
{
return;
}
// tinygltf will discard this texture info if there is no valid texture,
// causing potential loss of information for overrides, so ensure one is
// defined. -Cosmic,2023-01-30
gltf_allocate_texture_image(model, texture_info, texture_id.asString());
if (!is_blank_transform)
{
tinygltf::Value::Object transform_map;
transform_map[GLTF_FILE_EXTENSION_TRANSFORM_OFFSET] = tinygltf::Value(tinygltf::Value::Array({
tinygltf::Value(transform.mOffset.mV[VX]),
tinygltf::Value(transform.mOffset.mV[VY])
}));
transform_map[GLTF_FILE_EXTENSION_TRANSFORM_SCALE] = tinygltf::Value(tinygltf::Value::Array({
tinygltf::Value(transform.mScale.mV[VX]),
tinygltf::Value(transform.mScale.mV[VY])
}));
transform_map[GLTF_FILE_EXTENSION_TRANSFORM_ROTATION] = tinygltf::Value(transform.mRotation);
texture_info.extensions[GLTF_FILE_EXTENSION_TRANSFORM] = tinygltf::Value(transform_map);
}
}
void LLGLTFMaterial::sanitizeAssetMaterial()
{
mTextureTransform = sDefault.mTextureTransform;
@ -403,19 +312,19 @@ bool LLGLTFMaterial::setBaseMaterial()
return *this != old_override;
}
bool LLGLTFMaterial::isClearedForBaseMaterial()
{
LLGLTFMaterial cleared_override = sDefault;
cleared_override.setBaseMaterial(*this);
return *this == cleared_override;
}
// For material overrides only. Copies transforms from the old override.
void LLGLTFMaterial::setBaseMaterial(const LLGLTFMaterial& old_override_mat)
{
mTextureTransform = old_override_mat.mTextureTransform;
}
bool LLGLTFMaterial::isClearedForBaseMaterial() const
{
LLGLTFMaterial cleared_override = sDefault;
cleared_override.setBaseMaterial(*this);
return *this == cleared_override;
}
// static
void LLGLTFMaterial::hackOverrideUUID(LLUUID& id)
@ -516,7 +425,7 @@ void LLGLTFMaterial::setAlphaMode(const std::string& mode, bool for_override)
{
m = ALPHA_MODE_BLEND;
}
setAlphaMode(m, for_override);
}
@ -709,7 +618,6 @@ void LLGLTFMaterial::getOverrideLLSD(const LLGLTFMaterial& override_mat, LLSD& d
{
data["tex"][i] = LLSD::UUID(override_texture_id);
}
}
if (override_mat.mBaseColor != getDefaultBaseColor())
@ -764,23 +672,6 @@ void LLGLTFMaterial::getOverrideLLSD(const LLGLTFMaterial& override_mat, LLSD& d
data["ti"][i]["r"] = override_mat.mTextureTransform[i].mRotation;
}
}
#if 0
{
std::ostringstream ostr;
LLSDSerialize::serialize(data, ostr, LLSDSerialize::LLSD_NOTATION);
std::string param_str(ostr.str());
LL_INFOS() << param_str << LL_ENDL;
LL_INFOS() << "Notation size: " << param_str.size() << LL_ENDL;
}
{
std::ostringstream ostr;
LLSDSerialize::serialize(data, ostr, LLSDSerialize::LLSD_BINARY);
std::string param_str(ostr.str());
LL_INFOS() << "Binary size: " << param_str.size() << LL_ENDL;
}
#endif
}

View File

@ -35,10 +35,13 @@
#include "hbxxh.h"
#include <string>
#include <map>
namespace tinygltf
{
class Model;
struct TextureInfo;
class Value;
}
class LLTextureEntry;
@ -52,6 +55,9 @@ public:
static const char* const ASSET_VERSION;
static const char* const ASSET_TYPE;
// Max allowed size of a GLTF material asset or override, when serialized
// as a minified JSON string
static constexpr size_t MAX_ASSET_LENGTH = 2048;
static const std::array<std::string, 2> ACCEPTED_ASSET_VERSIONS;
static bool isAcceptedVersion(const std::string& version) { return std::find(ACCEPTED_ASSET_VERSIONS.cbegin(), ACCEPTED_ASSET_VERSIONS.cend(), version) != ACCEPTED_ASSET_VERSIONS.cend(); }
@ -64,6 +70,7 @@ public:
void getPacked(F32 (&packed)[8]) const;
bool operator==(const TextureTransform& other) const;
bool operator!=(const TextureTransform& other) const { return !(*this == other); }
};
enum AlphaMode
@ -96,8 +103,13 @@ public:
GLTF_TEXTURE_INFO_COUNT
};
std::array<LLUUID, GLTF_TEXTURE_INFO_COUNT> mTextureId;
static const char* const GLTF_FILE_EXTENSION_TRANSFORM;
static const char* const GLTF_FILE_EXTENSION_TRANSFORM_SCALE;
static const char* const GLTF_FILE_EXTENSION_TRANSFORM_OFFSET;
static const char* const GLTF_FILE_EXTENSION_TRANSFORM_ROTATION;
static const LLUUID GLTF_OVERRIDE_NULL_UUID;
std::array<LLUUID, GLTF_TEXTURE_INFO_COUNT> mTextureId;
std::array<TextureTransform, GLTF_TEXTURE_INFO_COUNT> mTextureTransform;
// NOTE: initialize values to defaults according to the GLTF spec
@ -137,7 +149,7 @@ public:
void setAlphaMode(S32 mode, bool for_override = false);
void setDoubleSided(bool double_sided, bool for_override = false);
//NOTE: texture offsets only exist in overrides, so "for_override" is not needed
// *NOTE: texture offsets only exist in overrides, so "for_override" is not needed
void setTextureOffset(TextureInfo texture_info, const LLVector2& offset);
void setTextureScale(TextureInfo texture_info, const LLVector2& scale);
@ -155,7 +167,6 @@ public:
static LLVector2 getDefaultTextureScale();
static F32 getDefaultTextureRotation();
static void hackOverrideUUID(LLUUID& id);
static void applyOverrideUUID(LLUUID& dst_id, const LLUUID& override_id);
@ -164,7 +175,7 @@ public:
void setAlphaMode(const std::string& mode, bool for_override = false);
const char* getAlphaMode() const;
// set the contents of this LLGLTFMaterial from the given json
// returns true if successful
// if unsuccessful, the contents of this LLGLTFMaterial should be left unchanged and false is returned
@ -176,7 +187,6 @@ public:
// 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
@ -202,21 +212,29 @@ public:
// For material overrides only. Clears most properties to
// default/fallthrough, but preserves the transforms.
bool setBaseMaterial();
void setBaseMaterial(const LLGLTFMaterial& old_override_mat);
// True if setBaseMaterial() was just called
bool isClearedForBaseMaterial();
bool isClearedForBaseMaterial() const;
// For local materials, they have to keep track of where
// they are assigned to for full updates
virtual void addTextureEntry(LLTextureEntry* te) {};
virtual void removeTextureEntry(LLTextureEntry* te) {};
private:
protected:
static LLVector2 vec2FromJson(const std::map<std::string, tinygltf::Value>& object, const char* key, const LLVector2& default_value);
static F32 floatFromJson(const std::map<std::string, tinygltf::Value>& object, const char* key, const F32 default_value);
template<typename T>
static void allocateTextureImage(tinygltf::Model& model, T& texture_info, const std::string& uri);
template<typename T>
void setFromTexture(const tinygltf::Model& model, const T& texture_info, TextureInfo texture_info_id);
template<typename T>
static void setFromTexture(const tinygltf::Model& model, const T& texture_info, LLUUID& texture_id, TextureTransform& transform);
template<typename T>
void writeToTexture(tinygltf::Model& model, T& texture_info, TextureInfo texture_info_id, bool force_write = false) const;
void setBaseMaterial(const LLGLTFMaterial& old_override_mat);
template<typename T>
static void writeToTexture(tinygltf::Model& model, T& texture_info, const LLUUID& texture_id, const TextureTransform& transform, bool force_write = false);
};

View File

@ -0,0 +1,142 @@
/**
* @file llgltfmaterial_templates.h
* @brief Material template definition
*
* $LicenseInfo:firstyear=2023&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2023, 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 "llgltfmaterial.h"
// Use templates here as workaround for the different similar texture info classes in tinygltf
// Includer must first include tiny_gltf.h with the desired flags
template<typename T>
std::string gltf_get_texture_image(const tinygltf::Model& model, const T& texture_info)
{
const S32 texture_idx = texture_info.index;
if (texture_idx < 0 || texture_idx >= model.textures.size())
{
return "";
}
const tinygltf::Texture& texture = model.textures[texture_idx];
// Ignore texture.sampler for now
const S32 image_idx = texture.source;
if (image_idx < 0 || image_idx >= model.images.size())
{
return "";
}
const tinygltf::Image& image = model.images[image_idx];
return image.uri;
}
template<typename T>
void LLGLTFMaterial::setFromTexture(const tinygltf::Model& model, const T& texture_info, TextureInfo texture_info_id)
{
setFromTexture(model, texture_info, mTextureId[texture_info_id], mTextureTransform[texture_info_id]);
const std::string uri = gltf_get_texture_image(model, texture_info);
}
// static
template<typename T>
void LLGLTFMaterial::setFromTexture(const tinygltf::Model& model, const T& texture_info, LLUUID& texture_id, TextureTransform& transform)
{
LL_PROFILE_ZONE_SCOPED;
const std::string uri = gltf_get_texture_image(model, texture_info);
texture_id.set(uri);
const tinygltf::Value::Object& extensions_object = texture_info.extensions;
const auto transform_it = extensions_object.find(GLTF_FILE_EXTENSION_TRANSFORM);
if (transform_it != extensions_object.end())
{
const tinygltf::Value& transform_json = std::get<1>(*transform_it);
if (transform_json.IsObject())
{
const tinygltf::Value::Object& transform_object = transform_json.Get<tinygltf::Value::Object>();
transform.mOffset = vec2FromJson(transform_object, GLTF_FILE_EXTENSION_TRANSFORM_OFFSET, getDefaultTextureOffset());
transform.mScale = vec2FromJson(transform_object, GLTF_FILE_EXTENSION_TRANSFORM_SCALE, getDefaultTextureScale());
transform.mRotation = floatFromJson(transform_object, GLTF_FILE_EXTENSION_TRANSFORM_ROTATION, getDefaultTextureRotation());
}
}
}
// static
template<typename T>
void LLGLTFMaterial::allocateTextureImage(tinygltf::Model& model, T& texture_info, const std::string& uri)
{
const S32 image_idx = model.images.size();
model.images.emplace_back();
model.images[image_idx].uri = uri;
// The texture, not to be confused with the texture info
const S32 texture_idx = model.textures.size();
model.textures.emplace_back();
tinygltf::Texture& texture = model.textures[texture_idx];
texture.source = image_idx;
texture_info.index = texture_idx;
}
// static
template<typename T>
void LLGLTFMaterial::writeToTexture(tinygltf::Model& model, T& texture_info, TextureInfo texture_info_id, bool force_write) const
{
writeToTexture(model, texture_info, mTextureId[texture_info_id], mTextureTransform[texture_info_id], force_write);
}
// static
template<typename T>
void LLGLTFMaterial::writeToTexture(tinygltf::Model& model, T& texture_info, const LLUUID& texture_id, const TextureTransform& transform, bool force_write)
{
LL_PROFILE_ZONE_SCOPED;
const bool is_blank_transform = transform == sDefault.mTextureTransform[0];
// Check if this material matches all the fallback values, and if so, then
// skip including it to reduce material size
if (!force_write && texture_id.isNull() && is_blank_transform)
{
return;
}
// tinygltf will discard this texture info if there is no valid texture,
// causing potential loss of information for overrides, so ensure one is
// defined. -Cosmic,2023-01-30
allocateTextureImage(model, texture_info, texture_id.asString());
if (!is_blank_transform)
{
tinygltf::Value::Object transform_map;
transform_map[GLTF_FILE_EXTENSION_TRANSFORM_OFFSET] = tinygltf::Value(tinygltf::Value::Array({
tinygltf::Value(transform.mOffset.mV[VX]),
tinygltf::Value(transform.mOffset.mV[VY])
}));
transform_map[GLTF_FILE_EXTENSION_TRANSFORM_SCALE] = tinygltf::Value(tinygltf::Value::Array({
tinygltf::Value(transform.mScale.mV[VX]),
tinygltf::Value(transform.mScale.mV[VY])
}));
transform_map[GLTF_FILE_EXTENSION_TRANSFORM_ROTATION] = tinygltf::Value(transform.mRotation);
texture_info.extensions[GLTF_FILE_EXTENSION_TRANSFORM] = tinygltf::Value(transform_map);
}
}