You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
716 lines
24 KiB
716 lines
24 KiB
/*
|
|
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
* SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
|
|
/**
|
|
\namespace nvh::gltf
|
|
|
|
These utilities are for loading glTF models in a
|
|
canonical scene representation. From this representation
|
|
you would create the appropriate 3D API resources (buffers
|
|
and textures).
|
|
|
|
\code{.cpp}
|
|
// Typical Usage
|
|
// Load the GLTF Scene using TinyGLTF
|
|
|
|
tinygltf::Model gltfModel;
|
|
tinygltf::TinyGLTF gltfContext;
|
|
fileLoaded = gltfContext.LoadASCIIFromFile(&gltfModel, &error, &warn, m_filename);
|
|
|
|
// Fill the data in the gltfScene
|
|
gltfScene.getMaterials(tmodel);
|
|
gltfScene.getDrawableNodes(tmodel, GltfAttributes::Normal | GltfAttributes::Texcoord_0);
|
|
|
|
// Todo in App:
|
|
// create buffers for vertices and indices, from gltfScene.m_position, gltfScene.m_index
|
|
// create textures from images: using tinygltf directly
|
|
// create descriptorSet for material using directly gltfScene.m_materials
|
|
\endcode
|
|
|
|
*/
|
|
|
|
#pragma once
|
|
#include "nvmath/nvmath.h"
|
|
#include "tiny_gltf.h"
|
|
#include <algorithm>
|
|
#include <cassert>
|
|
#include <functional>
|
|
#include <map>
|
|
#include <set>
|
|
#include <string>
|
|
#include <string.h>
|
|
#include <unordered_map>
|
|
#include <vector>
|
|
|
|
#define KHR_LIGHTS_PUNCTUAL_EXTENSION_NAME "KHR_lights_punctual"
|
|
|
|
namespace nvh {
|
|
|
|
// https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_materials_pbrSpecularGlossiness/README.md
|
|
#define KHR_MATERIALS_PBRSPECULARGLOSSINESS_EXTENSION_NAME "KHR_materials_pbrSpecularGlossiness"
|
|
struct KHR_materials_pbrSpecularGlossiness
|
|
{
|
|
nvmath::vec4f diffuseFactor{1.f, 1.f, 1.f, 1.f};
|
|
int diffuseTexture{-1};
|
|
nvmath::vec3f specularFactor{1.f, 1.f, 1.f};
|
|
float glossinessFactor{1.f};
|
|
int specularGlossinessTexture{-1};
|
|
};
|
|
|
|
// https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_materials_specular/README.md
|
|
#define KHR_MATERIALS_SPECULAR_EXTENSION_NAME "KHR_materials_specular"
|
|
struct KHR_materials_specular
|
|
{
|
|
float specularFactor{1.f};
|
|
int specularTexture{-1};
|
|
nvmath::vec3f specularColorFactor{1.f, 1.f, 1.f};
|
|
int specularColorTexture{-1};
|
|
};
|
|
|
|
// https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_texture_transform
|
|
#define KHR_TEXTURE_TRANSFORM_EXTENSION_NAME "KHR_texture_transform"
|
|
struct KHR_texture_transform
|
|
{
|
|
nvmath::vec2f offset{0.f, 0.f};
|
|
float rotation{0.f};
|
|
nvmath::vec2f scale{1.f};
|
|
int texCoord{0};
|
|
nvmath::mat3f uvTransform{1}; // Computed transform of offset, rotation, scale
|
|
};
|
|
|
|
|
|
// https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_materials_clearcoat/README.md
|
|
#define KHR_MATERIALS_CLEARCOAT_EXTENSION_NAME "KHR_materials_clearcoat"
|
|
struct KHR_materials_clearcoat
|
|
{
|
|
float factor{0.f};
|
|
int texture{-1};
|
|
float roughnessFactor{0.f};
|
|
int roughnessTexture{-1};
|
|
int normalTexture{-1};
|
|
};
|
|
|
|
// https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_materials_sheen/README.md
|
|
#define KHR_MATERIALS_SHEEN_EXTENSION_NAME "KHR_materials_sheen"
|
|
struct KHR_materials_sheen
|
|
{
|
|
nvmath::vec3f colorFactor{0.f, 0.f, 0.f};
|
|
int colorTexture{-1};
|
|
float roughnessFactor{0.f};
|
|
int roughnessTexture{-1};
|
|
};
|
|
|
|
// https://github.com/DassaultSystemes-Technology/glTF/tree/KHR_materials_volume/extensions/2.0/Khronos/KHR_materials_transmission
|
|
#define KHR_MATERIALS_TRANSMISSION_EXTENSION_NAME "KHR_materials_transmission"
|
|
struct KHR_materials_transmission
|
|
{
|
|
float factor{0.f};
|
|
int texture{-1};
|
|
};
|
|
|
|
// https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_unlit
|
|
#define KHR_MATERIALS_UNLIT_EXTENSION_NAME "KHR_materials_unlit"
|
|
struct KHR_materials_unlit
|
|
{
|
|
int active{0};
|
|
};
|
|
|
|
// PBR Next : KHR_materials_anisotropy
|
|
#define KHR_MATERIALS_ANISOTROPY_EXTENSION_NAME "KHR_materials_anisotropy"
|
|
struct KHR_materials_anisotropy
|
|
{
|
|
float factor{0.f};
|
|
nvmath::vec3f direction{1.f, 0.f, 0.f};
|
|
int texture{-1};
|
|
};
|
|
|
|
|
|
// https://github.com/DassaultSystemes-Technology/glTF/tree/KHR_materials_ior/extensions/2.0/Khronos/KHR_materials_ior
|
|
#define KHR_MATERIALS_IOR_EXTENSION_NAME "KHR_materials_ior"
|
|
struct KHR_materials_ior
|
|
{
|
|
float ior{1.5f};
|
|
};
|
|
|
|
// https://github.com/DassaultSystemes-Technology/glTF/tree/KHR_materials_volume/extensions/2.0/Khronos/KHR_materials_volume
|
|
#define KHR_MATERIALS_VOLUME_EXTENSION_NAME "KHR_materials_volume"
|
|
struct KHR_materials_volume
|
|
{
|
|
float thicknessFactor{0};
|
|
int thicknessTexture{-1};
|
|
float attenuationDistance{std::numeric_limits<float>::max()};
|
|
nvmath::vec3f attenuationColor{1.f, 1.f, 1.f};
|
|
};
|
|
|
|
|
|
// https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_texture_basisu/README.md
|
|
#define KHR_TEXTURE_BASISU_NAME "KHR_texture_basisu"
|
|
struct KHR_texture_basisu
|
|
{
|
|
int source{-1};
|
|
};
|
|
|
|
// https://github.com/KhronosGroup/glTF/issues/948
|
|
#define KHR_MATERIALS_DISPLACEMENT_NAME "KHR_materials_displacement"
|
|
struct KHR_materials_displacement
|
|
{
|
|
float displacementGeometryFactor{1.0f};
|
|
float displacementGeometryOffset{0.0f};
|
|
int displacementGeometryTexture{-1};
|
|
};
|
|
|
|
// https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#reference-material
|
|
struct GltfMaterial
|
|
{
|
|
int shadingModel{0}; // 0: metallic-roughness, 1: specular-glossiness
|
|
|
|
// pbrMetallicRoughness
|
|
nvmath::vec4f baseColorFactor{1.f, 1.f, 1.f, 1.f};
|
|
int baseColorTexture{-1};
|
|
float metallicFactor{1.f};
|
|
float roughnessFactor{1.f};
|
|
int metallicRoughnessTexture{-1};
|
|
|
|
int emissiveTexture{-1};
|
|
nvmath::vec3f emissiveFactor{0, 0, 0};
|
|
int alphaMode{0};
|
|
float alphaCutoff{0.5f};
|
|
int doubleSided{0};
|
|
|
|
int normalTexture{-1};
|
|
float normalTextureScale{1.f};
|
|
int occlusionTexture{-1};
|
|
float occlusionTextureStrength{1};
|
|
|
|
// Extensions
|
|
KHR_materials_pbrSpecularGlossiness specularGlossiness;
|
|
KHR_materials_specular specular;
|
|
KHR_texture_transform textureTransform;
|
|
KHR_materials_clearcoat clearcoat;
|
|
KHR_materials_sheen sheen;
|
|
KHR_materials_transmission transmission;
|
|
KHR_materials_unlit unlit;
|
|
KHR_materials_anisotropy anisotropy;
|
|
KHR_materials_ior ior;
|
|
KHR_materials_volume volume;
|
|
KHR_materials_displacement displacement;
|
|
|
|
// Tiny Reference
|
|
const tinygltf::Material* tmaterial{nullptr};
|
|
};
|
|
|
|
|
|
struct GltfNode
|
|
{
|
|
nvmath::mat4f worldMatrix{1};
|
|
int primMesh{0};
|
|
const tinygltf::Node* tnode{nullptr};
|
|
};
|
|
|
|
struct GltfPrimMesh
|
|
{
|
|
uint32_t firstIndex{0};
|
|
uint32_t indexCount{0};
|
|
uint32_t vertexOffset{0};
|
|
uint32_t vertexCount{0};
|
|
int materialIndex{0};
|
|
|
|
nvmath::vec3f posMin{0, 0, 0};
|
|
nvmath::vec3f posMax{0, 0, 0};
|
|
std::string name;
|
|
|
|
// Tiny Reference
|
|
const tinygltf::Mesh* tmesh{nullptr};
|
|
const tinygltf::Primitive* tprim{nullptr};
|
|
};
|
|
|
|
struct GltfStats
|
|
{
|
|
uint32_t nbCameras{0};
|
|
uint32_t nbImages{0};
|
|
uint32_t nbTextures{0};
|
|
uint32_t nbMaterials{0};
|
|
uint32_t nbSamplers{0};
|
|
uint32_t nbNodes{0};
|
|
uint32_t nbMeshes{0};
|
|
uint32_t nbLights{0};
|
|
uint32_t imageMem{0};
|
|
uint32_t nbUniqueTriangles{0};
|
|
uint32_t nbTriangles{0};
|
|
};
|
|
|
|
struct GltfCamera
|
|
{
|
|
nvmath::mat4f worldMatrix{1};
|
|
nvmath::vec3f eye{0, 0, 0};
|
|
nvmath::vec3f center{0, 0, 0};
|
|
nvmath::vec3f up{0, 1, 0};
|
|
|
|
tinygltf::Camera cam;
|
|
};
|
|
|
|
// See: https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_lights_punctual/README.md
|
|
struct GltfLight
|
|
{
|
|
nvmath::mat4f worldMatrix{1};
|
|
tinygltf::Light light;
|
|
};
|
|
|
|
|
|
enum class GltfAttributes : uint8_t
|
|
{
|
|
NoAttribs = 0,
|
|
Position = 1,
|
|
Normal = 2,
|
|
Texcoord_0 = 4,
|
|
Texcoord_1 = 8,
|
|
Tangent = 16,
|
|
Color_0 = 32,
|
|
All = 0xFF
|
|
};
|
|
|
|
using GltfAttributes_t = std::underlying_type_t<GltfAttributes>;
|
|
|
|
inline GltfAttributes operator|(GltfAttributes lhs, GltfAttributes rhs)
|
|
{
|
|
return static_cast<GltfAttributes>(static_cast<GltfAttributes_t>(lhs) | static_cast<GltfAttributes_t>(rhs));
|
|
}
|
|
|
|
inline GltfAttributes operator&(GltfAttributes lhs, GltfAttributes rhs)
|
|
{
|
|
return static_cast<GltfAttributes>(static_cast<GltfAttributes_t>(lhs) & static_cast<GltfAttributes_t>(rhs));
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
// Class to convert gltfScene in simple draw-able format
|
|
//
|
|
struct GltfScene
|
|
{
|
|
// Importing all materials in a vector of GltfMaterial structure
|
|
void importMaterials(const tinygltf::Model& tmodel);
|
|
|
|
// Import all Mesh and primitives in a vector of GltfPrimMesh,
|
|
// - Reads all requested GltfAttributes and create them if `forceRequested` contains it.
|
|
// - Create a vector of GltfNode, GltfLight and GltfCamera
|
|
void importDrawableNodes(const tinygltf::Model& tmodel,
|
|
GltfAttributes requestedAttributes,
|
|
GltfAttributes forceRequested = GltfAttributes::All);
|
|
|
|
void exportDrawableNodes(tinygltf::Model& tmodel, GltfAttributes requestedAttributes);
|
|
|
|
// Compute the scene bounding box
|
|
void computeSceneDimensions();
|
|
|
|
// Removes everything
|
|
void destroy();
|
|
|
|
static GltfStats getStatistics(const tinygltf::Model& tinyModel);
|
|
|
|
// Scene data
|
|
std::vector<GltfMaterial> m_materials; // Material for shading
|
|
std::vector<GltfNode> m_nodes; // Drawable nodes, flat hierarchy
|
|
std::vector<GltfPrimMesh> m_primMeshes; // Primitive promoted to meshes
|
|
std::vector<GltfCamera> m_cameras;
|
|
std::vector<GltfLight> m_lights;
|
|
|
|
// Attributes, all same length if valid
|
|
std::vector<nvmath::vec3f> m_positions;
|
|
std::vector<uint32_t> m_indices;
|
|
std::vector<nvmath::vec3f> m_normals;
|
|
std::vector<nvmath::vec4f> m_tangents;
|
|
std::vector<nvmath::vec2f> m_texcoords0;
|
|
std::vector<nvmath::vec2f> m_texcoords1;
|
|
std::vector<nvmath::vec4f> m_colors0;
|
|
|
|
// #TODO - Adding support for Skinning
|
|
//using vec4us = vector4<unsigned short>;
|
|
//std::vector<vec4us> m_joints0;
|
|
//std::vector<nvmath::vec4f> m_weights0;
|
|
|
|
// Size of the scene
|
|
struct Dimensions
|
|
{
|
|
nvmath::vec3f min = nvmath::vec3f(std::numeric_limits<float>::max());
|
|
nvmath::vec3f max = nvmath::vec3f(std::numeric_limits<float>::min());
|
|
nvmath::vec3f size{0.f};
|
|
nvmath::vec3f center{0.f};
|
|
float radius{0};
|
|
} m_dimensions;
|
|
|
|
|
|
private:
|
|
void processNode(const tinygltf::Model& tmodel, int& nodeIdx, const nvmath::mat4f& parentMatrix);
|
|
void processMesh(const tinygltf::Model& tmodel,
|
|
const tinygltf::Primitive& tmesh,
|
|
GltfAttributes requestedAttributes,
|
|
GltfAttributes forceRequested,
|
|
const std::string& name);
|
|
|
|
void createNormals(GltfPrimMesh& resultMesh);
|
|
void createTexcoords(GltfPrimMesh& resultMesh);
|
|
void createTangents(GltfPrimMesh& resultMesh);
|
|
void createColors(GltfPrimMesh& resultMesh);
|
|
|
|
// Temporary data
|
|
std::unordered_map<int, std::vector<uint32_t>> m_meshToPrimMeshes;
|
|
std::vector<uint32_t> primitiveIndices32u;
|
|
std::vector<uint16_t> primitiveIndices16u;
|
|
std::vector<uint8_t> primitiveIndices8u;
|
|
|
|
std::unordered_map<std::string, GltfPrimMesh> m_cachePrimMesh;
|
|
|
|
void computeCamera();
|
|
void checkRequiredExtensions(const tinygltf::Model& tmodel);
|
|
void findUsedMeshes(const tinygltf::Model& tmodel, std::set<uint32_t>& usedMeshes, int nodeIdx);
|
|
};
|
|
|
|
nvmath::mat4f getLocalMatrix(const tinygltf::Node& tnode);
|
|
|
|
// Return a vector of data for a tinygltf::Value
|
|
template <typename T>
|
|
static inline std::vector<T> getVector(const tinygltf::Value& value)
|
|
{
|
|
std::vector<T> result{0};
|
|
if(!value.IsArray())
|
|
return result;
|
|
result.resize(value.ArrayLen());
|
|
for(int i = 0; i < value.ArrayLen(); i++)
|
|
{
|
|
result[i] = static_cast<T>(value.Get(i).IsNumber() ? value.Get(i).Get<double>() : value.Get(i).Get<int>());
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static inline void getFloat(const tinygltf::Value& value, const std::string& name, float& val)
|
|
{
|
|
if(value.Has(name))
|
|
{
|
|
val = static_cast<float>(value.Get(name).Get<double>());
|
|
}
|
|
}
|
|
|
|
static inline void getInt(const tinygltf::Value& value, const std::string& name, int& val)
|
|
{
|
|
if(value.Has(name))
|
|
{
|
|
val = value.Get(name).Get<int>();
|
|
}
|
|
}
|
|
|
|
static inline void getVec2(const tinygltf::Value& value, const std::string& name, nvmath::vec2f& val)
|
|
{
|
|
if(value.Has(name))
|
|
{
|
|
auto s = getVector<float>(value.Get(name));
|
|
val = nvmath::vec2f{s[0], s[1]};
|
|
}
|
|
}
|
|
|
|
static inline void getVec3(const tinygltf::Value& value, const std::string& name, nvmath::vec3f& val)
|
|
{
|
|
if(value.Has(name))
|
|
{
|
|
auto s = getVector<float>(value.Get(name));
|
|
val = nvmath::vec3f{s[0], s[1], s[2]};
|
|
}
|
|
}
|
|
|
|
static inline void getVec4(const tinygltf::Value& value, const std::string& name, nvmath::vec4f& val)
|
|
{
|
|
if(value.Has(name))
|
|
{
|
|
auto s = getVector<float>(value.Get(name));
|
|
val = nvmath::vec4f{s[0], s[1], s[2], s[3]};
|
|
}
|
|
}
|
|
|
|
static inline void getTexId(const tinygltf::Value& value, const std::string& name, int& val)
|
|
{
|
|
if(value.Has(name))
|
|
{
|
|
val = value.Get(name).Get("index").Get<int>();
|
|
}
|
|
}
|
|
|
|
// Calls a function (such as a lambda function) for each (index, value) pair in
|
|
// a sparse accessor. It's only potentially called for indices from
|
|
// accessorFirstElement through accessorFirstElement + numElementsToProcess - 1.
|
|
template <class T>
|
|
void forEachSparseValue(const tinygltf::Model& tmodel,
|
|
const tinygltf::Accessor& accessor,
|
|
size_t accessorFirstElement,
|
|
size_t numElementsToProcess,
|
|
std::function<void(size_t index, const T* value)> fn)
|
|
{
|
|
if(!accessor.sparse.isSparse)
|
|
{
|
|
return; // Nothing to do
|
|
}
|
|
|
|
const auto& idxs = accessor.sparse.indices;
|
|
if(!(idxs.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE //
|
|
|| idxs.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT //
|
|
|| idxs.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT))
|
|
{
|
|
assert(!"Unsupported sparse accessor index type.");
|
|
return;
|
|
}
|
|
|
|
const tinygltf::BufferView& idxBufferView = tmodel.bufferViews[idxs.bufferView];
|
|
const unsigned char* idxBuffer = &tmodel.buffers[idxBufferView.buffer].data[idxBufferView.byteOffset];
|
|
const size_t idxBufferByteStride =
|
|
idxBufferView.byteStride ? idxBufferView.byteStride : tinygltf::GetComponentSizeInBytes(idxs.componentType);
|
|
if(idxBufferByteStride == size_t(-1))
|
|
return; // Invalid
|
|
|
|
const auto& vals = accessor.sparse.values;
|
|
const tinygltf::BufferView& valBufferView = tmodel.bufferViews[vals.bufferView];
|
|
const unsigned char* valBuffer = &tmodel.buffers[valBufferView.buffer].data[valBufferView.byteOffset];
|
|
const size_t valBufferByteStride = accessor.ByteStride(valBufferView);
|
|
if(valBufferByteStride == size_t(-1))
|
|
return; // Invalid
|
|
|
|
// Note that this could be faster for lots of small copies, since we could
|
|
// binary search for the first sparse accessor index to use (since the
|
|
// glTF specification requires the indices be sorted)!
|
|
for(size_t pairIdx = 0; pairIdx < accessor.sparse.count; pairIdx++)
|
|
{
|
|
// Read the index from the index buffer, converting its type
|
|
size_t index = 0;
|
|
const unsigned char* pIdx = idxBuffer + idxBufferByteStride * pairIdx;
|
|
switch(idxs.componentType)
|
|
{
|
|
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE:
|
|
index = *reinterpret_cast<const uint8_t*>(pIdx);
|
|
break;
|
|
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT:
|
|
index = *reinterpret_cast<const uint16_t*>(pIdx);
|
|
break;
|
|
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT:
|
|
index = *reinterpret_cast<const uint32_t*>(pIdx);
|
|
break;
|
|
}
|
|
|
|
// If it's not in range, skip it
|
|
if(index < accessorFirstElement || (index - accessorFirstElement) >= numElementsToProcess)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
fn(index, reinterpret_cast<const T*>(valBuffer + valBufferByteStride * pairIdx));
|
|
}
|
|
}
|
|
|
|
// Copies accessor elements accessorFirstElement through
|
|
// accessorFirstElement + numElementsToCopy - 1 to outData elements
|
|
// outFirstElement through outFirstElement + numElementsToCopy - 1.
|
|
// This handles sparse accessors correctly! It's intended as a replacement for
|
|
// what would be memcpy(..., &buffer.data[...], ...) calls.
|
|
//
|
|
// However, it performs no conversion: it assumes (but does not check) that
|
|
// accessor's elements are of type T. For instance, T should be a struct of two
|
|
// floats for a VEC2 float accessor.
|
|
//
|
|
// This is range-checked, so elements that would be out-of-bounds are not
|
|
// copied. We assume size_t overflow does not occur.
|
|
// Note that outDataSizeInT is the number of elements in the outDataBuffer,
|
|
// while numElementsToCopy is the number of elements to copy, not the number
|
|
// of elements in accessor.
|
|
template <class T>
|
|
void copyAccessorData(T* outData,
|
|
size_t outDataSizeInElements,
|
|
size_t outFirstElement,
|
|
const tinygltf::Model& tmodel,
|
|
const tinygltf::Accessor& accessor,
|
|
size_t accessorFirstElement,
|
|
size_t numElementsToCopy)
|
|
{
|
|
if(outFirstElement >= outDataSizeInElements)
|
|
{
|
|
assert(!"Invalid outFirstElement!");
|
|
return;
|
|
}
|
|
|
|
if(accessorFirstElement >= accessor.count)
|
|
{
|
|
assert(!"Invalid accessorFirstElement!");
|
|
return;
|
|
}
|
|
|
|
const tinygltf::BufferView& bufferView = tmodel.bufferViews[accessor.bufferView];
|
|
const unsigned char* buffer = &tmodel.buffers[bufferView.buffer].data[accessor.byteOffset + bufferView.byteOffset];
|
|
|
|
const size_t maxSafeCopySize = std::min(accessor.count - accessorFirstElement, outDataSizeInElements - outFirstElement);
|
|
numElementsToCopy = std::min(numElementsToCopy, maxSafeCopySize);
|
|
|
|
if(bufferView.byteStride == 0)
|
|
{
|
|
memcpy(outData + outFirstElement, reinterpret_cast<const T*>(buffer) + accessorFirstElement, numElementsToCopy * sizeof(T));
|
|
}
|
|
else
|
|
{
|
|
// Must copy one-by-one
|
|
for(size_t i = 0; i < numElementsToCopy; i++)
|
|
{
|
|
outData[outFirstElement + i] = *reinterpret_cast<const T*>(buffer + bufferView.byteStride * i);
|
|
}
|
|
}
|
|
|
|
// Handle sparse accessors by overwriting already copied elements.
|
|
forEachSparseValue<T>(tmodel, accessor, accessorFirstElement, numElementsToCopy,
|
|
[&outData](size_t index, const T* value) { outData[index] = *value; });
|
|
}
|
|
|
|
// Same as copyAccessorData(T*, ...), but taking a vector.
|
|
template <class T>
|
|
void copyAccessorData(std::vector<T>& outData,
|
|
size_t outFirstElement,
|
|
const tinygltf::Model& tmodel,
|
|
const tinygltf::Accessor& accessor,
|
|
size_t accessorFirstElement,
|
|
size_t numElementsToCopy)
|
|
{
|
|
copyAccessorData<T>(outData.data(), outData.size(), outFirstElement, tmodel, accessor, accessorFirstElement, numElementsToCopy);
|
|
}
|
|
|
|
// Appending to \p attribVec, all the values of \p accessor
|
|
// Return false if the accessor is invalid.
|
|
// T must be nvmath::vec2f, nvmath::vec3f, or nvmath::vec4f.
|
|
template <typename T>
|
|
static bool getAccessorData(const tinygltf::Model& tmodel, const tinygltf::Accessor& accessor, std::vector<T>& attribVec)
|
|
{
|
|
// Retrieving the data of the accessor
|
|
const auto nbElems = accessor.count;
|
|
|
|
const size_t oldNumElements = attribVec.size();
|
|
attribVec.resize(oldNumElements + nbElems);
|
|
|
|
// Copying the attributes
|
|
if(accessor.componentType == TINYGLTF_COMPONENT_TYPE_FLOAT)
|
|
{
|
|
copyAccessorData<T>(attribVec, oldNumElements, tmodel, accessor, 0, accessor.count);
|
|
}
|
|
else
|
|
{
|
|
// The component is smaller than float and need to be converted
|
|
const auto& bufView = tmodel.bufferViews[accessor.bufferView];
|
|
const auto& buffer = tmodel.buffers[bufView.buffer];
|
|
const unsigned char* bufferByte = &buffer.data[accessor.byteOffset + bufView.byteOffset];
|
|
|
|
// 2, 3, 4 for VEC2, VEC3, VEC4
|
|
const int nbComponents = tinygltf::GetNumComponentsInType(accessor.type);
|
|
if(nbComponents == -1)
|
|
return false; // Invalid
|
|
|
|
// Stride per element
|
|
const size_t byteStride = accessor.ByteStride(bufView);
|
|
if(byteStride == size_t(-1))
|
|
return false; // Invalid
|
|
|
|
if(!(accessor.componentType == TINYGLTF_COMPONENT_TYPE_BYTE || accessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE
|
|
|| accessor.componentType == TINYGLTF_COMPONENT_TYPE_SHORT || accessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT))
|
|
{
|
|
assert(!"Unhandled tinygltf component type!");
|
|
return false;
|
|
}
|
|
|
|
const auto& copyElementFn = [&](size_t elementIdx, const unsigned char* pElement) {
|
|
T vecValue;
|
|
|
|
for(int c = 0; c < nbComponents; c++)
|
|
{
|
|
switch(accessor.componentType)
|
|
{
|
|
case TINYGLTF_COMPONENT_TYPE_BYTE:
|
|
vecValue[c] = float(*(reinterpret_cast<const char*>(pElement) + c));
|
|
if(accessor.normalized)
|
|
{
|
|
vecValue[c] = std::max(vecValue[c] / 127.f, -1.f);
|
|
}
|
|
break;
|
|
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE:
|
|
vecValue[c] = float(*(reinterpret_cast<const unsigned char*>(pElement) + c));
|
|
if(accessor.normalized)
|
|
{
|
|
vecValue[c] = vecValue[c] / 255.f;
|
|
}
|
|
break;
|
|
case TINYGLTF_COMPONENT_TYPE_SHORT:
|
|
vecValue[c] = float(*(reinterpret_cast<const short*>(pElement) + c));
|
|
if(accessor.normalized)
|
|
{
|
|
vecValue[c] = std::max(vecValue[c] / 32767.f, -1.f);
|
|
}
|
|
break;
|
|
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT:
|
|
vecValue[c] = float(*(reinterpret_cast<const unsigned short*>(pElement) + c));
|
|
if(accessor.normalized)
|
|
{
|
|
vecValue[c] = vecValue[c] / 65535.f;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
attribVec[oldNumElements + elementIdx] = vecValue;
|
|
};
|
|
|
|
for(size_t i = 0; i < nbElems; i++)
|
|
{
|
|
copyElementFn(i, bufferByte + byteStride * i);
|
|
}
|
|
|
|
forEachSparseValue<unsigned char>(tmodel, accessor, 0, nbElems, copyElementFn);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// Appending to \p attribVec, all the values of \p attribName
|
|
// Return false if the attribute is missing or invalid.
|
|
// T must be nvmath::vec2f, nvmath::vec3f, or nvmath::vec4f.
|
|
template <typename T>
|
|
static bool getAttribute(const tinygltf::Model& tmodel, const tinygltf::Primitive& primitive, std::vector<T>& attribVec, const std::string& attribName)
|
|
{
|
|
const auto& it = primitive.attributes.find(attribName);
|
|
if(it == primitive.attributes.end())
|
|
return false;
|
|
const auto& accessor = tmodel.accessors[it->second];
|
|
return getAccessorData(tmodel, accessor, attribVec);
|
|
}
|
|
|
|
inline bool hasExtension(const tinygltf::ExtensionMap& extensions, const std::string& name)
|
|
{
|
|
return extensions.find(name) != extensions.end();
|
|
}
|
|
|
|
// This is appending the incoming data to the binary buffer (just one)
|
|
// and return the amount in byte of data that was added.
|
|
template <class T>
|
|
uint32_t appendData(tinygltf::Buffer& buffer, const T& inData)
|
|
{
|
|
auto* pData = reinterpret_cast<const char*>(inData.data());
|
|
uint32_t len = static_cast<uint32_t>(sizeof(inData[0]) * inData.size());
|
|
buffer.data.insert(buffer.data.end(), pData, pData + len);
|
|
return len;
|
|
}
|
|
|
|
|
|
} // namespace nvh
|
|
|