Files

1092 lines
32 KiB
C++

/*
* Copyright (C) Freespace Open 2015. All rights reserved.
*
* All source code herein is the property of Freespace Open. You may not sell
* or otherwise commercially exploit the source or things you created based on the
* source.
*
*/
#include "render/batching.h"
#include "globalincs/pstypes.h"
#include "globalincs/systemvars.h"
#include "graphics/2d.h"
#include "render/3d.h"
#include "graphics/material.h"
#include "tracing/tracing.h"
static SCP_map<batch_info, primitive_batch> Batching_primitives;
static SCP_map<batch_buffer_key, primitive_batch_buffer> Batching_buffers;
static int lineTexture = -1;
void primitive_batch::add_triangle(batch_vertex* v0, batch_vertex* v1, batch_vertex *v2)
{
Vertices.push_back(*v0);
Vertices.push_back(*v1);
Vertices.push_back(*v2);
}
void primitive_batch::add_point_sprite(batch_vertex *p)
{
Vertices.push_back(*p);
}
size_t primitive_batch::load_buffer(batch_vertex* buffer, size_t n_verts)
{
size_t verts_to_render = Vertices.size();
for ( size_t i = 0; i < verts_to_render; ++i) {
buffer[n_verts+i] = Vertices[i];
}
return verts_to_render;
}
void primitive_batch::clear()
{
Vertices.clear();
}
void batching_setup_vertex_layout(vertex_layout *layout, uint vert_mask)
{
int stride = sizeof(batch_vertex);
if ( vert_mask & vertex_format_data::mask(vertex_format_data::POSITION3) ) {
layout->add_vertex_component(vertex_format_data::POSITION3, stride, (int)offsetof(batch_vertex, position));
}
if ( vert_mask & vertex_format_data::mask(vertex_format_data::TEX_COORD4) ) {
layout->add_vertex_component(vertex_format_data::TEX_COORD4, stride, (int)offsetof(batch_vertex, tex_coord));
}
if ( vert_mask & vertex_format_data::mask(vertex_format_data::COLOR4) ) {
layout->add_vertex_component(vertex_format_data::COLOR4, stride, (int)offsetof(batch_vertex, r));
}
if ( vert_mask & vertex_format_data::mask(vertex_format_data::RADIUS) ) {
layout->add_vertex_component(vertex_format_data::RADIUS, stride, (int)offsetof(batch_vertex, radius));
}
if ( vert_mask & vertex_format_data::mask(vertex_format_data::UVEC) ) {
layout->add_vertex_component(vertex_format_data::UVEC, stride, (int)offsetof(batch_vertex, uvec));
}
}
void batching_init_buffer(primitive_batch_buffer *buffer, primitive_type prim_type, uint vertex_mask)
{
batching_setup_vertex_layout(&buffer->layout, vertex_mask);
buffer->buffer_num = gr_create_buffer(BufferType::Vertex, BufferUsageHint::Streaming);
buffer->buffer_ptr = NULL;
buffer->buffer_size = 0;
buffer->desired_buffer_size = 0;
buffer->prim_type = prim_type;
}
void batching_determine_blend_color(color *clr, int texture, float alpha)
{
gr_alpha_blend blend_mode = material_determine_blend_mode(texture, true);
if ( blend_mode == ALPHA_BLEND_ADDITIVE ) {
gr_init_alphacolor(clr, fl2i(255.0f*alpha), fl2i(255.0f*alpha), fl2i(255.0f*alpha), 255);
} else {
gr_init_alphacolor(clr, 255, 255, 255, fl2i(255.0f*alpha));
}
}
primitive_batch_buffer* batching_find_buffer(uint vertex_mask, primitive_type prim_type)
{
batch_buffer_key query(vertex_mask, prim_type);
SCP_map<batch_buffer_key, primitive_batch_buffer>::iterator iter = Batching_buffers.find(query);
if ( iter == Batching_buffers.end() ) {
primitive_batch_buffer *buffer = &Batching_buffers[query];
batching_init_buffer(buffer, prim_type, vertex_mask);
return buffer;
} else {
return &iter->second;
}
}
primitive_batch* batching_find_batch(int texture, batch_info::material_type material_id, primitive_type prim_type, bool thruster)
{
// Use the base texture for finding the batch item since all items can reuse the same texture array
auto base_tex = bm_get_base_frame(texture);
batch_info query(material_id, base_tex, prim_type, thruster);
SCP_map<batch_info, primitive_batch>::iterator iter = Batching_primitives.find(query);
if ( iter == Batching_primitives.end() ) {
primitive_batch* batch = &Batching_primitives[query];
*batch = primitive_batch(query);
return batch;
} else {
return &iter->second;
}
}
uint batching_determine_vertex_layout(batch_info *info)
{
if ( info->prim_type == PRIM_TYPE_POINTS ) {
return vertex_format_data::mask(vertex_format_data::POSITION3)
| vertex_format_data::mask(vertex_format_data::RADIUS)
| vertex_format_data::mask(vertex_format_data::UVEC)
| vertex_format_data::mask(vertex_format_data::TEX_COORD4);
}
if ( info->mat_type == batch_info::VOLUME_EMISSIVE || info->mat_type == batch_info::DISTORTION ) {
return vertex_format_data::mask(vertex_format_data::POSITION3)
| vertex_format_data::mask(vertex_format_data::COLOR4)
| vertex_format_data::mask(vertex_format_data::TEX_COORD4)
| vertex_format_data::mask(vertex_format_data::RADIUS);
} else {
return vertex_format_data::mask(vertex_format_data::POSITION3)
| vertex_format_data::mask(vertex_format_data::COLOR4)
| vertex_format_data::mask(vertex_format_data::TEX_COORD4);
}
}
void batching_add_bitmap_internal(primitive_batch *batch, int texture, vertex *pnt, int orient, float rad, color *clr, float depth)
{
Assert(batch->get_render_info().prim_type == PRIM_TYPE_TRIS);
float radius = rad;
rad *= 1.41421356f;//1/0.707, becase these are the points of a square or width and height rad
vec3d PNT(pnt->world);
vec3d p[4];
vec3d fvec, rvec, uvec;
batch_vertex verts[6];
// get the direction from the point to the eye
vm_vec_sub(&fvec, &View_position, &PNT);
vm_vec_normalize_safe(&fvec);
// get an up vector in the general direction of what we want
uvec = View_matrix.vec.uvec;
// make a right vector from the f and up vector, this r vec is exactly what we want, so...
vm_vec_cross(&rvec, &View_matrix.vec.fvec, &uvec);
vm_vec_normalize_safe(&rvec);
// fix the u vec with it
vm_vec_cross(&uvec, &View_matrix.vec.fvec, &rvec);
// move the center of the sprite based on the depth parameter
if ( depth != 0.0f )
vm_vec_scale_add(&PNT, &PNT, &fvec, depth);
// move one of the verts to the left
vm_vec_scale_add(&p[0], &PNT, &rvec, rad);
// and one to the right
vm_vec_scale_add(&p[2], &PNT, &rvec, -rad);
// now move all oof the verts to were they need to be
vm_vec_scale_add(&p[1], &p[2], &uvec, rad);
vm_vec_scale_add(&p[3], &p[0], &uvec, -rad);
vm_vec_scale_add(&p[0], &p[0], &uvec, rad);
vm_vec_scale_add(&p[2], &p[2], &uvec, -rad);
//move all the data from the vecs into the verts
//tri 1
verts[5].position = p[3];
verts[4].position = p[2];
verts[3].position = p[1];
//tri 2
verts[2].position = p[3];
verts[1].position = p[1];
verts[0].position = p[0];
// set up the UV coords
if ( orient & 1 ) {
// tri 1
verts[5].tex_coord.xyzw.x = 1.0f;
verts[4].tex_coord.xyzw.x = 0.0f;
verts[3].tex_coord.xyzw.x = 0.0f;
// tri 2
verts[2].tex_coord.xyzw.x = 1.0f;
verts[1].tex_coord.xyzw.x = 0.0f;
verts[0].tex_coord.xyzw.x = 1.0f;
} else {
// tri 1
verts[5].tex_coord.xyzw.x = 0.0f;
verts[4].tex_coord.xyzw.x = 1.0f;
verts[3].tex_coord.xyzw.x = 1.0f;
// tri 2
verts[2].tex_coord.xyzw.x = 0.0f;
verts[1].tex_coord.xyzw.x = 1.0f;
verts[0].tex_coord.xyzw.x = 0.0f;
}
if ( orient & 2 ) {
// tri 1
verts[5].tex_coord.xyzw.y = 1.0f;
verts[4].tex_coord.xyzw.y = 1.0f;
verts[3].tex_coord.xyzw.y = 0.0f;
// tri 2
verts[2].tex_coord.xyzw.y = 1.0f;
verts[1].tex_coord.xyzw.y = 0.0f;
verts[0].tex_coord.xyzw.y = 0.0f;
} else {
// tri 1
verts[5].tex_coord.xyzw.y = 0.0f;
verts[4].tex_coord.xyzw.y = 0.0f;
verts[3].tex_coord.xyzw.y = 1.0f;
// tri 2
verts[2].tex_coord.xyzw.y = 0.0f;
verts[1].tex_coord.xyzw.y = 1.0f;
verts[0].tex_coord.xyzw.y = 1.0f;
}
auto array_index = texture - batch->get_render_info().texture;
for (int i = 0; i < 6 ; i++) {
verts[i].r = clr->red;
verts[i].g = clr->green;
verts[i].b = clr->blue;
verts[i].a = clr->alpha;
verts[i].radius = radius;
// Set array index
verts[i].tex_coord.xyzw.z = (float) array_index;
verts[i].tex_coord.xyzw.w = 1.0f;
}
batch->add_triangle(&verts[5], &verts[4], &verts[3]);
batch->add_triangle(&verts[2], &verts[1], &verts[0]);
}
void batching_add_bitmap_rotated_internal(primitive_batch *batch, int texture, vertex *pnt, float angle, float rad, color *clr, float depth)
{
Assert(batch->get_render_info().prim_type == PRIM_TYPE_TRIS);
float radius = rad;
rad *= 1.41421356f;//1/0.707, becase these are the points of a square or width and height rad
extern float Physics_viewer_bank;
angle -= Physics_viewer_bank;
if ( angle < 0.0f )
angle += PI2;
else if ( angle > PI2 )
angle -= PI2;
vec3d PNT(pnt->world);
vec3d p[4];
vec3d fvec, rvec, uvec;
batch_vertex verts[6];
vm_vec_sub(&fvec, &View_position, &PNT);
vm_vec_normalize_safe(&fvec);
vm_rot_point_around_line(&uvec, &View_matrix.vec.uvec, angle, &vmd_zero_vector, &View_matrix.vec.fvec);
vm_vec_cross(&rvec, &View_matrix.vec.fvec, &uvec);
vm_vec_normalize_safe(&rvec);
vm_vec_cross(&uvec, &View_matrix.vec.fvec, &rvec);
vm_vec_scale_add(&PNT, &PNT, &fvec, depth);
vm_vec_scale_add(&p[0], &PNT, &rvec, rad);
vm_vec_scale_add(&p[2], &PNT, &rvec, -rad);
vm_vec_scale_add(&p[1], &p[2], &uvec, rad);
vm_vec_scale_add(&p[3], &p[0], &uvec, -rad);
vm_vec_scale_add(&p[0], &p[0], &uvec, rad);
vm_vec_scale_add(&p[2], &p[2], &uvec, -rad);
//move all the data from the vecs into the verts
//tri 1
verts[5].position = p[3];
verts[4].position = p[2];
verts[3].position = p[1];
//tri 2
verts[2].position = p[3];
verts[1].position = p[1];
verts[0].position = p[0];
//tri 1
verts[5].tex_coord.xyzw.x = 0.0f; verts[5].tex_coord.xyzw.y = 0.0f;
verts[4].tex_coord.xyzw.x = 1.0f; verts[4].tex_coord.xyzw.y = 0.0f;
verts[3].tex_coord.xyzw.x = 1.0f; verts[3].tex_coord.xyzw.y = 1.0f;
//tri 2
verts[2].tex_coord.xyzw.x = 0.0f; verts[2].tex_coord.xyzw.y = 0.0f;
verts[1].tex_coord.xyzw.x = 1.0f; verts[1].tex_coord.xyzw.y = 1.0f;
verts[0].tex_coord.xyzw.x = 0.0f; verts[0].tex_coord.xyzw.y = 1.0f;
auto array_index = texture - batch->get_render_info().texture;
for (int i = 0; i < 6 ; i++) {
verts[i].r = clr->red;
verts[i].g = clr->green;
verts[i].b = clr->blue;
verts[i].a = clr->alpha;
verts[i].radius = radius;
verts[i].tex_coord.xyzw.z = (float)array_index;
verts[i].tex_coord.xyzw.w = 1.0f;
}
batch->add_triangle(&verts[0], &verts[1], &verts[2]);
batch->add_triangle(&verts[3], &verts[4], &verts[5]);
}
void batching_add_polygon_internal(primitive_batch *batch, int texture, const vec3d *pos, const matrix *orient, float width, float height, color *clr)
{
Assert(batch->get_render_info().prim_type == PRIM_TYPE_TRIS);
//idiot-proof
if(width == 0 || height == 0)
return;
Assert(pos != NULL);
Assert(orient != NULL);
//Let's begin.
const int NUM_VERTICES = 4;
vec3d p[NUM_VERTICES] = { ZERO_VECTOR };
batch_vertex v[NUM_VERTICES];
p[0].xyz.x = width;
p[0].xyz.y = height;
p[1].xyz.x = -width;
p[1].xyz.y = height;
p[2].xyz.x = -width;
p[2].xyz.y = -height;
p[3].xyz.x = width;
p[3].xyz.y = -height;
auto array_index = texture - batch->get_render_info().texture;
for(int i = 0; i < NUM_VERTICES; i++)
{
vec3d tmp = vmd_zero_vector;
//Rotate correctly
vm_vec_unrotate(&tmp, &p[i], orient);
//Move to point in space
vm_vec_add2(&tmp, pos);
v[i].position = tmp;
v[i].r = clr->red;
v[i].g = clr->green;
v[i].b = clr->blue;
v[i].a = clr->alpha;
v[i].tex_coord.xyzw.z = (float)array_index;
v[i].tex_coord.xyzw.w = 1.0f;
v[i].radius = MAX(width * 0.5f, height * 0.5f);
}
v[0].tex_coord.xyzw.x = 1.0f;
v[0].tex_coord.xyzw.y = 0.0f;
v[1].tex_coord.xyzw.x = 0.0f;
v[1].tex_coord.xyzw.y = 0.0f;
v[2].tex_coord.xyzw.x = 0.0f;
v[2].tex_coord.xyzw.y = 1.0f;
v[3].tex_coord.xyzw.x = 1.0f;
v[3].tex_coord.xyzw.y = 1.0f;
batch->add_triangle(&v[0], &v[1], &v[2]);
batch->add_triangle(&v[0], &v[2], &v[3]);
}
void batching_add_quad_internal(primitive_batch* batch, int texture, vertex* verts, float trapezoidal_correction = 1.0f)
{
Assert(batch->get_render_info().prim_type == PRIM_TYPE_TRIS);
const int NUM_VERTICES = 4;
batch_vertex v[NUM_VERTICES];
auto array_index = texture - batch->get_render_info().texture;
v[0].tex_coord = vm_vec4_new(verts[0].texture_position.u, verts[0].texture_position.v, (float)array_index, 1.0f);
v[1].tex_coord = vm_vec4_new(verts[1].texture_position.u, verts[1].texture_position.v, (float)array_index, 1.0f);
v[2].tex_coord = vm_vec4_new(verts[2].texture_position.u, verts[2].texture_position.v * trapezoidal_correction, (float)array_index, trapezoidal_correction);
v[3].tex_coord = vm_vec4_new(verts[3].texture_position.u, verts[3].texture_position.v, (float)array_index, trapezoidal_correction);
for ( int i = 0; i < NUM_VERTICES; i++ ) {
v[i].position = verts[i].world;
v[i].r = verts[i].r;
v[i].g = verts[i].g;
v[i].b = verts[i].b;
v[i].a = verts[i].a;
}
batch->add_triangle(&v[0], &v[1], &v[2]);
batch->add_triangle(&v[0], &v[2], &v[3]);
}
void batching_add_tri_internal(primitive_batch *batch, int texture, vertex *verts)
{
Assert(batch->get_render_info().prim_type == PRIM_TYPE_TRIS);
const int NUM_VERTICES = 3;
batch_vertex v[NUM_VERTICES];
auto array_index = texture - batch->get_render_info().texture;
for ( int i = 0; i < NUM_VERTICES; i++ ) {
v[i].position = verts[i].world;
v[i].r = verts[i].r;
v[i].g = verts[i].g;
v[i].b = verts[i].b;
v[i].a = verts[i].a;
v[i].tex_coord.xyzw.x = verts[i].texture_position.u;
v[i].tex_coord.xyzw.y = verts[i].texture_position.v;
v[i].tex_coord.xyzw.z = (float)array_index;
v[i].tex_coord.xyzw.w = 1.0f;
}
batch->add_triangle(&v[0], &v[1], &v[2]);
}
void batching_add_beam_internal(primitive_batch *batch, int texture, vec3d *start, vec3d *end, float width, color *clr, float offset)
{
Assert(batch->get_render_info().prim_type == PRIM_TYPE_TRIS);
vec3d p[4];
batch_vertex verts[6];
vec3d fvec, uvecs, uvece, evec;
vm_vec_sub(&fvec, start, end);
vm_vec_normalize_safe(&fvec);
vm_vec_sub(&evec, &View_position, start);
vm_vec_normalize_safe(&evec);
vm_vec_cross(&uvecs, &fvec, &evec);
vm_vec_normalize_safe(&uvecs);
vm_vec_sub(&evec, &View_position, end);
vm_vec_normalize_safe(&evec);
vm_vec_cross(&uvece, &fvec, &evec);
vm_vec_normalize_safe(&uvece);
vm_vec_scale_add(&p[0], start, &uvecs, width);
vm_vec_scale_add(&p[1], end, &uvece, width);
vm_vec_scale_add(&p[2], end, &uvece, -width);
vm_vec_scale_add(&p[3], start, &uvecs, -width);
//move all the data from the vecs into the verts
//tri 1
verts[0].position = p[3];
verts[1].position = p[2];
verts[2].position = p[1];
//tri 2
verts[3].position = p[3];
verts[4].position = p[1];
verts[5].position = p[0];
//set up the UV coords
//tri 1
verts[0].tex_coord.xyzw.x = 0.0f; verts[0].tex_coord.xyzw.y = 0.0f;
verts[1].tex_coord.xyzw.x = 1.0f; verts[1].tex_coord.xyzw.y = 0.0f;
verts[2].tex_coord.xyzw.x = 1.0f; verts[2].tex_coord.xyzw.y = 1.0f;
//tri 2
verts[3].tex_coord.xyzw.x = 0.0f; verts[3].tex_coord.xyzw.y = 0.0f;
verts[4].tex_coord.xyzw.x = 1.0f; verts[4].tex_coord.xyzw.y = 1.0f;
verts[5].tex_coord.xyzw.x = 0.0f; verts[5].tex_coord.xyzw.y = 1.0f;
auto array_index = texture - batch->get_render_info().texture;
for(int i = 0; i < 6; i++){
verts[i].r = clr->red;
verts[i].g = clr->green;
verts[i].b = clr->blue;
verts[i].a = clr->alpha;
if(offset > 0.0f) {
verts[i].radius = offset;
} else {
verts[i].radius = width;
}
verts[i].tex_coord.xyzw.z = (float)array_index;
verts[i].tex_coord.xyzw.w = 1.0f;
}
batch->add_triangle(&verts[0], &verts[1], &verts[2]);
batch->add_triangle(&verts[3], &verts[4], &verts[5]);
}
void batching_add_line_internal(primitive_batch *batch, int texture, vec3d *start, vec3d *end, float width1, float width2, color *clr)
{
Assert(batch->get_render_info().prim_type == PRIM_TYPE_TRIS);
vec3d p[4];
batch_vertex verts[6];
vec3d fvec, uvecs, uvece, evec;
vm_vec_sub(&fvec, start, end);
vm_vec_normalize_safe(&fvec);
vm_vec_sub(&evec, &View_position, start);
vm_vec_normalize_safe(&evec);
vm_vec_cross(&uvecs, &fvec, &evec);
vm_vec_normalize_safe(&uvecs);
vm_vec_sub(&evec, &View_position, end);
vm_vec_normalize_safe(&evec);
vm_vec_cross(&uvece, &fvec, &evec);
vm_vec_normalize_safe(&uvece);
vm_vec_scale_add(&p[0], start, &uvecs, width1);
vm_vec_scale_add(&p[1], end, &uvece, width2);
vm_vec_scale_add(&p[2], end, &uvece, -width2);
vm_vec_scale_add(&p[3], start, &uvecs, -width1);
//move all the data from the vecs into the verts
//tri 1
verts[0].position = p[3];
verts[1].position = p[2];
verts[2].position = p[1];
//tri 2
verts[3].position = p[3];
verts[4].position = p[1];
verts[5].position = p[0];
//set up the UV coords
//tri 1
verts[0].tex_coord.xyzw.x = 0.0f; verts[0].tex_coord.xyzw.y = 0.0f;
verts[1].tex_coord.xyzw.x = 1.0f; verts[1].tex_coord.xyzw.y = 0.0f;
verts[2].tex_coord.xyzw.x = 1.0f; verts[2].tex_coord.xyzw.y = 1.0f;
//tri 2
verts[3].tex_coord.xyzw.x = 0.0f; verts[3].tex_coord.xyzw.y = 0.0f;
verts[4].tex_coord.xyzw.x = 1.0f; verts[4].tex_coord.xyzw.y = 1.0f;
verts[5].tex_coord.xyzw.x = 0.0f; verts[5].tex_coord.xyzw.y = 1.0f;
auto array_index = texture - batch->get_render_info().texture;
for(batch_vertex &vert : verts){
vert.r = clr->red;
vert.g = clr->green;
vert.b = clr->blue;
vert.a = clr->alpha;
vert.radius = width1;
vert.tex_coord.xyzw.z = (float)array_index;
vert.tex_coord.xyzw.w = 1.0f;
}
batch->add_triangle(&verts[0], &verts[1], &verts[2]);
batch->add_triangle(&verts[3], &verts[4], &verts[5]);
}
void batching_add_laser_internal(primitive_batch *batch, int texture, const vec3d *p0, float width1, const vec3d *p1, float width2, int r, int g, int b)
{
Assert(batch->get_render_info().prim_type == PRIM_TYPE_TRIS);
width1 *= 0.5f;
width2 *= 0.5f;
vec3d uvec, fvec, rvec, center, reye;
vm_vec_sub( &fvec, p0, p1 );
vm_vec_normalize_safe( &fvec );
vm_vec_avg( &center, p0, p1 ); // needed for the return value only
vm_vec_sub(&reye, &Eye_position, &center);
vm_vec_normalize(&reye);
// compute the up vector
vm_vec_cross(&uvec, &fvec, &reye);
vm_vec_normalize_safe(&uvec);
// ... the forward vector
vm_vec_cross(&fvec, &uvec, &reye);
vm_vec_normalize_safe(&fvec);
// now recompute right vector, in case it wasn't entirely perpendiclar
vm_vec_cross(&rvec, &uvec, &fvec);
// Now have uvec, which is up vector and rvec which is the normal
// of the face.
vec3d start, end;
vm_vec_scale_add(&start, p0, &fvec, -width1);
vm_vec_scale_add(&end, p1, &fvec, width2);
vec3d vecs[4];
batch_vertex verts[6];
vm_vec_scale_add( &vecs[0], &end, &uvec, width2 );
vm_vec_scale_add( &vecs[1], &start, &uvec, width1 );
vm_vec_scale_add( &vecs[2], &start, &uvec, -width1 );
vm_vec_scale_add( &vecs[3], &end, &uvec, -width2 );
verts[0].position = vecs[0];
verts[1].position = vecs[1];
verts[2].position = vecs[2];
verts[3].position = vecs[0];
verts[4].position = vecs[2];
verts[5].position = vecs[3];
auto array_index = texture - batch->get_render_info().texture;
float ratio = width2 / width1;
if (width1 <= 0.0f)
ratio = 999.0f;
verts[0].tex_coord = vm_vec4_new(1.0f, 0.0f, (float)array_index, ratio);
verts[1].tex_coord = vm_vec4_new(0.0f, 0.0f, (float)array_index, 1.0f);
verts[2].tex_coord = vm_vec4_new(0.0f, 1.0f, (float)array_index, 1.0f);
verts[3].tex_coord = vm_vec4_new(1.0f, 0.0f, (float)array_index, ratio);
verts[4].tex_coord = vm_vec4_new(0.0f, 1.0f, (float)array_index, 1.0f);
verts[5].tex_coord = vm_vec4_new(1.0f, ratio, (float)array_index, ratio);
for (auto& vert : verts) {
vert.r = (ubyte)r;
vert.g = (ubyte)g;
vert.b = (ubyte)b;
vert.a = 255;
}
batch->add_triangle(&verts[0], &verts[1], &verts[2]);
batch->add_triangle(&verts[3], &verts[4], &verts[5]);
}
void batching_add_bitmap(int texture, vertex *pnt, int orient, float rad, float alpha, float depth)
{
Assertion((texture >= 0), "batching_add_...() attempted for invalid texture");
if ( texture < 0 ) {
return;
}
primitive_batch *batch = batching_find_batch(texture, batch_info::FLAT_EMISSIVE);
color clr;
batching_determine_blend_color(&clr, texture, alpha);
batching_add_bitmap_internal(batch, texture, pnt, orient, rad, &clr, depth);
}
void batching_add_volume_bitmap(int texture, vertex *pnt, int orient, float rad, float alpha, float depth)
{
Assertion((texture >= 0), "batching_add_...() attempted for invalid texture");
if ( texture < 0 ) {
return;
}
primitive_batch *batch;
if ( gr_is_capable(gr_capability::CAPABILITY_SOFT_PARTICLES) ) {
batch = batching_find_batch(texture, batch_info::VOLUME_EMISSIVE);
} else {
batch = batching_find_batch(texture, batch_info::FLAT_EMISSIVE);
}
color clr;
batching_determine_blend_color(&clr, texture, alpha);
batching_add_bitmap_internal(batch, texture, pnt, orient, rad, &clr, depth);
}
void batching_add_volume_bitmap_rotated(int texture, vertex *pnt, float angle, float rad, float alpha, float depth)
{
Assertion((texture >= 0), "batching_add_...() attempted for invalid texture");
if ( texture < 0 ) {
return;
}
primitive_batch *batch;
if ( gr_is_capable(gr_capability::CAPABILITY_SOFT_PARTICLES) ) {
batch = batching_find_batch(texture, batch_info::VOLUME_EMISSIVE);
} else {
batch = batching_find_batch(texture, batch_info::FLAT_EMISSIVE);
}
color clr;
batching_determine_blend_color(&clr, texture, alpha);
batching_add_bitmap_rotated_internal(batch, texture, pnt, angle, rad, &clr, depth);
}
void batching_add_distortion_bitmap_rotated(int texture, vertex *pnt, float angle, float rad, float alpha, float depth)
{
Assertion((texture >= 0), "batching_add_...() attempted for invalid texture");
if ( texture < 0 ) {
return;
}
if ( !gr_is_capable(gr_capability::CAPABILITY_DISTORTION) ) {
return;
}
primitive_batch *batch = batching_find_batch(texture, batch_info::DISTORTION);
color clr;
batching_determine_blend_color(&clr, texture, alpha);
batching_add_bitmap_rotated_internal(batch, texture, pnt, angle, rad, &clr, depth);
}
void batching_add_distortion_beam(int texture, vec3d *start, vec3d *end, float width, float intensity, float offset)
{
Assertion((texture >= 0), "batching_add_...() attempted for invalid texture");
if ( texture < 0 ) {
return;
}
if ( !gr_is_capable(gr_capability::CAPABILITY_DISTORTION) ) {
return;
}
primitive_batch *batch = batching_find_batch(texture, batch_info::DISTORTION, PRIM_TYPE_TRIS, true);
color clr;
batching_determine_blend_color(&clr, texture, intensity);
batching_add_beam_internal(batch, texture, start, end, width, &clr, offset);
}
void batching_add_beam(int texture, vec3d *start, vec3d *end, float width, float intensity)
{
Assertion((texture >= 0), "batching_add_...() attempted for invalid texture");
if ( texture < 0 ) {
return;
}
primitive_batch *batch = batching_find_batch(texture, batch_info::FLAT_EMISSIVE);
color clr;
batching_determine_blend_color(&clr, texture, intensity);
batching_add_beam_internal(batch, texture, start, end, width, &clr, 0.0f);
}
void batching_add_line(vec3d *start, vec3d *end, float widthStart, float widthEnd, color custom_color, bool translucent)
{
color clr = custom_color;
primitive_batch *batch;
//The widths can't be negative.
if (widthStart < 0)
{
mprintf(("Warning: Neither end of a 3D line can have negative width. Attempted to draw a line with %f widthStart. Changing value to zero.\n", widthStart));
widthStart = 0;
}
if (widthEnd < 0)
{
mprintf(("Warning: Neither end of a 3D line can have negative width. Attempted to draw a line with %f widthEnd. Changing value to zero.\n", widthEnd));
widthEnd = 0;
}
//A line can't have zero width on both ends of it. (But it can have zero width on only one of the ends at a time)
if (widthStart <= 0 && widthEnd <= 0)
{
mprintf(("Warning: The width of a 3D line should be > 0 on at least one tip. Attempted to draw a line with %f to %f. Line will not be drawn.\n", widthStart, widthEnd));
return;
}
if (lineTexture < 0)
{
//We only need a single pixel sized texture to render as many lines as we want.
//If it doesn't exist yet, then we make one!
auto previous_target = gr_screen.rendering_to_texture;
//The texture needs to be colored white, otherwise the line will render
//as black (or invisible if in translucent mode) no matter which color the user picks
lineTexture = bm_make_render_target(1, 1, BMP_FLAG_RENDER_TARGET_STATIC);
bm_set_render_target(lineTexture);
color temp = gr_screen.current_color; //Store our working color
color c;
gr_init_color(&c, 255, 255, 255);
gr_set_color_fast(&c); //set white as the working color
gr_pixel(0, 0, GR_RESIZE_NONE); //paint the pixel to the texture
gr_set_color_fast(&temp); //Reset our working color
bm_set_render_target(previous_target); //Reset our render target
}
if (translucent){
batch = batching_find_batch(lineTexture, batch_info::FLAT_EMISSIVE);
}
else {
batch = batching_find_batch(lineTexture, batch_info::FLAT_OPAQUE);
}
batching_add_line_internal(batch, lineTexture, start, end, widthStart, widthEnd, &clr);
}
void batching_add_laser(int texture, const vec3d *p0, float width1, const vec3d *p1, float width2, int r, int g, int b)
{
Assertion((texture >= 0), "batching_add_laser() attempted for invalid texture");
if ( texture < 0 ) {
return;
}
primitive_batch *batch = batching_find_batch(texture, batch_info::FLAT_EMISSIVE);
batching_add_laser_internal(batch, texture, p0, width1, p1, width2, r, g, b);
}
void batching_add_volume_polygon(int texture, const vec3d* pos, const matrix* orient, float width, float height, float alpha)
{
Assertion((texture >= 0), "batching_add_volume_polygon() attempted for invalid texture");
if (texture < 0) {
return;
}
primitive_batch* batch;
if (gr_is_capable(gr_capability::CAPABILITY_SOFT_PARTICLES)) {
batch = batching_find_batch(texture, batch_info::VOLUME_EMISSIVE);
} else {
batch = batching_find_batch(texture, batch_info::FLAT_EMISSIVE);
}
color clr;
batching_determine_blend_color(&clr, texture, alpha);
batching_add_polygon_internal(batch, texture, pos, orient, width, height, &clr);
}
void batching_add_polygon(int texture, vec3d *pos, matrix *orient, float width, float height, float alpha)
{
Assertion((texture >= 0), "batching_add_polygon() attempted for invalid texture");
if ( texture < 0 ) {
return;
}
primitive_batch *batch = batching_find_batch(texture, batch_info::FLAT_EMISSIVE);
color clr;
batching_determine_blend_color(&clr, texture, alpha);
batching_add_polygon_internal(batch, texture, pos, orient, width, height, &clr);
}
void batching_add_quad(int texture, vertex *verts, float trapezoidal_correction)
{
Assertion((texture >= 0), "batching_add_quad() attempted for invalid texture");
if ( texture < 0 ) {
return;
}
primitive_batch *batch = batching_find_batch(texture, batch_info::FLAT_EMISSIVE);
batching_add_quad_internal(batch, texture, verts, trapezoidal_correction);
}
void batching_add_quad(int texture, vertex *verts, primitive_batch *batch, float trapezoidal_correction)
{
Assertion((texture >= 0), "batching_add_quad() attempted for invalid texture");
if ( texture < 0 ) {
return;
}
batching_add_quad_internal(batch, texture, verts, trapezoidal_correction);
}
void batching_add_tri(int texture, vertex *verts)
{
Assertion((texture >= 0), "batching_add_tri() attempted for invalid texture");
if ( texture < 0 ) {
return;
}
primitive_batch *batch = batching_find_batch(texture, batch_info::FLAT_EMISSIVE);
batching_add_tri_internal(batch, texture, verts);
}
void batching_add_tri(int texture, vertex *verts, primitive_batch *batch)
{
Assertion((texture >= 0), "batching_add_tri() attempted for invalid texture");
if ( texture < 0 ) {
return;
}
batching_add_tri_internal(batch, texture, verts);
}
void batching_render_batch_item(primitive_batch_item* item,
vertex_layout* layout,
primitive_type prim_type,
gr_buffer_handle buffer_num)
{
GR_DEBUG_SCOPE("Batching render item");
TRACE_SCOPE(tracing::RenderBatchItem);
if ( item->batch_item_info.mat_type == batch_info::VOLUME_EMISSIVE ) { // Cmdline_softparticles
particle_material material_def;
material_set_unlit_volume(&material_def, item->batch_item_info.texture, prim_type == PRIM_TYPE_POINTS);
gr_render_primitives_particle(&material_def, prim_type, layout, (int)item->offset, (int)item->n_verts, buffer_num);
} else if ( item->batch_item_info.mat_type == batch_info::DISTORTION ) {
distortion_material material_def;
material_set_distortion(&material_def, item->batch_item_info.texture, item->batch_item_info.thruster);
gr_render_primitives_distortion(&material_def, PRIM_TYPE_TRIS, layout, (int)item->offset, (int)item->n_verts, buffer_num);
} else if ( item->batch_item_info.mat_type == batch_info::FLAT_OPAQUE) {
batched_bitmap_material material_def;
material_set_batched_opaque_bitmap(&material_def, item->batch_item_info.texture, 2.0f);
gr_render_primitives_batched(&material_def, PRIM_TYPE_TRIS, layout, (int)item->offset, (int)item->n_verts, buffer_num);
} else {
batched_bitmap_material material_def;
material_set_batched_bitmap(&material_def, item->batch_item_info.texture, 1.0f, 2.0f);
gr_render_primitives_batched(&material_def, PRIM_TYPE_TRIS, layout, (int)item->offset, (int)item->n_verts, buffer_num);
}
}
void batching_allocate_and_load_buffer(primitive_batch_buffer *draw_queue)
{
Assert(draw_queue != NULL);
if ( draw_queue->buffer_size < draw_queue->desired_buffer_size ) {
if ( draw_queue->buffer_ptr != NULL ) {
vm_free(draw_queue->buffer_ptr);
}
draw_queue->buffer_size = draw_queue->desired_buffer_size;
draw_queue->buffer_ptr = vm_malloc(draw_queue->desired_buffer_size);
}
draw_queue->desired_buffer_size = 0;
size_t offset = 0;
size_t num_items = draw_queue->items.size();
for ( size_t i = 0; i < num_items; ++i ) {
primitive_batch_item *item = &draw_queue->items[i];
item->offset = offset;
item->n_verts = item->batch->load_buffer((batch_vertex*)draw_queue->buffer_ptr, offset);
item->batch->clear();
offset += item->n_verts;
}
// if there are no items in this batch, we will never render it and thus there is no need to update it in vmem
if (num_items && draw_queue->buffer_num.isValid()) {
gr_update_buffer_data(draw_queue->buffer_num, draw_queue->buffer_size, draw_queue->buffer_ptr);
}
}
void batching_load_buffers(bool distortion)
{
GR_DEBUG_SCOPE("Batching load buffers");
TRACE_SCOPE(tracing::LoadBatchingBuffers);
for (auto &buffer_iter : Batching_buffers) {
// zero out the buffers
buffer_iter.second.desired_buffer_size = 0;
}
// assign primitive batch items
for (auto &bi : Batching_primitives) {
if ( bi.first.mat_type == batch_info::DISTORTION ) {
if ( !distortion ) {
continue;
}
} else {
if ( distortion ) {
continue;
}
}
size_t num_verts = bi.second.num_verts();
if ( num_verts > 0 ) {
batch_info render_info = bi.second.get_render_info();
uint vertex_mask = batching_determine_vertex_layout(&render_info);
primitive_batch_buffer *buffer = batching_find_buffer(vertex_mask, render_info.prim_type);
primitive_batch_item draw_item;
draw_item.batch_item_info = render_info;
draw_item.offset = 0;
draw_item.n_verts = num_verts;
draw_item.batch = &bi.second;
buffer->desired_buffer_size += num_verts * sizeof(batch_vertex);
buffer->items.push_back(draw_item);
}
}
for (auto &buffer_iter : Batching_buffers) {
batching_allocate_and_load_buffer(&buffer_iter.second);
}
}
void batching_render_buffer(primitive_batch_buffer *buffer)
{
GR_DEBUG_SCOPE("Batching render buffer");
TRACE_SCOPE(tracing::RenderBatchBuffer);
size_t num_batches = buffer->items.size();
for ( int j = 0; j < batch_info::NUM_RENDER_TYPES; ++j ) {
for ( size_t i = 0; i < num_batches; ++i ) {
if ( buffer->items[i].batch_item_info.mat_type != j ) {
continue;
}
batching_render_batch_item(&buffer->items[i], &buffer->layout, buffer->prim_type, buffer->buffer_num);
}
}
buffer->items.clear();
}
void batching_render_all(bool render_distortions)
{
GR_DEBUG_SCOPE("Batching render all");
TRACE_SCOPE(tracing::DrawEffects);
batching_load_buffers(render_distortions);
SCP_map<batch_buffer_key, primitive_batch_buffer>::iterator bi;
for ( bi = Batching_buffers.begin(); bi != Batching_buffers.end(); ++bi ) {
batching_render_buffer(&bi->second);
}
gr_clear_states();
}
void batching_shutdown()
{
for ( auto buffer_iter = Batching_buffers.begin(); buffer_iter != Batching_buffers.end(); ++buffer_iter ) {
primitive_batch_buffer *batch_buffer = &buffer_iter->second;
if ( batch_buffer->buffer_ptr != NULL ) {
vm_free(batch_buffer->buffer_ptr);
batch_buffer->buffer_ptr = nullptr;
}
}
}