partially done work on total source tree rework, separation of engine context and game context, generalization of renderer for different backends as well as web platform target
This commit is contained in:
parent
ca0305feab
commit
551d60ef85
5
.gitignore
vendored
5
.gitignore
vendored
@ -5,6 +5,10 @@
|
||||
!Makefile
|
||||
|
||||
**/*.exe
|
||||
**/*.html
|
||||
**/*.js
|
||||
**/*.wasm
|
||||
**/*.wasm.map
|
||||
**/*.so
|
||||
**/*.dll
|
||||
**/*.7z
|
||||
@ -16,6 +20,7 @@
|
||||
.idea/
|
||||
.cache/
|
||||
.build/
|
||||
.build-web/
|
||||
build/
|
||||
out/
|
||||
|
||||
|
@ -3,7 +3,10 @@ cmake_minimum_required(VERSION 3.21)
|
||||
project(townengine LANGUAGES C)
|
||||
|
||||
# SDL dependencies
|
||||
find_package(SDL2 REQUIRED GLOBAL)
|
||||
# for whatever reason Emscripten has SDL2 config, but not actual SDL2 port by default
|
||||
if(NOT EMSCRIPTEN)
|
||||
find_package(SDL2 REQUIRED GLOBAL)
|
||||
endif()
|
||||
|
||||
# CMake actually has no default configuration and it's toolchain file dependent, set debug if not specified
|
||||
if(NOT CMAKE_BUILD_TYPE)
|
||||
@ -19,6 +22,14 @@ set(TWN_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR} CACHE INTERNAL "")
|
||||
option(TWN_FEATURE_DYNLIB_GAME "Enable dynamic library loading support" ON)
|
||||
option(TWN_ARCHIVE_DATA "Enable archival of assets" OFF)
|
||||
|
||||
# todo: figure out how to compile for dynamic linking instead
|
||||
if(EMSCRIPTEN)
|
||||
if(TWN_FEATURE_DYNLIB_GAME)
|
||||
message(WARNING "TWN_FEATURE_DYNLIB_GAME is set, but not supported - it is turned off")
|
||||
set(TWN_FEATURE_DYNLIB_GAME OFF CACHE INTERNAL "")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# add -fPIC globally so that it's linked well
|
||||
add_compile_options($<$<BOOL:${TWN_FEATURE_DYNLIB_GAME}>:-fPIC>)
|
||||
|
||||
@ -37,8 +48,8 @@ add_subdirectory(third-party/physfs SYSTEM)
|
||||
add_subdirectory(third-party/libxm SYSTEM)
|
||||
|
||||
|
||||
if(UNIX)
|
||||
set(SYSTEM_SOURCE_FILES townengine/system/linux/elf.c)
|
||||
if(LINUX)
|
||||
set(SYSTEM_SOURCE_FILES src/system/linux/elf.c)
|
||||
else()
|
||||
set(SYSTEM_SOURCE_FILES)
|
||||
endif()
|
||||
@ -46,22 +57,22 @@ endif()
|
||||
set(TWN_THIRD_PARTY_SOURCE_FILES
|
||||
third-party/physfs/extras/physfsrwops.c
|
||||
third-party/stb/stb_vorbis.c
|
||||
third-party/glad/src/glad.c)
|
||||
$<$<NOT:$<BOOL:${EMSCRIPTEN}>>:third-party/glad/src/glad.c>)
|
||||
|
||||
set(TWN_SOURCE_FILES
|
||||
townengine/twn_loop.c
|
||||
townengine/twn_main.c
|
||||
townengine/config.h
|
||||
townengine/context/context.c townengine/context.h
|
||||
townengine/audio/audio.c townengine/audio.h
|
||||
townengine/util.c townengine/util.h
|
||||
townengine/rendering.c townengine/rendering.h
|
||||
townengine/input/input.c townengine/input.h
|
||||
townengine/camera.c townengine/camera.h
|
||||
townengine/textures/textures.c
|
||||
townengine/twn_game_object.c
|
||||
src/twn_loop.c
|
||||
src/twn_main.c
|
||||
src/twn_context.c include/twn_context.h
|
||||
src/twn_audio.c include/twn_audio.h
|
||||
src/twn_util.c include/twn_util.h
|
||||
src/twn_rendering.c include/twn_rendering.h
|
||||
src/twn_input.c include/twn_input.h
|
||||
src/twn_camera.c include/twn_camera.h
|
||||
src/twn_textures.c include/twn_textures.c
|
||||
src/twn_game_object.c
|
||||
|
||||
$<$<NOT:$<BOOL:${TWN_FEATURE_DYNLIB_GAME}>>:townengine/twn_main.c>
|
||||
# for dynamic load based solution main is compiled in a separate target
|
||||
$<$<NOT:$<BOOL:${TWN_FEATURE_DYNLIB_GAME}>>:src/twn_main.c>
|
||||
|
||||
${SYSTEM_SOURCE_FILES})
|
||||
|
||||
@ -79,13 +90,13 @@ endif()
|
||||
source_group(TREE ${TWN_ROOT_DIR} FILES ${TWN_SOURCE_FILES})
|
||||
|
||||
set_target_properties(${TWN_TARGET} PROPERTIES
|
||||
C_STANDARD 11
|
||||
C_STANDARD_REQUIRED ON
|
||||
C_EXTENSIONS ON) # extensions are required by stb_ds.h
|
||||
C_STANDARD 11
|
||||
C_STANDARD_REQUIRED ON
|
||||
C_EXTENSIONS ON) # extensions are required by stb_ds.h
|
||||
|
||||
# precompile commonly used not-so-small headers
|
||||
target_precompile_headers(${TWN_TARGET} PRIVATE
|
||||
third-party/glad/include/glad/glad.h
|
||||
$<$<NOT:$<BOOL:${EMSCRIPTEN}>>:third-party/glad/include/glad/glad.h>
|
||||
third-party/stb/stb_ds.h)
|
||||
|
||||
# distribution definitions, set them with -DX=X in cli
|
||||
@ -104,7 +115,8 @@ function(give_options_without_warnings target)
|
||||
-ffp-contract=fast
|
||||
-fno-signed-zeros
|
||||
-fno-trapping-math
|
||||
-freciprocal-math)
|
||||
-freciprocal-math
|
||||
$<$<BOOL:${EMSCRIPTEN}>:-sUSE_SDL=2>)
|
||||
|
||||
set(BUILD_FLAGS_RELEASE
|
||||
-O3
|
||||
@ -114,8 +126,7 @@ function(give_options_without_warnings target)
|
||||
-fdata-sections
|
||||
-ffunction-sections
|
||||
-funroll-loops
|
||||
-fomit-frame-pointer
|
||||
$<$<STREQUAL:${CMAKE_C_COMPILER_ID},Gnu>:-fallow-store-data-races>)
|
||||
-fomit-frame-pointer)
|
||||
|
||||
set(BUILD_FLAGS_DEBUG
|
||||
-O0
|
||||
@ -124,7 +135,8 @@ function(give_options_without_warnings target)
|
||||
-fno-omit-frame-pointer
|
||||
-fstack-protector-all
|
||||
-fsanitize=undefined
|
||||
-fsanitize=address)
|
||||
-fsanitize=address
|
||||
$<$<BOOL:${EMSCRIPTEN}>:-gsource-map>)
|
||||
|
||||
target_compile_options(${target} PRIVATE
|
||||
${BUILD_FLAGS}
|
||||
@ -179,9 +191,9 @@ function(include_deps target)
|
||||
third-party/physfs/src
|
||||
third-party/physfs/extras
|
||||
third-party/libxm/include
|
||||
third-party/glad/include
|
||||
third-party/stb
|
||||
third-party/x-watcher)
|
||||
third-party/x-watcher
|
||||
$<$<NOT:$<BOOL:${EMSCRIPTEN}>>:third-party/glad/include>)
|
||||
|
||||
list(TRANSFORM THIRD_PARTY_INCLUDES PREPEND ${TWN_ROOT_DIR}/)
|
||||
target_include_directories(${target} SYSTEM PRIVATE ${THIRD_PARTY_INCLUDES})
|
||||
@ -193,7 +205,7 @@ endfunction()
|
||||
|
||||
function(link_deps target)
|
||||
target_link_libraries(${target} PUBLIC
|
||||
SDL2::SDL2
|
||||
$<$<NOT:$<BOOL:${EMSCRIPTEN}>>:SDL2::SDL2>
|
||||
physfs-static
|
||||
xms)
|
||||
endfunction()
|
||||
@ -205,14 +217,14 @@ function(use_townengine target sources output_directory data_dir)
|
||||
add_library(${target}_game SHARED ${sources})
|
||||
give_options(${target}_game)
|
||||
include_deps(${target}_game)
|
||||
target_link_libraries(${target}_game PUBLIC SDL2::SDL2 ${TWN_TARGET})
|
||||
target_link_libraries(${target}_game PUBLIC $<$<NOT:$<BOOL:${EMSCRIPTEN}>>:SDL2::SDL2> ${TWN_TARGET})
|
||||
set_target_properties(${target}_game PROPERTIES
|
||||
OUTPUT_NAME game
|
||||
LIBRARY_OUTPUT_DIRECTORY ${output_directory}
|
||||
RUNTIME_OUTPUT_DIRECTORY ${output_directory})
|
||||
|
||||
# launcher binary, loads game and engine shared library
|
||||
add_executable(${target}_app ${TWN_ROOT_DIR}/townengine/twn_main.c)
|
||||
add_executable(${target}_app ${TWN_ROOT_DIR}/src/twn_main.c)
|
||||
|
||||
# todo: copy instead?
|
||||
# put libtownengine.so alongside the binary
|
||||
@ -225,7 +237,16 @@ function(use_townengine target sources output_directory data_dir)
|
||||
|
||||
set_target_properties(${target}_app PROPERTIES
|
||||
OUTPUT_NAME launcher
|
||||
LIBRARY_OUTPUT_DIRECTORY ${output_directory})
|
||||
LIBRARY_OUTPUT_DIRECTORY ${output_directory}
|
||||
RUNTIME_OUTPUT_DIRECTORY ${output_directory})
|
||||
|
||||
if(EMSCRIPTEN)
|
||||
set_target_properties(${target}_app PROPERTIES
|
||||
SUFFIX .html)
|
||||
endif()
|
||||
|
||||
target_compile_options(${target}_app PRIVATE
|
||||
$<$<BOOL:${EMSCRIPTEN}>:--shell-file ${TWN_ROOT_DIR}/shell_minimal.html>)
|
||||
|
||||
# system libraries
|
||||
find_library(MATH_LIBRARY m)
|
||||
@ -236,10 +257,8 @@ function(use_townengine target sources output_directory data_dir)
|
||||
give_options(${target}_app)
|
||||
include_deps(${target}_app)
|
||||
link_deps(${target}_app)
|
||||
target_link_libraries(${target}_app PUBLIC ${TWN_TARGET})
|
||||
set_target_properties(${target}_app PROPERTIES
|
||||
RUNTIME_OUTPUT_DIRECTORY ${output_directory})
|
||||
|
||||
target_link_libraries(${target}_app PUBLIC ${TWN_TARGET})
|
||||
if(WIN32)
|
||||
# copy dlls for baby windows
|
||||
add_custom_command(TARGET ${target}_app POST_BUILD
|
||||
@ -297,5 +316,5 @@ endif()
|
||||
|
||||
# move compie_commands.json into root directory so that it plays nicer with code editors without any configuration
|
||||
add_custom_target(copy-compile-commands ALL
|
||||
${CMAKE_COMMAND} -E copy_if_different ${CMAKE_BINARY_DIR}/compile_commands.json
|
||||
${TWN_ROOT_DIR})
|
||||
${CMAKE_COMMAND} -E copy_if_different ${CMAKE_BINARY_DIR}/compile_commands.json
|
||||
${TWN_ROOT_DIR})
|
||||
|
@ -1,3 +1,7 @@
|
||||
#!/bin/env sh
|
||||
|
||||
cmake -B .build "$@" && cmake --build .build
|
||||
if [ $1 = "web" ]; then
|
||||
emcmake cmake -B .build-web "${@:2}" && cmake --build .build-web
|
||||
else
|
||||
cmake -B .build "$@" && cmake --build .build
|
||||
fi
|
||||
|
20
docs/source_directory_structure.txt
Normal file
20
docs/source_directory_structure.txt
Normal file
@ -0,0 +1,20 @@
|
||||
all source files are prefixed with 'twn_'
|
||||
|
||||
things are categorized into two main folders:
|
||||
- 'include' for any definition that are usable from townengine library
|
||||
- 'src' for any private implementations
|
||||
|
||||
internal headers have '_c.h' ending, so to not collide with our include style
|
||||
|
||||
example of a module:
|
||||
src/twn_module.c -- internal implementations
|
||||
src/twn_module_c.h -- private declarations
|
||||
include/twn_module.h -- public declarations
|
||||
|
||||
both private and public headers are included via root:
|
||||
#include "twn_module.h"
|
||||
#include "twn_module_c.h"
|
||||
|
||||
additional subfolders in src/ are possible when implementation permutations are in place, build system will chose the appropriate ones
|
||||
|
||||
all procedures in include/ headers must have TWN_API macro applied
|
@ -1,5 +1,5 @@
|
||||
#ifndef AUDIO_H
|
||||
#define AUDIO_H
|
||||
#ifndef TWN_AUDIO_H
|
||||
#define TWN_AUDIO_H
|
||||
|
||||
#include "twn_engine_api.h"
|
||||
|
@ -1,9 +1,10 @@
|
||||
#ifndef CAMERA_H
|
||||
#define CAMERA_H
|
||||
#ifndef TWN_CAMERA_H
|
||||
#define TWN_CAMERA_H
|
||||
|
||||
#include "util.h"
|
||||
#include "twn_util.h"
|
||||
#include "twn_engine_api.h"
|
||||
|
||||
/* TODO: make it cached */
|
||||
/* TODO: make it cached? */
|
||||
/* for example, perspective matrix only needs recaluclation on FOV change */
|
||||
|
||||
/* first person camera class */
|
||||
@ -14,8 +15,8 @@ typedef struct camera {
|
||||
float fov; /* field of view, in radians */
|
||||
} t_camera;
|
||||
|
||||
t_matrix4 camera_look_at(const t_camera *camera);
|
||||
TWN_API t_matrix4 camera_look_at(const t_camera *camera);
|
||||
|
||||
t_matrix4 camera_perspective(const t_camera *const camera);
|
||||
TWN_API t_matrix4 camera_perspective(const t_camera *const camera);
|
||||
|
||||
#endif
|
@ -1,9 +1,5 @@
|
||||
#ifndef CONFIG_H
|
||||
#define CONFIG_H
|
||||
|
||||
|
||||
#include <glad/glad.h>
|
||||
|
||||
#ifndef TWN_CONFIG_H
|
||||
#define TWN_CONFIG_H
|
||||
|
||||
/*
|
||||
* this file is for configuration values which are to be set at
|
||||
@ -34,10 +30,6 @@
|
||||
|
||||
#define TEXT_FONT_TEXTURE_SIZE 1024
|
||||
#define TEXT_FONT_OVERSAMPLING 4
|
||||
#define TEXT_FONT_FILTERING GL_LINEAR
|
||||
|
||||
/* 1024 * 1024 */
|
||||
/* #define UMKA_STACK_SIZE 1048576 */
|
||||
|
||||
#define TEXT_FONT_FILTERING TEXTURE_FILTER_LINEAR
|
||||
|
||||
#endif
|
38
include/twn_context.h
Normal file
38
include/twn_context.h
Normal file
@ -0,0 +1,38 @@
|
||||
#ifndef TWN_CONTEXT_H
|
||||
#define TWN_CONTEXT_H
|
||||
|
||||
#include "twn_input.h"
|
||||
#include "twn_engine_api.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
typedef struct context {
|
||||
struct input_state input;
|
||||
|
||||
int64_t delta_time; /* preserves real time frame delta with no manipilation */
|
||||
uint64_t tick_count;
|
||||
|
||||
/* set just once on startup */
|
||||
uint64_t random_seed;
|
||||
|
||||
/* this should be a multiple of TICKS_PER_SECOND */
|
||||
/* use it to simulate low framerate (e.g. at 60 tps, set to 2 for 30 fps) */
|
||||
/* it can be changed at runtime; any resulting logic anomalies are bugs */
|
||||
unsigned int update_multiplicity;
|
||||
int window_w;
|
||||
int window_h;
|
||||
|
||||
/* you may read from and write to these from game code */
|
||||
void *udata;
|
||||
|
||||
bool debug;
|
||||
bool is_running;
|
||||
bool window_size_has_changed;
|
||||
bool initialization_needed;
|
||||
} t_ctx;
|
||||
|
||||
TWN_API extern t_ctx ctx;
|
||||
|
||||
#endif
|
@ -3,11 +3,11 @@
|
||||
#define GAME_API_H
|
||||
|
||||
|
||||
#include "townengine/context.h"
|
||||
#include "townengine/rendering.h"
|
||||
#include "townengine/audio.h"
|
||||
#include "townengine/util.h"
|
||||
#include "townengine/input.h"
|
||||
#include "twn_context.h"
|
||||
#include "twn_rendering.h"
|
||||
#include "twn_audio.h"
|
||||
#include "twn_util.h"
|
||||
#include "twn_input.h"
|
||||
#include "twn_engine_api.h"
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
#ifndef INPUT_H
|
||||
#define INPUT_H
|
||||
#ifndef TWN_INPUT_H
|
||||
#define TWN_INPUT_H
|
||||
|
||||
#include "config.h"
|
||||
#include "vec.h"
|
||||
#include "util.h"
|
||||
#include "twn_config.h"
|
||||
#include "twn_vec.h"
|
||||
#include "twn_util.h"
|
||||
#include "twn_engine_api.h"
|
||||
|
||||
#include <SDL2/SDL.h>
|
@ -1,5 +1,5 @@
|
||||
#ifndef RENDERING_H
|
||||
#define RENDERING_H
|
||||
#ifndef TWN_RENDERING_H
|
||||
#define TWN_RENDERING_H
|
||||
|
||||
#include "util.h"
|
||||
#include "macros/option.h"
|
||||
@ -35,8 +35,6 @@ TWN_API void push_rectangle(t_frect rect, t_color color);
|
||||
/* pushes a filled circle onto the circle render queue */
|
||||
TWN_API void push_circle(t_fvec2 position, float radius, t_color color);
|
||||
|
||||
TWN_API void text_cache_init(struct text_cache *cache);
|
||||
TWN_API void text_cache_deinit(struct text_cache *cache);
|
||||
TWN_API void push_text(char *string, t_fvec2 position, int height_px, t_color color, const char *font_path);
|
||||
TWN_API int get_text_width(char *string, int height_px, const char *font_path);
|
||||
|
||||
@ -44,13 +42,15 @@ TWN_API int get_text_width(char *string, int height_px, const char *font_path);
|
||||
/* vertices are in absolute coordinates, relative to world origin */
|
||||
/* texture coordinates are in pixels */
|
||||
TWN_API void unfurl_triangle(const char *path,
|
||||
t_fvec3 v0,
|
||||
t_fvec3 v1,
|
||||
t_fvec3 v2,
|
||||
t_shvec2 uv0,
|
||||
t_shvec2 uv1,
|
||||
t_shvec2 uv2);
|
||||
t_fvec3 v0,
|
||||
t_fvec3 v1,
|
||||
t_fvec3 v2,
|
||||
t_shvec2 uv0,
|
||||
t_shvec2 uv1,
|
||||
t_shvec2 uv2);
|
||||
|
||||
// TODO: decide whether it's needed to begin with?
|
||||
// intended usage for it is baked lighting, i would think.
|
||||
/* pushes a colored textured 3d triangle onto the render queue */
|
||||
// void unfurl_colored_triangle(const char *path,
|
||||
// t_fvec3 v0,
|
||||
@ -66,9 +66,9 @@ TWN_API void unfurl_triangle(const char *path,
|
||||
// TODO:
|
||||
// http://www.lighthouse3d.com/opengl/billboarding/index.php?billCheat2
|
||||
// void unfurl_billboard(const char *path,
|
||||
// t_fvec3 position,
|
||||
// t_fvec2 scaling,
|
||||
// t_frect uvs);
|
||||
// t_fvec3 position,
|
||||
// t_fvec2 scaling,
|
||||
// t_frect uvs);
|
||||
|
||||
/* pushes a camera state to be used for all future unfurl_* commands */
|
||||
TWN_API void set_camera(const t_camera *camera);
|
@ -1,7 +1,7 @@
|
||||
#ifndef UTIL_H
|
||||
#define UTIL_H
|
||||
|
||||
#include "townengine/vec.h"
|
||||
#include "twn_vec.h"
|
||||
#include "twn_engine_api.h"
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
@ -164,5 +164,4 @@ static inline t_fvec2 fast_cossine(float a) {
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#endif
|
@ -1,5 +1,5 @@
|
||||
#ifndef VEC_H
|
||||
#define VEC_H
|
||||
#ifndef TWN_VEC_H
|
||||
#define TWN_VEC_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <math.h>
|
143
shell_minimal.html
Normal file
143
shell_minimal.html
Normal file
@ -0,0 +1,143 @@
|
||||
<!doctype html>
|
||||
<html lang="en-us">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<title>Emscripten-Generated Code</title>
|
||||
<style>
|
||||
.emscripten { padding-right: 0; margin-left: auto; margin-right: auto; display: block; }
|
||||
textarea.emscripten { font-family: monospace; width: 80%; }
|
||||
div.emscripten { text-align: center; }
|
||||
div.emscripten_border { border: 1px solid black; }
|
||||
/* the canvas *must not* have any border or padding, or mouse coords will be wrong */
|
||||
canvas.emscripten { border: 0px none; background-color: black; }
|
||||
|
||||
.spinner {
|
||||
height: 50px;
|
||||
width: 50px;
|
||||
margin: 0px auto;
|
||||
-webkit-animation: rotation .8s linear infinite;
|
||||
-moz-animation: rotation .8s linear infinite;
|
||||
-o-animation: rotation .8s linear infinite;
|
||||
animation: rotation 0.8s linear infinite;
|
||||
border-left: 10px solid rgb(0,150,240);
|
||||
border-right: 10px solid rgb(0,150,240);
|
||||
border-bottom: 10px solid rgb(0,150,240);
|
||||
border-top: 10px solid rgb(100,0,200);
|
||||
border-radius: 100%;
|
||||
background-color: rgb(200,100,250);
|
||||
}
|
||||
@-webkit-keyframes rotation {
|
||||
from {-webkit-transform: rotate(0deg);}
|
||||
to {-webkit-transform: rotate(360deg);}
|
||||
}
|
||||
@-moz-keyframes rotation {
|
||||
from {-moz-transform: rotate(0deg);}
|
||||
to {-moz-transform: rotate(360deg);}
|
||||
}
|
||||
@-o-keyframes rotation {
|
||||
from {-o-transform: rotate(0deg);}
|
||||
to {-o-transform: rotate(360deg);}
|
||||
}
|
||||
@keyframes rotation {
|
||||
from {transform: rotate(0deg);}
|
||||
to {transform: rotate(360deg);}
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<hr/>
|
||||
<figure style="overflow:visible;" id="spinner"><div class="spinner"></div><center style="margin-top:0.5em"><strong>emscripten</strong></center></figure>
|
||||
<div class="emscripten" id="status">Downloading...</div>
|
||||
<div class="emscripten">
|
||||
<progress value="0" max="100" id="progress" hidden=1></progress>
|
||||
</div>
|
||||
<div class="emscripten_border">
|
||||
<canvas class="emscripten" id="canvas" oncontextmenu="event.preventDefault()" tabindex=-1></canvas>
|
||||
</div>
|
||||
<hr/>
|
||||
<div class="emscripten">
|
||||
<input type="checkbox" id="resize">Resize canvas
|
||||
<input type="checkbox" id="pointerLock" checked>Lock/hide mouse pointer
|
||||
|
||||
<input type="button" value="Fullscreen" onclick="Module.requestFullscreen(document.getElementById('pointerLock').checked,
|
||||
document.getElementById('resize').checked)">
|
||||
</div>
|
||||
|
||||
<hr/>
|
||||
<textarea class="emscripten" id="output" rows="8"></textarea>
|
||||
<hr>
|
||||
<script type='text/javascript'>
|
||||
var statusElement = document.getElementById('status');
|
||||
var progressElement = document.getElementById('progress');
|
||||
var spinnerElement = document.getElementById('spinner');
|
||||
|
||||
var Module = {
|
||||
print: (function() {
|
||||
var element = document.getElementById('output');
|
||||
if (element) element.value = ''; // clear browser cache
|
||||
return (...args) => {
|
||||
var text = args.join(' ');
|
||||
// These replacements are necessary if you render to raw HTML
|
||||
//text = text.replace(/&/g, "&");
|
||||
//text = text.replace(/</g, "<");
|
||||
//text = text.replace(/>/g, ">");
|
||||
//text = text.replace('\n', '<br>', 'g');
|
||||
console.log(text);
|
||||
if (element) {
|
||||
element.value += text + "\n";
|
||||
element.scrollTop = element.scrollHeight; // focus on bottom
|
||||
}
|
||||
};
|
||||
})(),
|
||||
canvas: (() => {
|
||||
var canvas = document.getElementById('canvas');
|
||||
|
||||
// As a default initial behavior, pop up an alert when webgl context is lost. To make your
|
||||
// application robust, you may want to override this behavior before shipping!
|
||||
// See http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15.2
|
||||
canvas.addEventListener("webglcontextlost", (e) => { alert('WebGL context lost. You will need to reload the page.'); e.preventDefault(); }, false);
|
||||
|
||||
return canvas;
|
||||
})(),
|
||||
setStatus: (text) => {
|
||||
if (!Module.setStatus.last) Module.setStatus.last = { time: Date.now(), text: '' };
|
||||
if (text === Module.setStatus.last.text) return;
|
||||
var m = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/);
|
||||
var now = Date.now();
|
||||
if (m && now - Module.setStatus.last.time < 30) return; // if this is a progress update, skip it if too soon
|
||||
Module.setStatus.last.time = now;
|
||||
Module.setStatus.last.text = text;
|
||||
if (m) {
|
||||
text = m[1];
|
||||
progressElement.value = parseInt(m[2])*100;
|
||||
progressElement.max = parseInt(m[4])*100;
|
||||
progressElement.hidden = false;
|
||||
spinnerElement.hidden = false;
|
||||
} else {
|
||||
progressElement.value = null;
|
||||
progressElement.max = null;
|
||||
progressElement.hidden = true;
|
||||
if (!text) spinnerElement.hidden = true;
|
||||
}
|
||||
statusElement.innerHTML = text;
|
||||
},
|
||||
totalDependencies: 0,
|
||||
monitorRunDependencies: (left) => {
|
||||
this.totalDependencies = Math.max(this.totalDependencies, left);
|
||||
Module.setStatus(left ? 'Preparing... (' + (this.totalDependencies-left) + '/' + this.totalDependencies + ')' : 'All downloads complete.');
|
||||
}
|
||||
};
|
||||
Module.setStatus('Downloading...');
|
||||
window.onerror = () => {
|
||||
Module.setStatus('Exception thrown, see JavaScript console');
|
||||
spinnerElement.style.display = 'none';
|
||||
Module.setStatus = (text) => {
|
||||
if (text) console.error('[post-exception status] ' + text);
|
||||
};
|
||||
};
|
||||
</script>
|
||||
{{{ SCRIPT }}}
|
||||
</body>
|
||||
</html>
|
@ -1,16 +1,10 @@
|
||||
/* a rendering.c mixin */
|
||||
#ifndef CIRCLES_H
|
||||
#define CIRCLES_H
|
||||
|
||||
#include "../util.h"
|
||||
#include "townengine/util.h"
|
||||
#include "townengine/context.h"
|
||||
#include "internal_api.h"
|
||||
#include "twn_rendering_c.h"
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
#include <stb_ds.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
void push_circle(t_fvec2 position, float radius, t_color color) {
|
||||
struct circle_primitive circle = {
|
||||
@ -29,7 +23,7 @@ void push_circle(t_fvec2 position, float radius, t_color color) {
|
||||
|
||||
/* TODO: caching and reuse scheme */
|
||||
/* vertices_out and indices_out MUST BE FREED */
|
||||
static void create_circle_geometry(t_fvec2 position,
|
||||
void create_circle_geometry(t_fvec2 position,
|
||||
t_color color,
|
||||
float radius,
|
||||
size_t num_vertices,
|
||||
@ -91,42 +85,3 @@ static void create_circle_geometry(t_fvec2 position,
|
||||
*vertices_out = vertices;
|
||||
*indices_out = indices;
|
||||
}
|
||||
|
||||
|
||||
static void render_circle(const struct circle_primitive *circle) {
|
||||
SDL_Vertex *vertices = NULL;
|
||||
int *indices = NULL;
|
||||
int num_vertices = (int)circle->radius;
|
||||
|
||||
create_circle_geometry(circle->position,
|
||||
circle->color,
|
||||
circle->radius,
|
||||
num_vertices,
|
||||
&vertices,
|
||||
&indices);
|
||||
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glVertexPointer(2,
|
||||
GL_FLOAT,
|
||||
sizeof (SDL_Vertex),
|
||||
&vertices->position);
|
||||
|
||||
glEnableClientState(GL_COLOR_ARRAY);
|
||||
glColorPointer(4,
|
||||
GL_UNSIGNED_BYTE,
|
||||
sizeof (SDL_Vertex),
|
||||
&vertices->color);
|
||||
|
||||
glDrawElements(GL_TRIANGLES,
|
||||
num_vertices * 3,
|
||||
GL_UNSIGNED_INT,
|
||||
indices);
|
||||
|
||||
glDisableClientState(GL_COLOR_ARRAY);
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
|
||||
free(vertices);
|
||||
free(indices);
|
||||
}
|
||||
|
||||
#endif
|
28
src/rendering/twn_gl_15_gpu_texture.c
Normal file
28
src/rendering/twn_gl_15_gpu_texture.c
Normal file
@ -0,0 +1,28 @@
|
||||
static gpu_texture new_gl_texture(void) {
|
||||
GLuint texture;
|
||||
glGenTextures(1, &texture);
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
|
||||
|
||||
#if !defined(EMSCRIPTEN)
|
||||
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
||||
#endif
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
return create_gpu_texture(TEXTURE_FILTER_NEAREST, true);
|
||||
}
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D,
|
||||
0,
|
||||
GL_RGBA8,
|
||||
surface->w,
|
||||
surface->h,
|
||||
0,
|
||||
GL_RGBA,
|
||||
GL_UNSIGNED_BYTE,
|
||||
surface->pixels);
|
429
src/rendering/twn_gl_15_rendering.c
Normal file
429
src/rendering/twn_gl_15_rendering.c
Normal file
@ -0,0 +1,429 @@
|
||||
#include "twn_gl_15_rendering_c.h"
|
||||
#include "twn_rendering_c.h"
|
||||
#include "townengine/util.h"
|
||||
#include "townengine/config.h"
|
||||
#include "townengine/context.h"
|
||||
#include "twn_text_c.h"
|
||||
|
||||
#include <glad/glad.h>
|
||||
|
||||
|
||||
/* interleaved vertex array data */
|
||||
/* TODO: use int16_t for uvs */
|
||||
/* TODO: use packed types? */
|
||||
/* TODO: int16_t could be used for positioning, but we would need to have more CPU calcs */
|
||||
struct element_indexed_quad {
|
||||
/* upper-left */
|
||||
t_fvec2 v0;
|
||||
t_fvec2 uv0;
|
||||
t_color c0;
|
||||
/* bottom-left */
|
||||
t_fvec2 v1;
|
||||
t_fvec2 uv1;
|
||||
t_color c1;
|
||||
/* bottom-right */
|
||||
t_fvec2 v2;
|
||||
t_fvec2 uv2;
|
||||
t_color c2;
|
||||
/* upper-right */
|
||||
t_fvec2 v3;
|
||||
t_fvec2 uv3;
|
||||
t_color c3;
|
||||
};
|
||||
|
||||
|
||||
struct element_indexed_quad_without_color {
|
||||
/* upper-left */
|
||||
t_fvec2 v0;
|
||||
t_fvec2 uv0;
|
||||
/* bottom-left */
|
||||
t_fvec2 v1;
|
||||
t_fvec2 uv1;
|
||||
/* bottom-right */
|
||||
t_fvec2 v2;
|
||||
t_fvec2 uv2;
|
||||
/* upper-right */
|
||||
t_fvec2 v3;
|
||||
t_fvec2 uv3;
|
||||
};
|
||||
|
||||
|
||||
typedef enum {
|
||||
PIPELINE_NO,
|
||||
PIPELINE_SPACE,
|
||||
PIPELINE_2D,
|
||||
} pipeline;
|
||||
|
||||
|
||||
static pipeline pipeline_last_used = PIPELINE_NO;
|
||||
|
||||
|
||||
void use_space_pipeline(void) {
|
||||
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
|
||||
glShadeModel(GL_SMOOTH);
|
||||
|
||||
if (GLAD_GL_ARB_depth_clamp)
|
||||
glDisable(GL_DEPTH_CLAMP);
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadMatrixf(&camera_projection_matrix.row[0].x);
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadMatrixf(&camera_look_at_matrix.row[0].x);
|
||||
|
||||
glEnable(GL_CULL_FACE);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glDepthMask(GL_TRUE);
|
||||
glDepthFunc(GL_LESS);
|
||||
glDepthRange(0, 1);
|
||||
glDisable(GL_BLEND);
|
||||
glEnable(GL_ALPHA_TEST); /* TODO: infer its usage? */
|
||||
glAlphaFunc(GL_EQUAL, 1.0f);
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
|
||||
/* solid white, no modulation */
|
||||
glColor4ub(255, 255, 255, 255);
|
||||
|
||||
pipeline_last_used = PIPELINE_SPACE;
|
||||
}
|
||||
|
||||
|
||||
void use_2d_pipeline(void) {
|
||||
if (pipeline_last_used == PIPELINE_SPACE) {
|
||||
glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
||||
glFlush();
|
||||
}
|
||||
|
||||
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
|
||||
glShadeModel(GL_FLAT);
|
||||
|
||||
/* removes near/far plane comparison and discard */
|
||||
if (GLAD_GL_ARB_depth_clamp)
|
||||
glDisable(GL_DEPTH_CLAMP);
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
glOrtho(0, RENDER_BASE_WIDTH, RENDER_BASE_HEIGHT, 0, -1, 1);
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glDisable(GL_CULL_FACE);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
|
||||
pipeline_last_used = PIPELINE_2D;
|
||||
}
|
||||
|
||||
|
||||
void upload_quad_vertices(t_frect rect) {
|
||||
/* client memory needs to be reachable on glDraw*, so */
|
||||
static float vertices[6 * 2];
|
||||
|
||||
vertices[0] = rect.x; vertices[1] = rect.y;
|
||||
vertices[2] = rect.x; vertices[3] = rect.y + rect.h;
|
||||
vertices[4] = rect.x + rect.w; vertices[5] = rect.y + rect.h;
|
||||
vertices[6] = rect.x + rect.w; vertices[7] = rect.y + rect.h;
|
||||
vertices[8] = rect.x + rect.w; vertices[9] = rect.y;
|
||||
vertices[10] = rect.x; vertices[11] = rect.y;
|
||||
|
||||
glVertexPointer(2, GL_FLOAT, 0, (void *)&vertices);
|
||||
}
|
||||
|
||||
|
||||
void render_rectangle(const struct rect_primitive *rectangle) {
|
||||
glColor4ub(rectangle->color.r, rectangle->color.g,
|
||||
rectangle->color.b, rectangle->color.a);
|
||||
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
upload_quad_vertices(rectangle->rect);
|
||||
|
||||
glDrawArrays(GL_TRIANGLES, 0, 6);
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
}
|
||||
|
||||
|
||||
void render_circle(const struct circle_primitive *circle) {
|
||||
SDL_Vertex *vertices = NULL;
|
||||
int *indices = NULL;
|
||||
int num_vertices = (int)circle->radius;
|
||||
|
||||
create_circle_geometry(circle->position,
|
||||
circle->color,
|
||||
circle->radius,
|
||||
num_vertices,
|
||||
&vertices,
|
||||
&indices);
|
||||
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glVertexPointer(2,
|
||||
GL_FLOAT,
|
||||
sizeof (SDL_Vertex),
|
||||
&vertices->position);
|
||||
|
||||
glEnableClientState(GL_COLOR_ARRAY);
|
||||
glColorPointer(4,
|
||||
GL_UNSIGNED_BYTE,
|
||||
sizeof (SDL_Vertex),
|
||||
&vertices->color);
|
||||
|
||||
glDrawElements(GL_TRIANGLES,
|
||||
num_vertices * 3,
|
||||
GL_UNSIGNED_INT,
|
||||
indices);
|
||||
|
||||
glDisableClientState(GL_COLOR_ARRAY);
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
|
||||
free(vertices);
|
||||
free(indices);
|
||||
}
|
||||
|
||||
|
||||
void use_texture_mode(enum texture_mode mode) {
|
||||
if (mode == TEXTURE_MODE_GHOSTLY) {
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glDepthFunc(GL_ALWAYS);
|
||||
glDepthMask(GL_FALSE);
|
||||
glDisable(GL_ALPHA_TEST);
|
||||
} else if (mode == TEXTURE_MODE_SEETHROUGH) {
|
||||
glDisable(GL_BLEND);
|
||||
glDepthFunc(GL_LEQUAL);
|
||||
glDepthMask(GL_TRUE);
|
||||
glEnable(GL_ALPHA_TEST);
|
||||
glAlphaFunc(GL_EQUAL, 1.0f);
|
||||
} else {
|
||||
glDisable(GL_BLEND);
|
||||
glDepthFunc(GL_LESS);
|
||||
glDepthMask(GL_TRUE);
|
||||
glDisable(GL_ALPHA_TEST);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
vertex_buffer_builder build_vertex_buffer(vertex_buffer buffer, size_t bytes) {
|
||||
glBindBuffer(GL_ARRAY_BUFFER, buffer);
|
||||
void *mapping = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
|
||||
if (!mapping)
|
||||
CRY("build_vertex_buffer", "Error mapping a vertex array buffer");
|
||||
|
||||
return (vertex_buffer_builder) {
|
||||
.mapping = mapping,
|
||||
.bytes_left = bytes,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
bool push_to_vertex_buffer_builder(vertex_buffer_builder *builder,
|
||||
void *bytes, size_t size) {
|
||||
if (builder->bytes_left == 0)
|
||||
return false;
|
||||
|
||||
memcpy(builder->mapping, bytes, size);
|
||||
builder->bytes_left -= size;
|
||||
|
||||
/* trigger data send */
|
||||
if (builder->bytes_left == 0) {
|
||||
glUnmapBuffer(GL_ARRAY_BUFFER);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void finally_render_sprites(const struct primitive_2d primitives[],
|
||||
const struct sprite_batch batch,
|
||||
const vertex_buffer vertex_buffer)
|
||||
{
|
||||
/* TODO: maybe do, dunno */
|
||||
// glBindBuffer(GL_VERTEX_ARRAY, vertex_buffer);
|
||||
(void)vertex_buffer;
|
||||
|
||||
GLsizei off;
|
||||
GLsizei voff;
|
||||
GLsizei uvoff;
|
||||
|
||||
if (!batch.constant_colored) {
|
||||
off = offsetof(struct element_indexed_quad, v1);
|
||||
voff = offsetof(struct element_indexed_quad, v0);
|
||||
uvoff = offsetof(struct element_indexed_quad, uv0);
|
||||
} else {
|
||||
off = offsetof(struct element_indexed_quad_without_color, v1);
|
||||
voff = offsetof(struct element_indexed_quad_without_color, v0);
|
||||
uvoff = offsetof(struct element_indexed_quad_without_color, uv0);
|
||||
}
|
||||
|
||||
/* vertex specification */
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glVertexPointer(2,
|
||||
GL_FLOAT,
|
||||
off,
|
||||
(void *)(size_t)voff);
|
||||
|
||||
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
glClientActiveTexture(GL_TEXTURE0);
|
||||
glTexCoordPointer(2,
|
||||
GL_FLOAT,
|
||||
off,
|
||||
(void *)(size_t)uvoff);
|
||||
|
||||
if (!batch.constant_colored) {
|
||||
glEnableClientState(GL_COLOR_ARRAY);
|
||||
glColorPointer(4,
|
||||
GL_UNSIGNED_BYTE,
|
||||
off,
|
||||
(void *)offsetof(struct element_indexed_quad, c0));
|
||||
} else
|
||||
glColor4ub(primitives[0].sprite.color.r,
|
||||
primitives[0].sprite.color.g,
|
||||
primitives[0].sprite.color.b,
|
||||
primitives[0].sprite.color.a);
|
||||
|
||||
if (!batch.repeat)
|
||||
textures_bind(&ctx.texture_cache, primitives->sprite.texture_key);
|
||||
else
|
||||
textures_bind_repeating(&ctx.texture_cache, primitives->sprite.texture_key);
|
||||
|
||||
bind_quad_element_buffer();
|
||||
|
||||
glDrawElements(GL_TRIANGLES, 6 * (GLsizei)batch.size, GL_UNSIGNED_SHORT, NULL);
|
||||
|
||||
/* clear the state */
|
||||
glBufferData(GL_ARRAY_BUFFER, 0, NULL, GL_STREAM_DRAW);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
|
||||
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
glDisableClientState(GL_COLOR_ARRAY);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
}
|
||||
|
||||
|
||||
size_t get_sprite_payload_size(struct sprite_batch batch) {
|
||||
if (batch.constant_colored)
|
||||
return sizeof (struct element_indexed_quad_without_color);
|
||||
else
|
||||
return sizeof (struct element_indexed_quad);
|
||||
}
|
||||
|
||||
|
||||
bool push_sprite_payload_to_vertex_buffer_builder(struct sprite_batch batch,
|
||||
vertex_buffer_builder *builder,
|
||||
t_fvec2 v0, t_fvec2 v1, t_fvec2 v2, t_fvec2 v3,
|
||||
t_fvec2 uv0, t_fvec2 uv1, t_fvec2 uv2, t_fvec2 uv3,
|
||||
t_color color)
|
||||
{
|
||||
if (!batch.constant_colored) {
|
||||
struct element_indexed_quad buffer_element = {
|
||||
.v0 = v0,
|
||||
.v1 = v1,
|
||||
.v2 = v2,
|
||||
.v3 = v3,
|
||||
|
||||
.uv0 = uv0,
|
||||
.uv1 = uv1,
|
||||
.uv2 = uv2,
|
||||
.uv3 = uv3,
|
||||
|
||||
/* equal for all (flat shaded) */
|
||||
.c0 = color,
|
||||
.c1 = color,
|
||||
.c2 = color,
|
||||
.c3 = color,
|
||||
};
|
||||
|
||||
return push_to_vertex_buffer_builder(builder, &buffer_element, sizeof buffer_element);
|
||||
|
||||
} else {
|
||||
struct element_indexed_quad_without_color buffer_element = {
|
||||
.v0 = v0,
|
||||
.v1 = v1,
|
||||
.v2 = v2,
|
||||
.v3 = v3,
|
||||
|
||||
.uv0 = uv0,
|
||||
.uv1 = uv1,
|
||||
.uv2 = uv2,
|
||||
.uv3 = uv3,
|
||||
};
|
||||
|
||||
return push_to_vertex_buffer_builder(builder, &buffer_element, sizeof buffer_element);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void finally_draw_uncolored_space_traingle_batch(const struct mesh_batch *batch,
|
||||
const t_texture_key texture_key,
|
||||
const vertex_buffer vertex_buffer)
|
||||
{
|
||||
const size_t primitives_len = arrlenu(batch->primitives);
|
||||
|
||||
textures_bind(&ctx.texture_cache, texture_key);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);
|
||||
|
||||
/* vertex specification*/
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glVertexPointer(3,
|
||||
GL_FLOAT,
|
||||
offsetof(struct uncolored_space_triangle_payload, v1),
|
||||
(void *)offsetof(struct uncolored_space_triangle_payload, v0));
|
||||
|
||||
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
glClientActiveTexture(GL_TEXTURE0);
|
||||
glTexCoordPointer(2,
|
||||
GL_FLOAT,
|
||||
offsetof(struct uncolored_space_triangle_payload, v1),
|
||||
(void *)offsetof(struct uncolored_space_triangle_payload, uv0));
|
||||
|
||||
/* commit for drawing */
|
||||
glDrawArrays(GL_TRIANGLES, 0, 3 * (GLint)primitives_len);
|
||||
|
||||
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
|
||||
/* invalidate the buffer immediately */
|
||||
glBufferData(GL_ARRAY_BUFFER, 0, NULL, GL_STREAM_DRAW);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
}
|
||||
|
||||
|
||||
bool push_text_payload_to_vertex_buffer_builder(struct font_data const *font_data,
|
||||
vertex_buffer_builder *builder,
|
||||
stbtt_aligned_quad quad)
|
||||
{
|
||||
(void)font_data;
|
||||
|
||||
glTexCoord2f(quad.s0, quad.t0);
|
||||
glVertex2f(quad.x0, quad.y0);
|
||||
glTexCoord2f(quad.s1, quad.t0);
|
||||
glVertex2f(quad.x1, quad.y0);
|
||||
glTexCoord2f(quad.s1, quad.t1);
|
||||
glVertex2f(quad.x1, quad.y1);
|
||||
glTexCoord2f(quad.s0, quad.t1);
|
||||
glVertex2f(quad.x0, quad.y1);
|
||||
}
|
||||
|
||||
|
||||
void finally_draw_text(struct font_data const *font_data,
|
||||
size_t len,
|
||||
t_color color,
|
||||
vertex_buffer buffer)
|
||||
{
|
||||
use_texture_mode(TEXTURE_MODE_GHOSTLY);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, font_data->texture);
|
||||
|
||||
glColor4ub(color.r, color.g, color.b, color.a);
|
||||
}
|
||||
|
||||
|
||||
size_t get_text_payload_size(void) {
|
||||
return sizeof (struct element_indexed_quad_without_color);
|
||||
}
|
65
src/rendering/twn_gl_15_rendering_c.h
Normal file
65
src/rendering/twn_gl_15_rendering_c.h
Normal file
@ -0,0 +1,65 @@
|
||||
#ifndef TWN_GL_15_RENDERING_H
|
||||
#define TWN_GL_15_RENDERING_H
|
||||
|
||||
/*
|
||||
* OpenGL 1.5 and any 2.0+ compatibility version render implementation.
|
||||
*/
|
||||
|
||||
#include "twn_rendering_c.h"
|
||||
#include "twn_gl_any_rendering_c.h"
|
||||
|
||||
#ifdef EMSCRIPTEN
|
||||
#include <GLES2/gl2.h>
|
||||
#else
|
||||
#include <glad/glad.h>
|
||||
#endif
|
||||
|
||||
#include <stb_truetype.h>
|
||||
|
||||
|
||||
void render_circle(const struct circle_primitive *circle);
|
||||
|
||||
void render_rectangle(const struct rect_primitive *rectangle);
|
||||
|
||||
void use_space_pipeline(void);
|
||||
|
||||
void use_2d_pipeline(void);
|
||||
|
||||
void use_texture_mode(enum texture_mode mode);
|
||||
|
||||
/* uses present in 1.5 buffer mapping feature */
|
||||
vertex_buffer_builder build_vertex_buffer(vertex_buffer buffer, size_t bytes);
|
||||
|
||||
/* collects bytes for sending to the gpu until all is pushed, which is when false is returned */
|
||||
bool push_to_vertex_buffer_builder(vertex_buffer_builder *builder,
|
||||
void *bytes,
|
||||
size_t size);
|
||||
|
||||
void finally_render_sprites(struct primitive_2d const primitives[],
|
||||
struct sprite_batch batch,
|
||||
vertex_buffer buffer);
|
||||
|
||||
size_t get_sprite_payload_size(struct sprite_batch batch);
|
||||
|
||||
bool push_sprite_payload_to_vertex_buffer_builder(struct sprite_batch batch,
|
||||
vertex_buffer_builder *builder,
|
||||
t_fvec2 v0, t_fvec2 v1, t_fvec2 v2, t_fvec2 v3,
|
||||
t_fvec2 uv0, t_fvec2 uv1, t_fvec2 uv2, t_fvec2 uv3,
|
||||
t_color color);
|
||||
|
||||
void finally_draw_uncolored_space_traingle_batch(struct mesh_batch const *batch,
|
||||
t_texture_key texture_key,
|
||||
vertex_buffer buffer);
|
||||
|
||||
size_t get_text_payload_size(void);
|
||||
|
||||
bool push_text_payload_to_vertex_buffer_builder(struct font_data const *font_data,
|
||||
vertex_buffer_builder *builder,
|
||||
stbtt_aligned_quad quad);
|
||||
|
||||
void finally_draw_text(struct font_data const *font_data,
|
||||
size_t len,
|
||||
t_color color,
|
||||
vertex_buffer buffer);
|
||||
|
||||
#endif
|
@ -1,16 +1,41 @@
|
||||
/* a rendering.c mixin */
|
||||
#ifndef QUAD_ELEMENT_BUFFER_H
|
||||
#define QUAD_ELEMENT_BUFFER_H
|
||||
|
||||
#include "twn_gl_any_rendering_c.h"
|
||||
#include "townengine/context.h"
|
||||
#include "townengine/util.h"
|
||||
|
||||
#ifdef EMSCRIPTEN
|
||||
#include <GLES2/gl2.h>
|
||||
#else
|
||||
#include <glad/glad.h>
|
||||
#endif
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#define QUAD_ELEMENT_BUFFER_LENGTH (65536 / 6)
|
||||
void setup_viewport(int x, int y, int width, int height) {
|
||||
glViewport(x, y, width, height);
|
||||
}
|
||||
|
||||
static void bind_quad_element_buffer(void) {
|
||||
//////// VERTEX BUFFER ////////
|
||||
|
||||
vertex_buffer create_vertex_buffer(void) {
|
||||
GLuint result;
|
||||
glGenBuffers(1, &result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void delete_vertex_buffer(vertex_buffer buffer) {
|
||||
glDeleteBuffers(1, &buffer);
|
||||
}
|
||||
|
||||
|
||||
void specify_vertex_buffer(vertex_buffer buffer, void *data, size_t bytes) {
|
||||
glBindBuffer(GL_ARRAY_BUFFER, buffer);
|
||||
glBufferData(GL_ARRAY_BUFFER, bytes, data, GL_STREAM_DRAW);
|
||||
}
|
||||
|
||||
//////// END OF VERTEX BUFFER ////////
|
||||
|
||||
|
||||
void bind_quad_element_buffer(void) {
|
||||
static GLuint buffer = 0;
|
||||
|
||||
/* it's only generated once at runtime */
|
||||
@ -42,4 +67,23 @@ static void bind_quad_element_buffer(void) {
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void clear_draw_buffer(void) {
|
||||
glClearColor((1.0f / 255) * 230,
|
||||
(1.0f / 255) * 230,
|
||||
(1.0f / 255) * 230, 1);
|
||||
|
||||
glClear(GL_COLOR_BUFFER_BIT |
|
||||
GL_DEPTH_BUFFER_BIT |
|
||||
GL_STENCIL_BUFFER_BIT);
|
||||
}
|
||||
|
||||
|
||||
void swap_buffers(void) {
|
||||
SDL_GL_SwapWindow(ctx.window);
|
||||
}
|
||||
|
||||
|
||||
void set_depth_range(double low, double high) {
|
||||
glDepthRange(low, high);
|
||||
}
|
43
src/rendering/twn_gl_any_rendering_c.h
Normal file
43
src/rendering/twn_gl_any_rendering_c.h
Normal file
@ -0,0 +1,43 @@
|
||||
#ifndef TWN_GL_ANY_RENDERING_H
|
||||
#define TWN_GL_ANY_RENDERING_H
|
||||
|
||||
/*
|
||||
* Any OpenGL version base render methods.
|
||||
*/
|
||||
|
||||
#ifdef EMSCRIPTEN
|
||||
#include <GLES2/gl2.h>
|
||||
#else
|
||||
#include <glad/glad.h>
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
|
||||
#define QUAD_ELEMENT_BUFFER_LENGTH (65536 / 6)
|
||||
|
||||
typedef GLuint vertex_buffer;
|
||||
|
||||
typedef struct vertex_buffer_builder {
|
||||
size_t bytes_left;
|
||||
void *mapping;
|
||||
} vertex_buffer_builder;
|
||||
|
||||
|
||||
vertex_buffer create_vertex_buffer(void);
|
||||
|
||||
void delete_vertex_buffer(vertex_buffer buffer);
|
||||
|
||||
void specify_vertex_buffer(vertex_buffer buffer, void *data, size_t bytes);
|
||||
|
||||
void setup_viewport(int x, int y, int width, int height);
|
||||
|
||||
void bind_quad_element_buffer(void);
|
||||
|
||||
void clear_draw_buffer(void);
|
||||
|
||||
void swap_buffers(void);
|
||||
|
||||
void set_depth_range(double low, double high);
|
||||
|
||||
#endif
|
29
src/rendering/twn_gpu_texture.h
Normal file
29
src/rendering/twn_gpu_texture.h
Normal file
@ -0,0 +1,29 @@
|
||||
#ifndef TWN_GPU_TEXTURE_H
|
||||
#define TWN_GPU_TEXTURE_H
|
||||
|
||||
#ifdef EMSCRIPTEN
|
||||
#include <GLES2/gl2.h>
|
||||
#else
|
||||
#include <glad/glad.h>
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
|
||||
typedef GLuint gpu_texture;
|
||||
|
||||
enum texture_filter {
|
||||
TEXTURE_FILTER_NEAREAST,
|
||||
TEXTURE_FILTER_LINEAR,
|
||||
};
|
||||
|
||||
|
||||
gpu_texture create_gpu_texture(enum texture_filter filter, bool generate_mipmaps);
|
||||
|
||||
void delete_gpu_texture(gpu_texture texture);
|
||||
|
||||
void specify_gpu_texture(gpu_texture texture, void *pixels, int channels, int width, int height);
|
||||
|
||||
void bind_gpu_texture(gpu_texture texture);
|
||||
|
||||
#endif
|
@ -1,22 +1,27 @@
|
||||
#include "rendering/internal_api.h"
|
||||
#include "rendering/sprites.h"
|
||||
#include "rendering/triangles.h"
|
||||
#include "rendering/circles.h"
|
||||
#include "rendering/text.h"
|
||||
#include "textures/internal_api.h"
|
||||
#include "twn_rendering_c.h"
|
||||
#include "townengine/twn_rendering.h"
|
||||
#include "townengine/textures/internal_api.h"
|
||||
#include "townengine/context.h"
|
||||
#include "townengine/camera.h"
|
||||
|
||||
#include "twn_rendering_platform.h"
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
#include <glad/glad.h>
|
||||
#include <stb_ds.h>
|
||||
|
||||
#ifdef EMSCRIPTEN
|
||||
#include <GLES2/gl2.h>
|
||||
#else
|
||||
#include <glad/glad.h>
|
||||
#endif
|
||||
|
||||
#include <stddef.h>
|
||||
#include <tgmath.h>
|
||||
|
||||
|
||||
/* TODO: have a default initialized one */
|
||||
static t_matrix4 camera_projection_matrix;
|
||||
static t_matrix4 camera_look_at_matrix;
|
||||
t_matrix4 camera_projection_matrix;
|
||||
t_matrix4 camera_look_at_matrix;
|
||||
|
||||
|
||||
void render_queue_clear(void) {
|
||||
@ -54,38 +59,8 @@ void push_rectangle(t_frect rect, t_color color) {
|
||||
}
|
||||
|
||||
|
||||
static void upload_quad_vertices(t_frect rect) {
|
||||
/* client memory needs to be reachable on glDraw*, so */
|
||||
static float vertices[6 * 2];
|
||||
|
||||
vertices[0] = rect.x; vertices[1] = rect.y;
|
||||
vertices[2] = rect.x; vertices[3] = rect.y + rect.h;
|
||||
vertices[4] = rect.x + rect.w; vertices[5] = rect.y + rect.h;
|
||||
vertices[6] = rect.x + rect.w; vertices[7] = rect.y + rect.h;
|
||||
vertices[8] = rect.x + rect.w; vertices[9] = rect.y;
|
||||
vertices[10] = rect.x; vertices[11] = rect.y;
|
||||
|
||||
glVertexPointer(2, GL_FLOAT, 0, (void *)&vertices);
|
||||
}
|
||||
|
||||
|
||||
static void render_rectangle(const struct rect_primitive *rectangle) {
|
||||
glColor4ub(rectangle->color.r, rectangle->color.g,
|
||||
rectangle->color.b, rectangle->color.a);
|
||||
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
upload_quad_vertices(rectangle->rect);
|
||||
|
||||
glDrawArrays(GL_TRIANGLES, 0, 6);
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
}
|
||||
|
||||
|
||||
static void render_2d(void) {
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glDisable(GL_CULL_FACE);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
use_2d_pipeline();
|
||||
|
||||
const size_t render_queue_len = arrlenu(ctx.render_queue_2d);
|
||||
|
||||
@ -99,8 +74,8 @@ static void render_2d(void) {
|
||||
const struct sprite_batch batch =
|
||||
collect_sprite_batch(current, render_queue_len - i);
|
||||
|
||||
glDepthRange((double)batch_count / UINT16_MAX, 1.0);
|
||||
|
||||
/* TODO: what's even the point? just use OR_EQUAL comparison */
|
||||
set_depth_range((double)batch_count / UINT16_MAX, 1.0);
|
||||
render_sprites(current, batch);
|
||||
|
||||
i += batch.size - 1; ++batch_count;
|
||||
@ -121,19 +96,12 @@ static void render_2d(void) {
|
||||
|
||||
|
||||
static void render_space(void) {
|
||||
glEnable(GL_CULL_FACE);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glDepthMask(GL_TRUE);
|
||||
glDepthFunc(GL_LESS);
|
||||
glDepthRange(0, 1);
|
||||
glDisable(GL_BLEND);
|
||||
glEnable(GL_ALPHA_TEST); /* TODO: infer its usage */
|
||||
glAlphaFunc(GL_EQUAL, 1.0f);
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
/* nothing to do, abort */
|
||||
/* as space pipeline isn't used we can have fewer changes and initialization costs */
|
||||
if (hmlenu(ctx.uncolored_mesh_batches) == 0)
|
||||
return;
|
||||
|
||||
/* solid white, no modulation */
|
||||
glColor4ub(255, 255, 255, 255);
|
||||
use_space_pipeline();
|
||||
|
||||
for (size_t i = 0; i < hmlenu(ctx.uncolored_mesh_batches); ++i) {
|
||||
draw_uncolored_space_traingle_batch(&ctx.uncolored_mesh_batches[i].value,
|
||||
@ -150,7 +118,7 @@ void render(void) {
|
||||
if ((float)ctx.window_w / (float)ctx.window_h > RENDER_BASE_RATIO) {
|
||||
float ratio = (float)ctx.window_h / (float)RENDER_BASE_HEIGHT;
|
||||
int w = (int)((float)RENDER_BASE_WIDTH * ratio);
|
||||
glViewport(
|
||||
setup_viewport(
|
||||
ctx.window_w / 2 - w / 2,
|
||||
0,
|
||||
w,
|
||||
@ -159,7 +127,7 @@ void render(void) {
|
||||
} else {
|
||||
float ratio = (float)ctx.window_w / (float)RENDER_BASE_WIDTH;
|
||||
int h = (int)((float)RENDER_BASE_HEIGHT * ratio);
|
||||
glViewport(
|
||||
setup_viewport(
|
||||
0,
|
||||
ctx.window_h / 2 - h / 2,
|
||||
ctx.window_w,
|
||||
@ -168,57 +136,15 @@ void render(void) {
|
||||
}
|
||||
}
|
||||
|
||||
glClearColor((1.0f / 255) * 230,
|
||||
(1.0f / 255) * 230,
|
||||
(1.0f / 255) * 230, 1);
|
||||
|
||||
glClear(GL_COLOR_BUFFER_BIT |
|
||||
GL_DEPTH_BUFFER_BIT |
|
||||
GL_STENCIL_BUFFER_BIT);
|
||||
|
||||
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
|
||||
glShadeModel(GL_SMOOTH);
|
||||
|
||||
if (GLAD_GL_ARB_depth_clamp)
|
||||
glDisable(GL_DEPTH_CLAMP);
|
||||
|
||||
{
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadMatrixf(&camera_projection_matrix.row[0].x);
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadMatrixf(&camera_look_at_matrix.row[0].x);
|
||||
|
||||
render_space();
|
||||
}
|
||||
|
||||
/* TODO: only do it when transition between spaces is needed */
|
||||
glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
||||
glFlush();
|
||||
|
||||
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
|
||||
glShadeModel(GL_FLAT);
|
||||
|
||||
/* removes near/far plane comparison and discard */
|
||||
if (GLAD_GL_ARB_depth_clamp)
|
||||
glDisable(GL_DEPTH_CLAMP);
|
||||
|
||||
{
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
glOrtho(0, RENDER_BASE_WIDTH, RENDER_BASE_HEIGHT, 0, -1, 1);
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
|
||||
render_2d();
|
||||
}
|
||||
|
||||
SDL_GL_SwapWindow(ctx.window);
|
||||
clear_draw_buffer();
|
||||
render_space();
|
||||
render_2d();
|
||||
swap_buffers();
|
||||
}
|
||||
|
||||
|
||||
void set_camera(const t_camera *const camera) {
|
||||
/* TODO: skip recaulculating if it's the same? */
|
||||
camera_projection_matrix = camera_perspective(camera);
|
||||
camera_look_at_matrix = camera_look_at(camera);
|
||||
camera_look_at_matrix = camera_look_at(camera);
|
||||
}
|
@ -6,10 +6,12 @@
|
||||
#include "townengine/macros/option.h"
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
#include <glad/glad.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
extern t_matrix4 camera_projection_matrix;
|
||||
extern t_matrix4 camera_look_at_matrix;
|
||||
|
||||
struct sprite_primitive {
|
||||
t_frect rect;
|
||||
t_color color;
|
||||
@ -86,7 +88,6 @@ union uncolored_space_triangle {
|
||||
|
||||
/* batch of primitives with overlapping properties */
|
||||
struct mesh_batch {
|
||||
GLuint buffer; /* server side storage */
|
||||
uint8_t *primitives;
|
||||
};
|
||||
|
||||
@ -105,4 +106,40 @@ void render(void);
|
||||
/* clears all render queues */
|
||||
void render_queue_clear(void);
|
||||
|
||||
void push_circle(t_fvec2 position, float radius, t_color color);
|
||||
|
||||
void unfurl_triangle(const char *path,
|
||||
t_fvec3 v0,
|
||||
t_fvec3 v1,
|
||||
t_fvec3 v2,
|
||||
t_shvec2 uv0,
|
||||
t_shvec2 uv1,
|
||||
t_shvec2 uv2);
|
||||
|
||||
void create_circle_geometry(t_fvec2 position,
|
||||
t_color color,
|
||||
float radius,
|
||||
size_t num_vertices,
|
||||
SDL_Vertex **vertices_out,
|
||||
int **indices_out);
|
||||
|
||||
struct sprite_batch {
|
||||
size_t size; /* how many primitives are in current batch */
|
||||
enum texture_mode mode;
|
||||
bool constant_colored; /* whether colored batch is uniformly colored */
|
||||
bool repeat; /* whether repeat is needed */
|
||||
} collect_sprite_batch(const struct primitive_2d primitives[], size_t len);
|
||||
|
||||
void render_sprites(const struct primitive_2d primitives[],
|
||||
const struct sprite_batch batch);
|
||||
|
||||
void draw_uncolored_space_traingle_batch(struct mesh_batch *batch,
|
||||
t_texture_key texture_key);
|
||||
|
||||
void render_text(const struct text_primitive *text);
|
||||
|
||||
void text_cache_init(struct text_cache *cache);
|
||||
|
||||
void text_cache_deinit(struct text_cache *cache);
|
||||
|
||||
#endif
|
10
src/rendering/twn_rendering_platform.h
Normal file
10
src/rendering/twn_rendering_platform.h
Normal file
@ -0,0 +1,10 @@
|
||||
#ifndef TWN_RENDERING_PLATFORM_H
|
||||
#define TWN_RENDERING_PLATFORM_H
|
||||
|
||||
#ifdef EMSCRIPTEN
|
||||
#include "twn_gl_es2_rendering_c.h"
|
||||
#else
|
||||
#include "twn_gl_15_rendering_c.h"
|
||||
#endif
|
||||
|
||||
#endif
|
@ -1,58 +1,15 @@
|
||||
/* a rendering.c mixin */
|
||||
#ifndef SPRITES_H
|
||||
#define SPRITES_H
|
||||
|
||||
#include "../rendering.h"
|
||||
#include "townengine/twn_rendering.h"
|
||||
#include "twn_rendering_c.h"
|
||||
#include "townengine/context.h"
|
||||
#include "../util.h"
|
||||
#include "../textures/internal_api.h"
|
||||
#include "quad_element_buffer.h"
|
||||
#include "internal_api.h"
|
||||
#include "townengine/util.h"
|
||||
#include "townengine/textures/internal_api.h"
|
||||
#include "twn_rendering_platform.h"
|
||||
|
||||
#include <stb_ds.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/* interleaved vertex array data */
|
||||
/* TODO: use int16_t for uvs */
|
||||
/* TODO: use packed types? */
|
||||
/* TODO: int16_t could be used for positioning, but we would need to have more CPU calcs */
|
||||
struct sprite_primitive_payload {
|
||||
/* upper-left */
|
||||
t_fvec2 v0;
|
||||
t_fvec2 uv0;
|
||||
t_color c0;
|
||||
/* bottom-left */
|
||||
t_fvec2 v1;
|
||||
t_fvec2 uv1;
|
||||
t_color c1;
|
||||
/* bottom-right */
|
||||
t_fvec2 v2;
|
||||
t_fvec2 uv2;
|
||||
t_color c2;
|
||||
/* upper-right */
|
||||
t_fvec2 v3;
|
||||
t_fvec2 uv3;
|
||||
t_color c3;
|
||||
};
|
||||
|
||||
|
||||
struct sprite_primitive_payload_without_color {
|
||||
/* upper-left */
|
||||
t_fvec2 v0;
|
||||
t_fvec2 uv0;
|
||||
/* bottom-left */
|
||||
t_fvec2 v1;
|
||||
t_fvec2 uv1;
|
||||
/* bottom-right */
|
||||
t_fvec2 v2;
|
||||
t_fvec2 uv2;
|
||||
/* upper-right */
|
||||
t_fvec2 v3;
|
||||
t_fvec2 uv3;
|
||||
};
|
||||
|
||||
/*
|
||||
* an implementation note:
|
||||
* try to avoid doing expensive work in the push functions,
|
||||
@ -82,12 +39,7 @@ void push_sprite(const t_push_sprite_args args) {
|
||||
}
|
||||
|
||||
|
||||
static struct sprite_batch {
|
||||
size_t size; /* how many primitives are in current batch */
|
||||
enum texture_mode mode;
|
||||
bool constant_colored; /* whether colored batch is uniformly colored */
|
||||
bool repeat; /* whether repeat is needed */
|
||||
} collect_sprite_batch(const struct primitive_2d *primitives, size_t len) {
|
||||
struct sprite_batch collect_sprite_batch(const struct primitive_2d primitives[], size_t len) {
|
||||
/* assumes that first primitive is already a sprite */
|
||||
const uint16_t texture_key_id = primitives[0].sprite.texture_key.id;
|
||||
const int atlas_id = textures_get_atlas_id(&ctx.texture_cache, primitives[0].sprite.texture_key);
|
||||
@ -143,52 +95,22 @@ static struct sprite_batch {
|
||||
|
||||
|
||||
/* assumes that orthogonal matrix setup is done already */
|
||||
static void render_sprites(const struct primitive_2d primitives[],
|
||||
void render_sprites(const struct primitive_2d primitives[],
|
||||
const struct sprite_batch batch)
|
||||
{
|
||||
/* single vertex array is used for every batch with NULL glBufferData() trick at the end */
|
||||
static GLuint vertex_array = 0;
|
||||
static vertex_buffer vertex_array = 0;
|
||||
if (vertex_array == 0)
|
||||
glGenBuffers(1, &vertex_array);
|
||||
vertex_array = create_vertex_buffer();
|
||||
|
||||
if (batch.mode == TEXTURE_MODE_GHOSTLY) {
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glDepthFunc(GL_ALWAYS);
|
||||
glDepthMask(GL_FALSE);
|
||||
glDisable(GL_ALPHA_TEST);
|
||||
} else if (batch.mode == TEXTURE_MODE_SEETHROUGH) {
|
||||
glDisable(GL_BLEND);
|
||||
glDepthFunc(GL_LEQUAL);
|
||||
glDepthMask(GL_TRUE);
|
||||
glEnable(GL_ALPHA_TEST);
|
||||
glAlphaFunc(GL_EQUAL, 1.0f);
|
||||
} else {
|
||||
glDisable(GL_BLEND);
|
||||
glDepthFunc(GL_LESS);
|
||||
glDepthMask(GL_TRUE);
|
||||
glDisable(GL_ALPHA_TEST);
|
||||
}
|
||||
|
||||
size_t payload_size;
|
||||
if (!batch.constant_colored)
|
||||
payload_size = sizeof (struct sprite_primitive_payload);
|
||||
else
|
||||
payload_size = sizeof (struct sprite_primitive_payload_without_color);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vertex_array);
|
||||
glBufferData(GL_ARRAY_BUFFER,
|
||||
payload_size * batch.size,
|
||||
NULL,
|
||||
GL_STREAM_DRAW);
|
||||
use_sprite_blendmode(batch.mode);
|
||||
|
||||
const t_frect dims =
|
||||
textures_get_dims(&ctx.texture_cache, primitives->sprite.texture_key);
|
||||
|
||||
/* vertex population over a mapped buffer */
|
||||
/* vertex population over a vertex buffer builder interface */
|
||||
{
|
||||
/* TODO: check errors, ensure alignment ? */
|
||||
void *const payload = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
|
||||
vertex_buffer_builder payload = build_vertex_buffer(vertex_array, get_sprite_payload_size(batch) * batch.size);
|
||||
|
||||
for (size_t i = 0; i < batch.size; ++i) {
|
||||
/* render opaques front to back */
|
||||
@ -275,100 +197,9 @@ static void render_sprites(const struct primitive_2d primitives[],
|
||||
v3 = (t_fvec2){ c.x + t.x * +h.x - t.y * -h.y, c.y + t.y * +h.x + t.x * -h.y };
|
||||
}
|
||||
|
||||
if (!batch.constant_colored)
|
||||
((struct sprite_primitive_payload *)payload)[i] = (struct sprite_primitive_payload) {
|
||||
.v0 = v0,
|
||||
.v1 = v1,
|
||||
.v2 = v2,
|
||||
.v3 = v3,
|
||||
|
||||
.uv0 = uv0,
|
||||
.uv1 = uv1,
|
||||
.uv2 = uv2,
|
||||
.uv3 = uv3,
|
||||
|
||||
/* equal for all (flat shaded) */
|
||||
.c0 = sprite.color,
|
||||
.c1 = sprite.color,
|
||||
.c2 = sprite.color,
|
||||
.c3 = sprite.color,
|
||||
};
|
||||
else
|
||||
((struct sprite_primitive_payload_without_color *)payload)[i] = (struct sprite_primitive_payload_without_color) {
|
||||
.v0 = v0,
|
||||
.v1 = v1,
|
||||
.v2 = v2,
|
||||
.v3 = v3,
|
||||
|
||||
.uv0 = uv0,
|
||||
.uv1 = uv1,
|
||||
.uv2 = uv2,
|
||||
.uv3 = uv3,
|
||||
};
|
||||
push_sprite_payload_to_vertex_buffer_builder(batch, &payload, v0, v1, v2, v3, uv0, uv1, uv2, uv3, sprite.color);
|
||||
}
|
||||
|
||||
glUnmapBuffer(GL_ARRAY_BUFFER);
|
||||
}
|
||||
|
||||
GLsizei off;
|
||||
GLsizei voff;
|
||||
GLsizei uvoff;
|
||||
|
||||
if (!batch.constant_colored) {
|
||||
off = offsetof(struct sprite_primitive_payload, v1);
|
||||
voff = offsetof(struct sprite_primitive_payload, v0);
|
||||
uvoff = offsetof(struct sprite_primitive_payload, uv0);
|
||||
} else {
|
||||
off = offsetof(struct sprite_primitive_payload_without_color, v1);
|
||||
voff = offsetof(struct sprite_primitive_payload_without_color, v0);
|
||||
uvoff = offsetof(struct sprite_primitive_payload_without_color, uv0);
|
||||
}
|
||||
|
||||
/* vertex specification */
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glVertexPointer(2,
|
||||
GL_FLOAT,
|
||||
off,
|
||||
(void *)(size_t)voff);
|
||||
|
||||
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
glClientActiveTexture(GL_TEXTURE0);
|
||||
glTexCoordPointer(2,
|
||||
GL_FLOAT,
|
||||
off,
|
||||
(void *)(size_t)uvoff);
|
||||
|
||||
if (!batch.constant_colored) {
|
||||
glEnableClientState(GL_COLOR_ARRAY);
|
||||
glColorPointer(4,
|
||||
GL_UNSIGNED_BYTE,
|
||||
off,
|
||||
(void *)offsetof(struct sprite_primitive_payload, c0));
|
||||
} else
|
||||
glColor4ub(primitives[0].sprite.color.r,
|
||||
primitives[0].sprite.color.g,
|
||||
primitives[0].sprite.color.b,
|
||||
primitives[0].sprite.color.a);
|
||||
|
||||
if (!batch.repeat)
|
||||
textures_bind(&ctx.texture_cache, primitives->sprite.texture_key, GL_TEXTURE_2D);
|
||||
else
|
||||
textures_bind_repeating(&ctx.texture_cache, primitives->sprite.texture_key, GL_TEXTURE_2D);
|
||||
|
||||
bind_quad_element_buffer();
|
||||
|
||||
glDrawElements(GL_TRIANGLES, 6 * (GLsizei)batch.size, GL_UNSIGNED_SHORT, NULL);
|
||||
|
||||
/* clear the state */
|
||||
glBufferData(GL_ARRAY_BUFFER, 0, NULL, GL_STREAM_DRAW);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
|
||||
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
glDisableClientState(GL_COLOR_ARRAY);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
finally_render_sprites(primitives, batch, vertex_array);
|
||||
}
|
||||
|
||||
#endif
|
@ -1,13 +1,11 @@
|
||||
/* a rendering.c mixin */
|
||||
#ifndef TEXT_H
|
||||
#define TEXT_H
|
||||
|
||||
|
||||
#include "../util.h"
|
||||
#include "twn_rendering_c.h"
|
||||
#include "townengine/util.h"
|
||||
#include "townengine/config.h"
|
||||
#include "townengine/context.h"
|
||||
#include "townengine/twn_rendering.h"
|
||||
|
||||
#include "twn_rendering_platform.h"
|
||||
|
||||
#include <glad/glad.h>
|
||||
#include <stb_truetype.h>
|
||||
|
||||
|
||||
@ -24,7 +22,7 @@ struct font_data {
|
||||
unsigned char *file_bytes;
|
||||
size_t file_bytes_len;
|
||||
|
||||
GLuint texture;
|
||||
gpu_texture texture;
|
||||
|
||||
int height_px;
|
||||
float scale_factor;
|
||||
@ -67,24 +65,15 @@ static struct font_data *text_load_font_data(const char *path, int height_px) {
|
||||
stbtt_PackEnd(&pctx);
|
||||
}
|
||||
|
||||
glGenTextures(1, &font_data->texture);
|
||||
glBindTexture(GL_TEXTURE_2D, font_data->texture);
|
||||
glTexImage2D(
|
||||
GL_TEXTURE_2D,
|
||||
0,
|
||||
GL_ALPHA,
|
||||
font_data->texture = create_gpu_texture(TEXT_FONT_FILTERING, true);
|
||||
specify_gpu_texture(
|
||||
font_data->texture,
|
||||
bitmap,
|
||||
1,
|
||||
TEXT_FONT_TEXTURE_SIZE,
|
||||
TEXT_FONT_TEXTURE_SIZE,
|
||||
0,
|
||||
GL_ALPHA,
|
||||
GL_UNSIGNED_BYTE,
|
||||
bitmap
|
||||
TEXT_FONT_TEXTURE_SIZE
|
||||
);
|
||||
free(bitmap);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, TEXT_FONT_FILTERING);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, TEXT_FONT_FILTERING);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
|
||||
return font_data;
|
||||
}
|
||||
@ -92,25 +81,22 @@ static struct font_data *text_load_font_data(const char *path, int height_px) {
|
||||
|
||||
static void text_destroy_font_data(struct font_data *font_data) {
|
||||
free(font_data->file_bytes);
|
||||
glDeleteTextures(1, &font_data->texture);
|
||||
delete_gpu_texture(font_data->texture);
|
||||
free(font_data);
|
||||
}
|
||||
|
||||
|
||||
static void text_draw_with(struct font_data* font_data, char* text, t_fvec2 position, t_color color) {
|
||||
glBindTexture(GL_TEXTURE_2D, font_data->texture);
|
||||
static vertex_buffer vertex_array = 0;
|
||||
if (vertex_array == 0)
|
||||
vertex_array = create_vertex_buffer();
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glDepthFunc(GL_ALWAYS);
|
||||
glDepthMask(GL_FALSE);
|
||||
glDisable(GL_ALPHA_TEST);
|
||||
const size_t len = SDL_strlen(text);
|
||||
|
||||
glColor4ub(color.r, color.g, color.b, color.a);
|
||||
vertex_buffer_builder payload = build_vertex_buffer(vertex_array, get_text_payload_size() * len);
|
||||
|
||||
glBegin(GL_QUADS);
|
||||
for (const char *p = text; *p != '\0'; ++p) {
|
||||
const char c = *p;
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
const char c = text[i];
|
||||
|
||||
/* outside the range of what we want to display */
|
||||
//if (c < ASCII_START || c > ASCII_END)
|
||||
@ -136,20 +122,10 @@ static void text_draw_with(struct font_data* font_data, char* text, t_fvec2 posi
|
||||
quad.y0 += (float)font_data->ascent;
|
||||
quad.y1 += (float)font_data->ascent;
|
||||
|
||||
/* TODO: you know... */
|
||||
glTexCoord2f(quad.s0, quad.t0);
|
||||
glVertex2f(quad.x0, quad.y0);
|
||||
glTexCoord2f(quad.s1, quad.t0);
|
||||
glVertex2f(quad.x1, quad.y0);
|
||||
glTexCoord2f(quad.s1, quad.t1);
|
||||
glVertex2f(quad.x1, quad.y1);
|
||||
glTexCoord2f(quad.s0, quad.t1);
|
||||
glVertex2f(quad.x0, quad.y1);
|
||||
push_text_payload_to_vertex_buffer_builder(font_data, &payload, quad);
|
||||
}
|
||||
glEnd();
|
||||
|
||||
glColor4ub(255, 255, 255, 255);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
finally_draw_text(font_data, len, color, vertex_array);
|
||||
}
|
||||
|
||||
|
||||
@ -182,7 +158,7 @@ static struct font_data *get_font_data(const char *font_path, int height_px) {
|
||||
}
|
||||
|
||||
|
||||
static void render_text(const struct text_primitive *text) {
|
||||
void render_text(const struct text_primitive *text) {
|
||||
struct font_data *font_data = get_font_data(text->font, text->height_px);
|
||||
text_draw_with(font_data, text->text, text->position, text->color);
|
||||
}
|
||||
@ -243,6 +219,3 @@ int get_text_width(char *string, int height_px, const char *font_path) {
|
||||
|
||||
return (int)((float)length * font_data->scale_factor);
|
||||
}
|
||||
|
||||
|
||||
#endif
|
26
src/rendering/twn_text_c.h
Normal file
26
src/rendering/twn_text_c.h
Normal file
@ -0,0 +1,26 @@
|
||||
#include "twn_rendering_platform.h"
|
||||
|
||||
#include <stb_truetype.h>
|
||||
|
||||
|
||||
#define ASCII_START 32
|
||||
#define ASCII_END 128
|
||||
#define NUM_DISPLAY_ASCII ((ASCII_END - ASCII_START) + 1)
|
||||
|
||||
|
||||
struct font_data {
|
||||
stbtt_packedchar char_data[NUM_DISPLAY_ASCII];
|
||||
stbtt_fontinfo info;
|
||||
|
||||
const char *file_path;
|
||||
unsigned char *file_bytes;
|
||||
size_t file_bytes_len;
|
||||
|
||||
gpu_texture texture;
|
||||
|
||||
int height_px;
|
||||
float scale_factor;
|
||||
int ascent;
|
||||
int descent;
|
||||
int line_gap;
|
||||
};
|
81
src/rendering/twn_triangles.c
Normal file
81
src/rendering/twn_triangles.c
Normal file
@ -0,0 +1,81 @@
|
||||
#include "twn_rendering_c.h"
|
||||
#include "twn_context.h"
|
||||
#include "twn_textures_c.h"
|
||||
#include "twn_rendering_platform.h"
|
||||
|
||||
#include <stb_ds.h>
|
||||
|
||||
|
||||
/* TODO: automatic handling of repeating textures */
|
||||
/* for that we could allocate a loner texture */
|
||||
void unfurl_triangle(const char *path,
|
||||
t_fvec3 v0,
|
||||
t_fvec3 v1,
|
||||
t_fvec3 v2,
|
||||
t_shvec2 uv0,
|
||||
t_shvec2 uv1,
|
||||
t_shvec2 uv2)
|
||||
{
|
||||
const t_texture_key texture_key = textures_get_key(&ctx.texture_cache, path);
|
||||
|
||||
struct mesh_batch_item *batch_p = hmgetp_null(ctx.uncolored_mesh_batches, texture_key);
|
||||
if (!batch_p) {
|
||||
struct mesh_batch item = {0};
|
||||
hmput(ctx.uncolored_mesh_batches, texture_key, item);
|
||||
batch_p = &ctx.uncolored_mesh_batches[hmlenu(ctx.uncolored_mesh_batches) - 1]; /* TODO: can last index be used? */
|
||||
}
|
||||
|
||||
union uncolored_space_triangle triangle = { .primitive = {
|
||||
.v0 = v0,
|
||||
.v1 = v1,
|
||||
.v2 = v2,
|
||||
.uv1 = m_to_fvec2(uv1),
|
||||
.uv0 = m_to_fvec2(uv0),
|
||||
.uv2 = m_to_fvec2(uv2),
|
||||
}};
|
||||
|
||||
union uncolored_space_triangle *triangles = (union uncolored_space_triangle *)batch_p->value.primitives;
|
||||
|
||||
arrpush(triangles, triangle);
|
||||
batch_p->value.primitives = (uint8_t *)triangles;
|
||||
}
|
||||
|
||||
|
||||
void draw_uncolored_space_traingle_batch(struct mesh_batch *batch,
|
||||
t_texture_key texture_key)
|
||||
{
|
||||
static vertex_buffer vertex_array = 0;
|
||||
if (vertex_array == 0)
|
||||
vertex_array = create_vertex_buffer();
|
||||
|
||||
const size_t primitives_len = arrlenu(batch->primitives);
|
||||
|
||||
/* nothing to do */
|
||||
if (primitives_len == 0)
|
||||
return;
|
||||
|
||||
const t_frect srcrect = textures_get_srcrect(&ctx.texture_cache, texture_key);
|
||||
const t_frect dims = textures_get_dims(&ctx.texture_cache, texture_key);
|
||||
|
||||
const float wr = srcrect.w / dims.w;
|
||||
const float hr = srcrect.h / dims.h;
|
||||
const float xr = srcrect.x / dims.w;
|
||||
const float yr = srcrect.y / dims.h;
|
||||
|
||||
/* update pixel-based uvs to correspond with texture atlases */
|
||||
for (size_t i = 0; i < primitives_len; ++i) {
|
||||
struct uncolored_space_triangle_payload *payload =
|
||||
&((union uncolored_space_triangle *)batch->primitives)[i].payload;
|
||||
|
||||
payload->uv0.x = xr + ((float)payload->uv0.x / srcrect.w) * wr;
|
||||
payload->uv0.y = yr + ((float)payload->uv0.y / srcrect.h) * hr;
|
||||
payload->uv1.x = xr + ((float)payload->uv1.x / srcrect.w) * wr;
|
||||
payload->uv1.y = yr + ((float)payload->uv1.y / srcrect.h) * hr;
|
||||
payload->uv2.x = xr + ((float)payload->uv2.x / srcrect.w) * wr;
|
||||
payload->uv2.y = yr + ((float)payload->uv2.y / srcrect.h) * hr;
|
||||
}
|
||||
|
||||
specify_vertex_buffer(vertex_array, batch->primitives, primitives_len * sizeof (struct uncolored_space_triangle_payload));
|
||||
|
||||
finally_draw_uncolored_space_traingle_batch(batch, texture_key, vertex_array);
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
#ifndef ELF_H
|
||||
#define ELF_H
|
||||
#ifndef TWN_ELF_H
|
||||
#define TWN_ELF_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
@ -1,7 +1,7 @@
|
||||
#ifndef PRIVATE_AUDIO_H
|
||||
#define PRIVATE_AUDIO_H
|
||||
#ifndef TWN_AUDIO_C_H
|
||||
#define TWN_AUDIO_C_H
|
||||
|
||||
#include "../audio.h"
|
||||
#include "twn_audio.h"
|
||||
|
||||
#include <SDL2/SDL_audio.h>
|
||||
|
@ -1,5 +1,5 @@
|
||||
#include "camera.h"
|
||||
#include "townengine/context.h"
|
||||
#include "twn_camera.h"
|
||||
#include "twn_context.h"
|
||||
|
||||
#include <math.h>
|
||||
|
@ -1,5 +1,5 @@
|
||||
#ifndef CONCATENATE_H
|
||||
#define CONCATENATE_H
|
||||
#ifndef TWN_CONCATENATE_H
|
||||
#define TWN_CONCATENATE_H
|
||||
|
||||
#define m_concatenate(p_a, p_b) m_concatenate_(p_a, p_b)
|
||||
#define m_concatenate_(p_a, p_b) m_concatenate__(p_a, p_b)
|
3
src/twn_context.c
Normal file
3
src/twn_context.c
Normal file
@ -0,0 +1,3 @@
|
||||
#include "twn_engine_context_c.h"
|
||||
|
||||
t_engine_ctx ctx = {0};
|
@ -1,11 +1,9 @@
|
||||
#ifndef CONTEXT_H
|
||||
#define CONTEXT_H
|
||||
#ifndef TWN_ENGINE_CONTEXT_H
|
||||
#define TWN_ENGINE_CONTEXT_H
|
||||
|
||||
|
||||
#include "rendering/internal_api.h"
|
||||
#include "twn_context.h"
|
||||
#include "textures/internal_api.h"
|
||||
#include "input.h"
|
||||
#include "twn_engine_api.h"
|
||||
#include "twn_input.h"
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
@ -13,17 +11,17 @@
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
typedef struct context {
|
||||
typedef struct engine_context {
|
||||
t_ctx game_context;
|
||||
|
||||
/* the program's actual argc and argv */
|
||||
int argc;
|
||||
char **argv;
|
||||
|
||||
struct texture_cache texture_cache;
|
||||
struct input_state input;
|
||||
|
||||
struct primitive_2d *render_queue_2d;
|
||||
struct mesh_batch_item *uncolored_mesh_batches;
|
||||
struct text_cache text_cache;
|
||||
struct texture_cache texture_cache;
|
||||
|
||||
struct audio_channel_item *audio_channels;
|
||||
SDL_AudioDeviceID audio_device;
|
||||
@ -38,34 +36,15 @@ typedef struct context {
|
||||
int64_t frame_accumulator;
|
||||
int64_t delta_averager_residual;
|
||||
int64_t time_averager[4];
|
||||
int64_t delta_time; /* preserves real time frame delta with no manipilation */
|
||||
uint64_t tick_count;
|
||||
|
||||
/* set just once on startup */
|
||||
uint64_t random_seed;
|
||||
|
||||
/* this should be a multiple of TICKS_PER_SECOND */
|
||||
/* use it to simulate low framerate (e.g. at 60 tps, set to 2 for 30 fps) */
|
||||
/* it can be changed at runtime; any resulting logic anomalies are bugs */
|
||||
unsigned int update_multiplicity;
|
||||
|
||||
SDL_GLContext *gl_context;
|
||||
SDL_Window *window;
|
||||
uint32_t window_id;
|
||||
int window_w;
|
||||
int window_h;
|
||||
|
||||
/* you may read from and write to these from game code */
|
||||
void *udata;
|
||||
|
||||
bool debug;
|
||||
bool is_running;
|
||||
bool resync_flag;
|
||||
bool was_successful;
|
||||
bool window_size_has_changed;
|
||||
bool initialization_needed;
|
||||
} t_ctx;
|
||||
} t_engine_ctx;
|
||||
|
||||
TWN_API extern t_ctx ctx;
|
||||
extern t_engine_ctx ctx = ;
|
||||
|
||||
#endif
|
@ -1,5 +1,5 @@
|
||||
#ifndef GAME_OBJECT_H
|
||||
#define GAME_OBJECT_H
|
||||
#ifndef TWN_GAME_OBJECT_H
|
||||
#define TWN_GAME_OBJECT_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
@ -9,10 +9,14 @@
|
||||
#include "townengine/rendering/internal_api.h"
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
#include <SDL2/SDL_image.h>
|
||||
#include <physfs.h>
|
||||
#include <stb_ds.h>
|
||||
|
||||
#ifdef EMSCRIPTEN
|
||||
#include <GLES2/gl2.h>
|
||||
#else
|
||||
#include <glad/glad.h>
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
@ -54,6 +58,8 @@ static void poll_events(void) {
|
||||
}
|
||||
|
||||
|
||||
#ifndef EMSCRIPTEN
|
||||
|
||||
static void APIENTRY opengl_log(GLenum source,
|
||||
GLenum type,
|
||||
GLuint id,
|
||||
@ -71,6 +77,8 @@ static void APIENTRY opengl_log(GLenum source,
|
||||
log_info("OpenGL: %.*s\n", length, message);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
static void main_loop(void) {
|
||||
/*
|
||||
@ -185,7 +193,7 @@ static void main_loop(void) {
|
||||
|
||||
|
||||
static bool initialize(void) {
|
||||
if (SDL_Init(SDL_INIT_EVERYTHING) == -1) {
|
||||
if (SDL_Init(SDL_INIT_EVERYTHING & ~SDL_INIT_HAPTIC) == -1) {
|
||||
CRY_SDL("SDL initialization failed.");
|
||||
return false;
|
||||
}
|
||||
@ -199,8 +207,20 @@ static bool initialize(void) {
|
||||
ctx.debug = false;
|
||||
#endif
|
||||
|
||||
#ifdef EMSCRIPTEN
|
||||
/* emscripten interpretes those as GL ES version against WebGL */
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
|
||||
#else
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 1);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 5);
|
||||
|
||||
if (ctx.debug)
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG);
|
||||
else
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_NO_ERROR);
|
||||
#endif
|
||||
|
||||
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
|
||||
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 6);
|
||||
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
|
||||
@ -209,11 +229,6 @@ static bool initialize(void) {
|
||||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
||||
SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
|
||||
|
||||
if (ctx.debug)
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG);
|
||||
else
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_NO_ERROR);
|
||||
|
||||
/* init got far enough to create a window */
|
||||
ctx.window = SDL_CreateWindow("townengine",
|
||||
SDL_WINDOWPOS_CENTERED,
|
||||
@ -242,16 +257,20 @@ static bool initialize(void) {
|
||||
if (SDL_GL_SetSwapInterval(-1))
|
||||
SDL_GL_SetSwapInterval(1);
|
||||
|
||||
#ifndef EMSCRIPTEN
|
||||
if (gladLoadGL() == 0) {
|
||||
CRY("Init", "GLAD failed");
|
||||
goto fail;
|
||||
}
|
||||
#endif
|
||||
|
||||
log_info("OpenGL context: %s\n", glGetString(GL_VERSION));
|
||||
|
||||
#ifndef EMSCRIPTEN
|
||||
glHint(GL_TEXTURE_COMPRESSION_HINT, GL_NICEST);
|
||||
glHint(GL_GENERATE_MIPMAP_HINT, GL_NICEST);
|
||||
glHint(GL_FOG_HINT, GL_FASTEST);
|
||||
#endif
|
||||
|
||||
/* might need this to have multiple windows */
|
||||
ctx.window_id = SDL_GetWindowID(ctx.window);
|
||||
@ -295,11 +314,13 @@ static bool initialize(void) {
|
||||
/* you could change this at runtime if you wanted */
|
||||
ctx.update_multiplicity = 1;
|
||||
|
||||
#ifndef EMSCRIPTEN
|
||||
/* hook up opengl debugging callback */
|
||||
if (ctx.debug) {
|
||||
glEnable(GL_DEBUG_OUTPUT);
|
||||
glDebugMessageCallback(opengl_log, NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* random seeding */
|
||||
/* SDL_GetPerformanceCounter returns some platform-dependent number. */
|
@ -1,8 +1,8 @@
|
||||
#ifndef OPTION_H
|
||||
#define OPTION_H
|
||||
#ifndef TWN_OPTION_H
|
||||
#define TWN_OPTION_H
|
||||
|
||||
#include "concatenate.h"
|
||||
#include "varargcount.h"
|
||||
#include "twn_concatenate_c.h"
|
||||
#include "twn_varargcount_c.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
|
@ -1,5 +1,5 @@
|
||||
#ifndef TEXTURES_MODES_H
|
||||
#define TEXTURES_MODES_H
|
||||
#ifndef TWN_TEXTURES_MODES_H
|
||||
#define TWN_TEXTURES_MODES_H
|
||||
|
||||
/* alpha channel information */
|
||||
enum texture_mode {
|
@ -81,24 +81,6 @@ ERR_CANNOT_OPEN_FILE:
|
||||
}
|
||||
|
||||
|
||||
static GLuint new_gl_texture(void) {
|
||||
GLuint texture;
|
||||
glGenTextures(1, &texture);
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
|
||||
|
||||
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
return texture;
|
||||
}
|
||||
|
||||
|
||||
static SDL_Surface *create_surface(int width, int height) {
|
||||
Uint32 rmask, gmask, bmask, amask;
|
||||
|
||||
@ -134,28 +116,16 @@ static SDL_Surface *create_surface(int width, int height) {
|
||||
static void add_new_atlas(struct texture_cache *cache) {
|
||||
SDL_Surface *new_atlas = create_surface(TEXTURE_ATLAS_SIZE, TEXTURE_ATLAS_SIZE);
|
||||
arrput(cache->atlas_surfaces, new_atlas);
|
||||
arrput(cache->atlas_textures, new_gl_texture());
|
||||
arrput(cache->atlas_textures, create_gpu_texture(TEXTURE_FILTER_NEAREAST, true));
|
||||
}
|
||||
|
||||
|
||||
static void upload_texture_from_surface(GLuint texture, SDL_Surface *surface) {
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
|
||||
static void upload_texture_from_surface(gpu_texture texture, SDL_Surface *surface) {
|
||||
SDL_LockSurface(surface);
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D,
|
||||
0,
|
||||
GL_RGBA8,
|
||||
surface->w,
|
||||
surface->h,
|
||||
0,
|
||||
GL_RGBA,
|
||||
GL_UNSIGNED_BYTE,
|
||||
surface->pixels);
|
||||
specify_gpu_texture(texture, surface->pixels, surface->w, surface->h);
|
||||
|
||||
SDL_UnlockSurface(surface);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
|
||||
@ -285,7 +255,7 @@ void textures_cache_init(struct texture_cache *cache, SDL_Window *window) {
|
||||
void textures_cache_deinit(struct texture_cache *cache) {
|
||||
/* free atlas textures */
|
||||
for (size_t i = 0; i < arrlenu(cache->atlas_textures); ++i) {
|
||||
glDeleteTextures(1, &cache->atlas_textures[i]);
|
||||
delete_gpu_texture(cache->atlas_textures[i]);
|
||||
}
|
||||
arrfree(cache->atlas_textures);
|
||||
|
||||
@ -373,7 +343,7 @@ static t_texture_key textures_load(struct texture_cache *cache, const char *path
|
||||
|
||||
/* it's a "loner texture," it doesn't fit in an atlas so it's not in one */
|
||||
if (surface->w >= TEXTURE_ATLAS_SIZE || surface->h >= TEXTURE_ATLAS_SIZE) {
|
||||
new_texture.loner_texture = new_gl_texture();
|
||||
new_texture.loner_texture = create_gpu_texture(TEXTURE_FILTER_NEAREAST, true);
|
||||
upload_texture_from_surface(new_texture.loner_texture, surface);
|
||||
new_texture.srcrect = (t_frect) { .w = (float)surface->w, .h = (float)surface->h };
|
||||
shput(cache->hash, path, new_texture);
|
||||
@ -528,12 +498,12 @@ t_frect textures_get_dims(const struct texture_cache *cache, t_texture_key key)
|
||||
}
|
||||
|
||||
|
||||
void textures_bind(const struct texture_cache *cache, t_texture_key key, GLenum target) {
|
||||
void textures_bind(const struct texture_cache *cache, t_texture_key key) {
|
||||
if (m_texture_key_is_valid(key)) {
|
||||
if (cache->hash[key.id].value.loner_texture == 0)
|
||||
glBindTexture(target, cache->atlas_textures[cache->hash[key.id].value.atlas_index]);
|
||||
bind_gpu_texture(cache->atlas_textures[cache->hash[key.id].value.atlas_index]);
|
||||
else
|
||||
glBindTexture(target, cache->hash[key.id].value.loner_texture);
|
||||
bind_gpu_texture(cache->hash[key.id].value.loner_texture);
|
||||
} else if (key.id == 0) {
|
||||
CRY("Texture binding failed.",
|
||||
"Tried to get texture that isn't loaded.");
|
||||
@ -542,39 +512,33 @@ void textures_bind(const struct texture_cache *cache, t_texture_key key, GLenum
|
||||
|
||||
|
||||
/* TODO: alternative schemes, such as: array texture, fragment shader and geometry division */
|
||||
void textures_bind_repeating(const struct texture_cache *cache, t_texture_key key, GLenum target) {
|
||||
void textures_bind_repeating(const struct texture_cache *cache, t_texture_key key) {
|
||||
if (m_texture_key_is_valid(key)) {
|
||||
if (cache->hash[key.id].value.loner_texture == 0) {
|
||||
|
||||
/* already allocated */
|
||||
if (cache->hash[key.id].value.repeating_texture != 0) {
|
||||
glBindTexture(target, cache->hash[key.id].value.repeating_texture);
|
||||
bind_gpu_texture(cache->hash[key.id].value.repeating_texture);
|
||||
return;
|
||||
}
|
||||
|
||||
const struct texture texture = cache->hash[key.id].value;
|
||||
|
||||
const GLuint repeating_texture = new_gl_texture();
|
||||
glBindTexture(target, repeating_texture);
|
||||
const gpu_texture repeating_texture = create_gpu_texture(TEXTURE_FILTER_NEAREAST, false);
|
||||
|
||||
SDL_LockSurface(texture.data);
|
||||
|
||||
glTexImage2D(target,
|
||||
0,
|
||||
GL_RGBA8,
|
||||
texture.data->w,
|
||||
texture.data->h,
|
||||
0,
|
||||
GL_RGBA,
|
||||
GL_UNSIGNED_BYTE,
|
||||
texture.data->pixels);
|
||||
specify_gpu_texture(repeating_texture,
|
||||
texture.data->pixels,
|
||||
texture.data->w,
|
||||
texture.data->h);
|
||||
|
||||
SDL_UnlockSurface(texture.data);
|
||||
|
||||
cache->hash[key.id].value.repeating_texture = repeating_texture;
|
||||
|
||||
} else
|
||||
glBindTexture(target, cache->hash[key.id].value.loner_texture);
|
||||
bind_gpu_texture(cache->hash[key.id].value.loner_texture);
|
||||
|
||||
} else if (key.id == 0) {
|
||||
CRY("Texture binding failed.",
|
@ -1,13 +1,13 @@
|
||||
#ifndef TEXTURES_INTERNAL_API_H
|
||||
#define TEXTURES_INTERNAL_API_H
|
||||
#ifndef TWN_TEXTURES_H
|
||||
#define TWN_TEXTURES_H
|
||||
|
||||
#include "../util.h"
|
||||
#include "../textures/modes.h"
|
||||
#include "../twn_engine_api.h"
|
||||
#include "twn_util.h"
|
||||
#include "twn_texture_modes.h"
|
||||
#include "twn_engine_api.h"
|
||||
#include "twn_gpu_texture.h"
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
#include <stb_rect_pack.h>
|
||||
#include <glad/glad.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
@ -15,8 +15,8 @@ struct texture {
|
||||
t_frect srcrect; /* position in atlas */
|
||||
SDL_Surface *data; /* original image data */
|
||||
int atlas_index;
|
||||
GLuint loner_texture; /* stored directly for loners, == 0 means atlas_index should be used */
|
||||
GLuint repeating_texture; /* separately allocated texture, for loners == loner_texture */
|
||||
gpu_texture loner_texture; /* stored directly for loners, == 0 means atlas_index should be used */
|
||||
gpu_texture repeating_texture; /* separately allocated texture, for loners == loner_texture */
|
||||
enum texture_mode mode;
|
||||
};
|
||||
|
||||
@ -35,7 +35,7 @@ struct texture_cache {
|
||||
stbrp_node *node_buffer; /* used internally by stb_rect_pack */
|
||||
|
||||
SDL_Surface **atlas_surfaces;
|
||||
GLuint *atlas_textures; /* shared by atlas textures */
|
||||
gpu_texture *atlas_textures; /* shared by atlas textures */
|
||||
int atlas_index; /* atlas that is currently being built */
|
||||
|
||||
bool is_dirty; /* current atlas needs to be recreated */
|
||||
@ -47,11 +47,11 @@ typedef struct { uint16_t id; } t_texture_key;
|
||||
/* tests whether given key structure corresponds to any texture */
|
||||
#define m_texture_key_is_valid(p_key) ((p_key).id != (uint16_t)-1)
|
||||
|
||||
TWN_API void textures_cache_init(struct texture_cache *cache, SDL_Window *window);
|
||||
TWN_API void textures_cache_deinit(struct texture_cache *cache);
|
||||
void textures_cache_init(struct texture_cache *cache, SDL_Window *window);
|
||||
void textures_cache_deinit(struct texture_cache *cache);
|
||||
|
||||
/* for debugging */
|
||||
TWN_API void textures_dump_atlases(struct texture_cache *cache);
|
||||
void textures_dump_atlases(struct texture_cache *cache);
|
||||
|
||||
/* loads an image if it isn't in the cache, otherwise a no-op. */
|
||||
/* can be called from anywhere at any time after init, useful if you want to */
|
||||
@ -61,34 +61,32 @@ TWN_API void textures_dump_atlases(struct texture_cache *cache);
|
||||
/* repacks the current texture atlas based on the texture cache if needed */
|
||||
/* any previously returned srcrect results are invalidated after that */
|
||||
/* call it every time before rendering */
|
||||
TWN_API void textures_update_atlas(struct texture_cache *cache);
|
||||
void textures_update_atlas(struct texture_cache *cache);
|
||||
|
||||
/* returns a persistent handle to some texture in cache, loading it if needed */
|
||||
/* check the result with m_texture_key_is_valid() */
|
||||
TWN_API t_texture_key textures_get_key(struct texture_cache *cache, const char *path);
|
||||
t_texture_key textures_get_key(struct texture_cache *cache, const char *path);
|
||||
|
||||
/* returns a rect in a texture cache of the given key */
|
||||
TWN_API t_frect textures_get_srcrect(const struct texture_cache *cache, t_texture_key key);
|
||||
t_frect textures_get_srcrect(const struct texture_cache *cache, t_texture_key key);
|
||||
|
||||
/* returns a rect of dimensions of the whole texture (whole atlas) */
|
||||
TWN_API t_frect textures_get_dims(const struct texture_cache *cache, t_texture_key key);
|
||||
t_frect textures_get_dims(const struct texture_cache *cache, t_texture_key key);
|
||||
|
||||
/* returns an identifier that is equal for all textures placed in the same atlas */
|
||||
TWN_API int32_t textures_get_atlas_id(const struct texture_cache *cache, t_texture_key key);
|
||||
int32_t textures_get_atlas_id(const struct texture_cache *cache, t_texture_key key);
|
||||
|
||||
/* binds atlas texture in opengl state */
|
||||
TWN_API void textures_bind(const struct texture_cache *cache, t_texture_key key, GLenum target);
|
||||
void textures_bind(const struct texture_cache *cache, t_texture_key key);
|
||||
|
||||
/* binds texture in opengl state, ensuring that it's usable with texture repeat */
|
||||
TWN_API void textures_bind_repeating(const struct texture_cache *cache, t_texture_key key, GLenum target);
|
||||
void textures_bind_repeating(const struct texture_cache *cache, t_texture_key key);
|
||||
|
||||
/* returns helpful information about contents of alpha channel in given texture */
|
||||
TWN_API enum texture_mode textures_get_mode(const struct texture_cache *cache, t_texture_key key);
|
||||
enum texture_mode textures_get_mode(const struct texture_cache *cache, t_texture_key key);
|
||||
|
||||
/* returns the number of atlases in the cache */
|
||||
TWN_API size_t textures_get_num_atlases(const struct texture_cache *cache);
|
||||
size_t textures_get_num_atlases(const struct texture_cache *cache);
|
||||
|
||||
/* TODO: should recieve texture_cache, get_key optimization cache should be cleared some other way */
|
||||
TWN_API void textures_reset_state(void);
|
||||
void textures_reset_state(void);
|
||||
|
||||
#endif
|
@ -1,5 +1,5 @@
|
||||
#include "util.h"
|
||||
#include "townengine/context.h"
|
||||
#include "twn_util.h"
|
||||
#include "twn_context.h"
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
#include <physfsrwops.h>
|
@ -1,5 +1,5 @@
|
||||
#ifndef VARARGCOUNT_H
|
||||
#define VARARGCOUNT_H
|
||||
#ifndef TWN_VARARGCOUNT_H
|
||||
#define TWN_VARARGCOUNT_H
|
||||
|
||||
#define m_narg(...) m_narg_(__VA_ARGS__, m_rseq_n_())
|
||||
#define m_narg_(...) m_arg_n_(__VA_ARGS__)
|
@ -1,3 +0,0 @@
|
||||
#include "townengine/context.h"
|
||||
|
||||
t_ctx ctx = {0};
|
@ -1,118 +0,0 @@
|
||||
/* a rendering.c mixin */
|
||||
#ifndef TRIANGLES_H
|
||||
#define TRIANGLES_H
|
||||
|
||||
#include "townengine/context.h"
|
||||
#include "internal_api.h"
|
||||
#include "../textures/internal_api.h"
|
||||
|
||||
#include <stb_ds.h>
|
||||
|
||||
/* TODO: automatic handling of repeating textures */
|
||||
/* for that we could allocate a loner texture */
|
||||
void unfurl_triangle(const char *path,
|
||||
t_fvec3 v0,
|
||||
t_fvec3 v1,
|
||||
t_fvec3 v2,
|
||||
t_shvec2 uv0,
|
||||
t_shvec2 uv1,
|
||||
t_shvec2 uv2)
|
||||
{
|
||||
const t_texture_key texture_key = textures_get_key(&ctx.texture_cache, path);
|
||||
|
||||
struct mesh_batch_item *batch_p = hmgetp_null(ctx.uncolored_mesh_batches, texture_key);
|
||||
if (!batch_p) {
|
||||
struct mesh_batch item = {0};
|
||||
hmput(ctx.uncolored_mesh_batches, texture_key, item);
|
||||
batch_p = &ctx.uncolored_mesh_batches[hmlenu(ctx.uncolored_mesh_batches) - 1]; /* TODO: can last index be used? */
|
||||
}
|
||||
|
||||
union uncolored_space_triangle triangle = { .primitive = {
|
||||
.v0 = v0,
|
||||
.v1 = v1,
|
||||
.v2 = v2,
|
||||
.uv1 = m_to_fvec2(uv1),
|
||||
.uv0 = m_to_fvec2(uv0),
|
||||
.uv2 = m_to_fvec2(uv2),
|
||||
}};
|
||||
|
||||
union uncolored_space_triangle *triangles =
|
||||
(union uncolored_space_triangle *)batch_p->value.primitives;
|
||||
arrpush(triangles, triangle);
|
||||
batch_p->value.primitives = (uint8_t *)triangles;
|
||||
}
|
||||
|
||||
|
||||
static void draw_uncolored_space_traingle_batch(struct mesh_batch *batch,
|
||||
t_texture_key texture_key)
|
||||
{
|
||||
size_t primitives_len = arrlenu(batch->primitives);
|
||||
|
||||
if (primitives_len == 0)
|
||||
return;
|
||||
|
||||
/* create vertex array object */
|
||||
if (batch->buffer == 0)
|
||||
glGenBuffers(1, &batch->buffer);
|
||||
|
||||
/* TODO: try using mapped buffers while building batches instead? */
|
||||
/* this way we could skip client side copy that is kept until commitment */
|
||||
/* alternatively we could commit glBufferSubData based on a threshold */
|
||||
|
||||
/* update pixel-based uvs to correspond with texture atlases */
|
||||
for (size_t i = 0; i < primitives_len; ++i) {
|
||||
struct uncolored_space_triangle_payload *payload =
|
||||
&((union uncolored_space_triangle *)batch->primitives)[i].payload;
|
||||
|
||||
const t_frect srcrect = textures_get_srcrect(&ctx.texture_cache, texture_key);
|
||||
const t_frect dims = textures_get_dims(&ctx.texture_cache, texture_key);
|
||||
|
||||
const float wr = srcrect.w / dims.w;
|
||||
const float hr = srcrect.h / dims.h;
|
||||
const float xr = srcrect.x / dims.w;
|
||||
const float yr = srcrect.y / dims.h;
|
||||
|
||||
payload->uv0.x = xr + ((float)payload->uv0.x / srcrect.w) * wr;
|
||||
payload->uv0.y = yr + ((float)payload->uv0.y / srcrect.h) * hr;
|
||||
payload->uv1.x = xr + ((float)payload->uv1.x / srcrect.w) * wr;
|
||||
payload->uv1.y = yr + ((float)payload->uv1.y / srcrect.h) * hr;
|
||||
payload->uv2.x = xr + ((float)payload->uv2.x / srcrect.w) * wr;
|
||||
payload->uv2.y = yr + ((float)payload->uv2.y / srcrect.h) * hr;
|
||||
}
|
||||
|
||||
textures_bind(&ctx.texture_cache, texture_key, GL_TEXTURE_2D);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, batch->buffer);
|
||||
|
||||
/* upload batched data */
|
||||
glBufferData(GL_ARRAY_BUFFER,
|
||||
primitives_len * sizeof (struct uncolored_space_triangle_payload),
|
||||
batch->primitives,
|
||||
GL_STREAM_DRAW);
|
||||
|
||||
/* vertex specification*/
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glVertexPointer(3,
|
||||
GL_FLOAT,
|
||||
offsetof(struct uncolored_space_triangle_payload, v1),
|
||||
(void *)offsetof(struct uncolored_space_triangle_payload, v0));
|
||||
|
||||
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
glClientActiveTexture(GL_TEXTURE0);
|
||||
glTexCoordPointer(2,
|
||||
GL_FLOAT,
|
||||
offsetof(struct uncolored_space_triangle_payload, v1),
|
||||
(void *)offsetof(struct uncolored_space_triangle_payload, uv0));
|
||||
|
||||
/* commit for drawing */
|
||||
glDrawArrays(GL_TRIANGLES, 0, 3 * (GLint)primitives_len);
|
||||
|
||||
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
|
||||
/* invalidate the buffer immediately */
|
||||
glBufferData(GL_ARRAY_BUFFER, 0, NULL, GL_STREAM_DRAW);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
}
|
||||
|
||||
#endif
|
@ -1,139 +0,0 @@
|
||||
#include "scripting.h"
|
||||
#include "util.h"
|
||||
#include "townengine/context.h"
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
#include <umka_api.h>
|
||||
#include <physfs.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
static void msgbox(UmkaStackSlot *params, UmkaStackSlot *result) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION,
|
||||
params[1].ptrVal, params[0].ptrVal, NULL);
|
||||
}
|
||||
|
||||
static void umka_log_info(UmkaStackSlot *params, UmkaStackSlot *result) {
|
||||
log_info(params[0].ptrVal);
|
||||
}
|
||||
|
||||
static void umka_log_critical(UmkaStackSlot *params, UmkaStackSlot *result) {
|
||||
log_critical(params[0].ptrVal);
|
||||
}
|
||||
|
||||
static void umka_log_warn(UmkaStackSlot *params, UmkaStackSlot *result) {
|
||||
log_warn(params[0].ptrVal);
|
||||
}
|
||||
|
||||
static void is_action_pressed(UmkaStackSlot *params, UmkaStackSlot *result) {
|
||||
struct state *state = params[1].ptrVal;
|
||||
t_ctx *ctx = state->hidden_ptr;
|
||||
|
||||
bool value = input_is_action_pressed(&ctx->input, params[0].ptrVal);
|
||||
result->uintVal = value;
|
||||
}
|
||||
|
||||
static void is_action_just_pressed(UmkaStackSlot *params, UmkaStackSlot *result) {
|
||||
struct state *state = params[1].ptrVal;
|
||||
t_ctx *ctx = state->hidden_ptr;
|
||||
|
||||
bool value = input_is_action_just_pressed(&ctx->input, params[0].ptrVal);
|
||||
result->uintVal = value;
|
||||
}
|
||||
|
||||
static void is_action_just_released(UmkaStackSlot *params, UmkaStackSlot *result) {
|
||||
struct state *state = params[1].ptrVal;
|
||||
t_ctx *ctx = state->hidden_ptr;
|
||||
|
||||
bool value = input_is_action_just_released(&ctx->input, params[0].ptrVal);
|
||||
result->uintVal = value;
|
||||
}
|
||||
|
||||
static void get_action_position(UmkaStackSlot *params, UmkaStackSlot *result) {
|
||||
struct state *state = params[2].ptrVal;
|
||||
t_ctx *ctx = state->hidden_ptr;
|
||||
|
||||
t_fvec2 *position = params[0].ptrVal;
|
||||
*position = input_get_action_position(&ctx->input, params[1].ptrVal);
|
||||
|
||||
// the result is in a hidden result pointer allocated by Umka
|
||||
result->ptrVal = params[0].ptrVal;
|
||||
}
|
||||
|
||||
static void register_api(void *umka) {
|
||||
umkaAddFunc(umka, "msgbox", msgbox);
|
||||
umkaAddFunc(umka, "logInfo", umka_log_info);
|
||||
umkaAddFunc(umka, "logCritical", umka_log_critical);
|
||||
umkaAddFunc(umka, "logWarn", umka_log_warn);
|
||||
umkaAddFunc(umka, "cImplIsActionPressed", is_action_pressed);
|
||||
umkaAddFunc(umka, "cImplIsActionJustPressed", is_action_just_pressed);
|
||||
umkaAddFunc(umka, "cImplIsActionJustReleased", is_action_just_released);
|
||||
umkaAddFunc(umka, "getActionPosition", get_action_position);
|
||||
}
|
||||
|
||||
bool scripting_init(t_ctx *ctx) {
|
||||
if (!PHYSFS_exists("/scripts/main.um")) {
|
||||
CRY("Failed to initialize scripting", "Could not find a main.um (we need it)");
|
||||
return false;
|
||||
}
|
||||
|
||||
ctx->umka = umkaAlloc();
|
||||
|
||||
char *main_script = file_to_str("/scripts/main.um");
|
||||
bool umka_ok = umkaInit(ctx->umka,
|
||||
"main.um",
|
||||
main_script,
|
||||
UMKA_STACK_SIZE,
|
||||
NULL,
|
||||
ctx->argc,
|
||||
ctx->argv,
|
||||
false,
|
||||
false,
|
||||
NULL);
|
||||
free(main_script);
|
||||
|
||||
if (!umka_ok) {
|
||||
CRY("Failed to initialize scripting", "Unknown Umka error");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* all Umka files are compiled even if they're never used */
|
||||
char **dir_file_names = PHYSFS_enumerateFiles("/scripts");
|
||||
for (char **i = dir_file_names; *i != NULL; ++i) {
|
||||
char *file_name = *i;
|
||||
|
||||
if (!strends(file_name, ".um"))
|
||||
continue;
|
||||
|
||||
/* got this one already */
|
||||
if (strcmp(file_name, "main.um") == 0)
|
||||
continue;
|
||||
|
||||
/* need to figure out the actual path (as opposed to the lone file name) */
|
||||
const char *path_prefix = "/scripts/";
|
||||
size_t path_size = snprintf(NULL, 0, "%s%s", path_prefix, file_name) + 1;
|
||||
char *path = cmalloc(path_size);
|
||||
snprintf(path, path_size, "%s%s", path_prefix, file_name);
|
||||
|
||||
char *contents = file_to_str(path);
|
||||
umkaAddModule(ctx->umka, file_name, contents);
|
||||
|
||||
free(path);
|
||||
free(contents);
|
||||
}
|
||||
PHYSFS_freeList(dir_file_names);
|
||||
|
||||
register_api(ctx->umka);
|
||||
if (!umkaCompile(ctx->umka)) {
|
||||
cry_umka(ctx->umka);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void scripting_deinit(t_ctx *ctx) {
|
||||
umkaFree(ctx->umka);
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
#ifndef SCRIPTING_H
|
||||
#define SCRIPTING_H
|
||||
|
||||
#include <umka_api.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef struct context t_ctx;
|
||||
|
||||
struct state {
|
||||
t_ctx *hidden_ptr;
|
||||
uint64_t tick_count;
|
||||
};
|
||||
|
||||
bool scripting_init(void);
|
||||
|
||||
#endif
|
8
untitled.sublime-project
Normal file
8
untitled.sublime-project
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"folders":
|
||||
[
|
||||
{
|
||||
"path": "."
|
||||
}
|
||||
]
|
||||
}
|
1609
untitled.sublime-workspace
Normal file
1609
untitled.sublime-workspace
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user