Compare commits

..

15 Commits

19 changed files with 140 additions and 529 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

3
.gitignore vendored
View File

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

5
.gitmodules vendored
View File

@@ -1,6 +1,3 @@
[submodule "glfw"] [submodule "glfw"]
path = glfw path = glfw
url = https://git.shylie.info/mirror/glfw.git url = https://github.com/glfw/glfw.git
[submodule "Lua"]
path = Lua
url = https://git.shylie.info/mirror/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,75 +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.h
source/miniaudio.c 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

@@ -42,7 +42,7 @@ void glerminal_flush();
* @param layer layer of the cell in the range [0, LAYER_COUNT) * @param layer layer of the cell in the range [0, LAYER_COUNT)
* @param sprite sprite's index in the range [0, 4096) * @param sprite sprite's index in the range [0, 4096)
*/ */
void glerminal_set(unsigned char x, unsigned char y, unsigned char layer, unsigned short 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, GRID_WIDTH) * @param x position of the cell in the range [0, GRID_WIDTH)
@@ -50,7 +50,7 @@ void glerminal_set(unsigned char x, unsigned char y, unsigned char layer, unsign
* @param layer layer of the cell in the range [0, LAYER_COUNT) * @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 short glerminal_get(unsigned char x, unsigned char y, unsigned short 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, GRID_WIDTH) * @param x position of the cell in the range [0, GRID_WIDTH)
@@ -59,7 +59,7 @@ unsigned short glerminal_get(unsigned char x, unsigned char y, unsigned short la
* @param x_offset offset of the cell on the x axis in cells * @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 cells * @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 cell's color * @brief Set a cell's color
@@ -68,7 +68,7 @@ void glerminal_offset(unsigned char x, unsigned char y, unsigned char layer, flo
* @param layer layer of the cell in the range [0, LAYER_COUNT) * @param layer layer of the cell in the range [0, LAYER_COUNT)
* @param color The new color * @param color The new color
*/ */
void glerminal_color(unsigned char x, unsigned char y, unsigned char layer, unsigned int color); void glerminal_color(int x, int y, int layer, unsigned int color);
/** /**
* @brief Set a cell's scale * @brief Set a cell's scale
* @param x position of the cell in the range [0, GRID_WIDTH) * @param x position of the cell in the range [0, GRID_WIDTH)
@@ -76,7 +76,7 @@ void glerminal_color(unsigned char x, unsigned char y, unsigned char layer, unsi
* @param layer layer of the cell in the range [0, LAYER_COUNT) * @param layer layer of the cell in the range [0, LAYER_COUNT)
* @param scale The new scale * @param scale The new scale
*/ */
void glerminal_scale(unsigned char x, unsigned char y, 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

View File

@@ -1,35 +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
function glerminal.keypresse(key) end
function glerminal.keyrelease(key) end
function glerminal.mousemove(x, y) end
function glerminal.mousepress(button, x, y) end
function glerminal.mouserelease(button, x, y) 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,372 +0,0 @@
#include <glerminal.h>
#define GLFW_INCLUDE_NONE
#include <GLFW/glfw3.h>
extern "C"
{
#include <lualib.h>
#include <lauxlib.h>
}
#include <iostream>
#include <string>
#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);
const unsigned char y = luaL_checkinteger(L, 2);
const unsigned char layer = luaL_checkinteger(L, 3) - 1;
const unsigned short 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);
const unsigned char y = luaL_checkinteger(L, 2);
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);
const unsigned char y = luaL_checkinteger(L, 2);
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 x = luaL_checkinteger(L, 1);
const unsigned char y = luaL_checkinteger(L, 2);
const unsigned char layer = luaL_checkinteger(L, 3) - 1;
const float r = luaL_checknumber(L, 4);
const float g = luaL_checknumber(L, 5);
const float b = luaL_checknumber(L, 6);
const float a = luaL_optnumber(L, 7, 1.0f);
int ri = 255 * r;
int gi = 255 * g;
int bi = 255 * b;
int ai = 255 * a;
if (ri > 255) { ri = 255; }
if (ri < 0) { ri = 0; }
if (gi > 255) { ri = 255; }
if (gi < 0) { ri = 0; }
if (bi > 255) { ri = 255; }
if (bi < 0) { ri = 0; }
if (ai > 255) { ri = 255; }
if (ai < 0) { ri = 0; }
const unsigned int color = ri | (gi << 8) | (bi << 16) | (ai << 24);
glerminal_color(x, y, layer, color);
return 0;
}
int lglerminal_layer_scale(lua_State* L)
{
const unsigned char x = luaL_checkinteger(L, 1);
const unsigned char y = luaL_checkinteger(L, 2);
const unsigned char layer = luaL_checkinteger(L, 3) - 1;
const float scale = luaL_checknumber(L, 4);
glerminal_scale(x, y, 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;
}
int lglerminal_play_sound(lua_State* L)
{
const char* name = luaL_checkstring(L, 1);
glerminal_sound(name);
return 0;
}
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 },
{ "sound", lglerminal_play_sound },
{ nullptr, nullptr }
};
int message_handler(lua_State* L)
{
luaL_traceback(L, L, lua_tostring(L, -1), 1);
lua_remove(L, -2);
glerminal_quit();
return true;
}
lua_State* L;
void init()
{
lua_pushcfunction(L, message_handler);
const int handler = lua_gettop(L);
lua_getglobal(L, "glerminal");
lua_getfield(L, -1, "init");
lua_remove(L, -2);
if (lua_pcall(L, 0, 0, handler) != LUA_OK)
{
std::cout << lua_tostring(L, -1) << std::endl;
lua_pop(L, 1);
}
lua_remove(L, handler);
}
void mainloop(double dt)
{
lua_pushcfunction(L, message_handler);
const int handler = lua_gettop(L);
lua_getglobal(L, "glerminal");
lua_getfield(L, -1, "main");
lua_remove(L, -2);
lua_pushnumber(L, dt);
if (lua_pcall(L, 1, 0, handler) != LUA_OK)
{
std::cout << lua_tostring(L, -1) << std::endl;
lua_pop(L, 1);
}
lua_remove(L, handler);
}
void keypressed(int key)
{
const char* const name = glfwGetKeyName(key, 0);
if (name)
{
lua_pushcfunction(L, message_handler);
const int handler = lua_gettop(L);
lua_getglobal(L, "glerminal");
lua_getfield(L, -1, "keypress");
lua_remove(L, -2);
lua_pushstring(L, name);
if (lua_pcall(L, 1, 0, handler) != LUA_OK)
{
std::cout << lua_tostring(L, -1) << std::endl;
lua_pop(L, 1);
}
lua_remove(L, handler);
}
}
void keyrelease(int key)
{
const char* const name = glfwGetKeyName(key, 0);
if (name)
{
lua_pushcfunction(L, message_handler);
const int handler = lua_gettop(L);
lua_getglobal(L, "glerminal");
lua_getfield(L, -1, "keyrelease");
lua_remove(L, -2);
lua_pushstring(L, name);
if (lua_pcall(L, 1, 0, handler) != LUA_OK)
{
std::cout << lua_tostring(L, -1) << std::endl;
lua_pop(L, 1);
}
lua_remove(L, handler);
}
}
void mousemove(double x, double y)
{
lua_pushcfunction(L, message_handler);
const int handler = lua_gettop(L);
lua_getglobal(L, "glerminal");
lua_getfield(L, -1, "mousemove");
lua_remove(L, -2);
lua_pushnumber(L, x);
lua_pushnumber(L, y);
if (lua_pcall(L, 2, 0, handler) != LUA_OK)
{
std::cout << lua_tostring(L, -1) << std::endl;
lua_pop(L, 1);
}
lua_remove(L, handler);
}
void mousepress(int button, double x, double y)
{
const char* button_name = nullptr;
switch (button)
{
case GLFW_MOUSE_BUTTON_LEFT:
button_name = "left";
break;
case GLFW_MOUSE_BUTTON_RIGHT:
button_name = "right";
break;
case GLFW_MOUSE_BUTTON_MIDDLE:
button_name = "middle";
break;
}
if (button_name)
{
lua_pushcfunction(L, message_handler);
const int handler = lua_gettop(L);
lua_getglobal(L, "glerminal");
lua_getfield(L, -1, "mousepress");
lua_remove(L, -2);
lua_pushstring(L, button_name);
lua_pushnumber(L, x);
lua_pushnumber(L, y);
if (lua_pcall(L, 3, 0, handler) != LUA_OK)
{
std::cout << lua_tostring(L, -1) << std::endl;
lua_pop(L, 1);
}
lua_remove(L, handler);
}
}
void mouserelease(int button, double x, double y)
{
const char* button_name = nullptr;
switch (button)
{
case GLFW_MOUSE_BUTTON_LEFT:
button_name = "left";
break;
case GLFW_MOUSE_BUTTON_RIGHT:
button_name = "right";
break;
case GLFW_MOUSE_BUTTON_MIDDLE:
button_name = "middle";
break;
}
if (button_name)
{
lua_pushcfunction(L, message_handler);
const int handler = lua_gettop(L);
lua_getglobal(L, "glerminal");
lua_getfield(L, -1, "mouserelease");
lua_remove(L, -2);
lua_pushstring(L, button_name);
lua_pushnumber(L, x);
lua_pushnumber(L, y);
if (lua_pcall(L, 3, 0, handler) != LUA_OK)
{
std::cout << lua_tostring(L, -1) << std::endl;
lua_pop(L, 1);
}
lua_remove(L, handler);
}
}
}
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");
lua_pushcfunction(L, message_handler);
const int handler = lua_gettop(L);
if (luaL_loadfile(L, "main.lua") != LUA_OK)
{
const char* str = lua_tostring(L, -1);
std::cout << str << std::endl;
lua_pop(L, 1);
}
else if (lua_pcall(L, 0, LUA_MULTRET, handler) != LUA_OK)
{
const char* str = lua_tostring(L, -1);
std::cout << str << std::endl;
lua_pop(L, 1);
}
else
{
glerminal_init_params params;
params.init = init;
params.main = mainloop;
params.keypress = keypressed;
params.keyrelease = keyrelease;
params.moved = mousemove;
params.mousepress = mousepress;
params.mouserelease = mouserelease;
glerminal_run(params);
}
lua_close(L);
return 0;
}

View File

@@ -9,20 +9,18 @@
#include <GLFW/glfw3.h> #include <GLFW/glfw3.h>
#include <iostream> #include <iostream>
#include <fstream>
#include <stdexcept>
#include <map> #include <map>
#include <string> #include <string>
namespace glerminal namespace glerminal
{ {
constexpr unsigned int CELL_SIZE = 8;
constexpr unsigned int MAX_SPRITES_ROW = 64; constexpr unsigned int MAX_SPRITES_ROW = 64;
constexpr unsigned int MAX_SPRITES = MAX_SPRITES_ROW * MAX_SPRITES_ROW; 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;
@@ -47,11 +45,11 @@ namespace glerminal
void flush(); void flush();
void set(unsigned char x, unsigned char y, unsigned char layer, unsigned short sprite); void set(int x, int y, int layer, unsigned short sprite);
unsigned short 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 color(unsigned char x, unsigned char y, unsigned char layer, unsigned int color); void color(int x, int y, int layer, unsigned int color);
void scale(unsigned char x, unsigned char y, 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); bool load_sound(const char* name);
void play_sound(const char* name); void play_sound(const char* name);
@@ -121,4 +119,4 @@ namespace glerminal
}; };
} }
#endif//GLERMINAL_PRIVATE_H #endif//GLERMINAL_PRIVATE_H

View File

@@ -1,6 +1,6 @@
#define STB_IMAGE_IMPLEMENTATION #define STB_IMAGE_IMPLEMENTATION
#define STBI_ONLY_PNG #define STBI_ONLY_PNG
#define STBI_MAX_DIMENSIONS 512 #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"
@@ -8,6 +8,7 @@
#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 ATLAS_WIDTH_UNIFORM_NAME "atlas_width"
#define CELL_SIZE_UNIFORM_NAME "cell_size"
namespace namespace
{ {
@@ -33,6 +34,7 @@ namespace
"layout (location = 2) in int sprite;\n" "layout (location = 2) in int sprite;\n"
"layout (location = 3) in vec4 color;\n" "layout (location = 3) in vec4 color;\n"
"layout (location = 4) in float scale;\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"
"uniform int " ATLAS_WIDTH_UNIFORM_NAME ";\n" "uniform int " ATLAS_WIDTH_UNIFORM_NAME ";\n"
"out VS_OUT {\n" "out VS_OUT {\n"
@@ -48,7 +50,7 @@ namespace
" vs_out.sprite = sprite;\n" " vs_out.sprite = sprite;\n"
" vs_out.layer = layer;\n" " vs_out.layer = layer;\n"
" vs_out.layer_color = color;\n" " vs_out.layer_color = color;\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 / 16, (2 * position.y + 1) * " GRID_SIZE_UNIFORM_NAME ".z / 16);\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(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 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)) * scale + 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"
@@ -64,6 +66,7 @@ namespace
"layout (location = 2) in int sprite;\n" "layout (location = 2) in int sprite;\n"
"layout (location = 3) in vec4 color;\n" "layout (location = 3) in vec4 color;\n"
"layout (location = 4) in float scale;\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"
"uniform int " ATLAS_WIDTH_UNIFORM_NAME ";\n" "uniform int " ATLAS_WIDTH_UNIFORM_NAME ";\n"
"out VS_OUT {\n" "out VS_OUT {\n"
@@ -77,7 +80,7 @@ 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(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 / 16, (2 * position.y + 1) * " GRID_SIZE_UNIFORM_NAME ".z / 16);\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 = color;\n" " vs_out.layer_color = color;\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 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)) * scale + 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"
@@ -285,26 +288,23 @@ 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);
glfwSwapInterval(0);
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);
glfwSwapInterval(1);
glfwSwapBuffers(m_window); glfwSwapBuffers(m_window);
} }
void glerminal::set(unsigned char x, unsigned char y, unsigned char layer, unsigned short sprite) void glerminal::set(int x, int y, int layer, unsigned short sprite)
{ {
if (x < GRID_WIDTH + 2 && y < GRID_HEIGHT + 2 && 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 + 2) + layer * GRID_AREA_2] = sprite; m_cells[x + y * (GRID_WIDTH + 2) + layer * GRID_AREA_2] = sprite;
} }
} }
unsigned short 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 + 2 && y < GRID_HEIGHT + 2 && 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 + 2) + layer * GRID_AREA_2]; return m_cells[x + y * (GRID_WIDTH + 2) + layer * GRID_AREA_2];
} }
@@ -314,18 +314,18 @@ 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 + 2 && y < GRID_HEIGHT + 2 && 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 + 2) + layer * GRID_AREA_2) + 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 + 2) + layer * GRID_AREA_2) + 1] = y_offset; m_offsets[2 * (x + y * (GRID_WIDTH + 2) + layer * GRID_AREA_2) + 1] = y_offset;
} }
} }
void glerminal::color(unsigned char x, unsigned char y, unsigned char layer, unsigned int color) void glerminal::color(int x, int y, int layer, unsigned int color)
{ {
if (x < GRID_WIDTH + 2 && y < GRID_HEIGHT + 2 && layer < LAYER_COUNT) if (x >= 0 && x < GRID_WIDTH + 2 && y >= 0 && y < GRID_HEIGHT + 2 && layer >= 0 && layer < LAYER_COUNT)
{ {
m_colors[4 * (x + y * (GRID_WIDTH + 2) + layer * GRID_AREA_2) + 0] = (color >> 0) & 0xFF; m_colors[4 * (x + y * (GRID_WIDTH + 2) + layer * GRID_AREA_2) + 0] = (color >> 0) & 0xFF;
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) + 1] = (color >> 8) & 0xFF;
@@ -334,9 +334,12 @@ namespace glerminal
} }
} }
void glerminal::scale(unsigned char x, unsigned char y, unsigned char layer, float scale) void glerminal::scale(int x, int y, int layer, float scale)
{ {
m_scales[x + y * (GRID_WIDTH + 2) + layer * GRID_AREA_2] = 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)
@@ -654,6 +657,7 @@ namespace glerminal
// setup uniforms // setup uniforms
glUseProgram(m_program); glUseProgram(m_program);
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); 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); glUniform1i(glGetUniformLocation(m_program, ATLAS_WIDTH_UNIFORM_NAME), MAX_SPRITES_ROW);
@@ -735,7 +739,6 @@ namespace glerminal
update_sprites(); update_sprites();
glBindTextureUnit(2, m_sprites_texture); glBindTextureUnit(2, m_sprites_texture);
const auto err = glGetError();
// -- setup framebuffer -- // -- setup framebuffer --
glGenFramebuffers(1, &m_framebuffer); glGenFramebuffers(1, &m_framebuffer);
@@ -908,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 short 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 short glerminal_get(unsigned char x, unsigned char y, unsigned short 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_color(unsigned char x, unsigned char y, 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->color(x, y, layer, color); GLERMINAL_G->color(x + 1, y + 1, layer, color);
} }
void glerminal_scale(unsigned char x, unsigned char y, 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->scale(x, y, 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)
@@ -987,4 +990,4 @@ void glerminal_sound(const char* name)
if (!GLERMINAL_G) { return; } if (!GLERMINAL_G) { return; }
GLERMINAL_G->play_sound(name); GLERMINAL_G->play_sound(name);
} }

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

@@ -15,7 +15,7 @@ namespace
for (int i = 0; i < GRID_HEIGHT; i++) for (int i = 0; i < GRID_HEIGHT; i++)
{ {
glerminal_set(i + 1, i + 1, 0, 1); glerminal_set(i, i, 0, 1);
} }
glerminal_flush(); glerminal_flush();

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);
} }