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 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209
|
#include "graphics/decal_draw_list.h"
#include "graphics/util/uniform_structs.h"
#include "graphics/matrix.h"
#include "render/3d.h"
#include "tracing/tracing.h"
#include "light.h"
namespace {
// Discard any fragments where the angle to the direction to greater than 45°
const float DECAL_ANGLE_CUTOFF = fl_radians(45.f);
const float DECAL_ANGLE_FADE_START = fl_radians(30.f);
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;
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);
}
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 {
/**
* @brief Sorts Decals so that as many decals can be batched together as possible
*
* This uses the bitmaps in the definitions to determine if two decals can be rendered at the same time. Since we use
* texture arrays we can use the base frame for batching which increases the number of draw calls that can be batched together.
*
* @param left
* @param right
* @return
*/
bool decal_draw_list::sort_draws(const decal_draw_info& left, const decal_draw_info& right) {
auto left_diffuse_base = bm_get_base_frame(left.draw_mat.get_texture_map(TM_BASE_TYPE));
auto right_diffuse_base = bm_get_base_frame(right.draw_mat.get_texture_map(TM_BASE_TYPE));
if (left_diffuse_base != right_diffuse_base) {
return left_diffuse_base < right_diffuse_base;
}
auto left_glow_base = bm_get_base_frame(left.draw_mat.get_texture_map(TM_GLOW_TYPE));
auto right_glow_base = bm_get_base_frame(right.draw_mat.get_texture_map(TM_GLOW_TYPE));
if (left_glow_base != right_glow_base) {
return left_glow_base < right_glow_base;
}
auto left_normal_base = bm_get_base_frame(left.draw_mat.get_texture_map(TM_NORMAL_TYPE));
auto right_normal_base = bm_get_base_frame(left.draw_mat.get_texture_map(TM_NORMAL_TYPE));
return left_normal_base < right_normal_base;
}
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);
}
decal_draw_list::decal_draw_list(size_t num_decals)
{
_buffer = gr_get_uniform_buffer(uniform_block_type::DecalInfo, num_decals);
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;
gr_get_ambient_light(&header->ambientLight);
// Square the ambient part of the light to match the formula used in the main model shader
header->ambientLight.xyz.x *= header->ambientLight.xyz.x;
header->ambientLight.xyz.y *= header->ambientLight.xyz.y;
header->ambientLight.xyz.z *= header->ambientLight.xyz.z;
header->ambientLight.xyz.x += gr_light_emission[0];
header->ambientLight.xyz.y += gr_light_emission[1];
header->ambientLight.xyz.z += gr_light_emission[2];
}
decal_draw_list::~decal_draw_list() {
}
void decal_draw_list::render() {
GR_DEBUG_SCOPE("Render decals");
TRACE_SCOPE(tracing::RenderDecals);
_buffer.submitData();
std::sort(_draws.begin(), _draws.end(), decal_draw_list::sort_draws);
vertex_layout layout;
layout.add_vertex_component(vertex_format_data::POSITION3, sizeof(vec3d), 0);
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& draw : _draws) {
GR_DEBUG_SCOPE("Draw single decal");
TRACE_SCOPE(tracing::RenderSingleDecal);
gr_bind_uniform_buffer(uniform_block_type::DecalInfo, draw.uniform_offset, sizeof(graphics::decal_info),
_buffer.bufferHandle());
gr_screen.gf_render_decals(&draw.draw_mat, PRIM_TYPE_TRIS, &layout, BOX_NUM_FACES, source);
}
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& transform,
float base_alpha) {
if (!check_box_in_view(transform)) {
// The decal box is not in view so we don't need to render it
return;
}
auto& aligner = _buffer.aligner();
auto info = aligner.addTypedElement<graphics::decal_info>();
info->model_matrix = transform;
// This is currently a constant but in the future this may be configurable by the decals table
info->normal_angle_cutoff = DECAL_ANGLE_CUTOFF;
info->angle_fade_start = DECAL_ANGLE_FADE_START;
info->alpha_scale = base_alpha;
matrix transform_rot;
vm_matrix4_get_orientation(&transform_rot, &transform);
// The decal shader works in view-space so the direction also has to be transformed into that space
vm_vec_transform(&info->decal_direction, &transform_rot.vec.fvec, &gr_view_matrix, false);
vm_inverse_matrix4(&info->inv_model_matrix, &info->model_matrix);
info->diffuse_index = diffuse_bitmap < 0 ? -1 : bm_get_array_index(diffuse_bitmap);
info->glow_index = glow_bitmap < 0 ? -1 : bm_get_array_index(glow_bitmap);
info->normal_index = normal_bitmap < 0 ? -1 : bm_get_array_index(normal_bitmap);
decal_draw_info current_draw;
current_draw.uniform_offset = _buffer.getBufferOffset(aligner.getCurrentOffset());
material_set_decal(¤t_draw.draw_mat,
bm_get_base_frame(diffuse_bitmap),
bm_get_base_frame(glow_bitmap),
bm_get_base_frame(normal_bitmap));
info->diffuse_blend_mode = current_draw.draw_mat.get_blend_mode(0) == ALPHA_BLEND_ADDITIVE ? 1 : 0;
info->glow_blend_mode = current_draw.draw_mat.get_blend_mode(2) == ALPHA_BLEND_ADDITIVE ? 1 : 0;
_draws.push_back(current_draw);
}
}
|