diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..0db587396 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "cmake.configureOnOpen": true +} \ No newline at end of file diff --git a/examples/core/core_storage_values.c b/examples/core/core_storage_values.c index fbbbc5284..1592c0592 100644 --- a/examples/core/core_storage_values.c +++ b/examples/core/core_storage_values.c @@ -12,7 +12,10 @@ #include "raylib.h" // NOTE: Storage positions must start with 0, directly related to file memory layout -typedef enum { STORAGE_SCORE = 0, STORAGE_HISCORE } StorageData; +typedef enum { + STORAGE_POSITION_SCORE = 0, + STORAGE_POSITION_HISCORE = 1 +} StorageData; int main(void) { @@ -43,14 +46,14 @@ int main(void) if (IsKeyPressed(KEY_ENTER)) { - StorageSaveValue(STORAGE_SCORE, score); - StorageSaveValue(STORAGE_HISCORE, hiscore); + SaveStorageValue(STORAGE_POSITION_SCORE, score); + SaveStorageValue(STORAGE_POSITION_HISCORE, hiscore); } else if (IsKeyPressed(KEY_SPACE)) { // NOTE: If requested position could not be found, value 0 is returned - score = StorageLoadValue(STORAGE_SCORE); - hiscore = StorageLoadValue(STORAGE_HISCORE); + score = LoadStorageValue(STORAGE_POSITION_SCORE); + hiscore = LoadStorageValue(STORAGE_POSITION_HISCORE); } framesCounter++; diff --git a/examples/models/models_animation_gltf.c b/examples/models/models_animation_gltf.c new file mode 100644 index 000000000..2a980730a --- /dev/null +++ b/examples/models/models_animation_gltf.c @@ -0,0 +1,105 @@ +/******************************************************************************************* +* +* raylib [models] example - Load 3d model with animations and play them +* +* This example has been created using raylib 3.0 (www.raylib.com) +* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* +* Example contributed by Tyler Bezera (@gamerfiend) and reviewed by Ramon Santamaria (@raysan5) +* +* Copyright (c) 2019 Tyler Bezera (@gamerfiend) and Ramon Santamaria (@raysan5) +* +********************************************************************************************/ + +#include +#include "raylib.h" +#define RGLTFANIMATION_IMPLEMENTATION +#include "rgltfanim.h" + + + +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [models] example - model animation gltf"); + + // Define the camera to look into our 3d world + Camera camera = { 0 }; + camera.position = (Vector3){ 10.0f, 10.0f, 10.0f }; // Camera position + camera.target = (Vector3){ 0.0f, 0.0f, 0.0f }; // Camera looking at point + camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector (rotation towards target) + camera.fovy = 45.0f; // Camera field-of-view Y + camera.type = CAMERA_PERSPECTIVE; // Camera mode type + + // Load the animated model mesh and basic data + Model model = LoadModel("resources/models/RiggedFigure.glb"); + + Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position + + // Load animation data + // int animsCount = 0; + ModelAnimationsGLTF anims = LoadModelGLTFAnimations("resources/models/RiggedFigure.glb"); + // int animFrameCounter = 0; + + SetCameraMode(camera, CAMERA_FREE); // Set free camera mode + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + UpdateCamera(&camera); + + // Play animation when spacebar is held down + if (IsKeyDown(KEY_SPACE)) + { + // animFrameCounter++; + UpdateModelAnimationGLTF(model, 0, 0.5); + // if (animFrameCounter >= anims[0].frameCount) animFrameCounter = 0; + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + BeginMode3D(camera); + + DrawModelEx(model, position, (Vector3){ 1.0f, 0.0f, 0.0f }, -90.0f, (Vector3){ 1.0f, 1.0f, 1.0f }, WHITE); + + for (int i = 0; i < model.boneCount; i++) + { + //DrawCube(anims[0].framePoses[animFrameCounter][i].translation, 0.2f, 0.2f, 0.2f, RED); + } + + DrawGrid(10, 1.0f); // Draw a grid + + EndMode3D(); + + DrawText("PRESS SPACE to PLAY MODEL ANIMATION", 10, 10, 20, MAROON); + DrawText("(c) Guy IQM 3D model by @culacant", screenWidth - 200, screenHeight - 20, 10, GRAY); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // Unload model animations data + // for (int i = 0; i < animsCount; i++) UnloadModelAnimation(anims[i]); + // RL_FREE(anims); + + UnloadModel(model); // Unload model + + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} diff --git a/examples/models/resources/models/RiggedFigure.glb b/examples/models/resources/models/RiggedFigure.glb new file mode 100644 index 000000000..d1b4f4d4f Binary files /dev/null and b/examples/models/resources/models/RiggedFigure.glb differ diff --git a/examples/models/rgltfanim.h b/examples/models/rgltfanim.h new file mode 100644 index 000000000..c48bc7d8b --- /dev/null +++ b/examples/models/rgltfanim.h @@ -0,0 +1,257 @@ +/********************************************************************************************** +* +* raylib.gltfanimation - Simple module to have animation support with GLTF models, based on the +* implementation work for Google's Filament engine. +* +* CONFIGURATION: +* +* #define RGLTFANIMATION_IMPLEMENTATION +* Generates the implementation of the library into the included file. +* If not defined, the library is in header only mode and can be included in other headers +* or source files without problems. But only ONE file should hold the implementation. +* +* LICENSE: zlib/libpng +* +* Copyright (c) 2020 Tyler Bezera and Ramon Santamaria +* +* This software is provided "as-is", without any express or implied warranty. In no event +* will the authors be held liable for any damages arising from the use of this software. +* +* Permission is granted to anyone to use this software for any purpose, including commercial +* applications, and to alter it and redistribute it freely, subject to the following restrictions: +* +* 1. The origin of this software must not be misrepresented; you must not claim that you +* wrote the original software. If you use this software in a product, an acknowledgment +* in the product documentation would be appreciated but is not required. +* +* 2. Altered source versions must be plainly marked as such, and must not be misrepresented +* as being the original software. +* +* 3. This notice may not be removed or altered from any source distribution. +* +**********************************************************************************************/ + +#ifndef RGLTFANIM_H +#define RGLTFANIM_H + +#include "raylib.h" +#include "../../src/external/cgltf.h" + +typedef enum +{ + TYPE_LINEAR, + TYPE_STEP, + TYPE_CUBICSPLINE +} AnimationGLTFInterpolationType; + +typedef enum +{ + TYPE_TRANSLATION, + TYPE_ROTATION, + TYPE_SCALE, + TYPE_WEIGHTS +} AnimationGLTFPathType; + +typedef struct ModelGLTFAnimationSampler +{ + AnimationGLTFInterpolationType interpolationType; + float *sourceValues; + int sourceValuesCount; +} ModelGLTFAnimationSampler; + +typedef struct ModelGLTFAnimationChannel +{ + AnimationGLTFPathType pathType; + ModelGLTFAnimationSampler *sourceData; + Model targetModel; +} ModelGLTFAnimationChannel; + +typedef struct ModelAnimationGLTF +{ + char animationName[50]; + + ModelGLTFAnimationSampler *samplers; + int samplersCount; + + ModelGLTFAnimationChannel *channels; + int channelsCount; + + float duration; + float start; + float end; +} ModelAnimationGLTF; + +typedef struct ModelAnimationsGLTF +{ + ModelAnimationGLTF *animations; + int animationsCount; + + Matrix *boneMatrices; + int boneMatricesCount; +} ModelAnimationsGLTF; + +#ifdef __cplusplus +extern "C" +{ // Prevents name mangling of functions +#endif + + //---------------------------------------------------------------------------------- + // Module Functions Declaration + //---------------------------------------------------------------------------------- + ModelAnimationsGLTF LoadModelGLTFAnimations(const char *filename); + void UpdateModelAnimationGLTF(Model model, int animationIndex, float time); + void createSampler(cgltf_animation_sampler *src, ModelGLTFAnimationSampler *dst); + +#ifdef __cplusplus +} +#endif + +#endif //RGLTFANIM_H + +/*********************************************************************************** +* +* RGLTFANIM IMPLEMENTATION +* +************************************************************************************/ + +#if defined(RGLTFANIMATION_IMPLEMENTATION) +#include "raylib.h" +#include +//#define CGLTF_IMPLEMENTATION +#include "../../src/external/cgltf.h" + +void createSampler(cgltf_animation_sampler *src, ModelGLTFAnimationSampler *dst) { + //uint8_t *tileLineBlob = (uint8_t*)src->input->buffer_view->buffer->data; + //float *tileLineFloats = (float*)(tileLineBlob + src->input->offset + src->input->buffer_view->offset); + + for (int i = 0, len = src->input->count; i < len; ++i) { + + //TODO: Need to support map + //dst->times + + switch (src->output->type) { + case cgltf_type_scalar: + dst->sourceValues = RL_CALLOC(src->output->count, sizeof(float)); + cgltf_accessor_unpack_floats(src->output, &dst->sourceValues[0], src->output->count); + break; + + case cgltf_type_vec3: + dst->sourceValues = RL_CALLOC(src->output->count * 3, sizeof(float)); + cgltf_accessor_unpack_floats(src->output, &dst->sourceValues[0], src->output->count * 3); + break; + + case cgltf_type_vec4: + dst->sourceValues = RL_CALLOC(src->output->count * 3, sizeof(float)); + cgltf_accessor_unpack_floats(src->output, &dst->sourceValues[0], src->output->count * 3); + break; + default: + break; + } + + switch (src->interpolation) { + case cgltf_interpolation_type_linear: + dst->interpolationType = TYPE_LINEAR; + break; + + case cgltf_interpolation_type_step: + dst->interpolationType = TYPE_STEP; + break; + + case cgltf_interpolation_type_cubic_spline: + dst->interpolationType = TYPE_CUBICSPLINE; + break; + } + } +} + +void setTransformType(cgltf_animation_channel *src, ModelGLTFAnimationChannel *dst) { + switch (src->target_path) { + case cgltf_animation_path_type_translation: + dst->pathType = TYPE_TRANSLATION; + break; + + case cgltf_animation_path_type_rotation: + dst->pathType = TYPE_ROTATION; + break; + + case cgltf_animation_path_type_scale: + dst->pathType = TYPE_SCALE; + break; + + case cgltf_animation_path_type_weights: + dst->pathType = TYPE_WEIGHTS; + break; + } +} + +ModelAnimationsGLTF LoadModelGLTFAnimations(const char *fileName) { + + ModelAnimationsGLTF animationsGLTF = { 0 }; + // glTF file loading + FILE *gltfFile = fopen(fileName, "rb"); + + if (gltfFile == NULL) + { + TraceLog(LOG_WARNING, "[%s] glTF file could not be opened", fileName); + return animationsGLTF; + } + + fseek(gltfFile, 0, SEEK_END); + int size = ftell(gltfFile); + fseek(gltfFile, 0, SEEK_SET); + + void *buffer = RL_MALLOC(size); + fread(buffer, size, 1, gltfFile); + + fclose(gltfFile); + + // glTF data loading + cgltf_options options = { 0 }; + cgltf_data *data = NULL; + cgltf_result result = cgltf_parse(&options, buffer, size, &data); + + if (result == cgltf_result_success) + { + // Read data buffers + result = cgltf_load_buffers(&options, data, fileName); + if (result != cgltf_result_success) TraceLog(LOG_INFO, "[%s][%s] Error loading mesh/material buffers", fileName, (data->file_type == 2)? "glb" : "gltf"); + + //Animation count + int animationsCount = data->animations_count; + + //Initialize our animations array + animationsGLTF.animationsCount = animationsCount; + animationsGLTF.animations = RL_CALLOC(animationsCount, sizeof(ModelAnimationGLTF)); + + //Loop through each Animation + for (int i = 0; i < animationsCount; i++) { + //Copy Animation name + if(data->animations[i].name) TextCopy(animationsGLTF.animations[i].animationName, data->animations[i].name); + + int samplerCount = data->animations[i].samplers_count; + animationsGLTF.animations[i].samplersCount = samplerCount; + animationsGLTF.animations[i].samplers = RL_CALLOC(samplerCount, sizeof(ModelGLTFAnimationSampler)); + for (int j = 0; j < samplerCount; j++) { + createSampler(&data->animations[i].samplers[j], &animationsGLTF.animations[i].samplers[j]); + //TODO handle map times + } + + int channelCount = data->animations[i].channels_count; + animationsGLTF.animations[i].channelsCount = channelCount; + animationsGLTF.animations[i].channels = RL_CALLOC(channelCount, sizeof(ModelGLTFAnimationChannel)); + for (int j = 0; j < channelCount; j++) { + //TODO Model stuff? Also... strange pointer arthmetic? data->animations[i].channels[j].sampler - data->animations[i].samplers?? + animationsGLTF.animations[i].channels[j].sourceData = &animationsGLTF.animations[i].samplers[data->animations[i].channels[j].sampler - data->animations[i].samplers]; + setTransformType(&data->animations[i].channels[j], &animationsGLTF.animations[i].channels[j]); + } + } + } + + return animationsGLTF; +} + +void UpdateModelAnimationGLTF(Model model, int animationIndex, float time) { + +} + +#endif \ No newline at end of file diff --git a/examples/others/rlgl_standalone.c b/examples/others/rlgl_standalone.c index 506ddfdba..8f2e2b467 100644 --- a/examples/others/rlgl_standalone.c +++ b/examples/others/rlgl_standalone.c @@ -54,6 +54,8 @@ #include // Windows/Context and inputs management +#include // Required for: printf() + #define RED (Color){ 230, 41, 55, 255 } // Red #define RAYWHITE (Color){ 245, 245, 245, 255 } // My own White (raylib logo) #define DARKGRAY (Color){ 80, 80, 80, 255 } // Dark Gray @@ -61,8 +63,8 @@ //---------------------------------------------------------------------------------- // Module specific Functions Declaration //---------------------------------------------------------------------------------- -static void ErrorCallback(int error, const char* description); -static void KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods); +static void ErrorCallback(int error, const char *description); +static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods); // Drawing functions (uses rlgl functionality) static void DrawGrid(int slices, float spacing); @@ -86,10 +88,10 @@ int main(void) if (!glfwInit()) { - TraceLog(LOG_WARNING, "GLFW3: Can not initialize GLFW"); + printf("GLFW3: Can not initialize GLFW\n"); return 1; } - else TraceLog(LOG_INFO, "GLFW3: GLFW initialized successfully"); + else printf("GLFW3: GLFW initialized successfully\n"); glfwWindowHint(GLFW_SAMPLES, 4); glfwWindowHint(GLFW_DEPTH_BITS, 16); @@ -105,7 +107,7 @@ int main(void) glfwTerminate(); return 2; } - else TraceLog(LOG_INFO, "GLFW3: Window created successfully"); + else printf("GLFW3: Window created successfully\n"); glfwSetWindowPos(window, 200, 200); @@ -215,13 +217,13 @@ int main(void) //---------------------------------------------------------------------------------- // GLFW3: Error callback -static void ErrorCallback(int error, const char* description) +static void ErrorCallback(int error, const char *description) { - TraceLog(LOG_ERROR, description); + fprintf(stderr, description); } // GLFW3: Keyboard callback -static void KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) +static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods) { if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) { diff --git a/src/Makefile b/src/Makefile index e7086e55c..1d5bbb83c 100644 --- a/src/Makefile +++ b/src/Makefile @@ -150,7 +150,7 @@ ifeq ($(PLATFORM),PLATFORM_WEB) EMSDK_PATH ?= C:/emsdk EMSCRIPTEN_PATH ?= $(EMSDK_PATH)/upstream/emscripten CLANG_PATH = $(EMSDK_PATH)/upstream/bin - PYTHON_PATH = $(EMSDK_PATH)/python/2.7.13.1_64bit/python-2.7.13.amd64 + PYTHON_PATH = $(EMSDK_PATH)/python/3.7.4_64bit NODE_PATH = $(EMSDK_PATH)/node/12.9.1_64bit/bin export PATH = $(EMSDK_PATH);$(EMSCRIPTEN_PATH);$(CLANG_PATH);$(NODE_PATH);$(PYTHON_PATH);C:\raylib\MinGW\bin:$$(PATH) endif @@ -291,9 +291,16 @@ endif ifeq ($(RAYLIB_BUILD_MODE),DEBUG) CFLAGS += -g + ifeq ($(PLATFORM),PLATFORM_WEB) + CFLAGS += -s ASSERTIONS=1 --profiling + endif endif ifeq ($(RAYLIB_BUILD_MODE),RELEASE) - CFLAGS += -O1 + ifeq ($(PLATFORM),PLATFORM_WEB) + CFLAGS += -Os + else + CFLAGS += -s -O1 + endif endif # Additional flags for compiler (if desired) @@ -311,18 +318,12 @@ ifeq ($(PLATFORM),PLATFORM_WEB) # -s ALLOW_MEMORY_GROWTH=1 # to allow memory resizing -> WARNING: Audio buffers could FAIL! # -s TOTAL_MEMORY=16777216 # to specify heap memory size (default = 16MB) # -s USE_PTHREADS=1 # multithreading support - # -s WASM=0 # disable Web Assembly, emitted by default - # -s EMTERPRETIFY=1 # enable emscripten code interpreter (very slow) - # -s EMTERPRETIFY_ASYNC=1 # support synchronous loops by emterpreter # -s FORCE_FILESYSTEM=1 # force filesystem to load/save files data # -s ASSERTIONS=1 # enable runtime checks for common memory allocation errors (-O1 and above turn it off) # --profiling # include information for code profiling # --memory-init-file 0 # to avoid an external memory initialization code file (.mem) # --preload-file resources # specify a resources folder for data compilation CFLAGS += -s USE_GLFW=3 - ifeq ($(RAYLIB_BUILD_MODE),DEBUG) - CFLAGS += -s ASSERTIONS=1 --profiling - endif endif ifeq ($(PLATFORM),PLATFORM_ANDROID) # Compiler flags for arquitecture @@ -477,7 +478,7 @@ endif raylib: $(OBJS) ifeq ($(PLATFORM),PLATFORM_WEB) # Compile raylib for web. - emcc -O1 $(OBJS) -o $(RAYLIB_RELEASE_PATH)/libraylib.bc + $(CC) -O1 $(OBJS) -o $(RAYLIB_RELEASE_PATH)/libraylib.bc @echo "raylib library generated (libraylib.bc)!" else ifeq ($(RAYLIB_LIBTYPE),SHARED) diff --git a/src/config.h b/src/config.h index 91e0decf6..9b1b1ee2c 100644 --- a/src/config.h +++ b/src/config.h @@ -53,13 +53,15 @@ // Wait for events passively (sleeping while no events) instead of polling them actively every frame //#define SUPPORT_EVENTS_WAITING 1 // Allow automatic screen capture of current screen pressing F12, defined in KeyCallback() -#define SUPPORT_SCREEN_CAPTURE 1 +//#define SUPPORT_SCREEN_CAPTURE 1 // Allow automatic gif recording of current screen pressing CTRL+F12, defined in KeyCallback() -#define SUPPORT_GIF_RECORDING 1 +//#define SUPPORT_GIF_RECORDING 1 // Allow scale all the drawn content to match the high-DPI equivalent size (only PLATFORM_DESKTOP) //#define SUPPORT_HIGH_DPI 1 // Support CompressData() and DecompressData() functions -#define SUPPORT_COMPRESSION_API 1 +#define SUPPORT_COMPRESSION_API 1 +// Support saving binary data automatically to a generated storage.data file. This file is managed internally. +#define SUPPORT_DATA_STORAGE 1 //------------------------------------------------------------------------------------ // Module: rlgl - Configuration Flags @@ -85,14 +87,15 @@ //#define SUPPORT_FILEFORMAT_BMP 1 //#define SUPPORT_FILEFORMAT_TGA 1 //#define SUPPORT_FILEFORMAT_JPG 1 -#define SUPPORT_FILEFORMAT_GIF 1 +#define SUPPORT_FILEFORMAT_GIF 1 //#define SUPPORT_FILEFORMAT_PSD 1 -#define SUPPORT_FILEFORMAT_DDS 1 -#define SUPPORT_FILEFORMAT_HDR 1 +//#define SUPPORT_FILEFORMAT_DDS 1 +//#define SUPPORT_FILEFORMAT_HDR 1 //#define SUPPORT_FILEFORMAT_KTX 1 //#define SUPPORT_FILEFORMAT_ASTC 1 //#define SUPPORT_FILEFORMAT_PKM 1 //#define SUPPORT_FILEFORMAT_PVR 1 + // Support image export functionality (.png, .bmp, .tga, .jpg) #define SUPPORT_IMAGE_EXPORT 1 // Support multiple image editing functions to scale, adjust colors, flip, draw on images, crop... @@ -117,7 +120,7 @@ // Selected desired model fileformats to be supported for loading #define SUPPORT_FILEFORMAT_OBJ 1 #define SUPPORT_FILEFORMAT_MTL 1 -#define SUPPORT_FILEFORMAT_IQM 1 +//#define SUPPORT_FILEFORMAT_IQM 1 #define SUPPORT_FILEFORMAT_GLTF 1 // Support procedural mesh generation functions, uses external par_shapes.h library // NOTE: Some generated meshes DO NOT include generated texture coordinates @@ -131,8 +134,8 @@ #define SUPPORT_FILEFORMAT_OGG 1 #define SUPPORT_FILEFORMAT_XM 1 #define SUPPORT_FILEFORMAT_MOD 1 -#define SUPPORT_FILEFORMAT_FLAC 1 -#define SUPPORT_FILEFORMAT_MP3 1 +//#define SUPPORT_FILEFORMAT_FLAC 1 +//#define SUPPORT_FILEFORMAT_MP3 1 //------------------------------------------------------------------------------------ // Module: utils - Configuration Flags diff --git a/src/core.c b/src/core.c index 814ed9f5f..b1eaf0f96 100644 --- a/src/core.c +++ b/src/core.c @@ -82,6 +82,9 @@ * provided by stb_image and stb_image_write libraries, so, those libraries must be enabled on textures module * for linkage * +* #define SUPPORT_DATA_STORAGE +* Support saving binary data automatically to a generated storage.data file. This file is managed internally. +* * DEPENDENCIES: * rglfw - Manage graphic device, OpenGL context and inputs on PLATFORM_DESKTOP (Windows, Linux, OSX. FreeBSD, OpenBSD, NetBSD, DragonFly) * raymath - 3D math functionality (Vector2, Vector3, Matrix, Quaternion) @@ -152,7 +155,6 @@ #endif #include // Required for: srand(), rand(), atexit() -#include // Required for: FILE, fopen(), fseek(), fread(), fwrite(), fclose() [Used in StorageSaveValue()/StorageLoadValue()] #include // Required for: strrchr(), strcmp(), strlen() #include // Required for: time() [Used in InitTimer()] #include // Required for: tan() [Used in BeginMode3D()] @@ -219,33 +221,33 @@ #include // Defines AWINDOW_FLAG_FULLSCREEN and others #include // Defines basic app state struct and manages activity - #include // Khronos EGL library - Native platform display device control functions - #include // Khronos OpenGL ES 2.0 library + #include // Khronos EGL library - Native platform display device control functions + #include // Khronos OpenGL ES 2.0 library #endif #if defined(PLATFORM_RPI) - #include // POSIX file control definitions - open(), creat(), fcntl() - #include // POSIX standard function definitions - read(), close(), STDIN_FILENO - #include // POSIX terminal control definitions - tcgetattr(), tcsetattr() - #include // POSIX threads management (inputs reading) - #include // POSIX directory browsing + #include // POSIX file control definitions - open(), creat(), fcntl() + #include // POSIX standard function definitions - read(), close(), STDIN_FILENO + #include // POSIX terminal control definitions - tcgetattr(), tcsetattr() + #include // POSIX threads management (inputs reading) + #include // POSIX directory browsing - #include // UNIX System call for device-specific input/output operations - ioctl() - #include // Linux: KDSKBMODE, K_MEDIUMRAM constants definition - #include // Linux: Keycodes constants definition (KEY_A, ...) - #include // Linux: Joystick support library + #include // UNIX System call for device-specific input/output operations - ioctl() + #include // Linux: KDSKBMODE, K_MEDIUMRAM constants definition + #include // Linux: Keycodes constants definition (KEY_A, ...) + #include // Linux: Joystick support library - #include "bcm_host.h" // Raspberry Pi VideoCore IV access functions + #include "bcm_host.h" // Raspberry Pi VideoCore IV access functions - #include "EGL/egl.h" // Khronos EGL library - Native platform display device control functions - #include "EGL/eglext.h" // Khronos EGL library - Extensions - #include "GLES2/gl2.h" // Khronos OpenGL ES 2.0 library + #include "EGL/egl.h" // Khronos EGL library - Native platform display device control functions + #include "EGL/eglext.h" // Khronos EGL library - Extensions + #include "GLES2/gl2.h" // Khronos OpenGL ES 2.0 library #endif #if defined(PLATFORM_UWP) - #include "EGL/egl.h" // Khronos EGL library - Native platform display device control functions - #include "EGL/eglext.h" // Khronos EGL library - Extensions - #include "GLES2/gl2.h" // Khronos OpenGL ES 2.0 library + #include "EGL/egl.h" // Khronos EGL library - Native platform display device control functions + #include "EGL/eglext.h" // Khronos EGL library - Extensions + #include "GLES2/gl2.h" // Khronos OpenGL ES 2.0 library #endif #if defined(PLATFORM_WEB) @@ -283,34 +285,36 @@ #endif #define MAX_GAMEPADS 4 // Max number of gamepads supported -#define MAX_GAMEPAD_BUTTONS 32 // Max bumber of buttons supported (per gamepad) #define MAX_GAMEPAD_AXIS 8 // Max number of axis supported (per gamepad) +#define MAX_GAMEPAD_BUTTONS 32 // Max bumber of buttons supported (per gamepad) #define MAX_CHARS_QUEUE 16 // Max number of characters in the input queue -#define STORAGE_FILENAME "storage.data" +#if defined(SUPPORT_DATA_STORAGE) + #define STORAGE_DATA_FILE "storage.data" +#endif //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- #if defined(PLATFORM_RPI) typedef struct { - pthread_t threadId; // Event reading thread id - int fd; // File descriptor to the device it is assigned to - int eventNum; // Number of 'event' device - Rectangle absRange; // Range of values for absolute pointing devices (touchscreens) - int touchSlot; // Hold the touch slot number of the currently being sent multitouch block - bool isMouse; // True if device supports relative X Y movements - bool isTouch; // True if device supports absolute X Y movements and has BTN_TOUCH - bool isMultitouch; // True if device supports multiple absolute movevents and has BTN_TOUCH - bool isKeyboard; // True if device has letter keycodes - bool isGamepad; // True if device has gamepad buttons + pthread_t threadId; // Event reading thread id + int fd; // File descriptor to the device it is assigned to + int eventNum; // Number of 'event' device + Rectangle absRange; // Range of values for absolute pointing devices (touchscreens) + int touchSlot; // Hold the touch slot number of the currently being sent multitouch block + bool isMouse; // True if device supports relative X Y movements + bool isTouch; // True if device supports absolute X Y movements and has BTN_TOUCH + bool isMultitouch; // True if device supports multiple absolute movevents and has BTN_TOUCH + bool isKeyboard; // True if device has letter keycodes + bool isGamepad; // True if device has gamepad buttons } InputEventWorker; -typedef struct{ - int Contents[8]; - char Head; - char Tail; +typedef struct { + int contents[8]; // Key events FIFO contents (8 positions) + char head; // Key events FIFO head position + char tail; // Key events FIFO tail position } KeyEventFifo; #endif @@ -498,7 +502,7 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) #endif #if defined(PLATFORM_WEB) -static EM_BOOL EmscriptenFullscreenChangeCallback(int eventType, const EmscriptenFullscreenChangeEvent *e, void *userData); +static EM_BOOL EmscriptenFullscreenChangeCallback(int eventType, const EmscriptenFullscreenChangeEvent *event, void *userData); static EM_BOOL EmscriptenKeyboardCallback(int eventType, const EmscriptenKeyboardEvent *keyEvent, void *userData); static EM_BOOL EmscriptenMouseCallback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData); static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData); @@ -703,7 +707,8 @@ void InitWindow(int width, int height, const char *title) #endif #if defined(PLATFORM_WEB) - emscripten_set_fullscreenchange_callback(0, 0, 1, EmscriptenFullscreenChangeCallback); + // Detect fullscreen change events + emscripten_set_fullscreenchange_callback("#canvas", NULL, 1, EmscriptenFullscreenChangeCallback); // Support keyboard events emscripten_set_keypress_callback("#canvas", NULL, 1, EmscriptenKeyboardCallback); @@ -867,9 +872,9 @@ bool IsWindowHidden(void) // Toggle fullscreen mode (only PLATFORM_DESKTOP) void ToggleFullscreen(void) { -#if defined(PLATFORM_DESKTOP) CORE.Window.fullscreen = !CORE.Window.fullscreen; // Toggle fullscreen flag +#if defined(PLATFORM_DESKTOP) // NOTE: glfwSetWindowMonitor() doesn't work properly (bugs) if (CORE.Window.fullscreen) { @@ -893,7 +898,10 @@ void ToggleFullscreen(void) } else glfwSetWindowMonitor(CORE.Window.handle, NULL, CORE.Window.position.x, CORE.Window.position.y, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); #endif - +#if defined(PLATFORM_WEB) + if (CORE.Window.fullscreen) EM_ASM(Module.requestFullscreen(false, false);); + else EM_ASM(document.exitFullscreen();); +#endif #if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) TRACELOG(LOG_WARNING, "Could not toggle to windowed mode"); #endif @@ -971,6 +979,14 @@ void SetWindowSize(int width, int height) #if defined(PLATFORM_DESKTOP) glfwSetWindowSize(CORE.Window.handle, width, height); #endif +#if defined(PLATFORM_WEB) + emscripten_set_canvas_size(width, height); // DEPRECATED! + + // TODO: Below functions should be used to replace previous one but + // they do not seem to work properly + //emscripten_set_canvas_element_size("canvas", width, height); + //emscripten_set_element_css_size("canvas", width, height); +#endif } // Show the window @@ -1622,7 +1638,7 @@ int GetFPS(void) static float history[FPS_CAPTURE_FRAMES_COUNT] = { 0 }; static float average = 0, last = 0; float fpsFrame = GetFrameTime(); - + if (fpsFrame == 0) return 0; if ((GetTime() - last) > FPS_STEP) @@ -1633,7 +1649,7 @@ int GetFPS(void) history[index] = fpsFrame/FPS_CAPTURE_FRAMES_COUNT; average += history[index]; } - + return (int)roundf(1.0f/average); } @@ -2168,80 +2184,84 @@ unsigned char *DecompressData(unsigned char *compData, int compDataLength, int * // Save integer value to storage file (to defined position) // NOTE: Storage positions is directly related to file memory layout (4 bytes each integer) -void StorageSaveValue(int position, int value) +void SaveStorageValue(int position, int value) { - FILE *storageFile = NULL; - +#if defined(SUPPORT_DATA_STORAGE) char path[512] = { 0 }; #if defined(PLATFORM_ANDROID) strcpy(path, CORE.Android.internalDataPath); strcat(path, "/"); - strcat(path, STORAGE_FILENAME); + strcat(path, STORAGE_DATA_FILE); #else - strcpy(path, STORAGE_FILENAME); + strcpy(path, STORAGE_DATA_FILE); #endif - // Try open existing file to append data - storageFile = fopen(path, "rb+"); + int dataSize = 0; + unsigned char *fileData = LoadFileData(path, &dataSize); - // If file doesn't exist, create a new storage data file - if (!storageFile) storageFile = fopen(path, "wb"); - - if (!storageFile) TRACELOG(LOG_WARNING, "Storage data file could not be created"); - else + if (fileData != NULL) { - // Get file size - fseek(storageFile, 0, SEEK_END); - int fileSize = ftell(storageFile); // Size in bytes - fseek(storageFile, 0, SEEK_SET); - - if (fileSize < (position*sizeof(int))) TRACELOG(LOG_WARNING, "Storage position could not be found"); + if (dataSize <= (position*sizeof(int))) + { + // Increase data size up to position and store value + dataSize = (position + 1)*sizeof(int); + fileData = (unsigned char *)RL_REALLOC(fileData, dataSize); + int *dataPtr = (int *)fileData; + dataPtr[position] = value; + } else { - fseek(storageFile, (position*sizeof(int)), SEEK_SET); - fwrite(&value, 1, sizeof(int), storageFile); + // Replace value on selected position + int *dataPtr = (int *)fileData; + dataPtr[position] = value; } - fclose(storageFile); + SaveFileData(path, fileData, dataSize); + RL_FREE(fileData); } + else + { + dataSize = (position + 1)*sizeof(int); + fileData = (unsigned char *)RL_MALLOC(dataSize); + int *dataPtr = (int *)fileData; + dataPtr[position] = value; + + SaveFileData(path, fileData, dataSize); + RL_FREE(fileData); + } +#endif } // Load integer value from storage file (from defined position) // NOTE: If requested position could not be found, value 0 is returned -int StorageLoadValue(int position) +int LoadStorageValue(int position) { int value = 0; - +#if defined(SUPPORT_DATA_STORAGE) char path[512] = { 0 }; #if defined(PLATFORM_ANDROID) strcpy(path, CORE.Android.internalDataPath); strcat(path, "/"); - strcat(path, STORAGE_FILENAME); + strcat(path, STORAGE_DATA_FILE); #else - strcpy(path, STORAGE_FILENAME); + strcpy(path, STORAGE_DATA_FILE); #endif - // Try open existing file to append data - FILE *storageFile = fopen(path, "rb"); + int dataSize = 0; + unsigned char *fileData = LoadFileData(path, &dataSize); - if (!storageFile) TRACELOG(LOG_WARNING, "Storage data file could not be found"); - else + if (fileData != NULL) { - // Get file size - fseek(storageFile, 0, SEEK_END); - int fileSize = ftell(storageFile); // Size in bytes - fseek(storageFile, 0, SEEK_SET); // Reset file pointer - - if (fileSize < (position*4)) TRACELOG(LOG_WARNING, "Storage position could not be found"); + if (dataSize < (position*4)) TRACELOG(LOG_WARNING, "Storage position could not be found"); else { - fseek(storageFile, (position*4), SEEK_SET); - fread(&value, 4, 1, storageFile); // Read 1 element of 4 bytes size + int *dataPtr = (int *)fileData; + value = dataPtr[position]; } - fclose(storageFile); + RL_FREE(fileData); } - +#endif return value; } @@ -2511,7 +2531,11 @@ bool IsMouseButtonReleased(int button) { bool released = false; -#if !defined(PLATFORM_ANDROID) +#if defined(PLATFORM_ANDROID) + # if defined(SUPPORT_GESTURES_SYSTEM) + released = GetGestureDetected() == GESTURE_TAP; + # endif +#else if ((CORE.Input.Mouse.currentButtonState[button] != CORE.Input.Mouse.previousButtonState[button]) && (GetMouseButtonStatus(button) == 0)) released = true; #endif @@ -3574,12 +3598,12 @@ static void PollInputEvents(void) for (int i = 0; i < 512; i++)CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i]; // Grab a keypress from the evdev fifo if avalable - if (CORE.Input.Keyboard.lastKeyPressed.Head != CORE.Input.Keyboard.lastKeyPressed.Tail) + if (CORE.Input.Keyboard.lastKeyPressed.head != CORE.Input.Keyboard.lastKeyPressed.tail) { - CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = CORE.Input.Keyboard.lastKeyPressed.Contents[CORE.Input.Keyboard.lastKeyPressed.Tail]; // Read the key from the buffer + CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = CORE.Input.Keyboard.lastKeyPressed.contents[CORE.Input.Keyboard.lastKeyPressed.tail]; // Read the key from the buffer CORE.Input.Keyboard.keyPressedQueueCount++; - CORE.Input.Keyboard.lastKeyPressed.Tail = (CORE.Input.Keyboard.lastKeyPressed.Tail + 1) & 0x07; // Increment the tail pointer forwards and binary wraparound after 7 (fifo is 8 elements long) + CORE.Input.Keyboard.lastKeyPressed.tail = (CORE.Input.Keyboard.lastKeyPressed.tail + 1) & 0x07; // Increment the tail pointer forwards and binary wraparound after 7 (fifo is 8 elements long) } // Register previous mouse states @@ -4230,37 +4254,84 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) { // If additional inputs are required check: // https://developer.android.com/ndk/reference/group/input + // https://developer.android.com/training/game-controllers/controller-input int type = AInputEvent_getType(event); + int source = AInputEvent_getSource(event); if (type == AINPUT_EVENT_TYPE_MOTION) { - // Get first touch position - CORE.Input.Touch.position[0].x = AMotionEvent_getX(event, 0); - CORE.Input.Touch.position[0].y = AMotionEvent_getY(event, 0); - - // Get second touch position - CORE.Input.Touch.position[1].x = AMotionEvent_getX(event, 1); - CORE.Input.Touch.position[1].y = AMotionEvent_getY(event, 1); - - // Useful functions for gamepad inputs: - //AMotionEvent_getAction() - //AMotionEvent_getAxisValue() - //AMotionEvent_getButtonState() - - // Gamepad dpad button presses capturing - // TODO: That's weird, key input (or button) - // shouldn't come as a TYPE_MOTION event... - int32_t keycode = AKeyEvent_getKeyCode(event); - if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_DOWN) + if ((source & AINPUT_SOURCE_JOYSTICK) == AINPUT_SOURCE_JOYSTICK || (source & AINPUT_SOURCE_GAMEPAD) == AINPUT_SOURCE_GAMEPAD) { - CORE.Input.Keyboard.currentKeyState[keycode] = 1; // Key down + // Get first touch position + CORE.Input.Touch.position[0].x = AMotionEvent_getX(event, 0); + CORE.Input.Touch.position[0].y = AMotionEvent_getY(event, 0); - CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = keycode; - CORE.Input.Keyboard.keyPressedQueueCount++; + // Get second touch position + CORE.Input.Touch.position[1].x = AMotionEvent_getX(event, 1); + CORE.Input.Touch.position[1].y = AMotionEvent_getY(event, 1); + + int32_t keycode = AKeyEvent_getKeyCode(event); + if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_DOWN) + { + CORE.Input.Keyboard.currentKeyState[keycode] = 1; // Key down + + CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = keycode; + CORE.Input.Keyboard.keyPressedQueueCount++; + } + else CORE.Input.Keyboard.currentKeyState[keycode] = 0; // Key up + + // Stop processing gamepad buttons + return 1; } - else CORE.Input.Keyboard.currentKeyState[keycode] = 0; // Key up + int32_t action = AMotionEvent_getAction(event); + unsigned int flags = action & AMOTION_EVENT_ACTION_MASK; + + // Simple touch position + if (flags == AMOTION_EVENT_ACTION_DOWN) + { + // Get first touch position + CORE.Input.Touch.position[0].x = AMotionEvent_getX(event, 0); + CORE.Input.Touch.position[0].y = AMotionEvent_getY(event, 0); + } + +#if defined(SUPPORT_GESTURES_SYSTEM) + GestureEvent gestureEvent; + + // Register touch actions + if (flags == AMOTION_EVENT_ACTION_DOWN) gestureEvent.touchAction = TOUCH_DOWN; + else if (flags == AMOTION_EVENT_ACTION_UP) gestureEvent.touchAction = TOUCH_UP; + else if (flags == AMOTION_EVENT_ACTION_MOVE) gestureEvent.touchAction = TOUCH_MOVE; + + // Register touch points count + // NOTE: Documentation says pointerCount is Always >= 1, + // but in practice it can be 0 or over a million + gestureEvent.pointCount = AMotionEvent_getPointerCount(event); + + // Only enable gestures for 1-3 touch points + if ((gestureEvent.pointCount > 0) && (gestureEvent.pointCount < 4)) + { + // Register touch points id + // NOTE: Only two points registered + gestureEvent.pointerId[0] = AMotionEvent_getPointerId(event, 0); + gestureEvent.pointerId[1] = AMotionEvent_getPointerId(event, 1); + + // Register touch points position + gestureEvent.position[0] = (Vector2){ AMotionEvent_getX(event, 0), AMotionEvent_getY(event, 0) }; + gestureEvent.position[1] = (Vector2){ AMotionEvent_getX(event, 1), AMotionEvent_getY(event, 1) }; + + // Normalize gestureEvent.position[x] for screenWidth and screenHeight + gestureEvent.position[0].x /= (float)GetScreenWidth(); + gestureEvent.position[0].y /= (float)GetScreenHeight(); + + gestureEvent.position[1].x /= (float)GetScreenWidth(); + gestureEvent.position[1].y /= (float)GetScreenHeight(); + + // Gesture data is sent to gestures system for processing + ProcessGestureEvent(gestureEvent); + } +#endif } else if (type == AINPUT_EVENT_TYPE_KEY) { @@ -4297,11 +4368,29 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) // Set default OS behaviour return 0; } + + return 0; } int32_t action = AMotionEvent_getAction(event); unsigned int flags = action & AMOTION_EVENT_ACTION_MASK; + // Support only simple touch position + if (flags == AMOTION_EVENT_ACTION_DOWN) + { + // Get first touch position + CORE.Input.Touch.position[0].x = AMotionEvent_getX(event, 0); + CORE.Input.Touch.position[0].y = AMotionEvent_getY(event, 0); + } + else if (flags == AMOTION_EVENT_ACTION_UP) + { + // Get first touch position + CORE.Input.Touch.position[0].x = 0; + CORE.Input.Touch.position[0].y = 0; + } + else // TODO:Not sure what else should be handled + return 0; + #if defined(SUPPORT_GESTURES_SYSTEM) GestureEvent gestureEvent = { 0 }; @@ -4337,17 +4426,6 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) // Gesture data is sent to gestures system for processing ProcessGestureEvent(gestureEvent); } -#else - // Support only simple touch position - if (flags == AMOTION_EVENT_ACTION_DOWN) - { - // Get first touch position - CORE.Input.Touch.position[0].x = AMotionEvent_getX(event, 0); - CORE.Input.Touch.position[0].y = AMotionEvent_getY(event, 0); - - CORE.Input.Touch.position[0].x /= (float)GetScreenWidth(); - CORE.Input.Touch.position[0].y /= (float)GetScreenHeight(); - } #endif return 0; @@ -4355,7 +4433,6 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) #endif #if defined(PLATFORM_WEB) - // Register fullscreen change events static EM_BOOL EmscriptenFullscreenChangeCallback(int eventType, const EmscriptenFullscreenChangeEvent *event, void *userData) { @@ -4368,10 +4445,12 @@ static EM_BOOL EmscriptenFullscreenChangeCallback(int eventType, const Emscripte if (event->isFullscreen) { + CORE.Window.fullscreen = true; TRACELOG(LOG_INFO, "Canvas scaled to fullscreen. ElementSize: (%ix%i), ScreenSize(%ix%i)", event->elementWidth, event->elementHeight, event->screenWidth, event->screenHeight); } else { + CORE.Window.fullscreen = false; TRACELOG(LOG_INFO, "Canvas scaled to windowed. ElementSize: (%ix%i), ScreenSize(%ix%i)", event->elementWidth, event->elementHeight, event->screenWidth, event->screenHeight); } @@ -4688,8 +4767,8 @@ static void InitEvdevInput(void) } // Reset keypress buffer - CORE.Input.Keyboard.lastKeyPressed.Head = 0; - CORE.Input.Keyboard.lastKeyPressed.Tail = 0; + CORE.Input.Keyboard.lastKeyPressed.head = 0; + CORE.Input.Keyboard.lastKeyPressed.tail = 0; // Reset keyboard key state for (int i = 0; i < 512; i++) CORE.Input.Keyboard.currentKeyState[i] = 0; @@ -5052,8 +5131,8 @@ static void *EventThread(void *arg) if (event.value > 0) { // Add the key int the fifo - CORE.Input.Keyboard.lastKeyPressed.Contents[CORE.Input.Keyboard.lastKeyPressed.Head] = keycode; // Put the data at the front of the fifo snake - CORE.Input.Keyboard.lastKeyPressed.Head = (CORE.Input.Keyboard.lastKeyPressed.Head + 1) & 0x07; // Increment the head pointer forwards and binary wraparound after 7 (fifo is 8 elements long) + CORE.Input.Keyboard.lastKeyPressed.contents[CORE.Input.Keyboard.lastKeyPressed.head] = keycode; // Put the data at the front of the fifo snake + CORE.Input.Keyboard.lastKeyPressed.head = (CORE.Input.Keyboard.lastKeyPressed.head + 1) & 0x07; // Increment the head pointer forwards and binary wraparound after 7 (fifo is 8 elements long) // TODO: This fifo is not fully threadsafe with multiple writers, so multiple keyboards hitting a key at the exact same time could miss a key (double write to head before it was incremented) } */ diff --git a/src/models.c b/src/models.c index 6d95441fe..c290bec5f 100644 --- a/src/models.c +++ b/src/models.c @@ -45,10 +45,10 @@ #include "utils.h" // Required for: fopen() Android mapping -#include // Required for: malloc(), free(), fabs() +#include // Required for: malloc(), free() #include // Required for: FILE, fopen(), fclose() #include // Required for: strncmp() [Used in LoadModelAnimations()], strlen() [Used in LoadTextureFromCgltfImage()] -#include // Required for: sinf(), cosf(), sqrtf() +#include // Required for: sinf(), cosf(), sqrtf(), fabsf() #include "rlgl.h" // raylib OpenGL abstraction layer to OpenGL 1.1, 2.1, 3.3+ or ES2 @@ -2513,9 +2513,9 @@ void DrawBoundingBox(BoundingBox box, Color color) { Vector3 size; - size.x = (float)fabs(box.max.x - box.min.x); - size.y = (float)fabs(box.max.y - box.min.y); - size.z = (float)fabs(box.max.z - box.min.z); + size.x = fabsf(box.max.x - box.min.x); + size.y = fabsf(box.max.y - box.min.y); + size.z = fabsf(box.max.z - box.min.z); Vector3 center = { box.min.x + size.x/2.0f, box.min.y + size.y/2.0f, box.min.z + size.z/2.0f }; @@ -2761,7 +2761,7 @@ RayHitInfo GetCollisionRayGround(Ray ray, float groundHeight) RayHitInfo result = { 0 }; - if (fabs(ray.direction.y) > EPSILON) + if (fabsf(ray.direction.y) > EPSILON) { float distance = (ray.position.y - groundHeight)/-ray.direction.y; diff --git a/src/raudio.c b/src/raudio.c index 060dedc82..8dd400156 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -80,7 +80,7 @@ #if defined(_WIN32) // To avoid conflicting windows.h symbols with raylib, some flags are defined -// WARNING: Those flags avoid inclusion of some Win32 headers that could be required +// WARNING: Those flags avoid inclusion of some Win32 headers that could be required // by user at some point and won't be included... //------------------------------------------------------------------------------------- @@ -165,6 +165,10 @@ typedef struct tagBITMAPINFOHEADER { #if defined(RAUDIO_STANDALONE) #include // Required for: strcmp() [Used in IsFileExtension()] + + #if !defined(TRACELOG) + #define TRACELOG(level, ...) (void)0 + #endif #endif #if defined(SUPPORT_FILEFORMAT_OGG) @@ -740,9 +744,8 @@ void ExportWave(Wave wave, const char *fileName) { // Export raw sample data (without header) // NOTE: It's up to the user to track wave parameters - FILE *rawFile = fopen(fileName, "wb"); - success = fwrite(wave.data, wave.sampleCount*wave.channels*wave.sampleSize/8, 1, rawFile); - fclose(rawFile); + SaveFileData(fileName, wave.data, wave.sampleCount*wave.channels*wave.sampleSize/8); + success = true; } if (success) TRACELOG(LOG_INFO, "Wave exported successfully: %s", fileName); @@ -1552,11 +1555,7 @@ static ma_uint32 ReadAudioBufferFramesInInternalFormat(AudioBuffer *audioBuffer, ma_uint32 subBufferSizeInFrames = (audioBuffer->sizeInFrames > 1)? audioBuffer->sizeInFrames/2 : audioBuffer->sizeInFrames; ma_uint32 currentSubBufferIndex = audioBuffer->frameCursorPos/subBufferSizeInFrames; - if (currentSubBufferIndex > 1) - { - TRACELOGD("Frame cursor position moved too far forward in audio stream"); - return 0; - } + if (currentSubBufferIndex > 1) return 0; // Another thread can update the processed state of buffers so // we just take a copy here to try and avoid potential synchronization problems @@ -1639,7 +1638,7 @@ static ma_uint32 ReadAudioBufferFramesInInternalFormat(AudioBuffer *audioBuffer, // Reads audio data from an AudioBuffer object in device format. Returned data will be in a format appropriate for mixing. static ma_uint32 ReadAudioBufferFramesInMixingFormat(AudioBuffer *audioBuffer, float *framesOut, ma_uint32 frameCount) { - // What's going on here is that we're continuously converting data from the AudioBuffer's internal format to the mixing format, which + // What's going on here is that we're continuously converting data from the AudioBuffer's internal format to the mixing format, which // should be defined by the output format of the data converter. We do this until frameCount frames have been output. The important // detail to remember here is that we never, ever attempt to read more input data than is required for the specified number of output // frames. This can be achieved with ma_data_converter_get_required_input_frame_count(). @@ -1663,7 +1662,7 @@ static ma_uint32 ReadAudioBufferFramesInMixingFormat(AudioBuffer *audioBuffer, f ma_uint64 inputFramesProcessedThisIteration = ReadAudioBufferFramesInInternalFormat(audioBuffer, inputBuffer, (ma_uint32)inputFramesToProcessThisIteration); /* Safe cast. */ ma_uint64 outputFramesProcessedThisIteration = outputFramesToProcessThisIteration; ma_data_converter_process_pcm_frames(&audioBuffer->converter, inputBuffer, &inputFramesProcessedThisIteration, runningFramesOut, &outputFramesProcessedThisIteration); - + totalOutputFramesProcessed += (ma_uint32)outputFramesProcessedThisIteration; /* Safe cast. */ if (inputFramesProcessedThisIteration < inputFramesToProcessThisIteration) @@ -1704,13 +1703,7 @@ static void OnSendAudioDataToDevice(ma_device *pDevice, void *pFramesOut, const while (1) { - if (framesRead > frameCount) - { - TRACELOGD("Mixed too many frames from audio buffer"); - break; - } - - if (framesRead == frameCount) break; + if (framesRead >= frameCount) break; // Just read as much data as we can from the stream ma_uint32 framesToRead = (frameCount - framesRead); @@ -2037,9 +2030,7 @@ static Wave LoadOGG(const char *fileName) wave.data = (short *)RL_MALLOC(wave.sampleCount*wave.channels*sizeof(short)); // NOTE: Returns the number of samples to process (be careful! we ask for number of shorts!) - int numSamplesOgg = stb_vorbis_get_samples_short_interleaved(oggFile, info.channels, (short *)wave.data, wave.sampleCount*wave.channels); - TRACELOGD("[%s] Samples obtained: %i", fileName, numSamplesOgg); - + stb_vorbis_get_samples_short_interleaved(oggFile, info.channels, (short *)wave.data, wave.sampleCount*wave.channels); TRACELOG(LOG_INFO, "[%s] OGG file loaded successfully (%i Hz, %i bit, %s)", fileName, wave.sampleRate, wave.sampleSize, (wave.channels == 1)? "Mono" : "Stereo"); stb_vorbis_close(oggFile); @@ -2115,29 +2106,6 @@ bool IsFileExtension(const char *fileName, const char *ext) return result; } - -// Show trace log messages (LOG_INFO, LOG_WARNING, LOG_ERROR, LOG_DEBUG) -void TRACELOG(int msgType, const char *text, ...) -{ - va_list args; - va_start(args, text); - - switch (msgType) - { - case LOG_INFO: fprintf(stdout, "INFO: "); break; - case LOG_ERROR: fprintf(stdout, "ERROR: "); break; - case LOG_WARNING: fprintf(stdout, "WARNING: "); break; - case LOG_DEBUG: fprintf(stdout, "DEBUG: "); break; - default: break; - } - - vfprintf(stdout, text, args); - fprintf(stdout, "\n"); - - va_end(args); - - if (msgType == LOG_ERROR) exit(1); -} #endif #undef AudioBuffer diff --git a/src/raylib.h b/src/raylib.h index fa76b4e17..48728fc17 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -107,10 +107,10 @@ #define RL_CALLOC(n,sz) calloc(n,sz) #endif #ifndef RL_REALLOC - #define RL_REALLOC(n,sz) realloc(n,sz) + #define RL_REALLOC(ptr,sz) realloc(ptr,sz) #endif #ifndef RL_FREE - #define RL_FREE(p) free(p) + #define RL_FREE(ptr) free(ptr) #endif // NOTE: MSC C++ compiler does not support compound literals (C99 feature) @@ -949,6 +949,8 @@ RLAPI void TakeScreenshot(const char *fileName); // Takes a scr RLAPI int GetRandomValue(int min, int max); // Returns a random value between min and max (both included) // Files management functions +RLAPI unsigned char *LoadFileData(const char *fileName, int *bytesRead); // Load file data as byte array (read) +RLAPI void SaveFileData(const char *fileName, void *data, int bytesToWrite); // Save data to file from byte array (write) RLAPI bool FileExists(const char *fileName); // Check if file exists RLAPI bool IsFileExtension(const char *fileName, const char *ext);// Check file extension RLAPI bool DirectoryExists(const char *dirPath); // Check if a directory path exists @@ -970,8 +972,8 @@ RLAPI unsigned char *CompressData(unsigned char *data, int dataLength, int *comp RLAPI unsigned char *DecompressData(unsigned char *compData, int compDataLength, int *dataLength); // Decompress data (DEFLATE algorythm) // Persistent storage management -RLAPI void StorageSaveValue(int position, int value); // Save integer value to storage file (to defined position) -RLAPI int StorageLoadValue(int position); // Load integer value from storage file (from defined position) +RLAPI void SaveStorageValue(int position, int value); // Save integer value to storage file (to defined position) +RLAPI int LoadStorageValue(int position); // Load integer value from storage file (from defined position) RLAPI void OpenURL(const char *url); // Open URL with default system browser (if available) diff --git a/src/raymath.h b/src/raymath.h index d1662507b..c2dbc61e4 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -1135,8 +1135,8 @@ RMDEF Quaternion QuaternionSlerp(Quaternion q1, Quaternion q2, float amount) else if (cosHalfTheta > 0.95f) result = QuaternionNlerp(q1, q2, amount); else { - float halfTheta = (float) acos(cosHalfTheta); - float sinHalfTheta = (float) sqrt(1.0f - cosHalfTheta*cosHalfTheta); + float halfTheta = acosf(cosHalfTheta); + float sinHalfTheta = sqrtf(1.0f - cosHalfTheta*cosHalfTheta); if (fabs(sinHalfTheta) < 0.001f) { @@ -1191,7 +1191,7 @@ RMDEF Quaternion QuaternionFromMatrix(Matrix mat) if (trace > 0.0f) { - float s = (float)sqrt(trace + 1)*2.0f; + float s = sqrtf(trace + 1)*2.0f; float invS = 1.0f/s; result.w = s*0.25f; @@ -1215,7 +1215,7 @@ RMDEF Quaternion QuaternionFromMatrix(Matrix mat) } else if (m11 > m22) { - float s = (float)sqrt(1.0f + m11 - m00 - m22)*2.0f; + float s = sqrtf(1.0f + m11 - m00 - m22)*2.0f; float invS = 1.0f/s; result.w = (mat.m8 - mat.m2)*invS; @@ -1225,7 +1225,7 @@ RMDEF Quaternion QuaternionFromMatrix(Matrix mat) } else { - float s = (float)sqrt(1.0f + m22 - m00 - m11)*2.0f; + float s = sqrtf(1.0f + m22 - m00 - m11)*2.0f; float invS = 1.0f/s; result.w = (mat.m1 - mat.m4)*invS; @@ -1317,8 +1317,8 @@ RMDEF void QuaternionToAxisAngle(Quaternion q, Vector3 *outAxis, float *outAngle Vector3 resAxis = { 0.0f, 0.0f, 0.0f }; float resAngle = 0.0f; - resAngle = 2.0f*(float)acos(q.w); - float den = (float)sqrt(1.0f - q.w*q.w); + resAngle = 2.0f*acosf(q.w); + float den = sqrtf(1.0f - q.w*q.w); if (den > 0.0001f) { diff --git a/src/rlgl.h b/src/rlgl.h index 65117120a..536f8103e 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -76,15 +76,7 @@ #endif // Support TRACELOG macros - #if defined(RLGL_SUPPORT_TRACELOG) - #define TRACELOG(level, ...) TraceLog(level, __VA_ARGS__) - - #if defined(RLGL_SUPPORT_TRACELOG_DEBUG) - #define TRACELOGD(...) TraceLog(LOG_DEBUG, __VA_ARGS__) - #else - #define TRACELOGD(...) (void)0 - #endif - #else + #if !defined(TRACELOG) #define TRACELOG(level, ...) (void)0 #define TRACELOGD(...) (void)0 #endif @@ -596,7 +588,6 @@ RLAPI void ToggleVrMode(void); // Enable/Disable VR exp RLAPI void BeginVrDrawing(void); // Begin VR simulator stereo rendering RLAPI void EndVrDrawing(void); // End VR simulator stereo rendering -RLAPI void TRACELOG(int msgType, const char *text, ...); // Show trace log messages (LOG_INFO, LOG_WARNING, LOG_ERROR, LOG_DEBUG) RLAPI int GetPixelDataSize(int width, int height, int format);// Get pixel data size in bytes (image or texture) #endif @@ -621,10 +612,10 @@ RLAPI int GetPixelDataSize(int width, int height, int format);// Get pixel data #endif #endif -#include // Required for: malloc(), free(), fabs() +#include // Required for: malloc(), free() #include // Required for: fopen(), fseek(), fread(), fclose() [LoadText] #include // Required for: strcmp(), strlen() [Used in rlglInit(), on extensions loading] -#include // Required for: atan2f() +#include // Required for: atan2f(), fabs() #if !defined(RLGL_STANDALONE) #include "raymath.h" // Required for: Vector3 and Matrix functions @@ -676,10 +667,6 @@ RLAPI int GetPixelDataSize(int width, int height, int format);// Get pixel data #include // OpenGL ES 2.0 extensions library #endif -#if defined(RLGL_STANDALONE) - #include // Required for: va_list, va_start(), vfprintf(), va_end() [Used in TraceLog()] -#endif - //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- @@ -3001,15 +2988,17 @@ Shader GetShaderDefault(void) // NOTE: text chars array should be freed manually char *LoadText(const char *fileName) { - FILE *textFile = NULL; char *text = NULL; if (fileName != NULL) { - textFile = fopen(fileName,"rt"); + FILE *textFile = fopen(fileName, "rt"); if (textFile != NULL) { + // WARNING: When reading a file as 'text' file, + // text mode causes carriage return-linefeed translation... + // ...but using fseek() should return correct byte-offset fseek(textFile, 0, SEEK_END); int size = ftell(textFile); fseek(textFile, 0, SEEK_SET); @@ -3018,6 +3007,15 @@ char *LoadText(const char *fileName) { text = (char *)RL_MALLOC(sizeof(char)*(size + 1)); int count = fread(text, sizeof(char), size, textFile); + + // WARNING: \r\n is converted to \n on reading, so, + // read bytes count gets reduced by the number of lines + if (count < size) + { + text = RL_REALLOC(text, count + 1); + } + + // zero-terminate the string text[count] = '\0'; } @@ -3664,7 +3662,7 @@ void SetVrConfiguration(VrDeviceInfo hmd, Shader distortion) // Compute distortion scale parameters // NOTE: To get lens max radius, lensShift must be normalized to [-1..1] - float lensRadius = (float)fabs(-1.0f - 4.0f*lensShift); + float lensRadius = fabsf(-1.0f - 4.0f*lensShift); float lensRadiusSq = lensRadius*lensRadius; float distortionScale = hmd.lensDistortionValues[0] + hmd.lensDistortionValues[1]*lensRadiusSq + @@ -4645,29 +4643,6 @@ static Color *GenNextMipmap(Color *srcData, int srcWidth, int srcHeight) #endif #if defined(RLGL_STANDALONE) -// Show trace log messages (LOG_INFO, LOG_WARNING, LOG_ERROR, LOG_DEBUG) -void TraceLog(int msgType, const char *text, ...) -{ - va_list args; - va_start(args, text); - - switch (msgType) - { - case LOG_INFO: fprintf(stdout, "INFO: "); break; - case LOG_ERROR: fprintf(stdout, "ERROR: "); break; - case LOG_WARNING: fprintf(stdout, "WARNING: "); break; - case LOG_DEBUG: fprintf(stdout, "DEBUG: "); break; - default: break; - } - - vfprintf(stdout, text, args); - fprintf(stdout, "\n"); - - va_end(args); - - if (msgType == LOG_ERROR) exit(1); -} - // Get pixel data size in bytes (image or texture) // NOTE: Size depends on pixel format int GetPixelDataSize(int width, int height, int format) diff --git a/src/shapes.c b/src/shapes.c index 4fb38867e..02c0eeb7e 100644 --- a/src/shapes.c +++ b/src/shapes.c @@ -42,8 +42,7 @@ #include "rlgl.h" // raylib OpenGL abstraction layer to OpenGL 1.1, 2.1, 3.3+ or ES2 -#include // Required for: fabs() -#include // Required for: sinf(), asinf(), cosf(), acosf(), sqrtf() +#include // Required for: sinf(), asinf(), cosf(), acosf(), sqrtf(), fabsf() //---------------------------------------------------------------------------------- // Defines and Macros @@ -1471,8 +1470,8 @@ bool CheckCollisionCircleRec(Vector2 center, float radius, Rectangle rec) int recCenterX = (int)(rec.x + rec.width/2.0f); int recCenterY = (int)(rec.y + rec.height/2.0f); - float dx = (float)fabs(center.x - recCenterX); - float dy = (float)fabs(center.y - recCenterY); + float dx = fabsf(center.x - (float)recCenterX); + float dy = fabsf(center.y - (float)recCenterY); if (dx > (rec.width/2.0f + radius)) { return false; } if (dy > (rec.height/2.0f + radius)) { return false; } @@ -1493,8 +1492,8 @@ Rectangle GetCollisionRec(Rectangle rec1, Rectangle rec2) if (CheckCollisionRecs(rec1, rec2)) { - float dxx = (float)fabs(rec1.x - rec2.x); - float dyy = (float)fabs(rec1.y - rec2.y); + float dxx = fabsf(rec1.x - rec2.x); + float dyy = fabsf(rec1.y - rec2.y); if (rec1.x <= rec2.x) { diff --git a/src/shell.html b/src/shell.html index 7db891bef..507b35acf 100644 --- a/src/shell.html +++ b/src/shell.html @@ -241,8 +241,8 @@ jwE50AGjLCVuS8Yt4H7OgZLKK5EKOsLviEWJSL/+0uMi7gLUSBseYwqEbXvSHCec1CJvZPyHCmYQffaB canvas: (function() { var canvas = document.querySelector('#canvas'); - // As a default initial behavior, pop up an alert when webgl context is lost. To make your - // application robust, you may want to override this behavior before shipping! + // As a default initial behavior, pop up an alert when webgl context is lost. + // To make your application robust, you may want to override this behavior before shipping! // See http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15.2 canvas.addEventListener("webglcontextlost", function(e) { alert('WebGL context lost. You will need to reload the page.'); e.preventDefault(); }, false); diff --git a/src/text.c b/src/text.c index d2562179b..58596f8fc 100644 --- a/src/text.c +++ b/src/text.c @@ -333,11 +333,11 @@ Font LoadFontEx(const char *fileName, int fontSize, int *fontChars, int charsCou { Font font = { 0 }; +#if defined(SUPPORT_FILEFORMAT_TTF) font.baseSize = fontSize; font.charsCount = (charsCount > 0)? charsCount : 95; font.chars = LoadFontData(fileName, font.baseSize, fontChars, font.charsCount, FONT_DEFAULT); -#if defined(SUPPORT_FILEFORMAT_TTF) if (font.chars != NULL) { Image atlas = GenImageFontAtlas(font.chars, &font.recs, font.charsCount, font.baseSize, 2, 0); @@ -354,7 +354,6 @@ Font LoadFontEx(const char *fileName, int fontSize, int *fontChars, int charsCou } else font = GetFontDefault(); #else - UnloadFont(font); font = GetFontDefault(); #endif @@ -498,24 +497,16 @@ CharInfo *LoadFontData(const char *fileName, int fontSize, int *fontChars, int c #if defined(SUPPORT_FILEFORMAT_TTF) // Load font data (including pixel data) from TTF file - // NOTE: Loaded information should be enough to generate font image atlas, - // using any packaging method - FILE *fontFile = fopen(fileName, "rb"); // Load font file + // NOTE: Loaded information should be enough to generate + // font image atlas, using any packaging method + int dataSize = 0; + unsigned char *fileData = LoadFileData(fileName, &dataSize); - if (fontFile != NULL) + if (fileData != NULL) { - fseek(fontFile, 0, SEEK_END); - long size = ftell(fontFile); // Get file size - fseek(fontFile, 0, SEEK_SET); // Reset file pointer - - unsigned char *fontBuffer = (unsigned char *)RL_MALLOC(size); - - fread(fontBuffer, size, 1, fontFile); - fclose(fontFile); - // Init font for data reading stbtt_fontinfo fontInfo; - if (!stbtt_InitFont(&fontInfo, fontBuffer, 0)) TRACELOG(LOG_WARNING, "Failed to init font!"); + if (!stbtt_InitFont(&fontInfo, fileData, 0)) TRACELOG(LOG_WARNING, "Failed to init font!"); // Calculate font scale factor float scaleFactor = stbtt_ScaleForPixelHeight(&fontInfo, (float)fontSize); @@ -595,12 +586,9 @@ CharInfo *LoadFontData(const char *fileName, int fontSize, int *fontChars, int c */ } - RL_FREE(fontBuffer); + RL_FREE(fileData); if (genFontChars) RL_FREE(fontChars); } - else TRACELOG(LOG_WARNING, "[%s] TTF file could not be opened", fileName); -#else - TRACELOG(LOG_WARNING, "[%s] TTF support is disabled", fileName); #endif return chars; @@ -1149,6 +1137,7 @@ const char *TextFormat(const char *text, ...) static int index = 0; char *currentBuffer = buffers[index]; + memset(currentBuffer, 0, MAX_TEXT_BUFFER_LENGTH); // Clear buffer before using va_list args; va_start(args, text); @@ -1445,7 +1434,7 @@ char *TextToUtf8(int *codepoints, int length) // Resize memory to text length + string NULL terminator void *ptr = RL_REALLOC(text, size + 1); - + if (ptr != NULL) text = (char *)ptr; return text; @@ -1655,7 +1644,6 @@ static Font LoadBMFont(const char *fileName) #define MAX_BUFFER_SIZE 256 Font font = { 0 }; - font.texture.id = 0; char buffer[MAX_BUFFER_SIZE] = { 0 }; char *searchPoint = NULL; diff --git a/src/textures.c b/src/textures.c index ade4acfa2..98b060d7b 100644 --- a/src/textures.c +++ b/src/textures.c @@ -64,9 +64,10 @@ #include "config.h" // Defines module configuration flags #endif -#include // Required for: malloc(), free(), fabs() +#include // Required for: malloc(), free() #include // Required for: FILE, fopen(), fclose(), fread() #include // Required for: strlen() [Used in ImageTextEx()] +#include // Required for: fabsf() #include "utils.h" // Required for: fopen() Android mapping @@ -195,6 +196,7 @@ Image LoadImage(const char *fileName) defined(SUPPORT_FILEFORMAT_TGA) || \ defined(SUPPORT_FILEFORMAT_GIF) || \ defined(SUPPORT_FILEFORMAT_PIC) || \ + defined(SUPPORT_FILEFORMAT_HDR) || \ defined(SUPPORT_FILEFORMAT_PSD) #define STBI_REQUIRED #endif @@ -225,53 +227,53 @@ Image LoadImage(const char *fileName) ) { #if defined(STBI_REQUIRED) - int imgWidth = 0; - int imgHeight = 0; - int imgBpp = 0; + // NOTE: Using stb_image to load images (Supports multiple image formats) - FILE *imFile = fopen(fileName, "rb"); + int dataSize = 0; + unsigned char *fileData = LoadFileData(fileName, &dataSize); - if (imFile != NULL) + if (fileData != NULL) { - // NOTE: Using stb_image to load images (Supports multiple image formats) - image.data = stbi_load_from_file(imFile, &imgWidth, &imgHeight, &imgBpp, 0); + int comp = 0; + image.data = stbi_load_from_memory(fileData, dataSize, &image.width, &image.height, &comp, 0); - fclose(imFile); - - image.width = imgWidth; - image.height = imgHeight; image.mipmaps = 1; - if (imgBpp == 1) image.format = UNCOMPRESSED_GRAYSCALE; - else if (imgBpp == 2) image.format = UNCOMPRESSED_GRAY_ALPHA; - else if (imgBpp == 3) image.format = UNCOMPRESSED_R8G8B8; - else if (imgBpp == 4) image.format = UNCOMPRESSED_R8G8B8A8; + if (comp == 1) image.format = UNCOMPRESSED_GRAYSCALE; + else if (comp == 2) image.format = UNCOMPRESSED_GRAY_ALPHA; + else if (comp == 3) image.format = UNCOMPRESSED_R8G8B8; + else if (comp == 4) image.format = UNCOMPRESSED_R8G8B8A8; + + RL_FREE(fileData); } #endif } #if defined(SUPPORT_FILEFORMAT_HDR) else if (IsFileExtension(fileName, ".hdr")) { - int imgBpp = 0; +#if defined(STBI_REQUIRED) + int dataSize = 0; + unsigned char *fileData = LoadFileData(fileName, &dataSize); - FILE *imFile = fopen(fileName, "rb"); - - // Load 32 bit per channel floats data - //stbi_set_flip_vertically_on_load(true); - image.data = stbi_loadf_from_file(imFile, &image.width, &image.height, &imgBpp, 0); - - fclose(imFile); - - image.mipmaps = 1; - - if (imgBpp == 1) image.format = UNCOMPRESSED_R32; - else if (imgBpp == 3) image.format = UNCOMPRESSED_R32G32B32; - else if (imgBpp == 4) image.format = UNCOMPRESSED_R32G32B32A32; - else + if (fileData != NULL) { - TRACELOG(LOG_WARNING, "[%s] Image fileformat not supported", fileName); - UnloadImage(image); + int comp = 0; + image.data = stbi_loadf_from_memory(fileData, dataSize, &image.width, &image.height, &comp, 0); + + image.mipmaps = 1; + + if (comp == 1) image.format = UNCOMPRESSED_R32; + else if (comp == 3) image.format = UNCOMPRESSED_R32G32B32; + else if (comp == 4) image.format = UNCOMPRESSED_R32G32B32A32; + else + { + TRACELOG(LOG_WARNING, "[%s] HDR Image fileformat not supported", fileName); + UnloadImage(image); + } + + RL_FREE(fileData); } +#endif } #endif #if defined(SUPPORT_FILEFORMAT_DDS) @@ -346,40 +348,24 @@ Image LoadImageRaw(const char *fileName, int width, int height, int format, int { Image image = { 0 }; - FILE *rawFile = fopen(fileName, "rb"); + int dataSize = 0; + unsigned char *fileData = LoadFileData(fileName, &dataSize); - if (rawFile == NULL) + if (fileData != NULL) { - TRACELOG(LOG_WARNING, "[%s] RAW image file could not be opened", fileName); - } - else - { - if (headerSize > 0) fseek(rawFile, headerSize, SEEK_SET); - + unsigned char *dataPtr = fileData; unsigned int size = GetPixelDataSize(width, height, format); + if (headerSize > 0) dataPtr += headerSize; + image.data = RL_MALLOC(size); // Allocate required memory in bytes + memcpy(image.data, dataPtr, size); // Copy required data to image + image.width = width; + image.height = height; + image.mipmaps = 1; + image.format = format; - // NOTE: fread() returns num read elements instead of bytes, - // to get bytes we need to read (1 byte size, elements) instead of (x byte size, 1 element) - int bytes = fread(image.data, 1, size, rawFile); - - // Check if data has been read successfully - if (bytes < size) - { - TRACELOG(LOG_WARNING, "[%s] RAW image data can not be read, wrong requested format or size", fileName); - - RL_FREE(image.data); - } - else - { - image.width = width; - image.height = height; - image.mipmaps = 1; - image.format = format; - } - - fclose(rawFile); + RL_FREE(fileData); } return image; @@ -844,9 +830,8 @@ void ExportImage(Image image, const char *fileName) { // Export raw pixel data (without header) // NOTE: It's up to the user to track image parameters - FILE *rawFile = fopen(fileName, "wb"); - success = fwrite(image.data, GetPixelDataSize(image.width, image.height, image.format), 1, rawFile); - fclose(rawFile); + SaveFileData(fileName, image.data, GetPixelDataSize(image.width, image.height, image.format)); + success = true; } RL_FREE(imgData); @@ -2704,7 +2689,7 @@ void DrawTextureEx(Texture2D texture, Vector2 position, float rotation, float sc // Draw a part of a texture (defined by a rectangle) void DrawTextureRec(Texture2D texture, Rectangle sourceRec, Vector2 position, Color tint) { - Rectangle destRec = { position.x, position.y, (float)fabs(sourceRec.width), (float)fabs(sourceRec.height) }; + Rectangle destRec = { position.x, position.y, fabsf(sourceRec.width), fabsf(sourceRec.height) }; Vector2 origin = { 0.0f, 0.0f }; DrawTexturePro(texture, sourceRec, destRec, origin, 0.0f, tint); @@ -2983,30 +2968,18 @@ static Image LoadAnimatedGIF(const char *fileName, int *frames, int **delays) { Image image = { 0 }; - FILE *gifFile = fopen(fileName, "rb"); + int dataSize = 0; + unsigned char *fileData = LoadFileData(fileName, &dataSize); - if (gifFile == NULL) + if (fileData != NULL) { - TRACELOG(LOG_WARNING, "[%s] Animated GIF file could not be opened", fileName); - } - else - { - fseek(gifFile, 0L, SEEK_END); - int size = ftell(gifFile); - fseek(gifFile, 0L, SEEK_SET); - - unsigned char *buffer = (unsigned char *)RL_CALLOC(size, sizeof(char)); - fread(buffer, sizeof(char), size, gifFile); - - fclose(gifFile); // Close file pointer - int comp = 0; - image.data = stbi_load_gif_from_memory(buffer, size, delays, &image.width, &image.height, frames, &comp, 4); + image.data = stbi_load_gif_from_memory(fileData, dataSize, delays, &image.width, &image.height, frames, &comp, 4); image.mipmaps = 1; image.format = UNCOMPRESSED_R8G8B8A8; - free(buffer); + RL_FREE(fileData); } return image; @@ -3071,7 +3044,7 @@ static Image LoadDDS(const char *fileName) else { // Verify the type of file - char ddsHeaderId[4]; + char ddsHeaderId[4] = { 0 }; fread(ddsHeaderId, 4, 1, ddsFile); @@ -3081,7 +3054,7 @@ static Image LoadDDS(const char *fileName) } else { - DDSHeader ddsHeader; + DDSHeader ddsHeader = { 0 }; // Get the image header fread(&ddsHeader, sizeof(DDSHeader), 1, ddsFile); @@ -3250,7 +3223,7 @@ static Image LoadPKM(const char *fileName) } else { - PKMHeader pkmHeader; + PKMHeader pkmHeader = { 0 }; // Get the image header fread(&pkmHeader, sizeof(PKMHeader), 1, pkmFile); @@ -3343,7 +3316,7 @@ static Image LoadKTX(const char *fileName) } else { - KTXHeader ktxHeader; + KTXHeader ktxHeader = { 0 }; // Get the image header fread(&ktxHeader, sizeof(KTXHeader), 1, ktxFile); @@ -3424,7 +3397,7 @@ static int SaveKTX(Image image, const char *fileName) if (ktxFile == NULL) TRACELOG(LOG_WARNING, "[%s] KTX image file could not be created", fileName); else { - KTXHeader ktxHeader; + KTXHeader ktxHeader = { 0 }; // KTX identifier (v1.1) //unsigned char id[12] = { '«', 'K', 'T', 'X', ' ', '1', '1', '»', '\r', '\n', '\x1A', '\n' }; @@ -3560,7 +3533,7 @@ static Image LoadPVR(const char *fileName) // Load different PVR data formats if (pvrVersion == 0x50) { - PVRHeaderV3 pvrHeader; + PVRHeaderV3 pvrHeader = { 0 }; // Get PVR image header fread(&pvrHeader, sizeof(PVRHeaderV3), 1, pvrFile); @@ -3670,7 +3643,7 @@ static Image LoadASTC(const char *fileName) } else { - ASTCHeader astcHeader; + ASTCHeader astcHeader = { 0 }; // Get ASTC image header fread(&astcHeader, sizeof(ASTCHeader), 1, astcFile); diff --git a/src/utils.c b/src/utils.c index 8a957b459..9aaf548b4 100644 --- a/src/utils.c +++ b/src/utils.c @@ -64,7 +64,7 @@ static int logTypeExit = LOG_ERROR; // Log type that exits static TraceLogCallback logCallback = NULL; // Log callback function pointer #if defined(PLATFORM_ANDROID) -static AAssetManager *assetManager = NULL; // Android assets manager pointer +static AAssetManager *assetManager = NULL; // Android assets manager pointer #endif #if defined(PLATFORM_UWP) @@ -163,6 +163,59 @@ void TraceLog(int logType, const char *text, ...) #endif // SUPPORT_TRACELOG } +// Load data from file into a buffer +unsigned char *LoadFileData(const char *fileName, int *bytesRead) +{ + unsigned char *data = NULL; + *bytesRead = 0; + + FILE *file = fopen(fileName, "rb"); + + if (file != NULL) + { + // WARNING: On binary streams SEEK_END could not be found, + // using fseek() and ftell() could not work in some (rare) cases + fseek(file, 0, SEEK_END); + int size = ftell(file); + fseek(file, 0, SEEK_SET); + + if (size > 0) + { + data = (unsigned char *)RL_MALLOC(sizeof(unsigned char)*size); + + // NOTE: fread() returns number of read elements instead of bytes, so we read [1 byte, size elements] + int count = fread(data, sizeof(unsigned char), size, file); + *bytesRead = count; + + if (count != size) TRACELOG(LOG_WARNING, "[%s] File partially loaded", fileName); + else TRACELOG(LOG_INFO, "[%s] File loaded successfully", fileName); + } + else TRACELOG(LOG_WARNING, "[%s] File could not be read", fileName); + + fclose(file); + } + else TRACELOG(LOG_WARNING, "[%s] File could not be opened", fileName); + + return data; +} + +// Save data to file from buffer +void SaveFileData(const char *fileName, void *data, int bytesToWrite) +{ + FILE *file = fopen(fileName, "wb"); + + if (file != NULL) + { + int count = fwrite(data, sizeof(unsigned char), bytesToWrite, file); + + if (count == 0) TRACELOG(LOG_WARNING, "[%s] File could not be written", fileName); + else if (count != bytesToWrite) TRACELOG(LOG_WARNING, "[%s] File partially written", fileName); + + fclose(file); + } + else TRACELOG(LOG_WARNING, "[%s] File could not be opened", fileName); +} + #if defined(PLATFORM_ANDROID) // Initialize asset manager from android app void InitAssetManager(AAssetManager *manager)