commit cf822b5b0ae2d9d7f4a63dc9135801d04bfd12e0
parent a0e9824657d278530015246bc9e9f2d7571d93da
Author: amin <dev@aminmesbah.com>
Date: Mon, 8 Jul 2019 20:38:58 +0000
Merge branch 'features/textures'
FossilOrigin-Name: 993303fd18c0d0d15e103ed76f35037fb4bcc539fcdcf2404d8ea8162f2ddffc
Diffstat:
25 files changed, 396 insertions(+), 46 deletions(-)
diff --git a/assets/test_sw_origin.tga b/assets/test_sw_origin.tga
Binary files differ.
diff --git a/assets/test_sw_origin_rle.tga b/assets/test_sw_origin_rle.tga
Binary files differ.
diff --git a/assets/tile0.tga b/assets/tile0.tga
Binary files differ.
diff --git a/build_wasm.sh b/build_wasm.sh
@@ -12,6 +12,7 @@ mkdir -p $wasm_dir
cp src/platform_wasm_loader.js $wasm_dir/script.js
cp src/platform_wasm_index.html $wasm_dir/index.html
cp -r shader/ $wasm_dir
+cp -r assets/ $wasm_dir
clang \
-cc1 \
diff --git a/shader/main_f.glsl b/shader/main_f.glsl
@@ -6,10 +6,14 @@ precision highp float;
// TODO: dynamically instert the version header after loading the file
in vec4 vertex_color;
+in vec2 tex_coord;
out vec4 frag_color;
+uniform float tex_interp;
+uniform sampler2D main_texture;
+
void main()
{
- frag_color = vertex_color;
+ frag_color = mix(vertex_color, texture(main_texture, tex_coord), tex_interp);
}
diff --git a/shader/main_v.glsl b/shader/main_v.glsl
@@ -3,8 +3,10 @@
// TODO: dynamically instert the version header after loading the file
layout (location = 0) in vec2 position;
+layout (location = 1) in vec2 a_tex_coords;
out vec4 vertex_color;
+out vec2 tex_coord;
uniform mat4 model;
uniform mat4 view;
@@ -15,4 +17,5 @@ void main()
{
gl_Position = projection * view * model * vec4(position, 0.0f, 1.0f);
vertex_color = vec4(color, 1.0f);
+ tex_coord = a_tex_coords;
}
diff --git a/src/am_math.h b/src/am_math.h
@@ -270,6 +270,16 @@ internal inline v3 math_v3_s(v3 vec1, v3 vec2)
return math_v3_a(vec1, math_v3_negate(vec2));
}
+internal inline v3 math_v3_from_rgb(i32 rgb)
+{
+ v3 result = {
+ .x = ((rgb >> 16) & 0xFF) / 255.0f,
+ .y = ((rgb >> 8) & 0xFF) / 255.0f,
+ .z = ((rgb >> 0) & 0xFF) / 255.0f,
+ };
+ return result;
+}
+
internal inline bool math_v4v4_eq(v4 vec1, v4 vec2)
{
for (u8 i = 0; i < 4; ++i)
diff --git a/src/game.c b/src/game.c
@@ -10,6 +10,7 @@
#include "render.c"
#include "world.c"
#include "input.c"
+#include "image.c"
internal void move_mode_print(enum MoveMode s)
{
@@ -89,10 +90,16 @@ internal void game_init(struct GameMemory *game_memory, v2u framebuffer)
game_state->player.move_mode = MOVE_MODE_FALLING;
game_state->player.facing = DIR_RIGHT;
+ size_t temp_memory_size = MEBIBYTES(1);
+ size_t world_memory_size = game_memory->buffer_size - (sizeof(struct GameState) + temp_memory_size);
mem_st_init(
- &game_state->world_allocator,
+ &game_state->temp_allocator,
game_memory->buffer + sizeof(struct GameState),
- game_memory->buffer_size - sizeof(struct GameState));
+ temp_memory_size);
+ mem_st_init(
+ &game_state->world_allocator,
+ game_state->temp_allocator.base + temp_memory_size,
+ world_memory_size);
game_state->world = mem_st_alloc_struct(&game_state->world_allocator, struct World);
@@ -167,13 +174,11 @@ internal void game_init(struct GameMemory *game_memory, v2u framebuffer)
}
}
- renderer_init(&game_state->renderer, &game_state->world_allocator);
-
+ struct PlatformApi platform = game_memory->platform;
// game_shader_load
{
- struct PlatformApi platform = game_memory->platform;
- char *v_source = platform.platform_read_entire_file("shader/main_v.glsl");
- char *f_source = platform.platform_read_entire_file("shader/main_f.glsl");
+ char *v_source = (char *)platform.platform_read_entire_file("shader/main_v.glsl", NULL);
+ char *f_source = (char *)platform.platform_read_entire_file("shader/main_f.glsl", NULL);
struct Shader main_shader = {0};
if (!shader_compile(v_source, f_source, &main_shader))
{
@@ -183,6 +188,27 @@ internal void game_init(struct GameMemory *game_memory, v2u framebuffer)
platform.platform_memory_free(f_source);
game_state->renderer.shader = main_shader;
}
+
+ size_t temp_free_marker = mem_st_get_marker(&game_state->temp_allocator);
+ struct Image img = {0};
+ // game_load_images
+ {
+ size_t file_len = 0;
+ u8 *t = platform.platform_read_entire_file("assets/tile0.tga", &file_len);
+ assert(t);
+
+ i32 img_w = 0;
+ i32 img_h = 0;
+ u8 *data = img_load_from_memory(t, file_len, &img_w, &img_h, &game_state->temp_allocator);
+ assert(data);
+ platform.platform_memory_free(t);
+
+ img.data = data;
+ img.dim = (v2u) {img_w, img_h};
+ }
+
+ renderer_init(&game_state->renderer, &img, 1, &game_state->world_allocator);
+ mem_st_free_to_marker(&game_state->temp_allocator, temp_free_marker);
}
// NOTE(amin): For now updating and rendering are interleaved. We simulate the
@@ -192,6 +218,7 @@ void game_update_and_render(struct GameMemory *game_memory, struct GameInput *ga
{
assert(sizeof(struct GameState) <= game_memory->buffer_size);
struct GameState *game_state = (struct GameState *)game_memory->buffer;
+ f32 dt = game_input->dt;
rect viewport = {0};
f32 ppm = 0.0f;
@@ -223,15 +250,11 @@ void game_update_and_render(struct GameMemory *game_memory, struct GameInput *ga
viewport.max = math_v2_a(viewport.min, viewport_size);
m4 view = math_m4_init_id();
- // World origin is in the lower left
- view = math_translate(view, (v3) {0.0f, framebuffer.height, 0.0f});
- view = math_scale(view, (v3) {1.0f, -1.0f, 1.0f});
view = math_translate(view, (v3) {viewport.min.x, viewport.min.y, 0.0f});
view = math_scale(view, (v3) {ppm, ppm, 1.0f});
game_state->renderer.view = view;
- // Screen origin is in the upper left
- game_state->renderer.projection = math_projection_ortho(0.0f, framebuffer.width, framebuffer.height, 0.0f, -1.0f, 0.0f);
+ game_state->renderer.projection = math_projection_ortho(0.0f, framebuffer.width, 0.0f, framebuffer.height, -1.0f, 0.0f);
}
v2i current_room_i = game_state->player.pos.room;
@@ -251,8 +274,7 @@ void game_update_and_render(struct GameMemory *game_memory, struct GameInput *ga
// game_update_player
{
struct Entity *player = &game_state->player;
- move_mode_print(player->move_mode);
- f32 dt = game_input->dt;
+ //move_mode_print(player->move_mode);
u32 button_states = game_input->button_states;
if (dt >= 0.5f)
{
@@ -387,7 +409,7 @@ void game_update_and_render(struct GameMemory *game_memory, struct GameInput *ga
// game_detect_collisions
{
-#if 1
+#if 0
#define RENDER_COLLISION_DEBUG_QUAD(r, c) renderer_debug_quad_draw(&game_state->renderer, (r), (c));
#else
#define RENDER_COLLISION_DEBUG_QUAD(r, c)
@@ -566,13 +588,15 @@ void game_update_and_render(struct GameMemory *game_memory, struct GameInput *ga
v3 color;
bool solid = world_tile_in_room_is_solid(current_room->tiles, tile_pos);
+ f32 tex_interp = 0.0f;
if (solid)
{
color = (v3) {0.4f, 0.4f, 0.4f};
+ tex_interp = 1.0f;
}
else
{
- color = (v3) {0.3f, 0.3f, 0.3f};
+ color = (math_v3_from_rgb(0x16161D));
}
renderer_job_enqueue(
@@ -581,6 +605,7 @@ void game_update_and_render(struct GameMemory *game_memory, struct GameInput *ga
.ebo = game_state->renderer.quad_ebo,
.color = color,
.model = model,
+ .tex_interp = tex_interp,
.layer = RENDER_LAYER_TILES,
});
}
diff --git a/src/game.h b/src/game.h
@@ -81,6 +81,8 @@ void *memmove(void *dst, const void *src, size_t n)
#include "render.h"
#include "world.h"
#include "input.h"
+#include "image.h"
+
#include "platform.h"
static_assert(-1 == ~0, "Implementation doesn't use two's complement");
@@ -127,6 +129,7 @@ struct GameState
{
struct RendererState renderer;
struct Entity player;
+ struct StackAllocator temp_allocator;
struct StackAllocator world_allocator;
struct World *world;
};
diff --git a/src/image.c b/src/image.c
@@ -0,0 +1,129 @@
+internal void img_tga_header_print(struct ImgTgaHeader h)
+{
+ printf("id_length: %u\n", h.id_length);
+ printf("color_map_type: %u\n", h.color_map_type);
+ printf("data_type_code: %u\n", h.data_type_code);
+ printf("color_map_origin: %u\n", h.color_map_origin);
+ printf("color_map_length: %u\n", h.color_map_length);
+ printf("color_map_depth: %u\n", h.color_map_depth);
+ printf("x_origin: %u\n", h.x_origin);
+ printf("y_origin: %u\n", h.y_origin);
+ printf("width: %u\n", h.width);
+ printf("height: %u\n", h.height);
+ printf("bits_per_pixel: %u\n", h.bits_per_pixel);
+ printf("image_descriptor: %u\n", h.image_descriptor);
+}
+
+internal u8 img_consume_byte(u8 **buffer, uintptr_t end_address)
+{
+ u8 byte = 0;
+ if ((uintptr_t)*buffer < end_address)
+ {
+ byte = **buffer;
+ (*buffer)++;
+ }
+ return byte;
+}
+
+internal u8 *img_load_from_memory(u8 *buffer, size_t len, i32 *out_width, i32 *out_height, struct StackAllocator *allocator)
+{
+ assert(buffer);
+ uintptr_t buf_end = (uintptr_t)buffer + (len * sizeof(u8));
+
+ assert(len >= sizeof(struct ImgTgaHeader));
+ struct ImgTgaHeader h = *(struct ImgTgaHeader *)buffer;
+ buffer += sizeof(struct ImgTgaHeader);
+ //img_tga_header_print(h);
+
+ // Image descriptor not currently accounted for
+ assert(h.id_length == 0);
+ // Color maps not currently supported
+ assert(h.color_map_type == 0);
+ // Only RGB is supported
+ assert(h.data_type_code == 2 || h.data_type_code == 10);
+ assert(h.bits_per_pixel <= 32);
+
+ if (out_width)
+ {
+ *out_width = h.width;
+ }
+ if (out_height)
+ {
+ *out_height = h.height;
+ }
+
+ u8 bytes_per_pixel = h.bits_per_pixel / 8;
+ size_t bytes_per_row = h.width * bytes_per_pixel;
+ size_t img_data_buf_len = h.height * bytes_per_row;
+ u8 *img_data = mem_st_alloc_buffer(allocator, u8, img_data_buf_len);
+ assert(img_data);
+
+ if (h.data_type_code == 2)
+ {
+ // uncompressed RGB
+ for (size_t b = 0; b < img_data_buf_len; b++)
+ {
+ img_data[b] = img_consume_byte(&buffer, buf_end);
+ }
+ }
+ else if (h.data_type_code == 10)
+ {
+ // Run Length Encoded RGB
+ size_t img_bytes_read = 0;
+ while (img_bytes_read < img_data_buf_len)
+ {
+ u8 packet_header = img_consume_byte(&buffer, buf_end);
+ u8 run_length_header_bit = packet_header >> 7;
+ u32 run_count = (packet_header & (~(1 << 7))) + 1;
+ assert(run_count <= 128);
+ u32 run_length_in_bytes = run_count * bytes_per_pixel;
+
+ if (run_length_header_bit)
+ {
+ u8 pixel[4] = {0};
+ for (u32 b = 0; b < bytes_per_pixel; b++)
+ {
+ pixel[b] = img_consume_byte(&buffer, buf_end);
+ }
+ for (u32 b = 0; b < run_length_in_bytes; b++)
+ {
+ img_data[img_bytes_read + b] = pixel[b % bytes_per_pixel];
+ }
+ img_bytes_read += run_length_in_bytes;
+ }
+ else
+ {
+ for (u32 b = 0; b < run_length_in_bytes; b++)
+ {
+ img_data[img_bytes_read + b] = img_consume_byte(&buffer, buf_end);
+ }
+ img_bytes_read += run_length_in_bytes;
+ }
+ }
+ }
+
+ // NOTE(amin): we convert BGRA to RGBA because OpenGL ES and WebGL don't
+ // support the GL_BGRA texture format.
+ for (size_t b = 0; b < img_data_buf_len; b += 4)
+ {
+ u8 temp = img_data[b];
+ img_data[b + 0] = img_data[b + 2];
+ img_data[b + 2] = temp;
+ }
+
+ // NOTE(amin): Intentional truncating division. If height is odd, we want
+ // our last row offset to be 1 less than the middle row.
+ u32 half_height = h.height / 2;
+ for (size_t row_offset = 0; row_offset < half_height; row_offset++)
+ {
+ u8 *top_row = &img_data[row_offset * bytes_per_row];
+ u8 *bot_row = &img_data[(h.height - row_offset - 1) * bytes_per_row];
+ for (size_t b = 0; b < bytes_per_row; b++)
+ {
+ u8 temp = top_row[b];
+ top_row[b] = bot_row[b];
+ bot_row[b] = temp;
+ }
+ }
+ return img_data;
+}
diff --git a/src/image.h b/src/image.h
@@ -0,0 +1,25 @@
+struct Image
+{
+ v2u dim;
+ u8 *data;
+};
+
+#pragma pack(push, 1)
+struct ImgTgaHeader
+{
+ // http://paulbourke.net/dataformats/tga/
+ u8 id_length;
+ u8 color_map_type;
+ u8 data_type_code;
+ u16 color_map_origin;
+ u16 color_map_length;
+ u8 color_map_depth;
+ u16 x_origin;
+ u16 y_origin;
+ u16 width;
+ u16 height;
+ u8 bits_per_pixel;
+ u8 image_descriptor;
+};
+#pragma pack(pop)
+static_assert(sizeof(struct ImgTgaHeader) == 18, "");
diff --git a/src/memory.c b/src/memory.c
@@ -38,7 +38,10 @@ internal size_t mem_st_get_marker(struct StackAllocator *allocator)
internal void mem_st_free_to_marker(struct StackAllocator *allocator, size_t marker)
{
- allocator->used = marker;
+ if (allocator->used > marker)
+ {
+ allocator->used = marker;
+ }
}
internal void mem_st_free_all(struct StackAllocator *allocator)
diff --git a/src/memory.h b/src/memory.h
@@ -4,3 +4,6 @@ struct StackAllocator
size_t size;
size_t used;
};
+
+#define KIBIBYTES(n) (n) * 1024LL
+#define MEBIBYTES(n) KIBIBYTES((n) * 1024LL)
diff --git a/src/platform.h b/src/platform.h
@@ -2,10 +2,7 @@
#include "types.h"
-#define KIBIBYTES(n) (n) * 1024LL
-#define MEBIBYTES(n) KIBIBYTES((n) * 1024LL)
-
-#define PLATFORM_READ_ENTIRE_FILE(name) char *(name)(char *file_path)
+#define PLATFORM_READ_ENTIRE_FILE(name) u8 *(name)(char *file_path, size_t *out_num_bytes)
typedef PLATFORM_READ_ENTIRE_FILE(platform_read_entire_file_func);
#define PLATFORM_MEMORY_FREE(name) void (name)(void *ptr)
diff --git a/src/platform_linux.c b/src/platform_linux.c
@@ -210,7 +210,7 @@ PLATFORM_MEMORY_FREE(linux_memory_free)
internal PLATFORM_READ_ENTIRE_FILE(linux_read_entire_file)
{
FILE *handle = fopen(file_path, "r");
- char *buffer = NULL;
+ u8 *buffer = NULL;
if (handle)
{
@@ -219,10 +219,15 @@ internal PLATFORM_READ_ENTIRE_FILE(linux_read_entire_file)
u32 num_bytes_in_file = ftell(handle);
rewind(handle);
+ if (out_num_bytes)
+ {
+ *out_num_bytes = num_bytes_in_file;
+ }
+
// TODO: replace malloc with own allocator so I stop having nightmares
- buffer = (char*) malloc(sizeof(char) * (num_bytes_in_file + 1) );
+ buffer = malloc(sizeof(u8) * (num_bytes_in_file + 1) );
- u32 bytes_read = fread(buffer, sizeof(char), num_bytes_in_file, handle);
+ u32 bytes_read = fread(buffer, sizeof(u8), num_bytes_in_file, handle);
// IMPORTANT! fread() doesn't add the '\0'
buffer[num_bytes_in_file] = '\0';
diff --git a/src/platform_wasm.c b/src/platform_wasm.c
@@ -30,7 +30,8 @@ global_variable struct GameInput game_input = {0};
global_variable i32 g_width = PLATFORM_SCR_WIDTH;
global_variable i32 g_height = PLATFORM_SCR_HEIGHT;
// TODO: replace with allocator
-global_variable char g_mem_buffer[1000] = {0};
+// NOTE: If this buffer is too small, the browser will freeze up
+global_variable u8 g_mem_buffer[10000] = {0};
global_variable i32 g_mem_buffer_i = 0;
global_variable u32 previous_time = 0;
@@ -115,13 +116,15 @@ PLATFORM_MEMORY_FREE(wasm_memory_free)
// do nothing
}
+// TODO: robustly return file length
PLATFORM_READ_ENTIRE_FILE(wasm_read_entire_file)
{
g_mem_buffer_i++;
- char *buf = &g_mem_buffer[g_mem_buffer_i];
- if(js_read_entire_file((i32)file_path, str_length(file_path), buf))
+ u8 *buf = &g_mem_buffer[g_mem_buffer_i];
+ i32 file_data_length = js_read_entire_file((i32)file_path, str_length(file_path), buf);
+ if(file_data_length)
{
- i32 file_data_length = str_length(buf);
+ *out_num_bytes = file_data_length;
g_mem_buffer_i += file_data_length;
g_mem_buffer[g_mem_buffer_i] = '\0';
wasm_print("Succeeded in reading file.");
diff --git a/src/platform_wasm.h b/src/platform_wasm.h
@@ -5,5 +5,5 @@ PLATFORM_READ_ENTIRE_FILE(wasm_read_entire_file);
PLATFORM_MEMORY_FREE(wasm_memory_free);
// Functions implemented in the JS loader
-char *js_read_entire_file(i32 file_path, i32 name_len, char *file_data);
-int js_print(i32 string, i32 len);
+i32 js_read_entire_file(i32 file_path, i32 name_len, u8 *file_data);
+i32 js_print(i32 string, i32 len);
diff --git a/src/platform_wasm_js_symbols.txt b/src/platform_wasm_js_symbols.txt
@@ -1,6 +1,8 @@
+webglActiveTexture
webglAttachShader
webglBindBuffer
webglBindVertexArray
+webglBindTexture
webglBlendColor
webglBlendFunc
webglBufferData
@@ -10,6 +12,7 @@ webglCompileShader
webglCreateBuffer
webglCreateProgram
webglCreateShader
+webglCreateTexture
webglCreateVertexArray
webglDeleteBuffer
webglDeleteShader
@@ -19,6 +22,7 @@ webglDisable
webglDrawElements
webglEnable
webglEnableVertexAttribArray
+webglGenerateMipmap
webglGetProgramInfoLog
webglGetProgramParameter
webglGetShaderInfoLog
@@ -26,6 +30,8 @@ webglGetShaderParameter
webglGetUniformLocation
webglLinkProgram
webglShaderSource
+webglTexImage2D
+webglTexParameteri
webglUniform1f
webglUniform1i
webglUniform3f
diff --git a/src/platform_wasm_loader.js b/src/platform_wasm_loader.js
@@ -1,3 +1,4 @@
+'use strict';
let utf8decoder = new TextDecoder("utf-8");
let memory = null;
let exports = {};
@@ -20,6 +21,9 @@ const key_numeric_codes = {
KeyS: 4,
F11: 5,
};
+imports["webglActiveTexture"] = function(texture) {
+ gl.activeTexture(texture);
+}
imports["webglAttachShader"] = function(program_id, shader_id) {
let program = gl_id_map[program_id];
let shader = gl_id_map[shader_id];
@@ -29,6 +33,10 @@ imports["webglBindBuffer"] = function(target, buffer_id) {
let buffer = gl_id_map[buffer_id];
gl.bindBuffer(target, buffer);
}
+imports["webglBindTexture"] = function(target, texture_id) {
+ let texture = gl_id_map[texture_id];
+ gl.bindTexture(target, texture);
+}
imports["webglBindVertexArray"] = function(vao_id) {
let vao = gl_id_map[vao_id];
gl.bindVertexArray(vao);
@@ -68,6 +76,11 @@ imports["webglCreateShader"] = function(type) {
let shader_id = webgl_id_new(shader);
return shader_id;
}
+imports["webglCreateTexture"] = function(type) {
+ let texture = gl.createTexture();
+ let texture_id = webgl_id_new(texture);
+ return texture_id;
+}
imports["webglCreateVertexArray"] = function() {
let vao = gl.createVertexArray()
let vao_id = webgl_id_new(vao);
@@ -103,6 +116,9 @@ imports["webglEnable"] = function(cap) {
imports["webglEnableVertexAttribArray"] = function(index) {
gl.enableVertexAttribArray(index);
}
+imports["webglGenerateMipmap"] = function(target) {
+ gl.generateMipmap(target);
+}
imports["webglGetProgramInfoLog"] = function() {
}
imports["webglGetProgramParameter"] = function(program_id, param) {
@@ -142,6 +158,14 @@ imports["webglShaderSource"] = function(shader_id, source_ptr, source_len) {
let s = utf8decoder.decode(arr);
gl.shaderSource(shader, s);
}
+imports["webglTexImage2D"] = function(target, level, internalformat, width, height, border, format, type, pixels) {
+ const bytes_per_pixel = 4;
+ let dataslice = memory.subarray(pixels, pixels + (width * height * bytes_per_pixel));
+ gl.texImage2D(target, level, internalformat, width, height, border, format, type, dataslice);
+}
+imports["webglTexParameteri"] = function(target, pname, param) {
+ gl.texParameteri(target, pname, param);
+}
imports["webglUniform1f"] = function(location_id, value) {
let loc = gl_id_map[location_id];
gl['uniform1f'](loc, value);
@@ -169,19 +193,23 @@ imports["webglVertexAttribPointer"] = function(index, size, type, normalized, st
imports["webglViewport"] = function(x, y, width, height) {
gl.viewport(x, y, width, height);
}
-imports["js_read_entire_file"] = function(name, name_len, out_buf) {
+imports["js_read_entire_file"] = function(name, name_len, out_buf, out_len) {
let file_name = utf8decoder.decode(memory.subarray(name, name + name_len))
+ let file_size = 0;
if (file_name == "shader/main_f.glsl") {
var file = files[1];
} else if (file_name == "shader/main_v.glsl") {
var file = files[2];
+ } else if (file_name == "assets/tile0.tga") {
+ var file = files[3];
} else {
- return false;
+ return 0;
}
let arr = memory.subarray(out_buf, out_buf + file.byteLength);
+ file_size = arr.length * arr.BYTES_PER_ELEMENT;
let s = String.fromCharCode.apply(null, arr);
arr.set(new Uint8Array(file));
- return true;
+ return file_size;
}
imports["js_print"] = function(s, len) {
let arr = memory.subarray(s, s + len);
@@ -233,6 +261,10 @@ function file_load(name) {
function on_key_event(e, key_is_down) {
let sane_numeric_code = key_numeric_codes[e.code];
if (sane_numeric_code !== undefined) {
+ if (sane_numeric_code === 5) {
+ // Don't toggle full screen
+ e.preventDefault();
+ }
exports['key_callback'](sane_numeric_code, key_is_down);
}
}
@@ -251,6 +283,7 @@ window.onload = async function() {
files[0] = file_load("binary.wasm");
files[1] = file_load("shader/main_f.glsl");
files[2] = file_load("shader/main_v.glsl");
+ files[3] = file_load("assets/tile0.tga");
for(var i = 0; i < files.length; i++) {
files[i] = await files[i];
}
diff --git a/src/platform_windows.c b/src/platform_windows.c
@@ -174,7 +174,7 @@ PLATFORM_MEMORY_FREE(windows_memory_free)
internal PLATFORM_READ_ENTIRE_FILE(windows_read_entire_file)
{
FILE *handle = fopen(file_path, "rb");
- char *buffer = NULL;
+ u8 *buffer = NULL;
if (handle)
{
@@ -183,10 +183,15 @@ internal PLATFORM_READ_ENTIRE_FILE(windows_read_entire_file)
u32 num_bytes_in_file = ftell(handle);
rewind(handle);
+ if (out_num_bytes)
+ {
+ *out_num_bytes = num_bytes_in_file;
+ }
+
// TODO: replace malloc with own allocator so I stop having nightmares
- buffer = (char*) malloc(sizeof(char) * (num_bytes_in_file + 1) );
+ buffer = malloc(sizeof(u8) * (num_bytes_in_file + 1));
- u32 bytes_read = fread(buffer, sizeof(char), num_bytes_in_file, handle);
+ u32 bytes_read = fread(buffer, sizeof(u8), num_bytes_in_file, handle);
// IMPORTANT! fread() doesn't add the '\0'
buffer[num_bytes_in_file] = '\0';
diff --git a/src/render.c b/src/render.c
@@ -1,14 +1,14 @@
-internal void renderer_init(struct RendererState *renderer, struct StackAllocator *allocator)
+internal void renderer_init(struct RendererState *renderer, struct Image *images, size_t num_images, struct StackAllocator *allocator)
{
GLfloat quad_vertices[] = {
- 0.5f, 0.5f,
- 0.5f, -0.5f,
- -0.5f, -0.5f,
- -0.5f, 0.5f,
+ 0.5f, 0.5f, 1.0f, 0.0f,
+ 0.5f, -0.5f, 1.0f, 1.0f,
+ -0.5f, -0.5f, 0.0f, 1.0f,
+ -0.5f, 0.5f, 0.0f, 0.0f,
};
- GLuint quad_elements[] = { 0, 1, 3, 1, 2, 3 };
- GLuint rect_elements[] = { 0, 1, 1, 2, 2, 3, 3, 0 };
+ GLuint quad_elements[] = {0, 1, 3, 1, 2, 3};
+ GLuint rect_elements[] = {0, 1, 1, 2, 2, 3, 3, 0};
GLuint vao;
GLuint quad_vbo;
@@ -30,11 +30,15 @@ internal void renderer_init(struct RendererState *renderer, struct StackAllocato
2, // Number of values
GL_FLOAT, // Data type
GL_FALSE, // Normalize data
- 2 * sizeof(GLfloat), // Stride
+ 4 * sizeof(GLfloat), // Stride
(GLvoid*)0 // Position data offset (0)
);
-
glEnableVertexAttribArray(0);
+
+ // texture coordinates
+ glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (GLvoid*)(2 * sizeof(GLfloat)));
+ glEnableVertexAttribArray(1);
+
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quad_ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(quad_elements), quad_elements, GL_STATIC_DRAW);
@@ -43,12 +47,31 @@ internal void renderer_init(struct RendererState *renderer, struct StackAllocato
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quad_ebo);
+ for (u32 i = 0; i < num_images; i++)
+ {
+ struct Image img = images[i];
+ u32 texture_id;
+ glGenTextures(1, &texture_id);
+ glBindTexture(GL_TEXTURE_2D, texture_id);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img.dim.width, img.dim.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, img.data);
+ glGenerateMipmap(GL_TEXTURE_2D);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glActiveTexture(GL_TEXTURE0);
+ }
+
renderer->vao = vao;
renderer->quad_vbo = quad_vbo;
renderer->quad_ebo = quad_ebo;
renderer->rect_ebo = rect_ebo;
renderer->queue = mem_st_alloc_buffer(allocator, struct RenderJob, RENDER_QUEUE_SIZE);
renderer->queue_count = 0;
+
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
internal void renderer_jobs_draw(struct RendererState *renderer)
@@ -78,6 +101,7 @@ internal void renderer_jobs_draw(struct RendererState *renderer)
}
shader_setv3(&renderer->shader, SHADER_UNIFORM_COLOR, &j.color);
shader_setm4(&renderer->shader, SHADER_UNIFORM_MODEL, &j.model);
+ shader_setf(&renderer->shader, SHADER_UNIFORM_TEX_INTERP, j.tex_interp);
if (j.ebo == renderer->quad_ebo)
{
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
diff --git a/src/render.h b/src/render.h
@@ -9,11 +9,13 @@ enum RenderLayer
NUM_RENDER_LAYERS,
};
+// TODO: optimize packing
struct RenderJob
{
GLuint ebo;
v3 color;
m4 model;
+ f32 tex_interp;
enum RenderLayer layer;
};
diff --git a/src/shader.c b/src/shader.c
@@ -168,6 +168,9 @@ char *shader_uniform_get_name(enum ShaderUniform u)
case SHADER_UNIFORM_COLOR:
name = "color";
break;
+ case SHADER_UNIFORM_TEX_INTERP:
+ name = "tex_interp";
+ break;
case NUM_SHADER_UNIFORMS:
// fallthrough
default:
diff --git a/src/shader.h b/src/shader.h
@@ -4,6 +4,7 @@ enum ShaderUniform
SHADER_UNIFORM_VIEW,
SHADER_UNIFORM_PROJECTION,
SHADER_UNIFORM_COLOR,
+ SHADER_UNIFORM_TEX_INTERP,
NUM_SHADER_UNIFORMS,
};
diff --git a/src/webgl.h b/src/webgl.h
@@ -6,8 +6,10 @@ typedef double f64;
// NOTE(amin): Since these functions will be implemented in javascript, we can
// only use i32, f32, and f64 params.
+void webglActiveTexture(i32 texture);
void webglAttachShader(i32 program, i32 shader);
void webglBindBuffer(i32 target, i32 buffer);
+void webglBindTexture(i32 target, i32 texture);
void webglBindVertexArray(i32 vao);
void webglBlendColor(f32 r, f32 g, f32 b, f32 a);
void webglBlendFunc(i32 sfactor, i32 dfactor);
@@ -18,6 +20,7 @@ void webglCompileShader(i32 shader);
i32 webglCreateBuffer(void);
i32 webglCreateProgram(void);
i32 webglCreateShader(i32 type);
+i32 webglCreateTexture(void);
i32 webglCreateVertexArray(void);
void webglDeleteBuffer(i32 bo);
void webglDeleteShader(i32 shader);
@@ -27,6 +30,7 @@ void webglDisable(i32 cap);
void webglDrawElements(i32 mode, i32 count, i32 type, i32 offset);
void webglEnable(i32 cap);
void webglEnableVertexAttribArray(i32 index);
+void webglGenerateMipmap(i32 target);
void webglGetProgramInfoLog(void);
int webglGetProgramParameter(i32 program, i32 param);
void webglGetShaderInfoLog(i32 shader, char *out_buf);
@@ -34,6 +38,8 @@ int webglGetShaderParameter(i32 shader, i32 param);
i32 webglGetUniformLocation(i32 program, const char name[static 1], i32 name_len);
void webglLinkProgram(i32 program);
void webglShaderSource(i32 shader, const char source[static 1], i32 source_len);
+void webglTexImage2D(i32 target, i32 level, i32 internalformat, i32 width, i32 height, i32 border, i32 format, i32 type, i32 pixels);
+void webglTexParameteri(i32 target, i32 pname, i32 param);
void webglUniform1f(i32 location, f32 value);
void webglUniform1i(i32 location, i32 value);
void webglUniform3f(i32 location, f32 x, f32 y, f32 z);
@@ -49,13 +55,31 @@ void webglViewport(i32 x, i32 y, i32 width, i32 height);
#define GL_LINES 0x0001
#define GL_TRIANGLES 0x0004
#define GL_SRC_ALPHA 0x0302
+#define GL_ONE_MINUS_SRC_ALPHA 0x0303
#define GL_FRONT_AND_BACK 0x0408
#define GL_DEPTH_TEST 0x0B71
#define GL_BLEND 0x0BE2
+#define GL_TEXTURE_2D 0x0DE1
+#define GL_UNSIGNED_BYTE 0x1401
#define GL_UNSIGNED_INT 0x1405
#define GL_FLOAT 0x1406
+#define GL_RGBA 0x1908
#define GL_FILL 0x1B02
+#define GL_LINEAR 0x2601
+#define GL_LINEAR_MIPMAP_LINEAR 0x2703
+#define GL_TEXTURE_MAG_FILTER 0x2800
+#define GL_TEXTURE_MIN_FILTER 0x2801
+#define GL_TEXTURE_WRAP_S 0x2802
+#define GL_TEXTURE_WRAP_T 0x2803
#define GL_CONSTANT_ALPHA 0x8003
+// NOTE(amin): this should normally be: `#define GL_BGRA 0x80E1`, however
+// OpenGL ES 3.0, and consequently WebGL 2.0, doesn't support GL_BGRA, so we
+// resort to this hideous hack just to get things working, along with a texture
+// swizzle in the shader.
+// TODO(amin): fix this in a better way
+#define GL_BGRA GL_RGBA
+#define GL_CLAMP_TO_EDGE 0x812F
+#define GL_TEXTURE0 0x84C0
#define GL_ARRAY_BUFFER 0x8892
#define GL_ELEMENT_ARRAY_BUFFER 0x8893
#define GL_STATIC_DRAW 0x88E4
@@ -88,6 +112,11 @@ static inline i32 _webgl_strlen(const char *str)
return len;
}
+void glActiveTexture(GLenum texture)
+{
+ webglActiveTexture(WEBGL_CAST_I32(texture));
+}
+
inline void glAttachShader(GLuint program, GLuint shader)
{
webglAttachShader(WEBGL_CAST_I32(program), WEBGL_CAST_I32(shader));
@@ -98,6 +127,11 @@ inline void glBindBuffer(GLenum target, GLuint buffer)
webglBindBuffer(WEBGL_CAST_I32(target), WEBGL_CAST_I32(buffer));
}
+void glBindTexture(GLenum target, GLuint texture)
+{
+ webglBindTexture(WEBGL_CAST_I32(target), WEBGL_CAST_I32(texture));
+}
+
inline void glBindVertexArray(GLuint array)
{
webglBindVertexArray(WEBGL_CAST_I32(array));
@@ -196,6 +230,18 @@ inline void glGenBuffers(GLsizei n, GLuint *buffers)
*buffers = (GLuint)buffer_id;
}
+void glGenerateMipmap(GLenum target)
+{
+ webglGenerateMipmap(WEBGL_CAST_I32(target));
+}
+
+void glGenTextures(GLsizei n, GLuint *textures)
+{
+ assert(n == 1);
+ i32 texture_id = webglCreateTexture();
+ *textures = (GLuint)texture_id;
+}
+
inline void glGenVertexArrays(GLsizei n, GLuint *arrays)
{
assert(n == 1);
@@ -248,6 +294,25 @@ inline void glShaderSource(GLuint shader, GLsizei count, const GLchar *const *st
webglShaderSource(WEBGL_CAST_I32(shader), s, l);
}
+void glTexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels)
+{
+ webglTexImage2D(
+ WEBGL_CAST_I32(target),
+ WEBGL_CAST_I32(level),
+ WEBGL_CAST_I32(internalformat),
+ WEBGL_CAST_I32(width),
+ WEBGL_CAST_I32(height),
+ WEBGL_CAST_I32(border),
+ WEBGL_CAST_I32(format),
+ WEBGL_CAST_I32(type),
+ WEBGL_CAST_I32(pixels));
+}
+
+void glTexParameteri(GLenum target, GLenum pname, GLint param)
+{
+ webglTexParameteri(WEBGL_CAST_I32(target), WEBGL_CAST_I32(pname), WEBGL_CAST_I32(param));
+}
+
inline void glUniform1f(GLint location, GLfloat v0)
{
webglUniform1f(WEBGL_CAST_I32(location), (f32)v0);