From fc6081fe70ab7c3b037c0ab9f38478904d3cdde2 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Tue, 16 Sep 2014 22:51:31 +0200 Subject: [PATCH] raylib 1.2 This is a huge update. Check CHANGELOG for details --- CHANGELOG | 32 +- README.md | 166 ++++-- examples/makefile | 193 ++++++ src/audio.c | 65 +- src/core.c | 1448 +++++++++++++++++++++++++++++++++++++-------- src/makefile | 130 ++++ src/models.c | 76 ++- src/raylib.h | 68 ++- src/raymath.c | 41 +- src/raymath.h | 10 +- src/resources | Bin 0 -> 107204 bytes src/rlgl.c | 715 +++++++++++++++------- src/rlgl.h | 79 ++- src/shapes.c | 70 ++- src/stb_image.c | 9 +- src/stb_image.h | 6 +- src/stb_vorbis.h | 5 + src/text.c | 89 ++- src/textures.c | 145 ++++- src/utils.c | 130 +++- src/utils.h | 23 +- 21 files changed, 2742 insertions(+), 758 deletions(-) create mode 100644 examples/makefile create mode 100644 src/makefile create mode 100644 src/resources diff --git a/CHANGELOG b/CHANGELOG index 4e2797825..03dd14fc1 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,11 +1,41 @@ changelog --------- -Current Release: raylib 1.1.1 (22 July 2014) +Current Release: raylib 1.2 (16 September 2014) NOTE: Only versions marked as 'Release' are available on release folder, updates are only available as source. NOTE: Current Release includes all previous updates. +----------------------------------------------- +Release: raylib 1.2 (16 September 2014) +----------------------------------------------- +NOTE: + This version supposed a complete redesign of the [core] module to support Android and Raspberry Pi. + Multiples modules have also been tweaked to accomodate to the new platforms, specially [rlgl] + +[core] Added multiple platforms support: Android and Raspberry Pi +[core] InitWindow() - Complete rewrite and split for Android +[core] InitDisplay() - Internal function added to calculate proper display size +[core] InitGraphics() - Internal function where OpenGL graphics are initialized +[core] Complete refactoring of input functions to accomodate to new platforms +[core] Mouse and Keyboard raw data reading functions added for Raspberry Pi +[core] GetTouchX(), GetTouchY() - Added for Android +[core] Added Android callbacks to process inputs and Android activity commands +[rlgl] Adjusted buffers depending on platform +[rlgl] Added security check in case deployed vertex excess buffer size +[rlgl] Adjusted indices type depending on GL version (int or short) +[rlgl] Fallback to VBOs only usage if VAOs not supported on ES2 +[rlgl] rlglLoadModel() stores vbo ids on new Model struct +[textures] Added support for PKM files (ETC1, ETC2 compression support) +[shapes] DrawRectangleV() - Modified, depending on OGL version uses TRIANGLES or QUADS +[text] LoadSpriteFont() - Modified to use LoadImage() +[models] Minor changes on models loading to accomodate to new Model struct +[audio] PauseMusicStream(), ResumeMusicStream() - Added +[audio] Reduced music buffer size to avoid stalls on Raspberry Pi +[src] Added makefile for Windows and RPI +[src] Added resources file (raylib icon and executable info) +[examples] Added makefile for Windows and RPI + ----------------------------------------------- Release: raylib 1.1.1 (22 July 2014) ----------------------------------------------- diff --git a/README.md b/README.md index 7da8f1a6a..eae4f20ea 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ about raylib is a simple and easy-to-use library to learn videogames programming. -raylib is highly inspired by Borland BGI graphics lib (more specifically WinBGI) and by XNA framework. +raylib is highly inspired by Borland BGI graphics lib and by XNA framework. Allegro and SDL have also been analyzed for reference. Want to see how easy is making games with raylib? Jump to [code examples!] (http://www.raylib.com/examples.htm) @@ -55,6 +55,23 @@ Lots of code changes and lot of testing have concluded in this amazing new rayli Enjoy it. +notes on raylib 1.2 +------------------- + +On September 2014, after 5 month of raylib 1.1 release, it comes raylib 1.2. Again, this version presents a +complete internal redesign of [core] (https://github.com/raysan5/raylib/blob/master/src/core.h) module to support two new platforms: Android and Raspberry Pi. + +It's been some month of really hard work to accomodate raylib to those new platforms while keeping it easy for the user. +On Android, raylib manages internally the activity cicle, as well as the inputs; on Raspberry Pi, a complete raw input +system has been written from scratch. + +A new display initialization system has been created to accomodate to multiple resolutions, adding black bars if required; +user only defines whatever screen size and it gets properly displayed. + +Now raylib can easily deploy games to Android devices and Raspberry Pi (console mode). + +Lots of code changes and lot of testing have concluded in this amazing new raylib 1.2. + features -------- @@ -63,18 +80,27 @@ features * Hardware accelerated with OpenGL (1.1, 3.3+ or ES2) * Unique OpenGL abstraction layer [rlgl] * Powerful fonts module with SpriteFonts support - * Multiple textures support, including DDS and mipmaps generation + * Multiple textures support, including DDS, PKM and mipmaps generation * Basic 3d support for Shapes, Models, Heightmaps and Billboards * Powerful math module for Vector and Matrix operations [raymath] - * Audio loading and playing with streaming support + * Audio loading and playing with streaming support (WAV and OGG) * Custom color palette for fancy visuals on raywhite background + * Multiple platforms support: Windows, Linux, Mac, Android, Raspberry Pi raylib uses on its core module the outstanding [GLFW3] (http://www.glfw.org/) library. The best option by far I found for -window/context and input management (clean, focused, great license, well documented, modern, ...). +multiplatform (Windows, Linux, Mac) window/context and input management (clean, focused, great license, well documented, modern, ...). -raylib is licensed under a zlib/libpng license like GLFW3. View [LICENSE] (https://github.com/raysan5/raylib/blob/master/LICENSE.md). +raylib uses on its audio module [OpenAL Soft] (http://kcat.strangesoft.net/openal.html) audio library, in multiple flavours, +to accomodate to Android and Raspberry Pi. -tool requirements +On Android, raylib uses native_app_glue module (provided on Android NDK) and native Android libraries to manage window/context, +inputs and activity cycle. + +On Raspberry Pi, raylib uses Videocore API and EGL for window/context management and raw inputs reading. + +raylib is licensed under a zlib/libpng license. View [LICENSE] (https://github.com/raysan5/raylib/blob/master/LICENSE.md). + +tools requirements ------------------ raylib has been developed using exclusively two tools: @@ -90,39 +116,107 @@ to allow writing small-mid size programs with a printf-based debugging. All rayl Since raylib v1.1, you can download a windows Installer package for easy installation and configuration. Check [raylib Webpage](http://www.raylib.com/) -building --------- +building source (generate libraylib.a) +-------------------------------------- -raylib could be build with the following command lines (Using GCC compiler): +Building raylib sources on desktop platforms: - cd raylib/src - gcc -c core.c -std=c99 -Wall - gcc -c shapes.c -std=c99 -Wall - gcc -c textures.c -std=c99 -Wall - gcc -c text.c -std=c99 -Wall - gcc -c models.c -std=c99 -Wall - gcc -c raymath.c -std=c99 -Wall - gcc -c rlgl.c -std=c99 -Wall - gcc -c audio.c -std=c99 -Wall - gcc -c utils.c -std=c99 -Wall - gcc -c stb_image.c -std=c99 -Wall - gcc -c stb_vorbis.c -std=c99 -Wall - - ar rcs libraylib.a core.o shapes.o textures.o stb_image.o text.o models.o raymath.o rlgl.o utils.o stb_vorbis.o audio.o +Step 1: Using MinGW make tool, just navigate from command line to raylib/src/ folder and type: -To compile examples, make sure raylib.h is placed in the include path and the following libraries are placed in the libraries path: + mingw32-make PLATFORM=PLATFORM_DESKTOP + +* NOTE: By default raylib compiles using OpenGL 1.1 to maximize compatibility; to use OpenGL 3.3 just type: + + mingw32-make PLATFORM=PLATFORM_DESKTOP GRAPHICS=GRAPHICS_API_OPENGL_33 + +Building raylib sources on Raspberry Pi: + +Step 1. Make sure you have installed in your Raspberry Pi OpenAL Soft library for audio: + + sudo apt-get install openal1 + +Step 2. Navigate from command line to raylib/src/ folder and type: + + make + +Building raylib sources for Android: + +Step 1. Make sure you have installed Android SDK, Android NDK and Apache Ant tools: + + - Download and decompress on C: [Android SDK r23] (http://dl.google.com/android/android-sdk_r23.0.2-windows.zip) + - Download and decompress on C: [Android NDK r10b] (http://dl.google.com/android/ndk/android-ndk32-r10b-windows-x86.zip) + - Download and decompress on C: [Apache Ant 1.9.4] (http://ftp.cixug.es/apache//ant/binaries/apache-ant-1.9.4-bin.zip) + +Step 2. Create the following environment variables with the correct paths: + + ANDROID_SDK_TOOLS = C:\android-sdk\platform-tools + ANDROID_NDK_ROOT = C:\android-ndk-r10b + ANT_HOME = C:\apache-ant-1.9.4 + +Step 3. Navigate from command line to folder raylib/template_android/ and type: + + %ANDROID_NDK_ROOT%\ndk-build + +* NOTE: libraylib.a will be generated in folder raylib/src_android/obj/local/armeabi/, it must be copied +to Android project; if using raylib/template_android project, copy it to raylib/template_android/jni/libs/. + +building examples +----------------- + +Building raylib examples on desktop platforms: + +Step 1: Using MinGW make tool, just navigate from command line to raylib/examples/ folder and type: + + mingw32-make PLATFORM=PLATFORM_DESKTOP + +* NOTE: Make sure the following libs (and their headers) are placed on their respectibe MinGW folders: - libraylib.a - raylib libglfw3.a - GLFW3 (static version) libglew32.a - GLEW, OpenGL extension loading, only required if using OpenGL 3.3+ or ES2 - libopenal32.a - OpenAL, audio device management - -It's also recommended to link with file icon.o for fancy raylib icon usage. Linking command: + libopenal32.a - OpenAL Soft, audio device management - cd raylib/examples - gcc -o test_code.exe test_code.c icon.o -lraylib -lglfw3 -lglew32 -lopenal32 -lopengl32 -lgdi32 -std=c99 -Wl,--subsystem,windows - -If you have any doubt, [let me know][raysan5]. +Building raylib examples on Raspberry Pi: + +Step 1. Make sure you have installed in your Raspberry Pi OpenAL Soft library for audio: + + sudo apt-get install openal1 + +Step 2. Navigate from command line to raylib/examples/ folder and type: + + make + +Building raylib project for Android (using template): + +Step 1. Make sure you have installed Android SDK, Android NDK and Apache Ant tools: + + - Download and decompress on C: [Android SDK r23] (http://dl.google.com/android/android-sdk_r23.0.2-windows.zip) + - Download and decompress on C: [Android NDK r10b] (http://dl.google.com/android/ndk/android-ndk32-r10b-windows-x86.zip) + - Download and decompress on C: [Apache Ant 1.9.4] (http://ftp.cixug.es/apache//ant/binaries/apache-ant-1.9.4-bin.zip) + +Step 2. Create the following environment variables with the correct paths: + + ANDROID_SDK_TOOLS = C:\android-sdk\platform-tools + ANDROID_NDK_ROOT = C:\android-ndk-r10b + ANT_HOME = C:\apache-ant-1.9.4 + +Step 3. To compile project, navigate from command line to folder raylib/template_android/ and type: + + %ANDROID_NDK_ROOT%\ndk-build + +Step 4. To generate APK, navigate to folder raylib/template_android/ and type: + + %ANT_HOME%\bin\ant debug + +Step 5: To install APK into connected device (previously intalled drivers and activated USB debug mode on device): + + %ANT_HOME%\bin\ant installd + +Step 6: To view log output from device: + + %ANDROID_SDK_TOOLS%\adb logcat -c + %ANDROID_SDK_TOOLS%\adb -d logcat raylib:V *:S + +If you have any doubt, [just let me know][raysan5]. contact ------- @@ -140,9 +234,9 @@ acknowledgments The following people have contributed in some way to make raylib project a reality. Big thanks to them! - - [Zopokx](https://github.com/Zopokx) - - [Elendow](http://www.elendow.com) - - Victor Dual - - Marc Palau + - [Zopokx](https://github.com/Zopokx) for testing and hosting the web. + - [Elendow](http://www.elendow.com) for testing and helping on web development. + - Victor Dual for implementation and testing of 3D shapes functions. + - Marc Palau for implementation and testing of 3D shapes functions. [raysan5]: mailto:raysan@raysanweb.com "Ramon Santamaria - Ray San" diff --git a/examples/makefile b/examples/makefile new file mode 100644 index 000000000..59b16d70f --- /dev/null +++ b/examples/makefile @@ -0,0 +1,193 @@ +#************************************************************************************************** +# +# raylib for Raspberry Pi and Windows desktop +# +# makefile to compile raylib examples +# +# 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 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../src -I/opt/vc/include -I/opt/vc/include/interface/vcos/pthreads +else + INCLUDES = -I. -I../src +endif + +# define library paths containing required libs +LFLAGS = -L. -L../src -L/opt/vc/lib + +# define any libraries to link into executable +# if you want to link libraries (libname.so or libname.a), use the -lname +ifeq ($(PLATFORM),PLATFORM_RPI) + # libraries for Raspberry Pi compiling + # NOTE: OpenAL Soft library should be installed (libopenal1 package) + LIBS = -lraylib -lGLESv2 -lEGL -lm -lbcm_host -lopenal +else + # libraries for Windows desktop compiling + # NOTE: GLFW3 and OpenAL Soft libraries should be installed + LIBS = -lraylib -lglfw3 -lglew32 -lopengl32 -lopenal32 -lgdi32 +endif + +# define additional parameters and flags for windows +ifeq ($(PLATFORM),PLATFORM_DESKTOP) + # resources file contains windows exe icon + # -Wl,--subsystem,windows hides the console window + WINFLAGS = ../src/resources -Wl,--subsystem,windows +endif + +# define all object files required +EXAMPLES = \ + ex01_basic_window \ + ex02a_logo_raylib \ + ex02b_basic_shapes \ + ex02c_color_palette \ + ex03a_input_keys \ + ex03b_input_mouse \ + ex04a_textures \ + ex04b_texture_rectangle \ + ex05a_sprite_fonts \ + ex05b_rbmf_fonts \ + ex06a_color_select \ + ex06b_logo_anim \ + ex06c_font_select \ + ex07a_3d_mode \ + ex07b_3d_shapes \ + ex07c_3d_models \ + ex08_audio + #ex03c_input_gamepad \ + + +# typing 'make' will invoke the first target entry in the file, +# in this case, the 'default' target entry is raylib +default: examples + +# compile all examples +examples: $(EXAMPLES) + +# compile example 01 - basic window +ex01_basic_window: ex01_basic_window.c + $(CC) -o $@ $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) + +# compile example 01 - basic window +ex02a_logo_raylib: ex02a_logo_raylib.c + $(CC) -o $@ $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) + +# compile example 01 - basic window +ex02b_basic_shapes: ex02b_basic_shapes.c + $(CC) -o $@ $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) + +# compile example 01 - basic window +ex02c_color_palette: ex02c_color_palette.c + $(CC) -o $@ $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) + +# compile example 01 - basic window +ex03a_input_keys: ex03a_input_keys.c + $(CC) -o $@ $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) + +# compile example 01 - basic window +ex03b_input_mouse: ex03b_input_mouse.c + $(CC) -o $@ $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) + +ifeq ($(PLATFORM),PLATFORM_DESKTOP) +# compile example 01 - basic window +ex03c_input_gamepad: ex03c_input_gamepad.c + $(CC) -o $@ $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) +endif + +# compile example 01 - basic window +ex04a_textures: ex04a_textures.c + $(CC) -o $@ $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) + +# compile example 01 - basic window +ex04b_texture_rectangle: ex04b_texture_rectangle.c + $(CC) -o $@ $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) + +# compile example 01 - basic window +ex05a_sprite_fonts: ex05a_sprite_fonts.c + $(CC) -o $@ $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) + +# compile example 01 - basic window +ex05b_rbmf_fonts: ex05b_rbmf_fonts.c + $(CC) -o $@ $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) + +# compile example 01 - basic window +ex06a_color_select: ex06a_color_select.c + $(CC) -o $@ $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) + +# compile example 01 - basic window +ex06b_logo_anim: ex06b_logo_anim.c + $(CC) -o $@ $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) + +# compile example 01 - basic window +ex06b_shape_select: ex06b_shape_select.c + $(CC) -o $@ $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) + +# compile example 01 - basic window +ex06c_font_select: ex06c_font_select.c + $(CC) -o $@ $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) + +# compile example 01 - basic window +ex07a_3d_mode: ex07a_3d_mode.c + $(CC) -o $@ $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) + +# compile example 01 - basic window +ex07b_3d_shapes: ex07b_3d_shapes.c + $(CC) -o $@ $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) + +# compile example 01 - basic window +ex07c_3d_models: ex07c_3d_models.c + $(CC) -o $@ $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) + +# compile example 01 - basic window +ex08_audio: ex08_audio.c + $(CC) -o $@ $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) + +# clean everything +clean: +ifeq ($(PLATFORM),PLATFORM_RPI) + rm -f *.o +# find . -executable -delete +else + del *.o *.exe +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) +#%.exe : %.c +# $(CC) -o $@ $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) diff --git a/src/audio.c b/src/audio.c index 03f72a2e3..cc1d9f9aa 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 // OpenAL basic header -#include // 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 // Declares malloc() and free() for memory management -#include // Required for strcmp() -#include // Used for .WAV loading +#include // Declares malloc() and free() for memory management +#include // Required for strcmp() +#include // 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; diff --git a/src/core.c b/src/core.c index c9784238f..5bea82f99 100644 --- a/src/core.c +++ b/src/core.c @@ -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 // GLFW3 lib: Windows, OpenGL context and Input management -//#include // OpenGL functions (GLFW3 already includes gl.h) -#include // Standard input / output lib -#include // Declares malloc() and free() for memory management, rand() -#include // Useful to initialize random seed -#include // 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 "raymath.h" // Required for data type Matrix and Matrix functions +#include "utils.h" // TraceLog() function + // NOTE: Includes Android fopen map, InitAssetManager() -//#define GLFW_DLL // Using GLFW DLL on Windows -> No, we use static version! +#include // Standard input / output lib +#include // Declares malloc() and free() for memory management, rand(), atexit() +#include // Required for typedef unsigned long long int uint64_t, used by hi-res timer +#include // Useful to initialize random seed - Android/RPI hi-res timer +#include // Math related functions, tan() used to set perspective +#include // String function definitions, memset() +#include // Macros for reporting and retrieving error conditions through error codes + +#if defined(PLATFORM_DESKTOP) + #include // GLFW3 library: Windows, OpenGL context and Input management + //#include // 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 // Java native interface + #include // Android sensors functions + #include // Defines AWINDOW_FLAG_FULLSCREEN and others + //#include // Defines basic app state struct and manages activity + + #include // Khronos EGL library - Native platform display device control functions + #include // Khronos OpenGL ES 2.0 library +#endif + +#if defined(PLATFORM_RPI) + #include // POSIX file control definitions - open(), creat(), fcntl() + #include // POSIX standard function definitions - read(), close(), STDIN_FILENO + #include // POSIX terminal control definitions - tcgetattr(), tcsetattr() + #include // POSIX threads management (mouse input) + + #include // UNIX System call for device-specific input/output operations - ioctl() + #include // Linux: KDSKBMODE, K_MEDIUMRAM constants definition + #include // Linux: Keycodes constants definition (KEY_A, ...) + #include + + #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) -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 +// 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 -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) +// 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 + +#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 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,104 +171,97 @@ 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); -} - -// 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); - - if (!glfwInit()) TraceLog(ERROR, "Failed to initialize GLFW"); - - //glfwDefaultWindowHints() // Set default windows hints - - if (!resizable) glfwWindowHint(GLFW_RESIZABLE, GL_FALSE); // Avoid window being resizable - -#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); -#endif - - window = glfwCreateWindow(width, height, title, NULL, NULL); - - windowWidth = width; - windowHeight = height; + // Store window title (could be useful...) windowTitle = title; - if (!window) - { - glfwTerminate(); - TraceLog(ERROR, "Failed to initialize Window"); - } + // Init device display (monitor, LCD, ...) + InitDisplay(width, height); - glfwSetWindowSizeCallback(window, WindowSizeCallback); - glfwSetCursorEnterCallback(window, CursorEnterCallback); + // Init OpenGL graphics + InitGraphics(); - 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() + // Load default font for convenience + // NOTE: External function (defined in module: text) + LoadDefaultFont(); - //------------------------------------------------------ -#if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2) - rlglInit(); // Init rlgl + // Init hi-res timer + InitTimer(); + +#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; - int fbWidth, fbHeight; - glfwGetFramebufferSize(window, &fbWidth, &fbHeight); // Get framebuffer size of current window - - //------------------------------------------------------ - rlglInitGraphicsDevice(fbWidth, fbHeight); - //------------------------------------------------------ - - previousTime = glfwGetTime(); - - LoadDefaultFont(); // NOTE: External function (defined in module: text) - - if (cursorImage != NULL) SetCustomCursor(cursorImage); - - srand(time(NULL)); // Initialize random seed - - ClearBackground(RAYWHITE); // Default background color for raylib games :P - - // raylib logo appearing animation + // raylib logo appearing animation (if enabled) if (showLogo) { SetTargetFPS(60); @@ -189,21 +269,129 @@ void InitWindowEx(int width, int height, const char* title, bool resizable, cons } } +#elif defined(PLATFORM_ANDROID) +// Android activity initialization +void InitWindow(int width, int height, struct android_app *state) +{ + app_dummy(); + + screenWidth = width; + screenHeight = height; + + app = state; + + // 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); + + int orientation = AConfiguration_getOrientation(app->config); + + if (orientation == ACONFIGURATION_ORIENTATION_PORT) TraceLog(INFO, "PORTRAIT window orientation"); + else if (orientation == ACONFIGURATION_ORIENTATION_LAND) TraceLog(INFO, "LANDSCAPE window orientation"); + + // 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"); + } + + //AConfiguration_getDensity(app->config); + //AConfiguration_getKeyboard(app->config); + //AConfiguration_getScreenSize(app->config); + //AConfiguration_getScreenLong(app->config); + + //state->userData = &engine; + app->onAppCmd = CommandCallback; + app->onInputEvent = InputCallback; + + InitAssetManager(app->activity->assetManager); + + TraceLog(INFO, "Android app initialized successfully"); + + while (!windowReady) + { + // 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); + rlglDraw(); // Draw Buffers (Only OpenGL 3+ and ES2) - //------------------------------------------------------ -#if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2) - rlglDraw(); // Draw Buffers -#endif - //------------------------------------------------------ + SwapBuffers(); // Copy back buffer to front buffer + PollInputEvents(); // Poll user events - glfwSwapBuffers(window); // Swap back and front buffers - glfwPollEvents(); // Register keyboard/mouse 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 -static void LogoAnimation() +// Initialize hi-resolution timer +static void InitTimer(void) { - int logoPositionX = windowWidth/2 - 128; - int logoPositionY = windowHeight/2 - 128; + 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 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 +} + +#if defined(PLATFORM_RPI) +// Mouse initialization (including mouse thread) +static void InitMouse(void) +{ + // 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 000000000..6f0179ab4 --- /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 95dbae7cc..ab6abb554 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 // Standard input/output functions, used to read model files data -#include // Declares malloc() and free() for memory management -#include // Required for strcmp() -#include // Used for sin, cos, tan +#if defined(PLATFORM_ANDROID) + #include "utils.h" // Android fopen function map +#endif -#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 // Standard input/output functions, used to read model files data +#include // Declares malloc() and free() for memory management +#include // Required for strcmp() +#include // 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 //---------------------------------------------------------------------------------- // 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; + // NOTE: At this point we have all vertex, texcoord, normal data for the model in vData struct - 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); // 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 a3fccfdbd..9c7549521 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 // 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 b3706b25e..e598b3813 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 5dcfe0618..c396a347d 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 new file mode 100644 index 0000000000000000000000000000000000000000..2038c07a3f61871b1a5c97b6afd0a7614a8dbd5c GIT binary patch literal 107204 zcmeHQ3%r$6+JE=yq>ip+NH=H9bTKm}GvtzV5lK#xei>7!DNuN+l#x87HLFx7J?!S?gKvyPo%5@7jC6=P+mc?bfrN^<4hX zWvzGZb8}HD`|;89 zYOw!2uZ`!G)@Nhmlvmo2jTf+y4M?t5+RtbvSHSo)*vR!3vayhj%h_0yjc>5=2sVDg z##(G-N~L^I{!(5MQG6ZjAMPuyg-@Pzz5hICTx6#&HM3!W?r}T>D{B=kxh)WH7_r%&}JK3agSl5!Sp0{L9or8mU0~C$E;PP>v zSAP|Mr`BA*cqk*f_3YlY-=r%)-@JM3roSD1-rxh@U3L0%Yj2zLr`xY?yK(Y=b{;gO z|6?ovbNuw7|9wQgqdU#5_-N1JL+`G=Y(~$C|8?5o_O(}b8@ckyhHo|c{LI5Gyj#`} z9B@wR%D;KP?bBm<%{MRHd~ns)8qd`{epSuUN7wyy%s_8KQQd;p3;RD^H0h|ipEhs3 zwQ+}3)5dde`k-L>Np(MM)giUC@e#wmJZf~^$?abo-0+*)ZSMa(Rj2rfIbYO&{O(OR zpW437cdxX4wejYH8C^HO(&?#VS1o+_ozkx5b4sTj+P~x4X_4{Sz9_-}A}A zhwl5u;P&VD{CxPjqQx5*x9Zh+<|Tb+wcS&`zGQK$>#r;5+xMNl%;fB#bBmjB#a|Li~1+1luml6MQ|_Wt_y z{XI^*{LF8E_Oq5t%O-9;WlF)>w=%h|PYr7{y4QeqrIVj7YP0jLjO}_Sr2aCx&cC{I z+pF6=`X3t#o2{z3w?k(7(WaYcF8Qd@)>&M-utRDMb98G-m$n}@pVZ;8e;j?pkh%R< z6`k>iiCbGgcwa?Hm!0G1cStRp!rZ(+eP4@z4lTN+ycN&mnx~4+xV2HApML?NpH^17 ze#a#x^V&Cl=;0@p>}dY$OV1v6=#rBD2ghtZiMbrJJuTRfgE?-=>J85nEk0$}ORt}Q z+T||~nt9iVGg5zfs@9hh@)@b6>mMoUQoCQ9lXfoJSXj2IUBxjs)#;WAuy^^HyI#8J zr7!2mR6Tz8BkMW~^mCtmpk#jON*2O^J5Cu{Fyf?T#X~j}&aD`piT~C)rI+q{uI7Ku z9imebUf6NQhrPn)9W zw5;?uJ6H1(yV92*V+)zn#mvK4)+0R+EGe4N=Fv&m9}`_qr6HT(6?H{Nn) zx(l|l2HJ4T4abWJPx{}%4X%-;>`{Kw!DBzXEt9!-_Ofn2uKc}BRP&x;MZCCl4WHH? zSulHB!5ORHta*n>>(bU3evd9Yzswj}e^ZC+3chW%u>Ze`_BQ6>O)n`wA#gxPR^GsiRo`PFDRph$ZPowqaRIvGzJUQ=--L8okkW#;9(h z63_EC+gR9r?%QMEpT1@<_?*^wD2oJ@YjC5{GHO=+^xhf8U_N_SNtfEZ9gYg^7Q{muWdqb|Lz7w^b);%OrbdT%TEMsCXG?8rjr zNEth|YKjKrWn?|b{B62x;-F3qGR-@!WB<6F)pnq# z&Mq0 z)gjYy?=kOBe_+Ol^{1y6z11mw{<3rE$m7cSN%&~n~M}-yVHQxMtVWj2fYcgl?+44*}ct1PY*v?@a?`eFbl)c=F zKN{7c$R1MPu?Nk_f=>n7>kDRoQBYF;eQk6 zvZLXU{mIs2*8RqtbIgu8Yo_e~`;yMH4;(jZz-6d2N%tm+VSJp{dV9gaCcAs?ZnL)I z6Jw6axLR^`qc^Va+HPWxG4C_U#jU>kto%2h-CA?+><^|)KE(D&!^M62zIH>c7DLW! z)PCPi_UJC#zN>BRlKYS{Ia}VVxp(`R7w>7&Yx~;s z-?=VxL|pmjI^P}j?H%iW_wj_oQ;%wO=au)YUbOzpMJxxunANfA70ck%+4X;g4>#C9 z@;{z|Rr+tQ>Y~YWTq0*G?Q?=OS^Gy?g0br!|}U>Z!YTuAkQ9taf)VIJtR`^+Vno z^XN0{zGi)xeh{+6?fuJ>+pgX8)_3 z*PHvUrFFkd4^ZBLY z%3i4X+z03OUh&+=od+~*a?g$9R!(bn{5==0yJz-Kb}reu{FwXKwc2{kC+9vgX4rvU z6FT2lx^w?}D5{Qc7W2@0+N-xc^W=d;Q)C|b7HK!(tNkl8Xa5zePESqyXxC@Fo|*n( zDeRDbewUQ5dZ&arJ}{%CON;sZpk%kYiw1u2Vw-)Pp$jihtG4+&%8OIX(lDB=arPR6i?}Y=NsuHi&ouv!V#B$ za(ja_yS~%uy;nYaWX++Q%3f^L@RGhy?n2B*wx4)jgIk|?k3U))ezU!NNZs8p4ZP&1 z9p#mL^|3?GIqhcY)DzT@0 z@~E!o9B8%mq0MKc@9l-`R=IjsqdSiLYUGwDuWoQ_X3MRp@nZd-ZD7IdYYslxWxCf2 zukT9>W<4=^9!q%h^hJD9t(#6v3+@s3`HlZ7>9VYN*`IIj)bxtWw(#SuHao3n(#P~y z1HJ3gcgQPNb$qJ-yW+{z@`Y!cE-UAGvg8q$=s#b zJ$os;4%J`X=A_}B*_k<&JsjBmcvB`m_WU1R=a=l$K+gx@Vm@+z?ZxQA&}Mk&#)X>- z%huNH^Yc!F4h`OW@9wYfYOrKS!Sa?nuBvg%SudwAl+C6TvkzPB0Xu(e&9ax<&S*Ei z>(=JKKI=X9*c{$+a{GF9FYNq9(de0@Qm-r=^}(^-dp%ch;mP%GDeuwcuOA-#@7hPq zSyJ@S-DT72)%~r6dJ z?JwK;eC1e^GU{l{*06*_M5F9`{^IY@9T+sq0{}!0dv0k-I>`Cm^A)$UfG$&$2h(r|F|x8 zw5R#8(#hZhS&S2S{t@tTS-cjl8-D-5`jSTdOVu@$kG{VuZASDOj?Y%dBuC}OBx~Yf zeOnsa3F?IBpUVgIef?pS$$ZWT=*Il~5%qjF)*00|>VAOnB|+o-yZB4%8}g6s`f2RP zzh*PH(br@UK8+u<_8c45H_=h`HTDPk{&ZA-nSAa;=Z|qcbw2<*^qAZGpKT5(Jhu9jT$-QRpOg>I57jrE?bw#C ztMaGw>Ce5ka@=QBePNv%0XNlOu%4?iPHfmG;KTZMG=2ZW_BEQFZ`d|oqxgW2%Th9C zzUmL~W0phxg3e#6uAzK%KLDTk8};}iV`KRX!-Dl~X>1Sc2)>RFWP@@77sUrXKAL2B zf0+1K4`^FCE+1NF^qi;Uz^68%{?hm%wZZ)#rW1_KoPWwjS|8o7usw>#wy+H5`)MT? z|NLNo5l7Y<+f_Eh?{&6v+%{ELTYggaKj@D*vEkgsq8-j}zkLcP#mD-T48_N~oHofp z{`|7Gb#}l1g`dZMJn4DJ`C*F9Xd~pT(dEtO2jz$2>o$Wmfc)F&>oN+T&ZqNgE5|WI z{i*uGIyFMv5FgC*HKt=j=Od1lqB$18N6FCq_~$qOe2eYjTu^))8LVqdW4n?@`-`e; zDBt1!2liHa6=k)zA z{QHA|FWb?!KZ0>PeEl`%*U@ln*gxj`X-babV||kl)i>(KHXr_k+CPt;GFl6^{cxD*smgEYETm zTTpkZzOYV>;Ipd6_ru5MaK52_qUsvTNB0BBZ~n&Y;>#Z{ID3=9m#_U5T4#%UfHA*` zo{tXscfeT~M(?hs#IaO4o5d;dE02Zh_fhx$|Xg>`C#yjRuuDg~cI|0bG0gpaCgC_mBrUw-=^ z`on#6xPPGh+xm6yk8pg(I-~j;*U{Gnjrglje7VABv|~6=gL2{d56ZKCPcVN`^^ID0 z`u=Z%@fq_QRkx~+FABe5ojO0F>I>`C{lF=|27H>GA7K8q_@d(1)*a3dO+Wnk8C73c zr$&g2>MvNY>@iV%4*lz%pX~aBs%t18-4ASWyXQZ<_-ygH%T;xJcKcteH>$73NB2J9 z8}sJS|B3d813qJ%R_Clke>mWC&0o~dcS4=1(dqtYl{dX_UE_1An?wGc{5a&n)en_F zs;+*1Rlona=B4WEIh-Fc`U>hy)fd*O5%Nd%7p&)Wbl9KO`1JD>>Os{tl#lKQU?*yH z&wqaVKMHsBx&yvM`!^h4Xq{cZ|C?xh=DrTCn*+X3doj*iD?1MOT=!R{-2W1c&*^@e zD86L+m+Ft`{h#gMf3Dp4toB>3_is+^g`eNq#%Ei9N)G%}{RQilJtm4T(euNhKM+2u zuA%(u`ycungW2W3viF6t&Zxe|b@X*WBYxCh#&yH_=)ON4@^A2Oi!%{9hy1zXimGqa zdey&QIrN7^{@wexa_>h`^@Vln>qD-67pmuw|4@6O`@O37hXcOQc=djZsxPclqkGE&O}CZzEO3x<=f(|>iuCGM^G-A{&3GACz~?pV5v5zVFNTe_i8CO3%H2^ZR^<_$hqUzV`E}`vKU` z>6qyLOeX*C`EfWuEcQL%fjU$5g>`BKJ5+zcdS#D^;;ZWYi||o(wdLE^HPQUX=zm-M zv|N7oC-`;GUpPM7I#Y6hPa9ExY5b77*p(LY=Al!a6lV z{;2+f^~xUI@m2Ny%?=+`*HAwC`7qnMCYt|5&)w|upa1+B&M%ao&^n`^^P}X9_ zqcJ{|zr3tF?r|K0uOCjUnN3HC?!^-m`M?&o(^$LG*T4*fyp&$j;5 z{lM72hI1yCTn^)luZ$tRP4<{9&& z>p2}A`okSxGUtaoK8JGz<&UbXE#J1T?(sR~KSuxC;-}>t@S*(EcJ;dED}T{-PUiQ2 z-Q&~qWR*{c{9D=0Ro^+_bIo6+-2W1cFR8k@=D)J>MeqOb$KU^R&to#@w>v(E^DV|X z1ohAM{#RAw16+yj&qU9+?D`k#?>a($qel1qJH!{G{{eTR_#FBJaK<0Q^XFQ3(2I{J zzvr11o+^}oBVGr5uKf#jbREC%KmTCNf1>9{GW&t*58Jv^_k&#JPqX9D|C)ZR`h}m5 z?soOMQu)u7eOEhBf2zK)PL1F<(fAzt!yR8T{Q-H5KRVz;`J?J;%eTero_~EG+4@UP zF1!5O;&jhnIKJpQSAYG_wnjOVL;EvlHi>8A5C7|b;r(ySuS5Rb_ou`8mP35e`@j0@ zf5sjm_7f%R-X9MA8v`5EKim5s6?awPyXW6wf5yl^ZMd8YR zJ-htd`ghM?c>hM%-QoUM)%)M!`Dt6HoX91JFS`EU_rL!f-v7pWIpjZI`_qUohx^O6 z{?z>-8lS`YSk?2NC_ab$#pn;HKUH5?&)*pSe952R@N=!&Z%BU7@BiB7DbfCNz-Jpr zWy!hc&tZRMn?DErMB}sgy=H%%-SLI&BL^IQT-nbZ`lAwXSLYw~k;DGVwolyipG<$b z<3oAPV_zqV&!IoE#pkZ?&>s%*W!wMx)=xBldF=Ci=gG>xLw~sE-{Jglz-JYAuIidC zzHIe#6$fR<0iSz+IGk@3?hw8^zR3P%tylf3&OmhrsxwfXf$9uYXW-u{18rEaUD&&n zy;EK>`>_ds$oylKAH~3uRr_jelL#lgX5rRtcztZAJbR{(^!V0vAm$^ z_(dPM^*{C>^cn1TN?!3(snlWh_9UHceS+~*{jadt`NVN!U5bxoC>rbTOXnb1AFQj( z2lMXV|2THB4@^2i9_17B1+&2|P>;?H$fwxXI+dO-4>CNjK*K0>ba`dRUw_#t)}x!rj2>G4y# zn2%{aK4lBb1Zfeo&WAkE_=UV4zfJ$^eiTNG9m`-oMPvE+w2D>P$2}320~Nojx#Fq5 z(rr)}m47>VYzOONI$8$D3b_dOgxMyJ)&K0{Yv%f5Tbz!@sLR;MV;@+aqA7XI57K)4 zxc>z$;-`GZ|l9JV|hjE^~bT} z`YZY9d{D9&3#KV~%%|ir--gCE1+B+#)Bo58_KEqJ&PImP#e7U-znG?Fg7ND;5M6KD zw$jBi6m5rB*`(wt`)0l#zaBeZ)6Z?}6Vuo>=3^SmP&Aguw2i!t47Mp~J$~H(A{Qdx zl6@ReyqW&O{{R5AW6yHV$>tfnQHyat{OTY63a}Ym1Wn0&^ z;ZreXql;|@X;pvR`<$lwAKSpVZ1`9P)7TECu?$7q$YU9uR`CaGPxrrW!~IOQi3$6% zk*8$Ld=e@zAH(EKYuUs{=~Dcle?5M@2dMtHv5jRg9rPc}AC`|xWBZh@ zEe(_Xi6UQF^%m|d>a|8Yo_)3=c4~@aOFzI z9Fx9hu?+SrXgz+t_Ixe;d=A=E{Q_&@{=@B4x)dLL`bSD0_dkwZ(JCHngQBrt8<}k6 zDP4+h#-rCC`X9zze*VR{FpY6xKBln@rm+k~hs*2ntGWX}yg#u|><80WhN3BX%(tQA z+lM&uofq@m`yXS$*eO0GgXJk2>&By1ti1NB?}4wMrP=t$x)eq%gK0`WEWU&S4)J?{tA|GJ);k7Y1T`#0Oc@|eapF^y#?8p|tM z#UHo-DL)in`PBCmmd7%fretjRl#H;i$B)l{oBpS4V?L&FOqj+pn8q>`Z6l9mbefJ| z>_?_L+R0HcwBjGlcM9=#PXO9+SlXH zNB;+Xs-A*1j1kkY9yU_)m`~He_;C;5`wwL!3>&OrUEqa{xss>sD83#)jvbzVN>;BM z=3^Sih50r#*2OfXi)APp>jq`?_~Z6JwvB1*hvM7FP`a3pX>1eISVpJm_@VyT4$!&{ zy)QrpkCc2|el|AEy836{IQ?(7r{{u-&qkM$!8WlBMTg7N^%uDi@r2vZWA@{)(Zw>^ z(Ac+)9sKkE!P?WkVUp$hh2rCLB^x>$`;;!`W7>ohuVdOI%XwIaqOmTP!F-Cw@)V7A zu?*&$=s2=mAIoDJ+s1rMQ!<#3X&YTi#>NiF#I=FtF%9R_;s_zd2f5l<> z|2~j^7a!jt-E+@>7svm0$(fda^tb;cZuUELU{~@&V~L1Q>MFbY7!Q8ONcu4OHSje2 z>>dDr>klyNUsF>){eJN8J8?WhU;K`!o@XyzQ$Npx-x1^(fQB){<32zS@CEKJnHs=) zdX#to7aJu{k~kq8HP1nQv7qkmDHry08XV@LbQoI;%id;EC7=20RcS&KdU$ zz9HWbFW^qEXL7@Nd>wQEi<~n#zz=Ii8s^#YR>u-?hCwc_&YO)8bTiVJ}~hBZwmD&#|S?7$nyw1%m-cW1Nac1;)&b{ zJHjrf0Sm^=u|qu4Hj5c#qj-2-(B@hN<&+PI1IzH-1ngycjPGB-47ukc)>k!9e$+au zOQybjO~#z@6IDO(0X@%cKrEOCa^N3h2Aao{UYEX2cuf6mip5j|lRcy>{UTk0Col(V z0PzJ!lOKc!)j;YZ-=TGpx+Whe79=NoC(He->WX|LKfzc`^)uzcgj>m}{mVb+s?WXQ zc$B`xC9wvdZMqD?BkO|fA)eHeykKrHX6S!DN?ndi>LWiAZ_p;!m3<=R5D(eY{G?@3 zd{Wo!gX?0yJ9ugn?Bqs%wrndE{zb8b!cOg!`cRnIr+4?llVUDZ15$9VNm>&uu? zEG8cE3$f_?SKS{JE@V^cn|c|^A>Yyt;(_n*eFgcz`!DDt?*5f{OtFOWP+ep!2&2?R z7$pzM`E+5=;!*Bj3w^!*s4h|$;g&okC-ETMh$r)fc(Q)Xp1`fn{q&qY%Y>fEH}VsT z$CNXqOW*;&7$ePNcO-V!17^GzLiLevnI9Po!YFyBSnO*MhDBjkdzZ(f_Ld0`;-Ofi zAIU?wWh|(sNG?=Y$|2p1y?FZ<>4(_lHIaA_ZWGT`KgmNrq^{(N+=%|ybDz#X_lsnu zzQluaYm$>ZR4b$#w1;vdP5xV^RLcP+&e*xN?*yLJ~81zykM=6uFS1DW~rO8r{h=qK*Q|d+waHr^RFf+ zV?psrEF!*8{65)qOtw7f8`a9hllEwgS}uKiQs5VTBlm**{9@`y#FM@eM#PhTB@gM! zSWG-*&m@QJNzB^kU-SLh7LV*5>D%1PCVNsBVU#>+Pt{8AAN6$zyf396sgLqw@`HF% z7xBXID8J^I{kl5-82dLi9+fj06Ygc18`MuGACgD!U*1z`A5s?ClzvS4F=0ftLc0Du zQs<6}75wQwaQ^XL5PAqVibeVkLPo#xTPHOk>M%)YVYd!aek0*iAUu^#f*5! zw_G>zq`e?UZEr|j;lqSq?q6xsTx$~^*>}_xzRqOg0fu#)o*Jx-t*Qx8$LGNqZ;{ zl4pu9cn1O;L5#LGO?VJ50=M*wp%0H9WF^PT#9*;?@Kl2aqav6WU^!npmb9{#QQGGaG zte^J7*YR14;^+1NiyTq>>2)@Te!%iL|F8}*!yIge`q%ui7`3_BPhba+(m#`j{(yRE z{`tC$Kkoh)`BC=#`4afFb>>gaMLf#C-v8-%w0QmT^P1zgoqt^?oA`xqEgl0NZQUQg z*8ePi)c;ic%AO&=%pSci@~74Xel7q0{twN+$hWd*$iIds%D?D8&0eT~vprQ`Lw@}E zXY;u6`|)XWEnb6vZQY-LfBa%y#p93HoNpyB)@gix|6>1X@>>4=@%Zuh@f!U5^Uv}T znt!&g;_>@4*w^F*UQOShe}DbiS!f@>-@d@3{2A~?#n1fv_0`-TzhUl=UyIj|Pvln1 zzvf@qr~Mn^^ZVE0W%Alw*!TNa`8WI5@c8{1=ArhOyz*~&{%Q6E9%auTk6k|e_?3UV z{ipPWe>G?LG`s?j&cn|KA+Pxleg634HT(1DC)B@T|EaHUhF?;jhDNA|9>|gdeS`7NXA`kF+ ziLL25%zEy(PtCzEti#-HRDB8i1V(>f`{!mke;$;5vp&wBkOjNu(PW?V;ByCF$G*8A zKKJ_z&LM9ipF&4HkAyFAUh4M1zK{jEx^?SGzq-8eDv_=YRv`p)P8!{P|@;MvS^$SOXn3ia78(UxPRB)o8Y> z*3EIjI@pB~>Pe4&dz62{2fHwWUp4ymskxsoaDZJHY21E&nEOXPKCla;!mY~%{Zi|| z7bDn(eXZ<(9N>ewe}r}a2=?`Jmd5Y5>z{*tov-kV9#!~3-ampq#n;z^_Vx9kKUnvV z;1Bp>PTL1L7|r@o{-Lhmht9vNeRVc)9|{lPhS6`I;)9$R{rJ^f?<>XE@hCpX`$tNi z;#2y-_v^_yOHck@{2tbjy^Gmf-ot9MN$@4la<=41^H|}{ZA8ZI^Mju;xZa$Gx!xas ztfG&DvK$Mn2_1MYV~m_<_9gU%U9Jy!RE)w7_<(ho3prumtS@q|{6h?WJmPPc3%mL~ zOvT7^1p1iv+rs)7Ki0uC=)>sOha6)apg{+Z;{Jzg2>F8e%yY1b{fmB6dfX4lVV{`x z`#0g&<^A>*cE}(0%l%^vm=<#W{t@=LE&Rs&5%{pbgkPlxvLFZRFv5C3pP9(!mGWw( z)%hv&*ZK^%A<7iVVdsH*8TW*;2+ z+KeL4JHl(oczkwkp~pP*D>#*T>F-_YUB&)A&+E&mp5Db?Z#M7A{ym@3{PjZNoM|szH*E97y(lYJ6Q`slmGuiu8hJhpM z>-~s%0Qv4rXPh@AZD%yIH=OB?N;^N7@vim;Gv3wSP_NjVz~qFlVzx5ayMg%|?)^3` z+lFOE+3e3W#-^pg)+t^)@0U!ngI9;iag5yRST+u3R(i94uVZshvwIcej%NSySMN0D ziHY|#h7|vg^SWjV*>L>+%?vuaH_Zl%Aec0@J7TW}- zIUb?VaTI58@MwoH8t3R2>9}<}{^%k0?rG2cnT4@3`>I-=_fuxe^ZK#X!7MN1SYC8r zMTH+0&P>;ocUPID9+%V#Nxd$qO_JIrse_WLTaX6D?6n&^ZtM^-VVU85&(Zup3MsYB literal 0 HcmV?d00001 diff --git a/src/rlgl.c b/src/rlgl.c index b76a7b277..8f8d67e75 100644 --- a/src/rlgl.c +++ b/src/rlgl.c @@ -1,4 +1,4 @@ -/********************************************************************************************* +/********************************************************************************************** * * rlgl - raylib OpenGL abstraction layer * @@ -31,32 +31,21 @@ #include // Standard input / output lib #include // 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 // Basic OpenGL include #endif -#ifdef USE_OPENGL_33 +#if defined(GRAPHICS_API_OPENGL_33) #define GLEW_STATIC #include // 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 + #include + #include +#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,75 +1323,38 @@ 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; } - -#ifdef USE_OPENGL_33 - -// 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) +// Load vertex data into a VAO (if supported) and VBO +Model rlglLoadModel(VertexData mesh) { - // Create one OpenGL texture - GLuint id; + Model model; - glGenTextures(1, &id); + model.mesh = mesh; - TraceLog(DEBUG, "Compressed texture width: %i", width); - TraceLog(DEBUG, "Compressed texture height: %i", height); - TraceLog(DEBUG, "Compressed texture mipmap levels: %i", mipmapCount); - TraceLog(DEBUG, "Compressed texture format: 0x%x", compFormat); +#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 - if (compFormat == 0) - { - TraceLog(WARNING, "[ID %i] Texture compressed format not recognized", id); - id = 0; - } - else - { - // Bind the texture - glBindTexture(GL_TEXTURE_2D, id); - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); +#elif defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + model.textureId = 1; // Default whiteTexture - int blockSize = 0; - int offset = 0; - - if (compFormat == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) blockSize = 8; - else blockSize = 16; - - // 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; - - glCompressedTexImage2D(GL_TEXTURE_2D, level, compFormat, width, height, 0, size, data + offset); - - offset += size; - width /= 2; - height /= 2; - - // Security check for NPOT textures - if (width < 1) width = 1; - if (height < 1) height = 1; - } - } - - 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); + if (vaoSupported) + { + // Initialize Quads VAO (Buffer A) + glGenVertexArrays(1, &vaoModel); + glBindVertexArray(vaoModel); + } // Create buffers for our vertex data (positions, texcoords, normals) glGenBuffers(3, vertexBuffer); @@ -1209,6 +1371,8 @@ unsigned int rlglLoadModel(VertexData mesh) 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); @@ -1221,13 +1385,108 @@ unsigned int rlglLoadModel(VertexData mesh) 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)"); + 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 - return vaoModel; -} + 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 + 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) +{ + GLuint 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); + TraceLog(DEBUG, "Compressed texture format: 0x%x", compFormat); + + if (compFormat == 0) + { + 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; + + if (compFormat == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) blockSize = 8; + else blockSize = 16; + + // Load the mipmaps + for (int level = 0; level < mipmapCount && (width || height); level++) + { + 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; + width /= 2; + height /= 2; + + // Security check for NPOT textures + if (width < 1) width = 1; + if (height < 1) height = 1; + } + } +#endif + + return id; +} + // 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; diff --git a/src/rlgl.h b/src/rlgl.h index 7675963b8..7c8eb74b1 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -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); -unsigned int rlglLoadCompressedTexture(unsigned char *data, int width, int height, int mipmapCount, int format); -#endif +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); + +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 17210f21b..6fa26bee1 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); + if (rlGetVersion() == 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); + rlVertex2i(position.x, position.y); + rlVertex2i(position.x, position.y + size.y); + rlVertex2i(position.x + size.x, position.y + size.y); - 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 + 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 -#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 + 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 b11e44ffd..b9e1b304d 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 900e0c207..0eeece130 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 #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 2d7b61e51..bff27496d 100644 --- a/src/stb_vorbis.h +++ b/src/stb_vorbis.h @@ -37,6 +37,11 @@ #include #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 diff --git a/src/text.c b/src/text.c index 54294b146..b42d7d11e 100644 --- a/src/text.c +++ b/src/text.c @@ -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 // Declares malloc() and free() for memory management #include // String management functions (just strlen() is used) #include // Used for functions with variable number of parameters (FormatText()) -#include "stb_image.h" // Used to read image data (multiple formats support) +#include // 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(image.pixels); + + image.pixels = imgDataPixelPOT; + image.width = potWidth; + image.height = potHeight; } - - free(imgDataPixel); - - 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,13 +377,15 @@ int GetFontBaseSize(SpriteFont spriteFont) // NOTE: Uses default font void DrawFPS(int posX, int posY) { - // NOTE: We are rendering fps every second for better viewing on high framerates - static float fps; - static int counter = 0; - static int refreshRate = 0; - char buffer[20]; + // NOTE: We are rendering fps every second for better viewing on high framerates + // TODO: Not working properly on ANDROID and RPI + + static float fps = 0.0f; + static int counter = 0; + static int refreshRate = 20; + if (counter < refreshRate) { counter++; diff --git a/src/textures.c b/src/textures.c index 39947d12a..e342e21be 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 // Declares malloc() and free() for memory management #include // 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 254cc9556..b51121116 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 + #include + #include +#endif + #include // malloc(), free() #include // printf(), fprintf() #include // Used for functions with variable number of parameters (TraceLog()) //#include // 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]; - if (logstream == NULL) TraceLog(WARNING, "Unable to open log file"); + 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); } -// Close the trace log file -void TraceLogClose() +// Initialize asset manager from android app +void InitAssetManager(AAssetManager *manager) { - if (logstream != NULL) fclose(logstream); + assetManager = manager; } +// Replacement for fopen +FILE *android_fopen(const char *fileName, const char *mode) +{ + 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 ac8d55b14..784c7926a 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 // Defines FILE struct + #include // 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 }