1258 lines
47 KiB
C++
1258 lines
47 KiB
C++
|
|
#include "UtilsGLTF.h"
|
||
|
|
|
||
|
|
bool assignUVandSampler(
|
||
|
|
const GLTFGlobalSamplers& samplers, const aiMaterial* mtlDescriptor, aiTextureType textureType, uint32_t& uvIndex,
|
||
|
|
uint32_t& textureSampler, int index)
|
||
|
|
{
|
||
|
|
aiString path;
|
||
|
|
aiTextureMapMode mapmode[3] = { aiTextureMapMode_Clamp, aiTextureMapMode_Clamp, aiTextureMapMode_Clamp };
|
||
|
|
const bool res = mtlDescriptor->GetTexture(textureType, index, &path, 0, &uvIndex, 0, 0, mapmode) == AI_SUCCESS;
|
||
|
|
switch (mapmode[0]) {
|
||
|
|
case aiTextureMapMode_Clamp:
|
||
|
|
textureSampler = samplers.clamp.index();
|
||
|
|
break;
|
||
|
|
case aiTextureMapMode_Wrap:
|
||
|
|
textureSampler = samplers.wrap.index();
|
||
|
|
break;
|
||
|
|
case aiTextureMapMode_Mirror:
|
||
|
|
textureSampler = samplers.mirror.index();
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
return res;
|
||
|
|
}
|
||
|
|
|
||
|
|
namespace
|
||
|
|
{
|
||
|
|
void loadMaterialTexture(
|
||
|
|
const aiMaterial* mtlDescriptor, aiTextureType textureType, const char* assetFolder, lvk::Holder<lvk::TextureHandle>& textureHandle,
|
||
|
|
const std::unique_ptr<lvk::IContext>& ctx, bool sRGB, int index = 0)
|
||
|
|
{
|
||
|
|
if (mtlDescriptor->GetTextureCount(textureType) > 0) {
|
||
|
|
aiString path;
|
||
|
|
if (mtlDescriptor->GetTexture(textureType, index, &path) == AI_SUCCESS) {
|
||
|
|
aiString fullPath(assetFolder);
|
||
|
|
fullPath.Append(path.C_Str());
|
||
|
|
|
||
|
|
textureHandle = loadTexture(ctx, fullPath.C_Str(), lvk::TextureType_2D, sRGB);
|
||
|
|
if (textureHandle.empty()) {
|
||
|
|
assert(0);
|
||
|
|
exit(256);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
uint32_t getNextMtxId(GLTFContext& gltf, const char* name, uint32_t& nextEmptyId, const mat4& mtx)
|
||
|
|
{
|
||
|
|
const auto it = gltf.bonesByName.find(name);
|
||
|
|
|
||
|
|
const uint32_t mtxId = (it == gltf.bonesByName.end()) ? nextEmptyId++ : it->second.boneId;
|
||
|
|
|
||
|
|
if (gltf.matrices.size() <= mtxId) {
|
||
|
|
gltf.matrices.resize(mtxId + 1);
|
||
|
|
}
|
||
|
|
|
||
|
|
gltf.matrices[mtxId] = mtx;
|
||
|
|
|
||
|
|
return mtxId;
|
||
|
|
}
|
||
|
|
|
||
|
|
} // namespace
|
||
|
|
|
||
|
|
GLTFMaterialDataGPU setupglTFMaterialData(
|
||
|
|
const std::unique_ptr<lvk::IContext>& ctx, const GLTFGlobalSamplers& samplers, const aiMaterial* mtlDescriptor, const char* assetFolder,
|
||
|
|
GLTFDataHolder& glTFDataholder, bool& useVolumetric, bool& usedoublesided)
|
||
|
|
{
|
||
|
|
GLTFMaterialTextures mat;
|
||
|
|
|
||
|
|
uint32_t materialTypeFlags = MaterialType_Invalid;
|
||
|
|
|
||
|
|
const uint32_t whitePixel = 0xFFFFFFFF;
|
||
|
|
|
||
|
|
mat.white = ctx->createTexture(
|
||
|
|
{
|
||
|
|
.type = lvk::TextureType_2D,
|
||
|
|
.format = lvk::Format_RGBA_SRGB8,
|
||
|
|
.dimensions = {1, 1},
|
||
|
|
.usage = lvk::TextureUsageBits_Sampled,
|
||
|
|
.data = &whitePixel,
|
||
|
|
.debugName = "white1x1",
|
||
|
|
},
|
||
|
|
"white1x1");
|
||
|
|
|
||
|
|
aiShadingMode shadingMode = aiShadingMode_NoShading;
|
||
|
|
if (mtlDescriptor->Get(AI_MATKEY_SHADING_MODEL, shadingMode) == AI_SUCCESS) {
|
||
|
|
if (shadingMode == aiShadingMode_Unlit) {
|
||
|
|
materialTypeFlags = MaterialType_Unlit;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
loadMaterialTexture(mtlDescriptor, aiTextureType_BASE_COLOR, assetFolder, mat.baseColorTexture, ctx, true);
|
||
|
|
loadMaterialTexture(mtlDescriptor, aiTextureType_METALNESS, assetFolder, mat.surfacePropertiesTexture, ctx, false);
|
||
|
|
|
||
|
|
materialTypeFlags = MaterialType_MetallicRoughness;
|
||
|
|
|
||
|
|
// Load common textures
|
||
|
|
loadMaterialTexture(mtlDescriptor, aiTextureType_LIGHTMAP, assetFolder, mat.occlusionTexture, ctx, false);
|
||
|
|
loadMaterialTexture(mtlDescriptor, aiTextureType_EMISSIVE, assetFolder, mat.emissiveTexture, ctx, true);
|
||
|
|
loadMaterialTexture(mtlDescriptor, aiTextureType_NORMALS, assetFolder, mat.normalTexture, ctx, false);
|
||
|
|
|
||
|
|
// Sheen
|
||
|
|
loadMaterialTexture(mtlDescriptor, aiTextureType_SHEEN, assetFolder, mat.sheenColorTexture, ctx, true, 0);
|
||
|
|
loadMaterialTexture(mtlDescriptor, aiTextureType_SHEEN, assetFolder, mat.sheenRoughnessTexture, ctx, false, 1);
|
||
|
|
|
||
|
|
// Clearcoat
|
||
|
|
loadMaterialTexture(mtlDescriptor, aiTextureType_CLEARCOAT, assetFolder, mat.clearCoatTexture, ctx, true, 0);
|
||
|
|
loadMaterialTexture(mtlDescriptor, aiTextureType_CLEARCOAT, assetFolder, mat.clearCoatRoughnessTexture, ctx, false, 1);
|
||
|
|
loadMaterialTexture(mtlDescriptor, aiTextureType_CLEARCOAT, assetFolder, mat.clearCoatNormalTexture, ctx, false, 2);
|
||
|
|
|
||
|
|
// Specular
|
||
|
|
loadMaterialTexture(mtlDescriptor, aiTextureType_SPECULAR, assetFolder, mat.specularTexture, ctx, true, 0);
|
||
|
|
loadMaterialTexture(mtlDescriptor, aiTextureType_SPECULAR, assetFolder, mat.specularColorTexture, ctx, true, 1);
|
||
|
|
|
||
|
|
// Transmission
|
||
|
|
loadMaterialTexture(mtlDescriptor, aiTextureType_TRANSMISSION, assetFolder, mat.transmissionTexture, ctx, true, 0);
|
||
|
|
|
||
|
|
// Volume
|
||
|
|
loadMaterialTexture(mtlDescriptor, aiTextureType_TRANSMISSION, assetFolder, mat.thicknessTexture, ctx, true, 1);
|
||
|
|
|
||
|
|
// Iridescence
|
||
|
|
// loadMaterialTexture(mtlDescriptor, aiTextureType_IRID, assetFolder, mat.specularTexture, ctx, true, 0);
|
||
|
|
|
||
|
|
// Anisotropy
|
||
|
|
|
||
|
|
GLTFMaterialDataGPU res = {
|
||
|
|
.baseColorFactor = vec4(1.0f, 1.0f, 1.0f, 1.0f),
|
||
|
|
.metallicRoughnessNormalOcclusion = vec4(1.0f, 1.0f, 1.0f, 1.0f),
|
||
|
|
.specularFactors = vec4(1.0f, 1.0f, 1.0f, 1.0f),
|
||
|
|
.emissiveFactorAlphaCutoff = vec4(0.0f, 0.0f, 0.0f, 0.5f),
|
||
|
|
.occlusionTexture = mat.occlusionTexture.index(),
|
||
|
|
.emissiveTexture = mat.emissiveTexture.valid() ? mat.emissiveTexture.index() : mat.white.index(),
|
||
|
|
.baseColorTexture = mat.baseColorTexture.valid() ? mat.baseColorTexture.index() : mat.white.index(),
|
||
|
|
.surfacePropertiesTexture = mat.surfacePropertiesTexture.valid() ? mat.surfacePropertiesTexture.index() : mat.white.index(),
|
||
|
|
.normalTexture = mat.normalTexture.valid() ? mat.normalTexture.index() : ~0,
|
||
|
|
.sheenColorTexture = mat.sheenColorTexture.valid() ? mat.sheenColorTexture.index() : mat.white.index(),
|
||
|
|
.sheenRoughnessTexture = mat.sheenRoughnessTexture.valid() ? mat.sheenRoughnessTexture.index() : mat.white.index(),
|
||
|
|
.clearCoatTexture = mat.clearCoatTexture.valid() ? mat.clearCoatTexture.index() : mat.white.index(),
|
||
|
|
.clearCoatRoughnessTexture = mat.clearCoatRoughnessTexture.valid() ? mat.clearCoatRoughnessTexture.index() : mat.white.index(),
|
||
|
|
.clearCoatNormalTexture = mat.clearCoatNormalTexture.valid() ? mat.clearCoatNormalTexture.index() : mat.white.index(),
|
||
|
|
.specularTexture = mat.specularTexture.valid() ? mat.specularTexture.index() : mat.white.index(),
|
||
|
|
.specularColorTexture = mat.specularColorTexture.valid() ? mat.specularColorTexture.index() : mat.white.index(),
|
||
|
|
.transmissionTexture = mat.transmissionTexture.valid() ? mat.transmissionTexture.index() : mat.white.index(),
|
||
|
|
.thicknessTexture = mat.thicknessTexture.valid() ? mat.thicknessTexture.index() : mat.white.index(),
|
||
|
|
.iridescenceTexture = mat.iridescenceTexture.index(),
|
||
|
|
.iridescenceThicknessTexture = mat.iridescenceThicknessTexture.index(),
|
||
|
|
.anisotropyTexture = mat.anisotropyTexture.index(),
|
||
|
|
.materialTypeFlags = materialTypeFlags,
|
||
|
|
};
|
||
|
|
|
||
|
|
aiColor4D aiColor;
|
||
|
|
if (mtlDescriptor->Get(AI_MATKEY_COLOR_DIFFUSE, aiColor) == AI_SUCCESS) {
|
||
|
|
res.baseColorFactor = vec4(aiColor.r, aiColor.g, aiColor.b, aiColor.a);
|
||
|
|
}
|
||
|
|
assignUVandSampler(samplers, mtlDescriptor, aiTextureType_DIFFUSE, res.baseColorTextureUV, res.baseColorTextureSampler);
|
||
|
|
|
||
|
|
if (mtlDescriptor->Get(AI_MATKEY_COLOR_EMISSIVE, aiColor) == AI_SUCCESS) {
|
||
|
|
// mat.emissiveFactor = vec3(aiColor.r, aiColor.g, aiColor.b);
|
||
|
|
res.emissiveFactorAlphaCutoff = vec4(aiColor.r, aiColor.g, aiColor.b, 0.5f);
|
||
|
|
}
|
||
|
|
assignUVandSampler(samplers, mtlDescriptor, aiTextureType_EMISSIVE, res.emissiveTextureUV, res.emissiveTextureSampler);
|
||
|
|
|
||
|
|
ai_real emissiveStrength = 1.0f;
|
||
|
|
if (mtlDescriptor->Get(AI_MATKEY_EMISSIVE_INTENSITY, emissiveStrength) == AI_SUCCESS) {
|
||
|
|
res.emissiveFactorAlphaCutoff *= vec4(emissiveStrength, emissiveStrength, emissiveStrength, 1.0f);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (materialTypeFlags & MaterialType_MetallicRoughness) {
|
||
|
|
ai_real metallicFactor;
|
||
|
|
if (mtlDescriptor->Get(AI_MATKEY_METALLIC_FACTOR, metallicFactor) == AI_SUCCESS) {
|
||
|
|
res.metallicRoughnessNormalOcclusion.x = metallicFactor;
|
||
|
|
}
|
||
|
|
assignUVandSampler(
|
||
|
|
samplers, mtlDescriptor, aiTextureType_METALNESS, res.surfacePropertiesTextureUV, res.surfacePropertiesTextureSampler);
|
||
|
|
|
||
|
|
ai_real roughnessFactor;
|
||
|
|
if (mtlDescriptor->Get(AI_MATKEY_ROUGHNESS_FACTOR, roughnessFactor) == AI_SUCCESS) {
|
||
|
|
res.metallicRoughnessNormalOcclusion.y = roughnessFactor;
|
||
|
|
}
|
||
|
|
} else if (materialTypeFlags & MaterialType_SpecularGlossiness) {
|
||
|
|
ai_real specularFactor[3];
|
||
|
|
if (mtlDescriptor->Get(AI_MATKEY_SPECULAR_FACTOR, specularFactor) == AI_SUCCESS) {
|
||
|
|
res.specularGlossiness.x = specularFactor[0];
|
||
|
|
res.specularGlossiness.y = specularFactor[1];
|
||
|
|
res.specularGlossiness.z = specularFactor[2];
|
||
|
|
}
|
||
|
|
assignUVandSampler(
|
||
|
|
samplers, mtlDescriptor, aiTextureType_SPECULAR, res.surfacePropertiesTextureUV, res.surfacePropertiesTextureSampler);
|
||
|
|
|
||
|
|
ai_real glossinessFactor;
|
||
|
|
if (mtlDescriptor->Get(AI_MATKEY_GLOSSINESS_FACTOR, glossinessFactor) == AI_SUCCESS) {
|
||
|
|
res.specularGlossiness.w = glossinessFactor;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
ai_real normalScale;
|
||
|
|
if (mtlDescriptor->Get(AI_MATKEY_GLTF_TEXTURE_SCALE(aiTextureType_NORMALS, 0), normalScale) == AI_SUCCESS) {
|
||
|
|
res.metallicRoughnessNormalOcclusion.z = normalScale;
|
||
|
|
}
|
||
|
|
assignUVandSampler(samplers, mtlDescriptor, aiTextureType_NORMALS, res.normalTextureUV, res.normalTextureSampler);
|
||
|
|
|
||
|
|
ai_real occlusionStrength;
|
||
|
|
if (mtlDescriptor->Get(AI_MATKEY_GLTF_TEXTURE_SCALE(aiTextureType_LIGHTMAP, 0), occlusionStrength) == AI_SUCCESS) {
|
||
|
|
res.metallicRoughnessNormalOcclusion.w = occlusionStrength;
|
||
|
|
}
|
||
|
|
assignUVandSampler(samplers, mtlDescriptor, aiTextureType_LIGHTMAP, res.occlusionTextureUV, res.occlusionTextureSampler);
|
||
|
|
|
||
|
|
aiString alphaMode = aiString("OPAQUE");
|
||
|
|
if (mtlDescriptor->Get(AI_MATKEY_GLTF_ALPHAMODE, alphaMode) == AI_SUCCESS) {
|
||
|
|
if (alphaMode == aiString("MASK")) {
|
||
|
|
res.alphaMode = GLTFMaterialDataGPU::AlphaMode_Mask;
|
||
|
|
} else if (alphaMode == aiString("BLEND")) {
|
||
|
|
res.alphaMode = GLTFMaterialDataGPU::AlphaMode_Blend;
|
||
|
|
} else {
|
||
|
|
res.alphaMode = GLTFMaterialDataGPU::AlphaMode_Opaque;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
ai_real alphaCutoff;
|
||
|
|
if (mtlDescriptor->Get(AI_MATKEY_GLTF_ALPHACUTOFF, alphaCutoff) == AI_SUCCESS) {
|
||
|
|
res.emissiveFactorAlphaCutoff.w = alphaCutoff;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Extensions
|
||
|
|
// Sheen
|
||
|
|
{
|
||
|
|
bool useSheen = !mat.sheenColorTexture.empty() || !mat.sheenRoughnessTexture.empty();
|
||
|
|
aiColor4D sheenColorFactor;
|
||
|
|
if (mtlDescriptor->Get(AI_MATKEY_SHEEN_COLOR_FACTOR, sheenColorFactor) == AI_SUCCESS) {
|
||
|
|
res.sheenFactors = vec4(sheenColorFactor.r, sheenColorFactor.g, sheenColorFactor.b, sheenColorFactor.a);
|
||
|
|
useSheen = true;
|
||
|
|
}
|
||
|
|
ai_real sheenRoughnessFactor;
|
||
|
|
if (mtlDescriptor->Get(AI_MATKEY_SHEEN_ROUGHNESS_FACTOR, sheenRoughnessFactor) == AI_SUCCESS) {
|
||
|
|
res.sheenFactors.w = sheenRoughnessFactor;
|
||
|
|
useSheen = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (assignUVandSampler(samplers, mtlDescriptor, aiTextureType_SHEEN, res.sheenColorTextureUV, res.sheenColorTextureSampler, 0)) {
|
||
|
|
useSheen = true;
|
||
|
|
}
|
||
|
|
if (assignUVandSampler(
|
||
|
|
samplers, mtlDescriptor, aiTextureType_SHEEN, res.sheenRoughnessTextureUV, res.sheenRoughnessTextureSampler, 1)) {
|
||
|
|
useSheen = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (useSheen) {
|
||
|
|
res.materialTypeFlags |= MaterialType_Sheen;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Clear coat
|
||
|
|
{
|
||
|
|
bool useClearCoat = !mat.clearCoatTexture.empty() || !mat.clearCoatRoughnessTexture.empty() || !mat.clearCoatNormalTexture.empty();
|
||
|
|
ai_real clearcoatFactor;
|
||
|
|
if (mtlDescriptor->Get(AI_MATKEY_CLEARCOAT_FACTOR, clearcoatFactor) == AI_SUCCESS) {
|
||
|
|
res.clearcoatTransmissionThickness.x = clearcoatFactor;
|
||
|
|
useClearCoat = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
ai_real clearcoatRoughnessFactor;
|
||
|
|
if (mtlDescriptor->Get(AI_MATKEY_CLEARCOAT_ROUGHNESS_FACTOR, clearcoatRoughnessFactor) == AI_SUCCESS) {
|
||
|
|
res.clearcoatTransmissionThickness.y = clearcoatRoughnessFactor;
|
||
|
|
useClearCoat = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (assignUVandSampler(
|
||
|
|
samplers, mtlDescriptor, aiTextureType_CLEARCOAT, res.clearCoatTextureUV, res.clearCoatNormalTextureSampler, 0)) {
|
||
|
|
useClearCoat = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (assignUVandSampler(
|
||
|
|
samplers, mtlDescriptor, aiTextureType_CLEARCOAT, res.clearCoatRoughnessTextureUV, res.clearCoatRoughnessTextureSampler, 1)) {
|
||
|
|
useClearCoat = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (assignUVandSampler(
|
||
|
|
samplers, mtlDescriptor, aiTextureType_CLEARCOAT, res.clearCoatNormalTextureUV, res.clearCoatNormalTextureSampler, 2)) {
|
||
|
|
useClearCoat = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (useClearCoat) {
|
||
|
|
res.materialTypeFlags |= MaterialType_ClearCoat;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Specular
|
||
|
|
{
|
||
|
|
bool useSpecular = !mat.specularColorTexture.empty() || !mat.specularTexture.empty();
|
||
|
|
|
||
|
|
ai_real specularFactor;
|
||
|
|
if (mtlDescriptor->Get(AI_MATKEY_SPECULAR_FACTOR, specularFactor) == AI_SUCCESS) {
|
||
|
|
res.specularFactors.w = specularFactor;
|
||
|
|
useSpecular = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
assignUVandSampler(samplers, mtlDescriptor, aiTextureType_SPECULAR, res.specularTextureUV, res.specularTextureSampler, 0);
|
||
|
|
|
||
|
|
aiColor4D specularColorFactor;
|
||
|
|
if (mtlDescriptor->Get(AI_MATKEY_COLOR_SPECULAR, specularColorFactor) == AI_SUCCESS) {
|
||
|
|
res.specularFactors = vec4(specularColorFactor.r, specularColorFactor.g, specularColorFactor.b, res.specularFactors.w);
|
||
|
|
useSpecular = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
assignUVandSampler(samplers, mtlDescriptor, aiTextureType_SPECULAR, res.specularColorTextureUV, res.specularColorTextureSampler, 1);
|
||
|
|
|
||
|
|
if (useSpecular) {
|
||
|
|
res.materialTypeFlags |= MaterialType_Specular;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Transmission
|
||
|
|
{
|
||
|
|
bool useTransmission = !mat.transmissionTexture.empty();
|
||
|
|
|
||
|
|
ai_real transmissionFactor = 0.0f;
|
||
|
|
if (mtlDescriptor->Get(AI_MATKEY_TRANSMISSION_FACTOR, transmissionFactor) == AI_SUCCESS) {
|
||
|
|
res.clearcoatTransmissionThickness.z = transmissionFactor;
|
||
|
|
useTransmission = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (useTransmission) {
|
||
|
|
res.materialTypeFlags |= MaterialType_Transmission;
|
||
|
|
useVolumetric = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
assignUVandSampler(samplers, mtlDescriptor, aiTextureType_TRANSMISSION, res.transmissionTextureUV, res.transmissionTextureSampler, 0);
|
||
|
|
}
|
||
|
|
|
||
|
|
{
|
||
|
|
bool useVolume = !mat.thicknessTexture.empty();
|
||
|
|
|
||
|
|
ai_real thicknessFactor = 0.0f;
|
||
|
|
if (mtlDescriptor->Get(AI_MATKEY_VOLUME_THICKNESS_FACTOR, thicknessFactor) == AI_SUCCESS) {
|
||
|
|
res.clearcoatTransmissionThickness.w = thicknessFactor;
|
||
|
|
useVolume = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
ai_real attenuationDistance = 0.0f;
|
||
|
|
if (mtlDescriptor->Get(AI_MATKEY_VOLUME_ATTENUATION_DISTANCE, attenuationDistance) == AI_SUCCESS) {
|
||
|
|
res.attenuation.w = attenuationDistance;
|
||
|
|
useVolume = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
aiColor4D volumeAttenuationColor;
|
||
|
|
if (mtlDescriptor->Get(AI_MATKEY_VOLUME_ATTENUATION_COLOR, volumeAttenuationColor) == AI_SUCCESS) {
|
||
|
|
res.attenuation.x = volumeAttenuationColor.r;
|
||
|
|
res.attenuation.y = volumeAttenuationColor.g;
|
||
|
|
res.attenuation.z = volumeAttenuationColor.b;
|
||
|
|
useVolume = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (useVolume) {
|
||
|
|
res.materialTypeFlags |= MaterialType_Transmission | MaterialType_Volume;
|
||
|
|
useVolumetric = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
assignUVandSampler(samplers, mtlDescriptor, aiTextureType_TRANSMISSION, res.thicknessTextureUV, res.thicknessTextureSampler, 1);
|
||
|
|
}
|
||
|
|
|
||
|
|
// IOR
|
||
|
|
ai_real ior;
|
||
|
|
if (mtlDescriptor->Get(AI_MATKEY_REFRACTI, ior) == AI_SUCCESS) {
|
||
|
|
res.ior = ior;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Doublesided
|
||
|
|
bool ds = false;
|
||
|
|
if (mtlDescriptor->Get(AI_MATKEY_TWOSIDED, ds) == AI_SUCCESS) {
|
||
|
|
usedoublesided |= ds;
|
||
|
|
}
|
||
|
|
|
||
|
|
mat.wasLoaded = true;
|
||
|
|
|
||
|
|
glTFDataholder.textures.push_back(std::move(mat));
|
||
|
|
|
||
|
|
return res;
|
||
|
|
}
|
||
|
|
|
||
|
|
static uint32_t getNumVertices(const aiScene& scene)
|
||
|
|
{
|
||
|
|
uint32_t num = 0;
|
||
|
|
for (uint32_t i = 0; i != scene.mNumMeshes; i++) {
|
||
|
|
num += scene.mMeshes[i]->mNumVertices;
|
||
|
|
}
|
||
|
|
return num;
|
||
|
|
}
|
||
|
|
|
||
|
|
static uint32_t getNumIndices(const aiScene& scene)
|
||
|
|
{
|
||
|
|
uint32_t num = 0;
|
||
|
|
for (uint32_t i = 0; i != scene.mNumMeshes; i++) {
|
||
|
|
for (uint32_t j = 0; j != scene.mMeshes[i]->mNumFaces; j++) {
|
||
|
|
LVK_ASSERT(scene.mMeshes[i]->mFaces[j].mNumIndices == 3);
|
||
|
|
num += scene.mMeshes[i]->mFaces[j].mNumIndices;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return num;
|
||
|
|
}
|
||
|
|
|
||
|
|
static uint32_t getNumMorphVertices(const aiScene& scene)
|
||
|
|
{
|
||
|
|
uint32_t num = 0;
|
||
|
|
for (uint32_t i = 0; i != scene.mNumMeshes; i++) {
|
||
|
|
num += scene.mMeshes[i]->mNumVertices * scene.mMeshes[i]->mNumAnimMeshes;
|
||
|
|
}
|
||
|
|
return num;
|
||
|
|
}
|
||
|
|
|
||
|
|
static uint32_t getNodeId(GLTFContext& gltf, const char* name)
|
||
|
|
{
|
||
|
|
for (uint32_t i = 0; i != gltf.nodesStorage.size(); i++) {
|
||
|
|
if (gltf.nodesStorage[i].name == name)
|
||
|
|
return i;
|
||
|
|
}
|
||
|
|
return ~0;
|
||
|
|
}
|
||
|
|
|
||
|
|
void updateLights(GLTFContext& gltf, lvk::ICommandBuffer& buf)
|
||
|
|
{
|
||
|
|
for (LightDataGPU& light : gltf.lights) {
|
||
|
|
if (light.nodeId == -1)
|
||
|
|
continue;
|
||
|
|
light.position = vec3(gltf.matrices[light.nodeId][3]);
|
||
|
|
light.direction = gltf.matrices[light.nodeId] * vec4(light.direction, 0.0);
|
||
|
|
}
|
||
|
|
|
||
|
|
LVK_ASSERT(gltf.lights.size() <= kMaxLights);
|
||
|
|
|
||
|
|
buf.cmdUpdateBuffer(gltf.lightsBuffer, 0, gltf.lights.size() * sizeof(LightDataGPU), gltf.lights.data());
|
||
|
|
}
|
||
|
|
|
||
|
|
void loadGLTF(GLTFContext& gltf, const char* glTFName, const char* glTFDataPath)
|
||
|
|
{
|
||
|
|
const aiScene* scene = aiImportFile(glTFName, aiProcess_Triangulate);
|
||
|
|
if (!scene || !scene->HasMeshes()) {
|
||
|
|
printf("Unable to load %s\n", glTFName);
|
||
|
|
exit(255);
|
||
|
|
}
|
||
|
|
SCOPE_EXIT
|
||
|
|
{
|
||
|
|
aiReleaseImport(scene);
|
||
|
|
};
|
||
|
|
|
||
|
|
std::vector<Vertex> vertices;
|
||
|
|
std::vector<VertexBoneData> skinningData;
|
||
|
|
std::vector<uint32_t> indices;
|
||
|
|
std::vector<uint32_t> startVertex;
|
||
|
|
std::vector<uint32_t> startIndex;
|
||
|
|
|
||
|
|
startVertex.push_back(0);
|
||
|
|
startIndex.push_back(0);
|
||
|
|
|
||
|
|
vertices.reserve(getNumVertices(*scene));
|
||
|
|
indices.reserve(getNumIndices(*scene));
|
||
|
|
skinningData.resize(getNumVertices(*scene));
|
||
|
|
|
||
|
|
std::vector<Vertex> morphData;
|
||
|
|
gltf.morphTargets.resize(scene->mNumMeshes);
|
||
|
|
morphData.reserve(getNumMorphVertices(*scene));
|
||
|
|
|
||
|
|
uint32_t numBones = 0;
|
||
|
|
uint32_t morphTargetsOffset = 0;
|
||
|
|
uint32_t vertOffset = 0;
|
||
|
|
|
||
|
|
for (uint32_t m = 0; m < scene->mNumMeshes; ++m) {
|
||
|
|
const aiMesh* mesh = scene->mMeshes[m];
|
||
|
|
gltf.meshesRemap[mesh->mName.C_Str()] = m;
|
||
|
|
|
||
|
|
for (uint32_t i = 0; i < mesh->mNumVertices; i++) {
|
||
|
|
const aiVector3D v = mesh->mVertices[i];
|
||
|
|
const aiVector3D n = mesh->mNormals ? mesh->mNormals[i] : aiVector3D(0.0f, 1.0f, 0.0f);
|
||
|
|
const aiColor4D c = mesh->mColors[0] ? mesh->mColors[0][i] : aiColor4D(1.0f, 1.0f, 1.0f, 1.0f);
|
||
|
|
const aiVector3D uv0 = mesh->mTextureCoords[0] ? mesh->mTextureCoords[0][i] : aiVector3D(0.0f, 0.0f, 0.0f);
|
||
|
|
const aiVector3D uv1 = mesh->mTextureCoords[1] ? mesh->mTextureCoords[1][i] : aiVector3D(0.0f, 0.0f, 0.0f);
|
||
|
|
vertices.push_back({
|
||
|
|
.position = vec3(v.x, v.y, v.z),
|
||
|
|
.normal = vec3(n.x, n.y, n.z),
|
||
|
|
.color = vec4(c.r, c.g, c.b, c.a),
|
||
|
|
.uv0 = vec2(uv0.x, 1.0f - uv0.y),
|
||
|
|
.uv1 = vec2(uv1.x, 1.0f - uv1.y),
|
||
|
|
});
|
||
|
|
|
||
|
|
if (mesh->mNumBones == 0) {
|
||
|
|
auto& vertex = skinningData[vertices.size() - 1];
|
||
|
|
vertex.meshId = m;
|
||
|
|
vertex.position = vec4(v.x, v.y, v.z, 0.0f);
|
||
|
|
vertex.normal = vec4(n.x, n.y, n.z, 0.0f);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
startVertex.push_back((uint32_t)vertices.size());
|
||
|
|
for (uint32_t i = 0; i < mesh->mNumFaces; i++) {
|
||
|
|
for (int j = 0; j != 3; j++) {
|
||
|
|
indices.push_back(mesh->mFaces[i].mIndices[j]);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
startIndex.push_back((uint32_t)indices.size());
|
||
|
|
|
||
|
|
gltf.hasBones = mesh->mNumBones > 0;
|
||
|
|
// load bones
|
||
|
|
for (uint32_t id = 0; id < mesh->mNumBones; id++) {
|
||
|
|
const aiBone& bone = *mesh->mBones[id];
|
||
|
|
const char* boneName = bone.mName.C_Str();
|
||
|
|
|
||
|
|
const bool hasBone = gltf.bonesByName.contains(boneName);
|
||
|
|
|
||
|
|
const uint32_t boneId = hasBone ? gltf.bonesByName[boneName].boneId : numBones++;
|
||
|
|
|
||
|
|
if (!hasBone) {
|
||
|
|
gltf.bonesByName[boneName] = {
|
||
|
|
.boneId = boneId,
|
||
|
|
.transform = aiMatrix4x4ToMat4(bone.mOffsetMatrix),
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
for (uint32_t w = 0; w < bone.mNumWeights; w++) {
|
||
|
|
const uint32_t vertexId = bone.mWeights[w].mVertexId;
|
||
|
|
assert(vertexId <= vertices.size());
|
||
|
|
|
||
|
|
VertexBoneData& vtx = skinningData[vertexId + vertOffset];
|
||
|
|
assert(vtx.meshId == ~0u || vtx.meshId == m);
|
||
|
|
|
||
|
|
vtx.position = vec4(vertices[vertexId + vertOffset].position, 1.0f);
|
||
|
|
vtx.normal = vec4(vertices[vertexId + vertOffset].normal, 0.0f);
|
||
|
|
vtx.meshId = m;
|
||
|
|
for (uint32_t i = 0; i < MAX_BONES_PER_VERTEX; i++) {
|
||
|
|
if (vtx.boneId[i] == ~0u) {
|
||
|
|
vtx.weight[i] = bone.mWeights[w].mWeight;
|
||
|
|
vtx.boneId[i] = boneId;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
vertOffset += mesh->mNumVertices;
|
||
|
|
}
|
||
|
|
// load morph targets
|
||
|
|
for (uint32_t meshId = 0; meshId != scene->mNumMeshes; meshId++) {
|
||
|
|
const aiMesh* m = scene->mMeshes[meshId];
|
||
|
|
|
||
|
|
if (!m->mNumAnimMeshes)
|
||
|
|
continue;
|
||
|
|
|
||
|
|
MorphTarget& morphTarget = gltf.morphTargets[meshId];
|
||
|
|
morphTarget.meshId = meshId;
|
||
|
|
|
||
|
|
for (uint32_t a = 0; a < m->mNumAnimMeshes; a++) {
|
||
|
|
const aiAnimMesh* mesh = m->mAnimMeshes[a];
|
||
|
|
|
||
|
|
for (uint32_t i = 0; i < mesh->mNumVertices; i++) {
|
||
|
|
const aiVector3D v = mesh->mVertices[i];
|
||
|
|
const aiVector3D n = mesh->mNormals ? mesh->mNormals[i] : aiVector3D(0.0f, 1.0f, 0.0f);
|
||
|
|
const aiVector3D srcNorm = m->mNormals ? m->mNormals[i] : aiVector3D(0.0f, 1.0f, 0.0f);
|
||
|
|
const aiColor4D c = mesh->mColors[0] ? mesh->mColors[0][i] : aiColor4D(1.0f, 1.0f, 1.0f, 1.0f);
|
||
|
|
const aiVector3D uv0 = mesh->mTextureCoords[0] ? mesh->mTextureCoords[0][i] : aiVector3D(0.0f, 0.0f, 0.0f);
|
||
|
|
const aiVector3D uv1 = mesh->mTextureCoords[1] ? mesh->mTextureCoords[1][i] : aiVector3D(0.0f, 0.0f, 0.0f);
|
||
|
|
morphData.push_back({
|
||
|
|
.position = vec3(v.x - m->mVertices[i].x, v.y - m->mVertices[i].y, v.z - m->mVertices[i].z),
|
||
|
|
.normal = vec3(n.x - srcNorm.x, n.y - srcNorm.y, n.z - srcNorm.z),
|
||
|
|
.color = vec4(c.r, c.g, c.b, c.a),
|
||
|
|
.uv0 = vec2(uv0.x, 1.0f - uv0.y),
|
||
|
|
.uv1 = vec2(uv1.x, 1.0f - uv1.y),
|
||
|
|
});
|
||
|
|
}
|
||
|
|
morphTarget.offset.push_back(morphTargetsOffset);
|
||
|
|
morphTargetsOffset += mesh->mNumVertices;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!scene->mRootNode) {
|
||
|
|
printf("Scene has no root node\n");
|
||
|
|
exit(255);
|
||
|
|
}
|
||
|
|
|
||
|
|
auto& ctx = gltf.app.ctx_;
|
||
|
|
|
||
|
|
for (unsigned int mtl = 0; mtl < scene->mNumMaterials; ++mtl) {
|
||
|
|
const aiMaterial* mtlDescriptor = scene->mMaterials[mtl];
|
||
|
|
gltf.matPerFrame.materials[mtl] = setupglTFMaterialData(
|
||
|
|
ctx, gltf.samplers, mtlDescriptor, glTFDataPath, gltf.glTFDataholder, gltf.isVolumetricMaterial, gltf.doublesided);
|
||
|
|
gltf.inspector.materials.push_back({
|
||
|
|
.name = mtlDescriptor->GetName().C_Str() ? mtlDescriptor->GetName().C_Str() : "Material",
|
||
|
|
.materialMask = gltf.matPerFrame.materials[mtl].materialTypeFlags,
|
||
|
|
.currentMaterialMask = gltf.matPerFrame.materials[mtl].materialTypeFlags,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
uint32_t nonBoneMtxId = numBones;
|
||
|
|
|
||
|
|
const char* rootName = scene->mRootNode->mName.C_Str() ? scene->mRootNode->mName.C_Str() : "root";
|
||
|
|
|
||
|
|
gltf.nodesStorage.push_back({
|
||
|
|
.name = rootName,
|
||
|
|
.modelMtxId = getNextMtxId(gltf, rootName, nonBoneMtxId, aiMatrix4x4ToMat4(scene->mRootNode->mTransformation)),
|
||
|
|
.transform = aiMatrix4x4ToMat4(scene->mRootNode->mTransformation),
|
||
|
|
});
|
||
|
|
|
||
|
|
gltf.root = gltf.nodesStorage.size() - 1;
|
||
|
|
|
||
|
|
std::function<void(const aiNode* rootNode, GLTFNodeRef gltfNode)> traverseTree = [&](const aiNode* rootNode, GLTFNodeRef gltfNode) {
|
||
|
|
for (unsigned int m = 0; m < rootNode->mNumMeshes; ++m) {
|
||
|
|
const uint32_t meshIdx = rootNode->mMeshes[m];
|
||
|
|
const aiMesh* mesh = scene->mMeshes[meshIdx];
|
||
|
|
|
||
|
|
gltf.meshesStorage.push_back({
|
||
|
|
.primitive = lvk::Topology_Triangle,
|
||
|
|
.vertexOffset = startVertex[meshIdx],
|
||
|
|
.vertexCount = mesh->mNumVertices,
|
||
|
|
.indexOffset = startIndex[meshIdx],
|
||
|
|
.indexCount = mesh->mNumFaces * 3,
|
||
|
|
.matIdx = mesh->mMaterialIndex,
|
||
|
|
.sortingType =
|
||
|
|
gltf.matPerFrame.materials[mesh->mMaterialIndex].alphaMode == GLTFMaterialDataGPU::AlphaMode_Blend ? SortingType_Transparent
|
||
|
|
: gltf.matPerFrame.materials[mesh->mMaterialIndex].materialTypeFlags & MaterialType_Transmission ? SortingType_Transmission
|
||
|
|
: SortingType_Opaque,
|
||
|
|
});
|
||
|
|
gltf.nodesStorage[gltfNode].meshes.push_back(gltf.meshesStorage.size() - 1);
|
||
|
|
}
|
||
|
|
for (GLTFNodeRef i = 0; i < rootNode->mNumChildren; i++) {
|
||
|
|
const aiNode* node = rootNode->mChildren[i];
|
||
|
|
const char* childName = node->mName.C_Str() ? node->mName.C_Str() : "node";
|
||
|
|
const GLTFNode childNode{
|
||
|
|
.name = childName,
|
||
|
|
.modelMtxId = getNextMtxId(
|
||
|
|
gltf, childName, nonBoneMtxId,
|
||
|
|
gltf.matrices[gltf.nodesStorage[gltfNode].modelMtxId] * aiMatrix4x4ToMat4(node->mTransformation)),
|
||
|
|
.transform = aiMatrix4x4ToMat4(node->mTransformation),
|
||
|
|
};
|
||
|
|
gltf.nodesStorage.push_back(childNode);
|
||
|
|
const size_t nodeIdx = gltf.nodesStorage.size() - 1;
|
||
|
|
gltf.nodesStorage[gltfNode].children.push_back(nodeIdx);
|
||
|
|
traverseTree(node, nodeIdx);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
traverseTree(scene->mRootNode, gltf.root);
|
||
|
|
|
||
|
|
initAnimations(gltf, scene);
|
||
|
|
|
||
|
|
// add dummy vertices to align buffer to 16 to run compute shader
|
||
|
|
gltf.maxVertices = (1 + (vertices.size() / 16)) * 16;
|
||
|
|
vertices.resize(gltf.maxVertices);
|
||
|
|
|
||
|
|
gltf.vertexBuffer = ctx->createBuffer({
|
||
|
|
.usage = lvk::BufferUsageBits_Vertex | lvk::BufferUsageBits_Storage,
|
||
|
|
.storage = lvk::StorageType_Device,
|
||
|
|
.size = sizeof(Vertex) * vertices.size(),
|
||
|
|
.data = vertices.data(),
|
||
|
|
.debugName = "Buffer: vertex",
|
||
|
|
});
|
||
|
|
|
||
|
|
size_t vssize = (1 + (skinningData.size() / 16)) * 16;
|
||
|
|
skinningData.resize(vssize);
|
||
|
|
|
||
|
|
assert(vssize == gltf.maxVertices);
|
||
|
|
|
||
|
|
gltf.vertexSkinningBuffer = ctx->createBuffer({
|
||
|
|
.usage = lvk::BufferUsageBits_Vertex | lvk::BufferUsageBits_Storage,
|
||
|
|
.storage = lvk::StorageType_Device,
|
||
|
|
.size = sizeof(VertexBoneData) * skinningData.size(),
|
||
|
|
.data = skinningData.data(),
|
||
|
|
.debugName = "Buffer: skinning vertex data",
|
||
|
|
});
|
||
|
|
|
||
|
|
const bool hasMorphData = !morphData.empty();
|
||
|
|
|
||
|
|
gltf.vertexMorphingBuffer = ctx->createBuffer({
|
||
|
|
.usage = lvk::BufferUsageBits_Vertex | lvk::BufferUsageBits_Storage,
|
||
|
|
.storage = lvk::StorageType_Device,
|
||
|
|
.size = hasMorphData ? sizeof(Vertex) * morphData.size() : sizeof(Vertex), // always have dummy buffer
|
||
|
|
.data = hasMorphData ? morphData.data() : nullptr,
|
||
|
|
.debugName = "Buffer: morphing vertex data",
|
||
|
|
});
|
||
|
|
|
||
|
|
gltf.morphStatesBuffer = gltf.app.ctx_->createBuffer({
|
||
|
|
.usage = lvk::BufferUsageBits_Vertex | lvk::BufferUsageBits_Storage,
|
||
|
|
.storage = lvk::StorageType_HostVisible,
|
||
|
|
.size = MAX_MORPHS * sizeof(MorphState),
|
||
|
|
.debugName = "Morphs matrices",
|
||
|
|
});
|
||
|
|
|
||
|
|
gltf.indexBuffer = ctx->createBuffer({
|
||
|
|
.usage = lvk::BufferUsageBits_Index,
|
||
|
|
.storage = lvk::StorageType_Device,
|
||
|
|
.size = sizeof(uint32_t) * indices.size(),
|
||
|
|
.data = indices.data(),
|
||
|
|
.debugName = "Buffer: index",
|
||
|
|
});
|
||
|
|
|
||
|
|
const lvk::VertexInput vdesc = {
|
||
|
|
.attributes = { { .location = 0, .format = lvk::VertexFormat::Float3, .offset = 0 },
|
||
|
|
{ .location = 1, .format = lvk::VertexFormat::Float3, .offset = 12 },
|
||
|
|
{ .location = 2, .format = lvk::VertexFormat::Float4, .offset = 24 },
|
||
|
|
{ .location = 3, .format = lvk::VertexFormat::Float2, .offset = 40 },
|
||
|
|
{ .location = 4, .format = lvk::VertexFormat::Float2, .offset = 48 }, },
|
||
|
|
.inputBindings = { { .stride = sizeof(Vertex) } },
|
||
|
|
};
|
||
|
|
|
||
|
|
gltf.vert = loadShaderModule(ctx, "data/shaders/gltf/main.vert");
|
||
|
|
gltf.frag = loadShaderModule(ctx, "data/shaders/gltf/main.frag");
|
||
|
|
gltf.animation = loadShaderModule(ctx, "data/shaders/gltf/animation.comp");
|
||
|
|
|
||
|
|
gltf.pipelineSolid = ctx->createRenderPipeline({
|
||
|
|
.vertexInput = vdesc,
|
||
|
|
.smVert = gltf.vert,
|
||
|
|
.smFrag = gltf.frag,
|
||
|
|
.color = { { .format = ctx->getSwapchainFormat() } },
|
||
|
|
.depthFormat = gltf.app.getDepthFormat(),
|
||
|
|
.cullMode = gltf.doublesided ? lvk::CullMode_None : lvk::CullMode_Back,
|
||
|
|
});
|
||
|
|
|
||
|
|
gltf.pipelineTransparent = ctx->createRenderPipeline({
|
||
|
|
.vertexInput = vdesc,
|
||
|
|
.smVert = gltf.vert,
|
||
|
|
.smFrag = gltf.frag,
|
||
|
|
.color = { {
|
||
|
|
.format = ctx->getSwapchainFormat(),
|
||
|
|
.blendEnabled = true,
|
||
|
|
.rgbBlendOp = lvk::BlendOp_Subtract,
|
||
|
|
.alphaBlendOp = lvk::BlendOp_Subtract,
|
||
|
|
.srcRGBBlendFactor = lvk::BlendFactor_SrcColor,
|
||
|
|
.srcAlphaBlendFactor = lvk::BlendFactor_SrcAlpha,
|
||
|
|
.dstRGBBlendFactor = lvk::BlendFactor_OneMinusDstColor,
|
||
|
|
.dstAlphaBlendFactor = lvk::BlendFactor_OneMinusDstAlpha,
|
||
|
|
} },
|
||
|
|
.depthFormat = gltf.app.getDepthFormat(),
|
||
|
|
.cullMode = lvk::CullMode_Back,
|
||
|
|
});
|
||
|
|
|
||
|
|
gltf.matBuffer = ctx->createBuffer({
|
||
|
|
.usage = lvk::BufferUsageBits_Storage,
|
||
|
|
.storage = lvk::StorageType_HostVisible,
|
||
|
|
.size = sizeof(gltf.matPerFrame),
|
||
|
|
.data = &gltf.matPerFrame,
|
||
|
|
.debugName = "PerFrame materials",
|
||
|
|
});
|
||
|
|
|
||
|
|
const EnvironmentsPerFrame envPerFrame = {
|
||
|
|
.environments = { {
|
||
|
|
.envMapTexture = gltf.envMapTextures.envMapTexture.index(),
|
||
|
|
.envMapTextureSampler = gltf.samplers.clamp.index(),
|
||
|
|
.envMapTextureIrradiance = gltf.envMapTextures.envMapTextureIrradiance.index(),
|
||
|
|
.envMapTextureIrradianceSampler = gltf.samplers.clamp.index(),
|
||
|
|
.lutBRDFTexture = gltf.envMapTextures.texBRDF_LUT.index(),
|
||
|
|
.lutBRDFTextureSampler = gltf.samplers.clamp.index(),
|
||
|
|
.envMapTextureCharlie = gltf.envMapTextures.envMapTextureCharlie.index(),
|
||
|
|
.envMapTextureCharlieSampler = gltf.samplers.clamp.index(),
|
||
|
|
} },
|
||
|
|
};
|
||
|
|
|
||
|
|
gltf.envBuffer = ctx->createBuffer({
|
||
|
|
.usage = lvk::BufferUsageBits_Storage,
|
||
|
|
.storage = lvk::StorageType_HostVisible,
|
||
|
|
.size = sizeof(envPerFrame),
|
||
|
|
.data = &envPerFrame,
|
||
|
|
.debugName = "PerFrame environments",
|
||
|
|
});
|
||
|
|
|
||
|
|
gltf.lightsBuffer = ctx->createBuffer({
|
||
|
|
.usage = lvk::BufferUsageBits_Storage,
|
||
|
|
.storage = lvk::StorageType_HostVisible,
|
||
|
|
.size = sizeof(LightDataGPU) * kMaxLights,
|
||
|
|
.debugName = "Lights",
|
||
|
|
});
|
||
|
|
|
||
|
|
// Load lights
|
||
|
|
// Atten = 1/( att0 + att1 * d + att2 * d*d)
|
||
|
|
{
|
||
|
|
for (size_t i = 0; i < scene->mNumLights; ++i) {
|
||
|
|
LightDataGPU ld;
|
||
|
|
const aiLight* light = scene->mLights[i];
|
||
|
|
|
||
|
|
ld.color = vec3(light->mColorDiffuse[0], light->mColorDiffuse[1], light->mColorDiffuse[2]);
|
||
|
|
ld.nodeId = getNodeId(gltf, light->mName.C_Str());
|
||
|
|
ld.direction = vec3(light->mDirection[0], light->mDirection[1], light->mDirection[2]);
|
||
|
|
ld.range = 1000.0f;
|
||
|
|
|
||
|
|
if (light->mType == aiLightSource_POINT) {
|
||
|
|
ld.type = LightType_Point;
|
||
|
|
} else if (light->mType == aiLightSource_SPOT) {
|
||
|
|
ld.type = LightType_Spot;
|
||
|
|
ld.innerConeCos = light->mAngleInnerCone;
|
||
|
|
ld.outerConeCos = light->mAngleOuterCone;
|
||
|
|
} else if (light->mType == aiLightSource_DIRECTIONAL) {
|
||
|
|
ld.type = LightType_Directional;
|
||
|
|
}
|
||
|
|
|
||
|
|
gltf.lights.push_back(ld);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (gltf.lights.empty()) {
|
||
|
|
gltf.lights.push_back(LightDataGPU());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
{
|
||
|
|
lvk::ICommandBuffer& buf = ctx->acquireCommandBuffer();
|
||
|
|
updateLights(gltf, buf);
|
||
|
|
ctx->submit(buf);
|
||
|
|
}
|
||
|
|
|
||
|
|
gltf.perFrameBuffer = ctx->createBuffer({
|
||
|
|
.usage = lvk::BufferUsageBits_Uniform,
|
||
|
|
.storage = lvk::StorageType_Device,
|
||
|
|
.size = sizeof(GLTFFrameData),
|
||
|
|
.data = &gltf.frameData,
|
||
|
|
.debugName = "GLTFContext::perFrameBuffer",
|
||
|
|
});
|
||
|
|
|
||
|
|
LVK_ASSERT(gltf.pipelineSolid.valid());
|
||
|
|
|
||
|
|
// Cameras
|
||
|
|
|
||
|
|
gltf.cameras.reserve(scene->mNumCameras);
|
||
|
|
for (uint32_t c = 0; c < scene->mNumCameras; ++c) {
|
||
|
|
aiCamera* camera = scene->mCameras[c];
|
||
|
|
gltf.cameras.push_back({ .name = camera->mName.C_Str(),
|
||
|
|
.nodeIdx = getNodeId(gltf, camera->mName.C_Str()),
|
||
|
|
.pos = aiVector3DToVec3(camera->mPosition),
|
||
|
|
.up = aiVector3DToVec3(camera->mUp),
|
||
|
|
.lookAt = aiVector3DToVec3(camera->mUp),
|
||
|
|
.hFOV = camera->mHorizontalFOV,
|
||
|
|
.near = camera->mClipPlaneNear,
|
||
|
|
.far = camera->mClipPlaneFar,
|
||
|
|
.aspect = camera->mAspect,
|
||
|
|
.orthoWidth = camera->mOrthographicWidth });
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void updateCamera(GLTFContext& gltf, const mat4& model, mat4& view, mat4& proj, float aspectRatio)
|
||
|
|
{
|
||
|
|
if (gltf.inspector.activeCamera == ~0u || gltf.inspector.activeCamera >= gltf.cameras.size())
|
||
|
|
return;
|
||
|
|
|
||
|
|
const GLTFCamera& cam = gltf.cameras[gltf.inspector.activeCamera];
|
||
|
|
if (cam.nodeIdx == ~0u)
|
||
|
|
return;
|
||
|
|
|
||
|
|
view = glm::inverse(model * gltf.matrices[cam.nodeIdx]);
|
||
|
|
proj = cam.getProjection(aspectRatio);
|
||
|
|
}
|
||
|
|
|
||
|
|
void buildTransformsList(GLTFContext& gltf)
|
||
|
|
{
|
||
|
|
gltf.transforms.clear();
|
||
|
|
gltf.opaqueNodes.clear();
|
||
|
|
gltf.transmissionNodes.clear();
|
||
|
|
gltf.transparentNodes.clear();
|
||
|
|
|
||
|
|
std::function<void(GLTFNodeRef gltfNode)> traverseTree = [&](GLTFNodeRef nodeRef) {
|
||
|
|
GLTFNode& node = gltf.nodesStorage[nodeRef];
|
||
|
|
for (GLTFNodeRef meshId : node.meshes) {
|
||
|
|
const GLTFMesh& mesh = gltf.meshesStorage[meshId];
|
||
|
|
gltf.transforms.push_back({
|
||
|
|
.modelMtxId = node.modelMtxId,
|
||
|
|
.matId = mesh.matIdx,
|
||
|
|
.nodeRef = nodeRef,
|
||
|
|
.meshRef = meshId,
|
||
|
|
.sortingType = mesh.sortingType,
|
||
|
|
});
|
||
|
|
if (mesh.sortingType == SortingType_Transparent) {
|
||
|
|
gltf.transparentNodes.push_back(gltf.transforms.size() - 1);
|
||
|
|
} else if (mesh.sortingType == SortingType_Transmission) {
|
||
|
|
gltf.transmissionNodes.push_back(gltf.transforms.size() - 1);
|
||
|
|
} else {
|
||
|
|
gltf.opaqueNodes.push_back(gltf.transforms.size() - 1);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
for (GLTFNodeRef child : node.children) {
|
||
|
|
traverseTree(child);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
traverseTree(gltf.root);
|
||
|
|
|
||
|
|
gltf.transformBuffer = gltf.app.ctx_->createBuffer({
|
||
|
|
.usage = lvk::BufferUsageBits_Storage,
|
||
|
|
.storage = lvk::StorageType_HostVisible,
|
||
|
|
.size = gltf.transforms.size() * sizeof(GLTFTransforms),
|
||
|
|
.data = gltf.transforms.data(),
|
||
|
|
.debugName = "Per Frame data",
|
||
|
|
});
|
||
|
|
|
||
|
|
gltf.matricesBuffer = gltf.app.ctx_->createBuffer({
|
||
|
|
.usage = lvk::BufferUsageBits_Storage,
|
||
|
|
.storage = lvk::StorageType_HostVisible,
|
||
|
|
.size = gltf.matrices.size() * sizeof(mat4),
|
||
|
|
.data = gltf.matrices.data(),
|
||
|
|
.debugName = "Node matrices",
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
void sortTransparentNodes(GLTFContext& gltf, const vec3& cameraPos)
|
||
|
|
{
|
||
|
|
// glTF spec expects to sort based on pivot positions (not sure correct way though)
|
||
|
|
std::sort(gltf.transparentNodes.begin(), gltf.transparentNodes.end(), [&](uint32_t a, uint32_t b) {
|
||
|
|
float sqrDistA = glm::length2(cameraPos - vec3(gltf.matrices[gltf.transforms[a].modelMtxId][3]));
|
||
|
|
float sqrDistB = glm::length2(cameraPos - vec3(gltf.matrices[gltf.transforms[b].modelMtxId][3]));
|
||
|
|
return sqrDistA < sqrDistB;
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
void renderGLTF(GLTFContext& gltf, const mat4& model, const mat4& view, const mat4& proj, bool rebuildRenderList)
|
||
|
|
{
|
||
|
|
auto& ctx = gltf.app.ctx_;
|
||
|
|
|
||
|
|
const vec4 camPos = glm::inverse(view)[3];
|
||
|
|
|
||
|
|
gltf.inspector.animations = animationsGLTF(gltf);
|
||
|
|
gltf.inspector.cameras = camerasGLTF(gltf);
|
||
|
|
|
||
|
|
if (rebuildRenderList || gltf.transforms.empty()) {
|
||
|
|
buildTransformsList(gltf);
|
||
|
|
}
|
||
|
|
|
||
|
|
sortTransparentNodes(gltf, camPos);
|
||
|
|
|
||
|
|
gltf.frameData = {
|
||
|
|
.model = model,
|
||
|
|
.view = view,
|
||
|
|
.proj = proj,
|
||
|
|
.cameraPos = camPos,
|
||
|
|
};
|
||
|
|
|
||
|
|
struct PushConstants {
|
||
|
|
uint64_t draw;
|
||
|
|
uint64_t materials;
|
||
|
|
uint64_t environments;
|
||
|
|
uint64_t lights;
|
||
|
|
uint64_t transforms;
|
||
|
|
uint64_t matrices;
|
||
|
|
uint32_t envId;
|
||
|
|
uint32_t transmissionFramebuffer;
|
||
|
|
uint32_t transmissionFramebufferSampler;
|
||
|
|
uint32_t lightsCount;
|
||
|
|
} pushConstants = {
|
||
|
|
.draw = ctx->gpuAddress(gltf.perFrameBuffer),
|
||
|
|
.materials = ctx->gpuAddress(gltf.matBuffer),
|
||
|
|
.environments = ctx->gpuAddress(gltf.envBuffer),
|
||
|
|
.lights = ctx->gpuAddress(gltf.lightsBuffer),
|
||
|
|
.transforms = ctx->gpuAddress(gltf.transformBuffer),
|
||
|
|
.matrices = ctx->gpuAddress(gltf.matricesBuffer),
|
||
|
|
.envId = 0,
|
||
|
|
.transmissionFramebuffer = 0,
|
||
|
|
.transmissionFramebufferSampler = gltf.samplers.clamp.index(),
|
||
|
|
.lightsCount = (uint32_t)gltf.lights.size(),
|
||
|
|
};
|
||
|
|
|
||
|
|
lvk::ICommandBuffer& buf = ctx->acquireCommandBuffer();
|
||
|
|
|
||
|
|
buf.cmdUpdateBuffer(gltf.perFrameBuffer, gltf.frameData);
|
||
|
|
|
||
|
|
const bool isSizeChanged = ctx->getDimensions(ctx->getCurrentSwapchainTexture()) != ctx->getDimensions(gltf.offscreenTex[0]);
|
||
|
|
|
||
|
|
if (gltf.offscreenTex[0].empty() || isSizeChanged) {
|
||
|
|
const lvk::Dimensions res = ctx->getDimensions(ctx->getCurrentSwapchainTexture());
|
||
|
|
|
||
|
|
for (lvk::Holder<lvk::TextureHandle>& holder : gltf.offscreenTex) {
|
||
|
|
holder = ctx->createTexture({
|
||
|
|
.type = lvk::TextureType_2D,
|
||
|
|
.format = ctx->getSwapchainFormat(),
|
||
|
|
.dimensions = {res.width, res.height},
|
||
|
|
.usage = lvk::TextureUsageBits_Attachment | lvk::TextureUsageBits_Sampled,
|
||
|
|
.numMipLevels = lvk::calcNumMipLevels(res.width, res.height),
|
||
|
|
.debugName = "offscreenTex",
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
auto drawUI = [&](lvk::ICommandBuffer& buf, const lvk::Framebuffer& framebuffer) {
|
||
|
|
if (gltf.inspector.activeCamera == ~0u)
|
||
|
|
gltf.app.drawGrid(buf, proj, vec3(0, -1.0f, 0));
|
||
|
|
else
|
||
|
|
gltf.app.drawGrid(buf, proj * view, vec3(0, -1.0f, 0), camPos);
|
||
|
|
|
||
|
|
gltf.app.imgui_->beginFrame(framebuffer);
|
||
|
|
gltf.app.drawFPS();
|
||
|
|
gltf.app.drawMemo();
|
||
|
|
|
||
|
|
gltf.app.drawGTFInspector(gltf.inspector);
|
||
|
|
|
||
|
|
bool uploadMat = false;
|
||
|
|
for (uint32_t m = 0; m != gltf.inspector.materials.size(); m++) {
|
||
|
|
if (gltf.inspector.materials[m].modified) {
|
||
|
|
gltf.inspector.materials[m].modified = false;
|
||
|
|
uploadMat = true;
|
||
|
|
gltf.matPerFrame.materials[m].materialTypeFlags = gltf.inspector.materials[m].currentMaterialMask;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (uploadMat) {
|
||
|
|
ctx->upload(gltf.matBuffer, &gltf.matPerFrame, sizeof(gltf.matPerFrame));
|
||
|
|
}
|
||
|
|
|
||
|
|
if (gltf.inspector.activeCamera >= gltf.cameras.size() && !gltf.cameras.empty()) {
|
||
|
|
gltf.canvas3d.clear();
|
||
|
|
gltf.canvas3d.setMatrix(proj * view);
|
||
|
|
const float windowAspect = ctx->getAspectRatio(framebuffer.color[0].texture);
|
||
|
|
for (const auto& c : gltf.cameras) {
|
||
|
|
if (c.nodeIdx == ~0u)
|
||
|
|
continue;
|
||
|
|
const mat4 camView = glm::inverse(model * gltf.matrices[c.nodeIdx]);
|
||
|
|
const mat4 camProj = c.getProjection(windowAspect);
|
||
|
|
gltf.canvas3d.frustum(camView, camProj, vec4(1, 0, 0, 1));
|
||
|
|
gltf.canvas3d.render(*ctx.get(), framebuffer, buf);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
gltf.app.imgui_->endFrame(buf);
|
||
|
|
};
|
||
|
|
|
||
|
|
if (gltf.animated) {
|
||
|
|
buf.cmdUpdateBuffer(gltf.matricesBuffer, 0, gltf.matrices.size() * sizeof(mat4), gltf.matrices.data());
|
||
|
|
if (gltf.morphing) {
|
||
|
|
buf.cmdUpdateBuffer(gltf.morphStatesBuffer, 0, gltf.morphStates.size() * sizeof(MorphState), gltf.morphStates.data());
|
||
|
|
}
|
||
|
|
updateLights(gltf, buf);
|
||
|
|
|
||
|
|
if ((gltf.skinning && gltf.hasBones) || gltf.morphing) {
|
||
|
|
// Run compute shader to do skinning and morphing
|
||
|
|
|
||
|
|
struct ComputeSetup {
|
||
|
|
uint64_t matrices;
|
||
|
|
uint64_t morphStates;
|
||
|
|
uint64_t morphVertexBuffer;
|
||
|
|
uint64_t inBuffer;
|
||
|
|
uint64_t outBuffer;
|
||
|
|
uint32_t numMorphStates;
|
||
|
|
} pc = {
|
||
|
|
.matrices = ctx->gpuAddress(gltf.matricesBuffer),
|
||
|
|
.morphStates = ctx->gpuAddress(gltf.morphStatesBuffer),
|
||
|
|
.morphVertexBuffer = ctx->gpuAddress(gltf.vertexMorphingBuffer),
|
||
|
|
.inBuffer = ctx->gpuAddress(gltf.vertexSkinningBuffer),
|
||
|
|
.outBuffer = ctx->gpuAddress(gltf.vertexBuffer),
|
||
|
|
.numMorphStates = static_cast<uint32_t>(gltf.morphStates.size()),
|
||
|
|
};
|
||
|
|
buf.cmdBindComputePipeline(gltf.pipelineComputeAnimations);
|
||
|
|
buf.cmdPushConstants(pc);
|
||
|
|
// clang-format off
|
||
|
|
buf.cmdDispatchThreadGroups(
|
||
|
|
{ .width = gltf.maxVertices / 16 },
|
||
|
|
{ .buffers = { lvk::BufferHandle(gltf.vertexBuffer),
|
||
|
|
lvk::BufferHandle(gltf.morphStatesBuffer),
|
||
|
|
lvk::BufferHandle(gltf.matricesBuffer),
|
||
|
|
lvk::BufferHandle(gltf.vertexSkinningBuffer) } });
|
||
|
|
// clang-format on
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
const bool screenCopy = gltf.isScreenCopyRequired();
|
||
|
|
|
||
|
|
{
|
||
|
|
// 1st pass
|
||
|
|
pushConstants.transmissionFramebuffer = 0;
|
||
|
|
|
||
|
|
const lvk::RenderPass renderPass = {
|
||
|
|
.color = { { .loadOp = lvk::LoadOp_Clear, .clearColor = { 1.0f, 1.0f, 1.0f, 1.0f } } },
|
||
|
|
.depth = { .loadOp = lvk::LoadOp_Clear, .clearDepth = 1.0f },
|
||
|
|
};
|
||
|
|
|
||
|
|
const lvk::Framebuffer framebuffer = {
|
||
|
|
.color = { { .texture = screenCopy ? gltf.offscreenTex[gltf.currentOffscreenTex] : ctx->getCurrentSwapchainTexture() } },
|
||
|
|
.depthStencil = { .texture = gltf.app.getDepthTexture() },
|
||
|
|
};
|
||
|
|
|
||
|
|
{
|
||
|
|
buf.cmdBeginRendering(renderPass, framebuffer, { .buffers = { { lvk::BufferHandle(gltf.vertexBuffer) } } });
|
||
|
|
buf.cmdBindVertexBuffer(0, gltf.vertexBuffer, 0);
|
||
|
|
buf.cmdBindIndexBuffer(gltf.indexBuffer, lvk::IndexFormat_UI32);
|
||
|
|
|
||
|
|
buf.cmdBindDepthState({ .compareOp = lvk::CompareOp_Less, .isDepthWriteEnabled = true });
|
||
|
|
|
||
|
|
buf.cmdBindRenderPipeline(gltf.pipelineSolid);
|
||
|
|
buf.cmdPushConstants(pushConstants);
|
||
|
|
for (uint32_t transformId : gltf.opaqueNodes) {
|
||
|
|
const GLTFTransforms transform = gltf.transforms[transformId];
|
||
|
|
|
||
|
|
buf.cmdPushDebugGroupLabel(gltf.nodesStorage[transform.nodeRef].name.c_str(), 0xff0000ff);
|
||
|
|
const GLTFMesh submesh = gltf.meshesStorage[transform.meshRef];
|
||
|
|
buf.cmdDrawIndexed(submesh.indexCount, 1, submesh.indexOffset, submesh.vertexOffset, transformId);
|
||
|
|
buf.cmdPopDebugGroupLabel();
|
||
|
|
}
|
||
|
|
if (!screenCopy) {
|
||
|
|
drawUI(buf, framebuffer);
|
||
|
|
}
|
||
|
|
buf.cmdEndRendering();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!gltf.transmissionNodes.empty() || !gltf.transparentNodes.empty()) {
|
||
|
|
// 2nd pass
|
||
|
|
const lvk::RenderPass renderPass = {
|
||
|
|
.color = { { .loadOp = lvk::LoadOp_Load } },
|
||
|
|
.depth = { .loadOp = lvk::LoadOp_Load },
|
||
|
|
};
|
||
|
|
|
||
|
|
const lvk::Framebuffer framebuffer = {
|
||
|
|
.color = { { .texture = ctx->getCurrentSwapchainTexture() } },
|
||
|
|
.depthStencil = { .texture = gltf.app.getDepthTexture() },
|
||
|
|
};
|
||
|
|
|
||
|
|
if (screenCopy) {
|
||
|
|
buf.cmdCopyImage(
|
||
|
|
gltf.offscreenTex[gltf.currentOffscreenTex], ctx->getCurrentSwapchainTexture(),
|
||
|
|
ctx->getDimensions(ctx->getCurrentSwapchainTexture()));
|
||
|
|
buf.cmdGenerateMipmap(gltf.offscreenTex[gltf.currentOffscreenTex]);
|
||
|
|
|
||
|
|
pushConstants.transmissionFramebuffer = gltf.offscreenTex[gltf.currentOffscreenTex].index();
|
||
|
|
buf.cmdPushConstants(pushConstants);
|
||
|
|
}
|
||
|
|
|
||
|
|
buf.cmdBeginRendering(renderPass, framebuffer, { .textures = { lvk::TextureHandle(gltf.offscreenTex[gltf.currentOffscreenTex]) } });
|
||
|
|
buf.cmdBindVertexBuffer(0, gltf.vertexBuffer, 0);
|
||
|
|
buf.cmdBindIndexBuffer(gltf.indexBuffer, lvk::IndexFormat_UI32);
|
||
|
|
|
||
|
|
buf.cmdBindDepthState({ .compareOp = lvk::CompareOp_Less, .isDepthWriteEnabled = true });
|
||
|
|
|
||
|
|
// Volumetric opaque
|
||
|
|
buf.cmdBindRenderPipeline(gltf.pipelineSolid);
|
||
|
|
buf.cmdPushConstants(pushConstants);
|
||
|
|
for (uint32_t transformId : gltf.transmissionNodes) {
|
||
|
|
const GLTFTransforms transform = gltf.transforms[transformId];
|
||
|
|
buf.cmdPushDebugGroupLabel(gltf.nodesStorage[transform.nodeRef].name.c_str(), 0x00FF00ff);
|
||
|
|
const GLTFMesh submesh = gltf.meshesStorage[transform.meshRef];
|
||
|
|
buf.cmdDrawIndexed(submesh.indexCount, 1, submesh.indexOffset, submesh.vertexOffset, transformId);
|
||
|
|
buf.cmdPopDebugGroupLabel();
|
||
|
|
}
|
||
|
|
|
||
|
|
//
|
||
|
|
buf.cmdBindRenderPipeline(gltf.pipelineTransparent);
|
||
|
|
buf.cmdPushConstants(pushConstants);
|
||
|
|
for (uint32_t transformId : gltf.transparentNodes) {
|
||
|
|
const GLTFTransforms transform = gltf.transforms[transformId];
|
||
|
|
buf.cmdPushDebugGroupLabel(gltf.nodesStorage[transform.nodeRef].name.c_str(), 0x00FF00ff);
|
||
|
|
const GLTFMesh submesh = gltf.meshesStorage[transform.meshRef];
|
||
|
|
buf.cmdDrawIndexed(submesh.indexCount, 1, submesh.indexOffset, submesh.vertexOffset, transformId);
|
||
|
|
buf.cmdPopDebugGroupLabel();
|
||
|
|
}
|
||
|
|
|
||
|
|
drawUI(buf, framebuffer);
|
||
|
|
|
||
|
|
buf.cmdEndRendering();
|
||
|
|
}
|
||
|
|
|
||
|
|
ctx->wait(ctx->submit(buf, ctx->getCurrentSwapchainTexture()));
|
||
|
|
|
||
|
|
gltf.currentOffscreenTex = (gltf.currentOffscreenTex + 1) % LVK_ARRAY_NUM_ELEMENTS(gltf.offscreenTex);
|
||
|
|
}
|
||
|
|
|
||
|
|
MaterialType detectMaterialType(const aiMaterial* mtl)
|
||
|
|
{
|
||
|
|
aiShadingMode shadingMode = aiShadingMode_NoShading;
|
||
|
|
|
||
|
|
if (mtl->Get(AI_MATKEY_SHADING_MODEL, shadingMode) == AI_SUCCESS) {
|
||
|
|
if (shadingMode == aiShadingMode_Unlit) {
|
||
|
|
return MaterialType_Unlit;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (shadingMode == aiShadingMode_PBR_BRDF) {
|
||
|
|
ai_real factor = 0;
|
||
|
|
if (mtl->Get(AI_MATKEY_GLOSSINESS_FACTOR, factor) == AI_SUCCESS) {
|
||
|
|
return MaterialType_SpecularGlossiness;
|
||
|
|
} else if (mtl->Get(AI_MATKEY_METALLIC_FACTOR, factor) == AI_SUCCESS) {
|
||
|
|
return MaterialType_MetallicRoughness;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
LLOGW("Unknown material type\n");
|
||
|
|
|
||
|
|
return MaterialType_Invalid;
|
||
|
|
}
|
||
|
|
|
||
|
|
void printPrefix(int ofs)
|
||
|
|
{
|
||
|
|
for (int i = 0; i < ofs; i++)
|
||
|
|
printf("\t");
|
||
|
|
}
|
||
|
|
|
||
|
|
void printMat4(const aiMatrix4x4& m)
|
||
|
|
{
|
||
|
|
if (!m.IsIdentity()) {
|
||
|
|
for (int i = 0; i < 4; i++)
|
||
|
|
for (int j = 0; j < 4; j++)
|
||
|
|
printf("%f ;", m[i][j]);
|
||
|
|
} else {
|
||
|
|
printf(" Identity");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void animateGLTF(GLTFContext& gltf, AnimationState& anim, float dt)
|
||
|
|
{
|
||
|
|
if (gltf.transforms.empty())
|
||
|
|
return;
|
||
|
|
|
||
|
|
if (gltf.pipelineComputeAnimations.empty()) {
|
||
|
|
gltf.pipelineComputeAnimations = gltf.app.ctx_->createComputePipeline({
|
||
|
|
.smComp = gltf.animation,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
// we support only one single animation at this time
|
||
|
|
anim.active = anim.animId != ~0;
|
||
|
|
gltf.animated = anim.active;
|
||
|
|
if (anim.active) {
|
||
|
|
updateAnimation(gltf, anim, dt);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void animateBlendingGLTF(GLTFContext& gltf, AnimationState& anim1, AnimationState& anim2, float weight, float dt)
|
||
|
|
{
|
||
|
|
if (gltf.transforms.empty())
|
||
|
|
return;
|
||
|
|
|
||
|
|
if (gltf.pipelineComputeAnimations.empty()) {
|
||
|
|
gltf.pipelineComputeAnimations = gltf.app.ctx_->createComputePipeline({
|
||
|
|
.smComp = gltf.animation,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
anim1.active = anim1.animId != ~0;
|
||
|
|
anim2.active = anim2.animId != ~0;
|
||
|
|
gltf.animated = anim1.active || anim2.active;
|
||
|
|
if (anim1.active && anim2.active) {
|
||
|
|
updateAnimationBlending(gltf, anim1, anim2, weight, dt);
|
||
|
|
} else if (anim1.active) {
|
||
|
|
updateAnimation(gltf, anim1, dt);
|
||
|
|
} else if (anim2.active) {
|
||
|
|
updateAnimation(gltf, anim2, dt);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
std::vector<std::string> camerasGLTF(GLTFContext& gltf)
|
||
|
|
{
|
||
|
|
std::vector<std::string> names;
|
||
|
|
names.reserve(gltf.cameras.size() + 1);
|
||
|
|
|
||
|
|
for (auto c : gltf.cameras) {
|
||
|
|
names.push_back(c.name);
|
||
|
|
}
|
||
|
|
names.push_back("Application cam");
|
||
|
|
|
||
|
|
return names;
|
||
|
|
}
|
||
|
|
|
||
|
|
std::vector<std::string> animationsGLTF(GLTFContext& gltf)
|
||
|
|
{
|
||
|
|
std::vector<std::string> names;
|
||
|
|
names.reserve(gltf.animations.size());
|
||
|
|
|
||
|
|
for (auto c : gltf.animations) {
|
||
|
|
names.push_back(c.name);
|
||
|
|
}
|
||
|
|
|
||
|
|
return names;
|
||
|
|
}
|