Add billboard rendering in vulkan

This commit is contained in:
Benau 2022-09-14 16:19:38 +08:00
parent b1fa45437f
commit 787d157933
11 changed files with 243 additions and 24 deletions

View File

@ -74,6 +74,7 @@ set(GE_SOURCES
src/ge_vulkan_2d_renderer.cpp
src/ge_vulkan_array_texture.cpp
src/ge_vulkan_animated_mesh_scene_node.cpp
src/ge_vulkan_billboard_buffer.cpp
src/ge_vulkan_camera_scene_node.cpp
src/ge_vulkan_command_loader.cpp
src/ge_vulkan_depth_texture.cpp

View File

@ -13,9 +13,10 @@ namespace GE
{
class GESPMBuffer : public irr::scene::IMeshBuffer
{
private:
protected:
irr::video::SMaterial m_material;
private:
std::vector<irr::video::S3DVertexSkinnedMesh> m_vertices;
std::vector<irr::u16> m_indices;
@ -163,11 +164,11 @@ public:
// ------------------------------------------------------------------------
void setVBOOffset(size_t offset) { m_vbo_offset = offset; }
// ------------------------------------------------------------------------
size_t getVBOOffset() const { return m_vbo_offset; }
virtual size_t getVBOOffset() const { return m_vbo_offset; }
// ------------------------------------------------------------------------
void setIBOOffset(size_t offset) { m_ibo_offset = offset; }
// ------------------------------------------------------------------------
size_t getIBOOffset() const { return m_ibo_offset; }
virtual size_t getIBOOffset() const { return m_ibo_offset; }
// ------------------------------------------------------------------------
bool hasSkinning() const { return m_has_skinning; }
// ------------------------------------------------------------------------
@ -175,10 +176,11 @@ public:
// ------------------------------------------------------------------------
void bindVertexIndexBuffer(VkCommandBuffer cmd)
{
VkBuffer buffer = getVkBuffer();
std::array<VkBuffer, 2> vertex_buffer =
{{
m_buffer,
m_buffer
buffer,
buffer
}};
std::array<VkDeviceSize, 2> offsets =
{{
@ -187,7 +189,7 @@ public:
}};
vkCmdBindVertexBuffers(cmd, 0, vertex_buffer.size(),
vertex_buffer.data(), offsets.data());
vkCmdBindIndexBuffer(cmd, m_buffer, m_ibo_offset,
vkCmdBindIndexBuffer(cmd, buffer, getIBOOffset(),
VK_INDEX_TYPE_UINT16);
}
// ------------------------------------------------------------------------
@ -199,9 +201,11 @@ public:
{ return m_vertices; }
// ------------------------------------------------------------------------
std::vector<irr::u16>& getIndicesVector() { return m_indices; }
// ------------------------------------------------------------------------
virtual VkBuffer getVkBuffer() const { return m_buffer; }
};
} // end namespace irr
} // end namespace GE
#endif

View File

@ -23,6 +23,7 @@ using namespace video;
namespace GE
{
class GESPM;
class GEVulkanDepthTexture;
class GEVulkanDrawCall;
class GEVulkanFBOTexture;
@ -363,6 +364,7 @@ namespace GE
void clearDrawCallsCache();
void addDrawCallToCache(std::unique_ptr<GEVulkanDrawCall>& dc);
std::unique_ptr<GEVulkanDrawCall> getDrawCallFromCache();
GESPM* getBillboardQuad() const { return m_billboard_quad; }
private:
struct SwapChainSupportDetails
{
@ -506,6 +508,7 @@ namespace GE
u32 m_rtt_polycount;
std::vector<std::unique_ptr<GEVulkanDrawCall> > m_draw_calls_cache;
GESPM* m_billboard_quad;
void createInstance(SDL_Window* window);
void findPhysicalDevice();
@ -529,6 +532,7 @@ namespace GE
void destroySwapChainRelated(bool handle_surface);
void createSwapChainRelated(bool handle_surface);
void buildCommandBuffers();
void createBillboardQuad();
};
}

View File

@ -16,17 +16,13 @@ void GECullingTool::init(GEVulkanCameraSceneNode* cam)
} // init
// ----------------------------------------------------------------------------
bool GECullingTool::isCulled(GESPMBuffer* buffer,
irr::scene::ISceneNode* node)
bool GECullingTool::isCulled(irr::core::aabbox3df& bb)
{
using namespace irr;
using namespace core;
aabbox3df bb = buffer->getBoundingBox();
node->getAbsoluteTransformation().transformBoxEx(bb);
if (!m_cam_bbox.intersectsWithBox(bb))
return true;
using namespace irr;
using namespace core;
quaternion edges[8] =
{
quaternion(bb.MinEdge.X, bb.MinEdge.Y, bb.MinEdge.Z, 1.0f),
@ -56,4 +52,12 @@ bool GECullingTool::isCulled(GESPMBuffer* buffer,
return false;
} // isCulled
// ----------------------------------------------------------------------------
bool GECullingTool::isCulled(GESPMBuffer* buffer, irr::scene::ISceneNode* node)
{
irr::core::aabbox3df bb = buffer->getBoundingBox();
node->getAbsoluteTransformation().transformBoxEx(bb);
return isCulled(bb);
} // isCulled
}

View File

@ -25,6 +25,8 @@ public:
// ------------------------------------------------------------------------
void init(GEVulkanCameraSceneNode* cam);
// ------------------------------------------------------------------------
bool isCulled(irr::core::aabbox3df& bb);
// ------------------------------------------------------------------------
bool isCulled(GESPMBuffer* buffer, irr::scene::ISceneNode* node);
}; // GECullingTool

View File

@ -0,0 +1,17 @@
#include "ge_vulkan_billboard_buffer.hpp"
#include "ge_main.hpp"
#include "ge_spm.hpp"
#include "ge_vulkan_driver.hpp"
namespace GE
{
GEVulkanBillboardBuffer::GEVulkanBillboardBuffer(
irr::video::SMaterial& billboard_material)
{
m_billboard_buffer = static_cast<GESPMBuffer*>
(getVKDriver()->getBillboardQuad()->getMeshBuffer(0));
m_material = billboard_material;
} // GEVulkanBillboardBuffer
}

View File

@ -0,0 +1,31 @@
#ifndef HEADER_GE_VULKAN_BILLBOARD_BUFFER_HPP
#define HEADER_GE_VULKAN_BILLBOARD_BUFFER_HPP
#include "ge_spm_buffer.hpp"
namespace GE
{
class GEVulkanBillboardBuffer : public GESPMBuffer
{
private:
GESPMBuffer* m_billboard_buffer;
public:
// ------------------------------------------------------------------------
GEVulkanBillboardBuffer(irr::video::SMaterial& billboard_material);
// ------------------------------------------------------------------------
virtual irr::u32 getIndexCount() const
{ return m_billboard_buffer->getIndexCount(); }
// ------------------------------------------------------------------------
virtual size_t getVBOOffset() const
{ return m_billboard_buffer->getVBOOffset(); }
// ------------------------------------------------------------------------
virtual size_t getIBOOffset() const
{ return m_billboard_buffer->getIBOOffset(); }
// ------------------------------------------------------------------------
virtual VkBuffer getVkBuffer() const
{ return m_billboard_buffer->getVkBuffer(); }
};
} // end namespace GE
#endif

View File

@ -6,6 +6,7 @@
#include "ge_spm.hpp"
#include "ge_spm_buffer.hpp"
#include "ge_vulkan_animated_mesh_scene_node.hpp"
#include "ge_vulkan_billboard_buffer.hpp"
#include "ge_vulkan_camera_scene_node.hpp"
#include "ge_vulkan_driver.hpp"
#include "ge_vulkan_dynamic_buffer.hpp"
@ -18,6 +19,7 @@
#include "ge_vulkan_texture_descriptor.hpp"
#include "mini_glm.hpp"
#include "IBillboardSceneNode.h"
#include <algorithm>
#include <limits>
@ -66,6 +68,32 @@ ObjectData::ObjectData(irr::scene::ISceneNode* node, int material_id,
m_custom_vertex_color = irr::video::SColor((uint32_t)-1);
} // ObjectData
// ============================================================================
ObjectData::ObjectData(irr::scene::IBillboardSceneNode* node, int material_id,
const irr::core::quaternion& rotation)
{
const irr::core::matrix4& model_mat = node->getAbsoluteTransformation();
float translation[3] = { model_mat[12], model_mat[13], model_mat[14] };
irr::core::vector2df billboard_size = node->getSize();
irr::core::vector3df scale(billboard_size.X / 2.0f,
billboard_size.Y / 2.0f, 0);
memcpy(&m_translation_x, translation, sizeof(translation));
memcpy(m_rotation, &rotation, sizeof(irr::core::quaternion));
memcpy(&m_scale_x, &scale, sizeof(irr::core::vector3df));
m_skinning_offset = 0;
m_material_id = material_id;
m_texture_trans[0] = 0.0f;
m_texture_trans[1] = 0.0f;
m_hue_change = 0.0f;
// Only support average of them at the moment
irr::video::SColor top, bottom;
node->getColor(top, bottom);
m_custom_vertex_color.setAlpha((top.getAlpha() + bottom.getAlpha()) / 2);
m_custom_vertex_color.setRed((top.getRed() + bottom.getRed()) / 2);
m_custom_vertex_color.setGreen((top.getGreen() + bottom.getGreen()) / 2);
m_custom_vertex_color.setBlue((top.getBlue() + bottom.getBlue()) / 2);
} // ObjectData
// ----------------------------------------------------------------------------
GEVulkanDrawCall::GEVulkanDrawCall()
{
@ -97,6 +125,8 @@ GEVulkanDrawCall::~GEVulkanDrawCall()
delete [] m_data_padding;
delete m_culling_tool;
delete m_dynamic_data;
for (auto& p : m_billboard_buffers)
p.second->drop();
if (m_data_layout != VK_NULL_HANDLE)
{
GEVulkanDriver* vk = getVKDriver();
@ -150,6 +180,31 @@ void GEVulkanDrawCall::addNode(irr::scene::ISceneNode* node)
}
} // addNode
// ----------------------------------------------------------------------------
void GEVulkanDrawCall::addBillboardNode(irr::scene::IBillboardSceneNode* node)
{
irr::core::aabbox3df bb = node->getTransformedBoundingBox();
if (m_culling_tool->isCulled(bb))
return;
irr::video::SMaterial m = node->getMaterial(0);
std::array<const irr::video::ITexture*, 8> textures =
{{
m.TextureLayer[0].Texture,
m.TextureLayer[1].Texture,
m.TextureLayer[2].Texture,
m.TextureLayer[3].Texture,
m.TextureLayer[4].Texture,
m.TextureLayer[5].Texture,
m.TextureLayer[6].Texture,
m.TextureLayer[7].Texture
}};
if (m_billboard_buffers.find(textures) == m_billboard_buffers.end())
m_billboard_buffers[textures] = new GEVulkanBillboardBuffer(m);
GESPMBuffer* buffer = m_billboard_buffers.at(textures);
const std::string& shader = getShader(node, 0);
m_visible_nodes[buffer][shader].emplace_back(node, BILLBOARD_NODE);
} // addBillboardNode
// ----------------------------------------------------------------------------
void GEVulkanDrawCall::generate()
{
@ -173,7 +228,7 @@ void GEVulkanDrawCall::generate()
}
using Nodes = std::pair<GESPMBuffer*, std::unordered_map<
std::string, std::vector<std::pair<irr::scene::ISceneNode*, unsigned
std::string, std::vector<std::pair<irr::scene::ISceneNode*, int
> > > >;
std::vector<Nodes> visible_nodes;
@ -242,12 +297,21 @@ void GEVulkanDrawCall::generate()
for (auto& r : q.second)
{
irr::scene::ISceneNode* node = r.first;
int skinning_offset = -1000;
auto it = skinning_offets.find(node);
if (it != skinning_offets.end())
skinning_offset = it->second;
m_visible_objects.emplace_back(node, material_id,
skinning_offset, r.second);
if (r.second == BILLBOARD_NODE)
{
m_visible_objects.emplace_back(
static_cast<irr::scene::IBillboardSceneNode*>(
node), material_id, m_billboard_rotation);
}
else
{
int skinning_offset = -1000;
auto it = skinning_offets.find(node);
if (it != skinning_offets.end())
skinning_offset = it->second;
m_visible_objects.emplace_back(node, material_id,
skinning_offset, r.second);
}
}
VkDrawIndexedIndirectCommand cmd;
cmd.indexCount = p.first->getIndexCount();
@ -310,6 +374,7 @@ void GEVulkanDrawCall::prepare(GEVulkanCameraSceneNode* cam)
{
reset();
m_culling_tool->init(cam);
m_billboard_rotation = MiniGLM::getQuaternion(cam->getViewMatrix());
} // prepare
// ----------------------------------------------------------------------------

View File

@ -2,6 +2,7 @@
#define HEADER_GE_VULKAN_DRAW_CALL_HPP
#include <functional>
#include <map>
#include <string>
#include <unordered_map>
#include <unordered_set>
@ -10,11 +11,16 @@
#include "vulkan_wrapper.h"
#include "matrix4.h"
#include "quaternion.h"
#include "SColor.h"
#include "SMaterial.h"
namespace irr
{
namespace scene { class ISceneNode; }
namespace scene
{
class ISceneNode; class IBillboardSceneNode; struct SParticle;
}
}
namespace GE
@ -44,6 +50,9 @@ struct ObjectData
// ------------------------------------------------------------------------
ObjectData(irr::scene::ISceneNode* node, int material_id,
int skinning_offset, int irrlicht_material_id);
// ------------------------------------------------------------------------
ObjectData(irr::scene::IBillboardSceneNode* node, int material_id,
const irr::core::quaternion& rotation);
};
struct PipelineSettings
@ -75,8 +84,15 @@ struct DrawCallData
class GEVulkanDrawCall
{
private:
const int BILLBOARD_NODE = -1;
std::map<std::array<const irr::video::ITexture*, 8>, GESPMBuffer*>
m_billboard_buffers;
irr::core::quaternion m_billboard_rotation;
std::unordered_map<GESPMBuffer*, std::unordered_map<std::string,
std::vector<std::pair<irr::scene::ISceneNode*, unsigned> > > >
std::vector<std::pair<irr::scene::ISceneNode*, int> > > >
m_visible_nodes;
GECullingTool* m_culling_tool;
@ -144,6 +160,8 @@ public:
// ------------------------------------------------------------------------
void addNode(irr::scene::ISceneNode* node);
// ------------------------------------------------------------------------
void addBillboardNode(irr::scene::IBillboardSceneNode* node);
// ------------------------------------------------------------------------
void prepare(GEVulkanCameraSceneNode* cam);
// ------------------------------------------------------------------------
void generate();

View File

@ -3,6 +3,8 @@
#include "ge_compressor_astc_4x4.hpp"
#include "ge_compressor_bptc_bc7.hpp"
#include "ge_main.hpp"
#include "ge_spm.hpp"
#include "ge_spm_buffer.hpp"
#include "ge_vulkan_2d_renderer.hpp"
#include "ge_vulkan_camera_scene_node.hpp"
@ -17,6 +19,8 @@
#include "ge_vulkan_skybox_renderer.hpp"
#include "ge_vulkan_texture_descriptor.hpp"
#include "mini_glm.hpp"
#include "ISceneManager.h"
#include "IrrlichtDevice.h"
@ -499,7 +503,8 @@ GEVulkanDriver::GEVulkanDriver(const SIrrlichtCreationParameters& params,
m_params(params), m_irrlicht_device(device),
m_depth_texture(NULL), m_mesh_texture_descriptor(NULL),
m_rtt_texture(NULL), m_prev_rtt_texture(NULL),
m_separate_rtt_texture(NULL), m_rtt_polycount(0)
m_separate_rtt_texture(NULL), m_rtt_polycount(0),
m_billboard_quad(NULL)
{
m_vk.reset(new VK());
m_physical_device = VK_NULL_HANDLE;
@ -679,6 +684,11 @@ void GEVulkanDriver::destroyVulkan()
m_transparent_texture->drop();
m_transparent_texture = NULL;
}
if (m_billboard_quad)
{
getVulkanMeshCache()->removeMesh(m_billboard_quad);
m_billboard_quad = NULL;
}
if (m_irrlicht_device->getSceneManager() &&
m_irrlicht_device->getSceneManager()->getActiveCamera())
@ -1610,6 +1620,10 @@ bool GEVulkanDriver::beginScene(bool backBuffer, bool zBuffer, SColor color,
const SExposedVideoData& videoData,
core::rect<s32>* sourceRect)
{
if (m_billboard_quad == NULL && m_irrlicht_device->getSceneManager() &&
m_irrlicht_device->getSceneManager()->getMeshCache())
createBillboardQuad();
if (g_schedule_pausing_rendering.load())
{
pauseRendering();
@ -2466,6 +2480,57 @@ std::unique_ptr<GEVulkanDrawCall> GEVulkanDriver::getDrawCallFromCache()
return dc;
} // getDrawCallFromCache
// ----------------------------------------------------------------------------
void GEVulkanDriver::createBillboardQuad()
{
m_billboard_quad = new GESPM();
GESPMBuffer* buffer = new GESPMBuffer();
/* Vertices are:
(-1, 1, 0) 2--1 (1, 1, 0)
|\ |
| \|
(-1, -1, 0) 3--0 (1, -1, 0)
*/
short one_hf = 15360;
video::S3DVertexSkinnedMesh sp;
sp.m_position = core::vector3df(1, -1, 0);
sp.m_normal = MiniGLM::compressVector3(core::vector3df(0, 0, 1));
sp.m_color = video::SColor((uint32_t)-1);
sp.m_all_uvs[0] = 15360;
sp.m_all_uvs[1] = 15360;
buffer->getVerticesVector().push_back(sp);
sp.m_position = core::vector3df(1, 1, 0);
sp.m_all_uvs[0] = one_hf;
sp.m_all_uvs[1] = 0;
buffer->getVerticesVector().push_back(sp);
sp.m_position = core::vector3df(-1, 1, 0);
sp.m_all_uvs[0] = 0;
sp.m_all_uvs[1] = 0;
buffer->getVerticesVector().push_back(sp);
sp.m_position = core::vector3df(-1, -1, 0);
sp.m_all_uvs[0] = 0;
sp.m_all_uvs[1] = one_hf;
buffer->getVerticesVector().push_back(sp);
buffer->getIndicesVector().push_back(2);
buffer->getIndicesVector().push_back(1);
buffer->getIndicesVector().push_back(0);
buffer->getIndicesVector().push_back(2);
buffer->getIndicesVector().push_back(0);
buffer->getIndicesVector().push_back(3);
buffer->recalculateBoundingBox();
m_billboard_quad->addMeshBuffer(buffer);
m_billboard_quad->finalize();
std::stringstream oss;
oss << (uint64_t)m_billboard_quad;
getVulkanMeshCache()->addMesh(oss.str().c_str(), m_billboard_quad);
m_billboard_quad->drop();
} // createBillboardQuad
}
namespace irr

View File

@ -17,6 +17,7 @@
#include "ge_vulkan_texture_descriptor.hpp"
#include "mini_glm.hpp"
#include "IBillboardSceneNode.h"
#include <sstream>
namespace GE
@ -273,6 +274,13 @@ irr::u32 GEVulkanSceneManager::registerNodeForRendering(
return 1;
}
if (node->getType() == irr::scene::ESNT_BILLBOARD)
{
m_draw_calls.at(cam)->addBillboardNode(
static_cast<irr::scene::IBillboardSceneNode*>(node));
return 1;
}
if ((node->getType() == irr::scene::ESNT_ANIMATED_MESH &&
pass != irr::scene::ESNRP_SOLID) ||
(node->getType() == irr::scene::ESNT_MESH &&