From 411969c87c73c6bf251b237ab3c3e4ff9a4271f8 Mon Sep 17 00:00:00 2001 From: Lars Mueller Date: Tue, 21 May 2024 19:59:04 +0200 Subject: [PATCH] Bring accessor implementation from other branch over --- irr/src/CGLTFMeshFileLoader.cpp | 508 +++++++++++++++++++++----------- irr/src/CGLTFMeshFileLoader.h | 95 +++--- 2 files changed, 392 insertions(+), 211 deletions(-) diff --git a/irr/src/CGLTFMeshFileLoader.cpp b/irr/src/CGLTFMeshFileLoader.cpp index 31f56038f..63e2f47ea 100644 --- a/irr/src/CGLTFMeshFileLoader.cpp +++ b/irr/src/CGLTFMeshFileLoader.cpp @@ -24,6 +24,8 @@ #include #include +namespace irr { + /* Notes on the coordinate system. * * glTF uses a right-handed coordinate system where +Z is the @@ -35,33 +37,284 @@ * of the vertex indices. */ -namespace irr { +// Right-to-left handedness conversions + +template +static inline T convertHandedness(const T &t); + +template <> +core::vector3df convertHandedness(const core::vector3df &p) +{ + return core::vector3df(p.X, p.Y, -p.Z); +} namespace scene { -CGLTFMeshFileLoader::BufferOffset::BufferOffset( - const std::vector& buf, - const std::size_t offset) - : m_buf(buf) - , m_offset(offset) +using CMFL = CGLTFMeshFileLoader; + +template +CMFL::Accessor +CMFL::Accessor::sparseIndices(const tiniergltf::GlTF &model, + const tiniergltf::AccessorSparseIndices &indices, + const std::size_t count) { + const auto &view = model.bufferViews->at(indices.bufferView); + const auto byteStride = view.byteStride.value_or(indices.elementSize()); + + const auto &buffer = model.buffers->at(view.buffer); + const auto source = buffer.data.data() + view.byteOffset + indices.byteOffset; + + return CMFL::Accessor(source, byteStride, count); } -CGLTFMeshFileLoader::BufferOffset::BufferOffset( - const CGLTFMeshFileLoader::BufferOffset& other, - const std::size_t fromOffset) - : m_buf(other.m_buf) - , m_offset(other.m_offset + fromOffset) +template +CMFL::Accessor +CMFL::Accessor::sparseValues(const tiniergltf::GlTF &model, + const tiniergltf::AccessorSparseValues &values, + const std::size_t count, + const std::size_t defaultByteStride) { + const auto &view = model.bufferViews->at(values.bufferView); + const auto byteStride = view.byteStride.value_or(defaultByteStride); + + const auto &buffer = model.buffers->at(view.buffer); + const auto source = buffer.data.data() + view.byteOffset + values.byteOffset; + + return CMFL::Accessor(source, byteStride, count); } -/** - * Get a raw unsigned char (ubyte) from a buffer offset. -*/ -unsigned char CGLTFMeshFileLoader::BufferOffset::at( - const std::size_t fromOffset) const +template +CMFL::Accessor +CMFL::Accessor::base(const tiniergltf::GlTF &model, std::size_t accessorIdx) { - return m_buf.at(m_offset + fromOffset); + const auto &accessor = model.accessors->at(accessorIdx); + + if (!accessor.bufferView.has_value()) { + return Accessor(accessor.count); + } + + const auto &view = model.bufferViews->at(accessor.bufferView.value()); + const auto byteStride = view.byteStride.value_or(accessor.elementSize()); + + const auto &buffer = model.buffers->at(view.buffer); + const auto source = buffer.data.data() + view.byteOffset + accessor.byteOffset; + + return Accessor(source, byteStride, accessor.count); +} + +template +CMFL::Accessor +CMFL::Accessor::make(const tiniergltf::GlTF &model, std::size_t accessorIdx) +{ + const auto &accessor = model.accessors->at(accessorIdx); + if (accessor.componentType != getComponentType() || accessor.type != getType()) + throw std::runtime_error("invalid accessor"); + + const auto base = Accessor::base(model, accessorIdx); + + if (accessor.sparse.has_value()) { + std::vector vec(accessor.count); + for (std::size_t i = 0; i < accessor.count; ++i) { + vec[i] = base.get(i); + } + const auto overriddenCount = accessor.sparse->count; + const auto indicesAccessor = ([&]() -> AccessorVariant { + switch (accessor.sparse->indices.componentType) { + case tiniergltf::AccessorSparseIndices::ComponentType::UNSIGNED_BYTE: + return Accessor::sparseIndices(model, accessor.sparse->indices, overriddenCount); + case tiniergltf::AccessorSparseIndices::ComponentType::UNSIGNED_SHORT: + return Accessor::sparseIndices(model, accessor.sparse->indices, overriddenCount); + case tiniergltf::AccessorSparseIndices::ComponentType::UNSIGNED_INT: + return Accessor::sparseIndices(model, accessor.sparse->indices, overriddenCount); + } + throw std::logic_error("invalid enum value"); + })(); + + const auto valuesAccessor = Accessor::sparseValues(model, + accessor.sparse->values, overriddenCount, + accessor.bufferView.has_value() + ? model.bufferViews->at(*accessor.bufferView).byteStride.value_or(accessor.elementSize()) + : accessor.elementSize()); + + for (std::size_t i = 0; i < overriddenCount; ++i) { + u32 index; + std::visit([&](auto &&acc) { index = acc.get(i); }, indicesAccessor); + if (index >= accessor.count) + throw std::runtime_error("index out of bounds"); + vec[index] = valuesAccessor.get(i); + } + return Accessor(vec, accessor.count); + } + + return base; +} + +#define ACCESSOR_TYPES(T, U, V) \ + template <> \ + constexpr tiniergltf::Accessor::Type CMFL::Accessor::getType() \ + { \ + return tiniergltf::Accessor::Type::U; \ + } \ + template <> \ + constexpr tiniergltf::Accessor::ComponentType CMFL::Accessor::getComponentType() \ + { \ + return tiniergltf::Accessor::ComponentType::V; \ + } + +#define VEC_ACCESSOR_TYPES(T, U, n) \ + template <> \ + constexpr tiniergltf::Accessor::Type CMFL::Accessor>::getType() \ + { \ + return tiniergltf::Accessor::Type::VEC##n; \ + } \ + template <> \ + constexpr tiniergltf::Accessor::ComponentType CMFL::Accessor>::getComponentType() \ + { \ + return tiniergltf::Accessor::ComponentType::U; \ + } \ + template <> \ + std::array CMFL::rawget(const void *ptr) \ + { \ + const T *tptr = reinterpret_cast(ptr); \ + std::array res; \ + for (u8 i = 0; i < n; ++i) \ + res[i] = rawget(tptr + i); \ + return res; \ + } + +#define ACCESSOR_PRIMITIVE(T, U) \ + ACCESSOR_TYPES(T, SCALAR, U) \ + VEC_ACCESSOR_TYPES(T, U, 2) \ + VEC_ACCESSOR_TYPES(T, U, 3) \ + VEC_ACCESSOR_TYPES(T, U, 4) + +ACCESSOR_PRIMITIVE(f32, FLOAT) +ACCESSOR_PRIMITIVE(u8, UNSIGNED_BYTE) +ACCESSOR_PRIMITIVE(u16, UNSIGNED_SHORT) +ACCESSOR_PRIMITIVE(u32, UNSIGNED_INT) + +ACCESSOR_TYPES(core::vector3df, VEC3, FLOAT) + +template +T CMFL::Accessor::get(std::size_t i) const +{ + // Buffer-based accessor: Read directly from the buffer. + if (std::holds_alternative(source)) { + const auto bufsrc = std::get(source); + return rawget(bufsrc.ptr + i * bufsrc.byteStride); + } + // Array-based accessor (used for sparse accessors): Read from array. + if (std::holds_alternative>(source)) { + return std::get>(source)[i]; + } + // Default-initialized accessor. + // We differ slightly from glTF here in that + // we default-initialize quaternions and matrices properly, + // but this does not cause any discrepancies for valid glTF models. + std::get>(source); + return T(); +} + +// Note: clang and gcc should both optimize this out. +static inline bool isBigEndian() +{ + const u16 x = 0xFF00; + return *(const u8 *)(&x); +} + +template +T CMFL::rawget(const void *ptr) +{ + if (!isBigEndian()) + return *reinterpret_cast(ptr); + // glTF uses little endian. + // On big-endian systems, we have to swap the byte order. + // TODO test this on a big endian system + const u8 *bptr = reinterpret_cast(ptr); + u8 bytes[sizeof(T)]; + for (std::size_t i = 0; i < sizeof(T); ++i) { + bytes[sizeof(T) - i - 1] = bptr[i]; + } + return *reinterpret_cast(bytes); +} + +// Note that these "more specialized templates" should win. + +template <> +core::matrix4 CMFL::rawget(const void *ptr) +{ + const f32 *fptr = reinterpret_cast(ptr); + f32 M[16]; + for (u8 i = 0; i < 16; ++i) { + M[i] = rawget(fptr + i); + } + core::matrix4 mat; + mat.setM(M); + return mat; +} + +template <> +core::vector3df CMFL::rawget(const void *ptr) +{ + const f32 *fptr = reinterpret_cast(ptr); + return core::vector3df( + rawget(fptr), + rawget(fptr + 1), + rawget(fptr + 2)); +} + +template <> +core::quaternion CMFL::rawget(const void *ptr) +{ + const f32 *fptr = reinterpret_cast(ptr); + return core::quaternion( + rawget(fptr), + rawget(fptr + 1), + rawget(fptr + 2), + rawget(fptr + 3)); +} + +template +CMFL::NormalizedValuesAccessor +CMFL::createNormalizedValuesAccessor( + const tiniergltf::GlTF &model, + const std::size_t accessorIdx) +{ + const auto &acc = model.accessors->at(accessorIdx); + switch (acc.componentType) { + case tiniergltf::Accessor::ComponentType::UNSIGNED_BYTE: + return Accessor>::make(model, accessorIdx); + case tiniergltf::Accessor::ComponentType::UNSIGNED_SHORT: + return Accessor>::make(model, accessorIdx); + case tiniergltf::Accessor::ComponentType::FLOAT: + return Accessor>::make(model, accessorIdx); + default: + throw std::runtime_error("invalid component type"); + } +} + +template +std::array CMFL::getNormalizedValues( + const NormalizedValuesAccessor &accessor, + const std::size_t i) +{ + std::array values; + if (std::holds_alternative>>(accessor)) { + const auto u8s = std::get>>(accessor).get(i); + for (u8 i = 0; i < N; ++i) + values[i] = static_cast(u8s[i]) / std::numeric_limits::max(); + } else if (std::holds_alternative>>(accessor)) { + const auto u16s = std::get>>(accessor).get(i); + for (u8 i = 0; i < N; ++i) + values[i] = static_cast(u16s[i]) / std::numeric_limits::max(); + } else { + values = std::get>>(accessor).get(i); + for (u8 i = 0; i < N; ++i) { + if (values[i] < 0 || values[i] > 1) + throw std::runtime_error("invalid normalized value"); + } + } + return values; } CGLTFMeshFileLoader::CGLTFMeshFileLoader() noexcept @@ -262,49 +515,52 @@ void CGLTFMeshFileLoader::MeshExtractor::loadNodes() const } /** - * Extracts GLTF mesh indices into the irrlicht model. -*/ -std::optional> CGLTFMeshFileLoader::MeshExtractor::getIndices( + * Extracts GLTF mesh indices. + */ +std::optional> CMFL::MeshExtractor::getIndices( const std::size_t meshIdx, const std::size_t primitiveIdx) const { - const auto accessorIdx = getIndicesAccessorIdx(meshIdx, primitiveIdx); + const auto accessorIdx = m_gltf_model.meshes->at(meshIdx).primitives.at(primitiveIdx).indices; if (!accessorIdx.has_value()) return std::nullopt; // non-indexed geometry - const auto &accessor = m_gltf_model.accessors->at(accessorIdx.value()); - - const auto& buf = getBuffer(accessorIdx.value()); + + const auto accessor = ([&]() -> AccessorVariant { + const auto &acc = m_gltf_model.accessors->at(*accessorIdx); + switch (acc.componentType) { + case tiniergltf::Accessor::ComponentType::UNSIGNED_BYTE: + return Accessor::make(m_gltf_model, *accessorIdx); + case tiniergltf::Accessor::ComponentType::UNSIGNED_SHORT: + return Accessor::make(m_gltf_model, *accessorIdx); + case tiniergltf::Accessor::ComponentType::UNSIGNED_INT: + return Accessor::make(m_gltf_model, *accessorIdx); + default: + throw std::runtime_error("invalid component type"); + } + })(); + const auto count = std::visit([](auto &&a) { return a.getCount(); }, accessor); std::vector indices; - const auto count = getElemCount(accessorIdx.value()); - indices.reserve(count); for (std::size_t i = 0; i < count; ++i) { + // TODO (low-priority, maybe never) also reverse winding order based on determinant of global transform + // FIXME this hack also reverses triangle draw order std::size_t elemIdx = count - i - 1; // reverse index order u16 index; // Note: glTF forbids the max value for each component type. - switch (accessor.componentType) { - case tiniergltf::Accessor::ComponentType::UNSIGNED_BYTE: { - index = readPrimitive(BufferOffset(buf, elemIdx * sizeof(u8))); - if (index == std::numeric_limits::max()) - throw std::runtime_error("invalid index"); - break; - } - case tiniergltf::Accessor::ComponentType::UNSIGNED_SHORT: { - index = readPrimitive(BufferOffset(buf, elemIdx * sizeof(u16))); - if (index == std::numeric_limits::max()) - throw std::runtime_error("invalid index"); - break; - } - case tiniergltf::Accessor::ComponentType::UNSIGNED_INT: { - u32 indexWide = readPrimitive(BufferOffset(buf, elemIdx * sizeof(u32))); - // Use >= here for consistency. - if (indexWide >= std::numeric_limits::max()) - throw std::runtime_error("index too large (>= 65536)"); - index = static_cast(indexWide); - break; - } - default: - throw std::runtime_error("invalid index component type"); + if (std::holds_alternative>(accessor)) { + index = std::get>(accessor).get(elemIdx); + if (index == std::numeric_limits::max()) + throw std::runtime_error("invalid index"); + } else if (std::holds_alternative>(accessor)) { + index = std::get>(accessor).get(elemIdx); + if (index == std::numeric_limits::max()) + throw std::runtime_error("invalid index"); + } else if (std::holds_alternative>(accessor)) { + u32 indexWide = std::get>(accessor).get(elemIdx); + // Use >= here for consistency. + if (indexWide >= std::numeric_limits::max()) + throw std::runtime_error("index too large (>= 65536)"); + index = static_cast(indexWide); } indices.push_back(index); } @@ -314,32 +570,33 @@ std::optional> CGLTFMeshFileLoader::MeshExtractor::getIndices( /** * Create a vector of video::S3DVertex (model data) from a mesh & primitive index. -*/ -std::optional> CGLTFMeshFileLoader::MeshExtractor::getVertices( + */ +std::optional> CMFL::MeshExtractor::getVertices( const std::size_t meshIdx, const std::size_t primitiveIdx) const { - const auto positionAccessorIdx = getPositionAccessorIdx( - meshIdx, primitiveIdx); + const auto &attributes = m_gltf_model.meshes->at(meshIdx).primitives.at(primitiveIdx).attributes; + const auto positionAccessorIdx = attributes.position; if (!positionAccessorIdx.has_value()) { // "When positions are not specified, client implementations SHOULD skip primitive's rendering" return std::nullopt; } - std::vector vertices{}; - vertices.resize(getElemCount(*positionAccessorIdx)); + std::vector vertices; + const auto vertexCount = m_gltf_model.accessors->at(*positionAccessorIdx).count; + vertices.resize(vertexCount); copyPositions(*positionAccessorIdx, vertices); - const auto normalAccessorIdx = getNormalAccessorIdx( - meshIdx, primitiveIdx); + const auto normalAccessorIdx = attributes.normal; if (normalAccessorIdx.has_value()) { copyNormals(normalAccessorIdx.value(), vertices); } + // TODO verify that the automatic normal recalculation done in Minetest indeed works correctly - const auto tCoordAccessorIdx = getTCoordAccessorIdx( - meshIdx, primitiveIdx); - if (tCoordAccessorIdx.has_value()) { - copyTCoords(tCoordAccessorIdx.value(), vertices); + const auto &texcoords = m_gltf_model.meshes->at(meshIdx).primitives[primitiveIdx].attributes.texcoord; + if (texcoords.has_value()) { + const auto tCoordAccessorIdx = texcoords->at(0); + copyTCoords(tCoordAccessorIdx, vertices); } return vertices; @@ -362,70 +619,17 @@ std::size_t CGLTFMeshFileLoader::MeshExtractor::getPrimitiveCount( return m_gltf_model.meshes->at(meshIdx).primitives.size(); } -/** - * Templated buffer reader. Based on type width. - * This is specifically used to build upon to read more complex data types. - * It is also used raw to read arrays directly. - * Basically we're using the width of the type to infer - * how big of a gap we have from the beginning of the buffer. -*/ -template -T CGLTFMeshFileLoader::MeshExtractor::readPrimitive( - const BufferOffset& readFrom) -{ - unsigned char d[sizeof(T)]{}; - for (std::size_t i = 0; i < sizeof(T); ++i) { - d[i] = readFrom.at(i); - } - T dest; - std::memcpy(&dest, d, sizeof(dest)); - return dest; -} - -/** - * Read a vector2df from a buffer at an offset. - * @return vec2 core::Vector2df -*/ -core::vector2df CGLTFMeshFileLoader::MeshExtractor::readVec2DF( - const CGLTFMeshFileLoader::BufferOffset& readFrom) -{ - return core::vector2df(readPrimitive(readFrom), - readPrimitive(BufferOffset(readFrom, sizeof(float)))); - -} - -/** - * Read a vector3df from a buffer at an offset. - * Also does right-to-left-handed coordinate system conversion (inverts Z axis). - * @return vec3 core::Vector3df -*/ -core::vector3df CGLTFMeshFileLoader::MeshExtractor::readVec3DF( - const BufferOffset& readFrom, - const core::vector3df scale = {1.0f,1.0f,1.0f}) -{ - return core::vector3df( - readPrimitive(readFrom), - readPrimitive(BufferOffset(readFrom, sizeof(float))), - -readPrimitive(BufferOffset(readFrom, 2 * - sizeof(float)))); -} - /** * Streams vertex positions raw data into usable buffer via reference. * Buffer: ref Vector */ void CGLTFMeshFileLoader::MeshExtractor::copyPositions( const std::size_t accessorIdx, - std::vector& vertices) const + std::vector& vertices) const { - - const auto& buffer = getBuffer(accessorIdx); - const auto count = getElemCount(accessorIdx); - const auto byteStride = getByteStride(accessorIdx); - - for (std::size_t i = 0; i < count; i++) { - const auto v = readVec3DF(BufferOffset(buffer, byteStride * i)); - vertices[i].Pos = v; + const auto accessor = Accessor::make(m_gltf_model, accessorIdx); + for (std::size_t i = 0; i < accessor.getCount(); i++) { + vertices[i].Pos = convertHandedness(accessor.get(i)); } } @@ -435,15 +639,11 @@ void CGLTFMeshFileLoader::MeshExtractor::copyPositions( */ void CGLTFMeshFileLoader::MeshExtractor::copyNormals( const std::size_t accessorIdx, - std::vector& vertices) const + std::vector& vertices) const { - const auto& buffer = getBuffer(accessorIdx); - const auto count = getElemCount(accessorIdx); - - for (std::size_t i = 0; i < count; i++) { - const auto n = readVec3DF(BufferOffset(buffer, - 3 * sizeof(float) * i)); - vertices[i].Normal = n; + const auto accessor = Accessor::make(m_gltf_model, accessorIdx); + for (std::size_t i = 0; i < accessor.getCount(); ++i) { + vertices[i].Normal = convertHandedness(accessor.get(i)); } } @@ -453,62 +653,16 @@ void CGLTFMeshFileLoader::MeshExtractor::copyNormals( */ void CGLTFMeshFileLoader::MeshExtractor::copyTCoords( const std::size_t accessorIdx, - std::vector& vertices) const + std::vector& vertices) const { - - const auto& buffer = getBuffer(accessorIdx); - const auto count = getElemCount(accessorIdx); - + const auto accessor = createNormalizedValuesAccessor<2>(m_gltf_model, accessorIdx); + const auto count = std::visit([](auto &&a) { return a.getCount(); }, accessor); for (std::size_t i = 0; i < count; ++i) { - const auto t = readVec2DF(BufferOffset(buffer, - 2 * sizeof(float) * i)); - vertices[i].TCoords = t; + const auto vals = getNormalizedValues(accessor, i); + vertices[i].TCoords = core::vector2df(vals[0], vals[1]); } } -/** - * The number of elements referenced by this accessor, not to be confused with the number of bytes or number of components. - * Documentation: https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#_accessor_count - * Type: Integer - * Required: YES -*/ -std::size_t CGLTFMeshFileLoader::MeshExtractor::getElemCount( - const std::size_t accessorIdx) const -{ - return m_gltf_model.accessors->at(accessorIdx).count; -} - -/** - * The stride, in bytes, between vertex attributes. - * When this is not defined, data is tightly packed. - * When two or more accessors use the same buffer view, this field MUST be defined. - * Documentation: https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#_bufferview_bytestride - * Required: NO -*/ -std::size_t CGLTFMeshFileLoader::MeshExtractor::getByteStride( - const std::size_t accessorIdx) const -{ - const auto& accessor = m_gltf_model.accessors->at(accessorIdx); - // FIXME this does not work with sparse / zero-initialized accessors - const auto& view = m_gltf_model.bufferViews->at(accessor.bufferView.value()); - return view.byteStride.value_or(accessor.elementSize()); -} - -/** - * Walk through the complex chain of the model to extract the required buffer. - * Accessor -> BufferView -> Buffer -*/ -CGLTFMeshFileLoader::BufferOffset CGLTFMeshFileLoader::MeshExtractor::getBuffer( - const std::size_t accessorIdx) const -{ - const auto& accessor = m_gltf_model.accessors->at(accessorIdx); - // FIXME this does not work with sparse / zero-initialized accessors - const auto& view = m_gltf_model.bufferViews->at(accessor.bufferView.value()); - const auto& buffer = m_gltf_model.buffers->at(view.buffer); - - return BufferOffset(buffer.data, view.byteOffset); -} - /** * The index of the accessor that contains the vertex indices. * When this is undefined, the primitive defines non-indexed geometry. diff --git a/irr/src/CGLTFMeshFileLoader.h b/irr/src/CGLTFMeshFileLoader.h index d00a85452..30e9c03bb 100644 --- a/irr/src/CGLTFMeshFileLoader.h +++ b/irr/src/CGLTFMeshFileLoader.h @@ -30,24 +30,71 @@ class CGLTFMeshFileLoader : public IMeshLoader IAnimatedMesh* createMesh(io::IReadFile* file) override; private: - class BufferOffset + template + static T rawget(const void *ptr); + + template + class Accessor { + struct BufferSource + { + const u8 *ptr; + std::size_t byteStride; + }; + using Source = std::variant, std::tuple<>>; + public: - BufferOffset(const std::vector& buf, - const std::size_t offset); + static Accessor sparseIndices( + const tiniergltf::GlTF &model, + const tiniergltf::AccessorSparseIndices &indices, + const std::size_t count); + static Accessor sparseValues( + const tiniergltf::GlTF &model, + const tiniergltf::AccessorSparseValues &values, + const std::size_t count, + const std::size_t defaultByteStride); + static Accessor base( + const tiniergltf::GlTF &model, + std::size_t accessorIdx); + static Accessor make(const tiniergltf::GlTF &model, std::size_t accessorIdx); + static constexpr tiniergltf::Accessor::Type getType(); + static constexpr tiniergltf::Accessor::ComponentType getComponentType(); + std::size_t getCount() const { return count; } + T get(std::size_t i) const; - BufferOffset(const BufferOffset& other, - const std::size_t fromOffset); - - unsigned char at(const std::size_t fromOffset) const; private: - const std::vector& m_buf; - std::size_t m_offset; + Accessor(const u8 *ptr, std::size_t byteStride, std::size_t count) : + source(BufferSource{ptr, byteStride}), count(count) {} + Accessor(std::vector vec, std::size_t count) : + source(vec), count(count) {} + Accessor(std::size_t count) : + source(std::make_tuple()), count(count) {} + // Directly from buffer, sparse, or default-initialized + const Source source; + const std::size_t count; }; + template + using AccessorVariant = std::variant...>; + + template + using ArrayAccessorVariant = std::variant>...>; + + template + using NormalizedValuesAccessor = ArrayAccessorVariant; + + template + static NormalizedValuesAccessor createNormalizedValuesAccessor( + const tiniergltf::GlTF &model, + const std::size_t accessorIdx); + + template + static std::array getNormalizedValues( + const NormalizedValuesAccessor &accessor, + const std::size_t i); + class MeshExtractor { public: - using vertex_t = video::S3DVertex; MeshExtractor(const tiniergltf::GlTF &model, ISkinnedMesh *mesh) noexcept @@ -64,7 +111,7 @@ class CGLTFMeshFileLoader : public IMeshLoader std::optional> getIndices(const std::size_t meshIdx, const std::size_t primitiveIdx) const; - std::optional> getVertices(std::size_t meshIdx, + std::optional> getVertices(std::size_t meshIdx, const std::size_t primitiveIdx) const; std::size_t getMeshCount() const; @@ -77,34 +124,14 @@ class CGLTFMeshFileLoader : public IMeshLoader const tiniergltf::GlTF m_gltf_model; ISkinnedMesh *m_irr_model; - template - static T readPrimitive(const BufferOffset& readFrom); - - static core::vector2df readVec2DF( - const BufferOffset& readFrom); - - /* Read a vec3df from a buffer with transformations applied. - * - * Values are returned in Irrlicht coordinates. - */ - static core::vector3df readVec3DF( - const BufferOffset& readFrom, - const core::vector3df scale); - void copyPositions(const std::size_t accessorIdx, - std::vector& vertices) const; + std::vector& vertices) const; void copyNormals(const std::size_t accessorIdx, - std::vector& vertices) const; + std::vector& vertices) const; void copyTCoords(const std::size_t accessorIdx, - std::vector& vertices) const; - - std::size_t getElemCount(const std::size_t accessorIdx) const; - - std::size_t getByteStride(const std::size_t accessorIdx) const; - - BufferOffset getBuffer(const std::size_t accessorIdx) const; + std::vector& vertices) const; std::optional getIndicesAccessorIdx(const std::size_t meshIdx, const std::size_t primitiveIdx) const;