Compare commits

...

28 Commits

Author SHA1 Message Date
fb3eeb6ec9 Update known working version 2025-07-15 12:56:45 -04:00
263cf68ca2 Fix test common save image function 2025-07-15 13:01:42 -04:00
b8e616c8d6 Fix screen flashing on linux 2025-07-15 11:56:04 -04:00
1bfbab7a96 Fix formatting for example gif 2025-06-19 12:32:31 -04:00
d156005441 Add project retrospective 2025-06-19 12:28:00 -04:00
82389b8c12 change known working version 2025-06-11 10:19:06 -04:00
0a9d211942 Fix test build script 2025-06-11 10:22:09 -04:00
937693ce6a Update .gitea/workflows/make_png.yaml 2025-06-11 09:50:58 -04:00
365cad551b Allow cell size configuration 2025-01-31 13:59:04 -05:00
5fbeb1e4e9 remove lua 2025-01-31 08:11:57 -05:00
11ffc40bbb workaround to flush updating one call behind 2025-01-31 08:06:18 -05:00
db46885ee9 Update submodule URL 2025-01-15 23:31:26 -05:00
20fad92e4f Update latest known working commit 2024-12-18 19:42:49 -05:00
7c0c02b9af Fix image size/stride error in test program 2024-12-18 19:40:08 -05:00
1a1f4e8b13 Use integer coordinates instead of unsigned char. Ignores out-of-bounds coordinates. 2024-12-18 19:24:24 -05:00
shylie
6663804c88 Add audio; update Lua color API 2024-07-17 12:08:58 -04:00
Shylie
9d88ebbb3b Update submodule URL 2024-06-17 15:46:51 -04:00
Shylie
76bfd67f4e Fix not drawing all cells 2024-06-14 20:32:08 -04:00
Shylie
4811e4d970 Allow 1-tile offscreen rendering to prevent pop-in effect 2024-06-14 12:20:31 -04:00
Shylie
e402b5f5a3 Fix mouse coordinates 2024-06-13 18:08:02 -04:00
Shylie
139681a3fd Fix test not compiling 2024-06-11 00:41:21 -04:00
Shylie
45ec13db45 Add mouse callbacks 2024-06-11 00:40:43 -04:00
Shylie
996d14c93c Correctly initialize m_colors and m_scales 2024-06-07 12:36:27 -04:00
Shylie
d50fefc359 Make tinting and scaling per-cell instead of per-layer 2024-06-07 12:33:23 -04:00
Shylie
6444654f42 Improve lua error messages 2024-06-03 08:10:03 -04:00
Shylie
da1f32a014 Properly fixed texture bleeding 2024-06-02 14:34:07 -04:00
Shylie
71c052b38f Fix cells being drawn slightly larger than intended 2024-06-02 06:46:03 -04:00
Shylie
a39905c6cb Allow for up to 4096 sprites 2024-06-01 13:25:20 -04:00
25 changed files with 93085 additions and 540 deletions

View File

@@ -5,7 +5,7 @@ on: push
jobs: jobs:
build-app: build-app:
runs-on: linux_amd64 runs-on: shy-server
steps: steps:
- name: Checkout current - name: Checkout current
uses: actions/checkout@v4 uses: actions/checkout@v4
@@ -20,7 +20,7 @@ jobs:
fetch-depth: 0 fetch-depth: 0
submodules: recursive submodules: recursive
lfs: true lfs: true
- run: cd prev && git checkout 10a90a5f6a - run: cd prev && git checkout 263cf68ca2
- name: Build current - name: Build current
run: cmake -S curr -B curr/build -DGLERMINAL_TEST=ON && cmake --build curr/build run: cmake -S curr -B curr/build -DGLERMINAL_TEST=ON && cmake --build curr/build
- name: Build previous - name: Build previous

5
.gitignore vendored
View File

@@ -1 +1,4 @@
build/ build/
cmake-build-*/
.idea/
.cache/

5
.gitmodules vendored
View File

@@ -1,6 +1,3 @@
[submodule "glfw"] [submodule "glfw"]
path = glfw path = glfw
url = https://git.shylie.info/shylie/glfw.git url = https://github.com/glfw/glfw.git
[submodule "Lua"]
path = Lua
url = https://git.shylie.info/shylie/Lua.git

View File

@@ -1,8 +1,8 @@
cmake_minimum_required(VERSION 3.28) cmake_minimum_required(VERSION 3.28)
project(glerminal project(glerminal
VERSION 0.1.0 VERSION 0.1.0
LANGUAGES C CXX LANGUAGES C CXX
) )
option(GLERMINAL_OPENGL_DEBUG_CONTEXT "" OFF) option(GLERMINAL_OPENGL_DEBUG_CONTEXT "" OFF)
@@ -11,72 +11,58 @@ set(GLERMINAL_GRID_WIDTH 40 CACHE STRING "")
set(GLERMINAL_GRID_HEIGHT 25 CACHE STRING "") set(GLERMINAL_GRID_HEIGHT 25 CACHE STRING "")
set(GLERMINAL_LAYER_COUNT 64 CACHE STRING "") set(GLERMINAL_LAYER_COUNT 64 CACHE STRING "")
set(GLERMINAL_CELL_SCALE 4 CACHE STRING "") set(GLERMINAL_CELL_SCALE 4 CACHE STRING "")
set(GLERMINAL_CELL_SIZE 8 CACHE STRING "")
configure_file(source/glerminal-config.h.in glerminal-config.h @ONLY) configure_file(source/glerminal-config.h.in glerminal-config.h @ONLY)
add_subdirectory(glfw) add_subdirectory(glfw)
add_subdirectory(Lua)
add_library(glerminallib STATIC add_library(glerminal STATIC
${CMAKE_CURRENT_BINARY_DIR}/glerminal-config.h ${CMAKE_CURRENT_BINARY_DIR}/glerminal-config.h
include/glerminal.h include/glerminal.h
source/stb_image.h source/stb_image.h
source/glerminal-private.h source/glerminal-private.h
source/glerminal.cpp source/glerminal.cpp
source/glad/glad.h source/glad/glad.h
source/KHR/khrplatform.h source/KHR/khrplatform.h
source/glad.c source/glad.c
source/miniaudio.h
source/miniaudio.c
) )
set_target_properties(glerminallib set_target_properties(glerminal
PROPERTIES PROPERTIES
CXX_STANDARD 11 CXX_STANDARD 11
) )
target_include_directories(glerminallib target_include_directories(glerminal
PUBLIC PUBLIC
include include
${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}
PRIVATE PRIVATE
source source
)
target_link_libraries(glerminallib
PUBLIC
glfw
)
target_compile_definitions(glerminallib
PUBLIC
GLERMINAL_VERSION=${PROJECT_VERSION}
GLERMINAL_VERSION_MAJOR=${PROJECT_VERSION_MAJOR}
GLERMINAL_VERSION_MINOR=${PROJECT_VERSION_MINOR}
GLERMINAL_VERSION_PATCH=${PROJECT_VERSION_PATCH}
)
if (MSVC)
target_link_options(glerminallib
PUBLIC
"/ENTRY:mainCRTStartup"
)
endif()
add_executable(glerminal WIN32
source/glerminal-main.cpp
) )
target_link_libraries(glerminal target_link_libraries(glerminal
PRIVATE PUBLIC
glerminallib glfw
lua_static
) )
if (PROJECT_IS_TOP_LEVEL) target_compile_definitions(glerminal
add_subdirectory(examples examples) PUBLIC
GLERMINAL_VERSION=${PROJECT_VERSION}
GLERMINAL_VERSION_MAJOR=${PROJECT_VERSION_MAJOR}
GLERMINAL_VERSION_MINOR=${PROJECT_VERSION_MINOR}
GLERMINAL_VERSION_PATCH=${PROJECT_VERSION_PATCH}
)
if(PROJECT_IS_TOP_LEVEL)
add_subdirectory(examples examples)
endif() endif()
if (GLERMINAL_TEST) if(GLERMINAL_TEST)
add_subdirectory(tests tests) add_subdirectory(tests tests)
endif() endif()

1
Lua

Submodule Lua deleted from 88246d621a

37
PROJECT_RETROSPECTIVE.md Normal file
View File

@@ -0,0 +1,37 @@
# A look back on what I learned
## why?
I came across an art/rendering style called "sprite stacking".
I found sprite stacking very interesting in comparison to traditional methods,
and wanted to experiment with the style.
## challenge
This was my first big graphics-based project. Computer graphics
at the low-level was new to me. I took this as an opportunity to
learn the basics of OpenGL. This learning process was the most
difficult part of the project, especially the domain-specific
rendering techniques used.
## how?
I found some [great tutorials](https://learnopengl.com) to get started,
and though these helped, I also did a lot of individual
learning through experimentation and discussion with peers.
## results
In the end, the library had most of the functionality
I originally wanted. The library can draw up to 256 layers of icons in a grid,
with optional offsets and tints per icon. Here's an example with only 7 library calls:
![example gif](./example.gif)
## evaluation
Besides learning the basics of OpenGL, I also learned
how to use CMake to help build the library. Also, I set up
continuous integration for automated testing on a git commit.
If I were to rewrite this library, I would definitely invest more
into the automated testing process.

View File

@@ -1,2 +1,4 @@
# termg # glerminal
glerminal is a tile-based graphics library created
with the intent to use a sprite-stacking art style.

BIN
example.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 MiB

View File

@@ -36,5 +36,5 @@ list(TRANSFORM EXAMPLE_RESOURCES PREPEND ${CMAKE_CURRENT_BINARY_DIR}/)
foreach(SOURCE_FILE ${EXAMPLE_SOURCES}) foreach(SOURCE_FILE ${EXAMPLE_SOURCES})
get_filename_component(SOURCE_FILENAME ${SOURCE_FILE} NAME_WLE) get_filename_component(SOURCE_FILENAME ${SOURCE_FILE} NAME_WLE)
add_executable(${SOURCE_FILENAME} WIN32 ${SOURCE_FILE} ${EXAMPLE_RESOURCES}) add_executable(${SOURCE_FILENAME} WIN32 ${SOURCE_FILE} ${EXAMPLE_RESOURCES})
target_link_libraries(${SOURCE_FILENAME} PRIVATE glerminallib) target_link_libraries(${SOURCE_FILENAME} PRIVATE glerminal)
endforeach() endforeach()

View File

@@ -9,9 +9,9 @@ namespace
glerminal_load_sprites_file("resources/atlas.png"); glerminal_load_sprites_file("resources/atlas.png");
} }
void mainloop(float dt) void mainloop(double dt)
{ {
static float time = 1; static double time = 1;
time += dt; time += dt;
@@ -30,8 +30,8 @@ namespace
{ {
for (int k = 0; k < LAYER_COUNT; k++) for (int k = 0; k < LAYER_COUNT; k++)
{ {
glerminal_set(i, j, k, rand() % 4); glerminal_set(i + 1, j + 1, k, rand() % 4);
glerminal_offset(i, j, k, (rand() * rand()) % 64 - 32, (rand() * rand()) % 64 - 32); glerminal_offset(i + 1, j + 1, k, (rand() * rand()) % 64 - 32, (rand() * rand()) % 64 - 32);
} }
} }
} }
@@ -42,5 +42,5 @@ namespace
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
glerminal_run(init, mainloop, nullptr, nullptr); glerminal_run({init, mainloop});
} }

View File

@@ -9,25 +9,25 @@ namespace
{ {
glerminal_load_sprites_file("resources/basic.png"); glerminal_load_sprites_file("resources/basic.png");
for (int i = 0; i < GRID_WIDTH; i++) for (int i = 0; i < GRID_WIDTH + 2; i++)
{ {
for (int j = 0; j < GRID_HEIGHT; j++) for (int j = 0; j < GRID_HEIGHT + 2; j++)
{ {
glerminal_set(i, j, 0, 1); glerminal_set(i, j, 0, 1);
} }
} }
} }
void mainloop(float dt) void mainloop(double dt)
{ {
static float time = 0; static double time = 0;
time += dt; time += dt;
time = fmodf(time, 3.1415926f * 2); time = fmodf(time, 3.1415926f * 2);
for (int i = 0; i < GRID_WIDTH; i++) for (int i = 0; i < GRID_WIDTH + 2; i++)
{ {
for (int j = 0; j < GRID_HEIGHT; j++) for (int j = 0; j < GRID_HEIGHT + 2; j++)
{ {
glerminal_offset(i, j, 0, cosf(time - i / 3.1415f), sinf(time - j / 3.1415f)); glerminal_offset(i, j, 0, cosf(time - i / 3.1415f), sinf(time - j / 3.1415f));
} }
@@ -39,5 +39,5 @@ namespace
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
glerminal_run(init, mainloop, nullptr, nullptr); glerminal_run({init, mainloop});
} }

View File

@@ -18,22 +18,16 @@ namespace
{ {
glerminal_load_sprites_file("resources/rogue.png"); glerminal_load_sprites_file("resources/rogue.png");
for (int i = 0; i < WALL_LAYERS; i++) for (int i = 0; i < GRID_WIDTH + 2; i++)
{ {
const unsigned char v = (i + 1) * (256 / WALL_LAYERS) - 1; for (int j = 0; j < GRID_HEIGHT + 2; j++)
const unsigned int j = (0xFF << 24) | (v << 16) | (v << 8) | v;
glerminal_layer_color(i, j);
glerminal_layer_scale(i, (i / static_cast<float>(WALL_LAYERS)) + 1);
}
for (int i = 0; i < GRID_WIDTH; i++)
{
for (int j = 0; j < GRID_HEIGHT; j++)
{ {
for (int k = 0; k < WALL_LAYERS; k++) for (int k = 0; k < WALL_LAYERS; k++)
{ {
if (i == 0 || j == 0 || i == GRID_WIDTH - 1 || j == GRID_HEIGHT - 1) const unsigned char v = (k + 1) * (256 / WALL_LAYERS) - 1;
const unsigned int c = (0xFF << 24) | (v << 16) | (v << 8) | v;
if (i == 1 || j == 1 || i == GRID_WIDTH || j == GRID_HEIGHT)
{ {
glerminal_set(i, j, k, floor); glerminal_set(i, j, k, floor);
} }
@@ -41,22 +35,25 @@ namespace
{ {
glerminal_set(i, j, k, wall); glerminal_set(i, j, k, wall);
} }
glerminal_color(i, j, k, c);
glerminal_scale(i, j, k, (k / static_cast<float>(WALL_LAYERS)) + 1);
} }
} }
} }
} }
void mainloop(float dt) void mainloop(double dt)
{ {
static float time = 0; static double time = 0;
time += dt; time += dt;
const float cx = GRID_WIDTH / 2.0f * cosf(time / 2) + GRID_WIDTH / 2.0f; const float cx = GRID_WIDTH / 2.0f * cosf(time / 2) + GRID_WIDTH / 2.0f + 0.5f;
const float cy = GRID_HEIGHT / 2.0f * sinf(time / 2) + GRID_HEIGHT / 2.0f; const float cy = GRID_HEIGHT / 2.0f * sinf(time / 2) + GRID_HEIGHT / 2.0f + 0.5f;
for (int i = 0; i < GRID_WIDTH; i++) for (int i = 0; i < GRID_WIDTH + 2; i++)
{ {
for (int j = 0; j < GRID_HEIGHT; j++) for (int j = 0; j < GRID_HEIGHT + 2; j++)
{ {
for (int k = 0; k < WALL_LAYERS; k++) for (int k = 0; k < WALL_LAYERS; k++)
{ {
@@ -83,5 +80,5 @@ namespace
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
glerminal_run(init, mainloop, nullptr, nullptr); glerminal_run({init, mainloop});
} }

View File

@@ -11,40 +11,41 @@ namespace
glerminal_load_sprites_file("resources/towers.png"); glerminal_load_sprites_file("resources/towers.png");
for (int i = 0; i < LAYER_COUNT; i++)
{
constexpr unsigned char c = 16;
const unsigned char v = (255 - c) * powf((256 / LAYER_COUNT * i - 1) / 256.0f, 2.0f) + c;
const unsigned int j = (0xFF << 24) | (v << 16) | (v << 8) | v;
glerminal_layer_color(i, j);
glerminal_layer_scale(i, i / static_cast<float>(LAYER_COUNT) + 1);
}
for (int i = 0; i < GRID_WIDTH; i++) for (int i = 0; i < GRID_WIDTH + 2; i++)
{ {
for (int j = 0; j < GRID_HEIGHT; j++) for (int j = 0; j < GRID_HEIGHT + 2; j++)
{ {
const int c = rand() % (LAYER_COUNT * 3 / 4) + LAYER_COUNT / 4; const int c = rand() % (LAYER_COUNT * 3 / 4) + LAYER_COUNT / 4;
for (int k = 0; k < c; k++) for (int k = 0; k < c; k++)
{ {
glerminal_set(i, j, k, 1); glerminal_set(i, j, k, 1);
} }
for (int k = 0; k < LAYER_COUNT; k++)
{
constexpr unsigned char min = 16;
const unsigned char v = (255 - min) * powf((256 / LAYER_COUNT * k - 1) / 256.0f, 2.0f) + min;
const unsigned int col = (0xFF << 24) | (v << 16) | (v << 8) | v;
glerminal_color(i, j, k, col);
glerminal_scale(i, j, k, k / static_cast<float>(LAYER_COUNT) + 1);
}
} }
} }
} }
void mainloop(float dt) void mainloop(double dt)
{ {
static float time = 0; static double time = 0;
time += dt; time += dt;
const float cx = (GRID_WIDTH / 2.0f) * cosf(time / 3.1415f) + (GRID_WIDTH / 2.0f); const float cx = (GRID_WIDTH / 2.0f) * cosf(time / 3.1415f) + (GRID_WIDTH / 2.0f) + 0.5f;
const float cy = (GRID_HEIGHT / 2.0f) * sinf(time / 3.1415f) + (GRID_HEIGHT / 2.0f); const float cy = (GRID_HEIGHT / 2.0f) * sinf(time / 3.1415f) + (GRID_HEIGHT / 2.0f) + 0.5f;
for (int i = 0; i < GRID_WIDTH; i++) for (int i = 0; i < GRID_WIDTH + 2; i++)
{ {
for (int j = 0; j < GRID_WIDTH; j++) for (int j = 0; j < GRID_WIDTH + 2; j++)
{ {
for (int k = 0; k < LAYER_COUNT; k++) for (int k = 0; k < LAYER_COUNT; k++)
{ {
@@ -63,5 +64,5 @@ namespace
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
glerminal_run(init, mainloop, nullptr, nullptr); glerminal_run({init, mainloop});
} }

View File

@@ -9,17 +9,24 @@ extern "C"
#endif #endif
typedef void (*glerminal_init_cb)(); typedef void (*glerminal_init_cb)();
typedef void (*glerminal_main_cb)(float dt); typedef void (*glerminal_main_cb)(double dt);
typedef void (*glerminal_keys_cb)(int key); typedef void (*glerminal_keys_cb)(int key);
typedef void (*glerminal_mousemoved_cb)(double x, double y);
typedef void (*glerminal_mousepress_cb)(int button, double x, double y);
typedef struct {
glerminal_init_cb init;
glerminal_main_cb main;
glerminal_keys_cb keypress, keyrelease;
glerminal_mousemoved_cb moved;
glerminal_mousepress_cb mousepress, mouserelease;
} glerminal_init_params;
/** /**
* @brief Call init once, then run the application's mainloop * @brief Call init once, then run the application's mainloop
* @param init initialization callback * @param params Initialization parameters
* @param main main calllback
* @param pressed key pressed callback (can be null)
* @param released key released callback (can be null)
*/ */
void glerminal_run(glerminal_init_cb init, glerminal_main_cb main, glerminal_keys_cb pressed, glerminal_keys_cb released); void glerminal_run(glerminal_init_params params);
void glerminal_quit(); void glerminal_quit();
@@ -30,42 +37,46 @@ void glerminal_flush();
/** /**
* @brief Set a cell's sprite * @brief Set a cell's sprite
* @param x position of the cell in the range [0, 40) * @param x position of the cell in the range [0, GRID_WIDTH)
* @param y position of the cell in the range [0, 25) * @param y position of the cell in the range [0, GRID_HEIGHT)
* @param layer layer of the cell in the range [0, 8) * @param layer layer of the cell in the range [0, LAYER_COUNT)
* @param sprite sprite's index in the range [0, 256) * @param sprite sprite's index in the range [0, 4096)
*/ */
void glerminal_set(unsigned char x, unsigned char y, unsigned char layer, unsigned char sprite); void glerminal_set(int x, int y, int layer, unsigned short sprite);
/** /**
* @brief Get a cell's sprite * @brief Get a cell's sprite
* @param x position of the cell in the range [0, 40) * @param x position of the cell in the range [0, GRID_WIDTH)
* @param y position of the cell in the range [0, 25) * @param y position of the cell in the range [0, GRID_HEIGHT)
* @param layer layer of the cell in the range [0, 8) * @param layer layer of the cell in the range [0, LAYER_COUNT)
* @return sprite index currently assigned to the cell * @return sprite index currently assigned to the cell
*/ */
unsigned char glerminal_get(unsigned char x, unsigned char y, unsigned char layer); unsigned short glerminal_get(int x, int y, int layer);
/** /**
* @brief Set a cell's offset * @brief Set a cell's offset
* @param x position of the cell in the range [0, 40) * @param x position of the cell in the range [0, GRID_WIDTH)
* @param y position of the cell in the range [0, 25) * @param y position of the cell in the range [0, GRID_HEIGHT)
* @param layer layer of the cell in the range [0, 8) * @param layer layer of the cell in the range [0, LAYER_COUNT)
* @param x_offset offset of the cell on the x axis in the range [-128, 127], where 0 is no offset * @param x_offset offset of the cell on the x axis in cells
* @param y_offset offset of the cell on the y axis in the range [-128, 127], where 0 is no offset * @param y_offset offset of the cell on the y axis in cells
*/ */
void glerminal_offset(unsigned char x, unsigned char y, unsigned char layer, float x_offset, float y_offset); void glerminal_offset(int x, int y, int layer, float x_offset, float y_offset);
/** /**
* @brief Set a layer's color * @brief Set a cell's color
* @param layer The layer to modify * @param x position of the cell in the range [0, GRID_WIDTH)
* @param y position of the cell in the range [0, GRID_HEIGHT)
* @param layer layer of the cell in the range [0, LAYER_COUNT)
* @param color The new color * @param color The new color
*/ */
void glerminal_layer_color(unsigned char layer, unsigned int color); void glerminal_color(int x, int y, int layer, unsigned int color);
/** /**
* @brief Set a layer's scale * @brief Set a cell's scale
* @param layer The layer to modify * @param x position of the cell in the range [0, GRID_WIDTH)
* @param y position of the cell in the range [0, GRID_HEIGHT)
* @param layer layer of the cell in the range [0, LAYER_COUNT)
* @param scale The new scale * @param scale The new scale
*/ */
void glerminal_layer_scale(unsigned char layer, float scale); void glerminal_scale(int x, int y, int layer, float scale);
/** /**
* @brief Load sprites from a png file * @brief Load sprites from a png file
@@ -81,6 +92,8 @@ int glerminal_load_sprites_file(const char* filename);
*/ */
int glerminal_load_sprites_buffer(unsigned char width, unsigned char height, const unsigned int* buffer); int glerminal_load_sprites_buffer(unsigned char width, unsigned char height, const unsigned int* buffer);
void glerminal_sound(const char* name);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@@ -1,29 +0,0 @@
local time = 0
function glerminal.init()
if not glerminal.sprites('sprites.png') then
print('failed to load sprites')
end
for i = 1, 40 do
for j = 1, 25 do
glerminal.set(i, j, 1, 2)
end
end
glerminal.tint(1, 0xA0FFFFFF)
glerminal.flush()
end
function glerminal.main(dt)
time = time + dt / 2
for i = 1, 40 do
for j = 1, 25 do
glerminal.offset(i, j, 1, math.cos(time * i / math.pi), math.sin(time * j / math.pi))
end
end
glerminal.flush()
end

View File

@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6dee3efcdfa72e54a602f69643907bb8e8a3328d2b40c2116dce22ff179d8892
size 182

View File

@@ -16,7 +16,8 @@ enum
GRID_WIDTH = @GLERMINAL_GRID_WIDTH@, GRID_WIDTH = @GLERMINAL_GRID_WIDTH@,
GRID_HEIGHT = @GLERMINAL_GRID_HEIGHT@, GRID_HEIGHT = @GLERMINAL_GRID_HEIGHT@,
LAYER_COUNT = @GLERMINAL_LAYER_COUNT@, LAYER_COUNT = @GLERMINAL_LAYER_COUNT@,
CELL_SCALE = @GLERMINAL_CELL_SCALE@ CELL_SCALE = @GLERMINAL_CELL_SCALE@,
CELL_SIZE = @GLERMINAL_CELL_SIZE@
}; };
#ifdef __cplusplus #ifdef __cplusplus

View File

@@ -1,199 +0,0 @@
#include <glerminal.h>
#define GLFW_INCLUDE_NONE
#include <GLFW/glfw3.h>
extern "C"
{
#include <lualib.h>
#include <lauxlib.h>
}
#include <iostream>
#ifdef _WIN32
#define WINDOWS_LEAN_AND_MEAN
#include <Windows.h>
#define CHANGE_WORKING_DIRECTORY(path) !(SetCurrentDirectory(path))
#else
#include <unistd.h>
#define CHANGE_WORKING_DIRECTORY(path) chdir(path)
#endif
namespace
{
int lglerminal_quit(lua_State* L)
{
glerminal_quit();
return 0;
}
int lglerminal_flush(lua_State* L)
{
glerminal_flush();
return 0;
}
int lglerminal_set(lua_State* L)
{
const unsigned char x = luaL_checkinteger(L, 1) - 1;
const unsigned char y = luaL_checkinteger(L, 2) - 1;
const unsigned char layer = luaL_checkinteger(L, 3) - 1;
const unsigned char sprite = luaL_checkinteger(L, 4) - 1;
glerminal_set(x, y, layer, sprite);
return 0;
}
int lglerminal_get(lua_State* L)
{
const unsigned char x = luaL_checkinteger(L, 1) - 1;
const unsigned char y = luaL_checkinteger(L, 2) - 1;
const unsigned char layer = luaL_checkinteger(L, 3) - 1;
lua_pushnumber(L, glerminal_get(x, y, layer) + 1);
return 1;
}
int lglerminal_offset(lua_State* L)
{
const unsigned char x = luaL_checkinteger(L, 1) - 1;
const unsigned char y = luaL_checkinteger(L, 2) - 1;
const unsigned char layer = luaL_checkinteger(L, 3) - 1;
const float ox = luaL_checknumber(L, 4);
const float oy = luaL_checknumber(L, 5);
glerminal_offset(x, y, layer, ox, oy);
return 0;
}
int lglerminal_layer_color(lua_State* L)
{
const unsigned char layer = luaL_checkinteger(L, 1) - 1;
const unsigned int color = luaL_checkinteger(L, 2);
glerminal_layer_color(layer, color);
return 0;
}
int lglerminal_layer_scale(lua_State* L)
{
const unsigned char layer = luaL_checkinteger(L, 1) - 1;
const float scale = luaL_checknumber(L, 2);
glerminal_layer_scale(layer, scale);
return 0;
}
int lglerminal_load_sprites_file(lua_State* L)
{
const char* path = luaL_checkstring(L, 1);
lua_pushboolean(L, glerminal_load_sprites_file(path));
return 1;
}
const luaL_Reg lglerminal_methods[] =
{
{ "quit", lglerminal_quit },
{ "flush", lglerminal_flush },
{ "set", lglerminal_set },
{ "get", lglerminal_get },
{ "offset", lglerminal_offset },
{ "tint", lglerminal_layer_color },
{ "scale", lglerminal_layer_scale },
{ "sprites", lglerminal_load_sprites_file },
{ nullptr, nullptr }
};
lua_State* L;
void init()
{
lua_getglobal(L, "glerminal");
lua_getfield(L, -1, "init");
lua_remove(L, -2);
if (lua_pcall(L, 0, 0, 0) != LUA_OK)
{
// ignore error for now
lua_remove(L, -1);
}
}
void mainloop(float dt)
{
lua_getglobal(L, "glerminal");
lua_getfield(L, -1, "main");
lua_remove(L, -2);
lua_pushnumber(L, dt);
if (lua_pcall(L, 1, 0, 0) != LUA_OK)
{
// ignore error for now
lua_remove(L, -1);
}
}
void pressed(int key)
{
const char* const name = glfwGetKeyName(key, 0);
if (name)
{
lua_getglobal(L, "glerminal");
lua_getfield(L, -1, "pressed");
lua_remove(L, -2);
lua_pushstring(L, name);
if (lua_pcall(L, 1, 0, 0) != LUA_OK)
{
// ignore error for now
lua_remove(L, -1);
}
}
}
void released(int key)
{
const char* const name = glfwGetKeyName(key, 0);
if (name)
{
lua_getglobal(L, "glerminal");
lua_getfield(L, -1, "released");
lua_remove(L, -2);
lua_pushstring(L, name);
if (lua_pcall(L, 1, 0, 0) != LUA_OK)
{
// ignore error for now
lua_remove(L, -1);
}
}
}
}
int main(int argc, char** argv)
{
if (argc == 1)
{
std::cout << "Usage: " << argv[0] << " <project directory>" << std::endl;
return 0;
}
if (CHANGE_WORKING_DIRECTORY(argv[1]))
{
std::cout << "Failed to find project directory" << std::endl;
return 1;
}
L = luaL_newstate();
luaL_openlibs(L);
luaL_newlib(L, lglerminal_methods);
lua_setglobal(L, "glerminal");
luaL_dofile(L, "main.lua");
glerminal_run(init, mainloop, pressed, released);
lua_close(L);
return 0;
}

View File

@@ -2,30 +2,36 @@
#define GLERMINAL_PRIVATE_H #define GLERMINAL_PRIVATE_H
#include "glerminal.h" #include "glerminal.h"
#include "miniaudio.h"
#include <stb_image.h> #include <stb_image.h>
#include <glad/glad.h> #include <glad/glad.h>
#include <GLFW/glfw3.h> #include <GLFW/glfw3.h>
#include <iostream> #include <iostream>
#include <fstream> #include <map>
#include <stdexcept> #include <string>
namespace glerminal namespace glerminal
{ {
constexpr unsigned int CELL_SIZE = 8; constexpr unsigned int MAX_SPRITES_ROW = 64;
constexpr unsigned int MAX_SPRITES = MAX_SPRITES_ROW * MAX_SPRITES_ROW;
constexpr unsigned int GRID_WIDTH = ::GRID_WIDTH; constexpr unsigned int GRID_WIDTH = ::GRID_WIDTH;
constexpr unsigned int GRID_HEIGHT = ::GRID_HEIGHT; constexpr unsigned int GRID_HEIGHT = ::GRID_HEIGHT;
constexpr unsigned int LAYER_COUNT = ::LAYER_COUNT; constexpr unsigned int LAYER_COUNT = ::LAYER_COUNT;
constexpr unsigned int CELL_SCALE = ::CELL_SCALE; constexpr unsigned int CELL_SCALE = ::CELL_SCALE;
constexpr unsigned int CELL_SIZE = ::CELL_SIZE;
constexpr unsigned int GRID_AREA = GRID_WIDTH * GRID_HEIGHT; constexpr unsigned int GRID_AREA = GRID_WIDTH * GRID_HEIGHT;
constexpr unsigned int SCREEN_WIDTH = GRID_WIDTH * CELL_SIZE * CELL_SCALE; constexpr unsigned int SCREEN_WIDTH = GRID_WIDTH * CELL_SIZE * CELL_SCALE;
constexpr unsigned int SCREEN_HEIGHT = GRID_HEIGHT * CELL_SIZE * CELL_SCALE; constexpr unsigned int SCREEN_HEIGHT = GRID_HEIGHT * CELL_SIZE * CELL_SCALE;
constexpr unsigned int SOUND_CHANNELS = 8;
constexpr unsigned int GRID_AREA_2 = (GRID_WIDTH + 2) * (GRID_HEIGHT + 2);
class glerminal class glerminal
{ {
public: public:
glerminal(glerminal_init_cb init, glerminal_main_cb main, glerminal_keys_cb pressed, glerminal_keys_cb released); explicit glerminal(glerminal_init_params params);
~glerminal(); ~glerminal();
glerminal(const glerminal&) = delete; glerminal(const glerminal&) = delete;
@@ -39,12 +45,14 @@ namespace glerminal
void flush(); void flush();
void set(unsigned char x, unsigned char y, unsigned char layer, unsigned char sprite); void set(int x, int y, int layer, unsigned short sprite);
unsigned char get(unsigned char x, unsigned char y, unsigned char layer) const; unsigned short get(int x, int y, int layer) const;
void offset(unsigned char x, unsigned char y, unsigned char layer, float x_offset, float y_offset); void offset(int x, int y, int layer, float x_offset, float y_offset);
void layer_color(unsigned char layer, unsigned int color); void color(int x, int y, int layer, unsigned int color);
void layer_scale(unsigned char layer, float scale); void scale(int x,int y, int layer, float scale);
void load_atlas(unsigned char w, unsigned char h, const unsigned int* data); void load_atlas(unsigned char w, unsigned char h, const unsigned int* data);
bool load_sound(const char* name);
void play_sound(const char* name);
private: private:
// glfw data // glfw data
@@ -65,26 +73,26 @@ namespace glerminal
unsigned int m_framebuffer_backing_texture; unsigned int m_framebuffer_backing_texture;
unsigned int m_screen_framebuffer; unsigned int m_screen_framebuffer;
unsigned int m_screen_framebuffer_backing_texture; unsigned int m_screen_framebuffer_backing_texture;
unsigned int m_layer_colors_buffer; unsigned int m_colors_instance_vbo;
unsigned int m_layer_scales_buffer; unsigned int m_scales_instance_vbo;
unsigned int m_screen_size_uniform_location;
unsigned int m_palette_uniform_location;
// per-cell data // per-cell data
unsigned char m_cells[GRID_AREA * LAYER_COUNT]; unsigned short m_cells[GRID_AREA_2 * LAYER_COUNT];
float m_offsets[GRID_AREA * LAYER_COUNT * 2]; float m_offsets[GRID_AREA_2 * LAYER_COUNT * 2];
unsigned char m_colors[GRID_AREA_2 * LAYER_COUNT * 4];
// per-layer data float m_scales[GRID_AREA_2 * LAYER_COUNT];
float m_layer_colors[LAYER_COUNT * 4];
float m_layer_scales[LAYER_COUNT];
// library state // library state
unsigned int m_sprites[CELL_SIZE * CELL_SIZE * (1 << (8 * sizeof(*m_cells)))]; unsigned int m_sprites[(CELL_SIZE + 2) * (CELL_SIZE + 2) * MAX_SPRITES];
glerminal_main_cb m_main; glerminal_main_cb m_main;
glerminal_keys_cb m_pressed, m_released; glerminal_keys_cb m_keypressed, m_keyreleased;
glerminal_mousemoved_cb m_mousemoved;
glerminal_mousepress_cb m_mousepressed, m_mousereleased;
ma_engine m_audio_engine;
std::map<std::string, ma_sound> m_sounds;
#ifdef GLERMINAL_OPENGL_DEBUG_CONTEXT #ifdef GLERMINAL_OPENGL_DEBUG_CONTEXT
mutable std::ofstream m_log; mutable std::ofstream m_log;
@@ -94,15 +102,21 @@ namespace glerminal
void init_glfw(); void init_glfw();
void init_gl(); void init_gl();
void init_audio();
void deinit_glfw(); void deinit_glfw();
void deinit_gl(); void deinit_gl();
void deinit_audio();
void update_sprites(); void update_sprites();
void update_layer_colors(); void update_colors();
void update_layer_scales(); void update_scales();
static void glfw_key_handler(GLFWwindow* window, int key, int scancode, int action, int mods); static void glfw_key_handler(GLFWwindow* window, int key, int scancode, int action, int mods);
static void glfw_mousemoved_handler(GLFWwindow* window, double x, double y);
static void glfw_mousepress_handler(GLFWwindow* window, int button, int action, int mods);
}; };
} }
#endif//GLERMINAL_PRIVATE_H #endif//GLERMINAL_PRIVATE_H

View File

@@ -1,12 +1,14 @@
#define STB_IMAGE_IMPLEMENTATION #define STB_IMAGE_IMPLEMENTATION
#define STBI_ONLY_PNG #define STBI_ONLY_PNG
#define STBI_MAX_DIMENSIONS 128 #define STBI_MAX_DIMENSIONS 2048
#include "glerminal-private.h" #include "glerminal-private.h"
#define GRID_SIZE_UNIFORM_NAME "grid_size" #define GRID_SIZE_UNIFORM_NAME "grid_size"
#define SPRITES_UNIFORM_NAME "sprites" #define SPRITES_UNIFORM_NAME "sprites"
#define LAYERS_UNIFORM_NAME "layers" #define LAYERS_UNIFORM_NAME "layers"
#define LAYER_COUNT_UNIFORM_NAME "layer_count" #define LAYER_COUNT_UNIFORM_NAME "layer_count"
#define ATLAS_WIDTH_UNIFORM_NAME "atlas_width"
#define CELL_SIZE_UNIFORM_NAME "cell_size"
namespace namespace
{ {
@@ -30,15 +32,11 @@ namespace
"layout (location = 0) in vec2 position;\n" "layout (location = 0) in vec2 position;\n"
"layout (location = 1) in vec2 offset;\n" "layout (location = 1) in vec2 offset;\n"
"layout (location = 2) in int sprite;\n" "layout (location = 2) in int sprite;\n"
"layout (location = 3) in vec4 color;\n"
"layout (location = 4) in float scale;\n"
"uniform float " CELL_SIZE_UNIFORM_NAME ";\n"
"uniform vec4 " GRID_SIZE_UNIFORM_NAME ";\n" "uniform vec4 " GRID_SIZE_UNIFORM_NAME ";\n"
"layout (std430, binding = 0) buffer LayerScales" "uniform int " ATLAS_WIDTH_UNIFORM_NAME ";\n"
"{\n"
" float scales[];\n"
"} lss;\n"
"layout (std430, binding = 1) buffer LayerColors"
"{\n"
" vec4 colors[];\n"
"} lcs;\n"
"out VS_OUT {\n" "out VS_OUT {\n"
" flat int sprite;\n" " flat int sprite;\n"
" flat int layer;\n" " flat int layer;\n"
@@ -51,10 +49,10 @@ namespace
" vec2 scaled_offset = 2 * offset * " GRID_SIZE_UNIFORM_NAME ".zw;\n" " vec2 scaled_offset = 2 * offset * " GRID_SIZE_UNIFORM_NAME ".zw;\n"
" vs_out.sprite = sprite;\n" " vs_out.sprite = sprite;\n"
" vs_out.layer = layer;\n" " vs_out.layer = layer;\n"
" vs_out.layer_color = lcs.colors[layer];\n" " vs_out.layer_color = color;\n"
" vs_out.texcoord = vec2(position.x + 1, -position.y);\n" " vs_out.texcoord = vec2(sprite % " ATLAS_WIDTH_UNIFORM_NAME " + position.x + 1, (sprite / " ATLAS_WIDTH_UNIFORM_NAME ") - position.y) / vec2(" ATLAS_WIDTH_UNIFORM_NAME ") + vec2(-(2 * position.x + 1) * " GRID_SIZE_UNIFORM_NAME ".z / (2 * " CELL_SIZE_UNIFORM_NAME "), (2 * position.y + 1) * " GRID_SIZE_UNIFORM_NAME ".z / (2 * " CELL_SIZE_UNIFORM_NAME "));\n"
" vec2 cell_position = vec2(lss.scales[layer] + (gl_InstanceID % int(" GRID_SIZE_UNIFORM_NAME ".y)) - " GRID_SIZE_UNIFORM_NAME ".x * floor((gl_InstanceID % int(" GRID_SIZE_UNIFORM_NAME ".y)) * " GRID_SIZE_UNIFORM_NAME ".z), -floor((gl_InstanceID % int(" GRID_SIZE_UNIFORM_NAME ".y)) * " GRID_SIZE_UNIFORM_NAME ".z));\n" " vec2 cell_position = vec2(scale + (gl_InstanceID % int(" GRID_SIZE_UNIFORM_NAME ".y)) - " GRID_SIZE_UNIFORM_NAME ".x * floor((gl_InstanceID % int(" GRID_SIZE_UNIFORM_NAME ".y)) / " GRID_SIZE_UNIFORM_NAME ".x), -floor((gl_InstanceID % int(" GRID_SIZE_UNIFORM_NAME ".y)) / " GRID_SIZE_UNIFORM_NAME ".x));\n"
" vec2 temp = ((position + vec2(-0.5, 0.5)) * lss.scales[layer] + cell_position + vec2(0.5, -0.5)) * " GRID_SIZE_UNIFORM_NAME ".zw * 2 + vec2(-1, 1);\n" " vec2 temp = ((position + vec2(-0.5, 0.5)) * scale + cell_position + vec2(-0.5, 0.5)) * " GRID_SIZE_UNIFORM_NAME ".zw * 2 + vec2(-1, 1);\n"
" gl_Position = vec4(scaled_offset.x + temp.x, scaled_offset.y - temp.y, 0, 1);\n" " gl_Position = vec4(scaled_offset.x + temp.x, scaled_offset.y - temp.y, 0, 1);\n"
"}"; "}";
@@ -66,15 +64,11 @@ namespace
"layout (location = 0) in vec2 position;\n" "layout (location = 0) in vec2 position;\n"
"layout (location = 1) in vec2 offset;\n" "layout (location = 1) in vec2 offset;\n"
"layout (location = 2) in int sprite;\n" "layout (location = 2) in int sprite;\n"
"layout (location = 3) in vec4 color;\n"
"layout (location = 4) in float scale;\n"
"uniform float " CELL_SIZE_UNIFORM_NAME ";\n"
"uniform vec4 " GRID_SIZE_UNIFORM_NAME ";\n" "uniform vec4 " GRID_SIZE_UNIFORM_NAME ";\n"
"layout (std430, binding = 0) buffer LayerScales" "uniform int " ATLAS_WIDTH_UNIFORM_NAME ";\n"
"{\n"
" float scales[];\n"
"} lss;\n"
"layout (std430, binding = 1) buffer LayerColors"
"{\n"
" vec4 colors[];\n"
"} lcs;\n"
"out VS_OUT {\n" "out VS_OUT {\n"
" flat int sprite;\n" " flat int sprite;\n"
" flat vec4 layer_color;\n" " flat vec4 layer_color;\n"
@@ -86,10 +80,10 @@ namespace
" gl_Layer = layer;\n" " gl_Layer = layer;\n"
" vec2 scaled_offset = 2 * offset * " GRID_SIZE_UNIFORM_NAME ".zw;\n" " vec2 scaled_offset = 2 * offset * " GRID_SIZE_UNIFORM_NAME ".zw;\n"
" vs_out.sprite = sprite;\n" " vs_out.sprite = sprite;\n"
" vs_out.texcoord = vec2(position.x + 1, -position.y);\n" " vs_out.texcoord = vec2(sprite % " ATLAS_WIDTH_UNIFORM_NAME " + position.x + 1, (sprite / " ATLAS_WIDTH_UNIFORM_NAME ") - position.y) / vec2(" ATLAS_WIDTH_UNIFORM_NAME ") + vec2(-(2 * position.x + 1) * " GRID_SIZE_UNIFORM_NAME ".z / (2 * " CELL_SIZE_UNIFORM_NAME "), (2 * position.y + 1) * " GRID_SIZE_UNIFORM_NAME ".z / (2 * " CELL_SIZE_UNIFORM_NAME "));\n"
" vs_out.layer_color = lcs.colors[layer];\n" " vs_out.layer_color = color;\n"
" vec2 cell_position = vec2(lss.scales[layer] + (gl_InstanceID % int(" GRID_SIZE_UNIFORM_NAME ".y)) - " GRID_SIZE_UNIFORM_NAME ".x * floor((gl_InstanceID % int(" GRID_SIZE_UNIFORM_NAME ".y)) * " GRID_SIZE_UNIFORM_NAME ".z), -floor((gl_InstanceID % int(" GRID_SIZE_UNIFORM_NAME ".y)) * " GRID_SIZE_UNIFORM_NAME ".z));\n" " vec2 cell_position = vec2(scale + (gl_InstanceID % int(" GRID_SIZE_UNIFORM_NAME ".y)) - " GRID_SIZE_UNIFORM_NAME ".x * floor((gl_InstanceID % int(" GRID_SIZE_UNIFORM_NAME ".y)) / " GRID_SIZE_UNIFORM_NAME ".x), -floor((gl_InstanceID % int(" GRID_SIZE_UNIFORM_NAME ".y)) / " GRID_SIZE_UNIFORM_NAME ".x));\n"
" vec2 temp = ((position + vec2(-0.5, 0.5)) * lss.scales[layer] + cell_position + vec2(0.5, -0.5)) * " GRID_SIZE_UNIFORM_NAME ".zw * 2 + vec2(-1, 1);\n" " vec2 temp = ((position + vec2(-0.5, 0.5)) * scale + cell_position + vec2(-0.5, 0.5)) * " GRID_SIZE_UNIFORM_NAME ".zw * 2 + vec2(-1, 1);\n"
" gl_Position = vec4(scaled_offset.x + temp.x, scaled_offset.y - temp.y, 0, 1);\n" " gl_Position = vec4(scaled_offset.x + temp.x, scaled_offset.y - temp.y, 0, 1);\n"
"}"; "}";
@@ -139,11 +133,11 @@ namespace
"flat in int sprite;\n" "flat in int sprite;\n"
"flat vec4 layer_color;\n" "flat vec4 layer_color;\n"
"in vec2 texcoord;\n" "in vec2 texcoord;\n"
"layout (binding = 0) uniform sampler2DArray " SPRITES_UNIFORM_NAME ";\n" "layout (binding = 2) uniform sampler2D " SPRITES_UNIFORM_NAME ";\n"
"out vec4 FragColor;\n" "out vec4 FragColor;\n"
"void main()\n" "void main()\n"
"{\n" "{\n"
" FragColor = layer_color * texture(" SPRITES_UNIFORM_NAME ", vec3(texcoord, sprite));\n" " FragColor = layer_color * texture(" SPRITES_UNIFORM_NAME ", texcoord);\n"
"}"; "}";
// note: AMD_vertex_shader_layer support required // note: AMD_vertex_shader_layer support required
@@ -154,11 +148,11 @@ namespace
" flat vec4 layer_color;\n" " flat vec4 layer_color;\n"
" vec2 texcoord;\n" " vec2 texcoord;\n"
"} fs_in;\n" "} fs_in;\n"
"layout (binding = 0) uniform sampler2DArray " SPRITES_UNIFORM_NAME ";\n" "layout (binding = 2) uniform sampler2D " SPRITES_UNIFORM_NAME ";\n"
"out vec4 FragColor;\n" "out vec4 FragColor;\n"
"void main()\n" "void main()\n"
"{\n" "{\n"
" FragColor = fs_in.layer_color * texture(" SPRITES_UNIFORM_NAME ", vec3(fs_in.texcoord, fs_in.sprite));\n" " FragColor = fs_in.layer_color * texture(" SPRITES_UNIFORM_NAME ", fs_in.texcoord);\n"
"}"; "}";
constexpr const char* FRAGMENT_SHADER_SOURCE_PTR = FRAGMENT_SHADER_SOURCE; constexpr const char* FRAGMENT_SHADER_SOURCE_PTR = FRAGMENT_SHADER_SOURCE;
@@ -198,15 +192,18 @@ namespace
namespace glerminal namespace glerminal
{ {
glerminal::glerminal(glerminal_init_cb init, glerminal_main_cb main, glerminal_keys_cb pressed, glerminal_keys_cb released) : glerminal::glerminal(glerminal_init_params params) :
m_main(main), m_main(params.main),
m_pressed(pressed), m_keypressed(params.keypress),
m_released(released), m_keyreleased(params.keyrelease),
m_mousemoved(params.moved),
m_mousepressed(params.mousepress),
m_mousereleased(params.mouserelease),
m_cells{ }, m_cells{ },
m_offsets{ }, m_offsets{ },
m_sprites{ }, m_sprites{ },
m_layer_colors{ }, m_colors{ },
m_layer_scales{ } m_scales{ }
#ifdef GLERMINAL_OPENGL_DEBUG_CONTEXT #ifdef GLERMINAL_OPENGL_DEBUG_CONTEXT
, m_log("log.txt") , m_log("log.txt")
#endif #endif
@@ -217,7 +214,7 @@ namespace glerminal
} }
// unsure if this should be an error // unsure if this should be an error
if (!init) if (!params.init)
{ {
throw std::runtime_error("No init callback provided."); throw std::runtime_error("No init callback provided.");
} }
@@ -227,22 +224,24 @@ namespace glerminal
throw std::runtime_error("No main callback provided."); throw std::runtime_error("No main callback provided.");
} }
for (int i = 0; i < LAYER_COUNT; i++) for (int i = 0; i < GRID_AREA_2 * LAYER_COUNT; i++)
{ {
m_layer_colors[i * 4 + 0] = m_layer_colors[i * 4 + 1] = m_layer_colors[i * 4 + 2] = m_layer_colors[i * 4 + 3] = 1; m_colors[i * 4 + 0] = m_colors[i * 4 + 1] = m_colors[i * 4 + 2] = m_colors[i * 4 + 3] = 255;
m_layer_scales[i] = 1; m_scales[i] = 1;
} }
init_glfw(); init_glfw();
init_gl(); init_gl();
init_audio();
GLERMINAL_G = this; GLERMINAL_G = this;
init(); params.init();
} }
glerminal::~glerminal() glerminal::~glerminal()
{ {
deinit_audio();
deinit_gl(); deinit_gl();
deinit_glfw(); deinit_glfw();
@@ -251,18 +250,16 @@ namespace glerminal
void glerminal::run() void glerminal::run()
{ {
float last = glfwGetTime(); double last = glfwGetTime();
while (!glfwWindowShouldClose(m_window)) while (!glfwWindowShouldClose(m_window))
{ {
glfwPollEvents(); glfwPollEvents();
const float current = glfwGetTime(); const double current = glfwGetTime();
m_main(current - last); m_main(current - last);
last = current; last = current;
glfwSwapBuffers(m_window);
} }
} }
@@ -273,17 +270,17 @@ namespace glerminal
void glerminal::flush() void glerminal::flush()
{ {
glNamedBufferData(m_sprites_instance_vbo, sizeof(m_cells), m_cells, GL_STREAM_DRAW); glNamedBufferSubData(m_sprites_instance_vbo, 0, sizeof(m_cells), m_cells);
glNamedBufferData(m_offsets_instance_vbo, sizeof(m_offsets), m_offsets, GL_STREAM_DRAW); glNamedBufferSubData(m_offsets_instance_vbo, 0, sizeof(m_offsets), m_offsets);
update_sprites(); update_sprites();
update_layer_colors(); update_colors();
update_layer_scales(); update_scales();
glUseProgram(m_program); glUseProgram(m_program);
glBindFramebuffer(GL_FRAMEBUFFER, m_framebuffer); glBindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);
glBindVertexArray(m_vao); glBindVertexArray(m_vao);
glClear(GL_COLOR_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT);
glDrawArraysInstanced(GL_TRIANGLES, 0, 6, GRID_AREA * LAYER_COUNT); glDrawArraysInstanced(GL_TRIANGLES, 0, 6, GRID_AREA_2 * LAYER_COUNT);
glUseProgram(m_screen_program); glUseProgram(m_screen_program);
glBindFramebuffer(GL_FRAMEBUFFER, m_screen_framebuffer); glBindFramebuffer(GL_FRAMEBUFFER, m_screen_framebuffer);
@@ -291,26 +288,25 @@ namespace glerminal
glClear(GL_COLOR_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT);
glDrawArrays(GL_TRIANGLES, 0, 6); glDrawArrays(GL_TRIANGLES, 0, 6);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glClear(GL_COLOR_BUFFER_BIT);
glBlitNamedFramebuffer(m_screen_framebuffer, 0, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, GL_COLOR_BUFFER_BIT, GL_NEAREST); glBlitNamedFramebuffer(m_screen_framebuffer, 0, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, GL_COLOR_BUFFER_BIT, GL_NEAREST);
glfwSwapBuffers(m_window); glfwSwapBuffers(m_window);
glBlitNamedFramebuffer(m_screen_framebuffer, 0, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, GL_COLOR_BUFFER_BIT, GL_NEAREST);
} }
void glerminal::set(unsigned char x, unsigned char y, unsigned char layer, unsigned char sprite) void glerminal::set(int x, int y, int layer, unsigned short sprite)
{ {
if (x < GRID_WIDTH && y < GRID_HEIGHT && layer < LAYER_COUNT) if (x >= 0 && x < GRID_WIDTH + 2 && y >= 0 && y < GRID_HEIGHT + 2 && layer >= 0 && layer < LAYER_COUNT)
{ {
m_cells[x + y * GRID_WIDTH + layer * GRID_AREA] = sprite; m_cells[x + y * (GRID_WIDTH + 2) + layer * GRID_AREA_2] = sprite;
} }
} }
unsigned char glerminal::get(unsigned char x, unsigned char y, unsigned char layer) const unsigned short glerminal::get(int x, int y, int layer) const
{ {
if (x < GRID_WIDTH && y < GRID_HEIGHT && layer < LAYER_COUNT) if (x >= 0 && x < GRID_WIDTH + 2 && y >= 0 && y < GRID_HEIGHT + 2 && layer >= 0 && layer < LAYER_COUNT)
{ {
return m_cells[x + y * GRID_WIDTH + layer * GRID_AREA]; return m_cells[x + y * (GRID_WIDTH + 2) + layer * GRID_AREA_2];
} }
else else
{ {
@@ -318,55 +314,97 @@ namespace glerminal
} }
} }
void glerminal::offset(unsigned char x, unsigned char y, unsigned char layer, float x_offset, float y_offset) void glerminal::offset(int x, int y, int layer, float x_offset, float y_offset)
{ {
if (x < GRID_WIDTH && y < GRID_HEIGHT && layer < LAYER_COUNT) if (x >= 0 && x < GRID_WIDTH + 2 && y >= 0 && y < GRID_HEIGHT + 2 && layer >= 0 && layer < LAYER_COUNT)
{ {
m_offsets[2 * (x + y * GRID_WIDTH + layer * GRID_AREA) + 0] = x_offset; m_offsets[2 * (x + y * (GRID_WIDTH + 2) + layer * GRID_AREA_2) + 0] = x_offset;
m_offsets[2 * (x + y * GRID_WIDTH + layer * GRID_AREA) + 1] = y_offset; m_offsets[2 * (x + y * (GRID_WIDTH + 2) + layer * GRID_AREA_2) + 1] = y_offset;
} }
} }
void glerminal::layer_color(unsigned char layer, unsigned int color) void glerminal::color(int x, int y, int layer, unsigned int color)
{ {
m_layer_colors[layer * 4 + 0] = ((color >> 0) & 0xFF) / 255.0f; if (x >= 0 && x < GRID_WIDTH + 2 && y >= 0 && y < GRID_HEIGHT + 2 && layer >= 0 && layer < LAYER_COUNT)
m_layer_colors[layer * 4 + 1] = ((color >> 8) & 0xFF) / 255.0f; {
m_layer_colors[layer * 4 + 2] = ((color >> 16) & 0xFF) / 255.0f; m_colors[4 * (x + y * (GRID_WIDTH + 2) + layer * GRID_AREA_2) + 0] = (color >> 0) & 0xFF;
m_layer_colors[layer * 4 + 3] = ((color >> 24) & 0xFF) / 255.0f; m_colors[4 * (x + y * (GRID_WIDTH + 2) + layer * GRID_AREA_2) + 1] = (color >> 8) & 0xFF;
m_colors[4 * (x + y * (GRID_WIDTH + 2) + layer * GRID_AREA_2) + 2] = (color >> 16) & 0xFF;
m_colors[4 * (x + y * (GRID_WIDTH + 2) + layer * GRID_AREA_2) + 3] = (color >> 24) & 0xFF;
}
} }
void glerminal::layer_scale(unsigned char layer, float scale) void glerminal::scale(int x, int y, int layer, float scale)
{ {
m_layer_scales[layer] = scale; if (x >= 0 && x < GRID_WIDTH + 2 && y >= 0 && y < GRID_HEIGHT + 2 && layer >= 0 && layer < LAYER_COUNT)
{
m_scales[x + y * (GRID_WIDTH + 2) + layer * GRID_AREA_2] = scale;
}
} }
void glerminal::load_atlas(unsigned char w, unsigned char h, const unsigned int* data) void glerminal::load_atlas(unsigned char w, unsigned char h, const unsigned int* data)
{ {
// each row of the atlas for (int src_atlas_row = 0; src_atlas_row < h; src_atlas_row++)
for (int j = 0; j < h; j++)
{ {
// each column of the atlas for (int src_atlas_col = 0; src_atlas_col < w; src_atlas_col++)
for (int i = 0; i < w; i++)
{ {
// each row of the individual sprite for (int sprite_row = 0; sprite_row < CELL_SIZE; sprite_row++)
for (int k = 0; k < CELL_SIZE; k++)
{ {
// offset from base address in atlas layout const unsigned int sprite_index = src_atlas_col + src_atlas_row * w;
const unsigned int src_offset = i + k * w + j * w * CELL_SIZE; const unsigned int dst_atlas_row = sprite_index / MAX_SPRITES_ROW;
// offset from base address in array layout const unsigned int dst_atlas_col = sprite_index % MAX_SPRITES_ROW;
const unsigned int dst_offset = k + i * CELL_SIZE + j * w * CELL_SIZE;
memcpy(m_sprites + CELL_SIZE * dst_offset, data + CELL_SIZE * src_offset, CELL_SIZE * sizeof(unsigned int)); // offset from base address in source atlas layout
const unsigned int src_offset = CELL_SIZE * (src_atlas_col + sprite_row * w + src_atlas_row * w * CELL_SIZE);
const unsigned int dst_column = dst_atlas_col * (CELL_SIZE + 2) + 1;
const unsigned int dst_row = MAX_SPRITES_ROW * (CELL_SIZE + 2) * ((sprite_row + 1) + dst_atlas_row * (CELL_SIZE + 2));
// offset from base address in glerminal atlas layout
const unsigned int dst_offset = dst_column + dst_row;
memcpy(m_sprites + dst_offset, data + src_offset, CELL_SIZE * sizeof(unsigned int));
} }
} }
} }
} }
bool glerminal::load_sound(const char* name)
{
if (m_sounds.find(name) == m_sounds.end())
{
ma_sound& ref = m_sounds[name];
const ma_result result = ma_sound_init_from_file(
&m_audio_engine,
name,
MA_SOUND_FLAG_DECODE | MA_SOUND_FLAG_NO_SPATIALIZATION,
nullptr,
nullptr,
&ref
);
if (result != MA_SUCCESS)
{
return false;
}
}
return true;
}
void glerminal::play_sound(const char* name)
{
load_sound(name);
const ma_result result = ma_engine_play_sound(&m_audio_engine, name, nullptr);
if (result != MA_SUCCESS)
{
}
}
void glerminal::init_glfw() void glerminal::init_glfw()
{ {
glfwInit(); glfwInit();
glfwWindowHint(GLFW_DOUBLEBUFFER, GLFW_TRUE);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
@@ -388,7 +426,9 @@ namespace glerminal
} }
glfwSetWindowUserPointer(m_window, this); glfwSetWindowUserPointer(m_window, this);
glfwSetKeyCallback(m_window, glerminal::glfw_key_handler); glfwSetKeyCallback(m_window, glfw_key_handler);
glfwSetCursorPosCallback(m_window, glfw_mousemoved_handler);
glfwSetMouseButtonCallback(m_window, glfw_mousepress_handler);
} }
void glerminal::log(GLenum type, GLuint id, GLenum severity, const char* message) const void glerminal::log(GLenum type, GLuint id, GLenum severity, const char* message) const
@@ -401,7 +441,6 @@ namespace glerminal
void glerminal::init_gl() void glerminal::init_gl()
{ {
glfwMakeContextCurrent(m_window); glfwMakeContextCurrent(m_window);
glfwSwapInterval(1);
if (!gladLoadGLLoader(reinterpret_cast<GLADloadproc>(glfwGetProcAddress))) if (!gladLoadGLLoader(reinterpret_cast<GLADloadproc>(glfwGetProcAddress)))
{ {
@@ -476,8 +515,8 @@ namespace glerminal
glGenBuffers(1, &m_vbo); glGenBuffers(1, &m_vbo);
glGenBuffers(1, &m_sprites_instance_vbo); glGenBuffers(1, &m_sprites_instance_vbo);
glGenBuffers(1, &m_offsets_instance_vbo); glGenBuffers(1, &m_offsets_instance_vbo);
glGenBuffers(1, &m_layer_colors_buffer); glGenBuffers(1, &m_colors_instance_vbo);
glGenBuffers(1, &m_layer_scales_buffer); glGenBuffers(1, &m_scales_instance_vbo);
// create vertex array object // create vertex array object
glGenVertexArrays(1, &m_vao); glGenVertexArrays(1, &m_vao);
@@ -485,23 +524,35 @@ namespace glerminal
// set up static vertex attributes // set up static vertex attributes
glBindBuffer(GL_ARRAY_BUFFER, m_vbo); glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(VBO_VERTICES), VBO_VERTICES, GL_STATIC_DRAW); glBufferStorage(GL_ARRAY_BUFFER, sizeof(VBO_VERTICES), VBO_VERTICES, GL_NONE);
glEnableVertexAttribArray(0); glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(*VBO_VERTICES), reinterpret_cast<void*>(0)); glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(*VBO_VERTICES), reinterpret_cast<void*>(0));
// set up instanced vertex attributes // set up instanced vertex attributes
glBindBuffer(GL_ARRAY_BUFFER, m_offsets_instance_vbo); glBindBuffer(GL_ARRAY_BUFFER, m_offsets_instance_vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(m_offsets), m_offsets, GL_STREAM_DRAW); glBufferStorage(GL_ARRAY_BUFFER, sizeof(m_offsets), m_offsets, GL_DYNAMIC_STORAGE_BIT);
glEnableVertexAttribArray(1); glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(*m_offsets), reinterpret_cast<void*>(0)); glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(*m_offsets), reinterpret_cast<void*>(0));
glVertexAttribDivisor(1, 1); glVertexAttribDivisor(1, 1);
glBindBuffer(GL_ARRAY_BUFFER, m_sprites_instance_vbo); glBindBuffer(GL_ARRAY_BUFFER, m_sprites_instance_vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(m_cells), m_cells, GL_STREAM_DRAW); glBufferStorage(GL_ARRAY_BUFFER, sizeof(m_cells), m_cells, GL_DYNAMIC_STORAGE_BIT);
glEnableVertexAttribArray(2); glEnableVertexAttribArray(2);
glVertexAttribIPointer(2, 1, GL_UNSIGNED_BYTE, 1 * sizeof(*m_cells), reinterpret_cast<void*>(0)); glVertexAttribIPointer(2, 1, GL_UNSIGNED_SHORT, 1 * sizeof(*m_cells), reinterpret_cast<void*>(0));
glVertexAttribDivisor(2, 1); glVertexAttribDivisor(2, 1);
glBindBuffer(GL_ARRAY_BUFFER, m_colors_instance_vbo);
glBufferStorage(GL_ARRAY_BUFFER, sizeof(m_colors), m_colors, GL_DYNAMIC_STORAGE_BIT);
glEnableVertexAttribArray(3);
glVertexAttribPointer(3, 4, GL_UNSIGNED_BYTE, GL_TRUE, 4 * sizeof(*m_colors), reinterpret_cast<void*>(0));
glVertexAttribDivisor(3, 1);
glBindBuffer(GL_ARRAY_BUFFER, m_scales_instance_vbo);
glBufferStorage(GL_ARRAY_BUFFER, sizeof(m_scales), m_scales, GL_DYNAMIC_STORAGE_BIT);
glEnableVertexAttribArray(4);
glVertexAttribPointer(4, 1, GL_FLOAT, GL_FALSE, 1 * sizeof(*m_scales), reinterpret_cast<void*>(0));
glVertexAttribDivisor(4, 1);
// set up static vertex attributes // set up static vertex attributes
glGenVertexArrays(1, &m_screen_vao); glGenVertexArrays(1, &m_screen_vao);
glBindVertexArray(m_screen_vao); glBindVertexArray(m_screen_vao);
@@ -512,14 +563,6 @@ namespace glerminal
glBindVertexArray(0); glBindVertexArray(0);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_layer_scales_buffer);
glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(m_layer_scales), m_layer_scales, GL_DYNAMIC_READ);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, m_layer_scales_buffer);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_layer_colors_buffer);
glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(m_layer_colors), m_layer_colors, GL_DYNAMIC_READ);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, m_layer_colors_buffer);
// -- setup shader program -- // -- setup shader program --
// test for features // test for features
const bool vertex_shader_layer_supported = const bool vertex_shader_layer_supported =
@@ -613,10 +656,10 @@ namespace glerminal
} }
// setup uniforms // setup uniforms
m_screen_size_uniform_location = glGetUniformLocation(m_program, GRID_SIZE_UNIFORM_NAME);
glUseProgram(m_program); glUseProgram(m_program);
glUniform4f(m_screen_size_uniform_location, GRID_WIDTH, GRID_AREA, 1.0f / GRID_WIDTH, 1.0f / GRID_HEIGHT); glUniform1f(glGetUniformLocation(m_program, CELL_SIZE_UNIFORM_NAME), CELL_SIZE);
glUniform4f(glGetUniformLocation(m_program, GRID_SIZE_UNIFORM_NAME), GRID_WIDTH + 2, GRID_AREA_2, 1.0f / GRID_WIDTH, 1.0f / GRID_HEIGHT);
glUniform1i(glGetUniformLocation(m_program, ATLAS_WIDTH_UNIFORM_NAME), MAX_SPRITES_ROW);
// compile // compile
const unsigned int screen_vertex_shader = glCreateShader(GL_VERTEX_SHADER); const unsigned int screen_vertex_shader = glCreateShader(GL_VERTEX_SHADER);
@@ -680,20 +723,22 @@ namespace glerminal
// -- setup textures -- // -- setup textures --
glGenTextures(1, &m_sprites_texture); glGenTextures(1, &m_sprites_texture);
glBindTexture(GL_TEXTURE_2D_ARRAY, m_sprites_texture); glBindTexture(GL_TEXTURE_2D, m_sprites_texture);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_BASE_LEVEL, 0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_LEVEL, 0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
glTextureStorage2D(m_sprites_texture, 1, GL_RGBA8, MAX_SPRITES_ROW * (CELL_SIZE + 2), MAX_SPRITES_ROW * (CELL_SIZE + 2));
update_sprites(); update_sprites();
glBindTextureUnit(0, m_sprites_texture); glBindTextureUnit(2, m_sprites_texture);
// -- setup framebuffer -- // -- setup framebuffer --
glGenFramebuffers(1, &m_framebuffer); glGenFramebuffers(1, &m_framebuffer);
@@ -736,6 +781,18 @@ namespace glerminal
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_screen_framebuffer_backing_texture, 0); glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_screen_framebuffer_backing_texture, 0);
} }
void glerminal::init_audio()
{
const ma_result result = ma_engine_init(nullptr, &m_audio_engine);
if (result != MA_SUCCESS)
{
throw std::runtime_error("Failed to initialize audio engine");
}
ma_engine_set_volume(&m_audio_engine, 1);
}
void glerminal::deinit_glfw() void glerminal::deinit_glfw()
{ {
glfwDestroyWindow(m_window); glfwDestroyWindow(m_window);
@@ -755,48 +812,82 @@ namespace glerminal
glDeleteBuffers(1, &m_vbo); glDeleteBuffers(1, &m_vbo);
glDeleteBuffers(1, &m_sprites_instance_vbo); glDeleteBuffers(1, &m_sprites_instance_vbo);
glDeleteBuffers(1, &m_offsets_instance_vbo); glDeleteBuffers(1, &m_offsets_instance_vbo);
glDeleteBuffers(1, &m_layer_colors_buffer); glDeleteBuffers(1, &m_colors_instance_vbo);
glDeleteBuffers(1, &m_layer_scales_buffer); glDeleteBuffers(1, &m_scales_instance_vbo);
glDeleteProgram(m_program); glDeleteProgram(m_program);
} }
void glerminal::deinit_audio()
{
for (auto& elem : m_sounds)
{
ma_sound_uninit(&elem.second);
}
ma_engine_uninit(&m_audio_engine);
}
void glerminal::update_sprites() void glerminal::update_sprites()
{ {
glBindTexture(GL_TEXTURE_2D_ARRAY, m_sprites_texture); glTextureSubImage2D(m_sprites_texture, 0, 0, 0, (CELL_SIZE + 2) * MAX_SPRITES_ROW, (CELL_SIZE + 2) * MAX_SPRITES_ROW, GL_RGBA, GL_UNSIGNED_BYTE, m_sprites);
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, CELL_SIZE, CELL_SIZE, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, m_sprites);
} }
void glerminal::update_layer_colors() void glerminal::update_colors()
{ {
glNamedBufferData(m_layer_colors_buffer, sizeof(m_layer_colors), m_layer_colors, GL_DYNAMIC_READ); glNamedBufferSubData(m_colors_instance_vbo, 0, sizeof(m_colors), m_colors);
} }
void glerminal::update_layer_scales() void glerminal::update_scales()
{ {
glNamedBufferData(m_layer_scales_buffer, sizeof(m_layer_scales), m_layer_scales, GL_DYNAMIC_READ); glNamedBufferSubData(m_scales_instance_vbo, 0, sizeof(m_scales), m_scales);
} }
void glerminal::glfw_key_handler(GLFWwindow* window, int key, int scancode, int action, int mods) void glerminal::glfw_key_handler(GLFWwindow* window, int key, int scancode, int action, int mods)
{ {
glerminal* const self = static_cast<glerminal*>(glfwGetWindowUserPointer(window)); const glerminal* const self = static_cast<glerminal*>(glfwGetWindowUserPointer(window));
if (self->m_pressed && action == GLFW_PRESS) if (self->m_keypressed && action == GLFW_PRESS)
{ {
self->m_pressed(key); self->m_keypressed(key);
} }
if (self->m_released && action == GLFW_RELEASE) if (self->m_keyreleased && action == GLFW_RELEASE)
{ {
self->m_released(key); self->m_keyreleased(key);
} }
} }
void glerminal::glfw_mousemoved_handler(GLFWwindow *window, double x, double y)
{
const glerminal* const self = static_cast<glerminal*>(glfwGetWindowUserPointer(window));
if (self->m_mousemoved) { self->m_mousemoved(x / (CELL_SIZE * CELL_SCALE), y / (CELL_SIZE * CELL_SCALE)); }
}
void glerminal::glfw_mousepress_handler(GLFWwindow *window, int button, int action, int mods)
{
const glerminal* const self = static_cast<glerminal*>(glfwGetWindowUserPointer(window));
double x, y;
glfwGetCursorPos(window, &x, &y);
if (self->m_mousepressed && action == GLFW_PRESS)
{
self->m_mousepressed(button, x / (CELL_SIZE * CELL_SCALE), y / (CELL_SIZE * CELL_SCALE));
}
if (self->m_mousereleased && action == GLFW_RELEASE)
{
self->m_mousereleased(button, x / (CELL_SIZE * CELL_SCALE), y / (CELL_SIZE * CELL_SCALE));
}
}
} }
void glerminal_run(glerminal_init_cb init, glerminal_main_cb main, glerminal_keys_cb pressed, glerminal_keys_cb released) void glerminal_run(glerminal_init_params params)
{ {
try try
{ {
glerminal::glerminal* g = new glerminal::glerminal(init, main, pressed, released); glerminal::glerminal* g = new glerminal::glerminal(params);
g->run(); g->run();
delete g; delete g;
} }
@@ -820,39 +911,39 @@ void glerminal_flush()
GLERMINAL_G->flush(); GLERMINAL_G->flush();
} }
void glerminal_set(unsigned char x, unsigned char y, unsigned char layer, unsigned char sprite) void glerminal_set(int x, int y, int layer, unsigned short sprite)
{ {
if (!GLERMINAL_G) { return; } if (!GLERMINAL_G) { return; }
GLERMINAL_G->set(x, y, layer, sprite); GLERMINAL_G->set(x + 1, y + 1, layer, sprite);
} }
unsigned char glerminal_get(unsigned char x, unsigned char y, unsigned char layer) unsigned short glerminal_get(int x, int y, int layer)
{ {
if (!GLERMINAL_G) { return 0; } if (!GLERMINAL_G) { return 0; }
return GLERMINAL_G->get(x, y, layer); return GLERMINAL_G->get(x + 1, y + 1, layer);
} }
void glerminal_offset(unsigned char x, unsigned char y, unsigned char layer, float x_offset, float y_offset) void glerminal_offset(int x, int y, int layer, float x_offset, float y_offset)
{ {
if (!GLERMINAL_G) { return; } if (!GLERMINAL_G) { return; }
GLERMINAL_G->offset(x, y, layer, x_offset, y_offset); GLERMINAL_G->offset(x + 1, y + 1, layer, x_offset, y_offset);
} }
void glerminal_layer_color(unsigned char layer, unsigned int color) void glerminal_color(int x, int y, int layer, unsigned int color)
{ {
if (!GLERMINAL_G) { return; } if (!GLERMINAL_G) { return; }
GLERMINAL_G->layer_color(layer, color); GLERMINAL_G->color(x + 1, y + 1, layer, color);
} }
void glerminal_layer_scale(unsigned char layer, float scale) void glerminal_scale(int x, int y, int layer, float scale)
{ {
if (!GLERMINAL_G) { return; } if (!GLERMINAL_G) { return; }
GLERMINAL_G->layer_scale(layer, scale); GLERMINAL_G->scale(x + 1, y + 1, layer, scale);
} }
int glerminal_load_sprites_file(const char* filename) int glerminal_load_sprites_file(const char* filename)
@@ -865,7 +956,7 @@ int glerminal_load_sprites_file(const char* filename)
stbi_uc* const buffer = stbi_load(filename, &w, &h, nullptr, 4); stbi_uc* const buffer = stbi_load(filename, &w, &h, nullptr, 4);
// verify atlas size is a multiple of CELL_SIZE in each dimension // verify atlas size is a multiple of CELL_SIZE in each dimension
if (w % glerminal::CELL_SIZE == 0 && h % glerminal::CELL_SIZE == 0) if (buffer && w % glerminal::CELL_SIZE == 0 && h % glerminal::CELL_SIZE == 0)
{ {
GLERMINAL_G->load_atlas(w / glerminal::CELL_SIZE, h / glerminal::CELL_SIZE, reinterpret_cast<unsigned int*>(buffer)); GLERMINAL_G->load_atlas(w / glerminal::CELL_SIZE, h / glerminal::CELL_SIZE, reinterpret_cast<unsigned int*>(buffer));
@@ -892,4 +983,11 @@ int glerminal_load_sprites_buffer(unsigned char width, unsigned char height, con
{ {
return false; return false;
} }
} }
void glerminal_sound(const char* name)
{
if (!GLERMINAL_G) { return; }
GLERMINAL_G->play_sound(name);
}

7
source/miniaudio.c Normal file
View File

@@ -0,0 +1,7 @@
#ifndef GLERMINAL_MINIAUDIO_H
#define GLERMINAL_MINIAUDIO_H
#define MINIAUDIO_IMPLEMENTATION
#include "miniaudio.h"
#endif//GLERMINAL_MINIAUDIO_H

92621
source/miniaudio.h Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -17,7 +17,7 @@ target_include_directories(test-common
${CMAKE_SOURCE_DIR}/source ${CMAKE_SOURCE_DIR}/source
) )
target_link_libraries(test-common PRIVATE glerminallib) target_link_libraries(test-common PRIVATE glerminal)
file(GLOB_RECURSE file(GLOB_RECURSE
TEST_RESOURCES TEST_RESOURCES
@@ -52,5 +52,5 @@ list(TRANSFORM TEST_RESOURCES PREPEND ${CMAKE_CURRENT_BINARY_DIR}/)
foreach(SOURCE_FILE ${TEST_SOURCES}) foreach(SOURCE_FILE ${TEST_SOURCES})
get_filename_component(SOURCE_FILENAME ${SOURCE_FILE} NAME_WLE) get_filename_component(SOURCE_FILENAME ${SOURCE_FILE} NAME_WLE)
add_executable(test-${SOURCE_FILENAME} WIN32 ${SOURCE_FILE} ${TEST_RESOURCES}) add_executable(test-${SOURCE_FILENAME} WIN32 ${SOURCE_FILE} ${TEST_RESOURCES})
target_link_libraries(test-${SOURCE_FILENAME} PRIVATE glerminallib test-common) target_link_libraries(test-${SOURCE_FILENAME} PRIVATE glerminal test-common)
endforeach() endforeach()

View File

@@ -25,12 +25,12 @@ namespace
glerminal_quit(); glerminal_quit();
} }
void mainloop(float) {} void mainloop(double) {}
} }
int main() int main()
{ {
glerminal_run(init, mainloop, nullptr, nullptr); glerminal_run({init, mainloop});
return 0; return 0;
} }

View File

@@ -8,18 +8,18 @@
namespace namespace
{ {
unsigned char pixels[(GRID_WIDTH * CELL_SCALE * 8) * (GRID_HEIGHT * CELL_SCALE * 8) * 3]; unsigned char pixels[(GRID_WIDTH * CELL_SIZE * CELL_SCALE) * (GRID_HEIGHT * CELL_SIZE * CELL_SCALE) * 3];
} }
void glerminal_test_save_image() void glerminal_test_save_image()
{ {
// -- DIRTY HACK -- // -- DIRTY HACK --
// //
// GL_TEXTURE_2D is not rebound after setting up screen framebuffer // GL_TEXTURE_2D is not rebound after setting up screen framebuffer
// This code will break if this behavior changes // This code will break if this behavior changes
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGB, GL_UNSIGNED_BYTE, pixels); glGetTexImage(GL_TEXTURE_2D, 0, GL_RGB, GL_UNSIGNED_BYTE, pixels);
stbi_flip_vertically_on_write(true); stbi_flip_vertically_on_write(true);
stbi_write_png("image.png", GRID_WIDTH * CELL_SCALE * 8, GRID_HEIGHT * CELL_SCALE * 8, 3, pixels, GRID_WIDTH * CELL_SCALE * 8 * 3); stbi_write_png("image.png", GRID_WIDTH * CELL_SIZE * CELL_SCALE, GRID_HEIGHT * CELL_SIZE * CELL_SCALE, 3, pixels, GRID_WIDTH * CELL_SIZE * CELL_SCALE * 3);
} }