mirror of
https://github.com/phoboslab/wipeout-rewrite
synced 2024-12-27 09:59:24 +01:00
Add CRT post processing effect and allow for lower internal resolutions; see #1
This commit is contained in:
parent
7a9f757a79
commit
c0019b1bca
6 changed files with 505 additions and 156 deletions
18
src/render.h
18
src/render.h
|
@ -8,6 +8,18 @@ typedef enum {
|
||||||
RENDER_BLEND_LIGHTER
|
RENDER_BLEND_LIGHTER
|
||||||
} render_blend_mode_t;
|
} render_blend_mode_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
RENDER_RES_NATIVE,
|
||||||
|
RENDER_RES_240P,
|
||||||
|
RENDER_RES_480P,
|
||||||
|
} render_resolution_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
RENDER_POST_NONE,
|
||||||
|
RENDER_POST_CRT,
|
||||||
|
NUM_RENDER_POST_EFFCTS,
|
||||||
|
} render_post_effect_t;
|
||||||
|
|
||||||
#define RENDER_USE_MIPMAPS 1
|
#define RENDER_USE_MIPMAPS 1
|
||||||
|
|
||||||
#define RENDER_FADEOUT_NEAR 48000.0
|
#define RENDER_FADEOUT_NEAR 48000.0
|
||||||
|
@ -15,10 +27,12 @@ typedef enum {
|
||||||
|
|
||||||
extern uint16_t RENDER_NO_TEXTURE;
|
extern uint16_t RENDER_NO_TEXTURE;
|
||||||
|
|
||||||
void render_init(vec2i_t size);
|
void render_init(vec2i_t screen_size);
|
||||||
void render_cleanup();
|
void render_cleanup();
|
||||||
|
|
||||||
void render_resize(vec2i_t size);
|
void render_set_screen_size(vec2i_t size);
|
||||||
|
void render_set_resolution(render_resolution_t res);
|
||||||
|
void render_set_post_effect(render_post_effect_t post);
|
||||||
vec2i_t render_size();
|
vec2i_t render_size();
|
||||||
|
|
||||||
void render_frame_prepare();
|
void render_frame_prepare();
|
||||||
|
|
593
src/render_gl.c
593
src/render_gl.c
|
@ -19,6 +19,10 @@
|
||||||
void glCreateTextures(GLuint ignored, GLsizei n, GLuint *name) {
|
void glCreateTextures(GLuint ignored, GLsizei n, GLuint *name) {
|
||||||
glGenTextures(1, name);
|
glGenTextures(1, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void glFramebufferTexture(GLenum target, GLenum attachment, GLuint texture, GLint level) {
|
||||||
|
glFramebufferTexture2D(target, attachment, GL_TEXTURE_2D, texture, level);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// WINDOWS
|
// WINDOWS
|
||||||
|
@ -37,14 +41,12 @@
|
||||||
|
|
||||||
#include "libs/stb_image_write.h"
|
#include "libs/stb_image_write.h"
|
||||||
|
|
||||||
|
#include "system.h"
|
||||||
#include "render.h"
|
#include "render.h"
|
||||||
#include "mem.h"
|
#include "mem.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
|
|
||||||
#define NEAR_PLANE 16.0
|
|
||||||
#define FAR_PLANE 262144.0
|
|
||||||
|
|
||||||
#define ATLAS_SIZE 64
|
#define ATLAS_SIZE 64
|
||||||
#define ATLAS_GRID 32
|
#define ATLAS_GRID 32
|
||||||
#define ATLAS_BORDER 16
|
#define ATLAS_BORDER 16
|
||||||
|
@ -52,12 +54,24 @@
|
||||||
#define RENDER_TRIS_BUFFER_CAPACITY 2048
|
#define RENDER_TRIS_BUFFER_CAPACITY 2048
|
||||||
#define TEXTURES_MAX 1024
|
#define TEXTURES_MAX 1024
|
||||||
|
|
||||||
// WebGL (GLES) needs the `precision` to be set, OpenGL 2.something
|
|
||||||
// doesn't like that...
|
|
||||||
#ifdef __EMSCRIPTEN__
|
#ifdef __EMSCRIPTEN__
|
||||||
|
// WebGL (GLES) needs the `precision` to be set, wheras OpenGL 2
|
||||||
|
// doesn't like that...
|
||||||
#define SHADER_SOURCE(...) "precision highp float;" #__VA_ARGS__
|
#define SHADER_SOURCE(...) "precision highp float;" #__VA_ARGS__
|
||||||
|
|
||||||
|
// WebGL1 only allows for a 16 bit depth buffer attachment, so
|
||||||
|
// we sacrifice a bit of the near plane to get more precision
|
||||||
|
// further out
|
||||||
|
#define NEAR_PLANE 128.0
|
||||||
|
#define FAR_PLANE (RENDER_FADEOUT_FAR)
|
||||||
|
#define RENDER_DEPTH_BUFFER_INTERNAL_FORMAT GL_DEPTH_COMPONENT16
|
||||||
#else
|
#else
|
||||||
#define SHADER_SOURCE(...) #__VA_ARGS__
|
#define SHADER_SOURCE(...) #__VA_ARGS__
|
||||||
|
|
||||||
|
#define NEAR_PLANE 16.0
|
||||||
|
#define FAR_PLANE (RENDER_FADEOUT_FAR)
|
||||||
|
#define RENDER_DEPTH_BUFFER_INTERNAL_FORMAT GL_DEPTH_COMPONENT24
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
@ -68,98 +82,24 @@ typedef struct {
|
||||||
|
|
||||||
uint16_t RENDER_NO_TEXTURE;
|
uint16_t RENDER_NO_TEXTURE;
|
||||||
|
|
||||||
static GLuint u_color;
|
#define use_program(SHADER) \
|
||||||
static GLuint u_view;
|
glUseProgram((SHADER)->program); \
|
||||||
static GLuint u_model;
|
glBindVertexArray((SHADER)->vao);
|
||||||
static GLuint u_projection;
|
|
||||||
static GLuint u_screen;
|
|
||||||
static GLuint u_camera_pos;
|
|
||||||
static GLuint u_fade;
|
|
||||||
|
|
||||||
static GLuint a_pos;
|
#define bind_va_f(index, container, member, start) \
|
||||||
static GLuint a_uv;
|
|
||||||
static GLuint a_color;
|
|
||||||
|
|
||||||
static GLuint vbo;
|
|
||||||
|
|
||||||
static tris_t tris_buffer[RENDER_TRIS_BUFFER_CAPACITY];
|
|
||||||
static uint32_t tris_len = 0;
|
|
||||||
|
|
||||||
static vec2i_t screen_size;
|
|
||||||
|
|
||||||
static uint32_t atlas_map[ATLAS_SIZE] = {0};
|
|
||||||
static GLuint atlas_texture = 0;
|
|
||||||
static render_blend_mode_t blend_mode = RENDER_BLEND_NORMAL;
|
|
||||||
|
|
||||||
static mat4_t projection_mat_2d = mat4_identity();
|
|
||||||
static mat4_t projection_mat_3d = mat4_identity();
|
|
||||||
static mat4_t sprite_mat = mat4_identity();
|
|
||||||
static mat4_t view_mat = mat4_identity();
|
|
||||||
|
|
||||||
|
|
||||||
static render_texture_t textures[TEXTURES_MAX];
|
|
||||||
static uint32_t textures_len = 0;
|
|
||||||
static bool texture_mipmap_is_dirty = false;
|
|
||||||
|
|
||||||
static const char * const VERTEX_SHADER = SHADER_SOURCE(
|
|
||||||
attribute vec3 pos;
|
|
||||||
attribute vec2 uv;
|
|
||||||
attribute vec4 color;
|
|
||||||
|
|
||||||
varying vec4 v_color;
|
|
||||||
varying vec2 v_uv;
|
|
||||||
uniform mat4 view;
|
|
||||||
uniform mat4 model;
|
|
||||||
uniform mat4 projection;
|
|
||||||
uniform vec2 screen;
|
|
||||||
uniform vec3 camera_pos;
|
|
||||||
uniform vec2 fade;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
gl_Position = projection * view * model * vec4(pos, 1.0);
|
|
||||||
gl_Position.xy += screen.xy * gl_Position.w;
|
|
||||||
v_color = color;
|
|
||||||
v_color.a *= smoothstep(
|
|
||||||
fade.y, fade.x, // fadeout far, near
|
|
||||||
length(vec4(camera_pos, 1.0) - model * vec4(pos, 1.0))
|
|
||||||
);
|
|
||||||
v_uv = uv;
|
|
||||||
v_uv = uv / 2048.0; // ATLAS_GRID * ATLAS_SIZE
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
static const char * const FRAGMENT_SHADER_YCRCB = SHADER_SOURCE(
|
|
||||||
varying vec4 v_color;
|
|
||||||
varying vec2 v_uv;
|
|
||||||
uniform sampler2D texture;
|
|
||||||
void main() {
|
|
||||||
vec4 tex_color = texture2D(texture, v_uv);
|
|
||||||
vec4 color = tex_color * v_color;
|
|
||||||
if (color.a == 0.0) {
|
|
||||||
discard;
|
|
||||||
}
|
|
||||||
color.rgb = color.rgb * 2.0;
|
|
||||||
gl_FragColor = color;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
#define render_bind_va_f(index, container, member, start) \
|
|
||||||
glVertexAttribPointer( \
|
glVertexAttribPointer( \
|
||||||
index, member_size(container, member)/sizeof(float), GL_FLOAT, false, \
|
index, member_size(container, member)/sizeof(float), GL_FLOAT, false, \
|
||||||
sizeof(container), \
|
sizeof(container), \
|
||||||
(GLvoid*)(offsetof(container, member) + start) \
|
(GLvoid*)(offsetof(container, member) + start) \
|
||||||
)
|
)
|
||||||
|
|
||||||
#define render_bind_va_color(index, container, member, start) \
|
#define bind_va_color(index, container, member, start) \
|
||||||
glVertexAttribPointer( \
|
glVertexAttribPointer( \
|
||||||
index, 4, GL_UNSIGNED_BYTE, true, \
|
index, 4, GL_UNSIGNED_BYTE, true, \
|
||||||
sizeof(container), \
|
sizeof(container), \
|
||||||
(GLvoid*)(offsetof(container, member) + start) \
|
(GLvoid*)(offsetof(container, member) + start) \
|
||||||
)
|
)
|
||||||
|
|
||||||
static void render_flush();
|
|
||||||
static GLuint compile_shader(GLenum type, const char *source);
|
|
||||||
|
|
||||||
static GLuint compile_shader(GLenum type, const char *source) {
|
static GLuint compile_shader(GLenum type, const char *source) {
|
||||||
GLuint shader = glCreateShader(type);
|
GLuint shader = glCreateShader(type);
|
||||||
|
@ -177,11 +117,308 @@ static GLuint compile_shader(GLenum type, const char *source) {
|
||||||
return shader;
|
return shader;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static GLuint create_program(const char *vs_source, const char *fs_source) {
|
||||||
|
GLuint vs = compile_shader(GL_VERTEX_SHADER, vs_source);
|
||||||
|
GLuint fs = compile_shader(GL_FRAGMENT_SHADER, fs_source);
|
||||||
|
|
||||||
|
GLuint program = glCreateProgram();
|
||||||
|
glAttachShader(program, vs);
|
||||||
|
glAttachShader(program, fs);
|
||||||
|
glLinkProgram(program);
|
||||||
|
|
||||||
|
GLint linked;
|
||||||
|
glGetProgramiv(program, GL_LINK_STATUS, &linked);
|
||||||
|
if (!linked) {
|
||||||
|
#ifdef CRTEMU_REPORT_SHADER_ERRORS
|
||||||
|
char error_message[256];
|
||||||
|
strcpy(error_message, prefix);
|
||||||
|
int len = 0, written = 0;
|
||||||
|
glGetShaderiv(vs, GL_INFO_LOG_LENGTH, &len);
|
||||||
|
glGetShaderInfoLog(programm, (GLsizei)( sizeof(error_message)), &written, error_message);
|
||||||
|
die("Shader Link Error: %s" error_message);
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
glUseProgram(program);
|
||||||
|
return program;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Main game shaders
|
||||||
|
|
||||||
|
static const char * const SHADER_GAME_VS = SHADER_SOURCE(
|
||||||
|
attribute vec3 pos;
|
||||||
|
attribute vec2 uv;
|
||||||
|
attribute vec4 color;
|
||||||
|
|
||||||
|
varying vec4 v_color;
|
||||||
|
varying vec2 v_uv;
|
||||||
|
uniform mat4 view;
|
||||||
|
uniform mat4 model;
|
||||||
|
uniform mat4 projection;
|
||||||
|
uniform vec2 screen;
|
||||||
|
uniform vec3 camera_pos;
|
||||||
|
uniform vec2 fade;
|
||||||
|
uniform float time;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
gl_Position = projection * view * model * vec4(pos, 1.0);
|
||||||
|
gl_Position.xy += screen.xy * gl_Position.w;
|
||||||
|
v_color = color;
|
||||||
|
v_color.a *= smoothstep(
|
||||||
|
fade.y, fade.x, // fadeout far, near
|
||||||
|
length(vec4(camera_pos, 1.0) - model * vec4(pos, 1.0))
|
||||||
|
);
|
||||||
|
v_uv = uv;
|
||||||
|
v_uv = uv / 2048.0; // ATLAS_GRID * ATLAS_SIZE
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
static const char * const SHADER_GAME_FS = SHADER_SOURCE(
|
||||||
|
varying vec4 v_color;
|
||||||
|
varying vec2 v_uv;
|
||||||
|
uniform sampler2D texture;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec4 tex_color = texture2D(texture, v_uv);
|
||||||
|
vec4 color = tex_color * v_color;
|
||||||
|
if (color.a == 0.0) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
color.rgb = color.rgb * 2.0;
|
||||||
|
gl_FragColor = color;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
GLuint program;
|
||||||
|
GLuint vao;
|
||||||
|
struct {
|
||||||
|
GLuint view;
|
||||||
|
GLuint model;
|
||||||
|
GLuint projection;
|
||||||
|
GLuint screen;
|
||||||
|
GLuint camera_pos;
|
||||||
|
GLuint fade;
|
||||||
|
GLuint time;
|
||||||
|
} uniform;
|
||||||
|
struct {
|
||||||
|
GLuint pos;
|
||||||
|
GLuint uv;
|
||||||
|
GLuint color;
|
||||||
|
} attribute;
|
||||||
|
} prg_game_t;
|
||||||
|
|
||||||
|
prg_game_t *shader_game_init() {
|
||||||
|
prg_game_t *s = mem_bump(sizeof(prg_game_t));
|
||||||
|
|
||||||
|
s->program = create_program(SHADER_GAME_VS, SHADER_GAME_FS);
|
||||||
|
|
||||||
|
s->uniform.view = glGetUniformLocation(s->program, "view");
|
||||||
|
s->uniform.model = glGetUniformLocation(s->program, "model");
|
||||||
|
s->uniform.projection = glGetUniformLocation(s->program, "projection");
|
||||||
|
s->uniform.screen = glGetUniformLocation(s->program, "screen");
|
||||||
|
s->uniform.camera_pos = glGetUniformLocation(s->program, "camera_pos");
|
||||||
|
s->uniform.fade = glGetUniformLocation(s->program, "fade");
|
||||||
|
|
||||||
|
s->attribute.pos = glGetAttribLocation(s->program, "pos");
|
||||||
|
s->attribute.uv = glGetAttribLocation(s->program, "uv");
|
||||||
|
s->attribute.color = glGetAttribLocation(s->program, "color");
|
||||||
|
|
||||||
|
glGenVertexArrays(1, &s->vao);
|
||||||
|
glBindVertexArray(s->vao);
|
||||||
|
|
||||||
|
glEnableVertexAttribArray(s->attribute.pos);
|
||||||
|
glEnableVertexAttribArray(s->attribute.uv);
|
||||||
|
glEnableVertexAttribArray(s->attribute.color);
|
||||||
|
|
||||||
|
bind_va_f(s->attribute.pos, vertex_t, pos, 0);
|
||||||
|
bind_va_f(s->attribute.uv, vertex_t, uv, 0);
|
||||||
|
bind_va_color(s->attribute.color, vertex_t, color, 0);
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// POST Effect shaders
|
||||||
|
|
||||||
|
static const char * const SHADER_POST_VS = SHADER_SOURCE(
|
||||||
|
attribute vec3 pos;
|
||||||
|
attribute vec2 uv;
|
||||||
|
|
||||||
|
varying vec2 v_uv;
|
||||||
|
|
||||||
|
uniform mat4 projection;
|
||||||
|
uniform vec2 screen_size;
|
||||||
|
uniform float time;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
gl_Position = projection * vec4(pos, 1.0);
|
||||||
|
v_uv = uv;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
static const char * const SHADER_POST_FS_DEFAULT = SHADER_SOURCE(
|
||||||
|
varying vec2 v_uv;
|
||||||
|
|
||||||
|
uniform sampler2D texture;
|
||||||
|
uniform vec2 screen_size;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
gl_FragColor = texture2D(texture, v_uv);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// CRT effect based on https://www.shadertoy.com/view/Ms23DR
|
||||||
|
// by https://github.com/mattiasgustavsson/
|
||||||
|
static const char * const SHADER_POST_FS_CRT = SHADER_SOURCE(
|
||||||
|
varying vec2 v_uv;
|
||||||
|
|
||||||
|
uniform float time;
|
||||||
|
uniform sampler2D texture;
|
||||||
|
uniform vec2 screen_size;
|
||||||
|
|
||||||
|
vec2 curve(vec2 uv) {
|
||||||
|
uv = (uv - 0.5) * 2.0;
|
||||||
|
uv *= 1.1;
|
||||||
|
uv.x *= 1.0 + pow((abs(uv.y) / 5.0), 2.0);
|
||||||
|
uv.y *= 1.0 + pow((abs(uv.x) / 4.0), 2.0);
|
||||||
|
uv = (uv / 2.0) + 0.5;
|
||||||
|
uv = uv *0.92 + 0.04;
|
||||||
|
return uv;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main(){
|
||||||
|
vec2 uv = curve(gl_FragCoord.xy / screen_size);
|
||||||
|
vec3 color;
|
||||||
|
float x = sin(0.3*time+uv.y*21.0)*sin(0.7*time+uv.y*29.0)*sin(0.3+0.33*time+uv.y*31.0)*0.0017;
|
||||||
|
|
||||||
|
color.r = texture2D(texture, vec2(x+uv.x+0.001,uv.y+0.001)).x+0.05;
|
||||||
|
color.g = texture2D(texture, vec2(x+uv.x+0.000,uv.y-0.002)).y+0.05;
|
||||||
|
color.b = texture2D(texture, vec2(x+uv.x-0.002,uv.y+0.000)).z+0.05;
|
||||||
|
color.r += 0.08*texture2D(texture, 0.75*vec2(x+0.025, -0.027)+vec2(uv.x+0.001,uv.y+0.001)).x;
|
||||||
|
color.g += 0.05*texture2D(texture, 0.75*vec2(x+-0.022, -0.02)+vec2(uv.x+0.000,uv.y-0.002)).y;
|
||||||
|
color.b += 0.08*texture2D(texture, 0.75*vec2(x+-0.02, -0.018)+vec2(uv.x-0.002,uv.y+0.000)).z;
|
||||||
|
|
||||||
|
color = clamp(color*0.6+0.4*color*color*1.0,0.0,1.0);
|
||||||
|
|
||||||
|
float vignette = (0.0 + 1.0*16.0*uv.x*uv.y*(1.0-uv.x)*(1.0-uv.y));
|
||||||
|
color *= vec3(pow(vignette, 0.25));
|
||||||
|
|
||||||
|
color *= vec3(0.95,1.05,0.95);
|
||||||
|
color *= 2.8;
|
||||||
|
|
||||||
|
float scanlines = clamp( 0.35+0.35*sin(3.5*time+uv.y*screen_size.y*1.5), 0.0, 1.0);
|
||||||
|
|
||||||
|
float s = pow(scanlines,1.7);
|
||||||
|
color = color * vec3(0.4+0.7*s);
|
||||||
|
|
||||||
|
color *= 1.0+0.01*sin(110.0*time);
|
||||||
|
if (uv.x < 0.0 || uv.x > 1.0)
|
||||||
|
color *= 0.0;
|
||||||
|
if (uv.y < 0.0 || uv.y > 1.0)
|
||||||
|
color *= 0.0;
|
||||||
|
|
||||||
|
color*=1.0-0.65*vec3(clamp((mod(gl_FragCoord.x, 2.0)-1.0)*2.0,0.0,1.0));
|
||||||
|
gl_FragColor = vec4(color,1.0);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
GLuint program;
|
||||||
|
GLuint vao;
|
||||||
|
struct {
|
||||||
|
GLuint projection;
|
||||||
|
GLuint screen_size;
|
||||||
|
GLuint time;
|
||||||
|
} uniform;
|
||||||
|
struct {
|
||||||
|
GLuint pos;
|
||||||
|
GLuint uv;
|
||||||
|
} attribute;
|
||||||
|
} prg_post_t;
|
||||||
|
|
||||||
|
void shader_post_general_init(prg_post_t *s) {
|
||||||
|
s->uniform.projection = glGetUniformLocation(s->program, "projection");
|
||||||
|
s->uniform.screen_size = glGetUniformLocation(s->program, "screen_size");
|
||||||
|
s->uniform.time = glGetUniformLocation(s->program, "time");
|
||||||
|
|
||||||
|
s->attribute.pos = glGetAttribLocation(s->program, "pos");
|
||||||
|
s->attribute.uv = glGetAttribLocation(s->program, "uv");
|
||||||
|
|
||||||
|
glGenVertexArrays(1, &s->vao);
|
||||||
|
glBindVertexArray(s->vao);
|
||||||
|
|
||||||
|
glEnableVertexAttribArray(s->attribute.pos);
|
||||||
|
glEnableVertexAttribArray(s->attribute.uv);
|
||||||
|
|
||||||
|
bind_va_f(s->attribute.pos, vertex_t, pos, 0);
|
||||||
|
bind_va_f(s->attribute.uv, vertex_t, uv, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
prg_post_t *shader_post_default_init() {
|
||||||
|
prg_post_t *s = mem_bump(sizeof(prg_post_t));
|
||||||
|
s->program = create_program(SHADER_POST_VS, SHADER_POST_FS_DEFAULT);
|
||||||
|
shader_post_general_init(s);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
prg_post_t *shader_post_crt_init() {
|
||||||
|
prg_post_t *s = mem_bump(sizeof(prg_post_t));
|
||||||
|
s->program = create_program(SHADER_POST_VS, SHADER_POST_FS_CRT);
|
||||||
|
shader_post_general_init(s);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
static GLuint vbo;
|
||||||
|
|
||||||
|
static tris_t tris_buffer[RENDER_TRIS_BUFFER_CAPACITY];
|
||||||
|
static uint32_t tris_len = 0;
|
||||||
|
|
||||||
|
static vec2i_t screen_size;
|
||||||
|
static vec2i_t backbuffer_size;
|
||||||
|
|
||||||
|
static uint32_t atlas_map[ATLAS_SIZE] = {0};
|
||||||
|
static GLuint atlas_texture = 0;
|
||||||
|
static render_blend_mode_t blend_mode = RENDER_BLEND_NORMAL;
|
||||||
|
|
||||||
|
static mat4_t projection_mat_2d = mat4_identity();
|
||||||
|
static mat4_t projection_mat_bb = mat4_identity();
|
||||||
|
static mat4_t projection_mat_3d = mat4_identity();
|
||||||
|
static mat4_t sprite_mat = mat4_identity();
|
||||||
|
static mat4_t view_mat = mat4_identity();
|
||||||
|
|
||||||
|
|
||||||
|
static render_texture_t textures[TEXTURES_MAX];
|
||||||
|
static uint32_t textures_len = 0;
|
||||||
|
static bool texture_mipmap_is_dirty = false;
|
||||||
|
|
||||||
|
static render_resolution_t render_res;
|
||||||
|
static GLuint backbuffer = 0;
|
||||||
|
static GLuint backbuffer_texture = 0;
|
||||||
|
static GLuint backbuffer_depth_buffer = 0;
|
||||||
|
|
||||||
|
prg_game_t *prg_game;
|
||||||
|
prg_post_t *prg_post;
|
||||||
|
prg_post_t *prg_post_effects[NUM_RENDER_POST_EFFCTS] = {};
|
||||||
|
|
||||||
|
|
||||||
|
static void render_flush();
|
||||||
|
|
||||||
|
|
||||||
// static void gl_message_callback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *userParam) {
|
// static void gl_message_callback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *userParam) {
|
||||||
// puts(message);
|
// puts(message);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
void render_init(vec2i_t size) {
|
void render_init(vec2i_t screen_size) {
|
||||||
#if defined(__APPLE__) && defined(__MACH__)
|
#if defined(__APPLE__) && defined(__MACH__)
|
||||||
// OSX
|
// OSX
|
||||||
// (nothing to do here)
|
// (nothing to do here)
|
||||||
|
@ -215,50 +452,23 @@ void render_init(vec2i_t size) {
|
||||||
printf("atlas texture %5d\n", atlas_texture);
|
printf("atlas texture %5d\n", atlas_texture);
|
||||||
|
|
||||||
|
|
||||||
// Shaders
|
|
||||||
|
|
||||||
GLuint fragment_shader = compile_shader(GL_FRAGMENT_SHADER, FRAGMENT_SHADER_YCRCB);
|
|
||||||
GLuint vertex_shader = compile_shader(GL_VERTEX_SHADER, VERTEX_SHADER);
|
|
||||||
GLuint shader_program = glCreateProgram();
|
|
||||||
|
|
||||||
glAttachShader(shader_program, vertex_shader);
|
|
||||||
glAttachShader(shader_program, fragment_shader);
|
|
||||||
glLinkProgram(shader_program);
|
|
||||||
glUseProgram(shader_program);
|
|
||||||
|
|
||||||
u_color = glGetUniformLocation(shader_program, "color");
|
|
||||||
u_view = glGetUniformLocation(shader_program, "view");
|
|
||||||
u_model = glGetUniformLocation(shader_program, "model");
|
|
||||||
u_projection = glGetUniformLocation(shader_program, "projection");
|
|
||||||
u_screen = glGetUniformLocation(shader_program, "screen");
|
|
||||||
u_camera_pos = glGetUniformLocation(shader_program, "camera_pos");
|
|
||||||
u_fade = glGetUniformLocation(shader_program, "fade");
|
|
||||||
|
|
||||||
a_pos = glGetAttribLocation(shader_program, "pos");
|
|
||||||
a_uv = glGetAttribLocation(shader_program, "uv");
|
|
||||||
a_color = glGetAttribLocation(shader_program, "color");
|
|
||||||
|
|
||||||
// Tris buffer
|
// Tris buffer
|
||||||
|
|
||||||
glGenBuffers(1, &vbo);
|
glGenBuffers(1, &vbo);
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
||||||
|
|
||||||
GLuint va;
|
|
||||||
glGenVertexArrays(1, &va);
|
|
||||||
glBindVertexArray(va);
|
|
||||||
|
|
||||||
|
// Post Shaders
|
||||||
|
|
||||||
// Defaults
|
prg_post_effects[RENDER_POST_NONE] = shader_post_default_init();
|
||||||
|
prg_post_effects[RENDER_POST_CRT] = shader_post_crt_init();
|
||||||
|
render_set_post_effect(RENDER_POST_NONE);
|
||||||
|
|
||||||
glEnableVertexAttribArray(a_pos);
|
// Game shader
|
||||||
glEnableVertexAttribArray(a_uv);
|
|
||||||
glEnableVertexAttribArray(a_color);
|
|
||||||
|
|
||||||
render_bind_va_f(a_pos, vertex_t, pos, 0);
|
prg_game = shader_game_init();
|
||||||
render_bind_va_f(a_uv, vertex_t, uv, 0);
|
use_program(prg_game);
|
||||||
render_bind_va_color(a_color, vertex_t, color, 0);
|
|
||||||
|
|
||||||
render_resize(size);
|
|
||||||
render_set_view(vec3(0, 0, 0), vec3(0, 0, 0));
|
render_set_view(vec3(0, 0, 0), vec3(0, 0, 0));
|
||||||
render_set_model_mat(&mat4_identity());
|
render_set_model_mat(&mat4_identity());
|
||||||
|
|
||||||
|
@ -274,6 +484,12 @@ void render_init(vec2i_t size) {
|
||||||
rgba(128,128,128,255), rgba(128,128,128,255)
|
rgba(128,128,128,255), rgba(128,128,128,255)
|
||||||
};
|
};
|
||||||
RENDER_NO_TEXTURE = render_texture_create(2, 2, white_pixels);
|
RENDER_NO_TEXTURE = render_texture_create(2, 2, white_pixels);
|
||||||
|
|
||||||
|
|
||||||
|
// Backbuffer
|
||||||
|
|
||||||
|
render_res = RENDER_RES_NATIVE;
|
||||||
|
render_set_screen_size(screen_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void render_cleanup() {
|
void render_cleanup() {
|
||||||
|
@ -281,17 +497,17 @@ void render_cleanup() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void render_setup_2d_projection_mat() {
|
static mat4_t render_setup_2d_projection_mat(vec2i_t size) {
|
||||||
float near = -1;
|
float near = -1;
|
||||||
float far = 1;
|
float far = 1;
|
||||||
float left = 0;
|
float left = 0;
|
||||||
float right = screen_size.x;
|
float right = size.x;
|
||||||
float bottom = screen_size.y;
|
float bottom = size.y;
|
||||||
float top = 0;
|
float top = 0;
|
||||||
float lr = 1 / (left - right);
|
float lr = 1 / (left - right);
|
||||||
float bt = 1 / (bottom - top);
|
float bt = 1 / (bottom - top);
|
||||||
float nf = 1 / (near - far);
|
float nf = 1 / (near - far);
|
||||||
projection_mat_2d = mat4(
|
return mat4(
|
||||||
-2 * lr, 0, 0, 0,
|
-2 * lr, 0, 0, 0,
|
||||||
0, -2 * bt, 0, 0,
|
0, -2 * bt, 0, 0,
|
||||||
0, 0, 2 * nf, 0,
|
0, 0, 2 * nf, 0,
|
||||||
|
@ -299,16 +515,16 @@ static void render_setup_2d_projection_mat() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void render_setup_3d_projection_mat() {
|
static mat4_t render_setup_3d_projection_mat(vec2i_t size) {
|
||||||
// wipeout has a horizontal fov of 90deg, but we want the fov to be fixed
|
// wipeout has a horizontal fov of 90deg, but we want the fov to be fixed
|
||||||
// for the vertical axis, so that widescreen displays just have a wider
|
// for the vertical axis, so that widescreen displays just have a wider
|
||||||
// view. For the original 4/3 aspect ratio this equates to a vertial fov
|
// view. For the original 4/3 aspect ratio this equates to a vertial fov
|
||||||
// of 73.75deg.
|
// of 73.75deg.
|
||||||
float aspect = (float)screen_size.x / (float)screen_size.y;
|
float aspect = (float)size.x / (float)size.y;
|
||||||
float fov = (73.75 / 180.0) * 3.14159265358;
|
float fov = (73.75 / 180.0) * 3.14159265358;
|
||||||
float f = 1.0 / tan(fov / 2);
|
float f = 1.0 / tan(fov / 2);
|
||||||
float nf = 1.0 / (NEAR_PLANE - FAR_PLANE);
|
float nf = 1.0 / (NEAR_PLANE - FAR_PLANE);
|
||||||
projection_mat_3d = mat4(
|
return mat4(
|
||||||
f / aspect, 0, 0, 0,
|
f / aspect, 0, 0, 0,
|
||||||
0, f, 0, 0,
|
0, f, 0, 0,
|
||||||
0, 0, (FAR_PLANE + NEAR_PLANE) * nf, -1,
|
0, 0, (FAR_PLANE + NEAR_PLANE) * nf, -1,
|
||||||
|
@ -316,20 +532,85 @@ static void render_setup_3d_projection_mat() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void render_resize(vec2i_t size) {
|
void render_set_screen_size(vec2i_t size) {
|
||||||
glViewport(0, 0, size.x, size.y);
|
|
||||||
screen_size = size;
|
screen_size = size;
|
||||||
|
projection_mat_bb = render_setup_2d_projection_mat(screen_size);
|
||||||
|
|
||||||
render_setup_2d_projection_mat();
|
render_set_resolution(render_res);
|
||||||
render_setup_3d_projection_mat();
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void render_set_resolution(render_resolution_t res) {
|
||||||
|
render_res = res;
|
||||||
|
|
||||||
|
if (res == RENDER_RES_NATIVE) {
|
||||||
|
backbuffer_size = screen_size;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
float aspect = (float)screen_size.x / (float)screen_size.y;
|
||||||
|
if (res == RENDER_RES_240P) {
|
||||||
|
backbuffer_size = vec2i(240.0 * aspect, 240);
|
||||||
|
}
|
||||||
|
else if (res == RENDER_RES_480P) {
|
||||||
|
backbuffer_size = vec2i(480.0 * aspect, 480);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
die("Invalid resolution: %d", res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!backbuffer) {
|
||||||
|
glGenTextures(1, &backbuffer_texture);
|
||||||
|
glGenFramebuffers(1, &backbuffer);
|
||||||
|
glGenRenderbuffers(1, &backbuffer_depth_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
glBindTexture(GL_TEXTURE_2D, backbuffer_texture);
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, backbuffer_size.x, backbuffer_size.y, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||||
|
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, backbuffer);
|
||||||
|
glBindRenderbuffer(GL_RENDERBUFFER, backbuffer_depth_buffer);
|
||||||
|
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, backbuffer_depth_buffer);
|
||||||
|
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, backbuffer_texture, 0);
|
||||||
|
|
||||||
|
glBindRenderbuffer(GL_RENDERBUFFER, backbuffer_depth_buffer);
|
||||||
|
glRenderbufferStorage(GL_RENDERBUFFER, RENDER_DEPTH_BUFFER_INTERNAL_FORMAT, backbuffer_size.x, backbuffer_size.y);
|
||||||
|
|
||||||
|
projection_mat_2d = render_setup_2d_projection_mat(backbuffer_size);
|
||||||
|
projection_mat_3d = render_setup_3d_projection_mat(backbuffer_size);
|
||||||
|
|
||||||
|
|
||||||
|
// Use nearest texture min filter for 240p and 480p
|
||||||
|
glBindTexture(GL_TEXTURE_2D, atlas_texture);
|
||||||
|
if (res == RENDER_RES_NATIVE) {
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, RENDER_USE_MIPMAPS ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||||
|
}
|
||||||
|
glViewport(0, 0, backbuffer_size.x, backbuffer_size.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void render_set_post_effect(render_post_effect_t post) {
|
||||||
|
error_if(post < 0 || post > NUM_RENDER_POST_EFFCTS, "Invalid post effect %d", post);
|
||||||
|
prg_post = prg_post_effects[post];
|
||||||
}
|
}
|
||||||
|
|
||||||
vec2i_t render_size() {
|
vec2i_t render_size() {
|
||||||
return screen_size;
|
return backbuffer_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
void render_frame_prepare() {
|
void render_frame_prepare() {
|
||||||
glUniform2f(u_screen, 0, 0);
|
use_program(prg_game);
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, backbuffer);
|
||||||
|
glViewport(0, 0, backbuffer_size.x, backbuffer_size.y);
|
||||||
|
|
||||||
|
glBindTexture(GL_TEXTURE_2D, atlas_texture);
|
||||||
|
glUniform2f(prg_game->uniform.screen, 0, 0);
|
||||||
glEnable(GL_DEPTH_TEST);
|
glEnable(GL_DEPTH_TEST);
|
||||||
glDepthMask(true);
|
glDepthMask(true);
|
||||||
glDisable(GL_POLYGON_OFFSET_FILL);
|
glDisable(GL_POLYGON_OFFSET_FILL);
|
||||||
|
@ -340,6 +621,36 @@ void render_frame_prepare() {
|
||||||
|
|
||||||
void render_frame_end() {
|
void render_frame_end() {
|
||||||
render_flush();
|
render_flush();
|
||||||
|
|
||||||
|
use_program(prg_post);
|
||||||
|
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
|
glViewport(0, 0, screen_size.x, screen_size.y);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, backbuffer_texture);
|
||||||
|
glUniformMatrix4fv(prg_post->uniform.projection, 1, false, projection_mat_bb.m);
|
||||||
|
glUniform1f(prg_post->uniform.time, system_cycle_time());
|
||||||
|
glUniform2f(prg_post->uniform.screen_size, screen_size.x, screen_size.y);
|
||||||
|
|
||||||
|
glClearColor(0, 0, 0, 1);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||||
|
|
||||||
|
rgba_t white = rgba(128,128,128,255);
|
||||||
|
tris_buffer[tris_len++] = (tris_t){
|
||||||
|
.vertices = {
|
||||||
|
{.pos = {0, screen_size.y, 0}, .uv = {0, 0}, .color = white},
|
||||||
|
{.pos = {screen_size.x, 0, 0}, .uv = {1, 1}, .color = white},
|
||||||
|
{.pos = {0, 0, 0}, .uv = {0, 1}, .color = white},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
tris_buffer[tris_len++] = (tris_t){
|
||||||
|
.vertices = {
|
||||||
|
{.pos = {screen_size.x, screen_size.y, 0}, .uv = {1, 0}, .color = white},
|
||||||
|
{.pos = {screen_size.x, 0, 0}, .uv = {1, 1}, .color = white},
|
||||||
|
{.pos = {0, screen_size.y, 0}, .uv = {0, 0}, .color = white},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
render_flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
void render_flush() {
|
void render_flush() {
|
||||||
|
@ -373,10 +684,10 @@ void render_set_view(vec3_t pos, vec3_t angles) {
|
||||||
render_set_model_mat(&mat4_identity());
|
render_set_model_mat(&mat4_identity());
|
||||||
|
|
||||||
render_flush();
|
render_flush();
|
||||||
glUniformMatrix4fv(u_view, 1, false, view_mat.m);
|
glUniformMatrix4fv(prg_game->uniform.view, 1, false, view_mat.m);
|
||||||
glUniformMatrix4fv(u_projection, 1, false, projection_mat_3d.m);
|
glUniformMatrix4fv(prg_game->uniform.projection, 1, false, projection_mat_3d.m);
|
||||||
glUniform3f(u_camera_pos, pos.x, pos.y, pos.z);
|
glUniform3f(prg_game->uniform.camera_pos, pos.x, pos.y, pos.z);
|
||||||
glUniform2f(u_fade, RENDER_FADEOUT_NEAR, RENDER_FADEOUT_FAR);
|
glUniform2f(prg_game->uniform.fade, RENDER_FADEOUT_NEAR, RENDER_FADEOUT_FAR);
|
||||||
}
|
}
|
||||||
|
|
||||||
void render_set_view_2d() {
|
void render_set_view_2d() {
|
||||||
|
@ -385,14 +696,14 @@ void render_set_view_2d() {
|
||||||
render_set_depth_write(false);
|
render_set_depth_write(false);
|
||||||
|
|
||||||
render_set_model_mat(&mat4_identity());
|
render_set_model_mat(&mat4_identity());
|
||||||
glUniform3f(u_camera_pos, 0, 0, 0);
|
glUniform3f(prg_game->uniform.camera_pos, 0, 0, 0);
|
||||||
glUniformMatrix4fv(u_view, 1, false, mat4_identity().m);
|
glUniformMatrix4fv(prg_game->uniform.view, 1, false, mat4_identity().m);
|
||||||
glUniformMatrix4fv(u_projection, 1, false, projection_mat_2d.m);
|
glUniformMatrix4fv(prg_game->uniform.projection, 1, false, projection_mat_2d.m);
|
||||||
}
|
}
|
||||||
|
|
||||||
void render_set_model_mat(mat4_t *m) {
|
void render_set_model_mat(mat4_t *m) {
|
||||||
render_flush();
|
render_flush();
|
||||||
glUniformMatrix4fv(u_model, 1, false, m->m);
|
glUniformMatrix4fv(prg_game->uniform.model, 1, false, m->m);
|
||||||
}
|
}
|
||||||
|
|
||||||
void render_set_depth_write(bool enabled) {
|
void render_set_depth_write(bool enabled) {
|
||||||
|
@ -423,7 +734,7 @@ void render_set_depth_offset(float offset) {
|
||||||
|
|
||||||
void render_set_screen_position(vec2_t pos) {
|
void render_set_screen_position(vec2_t pos) {
|
||||||
render_flush();
|
render_flush();
|
||||||
glUniform2f(u_screen, pos.x, -pos.y);
|
glUniform2f(prg_game->uniform.screen, pos.x, -pos.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
void render_set_blend_mode(render_blend_mode_t new_mode) {
|
void render_set_blend_mode(render_blend_mode_t new_mode) {
|
||||||
|
|
|
@ -57,7 +57,7 @@ void system_reset_cycle_time() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void system_resize(vec2i_t size) {
|
void system_resize(vec2i_t size) {
|
||||||
render_resize(size);
|
render_set_screen_size(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
double system_time_scale_get() {
|
double system_time_scale_get() {
|
||||||
|
|
|
@ -399,6 +399,8 @@ save_t save = {
|
||||||
.ui_scale = 0,
|
.ui_scale = 0,
|
||||||
.show_fps = false,
|
.show_fps = false,
|
||||||
.fullscreen = false,
|
.fullscreen = false,
|
||||||
|
.screen_res = 0,
|
||||||
|
.post_effect = 0,
|
||||||
|
|
||||||
.has_rapier_class = true, // for testing; should be false in prod
|
.has_rapier_class = true, // for testing; should be false in prod
|
||||||
.has_bonus_circuts = true, // for testing; should be false in prod
|
.has_bonus_circuts = true, // for testing; should be false in prod
|
||||||
|
@ -488,6 +490,20 @@ static int global_textures_len = 0;
|
||||||
static void *global_mem_mark = 0;
|
static void *global_mem_mark = 0;
|
||||||
|
|
||||||
void game_init() {
|
void game_init() {
|
||||||
|
if (file_exists("save.dat")) {
|
||||||
|
uint32_t size;
|
||||||
|
save_t *save_file = (save_t *)file_load("save.dat", &size);
|
||||||
|
if (size == sizeof(save_t) && save_file->magic == SAVE_DATA_MAGIC) {
|
||||||
|
printf("load save data success\n");
|
||||||
|
memcpy(&save, save_file, sizeof(save_t));
|
||||||
|
}
|
||||||
|
mem_temp_free(save_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
platform_set_fullscreen(save.fullscreen);
|
||||||
|
render_set_resolution(save.screen_res);
|
||||||
|
render_set_post_effect(save.post_effect);
|
||||||
|
|
||||||
srand((int)(platform_now() * 100));
|
srand((int)(platform_now() * 100));
|
||||||
|
|
||||||
ui_load();
|
ui_load();
|
||||||
|
@ -574,16 +590,6 @@ void game_init() {
|
||||||
|
|
||||||
|
|
||||||
game_set_scene(GAME_SCENE_INTRO);
|
game_set_scene(GAME_SCENE_INTRO);
|
||||||
|
|
||||||
if (file_exists("save.dat")) {
|
|
||||||
uint32_t size;
|
|
||||||
save_t *save_file = (save_t *)file_load("save.dat", &size);
|
|
||||||
if (size == sizeof(save_t) && save_file->magic == SAVE_DATA_MAGIC) {
|
|
||||||
printf("load save data success\n");
|
|
||||||
memcpy(&save, save_file, sizeof(save_t));
|
|
||||||
}
|
|
||||||
mem_temp_free(save_file);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void game_set_scene(game_scene_t scene) {
|
void game_set_scene(game_scene_t scene) {
|
||||||
|
|
|
@ -242,6 +242,8 @@ typedef struct {
|
||||||
uint8_t ui_scale;
|
uint8_t ui_scale;
|
||||||
bool show_fps;
|
bool show_fps;
|
||||||
bool fullscreen;
|
bool fullscreen;
|
||||||
|
int screen_res;
|
||||||
|
int post_effect;
|
||||||
|
|
||||||
uint32_t has_rapier_class;
|
uint32_t has_rapier_class;
|
||||||
uint32_t has_bonus_circuts;
|
uint32_t has_bonus_circuts;
|
||||||
|
|
|
@ -156,8 +156,22 @@ static void toggle_ui_scale(menu_t *menu, int data) {
|
||||||
save.is_dirty = true;
|
save.is_dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void toggle_res(menu_t *menu, int data) {
|
||||||
|
render_set_resolution(data);
|
||||||
|
save.screen_res = data;
|
||||||
|
save.is_dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void toggle_post(menu_t *menu, int data) {
|
||||||
|
render_set_post_effect(data);
|
||||||
|
save.post_effect = data;
|
||||||
|
save.is_dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
static const char *opts_off_on[] = {"OFF", "ON"};
|
static const char *opts_off_on[] = {"OFF", "ON"};
|
||||||
static const char *opts_ui_sizes[] = {"AUTO", "1X", "2X", "3X", "4X"};
|
static const char *opts_ui_sizes[] = {"AUTO", "1X", "2X", "3X", "4X"};
|
||||||
|
static const char *opts_res[] = {"NATIVE", "240P", "480P"};
|
||||||
|
static const char *opts_post[] = {"NONE", "CRT EFFECT"};
|
||||||
|
|
||||||
static void page_options_video_init(menu_t *menu) {
|
static void page_options_video_init(menu_t *menu) {
|
||||||
menu_page_t *page = menu_push(menu, "VIDEO OPTIONS", NULL);
|
menu_page_t *page = menu_push(menu, "VIDEO OPTIONS", NULL);
|
||||||
|
@ -173,6 +187,8 @@ static void page_options_video_init(menu_t *menu) {
|
||||||
#endif
|
#endif
|
||||||
menu_page_add_toggle(page, save.ui_scale, "UI SCALE", opts_ui_sizes, len(opts_ui_sizes), toggle_ui_scale);
|
menu_page_add_toggle(page, save.ui_scale, "UI SCALE", opts_ui_sizes, len(opts_ui_sizes), toggle_ui_scale);
|
||||||
menu_page_add_toggle(page, save.show_fps, "SHOW FPS", opts_off_on, len(opts_off_on), toggle_show_fps);
|
menu_page_add_toggle(page, save.show_fps, "SHOW FPS", opts_off_on, len(opts_off_on), toggle_show_fps);
|
||||||
|
menu_page_add_toggle(page, save.screen_res, "SCREEN RESOLUTION", opts_res, len(opts_res), toggle_res);
|
||||||
|
menu_page_add_toggle(page, save.post_effect, "POST PROCESSING", opts_post, len(opts_post), toggle_post);
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
Loading…
Reference in a new issue