1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
|
#include "graphics/decal_draw_list.h"
#include "graphics/matrix.h"
#include "render/3d.h"
#include "tracing/tracing.h"
#include "light.h"
namespace {
vec3d BOX_VERTS[] = {{{{ -0.5f, -0.5f, -0.5f }}},
{{{ -0.5f, 0.5f, -0.5f }}},
{{{ 0.5f, 0.5f, -0.5f }}},
{{{ 0.5f, -0.5f, -0.5f }}},
{{{ -0.5f, -0.5f, 0.5f }}},
{{{ -0.5f, 0.5f, 0.5f }}},
{{{ 0.5f, 0.5f, 0.5f }}},
{{{ 0.5f, -0.5f, 0.5f }}}};
uint32_t BOX_FACES[] =
{ 7, 3, 4, 3, 0, 4, 2, 6, 1, 6, 5, 1, 7, 6, 3, 6, 2, 3, 0, 1, 4, 1, 5, 4, 6, 7, 4, 5, 6, 4, 3, 2, 0, 2, 1, 0, };
const size_t BOX_NUM_FACES = sizeof(BOX_FACES) / sizeof(BOX_FACES[0]);
gr_buffer_handle box_vertex_buffer;
gr_buffer_handle box_index_buffer;
gr_buffer_handle decal_instance_buffer;
void init_buffers() {
box_vertex_buffer = gr_create_buffer(BufferType::Vertex, BufferUsageHint::Static);
gr_update_buffer_data(box_vertex_buffer, sizeof(BOX_VERTS), BOX_VERTS);
box_index_buffer = gr_create_buffer(BufferType::Index, BufferUsageHint::Static);
gr_update_buffer_data(box_index_buffer, sizeof(BOX_FACES), BOX_FACES);
decal_instance_buffer = gr_create_buffer(BufferType::Vertex, BufferUsageHint::Streaming);
}
bool check_box_in_view(const matrix4& transform) {
for (auto& point : BOX_VERTS) {
vec3d pt;
vm_vec_transform(&pt, &point, &transform, true);
vec3d tmp;
if (!g3_rotate_vector(&tmp, &pt)) {
// This point lies in the view cone so we need to render it
return true;
}
}
return false;
}
}
namespace graphics {
void decal_draw_list::globalInit() {
init_buffers();
gr_maybe_create_shader(SDR_TYPE_DECAL, 0);
gr_maybe_create_shader(SDR_TYPE_DECAL, SDR_FLAG_DECAL_USE_NORMAL_MAP);
}
void decal_draw_list::globalShutdown() {
gr_delete_buffer(box_vertex_buffer);
gr_delete_buffer(box_index_buffer);
}
void decal_draw_list::prepare_global_data() {
_buffer = gr_get_uniform_buffer(uniform_block_type::DecalInfo, _draws.size());
auto& aligner = _buffer.aligner();
// Initialize header data
auto header = aligner.getHeader<graphics::decal_globals>();
matrix4 invView;
vm_inverse_matrix4(&invView, &gr_view_matrix);
header->viewMatrix = gr_view_matrix;
header->projMatrix = gr_projection_matrix;
header->invViewMatrix = invView;
vm_inverse_matrix4(&header->invProjMatrix, &gr_projection_matrix);
header->viewportSize.x = (float) gr_screen.max_w;
header->viewportSize.y = (float) gr_screen.max_h;
for (auto& [batch_info, draw_info] : _draws) {
auto info = aligner.addTypedElement<graphics::decal_info>();
info->diffuse_index = batch_info.diffuse < 0 ? -1 : bm_get_array_index(batch_info.diffuse);
info->glow_index = batch_info.glow < 0 ? -1 : bm_get_array_index(batch_info.glow);
info->normal_index = batch_info.normal < 0 ? -1 : bm_get_array_index(batch_info.normal);
draw_info.first.uniform_offset = _buffer.getBufferOffset(aligner.getCurrentOffset());
material_set_decal(&draw_info.first.material,
bm_get_base_frame(batch_info.diffuse),
bm_get_base_frame(batch_info.glow),
bm_get_base_frame(batch_info.normal));
info->diffuse_blend_mode = draw_info.first.material.get_blend_mode(0) == ALPHA_BLEND_ADDITIVE ? 1 : 0;
info->glow_blend_mode = draw_info.first.material.get_blend_mode(2) == ALPHA_BLEND_ADDITIVE ? 1 : 0;
}
}
void decal_draw_list::render() {
GR_DEBUG_SCOPE("Render decals");
TRACE_SCOPE(tracing::RenderDecals);
prepare_global_data();
_buffer.submitData();
vertex_layout layout;
layout.add_vertex_component(vertex_format_data::POSITION3, sizeof(vec3d), 0);
layout.add_vertex_component(vertex_format_data::MATRIX4, sizeof(matrix4), 0, 1, 1);
indexed_vertex_source source;
source.Vbuffer_handle = box_vertex_buffer;
source.Ibuffer_handle = box_index_buffer;
// Bind the global data only once
gr_bind_uniform_buffer(uniform_block_type::DecalGlobals,
_buffer.getBufferOffset(0),
sizeof(graphics::decal_globals),
_buffer.bufferHandle());
gr_screen.gf_start_decal_pass();
for (auto& [textures, decal_list] : _draws) {
GR_DEBUG_SCOPE("Draw decal type");
TRACE_SCOPE(tracing::RenderSingleDecal);
gr_update_buffer_data(decal_instance_buffer, sizeof(matrix4) * decal_list.second.size(), decal_list.second.data());
gr_bind_uniform_buffer(uniform_block_type::DecalInfo, decal_list.first.uniform_offset, sizeof(graphics::decal_info), _buffer.bufferHandle());
gr_screen.gf_render_decals(&decal_list.first.material, PRIM_TYPE_TRIS, &layout, BOX_NUM_FACES, source, decal_instance_buffer, static_cast<int>(decal_list.second.size()));
}
gr_screen.gf_stop_decal_pass();
}
void decal_draw_list::add_decal(int diffuse_bitmap,
int glow_bitmap,
int normal_bitmap,
float /*decal_timer*/,
const matrix4& instancedata) {
if (!check_box_in_view(instancedata)) {
// The decal box is not in view so we don't need to render it
return;
}
_draws[decal_batch_info{diffuse_bitmap, glow_bitmap, normal_bitmap}].second.emplace_back(instancedata);
}
}
|