aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOliver Collyer <ovcollyer@mac.com>2020-11-22 17:30:06 +0000
committerOliver Collyer <ovcollyer@mac.com>2020-11-22 17:30:06 +0000
commitbbae0015420909c92da00ab5f0816301a1f06031 (patch)
treef0c190069b4968b690b244b24e37e6e3ff2211e0
parenta49cfdc7811d90312405fdcd1c712c1e775d3b43 (diff)
parent4aef20682bf70e3a3f3dc3634bcce902fd600b08 (diff)
Merge remote-tracking branch 'floooh/master'
-rw-r--r--README.md26
-rw-r--r--sokol_app.h17
-rw-r--r--sokol_gfx.h2
-rw-r--r--util/README.md19
-rw-r--r--util/sokol_gfx_imgui.h6
-rw-r--r--util/sokol_shape.h1406
6 files changed, 1439 insertions, 37 deletions
diff --git a/README.md b/README.md
index 005a063a..81720c3f 100644
--- a/README.md
+++ b/README.md
@@ -6,14 +6,20 @@ Simple
[STB-style](https://github.com/nothings/stb/blob/master/docs/stb_howto.txt)
cross-platform libraries for C and C++, written in C.
-[See what's new](#updates) (**03-Nov-2020** sokol_app.h: drag'n'drop support for HTML5/WASM)
+[See what's new](#updates) (**17-Nov-2020** new utility header: sokol_shape.h)
[Live Samples](https://floooh.github.io/sokol-html5/index.html) via WASM.
+Sample code is in a separate repo: https://github.com/floooh/sokol-samples
+
[LearnOpenGL examples ported to sokol-gfx](https://www.geertarien.com/learnopengl-examples-html5/) by @geertarien (cool stuff!)
[Dear ImGui starterkit](https://github.com/floooh/cimgui-sokol-starterkit) a self-contained starterkit for writing Dear ImGui apps in C.
+Command line tools: https://github.com/floooh/sokol-tools
+
+Tiny 8-bit emulators: https://floooh.github.io/tiny8bit/
+
## Core libraries
- [**sokol\_gfx.h**](https://github.com/floooh/sokol/blob/master/sokol_gfx.h): 3D-API wrapper (GL + Metal + D3D11)
@@ -31,6 +37,7 @@ cross-platform libraries for C and C++, written in C.
- [**sokol\_gfx\_imgui.h**](https://github.com/floooh/sokol/blob/master/util/sokol_gfx_imgui.h): debug-inspection UI for sokol_gfx.h (implemented with Dear ImGui)
- [**sokol\_debugtext.h**](https://github.com/floooh/sokol/blob/master/util/sokol_debugtext.h): a simple text renderer using vintage home computer fonts
- [**sokol\_memtrack.h**](https://github.com/floooh/sokol/blob/master/util/sokol_memtrack.h): easily track memory allocations in sokol headers
+- [**sokol\_shape.h**](https://github.com/floooh/sokol/blob/master/util/sokol_shape.h): generate simple shapes and plug them into sokol-gfx resource creation structs
## Notes
@@ -40,12 +47,6 @@ minimal footprint on the web platform while still being useful.
The core headers are standalone and can be used independently from each other.
-Sample code is in a separate repo: https://github.com/floooh/sokol-samples
-
-Command line tools: https://github.com/floooh/sokol-tools
-
-Tiny 8-bit emulators: https://floooh.github.io/tiny8bit/
-
### Why C:
- easier integration with other languages
@@ -438,6 +439,17 @@ Emulators](https://floooh.github.io/tiny8bit/) for more interesting usage exampl
# Updates
+- **17-Nov-2020**: A new utility header **sokol_shape.h** to generate
+ vertices+indices for simple shapes (plane, box, sphere, cylinder and torus),
+ which seamlessly plug into the sokol_gfx.h resource creation functions. As
+ with most new utility headers, the initial functionality is a bit bare bones
+ and the public API shouldn't be considered stable yet. Check the sokol-samples
+ webpage for new and updates samples: https://floooh.github.io/sokol-html5/
+
+- **08-Nov-2020** PSA: It appears that RenderDoc v1.10 chokes on the new
+ D3D11/DXGI swapchain code from 10-Oct-2020 in sokol_app.h. The current
+ RenderDoc Nightly Build works, so I guess in v1.11 everything will be fine.
+
- **03-Nov-2020**: sokol_app.h: the missing drag'n'drop support for HTML5/WASM
has been added. This adds two platform-specific functions
```sapp_html5_get_dropped_file_size()``` and
diff --git a/sokol_app.h b/sokol_app.h
index 645dd177..aa67a216 100644
--- a/sokol_app.h
+++ b/sokol_app.h
@@ -2826,9 +2826,9 @@ _SOKOL_PRIVATE void _sapp_macos_app_event(sapp_event_type type) {
_SOKOL_PRIVATE void _sapp_macos_update_dimensions(void) {
#if defined(SOKOL_METAL)
- const CGSize fb_size = [_sapp.macos.view drawableSize];
- _sapp.framebuffer_width = fb_size.width;
- _sapp.framebuffer_height = fb_size.height;
+ const NSRect fb_rect = [_sapp.macos.view bounds];
+ _sapp.framebuffer_width = fb_rect.size.width * _sapp.dpi_scale;
+ _sapp.framebuffer_height = fb_rect.size.height * _sapp.dpi_scale;
#elif defined(SOKOL_GLCORE33)
const NSRect fb_rect = [_sapp.macos.view convertRectToBacking:[_sapp.macos.view frame]];
_sapp.framebuffer_width = fb_rect.size.width;
@@ -2850,6 +2850,12 @@ _SOKOL_PRIVATE void _sapp_macos_update_dimensions(void) {
_sapp.window_height = 1;
}
_sapp.dpi_scale = (float)_sapp.framebuffer_width / (float)_sapp.window_width;
+
+ /* also make sure the MTKView drawable size is uptodate */
+ #if defined(SOKOL_METAL)
+ CGSize drawable_size = { (CGFloat) _sapp.framebuffer_width, (CGFloat) _sapp.framebuffer_height };
+ _sapp.macos.view.drawableSize = drawable_size;
+ #endif
}
_SOKOL_PRIVATE void _sapp_macos_toggle_fullscreen(void) {
@@ -2997,12 +3003,9 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) {
_sapp.macos.view.colorPixelFormat = MTLPixelFormatBGRA8Unorm;
_sapp.macos.view.depthStencilPixelFormat = MTLPixelFormatDepth32Float_Stencil8;
_sapp.macos.view.sampleCount = _sapp.sample_count;
+ _sapp.macos.view.autoResizeDrawable = false;
_sapp.macos.window.contentView = _sapp.macos.view;
[_sapp.macos.window makeFirstResponder:_sapp.macos.view];
- if (!_sapp.desc.high_dpi) {
- CGSize drawable_size = { (CGFloat) _sapp.framebuffer_width, (CGFloat) _sapp.framebuffer_height };
- _sapp.macos.view.drawableSize = drawable_size;
- }
_sapp.macos.view.layer.magnificationFilter = kCAFilterNearest;
#elif defined(SOKOL_GLCORE33)
NSOpenGLPixelFormatAttribute attrs[32];
diff --git a/sokol_gfx.h b/sokol_gfx.h
index ff371533..8d423d63 100644
--- a/sokol_gfx.h
+++ b/sokol_gfx.h
@@ -11492,7 +11492,7 @@ _SOKOL_PRIVATE void _sg_wgpu_destroy_image(_sg_image_t* img) {
/*
How BindGroups work in WebGPU:
- - up to 4 bind groups can be bound simultanously
+ - up to 4 bind groups can be bound simultaneously
- up to 16 bindings per bind group
- 'binding' slots are local per bind group
- in the shader:
diff --git a/util/README.md b/util/README.md
deleted file mode 100644
index 44d385d2..00000000
--- a/util/README.md
+++ /dev/null
@@ -1,19 +0,0 @@
-# Sokol Utility Headers
-
-These are optional utility headers on top of the Sokol headers. Unlike the
-'core headers' they are not standalone but depend on other Sokol headers
-and sometimes also external libraries.
-
-### What's in here:
-
-- **sokol_imgui.h**: implements a renderer for [Dear ImGui](https://github.com/ocornut/imgui) on top of sokol_gfx.h and sokol_app.h (the latter being optional if you do your own input-forwarding to ImGui), the implementation
-can be compiled as C++ or C.
-- **sokol_gfx_imgui.h**: a debug-inspection UI for sokol_gfx.h, this hooks into the sokol-gfx API and lets you inspect resource objects and captured API calls
-- **sokol_gl.h**: an OpenGL 1.x style immediate-mode rendering API
-on top of sokol_gfx.h
-- **sokol_fontstash.h**: a renderer for [fontstash.h](https://github.com/memononen/fontstash) on
-on top of sokol_gl.h
-- **sokol_debugtext.h**: a simple text renderer using 8-bit home computer fonts
-- **sokol_memtrack.h**: simple utility header to easily track memory allocations in sokol headers
-
-See the embedded header-documentation for build- and usage-details.
diff --git a/util/sokol_gfx_imgui.h b/util/sokol_gfx_imgui.h
index dd1da8bc..e012e33e 100644
--- a/util/sokol_gfx_imgui.h
+++ b/util/sokol_gfx_imgui.h
@@ -698,8 +698,8 @@ _SOKOL_PRIVATE void igSetTooltip(const char* fmt,...) {
ImGui::SetTooltipV(fmt,args);
va_end(args);
}
-_SOKOL_PRIVATE bool igSliderFloat(const char* label,float* v,float v_min,float v_max,const char* format,float power) {
- return ImGui::SliderFloat(label,v,v_min,v_max,format,power);
+_SOKOL_PRIVATE bool igSliderFloat(const char* label,float* v,float v_min,float v_max,const char* format,ImGuiSliderFlags flags) {
+ return ImGui::SliderFloat(label,v,v_min,v_max,format,flags);
}
_SOKOL_PRIVATE void igImage(ImTextureID user_texture_id,const ImVec2 size,const ImVec2 uv0,const ImVec2 uv1,const ImVec4 tint_col,const ImVec4 border_col) {
return ImGui::Image(user_texture_id,size,uv0,uv1,tint_col,border_col);
@@ -2762,7 +2762,7 @@ _SOKOL_PRIVATE void _sg_imgui_draw_embedded_image(sg_imgui_t* ctx, sg_image img,
sg_imgui_image_t* img_ui = &ctx->images.slots[_sg_imgui_slot_index(img.id)];
if (_sg_imgui_image_renderable(img_ui->desc.type, img_ui->desc.pixel_format)) {
igPushIDInt((int)img.id);
- igSliderFloat("Scale", scale, 0.125f, 8.0f, "%.3f", 2.0f);
+ igSliderFloat("Scale", scale, 0.125f, 8.0f, "%.3f", ImGuiSliderFlags_Logarithmic);
float w = (float)img_ui->desc.width * (*scale);
float h = (float)img_ui->desc.height * (*scale);
igImage((ImTextureID)(intptr_t)img.id, IMVEC2(w, h), IMVEC2(0,0), IMVEC2(1,1), IMVEC4(1,1,1,1), IMVEC4(0,0,0,0));
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(&params.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(&params.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(&params.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(&params.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(&params.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(&params.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(&params.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(&params.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(&params.transform, norm));
+ const _sshape_vec4_t tpos = _sshape_mat4_mul(&params.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(&params->transform, _sshape_vec4(0.0f, norm_y, 0.0f, 0.0f)));
+ const _sshape_vec4_t tpos = _sshape_mat4_mul(&params->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(&params->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(&params->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, &params, y0, 1.0f, du, 0.0f, &rand_seed);
+ _sshape_build_cylinder_cap_ring(&buf, &params, 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(&params.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(&params.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, &params, y1, -1.0f, du, 1.0f - dv, &rand_seed);
+ _sshape_build_cylinder_cap_pole(&buf, &params, 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(&params.transform, pos);
+ const _sshape_vec4_t tnorm = _sshape_vec4_norm(_sshape_mat4_mul(&params.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