#pragma once /** * @file buffer_util.inl * @brief LL GLTF Implementation * * $LicenseInfo:firstyear=2024&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2024, 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$ */ // inline template implementations for copying data out of GLTF buffers // DO NOT include from header files to avoid the need to rebuild the whole project // whenever we add support for more types #ifdef _MSC_VER #define LL_FUNCSIG __FUNCSIG__ #else #define LL_FUNCSIG __PRETTY_FUNCTION__ #endif #include "accessor.h" namespace LL { namespace GLTF { using string_view = boost::json::string_view; // copy one Scalar from src to dst template inline void copyScalar(S* src, T& dst) { LL_ERRS() << "TODO: implement " << LL_FUNCSIG << LL_ENDL; } // copy one vec2 from src to dst template inline void copyVec2(S* src, T& dst) { LL_ERRS() << "TODO: implement " << LL_FUNCSIG << LL_ENDL; } // copy one vec3 from src to dst template inline void copyVec3(S* src, T& dst) { LL_ERRS() << "TODO: implement " << LL_FUNCSIG << LL_ENDL; } // copy one vec4 from src to dst template inline void copyVec4(S* src, T& dst) { LL_ERRS() << "TODO: implement " << LL_FUNCSIG << LL_ENDL; } // copy one mat2 from src to dst template inline void copyMat2(S* src, T& dst) { LL_ERRS() << "TODO: implement " << LL_FUNCSIG << LL_ENDL; } // copy one mat3 from src to dst template inline void copyMat3(S* src, T& dst) { LL_ERRS() << "TODO: implement " << LL_FUNCSIG << LL_ENDL; } // copy one mat4 from src to dst template inline void copyMat4(S* src, T& dst) { LL_ERRS() << "TODO: implement " << LL_FUNCSIG << LL_ENDL; } //========================================================================================================= // concrete implementations for different types of source and destination //========================================================================================================= template<> inline void copyScalar(F32* src, F32& dst) { dst = *src; } template<> inline void copyScalar(U32* src, U32& dst) { dst = *src; } template<> inline void copyScalar(U32* src, U16& dst) { dst = *src; } template<> inline void copyScalar(U16* src, U16& dst) { dst = *src; } template<> inline void copyScalar(U16* src, U32& dst) { dst = *src; } template<> inline void copyScalar(U8* src, U16& dst) { dst = *src; } template<> inline void copyScalar(U8* src, U32& dst) { dst = *src; } template<> inline void copyVec2(F32* src, LLVector2& dst) { dst.set(src[0], src[1]); } template<> inline void copyVec3(F32* src, vec3& dst) { dst = vec3(src[0], src[1], src[2]); } template<> inline void copyVec3(F32* src, LLVector4a& dst) { dst.load3(src); } template<> inline void copyVec3(U16* src, LLColor4U& dst) { dst.set(src[0], src[1], src[2], 255); } template<> inline void copyVec4(U8* src, LLColor4U& dst) { dst.set(src[0], src[1], src[2], src[3]); } template<> inline void copyVec4(U16* src, U64& dst) { U16* data = (U16*)&dst; data[0] = src[0]; data[1] = src[1]; data[2] = src[2]; data[3] = src[3]; } template<> inline void copyVec4(U16* src, LLColor4U& dst) { dst.set(src[0], src[1], src[2], src[3]); } template<> inline void copyVec4(F32* src, LLColor4U& dst) { dst.set(src[0]*255, src[1]*255, src[2]*255, src[3]*255); } template<> inline void copyVec4(F32* src, LLVector4a& dst) { dst.loadua(src); } template<> inline void copyVec4(U16* src, LLVector4a& dst) { dst.set(src[0], src[1], src[2], src[3]); } template<> inline void copyVec4(U8* src, LLVector4a& dst) { dst.set(src[0], src[1], src[2], src[3]); } template<> inline void copyVec4(F32* src, quat& dst) { dst.x = src[0]; dst.y = src[1]; dst.z = src[2]; dst.w = src[3]; } template<> inline void copyMat4(F32* src, mat4& dst) { dst = glm::make_mat4(src); } //========================================================================================================= // copy from src to dst, stride is the number of bytes between each element in src, count is number of elements to copy template inline void copyScalar(S* src, LLStrider dst, S32 stride, S32 count) { for (S32 i = 0; i < count; ++i) { copyScalar(src, *dst); dst++; src = (S*)((U8*)src + stride); } } // copy from src to dst, stride is the number of bytes between each element in src, count is number of elements to copy template inline void copyVec2(S* src, LLStrider dst, S32 stride, S32 count) { for (S32 i = 0; i < count; ++i) { copyVec2(src, *dst); dst++; src = (S*)((U8*)src + stride); } } // copy from src to dst, stride is the number of bytes between each element in src, count is number of elements to copy template inline void copyVec3(S* src, LLStrider dst, S32 stride, S32 count) { for (S32 i = 0; i < count; ++i) { copyVec3(src, *dst); dst++; src = (S*)((U8*)src + stride); } } // copy from src to dst, stride is the number of bytes between each element in src, count is number of elements to copy template inline void copyVec4(S* src, LLStrider dst, S32 stride, S32 count) { for (S32 i = 0; i < count; ++i) { copyVec4(src, *dst); dst++; src = (S*)((U8*)src + stride); } } // copy from src to dst, stride is the number of bytes between each element in src, count is number of elements to copy template inline void copyMat2(S* src, LLStrider dst, S32 stride, S32 count) { for (S32 i = 0; i < count; ++i) { copyMat2(src, *dst); dst++; src = (S*)((U8*)src + stride); } } // copy from src to dst, stride is the number of bytes between each element in src, count is number of elements to copy template inline void copyMat3(S* src, LLStrider dst, S32 stride, S32 count) { for (S32 i = 0; i < count; ++i) { copyMat3(src, *dst); dst++; src = (S*)((U8*)src + stride); } } // copy from src to dst, stride is the number of bytes between each element in src, count is number of elements to copy template inline void copyMat4(S* src, LLStrider dst, S32 stride, S32 count) { for (S32 i = 0; i < count; ++i) { copyMat4(src, *dst); dst++; src = (S*)((U8*)src + stride); } } template inline void copy(Asset& asset, Accessor& accessor, const S* src, LLStrider& dst, S32 byteStride) { if (accessor.mType == Accessor::Type::SCALAR) { S32 stride = byteStride == 0 ? sizeof(S) * 1 : byteStride; copyScalar((S*)src, dst, stride, accessor.mCount); } else if (accessor.mType == Accessor::Type::VEC2) { S32 stride = byteStride == 0 ? sizeof(S) * 2 : byteStride; copyVec2((S*)src, dst, stride, accessor.mCount); } else if (accessor.mType == Accessor::Type::VEC3) { S32 stride = byteStride == 0 ? sizeof(S) * 3 : byteStride; copyVec3((S*)src, dst, stride, accessor.mCount); } else if (accessor.mType == Accessor::Type::VEC4) { S32 stride = byteStride == 0 ? sizeof(S) * 4 : byteStride; copyVec4((S*)src, dst, stride, accessor.mCount); } else if (accessor.mType == Accessor::Type::MAT2) { S32 stride = byteStride == 0 ? sizeof(S) * 4 : byteStride; copyMat2((S*)src, dst, stride, accessor.mCount); } else if (accessor.mType == Accessor::Type::MAT3) { S32 stride = byteStride == 0 ? sizeof(S) * 9 : byteStride; copyMat3((S*)src, dst, stride, accessor.mCount); } else if (accessor.mType == Accessor::Type::MAT4) { S32 stride = byteStride == 0 ? sizeof(S) * 16 : byteStride; copyMat4((S*)src, dst, stride, accessor.mCount); } else { LL_ERRS("GLTF") << "Unsupported accessor type" << LL_ENDL; } } // copy data from accessor to strider template inline void copy(Asset& asset, Accessor& accessor, LLStrider& dst) { const BufferView& bufferView = asset.mBufferViews[accessor.mBufferView]; const Buffer& buffer = asset.mBuffers[bufferView.mBuffer]; const U8* src = buffer.mData.data() + bufferView.mByteOffset + accessor.mByteOffset; switch (accessor.mComponentType) { case Accessor::ComponentType::FLOAT: copy(asset, accessor, (const F32*)src, dst, bufferView.mByteStride); break; case Accessor::ComponentType::UNSIGNED_INT: copy(asset, accessor, (const U32*)src, dst, bufferView.mByteStride); break; case Accessor::ComponentType::SHORT: copy(asset, accessor, (const S16*)src, dst, bufferView.mByteStride); break; case Accessor::ComponentType::UNSIGNED_SHORT: copy(asset, accessor, (const U16*)src, dst, bufferView.mByteStride); break; case Accessor::ComponentType::BYTE: copy(asset, accessor, (const S8*)src, dst, bufferView.mByteStride); break; case Accessor::ComponentType::UNSIGNED_BYTE: copy(asset, accessor, (const U8*)src, dst, bufferView.mByteStride); break; default: LL_ERRS("GLTF") << "Invalid component type" << LL_ENDL; break; } } // copy data from accessor to vector template inline void copy(Asset& asset, Accessor& accessor, std::vector& dst) { dst.resize(accessor.mCount); LLStrider strider = dst.data(); copy(asset, accessor, strider); } //========================================================================================================= // boost::json copying utilities // ======================================================================================================== //====================== unspecialized base template, single value =========================== // to/from Value template inline bool copy(const Value& src, T& dst) { dst = src; return true; } template inline bool write(const T& src, Value& dst) { dst = boost::json::object(); src.serialize(dst.as_object()); return true; } template inline bool copy(const Value& src, std::unordered_map& dst) { if (src.is_object()) { const boost::json::object& obj = src.as_object(); for (const auto& [key, value] : obj) { copy(value, dst[key]); } return true; } return false; } template inline bool write(const std::unordered_map& src, Value& dst) { boost::json::object obj; for (const auto& [key, value] : src) { Value v; if (write(value, v)) { obj[key] = v; } else { return false; } } dst = obj; return true; } // to/from array template inline bool copy(const Value& src, std::vector& dst) { if (src.is_array()) { const boost::json::array& arr = src.get_array(); dst.resize(arr.size()); for (size_t i = 0; i < arr.size(); ++i) { copy(arr[i], dst[i]); } return true; } return false; } template inline bool write(const std::vector& src, Value& dst) { boost::json::array arr; for (const T& t : src) { Value v; if (write(t, v)) { arr.push_back(v); } else { return false; } } dst = arr; return true; } // to/from object member template inline bool copy(const boost::json::object& src, string_view member, T& dst) { auto it = src.find(member); if (it != src.end()) { return copy(it->value(), dst); } return false; } // always write a member to an object without checking default template inline bool write_always(const T& src, string_view member, boost::json::object& dst) { Value& v = dst[member]; if (!write(src, v)) { dst.erase(member); return false; } return true; } // to/from extension // for internal use only, use copy_extensions instead template 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 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 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, // mUnlit, "KHR_materials_unlit", // mPbrSpecularGlossiness, "KHR_materials_pbrSpecularGlossiness"); // returns true if any of the extensions are written template 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 inline bool write(const T& src, string_view member, boost::json::object& dst, const T& default_value = T()) { if (src != default_value) { return write_always(src, member, dst); } return false; } template inline bool write(const std::unordered_map& src, string_view member, boost::json::object& dst, const std::unordered_map& default_value = std::unordered_map()) { if (!src.empty()) { Value v; if (write(src, v)) { dst[member] = v; return true; } } return false; } template inline bool write(const std::vector& src, string_view member, boost::json::object& dst, const std::vector& deafault_value = std::vector()) { if (!src.empty()) { Value v; if (write(src, v)) { dst[member] = v; return true; } } return false; } template inline bool copy(const Value& src, string_view member, T& dst) { if (src.is_object()) { const boost::json::object& obj = src.as_object(); return copy(obj, member, dst); } return false; } // Accessor::ComponentType template<> inline bool copy(const Value& src, Accessor::ComponentType& dst) { if (src.is_int64()) { dst = (Accessor::ComponentType)src.get_int64(); return true; } return false; } template<> inline bool write(const Accessor::ComponentType& src, Value& dst) { dst = (S32)src; return true; } //Primitive::Mode template<> inline bool copy(const Value& src, Primitive::Mode& dst) { if (src.is_int64()) { dst = (Primitive::Mode)src.get_int64(); return true; } return false; } template<> inline bool write(const Primitive::Mode& src, Value& dst) { dst = (S32)src; return true; } // vec4 template<> inline bool copy(const Value& src, vec4& dst) { if (src.is_array()) { const boost::json::array& arr = src.as_array(); if (arr.size() == 4) { vec4 v; std::error_code ec; v.x = arr[0].to_number(ec); if (ec) return false; v.y = arr[1].to_number(ec); if (ec) return false; v.z = arr[2].to_number(ec); if (ec) return false; v.w = arr[3].to_number(ec); if (ec) return false; dst = v; return true; } } return false; } template<> inline bool write(const vec4& src, Value& dst) { dst = boost::json::array(); boost::json::array& arr = dst.get_array(); arr.resize(4); arr[0] = src.x; arr[1] = src.y; arr[2] = src.z; arr[3] = src.w; return true; } // quat template<> inline bool copy(const Value& src, quat& dst) { if (src.is_array()) { const boost::json::array& arr = src.as_array(); if (arr.size() == 4) { std::error_code ec; dst.x = arr[0].to_number(ec); if (ec) return false; dst.y = arr[1].to_number(ec); if (ec) return false; dst.z = arr[2].to_number(ec); if (ec) return false; dst.w = arr[3].to_number(ec); if (ec) return false; return true; } } return false; } template<> inline bool write(const quat& src, Value& dst) { dst = boost::json::array(); boost::json::array& arr = dst.get_array(); arr.resize(4); arr[0] = src.x; arr[1] = src.y; arr[2] = src.z; arr[3] = src.w; return true; } // vec3 template<> inline bool copy(const Value& src, vec3& dst) { if (src.is_array()) { const boost::json::array& arr = src.as_array(); if (arr.size() == 3) { std::error_code ec; vec3 t; t.x = arr[0].to_number(ec); if (ec) return false; t.y = arr[1].to_number(ec); if (ec) return false; t.z = arr[2].to_number(ec); if (ec) return false; dst = t; return true; } } return false; } template<> inline bool write(const vec3& src, Value& dst) { dst = boost::json::array(); boost::json::array& arr = dst.as_array(); arr.resize(3); arr[0] = src.x; arr[1] = src.y; arr[2] = src.z; return true; } // vec2 template<> inline bool copy(const Value& src, vec2& dst) { if (src.is_array()) { const boost::json::array& arr = src.as_array(); if (arr.size() == 2) { std::error_code ec; vec2 t; t.x = arr[0].to_number(ec); if (ec) return false; t.y = arr[1].to_number(ec); if (ec) return false; dst = t; return true; } } return false; } template<> inline bool write(const vec2& src, Value& dst) { dst = boost::json::array(); boost::json::array& arr = dst.as_array(); arr.resize(2); arr[0] = src.x; arr[1] = src.y; return true; } // bool template<> inline bool copy(const Value& src, bool& dst) { if (src.is_bool()) { dst = src.get_bool(); return true; } return false; } template<> inline bool write(const bool& src, Value& dst) { dst = src; return true; } // F32 template<> inline bool copy(const Value& src, F32& dst) { std::error_code ec; F32 t = src.to_number(ec); if (ec) return false; dst = t; return true; } template<> inline bool write(const F32& src, Value& dst) { dst = src; return true; } // U32 template<> inline bool copy(const Value& src, U32& dst) { if (src.is_int64()) { dst = src.get_int64(); return true; } return false; } template<> inline bool write(const U32& src, Value& dst) { dst = src; return true; } // F64 template<> inline bool copy(const Value& src, F64& dst) { std::error_code ec; F64 t = src.to_number(ec); if (ec) return false; dst = t; return true; } template<> inline bool write(const F64& src, Value& dst) { dst = src; return true; } // Accessor::Type template<> inline bool copy(const Value& src, Accessor::Type& dst) { if (src.is_string()) { dst = gltf_type_to_enum(src.get_string().c_str()); return true; } return false; } template<> inline bool write(const Accessor::Type& src, Value& dst) { dst = enum_to_gltf_type(src); return true; } // S32 template<> inline bool copy(const Value& src, S32& dst) { if (src.is_int64()) { dst = src.get_int64(); return true; } return false; } template<> inline bool write(const S32& src, Value& dst) { dst = src; return true; } // std::string template<> inline bool copy(const Value& src, std::string& dst) { if (src.is_string()) { dst = src.get_string().c_str(); return true; } return false; } template<> inline bool write(const std::string& src, Value& dst) { dst = src; return true; } // mat4 template<> inline bool copy(const Value& src, mat4& dst) { if (src.is_array()) { const boost::json::array& arr = src.get_array(); if (arr.size() == 16) { // populate a temporary local in case // we hit an error in the middle of the array // (don't partially write a matrix) mat4 t; F32* p = glm::value_ptr(t); for (U32 i = 0; i < arr.size(); ++i) { std::error_code ec; p[i] = arr[i].to_number(ec); if (ec) { return false; } } dst = t; return true; } } return false; } template<> inline bool write(const mat4& src, Value& dst) { dst = boost::json::array(); boost::json::array& arr = dst.get_array(); arr.resize(16); const F32* p = glm::value_ptr(src); for (U32 i = 0; i < 16; ++i) { arr[i] = p[i]; } return true; } // Material::AlphaMode template<> inline bool copy(const Value& src, Material::AlphaMode& dst) { if (src.is_string()) { dst = gltf_alpha_mode_to_enum(src.get_string().c_str()); return true; } return true; } template<> inline bool write(const Material::AlphaMode& src, Value& dst) { dst = enum_to_gltf_alpha_mode(src); return true; } // // ======================================================================================================== } }