prosperon/source/engine/render.c

560 lines
13 KiB
C
Raw Normal View History

#include "render.h"
2021-11-30 21:29:18 -06:00
2023-05-12 13:22:05 -05:00
#include "config.h"
#include "datastream.h"
2021-11-30 21:29:18 -06:00
#include "debugdraw.h"
2023-05-12 13:22:05 -05:00
#include "font.h"
#include "gameobject.h"
2021-11-30 21:29:18 -06:00
#include "log.h"
2023-05-12 13:22:05 -05:00
#include "sprite.h"
2023-12-29 19:08:53 -06:00
#include "particle.h"
2023-05-12 13:22:05 -05:00
#include "window.h"
#include "model.h"
#include "stb_ds.h"
#include "resources.h"
2023-09-12 00:02:57 -05:00
#include "yugine.h"
#include "sokol/sokol_app.h"
2024-03-03 16:49:34 -06:00
#define SOKOL_GLUE_IMPL
#include "sokol/sokol_glue.h"
2023-11-22 03:51:43 -06:00
#include "stb_image_write.h"
2023-05-12 13:22:05 -05:00
#include "box.sglsl.h"
#include "shadow.sglsl.h"
2023-09-11 15:07:36 -05:00
#include "sokol/sokol_gfx.h"
2024-03-03 16:49:34 -06:00
#include "sokol_gfx_ext.h"
2023-09-12 00:02:57 -05:00
2023-09-11 15:07:36 -05:00
#include "msf_gif.h"
2023-09-12 00:02:57 -05:00
static struct {
2024-03-03 16:49:34 -06:00
sg_swapchain swap;
2023-09-12 00:02:57 -05:00
sg_pipeline pipe;
sg_bindings bind;
sg_shader shader;
sg_image img;
sg_image depth;
} sg_gif;
2023-09-12 07:56:40 -05:00
static struct {
int w;
int h;
int cpf;
int depth;
double timer;
double spf;
int rec;
uint8_t *buffer;
2023-09-12 07:56:40 -05:00
} gif;
2023-09-11 15:07:36 -05:00
MsfGifState gif_state = {};
2023-09-12 00:02:57 -05:00
void gif_rec_start(int w, int h, int cpf, int bitdepth)
2023-09-11 15:07:36 -05:00
{
2023-09-12 07:56:40 -05:00
gif.w = w;
gif.h = h;
gif.depth = bitdepth;
msf_gif_begin(&gif_state, gif.w, gif.h);
gif.cpf = cpf;
gif.spf = cpf/100.0;
gif.rec = 1;
gif.timer = apptime();
2023-09-12 07:56:40 -05:00
if (gif.buffer) free(gif.buffer);
gif.buffer = malloc(gif.w*gif.h*4);
2023-09-12 00:02:57 -05:00
sg_destroy_image(sg_gif.img);
sg_destroy_image(sg_gif.depth);
sg_gif.img = sg_make_image(&(sg_image_desc){
.render_target = true,
2023-09-12 07:56:40 -05:00
.width = gif.w,
.height = gif.h,
.pixel_format = SG_PIXELFORMAT_RGBA8,
2023-09-25 08:21:02 -05:00
.label = "gif rt",
2023-09-12 00:02:57 -05:00
});
sg_gif.depth = sg_make_image(&(sg_image_desc){
.render_target = true,
2023-09-12 07:56:40 -05:00
.width = gif.w,
.height = gif.h,
2023-09-25 08:21:02 -05:00
.label = "gif depth",
2023-09-12 00:02:57 -05:00
});
2024-03-03 16:49:34 -06:00
sg_gif.swap = sglue_swapchain();
2023-09-11 15:07:36 -05:00
}
void gif_rec_end(const char *path)
2023-09-11 15:07:36 -05:00
{
if (!gif.rec) return;
2023-09-12 07:56:40 -05:00
MsfGifResult gif_res = msf_gif_end(&gif_state);
if (gif_res.data) {
2023-09-11 15:07:36 -05:00
FILE *f = fopen(path, "wb");
2023-09-12 07:56:40 -05:00
fwrite(gif_res.data, gif_res.dataSize, 1, f);
2023-09-11 15:07:36 -05:00
fclose(f);
}
2023-09-12 07:56:40 -05:00
msf_gif_free(gif_res);
gif.rec = 0;
2023-09-11 15:07:36 -05:00
}
void capture_screen(int x, int y, int w, int h, const char *path)
2023-11-22 03:51:43 -06:00
{
int n = 4;
void *data = malloc(w*h*n);
sg_query_pixels(0,0,w,h,1,data,w*h*sizeof(char)*n);
// sg_query_image_pixels(crt_post.img, crt_post.bind.fs.samplers[0], data, w*h*4);
stbi_write_png("cap.png", w, h, n, data, n*w);
// stbi_write_bmp("cap.bmp", w, h, n, data);
free(data);
}
#include "sokol/sokol_app.h"
2023-05-12 13:22:05 -05:00
#include "HandmadeMath.h"
2022-12-22 16:58:06 -06:00
2023-02-16 16:13:07 -06:00
int renderMode = LIT;
2021-11-30 21:29:18 -06:00
2022-11-19 17:13:57 -06:00
struct shader *spriteShader = NULL;
2023-02-16 16:13:07 -06:00
struct shader *wireframeShader = NULL;
2022-11-19 17:13:57 -06:00
struct shader *animSpriteShader = NULL;
static struct shader *textShader;
2021-11-30 21:29:18 -06:00
2023-05-24 20:45:50 -05:00
struct rgba editorClearColor = {35,60,92,255};
2021-11-30 21:29:18 -06:00
float shadowLookahead = 8.5f;
2023-05-24 20:45:50 -05:00
struct rgba gridSmallColor = {
.r = 255 * 0.35f,
.g = 255,
.b = 255 * 0.9f
};
2021-11-30 21:29:18 -06:00
2023-05-24 20:45:50 -05:00
struct rgba gridBigColor = {
.r = 255 * 0.92f,
.g = 255 * 0.92f,
.b = 255 * 0.68f
};
2021-11-30 21:29:18 -06:00
float gridScale = 500.f;
float smallGridUnit = 1.f;
float bigGridUnit = 10.f;
float gridSmallThickness = 2.f;
float gridBigThickness = 7.f;
float gridOpacity = 0.3f;
// Debug render modes
bool renderGizmos = false;
bool showGrid = true;
bool debugDrawPhysics = false;
bool renderNav = false;
// Lighting effect flags
bool renderAO = true;
bool renderDynamicShadows = true;
bool renderRefraction = true;
bool renderReflection = true;
///// for editing
2022-11-19 17:13:57 -06:00
struct gameobject *selectedobject = NULL;
2023-05-12 13:22:05 -05:00
char objectName[200] = {'\0'}; // object name buffer
2021-11-30 21:29:18 -06:00
2023-05-12 13:22:05 -05:00
sg_image ddimg;
void debug_draw_phys(int draw) {
2023-05-12 13:22:05 -05:00
debugDrawPhysics = draw;
}
2023-05-12 13:22:05 -05:00
void opengl_rendermode(enum RenderMode r) {
2023-02-16 16:13:07 -06:00
renderMode = r;
}
2023-05-04 17:07:00 -05:00
sg_pipeline mainpip;
sg_pass_action pass_action = {0};
2023-05-12 13:22:05 -05:00
static struct {
sg_pass_action pass_action;
sg_pass pass;
sg_pipeline pipe;
sg_shader shader;
} sg_shadow;
2024-03-14 09:33:15 -05:00
void trace_make_image(const sg_image_desc *d, sg_image id, void *data)
{
2024-03-14 09:33:15 -05:00
YughSpam("Made image %s.", d->label);
}
void trace_init_image(sg_image id, const sg_image_desc *d, void *data)
{
2024-03-14 09:33:15 -05:00
YughSpam("Init image %s", d->label);
}
2023-09-12 00:02:57 -05:00
2024-03-14 09:33:15 -05:00
void trace_make_shader(const sg_shader_desc *d, sg_shader id, void *data)
2023-05-24 20:45:50 -05:00
{
2024-03-14 09:33:15 -05:00
YughSpam("Making shader %s", d->label);
if (sg_query_shader_state(id) == SG_RESOURCESTATE_FAILED)
YughError("FAILED MAKING A SHADER: %s\n%s\n%s", d->label);
2023-05-24 20:45:50 -05:00
}
void trace_fail_shader(sg_shader id, void *data)
2023-05-24 20:45:50 -05:00
{
2024-03-14 09:33:15 -05:00
YughError("Shader %u did not compile.", id);
2023-06-28 11:35:41 -05:00
}
2024-03-14 09:33:15 -05:00
void trace_destroy_shader(sg_shader id, void *data)
2023-06-28 11:35:41 -05:00
{
2024-03-14 09:33:15 -05:00
YughSpam("Destroyed shader %u.", id);
2023-05-24 20:45:50 -05:00
}
2023-09-25 08:21:02 -05:00
void trace_fail_image(sg_image id, void *data)
{
sg_image_desc desc = sg_query_image_desc(id);
2024-03-14 09:33:15 -05:00
YughError("Failed to make image %u %s", id, desc.label);
}
2024-03-14 09:33:15 -05:00
void trace_make_pipeline(const sg_pipeline_desc *d, sg_pipeline id, void *data)
{
2024-03-14 09:33:15 -05:00
YughSpam("Making pipeline %u [%s].", id, d->label);
}
void trace_apply_pipeline(sg_pipeline pip, void *data)
{
// YughSpam("Applying pipeline %u %s.", pip, sg_query_pipeline_desc(pip).label);
}
void trace_fail_pipeline(sg_pipeline pip, void *data)
{
YughError("Failed pipeline %s", sg_query_pipeline_desc(pip).label);
}
2024-03-03 16:49:34 -06:00
void trace_make_attachments(const sg_attachment_desc *d, sg_attachments result, void *data)
{
2024-03-14 09:33:15 -05:00
YughSpam("Making attachments %s", "IMPLEMENT");
}
void trace_begin_pass(sg_pass pass, const sg_pass_action *action, void *data)
{
// YughSpam("Begin pass %s", pass.label);
2023-09-25 08:21:02 -05:00
}
2023-05-24 20:45:50 -05:00
static sg_trace_hooks hooks = {
.fail_shader = trace_fail_shader,
.make_shader = trace_make_shader,
.destroy_shader = trace_destroy_shader,
.fail_image = trace_fail_image,
.make_image = trace_make_image,
.init_image = trace_init_image,
.make_pipeline = trace_make_pipeline,
.fail_pipeline = trace_fail_pipeline,
.apply_pipeline = trace_apply_pipeline,
.begin_pass = trace_begin_pass,
2024-03-03 16:49:34 -06:00
.make_attachments = trace_make_attachments,
2023-05-24 20:45:50 -05:00
};
void render_init() {
2024-03-13 16:30:55 -05:00
mainwin.size = (HMM_Vec2){sapp_width(), sapp_height()};
sg_setup(&(sg_desc){
2024-03-03 16:49:34 -06:00
.environment = sglue_environment(),
.logger = { .func = sg_logging },
.buffer_pool_size = 1024
});
2023-05-24 20:45:50 -05:00
sg_trace_hooks hh = sg_install_trace_hooks(&hooks);
font_init();
2023-05-12 13:22:05 -05:00
debugdraw_init();
sprite_initialize();
model_init();
2023-05-24 20:45:50 -05:00
sg_color c;
rgba2floats((float*)&c, editorClearColor);
2024-03-03 16:49:34 -06:00
2023-05-12 13:22:05 -05:00
pass_action = (sg_pass_action){
2024-03-03 16:49:34 -06:00
.colors[0] = {.load_action = SG_LOADACTION_CLEAR, .clear_value = c},
2023-05-12 13:22:05 -05:00
};
sg_gif.shader = sg_make_shader(box_shader_desc(sg_query_backend()));
2023-09-12 00:02:57 -05:00
sg_gif.pipe = sg_make_pipeline(&(sg_pipeline_desc){
.shader = sg_gif.shader,
.layout = {
.attrs = {
[0].format = SG_VERTEXFORMAT_FLOAT2,
[1].format = SG_VERTEXFORMAT_FLOAT2
}
},
.colors[0].pixel_format = SG_PIXELFORMAT_RGBA8,
.label = "gif pipe",
2023-09-12 00:02:57 -05:00
});
#if defined SOKOL_GLCORE33 || defined SOKOL_GLES3
2023-05-12 13:22:05 -05:00
float crt_quad[] = {
-1, 1, 0, 1,
-1, -1, 0, 0,
1, -1, 1, 0,
-1, 1, 0, 1,
1, -1, 1, 0,
1, 1, 1, 1
};
#else
float crt_quad[] = {
-1, 1, 0, 0,
-1, -1, 0, 1,
1, -1, 1, 1,
-1, 1, 0, 0,
1, -1, 1, 1,
1, 1, 1, 0
};
#endif
float gif_quad[] = {
-1, 1, 0, 1,
-1, -1, 0, 0,
1, -1, 1, 0,
-1, 1, 0, 1,
1, -1, 1, 0,
1, 1, 1, 1
};
sg_gif.bind.vertex_buffers[0] = sg_make_buffer(&(sg_buffer_desc){
.size = sizeof(gif_quad),
.data = gif_quad,
});
sg_gif.bind.fs.samplers[0] = sg_make_sampler(&(sg_sampler_desc){});
2023-05-12 13:22:05 -05:00
2023-05-04 17:07:00 -05:00
/*
2023-05-12 13:22:05 -05:00
sg_image_desc shadow_desc = {
.render_target = true,
.width = 1024,
.height = 1024,
.pixel_format = SG_PIXELFORMAT_R32F,
};
sg_image depth_img = sg_make_image(&shadow_desc);
shadow_desc.pixel_format = sapp_depth_format();
2023-05-12 13:22:05 -05:00
ddimg = sg_make_image(&shadow_desc);
sg_shadow.pass = sg_make_pass(&(sg_pass_desc){
.color_attachments[0].image = depth_img,
.depth_stencil_attachment.image = ddimg,
});
sg_shadow.pass_action = (sg_pass_action) {
.colors[0] = { .action=SG_ACTION_CLEAR, .value = {1,1,1,1} } };
sg_shadow.shader = sg_make_shader(shadow_shader_desc(sg_query_backend()));
2023-05-12 13:22:05 -05:00
sg_shadow.pipe = sg_make_pipeline(&(sg_pipeline_desc){
.shader = sg_shadow.shader,
.layout = {
.attrs = {
[0].format = SG_VERTEXFORMAT_FLOAT3,
}
},
.depth = {
.compare = SG_COMPAREFUNC_LESS_EQUAL,
.write_enabled = true,
.pixel_format = sapp_depth_format()
2023-05-12 13:22:05 -05:00
},
.colors[0].pixel_format = SG_PIXELFORMAT_R32F,
.index_type = SG_INDEXTYPE_UINT16,
.cull_mode = SG_CULLMODE_BACK,
});
2023-05-04 17:07:00 -05:00
*/
2023-05-24 20:45:50 -05:00
}
2022-12-22 16:58:06 -06:00
static cpBody *camera = NULL;
2023-12-18 17:12:05 -06:00
void set_cam_body(cpBody *body) { camera = body; }
cpVect cam_pos() { return camera ? cpBodyGetPosition(camera) : cpvzero; }
2023-05-12 13:22:05 -05:00
2022-12-22 16:58:06 -06:00
static float zoom = 1.f;
2023-02-20 16:28:07 -06:00
float cam_zoom() { return zoom; }
2022-12-22 16:58:06 -06:00
void add_zoom(float val) { zoom = val; }
HMM_Vec2 world2screen(HMM_Vec2 pos)
{
pos = HMM_SubV2(pos, HMM_V2(cam_pos().x, cam_pos().y));
pos = HMM_ScaleV2(pos, 1.0/zoom);
2024-03-13 16:30:55 -05:00
pos = HMM_AddV2(pos, HMM_ScaleV2(mainwin.size,0.5));
return pos;
}
HMM_Vec2 screen2world(HMM_Vec2 pos)
{
pos = HMM_ScaleV2(pos, 1/mainwin.dpi);
2024-03-13 16:30:55 -05:00
pos = HMM_SubV2(pos, HMM_ScaleV2(mainwin.size, 0.5));
pos = HMM_ScaleV2(pos, zoom);
pos = HMM_AddV2(pos, HMM_V2(cam_pos().x, cam_pos().y));
return pos;
}
2023-05-24 20:45:50 -05:00
HMM_Mat4 projection = {0.f};
HMM_Mat4 hudproj = {0.f};
2023-05-04 17:07:00 -05:00
2023-05-12 13:22:05 -05:00
HMM_Vec3 dirl_pos = {4, 100, 20};
2022-06-30 10:31:23 -05:00
#define MODE_STRETCH 0
#define MODE_KEEP 1
#define MODE_WIDTH 2
#define MODE_HEIGHT 3
#define MODE_EXPAND 4
#define MODE_FULL 5
2023-09-12 00:02:57 -05:00
void full_2d_pass(struct window *window)
{
2024-03-13 16:30:55 -05:00
HMM_Vec2 usesize = window->rendersize;
switch(window->mode) {
case MODE_STRETCH:
2024-03-13 16:30:55 -05:00
sg_apply_viewportf(0,0,window->size.x,window->size.y,1);
break;
case MODE_WIDTH:
2024-03-13 16:30:55 -05:00
sg_apply_viewportf(0, window->top, window->size.x, window->psize.y,1); // keep width
break;
case MODE_HEIGHT:
2024-03-13 16:30:55 -05:00
sg_apply_viewportf(window->left,0,window->psize.x, window->size.y,1); // keep height
break;
case MODE_KEEP:
2024-03-13 16:30:55 -05:00
sg_apply_viewportf(0,0,window->rendersize.x, window->rendersize.y, 1); // no scaling
break;
case MODE_EXPAND:
2024-03-13 16:30:55 -05:00
if (window->aspect < window->raspect)
sg_apply_viewportf(0, window->top, window->size.x, window->psize.y,1); // keep width
else
2024-03-13 16:30:55 -05:00
sg_apply_viewportf(window->left,0,window->psize.x, window->size.y,1); // keep height
break;
case MODE_FULL:
2024-03-13 16:30:55 -05:00
usesize = window->size;
2024-03-09 18:22:06 -06:00
break;
}
// 2D projection
2023-05-12 13:22:05 -05:00
cpVect pos = cam_pos();
2023-12-04 13:38:37 -06:00
projection = HMM_Orthographic_LH_NO(
2024-03-13 16:30:55 -05:00
pos.x - zoom * usesize.x / 2,
pos.x + zoom * usesize.x / 2,
pos.y - zoom * usesize.y / 2,
pos.y + zoom * usesize.y / 2, -10000.f, 10000.f);
2024-03-13 16:30:55 -05:00
hudproj = HMM_Orthographic_LH_ZO(0, usesize.x, 0, usesize.y, -1.f, 1.f);
2023-05-12 13:22:05 -05:00
sprite_draw_all();
2023-12-20 17:20:29 -06:00
model_draw_all();
2024-03-11 22:23:02 -05:00
script_evalf("prosperon.draw();");
2023-12-29 19:08:53 -06:00
emitters_draw();
2023-05-12 13:22:05 -05:00
//// DEBUG
2023-12-12 19:35:34 -06:00
if (debugDrawPhysics) {
gameobject_draw_debugs();
2024-03-11 22:23:02 -05:00
script_evalf("prosperon.debug();");
2023-12-12 19:35:34 -06:00
}
2023-12-11 08:36:45 -06:00
2023-06-05 10:32:45 -05:00
debug_flush(&projection);
text_flush(&projection);
2023-05-24 20:45:50 -05:00
2023-05-12 13:22:05 -05:00
////// TEXT && GUI
2023-06-05 10:32:45 -05:00
debug_nextpass();
2024-03-11 22:23:02 -05:00
script_evalf("prosperon.gui();");
2023-06-05 10:32:45 -05:00
debug_flush(&hudproj);
2023-06-28 11:35:41 -05:00
text_flush(&hudproj);
sprite_flush();
2023-09-12 00:02:57 -05:00
}
2023-05-12 13:22:05 -05:00
2023-09-12 00:02:57 -05:00
void full_3d_pass(struct window *window)
{
HMM_Mat4 model = HMM_M4D(1.f);
float scale = 0.08;
model = HMM_MulM4(model, HMM_Scale((HMM_Vec3){scale,scale,scale}));
// Shadow pass
2023-11-21 17:05:06 -06:00
// sg_begin_pass(sg_shadow.pass, &sg_shadow.pass_action);
// sg_apply_pipeline(sg_shadow.pipe);
2023-09-12 00:02:57 -05:00
HMM_Mat4 light_proj = HMM_Orthographic_RH_ZO(-100.f, 100.f, -100.f, 100.f, 1.f, 100.f);
HMM_Mat4 light_view = HMM_LookAt_RH(dirl_pos, (HMM_Vec3){0,0,0}, (HMM_Vec3){0,1,0});
HMM_Mat4 lsm = HMM_MulM4(light_proj, light_view);
HMM_Mat4 subo[2];
subo[0] = lsm;
subo[1] = model;
sg_apply_uniforms(SG_SHADERSTAGE_VS, 0, SG_RANGE_REF(subo));
}
void openglRender(struct window *window) {
sg_swapchain sch = sglue_swapchain();
2024-03-03 16:49:34 -06:00
sg_begin_pass(&(sg_pass){
.action = pass_action,
.swapchain = sglue_swapchain(),
.label = "window pass"
});
2023-09-12 00:02:57 -05:00
full_2d_pass(window);
2023-05-12 13:22:05 -05:00
sg_end_pass();
2024-03-03 16:49:34 -06:00
/* if (gif.rec && (apptime() - gif.timer) > gif.spf) {
sg_begin_pass(&(sg_pass){
.action = pass_action,
.swapchain = sg_gif.swap
});
2023-09-12 00:02:57 -05:00
sg_apply_pipeline(sg_gif.pipe);
sg_apply_bindings(&sg_gif.bind);
2023-09-12 00:02:57 -05:00
sg_draw(0,6,1);
sg_end_pass();
gif.timer = apptime();
sg_query_image_pixels(sg_gif.img, crt_post.bind.fs.samplers[0], gif.buffer, gif.w*gif.h*4);
2023-09-12 07:56:40 -05:00
msf_gif_frame(&gif_state, gif.buffer, gif.cpf, gif.depth, gif.w * -4);
2023-09-12 00:02:57 -05:00
}
2024-03-03 16:49:34 -06:00
*/
2023-05-12 13:22:05 -05:00
sg_commit();
2023-06-05 10:32:45 -05:00
debug_newframe();
2021-11-30 21:29:18 -06:00
}
2023-05-24 20:45:50 -05:00
sg_shader sg_compile_shader(const char *v, const char *f, sg_shader_desc *d)
{
2023-06-07 08:41:09 -05:00
YughInfo("Making shader with %s and %s", v, f);
char *vs = slurp_text(v, NULL);
char *fs = slurp_text(f, NULL);
2023-05-24 20:45:50 -05:00
d->vs.source = vs;
d->fs.source = fs;
d->label = v;
2023-05-24 20:45:50 -05:00
sg_shader ret = sg_make_shader(d);
2023-05-24 20:45:50 -05:00
free(vs);
free(fs);
return ret;
}
struct boundingbox cwh2bb(HMM_Vec2 c, HMM_Vec2 wh) {
struct boundingbox bb = {
.t = c.Y + wh.Y/2,
.b = c.Y - wh.Y/2,
.r = c.X + wh.X/2,
.l = c.X - wh.X/2
};
return bb;
}
float *rgba2floats(float *r, struct rgba c)
{
r[0] = (float)c.r / RGBA_MAX;
r[1] = (float)c.g / RGBA_MAX;
r[2] = (float)c.b / RGBA_MAX;
r[3] = (float)c.a / RGBA_MAX;
return r;
}
sg_blend_state blend_trans = {
.enabled = true,
.src_factor_rgb = SG_BLENDFACTOR_SRC_ALPHA,
.dst_factor_rgb = SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA,
.src_factor_alpha = SG_BLENDFACTOR_SRC_ALPHA,
.dst_factor_alpha = SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA
};