vk-book/shared/UtilsGLTF.h
2025-05-23 21:13:53 -04:00

528 lines
15 KiB
C++

#pragma once
#include "VulkanApp.h"
#include <assimp/GltfMaterial.h>
#include <assimp/cimport.h>
#include <assimp/postprocess.h>
#include <assimp/scene.h>
#include <assimp/types.h>
#include "LineCanvas.h"
#include "UtilsAnim.h"
#include <lvk/LVK.h>
enum MaterialType : uint32_t {
MaterialType_Invalid = 0,
MaterialType_Unlit = 0x80,
MaterialType_MetallicRoughness = 0x1,
MaterialType_SpecularGlossiness = 0x2,
MaterialType_Sheen = 0x4,
MaterialType_ClearCoat = 0x8,
MaterialType_Specular = 0x10,
MaterialType_Transmission = 0x20,
MaterialType_Volume = 0x40,
};
const uint32_t kMaxMaterials = 128;
const uint32_t kMaxEnvironments = 4;
const uint32_t kMaxLights = 8;
using glm::mat4;
using glm::quat;
using glm::vec2;
using glm::vec3;
using glm::vec4;
inline mat4 aiMatrix4x4ToMat4(const aiMatrix4x4& from)
{
mat4 to;
to[0][0] = (float)from.a1;
to[0][1] = (float)from.b1;
to[0][2] = (float)from.c1;
to[0][3] = (float)from.d1;
to[1][0] = (float)from.a2;
to[1][1] = (float)from.b2;
to[1][2] = (float)from.c2;
to[1][3] = (float)from.d2;
to[2][0] = (float)from.a3;
to[2][1] = (float)from.b3;
to[2][2] = (float)from.c3;
to[2][3] = (float)from.d3;
to[3][0] = (float)from.a4;
to[3][1] = (float)from.b4;
to[3][2] = (float)from.c4;
to[3][3] = (float)from.d4;
return to;
}
inline vec3 aiVector3DToVec3(const aiVector3D& from)
{
return vec3(from.x, from.y, from.z);
}
inline glm::quat aiQuaternionToQuat(const aiQuaternion& from)
{
return glm::quat(from.w, from.x, from.y, from.z);
}
enum LightType : uint32_t {
LightType_Directional = 0,
LightType_Point = 1,
LightType_Spot = 2,
};
struct EnvironmentMapDataGPU {
uint32_t envMapTexture = 0;
uint32_t envMapTextureSampler = 0;
uint32_t envMapTextureIrradiance = 0;
uint32_t envMapTextureIrradianceSampler = 0;
uint32_t lutBRDFTexture = 0;
uint32_t lutBRDFTextureSampler = 0;
uint32_t envMapTextureCharlie = 0;
uint32_t envMapTextureCharlieSampler = 0;
};
struct LightDataGPU {
vec3 direction = vec3(0, 0, 1);
float range = 10000.0;
vec3 color = vec3(1, 1, 1);
float intensity = 1.0;
vec3 position = vec3(0, 0, -5);
float innerConeCos = 0.0;
float outerConeCos = 0.78;
LightType type = LightType_Directional;
int nodeId = ~0;
int padding = 0;
};
struct GLTFGlobalSamplers {
explicit GLTFGlobalSamplers(const std::unique_ptr<lvk::IContext>& ctx)
{
clamp = ctx->createSampler({
.minFilter = lvk::SamplerFilter::SamplerFilter_Linear,
.magFilter = lvk::SamplerFilter::SamplerFilter_Linear,
.mipMap = lvk::SamplerMip::SamplerMip_Linear,
.wrapU = lvk::SamplerWrap::SamplerWrap_Clamp,
.wrapV = lvk::SamplerWrap::SamplerWrap_Clamp,
.wrapW = lvk::SamplerWrap::SamplerWrap_Clamp,
.debugName = "Clamp Sampler",
});
wrap = ctx->createSampler({
.minFilter = lvk::SamplerFilter::SamplerFilter_Linear,
.magFilter = lvk::SamplerFilter::SamplerFilter_Linear,
.mipMap = lvk::SamplerMip::SamplerMip_Linear,
.wrapU = lvk::SamplerWrap::SamplerWrap_Repeat,
.wrapV = lvk::SamplerWrap::SamplerWrap_Repeat,
.wrapW = lvk::SamplerWrap::SamplerWrap_Repeat,
.debugName = "Wrap Sampler",
});
mirror = ctx->createSampler({
.minFilter = lvk::SamplerFilter::SamplerFilter_Linear,
.magFilter = lvk::SamplerFilter::SamplerFilter_Linear,
.mipMap = lvk::SamplerMip::SamplerMip_Linear,
.wrapU = lvk::SamplerWrap::SamplerWrap_MirrorRepeat,
.wrapV = lvk::SamplerWrap::SamplerWrap_MirrorRepeat,
.debugName = "Mirror Sampler",
});
}
lvk::Holder<lvk::SamplerHandle> clamp;
lvk::Holder<lvk::SamplerHandle> wrap;
lvk::Holder<lvk::SamplerHandle> mirror;
};
struct EnvironmentsPerFrame {
EnvironmentMapDataGPU environments[kMaxEnvironments];
};
struct Vertex {
vec3 position;
vec3 normal;
vec4 color;
vec2 uv0;
vec2 uv1;
float padding[2];
};
struct MorphTarget {
uint32_t meshId = ~0;
std::vector<uint32_t> offset;
};
static_assert(sizeof(Vertex) == sizeof(uint32_t) * 16);
struct GLTFMaterialTextures {
// Metallic Roughness / SpecluarGlossiness
lvk::Holder<lvk::TextureHandle> baseColorTexture;
lvk::Holder<lvk::TextureHandle> surfacePropertiesTexture;
// Common properties
lvk::Holder<lvk::TextureHandle> normalTexture;
lvk::Holder<lvk::TextureHandle> occlusionTexture;
lvk::Holder<lvk::TextureHandle> emissiveTexture;
// Sheen
lvk::Holder<lvk::TextureHandle> sheenColorTexture;
lvk::Holder<lvk::TextureHandle> sheenRoughnessTexture;
// Clearcoat
lvk::Holder<lvk::TextureHandle> clearCoatTexture;
lvk::Holder<lvk::TextureHandle> clearCoatRoughnessTexture;
lvk::Holder<lvk::TextureHandle> clearCoatNormalTexture;
// Specular
lvk::Holder<lvk::TextureHandle> specularTexture;
lvk::Holder<lvk::TextureHandle> specularColorTexture;
// Transmission
lvk::Holder<lvk::TextureHandle> transmissionTexture;
// Volumen
lvk::Holder<lvk::TextureHandle> thicknessTexture;
// Iridescence
lvk::Holder<lvk::TextureHandle> iridescenceTexture;
lvk::Holder<lvk::TextureHandle> iridescenceThicknessTexture;
// Anisotropy
lvk::Holder<lvk::TextureHandle> anisotropyTexture;
lvk::Holder<lvk::TextureHandle> white;
bool wasLoaded = false;
};
struct EnvironmentMapTextures {
explicit EnvironmentMapTextures(const std::unique_ptr<lvk::IContext>& ctx)
: EnvironmentMapTextures(
ctx, "data/brdfLUT.ktx", "data/piazza_bologni_1k_prefilter.ktx", "data/piazza_bologni_1k_irradiance.ktx",
"data/piazza_bologni_1k_charlie.ktx")
{
}
EnvironmentMapTextures(
const std::unique_ptr<lvk::IContext>& ctx, const char* brdfLUT, const char* prefilter, const char* irradiance,
const char* prefilterCharlie = nullptr)
{
texBRDF_LUT = loadTexture(ctx, brdfLUT, lvk::TextureType_2D);
if (texBRDF_LUT.empty()) {
assert(0);
exit(255);
}
envMapTexture = loadTexture(ctx, prefilter, lvk::TextureType_Cube);
if (envMapTexture.empty()) {
assert(0);
exit(255);
}
envMapTextureIrradiance = loadTexture(ctx, irradiance, lvk::TextureType_Cube);
if (envMapTextureIrradiance.empty()) {
assert(0);
exit(255);
}
if (prefilterCharlie) {
envMapTextureCharlie = loadTexture(ctx, prefilterCharlie, lvk::TextureType_Cube);
if (envMapTextureCharlie.empty()) {
assert(0);
exit(255);
}
}
}
lvk::Holder<lvk::TextureHandle> texBRDF_LUT;
lvk::Holder<lvk::TextureHandle> envMapTexture;
lvk::Holder<lvk::TextureHandle> envMapTextureCharlie;
lvk::Holder<lvk::TextureHandle> envMapTextureIrradiance;
};
bool assignUVandSampler(
const GLTFGlobalSamplers& samplers, const aiMaterial* mtlDescriptor, aiTextureType textureType, uint32_t& uvIndex,
uint32_t& textureSampler, int index = 0);
struct GLTFMaterialDataGPU {
vec4 baseColorFactor = vec4(1.0f, 1.0f, 1.0f, 1.0f);
vec4 metallicRoughnessNormalOcclusion =
vec4(1.0f, 1.0f, 1.0f, 1.0f); // Packed metallicFactor, roughnessFactor, normalScale, occlusionStrength
vec4 specularGlossiness = vec4(1.0f, 1.0f, 1.0f, 1.0f);
vec4 sheenFactors = vec4(1.0f, 1.0f, 1.0f, 1.0f); // Sheen
vec4 clearcoatTransmissionThickness = vec4(1.0f, 1.0f, 1.0f, 1.0f); // Clearcoat
vec4 specularFactors = vec4(1.0f, 1.0f, 1.0f, 1.0f);
vec4 attenuation = vec4(1.0f, 1.0f, 1.0f, 1.0f);
vec4 emissiveFactorAlphaCutoff = vec4(0.0f, 0.0f, 0.0f, 0.5f);
uint32_t occlusionTexture = 0;
uint32_t occlusionTextureSampler = 0;
uint32_t occlusionTextureUV = 0;
uint32_t emissiveTexture = 0;
uint32_t emissiveTextureSampler = 0;
uint32_t emissiveTextureUV = 0;
uint32_t baseColorTexture = 0;
uint32_t baseColorTextureSampler = 0;
uint32_t baseColorTextureUV = 0;
uint32_t surfacePropertiesTexture = 0;
uint32_t surfacePropertiesTextureSampler = 0;
uint32_t surfacePropertiesTextureUV = 0;
uint32_t normalTexture = 0;
uint32_t normalTextureSampler = 0;
uint32_t normalTextureUV = 0;
uint32_t sheenColorTexture = 0;
uint32_t sheenColorTextureSampler = 0;
uint32_t sheenColorTextureUV = 0;
uint32_t sheenRoughnessTexture = 0;
uint32_t sheenRoughnessTextureSampler = 0;
uint32_t sheenRoughnessTextureUV = 0;
uint32_t clearCoatTexture = 0;
uint32_t clearCoatTextureSampler = 0;
uint32_t clearCoatTextureUV = 0;
uint32_t clearCoatRoughnessTexture = 0;
uint32_t clearCoatRoughnessTextureSampler = 0;
uint32_t clearCoatRoughnessTextureUV = 0;
uint32_t clearCoatNormalTexture = 0;
uint32_t clearCoatNormalTextureSampler = 0;
uint32_t clearCoatNormalTextureUV = 0;
uint32_t specularTexture = 0;
uint32_t specularTextureSampler = 0;
uint32_t specularTextureUV = 0;
uint32_t specularColorTexture = 0;
uint32_t specularColorTextureSampler = 0;
uint32_t specularColorTextureUV = 0;
uint32_t transmissionTexture = 0;
uint32_t transmissionTextureSampler = 0;
uint32_t transmissionTextureUV = 0;
uint32_t thicknessTexture = 0;
uint32_t thicknessTextureSampler = 0;
uint32_t thicknessTextureUV = 0;
uint32_t iridescenceTexture = 0;
uint32_t iridescenceTextureSampler = 0;
uint32_t iridescenceTextureUV = 0;
uint32_t iridescenceThicknessTexture = 0;
uint32_t iridescenceThicknessTextureSampler = 0;
uint32_t iridescenceThicknessTextureUV = 0;
uint32_t anisotropyTexture = 0;
uint32_t anisotropyTextureSampler = 0;
uint32_t anisotropyTextureUV = 0;
uint32_t alphaMode = 0;
uint32_t materialTypeFlags = 0;
float ior = 1.5f;
uint32_t padding[2] = { 0, 0 };
enum AlphaMode : uint32_t {
AlphaMode_Opaque = 0,
AlphaMode_Mask = 1,
AlphaMode_Blend = 2,
};
};
static_assert(sizeof(GLTFMaterialDataGPU) % 16 == 0);
struct GLTFDataHolder {
std::vector<GLTFMaterialTextures> textures;
};
struct MaterialsPerFrame {
GLTFMaterialDataGPU materials[kMaxMaterials];
};
using GLTFNodeRef = uint32_t;
using GLTFMeshRef = uint32_t;
enum SortingType : uint32_t {
SortingType_Opaque = 0,
SortingType_Transmission = 1,
SortingType_Transparent = 2,
};
struct GLTFMesh {
lvk::Topology primitive;
uint32_t vertexOffset;
uint32_t vertexCount;
uint32_t indexOffset;
uint32_t indexCount;
uint32_t matIdx;
SortingType sortingType;
};
struct GLTFNode {
std::string name;
uint32_t modelMtxId;
mat4 transform = mat4(1);
std::vector<GLTFNodeRef> children;
std::vector<GLTFMeshRef> meshes;
};
struct GLTFFrameData {
mat4 model;
mat4 view;
mat4 proj;
vec4 cameraPos;
};
struct GLTFCamera {
std::string name;
uint32_t nodeIdx = ~0;
vec3 pos;
vec3 up;
vec3 lookAt;
float hFOV;
float near;
float far;
float aspect = 1.0f;
float orthoWidth;
mat4 getProjection(float windowAspect = 1.0f) const
{
return orthoWidth != 0.0f
? glm::ortho(-windowAspect / orthoWidth, windowAspect / orthoWidth, -1.0f / orthoWidth, 1.0f / orthoWidth, far, near)
: glm::perspective(hFOV, windowAspect == 1.0f ? aspect : windowAspect, near, far);
}
};
struct GLTFTransforms {
uint32_t modelMtxId;
uint32_t matId;
GLTFNodeRef nodeRef; // for CPU only
GLTFMeshRef meshRef; // for CPU only
uint32_t sortingType;
};
// Skeleton, animation, morphing
#define MAX_BONES_PER_VERTEX 8
struct VertexBoneData {
vec4 position;
vec4 normal;
uint32_t boneId[MAX_BONES_PER_VERTEX] = { ~0u, ~0u, ~0u, ~0u, ~0u, ~0u, ~0u, ~0u };
float weight[MAX_BONES_PER_VERTEX] = {};
uint32_t meshId = ~0u;
};
static_assert(sizeof(VertexBoneData) == sizeof(uint32_t) * 25);
struct GLTFBone {
uint32_t boneId = ~0u;
mat4 transform = mat4(1);
};
GLTFMaterialDataGPU setupglTFMaterialData(
const std::unique_ptr<lvk::IContext>& ctx, const GLTFGlobalSamplers& samplers, aiMaterial* const& mtlDescriptor,
const char* assetFolder, GLTFDataHolder& glTFDataholder, bool& useVolumetric);
struct GLTFContext {
explicit GLTFContext(VulkanApp& app_)
: app(app_)
, samplers(app_.ctx_)
, envMapTextures(app_.ctx_)
{
}
GLTFDataHolder glTFDataholder;
MaterialsPerFrame matPerFrame;
GLTFGlobalSamplers samplers;
EnvironmentMapTextures envMapTextures;
GLTFFrameData frameData;
std::vector<GLTFTransforms> transforms;
std::vector<mat4> matrices;
std::vector<GLTFNode> nodesStorage;
std::vector<GLTFMesh> meshesStorage;
std::unordered_map<std::string, GLTFBone> bonesByName;
std::vector<MorphTarget> morphTargets;
std::unordered_map<std::string, uint32_t> meshesRemap;
std::vector<Animation> animations;
std::vector<uint32_t> opaqueNodes;
std::vector<uint32_t> transmissionNodes;
std::vector<uint32_t> transparentNodes;
lvk::Holder<lvk::BufferHandle> envBuffer;
lvk::Holder<lvk::BufferHandle> lightsBuffer;
lvk::Holder<lvk::BufferHandle> perFrameBuffer;
lvk::Holder<lvk::BufferHandle> transformBuffer;
lvk::Holder<lvk::BufferHandle> matricesBuffer;
lvk::Holder<lvk::BufferHandle> morphStatesBuffer;
lvk::Holder<lvk::RenderPipelineHandle> pipelineSolid;
lvk::Holder<lvk::RenderPipelineHandle> pipelineTransparent;
lvk::Holder<lvk::ComputePipelineHandle> pipelineComputeAnimations;
lvk::Holder<lvk::ShaderModuleHandle> vert;
lvk::Holder<lvk::ShaderModuleHandle> frag;
lvk::Holder<lvk::ShaderModuleHandle> animation;
lvk::Holder<lvk::BufferHandle> vertexBuffer;
lvk::Holder<lvk::BufferHandle> vertexSkinningBuffer;
lvk::Holder<lvk::BufferHandle> vertexMorphingBuffer;
lvk::Holder<lvk::BufferHandle> indexBuffer;
lvk::Holder<lvk::BufferHandle> matBuffer;
lvk::Holder<lvk::TextureHandle> offscreenTex[3] = {};
uint32_t currentOffscreenTex = 0;
uint32_t maxVertices = 0;
std::vector<MorphState> morphStates;
std::vector<LightDataGPU> lights;
std::vector<GLTFCamera> cameras;
GLTFIntrospective inspector;
GLTFNodeRef root;
VulkanApp& app;
LineCanvas3D canvas3d;
bool hasBones = false;
bool isVolumetricMaterial = false;
bool animated = false;
bool skinning = false;
bool morphing = false;
bool doublesided = false;
bool enableMorphing = true;
bool isScreenCopyRequired() const { return isVolumetricMaterial; }
};
void loadGLTF(GLTFContext& context, const char* gltfName, const char* glTFDataPath);
void renderGLTF(GLTFContext& context, const mat4& model, const mat4& view, const mat4& proj, bool rebuildRenderList = false);
void animateGLTF(GLTFContext& gltf, AnimationState& anim, float dt);
void animateBlendingGLTF(GLTFContext& gltf, AnimationState& anim1, AnimationState& anim2, float weight, float dt);
MaterialType detectMaterialType(const aiMaterial* mtl);
void printPrefix(int ofs);
void printMat4(const aiMatrix4x4& m);
std::vector<std::string> camerasGLTF(GLTFContext& context);
void updateCamera(GLTFContext& gltf, const mat4& model, mat4& view, mat4& proj, float aspectRatio);
std::vector<std::string> animationsGLTF(GLTFContext& gltf);