This commit is contained in:
Dominic Szablewski
2023-08-10 14:10:51 +02:00
commit 7cfaff0c7b
67 changed files with 33289 additions and 0 deletions
+9
View File
@@ -0,0 +1,9 @@
/wipeout.sublime-project
/wipeout.sublime-workspace
/wipeout/
/build/
/wipegame
/save.dat
.DS_STORE
+166
View File
@@ -0,0 +1,166 @@
CC ?= gcc
EMCC ?= emcc
UNAME_S := $(shell uname -s)
RENDERER ?= GL
DEBUG ?= false
L_FLAGS ?= -lm -rdynamic
C_FLAGS ?= -std=gnu99 -Wall -Wno-unused-variable
ifeq ($(DEBUG), true)
C_FLAGS := $(C_FLAGS) -g
else
C_FLAGS := $(C_FLAGS) -O3
endif
# Rendeder ---------------------------------------------------------------------
ifeq ($(RENDERER), GL)
RENDERER_SRC = src/render_gl.c
C_FLAGS := $(C_FLAGS) -DRENDERER_GL
else
$(error Unknown RENDERER)
endif
# macOS ------------------------------------------------------------------------
ifeq ($(UNAME_S), Darwin)
C_FLAGS := $(C_FLAGS) -x objective-c -I/opt/homebrew/include -D_THREAD_SAFE -w
L_FLAGS := $(L_FLAGS) -L/opt/homebrew/lib -framework Foundation
ifeq ($(RENDERER), GL)
L_FLAGS := $(L_FLAGS) -lGLEW -GLU -framework OpenGL
endif
L_FLAGS_SDL = -lSDL2
L_FLAGS_SOKOL = -framework Cocoa -framework QuartzCore -framework AudioToolbox
# Linux ------------------------------------------------------------------------
else ifeq ($(UNAME_S), Linux)
ifeq ($(RENDERER), GL)
L_FLAGS := $(L_FLAGS) -lGLEW -lGL
endif
L_FLAGS_SDL = -lSDL2
L_FLAGS_SOKOL = -lX11 -lXcursor -pthread -lXi -ldl -lasound
# Windows ----------------------------------------------------------------------
else ifeq ($(OS), Windows_NT)
$(error TODO: FLAGS for windows have not been set up. Please modify this makefile and send a PR!)
else
$(error Unknown environment)
endif
# Source files -----------------------------------------------------------------
TARGET_NATIVE ?= wipegame
BUILD_DIR = build/obj/native
BUILD_DIR_WASM = build/obj/wasm
WASM_RELEASE_DIR ?= build/wasm
TARGET_WASM ?= $(WASM_RELEASE_DIR)/wipeout.js
TARGET_WASM_MINIMAL ?= $(WASM_RELEASE_DIR)/wipeout-minimal.js
COMMON_SRC = \
src/wipeout/race.c \
src/wipeout/camera.c \
src/wipeout/object.c \
src/wipeout/droid.c \
src/wipeout/ui.c \
src/wipeout/hud.c \
src/wipeout/image.c \
src/wipeout/game.c \
src/wipeout/menu.c \
src/wipeout/main_menu.c \
src/wipeout/ingame_menus.c \
src/wipeout/title.c \
src/wipeout/intro.c \
src/wipeout/scene.c \
src/wipeout/ship.c \
src/wipeout/ship_ai.c \
src/wipeout/ship_player.c \
src/wipeout/track.c \
src/wipeout/weapon.c \
src/wipeout/particle.c \
src/wipeout/sfx.c \
src/utils.c \
src/types.c \
src/system.c \
src/mem.c \
src/input.c \
$(RENDERER_SRC)
# Targets native ---------------------------------------------------------------
COMMON_OBJ = $(patsubst %.c, $(BUILD_DIR)/%.o, $(COMMON_SRC))
sdl: $(BUILD_DIR)/src/platform_sdl.o
sdl: $(COMMON_OBJ)
$(CC) $^ -o $(TARGET_NATIVE) $(L_FLAGS) $(L_FLAGS_SDL)
sokol: $(BUILD_DIR)/src/platform_sokol.o
sokol: $(COMMON_OBJ)
$(CC) $^ -o $(TARGET_NATIVE) $(L_FLAGS) $(L_FLAGS_SOKOL)
$(BUILD_DIR):
mkdir -p $(BUILD_DIR)
$(BUILD_DIR)/%.o: %.c
mkdir -p $(dir $@)
$(CC) $(C_FLAGS) -c $< -o $@
# Targets wasm -----------------------------------------------------------------
COMMON_OBJ_WASM = $(patsubst %.c, $(BUILD_DIR_WASM)/%.o, $(COMMON_SRC))
wasm: wasm_full wasm_minimal
cp src/wasm-index.html $(WASM_RELEASE_DIR)/game.html
wasm_full: $(BUILD_DIR_WASM)/src/platform_sokol.o
wasm_full: $(COMMON_OBJ_WASM)
mkdir -p $(WASM_RELEASE_DIR)
$(EMCC) $^ -o $(TARGET_WASM) -lGLEW -lGL \
-s ALLOW_MEMORY_GROWTH=1 \
-s ENVIRONMENT=web \
--preload-file wipeout
wasm_minimal: $(BUILD_DIR_WASM)/src/platform_sokol.o
wasm_minimal: $(COMMON_OBJ_WASM)
mkdir -p $(WASM_RELEASE_DIR)
$(EMCC) $^ -o $(TARGET_WASM_MINIMAL) -lGLEW -lGL \
-s ALLOW_MEMORY_GROWTH=1 \
-s ENVIRONMENT=web \
--preload-file wipeout \
--exclude-file wipeout/music \
--exclude-file wipeout/intro.mpeg
$(BUILD_DIR_WASM):
mkdir -p $(BUILD_DIR_WASM)
$(BUILD_DIR_WASM)/%.o: %.c
mkdir -p $(dir $@)
$(EMCC) $(C_FLAGS) -c $< -o $@
.PHONY: clean
clean:
$(RM) -rf $(BUILD_DIR) $(BUILD_DIR_WASM) $(WASM_RELEASE_DIR)
+126
View File
@@ -0,0 +1,126 @@
# wipEout Rewrite
This is a re-implementation of the 1995 PSX game wipEout.
Play here: https://phoboslab.org/wipegame/
More info in my blog: https://phoboslab.org/log/2023/08/rewriting-wipeout
⚠️ Work in progress. Expect bugs.
## Building
The game currently supports two different platform-backends: [SDL2](https://github.com/libsdl-org/SDL) and [Sokol](https://github.com/floooh/sokol). The only difference in features is that the SDL2 backend supports game controllers (joysticks, gamepads), while the Sokol backend does not.
### Linux
```
# for SDL2 backend
apt install libsdl2-dev
make sdl
```
```
# for Sokol backend
apt install libx11-dev libxcursor-dev libxi-dev libasound2-dev
make sokol
```
### macOS
Currently only the SDL2 backend works. macOS is very picky about the GLSL shader version when compiling with Sokol and OpenGL3.3; it shouldn't be too difficult to get it working, but will probably require a bunch of `#ifdefs` for SDL and WASM. PRs welcome!
```
brew install sdl2
make sdl
```
### Windows
In theory both backends should work on Windows, but the Makefile is missing the proper compiler flags. Please send a PR!
_todo_
### WASM
Install [emscripten](https://emscripten.org/) and activate emsdk, so that `emcc` is in your `PATH`. The WASM version automatically
selects the Sokol backend. I'm not sure what needs to be done to make the SDL2 backend work with WASM.
```
make wasm
```
This builds the minimal version (no music, no intro) as well as the full version.
## Running
This repository does not contain the assets (textures, 3d models etc.) required to run the game. This code mostly assumes to have the PSX NTSC data, but some menu models from the PC version are loaded as well. Both of these can be easily found on archive.org and similar sites. The music (optional) needs to be provided in [QOA format](https://github.com/phoboslab/qoa). The intro video as MPEG1.
The directory structure is assumed to be as follows
```
./wipegame # the executable
./wipeout/textures/
./wipeout/music/track01.qoa
./wipeout/music/track02.qoa
...
```
Note that the blog post announcing this project may or may not provide a link to a ZIP containing all files needed. Who knows!
## Ideas for improvements
PRs Welcome.
### Not yet implemented
Some things from the original game are not yet implemented in this rewrite. This includes
- screen shake effect
- game-end animations, formerly `Spline.cpp` (the end messages are just shown over the attract mode cameras)
- viewing highscores in options menu
- controller options menu
- reverb for sfx and music when there's more than 4 track faces (tunnels and such)
- some more? grep the source for `TODO` and `FIXME`
### Gameplay, Visuals
- less punishing physics for ship vs. ship collisions
- less punishing physics for sideways ship vs. track collisions (i.e. wall grinding like in newer wipEouts)
- somehow resolve the issue of inevitably running into an enemy that you just shot
- add option to lessen the roll in the internal view
- add additional external view that behaves more like in modern racing games
- dynamic lighting on ships
- allow lower resolutions and a drawing mode that resembles the PSX original
- the scene geometry could use some touch-ups to make an infinite draw distance option less awkward
- increase FOV when going over a boost
- better menu models for game exit and video options
- gamepad analog input feels like balancing an egg
- fix collision issues on junctions (also present in the original)
### Technical
- implement frustum culling for scene geometry, the track and ships. Currently everything within the fadeout radius is drawn.
- put all static geometry into a GPU-side buffer. Currently all triangles are constructed at draw time. Uploading geometry is complicated a bit by the fact that some scene animations and the ship's exhaust need to update geometry for each frame.
- the menu system is... not great. It's better than the 5000 lines of spaghetti that it was before, but the different layouts need a lot of `if`s
- the save data is just dumping the whole struct on disk. A textual format would be preferable.
- since this whole thing is relying on some custom assembled assets anyway, maybe all SFX should be in QOA format too (like the music). Or switch everything to Vorbis.
- a lot of functions assume that there's just one player. This needs to be fixed for a potential splitscreen mode.
## License
There is none. This code may or may not be based on the source code of the PC (ATI-Rage) version that was leaked in 2022. If it were, it would probably violate copyright law, but it may also fall under fair use ¯\\\_(ツ)\_/¯
Working with this source code is probably fine, considering that this game was originally released 28 years ago (in 1995), that the current copyright holders historically didn't care about any wipEout related files or code being available on the net and that the game is currently not purchasable in any shape or form.
In any case, you may NOT use this source code in a commercial release. A commercial release includes hosting it on a website that shows any forms of advertising.
PS.: Hey Sony! If you're reading this, I would love to work on a proper, officially sanctioned remaster. Please get in touch <3
+299
View File
@@ -0,0 +1,299 @@
#include <string.h>
#include "input.h"
#include "utils.h"
static const char *button_names[] = {
NULL,
NULL,
NULL,
NULL,
[INPUT_KEY_A] = "a",
[INPUT_KEY_B] = "b",
[INPUT_KEY_C] = "c",
[INPUT_KEY_D] = "d",
[INPUT_KEY_E] = "e",
[INPUT_KEY_F] = "f",
[INPUT_KEY_G] = "g",
[INPUT_KEY_H] = "h",
[INPUT_KEY_I] = "i",
[INPUT_KEY_J] = "j",
[INPUT_KEY_K] = "k",
[INPUT_KEY_L] = "l",
[INPUT_KEY_M] = "m",
[INPUT_KEY_N] = "n",
[INPUT_KEY_O] = "o",
[INPUT_KEY_P] = "p",
[INPUT_KEY_Q] = "q",
[INPUT_KEY_R] = "r",
[INPUT_KEY_S] = "s",
[INPUT_KEY_T] = "t",
[INPUT_KEY_U] = "u",
[INPUT_KEY_V] = "v",
[INPUT_KEY_W] = "w",
[INPUT_KEY_X] = "x",
[INPUT_KEY_Y] = "y",
[INPUT_KEY_Z] = "z",
[INPUT_KEY_1] = "1",
[INPUT_KEY_2] = "2",
[INPUT_KEY_3] = "3",
[INPUT_KEY_4] = "4",
[INPUT_KEY_5] = "5",
[INPUT_KEY_6] = "6",
[INPUT_KEY_7] = "7",
[INPUT_KEY_8] = "8",
[INPUT_KEY_9] = "9",
[INPUT_KEY_0] = "0",
[INPUT_KEY_RETURN] = "return",
[INPUT_KEY_ESCAPE] = "escape",
[INPUT_KEY_BACKSPACE] = "backspace",
[INPUT_KEY_TAB] = "tab",
[INPUT_KEY_SPACE] = "space",
[INPUT_KEY_MINUS] = "minus",
[INPUT_KEY_EQUALS] = "equals",
[INPUT_KEY_LEFTBRACKET] = "left_bracket",
[INPUT_KEY_RIGHTBRACKET] = "right_bracket",
[INPUT_KEY_BACKSLASH] = "backslash",
[INPUT_KEY_HASH] = "hash",
[INPUT_KEY_SEMICOLON] = "semicolon",
[INPUT_KEY_APOSTROPHE] = "apostrophe",
[INPUT_KEY_TILDE] = "tilde",
[INPUT_KEY_COMMA] = "comma",
[INPUT_KEY_PERIOD] = "period",
[INPUT_KEY_SLASH] = "slash",
[INPUT_KEY_CAPSLOCK] = "capslock",
[INPUT_KEY_F1] = "f1",
[INPUT_KEY_F2] = "f2",
[INPUT_KEY_F3] = "f3",
[INPUT_KEY_F4] = "f4",
[INPUT_KEY_F5] = "f5",
[INPUT_KEY_F6] = "f6",
[INPUT_KEY_F7] = "f7",
[INPUT_KEY_F8] = "f8",
[INPUT_KEY_F9] = "f9",
[INPUT_KEY_F10] = "f10",
[INPUT_KEY_F11] = "f11",
[INPUT_KEY_F12] = "f12",
[INPUT_KEY_PRINTSCREEN] = "print_screen",
[INPUT_KEY_SCROLLLOCK] = "scroll_lock",
[INPUT_KEY_PAUSE] = "pause",
[INPUT_KEY_INSERT] = "insert",
[INPUT_KEY_HOME] = "home",
[INPUT_KEY_PAGEUP] = "page_up",
[INPUT_KEY_DELETE] = "delete",
[INPUT_KEY_END] = "end",
[INPUT_KEY_PAGEDOWN] = "page_down",
[INPUT_KEY_RIGHT] = "right",
[INPUT_KEY_LEFT] = "left",
[INPUT_KEY_DOWN] = "down",
[INPUT_KEY_UP] = "up",
[INPUT_KEY_NUMLOCK] = "num_lock",
[INPUT_KEY_KP_DIVIDE] = "keypad_divide",
[INPUT_KEY_KP_MULTIPLY] = "keypad_multiply",
[INPUT_KEY_KP_MINUS] = "keypad_minus",
[INPUT_KEY_KP_PLUS] = "keypad_plus",
[INPUT_KEY_KP_ENTER] = "keypad_enter",
[INPUT_KEY_KP_1] = "keypad_1",
[INPUT_KEY_KP_2] = "keypad_2",
[INPUT_KEY_KP_3] = "keypad_3",
[INPUT_KEY_KP_4] = "keypad_4",
[INPUT_KEY_KP_5] = "keypad_5",
[INPUT_KEY_KP_6] = "keypad_6",
[INPUT_KEY_KP_7] = "keypad_7",
[INPUT_KEY_KP_8] = "keypad_8",
[INPUT_KEY_KP_9] = "keypad_9",
[INPUT_KEY_KP_0] = "keypad_0",
[INPUT_KEY_KP_PERIOD] = "keypad_period",
[INPUT_KEY_LCTRL] = "left_ctrl",
[INPUT_KEY_LSHIFT] = "left_shift",
[INPUT_KEY_LALT] = "left_alt",
[INPUT_KEY_LGUI] = "left_gui",
[INPUT_KEY_RCTRL] = "right_ctrl",
[INPUT_KEY_RSHIFT] = "right_shift",
[INPUT_KEY_RALT] = "right_alt",
NULL,
[INPUT_GAMEPAD_A] = "gamepad_a",
[INPUT_GAMEPAD_Y] = "gamepad_y",
[INPUT_GAMEPAD_B] = "gamepad_b",
[INPUT_GAMEPAD_X] = "gamepad_x",
[INPUT_GAMEPAD_L_SHOULDER] = "gamepad_left_shoulder",
[INPUT_GAMEPAD_R_SHOULDER] = "gamepad_right_shoulder",
[INPUT_GAMEPAD_L_TRIGGER] = "gamepad_left_trigger",
[INPUT_GAMEPAD_R_TRIGGER] = "gamepad_right_trigger",
[INPUT_GAMEPAD_SELECT] = "gamepad_select",
[INPUT_GAMEPAD_START] = "gamepad_start",
[INPUT_GAMEPAD_L_STICK_PRESS] = "gamepad_left_stick_press",
[INPUT_GAMEPAD_R_STICK_PRESS] = "gamepad_right_stick_press",
[INPUT_GAMEPAD_DPAD_UP] = "gamepad_dpad_up",
[INPUT_GAMEPAD_DPAD_DOWN] = "gamepad_dpad_down",
[INPUT_GAMEPAD_DPAD_LEFT] = "gamepad_dpad_left",
[INPUT_GAMEPAD_DPAD_RIGHT] = "gamepad_dpad_right",
[INPUT_GAMEPAD_HOME] = "gamepad_home",
[INPUT_GAMEPAD_L_STICK_UP] = "gamepad_left_stick_up",
[INPUT_GAMEPAD_L_STICK_DOWN] = "gamepad_left_stick_down",
[INPUT_GAMEPAD_L_STICK_LEFT] = "gamepad_left_stick_left",
[INPUT_GAMEPAD_L_STICK_RIGHT] = "gamepad_left_stick_right",
[INPUT_GAMEPAD_R_STICK_UP] = "gamepad_right_stick_up",
[INPUT_GAMEPAD_R_STICK_DOWN] = "gamepad_right_stick_down",
[INPUT_GAMEPAD_R_STICK_LEFT] = "gamepad_right_stick_left",
[INPUT_GAMEPAD_R_STICK_RIGHT] = "gamepad_right_stick_right",
NULL,
[INPUT_MOUSE_LEFT] = "mouse_left",
[INPUT_MOUSE_MIDDLE] = "mouse_middle",
[INPUT_MOUSE_RIGHT] = "mouse_right",
[INPUT_MOUSE_WHEEL_UP] = "mouse_wheel_up",
[INPUT_MOUSE_WHEEL_DOWN] = "mouse_wheel_down",
};
static float actions_state[INPUT_ACTION_MAX];
static bool actions_pressed[INPUT_ACTION_MAX];
static bool actions_released[INPUT_ACTION_MAX];
static uint8_t expected_button[INPUT_ACTION_MAX];
static uint8_t bindings[INPUT_LAYER_MAX][INPUT_BUTTON_MAX];
static input_capture_callback_t capture_callback;
static void *capture_user;
static int32_t mouse_x;
static int32_t mouse_y;
void input_init() {
input_unbind_all(INPUT_LAYER_SYSTEM);
input_unbind_all(INPUT_LAYER_USER);
}
void input_cleanup() {
}
void input_clear() {
clear(actions_pressed);
clear(actions_released);
}
void input_set_layer_button_state(input_layer_t layer, button_t button, float state) {
error_if(layer < 0 || layer >= INPUT_LAYER_MAX, "Invalid input layer %d", layer);
uint8_t action = bindings[layer][button];
if (action == INPUT_ACTION_NONE) {
return;
}
uint8_t expected = expected_button[action];
if (!expected || expected == button) {
state = (state > INPUT_DEADZONE) ? state : 0;
if (state && !actions_state[action]) {
actions_pressed[action] = true;
expected_button[action] = button;
}
else if (!state && actions_state[action]) {
actions_released[action] = true;
expected_button[action] = INPUT_BUTTON_NONE;
}
actions_state[action] = state;
}
}
void input_set_button_state(button_t button, float state) {
error_if(button < 0 || button >= INPUT_BUTTON_MAX, "Invalid input button %d", button);
if (capture_callback) {
if (state) {
capture_callback(capture_user, button, 0);
}
return;
}
input_set_layer_button_state(INPUT_LAYER_SYSTEM, button, state);
input_set_layer_button_state(INPUT_LAYER_USER, button, state);
}
void input_set_mouse_pos(int32_t x, int32_t y) {
mouse_x = x;
mouse_y = y;
}
void input_capture(input_capture_callback_t cb, void *user) {
capture_callback = cb;
capture_user = user;
clear(actions_state);
}
void input_textinput(int32_t ascii_char) {
if (capture_callback) {
capture_callback(capture_user, 0, ascii_char);
}
}
void input_bind(input_layer_t layer, button_t button, uint8_t action) {
error_if(button < 0 || button >= INPUT_BUTTON_MAX, "Invalid input button %d", button);
error_if(action < 0 || action >= INPUT_ACTION_MAX, "Invalid input action %d", action);
error_if(layer < 0 || layer >= INPUT_LAYER_MAX, "Invalid input layer %d", layer);
bindings[layer][button] = action;
}
uint8_t input_bound_to_action(button_t button) {
error_if(button < 0 || button >= INPUT_BUTTON_MAX, "Invalid input button %d", button);
return bindings[INPUT_LAYER_USER][button];
}
void input_unbind(input_layer_t layer, button_t button) {
error_if(layer < 0 || layer >= INPUT_LAYER_MAX, "Invalid input layer %d", layer);
error_if(button < 0 || button >= INPUT_BUTTON_MAX, "Invalid input button %d", button);
bindings[layer][button] = INPUT_ACTION_NONE;
}
void input_unbind_all(input_layer_t layer) {
error_if(layer < 0 || layer >= INPUT_LAYER_MAX, "Invalid input layer %d", layer);
for (uint32_t button = 0; button < INPUT_BUTTON_MAX; button++) {
input_unbind(layer, button);
}
}
float input_state(uint8_t action) {
error_if(action < 0 || action >= INPUT_ACTION_MAX, "Invalid input action %d", action);
return actions_state[action];
}
bool input_pressed(uint8_t action) {
error_if(action < 0 || action >= INPUT_ACTION_MAX, "Invalid input action %d", action);
return actions_pressed[action];
}
bool input_released(uint8_t action) {
error_if(action < 0 || action >= INPUT_ACTION_MAX, "Invalid input action %d", action);
return actions_released[action];
}
vec2_t input_mouse_pos() {
return vec2(mouse_x, mouse_y);
}
button_t input_name_to_button(const char *name) {
for (int32_t i = 0; i < INPUT_BUTTON_MAX; i++) {
if (button_names[i] && strcmp(name, button_names[i]) == 0) {
return i;
}
}
return INPUT_INVALID;
}
const char *input_button_to_name(button_t button) {
if (
button < 0 || button >= INPUT_BUTTON_MAX ||
!button_names[button]
) {
return NULL;
}
return button_names[button];
}
+190
View File
@@ -0,0 +1,190 @@
#ifndef INPUT_H
#define INPUT_H
#include "types.h"
typedef enum {
INPUT_INVALID = 0,
INPUT_KEY_A = 4,
INPUT_KEY_B = 5,
INPUT_KEY_C = 6,
INPUT_KEY_D = 7,
INPUT_KEY_E = 8,
INPUT_KEY_F = 9,
INPUT_KEY_G = 10,
INPUT_KEY_H = 11,
INPUT_KEY_I = 12,
INPUT_KEY_J = 13,
INPUT_KEY_K = 14,
INPUT_KEY_L = 15,
INPUT_KEY_M = 16,
INPUT_KEY_N = 17,
INPUT_KEY_O = 18,
INPUT_KEY_P = 19,
INPUT_KEY_Q = 20,
INPUT_KEY_R = 21,
INPUT_KEY_S = 22,
INPUT_KEY_T = 23,
INPUT_KEY_U = 24,
INPUT_KEY_V = 25,
INPUT_KEY_W = 26,
INPUT_KEY_X = 27,
INPUT_KEY_Y = 28,
INPUT_KEY_Z = 29,
INPUT_KEY_1 = 30,
INPUT_KEY_2 = 31,
INPUT_KEY_3 = 32,
INPUT_KEY_4 = 33,
INPUT_KEY_5 = 34,
INPUT_KEY_6 = 35,
INPUT_KEY_7 = 36,
INPUT_KEY_8 = 37,
INPUT_KEY_9 = 38,
INPUT_KEY_0 = 39,
INPUT_KEY_RETURN = 40,
INPUT_KEY_ESCAPE = 41,
INPUT_KEY_BACKSPACE = 42,
INPUT_KEY_TAB = 43,
INPUT_KEY_SPACE = 44,
INPUT_KEY_MINUS = 45,
INPUT_KEY_EQUALS = 46,
INPUT_KEY_LEFTBRACKET = 47,
INPUT_KEY_RIGHTBRACKET = 48,
INPUT_KEY_BACKSLASH = 49,
INPUT_KEY_HASH = 50,
INPUT_KEY_SEMICOLON = 51,
INPUT_KEY_APOSTROPHE = 52,
INPUT_KEY_TILDE = 53,
INPUT_KEY_COMMA = 54,
INPUT_KEY_PERIOD = 55,
INPUT_KEY_SLASH = 56,
INPUT_KEY_CAPSLOCK = 57,
INPUT_KEY_F1 = 58,
INPUT_KEY_F2 = 59,
INPUT_KEY_F3 = 60,
INPUT_KEY_F4 = 61,
INPUT_KEY_F5 = 62,
INPUT_KEY_F6 = 63,
INPUT_KEY_F7 = 64,
INPUT_KEY_F8 = 65,
INPUT_KEY_F9 = 66,
INPUT_KEY_F10 = 67,
INPUT_KEY_F11 = 68,
INPUT_KEY_F12 = 69,
INPUT_KEY_PRINTSCREEN = 70,
INPUT_KEY_SCROLLLOCK = 71,
INPUT_KEY_PAUSE = 72,
INPUT_KEY_INSERT = 73,
INPUT_KEY_HOME = 74,
INPUT_KEY_PAGEUP = 75,
INPUT_KEY_DELETE = 76,
INPUT_KEY_END = 77,
INPUT_KEY_PAGEDOWN = 78,
INPUT_KEY_RIGHT = 79,
INPUT_KEY_LEFT = 80,
INPUT_KEY_DOWN = 81,
INPUT_KEY_UP = 82,
INPUT_KEY_NUMLOCK = 83,
INPUT_KEY_KP_DIVIDE = 84,
INPUT_KEY_KP_MULTIPLY = 85,
INPUT_KEY_KP_MINUS = 86,
INPUT_KEY_KP_PLUS = 87,
INPUT_KEY_KP_ENTER = 88,
INPUT_KEY_KP_1 = 89,
INPUT_KEY_KP_2 = 90,
INPUT_KEY_KP_3 = 91,
INPUT_KEY_KP_4 = 92,
INPUT_KEY_KP_5 = 93,
INPUT_KEY_KP_6 = 94,
INPUT_KEY_KP_7 = 95,
INPUT_KEY_KP_8 = 96,
INPUT_KEY_KP_9 = 97,
INPUT_KEY_KP_0 = 98,
INPUT_KEY_KP_PERIOD = 99,
INPUT_KEY_LCTRL = 100,
INPUT_KEY_LSHIFT = 101,
INPUT_KEY_LALT = 102,
INPUT_KEY_LGUI = 103,
INPUT_KEY_RCTRL = 104,
INPUT_KEY_RSHIFT = 105,
INPUT_KEY_RALT = 106,
INPUT_KEY_MAX = 107,
INPUT_GAMEPAD_A = 108,
INPUT_GAMEPAD_Y = 109,
INPUT_GAMEPAD_B = 110,
INPUT_GAMEPAD_X = 111,
INPUT_GAMEPAD_L_SHOULDER = 112,
INPUT_GAMEPAD_R_SHOULDER = 113,
INPUT_GAMEPAD_L_TRIGGER = 114,
INPUT_GAMEPAD_R_TRIGGER = 115,
INPUT_GAMEPAD_SELECT = 116,
INPUT_GAMEPAD_START = 117,
INPUT_GAMEPAD_L_STICK_PRESS = 118,
INPUT_GAMEPAD_R_STICK_PRESS = 119,
INPUT_GAMEPAD_DPAD_UP = 120,
INPUT_GAMEPAD_DPAD_DOWN = 121,
INPUT_GAMEPAD_DPAD_LEFT = 122,
INPUT_GAMEPAD_DPAD_RIGHT = 123,
INPUT_GAMEPAD_HOME = 124,
INPUT_GAMEPAD_L_STICK_UP = 125,
INPUT_GAMEPAD_L_STICK_DOWN = 126,
INPUT_GAMEPAD_L_STICK_LEFT = 127,
INPUT_GAMEPAD_L_STICK_RIGHT = 128,
INPUT_GAMEPAD_R_STICK_UP = 129,
INPUT_GAMEPAD_R_STICK_DOWN = 130,
INPUT_GAMEPAD_R_STICK_LEFT = 131,
INPUT_GAMEPAD_R_STICK_RIGHT = 132,
INPUT_MOUSE_LEFT = 134,
INPUT_MOUSE_MIDDLE = 135,
INPUT_MOUSE_RIGHT = 136,
INPUT_MOUSE_WHEEL_UP = 137,
INPUT_MOUSE_WHEEL_DOWN = 138,
INPUT_BUTTON_MAX = 139
} button_t;
typedef enum {
INPUT_LAYER_SYSTEM = 0,
INPUT_LAYER_USER = 1,
INPUT_LAYER_MAX = 2
} input_layer_t;
typedef void(*input_capture_callback_t)
(void *user, button_t button, int32_t ascii_char);
void input_init();
void input_cleanup();
void input_clear();
void input_bind(input_layer_t layer, button_t button, uint8_t action);
void input_unbind(input_layer_t layer,button_t button);
void input_unbind_all(input_layer_t layer);
uint8_t input_bound_to_action(button_t button);
void input_set_button_state(button_t button, float state);
void input_set_mouse_pos(int32_t x, int32_t y);
void input_textinput(int32_t ascii_char);
void input_capture(input_capture_callback_t cb, void *user);
float input_state(uint8_t action);
bool input_pressed(uint8_t action);
bool input_released(uint8_t action);
vec2_t input_mouse_pos();
button_t input_name_to_button(const char *name);
const char *input_button_to_name(button_t button);
#define INPUT_ACTION_COMMAND 31
#define INPUT_ACTION_MAX 32
#define INPUT_DEADZONE 0.1
#define INPUT_ACTION_NONE 255
#define INPUT_BUTTON_NONE 0
#endif
+4274
View File
File diff suppressed because it is too large Load Diff
+728
View File
File diff suppressed because it is too large Load Diff
+11057
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+324
View File
@@ -0,0 +1,324 @@
#if defined(SOKOL_IMPL) && !defined(SOKOL_TIME_IMPL)
#define SOKOL_TIME_IMPL
#endif
#ifndef SOKOL_TIME_INCLUDED
/*
sokol_time.h -- simple cross-platform time measurement
Project URL: https://github.com/floooh/sokol
Do this:
#define SOKOL_IMPL or
#define SOKOL_TIME_IMPL
before you include this file in *one* C or C++ file to create the
implementation.
Optionally provide the following defines with your own implementations:
SOKOL_ASSERT(c) - your own assert macro (default: assert(c))
SOKOL_TIME_API_DECL - public function declaration prefix (default: extern)
SOKOL_API_DECL - same as SOKOL_TIME_API_DECL
SOKOL_API_IMPL - public function implementation prefix (default: -)
If sokol_time.h is compiled as a DLL, define the following before
including the declaration or implementation:
SOKOL_DLL
On Windows, SOKOL_DLL will define SOKOL_TIME_API_DECL as __declspec(dllexport)
or __declspec(dllimport) as needed.
void stm_setup();
Call once before any other functions to initialize sokol_time
(this calls for instance QueryPerformanceFrequency on Windows)
uint64_t stm_now();
Get current point in time in unspecified 'ticks'. The value that
is returned has no relation to the 'wall-clock' time and is
not in a specific time unit, it is only useful to compute
time differences.
uint64_t stm_diff(uint64_t new, uint64_t old);
Computes the time difference between new and old. This will always
return a positive, non-zero value.
uint64_t stm_since(uint64_t start);
Takes the current time, and returns the elapsed time since start
(this is a shortcut for "stm_diff(stm_now(), start)")
uint64_t stm_laptime(uint64_t* last_time);
This is useful for measuring frame time and other recurring
events. It takes the current time, returns the time difference
to the value in last_time, and stores the current time in
last_time for the next call. If the value in last_time is 0,
the return value will be zero (this usually happens on the
very first call).
uint64_t stm_round_to_common_refresh_rate(uint64_t duration)
This oddly named function takes a measured frame time and
returns the closest "nearby" common display refresh rate frame duration
in ticks. If the input duration isn't close to any common display
refresh rate, the input duration will be returned unchanged as a fallback.
The main purpose of this function is to remove jitter/inaccuracies from
measured frame times, and instead use the display refresh rate as
frame duration.
Use the following functions to convert a duration in ticks into
useful time units:
double stm_sec(uint64_t ticks);
double stm_ms(uint64_t ticks);
double stm_us(uint64_t ticks);
double stm_ns(uint64_t ticks);
Converts a tick value into seconds, milliseconds, microseconds
or nanoseconds. Note that not all platforms will have nanosecond
or even microsecond precision.
Uses the following time measurement functions under the hood:
Windows: QueryPerformanceFrequency() / QueryPerformanceCounter()
MacOS/iOS: mach_absolute_time()
emscripten: performance.now()
Linux+others: clock_gettime(CLOCK_MONOTONIC)
zlib/libpng license
Copyright (c) 2018 Andre Weissflog
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from the
use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software in a
product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not
be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
*/
#define SOKOL_TIME_INCLUDED (1)
#include <stdint.h>
#if defined(SOKOL_API_DECL) && !defined(SOKOL_TIME_API_DECL)
#define SOKOL_TIME_API_DECL SOKOL_API_DECL
#endif
#ifndef SOKOL_TIME_API_DECL
#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_TIME_IMPL)
#define SOKOL_TIME_API_DECL __declspec(dllexport)
#elif defined(_WIN32) && defined(SOKOL_DLL)
#define SOKOL_TIME_API_DECL __declspec(dllimport)
#else
#define SOKOL_TIME_API_DECL extern
#endif
#endif
#ifdef __cplusplus
extern "C" {
#endif
SOKOL_TIME_API_DECL void stm_setup(void);
SOKOL_TIME_API_DECL uint64_t stm_now(void);
SOKOL_TIME_API_DECL uint64_t stm_diff(uint64_t new_ticks, uint64_t old_ticks);
SOKOL_TIME_API_DECL uint64_t stm_since(uint64_t start_ticks);
SOKOL_TIME_API_DECL uint64_t stm_laptime(uint64_t* last_time);
SOKOL_TIME_API_DECL uint64_t stm_round_to_common_refresh_rate(uint64_t frame_ticks);
SOKOL_TIME_API_DECL double stm_sec(uint64_t ticks);
SOKOL_TIME_API_DECL double stm_ms(uint64_t ticks);
SOKOL_TIME_API_DECL double stm_us(uint64_t ticks);
SOKOL_TIME_API_DECL double stm_ns(uint64_t ticks);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif // SOKOL_TIME_INCLUDED
/*-- IMPLEMENTATION ----------------------------------------------------------*/
#ifdef SOKOL_TIME_IMPL
#define SOKOL_TIME_IMPL_INCLUDED (1)
#include <string.h> /* memset */
#ifndef SOKOL_API_IMPL
#define SOKOL_API_IMPL
#endif
#ifndef SOKOL_ASSERT
#include <assert.h>
#define SOKOL_ASSERT(c) assert(c)
#endif
#ifndef _SOKOL_PRIVATE
#if defined(__GNUC__) || defined(__clang__)
#define _SOKOL_PRIVATE __attribute__((unused)) static
#else
#define _SOKOL_PRIVATE static
#endif
#endif
#if defined(_WIN32)
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
typedef struct {
uint32_t initialized;
LARGE_INTEGER freq;
LARGE_INTEGER start;
} _stm_state_t;
#elif defined(__APPLE__) && defined(__MACH__)
#include <mach/mach_time.h>
typedef struct {
uint32_t initialized;
mach_timebase_info_data_t timebase;
uint64_t start;
} _stm_state_t;
#elif defined(__EMSCRIPTEN__)
#include <emscripten/emscripten.h>
typedef struct {
uint32_t initialized;
double start;
} _stm_state_t;
#else /* anything else, this will need more care for non-Linux platforms */
#ifdef ESP8266
// On the ESP8266, clock_gettime ignores the first argument and CLOCK_MONOTONIC isn't defined
#define CLOCK_MONOTONIC 0
#endif
#include <time.h>
typedef struct {
uint32_t initialized;
uint64_t start;
} _stm_state_t;
#endif
static _stm_state_t _stm;
/* prevent 64-bit overflow when computing relative timestamp
see https://gist.github.com/jspohr/3dc4f00033d79ec5bdaf67bc46c813e3
*/
#if defined(_WIN32) || (defined(__APPLE__) && defined(__MACH__))
_SOKOL_PRIVATE int64_t int64_muldiv(int64_t value, int64_t numer, int64_t denom) {
int64_t q = value / denom;
int64_t r = value % denom;
return q * numer + r * numer / denom;
}
#endif
#if defined(__EMSCRIPTEN__)
EM_JS(double, stm_js_perfnow, (void), {
return performance.now();
});
#endif
SOKOL_API_IMPL void stm_setup(void) {
memset(&_stm, 0, sizeof(_stm));
_stm.initialized = 0xABCDABCD;
#if defined(_WIN32)
QueryPerformanceFrequency(&_stm.freq);
QueryPerformanceCounter(&_stm.start);
#elif defined(__APPLE__) && defined(__MACH__)
mach_timebase_info(&_stm.timebase);
_stm.start = mach_absolute_time();
#elif defined(__EMSCRIPTEN__)
_stm.start = stm_js_perfnow();
#else
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
_stm.start = (uint64_t)ts.tv_sec*1000000000 + (uint64_t)ts.tv_nsec;
#endif
}
SOKOL_API_IMPL uint64_t stm_now(void) {
SOKOL_ASSERT(_stm.initialized == 0xABCDABCD);
uint64_t now;
#if defined(_WIN32)
LARGE_INTEGER qpc_t;
QueryPerformanceCounter(&qpc_t);
now = (uint64_t) int64_muldiv(qpc_t.QuadPart - _stm.start.QuadPart, 1000000000, _stm.freq.QuadPart);
#elif defined(__APPLE__) && defined(__MACH__)
const uint64_t mach_now = mach_absolute_time() - _stm.start;
now = (uint64_t) int64_muldiv((int64_t)mach_now, (int64_t)_stm.timebase.numer, (int64_t)_stm.timebase.denom);
#elif defined(__EMSCRIPTEN__)
double js_now = stm_js_perfnow() - _stm.start;
SOKOL_ASSERT(js_now >= 0.0);
now = (uint64_t) (js_now * 1000000.0);
#else
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
now = ((uint64_t)ts.tv_sec*1000000000 + (uint64_t)ts.tv_nsec) - _stm.start;
#endif
return now;
}
SOKOL_API_IMPL uint64_t stm_diff(uint64_t new_ticks, uint64_t old_ticks) {
if (new_ticks > old_ticks) {
return new_ticks - old_ticks;
}
else {
return 1;
}
}
SOKOL_API_IMPL uint64_t stm_since(uint64_t start_ticks) {
return stm_diff(stm_now(), start_ticks);
}
SOKOL_API_IMPL uint64_t stm_laptime(uint64_t* last_time) {
SOKOL_ASSERT(last_time);
uint64_t dt = 0;
uint64_t now = stm_now();
if (0 != *last_time) {
dt = stm_diff(now, *last_time);
}
*last_time = now;
return dt;
}
// first number is frame duration in ns, second number is tolerance in ns,
// the resulting min/max values must not overlap!
static const uint64_t _stm_refresh_rates[][2] = {
{ 16666667, 1000000 }, // 60 Hz: 16.6667 +- 1ms
{ 13888889, 250000 }, // 72 Hz: 13.8889 +- 0.25ms
{ 13333333, 250000 }, // 75 Hz: 13.3333 +- 0.25ms
{ 11764706, 250000 }, // 85 Hz: 11.7647 +- 0.25
{ 11111111, 250000 }, // 90 Hz: 11.1111 +- 0.25ms
{ 10000000, 500000 }, // 100 Hz: 10.0000 +- 0.5ms
{ 8333333, 500000 }, // 120 Hz: 8.3333 +- 0.5ms
{ 6944445, 500000 }, // 144 Hz: 6.9445 +- 0.5ms
{ 4166667, 1000000 }, // 240 Hz: 4.1666 +- 1ms
{ 0, 0 }, // keep the last element always at zero
};
SOKOL_API_IMPL uint64_t stm_round_to_common_refresh_rate(uint64_t ticks) {
uint64_t ns;
int i = 0;
while (0 != (ns = _stm_refresh_rates[i][0])) {
uint64_t tol = _stm_refresh_rates[i][1];
if ((ticks > (ns - tol)) && (ticks < (ns + tol))) {
return ns;
}
i++;
}
// fallthough: didn't fit into any buckets
return ticks;
}
SOKOL_API_IMPL double stm_sec(uint64_t ticks) {
return (double)ticks / 1000000000.0;
}
SOKOL_API_IMPL double stm_ms(uint64_t ticks) {
return (double)ticks / 1000000.0;
}
SOKOL_API_IMPL double stm_us(uint64_t ticks) {
return (double)ticks / 1000.0;
}
SOKOL_API_IMPL double stm_ns(uint64_t ticks) {
return (double)ticks;
}
#endif /* SOKOL_TIME_IMPL */
File diff suppressed because it is too large Load Diff
+81
View File
@@ -0,0 +1,81 @@
#include <stdlib.h>
#include <string.h>
#include "mem.h"
#include "utils.h"
static uint8_t hunk[MEM_HUNK_BYTES];
static uint32_t bump_len = 0;
static uint32_t temp_len = 0;
static uint32_t temp_objects[MEM_TEMP_OBJECTS_MAX] = {};
static uint32_t temp_objects_len;
// Bump allocator - returns bytes from the front of the hunk
// These allocations persist for many frames. The allocator level is reset
// whenever we load a new race track or menu in game_set_scene()
void *mem_mark() {
return &hunk[bump_len];
}
void *mem_bump(uint32_t size) {
error_if(bump_len + temp_len + size >= MEM_HUNK_BYTES, "Failed to allocate %d bytes in hunk mem", size);
uint8_t *p = &hunk[bump_len];
bump_len += size;
memset(p, 0, size);
return p;
}
void mem_reset(void *p) {
uint32_t offset = (uint8_t *)p - (uint8_t *)hunk;
error_if(offset > bump_len || offset > MEM_HUNK_BYTES, "Invalid mem reset");
bump_len = offset;
}
// Temp allocator - returns bytes from the back of the hunk
// Temporary allocated bytes are not allowed to persist for multiple frames. You
// need to explicitly free them when you are done. Temp allocated bytes don't
// have be freed in reverse allocation order. I.e. you can allocate A then B,
// and aftewards free A then B.
void *mem_temp_alloc(uint32_t size) {
size = ((size >> 3) + 7) << 3; // allign to 8 bytes
error_if(bump_len + temp_len + size >= MEM_HUNK_BYTES, "Failed to allocate %d bytes in temp mem", size);
error_if(temp_objects_len >= MEM_TEMP_OBJECTS_MAX, "MEM_TEMP_OBJECTS_MAX reached");
temp_len += size;
void *p = &hunk[MEM_HUNK_BYTES - temp_len];
temp_objects[temp_objects_len++] = temp_len;
return p;
}
void mem_temp_free(void *p) {
uint32_t offset = (uint8_t *)&hunk[MEM_HUNK_BYTES] - (uint8_t *)p;
error_if(offset > MEM_HUNK_BYTES, "Object 0x%p not in temp hunk", p);
bool found = false;
uint32_t remaining_max = 0;
for (int i = 0; i < temp_objects_len; i++) {
if (temp_objects[i] == offset) {
temp_objects[i--] = temp_objects[--temp_objects_len];
found = true;
}
else if (temp_objects[i] > remaining_max) {
remaining_max = temp_objects[i];
}
}
error_if(!found, "Object 0x%p not in temp hunk", p);
temp_len = remaining_max;
}
void mem_temp_check() {
error_if(temp_len != 0, "Temp memory not free: %d object(s)", temp_objects_len);
}
+17
View File
@@ -0,0 +1,17 @@
#ifndef MEM_H
#define MEM_H
#include "types.h"
#define MEM_TEMP_OBJECTS_MAX 8
#define MEM_HUNK_BYTES (4 * 1024 * 1024)
void *mem_bump(uint32_t size);
void *mem_mark();
void mem_reset(void *p);
void *mem_temp_alloc(uint32_t size);
void mem_temp_free(void *p);
void mem_temp_check();
#endif
+12
View File
@@ -0,0 +1,12 @@
#ifndef PLATFORM_H
#define PLATFORM_H
#include "types.h"
void platform_exit();
vec2i_t platform_screen_size();
double platform_now();
void platform_set_fullscreen(bool fullscreen);
void platform_set_audio_mix_cb(void (*cb)(float *buffer, uint32_t len));
#endif
+281
View File
@@ -0,0 +1,281 @@
#include <SDL2/SDL.h>
#include "platform.h"
#include "input.h"
#include "system.h"
static uint64_t perf_freq = 0;
static bool wants_to_exit = false;
static SDL_Window *window;
static SDL_AudioDeviceID audio_device;
static SDL_GameController *gamepad;
static void (*audio_callback)(float *buffer, uint32_t len) = NULL;
uint8_t platform_sdl_gamepad_map[] = {
[SDL_CONTROLLER_BUTTON_A] = INPUT_GAMEPAD_A,
[SDL_CONTROLLER_BUTTON_B] = INPUT_GAMEPAD_B,
[SDL_CONTROLLER_BUTTON_X] = INPUT_GAMEPAD_X,
[SDL_CONTROLLER_BUTTON_Y] = INPUT_GAMEPAD_Y,
[SDL_CONTROLLER_BUTTON_BACK] = INPUT_GAMEPAD_SELECT,
[SDL_CONTROLLER_BUTTON_GUIDE] = INPUT_INVALID,
[SDL_CONTROLLER_BUTTON_START] = INPUT_GAMEPAD_START,
[SDL_CONTROLLER_BUTTON_LEFTSTICK] = INPUT_GAMEPAD_L_STICK_PRESS,
[SDL_CONTROLLER_BUTTON_RIGHTSTICK] = INPUT_GAMEPAD_R_STICK_PRESS,
[SDL_CONTROLLER_BUTTON_LEFTSHOULDER] = INPUT_GAMEPAD_L_SHOULDER,
[SDL_CONTROLLER_BUTTON_RIGHTSHOULDER] = INPUT_GAMEPAD_R_SHOULDER,
[SDL_CONTROLLER_BUTTON_DPAD_UP] = INPUT_GAMEPAD_DPAD_UP,
[SDL_CONTROLLER_BUTTON_DPAD_DOWN] = INPUT_GAMEPAD_DPAD_DOWN,
[SDL_CONTROLLER_BUTTON_DPAD_LEFT] = INPUT_GAMEPAD_DPAD_LEFT,
[SDL_CONTROLLER_BUTTON_DPAD_RIGHT] = INPUT_GAMEPAD_DPAD_RIGHT,
[SDL_CONTROLLER_BUTTON_MAX] = INPUT_INVALID
};
uint8_t platform_sdl_axis_map[] = {
[SDL_CONTROLLER_AXIS_LEFTX] = INPUT_GAMEPAD_L_STICK_LEFT,
[SDL_CONTROLLER_AXIS_LEFTY] = INPUT_GAMEPAD_L_STICK_UP,
[SDL_CONTROLLER_AXIS_RIGHTX] = INPUT_GAMEPAD_R_STICK_LEFT,
[SDL_CONTROLLER_AXIS_RIGHTY] = INPUT_GAMEPAD_R_STICK_UP,
[SDL_CONTROLLER_AXIS_TRIGGERLEFT] = INPUT_GAMEPAD_L_TRIGGER,
[SDL_CONTROLLER_AXIS_TRIGGERRIGHT] = INPUT_GAMEPAD_R_TRIGGER,
[SDL_CONTROLLER_AXIS_MAX] = INPUT_INVALID
};
void platform_exit() {
wants_to_exit = true;
}
SDL_GameController *platform_find_gamepad() {
for (int i = 0; i < SDL_NumJoysticks(); i++) {
if (SDL_IsGameController(i)) {
return SDL_GameControllerOpen(i);
}
}
return NULL;
}
void platform_pump_events() {
SDL_Event ev;
while (SDL_PollEvent(&ev)) {
// Input Keyboard
if (ev.type == SDL_KEYDOWN || ev.type == SDL_KEYUP) {
int code = ev.key.keysym.scancode;
float state = ev.type == SDL_KEYDOWN ? 1.0 : 0.0;
if (code >= SDL_SCANCODE_LCTRL && code <= SDL_SCANCODE_RALT) {
int code_internal = code - SDL_SCANCODE_LCTRL + INPUT_KEY_LCTRL;
input_set_button_state(code_internal, state);
}
else if (code > 0 && code < INPUT_KEY_MAX) {
input_set_button_state(code, state);
}
}
else if (ev.type == SDL_TEXTINPUT) {
input_textinput(ev.text.text[0]);
}
// Gamepads connect/disconnect
else if (ev.type == SDL_CONTROLLERDEVICEADDED) {
gamepad = SDL_GameControllerOpen(ev.cdevice.which);
}
else if (ev.type == SDL_CONTROLLERDEVICEREMOVED) {
if (gamepad && ev.cdevice.which == SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(gamepad))) {
SDL_GameControllerClose(gamepad);
gamepad = platform_find_gamepad();
}
}
// Input Gamepad Buttons
else if (
ev.type == SDL_CONTROLLERBUTTONDOWN ||
ev.type == SDL_CONTROLLERBUTTONUP
) {
if (ev.cbutton.button < SDL_CONTROLLER_BUTTON_MAX) {
button_t button = platform_sdl_gamepad_map[ev.cbutton.button];
if (button != INPUT_INVALID) {
float state = ev.type == SDL_CONTROLLERBUTTONDOWN ? 1.0 : 0.0;
input_set_button_state(button, state);
}
}
}
// Input Gamepad Axis
else if (ev.type == SDL_CONTROLLERAXISMOTION) {
float state = (float)ev.caxis.value / 32767.0;
if (ev.caxis.axis < SDL_CONTROLLER_AXIS_MAX) {
int code = platform_sdl_axis_map[ev.caxis.axis];
if (
code == INPUT_GAMEPAD_L_TRIGGER ||
code == INPUT_GAMEPAD_R_TRIGGER
) {
input_set_button_state(code, state);
}
else if (state > 0) {
input_set_button_state(code, 0.0);
input_set_button_state(code+1, state);
}
else {
input_set_button_state(code, -state);
input_set_button_state(code+1, 0.0);
}
}
}
// Mouse buttons
else if (
ev.type == SDL_MOUSEBUTTONDOWN ||
ev.type == SDL_MOUSEBUTTONUP
) {
button_t button = INPUT_BUTTON_NONE;
switch (ev.button.button) {
case SDL_BUTTON_LEFT: button = INPUT_MOUSE_LEFT; break;
case SDL_BUTTON_MIDDLE: button = INPUT_MOUSE_MIDDLE; break;
case SDL_BUTTON_RIGHT: button = INPUT_MOUSE_RIGHT; break;
default: break;
}
if (button != INPUT_BUTTON_NONE) {
float state = ev.type == SDL_MOUSEBUTTONDOWN ? 1.0 : 0.0;
input_set_button_state(button, state);
}
}
// Mouse wheel
else if (ev.type == SDL_MOUSEWHEEL) {
button_t button = ev.wheel.y > 0
? INPUT_MOUSE_WHEEL_UP
: INPUT_MOUSE_WHEEL_DOWN;
input_set_button_state(button, 1.0);
input_set_button_state(button, 0.0);
}
// Mouse move
else if (ev.type == SDL_MOUSEMOTION) {
input_set_mouse_pos(ev.motion.x, ev.motion.y);
}
// Window Events
if (ev.type == SDL_QUIT) {
wants_to_exit = true;
}
else if (
ev.type == SDL_WINDOWEVENT &&
(
ev.window.event == SDL_WINDOWEVENT_SIZE_CHANGED ||
ev.window.event == SDL_WINDOWEVENT_RESIZED
)
) {
system_resize(platform_screen_size());
}
}
}
vec2i_t platform_screen_size() {
int width, height;
SDL_GL_GetDrawableSize(window, &width, &height);
return vec2i(width, height);
}
double platform_now() {
uint64_t perf_counter = SDL_GetPerformanceCounter();
return (double)perf_counter / (double)perf_freq;
}
void platform_set_fullscreen(bool fullscreen) {
if (fullscreen) {
int32_t display = SDL_GetWindowDisplayIndex(window);
SDL_DisplayMode mode;
SDL_GetDesktopDisplayMode(display, &mode);
SDL_SetWindowDisplayMode(window, &mode);
SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN);
}
else {
SDL_SetWindowFullscreen(window, 0);
}
}
void platform_audio_callback(void* userdata, uint8_t* stream, int len) {
if (audio_callback) {
audio_callback((float *)stream, len/sizeof(float));
}
else {
memset(stream, 0, len);
}
}
void platform_set_audio_mix_cb(void (*cb)(float *buffer, uint32_t len)) {
audio_callback = cb;
SDL_PauseAudioDevice(audio_device, 0);
}
#if defined(RENDERER_GL)
#define PLATFORM_WINDOW_FLAGS SDL_WINDOW_OPENGL
SDL_GLContext platform_gl;
void platform_video_init() {
platform_gl = SDL_GL_CreateContext(window);
SDL_GL_SetSwapInterval(1);
}
void platform_prepare_frame() {
// nothing
}
void platform_video_cleanup() {
SDL_GL_DeleteContext(platform_gl);
}
void platform_end_frame() {
SDL_GL_SwapWindow(window);
}
#else
#error "Unsupported renderer for platform SDL"
#endif
int main(int argc, char *argv[]) {
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_GAMECONTROLLER);
audio_device = SDL_OpenAudioDevice(NULL, 0, &(SDL_AudioSpec){
.freq = 44100,
.format = AUDIO_F32,
.channels = 2,
.samples = 4096,
.callback = platform_audio_callback
}, NULL, 0);
gamepad = platform_find_gamepad();
perf_freq = SDL_GetPerformanceFrequency();
window = SDL_CreateWindow(
SYSTEM_WINDOW_NAME,
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
SYSTEM_WINDOW_WIDTH, SYSTEM_WINDOW_HEIGHT,
SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE | PLATFORM_WINDOW_FLAGS | SDL_WINDOW_ALLOW_HIGHDPI
);
platform_video_init();
system_init();
while (!wants_to_exit) {
platform_pump_events();
platform_prepare_frame();
system_update();
platform_end_frame();
}
system_cleanup();
platform_video_cleanup();
SDL_CloseAudioDevice(audio_device);
SDL_Quit();
return 0;
}
+255
View File
@@ -0,0 +1,255 @@
#include "platform.h"
#include "system.h"
#if defined(RENDERER_GL)
#ifdef __EMSCRIPTEN__
#define SOKOL_GLES2
#else
#define SOKOL_GLCORE33
#endif
#else
#error "Unsupported renderer for platform SOKOL"
#endif
#define SOKOL_IMPL
#include "libs/sokol_audio.h"
#include "libs/sokol_time.h"
#include "libs/sokol_app.h"
#include "input.h"
static const uint8_t keyboard_map[] = {
[SAPP_KEYCODE_SPACE] = INPUT_KEY_SPACE,
[SAPP_KEYCODE_APOSTROPHE] = INPUT_KEY_APOSTROPHE,
[SAPP_KEYCODE_COMMA] = INPUT_KEY_COMMA,
[SAPP_KEYCODE_MINUS] = INPUT_KEY_MINUS,
[SAPP_KEYCODE_PERIOD] = INPUT_KEY_PERIOD,
[SAPP_KEYCODE_SLASH] = INPUT_KEY_SLASH,
[SAPP_KEYCODE_0] = INPUT_KEY_0,
[SAPP_KEYCODE_1] = INPUT_KEY_1,
[SAPP_KEYCODE_2] = INPUT_KEY_2,
[SAPP_KEYCODE_3] = INPUT_KEY_3,
[SAPP_KEYCODE_4] = INPUT_KEY_4,
[SAPP_KEYCODE_5] = INPUT_KEY_5,
[SAPP_KEYCODE_6] = INPUT_KEY_6,
[SAPP_KEYCODE_7] = INPUT_KEY_7,
[SAPP_KEYCODE_8] = INPUT_KEY_8,
[SAPP_KEYCODE_9] = INPUT_KEY_9,
[SAPP_KEYCODE_SEMICOLON] = INPUT_KEY_SEMICOLON,
[SAPP_KEYCODE_EQUAL] = INPUT_KEY_EQUALS,
[SAPP_KEYCODE_A] = INPUT_KEY_A,
[SAPP_KEYCODE_B] = INPUT_KEY_B,
[SAPP_KEYCODE_C] = INPUT_KEY_C,
[SAPP_KEYCODE_D] = INPUT_KEY_D,
[SAPP_KEYCODE_E] = INPUT_KEY_E,
[SAPP_KEYCODE_F] = INPUT_KEY_F,
[SAPP_KEYCODE_G] = INPUT_KEY_G,
[SAPP_KEYCODE_H] = INPUT_KEY_H,
[SAPP_KEYCODE_I] = INPUT_KEY_I,
[SAPP_KEYCODE_J] = INPUT_KEY_J,
[SAPP_KEYCODE_K] = INPUT_KEY_K,
[SAPP_KEYCODE_L] = INPUT_KEY_L,
[SAPP_KEYCODE_M] = INPUT_KEY_M,
[SAPP_KEYCODE_N] = INPUT_KEY_N,
[SAPP_KEYCODE_O] = INPUT_KEY_O,
[SAPP_KEYCODE_P] = INPUT_KEY_P,
[SAPP_KEYCODE_Q] = INPUT_KEY_Q,
[SAPP_KEYCODE_R] = INPUT_KEY_R,
[SAPP_KEYCODE_S] = INPUT_KEY_S,
[SAPP_KEYCODE_T] = INPUT_KEY_T,
[SAPP_KEYCODE_U] = INPUT_KEY_U,
[SAPP_KEYCODE_V] = INPUT_KEY_V,
[SAPP_KEYCODE_W] = INPUT_KEY_W,
[SAPP_KEYCODE_X] = INPUT_KEY_X,
[SAPP_KEYCODE_Y] = INPUT_KEY_Y,
[SAPP_KEYCODE_Z] = INPUT_KEY_Z,
[SAPP_KEYCODE_LEFT_BRACKET] = INPUT_KEY_LEFTBRACKET,
[SAPP_KEYCODE_BACKSLASH] = INPUT_KEY_BACKSLASH,
[SAPP_KEYCODE_RIGHT_BRACKET] = INPUT_KEY_RIGHTBRACKET,
[SAPP_KEYCODE_GRAVE_ACCENT] = INPUT_KEY_TILDE,
[SAPP_KEYCODE_WORLD_1] = INPUT_INVALID, // not implemented
[SAPP_KEYCODE_WORLD_2] = INPUT_INVALID, // not implemented
[SAPP_KEYCODE_ESCAPE] = INPUT_KEY_ESCAPE,
[SAPP_KEYCODE_ENTER] = INPUT_KEY_RETURN,
[SAPP_KEYCODE_TAB] = INPUT_KEY_TAB,
[SAPP_KEYCODE_BACKSPACE] = INPUT_KEY_BACKSPACE,
[SAPP_KEYCODE_INSERT] = INPUT_KEY_INSERT,
[SAPP_KEYCODE_DELETE] = INPUT_KEY_DELETE,
[SAPP_KEYCODE_RIGHT] = INPUT_KEY_RIGHT,
[SAPP_KEYCODE_LEFT] = INPUT_KEY_LEFT,
[SAPP_KEYCODE_DOWN] = INPUT_KEY_DOWN,
[SAPP_KEYCODE_UP] = INPUT_KEY_UP,
[SAPP_KEYCODE_PAGE_UP] = INPUT_KEY_PAGEUP,
[SAPP_KEYCODE_PAGE_DOWN] = INPUT_KEY_PAGEDOWN,
[SAPP_KEYCODE_HOME] = INPUT_KEY_HOME,
[SAPP_KEYCODE_END] = INPUT_KEY_END,
[SAPP_KEYCODE_CAPS_LOCK] = INPUT_KEY_CAPSLOCK,
[SAPP_KEYCODE_SCROLL_LOCK] = INPUT_KEY_SCROLLLOCK,
[SAPP_KEYCODE_NUM_LOCK] = INPUT_KEY_NUMLOCK,
[SAPP_KEYCODE_PRINT_SCREEN] = INPUT_KEY_PRINTSCREEN,
[SAPP_KEYCODE_PAUSE] = INPUT_KEY_PAUSE,
[SAPP_KEYCODE_F1] = INPUT_KEY_F1,
[SAPP_KEYCODE_F2] = INPUT_KEY_F2,
[SAPP_KEYCODE_F3] = INPUT_KEY_F3,
[SAPP_KEYCODE_F4] = INPUT_KEY_F4,
[SAPP_KEYCODE_F5] = INPUT_KEY_F5,
[SAPP_KEYCODE_F6] = INPUT_KEY_F6,
[SAPP_KEYCODE_F7] = INPUT_KEY_F7,
[SAPP_KEYCODE_F8] = INPUT_KEY_F8,
[SAPP_KEYCODE_F9] = INPUT_KEY_F9,
[SAPP_KEYCODE_F10] = INPUT_KEY_F10,
[SAPP_KEYCODE_F11] = INPUT_KEY_F11,
[SAPP_KEYCODE_F12] = INPUT_KEY_F12,
[SAPP_KEYCODE_F13] = INPUT_INVALID, // not implemented
[SAPP_KEYCODE_F14] = INPUT_INVALID, // not implemented
[SAPP_KEYCODE_F15] = INPUT_INVALID, // not implemented
[SAPP_KEYCODE_F16] = INPUT_INVALID, // not implemented
[SAPP_KEYCODE_F17] = INPUT_INVALID, // not implemented
[SAPP_KEYCODE_F18] = INPUT_INVALID, // not implemented
[SAPP_KEYCODE_F19] = INPUT_INVALID, // not implemented
[SAPP_KEYCODE_F20] = INPUT_INVALID, // not implemented
[SAPP_KEYCODE_F21] = INPUT_INVALID, // not implemented
[SAPP_KEYCODE_F22] = INPUT_INVALID, // not implemented
[SAPP_KEYCODE_F23] = INPUT_INVALID, // not implemented
[SAPP_KEYCODE_F24] = INPUT_INVALID, // not implemented
[SAPP_KEYCODE_F25] = INPUT_INVALID, // not implemented
[SAPP_KEYCODE_KP_0] = INPUT_KEY_KP_0,
[SAPP_KEYCODE_KP_1] = INPUT_KEY_KP_1,
[SAPP_KEYCODE_KP_2] = INPUT_KEY_KP_2,
[SAPP_KEYCODE_KP_3] = INPUT_KEY_KP_3,
[SAPP_KEYCODE_KP_4] = INPUT_KEY_KP_4,
[SAPP_KEYCODE_KP_5] = INPUT_KEY_KP_5,
[SAPP_KEYCODE_KP_6] = INPUT_KEY_KP_6,
[SAPP_KEYCODE_KP_7] = INPUT_KEY_KP_7,
[SAPP_KEYCODE_KP_8] = INPUT_KEY_KP_8,
[SAPP_KEYCODE_KP_9] = INPUT_KEY_KP_9,
[SAPP_KEYCODE_KP_DECIMAL] = INPUT_KEY_KP_PERIOD,
[SAPP_KEYCODE_KP_DIVIDE] = INPUT_KEY_KP_DIVIDE,
[SAPP_KEYCODE_KP_MULTIPLY] = INPUT_KEY_KP_MULTIPLY,
[SAPP_KEYCODE_KP_SUBTRACT] = INPUT_KEY_KP_MINUS,
[SAPP_KEYCODE_KP_ADD] = INPUT_KEY_KP_PLUS,
[SAPP_KEYCODE_KP_ENTER] = INPUT_KEY_KP_ENTER,
[SAPP_KEYCODE_KP_EQUAL] = INPUT_INVALID, // not implemented
[SAPP_KEYCODE_LEFT_SHIFT] = INPUT_KEY_LSHIFT,
[SAPP_KEYCODE_LEFT_CONTROL] = INPUT_KEY_LCTRL,
[SAPP_KEYCODE_LEFT_ALT] = INPUT_KEY_LALT,
[SAPP_KEYCODE_LEFT_SUPER] = INPUT_INVALID, // not implemented
[SAPP_KEYCODE_RIGHT_SHIFT] = INPUT_KEY_RSHIFT,
[SAPP_KEYCODE_RIGHT_CONTROL] = INPUT_KEY_RCTRL,
[SAPP_KEYCODE_RIGHT_ALT] = INPUT_KEY_RALT,
[SAPP_KEYCODE_RIGHT_SUPER] = INPUT_INVALID, // not implemented
[SAPP_KEYCODE_MENU] = INPUT_INVALID, // not implemented
};
static void (*audio_callback)(float *buffer, uint32_t len) = NULL;
void platform_exit() {
sapp_quit();
}
vec2i_t platform_screen_size() {
return vec2i(sapp_width(), sapp_height());
}
double platform_now() {
return stm_sec(stm_now());
}
void platform_set_fullscreen(bool fullscreen) {
if (fullscreen == sapp_is_fullscreen()) {
return;
}
sapp_toggle_fullscreen();
}
void platform_handle_event(const sapp_event* ev) {
// Input Keyboard
if (ev->type == SAPP_EVENTTYPE_KEY_DOWN || ev->type == SAPP_EVENTTYPE_KEY_UP) {
float state = ev->type == SAPP_EVENTTYPE_KEY_DOWN ? 1.0 : 0.0;
if (ev->key_code > 0 && ev->key_code < sizeof(keyboard_map)) {
int code = keyboard_map[ev->key_code];
input_set_button_state(code, state);
}
}
else if (ev->type == SAPP_EVENTTYPE_CHAR) {
input_textinput(ev->char_code);
}
// Input Gamepad Axis
// TODO: not implemented by sokol_app itself
// Mouse buttons
else if (
ev->type == SAPP_EVENTTYPE_MOUSE_DOWN ||
ev->type == SAPP_EVENTTYPE_MOUSE_UP
) {
button_t button = INPUT_BUTTON_NONE;
switch (ev->mouse_button) {
case SAPP_MOUSEBUTTON_LEFT: button = INPUT_MOUSE_LEFT; break;
case SAPP_MOUSEBUTTON_MIDDLE: button = INPUT_MOUSE_MIDDLE; break;
case SAPP_MOUSEBUTTON_RIGHT: button = INPUT_MOUSE_RIGHT; break;
default: break;
}
if (button != INPUT_BUTTON_NONE) {
float state = ev->type == SAPP_EVENTTYPE_MOUSE_DOWN ? 1.0 : 0.0;
input_set_button_state(button, state);
}
}
// Mouse wheel
else if (ev->type == SAPP_EVENTTYPE_MOUSE_SCROLL) {
button_t button = ev->scroll_y > 0
? INPUT_MOUSE_WHEEL_UP
: INPUT_MOUSE_WHEEL_DOWN;
input_set_button_state(button, 1.0);
input_set_button_state(button, 0.0);
}
// Mouse move
else if (ev->type == SAPP_EVENTTYPE_MOUSE_MOVE) {
input_set_mouse_pos(ev->mouse_x, ev->mouse_y);
}
// Window Events
if (ev->type == SAPP_EVENTTYPE_RESIZED) {
system_resize(vec2i(ev->window_width, ev->window_height));
}
}
void platform_audio_callback(float* buffer, int num_frames, int num_channels) {
if (audio_callback) {
audio_callback(buffer, num_frames * num_channels);
}
else {
memset(buffer, 0, num_frames * sizeof(float));
}
}
void platform_set_audio_mix_cb(void (*cb)(float *buffer, uint32_t len)) {
audio_callback = cb;
}
sapp_desc sokol_main(int argc, char* argv[]) {
stm_setup();
saudio_setup(&(saudio_desc){
.sample_rate = 44100,
.buffer_frames = 4096,
.num_packets = 256,
.num_channels = 2,
.stream_cb = platform_audio_callback,
});
return (sapp_desc) {
.width = SYSTEM_WINDOW_WIDTH,
.height = SYSTEM_WINDOW_HEIGHT,
.init_cb = system_init,
.frame_cb = system_update,
.cleanup_cb = system_cleanup,
.event_cb = platform_handle_event,
.win32_console_attach = true
};
}
+50
View File
@@ -0,0 +1,50 @@
#ifndef RENDER_H
#define RENDER_H
#include "types.h"
typedef enum {
RENDER_BLEND_NORMAL,
RENDER_BLEND_LIGHTER
} render_blend_mode_t;
#define RENDER_USE_MIPMAPS 1
#define RENDER_FADEOUT_NEAR 48000.0
#define RENDER_FADEOUT_FAR 64000.0
extern uint16_t RENDER_NO_TEXTURE;
void render_init(vec2i_t size);
void render_cleanup();
void render_resize(vec2i_t size);
vec2i_t render_size();
void render_frame_prepare();
void render_frame_end();
void render_set_view(vec3_t pos, vec3_t angles);
void render_set_view_2d();
void render_set_model_mat(mat4_t *m);
void render_set_depth_write(bool enabled);
void render_set_depth_test(bool enabled);
void render_set_depth_offset(float offset);
void render_set_screen_position(vec2_t pos);
void render_set_blend_mode(render_blend_mode_t mode);
void render_set_cull_backface(bool enabled);
vec3_t render_transform(vec3_t pos);
void render_push_tris(tris_t tris, uint16_t texture);
void render_push_sprite(vec3_t pos, vec2i_t size, rgba_t color, uint16_t texture);
void render_push_2d(vec2i_t pos, vec2i_t size, rgba_t color, uint16_t texture);
void render_push_2d_tile(vec2i_t pos, vec2i_t uv_offset, vec2i_t uv_size, vec2i_t size, rgba_t color, uint16_t texture_index);
uint16_t render_texture_create(uint32_t width, uint32_t height, rgba_t *pixels);
vec2i_t render_texture_size(uint16_t texture_index);
void render_texture_replace_pixels(int16_t texture_index, rgba_t *pixels);
uint16_t render_textures_len();
void render_textures_reset(uint16_t len);
void render_textures_dump(const char *path);
#endif
+719
View File
File diff suppressed because it is too large Load Diff
+81
View File
@@ -0,0 +1,81 @@
#include "system.h"
#include "input.h"
#include "render.h"
#include "platform.h"
#include "mem.h"
#include "utils.h"
#include "wipeout/game.h"
static double time_real;
static double time_scaled;
static double time_scale = 1.0;
static double tick_last;
static double cycle_time = 0;
void system_init() {
time_real = platform_now();
input_init();
render_init(platform_screen_size());
game_init();
}
void system_cleanup() {
render_cleanup();
input_cleanup();
}
void system_exit() {
platform_exit();
}
void system_update() {
double time_real_now = platform_now();
double real_delta = time_real_now - time_real;
time_real = time_real_now;
tick_last = min(real_delta, 0.1) * time_scale;
time_scaled += tick_last;
// FIXME: come up with a better way to wrap the cycle_time, so that it
// doesn't lose precission, but also doesn't jump upon reset.
cycle_time = time_scaled;
if (cycle_time > 3600 * M_PI) {
cycle_time -= 3600 * M_PI;
}
render_frame_prepare();
game_update();
render_frame_end();
input_clear();
mem_temp_check();
}
void system_reset_cycle_time() {
cycle_time = 0;
}
void system_resize(vec2i_t size) {
render_resize(size);
}
double system_time_scale_get() {
return time_scale;
}
void system_time_scale_set(double scale) {
time_scale = scale;
}
double system_tick() {
return tick_last;
}
double system_time() {
return time_scaled;
}
double system_cycle_time() {
return cycle_time;
}
Executable
+23
View File
@@ -0,0 +1,23 @@
#ifndef SYSTEM_H
#define SYSTEM_H
#include "types.h"
#define SYSTEM_WINDOW_NAME "wipEout"
#define SYSTEM_WINDOW_WIDTH 1280
#define SYSTEM_WINDOW_HEIGHT 720
void system_init();
void system_update();
void system_cleanup();
void system_exit();
void system_resize(vec2i_t size);
double system_time();
double system_tick();
double system_cycle_time();
void system_reset_cycle_time();
double system_time_scale_get();
void system_time_scale_set(double ts);
#endif

Some files were not shown because too many files have changed in this diff Show More