diff --git a/BINDINGS.md b/BINDINGS.md index b926b824d..b84c9064f 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -7,6 +7,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to - [raylib-lua-ffi](https://github.com/raysan5/raylib/issues/693) : raylib **Lua** ffi binding - [raylib-go](https://github.com/gen2brain/raylib-go) : raylib **Go** binding - [raylib-Nim](https://gitlab.com/define-private-public/raylib-Nim) : raylib **Nim** binding + - [raylib-cr](https://github.com/AregevDev/raylib-cr) : raylib **Crystal** binding - [cray](https://gitlab.com/Zatherz/cray) - raylib **Crystal** binding - [Graphics::Raylib](https://metacpan.org/pod/Graphics::Raylib) : raylib **Perl** wrapper - [raylib-pascal](https://github.com/drezgames/raylib-pascal) - raylib **Pascal** binding @@ -19,8 +20,12 @@ Some people ported raylib to other languages in form of bindings or wrappers to - [raylib-java](https://github.com/XoanaIO/raylib-java) : raylib **Java** binding - [raylib-chaiscript](https://github.com/RobLoach/raylib-chaiscript) : raylib **ChaiScript** binding - [node-raylib](https://github.com/RobLoach/node-raylib) : **Node.js** raylib binding - - *[raylib flat-assembler Usage example]()* + - [raylib-odin](https://github.com/kevinw/raylib-odin): **Odin** raylib binding + - [raylib-factor](https://github.com/Silverbeard00/raylib-factor): **Factor** raylib binding - *[raylib COBOL Usage example](https://github.com/Martinfx/Cobol/tree/master/OpenCobol/Games/raylib)* + - *raylib flat-assembler Usage example*: not uploaded yet... + +Missing some language? Check the [bindings not yet in this list](https://gist.github.com/raysan5/5764cc5b885183f523fce47f098f3d9b#bindings-not-yet-in-the-official-list) or create a new binding! :) Usually, raylib bindings follow the convention: `raylib-{language}` diff --git a/CHANGELOG b/CHANGELOG index 332533369..c133acfda 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -3,6 +3,132 @@ changelog Current Release: raylib 2.0.0 (July 2018) +----------------------------------------------- +Release: raylib 2.5 (May 2019) +----------------------------------------------- +KEY CHANGES: + - New text management API + - Cubemap textures support + - Skeletal model animation support + - New textures drawing: Quad and N-Patch based rectangles + - New drawing shapes available: CircleSector, Ring and RectangleRounded + - VR system redesign to allow custom device parameters and distortion shader + +Detailed changes: +[build] ... +[build] REVIEWED: Notepad++ NppExec scripts +[build] REVIEWED: VS2015 and VS2017 projects +[build] REVIEWED: Android APK build pipeline + +[core] Converted most #defined values as enum values +[core] Complete redesign of RPI input system +[core] ADDED: IsWindowResized() - Check if window has been resized +[core] ADDED: IsWindowHidden() - Check if window is currently hidden +[core] ADDED: UnhideWindow() - Show the window +[core] ADDED: HideWindow() - Hide the window +[core] ADDED: GetWindowHandle() - Get native window handle +[core] ADDED: GetMonitorCount() - Get number of connected monitors +[core] ADDED: GetMonitorWidth() - Get primary monitor width +[core] ADDED: GetMonitorHeight() - Get primary monitor height +[core] ADDED: GetMonitorPhysicalWidth() - Get primary monitor physical width in millimetres +[core] ADDED: GetMonitorPhysicalHeight() - Get primary monitor physical height in millimetres +[core] ADDED: GetMonitorName() - Get the human-readable, UTF-8 encoded name of the primary monitor +[core] ADDED: GetClipboardText() - Get clipboard text content +[core] ADDED: SetClipboardText() - Set clipboard text content +[core] ADDED: ColorFromHSV() - Returns a Color from HSV values +[core] ADDED: FileExists() - Check if file exists +[core] ADDED: GetFileNameWithoutExt() - Get filename string without extension (memory should be freed) +[core] ADDED: GetDirectoryFiles() - Get filenames in a directory path (memory should be freed) +[core] ADDED: ClearDirectoryFiles() - Clear directory files paths buffers (free memory) +[core] ADDED: OpenURL() - Open URL with default system browser (if available) +[core] ADDED: SetMouseOffset() - Set mouse offset +[core] ADDED: SetMouseScale() - Set mouse scaling +[core] REMOVED: ShowLogo() - Activate raylib logo at startup (can be done with flags) +[shapes] ADDED: DrawCircleSector() - Draw a piece of a circle +[shapes] ADDED: DrawCircleSectorLines() - Draw circle sector outline +[shapes] ADDED: DrawRing() - Draw ring +[shapes] ADDED: DrawRingLines() - Draw ring outline +[shapes] ADDED: DrawRectangleRounded() - Draw rectangle with rounded edges +[shapes] ADDED: DrawRectangleRoundedLines() - Draw rectangle with rounded edges outline +[shapes] ADDED: SetShapesTexture() - Define default texture used to draw shapes +[textures] REVIEWED: ExportImage() - Reorder function parameters +[textures] REVIEWED: ImageDrawRectangle() - Remove unneeded parameter +[textures] ADDED: ExportImageAsCode() - Export image as code file defining an array of bytes +[textures] ADDED: LoadTextureCubemap() - Load cubemap from image, multiple image cubemap layouts supported +[textures] ADDED: ImageExtractPalette() - Extract color palette from image to maximum size (memory should be freed) +[textures] ADDED: ImageDrawRectangleLines() - Draw rectangle lines within an image +[textures] ADDED: DrawTextureQuad() - Draw texture quad with tiling and offset parameters +[textures] ADDED: DrawTextureNPatch() - Draws a texture (or part of it) that stretches or shrinks nicely +[models] REVIEWED: LoadMesh() -> LoadMeshes() - Support multiple meshes loading +[models] REVIEWED: LoadMaterial() -> LoadMaterials() - Support multiple materials loading +[models] REVIEWED: ExportMesh() - Reorder parameters +[models] ADDED: DrawCubeWiresV() - Draw cube wires (Vector version) +[models] ADDED: GenMeshPoly() - Generate polygonal mesh +[models] ADDED: SetMaterialTexture() - Set texture for a material map type (MAP_DIFFUSE, MAP_SPECULAR...) +[models] ADDED: SetModelMeshMaterial() - Set material for a mesh +[models] ADDED: LoadModelAnimations() - Load model animations from file +[models] ADDED: UpdateModelAnimation() - Update model animation pose +[models] ADDED: UnloadModelAnimation() - Unload animation data +[models] ADDED: IsModelAnimationValid() - Check model animation skeleton match +[rlgl] Improved internal batching mechanism (multibuffering support, triangle texcoords...) +[rlgl] REVIEWED: rlPushMatrix()/rlPopMatrix() - Now works like OpenGL 1.1 +[rlgl] REVIEWED: SetShaderValue() - More generic, now requires uniform type +[rlgl] REMOVED: SetShaderValuei() - Can be acoomplished with new SetShaderValue() +[rlgl] ADDED: SetShaderValueV() - Set shader uniform value vector +[rlgl] ADDED: SetShaderValueTexture() - Set shader uniform value for texture +[rlgl] ADDED: BeginScissorMode() - Begin scissor mode (define screen area for following drawing) +[rlgl] ADDED: EndScissorMode() - End scissor mode +[rlgl] ADDED: SetVrConfiguration() - Set stereo rendering configuration parameters +[rlgl] REVIEWED: InitVrSimulator() - No input parameter required, use SetVrConfiguration() +[text] REVIEWED: LoadFontEx() - Reorder function parameters +[text] REVIEWED: LoadFontData() - Reorder function parameters +[text] REVIEWED: GenImageFontAtlas() - Reorder function parameters +[text] RENAMED: FormatText() -> TextFormat() +[text] RENAMED: SubText() -> TextSubtext() +[text] ADDED: LoadFontFromImage() - Load font from Image (XNA style) +[text] ADDED: DrawTextRec() - Draw text using font inside rectangle limits +[text] ADDED: DrawTextRecEx() - Draw text using font inside rectangle limits with support for text selection +[text] ADDED: TextIsEqual() - Check if two text string are equal +[text] ADDED: TextLength() - Get text length, checks for '\0' ending +[text] ADDED: TextReplace() - Replace text string (memory should be freed!) +[text] ADDED: TextInsert() - Insert text in a position (memory should be freed!) +[text] ADDED: TextJoin() - Join text strings with delimiter +[text] ADDED: TextSplit() - Split text into multiple strings +[text] ADDED: TextAppend() - Append text at specific position and move cursor! +[text] ADDED: TextFindIndex() - Find first text occurrence within a string +[text] ADDED: TextToUpper() - Get upper case version of provided string +[text] ADDED: TextToLower() - Get lower case version of provided string +[text] ADDED: TextToPascal() - Get Pascal case notation version of provided string +[text] ADDED: TextToInteger() - Get integer value from text (negative values not supported) +[raudio] ADDED: ExportWave() - Export wave data to file +[raudio] ADDED: ExportWaveAsCode() - Export wave sample data to code (.h) +[raudio] ADDED: IsAudioStreamPlaying() - Check if audio stream is playing +[raudio] ADDED: SetAudioStreamVolume() - Set volume for audio stream (1.0 is max level) +[raudio] ADDED: SetAudioStreamPitch() - Set pitch for audio stream (1.0 is base level) +[examples] ADDED: core_custom_logging - Custom trace log system +[examples] ADDED: core_input_multitouch - Multitouch input example +[examples] REVIEWED: core_vr_simulator - HMD device parameters and distortion shader should be provided +[examples] ADDED: core_window_scale_letterbox - Windows resizing and letterbox content +[examples] ADDED: shapes_rectangle_scaling_mouse - Scale a rectangle with mouse +TODO: [examples] ADDED: shapes_draw_circle_sector - Circle sector drawing +TODO: [examples] ADDED: shapes_draw_ring - Ring drawing +TODO: [examples] ADDED: shapes_draw_rectangle_rounded - Rounded rectangle drawing +[examples] ADDED: textures_background_scrolling - Scrolling and parallaz background effect +[examples] ADDED: textures_image_npatch - Drawing N-Patch based boxes +TODO: [examples] ADDED: textures_sprite_button +TODO: [examples] ADDED: textures_sprite_explosion +[examples] ADDED: text_draw_inside_rectangle - Drawing text inside a delimited rectangle box +[examples] ADDED: models_animation - Animated models loading and animation playing +[examples] ADDED: models_obj_viewer - Draw and drop models viewer +[examples] ADDED: models_rlgl_solar_system - Solar system simulation using rlgl functionality +[examples] ADDED: shaders_palette_switch - Switching color palette on shader +[examples] ADDED: shaders_raymarching - Raymarching shader +[examples] ADDED: shaders_texture_drawing - Texture drawing on GPU +[examples] REVIEWED: audio_raw_stream - Mostly rewritten +[games] ADDED: GGJ19 game - Cat vs Roomba +[*] Updated external libraries to latest version +[*] Multiple bugs corrected (check github issues) + ----------------------------------------------- Release: raylib 2.0 (July 2018) ----------------------------------------------- diff --git a/HISTORY.md b/HISTORY.md index f9f33ef31..bbd16c439 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -225,3 +225,8 @@ Refactored all raylib configuration #defines into a **centralized `config.h` hea A part of that, lots of new features, like a brand **new font rendering and packaging system** for TTF fonts with **SDF support** (thanks to the amazing STB headers), new functions for **CPU image data manipulation**, new orthographic 3d camera mode, a complete review of `raymath.h` single-file header-only library for better consistency and performance, new examples and way, [way more](https://github.com/raysan5/raylib/blob/master/CHANGELOG). Probably by now, **raylib 2.0 is the simplest and easiest-to-use library to learn (and understand) videogames programming**... but, undoubtly its development has exceeded any initial objective; raylib has become a simple and easy-to-use trully multiplatform portable standalone media library with thousands of possibilities... and that's just the beginning! + +notes on raylib 2.5 +------------------- + +SOON. \ No newline at end of file diff --git a/README.md b/README.md index b11665c8d..ba7f5389a 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,13 @@ raylib is highly inspired by Borland BGI graphics lib and by XNA framework. -raylib could be useful for prototyping, tools development, graphic applications, embedded systems and education. +raylib is especially well suited for prototyping, tooling, graphical applications, embedded systems and education. NOTE for ADVENTURERS: raylib is a programming library to enjoy videogames programming; no fancy interface, no visual helpers, no auto-debugging... just coding in the most -pure spartan-programmers way. Are you ready to learn? Jump to [code examples!](http://www.raylib.com/examples.html) +pure spartan-programmers way. + +Ready to learn? Jump to [code examples!](http://www.raylib.com/examples.html) [![Build Status](https://travis-ci.org/raysan5/raylib.svg?branch=master)](https://travis-ci.org/raysan5/raylib) [![https://ci.appveyor.com/api/projects/status/github/raysan5/raylib?svg=true](https://ci.appveyor.com/api/projects/status/github/raysan5/raylib?svg=true)](https://ci.appveyor.com/project/raysan5/raylib) @@ -18,20 +20,20 @@ pure spartan-programmers way. Are you ready to learn? Jump to [code examples!](h features -------- - - **NO external dependencies**, all required libraries included with raylib + - **NO external dependencies**, all required libraries are bundled into raylib - Multiple platforms supported: **Windows, Linux, MacOS, Android... and many more!** - Written in plain C code (C99) in PascalCase/camelCase notation - Hardware accelerated with OpenGL (**1.1, 2.1, 3.3 or ES 2.0**) - **Unique OpenGL abstraction layer** (usable as standalone module): [rlgl](https://github.com/raysan5/raylib/blob/master/src/rlgl.h) - Multiple Fonts formats supported (XNA fonts, AngelCode fonts, TTF) - Outstanding texture formats support, including compressed formats (DXT, ETC, ASTC) - - **Full 3d support** for 3d Shapes, Models, Billboards, Heightmaps and more! + - **Full 3D support**, including 3D Shapes, Models, Billboards, Heightmaps and more! - Flexible Materials system, supporting classic maps and **PBR maps** - - Shaders support, including Model shaders and Postprocessing shaders + - Custom shaders, including model and postprocessing shaders. - **Powerful math module** for Vector, Matrix and Quaternion operations: [raymath](https://github.com/raysan5/raylib/blob/master/src/raymath.h) - Audio loading and playing with streaming support (WAV, OGG, MP3, FLAC, XM, MOD) - **VR stereo rendering** support with configurable HMD device parameters - - Bindings to **Lua** ([raylib-lua](https://github.com/raysan5/raylib-lua)), **Go** ([raylib-go](https://github.com/gen2brain/raylib-go)) and [more](https://github.com/raysan5/raylib/blob/master/CONTRIBUTING.md#raylib-bindings)! + - Bindings to [multiple code languages](https://github.com/raysan5/raylib/blob/master/BINDINGS.md)! raylib uses on its [core](https://github.com/raysan5/raylib/blob/master/src/core.c) module the outstanding [GLFW3](http://www.glfw.org/) library, embedded inside raylib in the form of [rglfw](https://github.com/raysan5/raylib/blob/master/src/rglfw.c) module, avoiding that way external dependencies. @@ -46,16 +48,16 @@ raylib uses internally multiple single-file header-only libraries to support mul build and installation ---------------------- -Binary releases for Windows, Linux and macOS are available at the [Github Releases](https://github.com/raysan5/raylib/releases) page. raylib is also available via multiple package managers on multiple OS distributions. For more info check [raylib Wiki](https://github.com/raysan5/raylib/wiki). +Binary releases for Windows, Linux and macOS are available at the [Github Releases page](https://github.com/raysan5/raylib/releases). raylib is also available via multiple package managers on multiple OS distributions. For more info check [raylib Wiki](https://github.com/raysan5/raylib/wiki). -To build raylib yourself, check out also the [raylib Wiki](https://github.com/raysan5/raylib/wiki) for detailed instructions. +If you wish to build raylib yourself, [the raylib Wiki](https://github.com/raysan5/raylib/wiki) also contains detailed instructions on how to approach that. -raylib has been developed using exclusively two tools: +raylib has been developed exclusively using two tools: - * Notepad++ (text editor) - [http://notepad-plus-plus.org/](http://notepad-plus-plus.org/) - * MinGW (GCC compiler) - [http://www.mingw.org/](http://www.mingw.org/) - -Those are the tools recommended to develop with raylib, in fact, those are the tools my students use. + * Notepad++ (text editor) - [http://notepad-plus-plus.org](http://notepad-plus-plus.org/) + * MinGW (GCC compiler) - [http://www.mingw.org](http://www.mingw.org/) + +Those tools are recommended for raylib development. contact ------- @@ -71,7 +73,7 @@ contact If you are using raylib and you enjoy it, please, [let me know][raysan5]. -If you feel you can help, then, [helpme!](http://www.raylib.com/helpme.html) +If you are able to contribute, then please [helpme](http://www.raylib.com/helpme.html)! license ------- diff --git a/ROADMAP.md b/ROADMAP.md index f81514499..ae4706076 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -7,10 +7,10 @@ Here it is a wish-list with features and ideas to improve the library. Note that [raylib source code](https://github.com/raysan5/raylib/tree/master/src) has some *TODO* marks around code with pending things to review and improve. Check [GitHub Issues](https://github.com/raysan5/raylib/issues) for further details! **raylib 2.x** - - [ ] raynet: Network module + - [ ] rnet: Network module - [ ] Support Vulkan API (GRAPHICS_API_VULKAN) - [ ] Basic CPU/GPU stats sytem (memory, draws, time...) - - [ ] Support Animated models + - [x] Support Animated models (2.5) **raylib 1.8** - [x] Improved Materials system with PBR support diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 4b04d45a9..ab5ff5489 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -47,6 +47,7 @@ if(${PLATFORM} MATCHES "Android") list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/models/models_billboard.c) list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/models/models_rlgl_solar_system.c) list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/models/models_obj_viewer.c) + list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/models/models_animation.c) list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/shaders/shaders_custom_uniform.c) list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/shaders/shaders_model_shader.c) diff --git a/examples/Makefile b/examples/Makefile index e14762b31..2ceb3f7dc 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -2,7 +2,7 @@ # # raylib makefile for Desktop platforms, Raspberry Pi, Android and HTML5 # -# Copyright (c) 2013-2018 Ramon Santamaria (@raysan5) +# Copyright (c) 2013-2019 Ramon Santamaria (@raysan5) # # 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. @@ -377,7 +377,7 @@ EXAMPLES = \ core/core_2d_camera \ core/core_world_screen \ core/core_vr_simulator \ - core/core_multitouch \ + core/core_input_multitouch \ shapes/shapes_logo_raylib \ shapes/shapes_basic_shapes \ shapes/shapes_colors_palette \ diff --git a/examples/audio/audio_raw_stream.c b/examples/audio/audio_raw_stream.c index 7eee46f65..d7fa5d790 100644 --- a/examples/audio/audio_raw_stream.c +++ b/examples/audio/audio_raw_stream.c @@ -7,7 +7,7 @@ * This example has been created using raylib 1.6 (www.raylib.com) * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) * -* Copyright (c) 2015-2018 Ramon Santamaria (@raysan5) and James Hofmann (@triplefox) +* Copyright (c) 2015-2019 Ramon Santamaria (@raysan5) and James Hofmann (@triplefox) * ********************************************************************************************/ diff --git a/examples/core/core_multitouch.c b/examples/core/core_input_multitouch.c similarity index 67% rename from examples/core/core_multitouch.c rename to examples/core/core_input_multitouch.c index c059ac035..ef8fa7ae2 100644 --- a/examples/core/core_multitouch.c +++ b/examples/core/core_input_multitouch.c @@ -1,17 +1,15 @@ /******************************************************************************************* * -* raylib [core] example - Multitouch input +* raylib [core] example - Input multitouch * * This example has been created using raylib 2.1 (www.raylib.com) * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) * -* Copyright (c) 2014 Ramon Santamaria (@raysan5) -* Example by Berni +* Copyright (c) 2014-2019 Berni and Ramon Santamaria (@raysan5) * ********************************************************************************************/ #include "raylib.h" -#include int main() { @@ -20,13 +18,13 @@ int main() int screenWidth = 800; int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib [core] example - multitouch input"); + InitWindow(screenWidth, screenHeight, "raylib [core] example - input multitouch"); Vector2 ballPosition = { -100.0f, -100.0f }; - Color ballColor; - int PressedCounter = 0; - Vector2 TouchPos; - char Str[16]; + Color ballColor = BEIGE; + + int touchCounter = 0; + Vector2 touchPosition; SetTargetFPS(60); //--------------------------------------------------------------------------------------- @@ -44,11 +42,11 @@ int main() if (IsMouseButtonDown(MOUSE_MIDDLE_BUTTON)) ballColor = LIME; if (IsMouseButtonDown(MOUSE_RIGHT_BUTTON)) ballColor = DARKBLUE; - if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) PressedCounter = 10; - if (IsMouseButtonPressed(MOUSE_MIDDLE_BUTTON)) PressedCounter = 10; - if (IsMouseButtonPressed(MOUSE_RIGHT_BUTTON)) PressedCounter = 10; - if(PressedCounter > 0) - PressedCounter--; + if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) touchCounter = 10; + if (IsMouseButtonPressed(MOUSE_MIDDLE_BUTTON)) touchCounter = 10; + if (IsMouseButtonPressed(MOUSE_RIGHT_BUTTON)) touchCounter = 10; + + if (touchCounter > 0) touchCounter--; //---------------------------------------------------------------------------------- // Draw @@ -60,19 +58,18 @@ int main() // Multitouch for (int i = 0; i < MAX_TOUCH_POINTS; ++i) { - TouchPos = GetTouchPosition(i); // Get the touch point + touchPosition = GetTouchPosition(i); // Get the touch point - if( (TouchPos.x >= 0) && (TouchPos.y >= 0) ) // Make sure point is not (-1,-1) as this means there is no touch for it + if ((touchPosition.x >= 0) && (touchPosition.y >= 0)) // Make sure point is not (-1,-1) as this means there is no touch for it { - DrawCircleV(TouchPos, 34, ORANGE); // Draw a circle there - - sprintf(Str,"%d",i); - DrawText(Str, TouchPos.x - 10, TouchPos.y - 70, 40, BLACK); // Also show its index number + // Draw circle and touch index number + DrawCircleV(touchPosition, 34, ORANGE); + DrawText(FormatText("%d", i), touchPosition.x - 10, touchPosition.y - 70, 40, BLACK); } } // Draw the normal mouse location - DrawCircleV(ballPosition, 30 + (PressedCounter * 3), ballColor); + DrawCircleV(ballPosition, 30 + (touchCounter*3), ballColor); DrawText("move ball with mouse and click mouse button to change color", 10, 10, 20, DARKGRAY); DrawText("touch the screen at multiple locations to get multiple balls", 10, 30, 20, DARKGRAY); diff --git a/examples/core/core_multitouch.png b/examples/core/core_input_multitouch.png similarity index 100% rename from examples/core/core_multitouch.png rename to examples/core/core_input_multitouch.png diff --git a/examples/core/core_vr_simulator.c b/examples/core/core_vr_simulator.c index 3f59e8391..65054b732 100644 --- a/examples/core/core_vr_simulator.c +++ b/examples/core/core_vr_simulator.c @@ -23,7 +23,35 @@ int main() InitWindow(screenWidth, screenHeight, "raylib [core] example - vr simulator"); // Init VR simulator (Oculus Rift CV1 parameters) - InitVrSimulator(GetVrDeviceInfo(HMD_OCULUS_RIFT_CV1)); + InitVrSimulator(); + + VrDeviceInfo hmd = { 0 }; // VR device parameters (head-mounted-device) + + // Oculus Rift CV1 parameters for simulator + hmd.hResolution = 2160; // HMD horizontal resolution in pixels + hmd.vResolution = 1200; // HMD vertical resolution in pixels + hmd.hScreenSize = 0.133793f; // HMD horizontal size in meters + hmd.vScreenSize = 0.0669f; // HMD vertical size in meters + hmd.vScreenCenter = 0.04678f; // HMD screen center in meters + hmd.eyeToScreenDistance = 0.041f; // HMD distance between eye and display in meters + hmd.lensSeparationDistance = 0.07f; // HMD lens separation distance in meters + hmd.interpupillaryDistance = 0.07f; // HMD IPD (distance between pupils) in meters + + // NOTE: CV1 uses a Fresnel-hybrid-asymmetric lenses with specific distortion compute shaders. + // Following parameters are an approximation to distortion stereo rendering but results differ from actual device. + hmd.lensDistortionValues[0] = 1.0f; // HMD lens distortion constant parameter 0 + hmd.lensDistortionValues[1] = 0.22f; // HMD lens distortion constant parameter 1 + hmd.lensDistortionValues[2] = 0.24f; // HMD lens distortion constant parameter 2 + hmd.lensDistortionValues[3] = 0.0f; // HMD lens distortion constant parameter 3 + hmd.chromaAbCorrection[0] = 0.996f; // HMD chromatic aberration correction parameter 0 + hmd.chromaAbCorrection[1] = -0.004f; // HMD chromatic aberration correction parameter 1 + hmd.chromaAbCorrection[2] = 1.014f; // HMD chromatic aberration correction parameter 2 + hmd.chromaAbCorrection[3] = 0.0f; // HMD chromatic aberration correction parameter 3 + + // Distortion shader (uses device lens distortion and chroma) + Shader distortion = LoadShader(0, "resources/distortion.fs"); + + SetVrConfiguration(hmd, distortion); // Set Vr device parameters for stereo rendering // Define the camera to look into our 3d world Camera camera; @@ -77,9 +105,11 @@ int main() // De-Initialization //-------------------------------------------------------------------------------------- - CloseVrSimulator(); // Close VR simulator - - CloseWindow(); // Close window and OpenGL context + UnloadShader(distortion); // Unload distortion shader + + CloseVrSimulator(); // Close VR simulator + + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/examples/core/core_window_scale_letterbox.c b/examples/core/core_window_scale_letterbox.c new file mode 100644 index 000000000..ea4a375a2 --- /dev/null +++ b/examples/core/core_window_scale_letterbox.c @@ -0,0 +1,88 @@ +/******************************************************************************************* +* +* raylib [core] example - window scale letterbox +* +* This example has been created using raylib 2.5 (www.raylib.com) +* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* +* Copyright (c) 2019 Anata and Ramon Santamaria (@raysan5) +* +********************************************************************************************/ + +#include "raylib.h" + +#define max(a, b) ((a)>(b)? (a) : (b)) +#define min(a, b) ((a)<(b)? (a) : (b)) + +int main() +{ + const int windowWidth = 800; + const int windowHeight = 450; + + // Enable config flags for resizable window and vertical synchro + SetConfigFlags(FLAG_WINDOW_RESIZABLE | FLAG_VSYNC_HINT); + InitWindow(windowWidth, windowHeight, "raylib [core] example - window scale letterbox"); + SetWindowMinSize(320, 240); + + int gameScreenWidth = 640; + int gameScreenHeight = 480; + + // Render texture initialization + RenderTexture2D target = LoadRenderTexture(gameScreenWidth, gameScreenHeight); + SetTextureFilter(target.texture, FILTER_BILINEAR); // Texture scale filter to use + + Color colors[10] = { 0 }; + for (int i = 0; i < 10; i++) colors[i] = (Color){ GetRandomValue(100, 250), GetRandomValue(50, 150), GetRandomValue(10, 100), 255 }; + + SetTargetFPS(60); + //-------------------------------------------------------------------------------------- + + // Main game loop + while( !WindowShouldClose() ) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + // Compute required framebuffer scaling + float scale = min((float)GetScreenWidth()/gameScreenWidth, (float)GetScreenHeight()/gameScreenHeight); + + if (IsKeyPressed(KEY_SPACE)) + { + // Recalculate random colors for the bars + for (int i = 0; i < 10; i++) colors[i] = (Color){ GetRandomValue(100, 250), GetRandomValue(50, 150), GetRandomValue(10, 100), 255 }; + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(BLACK); + + // Draw everything in the render texture + BeginTextureMode(target); + + ClearBackground(RAYWHITE); // Clear render texture background color + + for (int i = 0; i < 10; i++) DrawRectangle(0, (gameScreenHeight/10)*i, gameScreenWidth, gameScreenHeight/10, colors[i]); + + DrawText("You can resize the window,\nand see the screen scaling!", 10, 25, 20, WHITE); + + EndTextureMode(); + + // Draw RenderTexture2D to window, properly scaled + DrawTexturePro(target.texture, (Rectangle){ 0.0f, 0.0f, (float)target.texture.width, (float)-target.texture.height }, + (Rectangle){ (GetScreenWidth() - ((float)gameScreenWidth*scale))*0.5, (GetScreenHeight() - ((float)gameScreenHeight*scale))*0.5, + (float)gameScreenWidth*scale, (float)gameScreenHeight*scale }, (Vector2){ 0, 0 }, 0.0f, WHITE); + + EndDrawing(); + //-------------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + UnloadRenderTexture(target); // Unload render texture + + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} diff --git a/examples/core/core_window_scale_letterbox.png b/examples/core/core_window_scale_letterbox.png new file mode 100644 index 000000000..5acf2d7cc Binary files /dev/null and b/examples/core/core_window_scale_letterbox.png differ diff --git a/examples/core/resources/distortion.fs b/examples/core/resources/distortion.fs new file mode 100644 index 000000000..207a0affb --- /dev/null +++ b/examples/core/resources/distortion.fs @@ -0,0 +1,43 @@ +#version 330 + +in vec2 fragTexCoord; +in vec4 fragColor; +out vec4 finalColor; + +uniform sampler2D texture0; +uniform vec2 leftLensCenter = vec2(0.288, 0.5); +uniform vec2 rightLensCenter = vec2(0.712, 0.5); +uniform vec2 leftScreenCenter = vec2(0.25, 0.5); +uniform vec2 rightScreenCenter = vec2(0.75, 0.5); +uniform vec2 scale = vec2(0.25, 0.45); +uniform vec2 scaleIn = vec2(4, 2.2222); +uniform vec4 hmdWarpParam = vec4(1, 0.22, 0.24, 0); +uniform vec4 chromaAbParam = vec4(0.996, -0.004, 1.014, 0.0); + +void main() +{ + vec2 lensCenter = fragTexCoord.x < 0.5? leftLensCenter : rightLensCenter; + vec2 screenCenter = fragTexCoord.x < 0.5? leftScreenCenter : rightScreenCenter; + vec2 theta = (fragTexCoord - lensCenter)*scaleIn; + float rSq = theta.x*theta.x + theta.y*theta.y; + vec2 theta1 = theta*(hmdWarpParam.x + hmdWarpParam.y*rSq + hmdWarpParam.z*rSq*rSq + hmdWarpParam.w*rSq*rSq*rSq); + vec2 thetaBlue = theta1*(chromaAbParam.z + chromaAbParam.w*rSq); + vec2 tcBlue = lensCenter + scale*thetaBlue; + + if (any(bvec2(clamp(tcBlue, screenCenter - vec2(0.25, 0.5), screenCenter + vec2(0.25, 0.5)) - tcBlue))) + { + finalColor = vec4(0.0, 0.0, 0.0, 1.0); + } + else + { + float blue = texture(texture0, tcBlue).b; + vec2 tcGreen = lensCenter + scale*theta1; + float green = texture(texture0, tcGreen).g; + + vec2 thetaRed = theta1*(chromaAbParam.x + chromaAbParam.y*rSq); + vec2 tcRed = lensCenter + scale*thetaRed; + + float red = texture(texture0, tcRed).r; + finalColor = vec4(red, green, blue, 1.0); + } +}; diff --git a/examples/others/iqm_loader/models_iqm_animation.c b/examples/models/models_animation.c similarity index 61% rename from examples/others/iqm_loader/models_iqm_animation.c rename to examples/models/models_animation.c index 18dd8577b..a75241b37 100644 --- a/examples/others/iqm_loader/models_iqm_animation.c +++ b/examples/models/models_animation.c @@ -1,19 +1,16 @@ /******************************************************************************************* * -* raylib [models] example - Load IQM 3d model with animations and play them +* raylib [models] example - Load 3d model with animations and play them * -* This example has been created using raylib 2.0 (www.raylib.com) +* This example has been created using raylib 2.5 (www.raylib.com) * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) * -* Copyright (c) 2018 @culacant and @raysan5 +* Copyright (c) 2019 Ramon Santamaria (@raysan5) and @culacant * ********************************************************************************************/ #include "raylib.h" -#define RIQM_IMPLEMENTATION -#include "riqm.h" - int main() { // Initialization @@ -21,7 +18,7 @@ int main() int screenWidth = 800; int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib [models] example - iqm animation"); + InitWindow(screenWidth, screenHeight, "raylib [models] example - model animation"); // Define the camera to look into our 3d world Camera camera = { 0 }; @@ -31,26 +28,25 @@ int main() camera.fovy = 45.0f; // Camera field-of-view Y camera.type = CAMERA_PERSPECTIVE; // Camera mode type - // Load the animated model mesh and basic data - AnimatedModel model = LoadAnimatedModel("resources/guy.iqm"); - // Load model texture and set material - // NOTE: There is only 1 mesh and 1 material (both at index 0), thats what the 2 0's are - model = AnimatedModelAddTexture(model, "resources/guytex.png"); // REPLACE! - model = SetMeshMaterial(model, 0, 0); // REPLACE! + Model model = LoadModel("resources/guy/guy.iqm"); // Load the animated model mesh and basic data + Texture2D texture = LoadTexture("resources/guy/guytex.png"); // Load model texture and set material + SetMaterialTexture(&model.materials[0], MAP_DIFFUSE, texture); // Set model material map texture + + Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position // Load animation data - Animation anim = LoadAnimationFromIQM("resources/guyanim.iqm"); - + int animsCount = 0; + ModelAnimation *anims = LoadModelAnimations("resources/guy/guyanim.iqm", &animsCount); int animFrameCounter = 0; - SetCameraMode(camera, CAMERA_FREE); // Set free camera mode + SetCameraMode(camera, CAMERA_FREE); // Set free camera mode - SetTargetFPS(60); // Set our game to run at 60 frames-per-second + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- // Main game loop - while (!WindowShouldClose()) // Detect window close button or ESC key + while (!WindowShouldClose()) // Detect window close button or ESC key { // Update //---------------------------------------------------------------------------------- @@ -60,7 +56,8 @@ int main() if (IsKeyDown(KEY_SPACE)) { animFrameCounter++; - AnimateModel(model, anim, animFrameCounter); // Animate the model with animation data and frame + UpdateModelAnimation(model, anims[0], animFrameCounter); + if (animFrameCounter >= anims[0].frameCount) animFrameCounter = 0; } //---------------------------------------------------------------------------------- @@ -72,14 +69,18 @@ int main() BeginMode3D(camera); - DrawAnimatedModel(model, Vector3Zero(), 1.0f, WHITE); // Draw animated model + 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 IQM MODEL ANIMATION", 10, 10, 20, MAROON); + 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(); @@ -88,8 +89,10 @@ int main() // De-Initialization //-------------------------------------------------------------------------------------- - UnloadAnimation(anim); // Unload animation data - UnloadAnimatedModel(model); // Unload animated model + // Unload model animations data + for (int i = 0; i < animsCount; i++) UnloadModelAnimation(anims[i]); + + UnloadModel(model); // Unload model CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- diff --git a/examples/models/models_cubicmap.c b/examples/models/models_cubicmap.c index c8d62c46f..ac24188e9 100644 --- a/examples/models/models_cubicmap.c +++ b/examples/models/models_cubicmap.c @@ -31,7 +31,7 @@ int main() // NOTE: By default each cube is mapped to one part of texture atlas Texture2D texture = LoadTexture("resources/cubicmap_atlas.png"); // Load map texture - model.material.maps[MAP_DIFFUSE].texture = texture; // Set map diffuse texture + model.materials[0].maps[MAP_DIFFUSE].texture = texture; // Set map diffuse texture Vector3 mapPosition = { -16.0f, 0.0f, -8.0f }; // Set model position diff --git a/examples/models/models_heightmap.c b/examples/models/models_heightmap.c index d131b127d..e0475f182 100644 --- a/examples/models/models_heightmap.c +++ b/examples/models/models_heightmap.c @@ -29,7 +29,7 @@ int main() Mesh mesh = GenMeshHeightmap(image, (Vector3){ 16, 8, 16 }); // Generate heightmap mesh (RAM and VRAM) Model model = LoadModelFromMesh(mesh); // Load model from generated mesh - model.material.maps[MAP_DIFFUSE].texture = texture; // Set map diffuse texture + model.materials[0].maps[MAP_DIFFUSE].texture = texture; // Set map diffuse texture Vector3 mapPosition = { -8.0f, 0.0f, -8.0f }; // Define model position UnloadImage(image); // Unload heightmap image from RAM, already uploaded to VRAM diff --git a/examples/models/models_material_pbr.c b/examples/models/models_material_pbr.c index f93c7a687..5c308cfcb 100644 --- a/examples/models/models_material_pbr.c +++ b/examples/models/models_material_pbr.c @@ -12,6 +12,8 @@ #include "raylib.h" #include "raymath.h" +#include + #define RLIGHTS_IMPLEMENTATION #include "rlights.h" @@ -34,20 +36,29 @@ int main() InitWindow(screenWidth, screenHeight, "raylib [models] example - pbr material"); // Define the camera to look into our 3d world - Camera camera = {{ 4.0f, 4.0f, 4.0f }, { 0.0f, 0.5f, 0.0f }, { 0.0f, 1.0f, 0.0f }, 45.0f, 0 }; + Camera camera = { 0 }; + camera.position = (Vector3){ 4.0f, 4.0f, 4.0f }; // Camera position + camera.target = (Vector3){ 0.0f, 0.5f, 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 model and PBR material Model model = LoadModel("resources/pbr/trooper.obj"); - MeshTangents(&model.mesh); - model.material = LoadMaterialPBR((Color){ 255, 255, 255, 255 }, 1.0f, 1.0f); + + // Mesh tangents are generated... and uploaded to GPU + // NOTE: New VBO for tangents is generated at default location and also binded to mesh VAO + MeshTangents(&model.meshes[0]); + + model.materials[0] = LoadMaterialPBR((Color){ 255, 255, 255, 255 }, 1.0f, 1.0f); // Define lights attributes // NOTE: Shader is passed to every light on creation to define shader bindings internally Light lights[MAX_LIGHTS] = { - CreateLight(LIGHT_POINT, (Vector3){ LIGHT_DISTANCE, LIGHT_HEIGHT, 0.0f }, (Vector3){ 0.0f, 0.0f, 0.0f }, (Color){ 255, 0, 0, 255 }, model.material.shader), - CreateLight(LIGHT_POINT, (Vector3){ 0.0f, LIGHT_HEIGHT, LIGHT_DISTANCE }, (Vector3){ 0.0f, 0.0f, 0.0f }, (Color){ 0, 255, 0, 255 }, model.material.shader), - CreateLight(LIGHT_POINT, (Vector3){ -LIGHT_DISTANCE, LIGHT_HEIGHT, 0.0f }, (Vector3){ 0.0f, 0.0f, 0.0f }, (Color){ 0, 0, 255, 255 }, model.material.shader), - CreateLight(LIGHT_DIRECTIONAL, (Vector3){ 0.0f, LIGHT_HEIGHT*2.0f, -LIGHT_DISTANCE }, (Vector3){ 0.0f, 0.0f, 0.0f }, (Color){ 255, 0, 255, 255 }, model.material.shader) + CreateLight(LIGHT_POINT, (Vector3){ LIGHT_DISTANCE, LIGHT_HEIGHT, 0.0f }, (Vector3){ 0.0f, 0.0f, 0.0f }, (Color){ 255, 0, 0, 255 }, model.materials[0].shader), + CreateLight(LIGHT_POINT, (Vector3){ 0.0f, LIGHT_HEIGHT, LIGHT_DISTANCE }, (Vector3){ 0.0f, 0.0f, 0.0f }, (Color){ 0, 255, 0, 255 }, model.materials[0].shader), + CreateLight(LIGHT_POINT, (Vector3){ -LIGHT_DISTANCE, LIGHT_HEIGHT, 0.0f }, (Vector3){ 0.0f, 0.0f, 0.0f }, (Color){ 0, 0, 255, 255 }, model.materials[0].shader), + CreateLight(LIGHT_DIRECTIONAL, (Vector3){ 0.0f, LIGHT_HEIGHT*2.0f, -LIGHT_DISTANCE }, (Vector3){ 0.0f, 0.0f, 0.0f }, (Color){ 255, 0, 255, 255 }, model.materials[0].shader) }; SetCameraMode(camera, CAMERA_ORBITAL); // Set an orbital camera mode @@ -64,7 +75,7 @@ int main() // Send to material PBR shader camera view position float cameraPos[3] = { camera.position.x, camera.position.y, camera.position.z }; - SetShaderValue(model.material.shader, model.material.shader.locs[LOC_VECTOR_VIEW], cameraPos, UNIFORM_VEC3); + SetShaderValue(model.materials[0].shader, model.materials[0].shader.locs[LOC_VECTOR_VIEW], cameraPos, UNIFORM_VEC3); //---------------------------------------------------------------------------------- // Draw @@ -143,9 +154,13 @@ static Material LoadMaterialPBR(Color albedo, float metalness, float roughness) #define PATH_BRDF_FS "resources/shaders/brdf.fs" // Path to bidirectional reflectance distribution function fragment shader Shader shdrCubemap = LoadShader(PATH_CUBEMAP_VS, PATH_CUBEMAP_FS); + printf("Loaded shader: cubemap\n"); Shader shdrIrradiance = LoadShader(PATH_SKYBOX_VS, PATH_IRRADIANCE_FS); + printf("Loaded shader: irradiance\n"); Shader shdrPrefilter = LoadShader(PATH_SKYBOX_VS, PATH_PREFILTER_FS); + printf("Loaded shader: prefilter\n"); Shader shdrBRDF = LoadShader(PATH_BRDF_VS, PATH_BRDF_FS); + printf("Loaded shader: brdf\n"); // Setup required shader locations SetShaderValue(shdrCubemap, GetShaderLocation(shdrCubemap, "equirectangularMap"), (int[1]){ 0 }, UNIFORM_INT); diff --git a/examples/models/models_mesh_generation.c b/examples/models/models_mesh_generation.c index d64889bdd..2b4b75abe 100644 --- a/examples/models/models_mesh_generation.c +++ b/examples/models/models_mesh_generation.c @@ -39,7 +39,7 @@ int main() models[7] = LoadModelFromMesh(GenMeshPoly(5, 2.0f)); // Set checked texture as default diffuse component for all models material - for (int i = 0; i < NUM_MODELS; i++) models[i].material.maps[MAP_DIFFUSE].texture = texture; + for (int i = 0; i < NUM_MODELS; i++) models[i].materials[0].maps[MAP_DIFFUSE].texture = texture; // Define the camera to look into our 3d world Camera camera = {{ 5.0f, 5.0f, 5.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, 45.0f, 0 }; @@ -65,6 +65,17 @@ int main() { currentModel = (currentModel + 1)%NUM_MODELS; // Cycle between the textures } + + if (IsKeyPressed(KEY_RIGHT)) + { + currentModel++; + if (currentModel >= NUM_MODELS) currentModel = 0; + } + else if (IsKeyPressed(KEY_LEFT)) + { + currentModel--; + if (currentModel < 0) currentModel = NUM_MODELS - 1; + } //---------------------------------------------------------------------------------- // Draw diff --git a/examples/models/models_mesh_picking.c b/examples/models/models_mesh_picking.c index 9b12e98c6..420288290 100644 --- a/examples/models/models_mesh_picking.c +++ b/examples/models/models_mesh_picking.c @@ -26,7 +26,7 @@ int main() // Define the camera to look into our 3d world Camera camera = { 0 }; - camera.position = (Vector3){ 20.0f, 20.0f, 20.0f }; // Camera position + camera.position = (Vector3){ 20.0f, 20.0f, 20.0f }; // Camera position camera.target = (Vector3){ 0.0f, 8.0f, 0.0f }; // Camera looking at point camera.up = (Vector3){ 0.0f, 1.6f, 0.0f }; // Camera up vector (rotation towards target) camera.fovy = 45.0f; // Camera field-of-view Y @@ -36,10 +36,10 @@ int main() Model tower = LoadModel("resources/models/turret.obj"); // Load OBJ model Texture2D texture = LoadTexture("resources/models/turret_diffuse.png"); // Load model texture - tower.material.maps[MAP_DIFFUSE].texture = texture; // Set model diffuse texture + tower.materials[0].maps[MAP_DIFFUSE].texture = texture; // Set model diffuse texture Vector3 towerPos = { 0.0f, 0.0f, 0.0f }; // Set model position - BoundingBox towerBBox = MeshBoundingBox(tower.mesh); // Get mesh bounding box + BoundingBox towerBBox = MeshBoundingBox(tower.meshes[0]); // Get mesh bounding box bool hitMeshBBox = false; bool hitTriangle = false; diff --git a/examples/models/models_obj_loading.c b/examples/models/models_obj_loading.c index 7ec2d3f05..74e7d08a0 100644 --- a/examples/models/models_obj_loading.c +++ b/examples/models/models_obj_loading.c @@ -30,7 +30,7 @@ int main() Model model = LoadModel("resources/models/castle.obj"); // Load OBJ model Texture2D texture = LoadTexture("resources/models/castle_diffuse.png"); // Load model texture - model.material.maps[MAP_DIFFUSE].texture = texture; // Set map diffuse texture + model.materials[0].maps[MAP_DIFFUSE].texture = texture; // Set map diffuse texture Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position SetTargetFPS(60); // Set our game to run at 60 frames-per-second @@ -59,7 +59,7 @@ int main() DrawGizmo(position); // Draw gizmo EndMode3D(); - + DrawText("(c) Castle 3D model by Alberto Cano", screenWidth - 200, screenHeight - 20, 10, GRAY); DrawFPS(10, 10); diff --git a/examples/models/models_obj_viewer.c b/examples/models/models_obj_viewer.c index 15f795497..7d3874416 100644 --- a/examples/models/models_obj_viewer.c +++ b/examples/models/models_obj_viewer.c @@ -5,7 +5,7 @@ * This example has been created using raylib 2.0 (www.raylib.com) * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -27,13 +27,13 @@ int main() Model model = LoadModel("resources/models/turret.obj"); // Load default model obj Texture2D texture = LoadTexture("resources/models/turret_diffuse.png"); // Load default model texture - model.material.maps[MAP_DIFFUSE].texture = texture; // Bind texture to model - - Vector3 position = { 0.0, 0.0, 0.0 }; // Set model position - BoundingBox bounds = MeshBoundingBox(model.mesh); // Set model bounds - bool selected = false; // Selected object flag - - SetCameraMode(camera, CAMERA_FREE); // Set a free camera mode + model.materials[0].maps[MAP_DIFFUSE].texture = texture; // Bind texture to model + + Vector3 position = { 0.0, 0.0, 0.0 }; // Set model position + BoundingBox bounds = MeshBoundingBox(model.meshes[0]); // Set model bounds + bool selected = false; // Selected object flag + + SetCameraMode(camera, CAMERA_FREE); // Set a free camera mode char objFilename[64] = "turret.obj"; @@ -49,30 +49,30 @@ int main() { int count = 0; char **droppedFiles = GetDroppedFiles(&count); - + if (count == 1) { if (IsFileExtension(droppedFiles[0], ".obj")) { - UnloadMesh(&model.mesh); - model.mesh = LoadMesh(droppedFiles[0]); - bounds = MeshBoundingBox(model.mesh); + for (int i = 0; i < model.meshCount; i++) UnloadMesh(&model.meshes[i]); + model.meshes = LoadMeshes(droppedFiles[0], &model.meshCount); + bounds = MeshBoundingBox(model.meshes[0]); } else if (IsFileExtension(droppedFiles[0], ".png")) { UnloadTexture(texture); texture = LoadTexture(droppedFiles[0]); - model.material.maps[MAP_DIFFUSE].texture = texture; + model.materials[0].maps[MAP_DIFFUSE].texture = texture; } strcpy(objFilename, GetFileName(droppedFiles[0])); } - + ClearDroppedFiles(); // Clear internal buffers } - + UpdateCamera(&camera); - + // Select model on mouse click if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) { @@ -93,21 +93,21 @@ int main() DrawModel(model, position, 1.0f, WHITE); // Draw 3d model with texture DrawGrid(20.0, 10.0); // Draw a grid - + if (selected) DrawBoundingBox(bounds, GREEN); - + EndMode3D(); - + DrawText("Free camera default controls:", 10, 20, 10, DARKGRAY); DrawText("- Mouse Wheel to Zoom in-out", 20, 40, 10, GRAY); DrawText("- Mouse Wheel Pressed to Pan", 20, 60, 10, GRAY); DrawText("- Alt + Mouse Wheel Pressed to Rotate", 20, 80, 10, GRAY); DrawText("- Alt + Ctrl + Mouse Wheel Pressed for Smooth Zoom", 20, 100, 10, GRAY); - + DrawText("Drag & drop .obj/.png to load mesh/texture.", 10, GetScreenHeight() - 20, 10, DARKGRAY); DrawText(FormatText("Current file: %s", objFilename), 250, GetScreenHeight() - 20, 10, GRAY); if (selected) DrawText("MODEL SELECTED", GetScreenWidth() - 110, 10, 10, GREEN); - + DrawText("(c) Turret 3D model by Alberto Cano", screenWidth - 200, screenHeight - 20, 10, GRAY); EndDrawing(); @@ -119,7 +119,7 @@ int main() UnloadModel(model); // Unload model ClearDroppedFiles(); // Clear internal buffers - + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- diff --git a/examples/models/models_skybox.c b/examples/models/models_skybox.c index c7f76ecf1..759c79c62 100644 --- a/examples/models/models_skybox.c +++ b/examples/models/models_skybox.c @@ -29,8 +29,8 @@ int main() // Load skybox shader and set required locations // NOTE: Some locations are automatically set at shader loading - skybox.material.shader = LoadShader("resources/shaders/skybox.vs", "resources/shaders/skybox.fs"); - SetShaderValue(skybox.material.shader, GetShaderLocation(skybox.material.shader, "environmentMap"), (int[1]){ MAP_CUBEMAP }, UNIFORM_INT); + skybox.materials[0].shader = LoadShader("resources/shaders/skybox.vs", "resources/shaders/skybox.fs"); + SetShaderValue(skybox.materials[0].shader, GetShaderLocation(skybox.materials[0].shader, "environmentMap"), (int[1]){ MAP_CUBEMAP }, UNIFORM_INT); // Load cubemap shader and setup required shader locations Shader shdrCubemap = LoadShader("resources/shaders/cubemap.vs", "resources/shaders/cubemap.fs"); @@ -41,7 +41,7 @@ int main() // Generate cubemap (texture with 6 quads-cube-mapping) from panorama HDR texture // NOTE: New texture is generated rendering to texture, shader computes the sphre->cube coordinates mapping - skybox.material.maps[MAP_CUBEMAP].texture = GenTextureCubemap(shdrCubemap, texHDR, 512); + skybox.materials[0].maps[MAP_CUBEMAP].texture = GenTextureCubemap(shdrCubemap, texHDR, 512); UnloadTexture(texHDR); // Texture not required anymore, cubemap already generated UnloadShader(shdrCubemap); // Unload cubemap generation shader, not required anymore diff --git a/examples/models/models_yaw_pitch_roll.c b/examples/models/models_yaw_pitch_roll.c index 88b0a6109..7eaaf0bea 100644 --- a/examples/models/models_yaw_pitch_roll.c +++ b/examples/models/models_yaw_pitch_roll.c @@ -37,10 +37,10 @@ int main() RenderTexture2D framebuffer = LoadRenderTexture(192, 192); // Model loading - Model model = LoadModel("resources/plane.obj"); // Load OBJ model - model.material.maps[MAP_DIFFUSE].texture = LoadTexture("resources/plane_diffuse.png"); // Set map diffuse texture + Model model = LoadModel("resources/plane.obj"); // Load OBJ model + model.materials[0].maps[MAP_DIFFUSE].texture = LoadTexture("resources/plane_diffuse.png"); // Set map diffuse texture - GenTextureMipmaps(&model.material.maps[MAP_DIFFUSE].texture); + GenTextureMipmaps(&model.materials[0].maps[MAP_DIFFUSE].texture); Camera camera = { 0 }; camera.position = (Vector3){ 0.0f, 60.0f, -120.0f };// Camera position perspective diff --git a/examples/others/iqm_loader/resources/guy.blend b/examples/models/resources/guy/guy.blend similarity index 100% rename from examples/others/iqm_loader/resources/guy.blend rename to examples/models/resources/guy/guy.blend diff --git a/examples/others/iqm_loader/resources/guy.iqm b/examples/models/resources/guy/guy.iqm similarity index 100% rename from examples/others/iqm_loader/resources/guy.iqm rename to examples/models/resources/guy/guy.iqm diff --git a/examples/others/iqm_loader/resources/guyanim.iqm b/examples/models/resources/guy/guyanim.iqm similarity index 100% rename from examples/others/iqm_loader/resources/guyanim.iqm rename to examples/models/resources/guy/guyanim.iqm diff --git a/examples/others/iqm_loader/resources/guytex.png b/examples/models/resources/guy/guytex.png similarity index 100% rename from examples/others/iqm_loader/resources/guytex.png rename to examples/models/resources/guy/guytex.png diff --git a/examples/models/resources/models/cube.obj b/examples/models/resources/models/cube.obj index 0e9d65979..bf7e3bee4 100644 --- a/examples/models/resources/models/cube.obj +++ b/examples/models/resources/models/cube.obj @@ -1,7 +1,7 @@ # reference material -mtllib myCube.mtl +#mtllib cube.mtl -# object Pau_Box +# object box # vertex (XZY) v 5.5 0 1.5 @@ -14,85 +14,55 @@ v 5.5 3 -1.5 v 8.5 3 -1.5 # normals (XYZ) -# red vn 0 -1 0 -#blue vn 0 1 0 -#top vn 0 0 1 -#yellow vn 1 0 0 -#bottom vn 0 0 -1 -#green vn -1 0 0 - # UVs (XY) -# yellow (1234) vt 0.5 0 0 vt 1 0 0 vt 1 0.5 0 vt 0.5 0.5 0 -# red (5678) vt 0.5 0.5 0 vt 1 0.5 0 vt 0.5 1 0 vt 1 1 0 -#bottom (9101112) vt 0 0.5 0 vt 1 0.5 0 vt 1 0 0 vt 0 0 0 -#top (13141516) vt 0 0.5 0 vt 1 0.5 0 vt 1 1 0 vt 0 1 0 -#green (17181920) vt 0.5 0 0 vt 0 0 0 vt 0 0.5 0 vt 0.5 0.5 0 -#blue (21222324) vt 0 0.5 0 vt 0.5 0.5 0 vt 0.5 1 0 vt 0 1 0 # merger -g Pau_Box +g box # reference material -usemtl Material_1 +#usemtl mat01 -# bottom -f 1/9/1 3/10/1 4/11/1 -f 4/11/1 2/12/1 1/9/1 - -# top +# faces +f 1/9/1 3/10/1 4/11/1 +f 4/11/1 2/12/1 1/9/1 f 5/13/2 6/14/2 8/15/2 f 8/15/2 7/16/2 5/13/2 - -# front-yellow f 1/17/6 2/18/6 6/19/6 f 6/19/6 5/20/6 1/17/6 - -# right-blue f 2/6/1 4/5/1 8/7/1 f 8/7/1 6/8/1 2/6/1 - -# back-green f 4/2/3 3/1/3 7/4/3 f 7/4/3 8/3/3 4/2/3 - -# left-red f 3/22/5 1/21/5 5/24/5 f 5/24/5 7/23/5 3/22/5 - - - - - - - diff --git a/examples/models/resources/shaders/brdf.fs b/examples/models/resources/shaders/brdf.fs index 3e8777d2b..d04bc6618 100644 --- a/examples/models/resources/shaders/brdf.fs +++ b/examples/models/resources/shaders/brdf.fs @@ -10,13 +10,13 @@ #version 330 -#define MAX_SAMPLES 1024u // Input vertex attributes (from vertex shader) in vec2 fragTexCoord; // Constant values const float PI = 3.14159265359; +const uint MAX_SAMPLES = 1024u; // Output fragment color out vec4 finalColor; @@ -93,7 +93,7 @@ vec2 IntegrateBRDF(float NdotV, float roughness) vec3 V = vec3(sqrt(1.0 - NdotV*NdotV), 0.0, NdotV); vec3 N = vec3(0.0, 0.0, 1.0); - for (int i = 0; i < MAX_SAMPLES; i++) + for (uint i = 0u; i < MAX_SAMPLES; i++) { // Generate a sample vector that's biased towards the preferred alignment direction (importance sampling) diff --git a/examples/models/resources/shaders/irradiance.fs b/examples/models/resources/shaders/irradiance.fs index 87113673f..b42d2143e 100644 --- a/examples/models/resources/shaders/irradiance.fs +++ b/examples/models/resources/shaders/irradiance.fs @@ -9,7 +9,7 @@ #version 330 // Input vertex attributes (from vertex shader) -in vec3 fragPos; +in vec3 fragPosition; // Input uniform values uniform samplerCube environmentMap; @@ -23,7 +23,7 @@ out vec4 finalColor; void main() { // The sample direction equals the hemisphere's orientation - vec3 normal = normalize(fragPos); + vec3 normal = normalize(fragPosition); vec3 irradiance = vec3(0.0); diff --git a/examples/models/resources/shaders/prefilter.fs b/examples/models/resources/shaders/prefilter.fs index f5cf64be2..9439810d7 100644 --- a/examples/models/resources/shaders/prefilter.fs +++ b/examples/models/resources/shaders/prefilter.fs @@ -11,7 +11,7 @@ #define CUBEMAP_RESOLUTION 1024.0 // Input vertex attributes (from vertex shader) -in vec3 fragPos; +in vec3 fragPosition; // Input uniform values uniform samplerCube environmentMap; @@ -79,7 +79,7 @@ vec3 ImportanceSampleGGX(vec2 Xi, vec3 N, float roughness) void main() { // Make the simplyfying assumption that V equals R equals the normal - vec3 N = normalize(fragPos); + vec3 N = normalize(fragPosition); vec3 R = N; vec3 V = R; diff --git a/examples/others/iqm_loader/models_iqm_animation.gif b/examples/others/iqm_loader/models_iqm_animation.gif deleted file mode 100644 index 3a51ad854..000000000 Binary files a/examples/others/iqm_loader/models_iqm_animation.gif and /dev/null differ diff --git a/examples/others/iqm_loader/raymath.h b/examples/others/iqm_loader/raymath.h deleted file mode 100644 index a574dd0d0..000000000 --- a/examples/others/iqm_loader/raymath.h +++ /dev/null @@ -1,1334 +0,0 @@ -/********************************************************************************************** -* -* raymath v1.2 - Math functions to work with Vector3, Matrix and Quaternions -* -* CONFIGURATION: -* -* #define RAYMATH_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. -* -* #define RAYMATH_HEADER_ONLY -* Define static inline functions code, so #include header suffices for use. -* This may use up lots of memory. -* -* #define RAYMATH_STANDALONE -* Avoid raylib.h header inclusion in this file. -* Vector3 and Matrix data types are defined internally in raymath module. -* -* -* LICENSE: zlib/libpng -* -* Copyright (c) 2015-2017 Ramon Santamaria (@raysan5) -* -* 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 RAYMATH_H -#define RAYMATH_H - -//#define RAYMATH_STANDALONE // NOTE: To use raymath as standalone lib, just uncomment this line -//#define RAYMATH_HEADER_ONLY // NOTE: To compile functions as static inline, uncomment this line - -#ifndef RAYMATH_STANDALONE - #include "raylib.h" // Required for structs: Vector3, Matrix -#endif - -#ifdef __cplusplus - #define RMEXTERN extern "C" // Functions visible from other files (no name mangling of functions in C++) -#else - #define RMEXTERN // Functions visible from other files -#endif - -#if defined RAYMATH_IMPLEMENTATION && defined RAYMATH_HEADER_ONLY - #error "Specifying both RAYMATH_IMPLEMENTATION and RAYMATH_HEADER_ONLY is contradictory" -#endif - -#ifdef RAYMATH_IMPLEMENTATION - #define RMDEF extern inline // Provide external definition -#elif defined RAYMATH_HEADER_ONLY - #define RMDEF static inline // Functions may be inlined, no external out-of-line definition -#else - #ifdef __TINYC__ - #define RMDEF static inline // plain inline not supported by tinycc (See issue #435) - #else - #define RMDEF inline // Functions may be inlined or external definition used - #endif -#endif - - -//---------------------------------------------------------------------------------- -// Defines and Macros -//---------------------------------------------------------------------------------- -#ifndef PI - #define PI 3.14159265358979323846 -#endif - -#ifndef DEG2RAD - #define DEG2RAD (PI/180.0f) -#endif - -#ifndef RAD2DEG - #define RAD2DEG (180.0f/PI) -#endif - -// Return float vector for Matrix -#ifndef MatrixToFloat - #define MatrixToFloat(mat) (MatrixToFloatV(mat).v) -#endif - -// Return float vector for Vector3 -#ifndef Vector3ToFloat - #define Vector3ToFloat(vec) (Vector3ToFloatV(vec).v) -#endif - -//---------------------------------------------------------------------------------- -// Types and Structures Definition -//---------------------------------------------------------------------------------- - -#if defined(RAYMATH_STANDALONE) - // Vector2 type - typedef struct Vector2 { - float x; - float y; - } Vector2; - - // Vector3 type - typedef struct Vector3 { - float x; - float y; - float z; - } Vector3; - - // Matrix type (OpenGL style 4x4 - right handed, column major) - typedef struct Matrix { - float m0, m4, m8, m12; - float m1, m5, m9, m13; - float m2, m6, m10, m14; - float m3, m7, m11, m15; - } Matrix; - - // Quaternion type - typedef struct Quaternion { - float x; - float y; - float z; - float w; - } Quaternion; -#endif - -// NOTE: Helper types to be used instead of array return types for *ToFloat functions -typedef struct float3 { float v[3]; } float3; -typedef struct float16 { float v[16]; } float16; - -#include // Required for: sinf(), cosf(), tan(), fabs() - -//---------------------------------------------------------------------------------- -// Module Functions Definition - Utils math -//---------------------------------------------------------------------------------- - -// Clamp float value -RMDEF float Clamp(float value, float min, float max) -{ - const float res = value < min ? min : value; - return res > max ? max : res; -} - -//---------------------------------------------------------------------------------- -// Module Functions Definition - Vector2 math -//---------------------------------------------------------------------------------- - -// Vector with components value 0.0f -RMDEF Vector2 Vector2Zero(void) -{ - Vector2 result = { 0.0f, 0.0f }; - return result; -} - -// Vector with components value 1.0f -RMDEF Vector2 Vector2One(void) -{ - Vector2 result = { 1.0f, 1.0f }; - return result; -} - -// Add two vectors (v1 + v2) -RMDEF Vector2 Vector2Add(Vector2 v1, Vector2 v2) -{ - Vector2 result = { v1.x + v2.x, v1.y + v2.y }; - return result; -} - -// Subtract two vectors (v1 - v2) -RMDEF Vector2 Vector2Subtract(Vector2 v1, Vector2 v2) -{ - Vector2 result = { v1.x - v2.x, v1.y - v2.y }; - return result; -} - -// Calculate vector length -RMDEF float Vector2Length(Vector2 v) -{ - float result = sqrtf((v.x*v.x) + (v.y*v.y)); - return result; -} - -// Calculate two vectors dot product -RMDEF float Vector2DotProduct(Vector2 v1, Vector2 v2) -{ - float result = (v1.x*v2.x + v1.y*v2.y); - return result; -} - -// Calculate distance between two vectors -RMDEF float Vector2Distance(Vector2 v1, Vector2 v2) -{ - float result = sqrtf((v1.x - v2.x)*(v1.x - v2.x) + (v1.y - v2.y)*(v1.y - v2.y)); - return result; -} - -// Calculate angle from two vectors in X-axis -RMDEF float Vector2Angle(Vector2 v1, Vector2 v2) -{ - float result = atan2f(v2.y - v1.y, v2.x - v1.x)*(180.0f/PI); - if (result < 0) result += 360.0f; - return result; -} - -// Scale vector (multiply by value) -RMDEF Vector2 Vector2Scale(Vector2 v, float scale) -{ - Vector2 result = { v.x*scale, v.y*scale }; - return result; -} - -// Negate vector -RMDEF Vector2 Vector2Negate(Vector2 v) -{ - Vector2 result = { -v.x, -v.y }; - return result; -} - -// Divide vector by a float value -RMDEF Vector2 Vector2Divide(Vector2 v, float div) -{ - Vector2 result = { v.x/div, v.y/div }; - return result; -} - -// Normalize provided vector -RMDEF Vector2 Vector2Normalize(Vector2 v) -{ - Vector2 result = Vector2Divide(v, Vector2Length(v)); - return result; -} - -//---------------------------------------------------------------------------------- -// Module Functions Definition - Vector3 math -//---------------------------------------------------------------------------------- - -// Vector with components value 0.0f -RMDEF Vector3 Vector3Zero(void) -{ - Vector3 result = { 0.0f, 0.0f, 0.0f }; - return result; -} - -// Vector with components value 1.0f -RMDEF Vector3 Vector3One(void) -{ - Vector3 result = { 1.0f, 1.0f, 1.0f }; - return result; -} - -// Add two vectors -RMDEF Vector3 Vector3Add(Vector3 v1, Vector3 v2) -{ - Vector3 result = { v1.x + v2.x, v1.y + v2.y, v1.z + v2.z }; - return result; -} - -// Substract two vectors -RMDEF Vector3 Vector3Subtract(Vector3 v1, Vector3 v2) -{ - Vector3 result = { v1.x - v2.x, v1.y - v2.y, v1.z - v2.z }; - return result; -} - -// Multiply vector by scalar -RMDEF Vector3 Vector3Multiply(Vector3 v, float scalar) -{ - Vector3 result = { v.x*scalar, v.y*scalar, v.z*scalar }; - return result; -} - -// Multiply vector by vector -RMDEF Vector3 Vector3MultiplyV(Vector3 v1, Vector3 v2) -{ - Vector3 result = { v1.x*v2.x, v1.y*v2.y, v1.z*v2.z }; - return result; -} - -// Calculate two vectors cross product -RMDEF Vector3 Vector3CrossProduct(Vector3 v1, Vector3 v2) -{ - Vector3 result = { v1.y*v2.z - v1.z*v2.y, v1.z*v2.x - v1.x*v2.z, v1.x*v2.y - v1.y*v2.x }; - return result; -} - -// Calculate one vector perpendicular vector -RMDEF Vector3 Vector3Perpendicular(Vector3 v) -{ - Vector3 result = { 0 }; - - float min = fabs(v.x); - Vector3 cardinalAxis = {1.0f, 0.0f, 0.0f}; - - if (fabs(v.y) < min) - { - min = fabs(v.y); - Vector3 tmp = {0.0f, 1.0f, 0.0f}; - cardinalAxis = tmp; - } - - if (fabs(v.z) < min) - { - Vector3 tmp = {0.0f, 0.0f, 1.0f}; - cardinalAxis = tmp; - } - - result = Vector3CrossProduct(v, cardinalAxis); - - return result; -} - -// Calculate vector length -RMDEF float Vector3Length(const Vector3 v) -{ - float result = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z); - return result; -} - -// Calculate two vectors dot product -RMDEF float Vector3DotProduct(Vector3 v1, Vector3 v2) -{ - float result = (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z); - return result; -} - -// Calculate distance between two vectors -RMDEF float Vector3Distance(Vector3 v1, Vector3 v2) -{ - float dx = v2.x - v1.x; - float dy = v2.y - v1.y; - float dz = v2.z - v1.z; - float result = sqrtf(dx*dx + dy*dy + dz*dz); - return result; -} - -// Scale provided vector -RMDEF Vector3 Vector3Scale(Vector3 v, float scale) -{ - Vector3 result = { v.x*scale, v.y*scale, v.z*scale }; - return result; -} - -// Negate provided vector (invert direction) -RMDEF Vector3 Vector3Negate(Vector3 v) -{ - Vector3 result = { -v.x, -v.y, -v.z }; - return result; -} - -// Normalize provided vector -RMDEF Vector3 Vector3Normalize(Vector3 v) -{ - Vector3 result = v; - - float length, ilength; - length = Vector3Length(v); - if (length == 0.0f) length = 1.0f; - ilength = 1.0f/length; - - result.x *= ilength; - result.y *= ilength; - result.z *= ilength; - - return result; -} - -// Orthonormalize provided vectors -// Makes vectors normalized and orthogonal to each other -// Gram-Schmidt function implementation -RMDEF void Vector3OrthoNormalize(Vector3 *v1, Vector3 *v2) -{ - *v1 = Vector3Normalize(*v1); - Vector3 vn = Vector3CrossProduct(*v1, *v2); - vn = Vector3Normalize(vn); - *v2 = Vector3CrossProduct(vn, *v1); -} - -// Transforms a Vector3 by a given Matrix -RMDEF Vector3 Vector3Transform(Vector3 v, Matrix mat) -{ - Vector3 result = { 0 }; - float x = v.x; - float y = v.y; - float z = v.z; - - result.x = mat.m0*x + mat.m4*y + mat.m8*z + mat.m12; - result.y = mat.m1*x + mat.m5*y + mat.m9*z + mat.m13; - result.z = mat.m2*x + mat.m6*y + mat.m10*z + mat.m14; - - return result; -}; - -// Transform a vector by quaternion rotation -RMDEF Vector3 Vector3RotateByQuaternion(Vector3 v, Quaternion q) -{ - Vector3 result = { 0 }; - - result.x = v.x*(q.x*q.x + q.w*q.w - q.y*q.y - q.z*q.z) + v.y*(2*q.x*q.y - 2*q.w*q.z) + v.z*(2*q.x*q.z + 2*q.w*q.y); - result.y = v.x*(2*q.w*q.z + 2*q.x*q.y) + v.y*(q.w*q.w - q.x*q.x + q.y*q.y - q.z*q.z) + v.z*(-2*q.w*q.x + 2*q.y*q.z); - result.z = v.x*(-2*q.w*q.y + 2*q.x*q.z) + v.y*(2*q.w*q.x + 2*q.y*q.z)+ v.z*(q.w*q.w - q.x*q.x - q.y*q.y + q.z*q.z); - - return result; -} - -// Calculate linear interpolation between two vectors -RMDEF Vector3 Vector3Lerp(Vector3 v1, Vector3 v2, float amount) -{ - Vector3 result = { 0 }; - - result.x = v1.x + amount*(v2.x - v1.x); - result.y = v1.y + amount*(v2.y - v1.y); - result.z = v1.z + amount*(v2.z - v1.z); - - return result; -} - -// Calculate reflected vector to normal -RMDEF Vector3 Vector3Reflect(Vector3 v, Vector3 normal) -{ - // I is the original vector - // N is the normal of the incident plane - // R = I - (2*N*( DotProduct[ I,N] )) - - Vector3 result = { 0 }; - - float dotProduct = Vector3DotProduct(v, normal); - - result.x = v.x - (2.0f*normal.x)*dotProduct; - result.y = v.y - (2.0f*normal.y)*dotProduct; - result.z = v.z - (2.0f*normal.z)*dotProduct; - - return result; -} - -// Return min value for each pair of components -RMDEF Vector3 Vector3Min(Vector3 v1, Vector3 v2) -{ - Vector3 result = { 0 }; - - result.x = fminf(v1.x, v2.x); - result.y = fminf(v1.y, v2.y); - result.z = fminf(v1.z, v2.z); - - return result; -} - -// Return max value for each pair of components -RMDEF Vector3 Vector3Max(Vector3 v1, Vector3 v2) -{ - Vector3 result = { 0 }; - - result.x = fmaxf(v1.x, v2.x); - result.y = fmaxf(v1.y, v2.y); - result.z = fmaxf(v1.z, v2.z); - - return result; -} - -// Compute barycenter coordinates (u, v, w) for point p with respect to triangle (a, b, c) -// NOTE: Assumes P is on the plane of the triangle -RMDEF Vector3 Vector3Barycenter(Vector3 p, Vector3 a, Vector3 b, Vector3 c) -{ - //Vector v0 = b - a, v1 = c - a, v2 = p - a; - - Vector3 v0 = Vector3Subtract(b, a); - Vector3 v1 = Vector3Subtract(c, a); - Vector3 v2 = Vector3Subtract(p, a); - float d00 = Vector3DotProduct(v0, v0); - float d01 = Vector3DotProduct(v0, v1); - float d11 = Vector3DotProduct(v1, v1); - float d20 = Vector3DotProduct(v2, v0); - float d21 = Vector3DotProduct(v2, v1); - - float denom = d00*d11 - d01*d01; - - Vector3 result = { 0 }; - - result.y = (d11*d20 - d01*d21)/denom; - result.z = (d00*d21 - d01*d20)/denom; - result.x = 1.0f - (result.z + result.y); - - return result; -} - -// Returns Vector3 as float array -RMDEF float3 Vector3ToFloatV(Vector3 v) -{ - float3 buffer = { 0 }; - - buffer.v[0] = v.x; - buffer.v[1] = v.y; - buffer.v[2] = v.z; - - return buffer; -} - -//---------------------------------------------------------------------------------- -// Module Functions Definition - Matrix math -//---------------------------------------------------------------------------------- - -// Compute matrix determinant -RMDEF float MatrixDeterminant(Matrix mat) -{ - float result = { 0 }; - - // Cache the matrix values (speed optimization) - float a00 = mat.m0, a01 = mat.m1, a02 = mat.m2, a03 = mat.m3; - float a10 = mat.m4, a11 = mat.m5, a12 = mat.m6, a13 = mat.m7; - float a20 = mat.m8, a21 = mat.m9, a22 = mat.m10, a23 = mat.m11; - float a30 = mat.m12, a31 = mat.m13, a32 = mat.m14, a33 = mat.m15; - - result = a30*a21*a12*a03 - a20*a31*a12*a03 - a30*a11*a22*a03 + a10*a31*a22*a03 + - a20*a11*a32*a03 - a10*a21*a32*a03 - a30*a21*a02*a13 + a20*a31*a02*a13 + - a30*a01*a22*a13 - a00*a31*a22*a13 - a20*a01*a32*a13 + a00*a21*a32*a13 + - a30*a11*a02*a23 - a10*a31*a02*a23 - a30*a01*a12*a23 + a00*a31*a12*a23 + - a10*a01*a32*a23 - a00*a11*a32*a23 - a20*a11*a02*a33 + a10*a21*a02*a33 + - a20*a01*a12*a33 - a00*a21*a12*a33 - a10*a01*a22*a33 + a00*a11*a22*a33; - - return result; -} - -// Returns the trace of the matrix (sum of the values along the diagonal) -RMDEF float MatrixTrace(Matrix mat) -{ - float result = (mat.m0 + mat.m5 + mat.m10 + mat.m15); - return result; -} - -// Transposes provided matrix -RMDEF Matrix MatrixTranspose(Matrix mat) -{ - Matrix result = { 0 }; - - result.m0 = mat.m0; - result.m1 = mat.m4; - result.m2 = mat.m8; - result.m3 = mat.m12; - result.m4 = mat.m1; - result.m5 = mat.m5; - result.m6 = mat.m9; - result.m7 = mat.m13; - result.m8 = mat.m2; - result.m9 = mat.m6; - result.m10 = mat.m10; - result.m11 = mat.m14; - result.m12 = mat.m3; - result.m13 = mat.m7; - result.m14 = mat.m11; - result.m15 = mat.m15; - - return result; -} - -// Invert provided matrix -RMDEF Matrix MatrixInvert(Matrix mat) -{ - Matrix result = { 0 }; - - // Cache the matrix values (speed optimization) - float a00 = mat.m0, a01 = mat.m1, a02 = mat.m2, a03 = mat.m3; - float a10 = mat.m4, a11 = mat.m5, a12 = mat.m6, a13 = mat.m7; - float a20 = mat.m8, a21 = mat.m9, a22 = mat.m10, a23 = mat.m11; - float a30 = mat.m12, a31 = mat.m13, a32 = mat.m14, a33 = mat.m15; - - float b00 = a00*a11 - a01*a10; - float b01 = a00*a12 - a02*a10; - float b02 = a00*a13 - a03*a10; - float b03 = a01*a12 - a02*a11; - float b04 = a01*a13 - a03*a11; - float b05 = a02*a13 - a03*a12; - float b06 = a20*a31 - a21*a30; - float b07 = a20*a32 - a22*a30; - float b08 = a20*a33 - a23*a30; - float b09 = a21*a32 - a22*a31; - float b10 = a21*a33 - a23*a31; - float b11 = a22*a33 - a23*a32; - - // Calculate the invert determinant (inlined to avoid double-caching) - float invDet = 1.0f/(b00*b11 - b01*b10 + b02*b09 + b03*b08 - b04*b07 + b05*b06); - - result.m0 = (a11*b11 - a12*b10 + a13*b09)*invDet; - result.m1 = (-a01*b11 + a02*b10 - a03*b09)*invDet; - result.m2 = (a31*b05 - a32*b04 + a33*b03)*invDet; - result.m3 = (-a21*b05 + a22*b04 - a23*b03)*invDet; - result.m4 = (-a10*b11 + a12*b08 - a13*b07)*invDet; - result.m5 = (a00*b11 - a02*b08 + a03*b07)*invDet; - result.m6 = (-a30*b05 + a32*b02 - a33*b01)*invDet; - result.m7 = (a20*b05 - a22*b02 + a23*b01)*invDet; - result.m8 = (a10*b10 - a11*b08 + a13*b06)*invDet; - result.m9 = (-a00*b10 + a01*b08 - a03*b06)*invDet; - result.m10 = (a30*b04 - a31*b02 + a33*b00)*invDet; - result.m11 = (-a20*b04 + a21*b02 - a23*b00)*invDet; - result.m12 = (-a10*b09 + a11*b07 - a12*b06)*invDet; - result.m13 = (a00*b09 - a01*b07 + a02*b06)*invDet; - result.m14 = (-a30*b03 + a31*b01 - a32*b00)*invDet; - result.m15 = (a20*b03 - a21*b01 + a22*b00)*invDet; - - return result; -} - -// Normalize provided matrix -RMDEF Matrix MatrixNormalize(Matrix mat) -{ - Matrix result = { 0 }; - - float det = MatrixDeterminant(mat); - - result.m0 = mat.m0/det; - result.m1 = mat.m1/det; - result.m2 = mat.m2/det; - result.m3 = mat.m3/det; - result.m4 = mat.m4/det; - result.m5 = mat.m5/det; - result.m6 = mat.m6/det; - result.m7 = mat.m7/det; - result.m8 = mat.m8/det; - result.m9 = mat.m9/det; - result.m10 = mat.m10/det; - result.m11 = mat.m11/det; - result.m12 = mat.m12/det; - result.m13 = mat.m13/det; - result.m14 = mat.m14/det; - result.m15 = mat.m15/det; - - return result; -} - -// Returns identity matrix -RMDEF Matrix MatrixIdentity(void) -{ - Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f }; - - return result; -} - -// Add two matrices -RMDEF Matrix MatrixAdd(Matrix left, Matrix right) -{ - Matrix result = MatrixIdentity(); - - result.m0 = left.m0 + right.m0; - result.m1 = left.m1 + right.m1; - result.m2 = left.m2 + right.m2; - result.m3 = left.m3 + right.m3; - result.m4 = left.m4 + right.m4; - result.m5 = left.m5 + right.m5; - result.m6 = left.m6 + right.m6; - result.m7 = left.m7 + right.m7; - result.m8 = left.m8 + right.m8; - result.m9 = left.m9 + right.m9; - result.m10 = left.m10 + right.m10; - result.m11 = left.m11 + right.m11; - result.m12 = left.m12 + right.m12; - result.m13 = left.m13 + right.m13; - result.m14 = left.m14 + right.m14; - result.m15 = left.m15 + right.m15; - - return result; -} - -// Substract two matrices (left - right) -RMDEF Matrix MatrixSubstract(Matrix left, Matrix right) -{ - Matrix result = MatrixIdentity(); - - result.m0 = left.m0 - right.m0; - result.m1 = left.m1 - right.m1; - result.m2 = left.m2 - right.m2; - result.m3 = left.m3 - right.m3; - result.m4 = left.m4 - right.m4; - result.m5 = left.m5 - right.m5; - result.m6 = left.m6 - right.m6; - result.m7 = left.m7 - right.m7; - result.m8 = left.m8 - right.m8; - result.m9 = left.m9 - right.m9; - result.m10 = left.m10 - right.m10; - result.m11 = left.m11 - right.m11; - result.m12 = left.m12 - right.m12; - result.m13 = left.m13 - right.m13; - result.m14 = left.m14 - right.m14; - result.m15 = left.m15 - right.m15; - - return result; -} - -// Returns translation matrix -RMDEF Matrix MatrixTranslate(float x, float y, float z) -{ - Matrix result = { 1.0f, 0.0f, 0.0f, x, - 0.0f, 1.0f, 0.0f, y, - 0.0f, 0.0f, 1.0f, z, - 0.0f, 0.0f, 0.0f, 1.0f }; - - return result; -} - -// Create rotation matrix from axis and angle -// NOTE: Angle should be provided in radians -RMDEF Matrix MatrixRotate(Vector3 axis, float angle) -{ - Matrix result = { 0 }; - - float x = axis.x, y = axis.y, z = axis.z; - - float length = sqrtf(x*x + y*y + z*z); - - if ((length != 1.0f) && (length != 0.0f)) - { - length = 1.0f/length; - x *= length; - y *= length; - z *= length; - } - - float sinres = sinf(angle); - float cosres = cosf(angle); - float t = 1.0f - cosres; - - result.m0 = x*x*t + cosres; - result.m1 = y*x*t + z*sinres; - result.m2 = z*x*t - y*sinres; - result.m3 = 0.0f; - - result.m4 = x*y*t - z*sinres; - result.m5 = y*y*t + cosres; - result.m6 = z*y*t + x*sinres; - result.m7 = 0.0f; - - result.m8 = x*z*t + y*sinres; - result.m9 = y*z*t - x*sinres; - result.m10 = z*z*t + cosres; - result.m11 = 0.0f; - - result.m12 = 0.0f; - result.m13 = 0.0f; - result.m14 = 0.0f; - result.m15 = 1.0f; - - return result; -} - -// Returns x-rotation matrix (angle in radians) -RMDEF Matrix MatrixRotateX(float angle) -{ - Matrix result = MatrixIdentity(); - - float cosres = cosf(angle); - float sinres = sinf(angle); - - result.m5 = cosres; - result.m6 = -sinres; - result.m9 = sinres; - result.m10 = cosres; - - return result; -} - -// Returns y-rotation matrix (angle in radians) -RMDEF Matrix MatrixRotateY(float angle) -{ - Matrix result = MatrixIdentity(); - - float cosres = cosf(angle); - float sinres = sinf(angle); - - result.m0 = cosres; - result.m2 = sinres; - result.m8 = -sinres; - result.m10 = cosres; - - return result; -} - -// Returns z-rotation matrix (angle in radians) -RMDEF Matrix MatrixRotateZ(float angle) -{ - Matrix result = MatrixIdentity(); - - float cosres = cosf(angle); - float sinres = sinf(angle); - - result.m0 = cosres; - result.m1 = -sinres; - result.m4 = sinres; - result.m5 = cosres; - - return result; -} - -// Returns scaling matrix -RMDEF Matrix MatrixScale(float x, float y, float z) -{ - Matrix result = { x, 0.0f, 0.0f, 0.0f, - 0.0f, y, 0.0f, 0.0f, - 0.0f, 0.0f, z, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f }; - - return result; -} - -// Returns two matrix multiplication -// NOTE: When multiplying matrices... the order matters! -RMDEF Matrix MatrixMultiply(Matrix left, Matrix right) -{ - Matrix result = { 0 }; - - result.m0 = left.m0*right.m0 + left.m1*right.m4 + left.m2*right.m8 + left.m3*right.m12; - result.m1 = left.m0*right.m1 + left.m1*right.m5 + left.m2*right.m9 + left.m3*right.m13; - result.m2 = left.m0*right.m2 + left.m1*right.m6 + left.m2*right.m10 + left.m3*right.m14; - result.m3 = left.m0*right.m3 + left.m1*right.m7 + left.m2*right.m11 + left.m3*right.m15; - result.m4 = left.m4*right.m0 + left.m5*right.m4 + left.m6*right.m8 + left.m7*right.m12; - result.m5 = left.m4*right.m1 + left.m5*right.m5 + left.m6*right.m9 + left.m7*right.m13; - result.m6 = left.m4*right.m2 + left.m5*right.m6 + left.m6*right.m10 + left.m7*right.m14; - result.m7 = left.m4*right.m3 + left.m5*right.m7 + left.m6*right.m11 + left.m7*right.m15; - result.m8 = left.m8*right.m0 + left.m9*right.m4 + left.m10*right.m8 + left.m11*right.m12; - result.m9 = left.m8*right.m1 + left.m9*right.m5 + left.m10*right.m9 + left.m11*right.m13; - result.m10 = left.m8*right.m2 + left.m9*right.m6 + left.m10*right.m10 + left.m11*right.m14; - result.m11 = left.m8*right.m3 + left.m9*right.m7 + left.m10*right.m11 + left.m11*right.m15; - result.m12 = left.m12*right.m0 + left.m13*right.m4 + left.m14*right.m8 + left.m15*right.m12; - result.m13 = left.m12*right.m1 + left.m13*right.m5 + left.m14*right.m9 + left.m15*right.m13; - result.m14 = left.m12*right.m2 + left.m13*right.m6 + left.m14*right.m10 + left.m15*right.m14; - result.m15 = left.m12*right.m3 + left.m13*right.m7 + left.m14*right.m11 + left.m15*right.m15; - - return result; -} - -// Returns perspective projection matrix -RMDEF Matrix MatrixFrustum(double left, double right, double bottom, double top, double near, double far) -{ - Matrix result = { 0 }; - - float rl = (right - left); - float tb = (top - bottom); - float fn = (far - near); - - result.m0 = (near*2.0f)/rl; - result.m1 = 0.0f; - result.m2 = 0.0f; - result.m3 = 0.0f; - - result.m4 = 0.0f; - result.m5 = (near*2.0f)/tb; - result.m6 = 0.0f; - result.m7 = 0.0f; - - result.m8 = (right + left)/rl; - result.m9 = (top + bottom)/tb; - result.m10 = -(far + near)/fn; - result.m11 = -1.0f; - - result.m12 = 0.0f; - result.m13 = 0.0f; - result.m14 = -(far*near*2.0f)/fn; - result.m15 = 0.0f; - - return result; -} - -// Returns perspective projection matrix -// NOTE: Angle should be provided in radians -RMDEF Matrix MatrixPerspective(double fovy, double aspect, double near, double far) -{ - double top = near*tan(fovy*0.5); - double right = top*aspect; - Matrix result = MatrixFrustum(-right, right, -top, top, near, far); - - return result; -} - -// Returns orthographic projection matrix -RMDEF Matrix MatrixOrtho(double left, double right, double bottom, double top, double near, double far) -{ - Matrix result = { 0 }; - - float rl = (right - left); - float tb = (top - bottom); - float fn = (far - near); - - result.m0 = 2.0f/rl; - result.m1 = 0.0f; - result.m2 = 0.0f; - result.m3 = 0.0f; - result.m4 = 0.0f; - result.m5 = 2.0f/tb; - result.m6 = 0.0f; - result.m7 = 0.0f; - result.m8 = 0.0f; - result.m9 = 0.0f; - result.m10 = -2.0f/fn; - result.m11 = 0.0f; - result.m12 = -(left + right)/rl; - result.m13 = -(top + bottom)/tb; - result.m14 = -(far + near)/fn; - result.m15 = 1.0f; - - return result; -} - -// Returns camera look-at matrix (view matrix) -RMDEF Matrix MatrixLookAt(Vector3 eye, Vector3 target, Vector3 up) -{ - Matrix result = { 0 }; - - Vector3 z = Vector3Subtract(eye, target); - z = Vector3Normalize(z); - Vector3 x = Vector3CrossProduct(up, z); - x = Vector3Normalize(x); - Vector3 y = Vector3CrossProduct(z, x); - y = Vector3Normalize(y); - - result.m0 = x.x; - result.m1 = x.y; - result.m2 = x.z; - result.m3 = 0.0f; - result.m4 = y.x; - result.m5 = y.y; - result.m6 = y.z; - result.m7 = 0.0f; - result.m8 = z.x; - result.m9 = z.y; - result.m10 = z.z; - result.m11 = 0.0f; - result.m12 = eye.x; - result.m13 = eye.y; - result.m14 = eye.z; - result.m15 = 1.0f; - - result = MatrixInvert(result); - - return result; -} - -// Returns float array of matrix data -RMDEF float16 MatrixToFloatV(Matrix mat) -{ - float16 buffer = { 0 }; - - buffer.v[0] = mat.m0; - buffer.v[1] = mat.m1; - buffer.v[2] = mat.m2; - buffer.v[3] = mat.m3; - buffer.v[4] = mat.m4; - buffer.v[5] = mat.m5; - buffer.v[6] = mat.m6; - buffer.v[7] = mat.m7; - buffer.v[8] = mat.m8; - buffer.v[9] = mat.m9; - buffer.v[10] = mat.m10; - buffer.v[11] = mat.m11; - buffer.v[12] = mat.m12; - buffer.v[13] = mat.m13; - buffer.v[14] = mat.m14; - buffer.v[15] = mat.m15; - - return buffer; -} - -//---------------------------------------------------------------------------------- -// Module Functions Definition - Quaternion math -//---------------------------------------------------------------------------------- - -// Returns identity quaternion -RMDEF Quaternion QuaternionIdentity(void) -{ - Quaternion result = { 0.0f, 0.0f, 0.0f, 1.0f }; - return result; -} - -// Computes the length of a quaternion -RMDEF float QuaternionLength(Quaternion q) -{ - float result = sqrt(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w); - return result; -} - -// Normalize provided quaternion -RMDEF Quaternion QuaternionNormalize(Quaternion q) -{ - Quaternion result = { 0 }; - - float length, ilength; - length = QuaternionLength(q); - if (length == 0.0f) length = 1.0f; - ilength = 1.0f/length; - - result.x = q.x*ilength; - result.y = q.y*ilength; - result.z = q.z*ilength; - result.w = q.w*ilength; - - return result; -} - -// Invert provided quaternion -RMDEF Quaternion QuaternionInvert(Quaternion q) -{ - Quaternion result = q; - float length = QuaternionLength(q); - float lengthSq = length*length; - - if (lengthSq != 0.0) - { - float i = 1.0f/lengthSq; - - result.x *= -i; - result.y *= -i; - result.z *= -i; - result.w *= i; - } - - return result; -} - -// Calculate two quaternion multiplication -RMDEF Quaternion QuaternionMultiply(Quaternion q1, Quaternion q2) -{ - Quaternion result = { 0 }; - - float qax = q1.x, qay = q1.y, qaz = q1.z, qaw = q1.w; - float qbx = q2.x, qby = q2.y, qbz = q2.z, qbw = q2.w; - - result.x = qax*qbw + qaw*qbx + qay*qbz - qaz*qby; - result.y = qay*qbw + qaw*qby + qaz*qbx - qax*qbz; - result.z = qaz*qbw + qaw*qbz + qax*qby - qay*qbx; - result.w = qaw*qbw - qax*qbx - qay*qby - qaz*qbz; - - return result; -} - -// Calculate linear interpolation between two quaternions -RMDEF Quaternion QuaternionLerp(Quaternion q1, Quaternion q2, float amount) -{ - Quaternion result = { 0 }; - - result.x = q1.x + amount*(q2.x - q1.x); - result.y = q1.y + amount*(q2.y - q1.y); - result.z = q1.z + amount*(q2.z - q1.z); - result.w = q1.w + amount*(q2.w - q1.w); - - return result; -} - -// Calculate slerp-optimized interpolation between two quaternions -RMDEF Quaternion QuaternionNlerp(Quaternion q1, Quaternion q2, float amount) -{ - Quaternion result = QuaternionLerp(q1, q2, amount); - result = QuaternionNormalize(result); - - return result; -} - -// Calculates spherical linear interpolation between two quaternions -RMDEF Quaternion QuaternionSlerp(Quaternion q1, Quaternion q2, float amount) -{ - Quaternion result = { 0 }; - - float cosHalfTheta = q1.x*q2.x + q1.y*q2.y + q1.z*q2.z + q1.w*q2.w; - - if (fabs(cosHalfTheta) >= 1.0f) result = q1; - else if (cosHalfTheta > 0.95f) result = QuaternionNlerp(q1, q2, amount); - else - { - float halfTheta = acos(cosHalfTheta); - float sinHalfTheta = sqrt(1.0f - cosHalfTheta*cosHalfTheta); - - if (fabs(sinHalfTheta) < 0.001f) - { - result.x = (q1.x*0.5f + q2.x*0.5f); - result.y = (q1.y*0.5f + q2.y*0.5f); - result.z = (q1.z*0.5f + q2.z*0.5f); - result.w = (q1.w*0.5f + q2.w*0.5f); - } - else - { - float ratioA = sinf((1 - amount)*halfTheta)/sinHalfTheta; - float ratioB = sinf(amount*halfTheta)/sinHalfTheta; - - result.x = (q1.x*ratioA + q2.x*ratioB); - result.y = (q1.y*ratioA + q2.y*ratioB); - result.z = (q1.z*ratioA + q2.z*ratioB); - result.w = (q1.w*ratioA + q2.w*ratioB); - } - } - - return result; -} - -// Calculate quaternion based on the rotation from one vector to another -RMDEF Quaternion QuaternionFromVector3ToVector3(Vector3 from, Vector3 to) -{ - Quaternion result = { 0 }; - - float cos2Theta = Vector3DotProduct(from, to); - Vector3 cross = Vector3CrossProduct(from, to); - - result.x = cross.x; - result.y = cross.y; - result.z = cross.y; - result.w = 1.0f + cos2Theta; // NOTE: Added QuaternioIdentity() - - // Normalize to essentially nlerp the original and identity to 0.5 - result = QuaternionNormalize(result); - - // Above lines are equivalent to: - //Quaternion result = QuaternionNlerp(q, QuaternionIdentity(), 0.5f); - - return result; -} - -// Returns a quaternion for a given rotation matrix -RMDEF Quaternion QuaternionFromMatrix(Matrix mat) -{ - Quaternion result = { 0 }; - - float trace = MatrixTrace(mat); - - if (trace > 0.0f) - { - float s = (float)sqrt(trace + 1)*2.0f; - float invS = 1.0f/s; - - result.w = s*0.25f; - result.x = (mat.m6 - mat.m9)*invS; - result.y = (mat.m8 - mat.m2)*invS; - result.z = (mat.m1 - mat.m4)*invS; - } - else - { - float m00 = mat.m0, m11 = mat.m5, m22 = mat.m10; - - if (m00 > m11 && m00 > m22) - { - float s = (float)sqrt(1.0f + m00 - m11 - m22)*2.0f; - float invS = 1.0f/s; - - result.w = (mat.m6 - mat.m9)*invS; - result.x = s*0.25f; - result.y = (mat.m4 + mat.m1)*invS; - result.z = (mat.m8 + mat.m2)*invS; - } - else if (m11 > m22) - { - float s = (float)sqrt(1.0f + m11 - m00 - m22)*2.0f; - float invS = 1.0f/s; - - result.w = (mat.m8 - mat.m2)*invS; - result.x = (mat.m4 + mat.m1)*invS; - result.y = s*0.25f; - result.z = (mat.m9 + mat.m6)*invS; - } - else - { - float s = (float)sqrt(1.0f + m22 - m00 - m11)*2.0f; - float invS = 1.0f/s; - - result.w = (mat.m1 - mat.m4)*invS; - result.x = (mat.m8 + mat.m2)*invS; - result.y = (mat.m9 + mat.m6)*invS; - result.z = s*0.25f; - } - } - - return result; -} - -// Returns a matrix for a given quaternion -RMDEF Matrix QuaternionToMatrix(Quaternion q) -{ - Matrix result = { 0 }; - - float x = q.x, y = q.y, z = q.z, w = q.w; - - float x2 = x + x; - float y2 = y + y; - float z2 = z + z; - - float length = QuaternionLength(q); - float lengthSquared = length*length; - - float xx = x*x2/lengthSquared; - float xy = x*y2/lengthSquared; - float xz = x*z2/lengthSquared; - - float yy = y*y2/lengthSquared; - float yz = y*z2/lengthSquared; - float zz = z*z2/lengthSquared; - - float wx = w*x2/lengthSquared; - float wy = w*y2/lengthSquared; - float wz = w*z2/lengthSquared; - - result.m0 = 1.0f - (yy + zz); - result.m1 = xy - wz; - result.m2 = xz + wy; - result.m3 = 0.0f; - result.m4 = xy + wz; - result.m5 = 1.0f - (xx + zz); - result.m6 = yz - wx; - result.m7 = 0.0f; - result.m8 = xz - wy; - result.m9 = yz + wx; - result.m10 = 1.0f - (xx + yy); - result.m11 = 0.0f; - result.m12 = 0.0f; - result.m13 = 0.0f; - result.m14 = 0.0f; - result.m15 = 1.0f; - - return result; -} - -// Returns rotation quaternion for an angle and axis -// NOTE: angle must be provided in radians -RMDEF Quaternion QuaternionFromAxisAngle(Vector3 axis, float angle) -{ - Quaternion result = { 0.0f, 0.0f, 0.0f, 1.0f }; - - if (Vector3Length(axis) != 0.0f) - - angle *= 0.5f; - - axis = Vector3Normalize(axis); - - float sinres = sinf(angle); - float cosres = cosf(angle); - - result.x = axis.x*sinres; - result.y = axis.y*sinres; - result.z = axis.z*sinres; - result.w = cosres; - - result = QuaternionNormalize(result); - - return result; -} - -// Returns the rotation angle and axis for a given quaternion -RMDEF void QuaternionToAxisAngle(Quaternion q, Vector3 *outAxis, float *outAngle) -{ - if (fabs(q.w) > 1.0f) q = QuaternionNormalize(q); - - 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); - - if (den > 0.0001f) - { - resAxis.x = q.x/den; - resAxis.y = q.y/den; - resAxis.z = q.z/den; - } - else - { - // This occurs when the angle is zero. - // Not a problem: just set an arbitrary normalized axis. - resAxis.x = 1.0f; - } - - *outAxis = resAxis; - *outAngle = resAngle; -} - -// Returns he quaternion equivalent to Euler angles -RMDEF Quaternion QuaternionFromEuler(float roll, float pitch, float yaw) -{ - Quaternion q = { 0 }; - - float x0 = cosf(roll*0.5f); - float x1 = sinf(roll*0.5f); - float y0 = cosf(pitch*0.5f); - float y1 = sinf(pitch*0.5f); - float z0 = cosf(yaw*0.5f); - float z1 = sinf(yaw*0.5f); - - q.x = x1*y0*z0 - x0*y1*z1; - q.y = x0*y1*z0 + x1*y0*z1; - q.z = x0*y0*z1 - x1*y1*z0; - q.w = x0*y0*z0 + x1*y1*z1; - - return q; -} - -// Return the Euler angles equivalent to quaternion (roll, pitch, yaw) -// NOTE: Angles are returned in a Vector3 struct in degrees -RMDEF Vector3 QuaternionToEuler(Quaternion q) -{ - Vector3 result = { 0 }; - - // roll (x-axis rotation) - float x0 = 2.0f*(q.w*q.x + q.y*q.z); - float x1 = 1.0f - 2.0f*(q.x*q.x + q.y*q.y); - result.x = atan2f(x0, x1)*RAD2DEG; - - // pitch (y-axis rotation) - float y0 = 2.0f*(q.w*q.y - q.z*q.x); - y0 = y0 > 1.0f ? 1.0f : y0; - y0 = y0 < -1.0f ? -1.0f : y0; - result.y = asinf(y0)*RAD2DEG; - - // yaw (z-axis rotation) - float z0 = 2.0f*(q.w*q.z + q.x*q.y); - float z1 = 1.0f - 2.0f*(q.y*q.y + q.z*q.z); - result.z = atan2f(z0, z1)*RAD2DEG; - - return result; -} - -// Transform a quaternion given a transformation matrix -RMDEF Quaternion QuaternionTransform(Quaternion q, Matrix mat) -{ - Quaternion result = { 0 }; - - result.x = mat.m0*q.x + mat.m4*q.y + mat.m8*q.z + mat.m12*q.w; - result.y = mat.m1*q.x + mat.m5*q.y + mat.m9*q.z + mat.m13*q.w; - result.z = mat.m2*q.x + mat.m6*q.y + mat.m10*q.z + mat.m14*q.w; - result.w = mat.m3*q.x + mat.m7*q.y + mat.m11*q.z + mat.m15*q.w; - - return result; -} - -#endif // RAYMATH_H diff --git a/examples/others/iqm_loader/riqm.h b/examples/others/iqm_loader/riqm.h deleted file mode 100644 index 694e3a958..000000000 --- a/examples/others/iqm_loader/riqm.h +++ /dev/null @@ -1,833 +0,0 @@ -/********************************************************************************************** -* -* riqm - InterQuake Model format (IQM) loader for animated meshes -* -* CONFIGURATION: -* -* #define RIQM_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) 2018 Jonas Daeyaert (@culacant) and Ramon Santamaria (@raysan5) -* -* 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 RIQM_H -#define RIQM_H - -//#define RIQM_STATIC -#ifdef RIQM_STATIC - #define RIQMDEF static // Functions just visible to module including this file -#else - #ifdef __cplusplus - #define RIQMDEF extern "C" // Functions visible from other files (no name mangling of functions in C++) - #else - #define RIQMDEF extern // Functions visible from other files - #endif -#endif - -//---------------------------------------------------------------------------------- -// Types and Structures Definition -//---------------------------------------------------------------------------------- - -#define JOINT_NAME_LENGTH 32 // Joint name string length -#define MESH_NAME_LENGTH 32 // Mesh name string length - -typedef struct Joint { - char name[JOINT_NAME_LENGTH]; - int parent; -} Joint; - -typedef struct Pose { - Vector3 translation; - Quaternion rotation; - Vector3 scale; -} Pose; - -typedef struct Animation { - int jointCount; // Number of joints (bones) - Joint *joints; // Joints array - // NOTE: Joints in anims do not have names - - int frameCount; // Number of animation frames - float framerate; // Frame change speed - - Pose **framepose; // Poses array by frame (and one pose by joint) -} Animation; - -// Animated Model type -typedef struct AnimatedModel { - Matrix transform; // Local transform matrix - - int meshCount; // Number of meshes - Mesh *meshes; // Meshes array - - int materialCount; // Number of materials - Material *materials; // Materials array - - int *meshMaterialId; // Mesh materials ids - - // Animation required data - int jointCount; // Number of joints (and keyposes) - Joint *joints; // Mesh joints (bones) - Pose *basepose; // Mesh base-poses by joint -} AnimatedModel; - -//---------------------------------------------------------------------------------- -// Module Functions Declaration -//---------------------------------------------------------------------------------- - -// Loading/Unloading functions -RIQMDEF AnimatedModel LoadAnimatedModel(const char *filename); -RIQMDEF void UnloadAnimatedModel(AnimatedModel model); -RIQMDEF Animation LoadAnimation(const char *filename); -RIQMDEF void UnloadAnimation(Animation anim); - -RIQMDEF AnimatedModel AnimatedModelAddTexture(AnimatedModel model, const char *filename); // GENERIC! -RIQMDEF AnimatedModel SetMeshMaterial(AnimatedModel model, int meshid, int textureid); // GENERIC! - -// Usage functionality -RIQMDEF bool CheckSkeletonsMatch(AnimatedModel model, Animation anim); -RIQMDEF void AnimateModel(AnimatedModel model, Animation anim, int frame); -RIQMDEF void DrawAnimatedModel(AnimatedModel model, Vector3 position, float scale, Color tint); -RIQMDEF void DrawAnimatedModelEx(AnimatedModel model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); - -#endif // RIQM_H - - -/*********************************************************************************** -* -* RIQM IMPLEMENTATION -* -************************************************************************************/ - -#if defined(RIQM_IMPLEMENTATION) - -//#include "utils.h" // Required for: fopen() Android mapping - -#include // Required for: FILE, fopen(), fclose(), feof(), fseek(), fread() -#include // Required for: malloc(), free() -#include // Required for: strncmp(),strcpy() - -#include "raymath.h" // Required for: Vector3, Quaternion functions - -//---------------------------------------------------------------------------------- -// Defines and Macros -//---------------------------------------------------------------------------------- -#define IQM_MAGIC "INTERQUAKEMODEL" // IQM file magic number -#define IQM_VERSION 2 // only IQM version 2 supported -#define ANIMJOINTNAME "ANIMJOINT" // default joint name (used in Animation) - -//---------------------------------------------------------------------------------- -// Types and Structures Definition -//---------------------------------------------------------------------------------- -// iqm file structs -typedef struct IQMHeader { - char magic[16]; - unsigned int version; - unsigned int filesize; - unsigned int flags; - unsigned int num_text, ofs_text; - unsigned int num_meshes, ofs_meshes; - unsigned int num_vertexarrays, num_vertexes, ofs_vertexarrays; - unsigned int num_triangles, ofs_triangles, ofs_adjacency; - unsigned int num_joints, ofs_joints; - unsigned int num_poses, ofs_poses; - unsigned int num_anims, ofs_anims; - unsigned int num_frames, num_framechannels, ofs_frames, ofs_bounds; - unsigned int num_comment, ofs_comment; - unsigned int num_extensions, ofs_extensions; -} IQMHeader; - -typedef struct IQMMesh { - unsigned int name; - unsigned int material; - unsigned int first_vertex, num_vertexes; - unsigned int first_triangle, num_triangles; -} IQMMesh; - -typedef struct IQMTriangle { - unsigned int vertex[3]; -} IQMTriangle; - -typedef struct IQMAdjacency { // adjacency unused by default - unsigned int triangle[3]; -} IQMAdjacency; - -typedef struct IQMJoint { - unsigned int name; - int parent; - float translate[3], rotate[4], scale[3]; -} IQMJoint; - -typedef struct IQMPose { - int parent; - unsigned int mask; - float channeloffset[10]; - float channelscale[10]; -} IQMPose; - -typedef struct IQMAnim { - unsigned int name; - unsigned int first_frame, num_frames; - float framerate; - unsigned int flags; -} IQMAnim; - -typedef struct IQMVertexArray { - unsigned int type; - unsigned int flags; - unsigned int format; - unsigned int size; - unsigned int offset; -} IQMVertexArray; - -typedef struct IQMBounds { // bounds unused by default - float bbmin[3], bbmax[3]; - float xyradius, radius; -} IQMBounds; - - -typedef enum { - IQM_POSITION = 0, - IQM_TEXCOORD = 1, - IQM_NORMAL = 2, - IQM_TANGENT = 3, // tangents unused by default - IQM_BLENDINDEXES = 4, - IQM_BLENDWEIGHTS = 5, - IQM_COLOR = 6, // vertex colors unused by default - IQM_CUSTOM = 0x10 // custom vertex values unused by default -} IQMVertexType; - -//---------------------------------------------------------------------------------- -// Global Variables Definition -//---------------------------------------------------------------------------------- - -//---------------------------------------------------------------------------------- -// Module specific Functions Declaration -//---------------------------------------------------------------------------------- -static AnimatedModel LoadIQM(const char *filename); - -#ifdef __cplusplus -extern "C" { // Prevents name mangling of functions -#endif - -// Load .iqm file and initialize animated model -AnimatedModel LoadAnimatedModel(const char *filename) -{ - AnimatedModel out = LoadIQM(filename); - - for (int i = 0; i < out.meshCount; i++) rlLoadMesh(&out.meshes[i], false); - - out.transform = MatrixIdentity(); - out.meshMaterialId = malloc(sizeof(int)*out.meshCount); - out.materials = NULL; - out.materialCount = 0; - - for (int i = 0; i < out.meshCount; i++) out.meshMaterialId[i] = -1; - - return out; -} - -// Add a texture to an animated model -AnimatedModel AnimatedModelAddTexture(AnimatedModel model, const char *filename) -{ - Texture2D texture = LoadTexture(filename); - - model.materials = realloc(model.materials, sizeof(Material)*(model.materialCount + 1)); - model.materials[model.materialCount] = LoadMaterialDefault(); - model.materials[model.materialCount].maps[MAP_DIFFUSE].texture = texture; - model.materialCount++; - - return model; -} - -// Set the material for a meshes -AnimatedModel SetMeshMaterial(AnimatedModel model, int meshid, int textureid) -{ - if (meshid > model.meshCount) - { - TraceLog(LOG_WARNING, "MeshId greater than meshCount\n"); - return model; - } - - if (textureid > model.materialCount) - { - TraceLog(LOG_WARNING,"textureid greater than materialCount\n"); - return model; - } - - model.meshMaterialId[meshid] = textureid; - - return model; -} - -// Load animations from a .iqm file -Animation LoadAnimationFromIQM(const char *filename) -{ - Animation animation = { 0 }; - - FILE *iqmFile; - IQMHeader iqm; - - iqmFile = fopen(filename,"rb"); - - if (!iqmFile) - { - TraceLog(LOG_ERROR, "[%s] Unable to open file", filename); - return animation; - } - - // header - fread(&iqm, sizeof(IQMHeader), 1, iqmFile); - - if (strncmp(iqm.magic, IQM_MAGIC, sizeof(IQM_MAGIC))) - { - TraceLog(LOG_ERROR, "Magic Number \"%s\"does not match.", iqm.magic); - fclose(iqmFile); - return animation; - } - - if (iqm.version != IQM_VERSION) - { - TraceLog(LOG_ERROR, "IQM version %i is incorrect.", iqm.version); - fclose(iqmFile); - return animation; - } - - // header - if (iqm.num_anims > 1) TraceLog(LOG_WARNING, "More than 1 animation in file, only the first one will get loaded"); - - // joints - IQMPose *poses; - poses = malloc(sizeof(IQMPose)*iqm.num_poses); - fseek(iqmFile, iqm.ofs_poses, SEEK_SET); - fread(poses, sizeof(IQMPose)*iqm.num_poses, 1, iqmFile); - - animation.jointCount = iqm.num_poses; - animation.joints = malloc(sizeof(Joint)*iqm.num_poses); - - for (int j = 0; j < iqm.num_poses; j++) - { - strcpy(animation.joints[j].name, ANIMJOINTNAME); - animation.joints[j].parent = poses[j].parent; - } - - // animations - IQMAnim anim = {0}; - fseek(iqmFile, iqm.ofs_anims, SEEK_SET); - fread(&anim, sizeof(IQMAnim), 1, iqmFile); - - animation.frameCount = anim.num_frames; - animation.framerate = anim.framerate; - - // frameposes - unsigned short *framedata = malloc(sizeof(unsigned short)*iqm.num_frames*iqm.num_framechannels); - fseek(iqmFile, iqm.ofs_frames, SEEK_SET); - fread(framedata, sizeof(unsigned short)*iqm.num_frames*iqm.num_framechannels, 1, iqmFile); - - animation.framepose = malloc(sizeof(Pose*)*anim.num_frames); - for (int j = 0; j < anim.num_frames; j++) animation.framepose[j] = malloc(sizeof(Pose)*iqm.num_poses); - - int dcounter = anim.first_frame*iqm.num_framechannels; - - for (int frame = 0; frame < anim.num_frames; frame++) - { - for (int i = 0; i < iqm.num_poses; i++) - { - animation.framepose[frame][i].translation.x = poses[i].channeloffset[0]; - - if (poses[i].mask & 0x01) - { - animation.framepose[frame][i].translation.x += framedata[dcounter]*poses[i].channelscale[0]; - dcounter++; - } - - animation.framepose[frame][i].translation.y = poses[i].channeloffset[1]; - - if (poses[i].mask & 0x02) - { - animation.framepose[frame][i].translation.y += framedata[dcounter]*poses[i].channelscale[1]; - dcounter++; - } - - animation.framepose[frame][i].translation.z = poses[i].channeloffset[2]; - - if (poses[i].mask & 0x04) - { - animation.framepose[frame][i].translation.z += framedata[dcounter]*poses[i].channelscale[2]; - dcounter++; - } - - animation.framepose[frame][i].rotation.x = poses[i].channeloffset[3]; - - if (poses[i].mask & 0x08) - { - animation.framepose[frame][i].rotation.x += framedata[dcounter]*poses[i].channelscale[3]; - dcounter++; - } - - animation.framepose[frame][i].rotation.y = poses[i].channeloffset[4]; - - if (poses[i].mask & 0x10) - { - animation.framepose[frame][i].rotation.y += framedata[dcounter]*poses[i].channelscale[4]; - dcounter++; - } - - animation.framepose[frame][i].rotation.z = poses[i].channeloffset[5]; - - if (poses[i].mask & 0x20) - { - animation.framepose[frame][i].rotation.z += framedata[dcounter]*poses[i].channelscale[5]; - dcounter++; - } - - animation.framepose[frame][i].rotation.w = poses[i].channeloffset[6]; - - if (poses[i].mask & 0x40) - { - animation.framepose[frame][i].rotation.w += framedata[dcounter]*poses[i].channelscale[6]; - dcounter++; - } - - animation.framepose[frame][i].scale.x = poses[i].channeloffset[7]; - - if (poses[i].mask & 0x80) - { - animation.framepose[frame][i].scale.x += framedata[dcounter]*poses[i].channelscale[7]; - dcounter++; - } - - animation.framepose[frame][i].scale.y = poses[i].channeloffset[8]; - - if (poses[i].mask & 0x100) - { - animation.framepose[frame][i].scale.y += framedata[dcounter]*poses[i].channelscale[8]; - dcounter++; - } - - animation.framepose[frame][i].scale.z = poses[i].channeloffset[9]; - - if (poses[i].mask & 0x200) - { - animation.framepose[frame][i].scale.z += framedata[dcounter]*poses[i].channelscale[9]; - dcounter++; - } - - animation.framepose[frame][i].rotation = QuaternionNormalize(animation.framepose[frame][i].rotation); - } - } - - // Build frameposes - for (int frame = 0; frame < anim.num_frames; frame++) - { - for (int i = 0; i < animation.jointCount; i++) - { - if (animation.joints[i].parent >= 0) - { - animation.framepose[frame][i].rotation = QuaternionMultiply(animation.framepose[frame][animation.joints[i].parent].rotation, animation.framepose[frame][i].rotation); - animation.framepose[frame][i].translation = Vector3RotateByQuaternion(animation.framepose[frame][i].translation, animation.framepose[frame][animation.joints[i].parent].rotation); - animation.framepose[frame][i].translation = Vector3Add(animation.framepose[frame][i].translation, animation.framepose[frame][animation.joints[i].parent].translation); - animation.framepose[frame][i].scale = Vector3MultiplyV(animation.framepose[frame][i].scale, animation.framepose[frame][animation.joints[i].parent].scale); - } - } - } - - free(framedata); - free(poses); - - fclose(iqmFile); - - return animation; -} - -// Unload animated model -void UnloadAnimatedModel(AnimatedModel model) -{ - free(model.materials); - free(model.meshMaterialId); - free(model.joints); - free(model.basepose); - - for (int i = 0; i < model.meshCount; i++) rlUnloadMesh(&model.meshes[i]); - - free(model.meshes); -} - -// Unload animation -void UnloadAnimation(Animation anim) -{ - free(anim.joints); - free(anim.framepose); - - for (int i = 0; i < anim.frameCount; i++) free(anim.framepose[i]); -} - -// Check if skeletons match, only parents and jointCount are checked -bool CheckSkeletonsMatch(AnimatedModel model, Animation anim) -{ - if (model.jointCount != anim.jointCount) return 0; - - for (int i = 0; i < model.jointCount; i++) - { - if (model.joints[i].parent != anim.joints[i].parent) return 0; - } - - return 1; -} - -// Calculate the animated vertex positions and normals based on an animation at a given frame -void AnimateModel(AnimatedModel model, Animation anim, int frame) -{ - if (frame >= anim.frameCount) frame = frame%anim.frameCount; - - for (int m = 0; m < model.meshCount; m++) - { - Vector3 outv = {0}; - Vector3 outn = {0}; - - Vector3 baset = {0}; - Quaternion baser = {0}; - Vector3 bases = {0}; - - Vector3 outt = {0}; - Quaternion outr = {0}; - Vector3 outs = {0}; - - int vcounter = 0; - int wcounter = 0; - int weightId = 0; - - for (int i = 0; i < model.meshes[m].vertexCount; i++) - { - weightId = model.meshes[m].weightId[wcounter]; - baset = model.basepose[weightId].translation; - baser = model.basepose[weightId].rotation; - bases = model.basepose[weightId].scale; - outt = anim.framepose[frame][weightId].translation; - outr = anim.framepose[frame][weightId].rotation; - outs = anim.framepose[frame][weightId].scale; - - // vertices - // NOTE: We use meshes.baseVertices (default position) to calculate meshes.vertices (animated position) - outv = (Vector3){ model.meshes[m].baseVertices[vcounter], model.meshes[m].baseVertices[vcounter + 1], model.meshes[m].baseVertices[vcounter + 2] }; - outv = Vector3MultiplyV(outv, outs); - outv = Vector3Subtract(outv, baset); - outv = Vector3RotateByQuaternion(outv, QuaternionMultiply(outr, QuaternionInvert(baser))); - outv = Vector3Add(outv, outt); - model.meshes[m].vertices[vcounter] = outv.x; - model.meshes[m].vertices[vcounter + 1] = outv.y; - model.meshes[m].vertices[vcounter + 2] = outv.z; - - // normals - // NOTE: We use meshes.baseNormals (default normal) to calculate meshes.normals (animated normals) - outn = (Vector3){ model.meshes[m].baseNormals[vcounter], model.meshes[m].baseNormals[vcounter + 1], model.meshes[m].baseNormals[vcounter + 2] }; - outn = Vector3RotateByQuaternion(outn, QuaternionMultiply(outr, QuaternionInvert(baser))); - model.meshes[m].normals[vcounter] = outn.x; - model.meshes[m].normals[vcounter + 1] = outn.y; - model.meshes[m].normals[vcounter + 2] = outn.z; - vcounter += 3; - wcounter += 4; - } - } -} - -// Draw an animated model -void DrawAnimatedModel(AnimatedModel model, Vector3 position, float scale, Color tint) -{ - Vector3 vScale = { scale, scale, scale }; - Vector3 rotationAxis = { 1.0f, 0.0f,0.0f }; - - DrawAnimatedModelEx(model, position, rotationAxis, -90.0f, vScale, tint); -} - -// Draw an animated model with extended parameters -void DrawAnimatedModelEx(AnimatedModel model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint) -{ - if (model.materialCount == 0) - { - TraceLog(LOG_WARNING,"No materials set, can't draw animated meshes\n"); - return; - } - - Matrix matScale = MatrixScale(scale.x, scale.y, scale.z); - Matrix matRotation = MatrixRotate(rotationAxis, rotationAngle*DEG2RAD); - Matrix matTranslation = MatrixTranslate(position.x, position.y, position.z); - - Matrix matTransform = MatrixMultiply(MatrixMultiply(matScale, matRotation), matTranslation); - model.transform = MatrixMultiply(model.transform, matTransform); - - for (int i = 0; i < model.meshCount; i++) - { - rlUpdateMesh(model.meshes[i], 0, model.meshes[i].vertexCount); // Update vertex position - rlUpdateMesh(model.meshes[i], 2, model.meshes[i].vertexCount); // Update vertex normals - rlDrawMesh(model.meshes[i], model.materials[model.meshMaterialId[i]], model.transform); // Draw meshes - } -} - -// Load animated model meshes from IQM file -static AnimatedModel LoadIQM(const char *filename) -{ - AnimatedModel model = { 0 }; - - FILE *iqmFile; - IQMHeader iqm; - - IQMMesh *imesh; - IQMTriangle *tri; - IQMVertexArray *va; - IQMJoint *ijoint; - - float *vertex; - float *normal; - float *text; - char *blendi; - unsigned char *blendw; - - iqmFile = fopen(filename, "rb"); - - if (!iqmFile) - { - TraceLog(LOG_ERROR, "[%s] Unable to open file", filename); - return model; - } - - // header - fread(&iqm,sizeof(IQMHeader), 1, iqmFile); - - if (strncmp(iqm.magic, IQM_MAGIC, sizeof(IQM_MAGIC))) - { - TraceLog(LOG_ERROR, "Magic Number \"%s\"does not match.", iqm.magic); - fclose(iqmFile); - return model; - } - - if(iqm.version != IQM_VERSION) - { - TraceLog(LOG_ERROR, "IQM version %i is incorrect.", iqm.version); - fclose(iqmFile); - return model; - } - - // meshes - imesh = malloc(sizeof(IQMMesh)*iqm.num_meshes); - fseek(iqmFile, iqm.ofs_meshes, SEEK_SET); - fread(imesh, sizeof(IQMMesh)*iqm.num_meshes, 1, iqmFile); - - model.meshCount = iqm.num_meshes; - model.meshes = malloc(sizeof(Mesh)*iqm.num_meshes); - - char name[MESH_NAME_LENGTH]; - - for (int i = 0; i < iqm.num_meshes; i++) - { - fseek(iqmFile,iqm.ofs_text+imesh[i].name,SEEK_SET); - fread(name, sizeof(char)*MESH_NAME_LENGTH, 1, iqmFile); // Mesh name not used... - model.meshes[i].vertexCount = imesh[i].num_vertexes; - - model.meshes[i].baseVertices = malloc(sizeof(float)*imesh[i].num_vertexes*3); // Default IQM base position - model.meshes[i].baseNormals = malloc(sizeof(float)*imesh[i].num_vertexes*3); // Default IQM base normal - - model.meshes[i].texcoords = malloc(sizeof(float)*imesh[i].num_vertexes*2); - model.meshes[i].weightId = malloc(sizeof(int)*imesh[i].num_vertexes*4); - model.meshes[i].weightBias = malloc(sizeof(float)*imesh[i].num_vertexes*4); - - model.meshes[i].triangleCount = imesh[i].num_triangles; - model.meshes[i].indices = malloc(sizeof(unsigned short)*imesh[i].num_triangles*3); - - // What we actually process for rendering, should be updated transforming meshes.vertices and meshes.normals - model.meshes[i].vertices = malloc(sizeof(float)*imesh[i].num_vertexes*3); - model.meshes[i].normals = malloc(sizeof(float)*imesh[i].num_vertexes*3); - } - - // tris - tri = malloc(sizeof(IQMTriangle)*iqm.num_triangles); - fseek(iqmFile, iqm.ofs_triangles, SEEK_SET); - fread(tri, sizeof(IQMTriangle)*iqm.num_triangles, 1, iqmFile); - - for (int m = 0; m < iqm.num_meshes; m++) - { - int tcounter = 0; - - for (int i = imesh[m].first_triangle; i < imesh[m].first_triangle+imesh[m].num_triangles; i++) - { - // IQM triangles are stored counter clockwise, but raylib sets opengl to clockwise drawing, so we swap them around - model.meshes[m].indices[tcounter+2] = tri[i].vertex[0] - imesh[m].first_vertex; - model.meshes[m].indices[tcounter+1] = tri[i].vertex[1] - imesh[m].first_vertex; - model.meshes[m].indices[tcounter] = tri[i].vertex[2] - imesh[m].first_vertex; - tcounter += 3; - } - } - - // vertarrays - va = malloc(sizeof(IQMVertexArray)*iqm.num_vertexarrays); - fseek(iqmFile, iqm.ofs_vertexarrays, SEEK_SET); - fread(va, sizeof(IQMVertexArray)*iqm.num_vertexarrays, 1, iqmFile); - - for (int i = 0; i < iqm.num_vertexarrays; i++) - { - switch (va[i].type) - { - case IQM_POSITION: - { - vertex = malloc(sizeof(float)*iqm.num_vertexes*3); - fseek(iqmFile, va[i].offset, SEEK_SET); - fread(vertex, sizeof(float)*iqm.num_vertexes*3, 1, iqmFile); - - for (int m = 0; m < iqm.num_meshes; m++) - { - int vcounter = 0; - for (int i = imesh[m].first_vertex*3; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*3; i++) - { - model.meshes[m].vertices[vcounter] = vertex[i]; - model.meshes[m].baseVertices[vcounter] = vertex[i]; - vcounter++; - } - } - } break; - case IQM_NORMAL: - { - normal = malloc(sizeof(float)*iqm.num_vertexes*3); - fseek(iqmFile, va[i].offset, SEEK_SET); - fread(normal, sizeof(float)*iqm.num_vertexes*3, 1, iqmFile); - - for (int m = 0; m < iqm.num_meshes; m++) - { - int vcounter = 0; - for (int i = imesh[m].first_vertex*3; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*3; i++) - { - model.meshes[m].normals[vcounter] = normal[i]; - model.meshes[m].baseNormals[vcounter] = normal[i]; - vcounter++; - } - } - } break; - case IQM_TEXCOORD: - { - text = malloc(sizeof(float)*iqm.num_vertexes*2); - fseek(iqmFile, va[i].offset, SEEK_SET); - fread(text, sizeof(float)*iqm.num_vertexes*2, 1, iqmFile); - - for (int m = 0; m < iqm.num_meshes; m++) - { - int vcounter = 0; - for (int i = imesh[m].first_vertex*2; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*2; i++) - { - model.meshes[m].texcoords[vcounter] = text[i]; - vcounter++; - } - } - } break; - case IQM_BLENDINDEXES: - { - blendi = malloc(sizeof(char)*iqm.num_vertexes*4); - fseek(iqmFile, va[i].offset, SEEK_SET); - fread(blendi, sizeof(char)*iqm.num_vertexes*4, 1, iqmFile); - - for (int m = 0; m < iqm.num_meshes; m++) - { - int vcounter = 0; - for (int i = imesh[m].first_vertex*4; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*4; i++) - { - model.meshes[m].weightId[vcounter] = blendi[i]; - vcounter++; - } - } - } break; - case IQM_BLENDWEIGHTS: - { - blendw = malloc(sizeof(unsigned char)*iqm.num_vertexes*4); - fseek(iqmFile,va[i].offset,SEEK_SET); - fread(blendw,sizeof(unsigned char)*iqm.num_vertexes*4,1,iqmFile); - - for (int m = 0; m < iqm.num_meshes; m++) - { - int vcounter = 0; - for (int i = imesh[m].first_vertex*4; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*4; i++) - { - model.meshes[m].weightBias[vcounter] = blendw[i]/255.0f; - vcounter++; - } - } - } break; - } - } - - // joints, include base poses - ijoint = malloc(sizeof(IQMJoint)*iqm.num_joints); - fseek(iqmFile, iqm.ofs_joints, SEEK_SET); - fread(ijoint, sizeof(IQMJoint)*iqm.num_joints, 1, iqmFile); - - model.jointCount = iqm.num_joints; - model.joints = malloc(sizeof(Joint)*iqm.num_joints); - model.basepose = malloc(sizeof(Pose)*iqm.num_joints); - - for (int i = 0; i < iqm.num_joints; i++) - { - // joints - model.joints[i].parent = ijoint[i].parent; - fseek(iqmFile, iqm.ofs_text + ijoint[i].name, SEEK_SET); - fread(model.joints[i].name,sizeof(char)*JOINT_NAME_LENGTH, 1, iqmFile); - - // basepose - model.basepose[i].translation.x = ijoint[i].translate[0]; - model.basepose[i].translation.y = ijoint[i].translate[1]; - model.basepose[i].translation.z = ijoint[i].translate[2]; - - model.basepose[i].rotation.x = ijoint[i].rotate[0]; - model.basepose[i].rotation.y = ijoint[i].rotate[1]; - model.basepose[i].rotation.z = ijoint[i].rotate[2]; - model.basepose[i].rotation.w = ijoint[i].rotate[3]; - - model.basepose[i].scale.x = ijoint[i].scale[0]; - model.basepose[i].scale.y = ijoint[i].scale[1]; - model.basepose[i].scale.z = ijoint[i].scale[2]; - } - - // build base pose - for (int i = 0; i < model.jointCount; i++) - { - if (model.joints[i].parent >= 0) - { - model.basepose[i].rotation = QuaternionMultiply(model.basepose[model.joints[i].parent].rotation, model.basepose[i].rotation); - model.basepose[i].translation = Vector3RotateByQuaternion(model.basepose[i].translation, model.basepose[model.joints[i].parent].rotation); - model.basepose[i].translation = Vector3Add(model.basepose[i].translation, model.basepose[model.joints[i].parent].translation); - model.basepose[i].scale = Vector3MultiplyV(model.basepose[i].scale, model.basepose[model.joints[i].parent].scale); - } - } - - fclose(iqmFile); - free(imesh); - free(tri); - free(va); - free(vertex); - free(normal); - free(text); - free(blendi); - free(blendw); - free(ijoint); - - return model; -} - -#endif diff --git a/examples/others/rlgl_standalone.c b/examples/others/rlgl_standalone.c index 4b262bbd2..42aec2e2f 100644 --- a/examples/others/rlgl_standalone.c +++ b/examples/others/rlgl_standalone.c @@ -24,7 +24,7 @@ * This example is licensed under an unmodified zlib/libpng license, which is an OSI-certified, * BSD-like license that allows static linking with closed source software: * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * * 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. diff --git a/examples/others/standard_lighting.c b/examples/others/standard_lighting.c index 7034aa35b..c37ae1512 100644 --- a/examples/others/standard_lighting.c +++ b/examples/others/standard_lighting.c @@ -113,7 +113,7 @@ int main() material.maps[MAP_DIFFUSE].color = WHITE; material.maps[MAP_SPECULAR].color = WHITE; - model.material = material; // Apply material to model + model.materials[0] = material; // Apply material to model Light spotLight = CreateLight(LIGHT_SPOT, (Vector3){3.0f, 5.0f, 2.0f}, (Color){255, 255, 255, 255}); spotLight->target = (Vector3){0.0f, 0.0f, 0.0f}; diff --git a/examples/shaders/resources/shaders/glsl330/cubes_panning.fs b/examples/shaders/resources/shaders/glsl330/cubes_panning.fs new file mode 100644 index 000000000..c92418a48 --- /dev/null +++ b/examples/shaders/resources/shaders/glsl330/cubes_panning.fs @@ -0,0 +1,61 @@ +#version 330 + +// Input vertex attributes (from vertex shader) +in vec2 fragTexCoord; +in vec4 fragColor; + +// Output fragment color +out vec4 finalColor; + +// Custom variables +#define PI 3.14159265358979323846 +uniform float uTime = 0.0; + +float divisions = 5.0; +float angle = 0.0; + +vec2 VectorRotateTime(vec2 v, float speed) +{ + float time = uTime*speed; + float localTime = fract(time); // The time domain this works on is 1 sec. + + if ((localTime >= 0.0) && (localTime < 0.25)) angle = 0.0; + else if ((localTime >= 0.25) && (localTime < 0.50)) angle = PI/4*sin(2*PI*localTime - PI/2); + else if ((localTime >= 0.50) && (localTime < 0.75)) angle = PI*0.25; + else if ((localTime >= 0.75) && (localTime < 1.00)) angle = PI/4*sin(2*PI*localTime); + + // Rotate vector by angle + v -= 0.5; + v = mat2(cos(angle), -sin(angle), sin(angle), cos(angle))*v; + v += 0.5; + + return v; +} + +float Rectangle(in vec2 st, in float size, in float fill) +{ + float roundSize = 0.5 - size/2.0; + float left = step(roundSize, st.x); + float top = step(roundSize, st.y); + float bottom = step(roundSize, 1.0 - st.y); + float right = step(roundSize, 1.0 - st.x); + + return (left*bottom*right*top)*fill; +} + +void main() +{ + vec2 fragPos = fragTexCoord; + fragPos.xy += uTime/9.0; + + fragPos *= divisions; + vec2 ipos = floor(fragPos); // Get the integer coords + vec2 fpos = fract(fragPos); // Get the fractional coords + + fpos = VectorRotateTime(fpos, 0.2); + + float alpha = Rectangle(fpos, 0.216, 1.0); + vec3 color = vec3(0.3, 0.3, 0.3); + + finalColor = vec4(color, alpha); +} \ No newline at end of file diff --git a/examples/shaders/shaders_custom_uniform.c b/examples/shaders/shaders_custom_uniform.c index fbfd82d07..74b9e7714 100644 --- a/examples/shaders/shaders_custom_uniform.c +++ b/examples/shaders/shaders_custom_uniform.c @@ -45,7 +45,7 @@ int main() Model model = LoadModel("resources/models/barracks.obj"); // Load OBJ model Texture2D texture = LoadTexture("resources/models/barracks_diffuse.png"); // Load model texture (diffuse map) - model.material.maps[MAP_DIFFUSE].texture = texture; // Set model diffuse texture + model.materials[0].maps[MAP_DIFFUSE].texture = texture; // Set model diffuse texture Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position diff --git a/examples/shaders/shaders_model_shader.c b/examples/shaders/shaders_model_shader.c index 6c64f0efd..2717c1927 100644 --- a/examples/shaders/shaders_model_shader.c +++ b/examples/shaders/shaders_model_shader.c @@ -50,8 +50,8 @@ int main() // NOTE: Defining 0 (NULL) for vertex shader forces usage of internal default vertex shader Shader shader = LoadShader(0, FormatText("resources/shaders/glsl%i/grayscale.fs", GLSL_VERSION)); - model.material.shader = shader; // Set shader effect to 3d model - model.material.maps[MAP_DIFFUSE].texture = texture; // Bind texture to model + model.materials[0].shader = shader; // Set shader effect to 3d model + model.materials[0].maps[MAP_DIFFUSE].texture = texture; // Bind texture to model Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position diff --git a/examples/shaders/shaders_postprocessing.c b/examples/shaders/shaders_postprocessing.c index f84835636..7c1464193 100644 --- a/examples/shaders/shaders_postprocessing.c +++ b/examples/shaders/shaders_postprocessing.c @@ -74,7 +74,7 @@ int main() Model model = LoadModel("resources/models/church.obj"); // Load OBJ model Texture2D texture = LoadTexture("resources/models/church_diffuse.png"); // Load model texture (diffuse map) - model.material.maps[MAP_DIFFUSE].texture = texture; // Set model diffuse texture + model.materials[0].maps[MAP_DIFFUSE].texture = texture; // Set model diffuse texture Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position diff --git a/examples/shaders/shaders_texture_drawing.c b/examples/shaders/shaders_texture_drawing.c new file mode 100644 index 000000000..cb8a9c1eb --- /dev/null +++ b/examples/shaders/shaders_texture_drawing.c @@ -0,0 +1,71 @@ +/******************************************************************************************* +* +* raylib [textures] example - Shader texture drawing +* +* This example illustrates how to draw on a blank texture using a shader +* +* This example has been created using raylib 2.0 (www.raylib.com) +* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* +* Copyright (c) 2019 Michał Ciesielski and Ramon Santamaria (@raysan5) +* +********************************************************************************************/ + +#include "raylib.h" + +int main() +{ + // Initialization + //-------------------------------------------------------------------------------------- + int screenWidth = 800; + int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [shaders] example - shader texture drawing"); + + Image imBlank = GenImageColor(1024, 1024, BLANK); + Texture2D texture = LoadTextureFromImage(imBlank); // Load blank texture to fill on shader + UnloadImage(imBlank); + + // NOTE: Using GLSL 330 shader version, on OpenGL ES 2.0 use GLSL 100 shader version + Shader shader = LoadShader(0, "resources/shaders/glsl330/cubes_panning.fs"); + + float time = 0.0f; + int timeLoc = GetShaderLocation(shader, "uTime"); + SetShaderValue(shader, timeLoc, &time, UNIFORM_FLOAT); + + SetTargetFPS(60); + //-------------------------------------------------------------------------------------- + + while (!WindowShouldClose()) + { + // Update + //---------------------------------------------------------------------------------- + time = GetTime(); + SetShaderValue(shader, timeLoc, &time, UNIFORM_FLOAT); + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + BeginShaderMode(shader); // Enable our custom shader for next shapes/textures drawings + DrawTexture(texture, 0, 0, WHITE); // Drawing BLANK texture, all magic happens on shader + EndShaderMode(); // Disable our custom shader, return to default shader + + DrawText("BACKGROUND is PAINTED and ANIMATED on SHADER!", 10, 10, 20, MAROON); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + UnloadShader(shader); + + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} diff --git a/examples/shaders/shaders_texture_drawing.png b/examples/shaders/shaders_texture_drawing.png new file mode 100644 index 000000000..12df6fae3 Binary files /dev/null and b/examples/shaders/shaders_texture_drawing.png differ diff --git a/examples/shapes/shapes_rectangle_scaling_mouse.c b/examples/shapes/shapes_rectangle_scaling_mouse.c new file mode 100644 index 000000000..d8c521cab --- /dev/null +++ b/examples/shapes/shapes_rectangle_scaling_mouse.c @@ -0,0 +1,92 @@ +/******************************************************************************************* +* +* raylib [shapes] example - rectangle scaling by mouse +* +* This example has been created using raylib 2.5 (www.raylib.com) +* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* +* Copyright (c) 2019 Demioz and Ramon Santamaria (@raysan5) +* +********************************************************************************************/ + +#include "raylib.h" + +#define MOUSE_SCALE_MARK_SIZE 12 + +int main() +{ + // Initialization + //-------------------------------------------------------------------------------------- + int screenWidth = 800; + int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [shapes] example - rectangle scaling mouse"); + + Rectangle rec = { 100, 100, 200, 80 }; + + Vector2 mousePosition = { 0 }; + + bool mouseScaleReady = false; + bool mouseScaleMode = false; + + SetTargetFPS(60); + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + mousePosition = GetMousePosition(); + + if (CheckCollisionPointRec(mousePosition, rec) && + CheckCollisionPointRec(mousePosition, (Rectangle){ rec.x + rec.width - MOUSE_SCALE_MARK_SIZE, rec.y + rec.height - MOUSE_SCALE_MARK_SIZE, MOUSE_SCALE_MARK_SIZE, MOUSE_SCALE_MARK_SIZE })) + { + mouseScaleReady = true; + if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) mouseScaleMode = true; + } + else mouseScaleReady = false; + + if (mouseScaleMode) + { + mouseScaleReady = true; + + rec.width = (mousePosition.x - rec.x); + rec.height = (mousePosition.y - rec.y); + + if (rec.width < MOUSE_SCALE_MARK_SIZE) rec.width = MOUSE_SCALE_MARK_SIZE; + if (rec.height < MOUSE_SCALE_MARK_SIZE) rec.height = MOUSE_SCALE_MARK_SIZE; + + if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) mouseScaleMode = false; + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + DrawText("Scale rectangle dragging from bottom-right corner!", 10, 10, 20, GRAY); + + DrawRectangleRec(rec, Fade(GREEN, 0.5f)); + + if (mouseScaleReady) + { + DrawRectangleLinesEx(rec, 1, RED); + DrawTriangle((Vector2){ rec.x + rec.width - MOUSE_SCALE_MARK_SIZE, rec.y + rec.height }, + (Vector2){ rec.x + rec.width, rec.y + rec.height }, + (Vector2){ rec.x + rec.width, rec.y + rec.height - MOUSE_SCALE_MARK_SIZE }, RED); + } + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} \ No newline at end of file diff --git a/examples/shapes/shapes_rectangle_scaling_mouse.png b/examples/shapes/shapes_rectangle_scaling_mouse.png new file mode 100644 index 000000000..83d67de9b Binary files /dev/null and b/examples/shapes/shapes_rectangle_scaling_mouse.png differ diff --git a/examples/textures/resources/cyberpunk_street_background.png b/examples/textures/resources/cyberpunk_street_background.png new file mode 100644 index 000000000..624a4b14b Binary files /dev/null and b/examples/textures/resources/cyberpunk_street_background.png differ diff --git a/examples/textures/resources/cyberpunk_street_foreground.png b/examples/textures/resources/cyberpunk_street_foreground.png new file mode 100644 index 000000000..fb622db68 Binary files /dev/null and b/examples/textures/resources/cyberpunk_street_foreground.png differ diff --git a/examples/textures/resources/cyberpunk_street_midground.png b/examples/textures/resources/cyberpunk_street_midground.png new file mode 100644 index 000000000..643a85e76 Binary files /dev/null and b/examples/textures/resources/cyberpunk_street_midground.png differ diff --git a/examples/textures/textures_background_scrolling.c b/examples/textures/textures_background_scrolling.c new file mode 100644 index 000000000..2be0810a4 --- /dev/null +++ b/examples/textures/textures_background_scrolling.c @@ -0,0 +1,87 @@ +/******************************************************************************************* +* +* raylib [textures] example - Background scrolling +* +* This example has been created using raylib 2.0 (www.raylib.com) +* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* +* Copyright (c) 2019 Ramon Santamaria (@raysan5) +* +********************************************************************************************/ + +#include "raylib.h" + +int main() +{ + // Initialization + //-------------------------------------------------------------------------------------- + int screenWidth = 800; + int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [textures] example - background scrolling"); + + // NOTE: Be careful, background width must be equal or bigger than screen width + // if not, texture should be draw more than two times for scrolling effect + Texture2D background = LoadTexture("resources/cyberpunk_street_background.png"); + Texture2D midground = LoadTexture("resources/cyberpunk_street_midground.png"); + Texture2D foreground = LoadTexture("resources/cyberpunk_street_foreground.png"); + + float scrollingBack = 0; + float scrollingMid = 0; + float scrollingFore = 0; + + SetTargetFPS(60); + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + scrollingBack -= 0.1f; + scrollingMid -= 0.5f; + scrollingFore -= 1.0f; + + // NOTE: Texture is scaled twice its size, so it sould be considered on scrolling + if (scrollingBack <= -background.width*2) scrollingBack = 0; + if (scrollingMid <= -midground.width*2) scrollingMid = 0; + if (scrollingFore <= -foreground.width*2) scrollingFore = 0; + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(GetColor(0x052c46ff)); + + // Draw background image twice + // NOTE: Texture is scaled twice its size + DrawTextureEx(background, (Vector2){ scrollingBack, 20 }, 0.0f, 2.0f, WHITE); + DrawTextureEx(background, (Vector2){ background.width*2 + scrollingBack, 20 }, 0.0f, 2.0f, WHITE); + + // Draw midground image twice + DrawTextureEx(midground, (Vector2){ scrollingMid, 20 }, 0.0f, 2.0f, WHITE); + DrawTextureEx(midground, (Vector2){ midground.width*2 + scrollingMid, 20 }, 0.0f, 2.0f, WHITE); + + // Draw foreground image twice + DrawTextureEx(foreground, (Vector2){ scrollingFore, 70 }, 0.0f, 2.0f, WHITE); + DrawTextureEx(foreground, (Vector2){ foreground.width*2 + scrollingFore, 70 }, 0.0f, 2.0f, WHITE); + + DrawText("BACKGROUND SCROLLING & PARALLAX", 10, 10, 20, RED); + DrawText("(c) Cyberpunk Street Environment by Luis Zuno (@ansimuz)", screenWidth - 330, screenHeight - 20, 10, RAYWHITE); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + UnloadTexture(background); // Unload background texture + UnloadTexture(midground); // Unload midground texture + UnloadTexture(foreground); // Unload foreground texture + + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} \ No newline at end of file diff --git a/examples/textures/textures_background_scrolling.png b/examples/textures/textures_background_scrolling.png new file mode 100644 index 000000000..d21e269d9 Binary files /dev/null and b/examples/textures/textures_background_scrolling.png differ diff --git a/examples/textures/textures_srcrec_dstrec.c b/examples/textures/textures_srcrec_dstrec.c index cc08eb58c..298a0c65e 100644 --- a/examples/textures/textures_srcrec_dstrec.c +++ b/examples/textures/textures_srcrec_dstrec.c @@ -27,13 +27,13 @@ int main() int frameHeight = scarfy.height; // NOTE: Source rectangle (part of the texture to use for drawing) - Rectangle sourceRec = { 0.0f, 0.0f, (float)frameWidth, (float)frameHeight }; + Rectangle sourceRec = { 0.0f, 0.0f, frameWidth, frameHeight }; // NOTE: Destination rectangle (screen rectangle where drawing part of texture) - Rectangle destRec = { (float)screenWidth/2, (float)screenHeight/2, (float)frameWidth*2, (float)frameHeight*2 }; + Rectangle destRec = { screenWidth/2, screenHeight/2, frameWidth*2, frameHeight*2 }; // NOTE: Origin of the texture (rotation/scale point), it's relative to destination rectangle size - Vector2 origin = { (float)frameWidth, (float)frameHeight }; + Vector2 origin = { frameWidth, frameHeight }; int rotation = 0; @@ -61,7 +61,7 @@ int main() // rotation defines the texture rotation (using origin as rotation point) DrawTexturePro(scarfy, sourceRec, destRec, origin, (float)rotation, WHITE); - DrawLine((int) destRec.x, 0, (int) destRec.x, screenHeight, GRAY); + DrawLine((int)destRec.x, 0, (int)destRec.x, screenHeight, GRAY); DrawLine(0, (int)destRec.y, screenWidth, (int)destRec.y, GRAY); DrawText("(c) Scarfy sprite by Eiden Marsal", screenWidth - 200, screenHeight - 20, 10, GRAY); diff --git a/games/Makefile b/games/Makefile index 5d60f15d7..44e053c6b 100644 --- a/games/Makefile +++ b/games/Makefile @@ -2,7 +2,7 @@ # # raylib makefile for Desktop platforms, Raspberry Pi, Android and HTML5 # -# Copyright (c) 2013-2018 Ramon Santamaria (@raysan5) +# Copyright (c) 2013-2019 Ramon Santamaria (@raysan5) # # 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. diff --git a/games/cat_vs_roomba/Makefile b/games/cat_vs_roomba/Makefile index 6662a6be4..b13046569 100644 --- a/games/cat_vs_roomba/Makefile +++ b/games/cat_vs_roomba/Makefile @@ -2,7 +2,7 @@ # # raylib makefile for Desktop platforms, Raspberry Pi, Android and HTML5 # -# Copyright (c) 2013-2018 Ramon Santamaria (@raysan5) +# Copyright (c) 2013-2019 Ramon Santamaria (@raysan5) # # 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. diff --git a/games/cat_vs_roomba/roomba.c b/games/cat_vs_roomba/roomba.c index 0d2367758..eeee7a719 100644 --- a/games/cat_vs_roomba/roomba.c +++ b/games/cat_vs_roomba/roomba.c @@ -8,7 +8,7 @@ * This game has been created using raylib (www.raylib.com) * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/games/cat_vs_roomba/screens/screen_ending.c b/games/cat_vs_roomba/screens/screen_ending.c index ef2a5f749..466d9b916 100644 --- a/games/cat_vs_roomba/screens/screen_ending.c +++ b/games/cat_vs_roomba/screens/screen_ending.c @@ -4,7 +4,7 @@ * * Ending Screen Functions Definitions (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * * 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. diff --git a/games/cat_vs_roomba/screens/screen_gameplay.c b/games/cat_vs_roomba/screens/screen_gameplay.c index 49a0bb6bf..4dd13856f 100644 --- a/games/cat_vs_roomba/screens/screen_gameplay.c +++ b/games/cat_vs_roomba/screens/screen_gameplay.c @@ -4,7 +4,7 @@ * * Gameplay Screen Functions Definitions (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * * 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. diff --git a/games/cat_vs_roomba/screens/screen_logo.c b/games/cat_vs_roomba/screens/screen_logo.c index 9fc704c76..a697013ef 100644 --- a/games/cat_vs_roomba/screens/screen_logo.c +++ b/games/cat_vs_roomba/screens/screen_logo.c @@ -4,7 +4,7 @@ * * Logo Screen Functions Definitions (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * * 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. diff --git a/games/cat_vs_roomba/screens/screen_title.c b/games/cat_vs_roomba/screens/screen_title.c index 009fbd0a0..6acadce5a 100644 --- a/games/cat_vs_roomba/screens/screen_title.c +++ b/games/cat_vs_roomba/screens/screen_title.c @@ -4,7 +4,7 @@ * * Title Screen Functions Definitions (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * * 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. diff --git a/games/cat_vs_roomba/screens/screens.h b/games/cat_vs_roomba/screens/screens.h index 9cc07eab6..0ad4f9af8 100644 --- a/games/cat_vs_roomba/screens/screens.h +++ b/games/cat_vs_roomba/screens/screens.h @@ -4,7 +4,7 @@ * * Screens Functions Declarations (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * * 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. diff --git a/games/drturtle/Makefile b/games/drturtle/Makefile index 8f1934b5d..4cd5033ea 100644 --- a/games/drturtle/Makefile +++ b/games/drturtle/Makefile @@ -2,7 +2,7 @@ # # raylib makefile for Desktop platforms, Raspberry Pi, Android and HTML5 # -# Copyright (c) 2013-2018 Ramon Santamaria (@raysan5) +# Copyright (c) 2013-2019 Ramon Santamaria (@raysan5) # # 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. diff --git a/games/just_do/Makefile b/games/just_do/Makefile index b6c935f66..af9b31c12 100644 --- a/games/just_do/Makefile +++ b/games/just_do/Makefile @@ -2,7 +2,7 @@ # # raylib makefile for Desktop platforms, Raspberry Pi, Android and HTML5 # -# Copyright (c) 2013-2018 Ramon Santamaria (@raysan5) +# Copyright (c) 2013-2019 Ramon Santamaria (@raysan5) # # 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. diff --git a/games/koala_seasons/Makefile b/games/koala_seasons/Makefile index 8482bf2d3..25dbe6964 100644 --- a/games/koala_seasons/Makefile +++ b/games/koala_seasons/Makefile @@ -2,7 +2,7 @@ # # raylib makefile for Desktop platforms, Raspberry Pi, Android and HTML5 # -# Copyright (c) 2013-2018 Ramon Santamaria (@raysan5) +# Copyright (c) 2013-2019 Ramon Santamaria (@raysan5) # # 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. diff --git a/games/light_my_ritual/Makefile b/games/light_my_ritual/Makefile index fbc34aac3..3291bb3e7 100644 --- a/games/light_my_ritual/Makefile +++ b/games/light_my_ritual/Makefile @@ -2,7 +2,7 @@ # # raylib makefile for Desktop platforms, Raspberry Pi, Android and HTML5 # -# Copyright (c) 2013-2018 Ramon Santamaria (@raysan5) +# Copyright (c) 2013-2019 Ramon Santamaria (@raysan5) # # 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. diff --git a/games/skully_escape/Makefile b/games/skully_escape/Makefile index 8875ad527..4673a549e 100644 --- a/games/skully_escape/Makefile +++ b/games/skully_escape/Makefile @@ -2,7 +2,7 @@ # # raylib makefile for Desktop platforms, Raspberry Pi, Android and HTML5 # -# Copyright (c) 2013-2018 Ramon Santamaria (@raysan5) +# Copyright (c) 2013-2019 Ramon Santamaria (@raysan5) # # 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. diff --git a/games/transmission/Makefile b/games/transmission/Makefile index a1b7e764e..f62fe0804 100644 --- a/games/transmission/Makefile +++ b/games/transmission/Makefile @@ -2,7 +2,7 @@ # # raylib makefile for Desktop platforms, Raspberry Pi, Android and HTML5 # -# Copyright (c) 2013-2018 Ramon Santamaria (@raysan5) +# Copyright (c) 2013-2019 Ramon Santamaria (@raysan5) # # 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. diff --git a/games/transmission/screens/screen_ending.c b/games/transmission/screens/screen_ending.c index bb355b8b0..0492a0ccc 100644 --- a/games/transmission/screens/screen_ending.c +++ b/games/transmission/screens/screen_ending.c @@ -4,7 +4,7 @@ * * Ending Screen Functions Definitions (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * * 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. diff --git a/games/transmission/screens/screen_gameplay.c b/games/transmission/screens/screen_gameplay.c index ee70632a5..3dfce7145 100644 --- a/games/transmission/screens/screen_gameplay.c +++ b/games/transmission/screens/screen_gameplay.c @@ -4,7 +4,7 @@ * * Gameplay Screen Functions Definitions (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * * 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. diff --git a/games/transmission/screens/screen_logo.c b/games/transmission/screens/screen_logo.c index 375433028..dc016423e 100644 --- a/games/transmission/screens/screen_logo.c +++ b/games/transmission/screens/screen_logo.c @@ -4,7 +4,7 @@ * * Logo Screen Functions Definitions (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * * 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. diff --git a/games/transmission/screens/screen_mission.c b/games/transmission/screens/screen_mission.c index 1cd2563b8..77777c73e 100644 --- a/games/transmission/screens/screen_mission.c +++ b/games/transmission/screens/screen_mission.c @@ -3,7 +3,7 @@ * raylib - transmission mission * * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * * 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. diff --git a/games/transmission/screens/screen_title.c b/games/transmission/screens/screen_title.c index 2a30a6bac..e9023b089 100644 --- a/games/transmission/screens/screen_title.c +++ b/games/transmission/screens/screen_title.c @@ -3,7 +3,7 @@ * raylib - transmission mission * * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * * 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. diff --git a/games/transmission/screens/screens.h b/games/transmission/screens/screens.h index be5e31d92..49698f0d9 100644 --- a/games/transmission/screens/screens.h +++ b/games/transmission/screens/screens.h @@ -4,7 +4,7 @@ * * Screens Functions Declarations (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * * 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. diff --git a/games/transmission/transmission.c b/games/transmission/transmission.c index 91ca28c1d..9fc3d802b 100644 --- a/games/transmission/transmission.c +++ b/games/transmission/transmission.c @@ -7,7 +7,7 @@ * This game has been created using raylib (www.raylib.com) * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/games/wave_collector/Makefile b/games/wave_collector/Makefile index 06e74799f..703383b89 100644 --- a/games/wave_collector/Makefile +++ b/games/wave_collector/Makefile @@ -2,7 +2,7 @@ # # raylib makefile for Desktop platforms, Raspberry Pi, Android and HTML5 # -# Copyright (c) 2013-2018 Ramon Santamaria (@raysan5) +# Copyright (c) 2013-2019 Ramon Santamaria (@raysan5) # # 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. diff --git a/projects/VS2017/examples/core_basic_window.vcxproj b/projects/VS2017/examples/core_basic_window.vcxproj index 83e790db0..87bef8d82 100644 --- a/projects/VS2017/examples/core_basic_window.vcxproj +++ b/projects/VS2017/examples/core_basic_window.vcxproj @@ -1,182 +1,182 @@ - - - - - Debug.DLL - Win32 - - - Debug - Win32 - - - Release.DLL - Win32 - - - Release - Win32 - - - - {0981CA98-E4A5-4DF1-987F-A41D09131EFC} - Win32Proj - core_basic_window - 10.0 - core_basic_window - - - - Application - true - v142 - Unicode - - - Application - true - v142 - Unicode - - - Application - false - v142 - true - Unicode - - - Application - false - v142 - true - Unicode - - - - - - - - - - - - - - - - - - - - - true - $(ProjectDir)$(ProjectName)\$(Configuration)\ - $(ProjectDir)$(ProjectName)\$(Configuration)\temp\ - - - true - $(ProjectDir)$(ProjectName)\$(Configuration)\ - $(ProjectDir)$(ProjectName)\$(Configuration)\temp - - - false - $(ProjectDir)$(ProjectName)\$(Configuration)\ - $(ProjectDir)$(ProjectName)\$(Configuration)\temp - - - false - $(ProjectDir)$(ProjectName)\$(Configuration)\ - $(ProjectDir)$(ProjectName)\$(Configuration)\temp - - - - - - Level3 - Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) - CompileAsC - $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) - - - Console - true - %(AdditionalLibraryDirectories) - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) - - - - - - - Level3 - Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) - CompileAsC - $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) - - - Console - true - %(AdditionalLibraryDirectories) - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) - - - - - Level3 - - - MaxSpeed - true - true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP - $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) - CompileAsC - true - - - Console - true - true - true - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) - %(AdditionalLibraryDirectories) - - - - - Level3 - - - MaxSpeed - true - true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP - $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) - CompileAsC - true - - - Console - true - true - true - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) - %(AdditionalLibraryDirectories) - - - - - - - - {e89d61ac-55de-4482-afd4-df7242ebc859} - - - - - + + + + + Debug.DLL + Win32 + + + Debug + Win32 + + + Release.DLL + Win32 + + + Release + Win32 + + + + {0981CA98-E4A5-4DF1-987F-A41D09131EFC} + Win32Proj + core_basic_window + 10.0.17763.0 + core_basic_window + + + + Application + true + v141 + Unicode + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + Application + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + $(ProjectDir)$(ProjectName)\$(Configuration)\ + $(ProjectDir)$(ProjectName)\$(Configuration)\temp + + + true + $(ProjectDir)$(ProjectName)\$(Configuration)\ + $(ProjectDir)$(ProjectName)\$(Configuration)\temp + + + false + $(ProjectDir)$(ProjectName)\$(Configuration)\ + $(ProjectDir)$(ProjectName)\$(Configuration)\temp + + + false + $(ProjectDir)$(ProjectName)\$(Configuration)\ + $(ProjectDir)$(ProjectName)\$(Configuration)\temp + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + %(AdditionalLibraryDirectories) + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + %(AdditionalLibraryDirectories) + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + %(AdditionalLibraryDirectories) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + %(AdditionalLibraryDirectories) + + + + + + + + {e89d61ac-55de-4482-afd4-df7242ebc859} + + + + + \ No newline at end of file diff --git a/projects/VS2017/examples/core_basic_window_cpp.vcxproj b/projects/VS2017/examples/core_basic_window_cpp.vcxproj index 949253fcf..cfe085241 100644 --- a/projects/VS2017/examples/core_basic_window_cpp.vcxproj +++ b/projects/VS2017/examples/core_basic_window_cpp.vcxproj @@ -1,180 +1,180 @@ - - - - - Debug.DLL - Win32 - - - Debug - Win32 - - - Release.DLL - Win32 - - - Release - Win32 - - - - {B655E850-3322-42F7-941D-6AC18FD66CA1} - Win32Proj - raylib_example_cpp - 10.0 - core_basic_window_cpp - - - - Application - true - v142 - Unicode - - - Application - true - v142 - Unicode - - - Application - false - v142 - true - Unicode - - - Application - false - v142 - true - Unicode - - - - - - - - - - - - - - - - - - - - - true - $(ProjectDir)$(ProjectName)\$(Configuration) - $(ProjectDir)$(ProjectName)\$(Configuration)\temp - - - true - $(ProjectDir)$(ProjectName)\$(Configuration) - $(ProjectDir)$(ProjectName)\$(Configuration)\temp - - - false - $(ProjectDir)$(ProjectName)\$(Configuration) - $(ProjectDir)$(ProjectName)\$(Configuration)\temp - - - false - $(ProjectDir)$(ProjectName)\$(Configuration) - $(ProjectDir)$(ProjectName)\$(Configuration)\temp - - - - - - Level3 - Disabled - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) - CompileAsCpp - - - Console - true - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) - $(SolutionDir)..\..\release\libs\win32\msvc;%(AdditionalLibraryDirectories) - - - - - - - Level3 - Disabled - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) - CompileAsCpp - - - Console - true - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) - $(SolutionDir)..\..\release\libs\win32\msvc;%(AdditionalLibraryDirectories) - - - - - Level3 - - - MaxSpeed - true - true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - CompileAsCpp - $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) - - - Console - true - true - true - $(SolutionDir)..\..\release\libs\win32\msvc;%(AdditionalLibraryDirectories) - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) - - - - - Level3 - - - MaxSpeed - true - true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - CompileAsCpp - $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) - - - Console - true - true - true - %(AdditionalLibraryDirectories) - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) - - - - - {e89d61ac-55de-4482-afd4-df7242ebc859} - - - - - - - - + + + + + Debug.DLL + Win32 + + + Debug + Win32 + + + Release.DLL + Win32 + + + Release + Win32 + + + + {B655E850-3322-42F7-941D-6AC18FD66CA1} + Win32Proj + raylib_example_cpp + 10.0.17763.0 + core_basic_window_cpp + + + + Application + true + v141 + Unicode + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + Application + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + $(ProjectDir)$(ProjectName)\$(Configuration) + $(ProjectDir)$(ProjectName)\$(Configuration)\temp + + + true + $(ProjectDir)$(ProjectName)\$(Configuration) + $(ProjectDir)$(ProjectName)\$(Configuration)\temp + + + false + $(ProjectDir)$(ProjectName)\$(Configuration) + $(ProjectDir)$(ProjectName)\$(Configuration)\temp + + + false + $(ProjectDir)$(ProjectName)\$(Configuration) + $(ProjectDir)$(ProjectName)\$(Configuration)\temp + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsCpp + + + Console + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)..\..\release\libs\win32\msvc;%(AdditionalLibraryDirectories) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsCpp + + + Console + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)..\..\release\libs\win32\msvc;%(AdditionalLibraryDirectories) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + CompileAsCpp + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + true + true + $(SolutionDir)..\..\release\libs\win32\msvc;%(AdditionalLibraryDirectories) + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + CompileAsCpp + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + true + true + %(AdditionalLibraryDirectories) + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + {e89d61ac-55de-4482-afd4-df7242ebc859} + + + + + + + + \ No newline at end of file diff --git a/projects/VS2017/raylib/raylib.vcxproj b/projects/VS2017/raylib/raylib.vcxproj index 888eea35e..d7d4f6d00 100644 --- a/projects/VS2017/raylib/raylib.vcxproj +++ b/projects/VS2017/raylib/raylib.vcxproj @@ -22,7 +22,7 @@ {E89D61AC-55DE-4482-AFD4-DF7242EBC859} Win32Proj raylib - 10.0 + 10.0.17763.0 @@ -34,20 +34,20 @@ DynamicLibrary true - v142 + v141 Unicode StaticLibrary false - v142 + v141 true Unicode DynamicLibrary false - v142 + v141 true Unicode @@ -163,7 +163,6 @@ - @@ -174,7 +173,7 @@ - + @@ -185,12 +184,8 @@ - - - - diff --git a/projects/VSCode/Makefile b/projects/VSCode/Makefile index 747718fc4..3cffaaba1 100644 --- a/projects/VSCode/Makefile +++ b/projects/VSCode/Makefile @@ -2,7 +2,7 @@ # # raylib makefile for Desktop platforms, Raspberry Pi, Android and HTML5 # -# Copyright (c) 2013-2018 Ramon Santamaria (@raysan5) +# Copyright (c) 2013-2019 Ramon Santamaria (@raysan5) # # 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. diff --git a/src/Makefile b/src/Makefile index e2fe6c5d5..997f041c9 100644 --- a/src/Makefile +++ b/src/Makefile @@ -14,7 +14,7 @@ # Many thanks to Milan Nikolic (@gen2brain) for implementing Android platform pipeline. # Many thanks to Emanuele Petriglia for his contribution on GNU/Linux pipeline. # -# Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +# Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) # # 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 @@ -155,7 +155,7 @@ endif ifeq ($(PLATFORM),PLATFORM_WEB) # Emscripten required variables EMSDK_PATH = C:/emsdk - EMSCRIPTEN_VERSION ?= 1.38.21 + EMSCRIPTEN_VERSION ?= 1.38.30 CLANG_VERSION = e$(EMSCRIPTEN_VERSION)_64bit PYTHON_VERSION = 2.7.13.1_64bit\python-2.7.13.amd64 NODE_VERSION = 8.9.1_64bit diff --git a/src/config.h b/src/config.h index d45ff7078..2ea4b438f 100644 --- a/src/config.h +++ b/src/config.h @@ -6,7 +6,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2018 Ahmad Fatoum & Ramon Santamaria (@raysan5) +* Copyright (c) 2018-2019 Ahmad Fatoum & Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. @@ -25,7 +25,7 @@ * **********************************************************************************************/ -#define RAYLIB_VERSION "2.4-dev" +#define RAYLIB_VERSION "2.5-dev" // Edit to control what features Makefile'd raylib is compiled with #if defined(RAYLIB_CMAKE) @@ -57,8 +57,6 @@ //------------------------------------------------------------------------------------ // Support VR simulation functionality (stereo rendering) #define SUPPORT_VR_SIMULATOR 1 -// Include stereo rendering distortion shader (shader_distortion.h) -#define SUPPORT_DISTORTION_SHADER 1 //------------------------------------------------------------------------------------ @@ -66,10 +64,10 @@ //------------------------------------------------------------------------------------ // Draw rectangle shapes using font texture white character instead of default white texture // Allows drawing rectangles and text with a single draw call, very useful for GUI systems! -#define SUPPORT_FONT_TEXTURE +#define SUPPORT_FONT_TEXTURE 1 // Use QUADS instead of TRIANGLES for drawing when possible // Some lines-based shapes could still use lines -#define SUPPORT_QUADS_DRAW_MODE +#define SUPPORT_QUADS_DRAW_MODE 1 //------------------------------------------------------------------------------------ // Module: textures - Configuration Flags @@ -114,6 +112,8 @@ // 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_GLTF 1 // Support procedural mesh generation functions, uses external par_shapes.h library // NOTE: Some generated meshes DO NOT include generated texture coordinates #define SUPPORT_MESH_GENERATION 1 diff --git a/src/core.c b/src/core.c index aab60f13c..ba41c7730 100644 --- a/src/core.c +++ b/src/core.c @@ -68,7 +68,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2013-2019 Ramon Santamaria (@raysan5) * * 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. @@ -361,6 +361,15 @@ typedef struct { static InputEventWorker eventWorkers[10]; // List of worker threads for every monitored "/dev/input/event" +typedef struct{ + int Contents[8]; + char Head; + char Tail; +} KeyEventFifo; + +static KeyEventFifo lastKeyPressedEvdev; // Buffer for holding keydown events as they arrive (Needed due to multitreading of event workers) +static char currentKeyStateEvdev[512] = { 0 }; // Registers current frame key state from event based driver (Needs to be seperate because the legacy console based method clears keys on every frame) + #endif #if defined(PLATFORM_WEB) static bool toggleCursorLock = false; // Ask for cursor pointer lock on next click @@ -470,7 +479,7 @@ static EM_BOOL EmscriptenGamepadCallback(int eventType, const EmscriptenGamepadE static void InitKeyboard(void); // Init raw keyboard system (standard input reading) static void ProcessKeyboard(void); // Process keyboard events static void RestoreKeyboard(void); // Restore keyboard system -static void InitMouse(void); // Mouse initialization (including mouse thread) +static void InitEvdevInput(void); // Mouse initialization (including mouse thread) static void EventThreadSpawn(char *device); // Identifies a input device and spawns a thread to handle it if needed static void *EventThread(void *arg); // Input device events reading thread static void InitGamepad(void); // Init raw gamepad input @@ -590,7 +599,7 @@ void InitWindow(int width, int height, const char *title) #if defined(PLATFORM_RPI) // Init raw input system - InitMouse(); // Mouse init + InitEvdevInput(); // Mouse init InitKeyboard(); // Keyboard init InitGamepad(); // Gamepad init #endif @@ -751,7 +760,7 @@ bool IsWindowResized(void) return windowResized; #else return false; -#endif +#endif } // Check if window is currently hidden @@ -1155,7 +1164,7 @@ void BeginMode2D(Camera2D camera) Matrix matTransform = MatrixMultiply(MatrixMultiply(matOrigin, MatrixMultiply(matScale, matRotation)), matTranslation); - rlMultMatrixf(MatrixToFloat(matTransform)); + rlMultMatrixf(MatrixToFloat(matTransform)); // Apply transformation to modelview } // Ends 2D mode with custom camera @@ -1675,14 +1684,14 @@ const char *GetFileName(const char *filePath) const char *GetFileNameWithoutExt(const char *filePath) { #define MAX_FILENAMEWITHOUTEXT_LENGTH 64 - + static char fileName[MAX_FILENAMEWITHOUTEXT_LENGTH]; memset(fileName, 0, MAX_FILENAMEWITHOUTEXT_LENGTH); - + strcpy(fileName, GetFileName(filePath)); // Get filename with extension - + int len = strlen(fileName); - + for (int i = 0; (i < len) && (i < MAX_FILENAMEWITHOUTEXT_LENGTH); i++) { if (fileName[i] == '.') @@ -2338,7 +2347,7 @@ static bool InitGraphicsDevice(int width, int height) displayHeight = screenHeight; #endif // defined(PLATFORM_WEB) - glfwDefaultWindowHints(); // Set default windows hints: + glfwDefaultWindowHints(); // Set default windows hints: //glfwWindowHint(GLFW_RED_BITS, 8); // Framebuffer red color component bits //glfwWindowHint(GLFW_GREEN_BITS, 8); // Framebuffer green color component bits //glfwWindowHint(GLFW_BLUE_BITS, 8); // Framebuffer blue color component bits @@ -2347,6 +2356,9 @@ static bool InitGraphicsDevice(int width, int height) //glfwWindowHint(GLFW_REFRESH_RATE, 0); // Refresh rate for fullscreen window //glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API); // OpenGL API to use. Alternative: GLFW_OPENGL_ES_API //glfwWindowHint(GLFW_AUX_BUFFERS, 0); // Number of auxiliar buffers +#if defined(PLATFORM_DESKTOP) + glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE); // Scale content area based on the monitor content scale where window is placed on +#endif // Check some Window creation flags if (configFlags & FLAG_WINDOW_HIDDEN) glfwWindowHint(GLFW_VISIBLE, GL_FALSE); // Visible window @@ -3054,8 +3066,16 @@ static void PollInputEvents(void) #endif #if defined(PLATFORM_RPI) + // Register previous keys states - for (int i = 0; i < 512; i++) previousKeyState[i] = currentKeyState[i]; + for (int i = 0; i < 512; i++)previousKeyState[i] = currentKeyState[i]; + + // Grab a keypress from the evdev fifo if avalable + if(lastKeyPressedEvdev.Head != lastKeyPressedEvdev.Tail) + { + lastKeyPressed = lastKeyPressedEvdev.Contents[lastKeyPressedEvdev.Tail]; // Read the key from the buffer + lastKeyPressedEvdev.Tail = (lastKeyPressedEvdev.Tail + 1) & 0x07; // Increment the tail pointer forwards and binary wraparound after 7 (fifo is 8 elements long) + } // Register previous mouse states previousMouseWheelY = currentMouseWheelY; @@ -3137,7 +3157,7 @@ static void PollInputEvents(void) gamepadAxisCount = axisCount; } } - + windowResized = false; #if defined(SUPPORT_EVENTS_WAITING) @@ -3174,7 +3194,7 @@ static void PollInputEvents(void) } else currentGamepadState[i][j] = 0; - //printf("Gamepad %d, button %d: Digital: %d, Analog: %g\n", gamepadState.index, j, gamepadState.digitalButton[j], gamepadState.analogButton[j]); + //TraceLog(LOG_DEBUG, "Gamepad %d, button %d: Digital: %d, Analog: %g", gamepadState.index, j, gamepadState.digitalButton[j], gamepadState.analogButton[j]); } // Register axis data for every connected gamepad @@ -3211,10 +3231,10 @@ static void PollInputEvents(void) #endif #if defined(PLATFORM_RPI) - // NOTE: Mouse input events polling is done asynchonously in another pthread - MouseThread() + // NOTE: Mouse input events polling is done asynchonously in another pthread - EventThread() // NOTE: Keyboard reading could be done using input_event(s) reading or just read from stdin, - // we use method 2 (stdin) but maybe in a future we should change to method 1... + // we now use both methods inside here. 2nd method is still used for legacy purposes (Allows for input trough SSH console) ProcessKeyboard(); // NOTE: Gamepad (Joystick) input events polling is done asynchonously in another pthread - GamepadThread() @@ -3416,7 +3436,7 @@ static void WindowSizeCallback(GLFWwindow *window, int width, int height) currentHeight = height; // NOTE: Postprocessing texture is not scaled to new size - + windowResized = true; } @@ -3684,7 +3704,6 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) ProcessGestureEvent(gestureEvent); } #else - // Support only simple touch position if (flags == AMOTION_EVENT_ACTION_DOWN) { @@ -3776,18 +3795,19 @@ static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent y = touchEvent->touches[i].canvasY; } - printf("%s, numTouches: %d %s%s%s%s\n", emscripten_event_type_to_string(eventType), event->numTouches, + TraceLog(LOG_DEBUG, "%s, numTouches: %d %s%s%s%s", emscripten_event_type_to_string(eventType), event->numTouches, event->ctrlKey? " CTRL" : "", event->shiftKey? " SHIFT" : "", event->altKey? " ALT" : "", event->metaKey? " META" : ""); for (int i = 0; i < event->numTouches; ++i) { const EmscriptenTouchPoint *t = &event->touches[i]; - printf(" %ld: screen: (%ld,%ld), client: (%ld,%ld), page: (%ld,%ld), isChanged: %d, onTarget: %d, canvas: (%ld, %ld)\n", + TraceLog(LOG_DEBUG, " %ld: screen: (%ld,%ld), client: (%ld,%ld), page: (%ld,%ld), isChanged: %d, onTarget: %d, canvas: (%ld, %ld)", t->identifier, t->screenX, t->screenY, t->clientX, t->clientY, t->pageX, t->pageY, t->isChanged, t->onTarget, t->canvasX, t->canvasY); } */ +#if defined(SUPPORT_GESTURES_SYSTEM) GestureEvent gestureEvent; // Register touch actions @@ -3822,6 +3842,17 @@ static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent // Gesture data is sent to gestures system for processing ProcessGestureEvent(gestureEvent); +#else + // Support only simple touch position + if (eventType == EMSCRIPTEN_EVENT_TOUCHSTART) + { + // Get first touch position + touchPosition[0] = (Vector2){ touchEvent->touches[0].targetX, touchEvent->touches[0].targetY }; + + touchPosition[0].x /= (float)GetScreenWidth(); + touchPosition[0].y /= (float)GetScreenHeight(); + } +#endif return 1; } @@ -3830,12 +3861,12 @@ static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent static EM_BOOL EmscriptenGamepadCallback(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData) { /* - printf("%s: timeStamp: %g, connected: %d, index: %ld, numAxes: %d, numButtons: %d, id: \"%s\", mapping: \"%s\"\n", + TraceLog(LOG_DEBUG, "%s: timeStamp: %g, connected: %d, index: %ld, numAxes: %d, numButtons: %d, id: \"%s\", mapping: \"%s\"", eventType != 0? emscripten_event_type_to_string(eventType) : "Gamepad state", gamepadEvent->timestamp, gamepadEvent->connected, gamepadEvent->index, gamepadEvent->numAxes, gamepadEvent->numButtons, gamepadEvent->id, gamepadEvent->mapping); - for(int i = 0; i < gamepadEvent->numAxes; ++i) printf("Axis %d: %g\n", i, gamepadEvent->axis[i]); - for(int i = 0; i < gamepadEvent->numButtons; ++i) printf("Button %d: Digital: %d, Analog: %g\n", i, gamepadEvent->digitalButton[i], gamepadEvent->analogButton[i]); + for(int i = 0; i < gamepadEvent->numAxes; ++i) TraceLog(LOG_DEBUG, "Axis %d: %g", i, gamepadEvent->axis[i]); + for(int i = 0; i < gamepadEvent->numButtons; ++i) TraceLog(LOG_DEBUG, "Button %d: Digital: %d, Analog: %g", i, gamepadEvent->digitalButton[i], gamepadEvent->analogButton[i]); */ if ((gamepadEvent->connected) && (gamepadEvent->index < MAX_GAMEPADS)) gamepadReady[gamepadEvent->index] = true; @@ -3910,15 +3941,14 @@ static void ProcessKeyboard(void) // Reset pressed keys array (it will be filled below) for (int i = 0; i < 512; i++) currentKeyState[i] = 0; + // Check keys from event input workers (This is the new keyboard reading method) + for (int i = 0; i < 512; i++) currentKeyState[i] = currentKeyStateEvdev[i]; + // Fill all read bytes (looking for keys) for (int i = 0; i < bufferByteCount; i++) { TraceLog(LOG_DEBUG, "Bytes on keysBuffer: %i", bufferByteCount); - //printf("Key(s) bytes: "); - //for (int i = 0; i < bufferByteCount; i++) printf("0x%02x ", keysBuffer[i]); - //printf("\n"); - // NOTE: If (key == 0x1b), depending on next key, it could be a special keymap code! // Up -> 1b 5b 41 / Left -> 1b 5b 44 / Right -> 1b 5b 43 / Down -> 1b 5b 42 if (keysBuffer[i] == 0x1b) @@ -4010,8 +4040,8 @@ static void RestoreKeyboard(void) ioctl(STDIN_FILENO, KDSKBMODE, defaultKeyboardMode); } -// Mouse initialization (including mouse thread) -static void InitMouse(void) +// Initialise user input from evdev(/dev/input/event) this means mouse, keyboard or gamepad devices +static void InitEvdevInput(void) { char path[MAX_FILEPATH_LENGTH]; DIR *directory; @@ -4023,6 +4053,11 @@ static void InitMouse(void) touchPosition[i].x = -1; touchPosition[i].y = -1; } + // Reset keypress buffer + lastKeyPressedEvdev.Head = 0; + lastKeyPressedEvdev.Tail = 0; + // Reset keyboard key state + for (int i = 0; i < 512; i++) currentKeyStateEvdev[i] = 0; // Open the linux directory of "/dev/input" directory = opendir(DEFAULT_EVDEV_PATH); @@ -4191,7 +4226,7 @@ static void EventThreadSpawn(char *device) // Decide what to do with the device //------------------------------------------------------------------------------------------------------- - if (worker->isTouch || worker->isMouse) + if (worker->isTouch || worker->isMouse || worker->isKeyboard) { // Looks like a interesting device TraceLog(LOG_INFO, "Opening input device [%s] (%s%s%s%s%s)", device, @@ -4241,13 +4276,35 @@ static void EventThreadSpawn(char *device) // Input device events reading thread static void *EventThread(void *arg) { + // Scancode to keycode mapping for US keyboards + // TODO: Proabobly replace this with a keymap from the X11 to get the correct regional map for the keyboard (Currently non US keyboards will have the wrong mapping for some keys) + static const int keymap_US[] = + {0,256,49,50,51,52,53,54,55,56,57,48,45,61,259,258,81,87,69,82,84, + 89,85,73,79,80,91,93,257,341,65,83,68,70,71,72,74,75,76,59,39,96, + 340,92,90,88,67,86,66,78,77,44,46,47,344,332,342,32,280,290,291, + 292,293,294,295,296,297,298,299,282,281,327,328,329,333,324,325, + 326,334,321,322,323,320,330,0,85,86,300,301,89,90,91,92,93,94,95, + 335,345,331,283,346,101,268,265,266,263,262,269,264,267,260,261, + 112,113,114,115,116,117,118,119,120,121,122,123,124,125,347,127, + 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, + 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, + 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, + 192,193,194,0,0,0,0,0,200,201,202,203,204,205,206,207,208,209,210, + 211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226, + 227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242, + 243,244,245,246,247,248,0,0,0,0,0,0,0,}; + struct input_event event; - GestureEvent gestureEvent; InputEventWorker *worker = (InputEventWorker *)arg; - bool GestureNeedsUpdate = false; + + int touchAction = -1; + bool gestureUpdate = false; + int keycode; while (!windowShouldClose) { + // Try to read data from the device and only continue if successful if (read(worker->fd, &event, sizeof(event)) == (int)sizeof(event)) { // Relative movement parsing @@ -4257,16 +4314,22 @@ static void *EventThread(void *arg) { mousePosition.x += event.value; touchPosition[0].x = mousePosition.x; - gestureEvent.touchAction = TOUCH_MOVE; - GestureNeedsUpdate = true; + + #if defined(SUPPORT_GESTURES_SYSTEM) + touchAction = TOUCH_MOVE; + gestureUpdate = true; + #endif } if (event.code == REL_Y) { mousePosition.y += event.value; touchPosition[0].y = mousePosition.y; - gestureEvent.touchAction = TOUCH_MOVE; - GestureNeedsUpdate = true; + + #if defined(SUPPORT_GESTURES_SYSTEM) + touchAction = TOUCH_MOVE; + gestureUpdate = true; + #endif } if (event.code == REL_WHEEL) @@ -4282,15 +4345,21 @@ static void *EventThread(void *arg) if (event.code == ABS_X) { mousePosition.x = (event.value - worker->absRange.x)*screenWidth/worker->absRange.width; // Scale acording to absRange - gestureEvent.touchAction = TOUCH_MOVE; - GestureNeedsUpdate = true; + + #if defined(SUPPORT_GESTURES_SYSTEM) + touchAction = TOUCH_MOVE; + gestureUpdate = true; + #endif } if (event.code == ABS_Y) { mousePosition.y = (event.value - worker->absRange.y)*screenHeight/worker->absRange.height; // Scale acording to absRange - gestureEvent.touchAction = TOUCH_MOVE; - GestureNeedsUpdate = true; + + #if defined(SUPPORT_GESTURES_SYSTEM) + touchAction = TOUCH_MOVE; + gestureUpdate = true; + #endif } // Multitouch movement @@ -4325,17 +4394,43 @@ static void *EventThread(void *arg) // Button parsing if (event.type == EV_KEY) { + + // Mouse button parsing if ((event.code == BTN_TOUCH) || (event.code == BTN_LEFT)) { currentMouseStateEvdev[MOUSE_LEFT_BUTTON] = event.value; - if (event.value > 0) gestureEvent.touchAction = TOUCH_DOWN; - else gestureEvent.touchAction = TOUCH_UP; - GestureNeedsUpdate = true; + + #if defined(SUPPORT_GESTURES_SYSTEM) + if (event.value > 0) touchAction = TOUCH_DOWN; + else touchAction = TOUCH_UP; + gestureUpdate = true; + #endif } if (event.code == BTN_RIGHT) currentMouseStateEvdev[MOUSE_RIGHT_BUTTON] = event.value; if (event.code == BTN_MIDDLE) currentMouseStateEvdev[MOUSE_MIDDLE_BUTTON] = event.value; + + // Keyboard button parsing + if((event.code >= 1) && (event.code <= 255)) //Keyboard keys appear for codes 1 to 255 + { + keycode = keymap_US[event.code & 0xFF]; // The code we get is a scancode so we look up the apropriate keycode + // Make sure we got a valid keycode + if((keycode > 0) && (keycode < sizeof(currentKeyState))) + { + // Store the key information for raylib to later use + currentKeyStateEvdev[keycode] = event.value; + if(event.value > 0) + { + // Add the key int the fifo + lastKeyPressedEvdev.Contents[lastKeyPressedEvdev.Head] = keycode; // Put the data at the front of the fifo snake + lastKeyPressedEvdev.Head = (lastKeyPressedEvdev.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) + } + TraceLog(LOG_DEBUG, "KEY%s ScanCode: %4i KeyCode: %4i",event.value == 0 ? "UP":"DOWN", event.code, keycode); + } + } + } // Screen confinement @@ -4346,22 +4441,31 @@ static void *EventThread(void *arg) if (mousePosition.y > screenHeight/mouseScale.y) mousePosition.y = screenHeight/mouseScale.y; // Gesture update - if (GestureNeedsUpdate) + if (gestureUpdate) { +#if defined(SUPPORT_GESTURES_SYSTEM) + GestureEvent gestureEvent = { 0 }; + gestureEvent.pointCount = 0; + gestureEvent.touchAction = touchAction; + if (touchPosition[0].x >= 0) gestureEvent.pointCount++; if (touchPosition[1].x >= 0) gestureEvent.pointCount++; if (touchPosition[2].x >= 0) gestureEvent.pointCount++; if (touchPosition[3].x >= 0) gestureEvent.pointCount++; + gestureEvent.pointerId[0] = 0; gestureEvent.pointerId[1] = 1; gestureEvent.pointerId[2] = 2; gestureEvent.pointerId[3] = 3; + gestureEvent.position[0] = touchPosition[0]; gestureEvent.position[1] = touchPosition[1]; gestureEvent.position[2] = touchPosition[2]; gestureEvent.position[3] = touchPosition[3]; + ProcessGestureEvent(gestureEvent); +#endif } } else diff --git a/src/external/tinyobj_loader_c.h b/src/external/tinyobj_loader_c.h new file mode 100644 index 000000000..846784fa3 --- /dev/null +++ b/src/external/tinyobj_loader_c.h @@ -0,0 +1,1584 @@ +/* + The MIT License (MIT) + + Copyright (c) 2016 - 2019 Syoyo Fujita and many contributors. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + */ +#ifndef TINOBJ_LOADER_C_H_ +#define TINOBJ_LOADER_C_H_ + +/* @todo { Remove stddef dependency. unsigned int? } ---> RAY: DONE. */ +//#include + +typedef struct { + char *name; + + float ambient[3]; + float diffuse[3]; + float specular[3]; + float transmittance[3]; + float emission[3]; + float shininess; + float ior; /* index of refraction */ + float dissolve; /* 1 == opaque; 0 == fully transparent */ + /* illumination model (see http://www.fileformat.info/format/material/) */ + int illum; + + int pad0; + + char *ambient_texname; /* map_Ka */ + char *diffuse_texname; /* map_Kd */ + char *specular_texname; /* map_Ks */ + char *specular_highlight_texname; /* map_Ns */ + char *bump_texname; /* map_bump, bump */ + char *displacement_texname; /* disp */ + char *alpha_texname; /* map_d */ +} tinyobj_material_t; + +typedef struct { + char *name; /* group name or object name. */ + unsigned int face_offset; + unsigned int length; +} tinyobj_shape_t; + +typedef struct { int v_idx, vt_idx, vn_idx; } tinyobj_vertex_index_t; + +typedef struct { + unsigned int num_vertices; + unsigned int num_normals; + unsigned int num_texcoords; + unsigned int num_faces; + unsigned int num_face_num_verts; + + int pad0; + + float *vertices; + float *normals; + float *texcoords; + tinyobj_vertex_index_t *faces; + int *face_num_verts; + int *material_ids; +} tinyobj_attrib_t; + + +#define TINYOBJ_FLAG_TRIANGULATE (1 << 0) + +#define TINYOBJ_INVALID_INDEX (0x80000000) + +#define TINYOBJ_SUCCESS (0) +#define TINYOBJ_ERROR_EMPTY (-1) +#define TINYOBJ_ERROR_INVALID_PARAMETER (-2) +#define TINYOBJ_ERROR_FILE_OPERATION (-3) + +/* Parse wavefront .obj(.obj string data is expanded to linear char array `buf') + * flags are combination of TINYOBJ_FLAG_*** + * Returns TINYOBJ_SUCCESS if things goes well. + * Returns TINYOBJ_ERR_*** when there is an error. + */ +extern int tinyobj_parse_obj(tinyobj_attrib_t *attrib, tinyobj_shape_t **shapes, + unsigned int *num_shapes, tinyobj_material_t **materials, + unsigned int *num_materials, const char *buf, unsigned int len, + unsigned int flags); +extern int tinyobj_parse_mtl_file(tinyobj_material_t **materials_out, + unsigned int *num_materials_out, + const char *filename); + +extern void tinyobj_attrib_init(tinyobj_attrib_t *attrib); +extern void tinyobj_attrib_free(tinyobj_attrib_t *attrib); +extern void tinyobj_shapes_free(tinyobj_shape_t *shapes, unsigned int num_shapes); +extern void tinyobj_materials_free(tinyobj_material_t *materials, + unsigned int num_materials); + +#ifdef TINYOBJ_LOADER_C_IMPLEMENTATION +#include +#include +#include +#include + +#if defined(TINYOBJ_MALLOC) && defined(TINYOBJ_REALLOC) && defined(TINYOBJ_CALLOC) && defined(TINYOBJ_FREE) +/* ok */ +#elif !defined(TINYOBJ_MALLOC) && !defined(TINYOBJ_REALLOC) && !defined(TINYOBJ_CALLOC) && !defined(TINYOBJ_FREE) +/* ok */ +#else +#error "Must define all or none of TINYOBJ_MALLOC, TINYOBJ_REALLOC, TINYOBJ_CALLOC and TINYOBJ_FREE." +#endif + +#ifndef TINYOBJ_MALLOC +#include +#define TINYOBJ_MALLOC malloc +#define TINYOBJ_REALLOC realloc +#define TINYOBJ_CALLOC calloc +#define TINYOBJ_FREE free +#endif + +#define TINYOBJ_MAX_FACES_PER_F_LINE (16) + +#define IS_SPACE(x) (((x) == ' ') || ((x) == '\t')) +#define IS_DIGIT(x) ((unsigned int)((x) - '0') < (unsigned int)(10)) +#define IS_NEW_LINE(x) (((x) == '\r') || ((x) == '\n') || ((x) == '\0')) + +static void skip_space(const char **token) { + while ((*token)[0] == ' ' || (*token)[0] == '\t') { + (*token)++; + } +} + +static void skip_space_and_cr(const char **token) { + while ((*token)[0] == ' ' || (*token)[0] == '\t' || (*token)[0] == '\r') { + (*token)++; + } +} + +static int until_space(const char *token) { + const char *p = token; + while (p[0] != '\0' && p[0] != ' ' && p[0] != '\t' && p[0] != '\r') { + p++; + } + + return (int)(p - token); +} + +static unsigned int length_until_newline(const char *token, unsigned int n) { + unsigned int len = 0; + + /* Assume token[n-1] = '\0' */ + for (len = 0; len < n - 1; len++) { + if (token[len] == '\n') { + break; + } + if ((token[len] == '\r') && ((len < (n - 2)) && (token[len + 1] != '\n'))) { + break; + } + } + + return len; +} + +static unsigned int length_until_line_feed(const char *token, unsigned int n) { + unsigned int len = 0; + + /* Assume token[n-1] = '\0' */ + for (len = 0; len < n; len++) { + if ((token[len] == '\n') || (token[len] == '\r')) { + break; + } + } + + return len; +} + +/* http://stackoverflow.com/questions/5710091/how-does-atoi-function-in-c-work +*/ +static int my_atoi(const char *c) { + int value = 0; + int sign = 1; + if (*c == '+' || *c == '-') { + if (*c == '-') sign = -1; + c++; + } + while (((*c) >= '0') && ((*c) <= '9')) { /* isdigit(*c) */ + value *= 10; + value += (int)(*c - '0'); + c++; + } + return value * sign; +} + +/* Make index zero-base, and also support relative index. */ +static int fixIndex(int idx, unsigned int n) { + if (idx > 0) return idx - 1; + if (idx == 0) return 0; + return (int)n + idx; /* negative value = relative */ +} + +/* Parse raw triples: i, i/j/k, i//k, i/j */ +static tinyobj_vertex_index_t parseRawTriple(const char **token) { + tinyobj_vertex_index_t vi; + /* 0x80000000 = -2147483648 = invalid */ + vi.v_idx = (int)(0x80000000); + vi.vn_idx = (int)(0x80000000); + vi.vt_idx = (int)(0x80000000); + + vi.v_idx = my_atoi((*token)); + while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' && + (*token)[0] != '\t' && (*token)[0] != '\r') { + (*token)++; + } + if ((*token)[0] != '/') { + return vi; + } + (*token)++; + + /* i//k */ + if ((*token)[0] == '/') { + (*token)++; + vi.vn_idx = my_atoi((*token)); + while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' && + (*token)[0] != '\t' && (*token)[0] != '\r') { + (*token)++; + } + return vi; + } + + /* i/j/k or i/j */ + vi.vt_idx = my_atoi((*token)); + while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' && + (*token)[0] != '\t' && (*token)[0] != '\r') { + (*token)++; + } + if ((*token)[0] != '/') { + return vi; + } + + /* i/j/k */ + (*token)++; /* skip '/' */ + vi.vn_idx = my_atoi((*token)); + while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' && + (*token)[0] != '\t' && (*token)[0] != '\r') { + (*token)++; + } + return vi; +} + +static int parseInt(const char **token) { + int i = 0; + skip_space(token); + i = my_atoi((*token)); + (*token) += until_space((*token)); + return i; +} + +/* + * Tries to parse a floating point number located at s. + * + * s_end should be a location in the string where reading should absolutely + * stop. For example at the end of the string, to prevent buffer overflows. + * + * Parses the following EBNF grammar: + * sign = "+" | "-" ; + * END = ? anything not in digit ? + * digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ; + * integer = [sign] , digit , {digit} ; + * decimal = integer , ["." , integer] ; + * float = ( decimal , END ) | ( decimal , ("E" | "e") , integer , END ) ; + * + * Valid strings are for example: + * -0 +3.1417e+2 -0.0E-3 1.0324 -1.41 11e2 + * + * If the parsing is a success, result is set to the parsed value and true + * is returned. + * + * The function is greedy and will parse until any of the following happens: + * - a non-conforming character is encountered. + * - s_end is reached. + * + * The following situations triggers a failure: + * - s >= s_end. + * - parse failure. + */ +static int tryParseDouble(const char *s, const char *s_end, double *result) { + double mantissa = 0.0; + /* This exponent is base 2 rather than 10. + * However the exponent we parse is supposed to be one of ten, + * thus we must take care to convert the exponent/and or the + * mantissa to a * 2^E, where a is the mantissa and E is the + * exponent. + * To get the final double we will use ldexp, it requires the + * exponent to be in base 2. + */ + int exponent = 0; + + /* NOTE: THESE MUST BE DECLARED HERE SINCE WE ARE NOT ALLOWED + * TO JUMP OVER DEFINITIONS. + */ + char sign = '+'; + char exp_sign = '+'; + char const *curr = s; + + /* How many characters were read in a loop. */ + int read = 0; + /* Tells whether a loop terminated due to reaching s_end. */ + int end_not_reached = 0; + + /* + BEGIN PARSING. + */ + + if (s >= s_end) { + return 0; /* fail */ + } + + /* Find out what sign we've got. */ + if (*curr == '+' || *curr == '-') { + sign = *curr; + curr++; + } else if (IS_DIGIT(*curr)) { /* Pass through. */ + } else { + goto fail; + } + + /* Read the integer part. */ + end_not_reached = (curr != s_end); + while (end_not_reached && IS_DIGIT(*curr)) { + mantissa *= 10; + mantissa += (int)(*curr - 0x30); + curr++; + read++; + end_not_reached = (curr != s_end); + } + + /* We must make sure we actually got something. */ + if (read == 0) goto fail; + /* We allow numbers of form "#", "###" etc. */ + if (!end_not_reached) goto assemble; + + /* Read the decimal part. */ + if (*curr == '.') { + curr++; + read = 1; + end_not_reached = (curr != s_end); + while (end_not_reached && IS_DIGIT(*curr)) { + /* pow(10.0, -read) */ + double frac_value = 1.0; + int f; + for (f = 0; f < read; f++) { + frac_value *= 0.1; + } + mantissa += (int)(*curr - 0x30) * frac_value; + read++; + curr++; + end_not_reached = (curr != s_end); + } + } else if (*curr == 'e' || *curr == 'E') { + } else { + goto assemble; + } + + if (!end_not_reached) goto assemble; + + /* Read the exponent part. */ + if (*curr == 'e' || *curr == 'E') { + curr++; + /* Figure out if a sign is present and if it is. */ + end_not_reached = (curr != s_end); + if (end_not_reached && (*curr == '+' || *curr == '-')) { + exp_sign = *curr; + curr++; + } else if (IS_DIGIT(*curr)) { /* Pass through. */ + } else { + /* Empty E is not allowed. */ + goto fail; + } + + read = 0; + end_not_reached = (curr != s_end); + while (end_not_reached && IS_DIGIT(*curr)) { + exponent *= 10; + exponent += (int)(*curr - 0x30); + curr++; + read++; + end_not_reached = (curr != s_end); + } + if (read == 0) goto fail; + } + +assemble : + + { + double a = 1.0; /* = pow(5.0, exponent); */ + double b = 1.0; /* = 2.0^exponent */ + int i; + for (i = 0; i < exponent; i++) { + a = a * 5.0; + } + + for (i = 0; i < exponent; i++) { + b = b * 2.0; + } + + if (exp_sign == '-') { + a = 1.0 / a; + b = 1.0 / b; + } + + *result = + /* (sign == '+' ? 1 : -1) * ldexp(mantissa * pow(5.0, exponent), + exponent); */ + (sign == '+' ? 1 : -1) * (mantissa * a * b); + } + + return 1; +fail: + return 0; +} + +static float parseFloat(const char **token) { + const char *end; + double val = 0.0; + float f = 0.0f; + skip_space(token); + end = (*token) + until_space((*token)); + val = 0.0; + tryParseDouble((*token), end, &val); + f = (float)(val); + (*token) = end; + return f; +} + +static void parseFloat2(float *x, float *y, const char **token) { + (*x) = parseFloat(token); + (*y) = parseFloat(token); +} + +static void parseFloat3(float *x, float *y, float *z, const char **token) { + (*x) = parseFloat(token); + (*y) = parseFloat(token); + (*z) = parseFloat(token); +} + +static char *my_strdup(const char *s, unsigned int max_length) { + char *d; + unsigned int len; + + if (s == NULL) return NULL; + + /* Do not consider CRLF line ending(#19) */ + len = length_until_line_feed(s, max_length); + /* len = strlen(s); */ + + /* trim line ending and append '\0' */ + d = (char *)TINYOBJ_MALLOC(len + 1); /* + '\0' */ + memcpy(d, s, (unsigned int)(len)); + d[len] = '\0'; + + return d; +} + +static char *my_strndup(const char *s, unsigned int len) { + char *d; + unsigned int slen; + + if (s == NULL) return NULL; + if (len == 0) return NULL; + + d = (char *)TINYOBJ_MALLOC(len + 1); /* + '\0' */ + slen = strlen(s); + if (slen < len) { + memcpy(d, s, slen); + d[slen] = '\0'; + } else { + memcpy(d, s, len); + d[len] = '\0'; + } + + return d; +} + +char *dynamic_fgets(char **buf, unsigned int *size, FILE *file) { + char *offset; + char *ret; + unsigned int old_size; + + if (!(ret = fgets(*buf, (int)*size, file))) { + return ret; + } + + if (NULL != strchr(*buf, '\n')) { + return ret; + } + + do { + old_size = *size; + *size *= 2; + *buf = (char*)TINYOBJ_REALLOC(*buf, *size); + offset = &((*buf)[old_size - 1]); + + ret = fgets(offset, (int)(old_size + 1), file); + } while(ret && (NULL == strchr(*buf, '\n'))); + + return ret; +} + +static void initMaterial(tinyobj_material_t *material) { + int i; + material->name = NULL; + material->ambient_texname = NULL; + material->diffuse_texname = NULL; + material->specular_texname = NULL; + material->specular_highlight_texname = NULL; + material->bump_texname = NULL; + material->displacement_texname = NULL; + material->alpha_texname = NULL; + for (i = 0; i < 3; i++) { + material->ambient[i] = 0.f; + material->diffuse[i] = 0.f; + material->specular[i] = 0.f; + material->transmittance[i] = 0.f; + material->emission[i] = 0.f; + } + material->illum = 0; + material->dissolve = 1.f; + material->shininess = 1.f; + material->ior = 1.f; +} + +/* Implementation of string to int hashtable */ + +#define HASH_TABLE_ERROR 1 +#define HASH_TABLE_SUCCESS 0 + +#define HASH_TABLE_DEFAULT_SIZE 10 + +typedef struct hash_table_entry_t +{ + unsigned long hash; + int filled; + int pad0; + long value; + + struct hash_table_entry_t* next; +} hash_table_entry_t; + +typedef struct +{ + unsigned long* hashes; + hash_table_entry_t* entries; + unsigned int capacity; + unsigned int n; +} hash_table_t; + +static unsigned long hash_djb2(const unsigned char* str) +{ + unsigned long hash = 5381; + int c; + + while ((c = *str++)) { + hash = ((hash << 5) + hash) + (unsigned long)(c); + } + + return hash; +} + +static void create_hash_table(unsigned int start_capacity, hash_table_t* hash_table) +{ + if (start_capacity < 1) + start_capacity = HASH_TABLE_DEFAULT_SIZE; + hash_table->hashes = (unsigned long*) TINYOBJ_MALLOC(start_capacity * sizeof(unsigned long)); + hash_table->entries = (hash_table_entry_t*) TINYOBJ_CALLOC(start_capacity, sizeof(hash_table_entry_t)); + hash_table->capacity = start_capacity; + hash_table->n = 0; +} + +static void destroy_hash_table(hash_table_t* hash_table) +{ + TINYOBJ_FREE(hash_table->entries); + TINYOBJ_FREE(hash_table->hashes); +} + +/* Insert with quadratic probing */ +static int hash_table_insert_value(unsigned long hash, long value, hash_table_t* hash_table) +{ + /* Insert value */ + unsigned int start_index = hash % hash_table->capacity; + unsigned int index = start_index; + hash_table_entry_t* start_entry = hash_table->entries + start_index; + unsigned int i; + hash_table_entry_t* entry; + + for (i = 1; hash_table->entries[index].filled; i++) + { + if (i >= hash_table->capacity) + return HASH_TABLE_ERROR; + index = (start_index + (i * i)) % hash_table->capacity; + } + + entry = hash_table->entries + index; + entry->hash = hash; + entry->filled = 1; + entry->value = value; + + if (index != start_index) { + /* This is a new entry, but not the start entry, hence we need to add a next pointer to our entry */ + entry->next = start_entry->next; + start_entry->next = entry; + } + + return HASH_TABLE_SUCCESS; +} + +static int hash_table_insert(unsigned long hash, long value, hash_table_t* hash_table) +{ + int ret = hash_table_insert_value(hash, value, hash_table); + if (ret == HASH_TABLE_SUCCESS) + { + hash_table->hashes[hash_table->n] = hash; + hash_table->n++; + } + return ret; +} + +static hash_table_entry_t* hash_table_find(unsigned long hash, hash_table_t* hash_table) +{ + hash_table_entry_t* entry = hash_table->entries + (hash % hash_table->capacity); + while (entry) + { + if (entry->hash == hash && entry->filled) + { + return entry; + } + entry = entry->next; + } + return NULL; +} + +static void hash_table_maybe_grow(unsigned int new_n, hash_table_t* hash_table) +{ + unsigned int new_capacity; + hash_table_t new_hash_table; + unsigned int i; + + if (new_n <= hash_table->capacity) { + return; + } + new_capacity = 2 * ((2 * hash_table->capacity) > new_n ? hash_table->capacity : new_n); + /* Create a new hash table. We're not calling create_hash_table because we want to realloc the hash array */ + new_hash_table.hashes = hash_table->hashes = (unsigned long*) TINYOBJ_REALLOC((void*) hash_table->hashes, sizeof(unsigned long) * new_capacity); + new_hash_table.entries = (hash_table_entry_t*) TINYOBJ_CALLOC(new_capacity, sizeof(hash_table_entry_t)); + new_hash_table.capacity = new_capacity; + new_hash_table.n = hash_table->n; + + /* Rehash */ + for (i = 0; i < hash_table->capacity; i++) + { + hash_table_entry_t* entry = hash_table_find(hash_table->hashes[i], hash_table); + hash_table_insert_value(hash_table->hashes[i], entry->value, &new_hash_table); + } + + TINYOBJ_FREE(hash_table->entries); + (*hash_table) = new_hash_table; +} + +static int hash_table_exists(const char* name, hash_table_t* hash_table) +{ + return hash_table_find(hash_djb2((const unsigned char*)name), hash_table) != NULL; +} + +static void hash_table_set(const char* name, unsigned int val, hash_table_t* hash_table) +{ + /* Hash name */ + unsigned long hash = hash_djb2((const unsigned char *)name); + + hash_table_entry_t* entry = hash_table_find(hash, hash_table); + if (entry) + { + entry->value = (long)val; + return; + } + + /* Expand if necessary + * Grow until the element has been added + */ + do + { + hash_table_maybe_grow(hash_table->n + 1, hash_table); + } + while (hash_table_insert(hash, (long)val, hash_table) != HASH_TABLE_SUCCESS); +} + +static long hash_table_get(const char* name, hash_table_t* hash_table) +{ + hash_table_entry_t* ret = hash_table_find(hash_djb2((const unsigned char*)(name)), hash_table); + return ret->value; +} + +static tinyobj_material_t *tinyobj_material_add(tinyobj_material_t *prev, + unsigned int num_materials, + tinyobj_material_t *new_mat) { + tinyobj_material_t *dst; + dst = (tinyobj_material_t *)TINYOBJ_REALLOC( + prev, sizeof(tinyobj_material_t) * (num_materials + 1)); + + dst[num_materials] = (*new_mat); /* Just copy pointer for char* members */ + return dst; +} + +static int tinyobj_parse_and_index_mtl_file(tinyobj_material_t **materials_out, + unsigned int *num_materials_out, + const char *filename, + hash_table_t* material_table) { + tinyobj_material_t material; + unsigned int buffer_size = 128; + char *linebuf; + FILE *fp; + unsigned int num_materials = 0; + tinyobj_material_t *materials = NULL; + int has_previous_material = 0; + const char *line_end = NULL; + + if (materials_out == NULL) { + return TINYOBJ_ERROR_INVALID_PARAMETER; + } + + if (num_materials_out == NULL) { + return TINYOBJ_ERROR_INVALID_PARAMETER; + } + + (*materials_out) = NULL; + (*num_materials_out) = 0; + + fp = fopen(filename, "r"); + if (!fp) { + fprintf(stderr, "TINYOBJ: Error reading file '%s': %s (%d)\n", filename, strerror(errno), errno); + return TINYOBJ_ERROR_FILE_OPERATION; + } + + /* Create a default material */ + initMaterial(&material); + + linebuf = (char*)TINYOBJ_MALLOC(buffer_size); + while (NULL != dynamic_fgets(&linebuf, &buffer_size, fp)) { + const char *token = linebuf; + + line_end = token + strlen(token); + + /* Skip leading space. */ + token += strspn(token, " \t"); + + assert(token); + if (token[0] == '\0') continue; /* empty line */ + + if (token[0] == '#') continue; /* comment line */ + + /* new mtl */ + if ((0 == strncmp(token, "newmtl", 6)) && IS_SPACE((token[6]))) { + char namebuf[4096]; + + /* flush previous material. */ + if (has_previous_material) { + materials = tinyobj_material_add(materials, num_materials, &material); + num_materials++; + } else { + has_previous_material = 1; + } + + /* initial temporary material */ + initMaterial(&material); + + /* set new mtl name */ + token += 7; +#ifdef _MSC_VER + sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf)); +#else + sscanf(token, "%s", namebuf); +#endif + material.name = my_strdup(namebuf, (unsigned int) (line_end - token)); + + /* Add material to material table */ + if (material_table) + hash_table_set(material.name, num_materials, material_table); + + continue; + } + + /* ambient */ + if (token[0] == 'K' && token[1] == 'a' && IS_SPACE((token[2]))) { + float r, g, b; + token += 2; + parseFloat3(&r, &g, &b, &token); + material.ambient[0] = r; + material.ambient[1] = g; + material.ambient[2] = b; + continue; + } + + /* diffuse */ + if (token[0] == 'K' && token[1] == 'd' && IS_SPACE((token[2]))) { + float r, g, b; + token += 2; + parseFloat3(&r, &g, &b, &token); + material.diffuse[0] = r; + material.diffuse[1] = g; + material.diffuse[2] = b; + continue; + } + + /* specular */ + if (token[0] == 'K' && token[1] == 's' && IS_SPACE((token[2]))) { + float r, g, b; + token += 2; + parseFloat3(&r, &g, &b, &token); + material.specular[0] = r; + material.specular[1] = g; + material.specular[2] = b; + continue; + } + + /* transmittance */ + if (token[0] == 'K' && token[1] == 't' && IS_SPACE((token[2]))) { + float r, g, b; + token += 2; + parseFloat3(&r, &g, &b, &token); + material.transmittance[0] = r; + material.transmittance[1] = g; + material.transmittance[2] = b; + continue; + } + + /* ior(index of refraction) */ + if (token[0] == 'N' && token[1] == 'i' && IS_SPACE((token[2]))) { + token += 2; + material.ior = parseFloat(&token); + continue; + } + + /* emission */ + if (token[0] == 'K' && token[1] == 'e' && IS_SPACE(token[2])) { + float r, g, b; + token += 2; + parseFloat3(&r, &g, &b, &token); + material.emission[0] = r; + material.emission[1] = g; + material.emission[2] = b; + continue; + } + + /* shininess */ + if (token[0] == 'N' && token[1] == 's' && IS_SPACE(token[2])) { + token += 2; + material.shininess = parseFloat(&token); + continue; + } + + /* illum model */ + if (0 == strncmp(token, "illum", 5) && IS_SPACE(token[5])) { + token += 6; + material.illum = parseInt(&token); + continue; + } + + /* dissolve */ + if ((token[0] == 'd' && IS_SPACE(token[1]))) { + token += 1; + material.dissolve = parseFloat(&token); + continue; + } + if (token[0] == 'T' && token[1] == 'r' && IS_SPACE(token[2])) { + token += 2; + /* Invert value of Tr(assume Tr is in range [0, 1]) */ + material.dissolve = 1.0f - parseFloat(&token); + continue; + } + + /* ambient texture */ + if ((0 == strncmp(token, "map_Ka", 6)) && IS_SPACE(token[6])) { + token += 7; + material.ambient_texname = my_strdup(token, (unsigned int) (line_end - token)); + continue; + } + + /* diffuse texture */ + if ((0 == strncmp(token, "map_Kd", 6)) && IS_SPACE(token[6])) { + token += 7; + material.diffuse_texname = my_strdup(token, (unsigned int) (line_end - token)); + continue; + } + + /* specular texture */ + if ((0 == strncmp(token, "map_Ks", 6)) && IS_SPACE(token[6])) { + token += 7; + material.specular_texname = my_strdup(token, (unsigned int) (line_end - token)); + continue; + } + + /* specular highlight texture */ + if ((0 == strncmp(token, "map_Ns", 6)) && IS_SPACE(token[6])) { + token += 7; + material.specular_highlight_texname = my_strdup(token, (unsigned int) (line_end - token)); + continue; + } + + /* bump texture */ + if ((0 == strncmp(token, "map_bump", 8)) && IS_SPACE(token[8])) { + token += 9; + material.bump_texname = my_strdup(token, (unsigned int) (line_end - token)); + continue; + } + + /* alpha texture */ + if ((0 == strncmp(token, "map_d", 5)) && IS_SPACE(token[5])) { + token += 6; + material.alpha_texname = my_strdup(token, (unsigned int) (line_end - token)); + continue; + } + + /* bump texture */ + if ((0 == strncmp(token, "bump", 4)) && IS_SPACE(token[4])) { + token += 5; + material.bump_texname = my_strdup(token, (unsigned int) (line_end - token)); + continue; + } + + /* displacement texture */ + if ((0 == strncmp(token, "disp", 4)) && IS_SPACE(token[4])) { + token += 5; + material.displacement_texname = my_strdup(token, (unsigned int) (line_end - token)); + continue; + } + + /* @todo { unknown parameter } */ + } + + if (material.name) { + /* Flush last material element */ + materials = tinyobj_material_add(materials, num_materials, &material); + num_materials++; + } + + (*num_materials_out) = num_materials; + (*materials_out) = materials; + + if (linebuf) { + TINYOBJ_FREE(linebuf); + } + + return TINYOBJ_SUCCESS; +} + +int tinyobj_parse_mtl_file(tinyobj_material_t **materials_out, + unsigned int *num_materials_out, + const char *filename) { + return tinyobj_parse_and_index_mtl_file(materials_out, num_materials_out, filename, NULL); +} + + +typedef enum { + COMMAND_EMPTY, + COMMAND_V, + COMMAND_VN, + COMMAND_VT, + COMMAND_F, + COMMAND_G, + COMMAND_O, + COMMAND_USEMTL, + COMMAND_MTLLIB + +} CommandType; + +typedef struct { + float vx, vy, vz; + float nx, ny, nz; + float tx, ty; + + /* @todo { Use dynamic array } */ + tinyobj_vertex_index_t f[TINYOBJ_MAX_FACES_PER_F_LINE]; + unsigned int num_f; + + int f_num_verts[TINYOBJ_MAX_FACES_PER_F_LINE]; + unsigned int num_f_num_verts; + + const char *group_name; + unsigned int group_name_len; + int pad0; + + const char *object_name; + unsigned int object_name_len; + int pad1; + + const char *material_name; + unsigned int material_name_len; + int pad2; + + const char *mtllib_name; + unsigned int mtllib_name_len; + + CommandType type; +} Command; + +static int parseLine(Command *command, const char *p, unsigned int p_len, + int triangulate) { + char linebuf[4096]; + const char *token; + assert(p_len < 4095); + + memcpy(linebuf, p, p_len); + linebuf[p_len] = '\0'; + + token = linebuf; + + command->type = COMMAND_EMPTY; + + /* Skip leading space. */ + skip_space(&token); + + assert(token); + if (token[0] == '\0') { /* empty line */ + return 0; + } + + if (token[0] == '#') { /* comment line */ + return 0; + } + + /* vertex */ + if (token[0] == 'v' && IS_SPACE((token[1]))) { + float x, y, z; + token += 2; + parseFloat3(&x, &y, &z, &token); + command->vx = x; + command->vy = y; + command->vz = z; + command->type = COMMAND_V; + return 1; + } + + /* normal */ + if (token[0] == 'v' && token[1] == 'n' && IS_SPACE((token[2]))) { + float x, y, z; + token += 3; + parseFloat3(&x, &y, &z, &token); + command->nx = x; + command->ny = y; + command->nz = z; + command->type = COMMAND_VN; + return 1; + } + + /* texcoord */ + if (token[0] == 'v' && token[1] == 't' && IS_SPACE((token[2]))) { + float x, y; + token += 3; + parseFloat2(&x, &y, &token); + command->tx = x; + command->ty = y; + command->type = COMMAND_VT; + return 1; + } + + /* face */ + if (token[0] == 'f' && IS_SPACE((token[1]))) { + unsigned int num_f = 0; + + tinyobj_vertex_index_t f[TINYOBJ_MAX_FACES_PER_F_LINE]; + token += 2; + skip_space(&token); + + while (!IS_NEW_LINE(token[0])) { + tinyobj_vertex_index_t vi = parseRawTriple(&token); + skip_space_and_cr(&token); + + f[num_f] = vi; + num_f++; + } + + command->type = COMMAND_F; + + if (triangulate) { + unsigned int k; + unsigned int n = 0; + + tinyobj_vertex_index_t i0 = f[0]; + tinyobj_vertex_index_t i1; + tinyobj_vertex_index_t i2 = f[1]; + + assert(3 * num_f < TINYOBJ_MAX_FACES_PER_F_LINE); + + for (k = 2; k < num_f; k++) { + i1 = i2; + i2 = f[k]; + command->f[3 * n + 0] = i0; + command->f[3 * n + 1] = i1; + command->f[3 * n + 2] = i2; + + command->f_num_verts[n] = 3; + n++; + } + command->num_f = 3 * n; + command->num_f_num_verts = n; + + } else { + unsigned int k = 0; + assert(num_f < TINYOBJ_MAX_FACES_PER_F_LINE); + for (k = 0; k < num_f; k++) { + command->f[k] = f[k]; + } + + command->num_f = num_f; + command->f_num_verts[0] = (int)num_f; + command->num_f_num_verts = 1; + } + + return 1; + } + + /* use mtl */ + if ((0 == strncmp(token, "usemtl", 6)) && IS_SPACE((token[6]))) { + token += 7; + + skip_space(&token); + command->material_name = p + (token - linebuf); + command->material_name_len = (unsigned int)length_until_newline( + token, (p_len - (unsigned int)(token - linebuf)) + 1); + command->type = COMMAND_USEMTL; + + return 1; + } + + /* load mtl */ + if ((0 == strncmp(token, "mtllib", 6)) && IS_SPACE((token[6]))) { + /* By specification, `mtllib` should be appear only once in .obj */ + token += 7; + + skip_space(&token); + command->mtllib_name = p + (token - linebuf); + command->mtllib_name_len = (unsigned int)length_until_newline( + token, p_len - (unsigned int)(token - linebuf)) + + 1; + command->type = COMMAND_MTLLIB; + + return 1; + } + + /* group name */ + if (token[0] == 'g' && IS_SPACE((token[1]))) { + /* @todo { multiple group name. } */ + token += 2; + + command->group_name = p + (token - linebuf); + command->group_name_len = (unsigned int)length_until_newline( + token, p_len - (unsigned int)(token - linebuf)) + + 1; + command->type = COMMAND_G; + + return 1; + } + + /* object name */ + if (token[0] == 'o' && IS_SPACE((token[1]))) { + /* @todo { multiple object name? } */ + token += 2; + + command->object_name = p + (token - linebuf); + command->object_name_len = (unsigned int)length_until_newline( + token, p_len - (unsigned int)(token - linebuf)) + + 1; + command->type = COMMAND_O; + + return 1; + } + + return 0; +} + +typedef struct { + unsigned int pos; + unsigned int len; +} LineInfo; + +static int is_line_ending(const char *p, unsigned int i, unsigned int end_i) { + if (p[i] == '\0') return 1; + if (p[i] == '\n') return 1; /* this includes \r\n */ + if (p[i] == '\r') { + if (((i + 1) < end_i) && (p[i + 1] != '\n')) { /* detect only \r case */ + return 1; + } + } + return 0; +} + +int tinyobj_parse_obj(tinyobj_attrib_t *attrib, tinyobj_shape_t **shapes, + unsigned int *num_shapes, tinyobj_material_t **materials_out, + unsigned int *num_materials_out, const char *buf, unsigned int len, + unsigned int flags) { + LineInfo *line_infos = NULL; + Command *commands = NULL; + unsigned int num_lines = 0; + + unsigned int num_v = 0; + unsigned int num_vn = 0; + unsigned int num_vt = 0; + unsigned int num_f = 0; + unsigned int num_faces = 0; + + int mtllib_line_index = -1; + + tinyobj_material_t *materials = NULL; + unsigned int num_materials = 0; + + hash_table_t material_table; + + if (len < 1) return TINYOBJ_ERROR_INVALID_PARAMETER; + if (attrib == NULL) return TINYOBJ_ERROR_INVALID_PARAMETER; + if (shapes == NULL) return TINYOBJ_ERROR_INVALID_PARAMETER; + if (num_shapes == NULL) return TINYOBJ_ERROR_INVALID_PARAMETER; + if (buf == NULL) return TINYOBJ_ERROR_INVALID_PARAMETER; + if (materials_out == NULL) return TINYOBJ_ERROR_INVALID_PARAMETER; + if (num_materials_out == NULL) return TINYOBJ_ERROR_INVALID_PARAMETER; + + tinyobj_attrib_init(attrib); + /* 1. Find '\n' and create line data. */ + { + unsigned int i; + unsigned int end_idx = len; + unsigned int prev_pos = 0; + unsigned int line_no = 0; + unsigned int last_line_ending = 0; + + /* Count # of lines. */ + for (i = 0; i < end_idx; i++) { + if (is_line_ending(buf, i, end_idx)) { + num_lines++; + last_line_ending = i; + } + } + /* The last char from the input may not be a line + * ending character so add an extra line if there + * are more characters after the last line ending + * that was found. */ + if (end_idx - last_line_ending > 0) { + num_lines++; + } + + if (num_lines == 0) return TINYOBJ_ERROR_EMPTY; + + line_infos = (LineInfo *)TINYOBJ_MALLOC(sizeof(LineInfo) * num_lines); + + /* Fill line infos. */ + for (i = 0; i < end_idx; i++) { + if (is_line_ending(buf, i, end_idx)) { + line_infos[line_no].pos = prev_pos; + line_infos[line_no].len = i - prev_pos; + prev_pos = i + 1; + line_no++; + } + } + if (end_idx - last_line_ending > 0) { + line_infos[line_no].pos = prev_pos; + line_infos[line_no].len = end_idx - 1 - last_line_ending; + } + } + + commands = (Command *)TINYOBJ_MALLOC(sizeof(Command) * num_lines); + + create_hash_table(HASH_TABLE_DEFAULT_SIZE, &material_table); + + /* 2. parse each line */ + { + unsigned int i = 0; + for (i = 0; i < num_lines; i++) { + int ret = parseLine(&commands[i], &buf[line_infos[i].pos], + line_infos[i].len, flags & TINYOBJ_FLAG_TRIANGULATE); + if (ret) { + if (commands[i].type == COMMAND_V) { + num_v++; + } else if (commands[i].type == COMMAND_VN) { + num_vn++; + } else if (commands[i].type == COMMAND_VT) { + num_vt++; + } else if (commands[i].type == COMMAND_F) { + num_f += commands[i].num_f; + num_faces += commands[i].num_f_num_verts; + } + + if (commands[i].type == COMMAND_MTLLIB) { + mtllib_line_index = (int)i; + } + } + } + } + + /* line_infos are not used anymore. Release memory. */ + if (line_infos) { + TINYOBJ_FREE(line_infos); + } + + /* Load material(if exits) */ + if (mtllib_line_index >= 0 && commands[mtllib_line_index].mtllib_name && + commands[mtllib_line_index].mtllib_name_len > 0) { + char *filename = my_strndup(commands[mtllib_line_index].mtllib_name, + commands[mtllib_line_index].mtllib_name_len); + + int ret = tinyobj_parse_and_index_mtl_file(&materials, &num_materials, filename, &material_table); + + if (ret != TINYOBJ_SUCCESS) { + /* warning. */ + fprintf(stderr, "TINYOBJ: Failed to parse material file '%s': %d\n", filename, ret); + } + + TINYOBJ_FREE(filename); + + } + + /* Construct attributes */ + + { + unsigned int v_count = 0; + unsigned int n_count = 0; + unsigned int t_count = 0; + unsigned int f_count = 0; + unsigned int face_count = 0; + int material_id = -1; /* -1 = default unknown material. */ + unsigned int i = 0; + + attrib->vertices = (float *)TINYOBJ_MALLOC(sizeof(float) * num_v * 3); + attrib->num_vertices = (unsigned int)num_v; + attrib->normals = (float *)TINYOBJ_MALLOC(sizeof(float) * num_vn * 3); + attrib->num_normals = (unsigned int)num_vn; + attrib->texcoords = (float *)TINYOBJ_MALLOC(sizeof(float) * num_vt * 2); + attrib->num_texcoords = (unsigned int)num_vt; + attrib->faces = (tinyobj_vertex_index_t *)TINYOBJ_MALLOC(sizeof(tinyobj_vertex_index_t) * num_f); + attrib->face_num_verts = (int *)TINYOBJ_MALLOC(sizeof(int) * num_faces); + + attrib->num_faces = (unsigned int)num_faces; + attrib->num_face_num_verts = (unsigned int)num_f; + + attrib->material_ids = (int *)TINYOBJ_MALLOC(sizeof(int) * num_faces); + + for (i = 0; i < num_lines; i++) { + if (commands[i].type == COMMAND_EMPTY) { + continue; + } else if (commands[i].type == COMMAND_USEMTL) { + /* @todo + if (commands[t][i].material_name && + commands[t][i].material_name_len > 0) { + std::string material_name(commands[t][i].material_name, + commands[t][i].material_name_len); + + if (material_map.find(material_name) != material_map.end()) { + material_id = material_map[material_name]; + } else { + // Assign invalid material ID + material_id = -1; + } + } + */ + if (commands[i].material_name && + commands[i].material_name_len >0) + { + /* Create a null terminated string */ + char* material_name_null_term = (char*) TINYOBJ_MALLOC(commands[i].material_name_len + 1); + memcpy((void*) material_name_null_term, (const void*) commands[i].material_name, commands[i].material_name_len); + material_name_null_term[commands[i].material_name_len - 1] = 0; + + if (hash_table_exists(material_name_null_term, &material_table)) + material_id = (int)hash_table_get(material_name_null_term, &material_table); + else + material_id = -1; + + TINYOBJ_FREE(material_name_null_term); + } + } else if (commands[i].type == COMMAND_V) { + attrib->vertices[3 * v_count + 0] = commands[i].vx; + attrib->vertices[3 * v_count + 1] = commands[i].vy; + attrib->vertices[3 * v_count + 2] = commands[i].vz; + v_count++; + } else if (commands[i].type == COMMAND_VN) { + attrib->normals[3 * n_count + 0] = commands[i].nx; + attrib->normals[3 * n_count + 1] = commands[i].ny; + attrib->normals[3 * n_count + 2] = commands[i].nz; + n_count++; + } else if (commands[i].type == COMMAND_VT) { + attrib->texcoords[2 * t_count + 0] = commands[i].tx; + attrib->texcoords[2 * t_count + 1] = commands[i].ty; + t_count++; + } else if (commands[i].type == COMMAND_F) { + unsigned int k = 0; + for (k = 0; k < commands[i].num_f; k++) { + tinyobj_vertex_index_t vi = commands[i].f[k]; + int v_idx = fixIndex(vi.v_idx, v_count); + int vn_idx = fixIndex(vi.vn_idx, n_count); + int vt_idx = fixIndex(vi.vt_idx, t_count); + attrib->faces[f_count + k].v_idx = v_idx; + attrib->faces[f_count + k].vn_idx = vn_idx; + attrib->faces[f_count + k].vt_idx = vt_idx; + } + + for (k = 0; k < commands[i].num_f_num_verts; k++) { + attrib->material_ids[face_count + k] = material_id; + attrib->face_num_verts[face_count + k] = commands[i].f_num_verts[k]; + } + + f_count += commands[i].num_f; + face_count += commands[i].num_f_num_verts; + } + } + } + + /* 5. Construct shape information. */ + { + unsigned int face_count = 0; + unsigned int i = 0; + unsigned int n = 0; + unsigned int shape_idx = 0; + + const char *shape_name = NULL; + unsigned int shape_name_len = 0; + const char *prev_shape_name = NULL; + unsigned int prev_shape_name_len = 0; + unsigned int prev_shape_face_offset = 0; + unsigned int prev_face_offset = 0; + tinyobj_shape_t prev_shape = {NULL, 0, 0}; + + /* Find the number of shapes in .obj */ + for (i = 0; i < num_lines; i++) { + if (commands[i].type == COMMAND_O || commands[i].type == COMMAND_G) { + n++; + } + } + + /* Allocate array of shapes with maximum possible size(+1 for unnamed + * group/object). + * Actual # of shapes found in .obj is determined in the later */ + (*shapes) = (tinyobj_shape_t*)TINYOBJ_MALLOC(sizeof(tinyobj_shape_t) * (n + 1)); + + for (i = 0; i < num_lines; i++) { + if (commands[i].type == COMMAND_O || commands[i].type == COMMAND_G) { + if (commands[i].type == COMMAND_O) { + shape_name = commands[i].object_name; + shape_name_len = commands[i].object_name_len; + } else { + shape_name = commands[i].group_name; + shape_name_len = commands[i].group_name_len; + } + + if (face_count == 0) { + /* 'o' or 'g' appears before any 'f' */ + prev_shape_name = shape_name; + prev_shape_name_len = shape_name_len; + prev_shape_face_offset = face_count; + prev_face_offset = face_count; + } else { + if (shape_idx == 0) { + /* 'o' or 'g' after some 'v' lines. */ + (*shapes)[shape_idx].name = my_strndup( + prev_shape_name, prev_shape_name_len); /* may be NULL */ + (*shapes)[shape_idx].face_offset = prev_shape.face_offset; + (*shapes)[shape_idx].length = face_count - prev_face_offset; + shape_idx++; + + prev_face_offset = face_count; + + } else { + if ((face_count - prev_face_offset) > 0) { + (*shapes)[shape_idx].name = + my_strndup(prev_shape_name, prev_shape_name_len); + (*shapes)[shape_idx].face_offset = prev_face_offset; + (*shapes)[shape_idx].length = face_count - prev_face_offset; + shape_idx++; + prev_face_offset = face_count; + } + } + + /* Record shape info for succeeding 'o' or 'g' command. */ + prev_shape_name = shape_name; + prev_shape_name_len = shape_name_len; + prev_shape_face_offset = face_count; + } + } + if (commands[i].type == COMMAND_F) { + face_count++; + } + } + + if ((face_count - prev_face_offset) > 0) { + unsigned int length = face_count - prev_shape_face_offset; + if (length > 0) { + (*shapes)[shape_idx].name = + my_strndup(prev_shape_name, prev_shape_name_len); + (*shapes)[shape_idx].face_offset = prev_face_offset; + (*shapes)[shape_idx].length = face_count - prev_face_offset; + shape_idx++; + } + } else { + /* Guess no 'v' line occurrence after 'o' or 'g', so discards current + * shape information. */ + } + + (*num_shapes) = shape_idx; + } + + if (commands) { + TINYOBJ_FREE(commands); + } + + destroy_hash_table(&material_table); + + (*materials_out) = materials; + (*num_materials_out) = num_materials; + + return TINYOBJ_SUCCESS; +} + +void tinyobj_attrib_init(tinyobj_attrib_t *attrib) { + attrib->vertices = NULL; + attrib->num_vertices = 0; + attrib->normals = NULL; + attrib->num_normals = 0; + attrib->texcoords = NULL; + attrib->num_texcoords = 0; + attrib->faces = NULL; + attrib->num_faces = 0; + attrib->face_num_verts = NULL; + attrib->num_face_num_verts = 0; + attrib->material_ids = NULL; +} + +void tinyobj_attrib_free(tinyobj_attrib_t *attrib) { + if (attrib->vertices) TINYOBJ_FREE(attrib->vertices); + if (attrib->normals) TINYOBJ_FREE(attrib->normals); + if (attrib->texcoords) TINYOBJ_FREE(attrib->texcoords); + if (attrib->faces) TINYOBJ_FREE(attrib->faces); + if (attrib->face_num_verts) TINYOBJ_FREE(attrib->face_num_verts); + if (attrib->material_ids) TINYOBJ_FREE(attrib->material_ids); +} + +void tinyobj_shapes_free(tinyobj_shape_t *shapes, unsigned int num_shapes) { + unsigned int i; + if (shapes == NULL) return; + + for (i = 0; i < num_shapes; i++) { + if (shapes[i].name) TINYOBJ_FREE(shapes[i].name); + } + + TINYOBJ_FREE(shapes); +} + +void tinyobj_materials_free(tinyobj_material_t *materials, + unsigned int num_materials) { + unsigned int i; + if (materials == NULL) return; + + for (i = 0; i < num_materials; i++) { + if (materials[i].name) TINYOBJ_FREE(materials[i].name); + if (materials[i].ambient_texname) TINYOBJ_FREE(materials[i].ambient_texname); + if (materials[i].diffuse_texname) TINYOBJ_FREE(materials[i].diffuse_texname); + if (materials[i].specular_texname) TINYOBJ_FREE(materials[i].specular_texname); + if (materials[i].specular_highlight_texname) + TINYOBJ_FREE(materials[i].specular_highlight_texname); + if (materials[i].bump_texname) TINYOBJ_FREE(materials[i].bump_texname); + if (materials[i].displacement_texname) + TINYOBJ_FREE(materials[i].displacement_texname); + if (materials[i].alpha_texname) TINYOBJ_FREE(materials[i].alpha_texname); + } + + TINYOBJ_FREE(materials); +} +#endif /* TINYOBJ_LOADER_C_IMPLEMENTATION */ + +#endif /* TINOBJ_LOADER_C_H_ */ diff --git a/src/gestures.h b/src/gestures.h index a4546eb1b..367753330 100644 --- a/src/gestures.h +++ b/src/gestures.h @@ -24,7 +24,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * * 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. diff --git a/src/models.c b/src/models.c index a5dbf5e7c..631c49420 100644 --- a/src/models.c +++ b/src/models.c @@ -17,7 +17,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2013-2019 Ramon Santamaria (@raysan5) * * 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. @@ -52,9 +52,9 @@ #include "rlgl.h" // raylib OpenGL abstraction layer to OpenGL 1.1, 2.1, 3.3+ or ES2 -#if defined(SUPPORT_FILEFORMAT_IQM) - #define RIQM_IMPLEMENTATION - #include "external/riqm.h" // IQM file format loading +#if defined(SUPPORT_FILEFORMAT_OBJ) || defined(SUPPORT_FILEFORMAT_MTL) + #define TINYOBJ_LOADER_C_IMPLEMENTATION + #include "external/tinyobj_loader_c.h" // OBJ/MTL file formats loading #endif #if defined(SUPPORT_FILEFORMAT_GLTF) @@ -86,16 +86,13 @@ // Module specific Functions Declaration //---------------------------------------------------------------------------------- #if defined(SUPPORT_FILEFORMAT_OBJ) -static Mesh LoadOBJ(const char *fileName); // Load OBJ mesh data -#endif -#if defined(SUPPORT_FILEFORMAT_MTL) -static Material LoadMTL(const char *fileName); // Load MTL material data +static Model LoadOBJ(const char *fileName); // Load OBJ mesh data #endif #if defined(SUPPORT_FILEFORMAT_GLTF) -static Mesh LoadIQM(const char *fileName); // Load IQM mesh data +static Model LoadIQM(const char *fileName); // Load IQM mesh data #endif #if defined(SUPPORT_FILEFORMAT_GLTF) -static Mesh LoadGLTF(const char *fileName); // Load GLTF mesh data +static Model LoadGLTF(const char *fileName); // Load GLTF mesh data #endif //---------------------------------------------------------------------------------- @@ -298,6 +295,8 @@ void DrawCubeTexture(Texture2D texture, Vector3 position, float width, float hei float x = position.x; float y = position.y; float z = position.z; + + if (rlCheckBufferLimit(36)) rlglDraw(); rlEnableTexture(texture.id); @@ -360,6 +359,9 @@ void DrawSphere(Vector3 centerPos, float radius, Color color) // Draw sphere with extended parameters void DrawSphereEx(Vector3 centerPos, float radius, int rings, int slices, Color color) { + int numVertex = (rings + 2)*slices*6; + if (rlCheckBufferLimit(numVertex)) rlglDraw(); + rlPushMatrix(); // NOTE: Transformation is applied in inverse order (scale -> translate) rlTranslatef(centerPos.x, centerPos.y, centerPos.z); @@ -400,6 +402,9 @@ void DrawSphereEx(Vector3 centerPos, float radius, int rings, int slices, Color // Draw sphere wires void DrawSphereWires(Vector3 centerPos, float radius, int rings, int slices, Color color) { + int numVertex = (rings + 2)*slices*6; + if (rlCheckBufferLimit(numVertex)) rlglDraw(); + rlPushMatrix(); // NOTE: Transformation is applied in inverse order (scale -> translate) rlTranslatef(centerPos.x, centerPos.y, centerPos.z); @@ -443,6 +448,9 @@ void DrawSphereWires(Vector3 centerPos, float radius, int rings, int slices, Col void DrawCylinder(Vector3 position, float radiusTop, float radiusBottom, float height, int sides, Color color) { if (sides < 3) sides = 3; + + int numVertex = sides*6; + if (rlCheckBufferLimit(numVertex)) rlglDraw(); rlPushMatrix(); rlTranslatef(position.x, position.y, position.z); @@ -499,6 +507,9 @@ void DrawCylinder(Vector3 position, float radiusTop, float radiusBottom, float h void DrawCylinderWires(Vector3 position, float radiusTop, float radiusBottom, float height, int sides, Color color) { if (sides < 3) sides = 3; + + int numVertex = sides*8; + if (rlCheckBufferLimit(numVertex)) rlglDraw(); rlPushMatrix(); rlTranslatef(position.x, position.y, position.z); @@ -527,6 +538,8 @@ void DrawCylinderWires(Vector3 position, float radiusTop, float radiusBottom, fl // Draw a plane void DrawPlane(Vector3 centerPos, Vector2 size, Color color) { + if (rlCheckBufferLimit(4)) rlglDraw(); + // NOTE: Plane is always created on XZ ground rlPushMatrix(); rlTranslatef(centerPos.x, centerPos.y, centerPos.z); @@ -563,6 +576,8 @@ void DrawGrid(int slices, float spacing) { int halfSlices = slices/2; + if (rlCheckBufferLimit(slices*4)) rlglDraw(); + rlBegin(RL_LINES); for (int i = -halfSlices; i <= halfSlices; i++) { @@ -618,9 +633,43 @@ Model LoadModel(const char *fileName) { Model model = { 0 }; - model.mesh = LoadMesh(fileName); +#if defined(SUPPORT_FILEFORMAT_OBJ) + if (IsFileExtension(fileName, ".obj")) model = LoadOBJ(fileName); +#endif +#if defined(SUPPORT_FILEFORMAT_GLTF) + if (IsFileExtension(fileName, ".gltf")) model = LoadGLTF(fileName); +#endif +#if defined(SUPPORT_FILEFORMAT_IQM) + if (IsFileExtension(fileName, ".iqm")) model = LoadIQM(fileName); +#endif + + // Make sure model transform is set to identity matrix! model.transform = MatrixIdentity(); - model.material = LoadMaterialDefault(); + + if (model.meshCount == 0) + { + TraceLog(LOG_WARNING, "[%s] No meshes can be loaded, default to cube mesh", fileName); + + model.meshCount = 1; + model.meshes = (Mesh *)calloc(model.meshCount, sizeof(Mesh)); + model.meshes[0] = GenMeshCube(1.0f, 1.0f, 1.0f); + } + else + { + // Upload vertex data to GPU (static mesh) + for (int i = 0; i < model.meshCount; i++) rlLoadMesh(&model.meshes[i], false); + } + + if (model.materialCount == 0) + { + TraceLog(LOG_WARNING, "[%s] No materials can be loaded, default to white material", fileName); + + model.materialCount = 1; + model.materials = (Material *)calloc(model.materialCount, sizeof(Material)); + model.materials[0] = LoadMaterialDefault(); + + model.meshMaterial = (int *)calloc(model.meshCount, sizeof(int)); + } return model; } @@ -633,9 +682,18 @@ Model LoadModelFromMesh(Mesh mesh) { Model model = { 0 }; - model.mesh = mesh; model.transform = MatrixIdentity(); - model.material = LoadMaterialDefault(); + + model.meshCount = 1; + model.meshes = (Mesh *)malloc(model.meshCount*sizeof(Mesh)); + model.meshes[0] = mesh; + + model.materialCount = 1; + model.materials = (Material *)malloc(model.materialCount*sizeof(Material)); + model.materials[0] = LoadMaterialDefault(); + + model.meshMaterial = (int *)malloc(model.meshCount*sizeof(int)); + model.meshMaterial[0] = 0; // First material index return model; } @@ -643,36 +701,30 @@ Model LoadModelFromMesh(Mesh mesh) // Unload model from memory (RAM and/or VRAM) void UnloadModel(Model model) { - UnloadMesh(&model.mesh); - UnloadMaterial(model.material); + for (int i = 0; i < model.meshCount; i++) UnloadMesh(&model.meshes[i]); + for (int i = 0; i < model.materialCount; i++) UnloadMaterial(model.materials[i]); - TraceLog(LOG_INFO, "Unloaded model data (mesh and material) from RAM and VRAM"); + free(model.meshes); + free(model.materials); + free(model.meshMaterial); + + // Unload animation data + free(model.bones); + free(model.bindPose); + + TraceLog(LOG_INFO, "Unloaded model data from RAM and VRAM"); } -// Load mesh from file -// NOTE: Mesh data loaded in CPU and GPU -Mesh LoadMesh(const char *fileName) +// Load meshes from model file +Mesh *LoadMeshes(const char *fileName, int *meshCount) { - Mesh mesh = { 0 }; - -#if defined(SUPPORT_FILEFORMAT_OBJ) - if (IsFileExtension(fileName, ".obj")) mesh = LoadOBJ(fileName); -#else - TraceLog(LOG_WARNING, "[%s] Mesh fileformat not supported, it can't be loaded", fileName); -#endif - -#if defined(SUPPORT_MESH_GENERATION) - if (mesh.vertexCount == 0) - { - TraceLog(LOG_WARNING, "Mesh could not be loaded! Let's load a cube to replace it!"); - mesh = GenMeshCube(1.0f, 1.0f, 1.0f); - } - else rlLoadMesh(&mesh, false); // Upload vertex data to GPU (static mesh) -#else - rlLoadMesh(&mesh, false); // Upload vertex data to GPU (static mesh) -#endif - - return mesh; + Mesh *meshes = NULL; + int count = 0; + + // TODO: Load meshes from file (OBJ, IQM, GLTF) + + *meshCount = count; + return meshes; } // Unload mesh from memory (RAM and/or VRAM) @@ -737,6 +789,386 @@ void ExportMesh(Mesh mesh, const char *fileName) else TraceLog(LOG_WARNING, "Mesh could not be exported."); } +// Load materials from model file +Material *LoadMaterials(const char *fileName, int *materialCount) +{ + Material *materials = NULL; + unsigned int count = 0; + + // TODO: Support IQM and GLTF for materials parsing + +#if defined(SUPPORT_FILEFORMAT_MTL) + if (IsFileExtension(fileName, ".mtl")) + { + tinyobj_material_t *mats; + + int result = tinyobj_parse_mtl_file(&mats, &count, fileName); + + // TODO: Process materials to return + + tinyobj_materials_free(mats, count); + } +#else + TraceLog(LOG_WARNING, "[%s] Materials file not supported", fileName); +#endif + + // Set materials shader to default (DIFFUSE, SPECULAR, NORMAL) + for (int i = 0; i < count; i++) materials[i].shader = GetShaderDefault(); + + *materialCount = count; + return materials; +} + +// Load default material (Supports: DIFFUSE, SPECULAR, NORMAL maps) +Material LoadMaterialDefault(void) +{ + Material material = { 0 }; + + material.shader = GetShaderDefault(); + material.maps[MAP_DIFFUSE].texture = GetTextureDefault(); // White texture (1x1 pixel) + //material.maps[MAP_NORMAL].texture; // NOTE: By default, not set + //material.maps[MAP_SPECULAR].texture; // NOTE: By default, not set + + material.maps[MAP_DIFFUSE].color = WHITE; // Diffuse color + material.maps[MAP_SPECULAR].color = WHITE; // Specular color + + return material; +} + +// Unload material from memory +void UnloadMaterial(Material material) +{ + // Unload material shader (avoid unloading default shader, managed by raylib) + if (material.shader.id != GetShaderDefault().id) UnloadShader(material.shader); + + // Unload loaded texture maps (avoid unloading default texture, managed by raylib) + for (int i = 0; i < MAX_MATERIAL_MAPS; i++) + { + if (material.maps[i].texture.id != GetTextureDefault().id) rlDeleteTextures(material.maps[i].texture.id); + } +} + +// Set texture for a material map type (MAP_DIFFUSE, MAP_SPECULAR...) +// NOTE: Previous texture should be manually unloaded +void SetMaterialTexture(Material *material, int mapType, Texture2D texture) +{ + material->maps[mapType].texture = texture; +} + +// Set the material for a mesh +void SetModelMeshMaterial(Model *model, int meshId, int materialId) +{ + if (meshId >= model->meshCount) TraceLog(LOG_WARNING, "Mesh id greater than mesh count"); + else if (materialId >= model->materialCount) TraceLog(LOG_WARNING,"Material id greater than material count"); + else model->meshMaterial[meshId] = materialId; +} + +// Load model animations from file +ModelAnimation *LoadModelAnimations(const char *filename, int *animCount) +{ + ModelAnimation *animations = (ModelAnimation *)malloc(1*sizeof(ModelAnimation)); + int count = 1; + + #define IQM_MAGIC "INTERQUAKEMODEL" // IQM file magic number + #define IQM_VERSION 2 // only IQM version 2 supported + + typedef struct IQMHeader { + char magic[16]; + unsigned int version; + unsigned int filesize; + unsigned int flags; + unsigned int num_text, ofs_text; + unsigned int num_meshes, ofs_meshes; + unsigned int num_vertexarrays, num_vertexes, ofs_vertexarrays; + unsigned int num_triangles, ofs_triangles, ofs_adjacency; + unsigned int num_joints, ofs_joints; + unsigned int num_poses, ofs_poses; + unsigned int num_anims, ofs_anims; + unsigned int num_frames, num_framechannels, ofs_frames, ofs_bounds; + unsigned int num_comment, ofs_comment; + unsigned int num_extensions, ofs_extensions; + } IQMHeader; + + typedef struct IQMPose { + int parent; + unsigned int mask; + float channeloffset[10]; + float channelscale[10]; + } IQMPose; + + typedef struct IQMAnim { + unsigned int name; + unsigned int first_frame, num_frames; + float framerate; + unsigned int flags; + } IQMAnim; + + ModelAnimation animation = { 0 }; + + FILE *iqmFile; + IQMHeader iqm; + + iqmFile = fopen(filename,"rb"); + + if (!iqmFile) + { + TraceLog(LOG_ERROR, "[%s] Unable to open file", filename); + } + + // header + fread(&iqm, sizeof(IQMHeader), 1, iqmFile); + + if (strncmp(iqm.magic, IQM_MAGIC, sizeof(IQM_MAGIC))) + { + TraceLog(LOG_ERROR, "Magic Number \"%s\"does not match.", iqm.magic); + fclose(iqmFile); + } + + if (iqm.version != IQM_VERSION) + { + TraceLog(LOG_ERROR, "IQM version %i is incorrect.", iqm.version); + fclose(iqmFile); + } + + // header + if (iqm.num_anims > 1) TraceLog(LOG_WARNING, "More than 1 animation in file, only the first one will be loaded"); + + // bones + IQMPose *poses; + poses = malloc(sizeof(IQMPose)*iqm.num_poses); + fseek(iqmFile, iqm.ofs_poses, SEEK_SET); + fread(poses, sizeof(IQMPose)*iqm.num_poses, 1, iqmFile); + + animation.boneCount = iqm.num_poses; + animation.bones = malloc(sizeof(BoneInfo)*iqm.num_poses); + + for (int j = 0; j < iqm.num_poses; j++) + { + strcpy(animation.bones[j].name, "ANIMJOINTNAME"); + animation.bones[j].parent = poses[j].parent; + } + + // animations + IQMAnim anim = {0}; + fseek(iqmFile, iqm.ofs_anims, SEEK_SET); + fread(&anim, sizeof(IQMAnim), 1, iqmFile); + + animation.frameCount = anim.num_frames; + //animation.framerate = anim.framerate; + + // frameposes + unsigned short *framedata = malloc(sizeof(unsigned short)*iqm.num_frames*iqm.num_framechannels); + fseek(iqmFile, iqm.ofs_frames, SEEK_SET); + fread(framedata, sizeof(unsigned short)*iqm.num_frames*iqm.num_framechannels, 1, iqmFile); + + animation.framePoses = malloc(sizeof(Transform*)*anim.num_frames); + for (int j = 0; j < anim.num_frames; j++) animation.framePoses[j] = malloc(sizeof(Transform)*iqm.num_poses); + + int dcounter = anim.first_frame*iqm.num_framechannels; + + for (int frame = 0; frame < anim.num_frames; frame++) + { + for (int i = 0; i < iqm.num_poses; i++) + { + animation.framePoses[frame][i].translation.x = poses[i].channeloffset[0]; + + if (poses[i].mask & 0x01) + { + animation.framePoses[frame][i].translation.x += framedata[dcounter]*poses[i].channelscale[0]; + dcounter++; + } + + animation.framePoses[frame][i].translation.y = poses[i].channeloffset[1]; + + if (poses[i].mask & 0x02) + { + animation.framePoses[frame][i].translation.y += framedata[dcounter]*poses[i].channelscale[1]; + dcounter++; + } + + animation.framePoses[frame][i].translation.z = poses[i].channeloffset[2]; + + if (poses[i].mask & 0x04) + { + animation.framePoses[frame][i].translation.z += framedata[dcounter]*poses[i].channelscale[2]; + dcounter++; + } + + animation.framePoses[frame][i].rotation.x = poses[i].channeloffset[3]; + + if (poses[i].mask & 0x08) + { + animation.framePoses[frame][i].rotation.x += framedata[dcounter]*poses[i].channelscale[3]; + dcounter++; + } + + animation.framePoses[frame][i].rotation.y = poses[i].channeloffset[4]; + + if (poses[i].mask & 0x10) + { + animation.framePoses[frame][i].rotation.y += framedata[dcounter]*poses[i].channelscale[4]; + dcounter++; + } + + animation.framePoses[frame][i].rotation.z = poses[i].channeloffset[5]; + + if (poses[i].mask & 0x20) + { + animation.framePoses[frame][i].rotation.z += framedata[dcounter]*poses[i].channelscale[5]; + dcounter++; + } + + animation.framePoses[frame][i].rotation.w = poses[i].channeloffset[6]; + + if (poses[i].mask & 0x40) + { + animation.framePoses[frame][i].rotation.w += framedata[dcounter]*poses[i].channelscale[6]; + dcounter++; + } + + animation.framePoses[frame][i].scale.x = poses[i].channeloffset[7]; + + if (poses[i].mask & 0x80) + { + animation.framePoses[frame][i].scale.x += framedata[dcounter]*poses[i].channelscale[7]; + dcounter++; + } + + animation.framePoses[frame][i].scale.y = poses[i].channeloffset[8]; + + if (poses[i].mask & 0x100) + { + animation.framePoses[frame][i].scale.y += framedata[dcounter]*poses[i].channelscale[8]; + dcounter++; + } + + animation.framePoses[frame][i].scale.z = poses[i].channeloffset[9]; + + if (poses[i].mask & 0x200) + { + animation.framePoses[frame][i].scale.z += framedata[dcounter]*poses[i].channelscale[9]; + dcounter++; + } + + animation.framePoses[frame][i].rotation = QuaternionNormalize(animation.framePoses[frame][i].rotation); + } + } + + // Build frameposes + for (int frame = 0; frame < anim.num_frames; frame++) + { + for (int i = 0; i < animation.boneCount; i++) + { + if (animation.bones[i].parent >= 0) + { + animation.framePoses[frame][i].rotation = QuaternionMultiply(animation.framePoses[frame][animation.bones[i].parent].rotation, animation.framePoses[frame][i].rotation); + animation.framePoses[frame][i].translation = Vector3RotateByQuaternion(animation.framePoses[frame][i].translation, animation.framePoses[frame][animation.bones[i].parent].rotation); + animation.framePoses[frame][i].translation = Vector3Add(animation.framePoses[frame][i].translation, animation.framePoses[frame][animation.bones[i].parent].translation); + animation.framePoses[frame][i].scale = Vector3MultiplyV(animation.framePoses[frame][i].scale, animation.framePoses[frame][animation.bones[i].parent].scale); + } + } + } + + free(framedata); + free(poses); + + fclose(iqmFile); + + animations[0] = animation; + + *animCount = count; + return animations; +} + +// Update model animated vertex data (positions and normals) for a given frame +// NOTE: Updated data is uploaded to GPU +void UpdateModelAnimation(Model model, ModelAnimation anim, int frame) +{ + if (frame >= anim.frameCount) frame = frame%anim.frameCount; + + for (int m = 0; m < model.meshCount; m++) + { + Vector3 animVertex = { 0 }; + Vector3 animNormal = { 0 }; + + Vector3 inTranslation = { 0 }; + Quaternion inRotation = { 0 }; + Vector3 inScale = { 0 }; + + Vector3 outTranslation = { 0 }; + Quaternion outRotation = { 0 }; + Vector3 outScale = { 0 }; + + int vCounter = 0; + int boneCounter = 0; + int boneId = 0; + + for (int i = 0; i < model.meshes[m].vertexCount; i++) + { + boneId = model.meshes[m].boneIds[boneCounter]; + inTranslation = model.bindPose[boneId].translation; + inRotation = model.bindPose[boneId].rotation; + inScale = model.bindPose[boneId].scale; + outTranslation = anim.framePoses[frame][boneId].translation; + outRotation = anim.framePoses[frame][boneId].rotation; + outScale = anim.framePoses[frame][boneId].scale; + + // Vertices processing + // NOTE: We use meshes.vertices (default vertex position) to calculate meshes.animVertices (animated vertex position) + animVertex = (Vector3){ model.meshes[m].vertices[vCounter], model.meshes[m].vertices[vCounter + 1], model.meshes[m].vertices[vCounter + 2] }; + animVertex = Vector3MultiplyV(animVertex, outScale); + animVertex = Vector3Subtract(animVertex, inTranslation); + animVertex = Vector3RotateByQuaternion(animVertex, QuaternionMultiply(outRotation, QuaternionInvert(inRotation))); + animVertex = Vector3Add(animVertex, outTranslation); + model.meshes[m].animVertices[vCounter] = animVertex.x; + model.meshes[m].animVertices[vCounter + 1] = animVertex.y; + model.meshes[m].animVertices[vCounter + 2] = animVertex.z; + + // Normals processing + // NOTE: We use meshes.baseNormals (default normal) to calculate meshes.normals (animated normals) + animNormal = (Vector3){ model.meshes[m].normals[vCounter], model.meshes[m].normals[vCounter + 1], model.meshes[m].normals[vCounter + 2] }; + animNormal = Vector3RotateByQuaternion(animNormal, QuaternionMultiply(outRotation, QuaternionInvert(inRotation))); + model.meshes[m].animNormals[vCounter] = animNormal.x; + model.meshes[m].animNormals[vCounter + 1] = animNormal.y; + model.meshes[m].animNormals[vCounter + 2] = animNormal.z; + vCounter += 3; + + boneCounter += 4; + } + + // Upload new vertex data to GPU for model drawing + rlUpdateBuffer(model.meshes[m].vboId[0], model.meshes[m].animVertices, model.meshes[m].vertexCount*3*sizeof(float)); // Update vertex position + rlUpdateBuffer(model.meshes[m].vboId[2], model.meshes[m].animVertices, model.meshes[m].vertexCount*3*sizeof(float)); // Update vertex normals + } +} + +// Unload animation data +void UnloadModelAnimation(ModelAnimation anim) +{ + for (int i = 0; i < anim.frameCount; i++) free(anim.framePoses[i]); + + free(anim.bones); + free(anim.framePoses); +} + +// Check model animation skeleton match +// NOTE: Only number of bones and parent connections are checked +bool IsModelAnimationValid(Model model, ModelAnimation anim) +{ + int result = true; + + if (model.boneCount != anim.boneCount) result = false; + else + { + for (int i = 0; i < model.boneCount; i++) + { + if (model.bones[i].parent != anim.bones[i].parent) { result = false; break; } + } + } + + return result; +} + #if defined(SUPPORT_MESH_GENERATION) // Generate polygonal mesh Mesh GenMeshPoly(int sides, float radius) @@ -1785,49 +2217,127 @@ Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize) } #endif // SUPPORT_MESH_GENERATION -// Load material data (from file) -Material LoadMaterial(const char *fileName) +// Compute mesh bounding box limits +// NOTE: minVertex and maxVertex should be transformed by model transform matrix +BoundingBox MeshBoundingBox(Mesh mesh) { - Material material = { 0 }; + // Get min and max vertex to construct bounds (AABB) + Vector3 minVertex = { 0 }; + Vector3 maxVertex = { 0 }; -#if defined(SUPPORT_FILEFORMAT_MTL) - if (IsFileExtension(fileName, ".mtl")) material = LoadMTL(fileName); -#else - TraceLog(LOG_WARNING, "[%s] Material fileformat not supported, it can't be loaded", fileName); -#endif - - // Our material uses the default shader (DIFFUSE, SPECULAR, NORMAL) - material.shader = GetShaderDefault(); - - return material; -} - -// Load default material (Supports: DIFFUSE, SPECULAR, NORMAL maps) -Material LoadMaterialDefault(void) -{ - Material material = { 0 }; - - material.shader = GetShaderDefault(); - material.maps[MAP_DIFFUSE].texture = GetTextureDefault(); // White texture (1x1 pixel) - //material.maps[MAP_NORMAL].texture; // NOTE: By default, not set - //material.maps[MAP_SPECULAR].texture; // NOTE: By default, not set - - material.maps[MAP_DIFFUSE].color = WHITE; // Diffuse color - material.maps[MAP_SPECULAR].color = WHITE; // Specular color - - return material; -} - -// Unload material from memory -void UnloadMaterial(Material material) -{ - // Unload material shader (avoid unloading default shader, managed by raylib) - if (material.shader.id != GetShaderDefault().id) UnloadShader(material.shader); - - // Unload loaded texture maps (avoid unloading default texture, managed by raylib) - for (int i = 0; i < MAX_MATERIAL_MAPS; i++) + if (mesh.vertices != NULL) { - if (material.maps[i].texture.id != GetTextureDefault().id) rlDeleteTextures(material.maps[i].texture.id); + minVertex = (Vector3){ mesh.vertices[0], mesh.vertices[1], mesh.vertices[2] }; + maxVertex = (Vector3){ mesh.vertices[0], mesh.vertices[1], mesh.vertices[2] }; + + for (int i = 1; i < mesh.vertexCount; i++) + { + minVertex = Vector3Min(minVertex, (Vector3){ mesh.vertices[i*3], mesh.vertices[i*3 + 1], mesh.vertices[i*3 + 2] }); + maxVertex = Vector3Max(maxVertex, (Vector3){ mesh.vertices[i*3], mesh.vertices[i*3 + 1], mesh.vertices[i*3 + 2] }); + } + } + + // Create the bounding box + BoundingBox box = { 0 }; + box.min = minVertex; + box.max = maxVertex; + + return box; +} + +// Compute mesh tangents +// NOTE: To calculate mesh tangents and binormals we need mesh vertex positions and texture coordinates +// Implementation base don: https://answers.unity.com/questions/7789/calculating-tangents-vector4.html +void MeshTangents(Mesh *mesh) +{ + if (mesh->tangents == NULL) mesh->tangents = (float *)malloc(mesh->vertexCount*4*sizeof(float)); + else TraceLog(LOG_WARNING, "Mesh tangents already exist"); + + Vector3 *tan1 = (Vector3 *)malloc(mesh->vertexCount*sizeof(Vector3)); + Vector3 *tan2 = (Vector3 *)malloc(mesh->vertexCount*sizeof(Vector3)); + + for (int i = 0; i < mesh->vertexCount; i += 3) + { + // Get triangle vertices + Vector3 v1 = { mesh->vertices[(i + 0)*3 + 0], mesh->vertices[(i + 0)*3 + 1], mesh->vertices[(i + 0)*3 + 2] }; + Vector3 v2 = { mesh->vertices[(i + 1)*3 + 0], mesh->vertices[(i + 1)*3 + 1], mesh->vertices[(i + 1)*3 + 2] }; + Vector3 v3 = { mesh->vertices[(i + 2)*3 + 0], mesh->vertices[(i + 2)*3 + 1], mesh->vertices[(i + 2)*3 + 2] }; + + // Get triangle texcoords + Vector2 uv1 = { mesh->texcoords[(i + 0)*2 + 0], mesh->texcoords[(i + 0)*2 + 1] }; + Vector2 uv2 = { mesh->texcoords[(i + 1)*2 + 0], mesh->texcoords[(i + 1)*2 + 1] }; + Vector2 uv3 = { mesh->texcoords[(i + 2)*2 + 0], mesh->texcoords[(i + 2)*2 + 1] }; + + float x1 = v2.x - v1.x; + float y1 = v2.y - v1.y; + float z1 = v2.z - v1.z; + float x2 = v3.x - v1.x; + float y2 = v3.y - v1.y; + float z2 = v3.z - v1.z; + + float s1 = uv2.x - uv1.x; + float t1 = uv2.y - uv1.y; + float s2 = uv3.x - uv1.x; + float t2 = uv3.y - uv1.y; + + float div = s1*t2 - s2*t1; + float r = (div == 0.0f)? 0.0f : 1.0f/div; + + Vector3 sdir = { (t2*x1 - t1*x2)*r, (t2*y1 - t1*y2)*r, (t2*z1 - t1*z2)*r }; + Vector3 tdir = { (s1*x2 - s2*x1)*r, (s1*y2 - s2*y1)*r, (s1*z2 - s2*z1)*r }; + + tan1[i + 0] = sdir; + tan1[i + 1] = sdir; + tan1[i + 2] = sdir; + + tan2[i + 0] = tdir; + tan2[i + 1] = tdir; + tan2[i + 2] = tdir; + } + + // Compute tangents considering normals + for (int i = 0; i < mesh->vertexCount; ++i) + { + Vector3 normal = { mesh->normals[i*3 + 0], mesh->normals[i*3 + 1], mesh->normals[i*3 + 2] }; + Vector3 tangent = tan1[i]; + + // TODO: Review, not sure if tangent computation is right, just used reference proposed maths... + #if defined(COMPUTE_TANGENTS_METHOD_01) + Vector3 tmp = Vector3Subtract(tangent, Vector3Multiply(normal, Vector3DotProduct(normal, tangent))); + tmp = Vector3Normalize(tmp); + mesh->tangents[i*4 + 0] = tmp.x; + mesh->tangents[i*4 + 1] = tmp.y; + mesh->tangents[i*4 + 2] = tmp.z; + mesh->tangents[i*4 + 3] = 1.0f; + #else + Vector3OrthoNormalize(&normal, &tangent); + mesh->tangents[i*4 + 0] = tangent.x; + mesh->tangents[i*4 + 1] = tangent.y; + mesh->tangents[i*4 + 2] = tangent.z; + mesh->tangents[i*4 + 3] = (Vector3DotProduct(Vector3CrossProduct(normal, tangent), tan2[i]) < 0.0f)? -1.0f : 1.0f; + #endif + } + + free(tan1); + free(tan2); + + // Load a new tangent attributes buffer + mesh->vboId[LOC_VERTEX_TANGENT] = rlLoadAttribBuffer(mesh->vaoId, LOC_VERTEX_TANGENT, mesh->tangents, mesh->vertexCount*4*sizeof(float), false); + + TraceLog(LOG_INFO, "Tangents computed for mesh"); +} + +// Compute mesh binormals (aka bitangent) +void MeshBinormals(Mesh *mesh) +{ + for (int i = 0; i < mesh->vertexCount; i++) + { + Vector3 normal = { mesh->normals[i*3 + 0], mesh->normals[i*3 + 1], mesh->normals[i*3 + 2] }; + Vector3 tangent = { mesh->tangents[i*4 + 0], mesh->tangents[i*4 + 1], mesh->tangents[i*4 + 2] }; + float tangentW = mesh->tangents[i*4 + 3]; + + // TODO: Register computed binormal in mesh->binormal? + // Vector3 binormal = Vector3Multiply(Vector3CrossProduct(normal, tangent), tangentW); } } @@ -1835,7 +2345,7 @@ void UnloadMaterial(Material material) void DrawModel(Model model, Vector3 position, float scale, Color tint) { Vector3 vScale = { scale, scale, scale }; - Vector3 rotationAxis = { 0.0f, 0.0f, 0.0f }; + Vector3 rotationAxis = { 0.0f, 1.0f, 0.0f }; DrawModelEx(model, position, rotationAxis, 0.0f, vScale, tint); } @@ -1852,12 +2362,13 @@ void DrawModelEx(Model model, Vector3 position, Vector3 rotationAxis, float rota Matrix matTransform = MatrixMultiply(MatrixMultiply(matScale, matRotation), matTranslation); // Combine model transformation matrix (model.transform) with matrix generated by function parameters (matTransform) - //Matrix matModel = MatrixMultiply(model.transform, matTransform); // Transform to world-space coordinates - model.transform = MatrixMultiply(model.transform, matTransform); - model.material.maps[MAP_DIFFUSE].color = tint; // TODO: Multiply tint color by diffuse color? - rlDrawMesh(model.mesh, model.material, model.transform); + for (int i = 0; i < model.meshCount; i++) + { + model.materials[model.meshMaterial[i]].maps[MAP_DIFFUSE].color = tint; + rlDrawMesh(model.meshes[i], model.materials[model.meshMaterial[i]], model.transform); + } } // Draw a model wires (with texture if set) @@ -2079,41 +2590,45 @@ RayHitInfo GetCollisionRayModel(Ray ray, Model *model) { RayHitInfo result = { 0 }; - // If mesh doesn't have vertex data on CPU, can't test it. - if (!model->mesh.vertices) return result; - - // model->mesh.triangleCount may not be set, vertexCount is more reliable - int triangleCount = model->mesh.vertexCount/3; - - // Test against all triangles in mesh - for (int i = 0; i < triangleCount; i++) + for (int m = 0; m < model->meshCount; m++) { - Vector3 a, b, c; - Vector3 *vertdata = (Vector3 *)model->mesh.vertices; - - if (model->mesh.indices) + // Check if meshhas vertex data on CPU for testing + if (model->meshes[m].vertices != NULL) { - a = vertdata[model->mesh.indices[i*3 + 0]]; - b = vertdata[model->mesh.indices[i*3 + 1]]; - c = vertdata[model->mesh.indices[i*3 + 2]]; - } - else - { - a = vertdata[i*3 + 0]; - b = vertdata[i*3 + 1]; - c = vertdata[i*3 + 2]; - } + // model->mesh.triangleCount may not be set, vertexCount is more reliable + int triangleCount = model->meshes[m].vertexCount/3; - a = Vector3Transform(a, model->transform); - b = Vector3Transform(b, model->transform); - c = Vector3Transform(c, model->transform); + // Test against all triangles in mesh + for (int i = 0; i < triangleCount; i++) + { + Vector3 a, b, c; + Vector3 *vertdata = (Vector3 *)model->meshes[m].vertices; - RayHitInfo triHitInfo = GetCollisionRayTriangle(ray, a, b, c); + if (model->meshes[m].indices) + { + a = vertdata[model->meshes[m].indices[i*3 + 0]]; + b = vertdata[model->meshes[m].indices[i*3 + 1]]; + c = vertdata[model->meshes[m].indices[i*3 + 2]]; + } + else + { + a = vertdata[i*3 + 0]; + b = vertdata[i*3 + 1]; + c = vertdata[i*3 + 2]; + } - if (triHitInfo.hit) - { - // Save the closest hit triangle - if ((!result.hit) || (result.distance > triHitInfo.distance)) result = triHitInfo; + a = Vector3Transform(a, model->transform); + b = Vector3Transform(b, model->transform); + c = Vector3Transform(c, model->transform); + + RayHitInfo triHitInfo = GetCollisionRayTriangle(ray, a, b, c); + + if (triHitInfo.hit) + { + // Save the closest hit triangle + if ((!result.hit) || (result.distance > triHitInfo.distance)) result = triHitInfo; + } + } } } @@ -2202,562 +2717,524 @@ RayHitInfo GetCollisionRayGround(Ray ray, float groundHeight) return result; } -// Compute mesh bounding box limits -// NOTE: minVertex and maxVertex should be transformed by model transform matrix -BoundingBox MeshBoundingBox(Mesh mesh) -{ - // Get min and max vertex to construct bounds (AABB) - Vector3 minVertex = { 0 }; - Vector3 maxVertex = { 0 }; - - if (mesh.vertices != NULL) - { - minVertex = (Vector3){ mesh.vertices[0], mesh.vertices[1], mesh.vertices[2] }; - maxVertex = (Vector3){ mesh.vertices[0], mesh.vertices[1], mesh.vertices[2] }; - - for (int i = 1; i < mesh.vertexCount; i++) - { - minVertex = Vector3Min(minVertex, (Vector3){ mesh.vertices[i*3], mesh.vertices[i*3 + 1], mesh.vertices[i*3 + 2] }); - maxVertex = Vector3Max(maxVertex, (Vector3){ mesh.vertices[i*3], mesh.vertices[i*3 + 1], mesh.vertices[i*3 + 2] }); - } - } - - // Create the bounding box - BoundingBox box; - box.min = minVertex; - box.max = maxVertex; - - return box; -} - -// Compute mesh tangents -// NOTE: To calculate mesh tangents and binormals we need mesh vertex positions and texture coordinates -// Implementation base don: https://answers.unity.com/questions/7789/calculating-tangents-vector4.html -void MeshTangents(Mesh *mesh) -{ - if (mesh->tangents == NULL) mesh->tangents = (float *)malloc(mesh->vertexCount*4*sizeof(float)); - else TraceLog(LOG_WARNING, "Mesh tangents already exist"); - - Vector3 *tan1 = (Vector3 *)malloc(mesh->vertexCount*sizeof(Vector3)); - Vector3 *tan2 = (Vector3 *)malloc(mesh->vertexCount*sizeof(Vector3)); - - for (int i = 0; i < mesh->vertexCount; i += 3) - { - // Get triangle vertices - Vector3 v1 = { mesh->vertices[(i + 0)*3 + 0], mesh->vertices[(i + 0)*3 + 1], mesh->vertices[(i + 0)*3 + 2] }; - Vector3 v2 = { mesh->vertices[(i + 1)*3 + 0], mesh->vertices[(i + 1)*3 + 1], mesh->vertices[(i + 1)*3 + 2] }; - Vector3 v3 = { mesh->vertices[(i + 2)*3 + 0], mesh->vertices[(i + 2)*3 + 1], mesh->vertices[(i + 2)*3 + 2] }; - - // Get triangle texcoords - Vector2 uv1 = { mesh->texcoords[(i + 0)*2 + 0], mesh->texcoords[(i + 0)*2 + 1] }; - Vector2 uv2 = { mesh->texcoords[(i + 1)*2 + 0], mesh->texcoords[(i + 1)*2 + 1] }; - Vector2 uv3 = { mesh->texcoords[(i + 2)*2 + 0], mesh->texcoords[(i + 2)*2 + 1] }; - - float x1 = v2.x - v1.x; - float y1 = v2.y - v1.y; - float z1 = v2.z - v1.z; - float x2 = v3.x - v1.x; - float y2 = v3.y - v1.y; - float z2 = v3.z - v1.z; - - float s1 = uv2.x - uv1.x; - float t1 = uv2.y - uv1.y; - float s2 = uv3.x - uv1.x; - float t2 = uv3.y - uv1.y; - - float div = s1*t2 - s2*t1; - float r = (div == 0.0f)? 0.0f : 1.0f/div; - - Vector3 sdir = { (t2*x1 - t1*x2)*r, (t2*y1 - t1*y2)*r, (t2*z1 - t1*z2)*r }; - Vector3 tdir = { (s1*x2 - s2*x1)*r, (s1*y2 - s2*y1)*r, (s1*z2 - s2*z1)*r }; - - tan1[i + 0] = sdir; - tan1[i + 1] = sdir; - tan1[i + 2] = sdir; - - tan2[i + 0] = tdir; - tan2[i + 1] = tdir; - tan2[i + 2] = tdir; - } - - // Compute tangents considering normals - for (int i = 0; i < mesh->vertexCount; ++i) - { - Vector3 normal = { mesh->normals[i*3 + 0], mesh->normals[i*3 + 1], mesh->normals[i*3 + 2] }; - Vector3 tangent = tan1[i]; - - // TODO: Review, not sure if tangent computation is right, just used reference proposed maths... - #if defined(COMPUTE_TANGENTS_METHOD_01) - Vector3 tmp = Vector3Subtract(tangent, Vector3Multiply(normal, Vector3DotProduct(normal, tangent))); - tmp = Vector3Normalize(tmp); - mesh->tangents[i*4 + 0] = tmp.x; - mesh->tangents[i*4 + 1] = tmp.y; - mesh->tangents[i*4 + 2] = tmp.z; - mesh->tangents[i*4 + 3] = 1.0f; - #else - Vector3OrthoNormalize(&normal, &tangent); - mesh->tangents[i*4 + 0] = tangent.x; - mesh->tangents[i*4 + 1] = tangent.y; - mesh->tangents[i*4 + 2] = tangent.z; - mesh->tangents[i*4 + 3] = (Vector3DotProduct(Vector3CrossProduct(normal, tangent), tan2[i]) < 0.0f)? -1.0f : 1.0f; - #endif - } - - free(tan1); - free(tan2); - - TraceLog(LOG_INFO, "Tangents computed for mesh"); -} - -// Compute mesh binormals (aka bitangent) -void MeshBinormals(Mesh *mesh) -{ - for (int i = 0; i < mesh->vertexCount; i++) - { - Vector3 normal = { mesh->normals[i*3 + 0], mesh->normals[i*3 + 1], mesh->normals[i*3 + 2] }; - Vector3 tangent = { mesh->tangents[i*4 + 0], mesh->tangents[i*4 + 1], mesh->tangents[i*4 + 2] }; - float tangentW = mesh->tangents[i*4 + 3]; - - // TODO: Register computed binormal in mesh->binormal? - // Vector3 binormal = Vector3Multiply(Vector3CrossProduct(normal, tangent), tangentW); - } -} - //---------------------------------------------------------------------------------- // Module specific Functions Definition //---------------------------------------------------------------------------------- #if defined(SUPPORT_FILEFORMAT_OBJ) // Load OBJ mesh data -static Mesh LoadOBJ(const char *fileName) +static Model LoadOBJ(const char *fileName) { - Mesh mesh = { 0 }; + Model model = { 0 }; - char dataType = 0; - char comments[200]; + tinyobj_attrib_t attrib; + tinyobj_shape_t *meshes = NULL; + unsigned int meshCount = 0; - int vertexCount = 0; - int normalCount = 0; - int texcoordCount = 0; - int triangleCount = 0; + tinyobj_material_t *materials = NULL; + unsigned int materialCount = 0; - FILE *objFile; + int dataLength = 0; + char *data = NULL; - objFile = fopen(fileName, "rt"); + // Load model data + FILE *objFile = fopen(fileName, "rb"); - if (objFile == NULL) + if (objFile != NULL) { - TraceLog(LOG_WARNING, "[%s] OBJ file could not be opened", fileName); - return mesh; + fseek(objFile, 0, SEEK_END); + long length = ftell(objFile); // Get file size + fseek(objFile, 0, SEEK_SET); // Reset file pointer + + data = (char *)malloc(length); + + fread(data, length, 1, objFile); + dataLength = length; + fclose(objFile); } - // First reading pass: Get vertexCount, normalCount, texcoordCount, triangleCount - // NOTE: vertex, texcoords and normals could be optimized (to be used indexed on faces definition) - // NOTE: faces MUST be defined as TRIANGLES (3 vertex per face) - while (!feof(objFile)) + if (data != NULL) { - dataType = 0; - fscanf(objFile, "%c", &dataType); + unsigned int flags = TINYOBJ_FLAG_TRIANGULATE; + int ret = tinyobj_parse_obj(&attrib, &meshes, &meshCount, &materials, &materialCount, data, dataLength, flags); - switch (dataType) + if (ret != TINYOBJ_SUCCESS) TraceLog(LOG_WARNING, "[%s] Model data could not be loaded", fileName); + else TraceLog(LOG_INFO, "[%s] Model data loaded successfully: %i meshes / %i materials", fileName, meshCount, materialCount); + + // Init model meshes array + model.meshCount = meshCount; + model.meshes = (Mesh *)malloc(model.meshCount*sizeof(Mesh)); + + // Init model materials array + model.materialCount = materialCount; + model.materials = (Material *)malloc(model.materialCount*sizeof(Material)); + model.meshMaterial = (int *)calloc(model.meshCount, sizeof(int)); + + /* + // Multiple meshes data reference + // NOTE: They are provided as a faces offset + typedef struct { + char *name; // group name or object name + unsigned int face_offset; + unsigned int length; + } tinyobj_shape_t; + */ + + // Init model meshes + for (int m = 0; m < 1; m++) { - case '#': // Comments - case 'o': // Object name (One OBJ file can contain multible named meshes) - case 'g': // Group name - case 's': // Smoothing level - case 'm': // mtllib [external .mtl file name] - case 'u': // usemtl [material name] - { - fgets(comments, 200, objFile); - } break; - case 'v': - { - fscanf(objFile, "%c", &dataType); + Mesh mesh = { 0 }; + memset(&mesh, 0, sizeof(Mesh)); + mesh.vertexCount = attrib.num_faces*3; + mesh.triangleCount = attrib.num_faces; + mesh.vertices = (float *)malloc(mesh.vertexCount*3*sizeof(float)); + mesh.texcoords = (float *)malloc(mesh.vertexCount*2*sizeof(float)); + mesh.normals = (float *)malloc(mesh.vertexCount*3*sizeof(float)); - if (dataType == 't') // Read texCoord - { - texcoordCount++; - fgets(comments, 200, objFile); - } - else if (dataType == 'n') // Read normals - { - normalCount++; - fgets(comments, 200, objFile); - } - else // Read vertex - { - vertexCount++; - fgets(comments, 200, objFile); - } - } break; - case 'f': + int vCount = 0; + int vtCount = 0; + int vnCount = 0; + + for (int f = 0; f < attrib.num_faces; f++) { - triangleCount++; - fgets(comments, 200, objFile); - } break; - default: break; + // Get indices for the face + tinyobj_vertex_index_t idx0 = attrib.faces[3*f + 0]; + tinyobj_vertex_index_t idx1 = attrib.faces[3*f + 1]; + tinyobj_vertex_index_t idx2 = attrib.faces[3*f + 2]; + + // TraceLog(LOG_DEBUG, "Face %i index: v %i/%i/%i . vt %i/%i/%i . vn %i/%i/%i\n", f, idx0.v_idx, idx1.v_idx, idx2.v_idx, idx0.vt_idx, idx1.vt_idx, idx2.vt_idx, idx0.vn_idx, idx1.vn_idx, idx2.vn_idx); + + // Fill vertices buffer (float) using vertex index of the face + for (int v = 0; v < 3; v++) { mesh.vertices[vCount + v] = attrib.vertices[idx0.v_idx*3 + v]; } vCount +=3; + for (int v = 0; v < 3; v++) { mesh.vertices[vCount + v] = attrib.vertices[idx1.v_idx*3 + v]; } vCount +=3; + for (int v = 0; v < 3; v++) { mesh.vertices[vCount + v] = attrib.vertices[idx2.v_idx*3 + v]; } vCount +=3; + + // Fill texcoords buffer (float) using vertex index of the face + // NOTE: Y-coordinate must be flipped upside-down + mesh.texcoords[vtCount + 0] = attrib.texcoords[idx0.vt_idx*2 + 0]; + mesh.texcoords[vtCount + 1] = 1.0f - attrib.texcoords[idx0.vt_idx*2 + 1]; vtCount += 2; + mesh.texcoords[vtCount + 0] = attrib.texcoords[idx1.vt_idx*2 + 0]; + mesh.texcoords[vtCount + 1] = 1.0f - attrib.texcoords[idx1.vt_idx*2 + 1]; vtCount += 2; + mesh.texcoords[vtCount + 0] = attrib.texcoords[idx2.vt_idx*2 + 0]; + mesh.texcoords[vtCount + 1] = 1.0f - attrib.texcoords[idx2.vt_idx*2 + 1]; vtCount += 2; + + // Fill normals buffer (float) using vertex index of the face + for (int v = 0; v < 3; v++) { mesh.normals[vnCount + v] = attrib.normals[idx0.vn_idx*3 + v]; } vnCount +=3; + for (int v = 0; v < 3; v++) { mesh.normals[vnCount + v] = attrib.normals[idx1.vn_idx*3 + v]; } vnCount +=3; + for (int v = 0; v < 3; v++) { mesh.normals[vnCount + v] = attrib.normals[idx2.vn_idx*3 + v]; } vnCount +=3; + } + + model.meshes[m] = mesh; // Assign mesh data to model + + // Assign mesh material for current mesh + model.meshMaterial[m] = attrib.material_ids[m]; } - } - TraceLog(LOG_DEBUG, "[%s] Model vertices: %i", fileName, vertexCount); - TraceLog(LOG_DEBUG, "[%s] Model texcoords: %i", fileName, texcoordCount); - TraceLog(LOG_DEBUG, "[%s] Model normals: %i", fileName, normalCount); - TraceLog(LOG_DEBUG, "[%s] Model triangles: %i", fileName, triangleCount); - - // Once we know the number of vertices to store, we create required arrays - Vector3 *midVertices = (Vector3 *)malloc(vertexCount*sizeof(Vector3)); - Vector3 *midNormals = NULL; - if (normalCount > 0) midNormals = (Vector3 *)malloc(normalCount*sizeof(Vector3)); - Vector2 *midTexCoords = NULL; - if (texcoordCount > 0) midTexCoords = (Vector2 *)malloc(texcoordCount*sizeof(Vector2)); - - int countVertex = 0; - int countNormals = 0; - int countTexCoords = 0; - - rewind(objFile); // Return to the beginning of the file, to read again - - // Second reading pass: Get vertex data to fill intermediate arrays - // NOTE: This second pass is required in case of multiple meshes defined in same OBJ - // TODO: Consider that different meshes can have different vertex data available (position, texcoords, normals) - while (!feof(objFile)) - { - fscanf(objFile, "%c", &dataType); - - switch (dataType) + // Init model materials + for (int m = 0; m < materialCount; m++) { - case '#': case 'o': case 'g': case 's': case 'm': case 'u': case 'f': fgets(comments, 200, objFile); break; - case 'v': - { - fscanf(objFile, "%c", &dataType); + // Init material to default + // NOTE: Uses default shader, only MAP_DIFFUSE supported + model.materials[m] = LoadMaterialDefault(); - if (dataType == 't') // Read texCoord - { - fscanf(objFile, "%f %f%*[^\n]s\n", &midTexCoords[countTexCoords].x, &midTexCoords[countTexCoords].y); - countTexCoords++; + /* + typedef struct { + char *name; - fscanf(objFile, "%c", &dataType); - } - else if (dataType == 'n') // Read normals - { - fscanf(objFile, "%f %f %f", &midNormals[countNormals].x, &midNormals[countNormals].y, &midNormals[countNormals].z); - countNormals++; + float ambient[3]; + float diffuse[3]; + float specular[3]; + float transmittance[3]; + float emission[3]; + float shininess; + float ior; // index of refraction + float dissolve; // 1 == opaque; 0 == fully transparent + // illumination model (see http://www.fileformat.info/format/material/) + int illum; - fscanf(objFile, "%c", &dataType); - } - else // Read vertex - { - fscanf(objFile, "%f %f %f", &midVertices[countVertex].x, &midVertices[countVertex].y, &midVertices[countVertex].z); - countVertex++; + int pad0; - fscanf(objFile, "%c", &dataType); - } - } break; - default: break; + char *ambient_texname; // map_Ka + char *diffuse_texname; // map_Kd + char *specular_texname; // map_Ks + char *specular_highlight_texname; // map_Ns + char *bump_texname; // map_bump, bump + char *displacement_texname; // disp + char *alpha_texname; // map_d + } tinyobj_material_t; + */ + + model.materials[m].maps[MAP_DIFFUSE].texture = GetTextureDefault(); // Get default texture, in case no texture is defined + + if (materials[m].diffuse_texname != NULL) model.materials[m].maps[MAP_DIFFUSE].texture = LoadTexture(materials[m].diffuse_texname); //char *diffuse_texname; // map_Kd + model.materials[m].maps[MAP_DIFFUSE].color = (Color){ (float)(materials[m].diffuse[0]*255.0f), (float)(materials[m].diffuse[1]*255.0f), (float)(materials[m].diffuse[2]*255.0f), 255 }; //float diffuse[3]; + model.materials[m].maps[MAP_DIFFUSE].value = 0.0f; + + if (materials[m].specular_texname != NULL) model.materials[m].maps[MAP_SPECULAR].texture = LoadTexture(materials[m].specular_texname); //char *specular_texname; // map_Ks + model.materials[m].maps[MAP_SPECULAR].color = (Color){ (float)(materials[m].specular[0]*255.0f), (float)(materials[m].specular[1]*255.0f), (float)(materials[m].specular[2]*255.0f), 255 }; //float specular[3]; + model.materials[m].maps[MAP_SPECULAR].value = 0.0f; + + if (materials[m].bump_texname != NULL) model.materials[m].maps[MAP_NORMAL].texture = LoadTexture(materials[m].bump_texname); //char *bump_texname; // map_bump, bump + model.materials[m].maps[MAP_NORMAL].color = WHITE; + model.materials[m].maps[MAP_NORMAL].value = materials[m].shininess; + + model.materials[m].maps[MAP_EMISSION].color = (Color){ (float)(materials[m].emission[0]*255.0f), (float)(materials[m].emission[1]*255.0f), (float)(materials[m].emission[2]*255.0f), 255 }; //float emission[3]; + + if (materials[m].displacement_texname != NULL) model.materials[m].maps[MAP_HEIGHT].texture = LoadTexture(materials[m].displacement_texname); //char *displacement_texname; // disp } + + tinyobj_attrib_free(&attrib); + tinyobj_shapes_free(meshes, meshCount); + tinyobj_materials_free(materials, materialCount); } - // At this point all vertex data (v, vt, vn) has been gathered on midVertices, midTexCoords, midNormals - // Now we can organize that data into our Mesh struct - - mesh.vertexCount = triangleCount*3; - - // Additional arrays to store vertex data as floats - mesh.vertices = (float *)malloc(mesh.vertexCount*3*sizeof(float)); - mesh.texcoords = (float *)malloc(mesh.vertexCount*2*sizeof(float)); - mesh.normals = (float *)malloc(mesh.vertexCount*3*sizeof(float)); - mesh.colors = NULL; - - int vCounter = 0; // Used to count vertices float by float - int tcCounter = 0; // Used to count texcoords float by float - int nCounter = 0; // Used to count normals float by float - - int vCount[3], vtCount[3], vnCount[3]; // Used to store triangle indices for v, vt, vn - - rewind(objFile); // Return to the beginning of the file, to read again - - if (normalCount == 0) TraceLog(LOG_INFO, "[%s] No normals data on OBJ, normals will be generated from faces data", fileName); - - // Third reading pass: Get faces (triangles) data and fill VertexArray - while (!feof(objFile)) - { - fscanf(objFile, "%c", &dataType); - - switch (dataType) - { - case '#': case 'o': case 'g': case 's': case 'm': case 'u': case 'v': fgets(comments, 200, objFile); break; - case 'f': - { - // NOTE: It could be that OBJ does not have normals or texcoords defined! - - if ((normalCount == 0) && (texcoordCount == 0)) fscanf(objFile, "%i %i %i", &vCount[0], &vCount[1], &vCount[2]); - else if (normalCount == 0) fscanf(objFile, "%i/%i %i/%i %i/%i", &vCount[0], &vtCount[0], &vCount[1], &vtCount[1], &vCount[2], &vtCount[2]); - else if (texcoordCount == 0) fscanf(objFile, "%i//%i %i//%i %i//%i", &vCount[0], &vnCount[0], &vCount[1], &vnCount[1], &vCount[2], &vnCount[2]); - else fscanf(objFile, "%i/%i/%i %i/%i/%i %i/%i/%i", &vCount[0], &vtCount[0], &vnCount[0], &vCount[1], &vtCount[1], &vnCount[1], &vCount[2], &vtCount[2], &vnCount[2]); - - mesh.vertices[vCounter] = midVertices[vCount[0]-1].x; - mesh.vertices[vCounter + 1] = midVertices[vCount[0]-1].y; - mesh.vertices[vCounter + 2] = midVertices[vCount[0]-1].z; - vCounter += 3; - mesh.vertices[vCounter] = midVertices[vCount[1]-1].x; - mesh.vertices[vCounter + 1] = midVertices[vCount[1]-1].y; - mesh.vertices[vCounter + 2] = midVertices[vCount[1]-1].z; - vCounter += 3; - mesh.vertices[vCounter] = midVertices[vCount[2]-1].x; - mesh.vertices[vCounter + 1] = midVertices[vCount[2]-1].y; - mesh.vertices[vCounter + 2] = midVertices[vCount[2]-1].z; - vCounter += 3; - - if (normalCount > 0) - { - mesh.normals[nCounter] = midNormals[vnCount[0]-1].x; - mesh.normals[nCounter + 1] = midNormals[vnCount[0]-1].y; - mesh.normals[nCounter + 2] = midNormals[vnCount[0]-1].z; - nCounter += 3; - mesh.normals[nCounter] = midNormals[vnCount[1]-1].x; - mesh.normals[nCounter + 1] = midNormals[vnCount[1]-1].y; - mesh.normals[nCounter + 2] = midNormals[vnCount[1]-1].z; - nCounter += 3; - mesh.normals[nCounter] = midNormals[vnCount[2]-1].x; - mesh.normals[nCounter + 1] = midNormals[vnCount[2]-1].y; - mesh.normals[nCounter + 2] = midNormals[vnCount[2]-1].z; - nCounter += 3; - } - else - { - // If normals not defined, they are calculated from the 3 vertices [N = (V2 - V1) x (V3 - V1)] - Vector3 norm = Vector3CrossProduct(Vector3Subtract(midVertices[vCount[1]-1], midVertices[vCount[0]-1]), Vector3Subtract(midVertices[vCount[2]-1], midVertices[vCount[0]-1])); - norm = Vector3Normalize(norm); - - mesh.normals[nCounter] = norm.x; - mesh.normals[nCounter + 1] = norm.y; - mesh.normals[nCounter + 2] = norm.z; - nCounter += 3; - mesh.normals[nCounter] = norm.x; - mesh.normals[nCounter + 1] = norm.y; - mesh.normals[nCounter + 2] = norm.z; - nCounter += 3; - mesh.normals[nCounter] = norm.x; - mesh.normals[nCounter + 1] = norm.y; - mesh.normals[nCounter + 2] = norm.z; - nCounter += 3; - } - - if (texcoordCount > 0) - { - // NOTE: If using negative texture coordinates with a texture filter of GL_CLAMP_TO_EDGE doesn't work! - // NOTE: Texture coordinates are Y flipped upside-down - mesh.texcoords[tcCounter] = midTexCoords[vtCount[0]-1].x; - mesh.texcoords[tcCounter + 1] = 1.0f - midTexCoords[vtCount[0]-1].y; - tcCounter += 2; - mesh.texcoords[tcCounter] = midTexCoords[vtCount[1]-1].x; - mesh.texcoords[tcCounter + 1] = 1.0f - midTexCoords[vtCount[1]-1].y; - tcCounter += 2; - mesh.texcoords[tcCounter] = midTexCoords[vtCount[2]-1].x; - mesh.texcoords[tcCounter + 1] = 1.0f - midTexCoords[vtCount[2]-1].y; - tcCounter += 2; - } - } break; - default: break; - } - } - - fclose(objFile); - - // Now we can free temp mid* arrays - free(midVertices); - free(midNormals); - free(midTexCoords); - - // NOTE: At this point we have all vertex, texcoord, normal data for the model in mesh struct + // NOTE: At this point we have all model data loaded TraceLog(LOG_INFO, "[%s] Model loaded successfully in RAM (CPU)", fileName); - return mesh; + return model; } #endif -#if defined(SUPPORT_FILEFORMAT_MTL) -// Load MTL material data (specs: http://paulbourke.net/dataformats/mtl/) -// NOTE: Texture map parameters are not supported -static Material LoadMTL(const char *fileName) +#if defined(SUPPORT_FILEFORMAT_IQM) +// Load IQM mesh data +static Model LoadIQM(const char *fileName) { - #define MAX_BUFFER_SIZE 128 + #define IQM_MAGIC "INTERQUAKEMODEL" // IQM file magic number + #define IQM_VERSION 2 // only IQM version 2 supported - Material material = { 0 }; + #define BONE_NAME_LENGTH 32 // BoneInfo name string length + #define MESH_NAME_LENGTH 32 // Mesh name string length - char buffer[MAX_BUFFER_SIZE]; - Vector3 color = { 1.0f, 1.0f, 1.0f }; - char mapFileName[128]; - int result = 0; + // IQM file structs + //----------------------------------------------------------------------------------- + typedef struct IQMHeader { + char magic[16]; + unsigned int version; + unsigned int filesize; + unsigned int flags; + unsigned int num_text, ofs_text; + unsigned int num_meshes, ofs_meshes; + unsigned int num_vertexarrays, num_vertexes, ofs_vertexarrays; + unsigned int num_triangles, ofs_triangles, ofs_adjacency; + unsigned int num_joints, ofs_joints; + unsigned int num_poses, ofs_poses; + unsigned int num_anims, ofs_anims; + unsigned int num_frames, num_framechannels, ofs_frames, ofs_bounds; + unsigned int num_comment, ofs_comment; + unsigned int num_extensions, ofs_extensions; + } IQMHeader; - FILE *mtlFile; + typedef struct IQMMesh { + unsigned int name; + unsigned int material; + unsigned int first_vertex, num_vertexes; + unsigned int first_triangle, num_triangles; + } IQMMesh; - mtlFile = fopen(fileName, "rt"); + typedef struct IQMTriangle { + unsigned int vertex[3]; + } IQMTriangle; - if (mtlFile == NULL) + // NOTE: Adjacency unused by default + typedef struct IQMAdjacency { + unsigned int triangle[3]; + } IQMAdjacency; + + typedef struct IQMJoint { + unsigned int name; + int parent; + float translate[3], rotate[4], scale[3]; + } IQMJoint; + + typedef struct IQMPose { + int parent; + unsigned int mask; + float channeloffset[10]; + float channelscale[10]; + } IQMPose; + + typedef struct IQMAnim { + unsigned int name; + unsigned int first_frame, num_frames; + float framerate; + unsigned int flags; + } IQMAnim; + + typedef struct IQMVertexArray { + unsigned int type; + unsigned int flags; + unsigned int format; + unsigned int size; + unsigned int offset; + } IQMVertexArray; + + // NOTE: Bounds unused by default + typedef struct IQMBounds { + float bbmin[3], bbmax[3]; + float xyradius, radius; + } IQMBounds; + //----------------------------------------------------------------------------------- + + // IQM vertex data types + typedef enum { + IQM_POSITION = 0, + IQM_TEXCOORD = 1, + IQM_NORMAL = 2, + IQM_TANGENT = 3, // NOTE: Tangents unused by default + IQM_BLENDINDEXES = 4, + IQM_BLENDWEIGHTS = 5, + IQM_COLOR = 6, // NOTE: Vertex colors unused by default + IQM_CUSTOM = 0x10 // NOTE: Custom vertex values unused by default + } IQMVertexType; + + Model model = { 0 }; + + FILE *iqmFile; + IQMHeader iqm; + + IQMMesh *imesh; + IQMTriangle *tri; + IQMVertexArray *va; + IQMJoint *ijoint; + + float *vertex = NULL; + float *normal = NULL; + float *text = NULL; + char *blendi = NULL; + unsigned char *blendw = NULL; + + iqmFile = fopen(fileName, "rb"); + + if (iqmFile == NULL) { - TraceLog(LOG_WARNING, "[%s] MTL file could not be opened", fileName); - return material; + TraceLog(LOG_WARNING, "[%s] IQM file could not be opened", fileName); + return model; } - while (!feof(mtlFile)) + fread(&iqm,sizeof(IQMHeader), 1, iqmFile); // Read IQM header + + if (strncmp(iqm.magic, IQM_MAGIC, sizeof(IQM_MAGIC))) { - fgets(buffer, MAX_BUFFER_SIZE, mtlFile); + TraceLog(LOG_WARNING, "[%s] IQM file does not seem to be valid", fileName); + fclose(iqmFile); + return model; + } - switch (buffer[0]) + if (iqm.version != IQM_VERSION) + { + TraceLog(LOG_WARNING, "[%s] IQM file version is not supported (%i).", fileName, iqm.version); + fclose(iqmFile); + return model; + } + + // Meshes data processing + imesh = malloc(sizeof(IQMMesh)*iqm.num_meshes); + fseek(iqmFile, iqm.ofs_meshes, SEEK_SET); + fread(imesh, sizeof(IQMMesh)*iqm.num_meshes, 1, iqmFile); + + model.meshCount = iqm.num_meshes; + model.meshes = malloc(model.meshCount*sizeof(Mesh)); + + char name[MESH_NAME_LENGTH]; + + for (int i = 0; i < model.meshCount; i++) + { + fseek(iqmFile,iqm.ofs_text+imesh[i].name,SEEK_SET); + fread(name, sizeof(char)*MESH_NAME_LENGTH, 1, iqmFile); // Mesh name not used... + model.meshes[i].vertexCount = imesh[i].num_vertexes; + + model.meshes[i].vertices = malloc(sizeof(float)*model.meshes[i].vertexCount*3); // Default vertex positions + model.meshes[i].normals = malloc(sizeof(float)*model.meshes[i].vertexCount*3); // Default vertex normals + model.meshes[i].texcoords = malloc(sizeof(float)*model.meshes[i].vertexCount*2); // Default vertex texcoords + + model.meshes[i].boneIds = malloc(sizeof(int)*model.meshes[i].vertexCount*4); // Up-to 4 bones supported! + model.meshes[i].boneWeights = malloc(sizeof(float)*model.meshes[i].vertexCount*4); // Up-to 4 bones supported! + + model.meshes[i].triangleCount = imesh[i].num_triangles; + model.meshes[i].indices = malloc(sizeof(unsigned short)*model.meshes[i].triangleCount*3); + + // Animated verted data, what we actually process for rendering + // NOTE: Animated vertex should be re-uploaded to GPU (if not using GPU skinning) + model.meshes[i].animVertices = malloc(sizeof(float)*model.meshes[i].vertexCount*3); + model.meshes[i].animNormals = malloc(sizeof(float)*model.meshes[i].vertexCount*3); + } + + // Triangles data processing + tri = malloc(sizeof(IQMTriangle)*iqm.num_triangles); + fseek(iqmFile, iqm.ofs_triangles, SEEK_SET); + fread(tri, sizeof(IQMTriangle)*iqm.num_triangles, 1, iqmFile); + + for (int m = 0; m < model.meshCount; m++) + { + int tcounter = 0; + + for (int i = imesh[m].first_triangle; i < (imesh[m].first_triangle + imesh[m].num_triangles); i++) { - case 'n': // newmtl string Material name. Begins a new material description. - { - // TODO: Support multiple materials in a single .mtl - sscanf(buffer, "newmtl %127s", mapFileName); - } - case 'i': // illum int Illumination model - { - // illum = 1 if specular disabled - // illum = 2 if specular enabled (lambertian model) - // ... - } - case 'K': // Ka, Kd, Ks, Ke - { - switch (buffer[1]) - { - case 'a': // Ka float float float Ambient color (RGB) - { - sscanf(buffer, "Ka %f %f %f", &color.x, &color.y, &color.z); - // TODO: Support ambient color - //material.colAmbient.r = (unsigned char)(color.x*255); - //material.colAmbient.g = (unsigned char)(color.y*255); - //material.colAmbient.b = (unsigned char)(color.z*255); - } break; - case 'd': // Kd float float float Diffuse color (RGB) - { - sscanf(buffer, "Kd %f %f %f", &color.x, &color.y, &color.z); - material.maps[MAP_DIFFUSE].color.r = (unsigned char)(color.x*255); - material.maps[MAP_DIFFUSE].color.g = (unsigned char)(color.y*255); - material.maps[MAP_DIFFUSE].color.b = (unsigned char)(color.z*255); - } break; - case 's': // Ks float float float Specular color (RGB) - { - sscanf(buffer, "Ks %f %f %f", &color.x, &color.y, &color.z); - material.maps[MAP_SPECULAR].color.r = (unsigned char)(color.x*255); - material.maps[MAP_SPECULAR].color.g = (unsigned char)(color.y*255); - material.maps[MAP_SPECULAR].color.b = (unsigned char)(color.z*255); - } break; - case 'e': // Ke float float float Emmisive color (RGB) - { - // TODO: Support Ke? - } break; - default: break; - } - } break; - case 'N': // Ns, Ni - { - if (buffer[1] == 's') // Ns int Shininess (specular exponent). Ranges from 0 to 1000. - { - int shininess = 0; - sscanf(buffer, "Ns %i", &shininess); - - //material.params[PARAM_GLOSSINES] = (float)shininess; - } - else if (buffer[1] == 'i') // Ni int Refraction index. - { - // Not supported... - } - } break; - case 'm': // map_Kd, map_Ks, map_Ka, map_Bump, map_d - { - switch (buffer[4]) - { - case 'K': // Color texture maps - { - if (buffer[5] == 'd') // map_Kd string Diffuse color texture map. - { - result = sscanf(buffer, "map_Kd %127s", mapFileName); - if (result != EOF) material.maps[MAP_DIFFUSE].texture = LoadTexture(mapFileName); - } - else if (buffer[5] == 's') // map_Ks string Specular color texture map. - { - result = sscanf(buffer, "map_Ks %127s", mapFileName); - if (result != EOF) material.maps[MAP_SPECULAR].texture = LoadTexture(mapFileName); - } - else if (buffer[5] == 'a') // map_Ka string Ambient color texture map. - { - // Not supported... - } - } break; - case 'B': // map_Bump string Bump texture map. - { - result = sscanf(buffer, "map_Bump %127s", mapFileName); - if (result != EOF) material.maps[MAP_NORMAL].texture = LoadTexture(mapFileName); - } break; - case 'b': // map_bump string Bump texture map. - { - result = sscanf(buffer, "map_bump %127s", mapFileName); - if (result != EOF) material.maps[MAP_NORMAL].texture = LoadTexture(mapFileName); - } break; - case 'd': // map_d string Opacity texture map. - { - // Not supported... - } break; - default: break; - } - } break; - case 'd': // d, disp - { - if (buffer[1] == ' ') // d float Dissolve factor. d is inverse of Tr - { - float alpha = 1.0f; - sscanf(buffer, "d %f", &alpha); - material.maps[MAP_DIFFUSE].color.a = (unsigned char)(alpha*255); - } - else if (buffer[1] == 'i') // disp string Displacement map - { - // Not supported... - } - } break; - case 'b': // bump string Bump texture map - { - result = sscanf(buffer, "bump %127s", mapFileName); - if (result != EOF) material.maps[MAP_NORMAL].texture = LoadTexture(mapFileName); - } break; - case 'T': // Tr float Transparency Tr (alpha). Tr is inverse of d - { - float ialpha = 0.0f; - sscanf(buffer, "Tr %f", &ialpha); - material.maps[MAP_DIFFUSE].color.a = (unsigned char)((1.0f - ialpha)*255); - - } break; - case 'r': // refl string Reflection texture map - default: break; + // IQM triangles are stored counter clockwise, but raylib sets opengl to clockwise drawing, so we swap them around + model.meshes[m].indices[tcounter + 2] = tri[i].vertex[0] - imesh[m].first_vertex; + model.meshes[m].indices[tcounter + 1] = tri[i].vertex[1] - imesh[m].first_vertex; + model.meshes[m].indices[tcounter] = tri[i].vertex[2] - imesh[m].first_vertex; + tcounter += 3; } } - fclose(mtlFile); + // Vertex arrays data processing + va = malloc(sizeof(IQMVertexArray)*iqm.num_vertexarrays); + fseek(iqmFile, iqm.ofs_vertexarrays, SEEK_SET); + fread(va, sizeof(IQMVertexArray)*iqm.num_vertexarrays, 1, iqmFile); - // NOTE: At this point we have all material data - TraceLog(LOG_INFO, "[%s] Material loaded successfully", fileName); + for (int i = 0; i < iqm.num_vertexarrays; i++) + { + switch (va[i].type) + { + case IQM_POSITION: + { + vertex = malloc(sizeof(float)*iqm.num_vertexes*3); + fseek(iqmFile, va[i].offset, SEEK_SET); + fread(vertex, sizeof(float)*iqm.num_vertexes*3, 1, iqmFile); - return material; -} -#endif + for (int m = 0; m < iqm.num_meshes; m++) + { + int vCounter = 0; + for (int i = imesh[m].first_vertex*3; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*3; i++) + { + model.meshes[m].vertices[vCounter] = vertex[i]; + model.meshes[m].animVertices[vCounter] = vertex[i]; + vCounter++; + } + } + } break; + case IQM_NORMAL: + { + normal = malloc(sizeof(float)*iqm.num_vertexes*3); + fseek(iqmFile, va[i].offset, SEEK_SET); + fread(normal, sizeof(float)*iqm.num_vertexes*3, 1, iqmFile); -#if defined(SUPPORT_FILEFORMAT_GLTF) -// Load IQM mesh data -static Mesh LoadIQM(const char *fileName) -{ - Mesh mesh = { 0 }; + for (int m = 0; m < iqm.num_meshes; m++) + { + int vCounter = 0; + for (int i = imesh[m].first_vertex*3; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*3; i++) + { + model.meshes[m].normals[vCounter] = normal[i]; + model.meshes[m].animNormals[vCounter] = normal[i]; + vCounter++; + } + } + } break; + case IQM_TEXCOORD: + { + text = malloc(sizeof(float)*iqm.num_vertexes*2); + fseek(iqmFile, va[i].offset, SEEK_SET); + fread(text, sizeof(float)*iqm.num_vertexes*2, 1, iqmFile); - // TODO: Load IQM file + for (int m = 0; m < iqm.num_meshes; m++) + { + int vCounter = 0; + for (int i = imesh[m].first_vertex*2; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*2; i++) + { + model.meshes[m].texcoords[vCounter] = text[i]; + vCounter++; + } + } + } break; + case IQM_BLENDINDEXES: + { + blendi = malloc(sizeof(char)*iqm.num_vertexes*4); + fseek(iqmFile, va[i].offset, SEEK_SET); + fread(blendi, sizeof(char)*iqm.num_vertexes*4, 1, iqmFile); - return mesh; + for (int m = 0; m < iqm.num_meshes; m++) + { + int boneCounter = 0; + for (int i = imesh[m].first_vertex*4; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*4; i++) + { + model.meshes[m].boneIds[boneCounter] = blendi[i]; + boneCounter++; + } + } + } break; + case IQM_BLENDWEIGHTS: + { + blendw = malloc(sizeof(unsigned char)*iqm.num_vertexes*4); + fseek(iqmFile,va[i].offset,SEEK_SET); + fread(blendw,sizeof(unsigned char)*iqm.num_vertexes*4,1,iqmFile); + + for (int m = 0; m < iqm.num_meshes; m++) + { + int boneCounter = 0; + for (int i = imesh[m].first_vertex*4; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*4; i++) + { + model.meshes[m].boneWeights[boneCounter] = blendw[i]/255.0f; + boneCounter++; + } + } + } break; + } + } + + // Bones (joints) data processing + ijoint = malloc(sizeof(IQMJoint)*iqm.num_joints); + fseek(iqmFile, iqm.ofs_joints, SEEK_SET); + fread(ijoint, sizeof(IQMJoint)*iqm.num_joints, 1, iqmFile); + + model.boneCount = iqm.num_joints; + model.bones = malloc(sizeof(BoneInfo)*iqm.num_joints); + model.bindPose = malloc(sizeof(Transform)*iqm.num_joints); + + for (int i = 0; i < iqm.num_joints; i++) + { + // Bones + model.bones[i].parent = ijoint[i].parent; + fseek(iqmFile, iqm.ofs_text + ijoint[i].name, SEEK_SET); + fread(model.bones[i].name,sizeof(char)*BONE_NAME_LENGTH, 1, iqmFile); + + // Bind pose (base pose) + model.bindPose[i].translation.x = ijoint[i].translate[0]; + model.bindPose[i].translation.y = ijoint[i].translate[1]; + model.bindPose[i].translation.z = ijoint[i].translate[2]; + + model.bindPose[i].rotation.x = ijoint[i].rotate[0]; + model.bindPose[i].rotation.y = ijoint[i].rotate[1]; + model.bindPose[i].rotation.z = ijoint[i].rotate[2]; + model.bindPose[i].rotation.w = ijoint[i].rotate[3]; + + model.bindPose[i].scale.x = ijoint[i].scale[0]; + model.bindPose[i].scale.y = ijoint[i].scale[1]; + model.bindPose[i].scale.z = ijoint[i].scale[2]; + } + + // Build bind pose from parent joints + for (int i = 0; i < model.boneCount; i++) + { + if (model.bones[i].parent >= 0) + { + model.bindPose[i].rotation = QuaternionMultiply(model.bindPose[model.bones[i].parent].rotation, model.bindPose[i].rotation); + model.bindPose[i].translation = Vector3RotateByQuaternion(model.bindPose[i].translation, model.bindPose[model.bones[i].parent].rotation); + model.bindPose[i].translation = Vector3Add(model.bindPose[i].translation, model.bindPose[model.bones[i].parent].translation); + model.bindPose[i].scale = Vector3MultiplyV(model.bindPose[i].scale, model.bindPose[model.bones[i].parent].scale); + } + } + + fclose(iqmFile); + free(imesh); + free(tri); + free(va); + free(vertex); + free(normal); + free(text); + free(blendi); + free(blendw); + free(ijoint); + + return model; } #endif #if defined(SUPPORT_FILEFORMAT_GLTF) // Load glTF mesh data -static Mesh LoadGLTF(const char *fileName) +static Model LoadGLTF(const char *fileName) { - Mesh mesh = { 0 }; + Model model = { 0 }; // glTF file loading FILE *gltfFile = fopen(fileName, "rb"); @@ -2765,7 +3242,7 @@ static Mesh LoadGLTF(const char *fileName) if (gltfFile == NULL) { TraceLog(LOG_WARNING, "[%s] glTF file could not be opened", fileName); - return mesh; + return model; } fseek(gltfFile, 0, SEEK_END); @@ -2778,27 +3255,49 @@ static Mesh LoadGLTF(const char *fileName) fclose(gltfFile); // glTF data loading - cgltf_options options = {0}; - cgltf_data data; + cgltf_options options = { 0 }; + cgltf_data *data; cgltf_result result = cgltf_parse(&options, buffer, size, &data); free(buffer); if (result == cgltf_result_success) { - printf("Type: %u\n", data.file_type); - printf("Version: %d\n", data.version); - printf("Meshes: %lu\n", data.meshes_count); + TraceLog(LOG_INFO, "[%s][%s] Model meshes/materials: %i/%i", (data->file_type == 2)? "glb" : "gltf", data->meshes_count, data->materials_count); - // TODO: Process glTF data and map to mesh + // Read data buffers + result = cgltf_load_buffers(&options, data, fileName); - // NOTE: data.buffers[] and data.images[] should be loaded - // using buffers[n].uri and images[n].uri... or use cgltf_load_buffers(&options, data, fileName); + // Process glTF data and map to model + model.meshCount = data->meshes_count; + model.meshes = malloc(model.meshCount*sizeof(Mesh)); + + for (int i = 0; i < model.meshCount; i++) + { + // NOTE: Only support meshes defined by triangle primitives + //if (data->meshes[i].primitives[n].type == cgltf_primitive_type_triangles) + { + // data.meshes[i].name not used + model.meshes[i].vertexCount = data->meshes[i].primitives_count*3; + model.meshes[i].triangleCount = data->meshes[i].primitives_count; + // data.meshes[i].weights not used (array of weights to be applied to the Morph Targets) - cgltf_free(&data); + model.meshes[i].vertices = malloc(sizeof(float)*model.meshes[i].vertexCount*3); // Default vertex positions + model.meshes[i].normals = malloc(sizeof(float)*model.meshes[i].vertexCount*3); // Default vertex normals + model.meshes[i].texcoords = malloc(sizeof(float)*model.meshes[i].vertexCount*2); // Default vertex texcoords + + model.meshes[i].indices = malloc(sizeof(unsigned short)*model.meshes[i].triangleCount*3); + + } + } + + // NOTE: data.buffers[] should be loaded to model.meshes and data.images[] should be loaded to model.materials + // Use buffers[n].uri and images[n].uri... or use cgltf_load_buffers(&options, data, fileName); + + cgltf_free(data); } else TraceLog(LOG_WARNING, "[%s] glTF data could not be loaded", fileName); - return mesh; + return model; } #endif diff --git a/src/raudio.c b/src/raudio.c index 1c1530098..636d15b85 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -46,7 +46,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) +* Copyright (c) 2013-2019 Ramon Santamaria (@raysan5) * * 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. @@ -525,7 +525,7 @@ void InitAudioDevice(void) TraceLog(LOG_INFO, "Audio channels: %d -> %d", device.playback.channels, device.playback.internalChannels); TraceLog(LOG_INFO, "Audio sample rate: %d -> %d", device.sampleRate, device.playback.internalSampleRate); TraceLog(LOG_INFO, "Audio buffer size: %d", device.playback.internalBufferSizeInFrames); - + isAudioInitialized = MA_TRUE; } @@ -587,7 +587,7 @@ AudioBuffer *CreateAudioBuffer(ma_format format, ma_uint32 channels, ma_uint32 s dspConfig.pUserData = audioBuffer; dspConfig.allowDynamicSampleRate = MA_TRUE; // <-- Required for pitch shifting. ma_result result = ma_pcm_converter_init(&dspConfig, &audioBuffer->dsp); - + if (result != MA_SUCCESS) { TraceLog(LOG_ERROR, "CreateAudioBuffer() : Failed to create data conversion pipeline"); diff --git a/src/raylib.h b/src/raylib.h index 8e0269ad4..d873e1ad0 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -4,19 +4,20 @@ * * FEATURES: * - NO external dependencies, all required libraries included with raylib -* - Multiple platforms support: Windows, Linux, FreeBSD, OpenBSD, NetBSD, DragonFly, MacOS, UWP, Android, Raspberry Pi, HTML5. +* - Multiplatform: Windows, Linux, FreeBSD, OpenBSD, NetBSD, DragonFly, MacOS, UWP, Android, Raspberry Pi, HTML5. * - Written in plain C code (C99) in PascalCase/camelCase notation * - Hardware accelerated with OpenGL (1.1, 2.1, 3.3 or ES2 - choose at compile) * - Unique OpenGL abstraction layer (usable as standalone module): [rlgl] -* - Powerful fonts module with Fonts support (XNA fonts, AngelCode fonts, TTF) +* - Powerful fonts module (XNA SpriteFonts, BMFonts, TTF) * - Outstanding texture formats support, including compressed formats (DXT, ETC, ASTC) * - Full 3d support for 3d Shapes, Models, Billboards, Heightmaps and more! * - Flexible Materials system, supporting classic maps and PBR maps +* - Skeletal Animation support (CPU bones-based animation) * - Shaders support, including Model shaders and Postprocessing shaders * - Powerful math module for Vector, Matrix and Quaternion operations: [raymath] * - Audio loading and playing with streaming support (WAV, OGG, MP3, FLAC, XM, MOD) * - VR stereo rendering with configurable HMD device parameters -* - Complete bindings to LUA (raylib-lua) and Go (raylib-go) +* - Bindings to multiple programming languages available! * * NOTES: * One custom font is loaded by default when InitWindow() [core] @@ -24,24 +25,26 @@ * If using OpenGL 3.3 or ES2, several vertex buffers (VAO/VBO) are created to manage lines-triangles-quads * * DEPENDENCIES (included): -* rglfw (github.com/glfw/glfw) for window/context management and input (only PLATFORM_DESKTOP) [core] -* glad (github.com/Dav1dde/glad) for OpenGL extensions loading (3.3 Core profile, only PLATFORM_DESKTOP) [rlgl] -* mini_al (github.com/dr-soft/mini_al) for audio device/context management [audio] +* [core] rglfw (github.com/glfw/glfw) for window/context management and input (only PLATFORM_DESKTOP) +* [rlgl] glad (github.com/Dav1dde/glad) for OpenGL 3.3 extensions loading (only PLATFORM_DESKTOP) +* [raudio] miniaudio (github.com/dr-soft/miniaudio) for audio device/context management * * OPTIONAL DEPENDENCIES (included): -* stb_image (Sean Barret) for images loading (BMP, TGA, PNG, JPEG, HDR...) [textures] -* stb_image_resize (Sean Barret) for image resizing algorythms [textures] -* stb_image_write (Sean Barret) for image writting (PNG) [utils] -* stb_truetype (Sean Barret) for ttf fonts loading [text] -* stb_rect_pack (Sean Barret) for rectangles packing [text] -* stb_vorbis (Sean Barret) for OGG audio loading [audio] -* stb_perlin (Sean Barret) for Perlin noise image generation [textures] -* par_shapes (Philip Rideout) for parametric 3d shapes generation [models] -* jar_xm (Joshua Reisenauer) for XM audio module loading [audio] -* jar_mod (Joshua Reisenauer) for MOD audio module loading [audio] -* dr_flac (David Reid) for FLAC audio file loading [audio] -* dr_mp3 (David Reid) for MP3 audio file loading [audio] -* rgif (Charlie Tangora, Ramon Santamaria) for GIF recording [core] +* [core] rgif (Charlie Tangora, Ramon Santamaria) for GIF recording +* [textures] stb_image (Sean Barret) for images loading (BMP, TGA, PNG, JPEG, HDR...) +* [textures] stb_image_write (Sean Barret) for image writting (BMP, TGA, PNG, JPG) +* [textures] stb_image_resize (Sean Barret) for image resizing algorythms +* [textures] stb_perlin (Sean Barret) for Perlin noise image generation +* [text] stb_truetype (Sean Barret) for ttf fonts loading +* [text] stb_rect_pack (Sean Barret) for rectangles packing +* [models] par_shapes (Philip Rideout) for parametric 3d shapes generation +* [models] tinyobj_loader_c (Syoyo Fujita) for models loading (OBJ, MTL) +* [models] cgltf (Johannes Kuhlmann) for models loading (glTF) +* [raudio] stb_vorbis (Sean Barret) for OGG audio loading +* [raudio] dr_flac (David Reid) for FLAC audio file loading +* [raudio] dr_mp3 (David Reid) for MP3 audio file loading +* [raudio] jar_xm (Joshua Reisenauer) for XM audio module loading +* [raudio] jar_mod (Joshua Reisenauer) for MOD audio module loading * * * LICENSE: zlib/libpng @@ -49,7 +52,7 @@ * raylib is licensed under an unmodified zlib/libpng license, which is an OSI-certified, * BSD-like license that allows static linking with closed source software: * -* Copyright (c) 2013-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2013-2019 Ramon Santamaria (@raysan5) * * 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. @@ -326,7 +329,7 @@ typedef struct Camera3D { int type; // Camera type, defines projection type: CAMERA_PERSPECTIVE or CAMERA_ORTHOGRAPHIC } Camera3D; -#define Camera Camera3D // Camera type fallback, defaults to Camera3D +typedef Camera3D Camera; // Camera type fallback, defaults to Camera3D // Camera2D type, defines a 2d camera typedef struct Camera2D { @@ -336,12 +339,6 @@ typedef struct Camera2D { float zoom; // Camera zoom (scaling), should be 1.0f by default } Camera2D; -// Bounding box type -typedef struct BoundingBox { - Vector3 min; // Minimum vertex box-corner - Vector3 max; // Maximum vertex box-corner -} BoundingBox; - // Vertex data definning a mesh // NOTE: Data stored in CPU memory (and GPU) typedef struct Mesh { @@ -358,10 +355,10 @@ typedef struct Mesh { unsigned short *indices;// Vertex indices (in case vertex data comes indexed) // Animation vertex data - float *baseVertices; // Vertex base position (required to apply bones transformations) - float *baseNormals; // Vertex base normals (required to apply bones transformations) - float *weightBias; // Vertex weight bias - int *weightId; // Vertex weight id + float *animVertices; // Animated vertex positions (after bones transformations) + float *animNormals; // Animated normals (after bones transformations) + int *boneIds; // Vertex bone ids, up to 4 bones influence by vertex (skinning) + float *boneWeights; // Vertex bone weight, up to 4 bones influence by vertex (skinning) // OpenGL identifiers unsigned int vaoId; // OpenGL Vertex Array Object id @@ -388,22 +385,45 @@ typedef struct Material { float *params; // Material generic parameters (if required) } Material; +// Transformation properties +typedef struct Transform { + Vector3 translation; // Translation + Quaternion rotation; // Rotation + Vector3 scale; // Scale +} Transform; + +// Bone information +typedef struct BoneInfo { + char name[32]; // Bone name + int parent; // Bone parent +} BoneInfo; + // Model type typedef struct Model { - Mesh mesh; // Vertex data buffers (RAM and VRAM) Matrix transform; // Local transform matrix - Material material; // Shader and textures data - /* - Mesh *meshes; // Vertex data buffers (RAM and VRAM) - int meshCount; - Material *materials; // Shader and textures data - int materialCount; + int meshCount; // Number of meshes + Mesh *meshes; // Meshes array - int *meshMaterial; // Material assigned to every mesh - */ + int materialCount; // Number of materials + Material *materials; // Materials array + int *meshMaterial; // Mesh material number + + // Animation data + int boneCount; // Number of bones + BoneInfo *bones; // Bones information (skeleton) + Transform *bindPose; // Bones base transformation (pose) } Model; +// Model animation +typedef struct ModelAnimation { + int boneCount; // Number of bones + BoneInfo *bones; // Bones information (skeleton) + + int frameCount; // Number of animation frames + Transform **framePoses; // Poses array by frame +} ModelAnimation; + // Ray type (useful for raycast) typedef struct Ray { Vector3 position; // Ray position (origin) @@ -418,6 +438,12 @@ typedef struct RayHitInfo { Vector3 normal; // Surface normal of hit } RayHitInfo; +// Bounding box type +typedef struct BoundingBox { + Vector3 min; // Minimum vertex box-corner + Vector3 max; // Maximum vertex box-corner +} BoundingBox; + // Wave type, defines audio wave data typedef struct Wave { unsigned int sampleCount; // Number of samples @@ -468,16 +494,6 @@ typedef struct VrDeviceInfo { float chromaAbCorrection[4]; // HMD chromatic aberration correction parameters } VrDeviceInfo; -// VR Stereo rendering configuration for simulator -typedef struct VrStereoConfig { - RenderTexture2D stereoFbo; // VR stereo rendering framebuffer - Shader distortionShader; // VR stereo rendering distortion shader - Matrix eyesProjection[2]; // VR stereo rendering eyes projection matrices - Matrix eyesViewOffset[2]; // VR stereo rendering eyes view offset matrices - int eyeViewportRight[4]; // VR stereo rendering right eye viewport [x, y, w, h] - int eyeViewportLeft[4]; // VR stereo rendering left eye viewport [x, y, w, h] -} VrStereoConfig; - // Network typedefs typedef uint32_t SocketChannel; typedef struct _AddressInformation * AddressInformation; @@ -575,7 +591,6 @@ typedef struct Packet //---------------------------------------------------------------------------------- // Enumerators Definition //---------------------------------------------------------------------------------- - // System config flags // NOTE: Used for bit masks typedef enum { @@ -591,14 +606,14 @@ typedef enum { // Trace log type typedef enum { - LOG_ALL, // Display all logs + LOG_ALL = 0, // Display all logs LOG_TRACE, LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERROR, LOG_FATAL, - LOG_NONE // Disable logging + LOG_NONE // Disable logging } TraceLogType; // Keyboard keys @@ -874,7 +889,7 @@ typedef enum { MAP_IRRADIANCE, // NOTE: Uses GL_TEXTURE_CUBE_MAP MAP_PREFILTER, // NOTE: Uses GL_TEXTURE_CUBE_MAP MAP_BRDF -} TexmapIndex; +} MaterialMapType; #define MAP_DIFFUSE MAP_ALBEDO #define MAP_SPECULAR MAP_METALNESS @@ -980,16 +995,6 @@ typedef enum { CAMERA_ORTHOGRAPHIC } CameraType; -// Head Mounted Display devices -typedef enum { - HMD_DEFAULT_DEVICE = 0, - HMD_OCULUS_RIFT_DK2, - HMD_OCULUS_RIFT_CV1, - HMD_OCULUS_GO, - HMD_VALVE_HTC_VIVE, - HMD_SONY_PSVR -} VrDeviceType; - // Type of n-patch typedef enum { NPT_9PATCH = 0, // Npatch defined by 3x3 tiles @@ -1065,7 +1070,7 @@ RLAPI Ray GetMouseRay(Vector2 mousePosition, Camera camera); // Returns a r RLAPI Vector2 GetWorldToScreen(Vector3 position, Camera camera); // Returns the screen space position for a 3d world space position RLAPI Matrix GetCameraMatrix(Camera camera); // Returns camera transform matrix (view matrix) -// timing-related functions +// Timing-related functions RLAPI void SetTargetFPS(int fps); // Set target FPS (maximum) RLAPI int GetFPS(void); // Returns current FPS RLAPI float GetFrameTime(void); // Returns time in seconds for last frame drawn @@ -1188,10 +1193,13 @@ RLAPI void DrawLineV(Vector2 startPos, Vector2 endPos, Color color); RLAPI void DrawLineEx(Vector2 startPos, Vector2 endPos, float thick, Color color); // Draw a line defining thickness RLAPI void DrawLineBezier(Vector2 startPos, Vector2 endPos, float thick, Color color); // Draw a line using cubic-bezier curves in-out RLAPI void DrawCircle(int centerX, int centerY, float radius, Color color); // Draw a color-filled circle -RLAPI void DrawCircleSector(Vector2 center, float radius, int startAngle, int endAngle, Color color); // Draw a piece of a circle +RLAPI void DrawCircleSector(Vector2 center, float radius, int startAngle, int endAngle, int segments, Color color); // Draw a piece of a circle +RLAPI void DrawCircleSectorLines(Vector2 center, float radius, int startAngle, int endAngle, int segments, Color color); // Draw circle sector outline RLAPI void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Color color2); // Draw a gradient-filled circle RLAPI void DrawCircleV(Vector2 center, float radius, Color color); // Draw a color-filled circle (Vector version) RLAPI void DrawCircleLines(int centerX, int centerY, float radius, Color color); // Draw circle outline +RLAPI void DrawRing(Vector2 center, float innerRadius, float outerRadius, int startAngle, int endAngle, int segments, Color color); // Draw ring +RLAPI void DrawRingLines(Vector2 center, float innerRadius, float outerRadius, int startAngle, int endAngle, int segments, Color color); // Draw ring outline RLAPI void DrawRectangle(int posX, int posY, int width, int height, Color color); // Draw a color-filled rectangle RLAPI void DrawRectangleV(Vector2 position, Vector2 size, Color color); // Draw a color-filled rectangle (Vector version) RLAPI void DrawRectangleRec(Rectangle rec, Color color); // Draw a color-filled rectangle @@ -1201,6 +1209,8 @@ RLAPI void DrawRectangleGradientH(int posX, int posY, int width, int height, Col RLAPI void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3, Color col4); // Draw a gradient-filled rectangle with custom vertex colors RLAPI void DrawRectangleLines(int posX, int posY, int width, int height, Color color); // Draw rectangle outline RLAPI void DrawRectangleLinesEx(Rectangle rec, int lineThick, Color color); // Draw rectangle outline with extended parameters +RLAPI void DrawRectangleRounded(Rectangle rec, float roundness, int segments, Color color); // Draw rectangle with rounded edges +RLAPI void DrawRectangleRoundedLines(Rectangle rec, float roundness, int segments, int lineThick, Color color); // Draw rectangle with rounded edges outline RLAPI void DrawTriangle(Vector2 v1, Vector2 v2, Vector2 v3, Color color); // Draw a color-filled triangle RLAPI void DrawTriangleLines(Vector2 v1, Vector2 v2, Vector2 v3, Color color); // Draw triangle outline RLAPI void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color color); // Draw a regular polygon (Vector version) @@ -1318,7 +1328,7 @@ RLAPI void DrawFPS(int posX, int posY); RLAPI void DrawText(const char *text, int posX, int posY, int fontSize, Color color); // Draw text (using default font) RLAPI void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, float spacing, Color tint); // Draw text using font and additional parameters RLAPI void DrawTextRec(Font font, const char *text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint); // Draw text using font inside rectangle limits -RLAPI void DrawTextRecEx(Font font, const char *text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint, +RLAPI void DrawTextRecEx(Font font, const char *text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint, int selectStart, int selectLength, Color selectText, Color selectBack); // Draw text using font inside rectangle limits with support for text selection // Text misc. functions @@ -1371,20 +1381,28 @@ RLAPI void DrawGizmo(Vector3 position); //------------------------------------------------------------------------------------ // Model loading/unloading functions -RLAPI Model LoadModel(const char *fileName); // Load model from files (mesh and material) -RLAPI Model LoadModelFromMesh(Mesh mesh); // Load model from generated mesh +RLAPI Model LoadModel(const char *fileName); // Load model from files (meshes and materials) +RLAPI Model LoadModelFromMesh(Mesh mesh); // Load model from generated mesh (default material) RLAPI void UnloadModel(Model model); // Unload model from memory (RAM and/or VRAM) // Mesh loading/unloading functions -RLAPI Mesh LoadMesh(const char *fileName); // Load mesh from file +RLAPI Mesh *LoadMeshes(const char *fileName, int *meshCount); // Load meshes from model file +RLAPI void ExportMesh(Mesh mesh, const char *fileName); // Export mesh data to file RLAPI void UnloadMesh(Mesh *mesh); // Unload mesh from memory (RAM and/or VRAM) -RLAPI void ExportMesh(Mesh mesh, const char *fileName); // Export mesh data to file -// Mesh manipulation functions -RLAPI BoundingBox MeshBoundingBox(Mesh mesh); // Compute mesh bounding box limits -RLAPI void MeshTangents(Mesh *mesh); // Compute mesh tangents -RLAPI void MeshBinormals(Mesh *mesh); // Compute mesh binormals +// Material loading/unloading functions +RLAPI Material *LoadMaterials(const char *fileName, int *materialCount); // Load materials from model file +RLAPI Material LoadMaterialDefault(void); // Load default material (Supports: DIFFUSE, SPECULAR, NORMAL maps) +RLAPI void UnloadMaterial(Material material); // Unload material from GPU memory (VRAM) +RLAPI void SetMaterialTexture(Material *material, int mapType, Texture2D texture); // Set texture for a material map type (MAP_DIFFUSE, MAP_SPECULAR...) +RLAPI void SetModelMeshMaterial(Model *model, int meshId, int materialId); // Set material for a mesh +// Model animations loading/unloading functions +RLAPI ModelAnimation *LoadModelAnimations(const char *fileName, int *animsCount); // Load model animations from file +RLAPI void UpdateModelAnimation(Model model, ModelAnimation anim, int frame); // Update model animation pose +RLAPI void UnloadModelAnimation(ModelAnimation anim); // Unload animation data +RLAPI bool IsModelAnimationValid(Model model, ModelAnimation anim); // Check model animation skeleton match + // Mesh generation functions RLAPI Mesh GenMeshPoly(int sides, float radius); // Generate polygonal mesh RLAPI Mesh GenMeshPlane(float width, float length, int resX, int resZ); // Generate plane mesh (with subdivisions) @@ -1397,10 +1415,10 @@ RLAPI Mesh GenMeshKnot(float radius, float size, int radSeg, int sides); RLAPI Mesh GenMeshHeightmap(Image heightmap, Vector3 size); // Generate heightmap mesh from image data RLAPI Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize); // Generate cubes-based map mesh from image data -// Material loading/unloading functions -RLAPI Material LoadMaterial(const char *fileName); // Load material from file -RLAPI Material LoadMaterialDefault(void); // Load default material (Supports: DIFFUSE, SPECULAR, NORMAL maps) -RLAPI void UnloadMaterial(Material material); // Unload material from GPU memory (VRAM) +// Mesh manipulation functions +RLAPI BoundingBox MeshBoundingBox(Mesh mesh); // Compute mesh bounding box limits +RLAPI void MeshTangents(Mesh *mesh); // Compute mesh tangents +RLAPI void MeshBinormals(Mesh *mesh); // Compute mesh binormals // Model drawing functions RLAPI void DrawModel(Model model, Vector3 position, float scale, Color tint); // Draw a model (with texture if set) @@ -1437,10 +1455,11 @@ RLAPI Shader GetShaderDefault(void); // Get RLAPI Texture2D GetTextureDefault(void); // Get default texture // Shader configuration functions -RLAPI int GetShaderLocation(Shader shader, const char *uniformName); // Get shader uniform location +RLAPI int GetShaderLocation(Shader shader, const char *uniformName); // Get shader uniform location RLAPI void SetShaderValue(Shader shader, int uniformLoc, const void *value, int uniformType); // Set shader uniform value RLAPI void SetShaderValueV(Shader shader, int uniformLoc, const void *value, int uniformType, int count); // Set shader uniform value vector -RLAPI void SetShaderValueMatrix(Shader shader, int uniformLoc, Matrix mat); // Set shader uniform value (matrix 4x4) +RLAPI void SetShaderValueMatrix(Shader shader, int uniformLoc, Matrix mat); // Set shader uniform value (matrix 4x4) +RLAPI void SetShaderValueTexture(Shader shader, int uniformLoc, Texture2D texture); // Set shader uniform value for texture RLAPI void SetMatrixProjection(Matrix proj); // Set a custom projection matrix (replaces internal projection matrix) RLAPI void SetMatrixModelview(Matrix view); // Set a custom modelview matrix (replaces internal modelview matrix) RLAPI Matrix GetMatrixModelview(); // Get internal modelview matrix @@ -1450,7 +1469,7 @@ RLAPI Matrix GetMatrixModelview(); // Get RLAPI Texture2D GenTextureCubemap(Shader shader, Texture2D skyHDR, int size); // Generate cubemap texture from HDR texture RLAPI Texture2D GenTextureIrradiance(Shader shader, Texture2D cubemap, int size); // Generate irradiance texture using cubemap data RLAPI Texture2D GenTexturePrefilter(Shader shader, Texture2D cubemap, int size); // Generate prefilter texture using cubemap data -RLAPI Texture2D GenTextureBRDF(Shader shader, int size); // Generate BRDF texture using cubemap data +RLAPI Texture2D GenTextureBRDF(Shader shader, int size); // Generate BRDF texture // Shading begin/end functions RLAPI void BeginShaderMode(Shader shader); // Begin custom shader drawing @@ -1461,10 +1480,10 @@ RLAPI void BeginScissorMode(int x, int y, int width, int height); // Beg RLAPI void EndScissorMode(void); // End scissor mode // VR control functions -RLAPI VrDeviceInfo GetVrDeviceInfo(int vrDeviceType); // Get VR device information for some standard devices -RLAPI void InitVrSimulator(VrDeviceInfo info); // Init VR simulator for selected device parameters +RLAPI void InitVrSimulator(void); // Init VR simulator for selected device parameters +RLAPI void CloseVrSimulator(void); // Close VR simulator for current device RLAPI void UpdateVrTracking(Camera *camera); // Update VR tracking (position and orientation) and camera -RLAPI void CloseVrSimulator(void); // Close VR simulator for current device +RLAPI void SetVrConfiguration(VrDeviceInfo info, Shader distortion); // Set stereo rendering configuration parameters RLAPI bool IsVrSimulatorReady(void); // Detect if VR simulator is ready RLAPI void ToggleVrMode(void); // Enable/Disable VR experience RLAPI void BeginVrDrawing(void); // Begin VR simulator stereo rendering diff --git a/src/raylib.rc b/src/raylib.rc index 2aaa5e26e..c2fdfa461 100644 --- a/src/raylib.rc +++ b/src/raylib.rc @@ -1,8 +1,8 @@ GLFW_ICON ICON "raylib.ico" 1 VERSIONINFO -FILEVERSION 2,0,0,0 -PRODUCTVERSION 2,0,0,0 +FILEVERSION 2,5,0,0 +PRODUCTVERSION 2,5,0,0 BEGIN BLOCK "StringFileInfo" BEGIN @@ -11,12 +11,12 @@ BEGIN BEGIN //VALUE "CompanyName", "raylib technologies" VALUE "FileDescription", "Created using raylib (www.raylib.com)" - VALUE "FileVersion", "2.0.0" + VALUE "FileVersion", "2.5.0" VALUE "InternalName", "raylib app" - VALUE "LegalCopyright", "(c) 2018 Ramon Santamaria - @raysan5" + VALUE "LegalCopyright", "(c) 2019 Ramon Santamaria (@raysan5)" //VALUE "OriginalFilename", "raylib_app.exe" VALUE "ProductName", "raylib game" - VALUE "ProductVersion", "2.0.0" + VALUE "ProductVersion", "2.5.0" END END BLOCK "VarFileInfo" diff --git a/src/raymath.h b/src/raymath.h index 42a45ad6c..b9dae5546 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -440,7 +440,7 @@ RMDEF Vector3 Vector3Transform(Vector3 v, Matrix mat) result.z = mat.m2*x + mat.m6*y + mat.m10*z + mat.m14; return result; -}; +} // Transform a vector by quaternion rotation RMDEF Vector3 Vector3RotateByQuaternion(Vector3 v, Quaternion q) diff --git a/src/rglfw.c b/src/rglfw.c index 853c52009..3463bb96d 100644 --- a/src/rglfw.c +++ b/src/rglfw.c @@ -7,7 +7,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2017-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2017-2019 Ramon Santamaria (@raysan5) * * 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. diff --git a/src/rlgl.h b/src/rlgl.h index 00658fa14..048c75da3 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -32,9 +32,6 @@ * #define SUPPORT_VR_SIMULATOR * Support VR simulation functionality (stereo rendering) * -* #define SUPPORT_DISTORTION_SHADER -* Include stereo rendering distortion shader (embedded) -* * DEPENDENCIES: * raymath - 3D math functionality (Vector3, Matrix, Quaternion) * GLAD - OpenGL extensions loading (OpenGL 3.3 Core only) @@ -42,7 +39,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * * 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. @@ -205,10 +202,10 @@ typedef unsigned char byte; unsigned short *indices;// vertex indices (in case vertex data comes indexed) // Animation vertex data - float *baseVertices; // Vertex base position (required to apply bones transformations) - float *baseNormals; // Vertex base normals (required to apply bones transformations) - float *weightBias; // Vertex weight bias - int *weightId; // Vertex weight id + float *animVertices; // Animated vertex positions (after bones transformations) + float *animNormals; // Animated normals (after bones transformations) + int *boneIds; // Vertex bone ids, up to 4 bones influence by vertex (skinning) + float *boneWeights; // Vertex bone weight, up to 4 bones influence by vertex (skinning) // OpenGL identifiers unsigned int vaoId; // OpenGL Vertex Array Object id @@ -263,7 +260,6 @@ typedef unsigned char byte; // VR Stereo rendering configuration for simulator typedef struct VrStereoConfig { - RenderTexture2D stereoFbo; // VR stereo rendering framebuffer Shader distortionShader; // VR stereo rendering distortion shader Matrix eyesProjection[2]; // VR stereo rendering eyes projection matrices Matrix eyesViewOffset[2]; // VR stereo rendering eyes view offset matrices @@ -386,20 +382,10 @@ typedef unsigned char byte; MAP_IRRADIANCE, // NOTE: Uses GL_TEXTURE_CUBE_MAP MAP_PREFILTER, // NOTE: Uses GL_TEXTURE_CUBE_MAP MAP_BRDF - } TexmapIndex; + } MaterialMapType; #define MAP_DIFFUSE MAP_ALBEDO #define MAP_SPECULAR MAP_METALNESS - - // VR Head Mounted Display devices - typedef enum { - HMD_DEFAULT_DEVICE = 0, - HMD_OCULUS_RIFT_DK2, - HMD_OCULUS_RIFT_CV1, - HMD_OCULUS_GO, - HMD_VALVE_HTC_VIVE, - HMD_SONY_PSVR - } VrDevice; #endif #if defined(__cplusplus) @@ -455,6 +441,8 @@ void rlDeleteVertexArrays(unsigned int id); // Unload vertex data (V void rlDeleteBuffers(unsigned int id); // Unload vertex data (VBO) from GPU memory void rlClearColor(byte r, byte g, byte b, byte a); // Clear color buffer with color void rlClearScreenBuffers(void); // Clear used screen buffers (color and depth) +void rlUpdateBuffer(int bufferId, void *data, int dataSize); // Update GPU buffer with new data +unsigned int rlLoadAttribBuffer(unsigned int vaoId, int shaderLoc, void *buffer, int size, bool dynamic); // Load a new attributes buffer //------------------------------------------------------------------------------------ // Functions Declaration - rlgl functionality @@ -532,10 +520,10 @@ void BeginBlendMode(int mode); // Begin blending mode (alpha, void EndBlendMode(void); // End blending mode (reset to default: alpha blending) // VR control functions -VrDeviceInfo GetVrDeviceInfo(int vrDeviceType); // Get VR device information for some standard devices -void InitVrSimulator(VrDeviceInfo info); // Init VR simulator for selected device parameters -void UpdateVrTracking(Camera *camera); // Update VR tracking (position and orientation) and camera +void InitVrSimulator(void); // Init VR simulator for selected device parameters void CloseVrSimulator(void); // Close VR simulator for current device +void UpdateVrTracking(Camera *camera); // Update VR tracking (position and orientation) and camera +void SetVrConfiguration(VrDeviceInfo info, Shader distortion); // Set stereo rendering configuration parameters bool IsVrSimulatorReady(void); // Detect if VR simulator is ready void ToggleVrMode(void); // Enable/Disable VR experience void BeginVrDrawing(void); // Begin VR simulator stereo rendering @@ -559,10 +547,7 @@ int GetPixelDataSize(int width, int height, int format);// Get pixel data size i #if defined(RLGL_IMPLEMENTATION) -#if defined(RLGL_STANDALONE) - #define SUPPORT_VR_SIMULATOR - #define SUPPORT_DISTORTION_SHADER -#else +#if !defined(RLGL_STANDALONE) // Check if config flags have been externally provided on compilation line #if !defined(EXTERNAL_CONFIG_FLAGS) #include "config.h" // Defines module configuration flags @@ -728,6 +713,7 @@ typedef struct DynamicBuffer { typedef struct DrawCall { int mode; // Drawing mode: LINES, TRIANGLES, QUADS int vertexCount; // Number of vertex of the draw + int vertexAlignment; // Number of vertex required for index alignment (LINES, TRIANGLES) //unsigned int vaoId; // Vertex Array id to be used on the draw //unsigned int shaderId; // Shader id to be used on the draw unsigned int textureId; // Texture id to be used on the draw @@ -737,88 +723,20 @@ typedef struct DrawCall { //Matrix modelview; // Modelview matrix for this draw } DrawCall; +#if defined(SUPPORT_VR_SIMULATOR) +// VR Stereo rendering configuration for simulator +typedef struct VrStereoConfig { + Shader distortionShader; // VR stereo rendering distortion shader + Matrix eyesProjection[2]; // VR stereo rendering eyes projection matrices + Matrix eyesViewOffset[2]; // VR stereo rendering eyes view offset matrices + int eyeViewportRight[4]; // VR stereo rendering right eye viewport [x, y, w, h] + int eyeViewportLeft[4]; // VR stereo rendering left eye viewport [x, y, w, h] +} VrStereoConfig; +#endif + //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- -#if !defined(GRAPHICS_API_OPENGL_11) && defined(SUPPORT_DISTORTION_SHADER) - // Distortion shader embedded - static char distortionFShaderStr[] = - #if defined(GRAPHICS_API_OPENGL_21) - "#version 120 \n" - #elif defined(GRAPHICS_API_OPENGL_ES2) - "#version 100 \n" - "precision mediump float; \n" // precision required for OpenGL ES2 (WebGL) - #endif - #if defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_21) - "varying vec2 fragTexCoord; \n" - "varying vec4 fragColor; \n" - #elif defined(GRAPHICS_API_OPENGL_33) - "#version 330 \n" - "in vec2 fragTexCoord; \n" - "in vec4 fragColor; \n" - "out vec4 finalColor; \n" - #endif - "uniform sampler2D texture0; \n" - #if defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_21) - "uniform vec2 leftLensCenter; \n" - "uniform vec2 rightLensCenter; \n" - "uniform vec2 leftScreenCenter; \n" - "uniform vec2 rightScreenCenter; \n" - "uniform vec2 scale; \n" - "uniform vec2 scaleIn; \n" - "uniform vec4 hmdWarpParam; \n" - "uniform vec4 chromaAbParam; \n" - #elif defined(GRAPHICS_API_OPENGL_33) - "uniform vec2 leftLensCenter = vec2(0.288, 0.5); \n" - "uniform vec2 rightLensCenter = vec2(0.712, 0.5); \n" - "uniform vec2 leftScreenCenter = vec2(0.25, 0.5); \n" - "uniform vec2 rightScreenCenter = vec2(0.75, 0.5); \n" - "uniform vec2 scale = vec2(0.25, 0.45); \n" - "uniform vec2 scaleIn = vec2(4, 2.2222); \n" - "uniform vec4 hmdWarpParam = vec4(1, 0.22, 0.24, 0); \n" - "uniform vec4 chromaAbParam = vec4(0.996, -0.004, 1.014, 0.0); \n" - #endif - "void main() \n" - "{ \n" - " vec2 lensCenter = fragTexCoord.x < 0.5? leftLensCenter : rightLensCenter; \n" - " vec2 screenCenter = fragTexCoord.x < 0.5? leftScreenCenter : rightScreenCenter; \n" - " vec2 theta = (fragTexCoord - lensCenter)*scaleIn; \n" - " float rSq = theta.x*theta.x + theta.y*theta.y; \n" - " vec2 theta1 = theta*(hmdWarpParam.x + hmdWarpParam.y*rSq + hmdWarpParam.z*rSq*rSq + hmdWarpParam.w*rSq*rSq*rSq); \n" - " vec2 thetaBlue = theta1*(chromaAbParam.z + chromaAbParam.w*rSq); \n" - " vec2 tcBlue = lensCenter + scale*thetaBlue; \n" - " if (any(bvec2(clamp(tcBlue, screenCenter - vec2(0.25, 0.5), screenCenter + vec2(0.25, 0.5)) - tcBlue))) \n" - " { \n" - #if defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_21) - " gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0); \n" - #elif defined(GRAPHICS_API_OPENGL_33) - " finalColor = vec4(0.0, 0.0, 0.0, 1.0); \n" - #endif - " } \n" - " else \n" - " { \n" - #if defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_21) - " float blue = texture2D(texture0, tcBlue).b; \n" - " vec2 tcGreen = lensCenter + scale*theta1; \n" - " float green = texture2D(texture0, tcGreen).g; \n" - #elif defined(GRAPHICS_API_OPENGL_33) - " float blue = texture(texture0, tcBlue).b; \n" - " vec2 tcGreen = lensCenter + scale*theta1; \n" - " float green = texture(texture0, tcGreen).g; \n" - #endif - " vec2 thetaRed = theta1*(chromaAbParam.x + chromaAbParam.y*rSq); \n" - " vec2 tcRed = lensCenter + scale*thetaRed; \n" - #if defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_21) - " float red = texture2D(texture0, tcRed).r; \n" - " gl_FragColor = vec4(red, green, blue, 1.0); \n" - #elif defined(GRAPHICS_API_OPENGL_33) - " float red = texture(texture0, tcRed).r; \n" - " finalColor = vec4(red, green, blue, 1.0); \n" - #endif - " } \n" - "} \n"; -#endif - #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) static Matrix stack[MAX_MATRIX_STACK_SIZE] = { 0 }; static int stackCounter = 0; @@ -888,6 +806,7 @@ static PFNGLDELETEVERTEXARRAYSOESPROC glDeleteVertexArrays; #if defined(SUPPORT_VR_SIMULATOR) // VR global variables static VrStereoConfig vrConfig = { 0 }; // VR stereo configuration for simulator +static RenderTexture2D stereoFbo; // VR stereo rendering framebuffer static bool vrSimulatorReady = false; // VR simulator ready flag static bool vrStereoRender = false; // VR stereo rendering enabled/disabled flag // NOTE: This flag is useful to render data over stereo image (i.e. FPS) @@ -921,7 +840,6 @@ static void GenDrawCube(void); // Generate and draw cube static void GenDrawQuad(void); // Generate and draw quad #if defined(SUPPORT_VR_SIMULATOR) -static VrStereoConfig SetStereoConfig(VrDeviceInfo info, Shader distortion); // Configure stereo rendering (including distortion shader) with HMD device parameters static void SetStereoView(int eye, Matrix matProjection, Matrix matModelView); // Set internal projection and modelview matrix depending on eye #endif @@ -1123,7 +1041,27 @@ void rlBegin(int mode) // NOTE: In all three cases, vertex are accumulated over default internal vertex buffer if (draws[drawsCounter - 1].mode != mode) { - if (draws[drawsCounter - 1].vertexCount > 0) drawsCounter++; + if (draws[drawsCounter - 1].vertexCount > 0) + { + // Make sure current draws[i].vertexCount is aligned a multiple of 4, + // that way, following QUADS drawing will keep aligned with index processing + // It implies adding some extra alignment vertex at the end of the draw, + // those vertex are not processed but they are considered as an additional offset + // for the next set of vertex to be drawn + if (draws[drawsCounter - 1].mode == RL_LINES) draws[drawsCounter - 1].vertexAlignment = ((draws[drawsCounter - 1].vertexCount < 4)? draws[drawsCounter - 1].vertexCount : draws[drawsCounter - 1].vertexCount%4); + else if (draws[drawsCounter - 1].mode == RL_TRIANGLES) draws[drawsCounter - 1].vertexAlignment = ((draws[drawsCounter - 1].vertexCount < 4)? 1 : (4 - (draws[drawsCounter - 1].vertexCount%4))); + + if (rlCheckBufferLimit(draws[drawsCounter - 1].vertexAlignment)) rlglDraw(); + else + { + vertexData[currentBuffer].vCounter += draws[drawsCounter - 1].vertexAlignment; + vertexData[currentBuffer].cCounter += draws[drawsCounter - 1].vertexAlignment; + vertexData[currentBuffer].tcCounter += draws[drawsCounter - 1].vertexAlignment; + + drawsCounter++; + } + } + if (drawsCounter >= MAX_DRAWCALL_REGISTERED) rlglDraw(); draws[drawsCounter - 1].mode = mode; @@ -1135,15 +1073,6 @@ void rlBegin(int mode) // Finish vertex providing void rlEnd(void) { - // Make sure current draws[i].vertexCount is multiple of 4, to align with index processing - // NOTE: It implies adding some extra vertex at the end of the draw, those vertex will be - // processed but are placed in a single point to not result in a fragment output... - // TODO: System could be improved (a bit) just storing every draw alignment value - // and adding it to vertexOffset on drawing... maybe in a future... - int vertexCount = draws[drawsCounter - 1].vertexCount; - int vertexToAlign = (vertexCount >= 4)? vertexCount%4 : (4 - vertexCount%4); - for (int i = 0; i < vertexToAlign; i++) rlVertex3f(-1, -1, -1); - // Make sure vertexCount is the same for vertices, texcoords, colors and normals // NOTE: In OpenGL 1.1, one glColor call can be made for all the subsequent glVertex calls @@ -1283,7 +1212,27 @@ void rlEnableTexture(unsigned int id) #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) if (draws[drawsCounter - 1].textureId != id) { - if (draws[drawsCounter - 1].vertexCount > 0) drawsCounter++; + if (draws[drawsCounter - 1].vertexCount > 0) + { + // Make sure current draws[i].vertexCount is aligned a multiple of 4, + // that way, following QUADS drawing will keep aligned with index processing + // It implies adding some extra alignment vertex at the end of the draw, + // those vertex are not processed but they are considered as an additional offset + // for the next set of vertex to be drawn + if (draws[drawsCounter - 1].mode == RL_LINES) draws[drawsCounter - 1].vertexAlignment = ((draws[drawsCounter - 1].vertexCount < 4)? draws[drawsCounter - 1].vertexCount : draws[drawsCounter - 1].vertexCount%4); + else if (draws[drawsCounter - 1].mode == RL_TRIANGLES) draws[drawsCounter - 1].vertexAlignment = ((draws[drawsCounter - 1].vertexCount < 4)? 1 : (4 - (draws[drawsCounter - 1].vertexCount%4))); + + if (rlCheckBufferLimit(draws[drawsCounter - 1].vertexAlignment)) rlglDraw(); + else + { + vertexData[currentBuffer].vCounter += draws[drawsCounter - 1].vertexAlignment; + vertexData[currentBuffer].cCounter += draws[drawsCounter - 1].vertexAlignment; + vertexData[currentBuffer].tcCounter += draws[drawsCounter - 1].vertexAlignment; + + drawsCounter++; + } + } + if (drawsCounter >= MAX_DRAWCALL_REGISTERED) rlglDraw(); draws[drawsCounter - 1].textureId = id; @@ -1469,6 +1418,13 @@ void rlClearScreenBuffers(void) //glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); // Stencil buffer not used... } +// Update GPU buffer with new data +void rlUpdateBuffer(int bufferId, void *data, int dataSize) +{ + glBindBuffer(GL_ARRAY_BUFFER, bufferId); + glBufferSubData(GL_ARRAY_BUFFER, 0, dataSize, data); +} + //---------------------------------------------------------------------------------- // Module Functions Definition - rlgl Functions //---------------------------------------------------------------------------------- @@ -1682,6 +1638,7 @@ void rlglInit(int width, int height) { draws[i].mode = RL_QUADS; draws[i].vertexCount = 0; + draws[i].vertexAlignment = 0; //draws[i].vaoId = 0; //draws[i].shaderId = 0; draws[i].textureId = defaultTextureId; @@ -1753,8 +1710,12 @@ void rlglClose(void) void rlglDraw(void) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - UpdateBuffersDefault(); - DrawBuffersDefault(); // NOTE: Stereo rendering is checked inside + // Only process data if we have data to process + if (vertexData[currentBuffer].vCounter > 0) + { + UpdateBuffersDefault(); + DrawBuffersDefault(); // NOTE: Stereo rendering is checked inside + } #endif } @@ -1999,6 +1960,8 @@ unsigned int rlLoadTexture(void *data, int width, int height, int format, int mi unsigned int rlLoadTextureDepth(int width, int height, int bits, bool useRenderBuffer) { unsigned int id = 0; + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) unsigned int glInternalFormat = GL_DEPTH_COMPONENT16; if ((bits != 16) && (bits != 24) && (bits != 32)) bits = 16; @@ -2044,6 +2007,7 @@ unsigned int rlLoadTextureDepth(int width, int height, int bits, bool useRenderB glBindRenderbuffer(GL_RENDERBUFFER, 0); } +#endif return id; } @@ -2056,16 +2020,17 @@ unsigned int rlLoadTextureCubemap(void *data, int size, int format) unsigned int cubemapId = 0; unsigned int dataSize = GetPixelDataSize(size, size, format); +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) glGenTextures(1, &cubemapId); glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapId); unsigned int glInternalFormat, glFormat, glType; rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType); - // Load cubemap faces - for (unsigned int i = 0; i < 6; i++) + if (glInternalFormat != -1) { - if (glInternalFormat != -1) + // Load cubemap faces + for (unsigned int i = 0; i < 6; i++) { if (format < COMPRESSED_DXT1_RGB) glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, size, size, 0, glFormat, glType, (unsigned char *)data + i*dataSize); #if !defined(GRAPHICS_API_OPENGL_11) @@ -2100,6 +2065,7 @@ unsigned int rlLoadTextureCubemap(void *data, int size, int format) #endif glBindTexture(GL_TEXTURE_CUBE_MAP, 0); +#endif return cubemapId; } @@ -2184,9 +2150,9 @@ RenderTexture2D rlLoadRenderTexture(int width, int height, int format, int depth { RenderTexture2D target = { 0 }; +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) if (useDepthTexture && texDepthSupported) target.depthTexture = true; -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) // Create the framebuffer object glGenFramebuffers(1, &target.id); glBindFramebuffer(GL_FRAMEBUFFER, target.id); @@ -2237,6 +2203,7 @@ RenderTexture2D rlLoadRenderTexture(int width, int height, int format, int depth // NOTE: Attach type: 0-Color, 1-Depth renderbuffer, 2-Depth texture void rlRenderTextureAttach(RenderTexture2D target, unsigned int id, int attachType) { +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) glBindFramebuffer(GL_FRAMEBUFFER, target.id); if (attachType == 0) glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, id, 0); @@ -2247,11 +2214,15 @@ void rlRenderTextureAttach(RenderTexture2D target, unsigned int id, int attachTy } glBindFramebuffer(GL_FRAMEBUFFER, 0); +#endif } // Verify render texture is complete bool rlRenderTextureComplete(RenderTexture target) { + bool result = false; + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) glBindFramebuffer(GL_FRAMEBUFFER, target.id); GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); @@ -2272,7 +2243,10 @@ bool rlRenderTextureComplete(RenderTexture target) glBindFramebuffer(GL_FRAMEBUFFER, 0); - return (status == GL_FRAMEBUFFER_COMPLETE); + result = (status == GL_FRAMEBUFFER_COMPLETE); +#endif + + return result; } // Generate mipmap data for selected texture @@ -2474,6 +2448,29 @@ void rlLoadMesh(Mesh *mesh, bool dynamic) #endif } +// Load a new attributes buffer +unsigned int rlLoadAttribBuffer(unsigned int vaoId, int shaderLoc, void *buffer, int size, bool dynamic) +{ + unsigned int id = 0; + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + int drawHint = GL_STATIC_DRAW; + if (dynamic) drawHint = GL_DYNAMIC_DRAW; + + if (vaoSupported) glBindVertexArray(vaoId); + + glGenBuffers(1, &id); + glBindBuffer(GL_ARRAY_BUFFER, id); + glBufferData(GL_ARRAY_BUFFER, size, buffer, drawHint); + glVertexAttribPointer(shaderLoc, 2, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(shaderLoc); + + if (vaoSupported) glBindVertexArray(0); +#endif + + return id; +} + // Update vertex data on GPU (upload new data to one buffer) void rlUpdateMesh(Mesh mesh, int buffer, int numVertex) { @@ -2738,10 +2735,10 @@ void rlUnloadMesh(Mesh *mesh) free(mesh->texcoords2); free(mesh->indices); - free(mesh->baseVertices); - free(mesh->baseNormals); - free(mesh->weightBias); - free(mesh->weightId); + free(mesh->animVertices); + free(mesh->animNormals); + free(mesh->boneWeights); + free(mesh->boneIds); rlDeleteBuffers(mesh->vboId[0]); // vertex rlDeleteBuffers(mesh->vboId[1]); // texcoords @@ -3087,6 +3084,18 @@ void SetShaderValueMatrix(Shader shader, int uniformLoc, Matrix mat) #endif } +// Set shader uniform value for texture +void SetShaderValueTexture(Shader shader, int uniformLoc, Texture2D texture) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glUseProgram(shader.id); + + glUniform1i(uniformLoc, texture.id); + + //glUseProgram(0); +#endif +} + // Set a custom projection matrix (replaces internal projection matrix) void SetMatrixProjection(Matrix proj) { @@ -3483,101 +3492,17 @@ void EndScissorMode(void) } #if defined(SUPPORT_VR_SIMULATOR) -// Get VR device information for some standard devices -VrDeviceInfo GetVrDeviceInfo(int vrDeviceType) -{ - VrDeviceInfo hmd = { 0 }; // Current VR device info - - switch (vrDeviceType) - { - case HMD_DEFAULT_DEVICE: - case HMD_OCULUS_RIFT_CV1: - { - // Oculus Rift CV1 parameters - // NOTE: CV1 represents a complete HMD redesign compared to previous versions, - // new Fresnel-hybrid-asymmetric lenses have been added and, consequently, - // previous parameters (DK2) and distortion shader (DK2) doesn't work any more. - // I just defined a set of parameters for simulator that approximate to CV1 stereo rendering - // but result is not the same obtained with Oculus PC SDK. - hmd.hResolution = 2160; // HMD horizontal resolution in pixels - hmd.vResolution = 1200; // HMD vertical resolution in pixels - hmd.hScreenSize = 0.133793f; // HMD horizontal size in meters - hmd.vScreenSize = 0.0669f; // HMD vertical size in meters - hmd.vScreenCenter = 0.04678f; // HMD screen center in meters - hmd.eyeToScreenDistance = 0.041f; // HMD distance between eye and display in meters - hmd.lensSeparationDistance = 0.07f; // HMD lens separation distance in meters - hmd.interpupillaryDistance = 0.07f; // HMD IPD (distance between pupils) in meters - hmd.lensDistortionValues[0] = 1.0f; // HMD lens distortion constant parameter 0 - hmd.lensDistortionValues[1] = 0.22f; // HMD lens distortion constant parameter 1 - hmd.lensDistortionValues[2] = 0.24f; // HMD lens distortion constant parameter 2 - hmd.lensDistortionValues[3] = 0.0f; // HMD lens distortion constant parameter 3 - hmd.chromaAbCorrection[0] = 0.996f; // HMD chromatic aberration correction parameter 0 - hmd.chromaAbCorrection[1] = -0.004f; // HMD chromatic aberration correction parameter 1 - hmd.chromaAbCorrection[2] = 1.014f; // HMD chromatic aberration correction parameter 2 - hmd.chromaAbCorrection[3] = 0.0f; // HMD chromatic aberration correction parameter 3 - - TraceLog(LOG_INFO, "Initializing VR Simulator (Oculus Rift CV1)"); - } break; - case HMD_OCULUS_RIFT_DK2: - { - // Oculus Rift DK2 parameters - hmd.hResolution = 1280; // HMD horizontal resolution in pixels - hmd.vResolution = 800; // HMD vertical resolution in pixels - hmd.hScreenSize = 0.14976f; // HMD horizontal size in meters - hmd.vScreenSize = 0.09356f; // HMD vertical size in meters - hmd.vScreenCenter = 0.04678f; // HMD screen center in meters - hmd.eyeToScreenDistance = 0.041f; // HMD distance between eye and display in meters - hmd.lensSeparationDistance = 0.0635f; // HMD lens separation distance in meters - hmd.interpupillaryDistance = 0.064f; // HMD IPD (distance between pupils) in meters - hmd.lensDistortionValues[0] = 1.0f; // HMD lens distortion constant parameter 0 - hmd.lensDistortionValues[1] = 0.22f; // HMD lens distortion constant parameter 1 - hmd.lensDistortionValues[2] = 0.24f; // HMD lens distortion constant parameter 2 - hmd.lensDistortionValues[3] = 0.0f; // HMD lens distortion constant parameter 3 - hmd.chromaAbCorrection[0] = 0.996f; // HMD chromatic aberration correction parameter 0 - hmd.chromaAbCorrection[1] = -0.004f; // HMD chromatic aberration correction parameter 1 - hmd.chromaAbCorrection[2] = 1.014f; // HMD chromatic aberration correction parameter 2 - hmd.chromaAbCorrection[3] = 0.0f; // HMD chromatic aberration correction parameter 3 - - TraceLog(LOG_INFO, "Initializing VR Simulator (Oculus Rift DK2)"); - } break; - case HMD_OCULUS_GO: - { - // TODO: Provide device display and lens parameters - } - case HMD_VALVE_HTC_VIVE: - { - // TODO: Provide device display and lens parameters - } - case HMD_SONY_PSVR: - { - // TODO: Provide device display and lens parameters - } - default: break; - } - - return hmd; -} - // Init VR simulator for selected device parameters -// NOTE: It modifies the global variable: VrStereoConfig vrConfig -void InitVrSimulator(VrDeviceInfo info) +// NOTE: It modifies the global variable: stereoFbo +void InitVrSimulator(void) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - Shader distortion = { 0 }; - -#if defined(SUPPORT_DISTORTION_SHADER) - // Load distortion shader - distortion = LoadShaderCode(NULL, distortionFShaderStr); - if (distortion.id > 0) SetShaderDefaultLocations(&distortion); -#endif - - // Set VR configutarion parameters, including distortion shader - vrConfig = SetStereoConfig(info, distortion); - + // Initialize framebuffer and textures for stereo rendering + // NOTE: Screen size should match HMD aspect ratio + stereoFbo = rlLoadRenderTexture(screenWidth, screenHeight, UNCOMPRESSED_R8G8B8A8, 24, false); + vrSimulatorReady = true; -#endif - -#if defined(GRAPHICS_API_OPENGL_11) +#else TraceLog(LOG_WARNING, "VR Simulator not supported on OpenGL 1.1"); #endif } @@ -3593,16 +3518,92 @@ void UpdateVrTracking(Camera *camera) void CloseVrSimulator(void) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - if (vrSimulatorReady) - { - rlDeleteRenderTextures(vrConfig.stereoFbo); // Unload stereo framebuffer and texture - #if defined(SUPPORT_DISTORTION_SHADER) - UnloadShader(vrConfig.distortionShader); // Unload distortion shader - #endif - } + if (vrSimulatorReady) rlDeleteRenderTextures(stereoFbo); // Unload stereo framebuffer and texture #endif } +// Set stereo rendering configuration parameters +void SetVrConfiguration(VrDeviceInfo hmd, Shader distortion) +{ + // Reset vrConfig for a new values assignment + memset(&vrConfig, 0, sizeof(vrConfig)); + + // Assign distortion shader + vrConfig.distortionShader = distortion; + + // Compute aspect ratio + float aspect = ((float)hmd.hResolution*0.5f)/(float)hmd.vResolution; + + // Compute lens parameters + float lensShift = (hmd.hScreenSize*0.25f - hmd.lensSeparationDistance*0.5f)/hmd.hScreenSize; + float leftLensCenter[2] = { 0.25f + lensShift, 0.5f }; + float rightLensCenter[2] = { 0.75f - lensShift, 0.5f }; + float leftScreenCenter[2] = { 0.25f, 0.5f }; + float rightScreenCenter[2] = { 0.75f, 0.5f }; + + // 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 lensRadiusSq = lensRadius*lensRadius; + float distortionScale = hmd.lensDistortionValues[0] + + hmd.lensDistortionValues[1]*lensRadiusSq + + hmd.lensDistortionValues[2]*lensRadiusSq*lensRadiusSq + + hmd.lensDistortionValues[3]*lensRadiusSq*lensRadiusSq*lensRadiusSq; + + TraceLog(LOG_DEBUG, "VR: Distortion Scale: %f", distortionScale); + + float normScreenWidth = 0.5f; + float normScreenHeight = 1.0f; + float scaleIn[2] = { 2.0f/normScreenWidth, 2.0f/normScreenHeight/aspect }; + float scale[2] = { normScreenWidth*0.5f/distortionScale, normScreenHeight*0.5f*aspect/distortionScale }; + + TraceLog(LOG_DEBUG, "VR: Distortion Shader: LeftLensCenter = { %f, %f }", leftLensCenter[0], leftLensCenter[1]); + TraceLog(LOG_DEBUG, "VR: Distortion Shader: RightLensCenter = { %f, %f }", rightLensCenter[0], rightLensCenter[1]); + TraceLog(LOG_DEBUG, "VR: Distortion Shader: Scale = { %f, %f }", scale[0], scale[1]); + TraceLog(LOG_DEBUG, "VR: Distortion Shader: ScaleIn = { %f, %f }", scaleIn[0], scaleIn[1]); + + // Fovy is normally computed with: 2*atan2(hmd.vScreenSize, 2*hmd.eyeToScreenDistance) + // ...but with lens distortion it is increased (see Oculus SDK Documentation) + //float fovy = 2.0f*atan2(hmd.vScreenSize*0.5f*distortionScale, hmd.eyeToScreenDistance); // Really need distortionScale? + float fovy = 2.0f*(float)atan2(hmd.vScreenSize*0.5f, hmd.eyeToScreenDistance); + + // Compute camera projection matrices + float projOffset = 4.0f*lensShift; // Scaled to projection space coordinates [-1..1] + Matrix proj = MatrixPerspective(fovy, aspect, 0.01, 1000.0); + vrConfig.eyesProjection[0] = MatrixMultiply(proj, MatrixTranslate(projOffset, 0.0f, 0.0f)); + vrConfig.eyesProjection[1] = MatrixMultiply(proj, MatrixTranslate(-projOffset, 0.0f, 0.0f)); + + // Compute camera transformation matrices + // NOTE: Camera movement might seem more natural if we model the head. + // Our axis of rotation is the base of our head, so we might want to add + // some y (base of head to eye level) and -z (center of head to eye protrusion) to the camera positions. + vrConfig.eyesViewOffset[0] = MatrixTranslate(-hmd.interpupillaryDistance*0.5f, 0.075f, 0.045f); + vrConfig.eyesViewOffset[1] = MatrixTranslate(hmd.interpupillaryDistance*0.5f, 0.075f, 0.045f); + + // Compute eyes Viewports + vrConfig.eyeViewportRight[2] = hmd.hResolution/2; + vrConfig.eyeViewportRight[3] = hmd.vResolution; + + vrConfig.eyeViewportLeft[0] = hmd.hResolution/2; + vrConfig.eyeViewportLeft[1] = 0; + vrConfig.eyeViewportLeft[2] = hmd.hResolution/2; + vrConfig.eyeViewportLeft[3] = hmd.vResolution; + + if (vrConfig.distortionShader.id > 0) + { + // Update distortion shader with lens and distortion-scale parameters + SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "leftLensCenter"), leftLensCenter, UNIFORM_VEC2); + SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "rightLensCenter"), rightLensCenter, UNIFORM_VEC2); + SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "leftScreenCenter"), leftScreenCenter, UNIFORM_VEC2); + SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "rightScreenCenter"), rightScreenCenter, UNIFORM_VEC2); + + SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "scale"), scale, UNIFORM_VEC2); + SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "scaleIn"), scaleIn, UNIFORM_VEC2); + SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "hmdWarpParam"), hmd.lensDistortionValues, UNIFORM_VEC4); + SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "chromaAbParam"), hmd.chromaAbCorrection, UNIFORM_VEC4); + } +} + // Detect if VR simulator is running bool IsVrSimulatorReady(void) { @@ -3639,7 +3640,7 @@ void BeginVrDrawing(void) if (vrSimulatorReady) { // Setup framebuffer for stereo rendering - rlEnableRenderTexture(vrConfig.stereoFbo.id); + rlEnableRenderTexture(stereoFbo.id); // NOTE: If your application is configured to treat the texture as a linear format (e.g. GL_RGBA) // and performs linear-to-gamma conversion in GLSL or does not care about gamma-correction, then: @@ -3677,13 +3678,11 @@ void EndVrDrawing(void) rlMatrixMode(RL_MODELVIEW); // Enable internal modelview matrix rlLoadIdentity(); // Reset internal modelview matrix -#if defined(SUPPORT_DISTORTION_SHADER) - // Draw RenderTexture (stereoFbo) using distortion shader - currentShader = vrConfig.distortionShader; -#else - currentShader = GetShaderDefault(); -#endif - rlEnableTexture(vrConfig.stereoFbo.texture.id); + // Draw RenderTexture (stereoFbo) using distortion shader if available + if (vrConfig.distortionShader.id > 0) currentShader = vrConfig.distortionShader; + else currentShader = GetShaderDefault(); + + rlEnableTexture(stereoFbo.texture.id); rlPushMatrix(); rlBegin(RL_QUADS); @@ -3696,15 +3695,15 @@ void EndVrDrawing(void) // Bottom-right corner for texture and quad rlTexCoord2f(0.0f, 0.0f); - rlVertex2f(0.0f, (float)vrConfig.stereoFbo.texture.height); + rlVertex2f(0.0f, (float)stereoFbo.texture.height); // Top-right corner for texture and quad rlTexCoord2f(1.0f, 0.0f); - rlVertex2f( (float)vrConfig.stereoFbo.texture.width, (float)vrConfig.stereoFbo.texture.height); + rlVertex2f( (float)stereoFbo.texture.width, (float)stereoFbo.texture.height); // Top-left corner for texture and quad rlTexCoord2f(1.0f, 1.0f); - rlVertex2f( (float)vrConfig.stereoFbo.texture.width, 0.0f); + rlVertex2f( (float)stereoFbo.texture.width, 0.0f); rlEnd(); rlPopMatrix(); @@ -4138,7 +4137,7 @@ static void DrawBuffersDefault(void) glUniformMatrix4fv(currentShader.locs[LOC_MATRIX_MVP], 1, false, MatrixToFloat(matMVP)); glUniform4f(currentShader.locs[LOC_COLOR_DIFFUSE], 1.0f, 1.0f, 1.0f, 1.0f); glUniform1i(currentShader.locs[LOC_MAP_DIFFUSE], 0); // Provided value refers to the texture unit (active) - + // TODO: Support additional texture units on custom shader //if (currentShader->locs[LOC_MAP_SPECULAR] > 0) glUniform1i(currentShader.locs[LOC_MAP_SPECULAR], 1); //if (currentShader->locs[LOC_MAP_NORMAL] > 0) glUniform1i(currentShader.locs[LOC_MAP_NORMAL], 2); @@ -4173,7 +4172,7 @@ static void DrawBuffersDefault(void) for (int i = 0; i < drawsCounter; i++) { glBindTexture(GL_TEXTURE_2D, draws[i].textureId); - + // TODO: Find some way to bind additional textures --> Use global texture IDs? Register them on draw[i]? //if (currentShader->locs[LOC_MAP_SPECULAR] > 0) { glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, textureUnit1_id); } //if (currentShader->locs[LOC_MAP_SPECULAR] > 0) { glActiveTexture(GL_TEXTURE2); glBindTexture(GL_TEXTURE_2D, textureUnit2_id); } @@ -4191,7 +4190,7 @@ static void DrawBuffersDefault(void) #endif } - vertexOffset += draws[i].vertexCount; + vertexOffset += (draws[i].vertexCount + draws[i].vertexAlignment); } if (!vaoSupported) @@ -4378,87 +4377,6 @@ static void GenDrawCube(void) } #if defined(SUPPORT_VR_SIMULATOR) -// Configure stereo rendering (including distortion shader) with HMD device parameters -static VrStereoConfig SetStereoConfig(VrDeviceInfo hmd, Shader distortion) -{ - VrStereoConfig config = { 0 }; - - // Initialize framebuffer and textures for stereo rendering - // NOTE: Screen size should match HMD aspect ratio - config.stereoFbo = rlLoadRenderTexture(screenWidth, screenHeight, UNCOMPRESSED_R8G8B8A8, 24, false); - - // Assign distortion shader - config.distortionShader = distortion; - - // Compute aspect ratio - float aspect = ((float)hmd.hResolution*0.5f)/(float)hmd.vResolution; - - // Compute lens parameters - float lensShift = (hmd.hScreenSize*0.25f - hmd.lensSeparationDistance*0.5f)/hmd.hScreenSize; - float leftLensCenter[2] = { 0.25f + lensShift, 0.5f }; - float rightLensCenter[2] = { 0.75f - lensShift, 0.5f }; - float leftScreenCenter[2] = { 0.25f, 0.5f }; - float rightScreenCenter[2] = { 0.75f, 0.5f }; - - // 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 lensRadiusSq = lensRadius*lensRadius; - float distortionScale = hmd.lensDistortionValues[0] + - hmd.lensDistortionValues[1]*lensRadiusSq + - hmd.lensDistortionValues[2]*lensRadiusSq*lensRadiusSq + - hmd.lensDistortionValues[3]*lensRadiusSq*lensRadiusSq*lensRadiusSq; - - TraceLog(LOG_DEBUG, "VR: Distortion Scale: %f", distortionScale); - - float normScreenWidth = 0.5f; - float normScreenHeight = 1.0f; - float scaleIn[2] = { 2.0f/normScreenWidth, 2.0f/normScreenHeight/aspect }; - float scale[2] = { normScreenWidth*0.5f/distortionScale, normScreenHeight*0.5f*aspect/distortionScale }; - - TraceLog(LOG_DEBUG, "VR: Distortion Shader: LeftLensCenter = { %f, %f }", leftLensCenter[0], leftLensCenter[1]); - TraceLog(LOG_DEBUG, "VR: Distortion Shader: RightLensCenter = { %f, %f }", rightLensCenter[0], rightLensCenter[1]); - TraceLog(LOG_DEBUG, "VR: Distortion Shader: Scale = { %f, %f }", scale[0], scale[1]); - TraceLog(LOG_DEBUG, "VR: Distortion Shader: ScaleIn = { %f, %f }", scaleIn[0], scaleIn[1]); - - // Fovy is normally computed with: 2*atan2(hmd.vScreenSize, 2*hmd.eyeToScreenDistance) - // ...but with lens distortion it is increased (see Oculus SDK Documentation) - //float fovy = 2.0f*atan2(hmd.vScreenSize*0.5f*distortionScale, hmd.eyeToScreenDistance); // Really need distortionScale? - float fovy = 2.0f*(float)atan2(hmd.vScreenSize*0.5f, hmd.eyeToScreenDistance); - - // Compute camera projection matrices - float projOffset = 4.0f*lensShift; // Scaled to projection space coordinates [-1..1] - Matrix proj = MatrixPerspective(fovy, aspect, 0.01, 1000.0); - config.eyesProjection[0] = MatrixMultiply(proj, MatrixTranslate(projOffset, 0.0f, 0.0f)); - config.eyesProjection[1] = MatrixMultiply(proj, MatrixTranslate(-projOffset, 0.0f, 0.0f)); - - // Compute camera transformation matrices - // NOTE: Camera movement might seem more natural if we model the head. - // Our axis of rotation is the base of our head, so we might want to add - // some y (base of head to eye level) and -z (center of head to eye protrusion) to the camera positions. - config.eyesViewOffset[0] = MatrixTranslate(-hmd.interpupillaryDistance*0.5f, 0.075f, 0.045f); - config.eyesViewOffset[1] = MatrixTranslate(hmd.interpupillaryDistance*0.5f, 0.075f, 0.045f); - - // Compute eyes Viewports - //config.eyeViewportRight[0] = (int[4]){ 0, 0, hmd.hResolution/2, hmd.vResolution }; - //config.eyeViewportLeft[0] = (int[4]){ hmd.hResolution/2, 0, hmd.hResolution/2, hmd.vResolution }; - -#if defined(SUPPORT_DISTORTION_SHADER) - // Update distortion shader with lens and distortion-scale parameters - SetShaderValue(config.distortionShader, GetShaderLocation(config.distortionShader, "leftLensCenter"), leftLensCenter, UNIFORM_VEC2); - SetShaderValue(config.distortionShader, GetShaderLocation(config.distortionShader, "rightLensCenter"), rightLensCenter, UNIFORM_VEC2); - SetShaderValue(config.distortionShader, GetShaderLocation(config.distortionShader, "leftScreenCenter"), leftScreenCenter, UNIFORM_VEC2); - SetShaderValue(config.distortionShader, GetShaderLocation(config.distortionShader, "rightScreenCenter"), rightScreenCenter, UNIFORM_VEC2); - - SetShaderValue(config.distortionShader, GetShaderLocation(config.distortionShader, "scale"), scale, UNIFORM_VEC2); - SetShaderValue(config.distortionShader, GetShaderLocation(config.distortionShader, "scaleIn"), scaleIn, UNIFORM_VEC2); - SetShaderValue(config.distortionShader, GetShaderLocation(config.distortionShader, "hmdWarpParam"), hmd.lensDistortionValues, UNIFORM_VEC4); - SetShaderValue(config.distortionShader, GetShaderLocation(config.distortionShader, "chromaAbParam"), hmd.chromaAbCorrection, UNIFORM_VEC4); -#endif - - return config; -} - // Set internal projection and modelview matrix depending on eyes tracking data static void SetStereoView(int eye, Matrix matProjection, Matrix matModelView) { diff --git a/src/shapes.c b/src/shapes.c index fd28f3a3b..f407f0d2c 100644 --- a/src/shapes.c +++ b/src/shapes.c @@ -14,7 +14,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2013-2019 Ramon Santamaria (@raysan5) * * 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. @@ -183,18 +183,42 @@ void DrawCircle(int centerX, int centerY, float radius, Color color) } // Draw a piece of a circle -// TODO: Support better angle resolution (now limited to CIRCLE_SECTOR_LENGTH) -void DrawCircleSector(Vector2 center, float radius, int startAngle, int endAngle, Color color) +void DrawCircleSector(Vector2 center, float radius, int startAngle, int endAngle, int segments, Color color) { - #define CIRCLE_SECTOR_LENGTH 10 + if (radius <= 0.0f) radius = 0.1f; // Avoid div by zero + + // Function expects (endAngle > startAngle) + if (endAngle < startAngle) + { + // Swap values + int tmp = startAngle; + startAngle = endAngle; + endAngle = tmp; + } + + if (segments < 4) + { + // Calculate how many segments we need to draw a smooth circle, taken from https://stackoverflow.com/a/2244088 + #define CIRCLE_ERROR_RATE 0.5f + + // Calculate the maximum angle between segments based on the error rate. + float th = acosf(2*powf(1 - CIRCLE_ERROR_RATE/radius, 2) - 1); + segments = (endAngle - startAngle)*ceilf(2*PI/th)/360; + + if (segments <= 0) segments = 4; + } + + float stepLength = (float)(endAngle - startAngle)/(float)segments; + float angle = startAngle; #if defined(SUPPORT_QUADS_DRAW_MODE) - if (rlCheckBufferLimit(4*((360/CIRCLE_SECTOR_LENGTH)/2))) rlglDraw(); + if (rlCheckBufferLimit(4*segments/2)) rlglDraw(); rlEnableTexture(GetShapesTexture().id); rlBegin(RL_QUADS); - for (int i = startAngle; i < endAngle; i += CIRCLE_SECTOR_LENGTH*2) + // NOTE: Every QUAD actually represents two segments + for (int i = 0; i < segments/2; i++) { rlColor4ub(color.r, color.g, color.b, color.a); @@ -202,33 +226,119 @@ void DrawCircleSector(Vector2 center, float radius, int startAngle, int endAngle rlVertex2f(center.x, center.y); rlTexCoord2f(recTexShapes.x/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height); - rlVertex2f(center.x + sinf(DEG2RAD*i)*radius, center.y + cosf(DEG2RAD*i)*radius); + rlVertex2f(center.x + sinf(DEG2RAD*angle)*radius, center.y + cosf(DEG2RAD*angle)*radius); rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height); - rlVertex2f(center.x + sinf(DEG2RAD*(i + CIRCLE_SECTOR_LENGTH))*radius, center.y + cosf(DEG2RAD*(i + CIRCLE_SECTOR_LENGTH))*radius); + rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*radius, center.y + cosf(DEG2RAD*(angle + stepLength))*radius); rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, recTexShapes.y/texShapes.height); - rlVertex2f(center.x + sinf(DEG2RAD*(i + CIRCLE_SECTOR_LENGTH*2))*radius, center.y + cosf(DEG2RAD*(i + CIRCLE_SECTOR_LENGTH*2))*radius); + rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength*2))*radius, center.y + cosf(DEG2RAD*(angle + stepLength*2))*radius); + + angle += (stepLength*2); + } + + // NOTE: In case number of segments is odd, we add one last piece to the cake + if (segments%2) + { + rlColor4ub(color.r, color.g, color.b, color.a); + + rlTexCoord2f(recTexShapes.x/texShapes.width, recTexShapes.y/texShapes.height); + rlVertex2f(center.x, center.y); + + rlTexCoord2f(recTexShapes.x/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height); + rlVertex2f(center.x + sinf(DEG2RAD*angle)*radius, center.y + cosf(DEG2RAD*angle)*radius); + + rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height); + rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*radius, center.y + cosf(DEG2RAD*(angle + stepLength))*radius); + + rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, recTexShapes.y/texShapes.height); + rlVertex2f(center.x, center.y); } rlEnd(); rlDisableTexture(); #else - if (rlCheckBufferLimit(3*((360/CIRCLE_SECTOR_LENGTH)/2))) rlglDraw(); + if (rlCheckBufferLimit(3*segments)) rlglDraw(); rlBegin(RL_TRIANGLES); - for (int i = startAngle; i < endAngle; i += CIRCLE_SECTOR_LENGTH) + for (int i = 0; i < segments; i++) { rlColor4ub(color.r, color.g, color.b, color.a); rlVertex2f(center.x, center.y); - rlVertex2f(center.x + sinf(DEG2RAD*i)*radius, center.y + cosf(DEG2RAD*i)*radius); - rlVertex2f(center.x + sinf(DEG2RAD*(i + CIRCLE_SECTOR_LENGTH))*radius, center.y + cosf(DEG2RAD*(i + CIRCLE_SECTOR_LENGTH))*radius); + rlVertex2f(center.x + sinf(DEG2RAD*angle)*radius, center.y + cosf(DEG2RAD*angle)*radius); + rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*radius, center.y + cosf(DEG2RAD*(angle + stepLength))*radius); + + angle += stepLength; } rlEnd(); #endif } +void DrawCircleSectorLines(Vector2 center, float radius, int startAngle, int endAngle, int segments, Color color) +{ + if (radius <= 0.0f) radius = 0.1f; // Avoid div by zero issue + + // Function expects (endAngle > startAngle) + if (endAngle < startAngle) + { + // Swap values + int tmp = startAngle; + startAngle = endAngle; + endAngle = tmp; + } + + if (segments < 4) + { + // Calculate how many segments we need to draw a smooth circle, taken from https://stackoverflow.com/a/2244088 + #ifndef CIRCLE_ERROR_RATE + #define CIRCLE_ERROR_RATE 0.5f + #endif + + // Calculate the maximum angle between segments based on the error rate. + float th = acosf(2*powf(1 - CIRCLE_ERROR_RATE/radius, 2) - 1); + segments = (endAngle - startAngle)*ceilf(2*PI/th)/360; + + if (segments <= 0) segments = 4; + } + + float stepLength = (float)(endAngle - startAngle)/(float)segments; + float angle = startAngle; + + // Hide the cap lines when the circle is full + bool showCapLines = true; + int limit = 2*(segments + 2); + if ((endAngle - startAngle)%360 == 0) { limit = 2*segments; showCapLines = false; } + + if (rlCheckBufferLimit(limit)) rlglDraw(); + + rlBegin(RL_LINES); + if (showCapLines) + { + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(center.x, center.y); + rlVertex2f(center.x + sinf(DEG2RAD*angle)*radius, center.y + cosf(DEG2RAD*angle)*radius); + } + + for (int i = 0; i < segments; i++) + { + rlColor4ub(color.r, color.g, color.b, color.a); + + rlVertex2f(center.x + sinf(DEG2RAD*angle)*radius, center.y + cosf(DEG2RAD*angle)*radius); + rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*radius, center.y + cosf(DEG2RAD*(angle + stepLength))*radius); + + angle += stepLength; + } + + if (showCapLines) + { + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(center.x, center.y); + rlVertex2f(center.x + sinf(DEG2RAD*angle)*radius, center.y + cosf(DEG2RAD*angle)*radius); + } + rlEnd(); +} + // Draw a gradient-filled circle // NOTE: Gradient goes from center (color1) to border (color2) void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Color color2) @@ -239,7 +349,7 @@ void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Co for (int i = 0; i < 360; i += 10) { rlColor4ub(color1.r, color1.g, color1.b, color1.a); - rlVertex2i(centerX, centerY); + rlVertex2f(centerX, centerY); rlColor4ub(color2.r, color2.g, color2.b, color2.a); rlVertex2f(centerX + sinf(DEG2RAD*i)*radius, centerY + cosf(DEG2RAD*i)*radius); rlColor4ub(color2.r, color2.g, color2.b, color2.a); @@ -252,7 +362,7 @@ void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Co // NOTE: On OpenGL 3.3 and ES2 we use QUADS to avoid drawing order issues (view rlglDraw) void DrawCircleV(Vector2 center, float radius, Color color) { - DrawCircleSector(center, radius, 0, 360, color); + DrawCircleSector(center, radius, 0, 360, 36, color); } // Draw circle outline @@ -272,6 +382,184 @@ void DrawCircleLines(int centerX, int centerY, float radius, Color color) rlEnd(); } +void DrawRing(Vector2 center, float innerRadius, float outerRadius, int startAngle, int endAngle, int segments, Color color) +{ + if (startAngle == endAngle) return; + + // Function expects (outerRadius > innerRadius) + if (outerRadius < innerRadius) + { + float tmp = outerRadius; + outerRadius = innerRadius; + innerRadius = tmp; + + if (outerRadius <= 0.0f) outerRadius = 0.1f; + } + + // Function expects (endAngle > startAngle) + if (endAngle < startAngle) + { + // Swap values + int tmp = startAngle; + startAngle = endAngle; + endAngle = tmp; + } + + if (segments < 4) + { + // Calculate how many segments we need to draw a smooth circle, taken from https://stackoverflow.com/a/2244088 + #ifndef CIRCLE_ERROR_RATE + #define CIRCLE_ERROR_RATE 0.5f + #endif + + // Calculate the maximum angle between segments based on the error rate. + float th = acosf(2*powf(1 - CIRCLE_ERROR_RATE/outerRadius, 2) - 1); + segments = (endAngle - startAngle)*ceilf(2*PI/th)/360; + + if (segments <= 0) segments = 4; + } + + // Not a ring + if (innerRadius <= 0.0f) + { + DrawCircleSector(center, outerRadius, startAngle, endAngle, segments, color); + return; + } + + float stepLength = (float)(endAngle - startAngle)/(float)segments; + float angle = startAngle; + +#if defined(SUPPORT_QUADS_DRAW_MODE) + if (rlCheckBufferLimit(4*segments)) rlglDraw(); + + rlEnableTexture(GetShapesTexture().id); + + rlBegin(RL_QUADS); + for (int i = 0; i < segments; i++) + { + rlColor4ub(color.r, color.g, color.b, color.a); + + rlTexCoord2f(recTexShapes.x/texShapes.width, recTexShapes.y/texShapes.height); + rlVertex2f(center.x + sinf(DEG2RAD*angle)*innerRadius, center.y + cosf(DEG2RAD*angle)*innerRadius); + + rlTexCoord2f(recTexShapes.x/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height); + rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius); + + rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height); + rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*outerRadius); + + rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, recTexShapes.y/texShapes.height); + rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*innerRadius); + + angle += stepLength; + } + rlEnd(); + + rlDisableTexture(); +#else + if (rlCheckBufferLimit(6*segments)) rlglDraw(); + + rlBegin(RL_TRIANGLES); + for (int i = 0; i < segments; i++) + { + rlColor4ub(color.r, color.g, color.b, color.a); + + rlVertex2f(center.x + sinf(DEG2RAD*angle)*innerRadius, center.y + cosf(DEG2RAD*angle)*innerRadius); + rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius); + rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*innerRadius); + + rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*innerRadius); + rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius); + rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*outerRadius); + + angle += stepLength; + } + rlEnd(); +#endif +} + +void DrawRingLines(Vector2 center, float innerRadius, float outerRadius, int startAngle, int endAngle, int segments, Color color) +{ + if (startAngle == endAngle) return; + + // Function expects (outerRadius > innerRadius) + if (outerRadius < innerRadius) + { + float tmp = outerRadius; + outerRadius = innerRadius; + innerRadius = tmp; + + if (outerRadius <= 0.0f) outerRadius = 0.1f; + } + + // Function expects (endAngle > startAngle) + if (endAngle < startAngle) + { + // Swap values + int tmp = startAngle; + startAngle = endAngle; + endAngle = tmp; + } + + if (segments < 4) + { + // Calculate how many segments we need to draw a smooth circle, taken from https://stackoverflow.com/a/2244088 + #ifndef CIRCLE_ERROR_RATE + #define CIRCLE_ERROR_RATE 0.5f + #endif + + // Calculate the maximum angle between segments based on the error rate. + float th = acosf(2*powf(1 - CIRCLE_ERROR_RATE/outerRadius, 2) - 1); + segments = (endAngle - startAngle)*ceilf(2*PI/th)/360; + + if (segments <= 0) segments = 4; + } + + if (innerRadius <= 0.0f) + { + DrawCircleSectorLines(center, outerRadius, startAngle, endAngle, segments, color); + return; + } + + float stepLength = (float)(endAngle - startAngle)/(float)segments; + float angle = startAngle; + + bool showCapLines = true; + int limit = 4*(segments + 1); + if ((endAngle - startAngle)%360 == 0) { limit = 4*segments; showCapLines = false; } + + if (rlCheckBufferLimit(limit)) rlglDraw(); + + rlBegin(RL_LINES); + if (showCapLines) + { + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius); + rlVertex2f(center.x + sinf(DEG2RAD*angle)*innerRadius, center.y + cosf(DEG2RAD*angle)*innerRadius); + } + + for (int i = 0; i < segments; i++) + { + rlColor4ub(color.r, color.g, color.b, color.a); + + rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius); + rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*outerRadius); + + rlVertex2f(center.x + sinf(DEG2RAD*angle)*innerRadius, center.y + cosf(DEG2RAD*angle)*innerRadius); + rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*innerRadius); + + angle += stepLength; + } + + if (showCapLines) + { + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius); + rlVertex2f(center.x + sinf(DEG2RAD*angle)*innerRadius, center.y + cosf(DEG2RAD*angle)*innerRadius); + } + rlEnd(); +} + // Draw a color-filled rectangle void DrawRectangle(int posX, int posY, int width, int height, Color color) { @@ -410,6 +698,413 @@ void DrawRectangleLinesEx(Rectangle rec, int lineThick, Color color) DrawRectangle( (int)rec.x, (int)(rec.y + lineThick), lineThick, (int)(rec.height - lineThick*2), color); } +// Draw rectangle with rounded edges +void DrawRectangleRounded(Rectangle rec, float roundness, int segments, Color color) +{ + // Not a rounded rectangle + if ((roundness <= 0.0f) || (rec.width < 1) || (rec.height < 1 )) + { + DrawRectangleRec(rec, color); + return; + } + + if (roundness >= 1.0f) roundness = 1.0f; + + // Calculate corner radius + float radius = (rec.width > rec.height)? (rec.height*roundness)/2 : (rec.width*roundness)/2; + if (radius <= 0.0f) return; + + // Calculate number of segments to use for the corners + if (segments < 4) + { + // Calculate how many segments we need to draw a smooth circle, taken from https://stackoverflow.com/a/2244088 + #ifndef CIRCLE_ERROR_RATE + #define CIRCLE_ERROR_RATE 0.5f + #endif + // Calculate the maximum angle between segments based on the error rate. + float th = acosf(2*powf(1 - CIRCLE_ERROR_RATE/radius, 2) - 1); + segments = ceilf(2*PI/th)/4; + if (segments <= 0) segments = 4; + } + + float stepLength = 90.0f/(float)segments; + + /* Quick sketch to make sense of all of this (there are 9 parts to draw, also mark the 12 points we'll use below) + * Not my best attempt at ASCII art, just preted it's rounded rectangle :) + * P0 P1 + * ____________________ + * /| |\ + * /1| 2 |3\ + *P7 /__|____________________|__\ P2 + * | |P8 P9| | + * | 8 | 9 | 4 | + * | __|____________________|__ | + *P6 \ |P11 P10| / P3 + * \7| 6 |5/ + * \|____________________|/ + * P5 P4 + */ + + const Vector2 point[12] = { // coordinates of the 12 points that define the rounded rect (the idea here is to make things easier) + {(float)rec.x + radius, rec.y}, {(float)(rec.x + rec.width) - radius, rec.y}, { rec.x + rec.width, (float)rec.y + radius }, // PO, P1, P2 + {rec.x + rec.width, (float)(rec.y + rec.height) - radius}, {(float)(rec.x + rec.width) - radius, rec.y + rec.height}, // P3, P4 + {(float)rec.x + radius, rec.y + rec.height}, { rec.x, (float)(rec.y + rec.height) - radius}, {rec.x, (float)rec.y + radius}, // P5, P6, P7 + {(float)rec.x + radius, (float)rec.y + radius}, {(float)(rec.x + rec.width) - radius, (float)rec.y + radius}, // P8, P9 + {(float)(rec.x + rec.width) - radius, (float)(rec.y + rec.height) - radius}, {(float)rec.x + radius, (float)(rec.y + rec.height) - radius} // P10, P11 + }; + + const Vector2 centers[4] = { point[8], point[9], point[10], point[11] }; + const float angles[4] = { 180.0f, 90.0f, 0.0f, 270.0f }; + +#if defined(SUPPORT_QUADS_DRAW_MODE) + if (rlCheckBufferLimit(16*segments/2 + 5*4)) rlglDraw(); + + rlBegin(RL_QUADS); + // Draw all of the 4 corners: [1] Upper Left Corner, [3] Upper Right Corner, [5] Lower Right Corner, [7] Lower Left Corner + for (int k = 0; k < 4; ++k) // Hope the compiler is smart enough to unroll this loop + { + float angle = angles[k]; + const Vector2 center = centers[k]; + // NOTE: Every QUAD actually represents two segments + for (int i = 0; i < segments/2; i++) + { + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(center.x, center.y); + rlVertex2f(center.x + sinf(DEG2RAD*angle)*radius, center.y + cosf(DEG2RAD*angle)*radius); + rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*radius, center.y + cosf(DEG2RAD*(angle + stepLength))*radius); + rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength*2))*radius, center.y + cosf(DEG2RAD*(angle + stepLength*2))*radius); + angle += (stepLength*2); + } + // NOTE: In case number of segments is odd, we add one last piece to the cake + if (segments%2) + { + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(center.x, center.y); + rlVertex2f(center.x + sinf(DEG2RAD*angle)*radius, center.y + cosf(DEG2RAD*angle)*radius); + rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*radius, center.y + cosf(DEG2RAD*(angle + stepLength))*radius); + rlVertex2f(center.x, center.y); + } + } + + // [2] Upper Rectangle + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(point[0].x, point[0].y); + rlVertex2f(point[8].x, point[8].y); + rlVertex2f(point[9].x, point[9].y); + rlVertex2f(point[1].x, point[1].y); + + // [4] Right Rectangle + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(point[2].x, point[2].y); + rlVertex2f(point[9].x, point[9].y); + rlVertex2f(point[10].x, point[10].y); + rlVertex2f(point[3].x, point[3].y); + + // [6] Bottom Rectangle + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(point[11].x, point[11].y); + rlVertex2f(point[5].x, point[5].y); + rlVertex2f(point[4].x, point[4].y); + rlVertex2f(point[10].x, point[10].y); + + // [8] Left Rectangle + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(point[7].x, point[7].y); + rlVertex2f(point[6].x, point[6].y); + rlVertex2f(point[11].x, point[11].y); + rlVertex2f(point[8].x, point[8].y); + + // [9] Middle Rectangle + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(point[8].x, point[8].y); + rlVertex2f(point[11].x, point[11].y); + rlVertex2f(point[10].x, point[10].y); + rlVertex2f(point[9].x, point[9].y); + + rlEnd(); +#else + if (rlCheckBufferLimit(12*segments + 5*6)) rlglDraw(); // 4 corners with 3 vertices per segment + 5 rectangles with 6 vertices each + + rlBegin(RL_TRIANGLES); + // Draw all of the 4 corners: [1] Upper Left Corner, [3] Upper Right Corner, [5] Lower Right Corner, [7] Lower Left Corner + for (int k = 0; k < 4; ++k) // Hope the compiler is smart enough to unroll this loop + { + float angle = angles[k]; + const Vector2 center = centers[k]; + for (int i = 0; i < segments; i++) + { + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(center.x, center.y); + rlVertex2f(center.x + sinf(DEG2RAD*angle)*radius, center.y + cosf(DEG2RAD*angle)*radius); + rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*radius, center.y + cosf(DEG2RAD*(angle + stepLength))*radius); + angle += stepLength; + } + } + + // [2] Upper Rectangle + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(point[0].x, point[0].y); + rlVertex2f(point[8].x, point[8].y); + rlVertex2f(point[9].x, point[9].y); + rlVertex2f(point[1].x, point[1].y); + rlVertex2f(point[0].x, point[0].y); + rlVertex2f(point[9].x, point[9].y); + + // [4] Right Rectangle + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(point[9].x, point[9].y); + rlVertex2f(point[10].x, point[10].y); + rlVertex2f(point[3].x, point[3].y); + rlVertex2f(point[2].x, point[2].y); + rlVertex2f(point[9].x, point[9].y); + rlVertex2f(point[3].x, point[3].y); + + // [6] Bottom Rectangle + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(point[11].x, point[11].y); + rlVertex2f(point[5].x, point[5].y); + rlVertex2f(point[4].x, point[4].y); + rlVertex2f(point[10].x, point[10].y); + rlVertex2f(point[11].x, point[11].y); + rlVertex2f(point[4].x, point[4].y); + + // [8] Left Rectangle + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(point[7].x, point[7].y); + rlVertex2f(point[6].x, point[6].y); + rlVertex2f(point[11].x, point[11].y); + rlVertex2f(point[8].x, point[8].y); + rlVertex2f(point[7].x, point[7].y); + rlVertex2f(point[11].x, point[11].y); + + // [9] Middle Rectangle + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(point[8].x, point[8].y); + rlVertex2f(point[11].x, point[11].y); + rlVertex2f(point[10].x, point[10].y); + rlVertex2f(point[9].x, point[9].y); + rlVertex2f(point[8].x, point[8].y); + rlVertex2f(point[10].x, point[10].y); + rlEnd(); +#endif +} + +// Draw rectangle with rounded edges outline +void DrawRectangleRoundedLines(Rectangle rec, float roundness, int segments, int lineThick, Color color) +{ + if (lineThick < 0) lineThick = 0; + + // Not a rounded rectangle + if (roundness <= 0.0f) + { + DrawRectangleLinesEx((Rectangle){rec.x-lineThick, rec.y-lineThick, rec.width+2*lineThick, rec.height+2*lineThick}, lineThick, color); + return; + } + + if (roundness >= 1.0f) roundness = 1.0f; + + // Calculate corner radius + float radius = (rec.width > rec.height)? (rec.height*roundness)/2 : (rec.width*roundness)/2; + if (radius <= 0.0f) return; + + // Calculate number of segments to use for the corners + if (segments < 4) + { + // Calculate how many segments we need to draw a smooth circle, taken from https://stackoverflow.com/a/2244088 + #ifndef CIRCLE_ERROR_RATE + #define CIRCLE_ERROR_RATE 0.5f + #endif + // Calculate the maximum angle between segments based on the error rate. + float th = acosf(2*powf(1 - CIRCLE_ERROR_RATE/radius, 2) - 1); + segments = ceilf(2*PI/th)/2; + if (segments <= 0) segments = 4; + } + + float stepLength = 90.0f/(float)segments; + const float outerRadius = radius + (float)lineThick, innerRadius = radius; + + /* Quick sketch to make sense of all of this (mark the 16 + 4(corner centers P16-19) points we'll use below) + * Not my best attempt at ASCII art, just preted it's rounded rectangle :) + * P0 P1 + * ==================== + * // P8 P9 \\ + * // \\ + *P7 // P15 P10 \\ P2 + * || *P16 P17* || + * || || + * || P14 P11 || + *P6 \\ *P19 P18* // P3 + * \\ // + * \\ P13 P12 // + * ==================== + * P5 P4 + */ + const Vector2 point[16] = { + {(float)rec.x + innerRadius, rec.y - lineThick}, {(float)(rec.x + rec.width) - innerRadius, rec.y - lineThick}, { rec.x + rec.width + lineThick, (float)rec.y + innerRadius }, // PO, P1, P2 + {rec.x + rec.width + lineThick, (float)(rec.y + rec.height) - innerRadius}, {(float)(rec.x + rec.width) - innerRadius, rec.y + rec.height + lineThick}, // P3, P4 + {(float)rec.x + innerRadius, rec.y + rec.height + lineThick}, { rec.x - lineThick, (float)(rec.y + rec.height) - innerRadius}, {rec.x - lineThick, (float)rec.y + innerRadius}, // P5, P6, P7 + {(float)rec.x + innerRadius, rec.y}, {(float)(rec.x + rec.width) - innerRadius, rec.y}, // P8, P9 + { rec.x + rec.width, (float)rec.y + innerRadius }, {rec.x + rec.width, (float)(rec.y + rec.height) - innerRadius}, // P10, P11 + {(float)(rec.x + rec.width) - innerRadius, rec.y + rec.height}, {(float)rec.x + innerRadius, rec.y + rec.height}, // P12, P13 + { rec.x, (float)(rec.y + rec.height) - innerRadius}, {rec.x, (float)rec.y + innerRadius} // P14, P15 + }; + + const Vector2 centers[4] = { + {(float)rec.x + innerRadius, (float)rec.y + innerRadius}, {(float)(rec.x + rec.width) - innerRadius, (float)rec.y + innerRadius}, // P16, P17 + {(float)(rec.x + rec.width) - innerRadius, (float)(rec.y + rec.height) - innerRadius}, {(float)rec.x + innerRadius, (float)(rec.y + rec.height) - innerRadius} // P18, P19 + }; + + const float angles[4] = { 180.0f, 90.0f, 0.0f, 270.0f }; + + if (lineThick > 1) + { +#if defined(SUPPORT_QUADS_DRAW_MODE) + if (rlCheckBufferLimit(4*4*segments + 4*4)) rlglDraw(); // 4 corners with 4 vertices for each segment + 4 rectangles with 4 vertices each + + rlBegin(RL_QUADS); + // Draw all of the 4 corners first: Upper Left Corner, Upper Right Corner, Lower Right Corner, Lower Left Corner + for (int k = 0; k < 4; ++k) // Hope the compiler is smart enough to unroll this loop + { + float angle = angles[k]; + const Vector2 center = centers[k]; + for (int i = 0; i < segments; i++) + { + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(center.x + sinf(DEG2RAD*angle)*innerRadius, center.y + cosf(DEG2RAD*angle)*innerRadius); + rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius); + rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*outerRadius); + rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*innerRadius); + + angle += stepLength; + } + } + + // Upper rectangle + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(point[0].x, point[0].y); + rlVertex2f(point[8].x, point[8].y); + rlVertex2f(point[9].x, point[9].y); + rlVertex2f(point[1].x, point[1].y); + + // Right rectangle + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(point[2].x, point[2].y); + rlVertex2f(point[10].x, point[10].y); + rlVertex2f(point[11].x, point[11].y); + rlVertex2f(point[3].x, point[3].y); + + // Lower rectangle + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(point[13].x, point[13].y); + rlVertex2f(point[5].x, point[5].y); + rlVertex2f(point[4].x, point[4].y); + rlVertex2f(point[12].x, point[12].y); + + // Left rectangle + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(point[15].x, point[15].y); + rlVertex2f(point[7].x, point[7].y); + rlVertex2f(point[6].x, point[6].y); + rlVertex2f(point[14].x, point[14].y); + + rlEnd(); +#else + if (rlCheckBufferLimit(4*6*segments + 4*6)) rlglDraw(); // 4 corners with 6(2*3) vertices for each segment + 4 rectangles with 6 vertices each + + rlBegin(RL_TRIANGLES); + + // Draw all of the 4 corners first: Upper Left Corner, Upper Right Corner, Lower Right Corner, Lower Left Corner + for (int k = 0; k < 4; ++k) // Hope the compiler is smart enough to unroll this loop + { + float angle = angles[k]; + const Vector2 center = centers[k]; + + for (int i = 0; i < segments; i++) + { + rlColor4ub(color.r, color.g, color.b, color.a); + + rlVertex2f(center.x + sinf(DEG2RAD*angle)*innerRadius, center.y + cosf(DEG2RAD*angle)*innerRadius); + rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius); + rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*innerRadius); + + rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*innerRadius); + rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius); + rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*outerRadius); + + angle += stepLength; + } + } + + // Upper rectangle + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(point[0].x, point[0].y); + rlVertex2f(point[8].x, point[8].y); + rlVertex2f(point[9].x, point[9].y); + rlVertex2f(point[1].x, point[1].y); + rlVertex2f(point[0].x, point[0].y); + rlVertex2f(point[9].x, point[9].y); + + // Right rectangle + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(point[10].x, point[10].y); + rlVertex2f(point[11].x, point[11].y); + rlVertex2f(point[3].x, point[3].y); + rlVertex2f(point[2].x, point[2].y); + rlVertex2f(point[10].x, point[10].y); + rlVertex2f(point[3].x, point[3].y); + + // Lower rectangle + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(point[13].x, point[13].y); + rlVertex2f(point[5].x, point[5].y); + rlVertex2f(point[4].x, point[4].y); + rlVertex2f(point[12].x, point[12].y); + rlVertex2f(point[13].x, point[13].y); + rlVertex2f(point[4].x, point[4].y); + + // Left rectangle + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(point[7].x, point[7].y); + rlVertex2f(point[6].x, point[6].y); + rlVertex2f(point[14].x, point[14].y); + rlVertex2f(point[15].x, point[15].y); + rlVertex2f(point[7].x, point[7].y); + rlVertex2f(point[14].x, point[14].y); + rlEnd(); +#endif + } + else + { + // Use LINES to draw the outline + if (rlCheckBufferLimit(8*segments + 4*2)) rlglDraw(); // 4 corners with 2 vertices for each segment + 4 rectangles with 2 vertices each + + rlBegin(RL_LINES); + + // Draw all of the 4 corners first: Upper Left Corner, Upper Right Corner, Lower Right Corner, Lower Left Corner + for (int k = 0; k < 4; ++k) // Hope the compiler is smart enough to unroll this loop + { + float angle = angles[k]; + const Vector2 center = centers[k]; + + for (int i = 0; i < segments; i++) + { + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius); + rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*outerRadius); + angle += stepLength; + } + } + // And now the remaining 4 lines + for(int i = 0; i < 8; i += 2) + { + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(point[i].x, point[i].y); + rlVertex2f(point[i + 1].x, point[i + 1].y); + } + rlEnd(); + } +} + // Draw a triangle void DrawTriangle(Vector2 v1, Vector2 v2, Vector2 v3, Color color) { diff --git a/src/text.c b/src/text.c index d44cdd11a..c07f807ae 100644 --- a/src/text.c +++ b/src/text.c @@ -17,7 +17,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2013-2019 Ramon Santamaria (@raysan5) * * 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. diff --git a/src/textures.c b/src/textures.c index 38995f116..7059fabb1 100644 --- a/src/textures.c +++ b/src/textures.c @@ -38,7 +38,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2013-2019 Ramon Santamaria (@raysan5) * * 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. @@ -181,7 +181,7 @@ static Image LoadASTC(const char *fileName); // Load ASTC file Image LoadImage(const char *fileName) { Image image = { 0 }; - + #if defined(SUPPORT_FILEFORMAT_PNG) || \ defined(SUPPORT_FILEFORMAT_BMP) || \ defined(SUPPORT_FILEFORMAT_TGA) || \ @@ -744,7 +744,7 @@ Image GetTextureData(Texture2D texture) RLAPI Image GetScreenData(void) { Image image = { 0 }; - + image.width = GetScreenWidth(); image.height = GetScreenHeight(); image.mipmaps = 1; @@ -1206,12 +1206,9 @@ TextureCubemap LoadTextureCubemap(Image image, int layoutType) cubemap.height = cubemap.width; } - int size = cubemap.width; - if (layoutType != CUBEMAP_AUTO_DETECT) { - //unsigned int dataSize = GetPixelDataSize(size, size, format); - //void *facesData = malloc(size*size*dataSize*6); // Get memory for 6 faces in a column + int size = cubemap.width; Image faces = { 0 }; // Vertical column image Rectangle faceRecs[6] = { 0 }; // Face source rectangles @@ -1225,6 +1222,7 @@ TextureCubemap LoadTextureCubemap(Image image, int layoutType) else if (layoutType == CUBEMAP_PANORAMA) { // TODO: Convert panorama image to square faces... + // Ref: https://github.com/denivip/panorama/blob/master/panorama.cpp } else { @@ -1411,13 +1409,13 @@ void ImageResizeNN(Image *image,int newWidth,int newHeight) void ImageResizeCanvas(Image *image, int newWidth,int newHeight, int offsetX, int offsetY, Color color) { // TODO: Review different scaling situations - + if ((newWidth != image->width) || (newHeight != image->height)) { if ((newWidth > image->width) && (newHeight > image->height)) { Image imTemp = GenImageColor(newWidth, newHeight, color); - + Rectangle srcRec = { 0.0f, 0.0f, (float)image->width, (float)image->height }; Rectangle dstRec = { (float)offsetX, (float)offsetY, (float)srcRec.width, (float)srcRec.height }; @@ -1434,23 +1432,23 @@ void ImageResizeCanvas(Image *image, int newWidth,int newHeight, int offsetX, in else // One side is bigger and the other is smaller { Image imTemp = GenImageColor(newWidth, newHeight, color); - + Rectangle srcRec = { 0.0f, 0.0f, (float)image->width, (float)image->height }; Rectangle dstRec = { (float)offsetX, (float)offsetY, (float)newWidth, (float)newHeight }; - + if (newWidth < image->width) { srcRec.x = offsetX; srcRec.width = newWidth; - + dstRec.x = 0.0f; } - + if (newHeight < image->height) { srcRec.y = offsetY; srcRec.height = newHeight; - + dstRec.y = 0.0f; } diff --git a/src/utils.c b/src/utils.c index 6b174354d..10cce9b92 100644 --- a/src/utils.c +++ b/src/utils.c @@ -11,7 +11,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * * 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. diff --git a/src/utils.h b/src/utils.h index 08b33962f..d7ab88294 100644 --- a/src/utils.h +++ b/src/utils.h @@ -5,7 +5,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * * 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. diff --git a/templates/advance_game/Makefile b/templates/advance_game/Makefile index 2e5d6e263..e475f72c5 100644 --- a/templates/advance_game/Makefile +++ b/templates/advance_game/Makefile @@ -2,7 +2,7 @@ # # raylib makefile for Desktop platforms, Raspberry Pi, Android and HTML5 # -# Copyright (c) 2013-2018 Ramon Santamaria (@raysan5) +# Copyright (c) 2013-2019 Ramon Santamaria (@raysan5) # # 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. diff --git a/templates/advance_game/advance_game.c b/templates/advance_game/advance_game.c index 48a34f6d7..3fb5d6571 100644 --- a/templates/advance_game/advance_game.c +++ b/templates/advance_game/advance_game.c @@ -8,7 +8,7 @@ * This game has been created using raylib (www.raylib.com) * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/templates/advance_game/screens/screen_ending.c b/templates/advance_game/screens/screen_ending.c index 66b5ddf9f..62d1267a3 100644 --- a/templates/advance_game/screens/screen_ending.c +++ b/templates/advance_game/screens/screen_ending.c @@ -4,7 +4,7 @@ * * Ending Screen Functions Definitions (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * * 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. diff --git a/templates/advance_game/screens/screen_gameplay.c b/templates/advance_game/screens/screen_gameplay.c index 8943adb5a..8ea614911 100644 --- a/templates/advance_game/screens/screen_gameplay.c +++ b/templates/advance_game/screens/screen_gameplay.c @@ -4,7 +4,7 @@ * * Gameplay Screen Functions Definitions (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * * 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. diff --git a/templates/advance_game/screens/screen_logo.c b/templates/advance_game/screens/screen_logo.c index 6282e83ed..8badbf52b 100644 --- a/templates/advance_game/screens/screen_logo.c +++ b/templates/advance_game/screens/screen_logo.c @@ -4,7 +4,7 @@ * * Logo Screen Functions Definitions (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * * 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. diff --git a/templates/advance_game/screens/screen_options.c b/templates/advance_game/screens/screen_options.c index dc8d74fa2..4b58da133 100644 --- a/templates/advance_game/screens/screen_options.c +++ b/templates/advance_game/screens/screen_options.c @@ -4,7 +4,7 @@ * * Options Screen Functions Definitions (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * * 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. diff --git a/templates/advance_game/screens/screen_title.c b/templates/advance_game/screens/screen_title.c index 5727546a3..8e74a0270 100644 --- a/templates/advance_game/screens/screen_title.c +++ b/templates/advance_game/screens/screen_title.c @@ -4,7 +4,7 @@ * * Title Screen Functions Definitions (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * * 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. diff --git a/templates/advance_game/screens/screens.h b/templates/advance_game/screens/screens.h index 4d7f9b538..7c2dfb48a 100644 --- a/templates/advance_game/screens/screens.h +++ b/templates/advance_game/screens/screens.h @@ -4,7 +4,7 @@ * * Screens Functions Declarations (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * * 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. diff --git a/templates/simple_game/Makefile b/templates/simple_game/Makefile index 86e0dbde7..9f27a429a 100644 --- a/templates/simple_game/Makefile +++ b/templates/simple_game/Makefile @@ -2,7 +2,7 @@ # # raylib makefile for Desktop platforms, Raspberry Pi, Android and HTML5 # -# Copyright (c) 2013-2018 Ramon Santamaria (@raysan5) +# Copyright (c) 2013-2019 Ramon Santamaria (@raysan5) # # 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. diff --git a/templates/simple_game/simple_game.c b/templates/simple_game/simple_game.c index 028b1da89..8f2dc36a0 100644 --- a/templates/simple_game/simple_game.c +++ b/templates/simple_game/simple_game.c @@ -8,7 +8,7 @@ * This game has been created using raylib (www.raylib.com) * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/templates/standard_game/Makefile b/templates/standard_game/Makefile index b9c6c188e..4333c8b84 100644 --- a/templates/standard_game/Makefile +++ b/templates/standard_game/Makefile @@ -2,7 +2,7 @@ # # raylib makefile for Desktop platforms, Raspberry Pi, Android and HTML5 # -# Copyright (c) 2013-2018 Ramon Santamaria (@raysan5) +# Copyright (c) 2013-2019 Ramon Santamaria (@raysan5) # # 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. diff --git a/templates/standard_game/screens/screen_ending.c b/templates/standard_game/screens/screen_ending.c index 871969779..1bd5ce988 100644 --- a/templates/standard_game/screens/screen_ending.c +++ b/templates/standard_game/screens/screen_ending.c @@ -4,7 +4,7 @@ * * Ending Screen Functions Definitions (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * * 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. diff --git a/templates/standard_game/screens/screen_gameplay.c b/templates/standard_game/screens/screen_gameplay.c index 7f108265a..427e5ba7d 100644 --- a/templates/standard_game/screens/screen_gameplay.c +++ b/templates/standard_game/screens/screen_gameplay.c @@ -4,7 +4,7 @@ * * Gameplay Screen Functions Definitions (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * * 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. diff --git a/templates/standard_game/screens/screen_logo.c b/templates/standard_game/screens/screen_logo.c index c0b60571a..435ea2418 100644 --- a/templates/standard_game/screens/screen_logo.c +++ b/templates/standard_game/screens/screen_logo.c @@ -4,7 +4,7 @@ * * Logo Screen Functions Definitions (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * * 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. diff --git a/templates/standard_game/screens/screen_options.c b/templates/standard_game/screens/screen_options.c index 9f6690d1a..df68dd266 100644 --- a/templates/standard_game/screens/screen_options.c +++ b/templates/standard_game/screens/screen_options.c @@ -4,7 +4,7 @@ * * Options Screen Functions Definitions (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * * 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. diff --git a/templates/standard_game/screens/screen_title.c b/templates/standard_game/screens/screen_title.c index 328448bac..5bd338258 100644 --- a/templates/standard_game/screens/screen_title.c +++ b/templates/standard_game/screens/screen_title.c @@ -4,7 +4,7 @@ * * Title Screen Functions Definitions (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * * 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. diff --git a/templates/standard_game/screens/screens.h b/templates/standard_game/screens/screens.h index e961b5338..9450c29f1 100644 --- a/templates/standard_game/screens/screens.h +++ b/templates/standard_game/screens/screens.h @@ -4,7 +4,7 @@ * * Screens Functions Declarations (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * * 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. diff --git a/templates/standard_game/standard_game.c b/templates/standard_game/standard_game.c index 8871484e2..7918562c1 100644 --- a/templates/standard_game/standard_game.c +++ b/templates/standard_game/standard_game.c @@ -8,7 +8,7 @@ * This game has been created using raylib (www.raylib.com) * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * ********************************************************************************************/