summaryrefslogtreecommitdiff
path: root/indra/llprimitive/tests
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llprimitive/tests')
-rw-r--r--indra/llprimitive/tests/llgltfmaterial_test.cpp369
-rw-r--r--indra/llprimitive/tests/llmessagesystem_stub.cpp2
-rw-r--r--indra/llprimitive/tests/llprimitive_test.cpp40
3 files changed, 410 insertions, 1 deletions
diff --git a/indra/llprimitive/tests/llgltfmaterial_test.cpp b/indra/llprimitive/tests/llgltfmaterial_test.cpp
new file mode 100644
index 0000000000..88b6fae3a7
--- /dev/null
+++ b/indra/llprimitive/tests/llgltfmaterial_test.cpp
@@ -0,0 +1,369 @@
+/**
+ * @file llgltfmaterial_test.cpp
+ *
+ * $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$
+ */
+
+#include "linden_common.h"
+#include "lltut.h"
+
+#include "../llgltfmaterial.h"
+#include "lluuid.cpp"
+
+// 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)
+
+// Disable reading external images to prevent warnings and speed up the tests.
+// We don't need this for the tests, but still need the filesystem
+// implementation to be defined in order for llprimitive to link correctly.
+#define TINYGLTF_NO_EXTERNAL_IMAGE 1
+
+#include "tinygltf/tiny_gltf.h"
+
+namespace tut
+{
+ struct llgltfmaterial
+ {
+ };
+ typedef test_group<llgltfmaterial> llgltfmaterial_t;
+ typedef llgltfmaterial_t::object llgltfmaterial_object_t;
+ tut::llgltfmaterial_t tut_llgltfmaterial("llgltfmaterial");
+
+ // A positive 32-bit float with a long string representation
+ constexpr F32 test_fraction = 1.09045365e-32;
+ // A larger positive 32-bit float for values that get zeroed if below a threshold
+ constexpr F32 test_fraction_big = 0.109045;
+
+ void apply_test_material_texture_ids(LLGLTFMaterial& material)
+ {
+ material.setBaseColorId(LLUUID::generateNewID());
+ material.setNormalId(LLUUID::generateNewID());
+ material.setOcclusionRoughnessMetallicId(LLUUID::generateNewID());
+ material.setEmissiveId(LLUUID::generateNewID());
+ }
+
+ void apply_test_material_texture_transforms(LLGLTFMaterial& material)
+ {
+ LLGLTFMaterial::TextureTransform test_transform;
+ test_transform.mOffset.mV[VX] = test_fraction;
+ test_transform.mOffset.mV[VY] = test_fraction;
+ test_transform.mScale.mV[VX] = test_fraction;
+ test_transform.mScale.mV[VY] = test_fraction;
+ test_transform.mRotation = test_fraction;
+ for (LLGLTFMaterial::TextureInfo i = LLGLTFMaterial::GLTF_TEXTURE_INFO_BASE_COLOR; i < LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT; i = LLGLTFMaterial::TextureInfo((U32)i + 1))
+ {
+ material.setTextureOffset(i, test_transform.mOffset);
+ material.setTextureScale(i, test_transform.mScale);
+ material.setTextureRotation(i, test_transform.mRotation);
+ }
+ }
+
+ void apply_test_material_factors(LLGLTFMaterial& material)
+ {
+ material.setBaseColorFactor(LLColor4(test_fraction_big, test_fraction_big, test_fraction_big, test_fraction_big));
+ material.setEmissiveColorFactor(LLColor3(test_fraction_big, test_fraction_big, test_fraction_big));
+ material.setMetallicFactor(test_fraction);
+ material.setRoughnessFactor(test_fraction);
+ }
+
+ LLGLTFMaterial create_test_material()
+ {
+ LLGLTFMaterial material;
+
+ apply_test_material_texture_ids(material);
+
+ apply_test_material_texture_transforms(material);
+
+ apply_test_material_factors(material);
+
+ material.setAlphaCutoff(test_fraction);
+ // Because this is the default value, it should append to the extras field to mark it as an override
+ material.setAlphaMode(LLGLTFMaterial::ALPHA_MODE_OPAQUE);
+ // Because this is the default value, it should append to the extras field to mark it as an override
+ material.setDoubleSided(false);
+
+ return material;
+ }
+
+ void ensure_gltf_material_serialize(const std::string& ensure_suffix, const LLGLTFMaterial& material_in)
+ {
+ const std::string json_in = material_in.asJSON();
+ LLGLTFMaterial material_out;
+ std::string warn_msg;
+ std::string error_msg;
+ bool serialize_success = material_out.fromJSON(json_in, warn_msg, error_msg);
+ ensure_equals("LLGLTFMaterial serialization has no warnings: " + ensure_suffix, "", warn_msg);
+ ensure_equals("LLGLTFMaterial serialization has no errors: " + ensure_suffix, "", error_msg);
+ ensure("LLGLTFMaterial serializes successfully: " + ensure_suffix, serialize_success);
+ ensure("LLGLTFMaterial is preserved when deserialized: " + ensure_suffix, material_in == material_out);
+ const std::string json_out = material_out.asJSON();
+ ensure_equals("LLGLTFMaterial is preserved when serialized: " + ensure_suffix, json_in, json_out);
+ }
+
+ void ensure_gltf_material_trimmed(const std::string& material_json, const std::string& must_not_contain)
+ {
+ ensure("LLGLTFMaterial serialization trims property '" + must_not_contain + "'", material_json.find(must_not_contain) == std::string::npos);
+ }
+
+ // Test that GLTF material fields have not changed since these tests were written
+ template<> template<>
+ void llgltfmaterial_object_t::test<1>()
+ {
+#if ADDRESS_SIZE != 32
+#if LL_WINDOWS
+ // If any fields are added/changed, these tests should be updated (consider also updating ASSET_VERSION in LLGLTFMaterial)
+ // This test result will vary between compilers, so only test a single platform
+ ensure_equals("fields supported for GLTF (sizeof check)", sizeof(LLGLTFMaterial), 216);
+#endif
+#endif
+ ensure_equals("LLGLTFMaterial texture info count", (U32)LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT, 4);
+ }
+
+ // Test that occlusion and metallicRoughness are the same (They are different for asset validation. See lluploadmaterial.cpp)
+ template<> template<>
+ void llgltfmaterial_object_t::test<2>()
+ {
+ ensure_equals("LLGLTFMaterial occlusion does not differ from metallic roughness", LLGLTFMaterial::GLTF_TEXTURE_INFO_METALLIC_ROUGHNESS, LLGLTFMaterial::GLTF_TEXTURE_INFO_OCCLUSION);
+ }
+
+ // Ensure double sided and alpha mode overrides serialize as expected
+ template<> template<>
+ void llgltfmaterial_object_t::test<3>()
+ {
+ const bool doubleSideds[] { false, true };
+ const LLGLTFMaterial::AlphaMode alphaModes[] { LLGLTFMaterial::ALPHA_MODE_OPAQUE, LLGLTFMaterial::ALPHA_MODE_BLEND, LLGLTFMaterial::ALPHA_MODE_MASK };
+ const bool forOverrides[] { false, true };
+
+ for (bool doubleSided : doubleSideds)
+ {
+ for (bool forOverride : forOverrides)
+ {
+ LLGLTFMaterial material;
+ material.setDoubleSided(doubleSided, forOverride);
+ const bool overrideBit = (doubleSided == false) && forOverride;
+ ensure_equals("LLGLTFMaterial: double sided = " + std::to_string(doubleSided) + " override bit when forOverride = " + std::to_string(forOverride), material.mOverrideDoubleSided, overrideBit);
+ ensure_gltf_material_serialize("double sided = " + std::to_string(doubleSided), material);
+ }
+ }
+
+ for (LLGLTFMaterial::AlphaMode alphaMode : alphaModes)
+ {
+ for (bool forOverride : forOverrides)
+ {
+ LLGLTFMaterial material;
+ material.setAlphaMode(alphaMode, forOverride);
+ const bool overrideBit = (alphaMode == LLGLTFMaterial::ALPHA_MODE_OPAQUE) && forOverride;
+ ensure_equals("LLGLTFMaterial: alpha mode = " + std::to_string(alphaMode) + " override bit when forOverride = " + std::to_string(forOverride), material.mOverrideAlphaMode, overrideBit);
+ ensure_gltf_material_serialize("alpha mode = " + std::to_string(alphaMode), material);
+ }
+ }
+ }
+
+ // Test that a GLTF material's transform components serialize as expected
+ template<> template<>
+ void llgltfmaterial_object_t::test<4>()
+ {
+ LLGLTFMaterial material;
+ LLGLTFMaterial::TextureTransform& transform = material.mTextureTransform[LLGLTFMaterial::GLTF_TEXTURE_INFO_BASE_COLOR];
+ transform.mOffset[VX] = 1.f;
+ transform.mOffset[VY] = 2.f;
+ transform.mScale[VX] = 0.05f;
+ transform.mScale[VY] = 100.f;
+ transform.mRotation = 1.571f;
+ ensure_gltf_material_serialize("material with transform", material);
+ }
+
+ // Test that a GLTF material avoids serializing a material unnecessarily
+ template<> template<>
+ void llgltfmaterial_object_t::test<5>()
+ {
+ {
+ const LLGLTFMaterial material;
+ const std::string material_json = material.asJSON();
+ ensure_gltf_material_trimmed(material_json, "pbrMetallicRoughness");
+ ensure_gltf_material_trimmed(material_json, "normalTexture");
+ ensure_gltf_material_trimmed(material_json, "emissiveTexture");
+ ensure_gltf_material_trimmed(material_json, "occlusionTexture");
+ }
+
+ {
+ LLGLTFMaterial metallic_factor_material;
+ metallic_factor_material.setMetallicFactor(0.5);
+ const std::string metallic_factor_material_json = metallic_factor_material.asJSON();
+ ensure_gltf_material_trimmed(metallic_factor_material_json, "baseColorTexture");
+ ensure_gltf_material_trimmed(metallic_factor_material_json, "metallicRoughnessTexture");
+ }
+ }
+
+ // Test that a GLTF material preserves values on serialization
+ template<> template<>
+ void llgltfmaterial_object_t::test<6>()
+ {
+ {
+ const LLGLTFMaterial full_material = create_test_material();
+ ensure_gltf_material_serialize("full material", full_material);
+ }
+
+ {
+ LLGLTFMaterial texture_ids_only_material;
+ apply_test_material_texture_ids(texture_ids_only_material);
+ ensure_gltf_material_serialize("material with texture IDs only", texture_ids_only_material);
+ }
+
+ {
+ LLGLTFMaterial texture_transforms_only_material;
+ apply_test_material_texture_ids(texture_transforms_only_material);
+ ensure_gltf_material_serialize("material with texture transforms only", texture_transforms_only_material);
+ }
+
+ {
+ LLGLTFMaterial factors_only_material;
+ apply_test_material_factors(factors_only_material);
+ ensure_gltf_material_serialize("material with scaling/tint factors only", factors_only_material);
+ }
+ }
+
+ // Test that sDefault is a no-op override
+ template<> template<>
+ void llgltfmaterial_object_t::test<7>()
+ {
+ const LLGLTFMaterial material_asset = create_test_material();
+ LLGLTFMaterial render_material = material_asset;
+ render_material.applyOverride(LLGLTFMaterial::sDefault);
+ ensure("LLGLTFMaterial: sDefault is a no-op override", material_asset == render_material);
+ }
+
+ // Test application of transform overrides
+ template<> template<>
+ void llgltfmaterial_object_t::test<8>()
+ {
+ LLGLTFMaterial override_material;
+ apply_test_material_texture_transforms(override_material);
+ LLGLTFMaterial render_material;
+ render_material.applyOverride(override_material);
+ ensure("LLGLTFMaterial: transform overrides", render_material == override_material);
+ }
+
+ // Test application of flag-based overrides
+ template<> template<>
+ void llgltfmaterial_object_t::test<9>()
+ {
+ {
+ LLGLTFMaterial override_material;
+ override_material.setAlphaMode(LLGLTFMaterial::ALPHA_MODE_BLEND, true);
+ override_material.setDoubleSided(true, true);
+
+ LLGLTFMaterial render_material;
+
+ render_material.applyOverride(override_material);
+
+ ensure("LLGLTFMaterial: extra overrides with non-default values applied over default", render_material == override_material);
+ }
+ {
+ LLGLTFMaterial override_material;
+ override_material.setAlphaMode(LLGLTFMaterial::ALPHA_MODE_OPAQUE, true);
+ override_material.setDoubleSided(false, true);
+
+ LLGLTFMaterial render_material;
+ override_material.setAlphaMode(LLGLTFMaterial::ALPHA_MODE_BLEND, false);
+ override_material.setDoubleSided(true, false);
+
+ render_material.applyOverride(override_material);
+ // Not interested in these flags for equality comparison
+ override_material.mOverrideDoubleSided = false;
+ override_material.mOverrideAlphaMode = false;
+
+ ensure("LLGLTFMaterial: extra overrides with default values applied over non-default", render_material == override_material);
+ }
+ }
+
+ // Test application of texture overrides
+ template<> template<>
+ void llgltfmaterial_object_t::test<10>()
+ {
+ const U32 texture_count = 2;
+ const LLUUID override_textures[texture_count] = { LLUUID::null, LLUUID::generateNewID() };
+ const LLUUID asset_textures[texture_count] = { LLUUID::generateNewID(), LLUUID::null };
+ for (U32 i = 0; i < texture_count; ++i)
+ {
+ LLGLTFMaterial override_material;
+ const LLUUID& override_texture = override_textures[i];
+ for (LLGLTFMaterial::TextureInfo j = LLGLTFMaterial::TextureInfo(0); j < LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT; j = LLGLTFMaterial::TextureInfo(U32(j) + 1))
+ {
+ override_material.setTextureId(j, override_texture, true);
+ }
+
+ LLGLTFMaterial render_material;
+ const LLUUID& asset_texture = asset_textures[i];
+ for (LLGLTFMaterial::TextureInfo j = LLGLTFMaterial::TextureInfo(0); j < LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT; j = LLGLTFMaterial::TextureInfo(U32(j) + 1))
+ {
+ render_material.setTextureId(j, asset_texture, false);
+ }
+
+ render_material.applyOverride(override_material);
+
+ for (LLGLTFMaterial::TextureInfo j = LLGLTFMaterial::TextureInfo(0); j < LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT; j = LLGLTFMaterial::TextureInfo(U32(j) + 1))
+ {
+ const LLUUID& render_texture = render_material.mTextureId[j];
+ ensure_equals("LLGLTFMaterial: Override texture ID " + override_texture.asString() + " replaces underlying texture ID " + asset_texture.asString(), render_texture, override_texture);
+ }
+ }
+ }
+
+ // Test non-persistence of default value flags in overrides
+ template<> template<>
+ void llgltfmaterial_object_t::test<11>()
+ {
+ const S32 non_default_alpha_modes[] = { LLGLTFMaterial::ALPHA_MODE_BLEND, LLGLTFMaterial::ALPHA_MODE_MASK };
+ for (S32 non_default_alpha_mode : non_default_alpha_modes)
+ {
+ LLGLTFMaterial material;
+ // Set default alpha mode
+ material.setAlphaMode(LLGLTFMaterial::ALPHA_MODE_OPAQUE, true);
+ ensure_equals("LLGLTFMaterial: alpha mode override flag set", material.mOverrideAlphaMode, true);
+ // Set non-default alpha mode
+ material.setAlphaMode(non_default_alpha_mode, true);
+ ensure_equals("LLGLTFMaterial: alpha mode override flag unset", material.mOverrideAlphaMode, false);
+ }
+
+ {
+ // Set default double sided
+ LLGLTFMaterial material;
+ material.setDoubleSided(false, true);
+ ensure_equals("LLGLTFMaterial: double sided override flag set", material.mOverrideDoubleSided, true);
+ // Set non-default double sided
+ material.setDoubleSided(true, true);
+ ensure_equals("LLGLTFMaterial: double sided override flag unset", material.mOverrideDoubleSided, false);
+ }
+ }
+}
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: