186 lines
6.6 KiB
C
186 lines
6.6 KiB
C
#include "twn_draw.h"
|
|
#include "twn_draw_c.h"
|
|
#include "twn_engine_context_c.h"
|
|
#include "twn_textures_c.h"
|
|
#include "twn_types.h"
|
|
#include "twn_util_c.h"
|
|
#include "twn_vec.h"
|
|
|
|
#include <stb_ds.h>
|
|
|
|
|
|
void draw_billboard(const char *texture,
|
|
Vec3 position,
|
|
Vec2 size,
|
|
Color color,
|
|
bool cylindrical)
|
|
{
|
|
// TODO: order drawing by atlas id as well, so that texture rebinding is not as common
|
|
const TextureKey texture_key = textures_get_key(&ctx.texture_cache, texture);
|
|
|
|
struct MeshBatchItem *batch_p = hmgetp_null(ctx.billboard_batches, texture_key);
|
|
if (!batch_p) {
|
|
struct MeshBatch item = {0};
|
|
hmput(ctx.billboard_batches, texture_key, item);
|
|
batch_p = &ctx.billboard_batches[hmlenu(ctx.billboard_batches) - 1]; /* TODO: can last index be used? */
|
|
}
|
|
|
|
struct SpaceBillboard billboard = {
|
|
.color = color,
|
|
.cylindrical = cylindrical,
|
|
.position = position,
|
|
.size = size,
|
|
};
|
|
|
|
struct SpaceBillboard *billboards = (struct SpaceBillboard *)(void *)batch_p->value.primitives;
|
|
|
|
arrpush(billboards, billboard);
|
|
batch_p->value.primitives = (uint8_t *)billboards;
|
|
}
|
|
|
|
|
|
/* reused for all billboards */
|
|
static Vec3 right_plus_up;
|
|
static Vec3 right_minus_up;
|
|
static Vec3 right_plus_up_cylindrical;
|
|
static Vec3 right_minus_up_cylindrical;
|
|
|
|
/* precalculate (right + up) and (right - up) that are used with all batches */
|
|
static void calculate_intermediates(void) {
|
|
Vec3 const right = { camera_look_at_matrix.row[0].x, camera_look_at_matrix.row[1].x, camera_look_at_matrix.row[2].x };
|
|
Vec3 const up = { camera_look_at_matrix.row[0].y, camera_look_at_matrix.row[1].y, camera_look_at_matrix.row[2].y };
|
|
|
|
right_plus_up = m_vec_add(right, up);
|
|
right_minus_up = m_vec_sub(right, up);
|
|
|
|
Vec3 const up_cylindrical = { 0, 1, 0 };
|
|
|
|
right_plus_up_cylindrical = m_vec_add(right, up_cylindrical);
|
|
right_minus_up_cylindrical = m_vec_sub(right, up_cylindrical);
|
|
}
|
|
|
|
|
|
/* http://www.lighthouse3d.com/opengl/billboarding/index.php?billCheat2 */
|
|
void finally_draw_billboard_batch(struct MeshBatch const *batch,
|
|
TextureKey texture_key)
|
|
{
|
|
const size_t primitives_len = arrlenu(batch->primitives);
|
|
|
|
/* nothing to do */
|
|
if (primitives_len == 0)
|
|
return;
|
|
|
|
/* TODO: only do it once per frame */
|
|
calculate_intermediates();
|
|
|
|
const Rect srcrect = textures_get_srcrect(&ctx.texture_cache, texture_key);
|
|
const Rect dims = textures_get_dims(&ctx.texture_cache, texture_key);
|
|
|
|
const float wr = srcrect.w / dims.w;
|
|
const float hr = srcrect.h / dims.h;
|
|
const float xr = srcrect.x / dims.w;
|
|
const float yr = srcrect.y / dims.h;
|
|
|
|
const Vec2 uv0 = { xr, yr };
|
|
const Vec2 uv1 = { xr, yr + hr };
|
|
const Vec2 uv2 = { xr + wr, yr + hr };
|
|
const Vec2 uv3 = { xr + wr, yr };
|
|
|
|
for (size_t batch_n = 0; batch_n <= (primitives_len - 1) / QUAD_ELEMENT_BUFFER_LENGTH; batch_n++) {
|
|
|
|
/* emit vertex data */
|
|
VertexBuffer const buffer = get_scratch_vertex_array();
|
|
VertexBufferBuilder builder = build_vertex_buffer(
|
|
buffer,
|
|
sizeof (ElementIndexedBillboard) * MIN(primitives_len - batch_n * QUAD_ELEMENT_BUFFER_LENGTH, QUAD_ELEMENT_BUFFER_LENGTH));
|
|
|
|
for (size_t i = 0; i < primitives_len; ++i) {
|
|
struct SpaceBillboard const billboard = ((SpaceBillboard *)(void *)batch->primitives)[batch_n * QUAD_ELEMENT_BUFFER_LENGTH + i];
|
|
|
|
/* a = (right + up) * size, b = (right - up) * size*/
|
|
Vec3 a, b;
|
|
if (billboard.cylindrical) {
|
|
a = vec3_mul(right_plus_up_cylindrical, ((Vec3){billboard.size.x, billboard.size.y, billboard.size.x }));
|
|
b = vec3_mul(right_minus_up_cylindrical, ((Vec3){billboard.size.x, billboard.size.y, billboard.size.x }));
|
|
} else {
|
|
a = vec3_mul(right_plus_up, ((Vec3){billboard.size.x, billboard.size.y, billboard.size.x }));
|
|
b = vec3_mul(right_minus_up, ((Vec3){billboard.size.x, billboard.size.y, billboard.size.x }));
|
|
}
|
|
|
|
struct ElementIndexedBillboard const payload = {
|
|
/* flat shading is assumed, so we can skip setting the duplicates */
|
|
.c0 = billboard.color,
|
|
// .c1 = billboard.color,
|
|
.c2 = billboard.color,
|
|
// .c3 = billboard.color,
|
|
|
|
.uv0 = uv0,
|
|
.uv1 = uv1,
|
|
.uv2 = uv2,
|
|
.uv3 = uv3,
|
|
|
|
.v0 = vec3_sub(billboard.position, b),
|
|
.v1 = vec3_sub(billboard.position, a),
|
|
.v2 = vec3_add(billboard.position, b),
|
|
.v3 = vec3_add(billboard.position, a),
|
|
};
|
|
|
|
((struct ElementIndexedBillboard *)builder.base)[i] = payload;
|
|
}
|
|
|
|
finish_vertex_builder(&builder);
|
|
|
|
/* commit to drawing */
|
|
DeferredCommandDraw command = {0};
|
|
|
|
command.vertices = (AttributeArrayPointer) {
|
|
.arity = 3,
|
|
.type = TWN_FLOAT,
|
|
.stride = offsetof(ElementIndexedBillboard, v1),
|
|
.offset = offsetof(ElementIndexedBillboard, v0),
|
|
.buffer = buffer
|
|
};
|
|
|
|
command.texcoords = (AttributeArrayPointer) {
|
|
.arity = 2,
|
|
.type = TWN_FLOAT,
|
|
.stride = offsetof(ElementIndexedBillboard, v1),
|
|
.offset = offsetof(ElementIndexedBillboard, uv0),
|
|
.buffer = buffer
|
|
};
|
|
|
|
command.colors = (AttributeArrayPointer) {
|
|
.arity = 4,
|
|
.type = TWN_UNSIGNED_BYTE,
|
|
.stride = offsetof(ElementIndexedBillboard, v1),
|
|
.offset = offsetof(ElementIndexedBillboard, c0),
|
|
.buffer = buffer
|
|
};
|
|
|
|
command.textured = true;
|
|
command.texture_key = texture_key;
|
|
|
|
command.element_buffer = get_quad_element_buffer();
|
|
command.element_count = 6 * (uint32_t)primitives_len;
|
|
command.range_end = 6 * (uint32_t)primitives_len;
|
|
|
|
/* TODO: support alpha blended case, with distance sort */
|
|
TextureMode mode = textures_get_mode(&ctx.texture_cache, texture_key);
|
|
if (mode == TEXTURE_MODE_GHOSTLY)
|
|
mode = TEXTURE_MODE_SEETHROUGH;
|
|
|
|
command.texture_mode = mode;
|
|
command.pipeline = PIPELINE_SPACE;
|
|
|
|
command.depth_range_high = depth_range_high;
|
|
command.depth_range_low = depth_range_low;
|
|
|
|
DeferredCommand final_command = {
|
|
.type = DEFERRED_COMMAND_TYPE_DRAW,
|
|
.draw = command
|
|
};
|
|
|
|
arrpush(deferred_commands, final_command);
|
|
}
|
|
}
|