Add ModelLoader (not tested) add TheChef for cooking
This commit is contained in:
4
.gitmodules
vendored
4
.gitmodules
vendored
@@ -29,3 +29,7 @@
|
|||||||
[submodule "destrum/third_party/spdlog"]
|
[submodule "destrum/third_party/spdlog"]
|
||||||
path = destrum/third_party/spdlog
|
path = destrum/third_party/spdlog
|
||||||
url = https://github.com/gabime/spdlog.git
|
url = https://github.com/gabime/spdlog.git
|
||||||
|
[submodule "destrum/third_party/tinygltf"]
|
||||||
|
path = destrum/third_party/tinygltf
|
||||||
|
url = https://github.com/syoyo/tinygltf.git
|
||||||
|
|
||||||
|
|||||||
@@ -10,3 +10,4 @@ endif()
|
|||||||
|
|
||||||
add_subdirectory(destrum)
|
add_subdirectory(destrum)
|
||||||
add_subdirectory(lightkeeper)
|
add_subdirectory(lightkeeper)
|
||||||
|
add_subdirectory(TheChef)
|
||||||
@@ -96,20 +96,19 @@ target_compile_definitions(destrum
|
|||||||
GLM_ENABLE_EXPERIMENTAL
|
GLM_ENABLE_EXPERIMENTAL
|
||||||
)
|
)
|
||||||
|
|
||||||
set(DESTRUM_SHADER_SRC "${CMAKE_CURRENT_LIST_DIR}/assets_src/shaders")
|
set(ASSETS_SRC_DIR "${CMAKE_CURRENT_LIST_DIR}/assets_src")
|
||||||
set(DESTRUM_SHADER_OUT "${CMAKE_CURRENT_LIST_DIR}/assets_runtime/shaders")
|
set(ASSETS_RUNTIME_DIR "${CMAKE_CURRENT_LIST_DIR}/assets_runtime")
|
||||||
|
|
||||||
include(../cmake/compile_shaders.cmake)
|
add_custom_target(cook_assets_clean
|
||||||
|
COMMAND TheChef
|
||||||
|
--input "${ASSETS_SRC_DIR}"
|
||||||
|
--output "${ASSETS_RUNTIME_DIR}"
|
||||||
|
--clean
|
||||||
|
)
|
||||||
|
|
||||||
compile_glsl_to_spv(destrum "${DESTRUM_SHADER_SRC}" "${DESTRUM_SHADER_OUT}" DESTRUM_SPV)
|
add_custom_target(cook_assets ALL
|
||||||
add_dependencies(destrum destrum_shaders)
|
COMMAND TheChef
|
||||||
#
|
--input "${ASSETS_SRC_DIR}"
|
||||||
#include(../cmake/compile_slang_shaders.cmake)
|
--output "${ASSETS_RUNTIME_DIR}"
|
||||||
#
|
DEPENDS TheChef
|
||||||
#compile_slang_shaders(
|
)
|
||||||
# TARGET compile_shaders
|
|
||||||
# SRC_DIR "${CMAKE_SOURCE_DIR}/destrum/assets_src/shaders"
|
|
||||||
# OUT_DIR "${CMAKE_SOURCE_DIR}/destrum/assets_runtime/shaders"
|
|
||||||
# EXTRA_ARGS -O3
|
|
||||||
# ALL
|
|
||||||
#)
|
|
||||||
|
|||||||
423
destrum/include/destrum/Util/ModelLoader.h
Normal file
423
destrum/include/destrum/Util/ModelLoader.h
Normal file
@@ -0,0 +1,423 @@
|
|||||||
|
#ifndef MODELLOADER_H
|
||||||
|
#define MODELLOADER_H
|
||||||
|
|
||||||
|
namespace ModelLoader {
|
||||||
|
// CPUMesh loader with tinygltf
|
||||||
|
// - Loads first scene (or default scene), iterates nodes, extracts mesh primitives.
|
||||||
|
// - Handles POSITION/NORMAL/TANGENT/TEXCOORD_0 and indices.
|
||||||
|
// - Computes minPos/maxPos.
|
||||||
|
// - Optionally merges primitives into a single CPUMesh per glTF mesh.
|
||||||
|
|
||||||
|
#include <tiny_gltf.h>
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstring>
|
||||||
|
#include <limits>
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
struct CPUMesh {
|
||||||
|
std::vector<std::uint32_t> indices;
|
||||||
|
|
||||||
|
struct Vertex {
|
||||||
|
glm::vec3 position;
|
||||||
|
float uv_x{};
|
||||||
|
glm::vec3 normal;
|
||||||
|
float uv_y{};
|
||||||
|
glm::vec4 tangent;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<Vertex> vertices;
|
||||||
|
|
||||||
|
std::string name;
|
||||||
|
|
||||||
|
glm::vec3 minPos;
|
||||||
|
glm::vec3 maxPos;
|
||||||
|
};
|
||||||
|
|
||||||
|
// -------------------- helpers --------------------
|
||||||
|
|
||||||
|
static size_t ComponentSizeInBytes(int componentType) {
|
||||||
|
switch (componentType) {
|
||||||
|
case TINYGLTF_COMPONENT_TYPE_BYTE: return 1;
|
||||||
|
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE: return 1;
|
||||||
|
case TINYGLTF_COMPONENT_TYPE_SHORT: return 2;
|
||||||
|
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT: return 2;
|
||||||
|
case TINYGLTF_COMPONENT_TYPE_INT: return 4;
|
||||||
|
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT: return 4;
|
||||||
|
case TINYGLTF_COMPONENT_TYPE_FLOAT: return 4;
|
||||||
|
case TINYGLTF_COMPONENT_TYPE_DOUBLE: return 8;
|
||||||
|
default: throw std::runtime_error("Unknown glTF component type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int TypeNumComponents(int type) {
|
||||||
|
switch (type) {
|
||||||
|
case TINYGLTF_TYPE_SCALAR: return 1;
|
||||||
|
case TINYGLTF_TYPE_VEC2: return 2;
|
||||||
|
case TINYGLTF_TYPE_VEC3: return 3;
|
||||||
|
case TINYGLTF_TYPE_VEC4: return 4;
|
||||||
|
case TINYGLTF_TYPE_MAT2: return 4;
|
||||||
|
case TINYGLTF_TYPE_MAT3: return 9;
|
||||||
|
case TINYGLTF_TYPE_MAT4: return 16;
|
||||||
|
default: throw std::runtime_error("Unknown glTF type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns pointer to the first element, plus stride in bytes.
|
||||||
|
static std::pair<const std::uint8_t*, size_t>
|
||||||
|
GetAccessorDataPtrAndStride(const tinygltf::Model& model, const tinygltf::Accessor& accessor) {
|
||||||
|
if (accessor.bufferView < 0) {
|
||||||
|
throw std::runtime_error("Accessor has no bufferView");
|
||||||
|
}
|
||||||
|
const tinygltf::BufferView& bv = model.bufferViews.at(accessor.bufferView);
|
||||||
|
const tinygltf::Buffer& buf = model.buffers.at(bv.buffer);
|
||||||
|
|
||||||
|
const size_t componentSize = ComponentSizeInBytes(accessor.componentType);
|
||||||
|
const int numComps = TypeNumComponents(accessor.type);
|
||||||
|
const size_t packedStride = componentSize * size_t(numComps);
|
||||||
|
|
||||||
|
size_t stride = bv.byteStride ? size_t(bv.byteStride) : packedStride;
|
||||||
|
|
||||||
|
const size_t start =
|
||||||
|
size_t(bv.byteOffset) + size_t(accessor.byteOffset);
|
||||||
|
|
||||||
|
if (start + accessor.count * stride > buf.data.size() + 0ull) {
|
||||||
|
// Note: This check is conservative; stride can be larger than packed.
|
||||||
|
// Still useful to catch obvious corrupt files.
|
||||||
|
// If you want strict validation, check per-element.
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::uint8_t* ptr = buf.data.data() + start;
|
||||||
|
return {ptr, stride};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static T ReadAs(const std::uint8_t* p) {
|
||||||
|
T v{};
|
||||||
|
std::memcpy(&v, p, sizeof(T));
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reads VEC3 float accessor element i into glm::vec3.
|
||||||
|
// Allows input component type float only for simplicity (common in glTF).
|
||||||
|
static glm::vec3 ReadVec3Float(const std::uint8_t* base, size_t stride, size_t i) {
|
||||||
|
const std::uint8_t* p = base + i * stride;
|
||||||
|
const float x = ReadAs<float>(p + 0);
|
||||||
|
const float y = ReadAs<float>(p + 4);
|
||||||
|
const float z = ReadAs<float>(p + 8);
|
||||||
|
return glm::vec3{x, y, z};
|
||||||
|
}
|
||||||
|
|
||||||
|
static glm::vec2 ReadVec2Float(const std::uint8_t* base, size_t stride, size_t i) {
|
||||||
|
const std::uint8_t* p = base + i * stride;
|
||||||
|
const float x = ReadAs<float>(p + 0);
|
||||||
|
const float y = ReadAs<float>(p + 4);
|
||||||
|
return glm::vec2{x, y};
|
||||||
|
}
|
||||||
|
|
||||||
|
static glm::vec4 ReadVec4Float(const std::uint8_t* base, size_t stride, size_t i) {
|
||||||
|
const std::uint8_t* p = base + i * stride;
|
||||||
|
const float x = ReadAs<float>(p + 0);
|
||||||
|
const float y = ReadAs<float>(p + 4);
|
||||||
|
const float z = ReadAs<float>(p + 8);
|
||||||
|
const float w = ReadAs<float>(p + 12);
|
||||||
|
return glm::vec4{x, y, z, w};
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::uint32_t ReadIndexAsU32(const tinygltf::Model& model, const tinygltf::Accessor& accessor, size_t i) {
|
||||||
|
auto [base, stride] = GetAccessorDataPtrAndStride(model, accessor);
|
||||||
|
const std::uint8_t* p = base + i * stride;
|
||||||
|
|
||||||
|
switch (accessor.componentType) {
|
||||||
|
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE:
|
||||||
|
return static_cast<std::uint32_t>(ReadAs<std::uint8_t>(p));
|
||||||
|
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT:
|
||||||
|
return static_cast<std::uint32_t>(ReadAs<std::uint16_t>(p));
|
||||||
|
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT:
|
||||||
|
return static_cast<std::uint32_t>(ReadAs<std::uint32_t>(p));
|
||||||
|
default:
|
||||||
|
throw std::runtime_error("Unsupported index componentType (expected u8/u16/u32)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void UpdateBounds(glm::vec3& mn, glm::vec3& mx, const glm::vec3& p) {
|
||||||
|
mn.x = std::min(mn.x, p.x);
|
||||||
|
mn.y = std::min(mn.y, p.y);
|
||||||
|
mn.z = std::min(mn.z, p.z);
|
||||||
|
mx.x = std::max(mx.x, p.x);
|
||||||
|
mx.y = std::max(mx.y, p.y);
|
||||||
|
mx.z = std::max(mx.z, p.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------- primitive extraction --------------------
|
||||||
|
|
||||||
|
static CPUMesh LoadPrimitiveIntoCPUMesh(const tinygltf::Model& model,
|
||||||
|
const tinygltf::Primitive& prim,
|
||||||
|
const std::string& nameForMesh) {
|
||||||
|
CPUMesh out{};
|
||||||
|
out.name = nameForMesh;
|
||||||
|
|
||||||
|
// POSITION is required for our vertex buffer
|
||||||
|
auto itPos = prim.attributes.find("POSITION");
|
||||||
|
if (itPos == prim.attributes.end()) {
|
||||||
|
throw std::runtime_error("Primitive has no POSITION attribute");
|
||||||
|
}
|
||||||
|
|
||||||
|
const tinygltf::Accessor& accPos = model.accessors.at(itPos->second);
|
||||||
|
if (accPos.componentType != TINYGLTF_COMPONENT_TYPE_FLOAT || accPos.type != TINYGLTF_TYPE_VEC3) {
|
||||||
|
throw std::runtime_error("POSITION must be VEC3 float for this loader");
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t vertexCount = size_t(accPos.count);
|
||||||
|
|
||||||
|
// Optional attributes
|
||||||
|
const tinygltf::Accessor* accNormal = nullptr;
|
||||||
|
const tinygltf::Accessor* accTangent = nullptr;
|
||||||
|
const tinygltf::Accessor* accUV0 = nullptr;
|
||||||
|
|
||||||
|
if (auto it = prim.attributes.find("NORMAL"); it != prim.attributes.end()) {
|
||||||
|
accNormal = &model.accessors.at(it->second);
|
||||||
|
if (accNormal->componentType != TINYGLTF_COMPONENT_TYPE_FLOAT || accNormal->type != TINYGLTF_TYPE_VEC3)
|
||||||
|
accNormal = nullptr; // ignore unsupported
|
||||||
|
}
|
||||||
|
if (auto it = prim.attributes.find("TANGENT"); it != prim.attributes.end()) {
|
||||||
|
accTangent = &model.accessors.at(it->second);
|
||||||
|
if (accTangent->componentType != TINYGLTF_COMPONENT_TYPE_FLOAT || accTangent->type != TINYGLTF_TYPE_VEC4)
|
||||||
|
accTangent = nullptr;
|
||||||
|
}
|
||||||
|
if (auto it = prim.attributes.find("TEXCOORD_0"); it != prim.attributes.end()) {
|
||||||
|
accUV0 = &model.accessors.at(it->second);
|
||||||
|
if (accUV0->componentType != TINYGLTF_COMPONENT_TYPE_FLOAT || accUV0->type != TINYGLTF_TYPE_VEC2)
|
||||||
|
accUV0 = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare pointers/strides
|
||||||
|
auto [posBase, posStride] = GetAccessorDataPtrAndStride(model, accPos);
|
||||||
|
|
||||||
|
const std::uint8_t* nrmBase = nullptr;
|
||||||
|
size_t nrmStride = 0;
|
||||||
|
const std::uint8_t* tanBase = nullptr;
|
||||||
|
size_t tanStride = 0;
|
||||||
|
const std::uint8_t* uvBase = nullptr;
|
||||||
|
size_t uvStride = 0;
|
||||||
|
|
||||||
|
if (accNormal) {
|
||||||
|
auto p = GetAccessorDataPtrAndStride(model, *accNormal);
|
||||||
|
nrmBase = p.first;
|
||||||
|
nrmStride = p.second;
|
||||||
|
if (size_t(accNormal->count) != vertexCount) accNormal = nullptr; // mismatch -> ignore
|
||||||
|
}
|
||||||
|
if (accTangent) {
|
||||||
|
auto p = GetAccessorDataPtrAndStride(model, *accTangent);
|
||||||
|
tanBase = p.first;
|
||||||
|
tanStride = p.second;
|
||||||
|
if (size_t(accTangent->count) != vertexCount) accTangent = nullptr;
|
||||||
|
}
|
||||||
|
if (accUV0) {
|
||||||
|
auto p = GetAccessorDataPtrAndStride(model, *accUV0);
|
||||||
|
uvBase = p.first;
|
||||||
|
uvStride = p.second;
|
||||||
|
if (size_t(accUV0->count) != vertexCount) accUV0 = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate vertices
|
||||||
|
out.vertices.resize(vertexCount);
|
||||||
|
|
||||||
|
// Bounds init
|
||||||
|
glm::vec3 mn{std::numeric_limits<float>::infinity()};
|
||||||
|
glm::vec3 mx{-std::numeric_limits<float>::infinity()};
|
||||||
|
|
||||||
|
// Fill vertices
|
||||||
|
for (size_t i = 0; i < vertexCount; ++i) {
|
||||||
|
CPUMesh::Vertex v{};
|
||||||
|
|
||||||
|
v.position = ReadVec3Float(posBase, posStride, i);
|
||||||
|
UpdateBounds(mn, mx, v.position);
|
||||||
|
|
||||||
|
if (accNormal) {
|
||||||
|
v.normal = ReadVec3Float(nrmBase, nrmStride, i);
|
||||||
|
} else {
|
||||||
|
v.normal = glm::vec3(0.0f, 1.0f, 0.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (accUV0) {
|
||||||
|
glm::vec2 uv = ReadVec2Float(uvBase, uvStride, i);
|
||||||
|
v.uv_x = uv.x;
|
||||||
|
v.uv_y = uv.y;
|
||||||
|
} else {
|
||||||
|
v.uv_x = 0.0f;
|
||||||
|
v.uv_y = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (accTangent) {
|
||||||
|
v.tangent = ReadVec4Float(tanBase, tanStride, i);
|
||||||
|
} else {
|
||||||
|
v.tangent = glm::vec4(1.0f, 0.0f, 0.0f, 1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
out.vertices[i] = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
out.minPos = (vertexCount > 0) ? mn : glm::vec3(0.0f);
|
||||||
|
out.maxPos = (vertexCount > 0) ? mx : glm::vec3(0.0f);
|
||||||
|
|
||||||
|
// Indices (optional; if absent, build linear indices)
|
||||||
|
if (prim.indices >= 0) {
|
||||||
|
const tinygltf::Accessor& accIdx = model.accessors.at(prim.indices);
|
||||||
|
if (accIdx.type != TINYGLTF_TYPE_SCALAR) {
|
||||||
|
throw std::runtime_error("Indices accessor must be SCALAR");
|
||||||
|
}
|
||||||
|
|
||||||
|
out.indices.resize(size_t(accIdx.count));
|
||||||
|
for (size_t i = 0; i < size_t(accIdx.count); ++i) {
|
||||||
|
out.indices[i] = ReadIndexAsU32(model, accIdx, i);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Non-indexed primitive: make it indexed
|
||||||
|
out.indices.resize(vertexCount);
|
||||||
|
for (size_t i = 0; i < vertexCount; ++i) out.indices[i] = static_cast<std::uint32_t>(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge "src" into "dst" (offset indices)
|
||||||
|
static void AppendMesh(CPUMesh& dst, const CPUMesh& src) {
|
||||||
|
const std::uint32_t baseVertex = static_cast<std::uint32_t>(dst.vertices.size());
|
||||||
|
dst.vertices.insert(dst.vertices.end(), src.vertices.begin(), src.vertices.end());
|
||||||
|
|
||||||
|
dst.indices.reserve(dst.indices.size() + src.indices.size());
|
||||||
|
for (std::uint32_t idx: src.indices) {
|
||||||
|
dst.indices.push_back(baseVertex + idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dst.vertices.size() == src.vertices.size()) {
|
||||||
|
dst.minPos = src.minPos;
|
||||||
|
dst.maxPos = src.maxPos;
|
||||||
|
} else {
|
||||||
|
// update bounds using src bounds (cheap)
|
||||||
|
dst.minPos = glm::vec3(
|
||||||
|
std::min(dst.minPos.x, src.minPos.x),
|
||||||
|
std::min(dst.minPos.y, src.minPos.y),
|
||||||
|
std::min(dst.minPos.z, src.minPos.z)
|
||||||
|
);
|
||||||
|
dst.maxPos = glm::vec3(
|
||||||
|
std::max(dst.maxPos.x, src.maxPos.x),
|
||||||
|
std::max(dst.maxPos.y, src.maxPos.y),
|
||||||
|
std::max(dst.maxPos.z, src.maxPos.z)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------- public API --------------------
|
||||||
|
|
||||||
|
// Option A: return one CPUMesh per *primitive* encountered in the scene
|
||||||
|
static std::vector<CPUMesh> LoadGLTF_CPUMeshes_PerPrimitive(const std::string& path) {
|
||||||
|
tinygltf::TinyGLTF loader;
|
||||||
|
tinygltf::Model model;
|
||||||
|
std::string err, warn;
|
||||||
|
|
||||||
|
bool ok = false;
|
||||||
|
const bool isGLB = (path.size() >= 4 && path.substr(path.size() - 4) == ".glb");
|
||||||
|
if (isGLB) ok = loader.LoadBinaryFromFile(&model, &err, &warn, path);
|
||||||
|
else ok = loader.LoadASCIIFromFile(&model, &err, &warn, path);
|
||||||
|
|
||||||
|
if (!warn.empty()) std::cerr << "tinygltf warn: " << warn << "\n";
|
||||||
|
if (!ok) throw std::runtime_error("Failed to load glTF: " + err);
|
||||||
|
|
||||||
|
int sceneIndex = model.defaultScene >= 0 ? model.defaultScene : 0;
|
||||||
|
if (sceneIndex < 0 || sceneIndex >= int(model.scenes.size())) {
|
||||||
|
throw std::runtime_error("glTF has no valid scene");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<CPUMesh> result;
|
||||||
|
const tinygltf::Scene& scene = model.scenes.at(sceneIndex);
|
||||||
|
|
||||||
|
// Traverse nodes and pull mesh primitives (ignoring node transforms in this basic example)
|
||||||
|
std::vector<int> stack(scene.nodes.begin(), scene.nodes.end());
|
||||||
|
while (!stack.empty()) {
|
||||||
|
int nodeIdx = stack.back();
|
||||||
|
stack.pop_back();
|
||||||
|
const tinygltf::Node& node = model.nodes.at(nodeIdx);
|
||||||
|
|
||||||
|
for (int child: node.children) stack.push_back(child);
|
||||||
|
|
||||||
|
if (node.mesh < 0) continue;
|
||||||
|
const tinygltf::Mesh& mesh = model.meshes.at(node.mesh);
|
||||||
|
|
||||||
|
for (size_t p = 0; p < mesh.primitives.size(); ++p) {
|
||||||
|
const tinygltf::Primitive& prim = mesh.primitives[p];
|
||||||
|
CPUMesh cpu = LoadPrimitiveIntoCPUMesh(model, prim, mesh.name.empty() ? ("mesh_" + std::to_string(node.mesh)) : mesh.name);
|
||||||
|
// Optionally make name unique per primitive
|
||||||
|
cpu.name += "_prim" + std::to_string(p);
|
||||||
|
result.push_back(std::move(cpu));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Option B: return one CPUMesh per *glTF mesh*, merging all primitives of that mesh into one CPUMesh
|
||||||
|
static std::vector<CPUMesh> LoadGLTF_CPUMeshes_MergedPerMesh(const std::string& path) {
|
||||||
|
tinygltf::TinyGLTF loader;
|
||||||
|
tinygltf::Model model;
|
||||||
|
std::string err, warn;
|
||||||
|
|
||||||
|
bool ok = false;
|
||||||
|
const bool isGLB = (path.size() >= 4 && path.substr(path.size() - 4) == ".glb");
|
||||||
|
if (isGLB) ok = loader.LoadBinaryFromFile(&model, &err, &warn, path);
|
||||||
|
else ok = loader.LoadASCIIFromFile(&model, &err, &warn, path);
|
||||||
|
|
||||||
|
if (!warn.empty()) std::cerr << "tinygltf warn: " << warn << "\n";
|
||||||
|
if (!ok) throw std::runtime_error("Failed to load glTF: " + err);
|
||||||
|
|
||||||
|
int sceneIndex = model.defaultScene >= 0 ? model.defaultScene : 0;
|
||||||
|
if (sceneIndex < 0 || sceneIndex >= int(model.scenes.size())) {
|
||||||
|
throw std::runtime_error("glTF has no valid scene");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<CPUMesh> result;
|
||||||
|
|
||||||
|
const tinygltf::Scene& scene = model.scenes.at(sceneIndex);
|
||||||
|
std::vector<int> stack(scene.nodes.begin(), scene.nodes.end());
|
||||||
|
|
||||||
|
while (!stack.empty()) {
|
||||||
|
int nodeIdx = stack.back();
|
||||||
|
stack.pop_back();
|
||||||
|
const tinygltf::Node& node = model.nodes.at(nodeIdx);
|
||||||
|
|
||||||
|
for (int child: node.children) stack.push_back(child);
|
||||||
|
|
||||||
|
if (node.mesh < 0) continue;
|
||||||
|
const tinygltf::Mesh& mesh = model.meshes.at(node.mesh);
|
||||||
|
|
||||||
|
CPUMesh merged{};
|
||||||
|
merged.name = mesh.name.empty() ? ("mesh_" + std::to_string(node.mesh)) : mesh.name;
|
||||||
|
merged.minPos = glm::vec3(std::numeric_limits<float>::infinity());
|
||||||
|
merged.maxPos = glm::vec3(-std::numeric_limits<float>::infinity());
|
||||||
|
|
||||||
|
bool any = false;
|
||||||
|
for (const tinygltf::Primitive& prim: mesh.primitives) {
|
||||||
|
CPUMesh part = LoadPrimitiveIntoCPUMesh(model, prim, merged.name);
|
||||||
|
if (!any) {
|
||||||
|
merged = part;
|
||||||
|
any = true;
|
||||||
|
} else {
|
||||||
|
AppendMesh(merged, part);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (any) result.push_back(std::move(merged));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //MODELLOADER_H
|
||||||
@@ -146,6 +146,7 @@ void App::run() {
|
|||||||
std::chrono::duration<float>(dt)
|
std::chrono::duration<float>(dt)
|
||||||
);
|
);
|
||||||
std::this_thread::sleep_until(targetEnd);
|
std::this_thread::sleep_until(targetEnd);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
gfxDevice.waitIdle();
|
gfxDevice.waitIdle();
|
||||||
|
|||||||
2
destrum/third_party/CMakeLists.txt
vendored
2
destrum/third_party/CMakeLists.txt
vendored
@@ -51,3 +51,5 @@ option(JSON_Install "Install CMake targets during install step." OFF)
|
|||||||
add_subdirectory(json)
|
add_subdirectory(json)
|
||||||
|
|
||||||
add_subdirectory(spdlog)
|
add_subdirectory(spdlog)
|
||||||
|
|
||||||
|
add_subdirectory(tinygltf)
|
||||||
1
destrum/third_party/tinygltf
vendored
Submodule
1
destrum/third_party/tinygltf
vendored
Submodule
Submodule destrum/third_party/tinygltf added at 81bd50c106
@@ -51,7 +51,7 @@ void LightKeeper::customInit() {
|
|||||||
meshComp->SetMeshID(testMeshID);
|
meshComp->SetMeshID(testMeshID);
|
||||||
meshComp->SetMaterialID(testMaterialID);
|
meshComp->SetMaterialID(testMaterialID);
|
||||||
|
|
||||||
testCube->AddComponent<Rotator>(10, 5);
|
// testCube->AddComponent<Rotator>(10, 5);
|
||||||
|
|
||||||
|
|
||||||
scene.Add(testCube);
|
scene.Add(testCube);
|
||||||
|
|||||||
Reference in New Issue
Block a user