소스 검색

Added full support for HTML5 (emscripten)

Corrected some bugs on the way...
Automatically convert textures to POT on RPI and WEB
raysan5 10 년 전
부모
커밋
905b6ec53d
14개의 변경된 파일515개의 추가작업 그리고 222개의 파일을 삭제
  1. 31 18
      src/audio.c
  2. 215 36
      src/core.c
  3. 8 6
      src/makefile
  4. 36 6
      src/models.c
  5. 7 5
      src/raylib.h
  6. 43 4
      src/raymath.c
  7. 2 2
      src/raymath.h
  8. 20 15
      src/rlgl.c
  9. 3 2
      src/rlgl.h
  10. 2 2
      src/shapes.c
  11. 65 109
      src/text.c
  12. 48 7
      src/textures.c
  13. 34 10
      src/utils.c
  14. 1 0
      src/utils.h

+ 31 - 18
src/audio.c

@@ -45,9 +45,14 @@
 // Defines and Macros
 //----------------------------------------------------------------------------------
 #define MUSIC_STREAM_BUFFERS        2
-#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
+
+#if defined(PLATFORM_RPI)
+    // NOTE: On RPI should be lower to avoid frame-stalls
+    #define MUSIC_BUFFER_SIZE      4096*2   // PCM data buffer (short) - 16Kb (RPI)
+#else                            
+    // NOTE: On HTML5 (emscripten) this is allocated on heap, by default it's only 16MB!...just take care...
+    #define MUSIC_BUFFER_SIZE      4096*8   // PCM data buffer (short) - 64Kb
+#endif
 
 //----------------------------------------------------------------------------------
 // Types and Structures Definition
@@ -97,7 +102,7 @@ void InitAudioDevice(void)
     // Open and initialize a device with default settings
     ALCdevice *device = alcOpenDevice(NULL);
 
-    if(!device) TraceLog(ERROR, "Could not open audio device");
+    if(!device) TraceLog(ERROR, "Audio device could not be opened");
 
     ALCcontext *context = alcCreateContext(device, NULL);
 
@@ -196,13 +201,12 @@ Sound LoadSound(char *fileName)
 
         // Attach sound buffer to source
         alSourcei(source, AL_BUFFER, buffer);
+        
+        TraceLog(INFO, "[%s] Sound file loaded successfully (SampleRate: %i, BitRate: %i, Channels: %i)", fileName, wave.sampleRate, wave.bitsPerSample, wave.channels);
 
         // Unallocate WAV data
         UnloadWave(wave);
 
-        TraceLog(INFO, "[%s] Sound file loaded successfully", fileName);
-        TraceLog(INFO, "[%s] Sample rate: %i - Channels: %i", fileName, wave.sampleRate, wave.channels);
-
         sound.source = source;
         sound.buffer = buffer;
     }
@@ -254,8 +258,7 @@ Sound LoadSoundFromWave(Wave wave)
         // Unallocate WAV data
         UnloadWave(wave);
 
-        TraceLog(INFO, "[Wave] Sound file loaded successfully");
-        TraceLog(INFO, "[Wave] Sample rate: %i - Channels: %i", wave.sampleRate, wave.channels);
+        TraceLog(INFO, "[Wave] Sound file loaded successfully (SampleRate: %i, BitRate: %i, Channels: %i)", wave.sampleRate, wave.bitsPerSample, wave.channels);
 
         sound.source = source;
         sound.buffer = buffer;
@@ -280,7 +283,10 @@ Sound LoadSoundFromRES(const char *rresName, int resId)
 
     FILE *rresFile = fopen(rresName, "rb");
 
-    if (!rresFile) TraceLog(WARNING, "[%s] Could not open raylib resource file", rresName);
+    if (rresFile == NULL) 
+    {
+        TraceLog(WARNING, "[%s] rRES raylib resource file could not be opened", rresName);
+    }
     else
     {
         // Read rres file (basic file check - id)
@@ -372,12 +378,12 @@ Sound LoadSoundFromRES(const char *rresName, int resId)
 
                         // Attach sound buffer to source
                         alSourcei(source, AL_BUFFER, buffer);
+                        
+                        TraceLog(INFO, "[%s] Sound loaded successfully from resource (SampleRate: %i, BitRate: %i, Channels: %i)", rresName, wave.sampleRate, wave.bitsPerSample, wave.channels);
 
                         // Unallocate WAV data
                         UnloadWave(wave);
 
-                        TraceLog(INFO, "[%s] Sound loaded successfully from resource, sample rate: %i", rresName, (int)sampleRate);
-
                         sound.source = source;
                         sound.buffer = buffer;
                     }
@@ -492,7 +498,10 @@ void PlayMusicStream(char *fileName)
         // Open audio stream
         currentMusic.stream = stb_vorbis_open_filename(fileName, NULL, NULL);
 
-        if (currentMusic.stream == NULL) TraceLog(WARNING, "[%s] Could not open ogg audio file", fileName);
+        if (currentMusic.stream == NULL)
+        {
+            TraceLog(WARNING, "[%s] OGG audio file could not be opened", fileName);
+        }
         else
         {
             // Get file info
@@ -582,11 +591,13 @@ void ResumeMusicStream(void)
 // Check if music is playing
 bool MusicIsPlaying(void)
 {
-    ALenum state;
+    bool playing = false;
+    ALint state;
 
     alGetSourcei(currentMusic.source, AL_SOURCE_STATE, &state);
+    if (state == AL_PLAYING) playing = true;
 
-    return (state == AL_PLAYING);
+    return playing;
 }
 
 // Set volume for music
@@ -757,9 +768,9 @@ static Wave LoadWAV(const char *fileName)
 
     wavFile = fopen(fileName, "rb");
 
-    if (!wavFile)
+    if (wavFile == NULL)
     {
-        TraceLog(WARNING, "[%s] Could not open WAV file", fileName);
+        TraceLog(WARNING, "[%s] WAV file could not be opened", fileName);
     }
     else
     {
@@ -811,7 +822,7 @@ static Wave LoadWAV(const char *fileName)
                     wave.channels = waveFormat.numChannels;
                     wave.bitsPerSample = waveFormat.bitsPerSample;
 
-                    TraceLog(INFO, "[%s] Wave file loaded successfully", fileName);
+                    TraceLog(INFO, "[%s] WAV file loaded successfully (SampleRate: %i, BitRate: %i, Channels: %i)", fileName, wave.sampleRate, wave.bitsPerSample, wave.channels);
                 }
             }
         }
@@ -860,6 +871,8 @@ static Wave LoadOGG(char *fileName)
     int samplesObtained = stb_vorbis_get_samples_short_interleaved(oggFile, info.channels, wave.data, totalSamplesLength);
 
     TraceLog(DEBUG, "[%s] Samples obtained: %i", fileName, samplesObtained);
+    
+    TraceLog(INFO, "[%s] OGG file loaded successfully (SampleRate: %i, BitRate: %i, Channels: %i)", fileName, wave.sampleRate, wave.bitsPerSample, wave.channels);
 
     stb_vorbis_close(oggFile);
 

+ 215 - 36
src/core.c

@@ -92,7 +92,7 @@
 //----------------------------------------------------------------------------------
 // Defines and Macros
 //----------------------------------------------------------------------------------
-// ...
+#define MAX_TOUCH_POINTS 256
 
 //----------------------------------------------------------------------------------
 // Types and Structures Definition
@@ -112,7 +112,14 @@ static bool windowReady = false;                // Used to detect display initia
 
 // Gestures detection variables
 static float tapTouchX, tapTouchY;
+static int64_t lastTapTime = 0;
+static float lastTapX = 0, lastTapY = 0;
 static bool touchTap = false;
+static bool doubleTap = false;
+static bool drag = false;
+static int stdVector[MAX_TOUCH_POINTS];
+static int indexPosition = 0;
+const AInputEvent* eventDrag;
 static int32_t touchId;
 const int32_t DOUBLE_TAP_TIMEOUT = 300*1000000;
 const int32_t DOUBLE_TAP_SLOP = 100;
@@ -184,6 +191,7 @@ static int previousMouseWheelY = 0;         // Required to track mouse wheel var
 static int currentMouseWheelY = 0;          // Required to track mouse wheel variation
 
 static int exitKey = KEY_ESCAPE;            // Default exit key (ESC)
+static int lastKeyPressed = -1;
 #endif
 
 #if defined(PLATFORM_ANDROID)
@@ -230,6 +238,8 @@ static void InitGamepad(void);                          // Init raw gamepad inpu
 #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
 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 MouseButtonCallback(GLFWwindow *window, int button, int action, int mods);     // GLFW3 Mouse Button Callback, runs on mouse button pressed
+static void CharCallback(GLFWwindow *window, unsigned int key);                            // GLFW3 Char Key Callback, runs on key pressed (get char value)
 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
@@ -441,6 +451,12 @@ int GetScreenHeight(void)
     return screenHeight;
 }
 
+// Get the last key pressed
+int GetKeyPressed(void)
+{
+    return lastKeyPressed;
+}
+
 // Sets Background Color
 void ClearBackground(Color color)
 {
@@ -623,13 +639,7 @@ bool IsKeyPressed(int key)
 {
     bool pressed = false;
 
-    currentKeyState[key] = IsKeyDown(key);
-
-    if (currentKeyState[key] != previousKeyState[key])
-    {
-        if (currentKeyState[key]) pressed = true;
-        previousKeyState[key] = currentKeyState[key];
-    }
+    if ((currentKeyState[key] != previousKeyState[key]) && (currentKeyState[key] == 1)) pressed = true;
     else pressed = false;
 
     return pressed;
@@ -647,13 +657,7 @@ bool IsKeyReleased(int key)
 {
     bool released = false;
 
-    currentKeyState[key] = IsKeyUp(key);
-
-    if (currentKeyState[key] != previousKeyState[key])
-    {
-        if (currentKeyState[key]) released = true;
-        previousKeyState[key] = currentKeyState[key];
-    }
+    if ((currentKeyState[key] != previousKeyState[key]) && (currentKeyState[key] == 0)) released = true;
     else released = false;
 
     return released;
@@ -671,13 +675,7 @@ bool IsMouseButtonPressed(int button)
 {
     bool pressed = false;
 
-    currentMouseState[button] = IsMouseButtonDown(button);
-
-    if (currentMouseState[button] != previousMouseState[button])
-    {
-        if (currentMouseState[button]) pressed = true;
-        previousMouseState[button] = currentMouseState[button];
-    }
+    if ((currentMouseState[button] != previousMouseState[button]) && (currentMouseState[button] == 1)) pressed = true;
     else pressed = false;
 
     return pressed;
@@ -695,13 +693,7 @@ bool IsMouseButtonReleased(int button)
 {
     bool released = false;
 
-    currentMouseState[button] = IsMouseButtonUp(button);
-
-    if (currentMouseState[button] != previousMouseState[button])
-    {
-        if (currentMouseState[button]) released = true;
-        previousMouseState[button] = currentMouseState[button];
-    }
+    if ((currentMouseState[button] != previousMouseState[button]) && (currentMouseState[button] == 0)) released = true;
     else released = false;
 
     return released;
@@ -858,6 +850,18 @@ bool IsScreenTouched(void)
     return touchTap;
 }
 
+bool IsDoubleTap(void)
+{
+    if (doubleTap) TraceLog(INFO, "DOUBLE TAP gesture detected");
+
+    return doubleTap;
+}
+
+bool IsDragGesture(void)
+{
+    return drag;
+}
+
 // Returns touch position X
 int GetTouchX(void)
 {
@@ -877,6 +881,27 @@ Vector2 GetTouchPosition(void)
 
     return position;
 }
+
+/*bool GetPointer(Vector2 *dragPositions)
+{
+    //static int stdVector[MAX_TOUCH_POINTS];
+    //static int indexPosition = 0;
+    //if (indexPosition == 0) return false;
+    Vector2 vec_pointers_[];
+
+    //eventDrag
+    int32_t iIndex = FindIndex( eventDrag, vec_pointers_[0] );
+    
+    if (iIndex == -1) return false;
+
+    float x = AMotionEvent_getX(eventDrag, iIndex);
+    float y = AMotionEvent_getY(eventDrag, iIndex);
+
+    *dragPositions = Vector2( x, y );
+
+
+    return true;
+}*/
 #endif
 
 //----------------------------------------------------------------------------------
@@ -977,6 +1002,8 @@ static void InitDisplay(int width, int height)
     glfwSetWindowSizeCallback(window, WindowSizeCallback);
     glfwSetCursorEnterCallback(window, CursorEnterCallback);
     glfwSetKeyCallback(window, KeyCallback);
+    glfwSetMouseButtonCallback(window, MouseButtonCallback);
+    glfwSetCharCallback(window, CharCallback);
     glfwSetScrollCallback(window, ScrollCallback);
 
     glfwMakeContextCurrent(window);
@@ -1168,6 +1195,25 @@ static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, i
         TakeScreenshot();
     }
 #endif
+    else currentKeyState[key] = action;
+
+    // HACK for GuiTextBox, to deteck back key
+    // TODO: Review...
+    if ((key == 259) && (action == GLFW_PRESS)) lastKeyPressed = 3;
+}
+
+// GLFW3 Mouse Button Callback, runs on mouse button pressed
+static void MouseButtonCallback(GLFWwindow *window, int button, int action, int mods)
+{
+    currentMouseState[button] = action;
+}
+
+// GLFW3 Char Key Callback, runs on key pressed (get char value)
+static void CharCallback(GLFWwindow *window, unsigned int key)
+{
+    lastKeyPressed = key;
+    
+    //TraceLog(INFO, "Char Callback Key pressed: %i\n", key);
 }
 
 // GLFW3 CursorEnter Callback, when cursor enters the window
@@ -1203,6 +1249,7 @@ static int32_t InputCallback(struct android_app *app, AInputEvent *event)
 
     if (type == AINPUT_EVENT_TYPE_MOTION)
     {
+        // Detect TOUCH position
         if ((screenWidth > displayWidth) || (screenHeight > displayHeight))
         {
             // TODO: Seems to work ok but... review!
@@ -1266,7 +1313,124 @@ static int32_t InputCallback(struct android_app *app, AInputEvent *event)
         //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
+        
+        // Detect DOUBLE TAP event
+        bool tapDetected = touchTap;
 
+        switch (flags)
+        {
+            case AMOTION_EVENT_ACTION_DOWN:
+            {
+                int64_t eventTime = AMotionEvent_getEventTime(event);
+                
+                if (eventTime - lastTapTime <= DOUBLE_TAP_TIMEOUT)
+                {
+                    float x = AMotionEvent_getX(event, 0) - lastTapX;
+                    float y = AMotionEvent_getY(event, 0) - lastTapY;
+                    
+                    float densityFactor = 1.0f;
+                    
+                    if ((x*x + y*y) < (DOUBLE_TAP_SLOP*DOUBLE_TAP_SLOP*densityFactor))
+                    {
+                        // Doubletap detected
+                        doubleTap = true;
+                        
+                    }
+                }
+            } break;
+            case AMOTION_EVENT_ACTION_UP:
+            {
+                if (tapDetected)
+                {
+                    lastTapTime = AMotionEvent_getEventTime(event);
+                    lastTapX = AMotionEvent_getX(event, 0);
+                    lastTapY = AMotionEvent_getY(event, 0);
+                    
+                }
+            } break;
+        }
+        
+        
+        // Detect DRAG event
+        //int32_t action = AMotionEvent_getAction(event);
+
+        int32_t index = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
+        //uint32_t flags = action & AMOTION_EVENT_ACTION_MASK;
+        //event_ = event;
+
+        int32_t count = AMotionEvent_getPointerCount(event);
+
+        switch (flags)
+        {
+            case AMOTION_EVENT_ACTION_DOWN:
+            {
+                stdVector[indexPosition] = AMotionEvent_getPointerId(event, 0);
+                indexPosition++;
+                TraceLog(INFO, "ACTION_DOWN");
+                
+                //ret = GESTURE_STATE_START;
+            } break;
+            case AMOTION_EVENT_ACTION_POINTER_DOWN:
+            {
+                stdVector[indexPosition] = AMotionEvent_getPointerId(event, index);
+                indexPosition++;
+                TraceLog(INFO, "ACTION_POINTER_DOWN");
+                
+            } break;
+            case AMOTION_EVENT_ACTION_UP:
+            {
+                //int value = stdVector[indexPosition];
+                indexPosition--;
+                //ret = GESTURE_STATE_END;
+                TraceLog(INFO, "ACTION_UP");
+                
+            } break;
+            case AMOTION_EVENT_ACTION_POINTER_UP:
+            {
+                int32_t releasedPointerId = AMotionEvent_getPointerId(event, index);
+                
+                int i = 0;
+                for (i = 0; i < MAX_TOUCH_POINTS; i++)
+                {
+                    if (stdVector[i] == releasedPointerId)
+                    {
+                        for (int k = i; k < indexPosition - 1; k++)
+                        {
+                            stdVector[k] = stdVector[k + 1];
+                        }
+                        
+                        //indexPosition--;
+                        indexPosition = 0;
+                        break;
+                    }
+                }
+                
+                if (i <= 1)
+                {
+                    // Reset pinch or drag
+                    //if (count == 2) //ret = GESTURE_STATE_START;
+                }
+                TraceLog(INFO, "ACTION_POINTER_UP");
+                
+            } break;
+            case AMOTION_EVENT_ACTION_MOVE:
+            {
+                if (count == 1)
+                {
+                    //TraceLog(INFO, "DRAG gesture detected");
+                
+                    drag = true; //ret = GESTURE_STATE_MOVE;
+                }
+                else break;
+                TraceLog(INFO, "ACTION_MOVE");
+
+            } break;
+            case AMOTION_EVENT_ACTION_CANCEL: break;
+            default: break;
+        }
+
+        //--------------------------------------------------------------------
+        
         return 1;
     }
     else if (type == AINPUT_EVENT_TYPE_KEY)
@@ -1467,14 +1631,23 @@ static void PollInputEvents(void)
 
     // Keyboard polling
     // Automatically managed by GLFW3 through callback
-
+    lastKeyPressed = -1;
+    
+    // Register previous keys states
+    for (int i = 0; i < 512; i++) previousKeyState[i] = currentKeyState[i];
+    
+    // Register previous mouse states
+    for (int i = 0; i < 3; i++) previousMouseState[i] = currentMouseState[i];
+    
     glfwPollEvents();       // Register keyboard/mouse events
 #elif defined(PLATFORM_ANDROID)
 
     // TODO: Check virtual keyboard (?)
 
-    // Reset touchTap event
+    // Reset touch events
     touchTap = false;
+    doubleTap = false;
+    drag = false;
 
     // Poll Events (registered events)
     while ((ident = ALooper_pollAll(0, NULL, &events,(void**)&source)) >= 0)
@@ -1623,14 +1796,17 @@ static void PollInputEvents(void)
 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");
+    if ((mouseStream = open(DEFAULT_MOUSE_DEV, O_RDONLY|O_NONBLOCK)) < 0)
+    {
+        TraceLog(WARNING, "Mouse device could not be opened, no mouse available");
+    }
     else
     {
         mouseReady = true;
 
-        int err = pthread_create(&mouseThreadId, NULL, &MouseThread, NULL);
+        int error = pthread_create(&mouseThreadId, NULL, &MouseThread, NULL);
 
-        if (err != 0) TraceLog(WARNING, "Error creating mouse input event thread");
+        if (error != 0) TraceLog(WARNING, "Error creating mouse input event thread");
         else TraceLog(INFO, "Mouse device initialized successfully");
     }
 }
@@ -1741,7 +1917,7 @@ static void RestoreKeyboard(void)
 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");
+    if ((gamepadStream = open(DEFAULT_GAMEPAD_DEV, O_RDONLY|O_NONBLOCK)) < 0) TraceLog(WARNING, "Gamepad device could not be opened, no gamepad available");
     else TraceLog(INFO, "Gamepad device initialized successfully");
 }
 #endif
@@ -1763,7 +1939,7 @@ 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);
+        TraceLog(WARNING, "DOWNSCALING: Required screen size (%ix%i) is bigger than display size (%ix%i)", screenWidth, screenHeight, displayWidth, displayHeight);
 
         // Downscaling to fit display with border-bars
         float widthRatio = (float)displayWidth/(float)screenWidth;
@@ -1832,6 +2008,7 @@ static void SetupFramebufferSize(int displayWidth, int displayHeight)
 // Plays raylib logo appearing animation
 static void LogoAnimation(void)
 {
+#ifndef PLATFORM_WEB
     int logoPositionX = screenWidth/2 - 128;
     int logoPositionY = screenHeight/2 - 128;
 
@@ -1949,6 +2126,8 @@ static void LogoAnimation(void)
         EndDrawing();
         //----------------------------------------------------------------------------------
     }
+#endif
 
     showLogo = false;  // Prevent for repeating when reloading window (Android)
 }
+

+ 8 - 6
src/makefile

@@ -1,8 +1,6 @@
 #**************************************************************************************************
 #
-#   raylib for Raspberry Pi and Windows desktop
-#
-#   makefile for library compilation (raylib.a)
+#   raylib makefile for desktop platforms, Raspberry Pi and HTML5 (emscripten)
 #
 #   Copyright (c) 2014 Ramon Santamaria (Ray San - [email protected])
 #    
@@ -23,8 +21,8 @@
 #
 #**************************************************************************************************
 
-# define raylib platform (by default, compile for RPI)
-# Other possible platforms: PLATFORM_DESKTOP_WIN PLATFORM_DESKTOP_LINUX PLATFORM_WEB
+# define raylib platform to compile for
+# possible platforms: PLATFORM_DESKTOP PLATFORM_RPI PLATFORM_WEB
 PLATFORM ?= PLATFORM_DESKTOP
 
 # define raylib graphics api depending on selected platform
@@ -81,7 +79,7 @@ default: raylib
 # compile raylib library
 raylib: $(OBJS)
 ifeq ($(PLATFORM),PLATFORM_WEB)
-	emcc -O1 $(OBJS) -o raylib.bc
+	emcc -O1 $(OBJS) -o libraylib.bc
 else
 	ar rcs libraylib.a $(OBJS)
 endif
@@ -135,9 +133,13 @@ else
 ifeq ($(PLATFORM),PLATFORM_DESKTOP_LINUX)
 	find . -type f -executable -delete
 	rm -f *.o libraylib.a
+else
+ifeq ($(PLATFORM),PLATFORM_WEB)
+	del *.o libraylib.bc
 else
 	del *.o libraylib.a
 endif
+endif
 endif
 	@echo Cleaning done
 

+ 36 - 6
src/models.c

@@ -50,7 +50,7 @@
 //----------------------------------------------------------------------------------
 // Global Variables Definition
 //----------------------------------------------------------------------------------
-// It's lonely here...
+extern unsigned int whiteTexture;
 
 //----------------------------------------------------------------------------------
 // Module specific Functions Declaration
@@ -476,7 +476,7 @@ void DrawQuad(Vector3 vertices[4], Vector2 textcoords[4], Vector3 normals[4], Co
 void DrawPlane(Vector3 centerPos, Vector2 size, Vector3 rotation, Color color)
 {
     // NOTE: QUADS usage require defining a texture on OpenGL 3.3+
-    if (rlGetVersion() != OPENGL_11) rlEnableTexture(1);    // Default white texture
+    if (rlGetVersion() != OPENGL_11) rlEnableTexture(whiteTexture);    // Default white texture
 
     // NOTE: Plane is always created on XZ ground and then rotated
     rlPushMatrix();
@@ -812,7 +812,7 @@ Model LoadHeightmap(Image heightmap, float maxHeight)
 }
 
 // Load a map image as a 3d model (cubes based)
-Model LoadCubesmap(Image cubesmap)
+Model LoadCubicmap(Image cubesmap)
 {
     VertexData vData;
 
@@ -1068,8 +1068,6 @@ Model LoadCubesmap(Image cubesmap)
     // Move data from mapVertices temp arays to vertices float array
     vData.vertexCount = vCounter;
 
-    //printf("Vertex count: %i\n", vCounter);
-
     vData.vertices = (float *)malloc(vData.vertexCount * 3 * sizeof(float));
     vData.normals = (float *)malloc(vData.vertexCount * 3 * sizeof(float));
     vData.texcoords = (float *)malloc(vData.vertexCount * 2 * sizeof(float));
@@ -1523,4 +1521,36 @@ static VertexData LoadOBJ(const char *fileName)
     TraceLog(INFO, "[%s] Model loaded successfully in RAM (CPU)", fileName);
 
     return vData;
-}
+}
+
+bool CheckCollisionSpheres(Vector3 centerA, float radiusA, Vector3 centerB, float radiusB)
+{
+
+    return false;
+}
+
+bool CheckCollisionBoxes(Vector3 minBBox1, Vector3 maxBBox1, Vector3 minBBox2, Vector3 maxBBox2)
+{
+    /*
+    // Get min and max vertex to construct bounds (AABB)
+    Vector3 minVertex = tempVertices[0]; 
+    Vector3 maxVertex = tempVertices[0];
+    
+    for (int i = 1; i < tempVertices.Count; i++)
+    {
+        minVertex = Vector3.Min(minVertex, tempVertices[i]);
+        maxVertex = Vector3.Max(maxVertex, tempVertices[i]);
+    }
+    
+    bounds = new BoundingBox(minVertex, maxVertex);
+    */
+    return false;
+}
+
+bool CheckCollisionBoxSphere(Vector3 minBBox, Vector3 maxBBox, Vector3 centerSphere, Vector3 radiusSphere)
+{
+
+    return false;
+}
+
+//BoundingBox GetCollisionArea(BoundingBox box1, BoundingBox box2)

+ 7 - 5
src/raylib.h

@@ -63,6 +63,7 @@
 //#define PLATFORM_DESKTOP      // Windows, Linux or OSX
 //#define PLATFORM_ANDROID      // Android device
 //#define PLATFORM_RPI          // Raspberry Pi
+//#define PLATFORM_WEB          // HTML5 (emscripten, asm.js)
 
 // Security check in case no PLATFORM_* defined
 #if !defined(PLATFORM_DESKTOP) && !defined(PLATFORM_ANDROID) && !defined(PLATFORM_RPI) && !defined(PLATFORM_WEB)
@@ -298,7 +299,7 @@ extern "C" {            // Prevents name mangling of functions
 //------------------------------------------------------------------------------------
 #if defined(PLATFORM_ANDROID)
 void InitWindow(int width, int height, struct android_app *state);  // Init Android activity
-#elif defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI)
+#elif defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) || defined(PLATFORM_WEB)
 void InitWindow(int width, int height, const char *title);  // Initialize Window and OpenGL Graphics
 #endif
 
@@ -311,6 +312,7 @@ void SetExitKey(int key);                                   // Set a custom key
 #endif
 int GetScreenWidth(void);                                   // Get current screen width
 int GetScreenHeight(void);                                  // Get current screen height
+int GetKeyPressed(void);                                    // Get latest key pressed
 
 void ClearBackground(Color color);                          // Sets Background Color
 void BeginDrawing(void);                                    // Setup drawing canvas to start drawing
@@ -330,8 +332,7 @@ int GetRandomValue(int min, int max);                       // Returns a random
 Color Fade(Color color, float alpha);                       // Color fade-in or fade-out, alpha goes from 0.0f to 1.0f
 
 void SetupFlags(char flags);                                // Enable some window configurations
-
-void ShowLogo(void);                                        // Activates raylib logo at startup
+void ShowLogo(void);                                        // Activates raylib logo at startup (can be done with flags)
 
 //------------------------------------------------------------------------------------
 // Input Handling Functions (Module: core)
@@ -410,6 +411,7 @@ Texture2D LoadTextureFromImage(Image image, bool genMipmaps);
 Texture2D CreateTexture(Image image, bool genMipmaps);                                             // [DEPRECATED] Same as LoadTextureFromImage()
 void UnloadImage(Image image);                                                                     // Unload image from CPU memory (RAM)
 void UnloadTexture(Texture2D texture);                                                             // Unload texture from GPU memory
+void ConvertToPOT(Image *image, Color fillColor);                                                  // Convert image to POT (power-of-two)
 
 void DrawTexture(Texture2D texture, int posX, int posY, Color tint);                               // Draw a Texture2D
 void DrawTextureV(Texture2D texture, Vector2 position, Color tint);                                // Draw a Texture2D with position defined as Vector2
@@ -460,7 +462,7 @@ void DrawGizmoEx(Vector3 position, Vector3 rotation, float scale);
 Model LoadModel(const char *fileName);                                                             // Load a 3d model (.OBJ)
 //Model LoadModelFromRES(const char *rresName, int resId);                                         // TODO: Load a 3d model from rRES file (raylib Resource)
 Model LoadHeightmap(Image heightmap, float maxHeight);                                             // Load a heightmap image as a 3d model
-Model LoadCubesmap(Image cubesmap);                                                                // Load a map image as a 3d model (cubes based)
+Model LoadCubicmap(Image cubicmap);                                                                // Load a map image as a 3d model (cubes based)
 void UnloadModel(Model model);                                                                     // Unload 3d model from memory
 void SetModelTexture(Model *model, Texture2D texture);                                             // Link a texture to a model
 
@@ -478,7 +480,7 @@ void InitAudioDevice(void);                                     // Initialize au
 void CloseAudioDevice(void);                                    // Close the audio device and context (and music stream)
 
 Sound LoadSound(char *fileName);                                // Load sound to memory
-Sound LoadSoundFromWave(Wave wave);                             // Load sound from wave data
+Sound LoadSoundFromWave(Wave wave);                             // Load sound to memory from wave data
 Sound LoadSoundFromRES(const char *rresName, int resId);        // Load sound to memory from rRES file (raylib Resource)
 void UnloadSound(Sound sound);                                  // Unload sound
 void PlaySound(Sound sound);                                    // Play a sound

+ 43 - 4
src/raymath.c

@@ -329,8 +329,6 @@ void MatrixInvert(Matrix *mat)
     // Calculate the invert determinant (inlined to avoid double-caching)
     float invDet = 1/(b00*b11 - b01*b10 + b02*b09 + b03*b08 - b04*b07 + b05*b06);
 
-    printf("%f\n", invDet);
-
     temp.m0 = (a11*b11 - a12*b10 + a13*b09)*invDet;
     temp.m1 = (-a01*b11 + a02*b10 - a03*b09)*invDet;
     temp.m2 = (a31*b05 - a32*b04 + a33*b03)*invDet;
@@ -492,6 +490,48 @@ Matrix MatrixRotate(float angleX, float angleY, float angleZ)
     return result;
 }
 
+/*
+Matrix MatrixRotate(float angle, float x, float y, float z)
+{
+    Matrix result = MatrixIdentity();
+
+    float c = cosf(angle*DEG2RAD);    // cosine
+    float s = sinf(angle*DEG2RAD);    // sine
+    float c1 = 1.0f - c;              // 1 - c
+    
+    float m0 = result.m0, m4 = result.m4, m8 = result.m8, m12 = result.m12,
+          m1 = result.m1, m5 = result.m5, m9 = result.m9,  m13 = result.m13,
+          m2 = result.m2, m6 = result.m6, m10 = result.m10, m14 = result.m14;
+
+    // build rotation matrix
+    float r0 = x * x * c1 + c;
+    float r1 = x * y * c1 + z * s;
+    float r2 = x * z * c1 - y * s;
+    float r4 = x * y * c1 - z * s;
+    float r5 = y * y * c1 + c;
+    float r6 = y * z * c1 + x * s;
+    float r8 = x * z * c1 + y * s;
+    float r9 = y * z * c1 - x * s;
+    float r10= z * z * c1 + c;
+
+    // multiply rotation matrix
+    result.m0 = r0*m0 + r4*m1 + r8*m2;
+    result.m1 = r1*m0 + r5*m1 + r9*m2;
+    result.m2 = r2*m0 + r6*m1 + r10*m2;
+    result.m4 = r0*m4 + r4*m5 + r8*m6;
+    result.m5 = r1*m4 + r5*m5 + r9*m6;
+    result.m6 = r2*m4 + r6*m5 + r10*m6;
+    result.m8 = r0*m8 + r4*m9 + r8*m10;
+    result.m9 = r1*m8 + r5*m9 + r9*m10;
+    result.m10 = r2*m8 + r6*m9 + r10*m10;
+    result.m12 = r0*m12+ r4*m13 + r8*m14;
+    result.m13 = r1*m12+ r5*m13 + r9*m14;
+    result.m14 = r2*m12+ r6*m13 + r10*m14;
+
+    return result;
+}
+*/
+
 // Create rotation matrix from axis and angle
 // TODO: Test this function
 // NOTE: NO prototype defined!
@@ -668,12 +708,11 @@ Matrix MatrixScale(float x, float y, float z)
 
 // Returns transformation matrix for a given translation, rotation and scale
 // NOTE: Transformation order is rotation -> scale -> translation
+// NOTE: Rotation angles should come in radians
 Matrix MatrixTransform(Vector3 translation, Vector3 rotation, Vector3 scale)
 {
     Matrix result = MatrixIdentity();
 
-	// TODO: Review, use DEG2RAD here?
-	//Matrix mRotation = MatrixRotate(rotation.x*DEG2RAD, rotation.y*DEG2RAD, rotation.z*DEG2RAD);
     Matrix mRotation = MatrixRotate(rotation.x, rotation.y, rotation.z);
     Matrix mScale = MatrixScale(scale.x, scale.y, scale.z);
     Matrix mTranslate = MatrixTranslate(translation.x, translation.y, translation.z);

+ 2 - 2
src/raymath.h

@@ -55,7 +55,7 @@
     } Vector3;
 #endif
 
-// Matrix type (OpenGL style 4x4 - right handed)
+// Matrix type (OpenGL style 4x4 - right handed, column major)
 typedef struct Matrix {
     float m0, m4, m8, m12;
     float m1, m5, m9, m13;
@@ -107,7 +107,7 @@ Matrix MatrixIdentity(void);                            // Returns identity matr
 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
-Matrix MatrixRotate(float angleX, float angleY, float angleZ); // Returns rotation matrix
+Matrix MatrixRotate(float axisX, float axisY, float axisZ); // Returns rotation matrix
 Matrix MatrixFromAxisAngle(Vector3 axis, float angle);      // Returns rotation matrix for an angle around an specified axis
 Matrix MatrixFromAxisAngle2(Vector3 axis, float angle);     // Returns rotation matrix for an angle around an specified axis (test another implemntation)
 Matrix MatrixFromQuaternion(Quaternion q);              // Returns rotation matrix for a given quaternion

+ 20 - 15
src/rlgl.c

@@ -169,7 +169,8 @@ static int tempBufferCount = 0;
 static bool useTempBuffer = false;
 
 // White texture useful for plain color polys (required by shader)
-static GLuint whiteTexture;
+// NOTE: It's required in shapes and models modules!
+extern unsigned int whiteTexture;
 
 // Support for VAOs (OpenGL ES2 could not support VAO extensions)
 static bool vaoSupported = false;
@@ -772,7 +773,7 @@ void rlglInit(void)
 #endif
 
 #if defined(GRAPHICS_API_OPENGL_ES2)
-	// NOTE: emscripten does not support VAOs
+	// NOTE: emscripten does not support VAOs natively, it uses emulation and it reduces overall performance...
 #if !defined(PLATFORM_WEB)
     glGenVertexArrays = (PFNGLGENVERTEXARRAYSOESPROC)eglGetProcAddress("glGenVertexArraysOES");
     glBindVertexArray = (PFNGLBINDVERTEXARRAYOESPROC)eglGetProcAddress("glBindVertexArrayOES");
@@ -1128,6 +1129,8 @@ void rlglDrawModel(Model model, Vector3 position, Vector3 rotation, Vector3 scal
 #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
     glUseProgram(shaderProgram);        // Use our shader
 
+    VectorScale(&rotation, DEG2RAD);
+    
     // Get transform matrix (rotation -> scale -> translation)
     Matrix transform = MatrixTransform(position, rotation, scale);
     Matrix modelviewworld = MatrixMultiply(transform, modelview);
@@ -1340,7 +1343,7 @@ unsigned int rlglLoadTexture(unsigned char *data, int width, int height, bool ge
     // Unbind current texture
     glBindTexture(GL_TEXTURE_2D, 0);
 
-    TraceLog(INFO, "[TEX ID %i] Texture created successfully (%i x %i)", id, width, height);
+    TraceLog(INFO, "[TEX ID %i] Texture created successfully (%ix%i)", id, width, height);
 
     return id;
 }
@@ -1690,33 +1693,35 @@ static GLuint LoadShaders(char *vertexFileName, char *fragmentFileName)
 }
 
 // Read shader text file
-static char *TextFileRead(char *fn)
+static char *TextFileRead(char *fileName)
 {
-    FILE *fp;
+    FILE *textFile;
     char *text = NULL;
 
     int count=0;
 
-    if (fn != NULL)
+    if (fileName != NULL)
     {
-        fp = fopen(fn,"rt");
+        textFile = fopen(fileName,"rt");
 
-        if (fp != NULL)
+        if (textFile != NULL)
         {
-            fseek(fp, 0, SEEK_END);
-            count = ftell(fp);
-            rewind(fp);
+            fseek(textFile, 0, SEEK_END);
+            count = ftell(textFile);
+            rewind(textFile);
 
             if (count > 0)
             {
                 text = (char *)malloc(sizeof(char) * (count+1));
-                count = fread(text, sizeof(char), count, fp);
+                count = fread(text, sizeof(char), count, textFile);
                 text[count] = '\0';
             }
 
-            fclose(fp);
+            fclose(textFile);
         }
+        else TraceLog(WARNING, "[%s] Text file could not be opened", fileName);
     }
+    
     return text;
 }
 
@@ -1992,7 +1997,7 @@ static int GenerateMipmaps(unsigned char *data, int baseWidth, int baseHeight)
         j++;
     }
 
-    TraceLog(DEBUG, "Mipmap base (%i, %i)", width, height);
+    TraceLog(DEBUG, "Mipmap base (%ix%i)", width, height);
 
     for (int mip = 1; mip < mipmapCount; mip++)
     {
@@ -2063,7 +2068,7 @@ static pixel *GenNextMipmap(pixel *srcData, int srcWidth, int srcHeight)
         }
     }
 
-    TraceLog(DEBUG, "Mipmap generated successfully (%i, %i)", width, height);
+    TraceLog(DEBUG, "Mipmap generated successfully (%ix%i)", width, height);
 
     return mipmap;
 }

+ 3 - 2
src/rlgl.h

@@ -72,8 +72,9 @@
     #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)
+    // NOTE: Reduce memory sizes for embedded systems (RPI and HTML5)
+    // NOTE: On HTML5 (emscripten) this is allocated on heap, by default it's only 16MB!...just take care...
+    #define MAX_LINES_BATCH         1024    // 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

+ 2 - 2
src/shapes.c

@@ -44,7 +44,7 @@
 //----------------------------------------------------------------------------------
 // Global Variables Definition
 //----------------------------------------------------------------------------------
-// It's lonely here...
+extern unsigned int whiteTexture;
 
 //----------------------------------------------------------------------------------
 // Module specific Functions Declaration
@@ -197,7 +197,7 @@ void DrawRectangleV(Vector2 position, Vector2 size, Color color)
     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
+        rlEnableTexture(whiteTexture); // Default white texture
 
         rlBegin(RL_QUADS);
             rlColor4ub(color.r, color.g, color.b, color.a);

+ 65 - 109
src/text.c

@@ -63,7 +63,6 @@ static SpriteFont defaultFont;        // Default font provided by raylib
 //----------------------------------------------------------------------------------
 static bool PixelIsMagenta(Color p);                // Check if a pixel is magenta
 static int ParseImageData(Color *imgDataPixel, int imgWidth, int imgHeight, Character **charSet);    // Parse image pixel data to obtain character set measures
-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);
@@ -195,9 +194,10 @@ SpriteFont LoadSpriteFont(const char *fileName)
         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, image.width, image.height);
-
+        
+#if defined(PLATFORM_RPI) || defined(PLATFORM_WEB)
+        ConvertToPOT(&image, MAGENTA);
+#endif
         // 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(image.pixels, image.width, image.height, &spriteFont.charSet);
@@ -207,40 +207,8 @@ 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(image.width);
-        int potHeight = GetNextPOT(image.height);
-
-        // Check if POT texture generation is required (if texture is not already POT)
-        if ((potWidth != image.width) || (potHeight != image.height))
-        {
-            Color *imgDataPixelPOT = NULL;
-
-            // Generate POT array from NPOT data
-            imgDataPixelPOT = (Color *)malloc(potWidth * potHeight * sizeof(Color));
-
-            for (int j = 0; j < potHeight; j++)
-            {
-                for (int i = 0; i < potWidth; 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;
-        }
-*/
         spriteFont.texture = LoadTextureFromImage(image, false); // Convert loaded image to OpenGL texture
+        
         UnloadImage(image);
     }
 
@@ -475,23 +443,6 @@ static int ParseImageData(Color *imgDataPixel, int imgWidth, int imgHeight, Char
     return index;
 }
 
-// Calculate next power-of-two value for a given num
-static int GetNextPOT(int num)
-{
-    if (num != 0)
-    {
-        num--;
-        num |= (num >> 1);     // Or first 2 bits
-        num |= (num >> 2);     // Or next 2 bits
-        num |= (num >> 4);     // Or next 4 bits
-        num |= (num >> 8);     // Or next 8 bits
-        num |= (num >> 16);    // Or next 16 bits
-        num++;
-    }
-
-    return num;
-}
-
 // Load a rBMF font file (raylib BitMap Font)
 static SpriteFont LoadRBMF(const char *fileName)
 {
@@ -517,91 +468,96 @@ static SpriteFont LoadRBMF(const char *fileName)
     Image image;
 
     rbmfInfoHeader rbmfHeader;
-    unsigned int *rbmfFileData;
-    unsigned char *rbmfCharWidthData;
+    unsigned int *rbmfFileData = NULL;
+    unsigned char *rbmfCharWidthData = NULL;
 
     int charsDivisor = 1;    // Every char is separated from the consecutive by a 1 pixel divisor, horizontally and vertically
 
     FILE *rbmfFile = fopen(fileName, "rb");        // Define a pointer to bitmap file and open it in read-binary mode
 
-    // TODO: Check if file could be loaded! (rbmfFile == NULL)?
+    if (rbmfFile == NULL)
+    {
+        TraceLog(WARNING, "[%s] rBMF font file could not be opened", fileName);
+    }
+    else
+    {
+        fread(&rbmfHeader, sizeof(rbmfInfoHeader), 1, rbmfFile);
 
-    fread(&rbmfHeader, sizeof(rbmfInfoHeader), 1, rbmfFile);
+        TraceLog(INFO, "[%s] Loading rBMF file, size: %ix%i, numChars: %i, charHeight: %i", fileName, rbmfHeader.imgWidth, rbmfHeader.imgHeight, rbmfHeader.numChars, rbmfHeader.charHeight);
 
-    TraceLog(INFO, "[%s] Loading rBMF file, size: %ix%i, numChars: %i, charHeight: %i", fileName, rbmfHeader.imgWidth, rbmfHeader.imgHeight, rbmfHeader.numChars, rbmfHeader.charHeight);
+        spriteFont.numChars = (int)rbmfHeader.numChars;
 
-    spriteFont.numChars = (int)rbmfHeader.numChars;
+        image.width = (int)rbmfHeader.imgWidth;
+        image.height = (int)rbmfHeader.imgHeight;
 
-    image.width = (int)rbmfHeader.imgWidth;
-    image.height = (int)rbmfHeader.imgHeight;
+        int numPixelBits = rbmfHeader.imgWidth * rbmfHeader.imgHeight / 32;
 
-    int numPixelBits = rbmfHeader.imgWidth * rbmfHeader.imgHeight / 32;
+        rbmfFileData = (unsigned int *)malloc(numPixelBits * sizeof(unsigned int));
 
-    rbmfFileData = (unsigned int *)malloc(numPixelBits * sizeof(unsigned int));
+        for(int i = 0; i < numPixelBits; i++) fread(&rbmfFileData[i], sizeof(unsigned int), 1, rbmfFile);
 
-    for(int i = 0; i < numPixelBits; i++) fread(&rbmfFileData[i], sizeof(unsigned int), 1, rbmfFile);
+        rbmfCharWidthData = (unsigned char *)malloc(spriteFont.numChars * sizeof(unsigned char));
 
-    rbmfCharWidthData = (unsigned char *)malloc(spriteFont.numChars * sizeof(unsigned char));
+        for(int i = 0; i < spriteFont.numChars; i++) fread(&rbmfCharWidthData[i], sizeof(unsigned char), 1, rbmfFile);
 
-    for(int i = 0; i < spriteFont.numChars; i++) fread(&rbmfCharWidthData[i], sizeof(unsigned char), 1, rbmfFile);
+        // Re-construct image from rbmfFileData
+        //-----------------------------------------
+        image.pixels = (Color *)malloc(image.width * image.height * sizeof(Color));
 
-    // Re-construct image from rbmfFileData
-    //-----------------------------------------
-    image.pixels = (Color *)malloc(image.width * image.height * sizeof(Color));
+        for (int i = 0; i < image.width * image.height; i++) image.pixels[i] = BLANK;        // Initialize array
 
-    for (int i = 0; i < image.width * image.height; i++) image.pixels[i] = BLANK;        // Initialize array
+        int counter = 0;        // Font data elements counter
 
-    int counter = 0;        // Font data elements counter
-
-    // Fill image data (convert from bit to pixel!)
-    for (int i = 0; i < image.width * image.height; i += 32)
-    {
-        for (int j = 31; j >= 0; j--)
+        // Fill image data (convert from bit to pixel!)
+        for (int i = 0; i < image.width * image.height; i += 32)
         {
-            if (BIT_CHECK(rbmfFileData[counter], j)) image.pixels[i+j] = WHITE;
-        }
+            for (int j = 31; j >= 0; j--)
+            {
+                if (BIT_CHECK(rbmfFileData[counter], j)) image.pixels[i+j] = WHITE;
+            }
 
-        counter++;
-    }
+            counter++;
+        }
 
-    TraceLog(INFO, "[%s] Image reconstructed correctly, now converting it to texture", fileName);
+        TraceLog(INFO, "[%s] Image reconstructed correctly, now converting it to texture", fileName);
 
-    spriteFont.texture = LoadTextureFromImage(image, false);
-    UnloadImage(image);     // Unload image data
+        spriteFont.texture = LoadTextureFromImage(image, false);
+        UnloadImage(image);     // Unload image data
 
-    TraceLog(INFO, "[%s] Starting charSet reconstruction", fileName);
+        //TraceLog(INFO, "[%s] Starting charSet reconstruction", fileName);
 
-    // Reconstruct charSet using rbmfCharWidthData, rbmfHeader.charHeight, charsDivisor, rbmfHeader.numChars
-    spriteFont.charSet = (Character *)malloc(spriteFont.numChars * sizeof(Character));     // Allocate space for our character data
+        // Reconstruct charSet using rbmfCharWidthData, rbmfHeader.charHeight, charsDivisor, rbmfHeader.numChars
+        spriteFont.charSet = (Character *)malloc(spriteFont.numChars * sizeof(Character));     // Allocate space for our character data
 
-    int currentLine = 0;
-    int currentPosX = charsDivisor;
-    int testPosX = charsDivisor;
+        int currentLine = 0;
+        int currentPosX = charsDivisor;
+        int testPosX = charsDivisor;
 
-    for (int i = 0; i < spriteFont.numChars; i++)
-    {
-        spriteFont.charSet[i].value = (int)rbmfHeader.firstChar + i;
-        spriteFont.charSet[i].x = currentPosX;
-        spriteFont.charSet[i].y = charsDivisor + currentLine * ((int)rbmfHeader.charHeight + charsDivisor);
-        spriteFont.charSet[i].w = (int)rbmfCharWidthData[i];
-        spriteFont.charSet[i].h = (int)rbmfHeader.charHeight;
+        for (int i = 0; i < spriteFont.numChars; i++)
+        {
+            spriteFont.charSet[i].value = (int)rbmfHeader.firstChar + i;
+            spriteFont.charSet[i].x = currentPosX;
+            spriteFont.charSet[i].y = charsDivisor + currentLine * ((int)rbmfHeader.charHeight + charsDivisor);
+            spriteFont.charSet[i].w = (int)rbmfCharWidthData[i];
+            spriteFont.charSet[i].h = (int)rbmfHeader.charHeight;
 
-        testPosX += (spriteFont.charSet[i].w + charsDivisor);
+            testPosX += (spriteFont.charSet[i].w + charsDivisor);
 
-        if (testPosX > spriteFont.texture.width)
-        {
-            currentLine++;
-            currentPosX = 2 * charsDivisor + (int)rbmfCharWidthData[i];
-            testPosX = currentPosX;
+            if (testPosX > spriteFont.texture.width)
+            {
+                currentLine++;
+                currentPosX = 2 * charsDivisor + (int)rbmfCharWidthData[i];
+                testPosX = currentPosX;
 
-            spriteFont.charSet[i].x = charsDivisor;
-            spriteFont.charSet[i].y = charsDivisor + currentLine * (rbmfHeader.charHeight + charsDivisor);
+                spriteFont.charSet[i].x = charsDivisor;
+                spriteFont.charSet[i].y = charsDivisor + currentLine * (rbmfHeader.charHeight + charsDivisor);
+            }
+            else currentPosX = testPosX;
         }
-        else currentPosX = testPosX;
-    }
-
-    TraceLog(INFO, "[%s] rBMF file loaded correctly as SpriteFont", fileName);
 
+        TraceLog(INFO, "[%s] rBMF file loaded correctly as SpriteFont", fileName);
+    }
+    
     fclose(rbmfFile);
 
     free(rbmfFileData);                // Now we can free loaded data from RAM memory

+ 48 - 7
src/textures.c

@@ -126,7 +126,7 @@ Image LoadImage(const char *fileName)
             image.width = imgWidth;
             image.height = imgHeight;
 
-            TraceLog(INFO, "[%s] Image loaded successfully", fileName);
+            TraceLog(INFO, "[%s] Image loaded successfully (%ix%i)", fileName, image.width, image.height);
         }
         else TraceLog(WARNING, "[%s] Image could not be loaded, file not recognized", fileName);
     }
@@ -187,7 +187,10 @@ Image LoadImageFromRES(const char *rresName, int resId)
 
     FILE *rresFile = fopen(rresName, "rb");
 
-    if (!rresFile) TraceLog(WARNING, "[%s] Could not open raylib resource file", rresName);
+    if (rresFile == NULL) 
+    {
+        TraceLog(WARNING, "[%s] rRES raylib resource file could not be opened", rresName);
+    }
     else
     {
         // Read rres file (basic file check - id)
@@ -337,9 +340,12 @@ Texture2D LoadTexture(const char *fileName)
     else
     {
         Image image = LoadImage(fileName);
-
+        
         if (image.pixels != NULL)
         {
+#if defined(PLATFORM_RPI) || defined(PLATFORM_WEB)
+            ConvertToPOT(&image, BLANK);
+#endif
             texture = LoadTextureFromImage(image, false);
             UnloadImage(image);
         }
@@ -425,6 +431,41 @@ void UnloadTexture(Texture2D texture)
     rlDeleteTextures(texture.id);
 }
 
+// Convert image to POT (power-of-two)
+// NOTE: Requirement on OpenGL ES 2.0 (RPI, HTML5)
+void ConvertToPOT(Image *image, Color fillColor)
+{
+    // Just add the required amount of pixels at the right and bottom sides of image...
+    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 != image->width) || (potHeight != image->height))
+    {
+        Color *imgDataPixelPOT = NULL;
+
+        // Generate POT array from NPOT data
+        imgDataPixelPOT = (Color *)malloc(potWidth * potHeight * sizeof(Color));
+
+        for (int j = 0; j < potHeight; j++)
+        {
+            for (int i = 0; i < potWidth; i++)
+            {
+                if ((j < image->height) && (i < image->width)) imgDataPixelPOT[j*potWidth + i] = image->pixels[j*image->width + i];
+                else imgDataPixelPOT[j*potWidth + i] = fillColor;
+            }
+        }
+
+        TraceLog(WARNING, "Image converted to POT: (%ix%i) -> (%ix%i)", image->width, image->height, potWidth, potHeight);
+
+        free(image->pixels);
+
+        image->pixels = imgDataPixelPOT;
+        image->width = potWidth;
+        image->height = potHeight;
+    }
+}
+
 // Draw a Texture2D
 void DrawTexture(Texture2D texture, int posX, int posY, Color tint)
 {
@@ -559,7 +600,7 @@ static ImageEx LoadDDS(const char *fileName)
 
     if (ddsFile == NULL)
     {
-        TraceLog(WARNING, "DDS File could not be opened");
+        TraceLog(WARNING, "[%s] DDS image file could not be opened", fileName);
     }
     else
     {
@@ -570,7 +611,7 @@ static ImageEx LoadDDS(const char *fileName)
 
         if (strncmp(filecode, "DDS ", 4) != 0)
         {
-            TraceLog(WARNING, "DDS File does not seem to be valid");
+            TraceLog(WARNING, "[%s] DDS file does not seem to be a valid image", fileName);
             fclose(ddsFile);
         }
         else
@@ -705,7 +746,7 @@ static ImageEx LoadPKM(const char *fileName)
 
     if (pkmFile == NULL)
     {
-        TraceLog(WARNING, "[%s] PKM File could not be opened", fileName);
+        TraceLog(WARNING, "[%s] PKM image file could not be opened", fileName);
     }
     else
     {
@@ -716,7 +757,7 @@ static ImageEx LoadPKM(const char *fileName)
 
         if (strncmp(filecode, "PKM ", 4) != 0)
         {
-            TraceLog(WARNING, "[%s] PKM File does not seem to be valid", fileName);
+            TraceLog(WARNING, "[%s] PKM file does not seem to be a valid image", fileName);
             fclose(pkmFile);
         }
         else

+ 34 - 10
src/utils.c

@@ -133,18 +133,25 @@ void WriteBitmap(const char *fileName, unsigned char *imgData, int width, int he
 
     FILE *bmpFile = fopen(fileName, "wb");    // Define a pointer to bitmap file and open it in write-binary mode
 
-    // NOTE: fwrite parameters are: data pointer, size in bytes of each element to be written, number of elements, file-to-write pointer
-    fwrite(bmpFileHeader, sizeof(unsigned char), 14, bmpFile);    // Write BMP file header data
-    fwrite(bmpInfoHeader, sizeof(unsigned char), 40, bmpFile);    // Write BMP info header data
-
-    // Write pixel data to file
-    for (int y = 0; y < height ; y++)
+    if (bmpFile == NULL)
+    {
+        TraceLog(WARNING, "[%s] BMP file could not be created", fileName);
+    }
+    else
     {
-        for (int x = 0; x < width; x++)
+        // NOTE: fwrite parameters are: data pointer, size in bytes of each element to be written, number of elements, file-to-write pointer
+        fwrite(bmpFileHeader, sizeof(unsigned char), 14, bmpFile);    // Write BMP file header data
+        fwrite(bmpInfoHeader, sizeof(unsigned char), 40, bmpFile);    // Write BMP info header data
+
+        // Write pixel data to file
+        for (int y = 0; y < height ; y++)
         {
-            fputc(imgData[(x*4)+2 + (y*width*4)], bmpFile);
-            fputc(imgData[(x*4)+1 + (y*width*4)], bmpFile);
-            fputc(imgData[(x*4) + (y*width*4)], bmpFile);
+            for (int x = 0; x < width; x++)
+            {
+                fputc(imgData[(x*4)+2 + (y*width*4)], bmpFile);
+                fputc(imgData[(x*4)+1 + (y*width*4)], bmpFile);
+                fputc(imgData[(x*4) + (y*width*4)], bmpFile);
+            }
         }
     }
 
@@ -264,6 +271,23 @@ const char *GetExtension(const char *fileName)
     return (dot + 1);
 }
 
+// Calculate next power-of-two value for a given num
+int GetNextPOT(int num)
+{
+    if (num != 0)
+    {
+        num--;
+        num |= (num >> 1);     // Or first 2 bits
+        num |= (num >> 2);     // Or next 2 bits
+        num |= (num >> 4);     // Or next 4 bits
+        num |= (num >> 8);     // Or next 8 bits
+        num |= (num >> 16);    // Or next 16 bits
+        num++;
+    }
+
+    return num;
+}
+
 //----------------------------------------------------------------------------------
 // Module specific Functions Definition
 //----------------------------------------------------------------------------------

+ 1 - 0
src/utils.h

@@ -77,6 +77,7 @@ void WritePNG(const char *fileName, unsigned char *imgData, int width, int heigh
 
 void TraceLog(int msgType, const char *text, ...);  // Outputs a trace log message
 const char *GetExtension(const char *fileName);     // Returns extension of a filename
+int GetNextPOT(int num);                            // Calculate next power-of-two value for a given num
 
 #if defined(PLATFORM_ANDROID)
 void InitAssetManager(AAssetManager *manager);  // Initialize asset manager from android app