h-raylib 4.5.3.4 → 4.6.0.1
raw patch · 20 files changed
+1315/−153 lines, 20 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
- Raylib.Types: (|*|) :: Vector a => a -> Float -> a
- Raylib.Types: (|+|) :: Vector a => a -> a -> a
- Raylib.Types: (|-|) :: Vector a => a -> a -> a
- Raylib.Types: (|.|) :: Vector a => a -> a -> Float
- Raylib.Types: (|/|) :: Vector a => a -> Float -> a
- Raylib.Types: asList :: Vector a => a -> [Float]
- Raylib.Types: class Vector a
- Raylib.Types: cross :: Vector3 -> Vector3 -> Vector3
- Raylib.Types: instance Raylib.Types.Vector Raylib.Types.Vector2
- Raylib.Types: instance Raylib.Types.Vector Raylib.Types.Vector3
- Raylib.Types: instance Raylib.Types.Vector Raylib.Types.Vector4
- Raylib.Types: inverse :: Vector a => a -> a
- Raylib.Types: magnitude :: Vector a => a -> Float
- Raylib.Types: normalize :: Vector a => a -> a
- Raylib.Types: zero :: Vector a => a
+ Raylib.Util: class Freeable a
+ Raylib.Util: rlFree :: Freeable a => a -> Ptr a -> IO ()
+ Raylib.Util: rlFreeDependents :: Freeable a => a -> Ptr a -> IO ()
+ Raylib.Util.Camera: cameraMove :: Camera -> Vector3 -> Camera
+ Raylib.Util.Camera: cameraMoveForward :: Camera -> Float -> Bool -> Camera
+ Raylib.Util.Camera: cameraMoveRight :: Camera -> Float -> Bool -> Camera
+ Raylib.Util.Camera: cameraMoveUp :: Camera -> Float -> Camera
+ Raylib.Util.Camera: cameraPitch :: Camera -> Float -> Bool -> Bool -> Bool -> Camera
+ Raylib.Util.Camera: cameraRoll :: Camera -> Float -> Camera
+ Raylib.Util.Camera: cameraRotate :: Camera -> Vector3 -> Float -> Bool -> Camera
+ Raylib.Util.Camera: cameraYaw :: Camera -> Float -> Bool -> Camera
+ Raylib.Util.Camera: getCameraForward :: Camera -> Vector3
+ Raylib.Util.Camera: getCameraProjectionMatrix :: Camera -> Float -> Float -> Float -> Matrix
+ Raylib.Util.Camera: getCameraRight :: Camera -> Vector3
+ Raylib.Util.Camera: getCameraUp :: Camera -> Vector3
+ Raylib.Util.Camera: getCameraViewMatrix :: Camera -> Matrix
+ Raylib.Util.Math: (/*/) :: Matrix -> Matrix -> Matrix
+ Raylib.Util.Math: (/+/) :: Matrix -> Matrix -> Matrix
+ Raylib.Util.Math: (/-/) :: Matrix -> Matrix -> Matrix
+ Raylib.Util.Math: (|*) :: Vector a => a -> Float -> a
+ Raylib.Util.Math: (|*|) :: Vector a => a -> a -> a
+ Raylib.Util.Math: (|+) :: Vector a => a -> Float -> a
+ Raylib.Util.Math: (|+|) :: Vector a => a -> a -> a
+ Raylib.Util.Math: (|-) :: Vector a => a -> Float -> a
+ Raylib.Util.Math: (|-|) :: Vector a => a -> a -> a
+ Raylib.Util.Math: (|.|) :: Vector a => a -> a -> Float
+ Raylib.Util.Math: (|/) :: Vector a => a -> Float -> a
+ Raylib.Util.Math: (|/|) :: Vector a => a -> a -> a
+ Raylib.Util.Math: additiveInverse :: Vector a => a -> a
+ Raylib.Util.Math: asList :: Vector a => a -> [Float]
+ Raylib.Util.Math: clamp :: Float -> Float -> Float -> Float
+ Raylib.Util.Math: class Vector a
+ Raylib.Util.Math: constant :: Vector a => Float -> a
+ Raylib.Util.Math: deg2Rad :: Float
+ Raylib.Util.Math: epsilon :: Float
+ Raylib.Util.Math: floatEquals :: Float -> Float -> Bool
+ Raylib.Util.Math: fromList :: Vector a => [Float] -> a
+ Raylib.Util.Math: instance Raylib.Util.Math.Vector Raylib.Types.Vector2
+ Raylib.Util.Math: instance Raylib.Util.Math.Vector Raylib.Types.Vector3
+ Raylib.Util.Math: instance Raylib.Util.Math.Vector Raylib.Types.Vector4
+ Raylib.Util.Math: lerp :: Float -> Float -> Float -> Float
+ Raylib.Util.Math: magnitude :: Vector a => a -> Float
+ Raylib.Util.Math: magnitudeSqr :: Vector a => a -> Float
+ Raylib.Util.Math: matrixAdd :: Matrix -> Matrix -> Matrix
+ Raylib.Util.Math: matrixConstant :: Float -> Matrix
+ Raylib.Util.Math: matrixDeterminant :: Matrix -> Float
+ Raylib.Util.Math: matrixFromList :: [Float] -> Matrix
+ Raylib.Util.Math: matrixFrustum :: Float -> Float -> Float -> Float -> Float -> Float -> Matrix
+ Raylib.Util.Math: matrixIdentity :: Matrix
+ Raylib.Util.Math: matrixInvert :: Matrix -> Matrix
+ Raylib.Util.Math: matrixLookAt :: Vector3 -> Vector3 -> Vector3 -> Matrix
+ Raylib.Util.Math: matrixMultiply :: Matrix -> Matrix -> Matrix
+ Raylib.Util.Math: matrixOrtho :: Float -> Float -> Float -> Float -> Float -> Float -> Matrix
+ Raylib.Util.Math: matrixPerspective :: Float -> Float -> Float -> Float -> Matrix
+ Raylib.Util.Math: matrixRotate :: Vector3 -> Float -> Matrix
+ Raylib.Util.Math: matrixRotateX :: Float -> Matrix
+ Raylib.Util.Math: matrixRotateXYZ :: Vector3 -> Matrix
+ Raylib.Util.Math: matrixRotateY :: Float -> Matrix
+ Raylib.Util.Math: matrixRotateZ :: Float -> Matrix
+ Raylib.Util.Math: matrixRotateZYX :: Vector3 -> Matrix
+ Raylib.Util.Math: matrixScale :: Vector3 -> Matrix
+ Raylib.Util.Math: matrixSubtract :: Matrix -> Matrix -> Matrix
+ Raylib.Util.Math: matrixToList :: Matrix -> [Float]
+ Raylib.Util.Math: matrixTrace :: Matrix -> Float
+ Raylib.Util.Math: matrixTranslate :: Float -> Float -> Float -> Matrix
+ Raylib.Util.Math: matrixTranspose :: Matrix -> Matrix
+ Raylib.Util.Math: multiplicativeInverse :: Vector a => a -> a
+ Raylib.Util.Math: normalize :: Float -> Float -> Float -> Float
+ Raylib.Util.Math: one :: Vector a => a
+ Raylib.Util.Math: quaternionFromAxisAngle :: Vector3 -> Float -> Quaternion
+ Raylib.Util.Math: quaternionFromEuler :: Float -> Float -> Float -> Quaternion
+ Raylib.Util.Math: quaternionFromMatrix :: Matrix -> Quaternion
+ Raylib.Util.Math: quaternionFromVector3ToVector3 :: Vector3 -> Vector3 -> Quaternion
+ Raylib.Util.Math: quaternionIdentity :: Quaternion
+ Raylib.Util.Math: quaternionInvert :: Quaternion -> Quaternion
+ Raylib.Util.Math: quaternionLerp :: Quaternion -> Quaternion -> Float -> Quaternion
+ Raylib.Util.Math: quaternionMultiply :: Quaternion -> Quaternion -> Quaternion
+ Raylib.Util.Math: quaternionNLerp :: Quaternion -> Quaternion -> Float -> Quaternion
+ Raylib.Util.Math: quaternionNormalize :: Quaternion -> Quaternion
+ Raylib.Util.Math: quaternionSLerp :: Quaternion -> Quaternion -> Float -> Quaternion
+ Raylib.Util.Math: quaternionToAxisAngle :: Quaternion -> (Vector3, Float)
+ Raylib.Util.Math: quaternionToEuler :: Quaternion -> Vector3
+ Raylib.Util.Math: quaternionToMatrix :: Quaternion -> Matrix
+ Raylib.Util.Math: quaternionTransform :: Quaternion -> Matrix -> Quaternion
+ Raylib.Util.Math: rad2Deg :: Float
+ Raylib.Util.Math: remap :: Float -> Float -> Float -> Float -> Float -> Float
+ Raylib.Util.Math: vector2Angle :: Vector2 -> Vector2 -> Float
+ Raylib.Util.Math: vector2LineAngle :: Vector2 -> Vector2 -> Float
+ Raylib.Util.Math: vector2Reflect :: Vector2 -> Vector2 -> Vector2
+ Raylib.Util.Math: vector2Rotate :: Vector2 -> Float -> Vector2
+ Raylib.Util.Math: vector2Transform :: Vector2 -> Matrix -> Vector2
+ Raylib.Util.Math: vector3Angle :: Vector3 -> Vector3 -> Float
+ Raylib.Util.Math: vector3Barycenter :: Vector3 -> (Vector3, Vector3, Vector3) -> Vector3
+ Raylib.Util.Math: vector3CrossProduct :: Vector3 -> Vector3 -> Vector3
+ Raylib.Util.Math: vector3OrthoNormalize :: Vector3 -> Vector3 -> (Vector3, Vector3)
+ Raylib.Util.Math: vector3Perpendicular :: Vector3 -> Vector3
+ Raylib.Util.Math: vector3Reflect :: Vector3 -> Vector3 -> Vector3
+ Raylib.Util.Math: vector3Refract :: Vector3 -> Vector3 -> Float -> Vector3
+ Raylib.Util.Math: vector3RotateByAxisAngle :: Vector3 -> Vector3 -> Float -> Vector3
+ Raylib.Util.Math: vector3RotateByQuaternion :: Vector3 -> Quaternion -> Vector3
+ Raylib.Util.Math: vector3Transform :: Vector3 -> Matrix -> Vector3
+ Raylib.Util.Math: vector3Unproject :: Vector3 -> Matrix -> Matrix -> Vector3
+ Raylib.Util.Math: vectorClamp :: Vector a => a -> a -> a -> a
+ Raylib.Util.Math: vectorClampValue :: Vector a => a -> Float -> Float -> a
+ Raylib.Util.Math: vectorDistance :: Vector a => a -> a -> Float
+ Raylib.Util.Math: vectorDistanceSqr :: Vector a => a -> a -> Float
+ Raylib.Util.Math: vectorLerp :: Vector a => a -> a -> Float -> a
+ Raylib.Util.Math: vectorMax :: Vector a => a -> a -> a
+ Raylib.Util.Math: vectorMin :: Vector a => a -> a -> a
+ Raylib.Util.Math: vectorMoveTowards :: Vector a => a -> a -> Float -> a
+ Raylib.Util.Math: vectorNormalize :: Vector a => a -> a
+ Raylib.Util.Math: wrap :: Float -> Float -> Float -> Float
+ Raylib.Util.Math: zero :: Vector a => a
Files
- CHANGELOG.md +9/−0
- CONTRIBUTING.md +3/−5
- README.md +5/−0
- ROADMAP.md +3/−3
- h-raylib.cabal +3/−1
- lib/rl_bindings.c +18/−3
- raylib/examples/core/core_2d_camera_platformer.c +1/−1
- raylib/src/external/qoaplay.c +2/−2
- raylib/src/external/rl_gputex.h +0/−1
- raylib/src/raudio.c +1/−1
- raylib/src/raylib.h +3/−3
- raylib/src/raymath.h +7/−5
- raylib/src/rcore.c +10/−10
- raylib/src/rmodels.c +1/−1
- raylib/src/rtext.c +38/−26
- raylib/src/rtextures.c +4/−1
- src/Raylib/Types.hs +11/−87
- src/Raylib/Util.hs +4/−3
- src/Raylib/Util/Camera.hs +187/−0
- src/Raylib/Util/Math.hs +1005/−0
CHANGELOG.md view
@@ -1,5 +1,14 @@ # h-raylib changelog +## Version 4.6.0.1 +_2 April, 2023_ + +- Created the `Raylib.Util.Math` and `Raylib.Util.Camera` modules. They are Haskell implementations of `raymath` and `rcamera`. + +\[[#15](https://github.com/Anut-py/h-raylib/pull/15)\] + +- Fixed a memory issue with `getFontDefault` + ## Version 4.5.3.4 _19 March, 2023_
CONTRIBUTING.md view
@@ -12,7 +12,7 @@ _This section only contains h-raylib specific information. For information about raylib in general, view the [raylib wiki](https://github.com/raysan5/raylib/wiki)._ -This project is split into 10 public modules. `Raylib.Types` contains all of raylib's types and low-level code to convert them to and from raw bytes. `Raylib.Util` contains miscellaneous utility functions. `Raylib.Util.Colors` contains some colors defined by raylib. The other 8 public modules, `Raylib.Core`, `Raylib.Core.Camera`, `Raylib.Core.Shapes`, `Raylib.Core.Textures`, `Raylib.Core.Text`, `Raylib.Core.Models`, `Raylib.Core.Audio`, and `Raylib.Util.RLGL`, correspond to their respective raylib modules. +This project is split into 12 public modules. `Raylib.Types` contains all of raylib's types and low-level code to convert them to and from raw bytes. `Raylib.Util` contains miscellaneous utility functions. `Raylib.Util.Colors` contains some colors defined by raylib. The other 10 public modules, `Raylib.Core`, `Raylib.Core.Camera`, `Raylib.Core.Shapes`, `Raylib.Core.Textures`, `Raylib.Core.Text`, `Raylib.Core.Models`, `Raylib.Core.Audio`, `Raylib.Util.Camera`, `Raylib.Util.Math`, and `Raylib.Util.RLGL`, correspond to their respective raylib modules. The functions in h-raylib are an almost one-to-one mapping to their corresponding raylib functions. The types are, in some cases, slightly modified if it is possible to utilize Haskell features. @@ -20,12 +20,10 @@ ### Raylib.Types -`Raylib.Types` has 4 sections: one for enumerations, one for typeclasses, one for structures, and one for callbacks. +`Raylib.Types` has 3 sections: one for enumerations, one for structures, and one for callbacks. The enumerations section contains Haskell sum types that are instances of `Enum`. Each of these types corresponds to a raylib `enum` or set of `define`s. The `fromEnum` and `toEnum` functions for these types use the numbers associated with these values in the C `enum`s. Most of these types are instances of `Storable` so they can be converted to raw bytes and passed to a C function. _NOTE: Some of these Haskell types correspond to C `enum`s that are in C source files, rather than header files._ -The typeclasses section contains Haskell typeclasses that are derived by some of the types in the structures section. - The structures section contains Haskell types that correspond to each of raylib's `structs`. Each field in these types is named `typeName'fieldName` (e.g. the C struct `Vector2`'s `x` field is called `vector2'x` in Haskell). These structs also all derive the typeclass `Freeable` (declared in the internal `Raylib.ForeignUtil` module). This typeclass allows types to describe how to properly free all the data associated with a pointer to that type. For example, `Image`'s implementation of `Freeable` also frees the pointer stored in the `Image.data` field in C. Finally, all of these types derive `Storable`, obviously, to convert them to and from pointers. The callbacks section contains `FunPtr` types that are passed to some functions. _NOTE: These callbacks are very unlikely to be used, so they may be removed in the future._ @@ -38,7 +36,7 @@ `Raylib.Util.Colors` is very simple: it declares 26 colors defined in `raylib.h`, namely `lightGray`, `gray`, `darkGray`, `yellow`, `gold`, `orange`, `pink`, `red`, `maroon`, `green`, `lime`, `darkGreen`, `skyBlue`, `blue`, `darkBlue`, `purple`, `violet`, `darkPurple`, `beige`, `brown`, `darkBrown`, `white`, `black`, `blank`, `magenta`, and `rayWhite`. -### The other 8 modules +### The other 10 modules These modules contain only functions. Each of these functions corresponds to a C function.
README.md view
@@ -78,6 +78,11 @@ To contribute or learn more about the project, see [CONTRIBUTING.md](https://github.com/Anut-py/h-raylib/blob/master/CONTRIBUTING.md). +## FAQ + +- When I try to run an h-raylib program I get an error saying "*The code execution cannot proceed because libwinpthread-1.dll was not found. Reinstalling the program may fix this problem.*" + - See [#14](https://github.com/Anut-py/h-raylib/issues/14) + ## License This project is licensed under the Apache License 2.0. See more in `LICENSE`.
ROADMAP.md view
@@ -3,12 +3,12 @@ Items higher on the list have higher priority. ## Uncompleted -- Bind `rcamera` -- Bind `raymath` - Bind `raygui` - Add web build support ## Completed - Make it easier to pass shader parameters (Added in `4.5.3.0`) - Allow manual unloading of assets for larger projects (Added in `4.5.3.1`) -- Bind `rlgl` (Not published to Hackage) +- Bind `rlgl` (Added in `4.5.3.2`) +- Bind `rcamera` (Not published to hackage) +- Bind `raymath` (Not published to hackage)
h-raylib.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.4 name: h-raylib -version: 4.5.3.4 +version: 4.6.0.1 synopsis: Raylib bindings for Haskell category: graphics description: @@ -153,7 +153,9 @@ Raylib.Core.Textures Raylib.Types Raylib.Util + Raylib.Util.Camera Raylib.Util.Colors + Raylib.Util.Math Raylib.Util.RLGL other-modules:
lib/rl_bindings.c view
@@ -976,9 +976,24 @@ Font *GetFontDefault_() { - Font *ptr = (Font *)malloc(sizeof(Font)); - *ptr = GetFontDefault(); - return ptr; + Font defaultFont = GetFontDefault(); + Font *defaultFontCopy = (Font *)malloc(sizeof(Font)); + + defaultFontCopy->baseSize = defaultFont.baseSize; + defaultFontCopy->glyphCount = defaultFont.glyphCount; + defaultFontCopy->glyphPadding = defaultFont.glyphPadding; + defaultFontCopy->texture = defaultFont.texture; + + defaultFontCopy->glyphs = malloc(sizeof(GlyphInfo) * defaultFont.glyphCount); + defaultFontCopy->recs = malloc(sizeof(Rectangle) * defaultFont.glyphCount); + + for (int i = 0; i < defaultFont.glyphCount; i++) { + defaultFontCopy->glyphs[i] = defaultFont.glyphs[i]; + defaultFontCopy->glyphs[i].image = ImageCopy(defaultFont.glyphs[i].image); + defaultFontCopy->recs[i] = defaultFont.recs[i]; + } + + return defaultFontCopy; } Font *LoadFont_(char *a)
raylib/examples/core/core_2d_camera_platformer.c view
@@ -90,7 +90,7 @@ "Follow player center", "Follow player center, but clamp to map edges", "Follow player center; smoothed", - "Follow player center horizontally; updateplayer center vertically after landing", + "Follow player center horizontally; update player center vertically after landing", "Player push camera on getting too close to screen edge" };
raylib/src/external/qoaplay.c view
@@ -62,7 +62,7 @@ extern "C" { // Prevents name mangling of functions #endif -qoaplay_desc *qoaplay_open(char *path); +qoaplay_desc *qoaplay_open(const char *path); qoaplay_desc *qoaplay_open_memory(const unsigned char *data, int data_size); void qoaplay_close(qoaplay_desc *qoa_ctx); @@ -83,7 +83,7 @@ //---------------------------------------------------------------------------------- // Open QOA file, keep FILE pointer to keep reading from file -qoaplay_desc *qoaplay_open(char *path) +qoaplay_desc *qoaplay_open(const char *path) { FILE *file = fopen(path, "rb"); if (!file) return NULL;
raylib/src/external/rl_gputex.h view
@@ -814,5 +814,4 @@ return data_size; } - #endif // RL_GPUTEX_IMPLEMENTATION
raylib/src/raudio.c view
@@ -1287,7 +1287,7 @@ #if defined(SUPPORT_FILEFORMAT_QOA) else if (IsFileExtension(fileName, ".qoa")) { - qoaplay_desc *ctxQoa = qoaplay_open((char *)fileName); + qoaplay_desc *ctxQoa = qoaplay_open(fileName); music.ctxType = MUSIC_AUDIO_QOA; music.ctxData = ctxQoa;
raylib/src/raylib.h view
@@ -1,6 +1,6 @@ /********************************************************************************************** * -* raylib v4.5 - A simple and easy-to-use library to enjoy videogames programming (www.raylib.com) +* raylib v4.6-dev - A simple and easy-to-use library to enjoy videogames programming (www.raylib.com) * * FEATURES: * - NO external dependencies, all required libraries included with raylib @@ -82,9 +82,9 @@ #include <stdarg.h> // Required for: va_list - Only used by TraceLogCallback #define RAYLIB_VERSION_MAJOR 4 -#define RAYLIB_VERSION_MINOR 5 +#define RAYLIB_VERSION_MINOR 6 #define RAYLIB_VERSION_PATCH 0 -#define RAYLIB_VERSION "4.5" +#define RAYLIB_VERSION "4.6-dev" // Function specifiers in case library is build/used as a shared library (Windows) // NOTE: Microsoft specifiers to tell compiler that symbols are imported/exported from a .dll
raylib/src/raymath.h view
@@ -703,12 +703,14 @@ Vector3 result = v; float length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z); - if (length == 0.0f) length = 1.0f; - float ilength = 1.0f/length; + if (length != 0.0f) + { + float ilength = 1.0f/length; - result.x *= ilength; - result.y *= ilength; - result.z *= ilength; + result.x *= ilength; + result.y *= ilength; + result.z *= ilength; + } return result; }
raylib/src/rcore.c view
@@ -595,14 +595,14 @@ "ACTION_SETTARGETFPS" }; -// Automation Event (20 bytes) +// Automation Event (24 bytes) typedef struct AutomationEvent { unsigned int frame; // Event frame - unsigned int type; // Event type (AutoEventType) - int params[3]; // Event parameters (if required) + unsigned int type; // Event type (AutomationEventType) + int params[4]; // Event parameters (if required) } AutomationEvent; -static AutomationEvent *events = NULL; // Events array +static AutomationEvent *events = NULL; // Events array static unsigned int eventCount = 0; // Events count static bool eventsPlaying = false; // Play events static bool eventsRecording = false; // Record events @@ -925,7 +925,7 @@ #endif #if defined(SUPPORT_EVENTS_AUTOMATION) - events = (AutomationEvent *)malloc(MAX_CODE_AUTOMATION_EVENTS*sizeof(AutomationEvent)); + events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); CORE.Time.frameCounter = 0; #endif @@ -1073,7 +1073,7 @@ #endif #if defined(SUPPORT_EVENTS_AUTOMATION) - free(events); + RL_FREE(events); #endif CORE.Window.ready = false; @@ -6945,11 +6945,11 @@ // TODO: This system should probably be redesigned static void LoadAutomationEvents(const char *fileName) { - //unsigned char fileId[4] = { 0 }; - - // Load binary + // Load events file (binary) /* FILE *repFile = fopen(fileName, "rb"); + unsigned char fileId[4] = { 0 }; + fread(fileId, 1, 4, repFile); if ((fileId[0] == 'r') && (fileId[1] == 'E') && (fileId[2] == 'P') && (fileId[1] == ' ')) @@ -6962,7 +6962,7 @@ fclose(repFile); */ - // Load events (text file) + // Load events file (text) FILE *repFile = fopen(fileName, "rt"); if (repFile != NULL)
raylib/src/rmodels.c view
@@ -3400,7 +3400,7 @@ { if (mesh->vboId[SHADER_LOC_VERTEX_TANGENT] != 0) { - // Upate existing vertex buffer + // Update existing vertex buffer rlUpdateVertexBuffer(mesh->vboId[SHADER_LOC_VERTEX_TANGENT], mesh->tangents, mesh->vertexCount*4*sizeof(float), 0); } else
raylib/src/rtext.c view
@@ -694,14 +694,25 @@ // NOTE: Rectangles memory is loaded here! Rectangle *recs = (Rectangle *)RL_MALLOC(glyphCount*sizeof(Rectangle)); - // Calculate image size based on required pixel area - // NOTE 1: Image is forced to be squared and POT... very conservative! - // NOTE 2: SDF font characters already contain an internal padding, - // so image size would result bigger than default font type - float requiredArea = 0; - for (int i = 0; i < glyphCount; i++) requiredArea += ((chars[i].image.width + 2*padding)*(fontSize + 2*padding)); - float guessSize = sqrtf(requiredArea)*1.4f; - int imageSize = (int)powf(2, ceilf(logf((float)guessSize)/logf(2))); // Calculate next POT + // Calculate image size based on total glyph width and glyph row count + int totalWidth = 0; + int maxGlyphWidth = 0; + + for (int i = 0; i < glyphCount; i++) + { + if (chars[i].image.width > maxGlyphWidth) maxGlyphWidth = chars[i].image.width; + totalWidth += chars[i].image.width + 2*padding; + } + + int rowCount = 0; + int imageSize = 64; // Define minimum starting value to avoid unnecessary calculation steps for very small images + + // NOTE: maxGlyphWidth is maximum possible space left at the end of row + while (totalWidth > (imageSize - maxGlyphWidth)*rowCount) + { + imageSize *= 2; // Double the size of image (to keep POT) + rowCount = imageSize/(fontSize + 2*padding); // Calculate new row count for the new image size + } atlas.width = imageSize; // Atlas bitmap width atlas.height = imageSize; // Atlas bitmap height @@ -720,24 +731,7 @@ // NOTE: Using simple packaging, one char after another for (int i = 0; i < glyphCount; i++) { - // Copy pixel data from fc.data to atlas - for (int y = 0; y < chars[i].image.height; y++) - { - for (int x = 0; x < chars[i].image.width; x++) - { - ((unsigned char *)atlas.data)[(offsetY + y)*atlas.width + (offsetX + x)] = ((unsigned char *)chars[i].image.data)[y*chars[i].image.width + x]; - } - } - - // Fill chars rectangles in atlas info - recs[i].x = (float)offsetX; - recs[i].y = (float)offsetY; - recs[i].width = (float)chars[i].image.width; - recs[i].height = (float)chars[i].image.height; - - // Move atlas position X for next character drawing - offsetX += (chars[i].image.width + 2*padding); - + // Check remaining space for glyph if (offsetX >= (atlas.width - chars[i].image.width - 2*padding)) { offsetX = padding; @@ -761,6 +755,24 @@ break; } } + + // Copy pixel data from fc.data to atlas + for (int y = 0; y < chars[i].image.height; y++) + { + for (int x = 0; x < chars[i].image.width; x++) + { + ((unsigned char *)atlas.data)[(offsetY + y)*atlas.width + (offsetX + x)] = ((unsigned char *)chars[i].image.data)[y*chars[i].image.width + x]; + } + } + + // Fill chars rectangles in atlas info + recs[i].x = (float)offsetX; + recs[i].y = (float)offsetY; + recs[i].width = (float)chars[i].image.width; + recs[i].height = (float)chars[i].image.height; + + // Move atlas position X for next character drawing + offsetX += (chars[i].image.width + 2*padding); } } else if (packMethod == 1) // Use Skyline rect packing algorithm (stb_pack_rect)
raylib/src/rtextures.c view
@@ -219,7 +219,7 @@ //---------------------------------------------------------------------------------- // Other Modules Functions Declaration (required by text) //---------------------------------------------------------------------------------- -// ... +extern void LoadFontDefault(void); // [Module: text] Loads default font, required by ImageDrawText() //---------------------------------------------------------------------------------- // Module specific Functions Declaration @@ -3152,6 +3152,9 @@ void ImageDrawText(Image *dst, const char *text, int posX, int posY, int fontSize, Color color) { #if defined(SUPPORT_MODULE_RTEXT) + // Make sure default font is loaded to be used on image text drawing + if (GetFontDefault().texture.id == 0) LoadFontDefault(); + Vector2 position = { (float)posX, (float)posY }; // NOTE: For default font, spacing is set to desired font size / default font size (10) ImageDrawTextEx(dst, GetFontDefault(), text, position, (float)fontSize, (float)fontSize/10, color); // WARNING: Module required: rtext
src/Raylib/Types.hs view
@@ -577,24 +577,24 @@ -- I don't know if there's a cleaner way to do this unpackShaderUniformData :: ShaderUniformData -> IO (ShaderUniformDataType, Ptr ()) -unpackShaderUniformData x = do - case x of +unpackShaderUniformData u = do + case u of (ShaderUniformFloat f) -> do ptr <- malloc poke ptr (realToFrac f :: CFloat) return (ShaderUniformFloatType, castPtr ptr) - (ShaderUniformVec2 v) -> + (ShaderUniformVec2 (Vector2 x y)) -> do - ptr <- newArray (map realToFrac (asList v) :: [CFloat]) + ptr <- newArray (map realToFrac [x, y] :: [CFloat]) return (ShaderUniformVec2Type, castPtr ptr) - (ShaderUniformVec3 v) -> + (ShaderUniformVec3 (Vector3 x y z)) -> do - ptr <- newArray (map realToFrac (asList v) :: [CFloat]) + ptr <- newArray (map realToFrac [x, y, z] :: [CFloat]) return (ShaderUniformVec3Type, castPtr ptr) - (ShaderUniformVec4 v) -> + (ShaderUniformVec4 (Vector4 x y z w)) -> do - ptr <- newArray (map realToFrac (asList v) :: [CFloat]) + ptr <- newArray (map realToFrac [x, y, z, w] :: [CFloat]) return (ShaderUniformVec4Type, castPtr ptr) (ShaderUniformInt i) -> do @@ -628,15 +628,15 @@ return (ShaderUniformFloatType, castPtr ptr, length fs) (ShaderUniformVec2V vs) -> do - ptr <- newArray (map realToFrac $ concatMap asList vs :: [CFloat]) + ptr <- newArray (map realToFrac $ concatMap (\(Vector2 x y) -> [x, y]) vs :: [CFloat]) return (ShaderUniformVec2Type, castPtr ptr, length vs) (ShaderUniformVec3V vs) -> do - ptr <- newArray (map realToFrac $ concatMap asList vs :: [CFloat]) + ptr <- newArray (map realToFrac $ concatMap (\(Vector3 x y z) -> [x, y, z]) vs :: [CFloat]) return (ShaderUniformVec3Type, castPtr ptr, length vs) (ShaderUniformVec4V vs) -> do - ptr <- newArray (map realToFrac $ concatMap asList vs :: [CFloat]) + ptr <- newArray (map realToFrac $ concatMap (\(Vector4 x y z w) -> [x, y, z, w]) vs :: [CFloat]) return (ShaderUniformVec4Type, castPtr ptr, length vs) (ShaderUniformIntV is) -> do @@ -1551,45 +1551,6 @@ poke ptr v = poke (castPtr ptr) (fromIntegral (fromEnum v) :: CInt) ------------------------------------------------ --- Raylib typeclasses -------------------------- ------------------------------------------------- - -class Vector a where - -- List representation of the vector - asList :: a -> [Float] - - -- Vector-vector addition - (|+|) :: a -> a -> a - - -- Vector-vector subtraction - (|-|) :: a -> a -> a - v1 |-| v2 = v1 |+| inverse v2 - - -- Vector-scalar multiplication - (|*|) :: a -> Float -> a - - -- Vector-scalar division - (|/|) :: a -> Float -> a - v |/| num = v |*| (1 / num) - - -- Vector-vector dot product - (|.|) :: a -> a -> Float - - -- Zero vector - zero :: a - - -- Vector additive inverse - inverse :: a -> a - - -- Normalize vector (same direction, magnitude 1) - normalize :: a -> a - normalize v = v |/| magnitude v - - -- Vector magnitude - magnitude :: a -> Float - magnitude x = sqrt $ x |.| x - ------------------------------------------------- -- Raylib structures --------------------------- ------------------------------------------------ @@ -1613,17 +1574,6 @@ pokeByteOff _p 4 (realToFrac y :: CFloat) return () -instance Vector Vector2 where - asList (Vector2 x y) = [x, y] - - (Vector2 x1 y1) |+| (Vector2 x2 y2) = Vector2 (x1 + x2) (y1 + y2) - (Vector2 x y) |*| num = Vector2 (x * num) (y * num) - - (Vector2 x1 y1) |.| (Vector2 x2 y2) = (x1 * x2) + (y1 * y2) - - zero = Vector2 0 0 - inverse (Vector2 x y) = Vector2 (- x) (- y) - data Vector3 = Vector3 { vector3'x :: Float, vector3'y :: Float, @@ -1645,21 +1595,6 @@ pokeByteOff _p 8 (realToFrac z :: CFloat) return () --- Vector cross-product -cross :: Vector3 -> Vector3 -> Vector3 -(Vector3 x1 y1 z1) `cross` (Vector3 x2 y2 z2) = Vector3 (y1 * z2 - z1 * y2) (z1 * x2 - x1 * z2) (x1 * y2 - y1 * x2) - -instance Vector Vector3 where - asList (Vector3 x y z) = [x, y, z] - - (Vector3 x1 y1 z1) |+| (Vector3 x2 y2 z2) = Vector3 (x1 + x2) (y1 + y2) (z1 + z2) - (Vector3 x y z) |*| num = Vector3 (x * num) (y * num) (z * num) - - (Vector3 x1 y1 z1) |.| (Vector3 x2 y2 z2) = (x1 * x2) + (y1 * y2) + (z1 * z2) - - zero = Vector3 0 0 0 - inverse (Vector3 x y z) = Vector3 (- x) (- y) (- z) - data Vector4 = Vector4 { vector4'x :: Float, vector4'y :: Float, @@ -1683,17 +1618,6 @@ pokeByteOff _p 8 (realToFrac z :: CFloat) pokeByteOff _p 12 (realToFrac w :: CFloat) return () - -instance Vector Vector4 where - asList (Vector4 x y z w) = [x, y, z, w] - - (Vector4 x1 y1 z1 w1) |+| (Vector4 x2 y2 z2 w2) = Vector4 (x1 + x2) (y1 + y2) (z1 + z2) (w1 + w2) - (Vector4 x y z w) |*| num = Vector4 (x * num) (y * num) (z * num) (w * num) - - (Vector4 x1 y1 z1 w1) |.| (Vector4 x2 y2 z2 w2) = (x1 * x2) + (y1 * y2) + (z1 * z2) + (w1 * w2) - - zero = Vector4 0 0 0 0 - inverse (Vector4 x y z w) = Vector4 (- x) (- y) (- z) (- w) type Quaternion = Vector4
src/Raylib/Util.hs view
@@ -1,10 +1,11 @@ {-# OPTIONS -Wall #-} {-# LANGUAGE CPP #-} -module Raylib.Util (WindowResources, cameraDirectionRay, whileWindowOpen, whileWindowOpen_, whileWindowOpen0, setMaterialShader, inGHCi) where +module Raylib.Util (WindowResources, cameraDirectionRay, whileWindowOpen, whileWindowOpen_, whileWindowOpen0, setMaterialShader, inGHCi, Freeable (..)) where import Control.Monad (void) import Raylib.Core (windowShouldClose) +import Raylib.ForeignUtil (Freeable (..)) import Raylib.Internal (WindowResources) import Raylib.Types ( Camera3D (camera3D'position, camera3D'target), @@ -12,12 +13,12 @@ Model (model'materials), Ray (Ray), Shader, - Vector (normalize, (|-|)), ) +import Raylib.Util.Math (Vector (vectorNormalize, (|-|))) -- | Gets the direction of a camera as a ray. cameraDirectionRay :: Camera3D -> Ray -cameraDirectionRay camera = Ray (camera3D'position camera) (normalize $ camera3D'target camera |-| camera3D'position camera) +cameraDirectionRay camera = Ray (camera3D'position camera) (vectorNormalize $ camera3D'target camera |-| camera3D'position camera) -- | Calls the game loop every frame as long as the window is open. -- For larger projects, instead of using this function, consider
+ src/Raylib/Util/Camera.hs view
@@ -0,0 +1,187 @@+{-# OPTIONS -Wall #-} + +module Raylib.Util.Camera + ( getCameraForward, + getCameraUp, + getCameraRight, + cameraMove, + cameraMoveForward, + cameraMoveUp, + cameraMoveRight, + cameraRotate, + cameraYaw, + cameraPitch, + cameraRoll, + getCameraViewMatrix, + getCameraProjectionMatrix, + ) +where + +import Raylib.Types (Camera, Camera3D (..), CameraProjection (CameraOrthographic, CameraPerspective), Matrix, Vector3 (..)) +import Raylib.Util.Math (Vector (..), clamp, deg2Rad, matrixLookAt, matrixOrtho, matrixPerspective, vector3Angle, vector3CrossProduct, vector3RotateByAxisAngle) + +-- | The camera's forward vector (normalized) +getCameraForward :: Camera -> Vector3 +getCameraForward cam = vectorNormalize $ camera3D'target cam |-| camera3D'position cam + +-- | The camera's up vector (normalized) +getCameraUp :: Camera -> Vector3 +getCameraUp cam = vectorNormalize $ camera3D'up cam + +-- | The camera's right vector (normalized) +getCameraRight :: Camera -> Vector3 +getCameraRight cam = vector3CrossProduct (getCameraForward cam) (getCameraUp cam) + +-- | Move the camera by a specific vector +cameraMove :: Camera -> Vector3 -> Camera +cameraMove cam dir = + cam {camera3D'position = camera3D'position cam |+| dir, camera3D'target = camera3D'target cam |+| dir} + +-- | Move the camera in its forward direction +cameraMoveForward :: + Camera -> + -- | Distance to move + Float -> + -- | Move in world plane (i.e. no vertical movement if enabled) + Bool -> + Camera +cameraMoveForward cam distance moveInWorldPlane = + cameraMove cam (forward |* distance) + where + forward = if moveInWorldPlane then camForward {vector3'y = 0} else camForward + camForward = getCameraForward cam + +-- | Move the camera in its up direction +cameraMoveUp :: + Camera -> + -- | Distance to move + Float -> + Camera +cameraMoveUp cam distance = + cameraMove cam (up |* distance) + where + up = getCameraUp cam + +-- | Move the camera in its right direction +cameraMoveRight :: + Camera -> + -- | Distance to move + Float -> + -- | Move in world plane (i.e. no vertical movement if enabled) + Bool -> + Camera +cameraMoveRight cam distance moveInWorldPlane = + cameraMove cam (right |* distance) + where + right = if moveInWorldPlane then camRight {vector3'y = 0} else camRight + camRight = getCameraRight cam + +-- | Rotate the camera using an axis and angle +cameraRotate :: + Camera -> + -- | Axis of rotation + Vector3 -> + -- | Angle to rotate by + Float -> + -- | Rotate around target (if false, the camera rotates around its position) + Bool -> + Camera +cameraRotate cam axis angle rotateAroundTarget = + cam + { camera3D'position = if rotateAroundTarget then target |-| viewRot else pos, + camera3D'target = if rotateAroundTarget then target else pos |+| viewRot + } + where + viewVec = target |-| pos + viewRot = vector3RotateByAxisAngle viewVec axis angle + pos = camera3D'position cam + target = camera3D'target cam + +-- | Rotate the camera around its up vector. +-- Yaw is "looking left and right". +cameraYaw :: + Camera -> + -- | Angle in radians + Float -> + -- | Rotate around target (if false, the camera rotates around its position) + Bool -> + Camera +cameraYaw cam angle rotateAroundTarget = + cam + { camera3D'position = if rotateAroundTarget then target |-| viewRot else pos, + camera3D'target = if rotateAroundTarget then target else pos |+| viewRot + } + where + viewVec = target |-| pos + viewRot = vector3RotateByAxisAngle viewVec (getCameraUp cam) angle + pos = camera3D'position cam + target = camera3D'target cam + +-- | Rotate the camera around its right vector. +-- Pitch is "looking up and down". +cameraPitch :: + Camera -> + -- | Angle in radians + Float -> + -- | Lock view (prevents camera overrotation, aka "somersaults") + Bool -> + -- | Rotate around target (if false, the camera rotates around its position) + Bool -> + -- | Rotate the camera's up vector to match the new pitch + Bool -> + Camera +cameraPitch cam angle lockView rotateAroundTarget rotateUp = + cam + { camera3D'position = if rotateAroundTarget then target |-| viewRot else pos, + camera3D'target = if rotateAroundTarget then target else pos |+| viewRot, + camera3D'up = if not rotateUp then up else vector3RotateByAxisAngle up right angle' + } + where + angle' = if not lockView then angle else clamp angle maxAngleDown maxAngleUp + maxAngleUp = vector3Angle up viewVec - 0.001 + maxAngleDown = (- vector3Angle (additiveInverse up) viewVec) - 0.001 + + viewVec = target |-| pos + viewRot = vector3RotateByAxisAngle viewVec right angle' + + pos = camera3D'position cam + target = camera3D'target cam + up = getCameraUp cam + right = getCameraRight cam + +-- | Rotates the camera around its forward vector. +-- Roll is "turning your head sideways to the left or right". +cameraRoll :: + Camera -> + -- | Angle in radians + Float -> + Camera +cameraRoll cam angle = + cam + { camera3D'up = vector3RotateByAxisAngle up forward angle + } + where + forward = getCameraForward cam + up = getCameraUp cam + +-- | View matrix from camera +getCameraViewMatrix :: Camera -> Matrix +getCameraViewMatrix cam = matrixLookAt (camera3D'position cam) (camera3D'target cam) (camera3D'up cam) + +-- | Projection matrix from camera +getCameraProjectionMatrix :: + Camera -> + -- | Aspect ratio + Float -> + -- | Near clipping plane distance (recommended: 0.01) + Float -> + -- | Far clipping plane distance (recommended: 1000) + Float -> + Matrix +getCameraProjectionMatrix cam aspect near far = + case camera3D'projection cam of + CameraPerspective -> matrixPerspective (camera3D'fovy cam * deg2Rad) aspect near far + CameraOrthographic -> matrixOrtho (- right) right (- top) top near far + where + top = camera3D'fovy cam / 2 + right = top * aspect
+ src/Raylib/Util/Math.hs view
@@ -0,0 +1,1005 @@+{-# OPTIONS -Wall #-} + +module Raylib.Util.Math + ( -- * Utility constants + epsilon, + deg2Rad, + rad2Deg, + + -- * Float math + clamp, + lerp, + normalize, + remap, + wrap, + floatEquals, + + -- * General vector math + Vector (..), + + -- * Vector2 math + vector2Angle, + vector2LineAngle, + vector2Transform, + vector2Reflect, + vector2Rotate, + + -- * Vector3 math + vector3CrossProduct, + vector3Perpendicular, + vector3Angle, + vector3OrthoNormalize, + vector3Transform, + vector3RotateByQuaternion, + vector3RotateByAxisAngle, + vector3Reflect, + vector3Barycenter, + vector3Unproject, + vector3Refract, + + -- * Matrix math + matrixToList, + matrixFromList, + matrixConstant, + matrixDeterminant, + matrixTrace, + matrixTranspose, + matrixInvert, + matrixIdentity, + matrixAdd, + (/+/), + matrixSubtract, + (/-/), + matrixMultiply, + (/*/), + matrixTranslate, + matrixRotate, + matrixRotateX, + matrixRotateY, + matrixRotateZ, + matrixRotateXYZ, + matrixRotateZYX, + matrixScale, + matrixFrustum, + matrixPerspective, + matrixOrtho, + matrixLookAt, + + -- * Quaternion math + quaternionIdentity, + quaternionInvert, + quaternionMultiply, + quaternionNormalize, + quaternionLerp, + quaternionNLerp, + quaternionSLerp, + quaternionFromVector3ToVector3, + quaternionFromMatrix, + quaternionToMatrix, + quaternionFromAxisAngle, + quaternionToAxisAngle, + quaternionFromEuler, + quaternionToEuler, + quaternionTransform, + ) +where + +import Raylib.Types (Matrix (..), Quaternion, Vector2 (Vector2), Vector3 (Vector3), Vector4 (Vector4)) + +epsilon :: Float +epsilon = 0.000001 + +deg2Rad :: Float +deg2Rad = pi / 180 + +rad2Deg :: Float +rad2Deg = 180 / pi + +------------------------------------------------ +-- Float math ---------------------------------- +------------------------------------------------ + +-- | Clamp float to range +clamp :: + -- | Value to clamp + Float -> + -- | Lower bound + Float -> + -- | Upper bound + Float -> + Float +clamp value low high + | value < low = low + | value > high = high + | otherwise = 0 + +-- | Calculate linear interpolation between two floats +lerp :: + -- | Starting value + Float -> + -- | Ending value + Float -> + -- | Lerp amount + Float -> + Float +lerp start end amount = start + amount * (end - start) + +-- | Normalize input value within input range +normalize :: + -- | Value to normalize + Float -> + -- | Starting value of range + Float -> + -- | Ending value of range + Float -> + Float +normalize value start end = (value - start) / (end - start) + +-- | Remap input value within input range to output range +remap :: + -- | Input value + Float -> + -- | Input range start + Float -> + -- | Input range end + Float -> + -- | Output range start + Float -> + -- | Output range end + Float -> + Float +remap value inputStart inputEnd outputStart outputEnd = (value - inputStart) / (inputEnd - inputStart) * (outputEnd - outputStart) + outputStart + +-- | Wrap input value from min to max +wrap :: + -- | Input value + Float -> + -- | Min value + Float -> + -- | Max value + Float -> + Float +wrap value low high = value - (high - low) * fromIntegral (floor ((value - low) / (high - low)) :: Integer) + +-- | Check if two floats are close to equal +floatEquals :: Float -> Float -> Bool +floatEquals x y = abs (x - y) <= (epsilon * max 1 (max (abs x) (abs y))) + +------------------------------------------------ +-- Vector math --------------------------------- +------------------------------------------------ + +class Vector a where + -- | List representation of a vector + asList :: a -> [Float] + + -- | Vector representation of a list + fromList :: [Float] -> a + + -- | Vector-vector addition + (|+|) :: a -> a -> a + a |+| b = fromList $ zipWith (+) (asList a) (asList b) + + -- | Vector-vector subtraction + (|-|) :: a -> a -> a + a |-| b = fromList $ zipWith (-) (asList a) (asList b) + + -- | Vector-scalar addition + (|+) :: a -> Float -> a + a |+ b = a |+| constant b + + -- | Vector-scalar subtraction + (|-) :: a -> Float -> a + a |- b = a |-| constant b + + -- | Vector-vector multiplication + (|*|) :: a -> a -> a + a |*| b = fromList $ zipWith (*) (asList a) (asList b) + + -- | Vector-vector division + (|/|) :: a -> a -> a + a |/| b = fromList $ zipWith (/) (asList a) (asList b) + + -- | Vector-scalar multiplication + (|*) :: a -> Float -> a + a |* b = a |*| constant b + + -- | Vector-scalar division + (|/) :: a -> Float -> a + a |/ b = a |/| constant b + + -- | Vector-vector dot product + (|.|) :: a -> a -> Float + a |.| b = sum . asList $ a |*| b + + -- | Zero vector + zero :: a + zero = constant 0 + + -- | One vector + one :: a + one = constant 1 + + -- | Scalar to vector (all elements are set to the scalar) + constant :: Float -> a + constant val = fromList $ repeat val + + -- | Vector additive inverse + additiveInverse :: a -> a + additiveInverse v = fromList $ map negate (asList v) + + -- | Vector multiplicative inverse + multiplicativeInverse :: a -> a + multiplicativeInverse v = fromList $ map (1 /) (asList v) + + -- | Squared magnitude of a vector + magnitudeSqr :: a -> Float + magnitudeSqr v = v |.| v + + -- | Vector magnitude + magnitude :: a -> Float + magnitude v = if m == 1 then m else sqrt m + where + m = magnitudeSqr v + + -- | Squared distance between two vectors + vectorDistanceSqr :: a -> a -> Float + vectorDistanceSqr a b = magnitudeSqr $ a |-| b + + -- | Distance between two vectors + vectorDistance :: a -> a -> Float + vectorDistance a b = sqrt $ vectorDistanceSqr a b + + -- | Normalize vector (same direction, magnitude 1) + vectorNormalize :: a -> a + vectorNormalize v = v |/ magnitude v + + -- | Lerp between two vectors + vectorLerp :: a -> a -> Float -> a + vectorLerp a b amount = fromList $ zipWith (\v1 v2 -> lerp v1 v2 amount) (asList a) (asList b) + + -- | Move vector towards target + vectorMoveTowards :: + -- | Vector to move + a -> + -- | Target vector + a -> + -- | Max distance to move by + Float -> + a + vectorMoveTowards v target maxDistance = + if distSquared <= maxDistance * maxDistance + then target + else v |+| fromList (map (* (maxDistance / dist)) (asList diff)) + where + diff = target |-| v + distSquared = magnitudeSqr diff + dist = sqrt distSquared + + -- | Clamp vector to range + vectorClamp :: + -- | Vector to clamp + a -> + -- | Lower bound + a -> + -- | Upper bound + a -> + a + vectorClamp v low high = fromList $ zipWith3 clamp (asList v) (asList low) (asList high) + + -- | Clamp the magnitude of a vector to a range + vectorClampValue :: + -- | Vector to clamp + a -> + -- | Lower bound + Float -> + -- | Upper bound + Float -> + a + vectorClampValue v low high = v |* (clamp size low high / size) + where + size = magnitude v + + -- | Min value for each pair of components + vectorMin :: a -> a -> a + vectorMin v1 v2 = fromList $ zipWith min (asList v1) (asList v2) + + -- | Max value for each pair of components + vectorMax :: a -> a -> a + vectorMax v1 v2 = fromList $ zipWith max (asList v1) (asList v2) + +instance Vector Vector2 where + asList (Vector2 x y) = [x, y] + fromList (x : y : _) = Vector2 x y + fromList _ = error "(Vector2.fromList) Input list must have at least two elements!" + +instance Vector Vector3 where + asList (Vector3 x y z) = [x, y, z] + fromList (x : y : z : _) = Vector3 x y z + fromList _ = error "(Vector3.fromList) Input list must have at least three elements!" + +instance Vector Vector4 where + asList (Vector4 x y z w) = [x, y, z, w] + fromList (x : y : z : w : _) = Vector4 x y z w + fromList _ = error "(Vector4.fromList) Input list must have at least four elements!" + +------------------------------------------------ +-- Vector2 math -------------------------------- +------------------------------------------------ + +-- | Angle between two 2D vectors +vector2Angle :: Vector2 -> Vector2 -> Float +vector2Angle (Vector2 x1 y1) (Vector2 x2 y2) = atan2 (y2 - y1) (x2 - x1) + +-- | Angle created by the line between two 2D vectors (parameters must be normalized) +vector2LineAngle :: Vector2 -> Vector2 -> Float +vector2LineAngle a b = acos $ clamp (a |.| b) (-1) 1 + +-- | Transform a 2D vector by the given matrix +vector2Transform :: Vector2 -> Matrix -> Vector2 +vector2Transform (Vector2 x y) mat = Vector2 ((x * matrix'm0 mat) + (y * matrix'm4 mat) + matrix'm12 mat) ((x * matrix'm1 mat) + (y * matrix'm5 mat) + matrix'm13 mat) + +-- | Reflect 2D vector to normal +vector2Reflect :: + -- | Input vector + Vector2 -> + -- | Normal vector + Vector2 -> + Vector2 +vector2Reflect v normal = v |-| (normal |* (2 * (v |.| normal))) + +-- | Rotate 2D vector by angle +vector2Rotate :: Vector2 -> Float -> Vector2 +vector2Rotate (Vector2 x y) angle = Vector2 (x * c - y * s) (x * s + y * c) + where + c = cos angle + s = sin angle + +------------------------------------------------ +-- Vector3 math -------------------------------- +------------------------------------------------ + +-- | 3D vector cross-product +vector3CrossProduct :: Vector3 -> Vector3 -> Vector3 +vector3CrossProduct (Vector3 x1 y1 z1) (Vector3 x2 y2 z2) = Vector3 (y1 * z2 - z1 * y2) (z1 * x2 - x1 * z2) (x1 * y2 - y1 * x2) + +-- | Perpendicular to given 3D vector +vector3Perpendicular :: Vector3 -> Vector3 +vector3Perpendicular v@(Vector3 x y z) = vector3CrossProduct v cardinalAxis + where + cardinalAxis + | abs y < abs x = Vector3 0 1 0 + | abs z < abs x = Vector3 0 0 1 + | otherwise = Vector3 1 0 0 + +-- | Angle between two 3D vectors +vector3Angle :: Vector3 -> Vector3 -> Float +vector3Angle v1 v2 = atan2 len dot + where + cross = vector3CrossProduct v1 v2 + len = magnitude cross + dot = v1 |.| v2 + +-- | Orthonormalize provided vectors. +-- Makes vectors normalized and orthogonal to each other. +-- Gram-Schmidt function implementation. +vector3OrthoNormalize :: Vector3 -> Vector3 -> (Vector3, Vector3) +vector3OrthoNormalize v1 v2 = (v1', v2') + where + v1' = vectorNormalize v1 + v2' = vector3CrossProduct vn1 v1' + vn1 = vectorNormalize $ vector3CrossProduct v1' v2 + +-- | Transform a 3D vector by a matrix +vector3Transform :: Vector3 -> Matrix -> Vector3 +vector3Transform + (Vector3 x y z) + (Matrix m0 m4 m8 m12 m1 m5 m9 m13 m2 m6 m10 m14 _ _ _ _) = + Vector3 + (x * m0 + y * m4 + z * m8 + m12) + (x * m1 + y * m5 + z * m9 + m13) + (x * m2 + y * m6 + z * m10 + m14) + +-- | Transform a 3D vector by quaternion rotation +vector3RotateByQuaternion :: Vector3 -> Quaternion -> Vector3 +vector3RotateByQuaternion (Vector3 a b c) (Vector4 x y z w) = + Vector3 + (a * (x * x + w * w - y * y - z * z) + b * (2 * x * y - 2 * w * z) + c * (2 * x * z + 2 * w * y)) + (a * (2 * w * z + 2 * x * y) + b * (w * w - x * x + y * y - z * z) + c * ((-2) * w * x + 2 * y * z)) + (a * ((-2) * w * y + 2 * x * z) + b * (2 * w * x + 2 * y * z) + c * (w * w - x * x - y * y + z * z)) + +-- | Rotate a 3D vector around an axis +vector3RotateByAxisAngle :: + -- | Vector to rotate + Vector3 -> + -- | Axis to rotate around + Vector3 -> + -- | Angle to rotate by + Float -> + Vector3 +vector3RotateByAxisAngle v axis angle = v |+| (wv |* (2 * a)) |+| (wwv |* 2) + where + (Vector3 ax ay az) = vectorNormalize axis + s = sin (angle / 2) + a = cos (angle / 2) + b = ax * s + c = ay * s + d = az * s + w = Vector3 b c d + wv = vector3CrossProduct w v + wwv = vector3CrossProduct w wv + +-- | Reflect 3D vector to normal +vector3Reflect :: + -- | Input vector + Vector3 -> + -- | Normal vector + Vector3 -> + Vector3 +vector3Reflect v normal = v |-| (normal |* (2 * v |.| normal)) + +-- | Compute barycenter coordinates (u, v, w) for a point with respect to a triangle. +-- NOTE: Assumes the point is on the plane of the triangle. +vector3Barycenter :: + -- | Input point + Vector3 -> + -- | Triangle vertices + (Vector3, Vector3, Vector3) -> + Vector3 +vector3Barycenter p (a, b, c) = Vector3 (1 - y - z) y z + where + v0 = b |-| a + v1 = c |-| a + v2 = p |-| a + d00 = v0 |.| v0 + d01 = v0 |.| v1 + d11 = v1 |.| v1 + d20 = v2 |.| v0 + d21 = v2 |.| v1 + denom = d00 * d11 - d01 * d01 + y = (d11 * d20 - d01 * d21) / denom + z = (d00 * d21 - d01 * d20) / denom + +-- | Project a Vector3 from screen space into object space +vector3Unproject :: + -- | Vector to unproject + Vector3 -> + -- | Projection matrix + Matrix -> + -- | View matrix + Matrix -> + Vector3 +vector3Unproject (Vector3 x y z) projection view = Vector3 (rx / rw) (ry / rw) (rz / rw) + where + matViewProj = view /*/ projection + a00 = matrix'm0 matViewProj + a01 = matrix'm1 matViewProj + a02 = matrix'm2 matViewProj + a03 = matrix'm3 matViewProj + a10 = matrix'm4 matViewProj + a11 = matrix'm5 matViewProj + a12 = matrix'm6 matViewProj + a13 = matrix'm7 matViewProj + a20 = matrix'm8 matViewProj + a21 = matrix'm9 matViewProj + a22 = matrix'm10 matViewProj + a23 = matrix'm11 matViewProj + a30 = matrix'm12 matViewProj + a31 = matrix'm13 matViewProj + a32 = matrix'm14 matViewProj + a33 = matrix'm15 matViewProj + b00 = a00 * a11 - a01 * a10 + b01 = a00 * a12 - a02 * a10 + b02 = a00 * a13 - a03 * a10 + b03 = a01 * a12 - a02 * a11 + b04 = a01 * a13 - a03 * a11 + b05 = a02 * a13 - a03 * a12 + b06 = a20 * a31 - a21 * a30 + b07 = a20 * a32 - a22 * a30 + b08 = a20 * a33 - a23 * a30 + b09 = a21 * a32 - a22 * a31 + b10 = a21 * a33 - a23 * a31 + b11 = a22 * a33 - a23 * a32 + invDet = 1 / (b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06) + matViewProjInv = + Matrix + ((a11 * b11 - a12 * b10 + a13 * b09) * invDet) + ((- a01 * b11 + a02 * b10 - a03 * b09) * invDet) + ((a31 * b05 - a32 * b04 + a33 * b03) * invDet) + ((- a21 * b05 + a22 * b04 - a23 * b03) * invDet) + ((- a10 * b11 + a12 * b08 - a13 * b07) * invDet) + ((a00 * b11 - a02 * b08 + a03 * b07) * invDet) + ((- a30 * b05 + a32 * b02 - a33 * b01) * invDet) + ((a20 * b05 - a22 * b02 + a23 * b01) * invDet) + ((a10 * b10 - a11 * b08 + a13 * b06) * invDet) + ((- a00 * b10 + a01 * b08 - a03 * b06) * invDet) + ((a30 * b04 - a31 * b02 + a33 * b00) * invDet) + ((- a20 * b04 + a21 * b02 - a23 * b00) * invDet) + ((- a10 * b09 + a11 * b07 - a12 * b06) * invDet) + ((a00 * b09 - a01 * b07 + a02 * b06) * invDet) + ((- a30 * b03 + a31 * b01 - a32 * b00) * invDet) + ((a20 * b03 - a21 * b01 + a22 * b00) * invDet) + (Vector4 rx ry rz rw) = quaternionTransform (Vector4 x y z 1) matViewProjInv + +-- | Compute the direction of a refracted ray +vector3Refract :: + -- | Normalized direction of the incoming ray + Vector3 -> + -- | Normalized normal vector of the interface of two optical media + Vector3 -> + -- | Ratio of the refractive index of the medium from where the ray + -- comes to the refractive index of the medium on the other side of + -- the surface + Float -> + Vector3 +vector3Refract (Vector3 x y z) (Vector3 nx ny nz) r = Vector3 (r * x - (r * dot + d) * nx) (r * y - (r * dot + d) * ny) (r * z - (r * dot + d) * nz) + where + dot = x * nx + y * ny + z * nz + d = sqrt $ 1 - r * r * (1 - dot * dot) + +------------------------------------------------ +-- Matrix math --------------------------------- +------------------------------------------------ + +-- | Utility function +matrixToList :: Matrix -> [Float] +matrixToList + (Matrix a00 a10 a20 a30 a01 a11 a21 a31 a02 a12 a22 a32 a03 a13 a23 a33) = + [a00, a10, a20, a30, a01, a11, a21, a31, a02, a12, a22, a32, a03, a13, a23, a33] + +-- | Utility function +matrixFromList :: [Float] -> Matrix +matrixFromList + (a00 : a10 : a20 : a30 : a01 : a11 : a21 : a31 : a02 : a12 : a22 : a32 : a03 : a13 : a23 : a33 : _) = + Matrix a00 a10 a20 a30 a01 a11 a21 a31 a02 a12 a22 a32 a03 a13 a23 a33 +matrixFromList _ = error "matrixFromList expects a list of at least 16 elements!" + +-- | Scalar to matrix (all elements are set to the scalar) +matrixConstant :: Float -> Matrix +matrixConstant n = matrixFromList $ repeat n + +-- | Compute matrix determinant +matrixDeterminant :: Matrix -> Float +matrixDeterminant + (Matrix a00 a10 a20 a30 a01 a11 a21 a31 a02 a12 a22 a32 a03 a13 a23 a33) = + 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 + +-- | Trace of a matrix (sum of the values along the diagonal) +matrixTrace :: Matrix -> Float +matrixTrace mat = matrix'm0 mat + matrix'm5 mat + matrix'm10 mat + matrix'm15 mat + +-- | Transpose a matrix +matrixTranspose :: Matrix -> Matrix +matrixTranspose + (Matrix m0 m4 m8 m12 m1 m5 m9 m13 m2 m6 m10 m14 m3 m7 m11 m15) = + Matrix m0 m1 m2 m3 m4 m5 m6 m7 m8 m9 m10 m11 m12 m13 m14 m15 + +-- | Invert a matrix +matrixInvert :: Matrix -> Matrix +matrixInvert + (Matrix a00 a10 a20 a30 a01 a11 a21 a31 a02 a12 a22 a32 a03 a13 a23 a33) = + Matrix + ((a11 * b11 - a12 * b10 + a13 * b09) * invDet) + ((- a10 * b11 + a12 * b08 - a13 * b07) * invDet) + ((a10 * b10 - a11 * b08 + a13 * b06) * invDet) + ((- a10 * b09 + a11 * b07 - a12 * b06) * invDet) + ((- a01 * b11 + a02 * b10 - a03 * b09) * invDet) + ((a00 * b11 - a02 * b08 + a03 * b07) * invDet) + ((- a00 * b10 + a01 * b08 - a03 * b06) * invDet) + ((a00 * b09 - a01 * b07 + a02 * b06) * invDet) + ((a31 * b05 - a32 * b04 + a33 * b03) * invDet) + ((- a30 * b05 + a32 * b02 - a33 * b01) * invDet) + ((a30 * b04 - a31 * b02 + a33 * b00) * invDet) + ((- a30 * b03 + a31 * b01 - a32 * b00) * invDet) + ((- a21 * b05 + a22 * b04 - a23 * b03) * invDet) + ((a20 * b05 - a22 * b02 + a23 * b01) * invDet) + ((- a20 * b04 + a21 * b02 - a23 * b00) * invDet) + ((a20 * b03 - a21 * b01 + a22 * b00) * invDet) + where + b00 = a00 * a11 - a01 * a10 + b01 = a00 * a12 - a02 * a10 + b02 = a00 * a13 - a03 * a10 + b03 = a01 * a12 - a02 * a11 + b04 = a01 * a13 - a03 * a11 + b05 = a02 * a13 - a03 * a12 + b06 = a20 * a31 - a21 * a30 + b07 = a20 * a32 - a22 * a30 + b08 = a20 * a33 - a23 * a30 + b09 = a21 * a32 - a22 * a31 + b10 = a21 * a33 - a23 * a31 + b11 = a22 * a33 - a23 * a32 + invDet = 1 / (b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06) + +-- | Identity matrix +matrixIdentity :: Matrix +matrixIdentity = Matrix 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 + +-- | Add two matrices +matrixAdd :: Matrix -> Matrix -> Matrix +matrixAdd left right = matrixFromList $ zipWith (+) (matrixToList left) (matrixToList right) + +-- | Alias for 'matrixAdd' +(/+/) :: Matrix -> Matrix -> Matrix +(/+/) = matrixAdd + +-- | Subtract two matrices +matrixSubtract :: Matrix -> Matrix -> Matrix +matrixSubtract left right = matrixFromList $ zipWith (-) (matrixToList left) (matrixToList right) + +-- | Alias for 'matrixSubtract' +(/-/) :: Matrix -> Matrix -> Matrix +(/-/) = matrixSubtract + +-- | Multiply two matrices (order matters!) +matrixMultiply :: Matrix -> Matrix -> Matrix +matrixMultiply + (Matrix l0 l4 l8 l12 l1 l5 l9 l13 l2 l6 l10 l14 l3 l7 l11 l15) + (Matrix r0 r4 r8 r12 r1 r5 r9 r13 r2 r6 r10 r14 r3 r7 r11 r15) = + Matrix + (l0 * r0 + l1 * r4 + l2 * r8 + l3 * r12) + (l4 * r0 + l5 * r4 + l6 * r8 + l7 * r12) + (l8 * r0 + l9 * r4 + l10 * r8 + l11 * r12) + (l12 * r0 + l13 * r4 + l14 * r8 + l15 * r12) + (l0 * r1 + l1 * r5 + l2 * r9 + l3 * r13) + (l4 * r1 + l5 * r5 + l6 * r9 + l7 * r13) + (l8 * r1 + l9 * r5 + l10 * r9 + l11 * r13) + (l12 * r1 + l13 * r5 + l14 * r9 + l15 * r13) + (l0 * r2 + l1 * r6 + l2 * r10 + l3 * r14) + (l4 * r2 + l5 * r6 + l6 * r10 + l7 * r14) + (l8 * r2 + l9 * r6 + l10 * r10 + l11 * r14) + (l12 * r2 + l13 * r6 + l14 * r10 + l15 * r14) + (l0 * r3 + l1 * r7 + l2 * r11 + l3 * r15) + (l4 * r3 + l5 * r7 + l6 * r11 + l7 * r15) + (l8 * r3 + l9 * r7 + l10 * r11 + l11 * r15) + (l12 * r3 + l13 * r7 + l14 * r11 + l15 * r15) + +-- | Alias for 'matrixMultiply' +(/*/) :: Matrix -> Matrix -> Matrix +(/*/) = matrixMultiply + +-- | Translation matrix +matrixTranslate :: + -- | x translation + Float -> + -- | y translation + Float -> + -- | z translation + Float -> + Matrix +matrixTranslate x y z = Matrix 1 0 0 x 0 1 0 y 0 0 1 z 0 0 0 1 + +-- | Axis-angle rotation matrix (angle should be in radians) +matrixRotate :: + -- | Axis to rotate around + Vector3 -> + -- | Angle to rotate by + Float -> + Matrix +matrixRotate axis angle = Matrix (x * x * t + c) (x * y * t - z * s) (x * z * t + y * s) 0 (y * x * t + z * s) (y * y * t + c) (y * z * t - x * s) 0 (z * x * t - y * s) (z * y * t + x * s) (z * z * t + c) 0 0 0 0 1 + where + (Vector3 x y z) = vectorNormalize axis + s = sin angle + c = cos angle + t = 1 - c + +-- | x-rotation matrix (angle should be in radians) +matrixRotateX :: Float -> Matrix +matrixRotateX angle = Matrix 1 0 0 0 0 c (- s) 0 0 s c 0 0 0 0 1 + where + s = sin angle + c = cos angle + +-- | y-rotation matrix (angle should be in radians) +matrixRotateY :: Float -> Matrix +matrixRotateY angle = Matrix c 0 s 0 0 1 0 0 (- s) 0 c 0 0 0 0 1 + where + s = sin angle + c = cos angle + +-- | z-rotation matrix (angle should be in radians) +matrixRotateZ :: Float -> Matrix +matrixRotateZ angle = Matrix c (- s) 0 0 s c 0 0 0 0 1 0 0 0 0 1 + where + s = sin angle + c = cos angle + +-- | Euler angle xyz rotation matrix (angles should be in radians) +matrixRotateXYZ :: Vector3 -> Matrix +matrixRotateXYZ (Vector3 x y z) = Matrix (cz - cy) (sz * cy) (- sy) 0 (cz * sy * sx - sz * cx) (sz * sy * sx + cz * cx) (cy * sx) 0 (cz * sy * cx + sz * sx) (sz * sy * cx - cz * sx) (cy * cx) 0 0 0 0 1 + where + cx = cos (- x) + sx = sin (- x) + cy = cos (- y) + sy = sin (- y) + cz = cos (- z) + sz = sin (- z) + +-- | Euler angle zyx rotation matrix (angles should be in radians) +matrixRotateZYX :: Vector3 -> Matrix +matrixRotateZYX (Vector3 x y z) = Matrix (cz * cy) (cz * sy * sx - cx * sz) (sz * sx + cz * cx * sy) 0 (cy * sz) (cz * cx + sz * sy * sx) (cx * sz * sy - cz * sx) 0 (- sy) (cy * sx) (cy * cx) 0 0 0 0 1 + where + cz = cos z + sz = sin z + cy = cos y + sy = sin y + cx = cos x + sx = sin x + +-- | Scaling matrix +matrixScale :: Vector3 -> Matrix +matrixScale (Vector3 x y z) = Matrix x 0 0 0 0 y 0 0 0 0 z 0 0 0 0 1 + +-- | Frustum projection matrix +matrixFrustum :: + -- | Left edge distance + Float -> + -- | Right edge distance + Float -> + -- | Bottom edge distance + Float -> + -- | Top edge distance + Float -> + -- | Near clipping plane distance + Float -> + -- | Far clipping plane distance + Float -> + Matrix +matrixFrustum left right bottom top near far = + Matrix + (near * 2 / x) + 0 + ((right + left) / x) + 0 + 0 + (near * 2 / y) + ((top + bottom) / y) + 0 + 0 + 0 + (- (far + near) / z) + (- far * near * 2 / z) + 0 + 0 + (-1) + 0 + where + x = right - left + y = top - bottom + z = far - near + +-- | Perspective projection matrix +matrixPerspective :: + -- | y-fov angle (should be in radians) + Float -> + -- | Aspect ratio + Float -> + -- | Near clipping plane distance + Float -> + -- | Far clipping plane distance + Float -> + Matrix +matrixPerspective fovy aspect near far = matrixFrustum left right bottom top near far + where + top = near * tan (fovy / 2) + bottom = - top + right = top * aspect + left = - right + +-- | Orthographic projection matrix +matrixOrtho :: + -- | Left edge distance + Float -> + -- | Right edge distance + Float -> + -- | Bottom edge distance + Float -> + -- | Top edge distance + Float -> + -- | Near clipping plane distance + Float -> + -- | Far clipping plane distance + Float -> + Matrix +matrixOrtho left right bottom top near far = + Matrix (2 / x) 0 0 (- (left + right) / x) 0 (2 / y) 0 (- (top + bottom) / y) 0 0 (-2 / z) (- (far + near) / z) 0 0 0 1 + where + x = right - left + y = top - bottom + z = far - near + +-- | Camera look-at matrix (view matrix) +matrixLookAt :: + -- | Camera position + Vector3 -> + -- | Camera target + Vector3 -> + -- | World up vector + Vector3 -> + Matrix +matrixLookAt eye target up = Matrix xx xy xz (- vx |.| eye) yx yy yz (- vy |.| eye) zx zy zz (- vz |.| eye) 0 0 0 1 + where + vz@(Vector3 zx zy zz) = vectorNormalize $ eye |-| target + vx@(Vector3 xx xy xz) = vectorNormalize $ vector3CrossProduct up vz + vy@(Vector3 yx yy yz) = vector3CrossProduct vz vx + +------------------------------------------------ +-- Quaternion math ----------------------------- +------------------------------------------------ + +-- | Identity quaternion +quaternionIdentity :: Quaternion +quaternionIdentity = Vector4 0 0 0 1 + +-- | Invert a quaternion +quaternionInvert :: Quaternion -> Quaternion +quaternionInvert q@(Vector4 x y z w) = Vector4 (- x * invLength) (- y * invLength) (- z * invLength) (w * invLength) + where + invLength = 1 / magnitudeSqr q + +-- | Multiply two quaternions +quaternionMultiply :: Quaternion -> Quaternion -> Quaternion +quaternionMultiply (Vector4 qax qay qaz qaw) (Vector4 qbx qby qbz qbw) = Vector4 (qax * qbw + qaw * qbx + qay * qbz - qaz * qby) (qay * qbw + qaw * qby + qaz * qbx - qax * qbz) (qaz * qbw + qaw * qbz + qax * qby - qay * qbx) (qaw * qbw - qax * qbx - qay * qby - qaz * qbz) + +-- | Normalize a quaternion (alias for 'vectorNormalize') +quaternionNormalize :: Quaternion -> Quaternion +quaternionNormalize = vectorNormalize + +-- | Lerp between two quaternions (alias for 'quaternionLerp') +quaternionLerp :: + -- | Lerp start value + Quaternion -> + -- | Lerp end value + Quaternion -> + -- | Lerp amount + Float -> + Quaternion +quaternionLerp = vectorLerp + +-- | Slerp-optimized interpolation between two quaternions +quaternionNLerp :: + -- | Lerp start value + Quaternion -> + -- | Lerp end value + Quaternion -> + -- | Lerp amount + Float -> + Quaternion +quaternionNLerp q1 q2 amount = vectorNormalize $ quaternionLerp q1 q2 amount + +-- | Spherical linear interpolation between two quaternions +quaternionSLerp :: + -- | Lerp start value + Quaternion -> + -- | Lerp end value + Quaternion -> + -- | Lerp amount + Float -> + Quaternion +quaternionSLerp q1 q2 amount + | cosHalfTheta >= 1 = q1 + | cosHalfTheta > 0.95 = quaternionNLerp q1 q2' amount + | abs sinHalfTheta < 0.001 = (q1 |+| q2') |/ 2 + | otherwise = (q1 |* ratioA) |+| (q2 |* ratioB) + where + cosHalfTheta = if dot < 0 then - dot else dot + sinHalfTheta = sqrt (1 - cosHalfTheta * cosHalfTheta) + halfTheta = acos cosHalfTheta + + ratioA = sin ((1 - amount) * halfTheta) / sinHalfTheta + ratioB = sin (amount * halfTheta) / sinHalfTheta + + q2' = if dot < 0 then additiveInverse q2 else q2 + dot = q1 |.| q2 + +-- | Quaternion based on the rotation between two vectors +quaternionFromVector3ToVector3 :: Vector3 -> Vector3 -> Quaternion +quaternionFromVector3ToVector3 from to = quaternionNormalize (Vector4 x y z (1 + cos2Theta)) + where + cos2Theta = from |.| to + (Vector3 x y z) = vector3CrossProduct from to + +-- | Create a quaternion from a rotation matrix +quaternionFromMatrix :: Matrix -> Quaternion +quaternionFromMatrix mat + | fourBiggestSquaredMinus1 == fourWSquaredMinus1 = Vector4 ((matrix'm6 mat - matrix'm9 mat) * mult) ((matrix'm8 mat - matrix'm2 mat) * mult) ((matrix'm1 mat - matrix'm4 mat) * mult) biggestVal + | fourBiggestSquaredMinus1 == fourXSquaredMinus1 = Vector4 biggestVal ((matrix'm1 mat + matrix'm4 mat) * mult) ((matrix'm8 mat + matrix'm2 mat) * mult) ((matrix'm6 mat - matrix'm9 mat) * mult) + | fourBiggestSquaredMinus1 == fourYSquaredMinus1 = Vector4 ((matrix'm1 mat + matrix'm4 mat) * mult) biggestVal ((matrix'm6 mat + matrix'm9 mat) * mult) ((matrix'm8 mat - matrix'm2 mat) * mult) + | fourBiggestSquaredMinus1 == fourZSquaredMinus1 = Vector4 ((matrix'm8 mat + matrix'm2 mat) * mult) ((matrix'm6 mat + matrix'm9 mat) * mult) biggestVal ((matrix'm1 mat - matrix'm4 mat) * mult) + | otherwise = error "(quaternionFromMatrix) This error should never happen" + where + fourWSquaredMinus1 = matrix'm0 mat + matrix'm5 mat + matrix'm10 mat + fourXSquaredMinus1 = matrix'm0 mat - matrix'm5 mat - matrix'm10 mat + fourYSquaredMinus1 = matrix'm5 mat - matrix'm0 mat - matrix'm10 mat + fourZSquaredMinus1 = matrix'm10 mat - matrix'm0 mat - matrix'm5 mat + fourBiggestSquaredMinus1 = + maximum + [ fourWSquaredMinus1, + fourXSquaredMinus1, + fourYSquaredMinus1, + fourZSquaredMinus1 + ] + biggestVal = sqrt (fourBiggestSquaredMinus1 + 1) / 2 + mult = 0.5 / biggestVal + +-- | Create a rotation matrix from a quaternion +quaternionToMatrix :: Quaternion -> Matrix +quaternionToMatrix (Vector4 x y z w) = Matrix (1 - 2 * (b2 + c2)) (2 * (ab - cd)) (2 * (ac + bd)) 0 (2 * (ab + cd)) (1 - 2 * (a2 + c2)) (2 * (bc - ad)) 0 (2 * (ac - bd)) (2 * (bc + ad)) (1 - 2 * (a2 + b2)) 0 0 0 0 1 + where + a2 = x * x + b2 = y * y + c2 = z * z + ac = x * z + ab = x * y + bc = y * z + ad = w * x + bd = w * y + cd = w * z + +-- | Create a quaternion for an angle and axis +quaternionFromAxisAngle :: + -- | Rotation axis + Vector3 -> + -- | Angle in radians + Float -> + Quaternion +quaternionFromAxisAngle axis angle = quaternionNormalize $ Vector4 (x * s) (y * s) (z * s) c -- I'm pretty sure normalizing the quaternion here is redundant + where + (Vector3 x y z) = vectorNormalize axis + s = sin (angle / 2) + c = cos (angle / 2) + +-- | Convert a quaternion to axis-angle representation +quaternionToAxisAngle :: Quaternion -> (Vector3, Float) +quaternionToAxisAngle q = (axis, angle) + where + (Vector4 x y z w) = quaternionNormalize q + s = sqrt (1 - w * w) + axis = if w > 0.9999 then Vector3 1 0 0 else Vector3 (x / s) (y / s) (z / s) + angle = acos w * 2 + +-- | Create a quaternion from Euler angles (ZYX rotation order, angles should be in radians) +quaternionFromEuler :: + -- | Pitch + Float -> + -- | Yaw + Float -> + -- | Roll + Float -> + Quaternion +quaternionFromEuler pitch yaw roll = Vector4 (x1 * y0 * z0 - x0 * y1 * z1) (x0 * y1 * z0 + x1 * y0 * z1) (x0 * y0 * z1 - x1 * y1 * z0) (x0 * y0 * z0 + x1 * y1 * z1) + where + x0 = cos (pitch * 0.5) + x1 = sin (pitch * 0.5) + y0 = cos (yaw * 0.5) + y1 = sin (yaw * 0.5) + z0 = cos (roll * 0.5) + z1 = sin (roll * 0.5) + +-- | Convert a quaternion to Euler angle representation (Vector3 roll pitch yaw, all angles in radians) +quaternionToEuler :: Quaternion -> Vector3 +quaternionToEuler (Vector4 x y z w) = Vector3 (atan2 x0 x1) (asin y0) (atan2 z0 z1) + where + x0 = 2 * (w * x + y * z) + x1 = 1 - 2 * (x * x + y * y) + y0 = clamp (2 * (w * y - z * x)) (-1) 1 + z0 = 2 * (w * z + x * y) + z1 = 1 - 2 * (y * y + z * z) + +-- | Transform a quaternion given a transformation matrix +quaternionTransform :: Quaternion -> Matrix -> Quaternion +quaternionTransform (Vector4 x y z w) mat = + Vector4 + (matrix'm0 mat * x + matrix'm4 mat * y + matrix'm8 mat * z + matrix'm12 mat * w) + (matrix'm1 mat * x + matrix'm5 mat * y + matrix'm9 mat * z + matrix'm13 mat * w) + (matrix'm2 mat * x + matrix'm6 mat * y + matrix'm10 mat * z + matrix'm14 mat * w) + (matrix'm3 mat * x + matrix'm7 mat * y + matrix'm11 mat * z + matrix'm15 mat * w)