diff --git a/src/external/par_shapes.h b/src/external/par_shapes.h index 8b65e384a..f44987a30 100644 --- a/src/external/par_shapes.h +++ b/src/external/par_shapes.h @@ -21,8 +21,7 @@ // coordinates (one per vertex). That's it! If you need something fancier, // look elsewhere. // -// The MIT License -// Copyright (c) 2015 Philip Rideout +// Distributed under the MIT License, see bottom of file. #ifndef PAR_SHAPES_H #define PAR_SHAPES_H @@ -32,21 +31,17 @@ extern "C" { #endif #include - -// Ray: commented to avoid conflict with raylib bool -/* #if !defined(_MSC_VER) # include #else // MSVC # if _MSC_VER >= 1800 # include # else // stdbool.h missing prior to MSVC++ 12.0 (VS2013) -//# define bool int -//# define true 1 -//# define false 0 +# define bool int +# define true 1 +# define false 0 # endif #endif -*/ #ifndef PAR_SHAPES_T #define PAR_SHAPES_T uint16_t @@ -71,6 +66,14 @@ void par_shapes_free_mesh(par_shapes_mesh*); // both 1.0, but they can easily be changed with par_shapes_scale. par_shapes_mesh* par_shapes_create_cylinder(int slices, int stacks); +// Cone is similar to cylinder but the radius diminishes to zero as Z increases. +// Again, height and radius are 1.0, but can be changed with par_shapes_scale. +par_shapes_mesh* par_shapes_create_cone(int slices, int stacks); + +// Create a disk of radius 1.0 with texture coordinates and normals by squashing +// a cone flat on the Z=0 plane. +par_shapes_mesh* par_shapes_create_parametric_disk(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); @@ -172,6 +175,17 @@ par_shapes_mesh* par_shapes_weld(par_shapes_mesh const*, float epsilon, // Compute smooth normals by averaging adjacent facet normals. void par_shapes_compute_normals(par_shapes_mesh* m); +// Global Config --------------------------------------------------------------- + +void par_shapes_set_epsilon_welded_normals(float epsilon); +void par_shapes_set_epsilon_degenerate_sphere(float epsilon); + +// Advanced -------------------------------------------------------------------- + +void par_shapes__compute_welded_normals(par_shapes_mesh* m); +void par_shapes__connect(par_shapes_mesh* scene, par_shapes_mesh* cylinder, + int slices); + #ifndef PAR_PI #define PAR_PI (3.14159265359) #define PAR_MIN(a, b) (a > b ? b : a) @@ -205,11 +219,15 @@ void par_shapes_compute_normals(par_shapes_mesh* m); #include #include +static float par_shapes__epsilon_welded_normals = 0.001; +static float par_shapes__epsilon_degenerate_sphere = 0.0001; + 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__cone(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*); @@ -298,11 +316,12 @@ static float par_shapes__sqrdist3(float const* a, float const* b) return dx * dx + dy * dy + dz * dz; } -static void par_shapes__compute_welded_normals(par_shapes_mesh* m) +void par_shapes__compute_welded_normals(par_shapes_mesh* m) { + const float epsilon = par_shapes__epsilon_welded_normals; 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_mesh* welded = par_shapes_weld(m, epsilon, weldmap); par_shapes_compute_normals(welded); float* pdst = m->normals; for (int i = 0; i < m->npoints; i++, pdst += 3) { @@ -325,6 +344,24 @@ par_shapes_mesh* par_shapes_create_cylinder(int slices, int stacks) stacks, 0); } +par_shapes_mesh* par_shapes_create_cone(int slices, int stacks) +{ + if (slices < 3 || stacks < 1) { + return 0; + } + return par_shapes_create_parametric(par_shapes__cone, slices, + stacks, 0); +} + +par_shapes_mesh* par_shapes_create_parametric_disk(int slices, int stacks) +{ + par_shapes_mesh* m = par_shapes_create_cone(slices, stacks); + if (m) { + par_shapes_scale(m, 1.0f, 1.0f, 0.0f); + } + return m; +} + par_shapes_mesh* par_shapes_create_parametric_sphere(int slices, int stacks) { if (slices < 3 || stacks < 3) { @@ -332,7 +369,7 @@ par_shapes_mesh* par_shapes_create_parametric_sphere(int slices, int stacks) } par_shapes_mesh* m = par_shapes_create_parametric(par_shapes__sphere, slices, stacks, 0); - par_shapes_remove_degenerate(m, 0.0001); + par_shapes_remove_degenerate(m, par_shapes__epsilon_degenerate_sphere); return m; } @@ -343,7 +380,7 @@ par_shapes_mesh* par_shapes_create_hemisphere(int slices, int stacks) } par_shapes_mesh* m = par_shapes_create_parametric(par_shapes__hemisphere, slices, stacks, 0); - par_shapes_remove_degenerate(m, 0.0001); + par_shapes_remove_degenerate(m, par_shapes__epsilon_degenerate_sphere); return m; } @@ -579,6 +616,15 @@ static void par_shapes__cylinder(float const* uv, float* xyz, void* userdata) xyz[2] = uv[0]; } +static void par_shapes__cone(float const* uv, float* xyz, void* userdata) +{ + float r = 1.0f - uv[0]; + float theta = uv[1] * 2 * PAR_PI; + xyz[0] = r * sinf(theta); + xyz[1] = r * cosf(theta); + xyz[2] = uv[0]; +} + static void par_shapes__torus(float const* uv, float* xyz, void* userdata) { float major = 1; @@ -620,6 +666,14 @@ static void par_shapes__trefoil(float const* uv, float* xyz, void* userdata) xyz[2] = z + d * ww[2] * sin(v); } +void par_shapes_set_epsilon_welded_normals(float epsilon) { + par_shapes__epsilon_welded_normals = epsilon; +} + +void par_shapes_set_epsilon_degenerate_sphere(float epsilon) { + par_shapes__epsilon_degenerate_sphere = epsilon; +} + void par_shapes_merge(par_shapes_mesh* dst, par_shapes_mesh const* src) { PAR_SHAPES_T offset = dst->npoints; @@ -744,15 +798,15 @@ void par_shapes_rotate(par_shapes_mesh* mesh, float radians, float const* axis) 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; + float* n = mesh->normals; + if (n) { + for (int i = 0; i < mesh->npoints; i++, n += 3) { + float x = col0[0] * n[0] + col1[0] * n[1] + col2[0] * n[2]; + float y = col0[1] * n[0] + col1[1] * n[1] + col2[1] * n[2]; + float z = col0[2] * n[0] + col1[2] * n[1] + col2[2] * n[2]; + n[0] = x; + n[1] = y; + n[2] = z; } } } @@ -765,6 +819,27 @@ void par_shapes_scale(par_shapes_mesh* m, float x, float y, float z) *points++ *= y; *points++ *= z; } + float* n = m->normals; + if (n && !(x == y && y == z)) { + bool x_zero = x == 0; + bool y_zero = y == 0; + bool z_zero = z == 0; + if (!x_zero && !y_zero && !z_zero) { + x = 1.0f / x; + y = 1.0f / y; + z = 1.0f / z; + } else { + x = x_zero && !y_zero && !z_zero; + y = y_zero && !x_zero && !z_zero; + z = z_zero && !x_zero && !y_zero; + } + for (int i = 0; i < m->npoints; i++, n += 3) { + n[0] *= x; + n[1] *= y; + n[2] *= z; + par_shapes__normalize3(n); + } + } } void par_shapes_merge_and_free(par_shapes_mesh* dst, par_shapes_mesh* src) @@ -1098,8 +1173,8 @@ static par_shapes_mesh* par_shapes__apply_turtle(par_shapes_mesh* mesh, return m; } -static void par_shapes__connect(par_shapes_mesh* scene, - par_shapes_mesh* cylinder, int slices) +void par_shapes__connect(par_shapes_mesh* scene, par_shapes_mesh* cylinder, + int slices) { int stacks = 1; int npoints = (slices + 1) * (stacks + 1); @@ -1118,7 +1193,8 @@ static void par_shapes__connect(par_shapes_mesh* scene, // 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); + memcpy(triangles, scene->triangles, + sizeof(PAR_SHAPES_T) * scene->ntriangles * 3); int v = scene->npoints - (slices + 1); PAR_SHAPES_T* face = triangles + scene->ntriangles * 3; for (int stack = 0; stack < stacks; stack++) { @@ -1154,7 +1230,7 @@ par_shapes_mesh* par_shapes_create_lsystem(char const* text, int slices, while (cmd) { char *arg = strtok(0, " "); if (!arg) { - //puts("lsystem error: unexpected end of program."); + puts("lsystem error: unexpected end of program."); break; } if (!strcmp(cmd, "rule")) { @@ -1208,7 +1284,6 @@ par_shapes_mesh* par_shapes_create_lsystem(char const* text, int slices, // 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); @@ -1217,7 +1292,6 @@ par_shapes_mesh* par_shapes_create_lsystem(char const* text, int slices, printf("\t%s %s\n", cmd.cmd, cmd.arg); } } - */ #endif // Instantiate the aggregated shape and the template shapes. @@ -1258,7 +1332,8 @@ par_shapes_mesh* par_shapes_create_lsystem(char const* text, int slices, 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); + printf("%5s %5s %5s:%d %03d\n", cmd->cmd, cmd->arg, rule->name, + frame->pc - 1, stackptr); #endif float value; @@ -1620,7 +1695,7 @@ static void par_shapes__weld_points(par_shapes_mesh* mesh, int gridsize, PAR_SHAPES_T binvalue = *(bins + binindex); if (binvalue > 0) { if (nbins == 8) { - //printf("Epsilon value is too large.\n"); + printf("Epsilon value is too large.\n"); break; } nearby[nbins++] = binindex; @@ -1632,8 +1707,9 @@ static void par_shapes__weld_points(par_shapes_mesh* mesh, int gridsize, // 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 binvalue = bins[binindex]; PAR_SHAPES_T nindex = binvalue - 1; + assert(nindex < mesh->npoints); while (true) { // If this isn't "self" and it's colocated, then weld it! @@ -1699,6 +1775,9 @@ static void par_shapes__weld_points(par_shapes_mesh* mesh, int gridsize, PAR_SHAPES_T b = weldmap[tsrc[1]]; PAR_SHAPES_T c = weldmap[tsrc[2]]; if (a != b && a != c && b != c) { + assert(a < mesh->npoints); + assert(b < mesh->npoints); + assert(c < mesh->npoints); *tdst++ = a; *tdst++ = b; *tdst++ = c; @@ -2049,3 +2128,25 @@ void par_shapes_remove_degenerate(par_shapes_mesh* mesh, float mintriarea) #endif // PAR_SHAPES_IMPLEMENTATION #endif // PAR_SHAPES_H + +// par_shapes is distributed under the MIT license: +// +// Copyright (c) 2019 Philip Rideout +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. diff --git a/src/models.c b/src/models.c index bce9c8d82..2b6f9a589 100644 --- a/src/models.c +++ b/src/models.c @@ -2084,6 +2084,61 @@ Mesh GenMeshCylinder(float radius, float height, int slices) return mesh; } +// Generate cone/pyramid mesh +Mesh GenMeshCone(float radius, float height, int slices) +{ + Mesh mesh = { 0 }; + + if (slices >= 3) + { + // Instance a cone 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 *cone = par_shapes_create_cone(slices, 8); + par_shapes_scale(cone, radius, radius, height); + par_shapes_rotate(cone, -PI/2.0f, (float[]){ 1, 0, 0 }); + par_shapes_rotate(cone, PI/2.0f, (float[]){ 0, 1, 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(cone, capBottom); + + mesh.vertices = (float *)RL_MALLOC(cone->ntriangles*3*3*sizeof(float)); + mesh.texcoords = (float *)RL_MALLOC(cone->ntriangles*3*2*sizeof(float)); + mesh.normals = (float *)RL_MALLOC(cone->ntriangles*3*3*sizeof(float)); + + mesh.vertexCount = cone->ntriangles*3; + mesh.triangleCount = cone->ntriangles; + + for (int k = 0; k < mesh.vertexCount; k++) + { + mesh.vertices[k*3] = cone->points[cone->triangles[k]*3]; + mesh.vertices[k*3 + 1] = cone->points[cone->triangles[k]*3 + 1]; + mesh.vertices[k*3 + 2] = cone->points[cone->triangles[k]*3 + 2]; + + mesh.normals[k*3] = cone->normals[cone->triangles[k]*3]; + mesh.normals[k*3 + 1] = cone->normals[cone->triangles[k]*3 + 1]; + mesh.normals[k*3 + 2] = cone->normals[cone->triangles[k]*3 + 2]; + + mesh.texcoords[k*2] = cone->tcoords[cone->triangles[k]*2]; + mesh.texcoords[k*2 + 1] = cone->tcoords[cone->triangles[k]*2 + 1]; + } + + par_shapes_free_mesh(cone); + + // Upload vertex data to GPU (static mesh) + UploadMesh(&mesh, false); + } + else TRACELOG(LOG_WARNING, "MESH: Failed to generate mesh: cone"); + + return mesh; +} + // Generate torus mesh Mesh GenMeshTorus(float radius, float size, int radSeg, int sides) { diff --git a/src/raylib.h b/src/raylib.h index 781c14202..92e592e90 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1428,6 +1428,7 @@ RLAPI Mesh GenMeshCube(float width, float height, float length); 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 GenMeshCone(float radius, float height, int slices); // Generate cone/pyramid 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