medfall

A super great game engine
Log | Files | Refs

renderer.h (7947B)


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
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
#pragma once

#include "intrinsics.h"
#include "array.h"
#include "linear_algebra.h"

/*
 * forward declare things so only the implementation includes the full GL headers
 */

typedef u32 VB;
typedef u32 IB;
typedef u32 Shader;
typedef u32 Texture;
typedef u32 TextureBufferObject;
typedef u32 FramebufferObject;

const u32 UNIFORMS_VIEW = 0;
const u32 UNIFORMS_MODEL = 1;
const u32 UNIFORMS_LIGHT_VIEW = 2;
const u32 UNIFORMS_WINDOW = 3;
const u32 UNIFORMS_SUN = 4;
const u32 UNIFORMS_SKY = 5;
const u32 UNIFORMS_CLIPMAP = 6;

#define RENDERER_MAX_TEXTURES 4
#define RENDERER_MAX_TEXTURE_BUFFERS 4
#define RENDERER_MAX_UNIFORMS 8

const u32 INVALID_SHADER = 0;

enum CullFace {
	CULLFACE_BACK,
	CULLFACE_FRONT,
	CULLFACE_DISABLED,
};

enum DepthFunc {
	DEPTHFUNC_LESS,
	DEPTHFUNC_EQUAL,
	DEPTHFUNC_ALWAYS,
	DEPTHFUNC_DISABLED, // also disables writing
};

enum BufferUsage {
	BUFFERUSAGE_STATIC,
	BUFFERUSAGE_DYNAMIC, // TODO: remove dynamic?
	BUFFERUSAGE_STREAM,
};

enum PrimitiveType {
	PRIMITIVETYPE_TRIANGLES,
	PRIMITIVETYPE_TRIANGLE_STRIP,
	PRIMITIVETYPE_POINTS,
	PRIMITIVETYPE_LINES,
};

enum TextureFormat {
	TEXFMT_INVALID,
	TEXFMT_RGBA_FLOAT,
	TEXFMT_RGB_FLOAT,
	TEXFMT_R_FLOAT,
	TEXFMT_R_U8,
	TEXFMT_R_U8NORM,
	TEXFMT_R_U16,

	TEXFMT_DEPTH,

	TEXFMT_BC1,
	TEXFMT_BC3,
	TEXFMT_BC4,
	TEXFMT_BC5,
};

enum TextureWrapMode {
	TEXWRAP_REPEAT,
	TEXWRAP_CLAMP,
	TEXWRAP_MIRROR,
	TEXWRAP_BORDER,
};

enum TextureFilterMode {
	TEXFILTER_LINEAR,
	TEXFILTER_POINT,
};

enum FramebufferAttachment {
	FB_COLOUR,
	FB_DEPTH,
};

struct TB {
	TextureBufferObject tbo;
	Texture texture;
};

struct FB {
	FramebufferObject fbo;
	FramebufferAttachment attachment;
	Texture texture;
	u32 width, height;
};

struct UniformBinding {
	// TODO: might be able to make these u16
	u32 offset;
	u32 size;
};

struct RenderState {
	StaticArray< UniformBinding, RENDERER_MAX_UNIFORMS > uniforms = { };
	StaticArray< Texture, RENDERER_MAX_TEXTURES > textures = { };
	StaticArray< TB, RENDERER_MAX_TEXTURE_BUFFERS > tbs = { };
	Shader shader = INVALID_SHADER;
	DepthFunc depth_func = DEPTHFUNC_LESS;
	CullFace cull_face = CULLFACE_BACK;
	bool disable_depth_writes = false;
	bool disable_colour_writes = false;
	bool enable_alpha_blending = false;
	bool wireframe = false;
};

struct Mesh {
	u32 num_vertices;
	PrimitiveType primitive_type;
	u32 vao;
	VB positions;
	VB normals;
	VB tex_coords0;
	VB tex_coords1;
	VB colours;
	IB indices;
};

struct MeshConfig {
	MeshConfig() {
		positions = 0;
		normals = 0;
		tex_coords0 = 0;
		tex_coords1 = 0;
		colours = 0;
	}

	VB unified_buffer = 0;
	u32 stride = 0;
	union {
		struct {
			VB positions;
			VB normals;
			VB tex_coords0;
			VB tex_coords1;
			VB colours;
		};
		struct {
			u32 positions_offset;
			u32 normals_offset;
			u32 tex_coords0_offset;
			u32 tex_coords1_offset;
			u32 colours_offset;
		};
	};
	IB indices = 0;
	u32 num_vertices = 0;
	PrimitiveType primitive_type = PRIMITIVETYPE_TRIANGLES;
};

struct ShaderConfig {
	StaticArray< const char *, 8 > srcs = { };
	StaticArray< const char *, RENDERER_MAX_TEXTURES > texture_uniform_names = { };
	StaticArray< const char *, RENDERER_MAX_TEXTURE_BUFFERS > texture_buffer_uniform_names = { };
};

struct TextureConfig {
	u32 width = 0;
	u32 height = 0;

	const void * data = NULL;
	u32 data_size;

	TextureFormat format = TEXFMT_INVALID;
	TextureWrapMode wrap = TEXWRAP_REPEAT;
	TextureFilterMode filter = TEXFILTER_LINEAR;
	bool has_mipmaps = false;

	v4 border_colour;
	bool srgb = false; // TODO: this should be true by default. maybe needs enum TextureColorSpace too
	// TODO: or it should be merged with TextureFormat since only some formats make sense with sRGB
};

enum ClearColourBool { RENDERER_CLEAR_COLOUR_DONT, RENDERER_CLEAR_COLOUR_DO };
enum ClearDepthBool { RENDERER_CLEAR_DEPTH_DONT, RENDERER_CLEAR_DEPTH_DO };

void renderer_init();
void renderer_term();

void renderer_begin_frame();
void renderer_begin_pass( FB render_target, ClearColourBool clear_colour, ClearDepthBool clear_depth );
void renderer_begin_pass( ClearColourBool clear_colour, ClearDepthBool clear_depth );
void renderer_end_pass();
void renderer_end_frame();

u32 renderer_num_draw_calls();
u32 renderer_num_vertices();

Texture renderer_blue_noise();

UniformBinding renderer_upload_uniforms( const void * data, size_t size, size_t alignment );

// void renderer_begin_frame( ClearColourBool clear_colour = RENDERER_CLEAR_COLOUR_DO, ClearDepthBool clear_depth = RENDERER_CLEAR_DEPTH_DO );
// TODO: define render passes that have clear/undefined input framebuffers
#define renderer_clear renderer_begin_frame

VB renderer_new_vb( const void * data = NULL, u32 len = 0, BufferUsage usage = BUFFERUSAGE_STATIC );
void renderer_delete_vb( VB vb );

IB renderer_new_ib( const void * data = NULL, u32 len = 0, BufferUsage usage = BUFFERUSAGE_STATIC );
void renderer_delete_ib( IB ib );

TB renderer_new_tb( TextureFormat format, const void * data = NULL, u32 len = 0, BufferUsage usage = BUFFERUSAGE_DYNAMIC );
void renderer_tb_data( TB tb, const void * data, u32 len, BufferUsage usage = BUFFERUSAGE_DYNAMIC );
void renderer_delete_tb( TB tb );

FB renderer_new_fb( TextureConfig texture_format, FramebufferAttachment attachment );
void renderer_delete_fb( FB fb );

Shader renderer_new_shader( ShaderConfig config );
Shader renderer_new_shader( const char * src );
void renderer_delete_shader( Shader shader );

Texture renderer_new_texture( TextureConfig config );
void renderer_delete_texture( Texture texture );

Mesh renderer_new_mesh( MeshConfig config );
void renderer_draw_mesh( const Mesh & mesh, const RenderState & state );
void renderer_draw_instances( const Mesh & mesh, const RenderState & state, u32 num_instances, VB instace_data );
void renderer_delete_mesh( const Mesh & mesh );

/*
 * renderer_new_*( array ) helpers
 */

template< typename T >
VB renderer_new_vb( const array< T > data, BufferUsage usage = BUFFERUSAGE_STATIC ) {
	return renderer_new_vb( data.ptr(), checked_cast< u32 >( data.num_bytes() ), usage );
}

template< typename T >
VB renderer_new_ib( const array< T > data, BufferUsage usage = BUFFERUSAGE_STATIC ) {
	return renderer_new_ib( data.ptr(), checked_cast< u32 >( data.num_bytes() ), usage );
}

/*
 * renderer_uniform helper
 */

template< typename T >
constexpr size_t renderer_ubo_alignment() {
	return min( align4( sizeof( T ) ), 4 * sizeof( float ) );
}

template<>
constexpr size_t renderer_ubo_alignment< v3 >() {
	return 4 * sizeof( float );
}

template< typename T >
constexpr size_t renderer_ub_alignment() {
	return renderer_ubo_alignment< T >();
}

template< typename S, typename T, typename... Rest >
constexpr size_t renderer_ub_alignment() {
	return max( renderer_ub_alignment< T, Rest... >(), renderer_ubo_alignment< S >() );
}

template< typename T >
constexpr size_t renderer_uniforms_size( size_t size ) {
	return sizeof( T ) + align_power_of_2( size, renderer_ubo_alignment< T >() );
}

template< typename S, typename T, typename... Rest >
constexpr size_t renderer_uniforms_size( size_t size ) {
	return renderer_uniforms_size< T, Rest... >( sizeof( S ) + align_power_of_2( size, renderer_ubo_alignment< S >() ) );
}

inline void renderer_serialise_uniforms( char * buf, size_t len ) { }

template< typename T, typename... Rest >
inline void renderer_serialise_uniforms( char * buf, size_t len, const T & first, Rest... rest ) {
	len = align_power_of_2( len, renderer_ubo_alignment< T >() );
	memcpy( buf + len, &first, sizeof( first ) );
	renderer_serialise_uniforms( buf, len + sizeof( first ), rest... );
}

template< typename... Rest >
inline UniformBinding renderer_uniforms( Rest... rest ) {
	constexpr size_t buf_size = renderer_uniforms_size< Rest... >( 0 );
	constexpr size_t alignment = renderer_ub_alignment< Rest... >();
	char buf[ buf_size ];
	memset( buf, 0, sizeof( buf ) );
	renderer_serialise_uniforms( buf, 0, rest... );
	return renderer_upload_uniforms( buf, sizeof( buf ), alignment );
}