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
 CTestTestfile.cmake
 build
+!templates/android_project/Makefile
 
 # Unignore These makefiles...
 !examples/CMakeLists.txt

+ 84 - 3
CHANGELOG

@@ -1,7 +1,88 @@
 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)
@@ -36,14 +117,14 @@ other changes:
 [rlgl] Removed function: CreateLight(), 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:  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: IsVrSimulator(), removed VR device render, using simulator
 [rlgl] Added function: InitVrSimulator(), init VR simulator for selected device
 [rlgl] Added function: CloseVrSimulator(), close VR simulator for current device
 [rlgl] Added function: IsVrSimulatorReady(), detect if VR device is ready
 [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] Removed internal lighting system and standard shader, moved to 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? 
 	- 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?
 	- 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 
-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
 ------------------

+ 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!
 
 **raylib 1.x**
- - [ ] Improved Materials system with PBR support
  - [ ] Basic GPU stats sytem (memory, draws, time...)
  - [ ] 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
+ 
+**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**
  - [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',
 		meson_version: '>= 0.39.1',
 		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
 
 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_ARCH ?= ARM
@@ -217,6 +212,10 @@ ifeq ($(PLATFORM),PLATFORM_WEB)
     # -s USE_PTHREADS=1          # multithreading support
 endif
 
+ifeq ($(PLATFORM),PLATFORM_ANDROID)
+    CFLAGS += -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16
+endif
+
 #CFLAGSEXTRA = -Wextra -Wmissing-prototypes -Wstrict-prototypes
 
 # 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_OGG
 #define SUPPORT_FILEFORMAT_XM
+#define SUPPORT_FILEFORMAT_MOD
 //-------------------------------------------------
 
 #if defined(AUDIO_STANDALONE)

+ 18 - 0
src/core.c

@@ -659,6 +659,14 @@ void SetWindowIcon(Image image)
 #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)
 void SetWindowPosition(int x, int y)
 {
@@ -1152,6 +1160,16 @@ bool IsFileExtension(const char *fileName, const char *ext)
     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)
 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,
                   dependencies : [ glfw_dep, gl_dep, openal_dep, m_dep, x11_dep],
                   install : true,
-                  version : '1.7.0')
+                  version : '1.8.0')
 

+ 398 - 1
src/models.c

@@ -10,6 +10,10 @@
 *   #define SUPPORT_FILEFORMAT_MTL
 *       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
 *
@@ -36,6 +40,7 @@
 //-------------------------------------------------
 #define SUPPORT_FILEFORMAT_OBJ
 #define SUPPORT_FILEFORMAT_MTL
+#define SUPPORT_MESH_GENERATION
 //-------------------------------------------------
 
 #include "raylib.h"
@@ -51,6 +56,9 @@
 
 #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
 //----------------------------------------------------------------------------------
@@ -644,12 +652,142 @@ void UnloadMesh(Mesh *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
-// NOTE: Vertex data is uploaded to GPU
 Mesh GenMeshCube(float width, float height, float length)
 {
     Mesh mesh = { 0 };
 
+#define CUSTOM_MESH_GEN_CUBE
+#if defined(CUSTOM_MESH_GEN_CUBE)
     float vertices[] = {
         -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.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)
     rlLoadMesh(&mesh, false);  
 
@@ -1234,6 +1630,7 @@ Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize)
 
     return mesh;
 }
+#endif      // SUPPORT_MESH_GENERATION
 
 // Load material data (from file)
 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 void ToggleFullscreen(void);                                // Toggle fullscreen mode (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 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)
@@ -759,6 +760,7 @@ RLAPI int GetRandomValue(int min, int max);                       // Returns a r
 
 // Files management functions
 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 *GetWorkingDirectory(void);                      // Get current working directory
 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 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 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 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 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)
@@ -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 DrawTextEx(SpriteFont spriteFont, const char* text, Vector2 position,                         // Draw text using SpriteFont and additional parameters
                 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
 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 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 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 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;
-    rlLoadIdentity();
     stackCounter++;
 
     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].vertexCount > 0) drawsCounter++;
+        
+        if (drawsCounter >= MAX_DRAWS_BY_TEXTURE)
+        {
+            rlglDraw();
+            drawsCounter = 1;
+        }
 
         draws[drawsCounter - 1].textureId = id;
         draws[drawsCounter - 1].vertexCount = 0;
@@ -2207,7 +2212,6 @@ void *rlReadTexturePixels(Texture2D texture)
     // 2 - Create an fbo, activate it, render quad with texture, glReadPixels()
 
 #define GET_TEXTURE_FBO_OPTION_1    // It works
-
 #if defined(GET_TEXTURE_FBO_OPTION_1)
     glBindFramebuffer(GL_FRAMEBUFFER, fbo.id);
     glBindTexture(GL_TEXTURE_2D, 0);
@@ -2941,7 +2945,7 @@ void ToggleVrMode(void)
         
         // Reset viewport and default projection-modelview matrices
         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();
     }
     else vrStereoRender = true;
@@ -3043,7 +3047,7 @@ void EndVrDrawing(void)
 
         // Reset viewport and default projection-modelview matrices
         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();
         
         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
 #define RRES_H
 
@@ -75,6 +83,9 @@
         void *data;                 // Resource data pointer (4 byte)
     } RRESData;
     
+    // RRES type (pointer to RRESData array)
+    typedef struct RRESData *RRES;  // Resource pointer
+    
     // RRESData type
     typedef enum { 
         RRES_TYPE_RAW = 0, 
@@ -83,12 +94,25 @@
         RRES_TYPE_VERTEX, 
         RRES_TYPE_TEXT,
         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
     } 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
 
 //----------------------------------------------------------------------------------
@@ -103,6 +127,54 @@
 RRESDEF RRES LoadResource(const char *fileName, int rresId);
 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
 
 
@@ -169,6 +241,7 @@ typedef enum {
     // gzip, zopfli, lzo, zstd  // Other compression algorythms...
 } RRESCompressionType;
 
+// Encryption types
 typedef enum {
     RRES_CRYPTO_NONE = 0,       // No data encryption
     RRES_CRYPTO_XOR,            // XOR (128 bit) encryption
@@ -179,6 +252,7 @@ typedef enum {
     // twofish, RC5, RC6        // Other encryption algorythm...
 } RRESEncryptionType;
 
+// Image/Texture data type
 typedef enum {
     RRES_IM_UNCOMP_GRAYSCALE = 1,     // 8 bit per pixel (no alpha)
     RRES_IM_UNCOMP_GRAY_ALPHA,        // 16 bpp (2 channels)
@@ -201,6 +275,7 @@ typedef enum {
     //...
 } RRESImageFormat;
 
+// Vertex data type
 typedef enum {
     RRES_VERT_POSITION,
     RRES_VERT_TEXCOORD1,
@@ -214,6 +289,7 @@ typedef enum {
     //...
 } RRESVertexType;
 
+// Vertex data format type
 typedef enum {
     RRES_VERT_BYTE,
     RRES_VERT_SHORT,
@@ -275,10 +351,10 @@ RRESDEF RRES LoadResource(const char *fileName, int rresId)
                 // Read resource info and parameters
                 fread(&infoHeader, sizeof(RRESInfoHeader), 1, rresFile);
                 
-                rres = (RRES)malloc(sizeof(RRESData)*infoHeader.partsCount);
-                
                 if (infoHeader.id == rresId)
                 {
+                    rres = (RRES)malloc(sizeof(RRESData)*infoHeader.partsCount);
+                                    
                     // Load all required resources parts
                     for (int k = 0; k < infoHeader.partsCount; k++)
                     {
@@ -327,8 +403,11 @@ RRESDEF RRES LoadResource(const char *fileName, int rresId)
     return rres;
 }
 
+// Unload resource data
 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);
 }
 
@@ -401,28 +480,4 @@ void TraceLog(int logType, const char *text, ...)
 }
 #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();
 }
 
+// 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)
 // 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)
@@ -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
 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'
 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...
 *       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:
 *       stb_image        - Multiple image formats loading (JPEG, PNG, BMP, TGA, PSD, GIF, PIC)
 *                          NOTE: stb_image has been slightly modified to support Android platform.
@@ -56,6 +59,7 @@
 #define SUPPORT_FILEFORMAT_DDS
 #define SUPPORT_FILEFORMAT_HDR
 #define SUPPORT_IMAGE_MANIPULATION
+#define SUPPORT_IMAGE_GENERATION
 //-------------------------------------------------
 
 #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);
 
-    // NOTE: GetTextureData() not available in OpenGL ES
+    // NOTE: glGetTexImage() not available in OpenGL ES
     Image imFont = GetTextureData(font.texture);
 
     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
 
+#if defined(SUPPORT_IMAGE_GENERATION)
 // Generate image: vertical gradient
 Image GenImageGradientV(int width, int height, Color top, Color bottom)
 {
@@ -1647,6 +1652,7 @@ Image GenImageCellular(int width, int height, int tileSize)
 
     return image;
 }
+#endif      // SUPPORT_IMAGE_GENERATION
 
 // Generate GPU mipmaps for a texture
 void GenTextureMipmaps(Texture2D *texture)

+ 1 - 1
templates/android_project/AndroidManifest.xml

@@ -14,7 +14,7 @@
         android:versionCode="1" 
         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" />
     <!--<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. -->

+ 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 "android_native_app_glue.h"
+
 //----------------------------------------------------------------------------------
 // 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)
 *
@@ -291,14 +291,17 @@
 #define MAGENTA    CLITERAL{ 255, 0, 255, 255 }     // Magenta
 #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
 //----------------------------------------------------------------------------------
 #ifndef __cplusplus
 // Boolean type
-    #if !defined(_STDBOOL_H)
+    #if !defined(_STDBOOL_H) || !defined(__STDBOOL_H)   // CLang uses second form
         typedef enum { false, true } bool;
-        #define _STDBOOL_H
     #endif
 #endif
 
@@ -401,63 +404,46 @@ typedef struct Camera2D {
 
 // Bounding box type
 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;
 
 // Vertex data definning a mesh
+// NOTE: Data stored in CPU memory (and GPU)
 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 vboId[7];  // OpenGL Vertex Buffer Objects id (7 types of vertex data)
 } Mesh;
 
-// Shader type (generic shader)
+// Shader type (generic)
 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;
 
-// 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;
 
 // Model type
@@ -473,7 +459,7 @@ typedef struct Ray {
     Vector3 direction;      // Ray direction
 } Ray;
 
-// Information returned from a raycast
+// Raycast hit information
 typedef struct RayHitInfo {
     bool hit;               // Did the ray hit something?
     float distance;         // Distance to nearest hit
@@ -534,13 +520,63 @@ typedef struct RRESData *RRES;
 //----------------------------------------------------------------------------------
 // Trace log type
 typedef enum { 
-    INFO = 0,
-    WARNING, 
-    ERROR, 
-    DEBUG, 
-    OTHER 
+    LOG_INFO = 0,
+    LOG_WARNING, 
+    LOG_ERROR, 
+    LOG_DEBUG, 
+    LOG_OTHER 
 } 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
 // NOTE: Support depends on OpenGL version and platform
 typedef enum {
@@ -653,17 +689,19 @@ extern "C" {            // Prevents name mangling of functions
 //------------------------------------------------------------------------------------
 // Window and Graphics Device Functions (Module: core)
 //------------------------------------------------------------------------------------
+
+// Window-related functions
 #if defined(PLATFORM_ANDROID)
 RLAPI void InitWindow(int width, int height, void *state);        // Initialize Android activity
 #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
 #endif
-
 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 IsWindowMinimized(void);                               // Check if window has been minimized (or lost focus)
 RLAPI void ToggleFullscreen(void);                                // Toggle fullscreen mode (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 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)
@@ -671,6 +709,7 @@ RLAPI int GetScreenWidth(void);                                   // Get current
 RLAPI int GetScreenHeight(void);                                  // Get current screen height
 
 #if !defined(PLATFORM_ANDROID)
+// Cursor-related functions
 RLAPI void ShowCursor(void);                                      // Shows cursor
 RLAPI void HideCursor(void);                                      // Hides cursor
 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)
 #endif
 
+// Drawing-related functions
 RLAPI void ClearBackground(Color color);                          // Set background color (framebuffer clear color)
 RLAPI void BeginDrawing(void);                                    // Setup canvas (framebuffer) to start drawing
 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 End2dMode(void);                                       // Ends 2D mode with custom camera
 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 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 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)
 
+// Timming-related functions
 RLAPI void SetTargetFPS(int fps);                                 // Set target FPS (maximum)
 RLAPI int GetFPS(void);                                           // Returns current FPS
 RLAPI float GetFrameTime(void);                                   // Returns time in seconds for last frame drawn
 
-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 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 *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 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 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 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 *GetWorkingDirectory(void);                      // Get current working directory
 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 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 int StorageLoadValue(int position);                         // Load integer value from storage file (from defined position)
 
 //------------------------------------------------------------------------------------
 // Input Handling Functions (Module: core)
 //------------------------------------------------------------------------------------
+
+// Input-related functions: keyboard
 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 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 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 IsGamepadName(int gamepad, const char *name);      // Check gamepad name (if available)
 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 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 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
@@ -753,6 +807,7 @@ RLAPI Vector2 GetMousePosition(void);                         // Returns mouse p
 RLAPI void SetMousePosition(Vector2 position);                // Set mouse position XY
 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 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)
@@ -786,6 +841,8 @@ RLAPI void SetCameraMoveControls(int frontKey, int backKey,
 //------------------------------------------------------------------------------------
 // Basic Shapes Drawing Functions (Module: shapes)
 //------------------------------------------------------------------------------------
+
+// Basic shapes drawing functions
 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 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 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 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 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 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 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
 
+// Basic shapes collision detection functions
 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 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)
 //------------------------------------------------------------------------------------
+
+// Image/Texture2D data loading/unloading/saving functions
 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 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 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 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 ImageFormat(Image *image, int newFormat);                                                     // Convert image data to desired format
 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 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)
+
+// 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 SetTextureFilter(Texture2D texture, int filterMode);                                          // Set texture scaling filter 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 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
@@ -867,24 +944,30 @@ RLAPI void DrawTexturePro(Texture2D texture, Rectangle sourceRec, Rectangle dest
 //------------------------------------------------------------------------------------
 // Font Loading and Text Drawing Functions (Module: text)
 //------------------------------------------------------------------------------------
+
+// SpriteFont loading/unloading functions
 RLAPI SpriteFont GetDefaultFont(void);                                                                   // Get the default SpriteFont
 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 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 DrawTextEx(SpriteFont spriteFont, const char* text, Vector2 position,                         // Draw text using SpriteFont and additional parameters
                 float fontSize, int spacing, Color tint);
+
+// Text misc. functions
 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 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 *SubText(const char *text, int position, int length);                                   // Get a piece of a text string
 
 //------------------------------------------------------------------------------------
 // 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 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
@@ -905,19 +988,33 @@ RLAPI void DrawGizmo(Vector3 position);
 //------------------------------------------------------------------------------------
 // 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 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 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 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)
 
+// Model drawing functions
 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,
                        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,
                             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 DrawBillboard(Camera camera, Texture2D texture, Vector3 center, float size, Color tint);     // Draw a billboard texture
 RLAPI void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle 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 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
@@ -946,46 +1043,56 @@ RLAPI RayHitInfo GetCollisionRayGround(Ray ray, float groundHeight);
 // Shaders System Functions (Module: rlgl)
 // 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 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 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 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 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 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 EndShaderMode(void);                                           // End custom shader drawing (use default shader)
 RLAPI void BeginBlendMode(int mode);                                      // Begin blending mode (alpha, additive, multiplied)
 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 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 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 EndVrDrawing(void);                      // End VR simulator stereo rendering
 
 //------------------------------------------------------------------------------------
 // Audio Loading and Playing Functions (Module: audio)
 //------------------------------------------------------------------------------------
+
+// Audio device management functions
 RLAPI void InitAudioDevice(void);                                     // Initialize 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 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 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
@@ -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 UnloadWave(Wave wave);                                     // Unload wave data
 RLAPI void UnloadSound(Sound sound);                                  // Unload sound
+
+// Wave/Sound management functions
 RLAPI void PlaySound(Sound sound);                                    // Play a sound
 RLAPI void PauseSound(Sound sound);                                   // Pause a 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 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
+
+// Music management functions
 RLAPI Music LoadMusicStream(const char *fileName);                    // Load music stream from file
 RLAPI void UnloadMusicStream(Music music);                            // Unload music stream
 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 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)
 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

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