From 905b6ec53df01a4f660c12c08c32e2cc301f7ad6 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Wed, 31 Dec 2014 18:03:32 +0100 Subject: [PATCH] Added full support for HTML5 (emscripten) Corrected some bugs on the way... Automatically convert textures to POT on RPI and WEB --- src/audio.c | 49 ++++++---- src/core.c | 251 ++++++++++++++++++++++++++++++++++++++++++------- src/makefile | 14 +-- src/models.c | 42 +++++++-- src/raylib.h | 12 ++- src/raymath.c | 47 ++++++++- src/raymath.h | 4 +- src/rlgl.c | 35 ++++--- src/rlgl.h | 5 +- src/shapes.c | 4 +- src/text.c | 198 +++++++++++++++----------------------- src/textures.c | 55 +++++++++-- src/utils.c | 44 +++++++-- src/utils.h | 1 + 14 files changed, 527 insertions(+), 234 deletions(-) diff --git a/src/audio.c b/src/audio.c index d6d16360b..40c248951 100644 --- a/src/audio.c +++ b/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); diff --git a/src/core.c b/src/core.c index 1488072ef..5ff44f89c 100644 --- a/src/core.c +++ b/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) } + diff --git a/src/makefile b/src/makefile index 88d2258cb..501bd0c91 100644 --- a/src/makefile +++ b/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 - raysan@raysanweb.com) # @@ -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 diff --git a/src/models.c b/src/models.c index 539af3f91..e8e4f635a 100644 --- a/src/models.c +++ b/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; -} \ No newline at end of file +} + +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) \ No newline at end of file diff --git a/src/raylib.h b/src/raylib.h index 89648ef12..d5be99908 100644 --- a/src/raylib.h +++ b/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 diff --git a/src/raymath.c b/src/raymath.c index 56b79b424..ed45ee920 100644 --- a/src/raymath.c +++ b/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); diff --git a/src/raymath.h b/src/raymath.h index 334df4f30..c8c1a26c3 100644 --- a/src/raymath.h +++ b/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 diff --git a/src/rlgl.c b/src/rlgl.c index 43052237d..4f033ad11 100644 --- a/src/rlgl.c +++ b/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; } diff --git a/src/rlgl.h b/src/rlgl.h index b1e16f3ad..5fa9c4862 100644 --- a/src/rlgl.h +++ b/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 diff --git a/src/shapes.c b/src/shapes.c index 6fa26bee1..d872eacfe 100644 --- a/src/shapes.c +++ b/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); diff --git a/src/text.c b/src/text.c index bc5eb7d3c..944818578 100644 --- a/src/text.c +++ b/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)? - - 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); - - spriteFont.numChars = (int)rbmfHeader.numChars; - - image.width = (int)rbmfHeader.imgWidth; - image.height = (int)rbmfHeader.imgHeight; - - int numPixelBits = rbmfHeader.imgWidth * rbmfHeader.imgHeight / 32; - - rbmfFileData = (unsigned int *)malloc(numPixelBits * sizeof(unsigned int)); - - for(int i = 0; i < numPixelBits; i++) fread(&rbmfFileData[i], sizeof(unsigned int), 1, rbmfFile); - - 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); - - // 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 - - 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) + if (rbmfFile == NULL) { - for (int j = 31; j >= 0; j--) + TraceLog(WARNING, "[%s] rBMF font file could not be opened", fileName); + } + else + { + 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); + + spriteFont.numChars = (int)rbmfHeader.numChars; + + image.width = (int)rbmfHeader.imgWidth; + image.height = (int)rbmfHeader.imgHeight; + + int numPixelBits = rbmfHeader.imgWidth * rbmfHeader.imgHeight / 32; + + rbmfFileData = (unsigned int *)malloc(numPixelBits * sizeof(unsigned int)); + + for(int i = 0; i < numPixelBits; i++) fread(&rbmfFileData[i], sizeof(unsigned int), 1, rbmfFile); + + 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); + + // 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 + + 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) { - 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; - - testPosX += (spriteFont.charSet[i].w + charsDivisor); - - if (testPosX > spriteFont.texture.width) + for (int i = 0; i < spriteFont.numChars; i++) { - currentLine++; - currentPosX = 2 * charsDivisor + (int)rbmfCharWidthData[i]; - testPosX = currentPosX; + 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; - spriteFont.charSet[i].x = charsDivisor; - spriteFont.charSet[i].y = charsDivisor + currentLine * (rbmfHeader.charHeight + charsDivisor); + testPosX += (spriteFont.charSet[i].w + charsDivisor); + + 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); + } + 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 diff --git a/src/textures.c b/src/textures.c index ce56d3a3a..f701c3800 100644 --- a/src/textures.c +++ b/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 diff --git a/src/utils.c b/src/utils.c index 27b728706..c3c20b472 100644 --- a/src/utils.c +++ b/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) { - for (int x = 0; x < width; x++) + TraceLog(WARNING, "[%s] BMP file could not be created", fileName); + } + else + { + // 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 //---------------------------------------------------------------------------------- diff --git a/src/utils.h b/src/utils.h index 784c7926a..882aebf60 100644 --- a/src/utils.h +++ b/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