summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorTom Black <[email protected]>2021-02-08 23:45:14 -0600
committerTom Black <[email protected]>2021-02-08 23:49:38 -0600
commite80f7a44658db4226bac976a2b1af99babbe9e89 (patch)
tree9406b05a30b74b871adc9be817ebcea279069631
parent925208af8c444939813d89e105ffb31594d78206 (diff)
downloadruby2d-e80f7a44658db4226bac976a2b1af99babbe9e89.tar.gz
ruby2d-e80f7a44658db4226bac976a2b1af99babbe9e89.zip
Merge Simple 2D into gem
-rw-r--r--.gitmodules2
-rw-r--r--README.md4
m---------assets0
-rwxr-xr-xbin/ruby2d1
-rw-r--r--ext/ruby2d/common.c185
-rw-r--r--ext/ruby2d/controllers.c110
-rw-r--r--ext/ruby2d/extconf.rb38
-rw-r--r--ext/ruby2d/gl.c430
-rw-r--r--ext/ruby2d/gl2.c146
-rw-r--r--ext/ruby2d/gl3.c348
-rw-r--r--ext/ruby2d/gles.c308
-rw-r--r--ext/ruby2d/image.c138
-rw-r--r--ext/ruby2d/input.c48
-rw-r--r--ext/ruby2d/music.c114
-rw-r--r--ext/ruby2d/ruby2d.c251
-rw-r--r--ext/ruby2d/ruby2d.h757
-rw-r--r--ext/ruby2d/shapes.c154
-rw-r--r--ext/ruby2d/sound.c93
-rw-r--r--ext/ruby2d/sprite.c147
-rw-r--r--ext/ruby2d/text.c129
-rw-r--r--ext/ruby2d/window.c414
-rw-r--r--lib/ruby2d/cli/build.rb9
-rw-r--r--ruby2d.gemspec2
23 files changed, 3656 insertions, 172 deletions
diff --git a/.gitmodules b/.gitmodules
index 2ad3adc..308f192 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -3,4 +3,4 @@
url = https://github.com/simple2d/test_media.git
[submodule "assets"]
path = assets
- url = https://github.com/ruby2d/assets
+ url = https://github.com/ruby2d/assets.git
diff --git a/README.md b/README.md
index 60b242c..401e225 100644
--- a/README.md
+++ b/README.md
@@ -23,10 +23,9 @@ Update these submodules at any time using `git submodule update --remote` or the
Next, install dependencies:
- With [Bundler](http://bundler.io), run `bundle install` to get the required development gems.
-- Install the native graphics library [Simple 2D](https://github.com/simple2d/simple2d) by following the instructions in its README.
- Install [MRuby](http://mruby.org) in order to build Ruby 2D apps natively. On macOS using [Homebrew](https://brew.sh), run `brew install mruby`. On Ubuntu, use `sudo apt install mruby libmruby-dev`
-Finally, run `rake` to build and install the gem locally. Use `rake dev` to build referencing user-installed libraries (e.g. Simple 2D and SDL).
+Finally, run `rake` to build and install the gem locally. Use `rake dev` to build referencing user-installed libraries (e.g. SDL).
## Tests
@@ -73,7 +72,6 @@ In order to achieve such simplicity, a lot has to happen under the hood. Whether
1. Update the [assets](https://github.com/ruby2d/assets) repo, follow the instructions in the README
2. Run `rake update` to update the submodules
-3. Update the Simple 2D minimum version required in [`extconf.rb`](ext/ruby2d/extconf.rb)
## Preparing a release
diff --git a/assets b/assets
-Subproject 166014b677a69c7966edfdffe301947fa4df9dd
+Subproject d640f88bdda5c3882f365190b9b3b6f66e8a7a7
diff --git a/bin/ruby2d b/bin/ruby2d
index 71d5f2b..a915b64 100755
--- a/bin/ruby2d
+++ b/bin/ruby2d
@@ -115,6 +115,7 @@ when 'launch'
else
puts usage_launch
end
+# TODO: Need add this functionality to the gem
when 'simulator'
case ARGV[1]
when '--list'
diff --git a/ext/ruby2d/common.c b/ext/ruby2d/common.c
new file mode 100644
index 0000000..1950049
--- /dev/null
+++ b/ext/ruby2d/common.c
@@ -0,0 +1,185 @@
+// Ruby 2D Shared functions and data
+
+#include "ruby2d.h"
+
+// Initalize shared data
+bool R2D_diagnostics = false;
+
+// Initialization status
+static bool initted = false;
+
+
+/*
+ * Provide a `vasprintf()` implementation for Windows
+ */
+#if WINDOWS && !MINGW
+int vasprintf(char **strp, const char *fmt, va_list ap) {
+ int r = -1, size = _vscprintf(fmt, ap);
+ if ((size >= 0) && (size < INT_MAX)) {
+ *strp = (char *)malloc(size + 1);
+ if (*strp) {
+ r = vsnprintf(*strp, size + 1, fmt, ap);
+ if (r == -1) free(*strp);
+ }
+ } else { *strp = 0; }
+ return(r);
+}
+#endif
+
+
+/*
+ * Checks if a file exists and can be accessed
+ */
+bool R2D_FileExists(const char *path) {
+ if (!path) return false;
+
+ if (access(path, F_OK) != -1) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+
+/*
+ * Logs standard messages to the console
+ */
+void R2D_Log(int type, const char *msg, ...) {
+
+ // Always log if diagnostics set, or if a warning or error message
+ if (R2D_diagnostics || type != R2D_INFO) {
+
+ va_list args;
+ va_start(args, msg);
+
+ switch (type) {
+ case R2D_INFO:
+ printf("\033[1;36mInfo:\033[0m ");
+ break;
+ case R2D_WARN:
+ printf("\033[1;33mWarning:\033[0m ");
+ break;
+ case R2D_ERROR:
+ printf("\033[1;31mError:\033[0m ");
+ break;
+ }
+
+ vprintf(msg, args);
+ printf("\n");
+ va_end(args);
+ }
+}
+
+
+/*
+ * Logs Ruby 2D errors to the console, with caller and message body
+ */
+void R2D_Error(const char *caller, const char *msg, ...) {
+ va_list args;
+ va_start(args, msg);
+ char *fmsg;
+ vasprintf(&fmsg, msg, args);
+ R2D_Log(R2D_ERROR, "(%s) %s", caller, fmsg);
+ free(fmsg);
+ va_end(args);
+}
+
+
+/*
+ * Enable/disable logging of diagnostics
+ */
+void R2D_Diagnostics(bool status) {
+ R2D_diagnostics = status;
+}
+
+
+/*
+ * Enable terminal colors in Windows
+ */
+void R2D_Windows_EnableTerminalColors() {
+ #if WINDOWS
+ HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
+ DWORD dwMode = 0;
+ GetConsoleMode(hOut, &dwMode);
+ dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
+ SetConsoleMode(hOut, dwMode);
+ #endif
+}
+
+
+/*
+ * Initialize Ruby 2D subsystems
+ */
+bool R2D_Init() {
+ if (initted) return true;
+
+ // Enable terminal colors in Windows
+ R2D_Windows_EnableTerminalColors();
+
+ R2D_Log(R2D_INFO, "Initializing Ruby 2D");
+
+ // Initialize SDL
+ if (SDL_Init(SDL_INIT_EVERYTHING) != 0) {
+ R2D_Error("SDL_Init", SDL_GetError());
+ return false;
+ }
+
+ // Initialize SDL_ttf
+ if (TTF_Init() != 0) {
+ R2D_Error("TTF_Init", TTF_GetError());
+ return false;
+ }
+
+ // Initialize SDL_mixer
+ int mix_flags = MIX_INIT_FLAC | MIX_INIT_OGG | MIX_INIT_MP3;
+ int mix_initted = Mix_Init(mix_flags);
+
+ // Bug in SDL2_mixer 2.0.2:
+ // Mix_Init should return OR'ed flags if successful, but returns 0 instead.
+ // Fixed in: https://hg.libsdl.org/SDL_mixer/rev/7fa15b556953
+ const SDL_version *linked_version = Mix_Linked_Version();
+ if (linked_version->major == 2 && linked_version->minor == 0 && linked_version->patch == 2) {
+ // It's version 2.0.2, don't check for Mix_Init errors
+ } else {
+ if ((mix_initted&mix_flags) != mix_flags) {
+ R2D_Error("Mix_Init", Mix_GetError());
+ }
+ }
+
+ if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 4096) != 0) {
+ R2D_Error("Mix_OpenAudio", Mix_GetError());
+ return false;
+ }
+
+ // Call `R2D_Quit` at program exit
+ atexit(R2D_Quit);
+
+ // All subsystems initted
+ initted = true;
+ return true;
+}
+
+
+/*
+ * Gets the primary display's dimensions
+ */
+void R2D_GetDisplayDimensions(int *w, int *h) {
+ R2D_Init();
+ SDL_DisplayMode dm;
+ SDL_GetCurrentDisplayMode(0, &dm);
+ *w = dm.w;
+ *h = dm.h;
+}
+
+
+/*
+ * Quits Ruby 2D subsystems
+ */
+void R2D_Quit() {
+ IMG_Quit();
+ Mix_CloseAudio();
+ Mix_Quit();
+ TTF_Quit();
+ SDL_Quit();
+ initted = false;
+}
diff --git a/ext/ruby2d/controllers.c b/ext/ruby2d/controllers.c
new file mode 100644
index 0000000..1566653
--- /dev/null
+++ b/ext/ruby2d/controllers.c
@@ -0,0 +1,110 @@
+// controllers.c
+
+#include "ruby2d.h"
+
+// Stores the last joystick instance ID seen by the system. These instance IDs
+// are unique and increment with each new joystick connected.
+static int last_intance_id = -1;
+
+
+/*
+ * Add controller mapping from string
+ */
+void R2D_AddControllerMapping(const char *map) {
+ int result = SDL_GameControllerAddMapping(map);
+
+ char guid[33];
+ strncpy(guid, map, 32);
+
+ switch (result) {
+ case 1:
+ R2D_Log(R2D_INFO, "Mapping added for GUID: %s", guid);
+ break;
+ case 0:
+ R2D_Log(R2D_INFO, "Mapping updated for GUID: %s", guid);
+ break;
+ case -1:
+ R2D_Error("SDL_GameControllerAddMapping", SDL_GetError());
+ break;
+ }
+}
+
+
+/*
+ * Add controller mappings from the specified file
+ */
+void R2D_AddControllerMappingsFromFile(const char *path) {
+ if (!R2D_FileExists(path)) {
+ R2D_Log(R2D_WARN, "Controller mappings file not found: %s", path);
+ return;
+ }
+
+ int mappings_added = SDL_GameControllerAddMappingsFromFile(path);
+ if (mappings_added == -1) {
+ R2D_Error("SDL_GameControllerAddMappingsFromFile", SDL_GetError());
+ } else {
+ R2D_Log(R2D_INFO, "Added %i controller mapping(s)", mappings_added);
+ }
+}
+
+
+/*
+ * Check if joystick is a controller
+ */
+bool R2D_IsController(SDL_JoystickID id) {
+ return SDL_GameControllerFromInstanceID(id) == NULL ? false : true;
+}
+
+
+/*
+ * Open controllers and joysticks
+ */
+void R2D_OpenControllers() {
+
+ char guid_str[33];
+
+ // Enumerate joysticks
+ for (int device_index = 0; device_index < SDL_NumJoysticks(); ++device_index) {
+
+ // Check if joystick supports SDL's game controller interface (a mapping is available)
+ if (SDL_IsGameController(device_index)) {
+ SDL_GameController *controller = SDL_GameControllerOpen(device_index);
+ SDL_JoystickID intance_id = SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(controller));
+
+ SDL_JoystickGetGUIDString(
+ SDL_JoystickGetGUID(SDL_GameControllerGetJoystick(controller)),
+ guid_str, 33
+ );
+
+ if (intance_id > last_intance_id) {
+ if (controller) {
+ R2D_Log(R2D_INFO, "Controller #%i: %s\n GUID: %s", intance_id, SDL_GameControllerName(controller), guid_str);
+ } else {
+ R2D_Log(R2D_ERROR, "Could not open controller #%i: %s", intance_id, SDL_GetError());
+ }
+ last_intance_id = intance_id;
+ }
+
+ // Controller interface not supported, try to open as joystick
+ } else {
+ SDL_Joystick *joy = SDL_JoystickOpen(device_index);
+ SDL_JoystickID intance_id = SDL_JoystickInstanceID(joy);
+
+ if (!joy) {
+ R2D_Log(R2D_ERROR, "Could not open controller");
+ } else if(intance_id > last_intance_id) {
+ SDL_JoystickGetGUIDString(
+ SDL_JoystickGetGUID(joy),
+ guid_str, 33
+ );
+ R2D_Log(R2D_INFO,
+ "Controller #%i: %s\n GUID: %s\n Axes: %d\n Buttons: %d\n Balls: %d",
+ intance_id, SDL_JoystickName(joy), guid_str, SDL_JoystickNumAxes(joy),
+ SDL_JoystickNumButtons(joy), SDL_JoystickNumBalls(joy)
+ );
+ R2D_Log(R2D_WARN, "Controller #%i does not have a mapping available", intance_id);
+ last_intance_id = intance_id;
+ }
+ }
+ }
+}
diff --git a/ext/ruby2d/extconf.rb b/ext/ruby2d/extconf.rb
index cfb741e..9aaddbb 100644
--- a/ext/ruby2d/extconf.rb
+++ b/ext/ruby2d/extconf.rb
@@ -1,7 +1,6 @@
require 'mkmf'
require_relative '../../lib/ruby2d/cli/colorize'
-S2D_VERSION = '1.2.0' # Simple 2D minimum version required
$errors = [] # Holds errors
# Set the OS platform
@@ -31,27 +30,6 @@ def print_errors
end
-# Check that Simple 2D is installed and meets minimum version requirements
-def check_s2d
-
- # Simple 2D not installed
- if `which simple2d`.empty?
- $errors << "Ruby 2D uses a native library called Simple 2D, which was not found." <<
- "To install, follow the instructions at #{"ruby2d.com".bold}"
- print_errors; exit
-
- # Simple 2D installed, checking version
- else
- unless Gem::Version.new(`bash simple2d --version`) >= Gem::Version.new(S2D_VERSION)
- $errors << "Simple 2D needs to be updated for this version of Ruby 2D." <<
- "Run the following, then try reinstalling this gem:\n" <<
- " simple2d update".bold
- print_errors; exit
- end
- end
-end
-
-
# Add compiler and linker flags
def add_flags(type, flags)
case type
@@ -106,14 +84,11 @@ def set_rpi_flags
end
-# Use the Simple 2D, SDL, and other libraries installed by the user (not those bundled with the gem)
+# Use SDL and other libraries installed by the user (not those bundled with the gem)
def use_usr_libs
- check_s2d
-
# Add flags
set_rpi_flags
add_flags(:c, '-I/usr/local/include')
- add_flags(:ld, `bash simple2d --libs`)
end
@@ -134,7 +109,6 @@ else
add_flags(:c, '-I../../assets/include')
ldir = "#{Dir.pwd}/../../assets/macos/lib"
- add_flags(:ld, "#{ldir}/libsimple2d.a")
add_flags(:ld, "#{ldir}/libSDL2.a #{ldir}/libSDL2_image.a #{ldir}/libSDL2_mixer.a #{ldir}/libSDL2_ttf.a")
add_flags(:ld, "#{ldir}/libjpeg.a #{ldir}/libpng16.a #{ldir}/libtiff.a #{ldir}/libwebp.a")
add_flags(:ld, "#{ldir}/libmpg123.a #{ldir}/libogg.a #{ldir}/libFLAC.a #{ldir}/libvorbis.a #{ldir}/libvorbisfile.a")
@@ -143,18 +117,14 @@ else
when :linux, :linux_rpi
check_sdl_linux
- simple2d_dir = "#{Dir.pwd}/../../assets/linux/simple2d"
-
- `(cd #{simple2d_dir} && make)`
set_rpi_flags
- add_flags(:c, "-I#{simple2d_dir}/include")
- add_flags(:ld, "#{simple2d_dir}/build/libsimple2d.a -lSDL2 -lSDL2_image -lSDL2_mixer -lSDL2_ttf -lm")
+ add_flags(:ld, "-lSDL2 -lSDL2_image -lSDL2_mixer -lSDL2_ttf -lm")
if $platform == :linux then add_flags(:ld, '-lGL') end
when :windows
add_flags(:c, '-I../../assets/include')
- add_flags(:ld, '-L../../assets/mingw/lib -lsimple2d -lSDL2 -lSDL2_image -lSDL2_mixer -lSDL2_ttf')
+ add_flags(:ld, '-L../../assets/mingw/lib -lSDL2 -lSDL2_image -lSDL2_mixer -lSDL2_ttf')
add_flags(:ld, '-lmingw32 -lopengl32 -lglew32')
# If can't detect the platform, use libraries installed by the user
@@ -165,5 +135,5 @@ end
$LDFLAGS.gsub!(/\n/, ' ') # remove newlines in flags, they can cause problems
-# Create the Makefile
+# Create Makefile
create_makefile('ruby2d/ruby2d')
diff --git a/ext/ruby2d/gl.c b/ext/ruby2d/gl.c
new file mode 100644
index 0000000..a2f14fa
--- /dev/null
+++ b/ext/ruby2d/gl.c
@@ -0,0 +1,430 @@
+// Ruby 2D OpenGL Functions
+
+#include "ruby2d.h"
+
+// Set to `true` to force OpenGL 2.1 (for testing)
+static bool FORCE_GL2 = false;
+
+// Flag set if using OpenGL 2.1
+static bool R2D_GL2 = false;
+
+// The orthographic projection matrix for 2D rendering.
+// Elements 0 and 5 are set in R2D_GL_SetViewport.
+static GLfloat orthoMatrix[16] =
+ { 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ -1.0f, 1.0f, -1.0f, 1.0f };
+
+
+/*
+ * Prints current GL error
+ */
+void R2D_GL_PrintError(char *error) {
+ R2D_Log(R2D_ERROR, "%s (%d)", error, glGetError());
+}
+
+
+/*
+ * Print info about the current OpenGL context
+ */
+void R2D_GL_PrintContextInfo(R2D_Window *window) {
+ R2D_Log(R2D_INFO,
+ "OpenGL Context\n"
+ " GL_VENDOR: %s\n"
+ " GL_RENDERER: %s\n"
+ " GL_VERSION: %s\n"
+ " GL_SHADING_LANGUAGE_VERSION: %s",
+ window->R2D_GL_VENDOR,
+ window->R2D_GL_RENDERER,
+ window->R2D_GL_VERSION,
+ window->R2D_GL_SHADING_LANGUAGE_VERSION
+ );
+}
+
+
+/*
+ * Store info about the current OpenGL context
+ */
+void R2D_GL_StoreContextInfo(R2D_Window *window) {
+
+ window->R2D_GL_VENDOR = glGetString(GL_VENDOR);
+ window->R2D_GL_RENDERER = glGetString(GL_RENDERER);
+ window->R2D_GL_VERSION = glGetString(GL_VERSION);
+
+ // These are not defined in GLES
+ #if GLES
+ window->R2D_GL_MAJOR_VERSION = 0;
+ window->R2D_GL_MINOR_VERSION = 0;
+ #else
+ glGetIntegerv(GL_MAJOR_VERSION, &window->R2D_GL_MAJOR_VERSION);
+ glGetIntegerv(GL_MINOR_VERSION, &window->R2D_GL_MINOR_VERSION);
+ #endif
+
+ window->R2D_GL_SHADING_LANGUAGE_VERSION = glGetString(GL_SHADING_LANGUAGE_VERSION);
+};
+
+
+/*
+ * Creates a shader object, loads shader string, and compiles.
+ * Returns 0 if shader could not be compiled.
+ */
+GLuint R2D_GL_LoadShader(GLenum type, const GLchar *shaderSrc, char *shaderName) {
+
+ // Create the shader object
+ GLuint shader = glCreateShader(type);
+
+ if (shader == 0) {
+ R2D_GL_PrintError("Failed to create shader program");
+ return 0;
+ }
+
+ // Load the shader source
+ glShaderSource(shader, 1, &shaderSrc, NULL);
+
+ // Compile the shader
+ glCompileShader(shader);
+
+ // Check the compile status
+ GLint compiled;
+ glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
+
+ if (!compiled) {
+ GLint infoLen = 0;
+ glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
+
+ if (infoLen > 1) {
+ char *infoLog = malloc(sizeof(char) * infoLen);
+ glGetShaderInfoLog(shader, infoLen, NULL, infoLog);
+ printf("Error compiling shader \"%s\":\n%s\n", shaderName, infoLog);
+ free(infoLog);
+ }
+
+ glDeleteShader(shader);
+ return 0;
+ }
+
+ return shader;
+}
+
+
+/*
+ * Check if shader program was linked
+ */
+int R2D_GL_CheckLinked(GLuint program, char *name) {
+
+ GLint linked;
+ glGetProgramiv(program, GL_LINK_STATUS, &linked);
+
+ if (!linked) {
+ GLint infoLen = 0;
+ glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLen);
+
+ if (infoLen > 1) {
+ char *infoLog = malloc(sizeof(char) * infoLen);
+ glGetProgramInfoLog(program, infoLen, NULL, infoLog);
+ printf("Error linking program `%s`: %s\n", name, infoLog);
+ free(infoLog);
+ }
+
+ glDeleteProgram(program);
+ return GL_FALSE;
+ }
+
+ return GL_TRUE;
+}
+
+
+/*
+ * Calculate the viewport's scaled width and height
+ */
+void R2D_GL_GetViewportScale(R2D_Window *window, int *w, int *h, double *scale) {
+
+ double s = fmin(
+ window->width / (double)window->viewport.width,
+ window->height / (double)window->viewport.height
+ );
+
+ *w = window->viewport.width * s;
+ *h = window->viewport.height * s;
+
+ if (scale) *scale = s;
+}
+
+
+/*
+ * Sets the viewport and matrix projection
+ */
+void R2D_GL_SetViewport(R2D_Window *window) {
+
+ int ortho_w = window->viewport.width;
+ int ortho_h = window->viewport.height;
+ int x, y, w, h; // calculated GL viewport values
+
+ x = 0; y = 0; w = window->width; h = window->height;
+
+ switch (window->viewport.mode) {
+
+ case R2D_FIXED:
+ w = window->orig_width;
+ h = window->orig_height;
+ y = window->height - h;
+ break;
+
+ case R2D_EXPAND:
+ ortho_w = w;
+ ortho_h = h;
+ break;
+
+ case R2D_SCALE:
+ R2D_GL_GetViewportScale(window, &w, &h, NULL);
+ // Center the viewport
+ x = window->width / 2.0 - w/2.0;
+ y = window->height / 2.0 - h/2.0;
+ break;
+
+ case R2D_STRETCH:
+ break;
+ }
+
+ glViewport(x, y, w, h);
+
+ // Set orthographic projection matrix
+ orthoMatrix[0] = 2.0f / (GLfloat)ortho_w;
+ orthoMatrix[5] = -2.0f / (GLfloat)ortho_h;
+
+ #if GLES
+ R2D_GLES_ApplyProjection(orthoMatrix);
+ #else
+ if (R2D_GL2) {
+ R2D_GL2_ApplyProjection(ortho_w, ortho_h);
+ } else {
+ R2D_GL3_ApplyProjection(orthoMatrix);
+ }
+ #endif
+}
+
+
+/*
+ * Initialize OpenGL
+ */
+int R2D_GL_Init(R2D_Window *window) {
+
+ // Specify OpenGL contexts and set attributes
+ #if GLES
+ SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
+ SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
+ SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
+ SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
+ #else
+ // Use legacy OpenGL 2.1
+ if (FORCE_GL2) {
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
+
+ // Request an OpenGL 3.3 forward-compatible core profile
+ } else {
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
+ }
+ #endif
+
+ // Create and store the OpenGL context
+ if (FORCE_GL2) {
+ window->glcontext = NULL;
+ } else {
+ // Ask SDL to create an OpenGL context
+ window->glcontext = SDL_GL_CreateContext(window->sdl);
+ }
+
+ // Check if a valid OpenGL context was created
+ if (window->glcontext) {
+ // Valid context found
+
+ // Initialize OpenGL ES 2.0
+ #if GLES
+ R2D_GLES_Init();
+ R2D_GL_SetViewport(window);
+
+ // Initialize OpenGL 3.3+
+ #else
+ // Initialize GLEW on Windows
+ #if WINDOWS
+ GLenum err = glewInit();
+ if (GLEW_OK != err) R2D_Error("GLEW", glewGetErrorString(err));
+ #endif
+ R2D_GL3_Init();
+ R2D_GL_SetViewport(window);
+ #endif
+
+ // Context could not be created
+ } else {
+
+ #if GLES
+ R2D_Error("GLES / SDL_GL_CreateContext", SDL_GetError());
+
+ #else
+ // Try to fallback using an OpenGL 2.1 context
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
+
+ // Try creating the context again
+ window->glcontext = SDL_GL_CreateContext(window->sdl);
+
+ // Check if this context was created
+ if (window->glcontext) {
+ // Valid context found
+ R2D_GL2 = true;
+ R2D_GL2_Init();
+ R2D_GL_SetViewport(window);
+
+ // Could not create any OpenGL contexts, hard failure
+ } else {
+ R2D_Error("GL2 / SDL_GL_CreateContext", SDL_GetError());
+ R2D_Log(R2D_ERROR, "An OpenGL context could not be created");
+ return -1;
+ }
+ #endif
+ }
+
+ // Store the context and print it if diagnostics is enabled
+ R2D_GL_StoreContextInfo(window);
+ if (R2D_diagnostics) R2D_GL_PrintContextInfo(window);
+
+ return 0;
+}
+
+
+/*
+ * Creates a texture for rendering
+ */
+void R2D_GL_CreateTexture(GLuint *id, GLint format,
+ int w, int h,
+ const GLvoid *data, GLint filter) {
+
+ // If 0, then a new texture; generate name
+ if (*id == 0) glGenTextures(1, id);
+
+ // Bind the named texture to a texturing target
+ glBindTexture(GL_TEXTURE_2D, *id);
+
+ // Specifies the 2D texture image
+ glTexImage2D(
+ GL_TEXTURE_2D, 0, format, w, h,
+ 0, format, GL_UNSIGNED_BYTE, data
+ );
+
+ // Set the filtering mode
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
+}
+
+
+/*
+ * Free a texture
+ */
+void R2D_GL_FreeTexture(GLuint *id) {
+ if (*id != 0) {
+ glDeleteTextures(1, id);
+ *id = 0;
+ }
+}
+
+
+/*
+ * Draw a triangle
+ */
+void R2D_GL_DrawTriangle(GLfloat x1, GLfloat y1,
+ GLfloat r1, GLfloat g1, GLfloat b1, GLfloat a1,
+ GLfloat x2, GLfloat y2,
+ GLfloat r2, GLfloat g2, GLfloat b2, GLfloat a2,
+ GLfloat x3, GLfloat y3,
+ GLfloat r3, GLfloat g3, GLfloat b3, GLfloat a3) {
+
+ #if GLES
+ R2D_GLES_DrawTriangle(x1, y1, r1, g1, b1, a1,
+ x2, y2, r2, g2, b2, a2,
+ x3, y3, r3, g3, b3, a3);
+ #else
+ if (R2D_GL2) {
+ R2D_GL2_DrawTriangle(x1, y1, r1, g1, b1, a1,
+ x2, y2, r2, g2, b2, a2,
+ x3, y3, r3, g3, b3, a3);
+ } else {
+ R2D_GL3_DrawTriangle(x1, y1, r1, g1, b1, a1,
+ x2, y2, r2, g2, b2, a2,
+ x3, y3, r3, g3, b3, a3);
+ }
+ #endif
+}
+
+
+/*
+ * Draw an image
+ */
+void R2D_GL_DrawImage(R2D_Image *img) {
+ #if GLES
+ R2D_GLES_DrawImage(img);
+ #else
+ if (R2D_GL2) {
+ R2D_GL2_DrawImage(img);
+ } else {
+ R2D_GL3_DrawImage(img);
+ }
+ #endif
+}
+
+
+/*
+ * Draw sprite
+ */
+void R2D_GL_DrawSprite(R2D_Sprite *spr) {
+ #if GLES
+ R2D_GLES_DrawSprite(spr);
+ #else
+ if (R2D_GL2) {
+ R2D_GL2_DrawSprite(spr);
+ } else {
+ R2D_GL3_DrawSprite(spr);
+ }
+ #endif
+}
+
+
+/*
+ * Draw text
+ */
+void R2D_GL_DrawText(R2D_Text *txt) {
+ #if GLES
+ R2D_GLES_DrawText(txt);
+ #else
+ if (R2D_GL2) {
+ R2D_GL2_DrawText(txt);
+ } else {
+ R2D_GL3_DrawText(txt);
+ }
+ #endif
+}
+
+
+/*
+ * Render and flush OpenGL buffers
+ */
+void R2D_GL_FlushBuffers() {
+ // Only implemented in our OpenGL 3.3+ and ES 2.0 renderers
+ #if GLES
+ // TODO: R2D_GLES_FlushBuffers();
+ #else
+ if (!R2D_GL2) R2D_GL3_FlushBuffers();
+ #endif
+}
+
+
+/*
+ * Clear buffers to given color values
+ */
+void R2D_GL_Clear(R2D_Color clr) {
+ glClearColor(clr.r, clr.g, clr.b, clr.a);
+ glClear(GL_COLOR_BUFFER_BIT);
+}
diff --git a/ext/ruby2d/gl2.c b/ext/ruby2d/gl2.c
new file mode 100644
index 0000000..92e6408
--- /dev/null
+++ b/ext/ruby2d/gl2.c
@@ -0,0 +1,146 @@
+// OpenGL 2.1
+
+#include "ruby2d.h"
+
+#if !GLES
+
+
+/*
+ * Applies the projection matrix
+ */
+void R2D_GL2_ApplyProjection(int w, int h) {
+
+ // Initialize the projection matrix
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+
+ // Multiply the current matrix with the orthographic matrix
+ glOrtho(0.f, w, h, 0.f, -1.f, 1.f);
+
+ // Initialize the model-view matrix
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+}
+
+
+/*
+ * Initalize OpenGL
+ */
+int R2D_GL2_Init() {
+
+ GLenum error = GL_NO_ERROR;
+
+ // Enable transparency
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ // Check for errors
+ error = glGetError();
+ if (error != GL_NO_ERROR) {
+ R2D_GL_PrintError("OpenGL initialization failed");
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+
+/*
+ * Draw triangle
+ */
+void R2D_GL2_DrawTriangle(GLfloat x1, GLfloat y1,
+ GLfloat r1, GLfloat g1, GLfloat b1, GLfloat a1,
+ GLfloat x2, GLfloat y2,
+ GLfloat r2, GLfloat g2, GLfloat b2, GLfloat a2,
+ GLfloat x3, GLfloat y3,
+ GLfloat r3, GLfloat g3, GLfloat b3, GLfloat a3) {
+
+ glBegin(GL_TRIANGLES);
+ glColor4f(r1, g1, b1, a1); glVertex2f(x1, y1);
+ glColor4f(r2, g2, b2, a2); glVertex2f(x2, y2);
+ glColor4f(r3, g3, b3, a3); glVertex2f(x3, y3);
+ glEnd();
+}
+
+
+/*
+ * Draw texture
+ */
+static void R2D_GL2_DrawTexture(int x, int y, int w, int h,
+ GLfloat angle, GLfloat rx, GLfloat ry,
+ GLfloat r, GLfloat g, GLfloat b, GLfloat a,
+ GLfloat tx1, GLfloat ty1, GLfloat tx2, GLfloat ty2,
+ GLfloat tx3, GLfloat ty3, GLfloat tx4, GLfloat ty4,
+ GLuint texture_id) {
+
+ R2D_GL_Point v1 = { .x = x, .y = y };
+ R2D_GL_Point v2 = { .x = x + w, .y = y };
+ R2D_GL_Point v3 = { .x = x + w, .y = y + h };
+ R2D_GL_Point v4 = { .x = x, .y = y + h };
+
+ // Rotate vertices
+ if (angle != 0) {
+ v1 = R2D_RotatePoint(v1, angle, rx, ry);
+ v2 = R2D_RotatePoint(v2, angle, rx, ry);
+ v3 = R2D_RotatePoint(v3, angle, rx, ry);
+ v4 = R2D_RotatePoint(v4, angle, rx, ry);
+ }
+
+ glEnable(GL_TEXTURE_2D);
+
+ glBindTexture(GL_TEXTURE_2D, texture_id);
+
+ glBegin(GL_QUADS);
+ glColor4f(r, g, b, a);
+ glTexCoord2f(tx1, ty1); glVertex2f(v1.x, v1.y);
+ glTexCoord2f(tx2, ty2); glVertex2f(v2.x, v2.y);
+ glTexCoord2f(tx3, ty3); glVertex2f(v3.x, v3.y);
+ glTexCoord2f(tx4, ty4); glVertex2f(v4.x, v4.y);
+ glEnd();
+
+ glDisable(GL_TEXTURE_2D);
+}
+
+
+/*
+ * Draw image
+ */
+void R2D_GL2_DrawImage(R2D_Image *img) {
+ R2D_GL2_DrawTexture(
+ img->x, img->y, img->width, img->height,
+ img->rotate, img->rx, img->ry,
+ img->color.r, img->color.g, img->color.b, img->color.a,
+ 0.f, 0.f, 1.f, 0.f, 1.f, 1.f, 0.f, 1.f,
+ img->texture_id
+ );
+}
+
+
+/*
+ * Draw sprite
+ */
+void R2D_GL2_DrawSprite(R2D_Sprite *spr) {
+ R2D_GL2_DrawTexture(
+ spr->x, spr->y, spr->width, spr->height,
+ spr->rotate, spr->rx, spr->ry,
+ spr->color.r, spr->color.g, spr->color.b, spr->color.a,
+ spr->tx1, spr->ty1, spr->tx2, spr->ty2, spr->tx3, spr->ty3, spr->tx4, spr->ty4,
+ spr->img->texture_id
+ );
+}
+
+
+/*
+ * Draw text
+ */
+void R2D_GL2_DrawText(R2D_Text *txt) {
+ R2D_GL2_DrawTexture(
+ txt->x, txt->y, txt->width, txt->height,
+ txt->rotate, txt->rx, txt->ry,
+ txt->color.r, txt->color.g, txt->color.b, txt->color.a,
+ 0.f, 0.f, 1.f, 0.f, 1.f, 1.f, 0.f, 1.f,
+ txt->texture_id
+ );
+}
+
+#endif
diff --git a/ext/ruby2d/gl3.c b/ext/ruby2d/gl3.c
new file mode 100644
index 0000000..f7cfe31
--- /dev/null
+++ b/ext/ruby2d/gl3.c
@@ -0,0 +1,348 @@
+// OpenGL 3.3+
+
+#include "ruby2d.h"
+
+// Skip this file if OpenGL ES
+#if !GLES
+
+static GLuint vbo; // our primary vertex buffer object (VBO)
+static GLuint vboSize; // size of the VBO in bytes
+static GLfloat *vboData; // pointer to the VBO data
+static GLfloat *vboDataCurrent; // pointer to the data for the current vertices
+static GLuint vboDataIndex = 0; // index of the current object being rendered
+static GLuint vboObjCapacity = 2500; // number of objects the VBO can store
+static GLuint shaderProgram; // triangle shader program
+static GLuint texShaderProgram; // texture shader program
+static GLuint indices[] = // indices for rendering textured quads
+ { 0, 1, 2,
+ 2, 3, 0 };
+
+
+/*
+ * Applies the projection matrix
+ */
+void R2D_GL3_ApplyProjection(GLfloat orthoMatrix[16]) {
+
+ // Use the program object
+ glUseProgram(shaderProgram);
+
+ // Apply the projection matrix to the triangle shader
+ glUniformMatrix4fv(
+ glGetUniformLocation(shaderProgram, "u_mvpMatrix"),
+ 1, GL_FALSE, orthoMatrix
+ );
+
+ // Use the texture program object
+ glUseProgram(texShaderProgram);
+
+ // Apply the projection matrix to the texture shader
+ glUniformMatrix4fv(
+ glGetUniformLocation(texShaderProgram, "u_mvpMatrix"),
+ 1, GL_FALSE, orthoMatrix
+ );
+}
+
+
+/*
+ * Initalize OpenGL
+ */
+int R2D_GL3_Init() {
+
+ // Enable transparency
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ // Vertex shader source string
+ GLchar vertexSource[] =
+ "#version 150 core\n" // shader version
+
+ "uniform mat4 u_mvpMatrix;" // projection matrix
+
+ // Input attributes to the vertex shader
+ "in vec4 position;" // position value
+ "in vec4 color;" // vertex color
+ "in vec2 texcoord;" // texture coordinates
+
+ // Outputs to the fragment shader
+ "out vec4 Color;" // vertex color
+ "out vec2 Texcoord;" // texture coordinates
+
+ "void main() {"
+ // Send the color and texture coordinates right through to the fragment shader
+ " Color = color;"
+ " Texcoord = texcoord;"
+ // Transform the vertex position using the projection matrix
+ " gl_Position = u_mvpMatrix * position;"
+ "}";
+
+ // Fragment shader source string
+ GLchar fragmentSource[] =
+ "#version 150 core\n" // shader version
+ "in vec4 Color;" // input color from vertex shader
+ "out vec4 outColor;" // output fragment color
+
+ "void main() {"
+ " outColor = Color;" // pass the color right through
+ "}";
+
+ // Fragment shader source string for textures
+ GLchar texFragmentSource[] =
+ "#version 150 core\n" // shader version
+ "in vec4 Color;" // input color from vertex shader
+ "in vec2 Texcoord;" // input texture coordinates
+ "out vec4 outColor;" // output fragment color
+ "uniform sampler2D tex;" // 2D texture unit
+
+ "void main() {"
+ // Apply the texture unit, texture coordinates, and color
+ " outColor = texture(tex, Texcoord) * Color;"
+ "}";
+
+ // Create a vertex array object
+ GLuint vao;
+ glGenVertexArrays(1, &vao);
+ glBindVertexArray(vao);
+
+ // Create a vertex buffer object and allocate data
+ glGenBuffers(1, &vbo);
+ glBindBuffer(GL_ARRAY_BUFFER, vbo);
+ vboSize = vboObjCapacity * sizeof(GLfloat) * 24;
+ vboData = (GLfloat *) malloc(vboSize);
+ vboDataCurrent = vboData;
+
+ // Create an element buffer object
+ GLuint ebo;
+ glGenBuffers(1, &ebo);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
+
+ // Load the vertex and fragment shaders
+ GLuint vertexShader = R2D_GL_LoadShader( GL_VERTEX_SHADER, vertexSource, "GL3 Vertex");
+ GLuint fragmentShader = R2D_GL_LoadShader(GL_FRAGMENT_SHADER, fragmentSource, "GL3 Fragment");
+ GLuint texFragmentShader = R2D_GL_LoadShader(GL_FRAGMENT_SHADER, texFragmentSource, "GL3 Texture Fragment");
+
+ // Triangle Shader //
+
+ // Create the shader program object
+ shaderProgram = glCreateProgram();
+
+ // Check if program was created successfully
+ if (shaderProgram == 0) {
+ R2D_GL_PrintError("Failed to create shader program");
+ return GL_FALSE;
+ }
+
+ // Attach the shader objects to the program object
+ glAttachShader(shaderProgram, vertexShader);
+ glAttachShader(shaderProgram, fragmentShader);
+
+ // Bind the output color variable to the fragment shader color number
+ glBindFragDataLocation(shaderProgram, 0, "outColor");
+
+ // Link the shader program
+ glLinkProgram(shaderProgram);
+
+ // Check if linked
+ R2D_GL_CheckLinked(shaderProgram, "GL3 shader");
+
+ // Specify the layout of the position vertex data...
+ GLint posAttrib = glGetAttribLocation(shaderProgram, "position");
+ glEnableVertexAttribArray(posAttrib);
+ glVertexAttribPointer(posAttrib, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), 0);
+
+ // ...and the color vertex data
+ GLint colAttrib = glGetAttribLocation(shaderProgram, "color");
+ glEnableVertexAttribArray(colAttrib);
+ glVertexAttribPointer(colAttrib, 4, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (void*)(2 * sizeof(GLfloat)));
+
+ // Texture Shader //
+
+ // Create the texture shader program object
+ texShaderProgram = glCreateProgram();
+
+ // Check if program was created successfully
+ if (texShaderProgram == 0) {
+ R2D_GL_PrintError("Failed to create shader program");
+ return GL_FALSE;
+ }
+
+ // Attach the shader objects to the program object
+ glAttachShader(texShaderProgram, vertexShader);
+ glAttachShader(texShaderProgram, texFragmentShader);
+
+ // Bind the output color variable to the fragment shader color number
+ glBindFragDataLocation(texShaderProgram, 0, "outColor");
+
+ // Link the shader program
+ glLinkProgram(texShaderProgram);
+
+ // Check if linked
+ R2D_GL_CheckLinked(texShaderProgram, "GL3 texture shader");
+
+ // Specify the layout of the position vertex data...
+ posAttrib = glGetAttribLocation(texShaderProgram, "position");
+ glVertexAttribPointer(posAttrib, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), 0);
+ glEnableVertexAttribArray(posAttrib);
+
+ // ...and the color vertex data...
+ colAttrib = glGetAttribLocation(texShaderProgram, "color");
+ glVertexAttribPointer(colAttrib, 4, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (void*)(2 * sizeof(GLfloat)));
+ glEnableVertexAttribArray(colAttrib);
+
+ // ...and the texture coordinates
+ GLint texAttrib = glGetAttribLocation(texShaderProgram, "texcoord");
+ glVertexAttribPointer(texAttrib, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (void*)(6 * sizeof(GLfloat)));
+ glEnableVertexAttribArray(texAttrib);
+
+ // Clean up
+ glDeleteShader(vertexShader);
+ glDeleteShader(fragmentShader);
+ glDeleteShader(texFragmentShader);
+
+ // If successful, return true
+ return GL_TRUE;
+}
+
+
+/*
+ * Render the vertex buffer and reset it
+ */
+void R2D_GL3_FlushBuffers() {
+
+ // Use the triangle shader program
+ glUseProgram(shaderProgram);
+
+ // Bind to the vertex buffer object and update its data
+ glBindBuffer(GL_ARRAY_BUFFER, vbo);
+ glBufferData(GL_ARRAY_BUFFER, vboSize, NULL, GL_DYNAMIC_DRAW);
+ glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(GLfloat) * vboDataIndex * 24, vboData);
+
+ // Render all the triangles in the buffer
+ glDrawArrays(GL_TRIANGLES, 0, (GLsizei)(vboDataIndex * 3));
+
+ // Reset the buffer object index and data pointer
+ vboDataIndex = 0;
+ vboDataCurrent = vboData;
+}
+
+
+/*
+ * Draw triangle
+ */
+void R2D_GL3_DrawTriangle(GLfloat x1, GLfloat y1,
+ GLfloat r1, GLfloat g1, GLfloat b1, GLfloat a1,
+ GLfloat x2, GLfloat y2,
+ GLfloat r2, GLfloat g2, GLfloat b2, GLfloat a2,
+ GLfloat x3, GLfloat y3,
+ GLfloat r3, GLfloat g3, GLfloat b3, GLfloat a3) {
+
+ // If buffer is full, flush it
+ if (vboDataIndex >= vboObjCapacity) R2D_GL3_FlushBuffers();
+
+ // Set the triangle data into a formatted array
+ GLfloat vertices[] =
+ { x1, y1, r1, g1, b1, a1, 0, 0,
+ x2, y2, r2, g2, b2, a2, 0, 0,
+ x3, y3, r3, g3, b3, a3, 0, 0 };
+
+ // Copy the vertex data into the current position of the buffer
+ memcpy(vboDataCurrent, vertices, sizeof(vertices));
+
+ // Increment the buffer object index and the vertex data pointer for next use
+ vboDataIndex++;
+ vboDataCurrent = (GLfloat *)((char *)vboDataCurrent + (sizeof(GLfloat) * 24));
+}
+
+
+/*
+ * Draw a texture
+ */
+static void R2D_GL3_DrawTexture(int x, int y, int w, int h,
+ GLfloat angle, GLfloat rx, GLfloat ry,
+ GLfloat r, GLfloat g, GLfloat b, GLfloat a,
+ GLfloat tx1, GLfloat ty1, GLfloat tx2, GLfloat ty2,
+ GLfloat tx3, GLfloat ty3, GLfloat tx4, GLfloat ty4,
+ GLuint texture_id) {
+
+ // Currently, textures are not buffered, so we have to flush all buffers so
+ // textures get rendered in the correct Z order
+ R2D_GL3_FlushBuffers();
+
+ // Set up the vertex points
+ R2D_GL_Point v1 = { .x = x, .y = y };
+ R2D_GL_Point v2 = { .x = x + w, .y = y };
+ R2D_GL_Point v3 = { .x = x + w, .y = y + h };
+ R2D_GL_Point v4 = { .x = x, .y = y + h };
+
+ // Rotate vertices
+ if (angle != 0) {
+ v1 = R2D_RotatePoint(v1, angle, rx, ry);
+ v2 = R2D_RotatePoint(v2, angle, rx, ry);
+ v3 = R2D_RotatePoint(v3, angle, rx, ry);
+ v4 = R2D_RotatePoint(v4, angle, rx, ry);
+ }
+
+ // Set the textured quad data into a formatted array
+ GLfloat vertices[] =
+ // vertex coords | colors | x, y texture coords
+ { v1.x, v1.y, r, g, b, a, tx1, ty1, // Top-left
+ v2.x, v2.y, r, g, b, a, tx2, ty2, // Top-right
+ v3.x, v3.y, r, g, b, a, tx3, ty3, // Bottom-right
+ v4.x, v4.y, r, g, b, a, tx4, ty4 }; // Bottom-left
+
+ // Use the texture shader program
+ glUseProgram(texShaderProgram);
+
+ // Bind the texture using the provided ID
+ glBindTexture(GL_TEXTURE_2D, texture_id);
+
+ // Create and Initialize the vertex data and array indices
+ glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
+
+ // Render the textured quad
+ glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
+}
+
+
+/*
+ * Draw image
+ */
+void R2D_GL3_DrawImage(R2D_Image *img) {
+ R2D_GL3_DrawTexture(
+ img->x, img->y, img->width, img->height,
+ img->rotate, img->rx, img->ry,
+ img->color.r, img->color.g, img->color.b, img->color.a,
+ 0.f, 0.f, 1.f, 0.f, 1.f, 1.f, 0.f, 1.f,
+ img->texture_id
+ );
+}
+
+
+/*
+ * Draw sprite
+ */
+void R2D_GL3_DrawSprite(R2D_Sprite *spr) {
+ R2D_GL3_DrawTexture(
+ spr->x, spr->y, spr->width, spr->height,
+ spr->rotate, spr->rx, spr->ry,
+ spr->color.r, spr->color.g, spr->color.b, spr->color.a,
+ spr->tx1, spr->ty1, spr->tx2, spr->ty2, spr->tx3, spr->ty3, spr->tx4, spr->ty4,
+ spr->img->texture_id
+ );
+}
+
+
+/*
+ * Draw text
+ */
+void R2D_GL3_DrawText(R2D_Text *txt) {
+ R2D_GL3_DrawTexture(
+ txt->x, txt->y, txt->width, txt->height,
+ txt->rotate, txt->rx, txt->ry,
+ txt->color.r, txt->color.g, txt->color.b, txt->color.a,
+ 0.f, 0.f, 1.f, 0.f, 1.f, 1.f, 0.f, 1.f,
+ txt->texture_id
+ );
+}
+
+#endif
diff --git a/ext/ruby2d/gles.c b/ext/ruby2d/gles.c
new file mode 100644
index 0000000..81da94c
--- /dev/null
+++ b/ext/ruby2d/gles.c
@@ -0,0 +1,308 @@
+// OpenGL ES 2.0
+
+#include "ruby2d.h"
+
+#if GLES
+
+// Triangle shader
+static GLuint shaderProgram;
+static GLuint positionLocation;
+static GLuint colorLocation;
+
+// Texture shader
+static GLuint texShaderProgram;
+static GLuint texPositionLocation;
+static GLuint texColorLocation;
+static GLuint texCoordLocation;
+static GLuint samplerLocation;
+
+static GLushort indices[] =
+ { 0, 1, 2,
+ 2, 3, 0 };
+
+
+/*
+ * Applies the projection matrix
+ */
+void R2D_GLES_ApplyProjection(GLfloat orthoMatrix[16]) {
+
+ // Use the program object
+ glUseProgram(shaderProgram);
+
+ glUniformMatrix4fv(
+ glGetUniformLocation(shaderProgram, "u_mvpMatrix"),
+ 1, GL_FALSE, orthoMatrix
+ );
+
+ // Use the texture program object
+ glUseProgram(texShaderProgram);
+
+ glUniformMatrix4fv(
+ glGetUniformLocation(texShaderProgram, "u_mvpMatrix"),
+ 1, GL_FALSE, orthoMatrix
+ );
+}
+
+
+/*
+ * Initalize OpenGL ES
+ */
+int R2D_GLES_Init() {
+
+ // Enable transparency
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ // Vertex shader source string
+ GLchar vertexSource[] =
+ // uniforms used by the vertex shader
+ "uniform mat4 u_mvpMatrix;" // projection matrix
+
+ // attributes input to the vertex shader
+ "attribute vec4 a_position;" // position value
+ "attribute vec4 a_color;" // input vertex color
+ "attribute vec2 a_texcoord;" // input texture
+
+ // varying variables, input to the fragment shader
+ "varying vec4 v_color;" // output vertex color
+ "varying vec2 v_texcoord;" // output texture
+
+ "void main()"
+ "{"
+ " v_color = a_color;"
+ " v_texcoord = a_texcoord;"
+ " gl_Position = u_mvpMatrix * a_position;"
+ "}";
+
+ // Fragment shader source string
+ GLchar fragmentSource[] =
+ "precision mediump float;"
+ // input vertex color from vertex shader
+ "varying vec4 v_color;"
+
+ "void main()"
+ "{"
+ " gl_FragColor = v_color;"
+ "}";
+
+ // Fragment shader source string for textures
+ GLchar texFragmentSource[] =
+ "precision mediump float;"
+ // input vertex color from vertex shader
+ "varying vec4 v_color;"
+ "varying vec2 v_texcoord;"
+ "uniform sampler2D s_texture;"
+
+ "void main()"
+ "{"
+ " gl_FragColor = texture2D(s_texture, v_texcoord) * v_color;"
+ "}";
+
+ // Load the vertex and fragment shaders
+ GLuint vertexShader = R2D_GL_LoadShader( GL_VERTEX_SHADER, vertexSource, "GLES Vertex");
+ GLuint fragmentShader = R2D_GL_LoadShader(GL_FRAGMENT_SHADER, fragmentSource, "GLES Fragment");
+ GLuint texFragmentShader = R2D_GL_LoadShader(GL_FRAGMENT_SHADER, texFragmentSource, "GLES Texture Fragment");
+
+ // Triangle Shader //
+
+ // Create the shader program object
+ shaderProgram = glCreateProgram();
+
+ // Check if program was created successfully
+ if (shaderProgram == 0) {
+ R2D_GL_PrintError("Failed to create shader program");
+ return GL_FALSE;
+ }
+
+ // Attach the shader objects to the program object
+ glAttachShader(shaderProgram, vertexShader);
+ glAttachShader(shaderProgram, fragmentShader);
+
+ // Link the shader program
+ glLinkProgram(shaderProgram);
+
+ // Check if linked
+ R2D_GL_CheckLinked(shaderProgram, "GLES shader");
+
+ // Get the attribute locations
+ positionLocation = glGetAttribLocation(shaderProgram, "a_position");
+ colorLocation = glGetAttribLocation(shaderProgram, "a_color");
+
+ // Texture Shader //
+
+ // Create the texture shader program object
+ texShaderProgram = glCreateProgram();
+
+ // Check if program was created successfully
+ if (texShaderProgram == 0) {
+ R2D_GL_PrintError("Failed to create shader program");
+ return GL_FALSE;
+ }
+
+ // Attach the shader objects to the program object
+ glAttachShader(texShaderProgram, vertexShader);
+ glAttachShader(texShaderProgram, texFragmentShader);
+
+ // Link the shader program
+ glLinkProgram(texShaderProgram);
+
+ // Check if linked
+ R2D_GL_CheckLinked(texShaderProgram, "GLES texture shader");
+
+ // Get the attribute locations
+ texPositionLocation = glGetAttribLocation(texShaderProgram, "a_position");
+ texColorLocation = glGetAttribLocation(texShaderProgram, "a_color");
+ texCoordLocation = glGetAttribLocation(texShaderProgram, "a_texcoord");
+
+ // Get the sampler location
+ samplerLocation = glGetUniformLocation(texShaderProgram, "s_texture");
+
+ // Clean up
+ glDeleteShader(vertexShader);
+ glDeleteShader(fragmentShader);
+ glDeleteShader(texFragmentShader);
+
+ return GL_TRUE;
+}
+
+
+/*
+ * Draw triangle
+ */
+void R2D_GLES_DrawTriangle(GLfloat x1, GLfloat y1,
+ GLfloat r1, GLfloat g1, GLfloat b1, GLfloat a1,
+ GLfloat x2, GLfloat y2,
+ GLfloat r2, GLfloat g2, GLfloat b2, GLfloat a2,
+ GLfloat x3, GLfloat y3,
+ GLfloat r3, GLfloat g3, GLfloat b3, GLfloat a3) {
+
+ GLfloat vertices[] =
+ { x1, y1, 0.f,
+ x2, y2, 0.f,
+ x3, y3, 0.f };
+
+ GLfloat colors[] =
+ { r1, g1, b1, a1,
+ r2, g2, b2, a2,
+ r3, g3, b3, a3 };
+
+ glUseProgram(shaderProgram);
+
+ // Load the vertex position
+ glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, vertices);
+ glEnableVertexAttribArray(positionLocation);
+
+ // Load the colors
+ glVertexAttribPointer(colorLocation, 4, GL_FLOAT, GL_FALSE, 0, colors);
+ glEnableVertexAttribArray(colorLocation);
+
+ // draw
+ glDrawArrays(GL_TRIANGLES, 0, 3);
+}
+
+
+/*
+ * Draw a texture
+ */
+static void R2D_GLES_DrawTexture(int x, int y, int w, int h,
+ GLfloat angle, GLfloat rx, GLfloat ry,
+ GLfloat r, GLfloat g, GLfloat b, GLfloat a,
+ GLfloat tx1, GLfloat ty1, GLfloat tx2, GLfloat ty2,
+ GLfloat tx3, GLfloat ty3, GLfloat tx4, GLfloat ty4,
+ GLuint texture_id) {
+
+ R2D_GL_Point v1 = { .x = x, .y = y };
+ R2D_GL_Point v2 = { .x = x + w, .y = y };
+ R2D_GL_Point v3 = { .x = x + w, .y = y + h };
+ R2D_GL_Point v4 = { .x = x, .y = y + h };
+
+ // Rotate vertices
+ if (angle != 0) {
+ v1 = R2D_RotatePoint(v1, angle, rx, ry);
+ v2 = R2D_RotatePoint(v2, angle, rx, ry);
+ v3 = R2D_RotatePoint(v3, angle, rx, ry);
+ v4 = R2D_RotatePoint(v4, angle, rx, ry);
+ }
+
+ GLfloat vertices[] =
+ // x, y coords | x, y texture coords
+ { v1.x, v1.y, 0.f, tx1, ty1,
+ v2.x, v2.y, 0.f, tx2, ty2,
+ v3.x, v3.y, 0.f, tx3, ty3,
+ v4.x, v4.y, 0.f, tx4, ty4 };
+
+ GLfloat colors[] =
+ { r, g, b, a,
+ r, g, b, a,
+ r, g, b, a,
+ r, g, b, a };
+
+ glUseProgram(texShaderProgram);
+
+ // Load the vertex position
+ glVertexAttribPointer(texPositionLocation, 3, GL_FLOAT, GL_FALSE,
+ 5 * sizeof(GLfloat), vertices);
+ glEnableVertexAttribArray(texPositionLocation);
+
+ // Load the colors
+ glVertexAttribPointer(texColorLocation, 4, GL_FLOAT, GL_FALSE, 0, colors);
+ glEnableVertexAttribArray(texColorLocation);
+
+ // Load the texture coordinate
+ glVertexAttribPointer(texCoordLocation, 2, GL_FLOAT, GL_FALSE,
+ 5 * sizeof(GLfloat), &vertices[3]);
+ glEnableVertexAttribArray(texCoordLocation);
+
+ // Bind the texture
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, texture_id);
+
+ // Set the sampler texture unit to 0
+ glUniform1i(samplerLocation, 0);
+
+ glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices);
+}
+
+
+/*
+ * Draw image
+ */
+void R2D_GLES_DrawImage(R2D_Image *img) {
+ R2D_GLES_DrawTexture(
+ img->x, img->y, img->width, img->height,
+ img->rotate, img->rx, img->ry,
+ img->color.r, img->color.g, img->color.b, img->color.a,
+ 0.f, 0.f, 1.f, 0.f, 1.f, 1.f, 0.f, 1.f,
+ img->texture_id
+ );
+}
+
+
+/*
+ * Draw sprite
+ */
+void R2D_GLES_DrawSprite(R2D_Sprite *spr) {
+ R2D_GLES_DrawTexture(
+ spr->x, spr->y, spr->width, spr->height,
+ spr->rotate, spr->rx, spr->ry,
+ spr->color.r, spr->color.g, spr->color.b, spr->color.a,
+ spr->tx1, spr->ty1, spr->tx2, spr->ty2, spr->tx3, spr->ty3, spr->tx4, spr->ty4,
+ spr->img->texture_id
+ );
+}
+
+
+/*
+ * Draw text
+ */
+void R2D_GLES_DrawText(R2D_Text *txt) {
+ R2D_GLES_DrawTexture(
+ txt->x, txt->y, txt->width, txt->height,
+ txt->rotate, txt->rx, txt->ry,
+ txt->color.r, txt->color.g, txt->color.b, txt->color.a,
+ 0.f, 0.f, 1.f, 0.f, 1.f, 1.f, 0.f, 1.f,
+ txt->texture_id
+ );
+}
+
+#endif
diff --git a/ext/ruby2d/image.c b/ext/ruby2d/image.c
new file mode 100644
index 0000000..68d0c30
--- /dev/null
+++ b/ext/ruby2d/image.c
@@ -0,0 +1,138 @@
+// image.c
+
+#include "ruby2d.h"
+
+
+/*
+ * Create an image, given a file path
+ */
+R2D_Image *R2D_CreateImage(const char *path) {
+ R2D_Init();
+
+ // Check if image file exists
+ if (!R2D_FileExists(path)) {
+ R2D_Error("R2D_CreateImage", "Image file `%s` not found", path);
+ return NULL;
+ }
+
+ // Allocate the image structure
+ R2D_Image *img = (R2D_Image *) malloc(sizeof(R2D_Image));
+ if (!img) {
+ R2D_Error("R2D_CreateImage", "Out of memory!");
+ return NULL;
+ }
+
+ // Load image from file as SDL_Surface
+ img->surface = IMG_Load(path);
+ if (!img->surface) {
+ R2D_Error("IMG_Load", IMG_GetError());
+ free(img);
+ return NULL;
+ }
+
+ int bits_per_color = img->surface->format->Amask == 0 ?
+ img->surface->format->BitsPerPixel / 3 :
+ img->surface->format->BitsPerPixel / 4;
+
+ if (bits_per_color < 8) {
+ R2D_Log(R2D_WARN, "`%s` has less than 8 bits per color and will likely not render correctly", path, bits_per_color);
+ }
+
+ // Initialize values
+ img->path = path;
+ img->x = 0;
+ img->y = 0;
+ img->color.r = 1.f;
+ img->color.g = 1.f;
+ img->color.b = 1.f;
+ img->color.a = 1.f;
+ img->orig_width = img->surface->w;
+ img->orig_height = img->surface->h;
+ img->width = img->orig_width;
+ img->height = img->orig_height;
+ img->rotate = 0;
+ img->rx = 0;
+ img->ry = 0;
+ img->texture_id = 0;
+
+ // Detect image mode
+ img->format = GL_RGB;
+ if (img->surface->format->BytesPerPixel == 4) {
+ img->format = GL_RGBA;
+ }
+
+ // Flip image bits if BGA
+
+ Uint32 r = img->surface->format->Rmask;
+ Uint32 g = img->surface->format->Gmask;
+ Uint32 a = img->surface->format->Amask;
+
+ if (r&0xFF000000 || r&0xFF0000) {
+ char *p = (char *)img->surface->pixels;
+ int bpp = img->surface->format->BytesPerPixel;
+ int w = img->surface->w;
+ int h = img->surface->h;
+ char tmp;
+ for (int i = 0; i < bpp * w * h; i += bpp) {
+ if (a&0xFF) {
+ tmp = p[i];
+ p[i] = p[i+3];
+ p[i+3] = tmp;
+ }
+ if (g&0xFF0000) {
+ tmp = p[i+1];
+ p[i+1] = p[i+2];
+ p[i+2] = tmp;
+ }
+ if (r&0xFF0000) {
+ tmp = p[i];
+ p[i] = p[i+2];
+ p[i+2] = tmp;
+ }
+ }
+ }
+
+ return img;
+}
+
+
+/*
+ * Rotate an image
+ */
+void R2D_RotateImage(R2D_Image *img, GLfloat angle, int position) {
+
+ R2D_GL_Point p = R2D_GetRectRotationPoint(
+ img->x, img->y, img->width, img->height, position
+ );
+
+ img->rotate = angle;
+ img->rx = p.x;
+ img->ry = p.y;
+}
+
+
+/*
+ * Draw an image
+ */
+void R2D_DrawImage(R2D_Image *img) {
+ if (!img) return;
+
+ if (img->texture_id == 0) {
+ R2D_GL_CreateTexture(&img->texture_id, img->format,
+ img->orig_width, img->orig_height,
+ img->surface->pixels, GL_NEAREST);
+ SDL_FreeSurface(img->surface);
+ }
+
+ R2D_GL_DrawImage(img);
+}
+
+
+/*
+ * Free an image
+ */
+void R2D_FreeImage(R2D_Image *img) {
+ if (!img) return;
+ R2D_GL_FreeTexture(&img->texture_id);
+ free(img);
+}
diff --git a/ext/ruby2d/input.c b/ext/ruby2d/input.c
new file mode 100644
index 0000000..d992d4d
--- /dev/null
+++ b/ext/ruby2d/input.c
@@ -0,0 +1,48 @@
+// input.c
+
+#include "ruby2d.h"
+
+
+/*
+ * Get the mouse coordinates relative to the viewport
+ */
+void R2D_GetMouseOnViewport(R2D_Window *window, int wx, int wy, int *x, int *y) {
+
+ double scale; // viewport scale factor
+ int w, h; // width and height of scaled viewport
+
+ switch (window->viewport.mode) {
+
+ case R2D_FIXED: case R2D_EXPAND:
+ *x = wx / (window->orig_width / (double)window->viewport.width);
+ *y = wy / (window->orig_height / (double)window->viewport.height);
+ break;
+
+ case R2D_SCALE:
+ R2D_GL_GetViewportScale(window, &w, &h, &scale);
+ *x = wx * 1 / scale - (window->width - w) / (2.0 * scale);
+ *y = wy * 1 / scale - (window->height - h) / (2.0 * scale);
+ break;
+
+ case R2D_STRETCH:
+ *x = wx * window->viewport.width / (double)window->width;
+ *y = wy * window->viewport.height / (double)window->height;
+ break;
+ }
+}
+
+
+/*
+ * Show the cursor over the window
+ */
+void R2D_ShowCursor() {
+ SDL_ShowCursor(SDL_ENABLE);
+}
+
+
+/*
+ * Hide the cursor over the window
+ */
+void R2D_HideCursor() {
+ SDL_ShowCursor(SDL_DISABLE);
+}
diff --git a/ext/ruby2d/music.c b/ext/ruby2d/music.c
new file mode 100644
index 0000000..0c1716f
--- /dev/null
+++ b/ext/ruby2d/music.c
@@ -0,0 +1,114 @@
+// music.c
+
+#include "ruby2d.h"
+
+
+/*
+ * Create the music
+ */
+R2D_Music *R2D_CreateMusic(const char *path) {
+ R2D_Init();
+
+ // Check if music file exists
+ if (!R2D_FileExists(path)) {
+ R2D_Error("R2D_CreateMusic", "Music file `%s` not found", path);
+ return NULL;
+ }
+
+ // Allocate the music structure
+ R2D_Music *mus = (R2D_Music *) malloc(sizeof(R2D_Music));
+ if (!mus) {
+ R2D_Error("R2D_CreateMusic", "Out of memory!");
+ return NULL;
+ }
+
+ // Load the music data from file
+ mus->data = Mix_LoadMUS(path);
+ if (!mus->data) {
+ R2D_Error("Mix_LoadMUS", Mix_GetError());
+ free(mus);
+ return NULL;
+ }
+
+ // Initialize values
+ mus->path = path;
+
+ return mus;
+}
+
+
+/*
+ * Play the music
+ */
+void R2D_PlayMusic(R2D_Music *mus, bool loop) {
+ if (!mus) return;
+
+ // If looping, set to -1 times; else 0
+ int times = loop ? -1 : 0;
+
+ // times: 0 == once, -1 == forever
+ if (Mix_PlayMusic(mus->data, times) == -1) {
+ // No music for you
+ R2D_Error("R2D_PlayMusic", Mix_GetError());
+ }
+}
+
+
+/*
+ * Pause the playing music
+ */
+void R2D_PauseMusic() {
+ Mix_PauseMusic();
+}
+
+
+/*
+ * Resume the current music
+ */
+void R2D_ResumeMusic() {
+ Mix_ResumeMusic();
+}
+
+
+/*
+ * Stop the playing music; interrupts fader effects
+ */
+void R2D_StopMusic() {
+ Mix_HaltMusic();
+}
+
+
+/*
+ * Get the music volume
+ */
+int R2D_GetMusicVolume() {
+ // Get music volume as percentage of maximum mix volume
+ return ceil(Mix_VolumeMusic(-1) * (100.0 / MIX_MAX_VOLUME));
+}
+
+
+/*
+ * Set the music volume a given percentage
+ */
+void R2D_SetMusicVolume(int volume) {
+ // Set volume to be a percentage of the maximum mix volume
+ Mix_VolumeMusic((volume / 100.0) * MIX_MAX_VOLUME);
+}
+
+
+/*
+ * Fade out the playing music
+ */
+void R2D_FadeOutMusic(int ms) {
+ Mix_FadeOutMusic(ms);
+}
+
+
+/*
+ * Free the music
+ */
+void R2D_FreeMusic(R2D_Music *mus) {
+ if (!mus) return;
+ Mix_FreeMusic(mus->data);
+ free(mus);
+}
diff --git a/ext/ruby2d/ruby2d.c b/ext/ruby2d/ruby2d.c
index bad3ae1..655a44a 100644
--- a/ext/ruby2d/ruby2d.c
+++ b/ext/ruby2d/ruby2d.c
@@ -1,10 +1,9 @@
// Native C extension for Ruby and MRuby
-// Simple 2D includes
+// Ruby 2D includes
#if RUBY2D_IOS_TVOS
- #include <Simple2D/simple2d.h>
#else
- #include <simple2d.h>
+ #include <ruby2d.h>
#endif
// Ruby includes
@@ -100,11 +99,11 @@
static mrb_state *mrb;
#endif
-// Ruby 2D window
+// Ruby 2D interpreter window
static R_VAL ruby2d_window;
-// Simple 2D window
-static S2D_Window *window;
+// Ruby 2D native window
+static R2D_Window *window;
// Method signatures and structures for Ruby 2D classes
@@ -130,19 +129,19 @@ static S2D_Window *window;
"music", free_music
};
#else
- static void free_image(S2D_Image *img);
- static void free_sprite(S2D_Sprite *spr);
- static void free_text(S2D_Text *txt);
- static void free_sound(S2D_Sound *snd);
- static void free_music(S2D_Music *mus);
+ static void free_image(R2D_Image *img);
+ static void free_sprite(R2D_Sprite *spr);
+ static void free_text(R2D_Text *txt);
+ static void free_sound(R2D_Sound *snd);
+ static void free_music(R2D_Music *mus);
#endif
/*
- * Function pointer to free the Simple 2D window
+ * Function pointer to free the Ruby 2D native window
*/
static void free_window() {
- S2D_FreeWindow(window);
+ R2D_FreeWindow(window);
}
@@ -178,7 +177,7 @@ static R_VAL ruby2d_triangle_ext_render(R_VAL self) {
R_VAL c2 = r_iv_get(self, "@c2");
R_VAL c3 = r_iv_get(self, "@c3");
- S2D_DrawTriangle(
+ R2D_DrawTriangle(
NUM2DBL(r_iv_get(self, "@x1")),
NUM2DBL(r_iv_get(self, "@y1")),
NUM2DBL(r_iv_get(c1, "@r")),
@@ -218,7 +217,7 @@ static R_VAL ruby2d_quad_ext_render(R_VAL self) {
R_VAL c3 = r_iv_get(self, "@c3");
R_VAL c4 = r_iv_get(self, "@c4");
- S2D_DrawQuad(
+ R2D_DrawQuad(
NUM2DBL(r_iv_get(self, "@x1")),
NUM2DBL(r_iv_get(self, "@y1")),
NUM2DBL(r_iv_get(c1, "@r")),
@@ -265,7 +264,7 @@ static R_VAL ruby2d_line_ext_render(R_VAL self) {
R_VAL c3 = r_iv_get(self, "@c3");
R_VAL c4 = r_iv_get(self, "@c4");
- S2D_DrawLine(
+ R2D_DrawLine(
NUM2DBL(r_iv_get(self, "@x1")),
NUM2DBL(r_iv_get(self, "@y1")),
NUM2DBL(r_iv_get(self, "@x2")),
@@ -307,7 +306,7 @@ static R_VAL ruby2d_circle_ext_render(R_VAL self) {
#endif
R_VAL c = r_iv_get(self, "@color");
- S2D_DrawCircle(
+ R2D_DrawCircle(
NUM2DBL(r_iv_get(self, "@x")),
NUM2DBL(r_iv_get(self, "@y")),
NUM2DBL(r_iv_get(self, "@radius")),
@@ -333,7 +332,7 @@ static R_VAL ruby2d_image_ext_init(mrb_state* mrb, R_VAL self) {
#else
static R_VAL ruby2d_image_ext_init(R_VAL self, R_VAL path) {
#endif
- S2D_Image *img = S2D_CreateImage(RSTRING_PTR(path));
+ R2D_Image *img = R2D_CreateImage(RSTRING_PTR(path));
if (!img) return R_FALSE;
// Get width and height from Ruby class. If set, use it, else choose the
@@ -356,8 +355,8 @@ static R_VAL ruby2d_image_ext_render(mrb_state* mrb, R_VAL self) {
#else
static R_VAL ruby2d_image_ext_render(R_VAL self) {
#endif
- S2D_Image *img;
- r_data_get_struct(self, "@data", &image_data_type, S2D_Image, img);
+ R2D_Image *img;
+ r_data_get_struct(self, "@data", &image_data_type, R2D_Image, img);
img->x = NUM2DBL(r_iv_get(self, "@x"));
img->y = NUM2DBL(r_iv_get(self, "@y"));
@@ -367,7 +366,7 @@ static R_VAL ruby2d_image_ext_render(R_VAL self) {
if (r_test(w)) img->width = NUM2INT(w);
if (r_test(h)) img->height = NUM2INT(h);
- S2D_RotateImage(img, NUM2DBL(r_iv_get(self, "@rotate")), S2D_CENTER);
+ R2D_RotateImage(img, NUM2DBL(r_iv_get(self, "@rotate")), R2D_CENTER);
R_VAL c = r_iv_get(self, "@color");
img->color.r = NUM2DBL(r_iv_get(c, "@r"));
@@ -375,7 +374,7 @@ static R_VAL ruby2d_image_ext_render(R_VAL self) {
img->color.b = NUM2DBL(r_iv_get(c, "@b"));
img->color.a = NUM2DBL(r_iv_get(c, "@a"));
- S2D_DrawImage(img);
+ R2D_DrawImage(img);
return R_NIL;
}
@@ -386,11 +385,11 @@ static R_VAL ruby2d_image_ext_render(R_VAL self) {
*/
#if MRUBY
static void free_image(mrb_state *mrb, void *p_) {
- S2D_Image *img = (S2D_Image *)p_;
+ R2D_Image *img = (R2D_Image *)p_;
#else
-static void free_image(S2D_Image *img) {
+static void free_image(R2D_Image *img) {
#endif
- S2D_FreeImage(img);
+ R2D_FreeImage(img);
}
@@ -405,7 +404,7 @@ static R_VAL ruby2d_sprite_ext_init(mrb_state* mrb, R_VAL self) {
#else
static R_VAL ruby2d_sprite_ext_init(R_VAL self, R_VAL path) {
#endif
- S2D_Sprite *spr = S2D_CreateSprite(RSTRING_PTR(path));
+ R2D_Sprite *spr = R2D_CreateSprite(RSTRING_PTR(path));
if (!spr) return R_FALSE;
r_iv_set(self, "@img_width" , INT2NUM(spr->width));
@@ -426,8 +425,8 @@ static R_VAL ruby2d_sprite_ext_render(R_VAL self) {
#endif
r_funcall(self, "update", 0);
- S2D_Sprite *spr;
- r_data_get_struct(self, "@data", &sprite_data_type, S2D_Sprite, spr);
+ R2D_Sprite *spr;
+ r_data_get_struct(self, "@data", &sprite_data_type, R2D_Sprite, spr);
spr->x = NUM2DBL(r_iv_get(self, "@flip_x"));
spr->y = NUM2DBL(r_iv_get(self, "@flip_y"));
@@ -438,7 +437,7 @@ static R_VAL ruby2d_sprite_ext_render(R_VAL self) {
R_VAL h = r_iv_get(self, "@flip_height");
if (r_test(h)) spr->height = NUM2DBL(h);
- S2D_RotateSprite(spr, NUM2DBL(r_iv_get(self, "@rotate")), S2D_CENTER);
+ R2D_RotateSprite(spr, NUM2DBL(r_iv_get(self, "@rotate")), R2D_CENTER);
R_VAL c = r_iv_get(self, "@color");
spr->color.r = NUM2DBL(r_iv_get(c, "@r"));
@@ -446,7 +445,7 @@ static R_VAL ruby2d_sprite_ext_render(R_VAL self) {
spr->color.b = NUM2DBL(r_iv_get(c, "@b"));
spr->color.a = NUM2DBL(r_iv_get(c, "@a"));
- S2D_ClipSprite(
+ R2D_ClipSprite(
spr,
NUM2INT(r_iv_get(self, "@clip_x")),
NUM2INT(r_iv_get(self, "@clip_y")),
@@ -454,7 +453,7 @@ static R_VAL ruby2d_sprite_ext_render(R_VAL self) {
NUM2INT(r_iv_get(self, "@clip_height"))
);
- S2D_DrawSprite(spr);
+ R2D_DrawSprite(spr);
return R_NIL;
}
@@ -465,11 +464,11 @@ static R_VAL ruby2d_sprite_ext_render(R_VAL self) {
*/
#if MRUBY
static void free_sprite(mrb_state *mrb, void *p_) {
- S2D_Sprite *spr = (S2D_Sprite *)p_;
+ R2D_Sprite *spr = (R2D_Sprite *)p_;
#else
-static void free_sprite(S2D_Sprite *spr) {
+static void free_sprite(R2D_Sprite *spr) {
#endif
- S2D_FreeSprite(spr);
+ R2D_FreeSprite(spr);
}
@@ -488,7 +487,7 @@ static R_VAL ruby2d_text_ext_init(R_VAL self) {
mrb_str_resize(mrb, s, RSTRING_LEN(s));
#endif
- S2D_Text *txt = S2D_CreateText(
+ R2D_Text *txt = R2D_CreateText(
RSTRING_PTR(r_iv_get(self, "@font")),
RSTRING_PTR(r_iv_get(self, "@text")),
NUM2DBL(r_iv_get(self, "@size"))
@@ -513,10 +512,10 @@ static R_VAL ruby2d_text_ext_set(mrb_state* mrb, R_VAL self) {
#else
static R_VAL ruby2d_text_ext_set(R_VAL self, R_VAL text) {
#endif
- S2D_Text *txt;
- r_data_get_struct(self, "@data", &text_data_type, S2D_Text, txt);
+ R2D_Text *txt;
+ r_data_get_struct(self, "@data", &text_data_type, R2D_Text, txt);
- S2D_SetText(txt, RSTRING_PTR(text));
+ R2D_SetText(txt, RSTRING_PTR(text));
r_iv_set(self, "@width", INT2NUM(txt->width));
r_iv_set(self, "@height", INT2NUM(txt->height));
@@ -533,13 +532,13 @@ static R_VAL ruby2d_text_ext_render(mrb_state* mrb, R_VAL self) {
#else
static R_VAL ruby2d_text_ext_render(R_VAL self) {
#endif
- S2D_Text *txt;
- r_data_get_struct(self, "@data", &text_data_type, S2D_Text, txt);
+ R2D_Text *txt;
+ r_data_get_struct(self, "@data", &text_data_type, R2D_Text, txt);
txt->x = NUM2DBL(r_iv_get(self, "@x"));
txt->y = NUM2DBL(r_iv_get(self, "@y"));
- S2D_RotateText(txt, NUM2DBL(r_iv_get(self, "@rotate")), S2D_CENTER);
+ R2D_RotateText(txt, NUM2DBL(r_iv_get(self, "@rotate")), R2D_CENTER);
R_VAL c = r_iv_get(self, "@color");
txt->color.r = NUM2DBL(r_iv_get(c, "@r"));
@@ -547,7 +546,7 @@ static R_VAL ruby2d_text_ext_render(R_VAL self) {
txt->color.b = NUM2DBL(r_iv_get(c, "@b"));
txt->color.a = NUM2DBL(r_iv_get(c, "@a"));
- S2D_DrawText(txt);
+ R2D_DrawText(txt);
return R_NIL;
}
@@ -558,11 +557,11 @@ static R_VAL ruby2d_text_ext_render(R_VAL self) {
*/
#if MRUBY
static void free_text(mrb_state *mrb, void *p_) {
- S2D_Text *txt = (S2D_Text *)p_;
+ R2D_Text *txt = (R2D_Text *)p_;
#else
-static void free_text(S2D_Text *txt) {
+static void free_text(R2D_Text *txt) {
#endif
- S2D_FreeText(txt);
+ R2D_FreeText(txt);
}
@@ -577,7 +576,7 @@ static R_VAL ruby2d_sound_ext_init(mrb_state* mrb, R_VAL self) {
#else
static R_VAL ruby2d_sound_ext_init(R_VAL self, R_VAL path) {
#endif
- S2D_Sound *snd = S2D_CreateSound(RSTRING_PTR(path));
+ R2D_Sound *snd = R2D_CreateSound(RSTRING_PTR(path));
if (!snd) return R_FALSE;
r_iv_set(self, "@data", r_data_wrap_struct(sound, snd));
return R_TRUE;
@@ -592,9 +591,9 @@ static R_VAL ruby2d_sound_ext_play(mrb_state* mrb, R_VAL self) {
#else
static R_VAL ruby2d_sound_ext_play(R_VAL self) {
#endif
- S2D_Sound *snd;
- r_data_get_struct(self, "@data", &sound_data_type, S2D_Sound, snd);
- S2D_PlaySound(snd);
+ R2D_Sound *snd;
+ r_data_get_struct(self, "@data", &sound_data_type, R2D_Sound, snd);
+ R2D_PlaySound(snd);
return R_NIL;
}
@@ -604,11 +603,11 @@ static R_VAL ruby2d_sound_ext_play(R_VAL self) {
*/
#if MRUBY
static void free_sound(mrb_state *mrb, void *p_) {
- S2D_Sound *snd = (S2D_Sound *)p_;
+ R2D_Sound *snd = (R2D_Sound *)p_;
#else
-static void free_sound(S2D_Sound *snd) {
+static void free_sound(R2D_Sound *snd) {
#endif
- S2D_FreeSound(snd);
+ R2D_FreeSound(snd);
}
@@ -623,7 +622,7 @@ static R_VAL ruby2d_music_ext_init(mrb_state* mrb, R_VAL self) {
#else
static R_VAL ruby2d_music_ext_init(R_VAL self, R_VAL path) {
#endif
- S2D_Music *mus = S2D_CreateMusic(RSTRING_PTR(path));
+ R2D_Music *mus = R2D_CreateMusic(RSTRING_PTR(path));
if (!mus) return R_FALSE;
r_iv_set(self, "@data", r_data_wrap_struct(music, mus));
return R_TRUE;
@@ -638,9 +637,9 @@ static R_VAL ruby2d_music_ext_play(mrb_state* mrb, R_VAL self) {
#else
static R_VAL ruby2d_music_ext_play(R_VAL self) {
#endif
- S2D_Music *mus;
- r_data_get_struct(self, "@data", &music_data_type, S2D_Music, mus);
- S2D_PlayMusic(mus, r_test(r_iv_get(self, "@loop")));
+ R2D_Music *mus;
+ r_data_get_struct(self, "@data", &music_data_type, R2D_Music, mus);
+ R2D_PlayMusic(mus, r_test(r_iv_get(self, "@loop")));
return R_NIL;
}
@@ -653,7 +652,7 @@ static R_VAL ruby2d_music_ext_pause(mrb_state* mrb, R_VAL self) {
#else
static R_VAL ruby2d_music_ext_pause(R_VAL self) {
#endif
- S2D_PauseMusic();
+ R2D_PauseMusic();
return R_NIL;
}
@@ -666,7 +665,7 @@ static R_VAL ruby2d_music_ext_resume(mrb_state* mrb, R_VAL self) {
#else
static R_VAL ruby2d_music_ext_resume(R_VAL self) {
#endif
- S2D_ResumeMusic();
+ R2D_ResumeMusic();
return R_NIL;
}
@@ -679,7 +678,7 @@ static R_VAL ruby2d_music_ext_stop(mrb_state* mrb, R_VAL self) {
#else
static R_VAL ruby2d_music_ext_stop(R_VAL self) {
#endif
- S2D_StopMusic();
+ R2D_StopMusic();
return R_NIL;
}
@@ -692,7 +691,7 @@ static R_VAL ruby2d_music_ext_get_volume(mrb_state* mrb, R_VAL self) {
#else
static R_VAL ruby2d_music_ext_get_volume(R_VAL self) {
#endif
- return INT2NUM(S2D_GetMusicVolume());
+ return INT2NUM(R2D_GetMusicVolume());
}
@@ -706,7 +705,7 @@ static R_VAL ruby2d_music_ext_set_volume(mrb_state* mrb, R_VAL self) {
#else
static R_VAL ruby2d_music_ext_set_volume(R_VAL self, R_VAL volume) {
#endif
- S2D_SetMusicVolume(NUM2INT(volume));
+ R2D_SetMusicVolume(NUM2INT(volume));
return R_NIL;
}
@@ -721,7 +720,7 @@ static R_VAL ruby2d_music_ext_fadeout(mrb_state* mrb, R_VAL self) {
#else
static R_VAL ruby2d_music_ext_fadeout(R_VAL self, R_VAL ms) {
#endif
- S2D_FadeOutMusic(NUM2INT(ms));
+ R2D_FadeOutMusic(NUM2INT(ms));
return R_NIL;
}
@@ -731,29 +730,29 @@ static R_VAL ruby2d_music_ext_fadeout(R_VAL self, R_VAL ms) {
*/
#if MRUBY
static void free_music(mrb_state *mrb, void *p_) {
- S2D_Music *mus = (S2D_Music *)p_;
+ R2D_Music *mus = (R2D_Music *)p_;
#else
-static void free_music(S2D_Music *mus) {
+static void free_music(R2D_Music *mus) {
#endif
- S2D_FreeMusic(mus);
+ R2D_FreeMusic(mus);
}
/*
- * Simple 2D `on_key` input callback function
+ * Ruby 2D native `on_key` input callback function
*/
-static void on_key(S2D_Event e) {
+static void on_key(R2D_Event e) {
R_VAL type;
switch (e.type) {
- case S2D_KEY_DOWN:
+ case R2D_KEY_DOWN:
type = r_char_to_sym("down");
break;
- case S2D_KEY_HELD:
+ case R2D_KEY_HELD:
type = r_char_to_sym("held");
break;
- case S2D_KEY_UP:
+ case R2D_KEY_UP:
type = r_char_to_sym("up");
break;
}
@@ -763,48 +762,48 @@ static void on_key(S2D_Event e) {
/*
- * Simple 2D `on_mouse` input callback function
+ * Ruby 2D native `on_mouse` input callback function
*/
-void on_mouse(S2D_Event e) {
+void on_mouse(R2D_Event e) {
R_VAL type = R_NIL; R_VAL button = R_NIL; R_VAL direction = R_NIL;
switch (e.type) {
- case S2D_MOUSE_DOWN:
+ case R2D_MOUSE_DOWN:
// type, button, x, y
type = r_char_to_sym("down");
break;
- case S2D_MOUSE_UP:
+ case R2D_MOUSE_UP:
// type, button, x, y
type = r_char_to_sym("up");
break;
- case S2D_MOUSE_SCROLL:
+ case R2D_MOUSE_SCROLL:
// type, direction, delta_x, delta_y
type = r_char_to_sym("scroll");
- direction = e.direction == S2D_MOUSE_SCROLL_NORMAL ?
+ direction = e.direction == R2D_MOUSE_SCROLL_NORMAL ?
r_char_to_sym("normal") : r_char_to_sym("inverted");
break;
- case S2D_MOUSE_MOVE:
+ case R2D_MOUSE_MOVE:
// type, x, y, delta_x, delta_y
type = r_char_to_sym("move");
break;
}
- if (e.type == S2D_MOUSE_DOWN || e.type == S2D_MOUSE_UP) {
+ if (e.type == R2D_MOUSE_DOWN || e.type == R2D_MOUSE_UP) {
switch (e.button) {
- case S2D_MOUSE_LEFT:
+ case R2D_MOUSE_LEFT:
button = r_char_to_sym("left");
break;
- case S2D_MOUSE_MIDDLE:
+ case R2D_MOUSE_MIDDLE:
button = r_char_to_sym("middle");
break;
- case S2D_MOUSE_RIGHT:
+ case R2D_MOUSE_RIGHT:
button = r_char_to_sym("right");
break;
- case S2D_MOUSE_X1:
+ case R2D_MOUSE_X1:
button = r_char_to_sym("x1");
break;
- case S2D_MOUSE_X2:
+ case R2D_MOUSE_X2:
button = r_char_to_sym("x2");
break;
}
@@ -818,88 +817,88 @@ void on_mouse(S2D_Event e) {
/*
- * Simple 2D `on_controller` input callback function
+ * Ruby 2D native `on_controller` input callback function
*/
-static void on_controller(S2D_Event e) {
+static void on_controller(R2D_Event e) {
R_VAL type = R_NIL; R_VAL axis = R_NIL; R_VAL button = R_NIL;
switch (e.type) {
- case S2D_AXIS:
+ case R2D_AXIS:
type = r_char_to_sym("axis");
switch (e.axis) {
- case S2D_AXIS_LEFTX:
+ case R2D_AXIS_LEFTX:
axis = r_char_to_sym("left_x");
break;
- case S2D_AXIS_LEFTY:
+ case R2D_AXIS_LEFTY:
axis = r_char_to_sym("left_y");
break;
- case S2D_AXIS_RIGHTX:
+ case R2D_AXIS_RIGHTX:
axis = r_char_to_sym("right_x");
break;
- case S2D_AXIS_RIGHTY:
+ case R2D_AXIS_RIGHTY:
axis = r_char_to_sym("right_y");
break;
- case S2D_AXIS_TRIGGERLEFT:
+ case R2D_AXIS_TRIGGERLEFT:
axis = r_char_to_sym("trigger_left");
break;
- case S2D_AXIS_TRIGGERRIGHT:
+ case R2D_AXIS_TRIGGERRIGHT:
axis = r_char_to_sym("trigger_right");
break;
- case S2D_AXIS_INVALID:
+ case R2D_AXIS_INVALID:
axis = r_char_to_sym("invalid");
break;
}
break;
- case S2D_BUTTON_DOWN: case S2D_BUTTON_UP:
- type = e.type == S2D_BUTTON_DOWN ? r_char_to_sym("button_down") : r_char_to_sym("button_up");
+ case R2D_BUTTON_DOWN: case R2D_BUTTON_UP:
+ type = e.type == R2D_BUTTON_DOWN ? r_char_to_sym("button_down") : r_char_to_sym("button_up");
switch (e.button) {
- case S2D_BUTTON_A:
+ case R2D_BUTTON_A:
button = r_char_to_sym("a");
break;
- case S2D_BUTTON_B:
+ case R2D_BUTTON_B:
button = r_char_to_sym("b");
break;
- case S2D_BUTTON_X:
+ case R2D_BUTTON_X:
button = r_char_to_sym("x");
break;
- case S2D_BUTTON_Y:
+ case R2D_BUTTON_Y:
button = r_char_to_sym("y");
break;
- case S2D_BUTTON_BACK:
+ case R2D_BUTTON_BACK:
button = r_char_to_sym("back");
break;
- case S2D_BUTTON_GUIDE:
+ case R2D_BUTTON_GUIDE:
button = r_char_to_sym("guide");
break;
- case S2D_BUTTON_START:
+ case R2D_BUTTON_START:
button = r_char_to_sym("start");
break;
- case S2D_BUTTON_LEFTSTICK:
+ case R2D_BUTTON_LEFTSTICK:
button = r_char_to_sym("left_stick");
break;
- case S2D_BUTTON_RIGHTSTICK:
+ case R2D_BUTTON_RIGHTSTICK:
button = r_char_to_sym("right_stick");
break;
- case S2D_BUTTON_LEFTSHOULDER:
+ case R2D_BUTTON_LEFTSHOULDER:
button = r_char_to_sym("left_shoulder");
break;
- case S2D_BUTTON_RIGHTSHOULDER:
+ case R2D_BUTTON_RIGHTSHOULDER:
button = r_char_to_sym("right_shoulder");
break;
- case S2D_BUTTON_DPAD_UP:
+ case R2D_BUTTON_DPAD_UP:
button = r_char_to_sym("up");
break;
- case S2D_BUTTON_DPAD_DOWN:
+ case R2D_BUTTON_DPAD_DOWN:
button = r_char_to_sym("down");
break;
- case S2D_BUTTON_DPAD_LEFT:
+ case R2D_BUTTON_DPAD_LEFT:
button = r_char_to_sym("left");
break;
- case S2D_BUTTON_DPAD_RIGHT:
+ case R2D_BUTTON_DPAD_RIGHT:
button = r_char_to_sym("right");
break;
- case S2D_BUTTON_INVALID:
+ case R2D_BUTTON_INVALID:
button = r_char_to_sym("invalid");
break;
}
@@ -914,7 +913,7 @@ static void on_controller(S2D_Event e) {
/*
- * Simple 2D `update` callback function
+ * Ruby 2D native `update` callback function
*/
static void update() {
@@ -934,7 +933,7 @@ static void update() {
/*
- * Simple 2D `render` callback function
+ * Ruby 2D native `render` callback function
*/
static void render() {
@@ -967,8 +966,8 @@ static R_VAL ruby2d_ext_diagnostics(mrb_state* mrb, R_VAL self) {
#else
static R_VAL ruby2d_ext_diagnostics(R_VAL self, R_VAL enable) {
#endif
- // Set Simple 2D diagnostics
- S2D_Diagnostics(r_test(enable));
+ // Set Ruby 2D native diagnostics
+ R2D_Diagnostics(r_test(enable));
return R_TRUE;
}
@@ -982,7 +981,7 @@ static R_VAL ruby2d_window_ext_get_display_dimensions(mrb_state* mrb, R_VAL self
static R_VAL ruby2d_window_ext_get_display_dimensions(R_VAL self) {
#endif
int w; int h;
- S2D_GetDisplayDimensions(&w, &h);
+ R2D_GetDisplayDimensions(&w, &h);
r_iv_set(self, "@display_width" , INT2NUM(w));
r_iv_set(self, "@display_height", INT2NUM(h));
return R_NIL;
@@ -999,8 +998,8 @@ static R_VAL ruby2d_window_ext_add_controller_mappings(mrb_state* mrb, R_VAL sel
#else
static R_VAL ruby2d_window_ext_add_controller_mappings(R_VAL self, R_VAL path) {
#endif
- S2D_Log(S2D_INFO, "Adding controller mappings from `%s`", RSTRING_PTR(path));
- S2D_AddControllerMappingsFromFile(RSTRING_PTR(path));
+ R2D_Log(R2D_INFO, "Adding controller mappings from `%s`", RSTRING_PTR(path));
+ R2D_AddControllerMappingsFromFile(RSTRING_PTR(path));
return R_NIL;
}
@@ -1032,16 +1031,16 @@ static R_VAL ruby2d_window_ext_show(R_VAL self) {
// Get window flags
int flags = 0;
if (r_test(r_iv_get(self, "@resizable"))) {
- flags = flags | S2D_RESIZABLE;
+ flags = flags | R2D_RESIZABLE;
}
if (r_test(r_iv_get(self, "@borderless"))) {
- flags = flags | S2D_BORDERLESS;
+ flags = flags | R2D_BORDERLESS;
}
if (r_test(r_iv_get(self, "@fullscreen"))) {
- flags = flags | S2D_FULLSCREEN;
+ flags = flags | R2D_FULLSCREEN;
}
if (r_test(r_iv_get(self, "@highdpi"))) {
- flags = flags | S2D_HIGHDPI;
+ flags = flags | R2D_HIGHDPI;
}
// Check viewport size and set
@@ -1054,7 +1053,7 @@ static R_VAL ruby2d_window_ext_show(R_VAL self) {
// Create and show window
- window = S2D_CreateWindow(
+ window = R2D_CreateWindow(
title, width, height, update, render, flags
);
@@ -1066,7 +1065,7 @@ static R_VAL ruby2d_window_ext_show(R_VAL self) {
window->on_mouse = on_mouse;
window->on_controller = on_controller;
- S2D_Show(window);
+ R2D_Show(window);
atexit(free_window);
return R_NIL;
@@ -1084,7 +1083,7 @@ static R_VAL ruby2d_ext_screenshot(mrb_state* mrb, R_VAL self) {
static R_VAL ruby2d_ext_screenshot(R_VAL self, R_VAL path) {
#endif
if (window) {
- S2D_Screenshot(window, RSTRING_PTR(path));
+ R2D_Screenshot(window, RSTRING_PTR(path));
return path;
} else {
return R_FALSE;
@@ -1096,7 +1095,7 @@ static R_VAL ruby2d_ext_screenshot(R_VAL self, R_VAL path) {
* Ruby2D::Window#ext_close
*/
static R_VAL ruby2d_window_ext_close() {
- S2D_Close(window);
+ R2D_Close(window);
return R_NIL;
}
diff --git a/ext/ruby2d/ruby2d.h b/ext/ruby2d/ruby2d.h
new file mode 100644
index 0000000..e6d21d7
--- /dev/null
+++ b/ext/ruby2d/ruby2d.h
@@ -0,0 +1,757 @@
+// ruby2d.h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdbool.h>
+
+// Set Platform Constants //////////////////////////////////////////////////////
+
+// Apple
+#ifdef __APPLE__
+ #ifndef __TARGETCONDITIONALS__
+ #include "TargetConditionals.h"
+ #endif
+ #if TARGET_OS_OSX
+ #define MACOS true
+ #elif TARGET_OS_IOS
+ #define IOS true
+ #elif TARGET_OS_TV
+ #define TVOS true
+ #endif
+#endif
+
+// Windows
+#ifdef _WIN32
+ #define WINDOWS true
+#endif
+
+// Windows and MinGW
+#ifdef __MINGW32__
+ #define MINGW true
+#endif
+
+// GLES
+#if defined(__arm__) || IOS || TVOS
+ #define GLES true
+#else
+ #define GLES false
+#endif
+
+// Includes ////////////////////////////////////////////////////////////////////
+
+// Define to get GNU extension functions and types, like `vasprintf()` and M_PI
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE 1
+#endif
+
+#if WINDOWS && !MINGW
+ #include <io.h>
+ #define F_OK 0 // For testing file existence
+#else
+ #include <unistd.h>
+#endif
+
+#if WINDOWS
+ #include <stdio.h>
+ #include <math.h>
+ #include <windows.h>
+ // For terminal colors
+ #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
+ #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
+ #endif
+#endif
+
+// SDL
+#if IOS || TVOS
+ #include "SDL2/SDL.h"
+#else
+ #include <SDL2/SDL.h>
+#endif
+
+// If MinGW, undefine `main()` from SDL_main.c
+#if MINGW
+ #undef main
+#endif
+
+// OpenGL
+#if GLES
+ #if IOS || TVOS
+ #include "SDL2/SDL_opengles2.h"
+ #else
+ #include <SDL2/SDL_opengles2.h>
+ #endif
+#else
+ #define GL_GLEXT_PROTOTYPES 1
+ #if WINDOWS
+ #include <glew.h>
+ #endif
+ #include <SDL2/SDL_opengl.h>
+#endif
+
+// SDL libraries
+#if IOS || TVOS
+ #include "SDL2/SDL_image.h"
+ #include "SDL2/SDL_mixer.h"
+ #include "SDL2/SDL_ttf.h"
+#else
+ #include <SDL2/SDL_image.h>
+ #include <SDL2/SDL_mixer.h>
+ #include <SDL2/SDL_ttf.h>
+#endif
+
+// Ruby 2D Definitions /////////////////////////////////////////////////////////
+
+// Messages
+#define R2D_INFO 1
+#define R2D_WARN 2
+#define R2D_ERROR 3
+
+// Window attributes
+#define R2D_RESIZABLE SDL_WINDOW_RESIZABLE
+#define R2D_BORDERLESS SDL_WINDOW_BORDERLESS
+#define R2D_FULLSCREEN SDL_WINDOW_FULLSCREEN_DESKTOP
+#define R2D_HIGHDPI SDL_WINDOW_ALLOW_HIGHDPI
+#define R2D_DISPLAY_WIDTH 0
+#define R2D_DISPLAY_HEIGHT 0
+
+// Viewport scaling modes
+#define R2D_FIXED 1
+#define R2D_EXPAND 2
+#define R2D_SCALE 3
+#define R2D_STRETCH 4
+
+// Positions
+#define R2D_CENTER 1
+#define R2D_TOP_LEFT 2
+#define R2D_TOP_RIGHT 3
+#define R2D_BOTTOM_LEFT 4
+#define R2D_BOTTOM_RIGHT 5
+
+// Keyboard events
+#define R2D_KEY_DOWN 1 // key is pressed
+#define R2D_KEY_HELD 2 // key is held down
+#define R2D_KEY_UP 3 // key is released
+
+// Mouse events
+#define R2D_MOUSE_DOWN 1 // mouse button pressed
+#define R2D_MOUSE_UP 2 // mouse button released
+#define R2D_MOUSE_SCROLL 3 // mouse scrolling or wheel movement
+#define R2D_MOUSE_MOVE 4 // mouse movement
+#define R2D_MOUSE_LEFT SDL_BUTTON_LEFT
+#define R2D_MOUSE_MIDDLE SDL_BUTTON_MIDDLE
+#define R2D_MOUSE_RIGHT SDL_BUTTON_RIGHT
+#define R2D_MOUSE_X1 SDL_BUTTON_X1
+#define R2D_MOUSE_X2 SDL_BUTTON_X2
+#define R2D_MOUSE_SCROLL_NORMAL SDL_MOUSEWHEEL_NORMAL
+#define R2D_MOUSE_SCROLL_INVERTED SDL_MOUSEWHEEL_FLIPPED
+
+// Controller events
+#define R2D_AXIS 1
+#define R2D_BUTTON_DOWN 2
+#define R2D_BUTTON_UP 3
+
+// Controller axis labels
+#define R2D_AXIS_INVALID SDL_CONTROLLER_AXIS_INVALID
+#define R2D_AXIS_LEFTX SDL_CONTROLLER_AXIS_LEFTX
+#define R2D_AXIS_LEFTY SDL_CONTROLLER_AXIS_LEFTY
+#define R2D_AXIS_RIGHTX SDL_CONTROLLER_AXIS_RIGHTX
+#define R2D_AXIS_RIGHTY SDL_CONTROLLER_AXIS_RIGHTY
+#define R2D_AXIS_TRIGGERLEFT SDL_CONTROLLER_AXIS_TRIGGERLEFT
+#define R2D_AXIS_TRIGGERRIGHT SDL_CONTROLLER_AXIS_TRIGGERRIGHT
+#define R2D_AXIS_MAX SDL_CONTROLLER_AXIS_MAX
+
+// Controller button labels
+#define R2D_BUTTON_INVALID SDL_CONTROLLER_BUTTON_INVALID
+#define R2D_BUTTON_A SDL_CONTROLLER_BUTTON_A
+#define R2D_BUTTON_B SDL_CONTROLLER_BUTTON_B
+#define R2D_BUTTON_X SDL_CONTROLLER_BUTTON_X
+#define R2D_BUTTON_Y SDL_CONTROLLER_BUTTON_Y
+#define R2D_BUTTON_BACK SDL_CONTROLLER_BUTTON_BACK
+#define R2D_BUTTON_GUIDE SDL_CONTROLLER_BUTTON_GUIDE
+#define R2D_BUTTON_START SDL_CONTROLLER_BUTTON_START
+#define R2D_BUTTON_LEFTSTICK SDL_CONTROLLER_BUTTON_LEFTSTICK
+#define R2D_BUTTON_RIGHTSTICK SDL_CONTROLLER_BUTTON_RIGHTSTICK
+#define R2D_BUTTON_LEFTSHOULDER SDL_CONTROLLER_BUTTON_LEFTSHOULDER
+#define R2D_BUTTON_RIGHTSHOULDER SDL_CONTROLLER_BUTTON_RIGHTSHOULDER
+#define R2D_BUTTON_DPAD_UP SDL_CONTROLLER_BUTTON_DPAD_UP
+#define R2D_BUTTON_DPAD_DOWN SDL_CONTROLLER_BUTTON_DPAD_DOWN
+#define R2D_BUTTON_DPAD_LEFT SDL_CONTROLLER_BUTTON_DPAD_LEFT
+#define R2D_BUTTON_DPAD_RIGHT SDL_CONTROLLER_BUTTON_DPAD_RIGHT
+#define R2D_BUTTON_MAX SDL_CONTROLLER_BUTTON_MAX
+
+// Internal Shared Data ////////////////////////////////////////////////////////
+
+extern bool R2D_diagnostics; // flag for whether to print diagnostics with R2D_Log
+
+// Type Definitions ////////////////////////////////////////////////////////////
+
+// R2D_Event
+typedef struct {
+ int which;
+ int type;
+ int button;
+ bool dblclick;
+ const char *key;
+ int x;
+ int y;
+ int delta_x;
+ int delta_y;
+ int direction;
+ int axis;
+ int value;
+} R2D_Event;
+
+typedef void (*R2D_Update)();
+typedef void (*R2D_Render)();
+typedef void (*R2D_On_Key)(R2D_Event e);
+typedef void (*R2D_On_Mouse)(R2D_Event e);
+typedef void (*R2D_On_Controller)(R2D_Event e);
+
+// R2D_GL_Point, for graphics calculations
+typedef struct {
+ GLfloat x;
+ GLfloat y;
+} R2D_GL_Point;
+
+// R2D_Color
+typedef struct {
+ GLfloat r;
+ GLfloat g;
+ GLfloat b;
+ GLfloat a;
+} R2D_Color;
+
+// R2D_Mouse
+typedef struct {
+ int visible;
+ int x;
+ int y;
+} R2D_Mouse;
+
+// R2D_Viewport
+typedef struct {
+ int width;
+ int height;
+ int mode;
+} R2D_Viewport;
+
+// R2D_Window
+typedef struct {
+ SDL_Window *sdl;
+ SDL_GLContext glcontext;
+ const GLubyte *R2D_GL_VENDOR;
+ const GLubyte *R2D_GL_RENDERER;
+ const GLubyte *R2D_GL_VERSION;
+ GLint R2D_GL_MAJOR_VERSION;
+ GLint R2D_GL_MINOR_VERSION;
+ const GLubyte *R2D_GL_SHADING_LANGUAGE_VERSION;
+ const char *title;
+ int width;
+ int height;
+ int orig_width;
+ int orig_height;
+ R2D_Viewport viewport;
+ R2D_Update update;
+ R2D_Render render;
+ int flags;
+ R2D_Mouse mouse;
+ R2D_On_Key on_key;
+ R2D_On_Mouse on_mouse;
+ R2D_On_Controller on_controller;
+ bool vsync;
+ int fps_cap;
+ R2D_Color background;
+ const char *icon;
+ Uint32 frames;
+ Uint32 elapsed_ms;
+ Uint32 loop_ms;
+ Uint32 delay_ms;
+ double fps;
+ bool close;
+} R2D_Window;
+
+// R2D_Image
+typedef struct {
+ const char *path;
+ SDL_Surface *surface;
+ int format;
+ GLuint texture_id;
+ R2D_Color color;
+ int x;
+ int y;
+ int width;
+ int height;
+ int orig_width;
+ int orig_height;
+ GLfloat rotate; // Rotation angle in degrees
+ GLfloat rx; // X coordinate to be rotated around
+ GLfloat ry; // Y coordinate to be rotated around
+} R2D_Image;
+
+// R2D_Sprite
+typedef struct {
+ const char *path;
+ R2D_Image *img;
+ R2D_Color color;
+ int x;
+ int y;
+ int width;
+ int height;
+ int clip_width;
+ int clip_height;
+ GLfloat rotate; // Rotation angle in degrees
+ GLfloat rx; // X coordinate to be rotated around
+ GLfloat ry; // Y coordinate to be rotated around
+ GLfloat tx1;
+ GLfloat ty1;
+ GLfloat tx2;
+ GLfloat ty2;
+ GLfloat tx3;
+ GLfloat ty3;
+ GLfloat tx4;
+ GLfloat ty4;
+} R2D_Sprite;
+
+// R2D_Text
+typedef struct {
+ const char *font;
+ SDL_Surface *surface;
+ GLuint texture_id;
+ TTF_Font *font_data;
+ R2D_Color color;
+ char *msg;
+ int x;
+ int y;
+ int width;
+ int height;
+ GLfloat rotate; // Rotation angle in degrees
+ GLfloat rx; // X coordinate to be rotated around
+ GLfloat ry; // Y coordinate to be rotated around
+} R2D_Text;
+
+// R2D_Sound
+typedef struct {
+ const char *path;
+ Mix_Chunk *data;
+} R2D_Sound;
+
+// R2D_Music
+typedef struct {
+ const char *path;
+ Mix_Music *data;
+} R2D_Music;
+
+// Ruby 2D Functions ///////////////////////////////////////////////////////////
+
+/*
+ * Checks if a file exists and can be accessed
+ */
+bool R2D_FileExists(const char *path);
+
+/*
+ * Logs standard messages to the console
+ */
+void R2D_Log(int type, const char *msg, ...);
+
+/*
+ * Logs Ruby 2D errors to the console, with caller and message body
+ */
+void R2D_Error(const char *caller, const char *msg, ...);
+
+/*
+ * Enable/disable logging of diagnostics
+ */
+void R2D_Diagnostics(bool status);
+
+/*
+ * Enable terminal colors in Windows
+ */
+void R2D_Windows_EnableTerminalColors();
+
+/*
+* Initialize Ruby 2D subsystems
+*/
+bool R2D_Init();
+
+/*
+ * Gets the primary display's dimensions
+ */
+void R2D_GetDisplayDimensions(int *w, int *h);
+
+/*
+ * Quits Ruby 2D subsystems
+ */
+void R2D_Quit(void);
+
+// Shapes //////////////////////////////////////////////////////////////////////
+
+/*
+ * Rotate a point around a given point
+ * Params:
+ * p The point to rotate
+ * angle The angle in degrees
+ * rx The x coordinate to rotate around
+ * ry The y coordinate to rotate around
+ */
+R2D_GL_Point R2D_RotatePoint(R2D_GL_Point p, GLfloat angle, GLfloat rx, GLfloat ry);
+
+/*
+ * Get the point to be rotated around given a position in a rectangle
+ */
+R2D_GL_Point R2D_GetRectRotationPoint(int x, int y, int w, int h, int position);
+
+/*
+ * Draw a triangle
+ */
+void R2D_DrawTriangle(
+ GLfloat x1, GLfloat y1,
+ GLfloat r1, GLfloat g1, GLfloat b1, GLfloat a1,
+ GLfloat x2, GLfloat y2,
+ GLfloat r2, GLfloat g2, GLfloat b2, GLfloat a2,
+ GLfloat x3, GLfloat y3,
+ GLfloat r3, GLfloat g3, GLfloat b3, GLfloat a3
+);
+
+/*
+ * Draw a quad, using two triangles
+ */
+void R2D_DrawQuad(
+ GLfloat x1, GLfloat y1,
+ GLfloat r1, GLfloat g1, GLfloat b1, GLfloat a1,
+ GLfloat x2, GLfloat y2,
+ GLfloat r2, GLfloat g2, GLfloat b2, GLfloat a2,
+ GLfloat x3, GLfloat y3,
+ GLfloat r3, GLfloat g3, GLfloat b3, GLfloat a3,
+ GLfloat x4, GLfloat y4,
+ GLfloat r4, GLfloat g4, GLfloat b4, GLfloat a4
+);
+
+/*
+ * Draw a line from a quad
+ */
+void R2D_DrawLine(
+ GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2,
+ GLfloat width,
+ GLfloat r1, GLfloat g1, GLfloat b1, GLfloat a1,
+ GLfloat r2, GLfloat g2, GLfloat b2, GLfloat a2,
+ GLfloat r3, GLfloat g3, GLfloat b3, GLfloat a3,
+ GLfloat r4, GLfloat g4, GLfloat b4, GLfloat a4
+);
+
+/*
+ * Draw a circle from triangles
+ */
+void R2D_DrawCircle(
+ GLfloat x, GLfloat y, GLfloat radius, int sectors,
+ GLfloat r, GLfloat g, GLfloat b, GLfloat a
+);
+
+// Image ///////////////////////////////////////////////////////////////////////
+
+/*
+ * Create an image, given a file path
+ */
+R2D_Image *R2D_CreateImage(const char *path);
+
+/*
+ * Rotate an image
+ */
+void R2D_RotateImage(R2D_Image *img, GLfloat angle, int position);
+
+/*
+ * Draw an image
+ */
+void R2D_DrawImage(R2D_Image *img);
+
+/*
+ * Free an image
+ */
+void R2D_FreeImage(R2D_Image *img);
+
+// Sprite //////////////////////////////////////////////////////////////////////
+
+/*
+ * Create a sprite, given an image file path
+ */
+R2D_Sprite *R2D_CreateSprite(const char *path);
+
+/*
+ * Clip a sprite
+ */
+void R2D_ClipSprite(R2D_Sprite *spr, int x, int y, int w, int h);
+
+/*
+ * Rotate a sprite
+ */
+void R2D_RotateSprite(R2D_Sprite *spr, GLfloat angle, int position);
+
+/*
+ * Draw a sprite
+ */
+void R2D_DrawSprite(R2D_Sprite *spr);
+
+/*
+ * Free a sprite
+ */
+void R2D_FreeSprite(R2D_Sprite *spr);
+
+// Text ////////////////////////////////////////////////////////////////////////
+
+/*
+ * Create text, given a font file path, the message, and size
+ */
+R2D_Text *R2D_CreateText(const char *font, const char *msg, int size);
+
+/*
+* Set the text message
+*/
+void R2D_SetText(R2D_Text *txt, const char *msg, ...);
+
+/*
+ * Rotate text
+ */
+void R2D_RotateText(R2D_Text *txt, GLfloat angle, int position);
+
+/*
+ * Draw text
+ */
+void R2D_DrawText(R2D_Text *txt);
+
+/*
+ * Free the text
+ */
+void R2D_FreeText(R2D_Text *txt);
+
+// Sound ///////////////////////////////////////////////////////////////////////
+
+/*
+ * Create a sound, given an audio file path
+ */
+R2D_Sound *R2D_CreateSound(const char *path);
+
+/*
+ * Play the sound
+ */
+void R2D_PlaySound(R2D_Sound *snd);
+
+/*
+ * Get the sound's volume
+ */
+int R2D_GetSoundVolume(R2D_Sound *snd);
+
+/*
+ * Set the sound's volume a given percentage
+ */
+void R2D_SetSoundVolume(R2D_Sound *snd, int volume);
+
+/*
+ * Get the sound mixer volume
+ */
+int R2D_GetSoundMixVolume();
+
+/*
+ * Set the sound mixer volume a given percentage
+ */
+void R2D_SetSoundMixVolume(int volume);
+
+/*
+ * Free the sound
+ */
+void R2D_FreeSound(R2D_Sound *snd);
+
+// Music ///////////////////////////////////////////////////////////////////////
+
+/*
+ * Create the music, given an audio file path
+ */
+R2D_Music *R2D_CreateMusic(const char *path);
+
+/*
+ * Play the music
+ */
+void R2D_PlayMusic(R2D_Music *mus, bool loop);
+
+/*
+ * Pause the playing music
+ */
+void R2D_PauseMusic();
+
+/*
+ * Resume the current music
+ */
+void R2D_ResumeMusic();
+
+/*
+ * Stop the playing music; interrupts fader effects
+ */
+void R2D_StopMusic();
+
+/*
+ * Get the music volume
+ */
+int R2D_GetMusicVolume();
+
+/*
+ * Set the music volume a given percentage
+ */
+void R2D_SetMusicVolume(int volume);
+
+/*
+ * Fade out the playing music
+ */
+void R2D_FadeOutMusic(int ms);
+
+/*
+ * Free the music
+ */
+void R2D_FreeMusic(R2D_Music *mus);
+
+// Input ///////////////////////////////////////////////////////////////////////
+
+/*
+ * Get the mouse coordinates relative to the viewport
+ */
+void R2D_GetMouseOnViewport(R2D_Window *window, int wx, int wy, int *x, int *y);
+
+/*
+ * Show the cursor over the window
+ */
+void R2D_ShowCursor();
+
+/*
+ * Hide the cursor over the window
+ */
+void R2D_HideCursor();
+
+// Controllers /////////////////////////////////////////////////////////////////
+
+/*
+ * Add controller mapping from string
+ */
+void R2D_AddControllerMapping(const char *map);
+
+/*
+ * Load controller mappings from the specified file
+ */
+void R2D_AddControllerMappingsFromFile(const char *path);
+
+/*
+ * Check if joystick is a controller
+ */
+bool R2D_IsController(SDL_JoystickID id);
+
+/*
+ * Open controllers and joysticks
+ */
+void R2D_OpenControllers();
+
+// Window //////////////////////////////////////////////////////////////////////
+
+/*
+ * Create a window
+ */
+R2D_Window *R2D_CreateWindow(
+ const char *title, int width, int height, R2D_Update, R2D_Render, int flags
+);
+
+/*
+ * Show the window
+ */
+int R2D_Show(R2D_Window *window);
+
+/*
+ * Set the icon for the window
+ */
+void R2D_SetIcon(R2D_Window *window, const char *icon);
+
+/*
+ * Take a screenshot of the window
+ */
+void R2D_Screenshot(R2D_Window *window, const char *path);
+
+/*
+ * Close the window
+ */
+int R2D_Close(R2D_Window *window);
+
+/*
+ * Free all resources
+ */
+int R2D_FreeWindow(R2D_Window *window);
+
+// Ruby 2D OpenGL Functions ////////////////////////////////////////////////////
+
+int R2D_GL_Init(R2D_Window *window);
+void R2D_GL_PrintError(char *error);
+void R2D_GL_PrintContextInfo(R2D_Window *window);
+void R2D_GL_StoreContextInfo(R2D_Window *window);
+GLuint R2D_GL_LoadShader(GLenum type, const GLchar *shaderSrc, char *shaderName);
+int R2D_GL_CheckLinked(GLuint program, char *name);
+void R2D_GL_GetViewportScale(R2D_Window *window, int *w, int *h, double *scale);
+void R2D_GL_SetViewport(R2D_Window *window);
+void R2D_GL_CreateTexture(
+ GLuint *id, GLint format,
+ int w, int h,
+ const GLvoid *data, GLint filter);
+void R2D_GL_DrawTriangle(
+ GLfloat x1, GLfloat y1,
+ GLfloat r1, GLfloat g1, GLfloat b1, GLfloat a1,
+ GLfloat x2, GLfloat y2,
+ GLfloat r2, GLfloat g2, GLfloat b2, GLfloat a2,
+ GLfloat x3, GLfloat y3,
+ GLfloat r3, GLfloat g3, GLfloat b3, GLfloat a3);
+void R2D_GL_DrawImage(R2D_Image *img);
+void R2D_GL_DrawSprite(R2D_Sprite *spr);
+void R2D_GL_DrawText(R2D_Text *txt);
+void R2D_GL_FreeTexture(GLuint *id);
+void R2D_GL_Clear(R2D_Color clr);
+void R2D_GL_FlushBuffers();
+
+// OpenGL & GLES Internal Functions ////////////////////////////////////////////
+
+#if GLES
+ int R2D_GLES_Init();
+ void R2D_GLES_ApplyProjection(GLfloat orthoMatrix[16]);
+ void R2D_GLES_DrawTriangle(
+ GLfloat x1, GLfloat y1,
+ GLfloat r1, GLfloat g1, GLfloat b1, GLfloat a1,
+ GLfloat x2, GLfloat y2,
+ GLfloat r2, GLfloat g2, GLfloat b2, GLfloat a2,
+ GLfloat x3, GLfloat y3,
+ GLfloat r3, GLfloat g3, GLfloat b3, GLfloat a3);
+ void R2D_GLES_DrawImage(R2D_Image *img);
+ void R2D_GLES_DrawSprite(R2D_Sprite *spr);
+ void R2D_GLES_DrawText(R2D_Text *txt);
+#else
+ int R2D_GL2_Init();
+ int R2D_GL3_Init();
+ void R2D_GL2_ApplyProjection(int w, int h);
+ void R2D_GL3_ApplyProjection(GLfloat orthoMatrix[16]);
+ void R2D_GL2_DrawTriangle(
+ GLfloat x1, GLfloat y1,
+ GLfloat r1, GLfloat g1, GLfloat b1, GLfloat a1,
+ GLfloat x2, GLfloat y2,
+ GLfloat r2, GLfloat g2, GLfloat b2, GLfloat a2,
+ GLfloat x3, GLfloat y3,
+ GLfloat r3, GLfloat g3, GLfloat b3, GLfloat a3);
+ void R2D_GL3_DrawTriangle(
+ GLfloat x1, GLfloat y1,
+ GLfloat r1, GLfloat g1, GLfloat b1, GLfloat a1,
+ GLfloat x2, GLfloat y2,
+ GLfloat r2, GLfloat g2, GLfloat b2, GLfloat a2,
+ GLfloat x3, GLfloat y3,
+ GLfloat r3, GLfloat g3, GLfloat b3, GLfloat a3);
+ void R2D_GL2_DrawImage(R2D_Image *img);
+ void R2D_GL3_DrawImage(R2D_Image *img);
+ void R2D_GL2_DrawSprite(R2D_Sprite *spr);
+ void R2D_GL3_DrawSprite(R2D_Sprite *spr);
+ void R2D_GL2_DrawText(R2D_Text *txt);
+ void R2D_GL3_DrawText(R2D_Text *txt);
+ void R2D_GL3_FlushBuffers();
+#endif
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/ext/ruby2d/shapes.c b/ext/ruby2d/shapes.c
new file mode 100644
index 0000000..5698e3f
--- /dev/null
+++ b/ext/ruby2d/shapes.c
@@ -0,0 +1,154 @@
+// shapes.c
+
+#include "ruby2d.h"
+
+
+/*
+ * Rotate a point around a given point
+ * Params:
+ * p The point to rotate
+ * angle The angle in degrees
+ * rx The x coordinate to rotate around
+ * ry The y coordinate to rotate around
+ */
+R2D_GL_Point R2D_RotatePoint(R2D_GL_Point p, GLfloat angle, GLfloat rx, GLfloat ry) {
+
+ // Convert from degrees to radians
+ angle = angle * M_PI / 180.0;
+
+ // Get the sine and cosine of the angle
+ GLfloat sa = sin(angle);
+ GLfloat ca = cos(angle);
+
+ // Translate point to origin
+ p.x -= rx;
+ p.y -= ry;
+
+ // Rotate point
+ GLfloat xnew = p.x * ca - p.y * sa;
+ GLfloat ynew = p.x * sa + p.y * ca;
+
+ // Translate point back
+ p.x = xnew + rx;
+ p.y = ynew + ry;
+
+ return p;
+}
+
+
+/*
+ * Get the point to be rotated around given a position in a rectangle
+ */
+R2D_GL_Point R2D_GetRectRotationPoint(int x, int y, int w, int h, int position) {
+
+ R2D_GL_Point p;
+
+ switch (position) {
+ case R2D_CENTER:
+ p.x = x + (w / 2.0);
+ p.y = y + (h / 2.0);
+ break;
+ case R2D_TOP_LEFT:
+ p.x = x;
+ p.y = y;
+ break;
+ case R2D_TOP_RIGHT:
+ p.x = x + w;
+ p.y = y;
+ break;
+ case R2D_BOTTOM_LEFT:
+ p.x = x;
+ p.y = y + h;
+ break;
+ case R2D_BOTTOM_RIGHT:
+ p.x = x + w;
+ p.y = y + h;
+ break;
+ }
+
+ return p;
+}
+
+
+/*
+ * Draw a triangle
+ */
+void R2D_DrawTriangle(GLfloat x1, GLfloat y1,
+ GLfloat r1, GLfloat g1, GLfloat b1, GLfloat a1,
+ GLfloat x2, GLfloat y2,
+ GLfloat r2, GLfloat g2, GLfloat b2, GLfloat a2,
+ GLfloat x3, GLfloat y3,
+ GLfloat r3, GLfloat g3, GLfloat b3, GLfloat a3) {
+
+ R2D_GL_DrawTriangle(x1, y1, r1, g1, b1, a1,
+ x2, y2, r2, g2, b2, a2,
+ x3, y3, r3, g3, b3, a3);
+}
+
+
+/*
+ * Draw a quad, using two triangles
+ */
+void R2D_DrawQuad(GLfloat x1, GLfloat y1,
+ GLfloat r1, GLfloat g1, GLfloat b1, GLfloat a1,
+ GLfloat x2, GLfloat y2,
+ GLfloat r2, GLfloat g2, GLfloat b2, GLfloat a2,
+ GLfloat x3, GLfloat y3,
+ GLfloat r3, GLfloat g3, GLfloat b3, GLfloat a3,
+ GLfloat x4, GLfloat y4,
+ GLfloat r4, GLfloat g4, GLfloat b4, GLfloat a4) {
+
+ R2D_GL_DrawTriangle(x1, y1, r1, g1, b1, a1,
+ x2, y2, r2, g2, b2, a2,
+ x3, y3, r3, g3, b3, a3);
+
+ R2D_GL_DrawTriangle(x3, y3, r3, g3, b3, a3,
+ x4, y4, r4, g4, b4, a4,
+ x1, y1, r1, g1, b1, a1);
+};
+
+
+/*
+ * Draw a line from a quad
+ */
+void R2D_DrawLine(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2,
+ GLfloat width,
+ GLfloat r1, GLfloat g1, GLfloat b1, GLfloat a1,
+ GLfloat r2, GLfloat g2, GLfloat b2, GLfloat a2,
+ GLfloat r3, GLfloat g3, GLfloat b3, GLfloat a3,
+ GLfloat r4, GLfloat g4, GLfloat b4, GLfloat a4) {
+
+ double length = sqrt(powf(x1 - x2, 2) + powf(y1 - y2, 2));
+ double x = ((x2 - x1) / length) * width / 2;
+ double y = ((y2 - y1) / length) * width / 2;
+
+ R2D_DrawQuad(
+ x1 - y, y1 + x, r1, g1, b1, a1,
+ x1 + y, y1 - x, r2, g2, b2, a2,
+ x2 + y, y2 - x, r3, g3, b3, a3,
+ x2 - y, y2 + x, r4, g4, b4, a4
+ );
+};
+
+
+/*
+ * Draw a circle from triangles
+ */
+void R2D_DrawCircle(GLfloat x, GLfloat y, GLfloat radius, int sectors,
+ GLfloat r, GLfloat g, GLfloat b, GLfloat a) {
+
+ double angle = 2 * M_PI / sectors;
+
+ for (int i = 0; i < sectors; i++) {
+
+ GLfloat x1 = x + radius * cos(i * angle);
+ GLfloat y1 = y + radius * sin(i * angle);
+
+ GLfloat x2 = x + radius * cos((i - 1) * angle);
+ GLfloat y2 = y + radius * sin((i - 1) * angle);
+
+ R2D_GL_DrawTriangle( x, y, r, g, b, a,
+ x1, y1, r, g, b, a,
+ x2, y2, r, g, b, a);
+ }
+}
diff --git a/ext/ruby2d/sound.c b/ext/ruby2d/sound.c
new file mode 100644
index 0000000..326c695
--- /dev/null
+++ b/ext/ruby2d/sound.c
@@ -0,0 +1,93 @@
+// sound.c
+
+#include "ruby2d.h"
+
+
+/*
+ * Create a sound, given an audio file path
+ */
+R2D_Sound *R2D_CreateSound(const char *path) {
+ R2D_Init();
+
+ // Check if sound file exists
+ if (!R2D_FileExists(path)) {
+ R2D_Error("R2D_CreateSound", "Sound file `%s` not found", path);
+ return NULL;
+ }
+
+ // Allocate the sound structure
+ R2D_Sound *snd = (R2D_Sound *) malloc(sizeof(R2D_Sound));
+ if (!snd) {
+ R2D_Error("R2D_CreateSound", "Out of memory!");
+ return NULL;
+ }
+
+ // Load the sound data from file
+ snd->data = Mix_LoadWAV(path);
+ if (!snd->data) {
+ R2D_Error("Mix_LoadWAV", Mix_GetError());
+ free(snd);
+ return NULL;
+ }
+
+ // Initialize values
+ snd->path = path;
+
+ return snd;
+}
+
+
+/*
+ * Play the sound
+ */
+void R2D_PlaySound(R2D_Sound *snd) {
+ if (!snd) return;
+ Mix_PlayChannel(-1, snd->data, 0);
+}
+
+
+/*
+ * Get the sound's volume
+ */
+int R2D_GetSoundVolume(R2D_Sound *snd) {
+ if (!snd) return -1;
+ return ceil(Mix_VolumeChunk(snd->data, -1) * (100.0 / MIX_MAX_VOLUME));
+}
+
+
+/*
+ * Set the sound's volume a given percentage
+ */
+void R2D_SetSoundVolume(R2D_Sound *snd, int volume) {
+ if (!snd) return;
+ // Set volume to be a percentage of the maximum mix volume
+ Mix_VolumeChunk(snd->data, (volume / 100.0) * MIX_MAX_VOLUME);
+}
+
+
+/*
+ * Get the sound mixer volume
+ */
+int R2D_GetSoundMixVolume() {
+ return ceil(Mix_Volume(-1, -1) * (100.0 / MIX_MAX_VOLUME));
+}
+
+
+/*
+ * Set the sound mixer volume a given percentage
+ */
+void R2D_SetSoundMixVolume(int volume) {
+ // This sets the volume value across all channels
+ // Set volume to be a percentage of the maximum mix volume
+ Mix_Volume(-1, (volume / 100.0) * MIX_MAX_VOLUME);
+}
+
+
+/*
+ * Free the sound
+ */
+void R2D_FreeSound(R2D_Sound *snd) {
+ if (!snd) return;
+ Mix_FreeChunk(snd->data);
+ free(snd);
+}
diff --git a/ext/ruby2d/sprite.c b/ext/ruby2d/sprite.c
new file mode 100644
index 0000000..30d2042
--- /dev/null
+++ b/ext/ruby2d/sprite.c
@@ -0,0 +1,147 @@
+// sprite.c
+
+#include "ruby2d.h"
+
+
+/*
+ * Create a sprite, given an image file path
+ */
+R2D_Sprite *R2D_CreateSprite(const char *path) {
+
+ // Check if image file exists
+ if (!R2D_FileExists(path)) {
+ R2D_Error("R2D_CreateSprite", "Sprite image file `%s` not found", path);
+ return NULL;
+ }
+
+ // Allocate the sprite structure
+ R2D_Sprite *spr = (R2D_Sprite *) malloc(sizeof(R2D_Sprite));
+ if (!spr) {
+ R2D_Error("R2D_CreateSprite", "Out of memory!");
+ return NULL;
+ }
+
+ // Load the sprite image file
+ spr->img = R2D_CreateImage(path);
+ if (!spr->img) {
+ R2D_Error("R2D_CreateSprite", "Cannot create sprite image `%s`", path);
+ free(spr);
+ return NULL;
+ }
+
+ // Initialize values
+ spr->path = path;
+ spr->x = 0;
+ spr->y = 0;
+ spr->color.r = 1.f;
+ spr->color.g = 1.f;
+ spr->color.b = 1.f;
+ spr->color.a = 1.f;
+ spr->width = spr->img->width;
+ spr->height = spr->img->height;
+ spr->clip_width = spr->img->width;
+ spr->clip_height = spr->img->height;
+ spr->rotate = 0;
+ spr->rx = 0;
+ spr->ry = 0;
+ spr->tx1 = 0.f;
+ spr->ty1 = 0.f;
+ spr->tx2 = 1.f;
+ spr->ty2 = 0.f;
+ spr->tx3 = 1.f;
+ spr->ty3 = 1.f;
+ spr->tx4 = 0.f;
+ spr->ty4 = 1.f;
+
+ return spr;
+}
+
+
+/*
+ * Clip a sprite
+ */
+void R2D_ClipSprite(R2D_Sprite *spr, int x, int y, int w, int h) {
+ if (!spr) return;
+
+ // Calculate ratios
+ // rw = ratio width; rh = ratio height
+ double rw = w / (double)spr->img->width;
+ double rh = h / (double)spr->img->height;
+
+ // Apply ratios to x, y coordinates
+ // cx = crop x coord; cy = crop y coord
+ double cx = x * rw;
+ double cy = y * rh;
+
+ // Convert given width, height to doubles
+ // cw = crop width; ch = crop height
+ double cw = (double)w;
+ double ch = (double)h;
+
+ // Apply ratio to texture width and height
+ // tw = texture width; th = texture height
+ double tw = rw * w;
+ double th = rh * h;
+
+ // Calculate and store sprite texture values
+
+ spr->tx1 = cx / cw;
+ spr->ty1 = cy / ch;
+
+ spr->tx2 = (cx + tw) / cw;
+ spr->ty2 = cy / ch;
+
+ spr->tx3 = (cx + tw) / cw;
+ spr->ty3 = (cy + th) / ch;
+
+ spr->tx4 = cx / cw;
+ spr->ty4 = (cy + th) / ch;
+
+ // Store the sprite dimensions
+ spr->width = (spr->width / (double)spr->clip_width ) * w;
+ spr->height = (spr->height / (double)spr->clip_height) * h;
+ spr->clip_width = w;
+ spr->clip_height = h;
+}
+
+
+/*
+ * Rotate a sprite
+ */
+void R2D_RotateSprite(R2D_Sprite *spr, GLfloat angle, int position) {
+
+ R2D_GL_Point p = R2D_GetRectRotationPoint(
+ spr->x, spr->y, spr->width, spr->height, position
+ );
+
+ spr->rotate = angle;
+ spr->rx = p.x;
+ spr->ry = p.y;
+}
+
+
+/*
+ * Draw a sprite
+ */
+void R2D_DrawSprite(R2D_Sprite *spr) {
+ if (!spr) return;
+
+ if (spr->img->texture_id == 0) {
+ R2D_GL_CreateTexture(&spr->img->texture_id, spr->img->format,
+ spr->img->width, spr->img->height,
+ spr->img->surface->pixels, GL_NEAREST);
+ SDL_FreeSurface(spr->img->surface);
+ }
+
+ R2D_GL_DrawSprite(spr);
+}
+
+
+/*
+ * Free a sprite
+ */
+void R2D_FreeSprite(R2D_Sprite *spr) {
+ if (!spr) return;
+ R2D_FreeImage(spr->img);
+ free(spr);
+}
diff --git a/ext/ruby2d/text.c b/ext/ruby2d/text.c
new file mode 100644
index 0000000..58d4086
--- /dev/null
+++ b/ext/ruby2d/text.c
@@ -0,0 +1,129 @@
+// text.c
+
+#include "ruby2d.h"
+
+
+/*
+ * Create text, given a font file path, the message, and size
+ */
+R2D_Text *R2D_CreateText(const char *font, const char *msg, int size) {
+ R2D_Init();
+
+ // Check if font file exists
+ if (!R2D_FileExists(font)) {
+ R2D_Error("R2D_CreateText", "Font file `%s` not found", font);
+ return NULL;
+ }
+
+ // `msg` cannot be an empty string or NULL for TTF_SizeText
+ if (msg == NULL || strlen(msg) == 0) msg = " ";
+
+ // Allocate the text structure
+ R2D_Text *txt = (R2D_Text *) malloc(sizeof(R2D_Text));
+ if (!txt) {
+ R2D_Error("R2D_CreateText", "Out of memory!");
+ return NULL;
+ }
+
+ // Open the font
+ txt->font_data = TTF_OpenFont(font, size);
+ if (!txt->font_data) {
+ R2D_Error("TTF_OpenFont", TTF_GetError());
+ free(txt);
+ return NULL;
+ }
+
+ // Initialize values
+ txt->font = font;
+ txt->msg = (char *) malloc(strlen(msg) + 1 * sizeof(char));
+ strcpy(txt->msg, msg);
+ txt->x = 0;
+ txt->y = 0;
+ txt->color.r = 1.f;
+ txt->color.g = 1.f;
+ txt->color.b = 1.f;
+ txt->color.a = 1.f;
+ txt->rotate = 0;
+ txt->rx = 0;
+ txt->ry = 0;
+ txt->texture_id = 0;
+
+ // Save the width and height of the text
+ TTF_SizeText(txt->font_data, txt->msg, &txt->width, &txt->height);
+
+ return txt;
+}
+
+
+/*
+ * Set the text message
+ */
+void R2D_SetText(R2D_Text *txt, const char *msg, ...) {
+ if (!txt) return;
+
+ // `msg` cannot be an empty string or NULL for TTF_SizeText
+ if (msg == NULL || strlen(msg) == 0) msg = " ";
+
+ // Format and store new text string
+ va_list args;
+ va_start(args, msg);
+ free(txt->msg);
+ vasprintf(&txt->msg, msg, args);
+ va_end(args);
+
+ // Save the width and height of the text
+ TTF_SizeText(txt->font_data, txt->msg, &txt->width, &txt->height);
+
+ // Delete the current texture so a new one can be generated
+ R2D_GL_FreeTexture(&txt->texture_id);
+}
+
+
+/*
+ * Rotate text
+ */
+void R2D_RotateText(R2D_Text *txt, GLfloat angle, int position) {
+
+ R2D_GL_Point p = R2D_GetRectRotationPoint(
+ txt->x, txt->y, txt->width, txt->height, position
+ );
+
+ txt->rotate = angle;
+ txt->rx = p.x;
+ txt->ry = p.y;
+}
+
+
+/*
+ * Draw text
+ */
+void R2D_DrawText(R2D_Text *txt) {
+ if (!txt) return;
+
+ if (txt->texture_id == 0) {
+ SDL_Color color = { 255, 255, 255 };
+ txt->surface = TTF_RenderText_Blended(txt->font_data, txt->msg, color);
+ if (!txt->surface) {
+ R2D_Error("TTF_RenderText_Blended", TTF_GetError());
+ return;
+ }
+ R2D_GL_CreateTexture(&txt->texture_id, GL_RGBA,
+ txt->width, txt->height,
+ txt->surface->pixels, GL_NEAREST);
+ SDL_FreeSurface(txt->surface);
+ }
+
+ R2D_GL_DrawText(txt);
+}
+
+
+/*
+ * Free the text
+ */
+void R2D_FreeText(R2D_Text *txt) {
+ if (!txt) return;
+ free(txt->msg);
+ R2D_GL_FreeTexture(&txt->texture_id);
+ TTF_CloseFont(txt->font_data);
+ free(txt);
+}
diff --git a/ext/ruby2d/window.c b/ext/ruby2d/window.c
new file mode 100644
index 0000000..f10c55b
--- /dev/null
+++ b/ext/ruby2d/window.c
@@ -0,0 +1,414 @@
+// window.c
+
+#include "ruby2d.h"
+
+
+/*
+ * Create a window
+ */
+R2D_Window *R2D_CreateWindow(const char *title, int width, int height,
+ R2D_Update update, R2D_Render render, int flags) {
+
+ R2D_Init();
+
+ SDL_DisplayMode dm;
+ SDL_GetCurrentDisplayMode(0, &dm);
+ R2D_Log(R2D_INFO, "Current display mode is %dx%dpx @ %dhz", dm.w, dm.h, dm.refresh_rate);
+
+ width = width == R2D_DISPLAY_WIDTH ? dm.w : width;
+ height = height == R2D_DISPLAY_HEIGHT ? dm.h : height;
+
+ // Allocate window and set default values
+ R2D_Window *window = (R2D_Window *) malloc(sizeof(R2D_Window));
+ window->sdl = NULL;
+ window->glcontext = NULL;
+ window->title = title;
+ window->width = width;
+ window->height = height;
+ window->orig_width = width;
+ window->orig_height = height;
+ window->viewport.width = width;
+ window->viewport.height = height;
+ window->viewport.mode = R2D_SCALE;
+ window->update = update;
+ window->render = render;
+ window->flags = flags;
+ window->on_key = NULL;
+ window->on_mouse = NULL;
+ window->on_controller = NULL;
+ window->vsync = true;
+ window->fps_cap = 60;
+ window->background.r = 0.0;
+ window->background.g = 0.0;
+ window->background.b = 0.0;
+ window->background.a = 1.0;
+ window->icon = NULL;
+ window->close = true;
+
+ // Return the window structure
+ return window;
+}
+
+
+/*
+ * Show the window
+ */
+int R2D_Show(R2D_Window *window) {
+
+ if (!window) {
+ R2D_Error("R2D_Show", "Window cannot be shown (because it's NULL)");
+ return 1;
+ }
+
+ // Create SDL window
+ window->sdl = SDL_CreateWindow(
+ window->title, // title
+ SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, // window position
+ window->width, window->height, // window size
+ SDL_WINDOW_OPENGL | window->flags // flags
+ );
+
+ if (!window->sdl) R2D_Error("SDL_CreateWindow", SDL_GetError());
+ if (window->icon) R2D_SetIcon(window, window->icon);
+
+ // The window created by SDL might not actually be the requested size.
+ // If it's not the same, retrieve and store the actual window size.
+ int actual_width, actual_height;
+ SDL_GetWindowSize(window->sdl, &actual_width, &actual_height);
+
+ if ((window->width != actual_width) || (window->height != actual_height)) {
+ R2D_Log(R2D_INFO,
+ "Scaling window to %ix%i (requested size was %ix%i)",
+ actual_width, actual_height, window->width, window->height
+ );
+ window->width = actual_width;
+ window->height = actual_height;
+ window->orig_width = actual_width;
+ window->orig_height = actual_height;
+ }
+
+ // Set Up OpenGL /////////////////////////////////////////////////////////////
+
+ R2D_GL_Init(window);
+
+ // SDL 2.0.10 and macOS 10.15 fix ////////////////////////////////////////////
+
+ #if MACOS
+ SDL_SetWindowSize(window->sdl, window->width, window->height);
+ #endif
+
+ // Set Main Loop Data ////////////////////////////////////////////////////////
+
+ const Uint8 *key_state;
+
+ Uint32 frames = 0; // Total frames since start
+ Uint32 frames_last_sec = 0; // Frames in the last second
+ Uint32 start_ms = SDL_GetTicks(); // Elapsed time since start
+ Uint32 next_second_ms = SDL_GetTicks(); // The last time plus a second
+ Uint32 begin_ms = start_ms; // Time at beginning of loop
+ Uint32 end_ms; // Time at end of loop
+ Uint32 elapsed_ms; // Total elapsed time
+ Uint32 loop_ms; // Elapsed time of loop
+ int delay_ms; // Amount of delay to achieve desired frame rate
+ const double decay_rate = 0.5; // Determines how fast an average decays over time
+ double fps = window->fps_cap; // Moving average of actual FPS, initial value a guess
+
+ // Enable VSync
+ if (window->vsync) {
+ if (!SDL_SetHint(SDL_HINT_RENDER_VSYNC, "1")) {
+ R2D_Log(R2D_WARN, "VSync cannot be enabled");
+ }
+ }
+
+ window->close = false;
+
+ // Main Loop /////////////////////////////////////////////////////////////////
+
+ while (!window->close) {
+
+ // Clear Frame /////////////////////////////////////////////////////////////
+
+ R2D_GL_Clear(window->background);
+
+ // Set FPS /////////////////////////////////////////////////////////////////
+
+ frames++;
+ frames_last_sec++;
+ end_ms = SDL_GetTicks();
+ elapsed_ms = end_ms - start_ms;
+
+ // Calculate the frame rate using an exponential moving average
+ if (next_second_ms < end_ms) {
+ fps = decay_rate * fps + (1.0 - decay_rate) * frames_last_sec;
+ frames_last_sec = 0;
+ next_second_ms = SDL_GetTicks() + 1000;
+ }
+
+ loop_ms = end_ms - begin_ms;
+ delay_ms = (1000 / window->fps_cap) - loop_ms;
+
+ if (delay_ms < 0) delay_ms = 0;
+
+ // Note: `loop_ms + delay_ms` should equal `1000 / fps_cap`
+
+ SDL_Delay(delay_ms);
+ begin_ms = SDL_GetTicks();
+
+ // Handle Input and Window Events //////////////////////////////////////////
+
+ int mx, my; // mouse x, y coordinates
+
+ SDL_Event e;
+ while (SDL_PollEvent(&e)) {
+ switch (e.type) {
+
+ case SDL_KEYDOWN:
+ if (window->on_key && e.key.repeat == 0) {
+ R2D_Event event = {
+ .type = R2D_KEY_DOWN, .key = SDL_GetScancodeName(e.key.keysym.scancode)
+ };
+ window->on_key(event);
+ }
+ break;
+
+ case SDL_KEYUP:
+ if (window->on_key) {
+ R2D_Event event = {
+ .type = R2D_KEY_UP, .key = SDL_GetScancodeName(e.key.keysym.scancode)
+ };
+ window->on_key(event);
+ }
+ break;
+
+ case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONUP:
+ if (window->on_mouse) {
+ R2D_GetMouseOnViewport(window, e.button.x, e.button.y, &mx, &my);
+ R2D_Event event = {
+ .button = e.button.button, .x = mx, .y = my
+ };
+ event.type = e.type == SDL_MOUSEBUTTONDOWN ? R2D_MOUSE_DOWN : R2D_MOUSE_UP;
+ event.dblclick = e.button.clicks == 2 ? true : false;
+ window->on_mouse(event);
+ }
+ break;
+
+ case SDL_MOUSEWHEEL:
+ if (window->on_mouse) {
+ R2D_Event event = {
+ .type = R2D_MOUSE_SCROLL, .direction = e.wheel.direction,
+ .delta_x = e.wheel.x, .delta_y = -e.wheel.y
+ };
+ window->on_mouse(event);
+ }
+ break;
+
+ case SDL_MOUSEMOTION:
+ if (window->on_mouse) {
+ R2D_GetMouseOnViewport(window, e.motion.x, e.motion.y, &mx, &my);
+ R2D_Event event = {
+ .type = R2D_MOUSE_MOVE,
+ .x = mx, .y = my, .delta_x = e.motion.xrel, .delta_y = e.motion.yrel
+ };
+ window->on_mouse(event);
+ }
+ break;
+
+ case SDL_CONTROLLERAXISMOTION:
+ if (window->on_controller) {
+ R2D_Event event = {
+ .which = e.caxis.which, .type = R2D_AXIS,
+ .axis = e.caxis.axis, .value = e.caxis.value
+ };
+ window->on_controller(event);
+ }
+ break;
+
+ case SDL_JOYAXISMOTION:
+ if (window->on_controller && !R2D_IsController(e.jbutton.which)) {
+ R2D_Event event = {
+ .which = e.jaxis.which, .type = R2D_AXIS,
+ .axis = e.jaxis.axis, .value = e.jaxis.value
+ };
+ window->on_controller(event);
+ }
+ break;
+
+ case SDL_CONTROLLERBUTTONDOWN: case SDL_CONTROLLERBUTTONUP:
+ if (window->on_controller) {
+ R2D_Event event = {
+ .which = e.cbutton.which, .button = e.cbutton.button
+ };
+ event.type = e.type == SDL_CONTROLLERBUTTONDOWN ? R2D_BUTTON_DOWN : R2D_BUTTON_UP;
+ window->on_controller(event);
+ }
+ break;
+
+ case SDL_JOYBUTTONDOWN: case SDL_JOYBUTTONUP:
+ if (window->on_controller && !R2D_IsController(e.jbutton.which)) {
+ R2D_Event event = {
+ .which = e.jbutton.which, .button = e.jbutton.button
+ };
+ event.type = e.type == SDL_JOYBUTTONDOWN ? R2D_BUTTON_DOWN : R2D_BUTTON_UP;
+ window->on_controller(event);
+ }
+ break;
+
+ case SDL_JOYDEVICEADDED:
+ R2D_Log(R2D_INFO, "Controller connected (%i total)", SDL_NumJoysticks());
+ R2D_OpenControllers();
+ break;
+
+ case SDL_JOYDEVICEREMOVED:
+ if (R2D_IsController(e.jdevice.which)) {
+ R2D_Log(R2D_INFO, "Controller #%i: %s removed (%i remaining)", e.jdevice.which, SDL_GameControllerName(SDL_GameControllerFromInstanceID(e.jdevice.which)), SDL_NumJoysticks());
+ SDL_GameControllerClose(SDL_GameControllerFromInstanceID(e.jdevice.which));
+ } else {
+ R2D_Log(R2D_INFO, "Controller #%i: %s removed (%i remaining)", e.jdevice.which, SDL_JoystickName(SDL_JoystickFromInstanceID(e.jdevice.which)), SDL_NumJoysticks());
+ SDL_JoystickClose(SDL_JoystickFromInstanceID(e.jdevice.which));
+ }
+ break;
+
+ case SDL_WINDOWEVENT:
+ switch (e.window.event) {
+ case SDL_WINDOWEVENT_RESIZED:
+ // Store new window size, set viewport
+ window->width = e.window.data1;
+ window->height = e.window.data2;
+ R2D_GL_SetViewport(window);
+ break;
+ }
+ break;
+
+ case SDL_QUIT:
+ R2D_Close(window);
+ break;
+ }
+ }
+
+ // Detect keys held down
+ int num_keys;
+ key_state = SDL_GetKeyboardState(&num_keys);
+
+ for (int i = 0; i < num_keys; i++) {
+ if (window->on_key) {
+ if (key_state[i] == 1) {
+ R2D_Event event = {
+ .type = R2D_KEY_HELD, .key = SDL_GetScancodeName(i)
+ };
+ window->on_key(event);
+ }
+ }
+ }
+
+ // Get and store mouse position relative to the viewport
+ int wx, wy; // mouse x, y coordinates relative to the window
+ SDL_GetMouseState(&wx, &wy);
+ R2D_GetMouseOnViewport(window, wx, wy, &window->mouse.x, &window->mouse.y);
+
+ // Update Window State /////////////////////////////////////////////////////
+
+ // Store new values in the window
+ window->frames = frames;
+ window->elapsed_ms = elapsed_ms;
+ window->loop_ms = loop_ms;
+ window->delay_ms = delay_ms;
+ window->fps = fps;
+
+ // Call update and render callbacks
+ if (window->update) window->update();
+ if (window->render) window->render();
+
+ // Draw Frame //////////////////////////////////////////////////////////////
+
+ // Render and flush all OpenGL buffers
+ R2D_GL_FlushBuffers();
+
+ // Swap buffers to display drawn contents in the window
+ SDL_GL_SwapWindow(window->sdl);
+ }
+
+ return 0;
+}
+
+
+/*
+ * Set the icon for the window
+ */
+void R2D_SetIcon(R2D_Window *window, const char *icon) {
+ R2D_Image *img = R2D_CreateImage(icon);
+ if (img) {
+ window->icon = icon;
+ SDL_SetWindowIcon(window->sdl, img->surface);
+ R2D_FreeImage(img);
+ } else {
+ R2D_Log(R2D_WARN, "Could not set window icon");
+ }
+}
+
+
+/*
+ * Take a screenshot of the window
+ */
+void R2D_Screenshot(R2D_Window *window, const char *path) {
+
+ #if GLES
+ R2D_Error("R2D_Screenshot", "Not supported in OpenGL ES");
+ #else
+ // Create a surface the size of the window
+ SDL_Surface *surface = SDL_CreateRGBSurface(
+ SDL_SWSURFACE, window->width, window->height, 24,
+ 0x000000FF, 0x0000FF00, 0x00FF0000, 0
+ );
+
+ // Grab the pixels from the front buffer, save to surface
+ glReadBuffer(GL_FRONT);
+ glReadPixels(0, 0, window->width, window->height, GL_RGB, GL_UNSIGNED_BYTE, surface->pixels);
+
+ // Flip image vertically
+
+ void *temp_row = (void *)malloc(surface->pitch);
+ if (!temp_row) {
+ R2D_Error("R2D_Screenshot", "Out of memory!");
+ SDL_FreeSurface(surface);
+ return;
+ }
+
+ int height_div_2 = (int) (surface->h * 0.5);
+
+ for (int index = 0; index < height_div_2; index++) {
+ memcpy((Uint8 *)temp_row,(Uint8 *)(surface->pixels) + surface->pitch * index, surface->pitch);
+ memcpy((Uint8 *)(surface->pixels) + surface->pitch * index, (Uint8 *)(surface->pixels) + surface->pitch * (surface->h - index-1), surface->pitch);
+ memcpy((Uint8 *)(surface->pixels) + surface->pitch * (surface->h - index-1), temp_row, surface->pitch);
+ }
+
+ free(temp_row);
+
+ // Save image to disk
+ IMG_SavePNG(surface, path);
+ SDL_FreeSurface(surface);
+ #endif
+}
+
+
+/*
+ * Close the window
+ */
+int R2D_Close(R2D_Window *window) {
+ if (!window->close) {
+ R2D_Log(R2D_INFO, "Closing window");
+ window->close = true;
+ }
+ return 0;
+}
+
+
+/*
+ * Free all resources
+ */
+int R2D_FreeWindow(R2D_Window *window) {
+ R2D_Close(window);
+ SDL_GL_DeleteContext(window->glcontext);
+ SDL_DestroyWindow(window->sdl);
+ free(window);
+ return 0;
+}
diff --git a/lib/ruby2d/cli/build.rb b/lib/ruby2d/cli/build.rb
index 9eb0728..1a47d1a 100644
--- a/lib/ruby2d/cli/build.rb
+++ b/lib/ruby2d/cli/build.rb
@@ -96,7 +96,7 @@ def build_native(rb_file)
end
# Compile to a native executable
- `cc build/app.c -lmruby \`simple2d --libs\` -o build/app`
+ `cc build/app.c -lmruby -o build/app`
# Clean up
clean_up unless @debug
@@ -162,12 +162,6 @@ end
def build_ios_tvos(rb_file, device)
check_build_src_file(rb_file)
- # Check for Simple 2D framework,
- unless File.exist?('/usr/local/Frameworks/Simple2D/iOS/Simple2D.framework') && File.exist?('/usr/local/Frameworks/Simple2D/tvOS/Simple2D.framework')
- puts "#{'Error:'.error} Simple 2D iOS and tvOS frameworks not found. Install them and try again.\n"
- exit
- end
-
# Check if MRuby exists; if not, quit
if `which mruby`.empty?
puts "#{'Error:'.error} Can't find MRuby, which is needed to build native Ruby 2D applications.\n"
@@ -197,6 +191,7 @@ def build_ios_tvos(rb_file, device)
f << File.read("#{@gem_dir}/ext/ruby2d/ruby2d.c")
end
+ # TODO: Need add this functionality to the gem
# Build the Xcode project
`simple2d build --#{device} build/#{device}/MyApp.xcodeproj`
diff --git a/ruby2d.gemspec b/ruby2d.gemspec
index 7afa33d..0247802 100644
--- a/ruby2d.gemspec
+++ b/ruby2d.gemspec
@@ -15,7 +15,7 @@ Gem::Specification.new do |s|
s.files = Dir.glob('lib/**/*') +
Dir.glob('assets/**/*') +
- Dir.glob('ext/**/*.{c,js,rb}')
+ Dir.glob('ext/**/*.{h,c,rb}')
s.extensions = ['ext/ruby2d/extconf.rb']
s.executables << 'ruby2d'
end