engine/ext/behaviors/baking/behavior.cpp

333 lines
13 KiB
C++

#include "behavior.h"
#include <uf/utils/renderer/renderer.h>
#include <uf/utils/math/transform.h>
#include <uf/utils/math/physics.h>
#include <uf/utils/camera/camera.h>
#include <uf/ext/gltf/gltf.h>
#include <uf/engine/asset/asset.h>
#include <uf/ext/xatlas/xatlas.h>
#include "../light/behavior.h"
UF_BEHAVIOR_REGISTER_CPP(ext::BakingBehavior)
#define this (&self)
void ext::BakingBehavior::initialize( uf::Object& self ) {
#if UF_USE_VULKAN
this->addHook( "entity:PostInitialization.%UID%", [&](){
auto& metadataJson = this->getComponent<uf::Serializer>();
auto& metadata = this->getComponent<ext::BakingBehavior::Metadata>();
metadata.names.model = this->grabURI( metadataJson["baking"]["model"].as<std::string>(), metadataJson["system"]["root"].as<std::string>() );
if ( metadataJson["baking"]["output"]["model"].is<std::string>() ) metadata.names.output.model = this->grabURI( metadataJson["baking"]["output"]["model"].as<std::string>(), metadataJson["system"]["root"].as<std::string>() );
metadata.names.output.map = this->grabURI( metadataJson["baking"]["output"]["map"].as<std::string>(), metadataJson["system"]["root"].as<std::string>() );
metadata.names.renderMode = "B:" + std::to_string((int) this->getUid());
metadata.trigger.mode = metadataJson["baking"]["trigger"]["mode"].as<std::string>();
metadata.trigger.value = metadataJson["baking"]["trigger"]["value"].as<std::string>();
if ( metadataJson["baking"]["resolution"].is<size_t>() ) metadata.size = { metadataJson["baking"]["resolution"].as<size_t>(), metadataJson["baking"]["resolution"].as<size_t>() };
metadata.shadows = metadataJson["baking"]["shadows"].as<bool>();
UF_DEBUG_MSG("Unwrapping...");
auto& graph = this->getComponent<pod::Graph>();
graph = ext::gltf::load( metadata.names.model, uf::graph::LoadMode::ATLAS );
auto size = ext::xatlas::unwrap( graph );
if ( size.x == 0 && size.y == 0 ) return;
if ( metadata.size.x == 0 && metadata.size.y == 0 ) metadata.size = size;
if ( metadata.names.output.model != "" ) {
UF_DEBUG_MSG("Saving to " << metadata.names.output.model);
uf::graph::save( graph, metadata.names.output.model );
}
auto& graphic = this->getComponent<uf::Graphic>();
graphic.process = false;
graphic.descriptor.cullMode = uf::renderer::enums::CullMode::NONE;
if ( graph.atlas.generated() ) {
for ( auto& texture : graph.textures ) {
++texture.storage.index;
}
auto& image = *graph.images.emplace(graph.images.begin(), graph.atlas.getAtlas());
auto& texture = *graph.textures.emplace(graph.textures.begin());
texture.name = "atlas";
texture.bind = true;
texture.storage.index = 0;
texture.storage.sampler = 0;
texture.storage.remap = 0;
texture.storage.blend = 0;
if ( graph.metadata["filter"].is<std::string>() ) {
std::string filter = graph.metadata["filter"].as<std::string>();
if ( filter == "NEAREST" ) {
texture.texture.sampler.descriptor.filter.min = uf::renderer::enums::Filter::NEAREST;
texture.texture.sampler.descriptor.filter.mag = uf::renderer::enums::Filter::NEAREST;
} else if ( filter == "LINEAR" ) {
texture.texture.sampler.descriptor.filter.min = uf::renderer::enums::Filter::LINEAR;
texture.texture.sampler.descriptor.filter.mag = uf::renderer::enums::Filter::LINEAR;
}
}
for ( auto& material : graph.materials ) {
if ( 0 <= material.storage.indexAlbedo ) ++material.storage.indexAlbedo;
if ( 0 <= material.storage.indexNormal ) ++material.storage.indexNormal;
if ( 0 <= material.storage.indexEmissive ) ++material.storage.indexEmissive;
if ( 0 <= material.storage.indexOcclusion ) ++material.storage.indexOcclusion;
if ( 0 <= material.storage.indexMetallicRoughness ) ++material.storage.indexMetallicRoughness;
material.storage.indexAtlas = texture.storage.index;
if ( 0 <= material.storage.indexLightmap ) ++material.storage.indexLightmap;
}
texture.texture.loadFromImage( image );
} else {
for ( size_t i = 0; i < graph.textures.size() && i < graph.images.size(); ++i ) {
auto& image = graph.images[i];
auto& texture = graph.textures[i];
texture.bind = true;
texture.storage.index = i;
if ( graph.metadata["filter"].is<std::string>() ) {
std::string filter = graph.metadata["filter"].as<std::string>();
if ( filter == "NEAREST" ) {
texture.texture.sampler.descriptor.filter.min = uf::renderer::enums::Filter::NEAREST;
texture.texture.sampler.descriptor.filter.mag = uf::renderer::enums::Filter::NEAREST;
} else if ( filter == "LINEAR" ) {
texture.texture.sampler.descriptor.filter.min = uf::renderer::enums::Filter::LINEAR;
texture.texture.sampler.descriptor.filter.mag = uf::renderer::enums::Filter::LINEAR;
}
}
texture.texture.loadFromImage( image );
}
}
{
size_t textureSlot = 0;
for ( auto& texture : graph.textures ) {
texture.storage.index = -1;
if ( !texture.bind ) continue;
texture.storage.index = textureSlot++;
}
}
for ( auto& texture : graph.textures ) {
if ( !texture.bind ) continue;
graphic.material.textures.emplace_back().aliasTexture(texture.texture);
}
auto& mesh = this->getComponent<uf::graph::mesh_t>();
for ( auto& m : graph.meshes ) mesh.insert(m);
auto& renderMode = this->getComponent<uf::renderer::RenderTargetRenderMode>();
uf::renderer::addRenderMode( &renderMode, metadata.names.renderMode );
renderMode.metadata["type"] = "single";
renderMode.metadata["samples"] = 1;
renderMode.width = metadata.size.x;
renderMode.height = metadata.size.y;
renderMode.blitter.process = false;
graphic.device = &ext::vulkan::device;
graphic.material.device = graphic.device;
std::vector<pod::Matrix4f> instances;
instances.reserve( graph.nodes.size() );
for ( auto& node : graph.nodes ) {
instances.emplace_back( uf::transform::model( node.transform ) );
}
// Models storage buffer
graphic.initializeBuffer(
(void*) instances.data(),
instances.size() * sizeof(pod::Matrix4f),
uf::renderer::enums::Buffer::STORAGE
);
// Materials storage buffer
std::vector<pod::Material::Storage> materials( graph.materials.size() );
for ( size_t i = 0; i < graph.materials.size(); ++i ) {
materials[i] = graph.materials[i].storage;
}
graphic.initializeBuffer(
(void*) materials.data(),
materials.size() * sizeof(pod::Material::Storage),
uf::renderer::enums::Buffer::STORAGE
);
// Texture storage buffer
std::vector<pod::Texture::Storage> textures( graph.textures.size() );
for ( size_t i = 0; i < graph.textures.size(); ++i ) {
textures[i] = graph.textures[i].storage;
}
graphic.initializeBuffer(
(void*) textures.data(),
textures.size() * sizeof(pod::Texture::Storage),
uf::renderer::enums::Buffer::STORAGE
);
UF_DEBUG_MSG("Unwrapped model");
});
this->queueHook( "entity:PostInitialization.%UID%", ext::json::null(), 2.0f );
#endif
}
void ext::BakingBehavior::tick( uf::Object& self ) {
#if 0
#if UF_USE_VULKAN
if ( !this->hasComponent<uf::renderer::RenderTargetRenderMode>() ) return;
auto& metadata = this->getComponent<ext::BakingBehavior::Metadata>();
auto& renderMode = this->getComponent<uf::renderer::RenderTargetRenderMode>();
if ( renderMode.executed && !metadata.initialized.renderMode ) {
UF_DEBUG_MSG("Preparing graphics to bake...");
auto& graph = this->getComponent<pod::Graph>();
auto& mesh = this->getComponent<uf::graph::mesh_t>();
auto& renderMode = this->getComponent<uf::renderer::RenderTargetRenderMode>();
auto& graphic = this->getComponent<uf::Graphic>();
auto& metadata = this->getComponent<ext::BakingBehavior::Metadata>();
graphic.initialize( metadata.names.renderMode );
graphic.initializeMesh( mesh );
// Light storage buffer
std::vector<pod::Light::Storage> lights;
lights.reserve(1024);
if ( metadata.shadows ) {
auto& scene = uf::scene::getCurrentScene();
auto& graph = scene.getGraph();
auto& controller = scene.getController();
struct LightInfo {
uf::Entity* entity = NULL;
pod::Vector3f color = {0,0,0,0};
pod::Vector3f position = {0,0,0};
float power = 0;
float bias = 0;
int32_t type = 0;
bool shadows = false;
};
std::vector<LightInfo> infos;
for ( auto entity : graph ) {
if ( entity == &controller || entity == this ) continue;
if ( entity->getName() != "Light" && !entity->hasComponent<ext::LightBehavior::Metadata>() ) continue;
auto& metadata = entity->getComponent<ext::LightBehavior::Metadata>();
if ( metadata.power <= 0 ) continue;
auto& transform = entity->getComponent<pod::Transform<>>();
auto flatten = uf::transform::flatten( transform );
LightInfo& info = infos.emplace_back(LightInfo{
.entity = entity,
.color = metadata.color,
.position = flatten.position,
.power = metadata.power,
.bias = metadata.bias,
.type = metadata.type,
.shadows = metadata.shadows,
});
}
size_t textureSlot = graphic.material.textures.size();
for ( auto& info : infos ) {
uf::Entity* entity = info.entity;
auto& transform = entity->getComponent<pod::Transform<>>();
auto& metadata = entity->getComponent<ext::LightBehavior::Metadata>();
auto& camera = entity->getComponent<uf::Camera>();
pod::Light::Storage light;
light.position = info.position;
light.color = info.color;
light.type = info.type;
light.indexMap = -1;
light.depthBias = info.bias;
if ( info.shadows && entity->hasComponent<uf::renderer::RenderTargetRenderMode>() ) {
auto& renderMode = entity->getComponent<uf::renderer::RenderTargetRenderMode>();
size_t view = 0;
for ( auto& attachment : renderMode.renderTarget.attachments ) {
if ( !(attachment.descriptor.usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) ) continue;
if ( attachment.descriptor.layout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR ) continue;
graphic.material.textures.emplace_back().aliasAttachment(attachment);
light.indexMap = textureSlot++;
light.view = camera.getView(view);
light.projection = camera.getProjection(view);
lights.emplace_back(light);
++view;
}
light.indexMap = -1;
} else {
lights.emplace_back(light);
}
}
} else {
for ( auto& l : graph.lights ) {
for ( auto& node : graph.nodes ) {
if ( l.name != node.name ) continue;
auto transform = uf::transform::flatten( node.transform );
auto& light = lights.emplace_back();
light.position = transform.position;
light.color = l.color;
light.color.w = l.intensity;
light.position.w = l.range;
break;
}
}
}
graphic.initializeBuffer(
(void*) lights.data(),
lights.size() * sizeof(pod::Light::Storage),
uf::renderer::enums::Buffer::STORAGE
);
graphic.material.attachShader(uf::io::root+"/shaders/gltf/baking/bake.vert.spv", uf::renderer::enums::Shader::VERTEX);
graphic.material.attachShader(uf::io::root+"/shaders/gltf/baking/bake.frag.spv", uf::renderer::enums::Shader::FRAGMENT);
{
auto& shader = graphic.material.getShader("vertex");
struct SpecializationConstant {
uint32_t passes = 6;
};
auto& specializationConstants = shader.specializationConstants.get<SpecializationConstant>();
specializationConstants.passes = uf::renderer::settings::maxViews;
}
{
auto& shader = graphic.material.getShader("fragment");
struct SpecializationConstant {
uint32_t textures = 1;
};
auto& specializationConstants = shader.specializationConstants.get<SpecializationConstant>();
specializationConstants.textures = graphic.material.textures.size();
for ( auto& binding : shader.descriptorSetLayoutBindings ) {
if ( binding.descriptorCount > 1 )
binding.descriptorCount = specializationConstants.textures;
}
}
graphic.process = true;
metadata.initialized.renderMode = true;
UF_DEBUG_MSG("Graphic configured, ready to bake");
} else if ( renderMode.executed && !metadata.initialized.map ) {
TIMER(1.0, (metadata.trigger.mode == "rendered" || (metadata.trigger.mode == "key" && uf::Window::isKeyPressed(metadata.trigger.value))) && ) {
goto SAVE;
}
}
return;
SAVE:
renderMode.execute = false;
UF_DEBUG_MSG("Baking...");
auto image = renderMode.screenshot();
std::string filename = metadata.names.output.map;
bool status = image.save(filename);
UF_DEBUG_MSG("Writing to " << filename << ": " << status);
UF_DEBUG_MSG("Baked.");
metadata.initialized.map = true;
uf::Serializer payload;
payload["uid"] = this->getUid();
uf::scene::getCurrentScene().queueHook("system:Destroy", payload);
#endif
#endif
}
void ext::BakingBehavior::render( uf::Object& self ){}
void ext::BakingBehavior::destroy( uf::Object& self ){
if ( this->hasComponent<uf::renderer::RenderTargetRenderMode>() ) {
auto& renderMode = this->getComponent<uf::renderer::RenderTargetRenderMode>();
uf::renderer::removeRenderMode( &renderMode, false );
this->deleteComponent<uf::renderer::RenderTargetRenderMode>();
}
if ( this->hasComponent<pod::Graph>() ) {
auto& graph = this->getComponent<pod::Graph>();
uf::graph::destroy( graph );
}
}
#undef this