diff options
| author | Andre Weissflog <floooh@gmail.com> | 2020-11-17 19:40:38 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-11-17 19:40:38 +0100 |
| commit | f21abdef3b05e6ca200d33e1603aba56aee28984 (patch) | |
| tree | 4f6db7b6a0a0adf9b969b7b6aa0709b211c557af /util | |
| parent | 8d042b5862bda588e3f8bcd264d6acc495e4edeb (diff) | |
New header: sokol_shape.h (#427)
Diffstat (limited to 'util')
| -rw-r--r-- | util/sokol_shape.h | 1406 |
1 files changed, 1406 insertions, 0 deletions
diff --git a/util/sokol_shape.h b/util/sokol_shape.h new file mode 100644 index 00000000..3317b603 --- /dev/null +++ b/util/sokol_shape.h @@ -0,0 +1,1406 @@ +#ifndef SOKOL_SHAPE_INCLUDED +/* + sokol_shape.h -- create simple primitive shapes for sokol_gfx.h + + Project URL: https://github.com/floooh/sokol + + Do this: + #define SOKOL_SHAPE_IMPL + before you include this file in *one* C or C++ file to create the + implementation. + + Include the following headers before including sokol_shape.h: + + sokol_gfx.h + + ...optionally provide the following macros to override defaults: + + SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) + SOKOL_API_DECL - public function declaration prefix (default: extern) + SOKOL_API_IMPL - public function implementation prefix (default: -) + + If sokol_shape.h is compiled as a DLL, define the following before + including the declaration or implementation: + + SOKOL_DLL + + On Windows, SOKOL_DLL will define SOKOL_API_DECL as __declspec(dllexport) + or __declspec(dllimport) as needed. + + FEATURE OVERVIEW + ================ + sokol_shape.h creates vertices and indices for simple shapes and + builds structs which can be plugged into sokol-gfx resource + creation descriptor structs. + + The following shape types are supported: + + - plane + - cube + - sphere (with poles, not geodesic) + - cylinder + - torus (donut) + + Generated vertices look like this: + + typedef struct sshape_vertex_t { + float x, y, z; + uint32_t normal; // packed normal as BYTE4N + uint16_t u, v; // packed uv coords as USHORT2N + uint32_t color; // packed color as UBYTE4N (r,g,b,a); + } sshape_vertex_t; + + Indices are generally 16-bits wide (SG_INDEXTYPE_UINT16) and the indices + are written as triangle-lists (SG_PRIMITIVETYPE_TRIANGLES). + + EXAMPLES: + ========= + + Create multiple shapes into the same vertex- and index-buffer and + render with separate draw calls: + + https://github.com/floooh/sokol-samples/blob/master/sapp/shapes-sapp.c + + Same as the above, but pre-transform shapes and merge them into a single + shape that's rendered with a single draw call. + + https://github.com/floooh/sokol-samples/blob/master/sapp/shapes-transform-sapp.c + + STEP-BY-STEP: + ============= + + Setup an sshape_buffer_t struct with pointers to memory buffers where + generated vertices and indices will be written to: + + ```c + sshape_vertex_t vertices[512]; + uint16_t indices[4096]; + + sshape_buffer_t buf = { + .vertices = { + .buffer_ptr = vertices, + .buffer_size = sizeof(vertices) + }, + .indices = { + .buffer_ptr = indices, + .buffer_size = sizeof(indices) + } + }; + ``` + + To find out how big those memory buffers must be (in case you want + to allocate dynamically) call the following functions: + + ```c + sshape_sizes_t sshape_plane_sizes(uint32_t tiles); + sshape_sizes_t sshape_box_sizes(uint32_t tiles); + sshape_sizes_t sshape_sphere_sizes(uint32_t slices, uint32_t stacks); + sshape_sizes_t sshape_cylinder_sizes(uint32_t slices, uint32_t stacks); + sshape_sizes_t sshape_torus_sizes(uint32_t sides, uint32_t rings); + ``` + + The returned sshape_sizes_t struct contains vertex- and index-counts + as well as the equivalent buffer sizes in bytes. For instance: + + ```c + sshape_sizes_t sizes = sshape_sphere_sizes(36, 12); + uint32_t num_vertices = sizes.vertices.num; + uint32_t num_indices = sizes.indices.num; + uint32_t vertex_buffer_size = sizes.vertices.size; + uint32_t index_buffer_size = sizes.indices.size; + ``` + + With the sshape_buffer_t struct that was setup earlier, call any + of the shape-builder functions: + + ```c + sshape_buffer_t sshape_build_plane(const sshape_buffer_t* buf, const sshape_plane_t* params); + sshape_buffer_t sshape_build_box(const sshape_buffer_t* buf, const sshape_box_t* params); + sshape_buffer_t sshape_build_sphere(const sshape_buffer_t* buf, const sshape_sphere_t* params); + sshape_buffer_t sshape_build_cylinder(const sshape_buffer_t* buf, const sshape_cylinder_t* params); + sshape_buffer_t sshape_build_torus(const sshape_buffer_t* buf, const sshape_torus_t* params); + ``` + + Note how the sshape_buffer_t struct is both an input value and the + return value. This can be used to append multiple shapes into the + same vertex- and index-buffers (more on this later). + + The second argument is a struct which holds creation parameters. + + For instance to build a sphere with radius 2, 36 "cake slices" and 12 stacks: + + ```c + sshape_buffer_t buf = ...; + buf = sshape_build_sphere(&buf, &(sshape_sphere_t){ + .radius = 2.0f, + .slices = 36, + .stacks = 12, + }); + ``` + + If the provided buffers are big enough to hold all generated vertices and + indices, the "valid" field in the result will be true: + + ```c + assert(buf.valid); + ``` + + The shape creation parameters have "useful defaults", refer to the + actual C struct declarations below to look up those defaults. + + You can also provide additional creation parameters, like a common vertex + color, a debug-helper to randomize colors, tell the shape builder function + to merge the new shape with the previous shape into the same draw-element-range, + or a 4x4 transform matrix to move, rotate and scale the generated matrices: + + ```c + sshape_buffer_t buf = ...; + buf = sshape_build_sphere(&buf, &(sshape_sphere_t){ + .radius = 2.0f, + .slices = 36, + .stacks = 12, + // merge with previous shape into a single element-range + .merge = true, + // set vertex color to red+opaque + .color = sshape_color_4f(1.0f, 0.0f, 0.0f, 1.0f), + // set position to y = 2.0 + .transform = { + .m = { + { 1.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 1.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 1.0f, 0.0f }, + { 0.0f, 2.0f, 0.0f, 1.0f }, + } + } + }); + assert(buf.valid); + ``` + + The following helper functions are offered to build a packed + color value or to convert from external matrix types: + + ```c + uint32_t sshape_color_4f(float r, float g, float b, float a); + uint32_t sshape_color_3f(float r, float g, float b); + uint32_t sshape_color_4b(uint8_t r, uint8_t g, uint8_t b, uint8_t a); + uint32_t sshape_color_3b(uint8_t r, uint8_t g, uint8_t b); + sshape_mat4_t sshape_mat4(const float m[16]); + sshape_mat4_t sshape_mat4_transpose(const float m[16]); + ``` + + After the shape builder function has been called, the following functions + are used to extract the build result for plugging into sokol_gfx.h: + + ```c + sshape_element_range_t sshape_element_range(const sshape_buffer_t* buf); + sg_buffer_desc sshape_vertex_buffer_desc(const sshape_buffer_t* buf); + sg_buffer_desc sshape_index_buffer_desc(const sshape_buffer_t* buf); + sg_buffer_layout_desc sshape_buffer_layout_desc(void); + sg_vertex_attr_desc sshape_position_attr_desc(void); + sg_vertex_attr_desc sshape_normal_attr_desc(void); + sg_vertex_attr_desc sshape_texcoord_attr_desc(void); + sg_vertex_attr_desc sshape_color_attr_desc(void); + ``` + + The sshape_element_range_t struct contains the base-index and number of + indices which can be plugged into the sg_draw() call: + + ```c + sshape_element_range_t elms = sshape_element_range(&buf); + ... + sg_draw(elms.base_element, elms.num_elements, 1); + ``` + + To create sokol-gfx vertex- and index-buffers from the generated + shape data: + + ```c + // create sokol-gfx vertex buffer + sg_buffer_desc vbuf_desc = sshape_vertex_buffer_desc(&buf); + sg_buffer vbuf = sg_make_buffer(&vbuf_desc; + + // create sokol-gfx index buffer + sg_buffer_desc ibuf_desc = sshape_index_buffer_desc(&buf); + sg_buffer ibuf = sg_make_buffer(&ibuf_desc); + ``` + + The remaining functions are used to populate the vertex-layout item + in sg_pipeline_desc, note that these functions don't depend on the + created geometry, they always return the same result: + + ```c + sg_pipeline pip = sg_make_pipeline(&(sg_pipeline_desc){ + .layout = { + .buffers[0] = sshape_buffer_layout_desc(), + .attrs = { + [0] = sshape_position_attr_desc(), + [1] = ssape_normal_attr_desc(), + [2] = sshape_texcoord_attr_desc(), + [3] = sshape_color_attr_desc() + } + }, + ... + }); + ``` + + Note that you don't have to use all generated vertex attributes in the + pipeline's vertex layout, the sg_buffer_layout_desc struct returned + by sshape_buffer_layout_desc() contains the correct vertex stride + to skip vertex components. + + WRITING MULTIPLE SHAPES INTO THE SAME BUFFER + ============================================ + You can merge multiple shapes into the same vertex- and + index-buffers and either render them as a single shape, or + in separate draw calls. + + To build a single shape made of two cubes which can be rendered + in a single draw-call: + + ``` + sshape_vertex_t vertices[128]; + uint16_t indices[16]; + + sshape_buffer_t buf = { + .vertices = { .buffer_ptr = vertices, .buffer_size = sizeof(vertices) }, + .indices = { .buffer_ptr = indices, .buffer_size = sizeof(indices) } + }; + + // first cube at pos x=-2.0 (with default size of 1x1x1) + buf = sshape_build_cube(&buf, &(sshape_box_t){ + .transform = { + .m = { + { 1.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 1.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 1.0f, 0.0f }, + {-2.0f, 0.0f, 0.0f, 1.0f }, + } + } + }); + // ...and append another cube at pos pos=+1.0 + // NOTE the .merge = true, this tells the shape builder + // function to not advance the current shape start offset + buf = sshape_build_cube(&buf, &(sshape_box_t){ + .merge = true, + .transform = { + .m = { + { 1.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 1.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 1.0f, 0.0f }, + {-2.0f, 0.0f, 0.0f, 1.0f }, + } + } + }); + assert(buf.valid); + + // skipping buffer- and pipeline-creation... + + sshape_element_range_t elms = sshape_element_range(&buf); + sg_draw(elms.base_element, elms.num_elements, 1); + ``` + + To render the two cubes in separate draw-calls, the element-ranges used + in the sg_draw() calls must be captured right after calling the + builder-functions: + + ```c + sshape_vertex_t vertices[128]; + uint16_t indices[16]; + sshape_buffer_t buf = { + .vertices = { .buffer_ptr = vertices, .buffer_size = sizeof(vertices) }, + .indices = { .buffer_ptr = indices, .buffer_size = sizeof(indices) } + }; + + // build a red cube... + buf = sshape_build_cube(&buf, &(sshape_box_t){ + .color = sshape_color_3b(255, 0, 0) + }); + sshape_element_range_t red_cube = sshape_element_range(&buf); + + // append a green cube to the same vertex-/index-buffer: + buf = sshape_build_cube(&bud, &sshape_box_t){ + .color = sshape_color_3b(0, 255, 0); + }); + sshape_element_range_t green_cube = sshape_element_range(&buf); + + // skipping buffer- and pipeline-creation... + + sg_draw(red_cube.base_element, red_cube.num_elements, 1); + sg_draw(green_cube.base_element, green_cube.num_elements, 1); + ``` + + ...that's about all :) + + LICENSE + ======= + zlib/libpng license + + Copyright (c) 2020 Andre Weissflog + + This software is provided 'as-is', without any express or implied warranty. + In no event will the authors be held liable for any damages arising from the + use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software in a + product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ +#define SOKOL_SHAPE_INCLUDED +#include <stdint.h> +#include <stdbool.h> + +#if !defined(SOKOL_GFX_INCLUDED) +#error "Please include sokol_gfx.h before sokol_shape.h" +#endif + +#ifndef SOKOL_API_DECL +#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_IMPL) +#define SOKOL_API_DECL __declspec(dllexport) +#elif defined(_WIN32) && defined(SOKOL_DLL) +#define SOKOL_API_DECL __declspec(dllimport) +#else +#define SOKOL_API_DECL extern +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* a 4x4 matrix wrapper struct */ +typedef struct sshape_mat4_t { float m[4][4]; } sshape_mat4_t; + +/* vertex layout of the generated geometry */ +typedef struct sshape_vertex_t { + float x, y, z; + uint32_t normal; // packed normal as BYTE4N + uint16_t u, v; // packed uv coords as USHORT2N + uint32_t color; // packed color as UBYTE4N (r,g,b,a); +} sshape_vertex_t; + +/* a range of draw-elements (sg_draw(int base_element, int num_element, ...)) */ +typedef struct sshape_element_range_t { + int base_element; + int num_elements; +} sshape_element_range_t; + +/* number of elements and byte size of build actions */ +typedef struct sshape_sizes_item_t { + uint32_t num; // number of elements + uint32_t size; // the same as size in bytes +} sshape_sizes_item_t; + +typedef struct sshape_sizes_t { + sshape_sizes_item_t vertices; + sshape_sizes_item_t indices; +} sshape_sizes_t; + +/* in/out struct to keep track of mesh-build state */ +typedef struct sshape_buffer_item_t { + void* buffer_ptr; // pointer to start of output buffer + uint32_t buffer_size; // size in bytes of output buffer + uint32_t data_size; // size in bytes of valid data in buffer + uint32_t shape_offset; // data offset of the most recent shape +} sshape_buffer_item_t; + +typedef struct sshape_buffer_t { + bool valid; + sshape_buffer_item_t vertices; + sshape_buffer_item_t indices; +} sshape_buffer_t; + +/* creation parameters for the different shape types */ +typedef struct sshape_plane_t { + float width, depth; // default: 1.0 + uint16_t tiles; // default: 1 + uint32_t color; // default: white + bool random_colors; // default: false + bool merge; // if true merge with previous shape (default: false) + sshape_mat4_t transform; // default: identity matrix +} sshape_plane_t; + +typedef struct sshape_box_t { + float width, height, depth; // default: 1.0 + uint16_t tiles; // default: 1 + uint32_t color; // default: white + bool random_colors; // default: false + bool merge; // if true merge with previous shape (default: false) + sshape_mat4_t transform; // default: identity matrix +} sshape_box_t; + +typedef struct sshape_sphere_t { + float radius; // default: 0.5 + uint16_t slices; // default: 5 + uint16_t stacks; // default: 4 + uint32_t color; // default: white + bool random_colors; // default: false + bool merge; // if true merge with previous shape (default: false) + sshape_mat4_t transform; // default: identity matrix +} sshape_sphere_t; + +typedef struct sshape_cylinder_t { + float radius; // default: 0.5 + float height; // default: 1.0 + uint16_t slices; // default: 5 + uint16_t stacks; // default: 1 + uint32_t color; // default: white + bool random_colors; // default: false + bool merge; // if true merge with previous shape (default: false) + sshape_mat4_t transform; // default: identity matrix +} sshape_cylinder_t; + +typedef struct sshape_torus_t { + float radius; // default: 0.5f + float ring_radius; // default: 0.2f + uint16_t sides; // default: 5 + uint16_t rings; // default: 5 + uint32_t color; // default: white + bool random_colors; // default: false + bool merge; // if true merge with previous shape (default: false) + sshape_mat4_t transform; // default: identity matrix +} sshape_torus_t; + +/* shape builder functions */ +SOKOL_API_DECL sshape_buffer_t sshape_build_plane(const sshape_buffer_t* buf, const sshape_plane_t* params); +SOKOL_API_DECL sshape_buffer_t sshape_build_box(const sshape_buffer_t* buf, const sshape_box_t* params); +SOKOL_API_DECL sshape_buffer_t sshape_build_sphere(const sshape_buffer_t* buf, const sshape_sphere_t* params); +SOKOL_API_DECL sshape_buffer_t sshape_build_cylinder(const sshape_buffer_t* buf, const sshape_cylinder_t* params); +SOKOL_API_DECL sshape_buffer_t sshape_build_torus(const sshape_buffer_t* buf, const sshape_torus_t* params); + +/* query required vertex- and index-buffer sizes in bytes */ +SOKOL_API_DECL sshape_sizes_t sshape_plane_sizes(uint32_t tiles); +SOKOL_API_DECL sshape_sizes_t sshape_box_sizes(uint32_t tiles); +SOKOL_API_DECL sshape_sizes_t sshape_sphere_sizes(uint32_t slices, uint32_t stacks); +SOKOL_API_DECL sshape_sizes_t sshape_cylinder_sizes(uint32_t slices, uint32_t stacks); +SOKOL_API_DECL sshape_sizes_t sshape_torus_sizes(uint32_t sides, uint32_t rings); + +/* extract sokol-gfx desc structs and primitive ranges from build state */ +SOKOL_API_DECL sshape_element_range_t sshape_element_range(const sshape_buffer_t* buf); +SOKOL_API_DECL sg_buffer_desc sshape_vertex_buffer_desc(const sshape_buffer_t* buf); +SOKOL_API_DECL sg_buffer_desc sshape_index_buffer_desc(const sshape_buffer_t* buf); +SOKOL_API_DECL sg_buffer_layout_desc sshape_buffer_layout_desc(void); +SOKOL_API_DECL sg_vertex_attr_desc sshape_position_attr_desc(void); +SOKOL_API_DECL sg_vertex_attr_desc sshape_normal_attr_desc(void); +SOKOL_API_DECL sg_vertex_attr_desc sshape_texcoord_attr_desc(void); +SOKOL_API_DECL sg_vertex_attr_desc sshape_color_attr_desc(void); + +/* helper functions to build packed color value from floats or bytes */ +SOKOL_API_DECL uint32_t sshape_color_4f(float r, float g, float b, float a); +SOKOL_API_DECL uint32_t sshape_color_3f(float r, float g, float b); +SOKOL_API_DECL uint32_t sshape_color_4b(uint8_t r, uint8_t g, uint8_t b, uint8_t a); +SOKOL_API_DECL uint32_t sshape_color_3b(uint8_t r, uint8_t g, uint8_t b); + +/* adapter function for filling matrix struct from generic float[16] array */ +SOKOL_API_DECL sshape_mat4_t sshape_mat4(const float m[16]); +SOKOL_API_DECL sshape_mat4_t sshape_mat4_transpose(const float m[16]); + +#ifdef __cplusplus +} // extern "C" + +// FIXME: C++ helper functions + +#endif +#endif // SOKOL_SHAPE_INCLUDED + +/*-- IMPLEMENTATION ----------------------------------------------------------*/ +#ifdef SOKOL_SHAPE_IMPL +#define SOKOL_SHAPE_IMPL_INCLUDED (1) + +#include <string.h> // memcpy +#include <stddef.h> // offsetof +#include <math.h> // sinf, cosf + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wmissing-field-initializers" +#pragma clang diagnostic ignored "-Wmissing-braces" +#endif + +#ifndef SOKOL_API_IMPL + #define SOKOL_API_IMPL +#endif +#ifndef SOKOL_ASSERT + #include <assert.h> + #define SOKOL_ASSERT(c) assert(c) +#endif + +#define _sshape_def(val, def) (((val) == 0) ? (def) : (val)) +#define _sshape_def_flt(val, def) (((val) == 0.0f) ? (def) : (val)) +#define _sshape_white (0xFFFFFFFF) + +typedef struct { float x, y, z, w; } _sshape_vec4_t; +typedef struct { float x, y; } _sshape_vec2_t; + +static inline float _sshape_clamp(float v) { + if (v < 0.0f) return 0.0f; + else if (v > 1.0f) return 1.0f; + else return v; +} + +static inline uint32_t _sshape_pack_ub4_ubyte4n(uint8_t x, uint8_t y, uint8_t z, uint8_t w) { + return (uint32_t)(((uint32_t)w<<24)|((uint32_t)z<<16)|((uint32_t)y<<8)|x); +} + +static inline uint32_t _sshape_pack_f4_ubyte4n(float x, float y, float z, float w) { + uint8_t x8 = (uint8_t) (x * 255.0f); + uint8_t y8 = (uint8_t) (y * 255.0f); + uint8_t z8 = (uint8_t) (z * 255.0f); + uint8_t w8 = (uint8_t) (w * 255.0f); + return _sshape_pack_ub4_ubyte4n(x8, y8, z8, w8); +} + +static inline uint32_t _sshape_pack_f4_byte4n(float x, float y, float z, float w) { + int8_t x8 = (int8_t) (x * 127.0f); + int8_t y8 = (int8_t) (y * 127.0f); + int8_t z8 = (int8_t) (z * 127.0f); + int8_t w8 = (int8_t) (w * 127.0f); + return _sshape_pack_ub4_ubyte4n((uint8_t)x8, (uint8_t)y8, (uint8_t)z8, (uint8_t)w8); +} + +static inline uint16_t _sshape_pack_f_ushortn(float x) { + return (uint16_t) (x * 65535.0f); +} + +static inline _sshape_vec4_t _sshape_vec4(float x, float y, float z, float w) { + _sshape_vec4_t v = { x, y, z, w }; + return v; +} + +static inline _sshape_vec4_t _sshape_vec4_norm(_sshape_vec4_t v) { + float l = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z + v.w*v.w); + if (l != 0.0f) { + return _sshape_vec4(v.x/l, v.y/l, v.z/l, v.w/l); + } + else { + return _sshape_vec4(0.0f, 1.0f, 0.0f, 0.0f); + } +} + +static inline _sshape_vec2_t _sshape_vec2(float x, float y) { + _sshape_vec2_t v = { x, y }; + return v; +} + +static bool _sshape_mat4_isnull(const sshape_mat4_t* m) { + for (int y = 0; y < 4; y++) { + for (int x = 0; x < 4; x++) { + if (0.0f != m->m[y][x]) { + return false; + } + } + } + return true; +} + +static sshape_mat4_t _sshape_mat4_identity(void) { + sshape_mat4_t m = { + { + { 1.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 1.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 1.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f, 1.0f } + } + }; + return m; +} + +static _sshape_vec4_t _sshape_mat4_mul(const sshape_mat4_t* m, _sshape_vec4_t v) { + _sshape_vec4_t res = { + m->m[0][0]*v.x + m->m[1][0]*v.y + m->m[2][0]*v.z + m->m[3][0]*v.w, + m->m[0][1]*v.x + m->m[1][1]*v.y + m->m[2][1]*v.z + m->m[3][1]*v.w, + m->m[0][2]*v.x + m->m[1][2]*v.y + m->m[2][2]*v.z + m->m[3][2]*v.w, + m->m[0][3]*v.x + m->m[1][3]*v.y + m->m[2][3]*v.z + m->m[3][3]*v.w + }; + return res; +} + +static uint32_t _sshape_plane_num_vertices(uint32_t tiles) { + return (tiles + 1) * (tiles + 1); +} + +static uint32_t _sshape_plane_num_indices(uint32_t tiles) { + return tiles * tiles * 2 * 3; +} + +static uint32_t _sshape_box_num_vertices(uint32_t tiles) { + return (tiles + 1) * (tiles + 1) * 6; +} + +static uint32_t _sshape_box_num_indices(uint32_t tiles) { + return tiles * tiles * 2 * 6 * 3; +} + +static uint32_t _sshape_sphere_num_vertices(uint32_t slices, uint32_t stacks) { + return (slices + 1) * (stacks + 1); +} + +static uint32_t _sshape_sphere_num_indices(uint32_t slices, uint32_t stacks) { + return ((2 * slices * stacks) - (2 * slices)) * 3; +} + +static uint32_t _sshape_cylinder_num_vertices(uint32_t slices, uint32_t stacks) { + return (slices + 1) * (stacks + 5); +} + +static uint32_t _sshape_cylinder_num_indices(uint32_t slices, uint32_t stacks) { + return ((2 * slices * stacks) + (2 * slices)) * 3; +} + +static uint32_t _sshape_torus_num_vertices(uint32_t sides, uint32_t rings) { + return (sides + 1) * (rings + 1); +} + +static uint32_t _sshape_torus_num_indices(uint32_t sides, uint32_t rings) { + return sides * rings * 2 * 3; +} + +static bool _sshape_validate_buffer_item(const sshape_buffer_item_t* item, uint32_t build_size) { + if (0 == item->buffer_ptr) { + return false; + } + if (0 == item->buffer_size) { + return false; + } + if ((item->data_size + build_size) > item->buffer_size) { + return false; + } + if (item->shape_offset > item->data_size) { + return false; + } + return true; +} + +static bool _sshape_validate_buffer(const sshape_buffer_t* buf, uint32_t num_vertices, uint32_t num_indices) { + if (!_sshape_validate_buffer_item(&buf->vertices, num_vertices * sizeof(sshape_vertex_t))) { + return false; + } + if (!_sshape_validate_buffer_item(&buf->indices, num_indices * sizeof(uint16_t))) { + return false; + } + return true; +} + +static void _sshape_advance_offset(sshape_buffer_item_t* item) { + item->shape_offset = item->data_size; +} + +static uint16_t _sshape_base_index(const sshape_buffer_t* buf) { + return (uint16_t) (buf->vertices.data_size / sizeof(sshape_vertex_t)); +} + +static sshape_plane_t _sshape_plane_defaults(const sshape_plane_t* params) { + sshape_plane_t res = *params; + res.width = _sshape_def_flt(res.width, 1.0f); + res.depth = _sshape_def_flt(res.depth, 1.0f); + res.tiles = _sshape_def(res.tiles, 1); + res.color = _sshape_def(res.color, _sshape_white); + res.transform = _sshape_mat4_isnull(&res.transform) ? _sshape_mat4_identity() : res.transform; + return res; +} + +static sshape_box_t _sshape_box_defaults(const sshape_box_t* params) { + sshape_box_t res = *params; + res.width = _sshape_def_flt(res.width, 1.0f); + res.height = _sshape_def_flt(res.height, 1.0f); + res.depth = _sshape_def_flt(res.depth, 1.0f); + res.tiles = _sshape_def(res.tiles, 1); + res.color = _sshape_def(res.color, _sshape_white); + res.transform = _sshape_mat4_isnull(&res.transform) ? _sshape_mat4_identity() : res.transform; + return res; +} + +static sshape_sphere_t _sshape_sphere_defaults(const sshape_sphere_t* params) { + sshape_sphere_t res = *params; + res.radius = _sshape_def_flt(res.radius, 0.5f); + res.slices = _sshape_def(res.slices, 5); + res.stacks = _sshape_def(res.stacks, 4); + res.color = _sshape_def(res.color, _sshape_white); + res.transform = _sshape_mat4_isnull(&res.transform) ? _sshape_mat4_identity() : res.transform; + return res; +} + +static sshape_cylinder_t _sshape_cylinder_defaults(const sshape_cylinder_t* params) { + sshape_cylinder_t res = *params; + res.radius = _sshape_def_flt(res.radius, 0.5f); + res.height = _sshape_def_flt(res.height, 1.0f); + res.slices = _sshape_def(res.slices, 5); + res.stacks = _sshape_def(res.stacks, 1); + res.color = _sshape_def(res.color, _sshape_white); + res.transform = _sshape_mat4_isnull(&res.transform) ? _sshape_mat4_identity() : res.transform; + return res; +} + +static sshape_torus_t _sshape_torus_defaults(const sshape_torus_t* params) { + sshape_torus_t res = *params; + res.radius = _sshape_def_flt(res.radius, 0.5f); + res.ring_radius = _sshape_def_flt(res.ring_radius, 0.2f); + res.sides = _sshape_def_flt(res.sides, 5); + res.rings = _sshape_def_flt(res.rings, 5); + res.color = _sshape_def(res.color, _sshape_white); + res.transform = _sshape_mat4_isnull(&res.transform) ? _sshape_mat4_identity() : res.transform; + return res; +} + +static void _sshape_add_vertex(sshape_buffer_t* buf, _sshape_vec4_t pos, _sshape_vec4_t norm, _sshape_vec2_t uv, uint32_t color) { + uint32_t offset = buf->vertices.data_size; + SOKOL_ASSERT((offset + sizeof(sshape_vertex_t)) <= buf->vertices.buffer_size); + buf->vertices.data_size += sizeof(sshape_vertex_t); + sshape_vertex_t* v_ptr = (sshape_vertex_t*) ((uint8_t*)buf->vertices.buffer_ptr + offset); + v_ptr->x = pos.x; + v_ptr->y = pos.y; + v_ptr->z = pos.z; + v_ptr->normal = _sshape_pack_f4_byte4n(norm.x, norm.y, norm.z, norm.w); + v_ptr->u = _sshape_pack_f_ushortn(uv.x); + v_ptr->v = _sshape_pack_f_ushortn(uv.y); + v_ptr->color = color; +} + +static void _sshape_add_triangle(sshape_buffer_t* buf, uint16_t i0, uint16_t i1, uint16_t i2) { + uint32_t offset = buf->indices.data_size; + SOKOL_ASSERT((offset + 3*sizeof(uint16_t)) <= buf->indices.buffer_size); + buf->indices.data_size += 3*sizeof(uint16_t); + uint16_t* i_ptr = (uint16_t*) ((uint8_t*)buf->indices.buffer_ptr + offset); + i_ptr[0] = i0; + i_ptr[1] = i1; + i_ptr[2] = i2; +} + +static uint32_t _sshape_rand_color(uint32_t* xorshift_state) { + // xorshift32 + uint32_t x = *xorshift_state; + x ^= x<<13; + x ^= x>>17; + x ^= x<<5; + *xorshift_state = x; + + // rand => bright color with alpha 1.0 + x |= 0xFF000000; + return x; + +} + +/*=== PUBLIC API FUNCTIONS ===================================================*/ +SOKOL_API_IMPL uint32_t sshape_color_4f(float r, float g, float b, float a) { + return _sshape_pack_f4_ubyte4n(_sshape_clamp(r), _sshape_clamp(g), _sshape_clamp(b), _sshape_clamp(a)); +} + +SOKOL_API_IMPL uint32_t sshape_color_3f(float r, float g, float b) { + return _sshape_pack_f4_ubyte4n(_sshape_clamp(r), _sshape_clamp(g), _sshape_clamp(b), 1.0f); +} + +SOKOL_API_IMPL uint32_t sshape_color_4b(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { + return _sshape_pack_ub4_ubyte4n(r, g, b, a); +} + +SOKOL_API_IMPL uint32_t sshape_color_3b(uint8_t r, uint8_t g, uint8_t b) { + return _sshape_pack_ub4_ubyte4n(r, g, b, 255); +} + +SOKOL_API_IMPL sshape_mat4_t sshape_mat4(const float m[16]) { + sshape_mat4_t res; + memcpy(&res.m[0][0], &m[0], 64); + return res; +} + +SOKOL_API_IMPL sshape_mat4_t sshape_mat4_transpose(const float m[16]) { + sshape_mat4_t res; + for (int c = 0; c < 4; c++) { + for (int r = 0; r < 4; r++) { + res.m[r][c] = m[c*4 + r]; + } + } + return res; +} + +SOKOL_API_IMPL sshape_sizes_t sshape_plane_sizes(uint32_t tiles) { + SOKOL_ASSERT(tiles >= 1); + sshape_sizes_t res = { 0 }; + res.vertices.num = _sshape_plane_num_vertices(tiles); + res.indices.num = _sshape_plane_num_indices(tiles); + res.vertices.size = res.vertices.num * sizeof(sshape_vertex_t); + res.indices.size = res.indices.num * sizeof(uint16_t); + return res; +} + +SOKOL_API_IMPL sshape_sizes_t sshape_box_sizes(uint32_t tiles) { + SOKOL_ASSERT(tiles >= 1); + sshape_sizes_t res = { 0 }; + res.vertices.num = _sshape_box_num_vertices(tiles); + res.indices.num = _sshape_box_num_indices(tiles); + res.vertices.size = res.vertices.num * sizeof(sshape_vertex_t); + res.indices.size = res.indices.num * sizeof(uint16_t); + return res; +} + +SOKOL_API_IMPL sshape_sizes_t sshape_sphere_sizes(uint32_t slices, uint32_t stacks) { + SOKOL_ASSERT((slices >= 3) && (stacks >= 2)); + sshape_sizes_t res = { 0 }; + res.vertices.num = _sshape_sphere_num_vertices(slices, stacks); + res.indices.num = _sshape_sphere_num_indices(slices, stacks); + res.vertices.size = res.vertices.num * sizeof(sshape_vertex_t); + res.indices.size = res.indices.num * sizeof(uint16_t); + return res; +} + +SOKOL_API_IMPL sshape_sizes_t sshape_cylinder_sizes(uint32_t slices, uint32_t stacks) { + SOKOL_ASSERT((slices >= 3) && (stacks >= 1)); + sshape_sizes_t res = { 0 }; + res.vertices.num = _sshape_cylinder_num_vertices(slices, stacks); + res.indices.num = _sshape_cylinder_num_indices(slices, stacks); + res.vertices.size = res.vertices.num * sizeof(sshape_vertex_t); + res.indices.size = res.indices.num * sizeof(uint16_t); + return res; +} + +SOKOL_API_IMPL sshape_sizes_t sshape_torus_sizes(uint32_t sides, uint32_t rings) { + SOKOL_ASSERT((sides >= 3) && (rings >= 3)); + sshape_sizes_t res = { 0 }; + res.vertices.num = _sshape_torus_num_vertices(sides, rings); + res.indices.num = _sshape_torus_num_indices(sides, rings); + res.vertices.size = res.vertices.num * sizeof(sshape_vertex_t); + res.indices.size = res.indices.num * sizeof(uint16_t); + return res; +} + +/* + Geometry layout for plane (4 tiles): + +--+--+--+--+ + |\ |\ |\ |\ | + | \| \| \| \| + +--+--+--+--+ 25 vertices (tiles + 1) * (tiles + 1) + |\ |\ |\ |\ | 32 triangles (tiles + 1) * (tiles + 1) * 2 + | \| \| \| \| + +--+--+--+--+ + |\ |\ |\ |\ | + | \| \| \| \| + +--+--+--+--+ + |\ |\ |\ |\ | + | \| \| \| \| + +--+--+--+--+ +*/ +SOKOL_API_IMPL sshape_buffer_t sshape_build_plane(const sshape_buffer_t* in_buf, const sshape_plane_t* in_params) { + SOKOL_ASSERT(in_buf && in_params); + const sshape_plane_t params = _sshape_plane_defaults(in_params); + const uint32_t num_vertices = _sshape_plane_num_vertices(params.tiles); + const uint32_t num_indices = _sshape_plane_num_indices(params.tiles); + sshape_buffer_t buf = *in_buf; + if (!_sshape_validate_buffer(&buf, num_vertices, num_indices)) { + buf.valid = false; + return buf; + } + buf.valid = true; + const uint16_t start_index = _sshape_base_index(&buf); + if (!params.merge) { + _sshape_advance_offset(&buf.vertices); + _sshape_advance_offset(&buf.indices); + } + + // write vertices + uint32_t rand_seed = 0x12345678; + const float x0 = -params.width * 0.5f; + const float z0 = params.depth * 0.5f; + const float dx = params.width / params.tiles; + const float dz = -params.depth / params.tiles; + const float duv = 1.0f / params.tiles; + _sshape_vec4_t tnorm = _sshape_vec4_norm(_sshape_mat4_mul(¶ms.transform, _sshape_vec4(0.0f, 1.0f, 0.0f, 0.0f))); + for (uint32_t ix = 0; ix <= params.tiles; ix++) { + for (uint32_t iz = 0; iz <= params.tiles; iz++) { + const _sshape_vec4_t pos = _sshape_vec4(x0 + dx*ix, 0.0f, z0 + dz*iz, 1.0f); + const _sshape_vec4_t tpos = _sshape_mat4_mul(¶ms.transform, pos); + const _sshape_vec2_t uv = _sshape_vec2(duv*ix, duv*iz); + const uint32_t color = params.random_colors ? _sshape_rand_color(&rand_seed) : params.color; + _sshape_add_vertex(&buf, tpos, tnorm, uv, color); + } + } + + // write indices + for (uint16_t j = 0; j < params.tiles; j++) { + for (uint16_t i = 0; i < params.tiles; i++) { + const uint16_t i0 = start_index + (j * (params.tiles + 1)) + i; + const uint16_t i1 = i0 + 1; + const uint16_t i2 = i0 + params.tiles + 1; + const uint16_t i3 = i2 + 1; + _sshape_add_triangle(&buf, i0, i1, i3); + _sshape_add_triangle(&buf, i0, i3, i2); + } + } + return buf; +} + +SOKOL_API_IMPL sshape_buffer_t sshape_build_box(const sshape_buffer_t* in_buf, const sshape_box_t* in_params) { + SOKOL_ASSERT(in_buf && in_params); + const sshape_box_t params = _sshape_box_defaults(in_params); + const uint32_t num_vertices = _sshape_box_num_vertices(params.tiles); + const uint32_t num_indices = _sshape_box_num_indices(params.tiles); + sshape_buffer_t buf = *in_buf; + if (!_sshape_validate_buffer(&buf, num_vertices, num_indices)) { + buf.valid = false; + return buf; + } + buf.valid = true; + const uint16_t start_index = _sshape_base_index(&buf); + if (!params.merge) { + _sshape_advance_offset(&buf.vertices); + _sshape_advance_offset(&buf.indices); + } + + // build vertices + uint32_t rand_seed = 0x12345678; + const float x0 = -params.width * 0.5f; + const float x1 = params.width * 0.5f; + const float y0 = -params.height * 0.5f; + const float y1 = params.height * 0.5f; + const float z0 = -params.depth * 0.5f; + const float z1 = params.depth * 0.5f; + const float dx = params.width / params.tiles; + const float dy = params.height / params.tiles; + const float dz = params.depth / params.tiles; + const float duv = 1.0f / params.tiles; + + // bottom/top vertices + for (uint32_t top_bottom = 0; top_bottom < 2; top_bottom++) { + _sshape_vec4_t pos = _sshape_vec4(0.0f, (0==top_bottom) ? y0:y1, 0.0f, 1.0f); + const _sshape_vec4_t norm = _sshape_vec4(0.0f, (0==top_bottom) ? -1.0f:1.0f, 0.0f, 0.0f); + const _sshape_vec4_t tnorm = _sshape_vec4_norm(_sshape_mat4_mul(¶ms.transform, norm)); + for (uint32_t ix = 0; ix <= params.tiles; ix++) { + pos.x = (0==top_bottom) ? (x0 + dx * ix) : (x1 - dx * ix); + for (uint32_t iz = 0; iz <= params.tiles; iz++) { + pos.z = z0 + dz * iz; + const _sshape_vec4_t tpos = _sshape_mat4_mul(¶ms.transform, pos); + const _sshape_vec2_t uv = _sshape_vec2(ix * duv, iz * duv); + const uint32_t color = params.random_colors ? _sshape_rand_color(&rand_seed) : params.color; + _sshape_add_vertex(&buf, tpos, tnorm, uv, color); + } + } + } + + // left/right vertices + for (uint32_t left_right = 0; left_right < 2; left_right++) { + _sshape_vec4_t pos = _sshape_vec4((0==left_right) ? x0:x1, 0.0f, 0.0f, 1.0f); + const _sshape_vec4_t norm = _sshape_vec4((0==left_right) ? -1.0f:1.0f, 0.0f, 0.0f, 0.0f); + const _sshape_vec4_t tnorm = _sshape_vec4_norm(_sshape_mat4_mul(¶ms.transform, norm)); + for (uint32_t iy = 0; iy <= params.tiles; iy++) { + pos.y = (0==left_right) ? (y1 - dy * iy) : (y0 + dy * iy); + for (uint32_t iz = 0; iz <= params.tiles; iz++) { + pos.z = z0 + dz * iz; + const _sshape_vec4_t tpos = _sshape_mat4_mul(¶ms.transform, pos); + const _sshape_vec2_t uv = _sshape_vec2(iy * duv, iz * duv); + const uint32_t color = params.random_colors ? _sshape_rand_color(&rand_seed) : params.color; + _sshape_add_vertex(&buf, tpos, tnorm, uv, color); + } + } + } + + // front/back vertices + for (uint32_t front_back = 0; front_back < 2; front_back++) { + _sshape_vec4_t pos = _sshape_vec4(0.0f, 0.0f, (0==front_back) ? z0:z1, 1.0f); + const _sshape_vec4_t norm = _sshape_vec4(0.0f, 0.0f, (0==front_back) ? -1.0f:1.0f, 0.0f); + const _sshape_vec4_t tnorm = _sshape_vec4_norm(_sshape_mat4_mul(¶ms.transform, norm)); + for (uint32_t ix = 0; ix <= params.tiles; ix++) { + pos.x = (0==front_back) ? (x1 - dx * ix) : (x0 + dx * ix); + for (uint32_t iy = 0; iy <= params.tiles; iy++) { + pos.y = y0 + dy * iy; + const _sshape_vec4_t tpos = _sshape_mat4_mul(¶ms.transform, pos); + const _sshape_vec2_t uv = _sshape_vec2(ix * duv, iy * duv); + const uint32_t color = params.random_colors ? _sshape_rand_color(&rand_seed) : params.color; + _sshape_add_vertex(&buf, tpos, tnorm, uv, color); + } + } + } + + // build indices + const uint16_t verts_per_face = (params.tiles + 1) * (params.tiles + 1); + for (uint16_t face = 0; face < 6; face++) { + uint16_t face_start_index = start_index + face * verts_per_face; + for (uint16_t j = 0; j < params.tiles; j++) { + for (uint16_t i = 0; i < params.tiles; i++) { + const uint16_t i0 = face_start_index + (j * (params.tiles + 1)) + i; + const uint16_t i1 = i0 + 1; + const uint16_t i2 = i0 + params.tiles + 1; + const uint16_t i3 = i2 + 1; + _sshape_add_triangle(&buf, i0, i1, i3); + _sshape_add_triangle(&buf, i0, i3, i2); + } + } + } + return buf; +} + +/* + Geometry layout for spheres is as follows (for 5 slices, 4 stacks): + + + + + + + + north pole + |\ |\ |\ |\ |\ + | \| \| \| \| \ + +--+--+--+--+--+ 30 vertices (slices + 1) * (stacks + 1) + |\ |\ |\ |\ |\ | 30 triangles (2 * slices * stacks) - (2 * slices) + | \| \| \| \| \| 2 orphaned vertices + +--+--+--+--+--+ + |\ |\ |\ |\ |\ | + | \| \| \| \| \| + +--+--+--+--+--+ + \ |\ |\ |\ |\ | + \| \| \| \| \| + + + + + + + south pole +*/ +SOKOL_API_IMPL sshape_buffer_t sshape_build_sphere(const sshape_buffer_t* in_buf, const sshape_sphere_t* in_params) { + SOKOL_ASSERT(in_buf && in_params); + const sshape_sphere_t params = _sshape_sphere_defaults(in_params); + const uint32_t num_vertices = _sshape_sphere_num_vertices(params.slices, params.stacks); + const uint32_t num_indices = _sshape_sphere_num_indices(params.slices, params.stacks); + sshape_buffer_t buf = *in_buf; + if (!_sshape_validate_buffer(&buf, num_vertices, num_indices)) { + buf.valid = false; + return buf; + } + buf.valid = true; + const uint16_t start_index = _sshape_base_index(&buf); + if (!params.merge) { + _sshape_advance_offset(&buf.vertices); + _sshape_advance_offset(&buf.indices); + } + + uint32_t rand_seed = 0x12345678; + const float pi = 3.14159265358979323846f; + const float two_pi = 2.0f * pi; + const float du = 1.0f / params.slices; + const float dv = 1.0f / params.stacks; + + // generate vertices + for (uint32_t stack = 0; stack <= params.stacks; stack++) { + const float stack_angle = (pi * stack) / params.stacks; + const float sin_stack = sinf(stack_angle); + const float cos_stack = cosf(stack_angle); + for (uint32_t slice = 0; slice <= params.slices; slice++) { + const float slice_angle = (two_pi * slice) / params.slices; + const float sin_slice = sinf(slice_angle); + const float cos_slice = cosf(slice_angle); + const _sshape_vec4_t norm = _sshape_vec4(-sin_slice * sin_stack, cos_stack, cos_slice * sin_stack, 0.0f); + const _sshape_vec4_t pos = _sshape_vec4(norm.x * params.radius, norm.y * params.radius, norm.z * params.radius, 1.0f); + const _sshape_vec4_t tnorm = _sshape_vec4_norm(_sshape_mat4_mul(¶ms.transform, norm)); + const _sshape_vec4_t tpos = _sshape_mat4_mul(¶ms.transform, pos); + const _sshape_vec2_t uv = _sshape_vec2(1.0f - slice * du, 1.0f - stack * dv); + const uint32_t color = params.random_colors ? _sshape_rand_color(&rand_seed) : params.color; + _sshape_add_vertex(&buf, tpos, tnorm, uv, color); + } + } + + // generate indices + { + // north-pole triangles + const uint16_t row_a = start_index; + const uint16_t row_b = row_a + params.slices + 1; + for (uint16_t slice = 0; slice < params.slices; slice++) { + _sshape_add_triangle(&buf, row_a + slice, row_b + slice, row_b + slice + 1); + } + } + // stack triangles + for (uint16_t stack = 1; stack < params.stacks - 1; stack++) { + const uint16_t row_a = start_index + stack * (params.slices + 1); + const uint16_t row_b = row_a + params.slices + 1; + for (uint16_t slice = 0; slice < params.slices; slice++) { + _sshape_add_triangle(&buf, row_a + slice, row_b + slice + 1, row_a + slice + 1); + _sshape_add_triangle(&buf, row_a + slice, row_b + slice, row_b + slice + 1); + } + } + { + // south-pole triangles + const uint16_t row_a = start_index + (params.stacks - 1) * (params.slices + 1); + const uint16_t row_b = row_a + params.slices + 1; + for (uint16_t slice = 0; slice < params.slices; slice++) { + _sshape_add_triangle(&buf, row_a + slice, row_b + slice + 1, row_a + slice + 1); + } + } + return buf; +} + +/* + Geometry for cylinders is as follows (2 stacks, 5 slices): + + + + + + + + + |\ |\ |\ |\ |\ + | \| \| \| \| \ + +--+--+--+--+--+ + +--+--+--+--+--+ 42 vertices (2 wasted) (slices + 1) * (stacks + 5) + |\ |\ |\ |\ |\ | 30 triangles (2 * slices * stacks) + (2 * slices) + | \| \| \| \| \| + +--+--+--+--+--+ + |\ |\ |\ |\ |\ | + | \| \| \| \| \| + +--+--+--+--+--+ + +--+--+--+--+--+ + \ |\ |\ |\ |\ | + \| \| \| \| \| + + + + + + + +*/ +static void _sshape_build_cylinder_cap_pole(sshape_buffer_t* buf, const sshape_cylinder_t* params, float pos_y, float norm_y, float du, float v, uint32_t* rand_seed) { + const _sshape_vec4_t tnorm = _sshape_vec4_norm(_sshape_mat4_mul(¶ms->transform, _sshape_vec4(0.0f, norm_y, 0.0f, 0.0f))); + const _sshape_vec4_t tpos = _sshape_mat4_mul(¶ms->transform, _sshape_vec4(0.0f, pos_y, 0.0f, 1.0f)); + for (uint32_t slice = 0; slice <= params->slices; slice++) { + const _sshape_vec2_t uv = _sshape_vec2(slice * du, 1.0f - v); + const uint32_t color = params->random_colors ? _sshape_rand_color(rand_seed) : params->color; + _sshape_add_vertex(buf, tpos, tnorm, uv, color); + } +} + +static void _sshape_build_cylinder_cap_ring(sshape_buffer_t* buf, const sshape_cylinder_t* params, float pos_y, float norm_y, float du, float v, uint32_t* rand_seed) { + const float two_pi = 2.0f * 3.14159265358979323846f; + const _sshape_vec4_t tnorm = _sshape_vec4_norm(_sshape_mat4_mul(¶ms->transform, _sshape_vec4(0.0f, norm_y, 0.0f, 0.0f))); + for (uint32_t slice = 0; slice <= params->slices; slice++) { + const float slice_angle = (two_pi * slice) / params->slices; + const float sin_slice = sinf(slice_angle); + const float cos_slice = cosf(slice_angle); + const _sshape_vec4_t pos = _sshape_vec4(sin_slice * params->radius, pos_y, cos_slice * params->radius, 1.0f); + const _sshape_vec4_t tpos = _sshape_mat4_mul(¶ms->transform, pos); + const _sshape_vec2_t uv = _sshape_vec2(slice * du, 1.0f - v); + const uint32_t color = params->random_colors ? _sshape_rand_color(rand_seed) : params->color; + _sshape_add_vertex(buf, tpos, tnorm, uv, color); + } +} + +SOKOL_API_DECL sshape_buffer_t sshape_build_cylinder(const sshape_buffer_t* in_buf, const sshape_cylinder_t* in_params) { + SOKOL_ASSERT(in_buf && in_params); + const sshape_cylinder_t params = _sshape_cylinder_defaults(in_params); + const uint32_t num_vertices = _sshape_cylinder_num_vertices(params.slices, params.stacks); + const uint32_t num_indices = _sshape_cylinder_num_indices(params.slices, params.stacks); + sshape_buffer_t buf = *in_buf; + if (!_sshape_validate_buffer(&buf, num_vertices, num_indices)) { + buf.valid = false; + return buf; + } + buf.valid = true; + const uint16_t start_index = _sshape_base_index(&buf); + if (!params.merge) { + _sshape_advance_offset(&buf.vertices); + _sshape_advance_offset(&buf.indices); + } + + uint32_t rand_seed = 0x12345678; + const float two_pi = 2.0f * 3.14159265358979323846f; + const float du = 1.0f / params.slices; + const float dv = 1.0f / (params.stacks + 2); + const float y0 = params.height * 0.5f; + const float y1 = -params.height * 0.5f; + const float dy = params.height / params.stacks; + + // generate vertices + _sshape_build_cylinder_cap_pole(&buf, ¶ms, y0, 1.0f, du, 0.0f, &rand_seed); + _sshape_build_cylinder_cap_ring(&buf, ¶ms, y0, 1.0f, du, dv, &rand_seed); + for (uint32_t stack = 0; stack <= params.stacks; stack++) { + const float y = y0 - dy * stack; + const float v = dv * stack + dv; + for (uint32_t slice = 0; slice <= params.slices; slice++) { + const float slice_angle = (two_pi * slice) / params.slices; + const float sin_slice = sinf(slice_angle); + const float cos_slice = cosf(slice_angle); + const _sshape_vec4_t pos = _sshape_vec4(sin_slice * params.radius, y, cos_slice * params.radius, 1.0f); + const _sshape_vec4_t tpos = _sshape_mat4_mul(¶ms.transform, pos); + const _sshape_vec4_t norm = _sshape_vec4(sin_slice, 0.0f, cos_slice, 0.0f); + const _sshape_vec4_t tnorm = _sshape_vec4_norm(_sshape_mat4_mul(¶ms.transform, norm)); + const _sshape_vec2_t uv = _sshape_vec2(slice * du, 1.0f - v); + const uint32_t color = params.random_colors ? _sshape_rand_color(&rand_seed) : params.color; + _sshape_add_vertex(&buf, tpos, tnorm, uv, color); + } + } + _sshape_build_cylinder_cap_ring(&buf, ¶ms, y1, -1.0f, du, 1.0f - dv, &rand_seed); + _sshape_build_cylinder_cap_pole(&buf, ¶ms, y1, -1.0f, du, 1.0f, &rand_seed); + + // generate indices + { + // top-cap indices + const uint16_t row_a = start_index; + const uint16_t row_b = row_a + params.slices + 1; + for (uint16_t slice = 0; slice < params.slices; slice++) { + _sshape_add_triangle(&buf, row_a + slice, row_b + slice + 1, row_b + slice); + } + } + // shaft triangles + for (uint16_t stack = 0; stack < params.stacks; stack++) { + const uint16_t row_a = start_index + (stack + 2) * (params.slices + 1); + const uint16_t row_b = row_a + params.slices + 1; + for (uint16_t slice = 0; slice < params.slices; slice++) { + _sshape_add_triangle(&buf, row_a + slice, row_a + slice + 1, row_b + slice + 1); + _sshape_add_triangle(&buf, row_a + slice, row_b + slice + 1, row_b + slice); + } + } + { + // bottom-cap indices + const uint16_t row_a = start_index + (params.stacks + 3) * (params.slices + 1); + const uint16_t row_b = row_a + params.slices + 1; + for (uint16_t slice = 0; slice < params.slices; slice++) { + _sshape_add_triangle(&buf, row_a + slice, row_a + slice + 1, row_b + slice + 1); + } + } + return buf; +} + +/* + Geometry layout for torus (sides = 4, rings = 5): + + +--+--+--+--+--+ + |\ |\ |\ |\ |\ | + | \| \| \| \| \| + +--+--+--+--+--+ 30 vertices (sides + 1) * (rings + 1) + |\ |\ |\ |\ |\ | 40 triangles (2 * sides * rings) + | \| \| \| \| \| + +--+--+--+--+--+ + |\ |\ |\ |\ |\ | + | \| \| \| \| \| + +--+--+--+--+--+ + |\ |\ |\ |\ |\ | + | \| \| \| \| \| + +--+--+--+--+--+ +*/ +SOKOL_API_IMPL sshape_buffer_t sshape_build_torus(const sshape_buffer_t* in_buf, const sshape_torus_t* in_params) { + SOKOL_ASSERT(in_buf && in_params); + const sshape_torus_t params = _sshape_torus_defaults(in_params); + const uint32_t num_vertices = _sshape_torus_num_vertices(params.sides, params.rings); + const uint32_t num_indices = _sshape_torus_num_indices(params.sides, params.rings); + sshape_buffer_t buf = *in_buf; + if (!_sshape_validate_buffer(&buf, num_vertices, num_indices)) { + buf.valid = false; + return buf; + } + buf.valid = true; + const uint16_t start_index = _sshape_base_index(&buf); + if (!params.merge) { + _sshape_advance_offset(&buf.vertices); + _sshape_advance_offset(&buf.indices); + } + + uint32_t rand_seed = 0x12345678; + const float two_pi = 2.0f * 3.14159265358979323846f; + const float dv = 1.0f / params.sides; + const float du = 1.0f / params.rings; + + // generate vertices + for (uint32_t side = 0; side <= params.sides; side++) { + const float phi = (side * two_pi) / params.sides; + const float sin_phi = sinf(phi); + const float cos_phi = cosf(phi); + for (uint32_t ring = 0; ring <= params.rings; ring++) { + const float theta = (ring * two_pi) / params.rings; + const float sin_theta = sinf(theta); + const float cos_theta = cosf(theta); + + // torus surface position + const float spx = sin_theta * (params.radius - (params.ring_radius * cos_phi)); + const float spy = sin_phi * params.ring_radius; + const float spz = cos_theta * (params.radius - (params.ring_radius * cos_phi)); + + // torus position with ring-radius zero (for normal computation) + const float ipx = sin_theta * params.radius; + const float ipy = 0.0f; + const float ipz = cos_theta * params.radius; + + const _sshape_vec4_t pos = _sshape_vec4(spx, spy, spz, 1.0f); + const _sshape_vec4_t norm = _sshape_vec4(spx - ipx, spy - ipy, spz - ipz, 0.0f); + const _sshape_vec4_t tpos = _sshape_mat4_mul(¶ms.transform, pos); + const _sshape_vec4_t tnorm = _sshape_vec4_norm(_sshape_mat4_mul(¶ms.transform, norm)); + const _sshape_vec2_t uv = _sshape_vec2(ring * du, 1.0f - side * dv); + const uint32_t color = params.random_colors ? _sshape_rand_color(&rand_seed) : params.color; + _sshape_add_vertex(&buf, tpos, tnorm, uv, color); + } + } + + // generate indices + for (uint16_t side = 0; side < params.sides; side++) { + const uint16_t row_a = start_index + side * (params.rings + 1); + const uint16_t row_b = row_a + params.rings + 1; + for (uint16_t ring = 0; ring < params.rings; ring++) { + _sshape_add_triangle(&buf, row_a + ring, row_a + ring + 1, row_b + ring + 1); + _sshape_add_triangle(&buf, row_a + ring, row_b + ring + 1, row_b + ring); + } + } + return buf; +} + +SOKOL_API_IMPL sg_buffer_desc sshape_vertex_buffer_desc(const sshape_buffer_t* buf) { + SOKOL_ASSERT(buf && buf->valid); + sg_buffer_desc desc = { 0 }; + if (buf->valid) { + desc.size = buf->vertices.data_size; + desc.type = SG_BUFFERTYPE_VERTEXBUFFER; + desc.usage = SG_USAGE_IMMUTABLE; + desc.content = buf->vertices.buffer_ptr; + } + return desc; +} + +SOKOL_API_IMPL sg_buffer_desc sshape_index_buffer_desc(const sshape_buffer_t* buf) { + SOKOL_ASSERT(buf && buf->valid); + sg_buffer_desc desc = { 0 }; + if (buf->valid) { + desc.size = buf->indices.data_size; + desc.type = SG_BUFFERTYPE_INDEXBUFFER; + desc.usage = SG_USAGE_IMMUTABLE; + desc.content = buf->indices.buffer_ptr; + } + return desc; +} + +SOKOL_API_DECL sshape_element_range_t sshape_element_range(const sshape_buffer_t* buf) { + SOKOL_ASSERT(buf && buf->valid); + SOKOL_ASSERT(buf->indices.shape_offset < buf->indices.data_size); + SOKOL_ASSERT(0 == (buf->indices.shape_offset & (sizeof(uint16_t) - 1))); + SOKOL_ASSERT(0 == (buf->indices.data_size & (sizeof(uint16_t) - 1))); + sshape_element_range_t range = { 0 }; + range.base_element = buf->indices.shape_offset / sizeof(uint16_t); + if (buf->valid) { + range.num_elements = (buf->indices.data_size - buf->indices.shape_offset) / sizeof(uint16_t); + } + else { + range.num_elements = 0; + } + return range; +} + +SOKOL_API_IMPL sg_buffer_layout_desc sshape_buffer_layout_desc(void) { + sg_buffer_layout_desc desc = { 0 }; + desc.stride = sizeof(sshape_vertex_t); + return desc; +} + +SOKOL_API_IMPL sg_vertex_attr_desc sshape_position_attr_desc(void) { + sg_vertex_attr_desc desc = { 0 }; + desc.offset = offsetof(sshape_vertex_t, x); + desc.format = SG_VERTEXFORMAT_FLOAT3; + return desc; +} + +SOKOL_API_IMPL sg_vertex_attr_desc sshape_normal_attr_desc(void) { + sg_vertex_attr_desc desc = { 0 }; + desc.offset = offsetof(sshape_vertex_t, normal); + desc.format = SG_VERTEXFORMAT_BYTE4N; + return desc; +} + +SOKOL_API_IMPL sg_vertex_attr_desc sshape_texcoord_attr_desc(void) { + sg_vertex_attr_desc desc = { 0 }; + desc.offset = offsetof(sshape_vertex_t, u); + desc.format = SG_VERTEXFORMAT_USHORT2N; + return desc; +} + +SOKOL_API_IMPL sg_vertex_attr_desc sshape_color_attr_desc(void) { + sg_vertex_attr_desc desc = { 0 }; + desc.offset = offsetof(sshape_vertex_t, color); + desc.format = SG_VERTEXFORMAT_UBYTE4N; + return desc; +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +#endif // SOKOL_SHAPE_IMPL |