diff --git a/README.md b/README.md index 7105676..9456e30 100644 --- a/README.md +++ b/README.md @@ -12,3 +12,18 @@ function order: - static functions(ones that don't take object as parameter) - empty line - methods(ones that do take object as parameter) + +## game skunk advance stuffs + +do lots automagic... +magic main function within the header? +autocalling update and init function. +texture loading compile time? parse directory, make a variable for each (player.png -> tex_player), and a loading function that will load all of them. + +### game skunk advance specs +8-bit colour, but user changeable palette(realtime) +tilesize 16x16 +screensize 304x176 (19x11 tiles) +multiple tilesets... or singular big tileset? the latter... could be a nice simplification... maybe 65536 tiles, a 4096x4096 pixels texture +4 tilemaps, 1024x1024 each, tileset offsets?, rotation and other effects +256 sprites (using tileset) diff --git a/build.bat b/build.bat index 3bbe4a1..cbae06f 100644 --- a/build.bat +++ b/build.bat @@ -2,5 +2,5 @@ REM cd build && cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_EXPORT_COMPILE_COMMANDS=1 -GNinja .. && ninja && copy compile_commands.json .. && skunkworks.exe -cd build && meson compile && copy compile_commands.json .. && skunktest.exe +cd build && meson compile && copy compile_commands.json .. && cd .. && build\gsa.exe diff --git a/examples/gsa_simple.c b/examples/gsa_simple.c new file mode 100644 index 0000000..a8b6c7d --- /dev/null +++ b/examples/gsa_simple.c @@ -0,0 +1,16 @@ +#include + +void init() { + sprites[0].tile = 2; + sprites[0].x = 10; + sprites[0].y = 10; + + sprites[1].tile = 3; + sprites[1].x = 10; + sprites[1].y = 100; +} + +void tick() { + sprites[0].x += 1; + sprites[1].x += 2; +} diff --git a/gfx.png b/gfx.png new file mode 100644 index 0000000..0491d1a Binary files /dev/null and b/gfx.png differ diff --git a/include/color8.h b/include/color8.h new file mode 100644 index 0000000..3cda90c --- /dev/null +++ b/include/color8.h @@ -0,0 +1,8 @@ +#ifndef GUARD_025B7F5FAD7E029F9A2F84062CDA4F53 +#define GUARD_025B7F5FAD7E029F9A2F84062CDA4F53 + +#include "types.h" + +typedef u8 sw_color8; + +#endif /* GUARD_025B7F5FAD7E029F9A2F84062CDA4F53 */ diff --git a/include/gsa.h b/include/gsa.h new file mode 100644 index 0000000..6a03cd9 --- /dev/null +++ b/include/gsa.h @@ -0,0 +1,56 @@ +/************************************************************************/ +/* ________ ___________ __ */ +/* / _____/_____ _____ ____ / _____/ | ____ __ ____ | | __ */ +/* / \ ___\__ \ / \_/ __ \ \_____ \| |/ / | \/ \| |/ / */ +/* \ \_\ \/ __ \| Y Y \ ___/ / \ <| | / | \ < */ +/* \______ (____ /__|_| /\___ >_______ /__|_ \____/|___| /__|_ \ */ +/* \/ \/ \/ \/ \/ \/ \/ \/ */ +/* _____ .___ */ +/* / _ \ __| _/__ _______ ____ ____ ____ */ +/* / /_\ \ / __ |\ \/ /\__ \ / \_/ ___\/ __ \ */ +/* / | \/ /_/ | \ / / __ \| | \ \__\ ___/ */ +/* \____|__ /\____ | \_/ (____ /___| /\___ >___ > */ +/* \/ \/ \/ \/ \/ \/ */ +/************************************************************************/ + +#ifndef GUARD_73FF49EF137A66E6AAD3A8A1BCF2F2DE +#define GUARD_73FF49EF137A66E6AAD3A8A1BCF2F2DE + +#include "image8.h" +#ifndef GSA_NOMAIN +#include "embed_gfx.h" +#endif + +#include "skunkworks.h" +#include "vec2i.h" +#include "window.h" + +struct gsa_sprite { + u16 tile; + i32 x, y; +}; + +#define MAX_SPRITES 256 + +extern struct gsa_sprite sprites[MAX_SPRITES]; +extern struct sw_image8 *_gfx; + +int gsa_main(int argc, char *argv[]); + +/* to be implemented in game */ + +void init(); +void tick(); + +#ifndef GSA_NOMAIN +int main(int argc, char *argv[]) { + _gfx = sw_image8_load_png_data(___gfx_png, ___gfx_png_len); + gsa_main(argc, argv); + + return 0; +} +#endif + +void _gsa_tick(); + +#endif /* GUARD_73FF49EF137A66E6AAD3A8A1BCF2F2DE */ diff --git a/include/image8.h b/include/image8.h new file mode 100644 index 0000000..3956d3c --- /dev/null +++ b/include/image8.h @@ -0,0 +1,20 @@ +#ifndef GUARD_6AEC99B12E1F76DC4E50DC199E93CDB5 +#define GUARD_6AEC99B12E1F76DC4E50DC199E93CDB5 + +#include "color8.h" +#include "vec2i.h" + +struct sw_image8 { + struct sw_vec2i size; + sw_color8 *_data; +}; + +struct sw_image8 *sw_image8_create(struct sw_vec2i size); +void sw_image8_destroy(struct sw_image8 *image); + +struct sw_image8 *sw_image8_load_png(char *path); +struct sw_image8 *sw_image8_load_png_data(u8 *data, u32 data_len); + +sw_color8 sw_image8_get(struct sw_image8 *image, struct sw_vec2i pos); + +#endif /* GUARD_6AEC99B12E1F76DC4E50DC199E93CDB5 */ diff --git a/include/shader.h b/include/shader.h new file mode 100644 index 0000000..e48b7a9 --- /dev/null +++ b/include/shader.h @@ -0,0 +1,24 @@ +#ifndef GUARD_2AF8C38E79755453C7B276BABE0DA7AE +#define GUARD_2AF8C38E79755453C7B276BABE0DA7AE + +#include "types.h" + +struct sw_shader { + i32 _shader; +}; + +struct sw_shaderprogram { + i32 _program; +}; + +#define SW_SHADER_VERTEX 1 +#define SW_SHADER_FRAGMENT 2 + +struct sw_shader sw_shader_create(char const *source, i32 type); + +struct sw_shaderprogram +sw_shaderprogram_create(char const *vertex, char const *fragment); + +void sw_shaderprogram_use(struct sw_shaderprogram program); + +#endif /* GUARD_2AF8C38E79755453C7B276BABE0DA7AE */ diff --git a/include/skunkworks.h b/include/skunkworks.h index a32bb5d..31c687d 100644 --- a/include/skunkworks.h +++ b/include/skunkworks.h @@ -5,6 +5,7 @@ #include "error.h" #include "file.h" #include "image32.h" +#include "image8.h" #include "types.h" #include "vec2i.h" #include "window.h" diff --git a/include/types.h b/include/types.h index e1b0d7d..487df85 100644 --- a/include/types.h +++ b/include/types.h @@ -11,5 +11,9 @@ typedef signed long long i64; typedef unsigned long long u64; typedef float f32; typedef double f64; +typedef i8 bool; + +#define true 1 +#define false 0 #endif /* GUARD_649E63003BEAE79ECA2B813C4E131C9A */ diff --git a/include/window.h b/include/window.h index a09213e..33f5532 100644 --- a/include/window.h +++ b/include/window.h @@ -13,6 +13,6 @@ struct sw_window { struct sw_window *sw_window_create(struct sw_vec2i size, char *title); -void sw_window_run(struct sw_window *window); +void sw_window_run(struct sw_window *window, void (*callback)()); #endif /* GUARD_F247452E0BF1EC9CD9131C2A6FD281CA */ diff --git a/meson.build b/meson.build index 6b062c3..250da70 100644 --- a/meson.build +++ b/meson.build @@ -43,18 +43,29 @@ skunk_sources = [ 'src/color32.c', 'src/error.c', 'src/file.c', + 'src/gsa.c', 'src/image32.c', + 'src/image8.c', + 'src/shader.c', 'src/skunkworks.c', - 'src/test.c', 'src/vec2i.c', 'src/window.c' ] +xxd = find_program('xxd') +embed_gfx = custom_target('embed-gfx', input: ['gfx.png'], output: ['embed_gfx.h'], + command: [xxd, '-i', '@INPUT@', '@OUTPUT@']) + add_project_arguments('-D_CRT_SECURE_NO_WARNINGS', language : 'c') +add_project_arguments('-Wall', language : 'c') +add_project_arguments('-Wextra', language : 'c') +add_project_arguments('-Werror', language : 'c') if host_machine.system() == 'emscripten' executable('skunktest', skunk_sources, include_directories: 'include', dependencies: deps, link_args: ['-lglfw', '-lGL', '-s', 'USE_GLFW=3'], name_suffix: 'html') else - executable('skunktest', skunk_sources, include_directories: 'include', dependencies: deps) + lib = static_library('skunkworks', skunk_sources, include_directories: 'include', dependencies: deps) + executable('skunktest', 'src/test.c', link_with: lib, include_directories: 'include', dependencies: deps) + executable('gsa', 'examples/gsa_simple.c', embed_gfx, link_with: lib, include_directories: 'include', dependencies: deps) endif \ No newline at end of file diff --git a/src/gsa.c b/src/gsa.c new file mode 100644 index 0000000..fa93b0d --- /dev/null +++ b/src/gsa.c @@ -0,0 +1,185 @@ +#include "error.h" +#include "gl.h" +#include "image8.h" +#include "shader.h" +#include +#define GSA_NOMAIN +#include "gsa.h" + +struct gsa_sprite sprites[256]; + +struct sw_image8 *_gfx; +struct sw_window *_win; + +static char *vert_source = "#version 450\n\ +in vec2 pos;\ +in vec2 tex_coord;\ +layout(location = 0) out vec2 out_tex_coord;\ +void main() {\ +gl_Position = vec4((pos / vec2(152, -88)) - vec2(1, -1), 0.f, 1.f);\ +out_tex_coord = tex_coord;\ +}\ +"; + +static char *frag_source = "#version 450\n\ +layout(location = 0) in vec2 tex_coord;\ +uniform sampler2D tex;\ +out vec4 color;\ +void main(){\ +float r = texture(tex, tex_coord).r;\ +if(r > 0)\ +color = vec4(0.9, 0.2, 0.1, 1.0);\ +}\ +"; + +static struct sw_shaderprogram program; + +static u32 vbo, vao; + +#define MAX_RENDER_VERTS 60 + +struct render_vert { + f32 x, y, tx, ty; +}; + +static i32 next_render_vert; +static struct render_vert render_verts[MAX_RENDER_VERTS * 4]; + +int gsa_main(int argc, char *argv[]) { + (void)argc; + (void)argv; + u32 tex; + struct sw_image8 *img; + + sw_log("Initialising GameSkunkAdvance v0.0"); + + _win = sw_window_create(sw_vec2i(304, 176), "Game Skunk Advance v0.0"); + program = sw_shaderprogram_create(vert_source, frag_source); + sw_shaderprogram_use(program); + + img = sw_image8_load_png("gfx.png"); + glGenTextures(1, &tex); + glBindTexture(GL_TEXTURE_2D, tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexImage2D( + GL_TEXTURE_2D, + 0, + GL_RED, + 4096, + 4096, + 0, + GL_RED, + GL_UNSIGNED_BYTE, + img->_data + ); + sw_image8_destroy(img); + + glGenBuffers(1, &vao); + glBindVertexArray(vao); + + glGenBuffers(1, &vbo); + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glVertexAttribPointer( + glGetAttribLocation(program._program, "pos"), + 2, + GL_FLOAT, + GL_FALSE, + sizeof(struct render_vert), + 0 + ); + glVertexAttribPointer( + glGetAttribLocation(program._program, "tex_coord"), + 2, + GL_FLOAT, + GL_FALSE, + sizeof(struct render_vert), + (void *)(2 * sizeof(float)) + ); + glEnableVertexAttribArray(glGetAttribLocation(program._program, "pos")); + glEnableVertexAttribArray(glGetAttribLocation(program._program, "tex_coord") + ); + + init(); + sw_window_run(_win, _gsa_tick); + + return 0; +} + +static void rect(f32 x, f32 y, f32 w, f32 h, i16 tile) { + f32 tx, ty, ts; + + tx = ((tile % 256) * 16.f) / 4096.f; + ty = ((tile / 256) * 16.f) / 4096.f; + ts = 1.f / 256.f; + + if(next_render_vert >= MAX_RENDER_VERTS) { + sw_error( + "trying to draw more than the current max of %i vertices in a " + "frame", + MAX_RENDER_VERTS + ); + } + + sw_log("tx %f | ty %f | ts %f", tx, ty, ts); + + render_verts[next_render_vert].x = x; + render_verts[next_render_vert].y = y; + render_verts[next_render_vert].tx = tx; + render_verts[next_render_vert].ty = ty; + ++next_render_vert; + + render_verts[next_render_vert].x = x + w; + render_verts[next_render_vert].y = y; + render_verts[next_render_vert].tx = tx + ts; + render_verts[next_render_vert].ty = ty; + ++next_render_vert; + + render_verts[next_render_vert].x = x; + render_verts[next_render_vert].y = y + h; + render_verts[next_render_vert].tx = tx; + render_verts[next_render_vert].ty = ty + ts; + ++next_render_vert; + + render_verts[next_render_vert].x = x + w; + render_verts[next_render_vert].y = y; + render_verts[next_render_vert].tx = tx + ts; + render_verts[next_render_vert].ty = ty; + ++next_render_vert; + + render_verts[next_render_vert].x = x; + render_verts[next_render_vert].y = y + h; + render_verts[next_render_vert].tx = tx; + render_verts[next_render_vert].ty = ty + ts; + ++next_render_vert; + + render_verts[next_render_vert].x = x + w; + render_verts[next_render_vert].y = y + h; + render_verts[next_render_vert].tx = tx + ts; + render_verts[next_render_vert].ty = ty + ts; + ++next_render_vert; +} + +void _gsa_tick() { + i32 i; + + tick(); + + next_render_vert = 0; + /* glBegin(GL_TRIANGLES); */ + for(i = 0; i < MAX_SPRITES; ++i) { + if(sprites[i].tile > 0) { + rect(sprites[i].x, sprites[i].y, 16, 16, sprites[i].tile); + } + } + + glBindVertexArray(vao); + glBufferData( + GL_ARRAY_BUFFER, + sizeof(struct render_vert) * (next_render_vert), + render_verts, + GL_DYNAMIC_DRAW + ); + glDrawArrays(GL_TRIANGLES, 0, next_render_vert); + /* glEnd(); */ +} diff --git a/src/image32.c b/src/image32.c index 7a6ba33..47af531 100644 --- a/src/image32.c +++ b/src/image32.c @@ -43,7 +43,7 @@ struct sw_image32 *sw_image32_load_png_data(u8 *data, u32 data_len) { u32 width, height; u8 color_type, bit_depth; u8 **row_pointers; - i32 i; + u32 i; buf = malloc(sizeof(struct sw_filebuffer)); buf->_data = data; diff --git a/src/image8.c b/src/image8.c new file mode 100644 index 0000000..804be36 --- /dev/null +++ b/src/image8.c @@ -0,0 +1,111 @@ +#include "image8.h" + +#include + +#include "error.h" +#include "file.h" +#include "png.h" +#include "vec2i.h" + +void png_read_fn(png_structp png, png_bytep out, png_size_t count); + +struct sw_image8 *sw_image8_create(struct sw_vec2i size) { + struct sw_image8 *image; + + image = malloc(sizeof(struct sw_image8)); + image->_data = malloc(sizeof(sw_color8) * size.x * size.y); + image->size = size; + + return image; +} + +void sw_image8_destroy(struct sw_image8 *image) { + free(image->_data); + free(image); +} + +struct sw_image8 *sw_image8_load_png(char *path) { + u32 size; + u8 *data; + struct sw_image8 *image; + + data = sw_file_load(path, &size); + image = sw_image8_load_png_data(data, size); + free(data); + return image; +} + +struct sw_image8 *sw_image8_load_png_data(u8 *data, u32 data_len) { + struct sw_image8 *img; + struct sw_filebuffer *buf; + png_structp png; + png_infop info; + u32 width, height; + u8 color_type, bit_depth; + u8 **row_pointers; + u32 i; + + buf = malloc(sizeof(struct sw_filebuffer)); + buf->_data = data; + buf->pos = 0; + buf->size = data_len; + + if(png_sig_cmp(data, 0, 8)) { + sw_error("File isn't recognised as a PNG file"); + } + + /* TODO: add error handles? <_< */ + png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0); + info = png_create_info_struct(png); + png_set_read_fn(png, buf, png_read_fn); + png_read_info(png, info); + + width = png_get_image_width(png, info); + height = png_get_image_height(png, info); + color_type = png_get_color_type(png, info); + bit_depth = png_get_bit_depth(png, info); + + sw_log( + "loading image of size %ux%u, color_type %u, bit_depth %u", + width, + height, + color_type, + bit_depth + ); + + if(color_type != PNG_COLOR_TYPE_PALETTE) { + sw_error("image8 can only load palette files"); + } + + if(bit_depth != 8) { + sw_error("image32 can currently only load 8 bit files"); + } + + img = sw_image8_create(sw_vec2i(width, height)); + + row_pointers = malloc(sizeof(u8 *) * height); + for(i = 0; i < height; ++i) { + row_pointers[i] = (u8 *)(img->_data + width * i); + } + png_read_image(png, row_pointers); + + free(row_pointers); + free(buf); + + /* TODO: cleanup of png structs? */ + + return img; +} + +sw_color8 sw_image8_get(struct sw_image8 *image, struct sw_vec2i pos) { + return image->_data[pos.x + pos.y * image->size.x]; +} + +/* private */ + +void png_read_fn(png_structp png, png_bytep out, png_size_t count) { + struct sw_filebuffer *buf; + + buf = png_get_io_ptr(png); + sw_filebuffer_read(buf, out, count); +} diff --git a/src/shader.c b/src/shader.c new file mode 100644 index 0000000..487778f --- /dev/null +++ b/src/shader.c @@ -0,0 +1,60 @@ +#include "shader.h" + +#include + +#include "error.h" +#include "gl.h" + +struct sw_shader sw_shader_create(char const *source, i32 type) { + struct sw_shader shader; + i32 gltype; + i32 length; + i32 ret; + + switch(type) { + case SW_SHADER_VERTEX: + gltype = GL_VERTEX_SHADER; + break; + case SW_SHADER_FRAGMENT: + gltype = GL_FRAGMENT_SHADER; + break; + default: + gltype = 0; + sw_error("unknown shader type"); + } + + shader._shader = glCreateShader(gltype); + length = strlen(source); + glShaderSource(shader._shader, 1, &source, &length); + glCompileShader(shader._shader); + + glGetShaderiv(shader._shader, GL_COMPILE_STATUS, &ret); + if(!ret) { + char str[256]; + i32 len; + + glGetShaderInfoLog(shader._shader, 256, &len, str); + sw_error("Shader Compile Error: %s", str); + } + + return shader; +} + +struct sw_shaderprogram +sw_shaderprogram_create(char const *vertex, char const *fragment) { + struct sw_shaderprogram program; + struct sw_shader v, f; + + v = sw_shader_create(vertex, SW_SHADER_VERTEX); + f = sw_shader_create(fragment, SW_SHADER_FRAGMENT); + program._program = glCreateProgram(); + glAttachShader(program._program, v._shader); + glAttachShader(program._program, f._shader); + glLinkProgram(program._program); + + return program; +} + +void sw_shaderprogram_use(struct sw_shaderprogram program) { + glUseProgram(program._program); +} diff --git a/src/skunkworks.h b/src/skunkworks.h index ead3db9..06433c3 100644 --- a/src/skunkworks.h +++ b/src/skunkworks.h @@ -1,3 +1,12 @@ +/***************************************************************************/ +/* ___________ __ __ */ +/* / _____/ | ____ __ ____ | | ____ _ _____________| | __ ______ */ +/* \_____ \| |/ / | \/ \| |/ /\ \/ \/ / _ \_ __ \ |/ / / ___/ */ +/* / \ <| | / | \ < \ ( <_> ) | \/ < \___ \ */ +/* /_______ /__|_ \____/|___| /__|_ \ \/\_/ \____/|__| |__|_ \/____ > */ +/* \/ \/ \/ \/ \/ \/ */ +/***************************************************************************/ + #ifndef GUARD_BAEE408B8F416CA55711305ABAF9CE6C #define GUARD_BAEE408B8F416CA55711305ABAF9CE6C diff --git a/src/test.c b/src/test.c index d65a6e6..2936610 100644 --- a/src/test.c +++ b/src/test.c @@ -14,13 +14,17 @@ void tick(); int main(int argc, char *argv[]) { struct sw_image32 *img; - sw_init(); - printf("hello world\n"); - win = sw_window_create(sw_vec2i(640, 480), "Skunkworks"); + (void)argc; + (void)argv; + sw_init(); + win = sw_window_create(sw_vec2i(640, 480), "Skunkworks"); img = sw_image32_load_png("d:/art/pfp.png"); sw_debug("%08X", sw_image32_get(img, sw_vec2i(10, 10))); + sw_window_run(win, tick); - sw_window_run(win); return 0; } + +void tick() { +} diff --git a/src/window.c b/src/window.c index 555e5ba..1db09b1 100644 --- a/src/window.c +++ b/src/window.c @@ -4,8 +4,9 @@ #include "gl.h" -void tick(); -struct sw_window *tick_window; +void _tick(); +struct sw_window *_tick_window; +void (*_tick_fn)(); struct sw_window *sw_window_create(struct sw_vec2i size, char *title) { struct sw_window *win; @@ -29,21 +30,23 @@ struct sw_window *sw_window_create(struct sw_vec2i size, char *title) { return win; } -void sw_window_run(struct sw_window *window) { - tick_window = window; +void sw_window_run(struct sw_window *window, void (*callback)()) { + _tick_window = window; + _tick_fn = callback; #ifdef __EMSCRIPTEN__ - emscripten_set_main_loop(tick, 60, 1); + emscripten_set_main_loop(_tick, 60, 1); #else while(!glfwWindowShouldClose(window->_window)) { - tick(); + _tick(); } #endif } /* private functions */ -void tick() { - glClear(GL_COLOR_BUFFER_BIT); - glfwSwapBuffers(tick_window->_window); +void _tick() { + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + _tick_fn(); + glfwSwapBuffers(_tick_window->_window); glfwPollEvents(); }