Browse Source

Merge pull request #360 from raysan5/develop

Integrate Develop branch
Ray 8 years ago
parent
commit
4a63e5dfb3

+ 1 - 0
.gitignore

@@ -143,6 +143,7 @@ install_manifest.txt
 compile_commands.json
 compile_commands.json
 CTestTestfile.cmake
 CTestTestfile.cmake
 build
 build
+!templates/android_project/Makefile
 
 
 # Unignore These makefiles...
 # Unignore These makefiles...
 !examples/CMakeLists.txt
 !examples/CMakeLists.txt

+ 84 - 3
CHANGELOG

@@ -1,7 +1,88 @@
 changelog
 changelog
 ---------
 ---------
 
 
-Current Release:    raylib 1.7.0 (20 May 2017)
+Current Release:    raylib 1.8.0 (Oct 2017)
+
+-----------------------------------------------
+Release:     raylib 1.8.0 (Oct 2017)
+-----------------------------------------------
+NOTE: 
+  In this release, multiple parts of the library have been reviewed again for consistency and simplification. 
+  It exposes more than 20 new functions in comparison with previous version and it improves overall programming experience.
+  
+BIG CHANGES:
+  - Image generation functions: Gradient, Checked, Noise, Cellular...
+  - Mesh generation functions: Cube, Sphere, Cylinder, Torus, Knot...
+  - New Shaders and Materials systems to support PBR materials
+  - Custom Android toolchain for APK building with simple Makefile
+  - Complete review of raymath functionality (Matrix, Quaternion)
+  - Complete review of rlgl layer functionality
+
+detailed changes:
+[rlgl] RENAMED: rlglLoadTexture() to rlLoadTexture()
+[rlgl] RENAMED: rlglLoadRenderTexture() to rlLoadRenderTexture()
+[rlgl] RENAMED: rlglUpdateTexture() to rlUpdateTexture()
+[rlgl] RENAMED: rlglGenerateMipmaps() to rlGenerateMipmaps()
+[rlgl] RENAMED: rlglReadScreenPixels() to rlReadScreenPixels()
+[rlgl] RENAMED: rlglReadTexturePixels() to rlReadTexturePixels()
+[rlgl] RENAMED: rlglLoadMesh() to rlLoadMesh()
+[rlgl] RENAMED: rlglUpdateMesh() to rlUpdateMesh()
+[rlgl] RENAMED: rlglDrawMesh() to rlDrawMesh()
+[rlgl] RENAMED: rlglUnloadMesh() to rlUnloadMesh()
+[rlgl] RENAMED: rlglUnproject() to rlUnproject()
+[rlgl] RENAMED: LoadCompressedTexture() to LoadTextureCompressed()
+[rlgl] RENAMED: GetDefaultTexture() to GetTextureDefault()
+[rlgl] RENAMED: LoadDefaultShader() to LoadShaderDefault()
+[rlgl] RENAMED: LoadDefaultShaderLocations() to SetShaderDefaultLocations()
+[rlgl] RENAMED: UnloadDefaultShader() to UnLoadShaderDefault()
+// Texture maps generation (PBR)
+[rlgl] ADDED: rlGenMapCubemap(Texture2D skyHDR, int size);              // Generate cubemap texture map from HDR texture
+[rlgl] ADDED: rlGenMapIrradiance(Texture2D cubemap, int size);          // Generate irradiance texture map
+[rlgl] ADDED: rlGenMapPrefilter(Texture2D cubemap, int size);           // Generate prefilter texture map
+[rlgl] ADDED: rlGenMapBRDF(Texture2D cubemap, int size);                // Generate BRDF texture map
+[core] ADDED: SetWindowTitle()
+[core] ADDED: GetExtension()
+[textures] ADDED: SaveImageAs() 
+? [textures]  ADDED: DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3, Color col4);       // Draw a gradient-filled rectangle with custom vertex colors
+? [textures]  ADDED: DrawRectangleT(int posX, int posY, int width, int height, Color color);                       // Draw rectangle using text character
+// Image generation functions
+[textures] ADDED: GenImageGradientV(int width, int height, Color top, Color bottom);                           // Generate image: vertical gradient
+[textures] ADDED: GenImageGradientH(int width, int height, Color left, Color right);                           // Generate image: horizontal gradient
+[textures] ADDED: GenImageGradientRadial(int width, int height, float density, Color inner, Color outer);      // Generate image: radial gradient
+[textures] ADDED: GenImageChecked(int width, int height, int checksX, int checksY, Color col1, Color col2);    // Generate image: checked
+[textures] ADDED: GenImageWhiteNoise(int width, int height, float factor);                                     // Generate image: white noise
+[textures] ADDED: GenImagePerlinNoise(int width, int height, float scale);                                     // Generate image: perlin noise
+[textures] ADDED: GenImageCellular(int width, int height, int tileSize);                                       // Generate image: cellular algorithm. Bigger tileSize means bigger cells
+// Texture maps generation (PBR)
+[textures] ADDED: GenTextureCubemap(Shader shader, Texture2D skyHDR, int size);       // Generate cubemap texture from HDR texture
+[textures] ADDED: GenTextureIrradiance(Shader shader, Texture2D cubemap, int size);   // Generate irradiance texture using cubemap data
+[textures] ADDED: GenTexturePrefilter(Shader shader, Texture2D cubemap, int size);    // Generate prefilter texture using cubemap data
+[textures] ADDED: GenTextureBRDF(Shader shader, Texture2D cubemap, int size);         // Generate BRDF texture using cubemap data
+[models] REMOVED: LoadMeshEx()
+[models] REMOVED: UpdateMesh()
+[models] REMOVED: LoadHeightmap()
+[models] REMOVED: LoadCubicmap()
+[models] RENAMED: LoadDefaultMaterial() to LoadMaterialDefault()
+// Mesh generation functions
+[models] ADDED: GenMeshPlane()
+[models] ADDED: GenMeshCube()
+[models] ADDED: GenMeshSphere()
+[models] ADDED: GenMeshHemiSphere(float radius, int rings, int slices);                                      // Generate half-sphere mesh (no bottom cap)
+[models] ADDED: GenMeshCylinder(float radius, float height, int slices);                                     // Generate cylinder mesh
+[models] ADDED: GenMeshTorus(float radius, float size, int radSeg, int sides);                               // Generate torus mesh
+[models] ADDED: GenMeshKnot(float radius, float size, int radSeg, int sides);                                // Generate trefoil knot mesh
+[models] ADDED: GenMeshHeightmap(Image heightmap, Vector3 size);                                             // Generate heightmap mesh from image data
+[models] ADDED: GenMeshCubicmap(Image cubicmap, Vector3 cubeSize);                                           // Generate cubes-based map mesh from image data
+
+[raymath] Reviewed full Matrix functionality
+[raymath] Renamed Vector3 functions for consistency
+[build] Integrate Android APK building into examples Makefile
+[example] ADDED: 
+[example] ADDED: 
+[example] ADDED: 
+[github] Moved raylib webpage to own repo: github.com/raysan5/raylib.com
+[games] Reviewed game: Koala Seasons
+[*] Updated STB libraries to latest version
 
 
 -----------------------------------------------
 -----------------------------------------------
 Release:     raylib 1.7.0 (20 May 2017)
 Release:     raylib 1.7.0 (20 May 2017)
@@ -36,14 +117,14 @@ other changes:
 [rlgl] Removed function: CreateLight(), removed internal lighting system
 [rlgl] Removed function: CreateLight(), removed internal lighting system
 [rlgl] Removed function: DestroyLight(), removed internal lighting system
 [rlgl] Removed function: DestroyLight(), removed internal lighting system
 [rlgl] Removed function: InitVrDevice(), removed VR device render, using simulator
 [rlgl] Removed function: InitVrDevice(), removed VR device render, using simulator
-[rlgl] Removed function:  CloseVrDevice(), removed VR device render, using simulator
+[rlgl] Removed function: CloseVrDevice(), removed VR device render, using simulator
 [rlgl] Removed function: IsVrDeviceReady(), removed VR device render, using simulator
 [rlgl] Removed function: IsVrDeviceReady(), removed VR device render, using simulator
 [rlgl] Removed function: IsVrSimulator(), removed VR device render, using simulator
 [rlgl] Removed function: IsVrSimulator(), removed VR device render, using simulator
 [rlgl] Added function: InitVrSimulator(), init VR simulator for selected device
 [rlgl] Added function: InitVrSimulator(), init VR simulator for selected device
 [rlgl] Added function: CloseVrSimulator(), close VR simulator for current device
 [rlgl] Added function: CloseVrSimulator(), close VR simulator for current device
 [rlgl] Added function: IsVrSimulatorReady(), detect if VR device is ready
 [rlgl] Added function: IsVrSimulatorReady(), detect if VR device is ready
 [rlgl] Added function: BeginVrDrawing(), begin VR simulator stereo rendering
 [rlgl] Added function: BeginVrDrawing(), begin VR simulator stereo rendering
-[rlgl] Added function:  EndVrDrawing(), end VR simulator stereo rendering
+[rlgl] Added function: EndVrDrawing(), end VR simulator stereo rendering
 [rlgl] Renamed function: ReadTextFile() to LoadText() and exposed to API
 [rlgl] Renamed function: ReadTextFile() to LoadText() and exposed to API
 [rlgl] Removed internal lighting system and standard shader, moved to example
 [rlgl] Removed internal lighting system and standard shader, moved to example
 [rlgl] Removed Oculus Rift support, moved to oculus_rift example
 [rlgl] Removed Oculus Rift support, moved to oculus_rift example

+ 34 - 0
CONTRIBUTORS.md

@@ -0,0 +1,34 @@
+
+I believe that time is the most valuable resource and the following people have invested part of their time
+contributing (in some way or another) to make raylib project better. Huge thanks!
+
+ - [Zopokx](https://github.com/Zopokx) for testing the web.
+ - [Elendow](http://www.elendow.com) for testing and helping on web development.
+ - Victor Dual for implementing and testing 3D shapes functions.
+ - Marc Palau for implementing and testing 3D shapes functions and contribute on camera and gestures modules.
+ - Kevin Gato for improving texture internal formats support and helping on raygui development. 
+ - Daniel Nicolas for improving texture internal formats support and helping on raygui development. 
+ - Marc Agüera for testing and using raylib on a real product ([Koala Seasons](http://www.koalaseasons.com))
+ - Daniel Moreno for testing and using raylib on a real product ([Koala Seasons](http://www.koalaseasons.com))
+ - Daniel Gomez for testing and using raylib on a real product ([Koala Seasons](http://www.koalaseasons.com))
+ - [Sergio Martinez](https://github.com/anidealgift) for helping on raygui development and tools development (raygui_styler).
+ - [Victor Fisac](https://github.com/victorfisac) for developing physics raylib module (physac) and implementing materials and lighting systems... among multiple other improvements and multiple tools and games. 
+ - Albert Martos for helping on raygui and porting examples and game-templates to Android and HTML5.
+ - Ian Eito for helping on raygui and porting examples and game-templates to Android and HTML5.
+ - [procedural](https://github.com/procedural) for testing raylib on Linux, correcting some bugs and adding several mouse functions.
+ - [Chris Hemingway](https://github.com/cHemingway) for improving raylib on OSX build system.
+ - [Emanuele Petriglia](https://github.com/LelixSuper) for working on multiple GNU/Linux improvements and developing [TicTacToe](https://github.com/LelixSuper/TicTacToe) raylib game.
+ - [Joshua Reisenauer](https://github.com/kd7tck) for adding audio modules support (XM, MOD) and reviewing audio system.
+ - [Marcelo Paez](https://github.com/paezao) for his help on OSX to solve High DPI display issue. Thanks Marcelo!
+ - [Ghassan Al-Mashareqa](https://github.com/ghassanpl) for his amazing contribution with raylib Lua module, I just work over his code to implement [rlua](https://github.com/raysan5/raylib/blob/master/src/rlua.h)
+ - [Teodor Stoenescu](https://github.com/teodor-stoenescu) for his improvements on OBJ object loading.
+ - [RDR8](https://github.com/RDR8) for helping with Linux build improvements
+ - [Saggi Mizrahi](https://github.com/ficoos) for multiple fixes on Linux and audio system
+ - [Daniel Lemos](https://github.com/xspager) for fixing issues on Linux games building
+ - [Joel Davis](https://github.com/joeld42) for adding raycast picking utilities and a [great example](https://github.com/raysan5/raylib/blob/master/examples/models/models_mesh_picking.c)
+ - [Richard Goodwin](https://github.com/AudioMorphology) for adding RPI touchscreen support
+ - [Milan Nikolic](https://github.com/gen2brain) for adding Android build support with custom standalone toolchain
+ 
+ ** TODO **
+ 
+Please, if I forget someone in this list, excuse me and write me an email to remind me to add you!

+ 2 - 2
HELPME.md

@@ -8,12 +8,12 @@ The following help is highly appreciated:
 
 
 	- C programming - Can you write / review / test / improve the code? 
 	- C programming - Can you write / review / test / improve the code? 
 	- Documentation / Tutorials / Example writters - Can you write some tutorial / example?
 	- Documentation / Tutorials / Example writters - Can you write some tutorial / example?
-	- Web Development - Can you help with the web? Can you setup a forum?
+	- Web Development - Can you help with the web? SEO, style, code writting: https://github.com/raysan5/raylib.com
 	- Porting to Linux, OSX, RaspberryPi, consoles... - Can you compile and test raylib on another systems?
 	- Porting to Linux, OSX, RaspberryPi, consoles... - Can you compile and test raylib on another systems?
 	- Testers of current features and multiple systems - Can you find some bug on raylib?
 	- Testers of current features and multiple systems - Can you find some bug on raylib?
 
 
 If you can not help on any of the above points but you still want to contribute in some way... please, consider helping 
 If you can not help on any of the above points but you still want to contribute in some way... please, consider helping 
-with a small [donation](http://www.raylib.com/helpme.html) or contributing with [raylib patreon](https://www.patreon.com/raysan5). It will really motivate to continue improving this project (and pay some bills… or some coffee).
+with a small [donation](http://www.raylib.com/helpme.html) or contributing with [raylib patreon](https://www.patreon.com/raysan5). It will really motivate to continue improving this project...
 
 
 raylib philosophy
 raylib philosophy
 ------------------
 ------------------

+ 5 - 3
ROADMAP.md

@@ -7,12 +7,14 @@ 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/develop/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!
 Note that [raylib source code](https://github.com/raysan5/raylib/tree/develop/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 1.x**
 **raylib 1.x**
- - [ ] Improved Materials system with PBR support
  - [ ] Basic GPU stats sytem (memory, draws, time...)
  - [ ] Basic GPU stats sytem (memory, draws, time...)
  - [ ] Improved custom file-format (.rres) and packaging tool
  - [ ] Improved custom file-format (.rres) and packaging tool
- - [ ] Procedural image generation functions (spot, gradient, noise...)
- - [ ] Procedural mesh generation functions (cube, cone, sphere...)
  - [ ] Touch-based camera controls for Android
  - [ ] Touch-based camera controls for Android
+ 
+**raylib 1.8**
+ - [x] Improved Materials system with PBR support
+ - [x] Procedural image generation functions (spot, gradient, noise...)
+ - [x] Procedural mesh generation functions (cube, sphere...)
 
 
 **raylib 1.7**
 **raylib 1.7**
  - [x] Support configuration flags
  - [x] Support configuration flags

+ 1 - 1
meson.build

@@ -1,4 +1,4 @@
-project('raylib', 'c', version:	'1.7.0',
+project('raylib', 'c', version:	'1.8.0',
 		license: 'zlib',
 		license: 'zlib',
 		meson_version: '>= 0.39.1',
 		meson_version: '>= 0.39.1',
 		default_options : 'c_std=gnu99')
 		default_options : 'c_std=gnu99')

BIN
release/android/armeabi-v7a/libraylib.a


+ 7 - 8
src/Makefile

@@ -97,14 +97,9 @@ ifeq ($(PLATFORM),PLATFORM_WEB)
 endif
 endif
 
 
 ifeq ($(PLATFORM),PLATFORM_ANDROID)
 ifeq ($(PLATFORM),PLATFORM_ANDROID)
-    # Android NDK path
-    # NOTE: Required for standalone toolchain generation
-    ANDROID_NDK = $(ANDROID_NDK_HOME)
-    
-    # Android standalone toolchain path
-    # NOTE: This path is also used if toolchain generation
-    #ANDROID_TOOLCHAIN = $(CURDIR)/toolchain
-    ANDROID_TOOLCHAIN = $(RAYLIB_PATH)/android-toolchain
+    # Android required path variables
+    ANDROID_NDK = C:/android-ndk
+    ANDROID_TOOLCHAIN = C:/android_toolchain_arm_api16
 
 
     # Android architecture: ARM or ARM64
     # Android architecture: ARM or ARM64
     ANDROID_ARCH ?= ARM
     ANDROID_ARCH ?= ARM
@@ -217,6 +212,10 @@ ifeq ($(PLATFORM),PLATFORM_WEB)
     # -s USE_PTHREADS=1          # multithreading support
     # -s USE_PTHREADS=1          # multithreading support
 endif
 endif
 
 
+ifeq ($(PLATFORM),PLATFORM_ANDROID)
+    CFLAGS += -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16
+endif
+
 #CFLAGSEXTRA = -Wextra -Wmissing-prototypes -Wstrict-prototypes
 #CFLAGSEXTRA = -Wextra -Wmissing-prototypes -Wstrict-prototypes
 
 
 # if shared library required, make sure code is compiled as position independent
 # if shared library required, make sure code is compiled as position independent

+ 1 - 0
src/audio.c

@@ -69,6 +69,7 @@
 #define SUPPORT_FILEFORMAT_WAV
 #define SUPPORT_FILEFORMAT_WAV
 #define SUPPORT_FILEFORMAT_OGG
 #define SUPPORT_FILEFORMAT_OGG
 #define SUPPORT_FILEFORMAT_XM
 #define SUPPORT_FILEFORMAT_XM
+#define SUPPORT_FILEFORMAT_MOD
 //-------------------------------------------------
 //-------------------------------------------------
 
 
 #if defined(AUDIO_STANDALONE)
 #if defined(AUDIO_STANDALONE)

+ 18 - 0
src/core.c

@@ -659,6 +659,14 @@ void SetWindowIcon(Image image)
 #endif
 #endif
 }
 }
 
 
+// Set title for window (only PLATFORM_DESKTOP)
+void SetWindowTitle(const char *title)
+{
+#if defined(PLATFORM_DESKTOP)
+    glfwSetWindowTitle(window, title);
+#endif
+}
+
 // Set window position on screen (windowed mode)
 // Set window position on screen (windowed mode)
 void SetWindowPosition(int x, int y)
 void SetWindowPosition(int x, int y)
 {
 {
@@ -1152,6 +1160,16 @@ bool IsFileExtension(const char *fileName, const char *ext)
     return result;
     return result;
 }
 }
 
 
+// Get the extension for a filename
+const char *GetExtension(const char *fileName)
+{
+    const char *dot = strrchr(fileName, '.');
+    
+    if (!dot || dot == fileName) return "";
+    
+    return (dot + 1);
+}
+
 // Get directory for a given fileName (with path)
 // Get directory for a given fileName (with path)
 const char *GetDirectoryPath(const char *fileName)
 const char *GetDirectoryPath(const char *fileName)
 {
 {

+ 2050 - 0
src/external/par_shapes.h

@@ -0,0 +1,2050 @@
+// SHAPES :: https://github.com/prideout/par
+// Simple C library for creation and manipulation of triangle meshes.
+//
+// The API is divided into three sections:
+//
+//   - Generators.  Create parametric surfaces, platonic solids, etc.
+//   - Queries.     Ask a mesh for its axis-aligned bounding box, etc.
+//   - Transforms.  Rotate a mesh, merge it with another, add normals, etc.
+//
+// In addition to the comment block above each function declaration, the API
+// has informal documentation here:
+//
+//     http://github.prideout.net/shapes/
+//
+// For our purposes, a "mesh" is a list of points and a list of triangles; the
+// former is a flattened list of three-tuples (32-bit floats) and the latter is
+// also a flattened list of three-tuples (16-bit uints).  Triangles are always
+// oriented such that their front face winds counter-clockwise.
+//
+// Optionally, meshes can contain 3D normals (one per vertex), and 2D texture
+// coordinates (one per vertex).  That's it!  If you need something fancier,
+// look elsewhere.
+//
+// The MIT License
+// Copyright (c) 2015 Philip Rideout
+
+#ifndef PAR_SHAPES_H
+#define PAR_SHAPES_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+// Ray: commented to avoid conflict with raylib bool
+/*
+#if !defined(_MSC_VER)
+# include <stdbool.h>
+#else // MSVC
+# if _MSC_VER >= 1800
+#  include <stdbool.h>
+# else // stdbool.h missing prior to MSVC++ 12.0 (VS2013)
+//#  define bool int      
+//#  define true 1
+//#  define false 0
+# endif
+#endif
+*/
+
+#ifndef PAR_SHAPES_T
+#define PAR_SHAPES_T uint16_t
+#endif
+
+typedef struct par_shapes_mesh_s {
+    float* points;           // Flat list of 3-tuples (X Y Z X Y Z...)
+    int npoints;             // Number of points
+    PAR_SHAPES_T* triangles; // Flat list of 3-tuples (I J K I J K...)
+    int ntriangles;          // Number of triangles
+    float* normals;          // Optional list of 3-tuples (X Y Z X Y Z...)
+    float* tcoords;          // Optional list of 2-tuples (U V U V U V...)
+} par_shapes_mesh;
+
+void par_shapes_free_mesh(par_shapes_mesh*);
+
+// Generators ------------------------------------------------------------------
+
+// Instance a cylinder that sits on the Z=0 plane using the given tessellation
+// levels across the UV domain.  Think of "slices" like a number of pizza
+// slices, and "stacks" like a number of stacked rings.  Height and radius are
+// both 1.0, but they can easily be changed with par_shapes_scale.
+par_shapes_mesh* par_shapes_create_cylinder(int slices, int stacks);
+
+// Create a donut that sits on the Z=0 plane with the specified inner radius.
+// The outer radius can be controlled with par_shapes_scale.
+par_shapes_mesh* par_shapes_create_torus(int slices, int stacks, float radius);
+
+// Create a sphere with texture coordinates and small triangles near the poles.
+par_shapes_mesh* par_shapes_create_parametric_sphere(int slices, int stacks);
+
+// Approximate a sphere with a subdivided icosahedron, which produces a nice
+// distribution of triangles, but no texture coordinates.  Each subdivision
+// level scales the number of triangles by four, so use a very low number.
+par_shapes_mesh* par_shapes_create_subdivided_sphere(int nsubdivisions);
+
+// More parametric surfaces.
+par_shapes_mesh* par_shapes_create_klein_bottle(int slices, int stacks);
+par_shapes_mesh* par_shapes_create_trefoil_knot(int slices, int stacks,
+    float radius);
+par_shapes_mesh* par_shapes_create_hemisphere(int slices, int stacks);
+par_shapes_mesh* par_shapes_create_plane(int slices, int stacks);
+
+// Create a parametric surface from a callback function that consumes a 2D
+// point in [0,1] and produces a 3D point.
+typedef void (*par_shapes_fn)(float const*, float*, void*);
+par_shapes_mesh* par_shapes_create_parametric(par_shapes_fn, int slices,
+    int stacks, void* userdata);
+
+// Generate points for a 20-sided polyhedron that fits in the unit sphere.
+// Texture coordinates and normals are not generated.
+par_shapes_mesh* par_shapes_create_icosahedron();
+
+// Generate points for a 12-sided polyhedron that fits in the unit sphere.
+// Again, texture coordinates and normals are not generated.
+par_shapes_mesh* par_shapes_create_dodecahedron();
+
+// More platonic solids.
+par_shapes_mesh* par_shapes_create_octahedron();
+par_shapes_mesh* par_shapes_create_tetrahedron();
+par_shapes_mesh* par_shapes_create_cube();
+
+// Generate an orientable disk shape in 3-space.  Does not include normals or
+// texture coordinates.
+par_shapes_mesh* par_shapes_create_disk(float radius, int slices,
+    float const* center, float const* normal);
+
+// Create an empty shape.  Useful for building scenes with merge_and_free.
+par_shapes_mesh* par_shapes_create_empty();
+
+// Generate a rock shape that sits on the Y=0 plane, and sinks into it a bit.
+// This includes smooth normals but no texture coordinates.  Each subdivision
+// level scales the number of triangles by four, so use a very low number.
+par_shapes_mesh* par_shapes_create_rock(int seed, int nsubdivisions);
+
+// Create trees or vegetation by executing a recursive turtle graphics program.
+// The program is a list of command-argument pairs.  See the unit test for
+// an example.  Texture coordinates and normals are not generated.
+par_shapes_mesh* par_shapes_create_lsystem(char const* program, int slices,
+    int maxdepth);
+
+// Queries ---------------------------------------------------------------------
+
+// Dump out a text file conforming to the venerable OBJ format.
+void par_shapes_export(par_shapes_mesh const*, char const* objfile);
+
+// Take a pointer to 6 floats and set them to min xyz, max xyz.
+void par_shapes_compute_aabb(par_shapes_mesh const* mesh, float* aabb);
+
+// Make a deep copy of a mesh.  To make a brand new copy, pass null to "target".
+// To avoid memory churn, pass an existing mesh to "target".
+par_shapes_mesh* par_shapes_clone(par_shapes_mesh const* mesh,
+    par_shapes_mesh* target);
+
+// Transformations -------------------------------------------------------------
+
+void par_shapes_merge(par_shapes_mesh* dst, par_shapes_mesh const* src);
+void par_shapes_translate(par_shapes_mesh*, float x, float y, float z);
+void par_shapes_rotate(par_shapes_mesh*, float radians, float const* axis);
+void par_shapes_scale(par_shapes_mesh*, float x, float y, float z);
+void par_shapes_merge_and_free(par_shapes_mesh* dst, par_shapes_mesh* src);
+
+// Reverse the winding of a run of faces.  Useful when drawing the inside of
+// a Cornell Box.  Pass 0 for nfaces to reverse every face in the mesh.
+void par_shapes_invert(par_shapes_mesh*, int startface, int nfaces);
+
+// Remove all triangles whose area is less than minarea.
+void par_shapes_remove_degenerate(par_shapes_mesh*, float minarea);
+
+// Dereference the entire index buffer and replace the point list.
+// This creates an inefficient structure, but is useful for drawing facets.
+// If create_indices is true, a trivial "0 1 2 3..." index buffer is generated.
+void par_shapes_unweld(par_shapes_mesh* mesh, bool create_indices);
+
+// Merge colocated verts, build a new index buffer, and return the
+// optimized mesh.  Epsilon is the maximum distance to consider when
+// welding vertices. The mapping argument can be null, or a pointer to
+// npoints integers, which gets filled with the mapping from old vertex
+// indices to new indices.
+par_shapes_mesh* par_shapes_weld(par_shapes_mesh const*, float epsilon,
+    PAR_SHAPES_T* mapping);
+
+// Compute smooth normals by averaging adjacent facet normals.
+void par_shapes_compute_normals(par_shapes_mesh* m);
+
+#ifndef PAR_PI
+#define PAR_PI (3.14159265359)
+#define PAR_MIN(a, b) (a > b ? b : a)
+#define PAR_MAX(a, b) (a > b ? a : b)
+#define PAR_CLAMP(v, lo, hi) PAR_MAX(lo, PAR_MIN(hi, v))
+#define PAR_SWAP(T, A, B) { T tmp = B; B = A; A = tmp; }
+#define PAR_SQR(a) ((a) * (a))
+#endif
+
+#ifndef PAR_MALLOC
+#define PAR_MALLOC(T, N) ((T*) malloc(N * sizeof(T)))
+#define PAR_CALLOC(T, N) ((T*) calloc(N * sizeof(T), 1))
+#define PAR_REALLOC(T, BUF, N) ((T*) realloc(BUF, sizeof(T) * (N)))
+#define PAR_FREE(BUF) free(BUF)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+// -----------------------------------------------------------------------------
+// END PUBLIC API
+// -----------------------------------------------------------------------------
+
+#ifdef PAR_SHAPES_IMPLEMENTATION
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <float.h>
+#include <string.h>
+#include <math.h>
+#include <errno.h>
+
+static void par_shapes__sphere(float const* uv, float* xyz, void*);
+static void par_shapes__hemisphere(float const* uv, float* xyz, void*);
+static void par_shapes__plane(float const* uv, float* xyz, void*);
+static void par_shapes__klein(float const* uv, float* xyz, void*);
+static void par_shapes__cylinder(float const* uv, float* xyz, void*);
+static void par_shapes__torus(float const* uv, float* xyz, void*);
+static void par_shapes__trefoil(float const* uv, float* xyz, void*);
+
+struct osn_context;
+static int par__simplex_noise(int64_t seed, struct osn_context** ctx);
+static void par__simplex_noise_free(struct osn_context* ctx);
+static double par__simplex_noise2(struct osn_context* ctx, double x, double y);
+
+static void par_shapes__copy3(float* result, float const* a)
+{
+    result[0] = a[0];
+    result[1] = a[1];
+    result[2] = a[2];
+}
+
+static float par_shapes__dot3(float const* a, float const* b)
+{
+    return b[0] * a[0] + b[1] * a[1] + b[2] * a[2];
+}
+
+static void par_shapes__transform3(float* p, float const* x, float const* y,
+    float const* z)
+{
+    float px = par_shapes__dot3(p, x);
+    float py = par_shapes__dot3(p, y);
+    float pz = par_shapes__dot3(p, z);
+    p[0] = px;
+    p[1] = py;
+    p[2] = pz;
+}
+
+static void par_shapes__cross3(float* result, float const* a, float const* b)
+{
+    float x = (a[1] * b[2]) - (a[2] * b[1]);
+    float y = (a[2] * b[0]) - (a[0] * b[2]);
+    float z = (a[0] * b[1]) - (a[1] * b[0]);
+    result[0] = x;
+    result[1] = y;
+    result[2] = z;
+}
+
+static void par_shapes__mix3(float* d, float const* a, float const* b, float t)
+{
+    float x = b[0] * t + a[0] * (1 - t);
+    float y = b[1] * t + a[1] * (1 - t);
+    float z = b[2] * t + a[2] * (1 - t);
+    d[0] = x;
+    d[1] = y;
+    d[2] = z;
+}
+
+static void par_shapes__scale3(float* result, float a)
+{
+    result[0] *= a;
+    result[1] *= a;
+    result[2] *= a;
+}
+
+static void par_shapes__normalize3(float* v)
+{
+    float lsqr = sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]);
+    if (lsqr > 0) {
+        par_shapes__scale3(v, 1.0f / lsqr);
+    }
+}
+
+static void par_shapes__subtract3(float* result, float const* a)
+{
+    result[0] -= a[0];
+    result[1] -= a[1];
+    result[2] -= a[2];
+}
+
+static void par_shapes__add3(float* result, float const* a)
+{
+    result[0] += a[0];
+    result[1] += a[1];
+    result[2] += a[2];
+}
+
+static float par_shapes__sqrdist3(float const* a, float const* b)
+{
+    float dx = a[0] - b[0];
+    float dy = a[1] - b[1];
+    float dz = a[2] - b[2];
+    return dx * dx + dy * dy + dz * dz;
+}
+
+static void par_shapes__compute_welded_normals(par_shapes_mesh* m)
+{
+    m->normals = PAR_MALLOC(float, m->npoints * 3);
+    PAR_SHAPES_T* weldmap = PAR_MALLOC(PAR_SHAPES_T, m->npoints);
+    par_shapes_mesh* welded = par_shapes_weld(m, 0.01, weldmap);
+    par_shapes_compute_normals(welded);
+    float* pdst = m->normals;
+    for (int i = 0; i < m->npoints; i++, pdst += 3) {
+        int d = weldmap[i];
+        float const* pnormal = welded->normals + d * 3;
+        pdst[0] = pnormal[0];
+        pdst[1] = pnormal[1];
+        pdst[2] = pnormal[2];
+    }
+    PAR_FREE(weldmap);
+    par_shapes_free_mesh(welded);
+}
+
+par_shapes_mesh* par_shapes_create_cylinder(int slices, int stacks)
+{
+    if (slices < 3 || stacks < 1) {
+        return 0;
+    }
+    return par_shapes_create_parametric(par_shapes__cylinder, slices,
+        stacks, 0);
+}
+
+par_shapes_mesh* par_shapes_create_parametric_sphere(int slices, int stacks)
+{
+    if (slices < 3 || stacks < 3) {
+        return 0;
+    }
+    par_shapes_mesh* m = par_shapes_create_parametric(par_shapes__sphere,
+        slices, stacks, 0);
+    par_shapes_remove_degenerate(m, 0.0001);
+    return m;
+}
+
+par_shapes_mesh* par_shapes_create_hemisphere(int slices, int stacks)
+{
+    if (slices < 3 || stacks < 3) {
+        return 0;
+    }
+    par_shapes_mesh* m = par_shapes_create_parametric(par_shapes__hemisphere,
+        slices, stacks, 0);
+    par_shapes_remove_degenerate(m, 0.0001);
+    return m;
+}
+
+par_shapes_mesh* par_shapes_create_torus(int slices, int stacks, float radius)
+{
+    if (slices < 3 || stacks < 3) {
+        return 0;
+    }
+    assert(radius <= 1.0 && "Use smaller radius to avoid self-intersection.");
+    assert(radius >= 0.1 && "Use larger radius to avoid self-intersection.");
+    void* userdata = (void*) &radius;
+    return par_shapes_create_parametric(par_shapes__torus, slices,
+        stacks, userdata);
+}
+
+par_shapes_mesh* par_shapes_create_klein_bottle(int slices, int stacks)
+{
+    if (slices < 3 || stacks < 3) {
+        return 0;
+    }
+    par_shapes_mesh* mesh = par_shapes_create_parametric(
+        par_shapes__klein, slices, stacks, 0);
+    int face = 0;
+    for (int stack = 0; stack < stacks; stack++) {
+        for (int slice = 0; slice < slices; slice++, face += 2) {
+            if (stack < 27 * stacks / 32) {
+                par_shapes_invert(mesh, face, 2);
+            }
+        }
+    }
+    par_shapes__compute_welded_normals(mesh);
+    return mesh;
+}
+
+par_shapes_mesh* par_shapes_create_trefoil_knot(int slices, int stacks,
+    float radius)
+{
+    if (slices < 3 || stacks < 3) {
+        return 0;
+    }
+    assert(radius <= 3.0 && "Use smaller radius to avoid self-intersection.");
+    assert(radius >= 0.5 && "Use larger radius to avoid self-intersection.");
+    void* userdata = (void*) &radius;
+    return par_shapes_create_parametric(par_shapes__trefoil, slices,
+        stacks, userdata);
+}
+
+par_shapes_mesh* par_shapes_create_plane(int slices, int stacks)
+{
+    if (slices < 1 || stacks < 1) {
+        return 0;
+    }
+    return par_shapes_create_parametric(par_shapes__plane, slices,
+        stacks, 0);
+}
+
+par_shapes_mesh* par_shapes_create_parametric(par_shapes_fn fn,
+    int slices, int stacks, void* userdata)
+{
+    par_shapes_mesh* mesh = PAR_CALLOC(par_shapes_mesh, 1);
+
+    // Generate verts.
+    mesh->npoints = (slices + 1) * (stacks + 1);
+    mesh->points = PAR_CALLOC(float, 3 * mesh->npoints);
+    float uv[2];
+    float xyz[3];
+    float* points = mesh->points;
+    for (int stack = 0; stack < stacks + 1; stack++) {
+        uv[0] = (float) stack / stacks;
+        for (int slice = 0; slice < slices + 1; slice++) {
+            uv[1] = (float) slice / slices;
+            fn(uv, xyz, userdata);
+            *points++ = xyz[0];
+            *points++ = xyz[1];
+            *points++ = xyz[2];
+        }
+    }
+
+    // Generate texture coordinates.
+    mesh->tcoords = PAR_CALLOC(float, 2 * mesh->npoints);
+    float* uvs = mesh->tcoords;
+    for (int stack = 0; stack < stacks + 1; stack++) {
+        uv[0] = (float) stack / stacks;
+        for (int slice = 0; slice < slices + 1; slice++) {
+            uv[1] = (float) slice / slices;
+            *uvs++ = uv[0];
+            *uvs++ = uv[1];
+        }
+    }
+
+    // Generate faces.
+    mesh->ntriangles = 2 * slices * stacks;
+    mesh->triangles = PAR_CALLOC(PAR_SHAPES_T, 3 * mesh->ntriangles);
+    int v = 0;
+    PAR_SHAPES_T* face = mesh->triangles;
+    for (int stack = 0; stack < stacks; stack++) {
+        for (int slice = 0; slice < slices; slice++) {
+            int next = slice + 1;
+            *face++ = v + slice + slices + 1;
+            *face++ = v + next;
+            *face++ = v + slice;
+            *face++ = v + slice + slices + 1;
+            *face++ = v + next + slices + 1;
+            *face++ = v + next;
+        }
+        v += slices + 1;
+    }
+
+    par_shapes__compute_welded_normals(mesh);
+    return mesh;
+}
+
+void par_shapes_free_mesh(par_shapes_mesh* mesh)
+{
+    PAR_FREE(mesh->points);
+    PAR_FREE(mesh->triangles);
+    PAR_FREE(mesh->normals);
+    PAR_FREE(mesh->tcoords);
+    PAR_FREE(mesh);
+}
+
+void par_shapes_export(par_shapes_mesh const* mesh, char const* filename)
+{
+    FILE* objfile = fopen(filename, "wt");
+    float const* points = mesh->points;
+    float const* tcoords = mesh->tcoords;
+    float const* norms = mesh->normals;
+    PAR_SHAPES_T const* indices = mesh->triangles;
+    if (tcoords && norms) {
+        for (int nvert = 0; nvert < mesh->npoints; nvert++) {
+            fprintf(objfile, "v %f %f %f\n", points[0], points[1], points[2]);
+            fprintf(objfile, "vt %f %f\n", tcoords[0], tcoords[1]);
+            fprintf(objfile, "vn %f %f %f\n", norms[0], norms[1], norms[2]);
+            points += 3;
+            norms += 3;
+            tcoords += 2;
+        }
+        for (int nface = 0; nface < mesh->ntriangles; nface++) {
+            int a = 1 + *indices++;
+            int b = 1 + *indices++;
+            int c = 1 + *indices++;
+            fprintf(objfile, "f %d/%d/%d %d/%d/%d %d/%d/%d\n",
+                a, a, a, b, b, b, c, c, c);
+        }
+    } else if (norms) {
+        for (int nvert = 0; nvert < mesh->npoints; nvert++) {
+            fprintf(objfile, "v %f %f %f\n", points[0], points[1], points[2]);
+            fprintf(objfile, "vn %f %f %f\n", norms[0], norms[1], norms[2]);
+            points += 3;
+            norms += 3;
+        }
+        for (int nface = 0; nface < mesh->ntriangles; nface++) {
+            int a = 1 + *indices++;
+            int b = 1 + *indices++;
+            int c = 1 + *indices++;
+            fprintf(objfile, "f %d//%d %d//%d %d//%d\n", a, a, b, b, c, c);
+        }
+    } else if (tcoords) {
+        for (int nvert = 0; nvert < mesh->npoints; nvert++) {
+            fprintf(objfile, "v %f %f %f\n", points[0], points[1], points[2]);
+            fprintf(objfile, "vt %f %f\n", tcoords[0], tcoords[1]);
+            points += 3;
+            tcoords += 2;
+        }
+        for (int nface = 0; nface < mesh->ntriangles; nface++) {
+            int a = 1 + *indices++;
+            int b = 1 + *indices++;
+            int c = 1 + *indices++;
+            fprintf(objfile, "f %d/%d %d/%d %d/%d\n", a, a, b, b, c, c);
+        }
+    } else {
+        for (int nvert = 0; nvert < mesh->npoints; nvert++) {
+            fprintf(objfile, "v %f %f %f\n", points[0], points[1], points[2]);
+            points += 3;
+        }
+        for (int nface = 0; nface < mesh->ntriangles; nface++) {
+            int a = 1 + *indices++;
+            int b = 1 + *indices++;
+            int c = 1 + *indices++;
+            fprintf(objfile, "f %d %d %d\n", a, b, c);
+        }
+    }
+    fclose(objfile);
+}
+
+static void par_shapes__sphere(float const* uv, float* xyz, void* userdata)
+{
+    float phi = uv[0] * PAR_PI;
+    float theta = uv[1] * 2 * PAR_PI;
+    xyz[0] = cosf(theta) * sinf(phi);
+    xyz[1] = sinf(theta) * sinf(phi);
+    xyz[2] = cosf(phi);
+}
+
+static void par_shapes__hemisphere(float const* uv, float* xyz, void* userdata)
+{
+    float phi = uv[0] * PAR_PI;
+    float theta = uv[1] * PAR_PI;
+    xyz[0] = cosf(theta) * sinf(phi);
+    xyz[1] = sinf(theta) * sinf(phi);
+    xyz[2] = cosf(phi);
+}
+
+static void par_shapes__plane(float const* uv, float* xyz, void* userdata)
+{
+    xyz[0] = uv[0];
+    xyz[1] = uv[1];
+    xyz[2] = 0;
+}
+
+static void par_shapes__klein(float const* uv, float* xyz, void* userdata)
+{
+    float u = uv[0] * PAR_PI;
+    float v = uv[1] * 2 * PAR_PI;
+    u = u * 2;
+    if (u < PAR_PI) {
+        xyz[0] = 3 * cosf(u) * (1 + sinf(u)) + (2 * (1 - cosf(u) / 2)) *
+            cosf(u) * cosf(v);
+        xyz[2] = -8 * sinf(u) - 2 * (1 - cosf(u) / 2) * sinf(u) * cosf(v);
+    } else {
+        xyz[0] = 3 * cosf(u) * (1 + sinf(u)) + (2 * (1 - cosf(u) / 2)) *
+            cosf(v + PAR_PI);
+        xyz[2] = -8 * sinf(u);
+    }
+    xyz[1] = -2 * (1 - cosf(u) / 2) * sinf(v);
+}
+
+static void par_shapes__cylinder(float const* uv, float* xyz, void* userdata)
+{
+    float theta = uv[1] * 2 * PAR_PI;
+    xyz[0] = sinf(theta);
+    xyz[1] = cosf(theta);
+    xyz[2] = uv[0];
+}
+
+static void par_shapes__torus(float const* uv, float* xyz, void* userdata)
+{
+    float major = 1;
+    float minor = *((float*) userdata);
+    float theta = uv[0] * 2 * PAR_PI;
+    float phi = uv[1] * 2 * PAR_PI;
+    float beta = major + minor * cosf(phi);
+    xyz[0] = cosf(theta) * beta;
+    xyz[1] = sinf(theta) * beta;
+    xyz[2] = sinf(phi) * minor;
+}
+
+static void par_shapes__trefoil(float const* uv, float* xyz, void* userdata)
+{
+    float minor = *((float*) userdata);
+    const float a = 0.5f;
+    const float b = 0.3f;
+    const float c = 0.5f;
+    const float d = minor * 0.1f;
+    const float u = (1 - uv[0]) * 4 * PAR_PI;
+    const float v = uv[1] * 2 * PAR_PI;
+    const float r = a + b * cos(1.5f * u);
+    const float x = r * cos(u);
+    const float y = r * sin(u);
+    const float z = c * sin(1.5f * u);
+    float q[3];
+    q[0] =
+        -1.5f * b * sin(1.5f * u) * cos(u) - (a + b * cos(1.5f * u)) * sin(u);
+    q[1] =
+        -1.5f * b * sin(1.5f * u) * sin(u) + (a + b * cos(1.5f * u)) * cos(u);
+    q[2] = 1.5f * c * cos(1.5f * u);
+    par_shapes__normalize3(q);
+    float qvn[3] = {q[1], -q[0], 0};
+    par_shapes__normalize3(qvn);
+    float ww[3];
+    par_shapes__cross3(ww, q, qvn);
+    xyz[0] = x + d * (qvn[0] * cos(v) + ww[0] * sin(v));
+    xyz[1] = y + d * (qvn[1] * cos(v) + ww[1] * sin(v));
+    xyz[2] = z + d * ww[2] * sin(v);
+}
+
+void par_shapes_merge(par_shapes_mesh* dst, par_shapes_mesh const* src)
+{
+    PAR_SHAPES_T offset = dst->npoints;
+    int npoints = dst->npoints + src->npoints;
+    int vecsize = sizeof(float) * 3;
+    dst->points = PAR_REALLOC(float, dst->points, 3 * npoints);
+    memcpy(dst->points + 3 * dst->npoints, src->points, vecsize * src->npoints);
+    dst->npoints = npoints;
+    if (src->normals || dst->normals) {
+        dst->normals = PAR_REALLOC(float, dst->normals, 3 * npoints);
+        if (src->normals) {
+            memcpy(dst->normals + 3 * offset, src->normals,
+                vecsize * src->npoints);
+        }
+    }
+    if (src->tcoords || dst->tcoords) {
+        int uvsize = sizeof(float) * 2;
+        dst->tcoords = PAR_REALLOC(float, dst->tcoords, 2 * npoints);
+        if (src->tcoords) {
+            memcpy(dst->tcoords + 2 * offset, src->tcoords,
+                uvsize * src->npoints);
+        }
+    }
+    int ntriangles = dst->ntriangles + src->ntriangles;
+    dst->triangles = PAR_REALLOC(PAR_SHAPES_T, dst->triangles, 3 * ntriangles);
+    PAR_SHAPES_T* ptriangles = dst->triangles + 3 * dst->ntriangles;
+    PAR_SHAPES_T const* striangles = src->triangles;
+    for (int i = 0; i < src->ntriangles; i++) {
+        *ptriangles++ = offset + *striangles++;
+        *ptriangles++ = offset + *striangles++;
+        *ptriangles++ = offset + *striangles++;
+    }
+    dst->ntriangles = ntriangles;
+}
+
+par_shapes_mesh* par_shapes_create_disk(float radius, int slices,
+    float const* center, float const* normal)
+{
+    par_shapes_mesh* mesh = PAR_CALLOC(par_shapes_mesh, 1);
+    mesh->npoints = slices + 1;
+    mesh->points = PAR_MALLOC(float, 3 * mesh->npoints);
+    float* points = mesh->points;
+    *points++ = 0;
+    *points++ = 0;
+    *points++ = 0;
+    for (int i = 0; i < slices; i++) {
+        float theta = i * PAR_PI * 2 / slices;
+        *points++ = radius * cos(theta);
+        *points++ = radius * sin(theta);
+        *points++ = 0;
+    }
+    float nnormal[3] = {normal[0], normal[1], normal[2]};
+    par_shapes__normalize3(nnormal);
+    mesh->normals = PAR_MALLOC(float, 3 * mesh->npoints);
+    float* norms = mesh->normals;
+    for (int i = 0; i < mesh->npoints; i++) {
+        *norms++ = nnormal[0];
+        *norms++ = nnormal[1];
+        *norms++ = nnormal[2];
+    }
+    mesh->ntriangles = slices;
+    mesh->triangles = PAR_MALLOC(PAR_SHAPES_T, 3 * mesh->ntriangles);
+    PAR_SHAPES_T* triangles = mesh->triangles;
+    for (int i = 0; i < slices; i++) {
+        *triangles++ = 0;
+        *triangles++ = 1 + i;
+        *triangles++ = 1 + (i + 1) % slices;
+    }
+    float k[3] = {0, 0, -1};
+    float axis[3];
+    par_shapes__cross3(axis, nnormal, k);
+    par_shapes__normalize3(axis);
+    par_shapes_rotate(mesh, acos(nnormal[2]), axis);
+    par_shapes_translate(mesh, center[0], center[1], center[2]);
+    return mesh;
+}
+
+par_shapes_mesh* par_shapes_create_empty()
+{
+    return PAR_CALLOC(par_shapes_mesh, 1);
+}
+
+void par_shapes_translate(par_shapes_mesh* m, float x, float y, float z)
+{
+    float* points = m->points;
+    for (int i = 0; i < m->npoints; i++) {
+        *points++ += x;
+        *points++ += y;
+        *points++ += z;
+    }
+}
+
+void par_shapes_rotate(par_shapes_mesh* mesh, float radians, float const* axis)
+{
+    float s = sinf(radians);
+    float c = cosf(radians);
+    float x = axis[0];
+    float y = axis[1];
+    float z = axis[2];
+    float xy = x * y;
+    float yz = y * z;
+    float zx = z * x;
+    float oneMinusC = 1.0f - c;
+    float col0[3] = {
+        (((x * x) * oneMinusC) + c),
+        ((xy * oneMinusC) + (z * s)), ((zx * oneMinusC) - (y * s))
+    };
+    float col1[3] = {
+        ((xy * oneMinusC) - (z * s)),
+        (((y * y) * oneMinusC) + c), ((yz * oneMinusC) + (x * s))
+    };
+    float col2[3] = {
+        ((zx * oneMinusC) + (y * s)),
+        ((yz * oneMinusC) - (x * s)), (((z * z) * oneMinusC) + c)
+    };
+    float* p = mesh->points;
+    for (int i = 0; i < mesh->npoints; i++, p += 3) {
+        float x = col0[0] * p[0] + col1[0] * p[1] + col2[0] * p[2];
+        float y = col0[1] * p[0] + col1[1] * p[1] + col2[1] * p[2];
+        float z = col0[2] * p[0] + col1[2] * p[1] + col2[2] * p[2];
+        p[0] = x;
+        p[1] = y;
+        p[2] = z;
+    }
+    p = mesh->normals;
+    if (p) {
+        for (int i = 0; i < mesh->npoints; i++, p += 3) {
+            float x = col0[0] * p[0] + col1[0] * p[1] + col2[0] * p[2];
+            float y = col0[1] * p[0] + col1[1] * p[1] + col2[1] * p[2];
+            float z = col0[2] * p[0] + col1[2] * p[1] + col2[2] * p[2];
+            p[0] = x;
+            p[1] = y;
+            p[2] = z;
+        }
+    }
+}
+
+void par_shapes_scale(par_shapes_mesh* m, float x, float y, float z)
+{
+    float* points = m->points;
+    for (int i = 0; i < m->npoints; i++) {
+        *points++ *= x;
+        *points++ *= y;
+        *points++ *= z;
+    }
+}
+
+void par_shapes_merge_and_free(par_shapes_mesh* dst, par_shapes_mesh* src)
+{
+    par_shapes_merge(dst, src);
+    par_shapes_free_mesh(src);
+}
+
+void par_shapes_compute_aabb(par_shapes_mesh const* m, float* aabb)
+{
+    float* points = m->points;
+    aabb[0] = aabb[3] = points[0];
+    aabb[1] = aabb[4] = points[1];
+    aabb[2] = aabb[5] = points[2];
+    points += 3;
+    for (int i = 1; i < m->npoints; i++, points += 3) {
+        aabb[0] = PAR_MIN(points[0], aabb[0]);
+        aabb[1] = PAR_MIN(points[1], aabb[1]);
+        aabb[2] = PAR_MIN(points[2], aabb[2]);
+        aabb[3] = PAR_MAX(points[0], aabb[3]);
+        aabb[4] = PAR_MAX(points[1], aabb[4]);
+        aabb[5] = PAR_MAX(points[2], aabb[5]);
+    }
+}
+
+void par_shapes_invert(par_shapes_mesh* m, int face, int nfaces)
+{
+    nfaces = nfaces ? nfaces : m->ntriangles;
+    PAR_SHAPES_T* tri = m->triangles + face * 3;
+    for (int i = 0; i < nfaces; i++) {
+        PAR_SWAP(PAR_SHAPES_T, tri[0], tri[2]);
+        tri += 3;
+    }
+}
+
+par_shapes_mesh* par_shapes_create_icosahedron()
+{
+    static float verts[] = {
+        0.000,  0.000,  1.000,
+        0.894,  0.000,  0.447,
+        0.276,  0.851,  0.447,
+        -0.724,  0.526,  0.447,
+        -0.724, -0.526,  0.447,
+        0.276, -0.851,  0.447,
+        0.724,  0.526, -0.447,
+        -0.276,  0.851, -0.447,
+        -0.894,  0.000, -0.447,
+        -0.276, -0.851, -0.447,
+        0.724, -0.526, -0.447,
+        0.000,  0.000, -1.000
+    };
+    static PAR_SHAPES_T faces[] = {
+        0,1,2,
+        0,2,3,
+        0,3,4,
+        0,4,5,
+        0,5,1,
+        7,6,11,
+        8,7,11,
+        9,8,11,
+        10,9,11,
+        6,10,11,
+        6,2,1,
+        7,3,2,
+        8,4,3,
+        9,5,4,
+        10,1,5,
+        6,7,2,
+        7,8,3,
+        8,9,4,
+        9,10,5,
+        10,6,1
+    };
+    par_shapes_mesh* mesh = PAR_CALLOC(par_shapes_mesh, 1);
+    mesh->npoints = sizeof(verts) / sizeof(verts[0]) / 3;
+    mesh->points = PAR_MALLOC(float, sizeof(verts) / 4);
+    memcpy(mesh->points, verts, sizeof(verts));
+    mesh->ntriangles = sizeof(faces) / sizeof(faces[0]) / 3;
+    mesh->triangles = PAR_MALLOC(PAR_SHAPES_T, sizeof(faces) / 2);
+    memcpy(mesh->triangles, faces, sizeof(faces));
+    return mesh;
+}
+
+par_shapes_mesh* par_shapes_create_dodecahedron()
+{
+    static float verts[20 * 3] = {
+        0.607, 0.000, 0.795,
+        0.188, 0.577, 0.795,
+        -0.491, 0.357, 0.795,
+        -0.491, -0.357, 0.795,
+        0.188, -0.577, 0.795,
+        0.982, 0.000, 0.188,
+        0.304, 0.934, 0.188,
+        -0.795, 0.577, 0.188,
+        -0.795, -0.577, 0.188,
+        0.304, -0.934, 0.188,
+        0.795, 0.577, -0.188,
+        -0.304, 0.934, -0.188,
+        -0.982, 0.000, -0.188,
+        -0.304, -0.934, -0.188,
+        0.795, -0.577, -0.188,
+        0.491, 0.357, -0.795,
+        -0.188, 0.577, -0.795,
+        -0.607, 0.000, -0.795,
+        -0.188, -0.577, -0.795,
+        0.491, -0.357, -0.795,
+    };
+    static PAR_SHAPES_T pentagons[12 * 5] = {
+        0,1,2,3,4,
+        5,10,6,1,0,
+        6,11,7,2,1,
+        7,12,8,3,2,
+        8,13,9,4,3,
+        9,14,5,0,4,
+        15,16,11,6,10,
+        16,17,12,7,11,
+        17,18,13,8,12,
+        18,19,14,9,13,
+        19,15,10,5,14,
+        19,18,17,16,15
+    };
+    int npentagons = sizeof(pentagons) / sizeof(pentagons[0]) / 5;
+    par_shapes_mesh* mesh = PAR_CALLOC(par_shapes_mesh, 1);
+    int ncorners = sizeof(verts) / sizeof(verts[0]) / 3;
+    mesh->npoints = ncorners;
+    mesh->points = PAR_MALLOC(float, mesh->npoints * 3);
+    memcpy(mesh->points, verts, sizeof(verts));
+    PAR_SHAPES_T const* pentagon = pentagons;
+    mesh->ntriangles = npentagons * 3;
+    mesh->triangles = PAR_MALLOC(PAR_SHAPES_T, mesh->ntriangles * 3);
+    PAR_SHAPES_T* tris = mesh->triangles;
+    for (int p = 0; p < npentagons; p++, pentagon += 5) {
+        *tris++ = pentagon[0];
+        *tris++ = pentagon[1];
+        *tris++ = pentagon[2];
+        *tris++ = pentagon[0];
+        *tris++ = pentagon[2];
+        *tris++ = pentagon[3];
+        *tris++ = pentagon[0];
+        *tris++ = pentagon[3];
+        *tris++ = pentagon[4];
+    }
+    return mesh;
+}
+
+par_shapes_mesh* par_shapes_create_octahedron()
+{
+    static float verts[6 * 3] = {
+        0.000, 0.000, 1.000,
+        1.000, 0.000, 0.000,
+        0.000, 1.000, 0.000,
+        -1.000, 0.000, 0.000,
+        0.000, -1.000, 0.000,
+        0.000, 0.000, -1.000
+    };
+    static PAR_SHAPES_T triangles[8 * 3] = {
+        0,1,2,
+        0,2,3,
+        0,3,4,
+        0,4,1,
+        2,1,5,
+        3,2,5,
+        4,3,5,
+        1,4,5,
+    };
+    int ntris = sizeof(triangles) / sizeof(triangles[0]) / 3;
+    par_shapes_mesh* mesh = PAR_CALLOC(par_shapes_mesh, 1);
+    int ncorners = sizeof(verts) / sizeof(verts[0]) / 3;
+    mesh->npoints = ncorners;
+    mesh->points = PAR_MALLOC(float, mesh->npoints * 3);
+    memcpy(mesh->points, verts, sizeof(verts));
+    PAR_SHAPES_T const* triangle = triangles;
+    mesh->ntriangles = ntris;
+    mesh->triangles = PAR_MALLOC(PAR_SHAPES_T, mesh->ntriangles * 3);
+    PAR_SHAPES_T* tris = mesh->triangles;
+    for (int p = 0; p < ntris; p++) {
+        *tris++ = *triangle++;
+        *tris++ = *triangle++;
+        *tris++ = *triangle++;
+    }
+    return mesh;
+}
+
+par_shapes_mesh* par_shapes_create_tetrahedron()
+{
+    static float verts[4 * 3] = {
+        0.000, 1.333, 0,
+        0.943, 0, 0,
+        -0.471, 0, 0.816,
+        -0.471, 0, -0.816,
+    };
+    static PAR_SHAPES_T triangles[4 * 3] = {
+        2,1,0,
+        3,2,0,
+        1,3,0,
+        1,2,3,
+    };
+    int ntris = sizeof(triangles) / sizeof(triangles[0]) / 3;
+    par_shapes_mesh* mesh = PAR_CALLOC(par_shapes_mesh, 1);
+    int ncorners = sizeof(verts) / sizeof(verts[0]) / 3;
+    mesh->npoints = ncorners;
+    mesh->points = PAR_MALLOC(float, mesh->npoints * 3);
+    memcpy(mesh->points, verts, sizeof(verts));
+    PAR_SHAPES_T const* triangle = triangles;
+    mesh->ntriangles = ntris;
+    mesh->triangles = PAR_MALLOC(PAR_SHAPES_T, mesh->ntriangles * 3);
+    PAR_SHAPES_T* tris = mesh->triangles;
+    for (int p = 0; p < ntris; p++) {
+        *tris++ = *triangle++;
+        *tris++ = *triangle++;
+        *tris++ = *triangle++;
+    }
+    return mesh;
+}
+
+par_shapes_mesh* par_shapes_create_cube()
+{
+    static float verts[8 * 3] = {
+        0, 0, 0, // 0
+        0, 1, 0, // 1
+        1, 1, 0, // 2
+        1, 0, 0, // 3
+        0, 0, 1, // 4
+        0, 1, 1, // 5
+        1, 1, 1, // 6
+        1, 0, 1, // 7
+    };
+    static PAR_SHAPES_T quads[6 * 4] = {
+        7,6,5,4, // front
+        0,1,2,3, // back
+        6,7,3,2, // right
+        5,6,2,1, // top
+        4,5,1,0, // left
+        7,4,0,3, // bottom
+    };
+    int nquads = sizeof(quads) / sizeof(quads[0]) / 4;
+    par_shapes_mesh* mesh = PAR_CALLOC(par_shapes_mesh, 1);
+    int ncorners = sizeof(verts) / sizeof(verts[0]) / 3;
+    mesh->npoints = ncorners;
+    mesh->points = PAR_MALLOC(float, mesh->npoints * 3);
+    memcpy(mesh->points, verts, sizeof(verts));
+    PAR_SHAPES_T const* quad = quads;
+    mesh->ntriangles = nquads * 2;
+    mesh->triangles = PAR_MALLOC(PAR_SHAPES_T, mesh->ntriangles * 3);
+    PAR_SHAPES_T* tris = mesh->triangles;
+    for (int p = 0; p < nquads; p++, quad += 4) {
+        *tris++ = quad[0];
+        *tris++ = quad[1];
+        *tris++ = quad[2];
+        *tris++ = quad[2];
+        *tris++ = quad[3];
+        *tris++ = quad[0];
+    }
+    return mesh;
+}
+
+typedef struct {
+    char* cmd;
+    char* arg;
+} par_shapes__command;
+
+typedef struct {
+    char const* name;
+    int weight;
+    int ncommands;
+    par_shapes__command* commands;
+} par_shapes__rule;
+
+typedef struct {
+    int pc;
+    float position[3];
+    float scale[3];
+    par_shapes_mesh* orientation;
+    par_shapes__rule* rule;
+} par_shapes__stackframe;
+
+static par_shapes__rule* par_shapes__pick_rule(const char* name,
+    par_shapes__rule* rules, int nrules)
+{
+    par_shapes__rule* rule = 0;
+    int total = 0;
+    for (int i = 0; i < nrules; i++) {
+        rule = rules + i;
+        if (!strcmp(rule->name, name)) {
+            total += rule->weight;
+        }
+    }
+    float r = (float) rand() / RAND_MAX;
+    float t = 0;
+    for (int i = 0; i < nrules; i++) {
+        rule = rules + i;
+        if (!strcmp(rule->name, name)) {
+            t += (float) rule->weight / total;
+            if (t >= r) {
+                return rule;
+            }
+        }
+    }
+    return rule;
+}
+
+static par_shapes_mesh* par_shapes__create_turtle()
+{
+    const float xaxis[] = {1, 0, 0};
+    const float yaxis[] = {0, 1, 0};
+    const float zaxis[] = {0, 0, 1};
+    par_shapes_mesh* turtle = PAR_CALLOC(par_shapes_mesh, 1);
+    turtle->npoints = 3;
+    turtle->points = PAR_CALLOC(float, turtle->npoints * 3);
+    par_shapes__copy3(turtle->points + 0, xaxis);
+    par_shapes__copy3(turtle->points + 3, yaxis);
+    par_shapes__copy3(turtle->points + 6, zaxis);
+    return turtle;
+}
+
+static par_shapes_mesh* par_shapes__apply_turtle(par_shapes_mesh* mesh,
+    par_shapes_mesh* turtle, float const* pos, float const* scale)
+{
+    par_shapes_mesh* m = par_shapes_clone(mesh, 0);
+    for (int p = 0; p < m->npoints; p++) {
+        float* pt = m->points + p * 3;
+        pt[0] *= scale[0];
+        pt[1] *= scale[1];
+        pt[2] *= scale[2];
+        par_shapes__transform3(pt,
+            turtle->points + 0, turtle->points + 3, turtle->points + 6);
+        pt[0] += pos[0];
+        pt[1] += pos[1];
+        pt[2] += pos[2];
+    }
+    return m;
+}
+
+static void par_shapes__connect(par_shapes_mesh* scene,
+    par_shapes_mesh* cylinder, int slices)
+{
+    int stacks = 1;
+    int npoints = (slices + 1) * (stacks + 1);
+    assert(scene->npoints >= npoints && "Cannot connect to empty scene.");
+
+    // Create the new point list.
+    npoints = scene->npoints + (slices + 1);
+    float* points = PAR_MALLOC(float, npoints * 3);
+    memcpy(points, scene->points, sizeof(float) * scene->npoints * 3);
+    float* newpts = points + scene->npoints * 3;
+    memcpy(newpts, cylinder->points + (slices + 1) * 3,
+        sizeof(float) * (slices + 1) * 3);
+    PAR_FREE(scene->points);
+    scene->points = points;
+
+    // Create the new triangle list.
+    int ntriangles = scene->ntriangles + 2 * slices * stacks;
+    PAR_SHAPES_T* triangles = PAR_MALLOC(PAR_SHAPES_T, ntriangles * 3);
+    memcpy(triangles, scene->triangles, 2 * scene->ntriangles * 3);
+    int v = scene->npoints - (slices + 1);
+    PAR_SHAPES_T* face = triangles + scene->ntriangles * 3;
+    for (int stack = 0; stack < stacks; stack++) {
+        for (int slice = 0; slice < slices; slice++) {
+            int next = slice + 1;
+            *face++ = v + slice + slices + 1;
+            *face++ = v + next;
+            *face++ = v + slice;
+            *face++ = v + slice + slices + 1;
+            *face++ = v + next + slices + 1;
+            *face++ = v + next;
+        }
+        v += slices + 1;
+    }
+    PAR_FREE(scene->triangles);
+    scene->triangles = triangles;
+
+    scene->npoints = npoints;
+    scene->ntriangles = ntriangles;
+}
+
+par_shapes_mesh* par_shapes_create_lsystem(char const* text, int slices,
+    int maxdepth)
+{
+    char* program;
+    program = PAR_MALLOC(char, strlen(text) + 1);
+
+    // The first pass counts the number of rules and commands.
+    strcpy(program, text);
+    char *cmd = strtok(program, " ");
+    int nrules = 1;
+    int ncommands = 0;
+    while (cmd) {
+        char *arg = strtok(0, " ");
+        if (!arg) {
+            puts("lsystem error: unexpected end of program.");
+            break;
+        }
+        if (!strcmp(cmd, "rule")) {
+            nrules++;
+        } else {
+            ncommands++;
+        }
+        cmd = strtok(0, " ");
+    }
+
+    // Allocate space.
+    par_shapes__rule* rules = PAR_MALLOC(par_shapes__rule, nrules);
+    par_shapes__command* commands = PAR_MALLOC(par_shapes__command, ncommands);
+
+    // Initialize the entry rule.
+    par_shapes__rule* current_rule = &rules[0];
+    par_shapes__command* current_command = &commands[0];
+    current_rule->name = "entry";
+    current_rule->weight = 1;
+    current_rule->ncommands = 0;
+    current_rule->commands = current_command;
+
+    // The second pass fills in the structures.
+    strcpy(program, text);
+    cmd = strtok(program, " ");
+    while (cmd) {
+        char *arg = strtok(0, " ");
+        if (!strcmp(cmd, "rule")) {
+            current_rule++;
+
+            // Split the argument into a rule name and weight.
+            char* dot = strchr(arg, '.');
+            if (dot) {
+                current_rule->weight = atoi(dot + 1);
+                *dot = 0;
+            } else {
+                current_rule->weight = 1;
+            }
+
+            current_rule->name = arg;
+            current_rule->ncommands = 0;
+            current_rule->commands = current_command;
+        } else {
+            current_rule->ncommands++;
+            current_command->cmd = cmd;
+            current_command->arg = arg;
+            current_command++;
+        }
+        cmd = strtok(0, " ");
+    }
+
+    // For testing purposes, dump out the parsed program.
+    #ifdef TEST_PARSE
+    for (int i = 0; i < nrules; i++) {
+        par_shapes__rule rule = rules[i];
+        printf("rule %s.%d\n", rule.name, rule.weight);
+        for (int c = 0; c < rule.ncommands; c++) {
+            par_shapes__command cmd = rule.commands[c];
+            printf("\t%s %s\n", cmd.cmd, cmd.arg);
+        }
+    }
+    #endif
+
+    // Instantiate the aggregated shape and the template shapes.
+    par_shapes_mesh* scene = PAR_CALLOC(par_shapes_mesh, 1);
+    par_shapes_mesh* tube = par_shapes_create_cylinder(slices, 1);
+    par_shapes_mesh* turtle = par_shapes__create_turtle();
+
+    // We're not attempting to support texture coordinates and normals
+    // with L-systems, so remove them from the template shape.
+    PAR_FREE(tube->normals);
+    PAR_FREE(tube->tcoords);
+    tube->normals = 0;
+    tube->tcoords = 0;
+
+    const float xaxis[] = {1, 0, 0};
+    const float yaxis[] = {0, 1, 0};
+    const float zaxis[] = {0, 0, 1};
+    const float units[] = {1, 1, 1};
+
+    // Execute the L-system program until the stack size is 0.
+    par_shapes__stackframe* stack =
+        PAR_CALLOC(par_shapes__stackframe, maxdepth);
+    int stackptr = 0;
+    stack[0].orientation = turtle;
+    stack[0].rule = &rules[0];
+    par_shapes__copy3(stack[0].scale, units);
+    while (stackptr >= 0) {
+        par_shapes__stackframe* frame = &stack[stackptr];
+        par_shapes__rule* rule = frame->rule;
+        par_shapes_mesh* turtle = frame->orientation;
+        float* position = frame->position;
+        float* scale = frame->scale;
+        if (frame->pc >= rule->ncommands) {
+            par_shapes_free_mesh(turtle);
+            stackptr--;
+            continue;
+        }
+
+        par_shapes__command* cmd = rule->commands + (frame->pc++);
+        #ifdef DUMP_TRACE
+        printf("%5s %5s %5s:%d  %03d\n", cmd->cmd, cmd->arg, rule->name,
+            frame->pc - 1, stackptr);
+        #endif
+
+        float value;
+        if (!strcmp(cmd->cmd, "shape")) {
+            par_shapes_mesh* m = par_shapes__apply_turtle(tube, turtle,
+                position, scale);
+            if (!strcmp(cmd->arg, "connect")) {
+                par_shapes__connect(scene, m, slices);
+            } else {
+                par_shapes_merge(scene, m);
+            }
+            par_shapes_free_mesh(m);
+        } else if (!strcmp(cmd->cmd, "call") && stackptr < maxdepth - 1) {
+            rule = par_shapes__pick_rule(cmd->arg, rules, nrules);
+            frame = &stack[++stackptr];
+            frame->rule = rule;
+            frame->orientation = par_shapes_clone(turtle, 0);
+            frame->pc = 0;
+            par_shapes__copy3(frame->scale, scale);
+            par_shapes__copy3(frame->position, position);
+            continue;
+        } else {
+            value = atof(cmd->arg);
+            if (!strcmp(cmd->cmd, "rx")) {
+                par_shapes_rotate(turtle, value * PAR_PI / 180.0, xaxis);
+            } else if (!strcmp(cmd->cmd, "ry")) {
+                par_shapes_rotate(turtle, value * PAR_PI / 180.0, yaxis);
+            } else if (!strcmp(cmd->cmd, "rz")) {
+                par_shapes_rotate(turtle, value * PAR_PI / 180.0, zaxis);
+            } else if (!strcmp(cmd->cmd, "tx")) {
+                float vec[3] = {value, 0, 0};
+                float t[3] = {
+                    par_shapes__dot3(turtle->points + 0, vec),
+                    par_shapes__dot3(turtle->points + 3, vec),
+                    par_shapes__dot3(turtle->points + 6, vec)
+                };
+                par_shapes__add3(position, t);
+            } else if (!strcmp(cmd->cmd, "ty")) {
+                float vec[3] = {0, value, 0};
+                float t[3] = {
+                    par_shapes__dot3(turtle->points + 0, vec),
+                    par_shapes__dot3(turtle->points + 3, vec),
+                    par_shapes__dot3(turtle->points + 6, vec)
+                };
+                par_shapes__add3(position, t);
+            } else if (!strcmp(cmd->cmd, "tz")) {
+                float vec[3] = {0, 0, value};
+                float t[3] = {
+                    par_shapes__dot3(turtle->points + 0, vec),
+                    par_shapes__dot3(turtle->points + 3, vec),
+                    par_shapes__dot3(turtle->points + 6, vec)
+                };
+                par_shapes__add3(position, t);
+            } else if (!strcmp(cmd->cmd, "sx")) {
+                scale[0] *= value;
+            } else if (!strcmp(cmd->cmd, "sy")) {
+                scale[1] *= value;
+            } else if (!strcmp(cmd->cmd, "sz")) {
+                scale[2] *= value;
+            } else if (!strcmp(cmd->cmd, "sa")) {
+                scale[0] *= value;
+                scale[1] *= value;
+                scale[2] *= value;
+            }
+        }
+    }
+    PAR_FREE(stack);
+    PAR_FREE(program);
+    PAR_FREE(rules);
+    PAR_FREE(commands);
+    return scene;
+}
+
+void par_shapes_unweld(par_shapes_mesh* mesh, bool create_indices)
+{
+    int npoints = mesh->ntriangles * 3;
+    float* points = PAR_MALLOC(float, 3 * npoints);
+    float* dst = points;
+    PAR_SHAPES_T const* index = mesh->triangles;
+    for (int i = 0; i < npoints; i++) {
+        float const* src = mesh->points + 3 * (*index++);
+        *dst++ = src[0];
+        *dst++ = src[1];
+        *dst++ = src[2];
+    }
+    PAR_FREE(mesh->points);
+    mesh->points = points;
+    mesh->npoints = npoints;
+    if (create_indices) {
+        PAR_SHAPES_T* tris = PAR_MALLOC(PAR_SHAPES_T, 3 * mesh->ntriangles);
+        PAR_SHAPES_T* index = tris;
+        for (int i = 0; i < mesh->ntriangles * 3; i++) {
+            *index++ = i;
+        }
+        PAR_FREE(mesh->triangles);
+        mesh->triangles = tris;
+    }
+}
+
+void par_shapes_compute_normals(par_shapes_mesh* m)
+{
+    PAR_FREE(m->normals);
+    m->normals = PAR_CALLOC(float, m->npoints * 3);
+    PAR_SHAPES_T const* triangle = m->triangles;
+    float next[3], prev[3], cp[3];
+    for (int f = 0; f < m->ntriangles; f++, triangle += 3) {
+        float const* pa = m->points + 3 * triangle[0];
+        float const* pb = m->points + 3 * triangle[1];
+        float const* pc = m->points + 3 * triangle[2];
+        par_shapes__copy3(next, pb);
+        par_shapes__subtract3(next, pa);
+        par_shapes__copy3(prev, pc);
+        par_shapes__subtract3(prev, pa);
+        par_shapes__cross3(cp, next, prev);
+        par_shapes__add3(m->normals + 3 * triangle[0], cp);
+        par_shapes__copy3(next, pc);
+        par_shapes__subtract3(next, pb);
+        par_shapes__copy3(prev, pa);
+        par_shapes__subtract3(prev, pb);
+        par_shapes__cross3(cp, next, prev);
+        par_shapes__add3(m->normals + 3 * triangle[1], cp);
+        par_shapes__copy3(next, pa);
+        par_shapes__subtract3(next, pc);
+        par_shapes__copy3(prev, pb);
+        par_shapes__subtract3(prev, pc);
+        par_shapes__cross3(cp, next, prev);
+        par_shapes__add3(m->normals + 3 * triangle[2], cp);
+    }
+    float* normal = m->normals;
+    for (int p = 0; p < m->npoints; p++, normal += 3) {
+        par_shapes__normalize3(normal);
+    }
+}
+
+static void par_shapes__subdivide(par_shapes_mesh* mesh)
+{
+    assert(mesh->npoints == mesh->ntriangles * 3 && "Must be unwelded.");
+    int ntriangles = mesh->ntriangles * 4;
+    int npoints = ntriangles * 3;
+    float* points = PAR_CALLOC(float, npoints * 3);
+    float* dpoint = points;
+    float const* spoint = mesh->points;
+    for (int t = 0; t < mesh->ntriangles; t++, spoint += 9, dpoint += 3) {
+        float const* a = spoint;
+        float const* b = spoint + 3;
+        float const* c = spoint + 6;
+        float const* p0 = dpoint;
+        float const* p1 = dpoint + 3;
+        float const* p2 = dpoint + 6;
+        par_shapes__mix3(dpoint, a, b, 0.5);
+        par_shapes__mix3(dpoint += 3, b, c, 0.5);
+        par_shapes__mix3(dpoint += 3, a, c, 0.5);
+        par_shapes__add3(dpoint += 3, a);
+        par_shapes__add3(dpoint += 3, p0);
+        par_shapes__add3(dpoint += 3, p2);
+        par_shapes__add3(dpoint += 3, p0);
+        par_shapes__add3(dpoint += 3, b);
+        par_shapes__add3(dpoint += 3, p1);
+        par_shapes__add3(dpoint += 3, p2);
+        par_shapes__add3(dpoint += 3, p1);
+        par_shapes__add3(dpoint += 3, c);
+    }
+    PAR_FREE(mesh->points);
+    mesh->points = points;
+    mesh->npoints = npoints;
+    mesh->ntriangles = ntriangles;
+}
+
+par_shapes_mesh* par_shapes_create_subdivided_sphere(int nsubd)
+{
+    par_shapes_mesh* mesh = par_shapes_create_icosahedron();
+    par_shapes_unweld(mesh, false);
+    PAR_FREE(mesh->triangles);
+    mesh->triangles = 0;
+    while (nsubd--) {
+        par_shapes__subdivide(mesh);
+    }
+    for (int i = 0; i < mesh->npoints; i++) {
+        par_shapes__normalize3(mesh->points + i * 3);
+    }
+    mesh->triangles = PAR_MALLOC(PAR_SHAPES_T, 3 * mesh->ntriangles);
+    for (int i = 0; i < mesh->ntriangles * 3; i++) {
+        mesh->triangles[i] = i;
+    }
+    par_shapes_mesh* tmp = mesh;
+    mesh = par_shapes_weld(mesh, 0.01, 0);
+    par_shapes_free_mesh(tmp);
+    par_shapes_compute_normals(mesh);
+    return mesh;
+}
+
+par_shapes_mesh* par_shapes_create_rock(int seed, int subd)
+{
+    par_shapes_mesh* mesh = par_shapes_create_subdivided_sphere(subd);
+    struct osn_context* ctx;
+    par__simplex_noise(seed, &ctx);
+    for (int p = 0; p < mesh->npoints; p++) {
+        float* pt = mesh->points + p * 3;
+        float a = 0.25, f = 1.0;
+        double n = a * par__simplex_noise2(ctx, f * pt[0], f * pt[2]);
+        a *= 0.5; f *= 2;
+        n += a * par__simplex_noise2(ctx, f * pt[0], f * pt[2]);
+        pt[0] *= 1 + 2 * n;
+        pt[1] *= 1 + n;
+        pt[2] *= 1 + 2 * n;
+        if (pt[1] < 0) {
+            pt[1] = -pow(-pt[1], 0.5) / 2;
+        }
+    }
+    par__simplex_noise_free(ctx);
+    par_shapes_compute_normals(mesh);
+    return mesh;
+}
+
+par_shapes_mesh* par_shapes_clone(par_shapes_mesh const* mesh,
+    par_shapes_mesh* clone)
+{
+    if (!clone) {
+        clone = PAR_CALLOC(par_shapes_mesh, 1);
+    }
+    clone->npoints = mesh->npoints;
+    clone->points = PAR_REALLOC(float, clone->points, 3 * clone->npoints);
+    memcpy(clone->points, mesh->points, sizeof(float) * 3 * clone->npoints);
+    clone->ntriangles = mesh->ntriangles;
+    clone->triangles = PAR_REALLOC(PAR_SHAPES_T, clone->triangles, 3 *
+        clone->ntriangles);
+    memcpy(clone->triangles, mesh->triangles,
+        sizeof(PAR_SHAPES_T) * 3 * clone->ntriangles);
+    if (mesh->normals) {
+        clone->normals = PAR_REALLOC(float, clone->normals, 3 * clone->npoints);
+        memcpy(clone->normals, mesh->normals,
+            sizeof(float) * 3 * clone->npoints);
+    }
+    if (mesh->tcoords) {
+        clone->tcoords = PAR_REALLOC(float, clone->tcoords, 2 * clone->npoints);
+        memcpy(clone->tcoords, mesh->tcoords,
+            sizeof(float) * 2 * clone->npoints);
+    }
+    return clone;
+}
+
+static struct {
+    float const* points;
+    int gridsize;
+} par_shapes__sort_context;
+
+static int par_shapes__cmp1(const void *arg0, const void *arg1)
+{
+    const int g = par_shapes__sort_context.gridsize;
+
+    // Convert arg0 into a flattened grid index.
+    PAR_SHAPES_T d0 = *(const PAR_SHAPES_T*) arg0;
+    float const* p0 = par_shapes__sort_context.points + d0 * 3;
+    int i0 = (int) p0[0];
+    int j0 = (int) p0[1];
+    int k0 = (int) p0[2];
+    int index0 = i0 + g * j0 + g * g * k0;
+
+    // Convert arg1 into a flattened grid index.
+    PAR_SHAPES_T d1 = *(const PAR_SHAPES_T*) arg1;
+    float const* p1 = par_shapes__sort_context.points + d1 * 3;
+    int i1 = (int) p1[0];
+    int j1 = (int) p1[1];
+    int k1 = (int) p1[2];
+    int index1 = i1 + g * j1 + g * g * k1;
+
+    // Return the ordering.
+    if (index0 < index1) return -1;
+    if (index0 > index1) return 1;
+    return 0;
+}
+
+static void par_shapes__sort_points(par_shapes_mesh* mesh, int gridsize,
+    PAR_SHAPES_T* sortmap)
+{
+    // Run qsort over a list of consecutive integers that get deferenced
+    // within the comparator function; this creates a reorder mapping.
+    for (int i = 0; i < mesh->npoints; i++) {
+        sortmap[i] = i;
+    }
+    par_shapes__sort_context.gridsize = gridsize;
+    par_shapes__sort_context.points = mesh->points;
+    qsort(sortmap, mesh->npoints, sizeof(PAR_SHAPES_T), par_shapes__cmp1);
+
+    // Apply the reorder mapping to the XYZ coordinate data.
+    float* newpts = PAR_MALLOC(float, mesh->npoints * 3);
+    PAR_SHAPES_T* invmap = PAR_MALLOC(PAR_SHAPES_T, mesh->npoints);
+    float* dstpt = newpts;
+    for (int i = 0; i < mesh->npoints; i++) {
+        invmap[sortmap[i]] = i;
+        float const* srcpt = mesh->points + 3 * sortmap[i];
+        *dstpt++ = *srcpt++;
+        *dstpt++ = *srcpt++;
+        *dstpt++ = *srcpt++;
+    }
+    PAR_FREE(mesh->points);
+    mesh->points = newpts;
+
+    // Apply the inverse reorder mapping to the triangle indices.
+    PAR_SHAPES_T* newinds = PAR_MALLOC(PAR_SHAPES_T, mesh->ntriangles * 3);
+    PAR_SHAPES_T* dstind = newinds;
+    PAR_SHAPES_T const* srcind = mesh->triangles;
+    for (int i = 0; i < mesh->ntriangles * 3; i++) {
+        *dstind++ = invmap[*srcind++];
+    }
+    PAR_FREE(mesh->triangles);
+    mesh->triangles = newinds;
+
+    // Cleanup.
+    memcpy(sortmap, invmap, sizeof(PAR_SHAPES_T) * mesh->npoints);
+    PAR_FREE(invmap);
+}
+
+static void par_shapes__weld_points(par_shapes_mesh* mesh, int gridsize,
+    float epsilon, PAR_SHAPES_T* weldmap)
+{
+    // Each bin contains a "pointer" (really an index) to its first point.
+    // We add 1 because 0 is reserved to mean that the bin is empty.
+    // Since the points are spatially sorted, there's no need to store
+    // a point count in each bin.
+    PAR_SHAPES_T* bins = PAR_CALLOC(PAR_SHAPES_T,
+        gridsize * gridsize * gridsize);
+    int prev_binindex = -1;
+    for (int p = 0; p < mesh->npoints; p++) {
+        float const* pt = mesh->points + p * 3;
+        int i = (int) pt[0];
+        int j = (int) pt[1];
+        int k = (int) pt[2];
+        int this_binindex = i + gridsize * j + gridsize * gridsize * k;
+        if (this_binindex != prev_binindex) {
+            bins[this_binindex] = 1 + p;
+        }
+        prev_binindex = this_binindex;
+    }
+
+    // Examine all bins that intersect the epsilon-sized cube centered at each
+    // point, and check for colocated points within those bins.
+    float const* pt = mesh->points;
+    int nremoved = 0;
+    for (int p = 0; p < mesh->npoints; p++, pt += 3) {
+
+        // Skip if this point has already been welded.
+        if (weldmap[p] != p) {
+            continue;
+        }
+
+        // Build a list of bins that intersect the epsilon-sized cube.
+        int nearby[8];
+        int nbins = 0;
+        int minp[3], maxp[3];
+        for (int c = 0; c < 3; c++) {
+            minp[c] = (int) (pt[c] - epsilon);
+            maxp[c] = (int) (pt[c] + epsilon);
+        }
+        for (int i = minp[0]; i <= maxp[0]; i++) {
+            for (int j = minp[1]; j <= maxp[1]; j++) {
+                for (int k = minp[2]; k <= maxp[2]; k++) {
+                    int binindex = i + gridsize * j + gridsize * gridsize * k;
+                    PAR_SHAPES_T binvalue = *(bins + binindex);
+                    if (binvalue > 0) {
+                        if (nbins == 8) {
+                            printf("Epsilon value is too large.\n");
+                            break;
+                        }
+                        nearby[nbins++] = binindex;
+                    }
+                }
+            }
+        }
+
+        // Check for colocated points in each nearby bin.
+        for (int b = 0; b < nbins; b++) {
+            int binindex = nearby[b];
+            PAR_SHAPES_T binvalue = *(bins + binindex);
+            PAR_SHAPES_T nindex = binvalue - 1;
+            while (true) {
+
+                // If this isn't "self" and it's colocated, then weld it!
+                if (nindex != p && weldmap[nindex] == nindex) {
+                    float const* thatpt = mesh->points + nindex * 3;
+                    float dist2 = par_shapes__sqrdist3(thatpt, pt);
+                    if (dist2 < epsilon) {
+                        weldmap[nindex] = p;
+                        nremoved++;
+                    }
+                }
+
+                // Advance to the next point if possible.
+                if (++nindex >= mesh->npoints) {
+                    break;
+                }
+
+                // If the next point is outside the bin, then we're done.
+                float const* nextpt = mesh->points + nindex * 3;
+                int i = (int) nextpt[0];
+                int j = (int) nextpt[1];
+                int k = (int) nextpt[2];
+                int nextbinindex = i + gridsize * j + gridsize * gridsize * k;
+                if (nextbinindex != binindex) {
+                    break;
+                }
+            }
+        }
+    }
+    PAR_FREE(bins);
+
+    // Apply the weldmap to the vertices.
+    int npoints = mesh->npoints - nremoved;
+    float* newpts = PAR_MALLOC(float, 3 * npoints);
+    float* dst = newpts;
+    PAR_SHAPES_T* condensed_map = PAR_MALLOC(PAR_SHAPES_T, mesh->npoints);
+    PAR_SHAPES_T* cmap = condensed_map;
+    float const* src = mesh->points;
+    int ci = 0;
+    for (int p = 0; p < mesh->npoints; p++, src += 3) {
+        if (weldmap[p] == p) {
+            *dst++ = src[0];
+            *dst++ = src[1];
+            *dst++ = src[2];
+            *cmap++ = ci++;
+        } else {
+            *cmap++ = condensed_map[weldmap[p]];
+        }
+    }
+    assert(ci == npoints);
+    PAR_FREE(mesh->points);
+    memcpy(weldmap, condensed_map, mesh->npoints * sizeof(PAR_SHAPES_T));
+    PAR_FREE(condensed_map);
+    mesh->points = newpts;
+    mesh->npoints = npoints;
+
+    // Apply the weldmap to the triangle indices and skip the degenerates.
+    PAR_SHAPES_T const* tsrc = mesh->triangles;
+    PAR_SHAPES_T* tdst = mesh->triangles;
+    int ntriangles = 0;
+    for (int i = 0; i < mesh->ntriangles; i++, tsrc += 3) {
+        PAR_SHAPES_T a = weldmap[tsrc[0]];
+        PAR_SHAPES_T b = weldmap[tsrc[1]];
+        PAR_SHAPES_T c = weldmap[tsrc[2]];
+        if (a != b && a != c && b != c) {
+            *tdst++ = a;
+            *tdst++ = b;
+            *tdst++ = c;
+            ntriangles++;
+        }
+    }
+    mesh->ntriangles = ntriangles;
+}
+
+par_shapes_mesh* par_shapes_weld(par_shapes_mesh const* mesh, float epsilon,
+    PAR_SHAPES_T* weldmap)
+{
+    par_shapes_mesh* clone = par_shapes_clone(mesh, 0);
+    float aabb[6];
+    int gridsize = 20;
+    float maxcell = gridsize - 1;
+    par_shapes_compute_aabb(clone, aabb);
+    float scale[3] = {
+        aabb[3] == aabb[0] ? 1.0f : maxcell / (aabb[3] - aabb[0]),
+        aabb[4] == aabb[1] ? 1.0f : maxcell / (aabb[4] - aabb[1]),
+        aabb[5] == aabb[2] ? 1.0f : maxcell / (aabb[5] - aabb[2]),
+    };
+    par_shapes_translate(clone, -aabb[0], -aabb[1], -aabb[2]);
+    par_shapes_scale(clone, scale[0], scale[1], scale[2]);
+    PAR_SHAPES_T* sortmap = PAR_MALLOC(PAR_SHAPES_T, mesh->npoints);
+    par_shapes__sort_points(clone, gridsize, sortmap);
+    bool owner = false;
+    if (!weldmap) {
+        owner = true;
+        weldmap = PAR_MALLOC(PAR_SHAPES_T, mesh->npoints);
+    }
+    for (int i = 0; i < mesh->npoints; i++) {
+        weldmap[i] = i;
+    }
+    par_shapes__weld_points(clone, gridsize, epsilon, weldmap);
+    if (owner) {
+        PAR_FREE(weldmap);
+    } else {
+        PAR_SHAPES_T* newmap = PAR_MALLOC(PAR_SHAPES_T, mesh->npoints);
+        for (int i = 0; i < mesh->npoints; i++) {
+            newmap[i] = weldmap[sortmap[i]];
+        }
+        memcpy(weldmap, newmap, sizeof(PAR_SHAPES_T) * mesh->npoints);
+        PAR_FREE(newmap);
+    }
+    PAR_FREE(sortmap);
+    par_shapes_scale(clone, 1.0 / scale[0], 1.0 / scale[1], 1.0 / scale[2]);
+    par_shapes_translate(clone, aabb[0], aabb[1], aabb[2]);
+    return clone;
+}
+
+// -----------------------------------------------------------------------------
+// BEGIN OPEN SIMPLEX NOISE
+// -----------------------------------------------------------------------------
+
+#define STRETCH_CONSTANT_2D (-0.211324865405187)  // (1 / sqrt(2 + 1) - 1 ) / 2;
+#define SQUISH_CONSTANT_2D (0.366025403784439)  // (sqrt(2 + 1) -1) / 2;
+#define STRETCH_CONSTANT_3D (-1.0 / 6.0)  // (1 / sqrt(3 + 1) - 1) / 3;
+#define SQUISH_CONSTANT_3D (1.0 / 3.0)  // (sqrt(3+1)-1)/3;
+#define STRETCH_CONSTANT_4D (-0.138196601125011)  // (1 / sqrt(4 + 1) - 1) / 4;
+#define SQUISH_CONSTANT_4D (0.309016994374947)  // (sqrt(4 + 1) - 1) / 4;
+
+#define NORM_CONSTANT_2D (47.0)
+#define NORM_CONSTANT_3D (103.0)
+#define NORM_CONSTANT_4D (30.0)
+
+#define DEFAULT_SEED (0LL)
+
+struct osn_context {
+    int16_t* perm;
+    int16_t* permGradIndex3D;
+};
+
+#define ARRAYSIZE(x) (sizeof((x)) / sizeof((x)[0]))
+
+/*
+ * Gradients for 2D. They approximate the directions to the
+ * vertices of an octagon from the center.
+ */
+static const int8_t gradients2D[] = {
+    5, 2, 2, 5, -5, 2, -2, 5, 5, -2, 2, -5, -5, -2, -2, -5,
+};
+
+/*
+ * Gradients for 3D. They approximate the directions to the
+ * vertices of a rhombicuboctahedron from the center, skewed so
+ * that the triangular and square facets can be inscribed inside
+ * circles of the same radius.
+ */
+static const signed char gradients3D[] = {
+    -11, 4, 4, -4, 11, 4, -4, 4, 11, 11, 4, 4, 4, 11, 4, 4, 4, 11, -11, -4, 4,
+    -4, -11, 4, -4, -4, 11, 11, -4, 4, 4, -11, 4, 4, -4, 11, -11, 4, -4, -4, 11,
+    -4, -4, 4, -11, 11, 4, -4, 4, 11, -4, 4, 4, -11, -11, -4, -4, -4, -11, -4,
+    -4, -4, -11, 11, -4, -4, 4, -11, -4, 4, -4, -11,
+};
+
+/*
+ * Gradients for 4D. They approximate the directions to the
+ * vertices of a disprismatotesseractihexadecachoron from the center,
+ * skewed so that the tetrahedral and cubic facets can be inscribed inside
+ * spheres of the same radius.
+ */
+static const signed char gradients4D[] = {
+    3, 1, 1, 1, 1, 3, 1, 1, 1, 1, 3, 1, 1, 1, 1, 3, -3, 1, 1, 1, -1, 3, 1, 1,
+    -1, 1, 3, 1, -1, 1, 1, 3, 3, -1, 1, 1, 1, -3, 1, 1, 1, -1, 3, 1, 1, -1, 1,
+    3, -3, -1, 1, 1, -1, -3, 1, 1, -1, -1, 3, 1, -1, -1, 1, 3, 3, 1, -1, 1, 1,
+    3, -1, 1, 1, 1, -3, 1, 1, 1, -1, 3, -3, 1, -1, 1, -1, 3, -1, 1, -1, 1, -3,
+    1, -1, 1, -1, 3, 3, -1, -1, 1, 1, -3, -1, 1, 1, -1, -3, 1, 1, -1, -1, 3, -3,
+    -1, -1, 1, -1, -3, -1, 1, -1, -1, -3, 1, -1, -1, -1, 3, 3, 1, 1, -1, 1, 3,
+    1, -1, 1, 1, 3, -1, 1, 1, 1, -3, -3, 1, 1, -1, -1, 3, 1, -1, -1, 1, 3, -1,
+    -1, 1, 1, -3, 3, -1, 1, -1, 1, -3, 1, -1, 1, -1, 3, -1, 1, -1, 1, -3, -3,
+    -1, 1, -1, -1, -3, 1, -1, -1, -1, 3, -1, -1, -1, 1, -3, 3, 1, -1, -1, 1, 3,
+    -1, -1, 1, 1, -3, -1, 1, 1, -1, -3, -3, 1, -1, -1, -1, 3, -1, -1, -1, 1, -3,
+    -1, -1, 1, -1, -3, 3, -1, -1, -1, 1, -3, -1, -1, 1, -1, -3, -1, 1, -1, -1,
+    -3, -3, -1, -1, -1, -1, -3, -1, -1, -1, -1, -3, -1, -1, -1, -1, -3,
+};
+
+static double extrapolate2(
+    struct osn_context* ctx, int xsb, int ysb, double dx, double dy)
+{
+    int16_t* perm = ctx->perm;
+    int index = perm[(perm[xsb & 0xFF] + ysb) & 0xFF] & 0x0E;
+    return gradients2D[index] * dx + gradients2D[index + 1] * dy;
+}
+
+static inline int fastFloor(double x)
+{
+    int xi = (int) x;
+    return x < xi ? xi - 1 : xi;
+}
+
+static int allocate_perm(struct osn_context* ctx, int nperm, int ngrad)
+{
+    PAR_FREE(ctx->perm);
+    PAR_FREE(ctx->permGradIndex3D);
+    ctx->perm = PAR_MALLOC(int16_t, nperm);
+    if (!ctx->perm) {
+        return -ENOMEM;
+    }
+    ctx->permGradIndex3D = PAR_MALLOC(int16_t, ngrad);
+    if (!ctx->permGradIndex3D) {
+        PAR_FREE(ctx->perm);
+        return -ENOMEM;
+    }
+    return 0;
+}
+
+static int par__simplex_noise(int64_t seed, struct osn_context** ctx)
+{
+    int rc;
+    int16_t source[256];
+    int i;
+    int16_t* perm;
+    int16_t* permGradIndex3D;
+    *ctx = PAR_MALLOC(struct osn_context, 1);
+    if (!(*ctx)) {
+        return -ENOMEM;
+    }
+    (*ctx)->perm = NULL;
+    (*ctx)->permGradIndex3D = NULL;
+    rc = allocate_perm(*ctx, 256, 256);
+    if (rc) {
+        PAR_FREE(*ctx);
+        return rc;
+    }
+    perm = (*ctx)->perm;
+    permGradIndex3D = (*ctx)->permGradIndex3D;
+    for (i = 0; i < 256; i++) {
+        source[i] = (int16_t) i;
+    }
+    seed = seed * 6364136223846793005LL + 1442695040888963407LL;
+    seed = seed * 6364136223846793005LL + 1442695040888963407LL;
+    seed = seed * 6364136223846793005LL + 1442695040888963407LL;
+    for (i = 255; i >= 0; i--) {
+        seed = seed * 6364136223846793005LL + 1442695040888963407LL;
+        int r = (int) ((seed + 31) % (i + 1));
+        if (r < 0)
+            r += (i + 1);
+        perm[i] = source[r];
+        permGradIndex3D[i] =
+            (short) ((perm[i] % (ARRAYSIZE(gradients3D) / 3)) * 3);
+        source[r] = source[i];
+    }
+    return 0;
+}
+
+static void par__simplex_noise_free(struct osn_context* ctx)
+{
+    if (!ctx)
+        return;
+    if (ctx->perm) {
+        PAR_FREE(ctx->perm);
+        ctx->perm = NULL;
+    }
+    if (ctx->permGradIndex3D) {
+        PAR_FREE(ctx->permGradIndex3D);
+        ctx->permGradIndex3D = NULL;
+    }
+    PAR_FREE(ctx);
+}
+
+static double par__simplex_noise2(struct osn_context* ctx, double x, double y)
+{
+    // Place input coordinates onto grid.
+    double stretchOffset = (x + y) * STRETCH_CONSTANT_2D;
+    double xs = x + stretchOffset;
+    double ys = y + stretchOffset;
+
+    // Floor to get grid coordinates of rhombus (stretched square) super-cell
+    // origin.
+    int xsb = fastFloor(xs);
+    int ysb = fastFloor(ys);
+
+    // Skew out to get actual coordinates of rhombus origin. We'll need these
+    // later.
+    double squishOffset = (xsb + ysb) * SQUISH_CONSTANT_2D;
+    double xb = xsb + squishOffset;
+    double yb = ysb + squishOffset;
+
+    // Compute grid coordinates relative to rhombus origin.
+    double xins = xs - xsb;
+    double yins = ys - ysb;
+
+    // Sum those together to get a value that determines which region we're in.
+    double inSum = xins + yins;
+
+    // Positions relative to origin point.
+    double dx0 = x - xb;
+    double dy0 = y - yb;
+
+    // We'll be defining these inside the next block and using them afterwards.
+    double dx_ext, dy_ext;
+    int xsv_ext, ysv_ext;
+
+    double value = 0;
+
+    // Contribution (1,0)
+    double dx1 = dx0 - 1 - SQUISH_CONSTANT_2D;
+    double dy1 = dy0 - 0 - SQUISH_CONSTANT_2D;
+    double attn1 = 2 - dx1 * dx1 - dy1 * dy1;
+    if (attn1 > 0) {
+        attn1 *= attn1;
+        value += attn1 * attn1 * extrapolate2(ctx, xsb + 1, ysb + 0, dx1, dy1);
+    }
+
+    // Contribution (0,1)
+    double dx2 = dx0 - 0 - SQUISH_CONSTANT_2D;
+    double dy2 = dy0 - 1 - SQUISH_CONSTANT_2D;
+    double attn2 = 2 - dx2 * dx2 - dy2 * dy2;
+    if (attn2 > 0) {
+        attn2 *= attn2;
+        value += attn2 * attn2 * extrapolate2(ctx, xsb + 0, ysb + 1, dx2, dy2);
+    }
+
+    if (inSum <= 1) {  // We're inside the triangle (2-Simplex) at (0,0)
+        double zins = 1 - inSum;
+        if (zins > xins || zins > yins) {
+            if (xins > yins) {
+                xsv_ext = xsb + 1;
+                ysv_ext = ysb - 1;
+                dx_ext = dx0 - 1;
+                dy_ext = dy0 + 1;
+            } else {
+                xsv_ext = xsb - 1;
+                ysv_ext = ysb + 1;
+                dx_ext = dx0 + 1;
+                dy_ext = dy0 - 1;
+            }
+        } else {  //(1,0) and (0,1) are the closest two vertices.
+            xsv_ext = xsb + 1;
+            ysv_ext = ysb + 1;
+            dx_ext = dx0 - 1 - 2 * SQUISH_CONSTANT_2D;
+            dy_ext = dy0 - 1 - 2 * SQUISH_CONSTANT_2D;
+        }
+    } else {  // We're inside the triangle (2-Simplex) at (1,1)
+        double zins = 2 - inSum;
+        if (zins < xins || zins < yins) {
+            if (xins > yins) {
+                xsv_ext = xsb + 2;
+                ysv_ext = ysb + 0;
+                dx_ext = dx0 - 2 - 2 * SQUISH_CONSTANT_2D;
+                dy_ext = dy0 + 0 - 2 * SQUISH_CONSTANT_2D;
+            } else {
+                xsv_ext = xsb + 0;
+                ysv_ext = ysb + 2;
+                dx_ext = dx0 + 0 - 2 * SQUISH_CONSTANT_2D;
+                dy_ext = dy0 - 2 - 2 * SQUISH_CONSTANT_2D;
+            }
+        } else {  //(1,0) and (0,1) are the closest two vertices.
+            dx_ext = dx0;
+            dy_ext = dy0;
+            xsv_ext = xsb;
+            ysv_ext = ysb;
+        }
+        xsb += 1;
+        ysb += 1;
+        dx0 = dx0 - 1 - 2 * SQUISH_CONSTANT_2D;
+        dy0 = dy0 - 1 - 2 * SQUISH_CONSTANT_2D;
+    }
+
+    // Contribution (0,0) or (1,1)
+    double attn0 = 2 - dx0 * dx0 - dy0 * dy0;
+    if (attn0 > 0) {
+        attn0 *= attn0;
+        value += attn0 * attn0 * extrapolate2(ctx, xsb, ysb, dx0, dy0);
+    }
+
+    // Extra Vertex
+    double attn_ext = 2 - dx_ext * dx_ext - dy_ext * dy_ext;
+    if (attn_ext > 0) {
+        attn_ext *= attn_ext;
+        value += attn_ext * attn_ext *
+            extrapolate2(ctx, xsv_ext, ysv_ext, dx_ext, dy_ext);
+    }
+
+    return value / NORM_CONSTANT_2D;
+}
+
+void par_shapes_remove_degenerate(par_shapes_mesh* mesh, float mintriarea)
+{
+    int ntriangles = 0;
+    PAR_SHAPES_T* triangles = PAR_MALLOC(PAR_SHAPES_T, mesh->ntriangles * 3);
+    PAR_SHAPES_T* dst = triangles;
+    PAR_SHAPES_T const* src = mesh->triangles;
+    float next[3], prev[3], cp[3];
+    float mincplen2 = (mintriarea * 2) * (mintriarea * 2);
+    for (int f = 0; f < mesh->ntriangles; f++, src += 3) {
+        float const* pa = mesh->points + 3 * src[0];
+        float const* pb = mesh->points + 3 * src[1];
+        float const* pc = mesh->points + 3 * src[2];
+        par_shapes__copy3(next, pb);
+        par_shapes__subtract3(next, pa);
+        par_shapes__copy3(prev, pc);
+        par_shapes__subtract3(prev, pa);
+        par_shapes__cross3(cp, next, prev);
+        float cplen2 = par_shapes__dot3(cp, cp);
+        if (cplen2 >= mincplen2) {
+            *dst++ = src[0];
+            *dst++ = src[1];
+            *dst++ = src[2];
+            ntriangles++;
+        }
+    }
+    mesh->ntriangles = ntriangles;
+    PAR_FREE(mesh->triangles);
+    mesh->triangles = triangles;
+}
+
+#endif // PAR_SHAPES_IMPLEMENTATION
+#endif // PAR_SHAPES_H

+ 1 - 1
src/meson.build

@@ -18,5 +18,5 @@ raylib = library('raylib',
                   source_c,
                   source_c,
                   dependencies : [ glfw_dep, gl_dep, openal_dep, m_dep, x11_dep],
                   dependencies : [ glfw_dep, gl_dep, openal_dep, m_dep, x11_dep],
                   install : true,
                   install : true,
-                  version : '1.7.0')
+                  version : '1.8.0')
 
 

+ 398 - 1
src/models.c

@@ -10,6 +10,10 @@
 *   #define SUPPORT_FILEFORMAT_MTL
 *   #define SUPPORT_FILEFORMAT_MTL
 *       Selected desired fileformats to be supported for loading.
 *       Selected desired fileformats to be supported for loading.
 *
 *
+*   #define SUPPORT_MESH_GENERATION
+*       Support procedural mesh generation functions, uses external par_shapes.h library
+*       NOTE: Some generated meshes DO NOT include generated texture coordinates
+*
 *
 *
 *   LICENSE: zlib/libpng
 *   LICENSE: zlib/libpng
 *
 *
@@ -36,6 +40,7 @@
 //-------------------------------------------------
 //-------------------------------------------------
 #define SUPPORT_FILEFORMAT_OBJ
 #define SUPPORT_FILEFORMAT_OBJ
 #define SUPPORT_FILEFORMAT_MTL
 #define SUPPORT_FILEFORMAT_MTL
+#define SUPPORT_MESH_GENERATION
 //-------------------------------------------------
 //-------------------------------------------------
 
 
 #include "raylib.h"
 #include "raylib.h"
@@ -51,6 +56,9 @@
 
 
 #include "rlgl.h"           // raylib OpenGL abstraction layer to OpenGL 1.1, 2.1, 3.3+ or ES2
 #include "rlgl.h"           // raylib OpenGL abstraction layer to OpenGL 1.1, 2.1, 3.3+ or ES2
 
 
+#define PAR_SHAPES_IMPLEMENTATION
+#include "external/par_shapes.h"
+
 //----------------------------------------------------------------------------------
 //----------------------------------------------------------------------------------
 // Defines and Macros
 // Defines and Macros
 //----------------------------------------------------------------------------------
 //----------------------------------------------------------------------------------
@@ -644,12 +652,142 @@ void UnloadMesh(Mesh *mesh)
     rlUnloadMesh(mesh);
     rlUnloadMesh(mesh);
 }
 }
 
 
+#if defined(SUPPORT_MESH_GENERATION)
+// Generate plane mesh (with subdivisions)
+Mesh GenMeshPlane(float width, float length, int resX, int resZ)
+{
+    Mesh mesh = { 0 };
+
+#define CUSTOM_MESH_GEN_PLANE
+#if defined(CUSTOM_MESH_GEN_PLANE)
+    resX++;
+    resZ++;
+    
+    // Vertices definition
+    int vertexCount = resX*resZ*6;  // 6 vertex by quad
+
+    Vector3 vertices[vertexCount];
+    for (int z = 0; z < resZ; z++)
+    {
+        // [-length/2, length/2]
+        float zPos = ((float)z/(resZ - 1) - 0.5f)*length;
+        for (int x = 0; x < resX; x++)
+        {
+            // [-width/2, width/2]
+            float xPos = ((float)x/(resX - 1) - 0.5f)*width;
+            vertices[x + z*resX] = (Vector3){ xPos, 0.0f, zPos };
+        }
+    }
+
+    // Normals definition
+    Vector3 normals[vertexCount];
+    for (int n = 0; n < vertexCount; n++) normals[n] = (Vector3){ 0.0f, 1.0f, 0.0f };   // Vector3.up;
+
+    // TexCoords definition		
+    Vector2 texcoords[vertexCount];
+    for (int v = 0; v < resZ; v++)
+    {
+        for (int u = 0; u < resX; u++)
+        {
+            texcoords[u + v*resX] = (Vector2){ (float)u/(resX - 1), (float)v/(resZ - 1) };
+        }
+    }
+
+    // Triangles definition (indices)
+    int nbFaces = (resX - 1)*(resZ - 1);
+    int triangles[nbFaces*6];
+    int t = 0;
+    for (int face = 0; face < nbFaces; face++)
+    {
+        // Retrieve lower left corner from face ind
+        int i = face % (resX - 1) + (face/(resZ - 1)*resX);
+
+        triangles[t++] = i + resX;
+        triangles[t++] = i + 1;
+        triangles[t++] = i;
+
+        triangles[t++] = i + resX;	
+        triangles[t++] = i + resX + 1;
+        triangles[t++] = i + 1;
+    }
+
+    mesh.vertexCount = vertexCount;
+    mesh.triangleCount = nbFaces*2;
+    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.indices = (unsigned short *)malloc(mesh.triangleCount*3*sizeof(unsigned short));
+    
+    // Mesh vertices position array
+    for (int i = 0; i < mesh.vertexCount; i++)
+    {
+        mesh.vertices[3*i] = vertices[i].x;
+        mesh.vertices[3*i + 1] = vertices[i].y;
+        mesh.vertices[3*i + 2] = vertices[i].z;
+    }
+    
+    // Mesh texcoords array
+    for (int i = 0; i < mesh.vertexCount; i++)
+    {
+        mesh.texcoords[2*i] = texcoords[i].x;
+        mesh.texcoords[2*i + 1] = texcoords[i].y;
+    }
+    
+    // Mesh normals array
+    for (int i = 0; i < mesh.vertexCount; i++)
+    {
+        mesh.normals[3*i] = normals[i].x;
+        mesh.normals[3*i + 1] = normals[i].y;
+        mesh.normals[3*i + 2] = normals[i].z;
+    }
+    
+    // Mesh indices array initialization
+    for (int i = 0; i < mesh.triangleCount*3; i++) mesh.indices[i] = triangles[i];
+    
+#else       // Use par_shapes library to generate plane mesh
+
+    par_shapes_mesh *plane = par_shapes_create_plane(resX, resZ);   // No normals/texcoords generated!!!
+    par_shapes_scale(plane, width, length, 1.0f);
+    par_shapes_rotate(plane, -PI/2.0f, (float[]){ 1, 0, 0 });
+    par_shapes_translate(plane, -width/2, 0.0f, length/2);
+    
+    mesh.vertices = (float *)malloc(plane->ntriangles*3*3*sizeof(float));
+    mesh.texcoords = (float *)malloc(plane->ntriangles*3*2*sizeof(float));
+    mesh.normals = (float *)malloc(plane->ntriangles*3*3*sizeof(float));
+
+    mesh.vertexCount = plane->ntriangles*3;
+    mesh.triangleCount = plane->ntriangles;
+
+    for (int k = 0; k < mesh.vertexCount; k++)
+    {
+        mesh.vertices[k*3] = plane->points[plane->triangles[k]*3];
+        mesh.vertices[k*3 + 1] = plane->points[plane->triangles[k]*3 + 1];
+        mesh.vertices[k*3 + 2] = plane->points[plane->triangles[k]*3 + 2];
+        
+        mesh.normals[k*3] = plane->normals[plane->triangles[k]*3];
+        mesh.normals[k*3 + 1] = plane->normals[plane->triangles[k]*3 + 1];
+        mesh.normals[k*3 + 2] = plane->normals[plane->triangles[k]*3 + 2];
+        
+        mesh.texcoords[k*2] = plane->tcoords[plane->triangles[k]*2];
+        mesh.texcoords[k*2 + 1] = plane->tcoords[plane->triangles[k]*2 + 1];
+    }
+
+    par_shapes_free_mesh(plane);
+#endif
+
+    // Upload vertex data to GPU (static mesh)
+    rlLoadMesh(&mesh, false);  
+
+    return mesh;
+}
+
 // Generated cuboid mesh
 // Generated cuboid mesh
-// NOTE: Vertex data is uploaded to GPU
 Mesh GenMeshCube(float width, float height, float length)
 Mesh GenMeshCube(float width, float height, float length)
 {
 {
     Mesh mesh = { 0 };
     Mesh mesh = { 0 };
 
 
+#define CUSTOM_MESH_GEN_CUBE
+#if defined(CUSTOM_MESH_GEN_CUBE)
     float vertices[] = {
     float vertices[] = {
         -width/2, -height/2, length/2,
         -width/2, -height/2, length/2,
         width/2, -height/2, length/2,
         width/2, -height/2, length/2,
@@ -760,6 +898,264 @@ Mesh GenMeshCube(float width, float height, float length)
     mesh.vertexCount = 24;
     mesh.vertexCount = 24;
     mesh.triangleCount = 12;
     mesh.triangleCount = 12;
     
     
+#else               // Use par_shapes library to generate cube mesh
+/*
+// Platonic solids:
+par_shapes_mesh* par_shapes_create_tetrahedron();       // 4 sides polyhedron (pyramid)
+par_shapes_mesh* par_shapes_create_cube();              // 6 sides polyhedron (cube)
+par_shapes_mesh* par_shapes_create_octahedron();        // 8 sides polyhedron (dyamond)
+par_shapes_mesh* par_shapes_create_dodecahedron();      // 12 sides polyhedron
+par_shapes_mesh* par_shapes_create_icosahedron();       // 20 sides polyhedron
+*/
+    // Platonic solid generation: cube (6 sides)
+    // NOTE: No normals/texcoords generated by default
+    par_shapes_mesh *cube = par_shapes_create_cube();
+    cube->tcoords = PAR_MALLOC(float, 2*cube->npoints);
+    for (int i = 0; i < 2*cube->npoints; i++) cube->tcoords[i] = 0.0f;    
+    par_shapes_scale(cube, width, height, length);
+    par_shapes_translate(cube, -width/2, 0.0f, -length/2);
+    par_shapes_compute_normals(cube);
+    
+    mesh.vertices = (float *)malloc(cube->ntriangles*3*3*sizeof(float));
+    mesh.texcoords = (float *)malloc(cube->ntriangles*3*2*sizeof(float));
+    mesh.normals = (float *)malloc(cube->ntriangles*3*3*sizeof(float));
+
+    mesh.vertexCount = cube->ntriangles*3;
+    mesh.triangleCount = cube->ntriangles;
+
+    for (int k = 0; k < mesh.vertexCount; k++)
+    {
+        mesh.vertices[k*3] = cube->points[cube->triangles[k]*3];
+        mesh.vertices[k*3 + 1] = cube->points[cube->triangles[k]*3 + 1];
+        mesh.vertices[k*3 + 2] = cube->points[cube->triangles[k]*3 + 2];
+        
+        mesh.normals[k*3] = cube->normals[cube->triangles[k]*3];
+        mesh.normals[k*3 + 1] = cube->normals[cube->triangles[k]*3 + 1];
+        mesh.normals[k*3 + 2] = cube->normals[cube->triangles[k]*3 + 2];
+        
+        mesh.texcoords[k*2] = cube->tcoords[cube->triangles[k]*2];
+        mesh.texcoords[k*2 + 1] = cube->tcoords[cube->triangles[k]*2 + 1];
+    }
+
+    par_shapes_free_mesh(cube);
+#endif
+
+    // Upload vertex data to GPU (static mesh)
+    rlLoadMesh(&mesh, false);  
+
+    return mesh;
+}
+
+// Generate sphere mesh (standard sphere)
+RLAPI Mesh GenMeshSphere(float radius, int rings, int slices)
+{
+    Mesh mesh = { 0 };
+
+    par_shapes_mesh *sphere = par_shapes_create_parametric_sphere(slices, rings);
+    par_shapes_scale(sphere, radius, radius, radius);
+    // NOTE: Soft normals are computed internally 
+    
+    mesh.vertices = (float *)malloc(sphere->ntriangles*3*3*sizeof(float));
+    mesh.texcoords = (float *)malloc(sphere->ntriangles*3*2*sizeof(float));
+    mesh.normals = (float *)malloc(sphere->ntriangles*3*3*sizeof(float));
+
+    mesh.vertexCount = sphere->ntriangles*3;
+    mesh.triangleCount = sphere->ntriangles;
+
+    for (int k = 0; k < mesh.vertexCount; k++)
+    {
+        mesh.vertices[k*3] = sphere->points[sphere->triangles[k]*3];
+        mesh.vertices[k*3 + 1] = sphere->points[sphere->triangles[k]*3 + 1];
+        mesh.vertices[k*3 + 2] = sphere->points[sphere->triangles[k]*3 + 2];
+        
+        mesh.normals[k*3] = sphere->normals[sphere->triangles[k]*3];
+        mesh.normals[k*3 + 1] = sphere->normals[sphere->triangles[k]*3 + 1];
+        mesh.normals[k*3 + 2] = sphere->normals[sphere->triangles[k]*3 + 2];
+        
+        mesh.texcoords[k*2] = sphere->tcoords[sphere->triangles[k]*2];
+        mesh.texcoords[k*2 + 1] = sphere->tcoords[sphere->triangles[k]*2 + 1];
+    }
+
+    par_shapes_free_mesh(sphere);
+    
+    // Upload vertex data to GPU (static mesh)
+    rlLoadMesh(&mesh, false);  
+
+    return mesh;
+}
+
+// Generate hemi-sphere mesh (half sphere, no bottom cap)
+RLAPI Mesh GenMeshHemiSphere(float radius, int rings, int slices)
+{
+    Mesh mesh = { 0 };
+
+    par_shapes_mesh *sphere = par_shapes_create_hemisphere(slices, rings);
+    par_shapes_scale(sphere, radius, radius, radius);
+    // NOTE: Soft normals are computed internally 
+    
+    mesh.vertices = (float *)malloc(sphere->ntriangles*3*3*sizeof(float));
+    mesh.texcoords = (float *)malloc(sphere->ntriangles*3*2*sizeof(float));
+    mesh.normals = (float *)malloc(sphere->ntriangles*3*3*sizeof(float));
+
+    mesh.vertexCount = sphere->ntriangles*3;
+    mesh.triangleCount = sphere->ntriangles;
+
+    for (int k = 0; k < mesh.vertexCount; k++)
+    {
+        mesh.vertices[k*3] = sphere->points[sphere->triangles[k]*3];
+        mesh.vertices[k*3 + 1] = sphere->points[sphere->triangles[k]*3 + 1];
+        mesh.vertices[k*3 + 2] = sphere->points[sphere->triangles[k]*3 + 2];
+        
+        mesh.normals[k*3] = sphere->normals[sphere->triangles[k]*3];
+        mesh.normals[k*3 + 1] = sphere->normals[sphere->triangles[k]*3 + 1];
+        mesh.normals[k*3 + 2] = sphere->normals[sphere->triangles[k]*3 + 2];
+        
+        mesh.texcoords[k*2] = sphere->tcoords[sphere->triangles[k]*2];
+        mesh.texcoords[k*2 + 1] = sphere->tcoords[sphere->triangles[k]*2 + 1];
+    }
+
+    par_shapes_free_mesh(sphere);
+    
+    // Upload vertex data to GPU (static mesh)
+    rlLoadMesh(&mesh, false);  
+
+    return mesh;
+}
+
+// Generate cylinder mesh
+Mesh GenMeshCylinder(float radius, float height, int slices)
+{
+    Mesh mesh = { 0 };
+
+    // Instance a cylinder that sits on the Z=0 plane using the given tessellation
+    // levels across the UV domain.  Think of "slices" like a number of pizza
+    // slices, and "stacks" like a number of stacked rings.  
+    // Height and radius are both 1.0, but they can easily be changed with par_shapes_scale
+    par_shapes_mesh *cylinder = par_shapes_create_cylinder(slices, 8);
+    par_shapes_scale(cylinder, radius, radius, height);
+    par_shapes_rotate(cylinder, -PI/2.0f, (float[]){ 1, 0, 0 });
+
+    // Generate an orientable disk shape (top cap)
+    par_shapes_mesh *capTop = par_shapes_create_disk(radius, slices, (float[]){ 0, 0, 0 }, (float[]){ 0, 0, 1 });
+    capTop->tcoords = PAR_MALLOC(float, 2*capTop->npoints);
+    for (int i = 0; i < 2*capTop->npoints; i++) capTop->tcoords[i] = 0.0f;
+    par_shapes_rotate(capTop, -PI/2.0f, (float[]){ 1, 0, 0 });
+    par_shapes_translate(capTop, 0, height, 0);
+    
+    // Generate an orientable disk shape (bottom cap)
+    par_shapes_mesh *capBottom = par_shapes_create_disk(radius, slices, (float[]){ 0, 0, 0 }, (float[]){ 0, 0, -1 });
+    capBottom->tcoords = PAR_MALLOC(float, 2*capBottom->npoints);
+    for (int i = 0; i < 2*capBottom->npoints; i++) capBottom->tcoords[i] = 0.95f;
+    par_shapes_rotate(capBottom, PI/2.0f, (float[]){ 1, 0, 0 });
+    
+    par_shapes_merge_and_free(cylinder, capTop);
+    par_shapes_merge_and_free(cylinder, capBottom);
+    
+    mesh.vertices = (float *)malloc(cylinder->ntriangles*3*3*sizeof(float));
+    mesh.texcoords = (float *)malloc(cylinder->ntriangles*3*2*sizeof(float));
+    mesh.normals = (float *)malloc(cylinder->ntriangles*3*3*sizeof(float));
+
+    mesh.vertexCount = cylinder->ntriangles*3;
+    mesh.triangleCount = cylinder->ntriangles;
+
+    for (int k = 0; k < mesh.vertexCount; k++)
+    {
+        mesh.vertices[k*3] = cylinder->points[cylinder->triangles[k]*3];
+        mesh.vertices[k*3 + 1] = cylinder->points[cylinder->triangles[k]*3 + 1];
+        mesh.vertices[k*3 + 2] = cylinder->points[cylinder->triangles[k]*3 + 2];
+        
+        mesh.normals[k*3] = cylinder->normals[cylinder->triangles[k]*3];
+        mesh.normals[k*3 + 1] = cylinder->normals[cylinder->triangles[k]*3 + 1];
+        mesh.normals[k*3 + 2] = cylinder->normals[cylinder->triangles[k]*3 + 2];
+        
+        mesh.texcoords[k*2] = cylinder->tcoords[cylinder->triangles[k]*2];
+        mesh.texcoords[k*2 + 1] = cylinder->tcoords[cylinder->triangles[k]*2 + 1];
+    }
+
+    par_shapes_free_mesh(cylinder);
+    
+    // Upload vertex data to GPU (static mesh)
+    rlLoadMesh(&mesh, false);  
+
+    return mesh;
+}
+
+// Generate torus mesh
+Mesh GenMeshTorus(float radius, float size, int radSeg, int sides)
+{
+    Mesh mesh = { 0 };
+
+    if (radius > 1.0f) radius = 1.0f;
+    else if (radius < 0.1f) radius = 0.1f;
+    
+    // Create a donut that sits on the Z=0 plane with the specified inner radius
+    // The outer radius can be controlled with par_shapes_scale
+    par_shapes_mesh *torus = par_shapes_create_torus(radSeg, sides, radius);
+    par_shapes_scale(torus, size/2, size/2, size/2);
+
+    mesh.vertices = (float *)malloc(torus->ntriangles*3*3*sizeof(float));
+    mesh.texcoords = (float *)malloc(torus->ntriangles*3*2*sizeof(float));
+    mesh.normals = (float *)malloc(torus->ntriangles*3*3*sizeof(float));
+
+    mesh.vertexCount = torus->ntriangles*3;
+    mesh.triangleCount = torus->ntriangles;
+
+    for (int k = 0; k < mesh.vertexCount; k++)
+    {
+        mesh.vertices[k*3] = torus->points[torus->triangles[k]*3];
+        mesh.vertices[k*3 + 1] = torus->points[torus->triangles[k]*3 + 1];
+        mesh.vertices[k*3 + 2] = torus->points[torus->triangles[k]*3 + 2];
+        
+        mesh.normals[k*3] = torus->normals[torus->triangles[k]*3];
+        mesh.normals[k*3 + 1] = torus->normals[torus->triangles[k]*3 + 1];
+        mesh.normals[k*3 + 2] = torus->normals[torus->triangles[k]*3 + 2];
+        
+        mesh.texcoords[k*2] = torus->tcoords[torus->triangles[k]*2];
+        mesh.texcoords[k*2 + 1] = torus->tcoords[torus->triangles[k]*2 + 1];
+    }
+
+    par_shapes_free_mesh(torus);
+    
+    // Upload vertex data to GPU (static mesh)
+    rlLoadMesh(&mesh, false);  
+
+    return mesh;
+}
+
+// Generate trefoil knot mesh
+Mesh GenMeshKnot(float radius, float size, int radSeg, int sides)
+{
+    Mesh mesh = { 0 };
+    
+    if (radius > 3.0f) radius = 3.0f;
+    else if (radius < 0.5f) radius = 0.5f;
+
+    par_shapes_mesh *knot = par_shapes_create_trefoil_knot(radSeg, sides, radius);
+    par_shapes_scale(knot, size, size, size);
+
+    mesh.vertices = (float *)malloc(knot->ntriangles*3*3*sizeof(float));
+    mesh.texcoords = (float *)malloc(knot->ntriangles*3*2*sizeof(float));
+    mesh.normals = (float *)malloc(knot->ntriangles*3*3*sizeof(float));
+
+    mesh.vertexCount = knot->ntriangles*3;
+    mesh.triangleCount = knot->ntriangles;
+
+    for (int k = 0; k < mesh.vertexCount; k++)
+    {
+        mesh.vertices[k*3] = knot->points[knot->triangles[k]*3];
+        mesh.vertices[k*3 + 1] = knot->points[knot->triangles[k]*3 + 1];
+        mesh.vertices[k*3 + 2] = knot->points[knot->triangles[k]*3 + 2];
+        
+        mesh.normals[k*3] = knot->normals[knot->triangles[k]*3];
+        mesh.normals[k*3 + 1] = knot->normals[knot->triangles[k]*3 + 1];
+        mesh.normals[k*3 + 2] = knot->normals[knot->triangles[k]*3 + 2];
+        
+        mesh.texcoords[k*2] = knot->tcoords[knot->triangles[k]*2];
+        mesh.texcoords[k*2 + 1] = knot->tcoords[knot->triangles[k]*2 + 1];
+    }
+
+    par_shapes_free_mesh(knot);
+    
     // Upload vertex data to GPU (static mesh)
     // Upload vertex data to GPU (static mesh)
     rlLoadMesh(&mesh, false);  
     rlLoadMesh(&mesh, false);  
 
 
@@ -1234,6 +1630,7 @@ Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize)
 
 
     return mesh;
     return mesh;
 }
 }
+#endif      // SUPPORT_MESH_GENERATION
 
 
 // Load material data (from file)
 // Load material data (from file)
 Material LoadMaterial(const char *fileName)
 Material LoadMaterial(const char *fileName)

+ 11 - 6
src/raylib.h

@@ -701,6 +701,7 @@ RLAPI bool WindowShouldClose(void);                               // Check if KE
 RLAPI bool IsWindowMinimized(void);                               // Check if window has been minimized (or lost focus)
 RLAPI bool IsWindowMinimized(void);                               // Check if window has been minimized (or lost focus)
 RLAPI void ToggleFullscreen(void);                                // Toggle fullscreen mode (only PLATFORM_DESKTOP)
 RLAPI void ToggleFullscreen(void);                                // Toggle fullscreen mode (only PLATFORM_DESKTOP)
 RLAPI void SetWindowIcon(Image image);                            // Set icon for window (only PLATFORM_DESKTOP)
 RLAPI void SetWindowIcon(Image image);                            // Set icon for window (only PLATFORM_DESKTOP)
+RLAPI void SetWindowTitle(const char *title);                     // Set title for window (only PLATFORM_DESKTOP)
 RLAPI void SetWindowPosition(int x, int y);                       // Set window position on screen (only PLATFORM_DESKTOP)
 RLAPI void SetWindowPosition(int x, int y);                       // Set window position on screen (only PLATFORM_DESKTOP)
 RLAPI void SetWindowMonitor(int monitor);                         // Set monitor for the current window (fullscreen mode)
 RLAPI void SetWindowMonitor(int monitor);                         // Set monitor for the current window (fullscreen mode)
 RLAPI void SetWindowMinSize(int width, int height);               // Set window minimum dimensions (for FLAG_WINDOW_RESIZABLE)
 RLAPI void SetWindowMinSize(int width, int height);               // Set window minimum dimensions (for FLAG_WINDOW_RESIZABLE)
@@ -759,6 +760,7 @@ RLAPI int GetRandomValue(int min, int max);                       // Returns a r
 
 
 // Files management functions
 // Files management functions
 RLAPI bool IsFileExtension(const char *fileName, const char *ext);// Check file extension
 RLAPI bool IsFileExtension(const char *fileName, const char *ext);// Check file extension
+RLAPI const char *GetExtension(const char *fileName);             // Get file extension
 RLAPI const char *GetDirectoryPath(const char *fileName);         // Get directory for a given fileName (with path)
 RLAPI const char *GetDirectoryPath(const char *fileName);         // Get directory for a given fileName (with path)
 RLAPI const char *GetWorkingDirectory(void);                      // Get current working directory
 RLAPI const char *GetWorkingDirectory(void);                      // Get current working directory
 RLAPI bool ChangeDirectory(const char *dir);                      // Change working directory, returns true if success
 RLAPI bool ChangeDirectory(const char *dir);                      // Change working directory, returns true if success
@@ -855,8 +857,10 @@ RLAPI void DrawRectangle(int posX, int posY, int width, int height, Color color)
 RLAPI void DrawRectangleRec(Rectangle rec, Color color);                                                 // Draw a color-filled rectangle
 RLAPI void DrawRectangleRec(Rectangle rec, Color color);                                                 // Draw a color-filled rectangle
 RLAPI void DrawRectanglePro(Rectangle rec, Vector2 origin, float rotation, Color color);                 // Draw a color-filled rectangle with pro parameters
 RLAPI void DrawRectanglePro(Rectangle rec, Vector2 origin, float rotation, Color color);                 // Draw a color-filled rectangle with pro parameters
 RLAPI void DrawRectangleGradient(int posX, int posY, int width, int height, Color color1, Color color2); // Draw a gradient-filled rectangle
 RLAPI void DrawRectangleGradient(int posX, int posY, int width, int height, Color color1, Color color2); // Draw a gradient-filled rectangle
+RLAPI void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3, Color col4);       // Draw a gradient-filled rectangle with custom vertex colors
 RLAPI void DrawRectangleV(Vector2 position, Vector2 size, Color color);                                  // Draw a color-filled rectangle (Vector version)
 RLAPI void DrawRectangleV(Vector2 position, Vector2 size, Color color);                                  // Draw a color-filled rectangle (Vector version)
 RLAPI void DrawRectangleLines(int posX, int posY, int width, int height, Color color);                   // Draw rectangle outline
 RLAPI void DrawRectangleLines(int posX, int posY, int width, int height, Color color);                   // Draw rectangle outline
+RLAPI void DrawRectangleT(int posX, int posY, int width, int height, Color color);                       // Draw rectangle using text character
 RLAPI void DrawTriangle(Vector2 v1, Vector2 v2, Vector2 v3, Color color);                                // Draw a color-filled triangle
 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 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)
 RLAPI void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color color);               // Draw a regular polygon (Vector version)
@@ -952,7 +956,6 @@ 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 DrawText(const char *text, int posX, int posY, int fontSize, Color color);                    // Draw text (using default font)
 RLAPI void DrawTextEx(SpriteFont spriteFont, const char* text, Vector2 position,                         // Draw text using SpriteFont and additional parameters
 RLAPI void DrawTextEx(SpriteFont spriteFont, const char* text, Vector2 position,                         // Draw text using SpriteFont and additional parameters
                 float fontSize, int spacing, Color tint);
                 float fontSize, int spacing, Color tint);
-RLAPI void DrawRectangleT(int posX, int posY, int width, int height, Color color);                       // Draw rectangle using text character
 
 
 // Text misc. functions
 // Text misc. functions
 RLAPI int MeasureText(const char *text, int fontSize);                                                   // Measure string width for default font
 RLAPI int MeasureText(const char *text, int fontSize);                                                   // Measure string width for default font
@@ -995,12 +998,14 @@ RLAPI void UnloadModel(Model model);
 RLAPI Mesh LoadMesh(const char *fileName);                                                              // Load mesh from file
 RLAPI Mesh LoadMesh(const char *fileName);                                                              // Load mesh from file
 RLAPI void UnloadMesh(Mesh *mesh);                                                                      // Unload mesh from memory (RAM and/or VRAM)
 RLAPI void UnloadMesh(Mesh *mesh);                                                                      // Unload mesh from memory (RAM and/or VRAM)
 
 
-//RLAPI Mesh GenMeshPlane(float width, float length, int resX, int resZ);                               // Generate plane mesh (with desired subdivisions)
+// Mesh generation functions
+RLAPI Mesh GenMeshPlane(float width, float length, int resX, int resZ);                                 // Generate plane mesh (with subdivisions)
 RLAPI Mesh GenMeshCube(float width, float height, float length);                                        // Generate cuboid mesh
 RLAPI Mesh GenMeshCube(float width, float height, float length);                                        // Generate cuboid mesh
-//RLAPI Mesh GenMeshSphere(float radius, int rings, int slices);                                        // Generate sphere mesh (standard sphere)
-//RLAPI Mesh GenMeshCylinder(float radiusTop, float radiusBottom, float height, int slices);            // Generate cylinder mesh
-//RLAPI Mesh GenMeshTorus(float radius1, float radius2, int radSeg, int sides);                         // Generate torus mesh
-//RLAPI Mesh GenMeshTube(float radius1, float radius2, float height, int sides);                        // Generate tube mesh
+RLAPI Mesh GenMeshSphere(float radius, int rings, int slices);                                          // Generate sphere mesh (standard sphere)
+RLAPI Mesh GenMeshHemiSphere(float radius, int rings, int slices);                                      // Generate half-sphere mesh (no bottom cap)
+RLAPI Mesh GenMeshCylinder(float radius, float height, int slices);                                     // Generate cylinder mesh
+RLAPI Mesh GenMeshTorus(float radius, float size, int radSeg, int sides);                               // Generate torus mesh
+RLAPI Mesh GenMeshKnot(float radius, float size, int radSeg, int sides);                                // Generate trefoil knot mesh
 RLAPI Mesh GenMeshHeightmap(Image heightmap, Vector3 size);                                             // Generate heightmap mesh from image data
 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
 RLAPI Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize);                                           // Generate cubes-based map mesh from image data
 
 

+ 8 - 4
src/rlgl.c

@@ -418,7 +418,6 @@ void rlPushMatrix(void)
     }
     }
 
 
     stack[stackCounter] = *currentMatrix;
     stack[stackCounter] = *currentMatrix;
-    rlLoadIdentity();
     stackCounter++;
     stackCounter++;
 
 
     if (currentMatrixMode == RL_MODELVIEW) useTempBuffer = true;
     if (currentMatrixMode == RL_MODELVIEW) useTempBuffer = true;
@@ -814,6 +813,12 @@ void rlEnableTexture(unsigned int id)
     if (draws[drawsCounter - 1].textureId != id)
     if (draws[drawsCounter - 1].textureId != id)
     {
     {
         if (draws[drawsCounter - 1].vertexCount > 0) drawsCounter++;
         if (draws[drawsCounter - 1].vertexCount > 0) drawsCounter++;
+        
+        if (drawsCounter >= MAX_DRAWS_BY_TEXTURE)
+        {
+            rlglDraw();
+            drawsCounter = 1;
+        }
 
 
         draws[drawsCounter - 1].textureId = id;
         draws[drawsCounter - 1].textureId = id;
         draws[drawsCounter - 1].vertexCount = 0;
         draws[drawsCounter - 1].vertexCount = 0;
@@ -2207,7 +2212,6 @@ void *rlReadTexturePixels(Texture2D texture)
     // 2 - Create an fbo, activate it, render quad with texture, glReadPixels()
     // 2 - Create an fbo, activate it, render quad with texture, glReadPixels()
 
 
 #define GET_TEXTURE_FBO_OPTION_1    // It works
 #define GET_TEXTURE_FBO_OPTION_1    // It works
-
 #if defined(GET_TEXTURE_FBO_OPTION_1)
 #if defined(GET_TEXTURE_FBO_OPTION_1)
     glBindFramebuffer(GL_FRAMEBUFFER, fbo.id);
     glBindFramebuffer(GL_FRAMEBUFFER, fbo.id);
     glBindTexture(GL_TEXTURE_2D, 0);
     glBindTexture(GL_TEXTURE_2D, 0);
@@ -2941,7 +2945,7 @@ void ToggleVrMode(void)
         
         
         // Reset viewport and default projection-modelview matrices
         // Reset viewport and default projection-modelview matrices
         rlViewport(0, 0, screenWidth, screenHeight);
         rlViewport(0, 0, screenWidth, screenHeight);
-        projection = MatrixOrtho(0, screenWidth, screenHeight, 0, 0.0f, 1.0f);
+        projection = MatrixOrtho(0.0, screenWidth, screenHeight, 0.0, 0.0, 1.0);
         modelview = MatrixIdentity();
         modelview = MatrixIdentity();
     }
     }
     else vrStereoRender = true;
     else vrStereoRender = true;
@@ -3043,7 +3047,7 @@ void EndVrDrawing(void)
 
 
         // Reset viewport and default projection-modelview matrices
         // Reset viewport and default projection-modelview matrices
         rlViewport(0, 0, screenWidth, screenHeight);
         rlViewport(0, 0, screenWidth, screenHeight);
-        projection = MatrixOrtho(0, screenWidth, screenHeight, 0, 0.0f, 1.0f);
+        projection = MatrixOrtho(0.0, screenWidth, screenHeight, 0.0, 0.0, 1.0);
         modelview = MatrixIdentity();
         modelview = MatrixIdentity();
         
         
         rlDisableDepthTest();
         rlDisableDepthTest();

+ 85 - 30
src/rres.h

@@ -34,6 +34,14 @@
 *
 *
 **********************************************************************************************/
 **********************************************************************************************/
 
 
+/*  
+References:
+    RIFF file-format:  http://www.johnloomis.org/cpe102/asgn/asgn1/riff.html
+    ZIP file-format:   https://en.wikipedia.org/wiki/Zip_(file_format)
+                       http://www.onicos.com/staff/iz/formats/zip.html
+    XNB file-format:   http://xbox.create.msdn.com/en-US/sample/xnb_format
+*/
+
 #ifndef RRES_H
 #ifndef RRES_H
 #define RRES_H
 #define RRES_H
 
 
@@ -75,6 +83,9 @@
         void *data;                 // Resource data pointer (4 byte)
         void *data;                 // Resource data pointer (4 byte)
     } RRESData;
     } RRESData;
     
     
+    // RRES type (pointer to RRESData array)
+    typedef struct RRESData *RRES;  // Resource pointer
+    
     // RRESData type
     // RRESData type
     typedef enum { 
     typedef enum { 
         RRES_TYPE_RAW = 0, 
         RRES_TYPE_RAW = 0, 
@@ -83,12 +94,25 @@
         RRES_TYPE_VERTEX, 
         RRES_TYPE_VERTEX, 
         RRES_TYPE_TEXT,
         RRES_TYPE_TEXT,
         RRES_TYPE_FONT_IMAGE,
         RRES_TYPE_FONT_IMAGE,
-        RRES_TYPE_FONT_CHARDATA,        // Character { int value, recX, recY, recWidth, recHeight, offsetX, offsetY, xAdvance } 
+        RRES_TYPE_FONT_CHARDATA,    // CharInfo { int value, recX, recY, recWidth, recHeight, offsetX, offsetY, xAdvance } 
         RRES_TYPE_DIRECTORY
         RRES_TYPE_DIRECTORY
     } RRESDataType;
     } RRESDataType;
     
     
-    // RRES type (pointer to RRESData array)
-    typedef struct RRESData *RRES;
+// Parameters information depending on resource type
+
+// RRES_TYPE_RAW params:        <custom>
+// RRES_TYPE_IMAGE params:      width, height, mipmaps, format
+// RRES_TYPE_WAVE params:       sampleCount, sampleRate, sampleSize, channels
+// RRES_TYPE_VERTEX params:     vertexCount, vertexType, vertexFormat           // Use masks instead?
+// RRES_TYPE_TEXT params:       charsCount, cultureCode
+// RRES_TYPE_FONT_IMAGE params: width, height, format, mipmaps;
+// RRES_TYPE_FONT_CHARDATA params:  charsCount, baseSize
+// RRES_TYPE_DIRECTORY params:  fileCount, directoryCount
+
+// SpriteFont = RRES_TYPE_FONT_IMAGE chunk + RRES_TYPE_FONT_DATA chunk
+// Mesh = multiple RRES_TYPE_VERTEX chunks
+    
+
 #endif
 #endif
 
 
 //----------------------------------------------------------------------------------
 //----------------------------------------------------------------------------------
@@ -103,6 +127,54 @@
 RRESDEF RRES LoadResource(const char *fileName, int rresId);
 RRESDEF RRES LoadResource(const char *fileName, int rresId);
 RRESDEF void UnloadResource(RRES rres);
 RRESDEF void UnloadResource(RRES rres);
 
 
+/*
+QUESTION: How to load each type of data from RRES ?
+
+rres->type == RRES_TYPE_RAW
+unsigned char data = (unsigned char *)rres[0]->data;
+
+rres->type == RRES_TYPE_IMAGE
+Image image;
+image.data = rres[0]->data;        // Be careful, duplicate pointer
+image.width = rres[0]->param1;
+image.height = rres[0]->param2;
+image.mipmaps = rres[0]->param3;
+image.format =  rres[0]->format;
+
+rres->type == RRES_TYPE_WAVE
+Wave wave;
+wave.data = rres[0]->data;
+wave.sampleCount = rres[0]->param1;
+wave.sampleRate = rres[0]->param2;
+wave.sampleSize = rres[0]->param3;
+wave.channels = rres[0]->param4;
+
+rres->type == RRES_TYPE_VERTEX (multiple parts)
+Mesh mesh;
+mesh.vertexCount = rres[0]->param1;
+mesh.vertices = (float *)rres[0]->data;
+mesh.texcoords = (float *)rres[1]->data;
+mesh.normals = (float *)rres[2]->data;
+mesh.tangents = (float *)rres[3]->data;
+mesh.tangents = (unsigned char *)rres[4]->data;
+
+rres->type == RRES_TYPE_TEXT
+unsigned char *text = (unsigned char *)rres->data;
+Shader shader = LoadShaderText(text, rres->param1);     Shader LoadShaderText(const char *shdrText, int length);
+
+rres->type == RRES_TYPE_FONT_IMAGE      (multiple parts)
+rres->type == RRES_TYPE_FONT_CHARDATA   
+SpriteFont font;
+font.texture = LoadTextureFromImage(image);     // rres[0]
+font.chars = (CharInfo *)rres[1]->data;
+font.charsCount = rres[1]->param1;
+font.baseSize = rres[1]->param2;
+
+rres->type == RRES_TYPE_DIRECTORY
+unsigned char *fileNames = (unsigned char *)rres[0]->data;  // fileNames separed by \n
+int filesCount = rres[0]->param1;
+*/
+
 #endif // RRES_H
 #endif // RRES_H
 
 
 
 
@@ -169,6 +241,7 @@ typedef enum {
     // gzip, zopfli, lzo, zstd  // Other compression algorythms...
     // gzip, zopfli, lzo, zstd  // Other compression algorythms...
 } RRESCompressionType;
 } RRESCompressionType;
 
 
+// Encryption types
 typedef enum {
 typedef enum {
     RRES_CRYPTO_NONE = 0,       // No data encryption
     RRES_CRYPTO_NONE = 0,       // No data encryption
     RRES_CRYPTO_XOR,            // XOR (128 bit) encryption
     RRES_CRYPTO_XOR,            // XOR (128 bit) encryption
@@ -179,6 +252,7 @@ typedef enum {
     // twofish, RC5, RC6        // Other encryption algorythm...
     // twofish, RC5, RC6        // Other encryption algorythm...
 } RRESEncryptionType;
 } RRESEncryptionType;
 
 
+// Image/Texture data type
 typedef enum {
 typedef enum {
     RRES_IM_UNCOMP_GRAYSCALE = 1,     // 8 bit per pixel (no alpha)
     RRES_IM_UNCOMP_GRAYSCALE = 1,     // 8 bit per pixel (no alpha)
     RRES_IM_UNCOMP_GRAY_ALPHA,        // 16 bpp (2 channels)
     RRES_IM_UNCOMP_GRAY_ALPHA,        // 16 bpp (2 channels)
@@ -201,6 +275,7 @@ typedef enum {
     //...
     //...
 } RRESImageFormat;
 } RRESImageFormat;
 
 
+// Vertex data type
 typedef enum {
 typedef enum {
     RRES_VERT_POSITION,
     RRES_VERT_POSITION,
     RRES_VERT_TEXCOORD1,
     RRES_VERT_TEXCOORD1,
@@ -214,6 +289,7 @@ typedef enum {
     //...
     //...
 } RRESVertexType;
 } RRESVertexType;
 
 
+// Vertex data format type
 typedef enum {
 typedef enum {
     RRES_VERT_BYTE,
     RRES_VERT_BYTE,
     RRES_VERT_SHORT,
     RRES_VERT_SHORT,
@@ -275,10 +351,10 @@ RRESDEF RRES LoadResource(const char *fileName, int rresId)
                 // Read resource info and parameters
                 // Read resource info and parameters
                 fread(&infoHeader, sizeof(RRESInfoHeader), 1, rresFile);
                 fread(&infoHeader, sizeof(RRESInfoHeader), 1, rresFile);
                 
                 
-                rres = (RRES)malloc(sizeof(RRESData)*infoHeader.partsCount);
-                
                 if (infoHeader.id == rresId)
                 if (infoHeader.id == rresId)
                 {
                 {
+                    rres = (RRES)malloc(sizeof(RRESData)*infoHeader.partsCount);
+                                    
                     // Load all required resources parts
                     // Load all required resources parts
                     for (int k = 0; k < infoHeader.partsCount; k++)
                     for (int k = 0; k < infoHeader.partsCount; k++)
                     {
                     {
@@ -327,8 +403,11 @@ RRESDEF RRES LoadResource(const char *fileName, int rresId)
     return rres;
     return rres;
 }
 }
 
 
+// Unload resource data
 RRESDEF void UnloadResource(RRES rres)
 RRESDEF void UnloadResource(RRES rres)
 {
 {
+    // TODO: When you load resource... how many parts conform it? depends on type? --> Not clear...
+    
     if (rres[0].data != NULL) free(rres[0].data);
     if (rres[0].data != NULL) free(rres[0].data);
 }
 }
 
 
@@ -401,28 +480,4 @@ void TraceLog(int logType, const char *text, ...)
 }
 }
 #endif
 #endif
 
 
-#endif // RAYGUI_IMPLEMENTATION
-
-/*
-Mesh LoadMeshEx(int numVertex, float *vData, float *vtData, float *vnData, Color *cData);
-Mesh LoadMeshEx(rres.param1, rres.data, rres.data + offset, rres.data + offset*2, rres.data + offset*3);
-
-Shader LoadShader(const char *vsText, int vsLength);
-Shader LoadShaderV(rres.data, rres.param1);
-
-// Parameters information depending on resource type
-
-// RRES_TYPE_IMAGE params:      imgWidth, imgHeight, format, mipmaps;
-// RRES_TYPE_WAVE params:       sampleCount, sampleRate, sampleSize, channels;
-// RRES_TYPE_FONT_IMAGE params: imgWidth, imgHeight, format, mipmaps;
-// RRES_TYPE_FONT_DATA params:  charsCount, baseSize
-// RRES_TYPE_VERTEX params:     vertexCount, vertexType, vertexFormat        // Use masks instead?
-// RRES_TYPE_TEXT params:       charsCount, cultureCode
-// RRES_TYPE_DIRECTORY params:  fileCount, directoryCount
-
-// SpriteFont = RRES_TYPE_FONT_IMAGE chunk + RRES_TYPE_FONT_DATA chunk
-// Mesh = multiple RRES_TYPE_VERTEX chunks
-
-Ref: RIFF file-format: http://www.johnloomis.org/cpe102/asgn/asgn1/riff.html
-
-*/
+#endif // RRES_IMPLEMENTATION

+ 55 - 0
src/shapes.c

@@ -289,6 +289,53 @@ void DrawRectangleGradient(int posX, int posY, int width, int height, Color colo
     rlEnd();
     rlEnd();
 }
 }
 
 
+// Draw a gradient-filled rectangle
+void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3, Color col4)
+{
+    rlEnableTexture(GetTextureDefault().id);    // Default white texture
+
+    rlBegin(RL_QUADS);
+        rlNormal3f(0.0f, 0.0f, 1.0f);
+
+        rlColor4ub(col1.r, col1.g, col1.b, col1.a);
+        rlTexCoord2f(0.0f, 0.0f);
+        rlVertex2f(rec.x, rec.y);
+
+        rlColor4ub(col2.r, col2.g, col2.b, col2.a);
+        rlTexCoord2f(0.0f, 1.0f);
+        rlVertex2f(rec.x, rec.y + rec.height);
+
+        rlColor4ub(col3.r, col3.g, col3.b, col3.a);
+        rlTexCoord2f(1.0f, 1.0f);
+        rlVertex2f(rec.x + rec.width, rec.y + rec.height);
+
+        rlColor4ub(col4.r, col4.g, col4.b, col4.a);
+        rlTexCoord2f(1.0f, 0.0f);
+        rlVertex2f(rec.x + rec.width, rec.y);
+    rlEnd();
+    
+    // Draw rectangle using font texture white character
+    /*
+    rlTexCoord2f((float)GetDefaultFont().chars[95].rec.x/GetDefaultFont().texture.width, 
+                 (float)GetDefaultFont().chars[95].rec.y/GetDefaultFont().texture.height);
+    rlVertex2f(rec.x, rec.y);
+    
+    rlTexCoord2f((float)GetDefaultFont().chars[95].rec.x/GetDefaultFont().texture.width, 
+                 (float)(GetDefaultFont().chars[95].rec.y + GetDefaultFont().chars[95].rec.height)/GetDefaultFont().texture.height);
+    rlVertex2f(rec.x, rec.y + rec.height);
+    
+    rlTexCoord2f((float)(GetDefaultFont().chars[95].rec.x + GetDefaultFont().chars[95].rec.width)/GetDefaultFont().texture.width, 
+                 (float)(GetDefaultFont().chars[95].rec.y + GetDefaultFont().chars[95].rec.height)/GetDefaultFont().texture.height);
+    rlVertex2f(rec.x + rec.width, rec.y + rec.height);
+    
+    rlTexCoord2f((float)(GetDefaultFont().chars[95].rec.x + GetDefaultFont().chars[95].rec.width)/GetDefaultFont().texture.width, 
+                 (float)GetDefaultFont().chars[95].rec.y/GetDefaultFont().texture.height);
+    rlVertex2f(rec.x + rec.width, rec.y);
+    */
+                
+    rlDisableTexture();
+}
+
 // Draw a color-filled rectangle (Vector version)
 // Draw a color-filled rectangle (Vector version)
 // NOTE: On OpenGL 3.3 and ES2 we use QUADS to avoid drawing order issues (view rlglDraw)
 // NOTE: On OpenGL 3.3 and ES2 we use QUADS to avoid drawing order issues (view rlglDraw)
 void DrawRectangleV(Vector2 position, Vector2 size, Color color)
 void DrawRectangleV(Vector2 position, Vector2 size, Color color)
@@ -362,6 +409,14 @@ void DrawRectangleLines(int posX, int posY, int width, int height, Color color)
     }
     }
 }
 }
 
 
+// Draw rectangle using text character (char: 127)
+// NOTE: Useful to avoid changing to default white texture
+void DrawRectangleT(int posX, int posY, int width, int height, Color color)
+{
+    DrawTexturePro(GetDefaultFont().texture, GetDefaultFont().chars[95].rec, 
+                   (Rectangle){ posX, posY, width, height }, (Vector2){ 0, 0 }, 0.0f, color);
+}
+
 // Draw a triangle
 // Draw a triangle
 void DrawTriangle(Vector2 v1, Vector2 v2, Vector2 v3, Color color)
 void DrawTriangle(Vector2 v1, Vector2 v2, Vector2 v3, Color color)
 {
 {

+ 0 - 8
src/text.c

@@ -457,14 +457,6 @@ void DrawTextEx(SpriteFont spriteFont, const char *text, Vector2 position, float
     }
     }
 }
 }
 
 
-// Draw rectangle using text character (char: 127)
-// NOTE: Useful to avoid changing to default white texture
-void DrawRectangleT(int posX, int posY, int width, int height, Color color)
-{
-    DrawTexturePro(GetDefaultFont().texture, GetDefaultFont().chars[95].rec, 
-                   (Rectangle){ posX, posY, width, height }, (Vector2){ 0, 0 }, 0.0f, color);
-}
-
 // Formatting of text with variables to 'embed'
 // Formatting of text with variables to 'embed'
 const char *FormatText(const char *text, ...)
 const char *FormatText(const char *text, ...)
 {
 {

+ 7 - 1
src/textures.c

@@ -23,6 +23,9 @@
 *       Support multiple image editing functions to scale, adjust colors, flip, draw on images, crop...
 *       Support multiple image editing functions to scale, adjust colors, flip, draw on images, crop...
 *       If not defined only three image editing functions supported: ImageFormat(), ImageAlphaMask(), ImageToPOT()
 *       If not defined only three image editing functions supported: ImageFormat(), ImageAlphaMask(), ImageToPOT()
 *
 *
+*   #define SUPPORT_IMAGE_GENERATION
+*       Support proedural image generation functionality (gradient, spot, perlin-noise, cellular)
+*
 *   DEPENDENCIES:
 *   DEPENDENCIES:
 *       stb_image        - Multiple image formats loading (JPEG, PNG, BMP, TGA, PSD, GIF, PIC)
 *       stb_image        - Multiple image formats loading (JPEG, PNG, BMP, TGA, PSD, GIF, PIC)
 *                          NOTE: stb_image has been slightly modified to support Android platform.
 *                          NOTE: stb_image has been slightly modified to support Android platform.
@@ -56,6 +59,7 @@
 #define SUPPORT_FILEFORMAT_DDS
 #define SUPPORT_FILEFORMAT_DDS
 #define SUPPORT_FILEFORMAT_HDR
 #define SUPPORT_FILEFORMAT_HDR
 #define SUPPORT_IMAGE_MANIPULATION
 #define SUPPORT_IMAGE_MANIPULATION
+#define SUPPORT_IMAGE_GENERATION
 //-------------------------------------------------
 //-------------------------------------------------
 
 
 #include "raylib.h"
 #include "raylib.h"
@@ -1064,7 +1068,7 @@ Image ImageTextEx(SpriteFont font, const char *text, float fontSize, int spacing
 
 
     Vector2 imSize = MeasureTextEx(font, text, font.baseSize, spacing);
     Vector2 imSize = MeasureTextEx(font, text, font.baseSize, spacing);
 
 
-    // NOTE: GetTextureData() not available in OpenGL ES
+    // NOTE: glGetTexImage() not available in OpenGL ES
     Image imFont = GetTextureData(font.texture);
     Image imFont = GetTextureData(font.texture);
 
 
     ImageFormat(&imFont, UNCOMPRESSED_R8G8B8A8);    // Convert to 32 bit for color tint
     ImageFormat(&imFont, UNCOMPRESSED_R8G8B8A8);    // Convert to 32 bit for color tint
@@ -1447,6 +1451,7 @@ void ImageColorBrightness(Image *image, int brightness)
 }
 }
 #endif      // SUPPORT_IMAGE_MANIPULATION
 #endif      // SUPPORT_IMAGE_MANIPULATION
 
 
+#if defined(SUPPORT_IMAGE_GENERATION)
 // Generate image: vertical gradient
 // Generate image: vertical gradient
 Image GenImageGradientV(int width, int height, Color top, Color bottom)
 Image GenImageGradientV(int width, int height, Color top, Color bottom)
 {
 {
@@ -1647,6 +1652,7 @@ Image GenImageCellular(int width, int height, int tileSize)
 
 
     return image;
     return image;
 }
 }
+#endif      // SUPPORT_IMAGE_GENERATION
 
 
 // Generate GPU mipmaps for a texture
 // Generate GPU mipmaps for a texture
 void GenTextureMipmaps(Texture2D *texture)
 void GenTextureMipmaps(Texture2D *texture)

+ 1 - 1
templates/android_project/AndroidManifest.xml

@@ -14,7 +14,7 @@
         android:versionCode="1" 
         android:versionCode="1" 
         android:versionName="1.0" >
         android:versionName="1.0" >
 
 
-    <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="17" />
+    <uses-sdk android:minSdkVersion="16" android:targetSdkVersion="16" />
     <uses-feature android:glEsVersion="0x00020000" android:required="true" />
     <uses-feature android:glEsVersion="0x00020000" android:required="true" />
     <!--<supports-gl-texture android:name="GL_OES_compressed_ETC1_RGB8_texture" android:required="true"/>-->
     <!--<supports-gl-texture android:name="GL_OES_compressed_ETC1_RGB8_texture" android:required="true"/>-->
     <!-- We do not have Java code. Therefore android:hasCode is set to false. -->
     <!-- We do not have Java code. Therefore android:hasCode is set to false. -->

+ 144 - 0
templates/android_project/Makefile

@@ -0,0 +1,144 @@
+#**************************************************************************************************
+#
+#   raylib makefile for Android project (APK building)
+#
+#   Copyright (c) 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.
+#
+#**************************************************************************************************
+
+# Define raylib platform to compile for
+PLATFORM ?= PLATFORM_ANDROID
+
+# Android project name (.apk)  
+PROJECT_NAME = NativeActivity
+PROJECT_DIR = ./
+
+# Generated shared library name
+# NOTE: It should match the name defined in the AndroidManifest.xml
+LIBRARY_NAME = raylib_game
+
+# Generated key pass
+KEYSTORE_PASS = raylib
+
+# Required path variables
+# NOTE: JAVA_HOME must be set to JDK
+ANDROID_HOME = C:/android-sdk
+ANDROID_NDK = C:/android-ndk
+ANDROID_TOOLCHAIN = C:/android_toolchain_arm_api16
+ANDROID_BUILD_TOOLS = C:/android-sdk/build-tools/26.0.1
+JAVA_HOME = C:/PROGRA~1/Java/jdk1.8.0_25
+
+# Compilers
+CC = $(ANDROID_TOOLCHAIN)/bin/arm-linux-androideabi-gcc
+AR = $(ANDROID_TOOLCHAIN)/bin/arm-linux-androideabi-ar
+
+# Define compiler flags
+CFLAGS = -O2 -s -Wall -std=c99 -DPLATFORM_ANDROID -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16
+
+# Define any directories containing required header files
+INCLUDES = -I. -Ijni/include -I$(ANDROID_NDK)/sources/android/native_app_glue
+
+# Define library paths containing required libs
+LFLAGS = -L. -Ljni/libs -Ljni -Ltemp/lib
+
+# Define any libraries to link into executable
+# if you want to link libraries (libname.so or libname.a), use the -lname
+LIBS = -lraylib -lopenal -llog -landroid -lEGL -lGLESv2 -lOpenSLES
+
+# Building APK
+# NOTE: typing 'make' will invoke the default target entry called 'all',
+all: project_dirs \
+     native_app_glue \
+     project_code \
+     gen_keystore \
+     project_package \
+     project_class \
+     project_class_dex \
+     project_apk \
+     apk_signing \
+     apk_zip_align
+
+# Create required temp directories for APK building
+project_dirs:
+	if not exist temp mkdir temp 
+	if not exist temp\obj mkdir temp\obj
+	if not exist temp\src mkdir temp\src
+	if not exist temp\lib mkdir temp\lib
+	if not exist temp\bin mkdir temp\bin
+     
+# Compile native_app_glue as static library
+# OUTPUT: $(PROJECT_DIR)/temp/obj/libnative_app_glue.a
+native_app_glue:
+	$(CC) -c $(ANDROID_NDK)/sources/android/native_app_glue/android_native_app_glue.c -o temp/obj/native_app_glue.o $(CFLAGS)
+	$(AR) rcs $(PROJECT_DIR)/temp/lib/libnative_app_glue.a temp/obj/native_app_glue.o
+
+# Compile project code as shared libraries
+# OUTPUT: $(PROJECT_DIR)/temp/lib/lib$(LIBRARY_NAME).so 
+project_code:
+	$(CC) -c jni/basic_game.c -o temp/obj/basic_game.o $(INCLUDES) $(CFLAGS) --sysroot=$(ANDROID_TOOLCHAIN)/sysroot -fPIC 
+	$(CC) -o temp/lib/lib$(LIBRARY_NAME).so temp/obj/basic_game.o -shared $(INCLUDES) $(LFLAGS) $(LIBS) -lnative_app_glue
+
+# Generate key for APK signing
+# OUTPUT: $(PROJECT_DIR)/temp/$(PROJECT_NAME).keystore
+gen_keystore: 
+	$(JAVA_HOME)/bin/keytool -genkeypair -validity 1000 -dname "CN=raylib,O=Android,C=JPN" -keystore temp/$(PROJECT_NAME).keystore -storepass $(KEYSTORE_PASS) -keypass $(KEYSTORE_PASS) -alias $(PROJECT_NAME)Key -keyalg RSA
+
+# Create temp/src/com/raylib/$(LIBRARY_NAME)/R.java
+# OUTPUT: $(PROJECT_DIR)/temp/src/com/raylib/$(LIBRARY_NAME)/R.java
+# NOTE: DEPENDS on res/values/strings.xml
+project_package:
+	$(ANDROID_BUILD_TOOLS)/aapt package -f -m -S res -J temp/src -M AndroidManifest.xml -I $(ANDROID_HOME)/platforms/android-16/android.jar
+
+# Create temp/obj/com/raylib/$(LIBRARY_NAME)/R.class   
+# OUTPUT: $(PROJECT_DIR)/temp/obj/com/raylib/$(LIBRARY_NAME)/R.class
+project_class:
+	$(JAVA_HOME)/bin/javac -source 1.7 -target 1.7 -d temp/obj -classpath $(ANDROID_HOME)/platforms/android-16/android.jar -sourcepath temp/src temp/src/com/raylib/game_sample/R.java
+
+# Create temp/bin/classes.dex
+# OUTPUT: $(PROJECT_DIR)/bin/classes.dex
+# NOTE: DEPENDS on temp/obj/com/raylib/$(LIBRARY_NAME)/R.class
+project_class_dex:
+	$(ANDROID_BUILD_TOOLS)/dx --dex --output=temp/bin/classes.dex temp/obj
+
+# Create temp/bin/$(PROJECT_NAME).unsigned.apk
+# NOTE: DEPENDS on temp/bin/classes.dex and temp/lib/lib$(LIBRARY_NAME).so
+# NOTE: Use -A resources to define additional directory in which to find raw asset files
+project_apk:
+	$(ANDROID_BUILD_TOOLS)/aapt package -f -m -M AndroidManifest.xml -S res -A assets -I $(ANDROID_HOME)/platforms/android-16/android.jar -F temp/bin/$(PROJECT_NAME).unsigned.apk -J temp/bin
+	$(ANDROID_BUILD_TOOLS)/aapt add $(PROJECT_DIR)/temp/bin/$(PROJECT_NAME).unsigned.apk temp/lib/lib$(LIBRARY_NAME).so
+
+# Create temp/bin/$(PROJECT_NAME).signed.apk
+apk_signing:
+	$(JAVA_HOME)/bin/jarsigner -keystore temp/$(PROJECT_NAME).keystore -storepass $(KEYSTORE_PASS) -keypass $(KEYSTORE_PASS) -signedjar $(PROJECT_DIR)/temp/bin/$(PROJECT_NAME).signed.apk temp/bin/$(PROJECT_NAME).unsigned.apk $(PROJECT_NAME)Key
+
+# Create temp/bin/$(PROJECT_NAME).apk 
+apk_zip_align:
+	$(ANDROID_BUILD_TOOLS)/zipalign -f 4 temp/bin/$(PROJECT_NAME).signed.apk $(PROJECT_NAME).apk
+
+# Deploy $(PROJECT_NAME).apk to device
+deploy:
+	$(ANDROID_HOME)/platform-tools/adb install -r $(PROJECT_NAME).apk
+	$(ANDROID_HOME)/platform-tools/adb logcat -c
+	$(ANDROID_HOME)/platform-tools/adb logcat *:W
+
+# Clean everything
+clean:
+	del temp\bin\* temp\lib\* temp\obj\* temp\src\* /f/s/q
+	del temp\*.keystore
+	rmdir temp /s /q
+	@echo Cleaning done

+ 0 - 92
templates/android_project/build.xml

@@ -1,92 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project name="nativeGame" default="help">
-
-    <!-- The local.properties file is created and updated by the 'android' tool.
-         It contains the path to the SDK. It should *NOT* be checked into
-         Version Control Systems. -->
-    <property file="local.properties" />
-
-    <!-- The ant.properties file can be created by you. It is only edited by the
-         'android' tool to add properties to it.
-         This is the place to change some Ant specific build properties.
-         Here are some properties you may want to change/update:
-
-         source.dir
-             The name of the source directory. Default is 'src'.
-         out.dir
-             The name of the output directory. Default is 'bin'.
-
-         For other overridable properties, look at the beginning of the rules
-         files in the SDK, at tools/ant/build.xml
-
-         Properties related to the SDK location or the project target should
-         be updated using the 'android' tool with the 'update' action.
-
-         This file is an integral part of the build system for your
-         application and should be checked into Version Control Systems.
-
-         -->
-    <property file="ant.properties" />
-
-    <!-- if sdk.dir was not set from one of the property file, then
-         get it from the ANDROID_HOME env var.
-         This must be done before we load project.properties since
-         the proguard config can use sdk.dir -->
-    <property environment="env" />
-    <condition property="sdk.dir" value="${env.ANDROID_HOME}">
-        <isset property="env.ANDROID_HOME" />
-    </condition>
-
-    <!-- The project.properties file is created and updated by the 'android'
-         tool, as well as ADT.
-
-         This contains project specific properties such as project target, and library
-         dependencies. Lower level build properties are stored in ant.properties
-         (or in .classpath for Eclipse projects).
-
-         This file is an integral part of the build system for your
-         application and should be checked into Version Control Systems. -->
-    <loadproperties srcFile="project.properties" />
-
-    <!-- quick check on sdk.dir -->
-    <fail
-            message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable."
-            unless="sdk.dir"
-    />
-
-    <!--
-        Import per project custom build rules if present at the root of the project.
-        This is the place to put custom intermediary targets such as:
-            -pre-build
-            -pre-compile
-            -post-compile (This is typically used for code obfuscation.
-                           Compiled code location: ${out.classes.absolute.dir}
-                           If this is not done in place, override ${out.dex.input.absolute.dir})
-            -post-package
-            -post-build
-            -pre-clean
-    -->
-    <import file="custom_rules.xml" optional="true" />
-
-    <!-- Import the actual build file.
-
-         To customize existing targets, there are two options:
-         - Customize only one target:
-             - copy/paste the target into this file, *before* the
-               <import> task.
-             - customize it to your needs.
-         - Customize the whole content of build.xml
-             - copy/paste the content of the rules files (minus the top node)
-               into this file, replacing the <import> task.
-             - customize to your needs.
-
-         ***********************
-         ****** IMPORTANT ******
-         ***********************
-         In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
-         in order to avoid having your file be overridden by tools such as "android update project"
-    -->
-    <!-- version-tag: 1 -->
-    <import file="${sdk.dir}/tools/ant/build.xml" />
-
-</project>

+ 2 - 0
templates/android_project/jni/basic_game.c

@@ -14,6 +14,8 @@
 
 
 #include "raylib.h"
 #include "raylib.h"
 
 
+#include "android_native_app_glue.h"
+
 //----------------------------------------------------------------------------------
 //----------------------------------------------------------------------------------
 // Types and Structures Definition
 // Types and Structures Definition
 //----------------------------------------------------------------------------------
 //----------------------------------------------------------------------------------

+ 194 - 83
templates/android_project/jni/include/raylib.h

@@ -1,6 +1,6 @@
-/**********************************************************************************************
+/**********************************************************************************************
 *
 *
-*   raylib v1.7.0 
+*   raylib v1.8.0 
 *
 *
 *   A simple and easy-to-use library to learn videogames programming (www.raylib.com)
 *   A simple and easy-to-use library to learn videogames programming (www.raylib.com)
 *
 *
@@ -291,14 +291,17 @@
 #define MAGENTA    CLITERAL{ 255, 0, 255, 255 }     // Magenta
 #define MAGENTA    CLITERAL{ 255, 0, 255, 255 }     // Magenta
 #define RAYWHITE   CLITERAL{ 245, 245, 245, 255 }   // My own White (raylib logo)
 #define RAYWHITE   CLITERAL{ 245, 245, 245, 255 }   // My own White (raylib logo)
 
 
+// Shader and material limits
+#define MAX_SHADER_LOCATIONS        32      // Maximum number of predefined locations stored in shader struct
+#define MAX_MATERIAL_MAPS           12      // Maximum number of texture maps stored in shader struct
+
 //----------------------------------------------------------------------------------
 //----------------------------------------------------------------------------------
 // Structures Definition
 // Structures Definition
 //----------------------------------------------------------------------------------
 //----------------------------------------------------------------------------------
 #ifndef __cplusplus
 #ifndef __cplusplus
 // Boolean type
 // Boolean type
-    #if !defined(_STDBOOL_H)
+    #if !defined(_STDBOOL_H) || !defined(__STDBOOL_H)   // CLang uses second form
         typedef enum { false, true } bool;
         typedef enum { false, true } bool;
-        #define _STDBOOL_H
     #endif
     #endif
 #endif
 #endif
 
 
@@ -401,63 +404,46 @@ typedef struct Camera2D {
 
 
 // Bounding box type
 // Bounding box type
 typedef struct BoundingBox {
 typedef struct BoundingBox {
-    Vector3 min;            // minimum vertex box-corner
-    Vector3 max;            // maximum vertex box-corner
+    Vector3 min;            // Minimum vertex box-corner
+    Vector3 max;            // Maximum vertex box-corner
 } BoundingBox;
 } BoundingBox;
 
 
 // Vertex data definning a mesh
 // Vertex data definning a mesh
+// NOTE: Data stored in CPU memory (and GPU)
 typedef struct Mesh {
 typedef struct Mesh {
-    int vertexCount;        // number of vertices stored in arrays
-    int triangleCount;      // number of triangles stored (indexed or not)
-    float *vertices;        // vertex position (XYZ - 3 components per vertex) (shader-location = 0)
-    float *texcoords;       // vertex texture coordinates (UV - 2 components per vertex) (shader-location = 1)
-    float *texcoords2;      // vertex second texture coordinates (useful for lightmaps) (shader-location = 5)
-    float *normals;         // vertex normals (XYZ - 3 components per vertex) (shader-location = 2)
-    float *tangents;        // vertex tangents (XYZ - 3 components per vertex) (shader-location = 4)
-    unsigned char *colors;  // vertex colors (RGBA - 4 components per vertex) (shader-location = 3)
-    unsigned short *indices;// vertex indices (in case vertex data comes indexed)
+    int vertexCount;        // Number of vertices stored in arrays
+    int triangleCount;      // Number of triangles stored (indexed or not)
+
+    float *vertices;        // Vertex position (XYZ - 3 components per vertex) (shader-location = 0)
+    float *texcoords;       // Vertex texture coordinates (UV - 2 components per vertex) (shader-location = 1)
+    float *texcoords2;      // Vertex second texture coordinates (useful for lightmaps) (shader-location = 5)
+    float *normals;         // Vertex normals (XYZ - 3 components per vertex) (shader-location = 2)
+    float *tangents;        // Vertex tangents (XYZ - 3 components per vertex) (shader-location = 4)
+    unsigned char *colors;  // Vertex colors (RGBA - 4 components per vertex) (shader-location = 3)
+    unsigned short *indices;// Vertex indices (in case vertex data comes indexed)
 
 
     unsigned int vaoId;     // OpenGL Vertex Array Object id
     unsigned int vaoId;     // OpenGL Vertex Array Object id
     unsigned int vboId[7];  // OpenGL Vertex Buffer Objects id (7 types of vertex data)
     unsigned int vboId[7];  // OpenGL Vertex Buffer Objects id (7 types of vertex data)
 } Mesh;
 } Mesh;
 
 
-// Shader type (generic shader)
+// Shader type (generic)
 typedef struct Shader {
 typedef struct Shader {
-    unsigned int id;        // Shader program id
-
-    // Vertex attributes locations (default locations)
-    int vertexLoc;          // Vertex attribute location point    (default-location = 0)
-    int texcoordLoc;        // Texcoord attribute location point  (default-location = 1)
-    int texcoord2Loc;       // Texcoord2 attribute location point (default-location = 5)
-    int normalLoc;          // Normal attribute location point    (default-location = 2)
-    int tangentLoc;         // Tangent attribute location point   (default-location = 4)
-    int colorLoc;           // Color attibute location point      (default-location = 3)
-
-    // Uniform locations
-    int mvpLoc;             // ModelView-Projection matrix uniform location point (vertex shader)
-    int colDiffuseLoc;      // Diffuse color uniform location point (fragment shader)
-    int colAmbientLoc;      // Ambient color uniform location point (fragment shader)
-    int colSpecularLoc;     // Specular color uniform location point (fragment shader)
-
-    // Texture map locations (generic for any kind of map)
-    int mapTexture0Loc;     // Map texture uniform location point (default-texture-unit = 0)
-    int mapTexture1Loc;     // Map texture uniform location point (default-texture-unit = 1)
-    int mapTexture2Loc;     // Map texture uniform location point (default-texture-unit = 2)
+    unsigned int id;                // Shader program id
+    int locs[MAX_SHADER_LOCATIONS]; // Shader locations array
 } Shader;
 } Shader;
 
 
-// Material type
-typedef struct Material {
-    Shader shader;          // Standard shader (supports 3 map textures)
-
-    Texture2D texDiffuse;   // Diffuse texture  (binded to shader mapTexture0Loc)
-    Texture2D texNormal;    // Normal texture   (binded to shader mapTexture1Loc)
-    Texture2D texSpecular;  // Specular texture (binded to shader mapTexture2Loc)
-
-    Color colDiffuse;       // Diffuse color
-    Color colAmbient;       // Ambient color
-    Color colSpecular;      // Specular color
+// Material texture map
+typedef struct MaterialMap {
+    Texture2D texture;      // Material map texture
+    Color color;            // Material map color
+    float value;            // Material map value
+} MaterialMap;
 
 
-    float glossiness;       // Glossiness level (Ranges from 0 to 1000)
+// Material type (generic)
+typedef struct Material {
+    Shader shader;          // Material shader
+    MaterialMap maps[MAX_MATERIAL_MAPS]; // Material maps
+    float *params;          // Material generic parameters (if required)
 } Material;
 } Material;
 
 
 // Model type
 // Model type
@@ -473,7 +459,7 @@ typedef struct Ray {
     Vector3 direction;      // Ray direction
     Vector3 direction;      // Ray direction
 } Ray;
 } Ray;
 
 
-// Information returned from a raycast
+// Raycast hit information
 typedef struct RayHitInfo {
 typedef struct RayHitInfo {
     bool hit;               // Did the ray hit something?
     bool hit;               // Did the ray hit something?
     float distance;         // Distance to nearest hit
     float distance;         // Distance to nearest hit
@@ -534,13 +520,63 @@ typedef struct RRESData *RRES;
 //----------------------------------------------------------------------------------
 //----------------------------------------------------------------------------------
 // Trace log type
 // Trace log type
 typedef enum { 
 typedef enum { 
-    INFO = 0,
-    WARNING, 
-    ERROR, 
-    DEBUG, 
-    OTHER 
+    LOG_INFO = 0,
+    LOG_WARNING, 
+    LOG_ERROR, 
+    LOG_DEBUG, 
+    LOG_OTHER 
 } LogType;
 } LogType;
 
 
+// Shader location point type
+typedef enum {
+    LOC_VERTEX_POSITION = 0,
+    LOC_VERTEX_TEXCOORD01,
+    LOC_VERTEX_TEXCOORD02,
+    LOC_VERTEX_NORMAL,
+    LOC_VERTEX_TANGENT,
+    LOC_VERTEX_COLOR,
+    LOC_MATRIX_MVP,
+    LOC_MATRIX_MODEL,
+    LOC_MATRIX_VIEW,
+    LOC_MATRIX_PROJECTION,
+    LOC_VECTOR_VIEW,
+    LOC_COLOR_DIFFUSE,
+    LOC_COLOR_SPECULAR,
+    LOC_COLOR_AMBIENT,
+    LOC_MAP_ALBEDO,          // LOC_MAP_DIFFUSE
+    LOC_MAP_METALNESS,       // LOC_MAP_SPECULAR
+    LOC_MAP_NORMAL,
+    LOC_MAP_ROUGHNESS,
+    LOC_MAP_OCCUSION,
+    LOC_MAP_EMISSION,
+    LOC_MAP_HEIGHT,
+    LOC_MAP_CUBEMAP,
+    LOC_MAP_IRRADIANCE,
+    LOC_MAP_PREFILTER,
+    LOC_MAP_BRDF
+} ShaderLocationIndex;
+
+#define LOC_MAP_DIFFUSE      LOC_MAP_ALBEDO
+#define LOC_MAP_SPECULAR     LOC_MAP_METALNESS
+
+// Material map type
+typedef enum {
+    MAP_ALBEDO    = 0,       // MAP_DIFFUSE
+    MAP_METALNESS = 1,       // MAP_SPECULAR
+    MAP_NORMAL    = 2,
+    MAP_ROUGHNESS = 3,
+    MAP_OCCLUSION,
+    MAP_EMISSION,
+    MAP_HEIGHT,
+    MAP_CUBEMAP,             // NOTE: Uses GL_TEXTURE_CUBE_MAP
+    MAP_IRRADIANCE,          // NOTE: Uses GL_TEXTURE_CUBE_MAP
+    MAP_PREFILTER,           // NOTE: Uses GL_TEXTURE_CUBE_MAP
+    MAP_BRDF
+} TexmapIndex;
+
+#define MAP_DIFFUSE      MAP_ALBEDO
+#define MAP_SPECULAR     MAP_METALNESS
+
 // Texture formats
 // Texture formats
 // NOTE: Support depends on OpenGL version and platform
 // NOTE: Support depends on OpenGL version and platform
 typedef enum {
 typedef enum {
@@ -653,17 +689,19 @@ extern "C" {            // Prevents name mangling of functions
 //------------------------------------------------------------------------------------
 //------------------------------------------------------------------------------------
 // Window and Graphics Device Functions (Module: core)
 // Window and Graphics Device Functions (Module: core)
 //------------------------------------------------------------------------------------
 //------------------------------------------------------------------------------------
+
+// Window-related functions
 #if defined(PLATFORM_ANDROID)
 #if defined(PLATFORM_ANDROID)
 RLAPI void InitWindow(int width, int height, void *state);        // Initialize Android activity
 RLAPI void InitWindow(int width, int height, void *state);        // Initialize Android activity
 #elif defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) || defined(PLATFORM_WEB)
 #elif defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) || defined(PLATFORM_WEB)
 RLAPI void InitWindow(int width, int height, const char *title);  // Initialize window and OpenGL context
 RLAPI void InitWindow(int width, int height, const char *title);  // Initialize window and OpenGL context
 #endif
 #endif
-
 RLAPI void CloseWindow(void);                                     // Close window and unload OpenGL context
 RLAPI void CloseWindow(void);                                     // Close window and unload OpenGL context
 RLAPI bool WindowShouldClose(void);                               // Check if KEY_ESCAPE pressed or Close icon pressed
 RLAPI bool WindowShouldClose(void);                               // Check if KEY_ESCAPE pressed or Close icon pressed
 RLAPI bool IsWindowMinimized(void);                               // Check if window has been minimized (or lost focus)
 RLAPI bool IsWindowMinimized(void);                               // Check if window has been minimized (or lost focus)
 RLAPI void ToggleFullscreen(void);                                // Toggle fullscreen mode (only PLATFORM_DESKTOP)
 RLAPI void ToggleFullscreen(void);                                // Toggle fullscreen mode (only PLATFORM_DESKTOP)
 RLAPI void SetWindowIcon(Image image);                            // Set icon for window (only PLATFORM_DESKTOP)
 RLAPI void SetWindowIcon(Image image);                            // Set icon for window (only PLATFORM_DESKTOP)
+RLAPI void SetWindowTitle(const char *title);                     // Set title for window (only PLATFORM_DESKTOP)
 RLAPI void SetWindowPosition(int x, int y);                       // Set window position on screen (only PLATFORM_DESKTOP)
 RLAPI void SetWindowPosition(int x, int y);                       // Set window position on screen (only PLATFORM_DESKTOP)
 RLAPI void SetWindowMonitor(int monitor);                         // Set monitor for the current window (fullscreen mode)
 RLAPI void SetWindowMonitor(int monitor);                         // Set monitor for the current window (fullscreen mode)
 RLAPI void SetWindowMinSize(int width, int height);               // Set window minimum dimensions (for FLAG_WINDOW_RESIZABLE)
 RLAPI void SetWindowMinSize(int width, int height);               // Set window minimum dimensions (for FLAG_WINDOW_RESIZABLE)
@@ -671,6 +709,7 @@ RLAPI int GetScreenWidth(void);                                   // Get current
 RLAPI int GetScreenHeight(void);                                  // Get current screen height
 RLAPI int GetScreenHeight(void);                                  // Get current screen height
 
 
 #if !defined(PLATFORM_ANDROID)
 #if !defined(PLATFORM_ANDROID)
+// Cursor-related functions
 RLAPI void ShowCursor(void);                                      // Shows cursor
 RLAPI void ShowCursor(void);                                      // Shows cursor
 RLAPI void HideCursor(void);                                      // Hides cursor
 RLAPI void HideCursor(void);                                      // Hides cursor
 RLAPI bool IsCursorHidden(void);                                  // Check if cursor is not visible
 RLAPI bool IsCursorHidden(void);                                  // Check if cursor is not visible
@@ -678,10 +717,10 @@ RLAPI void EnableCursor(void);                                    // Enables cur
 RLAPI void DisableCursor(void);                                   // Disables cursor (lock cursor)
 RLAPI void DisableCursor(void);                                   // Disables cursor (lock cursor)
 #endif
 #endif
 
 
+// Drawing-related functions
 RLAPI void ClearBackground(Color color);                          // Set background color (framebuffer clear color)
 RLAPI void ClearBackground(Color color);                          // Set background color (framebuffer clear color)
 RLAPI void BeginDrawing(void);                                    // Setup canvas (framebuffer) to start drawing
 RLAPI void BeginDrawing(void);                                    // Setup canvas (framebuffer) to start drawing
 RLAPI void EndDrawing(void);                                      // End canvas drawing and swap buffers (double buffering)
 RLAPI void EndDrawing(void);                                      // End canvas drawing and swap buffers (double buffering)
-
 RLAPI void Begin2dMode(Camera2D camera);                          // Initialize 2D mode with custom camera (2D)
 RLAPI void Begin2dMode(Camera2D camera);                          // Initialize 2D mode with custom camera (2D)
 RLAPI void End2dMode(void);                                       // Ends 2D mode with custom camera
 RLAPI void End2dMode(void);                                       // Ends 2D mode with custom camera
 RLAPI void Begin3dMode(Camera camera);                            // Initializes 3D mode with custom camera (3D)
 RLAPI void Begin3dMode(Camera camera);                            // Initializes 3D mode with custom camera (3D)
@@ -689,29 +728,39 @@ RLAPI void End3dMode(void);                                       // Ends 3D mod
 RLAPI void BeginTextureMode(RenderTexture2D target);              // Initializes render texture for drawing
 RLAPI void BeginTextureMode(RenderTexture2D target);              // Initializes render texture for drawing
 RLAPI void EndTextureMode(void);                                  // Ends drawing to render texture
 RLAPI void EndTextureMode(void);                                  // Ends drawing to render texture
 
 
+// Screen-space-related functions
 RLAPI Ray GetMouseRay(Vector2 mousePosition, Camera camera);      // Returns a ray trace from mouse position
 RLAPI Ray GetMouseRay(Vector2 mousePosition, Camera camera);      // Returns a ray trace from mouse position
 RLAPI Vector2 GetWorldToScreen(Vector3 position, Camera camera);  // Returns the screen space position for a 3d world space position
 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)
 RLAPI Matrix GetCameraMatrix(Camera camera);                      // Returns camera transform matrix (view matrix)
 
 
+// Timming-related functions
 RLAPI void SetTargetFPS(int fps);                                 // Set target FPS (maximum)
 RLAPI void SetTargetFPS(int fps);                                 // Set target FPS (maximum)
 RLAPI int GetFPS(void);                                           // Returns current FPS
 RLAPI int GetFPS(void);                                           // Returns current FPS
 RLAPI float GetFrameTime(void);                                   // Returns time in seconds for last frame drawn
 RLAPI float GetFrameTime(void);                                   // Returns time in seconds for last frame drawn
 
 
-RLAPI Color GetColor(int hexValue);                               // Returns a Color struct from hexadecimal value
+// Color-related functions
 RLAPI int GetHexValue(Color color);                               // Returns hexadecimal value for a Color
 RLAPI int GetHexValue(Color color);                               // Returns hexadecimal value for a Color
+RLAPI Color GetColor(int hexValue);                               // Returns a Color struct from hexadecimal value
+RLAPI Color Fade(Color color, float alpha);                       // Color fade-in or fade-out, alpha goes from 0.0f to 1.0f
 RLAPI float *ColorToFloat(Color color);                           // Converts Color to float array and normalizes
 RLAPI float *ColorToFloat(Color color);                           // Converts Color to float array and normalizes
-RLAPI float *VectorToFloat(Vector3 vec);                          // Converts Vector3 to float array
-RLAPI float *MatrixToFloat(Matrix mat);                           // Converts Matrix to float array
 
 
-RLAPI int GetRandomValue(int min, int max);                       // Returns a random value between min and max (both included)
-RLAPI Color Fade(Color color, float alpha);                       // Color fade-in or fade-out, alpha goes from 0.0f to 1.0f
+// Math useful functions (available from raymath.h)
+RLAPI float *VectorToFloat(Vector3 vec);                          // Returns Vector3 as float array
+RLAPI float *MatrixToFloat(Matrix mat);                           // Returns Matrix as float array
+RLAPI Vector3 Vector3Zero(void);                                  // Vector with components value 0.0f
+RLAPI Vector3 Vector3One(void);                                   // Vector with components value 1.0f
+RLAPI Matrix MatrixIdentity(void);                                // Returns identity matrix
 
 
+// Misc. functions
 RLAPI void ShowLogo(void);                                        // Activate raylib logo at startup (can be done with flags)
 RLAPI void ShowLogo(void);                                        // Activate raylib logo at startup (can be done with flags)
 RLAPI void SetConfigFlags(char flags);                            // Setup window configuration flags (view FLAGS)
 RLAPI void SetConfigFlags(char flags);                            // Setup window configuration flags (view FLAGS)
-RLAPI void TraceLog(int logType, const char *text, ...);          // Show trace log messages (INFO, WARNING, ERROR, DEBUG)
+RLAPI void TraceLog(int logType, const char *text, ...);          // Show trace log messages (LOG_INFO, LOG_WARNING, LOG_ERROR, LOG_DEBUG)
 RLAPI void TakeScreenshot(const char *fileName);                  // Takes a screenshot of current screen (saved a .png)
 RLAPI void TakeScreenshot(const char *fileName);                  // Takes a screenshot of current screen (saved a .png)
+RLAPI int GetRandomValue(int min, int max);                       // Returns a random value between min and max (both included)
 
 
+// Files management functions
 RLAPI bool IsFileExtension(const char *fileName, const char *ext);// Check file extension
 RLAPI bool IsFileExtension(const char *fileName, const char *ext);// Check file extension
+RLAPI const char *GetExtension(const char *fileName);             // Get file extension
 RLAPI const char *GetDirectoryPath(const char *fileName);         // Get directory for a given fileName (with path)
 RLAPI const char *GetDirectoryPath(const char *fileName);         // Get directory for a given fileName (with path)
 RLAPI const char *GetWorkingDirectory(void);                      // Get current working directory
 RLAPI const char *GetWorkingDirectory(void);                      // Get current working directory
 RLAPI bool ChangeDirectory(const char *dir);                      // Change working directory, returns true if success
 RLAPI bool ChangeDirectory(const char *dir);                      // Change working directory, returns true if success
@@ -719,12 +768,15 @@ RLAPI bool IsFileDropped(void);                                   // Check if a
 RLAPI char **GetDroppedFiles(int *count);                         // Get dropped files names
 RLAPI char **GetDroppedFiles(int *count);                         // Get dropped files names
 RLAPI void ClearDroppedFiles(void);                               // Clear dropped files paths buffer
 RLAPI void ClearDroppedFiles(void);                               // Clear dropped files paths buffer
 
 
+// Persistent storage management
 RLAPI void StorageSaveValue(int position, int value);             // Save integer value to storage file (to defined position)
 RLAPI void StorageSaveValue(int position, int value);             // Save integer value to storage file (to defined position)
 RLAPI int StorageLoadValue(int position);                         // Load integer value from storage file (from defined position)
 RLAPI int StorageLoadValue(int position);                         // Load integer value from storage file (from defined position)
 
 
 //------------------------------------------------------------------------------------
 //------------------------------------------------------------------------------------
 // Input Handling Functions (Module: core)
 // Input Handling Functions (Module: core)
 //------------------------------------------------------------------------------------
 //------------------------------------------------------------------------------------
+
+// Input-related functions: keyboard
 RLAPI bool IsKeyPressed(int key);                             // Detect if a key has been pressed once
 RLAPI bool IsKeyPressed(int key);                             // Detect if a key has been pressed once
 RLAPI bool IsKeyDown(int key);                                // Detect if a key is being pressed
 RLAPI bool IsKeyDown(int key);                                // Detect if a key is being pressed
 RLAPI bool IsKeyReleased(int key);                            // Detect if a key has been released once
 RLAPI bool IsKeyReleased(int key);                            // Detect if a key has been released once
@@ -732,6 +784,7 @@ RLAPI bool IsKeyUp(int key);                                  // Detect if a key
 RLAPI int GetKeyPressed(void);                                // Get latest key pressed
 RLAPI int GetKeyPressed(void);                                // Get latest key pressed
 RLAPI void SetExitKey(int key);                               // Set a custom key to exit program (default is ESC)
 RLAPI void SetExitKey(int key);                               // Set a custom key to exit program (default is ESC)
 
 
+// Input-related functions: gamepads
 RLAPI bool IsGamepadAvailable(int gamepad);                   // Detect if a gamepad is available
 RLAPI bool IsGamepadAvailable(int gamepad);                   // Detect if a gamepad is available
 RLAPI bool IsGamepadName(int gamepad, const char *name);      // Check gamepad name (if available)
 RLAPI bool IsGamepadName(int gamepad, const char *name);      // Check gamepad name (if available)
 RLAPI const char *GetGamepadName(int gamepad);                // Return gamepad internal name id
 RLAPI const char *GetGamepadName(int gamepad);                // Return gamepad internal name id
@@ -743,6 +796,7 @@ RLAPI int GetGamepadButtonPressed(void);                      // Get the last ga
 RLAPI int GetGamepadAxisCount(int gamepad);                   // Return gamepad axis count for a gamepad
 RLAPI int GetGamepadAxisCount(int gamepad);                   // Return gamepad axis count for a gamepad
 RLAPI float GetGamepadAxisMovement(int gamepad, int axis);    // Return axis movement value for a gamepad axis
 RLAPI float GetGamepadAxisMovement(int gamepad, int axis);    // Return axis movement value for a gamepad axis
 
 
+// Input-related functions: mouse
 RLAPI bool IsMouseButtonPressed(int button);                  // Detect if a mouse button has been pressed once
 RLAPI bool IsMouseButtonPressed(int button);                  // Detect if a mouse button has been pressed once
 RLAPI bool IsMouseButtonDown(int button);                     // Detect if a mouse button is being pressed
 RLAPI bool IsMouseButtonDown(int button);                     // Detect if a mouse button is being pressed
 RLAPI bool IsMouseButtonReleased(int button);                 // Detect if a mouse button has been released once
 RLAPI bool IsMouseButtonReleased(int button);                 // Detect if a mouse button has been released once
@@ -753,6 +807,7 @@ RLAPI Vector2 GetMousePosition(void);                         // Returns mouse p
 RLAPI void SetMousePosition(Vector2 position);                // Set mouse position XY
 RLAPI void SetMousePosition(Vector2 position);                // Set mouse position XY
 RLAPI int GetMouseWheelMove(void);                            // Returns mouse wheel movement Y
 RLAPI int GetMouseWheelMove(void);                            // Returns mouse wheel movement Y
 
 
+// Input-related functions: touch
 RLAPI int GetTouchX(void);                                    // Returns touch position X for touch point 0 (relative to screen size)
 RLAPI int GetTouchX(void);                                    // Returns touch position X for touch point 0 (relative to screen size)
 RLAPI int GetTouchY(void);                                    // Returns touch position Y for touch point 0 (relative to screen size)
 RLAPI int GetTouchY(void);                                    // Returns touch position Y for touch point 0 (relative to screen size)
 RLAPI Vector2 GetTouchPosition(int index);                    // Returns touch position XY for a touch point index (relative to screen size)
 RLAPI Vector2 GetTouchPosition(int index);                    // Returns touch position XY for a touch point index (relative to screen size)
@@ -786,6 +841,8 @@ RLAPI void SetCameraMoveControls(int frontKey, int backKey,
 //------------------------------------------------------------------------------------
 //------------------------------------------------------------------------------------
 // Basic Shapes Drawing Functions (Module: shapes)
 // Basic Shapes Drawing Functions (Module: shapes)
 //------------------------------------------------------------------------------------
 //------------------------------------------------------------------------------------
+
+// Basic shapes drawing functions
 RLAPI void DrawPixel(int posX, int posY, Color color);                                                   // Draw a pixel
 RLAPI void DrawPixel(int posX, int posY, Color color);                                                   // Draw a pixel
 RLAPI void DrawPixelV(Vector2 position, Color color);                                                    // Draw a pixel (Vector version)
 RLAPI void DrawPixelV(Vector2 position, Color color);                                                    // Draw a pixel (Vector version)
 RLAPI void DrawLine(int startPosX, int startPosY, int endPosX, int endPosY, Color color);                // Draw a line
 RLAPI void DrawLine(int startPosX, int startPosY, int endPosX, int endPosY, Color color);                // Draw a line
@@ -800,14 +857,17 @@ RLAPI void DrawRectangle(int posX, int posY, int width, int height, Color color)
 RLAPI void DrawRectangleRec(Rectangle rec, Color color);                                                 // Draw a color-filled rectangle
 RLAPI void DrawRectangleRec(Rectangle rec, Color color);                                                 // Draw a color-filled rectangle
 RLAPI void DrawRectanglePro(Rectangle rec, Vector2 origin, float rotation, Color color);                 // Draw a color-filled rectangle with pro parameters
 RLAPI void DrawRectanglePro(Rectangle rec, Vector2 origin, float rotation, Color color);                 // Draw a color-filled rectangle with pro parameters
 RLAPI void DrawRectangleGradient(int posX, int posY, int width, int height, Color color1, Color color2); // Draw a gradient-filled rectangle
 RLAPI void DrawRectangleGradient(int posX, int posY, int width, int height, Color color1, Color color2); // Draw a gradient-filled rectangle
+RLAPI void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3, Color col4);       // Draw a gradient-filled rectangle with custom vertex colors
 RLAPI void DrawRectangleV(Vector2 position, Vector2 size, Color color);                                  // Draw a color-filled rectangle (Vector version)
 RLAPI void DrawRectangleV(Vector2 position, Vector2 size, Color color);                                  // Draw a color-filled rectangle (Vector version)
 RLAPI void DrawRectangleLines(int posX, int posY, int width, int height, Color color);                   // Draw rectangle outline
 RLAPI void DrawRectangleLines(int posX, int posY, int width, int height, Color color);                   // Draw rectangle outline
+RLAPI void DrawRectangleT(int posX, int posY, int width, int height, Color color);                       // Draw rectangle using text character
 RLAPI void DrawTriangle(Vector2 v1, Vector2 v2, Vector2 v3, Color color);                                // Draw a color-filled triangle
 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 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)
 RLAPI void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color color);               // Draw a regular polygon (Vector version)
 RLAPI void DrawPolyEx(Vector2 *points, int numPoints, Color color);                                      // Draw a closed polygon defined by points
 RLAPI void DrawPolyEx(Vector2 *points, int numPoints, Color color);                                      // Draw a closed polygon defined by points
 RLAPI void DrawPolyExLines(Vector2 *points, int numPoints, Color color);                                 // Draw polygon lines
 RLAPI void DrawPolyExLines(Vector2 *points, int numPoints, Color color);                                 // Draw polygon lines
 
 
+// Basic shapes collision detection functions
 RLAPI bool CheckCollisionRecs(Rectangle rec1, Rectangle rec2);                                           // Check collision between two rectangles
 RLAPI bool CheckCollisionRecs(Rectangle rec1, Rectangle rec2);                                           // Check collision between two rectangles
 RLAPI bool CheckCollisionCircles(Vector2 center1, float radius1, Vector2 center2, float radius2);        // Check collision between two circles
 RLAPI bool CheckCollisionCircles(Vector2 center1, float radius1, Vector2 center2, float radius2);        // Check collision between two circles
 RLAPI bool CheckCollisionCircleRec(Vector2 center, float radius, Rectangle rec);                         // Check collision between circle and rectangle
 RLAPI bool CheckCollisionCircleRec(Vector2 center, float radius, Rectangle rec);                         // Check collision between circle and rectangle
@@ -819,6 +879,8 @@ RLAPI bool CheckCollisionPointTriangle(Vector2 point, Vector2 p1, Vector2 p2, Ve
 //------------------------------------------------------------------------------------
 //------------------------------------------------------------------------------------
 // Texture Loading and Drawing Functions (Module: textures)
 // Texture Loading and Drawing Functions (Module: textures)
 //------------------------------------------------------------------------------------
 //------------------------------------------------------------------------------------
+
+// Image/Texture2D data loading/unloading/saving functions
 RLAPI Image LoadImage(const char *fileName);                                                             // Load image from file into CPU memory (RAM)
 RLAPI Image LoadImage(const char *fileName);                                                             // Load image from file into CPU memory (RAM)
 RLAPI Image LoadImageEx(Color *pixels, int width, int height);                                           // Load image from Color array data (RGBA - 32bit)
 RLAPI Image LoadImageEx(Color *pixels, int width, int height);                                           // Load image from Color array data (RGBA - 32bit)
 RLAPI Image LoadImagePro(void *data, int width, int height, int format);                                 // Load image from raw data with parameters
 RLAPI Image LoadImagePro(void *data, int width, int height, int format);                                 // Load image from raw data with parameters
@@ -832,6 +894,9 @@ RLAPI void UnloadRenderTexture(RenderTexture2D target);
 RLAPI Color *GetImageData(Image image);                                                                  // Get pixel data from image as a Color struct array
 RLAPI Color *GetImageData(Image image);                                                                  // Get pixel data from image as a Color struct array
 RLAPI Image GetTextureData(Texture2D texture);                                                           // Get pixel data from GPU texture and return an Image
 RLAPI Image GetTextureData(Texture2D texture);                                                           // Get pixel data from GPU texture and return an Image
 RLAPI void UpdateTexture(Texture2D texture, const void *pixels);                                         // Update GPU texture with new data
 RLAPI void UpdateTexture(Texture2D texture, const void *pixels);                                         // Update GPU texture with new data
+RLAPI void SaveImageAs(const char *fileName, Image image);                                               // Save image to a PNG file
+
+// Image manipulation functions
 RLAPI void ImageToPOT(Image *image, Color fillColor);                                                    // Convert image to POT (power-of-two)
 RLAPI void ImageToPOT(Image *image, Color fillColor);                                                    // Convert image to POT (power-of-two)
 RLAPI void ImageFormat(Image *image, int newFormat);                                                     // Convert image data to desired format
 RLAPI void ImageFormat(Image *image, int newFormat);                                                     // Convert image data to desired format
 RLAPI void ImageAlphaMask(Image *image, Image alphaMask);                                                // Apply alpha mask to image
 RLAPI void ImageAlphaMask(Image *image, Image alphaMask);                                                // Apply alpha mask to image
@@ -853,10 +918,22 @@ RLAPI void ImageColorInvert(Image *image);
 RLAPI void ImageColorGrayscale(Image *image);                                                            // Modify image color: grayscale
 RLAPI void ImageColorGrayscale(Image *image);                                                            // Modify image color: grayscale
 RLAPI void ImageColorContrast(Image *image, float contrast);                                             // Modify image color: contrast (-100 to 100)
 RLAPI void ImageColorContrast(Image *image, float contrast);                                             // Modify image color: contrast (-100 to 100)
 RLAPI void ImageColorBrightness(Image *image, int brightness);                                           // Modify image color: brightness (-255 to 255)
 RLAPI void ImageColorBrightness(Image *image, int brightness);                                           // Modify image color: brightness (-255 to 255)
+
+// Image generation functions
+RLAPI Image GenImageGradientV(int width, int height, Color top, Color bottom);                           // Generate image: vertical gradient
+RLAPI Image GenImageGradientH(int width, int height, Color left, Color right);                           // Generate image: horizontal gradient
+RLAPI Image GenImageGradientRadial(int width, int height, float density, Color inner, Color outer);      // Generate image: radial gradient
+RLAPI Image GenImageChecked(int width, int height, int checksX, int checksY, Color col1, Color col2);    // Generate image: checked
+RLAPI Image GenImageWhiteNoise(int width, int height, float factor);                                     // Generate image: white noise
+RLAPI Image GenImagePerlinNoise(int width, int height, float scale);                                     // Generate image: perlin noise
+RLAPI Image GenImageCellular(int width, int height, int tileSize);                                       // Generate image: cellular algorithm. Bigger tileSize means bigger cells
+
+// Texture2D configuration functions
 RLAPI void GenTextureMipmaps(Texture2D *texture);                                                        // Generate GPU mipmaps for a texture
 RLAPI void GenTextureMipmaps(Texture2D *texture);                                                        // Generate GPU mipmaps for a texture
 RLAPI void SetTextureFilter(Texture2D texture, int filterMode);                                          // Set texture scaling filter mode
 RLAPI void SetTextureFilter(Texture2D texture, int filterMode);                                          // Set texture scaling filter mode
 RLAPI void SetTextureWrap(Texture2D texture, int wrapMode);                                              // Set texture wrapping mode
 RLAPI void SetTextureWrap(Texture2D texture, int wrapMode);                                              // Set texture wrapping mode
 
 
+// Texture2D drawing functions
 RLAPI void DrawTexture(Texture2D texture, int posX, int posY, Color tint);                               // Draw a Texture2D
 RLAPI void DrawTexture(Texture2D texture, int posX, int posY, Color tint);                               // Draw a Texture2D
 RLAPI void DrawTextureV(Texture2D texture, Vector2 position, Color tint);                                // Draw a Texture2D with position defined as Vector2
 RLAPI void DrawTextureV(Texture2D texture, Vector2 position, Color tint);                                // Draw a Texture2D with position defined as Vector2
 RLAPI void DrawTextureEx(Texture2D texture, Vector2 position, float rotation, float scale, Color tint);  // Draw a Texture2D with extended parameters
 RLAPI void DrawTextureEx(Texture2D texture, Vector2 position, float rotation, float scale, Color tint);  // Draw a Texture2D with extended parameters
@@ -867,24 +944,30 @@ RLAPI void DrawTexturePro(Texture2D texture, Rectangle sourceRec, Rectangle dest
 //------------------------------------------------------------------------------------
 //------------------------------------------------------------------------------------
 // Font Loading and Text Drawing Functions (Module: text)
 // Font Loading and Text Drawing Functions (Module: text)
 //------------------------------------------------------------------------------------
 //------------------------------------------------------------------------------------
+
+// SpriteFont loading/unloading functions
 RLAPI SpriteFont GetDefaultFont(void);                                                                   // Get the default SpriteFont
 RLAPI SpriteFont GetDefaultFont(void);                                                                   // Get the default SpriteFont
 RLAPI SpriteFont LoadSpriteFont(const char *fileName);                                                   // Load SpriteFont from file into GPU memory (VRAM)
 RLAPI SpriteFont LoadSpriteFont(const char *fileName);                                                   // Load SpriteFont from file into GPU memory (VRAM)
 RLAPI SpriteFont LoadSpriteFontEx(const char *fileName, int fontSize, int charsCount, int *fontChars);   // Load SpriteFont from file with extended parameters
 RLAPI SpriteFont LoadSpriteFontEx(const char *fileName, int fontSize, int charsCount, int *fontChars);   // Load SpriteFont from file with extended parameters
 RLAPI void UnloadSpriteFont(SpriteFont spriteFont);                                                      // Unload SpriteFont from GPU memory (VRAM)
 RLAPI void UnloadSpriteFont(SpriteFont spriteFont);                                                      // Unload SpriteFont from GPU memory (VRAM)
 
 
+// Text drawing functions
+RLAPI void DrawFPS(int posX, int posY);                                                                  // Shows current FPS
 RLAPI void DrawText(const char *text, int posX, int posY, int fontSize, Color color);                    // Draw text (using default font)
 RLAPI void DrawText(const char *text, int posX, int posY, int fontSize, Color color);                    // Draw text (using default font)
 RLAPI void DrawTextEx(SpriteFont spriteFont, const char* text, Vector2 position,                         // Draw text using SpriteFont and additional parameters
 RLAPI void DrawTextEx(SpriteFont spriteFont, const char* text, Vector2 position,                         // Draw text using SpriteFont and additional parameters
                 float fontSize, int spacing, Color tint);
                 float fontSize, int spacing, Color tint);
+
+// Text misc. functions
 RLAPI int MeasureText(const char *text, int fontSize);                                                   // Measure string width for default font
 RLAPI int MeasureText(const char *text, int fontSize);                                                   // Measure string width for default font
 RLAPI Vector2 MeasureTextEx(SpriteFont spriteFont, const char *text, float fontSize, int spacing);       // Measure string size for SpriteFont
 RLAPI Vector2 MeasureTextEx(SpriteFont spriteFont, const char *text, float fontSize, int spacing);       // Measure string size for SpriteFont
-
-RLAPI void DrawFPS(int posX, int posY);                                                                  // Shows current FPS
 RLAPI const char *FormatText(const char *text, ...);                                                     // Formatting of text with variables to 'embed'
 RLAPI const char *FormatText(const char *text, ...);                                                     // Formatting of text with variables to 'embed'
 RLAPI const char *SubText(const char *text, int position, int length);                                   // Get a piece of a text string
 RLAPI const char *SubText(const char *text, int position, int length);                                   // Get a piece of a text string
 
 
 //------------------------------------------------------------------------------------
 //------------------------------------------------------------------------------------
 // Basic 3d Shapes Drawing Functions (Module: models)
 // Basic 3d Shapes Drawing Functions (Module: models)
 //------------------------------------------------------------------------------------
 //------------------------------------------------------------------------------------
+
+// Basic geometric 3D shapes drawing functions
 RLAPI void DrawLine3D(Vector3 startPos, Vector3 endPos, Color color);                                    // Draw a line in 3D world space
 RLAPI void DrawLine3D(Vector3 startPos, Vector3 endPos, Color color);                                    // Draw a line in 3D world space
 RLAPI void DrawCircle3D(Vector3 center, float radius, Vector3 rotationAxis, float rotationAngle, Color color); // Draw a circle in 3D world space
 RLAPI void DrawCircle3D(Vector3 center, float radius, Vector3 rotationAxis, float rotationAngle, Color color); // Draw a circle in 3D world space
 RLAPI void DrawCube(Vector3 position, float width, float height, float length, Color color);             // Draw cube
 RLAPI void DrawCube(Vector3 position, float width, float height, float length, Color color);             // Draw cube
@@ -905,19 +988,33 @@ RLAPI void DrawGizmo(Vector3 position);
 //------------------------------------------------------------------------------------
 //------------------------------------------------------------------------------------
 // Model 3d Loading and Drawing Functions (Module: models)
 // Model 3d Loading and Drawing Functions (Module: models)
 //------------------------------------------------------------------------------------
 //------------------------------------------------------------------------------------
+
+// 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 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 LoadMesh(const char *fileName);                                                              // Load mesh from file
-RLAPI Mesh LoadMeshEx(int numVertex, float *vData, float *vtData, float *vnData, Color *cData);         // Load mesh from vertex data
-RLAPI Model LoadModel(const char *fileName);                                                            // Load model from file
-RLAPI Model LoadModelFromMesh(Mesh data, bool dynamic);                                                 // Load model from mesh data
-RLAPI Model LoadHeightmap(Image heightmap, Vector3 size);                                               // Load heightmap model from image data
-RLAPI Model LoadCubicmap(Image cubicmap);                                                               // Load cubes-based map model from image data
 RLAPI void UnloadMesh(Mesh *mesh);                                                                      // Unload mesh from memory (RAM and/or VRAM)
 RLAPI void UnloadMesh(Mesh *mesh);                                                                      // Unload mesh from memory (RAM and/or VRAM)
-RLAPI void UnloadModel(Model model);                                                                    // Unload model from memory (RAM and/or VRAM)
 
 
+// Mesh generation functions
+RLAPI Mesh GenMeshPlane(float width, float length, int resX, int resZ);                                 // Generate plane mesh (with subdivisions)
+RLAPI Mesh GenMeshCube(float width, float height, float length);                                        // Generate cuboid mesh
+RLAPI Mesh GenMeshSphere(float radius, int rings, int slices);                                          // Generate sphere mesh (standard sphere)
+RLAPI Mesh GenMeshHemiSphere(float radius, int rings, int slices);                                      // Generate half-sphere mesh (no bottom cap)
+RLAPI Mesh GenMeshCylinder(float radius, float height, int slices);                                     // Generate cylinder mesh
+RLAPI Mesh GenMeshTorus(float radius, float size, int radSeg, int sides);                               // Generate torus mesh
+RLAPI Mesh GenMeshKnot(float radius, float size, int radSeg, int sides);                                // Generate trefoil knot mesh
+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 LoadMaterial(const char *fileName);                                                      // Load material from file
-RLAPI Material LoadDefaultMaterial(void);                                                               // Load default material (uses default models shader)
+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 UnloadMaterial(Material material);                                                           // Unload material from GPU memory (VRAM)
 
 
+// Model drawing functions
 RLAPI void DrawModel(Model model, Vector3 position, float scale, Color tint);                           // Draw a model (with texture if set)
 RLAPI void DrawModel(Model model, Vector3 position, float scale, Color tint);                           // Draw a model (with texture if set)
 RLAPI void DrawModelEx(Model model, Vector3 position, Vector3 rotationAxis,
 RLAPI void DrawModelEx(Model model, Vector3 position, Vector3 rotationAxis,
                        float rotationAngle, Vector3 scale, Color tint);                                 // Draw a model with extended parameters
                        float rotationAngle, Vector3 scale, Color tint);                                 // Draw a model with extended parameters
@@ -925,11 +1022,11 @@ RLAPI void DrawModelWires(Model model, Vector3 position, float scale, Color tint
 RLAPI void DrawModelWiresEx(Model model, Vector3 position, Vector3 rotationAxis,
 RLAPI void DrawModelWiresEx(Model model, Vector3 position, Vector3 rotationAxis,
                             float rotationAngle, Vector3 scale, Color tint);                            // Draw a model wires (with texture if set) with extended parameters
                             float rotationAngle, Vector3 scale, Color tint);                            // Draw a model wires (with texture if set) with extended parameters
 RLAPI void DrawBoundingBox(BoundingBox box, Color color);                                               // Draw bounding box (wires)
 RLAPI void DrawBoundingBox(BoundingBox box, Color color);                                               // Draw bounding box (wires)
-
 RLAPI void DrawBillboard(Camera camera, Texture2D texture, Vector3 center, float size, Color tint);     // Draw a billboard texture
 RLAPI void DrawBillboard(Camera camera, Texture2D texture, Vector3 center, float size, Color tint);     // Draw a billboard texture
 RLAPI void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle sourceRec,
 RLAPI void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle sourceRec,
                             Vector3 center, float size, Color tint);                                    // Draw a billboard texture defined by sourceRec
                             Vector3 center, float size, Color tint);                                    // Draw a billboard texture defined by sourceRec
 
 
+// Collision detection functions
 RLAPI BoundingBox CalculateBoundingBox(Mesh mesh);                                                      // Calculate mesh bounding box limits
 RLAPI BoundingBox CalculateBoundingBox(Mesh mesh);                                                      // Calculate mesh bounding box limits
 RLAPI bool CheckCollisionSpheres(Vector3 centerA, float radiusA, Vector3 centerB, float radiusB);       // Detect collision between two spheres
 RLAPI bool CheckCollisionSpheres(Vector3 centerA, float radiusA, Vector3 centerB, float radiusB);       // Detect collision between two spheres
 RLAPI bool CheckCollisionBoxes(BoundingBox box1, BoundingBox box2);                                     // Detect collision between two bounding boxes
 RLAPI bool CheckCollisionBoxes(BoundingBox box1, BoundingBox box2);                                     // Detect collision between two bounding boxes
@@ -946,46 +1043,56 @@ RLAPI RayHitInfo GetCollisionRayGround(Ray ray, float groundHeight);
 // Shaders System Functions (Module: rlgl)
 // Shaders System Functions (Module: rlgl)
 // NOTE: This functions are useless when using OpenGL 1.1
 // NOTE: This functions are useless when using OpenGL 1.1
 //------------------------------------------------------------------------------------
 //------------------------------------------------------------------------------------
+
+// Shader loading/unloading functions
 RLAPI char *LoadText(const char *fileName);                               // Load chars array from text file
 RLAPI char *LoadText(const char *fileName);                               // Load chars array from text file
 RLAPI Shader LoadShader(char *vsFileName, char *fsFileName);              // Load shader from files and bind default locations
 RLAPI Shader LoadShader(char *vsFileName, char *fsFileName);              // Load shader from files and bind default locations
 RLAPI void UnloadShader(Shader shader);                                   // Unload shader from GPU memory (VRAM)
 RLAPI void UnloadShader(Shader shader);                                   // Unload shader from GPU memory (VRAM)
 
 
-RLAPI Shader GetDefaultShader(void);                                      // Get default shader
-RLAPI Texture2D GetDefaultTexture(void);                                  // Get default texture
+RLAPI Shader GetShaderDefault(void);                                      // Get default shader
+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, float *value, int size); // Set shader uniform value (float)
 RLAPI void SetShaderValue(Shader shader, int uniformLoc, float *value, int size); // Set shader uniform value (float)
 RLAPI void SetShaderValuei(Shader shader, int uniformLoc, int *value, int size);  // Set shader uniform value (int)
 RLAPI void SetShaderValuei(Shader shader, int uniformLoc, int *value, int size);  // Set shader uniform value (int)
 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 SetMatrixProjection(Matrix proj);                              // Set a custom projection matrix (replaces internal projection matrix)
 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 void SetMatrixModelview(Matrix view);                               // Set a custom modelview matrix (replaces internal modelview matrix)
 
 
+// Texture maps generation (PBR)
+// NOTE: Required shaders should be provided
+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, Texture2D cubemap, int size);         // Generate BRDF texture using cubemap data
+
+// Shading begin/end functions
 RLAPI void BeginShaderMode(Shader shader);                                // Begin custom shader drawing
 RLAPI void BeginShaderMode(Shader shader);                                // Begin custom shader drawing
 RLAPI void EndShaderMode(void);                                           // End custom shader drawing (use default shader)
 RLAPI void EndShaderMode(void);                                           // End custom shader drawing (use default shader)
 RLAPI void BeginBlendMode(int mode);                                      // Begin blending mode (alpha, additive, multiplied)
 RLAPI void BeginBlendMode(int mode);                                      // Begin blending mode (alpha, additive, multiplied)
 RLAPI void EndBlendMode(void);                                            // End blending mode (reset to default: alpha blending)
 RLAPI void EndBlendMode(void);                                            // End blending mode (reset to default: alpha blending)
 
 
-//------------------------------------------------------------------------------------
-// VR experience Functions (Module: rlgl)
-// NOTE: This functions are useless when using OpenGL 1.1
-//------------------------------------------------------------------------------------
+// VR control functions
 RLAPI void InitVrSimulator(int vrDevice);           // Init VR simulator for selected device
 RLAPI void InitVrSimulator(int vrDevice);           // Init VR simulator for selected device
 RLAPI void CloseVrSimulator(void);                  // Close VR simulator for current device
 RLAPI void CloseVrSimulator(void);                  // Close VR simulator for current device
-RLAPI bool IsVrSimulatorReady(void);                // Detect if VR device is ready
+RLAPI bool IsVrSimulatorReady(void);                // Detect if VR simulator is ready
 RLAPI void UpdateVrTracking(Camera *camera);        // Update VR tracking (position and orientation) and camera
 RLAPI void UpdateVrTracking(Camera *camera);        // Update VR tracking (position and orientation) and camera
-RLAPI void ToggleVrMode(void);                      // Enable/Disable VR experience (device or simulator)
+RLAPI void ToggleVrMode(void);                      // Enable/Disable VR experience
 RLAPI void BeginVrDrawing(void);                    // Begin VR simulator stereo rendering
 RLAPI void BeginVrDrawing(void);                    // Begin VR simulator stereo rendering
 RLAPI void EndVrDrawing(void);                      // End VR simulator stereo rendering
 RLAPI void EndVrDrawing(void);                      // End VR simulator stereo rendering
 
 
 //------------------------------------------------------------------------------------
 //------------------------------------------------------------------------------------
 // Audio Loading and Playing Functions (Module: audio)
 // Audio Loading and Playing Functions (Module: audio)
 //------------------------------------------------------------------------------------
 //------------------------------------------------------------------------------------
+
+// Audio device management functions
 RLAPI void InitAudioDevice(void);                                     // Initialize audio device and context
 RLAPI void InitAudioDevice(void);                                     // Initialize audio device and context
 RLAPI void CloseAudioDevice(void);                                    // Close the audio device and context
 RLAPI void CloseAudioDevice(void);                                    // Close the audio device and context
 RLAPI bool IsAudioDeviceReady(void);                                  // Check if audio device has been initialized successfully
 RLAPI bool IsAudioDeviceReady(void);                                  // Check if audio device has been initialized successfully
 RLAPI void SetMasterVolume(float volume);                             // Set master volume (listener)
 RLAPI void SetMasterVolume(float volume);                             // Set master volume (listener)
 
 
+// Wave/Sound loading/unloading functions
 RLAPI Wave LoadWave(const char *fileName);                            // Load wave data from file
 RLAPI Wave LoadWave(const char *fileName);                            // Load wave data from file
 RLAPI Wave LoadWaveEx(void *data, int sampleCount, int sampleRate, int sampleSize, int channels); // Load wave data from raw array data
 RLAPI Wave LoadWaveEx(void *data, int sampleCount, int sampleRate, int sampleSize, int channels); // Load wave data from raw array data
 RLAPI Sound LoadSound(const char *fileName);                          // Load sound from file
 RLAPI Sound LoadSound(const char *fileName);                          // Load sound from file
@@ -993,6 +1100,8 @@ RLAPI Sound LoadSoundFromWave(Wave wave);                             // Load so
 RLAPI void UpdateSound(Sound sound, const void *data, int samplesCount);// Update sound buffer with new data
 RLAPI void UpdateSound(Sound sound, const void *data, int samplesCount);// Update sound buffer with new data
 RLAPI void UnloadWave(Wave wave);                                     // Unload wave data
 RLAPI void UnloadWave(Wave wave);                                     // Unload wave data
 RLAPI void UnloadSound(Sound sound);                                  // Unload sound
 RLAPI void UnloadSound(Sound sound);                                  // Unload sound
+
+// Wave/Sound management functions
 RLAPI void PlaySound(Sound sound);                                    // Play a sound
 RLAPI void PlaySound(Sound sound);                                    // Play a sound
 RLAPI void PauseSound(Sound sound);                                   // Pause a sound
 RLAPI void PauseSound(Sound sound);                                   // Pause a sound
 RLAPI void ResumeSound(Sound sound);                                  // Resume a paused sound
 RLAPI void ResumeSound(Sound sound);                                  // Resume a paused sound
@@ -1004,6 +1113,8 @@ RLAPI void WaveFormat(Wave *wave, int sampleRate, int sampleSize, int channels);
 RLAPI Wave WaveCopy(Wave wave);                                       // Copy a wave to a new wave
 RLAPI Wave WaveCopy(Wave wave);                                       // Copy a wave to a new wave
 RLAPI void WaveCrop(Wave *wave, int initSample, int finalSample);     // Crop a wave to defined samples range
 RLAPI void WaveCrop(Wave *wave, int initSample, int finalSample);     // Crop a wave to defined samples range
 RLAPI float *GetWaveData(Wave wave);                                  // Get samples data from wave as a floats array
 RLAPI float *GetWaveData(Wave wave);                                  // Get samples data from wave as a floats array
+
+// Music management functions
 RLAPI Music LoadMusicStream(const char *fileName);                    // Load music stream from file
 RLAPI Music LoadMusicStream(const char *fileName);                    // Load music stream from file
 RLAPI void UnloadMusicStream(Music music);                            // Unload music stream
 RLAPI void UnloadMusicStream(Music music);                            // Unload music stream
 RLAPI void PlayMusicStream(Music music);                              // Start music playing
 RLAPI void PlayMusicStream(Music music);                              // Start music playing
@@ -1018,8 +1129,8 @@ RLAPI void SetMusicLoopCount(Music music, float count);               // Set mus
 RLAPI float GetMusicTimeLength(Music music);                          // Get music time length (in seconds)
 RLAPI float GetMusicTimeLength(Music music);                          // Get music time length (in seconds)
 RLAPI float GetMusicTimePlayed(Music music);                          // Get current music time played (in seconds)
 RLAPI float GetMusicTimePlayed(Music music);                          // Get current music time played (in seconds)
 
 
-RLAPI AudioStream InitAudioStream(unsigned int sampleRate,
-                                  unsigned int sampleSize,
+// AudioStream management functions
+RLAPI AudioStream InitAudioStream(unsigned int sampleRate, unsigned int sampleSize,
                                   unsigned int channels);             // Init audio stream (to stream raw audio pcm data)
                                   unsigned int channels);             // Init audio stream (to stream raw audio pcm data)
 RLAPI void UpdateAudioStream(AudioStream stream, const void *data, int samplesCount); // Update audio stream buffers with data
 RLAPI void UpdateAudioStream(AudioStream stream, const void *data, int samplesCount); // Update audio stream buffers with data
 RLAPI void CloseAudioStream(AudioStream stream);                      // Close audio stream and free memory
 RLAPI void CloseAudioStream(AudioStream stream);                      // Close audio stream and free memory

BIN
templates/android_project/jni/libs/libraylib.a


+ 0 - 14
templates/android_project/project.properties

@@ -1,14 +0,0 @@
-# This file is automatically generated by Android Tools.
-# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
-#
-# This file must be checked in Version Control Systems.
-#
-# To customize properties used by the Ant build system edit
-# "ant.properties", and override values to adapt the script to your
-# project structure.
-#
-# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
-#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
-
-# Project target.
-target=android-19