diff options
| author | raysan5 <raysan5@gmail.com> | 2014-09-16 22:51:31 +0200 |
|---|---|---|
| committer | raysan5 <raysan5@gmail.com> | 2014-09-16 22:51:31 +0200 |
| commit | fc6081fe70ab7c3b037c0ab9f38478904d3cdde2 (patch) | |
| tree | 6635fd800fa673ef3fb568c6f47ebc76a2b8ad6e /src | |
| parent | 01651af08a494b1ac08c897695891ad7cf44ad47 (diff) | |
| download | raylib-fc6081fe70ab7c3b037c0ab9f38478904d3cdde2.tar.gz raylib-fc6081fe70ab7c3b037c0ab9f38478904d3cdde2.zip | |
raylib 1.2
This is a huge update. Check CHANGELOG for details
Diffstat (limited to 'src')
| -rw-r--r-- | src/audio.c | 65 | ||||
| -rw-r--r-- | src/core.c | 1422 | ||||
| -rw-r--r-- | src/makefile | 130 | ||||
| -rw-r--r-- | src/models.c | 76 | ||||
| -rw-r--r-- | src/raylib.h | 68 | ||||
| -rw-r--r-- | src/raymath.c | 41 | ||||
| -rw-r--r-- | src/raymath.h | 10 | ||||
| -rw-r--r-- | src/resources | bin | 0 -> 107204 bytes | |||
| -rw-r--r-- | src/rlgl.c | 695 | ||||
| -rw-r--r-- | src/rlgl.h | 77 | ||||
| -rw-r--r-- | src/shapes.c | 70 | ||||
| -rw-r--r-- | src/stb_image.c | 9 | ||||
| -rw-r--r-- | src/stb_image.h | 6 | ||||
| -rw-r--r-- | src/stb_vorbis.h | 5 | ||||
| -rw-r--r-- | src/text.c | 85 | ||||
| -rw-r--r-- | src/textures.c | 145 | ||||
| -rw-r--r-- | src/utils.c | 130 | ||||
| -rw-r--r-- | src/utils.h | 23 |
18 files changed, 2362 insertions, 695 deletions
diff --git a/src/audio.c b/src/audio.c index 03f72a2e..cc1d9f9a 100644 --- a/src/audio.c +++ b/src/audio.c @@ -1,14 +1,14 @@ -/********************************************************************************************* +/********************************************************************************************** * * raylib.audio * * Basic functions to manage Audio: InitAudioDevice, LoadAudioFiles, PlayAudioFiles * * Uses external lib: -* OpenAL - Audio device management lib -* stb_vorbis - Ogg audio files loading +* OpenAL Soft - Audio device management lib (http://kcat.strangesoft.net/openal.html) +* stb_vorbis - Ogg audio files loading (http://www.nothings.org/stb_vorbis/) * -* Copyright (c) 2013 Ramon Santamaria (Ray San - raysan@raysanweb.com) +* Copyright (c) 2014 Ramon Santamaria (Ray San - raysan@raysanweb.com) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. @@ -29,22 +29,25 @@ #include "raylib.h" -#include <AL/al.h> // OpenAL basic header -#include <AL/alc.h> // OpenAL context header (like OpenGL, OpenAL requires a context to work) +#include "AL/al.h" // OpenAL basic header +#include "AL/alc.h" // OpenAL context header (like OpenGL, OpenAL requires a context to work) -#include <stdlib.h> // Declares malloc() and free() for memory management -#include <string.h> // Required for strcmp() -#include <stdio.h> // Used for .WAV loading +#include <stdlib.h> // Declares malloc() and free() for memory management +#include <string.h> // Required for strcmp() +#include <stdio.h> // Used for .WAV loading -#include "utils.h" // rRES data decompression utility function +#include "utils.h" // rRES data decompression utility function + // NOTE: Includes Android fopen function map -#include "stb_vorbis.h" // OGG loading functions +#include "stb_vorbis.h" // OGG loading functions //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- #define MUSIC_STREAM_BUFFERS 2 -#define MUSIC_BUFFER_SIZE 4096*8 //4096*32 +#define MUSIC_BUFFER_SIZE 4096*2 // PCM data buffer (short) - 16Kb + // NOTE: Reduced to avoid frame-stalls on RPI +//#define MUSIC_BUFFER_SIZE 4096*8 // PCM data buffer (short) - 64Kb //---------------------------------------------------------------------------------- // Types and Structures Definition @@ -85,9 +88,9 @@ static Music currentMusic; // Current music loaded //---------------------------------------------------------------------------------- // Module specific Functions Declaration //---------------------------------------------------------------------------------- -static Wave LoadWAV(const char *fileName); -static Wave LoadOGG(char *fileName); -static void UnloadWave(Wave wave); +static Wave LoadWAV(const char *fileName); // Load WAV file +static Wave LoadOGG(char *fileName); // Load OGG file +static void UnloadWave(Wave wave); // Unload wave data static bool BufferMusicStream(ALuint buffer); // Fill music buffers with data static void EmptyMusicStream(void); // Empty music buffers @@ -116,7 +119,7 @@ void InitAudioDevice(void) TraceLog(ERROR, "Could not setup audio context"); } - TraceLog(INFO, "Audio device and context initialized successfully: %s\n", alcGetString(device, ALC_DEVICE_SPECIFIER)); + TraceLog(INFO, "Audio device and context initialized successfully: %s", alcGetString(device, ALC_DEVICE_SPECIFIER)); // Listener definition (just for 2D) alListener3f(AL_POSITION, 0, 0, 0); @@ -151,6 +154,13 @@ Sound LoadSound(char *fileName) Sound sound; Wave wave; + // Init some default values for wave... + wave.data = NULL; + wave.dataSize = 0; + wave.sampleRate = 0; + wave.bitsPerSample = 0; + wave.channels = 0; + // NOTE: The entire file is loaded to memory to play it all at once (no-streaming) // Audio file loading @@ -297,7 +307,6 @@ Sound LoadSoundFromRES(const char *rresName, int resId) else if (wave.bitsPerSample == 16) format = AL_FORMAT_STEREO16; } - // Create an audio source ALuint source; alGenSources(1, &source); // Generate pointer to audio source @@ -506,8 +515,23 @@ void StopMusicStream(void) // Pause music playing void PauseMusicStream(void) { - // TODO: Record music is paused or check if music available! - alSourcePause(currentMusic.source); + // Pause music stream if music available! + if (musicEnabled) + { + TraceLog(INFO, "Pausing music stream"); + alSourcePause(currentMusic.source); + } +} + +// Resume music playing +void ResumeMusicStream(void) +{ + // Resume music playing... if music available! + if (musicEnabled) + { + TraceLog(INFO, "Resume music stream"); + alSourcePlay(currentMusic.source); + } } // Check if music is playing @@ -570,7 +594,7 @@ static bool BufferMusicStream(ALuint buffer) else break; } - TraceLog(DEBUG, "Streaming music data to buffer. Bytes streamed: %i", size); + //TraceLog(DEBUG, "Streaming music data to buffer. Bytes streamed: %i", size); } if (size > 0) @@ -754,6 +778,7 @@ static Wave LoadWAV(const char *fileName) } // Load OGG file into Wave structure +// NOTE: Using stb_vorbis library static Wave LoadOGG(char *fileName) { Wave wave; @@ -1,13 +1,22 @@ -/********************************************************************************************* +/********************************************************************************************** * * raylib.core * -* Basic functions to manage Windows, OpenGL context and Input +* Basic functions to manage windows, OpenGL context and input on multiple platforms * -* Uses external lib: -* GLFW3 - Window, context and Input management (static lib version) +* The following platforms are supported: +* PLATFORM_DESKTOP - Windows, Linux, Mac (OSX) +* PLATFORM_ANDROID - Only OpenGL ES 2.0 devices +* PLATFORM_RPI - Rapsberry Pi (tested on Raspbian) * -* Copyright (c) 2013 Ramon Santamaria (Ray San - raysan@raysanweb.com) +* On PLATFORM_DESKTOP, the external lib GLFW3 (www.glfw.com) is used to manage graphic +* device, OpenGL context and input on multiple operating systems (Windows, Linux, OSX). +* +* On PLATFORM_ANDROID, graphic device is managed by EGL and input system by Android activity. +* +* On PLATFORM_RPI, graphic device is managed by EGL and input system is coded in raw mode. +* +* Copyright (c) 2014 Ramon Santamaria (Ray San - raysan@raysanweb.com) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. @@ -26,27 +35,63 @@ * **********************************************************************************************/ -#include "raylib.h" - +#include "raylib.h" // raylib main header #include "rlgl.h" // raylib OpenGL abstraction layer to OpenGL 1.1, 3.3+ or ES2 +#include "raymath.h" // Required for data type Matrix and Matrix functions +#include "utils.h" // TraceLog() function + // NOTE: Includes Android fopen map, InitAssetManager() -#include <GLFW/glfw3.h> // GLFW3 lib: Windows, OpenGL context and Input management -//#include <GL/gl.h> // OpenGL functions (GLFW3 already includes gl.h) #include <stdio.h> // Standard input / output lib -#include <stdlib.h> // Declares malloc() and free() for memory management, rand() -#include <time.h> // Useful to initialize random seed +#include <stdlib.h> // Declares malloc() and free() for memory management, rand(), atexit() +#include <stdint.h> // Required for typedef unsigned long long int uint64_t, used by hi-res timer +#include <time.h> // Useful to initialize random seed - Android/RPI hi-res timer #include <math.h> // Math related functions, tan() used to set perspective -//#include "vector3.h" // Basic Vector3 functions, not required any more, replaced by raymath -#include "utils.h" // WritePNG() function +#include <string.h> // String function definitions, memset() +#include <errno.h> // Macros for reporting and retrieving error conditions through error codes -#include "raymath.h" // Required for data type Matrix and Matrix functions +#if defined(PLATFORM_DESKTOP) + #include <GLFW/glfw3.h> // GLFW3 library: Windows, OpenGL context and Input management + //#include <GL/gl.h> // OpenGL functions (GLFW3 already includes gl.h) + //#define GLFW_DLL // Using GLFW DLL on Windows -> No, we use static version! +#endif + +#if defined(PLATFORM_ANDROID) + #include <jni.h> // Java native interface + #include <android/sensor.h> // Android sensors functions + #include <android/window.h> // Defines AWINDOW_FLAG_FULLSCREEN and others + //#include <android_native_app_glue.h> // Defines basic app state struct and manages activity -//#define GLFW_DLL // Using GLFW DLL on Windows -> No, we use static version! + #include <EGL/egl.h> // Khronos EGL library - Native platform display device control functions + #include <GLES2/gl2.h> // Khronos OpenGL ES 2.0 library +#endif + +#if defined(PLATFORM_RPI) + #include <fcntl.h> // POSIX file control definitions - open(), creat(), fcntl() + #include <unistd.h> // POSIX standard function definitions - read(), close(), STDIN_FILENO + #include <termios.h> // POSIX terminal control definitions - tcgetattr(), tcsetattr() + #include <pthread.h> // POSIX threads management (mouse input) + + #include <sys/ioctl.h> // UNIX System call for device-specific input/output operations - ioctl() + #include <linux/kd.h> // Linux: KDSKBMODE, K_MEDIUMRAM constants definition + #include <linux/input.h> // Linux: Keycodes constants definition (KEY_A, ...) + #include <linux/joystick.h> + + #include "bcm_host.h" // Raspberry Pi VideoCore IV access functions + + #include "EGL/egl.h" // Khronos EGL library - Native platform display device control functions + #include "EGL/eglext.h" // Khronos EGL library - Extensions + #include "GLES2/gl2.h" // Khronos OpenGL ES 2.0 library + + #define DEFAULT_KEYBOARD_DEV "/dev/input/event0" // Not used, keyboard inputs are read raw from stdin + #define DEFAULT_MOUSE_DEV "/dev/input/event1" + //#define DEFAULT_MOUSE_DEV "/dev/input/mouse0" + #define DEFAULT_GAMEPAD_DEV "/dev/input/js0" +#endif //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- -// Nop... +// ... //---------------------------------------------------------------------------------- // Types and Structures Definition @@ -56,22 +101,64 @@ //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- -static GLFWwindow* window; // Main window -static bool fullscreen; // Fullscreen mode track +#if defined(PLATFORM_DESKTOP) +static GLFWwindow *window; // Native window (graphic device) +#elif defined(PLATFORM_ANDROID) +static struct android_app *app; // Android activity +static struct android_poll_source *source; // Android events polling source +static int ident, events; +static bool windowReady = false; // Used to detect display initialization +#elif defined(PLATFORM_RPI) +static EGL_DISPMANX_WINDOW_T nativeWindow; // Native window (graphic device) + +// Input variables (mouse/keyboard) +static int mouseStream = -1; // Mouse device file descriptor +static bool mouseReady = false; // Flag to know if mouse is ready +pthread_t mouseThreadId; // Mouse reading thread id + +// NOTE: For keyboard we will use the standard input (but reconfigured...) +static int defaultKeyboardMode; // Used to store default keyboard mode +static struct termios defaultKeyboardSettings; // Used to staore default keyboard settings + +static int keyboardMode = 0; // Keyboard mode: 1 (KEYCODES), 2 (ASCII) + +// This array maps Unix keycodes to ASCII equivalent and to GLFW3 equivalent for special function keys (>256) +const short UnixKeycodeToASCII[128] = { 256, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 45, 61, 259, 9, 81, 87, 69, 82, 84, 89, 85, 73, 79, 80, 91, 93, 257, 341, 65, 83, 68, + 70, 71, 72, 74, 75, 76, 59, 39, 96, 340, 92, 90, 88, 67, 86, 66, 78, 77, 44, 46, 47, 344, -1, 342, 32, -1, 290, 291, 292, 293, 294, 295, 296, + 297, 298, 299, -1, -1, -1, -1, -1, 45, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 257, 345, 47, -1, + 346, -1, -1, 265, -1, 263, 262, -1, 264, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; + +static int gamepadStream = -1; // Gamepad device file descriptor +#endif -static double currentTime, previousTime; // Used to track timmings -static double updateTime, drawTime; // Time measures for update and draw -static double frameTime; // Time measure for one frame -static double targetTime = 0; // Desired time for one frame, if 0 not applied +#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) +static EGLDisplay display; // Native display device (physical screen connection) +static EGLSurface surface; // Surface to draw on, framebuffers (connected to context) +static EGLContext context; // Graphic context, mode in which drawing can be done + +static uint64_t baseTime; // Base time measure for hi-res timer +static bool windowShouldClose = false; // Flag to set window for closing +#endif + +static unsigned int displayWidth, displayHeight; // Display width and height (monitor, device-screen, LCD, ...) +static int screenWidth, screenHeight; // Screen width and height (used render area) +static int renderWidth, renderHeight; // Framebuffer width and height (render area) + // NOTE: Framebuffer could include black bars -static int windowWidth, windowHeight; // Required to switch between windowed/fullscren mode (F11) -static const char *windowTitle; // Required to switch between windowed/fullscren mode (F11) -static int exitKey = GLFW_KEY_ESCAPE; // Default exit key (ESC) +static int renderOffsetX = 0; // Offset X from render area (must be divided by 2) +static int renderOffsetY = 0; // Offset Y from render area (must be divided by 2) +static bool fullscreen = false; // Fullscreen mode (useful only for PLATFORM_DESKTOP) +static Matrix downscaleView; // Matrix to downscale view (in case screen size bigger than display size) + +#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) +static const char *windowTitle; // Window text title... static bool customCursor = false; // Tracks if custom cursor has been set static bool cursorOnScreen = false; // Tracks if cursor is inside client area static Texture2D cursor; // Cursor texture +static Vector2 mousePosition; + static char previousKeyState[512] = { 0 }; // Required to check if key pressed/released once static char currentKeyState[512] = { 0 }; // Required to check if key pressed/released once @@ -84,126 +171,227 @@ static char currentGamepadState[32] = {0}; // Required to check if gamepad btn static int previousMouseWheelY = 0; // Required to track mouse wheel variation static int currentMouseWheelY = 0; // Required to track mouse wheel variation -static Color background = { 0, 0, 0, 0 }; // Screen background color +static int exitKey = KEY_ESCAPE; // Default exit key (ESC) +#endif + +#if defined(PLATFORM_ANDROID) +static float touchX; // Touch position X +static float touchY; // Touch position Y +#endif + +static double currentTime, previousTime; // Used to track timmings +static double updateTime, drawTime; // Time measures for update and draw +static double frameTime; // Time measure for one frame +static double targetTime = 0.0; // Desired time for one frame, if 0 not applied static bool showLogo = false; //---------------------------------------------------------------------------------- // Other Modules Functions Declaration (required by core) //---------------------------------------------------------------------------------- -extern void LoadDefaultFont(void); // [Module: text] Loads default font on InitWindow() -extern void UnloadDefaultFont(void); // [Module: text] Unloads default font from GPU memory +extern void LoadDefaultFont(void); // [Module: text] Loads default font on InitWindow() +extern void UnloadDefaultFont(void); // [Module: text] Unloads default font from GPU memory -extern void UpdateMusicStream(void); // [Module: audio] Updates buffers for music streaming +extern void UpdateMusicStream(void); // [Module: audio] Updates buffers for music streaming //---------------------------------------------------------------------------------- // Module specific Functions Declaration //---------------------------------------------------------------------------------- +static void InitDisplay(int width, int height); // Initialize display device and framebuffer +static void InitGraphics(void); // Initialize OpenGL graphics +static void InitTimer(void); // Initialize timer +static double GetTime(void); // Returns time since InitTimer() was run +static bool GetKeyStatus(int key); // Returns if a key has been pressed +static bool GetMouseButtonStatus(int button); // Returns if a mouse button has been pressed +static void SwapBuffers(void); // Copy back buffer to front buffers +static void PollInputEvents(void); // Register user events +static void LogoAnimation(void); // Plays raylib logo appearing animation +static void SetupFramebufferSize(int displayWidth, int displayHeight); +#if defined(PLATFORM_RPI) +static void InitMouse(void); // Mouse initialization (including mouse thread) +static void *MouseThread(void *arg); // Mouse reading thread +static void InitKeyboard(void); // Init raw keyboard system (standard input reading) +static void RestoreKeyboard(void); // Restore keyboard system +static void InitGamepad(void); // Init raw gamepad input +#endif + +#if defined(PLATFORM_DESKTOP) static void ErrorCallback(int error, const char *description); // GLFW3 Error Callback, runs on GLFW3 error -static void KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods); // GLFW3 Keyboard Callback, runs on key pressed -static void ScrollCallback(GLFWwindow* window, double xoffset, double yoffset); // GLFW3 Srolling Callback, runs on mouse wheel -static void CursorEnterCallback(GLFWwindow* window, int enter); // GLFW3 Cursor Enter Callback, cursor enters client area -static void WindowSizeCallback(GLFWwindow* window, int width, int height); // GLFW3 WindowSize Callback, runs when window is resized +static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods); // GLFW3 Keyboard Callback, runs on key pressed +static void ScrollCallback(GLFWwindow *window, double xoffset, double yoffset); // GLFW3 Srolling Callback, runs on mouse wheel +static void CursorEnterCallback(GLFWwindow *window, int enter); // GLFW3 Cursor Enter Callback, cursor enters client area +static void WindowSizeCallback(GLFWwindow *window, int width, int height); // GLFW3 WindowSize Callback, runs when window is resized static void TakeScreenshot(void); // Takes a screenshot and saves it in the same folder as executable -static void LogoAnimation(void); // Plays raylib logo appearing animation +#endif + +#if defined(PLATFORM_ANDROID) +static int32_t InputCallback(struct android_app *app, AInputEvent *event); // Process Android activity input events +static void CommandCallback(struct android_app *app, int32_t cmd); // Process Android activity lifecycle commands +#endif //---------------------------------------------------------------------------------- // Module Functions Definition - Window and OpenGL Context Functions //---------------------------------------------------------------------------------- - +#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) // Initialize Window and Graphics Context (OpenGL) void InitWindow(int width, int height, const char *title) { - InitWindowEx(width, height, title, true, NULL); -} + // Store window title (could be useful...) + windowTitle = title; -// Initialize Window and Graphics Context (OpenGL) with extended parameters -void InitWindowEx(int width, int height, const char* title, bool resizable, const char *cursorImage) -{ - glfwSetErrorCallback(ErrorCallback); + // Init device display (monitor, LCD, ...) + InitDisplay(width, height); - if (!glfwInit()) TraceLog(ERROR, "Failed to initialize GLFW"); + // Init OpenGL graphics + InitGraphics(); - //glfwDefaultWindowHints() // Set default windows hints + // Load default font for convenience + // NOTE: External function (defined in module: text) + LoadDefaultFont(); - if (!resizable) glfwWindowHint(GLFW_RESIZABLE, GL_FALSE); // Avoid window being resizable + // Init hi-res timer + InitTimer(); -#ifdef USE_OPENGL_33 - //glfwWindowHint(GLFW_SAMPLES, 4); // Enables multisampling x4 (MSAA), default is 0 - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); - glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); - glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_FALSE); +#if defined(PLATFORM_RPI) + // Init raw input system + InitMouse(); // Mouse init + InitKeyboard(); // Keyboard init + InitGamepad(); // Gamepad init #endif + mousePosition.x = screenWidth/2; + mousePosition.y = screenHeight/2; - window = glfwCreateWindow(width, height, title, NULL, NULL); - - windowWidth = width; - windowHeight = height; - windowTitle = title; - - if (!window) + // raylib logo appearing animation (if enabled) + if (showLogo) { - glfwTerminate(); - TraceLog(ERROR, "Failed to initialize Window"); + SetTargetFPS(60); + LogoAnimation(); } +} - glfwSetWindowSizeCallback(window, WindowSizeCallback); - glfwSetCursorEnterCallback(window, CursorEnterCallback); +#elif defined(PLATFORM_ANDROID) +// Android activity initialization +void InitWindow(int width, int height, struct android_app *state) +{ + app_dummy(); - glfwMakeContextCurrent(window); - glfwSetKeyCallback(window, KeyCallback); - glfwSetScrollCallback(window, ScrollCallback); - glfwSwapInterval(0); // Disables GPU v-sync (if set), so frames are not limited to screen refresh rate (60Hz -> 60 FPS) - // If not set, swap interval uses GPU v-sync configuration - // Framerate can be setup using SetTargetFPS() + screenWidth = width; + screenHeight = height; - //------------------------------------------------------ -#if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2) - rlglInit(); // Init rlgl -#endif - //------------------------------------------------------ + app = state; - int fbWidth, fbHeight; - glfwGetFramebufferSize(window, &fbWidth, &fbHeight); // Get framebuffer size of current window + // Set desired windows flags before initializing anything + ANativeActivity_setWindowFlags(app->activity, AWINDOW_FLAG_FULLSCREEN, 0); //AWINDOW_FLAG_SCALED, AWINDOW_FLAG_DITHER + //ANativeActivity_setWindowFlags(app->activity, AWINDOW_FLAG_FORCE_NOT_FULLSCREEN, AWINDOW_FLAG_FULLSCREEN); - //------------------------------------------------------ - rlglInitGraphicsDevice(fbWidth, fbHeight); - //------------------------------------------------------ + int orientation = AConfiguration_getOrientation(app->config); - previousTime = glfwGetTime(); + if (orientation == ACONFIGURATION_ORIENTATION_PORT) TraceLog(INFO, "PORTRAIT window orientation"); + else if (orientation == ACONFIGURATION_ORIENTATION_LAND) TraceLog(INFO, "LANDSCAPE window orientation"); - LoadDefaultFont(); // NOTE: External function (defined in module: text) + // TODO: Review, it doesn't work... + if (width <= height) + { + AConfiguration_setOrientation(app->config, ACONFIGURATION_ORIENTATION_PORT); + TraceLog(WARNING, "Window set to portraid mode"); + } + else + { + AConfiguration_setOrientation(app->config, ACONFIGURATION_ORIENTATION_LAND); + TraceLog(WARNING, "Window set to landscape mode"); + } - if (cursorImage != NULL) SetCustomCursor(cursorImage); + //AConfiguration_getDensity(app->config); + //AConfiguration_getKeyboard(app->config); + //AConfiguration_getScreenSize(app->config); + //AConfiguration_getScreenLong(app->config); - srand(time(NULL)); // Initialize random seed + //state->userData = &engine; + app->onAppCmd = CommandCallback; + app->onInputEvent = InputCallback; - ClearBackground(RAYWHITE); // Default background color for raylib games :P + InitAssetManager(app->activity->assetManager); - // raylib logo appearing animation - if (showLogo) + TraceLog(INFO, "Android app initialized successfully"); + + while (!windowReady) { - SetTargetFPS(60); - LogoAnimation(); + // Wait for window to be initialized (display and context) + // Process events loop + while ((ident = ALooper_pollAll(0, NULL, &events,(void**)&source)) >= 0) + { + // Process this event + if (source != NULL) source->process(app, source); + + // Check if we are exiting + if (app->destroyRequested != 0) windowShouldClose = true; + } } } +#endif // Close Window and Terminate Context void CloseWindow(void) { UnloadDefaultFont(); - //------------------------------------------------------ -#if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2) - rlglClose(); // De-init rlgl -#endif - //------------------------------------------------------ + rlglClose(); // De-init rlgl +#if defined(PLATFORM_DESKTOP) glfwDestroyWindow(window); glfwTerminate(); +#elif defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) + // Close surface, context and display + if (display != EGL_NO_DISPLAY) + { + eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + + if (surface != EGL_NO_SURFACE) + { + eglDestroySurface(display, surface); + surface = EGL_NO_SURFACE; + } + + if (context != EGL_NO_CONTEXT) + { + eglDestroyContext(display, context); + context = EGL_NO_CONTEXT; + } + + eglTerminate(display); + display = EGL_NO_DISPLAY; + } +#endif + + TraceLog(INFO, "Window closed successfully"); } +// Detect if KEY_ESCAPE pressed or Close icon pressed +bool WindowShouldClose(void) +{ +#if defined(PLATFORM_DESKTOP) + return (glfwWindowShouldClose(window)); +#elif defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) + return windowShouldClose; +#endif +} + +// Fullscreen toggle +void ToggleFullscreen(void) +{ +#if defined(PLATFORM_DESKTOP) + fullscreen = !fullscreen; // Toggle fullscreen flag + + rlglClose(); // De-init rlgl + glfwDestroyWindow(window); // Destroy the current window (we will recreate it!) + + InitWindow(screenWidth, screenHeight, windowTitle); +#elif defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) + TraceLog(WARNING, "Could not toggle to windowed mode"); +#endif +} + +#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) // Set a custom cursor icon/image void SetCustomCursor(const char *cursorImage) { @@ -211,7 +399,9 @@ void SetCustomCursor(const char *cursorImage) cursor = LoadTexture(cursorImage); +#if defined(PLATFORM_DESKTOP) glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); +#endif customCursor = true; } @@ -221,71 +411,31 @@ void SetExitKey(int key) { exitKey = key; } +#endif -// Detect if KEY_ESCAPE pressed or Close icon pressed -bool WindowShouldClose(void) +// Get current screen width +int GetScreenWidth(void) { - return (glfwWindowShouldClose(window)); + return screenWidth; } -// Fullscreen toggle (by default F11) -void ToggleFullscreen(void) +// Get current screen height +int GetScreenHeight(void) { - if (glfwGetKey(window, GLFW_KEY_F11)) - { - fullscreen = !fullscreen; // Toggle fullscreen flag - - UnloadDefaultFont(); - - glfwDestroyWindow(window); // Destroy the current window (we will recreate it!) - - // TODO: WARNING! All loaded resources are lost, we loose Context! - - // NOTE: Window aspect ratio is always windowWidth / windowHeight - if (fullscreen) - { - // TODO: Get desktop window size and adapt aspect-ratio (?) - //const GLFWvidmode *mode = glfwGetVideoMode(glfwGetPrimaryMonitor()); - //windowWidth = mode->width; - //windowHeight = mode->height; - - window = glfwCreateWindow(windowWidth, windowHeight, windowTitle, glfwGetPrimaryMonitor(), NULL); // Fullscreen mode - } - else window = glfwCreateWindow(windowWidth, windowHeight, windowTitle, NULL, NULL); - - if (!window) - { - glfwTerminate(); - TraceLog(ERROR, "Failed to initialize Window when switching fullscreen mode"); - } - - glfwMakeContextCurrent(window); - glfwSetKeyCallback(window, KeyCallback); - - int fbWidth, fbHeight; - glfwGetFramebufferSize(window, &fbWidth, &fbHeight); // Get framebuffer size of current window - - rlglInitGraphicsDevice(fbWidth, fbHeight); - - LoadDefaultFont(); - } + return screenHeight; } // Sets Background Color void ClearBackground(Color color) { - if ((color.r != background.r) || (color.g != background.g) || (color.b != background.b) || (color.a != background.a)) - { - rlClearColor(color.r, color.g, color.b, color.a); - - background = color; - } + // TODO: Review "clearing area", full framebuffer vs render area + rlClearColor(color.r, color.g, color.b, color.a); } // Setup drawing canvas to start drawing void BeginDrawing(void) { - currentTime = glfwGetTime(); // glfwGetTime() returns a 'double' containing the number of elapsed seconds since glfwInit() was called + currentTime = GetTime(); // Number of elapsed seconds since InitTimer() was called updateTime = currentTime - previousTime; previousTime = currentTime; @@ -293,40 +443,34 @@ void BeginDrawing(void) rlLoadIdentity(); // Reset current matrix (MODELVIEW) -//#ifdef USE_OPENGL_11 -// rlTranslatef(0.375, 0.375, 0); // HACK to have 2D pixel-perfect drawing on OpenGL + rlMultMatrixf(GetMatrixVector(downscaleView)); // If downscale required, apply it here + +// rlTranslatef(0.375, 0.375, 0); // HACK to have 2D pixel-perfect drawing on OpenGL 1.1 // NOTE: Not required with OpenGL 3.3+ -//#endif } // End canvas drawing and Swap Buffers (Double Buffering) void EndDrawing(void) { - if (customCursor && cursorOnScreen) DrawTexture(cursor, GetMouseX(), GetMouseY(), WHITE); - - //------------------------------------------------------ -#if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2) - rlglDraw(); // Draw Buffers -#endif - //------------------------------------------------------ + rlglDraw(); // Draw Buffers (Only OpenGL 3+ and ES2) - glfwSwapBuffers(window); // Swap back and front buffers - glfwPollEvents(); // Register keyboard/mouse events + SwapBuffers(); // Copy back buffer to front buffer + PollInputEvents(); // Poll user events - UpdateMusicStream(); // NOTE: Function checks if music is enabled + UpdateMusicStream(); // NOTE: Function checks if music is enabled - currentTime = glfwGetTime(); + currentTime = GetTime(); drawTime = currentTime - previousTime; previousTime = currentTime; frameTime = updateTime + drawTime; - double extraTime = 0; + double extraTime = 0.0; while (frameTime < targetTime) { // Implement a delay - currentTime = glfwGetTime(); + currentTime = GetTime(); extraTime = currentTime - previousTime; previousTime = currentTime; frameTime += extraTime; @@ -336,11 +480,7 @@ void EndDrawing(void) // Initializes 3D mode for drawing (Camera setup) void Begin3dMode(Camera camera) { - //------------------------------------------------------ -#if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2) - rlglDraw(); // Draw Buffers -#endif - //------------------------------------------------------ + rlglDraw(); // Draw Buffers (Only OpenGL 3+ and ES2) rlMatrixMode(RL_PROJECTION); // Switch to projection matrix @@ -348,11 +488,11 @@ void Begin3dMode(Camera camera) rlLoadIdentity(); // Reset current matrix (PROJECTION) // Setup perspective projection - float aspect = (GLfloat)windowWidth/(GLfloat)windowHeight; - double top = 0.1f*tan(45.0f*PI / 360.0); + float aspect = (GLfloat)screenWidth/(GLfloat)screenHeight; + double top = 0.1f*tan(45.0f*PI / 360.0f); double right = top*aspect; - rlFrustum(-right, right, -top, top, 0.1f, 100.0f); + rlFrustum(-right, right, -top, top, 0.1f, 1000.0f); rlMatrixMode(RL_MODELVIEW); // Switch back to modelview matrix rlLoadIdentity(); // Reset current matrix (MODELVIEW) @@ -365,11 +505,7 @@ void Begin3dMode(Camera camera) // Ends 3D mode and returns to default 2D orthographic mode void End3dMode(void) { - //------------------------------------------------------ -#if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2) - rlglDraw(); // Draw Buffers -#endif - //------------------------------------------------------ + rlglDraw(); // Draw Buffers (Only OpenGL 3+ and ES2) rlMatrixMode(RL_PROJECTION); // Switch to projection matrix rlPopMatrix(); // Restore previous matrix (PROJECTION) from matrix stack @@ -440,8 +576,8 @@ int GetRandomValue(int min, int max) // Fades color by a percentadge Color Fade(Color color, float alpha) { - if (alpha < 0.0) alpha = 0.0; - else if (alpha > 1.0) alpha = 1.0; + if (alpha < 0.0f) alpha = 0.0f; + else if (alpha > 1.0f) alpha = 1.0f; return (Color){color.r, color.g, color.b, color.a*alpha}; } @@ -455,7 +591,7 @@ void ShowLogo(void) //---------------------------------------------------------------------------------- // Module Functions Definition - Input (Keyboard, Mouse, Gamepad) Functions //---------------------------------------------------------------------------------- - +#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) // Detect if a key has been pressed once bool IsKeyPressed(int key) { @@ -476,7 +612,7 @@ bool IsKeyPressed(int key) // Detect if a key is being pressed (key held down) bool IsKeyDown(int key) { - if (glfwGetKey(window, key) == GLFW_PRESS) return true; + if (GetKeyStatus(key) == 1) return true; else return false; } @@ -500,7 +636,7 @@ bool IsKeyReleased(int key) // Detect if a key is NOT being pressed (key not held down) bool IsKeyUp(int key) { - if (glfwGetKey(window, key) == GLFW_RELEASE) return true; + if (GetKeyStatus(key) == 0) return true; else return false; } @@ -524,7 +660,7 @@ bool IsMouseButtonPressed(int button) // Detect if a mouse button is being pressed bool IsMouseButtonDown(int button) { - if (glfwGetMouseButton(window, button) == GLFW_PRESS) return true; + if (GetMouseButtonStatus(button) == 1) return true; else return false; } @@ -548,43 +684,26 @@ bool IsMouseButtonReleased(int button) // Detect if a mouse button is NOT being pressed bool IsMouseButtonUp(int button) { - if (glfwGetMouseButton(window, button) == GLFW_RELEASE) return true; + if (GetMouseButtonStatus(button) == 0) return true; else return false; } // Returns mouse position X int GetMouseX(void) { - double mouseX; - double mouseY; - - glfwGetCursorPos(window, &mouseX, &mouseY); - - return (int)mouseX; + return (int)mousePosition.x; } // Returns mouse position Y int GetMouseY(void) { - double mouseX; - double mouseY; - - glfwGetCursorPos(window, &mouseX, &mouseY); - - return (int)mouseY; + return (int)mousePosition.y; } // Returns mouse position XY Vector2 GetMousePosition(void) { - double mouseX; - double mouseY; - - glfwGetCursorPos(window, &mouseX, &mouseY); - - Vector2 position = { (float)mouseX, (float)mouseY }; - - return position; + return mousePosition; } // Returns mouse wheel movement Y @@ -596,7 +715,10 @@ int GetMouseWheelMove(void) return previousMouseWheelY; } +#endif +// TODO: Enable gamepad usage on Rapsberr Pi +#if defined(PLATFORM_DESKTOP) // Detect if a gamepad is available bool IsGamepadAvailable(int gamepad) { @@ -628,7 +750,7 @@ Vector2 GetGamepadMovement(int gamepad) return vec; } -// Detect if a gamepad button is being pressed +// Detect if a gamepad button has been pressed once bool IsGamepadButtonPressed(int gamepad, int button) { bool pressed = false; @@ -645,9 +767,10 @@ bool IsGamepadButtonPressed(int gamepad, int button) return pressed; } +// Detect if a gamepad button is being pressed bool IsGamepadButtonDown(int gamepad, int button) { - const unsigned char* buttons; + const unsigned char *buttons; int buttonsCount; buttons = glfwGetJoystickButtons(gamepad, &buttonsCount); @@ -659,7 +782,7 @@ bool IsGamepadButtonDown(int gamepad, int button) else return false; } -// Detect if a gamepad button is NOT being pressed +// Detect if a gamepad button has NOT been pressed once bool IsGamepadButtonReleased(int gamepad, int button) { bool released = false; @@ -676,9 +799,10 @@ bool IsGamepadButtonReleased(int gamepad, int button) return released; } +// Detect if a mouse button is NOT being pressed bool IsGamepadButtonUp(int gamepad, int button) { - const unsigned char* buttons; + const unsigned char *buttons; int buttonsCount; buttons = glfwGetJoystickButtons(gamepad, &buttonsCount); @@ -689,11 +813,276 @@ bool IsGamepadButtonUp(int gamepad, int button) } else return false; } +#endif + +#if defined(PLATFORM_ANDROID) +// Returns touch position X +int GetTouchX(void) +{ + return (int)touchX; +} + +// Returns touch position Y +int GetTouchY(void) +{ + return (int)touchY; +} + +// Returns touch position XY +Vector2 GetTouchPosition(void) +{ + Vector2 position = { touchX, touchY }; + + return position; +} +#endif //---------------------------------------------------------------------------------- // Module specific Functions Definition //---------------------------------------------------------------------------------- +// Initialize display device and framebuffer +// NOTE: width and height represent the screen (framebuffer) desired size, not actual display size +// If width or height are 0, default display size will be used for framebuffer size +static void InitDisplay(int width, int height) +{ + screenWidth = width; // User desired width + screenHeight = height; // User desired height + + // NOTE: Framebuffer (render area - renderWidth, renderHeight) could include black bars... + // ...in top-down or left-right to match display aspect ratio (no weird scalings) + + // Downscale matrix is required in case desired screen area is bigger than display area + downscaleView = MatrixIdentity(); + +#if defined(PLATFORM_DESKTOP) + glfwSetErrorCallback(ErrorCallback); + + if (!glfwInit()) TraceLog(ERROR, "Failed to initialize GLFW"); + + // Find monitor resolution + const GLFWvidmode *mode = glfwGetVideoMode(glfwGetPrimaryMonitor()); + + displayWidth = mode->width; + displayHeight = mode->height; + + // Screen size security check + if (screenWidth <= 0) screenWidth = displayWidth; + if (screenHeight <= 0) screenHeight = displayHeight; + + glfwDefaultWindowHints(); // Set default windows hints + + glfwWindowHint(GLFW_RESIZABLE, GL_FALSE); // Avoid window being resizable + //glfwWindowHint(GLFW_DECORATED, GL_TRUE); // Border and buttons on Window + //glfwWindowHint(GLFW_RED_BITS, 8); // Bit depths of color components for default framebuffer + //glfwWindowHint(GLFW_REFRESH_RATE, 0); // Refresh rate for fullscreen window + //glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API); // Default OpenGL API to use. Alternative: GLFW_OPENGL_ES_API + //glfwWindowHint(GLFW_AUX_BUFFERS, 0); // Number of auxiliar buffers + + // NOTE: When asking for an OpenGL context version, most drivers provide highest supported version + // with forward compatibility to older OpenGL versions. + // For example, if using OpenGL 1.1, driver can provide a 3.3 context fordward compatible. + + if (rlGetVersion() == OPENGL_33) + { + //glfwWindowHint(GLFW_SAMPLES, 4); // Enables multisampling x4 (MSAA), default is 0 + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // Choose OpenGL major version (just hint) + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // Choose OpenGL minor version (just hint) + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // Profiles Hint: Only 3.2 and above! + // Other values: GLFW_OPENGL_ANY_PROFILE, GLFW_OPENGL_COMPAT_PROFILE + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_FALSE); // Fordward Compatibility Hint: Only 3.0 and above! + } + + if (fullscreen) + { + // At this point we need to manage render size vs screen size + // NOTE: This function use and modify global module variables: screenWidth/screenHeight and renderWidth/renderHeight and downscaleView + SetupFramebufferSize(displayWidth, displayHeight); + + window = glfwCreateWindow(screenWidth, screenHeight, windowTitle, glfwGetPrimaryMonitor(), NULL); + } + else + { + // No-fullscreen window creation + window = glfwCreateWindow(screenWidth, screenHeight, windowTitle, NULL, NULL); + + renderWidth = screenWidth; + renderHeight = screenHeight; + } + + if (!window) + { + glfwTerminate(); + TraceLog(ERROR, "GLFW Failed to initialize Window"); + } + else + { + TraceLog(INFO, "Display device initialized successfully"); + TraceLog(INFO, "Display size: %i x %i", displayWidth, displayHeight); + TraceLog(INFO, "Render size: %i x %i", renderWidth, renderHeight); + TraceLog(INFO, "Screen size: %i x %i", screenWidth, screenHeight); + TraceLog(INFO, "Viewport offsets: %i, %i", renderOffsetX, renderOffsetY); + } + + glfwSetWindowSizeCallback(window, WindowSizeCallback); + glfwSetCursorEnterCallback(window, CursorEnterCallback); + glfwSetKeyCallback(window, KeyCallback); + glfwSetScrollCallback(window, ScrollCallback); + + glfwMakeContextCurrent(window); + + //glfwSwapInterval(0); // Disables GPU v-sync (if set), so frames are not limited to screen refresh rate (60Hz -> 60 FPS) + // If not set, swap interval uses GPU v-sync configuration + // Framerate can be setup using SetTargetFPS() + + //glfwGetFramebufferSize(window, &renderWidth, &renderHeight); // Get framebuffer size of current window + +#elif defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) + fullscreen = true; + + // Screen size security check + if (screenWidth <= 0) screenWidth = displayWidth; + if (screenHeight <= 0) screenHeight = displayHeight; + +#if defined(PLATFORM_RPI) + bcm_host_init(); + + DISPMANX_ELEMENT_HANDLE_T dispmanElement; + DISPMANX_DISPLAY_HANDLE_T dispmanDisplay; + DISPMANX_UPDATE_HANDLE_T dispmanUpdate; + VC_RECT_T dstRect; + VC_RECT_T srcRect; +#endif + + const EGLint framebufferAttribs[] = + { + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, // Type of context support -> Required on RPI? + //EGL_SURFACE_TYPE, EGL_WINDOW_BIT, // Don't use it on Android! + EGL_BLUE_SIZE, 8, // Alternative: 5 + EGL_GREEN_SIZE, 8, // Alternative: 6 + EGL_RED_SIZE, 8, // Alternative: 5 + //EGL_ALPHA_SIZE, 8, + EGL_DEPTH_SIZE, 8, // NOTE: Required to use Depth testing! + //EGL_SAMPLES, 4, // 4x Antialiasing (Free on MALI GPUs) + EGL_NONE + }; + + EGLint contextAttribs[] = + { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + + EGLint numConfigs; + EGLConfig config; + + // Get an EGL display connection + display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + + // Initialize the EGL display connection + eglInitialize(display, NULL, NULL); + + // Get an appropriate EGL framebuffer configuration + eglChooseConfig(display, framebufferAttribs, &config, 1, &numConfigs); + + // Set rendering API + eglBindAPI(EGL_OPENGL_ES_API); + + // Create an EGL rendering context + context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttribs); + + // Create an EGL window surface + //--------------------------------------------------------------------------------- +#if defined(PLATFORM_ANDROID) + EGLint displayFormat; + + displayWidth = ANativeWindow_getWidth(app->window); + displayHeight = ANativeWindow_getHeight(app->window); + + // EGL_NATIVE_VISUAL_ID is an attribute of the EGLConfig that is guaranteed to be accepted by ANativeWindow_setBuffersGeometry() + // As soon as we picked a EGLConfig, we can safely reconfigure the ANativeWindow buffers to match, using EGL_NATIVE_VISUAL_ID + eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &displayFormat); + + // At this point we need to manage render size vs screen size + // NOTE: This function use and modify global module variables: screenWidth/screenHeight and renderWidth/renderHeight and downscaleView + SetupFramebufferSize(displayWidth, displayHeight); + + ANativeWindow_setBuffersGeometry(app->window, renderWidth, renderHeight, displayFormat); + //ANativeWindow_setBuffersGeometry(app->window, 0, 0, displayFormat); // Force use of native display size + + surface = eglCreateWindowSurface(display, config, app->window, NULL); + +#elif defined(PLATFORM_RPI) + graphics_get_display_size(0, &displayWidth, &displayHeight); + + // At this point we need to manage render size vs screen size + // NOTE: This function use and modify global module variables: screenWidth/screenHeight and renderWidth/renderHeight and downscaleView + SetupFramebufferSize(displayWidth, displayHeight); + + dstRect.x = 0; + dstRect.y = 0; + dstRect.width = displayWidth; + dstRect.height = displayHeight; + + srcRect.x = 0; + srcRect.y = 0; + srcRect.width = renderWidth << 16; + srcRect.height = renderHeight << 16; + + // NOTE: RPI dispmanx windowing system takes care of srcRec scaling to dstRec by hardware (no cost) + // Take care that renderWidth/renderHeight fit on displayWidth/displayHeight aspect ratio + + dispmanDisplay = vc_dispmanx_display_open(0); + dispmanUpdate = vc_dispmanx_update_start(0); + + dispmanElement = vc_dispmanx_element_add(dispmanUpdate, dispmanDisplay, 0/*layer*/, &dstRect, 0/*src*/, + &srcRect, DISPMANX_PROTECTION_NONE, 0/*alpha*/, 0/*clamp*/, 0/*transform*/); + + nativeWindow.element = dispmanElement; + nativeWindow.width = renderWidth; + nativeWindow.height = renderHeight; + vc_dispmanx_update_submit_sync(dispmanUpdate); + + surface = eglCreateWindowSurface(display, config, &nativeWindow, NULL); + //--------------------------------------------------------------------------------- +#endif + // There must be at least one frame displayed before the buffers are swapped + //eglSwapInterval(display, 1); + + if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE) + { + TraceLog(ERROR, "Unable to attach EGL rendering context to EGL surface"); + } + else + { + // Grab the width and height of the surface + //eglQuerySurface(display, surface, EGL_WIDTH, &renderWidth); + //eglQuerySurface(display, surface, EGL_HEIGHT, &renderHeight); + + TraceLog(INFO, "Display device initialized successfully"); + TraceLog(INFO, "Display size: %i x %i", displayWidth, displayHeight); + TraceLog(INFO, "Render size: %i x %i", renderWidth, renderHeight); + TraceLog(INFO, "Screen size: %i x %i", screenWidth, screenHeight); + TraceLog(INFO, "Viewport offsets: %i, %i", renderOffsetX, renderOffsetY); + } +#endif +} + +// Initialize OpenGL graphics +void InitGraphics(void) +{ + rlglInit(); // Init rlgl + + rlglInitGraphics(renderOffsetX, renderOffsetY, renderWidth, renderHeight); // Init graphics (OpenGL stuff) + + ClearBackground(RAYWHITE); // Default background color for raylib games :P + +#if defined(PLATFORM_ANDROID) + windowReady = true; // IMPORTANT! +#endif +} + +#if defined(PLATFORM_DESKTOP) // GLFW3 Error Callback, runs on GLFW3 error static void ErrorCallback(int error, const char *description) { @@ -701,13 +1090,13 @@ static void ErrorCallback(int error, const char *description) } // GLFW3 Srolling Callback, runs on mouse wheel -static void ScrollCallback(GLFWwindow* window, double xoffset, double yoffset) +static void ScrollCallback(GLFWwindow *window, double xoffset, double yoffset) { currentMouseWheelY = (int)yoffset; } // GLFW3 Keyboard Callback, runs on key pressed -static void KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) +static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods) { if (key == exitKey && action == GLFW_PRESS) { @@ -715,54 +1104,179 @@ static void KeyCallback(GLFWwindow* window, int key, int scancode, int action, i // NOTE: Before closing window, while loop must be left! } - else if (key == GLFW_KEY_F11 && action == GLFW_PRESS) - { - ToggleFullscreen(); - } else if (key == GLFW_KEY_F12 && action == GLFW_PRESS) { TakeScreenshot(); } } -static void CursorEnterCallback(GLFWwindow* window, int enter) +// GLFW3 CursorEnter Callback, when cursor enters the window +static void CursorEnterCallback(GLFWwindow *window, int enter) { - if (enter == GL_TRUE) cursorOnScreen = true; + if (enter == true) cursorOnScreen = true; else cursorOnScreen = false; } // GLFW3 WindowSize Callback, runs when window is resized -static void WindowSizeCallback(GLFWwindow* window, int width, int height) +static void WindowSizeCallback(GLFWwindow *window, int width, int height) { - int fbWidth, fbHeight; - glfwGetFramebufferSize(window, &fbWidth, &fbHeight); // Get framebuffer size of current window - // If window is resized, graphics device is re-initialized (but only ortho mode) - rlglInitGraphicsDevice(fbWidth, fbHeight); + rlglInitGraphics(0, 0, width, height); // Window size must be updated to be used on 3D mode to get new aspect ratio (Begin3dMode()) - windowWidth = fbWidth; - windowHeight = fbHeight; + screenWidth = width; + screenHeight = height; + + // TODO: Update render size? // Background must be also re-cleared - rlClearColor(background.r, background.g, background.b, background.a); + ClearBackground(RAYWHITE); +} +#endif + +#if defined(PLATFORM_ANDROID) +// Android: Process activity input events +static int32_t InputCallback(struct android_app *app, AInputEvent *event) +{ + int type = AInputEvent_getType(event); + //int32_t key = 0; + + if (type == AINPUT_EVENT_TYPE_MOTION) + { + if ((screenWidth > displayWidth) || (screenHeight > displayHeight)) + { + // TODO: Seems to work ok but... review! + touchX = AMotionEvent_getX(event, 0) * ((float)screenWidth / (float)(displayWidth - renderOffsetX)) - renderOffsetX/2; + touchY = AMotionEvent_getY(event, 0) * ((float)screenHeight / (float)(displayHeight - renderOffsetY)) - renderOffsetY/2; + } + else + { + touchX = AMotionEvent_getX(event, 0) * ((float)renderWidth / (float)displayWidth) - renderOffsetX/2; + touchY = AMotionEvent_getY(event, 0) * ((float)renderHeight / (float)displayHeight) - renderOffsetY/2; + } + + //float AMotionEvent_getX(event, size_t pointer_index); + //int32_t AMotionEvent_getButtonState(event); // Pressed buttons + //int32_t AMotionEvent_getPointerId(event, size_t pointer_index); + //size_t pointerCount = AMotionEvent_getPointerCount(event); + //float AMotionEvent_getPressure(const AInputEvent *motion_event, size_t pointer_index); // 0 to 1 + //float AMotionEvent_getSize(const AInputEvent *motion_event, size_t pointer_index); // Pressed area + + return 1; + } + else if (type == AINPUT_EVENT_TYPE_KEY) + { + //key = AKeyEvent_getKeyCode(event); + //int32_t AKeyEvent_getMetaState(event); + } + + return 0; } -// Takes a bitmap (BMP) screenshot and saves it in the same folder as executable +// Android: Process activity lifecycle commands +static void CommandCallback(struct android_app *app, int32_t cmd) +{ + switch (cmd) + { + case APP_CMD_START: + { + //rendering = true; + TraceLog(INFO, "APP_CMD_START"); + } break; + case APP_CMD_RESUME: + { + TraceLog(INFO, "APP_CMD_RESUME"); + } break; + case APP_CMD_INIT_WINDOW: + { + TraceLog(INFO, "APP_CMD_INIT_WINDOW"); + + if (app->window != NULL) + { + // Init device display (monitor, LCD, ...) + InitDisplay(screenWidth, screenHeight); + + // Init OpenGL graphics + InitGraphics(); + + // Load default font for convenience + // NOTE: External function (defined in module: text) + LoadDefaultFont(); + + // Init hi-res timer + InitTimer(); + + // raylib logo appearing animation (if enabled) + if (showLogo) + { + SetTargetFPS(60); + LogoAnimation(); + } + } + } break; + case APP_CMD_GAINED_FOCUS: + { + TraceLog(INFO, "APP_CMD_GAINED_FOCUS"); + ResumeMusicStream(); + } break; + case APP_CMD_PAUSE: + { + TraceLog(INFO, "APP_CMD_PAUSE"); + } break; + case APP_CMD_LOST_FOCUS: + { + //DrawFrame(); + TraceLog(INFO, "APP_CMD_LOST_FOCUS"); + PauseMusicStream(); + } break; + case APP_CMD_TERM_WINDOW: + { + // TODO: Do display destruction here? -> Yes but only display, don't free buffers! + + TraceLog(INFO, "APP_CMD_TERM_WINDOW"); + } break; + case APP_CMD_SAVE_STATE: + { + TraceLog(INFO, "APP_CMD_SAVE_STATE"); + } break; + case APP_CMD_STOP: + { + TraceLog(INFO, "APP_CMD_STOP"); + } break; + case APP_CMD_DESTROY: + { + // TODO: Finish activity? + //ANativeActivity_finish(app->activity); + + TraceLog(INFO, "APP_CMD_DESTROY"); + } break; + case APP_CMD_CONFIG_CHANGED: + { + //AConfiguration_fromAssetManager(app->config, app->activity->assetManager); + //print_cur_config(app); + + // Check screen orientation here! + + TraceLog(INFO, "APP_CMD_CONFIG_CHANGED"); + } break; + default: break; + } +} +#endif + +#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) +// Takes a screenshot and saves it in the same folder as executable static void TakeScreenshot(void) { static int shotNum = 0; // Screenshot number, increments every screenshot take during program execution - char buffer[20]; // Buffer to store file name - int fbWidth, fbHeight; // Frame buffer width and height - glfwGetFramebufferSize(window, &fbWidth, &fbHeight); // Get framebuffer size of current window - - unsigned char *imgData = rlglReadScreenPixels(fbWidth, fbHeight); + unsigned char *imgData = rlglReadScreenPixels(renderWidth, renderHeight); sprintf(buffer, "screenshot%03i.png", shotNum); - WritePNG(buffer, imgData, fbWidth, fbHeight); + // Save image as PNG + WritePNG(buffer, imgData, renderWidth, renderHeight); free(imgData); @@ -770,11 +1284,447 @@ static void TakeScreenshot(void) TraceLog(INFO, "[%s] Screenshot taken!", buffer); } +#endif + +// Initialize hi-resolution timer +static void InitTimer(void) +{ + srand(time(NULL)); // Initialize random seed + +#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) + struct timespec now; + + if (clock_gettime(CLOCK_MONOTONIC, &now) == 0) // Success + { + baseTime = (uint64_t)now.tv_sec*1000000000LLU + (uint64_t)now.tv_nsec; + } + else TraceLog(WARNING, "No hi-resolution timer available"); +#endif + + previousTime = GetTime(); // Get time as double +} + +// Get current time measure since InitTimer() +static double GetTime(void) +{ +#if defined(PLATFORM_DESKTOP) + return glfwGetTime(); +#elif defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + uint64_t time = ts.tv_sec*1000000000LLU + (uint64_t)ts.tv_nsec; + + return (double)(time - baseTime) * 1e-9; +#endif +} + +// Get one key state +static bool GetKeyStatus(int key) +{ +#if defined(PLATFORM_DESKTOP) + return glfwGetKey(window, key); +#elif defined(PLATFORM_ANDROID) + // TODO: Check virtual keyboard (?) + return false; +#elif defined(PLATFORM_RPI) + // NOTE: Keys states are filled in PollInputEvents() + if (key < 0 || key > 511) return false; + else return currentKeyState[key]; +#endif +} + +// Get one mouse button state +static bool GetMouseButtonStatus(int button) +{ +#if defined(PLATFORM_DESKTOP) + return glfwGetMouseButton(window, button); +#elif defined(PLATFORM_ANDROID) + // TODO: Check virtual keyboard (?) + return false; +#elif defined(PLATFORM_RPI) + // NOTE: mouse buttons array is filled on PollInputEvents() + return currentMouseState[button]; +#endif +} + +// Poll (store) all input events +static void PollInputEvents(void) +{ +#if defined(PLATFORM_DESKTOP) + // Mouse input polling + double mouseX; + double mouseY; + + glfwGetCursorPos(window, &mouseX, &mouseY); + + mousePosition.x = (float)mouseX; + mousePosition.y = (float)mouseY; + + // Keyboard polling + // Automatically managed by GLFW3 through callback + + glfwPollEvents(); // Register keyboard/mouse events +#elif defined(PLATFORM_ANDROID) + + // TODO: Check virtual keyboard (?) + + // Poll Events (registered events) + while ((ident = ALooper_pollAll(0, NULL, &events,(void**)&source)) >= 0) + { + // Process this event + if (source != NULL) source->process(app, source); + + // Check if we are exiting + if (app->destroyRequested != 0) + { + TraceLog(INFO, "Closing Window..."); + //CloseWindow(); + windowShouldClose = true; + //ANativeActivity_finish(app->activity); + } + } +#elif defined(PLATFORM_RPI) + + // NOTE: Mouse input events polling is done asynchonously in another pthread - MouseThread() + + // NOTE: Keyboard reading could be done using input_event(s) reading or just read from stdin, + // we use method 2 (stdin) but maybe in a future we should change to method 1... + + // Keyboard input polling (fill keys[256] array with status) + int numKeysBuffer = 0; // Keys available on buffer + char keysBuffer[32]; // Max keys to be read at a time + + // Reset pressed keys array + for (int i = 0; i < 512; i++) currentKeyState[i] = 0; + + // Read availables keycodes from stdin + numKeysBuffer = read(STDIN_FILENO, keysBuffer, 32); // POSIX system call + + // Fill array with pressed keys + for (int i = 0; i < numKeysBuffer; i++) + { + //TraceLog(INFO, "Bytes on keysBuffer: %i", numKeysBuffer); + + int key = keysBuffer[i]; + + if (keyboardMode == 2) + { + // NOTE: If (key == 0x1b), depending on next key, it could be a special keymap code! + // Up -> 1b 5b 41 / Left -> 1b 5b 44 / Right -> 1b 5b 43 / Down -> 1b 5b 42 + if (key == 0x1b) + { + if (keysBuffer[i+1] == 0x5b) // Special function key + { + switch (keysBuffer[i+2]) + { + case 0x41: currentKeyState[265] = 1; break; + case 0x42: currentKeyState[264] = 1; break; + case 0x43: currentKeyState[262] = 1; break; + case 0x44: currentKeyState[263] = 1; break; + default: break; + } + + i += 2; // Jump to next key + + // NOTE: Other special function keys (F1, F2...) are not contempled for this keyboardMode... + // ...or they are just not directly keymapped (CTRL, ALT, SHIFT) + } + } + else if (key == 0x0a) currentKeyState[257] = 1; // raylib KEY_ENTER (don't mix with <linux/input.h> KEY_*) + else if (key == 0x7f) currentKeyState[259] = 1; + else + { + TraceLog(INFO, "Pressed key (ASCII): 0x%02x", key); + + currentKeyState[key] = 1; + } + + // Detect ESC to stop program + if ((key == 0x1b) && (numKeysBuffer == 1)) windowShouldClose = true; + } + else if (keyboardMode == 1) + { + TraceLog(INFO, "Pressed key (keycode): 0x%02x", key); + + int asciiKey = -1; + + // Convert keycode to some recognized key (ASCII or GLFW3 equivalent) + if (key < 128) asciiKey = (int)UnixKeycodeToASCII[key]; + + // Record equivalent key state + if ((asciiKey >= 0) && (asciiKey < 512)) currentKeyState[asciiKey] = 1; + + // In case of letter, we also activate lower case version + if ((asciiKey >= 65) && (asciiKey <=90)) currentKeyState[asciiKey + 32] = 1; + + // Detect KEY_ESC to stop program + if (key == 0x01) windowShouldClose = true; + } + + + // Same fucnionality as GLFW3 KeyCallback() + /* + if (asciiKey == exitKey) windowShouldClose = true; + else if (key == GLFW_KEY_F12 && action == GLFW_PRESS) + { + TakeScreenshot(); + } + */ + } + + // TODO: Gamepad support (use events, easy!) +/* + struct js_event gamepadEvent; + + read(gamepadStream, &gamepadEvent, sizeof(struct js_event)); + + if (gamepadEvent.type == JS_EVENT_BUTTON) + { + switch (gamepadEvent.number) + { + case 0: // 1st Axis X + case 1: // 1st Axis Y + case 2: // 2st Axis X + case 3: // 2st Axis Y + case 4: + { + if (gamepadEvent.value == 1) // Button pressed, 0 release + + } break; + // Buttons is similar, variable for every joystick + } + } + else if (gamepadEvent.type == JS_EVENT_AXIS) + { + switch (gamepadEvent.number) + { + case 0: // 1st Axis X + case 1: // 1st Axis Y + case 2: // 2st Axis X + case 3: // 2st Axis Y + // Buttons is similar, variable for every joystick + } + } +*/ +#endif +} -static void LogoAnimation() +#if defined(PLATFORM_RPI) +// Mouse initialization (including mouse thread) +static void InitMouse(void) { - int logoPositionX = windowWidth/2 - 128; - int logoPositionY = windowHeight/2 - 128; + // NOTE: We can use /dev/input/mice to read from all available mice + if ((mouseStream = open(DEFAULT_MOUSE_DEV, O_RDONLY|O_NONBLOCK)) < 0) TraceLog(WARNING, "Could not open mouse device, no mouse available"); + else + { + mouseReady = true; + + int err = pthread_create(&mouseThreadId, NULL, &MouseThread, NULL); + + if (err != 0) TraceLog(WARNING, "Error creating mouse input event thread"); + else TraceLog(INFO, "Mouse device initialized successfully"); + } +} + +// Mouse reading thread +// NOTE: We need a separate thread to avoid loosing mouse events, +// if too much time passes between reads, queue gets full and new events override older wants... +static void *MouseThread(void *arg) +{ + struct input_event mouseEvent; + + while(1) + { + // NOTE: read() will return -1 if the events queue is empty + read(mouseStream, &mouseEvent, sizeof(struct input_event)); + + // Check event types + if (mouseEvent.type == EV_REL) // Relative motion event + { + if (mouseEvent.code == REL_X) + { + mousePosition.x += (float)mouseEvent.value; + + // Screen limits X check + if (mousePosition.x < 0) mousePosition.x = 0; + if (mousePosition.x > screenWidth) mousePosition.x = screenWidth; + } + + if (mouseEvent.code == REL_Y) + { + mousePosition.y += (float)mouseEvent.value; + + // Screen limits Y check + if (mousePosition.y < 0) mousePosition.y = 0; + if (mousePosition.y > screenHeight) mousePosition.y = screenHeight; + } + + if (mouseEvent.code == REL_WHEEL) + { + // mouseEvent.value give 1 or -1 (direction) + } + } + else if (mouseEvent.type == EV_KEY) // Mouse button event + { + if (mouseEvent.code == BTN_LEFT) currentMouseState[0] = mouseEvent.value; + if (mouseEvent.code == BTN_RIGHT) currentMouseState[1] = mouseEvent.value; + if (mouseEvent.code == BTN_MIDDLE) currentMouseState[2] = mouseEvent.value; + } + } + + return NULL; +} + +// Initialize Keyboard system (using standard input) +static void InitKeyboard(void) +{ + // NOTE: We read directly from Standard Input (stdin) - STDIN_FILENO file descriptor + + // Make stdin non-blocking (not enough, need to configure to non-canonical mode) + int flags = fcntl(STDIN_FILENO, F_GETFL, 0); // F_GETFL: Get the file access mode and the file status flags + fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK); // F_SETFL: Set the file status flags to the value specified + + // Save terminal keyboard settings and reconfigure terminal with new settings + struct termios keyboardNewSettings; + tcgetattr(STDIN_FILENO, &defaultKeyboardSettings); // Get current keyboard settings + keyboardNewSettings = defaultKeyboardSettings; + + // New terminal settings for keyboard: turn off buffering (non-canonical mode), echo and key processing + // NOTE: ISIG controls if ^C and ^Z generate break signals or not + keyboardNewSettings.c_lflag &= ~(ICANON | ECHO | ISIG); + //keyboardNewSettings.c_iflag &= ~(ISTRIP | INLCR | ICRNL | IGNCR | IXON | IXOFF); + keyboardNewSettings.c_cc[VMIN] = 1; + keyboardNewSettings.c_cc[VTIME] = 0; + + // Set new keyboard settings (change occurs immediately) + tcsetattr(STDIN_FILENO, TCSANOW, &keyboardNewSettings); + + // NOTE: Reading directly from stdin will give chars already key-mapped by kernel to ASCII or UNICODE, we change that! + + // Save old keyboard mode to restore it at the end + if (ioctl(STDIN_FILENO, KDGKBMODE, &defaultKeyboardMode) < 0) + { + // NOTE: It could mean we are using a remote keyboard through ssh! + TraceLog(WARNING, "Could not change keyboard mode (SSH keyboard?)"); + + keyboardMode = 2; // ASCII + } + else + { + // We reconfigure keyboard mode to get scancodes (K_RAW) or keycodes (K_MEDIUMRAW) + ioctl(STDIN_FILENO, KDSKBMODE, K_MEDIUMRAW); // ASCII chars (K_XLATE), UNICODE chars (K_UNICODE) + + keyboardMode = 1; // keycodes + } + + // Register keyboard restore when program finishes + atexit(RestoreKeyboard); +} + +// Restore default keyboard input +static void RestoreKeyboard(void) +{ + tcsetattr(STDIN_FILENO, TCSANOW, &defaultKeyboardSettings); + ioctl(STDIN_FILENO, KDSKBMODE, defaultKeyboardMode); +} + +// Init gamepad system +static void InitGamepad(void) +{ + // TODO: Gamepad support + if ((gamepadStream = open(DEFAULT_GAMEPAD_DEV, O_RDONLY|O_NONBLOCK)) < 0) TraceLog(WARNING, "Could not open gamepad device, no gamepad available"); + else TraceLog(INFO, "Gamepad device initialized successfully"); +} +#endif + +// Copy back buffer to front buffers +static void SwapBuffers(void) +{ +#if defined(PLATFORM_DESKTOP) + glfwSwapBuffers(window); +#elif defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) + eglSwapBuffers(display, surface); +#endif +} + +// Compute framebuffer size relative to screen size and display size +// NOTE: Global variables renderWidth/renderHeight can be modified +static void SetupFramebufferSize(int displayWidth, int displayHeight) +{ + // Calculate renderWidth and renderHeight, we have the display size (input params) and the desired screen size (global var) + if ((screenWidth > displayWidth) || (screenHeight > displayHeight)) + { + TraceLog(WARNING, "DOWNSCALING: Required screen size (%i x %i) is bigger than display size (%i x %i)", screenWidth, screenHeight, displayWidth, displayHeight); + + // Downscaling to fit display with border-bars + float widthRatio = (float)displayWidth/(float)screenWidth; + float heightRatio = (float)displayHeight/(float)screenHeight; + + if (widthRatio <= heightRatio) + { + renderWidth = displayWidth; + renderHeight = (int)((float)screenHeight*widthRatio); + renderOffsetX = 0; + renderOffsetY = (displayHeight - renderHeight); + } + else + { + renderWidth = (int)((float)screenWidth*heightRatio); + renderHeight = displayHeight; + renderOffsetX = (displayWidth - renderWidth); + renderOffsetY = 0; + } + + // NOTE: downscale matrix required! + float scaleRatio = (float)renderWidth/(float)screenWidth; + + downscaleView = MatrixScale(scaleRatio, scaleRatio, scaleRatio); + + // NOTE: We render to full display resolution! + // We just need to calculate above parameters for downscale matrix and offsets + renderWidth = displayWidth; + renderHeight = displayHeight; + + TraceLog(WARNING, "Downscale matrix generated, content will be rendered at: %i x %i", renderWidth, renderHeight); + } + else if ((screenWidth < displayWidth) || (screenHeight < displayHeight)) + { + // Required screen size is smaller than display size + TraceLog(INFO, "UPSCALING: Required screen size: %i x %i -> Display size: %i x %i", screenWidth, screenHeight, displayWidth, displayHeight); + + // Upscaling to fit display with border-bars + float displayRatio = (float)displayWidth/(float)displayHeight; + float screenRatio = (float)screenWidth/(float)screenHeight; + + if (displayRatio <= screenRatio) + { + renderWidth = screenWidth; + renderHeight = (int)((float)screenWidth/displayRatio); + renderOffsetX = 0; + renderOffsetY = (renderHeight - screenHeight); + } + else + { + renderWidth = (int)((float)screenHeight*displayRatio); + renderHeight = screenHeight; + renderOffsetX = (renderWidth - screenWidth); + renderOffsetY = 0; + } + } + else // screen == display + { + renderWidth = screenWidth; + renderHeight = screenHeight; + renderOffsetX = 0; + renderOffsetY = 0; + } +} + +// Plays raylib logo appearing animation +static void LogoAnimation(void) +{ + int logoPositionX = screenWidth/2 - 128; + int logoPositionY = screenHeight/2 - 128; int framesCounter = 0; int lettersCount = 0; @@ -787,7 +1737,7 @@ static void LogoAnimation() char raylib[8] = " "; // raylib text array, max 8 letters int state = 0; // Tracking animation states (State Machine) - float alpha = 1.0; // Useful for fading + float alpha = 1.0f; // Useful for fading while (!WindowShouldClose() && (state != 4)) // Detect window close button or ESC key { @@ -840,11 +1790,11 @@ static void LogoAnimation() if (lettersCount >= 10) // When all letters have appeared, just fade out everything { - alpha -= 0.02; + alpha -= 0.02f; - if (alpha <= 0) + if (alpha <= 0.0f) { - alpha = 0; + alpha = 0.0f; state = 4; } } @@ -855,6 +1805,8 @@ static void LogoAnimation() //---------------------------------------------------------------------------------- BeginDrawing(); + ClearBackground(RAYWHITE); + if (state == 0) { if ((framesCounter/12)%2) DrawRectangle(logoPositionX, logoPositionY, 16, 16, BLACK); @@ -880,12 +1832,14 @@ static void LogoAnimation() DrawRectangle(logoPositionX + 240, logoPositionY + 16, 16, rightSideRecHeight - 32, Fade(BLACK, alpha)); DrawRectangle(logoPositionX, logoPositionY + 240, bottomSideRecWidth, 16, Fade(BLACK, alpha)); - DrawRectangle(windowWidth/2 - 112, windowHeight/2 - 112, 224, 224, Fade(RAYWHITE, alpha)); + DrawRectangle(screenWidth/2 - 112, screenHeight/2 - 112, 224, 224, Fade(RAYWHITE, alpha)); - DrawText(raylib, windowWidth/2 - 44, windowHeight/2 + 48, 50, Fade(BLACK, alpha)); + DrawText(raylib, screenWidth/2 - 44, screenHeight/2 + 48, 50, Fade(BLACK, alpha)); } EndDrawing(); //---------------------------------------------------------------------------------- } -}
\ No newline at end of file + + showLogo = false; // Prevent for repeating when reloading window (Android) +} diff --git a/src/makefile b/src/makefile new file mode 100644 index 00000000..6f0179ab --- /dev/null +++ b/src/makefile @@ -0,0 +1,130 @@ +#************************************************************************************************** +# +# raylib for Raspberry Pi and Windows desktop +# +# makefile for library compilation (raylib.a) +# +# Copyright (c) 2014 Ramon Santamaria (Ray San - raysan@raysanweb.com) +# +# This software is provided "as-is", without any express or implied warranty. In no event +# will the authors be held liable for any damages arising from the use of this software. +# +# Permission is granted to anyone to use this software for any purpose, including commercial +# applications, and to alter it and redistribute it freely, subject to the following restrictions: +# +# 1. The origin of this software must not be misrepresented; you must not claim that you +# wrote the original software. If you use this software in a product, an acknowledgment +# in the product documentation would be appreciated but is not required. +# +# 2. Altered source versions must be plainly marked as such, and must not be misrepresented +# as being the original software. +# +# 3. This notice may not be removed or altered from any source distribution. +# +#************************************************************************************************** + +# define raylib platform (by default, compile for RPI) +# Other possible platform: PLATFORM_DESKTOP +PLATFORM ?= PLATFORM_RPI + +# define raylib graphics api depending on selected platform +ifeq ($(PLATFORM),PLATFORM_RPI) + # define raylib graphics api to use (on RPI, OpenGL ES 2.0 must be used) + GRAPHICS = GRAPHICS_API_OPENGL_ES2 +else + # define raylib graphics api to use (on Windows desktop, OpenGL 1.1 by default) + GRAPHICS = GRAPHICS_API_OPENGL_11 + #GRAPHICS = GRAPHICS_API_OPENGL_33 # Uncomment to use OpenGL 3.3 +endif + +# NOTE: makefiles targets require tab indentation + +# define compiler: gcc for C program, define as g++ for C++ +CC = gcc + +# define compiler flags: +# -O2 defines optimization level +# -Wall turns on most, but not all, compiler warnings +# -std=c99 use standard C from 1999 revision +ifeq ($(PLATFORM),PLATFORM_RPI) + CFLAGS = -O2 -Wall -std=gnu99 -fgnu89-inline +else + CFLAGS = -O2 -Wall -std=c99 +endif +#CFLAGSEXTRA = -Wextra -Wmissing-prototypes -Wstrict-prototypes + +# define any directories containing required header files +ifeq ($(PLATFORM),PLATFORM_RPI) + INCLUDES = -I. -I/opt/vc/include -I/opt/vc/include/interface/vcos/pthreads +else + INCLUDES = -I. +endif + +# define all object files required +OBJS = core.o rlgl.o raymath.o shapes.o text.o textures.o models.o audio.o utils.o stb_image.o stb_vorbis.o + +# typing 'make' will invoke the first target entry in the file, +# in this case, the 'default' target entry is raylib +default: raylib + +# compile raylib library +raylib: $(OBJS) + ar rcs libraylib.a $(OBJS) + +# compile core module +core.o: core.c + $(CC) -c core.c $(CFLAGS) $(INCLUDES) -D$(PLATFORM) + +# compile rlgl module +rlgl.o: rlgl.c + $(CC) -c rlgl.c $(CFLAGS) $(INCLUDES) -D$(GRAPHICS) + +# compile raymath module +raymath.o: raymath.c + $(CC) -c raymath.c $(CFLAGS) $(INCLUDES) + +# compile shapes module +shapes.o: shapes.c + $(CC) -c shapes.c $(CFLAGS) $(INCLUDES) -D$(PLATFORM) + +# compile textures module +textures.o: textures.c + $(CC) -c textures.c $(CFLAGS) $(INCLUDES) -D$(PLATFORM) + +# compile text module +text.o: text.c + $(CC) -c text.c $(CFLAGS) $(INCLUDES) -D$(PLATFORM) + +# compile models module +models.o: models.c + $(CC) -c models.c $(CFLAGS) $(INCLUDES) -D$(PLATFORM) + +# compile audio module +audio.o: audio.c + $(CC) -c audio.c $(CFLAGS) $(INCLUDES) -D$(PLATFORM) + +# compile utils module +utils.o: utils.c + $(CC) -c utils.c $(CFLAGS) $(INCLUDES) -D$(PLATFORM) + +# compile stb_image library +stb_image.o: stb_image.c + $(CC) -c stb_image.c $(CFLAGS) $(INCLUDES) -D$(PLATFORM) + +# compile stb_vorbis library +stb_vorbis.o: stb_vorbis.c + $(CC) -c stb_vorbis.c $(CFLAGS) $(INCLUDES) -D$(PLATFORM) + +# clean everything +clean: +ifeq ($(PLATFORM),PLATFORM_RPI) + rm -f *.o libraylib.a +else + del *.o libraylib.a +endif + @echo Cleaning done + +# instead of defining every module one by one, we can define a pattern +# this pattern below will automatically compile every module defined on $(OBJS) +#%.o : %.c +# $(CC) -c $< $(CFLAGS) $(INCLUDES) -D$(PLATFORM) -D$(GRAPHICS) diff --git a/src/models.c b/src/models.c index 95dbae7c..ab6abb55 100644 --- a/src/models.c +++ b/src/models.c @@ -1,10 +1,10 @@ -/********************************************************************************************* +/********************************************************************************************** * * raylib.models * * Basic functions to draw 3d shapes and load/draw 3d models (.OBJ) * -* Copyright (c) 2013 Ramon Santamaria (Ray San - raysan@raysanweb.com) +* Copyright (c) 2014 Ramon Santamaria (Ray San - raysan@raysanweb.com) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. @@ -25,13 +25,17 @@ #include "raylib.h" -#include <stdio.h> // Standard input/output functions, used to read model files data -#include <stdlib.h> // Declares malloc() and free() for memory management -#include <string.h> // Required for strcmp() -#include <math.h> // Used for sin, cos, tan +#if defined(PLATFORM_ANDROID) + #include "utils.h" // Android fopen function map +#endif + +#include <stdio.h> // Standard input/output functions, used to read model files data +#include <stdlib.h> // Declares malloc() and free() for memory management +#include <string.h> // Required for strcmp() +#include <math.h> // Used for sin, cos, tan -#include "raymath.h" // Required for data type Matrix and Matrix functions -#include "rlgl.h" // raylib OpenGL abstraction layer to OpenGL 1.1, 3.3+ or ES2 +#include "raymath.h" // Required for data type Matrix and Matrix functions +#include "rlgl.h" // raylib OpenGL abstraction layer to OpenGL 1.1, 3.3+ or ES2 //---------------------------------------------------------------------------------- // Defines and Macros @@ -442,9 +446,11 @@ void DrawCylinderWires(Vector3 position, float radiusTop, float radiusBottom, fl } // Draw a plane -// TODO: Test this function void DrawPlane(Vector3 centerPos, Vector2 size, Vector3 rotation, Color color) { + // NOTE: QUADS usage require defining a texture + rlEnableTexture(1); // Default white texture + // NOTE: Plane is always created on XZ ground and then rotated rlPushMatrix(); rlTranslatef(centerPos.x, centerPos.y, centerPos.z); @@ -459,11 +465,13 @@ void DrawPlane(Vector3 centerPos, Vector2 size, Vector3 rotation, Color color) rlColor4ub(color.r, color.g, color.b, color.a); rlNormal3f(0.0f, 1.0f, 0.0f); rlTexCoord2f(0.0f, 0.0f); rlVertex3f(-0.5f, 0.0f, -0.5f); - rlTexCoord2f(1.0f, 0.0f); rlVertex3f(0.5f, 0.0f, -0.5f); + rlTexCoord2f(1.0f, 0.0f); rlVertex3f(-0.5f, 0.0f, 0.5f); rlTexCoord2f(1.0f, 1.0f); rlVertex3f(0.5f, 0.0f, 0.5f); - rlTexCoord2f(0.0f, 1.0f); rlVertex3f(-0.5f, 0.0f, 0.5f); + rlTexCoord2f(0.0f, 1.0f); rlVertex3f(0.5f, 0.0f, -0.5f); rlEnd(); rlPopMatrix(); + + rlDisableTexture(); } // Draw a plane with divisions @@ -646,20 +654,15 @@ Model LoadModel(const char *fileName) if (strcmp(GetExtension(fileName),"obj") == 0) vData = LoadOBJ(fileName); else TraceLog(WARNING, "[%s] Model extension not recognized, it can't be loaded", fileName); - Model model; - - model.mesh = vData; // Model mesh is vertex data - model.textureId = 0; + // NOTE: At this point we have all vertex, texcoord, normal data for the model in vData struct -#if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2) - model.vaoId = rlglLoadModel(vData); // Use loaded data to generate VAO - model.textureId = 1; // Default whiteTexture + Model model = rlglLoadModel(vData); // Upload vertex data to GPU // Now that vertex data is uploaded to GPU, we can free arrays + // NOTE: Despite vertex data is useless on OpenGL 3.3 or ES2, we will keep it... //free(vData.vertices); //free(vData.texcoords); //free(vData.normals); -#endif return model; } @@ -764,25 +767,19 @@ Model LoadHeightmap(Image heightmap, float maxHeight) } } - // NOTE: At this point we have all vertex, texcoord, normal data for the model in vData struct - // Fill color data for (int i = 0; i < (4*vData.vertexCount); i++) vData.colors[i] = 255; - Model model; - model.mesh = vData; // Model mesh is vertex data - model.textureId = 0; + // NOTE: At this point we have all vertex, texcoord, normal data for the model in vData struct -#if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2) - model.vaoId = rlglLoadModel(vData); // Use loaded data to generate VAO - model.textureId = 1; // Default whiteTexture + Model model = rlglLoadModel(vData); // Now that vertex data is uploaded to GPU, we can free arrays + // NOTE: Despite vertex data is useless on OpenGL 3.3 or ES2, we will keep it... //free(vData.vertices); //free(vData.texcoords); //free(vData.normals); -#endif return model; } @@ -1092,20 +1089,13 @@ Model LoadCubesmap(Image cubesmap) // NOTE: At this point we have all vertex, texcoord, normal data for the model in vData struct - Model model; - - model.mesh = vData; // Model mesh is vertex data - model.textureId = 0; - -#if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2) - model.vaoId = rlglLoadModel(vData); // Use loaded data to generate VAO - model.textureId = 1; // Default whiteTexture + Model model = rlglLoadModel(vData); // Now that vertex data is uploaded to GPU, we can free arrays + // NOTE: Despite vertex data is useless on OpenGL 3.3 or ES2, we will keep it... //free(vData.vertices); //free(vData.texcoords); //free(vData.normals); -#endif return model; } @@ -1117,9 +1107,11 @@ void UnloadModel(Model model) free(model.mesh.texcoords); free(model.mesh.normals); -#if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2) + rlDeleteBuffers(model.vboId[0]); + rlDeleteBuffers(model.vboId[1]); + rlDeleteBuffers(model.vboId[2]); + rlDeleteVertexArrays(model.vaoId); -#endif } void SetModelTexture(Model *model, Texture2D texture) @@ -1268,7 +1260,7 @@ static VertexData LoadOBJ(const char *fileName) int numTexCoords = 0; int numTriangles = 0; - FILE* objFile; + FILE *objFile; objFile = fopen(fileName, "rt"); @@ -1326,9 +1318,9 @@ static VertexData LoadOBJ(const char *fileName) // Once we know the number of vertices to store, we create required arrays Vector3 *midVertices = (Vector3 *)malloc(numVertex*sizeof(Vector3)); - Vector3 *midNormals; + Vector3 *midNormals = NULL; if (numNormals > 0) midNormals = (Vector3 *)malloc(numNormals*sizeof(Vector3)); - Vector2 *midTexCoords; + Vector2 *midTexCoords = NULL; if (numTexCoords > 0) midTexCoords = (Vector2 *)malloc(numTexCoords*sizeof(Vector2)); int countVertex = 0; diff --git a/src/raylib.h b/src/raylib.h index a3fccfdb..9c754952 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1,6 +1,6 @@ -/********************************************************************************************* +/********************************************************************************************** * -* raylib 1.1 (www.raylib.com) +* raylib 1.2 (www.raylib.com) * * A simple and easy-to-use library to learn videogames programming * @@ -31,7 +31,7 @@ * One custom default font is loaded automatically when InitWindow() * If using OpenGL 3.3+ or ES2, one default shader is loaded automatically (internally defined) * -* -- LICENSE (raylib v1.1, April 2014) -- +* -- LICENSE (raylib v1.2, September 2014) -- * * raylib is licensed under an unmodified zlib/libpng license, which is an OSI-certified, * BSD-like license that allows static linking with closed source software: @@ -58,6 +58,15 @@ #ifndef RAYLIB_H #define RAYLIB_H +// Choose your platform here or just define it at compile time: -DPLATFORM_DESKTOP +//#define PLATFORM_DESKTOP // Windows, Linux or OSX +//#define PLATFORM_ANDROID // Android device +//#define PLATFORM_RPI // Raspberry Pi + +#if defined(PLATFORM_ANDROID) + #include <android_native_app_glue.h> // Defines android_app struct +#endif + //---------------------------------------------------------------------------------- // Some basic Defines //---------------------------------------------------------------------------------- @@ -65,10 +74,10 @@ #define PI 3.14159265358979323846 #endif -#define DEG2RAD (PI / 180.0) -#define RAD2DEG (180.0 / PI) +#define DEG2RAD (PI / 180.0f) +#define RAD2DEG (180.0f / PI) -// Keyboard Function Keys +// Keyboard Function Keys #define KEY_SPACE 32 #define KEY_ESCAPE 256 #define KEY_ENTER 257 @@ -107,16 +116,16 @@ // Gamepad Buttons // NOTE: Adjusted for a PS3 USB Controller -#define GAMEPAD_BUTTON_A 2 -#define GAMEPAD_BUTTON_B 1 -#define GAMEPAD_BUTTON_X 3 -#define GAMEPAD_BUTTON_Y 4 -#define GAMEPAD_BUTTON_R1 7 -#define GAMEPAD_BUTTON_R2 5 -#define GAMEPAD_BUTTON_L1 6 -#define GAMEPAD_BUTTON_L2 8 -#define GAMEPAD_BUTTON_SELECT 9 -#define GAMEPAD_BUTTON_START 10 +#define GAMEPAD_BUTTON_A 2 +#define GAMEPAD_BUTTON_B 1 +#define GAMEPAD_BUTTON_X 3 +#define GAMEPAD_BUTTON_Y 4 +#define GAMEPAD_BUTTON_R1 7 +#define GAMEPAD_BUTTON_R2 5 +#define GAMEPAD_BUTTON_L1 6 +#define GAMEPAD_BUTTON_L2 8 +#define GAMEPAD_BUTTON_SELECT 9 +#define GAMEPAD_BUTTON_START 10 // TODO: Review Xbox360 USB Controller Buttons @@ -234,6 +243,7 @@ typedef struct VertexData { typedef struct Model { VertexData mesh; unsigned int vaoId; + unsigned int vboId[4]; unsigned int textureId; //Matrix transform; } Model; @@ -256,14 +266,21 @@ extern "C" { // Prevents name mangling of functions //------------------------------------------------------------------------------------ // Window and Graphics Device Functions (Module: core) //------------------------------------------------------------------------------------ -void InitWindow(int width, int height, const char *title); // Initialize Window and Graphics Context (OpenGL) -void InitWindowEx(int width, int height, const char* title, // Initialize Window and Graphics Context (OpenGL),... - bool resizable, const char *cursorImage); // ...define if windows-resizable and custom cursor +#if defined(PLATFORM_ANDROID) +void InitWindow(int width, int height, struct android_app *state); // Init Android activity +#elif defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) +void InitWindow(int width, int height, const char *title); // Initialize Window and OpenGL Graphics +#endif + void CloseWindow(void); // Close Window and Terminate Context bool WindowShouldClose(void); // Detect if KEY_ESCAPE pressed or Close icon pressed -void ToggleFullscreen(void); // Fullscreen toggle (by default F11) +void ToggleFullscreen(void); // Fullscreen toggle (only PLATFORM_DESKTOP) +#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) void SetCustomCursor(const char *cursorImage); // Set a custom cursor icon/image void SetExitKey(int key); // Set a custom key to exit program (default is ESC) +#endif +int GetScreenWidth(void); // Get current screen width +int GetScreenHeight(void); // Get current screen height void ClearBackground(Color color); // Sets Background Color void BeginDrawing(void); // Setup drawing canvas to start drawing @@ -280,13 +297,14 @@ Color GetColor(int hexValue); // Returns a Color s int GetHexValue(Color color); // Returns hexadecimal value for a Color int GetRandomValue(int min, int max); // Returns a random value between min and max (both included) -Color Fade(Color color, float alpha); // Color fade-in or fade-out, alpha goes from 0.0 to 1.0 +Color Fade(Color color, float alpha); // Color fade-in or fade-out, alpha goes from 0.0f to 1.0f void ShowLogo(void); // Activates raylib logo at startup //------------------------------------------------------------------------------------ // Input Handling Functions (Module: core) //------------------------------------------------------------------------------------ +#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) bool IsKeyPressed(int key); // Detect if a key has been pressed once bool IsKeyDown(int key); // Detect if a key is being pressed bool IsKeyReleased(int key); // Detect if a key has been released once @@ -307,6 +325,13 @@ bool IsGamepadButtonPressed(int gamepad, int button); // Detect if a gamepad b bool IsGamepadButtonDown(int gamepad, int button); // Detect if a gamepad button is being pressed bool IsGamepadButtonReleased(int gamepad, int button); // Detect if a gamepad button has been released once bool IsGamepadButtonUp(int gamepad, int button); // Detect if a gamepad button is NOT being pressed +#endif + +#if defined(PLATFORM_ANDROID) +int GetTouchX(void); // Returns touch position X +int GetTouchY(void); // Returns touch position Y +Vector2 GetTouchPosition(void); // Returns touch position XY +#endif //------------------------------------------------------------------------------------ // Basic Shapes Drawing Functions (Module: shapes) @@ -427,6 +452,7 @@ void SetSoundPitch(Sound sound, float pitch); // Set pitch for void PlayMusicStream(char *fileName); // Start music playing (open stream) void StopMusicStream(void); // Stop music playing (close stream) void PauseMusicStream(void); // Pause music playing +void ResumeMusicStream(void); // Resume playing paused music bool MusicIsPlaying(void); // Check if music is playing void SetMusicVolume(float volume); // Set volume for music (1.0 is max level) float GetMusicTimeLength(void); // Get current music time length (in seconds) diff --git a/src/raymath.c b/src/raymath.c index b3706b25..e598b381 100644 --- a/src/raymath.c +++ b/src/raymath.c @@ -1,4 +1,4 @@ -/********************************************************************************************* +/********************************************************************************************** * * raymath * @@ -85,17 +85,17 @@ Vector3 VectorPerpendicular(Vector3 v) Vector3 result; float min = fabs(v.x); - Vector3 cardinalAxis = {1.0, 0.0, 0.0}; + Vector3 cardinalAxis = {1.0f, 0.0f, 0.0f}; if (fabs(v.y) < min) { min = fabs(v.y); - cardinalAxis = (Vector3){0.0, 1.0, 0.0}; + cardinalAxis = (Vector3){0.0f, 1.0f, 0.0f}; } if(fabs(v.z) < min) { - cardinalAxis = (Vector3){0.0, 0.0, 1.0}; + cardinalAxis = (Vector3){0.0f, 0.0f, 1.0f}; } result = VectorCrossProduct(v, cardinalAxis); @@ -216,7 +216,7 @@ void VectorTransform(Vector3 *v, Matrix mat) // Return a Vector3 init to zero Vector3 VectorZero(void) { - Vector3 zero = { 0.0, 0.0, 0.0 }; + Vector3 zero = { 0.0f, 0.0f, 0.0f }; return zero; } @@ -377,7 +377,7 @@ void MatrixNormalize(Matrix *mat) } // Returns identity matrix -Matrix MatrixIdentity() +Matrix MatrixIdentity(void) { Matrix result = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 }; @@ -494,6 +494,7 @@ Matrix MatrixRotate(float angleX, float angleY, float angleZ) // Create rotation matrix from axis and angle // TODO: Test this function +// NOTE: NO prototype defined! Matrix MatrixFromAxisAngle(Vector3 axis, float angle) { Matrix result; @@ -549,6 +550,7 @@ Matrix MatrixFromAxisAngle(Vector3 axis, float angle) // Create rotation matrix from axis and angle (version 2) // TODO: Test this function +// NOTE: NO prototype defined! Matrix MatrixFromAxisAngle2(Vector3 axis, float angle) { Matrix result; @@ -725,21 +727,24 @@ Matrix MatrixFrustum(double left, double right, double bottom, double top, doubl float tb = (top - bottom); float fn = (far - near); - result.m0 = (near*2) / rl; + result.m0 = (near*2.0f) / rl; result.m1 = 0; result.m2 = 0; result.m3 = 0; + result.m4 = 0; - result.m5 = (near*2) / tb; + result.m5 = (near*2.0f) / tb; result.m6 = 0; result.m7 = 0; + result.m8 = (right + left) / rl; result.m9 = (top + bottom) / tb; result.m10 = -(far + near) / fn; - result.m11 = -1; + result.m11 = -1.0f; + result.m12 = 0; result.m13 = 0; - result.m14 = -(far*near*2) / fn; + result.m14 = -(far*near*2.0f) / fn; result.m15 = 0; return result; @@ -748,7 +753,7 @@ Matrix MatrixFrustum(double left, double right, double bottom, double top, doubl // Returns perspective projection matrix Matrix MatrixPerspective(double fovy, double aspect, double near, double far) { - double top = near*tan(fovy*PI / 360.0); + double top = near*tanf(fovy*PI / 360.0f); double right = top*aspect; return MatrixFrustum(-right, right, -top, top, near, far); @@ -876,18 +881,18 @@ Quaternion QuaternionSlerp(Quaternion q1, Quaternion q2, float amount) float cosHalfTheta = q1.x*q2.x + q1.y*q2.y + q1.z*q2.z + q1.w*q2.w; - if (abs(cosHalfTheta) >= 1.0) result = q1; + if (abs(cosHalfTheta) >= 1.0f) result = q1; else { float halfTheta = acos(cosHalfTheta); - float sinHalfTheta = sqrt(1.0 - cosHalfTheta*cosHalfTheta); + float sinHalfTheta = sqrt(1.0f - cosHalfTheta*cosHalfTheta); - if (abs(sinHalfTheta) < 0.001) + if (abs(sinHalfTheta) < 0.001f) { - result.x = (q1.x*0.5 + q2.x*0.5); - result.y = (q1.y*0.5 + q2.y*0.5); - result.z = (q1.z*0.5 + q2.z*0.5); - result.w = (q1.w*0.5 + q2.w*0.5); + result.x = (q1.x*0.5f + q2.x*0.5f); + result.y = (q1.y*0.5f + q2.y*0.5f); + result.z = (q1.z*0.5f + q2.z*0.5f); + result.w = (q1.w*0.5f + q2.w*0.5f); } else { diff --git a/src/raymath.h b/src/raymath.h index 5dcfe061..c396a347 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -1,4 +1,4 @@ -/********************************************************************************************* +/********************************************************************************************** * * raymath * @@ -39,8 +39,8 @@ #define PI 3.14159265358979323846 #endif -#define DEG2RAD (PI / 180.0) -#define RAD2DEG (180.0 / PI) +#define DEG2RAD (PI / 180.0f) +#define RAD2DEG (180.0f / PI) //---------------------------------------------------------------------------------- // Types and Structures Definition @@ -101,9 +101,9 @@ float *GetMatrixVector(Matrix mat); // Returns an OpenGL-rea float MatrixDeterminant(Matrix mat); // Compute matrix determinant float MatrixTrace(Matrix mat); // Returns the trace of the matrix (sum of the values along the diagonal) void MatrixTranspose(Matrix *mat); // Transposes provided matrix -void MatrixInvert(Matrix *mat); // Invert provided matrix +void MatrixInvert(Matrix *mat); // Invert provided matrix void MatrixNormalize(Matrix *mat); // Normalize provided matrix -Matrix MatrixIdentity(); // Returns identity matrix +Matrix MatrixIdentity(void); // Returns identity matrix Matrix MatrixAdd(Matrix left, Matrix right); // Add two matrices Matrix MatrixSubstract(Matrix left, Matrix right); // Substract two matrices (left - right) Matrix MatrixTranslate(float x, float y, float z); // Returns translation matrix diff --git a/src/resources b/src/resources Binary files differnew file mode 100644 index 00000000..2038c07a --- /dev/null +++ b/src/resources @@ -1,4 +1,4 @@ -/********************************************************************************************* +/********************************************************************************************** * * rlgl - raylib OpenGL abstraction layer * @@ -31,32 +31,21 @@ #include <stdio.h> // Standard input / output lib #include <stdlib.h> // Declares malloc() and free() for memory management, rand() -// Security check in case no USE_OPENGL_* defined -#if !defined(USE_OPENGL_11) && !defined(USE_OPENGL_33) && !defined(USE_OPENGL_ES2) - #define USE_OPENGL_11 -#endif - -// Security check in case multiple USE_OPENGL_* defined -#ifdef USE_OPENGL_11 - #ifdef USE_OPENGL_33 - #undef USE_OPENGL_33 - #endif - - #ifdef USE_OPENGL_ES2 - #undef USE_OPENGL_ES2 - #endif -#endif - -#ifdef USE_OPENGL_11 +#if defined(GRAPHICS_API_OPENGL_11) #include <GL/gl.h> // Basic OpenGL include #endif -#ifdef USE_OPENGL_33 +#if defined(GRAPHICS_API_OPENGL_33) #define GLEW_STATIC #include <GL/glew.h> // Extensions loading lib + //#include "glad.h" // TODO: Other extensions loading lib? --> REVIEW #endif -//#include "glad.h" // Other extensions loading lib? --> REVIEW +#if defined(GRAPHICS_API_OPENGL_ES2) + #include <EGL/egl.h> + #include <GLES2/gl2.h> + #include <GLES2/gl2ext.h> +#endif //---------------------------------------------------------------------------------- // Defines and Macros @@ -64,6 +53,7 @@ #define MATRIX_STACK_SIZE 16 // Matrix stack max size #define MAX_DRAWS_BY_TEXTURE 256 // Draws are organized by texture changes #define TEMP_VERTEX_BUFFER_SIZE 4096 // Temporal Vertex Buffer (required for vertex-transformations) + // NOTE: Every vertex are 3 floats (12 bytes) //---------------------------------------------------------------------------------- // Types and Structures Definition @@ -98,6 +88,7 @@ typedef struct { float *vertices; // 3 components per vertex float *texcoords; // 2 components per vertex float *normals; // 3 components per vertex + //short *normals; // NOTE: Less data load... but padding issues and normalizing required! } VertexPositionTextureNormalBuffer; // Vertex buffer (position + texcoords + colors + indices arrays) @@ -109,7 +100,12 @@ typedef struct { float *vertices; // 3 components per vertex float *texcoords; // 2 components per vertex unsigned char *colors; // 4 components per vertex - unsigned int *indices; // 6 indices per quad +#if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_33) + unsigned int *indices; // 6 indices per quad (could be int) +#elif defined(GRAPHICS_API_OPENGL_ES2) + unsigned short *indices; // 6 indices per quad (must be short) + // NOTE: 6*2 byte = 12 byte, not alignment problem! +#endif } VertexPositionColorTextureIndexBuffer; // Draw call type @@ -131,7 +127,7 @@ typedef struct { //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- -#if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2) +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) static Matrix stack[MATRIX_STACK_SIZE]; static int stackCounter = 0; @@ -173,15 +169,26 @@ static bool useTempBuffer = false; // White texture useful for plain color polys (required by shader) static GLuint whiteTexture; + +// Support for VAOs (OpenGL ES2 could not support VAO extensions) +static bool vaoSupported = false; +#endif + +#if defined(GRAPHICS_API_OPENGL_ES2) +// NOTE: VAO functionality is exposed through extensions (OES) +static PFNGLGENVERTEXARRAYSOESPROC glGenVertexArrays; +static PFNGLBINDVERTEXARRAYOESPROC glBindVertexArray; +static PFNGLDELETEVERTEXARRAYSOESPROC glDeleteVertexArrays; +static PFNGLISVERTEXARRAYOESPROC glIsVertexArray; #endif //---------------------------------------------------------------------------------- // Module specific Functions Declaration //---------------------------------------------------------------------------------- -#if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2) +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) static GLuint LoadDefaultShaders(void); static void InitializeBuffers(void); -static void InitializeVAOs(void); +static void InitializeBuffersGPU(void); static void UpdateBuffers(void); // Shader files loading (external) - Not used but useful... @@ -189,7 +196,7 @@ static GLuint LoadShaders(char *vertexFileName, char *fragmentFileName); static char *TextFileRead(char *fn); #endif -#ifdef USE_OPENGL_11 +#if defined(GRAPHICS_API_OPENGL_11) static int GenerateMipmaps(unsigned char *data, int baseWidth, int baseHeight); static pixel *GenNextMipmap(pixel *srcData, int srcWidth, int srcHeight); #endif @@ -198,7 +205,7 @@ static pixel *GenNextMipmap(pixel *srcData, int srcWidth, int srcHeight); // Module Functions Definition - Matrix operations //---------------------------------------------------------------------------------- -#ifdef USE_OPENGL_11 +#if defined(GRAPHICS_API_OPENGL_11) // Fallback to OpenGL 1.1 function calls //--------------------------------------- @@ -231,7 +238,7 @@ void rlRotatef(float angleDeg, float x, float y, float z) { glRotatef(angleDeg, void rlScalef(float x, float y, float z) { glScalef(x, y, z); } void rlMultMatrixf(float *mat) { glMultMatrixf(mat); } -#else +#elif defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) // Choose the current matrix to be transformed void rlMatrixMode(int mode) @@ -343,7 +350,7 @@ void rlOrtho(double left, double right, double bottom, double top, double near, //---------------------------------------------------------------------------------- // Module Functions Definition - Vertex level operations //---------------------------------------------------------------------------------- -#ifdef USE_OPENGL_11 +#if defined(GRAPHICS_API_OPENGL_11) // Fallback to OpenGL 1.1 function calls //--------------------------------------- @@ -358,7 +365,7 @@ void rlBegin(int mode) } } -void rlEnd(void) { glEnd(); } +void rlEnd() { glEnd(); } void rlVertex2i(int x, int y) { glVertex2i(x, y); } void rlVertex2f(float x, float y) { glVertex2f(x, y); } void rlVertex3f(float x, float y, float z) { glVertex3f(x, y, z); } @@ -368,7 +375,7 @@ void rlColor4ub(byte r, byte g, byte b, byte a) { glColor4ub(r, g, b, a); } void rlColor3f(float x, float y, float z) { glColor3f(x, y, z); } void rlColor4f(float x, float y, float z, float w) { glColor4f(x, y, z, w); } -#else +#elif defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) // Initialize drawing mode (how to organize vertex) void rlBegin(int mode) @@ -492,31 +499,46 @@ void rlVertex3f(float x, float y, float z) { case RL_LINES: { - lines.vertices[3*lines.vCounter] = x; - lines.vertices[3*lines.vCounter + 1] = y; - lines.vertices[3*lines.vCounter + 2] = z; + // Verify that MAX_LINES_BATCH limit not reached + if (lines.vCounter / 2 < MAX_LINES_BATCH) + { + lines.vertices[3*lines.vCounter] = x; + lines.vertices[3*lines.vCounter + 1] = y; + lines.vertices[3*lines.vCounter + 2] = z; - lines.vCounter++; + lines.vCounter++; + } + else TraceLog(ERROR, "MAX_LINES_BATCH overflow"); } break; case RL_TRIANGLES: { - triangles.vertices[3*triangles.vCounter] = x; - triangles.vertices[3*triangles.vCounter + 1] = y; - triangles.vertices[3*triangles.vCounter + 2] = z; + // Verify that MAX_TRIANGLES_BATCH limit not reached + if (triangles.vCounter / 3 < MAX_TRIANGLES_BATCH) + { + triangles.vertices[3*triangles.vCounter] = x; + triangles.vertices[3*triangles.vCounter + 1] = y; + triangles.vertices[3*triangles.vCounter + 2] = z; - triangles.vCounter++; + triangles.vCounter++; + } + else TraceLog(ERROR, "MAX_TRIANGLES_BATCH overflow"); } break; case RL_QUADS: { - quads.vertices[3*quads.vCounter] = x; - quads.vertices[3*quads.vCounter + 1] = y; - quads.vertices[3*quads.vCounter + 2] = z; + // Verify that MAX_QUADS_BATCH limit not reached + if (quads.vCounter / 4 < MAX_QUADS_BATCH) + { + quads.vertices[3*quads.vCounter] = x; + quads.vertices[3*quads.vCounter + 1] = y; + quads.vertices[3*quads.vCounter + 2] = z; - quads.vCounter++; + quads.vCounter++; - draws[drawsCounter - 1].vertexCount++; + draws[drawsCounter - 1].vertexCount++; + } + else TraceLog(ERROR, "MAX_QUADS_BATCH overflow"); } break; default: break; @@ -527,17 +549,17 @@ void rlVertex3f(float x, float y, float z) // Define one vertex (position) void rlVertex2f(float x, float y) { - rlVertex3f(x, y, 0.0); + rlVertex3f(x, y, 0.0f); } // Define one vertex (position) void rlVertex2i(int x, int y) { - rlVertex3f((float)x, (float)y, 0.0); + rlVertex3f((float)x, (float)y, 0.0f); } // Define one vertex (texture coordinate) -// NOTE: Texture coordinates are limited to TRIANGLES only +// NOTE: Texture coordinates are limited to QUADS only void rlTexCoord2f(float x, float y) { if (currentDrawMode == RL_QUADS) @@ -616,12 +638,12 @@ void rlColor3f(float x, float y, float z) // Enable texture usage void rlEnableTexture(unsigned int id) { -#ifdef USE_OPENGL_11 +#if defined(GRAPHICS_API_OPENGL_11) glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, id); #endif -#if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2) +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) if (draws[drawsCounter - 1].textureId != id) { if (draws[drawsCounter - 1].vertexCount > 0) drawsCounter++; @@ -635,7 +657,7 @@ void rlEnableTexture(unsigned int id) // Disable texture usage void rlDisableTexture(void) { -#ifdef USE_OPENGL_11 +#if defined(GRAPHICS_API_OPENGL_11) glDisable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, 0); #endif @@ -647,11 +669,19 @@ void rlDeleteTextures(unsigned int id) glDeleteTextures(1, &id); } -// Unload vertex data from GPU memory +// Unload vertex data (VAO) from GPU memory void rlDeleteVertexArrays(unsigned int id) { -#if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2) - glDeleteVertexArrays(1, &id); +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + if (vaoSupported) glDeleteVertexArrays(1, &id); +#endif +} + +// Unload vertex data (VBO) from GPU memory +void rlDeleteBuffers(unsigned int id) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glDeleteBuffers(1, &id); #endif } @@ -674,40 +704,109 @@ void rlClearScreenBuffers(void) //glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); // Stencil buffer not used... } +// Returns current OpenGL version +int rlGetVersion(void) +{ +#if defined(GRAPHICS_API_OPENGL_11) + return OPENGL_11; +#elif defined(GRAPHICS_API_OPENGL_33) + return OPENGL_33; +#elif defined(GRAPHICS_API_OPENGL_ES2) + return OPENGL_ES_20; +#endif +} + //---------------------------------------------------------------------------------- // Module Functions Definition - rlgl Functions //---------------------------------------------------------------------------------- -#if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2) - // Init OpenGL 3.3+ required data void rlglInit(void) { - // Initialize GLEW +#if defined(GRAPHICS_API_OPENGL_33) + // Loading extensions the hard way (Example) +/* + GLint numExt; + glGetIntegerv(GL_NUM_EXTENSIONS, &numExt); + + for (int i = 0; i < numExt; i++) + { + const GLubyte *extensionName = glGetStringi(GL_EXTENSIONS, i); + if (strcmp(extensionName, (const GLubyte *)"GL_ARB_vertex_array_object") == 0) + { + // The extension is supported by our hardware and driver, try to get related functions popinters + glGenVertexArrays = (PFNGLGENVERTEXARRAYSOESPROC)wglGetProcAddress("glGenVertexArrays"); + glBindVertexArray = (PFNGLBINDVERTEXARRAYOESPROC)wglGetProcAddress("glBindVertexArray"); + glDeleteVertexArrays = (PFNGLDELETEVERTEXARRAYSOESPROC)wglGetProcAddress("glDeleteVertexArrays"); + glIsVertexArray = (PFNGLISVERTEXARRAYOESPROC)wglGetProcAddress("glIsVertexArray"); + } + } +*/ + + // Initialize extensions using GLEW glewExperimental = 1; // Needed for core profile GLenum error = glewInit(); - if (error != GLEW_OK) + if (error != GLEW_OK) TraceLog(ERROR, "Failed to initialize GLEW - Error Code: %s\n", glewGetErrorString(error)); + + if (glewIsSupported("GL_VERSION_3_3")) TraceLog(INFO, "OpenGL 3.3 extensions supported"); + + // NOTE: GLEW is a big library that loads ALL extensions, using glad we can only load required ones... + //if (!gladLoadGL()) TraceLog("ERROR: Failed to initialize glad\n"); + + vaoSupported = true; +#endif + +#if defined(GRAPHICS_API_OPENGL_ES2) + glGenVertexArrays = (PFNGLGENVERTEXARRAYSOESPROC)eglGetProcAddress("glGenVertexArraysOES"); + glBindVertexArray = (PFNGLBINDVERTEXARRAYOESPROC)eglGetProcAddress("glBindVertexArrayOES"); + glDeleteVertexArrays = (PFNGLDELETEVERTEXARRAYSOESPROC)eglGetProcAddress("glDeleteVertexArraysOES"); + glIsVertexArray = (PFNGLISVERTEXARRAYOESPROC)eglGetProcAddress("glIsVertexArrayOES"); + + if (glGenVertexArrays == NULL) TraceLog(WARNING, "Could not initialize VAO extensions, VAOs not supported"); + else { - TraceLog(ERROR, "Failed to initialize GLEW - Error Code: %s\n", glewGetErrorString(error)); + vaoSupported = true; + TraceLog(INFO, "VAO extensions initialized successfully"); } +#endif - if (glewIsSupported("GL_VERSION_3_3")) TraceLog(INFO, "OpenGL 3.3 initialized successfully\n"); + // Print current OpenGL and GLSL version + TraceLog(INFO, "GPU: Vendor: %s", glGetString(GL_VENDOR)); + TraceLog(INFO, "GPU: Renderer: %s", glGetString(GL_RENDERER)); + TraceLog(INFO, "GPU: Version: %s", glGetString(GL_VERSION)); + TraceLog(INFO, "GPU: GLSL: %s", glGetString(0x8B8C)); //GL_SHADING_LANGUAGE_VERSION - // Print OpenGL and GLSL version - TraceLog(INFO, "Vendor: %s", glGetString(GL_VENDOR)); - TraceLog(INFO, "Renderer: %s", glGetString(GL_RENDERER)); - TraceLog(INFO, "Version: %s", glGetString(GL_VERSION)); - TraceLog(INFO, "GLSL: %s\n", glGetString(0x8B8C)); //GL_SHADING_LANGUAGE_VERSION + // NOTE: We can get a bunch of extra information about GPU capabilities (glGet*) + //int maxTexSize; + //glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTexSize); + //TraceLog(INFO, "GL_MAX_TEXTURE_SIZE: %i", maxTexSize); + //int numAuxBuffers; + //glGetIntegerv(GL_AUX_BUFFERS, &numAuxBuffers); + //TraceLog(INFO, "GL_AUX_BUFFERS: %i", numAuxBuffers); + + // Show supported extensions + // NOTE: We don't need that much data on screen... right now... /* - // TODO: GLEW is a big library that loads ALL extensions, maybe using glad we can only load required ones... - if (!gladLoadGL()) +#if defined(GRAPHICS_API_OPENGL_33) + GLint numExt; + glGetIntegerv(GL_NUM_EXTENSIONS, &numExt); + + for (int i = 0; i < numExt; i++) { - TraceLog("ERROR: Failed to initialize glad\n"); + TraceLog(INFO, "Supported extension: %s", glGetStringi(GL_EXTENSIONS, i)); } +#elif defined(GRAPHICS_API_OPENGL_ES2) + char *extensions = (char *)glGetString(GL_EXTENSIONS); // One big string + + // NOTE: String could be splitted using strtok() function (string.h) + TraceLog(INFO, "Supported extension: %s", extensions); +#endif */ + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) // Set default draw mode currentDrawMode = RL_TRIANGLES; @@ -735,8 +834,8 @@ void rlglInit(void) // Get handles to GLSL uniform vars locations (fragment-shader) textureLoc = glGetUniformLocation(shaderProgram, "texture0"); - InitializeBuffers(); // Init vertex arrays - InitializeVAOs(); // Init VBO and VAO + InitializeBuffers(); // Init vertex arrays + InitializeBuffersGPU(); // Init VBO and VAO // Init temp vertex buffer, used when transformation required (translate, rotate, scale) tempBuffer = (Vector3 *)malloc(sizeof(Vector3)*TEMP_VERTEX_BUFFER_SIZE); @@ -748,7 +847,7 @@ void rlglInit(void) whiteTexture = rlglLoadTexture(pixels, 1, 1, false); - if (whiteTexture != 0) TraceLog(INFO, "[ID %i] Base white texture created successfully", whiteTexture); + if (whiteTexture != 0) TraceLog(INFO, "[TEX ID %i] Base white texture created successfully", whiteTexture); else TraceLog(WARNING, "Base white texture could not be created"); // Init draw calls tracking system @@ -762,13 +861,15 @@ void rlglInit(void) drawsCounter = 1; draws[drawsCounter - 1].textureId = whiteTexture; +#endif } // Vertex Buffer Object deinitialization (memory free) void rlglClose(void) { +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) // Unbind everything - glBindVertexArray(0); + if (vaoSupported) glBindVertexArray(0); glDisableVertexAttribArray(0); glDisableVertexAttribArray(1); glDisableVertexAttribArray(2); @@ -778,7 +879,7 @@ void rlglClose(void) glUseProgram(0); - // Delete VAOs and VBOs + // Delete VBOs glDeleteBuffers(1, &linesBuffer[0]); glDeleteBuffers(1, &linesBuffer[1]); glDeleteBuffers(1, &trianglesBuffer[0]); @@ -788,9 +889,13 @@ void rlglClose(void) glDeleteBuffers(1, &quadsBuffer[2]); glDeleteBuffers(1, &quadsBuffer[3]); - glDeleteVertexArrays(1, &vaoLines); - glDeleteVertexArrays(1, &vaoTriangles); - glDeleteVertexArrays(1, &vaoQuads); + if (vaoSupported) + { + // Delete VAOs + glDeleteVertexArrays(1, &vaoLines); + glDeleteVertexArrays(1, &vaoTriangles); + glDeleteVertexArrays(1, &vaoQuads); + } //glDetachShader(shaderProgram, v); //glDetachShader(shaderProgram, f); @@ -808,15 +913,18 @@ void rlglClose(void) free(quads.vertices); free(quads.texcoords); free(quads.colors); + free(quads.indices); // Free GPU texture glDeleteTextures(1, &whiteTexture); free(draws); +#endif } void rlglDraw(void) { +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) UpdateBuffers(); glUseProgram(shaderProgram); // Use our shader @@ -831,9 +939,24 @@ void rlglDraw(void) { glBindTexture(GL_TEXTURE_2D, whiteTexture); - glBindVertexArray(vaoTriangles); + if (vaoSupported) + { + glBindVertexArray(vaoTriangles); + } + else + { + glBindBuffer(GL_ARRAY_BUFFER, trianglesBuffer[0]); + glVertexAttribPointer(vertexLoc, 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(vertexLoc); + + glBindBuffer(GL_ARRAY_BUFFER, trianglesBuffer[1]); + glVertexAttribPointer(colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); + glEnableVertexAttribArray(colorLoc); + } + glDrawArrays(GL_TRIANGLES, 0, triangles.vCounter); + if (!vaoSupported) glBindBuffer(GL_ARRAY_BUFFER, 0); glBindTexture(GL_TEXTURE_2D, 0); } @@ -843,7 +966,27 @@ void rlglDraw(void) int numIndicesToProcess = 0; int indicesOffset = 0; - glBindVertexArray(vaoQuads); + if (vaoSupported) + { + glBindVertexArray(vaoQuads); + } + else + { + // Enable vertex attributes + glBindBuffer(GL_ARRAY_BUFFER, quadsBuffer[0]); + glVertexAttribPointer(vertexLoc, 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(vertexLoc); + + glBindBuffer(GL_ARRAY_BUFFER, quadsBuffer[1]); + glVertexAttribPointer(texcoordLoc, 2, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(texcoordLoc); + + glBindBuffer(GL_ARRAY_BUFFER, quadsBuffer[2]); + glVertexAttribPointer(colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); + glEnableVertexAttribArray(colorLoc); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quadsBuffer[3]); + } //TraceLog(DEBUG, "Draws required per frame: %i", drawsCounter); @@ -857,11 +1000,23 @@ void rlglDraw(void) glBindTexture(GL_TEXTURE_2D, draws[i].textureId); // NOTE: The final parameter tells the GPU the offset in bytes from the start of the index buffer to the location of the first index to process +#if defined(GRAPHICS_API_OPENGL_33) glDrawElements(GL_TRIANGLES, numIndicesToProcess, GL_UNSIGNED_INT, (GLvoid*) (sizeof(GLuint) * indicesOffset)); +#elif defined(GRAPHICS_API_OPENGL_ES2) + glDrawElements(GL_TRIANGLES, numIndicesToProcess, GL_UNSIGNED_SHORT, (GLvoid*) (sizeof(GLushort) * indicesOffset)); +#endif + //GLenum err; + //if ((err = glGetError()) != GL_NO_ERROR) TraceLog(INFO, "OpenGL error: %i", (int)err); //GL_INVALID_ENUM! indicesOffset += draws[i].vertexCount/4*6; } + if (!vaoSupported) + { + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + } + glBindTexture(GL_TEXTURE_2D, 0); // Unbind textures } @@ -869,13 +1024,28 @@ void rlglDraw(void) { glBindTexture(GL_TEXTURE_2D, whiteTexture); - glBindVertexArray(vaoLines); + if (vaoSupported) + { + glBindVertexArray(vaoLines); + } + else + { + glBindBuffer(GL_ARRAY_BUFFER, linesBuffer[0]); + glVertexAttribPointer(vertexLoc, 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(vertexLoc); + + glBindBuffer(GL_ARRAY_BUFFER, linesBuffer[1]); + glVertexAttribPointer(colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); + glEnableVertexAttribArray(colorLoc); + } + glDrawArrays(GL_LINES, 0, lines.vCounter); + if (!vaoSupported) glBindBuffer(GL_ARRAY_BUFFER, 0); glBindTexture(GL_TEXTURE_2D, 0); } - glBindVertexArray(0); // Unbind VAO + if (vaoSupported) glBindVertexArray(0); // Unbind VAO // Reset draws counter drawsCounter = 1; @@ -892,16 +1062,18 @@ void rlglDraw(void) quads.vCounter = 0; quads.tcCounter = 0; quads.cCounter = 0; +#endif } -#endif // End for OpenGL 3.3+ and ES2 only functions - // Draw a 3d model void rlglDrawModel(Model model, Vector3 position, Vector3 rotation, Vector3 scale, Color color, bool wires) { +#if defined (GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_33) + // NOTE: glPolygonMode() not available on OpenGL ES if (wires) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); +#endif -#ifdef USE_OPENGL_11 +#if defined(GRAPHICS_API_OPENGL_11) glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, model.textureId); @@ -937,7 +1109,7 @@ void rlglDrawModel(Model model, Vector3 position, Vector3 rotation, Vector3 scal glBindTexture(GL_TEXTURE_2D, 0); #endif -#if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2) +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) glUseProgram(shaderProgram); // Use our shader // Get transform matrix (rotation -> scale -> translation) @@ -964,7 +1136,7 @@ void rlglDrawModel(Model model, Vector3 position, Vector3 rotation, Vector3 scal } // Update colors buffer in CPU (using Shader) - glBindVertexArray(model.vaoId); + if (vaoSupported) glBindVertexArray(model.vaoId); GLuint colorVboId; glGetVertexAttribIuiv(2, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, &colorVboId); // NOTE: Color VBO is buffer index 2 glBindBuffer(GL_ARRAY_BUFFER, colorVboId); @@ -977,23 +1149,47 @@ void rlglDrawModel(Model model, Vector3 position, Vector3 rotation, Vector3 scal //TraceLog(DEBUG, "ShaderProgram: %i, VAO ID: %i, VertexCount: %i", shaderProgram, model.vaoId, model.mesh.vertexCount); - glBindVertexArray(model.vaoId); + if (vaoSupported) + { + glBindVertexArray(model.vaoId); + } + else + { + // Bind model VBOs data + glBindBuffer(GL_ARRAY_BUFFER, model.vboId[0]); + glVertexAttribPointer(vertexLoc, 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(vertexLoc); + + glBindBuffer(GL_ARRAY_BUFFER, model.vboId[1]); + glVertexAttribPointer(texcoordLoc, 2, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(texcoordLoc); + + glBindBuffer(GL_ARRAY_BUFFER, model.vboId[2]); + glVertexAttribPointer(colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); + glEnableVertexAttribArray(colorLoc); + } + glBindTexture(GL_TEXTURE_2D, model.textureId); glDrawArrays(GL_TRIANGLES, 0, model.mesh.vertexCount); - glBindTexture(GL_TEXTURE_2D, 0); // Unbind textures - glBindVertexArray(0); // Unbind VAO + glBindTexture(GL_TEXTURE_2D, 0); // Unbind textures + + if (vaoSupported) glBindVertexArray(0); // Unbind VAO + else glBindBuffer(GL_ARRAY_BUFFER, 0); // Unbind VBOs #endif +#if defined (GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_33) + // NOTE: glPolygonMode() not available on OpenGL ES if (wires) glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); +#endif } // Initialize Graphics Device (OpenGL stuff) -void rlglInitGraphicsDevice(int fbWidth, int fbHeight) +void rlglInitGraphics(int offsetX, int offsetY, int width, int height) { - glViewport(0, 0, fbWidth, fbHeight); // Set viewport width and height - // NOTE: Required! viewport must be recalculated if screen resized! + // NOTE: Required! viewport must be recalculated if screen resized! + glViewport(offsetX/2, offsetY/2, width - offsetX, height - offsetY); // Set viewport width and height // NOTE: Don't confuse glViewport with the transformation matrix // NOTE: glViewport just defines the area of the context that you will actually draw to. @@ -1008,7 +1204,7 @@ void rlglInitGraphicsDevice(int fbWidth, int fbHeight) glEnable(GL_BLEND); // Enable color blending (required to work with transparencies) glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Color blending function (how colors are mixed) -#ifdef USE_OPENGL_11 +#if defined(GRAPHICS_API_OPENGL_11) glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // Improve quality of color and texture coordinate interpolation (Deprecated in OGL 3.0) // Other options: GL_FASTEST, GL_DONT_CARE (default) #endif @@ -1016,7 +1212,7 @@ void rlglInitGraphicsDevice(int fbWidth, int fbHeight) rlMatrixMode(RL_PROJECTION); // Switch to PROJECTION matrix rlLoadIdentity(); // Reset current matrix (PROJECTION) - rlOrtho(0, fbWidth, fbHeight, 0, 0, 1); // Config orthographic mode: top-left corner --> (0,0) + rlOrtho(0, width - offsetX, height - offsetY, 0, 0, 1); // Config orthographic mode: top-left corner --> (0,0) rlMatrixMode(RL_MODELVIEW); // Switch back to MODELVIEW matrix rlLoadIdentity(); // Reset current matrix (MODELVIEW) @@ -1027,12 +1223,12 @@ void rlglInitGraphicsDevice(int fbWidth, int fbHeight) //glCullFace(GL_BACK); // Cull the Back face (default) //glFrontFace(GL_CCW); // Front face are defined counter clockwise (default) -#ifdef USE_OPENGL_11 +#if defined(GRAPHICS_API_OPENGL_11) glShadeModel(GL_SMOOTH); // Smooth shading between vertex (vertex colors interpolation) (Deprecated on OpenGL 3.3+) // Possible options: GL_SMOOTH (Color interpolation) or GL_FLAT (no interpolation) #endif - TraceLog(INFO, "OpenGL Graphics Device initialized successfully"); + TraceLog(INFO, "OpenGL Graphics initialized successfully"); } // Convert image data to OpenGL texture (returns OpenGL valid Id) @@ -1057,7 +1253,7 @@ unsigned int rlglLoadTexture(unsigned char *data, int width, int height, bool ge if (genMipmaps && !texIsPOT) { - TraceLog(WARNING, "[ID %i] Texture is not power-of-two, mipmaps can not be generated", id); + TraceLog(WARNING, "[TEX ID %i] Texture is not power-of-two, mipmaps can not be generated", id); genMipmaps = false; } @@ -1076,10 +1272,10 @@ unsigned int rlglLoadTexture(unsigned char *data, int width, int height, bool ge glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); // Filter for pixel-perfect drawing, alternative: GL_LINEAR } -#ifdef USE_OPENGL_11 +#if defined(GRAPHICS_API_OPENGL_11) if (genMipmaps) { - TraceLog(WARNING, "[ID %i] Mipmaps generated manually on CPU side", id); + TraceLog(WARNING, "[TEX ID %i] Mipmaps generated manually on CPU side", id); // Compute required mipmaps // NOTE: data size is reallocated to fit mipmaps data @@ -1106,17 +1302,20 @@ unsigned int rlglLoadTexture(unsigned char *data, int width, int height, bool ge else glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); #endif - -#if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2) - +#if defined(GRAPHICS_API_OPENGL_33) + // NOTE: We define internal (GPU) format as GL_RGBA8 (probably BGRA8 in practice, driver takes care) glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); +#elif defined(GRAPHICS_API_OPENGL_ES2) + // NOTE: On embedded systems, we let the driver choose the best internal format + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); +#endif +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) if (genMipmaps) { glGenerateMipmap(GL_TEXTURE_2D); // Generate mipmaps automatically - TraceLog(INFO, "[ID %i] Mipmaps generated automatically for new texture", id); + TraceLog(INFO, "[TEX ID %i] Mipmaps generated automatically for new texture", id); } - #endif // At this point we have the image converted to texture and uploaded to GPU @@ -1124,23 +1323,102 @@ unsigned int rlglLoadTexture(unsigned char *data, int width, int height, bool ge // Unbind current texture glBindTexture(GL_TEXTURE_2D, 0); - TraceLog(INFO, "[ID %i] New texture created (%i x %i)", id, width, height); + TraceLog(INFO, "[TEX ID %i] Texture created successfully (%i x %i)", id, width, height); return id; } +// Load vertex data into a VAO (if supported) and VBO +Model rlglLoadModel(VertexData mesh) +{ + Model model; + + model.mesh = mesh; + +#if defined(GRAPHICS_API_OPENGL_11) + model.textureId = 0; // No texture required + model.vaoId = 0; // Vertex Array Object + model.vboId[0] = 0; // Vertex position VBO + model.vboId[1] = 0; // Texcoords VBO + //model.vboId[2] = 0; // Normals VBO (not used) + model.vboId[2] = 0; // Colors VBO + +#elif defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + model.textureId = 1; // Default whiteTexture + + GLuint vaoModel; // Vertex Array Objects (VAO) + GLuint vertexBuffer[3]; // Vertex Buffer Objects (VBO) + + if (vaoSupported) + { + // Initialize Quads VAO (Buffer A) + glGenVertexArrays(1, &vaoModel); + glBindVertexArray(vaoModel); + } + + // Create buffers for our vertex data (positions, texcoords, normals) + glGenBuffers(3, vertexBuffer); + + // Enable vertex attributes: position + glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer[0]); + glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*mesh.vertexCount, mesh.vertices, GL_STATIC_DRAW); + glEnableVertexAttribArray(vertexLoc); + glVertexAttribPointer(vertexLoc, 3, GL_FLOAT, 0, 0, 0); + + // Enable vertex attributes: texcoords + glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer[1]); + glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*mesh.vertexCount, mesh.texcoords, GL_STATIC_DRAW); + glEnableVertexAttribArray(texcoordLoc); + glVertexAttribPointer(texcoordLoc, 2, GL_FLOAT, 0, 0, 0); + + // TODO: Normals support -> Lighting + + // Enable vertex attributes: normals + //glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer[2]); + //glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*mesh.vertexCount, mesh.normals, GL_STATIC_DRAW); + //glEnableVertexAttribArray(normalLoc); + //glVertexAttribPointer(normalLoc, 3, GL_FLOAT, 0, 0, 0); + + // Enable vertex attributes: colors + glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer[2]); + glBufferData(GL_ARRAY_BUFFER, sizeof(unsigned char)*4*mesh.vertexCount, mesh.colors, GL_STATIC_DRAW); + glEnableVertexAttribArray(colorLoc); + glVertexAttribPointer(colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); + + if (vaoSupported) + { + if (vaoModel > 0) + { + model.vaoId = vaoModel; + TraceLog(INFO, "[VAO ID %i] Model uploaded successfully to VRAM (GPU)", vaoModel); + } + else TraceLog(WARNING, "Model could not be uploaded to VRAM (GPU)"); + } + else + { + model.vboId[0] = vertexBuffer[0]; // Vertex position VBO + model.vboId[1] = vertexBuffer[1]; // Texcoords VBO + //model.vboId[2] = 0; // Normals VBO (not used) + model.vboId[2] = vertexBuffer[2]; // Colors VBO + + TraceLog(INFO, "[VBO ID %i][VBO ID %i][VBO ID %i] Model uploaded successfully to VRAM (GPU)", model.vboId[0], model.vboId[1], model.vboId[2]); + } +#endif -#ifdef USE_OPENGL_33 + return model; +} // Convert image data to OpenGL texture (returns OpenGL valid Id) // NOTE: Expected compressed image data and POT image unsigned int rlglLoadCompressedTexture(unsigned char *data, int width, int height, int mipmapCount, int compFormat) { - // Create one OpenGL texture GLuint id; - glGenTextures(1, &id); +#if defined(GRAPHICS_API_OPENGL_11) + id = 0; + TraceLog(WARNING, "GPU compressed textures not supported on OpenGL 1.1"); +#elif defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) TraceLog(DEBUG, "Compressed texture width: %i", width); TraceLog(DEBUG, "Compressed texture height: %i", height); TraceLog(DEBUG, "Compressed texture mipmap levels: %i", mipmapCount); @@ -1148,15 +1426,35 @@ unsigned int rlglLoadCompressedTexture(unsigned char *data, int width, int heigh if (compFormat == 0) { - TraceLog(WARNING, "[ID %i] Texture compressed format not recognized", id); id = 0; + TraceLog(WARNING, "Texture compressed format not recognized", id); } else { + glGenTextures(1, &id); + // Bind the texture glBindTexture(GL_TEXTURE_2D, id); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + // Set texture parameters + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + // If mipmaps are being used, we configure mag-min filters accordingly + if (mipmapCount > 1) + { + // Trilinear filtering with mipmaps + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); // Activate use of mipmaps (must be available) + } + else + { + // Not using mipmappings + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // Filter for pixel-perfect drawing, alternative: GL_LINEAR + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); // Filter for pixel-perfect drawing, alternative: GL_LINEAR + } + int blockSize = 0; int offset = 0; @@ -1166,9 +1464,13 @@ unsigned int rlglLoadCompressedTexture(unsigned char *data, int width, int heigh // Load the mipmaps for (int level = 0; level < mipmapCount && (width || height); level++) { - // NOTE: size specifies the number of bytes of image data (S3TC/DXTC) - unsigned int size = ((width + 3)/4)*((height + 3)/4)*blockSize; + unsigned int size = 0; + // NOTE: size specifies the number of bytes of image data (S3TC/DXTC) + if (compFormat == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) size = ((width + 3)/4)*((height + 3)/4)*blockSize; // S3TC/DXTC +#if defined(GRAPHICS_API_OPENGL_ES2) + else if (compFormat == GL_ETC1_RGB8_OES) size = 8*((width + 3) >> 2)*((height + 3) >> 2); // ETC1 +#endif glCompressedTexImage2D(GL_TEXTURE_2D, level, compFormat, width, height, 0, size, data + offset); offset += size; @@ -1180,54 +1482,11 @@ unsigned int rlglLoadCompressedTexture(unsigned char *data, int width, int heigh if (height < 1) height = 1; } } +#endif return id; } -// Load vertex data into a VAO -unsigned int rlglLoadModel(VertexData mesh) -{ - GLuint vaoModel; // Vertex Array Objects (VAO) - GLuint vertexBuffer[3]; // Vertex Buffer Objects (VBO) - - // Initialize Quads VAO (Buffer A) - glGenVertexArrays(1, &vaoModel); - glBindVertexArray(vaoModel); - - // Create buffers for our vertex data (positions, texcoords, normals) - glGenBuffers(3, vertexBuffer); - - // Enable vertex attributes: position - glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer[0]); - glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*mesh.vertexCount, mesh.vertices, GL_STATIC_DRAW); - glEnableVertexAttribArray(vertexLoc); - glVertexAttribPointer(vertexLoc, 3, GL_FLOAT, 0, 0, 0); - - // Enable vertex attributes: texcoords - glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer[1]); - glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*mesh.vertexCount, mesh.texcoords, GL_STATIC_DRAW); - glEnableVertexAttribArray(texcoordLoc); - glVertexAttribPointer(texcoordLoc, 2, GL_FLOAT, 0, 0, 0); - - // Enable vertex attributes: normals - //glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer[2]); - //glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*mesh.vertexCount, mesh.normals, GL_STATIC_DRAW); - //glEnableVertexAttribArray(normalLoc); - //glVertexAttribPointer(normalLoc, 3, GL_FLOAT, 0, 0, 0); - - // Enable vertex attributes: colors - glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer[2]); - glBufferData(GL_ARRAY_BUFFER, sizeof(unsigned char)*4*mesh.vertexCount, mesh.colors, GL_STATIC_DRAW); - glEnableVertexAttribArray(colorLoc); - glVertexAttribPointer(colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); - - if (vaoModel > 0) TraceLog(INFO, "[ID %i] Model uploaded successfully to VRAM (GPU)", vaoModel); - else TraceLog(WARNING, "Model could not be uploaded to VRAM (GPU)"); - - return vaoModel; -} -#endif - // Read screen pixel data (color buffer) unsigned char *rlglReadScreenPixels(int width, int height) { @@ -1252,14 +1511,14 @@ unsigned char *rlglReadScreenPixels(int width, int height) return imgData; // NOTE: image data should be freed } -#if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2) +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) -void PrintProjectionMatrix(void) +void PrintProjectionMatrix() { PrintMatrix(projection); } -void PrintModelviewMatrix(void) +void PrintModelviewMatrix() { PrintMatrix(modelview); } @@ -1270,7 +1529,7 @@ void PrintModelviewMatrix(void) // Module specific Functions Definition //---------------------------------------------------------------------------------- -#if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2) +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) // Load Shaders (Vertex and Fragment) static GLuint LoadDefaultShaders(void) @@ -1278,7 +1537,11 @@ static GLuint LoadDefaultShaders(void) // NOTE: Shaders are written using GLSL 110 (desktop), that is equivalent to GLSL 100 on ES2 // Vertex shader directly defined, no external file required - char vShaderStr[] = " #version 110 \n" // Equivalent to version 100 on ES2 +#if defined(GRAPHICS_API_OPENGL_33) + char vShaderStr[] = " #version 110 \n" // NOTE: Equivalent to version 100 on ES2 +#elif defined(GRAPHICS_API_OPENGL_ES2) + char vShaderStr[] = " #version 100 \n" // NOTE: Must be defined this way! 110 doesn't work! +#endif "uniform mat4 projectionMatrix; \n" "uniform mat4 modelviewMatrix; \n" "attribute vec3 vertexPosition; \n" @@ -1294,7 +1557,11 @@ static GLuint LoadDefaultShaders(void) "} \n"; // Fragment shader directly defined, no external file required - char fShaderStr[] = " #version 110 \n" // Equivalent to version 100 on ES2 +#if defined(GRAPHICS_API_OPENGL_33) + char fShaderStr[] = " #version 110 \n" // NOTE: Equivalent to version 100 on ES2 +#elif defined(GRAPHICS_API_OPENGL_ES2) + char fShaderStr[] = " #version 100 \n" // NOTE: Must be defined this way! 110 doesn't work! +#endif "uniform sampler2D texture0; \n" "varying vec2 fragTexCoord; \n" "varying vec4 fragColor; \n" @@ -1316,11 +1583,21 @@ static GLuint LoadDefaultShaders(void) glShaderSource(vertexShader, 1, &pvs, NULL); glShaderSource(fragmentShader, 1, &pfs, NULL); + GLint success = 0; + glCompileShader(vertexShader); + + glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success); + + if (success != GL_TRUE) TraceLog(WARNING, "[VSHDR ID %i] Failed to compile default vertex shader...", vertexShader); + else TraceLog(INFO, "[VSHDR ID %i] Default vertex shader compiled successfully", vertexShader); + glCompileShader(fragmentShader); - TraceLog(INFO, "[ID %i] Default vertex shader compiled successfully", vertexShader); - TraceLog(INFO, "[ID %i] Default fragment shader compiled successfully", fragmentShader); + glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success); + + if (success != GL_TRUE) TraceLog(WARNING, "[FSHDR ID %i] Failed to compile default fragment shader...", fragmentShader); + else TraceLog(INFO, "[FSHDR ID %i] Default fragment shader compiled successfully", fragmentShader); program = glCreateProgram(); @@ -1329,11 +1606,26 @@ static GLuint LoadDefaultShaders(void) glLinkProgram(program); + glGetProgramiv(program, GL_LINK_STATUS, &success); + + if (success == GL_FALSE) + { + int maxLength; + int length; + + glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength); + + char log[maxLength]; + + glGetProgramInfoLog(program, maxLength, &length, log); + + TraceLog(INFO, "Shader program fail log: %s", log); + } + else TraceLog(INFO, "[SHDR ID %i] Default shader program loaded successfully", program); + glDeleteShader(vertexShader); glDeleteShader(fragmentShader); - TraceLog(INFO, "[ID %i] Default shader program loaded successfully", program); - return program; } @@ -1361,8 +1653,8 @@ static GLuint LoadShaders(char *vertexFileName, char *fragmentFileName) glCompileShader(vertexShader); glCompileShader(fragmentShader); - TraceLog(INFO, "[ID %i] Vertex shader compiled successfully", vertexShader); - TraceLog(INFO, "[ID %i] Fragment shader compiled successfully", fragmentShader); + TraceLog(INFO, "[VSHDR ID %i] Vertex shader compiled successfully", vertexShader); + TraceLog(INFO, "[FSHDR ID %i] Fragment shader compiled successfully", fragmentShader); program = glCreateProgram(); @@ -1374,7 +1666,7 @@ static GLuint LoadShaders(char *vertexFileName, char *fragmentFileName) glDeleteShader(vertexShader); glDeleteShader(fragmentShader); - TraceLog(INFO, "[ID %i] Shader program loaded successfully", program); + TraceLog(INFO, "[SHDR ID %i] Shader program loaded successfully", program); return program; } @@ -1417,7 +1709,7 @@ static void InitializeBuffers(void) lines.vertices = (float *)malloc(sizeof(float)*3*2*MAX_LINES_BATCH); // 3 float by vertex, 2 vertex by line lines.colors = (unsigned char *)malloc(sizeof(unsigned char)*4*2*MAX_LINES_BATCH); // 4 float by color, 2 colors by line - for (int i = 0; i < (3*2*MAX_LINES_BATCH); i++) lines.vertices[i] = 0.0; + for (int i = 0; i < (3*2*MAX_LINES_BATCH); i++) lines.vertices[i] = 0.0f; for (int i = 0; i < (4*2*MAX_LINES_BATCH); i++) lines.colors[i] = 0; lines.vCounter = 0; @@ -1427,7 +1719,7 @@ static void InitializeBuffers(void) triangles.vertices = (float *)malloc(sizeof(float)*3*3*MAX_TRIANGLES_BATCH); // 3 float by vertex, 3 vertex by triangle triangles.colors = (unsigned char *)malloc(sizeof(unsigned char)*4*3*MAX_TRIANGLES_BATCH); // 4 float by color, 3 colors by triangle - for (int i = 0; i < (3*3*MAX_TRIANGLES_BATCH); i++) triangles.vertices[i] = 0.0; + for (int i = 0; i < (3*3*MAX_TRIANGLES_BATCH); i++) triangles.vertices[i] = 0.0f; for (int i = 0; i < (4*3*MAX_TRIANGLES_BATCH); i++) triangles.colors[i] = 0; triangles.vCounter = 0; @@ -1437,10 +1729,15 @@ static void InitializeBuffers(void) quads.vertices = (float *)malloc(sizeof(float)*3*4*MAX_QUADS_BATCH); // 3 float by vertex, 4 vertex by quad quads.texcoords = (float *)malloc(sizeof(float)*2*4*MAX_QUADS_BATCH); // 2 float by texcoord, 4 texcoord by quad quads.colors = (unsigned char *)malloc(sizeof(unsigned char)*4*4*MAX_QUADS_BATCH); // 4 float by color, 4 colors by quad +#if defined(GRAPHICS_API_OPENGL_33) quads.indices = (unsigned int *)malloc(sizeof(int)*6*MAX_QUADS_BATCH); // 6 int by quad (indices) +#elif defined(GRAPHICS_API_OPENGL_ES2) + quads.indices = (unsigned short *)malloc(sizeof(short)*6*MAX_QUADS_BATCH); // 6 int by quad (indices) +#endif + - for (int i = 0; i < (3*4*MAX_QUADS_BATCH); i++) quads.vertices[i] = 0.0; - for (int i = 0; i < (2*4*MAX_QUADS_BATCH); i++) quads.texcoords[i] = 0.0; + for (int i = 0; i < (3*4*MAX_QUADS_BATCH); i++) quads.vertices[i] = 0.0f; + for (int i = 0; i < (2*4*MAX_QUADS_BATCH); i++) quads.texcoords[i] = 0.0f; for (int i = 0; i < (4*4*MAX_QUADS_BATCH); i++) quads.colors[i] = 0; int k = 0; @@ -1461,14 +1758,19 @@ static void InitializeBuffers(void) quads.vCounter = 0; quads.tcCounter = 0; quads.cCounter = 0; + + TraceLog(INFO, "CPU buffers (lines, triangles, quads) initialized successfully"); } // Initialize Vertex Array Objects (Contain VBO) -static void InitializeVAOs(void) +static void InitializeBuffersGPU(void) { - // Initialize Lines VAO - glGenVertexArrays(1, &vaoLines); - glBindVertexArray(vaoLines); + if (vaoSupported) + { + // Initialize Lines VAO + glGenVertexArrays(1, &vaoLines); + glBindVertexArray(vaoLines); + } // Create buffers for our vertex data glGenBuffers(2, linesBuffer); @@ -1485,12 +1787,16 @@ static void InitializeVAOs(void) glEnableVertexAttribArray(colorLoc); glVertexAttribPointer(colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); - TraceLog(INFO, "[ID %i] Lines VAO initialized successfully", vaoLines); + if (vaoSupported) TraceLog(INFO, "[VAO ID %i] Lines VAO initialized successfully", vaoLines); + else TraceLog(INFO, "[VBO ID %i][VBO ID %i] Lines VBOs initialized successfully", linesBuffer[0], linesBuffer[1]); //-------------------------------------------------------------- - // Initialize Triangles VAO - glGenVertexArrays(1, &vaoTriangles); - glBindVertexArray(vaoTriangles); + if (vaoSupported) + { + // Initialize Triangles VAO + glGenVertexArrays(1, &vaoTriangles); + glBindVertexArray(vaoTriangles); + } // Create buffers for our vertex data glGenBuffers(2, trianglesBuffer); @@ -1506,12 +1812,16 @@ static void InitializeVAOs(void) glEnableVertexAttribArray(colorLoc); glVertexAttribPointer(colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); - TraceLog(INFO, "[ID %i] Triangles VAO initialized successfully", vaoTriangles); + if (vaoSupported) TraceLog(INFO, "[VAO ID %i] Triangles VAO initialized successfully", vaoTriangles); + else TraceLog(INFO, "[VBO ID %i][VBO ID %i] Triangles VBOs initialized successfully", trianglesBuffer[0], trianglesBuffer[1]); //-------------------------------------------------------------- - // Initialize Quads VAO (Buffer A) - glGenVertexArrays(1, &vaoQuads); - glBindVertexArray(vaoQuads); + if (vaoSupported) + { + // Initialize Quads VAO + glGenVertexArrays(1, &vaoQuads); + glBindVertexArray(vaoQuads); + } // Create buffers for our vertex data glGenBuffers(4, quadsBuffer); @@ -1534,19 +1844,24 @@ static void InitializeVAOs(void) // Fill index buffer glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quadsBuffer[3]); +#if defined(GRAPHICS_API_OPENGL_33) glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(int)*6*MAX_QUADS_BATCH, quads.indices, GL_STATIC_DRAW); +#elif defined(GRAPHICS_API_OPENGL_ES2) + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(short)*6*MAX_QUADS_BATCH, quads.indices, GL_STATIC_DRAW); +#endif - TraceLog(INFO, "[ID %i] Quads VAO initialized successfully", vaoQuads); + if (vaoSupported) TraceLog(INFO, "[VAO ID %i] Quads VAO initialized successfully", vaoQuads); + else TraceLog(INFO, "[VBO ID %i][VBO ID %i][VBO ID %i][VBO ID %i] Quads VBOs initialized successfully", quadsBuffer[0], quadsBuffer[1], quadsBuffer[2], quadsBuffer[3]); // Unbind the current VAO - glBindVertexArray(0); + if (vaoSupported) glBindVertexArray(0); } // Update VBOs with vertex array data static void UpdateBuffers(void) { // Activate Lines VAO - glBindVertexArray(vaoLines); + if (vaoSupported) glBindVertexArray(vaoLines); // Lines - vertex positions buffer glBindBuffer(GL_ARRAY_BUFFER, linesBuffer[0]); @@ -1561,7 +1876,7 @@ static void UpdateBuffers(void) //-------------------------------------------------------------- // Activate Triangles VAO - glBindVertexArray(vaoTriangles); + if (vaoSupported) glBindVertexArray(vaoTriangles); // Triangles - vertex positions buffer glBindBuffer(GL_ARRAY_BUFFER, trianglesBuffer[0]); @@ -1576,7 +1891,7 @@ static void UpdateBuffers(void) //-------------------------------------------------------------- // Activate Quads VAO - glBindVertexArray(vaoQuads); + if (vaoSupported) glBindVertexArray(vaoQuads); // Quads - vertex positions buffer glBindBuffer(GL_ARRAY_BUFFER, quadsBuffer[0]); @@ -1601,12 +1916,12 @@ static void UpdateBuffers(void) //-------------------------------------------------------------- // Unbind the current VAO - glBindVertexArray(0); + if (vaoSupported) glBindVertexArray(0); } -#endif //defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2) +#endif //defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) -#ifdef USE_OPENGL_11 +#if defined(GRAPHICS_API_OPENGL_11) // Mipmaps data is generated after image data static int GenerateMipmaps(unsigned char *data, int baseWidth, int baseHeight) @@ -1735,7 +2050,7 @@ static pixel *GenNextMipmap(pixel *srcData, int srcWidth, int srcHeight) #endif -#ifdef RLGL_STANDALONE +#if defined(RLGL_STANDALONE) typedef enum { INFO = 0, ERROR, WARNING, DEBUG, OTHER } TraceLogType; @@ -1,11 +1,11 @@ -/********************************************************************************************* +/********************************************************************************************** * * rlgl - raylib OpenGL abstraction layer * * raylib now uses OpenGL 1.1 style functions (rlVertex) that are mapped to selected OpenGL version: * OpenGL 1.1 - Direct map rl* -> gl* * OpenGL 3.3+ - Vertex data is stored in VAOs, call rlglDraw() to render -* OpenGL ES 2 - Same behaviour as OpenGL 3.3+ (NOT TESTED) +* OpenGL ES 2 - Same behaviour as OpenGL 3.3+ * * Copyright (c) 2014 Ramon Santamaria (Ray San - raysan@raysanweb.com) * @@ -39,16 +39,49 @@ #include "raymath.h" // Required for data type Matrix and Matrix functions // Select desired OpenGL version -//#define USE_OPENGL_11 -//#define USE_OPENGL_33 -//#define USE_OPENGL_ES2 +// NOTE: Those preprocessor defines are only used on rlgl module, +// if OpenGL version is required by any other module, it uses rlGetVersion() + +// Choose opengl version here or just define it at compile time: -DGRAPHICS_API_OPENGL_33 +//#define GRAPHICS_API_OPENGL_11 // Only available on PLATFORM_DESKTOP +//#define GRAPHICS_API_OPENGL_33 // Only available on PLATFORM_DESKTOP +//#define GRAPHICS_API_OPENGL_ES2 // Only available on PLATFORM_ANDROID or PLATFORM_RPI + +// Security check in case no GRAPHICS_API_OPENGL_* defined +#if !defined(GRAPHICS_API_OPENGL_11) && !defined(GRAPHICS_API_OPENGL_33) && !defined(GRAPHICS_API_OPENGL_ES2) + #define GRAPHICS_API_OPENGL_11 +#endif + +// Security check in case no GRAPHICS_API_OPENGL_* defined +#if !defined(GRAPHICS_API_OPENGL_11) && !defined(GRAPHICS_API_OPENGL_33) && !defined(GRAPHICS_API_OPENGL_ES2) + #define GRAPHICS_API_OPENGL_11 +#endif + +// Security check in case multiple GRAPHICS_API_OPENGL_* defined +#if defined(GRAPHICS_API_OPENGL_11) + #if defined(GRAPHICS_API_OPENGL_33) + #undef GRAPHICS_API_OPENGL_33 + #endif + + #if defined(GRAPHICS_API_OPENGL_ES2) + #undef GRAPHICS_API_OPENGL_ES2 + #endif +#endif //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- -#define MAX_LINES_BATCH 8192 // NOTE: Be careful with limits! -#define MAX_TRIANGLES_BATCH 4096 // NOTE: Be careful with limits! -#define MAX_QUADS_BATCH 8192 // NOTE: Be careful with limits! +#if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_33) + // NOTE: This is the maximum amount of lines, triangles and quads per frame, be careful! + #define MAX_LINES_BATCH 8192 + #define MAX_TRIANGLES_BATCH 4096 + #define MAX_QUADS_BATCH 4096 +#elif defined(GRAPHICS_API_OPENGL_ES2) + // NOTE: Reduce memory sizes for embedded systems (RPI) + #define MAX_LINES_BATCH 2048 // Critical for wire shapes (sphere) + #define MAX_TRIANGLES_BATCH 2048 // Critical for some shapes (sphere) + #define MAX_QUADS_BATCH 1024 // Be careful with text, every letter maps a quad +#endif //---------------------------------------------------------------------------------- // Types and Structures Definition @@ -59,6 +92,8 @@ typedef enum { RL_PROJECTION, RL_MODELVIEW, RL_TEXTURE } MatrixMode; typedef enum { RL_LINES, RL_TRIANGLES, RL_QUADS } DrawMode; +typedef enum { OPENGL_11 = 1, OPENGL_33, OPENGL_ES_20 } GlVersion; + #ifdef RLGL_STANDALONE typedef struct { int vertexCount; @@ -71,6 +106,7 @@ typedef enum { RL_LINES, RL_TRIANGLES, RL_QUADS } DrawMode; typedef struct Model { VertexData mesh; unsigned int vaoId; + unsigned int vboId[4]; unsigned int textureId; //Matrix transform; } Model; @@ -112,31 +148,32 @@ void rlColor4f(float x, float y, float z, float w); // Define one vertex (color) // Functions Declaration - OpenGL equivalent functions (common to 1.1, 3.3+, ES2) // NOTE: This functions are used to completely abstract raylib code from OpenGL layer //------------------------------------------------------------------------------------ -void rlEnableTexture(unsigned int id); // Enable texture usage -void rlDisableTexture(void); // Disable texture usage -void rlDeleteTextures(unsigned int id); // Delete OpenGL texture from GPU -void rlDeleteVertexArrays(unsigned int id); // Unload vertex data from GPU memory +void rlEnableTexture(unsigned int id); // Enable texture usage +void rlDisableTexture(void); // Disable texture usage +void rlDeleteTextures(unsigned int id); // Delete OpenGL texture from GPU +void rlDeleteVertexArrays(unsigned int id); // Unload vertex data (VAO) from GPU memory +void rlDeleteBuffers(unsigned int id); // Unload vertex data (VBO) from GPU memory void rlClearColor(byte r, byte g, byte b, byte a); // Clear color buffer with color -void rlClearScreenBuffers(void); // Clear used screen buffers (color and depth) +void rlClearScreenBuffers(void); // Clear used screen buffers (color and depth) +int rlGetVersion(void); // Returns current OpenGL version //------------------------------------------------------------------------------------ // Functions Declaration - rlgl functionality //------------------------------------------------------------------------------------ -#if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2) void rlglInit(void); // Initialize rlgl (shaders, VAO, VBO...) void rlglClose(void); // De-init rlgl -void rlglDraw(void); // Draw VAOs -unsigned int rlglLoadModel(VertexData mesh); +void rlglDraw(void); // Draw VAO/VBO +void rlglInitGraphics(int offsetX, int offsetY, int width, int height); // Initialize Graphics (OpenGL stuff) + +unsigned int rlglLoadTexture(unsigned char *data, int width, int height, bool genMipmaps); // Load in GPU OpenGL texture unsigned int rlglLoadCompressedTexture(unsigned char *data, int width, int height, int mipmapCount, int format); -#endif +Model rlglLoadModel(VertexData mesh); // Upload vertex data into GPU and provided VAO/VBO ids void rlglDrawModel(Model model, Vector3 position, Vector3 rotation, Vector3 scale, Color color, bool wires); -void rlglInitGraphicsDevice(int fbWidth, int fbHeight); // Initialize Graphics Device (OpenGL stuff) -unsigned int rlglLoadTexture(unsigned char *data, int width, int height, bool genMipmaps); // Load in GPU OpenGL texture byte *rlglReadScreenPixels(int width, int height); // Read screen pixel data (color buffer) -#if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2) +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) void PrintProjectionMatrix(void); // DEBUG: Print projection matrix void PrintModelviewMatrix(void); // DEBUG: Print modelview matrix #endif diff --git a/src/shapes.c b/src/shapes.c index 17210f21..6fa26bee 100644 --- a/src/shapes.c +++ b/src/shapes.c @@ -1,10 +1,10 @@ -/********************************************************************************************* +/********************************************************************************************** * * raylib.shapes * * Basic functions to draw 2d Shapes and check collisions * -* Copyright (c) 2013 Ramon Santamaria (Ray San - raysan@raysanweb.com) +* Copyright (c) 2014 Ramon Santamaria (Ray San - raysan@raysanweb.com) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. @@ -31,11 +31,6 @@ #include "rlgl.h" // raylib OpenGL abstraction layer to OpenGL 1.1, 3.3+ or ES2 -// Security check in case no USE_OPENGL_* defined -#if !defined(USE_OPENGL_11) && !defined(USE_OPENGL_33) && !defined(USE_OPENGL_ES2) - #define USE_OPENGL_11 -#endif - //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- @@ -185,43 +180,44 @@ void DrawRectangleGradient(int posX, int posY, int width, int height, Color colo // Draw a color-filled rectangle (Vector version) void DrawRectangleV(Vector2 position, Vector2 size, Color color) { -#ifdef USE_OPENGL_11 - rlBegin(RL_TRIANGLES); - rlColor4ub(color.r, color.g, color.b, color.a); - - rlVertex2i(position.x, position.y); - rlVertex2i(position.x, position.y + size.y); - rlVertex2i(position.x + size.x, position.y + size.y); + if (rlGetVersion() == OPENGL_11) + { + rlBegin(RL_TRIANGLES); + rlColor4ub(color.r, color.g, color.b, color.a); - rlVertex2i(position.x, position.y); - rlVertex2i(position.x + size.x, position.y + size.y); - rlVertex2i(position.x + size.x, position.y); - rlEnd(); -#endif + rlVertex2i(position.x, position.y); + rlVertex2i(position.x, position.y + size.y); + rlVertex2i(position.x + size.x, position.y + size.y); -#if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2) - // NOTE: This shape uses QUADS to avoid drawing order issues (view rlglDraw) - rlEnableTexture(1); // Default white texture + rlVertex2i(position.x, position.y); + rlVertex2i(position.x + size.x, position.y + size.y); + rlVertex2i(position.x + size.x, position.y); + rlEnd(); + } + else if ((rlGetVersion() == OPENGL_33) || (rlGetVersion() == OPENGL_ES_20)) + { + // NOTE: This shape uses QUADS to avoid drawing order issues (view rlglDraw) + rlEnableTexture(1); // Default white texture - rlBegin(RL_QUADS); - rlColor4ub(color.r, color.g, color.b, color.a); - rlNormal3f(0.0f, 0.0f, 1.0f); // Normal Pointing Towards Viewer + rlBegin(RL_QUADS); + rlColor4ub(color.r, color.g, color.b, color.a); + rlNormal3f(0.0f, 0.0f, 1.0f); // Normal Pointing Towards Viewer - rlTexCoord2f(0.0f, 0.0f); - rlVertex2f(position.x, position.y); + rlTexCoord2f(0.0f, 0.0f); + rlVertex2f(position.x, position.y); - rlTexCoord2f(0.0f, 1.0f); - rlVertex2f(position.x, position.y + size.y); + rlTexCoord2f(0.0f, 1.0f); + rlVertex2f(position.x, position.y + size.y); - rlTexCoord2f(1.0f, 1.0f); - rlVertex2f(position.x + size.x, position.y + size.y); + rlTexCoord2f(1.0f, 1.0f); + rlVertex2f(position.x + size.x, position.y + size.y); - rlTexCoord2f(1.0f, 0.0f); - rlVertex2f(position.x + size.x, position.y); - rlEnd(); + rlTexCoord2f(1.0f, 0.0f); + rlVertex2f(position.x + size.x, position.y); + rlEnd(); - rlDisableTexture(); -#endif + rlDisableTexture(); + } } // Draw rectangle outline @@ -457,4 +453,4 @@ Rectangle GetCollisionRec(Rectangle rec1, Rectangle rec2) } return retRec; -} +}
\ No newline at end of file diff --git a/src/stb_image.c b/src/stb_image.c index b11e44ff..b9e1b304 100644 --- a/src/stb_image.c +++ b/src/stb_image.c @@ -151,14 +151,15 @@ static stbi_uc *stbi_tga_load(stbi *s, int *x, int *y, int *comp, int req_comp); static int stbi_tga_info(stbi *s, int *x, int *y, int *comp); static int stbi_psd_test(stbi *s); static stbi_uc *stbi_psd_load(stbi *s, int *x, int *y, int *comp, int req_comp); -static int stbi_hdr_test(stbi *s); -static float *stbi_hdr_load(stbi *s, int *x, int *y, int *comp, int req_comp); static int stbi_pic_test(stbi *s); static stbi_uc *stbi_pic_load(stbi *s, int *x, int *y, int *comp, int req_comp); static int stbi_gif_test(stbi *s); static stbi_uc *stbi_gif_load(stbi *s, int *x, int *y, int *comp, int req_comp); static int stbi_gif_info(stbi *s, int *x, int *y, int *comp); +// RAY: Commented because not used +//static int stbi_hdr_test(stbi *s); +//static float *stbi_hdr_load(stbi *s, int *x, int *y, int *comp, int req_comp); // this is not threadsafe static const char *failure_reason; @@ -2619,7 +2620,7 @@ static int shiftsigned(int v, int shift, int bits) static stbi_uc *bmp_load(stbi *s, int *x, int *y, int *comp, int req_comp) { uint8 *out; - unsigned int mr=0,mg=0,mb=0,ma=0, fake_a=0; + unsigned int mr=0,mg=0,mb=0,ma=0; //fake_a=0; stbi_uc pal[256][4]; int psize=0,i,j,compress=0,width; int bpp, flip_vertically, pad, target, offset, hsz; @@ -2668,7 +2669,7 @@ static stbi_uc *bmp_load(stbi *s, int *x, int *y, int *comp, int req_comp) mg = 0xffu << 8; mb = 0xffu << 0; ma = 0xffu << 24; - fake_a = 1; // @TODO: check for cases like alpha value is all 0 and switch it to 255 + //fake_a = 1; // @TODO: check for cases like alpha value is all 0 and switch it to 255 } else { mr = 31u << 10; mg = 31u << 5; diff --git a/src/stb_image.h b/src/stb_image.h index 900e0c20..0eeece13 100644 --- a/src/stb_image.h +++ b/src/stb_image.h @@ -183,7 +183,6 @@ // The three functions you must define are "read" (reads some bytes of data), // "skip" (skips some bytes of data), "eof" (reports if the stream is at the end). - #define STBI_NO_HDR // RaySan: not required by raylib #ifndef STBI_NO_STDIO @@ -195,6 +194,11 @@ #include <stdio.h> #endif +// NOTE: Added to work with raylib on Android +#if defined(PLATFORM_ANDROID) + #include "utils.h" // Android fopen function map +#endif + #define STBI_VERSION 1 enum diff --git a/src/stb_vorbis.h b/src/stb_vorbis.h index 2d7b61e5..bff27496 100644 --- a/src/stb_vorbis.h +++ b/src/stb_vorbis.h @@ -37,6 +37,11 @@ #include <stdio.h> #endif +// NOTE: Added to work with raylib on Android +#if defined(PLATFORM_ANDROID) + #include "utils.h" // Android fopen function map +#endif + #ifdef __cplusplus extern "C" { #endif @@ -1,13 +1,10 @@ -/********************************************************************************************* +/********************************************************************************************** * * raylib.text * * Basic functions to load SpriteFonts and draw Text * -* Uses external lib: -* stb_image - Multiple formats image loading (JPEG, PNG, BMP, TGA, PSD, GIF, HDR, PIC) -* -* Copyright (c) 2013 Ramon Santamaria (Ray San - raysan@raysanweb.com) +* Copyright (c) 2014 Ramon Santamaria (Ray San - raysan@raysanweb.com) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. @@ -31,10 +28,9 @@ #include <stdlib.h> // Declares malloc() and free() for memory management #include <string.h> // String management functions (just strlen() is used) #include <stdarg.h> // Used for functions with variable number of parameters (FormatText()) -#include "stb_image.h" // Used to read image data (multiple formats support) +#include <stdio.h> // Standard input / output lib #include "rlgl.h" // raylib OpenGL abstraction layer to OpenGL 1.1, 3.3+ or ES2 - #include "utils.h" // Required for function GetExtendion() //---------------------------------------------------------------------------------- @@ -79,6 +75,9 @@ static int ParseImageData(Color *imgDataPixel, int imgWidth, int imgHeight, Char static int GetNextPOT(int num); // Calculate next power-of-two value for a given value static SpriteFont LoadRBMF(const char *fileName); // Load a rBMF font file (raylib BitMap Font) +extern void LoadDefaultFont(void); +extern void UnloadDefaultFont(void); + //---------------------------------------------------------------------------------- // Module Functions Definition //---------------------------------------------------------------------------------- @@ -188,53 +187,29 @@ extern void UnloadDefaultFont(void) } // Get the default font, useful to be used with extended parameters -SpriteFont GetDefaultFont(void) +SpriteFont GetDefaultFont() { return defaultFont; } // Load a SpriteFont image into GPU memory -SpriteFont LoadSpriteFont(const char* fileName) +SpriteFont LoadSpriteFont(const char *fileName) { SpriteFont spriteFont; - Image image; - // Check file extension if (strcmp(GetExtension(fileName),"rbmf") == 0) spriteFont = LoadRBMF(fileName); else { - // Use stb_image to load image data! - int imgWidth; - int imgHeight; - int imgBpp; - - byte *imgData = stbi_load(fileName, &imgWidth, &imgHeight, &imgBpp, 4); // Force loading to 4 components (RGBA) - - // Convert array to pixel array for working convenience - Color *imgDataPixel = (Color *)malloc(imgWidth * imgHeight * sizeof(Color)); - Color *imgDataPixelPOT = NULL; - - int pix = 0; - - for (int i = 0; i < (imgWidth * imgHeight * 4); i += 4) - { - imgDataPixel[pix].r = imgData[i]; - imgDataPixel[pix].g = imgData[i+1]; - imgDataPixel[pix].b = imgData[i+2]; - imgDataPixel[pix].a = imgData[i+3]; - pix++; - } - - stbi_image_free(imgData); + Image image = LoadImage(fileName); // At this point we have a pixel array with all the data... - TraceLog(INFO, "[%s] SpriteFont image loaded: %i x %i", fileName, imgWidth, imgHeight); + TraceLog(INFO, "[%s] SpriteFont image loaded: %i x %i", fileName, image.width, image.height); // Process bitmap Font pixel data to get measures (Character array) // spriteFont.charSet data is filled inside the function and memory is allocated! - int numChars = ParseImageData(imgDataPixel, imgWidth, imgHeight, &spriteFont.charSet); + int numChars = ParseImageData(image.pixels, image.width, image.height, &spriteFont.charSet); TraceLog(INFO, "[%s] SpriteFont data parsed correctly", fileName); TraceLog(INFO, "[%s] SpriteFont num chars detected: %i", fileName, numChars); @@ -242,13 +217,17 @@ SpriteFont LoadSpriteFont(const char* fileName) spriteFont.numChars = numChars; // Convert image font to POT image before conversion to texture + // NOTE: Not required, we skip this step +/* // Just add the required amount of pixels at the right and bottom sides of image... - int potWidth = GetNextPOT(imgWidth); - int potHeight = GetNextPOT(imgHeight); + int potWidth = GetNextPOT(image.width); + int potHeight = GetNextPOT(image.height); // Check if POT texture generation is required (if texture is not already POT) - if ((potWidth != imgWidth) || (potHeight != imgHeight)) + if ((potWidth != image.width) || (potHeight != image.height)) { + Color *imgDataPixelPOT = NULL; + // Generate POT array from NPOT data imgDataPixelPOT = (Color *)malloc(potWidth * potHeight * sizeof(Color)); @@ -256,20 +235,20 @@ SpriteFont LoadSpriteFont(const char* fileName) { for (int i = 0; i < potWidth; i++) { - if ((j < imgHeight) && (i < imgWidth)) imgDataPixelPOT[j*potWidth + i] = imgDataPixel[j*imgWidth + i]; + if ((j < image.height) && (i < image.width)) imgDataPixelPOT[j*potWidth + i] = image.pixels[j*image.width + i]; else imgDataPixelPOT[j*potWidth + i] = MAGENTA; } } TraceLog(WARNING, "SpriteFont texture converted to POT: %ix%i", potWidth, potHeight); - } - free(imgDataPixel); - - image.pixels = imgDataPixelPOT; - image.width = potWidth; - image.height = potHeight; + free(image.pixels); + image.pixels = imgDataPixelPOT; + image.width = potWidth; + image.height = potHeight; + } +*/ spriteFont.texture = CreateTexture(image, false); // Convert loaded image to OpenGL texture UnloadImage(image); } @@ -287,7 +266,7 @@ void UnloadSpriteFont(SpriteFont spriteFont) // Draw text (using default font) // NOTE: fontSize work like in any drawing program but if fontSize is lower than font-base-size, then font-base-size is used // NOTE: chars spacing is proportional to fontSize -void DrawText(const char* text, int posX, int posY, int fontSize, Color color) +void DrawText(const char *text, int posX, int posY, int fontSize, Color color) { Vector2 position = { (float)posX, (float)posY }; @@ -303,7 +282,7 @@ void DrawText(const char* text, int posX, int posY, int fontSize, Color color) // Draw text using SpriteFont // NOTE: If font size is lower than base size, base size is used // NOTE: chars spacing is NOT proportional to fontSize -void DrawTextEx(SpriteFont spriteFont, const char* text, Vector2 position, int fontSize, int spacing, Color tint) +void DrawTextEx(SpriteFont spriteFont, const char *text, Vector2 position, int fontSize, int spacing, Color tint) { int length = strlen(text); int positionX = (int)position.x; @@ -398,12 +377,14 @@ int GetFontBaseSize(SpriteFont spriteFont) // NOTE: Uses default font void DrawFPS(int posX, int posY) { + char buffer[20]; + // NOTE: We are rendering fps every second for better viewing on high framerates - static float fps; - static int counter = 0; - static int refreshRate = 0; + // TODO: Not working properly on ANDROID and RPI - char buffer[20]; + static float fps = 0.0f; + static int counter = 0; + static int refreshRate = 20; if (counter < refreshRate) { diff --git a/src/textures.c b/src/textures.c index 39947d12..e342e21b 100644 --- a/src/textures.c +++ b/src/textures.c @@ -1,4 +1,4 @@ -/********************************************************************************************* +/********************************************************************************************** * * raylib.textures * @@ -6,8 +6,9 @@ * * Uses external lib: * stb_image - Multiple formats image loading (JPEG, PNG, BMP, TGA, PSD, GIF, PIC) +* NOTE: stb_image has been slightly modified, original library: https://github.com/nothings/stb * -* Copyright (c) 2013 Ramon Santamaria (Ray San - raysan@raysanweb.com) +* Copyright (c) 2014 Ramon Santamaria (Ray San - raysan@raysanweb.com) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. @@ -30,15 +31,12 @@ #include <stdlib.h> // Declares malloc() and free() for memory management #include <string.h> // Required for strcmp(), strrchr(), strncmp() -#include "stb_image.h" // Used to read image data (multiple formats support) -#include "utils.h" // rRES data decompression utility function #include "rlgl.h" // raylib OpenGL abstraction layer to OpenGL 1.1, 3.3+ or ES2 +#include "utils.h" // rRES data decompression utility function + // NOTE: Includes Android fopen function map -// Security check in case no USE_OPENGL_* defined -#if !defined(USE_OPENGL_11) && !defined(USE_OPENGL_33) && !defined(USE_OPENGL_ES2) - #define USE_OPENGL_11 -#endif +#include "stb_image.h" // Used to read image data (multiple formats support) //---------------------------------------------------------------------------------- // Defines and Macros @@ -73,7 +71,8 @@ typedef struct { //---------------------------------------------------------------------------------- // Module specific Functions Declaration //---------------------------------------------------------------------------------- -static ImageEx LoadDDS(const char *fileName); +static ImageEx LoadDDS(const char *fileName); // Load DDS file +static ImageEx LoadPKM(const char *fileName); // Load PKM file //---------------------------------------------------------------------------------- // Module Functions Definition @@ -155,14 +154,18 @@ Image LoadImage(const char *fileName) free(imageDDS.data); - TraceLog(INFO, "[%s] Image loaded successfully", fileName); + TraceLog(INFO, "[%s] DDS Image loaded successfully (uncompressed, no mipmaps)", fileName); } - else TraceLog(WARNING, "[%s] Compressed image data could not be loaded", fileName); + else TraceLog(WARNING, "[%s] DDS Compressed image data could not be loaded", fileName); + } + else if (strcmp(GetExtension(fileName),"pkm") == 0) + { + TraceLog(INFO, "[%s] PKM Compressed image data could not be loaded", fileName); } else TraceLog(WARNING, "[%s] Image extension not recognized, it can't be loaded", fileName); // ALTERNATIVE: We can load pixel data directly into Color struct pixels array, - // to do that struct data alignment should be the right one (4 byte); it is. + // to do that, struct data alignment should be the right one (4 byte); it is. //image.pixels = stbi_load(fileName, &imgWidth, &imgHeight, &imgBpp, 4); return image; @@ -302,9 +305,7 @@ Texture2D LoadTexture(const char *fileName) } else { -#if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2) texture.id = rlglLoadCompressedTexture(image.data, image.width, image.height, image.mipmaps, image.compFormat); -#endif } texture.width = image.width; @@ -315,6 +316,20 @@ Texture2D LoadTexture(const char *fileName) free(image.data); } + else if (strcmp(GetExtension(fileName),"pkm") == 0) + { + ImageEx image = LoadPKM(fileName); + + texture.id = rlglLoadCompressedTexture(image.data, image.width, image.height, image.mipmaps, image.compFormat); + + texture.width = image.width; + texture.height = image.height; + + if (texture.id == 0) TraceLog(WARNING, "[%s] PKM texture could not be loaded", fileName); + else TraceLog(INFO, "[%s] PKM texture loaded successfully", fileName); + + free(image.data); + } else { Image image = LoadImage(fileName); @@ -453,8 +468,6 @@ Texture2D CreateTexture(Image image, bool genMipmaps) texture.width = image.width; texture.height = image.height; - TraceLog(INFO, "[ID %i] Texture created successfully", texture.id); - free(imgData); } else TraceLog(WARNING, "Texture could not be created, image data is not valid"); @@ -471,15 +484,15 @@ ImageEx LoadDDS(const char *fileName) #define FOURCC_DXT5 0x35545844 // Equivalent to "DXT5" in ASCII #ifndef GL_COMPRESSED_RGBA_S3TC_DXT1_EXT - #define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 + #define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 #endif #ifndef GL_COMPRESSED_RGBA_S3TC_DXT3_EXT - #define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2 + #define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2 #endif #ifndef GL_COMPRESSED_RGBA_S3TC_DXT5_EXT - #define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 + #define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 #endif // DDS Pixel Format @@ -582,11 +595,9 @@ ImageEx LoadDDS(const char *fileName) } else if ((header.ddspf.flags == 0x04) && (header.ddspf.fourCC > 0)) { -#ifdef USE_OPENGL_11 - TraceLog(WARNING, "[%s] DDS image uses compression, not supported by current OpenGL version", fileName); + TraceLog(WARNING, "[%s] DDS image uses compression, not supported on OpenGL 1.1", fileName); TraceLog(WARNING, "[%s] DDS compressed files require OpenGL 3.2+ or ES 2.0", fileName); - fclose(ddsFile); -#else + int bufsize; // Calculate data size, including all mipmaps @@ -614,10 +625,96 @@ ImageEx LoadDDS(const char *fileName) // NOTE: Image num color components not required... for now... //if (fourCC == FOURCC_DXT1) image.components = 3; //else image.components = 4; -#endif } } } return image; +} + +// Loading PKM image data (ETC1/ETC2 compression) +// NOTE: KTX is the standard Khronos Group compression format (ETC1/ETC2, mipmaps) +// PKM is a much simpler file format used mainly to contain a single ETC1/ETC2 compressed image (no mipmaps) +ImageEx LoadPKM(const char *fileName) +{ + // If OpenGL ES 2.0. the following format could be supported (ETC1): + //GL_ETC1_RGB8_OES + + #ifndef GL_ETC1_RGB8_OES + #define GL_ETC1_RGB8_OES 0x8D64 + #endif + + // If OpenGL ES 3.0, the following formats are supported (ETC2/EAC): + //GL_COMPRESSED_RGB8_ETC2 + //GL_COMPRESSED_RGBA8_ETC2 + //GL_COMPRESSED_RG11_EAC + //... + + // PKM file (ETC1) Header (16 bytes) + typedef struct { + char id[4]; // "PKM " + char version[2]; // "10" + unsigned short format; // Format = number of mipmaps = 0 (ETC1_RGB_NO_MIPMAPS) + unsigned short extWidth; // Texture width (big-endian) + unsigned short extHeight; // Texture height (big-endian) + unsigned short origWidth; // Original width (big-endian) + unsigned short origHeight; // Original height (big-endian) + } pkmHeader; + + // NOTE: The extended width and height are the widths rounded up to a multiple of 4. + // NOTE: ETC is always 4bit per pixel (64 bits for each 4x4 block of pixels) + + // Bytes Swap (little-endian <-> big-endian) + //unsigned short data; + //unsigned short swap = ((data & 0x00FF) << 8) | ((data & 0xFF00) >> 8); + + ImageEx image; + + unsigned short width; + unsigned short height; + unsigned short useless; + + FILE *pkmFile = fopen(fileName, "rb"); + + if (pkmFile == NULL) + { + TraceLog(WARNING, "[%s] PKM File could not be opened", fileName); + } + else + { + // Verify the type of file + char filecode[4]; + + fread(filecode, 1, 4, pkmFile); + + if (strncmp(filecode, "PKM ", 4) != 0) + { + TraceLog(WARNING, "[%s] PKM File does not seem to be valid", fileName); + fclose(pkmFile); + } + else + { + // Get the surface descriptor + fread(&useless, sizeof(unsigned short), 1, pkmFile); // Discard version + fread(&useless, sizeof(unsigned short), 1, pkmFile); // Discard format + + fread(&width, sizeof(unsigned short), 1, pkmFile); // Read extended width + fread(&height, sizeof(unsigned short), 1, pkmFile); // Read extended height + + int size = (width/4)*(height/4)*8; // Total data size in bytes + + image.data = (unsigned char*)malloc(size * sizeof(unsigned char)); + + fread(image.data, 1, size, pkmFile); + + fclose(pkmFile); // Close file pointer + + image.width = width; + image.height = height; + image.mipmaps = 1; + image.compFormat = GL_ETC1_RGB8_OES; + } + } + + return image; }
\ No newline at end of file diff --git a/src/utils.c b/src/utils.c index 254cc955..b5112111 100644 --- a/src/utils.c +++ b/src/utils.c @@ -1,4 +1,4 @@ -/********************************************************************************************* +/********************************************************************************************** * * raylib.utils * @@ -8,7 +8,7 @@ * tinfl - zlib DEFLATE algorithm decompression lib * stb_image_write - PNG writting functions * -* Copyright (c) 2013 Ramon Santamaria (Ray San - raysan@raysanweb.com) +* Copyright (c) 2014 Ramon Santamaria (Ray San - raysan@raysanweb.com) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. @@ -29,20 +29,40 @@ #include "utils.h" +#if defined(PLATFORM_ANDROID) + #include <errno.h> + #include <android/log.h> + #include <android/asset_manager.h> +#endif + #include <stdlib.h> // malloc(), free() #include <stdio.h> // printf(), fprintf() #include <stdarg.h> // Used for functions with variable number of parameters (TraceLog()) //#include <string.h> // String management functions: strlen(), strrchr(), strcmp() -#define STB_IMAGE_WRITE_IMPLEMENTATION -#include "stb_image_write.h" // Create PNG file +#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) + #define STB_IMAGE_WRITE_IMPLEMENTATION + #include "stb_image_write.h" // Create PNG file +#endif #include "tinfl.c" //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- -static FILE *logstream = NULL; +#if defined(PLATFORM_ANDROID) +AAssetManager *assetManager; +#endif + +//---------------------------------------------------------------------------------- +// Module specific Functions Declaration +//---------------------------------------------------------------------------------- +#if defined(PLATFORM_ANDROID) +static int android_read(void *cookie, char *buf, int size); +static int android_write(void *cookie, const char *buf, int size); +static fpos_t android_seek(void *cookie, fpos_t offset, int whence); +static int android_close(void *cookie); +#endif //---------------------------------------------------------------------------------- // Module Functions Definition - Utilities @@ -87,6 +107,7 @@ unsigned char *DecompressData(const unsigned char *data, unsigned long compSize, return pUncomp; } +#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) // Creates a bitmap (BMP) file from an array of pixel data // NOTE: This function is not explicitly available to raylib users void WriteBitmap(const char *fileName, unsigned char *imgData, int width, int height) @@ -148,52 +169,90 @@ void TraceLog(int msgType, const char *text, ...) traceDebugMsgs = 0; #endif - // NOTE: If trace log file not set, output redirected to stdout - if (logstream == NULL) logstream = stdout; - switch(msgType) { - case INFO: fprintf(logstream, "INFO: "); break; - case ERROR: fprintf(logstream, "ERROR: "); break; - case WARNING: fprintf(logstream, "WARNING: "); break; - case DEBUG: if (traceDebugMsgs) fprintf(logstream, "DEBUG: "); break; + case INFO: fprintf(stdout, "INFO: "); break; + case ERROR: fprintf(stdout, "ERROR: "); break; + case WARNING: fprintf(stdout, "WARNING: "); break; + case DEBUG: if (traceDebugMsgs) fprintf(stdout, "DEBUG: "); break; default: break; } if ((msgType != DEBUG) || ((msgType == DEBUG) && (traceDebugMsgs))) { va_start(args, text); - vfprintf(logstream, text, args); + vfprintf(stdout, text, args); va_end(args); - fprintf(logstream, "\n"); + fprintf(stdout, "\n"); } if (msgType == ERROR) exit(1); // If ERROR message, exit program } +#endif -// Open a trace log file (if desired) -void TraceLogOpen(const char *logFileName) +#if defined(PLATFORM_ANDROID) +void TraceLog(int msgType, const char *text, ...) { - // stdout redirected to stream file - FILE *logstream = fopen(logFileName, "w"); + static char buffer[100]; + + switch(msgType) + { + case INFO: strcpy(buffer, "INFO: "); break; + case ERROR: strcpy(buffer, "ERROR: "); break; + case WARNING: strcpy(buffer, "WARNING: "); break; + case DEBUG: strcpy(buffer, "DEBUG: "); break; + default: break; + } + + strcat(buffer, text); + strcat(buffer, "\n"); + + va_list args; + va_start(args, buffer); + + switch(msgType) + { + case INFO: __android_log_vprint(ANDROID_LOG_INFO, "raylib", buffer, args); break; + case ERROR: __android_log_vprint(ANDROID_LOG_ERROR, "raylib", buffer, args); break; + case WARNING: __android_log_vprint(ANDROID_LOG_WARN, "raylib", buffer, args); break; + case DEBUG: __android_log_vprint(ANDROID_LOG_DEBUG, "raylib", buffer, args); break; + default: break; + } + + va_end(args); + + if (msgType == ERROR) exit(1); +} - if (logstream == NULL) TraceLog(WARNING, "Unable to open log file"); +// Initialize asset manager from android app +void InitAssetManager(AAssetManager *manager) +{ + assetManager = manager; } -// Close the trace log file -void TraceLogClose() +// Replacement for fopen +FILE *android_fopen(const char *fileName, const char *mode) { - if (logstream != NULL) fclose(logstream); + if (mode[0] == 'w') return NULL; + + AAsset *asset = AAssetManager_open(assetManager, fileName, 0); + + if(!asset) return NULL; + + return funopen(asset, android_read, android_write, android_seek, android_close); } +#endif // Keep track of memory allocated // NOTE: mallocType defines the type of data allocated +/* void RecordMalloc(int mallocType, int mallocSize, const char *msg) { // TODO: Investigate how to record memory allocation data... // Maybe creating my own malloc function... } +*/ // Get the extension for a filename const char *GetExtension(const char *fileName) @@ -203,3 +262,30 @@ const char *GetExtension(const char *fileName) return (dot + 1); } +//---------------------------------------------------------------------------------- +// Module specific Functions Definition +//---------------------------------------------------------------------------------- +#if defined(PLATFORM_ANDROID) +static int android_read(void *cookie, char *buf, int size) +{ + return AAsset_read((AAsset *)cookie, buf, size); +} + +static int android_write(void *cookie, const char *buf, int size) +{ + TraceLog(ERROR, "Can't provide write access to the APK"); + + return EACCES; +} + +static fpos_t android_seek(void *cookie, fpos_t offset, int whence) +{ + return AAsset_seek((AAsset *)cookie, offset, whence); +} + +static int android_close(void *cookie) +{ + AAsset_close((AAsset *)cookie); + return 0; +} +#endif diff --git a/src/utils.h b/src/utils.h index ac8d55b1..784c7926 100644 --- a/src/utils.h +++ b/src/utils.h @@ -1,10 +1,10 @@ -/********************************************************************************************* +/********************************************************************************************** * * raylib.utils * * Some utility functions: rRES files data decompression * -* Copyright (c) 2013 Ramon Santamaria (Ray San - raysan@raysanweb.com) +* Copyright (c) 2014 Ramon Santamaria (Ray San - raysan@raysanweb.com) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. @@ -26,11 +26,20 @@ #ifndef UTILS_H #define UTILS_H +#if defined(PLATFORM_ANDROID) + #include <stdio.h> // Defines FILE struct + #include <android/asset_manager.h> // defines AAssetManager struct +#endif + //---------------------------------------------------------------------------------- // Some basic Defines //---------------------------------------------------------------------------------- #define DO_NOT_TRACE_DEBUG_MSGS // Use this define to avoid DEBUG tracing +#if defined(PLATFORM_ANDROID) + #define fopen(name, mode) android_fopen(name, mode) +#endif + //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- @@ -61,14 +70,18 @@ extern "C" { // Prevents name mangling of functions //---------------------------------------------------------------------------------- unsigned char *DecompressData(const unsigned char *data, unsigned long compSize, int uncompSize); +#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) void WriteBitmap(const char *fileName, unsigned char *imgData, int width, int height); void WritePNG(const char *fileName, unsigned char *imgData, int width, int height); +#endif void TraceLog(int msgType, const char *text, ...); // Outputs a trace log message -void TraceLogOpen(const char *logFileName); // Open a trace log file (if desired) -void TraceLogClose(); // Close the trace log file +const char *GetExtension(const char *fileName); // Returns extension of a filename -const char *GetExtension(const char *fileName); +#if defined(PLATFORM_ANDROID) +void InitAssetManager(AAssetManager *manager); // Initialize asset manager from android app +FILE *android_fopen(const char *fileName, const char *mode); // Replacement for fopen() +#endif #ifdef __cplusplus } |
