aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRay <raysan5@gmail.com>2017-09-26 09:13:16 +0200
committerGitHub <noreply@github.com>2017-09-26 09:13:16 +0200
commit4a63e5dfb3006483cace85c8161d12057a9e8488 (patch)
tree53f1ca68df7c2d398a157a825c21fc4ea704f07d /src
parent7ca90d87f9fa6f399d3316df347c63baf0eb675d (diff)
parent5ace947a809d32d0177334933b0709b3164a79d5 (diff)
downloadraylib-4a63e5dfb3006483cace85c8161d12057a9e8488.tar.gz
raylib-4a63e5dfb3006483cace85c8161d12057a9e8488.zip
Merge pull request #360 from raysan5/develop
Integrate Develop branch
Diffstat (limited to 'src')
-rw-r--r--src/Makefile15
-rw-r--r--src/audio.c1
-rw-r--r--src/core.c18
-rw-r--r--src/external/par_shapes.h2050
-rw-r--r--src/meson.build2
-rw-r--r--src/models.c399
-rw-r--r--src/raylib.h17
-rw-r--r--src/rlgl.c12
-rw-r--r--src/rres.h115
-rw-r--r--src/shapes.c55
-rw-r--r--src/text.c8
-rw-r--r--src/textures.c8
12 files changed, 2641 insertions, 59 deletions
diff --git a/src/Makefile b/src/Makefile
index 06b67a04..72f37b09 100644
--- a/src/Makefile
+++ b/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
diff --git a/src/audio.c b/src/audio.c
index 1b340377..06af8ed4 100644
--- a/src/audio.c
+++ b/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)
diff --git a/src/core.c b/src/core.c
index 78c0a7ef..586d1137 100644
--- a/src/core.c
+++ b/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)
{
diff --git a/src/external/par_shapes.h b/src/external/par_shapes.h
new file mode 100644
index 00000000..39831c8b
--- /dev/null
+++ b/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
diff --git a/src/meson.build b/src/meson.build
index bdffb98e..12385fc9 100644
--- a/src/meson.build
+++ b/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')
diff --git a/src/models.c b/src/models.c
index 315b51d4..ea8f6c30 100644
--- a/src/models.c
+++ b/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)
diff --git a/src/raylib.h b/src/raylib.h
index 85499eb1..07674531 100644
--- a/src/raylib.h
+++ b/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
diff --git a/src/rlgl.c b/src/rlgl.c
index 373dada9..c336ac4c 100644
--- a/src/rlgl.c
+++ b/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();
diff --git a/src/rres.h b/src/rres.h
index 93d1c395..75faf640 100644
--- a/src/rres.h
+++ b/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
-
-*/ \ No newline at end of file
+#endif // RRES_IMPLEMENTATION
diff --git a/src/shapes.c b/src/shapes.c
index 0e544718..8c7f2419 100644
--- a/src/shapes.c
+++ b/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)
{
diff --git a/src/text.c b/src/text.c
index fe00ce86..465e4546 100644
--- a/src/text.c
+++ b/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, ...)
{
diff --git a/src/textures.c b/src/textures.c
index 9322004b..b9fc5c19 100644
--- a/src/textures.c
+++ b/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)