commit d649f58845e506734780a27f55a3088855b38fac
parent 30b0d3191144fd86e2eb9824d5e4c209a7dc959f
Author: Jack Mordaunt <jackmordaunt.dev@gmail.com>
Date: Wed, 9 Jul 2025 13:15:43 -0300
Merge commit '9c2fd5df938450f85b55e72fe8c6292834e18e9b' as 'odin-c-bindgen'
Diffstat:
62 files changed, 28124 insertions(+), 0 deletions(-)
diff --git a/odin-c-bindgen/.github/workflows/build.yml b/odin-c-bindgen/.github/workflows/build.yml
@@ -0,0 +1,48 @@
+name: Build
+
+on:
+ push:
+ branches:
+ - main
+ pull_request:
+ branches:
+ - main
+
+
+jobs:
+ build_windows:
+ name: Windows
+ runs-on: windows-latest
+ steps:
+ - uses: laytan/setup-odin@v2
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
+
+ - uses: actions/checkout@v4
+
+ - name: Build bindgen
+ run: odin build src -out:bindgen.exe -vet -strict-style
+
+ - name: Raylib
+ run: |
+ .\bindgen.exe examples/raylib
+ odin build examples/raylib/test
+
+ - name: Box2D
+ run: |
+ .\bindgen.exe examples/box2d
+ odin build examples/box2d/test
+
+ - name: pdfio
+ run: |
+ .\bindgen.exe examples/pdfio
+ cd examples/pdfio/test
+ copy ..\pdfio\pdfio1.dll .
+ copy ..\pdfio\zlib.dll .
+ odin run .
+
+ - name: ufbx
+ run: |
+ .\bindgen.exe examples/ufbx
+ odin build examples/ufbx/test
+
diff --git a/odin-c-bindgen/.gitignore b/odin-c-bindgen/.gitignore
@@ -0,0 +1,6 @@
+*.pdb
+*.rdi
+*.exe
+*debug_dump.json
+*macro_dump.h
+scrap
+\ No newline at end of file
diff --git a/odin-c-bindgen/LICENSE b/odin-c-bindgen/LICENSE
@@ -0,0 +1,7 @@
+Copyright (c) 2025 Karl Zylinski
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+\ No newline at end of file
diff --git a/odin-c-bindgen/README.md b/odin-c-bindgen/README.md
@@ -0,0 +1,185 @@
+# odin-c-bindgen: Generate Odin bindings for C libraries
+
+This generator makes it possible to quickly generate C library bindings for the Odin Programming Language.
+
+Features:
+- Easy to get started with. Can generate bindings from a folder of headers.
+- Generates nice-looking bindings that retain comments. Example: [Generated Raylib bindings](https://github.com/karl-zylinski/odin-c-bindgen/blob/main/examples/raylib/raylib/raylib.odin).
+- Simplicity. The generator is simple enough that you can modify it, should the need arise.
+- Configurable. Easy to override types and turn enums into bit_sets, etc. More info [below](#configuration) and [in the examples](https://github.com/karl-zylinski/odin-c-bindgen/blob/main/examples/raylib/bindgen.sjson).
+
+## Requirements
+- Odin
+- clang (download from https://llvm.org/ or using the clang payload in Visual Studio installer)
+
+> [!NOTE]
+> clang is used for analysing the C headers and outputting an AST. The binding generator then processses that AST into Odin code.
+
+## Getting started
+
+1. Build the generator: `odin build src -out:bindgen.exe` (replace `.exe` with `.bin` on mac/Linux)
+2. Make a folder. Inside it, put the C headers (`.h` files) of the library you want to generate bindings for.
+3. Execute `bindgen the_folder`
+4. Bindings can be found inside `the_folder/the_folder`
+5. To get more control of how the generation happens, use a `bindgen.sjson` file to. See how in the next section, or look in the `examples` folder.
+
+> [!WARNING]
+> The generator assumes that the `clang` executable is in your PATH, i.e. that it is accessible system-wide.
+
+## Configuration
+
+Add a `bindgen.sjson` to your bindings folder. I.e. inside the folder you feed into `bindgen`. Below is an example. See the [examples folder](https://github.com/karl-zylinski/odin-c-bindgen/tree/main/examples) for more advanced examples.
+
+> NOTE: Config uses the function/type names as found in header files.
+
+```sjson
+// Inputs can be folders or files. It will look for header (.h) files inside
+// any folder. The bindings will be based on those headers. Also, any .lib,
+// .odin, .dll etc will be copied to the output folder.
+inputs = [
+ "input"
+]
+
+// Files to ignore when processing files in the inputs folders
+ignore_inputs = [
+ // "file.h"
+]
+
+// Output folder: One .odin file per processed header
+output_folder = "my_lib"
+
+// Remove this prefix from types names (structs, enums, etc)
+remove_type_prefix = ""
+
+// Remove this prefix from macro names
+remove_macro_prefix = ""
+
+// Remove this prefix from function names (and add it as link_prefix) to the foreign group
+remove_function_prefix = ""
+
+// Only include things that has this prefix
+required_prefix = ""
+
+// Set to true translate type names to Ada_Case
+force_ada_case_types = false
+
+// Single lib file to import
+import_lib = "my_lib.lib"
+
+// Use this file instead of `import_lib`. This is a whole file that is pasted near
+// the top of the file. In it you can do platform-specific library imports etc.
+imports_file = ""
+
+// For package line at top of output files
+package_name = "my_lib"
+
+// "Old_Name" = "New_Name",
+rename = {
+}
+
+// Turns an enum into a bit_set. Converts the values of the enum into
+// appropriate values for a bit_set. Creates a bit_set type that uses the enum.
+// Properly removes enum values with value 0. Translates the enum values using
+// a log2 procedure.
+bit_setify = {
+ // "Pre_Existing_Enum_Type" = "New_Bit_Set_Type"
+}
+
+// Completely override the definition of a type. The type needs to be pre-existing.
+type_overrides = {
+ // "Vector2" = "[2]f32"
+}
+
+// Override the type of a struct field. Note that a plain `[^]` can be used to
+// modify the existing type.
+struct_field_overrides = {
+ // "Some_Type.some_field" = "My_Type"
+}
+
+// Overrides the type of a procedure parameter or return value. For a parameter
+// use the key Proc_Name.parameter_name. For a return value use the key Proc_Name.
+// Note that a plain `[^]` and `#by_ptr` can be used to modify the existing type.
+procedure_type_overrides = {
+ // "SetConfigFlags.flags" = "ConfigFlags"
+ // "GetKeyPressed" = "KeyboardKey"
+}
+
+// Inject a new type before another type. Use `rename` to just rename
+// a pre-existing type.
+inject_before = {
+ // "Some_Type" = "New_Type :: distinct int"
+}
+
+// For typedefs that don't resolve to anything: Put them in here to create
+// empty structs with that name.
+opaque_types = [
+ // "Some_Type"
+]
+
+// additional include paths to send into clang. While generating the bindings
+// clang will look into this path in search for included headers.
+clang_include_paths = [
+ // "include"
+]
+
+// Writes the clang JSON ast dump for debug inspection (in output folder)
+debug_dump_json_ast = false
+
+// Writes the clang preprocessor macro dump
+debug_dump_macros = false
+```
+
+## FAQ and common problems
+
+### Why didn't my bindings generate correctly?
+
+If your bindings don't work because of a missing C type, then chances are I've forgotten to add support for it. Try adding it to `c_type_mapping` inside `bindgen.odin` and recompile the generator.
+
+If you have some library that is hard to generate bindings for, then submit an issue on this GitHub page and provide the headers in a zip. I'll try to help if I can find some time.
+
+The generator won't bring along any inline functions.
+
+### How do I include a pre-made Odin file?
+
+Add it to the input folder.
+
+### How do I manually specify which libraries to load on different platforms etc?
+
+Use `imports_file` in `bindgen.sjson`. See `examples/raylib`
+
+### How can I turn an enum into a bit_set?
+
+In `bindgen.sjson`:
+
+```
+bit_setify = {
+ "your_enum" = "the_bit_set_type"
+}
+```
+
+This will create a type `the_bit_set_type :: bit_set[your_enum; c.int`.
+
+It will also translate the values of the enum by calculating their log2 value (that gives you the bit index instead of the integer value corresponding to that bit).
+
+### My headers can't find other headers in the same folder
+
+If the generator is processing `include/some_folder/header.h` and it can't find some other header `include/some_folder/something.h`, then add `include` to the include search path by adding he following to `bindgen.sjson`:
+
+```
+clang_include_path = "include"
+```
+
+### My forward-declared type is missing in the bindings
+
+Add the typename to `opaque_types` in `bindgen.sjson`:
+```
+opaque_types = [
+ "The_Type"
+]
+```
+
+You should put in the translated type name, as it would appear in the Odin file (will all prefixes removed, etc).
+
+## Acknowledgements
+
+This generator was inspired by floooh's Sokol bindgen: https://github.com/floooh/sokol/tree/master/bindgen
diff --git a/odin-c-bindgen/examples/.gitignore b/odin-c-bindgen/examples/.gitignore
@@ -0,0 +1 @@
+ffmpeg
+\ No newline at end of file
diff --git a/odin-c-bindgen/examples/README.md b/odin-c-bindgen/examples/README.md
@@ -0,0 +1,10 @@
+This folder contains examples of how to use the binding generator and how to configure it using `bindgen.sjson`.
+
+Note that the bindings created by these examples are _not_ production ready. For example, within `bindgen.sjson` of each binding I haven't added every procedure parameter that should be a multi-pointer or use `#by_ptr`.
+
+## How to generate the bindings
+
+Make sure you've compiled the bindings generator from the source in the `../src` folder. Then run:
+`bindgen examples/raylib` to create the raylib bindings. There's a test program in each example that uses the bindings.
+
+Note that I provide pre-generated versions of the bindings, for example in `raylib/raylib` folder. They are just there to make the repository more informative while browsing online.
+\ No newline at end of file
diff --git a/odin-c-bindgen/examples/box2d/.gitignore b/odin-c-bindgen/examples/box2d/.gitignore
@@ -0,0 +1 @@
+box2d/*.lib
+\ No newline at end of file
diff --git a/odin-c-bindgen/examples/box2d/bindgen.sjson b/odin-c-bindgen/examples/box2d/bindgen.sjson
@@ -0,0 +1,36 @@
+// See README.md in root of repository for documentation and more configuration options.
+
+inputs = [
+ "input"
+]
+
+output_folder = "box2d"
+remove_type_prefix = "b2"
+remove_function_prefix = "b2"
+remove_macro_prefix = "B2_"
+required_prefix = "b2"
+import_lib = "box2d.lib"
+package_name = "box2d"
+
+struct_field_overrides = {
+ "b2DynamicTree.nodes" = "[^]"
+ "b2ChainDef.points" = "[^]"
+ "b2ChainDef.materials" = "[^]"
+
+ // This is not a complete override list, it's just an example.
+}
+
+procedure_type_overrides = {
+ "b2CreateWorld.def" = "#by_ptr"
+ "b2CreateBody.def" = "#by_ptr"
+ "b2CreatePolygonShape.def" = "#by_ptr"
+ "b2CreatePolygonShape.polygon" = "#by_ptr"
+ "b2CreateCircleShape.def" = "#by_ptr"
+ "b2CreateCircleShape.circle" = "#by_ptr"
+
+ // This is not a complete override list, it's just an example.
+}
+
+inject_before = {
+ "b2DynamicTree" = "TreeNode :: struct {}"
+}
diff --git a/odin-c-bindgen/examples/box2d/box2d/base.odin b/odin-c-bindgen/examples/box2d/box2d/base.odin
@@ -0,0 +1,73 @@
+// SPDX-FileCopyrightText: 2023 Erin Catto
+// SPDX-License-Identifier: MIT
+package box2d
+
+import "core:c"
+
+_ :: c
+
+foreign import lib "box2d.lib"
+
+// API :: BOX2D_EXPORT
+// INLINE :: static inline
+
+/// Prototype for user allocation function
+/// @param size the allocation size in bytes
+/// @param alignment the required alignment, guaranteed to be a power of 2
+AllocFcn :: proc "c" (c.uint, c.int) -> rawptr
+
+/// Prototype for user free function
+/// @param mem the memory previously allocated through `b2AllocFcn`
+FreeFcn :: proc "c" (rawptr)
+
+/// Prototype for the user assert callback. Return 0 to skip the debugger break.
+AssertFcn :: proc "c" (cstring, cstring, c.int) -> c.int
+
+// BREAKPOINT :: _debugbreak()
+
+/// Version numbering scheme.
+/// See https://semver.org/
+Version :: struct {
+ /// Significant changes
+ major: c.int,
+
+ /// Incremental changes
+ minor: c.int,
+
+ /// Bug fixes
+ revision: c.int,
+}
+
+/// Simple djb2 hash function for determinism testing
+HASH_INIT :: 5381
+
+@(default_calling_convention="c", link_prefix="b2")
+foreign lib {
+ /// This allows the user to override the allocation functions. These should be
+ /// set during application startup.
+ SetAllocator :: proc(allocFcn: AllocFcn, freeFcn: FreeFcn) ---
+
+ /// @return the total bytes allocated by Box2D
+ GetByteCount :: proc() -> c.int ---
+
+ /// Override the default assert callback
+ /// @param assertFcn a non-null assert callback
+ SetAssertFcn :: proc(assertFcn: AssertFcn) ---
+ InternalAssertFcn :: proc(condition: cstring, fileName: cstring, lineNumber: c.int) -> c.int ---
+
+ /// Get the current version of Box2D
+ GetVersion :: proc() -> Version ---
+
+ /// Get the absolute number of system ticks. The value is platform specific.
+ GetTicks :: proc() -> u64 ---
+
+ /// Get the milliseconds passed from an initial tick value.
+ GetMilliseconds :: proc(ticks: u64) -> f32 ---
+
+ /// Get the milliseconds passed from an initial tick value.
+ GetMillisecondsAndReset :: proc(ticks: ^u64) -> f32 ---
+
+ /// Yield to be used in a busy loop.
+ Yield :: proc() ---
+ Hash :: proc(hash: u32, data: ^u8, count: c.int) -> u32 ---
+}
diff --git a/odin-c-bindgen/examples/box2d/box2d/box2d.odin b/odin-c-bindgen/examples/box2d/box2d/box2d.odin
@@ -0,0 +1,1082 @@
+// SPDX-FileCopyrightText: 2023 Erin Catto
+// SPDX-License-Identifier: MIT
+package box2d
+
+import "core:c"
+
+_ :: c
+
+foreign import lib "box2d.lib"
+
+@(default_calling_convention="c", link_prefix="b2")
+foreign lib {
+ /// Create a world for rigid body simulation. A world contains bodies, shapes, and constraints. You make create
+ /// up to 128 worlds. Each world is completely independent and may be simulated in parallel.
+ /// @return the world id.
+ CreateWorld :: proc(#by_ptr def: WorldDef) -> WorldId ---
+
+ /// Destroy a world
+ DestroyWorld :: proc(worldId: WorldId) ---
+
+ /// World id validation. Provides validation for up to 64K allocations.
+ World_IsValid :: proc(id: WorldId) -> bool ---
+
+ /// Simulate a world for one time step. This performs collision detection, integration, and constraint solution.
+ /// @param worldId The world to simulate
+ /// @param timeStep The amount of time to simulate, this should be a fixed number. Usually 1/60.
+ /// @param subStepCount The number of sub-steps, increasing the sub-step count can increase accuracy. Usually 4.
+ World_Step :: proc(worldId: WorldId, timeStep: f32, subStepCount: c.int) ---
+
+ /// Call this to draw shapes and other debug draw data
+ World_Draw :: proc(worldId: WorldId, draw: ^DebugDraw) ---
+
+ /// Get the body events for the current time step. The event data is transient. Do not store a reference to this data.
+ World_GetBodyEvents :: proc(worldId: WorldId) -> BodyEvents ---
+
+ /// Get sensor events for the current time step. The event data is transient. Do not store a reference to this data.
+ World_GetSensorEvents :: proc(worldId: WorldId) -> SensorEvents ---
+
+ /// Get contact events for this current time step. The event data is transient. Do not store a reference to this data.
+ World_GetContactEvents :: proc(worldId: WorldId) -> ContactEvents ---
+
+ /// Overlap test for all shapes that *potentially* overlap the provided AABB
+ World_OverlapAABB :: proc(worldId: WorldId, aabb: AABB, filter: QueryFilter, fcn: ^OverlapResultFcn, _context: rawptr) -> TreeStats ---
+
+ /// Overlap test for for all shapes that overlap the provided point.
+ World_OverlapPoint :: proc(worldId: WorldId, point: Vec2, transform: Transform, filter: QueryFilter, fcn: ^OverlapResultFcn, _context: rawptr) -> TreeStats ---
+
+ /// Overlap test for for all shapes that overlap the provided circle. A zero radius may be used for a point query.
+ World_OverlapCircle :: proc(worldId: WorldId, circle: ^Circle, transform: Transform, filter: QueryFilter, fcn: ^OverlapResultFcn, _context: rawptr) -> TreeStats ---
+
+ /// Overlap test for all shapes that overlap the provided capsule
+ World_OverlapCapsule :: proc(worldId: WorldId, capsule: ^Capsule, transform: Transform, filter: QueryFilter, fcn: ^OverlapResultFcn, _context: rawptr) -> TreeStats ---
+
+ /// Overlap test for all shapes that overlap the provided polygon
+ World_OverlapPolygon :: proc(worldId: WorldId, polygon: ^Polygon, transform: Transform, filter: QueryFilter, fcn: ^OverlapResultFcn, _context: rawptr) -> TreeStats ---
+
+ /// Cast a ray into the world to collect shapes in the path of the ray.
+ /// Your callback function controls whether you get the closest point, any point, or n-points.
+ /// The ray-cast ignores shapes that contain the starting point.
+ /// @note The callback function may receive shapes in any order
+ /// @param worldId The world to cast the ray against
+ /// @param origin The start point of the ray
+ /// @param translation The translation of the ray from the start point to the end point
+ /// @param filter Contains bit flags to filter unwanted shapes from the results
+ /// @param fcn A user implemented callback function
+ /// @param context A user context that is passed along to the callback function
+ /// @return traversal performance counters
+ World_CastRay :: proc(worldId: WorldId, origin: Vec2, translation: Vec2, filter: QueryFilter, fcn: ^CastResultFcn, _context: rawptr) -> TreeStats ---
+
+ /// Cast a ray into the world to collect the closest hit. This is a convenience function.
+ /// This is less general than b2World_CastRay() and does not allow for custom filtering.
+ World_CastRayClosest :: proc(worldId: WorldId, origin: Vec2, translation: Vec2, filter: QueryFilter) -> RayResult ---
+
+ /// Cast a circle through the world. Similar to a cast ray except that a circle is cast instead of a point.
+ /// @see b2World_CastRay
+ World_CastCircle :: proc(worldId: WorldId, circle: ^Circle, originTransform: Transform, translation: Vec2, filter: QueryFilter, fcn: ^CastResultFcn, _context: rawptr) -> TreeStats ---
+
+ /// Cast a capsule through the world. Similar to a cast ray except that a capsule is cast instead of a point.
+ /// @see b2World_CastRay
+ World_CastCapsule :: proc(worldId: WorldId, capsule: ^Capsule, originTransform: Transform, translation: Vec2, filter: QueryFilter, fcn: ^CastResultFcn, _context: rawptr) -> TreeStats ---
+
+ /// Cast a polygon through the world. Similar to a cast ray except that a polygon is cast instead of a point.
+ /// @see b2World_CastRay
+ World_CastPolygon :: proc(worldId: WorldId, polygon: ^Polygon, originTransform: Transform, translation: Vec2, filter: QueryFilter, fcn: ^CastResultFcn, _context: rawptr) -> TreeStats ---
+
+ /// Enable/disable sleep. If your application does not need sleeping, you can gain some performance
+ /// by disabling sleep completely at the world level.
+ /// @see b2WorldDef
+ World_EnableSleeping :: proc(worldId: WorldId, flag: bool) ---
+
+ /// Is body sleeping enabled?
+ World_IsSleepingEnabled :: proc(worldId: WorldId) -> bool ---
+
+ /// Enable/disable continuous collision between dynamic and static bodies. Generally you should keep continuous
+ /// collision enabled to prevent fast moving objects from going through static objects. The performance gain from
+ /// disabling continuous collision is minor.
+ /// @see b2WorldDef
+ World_EnableContinuous :: proc(worldId: WorldId, flag: bool) ---
+
+ /// Is continuous collision enabled?
+ World_IsContinuousEnabled :: proc(worldId: WorldId) -> bool ---
+
+ /// Adjust the restitution threshold. It is recommended not to make this value very small
+ /// because it will prevent bodies from sleeping. Usually in meters per second.
+ /// @see b2WorldDef
+ World_SetRestitutionThreshold :: proc(worldId: WorldId, value: f32) ---
+
+ /// Get the the restitution speed threshold. Usually in meters per second.
+ World_GetRestitutionThreshold :: proc(worldId: WorldId) -> f32 ---
+
+ /// Adjust the hit event threshold. This controls the collision speed needed to generate a b2ContactHitEvent.
+ /// Usually in meters per second.
+ /// @see b2WorldDef::hitEventThreshold
+ World_SetHitEventThreshold :: proc(worldId: WorldId, value: f32) ---
+
+ /// Get the the hit event speed threshold. Usually in meters per second.
+ World_GetHitEventThreshold :: proc(worldId: WorldId) -> f32 ---
+
+ /// Register the custom filter callback. This is optional.
+ World_SetCustomFilterCallback :: proc(worldId: WorldId, fcn: ^CustomFilterFcn, _context: rawptr) ---
+
+ /// Register the pre-solve callback. This is optional.
+ World_SetPreSolveCallback :: proc(worldId: WorldId, fcn: ^PreSolveFcn, _context: rawptr) ---
+
+ /// Set the gravity vector for the entire world. Box2D has no concept of an up direction and this
+ /// is left as a decision for the application. Usually in m/s^2.
+ /// @see b2WorldDef
+ World_SetGravity :: proc(worldId: WorldId, gravity: Vec2) ---
+
+ /// Get the gravity vector
+ World_GetGravity :: proc(worldId: WorldId) -> Vec2 ---
+
+ /// Apply a radial explosion
+ /// @param worldId The world id
+ /// @param explosionDef The explosion definition
+ World_Explode :: proc(worldId: WorldId, explosionDef: ^ExplosionDef) ---
+
+ /// Adjust contact tuning parameters
+ /// @param worldId The world id
+ /// @param hertz The contact stiffness (cycles per second)
+ /// @param dampingRatio The contact bounciness with 1 being critical damping (non-dimensional)
+ /// @param pushSpeed The maximum contact constraint push out speed (meters per second)
+ /// @note Advanced feature
+ World_SetContactTuning :: proc(worldId: WorldId, hertz: f32, dampingRatio: f32, pushSpeed: f32) ---
+
+ /// Adjust joint tuning parameters
+ /// @param worldId The world id
+ /// @param hertz The contact stiffness (cycles per second)
+ /// @param dampingRatio The contact bounciness with 1 being critical damping (non-dimensional)
+ /// @note Advanced feature
+ World_SetJointTuning :: proc(worldId: WorldId, hertz: f32, dampingRatio: f32) ---
+
+ /// Set the maximum linear speed. Usually in m/s.
+ World_SetMaximumLinearSpeed :: proc(worldId: WorldId, maximumLinearSpeed: f32) ---
+
+ /// Get the maximum linear speed. Usually in m/s.
+ World_GetMaximumLinearSpeed :: proc(worldId: WorldId) -> f32 ---
+
+ /// Enable/disable constraint warm starting. Advanced feature for testing. Disabling
+ /// sleeping greatly reduces stability and provides no performance gain.
+ World_EnableWarmStarting :: proc(worldId: WorldId, flag: bool) ---
+
+ /// Is constraint warm starting enabled?
+ World_IsWarmStartingEnabled :: proc(worldId: WorldId) -> bool ---
+
+ /// Get the number of awake bodies.
+ World_GetAwakeBodyCount :: proc(worldId: WorldId) -> c.int ---
+
+ /// Get the current world performance profile
+ World_GetProfile :: proc(worldId: WorldId) -> Profile ---
+
+ /// Get world counters and sizes
+ World_GetCounters :: proc(worldId: WorldId) -> Counters ---
+
+ /// Set the user data pointer.
+ World_SetUserData :: proc(worldId: WorldId, userData: rawptr) ---
+
+ /// Get the user data pointer.
+ World_GetUserData :: proc(worldId: WorldId) -> rawptr ---
+
+ /// Set the friction callback. Passing NULL resets to default.
+ World_SetFrictionCallback :: proc(worldId: WorldId, callback: ^FrictionCallback) ---
+
+ /// Set the restitution callback. Passing NULL resets to default.
+ World_SetRestitutionCallback :: proc(worldId: WorldId, callback: ^RestitutionCallback) ---
+
+ /// Dump memory stats to box2d_memory.txt
+ World_DumpMemoryStats :: proc(worldId: WorldId) ---
+
+ /// This is for internal testing
+ World_RebuildStaticTree :: proc(worldId: WorldId) ---
+
+ /// This is for internal testing
+ World_EnableSpeculative :: proc(worldId: WorldId, flag: bool) ---
+
+ /// Create a rigid body given a definition. No reference to the definition is retained. So you can create the definition
+ /// on the stack and pass it as a pointer.
+ /// @code{.c}
+ /// b2BodyDef bodyDef = b2DefaultBodyDef();
+ /// b2BodyId myBodyId = b2CreateBody(myWorldId, &bodyDef);
+ /// @endcode
+ /// @warning This function is locked during callbacks.
+ CreateBody :: proc(worldId: WorldId, #by_ptr def: BodyDef) -> BodyId ---
+
+ /// Destroy a rigid body given an id. This destroys all shapes and joints attached to the body.
+ /// Do not keep references to the associated shapes and joints.
+ DestroyBody :: proc(bodyId: BodyId) ---
+
+ /// Body identifier validation. Can be used to detect orphaned ids. Provides validation for up to 64K allocations.
+ Body_IsValid :: proc(id: BodyId) -> bool ---
+
+ /// Get the body type: static, kinematic, or dynamic
+ Body_GetType :: proc(bodyId: BodyId) -> BodyType ---
+
+ /// Change the body type. This is an expensive operation. This automatically updates the mass
+ /// properties regardless of the automatic mass setting.
+ Body_SetType :: proc(bodyId: BodyId, type: BodyType) ---
+
+ /// Set the body name. Up to 31 characters excluding 0 termination.
+ Body_SetName :: proc(bodyId: BodyId, name: cstring) ---
+
+ /// Get the body name. May be null.
+ Body_GetName :: proc(bodyId: BodyId) -> cstring ---
+
+ /// Set the user data for a body
+ Body_SetUserData :: proc(bodyId: BodyId, userData: rawptr) ---
+
+ /// Get the user data stored in a body
+ Body_GetUserData :: proc(bodyId: BodyId) -> rawptr ---
+
+ /// Get the world position of a body. This is the location of the body origin.
+ Body_GetPosition :: proc(bodyId: BodyId) -> Vec2 ---
+
+ /// Get the world rotation of a body as a cosine/sine pair (complex number)
+ Body_GetRotation :: proc(bodyId: BodyId) -> Rot ---
+
+ /// Get the world transform of a body.
+ Body_GetTransform :: proc(bodyId: BodyId) -> Transform ---
+
+ /// Set the world transform of a body. This acts as a teleport and is fairly expensive.
+ /// @note Generally you should create a body with then intended transform.
+ /// @see b2BodyDef::position and b2BodyDef::angle
+ Body_SetTransform :: proc(bodyId: BodyId, position: Vec2, rotation: Rot) ---
+
+ /// Get a local point on a body given a world point
+ Body_GetLocalPoint :: proc(bodyId: BodyId, worldPoint: Vec2) -> Vec2 ---
+
+ /// Get a world point on a body given a local point
+ Body_GetWorldPoint :: proc(bodyId: BodyId, localPoint: Vec2) -> Vec2 ---
+
+ /// Get a local vector on a body given a world vector
+ Body_GetLocalVector :: proc(bodyId: BodyId, worldVector: Vec2) -> Vec2 ---
+
+ /// Get a world vector on a body given a local vector
+ Body_GetWorldVector :: proc(bodyId: BodyId, localVector: Vec2) -> Vec2 ---
+
+ /// Get the linear velocity of a body's center of mass. Usually in meters per second.
+ Body_GetLinearVelocity :: proc(bodyId: BodyId) -> Vec2 ---
+
+ /// Get the angular velocity of a body in radians per second
+ Body_GetAngularVelocity :: proc(bodyId: BodyId) -> f32 ---
+
+ /// Set the linear velocity of a body. Usually in meters per second.
+ Body_SetLinearVelocity :: proc(bodyId: BodyId, linearVelocity: Vec2) ---
+
+ /// Set the angular velocity of a body in radians per second
+ Body_SetAngularVelocity :: proc(bodyId: BodyId, angularVelocity: f32) ---
+
+ /// Get the linear velocity of a local point attached to a body. Usually in meters per second.
+ Body_GetLocalPointVelocity :: proc(bodyId: BodyId, localPoint: Vec2) -> Vec2 ---
+
+ /// Get the linear velocity of a world point attached to a body. Usually in meters per second.
+ Body_GetWorldPointVelocity :: proc(bodyId: BodyId, worldPoint: Vec2) -> Vec2 ---
+
+ /// Apply a force at a world point. If the force is not applied at the center of mass,
+ /// it will generate a torque and affect the angular velocity. This optionally wakes up the body.
+ /// The force is ignored if the body is not awake.
+ /// @param bodyId The body id
+ /// @param force The world force vector, usually in newtons (N)
+ /// @param point The world position of the point of application
+ /// @param wake Option to wake up the body
+ Body_ApplyForce :: proc(bodyId: BodyId, force: Vec2, point: Vec2, wake: bool) ---
+
+ /// Apply a force to the center of mass. This optionally wakes up the body.
+ /// The force is ignored if the body is not awake.
+ /// @param bodyId The body id
+ /// @param force the world force vector, usually in newtons (N).
+ /// @param wake also wake up the body
+ Body_ApplyForceToCenter :: proc(bodyId: BodyId, force: Vec2, wake: bool) ---
+
+ /// Apply a torque. This affects the angular velocity without affecting the linear velocity.
+ /// This optionally wakes the body. The torque is ignored if the body is not awake.
+ /// @param bodyId The body id
+ /// @param torque about the z-axis (out of the screen), usually in N*m.
+ /// @param wake also wake up the body
+ Body_ApplyTorque :: proc(bodyId: BodyId, torque: f32, wake: bool) ---
+
+ /// Apply an impulse at a point. This immediately modifies the velocity.
+ /// It also modifies the angular velocity if the point of application
+ /// is not at the center of mass. This optionally wakes the body.
+ /// The impulse is ignored if the body is not awake.
+ /// @param bodyId The body id
+ /// @param impulse the world impulse vector, usually in N*s or kg*m/s.
+ /// @param point the world position of the point of application.
+ /// @param wake also wake up the body
+ /// @warning This should be used for one-shot impulses. If you need a steady force,
+ /// use a force instead, which will work better with the sub-stepping solver.
+ Body_ApplyLinearImpulse :: proc(bodyId: BodyId, impulse: Vec2, point: Vec2, wake: bool) ---
+
+ /// Apply an impulse to the center of mass. This immediately modifies the velocity.
+ /// The impulse is ignored if the body is not awake. This optionally wakes the body.
+ /// @param bodyId The body id
+ /// @param impulse the world impulse vector, usually in N*s or kg*m/s.
+ /// @param wake also wake up the body
+ /// @warning This should be used for one-shot impulses. If you need a steady force,
+ /// use a force instead, which will work better with the sub-stepping solver.
+ Body_ApplyLinearImpulseToCenter :: proc(bodyId: BodyId, impulse: Vec2, wake: bool) ---
+
+ /// Apply an angular impulse. The impulse is ignored if the body is not awake.
+ /// This optionally wakes the body.
+ /// @param bodyId The body id
+ /// @param impulse the angular impulse, usually in units of kg*m*m/s
+ /// @param wake also wake up the body
+ /// @warning This should be used for one-shot impulses. If you need a steady force,
+ /// use a force instead, which will work better with the sub-stepping solver.
+ Body_ApplyAngularImpulse :: proc(bodyId: BodyId, impulse: f32, wake: bool) ---
+
+ /// Get the mass of the body, usually in kilograms
+ Body_GetMass :: proc(bodyId: BodyId) -> f32 ---
+
+ /// Get the rotational inertia of the body, usually in kg*m^2
+ Body_GetRotationalInertia :: proc(bodyId: BodyId) -> f32 ---
+
+ /// Get the center of mass position of the body in local space
+ Body_GetLocalCenterOfMass :: proc(bodyId: BodyId) -> Vec2 ---
+
+ /// Get the center of mass position of the body in world space
+ Body_GetWorldCenterOfMass :: proc(bodyId: BodyId) -> Vec2 ---
+
+ /// Override the body's mass properties. Normally this is computed automatically using the
+ /// shape geometry and density. This information is lost if a shape is added or removed or if the
+ /// body type changes.
+ Body_SetMassData :: proc(bodyId: BodyId, massData: MassData) ---
+
+ /// Get the mass data for a body
+ Body_GetMassData :: proc(bodyId: BodyId) -> MassData ---
+
+ /// This update the mass properties to the sum of the mass properties of the shapes.
+ /// This normally does not need to be called unless you called SetMassData to override
+ /// the mass and you later want to reset the mass.
+ /// You may also use this when automatic mass computation has been disabled.
+ /// You should call this regardless of body type.
+ Body_ApplyMassFromShapes :: proc(bodyId: BodyId) ---
+
+ /// Adjust the linear damping. Normally this is set in b2BodyDef before creation.
+ Body_SetLinearDamping :: proc(bodyId: BodyId, linearDamping: f32) ---
+
+ /// Get the current linear damping.
+ Body_GetLinearDamping :: proc(bodyId: BodyId) -> f32 ---
+
+ /// Adjust the angular damping. Normally this is set in b2BodyDef before creation.
+ Body_SetAngularDamping :: proc(bodyId: BodyId, angularDamping: f32) ---
+
+ /// Get the current angular damping.
+ Body_GetAngularDamping :: proc(bodyId: BodyId) -> f32 ---
+
+ /// Adjust the gravity scale. Normally this is set in b2BodyDef before creation.
+ /// @see b2BodyDef::gravityScale
+ Body_SetGravityScale :: proc(bodyId: BodyId, gravityScale: f32) ---
+
+ /// Get the current gravity scale
+ Body_GetGravityScale :: proc(bodyId: BodyId) -> f32 ---
+
+ /// @return true if this body is awake
+ Body_IsAwake :: proc(bodyId: BodyId) -> bool ---
+
+ /// Wake a body from sleep. This wakes the entire island the body is touching.
+ /// @warning Putting a body to sleep will put the entire island of bodies touching this body to sleep,
+ /// which can be expensive and possibly unintuitive.
+ Body_SetAwake :: proc(bodyId: BodyId, awake: bool) ---
+
+ /// Enable or disable sleeping for this body. If sleeping is disabled the body will wake.
+ Body_EnableSleep :: proc(bodyId: BodyId, enableSleep: bool) ---
+
+ /// Returns true if sleeping is enabled for this body
+ Body_IsSleepEnabled :: proc(bodyId: BodyId) -> bool ---
+
+ /// Set the sleep threshold, usually in meters per second
+ Body_SetSleepThreshold :: proc(bodyId: BodyId, sleepThreshold: f32) ---
+
+ /// Get the sleep threshold, usually in meters per second.
+ Body_GetSleepThreshold :: proc(bodyId: BodyId) -> f32 ---
+
+ /// Returns true if this body is enabled
+ Body_IsEnabled :: proc(bodyId: BodyId) -> bool ---
+
+ /// Disable a body by removing it completely from the simulation. This is expensive.
+ Body_Disable :: proc(bodyId: BodyId) ---
+
+ /// Enable a body by adding it to the simulation. This is expensive.
+ Body_Enable :: proc(bodyId: BodyId) ---
+
+ /// Set this body to have fixed rotation. This causes the mass to be reset in all cases.
+ Body_SetFixedRotation :: proc(bodyId: BodyId, flag: bool) ---
+
+ /// Does this body have fixed rotation?
+ Body_IsFixedRotation :: proc(bodyId: BodyId) -> bool ---
+
+ /// Set this body to be a bullet. A bullet does continuous collision detection
+ /// against dynamic bodies (but not other bullets).
+ Body_SetBullet :: proc(bodyId: BodyId, flag: bool) ---
+
+ /// Is this body a bullet?
+ Body_IsBullet :: proc(bodyId: BodyId) -> bool ---
+
+ /// Enable/disable contact events on all shapes.
+ /// @see b2ShapeDef::enableContactEvents
+ /// @warning changing this at runtime may cause mismatched begin/end touch events
+ Body_EnableContactEvents :: proc(bodyId: BodyId, flag: bool) ---
+
+ /// Enable/disable hit events on all shapes
+ /// @see b2ShapeDef::enableHitEvents
+ Body_EnableHitEvents :: proc(bodyId: BodyId, flag: bool) ---
+
+ /// Get the world that owns this body
+ Body_GetWorld :: proc(bodyId: BodyId) -> WorldId ---
+
+ /// Get the number of shapes on this body
+ Body_GetShapeCount :: proc(bodyId: BodyId) -> c.int ---
+
+ /// Get the shape ids for all shapes on this body, up to the provided capacity.
+ /// @returns the number of shape ids stored in the user array
+ Body_GetShapes :: proc(bodyId: BodyId, shapeArray: ^ShapeId, capacity: c.int) -> c.int ---
+
+ /// Get the number of joints on this body
+ Body_GetJointCount :: proc(bodyId: BodyId) -> c.int ---
+
+ /// Get the joint ids for all joints on this body, up to the provided capacity
+ /// @returns the number of joint ids stored in the user array
+ Body_GetJoints :: proc(bodyId: BodyId, jointArray: ^JointId, capacity: c.int) -> c.int ---
+
+ /// Get the maximum capacity required for retrieving all the touching contacts on a body
+ Body_GetContactCapacity :: proc(bodyId: BodyId) -> c.int ---
+
+ /// Get the touching contact data for a body.
+ /// @note Box2D uses speculative collision so some contact points may be separated.
+ /// @returns the number of elements filled in the provided array
+ /// @warning do not ignore the return value, it specifies the valid number of elements
+ Body_GetContactData :: proc(bodyId: BodyId, contactData: ^ContactData, capacity: c.int) -> c.int ---
+
+ /// Get the current world AABB that contains all the attached shapes. Note that this may not encompass the body origin.
+ /// If there are no shapes attached then the returned AABB is empty and centered on the body origin.
+ Body_ComputeAABB :: proc(bodyId: BodyId) -> AABB ---
+
+ /// Create a circle shape and attach it to a body. The shape definition and geometry are fully cloned.
+ /// Contacts are not created until the next time step.
+ /// @return the shape id for accessing the shape
+ CreateCircleShape :: proc(bodyId: BodyId, #by_ptr def: ShapeDef, #by_ptr circle: Circle) -> ShapeId ---
+
+ /// Create a line segment shape and attach it to a body. The shape definition and geometry are fully cloned.
+ /// Contacts are not created until the next time step.
+ /// @return the shape id for accessing the shape
+ CreateSegmentShape :: proc(bodyId: BodyId, def: ^ShapeDef, segment: ^Segment) -> ShapeId ---
+
+ /// Create a capsule shape and attach it to a body. The shape definition and geometry are fully cloned.
+ /// Contacts are not created until the next time step.
+ /// @return the shape id for accessing the shape
+ CreateCapsuleShape :: proc(bodyId: BodyId, def: ^ShapeDef, capsule: ^Capsule) -> ShapeId ---
+
+ /// Create a polygon shape and attach it to a body. The shape definition and geometry are fully cloned.
+ /// Contacts are not created until the next time step.
+ /// @return the shape id for accessing the shape
+ CreatePolygonShape :: proc(bodyId: BodyId, #by_ptr def: ShapeDef, #by_ptr polygon: Polygon) -> ShapeId ---
+
+ /// Destroy a shape. You may defer the body mass update which can improve performance if several shapes on a
+ /// body are destroyed at once.
+ /// @see b2Body_ApplyMassFromShapes
+ DestroyShape :: proc(shapeId: ShapeId, updateBodyMass: bool) ---
+
+ /// Shape identifier validation. Provides validation for up to 64K allocations.
+ Shape_IsValid :: proc(id: ShapeId) -> bool ---
+
+ /// Get the type of a shape
+ Shape_GetType :: proc(shapeId: ShapeId) -> ShapeType ---
+
+ /// Get the id of the body that a shape is attached to
+ Shape_GetBody :: proc(shapeId: ShapeId) -> BodyId ---
+
+ /// Get the world that owns this shape
+ Shape_GetWorld :: proc(shapeId: ShapeId) -> WorldId ---
+
+ /// Returns true If the shape is a sensor
+ Shape_IsSensor :: proc(shapeId: ShapeId) -> bool ---
+
+ /// Set the user data for a shape
+ Shape_SetUserData :: proc(shapeId: ShapeId, userData: rawptr) ---
+
+ /// Get the user data for a shape. This is useful when you get a shape id
+ /// from an event or query.
+ Shape_GetUserData :: proc(shapeId: ShapeId) -> rawptr ---
+
+ /// Set the mass density of a shape, usually in kg/m^2.
+ /// This will optionally update the mass properties on the parent body.
+ /// @see b2ShapeDef::density, b2Body_ApplyMassFromShapes
+ Shape_SetDensity :: proc(shapeId: ShapeId, density: f32, updateBodyMass: bool) ---
+
+ /// Get the density of a shape, usually in kg/m^2
+ Shape_GetDensity :: proc(shapeId: ShapeId) -> f32 ---
+
+ /// Set the friction on a shape
+ /// @see b2ShapeDef::friction
+ Shape_SetFriction :: proc(shapeId: ShapeId, friction: f32) ---
+
+ /// Get the friction of a shape
+ Shape_GetFriction :: proc(shapeId: ShapeId) -> f32 ---
+
+ /// Set the shape restitution (bounciness)
+ /// @see b2ShapeDef::restitution
+ Shape_SetRestitution :: proc(shapeId: ShapeId, restitution: f32) ---
+
+ /// Get the shape restitution
+ Shape_GetRestitution :: proc(shapeId: ShapeId) -> f32 ---
+
+ /// Set the shape material identifier
+ /// @see b2ShapeDef::material
+ Shape_SetMaterial :: proc(shapeId: ShapeId, material: c.int) ---
+
+ /// Get the shape material identifier
+ Shape_GetMaterial :: proc(shapeId: ShapeId) -> c.int ---
+
+ /// Get the shape filter
+ Shape_GetFilter :: proc(shapeId: ShapeId) -> Filter ---
+
+ /// Set the current filter. This is almost as expensive as recreating the shape. This may cause
+ /// contacts to be immediately destroyed. However contacts are not created until the next world step.
+ /// Sensor overlap state is also not updated until the next world step.
+ /// @see b2ShapeDef::filter
+ Shape_SetFilter :: proc(shapeId: ShapeId, filter: Filter) ---
+
+ /// Enable contact events for this shape. Only applies to kinematic and dynamic bodies. Ignored for sensors.
+ /// @see b2ShapeDef::enableContactEvents
+ /// @warning changing this at run-time may lead to lost begin/end events
+ Shape_EnableContactEvents :: proc(shapeId: ShapeId, flag: bool) ---
+
+ /// Returns true if contact events are enabled
+ Shape_AreContactEventsEnabled :: proc(shapeId: ShapeId) -> bool ---
+
+ /// Enable pre-solve contact events for this shape. Only applies to dynamic bodies. These are expensive
+ /// and must be carefully handled due to multithreading. Ignored for sensors.
+ /// @see b2PreSolveFcn
+ Shape_EnablePreSolveEvents :: proc(shapeId: ShapeId, flag: bool) ---
+
+ /// Returns true if pre-solve events are enabled
+ Shape_ArePreSolveEventsEnabled :: proc(shapeId: ShapeId) -> bool ---
+
+ /// Enable contact hit events for this shape. Ignored for sensors.
+ /// @see b2WorldDef.hitEventThreshold
+ Shape_EnableHitEvents :: proc(shapeId: ShapeId, flag: bool) ---
+
+ /// Returns true if hit events are enabled
+ Shape_AreHitEventsEnabled :: proc(shapeId: ShapeId) -> bool ---
+
+ /// Test a point for overlap with a shape
+ Shape_TestPoint :: proc(shapeId: ShapeId, point: Vec2) -> bool ---
+
+ /// Ray cast a shape directly
+ Shape_RayCast :: proc(shapeId: ShapeId, input: ^RayCastInput) -> CastOutput ---
+
+ /// Get a copy of the shape's circle. Asserts the type is correct.
+ Shape_GetCircle :: proc(shapeId: ShapeId) -> Circle ---
+
+ /// Get a copy of the shape's line segment. Asserts the type is correct.
+ Shape_GetSegment :: proc(shapeId: ShapeId) -> Segment ---
+
+ /// Get a copy of the shape's chain segment. These come from chain shapes.
+ /// Asserts the type is correct.
+ Shape_GetChainSegment :: proc(shapeId: ShapeId) -> ChainSegment ---
+
+ /// Get a copy of the shape's capsule. Asserts the type is correct.
+ Shape_GetCapsule :: proc(shapeId: ShapeId) -> Capsule ---
+
+ /// Get a copy of the shape's convex polygon. Asserts the type is correct.
+ Shape_GetPolygon :: proc(shapeId: ShapeId) -> Polygon ---
+
+ /// Allows you to change a shape to be a circle or update the current circle.
+ /// This does not modify the mass properties.
+ /// @see b2Body_ApplyMassFromShapes
+ Shape_SetCircle :: proc(shapeId: ShapeId, circle: ^Circle) ---
+
+ /// Allows you to change a shape to be a capsule or update the current capsule.
+ /// This does not modify the mass properties.
+ /// @see b2Body_ApplyMassFromShapes
+ Shape_SetCapsule :: proc(shapeId: ShapeId, capsule: ^Capsule) ---
+
+ /// Allows you to change a shape to be a segment or update the current segment.
+ Shape_SetSegment :: proc(shapeId: ShapeId, segment: ^Segment) ---
+
+ /// Allows you to change a shape to be a polygon or update the current polygon.
+ /// This does not modify the mass properties.
+ /// @see b2Body_ApplyMassFromShapes
+ Shape_SetPolygon :: proc(shapeId: ShapeId, polygon: ^Polygon) ---
+
+ /// Get the parent chain id if the shape type is a chain segment, otherwise
+ /// returns b2_nullChainId.
+ Shape_GetParentChain :: proc(shapeId: ShapeId) -> ChainId ---
+
+ /// Get the maximum capacity required for retrieving all the touching contacts on a shape
+ Shape_GetContactCapacity :: proc(shapeId: ShapeId) -> c.int ---
+
+ /// Get the touching contact data for a shape. The provided shapeId will be either shapeIdA or shapeIdB on the contact data.
+ /// @note Box2D uses speculative collision so some contact points may be separated.
+ /// @returns the number of elements filled in the provided array
+ /// @warning do not ignore the return value, it specifies the valid number of elements
+ Shape_GetContactData :: proc(shapeId: ShapeId, contactData: ^ContactData, capacity: c.int) -> c.int ---
+
+ /// Get the maximum capacity required for retrieving all the overlapped shapes on a sensor shape.
+ /// This returns 0 if the provided shape is not a sensor.
+ /// @param shapeId the id of a sensor shape
+ /// @returns the required capacity to get all the overlaps in b2Shape_GetSensorOverlaps
+ Shape_GetSensorCapacity :: proc(shapeId: ShapeId) -> c.int ---
+
+ /// Get the overlapped shapes for a sensor shape.
+ /// @param shapeId the id of a sensor shape
+ /// @param overlaps a user allocated array that is filled with the overlapping shapes
+ /// @param capacity the capacity of overlappedShapes
+ /// @returns the number of elements filled in the provided array
+ /// @warning do not ignore the return value, it specifies the valid number of elements
+ /// @warning overlaps may contain destroyed shapes so use b2Shape_IsValid to confirm each overlap
+ Shape_GetSensorOverlaps :: proc(shapeId: ShapeId, overlaps: ^ShapeId, capacity: c.int) -> c.int ---
+
+ /// Get the current world AABB
+ Shape_GetAABB :: proc(shapeId: ShapeId) -> AABB ---
+
+ /// Get the mass data for a shape
+ Shape_GetMassData :: proc(shapeId: ShapeId) -> MassData ---
+
+ /// Get the closest point on a shape to a target point. Target and result are in world space.
+ /// todo need sample
+ Shape_GetClosestPoint :: proc(shapeId: ShapeId, target: Vec2) -> Vec2 ---
+
+ /// Create a chain shape
+ /// @see b2ChainDef for details
+ CreateChain :: proc(bodyId: BodyId, def: ^ChainDef) -> ChainId ---
+
+ /// Destroy a chain shape
+ DestroyChain :: proc(chainId: ChainId) ---
+
+ /// Get the world that owns this chain shape
+ Chain_GetWorld :: proc(chainId: ChainId) -> WorldId ---
+
+ /// Get the number of segments on this chain
+ Chain_GetSegmentCount :: proc(chainId: ChainId) -> c.int ---
+
+ /// Fill a user array with chain segment shape ids up to the specified capacity. Returns
+ /// the actual number of segments returned.
+ Chain_GetSegments :: proc(chainId: ChainId, segmentArray: ^ShapeId, capacity: c.int) -> c.int ---
+
+ /// Set the chain friction
+ /// @see b2ChainDef::friction
+ Chain_SetFriction :: proc(chainId: ChainId, friction: f32) ---
+
+ /// Get the chain friction
+ Chain_GetFriction :: proc(chainId: ChainId) -> f32 ---
+
+ /// Set the chain restitution (bounciness)
+ /// @see b2ChainDef::restitution
+ Chain_SetRestitution :: proc(chainId: ChainId, restitution: f32) ---
+
+ /// Get the chain restitution
+ Chain_GetRestitution :: proc(chainId: ChainId) -> f32 ---
+
+ /// Set the chain material
+ /// @see b2ChainDef::material
+ Chain_SetMaterial :: proc(chainId: ChainId, material: c.int) ---
+
+ /// Get the chain material
+ Chain_GetMaterial :: proc(chainId: ChainId) -> c.int ---
+
+ /// Chain identifier validation. Provides validation for up to 64K allocations.
+ Chain_IsValid :: proc(id: ChainId) -> bool ---
+
+ /// Destroy a joint
+ DestroyJoint :: proc(jointId: JointId) ---
+
+ /// Joint identifier validation. Provides validation for up to 64K allocations.
+ Joint_IsValid :: proc(id: JointId) -> bool ---
+
+ /// Get the joint type
+ Joint_GetType :: proc(jointId: JointId) -> JointType ---
+
+ /// Get body A id on a joint
+ Joint_GetBodyA :: proc(jointId: JointId) -> BodyId ---
+
+ /// Get body B id on a joint
+ Joint_GetBodyB :: proc(jointId: JointId) -> BodyId ---
+
+ /// Get the world that owns this joint
+ Joint_GetWorld :: proc(jointId: JointId) -> WorldId ---
+
+ /// Get the local anchor on bodyA
+ Joint_GetLocalAnchorA :: proc(jointId: JointId) -> Vec2 ---
+
+ /// Get the local anchor on bodyB
+ Joint_GetLocalAnchorB :: proc(jointId: JointId) -> Vec2 ---
+
+ /// Toggle collision between connected bodies
+ Joint_SetCollideConnected :: proc(jointId: JointId, shouldCollide: bool) ---
+
+ /// Is collision allowed between connected bodies?
+ Joint_GetCollideConnected :: proc(jointId: JointId) -> bool ---
+
+ /// Set the user data on a joint
+ Joint_SetUserData :: proc(jointId: JointId, userData: rawptr) ---
+
+ /// Get the user data on a joint
+ Joint_GetUserData :: proc(jointId: JointId) -> rawptr ---
+
+ /// Wake the bodies connect to this joint
+ Joint_WakeBodies :: proc(jointId: JointId) ---
+
+ /// Get the current constraint force for this joint. Usually in Newtons.
+ Joint_GetConstraintForce :: proc(jointId: JointId) -> Vec2 ---
+
+ /// Get the current constraint torque for this joint. Usually in Newton * meters.
+ Joint_GetConstraintTorque :: proc(jointId: JointId) -> f32 ---
+
+ /// Create a distance joint
+ /// @see b2DistanceJointDef for details
+ CreateDistanceJoint :: proc(worldId: WorldId, def: ^DistanceJointDef) -> JointId ---
+
+ /// Set the rest length of a distance joint
+ /// @param jointId The id for a distance joint
+ /// @param length The new distance joint length
+ DistanceJoint_SetLength :: proc(jointId: JointId, length: f32) ---
+
+ /// Get the rest length of a distance joint
+ DistanceJoint_GetLength :: proc(jointId: JointId) -> f32 ---
+
+ /// Enable/disable the distance joint spring. When disabled the distance joint is rigid.
+ DistanceJoint_EnableSpring :: proc(jointId: JointId, enableSpring: bool) ---
+
+ /// Is the distance joint spring enabled?
+ DistanceJoint_IsSpringEnabled :: proc(jointId: JointId) -> bool ---
+
+ /// Set the spring stiffness in Hertz
+ DistanceJoint_SetSpringHertz :: proc(jointId: JointId, hertz: f32) ---
+
+ /// Set the spring damping ratio, non-dimensional
+ DistanceJoint_SetSpringDampingRatio :: proc(jointId: JointId, dampingRatio: f32) ---
+
+ /// Get the spring Hertz
+ DistanceJoint_GetSpringHertz :: proc(jointId: JointId) -> f32 ---
+
+ /// Get the spring damping ratio
+ DistanceJoint_GetSpringDampingRatio :: proc(jointId: JointId) -> f32 ---
+
+ /// Enable joint limit. The limit only works if the joint spring is enabled. Otherwise the joint is rigid
+ /// and the limit has no effect.
+ DistanceJoint_EnableLimit :: proc(jointId: JointId, enableLimit: bool) ---
+
+ /// Is the distance joint limit enabled?
+ DistanceJoint_IsLimitEnabled :: proc(jointId: JointId) -> bool ---
+
+ /// Set the minimum and maximum length parameters of a distance joint
+ DistanceJoint_SetLengthRange :: proc(jointId: JointId, minLength: f32, maxLength: f32) ---
+
+ /// Get the distance joint minimum length
+ DistanceJoint_GetMinLength :: proc(jointId: JointId) -> f32 ---
+
+ /// Get the distance joint maximum length
+ DistanceJoint_GetMaxLength :: proc(jointId: JointId) -> f32 ---
+
+ /// Get the current length of a distance joint
+ DistanceJoint_GetCurrentLength :: proc(jointId: JointId) -> f32 ---
+
+ /// Enable/disable the distance joint motor
+ DistanceJoint_EnableMotor :: proc(jointId: JointId, enableMotor: bool) ---
+
+ /// Is the distance joint motor enabled?
+ DistanceJoint_IsMotorEnabled :: proc(jointId: JointId) -> bool ---
+
+ /// Set the distance joint motor speed, usually in meters per second
+ DistanceJoint_SetMotorSpeed :: proc(jointId: JointId, motorSpeed: f32) ---
+
+ /// Get the distance joint motor speed, usually in meters per second
+ DistanceJoint_GetMotorSpeed :: proc(jointId: JointId) -> f32 ---
+
+ /// Set the distance joint maximum motor force, usually in newtons
+ DistanceJoint_SetMaxMotorForce :: proc(jointId: JointId, force: f32) ---
+
+ /// Get the distance joint maximum motor force, usually in newtons
+ DistanceJoint_GetMaxMotorForce :: proc(jointId: JointId) -> f32 ---
+
+ /// Get the distance joint current motor force, usually in newtons
+ DistanceJoint_GetMotorForce :: proc(jointId: JointId) -> f32 ---
+
+ /// Create a motor joint
+ /// @see b2MotorJointDef for details
+ CreateMotorJoint :: proc(worldId: WorldId, def: ^MotorJointDef) -> JointId ---
+
+ /// Set the motor joint linear offset target
+ MotorJoint_SetLinearOffset :: proc(jointId: JointId, linearOffset: Vec2) ---
+
+ /// Get the motor joint linear offset target
+ MotorJoint_GetLinearOffset :: proc(jointId: JointId) -> Vec2 ---
+
+ /// Set the motor joint angular offset target in radians
+ MotorJoint_SetAngularOffset :: proc(jointId: JointId, angularOffset: f32) ---
+
+ /// Get the motor joint angular offset target in radians
+ MotorJoint_GetAngularOffset :: proc(jointId: JointId) -> f32 ---
+
+ /// Set the motor joint maximum force, usually in newtons
+ MotorJoint_SetMaxForce :: proc(jointId: JointId, maxForce: f32) ---
+
+ /// Get the motor joint maximum force, usually in newtons
+ MotorJoint_GetMaxForce :: proc(jointId: JointId) -> f32 ---
+
+ /// Set the motor joint maximum torque, usually in newton-meters
+ MotorJoint_SetMaxTorque :: proc(jointId: JointId, maxTorque: f32) ---
+
+ /// Get the motor joint maximum torque, usually in newton-meters
+ MotorJoint_GetMaxTorque :: proc(jointId: JointId) -> f32 ---
+
+ /// Set the motor joint correction factor, usually in [0, 1]
+ MotorJoint_SetCorrectionFactor :: proc(jointId: JointId, correctionFactor: f32) ---
+
+ /// Get the motor joint correction factor, usually in [0, 1]
+ MotorJoint_GetCorrectionFactor :: proc(jointId: JointId) -> f32 ---
+
+ /// Create a mouse joint
+ /// @see b2MouseJointDef for details
+ CreateMouseJoint :: proc(worldId: WorldId, def: ^MouseJointDef) -> JointId ---
+
+ /// Set the mouse joint target
+ MouseJoint_SetTarget :: proc(jointId: JointId, target: Vec2) ---
+
+ /// Get the mouse joint target
+ MouseJoint_GetTarget :: proc(jointId: JointId) -> Vec2 ---
+
+ /// Set the mouse joint spring stiffness in Hertz
+ MouseJoint_SetSpringHertz :: proc(jointId: JointId, hertz: f32) ---
+
+ /// Get the mouse joint spring stiffness in Hertz
+ MouseJoint_GetSpringHertz :: proc(jointId: JointId) -> f32 ---
+
+ /// Set the mouse joint spring damping ratio, non-dimensional
+ MouseJoint_SetSpringDampingRatio :: proc(jointId: JointId, dampingRatio: f32) ---
+
+ /// Get the mouse joint damping ratio, non-dimensional
+ MouseJoint_GetSpringDampingRatio :: proc(jointId: JointId) -> f32 ---
+
+ /// Set the mouse joint maximum force, usually in newtons
+ MouseJoint_SetMaxForce :: proc(jointId: JointId, maxForce: f32) ---
+
+ /// Get the mouse joint maximum force, usually in newtons
+ MouseJoint_GetMaxForce :: proc(jointId: JointId) -> f32 ---
+
+ /// Create a null joint.
+ /// @see b2NullJointDef for details
+ CreateNullJoint :: proc(worldId: WorldId, def: ^NullJointDef) -> JointId ---
+
+ /// Create a prismatic (slider) joint.
+ /// @see b2PrismaticJointDef for details
+ CreatePrismaticJoint :: proc(worldId: WorldId, def: ^PrismaticJointDef) -> JointId ---
+
+ /// Enable/disable the joint spring.
+ PrismaticJoint_EnableSpring :: proc(jointId: JointId, enableSpring: bool) ---
+
+ /// Is the prismatic joint spring enabled or not?
+ PrismaticJoint_IsSpringEnabled :: proc(jointId: JointId) -> bool ---
+
+ /// Set the prismatic joint stiffness in Hertz.
+ /// This should usually be less than a quarter of the simulation rate. For example, if the simulation
+ /// runs at 60Hz then the joint stiffness should be 15Hz or less.
+ PrismaticJoint_SetSpringHertz :: proc(jointId: JointId, hertz: f32) ---
+
+ /// Get the prismatic joint stiffness in Hertz
+ PrismaticJoint_GetSpringHertz :: proc(jointId: JointId) -> f32 ---
+
+ /// Set the prismatic joint damping ratio (non-dimensional)
+ PrismaticJoint_SetSpringDampingRatio :: proc(jointId: JointId, dampingRatio: f32) ---
+
+ /// Get the prismatic spring damping ratio (non-dimensional)
+ PrismaticJoint_GetSpringDampingRatio :: proc(jointId: JointId) -> f32 ---
+
+ /// Enable/disable a prismatic joint limit
+ PrismaticJoint_EnableLimit :: proc(jointId: JointId, enableLimit: bool) ---
+
+ /// Is the prismatic joint limit enabled?
+ PrismaticJoint_IsLimitEnabled :: proc(jointId: JointId) -> bool ---
+
+ /// Get the prismatic joint lower limit
+ PrismaticJoint_GetLowerLimit :: proc(jointId: JointId) -> f32 ---
+
+ /// Get the prismatic joint upper limit
+ PrismaticJoint_GetUpperLimit :: proc(jointId: JointId) -> f32 ---
+
+ /// Set the prismatic joint limits
+ PrismaticJoint_SetLimits :: proc(jointId: JointId, lower: f32, upper: f32) ---
+
+ /// Enable/disable a prismatic joint motor
+ PrismaticJoint_EnableMotor :: proc(jointId: JointId, enableMotor: bool) ---
+
+ /// Is the prismatic joint motor enabled?
+ PrismaticJoint_IsMotorEnabled :: proc(jointId: JointId) -> bool ---
+
+ /// Set the prismatic joint motor speed, usually in meters per second
+ PrismaticJoint_SetMotorSpeed :: proc(jointId: JointId, motorSpeed: f32) ---
+
+ /// Get the prismatic joint motor speed, usually in meters per second
+ PrismaticJoint_GetMotorSpeed :: proc(jointId: JointId) -> f32 ---
+
+ /// Set the prismatic joint maximum motor force, usually in newtons
+ PrismaticJoint_SetMaxMotorForce :: proc(jointId: JointId, force: f32) ---
+
+ /// Get the prismatic joint maximum motor force, usually in newtons
+ PrismaticJoint_GetMaxMotorForce :: proc(jointId: JointId) -> f32 ---
+
+ /// Get the prismatic joint current motor force, usually in newtons
+ PrismaticJoint_GetMotorForce :: proc(jointId: JointId) -> f32 ---
+
+ /// Get the current joint translation, usually in meters.
+ PrismaticJoint_GetTranslation :: proc(jointId: JointId) -> f32 ---
+
+ /// Get the current joint translation speed, usually in meters per second.
+ PrismaticJoint_GetSpeed :: proc(jointId: JointId) -> f32 ---
+
+ /// Create a revolute joint
+ /// @see b2RevoluteJointDef for details
+ CreateRevoluteJoint :: proc(worldId: WorldId, def: ^RevoluteJointDef) -> JointId ---
+
+ /// Enable/disable the revolute joint spring
+ RevoluteJoint_EnableSpring :: proc(jointId: JointId, enableSpring: bool) ---
+
+ /// It the revolute angular spring enabled?
+ RevoluteJoint_IsSpringEnabled :: proc(jointId: JointId) -> bool ---
+
+ /// Set the revolute joint spring stiffness in Hertz
+ RevoluteJoint_SetSpringHertz :: proc(jointId: JointId, hertz: f32) ---
+
+ /// Get the revolute joint spring stiffness in Hertz
+ RevoluteJoint_GetSpringHertz :: proc(jointId: JointId) -> f32 ---
+
+ /// Set the revolute joint spring damping ratio, non-dimensional
+ RevoluteJoint_SetSpringDampingRatio :: proc(jointId: JointId, dampingRatio: f32) ---
+
+ /// Get the revolute joint spring damping ratio, non-dimensional
+ RevoluteJoint_GetSpringDampingRatio :: proc(jointId: JointId) -> f32 ---
+
+ /// Get the revolute joint current angle in radians relative to the reference angle
+ /// @see b2RevoluteJointDef::referenceAngle
+ RevoluteJoint_GetAngle :: proc(jointId: JointId) -> f32 ---
+
+ /// Enable/disable the revolute joint limit
+ RevoluteJoint_EnableLimit :: proc(jointId: JointId, enableLimit: bool) ---
+
+ /// Is the revolute joint limit enabled?
+ RevoluteJoint_IsLimitEnabled :: proc(jointId: JointId) -> bool ---
+
+ /// Get the revolute joint lower limit in radians
+ RevoluteJoint_GetLowerLimit :: proc(jointId: JointId) -> f32 ---
+
+ /// Get the revolute joint upper limit in radians
+ RevoluteJoint_GetUpperLimit :: proc(jointId: JointId) -> f32 ---
+
+ /// Set the revolute joint limits in radians
+ RevoluteJoint_SetLimits :: proc(jointId: JointId, lower: f32, upper: f32) ---
+
+ /// Enable/disable a revolute joint motor
+ RevoluteJoint_EnableMotor :: proc(jointId: JointId, enableMotor: bool) ---
+
+ /// Is the revolute joint motor enabled?
+ RevoluteJoint_IsMotorEnabled :: proc(jointId: JointId) -> bool ---
+
+ /// Set the revolute joint motor speed in radians per second
+ RevoluteJoint_SetMotorSpeed :: proc(jointId: JointId, motorSpeed: f32) ---
+
+ /// Get the revolute joint motor speed in radians per second
+ RevoluteJoint_GetMotorSpeed :: proc(jointId: JointId) -> f32 ---
+
+ /// Get the revolute joint current motor torque, usually in newton-meters
+ RevoluteJoint_GetMotorTorque :: proc(jointId: JointId) -> f32 ---
+
+ /// Set the revolute joint maximum motor torque, usually in newton-meters
+ RevoluteJoint_SetMaxMotorTorque :: proc(jointId: JointId, torque: f32) ---
+
+ /// Get the revolute joint maximum motor torque, usually in newton-meters
+ RevoluteJoint_GetMaxMotorTorque :: proc(jointId: JointId) -> f32 ---
+
+ /// Create a weld joint
+ /// @see b2WeldJointDef for details
+ CreateWeldJoint :: proc(worldId: WorldId, def: ^WeldJointDef) -> JointId ---
+
+ /// Get the weld joint reference angle in radians
+ WeldJoint_GetReferenceAngle :: proc(jointId: JointId) -> f32 ---
+
+ /// Set the weld joint reference angle in radians, must be in [-pi,pi].
+ WeldJoint_SetReferenceAngle :: proc(jointId: JointId, angleInRadians: f32) ---
+
+ /// Set the weld joint linear stiffness in Hertz. 0 is rigid.
+ WeldJoint_SetLinearHertz :: proc(jointId: JointId, hertz: f32) ---
+
+ /// Get the weld joint linear stiffness in Hertz
+ WeldJoint_GetLinearHertz :: proc(jointId: JointId) -> f32 ---
+
+ /// Set the weld joint linear damping ratio (non-dimensional)
+ WeldJoint_SetLinearDampingRatio :: proc(jointId: JointId, dampingRatio: f32) ---
+
+ /// Get the weld joint linear damping ratio (non-dimensional)
+ WeldJoint_GetLinearDampingRatio :: proc(jointId: JointId) -> f32 ---
+
+ /// Set the weld joint angular stiffness in Hertz. 0 is rigid.
+ WeldJoint_SetAngularHertz :: proc(jointId: JointId, hertz: f32) ---
+
+ /// Get the weld joint angular stiffness in Hertz
+ WeldJoint_GetAngularHertz :: proc(jointId: JointId) -> f32 ---
+
+ /// Set weld joint angular damping ratio, non-dimensional
+ WeldJoint_SetAngularDampingRatio :: proc(jointId: JointId, dampingRatio: f32) ---
+
+ /// Get the weld joint angular damping ratio, non-dimensional
+ WeldJoint_GetAngularDampingRatio :: proc(jointId: JointId) -> f32 ---
+
+ /// Create a wheel joint
+ /// @see b2WheelJointDef for details
+ CreateWheelJoint :: proc(worldId: WorldId, def: ^WheelJointDef) -> JointId ---
+
+ /// Enable/disable the wheel joint spring
+ WheelJoint_EnableSpring :: proc(jointId: JointId, enableSpring: bool) ---
+
+ /// Is the wheel joint spring enabled?
+ WheelJoint_IsSpringEnabled :: proc(jointId: JointId) -> bool ---
+
+ /// Set the wheel joint stiffness in Hertz
+ WheelJoint_SetSpringHertz :: proc(jointId: JointId, hertz: f32) ---
+
+ /// Get the wheel joint stiffness in Hertz
+ WheelJoint_GetSpringHertz :: proc(jointId: JointId) -> f32 ---
+
+ /// Set the wheel joint damping ratio, non-dimensional
+ WheelJoint_SetSpringDampingRatio :: proc(jointId: JointId, dampingRatio: f32) ---
+
+ /// Get the wheel joint damping ratio, non-dimensional
+ WheelJoint_GetSpringDampingRatio :: proc(jointId: JointId) -> f32 ---
+
+ /// Enable/disable the wheel joint limit
+ WheelJoint_EnableLimit :: proc(jointId: JointId, enableLimit: bool) ---
+
+ /// Is the wheel joint limit enabled?
+ WheelJoint_IsLimitEnabled :: proc(jointId: JointId) -> bool ---
+
+ /// Get the wheel joint lower limit
+ WheelJoint_GetLowerLimit :: proc(jointId: JointId) -> f32 ---
+
+ /// Get the wheel joint upper limit
+ WheelJoint_GetUpperLimit :: proc(jointId: JointId) -> f32 ---
+
+ /// Set the wheel joint limits
+ WheelJoint_SetLimits :: proc(jointId: JointId, lower: f32, upper: f32) ---
+
+ /// Enable/disable the wheel joint motor
+ WheelJoint_EnableMotor :: proc(jointId: JointId, enableMotor: bool) ---
+
+ /// Is the wheel joint motor enabled?
+ WheelJoint_IsMotorEnabled :: proc(jointId: JointId) -> bool ---
+
+ /// Set the wheel joint motor speed in radians per second
+ WheelJoint_SetMotorSpeed :: proc(jointId: JointId, motorSpeed: f32) ---
+
+ /// Get the wheel joint motor speed in radians per second
+ WheelJoint_GetMotorSpeed :: proc(jointId: JointId) -> f32 ---
+
+ /// Set the wheel joint maximum motor torque, usually in newton-meters
+ WheelJoint_SetMaxMotorTorque :: proc(jointId: JointId, torque: f32) ---
+
+ /// Get the wheel joint maximum motor torque, usually in newton-meters
+ WheelJoint_GetMaxMotorTorque :: proc(jointId: JointId) -> f32 ---
+
+ /// Get the wheel joint current motor torque, usually in newton-meters
+ WheelJoint_GetMotorTorque :: proc(jointId: JointId) -> f32 ---
+}
diff --git a/odin-c-bindgen/examples/box2d/box2d/collision.odin b/odin-c-bindgen/examples/box2d/box2d/collision.odin
@@ -0,0 +1,679 @@
+// SPDX-FileCopyrightText: 2023 Erin Catto
+// SPDX-License-Identifier: MIT
+package box2d
+
+import "core:c"
+
+_ :: c
+
+foreign import lib "box2d.lib"
+
+/**
+ * @defgroup geometry Geometry
+ * @brief Geometry types and algorithms
+ *
+ * Definitions of circles, capsules, segments, and polygons. Various algorithms to compute hulls, mass properties, and so on.
+ * @{
+ */
+
+/// The maximum number of vertices on a convex polygon. Changing this affects performance even if you
+/// don't use more vertices.
+MAX_POLYGON_VERTICES :: 8
+
+/// Low level ray cast input data
+RayCastInput :: struct {
+ /// Start point of the ray cast
+ origin: Vec2,
+
+ /// Translation of the ray cast
+ translation: Vec2,
+
+ /// The maximum fraction of the translation to consider, typically 1
+ maxFraction: f32,
+}
+
+/// Low level shape cast input in generic form. This allows casting an arbitrary point
+/// cloud wrap with a radius. For example, a circle is a single point with a non-zero radius.
+/// A capsule is two points with a non-zero radius. A box is four points with a zero radius.
+ShapeCastInput :: struct {
+ /// A point cloud to cast
+ points: [8]Vec2,
+
+ /// The number of points
+ count: c.int,
+
+ /// The radius around the point cloud
+ radius: f32,
+
+ /// The translation of the shape cast
+ translation: Vec2,
+
+ /// The maximum fraction of the translation to consider, typically 1
+ maxFraction: f32,
+}
+
+/// Low level ray cast or shape-cast output data
+CastOutput :: struct {
+ /// The surface normal at the hit point
+ normal: Vec2,
+
+ /// The surface hit point
+ point: Vec2,
+
+ /// The fraction of the input translation at collision
+ fraction: f32,
+
+ /// The number of iterations used
+ iterations: c.int,
+
+ /// Did the cast hit?
+ hit: bool,
+}
+
+/// This holds the mass data computed for a shape.
+MassData :: struct {
+ /// The mass of the shape, usually in kilograms.
+ mass: f32,
+
+ /// The position of the shape's centroid relative to the shape's origin.
+ center: Vec2,
+
+ /// The rotational inertia of the shape about the local origin.
+ rotationalInertia: f32,
+}
+
+/// A solid circle
+Circle :: struct {
+ /// The local center
+ center: Vec2,
+
+ /// The radius
+ radius: f32,
+}
+
+/// A solid capsule can be viewed as two semicircles connected
+/// by a rectangle.
+Capsule :: struct {
+ /// Local center of the first semicircle
+ center1: Vec2,
+
+ /// Local center of the second semicircle
+ center2: Vec2,
+
+ /// The radius of the semicircles
+ radius: f32,
+}
+
+/// A solid convex polygon. It is assumed that the interior of the polygon is to
+/// the left of each edge.
+/// Polygons have a maximum number of vertices equal to B2_MAX_POLYGON_VERTICES.
+/// In most cases you should not need many vertices for a convex polygon.
+/// @warning DO NOT fill this out manually, instead use a helper function like
+/// b2MakePolygon or b2MakeBox.
+Polygon :: struct {
+ /// The polygon vertices
+ vertices: [8]Vec2,
+
+ /// The outward normal vectors of the polygon sides
+ normals: [8]Vec2,
+
+ /// The centroid of the polygon
+ centroid: Vec2,
+
+ /// The external radius for rounded polygons
+ radius: f32,
+
+ /// The number of polygon vertices
+ count: c.int,
+}
+
+/// A line segment with two-sided collision.
+Segment :: struct {
+ /// The first point
+ point1: Vec2,
+
+ /// The second point
+ point2: Vec2,
+}
+
+/// A line segment with one-sided collision. Only collides on the right side.
+/// Several of these are generated for a chain shape.
+/// ghost1 -> point1 -> point2 -> ghost2
+ChainSegment :: struct {
+ /// The tail ghost vertex
+ ghost1: Vec2,
+
+ /// The line segment
+ segment: Segment,
+
+ /// The head ghost vertex
+ ghost2: Vec2,
+
+ /// The owning chain shape index (internal usage only)
+ chainId: c.int,
+}
+
+/// A convex hull. Used to create convex polygons.
+/// @warning Do not modify these values directly, instead use b2ComputeHull()
+Hull :: struct {
+ /// The final points of the hull
+ points: [8]Vec2,
+
+ /// The number of points
+ count: c.int,
+}
+
+/// Result of computing the distance between two line segments
+SegmentDistanceResult :: struct {
+ /// The closest point on the first segment
+ closest1: Vec2,
+
+ /// The closest point on the second segment
+ closest2: Vec2,
+
+ /// The barycentric coordinate on the first segment
+ fraction1: f32,
+
+ /// The barycentric coordinate on the second segment
+ fraction2: f32,
+
+ /// The squared distance between the closest points
+ distanceSquared: f32,
+}
+
+/// A distance proxy is used by the GJK algorithm. It encapsulates any shape.
+ShapeProxy :: struct {
+ /// The point cloud
+ points: [8]Vec2,
+
+ /// The number of points
+ count: c.int,
+
+ /// The external radius of the point cloud
+ radius: f32,
+}
+
+/// Used to warm start the GJK simplex. If you call this function multiple times with nearby
+/// transforms this might improve performance. Otherwise you can zero initialize this.
+/// The distance cache must be initialized to zero on the first call.
+/// Users should generally just zero initialize this structure for each call.
+SimplexCache :: struct {
+ /// The number of stored simplex points
+ count: u16,
+
+ /// The cached simplex indices on shape A
+ indexA: [3]u8,
+
+ /// The cached simplex indices on shape B
+ indexB: [3]u8,
+}
+
+/// Input for b2ShapeDistance
+DistanceInput :: struct {
+ /// The proxy for shape A
+ proxyA: ShapeProxy,
+
+ /// The proxy for shape B
+ proxyB: ShapeProxy,
+
+ /// The world transform for shape A
+ transformA: Transform,
+
+ /// The world transform for shape B
+ transformB: Transform,
+
+ /// Should the proxy radius be considered?
+ useRadii: bool,
+}
+
+/// Output for b2ShapeDistance
+DistanceOutput :: struct {
+ pointA: Vec2, ///< Closest point on shapeA
+ pointB: Vec2, ///< Closest point on shapeB
+ distance: f32, ///< The final distance, zero if overlapped
+ iterations: c.int, ///< Number of GJK iterations used
+ simplexCount: c.int, ///< The number of simplexes stored in the simplex array
+}
+
+/// Simplex vertex for debugging the GJK algorithm
+SimplexVertex :: struct {
+ wA: Vec2, ///< support point in proxyA
+ wB: Vec2, ///< support point in proxyB
+ w: Vec2, ///< wB - wA
+ a: f32, ///< barycentric coordinate for closest point
+ indexA: c.int, ///< wA index
+ indexB: c.int, ///< wB index
+}
+
+/// Simplex from the GJK algorithm
+Simplex :: struct {
+ v1, v2, v3: SimplexVertex, ///< vertices
+ count: c.int, ///< number of valid vertices
+}
+
+/// Input parameters for b2ShapeCast
+ShapeCastPairInput :: struct {
+ proxyA: ShapeProxy, ///< The proxy for shape A
+ proxyB: ShapeProxy, ///< The proxy for shape B
+ transformA: Transform, ///< The world transform for shape A
+ transformB: Transform, ///< The world transform for shape B
+ translationB: Vec2, ///< The translation of shape B
+ maxFraction: f32, ///< The fraction of the translation to consider, typically 1
+}
+
+/// This describes the motion of a body/shape for TOI computation. Shapes are defined with respect to the body origin,
+/// which may not coincide with the center of mass. However, to support dynamics we must interpolate the center of mass
+/// position.
+Sweep :: struct {
+ localCenter: Vec2, ///< Local center of mass position
+ c1: Vec2, ///< Starting center of mass world position
+ c2: Vec2, ///< Ending center of mass world position
+ q1: Rot, ///< Starting world rotation
+ q2: Rot, ///< Ending world rotation
+}
+
+/// Input parameters for b2TimeOfImpact
+TOIInput :: struct {
+ proxyA: ShapeProxy, ///< The proxy for shape A
+ proxyB: ShapeProxy, ///< The proxy for shape B
+ sweepA: Sweep, ///< The movement of shape A
+ sweepB: Sweep, ///< The movement of shape B
+ maxFraction: f32, ///< Defines the sweep interval [0, maxFraction]
+}
+
+/// Describes the TOI output
+TOIState :: enum c.int {
+ Unknown,
+ Failed,
+ Overlapped,
+ Hit,
+ Separated,
+}
+
+/// Output parameters for b2TimeOfImpact.
+TOIOutput :: struct {
+ state: TOIState, ///< The type of result
+ fraction: f32, ///< The sweep time of the collision
+}
+
+/// A manifold point is a contact point belonging to a contact manifold.
+/// It holds details related to the geometry and dynamics of the contact points.
+/// Box2D uses speculative collision so some contact points may be separated.
+/// You may use the maxNormalImpulse to determine if there was an interaction during
+/// the time step.
+ManifoldPoint :: struct {
+ /// Location of the contact point in world space. Subject to precision loss at large coordinates.
+ /// @note Should only be used for debugging.
+ point: Vec2,
+
+ /// Location of the contact point relative to shapeA's origin in world space
+ /// @note When used internally to the Box2D solver, this is relative to the body center of mass.
+ anchorA: Vec2,
+
+ /// Location of the contact point relative to shapeB's origin in world space
+ /// @note When used internally to the Box2D solver, this is relative to the body center of mass.
+ anchorB: Vec2,
+
+ /// The separation of the contact point, negative if penetrating
+ separation: f32,
+
+ /// The impulse along the manifold normal vector.
+ normalImpulse: f32,
+
+ /// The friction impulse
+ tangentImpulse: f32,
+
+ /// The maximum normal impulse applied during sub-stepping. This is important
+ /// to identify speculative contact points that had an interaction in the time step.
+ maxNormalImpulse: f32,
+
+ /// Relative normal velocity pre-solve. Used for hit events. If the normal impulse is
+ /// zero then there was no hit. Negative means shapes are approaching.
+ normalVelocity: f32,
+
+ /// Uniquely identifies a contact point between two shapes
+ id: u16,
+
+ /// Did this contact point exist the previous step?
+ persisted: bool,
+}
+
+/// A contact manifold describes the contact points between colliding shapes.
+/// @note Box2D uses speculative collision so some contact points may be separated.
+Manifold :: struct {
+ /// The unit normal vector in world space, points from shape A to bodyB
+ normal: Vec2,
+
+ /// Angular impulse applied for rolling resistance. N * m * s = kg * m^2 / s
+ rollingImpulse: f32,
+
+ /// The manifold points, up to two are possible in 2D
+ points: [2]ManifoldPoint,
+
+ /// The number of contacts points, will be 0, 1, or 2
+ pointCount: c.int,
+}
+
+/// The dynamic tree structure. This should be considered private data.
+/// It is placed here for performance reasons.
+TreeNode :: struct {}
+
+DynamicTree :: struct {
+ /// The tree nodes
+ nodes: [^]TreeNode,
+
+ /// The root index
+ root: c.int,
+
+ /// The number of nodes
+ nodeCount: c.int,
+
+ /// The allocated node space
+ nodeCapacity: c.int,
+
+ /// Node free list
+ freeList: c.int,
+
+ /// Number of proxies created
+ proxyCount: c.int,
+
+ /// Leaf indices for rebuild
+ leafIndices: ^c.int,
+
+ /// Leaf bounding boxes for rebuild
+ leafBoxes: ^AABB,
+
+ /// Leaf bounding box centers for rebuild
+ leafCenters: ^Vec2,
+
+ /// Bins for sorting during rebuild
+ binIndices: ^c.int,
+
+ /// Allocated space for rebuilding
+ rebuildCapacity: c.int,
+}
+
+/// These are performance results returned by dynamic tree queries.
+TreeStats :: struct {
+ /// Number of internal nodes visited during the query
+ nodeVisits: c.int,
+
+ /// Number of leaf nodes visited during the query
+ leafVisits: c.int,
+}
+
+/// This function receives proxies found in the AABB query.
+/// @return true if the query should continue
+TreeQueryCallbackFcn :: proc "c" (c.int, c.int, rawptr) -> bool
+
+/// This function receives clipped ray cast input for a proxy. The function
+/// returns the new ray fraction.
+/// - return a value of 0 to terminate the ray cast
+/// - return a value less than input->maxFraction to clip the ray
+/// - return a value of input->maxFraction to continue the ray cast without clipping
+TreeRayCastCallbackFcn :: proc "c" (^RayCastInput, c.int, c.int, rawptr) -> f32
+
+/// This function receives clipped ray cast input for a proxy. The function
+/// returns the new ray fraction.
+/// - return a value of 0 to terminate the ray cast
+/// - return a value less than input->maxFraction to clip the ray
+/// - return a value of input->maxFraction to continue the ray cast without clipping
+TreeShapeCastCallbackFcn :: proc "c" (^ShapeCastInput, c.int, c.int, rawptr) -> f32
+
+@(default_calling_convention="c", link_prefix="b2")
+foreign lib {
+ /// Validate ray cast input data (NaN, etc)
+ IsValidRay :: proc(input: ^RayCastInput) -> bool ---
+
+ /// Make a convex polygon from a convex hull. This will assert if the hull is not valid.
+ /// @warning Do not manually fill in the hull data, it must come directly from b2ComputeHull
+ MakePolygon :: proc(hull: ^Hull, radius: f32) -> Polygon ---
+
+ /// Make an offset convex polygon from a convex hull. This will assert if the hull is not valid.
+ /// @warning Do not manually fill in the hull data, it must come directly from b2ComputeHull
+ MakeOffsetPolygon :: proc(hull: ^Hull, position: Vec2, rotation: Rot) -> Polygon ---
+
+ /// Make an offset convex polygon from a convex hull. This will assert if the hull is not valid.
+ /// @warning Do not manually fill in the hull data, it must come directly from b2ComputeHull
+ MakeOffsetRoundedPolygon :: proc(hull: ^Hull, position: Vec2, rotation: Rot, radius: f32) -> Polygon ---
+
+ /// Make a square polygon, bypassing the need for a convex hull.
+ /// @param halfWidth the half-width
+ MakeSquare :: proc(halfWidth: f32) -> Polygon ---
+
+ /// Make a box (rectangle) polygon, bypassing the need for a convex hull.
+ /// @param halfWidth the half-width (x-axis)
+ /// @param halfHeight the half-height (y-axis)
+ MakeBox :: proc(halfWidth: f32, halfHeight: f32) -> Polygon ---
+
+ /// Make a rounded box, bypassing the need for a convex hull.
+ /// @param halfWidth the half-width (x-axis)
+ /// @param halfHeight the half-height (y-axis)
+ /// @param radius the radius of the rounded extension
+ MakeRoundedBox :: proc(halfWidth: f32, halfHeight: f32, radius: f32) -> Polygon ---
+
+ /// Make an offset box, bypassing the need for a convex hull.
+ /// @param halfWidth the half-width (x-axis)
+ /// @param halfHeight the half-height (y-axis)
+ /// @param center the local center of the box
+ /// @param rotation the local rotation of the box
+ MakeOffsetBox :: proc(halfWidth: f32, halfHeight: f32, center: Vec2, rotation: Rot) -> Polygon ---
+
+ /// Make an offset rounded box, bypassing the need for a convex hull.
+ /// @param halfWidth the half-width (x-axis)
+ /// @param halfHeight the half-height (y-axis)
+ /// @param center the local center of the box
+ /// @param rotation the local rotation of the box
+ /// @param radius the radius of the rounded extension
+ MakeOffsetRoundedBox :: proc(halfWidth: f32, halfHeight: f32, center: Vec2, rotation: Rot, radius: f32) -> Polygon ---
+
+ /// Transform a polygon. This is useful for transferring a shape from one body to another.
+ TransformPolygon :: proc(transform: Transform, polygon: ^Polygon) -> Polygon ---
+
+ /// Compute mass properties of a circle
+ ComputeCircleMass :: proc(shape: ^Circle, density: f32) -> MassData ---
+
+ /// Compute mass properties of a capsule
+ ComputeCapsuleMass :: proc(shape: ^Capsule, density: f32) -> MassData ---
+
+ /// Compute mass properties of a polygon
+ ComputePolygonMass :: proc(shape: ^Polygon, density: f32) -> MassData ---
+
+ /// Compute the bounding box of a transformed circle
+ ComputeCircleAABB :: proc(shape: ^Circle, transform: Transform) -> AABB ---
+
+ /// Compute the bounding box of a transformed capsule
+ ComputeCapsuleAABB :: proc(shape: ^Capsule, transform: Transform) -> AABB ---
+
+ /// Compute the bounding box of a transformed polygon
+ ComputePolygonAABB :: proc(shape: ^Polygon, transform: Transform) -> AABB ---
+
+ /// Compute the bounding box of a transformed line segment
+ ComputeSegmentAABB :: proc(shape: ^Segment, transform: Transform) -> AABB ---
+
+ /// Test a point for overlap with a circle in local space
+ PointInCircle :: proc(point: Vec2, shape: ^Circle) -> bool ---
+
+ /// Test a point for overlap with a capsule in local space
+ PointInCapsule :: proc(point: Vec2, shape: ^Capsule) -> bool ---
+
+ /// Test a point for overlap with a convex polygon in local space
+ PointInPolygon :: proc(point: Vec2, shape: ^Polygon) -> bool ---
+
+ /// Ray cast versus circle shape in local space. Initial overlap is treated as a miss.
+ RayCastCircle :: proc(input: ^RayCastInput, shape: ^Circle) -> CastOutput ---
+
+ /// Ray cast versus capsule shape in local space. Initial overlap is treated as a miss.
+ RayCastCapsule :: proc(input: ^RayCastInput, shape: ^Capsule) -> CastOutput ---
+
+ /// Ray cast versus segment shape in local space. Optionally treat the segment as one-sided with hits from
+ /// the left side being treated as a miss.
+ RayCastSegment :: proc(input: ^RayCastInput, shape: ^Segment, oneSided: bool) -> CastOutput ---
+
+ /// Ray cast versus polygon shape in local space. Initial overlap is treated as a miss.
+ RayCastPolygon :: proc(input: ^RayCastInput, shape: ^Polygon) -> CastOutput ---
+
+ /// Shape cast versus a circle. Initial overlap is treated as a miss.
+ ShapeCastCircle :: proc(input: ^ShapeCastInput, shape: ^Circle) -> CastOutput ---
+
+ /// Shape cast versus a capsule. Initial overlap is treated as a miss.
+ ShapeCastCapsule :: proc(input: ^ShapeCastInput, shape: ^Capsule) -> CastOutput ---
+
+ /// Shape cast versus a line segment. Initial overlap is treated as a miss.
+ ShapeCastSegment :: proc(input: ^ShapeCastInput, shape: ^Segment) -> CastOutput ---
+
+ /// Shape cast versus a convex polygon. Initial overlap is treated as a miss.
+ ShapeCastPolygon :: proc(input: ^ShapeCastInput, shape: ^Polygon) -> CastOutput ---
+
+ /// Compute the convex hull of a set of points. Returns an empty hull if it fails.
+ /// Some failure cases:
+ /// - all points very close together
+ /// - all points on a line
+ /// - less than 3 points
+ /// - more than B2_MAX_POLYGON_VERTICES points
+ /// This welds close points and removes collinear points.
+ /// @warning Do not modify a hull once it has been computed
+ ComputeHull :: proc(points: ^Vec2, count: c.int) -> Hull ---
+
+ /// This determines if a hull is valid. Checks for:
+ /// - convexity
+ /// - collinear points
+ /// This is expensive and should not be called at runtime.
+ ValidateHull :: proc(hull: ^Hull) -> bool ---
+
+ /// Compute the distance between two line segments, clamping at the end points if needed.
+ SegmentDistance :: proc(p1: Vec2, q1: Vec2, p2: Vec2, q2: Vec2) -> SegmentDistanceResult ---
+
+ /// Compute the closest points between two shapes represented as point clouds.
+ /// b2SimplexCache cache is input/output. On the first call set b2SimplexCache.count to zero.
+ /// The underlying GJK algorithm may be debugged by passing in debug simplexes and capacity. You may pass in NULL and 0 for these.
+ ShapeDistance :: proc(cache: ^SimplexCache, input: ^DistanceInput, simplexes: ^Simplex, simplexCapacity: c.int) -> DistanceOutput ---
+
+ /// Perform a linear shape cast of shape B moving and shape A fixed. Determines the hit point, normal, and translation fraction.
+ ShapeCast :: proc(input: ^ShapeCastPairInput) -> CastOutput ---
+
+ /// Make a proxy for use in GJK and related functions.
+ MakeProxy :: proc(vertices: ^Vec2, count: c.int, radius: f32) -> ShapeProxy ---
+
+ /// Evaluate the transform sweep at a specific time.
+ GetSweepTransform :: proc(sweep: ^Sweep, time: f32) -> Transform ---
+
+ /// Compute the upper bound on time before two shapes penetrate. Time is represented as
+ /// a fraction between [0,tMax]. This uses a swept separating axis and may miss some intermediate,
+ /// non-tunneling collisions. If you change the time interval, you should call this function
+ /// again.
+ TimeOfImpact :: proc(input: ^TOIInput) -> TOIOutput ---
+
+ /// Compute the contact manifold between two circles
+ CollideCircles :: proc(circleA: ^Circle, xfA: Transform, circleB: ^Circle, xfB: Transform) -> Manifold ---
+
+ /// Compute the contact manifold between a capsule and circle
+ CollideCapsuleAndCircle :: proc(capsuleA: ^Capsule, xfA: Transform, circleB: ^Circle, xfB: Transform) -> Manifold ---
+
+ /// Compute the contact manifold between an segment and a circle
+ CollideSegmentAndCircle :: proc(segmentA: ^Segment, xfA: Transform, circleB: ^Circle, xfB: Transform) -> Manifold ---
+
+ /// Compute the contact manifold between a polygon and a circle
+ CollidePolygonAndCircle :: proc(polygonA: ^Polygon, xfA: Transform, circleB: ^Circle, xfB: Transform) -> Manifold ---
+
+ /// Compute the contact manifold between a capsule and circle
+ CollideCapsules :: proc(capsuleA: ^Capsule, xfA: Transform, capsuleB: ^Capsule, xfB: Transform) -> Manifold ---
+
+ /// Compute the contact manifold between an segment and a capsule
+ CollideSegmentAndCapsule :: proc(segmentA: ^Segment, xfA: Transform, capsuleB: ^Capsule, xfB: Transform) -> Manifold ---
+
+ /// Compute the contact manifold between a polygon and capsule
+ CollidePolygonAndCapsule :: proc(polygonA: ^Polygon, xfA: Transform, capsuleB: ^Capsule, xfB: Transform) -> Manifold ---
+
+ /// Compute the contact manifold between two polygons
+ CollidePolygons :: proc(polygonA: ^Polygon, xfA: Transform, polygonB: ^Polygon, xfB: Transform) -> Manifold ---
+
+ /// Compute the contact manifold between an segment and a polygon
+ CollideSegmentAndPolygon :: proc(segmentA: ^Segment, xfA: Transform, polygonB: ^Polygon, xfB: Transform) -> Manifold ---
+
+ /// Compute the contact manifold between a chain segment and a circle
+ CollideChainSegmentAndCircle :: proc(segmentA: ^ChainSegment, xfA: Transform, circleB: ^Circle, xfB: Transform) -> Manifold ---
+
+ /// Compute the contact manifold between a chain segment and a capsule
+ CollideChainSegmentAndCapsule :: proc(segmentA: ^ChainSegment, xfA: Transform, capsuleB: ^Capsule, xfB: Transform, cache: ^SimplexCache) -> Manifold ---
+
+ /// Compute the contact manifold between a chain segment and a rounded polygon
+ CollideChainSegmentAndPolygon :: proc(segmentA: ^ChainSegment, xfA: Transform, polygonB: ^Polygon, xfB: Transform, cache: ^SimplexCache) -> Manifold ---
+
+ /// Constructing the tree initializes the node pool.
+ DynamicTree_Create :: proc() -> DynamicTree ---
+
+ /// Destroy the tree, freeing the node pool.
+ DynamicTree_Destroy :: proc(tree: ^DynamicTree) ---
+
+ /// Create a proxy. Provide an AABB and a userData value.
+ DynamicTree_CreateProxy :: proc(tree: ^DynamicTree, aabb: AABB, categoryBits: u64, userData: c.int) -> c.int ---
+
+ /// Destroy a proxy. This asserts if the id is invalid.
+ DynamicTree_DestroyProxy :: proc(tree: ^DynamicTree, proxyId: c.int) ---
+
+ /// Move a proxy to a new AABB by removing and reinserting into the tree.
+ DynamicTree_MoveProxy :: proc(tree: ^DynamicTree, proxyId: c.int, aabb: AABB) ---
+
+ /// Enlarge a proxy and enlarge ancestors as necessary.
+ DynamicTree_EnlargeProxy :: proc(tree: ^DynamicTree, proxyId: c.int, aabb: AABB) ---
+
+ /// Query an AABB for overlapping proxies. The callback class is called for each proxy that overlaps the supplied AABB.
+ /// @return performance data
+ DynamicTree_Query :: proc(tree: ^DynamicTree, aabb: AABB, maskBits: u64, callback: TreeQueryCallbackFcn, _context: rawptr) -> TreeStats ---
+
+ /// Ray cast against the proxies in the tree. This relies on the callback
+ /// to perform a exact ray cast in the case were the proxy contains a shape.
+ /// The callback also performs the any collision filtering. This has performance
+ /// roughly equal to k * log(n), where k is the number of collisions and n is the
+ /// number of proxies in the tree.
+ /// Bit-wise filtering using mask bits can greatly improve performance in some scenarios.
+ /// However, this filtering may be approximate, so the user should still apply filtering to results.
+ /// @param tree the dynamic tree to ray cast
+ /// @param input the ray cast input data. The ray extends from p1 to p1 + maxFraction * (p2 - p1)
+ /// @param maskBits mask bit hint: `bool accept = (maskBits & node->categoryBits) != 0;`
+ /// @param callback a callback class that is called for each proxy that is hit by the ray
+ /// @param context user context that is passed to the callback
+ /// @return performance data
+ DynamicTree_RayCast :: proc(tree: ^DynamicTree, input: ^RayCastInput, maskBits: u64, callback: TreeRayCastCallbackFcn, _context: rawptr) -> TreeStats ---
+
+ /// Ray cast against the proxies in the tree. This relies on the callback
+ /// to perform a exact ray cast in the case were the proxy contains a shape.
+ /// The callback also performs the any collision filtering. This has performance
+ /// roughly equal to k * log(n), where k is the number of collisions and n is the
+ /// number of proxies in the tree.
+ /// @param tree the dynamic tree to ray cast
+ /// @param input the ray cast input data. The ray extends from p1 to p1 + maxFraction * (p2 - p1).
+ /// @param maskBits filter bits: `bool accept = (maskBits & node->categoryBits) != 0;`
+ /// @param callback a callback class that is called for each proxy that is hit by the shape
+ /// @param context user context that is passed to the callback
+ /// @return performance data
+ DynamicTree_ShapeCast :: proc(tree: ^DynamicTree, input: ^ShapeCastInput, maskBits: u64, callback: TreeShapeCastCallbackFcn, _context: rawptr) -> TreeStats ---
+
+ /// Get the height of the binary tree.
+ DynamicTree_GetHeight :: proc(tree: ^DynamicTree) -> c.int ---
+
+ /// Get the ratio of the sum of the node areas to the root area.
+ DynamicTree_GetAreaRatio :: proc(tree: ^DynamicTree) -> f32 ---
+
+ /// Get the number of proxies created
+ DynamicTree_GetProxyCount :: proc(tree: ^DynamicTree) -> c.int ---
+
+ /// Rebuild the tree while retaining subtrees that haven't changed. Returns the number of boxes sorted.
+ DynamicTree_Rebuild :: proc(tree: ^DynamicTree, fullBuild: bool) -> c.int ---
+
+ /// Get the number of bytes used by this tree
+ DynamicTree_GetByteCount :: proc(tree: ^DynamicTree) -> c.int ---
+
+ /// Get proxy user data
+ DynamicTree_GetUserData :: proc(tree: ^DynamicTree, proxyId: c.int) -> c.int ---
+
+ /// Get the AABB of a proxy
+ DynamicTree_GetAABB :: proc(tree: ^DynamicTree, proxyId: c.int) -> AABB ---
+
+ /// Validate this tree. For testing.
+ DynamicTree_Validate :: proc(tree: ^DynamicTree) ---
+
+ /// Validate this tree has no enlarged AABBs. For testing.
+ DynamicTree_ValidateNoEnlarged :: proc(tree: ^DynamicTree) ---
+}
diff --git a/odin-c-bindgen/examples/box2d/box2d/id.odin b/odin-c-bindgen/examples/box2d/box2d/id.odin
@@ -0,0 +1,70 @@
+// SPDX-FileCopyrightText: 2023 Erin Catto
+// SPDX-License-Identifier: MIT
+package box2d
+
+import "core:c"
+
+_ :: c
+
+foreign import lib "box2d.lib"
+
+/// World id references a world instance. This should be treated as an opaque handle.
+WorldId :: struct {
+ index1: u16,
+ generation: u16,
+}
+
+/// Body id references a body instance. This should be treated as an opaque handle.
+BodyId :: struct {
+ index1: i32,
+ world0: u16,
+ generation: u16,
+}
+
+/// Shape id references a shape instance. This should be treated as an opaque handle.
+ShapeId :: struct {
+ index1: i32,
+ world0: u16,
+ generation: u16,
+}
+
+/// Chain id references a chain instances. This should be treated as an opaque handle.
+ChainId :: struct {
+ index1: i32,
+ world0: u16,
+ generation: u16,
+}
+
+/// Joint id references a joint instance. This should be treated as an opaque handle.
+JointId :: struct {
+ index1: i32,
+ world0: u16,
+ generation: u16,
+}
+
+@(default_calling_convention="c", link_prefix="b2")
+foreign lib {
+ /// Store a body id into a uint64_t.
+ StoreBodyId :: proc(id: BodyId) -> u64 ---
+
+ /// Load a uint64_t into a body id.
+ LoadBodyId :: proc(x: u64) -> BodyId ---
+
+ /// Store a shape id into a uint64_t.
+ StoreShapeId :: proc(id: ShapeId) -> u64 ---
+
+ /// Load a uint64_t into a shape id.
+ LoadShapeId :: proc(x: u64) -> ShapeId ---
+
+ /// Store a chain id into a uint64_t.
+ StoreChainId :: proc(id: ChainId) -> u64 ---
+
+ /// Load a uint64_t into a chain id.
+ LoadChainId :: proc(x: u64) -> ChainId ---
+
+ /// Store a joint id into a uint64_t.
+ StoreJointId :: proc(id: JointId) -> u64 ---
+
+ /// Load a uint64_t into a joint id.
+ LoadJointId :: proc(x: u64) -> JointId ---
+}
diff --git a/odin-c-bindgen/examples/box2d/box2d/math_functions.odin b/odin-c-bindgen/examples/box2d/box2d/math_functions.odin
@@ -0,0 +1,297 @@
+// SPDX-FileCopyrightText: 2023 Erin Catto
+// SPDX-License-Identifier: MIT
+package box2d
+
+import "core:c"
+
+_ :: c
+
+foreign import lib "box2d.lib"
+
+/// 2D vector
+/// This can be used to represent a point or free vector
+Vec2 :: struct {
+ /// coordinates
+ x, y: f32,
+}
+
+/// Cosine and sine pair
+/// This uses a custom implementation designed for cross-platform determinism
+CosSin :: struct {
+ /// cosine and sine
+ cosine: f32,
+ sine: f32,
+}
+
+/// 2D rotation
+/// This is similar to using a complex number for rotation
+Rot :: struct {
+ /// cosine and sine
+ _c, s: f32,
+}
+
+/// A 2D rigid transform
+Transform :: struct {
+ p: Vec2,
+ q: Rot,
+}
+
+/// A 2-by-2 Matrix
+Mat22 :: struct {
+ /// columns
+ cx, cy: Vec2,
+}
+
+/// Axis-aligned bounding box
+AABB :: struct {
+ lowerBound: Vec2,
+ upperBound: Vec2,
+}
+
+/**
+ * @addtogroup math
+ * @{
+ */
+
+/// https://en.wikipedia.org/wiki/Pi
+PI :: 3.14159265359
+
+@(default_calling_convention="c", link_prefix="b2")
+foreign lib {
+ /// @return the minimum of two integers
+ MinInt :: proc(a: c.int, b: c.int) -> c.int ---
+
+ /// @return the maximum of two integers
+ MaxInt :: proc(a: c.int, b: c.int) -> c.int ---
+
+ /// @return the absolute value of an integer
+ AbsInt :: proc(a: c.int) -> c.int ---
+
+ /// @return an integer clamped between a lower and upper bound
+ ClampInt :: proc(a: c.int, lower: c.int, upper: c.int) -> c.int ---
+
+ /// @return the minimum of two floats
+ MinFloat :: proc(a: f32, b: f32) -> f32 ---
+
+ /// @return the maximum of two floats
+ MaxFloat :: proc(a: f32, b: f32) -> f32 ---
+
+ /// @return the absolute value of a float
+ AbsFloat :: proc(a: f32) -> f32 ---
+
+ /// @return a float clamped between a lower and upper bound
+ ClampFloat :: proc(a: f32, lower: f32, upper: f32) -> f32 ---
+
+ /// Compute an approximate arctangent in the range [-pi, pi]
+ /// This is hand coded for cross-platform determinism. The atan2f
+ /// function in the standard library is not cross-platform deterministic.
+ /// Accurate to around 0.0023 degrees
+ Atan2 :: proc(y: f32, x: f32) -> f32 ---
+
+ /// Compute the cosine and sine of an angle in radians. Implemented
+ /// for cross-platform determinism.
+ ComputeCosSin :: proc(radians: f32) -> CosSin ---
+
+ /// Vector dot product
+ Dot :: proc(a: Vec2, b: Vec2) -> f32 ---
+
+ /// Vector cross product. In 2D this yields a scalar.
+ Cross :: proc(a: Vec2, b: Vec2) -> f32 ---
+
+ /// Perform the cross product on a vector and a scalar. In 2D this produces a vector.
+ CrossVS :: proc(v: Vec2, s: f32) -> Vec2 ---
+
+ /// Perform the cross product on a scalar and a vector. In 2D this produces a vector.
+ CrossSV :: proc(s: f32, v: Vec2) -> Vec2 ---
+
+ /// Get a left pointing perpendicular vector. Equivalent to b2CrossSV(1.0f, v)
+ LeftPerp :: proc(v: Vec2) -> Vec2 ---
+
+ /// Get a right pointing perpendicular vector. Equivalent to b2CrossVS(v, 1.0f)
+ RightPerp :: proc(v: Vec2) -> Vec2 ---
+
+ /// Vector addition
+ Add :: proc(a: Vec2, b: Vec2) -> Vec2 ---
+
+ /// Vector subtraction
+ Sub :: proc(a: Vec2, b: Vec2) -> Vec2 ---
+
+ /// Vector negation
+ Neg :: proc(a: Vec2) -> Vec2 ---
+
+ /// Vector linear interpolation
+ /// https://fgiesen.wordpress.com/2012/08/15/linear-interpolation-past-present-and-future/
+ Lerp :: proc(a: Vec2, b: Vec2, t: f32) -> Vec2 ---
+
+ /// Component-wise multiplication
+ Mul :: proc(a: Vec2, b: Vec2) -> Vec2 ---
+
+ /// Multiply a scalar and vector
+ MulSV :: proc(s: f32, v: Vec2) -> Vec2 ---
+
+ /// a + s * b
+ MulAdd :: proc(a: Vec2, s: f32, b: Vec2) -> Vec2 ---
+
+ /// a - s * b
+ MulSub :: proc(a: Vec2, s: f32, b: Vec2) -> Vec2 ---
+
+ /// Component-wise absolute vector
+ Abs :: proc(a: Vec2) -> Vec2 ---
+
+ /// Component-wise minimum vector
+ Min :: proc(a: Vec2, b: Vec2) -> Vec2 ---
+
+ /// Component-wise maximum vector
+ Max :: proc(a: Vec2, b: Vec2) -> Vec2 ---
+
+ /// Component-wise clamp vector v into the range [a, b]
+ Clamp :: proc(v: Vec2, a: Vec2, b: Vec2) -> Vec2 ---
+
+ /// Get the length of this vector (the norm)
+ Length :: proc(v: Vec2) -> f32 ---
+
+ /// Get the distance between two points
+ Distance :: proc(a: Vec2, b: Vec2) -> f32 ---
+
+ /// Convert a vector into a unit vector if possible, otherwise returns the zero vector.
+ Normalize :: proc(v: Vec2) -> Vec2 ---
+
+ /// Convert a vector into a unit vector if possible, otherwise returns the zero vector. Also
+ /// outputs the length.
+ GetLengthAndNormalize :: proc(length: ^f32, v: Vec2) -> Vec2 ---
+
+ /// Normalize rotation
+ NormalizeRot :: proc(q: Rot) -> Rot ---
+
+ /// Integrate rotation from angular velocity
+ /// @param q1 initial rotation
+ /// @param deltaAngle the angular displacement in radians
+ IntegrateRotation :: proc(q1: Rot, deltaAngle: f32) -> Rot ---
+
+ /// Get the length squared of this vector
+ LengthSquared :: proc(v: Vec2) -> f32 ---
+
+ /// Get the distance squared between points
+ DistanceSquared :: proc(a: Vec2, b: Vec2) -> f32 ---
+
+ /// Make a rotation using an angle in radians
+ MakeRot :: proc(radians: f32) -> Rot ---
+
+ /// Compute the rotation between two unit vectors
+ ComputeRotationBetweenUnitVectors :: proc(v1: Vec2, v2: Vec2) -> Rot ---
+
+ /// Is this rotation normalized?
+ IsNormalized :: proc(q: Rot) -> bool ---
+
+ /// Normalized linear interpolation
+ /// https://fgiesen.wordpress.com/2012/08/15/linear-interpolation-past-present-and-future/
+ /// https://web.archive.org/web/20170825184056/http://number-none.com/product/Understanding%20Slerp,%20Then%20Not%20Using%20It/
+ NLerp :: proc(q1: Rot, q2: Rot, t: f32) -> Rot ---
+
+ /// Compute the angular velocity necessary to rotate between two rotations over a give time
+ /// @param q1 initial rotation
+ /// @param q2 final rotation
+ /// @param inv_h inverse time step
+ ComputeAngularVelocity :: proc(q1: Rot, q2: Rot, inv_h: f32) -> f32 ---
+
+ /// Get the angle in radians in the range [-pi, pi]
+ Rot_GetAngle :: proc(q: Rot) -> f32 ---
+
+ /// Get the x-axis
+ Rot_GetXAxis :: proc(q: Rot) -> Vec2 ---
+
+ /// Get the y-axis
+ Rot_GetYAxis :: proc(q: Rot) -> Vec2 ---
+
+ /// Multiply two rotations: q * r
+ MulRot :: proc(q: Rot, r: Rot) -> Rot ---
+
+ /// Transpose multiply two rotations: qT * r
+ InvMulRot :: proc(q: Rot, r: Rot) -> Rot ---
+
+ /// relative angle between b and a (rot_b * inv(rot_a))
+ RelativeAngle :: proc(b: Rot, a: Rot) -> f32 ---
+
+ /// Convert an angle in the range [-2*pi, 2*pi] into the range [-pi, pi]
+ UnwindAngle :: proc(radians: f32) -> f32 ---
+
+ /// Convert any into the range [-pi, pi] (slow)
+ UnwindLargeAngle :: proc(radians: f32) -> f32 ---
+
+ /// Rotate a vector
+ RotateVector :: proc(q: Rot, v: Vec2) -> Vec2 ---
+
+ /// Inverse rotate a vector
+ InvRotateVector :: proc(q: Rot, v: Vec2) -> Vec2 ---
+
+ /// Transform a point (e.g. local space to world space)
+ TransformPoint :: proc(t: Transform, p: Vec2) -> Vec2 ---
+
+ /// Inverse transform a point (e.g. world space to local space)
+ InvTransformPoint :: proc(t: Transform, p: Vec2) -> Vec2 ---
+
+ /// Multiply two transforms. If the result is applied to a point p local to frame B,
+ /// the transform would first convert p to a point local to frame A, then into a point
+ /// in the world frame.
+ /// v2 = A.q.Rot(B.q.Rot(v1) + B.p) + A.p
+ /// = (A.q * B.q).Rot(v1) + A.q.Rot(B.p) + A.p
+ MulTransforms :: proc(A: Transform, B: Transform) -> Transform ---
+
+ /// Creates a transform that converts a local point in frame B to a local point in frame A.
+ /// v2 = A.q' * (B.q * v1 + B.p - A.p)
+ /// = A.q' * B.q * v1 + A.q' * (B.p - A.p)
+ InvMulTransforms :: proc(A: Transform, B: Transform) -> Transform ---
+
+ /// Multiply a 2-by-2 matrix times a 2D vector
+ MulMV :: proc(A: Mat22, v: Vec2) -> Vec2 ---
+
+ /// Get the inverse of a 2-by-2 matrix
+ GetInverse22 :: proc(A: Mat22) -> Mat22 ---
+
+ /// Solve A * x = b, where b is a column vector. This is more efficient
+ /// than computing the inverse in one-shot cases.
+ Solve22 :: proc(A: Mat22, b: Vec2) -> Vec2 ---
+
+ /// Does a fully contain b
+ AABB_Contains :: proc(a: AABB, b: AABB) -> bool ---
+
+ /// Get the center of the AABB.
+ AABB_Center :: proc(a: AABB) -> Vec2 ---
+
+ /// Get the extents of the AABB (half-widths).
+ AABB_Extents :: proc(a: AABB) -> Vec2 ---
+
+ /// Union of two AABBs
+ AABB_Union :: proc(a: AABB, b: AABB) -> AABB ---
+
+ /// Is this a valid number? Not NaN or infinity.
+ IsValidFloat :: proc(a: f32) -> bool ---
+
+ /// Is this a valid vector? Not NaN or infinity.
+ IsValidVec2 :: proc(v: Vec2) -> bool ---
+
+ /// Is this a valid rotation? Not NaN or infinity. Is normalized.
+ IsValidRotation :: proc(q: Rot) -> bool ---
+
+ /// Is this a valid bounding box? Not Nan or infinity. Upper bound greater than or equal to lower bound.
+ IsValidAABB :: proc(aabb: AABB) -> bool ---
+
+ /// Box2D bases all length units on meters, but you may need different units for your game.
+ /// You can set this value to use different units. This should be done at application startup
+ /// and only modified once. Default value is 1.
+ /// For example, if your game uses pixels for units you can use pixels for all length values
+ /// sent to Box2D. There should be no extra cost. However, Box2D has some internal tolerances
+ /// and thresholds that have been tuned for meters. By calling this function, Box2D is able
+ /// to adjust those tolerances and thresholds to improve accuracy.
+ /// A good rule of thumb is to pass the height of your player character to this function. So
+ /// if your player character is 32 pixels high, then pass 32 to this function. Then you may
+ /// confidently use pixels for all the length values sent to Box2D. All length values returned
+ /// from Box2D will also be pixels because Box2D does not do any scaling internally.
+ /// However, you are now on the hook for coming up with good values for gravity, density, and
+ /// forces.
+ /// @warning This must be modified before any calls to Box2D
+ SetLengthUnitsPerMeter :: proc(lengthUnits: f32) ---
+
+ /// Get the current length units per meter.
+ GetLengthUnitsPerMeter :: proc() -> f32 ---
+}
diff --git a/odin-c-bindgen/examples/box2d/box2d/types.odin b/odin-c-bindgen/examples/box2d/box2d/types.odin
@@ -0,0 +1,1384 @@
+// SPDX-FileCopyrightText: 2023 Erin Catto
+// SPDX-License-Identifier: MIT
+package box2d
+
+import "core:c"
+
+_ :: c
+
+foreign import lib "box2d.lib"
+
+DEFAULT_CATEGORY_BITS :: 0x0001
+// DEFAULT_MASK_BITS :: UINT64_MAX
+
+/// Task interface
+/// This is prototype for a Box2D task. Your task system is expected to invoke the Box2D task with these arguments.
+/// The task spans a range of the parallel-for: [startIndex, endIndex)
+/// The worker index must correctly identify each worker in the user thread pool, expected in [0, workerCount).
+/// A worker must only exist on only one thread at a time and is analogous to the thread index.
+/// The task context is the context pointer sent from Box2D when it is enqueued.
+/// The startIndex and endIndex are expected in the range [0, itemCount) where itemCount is the argument to b2EnqueueTaskCallback
+/// below. Box2D expects startIndex < endIndex and will execute a loop like this:
+///
+/// @code{.c}
+/// for (int i = startIndex; i < endIndex; ++i)
+/// {
+/// DoWork();
+/// }
+/// @endcode
+/// @ingroup world
+TaskCallback :: proc "c" (c.int, c.int, u32, rawptr)
+
+/// These functions can be provided to Box2D to invoke a task system. These are designed to work well with enkiTS.
+/// Returns a pointer to the user's task object. May be nullptr. A nullptr indicates to Box2D that the work was executed
+/// serially within the callback and there is no need to call b2FinishTaskCallback.
+/// The itemCount is the number of Box2D work items that are to be partitioned among workers by the user's task system.
+/// This is essentially a parallel-for. The minRange parameter is a suggestion of the minimum number of items to assign
+/// per worker to reduce overhead. For example, suppose the task is small and that itemCount is 16. A minRange of 8 suggests
+/// that your task system should split the work items among just two workers, even if you have more available.
+/// In general the range [startIndex, endIndex) send to b2TaskCallback should obey:
+/// endIndex - startIndex >= minRange
+/// The exception of course is when itemCount < minRange.
+/// @ingroup world
+EnqueueTaskCallback :: proc "c" (TaskCallback, c.int, c.int, rawptr, rawptr) -> rawptr
+
+/// Finishes a user task object that wraps a Box2D task.
+/// @ingroup world
+FinishTaskCallback :: proc "c" (rawptr, rawptr)
+
+/// Optional friction mixing callback. This intentionally provides no context objects because this is called
+/// from a worker thread.
+/// @warning This function should not attempt to modify Box2D state or user application state.
+FrictionCallback :: proc "c" (f32, c.int, f32, c.int) -> f32
+
+/// Optional restitution mixing callback. This intentionally provides no context objects because this is called
+/// from a worker thread.
+/// @warning This function should not attempt to modify Box2D state or user application state.
+RestitutionCallback :: proc "c" (f32, c.int, f32, c.int) -> f32
+
+/// Result from b2World_RayCastClosest
+/// @ingroup world
+RayResult :: struct {
+ shapeId: ShapeId,
+ point: Vec2,
+ normal: Vec2,
+ fraction: f32,
+ nodeVisits: c.int,
+ leafVisits: c.int,
+ hit: bool,
+}
+
+/// World definition used to create a simulation world.
+/// Must be initialized using b2DefaultWorldDef().
+/// @ingroup world
+WorldDef :: struct {
+ /// Gravity vector. Box2D has no up-vector defined.
+ gravity: Vec2,
+
+ /// Restitution speed threshold, usually in m/s. Collisions above this
+ /// speed have restitution applied (will bounce).
+ restitutionThreshold: f32,
+
+ /// Threshold speed for hit events. Usually meters per second.
+ hitEventThreshold: f32,
+
+ /// Contact stiffness. Cycles per second. Increasing this increases the speed of overlap recovery, but can introduce jitter.
+ contactHertz: f32,
+
+ /// Contact bounciness. Non-dimensional. You can speed up overlap recovery by decreasing this with
+ /// the trade-off that overlap resolution becomes more energetic.
+ contactDampingRatio: f32,
+
+ /// This parameter controls how fast overlap is resolved and usually has units of meters per second. This only
+ /// puts a cap on the resolution speed. The resolution speed is increased by increasing the hertz and/or
+ /// decreasing the damping ratio.
+ contactPushMaxSpeed: f32,
+
+ /// Joint stiffness. Cycles per second.
+ jointHertz: f32,
+
+ /// Joint bounciness. Non-dimensional.
+ jointDampingRatio: f32,
+
+ /// Maximum linear speed. Usually meters per second.
+ maximumLinearSpeed: f32,
+
+ /// Optional mixing callback for friction. The default uses sqrt(frictionA * frictionB).
+ frictionCallback: FrictionCallback,
+
+ /// Optional mixing callback for restitution. The default uses max(restitutionA, restitutionB).
+ restitutionCallback: RestitutionCallback,
+
+ /// Can bodies go to sleep to improve performance
+ enableSleep: bool,
+
+ /// Enable continuous collision
+ enableContinuous: bool,
+
+ /// Number of workers to use with the provided task system. Box2D performs best when using only
+ /// performance cores and accessing a single L2 cache. Efficiency cores and hyper-threading provide
+ /// little benefit and may even harm performance.
+ /// @note Box2D does not create threads. This is the number of threads your applications has created
+ /// that you are allocating to b2World_Step.
+ /// @warning Do not modify the default value unless you are also providing a task system and providing
+ /// task callbacks (enqueueTask and finishTask).
+ workerCount: c.int,
+
+ /// Function to spawn tasks
+ enqueueTask: EnqueueTaskCallback,
+
+ /// Function to finish a task
+ finishTask: FinishTaskCallback,
+
+ /// User context that is provided to enqueueTask and finishTask
+ userTaskContext: rawptr,
+
+ /// User data
+ userData: rawptr,
+
+ /// Used internally to detect a valid definition. DO NOT SET.
+ internalValue: c.int,
+}
+
+/// The body simulation type.
+/// Each body is one of these three types. The type determines how the body behaves in the simulation.
+/// @ingroup body
+BodyType :: enum c.int {
+ /// zero mass, zero velocity, may be manually moved
+ staticBody = 0,
+
+ /// zero mass, velocity set by user, moved by solver
+ kinematicBody = 1,
+
+ /// positive mass, velocity determined by forces, moved by solver
+ dynamicBody = 2,
+
+ /// number of body types
+ bodyTypeCount,
+}
+
+/// A body definition holds all the data needed to construct a rigid body.
+/// You can safely re-use body definitions. Shapes are added to a body after construction.
+/// Body definitions are temporary objects used to bundle creation parameters.
+/// Must be initialized using b2DefaultBodyDef().
+/// @ingroup body
+BodyDef :: struct {
+ /// The body type: static, kinematic, or dynamic.
+ type: BodyType,
+
+ /// The initial world position of the body. Bodies should be created with the desired position.
+ /// @note Creating bodies at the origin and then moving them nearly doubles the cost of body creation, especially
+ /// if the body is moved after shapes have been added.
+ position: Vec2,
+
+ /// The initial world rotation of the body. Use b2MakeRot() if you have an angle.
+ rotation: Rot,
+
+ /// The initial linear velocity of the body's origin. Usually in meters per second.
+ linearVelocity: Vec2,
+
+ /// The initial angular velocity of the body. Radians per second.
+ angularVelocity: f32,
+
+ /// Linear damping is used to reduce the linear velocity. The damping parameter
+ /// can be larger than 1 but the damping effect becomes sensitive to the
+ /// time step when the damping parameter is large.
+ /// Generally linear damping is undesirable because it makes objects move slowly
+ /// as if they are floating.
+ linearDamping: f32,
+
+ /// Angular damping is used to reduce the angular velocity. The damping parameter
+ /// can be larger than 1.0f but the damping effect becomes sensitive to the
+ /// time step when the damping parameter is large.
+ /// Angular damping can be use slow down rotating bodies.
+ angularDamping: f32,
+
+ /// Scale the gravity applied to this body. Non-dimensional.
+ gravityScale: f32,
+
+ /// Sleep speed threshold, default is 0.05 meters per second
+ sleepThreshold: f32,
+
+ /// Optional body name for debugging. Up to 31 characters (excluding null termination)
+ name: cstring,
+
+ /// Use this to store application specific body data.
+ userData: rawptr,
+
+ /// Set this flag to false if this body should never fall asleep.
+ enableSleep: bool,
+
+ /// Is this body initially awake or sleeping?
+ isAwake: bool,
+
+ /// Should this body be prevented from rotating? Useful for characters.
+ fixedRotation: bool,
+
+ /// Treat this body as high speed object that performs continuous collision detection
+ /// against dynamic and kinematic bodies, but not other bullet bodies.
+ /// @warning Bullets should be used sparingly. They are not a solution for general dynamic-versus-dynamic
+ /// continuous collision. They may interfere with joint constraints.
+ isBullet: bool,
+
+ /// Used to disable a body. A disabled body does not move or collide.
+ isEnabled: bool,
+
+ /// This allows this body to bypass rotational speed limits. Should only be used
+ /// for circular objects, like wheels.
+ allowFastRotation: bool,
+
+ /// Used internally to detect a valid definition. DO NOT SET.
+ internalValue: c.int,
+}
+
+/// This is used to filter collision on shapes. It affects shape-vs-shape collision
+/// and shape-versus-query collision (such as b2World_CastRay).
+/// @ingroup shape
+Filter :: struct {
+ /// The collision category bits. Normally you would just set one bit. The category bits should
+ /// represent your application object types. For example:
+ /// @code{
+ categoryBits: u64,
+
+ /// The collision mask bits. This states the categories that this
+ /// shape would accept for collision.
+ /// For example, you may want your player to only collide with static objects
+ /// and other players.
+ /// @code{
+ maskBits: u64,
+
+ /// Collision groups allow a certain group of objects to never collide (negative)
+ /// or always collide (positive). A group index of zero has no effect. Non-zero group filtering
+ /// always wins against the mask bits.
+ /// For example, you may want ragdolls to collide with other ragdolls but you don't want
+ /// ragdoll self-collision. In this case you would give each ragdoll a unique negative group index
+ /// and apply that group index to all shapes on the ragdoll.
+ groupIndex: c.int,
+}
+
+/// The query filter is used to filter collisions between queries and shapes. For example,
+/// you may want a ray-cast representing a projectile to hit players and the static environment
+/// but not debris.
+/// @ingroup shape
+QueryFilter :: struct {
+ /// The collision category bits of this query. Normally you would just set one bit.
+ categoryBits: u64,
+
+ /// The collision mask bits. This states the shape categories that this
+ /// query would accept for collision.
+ maskBits: u64,
+}
+
+/// Shape type
+/// @ingroup shape
+ShapeType :: enum c.int {
+ /// A circle with an offset
+ circleShape,
+
+ /// A capsule is an extruded circle
+ capsuleShape,
+
+ /// A line segment
+ segmentShape,
+
+ /// A convex polygon
+ polygonShape,
+
+ /// A line segment owned by a chain shape
+ chainSegmentShape,
+
+ /// The number of shape types
+ shapeTypeCount,
+}
+
+/// Used to create a shape.
+/// This is a temporary object used to bundle shape creation parameters. You may use
+/// the same shape definition to create multiple shapes.
+/// Must be initialized using b2DefaultShapeDef().
+/// @ingroup shape
+ShapeDef :: struct {
+ /// Use this to store application specific shape data.
+ userData: rawptr,
+
+ /// The Coulomb (dry) friction coefficient, usually in the range [0,1].
+ friction: f32,
+
+ /// The coefficient of restitution (bounce) usually in the range [0,1].
+ /// https://en.wikipedia.org/wiki/Coefficient_of_restitution
+ restitution: f32,
+
+ /// The rolling resistance usually in the range [0,1].
+ rollingResistance: f32,
+
+ /// The tangent speed for conveyor belts
+ tangentSpeed: f32,
+
+ /// User material identifier. This is passed with query results and to friction and restitution
+ /// combining functions. It is not used internally.
+ material: c.int,
+
+ /// The density, usually in kg/m^2.
+ density: f32,
+
+ /// Collision filtering data.
+ filter: Filter,
+
+ /// Custom debug draw color.
+ customColor: u32,
+
+ /// A sensor shape generates overlap events but never generates a collision response.
+ /// Sensors do not collide with other sensors and do not have continuous collision.
+ /// Instead, use a ray or shape cast for those scenarios.
+ isSensor: bool,
+
+ /// Enable contact events for this shape. Only applies to kinematic and dynamic bodies. Ignored for sensors.
+ enableContactEvents: bool,
+
+ /// Enable hit events for this shape. Only applies to kinematic and dynamic bodies. Ignored for sensors.
+ enableHitEvents: bool,
+
+ /// Enable pre-solve contact events for this shape. Only applies to dynamic bodies. These are expensive
+ /// and must be carefully handled due to threading. Ignored for sensors.
+ enablePreSolveEvents: bool,
+
+ /// Normally shapes on static bodies don't invoke contact creation when they are added to the world. This overrides
+ /// that behavior and causes contact creation. This significantly slows down static body creation which can be important
+ /// when there are many static shapes.
+ /// This is implicitly always true for sensors, dynamic bodies, and kinematic bodies.
+ invokeContactCreation: bool,
+
+ /// Should the body update the mass properties when this shape is created. Default is true.
+ updateBodyMass: bool,
+
+ /// Used internally to detect a valid definition. DO NOT SET.
+ internalValue: c.int,
+}
+
+/// Surface materials allow chain shapes to have per segment surface properties.
+/// @ingroup shape
+SurfaceMaterial :: struct {
+ /// The Coulomb (dry) friction coefficient, usually in the range [0,1].
+ friction: f32,
+
+ /// The coefficient of restitution (bounce) usually in the range [0,1].
+ /// https://en.wikipedia.org/wiki/Coefficient_of_restitution
+ restitution: f32,
+
+ /// The rolling resistance usually in the range [0,1].
+ rollingResistance: f32,
+
+ /// The tangent speed for conveyor belts
+ tangentSpeed: f32,
+
+ /// User material identifier. This is passed with query results and to friction and restitution
+ /// combining functions. It is not used internally.
+ material: c.int,
+
+ /// Custom debug draw color.
+ customColor: u32,
+}
+
+/// Used to create a chain of line segments. This is designed to eliminate ghost collisions with some limitations.
+/// - chains are one-sided
+/// - chains have no mass and should be used on static bodies
+/// - chains have a counter-clockwise winding order
+/// - chains are either a loop or open
+/// - a chain must have at least 4 points
+/// - the distance between any two points must be greater than B2_LINEAR_SLOP
+/// - a chain shape should not self intersect (this is not validated)
+/// - an open chain shape has NO COLLISION on the first and final edge
+/// - you may overlap two open chains on their first three and/or last three points to get smooth collision
+/// - a chain shape creates multiple line segment shapes on the body
+/// https://en.wikipedia.org/wiki/Polygonal_chain
+/// Must be initialized using b2DefaultChainDef().
+/// @warning Do not use chain shapes unless you understand the limitations. This is an advanced feature.
+/// @ingroup shape
+ChainDef :: struct {
+ /// Use this to store application specific shape data.
+ userData: rawptr,
+
+ /// An array of at least 4 points. These are cloned and may be temporary.
+ points: [^]Vec2,
+
+ /// The point count, must be 4 or more.
+ count: c.int,
+
+ /// Surface materials for each segment. These are cloned.
+ materials: [^]SurfaceMaterial,
+
+ /// The material count. Must be 1 or count. This allows you to provide one
+ /// material for all segments or a unique material per segment.
+ materialCount: c.int,
+
+ /// Contact filtering data.
+ filter: Filter,
+
+ /// Indicates a closed chain formed by connecting the first and last points
+ isLoop: bool,
+
+ /// Used internally to detect a valid definition. DO NOT SET.
+ internalValue: c.int,
+}
+
+//! @cond
+/// Profiling data. Times are in milliseconds.
+Profile :: struct {
+ step: f32,
+ pairs: f32,
+ collide: f32,
+ solve: f32,
+ mergeIslands: f32,
+ prepareStages: f32,
+ solveConstraints: f32,
+ prepareConstraints: f32,
+ integrateVelocities: f32,
+ warmStart: f32,
+ solveImpulses: f32,
+ integratePositions: f32,
+ relaxImpulses: f32,
+ applyRestitution: f32,
+ storeImpulses: f32,
+ splitIslands: f32,
+ transforms: f32,
+ hitEvents: f32,
+ refit: f32,
+ bullets: f32,
+ sleepIslands: f32,
+ sensors: f32,
+}
+
+/// Counters that give details of the simulation size.
+Counters :: struct {
+ bodyCount: c.int,
+ shapeCount: c.int,
+ contactCount: c.int,
+ jointCount: c.int,
+ islandCount: c.int,
+ stackUsed: c.int,
+ staticTreeHeight: c.int,
+ treeHeight: c.int,
+ byteCount: c.int,
+ taskCount: c.int,
+ colorCounts: [12]c.int,
+}
+
+/// Joint type enumeration
+///
+/// This is useful because all joint types use b2JointId and sometimes you
+/// want to get the type of a joint.
+/// @ingroup joint
+JointType :: enum c.int {
+ distanceJoint,
+ motorJoint,
+ mouseJoint,
+ nullJoint,
+ prismaticJoint,
+ revoluteJoint,
+ weldJoint,
+ wheelJoint,
+}
+
+/// Distance joint definition
+///
+/// This requires defining an anchor point on both
+/// bodies and the non-zero distance of the distance joint. The definition uses
+/// local anchor points so that the initial configuration can violate the
+/// constraint slightly. This helps when saving and loading a game.
+/// @ingroup distance_joint
+DistanceJointDef :: struct {
+ /// The first attached body
+ bodyIdA: BodyId,
+
+ /// The second attached body
+ bodyIdB: BodyId,
+
+ /// The local anchor point relative to bodyA's origin
+ localAnchorA: Vec2,
+
+ /// The local anchor point relative to bodyB's origin
+ localAnchorB: Vec2,
+
+ /// The rest length of this joint. Clamped to a stable minimum value.
+ length: f32,
+
+ /// Enable the distance constraint to behave like a spring. If false
+ /// then the distance joint will be rigid, overriding the limit and motor.
+ enableSpring: bool,
+
+ /// The spring linear stiffness Hertz, cycles per second
+ hertz: f32,
+
+ /// The spring linear damping ratio, non-dimensional
+ dampingRatio: f32,
+
+ /// Enable/disable the joint limit
+ enableLimit: bool,
+
+ /// Minimum length. Clamped to a stable minimum value.
+ minLength: f32,
+
+ /// Maximum length. Must be greater than or equal to the minimum length.
+ maxLength: f32,
+
+ /// Enable/disable the joint motor
+ enableMotor: bool,
+
+ /// The maximum motor force, usually in newtons
+ maxMotorForce: f32,
+
+ /// The desired motor speed, usually in meters per second
+ motorSpeed: f32,
+
+ /// Set this flag to true if the attached bodies should collide
+ collideConnected: bool,
+
+ /// User data pointer
+ userData: rawptr,
+
+ /// Used internally to detect a valid definition. DO NOT SET.
+ internalValue: c.int,
+}
+
+/// A motor joint is used to control the relative motion between two bodies
+///
+/// A typical usage is to control the movement of a dynamic body with respect to the ground.
+/// @ingroup motor_joint
+MotorJointDef :: struct {
+ /// The first attached body
+ bodyIdA: BodyId,
+
+ /// The second attached body
+ bodyIdB: BodyId,
+
+ /// Position of bodyB minus the position of bodyA, in bodyA's frame
+ linearOffset: Vec2,
+
+ /// The bodyB angle minus bodyA angle in radians
+ angularOffset: f32,
+
+ /// The maximum motor force in newtons
+ maxForce: f32,
+
+ /// The maximum motor torque in newton-meters
+ maxTorque: f32,
+
+ /// Position correction factor in the range [0,1]
+ correctionFactor: f32,
+
+ /// Set this flag to true if the attached bodies should collide
+ collideConnected: bool,
+
+ /// User data pointer
+ userData: rawptr,
+
+ /// Used internally to detect a valid definition. DO NOT SET.
+ internalValue: c.int,
+}
+
+/// A mouse joint is used to make a point on a body track a specified world point.
+///
+/// This a soft constraint and allows the constraint to stretch without
+/// applying huge forces. This also applies rotation constraint heuristic to improve control.
+/// @ingroup mouse_joint
+MouseJointDef :: struct {
+ /// The first attached body. This is assumed to be static.
+ bodyIdA: BodyId,
+
+ /// The second attached body.
+ bodyIdB: BodyId,
+
+ /// The initial target point in world space
+ target: Vec2,
+
+ /// Stiffness in hertz
+ hertz: f32,
+
+ /// Damping ratio, non-dimensional
+ dampingRatio: f32,
+
+ /// Maximum force, typically in newtons
+ maxForce: f32,
+
+ /// Set this flag to true if the attached bodies should collide.
+ collideConnected: bool,
+
+ /// User data pointer
+ userData: rawptr,
+
+ /// Used internally to detect a valid definition. DO NOT SET.
+ internalValue: c.int,
+}
+
+/// A null joint is used to disable collision between two specific bodies.
+///
+/// @ingroup null_joint
+NullJointDef :: struct {
+ /// The first attached body.
+ bodyIdA: BodyId,
+
+ /// The second attached body.
+ bodyIdB: BodyId,
+
+ /// User data pointer
+ userData: rawptr,
+
+ /// Used internally to detect a valid definition. DO NOT SET.
+ internalValue: c.int,
+}
+
+/// Prismatic joint definition
+///
+/// This requires defining a line of motion using an axis and an anchor point.
+/// The definition uses local anchor points and a local axis so that the initial
+/// configuration can violate the constraint slightly. The joint translation is zero
+/// when the local anchor points coincide in world space.
+/// @ingroup prismatic_joint
+PrismaticJointDef :: struct {
+ /// The first attached body
+ bodyIdA: BodyId,
+
+ /// The second attached body
+ bodyIdB: BodyId,
+
+ /// The local anchor point relative to bodyA's origin
+ localAnchorA: Vec2,
+
+ /// The local anchor point relative to bodyB's origin
+ localAnchorB: Vec2,
+
+ /// The local translation unit axis in bodyA
+ localAxisA: Vec2,
+
+ /// The constrained angle between the bodies: bodyB_angle - bodyA_angle
+ referenceAngle: f32,
+
+ /// Enable a linear spring along the prismatic joint axis
+ enableSpring: bool,
+
+ /// The spring stiffness Hertz, cycles per second
+ hertz: f32,
+
+ /// The spring damping ratio, non-dimensional
+ dampingRatio: f32,
+
+ /// Enable/disable the joint limit
+ enableLimit: bool,
+
+ /// The lower translation limit
+ lowerTranslation: f32,
+
+ /// The upper translation limit
+ upperTranslation: f32,
+
+ /// Enable/disable the joint motor
+ enableMotor: bool,
+
+ /// The maximum motor force, typically in newtons
+ maxMotorForce: f32,
+
+ /// The desired motor speed, typically in meters per second
+ motorSpeed: f32,
+
+ /// Set this flag to true if the attached bodies should collide
+ collideConnected: bool,
+
+ /// User data pointer
+ userData: rawptr,
+
+ /// Used internally to detect a valid definition. DO NOT SET.
+ internalValue: c.int,
+}
+
+/// Revolute joint definition
+///
+/// This requires defining an anchor point where the bodies are joined.
+/// The definition uses local anchor points so that the
+/// initial configuration can violate the constraint slightly. You also need to
+/// specify the initial relative angle for joint limits. This helps when saving
+/// and loading a game.
+/// The local anchor points are measured from the body's origin
+/// rather than the center of mass because:
+/// 1. you might not know where the center of mass will be
+/// 2. if you add/remove shapes from a body and recompute the mass, the joints will be broken
+/// @ingroup revolute_joint
+RevoluteJointDef :: struct {
+ /// The first attached body
+ bodyIdA: BodyId,
+
+ /// The second attached body
+ bodyIdB: BodyId,
+
+ /// The local anchor point relative to bodyA's origin
+ localAnchorA: Vec2,
+
+ /// The local anchor point relative to bodyB's origin
+ localAnchorB: Vec2,
+
+ /// The bodyB angle minus bodyA angle in the reference state (radians).
+ /// This defines the zero angle for the joint limit.
+ referenceAngle: f32,
+
+ /// Enable a rotational spring on the revolute hinge axis
+ enableSpring: bool,
+
+ /// The spring stiffness Hertz, cycles per second
+ hertz: f32,
+
+ /// The spring damping ratio, non-dimensional
+ dampingRatio: f32,
+
+ /// A flag to enable joint limits
+ enableLimit: bool,
+
+ /// The lower angle for the joint limit in radians
+ lowerAngle: f32,
+
+ /// The upper angle for the joint limit in radians
+ upperAngle: f32,
+
+ /// A flag to enable the joint motor
+ enableMotor: bool,
+
+ /// The maximum motor torque, typically in newton-meters
+ maxMotorTorque: f32,
+
+ /// The desired motor speed in radians per second
+ motorSpeed: f32,
+
+ /// Scale the debug draw
+ drawSize: f32,
+
+ /// Set this flag to true if the attached bodies should collide
+ collideConnected: bool,
+
+ /// User data pointer
+ userData: rawptr,
+
+ /// Used internally to detect a valid definition. DO NOT SET.
+ internalValue: c.int,
+}
+
+/// Weld joint definition
+///
+/// A weld joint connect to bodies together rigidly. This constraint provides springs to mimic
+/// soft-body simulation.
+/// @note The approximate solver in Box2D cannot hold many bodies together rigidly
+/// @ingroup weld_joint
+WeldJointDef :: struct {
+ /// The first attached body
+ bodyIdA: BodyId,
+
+ /// The second attached body
+ bodyIdB: BodyId,
+
+ /// The local anchor point relative to bodyA's origin
+ localAnchorA: Vec2,
+
+ /// The local anchor point relative to bodyB's origin
+ localAnchorB: Vec2,
+
+ /// The bodyB angle minus bodyA angle in the reference state (radians)
+ referenceAngle: f32,
+
+ /// Linear stiffness expressed as Hertz (cycles per second). Use zero for maximum stiffness.
+ linearHertz: f32,
+
+ /// Angular stiffness as Hertz (cycles per second). Use zero for maximum stiffness.
+ angularHertz: f32,
+
+ /// Linear damping ratio, non-dimensional. Use 1 for critical damping.
+ linearDampingRatio: f32,
+
+ /// Linear damping ratio, non-dimensional. Use 1 for critical damping.
+ angularDampingRatio: f32,
+
+ /// Set this flag to true if the attached bodies should collide
+ collideConnected: bool,
+
+ /// User data pointer
+ userData: rawptr,
+
+ /// Used internally to detect a valid definition. DO NOT SET.
+ internalValue: c.int,
+}
+
+/// Wheel joint definition
+///
+/// This requires defining a line of motion using an axis and an anchor point.
+/// The definition uses local anchor points and a local axis so that the initial
+/// configuration can violate the constraint slightly. The joint translation is zero
+/// when the local anchor points coincide in world space.
+/// @ingroup wheel_joint
+WheelJointDef :: struct {
+ /// The first attached body
+ bodyIdA: BodyId,
+
+ /// The second attached body
+ bodyIdB: BodyId,
+
+ /// The local anchor point relative to bodyA's origin
+ localAnchorA: Vec2,
+
+ /// The local anchor point relative to bodyB's origin
+ localAnchorB: Vec2,
+
+ /// The local translation unit axis in bodyA
+ localAxisA: Vec2,
+
+ /// Enable a linear spring along the local axis
+ enableSpring: bool,
+
+ /// Spring stiffness in Hertz
+ hertz: f32,
+
+ /// Spring damping ratio, non-dimensional
+ dampingRatio: f32,
+
+ /// Enable/disable the joint linear limit
+ enableLimit: bool,
+
+ /// The lower translation limit
+ lowerTranslation: f32,
+
+ /// The upper translation limit
+ upperTranslation: f32,
+
+ /// Enable/disable the joint rotational motor
+ enableMotor: bool,
+
+ /// The maximum motor torque, typically in newton-meters
+ maxMotorTorque: f32,
+
+ /// The desired motor speed in radians per second
+ motorSpeed: f32,
+
+ /// Set this flag to true if the attached bodies should collide
+ collideConnected: bool,
+
+ /// User data pointer
+ userData: rawptr,
+
+ /// Used internally to detect a valid definition. DO NOT SET.
+ internalValue: c.int,
+}
+
+/// The explosion definition is used to configure options for explosions. Explosions
+/// consider shape geometry when computing the impulse.
+/// @ingroup world
+ExplosionDef :: struct {
+ /// Mask bits to filter shapes
+ maskBits: u64,
+
+ /// The center of the explosion in world space
+ position: Vec2,
+
+ /// The radius of the explosion
+ radius: f32,
+
+ /// The falloff distance beyond the radius. Impulse is reduced to zero at this distance.
+ falloff: f32,
+
+ /// Impulse per unit length. This applies an impulse according to the shape perimeter that
+ /// is facing the explosion. Explosions only apply to circles, capsules, and polygons. This
+ /// may be negative for implosions.
+ impulsePerLength: f32,
+}
+
+/// A begin touch event is generated when a shape starts to overlap a sensor shape.
+SensorBeginTouchEvent :: struct {
+ /// The id of the sensor shape
+ sensorShapeId: ShapeId,
+
+ /// The id of the dynamic shape that began touching the sensor shape
+ visitorShapeId: ShapeId,
+}
+
+/// An end touch event is generated when a shape stops overlapping a sensor shape.
+/// These include things like setting the transform, destroying a body or shape, or changing
+/// a filter. You will also get an end event if the sensor or visitor are destroyed.
+/// Therefore you should always confirm the shape id is valid using b2Shape_IsValid.
+SensorEndTouchEvent :: struct {
+ /// The id of the sensor shape
+ /// @warning this shape may have been destroyed
+ /// @see b2Shape_IsValid
+ sensorShapeId: ShapeId,
+
+ /// The id of the dynamic shape that stopped touching the sensor shape
+ /// @warning this shape may have been destroyed
+ /// @see b2Shape_IsValid
+ visitorShapeId: ShapeId,
+}
+
+/// Sensor events are buffered in the Box2D world and are available
+/// as begin/end overlap event arrays after the time step is complete.
+/// Note: these may become invalid if bodies and/or shapes are destroyed
+SensorEvents :: struct {
+ /// Array of sensor begin touch events
+ beginEvents: ^SensorBeginTouchEvent,
+
+ /// Array of sensor end touch events
+ endEvents: ^SensorEndTouchEvent,
+
+ /// The number of begin touch events
+ beginCount: c.int,
+
+ /// The number of end touch events
+ endCount: c.int,
+}
+
+/// A begin touch event is generated when two shapes begin touching.
+ContactBeginTouchEvent :: struct {
+ /// Id of the first shape
+ shapeIdA: ShapeId,
+
+ /// Id of the second shape
+ shapeIdB: ShapeId,
+
+ /// The initial contact manifold. This is recorded before the solver is called,
+ /// so all the impulses will be zero.
+ manifold: Manifold,
+}
+
+/// An end touch event is generated when two shapes stop touching.
+/// You will get an end event if you do anything that destroys contacts previous to the last
+/// world step. These include things like setting the transform, destroying a body
+/// or shape, or changing a filter or body type.
+ContactEndTouchEvent :: struct {
+ /// Id of the first shape
+ /// @warning this shape may have been destroyed
+ /// @see b2Shape_IsValid
+ shapeIdA: ShapeId,
+
+ /// Id of the second shape
+ /// @warning this shape may have been destroyed
+ /// @see b2Shape_IsValid
+ shapeIdB: ShapeId,
+}
+
+/// A hit touch event is generated when two shapes collide with a speed faster than the hit speed threshold.
+ContactHitEvent :: struct {
+ /// Id of the first shape
+ shapeIdA: ShapeId,
+
+ /// Id of the second shape
+ shapeIdB: ShapeId,
+
+ /// Point where the shapes hit
+ point: Vec2,
+
+ /// Normal vector pointing from shape A to shape B
+ normal: Vec2,
+
+ /// The speed the shapes are approaching. Always positive. Typically in meters per second.
+ approachSpeed: f32,
+}
+
+/// Contact events are buffered in the Box2D world and are available
+/// as event arrays after the time step is complete.
+/// Note: these may become invalid if bodies and/or shapes are destroyed
+ContactEvents :: struct {
+ /// Array of begin touch events
+ beginEvents: ^ContactBeginTouchEvent,
+
+ /// Array of end touch events
+ endEvents: ^ContactEndTouchEvent,
+
+ /// Array of hit events
+ hitEvents: ^ContactHitEvent,
+
+ /// Number of begin touch events
+ beginCount: c.int,
+
+ /// Number of end touch events
+ endCount: c.int,
+
+ /// Number of hit events
+ hitCount: c.int,
+}
+
+/// Body move events triggered when a body moves.
+/// Triggered when a body moves due to simulation. Not reported for bodies moved by the user.
+/// This also has a flag to indicate that the body went to sleep so the application can also
+/// sleep that actor/entity/object associated with the body.
+/// On the other hand if the flag does not indicate the body went to sleep then the application
+/// can treat the actor/entity/object associated with the body as awake.
+/// This is an efficient way for an application to update game object transforms rather than
+/// calling functions such as b2Body_GetTransform() because this data is delivered as a contiguous array
+/// and it is only populated with bodies that have moved.
+/// @note If sleeping is disabled all dynamic and kinematic bodies will trigger move events.
+BodyMoveEvent :: struct {
+ transform: Transform,
+ bodyId: BodyId,
+ userData: rawptr,
+ fellAsleep: bool,
+}
+
+/// Body events are buffered in the Box2D world and are available
+/// as event arrays after the time step is complete.
+/// Note: this data becomes invalid if bodies are destroyed
+BodyEvents :: struct {
+ /// Array of move events
+ moveEvents: ^BodyMoveEvent,
+
+ /// Number of move events
+ moveCount: c.int,
+}
+
+/// The contact data for two shapes. By convention the manifold normal points
+/// from shape A to shape B.
+/// @see b2Shape_GetContactData() and b2Body_GetContactData()
+ContactData :: struct {
+ shapeIdA: ShapeId,
+ shapeIdB: ShapeId,
+ manifold: Manifold,
+}
+
+/// Prototype for a contact filter callback.
+/// This is called when a contact pair is considered for collision. This allows you to
+/// perform custom logic to prevent collision between shapes. This is only called if
+/// one of the two shapes has custom filtering enabled.
+/// Notes:
+/// - this function must be thread-safe
+/// - this is only called if one of the two shapes has enabled custom filtering
+/// - this is called only for awake dynamic bodies
+/// Return false if you want to disable the collision
+/// @see b2ShapeDef
+/// @warning Do not attempt to modify the world inside this callback
+/// @ingroup world
+CustomFilterFcn :: proc "c" (ShapeId, ShapeId, rawptr) -> bool
+
+/// Prototype for a pre-solve callback.
+/// This is called after a contact is updated. This allows you to inspect a
+/// contact before it goes to the solver. If you are careful, you can modify the
+/// contact manifold (e.g. modify the normal).
+/// Notes:
+/// - this function must be thread-safe
+/// - this is only called if the shape has enabled pre-solve events
+/// - this is called only for awake dynamic bodies
+/// - this is not called for sensors
+/// - the supplied manifold has impulse values from the previous step
+/// Return false if you want to disable the contact this step
+/// @warning Do not attempt to modify the world inside this callback
+/// @ingroup world
+PreSolveFcn :: proc "c" (ShapeId, ShapeId, ^Manifold, rawptr) -> bool
+
+/// Prototype callback for overlap queries.
+/// Called for each shape found in the query.
+/// @see b2World_OverlapABB
+/// @return false to terminate the query.
+/// @ingroup world
+OverlapResultFcn :: proc "c" (ShapeId, rawptr) -> bool
+
+/// Prototype callback for ray casts.
+/// Called for each shape found in the query. You control how the ray cast
+/// proceeds by returning a float:
+/// return -1: ignore this shape and continue
+/// return 0: terminate the ray cast
+/// return fraction: clip the ray to this point
+/// return 1: don't clip the ray and continue
+/// @param shapeId the shape hit by the ray
+/// @param point the point of initial intersection
+/// @param normal the normal vector at the point of intersection
+/// @param fraction the fraction along the ray at the point of intersection
+/// @param context the user context
+/// @return -1 to filter, 0 to terminate, fraction to clip the ray for closest hit, 1 to continue
+/// @see b2World_CastRay
+/// @ingroup world
+CastResultFcn :: proc "c" (ShapeId, Vec2, Vec2, f32, rawptr) -> f32
+
+/// These colors are used for debug draw and mostly match the named SVG colors.
+/// See https://www.rapidtables.com/web/color/index.html
+/// https://johndecember.com/html/spec/colorsvg.html
+/// https://upload.wikimedia.org/wikipedia/commons/2/2b/SVG_Recognized_color_keyword_names.svg
+HexColor :: enum c.int {
+ AliceBlue = 15792383,
+ AntiqueWhite = 16444375,
+ Aqua = 65535,
+ Aquamarine = 8388564,
+ Azure = 15794175,
+ Beige = 16119260,
+ Bisque = 16770244,
+ Black = 0,
+ BlanchedAlmond = 16772045,
+ Blue = 255,
+ BlueViolet = 9055202,
+ Brown = 10824234,
+ Burlywood = 14596231,
+ CadetBlue = 6266528,
+ Chartreuse = 8388352,
+ Chocolate = 13789470,
+ Coral = 16744272,
+ CornflowerBlue = 6591981,
+ Cornsilk = 16775388,
+ Crimson = 14423100,
+ Cyan = 65535,
+ DarkBlue = 139,
+ DarkCyan = 35723,
+ DarkGoldenRod = 12092939,
+ DarkGray = 11119017,
+ DarkGreen = 25600,
+ DarkKhaki = 12433259,
+ DarkMagenta = 9109643,
+ DarkOliveGreen = 5597999,
+ DarkOrange = 16747520,
+ DarkOrchid = 10040012,
+ DarkRed = 9109504,
+ DarkSalmon = 15308410,
+ DarkSeaGreen = 9419919,
+ DarkSlateBlue = 4734347,
+ DarkSlateGray = 3100495,
+ DarkTurquoise = 52945,
+ DarkViolet = 9699539,
+ DeepPink = 16716947,
+ DeepSkyBlue = 49151,
+ DimGray = 6908265,
+ DodgerBlue = 2003199,
+ FireBrick = 11674146,
+ FloralWhite = 16775920,
+ ForestGreen = 2263842,
+ Fuchsia = 16711935,
+ Gainsboro = 14474460,
+ GhostWhite = 16316671,
+ Gold = 16766720,
+ GoldenRod = 14329120,
+ Gray = 8421504,
+ Green = 32768,
+ GreenYellow = 11403055,
+ HoneyDew = 15794160,
+ HotPink = 16738740,
+ IndianRed = 13458524,
+ Indigo = 4915330,
+ Ivory = 16777200,
+ Khaki = 15787660,
+ Lavender = 15132410,
+ LavenderBlush = 16773365,
+ LawnGreen = 8190976,
+ LemonChiffon = 16775885,
+ LightBlue = 11393254,
+ LightCoral = 15761536,
+ LightCyan = 14745599,
+ LightGoldenRodYellow = 16448210,
+ LightGray = 13882323,
+ LightGreen = 9498256,
+ LightPink = 16758465,
+ LightSalmon = 16752762,
+ LightSeaGreen = 2142890,
+ LightSkyBlue = 8900346,
+ LightSlateGray = 7833753,
+ LightSteelBlue = 11584734,
+ LightYellow = 16777184,
+ Lime = 65280,
+ LimeGreen = 3329330,
+ Linen = 16445670,
+ Magenta = 16711935,
+ Maroon = 8388608,
+ MediumAquaMarine = 6737322,
+ MediumBlue = 205,
+ MediumOrchid = 12211667,
+ MediumPurple = 9662683,
+ MediumSeaGreen = 3978097,
+ MediumSlateBlue = 8087790,
+ MediumSpringGreen = 64154,
+ MediumTurquoise = 4772300,
+ MediumVioletRed = 13047173,
+ MidnightBlue = 1644912,
+ MintCream = 16121850,
+ MistyRose = 16770273,
+ Moccasin = 16770229,
+ NavajoWhite = 16768685,
+ Navy = 128,
+ OldLace = 16643558,
+ Olive = 8421376,
+ OliveDrab = 7048739,
+ Orange = 16753920,
+ OrangeRed = 16729344,
+ Orchid = 14315734,
+ PaleGoldenRod = 15657130,
+ PaleGreen = 10025880,
+ PaleTurquoise = 11529966,
+ PaleVioletRed = 14381203,
+ PapayaWhip = 16773077,
+ PeachPuff = 16767673,
+ Peru = 13468991,
+ Pink = 16761035,
+ Plum = 14524637,
+ PowderBlue = 11591910,
+ Purple = 8388736,
+ RebeccaPurple = 6697881,
+ Red = 16711680,
+ RosyBrown = 12357519,
+ RoyalBlue = 4286945,
+ SaddleBrown = 9127187,
+ Salmon = 16416882,
+ SandyBrown = 16032864,
+ SeaGreen = 3050327,
+ SeaShell = 16774638,
+ Sienna = 10506797,
+ Silver = 12632256,
+ SkyBlue = 8900331,
+ SlateBlue = 6970061,
+ SlateGray = 7372944,
+ Snow = 16775930,
+ SpringGreen = 65407,
+ SteelBlue = 4620980,
+ Tan = 13808780,
+ Teal = 32896,
+ Thistle = 14204888,
+ Tomato = 16737095,
+ Turquoise = 4251856,
+ Violet = 15631086,
+ Wheat = 16113331,
+ White = 16777215,
+ WhiteSmoke = 16119285,
+ Yellow = 16776960,
+ YellowGreen = 10145074,
+ Box2DRed = 14430514,
+ Box2DBlue = 3190463,
+ Box2DGreen = 9226532,
+ Box2DYellow = 16772748,
+}
+
+/// This struct holds callbacks you can implement to draw a Box2D world.
+/// This structure should be zero initialized.
+/// @ingroup world
+DebugDraw :: struct {
+ /// Draw a closed polygon provided in CCW order.
+ DrawPolygon: proc "c" (^Vec2, c.int, HexColor, rawptr),
+
+ /// Draw a solid closed polygon provided in CCW order.
+ DrawSolidPolygon: proc "c" (Transform, ^Vec2, c.int, f32, HexColor, rawptr),
+
+ /// Draw a circle.
+ DrawCircle: proc "c" (Vec2, f32, HexColor, rawptr),
+
+ /// Draw a solid circle.
+ DrawSolidCircle: proc "c" (Transform, f32, HexColor, rawptr),
+
+ /// Draw a solid capsule.
+ DrawSolidCapsule: proc "c" (Vec2, Vec2, f32, HexColor, rawptr),
+
+ /// Draw a line segment.
+ DrawSegment: proc "c" (Vec2, Vec2, HexColor, rawptr),
+
+ /// Draw a transform. Choose your own length scale.
+ DrawTransform: proc "c" (Transform, rawptr),
+
+ /// Draw a point.
+ DrawPoint: proc "c" (Vec2, f32, HexColor, rawptr),
+
+ /// Draw a string in world space
+ DrawString: proc "c" (Vec2, cstring, HexColor, rawptr),
+
+ /// Bounds to use if restricting drawing to a rectangular region
+ drawingBounds: AABB,
+
+ /// Option to restrict drawing to a rectangular region. May suffer from unstable depth sorting.
+ useDrawingBounds: bool,
+
+ /// Option to draw shapes
+ drawShapes: bool,
+
+ /// Option to draw joints
+ drawJoints: bool,
+
+ /// Option to draw additional information for joints
+ drawJointExtras: bool,
+
+ /// Option to draw the bounding boxes for shapes
+ drawAABBs: bool,
+
+ /// Option to draw the mass and center of mass of dynamic bodies
+ drawMass: bool,
+
+ /// Option to draw body names
+ drawBodyNames: bool,
+
+ /// Option to draw contact points
+ drawContacts: bool,
+
+ /// Option to visualize the graph coloring used for contacts and joints
+ drawGraphColors: bool,
+
+ /// Option to draw contact normals
+ drawContactNormals: bool,
+
+ /// Option to draw contact normal impulses
+ drawContactImpulses: bool,
+
+ /// Option to draw contact friction impulses
+ drawFrictionImpulses: bool,
+
+ /// User context that is passed as an argument to drawing callback functions
+ _context: rawptr,
+}
+
+@(default_calling_convention="c", link_prefix="b2")
+foreign lib {
+ /// Use this to initialize your world definition
+ /// @ingroup world
+ DefaultWorldDef :: proc() -> WorldDef ---
+
+ /// Use this to initialize your body definition
+ /// @ingroup body
+ DefaultBodyDef :: proc() -> BodyDef ---
+
+ /// Use this to initialize your filter
+ /// @ingroup shape
+ DefaultFilter :: proc() -> Filter ---
+
+ /// Use this to initialize your query filter
+ /// @ingroup shape
+ DefaultQueryFilter :: proc() -> QueryFilter ---
+
+ /// Use this to initialize your shape definition
+ /// @ingroup shape
+ DefaultShapeDef :: proc() -> ShapeDef ---
+
+ /// Use this to initialize your surface material
+ /// @ingroup shape
+ DefaultSurfaceMaterial :: proc() -> SurfaceMaterial ---
+
+ /// Use this to initialize your chain definition
+ /// @ingroup shape
+ DefaultChainDef :: proc() -> ChainDef ---
+
+ /// Use this to initialize your joint definition
+ /// @ingroup distance_joint
+ DefaultDistanceJointDef :: proc() -> DistanceJointDef ---
+
+ /// Use this to initialize your joint definition
+ /// @ingroup motor_joint
+ DefaultMotorJointDef :: proc() -> MotorJointDef ---
+
+ /// Use this to initialize your joint definition
+ /// @ingroup mouse_joint
+ DefaultMouseJointDef :: proc() -> MouseJointDef ---
+
+ /// Use this to initialize your joint definition
+ /// @ingroup null_joint
+ DefaultNullJointDef :: proc() -> NullJointDef ---
+
+ /// Use this to initialize your joint definition
+ /// @ingroupd prismatic_joint
+ DefaultPrismaticJointDef :: proc() -> PrismaticJointDef ---
+
+ /// Use this to initialize your joint definition.
+ /// @ingroup revolute_joint
+ DefaultRevoluteJointDef :: proc() -> RevoluteJointDef ---
+
+ /// Use this to initialize your joint definition
+ /// @ingroup weld_joint
+ DefaultWeldJointDef :: proc() -> WeldJointDef ---
+
+ /// Use this to initialize your joint definition
+ /// @ingroup wheel_joint
+ DefaultWheelJointDef :: proc() -> WheelJointDef ---
+
+ /// Use this to initialize your explosion definition
+ /// @ingroup world
+ DefaultExplosionDef :: proc() -> ExplosionDef ---
+
+ /// Use this to initialize your drawing interface. This allows you to implement a sub-set
+ /// of the drawing functions.
+ DefaultDebugDraw :: proc() -> DebugDraw ---
+}
diff --git a/odin-c-bindgen/examples/box2d/input/base.h b/odin-c-bindgen/examples/box2d/input/base.h
@@ -0,0 +1,130 @@
+// SPDX-FileCopyrightText: 2023 Erin Catto
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <stdint.h>
+
+// clang-format off
+//
+// Shared library macros
+#if defined( _MSC_VER ) && defined( box2d_EXPORTS )
+ // build the Windows DLL
+ #define BOX2D_EXPORT __declspec( dllexport )
+#elif defined( _MSC_VER ) && defined( BOX2D_DLL )
+ // using the Windows DLL
+ #define BOX2D_EXPORT __declspec( dllimport )
+#elif defined( box2d_EXPORTS )
+ // building or using the shared library
+ #define BOX2D_EXPORT __attribute__( ( visibility( "default" ) ) )
+#else
+ // static library
+ #define BOX2D_EXPORT
+#endif
+
+// C++ macros
+#ifdef __cplusplus
+ #define B2_API extern "C" BOX2D_EXPORT
+ #define B2_INLINE inline
+ #define B2_LITERAL(T) T
+ #define B2_ZERO_INIT {}
+#else
+ #define B2_API BOX2D_EXPORT
+ #define B2_INLINE static inline
+ /// Used for C literals like (b2Vec2){1.0f, 2.0f} where C++ requires b2Vec2{1.0f, 2.0f}
+ #define B2_LITERAL(T) (T)
+ #define B2_ZERO_INIT {0}
+#endif
+// clang-format on
+
+/**
+ * @defgroup base Base
+ * Base functionality
+ * @{
+ */
+
+/// Prototype for user allocation function
+/// @param size the allocation size in bytes
+/// @param alignment the required alignment, guaranteed to be a power of 2
+typedef void* b2AllocFcn( unsigned int size, int alignment );
+
+/// Prototype for user free function
+/// @param mem the memory previously allocated through `b2AllocFcn`
+typedef void b2FreeFcn( void* mem );
+
+/// Prototype for the user assert callback. Return 0 to skip the debugger break.
+typedef int b2AssertFcn( const char* condition, const char* fileName, int lineNumber );
+
+/// This allows the user to override the allocation functions. These should be
+/// set during application startup.
+B2_API void b2SetAllocator( b2AllocFcn* allocFcn, b2FreeFcn* freeFcn );
+
+/// @return the total bytes allocated by Box2D
+B2_API int b2GetByteCount( void );
+
+/// Override the default assert callback
+/// @param assertFcn a non-null assert callback
+B2_API void b2SetAssertFcn( b2AssertFcn* assertFcn );
+
+// see https://github.com/scottt/debugbreak
+#if defined( _MSC_VER )
+#define B2_BREAKPOINT __debugbreak()
+#elif defined( __GNUC__ ) || defined( __clang__ )
+#define B2_BREAKPOINT __builtin_trap()
+#else
+// Unknown compiler
+#include <assert.h>
+#define B2_BREAKPOINT assert( 0 )
+#endif
+
+#if !defined( NDEBUG ) || defined( B2_ENABLE_ASSERT )
+B2_API int b2InternalAssertFcn( const char* condition, const char* fileName, int lineNumber );
+#define B2_ASSERT( condition ) \
+ do \
+ { \
+ if ( !( condition ) && b2InternalAssertFcn( #condition, __FILE__, (int)__LINE__ ) ) \
+ B2_BREAKPOINT; \
+ } \
+ while ( 0 )
+#else
+#define B2_ASSERT( ... ) ( (void)0 )
+#endif
+
+/// Version numbering scheme.
+/// See https://semver.org/
+typedef struct b2Version
+{
+ /// Significant changes
+ int major;
+
+ /// Incremental changes
+ int minor;
+
+ /// Bug fixes
+ int revision;
+} b2Version;
+
+/// Get the current version of Box2D
+B2_API b2Version b2GetVersion( void );
+
+/**@}*/
+
+//! @cond
+
+/// Get the absolute number of system ticks. The value is platform specific.
+B2_API uint64_t b2GetTicks( void );
+
+/// Get the milliseconds passed from an initial tick value.
+B2_API float b2GetMilliseconds( uint64_t ticks );
+
+/// Get the milliseconds passed from an initial tick value.
+B2_API float b2GetMillisecondsAndReset( uint64_t* ticks );
+
+/// Yield to be used in a busy loop.
+B2_API void b2Yield( void );
+
+/// Simple djb2 hash function for determinism testing
+#define B2_HASH_INIT 5381
+B2_API uint32_t b2Hash( uint32_t hash, const uint8_t* data, int count );
+
+//! @endcond
diff --git a/odin-c-bindgen/examples/box2d/input/box2d.h b/odin-c-bindgen/examples/box2d/input/box2d.h
@@ -0,0 +1,1219 @@
+// SPDX-FileCopyrightText: 2023 Erin Catto
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include "base.h"
+#include "collision.h"
+#include "id.h"
+#include "types.h"
+
+#include <stdbool.h>
+
+/**
+ * @defgroup world World
+ * These functions allow you to create a simulation world.
+ *
+ * You can add rigid bodies and joint constraints to the world and run the simulation. You can get contact
+ * information to get contact points and normals as well as events. You can query to world, checking for overlaps and casting rays
+ * or shapes. There is also debugging information such as debug draw, timing information, and counters. You can find documentation
+ * here: https://box2d.org/
+ * @{
+ */
+
+/// Create a world for rigid body simulation. A world contains bodies, shapes, and constraints. You make create
+/// up to 128 worlds. Each world is completely independent and may be simulated in parallel.
+/// @return the world id.
+B2_API b2WorldId b2CreateWorld( const b2WorldDef* def );
+
+/// Destroy a world
+B2_API void b2DestroyWorld( b2WorldId worldId );
+
+/// World id validation. Provides validation for up to 64K allocations.
+B2_API bool b2World_IsValid( b2WorldId id );
+
+/// Simulate a world for one time step. This performs collision detection, integration, and constraint solution.
+/// @param worldId The world to simulate
+/// @param timeStep The amount of time to simulate, this should be a fixed number. Usually 1/60.
+/// @param subStepCount The number of sub-steps, increasing the sub-step count can increase accuracy. Usually 4.
+B2_API void b2World_Step( b2WorldId worldId, float timeStep, int subStepCount );
+
+/// Call this to draw shapes and other debug draw data
+B2_API void b2World_Draw( b2WorldId worldId, b2DebugDraw* draw );
+
+/// Get the body events for the current time step. The event data is transient. Do not store a reference to this data.
+B2_API b2BodyEvents b2World_GetBodyEvents( b2WorldId worldId );
+
+/// Get sensor events for the current time step. The event data is transient. Do not store a reference to this data.
+B2_API b2SensorEvents b2World_GetSensorEvents( b2WorldId worldId );
+
+/// Get contact events for this current time step. The event data is transient. Do not store a reference to this data.
+B2_API b2ContactEvents b2World_GetContactEvents( b2WorldId worldId );
+
+/// Overlap test for all shapes that *potentially* overlap the provided AABB
+B2_API b2TreeStats b2World_OverlapAABB( b2WorldId worldId, b2AABB aabb, b2QueryFilter filter, b2OverlapResultFcn* fcn,
+ void* context );
+
+/// Overlap test for for all shapes that overlap the provided point.
+B2_API b2TreeStats b2World_OverlapPoint( b2WorldId worldId, b2Vec2 point, b2Transform transform,
+ b2QueryFilter filter, b2OverlapResultFcn* fcn, void* context );
+
+/// Overlap test for for all shapes that overlap the provided circle. A zero radius may be used for a point query.
+B2_API b2TreeStats b2World_OverlapCircle( b2WorldId worldId, const b2Circle* circle, b2Transform transform,
+ b2QueryFilter filter, b2OverlapResultFcn* fcn, void* context );
+
+/// Overlap test for all shapes that overlap the provided capsule
+B2_API b2TreeStats b2World_OverlapCapsule( b2WorldId worldId, const b2Capsule* capsule, b2Transform transform,
+ b2QueryFilter filter, b2OverlapResultFcn* fcn, void* context );
+
+/// Overlap test for all shapes that overlap the provided polygon
+B2_API b2TreeStats b2World_OverlapPolygon( b2WorldId worldId, const b2Polygon* polygon, b2Transform transform,
+ b2QueryFilter filter, b2OverlapResultFcn* fcn, void* context );
+
+/// Cast a ray into the world to collect shapes in the path of the ray.
+/// Your callback function controls whether you get the closest point, any point, or n-points.
+/// The ray-cast ignores shapes that contain the starting point.
+/// @note The callback function may receive shapes in any order
+/// @param worldId The world to cast the ray against
+/// @param origin The start point of the ray
+/// @param translation The translation of the ray from the start point to the end point
+/// @param filter Contains bit flags to filter unwanted shapes from the results
+/// @param fcn A user implemented callback function
+/// @param context A user context that is passed along to the callback function
+/// @return traversal performance counters
+B2_API b2TreeStats b2World_CastRay( b2WorldId worldId, b2Vec2 origin, b2Vec2 translation, b2QueryFilter filter,
+ b2CastResultFcn* fcn, void* context );
+
+/// Cast a ray into the world to collect the closest hit. This is a convenience function.
+/// This is less general than b2World_CastRay() and does not allow for custom filtering.
+B2_API b2RayResult b2World_CastRayClosest( b2WorldId worldId, b2Vec2 origin, b2Vec2 translation, b2QueryFilter filter );
+
+/// Cast a circle through the world. Similar to a cast ray except that a circle is cast instead of a point.
+/// @see b2World_CastRay
+B2_API b2TreeStats b2World_CastCircle( b2WorldId worldId, const b2Circle* circle, b2Transform originTransform,
+ b2Vec2 translation, b2QueryFilter filter, b2CastResultFcn* fcn, void* context );
+
+/// Cast a capsule through the world. Similar to a cast ray except that a capsule is cast instead of a point.
+/// @see b2World_CastRay
+B2_API b2TreeStats b2World_CastCapsule( b2WorldId worldId, const b2Capsule* capsule, b2Transform originTransform,
+ b2Vec2 translation, b2QueryFilter filter, b2CastResultFcn* fcn, void* context );
+
+/// Cast a polygon through the world. Similar to a cast ray except that a polygon is cast instead of a point.
+/// @see b2World_CastRay
+B2_API b2TreeStats b2World_CastPolygon( b2WorldId worldId, const b2Polygon* polygon, b2Transform originTransform,
+ b2Vec2 translation, b2QueryFilter filter, b2CastResultFcn* fcn, void* context );
+
+/// Enable/disable sleep. If your application does not need sleeping, you can gain some performance
+/// by disabling sleep completely at the world level.
+/// @see b2WorldDef
+B2_API void b2World_EnableSleeping( b2WorldId worldId, bool flag );
+
+/// Is body sleeping enabled?
+B2_API bool b2World_IsSleepingEnabled( b2WorldId worldId );
+
+/// Enable/disable continuous collision between dynamic and static bodies. Generally you should keep continuous
+/// collision enabled to prevent fast moving objects from going through static objects. The performance gain from
+/// disabling continuous collision is minor.
+/// @see b2WorldDef
+B2_API void b2World_EnableContinuous( b2WorldId worldId, bool flag );
+
+/// Is continuous collision enabled?
+B2_API bool b2World_IsContinuousEnabled( b2WorldId worldId );
+
+/// Adjust the restitution threshold. It is recommended not to make this value very small
+/// because it will prevent bodies from sleeping. Usually in meters per second.
+/// @see b2WorldDef
+B2_API void b2World_SetRestitutionThreshold( b2WorldId worldId, float value );
+
+/// Get the the restitution speed threshold. Usually in meters per second.
+B2_API float b2World_GetRestitutionThreshold( b2WorldId worldId );
+
+/// Adjust the hit event threshold. This controls the collision speed needed to generate a b2ContactHitEvent.
+/// Usually in meters per second.
+/// @see b2WorldDef::hitEventThreshold
+B2_API void b2World_SetHitEventThreshold( b2WorldId worldId, float value );
+
+/// Get the the hit event speed threshold. Usually in meters per second.
+B2_API float b2World_GetHitEventThreshold( b2WorldId worldId );
+
+/// Register the custom filter callback. This is optional.
+B2_API void b2World_SetCustomFilterCallback( b2WorldId worldId, b2CustomFilterFcn* fcn, void* context );
+
+/// Register the pre-solve callback. This is optional.
+B2_API void b2World_SetPreSolveCallback( b2WorldId worldId, b2PreSolveFcn* fcn, void* context );
+
+/// Set the gravity vector for the entire world. Box2D has no concept of an up direction and this
+/// is left as a decision for the application. Usually in m/s^2.
+/// @see b2WorldDef
+B2_API void b2World_SetGravity( b2WorldId worldId, b2Vec2 gravity );
+
+/// Get the gravity vector
+B2_API b2Vec2 b2World_GetGravity( b2WorldId worldId );
+
+/// Apply a radial explosion
+/// @param worldId The world id
+/// @param explosionDef The explosion definition
+B2_API void b2World_Explode( b2WorldId worldId, const b2ExplosionDef* explosionDef );
+
+/// Adjust contact tuning parameters
+/// @param worldId The world id
+/// @param hertz The contact stiffness (cycles per second)
+/// @param dampingRatio The contact bounciness with 1 being critical damping (non-dimensional)
+/// @param pushSpeed The maximum contact constraint push out speed (meters per second)
+/// @note Advanced feature
+B2_API void b2World_SetContactTuning( b2WorldId worldId, float hertz, float dampingRatio, float pushSpeed );
+
+/// Adjust joint tuning parameters
+/// @param worldId The world id
+/// @param hertz The contact stiffness (cycles per second)
+/// @param dampingRatio The contact bounciness with 1 being critical damping (non-dimensional)
+/// @note Advanced feature
+B2_API void b2World_SetJointTuning( b2WorldId worldId, float hertz, float dampingRatio );
+
+/// Set the maximum linear speed. Usually in m/s.
+B2_API void b2World_SetMaximumLinearSpeed( b2WorldId worldId, float maximumLinearSpeed );
+
+/// Get the maximum linear speed. Usually in m/s.
+B2_API float b2World_GetMaximumLinearSpeed( b2WorldId worldId );
+
+/// Enable/disable constraint warm starting. Advanced feature for testing. Disabling
+/// sleeping greatly reduces stability and provides no performance gain.
+B2_API void b2World_EnableWarmStarting( b2WorldId worldId, bool flag );
+
+/// Is constraint warm starting enabled?
+B2_API bool b2World_IsWarmStartingEnabled( b2WorldId worldId );
+
+/// Get the number of awake bodies.
+B2_API int b2World_GetAwakeBodyCount( b2WorldId worldId );
+
+/// Get the current world performance profile
+B2_API b2Profile b2World_GetProfile( b2WorldId worldId );
+
+/// Get world counters and sizes
+B2_API b2Counters b2World_GetCounters( b2WorldId worldId );
+
+/// Set the user data pointer.
+B2_API void b2World_SetUserData( b2WorldId worldId, void* userData );
+
+/// Get the user data pointer.
+B2_API void* b2World_GetUserData( b2WorldId worldId );
+
+/// Set the friction callback. Passing NULL resets to default.
+B2_API void b2World_SetFrictionCallback( b2WorldId worldId, b2FrictionCallback* callback );
+
+/// Set the restitution callback. Passing NULL resets to default.
+B2_API void b2World_SetRestitutionCallback( b2WorldId worldId, b2RestitutionCallback* callback );
+
+/// Dump memory stats to box2d_memory.txt
+B2_API void b2World_DumpMemoryStats( b2WorldId worldId );
+
+/// This is for internal testing
+B2_API void b2World_RebuildStaticTree( b2WorldId worldId );
+
+/// This is for internal testing
+B2_API void b2World_EnableSpeculative( b2WorldId worldId, bool flag );
+
+/** @} */
+
+/**
+ * @defgroup body Body
+ * This is the body API.
+ * @{
+ */
+
+/// Create a rigid body given a definition. No reference to the definition is retained. So you can create the definition
+/// on the stack and pass it as a pointer.
+/// @code{.c}
+/// b2BodyDef bodyDef = b2DefaultBodyDef();
+/// b2BodyId myBodyId = b2CreateBody(myWorldId, &bodyDef);
+/// @endcode
+/// @warning This function is locked during callbacks.
+B2_API b2BodyId b2CreateBody( b2WorldId worldId, const b2BodyDef* def );
+
+/// Destroy a rigid body given an id. This destroys all shapes and joints attached to the body.
+/// Do not keep references to the associated shapes and joints.
+B2_API void b2DestroyBody( b2BodyId bodyId );
+
+/// Body identifier validation. Can be used to detect orphaned ids. Provides validation for up to 64K allocations.
+B2_API bool b2Body_IsValid( b2BodyId id );
+
+/// Get the body type: static, kinematic, or dynamic
+B2_API b2BodyType b2Body_GetType( b2BodyId bodyId );
+
+/// Change the body type. This is an expensive operation. This automatically updates the mass
+/// properties regardless of the automatic mass setting.
+B2_API void b2Body_SetType( b2BodyId bodyId, b2BodyType type );
+
+/// Set the body name. Up to 31 characters excluding 0 termination.
+B2_API void b2Body_SetName( b2BodyId bodyId, const char* name );
+
+/// Get the body name. May be null.
+B2_API const char* b2Body_GetName( b2BodyId bodyId );
+
+/// Set the user data for a body
+B2_API void b2Body_SetUserData( b2BodyId bodyId, void* userData );
+
+/// Get the user data stored in a body
+B2_API void* b2Body_GetUserData( b2BodyId bodyId );
+
+/// Get the world position of a body. This is the location of the body origin.
+B2_API b2Vec2 b2Body_GetPosition( b2BodyId bodyId );
+
+/// Get the world rotation of a body as a cosine/sine pair (complex number)
+B2_API b2Rot b2Body_GetRotation( b2BodyId bodyId );
+
+/// Get the world transform of a body.
+B2_API b2Transform b2Body_GetTransform( b2BodyId bodyId );
+
+/// Set the world transform of a body. This acts as a teleport and is fairly expensive.
+/// @note Generally you should create a body with then intended transform.
+/// @see b2BodyDef::position and b2BodyDef::angle
+B2_API void b2Body_SetTransform( b2BodyId bodyId, b2Vec2 position, b2Rot rotation );
+
+/// Get a local point on a body given a world point
+B2_API b2Vec2 b2Body_GetLocalPoint( b2BodyId bodyId, b2Vec2 worldPoint );
+
+/// Get a world point on a body given a local point
+B2_API b2Vec2 b2Body_GetWorldPoint( b2BodyId bodyId, b2Vec2 localPoint );
+
+/// Get a local vector on a body given a world vector
+B2_API b2Vec2 b2Body_GetLocalVector( b2BodyId bodyId, b2Vec2 worldVector );
+
+/// Get a world vector on a body given a local vector
+B2_API b2Vec2 b2Body_GetWorldVector( b2BodyId bodyId, b2Vec2 localVector );
+
+/// Get the linear velocity of a body's center of mass. Usually in meters per second.
+B2_API b2Vec2 b2Body_GetLinearVelocity( b2BodyId bodyId );
+
+/// Get the angular velocity of a body in radians per second
+B2_API float b2Body_GetAngularVelocity( b2BodyId bodyId );
+
+/// Set the linear velocity of a body. Usually in meters per second.
+B2_API void b2Body_SetLinearVelocity( b2BodyId bodyId, b2Vec2 linearVelocity );
+
+/// Set the angular velocity of a body in radians per second
+B2_API void b2Body_SetAngularVelocity( b2BodyId bodyId, float angularVelocity );
+
+/// Get the linear velocity of a local point attached to a body. Usually in meters per second.
+B2_API b2Vec2 b2Body_GetLocalPointVelocity( b2BodyId bodyId, b2Vec2 localPoint );
+
+/// Get the linear velocity of a world point attached to a body. Usually in meters per second.
+B2_API b2Vec2 b2Body_GetWorldPointVelocity( b2BodyId bodyId, b2Vec2 worldPoint );
+
+/// Apply a force at a world point. If the force is not applied at the center of mass,
+/// it will generate a torque and affect the angular velocity. This optionally wakes up the body.
+/// The force is ignored if the body is not awake.
+/// @param bodyId The body id
+/// @param force The world force vector, usually in newtons (N)
+/// @param point The world position of the point of application
+/// @param wake Option to wake up the body
+B2_API void b2Body_ApplyForce( b2BodyId bodyId, b2Vec2 force, b2Vec2 point, bool wake );
+
+/// Apply a force to the center of mass. This optionally wakes up the body.
+/// The force is ignored if the body is not awake.
+/// @param bodyId The body id
+/// @param force the world force vector, usually in newtons (N).
+/// @param wake also wake up the body
+B2_API void b2Body_ApplyForceToCenter( b2BodyId bodyId, b2Vec2 force, bool wake );
+
+/// Apply a torque. This affects the angular velocity without affecting the linear velocity.
+/// This optionally wakes the body. The torque is ignored if the body is not awake.
+/// @param bodyId The body id
+/// @param torque about the z-axis (out of the screen), usually in N*m.
+/// @param wake also wake up the body
+B2_API void b2Body_ApplyTorque( b2BodyId bodyId, float torque, bool wake );
+
+/// Apply an impulse at a point. This immediately modifies the velocity.
+/// It also modifies the angular velocity if the point of application
+/// is not at the center of mass. This optionally wakes the body.
+/// The impulse is ignored if the body is not awake.
+/// @param bodyId The body id
+/// @param impulse the world impulse vector, usually in N*s or kg*m/s.
+/// @param point the world position of the point of application.
+/// @param wake also wake up the body
+/// @warning This should be used for one-shot impulses. If you need a steady force,
+/// use a force instead, which will work better with the sub-stepping solver.
+B2_API void b2Body_ApplyLinearImpulse( b2BodyId bodyId, b2Vec2 impulse, b2Vec2 point, bool wake );
+
+/// Apply an impulse to the center of mass. This immediately modifies the velocity.
+/// The impulse is ignored if the body is not awake. This optionally wakes the body.
+/// @param bodyId The body id
+/// @param impulse the world impulse vector, usually in N*s or kg*m/s.
+/// @param wake also wake up the body
+/// @warning This should be used for one-shot impulses. If you need a steady force,
+/// use a force instead, which will work better with the sub-stepping solver.
+B2_API void b2Body_ApplyLinearImpulseToCenter( b2BodyId bodyId, b2Vec2 impulse, bool wake );
+
+/// Apply an angular impulse. The impulse is ignored if the body is not awake.
+/// This optionally wakes the body.
+/// @param bodyId The body id
+/// @param impulse the angular impulse, usually in units of kg*m*m/s
+/// @param wake also wake up the body
+/// @warning This should be used for one-shot impulses. If you need a steady force,
+/// use a force instead, which will work better with the sub-stepping solver.
+B2_API void b2Body_ApplyAngularImpulse( b2BodyId bodyId, float impulse, bool wake );
+
+/// Get the mass of the body, usually in kilograms
+B2_API float b2Body_GetMass( b2BodyId bodyId );
+
+/// Get the rotational inertia of the body, usually in kg*m^2
+B2_API float b2Body_GetRotationalInertia( b2BodyId bodyId );
+
+/// Get the center of mass position of the body in local space
+B2_API b2Vec2 b2Body_GetLocalCenterOfMass( b2BodyId bodyId );
+
+/// Get the center of mass position of the body in world space
+B2_API b2Vec2 b2Body_GetWorldCenterOfMass( b2BodyId bodyId );
+
+/// Override the body's mass properties. Normally this is computed automatically using the
+/// shape geometry and density. This information is lost if a shape is added or removed or if the
+/// body type changes.
+B2_API void b2Body_SetMassData( b2BodyId bodyId, b2MassData massData );
+
+/// Get the mass data for a body
+B2_API b2MassData b2Body_GetMassData( b2BodyId bodyId );
+
+/// This update the mass properties to the sum of the mass properties of the shapes.
+/// This normally does not need to be called unless you called SetMassData to override
+/// the mass and you later want to reset the mass.
+/// You may also use this when automatic mass computation has been disabled.
+/// You should call this regardless of body type.
+B2_API void b2Body_ApplyMassFromShapes( b2BodyId bodyId );
+
+/// Adjust the linear damping. Normally this is set in b2BodyDef before creation.
+B2_API void b2Body_SetLinearDamping( b2BodyId bodyId, float linearDamping );
+
+/// Get the current linear damping.
+B2_API float b2Body_GetLinearDamping( b2BodyId bodyId );
+
+/// Adjust the angular damping. Normally this is set in b2BodyDef before creation.
+B2_API void b2Body_SetAngularDamping( b2BodyId bodyId, float angularDamping );
+
+/// Get the current angular damping.
+B2_API float b2Body_GetAngularDamping( b2BodyId bodyId );
+
+/// Adjust the gravity scale. Normally this is set in b2BodyDef before creation.
+/// @see b2BodyDef::gravityScale
+B2_API void b2Body_SetGravityScale( b2BodyId bodyId, float gravityScale );
+
+/// Get the current gravity scale
+B2_API float b2Body_GetGravityScale( b2BodyId bodyId );
+
+/// @return true if this body is awake
+B2_API bool b2Body_IsAwake( b2BodyId bodyId );
+
+/// Wake a body from sleep. This wakes the entire island the body is touching.
+/// @warning Putting a body to sleep will put the entire island of bodies touching this body to sleep,
+/// which can be expensive and possibly unintuitive.
+B2_API void b2Body_SetAwake( b2BodyId bodyId, bool awake );
+
+/// Enable or disable sleeping for this body. If sleeping is disabled the body will wake.
+B2_API void b2Body_EnableSleep( b2BodyId bodyId, bool enableSleep );
+
+/// Returns true if sleeping is enabled for this body
+B2_API bool b2Body_IsSleepEnabled( b2BodyId bodyId );
+
+/// Set the sleep threshold, usually in meters per second
+B2_API void b2Body_SetSleepThreshold( b2BodyId bodyId, float sleepThreshold );
+
+/// Get the sleep threshold, usually in meters per second.
+B2_API float b2Body_GetSleepThreshold( b2BodyId bodyId );
+
+/// Returns true if this body is enabled
+B2_API bool b2Body_IsEnabled( b2BodyId bodyId );
+
+/// Disable a body by removing it completely from the simulation. This is expensive.
+B2_API void b2Body_Disable( b2BodyId bodyId );
+
+/// Enable a body by adding it to the simulation. This is expensive.
+B2_API void b2Body_Enable( b2BodyId bodyId );
+
+/// Set this body to have fixed rotation. This causes the mass to be reset in all cases.
+B2_API void b2Body_SetFixedRotation( b2BodyId bodyId, bool flag );
+
+/// Does this body have fixed rotation?
+B2_API bool b2Body_IsFixedRotation( b2BodyId bodyId );
+
+/// Set this body to be a bullet. A bullet does continuous collision detection
+/// against dynamic bodies (but not other bullets).
+B2_API void b2Body_SetBullet( b2BodyId bodyId, bool flag );
+
+/// Is this body a bullet?
+B2_API bool b2Body_IsBullet( b2BodyId bodyId );
+
+/// Enable/disable contact events on all shapes.
+/// @see b2ShapeDef::enableContactEvents
+/// @warning changing this at runtime may cause mismatched begin/end touch events
+B2_API void b2Body_EnableContactEvents( b2BodyId bodyId, bool flag );
+
+/// Enable/disable hit events on all shapes
+/// @see b2ShapeDef::enableHitEvents
+B2_API void b2Body_EnableHitEvents( b2BodyId bodyId, bool flag );
+
+/// Get the world that owns this body
+B2_API b2WorldId b2Body_GetWorld( b2BodyId bodyId );
+
+/// Get the number of shapes on this body
+B2_API int b2Body_GetShapeCount( b2BodyId bodyId );
+
+/// Get the shape ids for all shapes on this body, up to the provided capacity.
+/// @returns the number of shape ids stored in the user array
+B2_API int b2Body_GetShapes( b2BodyId bodyId, b2ShapeId* shapeArray, int capacity );
+
+/// Get the number of joints on this body
+B2_API int b2Body_GetJointCount( b2BodyId bodyId );
+
+/// Get the joint ids for all joints on this body, up to the provided capacity
+/// @returns the number of joint ids stored in the user array
+B2_API int b2Body_GetJoints( b2BodyId bodyId, b2JointId* jointArray, int capacity );
+
+/// Get the maximum capacity required for retrieving all the touching contacts on a body
+B2_API int b2Body_GetContactCapacity( b2BodyId bodyId );
+
+/// Get the touching contact data for a body.
+/// @note Box2D uses speculative collision so some contact points may be separated.
+/// @returns the number of elements filled in the provided array
+/// @warning do not ignore the return value, it specifies the valid number of elements
+B2_API int b2Body_GetContactData( b2BodyId bodyId, b2ContactData* contactData, int capacity );
+
+/// Get the current world AABB that contains all the attached shapes. Note that this may not encompass the body origin.
+/// If there are no shapes attached then the returned AABB is empty and centered on the body origin.
+B2_API b2AABB b2Body_ComputeAABB( b2BodyId bodyId );
+
+/** @} */
+
+/**
+ * @defgroup shape Shape
+ * Functions to create, destroy, and access.
+ * Shapes bind raw geometry to bodies and hold material properties including friction and restitution.
+ * @{
+ */
+
+/// Create a circle shape and attach it to a body. The shape definition and geometry are fully cloned.
+/// Contacts are not created until the next time step.
+/// @return the shape id for accessing the shape
+B2_API b2ShapeId b2CreateCircleShape( b2BodyId bodyId, const b2ShapeDef* def, const b2Circle* circle );
+
+/// Create a line segment shape and attach it to a body. The shape definition and geometry are fully cloned.
+/// Contacts are not created until the next time step.
+/// @return the shape id for accessing the shape
+B2_API b2ShapeId b2CreateSegmentShape( b2BodyId bodyId, const b2ShapeDef* def, const b2Segment* segment );
+
+/// Create a capsule shape and attach it to a body. The shape definition and geometry are fully cloned.
+/// Contacts are not created until the next time step.
+/// @return the shape id for accessing the shape
+B2_API b2ShapeId b2CreateCapsuleShape( b2BodyId bodyId, const b2ShapeDef* def, const b2Capsule* capsule );
+
+/// Create a polygon shape and attach it to a body. The shape definition and geometry are fully cloned.
+/// Contacts are not created until the next time step.
+/// @return the shape id for accessing the shape
+B2_API b2ShapeId b2CreatePolygonShape( b2BodyId bodyId, const b2ShapeDef* def, const b2Polygon* polygon );
+
+/// Destroy a shape. You may defer the body mass update which can improve performance if several shapes on a
+/// body are destroyed at once.
+/// @see b2Body_ApplyMassFromShapes
+B2_API void b2DestroyShape( b2ShapeId shapeId, bool updateBodyMass );
+
+/// Shape identifier validation. Provides validation for up to 64K allocations.
+B2_API bool b2Shape_IsValid( b2ShapeId id );
+
+/// Get the type of a shape
+B2_API b2ShapeType b2Shape_GetType( b2ShapeId shapeId );
+
+/// Get the id of the body that a shape is attached to
+B2_API b2BodyId b2Shape_GetBody( b2ShapeId shapeId );
+
+/// Get the world that owns this shape
+B2_API b2WorldId b2Shape_GetWorld( b2ShapeId shapeId );
+
+/// Returns true If the shape is a sensor
+B2_API bool b2Shape_IsSensor( b2ShapeId shapeId );
+
+/// Set the user data for a shape
+B2_API void b2Shape_SetUserData( b2ShapeId shapeId, void* userData );
+
+/// Get the user data for a shape. This is useful when you get a shape id
+/// from an event or query.
+B2_API void* b2Shape_GetUserData( b2ShapeId shapeId );
+
+/// Set the mass density of a shape, usually in kg/m^2.
+/// This will optionally update the mass properties on the parent body.
+/// @see b2ShapeDef::density, b2Body_ApplyMassFromShapes
+B2_API void b2Shape_SetDensity( b2ShapeId shapeId, float density, bool updateBodyMass );
+
+/// Get the density of a shape, usually in kg/m^2
+B2_API float b2Shape_GetDensity( b2ShapeId shapeId );
+
+/// Set the friction on a shape
+/// @see b2ShapeDef::friction
+B2_API void b2Shape_SetFriction( b2ShapeId shapeId, float friction );
+
+/// Get the friction of a shape
+B2_API float b2Shape_GetFriction( b2ShapeId shapeId );
+
+/// Set the shape restitution (bounciness)
+/// @see b2ShapeDef::restitution
+B2_API void b2Shape_SetRestitution( b2ShapeId shapeId, float restitution );
+
+/// Get the shape restitution
+B2_API float b2Shape_GetRestitution( b2ShapeId shapeId );
+
+/// Set the shape material identifier
+/// @see b2ShapeDef::material
+B2_API void b2Shape_SetMaterial( b2ShapeId shapeId, int material );
+
+/// Get the shape material identifier
+B2_API int b2Shape_GetMaterial( b2ShapeId shapeId );
+
+/// Get the shape filter
+B2_API b2Filter b2Shape_GetFilter( b2ShapeId shapeId );
+
+/// Set the current filter. This is almost as expensive as recreating the shape. This may cause
+/// contacts to be immediately destroyed. However contacts are not created until the next world step.
+/// Sensor overlap state is also not updated until the next world step.
+/// @see b2ShapeDef::filter
+B2_API void b2Shape_SetFilter( b2ShapeId shapeId, b2Filter filter );
+
+/// Enable contact events for this shape. Only applies to kinematic and dynamic bodies. Ignored for sensors.
+/// @see b2ShapeDef::enableContactEvents
+/// @warning changing this at run-time may lead to lost begin/end events
+B2_API void b2Shape_EnableContactEvents( b2ShapeId shapeId, bool flag );
+
+/// Returns true if contact events are enabled
+B2_API bool b2Shape_AreContactEventsEnabled( b2ShapeId shapeId );
+
+/// Enable pre-solve contact events for this shape. Only applies to dynamic bodies. These are expensive
+/// and must be carefully handled due to multithreading. Ignored for sensors.
+/// @see b2PreSolveFcn
+B2_API void b2Shape_EnablePreSolveEvents( b2ShapeId shapeId, bool flag );
+
+/// Returns true if pre-solve events are enabled
+B2_API bool b2Shape_ArePreSolveEventsEnabled( b2ShapeId shapeId );
+
+/// Enable contact hit events for this shape. Ignored for sensors.
+/// @see b2WorldDef.hitEventThreshold
+B2_API void b2Shape_EnableHitEvents( b2ShapeId shapeId, bool flag );
+
+/// Returns true if hit events are enabled
+B2_API bool b2Shape_AreHitEventsEnabled( b2ShapeId shapeId );
+
+/// Test a point for overlap with a shape
+B2_API bool b2Shape_TestPoint( b2ShapeId shapeId, b2Vec2 point );
+
+/// Ray cast a shape directly
+B2_API b2CastOutput b2Shape_RayCast( b2ShapeId shapeId, const b2RayCastInput* input );
+
+/// Get a copy of the shape's circle. Asserts the type is correct.
+B2_API b2Circle b2Shape_GetCircle( b2ShapeId shapeId );
+
+/// Get a copy of the shape's line segment. Asserts the type is correct.
+B2_API b2Segment b2Shape_GetSegment( b2ShapeId shapeId );
+
+/// Get a copy of the shape's chain segment. These come from chain shapes.
+/// Asserts the type is correct.
+B2_API b2ChainSegment b2Shape_GetChainSegment( b2ShapeId shapeId );
+
+/// Get a copy of the shape's capsule. Asserts the type is correct.
+B2_API b2Capsule b2Shape_GetCapsule( b2ShapeId shapeId );
+
+/// Get a copy of the shape's convex polygon. Asserts the type is correct.
+B2_API b2Polygon b2Shape_GetPolygon( b2ShapeId shapeId );
+
+/// Allows you to change a shape to be a circle or update the current circle.
+/// This does not modify the mass properties.
+/// @see b2Body_ApplyMassFromShapes
+B2_API void b2Shape_SetCircle( b2ShapeId shapeId, const b2Circle* circle );
+
+/// Allows you to change a shape to be a capsule or update the current capsule.
+/// This does not modify the mass properties.
+/// @see b2Body_ApplyMassFromShapes
+B2_API void b2Shape_SetCapsule( b2ShapeId shapeId, const b2Capsule* capsule );
+
+/// Allows you to change a shape to be a segment or update the current segment.
+B2_API void b2Shape_SetSegment( b2ShapeId shapeId, const b2Segment* segment );
+
+/// Allows you to change a shape to be a polygon or update the current polygon.
+/// This does not modify the mass properties.
+/// @see b2Body_ApplyMassFromShapes
+B2_API void b2Shape_SetPolygon( b2ShapeId shapeId, const b2Polygon* polygon );
+
+/// Get the parent chain id if the shape type is a chain segment, otherwise
+/// returns b2_nullChainId.
+B2_API b2ChainId b2Shape_GetParentChain( b2ShapeId shapeId );
+
+/// Get the maximum capacity required for retrieving all the touching contacts on a shape
+B2_API int b2Shape_GetContactCapacity( b2ShapeId shapeId );
+
+/// Get the touching contact data for a shape. The provided shapeId will be either shapeIdA or shapeIdB on the contact data.
+/// @note Box2D uses speculative collision so some contact points may be separated.
+/// @returns the number of elements filled in the provided array
+/// @warning do not ignore the return value, it specifies the valid number of elements
+B2_API int b2Shape_GetContactData( b2ShapeId shapeId, b2ContactData* contactData, int capacity );
+
+/// Get the maximum capacity required for retrieving all the overlapped shapes on a sensor shape.
+/// This returns 0 if the provided shape is not a sensor.
+/// @param shapeId the id of a sensor shape
+/// @returns the required capacity to get all the overlaps in b2Shape_GetSensorOverlaps
+B2_API int b2Shape_GetSensorCapacity( b2ShapeId shapeId );
+
+/// Get the overlapped shapes for a sensor shape.
+/// @param shapeId the id of a sensor shape
+/// @param overlaps a user allocated array that is filled with the overlapping shapes
+/// @param capacity the capacity of overlappedShapes
+/// @returns the number of elements filled in the provided array
+/// @warning do not ignore the return value, it specifies the valid number of elements
+/// @warning overlaps may contain destroyed shapes so use b2Shape_IsValid to confirm each overlap
+B2_API int b2Shape_GetSensorOverlaps( b2ShapeId shapeId, b2ShapeId* overlaps, int capacity );
+
+/// Get the current world AABB
+B2_API b2AABB b2Shape_GetAABB( b2ShapeId shapeId );
+
+/// Get the mass data for a shape
+B2_API b2MassData b2Shape_GetMassData( b2ShapeId shapeId );
+
+/// Get the closest point on a shape to a target point. Target and result are in world space.
+/// todo need sample
+B2_API b2Vec2 b2Shape_GetClosestPoint( b2ShapeId shapeId, b2Vec2 target );
+
+/// Chain Shape
+
+/// Create a chain shape
+/// @see b2ChainDef for details
+B2_API b2ChainId b2CreateChain( b2BodyId bodyId, const b2ChainDef* def );
+
+/// Destroy a chain shape
+B2_API void b2DestroyChain( b2ChainId chainId );
+
+/// Get the world that owns this chain shape
+B2_API b2WorldId b2Chain_GetWorld( b2ChainId chainId );
+
+/// Get the number of segments on this chain
+B2_API int b2Chain_GetSegmentCount( b2ChainId chainId );
+
+/// Fill a user array with chain segment shape ids up to the specified capacity. Returns
+/// the actual number of segments returned.
+B2_API int b2Chain_GetSegments( b2ChainId chainId, b2ShapeId* segmentArray, int capacity );
+
+/// Set the chain friction
+/// @see b2ChainDef::friction
+B2_API void b2Chain_SetFriction( b2ChainId chainId, float friction );
+
+/// Get the chain friction
+B2_API float b2Chain_GetFriction( b2ChainId chainId );
+
+/// Set the chain restitution (bounciness)
+/// @see b2ChainDef::restitution
+B2_API void b2Chain_SetRestitution( b2ChainId chainId, float restitution );
+
+/// Get the chain restitution
+B2_API float b2Chain_GetRestitution( b2ChainId chainId );
+
+/// Set the chain material
+/// @see b2ChainDef::material
+B2_API void b2Chain_SetMaterial( b2ChainId chainId, int material );
+
+/// Get the chain material
+B2_API int b2Chain_GetMaterial( b2ChainId chainId );
+
+/// Chain identifier validation. Provides validation for up to 64K allocations.
+B2_API bool b2Chain_IsValid( b2ChainId id );
+
+/** @} */
+
+/**
+ * @defgroup joint Joint
+ * @brief Joints allow you to connect rigid bodies together while allowing various forms of relative motions.
+ * @{
+ */
+
+/// Destroy a joint
+B2_API void b2DestroyJoint( b2JointId jointId );
+
+/// Joint identifier validation. Provides validation for up to 64K allocations.
+B2_API bool b2Joint_IsValid( b2JointId id );
+
+/// Get the joint type
+B2_API b2JointType b2Joint_GetType( b2JointId jointId );
+
+/// Get body A id on a joint
+B2_API b2BodyId b2Joint_GetBodyA( b2JointId jointId );
+
+/// Get body B id on a joint
+B2_API b2BodyId b2Joint_GetBodyB( b2JointId jointId );
+
+/// Get the world that owns this joint
+B2_API b2WorldId b2Joint_GetWorld( b2JointId jointId );
+
+/// Get the local anchor on bodyA
+B2_API b2Vec2 b2Joint_GetLocalAnchorA( b2JointId jointId );
+
+/// Get the local anchor on bodyB
+B2_API b2Vec2 b2Joint_GetLocalAnchorB( b2JointId jointId );
+
+/// Toggle collision between connected bodies
+B2_API void b2Joint_SetCollideConnected( b2JointId jointId, bool shouldCollide );
+
+/// Is collision allowed between connected bodies?
+B2_API bool b2Joint_GetCollideConnected( b2JointId jointId );
+
+/// Set the user data on a joint
+B2_API void b2Joint_SetUserData( b2JointId jointId, void* userData );
+
+/// Get the user data on a joint
+B2_API void* b2Joint_GetUserData( b2JointId jointId );
+
+/// Wake the bodies connect to this joint
+B2_API void b2Joint_WakeBodies( b2JointId jointId );
+
+/// Get the current constraint force for this joint. Usually in Newtons.
+B2_API b2Vec2 b2Joint_GetConstraintForce( b2JointId jointId );
+
+/// Get the current constraint torque for this joint. Usually in Newton * meters.
+B2_API float b2Joint_GetConstraintTorque( b2JointId jointId );
+
+/**
+ * @defgroup distance_joint Distance Joint
+ * @brief Functions for the distance joint.
+ * @{
+ */
+
+/// Create a distance joint
+/// @see b2DistanceJointDef for details
+B2_API b2JointId b2CreateDistanceJoint( b2WorldId worldId, const b2DistanceJointDef* def );
+
+/// Set the rest length of a distance joint
+/// @param jointId The id for a distance joint
+/// @param length The new distance joint length
+B2_API void b2DistanceJoint_SetLength( b2JointId jointId, float length );
+
+/// Get the rest length of a distance joint
+B2_API float b2DistanceJoint_GetLength( b2JointId jointId );
+
+/// Enable/disable the distance joint spring. When disabled the distance joint is rigid.
+B2_API void b2DistanceJoint_EnableSpring( b2JointId jointId, bool enableSpring );
+
+/// Is the distance joint spring enabled?
+B2_API bool b2DistanceJoint_IsSpringEnabled( b2JointId jointId );
+
+/// Set the spring stiffness in Hertz
+B2_API void b2DistanceJoint_SetSpringHertz( b2JointId jointId, float hertz );
+
+/// Set the spring damping ratio, non-dimensional
+B2_API void b2DistanceJoint_SetSpringDampingRatio( b2JointId jointId, float dampingRatio );
+
+/// Get the spring Hertz
+B2_API float b2DistanceJoint_GetSpringHertz( b2JointId jointId );
+
+/// Get the spring damping ratio
+B2_API float b2DistanceJoint_GetSpringDampingRatio( b2JointId jointId );
+
+/// Enable joint limit. The limit only works if the joint spring is enabled. Otherwise the joint is rigid
+/// and the limit has no effect.
+B2_API void b2DistanceJoint_EnableLimit( b2JointId jointId, bool enableLimit );
+
+/// Is the distance joint limit enabled?
+B2_API bool b2DistanceJoint_IsLimitEnabled( b2JointId jointId );
+
+/// Set the minimum and maximum length parameters of a distance joint
+B2_API void b2DistanceJoint_SetLengthRange( b2JointId jointId, float minLength, float maxLength );
+
+/// Get the distance joint minimum length
+B2_API float b2DistanceJoint_GetMinLength( b2JointId jointId );
+
+/// Get the distance joint maximum length
+B2_API float b2DistanceJoint_GetMaxLength( b2JointId jointId );
+
+/// Get the current length of a distance joint
+B2_API float b2DistanceJoint_GetCurrentLength( b2JointId jointId );
+
+/// Enable/disable the distance joint motor
+B2_API void b2DistanceJoint_EnableMotor( b2JointId jointId, bool enableMotor );
+
+/// Is the distance joint motor enabled?
+B2_API bool b2DistanceJoint_IsMotorEnabled( b2JointId jointId );
+
+/// Set the distance joint motor speed, usually in meters per second
+B2_API void b2DistanceJoint_SetMotorSpeed( b2JointId jointId, float motorSpeed );
+
+/// Get the distance joint motor speed, usually in meters per second
+B2_API float b2DistanceJoint_GetMotorSpeed( b2JointId jointId );
+
+/// Set the distance joint maximum motor force, usually in newtons
+B2_API void b2DistanceJoint_SetMaxMotorForce( b2JointId jointId, float force );
+
+/// Get the distance joint maximum motor force, usually in newtons
+B2_API float b2DistanceJoint_GetMaxMotorForce( b2JointId jointId );
+
+/// Get the distance joint current motor force, usually in newtons
+B2_API float b2DistanceJoint_GetMotorForce( b2JointId jointId );
+
+/** @} */
+
+/**
+ * @defgroup motor_joint Motor Joint
+ * @brief Functions for the motor joint.
+ *
+ * The motor joint is used to drive the relative transform between two bodies. It takes
+ * a relative position and rotation and applies the forces and torques needed to achieve
+ * that relative transform over time.
+ * @{
+ */
+
+/// Create a motor joint
+/// @see b2MotorJointDef for details
+B2_API b2JointId b2CreateMotorJoint( b2WorldId worldId, const b2MotorJointDef* def );
+
+/// Set the motor joint linear offset target
+B2_API void b2MotorJoint_SetLinearOffset( b2JointId jointId, b2Vec2 linearOffset );
+
+/// Get the motor joint linear offset target
+B2_API b2Vec2 b2MotorJoint_GetLinearOffset( b2JointId jointId );
+
+/// Set the motor joint angular offset target in radians
+B2_API void b2MotorJoint_SetAngularOffset( b2JointId jointId, float angularOffset );
+
+/// Get the motor joint angular offset target in radians
+B2_API float b2MotorJoint_GetAngularOffset( b2JointId jointId );
+
+/// Set the motor joint maximum force, usually in newtons
+B2_API void b2MotorJoint_SetMaxForce( b2JointId jointId, float maxForce );
+
+/// Get the motor joint maximum force, usually in newtons
+B2_API float b2MotorJoint_GetMaxForce( b2JointId jointId );
+
+/// Set the motor joint maximum torque, usually in newton-meters
+B2_API void b2MotorJoint_SetMaxTorque( b2JointId jointId, float maxTorque );
+
+/// Get the motor joint maximum torque, usually in newton-meters
+B2_API float b2MotorJoint_GetMaxTorque( b2JointId jointId );
+
+/// Set the motor joint correction factor, usually in [0, 1]
+B2_API void b2MotorJoint_SetCorrectionFactor( b2JointId jointId, float correctionFactor );
+
+/// Get the motor joint correction factor, usually in [0, 1]
+B2_API float b2MotorJoint_GetCorrectionFactor( b2JointId jointId );
+
+/**@}*/
+
+/**
+ * @defgroup mouse_joint Mouse Joint
+ * @brief Functions for the mouse joint.
+ *
+ * The mouse joint is designed for use in the samples application, but you may find it useful in applications where
+ * the user moves a rigid body with a cursor.
+ * @{
+ */
+
+/// Create a mouse joint
+/// @see b2MouseJointDef for details
+B2_API b2JointId b2CreateMouseJoint( b2WorldId worldId, const b2MouseJointDef* def );
+
+/// Set the mouse joint target
+B2_API void b2MouseJoint_SetTarget( b2JointId jointId, b2Vec2 target );
+
+/// Get the mouse joint target
+B2_API b2Vec2 b2MouseJoint_GetTarget( b2JointId jointId );
+
+/// Set the mouse joint spring stiffness in Hertz
+B2_API void b2MouseJoint_SetSpringHertz( b2JointId jointId, float hertz );
+
+/// Get the mouse joint spring stiffness in Hertz
+B2_API float b2MouseJoint_GetSpringHertz( b2JointId jointId );
+
+/// Set the mouse joint spring damping ratio, non-dimensional
+B2_API void b2MouseJoint_SetSpringDampingRatio( b2JointId jointId, float dampingRatio );
+
+/// Get the mouse joint damping ratio, non-dimensional
+B2_API float b2MouseJoint_GetSpringDampingRatio( b2JointId jointId );
+
+/// Set the mouse joint maximum force, usually in newtons
+B2_API void b2MouseJoint_SetMaxForce( b2JointId jointId, float maxForce );
+
+/// Get the mouse joint maximum force, usually in newtons
+B2_API float b2MouseJoint_GetMaxForce( b2JointId jointId );
+
+/**@}*/
+
+/**
+ * @defgroup null_joint Null Joint
+ * @brief Functions for the null joint.
+ *
+ * The null joint is used to disable collision between two bodies. As a side effect of being a joint, it also
+ * keeps the two bodies in the same simulation island.
+ * @{
+ */
+
+/// Create a null joint.
+/// @see b2NullJointDef for details
+B2_API b2JointId b2CreateNullJoint( b2WorldId worldId, const b2NullJointDef* def );
+
+/**@}*/
+
+/**
+ * @defgroup prismatic_joint Prismatic Joint
+ * @brief A prismatic joint allows for translation along a single axis with no rotation.
+ *
+ * The prismatic joint is useful for things like pistons and moving platforms, where you want a body to translate
+ * along an axis and have no rotation. Also called a *slider* joint.
+ * @{
+ */
+
+/// Create a prismatic (slider) joint.
+/// @see b2PrismaticJointDef for details
+B2_API b2JointId b2CreatePrismaticJoint( b2WorldId worldId, const b2PrismaticJointDef* def );
+
+/// Enable/disable the joint spring.
+B2_API void b2PrismaticJoint_EnableSpring( b2JointId jointId, bool enableSpring );
+
+/// Is the prismatic joint spring enabled or not?
+B2_API bool b2PrismaticJoint_IsSpringEnabled( b2JointId jointId );
+
+/// Set the prismatic joint stiffness in Hertz.
+/// This should usually be less than a quarter of the simulation rate. For example, if the simulation
+/// runs at 60Hz then the joint stiffness should be 15Hz or less.
+B2_API void b2PrismaticJoint_SetSpringHertz( b2JointId jointId, float hertz );
+
+/// Get the prismatic joint stiffness in Hertz
+B2_API float b2PrismaticJoint_GetSpringHertz( b2JointId jointId );
+
+/// Set the prismatic joint damping ratio (non-dimensional)
+B2_API void b2PrismaticJoint_SetSpringDampingRatio( b2JointId jointId, float dampingRatio );
+
+/// Get the prismatic spring damping ratio (non-dimensional)
+B2_API float b2PrismaticJoint_GetSpringDampingRatio( b2JointId jointId );
+
+/// Enable/disable a prismatic joint limit
+B2_API void b2PrismaticJoint_EnableLimit( b2JointId jointId, bool enableLimit );
+
+/// Is the prismatic joint limit enabled?
+B2_API bool b2PrismaticJoint_IsLimitEnabled( b2JointId jointId );
+
+/// Get the prismatic joint lower limit
+B2_API float b2PrismaticJoint_GetLowerLimit( b2JointId jointId );
+
+/// Get the prismatic joint upper limit
+B2_API float b2PrismaticJoint_GetUpperLimit( b2JointId jointId );
+
+/// Set the prismatic joint limits
+B2_API void b2PrismaticJoint_SetLimits( b2JointId jointId, float lower, float upper );
+
+/// Enable/disable a prismatic joint motor
+B2_API void b2PrismaticJoint_EnableMotor( b2JointId jointId, bool enableMotor );
+
+/// Is the prismatic joint motor enabled?
+B2_API bool b2PrismaticJoint_IsMotorEnabled( b2JointId jointId );
+
+/// Set the prismatic joint motor speed, usually in meters per second
+B2_API void b2PrismaticJoint_SetMotorSpeed( b2JointId jointId, float motorSpeed );
+
+/// Get the prismatic joint motor speed, usually in meters per second
+B2_API float b2PrismaticJoint_GetMotorSpeed( b2JointId jointId );
+
+/// Set the prismatic joint maximum motor force, usually in newtons
+B2_API void b2PrismaticJoint_SetMaxMotorForce( b2JointId jointId, float force );
+
+/// Get the prismatic joint maximum motor force, usually in newtons
+B2_API float b2PrismaticJoint_GetMaxMotorForce( b2JointId jointId );
+
+/// Get the prismatic joint current motor force, usually in newtons
+B2_API float b2PrismaticJoint_GetMotorForce( b2JointId jointId );
+
+/// Get the current joint translation, usually in meters.
+B2_API float b2PrismaticJoint_GetTranslation( b2JointId jointId );
+
+/// Get the current joint translation speed, usually in meters per second.
+B2_API float b2PrismaticJoint_GetSpeed( b2JointId jointId );
+
+/** @} */
+
+/**
+ * @defgroup revolute_joint Revolute Joint
+ * @brief A revolute joint allows for relative rotation in the 2D plane with no relative translation.
+ *
+ * The revolute joint is probably the most common joint. It can be used for ragdolls and chains.
+ * Also called a *hinge* or *pin* joint.
+ * @{
+ */
+
+/// Create a revolute joint
+/// @see b2RevoluteJointDef for details
+B2_API b2JointId b2CreateRevoluteJoint( b2WorldId worldId, const b2RevoluteJointDef* def );
+
+/// Enable/disable the revolute joint spring
+B2_API void b2RevoluteJoint_EnableSpring( b2JointId jointId, bool enableSpring );
+
+/// It the revolute angular spring enabled?
+B2_API bool b2RevoluteJoint_IsSpringEnabled( b2JointId jointId );
+
+/// Set the revolute joint spring stiffness in Hertz
+B2_API void b2RevoluteJoint_SetSpringHertz( b2JointId jointId, float hertz );
+
+/// Get the revolute joint spring stiffness in Hertz
+B2_API float b2RevoluteJoint_GetSpringHertz( b2JointId jointId );
+
+/// Set the revolute joint spring damping ratio, non-dimensional
+B2_API void b2RevoluteJoint_SetSpringDampingRatio( b2JointId jointId, float dampingRatio );
+
+/// Get the revolute joint spring damping ratio, non-dimensional
+B2_API float b2RevoluteJoint_GetSpringDampingRatio( b2JointId jointId );
+
+/// Get the revolute joint current angle in radians relative to the reference angle
+/// @see b2RevoluteJointDef::referenceAngle
+B2_API float b2RevoluteJoint_GetAngle( b2JointId jointId );
+
+/// Enable/disable the revolute joint limit
+B2_API void b2RevoluteJoint_EnableLimit( b2JointId jointId, bool enableLimit );
+
+/// Is the revolute joint limit enabled?
+B2_API bool b2RevoluteJoint_IsLimitEnabled( b2JointId jointId );
+
+/// Get the revolute joint lower limit in radians
+B2_API float b2RevoluteJoint_GetLowerLimit( b2JointId jointId );
+
+/// Get the revolute joint upper limit in radians
+B2_API float b2RevoluteJoint_GetUpperLimit( b2JointId jointId );
+
+/// Set the revolute joint limits in radians
+B2_API void b2RevoluteJoint_SetLimits( b2JointId jointId, float lower, float upper );
+
+/// Enable/disable a revolute joint motor
+B2_API void b2RevoluteJoint_EnableMotor( b2JointId jointId, bool enableMotor );
+
+/// Is the revolute joint motor enabled?
+B2_API bool b2RevoluteJoint_IsMotorEnabled( b2JointId jointId );
+
+/// Set the revolute joint motor speed in radians per second
+B2_API void b2RevoluteJoint_SetMotorSpeed( b2JointId jointId, float motorSpeed );
+
+/// Get the revolute joint motor speed in radians per second
+B2_API float b2RevoluteJoint_GetMotorSpeed( b2JointId jointId );
+
+/// Get the revolute joint current motor torque, usually in newton-meters
+B2_API float b2RevoluteJoint_GetMotorTorque( b2JointId jointId );
+
+/// Set the revolute joint maximum motor torque, usually in newton-meters
+B2_API void b2RevoluteJoint_SetMaxMotorTorque( b2JointId jointId, float torque );
+
+/// Get the revolute joint maximum motor torque, usually in newton-meters
+B2_API float b2RevoluteJoint_GetMaxMotorTorque( b2JointId jointId );
+
+/**@}*/
+
+/**
+ * @defgroup weld_joint Weld Joint
+ * @brief A weld joint fully constrains the relative transform between two bodies while allowing for springiness
+ *
+ * A weld joint constrains the relative rotation and translation between two bodies. Both rotation and translation
+ * can have damped springs.
+ *
+ * @note The accuracy of weld joint is limited by the accuracy of the solver. Long chains of weld joints may flex.
+ * @{
+ */
+
+/// Create a weld joint
+/// @see b2WeldJointDef for details
+B2_API b2JointId b2CreateWeldJoint( b2WorldId worldId, const b2WeldJointDef* def );
+
+/// Get the weld joint reference angle in radians
+B2_API float b2WeldJoint_GetReferenceAngle( b2JointId jointId );
+
+/// Set the weld joint reference angle in radians, must be in [-pi,pi].
+B2_API void b2WeldJoint_SetReferenceAngle( b2JointId jointId, float angleInRadians );
+
+/// Set the weld joint linear stiffness in Hertz. 0 is rigid.
+B2_API void b2WeldJoint_SetLinearHertz( b2JointId jointId, float hertz );
+
+/// Get the weld joint linear stiffness in Hertz
+B2_API float b2WeldJoint_GetLinearHertz( b2JointId jointId );
+
+/// Set the weld joint linear damping ratio (non-dimensional)
+B2_API void b2WeldJoint_SetLinearDampingRatio( b2JointId jointId, float dampingRatio );
+
+/// Get the weld joint linear damping ratio (non-dimensional)
+B2_API float b2WeldJoint_GetLinearDampingRatio( b2JointId jointId );
+
+/// Set the weld joint angular stiffness in Hertz. 0 is rigid.
+B2_API void b2WeldJoint_SetAngularHertz( b2JointId jointId, float hertz );
+
+/// Get the weld joint angular stiffness in Hertz
+B2_API float b2WeldJoint_GetAngularHertz( b2JointId jointId );
+
+/// Set weld joint angular damping ratio, non-dimensional
+B2_API void b2WeldJoint_SetAngularDampingRatio( b2JointId jointId, float dampingRatio );
+
+/// Get the weld joint angular damping ratio, non-dimensional
+B2_API float b2WeldJoint_GetAngularDampingRatio( b2JointId jointId );
+
+/** @} */
+
+/**
+ * @defgroup wheel_joint Wheel Joint
+ * The wheel joint can be used to simulate wheels on vehicles.
+ *
+ * The wheel joint restricts body B to move along a local axis in body A. Body B is free to
+ * rotate. Supports a linear spring, linear limits, and a rotational motor.
+ *
+ * @{
+ */
+
+/// Create a wheel joint
+/// @see b2WheelJointDef for details
+B2_API b2JointId b2CreateWheelJoint( b2WorldId worldId, const b2WheelJointDef* def );
+
+/// Enable/disable the wheel joint spring
+B2_API void b2WheelJoint_EnableSpring( b2JointId jointId, bool enableSpring );
+
+/// Is the wheel joint spring enabled?
+B2_API bool b2WheelJoint_IsSpringEnabled( b2JointId jointId );
+
+/// Set the wheel joint stiffness in Hertz
+B2_API void b2WheelJoint_SetSpringHertz( b2JointId jointId, float hertz );
+
+/// Get the wheel joint stiffness in Hertz
+B2_API float b2WheelJoint_GetSpringHertz( b2JointId jointId );
+
+/// Set the wheel joint damping ratio, non-dimensional
+B2_API void b2WheelJoint_SetSpringDampingRatio( b2JointId jointId, float dampingRatio );
+
+/// Get the wheel joint damping ratio, non-dimensional
+B2_API float b2WheelJoint_GetSpringDampingRatio( b2JointId jointId );
+
+/// Enable/disable the wheel joint limit
+B2_API void b2WheelJoint_EnableLimit( b2JointId jointId, bool enableLimit );
+
+/// Is the wheel joint limit enabled?
+B2_API bool b2WheelJoint_IsLimitEnabled( b2JointId jointId );
+
+/// Get the wheel joint lower limit
+B2_API float b2WheelJoint_GetLowerLimit( b2JointId jointId );
+
+/// Get the wheel joint upper limit
+B2_API float b2WheelJoint_GetUpperLimit( b2JointId jointId );
+
+/// Set the wheel joint limits
+B2_API void b2WheelJoint_SetLimits( b2JointId jointId, float lower, float upper );
+
+/// Enable/disable the wheel joint motor
+B2_API void b2WheelJoint_EnableMotor( b2JointId jointId, bool enableMotor );
+
+/// Is the wheel joint motor enabled?
+B2_API bool b2WheelJoint_IsMotorEnabled( b2JointId jointId );
+
+/// Set the wheel joint motor speed in radians per second
+B2_API void b2WheelJoint_SetMotorSpeed( b2JointId jointId, float motorSpeed );
+
+/// Get the wheel joint motor speed in radians per second
+B2_API float b2WheelJoint_GetMotorSpeed( b2JointId jointId );
+
+/// Set the wheel joint maximum motor torque, usually in newton-meters
+B2_API void b2WheelJoint_SetMaxMotorTorque( b2JointId jointId, float torque );
+
+/// Get the wheel joint maximum motor torque, usually in newton-meters
+B2_API float b2WheelJoint_GetMaxMotorTorque( b2JointId jointId );
+
+/// Get the wheel joint current motor torque, usually in newton-meters
+B2_API float b2WheelJoint_GetMotorTorque( b2JointId jointId );
+
+/**@}*/
+
+/**@}*/
diff --git a/odin-c-bindgen/examples/box2d/input/box2d.lib b/odin-c-bindgen/examples/box2d/input/box2d.lib
Binary files differ.
diff --git a/odin-c-bindgen/examples/box2d/input/collision.h b/odin-c-bindgen/examples/box2d/input/collision.h
@@ -0,0 +1,765 @@
+// SPDX-FileCopyrightText: 2023 Erin Catto
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include "base.h"
+#include "math_functions.h"
+
+#include <stdbool.h>
+
+typedef struct b2SimplexCache b2SimplexCache;
+typedef struct b2Hull b2Hull;
+
+/**
+ * @defgroup geometry Geometry
+ * @brief Geometry types and algorithms
+ *
+ * Definitions of circles, capsules, segments, and polygons. Various algorithms to compute hulls, mass properties, and so on.
+ * @{
+ */
+
+/// The maximum number of vertices on a convex polygon. Changing this affects performance even if you
+/// don't use more vertices.
+#define B2_MAX_POLYGON_VERTICES 8
+
+/// Low level ray cast input data
+typedef struct b2RayCastInput
+{
+ /// Start point of the ray cast
+ b2Vec2 origin;
+
+ /// Translation of the ray cast
+ b2Vec2 translation;
+
+ /// The maximum fraction of the translation to consider, typically 1
+ float maxFraction;
+} b2RayCastInput;
+
+/// Low level shape cast input in generic form. This allows casting an arbitrary point
+/// cloud wrap with a radius. For example, a circle is a single point with a non-zero radius.
+/// A capsule is two points with a non-zero radius. A box is four points with a zero radius.
+typedef struct b2ShapeCastInput
+{
+ /// A point cloud to cast
+ b2Vec2 points[B2_MAX_POLYGON_VERTICES];
+
+ /// The number of points
+ int count;
+
+ /// The radius around the point cloud
+ float radius;
+
+ /// The translation of the shape cast
+ b2Vec2 translation;
+
+ /// The maximum fraction of the translation to consider, typically 1
+ float maxFraction;
+} b2ShapeCastInput;
+
+/// Low level ray cast or shape-cast output data
+typedef struct b2CastOutput
+{
+ /// The surface normal at the hit point
+ b2Vec2 normal;
+
+ /// The surface hit point
+ b2Vec2 point;
+
+ /// The fraction of the input translation at collision
+ float fraction;
+
+ /// The number of iterations used
+ int iterations;
+
+ /// Did the cast hit?
+ bool hit;
+} b2CastOutput;
+
+/// This holds the mass data computed for a shape.
+typedef struct b2MassData
+{
+ /// The mass of the shape, usually in kilograms.
+ float mass;
+
+ /// The position of the shape's centroid relative to the shape's origin.
+ b2Vec2 center;
+
+ /// The rotational inertia of the shape about the local origin.
+ float rotationalInertia;
+} b2MassData;
+
+/// A solid circle
+typedef struct b2Circle
+{
+ /// The local center
+ b2Vec2 center;
+
+ /// The radius
+ float radius;
+} b2Circle;
+
+/// A solid capsule can be viewed as two semicircles connected
+/// by a rectangle.
+typedef struct b2Capsule
+{
+ /// Local center of the first semicircle
+ b2Vec2 center1;
+
+ /// Local center of the second semicircle
+ b2Vec2 center2;
+
+ /// The radius of the semicircles
+ float radius;
+} b2Capsule;
+
+/// A solid convex polygon. It is assumed that the interior of the polygon is to
+/// the left of each edge.
+/// Polygons have a maximum number of vertices equal to B2_MAX_POLYGON_VERTICES.
+/// In most cases you should not need many vertices for a convex polygon.
+/// @warning DO NOT fill this out manually, instead use a helper function like
+/// b2MakePolygon or b2MakeBox.
+typedef struct b2Polygon
+{
+ /// The polygon vertices
+ b2Vec2 vertices[B2_MAX_POLYGON_VERTICES];
+
+ /// The outward normal vectors of the polygon sides
+ b2Vec2 normals[B2_MAX_POLYGON_VERTICES];
+
+ /// The centroid of the polygon
+ b2Vec2 centroid;
+
+ /// The external radius for rounded polygons
+ float radius;
+
+ /// The number of polygon vertices
+ int count;
+} b2Polygon;
+
+/// A line segment with two-sided collision.
+typedef struct b2Segment
+{
+ /// The first point
+ b2Vec2 point1;
+
+ /// The second point
+ b2Vec2 point2;
+} b2Segment;
+
+/// A line segment with one-sided collision. Only collides on the right side.
+/// Several of these are generated for a chain shape.
+/// ghost1 -> point1 -> point2 -> ghost2
+typedef struct b2ChainSegment
+{
+ /// The tail ghost vertex
+ b2Vec2 ghost1;
+
+ /// The line segment
+ b2Segment segment;
+
+ /// The head ghost vertex
+ b2Vec2 ghost2;
+
+ /// The owning chain shape index (internal usage only)
+ int chainId;
+} b2ChainSegment;
+
+/// Validate ray cast input data (NaN, etc)
+B2_API bool b2IsValidRay( const b2RayCastInput* input );
+
+/// Make a convex polygon from a convex hull. This will assert if the hull is not valid.
+/// @warning Do not manually fill in the hull data, it must come directly from b2ComputeHull
+B2_API b2Polygon b2MakePolygon( const b2Hull* hull, float radius );
+
+/// Make an offset convex polygon from a convex hull. This will assert if the hull is not valid.
+/// @warning Do not manually fill in the hull data, it must come directly from b2ComputeHull
+B2_API b2Polygon b2MakeOffsetPolygon( const b2Hull* hull, b2Vec2 position, b2Rot rotation );
+
+/// Make an offset convex polygon from a convex hull. This will assert if the hull is not valid.
+/// @warning Do not manually fill in the hull data, it must come directly from b2ComputeHull
+B2_API b2Polygon b2MakeOffsetRoundedPolygon( const b2Hull* hull, b2Vec2 position, b2Rot rotation, float radius );
+
+/// Make a square polygon, bypassing the need for a convex hull.
+/// @param halfWidth the half-width
+B2_API b2Polygon b2MakeSquare( float halfWidth );
+
+/// Make a box (rectangle) polygon, bypassing the need for a convex hull.
+/// @param halfWidth the half-width (x-axis)
+/// @param halfHeight the half-height (y-axis)
+B2_API b2Polygon b2MakeBox( float halfWidth, float halfHeight );
+
+/// Make a rounded box, bypassing the need for a convex hull.
+/// @param halfWidth the half-width (x-axis)
+/// @param halfHeight the half-height (y-axis)
+/// @param radius the radius of the rounded extension
+B2_API b2Polygon b2MakeRoundedBox( float halfWidth, float halfHeight, float radius );
+
+/// Make an offset box, bypassing the need for a convex hull.
+/// @param halfWidth the half-width (x-axis)
+/// @param halfHeight the half-height (y-axis)
+/// @param center the local center of the box
+/// @param rotation the local rotation of the box
+B2_API b2Polygon b2MakeOffsetBox( float halfWidth, float halfHeight, b2Vec2 center, b2Rot rotation );
+
+/// Make an offset rounded box, bypassing the need for a convex hull.
+/// @param halfWidth the half-width (x-axis)
+/// @param halfHeight the half-height (y-axis)
+/// @param center the local center of the box
+/// @param rotation the local rotation of the box
+/// @param radius the radius of the rounded extension
+B2_API b2Polygon b2MakeOffsetRoundedBox( float halfWidth, float halfHeight, b2Vec2 center, b2Rot rotation, float radius );
+
+/// Transform a polygon. This is useful for transferring a shape from one body to another.
+B2_API b2Polygon b2TransformPolygon( b2Transform transform, const b2Polygon* polygon );
+
+/// Compute mass properties of a circle
+B2_API b2MassData b2ComputeCircleMass( const b2Circle* shape, float density );
+
+/// Compute mass properties of a capsule
+B2_API b2MassData b2ComputeCapsuleMass( const b2Capsule* shape, float density );
+
+/// Compute mass properties of a polygon
+B2_API b2MassData b2ComputePolygonMass( const b2Polygon* shape, float density );
+
+/// Compute the bounding box of a transformed circle
+B2_API b2AABB b2ComputeCircleAABB( const b2Circle* shape, b2Transform transform );
+
+/// Compute the bounding box of a transformed capsule
+B2_API b2AABB b2ComputeCapsuleAABB( const b2Capsule* shape, b2Transform transform );
+
+/// Compute the bounding box of a transformed polygon
+B2_API b2AABB b2ComputePolygonAABB( const b2Polygon* shape, b2Transform transform );
+
+/// Compute the bounding box of a transformed line segment
+B2_API b2AABB b2ComputeSegmentAABB( const b2Segment* shape, b2Transform transform );
+
+/// Test a point for overlap with a circle in local space
+B2_API bool b2PointInCircle( b2Vec2 point, const b2Circle* shape );
+
+/// Test a point for overlap with a capsule in local space
+B2_API bool b2PointInCapsule( b2Vec2 point, const b2Capsule* shape );
+
+/// Test a point for overlap with a convex polygon in local space
+B2_API bool b2PointInPolygon( b2Vec2 point, const b2Polygon* shape );
+
+/// Ray cast versus circle shape in local space. Initial overlap is treated as a miss.
+B2_API b2CastOutput b2RayCastCircle( const b2RayCastInput* input, const b2Circle* shape );
+
+/// Ray cast versus capsule shape in local space. Initial overlap is treated as a miss.
+B2_API b2CastOutput b2RayCastCapsule( const b2RayCastInput* input, const b2Capsule* shape );
+
+/// Ray cast versus segment shape in local space. Optionally treat the segment as one-sided with hits from
+/// the left side being treated as a miss.
+B2_API b2CastOutput b2RayCastSegment( const b2RayCastInput* input, const b2Segment* shape, bool oneSided );
+
+/// Ray cast versus polygon shape in local space. Initial overlap is treated as a miss.
+B2_API b2CastOutput b2RayCastPolygon( const b2RayCastInput* input, const b2Polygon* shape );
+
+/// Shape cast versus a circle. Initial overlap is treated as a miss.
+B2_API b2CastOutput b2ShapeCastCircle( const b2ShapeCastInput* input, const b2Circle* shape );
+
+/// Shape cast versus a capsule. Initial overlap is treated as a miss.
+B2_API b2CastOutput b2ShapeCastCapsule( const b2ShapeCastInput* input, const b2Capsule* shape );
+
+/// Shape cast versus a line segment. Initial overlap is treated as a miss.
+B2_API b2CastOutput b2ShapeCastSegment( const b2ShapeCastInput* input, const b2Segment* shape );
+
+/// Shape cast versus a convex polygon. Initial overlap is treated as a miss.
+B2_API b2CastOutput b2ShapeCastPolygon( const b2ShapeCastInput* input, const b2Polygon* shape );
+
+/// A convex hull. Used to create convex polygons.
+/// @warning Do not modify these values directly, instead use b2ComputeHull()
+typedef struct b2Hull
+{
+ /// The final points of the hull
+ b2Vec2 points[B2_MAX_POLYGON_VERTICES];
+
+ /// The number of points
+ int count;
+} b2Hull;
+
+/// Compute the convex hull of a set of points. Returns an empty hull if it fails.
+/// Some failure cases:
+/// - all points very close together
+/// - all points on a line
+/// - less than 3 points
+/// - more than B2_MAX_POLYGON_VERTICES points
+/// This welds close points and removes collinear points.
+/// @warning Do not modify a hull once it has been computed
+B2_API b2Hull b2ComputeHull( const b2Vec2* points, int count );
+
+/// This determines if a hull is valid. Checks for:
+/// - convexity
+/// - collinear points
+/// This is expensive and should not be called at runtime.
+B2_API bool b2ValidateHull( const b2Hull* hull );
+
+/**@}*/
+
+/**
+ * @defgroup distance Distance
+ * Functions for computing the distance between shapes.
+ *
+ * These are advanced functions you can use to perform distance calculations. There
+ * are functions for computing the closest points between shapes, doing linear shape casts,
+ * and doing rotational shape casts. The latter is called time of impact (TOI).
+ * @{
+ */
+
+/// Result of computing the distance between two line segments
+typedef struct b2SegmentDistanceResult
+{
+ /// The closest point on the first segment
+ b2Vec2 closest1;
+
+ /// The closest point on the second segment
+ b2Vec2 closest2;
+
+ /// The barycentric coordinate on the first segment
+ float fraction1;
+
+ /// The barycentric coordinate on the second segment
+ float fraction2;
+
+ /// The squared distance between the closest points
+ float distanceSquared;
+} b2SegmentDistanceResult;
+
+/// Compute the distance between two line segments, clamping at the end points if needed.
+B2_API b2SegmentDistanceResult b2SegmentDistance( b2Vec2 p1, b2Vec2 q1, b2Vec2 p2, b2Vec2 q2 );
+
+/// A distance proxy is used by the GJK algorithm. It encapsulates any shape.
+typedef struct b2ShapeProxy
+{
+ /// The point cloud
+ b2Vec2 points[B2_MAX_POLYGON_VERTICES];
+
+ /// The number of points
+ int count;
+
+ /// The external radius of the point cloud
+ float radius;
+} b2ShapeProxy;
+
+/// Used to warm start the GJK simplex. If you call this function multiple times with nearby
+/// transforms this might improve performance. Otherwise you can zero initialize this.
+/// The distance cache must be initialized to zero on the first call.
+/// Users should generally just zero initialize this structure for each call.
+typedef struct b2SimplexCache
+{
+ /// The number of stored simplex points
+ uint16_t count;
+
+ /// The cached simplex indices on shape A
+ uint8_t indexA[3];
+
+ /// The cached simplex indices on shape B
+ uint8_t indexB[3];
+} b2SimplexCache;
+
+static const b2SimplexCache b2_emptySimplexCache = B2_ZERO_INIT;
+
+/// Input for b2ShapeDistance
+typedef struct b2DistanceInput
+{
+ /// The proxy for shape A
+ b2ShapeProxy proxyA;
+
+ /// The proxy for shape B
+ b2ShapeProxy proxyB;
+
+ /// The world transform for shape A
+ b2Transform transformA;
+
+ /// The world transform for shape B
+ b2Transform transformB;
+
+ /// Should the proxy radius be considered?
+ bool useRadii;
+} b2DistanceInput;
+
+/// Output for b2ShapeDistance
+typedef struct b2DistanceOutput
+{
+ b2Vec2 pointA; ///< Closest point on shapeA
+ b2Vec2 pointB; ///< Closest point on shapeB
+ // todo_erin implement this
+ // b2Vec2 normal; ///< Normal vector that points from A to B
+ float distance; ///< The final distance, zero if overlapped
+ int iterations; ///< Number of GJK iterations used
+ int simplexCount; ///< The number of simplexes stored in the simplex array
+} b2DistanceOutput;
+
+/// Simplex vertex for debugging the GJK algorithm
+typedef struct b2SimplexVertex
+{
+ b2Vec2 wA; ///< support point in proxyA
+ b2Vec2 wB; ///< support point in proxyB
+ b2Vec2 w; ///< wB - wA
+ float a; ///< barycentric coordinate for closest point
+ int indexA; ///< wA index
+ int indexB; ///< wB index
+} b2SimplexVertex;
+
+/// Simplex from the GJK algorithm
+typedef struct b2Simplex
+{
+ b2SimplexVertex v1, v2, v3; ///< vertices
+ int count; ///< number of valid vertices
+} b2Simplex;
+
+/// Compute the closest points between two shapes represented as point clouds.
+/// b2SimplexCache cache is input/output. On the first call set b2SimplexCache.count to zero.
+/// The underlying GJK algorithm may be debugged by passing in debug simplexes and capacity. You may pass in NULL and 0 for these.
+B2_API b2DistanceOutput b2ShapeDistance( b2SimplexCache* cache, const b2DistanceInput* input, b2Simplex* simplexes,
+ int simplexCapacity );
+
+/// Input parameters for b2ShapeCast
+typedef struct b2ShapeCastPairInput
+{
+ b2ShapeProxy proxyA; ///< The proxy for shape A
+ b2ShapeProxy proxyB; ///< The proxy for shape B
+ b2Transform transformA; ///< The world transform for shape A
+ b2Transform transformB; ///< The world transform for shape B
+ b2Vec2 translationB; ///< The translation of shape B
+ float maxFraction; ///< The fraction of the translation to consider, typically 1
+} b2ShapeCastPairInput;
+
+/// Perform a linear shape cast of shape B moving and shape A fixed. Determines the hit point, normal, and translation fraction.
+B2_API b2CastOutput b2ShapeCast( const b2ShapeCastPairInput* input );
+
+/// Make a proxy for use in GJK and related functions.
+B2_API b2ShapeProxy b2MakeProxy( const b2Vec2* vertices, int count, float radius );
+
+/// This describes the motion of a body/shape for TOI computation. Shapes are defined with respect to the body origin,
+/// which may not coincide with the center of mass. However, to support dynamics we must interpolate the center of mass
+/// position.
+typedef struct b2Sweep
+{
+ b2Vec2 localCenter; ///< Local center of mass position
+ b2Vec2 c1; ///< Starting center of mass world position
+ b2Vec2 c2; ///< Ending center of mass world position
+ b2Rot q1; ///< Starting world rotation
+ b2Rot q2; ///< Ending world rotation
+} b2Sweep;
+
+/// Evaluate the transform sweep at a specific time.
+B2_API b2Transform b2GetSweepTransform( const b2Sweep* sweep, float time );
+
+/// Input parameters for b2TimeOfImpact
+typedef struct b2TOIInput
+{
+ b2ShapeProxy proxyA; ///< The proxy for shape A
+ b2ShapeProxy proxyB; ///< The proxy for shape B
+ b2Sweep sweepA; ///< The movement of shape A
+ b2Sweep sweepB; ///< The movement of shape B
+ float maxFraction; ///< Defines the sweep interval [0, maxFraction]
+} b2TOIInput;
+
+/// Describes the TOI output
+typedef enum b2TOIState
+{
+ b2_toiStateUnknown,
+ b2_toiStateFailed,
+ b2_toiStateOverlapped,
+ b2_toiStateHit,
+ b2_toiStateSeparated
+} b2TOIState;
+
+/// Output parameters for b2TimeOfImpact.
+typedef struct b2TOIOutput
+{
+ b2TOIState state; ///< The type of result
+ float fraction; ///< The sweep time of the collision
+} b2TOIOutput;
+
+/// Compute the upper bound on time before two shapes penetrate. Time is represented as
+/// a fraction between [0,tMax]. This uses a swept separating axis and may miss some intermediate,
+/// non-tunneling collisions. If you change the time interval, you should call this function
+/// again.
+B2_API b2TOIOutput b2TimeOfImpact( const b2TOIInput* input );
+
+/**@}*/
+
+/**
+ * @defgroup collision Collision
+ * @brief Functions for colliding pairs of shapes
+ * @{
+ */
+
+/// A manifold point is a contact point belonging to a contact manifold.
+/// It holds details related to the geometry and dynamics of the contact points.
+/// Box2D uses speculative collision so some contact points may be separated.
+/// You may use the maxNormalImpulse to determine if there was an interaction during
+/// the time step.
+typedef struct b2ManifoldPoint
+{
+ /// Location of the contact point in world space. Subject to precision loss at large coordinates.
+ /// @note Should only be used for debugging.
+ b2Vec2 point;
+
+ /// Location of the contact point relative to shapeA's origin in world space
+ /// @note When used internally to the Box2D solver, this is relative to the body center of mass.
+ b2Vec2 anchorA;
+
+ /// Location of the contact point relative to shapeB's origin in world space
+ /// @note When used internally to the Box2D solver, this is relative to the body center of mass.
+ b2Vec2 anchorB;
+
+ /// The separation of the contact point, negative if penetrating
+ float separation;
+
+ /// The impulse along the manifold normal vector.
+ float normalImpulse;
+
+ /// The friction impulse
+ float tangentImpulse;
+
+ /// The maximum normal impulse applied during sub-stepping. This is important
+ /// to identify speculative contact points that had an interaction in the time step.
+ float maxNormalImpulse;
+
+ /// Relative normal velocity pre-solve. Used for hit events. If the normal impulse is
+ /// zero then there was no hit. Negative means shapes are approaching.
+ float normalVelocity;
+
+ /// Uniquely identifies a contact point between two shapes
+ uint16_t id;
+
+ /// Did this contact point exist the previous step?
+ bool persisted;
+} b2ManifoldPoint;
+
+/// A contact manifold describes the contact points between colliding shapes.
+/// @note Box2D uses speculative collision so some contact points may be separated.
+typedef struct b2Manifold
+{
+ /// The unit normal vector in world space, points from shape A to bodyB
+ b2Vec2 normal;
+
+ /// Angular impulse applied for rolling resistance. N * m * s = kg * m^2 / s
+ float rollingImpulse;
+
+ /// The manifold points, up to two are possible in 2D
+ b2ManifoldPoint points[2];
+
+ /// The number of contacts points, will be 0, 1, or 2
+ int pointCount;
+
+} b2Manifold;
+
+/// Compute the contact manifold between two circles
+B2_API b2Manifold b2CollideCircles( const b2Circle* circleA, b2Transform xfA, const b2Circle* circleB, b2Transform xfB );
+
+/// Compute the contact manifold between a capsule and circle
+B2_API b2Manifold b2CollideCapsuleAndCircle( const b2Capsule* capsuleA, b2Transform xfA, const b2Circle* circleB,
+ b2Transform xfB );
+
+/// Compute the contact manifold between an segment and a circle
+B2_API b2Manifold b2CollideSegmentAndCircle( const b2Segment* segmentA, b2Transform xfA, const b2Circle* circleB,
+ b2Transform xfB );
+
+/// Compute the contact manifold between a polygon and a circle
+B2_API b2Manifold b2CollidePolygonAndCircle( const b2Polygon* polygonA, b2Transform xfA, const b2Circle* circleB,
+ b2Transform xfB );
+
+/// Compute the contact manifold between a capsule and circle
+B2_API b2Manifold b2CollideCapsules( const b2Capsule* capsuleA, b2Transform xfA, const b2Capsule* capsuleB, b2Transform xfB );
+
+/// Compute the contact manifold between an segment and a capsule
+B2_API b2Manifold b2CollideSegmentAndCapsule( const b2Segment* segmentA, b2Transform xfA, const b2Capsule* capsuleB,
+ b2Transform xfB );
+
+/// Compute the contact manifold between a polygon and capsule
+B2_API b2Manifold b2CollidePolygonAndCapsule( const b2Polygon* polygonA, b2Transform xfA, const b2Capsule* capsuleB,
+ b2Transform xfB );
+
+/// Compute the contact manifold between two polygons
+B2_API b2Manifold b2CollidePolygons( const b2Polygon* polygonA, b2Transform xfA, const b2Polygon* polygonB, b2Transform xfB );
+
+/// Compute the contact manifold between an segment and a polygon
+B2_API b2Manifold b2CollideSegmentAndPolygon( const b2Segment* segmentA, b2Transform xfA, const b2Polygon* polygonB,
+ b2Transform xfB );
+
+/// Compute the contact manifold between a chain segment and a circle
+B2_API b2Manifold b2CollideChainSegmentAndCircle( const b2ChainSegment* segmentA, b2Transform xfA, const b2Circle* circleB,
+ b2Transform xfB );
+
+/// Compute the contact manifold between a chain segment and a capsule
+B2_API b2Manifold b2CollideChainSegmentAndCapsule( const b2ChainSegment* segmentA, b2Transform xfA, const b2Capsule* capsuleB,
+ b2Transform xfB, b2SimplexCache* cache );
+
+/// Compute the contact manifold between a chain segment and a rounded polygon
+B2_API b2Manifold b2CollideChainSegmentAndPolygon( const b2ChainSegment* segmentA, b2Transform xfA, const b2Polygon* polygonB,
+ b2Transform xfB, b2SimplexCache* cache );
+
+/**@}*/
+
+/**
+ * @defgroup tree Dynamic Tree
+ * The dynamic tree is a binary AABB tree to organize and query large numbers of geometric objects
+ *
+ * Box2D uses the dynamic tree internally to sort collision shapes into a binary bounding volume hierarchy.
+ * This data structure may have uses in games for organizing other geometry data and may be used independently
+ * of Box2D rigid body simulation.
+ *
+ * A dynamic AABB tree broad-phase, inspired by Nathanael Presson's btDbvt.
+ * A dynamic tree arranges data in a binary tree to accelerate
+ * queries such as AABB queries and ray casts. Leaf nodes are proxies
+ * with an AABB. These are used to hold a user collision object.
+ * Nodes are pooled and relocatable, so I use node indices rather than pointers.
+ * The dynamic tree is made available for advanced users that would like to use it to organize
+ * spatial game data besides rigid bodies.
+ * @{
+ */
+
+/// The dynamic tree structure. This should be considered private data.
+/// It is placed here for performance reasons.
+typedef struct b2DynamicTree
+{
+ /// The tree nodes
+ struct b2TreeNode* nodes;
+
+ /// The root index
+ int root;
+
+ /// The number of nodes
+ int nodeCount;
+
+ /// The allocated node space
+ int nodeCapacity;
+
+ /// Node free list
+ int freeList;
+
+ /// Number of proxies created
+ int proxyCount;
+
+ /// Leaf indices for rebuild
+ int* leafIndices;
+
+ /// Leaf bounding boxes for rebuild
+ b2AABB* leafBoxes;
+
+ /// Leaf bounding box centers for rebuild
+ b2Vec2* leafCenters;
+
+ /// Bins for sorting during rebuild
+ int* binIndices;
+
+ /// Allocated space for rebuilding
+ int rebuildCapacity;
+} b2DynamicTree;
+
+/// These are performance results returned by dynamic tree queries.
+typedef struct b2TreeStats
+{
+ /// Number of internal nodes visited during the query
+ int nodeVisits;
+
+ /// Number of leaf nodes visited during the query
+ int leafVisits;
+} b2TreeStats;
+
+/// Constructing the tree initializes the node pool.
+B2_API b2DynamicTree b2DynamicTree_Create( void );
+
+/// Destroy the tree, freeing the node pool.
+B2_API void b2DynamicTree_Destroy( b2DynamicTree* tree );
+
+/// Create a proxy. Provide an AABB and a userData value.
+B2_API int b2DynamicTree_CreateProxy( b2DynamicTree* tree, b2AABB aabb, uint64_t categoryBits, int userData );
+
+/// Destroy a proxy. This asserts if the id is invalid.
+B2_API void b2DynamicTree_DestroyProxy( b2DynamicTree* tree, int proxyId );
+
+/// Move a proxy to a new AABB by removing and reinserting into the tree.
+B2_API void b2DynamicTree_MoveProxy( b2DynamicTree* tree, int proxyId, b2AABB aabb );
+
+/// Enlarge a proxy and enlarge ancestors as necessary.
+B2_API void b2DynamicTree_EnlargeProxy( b2DynamicTree* tree, int proxyId, b2AABB aabb );
+
+/// This function receives proxies found in the AABB query.
+/// @return true if the query should continue
+typedef bool b2TreeQueryCallbackFcn( int proxyId, int userData, void* context );
+
+/// Query an AABB for overlapping proxies. The callback class is called for each proxy that overlaps the supplied AABB.
+/// @return performance data
+B2_API b2TreeStats b2DynamicTree_Query( const b2DynamicTree* tree, b2AABB aabb, uint64_t maskBits,
+ b2TreeQueryCallbackFcn* callback, void* context );
+
+/// This function receives clipped ray cast input for a proxy. The function
+/// returns the new ray fraction.
+/// - return a value of 0 to terminate the ray cast
+/// - return a value less than input->maxFraction to clip the ray
+/// - return a value of input->maxFraction to continue the ray cast without clipping
+typedef float b2TreeRayCastCallbackFcn( const b2RayCastInput* input, int proxyId, int userData, void* context );
+
+/// Ray cast against the proxies in the tree. This relies on the callback
+/// to perform a exact ray cast in the case were the proxy contains a shape.
+/// The callback also performs the any collision filtering. This has performance
+/// roughly equal to k * log(n), where k is the number of collisions and n is the
+/// number of proxies in the tree.
+/// Bit-wise filtering using mask bits can greatly improve performance in some scenarios.
+/// However, this filtering may be approximate, so the user should still apply filtering to results.
+/// @param tree the dynamic tree to ray cast
+/// @param input the ray cast input data. The ray extends from p1 to p1 + maxFraction * (p2 - p1)
+/// @param maskBits mask bit hint: `bool accept = (maskBits & node->categoryBits) != 0;`
+/// @param callback a callback class that is called for each proxy that is hit by the ray
+/// @param context user context that is passed to the callback
+/// @return performance data
+B2_API b2TreeStats b2DynamicTree_RayCast( const b2DynamicTree* tree, const b2RayCastInput* input, uint64_t maskBits,
+ b2TreeRayCastCallbackFcn* callback, void* context );
+
+/// This function receives clipped ray cast input for a proxy. The function
+/// returns the new ray fraction.
+/// - return a value of 0 to terminate the ray cast
+/// - return a value less than input->maxFraction to clip the ray
+/// - return a value of input->maxFraction to continue the ray cast without clipping
+typedef float b2TreeShapeCastCallbackFcn( const b2ShapeCastInput* input, int proxyId, int userData, void* context );
+
+/// Ray cast against the proxies in the tree. This relies on the callback
+/// to perform a exact ray cast in the case were the proxy contains a shape.
+/// The callback also performs the any collision filtering. This has performance
+/// roughly equal to k * log(n), where k is the number of collisions and n is the
+/// number of proxies in the tree.
+/// @param tree the dynamic tree to ray cast
+/// @param input the ray cast input data. The ray extends from p1 to p1 + maxFraction * (p2 - p1).
+/// @param maskBits filter bits: `bool accept = (maskBits & node->categoryBits) != 0;`
+/// @param callback a callback class that is called for each proxy that is hit by the shape
+/// @param context user context that is passed to the callback
+/// @return performance data
+B2_API b2TreeStats b2DynamicTree_ShapeCast( const b2DynamicTree* tree, const b2ShapeCastInput* input, uint64_t maskBits,
+ b2TreeShapeCastCallbackFcn* callback, void* context );
+
+/// Get the height of the binary tree.
+B2_API int b2DynamicTree_GetHeight( const b2DynamicTree* tree );
+
+/// Get the ratio of the sum of the node areas to the root area.
+B2_API float b2DynamicTree_GetAreaRatio( const b2DynamicTree* tree );
+
+/// Get the number of proxies created
+B2_API int b2DynamicTree_GetProxyCount( const b2DynamicTree* tree );
+
+/// Rebuild the tree while retaining subtrees that haven't changed. Returns the number of boxes sorted.
+B2_API int b2DynamicTree_Rebuild( b2DynamicTree* tree, bool fullBuild );
+
+/// Get the number of bytes used by this tree
+B2_API int b2DynamicTree_GetByteCount( const b2DynamicTree* tree );
+
+/// Get proxy user data
+B2_API int b2DynamicTree_GetUserData( const b2DynamicTree* tree, int proxyId );
+
+/// Get the AABB of a proxy
+B2_API b2AABB b2DynamicTree_GetAABB( const b2DynamicTree* tree, int proxyId );
+
+/// Validate this tree. For testing.
+B2_API void b2DynamicTree_Validate( const b2DynamicTree* tree );
+
+/// Validate this tree has no enlarged AABBs. For testing.
+B2_API void b2DynamicTree_ValidateNoEnlarged( const b2DynamicTree* tree );
+
+
+
+/**@}*/
diff --git a/odin-c-bindgen/examples/box2d/input/id.h b/odin-c-bindgen/examples/box2d/input/id.h
@@ -0,0 +1,144 @@
+// SPDX-FileCopyrightText: 2023 Erin Catto
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include "base.h"
+
+#include <stdint.h>
+
+/**
+ * @defgroup id Ids
+ * These ids serve as handles to internal Box2D objects.
+ * These should be considered opaque data and passed by value.
+ * Include this header if you need the id types and not the whole Box2D API.
+ * All ids are considered null if initialized to zero.
+ *
+ * For example in C++:
+ *
+ * @code{.cxx}
+ * b2WorldId worldId = {};
+ * @endcode
+ *
+ * Or in C:
+ *
+ * @code{.c}
+ * b2WorldId worldId = {0};
+ * @endcode
+ *
+ * These are both considered null.
+ *
+ * @warning Do not use the internals of these ids. They are subject to change. Ids should be treated as opaque objects.
+ * @warning You should use ids to access objects in Box2D. Do not access files within the src folder. Such usage is unsupported.
+ * @{
+ */
+
+/// World id references a world instance. This should be treated as an opaque handle.
+typedef struct b2WorldId
+{
+ uint16_t index1;
+ uint16_t generation;
+} b2WorldId;
+
+/// Body id references a body instance. This should be treated as an opaque handle.
+typedef struct b2BodyId
+{
+ int32_t index1;
+ uint16_t world0;
+ uint16_t generation;
+} b2BodyId;
+
+/// Shape id references a shape instance. This should be treated as an opaque handle.
+typedef struct b2ShapeId
+{
+ int32_t index1;
+ uint16_t world0;
+ uint16_t generation;
+} b2ShapeId;
+
+/// Chain id references a chain instances. This should be treated as an opaque handle.
+typedef struct b2ChainId
+{
+ int32_t index1;
+ uint16_t world0;
+ uint16_t generation;
+} b2ChainId;
+
+/// Joint id references a joint instance. This should be treated as an opaque handle.
+typedef struct b2JointId
+{
+ int32_t index1;
+ uint16_t world0;
+ uint16_t generation;
+} b2JointId;
+
+/// Use these to make your identifiers null.
+/// You may also use zero initialization to get null.
+static const b2WorldId b2_nullWorldId = B2_ZERO_INIT;
+static const b2BodyId b2_nullBodyId = B2_ZERO_INIT;
+static const b2ShapeId b2_nullShapeId = B2_ZERO_INIT;
+static const b2ChainId b2_nullChainId = B2_ZERO_INIT;
+static const b2JointId b2_nullJointId = B2_ZERO_INIT;
+
+/// Macro to determine if any id is null.
+#define B2_IS_NULL( id ) ( id.index1 == 0 )
+
+/// Macro to determine if any id is non-null.
+#define B2_IS_NON_NULL( id ) ( id.index1 != 0 )
+
+/// Compare two ids for equality. Doesn't work for b2WorldId.
+#define B2_ID_EQUALS( id1, id2 ) ( id1.index1 == id2.index1 && id1.world0 == id2.world0 && id1.generation == id2.generation )
+
+/// Store a body id into a uint64_t.
+B2_INLINE uint64_t b2StoreBodyId( b2BodyId id )
+{
+ return ( (uint64_t)id.index1 << 32 ) | ( (uint64_t)id.world0 ) << 16 | (uint64_t)id.generation;
+}
+
+/// Load a uint64_t into a body id.
+B2_INLINE b2BodyId b2LoadBodyId( uint64_t x )
+{
+ b2BodyId id = { (int32_t)( x >> 32 ), (uint16_t)( x >> 16 ), (uint16_t)( x ) };
+ return id;
+}
+
+/// Store a shape id into a uint64_t.
+B2_INLINE uint64_t b2StoreShapeId( b2ShapeId id )
+{
+ return ( (uint64_t)id.index1 << 32 ) | ( (uint64_t)id.world0 ) << 16 | (uint64_t)id.generation;
+}
+
+/// Load a uint64_t into a shape id.
+B2_INLINE b2ShapeId b2LoadShapeId( uint64_t x )
+{
+ b2ShapeId id = { (int32_t)( x >> 32 ), (uint16_t)( x >> 16 ), (uint16_t)( x ) };
+ return id;
+}
+
+/// Store a chain id into a uint64_t.
+B2_INLINE uint64_t b2StoreChainId( b2ChainId id )
+{
+ return ( (uint64_t)id.index1 << 32 ) | ( (uint64_t)id.world0 ) << 16 | (uint64_t)id.generation;
+}
+
+/// Load a uint64_t into a chain id.
+B2_INLINE b2ChainId b2LoadChainId( uint64_t x )
+{
+ b2ChainId id = { (int32_t)( x >> 32 ), (uint16_t)( x >> 16 ), (uint16_t)( x ) };
+ return id;
+}
+
+/// Store a joint id into a uint64_t.
+B2_INLINE uint64_t b2StoreJointId( b2JointId id )
+{
+ return ( (uint64_t)id.index1 << 32 ) | ( (uint64_t)id.world0 ) << 16 | (uint64_t)id.generation;
+}
+
+/// Load a uint64_t into a joint id.
+B2_INLINE b2JointId b2LoadJointId( uint64_t x )
+{
+ b2JointId id = { (int32_t)( x >> 32 ), (uint16_t)( x >> 16 ), (uint16_t)( x ) };
+ return id;
+}
+
+/**@}*/
diff --git a/odin-c-bindgen/examples/box2d/input/math_functions.h b/odin-c-bindgen/examples/box2d/input/math_functions.h
@@ -0,0 +1,716 @@
+// SPDX-FileCopyrightText: 2023 Erin Catto
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include "base.h"
+
+#include <float.h>
+#include <math.h>
+#include <stdbool.h>
+
+/**
+ * @defgroup math Math
+ * @brief Vector math types and functions
+ * @{
+ */
+
+/// 2D vector
+/// This can be used to represent a point or free vector
+typedef struct b2Vec2
+{
+ /// coordinates
+ float x, y;
+} b2Vec2;
+
+/// Cosine and sine pair
+/// This uses a custom implementation designed for cross-platform determinism
+typedef struct b2CosSin
+{
+ /// cosine and sine
+ float cosine;
+ float sine;
+} b2CosSin;
+
+/// 2D rotation
+/// This is similar to using a complex number for rotation
+typedef struct b2Rot
+{
+ /// cosine and sine
+ float c, s;
+} b2Rot;
+
+/// A 2D rigid transform
+typedef struct b2Transform
+{
+ b2Vec2 p;
+ b2Rot q;
+} b2Transform;
+
+/// A 2-by-2 Matrix
+typedef struct b2Mat22
+{
+ /// columns
+ b2Vec2 cx, cy;
+} b2Mat22;
+
+/// Axis-aligned bounding box
+typedef struct b2AABB
+{
+ b2Vec2 lowerBound;
+ b2Vec2 upperBound;
+} b2AABB;
+
+/**@}*/
+
+/**
+ * @addtogroup math
+ * @{
+ */
+
+/// https://en.wikipedia.org/wiki/Pi
+#define B2_PI 3.14159265359f
+
+static const b2Vec2 b2Vec2_zero = { 0.0f, 0.0f };
+static const b2Rot b2Rot_identity = { 1.0f, 0.0f };
+static const b2Transform b2Transform_identity = { { 0.0f, 0.0f }, { 1.0f, 0.0f } };
+static const b2Mat22 b2Mat22_zero = { { 0.0f, 0.0f }, { 0.0f, 0.0f } };
+
+/// @return the minimum of two integers
+B2_INLINE int b2MinInt( int a, int b )
+{
+ return a < b ? a : b;
+}
+
+/// @return the maximum of two integers
+B2_INLINE int b2MaxInt( int a, int b )
+{
+ return a > b ? a : b;
+}
+
+/// @return the absolute value of an integer
+B2_INLINE int b2AbsInt( int a )
+{
+ return a < 0 ? -a : a;
+}
+
+/// @return an integer clamped between a lower and upper bound
+B2_INLINE int b2ClampInt( int a, int lower, int upper )
+{
+ return a < lower ? lower : ( a > upper ? upper : a );
+}
+
+/// @return the minimum of two floats
+B2_INLINE float b2MinFloat( float a, float b )
+{
+ return a < b ? a : b;
+}
+
+/// @return the maximum of two floats
+B2_INLINE float b2MaxFloat( float a, float b )
+{
+ return a > b ? a : b;
+}
+
+/// @return the absolute value of a float
+B2_INLINE float b2AbsFloat( float a )
+{
+ return a < 0 ? -a : a;
+}
+
+/// @return a float clamped between a lower and upper bound
+B2_INLINE float b2ClampFloat( float a, float lower, float upper )
+{
+ return a < lower ? lower : ( a > upper ? upper : a );
+}
+
+/// Compute an approximate arctangent in the range [-pi, pi]
+/// This is hand coded for cross-platform determinism. The atan2f
+/// function in the standard library is not cross-platform deterministic.
+/// Accurate to around 0.0023 degrees
+B2_API float b2Atan2( float y, float x );
+
+/// Compute the cosine and sine of an angle in radians. Implemented
+/// for cross-platform determinism.
+B2_API b2CosSin b2ComputeCosSin( float radians );
+
+/// Vector dot product
+B2_INLINE float b2Dot( b2Vec2 a, b2Vec2 b )
+{
+ return a.x * b.x + a.y * b.y;
+}
+
+/// Vector cross product. In 2D this yields a scalar.
+B2_INLINE float b2Cross( b2Vec2 a, b2Vec2 b )
+{
+ return a.x * b.y - a.y * b.x;
+}
+
+/// Perform the cross product on a vector and a scalar. In 2D this produces a vector.
+B2_INLINE b2Vec2 b2CrossVS( b2Vec2 v, float s )
+{
+ return B2_LITERAL( b2Vec2 ){ s * v.y, -s * v.x };
+}
+
+/// Perform the cross product on a scalar and a vector. In 2D this produces a vector.
+B2_INLINE b2Vec2 b2CrossSV( float s, b2Vec2 v )
+{
+ return B2_LITERAL( b2Vec2 ){ -s * v.y, s * v.x };
+}
+
+/// Get a left pointing perpendicular vector. Equivalent to b2CrossSV(1.0f, v)
+B2_INLINE b2Vec2 b2LeftPerp( b2Vec2 v )
+{
+ return B2_LITERAL( b2Vec2 ){ -v.y, v.x };
+}
+
+/// Get a right pointing perpendicular vector. Equivalent to b2CrossVS(v, 1.0f)
+B2_INLINE b2Vec2 b2RightPerp( b2Vec2 v )
+{
+ return B2_LITERAL( b2Vec2 ){ v.y, -v.x };
+}
+
+/// Vector addition
+B2_INLINE b2Vec2 b2Add( b2Vec2 a, b2Vec2 b )
+{
+ return B2_LITERAL( b2Vec2 ){ a.x + b.x, a.y + b.y };
+}
+
+/// Vector subtraction
+B2_INLINE b2Vec2 b2Sub( b2Vec2 a, b2Vec2 b )
+{
+ return B2_LITERAL( b2Vec2 ){ a.x - b.x, a.y - b.y };
+}
+
+/// Vector negation
+B2_INLINE b2Vec2 b2Neg( b2Vec2 a )
+{
+ return B2_LITERAL( b2Vec2 ){ -a.x, -a.y };
+}
+
+/// Vector linear interpolation
+/// https://fgiesen.wordpress.com/2012/08/15/linear-interpolation-past-present-and-future/
+B2_INLINE b2Vec2 b2Lerp( b2Vec2 a, b2Vec2 b, float t )
+{
+ return B2_LITERAL( b2Vec2 ){ ( 1.0f - t ) * a.x + t * b.x, ( 1.0f - t ) * a.y + t * b.y };
+}
+
+/// Component-wise multiplication
+B2_INLINE b2Vec2 b2Mul( b2Vec2 a, b2Vec2 b )
+{
+ return B2_LITERAL( b2Vec2 ){ a.x * b.x, a.y * b.y };
+}
+
+/// Multiply a scalar and vector
+B2_INLINE b2Vec2 b2MulSV( float s, b2Vec2 v )
+{
+ return B2_LITERAL( b2Vec2 ){ s * v.x, s * v.y };
+}
+
+/// a + s * b
+B2_INLINE b2Vec2 b2MulAdd( b2Vec2 a, float s, b2Vec2 b )
+{
+ return B2_LITERAL( b2Vec2 ){ a.x + s * b.x, a.y + s * b.y };
+}
+
+/// a - s * b
+B2_INLINE b2Vec2 b2MulSub( b2Vec2 a, float s, b2Vec2 b )
+{
+ return B2_LITERAL( b2Vec2 ){ a.x - s * b.x, a.y - s * b.y };
+}
+
+/// Component-wise absolute vector
+B2_INLINE b2Vec2 b2Abs( b2Vec2 a )
+{
+ b2Vec2 b;
+ b.x = b2AbsFloat( a.x );
+ b.y = b2AbsFloat( a.y );
+ return b;
+}
+
+/// Component-wise minimum vector
+B2_INLINE b2Vec2 b2Min( b2Vec2 a, b2Vec2 b )
+{
+ b2Vec2 c;
+ c.x = b2MinFloat( a.x, b.x );
+ c.y = b2MinFloat( a.y, b.y );
+ return c;
+}
+
+/// Component-wise maximum vector
+B2_INLINE b2Vec2 b2Max( b2Vec2 a, b2Vec2 b )
+{
+ b2Vec2 c;
+ c.x = b2MaxFloat( a.x, b.x );
+ c.y = b2MaxFloat( a.y, b.y );
+ return c;
+}
+
+/// Component-wise clamp vector v into the range [a, b]
+B2_INLINE b2Vec2 b2Clamp( b2Vec2 v, b2Vec2 a, b2Vec2 b )
+{
+ b2Vec2 c;
+ c.x = b2ClampFloat( v.x, a.x, b.x );
+ c.y = b2ClampFloat( v.y, a.y, b.y );
+ return c;
+}
+
+/// Get the length of this vector (the norm)
+B2_INLINE float b2Length( b2Vec2 v )
+{
+ return sqrtf( v.x * v.x + v.y * v.y );
+}
+
+/// Get the distance between two points
+B2_INLINE float b2Distance( b2Vec2 a, b2Vec2 b )
+{
+ float dx = b.x - a.x;
+ float dy = b.y - a.y;
+ return sqrtf( dx * dx + dy * dy );
+}
+
+/// Convert a vector into a unit vector if possible, otherwise returns the zero vector.
+B2_INLINE b2Vec2 b2Normalize( b2Vec2 v )
+{
+ float length = sqrtf( v.x * v.x + v.y * v.y );
+ if ( length < FLT_EPSILON )
+ {
+ return b2Vec2_zero;
+ }
+
+ float invLength = 1.0f / length;
+ b2Vec2 n = { invLength * v.x, invLength * v.y };
+ return n;
+}
+
+/// Convert a vector into a unit vector if possible, otherwise returns the zero vector. Also
+/// outputs the length.
+B2_INLINE b2Vec2 b2GetLengthAndNormalize( float* length, b2Vec2 v )
+{
+ *length = b2Length( v );
+ if ( *length < FLT_EPSILON )
+ {
+ return b2Vec2_zero;
+ }
+
+ float invLength = 1.0f / *length;
+ b2Vec2 n = { invLength * v.x, invLength * v.y };
+ return n;
+}
+
+/// Normalize rotation
+B2_INLINE b2Rot b2NormalizeRot( b2Rot q )
+{
+ float mag = sqrtf( q.s * q.s + q.c * q.c );
+ float invMag = mag > 0.0 ? 1.0f / mag : 0.0f;
+ b2Rot qn = { q.c * invMag, q.s * invMag };
+ return qn;
+}
+
+/// Integrate rotation from angular velocity
+/// @param q1 initial rotation
+/// @param deltaAngle the angular displacement in radians
+B2_INLINE b2Rot b2IntegrateRotation( b2Rot q1, float deltaAngle )
+{
+ // dc/dt = -omega * sin(t)
+ // ds/dt = omega * cos(t)
+ // c2 = c1 - omega * h * s1
+ // s2 = s1 + omega * h * c1
+ b2Rot q2 = { q1.c - deltaAngle * q1.s, q1.s + deltaAngle * q1.c };
+ float mag = sqrtf( q2.s * q2.s + q2.c * q2.c );
+ float invMag = mag > 0.0 ? 1.0f / mag : 0.0f;
+ b2Rot qn = { q2.c * invMag, q2.s * invMag };
+ return qn;
+}
+
+/// Get the length squared of this vector
+B2_INLINE float b2LengthSquared( b2Vec2 v )
+{
+ return v.x * v.x + v.y * v.y;
+}
+
+/// Get the distance squared between points
+B2_INLINE float b2DistanceSquared( b2Vec2 a, b2Vec2 b )
+{
+ b2Vec2 c = { b.x - a.x, b.y - a.y };
+ return c.x * c.x + c.y * c.y;
+}
+
+/// Make a rotation using an angle in radians
+B2_INLINE b2Rot b2MakeRot( float radians )
+{
+ b2CosSin cs = b2ComputeCosSin( radians );
+ return B2_LITERAL( b2Rot ){ cs.cosine, cs.sine };
+}
+
+/// Compute the rotation between two unit vectors
+B2_API b2Rot b2ComputeRotationBetweenUnitVectors( b2Vec2 v1, b2Vec2 v2 );
+
+/// Is this rotation normalized?
+B2_INLINE bool b2IsNormalized( b2Rot q )
+{
+ // larger tolerance due to failure on mingw 32-bit
+ float qq = q.s * q.s + q.c * q.c;
+ return 1.0f - 0.0006f < qq && qq < 1.0f + 0.0006f;
+}
+
+/// Normalized linear interpolation
+/// https://fgiesen.wordpress.com/2012/08/15/linear-interpolation-past-present-and-future/
+/// https://web.archive.org/web/20170825184056/http://number-none.com/product/Understanding%20Slerp,%20Then%20Not%20Using%20It/
+B2_INLINE b2Rot b2NLerp( b2Rot q1, b2Rot q2, float t )
+{
+ float omt = 1.0f - t;
+ b2Rot q = {
+ omt * q1.c + t * q2.c,
+ omt * q1.s + t * q2.s,
+ };
+
+ return b2NormalizeRot( q );
+}
+
+/// Compute the angular velocity necessary to rotate between two rotations over a give time
+/// @param q1 initial rotation
+/// @param q2 final rotation
+/// @param inv_h inverse time step
+B2_INLINE float b2ComputeAngularVelocity( b2Rot q1, b2Rot q2, float inv_h )
+{
+ // ds/dt = omega * cos(t)
+ // dc/dt = -omega * sin(t)
+ // s2 = s1 + omega * h * c1
+ // c2 = c1 - omega * h * s1
+
+ // omega * h * s1 = c1 - c2
+ // omega * h * c1 = s2 - s1
+ // omega * h = (c1 - c2) * s1 + (s2 - s1) * c1;
+ // omega * h = s1 * c1 - c2 * s1 + s2 * c1 - s1 * c1
+ // omega * h = s2 * c1 - c2 * s1 = sin(a2 - a1) ~= a2 - a1 for small delta
+ float omega = inv_h * ( q2.s * q1.c - q2.c * q1.s );
+ return omega;
+}
+
+/// Get the angle in radians in the range [-pi, pi]
+B2_INLINE float b2Rot_GetAngle( b2Rot q )
+{
+ return b2Atan2( q.s, q.c );
+}
+
+/// Get the x-axis
+B2_INLINE b2Vec2 b2Rot_GetXAxis( b2Rot q )
+{
+ b2Vec2 v = { q.c, q.s };
+ return v;
+}
+
+/// Get the y-axis
+B2_INLINE b2Vec2 b2Rot_GetYAxis( b2Rot q )
+{
+ b2Vec2 v = { -q.s, q.c };
+ return v;
+}
+
+/// Multiply two rotations: q * r
+B2_INLINE b2Rot b2MulRot( b2Rot q, b2Rot r )
+{
+ // [qc -qs] * [rc -rs] = [qc*rc-qs*rs -qc*rs-qs*rc]
+ // [qs qc] [rs rc] [qs*rc+qc*rs -qs*rs+qc*rc]
+ // s(q + r) = qs * rc + qc * rs
+ // c(q + r) = qc * rc - qs * rs
+ b2Rot qr;
+ qr.s = q.s * r.c + q.c * r.s;
+ qr.c = q.c * r.c - q.s * r.s;
+ return qr;
+}
+
+/// Transpose multiply two rotations: qT * r
+B2_INLINE b2Rot b2InvMulRot( b2Rot q, b2Rot r )
+{
+ // [ qc qs] * [rc -rs] = [qc*rc+qs*rs -qc*rs+qs*rc]
+ // [-qs qc] [rs rc] [-qs*rc+qc*rs qs*rs+qc*rc]
+ // s(q - r) = qc * rs - qs * rc
+ // c(q - r) = qc * rc + qs * rs
+ b2Rot qr;
+ qr.s = q.c * r.s - q.s * r.c;
+ qr.c = q.c * r.c + q.s * r.s;
+ return qr;
+}
+
+/// relative angle between b and a (rot_b * inv(rot_a))
+B2_INLINE float b2RelativeAngle( b2Rot b, b2Rot a )
+{
+ // sin(b - a) = bs * ac - bc * as
+ // cos(b - a) = bc * ac + bs * as
+ float s = b.s * a.c - b.c * a.s;
+ float c = b.c * a.c + b.s * a.s;
+ return b2Atan2( s, c );
+}
+
+/// Convert an angle in the range [-2*pi, 2*pi] into the range [-pi, pi]
+B2_INLINE float b2UnwindAngle( float radians )
+{
+ if ( radians < -B2_PI )
+ {
+ return radians + 2.0f * B2_PI;
+ }
+ else if ( radians > B2_PI )
+ {
+ return radians - 2.0f * B2_PI;
+ }
+
+ return radians;
+}
+
+/// Convert any into the range [-pi, pi] (slow)
+B2_INLINE float b2UnwindLargeAngle( float radians )
+{
+ while ( radians > B2_PI )
+ {
+ radians -= 2.0f * B2_PI;
+ }
+
+ while ( radians < -B2_PI )
+ {
+ radians += 2.0f * B2_PI;
+ }
+
+ return radians;
+}
+
+/// Rotate a vector
+B2_INLINE b2Vec2 b2RotateVector( b2Rot q, b2Vec2 v )
+{
+ return B2_LITERAL( b2Vec2 ){ q.c * v.x - q.s * v.y, q.s * v.x + q.c * v.y };
+}
+
+/// Inverse rotate a vector
+B2_INLINE b2Vec2 b2InvRotateVector( b2Rot q, b2Vec2 v )
+{
+ return B2_LITERAL( b2Vec2 ){ q.c * v.x + q.s * v.y, -q.s * v.x + q.c * v.y };
+}
+
+/// Transform a point (e.g. local space to world space)
+B2_INLINE b2Vec2 b2TransformPoint( b2Transform t, const b2Vec2 p )
+{
+ float x = ( t.q.c * p.x - t.q.s * p.y ) + t.p.x;
+ float y = ( t.q.s * p.x + t.q.c * p.y ) + t.p.y;
+
+ return B2_LITERAL( b2Vec2 ){ x, y };
+}
+
+/// Inverse transform a point (e.g. world space to local space)
+B2_INLINE b2Vec2 b2InvTransformPoint( b2Transform t, const b2Vec2 p )
+{
+ float vx = p.x - t.p.x;
+ float vy = p.y - t.p.y;
+ return B2_LITERAL( b2Vec2 ){ t.q.c * vx + t.q.s * vy, -t.q.s * vx + t.q.c * vy };
+}
+
+/// Multiply two transforms. If the result is applied to a point p local to frame B,
+/// the transform would first convert p to a point local to frame A, then into a point
+/// in the world frame.
+/// v2 = A.q.Rot(B.q.Rot(v1) + B.p) + A.p
+/// = (A.q * B.q).Rot(v1) + A.q.Rot(B.p) + A.p
+B2_INLINE b2Transform b2MulTransforms( b2Transform A, b2Transform B )
+{
+ b2Transform C;
+ C.q = b2MulRot( A.q, B.q );
+ C.p = b2Add( b2RotateVector( A.q, B.p ), A.p );
+ return C;
+}
+
+/// Creates a transform that converts a local point in frame B to a local point in frame A.
+/// v2 = A.q' * (B.q * v1 + B.p - A.p)
+/// = A.q' * B.q * v1 + A.q' * (B.p - A.p)
+B2_INLINE b2Transform b2InvMulTransforms( b2Transform A, b2Transform B )
+{
+ b2Transform C;
+ C.q = b2InvMulRot( A.q, B.q );
+ C.p = b2InvRotateVector( A.q, b2Sub( B.p, A.p ) );
+ return C;
+}
+
+/// Multiply a 2-by-2 matrix times a 2D vector
+B2_INLINE b2Vec2 b2MulMV( b2Mat22 A, b2Vec2 v )
+{
+ b2Vec2 u = {
+ A.cx.x * v.x + A.cy.x * v.y,
+ A.cx.y * v.x + A.cy.y * v.y,
+ };
+ return u;
+}
+
+/// Get the inverse of a 2-by-2 matrix
+B2_INLINE b2Mat22 b2GetInverse22( b2Mat22 A )
+{
+ float a = A.cx.x, b = A.cy.x, c = A.cx.y, d = A.cy.y;
+ float det = a * d - b * c;
+ if ( det != 0.0f )
+ {
+ det = 1.0f / det;
+ }
+
+ b2Mat22 B = {
+ { det * d, -det * c },
+ { -det * b, det * a },
+ };
+ return B;
+}
+
+/// Solve A * x = b, where b is a column vector. This is more efficient
+/// than computing the inverse in one-shot cases.
+B2_INLINE b2Vec2 b2Solve22( b2Mat22 A, b2Vec2 b )
+{
+ float a11 = A.cx.x, a12 = A.cy.x, a21 = A.cx.y, a22 = A.cy.y;
+ float det = a11 * a22 - a12 * a21;
+ if ( det != 0.0f )
+ {
+ det = 1.0f / det;
+ }
+ b2Vec2 x = { det * ( a22 * b.x - a12 * b.y ), det * ( a11 * b.y - a21 * b.x ) };
+ return x;
+}
+
+/// Does a fully contain b
+B2_INLINE bool b2AABB_Contains( b2AABB a, b2AABB b )
+{
+ bool s = true;
+ s = s && a.lowerBound.x <= b.lowerBound.x;
+ s = s && a.lowerBound.y <= b.lowerBound.y;
+ s = s && b.upperBound.x <= a.upperBound.x;
+ s = s && b.upperBound.y <= a.upperBound.y;
+ return s;
+}
+
+/// Get the center of the AABB.
+B2_INLINE b2Vec2 b2AABB_Center( b2AABB a )
+{
+ b2Vec2 b = { 0.5f * ( a.lowerBound.x + a.upperBound.x ), 0.5f * ( a.lowerBound.y + a.upperBound.y ) };
+ return b;
+}
+
+/// Get the extents of the AABB (half-widths).
+B2_INLINE b2Vec2 b2AABB_Extents( b2AABB a )
+{
+ b2Vec2 b = { 0.5f * ( a.upperBound.x - a.lowerBound.x ), 0.5f * ( a.upperBound.y - a.lowerBound.y ) };
+ return b;
+}
+
+/// Union of two AABBs
+B2_INLINE b2AABB b2AABB_Union( b2AABB a, b2AABB b )
+{
+ b2AABB c;
+ c.lowerBound.x = b2MinFloat( a.lowerBound.x, b.lowerBound.x );
+ c.lowerBound.y = b2MinFloat( a.lowerBound.y, b.lowerBound.y );
+ c.upperBound.x = b2MaxFloat( a.upperBound.x, b.upperBound.x );
+ c.upperBound.y = b2MaxFloat( a.upperBound.y, b.upperBound.y );
+ return c;
+}
+
+/// Is this a valid number? Not NaN or infinity.
+B2_API bool b2IsValidFloat( float a );
+
+/// Is this a valid vector? Not NaN or infinity.
+B2_API bool b2IsValidVec2( b2Vec2 v );
+
+/// Is this a valid rotation? Not NaN or infinity. Is normalized.
+B2_API bool b2IsValidRotation( b2Rot q );
+
+/// Is this a valid bounding box? Not Nan or infinity. Upper bound greater than or equal to lower bound.
+B2_API bool b2IsValidAABB( b2AABB aabb );
+
+/// Box2D bases all length units on meters, but you may need different units for your game.
+/// You can set this value to use different units. This should be done at application startup
+/// and only modified once. Default value is 1.
+/// For example, if your game uses pixels for units you can use pixels for all length values
+/// sent to Box2D. There should be no extra cost. However, Box2D has some internal tolerances
+/// and thresholds that have been tuned for meters. By calling this function, Box2D is able
+/// to adjust those tolerances and thresholds to improve accuracy.
+/// A good rule of thumb is to pass the height of your player character to this function. So
+/// if your player character is 32 pixels high, then pass 32 to this function. Then you may
+/// confidently use pixels for all the length values sent to Box2D. All length values returned
+/// from Box2D will also be pixels because Box2D does not do any scaling internally.
+/// However, you are now on the hook for coming up with good values for gravity, density, and
+/// forces.
+/// @warning This must be modified before any calls to Box2D
+B2_API void b2SetLengthUnitsPerMeter( float lengthUnits );
+
+/// Get the current length units per meter.
+B2_API float b2GetLengthUnitsPerMeter( void );
+
+/**@}*/
+
+/**
+ * @defgroup math_cpp C++ Math
+ * @brief Math operator overloads for C++
+ *
+ * See math_functions.h for details.
+ * @{
+ */
+
+#ifdef __cplusplus
+
+/// Unary add one vector to another
+inline void operator+=( b2Vec2& a, b2Vec2 b )
+{
+ a.x += b.x;
+ a.y += b.y;
+}
+
+/// Unary subtract one vector from another
+inline void operator-=( b2Vec2& a, b2Vec2 b )
+{
+ a.x -= b.x;
+ a.y -= b.y;
+}
+
+/// Unary multiply a vector by a scalar
+inline void operator*=( b2Vec2& a, float b )
+{
+ a.x *= b;
+ a.y *= b;
+}
+
+/// Unary negate a vector
+inline b2Vec2 operator-( b2Vec2 a )
+{
+ return { -a.x, -a.y };
+}
+
+/// Binary vector addition
+inline b2Vec2 operator+( b2Vec2 a, b2Vec2 b )
+{
+ return { a.x + b.x, a.y + b.y };
+}
+
+/// Binary vector subtraction
+inline b2Vec2 operator-( b2Vec2 a, b2Vec2 b )
+{
+ return { a.x - b.x, a.y - b.y };
+}
+
+/// Binary scalar and vector multiplication
+inline b2Vec2 operator*( float a, b2Vec2 b )
+{
+ return { a * b.x, a * b.y };
+}
+
+/// Binary scalar and vector multiplication
+inline b2Vec2 operator*( b2Vec2 a, float b )
+{
+ return { a.x * b, a.y * b };
+}
+
+/// Binary vector equality
+inline bool operator==( b2Vec2 a, b2Vec2 b )
+{
+ return a.x == b.x && a.y == b.y;
+}
+
+/// Binary vector inequality
+inline bool operator!=( b2Vec2 a, b2Vec2 b )
+{
+ return a.x != b.x || a.y != b.y;
+}
+
+#endif
+
+/**@}*/
diff --git a/odin-c-bindgen/examples/box2d/input/types.h b/odin-c-bindgen/examples/box2d/input/types.h
@@ -0,0 +1,1452 @@
+// SPDX-FileCopyrightText: 2023 Erin Catto
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include "base.h"
+#include "collision.h"
+#include "id.h"
+#include "math_functions.h"
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#define B2_DEFAULT_CATEGORY_BITS 0x0001ULL
+#define B2_DEFAULT_MASK_BITS UINT64_MAX
+
+/// Task interface
+/// This is prototype for a Box2D task. Your task system is expected to invoke the Box2D task with these arguments.
+/// The task spans a range of the parallel-for: [startIndex, endIndex)
+/// The worker index must correctly identify each worker in the user thread pool, expected in [0, workerCount).
+/// A worker must only exist on only one thread at a time and is analogous to the thread index.
+/// The task context is the context pointer sent from Box2D when it is enqueued.
+/// The startIndex and endIndex are expected in the range [0, itemCount) where itemCount is the argument to b2EnqueueTaskCallback
+/// below. Box2D expects startIndex < endIndex and will execute a loop like this:
+///
+/// @code{.c}
+/// for (int i = startIndex; i < endIndex; ++i)
+/// {
+/// DoWork();
+/// }
+/// @endcode
+/// @ingroup world
+typedef void b2TaskCallback( int startIndex, int endIndex, uint32_t workerIndex, void* taskContext );
+
+/// These functions can be provided to Box2D to invoke a task system. These are designed to work well with enkiTS.
+/// Returns a pointer to the user's task object. May be nullptr. A nullptr indicates to Box2D that the work was executed
+/// serially within the callback and there is no need to call b2FinishTaskCallback.
+/// The itemCount is the number of Box2D work items that are to be partitioned among workers by the user's task system.
+/// This is essentially a parallel-for. The minRange parameter is a suggestion of the minimum number of items to assign
+/// per worker to reduce overhead. For example, suppose the task is small and that itemCount is 16. A minRange of 8 suggests
+/// that your task system should split the work items among just two workers, even if you have more available.
+/// In general the range [startIndex, endIndex) send to b2TaskCallback should obey:
+/// endIndex - startIndex >= minRange
+/// The exception of course is when itemCount < minRange.
+/// @ingroup world
+typedef void* b2EnqueueTaskCallback( b2TaskCallback* task, int itemCount, int minRange, void* taskContext, void* userContext );
+
+/// Finishes a user task object that wraps a Box2D task.
+/// @ingroup world
+typedef void b2FinishTaskCallback( void* userTask, void* userContext );
+
+/// Optional friction mixing callback. This intentionally provides no context objects because this is called
+/// from a worker thread.
+/// @warning This function should not attempt to modify Box2D state or user application state.
+typedef float b2FrictionCallback( float frictionA, int materialA, float frictionB, int materialB );
+
+/// Optional restitution mixing callback. This intentionally provides no context objects because this is called
+/// from a worker thread.
+/// @warning This function should not attempt to modify Box2D state or user application state.
+typedef float b2RestitutionCallback( float restitutionA, int materialA, float restitutionB, int materialB );
+
+/// Result from b2World_RayCastClosest
+/// @ingroup world
+typedef struct b2RayResult
+{
+ b2ShapeId shapeId;
+ b2Vec2 point;
+ b2Vec2 normal;
+ float fraction;
+ int nodeVisits;
+ int leafVisits;
+ bool hit;
+} b2RayResult;
+
+/// World definition used to create a simulation world.
+/// Must be initialized using b2DefaultWorldDef().
+/// @ingroup world
+typedef struct b2WorldDef
+{
+ /// Gravity vector. Box2D has no up-vector defined.
+ b2Vec2 gravity;
+
+ /// Restitution speed threshold, usually in m/s. Collisions above this
+ /// speed have restitution applied (will bounce).
+ float restitutionThreshold;
+
+ /// Threshold speed for hit events. Usually meters per second.
+ float hitEventThreshold;
+
+ /// Contact stiffness. Cycles per second. Increasing this increases the speed of overlap recovery, but can introduce jitter.
+ float contactHertz;
+
+ /// Contact bounciness. Non-dimensional. You can speed up overlap recovery by decreasing this with
+ /// the trade-off that overlap resolution becomes more energetic.
+ float contactDampingRatio;
+
+ /// This parameter controls how fast overlap is resolved and usually has units of meters per second. This only
+ /// puts a cap on the resolution speed. The resolution speed is increased by increasing the hertz and/or
+ /// decreasing the damping ratio.
+ float contactPushMaxSpeed;
+
+ /// Joint stiffness. Cycles per second.
+ float jointHertz;
+
+ /// Joint bounciness. Non-dimensional.
+ float jointDampingRatio;
+
+ /// Maximum linear speed. Usually meters per second.
+ float maximumLinearSpeed;
+
+ /// Optional mixing callback for friction. The default uses sqrt(frictionA * frictionB).
+ b2FrictionCallback* frictionCallback;
+
+ /// Optional mixing callback for restitution. The default uses max(restitutionA, restitutionB).
+ b2RestitutionCallback* restitutionCallback;
+
+ /// Can bodies go to sleep to improve performance
+ bool enableSleep;
+
+ /// Enable continuous collision
+ bool enableContinuous;
+
+ /// Number of workers to use with the provided task system. Box2D performs best when using only
+ /// performance cores and accessing a single L2 cache. Efficiency cores and hyper-threading provide
+ /// little benefit and may even harm performance.
+ /// @note Box2D does not create threads. This is the number of threads your applications has created
+ /// that you are allocating to b2World_Step.
+ /// @warning Do not modify the default value unless you are also providing a task system and providing
+ /// task callbacks (enqueueTask and finishTask).
+ int workerCount;
+
+ /// Function to spawn tasks
+ b2EnqueueTaskCallback* enqueueTask;
+
+ /// Function to finish a task
+ b2FinishTaskCallback* finishTask;
+
+ /// User context that is provided to enqueueTask and finishTask
+ void* userTaskContext;
+
+ /// User data
+ void* userData;
+
+ /// Used internally to detect a valid definition. DO NOT SET.
+ int internalValue;
+} b2WorldDef;
+
+/// Use this to initialize your world definition
+/// @ingroup world
+B2_API b2WorldDef b2DefaultWorldDef( void );
+
+/// The body simulation type.
+/// Each body is one of these three types. The type determines how the body behaves in the simulation.
+/// @ingroup body
+typedef enum b2BodyType
+{
+ /// zero mass, zero velocity, may be manually moved
+ b2_staticBody = 0,
+
+ /// zero mass, velocity set by user, moved by solver
+ b2_kinematicBody = 1,
+
+ /// positive mass, velocity determined by forces, moved by solver
+ b2_dynamicBody = 2,
+
+ /// number of body types
+ b2_bodyTypeCount,
+} b2BodyType;
+
+/// A body definition holds all the data needed to construct a rigid body.
+/// You can safely re-use body definitions. Shapes are added to a body after construction.
+/// Body definitions are temporary objects used to bundle creation parameters.
+/// Must be initialized using b2DefaultBodyDef().
+/// @ingroup body
+typedef struct b2BodyDef
+{
+ /// The body type: static, kinematic, or dynamic.
+ b2BodyType type;
+
+ /// The initial world position of the body. Bodies should be created with the desired position.
+ /// @note Creating bodies at the origin and then moving them nearly doubles the cost of body creation, especially
+ /// if the body is moved after shapes have been added.
+ b2Vec2 position;
+
+ /// The initial world rotation of the body. Use b2MakeRot() if you have an angle.
+ b2Rot rotation;
+
+ /// The initial linear velocity of the body's origin. Usually in meters per second.
+ b2Vec2 linearVelocity;
+
+ /// The initial angular velocity of the body. Radians per second.
+ float angularVelocity;
+
+ /// Linear damping is used to reduce the linear velocity. The damping parameter
+ /// can be larger than 1 but the damping effect becomes sensitive to the
+ /// time step when the damping parameter is large.
+ /// Generally linear damping is undesirable because it makes objects move slowly
+ /// as if they are floating.
+ float linearDamping;
+
+ /// Angular damping is used to reduce the angular velocity. The damping parameter
+ /// can be larger than 1.0f but the damping effect becomes sensitive to the
+ /// time step when the damping parameter is large.
+ /// Angular damping can be use slow down rotating bodies.
+ float angularDamping;
+
+ /// Scale the gravity applied to this body. Non-dimensional.
+ float gravityScale;
+
+ /// Sleep speed threshold, default is 0.05 meters per second
+ float sleepThreshold;
+
+ /// Optional body name for debugging. Up to 31 characters (excluding null termination)
+ const char* name;
+
+ /// Use this to store application specific body data.
+ void* userData;
+
+ /// Set this flag to false if this body should never fall asleep.
+ bool enableSleep;
+
+ /// Is this body initially awake or sleeping?
+ bool isAwake;
+
+ /// Should this body be prevented from rotating? Useful for characters.
+ bool fixedRotation;
+
+ /// Treat this body as high speed object that performs continuous collision detection
+ /// against dynamic and kinematic bodies, but not other bullet bodies.
+ /// @warning Bullets should be used sparingly. They are not a solution for general dynamic-versus-dynamic
+ /// continuous collision. They may interfere with joint constraints.
+ bool isBullet;
+
+ /// Used to disable a body. A disabled body does not move or collide.
+ bool isEnabled;
+
+ /// This allows this body to bypass rotational speed limits. Should only be used
+ /// for circular objects, like wheels.
+ bool allowFastRotation;
+
+ /// Used internally to detect a valid definition. DO NOT SET.
+ int internalValue;
+} b2BodyDef;
+
+/// Use this to initialize your body definition
+/// @ingroup body
+B2_API b2BodyDef b2DefaultBodyDef( void );
+
+/// This is used to filter collision on shapes. It affects shape-vs-shape collision
+/// and shape-versus-query collision (such as b2World_CastRay).
+/// @ingroup shape
+typedef struct b2Filter
+{
+ /// The collision category bits. Normally you would just set one bit. The category bits should
+ /// represent your application object types. For example:
+ /// @code{.cpp}
+ /// enum MyCategories
+ /// {
+ /// Static = 0x00000001,
+ /// Dynamic = 0x00000002,
+ /// Debris = 0x00000004,
+ /// Player = 0x00000008,
+ /// // etc
+ /// };
+ /// @endcode
+ uint64_t categoryBits;
+
+ /// The collision mask bits. This states the categories that this
+ /// shape would accept for collision.
+ /// For example, you may want your player to only collide with static objects
+ /// and other players.
+ /// @code{.c}
+ /// maskBits = Static | Player;
+ /// @endcode
+ uint64_t maskBits;
+
+ /// Collision groups allow a certain group of objects to never collide (negative)
+ /// or always collide (positive). A group index of zero has no effect. Non-zero group filtering
+ /// always wins against the mask bits.
+ /// For example, you may want ragdolls to collide with other ragdolls but you don't want
+ /// ragdoll self-collision. In this case you would give each ragdoll a unique negative group index
+ /// and apply that group index to all shapes on the ragdoll.
+ int groupIndex;
+} b2Filter;
+
+/// Use this to initialize your filter
+/// @ingroup shape
+B2_API b2Filter b2DefaultFilter( void );
+
+/// The query filter is used to filter collisions between queries and shapes. For example,
+/// you may want a ray-cast representing a projectile to hit players and the static environment
+/// but not debris.
+/// @ingroup shape
+typedef struct b2QueryFilter
+{
+ /// The collision category bits of this query. Normally you would just set one bit.
+ uint64_t categoryBits;
+
+ /// The collision mask bits. This states the shape categories that this
+ /// query would accept for collision.
+ uint64_t maskBits;
+} b2QueryFilter;
+
+/// Use this to initialize your query filter
+/// @ingroup shape
+B2_API b2QueryFilter b2DefaultQueryFilter( void );
+
+/// Shape type
+/// @ingroup shape
+typedef enum b2ShapeType
+{
+ /// A circle with an offset
+ b2_circleShape,
+
+ /// A capsule is an extruded circle
+ b2_capsuleShape,
+
+ /// A line segment
+ b2_segmentShape,
+
+ /// A convex polygon
+ b2_polygonShape,
+
+ /// A line segment owned by a chain shape
+ b2_chainSegmentShape,
+
+ /// The number of shape types
+ b2_shapeTypeCount
+} b2ShapeType;
+
+/// Used to create a shape.
+/// This is a temporary object used to bundle shape creation parameters. You may use
+/// the same shape definition to create multiple shapes.
+/// Must be initialized using b2DefaultShapeDef().
+/// @ingroup shape
+typedef struct b2ShapeDef
+{
+ /// Use this to store application specific shape data.
+ void* userData;
+
+ /// The Coulomb (dry) friction coefficient, usually in the range [0,1].
+ float friction;
+
+ /// The coefficient of restitution (bounce) usually in the range [0,1].
+ /// https://en.wikipedia.org/wiki/Coefficient_of_restitution
+ float restitution;
+
+ /// The rolling resistance usually in the range [0,1].
+ float rollingResistance;
+
+ /// The tangent speed for conveyor belts
+ float tangentSpeed;
+
+ /// User material identifier. This is passed with query results and to friction and restitution
+ /// combining functions. It is not used internally.
+ int material;
+
+ /// The density, usually in kg/m^2.
+ float density;
+
+ /// Collision filtering data.
+ b2Filter filter;
+
+ /// Custom debug draw color.
+ uint32_t customColor;
+
+ /// A sensor shape generates overlap events but never generates a collision response.
+ /// Sensors do not collide with other sensors and do not have continuous collision.
+ /// Instead, use a ray or shape cast for those scenarios.
+ bool isSensor;
+
+ /// Enable contact events for this shape. Only applies to kinematic and dynamic bodies. Ignored for sensors.
+ bool enableContactEvents;
+
+ /// Enable hit events for this shape. Only applies to kinematic and dynamic bodies. Ignored for sensors.
+ bool enableHitEvents;
+
+ /// Enable pre-solve contact events for this shape. Only applies to dynamic bodies. These are expensive
+ /// and must be carefully handled due to threading. Ignored for sensors.
+ bool enablePreSolveEvents;
+
+ /// Normally shapes on static bodies don't invoke contact creation when they are added to the world. This overrides
+ /// that behavior and causes contact creation. This significantly slows down static body creation which can be important
+ /// when there are many static shapes.
+ /// This is implicitly always true for sensors, dynamic bodies, and kinematic bodies.
+ bool invokeContactCreation;
+
+ /// Should the body update the mass properties when this shape is created. Default is true.
+ bool updateBodyMass;
+
+ /// Used internally to detect a valid definition. DO NOT SET.
+ int internalValue;
+} b2ShapeDef;
+
+/// Use this to initialize your shape definition
+/// @ingroup shape
+B2_API b2ShapeDef b2DefaultShapeDef( void );
+
+/// Surface materials allow chain shapes to have per segment surface properties.
+/// @ingroup shape
+typedef struct b2SurfaceMaterial
+{
+ /// The Coulomb (dry) friction coefficient, usually in the range [0,1].
+ float friction;
+
+ /// The coefficient of restitution (bounce) usually in the range [0,1].
+ /// https://en.wikipedia.org/wiki/Coefficient_of_restitution
+ float restitution;
+
+ /// The rolling resistance usually in the range [0,1].
+ float rollingResistance;
+
+ /// The tangent speed for conveyor belts
+ float tangentSpeed;
+
+ /// User material identifier. This is passed with query results and to friction and restitution
+ /// combining functions. It is not used internally.
+ int material;
+
+ /// Custom debug draw color.
+ uint32_t customColor;
+} b2SurfaceMaterial;
+
+/// Use this to initialize your surface material
+/// @ingroup shape
+B2_API b2SurfaceMaterial b2DefaultSurfaceMaterial( void );
+
+/// Used to create a chain of line segments. This is designed to eliminate ghost collisions with some limitations.
+/// - chains are one-sided
+/// - chains have no mass and should be used on static bodies
+/// - chains have a counter-clockwise winding order
+/// - chains are either a loop or open
+/// - a chain must have at least 4 points
+/// - the distance between any two points must be greater than B2_LINEAR_SLOP
+/// - a chain shape should not self intersect (this is not validated)
+/// - an open chain shape has NO COLLISION on the first and final edge
+/// - you may overlap two open chains on their first three and/or last three points to get smooth collision
+/// - a chain shape creates multiple line segment shapes on the body
+/// https://en.wikipedia.org/wiki/Polygonal_chain
+/// Must be initialized using b2DefaultChainDef().
+/// @warning Do not use chain shapes unless you understand the limitations. This is an advanced feature.
+/// @ingroup shape
+typedef struct b2ChainDef
+{
+ /// Use this to store application specific shape data.
+ void* userData;
+
+ /// An array of at least 4 points. These are cloned and may be temporary.
+ const b2Vec2* points;
+
+ /// The point count, must be 4 or more.
+ int count;
+
+ /// Surface materials for each segment. These are cloned.
+ const b2SurfaceMaterial* materials;
+
+ /// The material count. Must be 1 or count. This allows you to provide one
+ /// material for all segments or a unique material per segment.
+ int materialCount;
+
+ /// Contact filtering data.
+ b2Filter filter;
+
+ /// Indicates a closed chain formed by connecting the first and last points
+ bool isLoop;
+
+ /// Used internally to detect a valid definition. DO NOT SET.
+ int internalValue;
+} b2ChainDef;
+
+/// Use this to initialize your chain definition
+/// @ingroup shape
+B2_API b2ChainDef b2DefaultChainDef( void );
+
+//! @cond
+/// Profiling data. Times are in milliseconds.
+typedef struct b2Profile
+{
+ float step;
+ float pairs;
+ float collide;
+ float solve;
+ float mergeIslands;
+ float prepareStages;
+ float solveConstraints;
+ float prepareConstraints;
+ float integrateVelocities;
+ float warmStart;
+ float solveImpulses;
+ float integratePositions;
+ float relaxImpulses;
+ float applyRestitution;
+ float storeImpulses;
+ float splitIslands;
+ float transforms;
+ float hitEvents;
+ float refit;
+ float bullets;
+ float sleepIslands;
+ float sensors;
+} b2Profile;
+
+/// Counters that give details of the simulation size.
+typedef struct b2Counters
+{
+ int bodyCount;
+ int shapeCount;
+ int contactCount;
+ int jointCount;
+ int islandCount;
+ int stackUsed;
+ int staticTreeHeight;
+ int treeHeight;
+ int byteCount;
+ int taskCount;
+ int colorCounts[12];
+} b2Counters;
+//! @endcond
+
+/// Joint type enumeration
+///
+/// This is useful because all joint types use b2JointId and sometimes you
+/// want to get the type of a joint.
+/// @ingroup joint
+typedef enum b2JointType
+{
+ b2_distanceJoint,
+ b2_motorJoint,
+ b2_mouseJoint,
+ b2_nullJoint,
+ b2_prismaticJoint,
+ b2_revoluteJoint,
+ b2_weldJoint,
+ b2_wheelJoint,
+} b2JointType;
+
+/// Distance joint definition
+///
+/// This requires defining an anchor point on both
+/// bodies and the non-zero distance of the distance joint. The definition uses
+/// local anchor points so that the initial configuration can violate the
+/// constraint slightly. This helps when saving and loading a game.
+/// @ingroup distance_joint
+typedef struct b2DistanceJointDef
+{
+ /// The first attached body
+ b2BodyId bodyIdA;
+
+ /// The second attached body
+ b2BodyId bodyIdB;
+
+ /// The local anchor point relative to bodyA's origin
+ b2Vec2 localAnchorA;
+
+ /// The local anchor point relative to bodyB's origin
+ b2Vec2 localAnchorB;
+
+ /// The rest length of this joint. Clamped to a stable minimum value.
+ float length;
+
+ /// Enable the distance constraint to behave like a spring. If false
+ /// then the distance joint will be rigid, overriding the limit and motor.
+ bool enableSpring;
+
+ /// The spring linear stiffness Hertz, cycles per second
+ float hertz;
+
+ /// The spring linear damping ratio, non-dimensional
+ float dampingRatio;
+
+ /// Enable/disable the joint limit
+ bool enableLimit;
+
+ /// Minimum length. Clamped to a stable minimum value.
+ float minLength;
+
+ /// Maximum length. Must be greater than or equal to the minimum length.
+ float maxLength;
+
+ /// Enable/disable the joint motor
+ bool enableMotor;
+
+ /// The maximum motor force, usually in newtons
+ float maxMotorForce;
+
+ /// The desired motor speed, usually in meters per second
+ float motorSpeed;
+
+ /// Set this flag to true if the attached bodies should collide
+ bool collideConnected;
+
+ /// User data pointer
+ void* userData;
+
+ /// Used internally to detect a valid definition. DO NOT SET.
+ int internalValue;
+} b2DistanceJointDef;
+
+/// Use this to initialize your joint definition
+/// @ingroup distance_joint
+B2_API b2DistanceJointDef b2DefaultDistanceJointDef( void );
+
+/// A motor joint is used to control the relative motion between two bodies
+///
+/// A typical usage is to control the movement of a dynamic body with respect to the ground.
+/// @ingroup motor_joint
+typedef struct b2MotorJointDef
+{
+ /// The first attached body
+ b2BodyId bodyIdA;
+
+ /// The second attached body
+ b2BodyId bodyIdB;
+
+ /// Position of bodyB minus the position of bodyA, in bodyA's frame
+ b2Vec2 linearOffset;
+
+ /// The bodyB angle minus bodyA angle in radians
+ float angularOffset;
+
+ /// The maximum motor force in newtons
+ float maxForce;
+
+ /// The maximum motor torque in newton-meters
+ float maxTorque;
+
+ /// Position correction factor in the range [0,1]
+ float correctionFactor;
+
+ /// Set this flag to true if the attached bodies should collide
+ bool collideConnected;
+
+ /// User data pointer
+ void* userData;
+
+ /// Used internally to detect a valid definition. DO NOT SET.
+ int internalValue;
+} b2MotorJointDef;
+
+/// Use this to initialize your joint definition
+/// @ingroup motor_joint
+B2_API b2MotorJointDef b2DefaultMotorJointDef( void );
+
+/// A mouse joint is used to make a point on a body track a specified world point.
+///
+/// This a soft constraint and allows the constraint to stretch without
+/// applying huge forces. This also applies rotation constraint heuristic to improve control.
+/// @ingroup mouse_joint
+typedef struct b2MouseJointDef
+{
+ /// The first attached body. This is assumed to be static.
+ b2BodyId bodyIdA;
+
+ /// The second attached body.
+ b2BodyId bodyIdB;
+
+ /// The initial target point in world space
+ b2Vec2 target;
+
+ /// Stiffness in hertz
+ float hertz;
+
+ /// Damping ratio, non-dimensional
+ float dampingRatio;
+
+ /// Maximum force, typically in newtons
+ float maxForce;
+
+ /// Set this flag to true if the attached bodies should collide.
+ bool collideConnected;
+
+ /// User data pointer
+ void* userData;
+
+ /// Used internally to detect a valid definition. DO NOT SET.
+ int internalValue;
+} b2MouseJointDef;
+
+/// Use this to initialize your joint definition
+/// @ingroup mouse_joint
+B2_API b2MouseJointDef b2DefaultMouseJointDef( void );
+
+/// A null joint is used to disable collision between two specific bodies.
+///
+/// @ingroup null_joint
+typedef struct b2NullJointDef
+{
+ /// The first attached body.
+ b2BodyId bodyIdA;
+
+ /// The second attached body.
+ b2BodyId bodyIdB;
+
+ /// User data pointer
+ void* userData;
+
+ /// Used internally to detect a valid definition. DO NOT SET.
+ int internalValue;
+} b2NullJointDef;
+
+/// Use this to initialize your joint definition
+/// @ingroup null_joint
+B2_API b2NullJointDef b2DefaultNullJointDef( void );
+
+/// Prismatic joint definition
+///
+/// This requires defining a line of motion using an axis and an anchor point.
+/// The definition uses local anchor points and a local axis so that the initial
+/// configuration can violate the constraint slightly. The joint translation is zero
+/// when the local anchor points coincide in world space.
+/// @ingroup prismatic_joint
+typedef struct b2PrismaticJointDef
+{
+ /// The first attached body
+ b2BodyId bodyIdA;
+
+ /// The second attached body
+ b2BodyId bodyIdB;
+
+ /// The local anchor point relative to bodyA's origin
+ b2Vec2 localAnchorA;
+
+ /// The local anchor point relative to bodyB's origin
+ b2Vec2 localAnchorB;
+
+ /// The local translation unit axis in bodyA
+ b2Vec2 localAxisA;
+
+ /// The constrained angle between the bodies: bodyB_angle - bodyA_angle
+ float referenceAngle;
+
+ /// Enable a linear spring along the prismatic joint axis
+ bool enableSpring;
+
+ /// The spring stiffness Hertz, cycles per second
+ float hertz;
+
+ /// The spring damping ratio, non-dimensional
+ float dampingRatio;
+
+ /// Enable/disable the joint limit
+ bool enableLimit;
+
+ /// The lower translation limit
+ float lowerTranslation;
+
+ /// The upper translation limit
+ float upperTranslation;
+
+ /// Enable/disable the joint motor
+ bool enableMotor;
+
+ /// The maximum motor force, typically in newtons
+ float maxMotorForce;
+
+ /// The desired motor speed, typically in meters per second
+ float motorSpeed;
+
+ /// Set this flag to true if the attached bodies should collide
+ bool collideConnected;
+
+ /// User data pointer
+ void* userData;
+
+ /// Used internally to detect a valid definition. DO NOT SET.
+ int internalValue;
+} b2PrismaticJointDef;
+
+/// Use this to initialize your joint definition
+/// @ingroupd prismatic_joint
+B2_API b2PrismaticJointDef b2DefaultPrismaticJointDef( void );
+
+/// Revolute joint definition
+///
+/// This requires defining an anchor point where the bodies are joined.
+/// The definition uses local anchor points so that the
+/// initial configuration can violate the constraint slightly. You also need to
+/// specify the initial relative angle for joint limits. This helps when saving
+/// and loading a game.
+/// The local anchor points are measured from the body's origin
+/// rather than the center of mass because:
+/// 1. you might not know where the center of mass will be
+/// 2. if you add/remove shapes from a body and recompute the mass, the joints will be broken
+/// @ingroup revolute_joint
+typedef struct b2RevoluteJointDef
+{
+ /// The first attached body
+ b2BodyId bodyIdA;
+
+ /// The second attached body
+ b2BodyId bodyIdB;
+
+ /// The local anchor point relative to bodyA's origin
+ b2Vec2 localAnchorA;
+
+ /// The local anchor point relative to bodyB's origin
+ b2Vec2 localAnchorB;
+
+ /// The bodyB angle minus bodyA angle in the reference state (radians).
+ /// This defines the zero angle for the joint limit.
+ float referenceAngle;
+
+ /// Enable a rotational spring on the revolute hinge axis
+ bool enableSpring;
+
+ /// The spring stiffness Hertz, cycles per second
+ float hertz;
+
+ /// The spring damping ratio, non-dimensional
+ float dampingRatio;
+
+ /// A flag to enable joint limits
+ bool enableLimit;
+
+ /// The lower angle for the joint limit in radians
+ float lowerAngle;
+
+ /// The upper angle for the joint limit in radians
+ float upperAngle;
+
+ /// A flag to enable the joint motor
+ bool enableMotor;
+
+ /// The maximum motor torque, typically in newton-meters
+ float maxMotorTorque;
+
+ /// The desired motor speed in radians per second
+ float motorSpeed;
+
+ /// Scale the debug draw
+ float drawSize;
+
+ /// Set this flag to true if the attached bodies should collide
+ bool collideConnected;
+
+ /// User data pointer
+ void* userData;
+
+ /// Used internally to detect a valid definition. DO NOT SET.
+ int internalValue;
+} b2RevoluteJointDef;
+
+/// Use this to initialize your joint definition.
+/// @ingroup revolute_joint
+B2_API b2RevoluteJointDef b2DefaultRevoluteJointDef( void );
+
+/// Weld joint definition
+///
+/// A weld joint connect to bodies together rigidly. This constraint provides springs to mimic
+/// soft-body simulation.
+/// @note The approximate solver in Box2D cannot hold many bodies together rigidly
+/// @ingroup weld_joint
+typedef struct b2WeldJointDef
+{
+ /// The first attached body
+ b2BodyId bodyIdA;
+
+ /// The second attached body
+ b2BodyId bodyIdB;
+
+ /// The local anchor point relative to bodyA's origin
+ b2Vec2 localAnchorA;
+
+ /// The local anchor point relative to bodyB's origin
+ b2Vec2 localAnchorB;
+
+ /// The bodyB angle minus bodyA angle in the reference state (radians)
+ float referenceAngle;
+
+ /// Linear stiffness expressed as Hertz (cycles per second). Use zero for maximum stiffness.
+ float linearHertz;
+
+ /// Angular stiffness as Hertz (cycles per second). Use zero for maximum stiffness.
+ float angularHertz;
+
+ /// Linear damping ratio, non-dimensional. Use 1 for critical damping.
+ float linearDampingRatio;
+
+ /// Linear damping ratio, non-dimensional. Use 1 for critical damping.
+ float angularDampingRatio;
+
+ /// Set this flag to true if the attached bodies should collide
+ bool collideConnected;
+
+ /// User data pointer
+ void* userData;
+
+ /// Used internally to detect a valid definition. DO NOT SET.
+ int internalValue;
+} b2WeldJointDef;
+
+/// Use this to initialize your joint definition
+/// @ingroup weld_joint
+B2_API b2WeldJointDef b2DefaultWeldJointDef( void );
+
+/// Wheel joint definition
+///
+/// This requires defining a line of motion using an axis and an anchor point.
+/// The definition uses local anchor points and a local axis so that the initial
+/// configuration can violate the constraint slightly. The joint translation is zero
+/// when the local anchor points coincide in world space.
+/// @ingroup wheel_joint
+typedef struct b2WheelJointDef
+{
+ /// The first attached body
+ b2BodyId bodyIdA;
+
+ /// The second attached body
+ b2BodyId bodyIdB;
+
+ /// The local anchor point relative to bodyA's origin
+ b2Vec2 localAnchorA;
+
+ /// The local anchor point relative to bodyB's origin
+ b2Vec2 localAnchorB;
+
+ /// The local translation unit axis in bodyA
+ b2Vec2 localAxisA;
+
+ /// Enable a linear spring along the local axis
+ bool enableSpring;
+
+ /// Spring stiffness in Hertz
+ float hertz;
+
+ /// Spring damping ratio, non-dimensional
+ float dampingRatio;
+
+ /// Enable/disable the joint linear limit
+ bool enableLimit;
+
+ /// The lower translation limit
+ float lowerTranslation;
+
+ /// The upper translation limit
+ float upperTranslation;
+
+ /// Enable/disable the joint rotational motor
+ bool enableMotor;
+
+ /// The maximum motor torque, typically in newton-meters
+ float maxMotorTorque;
+
+ /// The desired motor speed in radians per second
+ float motorSpeed;
+
+ /// Set this flag to true if the attached bodies should collide
+ bool collideConnected;
+
+ /// User data pointer
+ void* userData;
+
+ /// Used internally to detect a valid definition. DO NOT SET.
+ int internalValue;
+} b2WheelJointDef;
+
+/// Use this to initialize your joint definition
+/// @ingroup wheel_joint
+B2_API b2WheelJointDef b2DefaultWheelJointDef( void );
+
+/// The explosion definition is used to configure options for explosions. Explosions
+/// consider shape geometry when computing the impulse.
+/// @ingroup world
+typedef struct b2ExplosionDef
+{
+ /// Mask bits to filter shapes
+ uint64_t maskBits;
+
+ /// The center of the explosion in world space
+ b2Vec2 position;
+
+ /// The radius of the explosion
+ float radius;
+
+ /// The falloff distance beyond the radius. Impulse is reduced to zero at this distance.
+ float falloff;
+
+ /// Impulse per unit length. This applies an impulse according to the shape perimeter that
+ /// is facing the explosion. Explosions only apply to circles, capsules, and polygons. This
+ /// may be negative for implosions.
+ float impulsePerLength;
+} b2ExplosionDef;
+
+/// Use this to initialize your explosion definition
+/// @ingroup world
+B2_API b2ExplosionDef b2DefaultExplosionDef( void );
+
+/**
+ * @defgroup events Events
+ * World event types.
+ *
+ * Events are used to collect events that occur during the world time step. These events
+ * are then available to query after the time step is complete. This is preferable to callbacks
+ * because Box2D uses multithreaded simulation.
+ *
+ * Also when events occur in the simulation step it may be problematic to modify the world, which is
+ * often what applications want to do when events occur.
+ *
+ * With event arrays, you can scan the events in a loop and modify the world. However, you need to be careful
+ * that some event data may become invalid. There are several samples that show how to do this safely.
+ *
+ * @{
+ */
+
+/// A begin touch event is generated when a shape starts to overlap a sensor shape.
+typedef struct b2SensorBeginTouchEvent
+{
+ /// The id of the sensor shape
+ b2ShapeId sensorShapeId;
+
+ /// The id of the dynamic shape that began touching the sensor shape
+ b2ShapeId visitorShapeId;
+} b2SensorBeginTouchEvent;
+
+/// An end touch event is generated when a shape stops overlapping a sensor shape.
+/// These include things like setting the transform, destroying a body or shape, or changing
+/// a filter. You will also get an end event if the sensor or visitor are destroyed.
+/// Therefore you should always confirm the shape id is valid using b2Shape_IsValid.
+typedef struct b2SensorEndTouchEvent
+{
+ /// The id of the sensor shape
+ /// @warning this shape may have been destroyed
+ /// @see b2Shape_IsValid
+ b2ShapeId sensorShapeId;
+
+ /// The id of the dynamic shape that stopped touching the sensor shape
+ /// @warning this shape may have been destroyed
+ /// @see b2Shape_IsValid
+ b2ShapeId visitorShapeId;
+
+} b2SensorEndTouchEvent;
+
+/// Sensor events are buffered in the Box2D world and are available
+/// as begin/end overlap event arrays after the time step is complete.
+/// Note: these may become invalid if bodies and/or shapes are destroyed
+typedef struct b2SensorEvents
+{
+ /// Array of sensor begin touch events
+ b2SensorBeginTouchEvent* beginEvents;
+
+ /// Array of sensor end touch events
+ b2SensorEndTouchEvent* endEvents;
+
+ /// The number of begin touch events
+ int beginCount;
+
+ /// The number of end touch events
+ int endCount;
+} b2SensorEvents;
+
+/// A begin touch event is generated when two shapes begin touching.
+typedef struct b2ContactBeginTouchEvent
+{
+ /// Id of the first shape
+ b2ShapeId shapeIdA;
+
+ /// Id of the second shape
+ b2ShapeId shapeIdB;
+
+ /// The initial contact manifold. This is recorded before the solver is called,
+ /// so all the impulses will be zero.
+ b2Manifold manifold;
+} b2ContactBeginTouchEvent;
+
+/// An end touch event is generated when two shapes stop touching.
+/// You will get an end event if you do anything that destroys contacts previous to the last
+/// world step. These include things like setting the transform, destroying a body
+/// or shape, or changing a filter or body type.
+typedef struct b2ContactEndTouchEvent
+{
+ /// Id of the first shape
+ /// @warning this shape may have been destroyed
+ /// @see b2Shape_IsValid
+ b2ShapeId shapeIdA;
+
+ /// Id of the second shape
+ /// @warning this shape may have been destroyed
+ /// @see b2Shape_IsValid
+ b2ShapeId shapeIdB;
+} b2ContactEndTouchEvent;
+
+/// A hit touch event is generated when two shapes collide with a speed faster than the hit speed threshold.
+typedef struct b2ContactHitEvent
+{
+ /// Id of the first shape
+ b2ShapeId shapeIdA;
+
+ /// Id of the second shape
+ b2ShapeId shapeIdB;
+
+ /// Point where the shapes hit
+ b2Vec2 point;
+
+ /// Normal vector pointing from shape A to shape B
+ b2Vec2 normal;
+
+ /// The speed the shapes are approaching. Always positive. Typically in meters per second.
+ float approachSpeed;
+} b2ContactHitEvent;
+
+/// Contact events are buffered in the Box2D world and are available
+/// as event arrays after the time step is complete.
+/// Note: these may become invalid if bodies and/or shapes are destroyed
+typedef struct b2ContactEvents
+{
+ /// Array of begin touch events
+ b2ContactBeginTouchEvent* beginEvents;
+
+ /// Array of end touch events
+ b2ContactEndTouchEvent* endEvents;
+
+ /// Array of hit events
+ b2ContactHitEvent* hitEvents;
+
+ /// Number of begin touch events
+ int beginCount;
+
+ /// Number of end touch events
+ int endCount;
+
+ /// Number of hit events
+ int hitCount;
+} b2ContactEvents;
+
+/// Body move events triggered when a body moves.
+/// Triggered when a body moves due to simulation. Not reported for bodies moved by the user.
+/// This also has a flag to indicate that the body went to sleep so the application can also
+/// sleep that actor/entity/object associated with the body.
+/// On the other hand if the flag does not indicate the body went to sleep then the application
+/// can treat the actor/entity/object associated with the body as awake.
+/// This is an efficient way for an application to update game object transforms rather than
+/// calling functions such as b2Body_GetTransform() because this data is delivered as a contiguous array
+/// and it is only populated with bodies that have moved.
+/// @note If sleeping is disabled all dynamic and kinematic bodies will trigger move events.
+typedef struct b2BodyMoveEvent
+{
+ b2Transform transform;
+ b2BodyId bodyId;
+ void* userData;
+ bool fellAsleep;
+} b2BodyMoveEvent;
+
+/// Body events are buffered in the Box2D world and are available
+/// as event arrays after the time step is complete.
+/// Note: this data becomes invalid if bodies are destroyed
+typedef struct b2BodyEvents
+{
+ /// Array of move events
+ b2BodyMoveEvent* moveEvents;
+
+ /// Number of move events
+ int moveCount;
+} b2BodyEvents;
+
+/// The contact data for two shapes. By convention the manifold normal points
+/// from shape A to shape B.
+/// @see b2Shape_GetContactData() and b2Body_GetContactData()
+typedef struct b2ContactData
+{
+ b2ShapeId shapeIdA;
+ b2ShapeId shapeIdB;
+ b2Manifold manifold;
+} b2ContactData;
+
+/**@}*/
+
+/// Prototype for a contact filter callback.
+/// This is called when a contact pair is considered for collision. This allows you to
+/// perform custom logic to prevent collision between shapes. This is only called if
+/// one of the two shapes has custom filtering enabled.
+/// Notes:
+/// - this function must be thread-safe
+/// - this is only called if one of the two shapes has enabled custom filtering
+/// - this is called only for awake dynamic bodies
+/// Return false if you want to disable the collision
+/// @see b2ShapeDef
+/// @warning Do not attempt to modify the world inside this callback
+/// @ingroup world
+typedef bool b2CustomFilterFcn( b2ShapeId shapeIdA, b2ShapeId shapeIdB, void* context );
+
+/// Prototype for a pre-solve callback.
+/// This is called after a contact is updated. This allows you to inspect a
+/// contact before it goes to the solver. If you are careful, you can modify the
+/// contact manifold (e.g. modify the normal).
+/// Notes:
+/// - this function must be thread-safe
+/// - this is only called if the shape has enabled pre-solve events
+/// - this is called only for awake dynamic bodies
+/// - this is not called for sensors
+/// - the supplied manifold has impulse values from the previous step
+/// Return false if you want to disable the contact this step
+/// @warning Do not attempt to modify the world inside this callback
+/// @ingroup world
+typedef bool b2PreSolveFcn( b2ShapeId shapeIdA, b2ShapeId shapeIdB, b2Manifold* manifold, void* context );
+
+/// Prototype callback for overlap queries.
+/// Called for each shape found in the query.
+/// @see b2World_OverlapABB
+/// @return false to terminate the query.
+/// @ingroup world
+typedef bool b2OverlapResultFcn( b2ShapeId shapeId, void* context );
+
+/// Prototype callback for ray casts.
+/// Called for each shape found in the query. You control how the ray cast
+/// proceeds by returning a float:
+/// return -1: ignore this shape and continue
+/// return 0: terminate the ray cast
+/// return fraction: clip the ray to this point
+/// return 1: don't clip the ray and continue
+/// @param shapeId the shape hit by the ray
+/// @param point the point of initial intersection
+/// @param normal the normal vector at the point of intersection
+/// @param fraction the fraction along the ray at the point of intersection
+/// @param context the user context
+/// @return -1 to filter, 0 to terminate, fraction to clip the ray for closest hit, 1 to continue
+/// @see b2World_CastRay
+/// @ingroup world
+typedef float b2CastResultFcn( b2ShapeId shapeId, b2Vec2 point, b2Vec2 normal, float fraction, void* context );
+
+/// These colors are used for debug draw and mostly match the named SVG colors.
+/// See https://www.rapidtables.com/web/color/index.html
+/// https://johndecember.com/html/spec/colorsvg.html
+/// https://upload.wikimedia.org/wikipedia/commons/2/2b/SVG_Recognized_color_keyword_names.svg
+typedef enum b2HexColor
+{
+ b2_colorAliceBlue = 0xF0F8FF,
+ b2_colorAntiqueWhite = 0xFAEBD7,
+ b2_colorAqua = 0x00FFFF,
+ b2_colorAquamarine = 0x7FFFD4,
+ b2_colorAzure = 0xF0FFFF,
+ b2_colorBeige = 0xF5F5DC,
+ b2_colorBisque = 0xFFE4C4,
+ b2_colorBlack = 0x000000,
+ b2_colorBlanchedAlmond = 0xFFEBCD,
+ b2_colorBlue = 0x0000FF,
+ b2_colorBlueViolet = 0x8A2BE2,
+ b2_colorBrown = 0xA52A2A,
+ b2_colorBurlywood = 0xDEB887,
+ b2_colorCadetBlue = 0x5F9EA0,
+ b2_colorChartreuse = 0x7FFF00,
+ b2_colorChocolate = 0xD2691E,
+ b2_colorCoral = 0xFF7F50,
+ b2_colorCornflowerBlue = 0x6495ED,
+ b2_colorCornsilk = 0xFFF8DC,
+ b2_colorCrimson = 0xDC143C,
+ b2_colorCyan = 0x00FFFF,
+ b2_colorDarkBlue = 0x00008B,
+ b2_colorDarkCyan = 0x008B8B,
+ b2_colorDarkGoldenRod = 0xB8860B,
+ b2_colorDarkGray = 0xA9A9A9,
+ b2_colorDarkGreen = 0x006400,
+ b2_colorDarkKhaki = 0xBDB76B,
+ b2_colorDarkMagenta = 0x8B008B,
+ b2_colorDarkOliveGreen = 0x556B2F,
+ b2_colorDarkOrange = 0xFF8C00,
+ b2_colorDarkOrchid = 0x9932CC,
+ b2_colorDarkRed = 0x8B0000,
+ b2_colorDarkSalmon = 0xE9967A,
+ b2_colorDarkSeaGreen = 0x8FBC8F,
+ b2_colorDarkSlateBlue = 0x483D8B,
+ b2_colorDarkSlateGray = 0x2F4F4F,
+ b2_colorDarkTurquoise = 0x00CED1,
+ b2_colorDarkViolet = 0x9400D3,
+ b2_colorDeepPink = 0xFF1493,
+ b2_colorDeepSkyBlue = 0x00BFFF,
+ b2_colorDimGray = 0x696969,
+ b2_colorDodgerBlue = 0x1E90FF,
+ b2_colorFireBrick = 0xB22222,
+ b2_colorFloralWhite = 0xFFFAF0,
+ b2_colorForestGreen = 0x228B22,
+ b2_colorFuchsia = 0xFF00FF,
+ b2_colorGainsboro = 0xDCDCDC,
+ b2_colorGhostWhite = 0xF8F8FF,
+ b2_colorGold = 0xFFD700,
+ b2_colorGoldenRod = 0xDAA520,
+ b2_colorGray = 0x808080,
+ b2_colorGreen = 0x008000,
+ b2_colorGreenYellow = 0xADFF2F,
+ b2_colorHoneyDew = 0xF0FFF0,
+ b2_colorHotPink = 0xFF69B4,
+ b2_colorIndianRed = 0xCD5C5C,
+ b2_colorIndigo = 0x4B0082,
+ b2_colorIvory = 0xFFFFF0,
+ b2_colorKhaki = 0xF0E68C,
+ b2_colorLavender = 0xE6E6FA,
+ b2_colorLavenderBlush = 0xFFF0F5,
+ b2_colorLawnGreen = 0x7CFC00,
+ b2_colorLemonChiffon = 0xFFFACD,
+ b2_colorLightBlue = 0xADD8E6,
+ b2_colorLightCoral = 0xF08080,
+ b2_colorLightCyan = 0xE0FFFF,
+ b2_colorLightGoldenRodYellow = 0xFAFAD2,
+ b2_colorLightGray = 0xD3D3D3,
+ b2_colorLightGreen = 0x90EE90,
+ b2_colorLightPink = 0xFFB6C1,
+ b2_colorLightSalmon = 0xFFA07A,
+ b2_colorLightSeaGreen = 0x20B2AA,
+ b2_colorLightSkyBlue = 0x87CEFA,
+ b2_colorLightSlateGray = 0x778899,
+ b2_colorLightSteelBlue = 0xB0C4DE,
+ b2_colorLightYellow = 0xFFFFE0,
+ b2_colorLime = 0x00FF00,
+ b2_colorLimeGreen = 0x32CD32,
+ b2_colorLinen = 0xFAF0E6,
+ b2_colorMagenta = 0xFF00FF,
+ b2_colorMaroon = 0x800000,
+ b2_colorMediumAquaMarine = 0x66CDAA,
+ b2_colorMediumBlue = 0x0000CD,
+ b2_colorMediumOrchid = 0xBA55D3,
+ b2_colorMediumPurple = 0x9370DB,
+ b2_colorMediumSeaGreen = 0x3CB371,
+ b2_colorMediumSlateBlue = 0x7B68EE,
+ b2_colorMediumSpringGreen = 0x00FA9A,
+ b2_colorMediumTurquoise = 0x48D1CC,
+ b2_colorMediumVioletRed = 0xC71585,
+ b2_colorMidnightBlue = 0x191970,
+ b2_colorMintCream = 0xF5FFFA,
+ b2_colorMistyRose = 0xFFE4E1,
+ b2_colorMoccasin = 0xFFE4B5,
+ b2_colorNavajoWhite = 0xFFDEAD,
+ b2_colorNavy = 0x000080,
+ b2_colorOldLace = 0xFDF5E6,
+ b2_colorOlive = 0x808000,
+ b2_colorOliveDrab = 0x6B8E23,
+ b2_colorOrange = 0xFFA500,
+ b2_colorOrangeRed = 0xFF4500,
+ b2_colorOrchid = 0xDA70D6,
+ b2_colorPaleGoldenRod = 0xEEE8AA,
+ b2_colorPaleGreen = 0x98FB98,
+ b2_colorPaleTurquoise = 0xAFEEEE,
+ b2_colorPaleVioletRed = 0xDB7093,
+ b2_colorPapayaWhip = 0xFFEFD5,
+ b2_colorPeachPuff = 0xFFDAB9,
+ b2_colorPeru = 0xCD853F,
+ b2_colorPink = 0xFFC0CB,
+ b2_colorPlum = 0xDDA0DD,
+ b2_colorPowderBlue = 0xB0E0E6,
+ b2_colorPurple = 0x800080,
+ b2_colorRebeccaPurple = 0x663399,
+ b2_colorRed = 0xFF0000,
+ b2_colorRosyBrown = 0xBC8F8F,
+ b2_colorRoyalBlue = 0x4169E1,
+ b2_colorSaddleBrown = 0x8B4513,
+ b2_colorSalmon = 0xFA8072,
+ b2_colorSandyBrown = 0xF4A460,
+ b2_colorSeaGreen = 0x2E8B57,
+ b2_colorSeaShell = 0xFFF5EE,
+ b2_colorSienna = 0xA0522D,
+ b2_colorSilver = 0xC0C0C0,
+ b2_colorSkyBlue = 0x87CEEB,
+ b2_colorSlateBlue = 0x6A5ACD,
+ b2_colorSlateGray = 0x708090,
+ b2_colorSnow = 0xFFFAFA,
+ b2_colorSpringGreen = 0x00FF7F,
+ b2_colorSteelBlue = 0x4682B4,
+ b2_colorTan = 0xD2B48C,
+ b2_colorTeal = 0x008080,
+ b2_colorThistle = 0xD8BFD8,
+ b2_colorTomato = 0xFF6347,
+ b2_colorTurquoise = 0x40E0D0,
+ b2_colorViolet = 0xEE82EE,
+ b2_colorWheat = 0xF5DEB3,
+ b2_colorWhite = 0xFFFFFF,
+ b2_colorWhiteSmoke = 0xF5F5F5,
+ b2_colorYellow = 0xFFFF00,
+ b2_colorYellowGreen = 0x9ACD32,
+
+ b2_colorBox2DRed = 0xDC3132,
+ b2_colorBox2DBlue = 0x30AEBF,
+ b2_colorBox2DGreen = 0x8CC924,
+ b2_colorBox2DYellow = 0xFFEE8C
+} b2HexColor;
+
+/// This struct holds callbacks you can implement to draw a Box2D world.
+/// This structure should be zero initialized.
+/// @ingroup world
+typedef struct b2DebugDraw
+{
+ /// Draw a closed polygon provided in CCW order.
+ void ( *DrawPolygon )( const b2Vec2* vertices, int vertexCount, b2HexColor color, void* context );
+
+ /// Draw a solid closed polygon provided in CCW order.
+ void ( *DrawSolidPolygon )( b2Transform transform, const b2Vec2* vertices, int vertexCount, float radius, b2HexColor color,
+ void* context );
+
+ /// Draw a circle.
+ void ( *DrawCircle )( b2Vec2 center, float radius, b2HexColor color, void* context );
+
+ /// Draw a solid circle.
+ void ( *DrawSolidCircle )( b2Transform transform, float radius, b2HexColor color, void* context );
+
+ /// Draw a solid capsule.
+ void ( *DrawSolidCapsule )( b2Vec2 p1, b2Vec2 p2, float radius, b2HexColor color, void* context );
+
+ /// Draw a line segment.
+ void ( *DrawSegment )( b2Vec2 p1, b2Vec2 p2, b2HexColor color, void* context );
+
+ /// Draw a transform. Choose your own length scale.
+ void ( *DrawTransform )( b2Transform transform, void* context );
+
+ /// Draw a point.
+ void ( *DrawPoint )( b2Vec2 p, float size, b2HexColor color, void* context );
+
+ /// Draw a string in world space
+ void ( *DrawString )( b2Vec2 p, const char* s, b2HexColor color, void* context );
+
+ /// Bounds to use if restricting drawing to a rectangular region
+ b2AABB drawingBounds;
+
+ /// Option to restrict drawing to a rectangular region. May suffer from unstable depth sorting.
+ bool useDrawingBounds;
+
+ /// Option to draw shapes
+ bool drawShapes;
+
+ /// Option to draw joints
+ bool drawJoints;
+
+ /// Option to draw additional information for joints
+ bool drawJointExtras;
+
+ /// Option to draw the bounding boxes for shapes
+ bool drawAABBs;
+
+ /// Option to draw the mass and center of mass of dynamic bodies
+ bool drawMass;
+
+ /// Option to draw body names
+ bool drawBodyNames;
+
+ /// Option to draw contact points
+ bool drawContacts;
+
+ /// Option to visualize the graph coloring used for contacts and joints
+ bool drawGraphColors;
+
+ /// Option to draw contact normals
+ bool drawContactNormals;
+
+ /// Option to draw contact normal impulses
+ bool drawContactImpulses;
+
+ /// Option to draw contact friction impulses
+ bool drawFrictionImpulses;
+
+ /// User context that is passed as an argument to drawing callback functions
+ void* context;
+} b2DebugDraw;
+
+/// Use this to initialize your drawing interface. This allows you to implement a sub-set
+/// of the drawing functions.
+B2_API b2DebugDraw b2DefaultDebugDraw( void );
diff --git a/odin-c-bindgen/examples/box2d/test/main.odin b/odin-c-bindgen/examples/box2d/test/main.odin
@@ -0,0 +1,112 @@
+// Odin + Box2D + Raylib example with stacking boxes and a shape attached to the cursor that can smack the shapes.
+// Made (mostly) during this stream: https://www.youtube.com/watch?v=LYW7jdwEnaI
+
+// I have updated this to use the `vendor:box2d` bindings instead of the ones I used on the stream.
+
+package game
+
+import b2 "../box2d"
+import rl "vendor:raylib"
+import "core:math"
+
+create_box :: proc(world_id: b2.WorldId, pos: b2.Vec2) -> b2.BodyId{
+ body_def := b2.DefaultBodyDef()
+ body_def.type = .dynamicBody
+ body_def.position = pos
+ body_id := b2.CreateBody(world_id, body_def)
+
+ shape_def := b2.DefaultShapeDef()
+ shape_def.density = 1
+ shape_def.friction = 0.3
+
+ box := b2.MakeBox(20, 20)
+ box_def := b2.DefaultShapeDef()
+ _ = b2.CreatePolygonShape(body_id, box_def, box)
+
+ return body_id
+}
+
+main :: proc() {
+ rl.InitWindow(1280, 720, "Box2D + Raylib example")
+
+ world_def := b2.DefaultWorldDef()
+ world_def.gravity = b2.Vec2{0, -1}
+ world_id := b2.CreateWorld(world_def)
+ defer b2.DestroyWorld(world_id)
+
+ ground := rl.Rectangle {
+ 0, 600,
+ 1280, 120,
+ }
+
+ ground_body_def := b2.DefaultBodyDef()
+ ground_body_def.position = b2.Vec2{ground.x, -ground.y-ground.height}
+ ground_body_id := b2.CreateBody(world_id, ground_body_def)
+
+ ground_box := b2.MakeBox(ground.width, ground.height)
+ ground_shape_def := b2.DefaultShapeDef()
+ _ = b2.CreatePolygonShape(ground_body_id, ground_shape_def, ground_box)
+
+ bodies: [dynamic]b2.BodyId
+
+ px: f32 = 400
+ py: f32 = -400
+
+ num_per_row := 10
+ num_in_row := 0
+
+ for _ in 0..<50 {
+ b := create_box(world_id, {px, py})
+ append(&bodies, b)
+ num_in_row += 1
+
+ if num_in_row == num_per_row {
+ py += 30
+ px = 200
+ num_per_row -= 1
+ num_in_row = 0
+ }
+
+ px += 30
+ }
+
+ body_def := b2.DefaultBodyDef()
+ body_def.type = .dynamicBody
+ body_def.position = b2.Vec2{0, 4}
+ body_id := b2.CreateBody(world_id, body_def)
+
+ shape_def := b2.DefaultShapeDef()
+ shape_def.density = 1000
+ shape_def.friction = 0.3
+
+ circle: b2.Circle
+ circle.radius = 40
+ _ = b2.CreateCircleShape(body_id, shape_def, circle)
+
+ time_step: f32 = 1.0 / 60
+ sub_steps: i32 = 4
+
+ for !rl.WindowShouldClose() {
+ rl.BeginDrawing()
+ rl.ClearBackground(rl.BLACK)
+
+ rl.DrawRectangleRec(ground, rl.RED)
+ mouse_pos := rl.GetMousePosition()
+
+ b2.Body_SetTransform(body_id, {mouse_pos.x, -mouse_pos.y}, {})
+ b2.World_Step(world_id, time_step, sub_steps)
+
+ for b in bodies {
+ position := b2.Body_GetPosition(b)
+ r := b2.Body_GetRotation(b)
+ a := math.atan2(r.s, r._c)
+ // Y position is flipped because raylib has Y down and box2d has Y up.
+ rl.DrawRectanglePro({position.x, -position.y, 40, 40}, {20, 20}, a*(180/3.14), rl.YELLOW)
+ }
+
+ rl.DrawCircleV(mouse_pos, 40, rl.MAGENTA)
+ rl.EndDrawing()
+ }
+
+ rl.CloseWindow()
+}
+\ No newline at end of file
diff --git a/odin-c-bindgen/examples/pdfio/.gitignore b/odin-c-bindgen/examples/pdfio/.gitignore
@@ -0,0 +1,3 @@
+pdfio/*.lib
+pdfio/*.dll
+test/*.dll
+\ No newline at end of file
diff --git a/odin-c-bindgen/examples/pdfio/bindgen.sjson b/odin-c-bindgen/examples/pdfio/bindgen.sjson
@@ -0,0 +1,34 @@
+// See README.md in root of repository for documentation and more configuration options.
+
+inputs = [
+ "input"
+]
+
+output_folder = "pdfio"
+remove_type_prefix = "pdfio_"
+remove_function_prefix = "pdfio"
+required_prefix = "pdfio"
+import_lib = "pdfio1.lib"
+package_name = "pdfio"
+
+// "Pre_Existing_Enum_Type" = "New_Bit_Set_Type"
+bit_setify = {
+ "pdfio_permission_e" = "permission_t"
+}
+
+procedure_type_overrides = {
+ "pdfioArrayAppendBinary.value" = "[^]"
+ "pdfioArrayGetBinary" = "[^]"
+ "pdfioFileCreateTemporary.buffer" = "[^]c.char"
+ "pdfioStreamGetToken.buffer" = "[^]c.char"
+
+ // This is not a complete override list, it's just an example.
+}
+
+opaque_types = [
+ "pdfio_dict_t"
+ "pdfio_file_t"
+ "pdfio_array_t"
+ "pdfio_stream_t"
+ "pdfio_obj_t"
+]
diff --git a/odin-c-bindgen/examples/pdfio/input/pdfio-content.h b/odin-c-bindgen/examples/pdfio/input/pdfio-content.h
@@ -0,0 +1,150 @@
+//
+// Public content header file for PDFio.
+//
+// Copyright © 2021-2023 by Michael R Sweet.
+//
+// Licensed under Apache License v2.0. See the file "LICENSE" for more
+// information.
+//
+
+#ifndef PDFIO_CONTENT_H
+# define PDFIO_CONTENT_H
+# include "pdfio.h"
+# ifdef __cplusplus
+extern "C" {
+# endif // __cplusplus
+
+
+//
+// Types and constants...
+//
+
+typedef enum pdfio_cs_e // Standard color spaces
+{
+ PDFIO_CS_ADOBE, // AdobeRGB 1998
+ PDFIO_CS_P3_D65, // Display P3
+ PDFIO_CS_SRGB // sRGB
+} pdfio_cs_t;
+
+typedef enum pdfio_linecap_e // Line capping modes
+{
+ PDFIO_LINECAP_BUTT, // Butt ends
+ PDFIO_LINECAP_ROUND, // Round ends
+ PDFIO_LINECAP_SQUARE // Square ends
+} pdfio_linecap_t;
+
+typedef enum pdfio_linejoin_e // Line joining modes
+{
+ PDFIO_LINEJOIN_MITER, // Miter joint
+ PDFIO_LINEJOIN_ROUND, // Round joint
+ PDFIO_LINEJOIN_BEVEL // Bevel joint
+} pdfio_linejoin_t;
+
+typedef double pdfio_matrix_t[3][2]; // Transform matrix
+
+typedef enum pdfio_textrendering_e // Text rendering modes
+{
+ PDFIO_TEXTRENDERING_FILL, // Fill text
+ PDFIO_TEXTRENDERING_STROKE, // Stroke text
+ PDFIO_TEXTRENDERING_FILL_AND_STROKE, // Fill then stroke text
+ PDFIO_TEXTRENDERING_INVISIBLE, // Don't fill or stroke (invisible)
+ PDFIO_TEXTRENDERING_FILL_PATH, // Fill text and add to path
+ PDFIO_TEXTRENDERING_STROKE_PATH, // Stroke text and add to path
+ PDFIO_TEXTRENDERING_FILL_AND_STROKE_PATH,
+ // Fill then stroke text and add to path
+ PDFIO_TEXTRENDERING_TEXT_PATH // Add text to path (invisible)
+} pdfio_textrendering_t;
+
+
+//
+// Functions...
+//
+
+// Color array functions...
+extern pdfio_array_t *pdfioArrayCreateColorFromICCObj(pdfio_file_t *pdf, pdfio_obj_t *icc_object) _PDFIO_PUBLIC;
+extern pdfio_array_t *pdfioArrayCreateColorFromMatrix(pdfio_file_t *pdf, size_t num_colors, double gamma, const double matrix[3][3], const double white_point[3]) _PDFIO_PUBLIC;
+extern pdfio_array_t *pdfioArrayCreateColorFromPalette(pdfio_file_t *pdf, size_t num_colors, const unsigned char *colors) _PDFIO_PUBLIC;
+extern pdfio_array_t *pdfioArrayCreateColorFromPrimaries(pdfio_file_t *pdf, size_t num_colors, double gamma, double wx, double wy, double rx, double ry, double gx, double gy, double bx, double by) _PDFIO_PUBLIC;
+extern pdfio_array_t *pdfioArrayCreateColorFromStandard(pdfio_file_t *pdf, size_t num_colors, pdfio_cs_t cs);
+
+// PDF content drawing functions...
+extern bool pdfioContentClip(pdfio_stream_t *st, bool even_odd) _PDFIO_PUBLIC;
+extern bool pdfioContentDrawImage(pdfio_stream_t *st, const char *name, double x, double y, double w, double h) _PDFIO_PUBLIC;
+extern bool pdfioContentFill(pdfio_stream_t *st, bool even_odd) _PDFIO_PUBLIC;
+extern bool pdfioContentFillAndStroke(pdfio_stream_t *st, bool even_odd) _PDFIO_PUBLIC;
+extern bool pdfioContentMatrixConcat(pdfio_stream_t *st, pdfio_matrix_t m) _PDFIO_PUBLIC;
+extern bool pdfioContentMatrixRotate(pdfio_stream_t *st, double degrees) _PDFIO_PUBLIC;
+extern bool pdfioContentMatrixScale(pdfio_stream_t *st, double sx, double sy) _PDFIO_PUBLIC;
+extern bool pdfioContentMatrixTranslate(pdfio_stream_t *st, double tx, double ty) _PDFIO_PUBLIC;
+extern bool pdfioContentPathClose(pdfio_stream_t *st) _PDFIO_PUBLIC;
+extern bool pdfioContentPathCurve(pdfio_stream_t *st, double x1, double y1, double x2, double y2, double x3, double y3) _PDFIO_PUBLIC;
+extern bool pdfioContentPathCurve13(pdfio_stream_t *st, double x1, double y1, double x3, double y3) _PDFIO_PUBLIC;
+extern bool pdfioContentPathCurve23(pdfio_stream_t *st, double x2, double y2, double x3, double y3) _PDFIO_PUBLIC;
+extern bool pdfioContentPathEnd(pdfio_stream_t *st) _PDFIO_PUBLIC;
+extern bool pdfioContentPathLineTo(pdfio_stream_t *st, double x, double y) _PDFIO_PUBLIC;
+extern bool pdfioContentPathMoveTo(pdfio_stream_t *st, double x, double y) _PDFIO_PUBLIC;
+extern bool pdfioContentPathRect(pdfio_stream_t *st, double x, double y, double width, double height) _PDFIO_PUBLIC;
+extern bool pdfioContentRestore(pdfio_stream_t *st) _PDFIO_PUBLIC;
+extern bool pdfioContentSave(pdfio_stream_t *st) _PDFIO_PUBLIC;
+extern bool pdfioContentSetDashPattern(pdfio_stream_t *st, double phase, double on, double off) _PDFIO_PUBLIC;
+extern bool pdfioContentSetFillColorDeviceCMYK(pdfio_stream_t *st, double c, double m, double y, double k) _PDFIO_PUBLIC;
+extern bool pdfioContentSetFillColorDeviceGray(pdfio_stream_t *st, double g) _PDFIO_PUBLIC;
+extern bool pdfioContentSetFillColorDeviceRGB(pdfio_stream_t *st, double r, double g, double b) _PDFIO_PUBLIC;
+extern bool pdfioContentSetFillColorGray(pdfio_stream_t *st, double g) _PDFIO_PUBLIC;
+extern bool pdfioContentSetFillColorRGB(pdfio_stream_t *st, double r, double g, double b) _PDFIO_PUBLIC;
+extern bool pdfioContentSetFillColorSpace(pdfio_stream_t *st, const char *name) _PDFIO_PUBLIC;
+extern bool pdfioContentSetFlatness(pdfio_stream_t *st, double f) _PDFIO_PUBLIC;
+extern bool pdfioContentSetLineCap(pdfio_stream_t *st, pdfio_linecap_t lc) _PDFIO_PUBLIC;
+extern bool pdfioContentSetLineJoin(pdfio_stream_t *st, pdfio_linejoin_t lj) _PDFIO_PUBLIC;
+extern bool pdfioContentSetLineWidth(pdfio_stream_t *st, double width) _PDFIO_PUBLIC;
+extern bool pdfioContentSetMiterLimit(pdfio_stream_t *st, double limit) _PDFIO_PUBLIC;
+extern bool pdfioContentSetStrokeColorDeviceCMYK(pdfio_stream_t *st, double c, double m, double y, double k) _PDFIO_PUBLIC;
+extern bool pdfioContentSetStrokeColorDeviceGray(pdfio_stream_t *st, double g) _PDFIO_PUBLIC;
+extern bool pdfioContentSetStrokeColorDeviceRGB(pdfio_stream_t *st, double r, double g, double b) _PDFIO_PUBLIC;
+extern bool pdfioContentSetStrokeColorGray(pdfio_stream_t *st, double g) _PDFIO_PUBLIC;
+extern bool pdfioContentSetStrokeColorRGB(pdfio_stream_t *st, double r, double g, double b) _PDFIO_PUBLIC;
+extern bool pdfioContentSetStrokeColorSpace(pdfio_stream_t *st, const char *name) _PDFIO_PUBLIC;
+extern bool pdfioContentSetTextCharacterSpacing(pdfio_stream_t *st, double spacing) _PDFIO_PUBLIC;
+extern bool pdfioContentSetTextFont(pdfio_stream_t *st, const char *name, double size) _PDFIO_PUBLIC;
+extern bool pdfioContentSetTextLeading(pdfio_stream_t *st, double leading) _PDFIO_PUBLIC;
+extern bool pdfioContentSetTextMatrix(pdfio_stream_t *st, pdfio_matrix_t m) _PDFIO_PUBLIC;
+extern bool pdfioContentSetTextRenderingMode(pdfio_stream_t *st, pdfio_textrendering_t mode) _PDFIO_PUBLIC;
+extern bool pdfioContentSetTextRise(pdfio_stream_t *st, double rise) _PDFIO_PUBLIC;
+extern bool pdfioContentSetTextWordSpacing(pdfio_stream_t *st, double spacing) _PDFIO_PUBLIC;
+extern bool pdfioContentSetTextXScaling(pdfio_stream_t *st, double percent) _PDFIO_PUBLIC;
+extern bool pdfioContentStroke(pdfio_stream_t *st) _PDFIO_PUBLIC;
+extern bool pdfioContentTextBegin(pdfio_stream_t *st) _PDFIO_PUBLIC;
+extern bool pdfioContentTextEnd(pdfio_stream_t *st) _PDFIO_PUBLIC;
+extern double pdfioContentTextMeasure(pdfio_obj_t *font, const char *s, double size) _PDFIO_PUBLIC;
+extern bool pdfioContentTextMoveLine(pdfio_stream_t *st, double tx, double ty) _PDFIO_PUBLIC;
+extern bool pdfioContentTextMoveTo(pdfio_stream_t *st, double tx, double ty) _PDFIO_PUBLIC;
+extern bool pdfioContentTextNewLine(pdfio_stream_t *st) _PDFIO_PUBLIC;
+extern bool pdfioContentTextNewLineShow(pdfio_stream_t *st, double ws, double cs, bool unicode, const char *s) _PDFIO_PUBLIC;
+extern bool pdfioContentTextNewLineShowf(pdfio_stream_t *st, double ws, double cs, bool unicode, const char *format, ...) _PDFIO_PUBLIC _PDFIO_FORMAT(5,6);
+extern bool pdfioContentTextNextLine(pdfio_stream_t *st) _PDFIO_PUBLIC;
+extern bool pdfioContentTextShow(pdfio_stream_t *st, bool unicode, const char *s) _PDFIO_PUBLIC;
+extern bool pdfioContentTextShowf(pdfio_stream_t *st, bool unicode, const char *format, ...) _PDFIO_PUBLIC _PDFIO_FORMAT(3,4);
+extern bool pdfioContentTextShowJustified(pdfio_stream_t *st, bool unicode, size_t num_fragments, const double *offsets, const char * const *fragments) _PDFIO_PUBLIC;
+
+// Resource helpers...
+extern pdfio_obj_t *pdfioFileCreateFontObjFromBase(pdfio_file_t *pdf, const char *name) _PDFIO_PUBLIC;
+extern pdfio_obj_t *pdfioFileCreateFontObjFromFile(pdfio_file_t *pdf, const char *filename, bool unicode) _PDFIO_PUBLIC;
+extern pdfio_obj_t *pdfioFileCreateICCObjFromFile(pdfio_file_t *pdf, const char *filename, size_t num_colors) _PDFIO_PUBLIC;
+extern pdfio_obj_t *pdfioFileCreateImageObjFromData(pdfio_file_t *pdf, const unsigned char *data, size_t width, size_t height, size_t num_colors, pdfio_array_t *color_data, bool alpha, bool interpolate) _PDFIO_PUBLIC;
+extern pdfio_obj_t *pdfioFileCreateImageObjFromFile(pdfio_file_t *pdf, const char *filename, bool interpolate) _PDFIO_PUBLIC;
+
+// Image object helpers...
+extern size_t pdfioImageGetBytesPerLine(pdfio_obj_t *obj) _PDFIO_PUBLIC;
+extern double pdfioImageGetHeight(pdfio_obj_t *obj) _PDFIO_PUBLIC;
+extern double pdfioImageGetWidth(pdfio_obj_t *obj) _PDFIO_PUBLIC;
+
+// Page dictionary helpers...
+extern bool pdfioPageDictAddColorSpace(pdfio_dict_t *dict, const char *name, pdfio_array_t *data) _PDFIO_PUBLIC;
+extern bool pdfioPageDictAddFont(pdfio_dict_t *dict, const char *name, pdfio_obj_t *obj) _PDFIO_PUBLIC;
+extern bool pdfioPageDictAddImage(pdfio_dict_t *dict, const char *name, pdfio_obj_t *obj) _PDFIO_PUBLIC;
+
+
+# ifdef __cplusplus
+}
+# endif // __cplusplus
+#endif // !PDFIO_CONTENT_H
diff --git a/odin-c-bindgen/examples/pdfio/input/pdfio.h b/odin-c-bindgen/examples/pdfio/input/pdfio.h
@@ -0,0 +1,257 @@
+//
+// Public header file for PDFio.
+//
+// Copyright © 2021-2025 by Michael R Sweet.
+//
+// Licensed under Apache License v2.0. See the file "LICENSE" for more
+// information.
+//
+
+#ifndef PDFIO_H
+# define PDFIO_H
+# include <stdio.h>
+# include <stdlib.h>
+# include <stdbool.h>
+# include <sys/types.h>
+# include <time.h>
+# ifdef __cplusplus
+extern "C" {
+# endif // __cplusplus
+
+
+//
+// Version number...
+//
+
+# define PDFIO_VERSION "1.4.1"
+
+
+//
+// Visibility and other annotations...
+//
+
+# if defined(__has_extension) || defined(__GNUC__)
+# define _PDFIO_PUBLIC __attribute__ ((visibility("default")))
+# define _PDFIO_FORMAT(a,b) __attribute__ ((__format__(__printf__, a,b)))
+# define _PDFIO_DEPRECATED __attribute__ ((deprecated)) _PDFIO_PUBLIC
+# else
+# define _PDFIO_PUBLIC
+# define _PDFIO_FORMAT(a,b)
+# define _PDFIO_DEPRECATED
+# endif // __has_extension || __GNUC__
+
+
+//
+// Types and constants...
+//
+
+# if _WIN32
+typedef __int64 ssize_t; // POSIX type not present on Windows... @private@
+# endif // _WIN32
+
+typedef struct _pdfio_array_s pdfio_array_t;
+ // Array of PDF values
+typedef struct _pdfio_dict_s pdfio_dict_t;
+ // Key/value dictionary
+typedef bool (*pdfio_dict_cb_t)(pdfio_dict_t *dict, const char *key, void *cb_data);
+ // Dictionary iterator callback
+typedef struct _pdfio_file_s pdfio_file_t;
+ // PDF file
+typedef bool (*pdfio_error_cb_t)(pdfio_file_t *pdf, const char *message, void *data);
+ // Error callback
+typedef enum pdfio_encryption_e // PDF encryption modes
+{
+ PDFIO_ENCRYPTION_NONE = 0, // No encryption
+ PDFIO_ENCRYPTION_RC4_40, // 40-bit RC4 encryption (PDF 1.3)
+ PDFIO_ENCRYPTION_RC4_128, // 128-bit RC4 encryption (PDF 1.4)
+ PDFIO_ENCRYPTION_AES_128, // 128-bit AES encryption (PDF 1.6)
+ PDFIO_ENCRYPTION_AES_256 // 256-bit AES encryption (PDF 2.0) @exclude all@
+} pdfio_encryption_t;
+typedef enum pdfio_filter_e // Compression/decompression filters for streams
+{
+ PDFIO_FILTER_NONE, // No filter
+ PDFIO_FILTER_ASCIIHEX, // ASCIIHexDecode filter (reading only)
+ PDFIO_FILTER_ASCII85, // ASCII85Decode filter (reading only)
+ PDFIO_FILTER_CCITTFAX, // CCITTFaxDecode filter
+ PDFIO_FILTER_CRYPT, // Encryption filter
+ PDFIO_FILTER_DCT, // DCTDecode (JPEG) filter
+ PDFIO_FILTER_FLATE, // FlateDecode filter
+ PDFIO_FILTER_JBIG2, // JBIG2Decode filter
+ PDFIO_FILTER_JPX, // JPXDecode filter (reading only)
+ PDFIO_FILTER_LZW, // LZWDecode filter (reading only)
+ PDFIO_FILTER_RUNLENGTH, // RunLengthDecode filter (reading only)
+} pdfio_filter_t;
+typedef struct _pdfio_obj_s pdfio_obj_t;// Numbered object in PDF file
+typedef ssize_t (*pdfio_output_cb_t)(void *ctx, const void *data, size_t datalen);
+ // Output callback for pdfioFileCreateOutput
+typedef const char *(*pdfio_password_cb_t)(void *data, const char *filename);
+ // Password callback for pdfioFileOpen
+enum pdfio_permission_e // PDF permission bits
+{
+ PDFIO_PERMISSION_NONE = 0, // No permissions
+ PDFIO_PERMISSION_PRINT = 0x0004, // PDF allows printing
+ PDFIO_PERMISSION_MODIFY = 0x0008, // PDF allows modification
+ PDFIO_PERMISSION_COPY = 0x0010, // PDF allows copying
+ PDFIO_PERMISSION_ANNOTATE = 0x0020, // PDF allows annotation
+ PDFIO_PERMISSION_FORMS = 0x0100, // PDF allows filling in forms
+ PDFIO_PERMISSION_READING = 0x0200, // PDF allows screen reading/accessibility (deprecated in PDF 2.0)
+ PDFIO_PERMISSION_ASSEMBLE = 0x0400, // PDF allows assembly (insert, delete, or rotate pages, add document outlines and thumbnails)
+ PDFIO_PERMISSION_PRINT_HIGH = 0x0800, // PDF allows high quality printing
+ PDFIO_PERMISSION_ALL = ~0 // All permissions
+};
+typedef int pdfio_permission_t; // PDF permission bitfield
+typedef struct pdfio_rect_s // PDF rectangle
+{
+ double x1; // Lower-left X coordinate
+ double y1; // Lower-left Y coordinate
+ double x2; // Upper-right X coordinate
+ double y2; // Upper-right Y coordinate
+} pdfio_rect_t;
+typedef struct _pdfio_stream_s pdfio_stream_t;
+ // Object data stream in PDF file
+typedef enum pdfio_valtype_e // PDF value types
+{
+ PDFIO_VALTYPE_NONE, // No value, not set
+ PDFIO_VALTYPE_ARRAY, // Array
+ PDFIO_VALTYPE_BINARY, // Binary data
+ PDFIO_VALTYPE_BOOLEAN, // Boolean
+ PDFIO_VALTYPE_DATE, // Date/time
+ PDFIO_VALTYPE_DICT, // Dictionary
+ PDFIO_VALTYPE_INDIRECT, // Indirect object (N G obj)
+ PDFIO_VALTYPE_NAME, // Name
+ PDFIO_VALTYPE_NULL, // Null object
+ PDFIO_VALTYPE_NUMBER, // Number (integer or real)
+ PDFIO_VALTYPE_STRING // String
+} pdfio_valtype_t;
+
+
+//
+// Functions...
+//
+
+extern bool pdfioArrayAppendArray(pdfio_array_t *a, pdfio_array_t *value) _PDFIO_PUBLIC;
+extern bool pdfioArrayAppendBinary(pdfio_array_t *a, const unsigned char *value, size_t valuelen) _PDFIO_PUBLIC;
+extern bool pdfioArrayAppendBoolean(pdfio_array_t *a, bool value) _PDFIO_PUBLIC;
+extern bool pdfioArrayAppendDate(pdfio_array_t *a, time_t value) _PDFIO_PUBLIC;
+extern bool pdfioArrayAppendDict(pdfio_array_t *a, pdfio_dict_t *value) _PDFIO_PUBLIC;
+extern bool pdfioArrayAppendName(pdfio_array_t *a, const char *value) _PDFIO_PUBLIC;
+extern bool pdfioArrayAppendNumber(pdfio_array_t *a, double value) _PDFIO_PUBLIC;
+extern bool pdfioArrayAppendObj(pdfio_array_t *a, pdfio_obj_t *value) _PDFIO_PUBLIC;
+extern bool pdfioArrayAppendString(pdfio_array_t *a, const char *value) _PDFIO_PUBLIC;
+extern pdfio_array_t *pdfioArrayCopy(pdfio_file_t *pdf, pdfio_array_t *a) _PDFIO_PUBLIC;
+extern pdfio_array_t *pdfioArrayCreate(pdfio_file_t *pdf) _PDFIO_PUBLIC;
+extern pdfio_array_t *pdfioArrayGetArray(pdfio_array_t *a, size_t n) _PDFIO_PUBLIC;
+extern unsigned char *pdfioArrayGetBinary(pdfio_array_t *a, size_t n, size_t *length) _PDFIO_PUBLIC;
+extern bool pdfioArrayGetBoolean(pdfio_array_t *a, size_t n) _PDFIO_PUBLIC;
+extern time_t pdfioArrayGetDate(pdfio_array_t *a, size_t n) _PDFIO_PUBLIC;
+extern pdfio_dict_t *pdfioArrayGetDict(pdfio_array_t *a, size_t n) _PDFIO_PUBLIC;
+extern const char *pdfioArrayGetName(pdfio_array_t *a, size_t n) _PDFIO_PUBLIC;
+extern double pdfioArrayGetNumber(pdfio_array_t *a, size_t n) _PDFIO_PUBLIC;
+extern pdfio_obj_t *pdfioArrayGetObj(pdfio_array_t *a, size_t n) _PDFIO_PUBLIC;
+extern size_t pdfioArrayGetSize(pdfio_array_t *a) _PDFIO_PUBLIC;
+extern const char *pdfioArrayGetString(pdfio_array_t *a, size_t n) _PDFIO_PUBLIC;
+extern pdfio_valtype_t pdfioArrayGetType(pdfio_array_t *a, size_t n) _PDFIO_PUBLIC;
+extern bool pdfioArrayRemove(pdfio_array_t *a, size_t n) _PDFIO_PUBLIC;
+
+extern bool pdfioDictClear(pdfio_dict_t *dict, const char *key) _PDFIO_PUBLIC;
+extern pdfio_dict_t *pdfioDictCopy(pdfio_file_t *pdf, pdfio_dict_t *dict) _PDFIO_PUBLIC;
+extern pdfio_dict_t *pdfioDictCreate(pdfio_file_t *pdf) _PDFIO_PUBLIC;
+extern pdfio_array_t *pdfioDictGetArray(pdfio_dict_t *dict, const char *key) _PDFIO_PUBLIC;
+extern unsigned char *pdfioDictGetBinary(pdfio_dict_t *dict, const char *key, size_t *length) _PDFIO_PUBLIC;
+extern bool pdfioDictGetBoolean(pdfio_dict_t *dict, const char *key) _PDFIO_PUBLIC;
+extern time_t pdfioDictGetDate(pdfio_dict_t *dict, const char *key) _PDFIO_PUBLIC;
+extern pdfio_dict_t *pdfioDictGetDict(pdfio_dict_t *dict, const char *key) _PDFIO_PUBLIC;
+extern const char *pdfioDictGetKey(pdfio_dict_t *dict, size_t n) _PDFIO_PUBLIC;
+extern const char *pdfioDictGetName(pdfio_dict_t *dict, const char *key) _PDFIO_PUBLIC;
+extern size_t pdfioDictGetNumPairs(pdfio_dict_t *dict) _PDFIO_PUBLIC;
+extern double pdfioDictGetNumber(pdfio_dict_t *dict, const char *key) _PDFIO_PUBLIC;
+extern pdfio_obj_t *pdfioDictGetObj(pdfio_dict_t *dict, const char *key) _PDFIO_PUBLIC;
+extern pdfio_rect_t *pdfioDictGetRect(pdfio_dict_t *dict, const char *key, pdfio_rect_t *rect) _PDFIO_PUBLIC;
+extern const char *pdfioDictGetString(pdfio_dict_t *dict, const char *key) _PDFIO_PUBLIC;
+extern pdfio_valtype_t pdfioDictGetType(pdfio_dict_t *dict, const char *key) _PDFIO_PUBLIC;
+extern void pdfioDictIterateKeys(pdfio_dict_t *dict, pdfio_dict_cb_t cb, void *cb_data) _PDFIO_PUBLIC;
+extern bool pdfioDictSetArray(pdfio_dict_t *dict, const char *key, pdfio_array_t *value) _PDFIO_PUBLIC;
+extern bool pdfioDictSetBinary(pdfio_dict_t *dict, const char *key, const unsigned char *value, size_t valuelen) _PDFIO_PUBLIC;
+extern bool pdfioDictSetBoolean(pdfio_dict_t *dict, const char *key, bool value) _PDFIO_PUBLIC;
+extern bool pdfioDictSetDate(pdfio_dict_t *dict, const char *key, time_t value) _PDFIO_PUBLIC;
+extern bool pdfioDictSetDict(pdfio_dict_t *dict, const char *key, pdfio_dict_t *value) _PDFIO_PUBLIC;
+extern bool pdfioDictSetName(pdfio_dict_t *dict, const char *key, const char *value) _PDFIO_PUBLIC;
+extern bool pdfioDictSetNull(pdfio_dict_t *dict, const char *key) _PDFIO_PUBLIC;
+extern bool pdfioDictSetNumber(pdfio_dict_t *dict, const char *key, double value) _PDFIO_PUBLIC;
+extern bool pdfioDictSetObj(pdfio_dict_t *dict, const char *key, pdfio_obj_t *value) _PDFIO_PUBLIC;
+extern bool pdfioDictSetRect(pdfio_dict_t *dict, const char *key, pdfio_rect_t *value) _PDFIO_PUBLIC;
+extern bool pdfioDictSetString(pdfio_dict_t *dict, const char *key, const char *value) _PDFIO_PUBLIC;
+extern bool pdfioDictSetStringf(pdfio_dict_t *dict, const char *key, const char *format, ...) _PDFIO_PUBLIC _PDFIO_FORMAT(3,4);
+
+extern bool pdfioFileClose(pdfio_file_t *pdf) _PDFIO_PUBLIC;
+extern pdfio_file_t *pdfioFileCreate(const char *filename, const char *version, pdfio_rect_t *media_box, pdfio_rect_t *crop_box, pdfio_error_cb_t error_cb, void *error_data) _PDFIO_PUBLIC;
+extern pdfio_obj_t *pdfioFileCreateArrayObj(pdfio_file_t *pdf, pdfio_array_t *array) _PDFIO_PUBLIC;
+extern pdfio_obj_t *pdfioFileCreateNameObj(pdfio_file_t *pdf, const char *name) _PDFIO_PUBLIC;
+extern pdfio_obj_t *pdfioFileCreateNumberObj(pdfio_file_t *pdf, double number) _PDFIO_PUBLIC;
+extern pdfio_obj_t *pdfioFileCreateObj(pdfio_file_t *pdf, pdfio_dict_t *dict) _PDFIO_PUBLIC;
+extern pdfio_file_t *pdfioFileCreateOutput(pdfio_output_cb_t output_cb, void *output_ctx, const char *version, pdfio_rect_t *media_box, pdfio_rect_t *crop_box, pdfio_error_cb_t error_cb, void *error_data) _PDFIO_PUBLIC;
+// TODO: Add number, array, string, etc. versions of pdfioFileCreateObject?
+extern pdfio_stream_t *pdfioFileCreatePage(pdfio_file_t *pdf, pdfio_dict_t *dict) _PDFIO_PUBLIC;
+extern pdfio_obj_t *pdfioFileCreateStringObj(pdfio_file_t *pdf, const char *s) _PDFIO_PUBLIC;
+extern pdfio_file_t *pdfioFileCreateTemporary(char *buffer, size_t bufsize, const char *version, pdfio_rect_t *media_box, pdfio_rect_t *crop_box, pdfio_error_cb_t error_cb, void *error_data) _PDFIO_PUBLIC;
+extern pdfio_obj_t *pdfioFileFindObj(pdfio_file_t *pdf, size_t number) _PDFIO_PUBLIC;
+extern const char *pdfioFileGetAuthor(pdfio_file_t *pdf) _PDFIO_PUBLIC;
+extern pdfio_dict_t *pdfioFileGetCatalog(pdfio_file_t *pdf) _PDFIO_PUBLIC;
+extern time_t pdfioFileGetCreationDate(pdfio_file_t *pdf) _PDFIO_PUBLIC;
+extern const char *pdfioFileGetCreator(pdfio_file_t *pdf) _PDFIO_PUBLIC;
+extern pdfio_array_t *pdfioFileGetID(pdfio_file_t *pdf) _PDFIO_PUBLIC;
+extern const char *pdfioFileGetKeywords(pdfio_file_t *pdf) _PDFIO_PUBLIC;
+extern const char *pdfioFileGetName(pdfio_file_t *pdf) _PDFIO_PUBLIC;
+extern size_t pdfioFileGetNumObjs(pdfio_file_t *pdf) _PDFIO_PUBLIC;
+extern size_t pdfioFileGetNumPages(pdfio_file_t *pdf) _PDFIO_PUBLIC;
+extern pdfio_obj_t *pdfioFileGetObj(pdfio_file_t *pdf, size_t n) _PDFIO_PUBLIC;
+extern pdfio_obj_t *pdfioFileGetPage(pdfio_file_t *pdf, size_t n) _PDFIO_PUBLIC;
+extern pdfio_permission_t pdfioFileGetPermissions(pdfio_file_t *pdf, pdfio_encryption_t *encryption) _PDFIO_PUBLIC;
+extern const char *pdfioFileGetProducer(pdfio_file_t *pdf) _PDFIO_PUBLIC;
+extern const char *pdfioFileGetSubject(pdfio_file_t *pdf) _PDFIO_PUBLIC;
+extern const char *pdfioFileGetTitle(pdfio_file_t *pdf) _PDFIO_PUBLIC;
+extern const char *pdfioFileGetVersion(pdfio_file_t *pdf) _PDFIO_PUBLIC;
+extern pdfio_file_t *pdfioFileOpen(const char *filename, pdfio_password_cb_t password_cb, void *password_data, pdfio_error_cb_t error_cb, void *error_data) _PDFIO_PUBLIC;
+extern void pdfioFileSetAuthor(pdfio_file_t *pdf, const char *value) _PDFIO_PUBLIC;
+extern void pdfioFileSetCreationDate(pdfio_file_t *pdf, time_t value) _PDFIO_PUBLIC;
+extern void pdfioFileSetCreator(pdfio_file_t *pdf, const char *value) _PDFIO_PUBLIC;
+extern void pdfioFileSetKeywords(pdfio_file_t *pdf, const char *value) _PDFIO_PUBLIC;
+extern bool pdfioFileSetPermissions(pdfio_file_t *pdf, pdfio_permission_t permissions, pdfio_encryption_t encryption, const char *owner_password, const char *user_password) _PDFIO_PUBLIC;
+extern void pdfioFileSetSubject(pdfio_file_t *pdf, const char *value) _PDFIO_PUBLIC;
+extern void pdfioFileSetTitle(pdfio_file_t *pdf, const char *value) _PDFIO_PUBLIC;
+
+extern bool pdfioObjClose(pdfio_obj_t *obj) _PDFIO_PUBLIC;
+extern pdfio_obj_t *pdfioObjCopy(pdfio_file_t *pdf, pdfio_obj_t *srcobj) _PDFIO_PUBLIC;
+extern pdfio_stream_t *pdfioObjCreateStream(pdfio_obj_t *obj, pdfio_filter_t compression) _PDFIO_PUBLIC;
+extern pdfio_array_t *pdfioObjGetArray(pdfio_obj_t *obj) _PDFIO_PUBLIC;
+extern pdfio_dict_t *pdfioObjGetDict(pdfio_obj_t *obj) _PDFIO_PUBLIC;
+extern unsigned short pdfioObjGetGeneration(pdfio_obj_t *obj) _PDFIO_PUBLIC;
+extern size_t pdfioObjGetLength(pdfio_obj_t *obj) _PDFIO_PUBLIC;
+extern const char *pdfioObjGetName(pdfio_obj_t *obj) _PDFIO_PUBLIC;
+extern size_t pdfioObjGetNumber(pdfio_obj_t *obj) _PDFIO_PUBLIC;
+extern const char *pdfioObjGetSubtype(pdfio_obj_t *obj) _PDFIO_PUBLIC;
+extern const char *pdfioObjGetType(pdfio_obj_t *obj) _PDFIO_PUBLIC;
+extern pdfio_stream_t *pdfioObjOpenStream(pdfio_obj_t *obj, bool decode) _PDFIO_PUBLIC;
+
+extern bool pdfioPageCopy(pdfio_file_t *pdf, pdfio_obj_t *srcpage) _PDFIO_PUBLIC;
+extern size_t pdfioPageGetNumStreams(pdfio_obj_t *page) _PDFIO_PUBLIC;
+extern pdfio_stream_t *pdfioPageOpenStream(pdfio_obj_t *page, size_t n, bool decode) _PDFIO_PUBLIC;
+
+extern bool pdfioStreamClose(pdfio_stream_t *st) _PDFIO_PUBLIC;
+extern bool pdfioStreamConsume(pdfio_stream_t *st, size_t bytes) _PDFIO_PUBLIC;
+extern bool pdfioStreamGetToken(pdfio_stream_t *st, char *buffer, size_t bufsize) _PDFIO_PUBLIC;
+extern ssize_t pdfioStreamPeek(pdfio_stream_t *st, void *buffer, size_t bytes) _PDFIO_PUBLIC;
+extern bool pdfioStreamPrintf(pdfio_stream_t *st, const char *format, ...) _PDFIO_PUBLIC _PDFIO_FORMAT(2,3);
+extern bool pdfioStreamPutChar(pdfio_stream_t *st, int ch) _PDFIO_PUBLIC;
+extern bool pdfioStreamPuts(pdfio_stream_t *st, const char *s) _PDFIO_PUBLIC;
+extern ssize_t pdfioStreamRead(pdfio_stream_t *st, void *buffer, size_t bytes) _PDFIO_PUBLIC;
+extern bool pdfioStreamWrite(pdfio_stream_t *st, const void *buffer, size_t bytes) _PDFIO_PUBLIC;
+
+extern char *pdfioStringCreate(pdfio_file_t *pdf, const char *s) _PDFIO_PUBLIC;
+extern char *pdfioStringCreatef(pdfio_file_t *pdf, const char *format, ...) _PDFIO_FORMAT(2,3) _PDFIO_PUBLIC;
+
+
+# ifdef __cplusplus
+}
+# endif // __cplusplus
+#endif // !PDFIO_H
diff --git a/odin-c-bindgen/examples/pdfio/input/pdfio1.dll b/odin-c-bindgen/examples/pdfio/input/pdfio1.dll
Binary files differ.
diff --git a/odin-c-bindgen/examples/pdfio/input/pdfio1.lib b/odin-c-bindgen/examples/pdfio/input/pdfio1.lib
Binary files differ.
diff --git a/odin-c-bindgen/examples/pdfio/input/zlib.dll b/odin-c-bindgen/examples/pdfio/input/zlib.dll
Binary files differ.
diff --git a/odin-c-bindgen/examples/pdfio/pdfio/pdfio-content.odin b/odin-c-bindgen/examples/pdfio/pdfio/pdfio-content.odin
@@ -0,0 +1,133 @@
+//
+// Public content header file for PDFio.
+//
+// Copyright © 2021-2023 by Michael R Sweet.
+//
+// Licensed under Apache License v2.0. See the file "LICENSE" for more
+// information.
+//
+package pdfio
+
+import "core:c"
+
+_ :: c
+
+foreign import lib "pdfio1.lib"
+
+// Types and constants...
+cs_t :: enum c.int {
+ ADOBE, // AdobeRGB 1998
+ P3_D65, // Display P3
+ SRGB, // sRGB
+}
+
+linecap_t :: enum c.int {
+ BUTT, // Butt ends
+ ROUND, // Round ends
+ SQUARE, // Square ends
+}
+
+linejoin_t :: enum c.int {
+ MITER, // Miter joint
+ ROUND, // Round joint
+ BEVEL, // Bevel joint
+}
+
+matrix_t :: [3][2]f64 // Transform matrix
+
+textrendering_t :: enum c.int {
+ FILL, // Fill text
+ STROKE, // Stroke text
+ FILL_AND_STROKE, // Fill then stroke text
+ INVISIBLE, // Don't fill or stroke (invisible)
+ FILL_PATH, // Fill text and add to path
+ STROKE_PATH, // Stroke text and add to path
+ FILL_AND_STROKE_PATH,
+ TEXT_PATH, // Add text to path (invisible)
+}
+
+@(default_calling_convention="c", link_prefix="pdfio")
+foreign lib {
+ // Color array functions...
+ ArrayCreateColorFromICCObj :: proc(pdf: ^file_t, icc_object: ^obj_t) -> ^array_t ---
+ ArrayCreateColorFromMatrix :: proc(pdf: ^file_t, num_colors: c.size_t, gamma: f64, _matrix: [^][3]f64, white_point: ^f64) -> ^array_t ---
+ ArrayCreateColorFromPalette :: proc(pdf: ^file_t, num_colors: c.size_t, colors: ^c.uchar) -> ^array_t ---
+ ArrayCreateColorFromPrimaries :: proc(pdf: ^file_t, num_colors: c.size_t, gamma: f64, wx: f64, wy: f64, rx: f64, ry: f64, gx: f64, gy: f64, bx: f64, by: f64) -> ^array_t ---
+ ArrayCreateColorFromStandard :: proc(pdf: ^file_t, num_colors: c.size_t, cs: cs_t) -> ^array_t ---
+
+ // PDF content drawing functions...
+ ContentClip :: proc(st: ^stream_t, even_odd: bool) -> bool ---
+ ContentDrawImage :: proc(st: ^stream_t, name: cstring, x: f64, y: f64, w: f64, h: f64) -> bool ---
+ ContentFill :: proc(st: ^stream_t, even_odd: bool) -> bool ---
+ ContentFillAndStroke :: proc(st: ^stream_t, even_odd: bool) -> bool ---
+ ContentMatrixConcat :: proc(st: ^stream_t, m: [^][2]f64) -> bool ---
+ ContentMatrixRotate :: proc(st: ^stream_t, degrees: f64) -> bool ---
+ ContentMatrixScale :: proc(st: ^stream_t, sx: f64, sy: f64) -> bool ---
+ ContentMatrixTranslate :: proc(st: ^stream_t, tx: f64, ty: f64) -> bool ---
+ ContentPathClose :: proc(st: ^stream_t) -> bool ---
+ ContentPathCurve :: proc(st: ^stream_t, x1: f64, y1: f64, x2: f64, y2: f64, x3: f64, y3: f64) -> bool ---
+ ContentPathCurve13 :: proc(st: ^stream_t, x1: f64, y1: f64, x3: f64, y3: f64) -> bool ---
+ ContentPathCurve23 :: proc(st: ^stream_t, x2: f64, y2: f64, x3: f64, y3: f64) -> bool ---
+ ContentPathEnd :: proc(st: ^stream_t) -> bool ---
+ ContentPathLineTo :: proc(st: ^stream_t, x: f64, y: f64) -> bool ---
+ ContentPathMoveTo :: proc(st: ^stream_t, x: f64, y: f64) -> bool ---
+ ContentPathRect :: proc(st: ^stream_t, x: f64, y: f64, width: f64, height: f64) -> bool ---
+ ContentRestore :: proc(st: ^stream_t) -> bool ---
+ ContentSave :: proc(st: ^stream_t) -> bool ---
+ ContentSetDashPattern :: proc(st: ^stream_t, phase: f64, on: f64, off: f64) -> bool ---
+ ContentSetFillColorDeviceCMYK :: proc(st: ^stream_t, _c: f64, m: f64, y: f64, k: f64) -> bool ---
+ ContentSetFillColorDeviceGray :: proc(st: ^stream_t, g: f64) -> bool ---
+ ContentSetFillColorDeviceRGB :: proc(st: ^stream_t, r: f64, g: f64, b: f64) -> bool ---
+ ContentSetFillColorGray :: proc(st: ^stream_t, g: f64) -> bool ---
+ ContentSetFillColorRGB :: proc(st: ^stream_t, r: f64, g: f64, b: f64) -> bool ---
+ ContentSetFillColorSpace :: proc(st: ^stream_t, name: cstring) -> bool ---
+ ContentSetFlatness :: proc(st: ^stream_t, f: f64) -> bool ---
+ ContentSetLineCap :: proc(st: ^stream_t, lc: linecap_t) -> bool ---
+ ContentSetLineJoin :: proc(st: ^stream_t, lj: linejoin_t) -> bool ---
+ ContentSetLineWidth :: proc(st: ^stream_t, width: f64) -> bool ---
+ ContentSetMiterLimit :: proc(st: ^stream_t, limit: f64) -> bool ---
+ ContentSetStrokeColorDeviceCMYK :: proc(st: ^stream_t, _c: f64, m: f64, y: f64, k: f64) -> bool ---
+ ContentSetStrokeColorDeviceGray :: proc(st: ^stream_t, g: f64) -> bool ---
+ ContentSetStrokeColorDeviceRGB :: proc(st: ^stream_t, r: f64, g: f64, b: f64) -> bool ---
+ ContentSetStrokeColorGray :: proc(st: ^stream_t, g: f64) -> bool ---
+ ContentSetStrokeColorRGB :: proc(st: ^stream_t, r: f64, g: f64, b: f64) -> bool ---
+ ContentSetStrokeColorSpace :: proc(st: ^stream_t, name: cstring) -> bool ---
+ ContentSetTextCharacterSpacing :: proc(st: ^stream_t, spacing: f64) -> bool ---
+ ContentSetTextFont :: proc(st: ^stream_t, name: cstring, size: f64) -> bool ---
+ ContentSetTextLeading :: proc(st: ^stream_t, leading: f64) -> bool ---
+ ContentSetTextMatrix :: proc(st: ^stream_t, m: [^][2]f64) -> bool ---
+ ContentSetTextRenderingMode :: proc(st: ^stream_t, mode: textrendering_t) -> bool ---
+ ContentSetTextRise :: proc(st: ^stream_t, rise: f64) -> bool ---
+ ContentSetTextWordSpacing :: proc(st: ^stream_t, spacing: f64) -> bool ---
+ ContentSetTextXScaling :: proc(st: ^stream_t, percent: f64) -> bool ---
+ ContentStroke :: proc(st: ^stream_t) -> bool ---
+ ContentTextBegin :: proc(st: ^stream_t) -> bool ---
+ ContentTextEnd :: proc(st: ^stream_t) -> bool ---
+ ContentTextMeasure :: proc(font: ^obj_t, s: cstring, size: f64) -> f64 ---
+ ContentTextMoveLine :: proc(st: ^stream_t, tx: f64, ty: f64) -> bool ---
+ ContentTextMoveTo :: proc(st: ^stream_t, tx: f64, ty: f64) -> bool ---
+ ContentTextNewLine :: proc(st: ^stream_t) -> bool ---
+ ContentTextNewLineShow :: proc(st: ^stream_t, ws: f64, cs: f64, unicode: bool, s: cstring) -> bool ---
+ ContentTextNewLineShowf :: proc(st: ^stream_t, ws: f64, cs: f64, unicode: bool, format: cstring, #c_vararg _: ..any) -> bool ---
+ ContentTextNextLine :: proc(st: ^stream_t) -> bool ---
+ ContentTextShow :: proc(st: ^stream_t, unicode: bool, s: cstring) -> bool ---
+ ContentTextShowf :: proc(st: ^stream_t, unicode: bool, format: cstring, #c_vararg _: ..any) -> bool ---
+ ContentTextShowJustified :: proc(st: ^stream_t, unicode: bool, num_fragments: c.size_t, offsets: ^f64, fragments: [^]cstring) -> bool ---
+
+ // Resource helpers...
+ FileCreateFontObjFromBase :: proc(pdf: ^file_t, name: cstring) -> ^obj_t ---
+ FileCreateFontObjFromFile :: proc(pdf: ^file_t, filename: cstring, unicode: bool) -> ^obj_t ---
+ FileCreateICCObjFromFile :: proc(pdf: ^file_t, filename: cstring, num_colors: c.size_t) -> ^obj_t ---
+ FileCreateImageObjFromData :: proc(pdf: ^file_t, data: ^c.uchar, width: c.size_t, height: c.size_t, num_colors: c.size_t, color_data: ^array_t, alpha: bool, interpolate: bool) -> ^obj_t ---
+ FileCreateImageObjFromFile :: proc(pdf: ^file_t, filename: cstring, interpolate: bool) -> ^obj_t ---
+
+ // Image object helpers...
+ ImageGetBytesPerLine :: proc(obj: ^obj_t) -> c.size_t ---
+ ImageGetHeight :: proc(obj: ^obj_t) -> f64 ---
+ ImageGetWidth :: proc(obj: ^obj_t) -> f64 ---
+
+ // Page dictionary helpers...
+ PageDictAddColorSpace :: proc(dict: ^dict_t, name: cstring, data: ^array_t) -> bool ---
+ PageDictAddFont :: proc(dict: ^dict_t, name: cstring, obj: ^obj_t) -> bool ---
+ PageDictAddImage :: proc(dict: ^dict_t, name: cstring, obj: ^obj_t) -> bool ---
+}
diff --git a/odin-c-bindgen/examples/pdfio/pdfio/pdfio.odin b/odin-c-bindgen/examples/pdfio/pdfio/pdfio.odin
@@ -0,0 +1,221 @@
+//
+// Public header file for PDFio.
+//
+// Copyright © 2021-2025 by Michael R Sweet.
+//
+// Licensed under Apache License v2.0. See the file "LICENSE" for more
+// information.
+//
+package pdfio
+
+import "core:c"
+import "core:c/libc"
+
+_ :: c
+_ :: libc
+
+foreign import lib "pdfio1.lib"
+
+array_t :: struct {}
+
+// Array of PDF values
+dict_t :: struct {}
+
+// Key/value dictionary
+dict_cb_t :: proc "c" (^dict_t, cstring, rawptr) -> bool
+
+// Dictionary iterator callback
+file_t :: struct {}
+
+// PDF file
+error_cb_t :: proc "c" (^file_t, cstring, rawptr) -> bool
+
+// Error callback
+encryption_t :: enum c.int {
+ NONE = 0, // No encryption
+ RC4_40, // 40-bit RC4 encryption (PDF 1.3)
+ RC4_128, // 128-bit RC4 encryption (PDF 1.4)
+ AES_128, // 128-bit AES encryption (PDF 1.6)
+ AES_256, // 256-bit AES encryption (PDF 2.0) @exclude all@
+}
+
+filter_t :: enum c.int {
+ NONE, // No filter
+ ASCIIHEX, // ASCIIHexDecode filter (reading only)
+ ASCII85, // ASCII85Decode filter (reading only)
+ CCITTFAX, // CCITTFaxDecode filter
+ CRYPT, // Encryption filter
+ DCT, // DCTDecode (JPEG) filter
+ FLATE, // FlateDecode filter
+ JBIG2, // JBIG2Decode filter
+ JPX, // JPXDecode filter (reading only)
+ LZW, // LZWDecode filter (reading only)
+ RUNLENGTH, // RunLengthDecode filter (reading only)
+}
+
+obj_t :: struct {} // Numbered object in PDF file
+
+output_cb_t :: proc "c" (rawptr, rawptr, c.size_t) -> c.ssize_t
+
+// Output callback for pdfioFileCreateOutput
+password_cb_t :: proc "c" (rawptr, cstring) -> cstring
+
+// Password callback for pdfioFileOpen
+permission_e :: enum c.int {
+ PRINT = 2, // PDF allows printing
+ MODIFY = 3, // PDF allows modification
+ COPY = 4, // PDF allows copying
+ ANNOTATE = 5, // PDF allows annotation
+ FORMS = 8, // PDF allows filling in forms
+ READING = 9, // PDF allows screen reading/accessibility (deprecated in PDF 2.0)
+ ASSEMBLE = 10, // PDF allows assembly (insert, delete, or rotate pages, add document outlines and thumbnails)
+ PRINT_HIGH = 11, // PDF allows high quality printing
+}
+
+permission_t :: distinct bit_set[permission_e; c.int]
+
+PERMISSION_ALL :: permission_t { .PRINT, .MODIFY, .COPY, .ANNOTATE, .FORMS, .READING, .ASSEMBLE, .PRINT_HIGH }
+
+rect_t :: struct {
+ x1: f64, // Lower-left X coordinate
+ y1: f64, // Lower-left Y coordinate
+ x2: f64, // Upper-right X coordinate
+ y2: f64, // Upper-right Y coordinate
+}
+
+stream_t :: struct {}
+
+// Object data stream in PDF file
+valtype_t :: enum c.int {
+ NONE, // No value, not set
+ ARRAY, // Array
+ BINARY, // Binary data
+ BOOLEAN, // Boolean
+ DATE, // Date/time
+ DICT, // Dictionary
+ INDIRECT, // Indirect object (N G obj)
+ NAME, // Name
+ NULL, // Null object
+ NUMBER, // Number (integer or real)
+ STRING, // String
+}
+
+@(default_calling_convention="c", link_prefix="pdfio")
+foreign lib {
+ // Functions...
+ ArrayAppendArray :: proc(a: ^array_t, value: ^array_t) -> bool ---
+ ArrayAppendBinary :: proc(a: ^array_t, value: [^]c.uchar, valuelen: c.size_t) -> bool ---
+ ArrayAppendBoolean :: proc(a: ^array_t, value: bool) -> bool ---
+ ArrayAppendDate :: proc(a: ^array_t, value: libc.time_t) -> bool ---
+ ArrayAppendDict :: proc(a: ^array_t, value: ^dict_t) -> bool ---
+ ArrayAppendName :: proc(a: ^array_t, value: cstring) -> bool ---
+ ArrayAppendNumber :: proc(a: ^array_t, value: f64) -> bool ---
+ ArrayAppendObj :: proc(a: ^array_t, value: ^obj_t) -> bool ---
+ ArrayAppendString :: proc(a: ^array_t, value: cstring) -> bool ---
+ ArrayCopy :: proc(pdf: ^file_t, a: ^array_t) -> ^array_t ---
+ ArrayCreate :: proc(pdf: ^file_t) -> ^array_t ---
+ ArrayGetArray :: proc(a: ^array_t, n: c.size_t) -> ^array_t ---
+ ArrayGetBinary :: proc(a: ^array_t, n: c.size_t, length: ^c.size_t) -> [^]c.uchar ---
+ ArrayGetBoolean :: proc(a: ^array_t, n: c.size_t) -> bool ---
+ ArrayGetDate :: proc(a: ^array_t, n: c.size_t) -> libc.time_t ---
+ ArrayGetDict :: proc(a: ^array_t, n: c.size_t) -> ^dict_t ---
+ ArrayGetName :: proc(a: ^array_t, n: c.size_t) -> cstring ---
+ ArrayGetNumber :: proc(a: ^array_t, n: c.size_t) -> f64 ---
+ ArrayGetObj :: proc(a: ^array_t, n: c.size_t) -> ^obj_t ---
+ ArrayGetSize :: proc(a: ^array_t) -> c.size_t ---
+ ArrayGetString :: proc(a: ^array_t, n: c.size_t) -> cstring ---
+ ArrayGetType :: proc(a: ^array_t, n: c.size_t) -> valtype_t ---
+ ArrayRemove :: proc(a: ^array_t, n: c.size_t) -> bool ---
+ DictClear :: proc(dict: ^dict_t, key: cstring) -> bool ---
+ DictCopy :: proc(pdf: ^file_t, dict: ^dict_t) -> ^dict_t ---
+ DictCreate :: proc(pdf: ^file_t) -> ^dict_t ---
+ DictGetArray :: proc(dict: ^dict_t, key: cstring) -> ^array_t ---
+ DictGetBinary :: proc(dict: ^dict_t, key: cstring, length: ^c.size_t) -> ^c.uchar ---
+ DictGetBoolean :: proc(dict: ^dict_t, key: cstring) -> bool ---
+ DictGetDate :: proc(dict: ^dict_t, key: cstring) -> libc.time_t ---
+ DictGetDict :: proc(dict: ^dict_t, key: cstring) -> ^dict_t ---
+ DictGetKey :: proc(dict: ^dict_t, n: c.size_t) -> cstring ---
+ DictGetName :: proc(dict: ^dict_t, key: cstring) -> cstring ---
+ DictGetNumPairs :: proc(dict: ^dict_t) -> c.size_t ---
+ DictGetNumber :: proc(dict: ^dict_t, key: cstring) -> f64 ---
+ DictGetObj :: proc(dict: ^dict_t, key: cstring) -> ^obj_t ---
+ DictGetRect :: proc(dict: ^dict_t, key: cstring, rect: ^rect_t) -> ^rect_t ---
+ DictGetString :: proc(dict: ^dict_t, key: cstring) -> cstring ---
+ DictGetType :: proc(dict: ^dict_t, key: cstring) -> valtype_t ---
+ DictIterateKeys :: proc(dict: ^dict_t, cb: dict_cb_t, cb_data: rawptr) ---
+ DictSetArray :: proc(dict: ^dict_t, key: cstring, value: ^array_t) -> bool ---
+ DictSetBinary :: proc(dict: ^dict_t, key: cstring, value: ^c.uchar, valuelen: c.size_t) -> bool ---
+ DictSetBoolean :: proc(dict: ^dict_t, key: cstring, value: bool) -> bool ---
+ DictSetDate :: proc(dict: ^dict_t, key: cstring, value: libc.time_t) -> bool ---
+ DictSetDict :: proc(dict: ^dict_t, key: cstring, value: ^dict_t) -> bool ---
+ DictSetName :: proc(dict: ^dict_t, key: cstring, value: cstring) -> bool ---
+ DictSetNull :: proc(dict: ^dict_t, key: cstring) -> bool ---
+ DictSetNumber :: proc(dict: ^dict_t, key: cstring, value: f64) -> bool ---
+ DictSetObj :: proc(dict: ^dict_t, key: cstring, value: ^obj_t) -> bool ---
+ DictSetRect :: proc(dict: ^dict_t, key: cstring, value: ^rect_t) -> bool ---
+ DictSetString :: proc(dict: ^dict_t, key: cstring, value: cstring) -> bool ---
+ DictSetStringf :: proc(dict: ^dict_t, key: cstring, format: cstring, #c_vararg _: ..any) -> bool ---
+ FileClose :: proc(pdf: ^file_t) -> bool ---
+ FileCreate :: proc(filename: cstring, version: cstring, media_box: ^rect_t, crop_box: ^rect_t, error_cb: error_cb_t, error_data: rawptr) -> ^file_t ---
+ FileCreateArrayObj :: proc(pdf: ^file_t, array: ^array_t) -> ^obj_t ---
+ FileCreateNameObj :: proc(pdf: ^file_t, name: cstring) -> ^obj_t ---
+ FileCreateNumberObj :: proc(pdf: ^file_t, number: f64) -> ^obj_t ---
+ FileCreateObj :: proc(pdf: ^file_t, dict: ^dict_t) -> ^obj_t ---
+ FileCreateOutput :: proc(output_cb: output_cb_t, output_ctx: rawptr, version: cstring, media_box: ^rect_t, crop_box: ^rect_t, error_cb: error_cb_t, error_data: rawptr) -> ^file_t ---
+
+ // TODO: Add number, array, string, etc. versions of pdfioFileCreateObject?
+ FileCreatePage :: proc(pdf: ^file_t, dict: ^dict_t) -> ^stream_t ---
+ FileCreateStringObj :: proc(pdf: ^file_t, s: cstring) -> ^obj_t ---
+ FileCreateTemporary :: proc(buffer: [^]c.char, bufsize: c.size_t, version: cstring, media_box: ^rect_t, crop_box: ^rect_t, error_cb: error_cb_t, error_data: rawptr) -> ^file_t ---
+ FileFindObj :: proc(pdf: ^file_t, number: c.size_t) -> ^obj_t ---
+ FileGetAuthor :: proc(pdf: ^file_t) -> cstring ---
+ FileGetCatalog :: proc(pdf: ^file_t) -> ^dict_t ---
+ FileGetCreationDate :: proc(pdf: ^file_t) -> libc.time_t ---
+ FileGetCreator :: proc(pdf: ^file_t) -> cstring ---
+ FileGetID :: proc(pdf: ^file_t) -> ^array_t ---
+ FileGetKeywords :: proc(pdf: ^file_t) -> cstring ---
+ FileGetName :: proc(pdf: ^file_t) -> cstring ---
+ FileGetNumObjs :: proc(pdf: ^file_t) -> c.size_t ---
+ FileGetNumPages :: proc(pdf: ^file_t) -> c.size_t ---
+ FileGetObj :: proc(pdf: ^file_t, n: c.size_t) -> ^obj_t ---
+ FileGetPage :: proc(pdf: ^file_t, n: c.size_t) -> ^obj_t ---
+ FileGetPermissions :: proc(pdf: ^file_t, encryption: ^encryption_t) -> permission_t ---
+ FileGetProducer :: proc(pdf: ^file_t) -> cstring ---
+ FileGetSubject :: proc(pdf: ^file_t) -> cstring ---
+ FileGetTitle :: proc(pdf: ^file_t) -> cstring ---
+ FileGetVersion :: proc(pdf: ^file_t) -> cstring ---
+ FileOpen :: proc(filename: cstring, password_cb: password_cb_t, password_data: rawptr, error_cb: error_cb_t, error_data: rawptr) -> ^file_t ---
+ FileSetAuthor :: proc(pdf: ^file_t, value: cstring) ---
+ FileSetCreationDate :: proc(pdf: ^file_t, value: libc.time_t) ---
+ FileSetCreator :: proc(pdf: ^file_t, value: cstring) ---
+ FileSetKeywords :: proc(pdf: ^file_t, value: cstring) ---
+ FileSetPermissions :: proc(pdf: ^file_t, permissions: permission_t, encryption: encryption_t, owner_password: cstring, user_password: cstring) -> bool ---
+ FileSetSubject :: proc(pdf: ^file_t, value: cstring) ---
+ FileSetTitle :: proc(pdf: ^file_t, value: cstring) ---
+ ObjClose :: proc(obj: ^obj_t) -> bool ---
+ ObjCopy :: proc(pdf: ^file_t, srcobj: ^obj_t) -> ^obj_t ---
+ ObjCreateStream :: proc(obj: ^obj_t, compression: filter_t) -> ^stream_t ---
+ ObjGetArray :: proc(obj: ^obj_t) -> ^array_t ---
+ ObjGetDict :: proc(obj: ^obj_t) -> ^dict_t ---
+ ObjGetGeneration :: proc(obj: ^obj_t) -> c.ushort ---
+ ObjGetLength :: proc(obj: ^obj_t) -> c.size_t ---
+ ObjGetName :: proc(obj: ^obj_t) -> cstring ---
+ ObjGetNumber :: proc(obj: ^obj_t) -> c.size_t ---
+ ObjGetSubtype :: proc(obj: ^obj_t) -> cstring ---
+ ObjGetType :: proc(obj: ^obj_t) -> cstring ---
+ ObjOpenStream :: proc(obj: ^obj_t, decode: bool) -> ^stream_t ---
+ PageCopy :: proc(pdf: ^file_t, srcpage: ^obj_t) -> bool ---
+ PageGetNumStreams :: proc(page: ^obj_t) -> c.size_t ---
+ PageOpenStream :: proc(page: ^obj_t, n: c.size_t, decode: bool) -> ^stream_t ---
+ StreamClose :: proc(st: ^stream_t) -> bool ---
+ StreamConsume :: proc(st: ^stream_t, bytes: c.size_t) -> bool ---
+ StreamGetToken :: proc(st: ^stream_t, buffer: [^]c.char, bufsize: c.size_t) -> bool ---
+ StreamPeek :: proc(st: ^stream_t, buffer: rawptr, bytes: c.size_t) -> c.ssize_t ---
+ StreamPrintf :: proc(st: ^stream_t, format: cstring, #c_vararg _: ..any) -> bool ---
+ StreamPutChar :: proc(st: ^stream_t, ch: c.int) -> bool ---
+ StreamPuts :: proc(st: ^stream_t, s: cstring) -> bool ---
+ StreamRead :: proc(st: ^stream_t, buffer: rawptr, bytes: c.size_t) -> c.ssize_t ---
+ StreamWrite :: proc(st: ^stream_t, buffer: rawptr, bytes: c.size_t) -> bool ---
+ StringCreate :: proc(pdf: ^file_t, s: cstring) -> cstring ---
+ StringCreatef :: proc(pdf: ^file_t, format: cstring, #c_vararg _: ..any) -> cstring ---
+}
diff --git a/odin-c-bindgen/examples/pdfio/test/.gitignore b/odin-c-bindgen/examples/pdfio/test/.gitignore
@@ -0,0 +1 @@
+test.pdf
+\ No newline at end of file
diff --git a/odin-c-bindgen/examples/pdfio/test/main.odin b/odin-c-bindgen/examples/pdfio/test/main.odin
@@ -0,0 +1,27 @@
+package pdfio_test
+
+import "core:fmt"
+import pio "../pdfio"
+
+main :: proc() {
+ pdf := pio.FileCreate("test.pdf", nil, nil, nil, nil, nil)
+ pio.FileSetTitle(pdf, "The Test")
+ pio.FileSetAuthor(pdf, "Karl")
+ page_dict := pio.DictCreate(pdf)
+ page := pio.FileCreatePage(pdf, page_dict)
+ pio.ContentSetFillColorDeviceRGB(page, 0.6, 0.0, 0.0)
+ pio.ContentSetStrokeColorDeviceRGB(page, 0.6, 0.0, 0.0)
+ pio.ContentPathRect(page, 100, 100, 300, 300)
+ pio.ContentFill(page, false)
+ pio.ContentTextBegin(page)
+ pio.ContentSetTextFont(page, "Arial", 100);
+ pio.ContentSetFillColorDeviceRGB(page, 0.6, 0.0, 1.0)
+ pio.ContentSetStrokeColorDeviceRGB(page, 0.6, 0.0, 1.0)
+ pio.ContentTextMoveTo(page, 20, 600)
+
+ pio.ContentTextShow(page, true, "Hi!")
+ pio.ContentTextEnd(page)
+ pio.StreamClose(page)
+ pio.FileClose(pdf)
+ fmt.println("test.pdf created")
+}
+\ No newline at end of file
diff --git a/odin-c-bindgen/examples/raylib/.gitignore b/odin-c-bindgen/examples/raylib/.gitignore
@@ -0,0 +1 @@
+raylib/*.lib
diff --git a/odin-c-bindgen/examples/raylib/bindgen.sjson b/odin-c-bindgen/examples/raylib/bindgen.sjson
@@ -0,0 +1,86 @@
+// See README.md in root of repository for documentation and more configuration options.
+
+inputs = [
+ "input"
+]
+
+output_folder = "raylib"
+imports_file = "imports.odin"
+package_name = "raylib"
+
+// "Old_Name" = "New_Name"
+// We rename ConfigFlags to ConfigFlag so we can introduce a bit_set with the name ConfigFlags.
+rename = {
+ "ConfigFlags" = "ConfigFlag"
+}
+
+// "Pre_Existing_Enum_Type" = "New_Bit_Set_Type"
+bit_setify = {
+ "Gesture" = "Gestures"
+ "ConfigFlags" = "ConfigFlags"
+}
+
+type_overrides = {
+ "Vector2" = "[2]f32"
+ "Vector3" = "[3]f32"
+ "Vector4" = "[4]f32"
+ "Matrix" = "#row_major matrix[4, 4]f32"
+ "Color" = "distinct [4]u8"
+}
+
+struct_field_overrides = {
+ "Image.format" = "PixelFormat"
+ "Texture.format" = "PixelFormat"
+ "NPatchInfo.layout" = "NPatchLayout"
+ "GlyphInfo.value" = "rune"
+
+ "Mesh.vertices" = "[^]"
+ "Mesh.texcoords" = "[^]"
+ "Mesh.texcoords2" = "[^]"
+ "Mesh.normals" = "[^]"
+ "Mesh.tangents" = "[^]"
+ "Mesh.colors" = "[^]"
+ "Mesh.indices" = "[^]"
+ "Mesh.animVertices" = "[^]"
+ "Mesh.animNormals" = "[^]"
+ "Mesh.boneIds" = "[^]"
+ "Mesh.boneWeights" = "[^]"
+ "Mesh.boneMatrices" = "[^]"
+ "Mesh.vboId" = "[^]"
+ "Shader.locs" = "[^]"
+ "Material.maps" = "[^]"
+ "Model.meshes" = "[^]"
+ "Model.materials" = "[^]"
+ "Model.meshMaterials" = "[^]"
+ "Model.bones" = "[^]"
+ "Model.bindPose" = "[^]"
+ "ModelAnimation.bones" = "[^]"
+ "ModelAnimation.framePoses" = "[^][^]Transform"
+
+ "AudioStream.buffer" = "rawptr"
+ "AudioStream.processor" = "rawptr"
+
+ // This is not a complete override list, it's just an example.
+}
+
+procedure_type_overrides = {
+ "SetConfigFlags.flags" = "ConfigFlags"
+ "IsKeyPressed.key" = "KeyboardKey"
+ "IsKeyPressedRepeat.key" = "KeyboardKey"
+ "IsKeyDown.key" = "KeyboardKey"
+ "IsKeyReleased.key" = "KeyboardKey"
+ "IsKeyUp.key" = "KeyboardKey"
+ "GetKeyPressed" = "KeyboardKey"
+ "IsMouseButtonPressed.button" = "MouseButton"
+ "IsMouseButtonDown.button" = "MouseButton"
+ "IsMouseButtonReleased.button" = "MouseButton"
+ "IsMouseButtonUp.button" = "MouseButton"
+ "SetShaderValue.uniformType" = "ShaderUniformDataType"
+ "SetShaderValueV.uniformType" = "ShaderUniformDataType"
+ "SetExitKey.key" = "KeyboardKey"
+ "GetKeyName.key" = "KeyboardKey"
+ "IsGestureDetected.gesture" = "Gestures"
+ "SetGesturesEnabled.flags" = "Gestures"
+
+ // This is not a complete override list, it's just an example.
+}
diff --git a/odin-c-bindgen/examples/raylib/imports.odin b/odin-c-bindgen/examples/raylib/imports.odin
@@ -0,0 +1,8 @@
+@(extra_linker_flags="/NODEFAULTLIB:libcmt")
+foreign import lib {
+ "raylib.lib",
+ "system:Winmm.lib",
+ "system:Gdi32.lib",
+ "system:User32.lib",
+ "system:Shell32.lib",
+}
+\ No newline at end of file
diff --git a/odin-c-bindgen/examples/raylib/input/raylib.h b/odin-c-bindgen/examples/raylib/input/raylib.h
@@ -0,0 +1,1714 @@
+/**********************************************************************************************
+*
+* raylib v5.6-dev - A simple and easy-to-use library to enjoy videogames programming (www.raylib.com)
+*
+* FEATURES:
+* - NO external dependencies, all required libraries included with raylib
+* - Multiplatform: Windows, Linux, FreeBSD, OpenBSD, NetBSD, DragonFly,
+* MacOS, Haiku, Android, Raspberry Pi, DRM native, HTML5.
+* - Written in plain C code (C99) in PascalCase/camelCase notation
+* - Hardware accelerated with OpenGL (1.1, 2.1, 3.3, 4.3, ES2, ES3 - choose at compile)
+* - Unique OpenGL abstraction layer (usable as standalone module): [rlgl]
+* - Multiple Fonts formats supported (TTF, OTF, FNT, BDF, Sprite fonts)
+* - Outstanding texture formats support, including compressed formats (DXT, ETC, ASTC)
+* - Full 3d support for 3d Shapes, Models, Billboards, Heightmaps and more!
+* - Flexible Materials system, supporting classic maps and PBR maps
+* - Animated 3D models supported (skeletal bones animation) (IQM, M3D, GLTF)
+* - Shaders support, including Model shaders and Postprocessing shaders
+* - Powerful math module for Vector, Matrix and Quaternion operations: [raymath]
+* - Audio loading and playing with streaming support (WAV, OGG, MP3, FLAC, QOA, XM, MOD)
+* - VR stereo rendering with configurable HMD device parameters
+* - Bindings to multiple programming languages available!
+*
+* NOTES:
+* - One default Font is loaded on InitWindow()->LoadFontDefault() [core, text]
+* - One default Texture2D is loaded on rlglInit(), 1x1 white pixel R8G8B8A8 [rlgl] (OpenGL 3.3 or ES2)
+* - One default Shader is loaded on rlglInit()->rlLoadShaderDefault() [rlgl] (OpenGL 3.3 or ES2)
+* - One default RenderBatch is loaded on rlglInit()->rlLoadRenderBatch() [rlgl] (OpenGL 3.3 or ES2)
+*
+* DEPENDENCIES (included):
+* [rcore][GLFW] rglfw (Camilla Löwy - github.com/glfw/glfw) for window/context management and input
+* [rcore][RGFW] rgfw (ColleagueRiley - github.com/ColleagueRiley/RGFW) for window/context management and input
+* [rlgl] glad/glad_gles2 (David Herberth - github.com/Dav1dde/glad) for OpenGL 3.3 extensions loading
+* [raudio] miniaudio (David Reid - github.com/mackron/miniaudio) for audio device/context management
+*
+* OPTIONAL DEPENDENCIES (included):
+* [rcore] msf_gif (Miles Fogle) for GIF recording
+* [rcore] sinfl (Micha Mettke) for DEFLATE decompression algorithm
+* [rcore] sdefl (Micha Mettke) for DEFLATE compression algorithm
+* [rcore] rprand (Ramon Snatamaria) for pseudo-random numbers generation
+* [rtextures] qoi (Dominic Szablewski - https://phoboslab.org) for QOI image manage
+* [rtextures] stb_image (Sean Barret) for images loading (BMP, TGA, PNG, JPEG, HDR...)
+* [rtextures] stb_image_write (Sean Barret) for image writing (BMP, TGA, PNG, JPG)
+* [rtextures] stb_image_resize2 (Sean Barret) for image resizing algorithms
+* [rtextures] stb_perlin (Sean Barret) for Perlin Noise image generation
+* [rtext] stb_truetype (Sean Barret) for ttf fonts loading
+* [rtext] stb_rect_pack (Sean Barret) for rectangles packing
+* [rmodels] par_shapes (Philip Rideout) for parametric 3d shapes generation
+* [rmodels] tinyobj_loader_c (Syoyo Fujita) for models loading (OBJ, MTL)
+* [rmodels] cgltf (Johannes Kuhlmann) for models loading (glTF)
+* [rmodels] m3d (bzt) for models loading (M3D, https://bztsrc.gitlab.io/model3d)
+* [rmodels] vox_loader (Johann Nadalutti) for models loading (VOX)
+* [raudio] dr_wav (David Reid) for WAV audio file loading
+* [raudio] dr_flac (David Reid) for FLAC audio file loading
+* [raudio] dr_mp3 (David Reid) for MP3 audio file loading
+* [raudio] stb_vorbis (Sean Barret) for OGG audio loading
+* [raudio] jar_xm (Joshua Reisenauer) for XM audio module loading
+* [raudio] jar_mod (Joshua Reisenauer) for MOD audio module loading
+* [raudio] qoa (Dominic Szablewski - https://phoboslab.org) for QOA audio manage
+*
+*
+* LICENSE: zlib/libpng
+*
+* raylib is licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+* BSD-like license that allows static linking with closed source software:
+*
+* Copyright (c) 2013-2025 Ramon Santamaria (@raysan5)
+*
+* This software is provided "as-is", without any express or implied warranty. In no event
+* will the authors be held liable for any damages arising from the use of this software.
+*
+* Permission is granted to anyone to use this software for any purpose, including commercial
+* applications, and to alter it and redistribute it freely, subject to the following restrictions:
+*
+* 1. The origin of this software must not be misrepresented; you must not claim that you
+* wrote the original software. If you use this software in a product, an acknowledgment
+* in the product documentation would be appreciated but is not required.
+*
+* 2. Altered source versions must be plainly marked as such, and must not be misrepresented
+* as being the original software.
+*
+* 3. This notice may not be removed or altered from any source distribution.
+*
+**********************************************************************************************/
+
+#ifndef RAYLIB_H
+#define RAYLIB_H
+
+#include <stdarg.h> // Required for: va_list - Only used by TraceLogCallback
+
+#define RAYLIB_VERSION_MAJOR 5
+#define RAYLIB_VERSION_MINOR 6
+#define RAYLIB_VERSION_PATCH 0
+#define RAYLIB_VERSION "5.6-dev"
+
+// Function specifiers in case library is build/used as a shared library
+// NOTE: Microsoft specifiers to tell compiler that symbols are imported/exported from a .dll
+// NOTE: visibility("default") attribute makes symbols "visible" when compiled with -fvisibility=hidden
+#if defined(_WIN32)
+ #if defined(__TINYC__)
+ #define __declspec(x) __attribute__((x))
+ #endif
+ #if defined(BUILD_LIBTYPE_SHARED)
+ #define RLAPI __declspec(dllexport) // We are building the library as a Win32 shared library (.dll)
+ #elif defined(USE_LIBTYPE_SHARED)
+ #define RLAPI __declspec(dllimport) // We are using the library as a Win32 shared library (.dll)
+ #endif
+#else
+ #if defined(BUILD_LIBTYPE_SHARED)
+ #define RLAPI __attribute__((visibility("default"))) // We are building as a Unix shared library (.so/.dylib)
+ #endif
+#endif
+
+#ifndef RLAPI
+ #define RLAPI // Functions defined as 'extern' by default (implicit specifiers)
+#endif
+
+//----------------------------------------------------------------------------------
+// Some basic Defines
+//----------------------------------------------------------------------------------
+#ifndef PI
+ #define PI 3.14159265358979323846f
+#endif
+#ifndef DEG2RAD
+ #define DEG2RAD (PI/180.0f)
+#endif
+#ifndef RAD2DEG
+ #define RAD2DEG (180.0f/PI)
+#endif
+
+// Allow custom memory allocators
+// NOTE: Require recompiling raylib sources
+#ifndef RL_MALLOC
+ #define RL_MALLOC(sz) malloc(sz)
+#endif
+#ifndef RL_CALLOC
+ #define RL_CALLOC(n,sz) calloc(n,sz)
+#endif
+#ifndef RL_REALLOC
+ #define RL_REALLOC(ptr,sz) realloc(ptr,sz)
+#endif
+#ifndef RL_FREE
+ #define RL_FREE(ptr) free(ptr)
+#endif
+
+// NOTE: MSVC C++ compiler does not support compound literals (C99 feature)
+// Plain structures in C++ (without constructors) can be initialized with { }
+// This is called aggregate initialization (C++11 feature)
+#if defined(__cplusplus)
+ #define CLITERAL(type) type
+#else
+ #define CLITERAL(type) (type)
+#endif
+
+// Some compilers (mostly macos clang) default to C++98,
+// where aggregate initialization can't be used
+// So, give a more clear error stating how to fix this
+#if !defined(_MSC_VER) && (defined(__cplusplus) && __cplusplus < 201103L)
+ #error "C++11 or later is required. Add -std=c++11"
+#endif
+
+// NOTE: We set some defines with some data types declared by raylib
+// Other modules (raymath, rlgl) also require some of those types, so,
+// to be able to use those other modules as standalone (not depending on raylib)
+// this defines are very useful for internal check and avoid type (re)definitions
+#define RL_COLOR_TYPE
+#define RL_RECTANGLE_TYPE
+#define RL_VECTOR2_TYPE
+#define RL_VECTOR3_TYPE
+#define RL_VECTOR4_TYPE
+#define RL_QUATERNION_TYPE
+#define RL_MATRIX_TYPE
+
+// Some Basic Colors
+// NOTE: Custom raylib color palette for amazing visuals on WHITE background
+#define LIGHTGRAY CLITERAL(Color){ 200, 200, 200, 255 } // Light Gray
+#define GRAY CLITERAL(Color){ 130, 130, 130, 255 } // Gray
+#define DARKGRAY CLITERAL(Color){ 80, 80, 80, 255 } // Dark Gray
+#define YELLOW CLITERAL(Color){ 253, 249, 0, 255 } // Yellow
+#define GOLD CLITERAL(Color){ 255, 203, 0, 255 } // Gold
+#define ORANGE CLITERAL(Color){ 255, 161, 0, 255 } // Orange
+#define PINK CLITERAL(Color){ 255, 109, 194, 255 } // Pink
+#define RED CLITERAL(Color){ 230, 41, 55, 255 } // Red
+#define MAROON CLITERAL(Color){ 190, 33, 55, 255 } // Maroon
+#define GREEN CLITERAL(Color){ 0, 228, 48, 255 } // Green
+#define LIME CLITERAL(Color){ 0, 158, 47, 255 } // Lime
+#define DARKGREEN CLITERAL(Color){ 0, 117, 44, 255 } // Dark Green
+#define SKYBLUE CLITERAL(Color){ 102, 191, 255, 255 } // Sky Blue
+#define BLUE CLITERAL(Color){ 0, 121, 241, 255 } // Blue
+#define DARKBLUE CLITERAL(Color){ 0, 82, 172, 255 } // Dark Blue
+#define PURPLE CLITERAL(Color){ 200, 122, 255, 255 } // Purple
+#define VIOLET CLITERAL(Color){ 135, 60, 190, 255 } // Violet
+#define DARKPURPLE CLITERAL(Color){ 112, 31, 126, 255 } // Dark Purple
+#define BEIGE CLITERAL(Color){ 211, 176, 131, 255 } // Beige
+#define BROWN CLITERAL(Color){ 127, 106, 79, 255 } // Brown
+#define DARKBROWN CLITERAL(Color){ 76, 63, 47, 255 } // Dark Brown
+
+#define WHITE CLITERAL(Color){ 255, 255, 255, 255 } // White
+#define BLACK CLITERAL(Color){ 0, 0, 0, 255 } // Black
+#define BLANK CLITERAL(Color){ 0, 0, 0, 0 } // Blank (Transparent)
+#define MAGENTA CLITERAL(Color){ 255, 0, 255, 255 } // Magenta
+#define RAYWHITE CLITERAL(Color){ 245, 245, 245, 255 } // My own White (raylib logo)
+
+//----------------------------------------------------------------------------------
+// Structures Definition
+//----------------------------------------------------------------------------------
+// Boolean type
+#if (defined(__STDC__) && __STDC_VERSION__ >= 199901L) || (defined(_MSC_VER) && _MSC_VER >= 1800)
+ #include <stdbool.h>
+#elif !defined(__cplusplus) && !defined(bool)
+ typedef enum bool { false = 0, true = !false } bool;
+ #define RL_BOOL_TYPE
+#endif
+
+// Vector2, 2 components
+typedef struct Vector2 {
+ float x; // Vector x component
+ float y; // Vector y component
+} Vector2;
+
+// Vector3, 3 components
+typedef struct Vector3 {
+ float x; // Vector x component
+ float y; // Vector y component
+ float z; // Vector z component
+} Vector3;
+
+// Vector4, 4 components
+typedef struct Vector4 {
+ float x; // Vector x component
+ float y; // Vector y component
+ float z; // Vector z component
+ float w; // Vector w component
+} Vector4;
+
+// Quaternion, 4 components (Vector4 alias)
+typedef Vector4 Quaternion;
+
+// Matrix, 4x4 components, column major, OpenGL style, right-handed
+typedef struct Matrix {
+ float m0, m4, m8, m12; // Matrix first row (4 components)
+ float m1, m5, m9, m13; // Matrix second row (4 components)
+ float m2, m6, m10, m14; // Matrix third row (4 components)
+ float m3, m7, m11, m15; // Matrix fourth row (4 components)
+} Matrix;
+
+// Color, 4 components, R8G8B8A8 (32bit)
+typedef struct Color {
+ unsigned char r; // Color red value
+ unsigned char g; // Color green value
+ unsigned char b; // Color blue value
+ unsigned char a; // Color alpha value
+} Color;
+
+// Rectangle, 4 components
+typedef struct Rectangle {
+ float x; // Rectangle top-left corner position x
+ float y; // Rectangle top-left corner position y
+ float width; // Rectangle width
+ float height; // Rectangle height
+} Rectangle;
+
+// Image, pixel data stored in CPU memory (RAM)
+typedef struct Image {
+ void *data; // Image raw data
+ int width; // Image base width
+ int height; // Image base height
+ int mipmaps; // Mipmap levels, 1 by default
+ int format; // Data format (PixelFormat type)
+} Image;
+
+// Texture, tex data stored in GPU memory (VRAM)
+typedef struct Texture {
+ unsigned int id; // OpenGL texture id
+ int width; // Texture base width
+ int height; // Texture base height
+ int mipmaps; // Mipmap levels, 1 by default
+ int format; // Data format (PixelFormat type)
+} Texture;
+
+// Texture2D, same as Texture
+typedef Texture Texture2D;
+
+// TextureCubemap, same as Texture
+typedef Texture TextureCubemap;
+
+// RenderTexture, fbo for texture rendering
+typedef struct RenderTexture {
+ unsigned int id; // OpenGL framebuffer object id
+ Texture texture; // Color buffer attachment texture
+ Texture depth; // Depth buffer attachment texture
+} RenderTexture;
+
+// RenderTexture2D, same as RenderTexture
+typedef RenderTexture RenderTexture2D;
+
+// NPatchInfo, n-patch layout info
+typedef struct NPatchInfo {
+ Rectangle source; // Texture source rectangle
+ int left; // Left border offset
+ int top; // Top border offset
+ int right; // Right border offset
+ int bottom; // Bottom border offset
+ int layout; // Layout of the n-patch: 3x3, 1x3 or 3x1
+} NPatchInfo;
+
+// GlyphInfo, font characters glyphs info
+typedef struct GlyphInfo {
+ int value; // Character value (Unicode)
+ int offsetX; // Character offset X when drawing
+ int offsetY; // Character offset Y when drawing
+ int advanceX; // Character advance position X
+ Image image; // Character image data
+} GlyphInfo;
+
+// Font, font texture and GlyphInfo array data
+typedef struct Font {
+ int baseSize; // Base size (default chars height)
+ int glyphCount; // Number of glyph characters
+ int glyphPadding; // Padding around the glyph characters
+ Texture2D texture; // Texture atlas containing the glyphs
+ Rectangle *recs; // Rectangles in texture for the glyphs
+ GlyphInfo *glyphs; // Glyphs info data
+} Font;
+
+// Camera, defines position/orientation in 3d space
+typedef struct Camera3D {
+ Vector3 position; // Camera position
+ Vector3 target; // Camera target it looks-at
+ Vector3 up; // Camera up vector (rotation over its axis)
+ float fovy; // Camera field-of-view aperture in Y (degrees) in perspective, used as near plane width in orthographic
+ int projection; // Camera projection: CAMERA_PERSPECTIVE or CAMERA_ORTHOGRAPHIC
+} Camera3D;
+
+typedef Camera3D Camera; // Camera type fallback, defaults to Camera3D
+
+// Camera2D, defines position/orientation in 2d space
+typedef struct Camera2D {
+ Vector2 offset; // Camera offset (displacement from target)
+ Vector2 target; // Camera target (rotation and zoom origin)
+ float rotation; // Camera rotation in degrees
+ float zoom; // Camera zoom (scaling), should be 1.0f by default
+} Camera2D;
+
+// Mesh, vertex data and vao/vbo
+typedef struct Mesh {
+ int vertexCount; // Number of vertices stored in arrays
+ int triangleCount; // Number of triangles stored (indexed or not)
+
+ // Vertex attributes data
+ float *vertices; // Vertex position (XYZ - 3 components per vertex) (shader-location = 0)
+ float *texcoords; // Vertex texture coordinates (UV - 2 components per vertex) (shader-location = 1)
+ float *texcoords2; // Vertex texture second coordinates (UV - 2 components per vertex) (shader-location = 5)
+ float *normals; // Vertex normals (XYZ - 3 components per vertex) (shader-location = 2)
+ float *tangents; // Vertex tangents (XYZW - 4 components per vertex) (shader-location = 4)
+ unsigned char *colors; // Vertex colors (RGBA - 4 components per vertex) (shader-location = 3)
+ unsigned short *indices; // Vertex indices (in case vertex data comes indexed)
+
+ // Animation vertex data
+ float *animVertices; // Animated vertex positions (after bones transformations)
+ float *animNormals; // Animated normals (after bones transformations)
+ unsigned char *boneIds; // Vertex bone ids, max 255 bone ids, up to 4 bones influence by vertex (skinning) (shader-location = 6)
+ float *boneWeights; // Vertex bone weight, up to 4 bones influence by vertex (skinning) (shader-location = 7)
+ Matrix *boneMatrices; // Bones animated transformation matrices
+ int boneCount; // Number of bones
+
+ // OpenGL identifiers
+ unsigned int vaoId; // OpenGL Vertex Array Object id
+ unsigned int *vboId; // OpenGL Vertex Buffer Objects id (default vertex data)
+} Mesh;
+
+// Shader
+typedef struct Shader {
+ unsigned int id; // Shader program id
+ int *locs; // Shader locations array (RL_MAX_SHADER_LOCATIONS)
+} Shader;
+
+// MaterialMap
+typedef struct MaterialMap {
+ Texture2D texture; // Material map texture
+ Color color; // Material map color
+ float value; // Material map value
+} MaterialMap;
+
+// Material, includes shader and maps
+typedef struct Material {
+ Shader shader; // Material shader
+ MaterialMap *maps; // Material maps array (MAX_MATERIAL_MAPS)
+ float params[4]; // Material generic parameters (if required)
+} Material;
+
+// Transform, vertex transformation data
+typedef struct Transform {
+ Vector3 translation; // Translation
+ Quaternion rotation; // Rotation
+ Vector3 scale; // Scale
+} Transform;
+
+// Bone, skeletal animation bone
+typedef struct BoneInfo {
+ char name[32]; // Bone name
+ int parent; // Bone parent
+} BoneInfo;
+
+// Model, meshes, materials and animation data
+typedef struct Model {
+ Matrix transform; // Local transform matrix
+
+ int meshCount; // Number of meshes
+ int materialCount; // Number of materials
+ Mesh *meshes; // Meshes array
+ Material *materials; // Materials array
+ int *meshMaterial; // Mesh material number
+
+ // Animation data
+ int boneCount; // Number of bones
+ BoneInfo *bones; // Bones information (skeleton)
+ Transform *bindPose; // Bones base transformation (pose)
+} Model;
+
+// ModelAnimation
+typedef struct ModelAnimation {
+ int boneCount; // Number of bones
+ int frameCount; // Number of animation frames
+ BoneInfo *bones; // Bones information (skeleton)
+ Transform **framePoses; // Poses array by frame
+ char name[32]; // Animation name
+} ModelAnimation;
+
+// Ray, ray for raycasting
+typedef struct Ray {
+ Vector3 position; // Ray position (origin)
+ Vector3 direction; // Ray direction (normalized)
+} Ray;
+
+// RayCollision, ray hit information
+typedef struct RayCollision {
+ bool hit; // Did the ray hit something?
+ float distance; // Distance to the nearest hit
+ Vector3 point; // Point of the nearest hit
+ Vector3 normal; // Surface normal of hit
+} RayCollision;
+
+// BoundingBox
+typedef struct BoundingBox {
+ Vector3 min; // Minimum vertex box-corner
+ Vector3 max; // Maximum vertex box-corner
+} BoundingBox;
+
+// Wave, audio wave data
+typedef struct Wave {
+ unsigned int frameCount; // Total number of frames (considering channels)
+ unsigned int sampleRate; // Frequency (samples per second)
+ unsigned int sampleSize; // Bit depth (bits per sample): 8, 16, 32 (24 not supported)
+ unsigned int channels; // Number of channels (1-mono, 2-stereo, ...)
+ void *data; // Buffer data pointer
+} Wave;
+
+// Opaque structs declaration
+// NOTE: Actual structs are defined internally in raudio module
+typedef struct rAudioBuffer rAudioBuffer;
+typedef struct rAudioProcessor rAudioProcessor;
+
+// AudioStream, custom audio stream
+typedef struct AudioStream {
+ rAudioBuffer *buffer; // Pointer to internal data used by the audio system
+ rAudioProcessor *processor; // Pointer to internal data processor, useful for audio effects
+
+ unsigned int sampleRate; // Frequency (samples per second)
+ unsigned int sampleSize; // Bit depth (bits per sample): 8, 16, 32 (24 not supported)
+ unsigned int channels; // Number of channels (1-mono, 2-stereo, ...)
+} AudioStream;
+
+// Sound
+typedef struct Sound {
+ AudioStream stream; // Audio stream
+ unsigned int frameCount; // Total number of frames (considering channels)
+} Sound;
+
+// Music, audio stream, anything longer than ~10 seconds should be streamed
+typedef struct Music {
+ AudioStream stream; // Audio stream
+ unsigned int frameCount; // Total number of frames (considering channels)
+ bool looping; // Music looping enable
+
+ int ctxType; // Type of music context (audio filetype)
+ void *ctxData; // Audio context data, depends on type
+} Music;
+
+// VrDeviceInfo, Head-Mounted-Display device parameters
+typedef struct VrDeviceInfo {
+ int hResolution; // Horizontal resolution in pixels
+ int vResolution; // Vertical resolution in pixels
+ float hScreenSize; // Horizontal size in meters
+ float vScreenSize; // Vertical size in meters
+ float eyeToScreenDistance; // Distance between eye and display in meters
+ float lensSeparationDistance; // Lens separation distance in meters
+ float interpupillaryDistance; // IPD (distance between pupils) in meters
+ float lensDistortionValues[4]; // Lens distortion constant parameters
+ float chromaAbCorrection[4]; // Chromatic aberration correction parameters
+} VrDeviceInfo;
+
+// VrStereoConfig, VR stereo rendering configuration for simulator
+typedef struct VrStereoConfig {
+ Matrix projection[2]; // VR projection matrices (per eye)
+ Matrix viewOffset[2]; // VR view offset matrices (per eye)
+ float leftLensCenter[2]; // VR left lens center
+ float rightLensCenter[2]; // VR right lens center
+ float leftScreenCenter[2]; // VR left screen center
+ float rightScreenCenter[2]; // VR right screen center
+ float scale[2]; // VR distortion scale
+ float scaleIn[2]; // VR distortion scale in
+} VrStereoConfig;
+
+// File path list
+typedef struct FilePathList {
+ unsigned int capacity; // Filepaths max entries
+ unsigned int count; // Filepaths entries count
+ char **paths; // Filepaths entries
+} FilePathList;
+
+// Automation event
+typedef struct AutomationEvent {
+ unsigned int frame; // Event frame
+ unsigned int type; // Event type (AutomationEventType)
+ int params[4]; // Event parameters (if required)
+} AutomationEvent;
+
+// Automation event list
+typedef struct AutomationEventList {
+ unsigned int capacity; // Events max entries (MAX_AUTOMATION_EVENTS)
+ unsigned int count; // Events entries count
+ AutomationEvent *events; // Events entries
+} AutomationEventList;
+
+//----------------------------------------------------------------------------------
+// Enumerators Definition
+//----------------------------------------------------------------------------------
+// System/Window config flags
+// NOTE: Every bit registers one state (use it with bit masks)
+// By default all flags are set to 0
+typedef enum {
+ FLAG_VSYNC_HINT = 0x00000040, // Set to try enabling V-Sync on GPU
+ FLAG_FULLSCREEN_MODE = 0x00000002, // Set to run program in fullscreen
+ FLAG_WINDOW_RESIZABLE = 0x00000004, // Set to allow resizable window
+ FLAG_WINDOW_UNDECORATED = 0x00000008, // Set to disable window decoration (frame and buttons)
+ FLAG_WINDOW_HIDDEN = 0x00000080, // Set to hide window
+ FLAG_WINDOW_MINIMIZED = 0x00000200, // Set to minimize window (iconify)
+ FLAG_WINDOW_MAXIMIZED = 0x00000400, // Set to maximize window (expanded to monitor)
+ FLAG_WINDOW_UNFOCUSED = 0x00000800, // Set to window non focused
+ FLAG_WINDOW_TOPMOST = 0x00001000, // Set to window always on top
+ FLAG_WINDOW_ALWAYS_RUN = 0x00000100, // Set to allow windows running while minimized
+ FLAG_WINDOW_TRANSPARENT = 0x00000010, // Set to allow transparent framebuffer
+ FLAG_WINDOW_HIGHDPI = 0x00002000, // Set to support HighDPI
+ FLAG_WINDOW_MOUSE_PASSTHROUGH = 0x00004000, // Set to support mouse passthrough, only supported when FLAG_WINDOW_UNDECORATED
+ FLAG_BORDERLESS_WINDOWED_MODE = 0x00008000, // Set to run program in borderless windowed mode
+ FLAG_MSAA_4X_HINT = 0x00000020, // Set to try enabling MSAA 4X
+ FLAG_INTERLACED_HINT = 0x00010000 // Set to try enabling interlaced video format (for V3D)
+} ConfigFlags;
+
+// Trace log level
+// NOTE: Organized by priority level
+typedef enum {
+ LOG_ALL = 0, // Display all logs
+ LOG_TRACE, // Trace logging, intended for internal use only
+ LOG_DEBUG, // Debug logging, used for internal debugging, it should be disabled on release builds
+ LOG_INFO, // Info logging, used for program execution info
+ LOG_WARNING, // Warning logging, used on recoverable failures
+ LOG_ERROR, // Error logging, used on unrecoverable failures
+ LOG_FATAL, // Fatal logging, used to abort program: exit(EXIT_FAILURE)
+ LOG_NONE // Disable logging
+} TraceLogLevel;
+
+// Keyboard keys (US keyboard layout)
+// NOTE: Use GetKeyPressed() to allow redefining
+// required keys for alternative layouts
+typedef enum {
+ KEY_NULL = 0, // Key: NULL, used for no key pressed
+ // Alphanumeric keys
+ KEY_APOSTROPHE = 39, // Key: '
+ KEY_COMMA = 44, // Key: ,
+ KEY_MINUS = 45, // Key: -
+ KEY_PERIOD = 46, // Key: .
+ KEY_SLASH = 47, // Key: /
+ KEY_ZERO = 48, // Key: 0
+ KEY_ONE = 49, // Key: 1
+ KEY_TWO = 50, // Key: 2
+ KEY_THREE = 51, // Key: 3
+ KEY_FOUR = 52, // Key: 4
+ KEY_FIVE = 53, // Key: 5
+ KEY_SIX = 54, // Key: 6
+ KEY_SEVEN = 55, // Key: 7
+ KEY_EIGHT = 56, // Key: 8
+ KEY_NINE = 57, // Key: 9
+ KEY_SEMICOLON = 59, // Key: ;
+ KEY_EQUAL = 61, // Key: =
+ KEY_A = 65, // Key: A | a
+ KEY_B = 66, // Key: B | b
+ KEY_C = 67, // Key: C | c
+ KEY_D = 68, // Key: D | d
+ KEY_E = 69, // Key: E | e
+ KEY_F = 70, // Key: F | f
+ KEY_G = 71, // Key: G | g
+ KEY_H = 72, // Key: H | h
+ KEY_I = 73, // Key: I | i
+ KEY_J = 74, // Key: J | j
+ KEY_K = 75, // Key: K | k
+ KEY_L = 76, // Key: L | l
+ KEY_M = 77, // Key: M | m
+ KEY_N = 78, // Key: N | n
+ KEY_O = 79, // Key: O | o
+ KEY_P = 80, // Key: P | p
+ KEY_Q = 81, // Key: Q | q
+ KEY_R = 82, // Key: R | r
+ KEY_S = 83, // Key: S | s
+ KEY_T = 84, // Key: T | t
+ KEY_U = 85, // Key: U | u
+ KEY_V = 86, // Key: V | v
+ KEY_W = 87, // Key: W | w
+ KEY_X = 88, // Key: X | x
+ KEY_Y = 89, // Key: Y | y
+ KEY_Z = 90, // Key: Z | z
+ KEY_LEFT_BRACKET = 91, // Key: [
+ KEY_BACKSLASH = 92, // Key: '\'
+ KEY_RIGHT_BRACKET = 93, // Key: ]
+ KEY_GRAVE = 96, // Key: `
+ // Function keys
+ KEY_SPACE = 32, // Key: Space
+ KEY_ESCAPE = 256, // Key: Esc
+ KEY_ENTER = 257, // Key: Enter
+ KEY_TAB = 258, // Key: Tab
+ KEY_BACKSPACE = 259, // Key: Backspace
+ KEY_INSERT = 260, // Key: Ins
+ KEY_DELETE = 261, // Key: Del
+ KEY_RIGHT = 262, // Key: Cursor right
+ KEY_LEFT = 263, // Key: Cursor left
+ KEY_DOWN = 264, // Key: Cursor down
+ KEY_UP = 265, // Key: Cursor up
+ KEY_PAGE_UP = 266, // Key: Page up
+ KEY_PAGE_DOWN = 267, // Key: Page down
+ KEY_HOME = 268, // Key: Home
+ KEY_END = 269, // Key: End
+ KEY_CAPS_LOCK = 280, // Key: Caps lock
+ KEY_SCROLL_LOCK = 281, // Key: Scroll down
+ KEY_NUM_LOCK = 282, // Key: Num lock
+ KEY_PRINT_SCREEN = 283, // Key: Print screen
+ KEY_PAUSE = 284, // Key: Pause
+ KEY_F1 = 290, // Key: F1
+ KEY_F2 = 291, // Key: F2
+ KEY_F3 = 292, // Key: F3
+ KEY_F4 = 293, // Key: F4
+ KEY_F5 = 294, // Key: F5
+ KEY_F6 = 295, // Key: F6
+ KEY_F7 = 296, // Key: F7
+ KEY_F8 = 297, // Key: F8
+ KEY_F9 = 298, // Key: F9
+ KEY_F10 = 299, // Key: F10
+ KEY_F11 = 300, // Key: F11
+ KEY_F12 = 301, // Key: F12
+ KEY_LEFT_SHIFT = 340, // Key: Shift left
+ KEY_LEFT_CONTROL = 341, // Key: Control left
+ KEY_LEFT_ALT = 342, // Key: Alt left
+ KEY_LEFT_SUPER = 343, // Key: Super left
+ KEY_RIGHT_SHIFT = 344, // Key: Shift right
+ KEY_RIGHT_CONTROL = 345, // Key: Control right
+ KEY_RIGHT_ALT = 346, // Key: Alt right
+ KEY_RIGHT_SUPER = 347, // Key: Super right
+ KEY_KB_MENU = 348, // Key: KB menu
+ // Keypad keys
+ KEY_KP_0 = 320, // Key: Keypad 0
+ KEY_KP_1 = 321, // Key: Keypad 1
+ KEY_KP_2 = 322, // Key: Keypad 2
+ KEY_KP_3 = 323, // Key: Keypad 3
+ KEY_KP_4 = 324, // Key: Keypad 4
+ KEY_KP_5 = 325, // Key: Keypad 5
+ KEY_KP_6 = 326, // Key: Keypad 6
+ KEY_KP_7 = 327, // Key: Keypad 7
+ KEY_KP_8 = 328, // Key: Keypad 8
+ KEY_KP_9 = 329, // Key: Keypad 9
+ KEY_KP_DECIMAL = 330, // Key: Keypad .
+ KEY_KP_DIVIDE = 331, // Key: Keypad /
+ KEY_KP_MULTIPLY = 332, // Key: Keypad *
+ KEY_KP_SUBTRACT = 333, // Key: Keypad -
+ KEY_KP_ADD = 334, // Key: Keypad +
+ KEY_KP_ENTER = 335, // Key: Keypad Enter
+ KEY_KP_EQUAL = 336, // Key: Keypad =
+ // Android key buttons
+ KEY_BACK = 4, // Key: Android back button
+ KEY_MENU = 5, // Key: Android menu button
+ KEY_VOLUME_UP = 24, // Key: Android volume up button
+ KEY_VOLUME_DOWN = 25 // Key: Android volume down button
+} KeyboardKey;
+
+// Add backwards compatibility support for deprecated names
+#define MOUSE_LEFT_BUTTON MOUSE_BUTTON_LEFT
+#define MOUSE_RIGHT_BUTTON MOUSE_BUTTON_RIGHT
+#define MOUSE_MIDDLE_BUTTON MOUSE_BUTTON_MIDDLE
+
+// Mouse buttons
+typedef enum {
+ MOUSE_BUTTON_LEFT = 0, // Mouse button left
+ MOUSE_BUTTON_RIGHT = 1, // Mouse button right
+ MOUSE_BUTTON_MIDDLE = 2, // Mouse button middle (pressed wheel)
+ MOUSE_BUTTON_SIDE = 3, // Mouse button side (advanced mouse device)
+ MOUSE_BUTTON_EXTRA = 4, // Mouse button extra (advanced mouse device)
+ MOUSE_BUTTON_FORWARD = 5, // Mouse button forward (advanced mouse device)
+ MOUSE_BUTTON_BACK = 6, // Mouse button back (advanced mouse device)
+} MouseButton;
+
+// Mouse cursor
+typedef enum {
+ MOUSE_CURSOR_DEFAULT = 0, // Default pointer shape
+ MOUSE_CURSOR_ARROW = 1, // Arrow shape
+ MOUSE_CURSOR_IBEAM = 2, // Text writing cursor shape
+ MOUSE_CURSOR_CROSSHAIR = 3, // Cross shape
+ MOUSE_CURSOR_POINTING_HAND = 4, // Pointing hand cursor
+ MOUSE_CURSOR_RESIZE_EW = 5, // Horizontal resize/move arrow shape
+ MOUSE_CURSOR_RESIZE_NS = 6, // Vertical resize/move arrow shape
+ MOUSE_CURSOR_RESIZE_NWSE = 7, // Top-left to bottom-right diagonal resize/move arrow shape
+ MOUSE_CURSOR_RESIZE_NESW = 8, // The top-right to bottom-left diagonal resize/move arrow shape
+ MOUSE_CURSOR_RESIZE_ALL = 9, // The omnidirectional resize/move cursor shape
+ MOUSE_CURSOR_NOT_ALLOWED = 10 // The operation-not-allowed shape
+} MouseCursor;
+
+// Gamepad buttons
+typedef enum {
+ GAMEPAD_BUTTON_UNKNOWN = 0, // Unknown button, just for error checking
+ GAMEPAD_BUTTON_LEFT_FACE_UP, // Gamepad left DPAD up button
+ GAMEPAD_BUTTON_LEFT_FACE_RIGHT, // Gamepad left DPAD right button
+ GAMEPAD_BUTTON_LEFT_FACE_DOWN, // Gamepad left DPAD down button
+ GAMEPAD_BUTTON_LEFT_FACE_LEFT, // Gamepad left DPAD left button
+ GAMEPAD_BUTTON_RIGHT_FACE_UP, // Gamepad right button up (i.e. PS3: Triangle, Xbox: Y)
+ GAMEPAD_BUTTON_RIGHT_FACE_RIGHT, // Gamepad right button right (i.e. PS3: Circle, Xbox: B)
+ GAMEPAD_BUTTON_RIGHT_FACE_DOWN, // Gamepad right button down (i.e. PS3: Cross, Xbox: A)
+ GAMEPAD_BUTTON_RIGHT_FACE_LEFT, // Gamepad right button left (i.e. PS3: Square, Xbox: X)
+ GAMEPAD_BUTTON_LEFT_TRIGGER_1, // Gamepad top/back trigger left (first), it could be a trailing button
+ GAMEPAD_BUTTON_LEFT_TRIGGER_2, // Gamepad top/back trigger left (second), it could be a trailing button
+ GAMEPAD_BUTTON_RIGHT_TRIGGER_1, // Gamepad top/back trigger right (first), it could be a trailing button
+ GAMEPAD_BUTTON_RIGHT_TRIGGER_2, // Gamepad top/back trigger right (second), it could be a trailing button
+ GAMEPAD_BUTTON_MIDDLE_LEFT, // Gamepad center buttons, left one (i.e. PS3: Select)
+ GAMEPAD_BUTTON_MIDDLE, // Gamepad center buttons, middle one (i.e. PS3: PS, Xbox: XBOX)
+ GAMEPAD_BUTTON_MIDDLE_RIGHT, // Gamepad center buttons, right one (i.e. PS3: Start)
+ GAMEPAD_BUTTON_LEFT_THUMB, // Gamepad joystick pressed button left
+ GAMEPAD_BUTTON_RIGHT_THUMB // Gamepad joystick pressed button right
+} GamepadButton;
+
+// Gamepad axis
+typedef enum {
+ GAMEPAD_AXIS_LEFT_X = 0, // Gamepad left stick X axis
+ GAMEPAD_AXIS_LEFT_Y = 1, // Gamepad left stick Y axis
+ GAMEPAD_AXIS_RIGHT_X = 2, // Gamepad right stick X axis
+ GAMEPAD_AXIS_RIGHT_Y = 3, // Gamepad right stick Y axis
+ GAMEPAD_AXIS_LEFT_TRIGGER = 4, // Gamepad back trigger left, pressure level: [1..-1]
+ GAMEPAD_AXIS_RIGHT_TRIGGER = 5 // Gamepad back trigger right, pressure level: [1..-1]
+} GamepadAxis;
+
+// Material map index
+typedef enum {
+ MATERIAL_MAP_ALBEDO = 0, // Albedo material (same as: MATERIAL_MAP_DIFFUSE)
+ MATERIAL_MAP_METALNESS, // Metalness material (same as: MATERIAL_MAP_SPECULAR)
+ MATERIAL_MAP_NORMAL, // Normal material
+ MATERIAL_MAP_ROUGHNESS, // Roughness material
+ MATERIAL_MAP_OCCLUSION, // Ambient occlusion material
+ MATERIAL_MAP_EMISSION, // Emission material
+ MATERIAL_MAP_HEIGHT, // Heightmap material
+ MATERIAL_MAP_CUBEMAP, // Cubemap material (NOTE: Uses GL_TEXTURE_CUBE_MAP)
+ MATERIAL_MAP_IRRADIANCE, // Irradiance material (NOTE: Uses GL_TEXTURE_CUBE_MAP)
+ MATERIAL_MAP_PREFILTER, // Prefilter material (NOTE: Uses GL_TEXTURE_CUBE_MAP)
+ MATERIAL_MAP_BRDF // Brdf material
+} MaterialMapIndex;
+
+#define MATERIAL_MAP_DIFFUSE MATERIAL_MAP_ALBEDO
+#define MATERIAL_MAP_SPECULAR MATERIAL_MAP_METALNESS
+
+// Shader location index
+typedef enum {
+ SHADER_LOC_VERTEX_POSITION = 0, // Shader location: vertex attribute: position
+ SHADER_LOC_VERTEX_TEXCOORD01, // Shader location: vertex attribute: texcoord01
+ SHADER_LOC_VERTEX_TEXCOORD02, // Shader location: vertex attribute: texcoord02
+ SHADER_LOC_VERTEX_NORMAL, // Shader location: vertex attribute: normal
+ SHADER_LOC_VERTEX_TANGENT, // Shader location: vertex attribute: tangent
+ SHADER_LOC_VERTEX_COLOR, // Shader location: vertex attribute: color
+ SHADER_LOC_MATRIX_MVP, // Shader location: matrix uniform: model-view-projection
+ SHADER_LOC_MATRIX_VIEW, // Shader location: matrix uniform: view (camera transform)
+ SHADER_LOC_MATRIX_PROJECTION, // Shader location: matrix uniform: projection
+ SHADER_LOC_MATRIX_MODEL, // Shader location: matrix uniform: model (transform)
+ SHADER_LOC_MATRIX_NORMAL, // Shader location: matrix uniform: normal
+ SHADER_LOC_VECTOR_VIEW, // Shader location: vector uniform: view
+ SHADER_LOC_COLOR_DIFFUSE, // Shader location: vector uniform: diffuse color
+ SHADER_LOC_COLOR_SPECULAR, // Shader location: vector uniform: specular color
+ SHADER_LOC_COLOR_AMBIENT, // Shader location: vector uniform: ambient color
+ SHADER_LOC_MAP_ALBEDO, // Shader location: sampler2d texture: albedo (same as: SHADER_LOC_MAP_DIFFUSE)
+ SHADER_LOC_MAP_METALNESS, // Shader location: sampler2d texture: metalness (same as: SHADER_LOC_MAP_SPECULAR)
+ SHADER_LOC_MAP_NORMAL, // Shader location: sampler2d texture: normal
+ SHADER_LOC_MAP_ROUGHNESS, // Shader location: sampler2d texture: roughness
+ SHADER_LOC_MAP_OCCLUSION, // Shader location: sampler2d texture: occlusion
+ SHADER_LOC_MAP_EMISSION, // Shader location: sampler2d texture: emission
+ SHADER_LOC_MAP_HEIGHT, // Shader location: sampler2d texture: height
+ SHADER_LOC_MAP_CUBEMAP, // Shader location: samplerCube texture: cubemap
+ SHADER_LOC_MAP_IRRADIANCE, // Shader location: samplerCube texture: irradiance
+ SHADER_LOC_MAP_PREFILTER, // Shader location: samplerCube texture: prefilter
+ SHADER_LOC_MAP_BRDF, // Shader location: sampler2d texture: brdf
+ SHADER_LOC_VERTEX_BONEIDS, // Shader location: vertex attribute: boneIds
+ SHADER_LOC_VERTEX_BONEWEIGHTS, // Shader location: vertex attribute: boneWeights
+ SHADER_LOC_BONE_MATRICES, // Shader location: array of matrices uniform: boneMatrices
+ SHADER_LOC_VERTEX_INSTANCE_TX // Shader location: vertex attribute: instanceTransform
+} ShaderLocationIndex;
+
+#define SHADER_LOC_MAP_DIFFUSE SHADER_LOC_MAP_ALBEDO
+#define SHADER_LOC_MAP_SPECULAR SHADER_LOC_MAP_METALNESS
+
+// Shader uniform data type
+typedef enum {
+ SHADER_UNIFORM_FLOAT = 0, // Shader uniform type: float
+ SHADER_UNIFORM_VEC2, // Shader uniform type: vec2 (2 float)
+ SHADER_UNIFORM_VEC3, // Shader uniform type: vec3 (3 float)
+ SHADER_UNIFORM_VEC4, // Shader uniform type: vec4 (4 float)
+ SHADER_UNIFORM_INT, // Shader uniform type: int
+ SHADER_UNIFORM_IVEC2, // Shader uniform type: ivec2 (2 int)
+ SHADER_UNIFORM_IVEC3, // Shader uniform type: ivec3 (3 int)
+ SHADER_UNIFORM_IVEC4, // Shader uniform type: ivec4 (4 int)
+ SHADER_UNIFORM_UINT, // Shader uniform type: unsigned int
+ SHADER_UNIFORM_UIVEC2, // Shader uniform type: uivec2 (2 unsigned int)
+ SHADER_UNIFORM_UIVEC3, // Shader uniform type: uivec3 (3 unsigned int)
+ SHADER_UNIFORM_UIVEC4, // Shader uniform type: uivec4 (4 unsigned int)
+ SHADER_UNIFORM_SAMPLER2D // Shader uniform type: sampler2d
+} ShaderUniformDataType;
+
+// Shader attribute data types
+typedef enum {
+ SHADER_ATTRIB_FLOAT = 0, // Shader attribute type: float
+ SHADER_ATTRIB_VEC2, // Shader attribute type: vec2 (2 float)
+ SHADER_ATTRIB_VEC3, // Shader attribute type: vec3 (3 float)
+ SHADER_ATTRIB_VEC4 // Shader attribute type: vec4 (4 float)
+} ShaderAttributeDataType;
+
+// Pixel formats
+// NOTE: Support depends on OpenGL version and platform
+typedef enum {
+ PIXELFORMAT_UNCOMPRESSED_GRAYSCALE = 1, // 8 bit per pixel (no alpha)
+ PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA, // 8*2 bpp (2 channels)
+ PIXELFORMAT_UNCOMPRESSED_R5G6B5, // 16 bpp
+ PIXELFORMAT_UNCOMPRESSED_R8G8B8, // 24 bpp
+ PIXELFORMAT_UNCOMPRESSED_R5G5B5A1, // 16 bpp (1 bit alpha)
+ PIXELFORMAT_UNCOMPRESSED_R4G4B4A4, // 16 bpp (4 bit alpha)
+ PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, // 32 bpp
+ PIXELFORMAT_UNCOMPRESSED_R32, // 32 bpp (1 channel - float)
+ PIXELFORMAT_UNCOMPRESSED_R32G32B32, // 32*3 bpp (3 channels - float)
+ PIXELFORMAT_UNCOMPRESSED_R32G32B32A32, // 32*4 bpp (4 channels - float)
+ PIXELFORMAT_UNCOMPRESSED_R16, // 16 bpp (1 channel - half float)
+ PIXELFORMAT_UNCOMPRESSED_R16G16B16, // 16*3 bpp (3 channels - half float)
+ PIXELFORMAT_UNCOMPRESSED_R16G16B16A16, // 16*4 bpp (4 channels - half float)
+ PIXELFORMAT_COMPRESSED_DXT1_RGB, // 4 bpp (no alpha)
+ PIXELFORMAT_COMPRESSED_DXT1_RGBA, // 4 bpp (1 bit alpha)
+ PIXELFORMAT_COMPRESSED_DXT3_RGBA, // 8 bpp
+ PIXELFORMAT_COMPRESSED_DXT5_RGBA, // 8 bpp
+ PIXELFORMAT_COMPRESSED_ETC1_RGB, // 4 bpp
+ PIXELFORMAT_COMPRESSED_ETC2_RGB, // 4 bpp
+ PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA, // 8 bpp
+ PIXELFORMAT_COMPRESSED_PVRT_RGB, // 4 bpp
+ PIXELFORMAT_COMPRESSED_PVRT_RGBA, // 4 bpp
+ PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA, // 8 bpp
+ PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA // 2 bpp
+} PixelFormat;
+
+// Texture parameters: filter mode
+// NOTE 1: Filtering considers mipmaps if available in the texture
+// NOTE 2: Filter is accordingly set for minification and magnification
+typedef enum {
+ TEXTURE_FILTER_POINT = 0, // No filter, just pixel approximation
+ TEXTURE_FILTER_BILINEAR, // Linear filtering
+ TEXTURE_FILTER_TRILINEAR, // Trilinear filtering (linear with mipmaps)
+ TEXTURE_FILTER_ANISOTROPIC_4X, // Anisotropic filtering 4x
+ TEXTURE_FILTER_ANISOTROPIC_8X, // Anisotropic filtering 8x
+ TEXTURE_FILTER_ANISOTROPIC_16X, // Anisotropic filtering 16x
+} TextureFilter;
+
+// Texture parameters: wrap mode
+typedef enum {
+ TEXTURE_WRAP_REPEAT = 0, // Repeats texture in tiled mode
+ TEXTURE_WRAP_CLAMP, // Clamps texture to edge pixel in tiled mode
+ TEXTURE_WRAP_MIRROR_REPEAT, // Mirrors and repeats the texture in tiled mode
+ TEXTURE_WRAP_MIRROR_CLAMP // Mirrors and clamps to border the texture in tiled mode
+} TextureWrap;
+
+// Cubemap layouts
+typedef enum {
+ CUBEMAP_LAYOUT_AUTO_DETECT = 0, // Automatically detect layout type
+ CUBEMAP_LAYOUT_LINE_VERTICAL, // Layout is defined by a vertical line with faces
+ CUBEMAP_LAYOUT_LINE_HORIZONTAL, // Layout is defined by a horizontal line with faces
+ CUBEMAP_LAYOUT_CROSS_THREE_BY_FOUR, // Layout is defined by a 3x4 cross with cubemap faces
+ CUBEMAP_LAYOUT_CROSS_FOUR_BY_THREE // Layout is defined by a 4x3 cross with cubemap faces
+} CubemapLayout;
+
+// Font type, defines generation method
+typedef enum {
+ FONT_DEFAULT = 0, // Default font generation, anti-aliased
+ FONT_BITMAP, // Bitmap font generation, no anti-aliasing
+ FONT_SDF // SDF font generation, requires external shader
+} FontType;
+
+// Color blending modes (pre-defined)
+typedef enum {
+ BLEND_ALPHA = 0, // Blend textures considering alpha (default)
+ BLEND_ADDITIVE, // Blend textures adding colors
+ BLEND_MULTIPLIED, // Blend textures multiplying colors
+ BLEND_ADD_COLORS, // Blend textures adding colors (alternative)
+ BLEND_SUBTRACT_COLORS, // Blend textures subtracting colors (alternative)
+ BLEND_ALPHA_PREMULTIPLY, // Blend premultiplied textures considering alpha
+ BLEND_CUSTOM, // Blend textures using custom src/dst factors (use rlSetBlendFactors())
+ BLEND_CUSTOM_SEPARATE // Blend textures using custom rgb/alpha separate src/dst factors (use rlSetBlendFactorsSeparate())
+} BlendMode;
+
+// Gesture
+// NOTE: Provided as bit-wise flags to enable only desired gestures
+typedef enum {
+ GESTURE_NONE = 0, // No gesture
+ GESTURE_TAP = 1, // Tap gesture
+ GESTURE_DOUBLETAP = 2, // Double tap gesture
+ GESTURE_HOLD = 4, // Hold gesture
+ GESTURE_DRAG = 8, // Drag gesture
+ GESTURE_SWIPE_RIGHT = 16, // Swipe right gesture
+ GESTURE_SWIPE_LEFT = 32, // Swipe left gesture
+ GESTURE_SWIPE_UP = 64, // Swipe up gesture
+ GESTURE_SWIPE_DOWN = 128, // Swipe down gesture
+ GESTURE_PINCH_IN = 256, // Pinch in gesture
+ GESTURE_PINCH_OUT = 512 // Pinch out gesture
+} Gesture;
+
+// Camera system modes
+typedef enum {
+ CAMERA_CUSTOM = 0, // Camera custom, controlled by user (UpdateCamera() does nothing)
+ CAMERA_FREE, // Camera free mode
+ CAMERA_ORBITAL, // Camera orbital, around target, zoom supported
+ CAMERA_FIRST_PERSON, // Camera first person
+ CAMERA_THIRD_PERSON // Camera third person
+} CameraMode;
+
+// Camera projection
+typedef enum {
+ CAMERA_PERSPECTIVE = 0, // Perspective projection
+ CAMERA_ORTHOGRAPHIC // Orthographic projection
+} CameraProjection;
+
+// N-patch layout
+typedef enum {
+ NPATCH_NINE_PATCH = 0, // Npatch layout: 3x3 tiles
+ NPATCH_THREE_PATCH_VERTICAL, // Npatch layout: 1x3 tiles
+ NPATCH_THREE_PATCH_HORIZONTAL // Npatch layout: 3x1 tiles
+} NPatchLayout;
+
+// Callbacks to hook some internal functions
+// WARNING: These callbacks are intended for advanced users
+typedef void (*TraceLogCallback)(int logLevel, const char *text, va_list args); // Logging: Redirect trace log messages
+typedef unsigned char *(*LoadFileDataCallback)(const char *fileName, int *dataSize); // FileIO: Load binary data
+typedef bool (*SaveFileDataCallback)(const char *fileName, void *data, int dataSize); // FileIO: Save binary data
+typedef char *(*LoadFileTextCallback)(const char *fileName); // FileIO: Load text data
+typedef bool (*SaveFileTextCallback)(const char *fileName, char *text); // FileIO: Save text data
+
+//------------------------------------------------------------------------------------
+// Global Variables Definition
+//------------------------------------------------------------------------------------
+// It's lonely here...
+
+//------------------------------------------------------------------------------------
+// Window and Graphics Device Functions (Module: core)
+//------------------------------------------------------------------------------------
+
+#if defined(__cplusplus)
+extern "C" { // Prevents name mangling of functions
+#endif
+
+// Window-related functions
+RLAPI void InitWindow(int width, int height, const char *title); // Initialize window and OpenGL context
+RLAPI void CloseWindow(void); // Close window and unload OpenGL context
+RLAPI bool WindowShouldClose(void); // Check if application should close (KEY_ESCAPE pressed or windows close icon clicked)
+RLAPI bool IsWindowReady(void); // Check if window has been initialized successfully
+RLAPI bool IsWindowFullscreen(void); // Check if window is currently fullscreen
+RLAPI bool IsWindowHidden(void); // Check if window is currently hidden
+RLAPI bool IsWindowMinimized(void); // Check if window is currently minimized
+RLAPI bool IsWindowMaximized(void); // Check if window is currently maximized
+RLAPI bool IsWindowFocused(void); // Check if window is currently focused
+RLAPI bool IsWindowResized(void); // Check if window has been resized last frame
+RLAPI bool IsWindowState(unsigned int flag); // Check if one specific window flag is enabled
+RLAPI void SetWindowState(unsigned int flags); // Set window configuration state using flags
+RLAPI void ClearWindowState(unsigned int flags); // Clear window configuration state flags
+RLAPI void ToggleFullscreen(void); // Toggle window state: fullscreen/windowed, resizes monitor to match window resolution
+RLAPI void ToggleBorderlessWindowed(void); // Toggle window state: borderless windowed, resizes window to match monitor resolution
+RLAPI void MaximizeWindow(void); // Set window state: maximized, if resizable
+RLAPI void MinimizeWindow(void); // Set window state: minimized, if resizable
+RLAPI void RestoreWindow(void); // Set window state: not minimized/maximized
+RLAPI void SetWindowIcon(Image image); // Set icon for window (single image, RGBA 32bit)
+RLAPI void SetWindowIcons(Image *images, int count); // Set icon for window (multiple images, RGBA 32bit)
+RLAPI void SetWindowTitle(const char *title); // Set title for window
+RLAPI void SetWindowPosition(int x, int y); // Set window position on screen
+RLAPI void SetWindowMonitor(int monitor); // Set monitor for the current window
+RLAPI void SetWindowMinSize(int width, int height); // Set window minimum dimensions (for FLAG_WINDOW_RESIZABLE)
+RLAPI void SetWindowMaxSize(int width, int height); // Set window maximum dimensions (for FLAG_WINDOW_RESIZABLE)
+RLAPI void SetWindowSize(int width, int height); // Set window dimensions
+RLAPI void SetWindowOpacity(float opacity); // Set window opacity [0.0f..1.0f]
+RLAPI void SetWindowFocused(void); // Set window focused
+RLAPI void *GetWindowHandle(void); // Get native window handle
+RLAPI int GetScreenWidth(void); // Get current screen width
+RLAPI int GetScreenHeight(void); // Get current screen height
+RLAPI int GetRenderWidth(void); // Get current render width (it considers HiDPI)
+RLAPI int GetRenderHeight(void); // Get current render height (it considers HiDPI)
+RLAPI int GetMonitorCount(void); // Get number of connected monitors
+RLAPI int GetCurrentMonitor(void); // Get current monitor where window is placed
+RLAPI Vector2 GetMonitorPosition(int monitor); // Get specified monitor position
+RLAPI int GetMonitorWidth(int monitor); // Get specified monitor width (current video mode used by monitor)
+RLAPI int GetMonitorHeight(int monitor); // Get specified monitor height (current video mode used by monitor)
+RLAPI int GetMonitorPhysicalWidth(int monitor); // Get specified monitor physical width in millimetres
+RLAPI int GetMonitorPhysicalHeight(int monitor); // Get specified monitor physical height in millimetres
+RLAPI int GetMonitorRefreshRate(int monitor); // Get specified monitor refresh rate
+RLAPI Vector2 GetWindowPosition(void); // Get window position XY on monitor
+RLAPI Vector2 GetWindowScaleDPI(void); // Get window scale DPI factor
+RLAPI const char *GetMonitorName(int monitor); // Get the human-readable, UTF-8 encoded name of the specified monitor
+RLAPI void SetClipboardText(const char *text); // Set clipboard text content
+RLAPI const char *GetClipboardText(void); // Get clipboard text content
+RLAPI Image GetClipboardImage(void); // Get clipboard image content
+RLAPI void EnableEventWaiting(void); // Enable waiting for events on EndDrawing(), no automatic event polling
+RLAPI void DisableEventWaiting(void); // Disable waiting for events on EndDrawing(), automatic events polling
+
+// Cursor-related functions
+RLAPI void ShowCursor(void); // Shows cursor
+RLAPI void HideCursor(void); // Hides cursor
+RLAPI bool IsCursorHidden(void); // Check if cursor is not visible
+RLAPI void EnableCursor(void); // Enables cursor (unlock cursor)
+RLAPI void DisableCursor(void); // Disables cursor (lock cursor)
+RLAPI bool IsCursorOnScreen(void); // Check if cursor is on the screen
+
+// Drawing-related functions
+RLAPI void ClearBackground(Color color); // Set background color (framebuffer clear color)
+RLAPI void BeginDrawing(void); // Setup canvas (framebuffer) to start drawing
+RLAPI void EndDrawing(void); // End canvas drawing and swap buffers (double buffering)
+RLAPI void BeginMode2D(Camera2D camera); // Begin 2D mode with custom camera (2D)
+RLAPI void EndMode2D(void); // Ends 2D mode with custom camera
+RLAPI void BeginMode3D(Camera3D camera); // Begin 3D mode with custom camera (3D)
+RLAPI void EndMode3D(void); // Ends 3D mode and returns to default 2D orthographic mode
+RLAPI void BeginTextureMode(RenderTexture2D target); // Begin drawing to render texture
+RLAPI void EndTextureMode(void); // Ends drawing to render texture
+RLAPI void BeginShaderMode(Shader shader); // Begin custom shader drawing
+RLAPI void EndShaderMode(void); // End custom shader drawing (use default shader)
+RLAPI void BeginBlendMode(int mode); // Begin blending mode (alpha, additive, multiplied, subtract, custom)
+RLAPI void EndBlendMode(void); // End blending mode (reset to default: alpha blending)
+RLAPI void BeginScissorMode(int x, int y, int width, int height); // Begin scissor mode (define screen area for following drawing)
+RLAPI void EndScissorMode(void); // End scissor mode
+RLAPI void BeginVrStereoMode(VrStereoConfig config); // Begin stereo rendering (requires VR simulator)
+RLAPI void EndVrStereoMode(void); // End stereo rendering (requires VR simulator)
+
+// VR stereo config functions for VR simulator
+RLAPI VrStereoConfig LoadVrStereoConfig(VrDeviceInfo device); // Load VR stereo config for VR simulator device parameters
+RLAPI void UnloadVrStereoConfig(VrStereoConfig config); // Unload VR stereo config
+
+// Shader management functions
+// NOTE: Shader functionality is not available on OpenGL 1.1
+RLAPI Shader LoadShader(const char *vsFileName, const char *fsFileName); // Load shader from files and bind default locations
+RLAPI Shader LoadShaderFromMemory(const char *vsCode, const char *fsCode); // Load shader from code strings and bind default locations
+RLAPI bool IsShaderValid(Shader shader); // Check if a shader is valid (loaded on GPU)
+RLAPI int GetShaderLocation(Shader shader, const char *uniformName); // Get shader uniform location
+RLAPI int GetShaderLocationAttrib(Shader shader, const char *attribName); // Get shader attribute location
+RLAPI void SetShaderValue(Shader shader, int locIndex, const void *value, int uniformType); // Set shader uniform value
+RLAPI void SetShaderValueV(Shader shader, int locIndex, const void *value, int uniformType, int count); // Set shader uniform value vector
+RLAPI void SetShaderValueMatrix(Shader shader, int locIndex, Matrix mat); // Set shader uniform value (matrix 4x4)
+RLAPI void SetShaderValueTexture(Shader shader, int locIndex, Texture2D texture); // Set shader uniform value and bind the texture (sampler2d)
+RLAPI void UnloadShader(Shader shader); // Unload shader from GPU memory (VRAM)
+
+// Screen-space-related functions
+#define GetMouseRay GetScreenToWorldRay // Compatibility hack for previous raylib versions
+RLAPI Ray GetScreenToWorldRay(Vector2 position, Camera camera); // Get a ray trace from screen position (i.e mouse)
+RLAPI Ray GetScreenToWorldRayEx(Vector2 position, Camera camera, int width, int height); // Get a ray trace from screen position (i.e mouse) in a viewport
+RLAPI Vector2 GetWorldToScreen(Vector3 position, Camera camera); // Get the screen space position for a 3d world space position
+RLAPI Vector2 GetWorldToScreenEx(Vector3 position, Camera camera, int width, int height); // Get size position for a 3d world space position
+RLAPI Vector2 GetWorldToScreen2D(Vector2 position, Camera2D camera); // Get the screen space position for a 2d camera world space position
+RLAPI Vector2 GetScreenToWorld2D(Vector2 position, Camera2D camera); // Get the world space position for a 2d camera screen space position
+RLAPI Matrix GetCameraMatrix(Camera camera); // Get camera transform matrix (view matrix)
+RLAPI Matrix GetCameraMatrix2D(Camera2D camera); // Get camera 2d transform matrix
+
+// Timing-related functions
+RLAPI void SetTargetFPS(int fps); // Set target FPS (maximum)
+RLAPI float GetFrameTime(void); // Get time in seconds for last frame drawn (delta time)
+RLAPI double GetTime(void); // Get elapsed time in seconds since InitWindow()
+RLAPI int GetFPS(void); // Get current FPS
+
+// Custom frame control functions
+// NOTE: Those functions are intended for advanced users that want full control over the frame processing
+// By default EndDrawing() does this job: draws everything + SwapScreenBuffer() + manage frame timing + PollInputEvents()
+// To avoid that behaviour and control frame processes manually, enable in config.h: SUPPORT_CUSTOM_FRAME_CONTROL
+RLAPI void SwapScreenBuffer(void); // Swap back buffer with front buffer (screen drawing)
+RLAPI void PollInputEvents(void); // Register all input events
+RLAPI void WaitTime(double seconds); // Wait for some time (halt program execution)
+
+// Random values generation functions
+RLAPI void SetRandomSeed(unsigned int seed); // Set the seed for the random number generator
+RLAPI int GetRandomValue(int min, int max); // Get a random value between min and max (both included)
+RLAPI int *LoadRandomSequence(unsigned int count, int min, int max); // Load random values sequence, no values repeated
+RLAPI void UnloadRandomSequence(int *sequence); // Unload random values sequence
+
+// Misc. functions
+RLAPI void TakeScreenshot(const char *fileName); // Takes a screenshot of current screen (filename extension defines format)
+RLAPI void SetConfigFlags(unsigned int flags); // Setup init configuration flags (view FLAGS)
+RLAPI void OpenURL(const char *url); // Open URL with default system browser (if available)
+
+// NOTE: Following functions implemented in module [utils]
+//------------------------------------------------------------------
+RLAPI void TraceLog(int logLevel, const char *text, ...); // Show trace log messages (LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERROR...)
+RLAPI void SetTraceLogLevel(int logLevel); // Set the current threshold (minimum) log level
+RLAPI void *MemAlloc(unsigned int size); // Internal memory allocator
+RLAPI void *MemRealloc(void *ptr, unsigned int size); // Internal memory reallocator
+RLAPI void MemFree(void *ptr); // Internal memory free
+
+// Set custom callbacks
+// WARNING: Callbacks setup is intended for advanced users
+RLAPI void SetTraceLogCallback(TraceLogCallback callback); // Set custom trace log
+RLAPI void SetLoadFileDataCallback(LoadFileDataCallback callback); // Set custom file binary data loader
+RLAPI void SetSaveFileDataCallback(SaveFileDataCallback callback); // Set custom file binary data saver
+RLAPI void SetLoadFileTextCallback(LoadFileTextCallback callback); // Set custom file text data loader
+RLAPI void SetSaveFileTextCallback(SaveFileTextCallback callback); // Set custom file text data saver
+
+// Files management functions
+RLAPI unsigned char *LoadFileData(const char *fileName, int *dataSize); // Load file data as byte array (read)
+RLAPI void UnloadFileData(unsigned char *data); // Unload file data allocated by LoadFileData()
+RLAPI bool SaveFileData(const char *fileName, void *data, int dataSize); // Save data to file from byte array (write), returns true on success
+RLAPI bool ExportDataAsCode(const unsigned char *data, int dataSize, const char *fileName); // Export data to code (.h), returns true on success
+RLAPI char *LoadFileText(const char *fileName); // Load text data from file (read), returns a '\0' terminated string
+RLAPI void UnloadFileText(char *text); // Unload file text data allocated by LoadFileText()
+RLAPI bool SaveFileText(const char *fileName, char *text); // Save text data to file (write), string must be '\0' terminated, returns true on success
+//------------------------------------------------------------------
+
+// File system functions
+RLAPI bool FileExists(const char *fileName); // Check if file exists
+RLAPI bool DirectoryExists(const char *dirPath); // Check if a directory path exists
+RLAPI bool IsFileExtension(const char *fileName, const char *ext); // Check file extension (including point: .png, .wav)
+RLAPI int GetFileLength(const char *fileName); // Get file length in bytes (NOTE: GetFileSize() conflicts with windows.h)
+RLAPI const char *GetFileExtension(const char *fileName); // Get pointer to extension for a filename string (includes dot: '.png')
+RLAPI const char *GetFileName(const char *filePath); // Get pointer to filename for a path string
+RLAPI const char *GetFileNameWithoutExt(const char *filePath); // Get filename string without extension (uses static string)
+RLAPI const char *GetDirectoryPath(const char *filePath); // Get full path for a given fileName with path (uses static string)
+RLAPI const char *GetPrevDirectoryPath(const char *dirPath); // Get previous directory path for a given path (uses static string)
+RLAPI const char *GetWorkingDirectory(void); // Get current working directory (uses static string)
+RLAPI const char *GetApplicationDirectory(void); // Get the directory of the running application (uses static string)
+RLAPI int MakeDirectory(const char *dirPath); // Create directories (including full path requested), returns 0 on success
+RLAPI bool ChangeDirectory(const char *dir); // Change working directory, return true on success
+RLAPI bool IsPathFile(const char *path); // Check if a given path is a file or a directory
+RLAPI bool IsFileNameValid(const char *fileName); // Check if fileName is valid for the platform/OS
+RLAPI FilePathList LoadDirectoryFiles(const char *dirPath); // Load directory filepaths
+RLAPI FilePathList LoadDirectoryFilesEx(const char *basePath, const char *filter, bool scanSubdirs); // Load directory filepaths with extension filtering and recursive directory scan. Use 'DIR' in the filter string to include directories in the result
+RLAPI void UnloadDirectoryFiles(FilePathList files); // Unload filepaths
+RLAPI bool IsFileDropped(void); // Check if a file has been dropped into window
+RLAPI FilePathList LoadDroppedFiles(void); // Load dropped filepaths
+RLAPI void UnloadDroppedFiles(FilePathList files); // Unload dropped filepaths
+RLAPI long GetFileModTime(const char *fileName); // Get file modification time (last write time)
+
+// Compression/Encoding functionality
+RLAPI unsigned char *CompressData(const unsigned char *data, int dataSize, int *compDataSize); // Compress data (DEFLATE algorithm), memory must be MemFree()
+RLAPI unsigned char *DecompressData(const unsigned char *compData, int compDataSize, int *dataSize); // Decompress data (DEFLATE algorithm), memory must be MemFree()
+RLAPI char *EncodeDataBase64(const unsigned char *data, int dataSize, int *outputSize); // Encode data to Base64 string, memory must be MemFree()
+RLAPI unsigned char *DecodeDataBase64(const unsigned char *data, int *outputSize); // Decode Base64 string data, memory must be MemFree()
+RLAPI unsigned int ComputeCRC32(unsigned char *data, int dataSize); // Compute CRC32 hash code
+RLAPI unsigned int *ComputeMD5(unsigned char *data, int dataSize); // Compute MD5 hash code, returns static int[4] (16 bytes)
+RLAPI unsigned int *ComputeSHA1(unsigned char *data, int dataSize); // Compute SHA1 hash code, returns static int[5] (20 bytes)
+
+// Automation events functionality
+RLAPI AutomationEventList LoadAutomationEventList(const char *fileName); // Load automation events list from file, NULL for empty list, capacity = MAX_AUTOMATION_EVENTS
+RLAPI void UnloadAutomationEventList(AutomationEventList list); // Unload automation events list from file
+RLAPI bool ExportAutomationEventList(AutomationEventList list, const char *fileName); // Export automation events list as text file
+RLAPI void SetAutomationEventList(AutomationEventList *list); // Set automation event list to record to
+RLAPI void SetAutomationEventBaseFrame(int frame); // Set automation event internal base frame to start recording
+RLAPI void StartAutomationEventRecording(void); // Start recording automation events (AutomationEventList must be set)
+RLAPI void StopAutomationEventRecording(void); // Stop recording automation events
+RLAPI void PlayAutomationEvent(AutomationEvent event); // Play a recorded automation event
+
+//------------------------------------------------------------------------------------
+// Input Handling Functions (Module: core)
+//------------------------------------------------------------------------------------
+
+// Input-related functions: keyboard
+RLAPI bool IsKeyPressed(int key); // Check if a key has been pressed once
+RLAPI bool IsKeyPressedRepeat(int key); // Check if a key has been pressed again
+RLAPI bool IsKeyDown(int key); // Check if a key is being pressed
+RLAPI bool IsKeyReleased(int key); // Check if a key has been released once
+RLAPI bool IsKeyUp(int key); // Check if a key is NOT being pressed
+RLAPI int GetKeyPressed(void); // Get key pressed (keycode), call it multiple times for keys queued, returns 0 when the queue is empty
+RLAPI int GetCharPressed(void); // Get char pressed (unicode), call it multiple times for chars queued, returns 0 when the queue is empty
+RLAPI const char *GetKeyName(int key); // Get name of a QWERTY key on the current keyboard layout (eg returns string 'q' for KEY_A on an AZERTY keyboard)
+RLAPI void SetExitKey(int key); // Set a custom key to exit program (default is ESC)
+
+// Input-related functions: gamepads
+RLAPI bool IsGamepadAvailable(int gamepad); // Check if a gamepad is available
+RLAPI const char *GetGamepadName(int gamepad); // Get gamepad internal name id
+RLAPI bool IsGamepadButtonPressed(int gamepad, int button); // Check if a gamepad button has been pressed once
+RLAPI bool IsGamepadButtonDown(int gamepad, int button); // Check if a gamepad button is being pressed
+RLAPI bool IsGamepadButtonReleased(int gamepad, int button); // Check if a gamepad button has been released once
+RLAPI bool IsGamepadButtonUp(int gamepad, int button); // Check if a gamepad button is NOT being pressed
+RLAPI int GetGamepadButtonPressed(void); // Get the last gamepad button pressed
+RLAPI int GetGamepadAxisCount(int gamepad); // Get gamepad axis count for a gamepad
+RLAPI float GetGamepadAxisMovement(int gamepad, int axis); // Get axis movement value for a gamepad axis
+RLAPI int SetGamepadMappings(const char *mappings); // Set internal gamepad mappings (SDL_GameControllerDB)
+RLAPI void SetGamepadVibration(int gamepad, float leftMotor, float rightMotor, float duration); // Set gamepad vibration for both motors (duration in seconds)
+
+// Input-related functions: mouse
+RLAPI bool IsMouseButtonPressed(int button); // Check if a mouse button has been pressed once
+RLAPI bool IsMouseButtonDown(int button); // Check if a mouse button is being pressed
+RLAPI bool IsMouseButtonReleased(int button); // Check if a mouse button has been released once
+RLAPI bool IsMouseButtonUp(int button); // Check if a mouse button is NOT being pressed
+RLAPI int GetMouseX(void); // Get mouse position X
+RLAPI int GetMouseY(void); // Get mouse position Y
+RLAPI Vector2 GetMousePosition(void); // Get mouse position XY
+RLAPI Vector2 GetMouseDelta(void); // Get mouse delta between frames
+RLAPI void SetMousePosition(int x, int y); // Set mouse position XY
+RLAPI void SetMouseOffset(int offsetX, int offsetY); // Set mouse offset
+RLAPI void SetMouseScale(float scaleX, float scaleY); // Set mouse scaling
+RLAPI float GetMouseWheelMove(void); // Get mouse wheel movement for X or Y, whichever is larger
+RLAPI Vector2 GetMouseWheelMoveV(void); // Get mouse wheel movement for both X and Y
+RLAPI void SetMouseCursor(int cursor); // Set mouse cursor
+
+// Input-related functions: touch
+RLAPI int GetTouchX(void); // Get touch position X for touch point 0 (relative to screen size)
+RLAPI int GetTouchY(void); // Get touch position Y for touch point 0 (relative to screen size)
+RLAPI Vector2 GetTouchPosition(int index); // Get touch position XY for a touch point index (relative to screen size)
+RLAPI int GetTouchPointId(int index); // Get touch point identifier for given index
+RLAPI int GetTouchPointCount(void); // Get number of touch points
+
+//------------------------------------------------------------------------------------
+// Gestures and Touch Handling Functions (Module: rgestures)
+//------------------------------------------------------------------------------------
+RLAPI void SetGesturesEnabled(unsigned int flags); // Enable a set of gestures using flags
+RLAPI bool IsGestureDetected(unsigned int gesture); // Check if a gesture have been detected
+RLAPI int GetGestureDetected(void); // Get latest detected gesture
+RLAPI float GetGestureHoldDuration(void); // Get gesture hold time in seconds
+RLAPI Vector2 GetGestureDragVector(void); // Get gesture drag vector
+RLAPI float GetGestureDragAngle(void); // Get gesture drag angle
+RLAPI Vector2 GetGesturePinchVector(void); // Get gesture pinch delta
+RLAPI float GetGesturePinchAngle(void); // Get gesture pinch angle
+
+//------------------------------------------------------------------------------------
+// Camera System Functions (Module: rcamera)
+//------------------------------------------------------------------------------------
+RLAPI void UpdateCamera(Camera *camera, int mode); // Update camera position for selected mode
+RLAPI void UpdateCameraPro(Camera *camera, Vector3 movement, Vector3 rotation, float zoom); // Update camera movement/rotation
+
+//------------------------------------------------------------------------------------
+// Basic Shapes Drawing Functions (Module: shapes)
+//------------------------------------------------------------------------------------
+// Set texture and rectangle to be used on shapes drawing
+// NOTE: It can be useful when using basic shapes and one single font,
+// defining a font char white rectangle would allow drawing everything in a single draw call
+RLAPI void SetShapesTexture(Texture2D texture, Rectangle source); // Set texture and rectangle to be used on shapes drawing
+RLAPI Texture2D GetShapesTexture(void); // Get texture that is used for shapes drawing
+RLAPI Rectangle GetShapesTextureRectangle(void); // Get texture source rectangle that is used for shapes drawing
+
+// Basic shapes drawing functions
+RLAPI void DrawPixel(int posX, int posY, Color color); // Draw a pixel using geometry [Can be slow, use with care]
+RLAPI void DrawPixelV(Vector2 position, Color color); // Draw a pixel using geometry (Vector version) [Can be slow, use with care]
+RLAPI void DrawLine(int startPosX, int startPosY, int endPosX, int endPosY, Color color); // Draw a line
+RLAPI void DrawLineV(Vector2 startPos, Vector2 endPos, Color color); // Draw a line (using gl lines)
+RLAPI void DrawLineEx(Vector2 startPos, Vector2 endPos, float thick, Color color); // Draw a line (using triangles/quads)
+RLAPI void DrawLineStrip(const Vector2 *points, int pointCount, Color color); // Draw lines sequence (using gl lines)
+RLAPI void DrawLineBezier(Vector2 startPos, Vector2 endPos, float thick, Color color); // Draw line segment cubic-bezier in-out interpolation
+RLAPI void DrawCircle(int centerX, int centerY, float radius, Color color); // Draw a color-filled circle
+RLAPI void DrawCircleSector(Vector2 center, float radius, float startAngle, float endAngle, int segments, Color color); // Draw a piece of a circle
+RLAPI void DrawCircleSectorLines(Vector2 center, float radius, float startAngle, float endAngle, int segments, Color color); // Draw circle sector outline
+RLAPI void DrawCircleGradient(int centerX, int centerY, float radius, Color inner, Color outer); // Draw a gradient-filled circle
+RLAPI void DrawCircleV(Vector2 center, float radius, Color color); // Draw a color-filled circle (Vector version)
+RLAPI void DrawCircleLines(int centerX, int centerY, float radius, Color color); // Draw circle outline
+RLAPI void DrawCircleLinesV(Vector2 center, float radius, Color color); // Draw circle outline (Vector version)
+RLAPI void DrawEllipse(int centerX, int centerY, float radiusH, float radiusV, Color color); // Draw ellipse
+RLAPI void DrawEllipseLines(int centerX, int centerY, float radiusH, float radiusV, Color color); // Draw ellipse outline
+RLAPI void DrawRing(Vector2 center, float innerRadius, float outerRadius, float startAngle, float endAngle, int segments, Color color); // Draw ring
+RLAPI void DrawRingLines(Vector2 center, float innerRadius, float outerRadius, float startAngle, float endAngle, int segments, Color color); // Draw ring outline
+RLAPI void DrawRectangle(int posX, int posY, int width, int height, Color color); // Draw a color-filled rectangle
+RLAPI void DrawRectangleV(Vector2 position, Vector2 size, Color color); // Draw a color-filled rectangle (Vector version)
+RLAPI void DrawRectangleRec(Rectangle rec, Color color); // Draw a color-filled rectangle
+RLAPI void DrawRectanglePro(Rectangle rec, Vector2 origin, float rotation, Color color); // Draw a color-filled rectangle with pro parameters
+RLAPI void DrawRectangleGradientV(int posX, int posY, int width, int height, Color top, Color bottom); // Draw a vertical-gradient-filled rectangle
+RLAPI void DrawRectangleGradientH(int posX, int posY, int width, int height, Color left, Color right); // Draw a horizontal-gradient-filled rectangle
+RLAPI void DrawRectangleGradientEx(Rectangle rec, Color topLeft, Color bottomLeft, Color topRight, Color bottomRight); // Draw a gradient-filled rectangle with custom vertex colors
+RLAPI void DrawRectangleLines(int posX, int posY, int width, int height, Color color); // Draw rectangle outline
+RLAPI void DrawRectangleLinesEx(Rectangle rec, float lineThick, Color color); // Draw rectangle outline with extended parameters
+RLAPI void DrawRectangleRounded(Rectangle rec, float roundness, int segments, Color color); // Draw rectangle with rounded edges
+RLAPI void DrawRectangleRoundedLines(Rectangle rec, float roundness, int segments, Color color); // Draw rectangle lines with rounded edges
+RLAPI void DrawRectangleRoundedLinesEx(Rectangle rec, float roundness, int segments, float lineThick, Color color); // Draw rectangle with rounded edges outline
+RLAPI void DrawTriangle(Vector2 v1, Vector2 v2, Vector2 v3, Color color); // Draw a color-filled triangle (vertex in counter-clockwise order!)
+RLAPI void DrawTriangleLines(Vector2 v1, Vector2 v2, Vector2 v3, Color color); // Draw triangle outline (vertex in counter-clockwise order!)
+RLAPI void DrawTriangleFan(const Vector2 *points, int pointCount, Color color); // Draw a triangle fan defined by points (first vertex is the center)
+RLAPI void DrawTriangleStrip(const Vector2 *points, int pointCount, Color color); // Draw a triangle strip defined by points
+RLAPI void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color color); // Draw a regular polygon (Vector version)
+RLAPI void DrawPolyLines(Vector2 center, int sides, float radius, float rotation, Color color); // Draw a polygon outline of n sides
+RLAPI void DrawPolyLinesEx(Vector2 center, int sides, float radius, float rotation, float lineThick, Color color); // Draw a polygon outline of n sides with extended parameters
+
+// Splines drawing functions
+RLAPI void DrawSplineLinear(const Vector2 *points, int pointCount, float thick, Color color); // Draw spline: Linear, minimum 2 points
+RLAPI void DrawSplineBasis(const Vector2 *points, int pointCount, float thick, Color color); // Draw spline: B-Spline, minimum 4 points
+RLAPI void DrawSplineCatmullRom(const Vector2 *points, int pointCount, float thick, Color color); // Draw spline: Catmull-Rom, minimum 4 points
+RLAPI void DrawSplineBezierQuadratic(const Vector2 *points, int pointCount, float thick, Color color); // Draw spline: Quadratic Bezier, minimum 3 points (1 control point): [p1, c2, p3, c4...]
+RLAPI void DrawSplineBezierCubic(const Vector2 *points, int pointCount, float thick, Color color); // Draw spline: Cubic Bezier, minimum 4 points (2 control points): [p1, c2, c3, p4, c5, c6...]
+RLAPI void DrawSplineSegmentLinear(Vector2 p1, Vector2 p2, float thick, Color color); // Draw spline segment: Linear, 2 points
+RLAPI void DrawSplineSegmentBasis(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4, float thick, Color color); // Draw spline segment: B-Spline, 4 points
+RLAPI void DrawSplineSegmentCatmullRom(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4, float thick, Color color); // Draw spline segment: Catmull-Rom, 4 points
+RLAPI void DrawSplineSegmentBezierQuadratic(Vector2 p1, Vector2 c2, Vector2 p3, float thick, Color color); // Draw spline segment: Quadratic Bezier, 2 points, 1 control point
+RLAPI void DrawSplineSegmentBezierCubic(Vector2 p1, Vector2 c2, Vector2 c3, Vector2 p4, float thick, Color color); // Draw spline segment: Cubic Bezier, 2 points, 2 control points
+
+// Spline segment point evaluation functions, for a given t [0.0f .. 1.0f]
+RLAPI Vector2 GetSplinePointLinear(Vector2 startPos, Vector2 endPos, float t); // Get (evaluate) spline point: Linear
+RLAPI Vector2 GetSplinePointBasis(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4, float t); // Get (evaluate) spline point: B-Spline
+RLAPI Vector2 GetSplinePointCatmullRom(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4, float t); // Get (evaluate) spline point: Catmull-Rom
+RLAPI Vector2 GetSplinePointBezierQuad(Vector2 p1, Vector2 c2, Vector2 p3, float t); // Get (evaluate) spline point: Quadratic Bezier
+RLAPI Vector2 GetSplinePointBezierCubic(Vector2 p1, Vector2 c2, Vector2 c3, Vector2 p4, float t); // Get (evaluate) spline point: Cubic Bezier
+
+// Basic shapes collision detection functions
+RLAPI bool CheckCollisionRecs(Rectangle rec1, Rectangle rec2); // Check collision between two rectangles
+RLAPI bool CheckCollisionCircles(Vector2 center1, float radius1, Vector2 center2, float radius2); // Check collision between two circles
+RLAPI bool CheckCollisionCircleRec(Vector2 center, float radius, Rectangle rec); // Check collision between circle and rectangle
+RLAPI bool CheckCollisionCircleLine(Vector2 center, float radius, Vector2 p1, Vector2 p2); // Check if circle collides with a line created betweeen two points [p1] and [p2]
+RLAPI bool CheckCollisionPointRec(Vector2 point, Rectangle rec); // Check if point is inside rectangle
+RLAPI bool CheckCollisionPointCircle(Vector2 point, Vector2 center, float radius); // Check if point is inside circle
+RLAPI bool CheckCollisionPointTriangle(Vector2 point, Vector2 p1, Vector2 p2, Vector2 p3); // Check if point is inside a triangle
+RLAPI bool CheckCollisionPointLine(Vector2 point, Vector2 p1, Vector2 p2, int threshold); // Check if point belongs to line created between two points [p1] and [p2] with defined margin in pixels [threshold]
+RLAPI bool CheckCollisionPointPoly(Vector2 point, const Vector2 *points, int pointCount); // Check if point is within a polygon described by array of vertices
+RLAPI bool CheckCollisionLines(Vector2 startPos1, Vector2 endPos1, Vector2 startPos2, Vector2 endPos2, Vector2 *collisionPoint); // Check the collision between two lines defined by two points each, returns collision point by reference
+RLAPI Rectangle GetCollisionRec(Rectangle rec1, Rectangle rec2); // Get collision rectangle for two rectangles collision
+
+//------------------------------------------------------------------------------------
+// Texture Loading and Drawing Functions (Module: textures)
+//------------------------------------------------------------------------------------
+
+// Image loading functions
+// NOTE: These functions do not require GPU access
+RLAPI Image LoadImage(const char *fileName); // Load image from file into CPU memory (RAM)
+RLAPI Image LoadImageRaw(const char *fileName, int width, int height, int format, int headerSize); // Load image from RAW file data
+RLAPI Image LoadImageAnim(const char *fileName, int *frames); // Load image sequence from file (frames appended to image.data)
+RLAPI Image LoadImageAnimFromMemory(const char *fileType, const unsigned char *fileData, int dataSize, int *frames); // Load image sequence from memory buffer
+RLAPI Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, int dataSize); // Load image from memory buffer, fileType refers to extension: i.e. '.png'
+RLAPI Image LoadImageFromTexture(Texture2D texture); // Load image from GPU texture data
+RLAPI Image LoadImageFromScreen(void); // Load image from screen buffer and (screenshot)
+RLAPI bool IsImageValid(Image image); // Check if an image is valid (data and parameters)
+RLAPI void UnloadImage(Image image); // Unload image from CPU memory (RAM)
+RLAPI bool ExportImage(Image image, const char *fileName); // Export image data to file, returns true on success
+RLAPI unsigned char *ExportImageToMemory(Image image, const char *fileType, int *fileSize); // Export image to memory buffer
+RLAPI bool ExportImageAsCode(Image image, const char *fileName); // Export image as code file defining an array of bytes, returns true on success
+
+// Image generation functions
+RLAPI Image GenImageColor(int width, int height, Color color); // Generate image: plain color
+RLAPI Image GenImageGradientLinear(int width, int height, int direction, Color start, Color end); // Generate image: linear gradient, direction in degrees [0..360], 0=Vertical gradient
+RLAPI Image GenImageGradientRadial(int width, int height, float density, Color inner, Color outer); // Generate image: radial gradient
+RLAPI Image GenImageGradientSquare(int width, int height, float density, Color inner, Color outer); // Generate image: square gradient
+RLAPI Image GenImageChecked(int width, int height, int checksX, int checksY, Color col1, Color col2); // Generate image: checked
+RLAPI Image GenImageWhiteNoise(int width, int height, float factor); // Generate image: white noise
+RLAPI Image GenImagePerlinNoise(int width, int height, int offsetX, int offsetY, float scale); // Generate image: perlin noise
+RLAPI Image GenImageCellular(int width, int height, int tileSize); // Generate image: cellular algorithm, bigger tileSize means bigger cells
+RLAPI Image GenImageText(int width, int height, const char *text); // Generate image: grayscale image from text data
+
+// Image manipulation functions
+RLAPI Image ImageCopy(Image image); // Create an image duplicate (useful for transformations)
+RLAPI Image ImageFromImage(Image image, Rectangle rec); // Create an image from another image piece
+RLAPI Image ImageFromChannel(Image image, int selectedChannel); // Create an image from a selected channel of another image (GRAYSCALE)
+RLAPI Image ImageText(const char *text, int fontSize, Color color); // Create an image from text (default font)
+RLAPI Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Color tint); // Create an image from text (custom sprite font)
+RLAPI void ImageFormat(Image *image, int newFormat); // Convert image data to desired format
+RLAPI void ImageToPOT(Image *image, Color fill); // Convert image to POT (power-of-two)
+RLAPI void ImageCrop(Image *image, Rectangle crop); // Crop an image to a defined rectangle
+RLAPI void ImageAlphaCrop(Image *image, float threshold); // Crop image depending on alpha value
+RLAPI void ImageAlphaClear(Image *image, Color color, float threshold); // Clear alpha channel to desired color
+RLAPI void ImageAlphaMask(Image *image, Image alphaMask); // Apply alpha mask to image
+RLAPI void ImageAlphaPremultiply(Image *image); // Premultiply alpha channel
+RLAPI void ImageBlurGaussian(Image *image, int blurSize); // Apply Gaussian blur using a box blur approximation
+RLAPI void ImageKernelConvolution(Image *image, const float *kernel, int kernelSize); // Apply custom square convolution kernel to image
+RLAPI void ImageResize(Image *image, int newWidth, int newHeight); // Resize image (Bicubic scaling algorithm)
+RLAPI void ImageResizeNN(Image *image, int newWidth,int newHeight); // Resize image (Nearest-Neighbor scaling algorithm)
+RLAPI void ImageResizeCanvas(Image *image, int newWidth, int newHeight, int offsetX, int offsetY, Color fill); // Resize canvas and fill with color
+RLAPI void ImageMipmaps(Image *image); // Compute all mipmap levels for a provided image
+RLAPI void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp); // Dither image data to 16bpp or lower (Floyd-Steinberg dithering)
+RLAPI void ImageFlipVertical(Image *image); // Flip image vertically
+RLAPI void ImageFlipHorizontal(Image *image); // Flip image horizontally
+RLAPI void ImageRotate(Image *image, int degrees); // Rotate image by input angle in degrees (-359 to 359)
+RLAPI void ImageRotateCW(Image *image); // Rotate image clockwise 90deg
+RLAPI void ImageRotateCCW(Image *image); // Rotate image counter-clockwise 90deg
+RLAPI void ImageColorTint(Image *image, Color color); // Modify image color: tint
+RLAPI void ImageColorInvert(Image *image); // Modify image color: invert
+RLAPI void ImageColorGrayscale(Image *image); // Modify image color: grayscale
+RLAPI void ImageColorContrast(Image *image, float contrast); // Modify image color: contrast (-100 to 100)
+RLAPI void ImageColorBrightness(Image *image, int brightness); // Modify image color: brightness (-255 to 255)
+RLAPI void ImageColorReplace(Image *image, Color color, Color replace); // Modify image color: replace color
+RLAPI Color *LoadImageColors(Image image); // Load color data from image as a Color array (RGBA - 32bit)
+RLAPI Color *LoadImagePalette(Image image, int maxPaletteSize, int *colorCount); // Load colors palette from image as a Color array (RGBA - 32bit)
+RLAPI void UnloadImageColors(Color *colors); // Unload color data loaded with LoadImageColors()
+RLAPI void UnloadImagePalette(Color *colors); // Unload colors palette loaded with LoadImagePalette()
+RLAPI Rectangle GetImageAlphaBorder(Image image, float threshold); // Get image alpha border rectangle
+RLAPI Color GetImageColor(Image image, int x, int y); // Get image pixel color at (x, y) position
+
+// Image drawing functions
+// NOTE: Image software-rendering functions (CPU)
+RLAPI void ImageClearBackground(Image *dst, Color color); // Clear image background with given color
+RLAPI void ImageDrawPixel(Image *dst, int posX, int posY, Color color); // Draw pixel within an image
+RLAPI void ImageDrawPixelV(Image *dst, Vector2 position, Color color); // Draw pixel within an image (Vector version)
+RLAPI void ImageDrawLine(Image *dst, int startPosX, int startPosY, int endPosX, int endPosY, Color color); // Draw line within an image
+RLAPI void ImageDrawLineV(Image *dst, Vector2 start, Vector2 end, Color color); // Draw line within an image (Vector version)
+RLAPI void ImageDrawLineEx(Image *dst, Vector2 start, Vector2 end, int thick, Color color); // Draw a line defining thickness within an image
+RLAPI void ImageDrawCircle(Image *dst, int centerX, int centerY, int radius, Color color); // Draw a filled circle within an image
+RLAPI void ImageDrawCircleV(Image *dst, Vector2 center, int radius, Color color); // Draw a filled circle within an image (Vector version)
+RLAPI void ImageDrawCircleLines(Image *dst, int centerX, int centerY, int radius, Color color); // Draw circle outline within an image
+RLAPI void ImageDrawCircleLinesV(Image *dst, Vector2 center, int radius, Color color); // Draw circle outline within an image (Vector version)
+RLAPI void ImageDrawRectangle(Image *dst, int posX, int posY, int width, int height, Color color); // Draw rectangle within an image
+RLAPI void ImageDrawRectangleV(Image *dst, Vector2 position, Vector2 size, Color color); // Draw rectangle within an image (Vector version)
+RLAPI void ImageDrawRectangleRec(Image *dst, Rectangle rec, Color color); // Draw rectangle within an image
+RLAPI void ImageDrawRectangleLines(Image *dst, Rectangle rec, int thick, Color color); // Draw rectangle lines within an image
+RLAPI void ImageDrawTriangle(Image *dst, Vector2 v1, Vector2 v2, Vector2 v3, Color color); // Draw triangle within an image
+RLAPI void ImageDrawTriangleEx(Image *dst, Vector2 v1, Vector2 v2, Vector2 v3, Color c1, Color c2, Color c3); // Draw triangle with interpolated colors within an image
+RLAPI void ImageDrawTriangleLines(Image *dst, Vector2 v1, Vector2 v2, Vector2 v3, Color color); // Draw triangle outline within an image
+RLAPI void ImageDrawTriangleFan(Image *dst, Vector2 *points, int pointCount, Color color); // Draw a triangle fan defined by points within an image (first vertex is the center)
+RLAPI void ImageDrawTriangleStrip(Image *dst, Vector2 *points, int pointCount, Color color); // Draw a triangle strip defined by points within an image
+RLAPI void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec, Color tint); // Draw a source image within a destination image (tint applied to source)
+RLAPI void ImageDrawText(Image *dst, const char *text, int posX, int posY, int fontSize, Color color); // Draw text (using default font) within an image (destination)
+RLAPI void ImageDrawTextEx(Image *dst, Font font, const char *text, Vector2 position, float fontSize, float spacing, Color tint); // Draw text (custom sprite font) within an image (destination)
+
+// Texture loading functions
+// NOTE: These functions require GPU access
+RLAPI Texture2D LoadTexture(const char *fileName); // Load texture from file into GPU memory (VRAM)
+RLAPI Texture2D LoadTextureFromImage(Image image); // Load texture from image data
+RLAPI TextureCubemap LoadTextureCubemap(Image image, int layout); // Load cubemap from image, multiple image cubemap layouts supported
+RLAPI RenderTexture2D LoadRenderTexture(int width, int height); // Load texture for rendering (framebuffer)
+RLAPI bool IsTextureValid(Texture2D texture); // Check if a texture is valid (loaded in GPU)
+RLAPI void UnloadTexture(Texture2D texture); // Unload texture from GPU memory (VRAM)
+RLAPI bool IsRenderTextureValid(RenderTexture2D target); // Check if a render texture is valid (loaded in GPU)
+RLAPI void UnloadRenderTexture(RenderTexture2D target); // Unload render texture from GPU memory (VRAM)
+RLAPI void UpdateTexture(Texture2D texture, const void *pixels); // Update GPU texture with new data
+RLAPI void UpdateTextureRec(Texture2D texture, Rectangle rec, const void *pixels); // Update GPU texture rectangle with new data
+
+// Texture configuration functions
+RLAPI void GenTextureMipmaps(Texture2D *texture); // Generate GPU mipmaps for a texture
+RLAPI void SetTextureFilter(Texture2D texture, int filter); // Set texture scaling filter mode
+RLAPI void SetTextureWrap(Texture2D texture, int wrap); // Set texture wrapping mode
+
+// Texture drawing functions
+RLAPI void DrawTexture(Texture2D texture, int posX, int posY, Color tint); // Draw a Texture2D
+RLAPI void DrawTextureV(Texture2D texture, Vector2 position, Color tint); // Draw a Texture2D with position defined as Vector2
+RLAPI void DrawTextureEx(Texture2D texture, Vector2 position, float rotation, float scale, Color tint); // Draw a Texture2D with extended parameters
+RLAPI void DrawTextureRec(Texture2D texture, Rectangle source, Vector2 position, Color tint); // Draw a part of a texture defined by a rectangle
+RLAPI void DrawTexturePro(Texture2D texture, Rectangle source, Rectangle dest, Vector2 origin, float rotation, Color tint); // Draw a part of a texture defined by a rectangle with 'pro' parameters
+RLAPI void DrawTextureNPatch(Texture2D texture, NPatchInfo nPatchInfo, Rectangle dest, Vector2 origin, float rotation, Color tint); // Draws a texture (or part of it) that stretches or shrinks nicely
+
+// Color/pixel related functions
+RLAPI bool ColorIsEqual(Color col1, Color col2); // Check if two colors are equal
+RLAPI Color Fade(Color color, float alpha); // Get color with alpha applied, alpha goes from 0.0f to 1.0f
+RLAPI int ColorToInt(Color color); // Get hexadecimal value for a Color (0xRRGGBBAA)
+RLAPI Vector4 ColorNormalize(Color color); // Get Color normalized as float [0..1]
+RLAPI Color ColorFromNormalized(Vector4 normalized); // Get Color from normalized values [0..1]
+RLAPI Vector3 ColorToHSV(Color color); // Get HSV values for a Color, hue [0..360], saturation/value [0..1]
+RLAPI Color ColorFromHSV(float hue, float saturation, float value); // Get a Color from HSV values, hue [0..360], saturation/value [0..1]
+RLAPI Color ColorTint(Color color, Color tint); // Get color multiplied with another color
+RLAPI Color ColorBrightness(Color color, float factor); // Get color with brightness correction, brightness factor goes from -1.0f to 1.0f
+RLAPI Color ColorContrast(Color color, float contrast); // Get color with contrast correction, contrast values between -1.0f and 1.0f
+RLAPI Color ColorAlpha(Color color, float alpha); // Get color with alpha applied, alpha goes from 0.0f to 1.0f
+RLAPI Color ColorAlphaBlend(Color dst, Color src, Color tint); // Get src alpha-blended into dst color with tint
+RLAPI Color ColorLerp(Color color1, Color color2, float factor); // Get color lerp interpolation between two colors, factor [0.0f..1.0f]
+RLAPI Color GetColor(unsigned int hexValue); // Get Color structure from hexadecimal value
+RLAPI Color GetPixelColor(void *srcPtr, int format); // Get Color from a source pixel pointer of certain format
+RLAPI void SetPixelColor(void *dstPtr, Color color, int format); // Set color formatted into destination pixel pointer
+RLAPI int GetPixelDataSize(int width, int height, int format); // Get pixel data size in bytes for certain format
+
+//------------------------------------------------------------------------------------
+// Font Loading and Text Drawing Functions (Module: text)
+//------------------------------------------------------------------------------------
+
+// Font loading/unloading functions
+RLAPI Font GetFontDefault(void); // Get the default Font
+RLAPI Font LoadFont(const char *fileName); // Load font from file into GPU memory (VRAM)
+RLAPI Font LoadFontEx(const char *fileName, int fontSize, int *codepoints, int codepointCount); // Load font from file with extended parameters, use NULL for codepoints and 0 for codepointCount to load the default character set, font size is provided in pixels height
+RLAPI Font LoadFontFromImage(Image image, Color key, int firstChar); // Load font from Image (XNA style)
+RLAPI Font LoadFontFromMemory(const char *fileType, const unsigned char *fileData, int dataSize, int fontSize, int *codepoints, int codepointCount); // Load font from memory buffer, fileType refers to extension: i.e. '.ttf'
+RLAPI bool IsFontValid(Font font); // Check if a font is valid (font data loaded, WARNING: GPU texture not checked)
+RLAPI GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSize, int *codepoints, int codepointCount, int type); // Load font data for further use
+RLAPI Image GenImageFontAtlas(const GlyphInfo *glyphs, Rectangle **glyphRecs, int glyphCount, int fontSize, int padding, int packMethod); // Generate image font atlas using chars info
+RLAPI void UnloadFontData(GlyphInfo *glyphs, int glyphCount); // Unload font chars info data (RAM)
+RLAPI void UnloadFont(Font font); // Unload font from GPU memory (VRAM)
+RLAPI bool ExportFontAsCode(Font font, const char *fileName); // Export font as code file, returns true on success
+
+// Text drawing functions
+RLAPI void DrawFPS(int posX, int posY); // Draw current FPS
+RLAPI void DrawText(const char *text, int posX, int posY, int fontSize, Color color); // Draw text (using default font)
+RLAPI void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, float spacing, Color tint); // Draw text using font and additional parameters
+RLAPI void DrawTextPro(Font font, const char *text, Vector2 position, Vector2 origin, float rotation, float fontSize, float spacing, Color tint); // Draw text using Font and pro parameters (rotation)
+RLAPI void DrawTextCodepoint(Font font, int codepoint, Vector2 position, float fontSize, Color tint); // Draw one character (codepoint)
+RLAPI void DrawTextCodepoints(Font font, const int *codepoints, int codepointCount, Vector2 position, float fontSize, float spacing, Color tint); // Draw multiple character (codepoint)
+
+// Text font info functions
+RLAPI void SetTextLineSpacing(int spacing); // Set vertical line spacing when drawing with line-breaks
+RLAPI int MeasureText(const char *text, int fontSize); // Measure string width for default font
+RLAPI Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing); // Measure string size for Font
+RLAPI int GetGlyphIndex(Font font, int codepoint); // Get glyph index position in font for a codepoint (unicode character), fallback to '?' if not found
+RLAPI GlyphInfo GetGlyphInfo(Font font, int codepoint); // Get glyph font info data for a codepoint (unicode character), fallback to '?' if not found
+RLAPI Rectangle GetGlyphAtlasRec(Font font, int codepoint); // Get glyph rectangle in font atlas for a codepoint (unicode character), fallback to '?' if not found
+
+// Text codepoints management functions (unicode characters)
+RLAPI char *LoadUTF8(const int *codepoints, int length); // Load UTF-8 text encoded from codepoints array
+RLAPI void UnloadUTF8(char *text); // Unload UTF-8 text encoded from codepoints array
+RLAPI int *LoadCodepoints(const char *text, int *count); // Load all codepoints from a UTF-8 text string, codepoints count returned by parameter
+RLAPI void UnloadCodepoints(int *codepoints); // Unload codepoints data from memory
+RLAPI int GetCodepointCount(const char *text); // Get total number of codepoints in a UTF-8 encoded string
+RLAPI int GetCodepoint(const char *text, int *codepointSize); // Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure
+RLAPI int GetCodepointNext(const char *text, int *codepointSize); // Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure
+RLAPI int GetCodepointPrevious(const char *text, int *codepointSize); // Get previous codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure
+RLAPI const char *CodepointToUTF8(int codepoint, int *utf8Size); // Encode one codepoint into UTF-8 byte array (array length returned as parameter)
+
+// Text strings management functions (no UTF-8 strings, only byte chars)
+// WARNING 1: Most of these functions use internal static buffers, it's recommended to store returned data on user-side for re-use
+// WARNING 2: Some strings allocate memory internally for the returned strings, those strings must be free by user using MemFree()
+RLAPI int TextCopy(char *dst, const char *src); // Copy one string to another, returns bytes copied
+RLAPI bool TextIsEqual(const char *text1, const char *text2); // Check if two text string are equal
+RLAPI unsigned int TextLength(const char *text); // Get text length, checks for '\0' ending
+RLAPI const char *TextFormat(const char *text, ...); // Text formatting with variables (sprintf() style)
+RLAPI const char *TextSubtext(const char *text, int position, int length); // Get a piece of a text string
+RLAPI char *TextReplace(const char *text, const char *replace, const char *by); // Replace text string (WARNING: memory must be freed!)
+RLAPI char *TextInsert(const char *text, const char *insert, int position); // Insert text in a position (WARNING: memory must be freed!)
+RLAPI char *TextJoin(char **textList, int count, const char *delimiter); // Join text strings with delimiter
+RLAPI char **TextSplit(const char *text, char delimiter, int *count); // Split text into multiple strings
+RLAPI void TextAppend(char *text, const char *append, int *position); // Append text at specific position and move cursor!
+RLAPI int TextFindIndex(const char *text, const char *find); // Find first text occurrence within a string
+RLAPI char *TextToUpper(const char *text); // Get upper case version of provided string
+RLAPI char *TextToLower(const char *text); // Get lower case version of provided string
+RLAPI char *TextToPascal(const char *text); // Get Pascal case notation version of provided string
+RLAPI char *TextToSnake(const char *text); // Get Snake case notation version of provided string
+RLAPI char *TextToCamel(const char *text); // Get Camel case notation version of provided string
+
+RLAPI int TextToInteger(const char *text); // Get integer value from text
+RLAPI float TextToFloat(const char *text); // Get float value from text
+
+//------------------------------------------------------------------------------------
+// Basic 3d Shapes Drawing Functions (Module: models)
+//------------------------------------------------------------------------------------
+
+// Basic geometric 3D shapes drawing functions
+RLAPI void DrawLine3D(Vector3 startPos, Vector3 endPos, Color color); // Draw a line in 3D world space
+RLAPI void DrawPoint3D(Vector3 position, Color color); // Draw a point in 3D space, actually a small line
+RLAPI void DrawCircle3D(Vector3 center, float radius, Vector3 rotationAxis, float rotationAngle, Color color); // Draw a circle in 3D world space
+RLAPI void DrawTriangle3D(Vector3 v1, Vector3 v2, Vector3 v3, Color color); // Draw a color-filled triangle (vertex in counter-clockwise order!)
+RLAPI void DrawTriangleStrip3D(const Vector3 *points, int pointCount, Color color); // Draw a triangle strip defined by points
+RLAPI void DrawCube(Vector3 position, float width, float height, float length, Color color); // Draw cube
+RLAPI void DrawCubeV(Vector3 position, Vector3 size, Color color); // Draw cube (Vector version)
+RLAPI void DrawCubeWires(Vector3 position, float width, float height, float length, Color color); // Draw cube wires
+RLAPI void DrawCubeWiresV(Vector3 position, Vector3 size, Color color); // Draw cube wires (Vector version)
+RLAPI void DrawSphere(Vector3 centerPos, float radius, Color color); // Draw sphere
+RLAPI void DrawSphereEx(Vector3 centerPos, float radius, int rings, int slices, Color color); // Draw sphere with extended parameters
+RLAPI void DrawSphereWires(Vector3 centerPos, float radius, int rings, int slices, Color color); // Draw sphere wires
+RLAPI void DrawCylinder(Vector3 position, float radiusTop, float radiusBottom, float height, int slices, Color color); // Draw a cylinder/cone
+RLAPI void DrawCylinderEx(Vector3 startPos, Vector3 endPos, float startRadius, float endRadius, int sides, Color color); // Draw a cylinder with base at startPos and top at endPos
+RLAPI void DrawCylinderWires(Vector3 position, float radiusTop, float radiusBottom, float height, int slices, Color color); // Draw a cylinder/cone wires
+RLAPI void DrawCylinderWiresEx(Vector3 startPos, Vector3 endPos, float startRadius, float endRadius, int sides, Color color); // Draw a cylinder wires with base at startPos and top at endPos
+RLAPI void DrawCapsule(Vector3 startPos, Vector3 endPos, float radius, int slices, int rings, Color color); // Draw a capsule with the center of its sphere caps at startPos and endPos
+RLAPI void DrawCapsuleWires(Vector3 startPos, Vector3 endPos, float radius, int slices, int rings, Color color); // Draw capsule wireframe with the center of its sphere caps at startPos and endPos
+RLAPI void DrawPlane(Vector3 centerPos, Vector2 size, Color color); // Draw a plane XZ
+RLAPI void DrawRay(Ray ray, Color color); // Draw a ray line
+RLAPI void DrawGrid(int slices, float spacing); // Draw a grid (centered at (0, 0, 0))
+
+//------------------------------------------------------------------------------------
+// Model 3d Loading and Drawing Functions (Module: models)
+//------------------------------------------------------------------------------------
+
+// Model management functions
+RLAPI Model LoadModel(const char *fileName); // Load model from files (meshes and materials)
+RLAPI Model LoadModelFromMesh(Mesh mesh); // Load model from generated mesh (default material)
+RLAPI bool IsModelValid(Model model); // Check if a model is valid (loaded in GPU, VAO/VBOs)
+RLAPI void UnloadModel(Model model); // Unload model (including meshes) from memory (RAM and/or VRAM)
+RLAPI BoundingBox GetModelBoundingBox(Model model); // Compute model bounding box limits (considers all meshes)
+
+// Model drawing functions
+RLAPI void DrawModel(Model model, Vector3 position, float scale, Color tint); // Draw a model (with texture if set)
+RLAPI void DrawModelEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); // Draw a model with extended parameters
+RLAPI void DrawModelWires(Model model, Vector3 position, float scale, Color tint); // Draw a model wires (with texture if set)
+RLAPI void DrawModelWiresEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); // Draw a model wires (with texture if set) with extended parameters
+RLAPI void DrawModelPoints(Model model, Vector3 position, float scale, Color tint); // Draw a model as points
+RLAPI void DrawModelPointsEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); // Draw a model as points with extended parameters
+RLAPI void DrawBoundingBox(BoundingBox box, Color color); // Draw bounding box (wires)
+RLAPI void DrawBillboard(Camera camera, Texture2D texture, Vector3 position, float scale, Color tint); // Draw a billboard texture
+RLAPI void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle source, Vector3 position, Vector2 size, Color tint); // Draw a billboard texture defined by source
+RLAPI void DrawBillboardPro(Camera camera, Texture2D texture, Rectangle source, Vector3 position, Vector3 up, Vector2 size, Vector2 origin, float rotation, Color tint); // Draw a billboard texture defined by source and rotation
+
+// Mesh management functions
+RLAPI void UploadMesh(Mesh *mesh, bool dynamic); // Upload mesh vertex data in GPU and provide VAO/VBO ids
+RLAPI void UpdateMeshBuffer(Mesh mesh, int index, const void *data, int dataSize, int offset); // Update mesh vertex data in GPU for a specific buffer index
+RLAPI void UnloadMesh(Mesh mesh); // Unload mesh data from CPU and GPU
+RLAPI void DrawMesh(Mesh mesh, Material material, Matrix transform); // Draw a 3d mesh with material and transform
+RLAPI void DrawMeshInstanced(Mesh mesh, Material material, const Matrix *transforms, int instances); // Draw multiple mesh instances with material and different transforms
+RLAPI BoundingBox GetMeshBoundingBox(Mesh mesh); // Compute mesh bounding box limits
+RLAPI void GenMeshTangents(Mesh *mesh); // Compute mesh tangents
+RLAPI bool ExportMesh(Mesh mesh, const char *fileName); // Export mesh data to file, returns true on success
+RLAPI bool ExportMeshAsCode(Mesh mesh, const char *fileName); // Export mesh as code file (.h) defining multiple arrays of vertex attributes
+
+// Mesh generation functions
+RLAPI Mesh GenMeshPoly(int sides, float radius); // Generate polygonal mesh
+RLAPI Mesh GenMeshPlane(float width, float length, int resX, int resZ); // Generate plane mesh (with subdivisions)
+RLAPI Mesh GenMeshCube(float width, float height, float length); // Generate cuboid mesh
+RLAPI Mesh GenMeshSphere(float radius, int rings, int slices); // Generate sphere mesh (standard sphere)
+RLAPI Mesh GenMeshHemiSphere(float radius, int rings, int slices); // Generate half-sphere mesh (no bottom cap)
+RLAPI Mesh GenMeshCylinder(float radius, float height, int slices); // Generate cylinder mesh
+RLAPI Mesh GenMeshCone(float radius, float height, int slices); // Generate cone/pyramid mesh
+RLAPI Mesh GenMeshTorus(float radius, float size, int radSeg, int sides); // Generate torus mesh
+RLAPI Mesh GenMeshKnot(float radius, float size, int radSeg, int sides); // Generate trefoil knot mesh
+RLAPI Mesh GenMeshHeightmap(Image heightmap, Vector3 size); // Generate heightmap mesh from image data
+RLAPI Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize); // Generate cubes-based map mesh from image data
+
+// Material loading/unloading functions
+RLAPI Material *LoadMaterials(const char *fileName, int *materialCount); // Load materials from model file
+RLAPI Material LoadMaterialDefault(void); // Load default material (Supports: DIFFUSE, SPECULAR, NORMAL maps)
+RLAPI bool IsMaterialValid(Material material); // Check if a material is valid (shader assigned, map textures loaded in GPU)
+RLAPI void UnloadMaterial(Material material); // Unload material from GPU memory (VRAM)
+RLAPI void SetMaterialTexture(Material *material, int mapType, Texture2D texture); // Set texture for a material map type (MATERIAL_MAP_DIFFUSE, MATERIAL_MAP_SPECULAR...)
+RLAPI void SetModelMeshMaterial(Model *model, int meshId, int materialId); // Set material for a mesh
+
+// Model animations loading/unloading functions
+RLAPI ModelAnimation *LoadModelAnimations(const char *fileName, int *animCount); // Load model animations from file
+RLAPI void UpdateModelAnimation(Model model, ModelAnimation anim, int frame); // Update model animation pose (CPU)
+RLAPI void UpdateModelAnimationBones(Model model, ModelAnimation anim, int frame); // Update model animation mesh bone matrices (GPU skinning)
+RLAPI void UnloadModelAnimation(ModelAnimation anim); // Unload animation data
+RLAPI void UnloadModelAnimations(ModelAnimation *animations, int animCount); // Unload animation array data
+RLAPI bool IsModelAnimationValid(Model model, ModelAnimation anim); // Check model animation skeleton match
+
+// Collision detection functions
+RLAPI bool CheckCollisionSpheres(Vector3 center1, float radius1, Vector3 center2, float radius2); // Check collision between two spheres
+RLAPI bool CheckCollisionBoxes(BoundingBox box1, BoundingBox box2); // Check collision between two bounding boxes
+RLAPI bool CheckCollisionBoxSphere(BoundingBox box, Vector3 center, float radius); // Check collision between box and sphere
+RLAPI RayCollision GetRayCollisionSphere(Ray ray, Vector3 center, float radius); // Get collision info between ray and sphere
+RLAPI RayCollision GetRayCollisionBox(Ray ray, BoundingBox box); // Get collision info between ray and box
+RLAPI RayCollision GetRayCollisionMesh(Ray ray, Mesh mesh, Matrix transform); // Get collision info between ray and mesh
+RLAPI RayCollision GetRayCollisionTriangle(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3); // Get collision info between ray and triangle
+RLAPI RayCollision GetRayCollisionQuad(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3, Vector3 p4); // Get collision info between ray and quad
+
+//------------------------------------------------------------------------------------
+// Audio Loading and Playing Functions (Module: audio)
+//------------------------------------------------------------------------------------
+typedef void (*AudioCallback)(void *bufferData, unsigned int frames);
+
+// Audio device management functions
+RLAPI void InitAudioDevice(void); // Initialize audio device and context
+RLAPI void CloseAudioDevice(void); // Close the audio device and context
+RLAPI bool IsAudioDeviceReady(void); // Check if audio device has been initialized successfully
+RLAPI void SetMasterVolume(float volume); // Set master volume (listener)
+RLAPI float GetMasterVolume(void); // Get master volume (listener)
+
+// Wave/Sound loading/unloading functions
+RLAPI Wave LoadWave(const char *fileName); // Load wave data from file
+RLAPI Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileData, int dataSize); // Load wave from memory buffer, fileType refers to extension: i.e. '.wav'
+RLAPI bool IsWaveValid(Wave wave); // Checks if wave data is valid (data loaded and parameters)
+RLAPI Sound LoadSound(const char *fileName); // Load sound from file
+RLAPI Sound LoadSoundFromWave(Wave wave); // Load sound from wave data
+RLAPI Sound LoadSoundAlias(Sound source); // Create a new sound that shares the same sample data as the source sound, does not own the sound data
+RLAPI bool IsSoundValid(Sound sound); // Checks if a sound is valid (data loaded and buffers initialized)
+RLAPI void UpdateSound(Sound sound, const void *data, int sampleCount); // Update sound buffer with new data
+RLAPI void UnloadWave(Wave wave); // Unload wave data
+RLAPI void UnloadSound(Sound sound); // Unload sound
+RLAPI void UnloadSoundAlias(Sound alias); // Unload a sound alias (does not deallocate sample data)
+RLAPI bool ExportWave(Wave wave, const char *fileName); // Export wave data to file, returns true on success
+RLAPI bool ExportWaveAsCode(Wave wave, const char *fileName); // Export wave sample data to code (.h), returns true on success
+
+// Wave/Sound management functions
+RLAPI void PlaySound(Sound sound); // Play a sound
+RLAPI void StopSound(Sound sound); // Stop playing a sound
+RLAPI void PauseSound(Sound sound); // Pause a sound
+RLAPI void ResumeSound(Sound sound); // Resume a paused sound
+RLAPI bool IsSoundPlaying(Sound sound); // Check if a sound is currently playing
+RLAPI void SetSoundVolume(Sound sound, float volume); // Set volume for a sound (1.0 is max level)
+RLAPI void SetSoundPitch(Sound sound, float pitch); // Set pitch for a sound (1.0 is base level)
+RLAPI void SetSoundPan(Sound sound, float pan); // Set pan for a sound (0.5 is center)
+RLAPI Wave WaveCopy(Wave wave); // Copy a wave to a new wave
+RLAPI void WaveCrop(Wave *wave, int initFrame, int finalFrame); // Crop a wave to defined frames range
+RLAPI void WaveFormat(Wave *wave, int sampleRate, int sampleSize, int channels); // Convert wave data to desired format
+RLAPI float *LoadWaveSamples(Wave wave); // Load samples data from wave as a 32bit float data array
+RLAPI void UnloadWaveSamples(float *samples); // Unload samples data loaded with LoadWaveSamples()
+
+// Music management functions
+RLAPI Music LoadMusicStream(const char *fileName); // Load music stream from file
+RLAPI Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, int dataSize); // Load music stream from data
+RLAPI bool IsMusicValid(Music music); // Checks if a music stream is valid (context and buffers initialized)
+RLAPI void UnloadMusicStream(Music music); // Unload music stream
+RLAPI void PlayMusicStream(Music music); // Start music playing
+RLAPI bool IsMusicStreamPlaying(Music music); // Check if music is playing
+RLAPI void UpdateMusicStream(Music music); // Updates buffers for music streaming
+RLAPI void StopMusicStream(Music music); // Stop music playing
+RLAPI void PauseMusicStream(Music music); // Pause music playing
+RLAPI void ResumeMusicStream(Music music); // Resume playing paused music
+RLAPI void SeekMusicStream(Music music, float position); // Seek music to a position (in seconds)
+RLAPI void SetMusicVolume(Music music, float volume); // Set volume for music (1.0 is max level)
+RLAPI void SetMusicPitch(Music music, float pitch); // Set pitch for a music (1.0 is base level)
+RLAPI void SetMusicPan(Music music, float pan); // Set pan for a music (0.5 is center)
+RLAPI float GetMusicTimeLength(Music music); // Get music time length (in seconds)
+RLAPI float GetMusicTimePlayed(Music music); // Get current music time played (in seconds)
+
+// AudioStream management functions
+RLAPI AudioStream LoadAudioStream(unsigned int sampleRate, unsigned int sampleSize, unsigned int channels); // Load audio stream (to stream raw audio pcm data)
+RLAPI bool IsAudioStreamValid(AudioStream stream); // Checks if an audio stream is valid (buffers initialized)
+RLAPI void UnloadAudioStream(AudioStream stream); // Unload audio stream and free memory
+RLAPI void UpdateAudioStream(AudioStream stream, const void *data, int frameCount); // Update audio stream buffers with data
+RLAPI bool IsAudioStreamProcessed(AudioStream stream); // Check if any audio stream buffers requires refill
+RLAPI void PlayAudioStream(AudioStream stream); // Play audio stream
+RLAPI void PauseAudioStream(AudioStream stream); // Pause audio stream
+RLAPI void ResumeAudioStream(AudioStream stream); // Resume audio stream
+RLAPI bool IsAudioStreamPlaying(AudioStream stream); // Check if audio stream is playing
+RLAPI void StopAudioStream(AudioStream stream); // Stop audio stream
+RLAPI void SetAudioStreamVolume(AudioStream stream, float volume); // Set volume for audio stream (1.0 is max level)
+RLAPI void SetAudioStreamPitch(AudioStream stream, float pitch); // Set pitch for audio stream (1.0 is base level)
+RLAPI void SetAudioStreamPan(AudioStream stream, float pan); // Set pan for audio stream (0.5 is centered)
+RLAPI void SetAudioStreamBufferSizeDefault(int size); // Default size for new audio streams
+RLAPI void SetAudioStreamCallback(AudioStream stream, AudioCallback callback); // Audio thread callback to request new data
+
+RLAPI void AttachAudioStreamProcessor(AudioStream stream, AudioCallback processor); // Attach audio stream processor to stream, receives the samples as 'float'
+RLAPI void DetachAudioStreamProcessor(AudioStream stream, AudioCallback processor); // Detach audio stream processor from stream
+
+RLAPI void AttachAudioMixedProcessor(AudioCallback processor); // Attach audio stream processor to the entire audio pipeline, receives the samples as 'float'
+RLAPI void DetachAudioMixedProcessor(AudioCallback processor); // Detach audio stream processor from the entire audio pipeline
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif // RAYLIB_H
diff --git a/odin-c-bindgen/examples/raylib/input/raylib.lib b/odin-c-bindgen/examples/raylib/input/raylib.lib
Binary files differ.
diff --git a/odin-c-bindgen/examples/raylib/raylib/raylib.odin b/odin-c-bindgen/examples/raylib/raylib/raylib.odin
@@ -0,0 +1,1565 @@
+/**********************************************************************************************
+*
+* raylib v5.6-dev - A simple and easy-to-use library to enjoy videogames programming (www.raylib.com)
+*
+* FEATURES:
+* - NO external dependencies, all required libraries included with raylib
+* - Multiplatform: Windows, Linux, FreeBSD, OpenBSD, NetBSD, DragonFly,
+* MacOS, Haiku, Android, Raspberry Pi, DRM native, HTML5.
+* - Written in plain C code (C99) in PascalCase/camelCase notation
+* - Hardware accelerated with OpenGL (1.1, 2.1, 3.3, 4.3, ES2, ES3 - choose at compile)
+* - Unique OpenGL abstraction layer (usable as standalone module): [rlgl]
+* - Multiple Fonts formats supported (TTF, OTF, FNT, BDF, Sprite fonts)
+* - Outstanding texture formats support, including compressed formats (DXT, ETC, ASTC)
+* - Full 3d support for 3d Shapes, Models, Billboards, Heightmaps and more!
+* - Flexible Materials system, supporting classic maps and PBR maps
+* - Animated 3D models supported (skeletal bones animation) (IQM, M3D, GLTF)
+* - Shaders support, including Model shaders and Postprocessing shaders
+* - Powerful math module for Vector, Matrix and Quaternion operations: [raymath]
+* - Audio loading and playing with streaming support (WAV, OGG, MP3, FLAC, QOA, XM, MOD)
+* - VR stereo rendering with configurable HMD device parameters
+* - Bindings to multiple programming languages available!
+*
+* NOTES:
+* - One default Font is loaded on InitWindow()->LoadFontDefault() [core, text]
+* - One default Texture2D is loaded on rlglInit(), 1x1 white pixel R8G8B8A8 [rlgl] (OpenGL 3.3 or ES2)
+* - One default Shader is loaded on rlglInit()->rlLoadShaderDefault() [rlgl] (OpenGL 3.3 or ES2)
+* - One default RenderBatch is loaded on rlglInit()->rlLoadRenderBatch() [rlgl] (OpenGL 3.3 or ES2)
+*
+* DEPENDENCIES (included):
+* [rcore][GLFW] rglfw (Camilla Löwy - github.com/glfw/glfw) for window/context management and input
+* [rcore][RGFW] rgfw (ColleagueRiley - github.com/ColleagueRiley/RGFW) for window/context management and input
+* [rlgl] glad/glad_gles2 (David Herberth - github.com/Dav1dde/glad) for OpenGL 3.3 extensions loading
+* [raudio] miniaudio (David Reid - github.com/mackron/miniaudio) for audio device/context management
+*
+* OPTIONAL DEPENDENCIES (included):
+* [rcore] msf_gif (Miles Fogle) for GIF recording
+* [rcore] sinfl (Micha Mettke) for DEFLATE decompression algorithm
+* [rcore] sdefl (Micha Mettke) for DEFLATE compression algorithm
+* [rcore] rprand (Ramon Snatamaria) for pseudo-random numbers generation
+* [rtextures] qoi (Dominic Szablewski - https://phoboslab.org) for QOI image manage
+* [rtextures] stb_image (Sean Barret) for images loading (BMP, TGA, PNG, JPEG, HDR...)
+* [rtextures] stb_image_write (Sean Barret) for image writing (BMP, TGA, PNG, JPG)
+* [rtextures] stb_image_resize2 (Sean Barret) for image resizing algorithms
+* [rtextures] stb_perlin (Sean Barret) for Perlin Noise image generation
+* [rtext] stb_truetype (Sean Barret) for ttf fonts loading
+* [rtext] stb_rect_pack (Sean Barret) for rectangles packing
+* [rmodels] par_shapes (Philip Rideout) for parametric 3d shapes generation
+* [rmodels] tinyobj_loader_c (Syoyo Fujita) for models loading (OBJ, MTL)
+* [rmodels] cgltf (Johannes Kuhlmann) for models loading (glTF)
+* [rmodels] m3d (bzt) for models loading (M3D, https://bztsrc.gitlab.io/model3d)
+* [rmodels] vox_loader (Johann Nadalutti) for models loading (VOX)
+* [raudio] dr_wav (David Reid) for WAV audio file loading
+* [raudio] dr_flac (David Reid) for FLAC audio file loading
+* [raudio] dr_mp3 (David Reid) for MP3 audio file loading
+* [raudio] stb_vorbis (Sean Barret) for OGG audio loading
+* [raudio] jar_xm (Joshua Reisenauer) for XM audio module loading
+* [raudio] jar_mod (Joshua Reisenauer) for MOD audio module loading
+* [raudio] qoa (Dominic Szablewski - https://phoboslab.org) for QOA audio manage
+*
+*
+* LICENSE: zlib/libpng
+*
+* raylib is licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+* BSD-like license that allows static linking with closed source software:
+*
+* Copyright (c) 2013-2025 Ramon Santamaria (@raysan5)
+*
+* This software is provided "as-is", without any express or implied warranty. In no event
+* will the authors be held liable for any damages arising from the use of this software.
+*
+* Permission is granted to anyone to use this software for any purpose, including commercial
+* applications, and to alter it and redistribute it freely, subject to the following restrictions:
+*
+* 1. The origin of this software must not be misrepresented; you must not claim that you
+* wrote the original software. If you use this software in a product, an acknowledgment
+* in the product documentation would be appreciated but is not required.
+*
+* 2. Altered source versions must be plainly marked as such, and must not be misrepresented
+* as being the original software.
+*
+* 3. This notice may not be removed or altered from any source distribution.
+*
+**********************************************************************************************/
+package raylib
+
+import "core:c"
+
+_ :: c
+
+@(extra_linker_flags="/NODEFAULTLIB:libcmt")
+foreign import lib {
+ "raylib.lib",
+ "system:Winmm.lib",
+ "system:Gdi32.lib",
+ "system:User32.lib",
+ "system:Shell32.lib",
+}
+
+RAYLIB_VERSION_MAJOR :: 5
+RAYLIB_VERSION_MINOR :: 6
+RAYLIB_VERSION_PATCH :: 0
+RAYLIB_VERSION :: "5.6-dev"
+
+PI :: 3.14159265358979323846
+
+DEG2RAD :: PI/180.0
+
+RAD2DEG :: 180.0/PI
+
+// Some Basic Colors
+// NOTE: Custom raylib color palette for amazing visuals on WHITE background
+LIGHTGRAY :: (Color){ 200, 200, 200, 255 } // Light Gray
+GRAY :: (Color){ 130, 130, 130, 255 } // Gray
+DARKGRAY :: (Color){ 80, 80, 80, 255 } // Dark Gray
+YELLOW :: (Color){ 253, 249, 0, 255 } // Yellow
+GOLD :: (Color){ 255, 203, 0, 255 } // Gold
+ORANGE :: (Color){ 255, 161, 0, 255 } // Orange
+PINK :: (Color){ 255, 109, 194, 255 } // Pink
+RED :: (Color){ 230, 41, 55, 255 } // Red
+MAROON :: (Color){ 190, 33, 55, 255 } // Maroon
+GREEN :: (Color){ 0, 228, 48, 255 } // Green
+LIME :: (Color){ 0, 158, 47, 255 } // Lime
+DARKGREEN :: (Color){ 0, 117, 44, 255 } // Dark Green
+SKYBLUE :: (Color){ 102, 191, 255, 255 } // Sky Blue
+BLUE :: (Color){ 0, 121, 241, 255 } // Blue
+DARKBLUE :: (Color){ 0, 82, 172, 255 } // Dark Blue
+PURPLE :: (Color){ 200, 122, 255, 255 } // Purple
+VIOLET :: (Color){ 135, 60, 190, 255 } // Violet
+DARKPURPLE :: (Color){ 112, 31, 126, 255 } // Dark Purple
+BEIGE :: (Color){ 211, 176, 131, 255 } // Beige
+BROWN :: (Color){ 127, 106, 79, 255 } // Brown
+DARKBROWN :: (Color){ 76, 63, 47, 255 } // Dark Brown
+
+WHITE :: (Color){ 255, 255, 255, 255 } // White
+BLACK :: (Color){ 0, 0, 0, 255 } // Black
+BLANK :: (Color){ 0, 0, 0, 0 } // Blank (Transparent)
+MAGENTA :: (Color){ 255, 0, 255, 255 } // Magenta
+RAYWHITE :: (Color){ 245, 245, 245, 255 } // My own White (raylib logo)
+
+// Vector2, 2 components
+Vector2 :: [2]f32
+
+// Vector3, 3 components
+Vector3 :: [3]f32
+
+// Vector4, 4 components
+Vector4 :: [4]f32
+
+// Quaternion, 4 components (Vector4 alias)
+Quaternion :: Vector4
+
+// Matrix, 4x4 components, column major, OpenGL style, right-handed
+Matrix :: #row_major matrix[4, 4]f32
+
+// Color, 4 components, R8G8B8A8 (32bit)
+Color :: distinct [4]u8
+
+// Rectangle, 4 components
+Rectangle :: struct {
+ x: f32, // Rectangle top-left corner position x
+ y: f32, // Rectangle top-left corner position y
+ width: f32, // Rectangle width
+ height: f32, // Rectangle height
+}
+
+// Image, pixel data stored in CPU memory (RAM)
+Image :: struct {
+ data: rawptr, // Image raw data
+ width: c.int, // Image base width
+ height: c.int, // Image base height
+ mipmaps: c.int, // Mipmap levels, 1 by default
+ format: PixelFormat, // Data format (PixelFormat type)
+}
+
+// Texture, tex data stored in GPU memory (VRAM)
+Texture :: struct {
+ id: c.uint, // OpenGL texture id
+ width: c.int, // Texture base width
+ height: c.int, // Texture base height
+ mipmaps: c.int, // Mipmap levels, 1 by default
+ format: PixelFormat, // Data format (PixelFormat type)
+}
+
+// Texture2D, same as Texture
+Texture2D :: Texture
+
+// TextureCubemap, same as Texture
+TextureCubemap :: Texture
+
+// RenderTexture, fbo for texture rendering
+RenderTexture :: struct {
+ id: c.uint, // OpenGL framebuffer object id
+ texture: Texture, // Color buffer attachment texture
+ depth: Texture, // Depth buffer attachment texture
+}
+
+// RenderTexture2D, same as RenderTexture
+RenderTexture2D :: RenderTexture
+
+// NPatchInfo, n-patch layout info
+NPatchInfo :: struct {
+ source: Rectangle, // Texture source rectangle
+ left: c.int, // Left border offset
+ top: c.int, // Top border offset
+ right: c.int, // Right border offset
+ bottom: c.int, // Bottom border offset
+ layout: NPatchLayout, // Layout of the n-patch: 3x3, 1x3 or 3x1
+}
+
+// GlyphInfo, font characters glyphs info
+GlyphInfo :: struct {
+ value: rune, // Character value (Unicode)
+ offsetX: c.int, // Character offset X when drawing
+ offsetY: c.int, // Character offset Y when drawing
+ advanceX: c.int, // Character advance position X
+ image: Image, // Character image data
+}
+
+// Font, font texture and GlyphInfo array data
+Font :: struct {
+ baseSize: c.int, // Base size (default chars height)
+ glyphCount: c.int, // Number of glyph characters
+ glyphPadding: c.int, // Padding around the glyph characters
+ texture: Texture2D, // Texture atlas containing the glyphs
+ recs: ^Rectangle, // Rectangles in texture for the glyphs
+ glyphs: ^GlyphInfo, // Glyphs info data
+}
+
+// Camera, defines position/orientation in 3d space
+Camera3D :: struct {
+ position: Vector3, // Camera position
+ target: Vector3, // Camera target it looks-at
+ up: Vector3, // Camera up vector (rotation over its axis)
+ fovy: f32, // Camera field-of-view aperture in Y (degrees) in perspective, used as near plane width in orthographic
+ projection: c.int, // Camera projection: CAMERA_PERSPECTIVE or CAMERA_ORTHOGRAPHIC
+}
+
+Camera :: Camera3D // Camera type fallback, defaults to Camera3D
+
+// Camera2D, defines position/orientation in 2d space
+Camera2D :: struct {
+ offset: Vector2, // Camera offset (displacement from target)
+ target: Vector2, // Camera target (rotation and zoom origin)
+ rotation: f32, // Camera rotation in degrees
+ zoom: f32, // Camera zoom (scaling), should be 1.0f by default
+}
+
+// Mesh, vertex data and vao/vbo
+Mesh :: struct {
+ vertexCount: c.int, // Number of vertices stored in arrays
+ triangleCount: c.int, // Number of triangles stored (indexed or not)
+ vertices: [^]f32, // Vertex position (XYZ - 3 components per vertex) (shader-location = 0)
+ texcoords: [^]f32, // Vertex texture coordinates (UV - 2 components per vertex) (shader-location = 1)
+ texcoords2: [^]f32, // Vertex texture second coordinates (UV - 2 components per vertex) (shader-location = 5)
+ normals: [^]f32, // Vertex normals (XYZ - 3 components per vertex) (shader-location = 2)
+ tangents: [^]f32, // Vertex tangents (XYZW - 4 components per vertex) (shader-location = 4)
+ colors: [^]c.uchar, // Vertex colors (RGBA - 4 components per vertex) (shader-location = 3)
+ indices: [^]c.ushort, // Vertex indices (in case vertex data comes indexed)
+ animVertices: [^]f32, // Animated vertex positions (after bones transformations)
+ animNormals: [^]f32, // Animated normals (after bones transformations)
+ boneIds: [^]c.uchar, // Vertex bone ids, max 255 bone ids, up to 4 bones influence by vertex (skinning) (shader-location = 6)
+ boneWeights: [^]f32, // Vertex bone weight, up to 4 bones influence by vertex (skinning) (shader-location = 7)
+ boneMatrices: [^]Matrix, // Bones animated transformation matrices
+ boneCount: c.int, // Number of bones
+ vaoId: c.uint, // OpenGL Vertex Array Object id
+ vboId: [^]c.uint, // OpenGL Vertex Buffer Objects id (default vertex data)
+}
+
+// Shader
+Shader :: struct {
+ id: c.uint, // Shader program id
+ locs: [^]c.int, // Shader locations array (RL_MAX_SHADER_LOCATIONS)
+}
+
+// MaterialMap
+MaterialMap :: struct {
+ texture: Texture2D, // Material map texture
+ color: Color, // Material map color
+ value: f32, // Material map value
+}
+
+// Material, includes shader and maps
+Material :: struct {
+ shader: Shader, // Material shader
+ maps: [^]MaterialMap, // Material maps array (MAX_MATERIAL_MAPS)
+ params: [4]f32, // Material generic parameters (if required)
+}
+
+// Transform, vertex transformation data
+Transform :: struct {
+ translation: Vector3, // Translation
+ rotation: Quaternion, // Rotation
+ scale: Vector3, // Scale
+}
+
+// Bone, skeletal animation bone
+BoneInfo :: struct {
+ name: [32]c.char, // Bone name
+ parent: c.int, // Bone parent
+}
+
+// Model, meshes, materials and animation data
+Model :: struct {
+ transform: Matrix, // Local transform matrix
+ meshCount: c.int, // Number of meshes
+ materialCount: c.int, // Number of materials
+ meshes: [^]Mesh, // Meshes array
+ materials: [^]Material, // Materials array
+ meshMaterial: ^c.int, // Mesh material number
+ boneCount: c.int, // Number of bones
+ bones: [^]BoneInfo, // Bones information (skeleton)
+ bindPose: [^]Transform, // Bones base transformation (pose)
+}
+
+// ModelAnimation
+ModelAnimation :: struct {
+ boneCount: c.int, // Number of bones
+ frameCount: c.int, // Number of animation frames
+ bones: [^]BoneInfo, // Bones information (skeleton)
+ framePoses: [^][^]Transform, // Poses array by frame
+ name: [32]c.char, // Animation name
+}
+
+// Ray, ray for raycasting
+Ray :: struct {
+ position: Vector3, // Ray position (origin)
+ direction: Vector3, // Ray direction (normalized)
+}
+
+// RayCollision, ray hit information
+RayCollision :: struct {
+ hit: bool, // Did the ray hit something?
+ distance: f32, // Distance to the nearest hit
+ point: Vector3, // Point of the nearest hit
+ normal: Vector3, // Surface normal of hit
+}
+
+// BoundingBox
+BoundingBox :: struct {
+ min: Vector3, // Minimum vertex box-corner
+ max: Vector3, // Maximum vertex box-corner
+}
+
+// Wave, audio wave data
+Wave :: struct {
+ frameCount: c.uint, // Total number of frames (considering channels)
+ sampleRate: c.uint, // Frequency (samples per second)
+ sampleSize: c.uint, // Bit depth (bits per sample): 8, 16, 32 (24 not supported)
+ channels: c.uint, // Number of channels (1-mono, 2-stereo, ...)
+ data: rawptr, // Buffer data pointer
+}
+
+// AudioStream, custom audio stream
+AudioStream :: struct {
+ buffer: rawptr, // Pointer to internal data used by the audio system
+ processor: rawptr, // Pointer to internal data processor, useful for audio effects
+ sampleRate: c.uint, // Frequency (samples per second)
+ sampleSize: c.uint, // Bit depth (bits per sample): 8, 16, 32 (24 not supported)
+ channels: c.uint, // Number of channels (1-mono, 2-stereo, ...)
+}
+
+// Sound
+Sound :: struct {
+ stream: AudioStream, // Audio stream
+ frameCount: c.uint, // Total number of frames (considering channels)
+}
+
+// Music, audio stream, anything longer than ~10 seconds should be streamed
+Music :: struct {
+ stream: AudioStream, // Audio stream
+ frameCount: c.uint, // Total number of frames (considering channels)
+ looping: bool, // Music looping enable
+ ctxType: c.int, // Type of music context (audio filetype)
+ ctxData: rawptr, // Audio context data, depends on type
+}
+
+// VrDeviceInfo, Head-Mounted-Display device parameters
+VrDeviceInfo :: struct {
+ hResolution: c.int, // Horizontal resolution in pixels
+ vResolution: c.int, // Vertical resolution in pixels
+ hScreenSize: f32, // Horizontal size in meters
+ vScreenSize: f32, // Vertical size in meters
+ eyeToScreenDistance: f32, // Distance between eye and display in meters
+ lensSeparationDistance: f32, // Lens separation distance in meters
+ interpupillaryDistance: f32, // IPD (distance between pupils) in meters
+ lensDistortionValues: [4]f32, // Lens distortion constant parameters
+ chromaAbCorrection: [4]f32, // Chromatic aberration correction parameters
+}
+
+// VrStereoConfig, VR stereo rendering configuration for simulator
+VrStereoConfig :: struct {
+ projection: [2]Matrix, // VR projection matrices (per eye)
+ viewOffset: [2]Matrix, // VR view offset matrices (per eye)
+ leftLensCenter: [2]f32, // VR left lens center
+ rightLensCenter: [2]f32, // VR right lens center
+ leftScreenCenter: [2]f32, // VR left screen center
+ rightScreenCenter: [2]f32, // VR right screen center
+ scale: [2]f32, // VR distortion scale
+ scaleIn: [2]f32, // VR distortion scale in
+}
+
+// File path list
+FilePathList :: struct {
+ capacity: c.uint, // Filepaths max entries
+ count: c.uint, // Filepaths entries count
+ paths: [^]cstring, // Filepaths entries
+}
+
+// Automation event
+AutomationEvent :: struct {
+ frame: c.uint, // Event frame
+ type: c.uint, // Event type (AutomationEventType)
+ params: [4]c.int, // Event parameters (if required)
+}
+
+// Automation event list
+AutomationEventList :: struct {
+ capacity: c.uint, // Events max entries (MAX_AUTOMATION_EVENTS)
+ count: c.uint, // Events entries count
+ events: ^AutomationEvent, // Events entries
+}
+
+//----------------------------------------------------------------------------------
+// Enumerators Definition
+//----------------------------------------------------------------------------------
+// System/Window config flags
+// NOTE: Every bit registers one state (use it with bit masks)
+// By default all flags are set to 0
+ConfigFlag :: enum c.int {
+ VSYNC_HINT = 6, // Set to try enabling V-Sync on GPU
+ FULLSCREEN_MODE = 1, // Set to run program in fullscreen
+ WINDOW_RESIZABLE = 2, // Set to allow resizable window
+ WINDOW_UNDECORATED = 3, // Set to disable window decoration (frame and buttons)
+ WINDOW_HIDDEN = 7, // Set to hide window
+ WINDOW_MINIMIZED = 9, // Set to minimize window (iconify)
+ WINDOW_MAXIMIZED = 10, // Set to maximize window (expanded to monitor)
+ WINDOW_UNFOCUSED = 11, // Set to window non focused
+ WINDOW_TOPMOST = 12, // Set to window always on top
+ WINDOW_ALWAYS_RUN = 8, // Set to allow windows running while minimized
+ WINDOW_TRANSPARENT = 4, // Set to allow transparent framebuffer
+ WINDOW_HIGHDPI = 13, // Set to support HighDPI
+ WINDOW_MOUSE_PASSTHROUGH = 14, // Set to support mouse passthrough, only supported when FLAG_WINDOW_UNDECORATED
+ BORDERLESS_WINDOWED_MODE = 15, // Set to run program in borderless windowed mode
+ MSAA_4X_HINT = 5, // Set to try enabling MSAA 4X
+ INTERLACED_HINT = 16, // Set to try enabling interlaced video format (for V3D)
+}
+
+ConfigFlags :: distinct bit_set[ConfigFlag; c.int]
+
+// Trace log level
+// NOTE: Organized by priority level
+TraceLogLevel :: enum c.int {
+ ALL = 0, // Display all logs
+ TRACE, // Trace logging, intended for internal use only
+ DEBUG, // Debug logging, used for internal debugging, it should be disabled on release builds
+ INFO, // Info logging, used for program execution info
+ WARNING, // Warning logging, used on recoverable failures
+ ERROR, // Error logging, used on unrecoverable failures
+ FATAL, // Fatal logging, used to abort program: exit(EXIT_FAILURE)
+ NONE, // Disable logging
+}
+
+// Keyboard keys (US keyboard layout)
+// NOTE: Use GetKeyPressed() to allow redefining
+// required keys for alternative layouts
+KeyboardKey :: enum c.int {
+ NULL = 0, // Key: NULL, used for no key pressed
+ APOSTROPHE = 39, // Key: '
+ COMMA = 44, // Key: ,
+ MINUS = 45, // Key: -
+ PERIOD = 46, // Key: .
+ SLASH = 47, // Key: /
+ ZERO = 48, // Key: 0
+ ONE = 49, // Key: 1
+ TWO = 50, // Key: 2
+ THREE = 51, // Key: 3
+ FOUR = 52, // Key: 4
+ FIVE = 53, // Key: 5
+ SIX = 54, // Key: 6
+ SEVEN = 55, // Key: 7
+ EIGHT = 56, // Key: 8
+ NINE = 57, // Key: 9
+ SEMICOLON = 59, // Key: ;
+ EQUAL = 61, // Key: =
+ A = 65, // Key: A | a
+ B = 66, // Key: B | b
+ C = 67, // Key: C | c
+ D = 68, // Key: D | d
+ E = 69, // Key: E | e
+ F = 70, // Key: F | f
+ G = 71, // Key: G | g
+ H = 72, // Key: H | h
+ I = 73, // Key: I | i
+ J = 74, // Key: J | j
+ K = 75, // Key: K | k
+ L = 76, // Key: L | l
+ M = 77, // Key: M | m
+ N = 78, // Key: N | n
+ O = 79, // Key: O | o
+ P = 80, // Key: P | p
+ Q = 81, // Key: Q | q
+ R = 82, // Key: R | r
+ S = 83, // Key: S | s
+ T = 84, // Key: T | t
+ U = 85, // Key: U | u
+ V = 86, // Key: V | v
+ W = 87, // Key: W | w
+ X = 88, // Key: X | x
+ Y = 89, // Key: Y | y
+ Z = 90, // Key: Z | z
+ LEFT_BRACKET = 91, // Key: [
+ BACKSLASH = 92, // Key: '\'
+ RIGHT_BRACKET = 93, // Key: ]
+ GRAVE = 96, // Key: `
+ SPACE = 32, // Key: Space
+ ESCAPE = 256, // Key: Esc
+ ENTER = 257, // Key: Enter
+ TAB = 258, // Key: Tab
+ BACKSPACE = 259, // Key: Backspace
+ INSERT = 260, // Key: Ins
+ DELETE = 261, // Key: Del
+ RIGHT = 262, // Key: Cursor right
+ LEFT = 263, // Key: Cursor left
+ DOWN = 264, // Key: Cursor down
+ UP = 265, // Key: Cursor up
+ PAGE_UP = 266, // Key: Page up
+ PAGE_DOWN = 267, // Key: Page down
+ HOME = 268, // Key: Home
+ END = 269, // Key: End
+ CAPS_LOCK = 280, // Key: Caps lock
+ SCROLL_LOCK = 281, // Key: Scroll down
+ NUM_LOCK = 282, // Key: Num lock
+ PRINT_SCREEN = 283, // Key: Print screen
+ PAUSE = 284, // Key: Pause
+ F1 = 290, // Key: F1
+ F2 = 291, // Key: F2
+ F3 = 292, // Key: F3
+ F4 = 293, // Key: F4
+ F5 = 294, // Key: F5
+ F6 = 295, // Key: F6
+ F7 = 296, // Key: F7
+ F8 = 297, // Key: F8
+ F9 = 298, // Key: F9
+ F10 = 299, // Key: F10
+ F11 = 300, // Key: F11
+ F12 = 301, // Key: F12
+ LEFT_SHIFT = 340, // Key: Shift left
+ LEFT_CONTROL = 341, // Key: Control left
+ LEFT_ALT = 342, // Key: Alt left
+ LEFT_SUPER = 343, // Key: Super left
+ RIGHT_SHIFT = 344, // Key: Shift right
+ RIGHT_CONTROL = 345, // Key: Control right
+ RIGHT_ALT = 346, // Key: Alt right
+ RIGHT_SUPER = 347, // Key: Super right
+ KB_MENU = 348, // Key: KB menu
+ KP_0 = 320, // Key: Keypad 0
+ KP_1 = 321, // Key: Keypad 1
+ KP_2 = 322, // Key: Keypad 2
+ KP_3 = 323, // Key: Keypad 3
+ KP_4 = 324, // Key: Keypad 4
+ KP_5 = 325, // Key: Keypad 5
+ KP_6 = 326, // Key: Keypad 6
+ KP_7 = 327, // Key: Keypad 7
+ KP_8 = 328, // Key: Keypad 8
+ KP_9 = 329, // Key: Keypad 9
+ KP_DECIMAL = 330, // Key: Keypad .
+ KP_DIVIDE = 331, // Key: Keypad /
+ KP_MULTIPLY = 332, // Key: Keypad *
+ KP_SUBTRACT = 333, // Key: Keypad -
+ KP_ADD = 334, // Key: Keypad +
+ KP_ENTER = 335, // Key: Keypad Enter
+ KP_EQUAL = 336, // Key: Keypad =
+ BACK = 4, // Key: Android back button
+ MENU = 5, // Key: Android menu button
+ VOLUME_UP = 24, // Key: Android volume up button
+ VOLUME_DOWN = 25, // Key: Android volume down button
+}
+
+// Add backwards compatibility support for deprecated names
+// MOUSE_LEFT_BUTTON :: MOUSE_BUTTON_LEFT
+// MOUSE_RIGHT_BUTTON :: MOUSE_BUTTON_RIGHT
+// MOUSE_MIDDLE_BUTTON :: MOUSE_BUTTON_MIDDLE
+
+// Mouse buttons
+MouseButton :: enum c.int {
+ LEFT = 0, // Mouse button left
+ RIGHT = 1, // Mouse button right
+ MIDDLE = 2, // Mouse button middle (pressed wheel)
+ SIDE = 3, // Mouse button side (advanced mouse device)
+ EXTRA = 4, // Mouse button extra (advanced mouse device)
+ FORWARD = 5, // Mouse button forward (advanced mouse device)
+ BACK = 6, // Mouse button back (advanced mouse device)
+}
+
+// Mouse cursor
+MouseCursor :: enum c.int {
+ DEFAULT = 0, // Default pointer shape
+ ARROW = 1, // Arrow shape
+ IBEAM = 2, // Text writing cursor shape
+ CROSSHAIR = 3, // Cross shape
+ POINTING_HAND = 4, // Pointing hand cursor
+ RESIZE_EW = 5, // Horizontal resize/move arrow shape
+ RESIZE_NS = 6, // Vertical resize/move arrow shape
+ RESIZE_NWSE = 7, // Top-left to bottom-right diagonal resize/move arrow shape
+ RESIZE_NESW = 8, // The top-right to bottom-left diagonal resize/move arrow shape
+ RESIZE_ALL = 9, // The omnidirectional resize/move cursor shape
+ NOT_ALLOWED = 10, // The operation-not-allowed shape
+}
+
+// Gamepad buttons
+GamepadButton :: enum c.int {
+ UNKNOWN = 0, // Unknown button, just for error checking
+ LEFT_FACE_UP, // Gamepad left DPAD up button
+ LEFT_FACE_RIGHT, // Gamepad left DPAD right button
+ LEFT_FACE_DOWN, // Gamepad left DPAD down button
+ LEFT_FACE_LEFT, // Gamepad left DPAD left button
+ RIGHT_FACE_UP, // Gamepad right button up (i.e. PS3: Triangle, Xbox: Y)
+ RIGHT_FACE_RIGHT, // Gamepad right button right (i.e. PS3: Circle, Xbox: B)
+ RIGHT_FACE_DOWN, // Gamepad right button down (i.e. PS3: Cross, Xbox: A)
+ RIGHT_FACE_LEFT, // Gamepad right button left (i.e. PS3: Square, Xbox: X)
+ LEFT_TRIGGER_1, // Gamepad top/back trigger left (first), it could be a trailing button
+ LEFT_TRIGGER_2, // Gamepad top/back trigger left (second), it could be a trailing button
+ RIGHT_TRIGGER_1, // Gamepad top/back trigger right (first), it could be a trailing button
+ RIGHT_TRIGGER_2, // Gamepad top/back trigger right (second), it could be a trailing button
+ MIDDLE_LEFT, // Gamepad center buttons, left one (i.e. PS3: Select)
+ MIDDLE, // Gamepad center buttons, middle one (i.e. PS3: PS, Xbox: XBOX)
+ MIDDLE_RIGHT, // Gamepad center buttons, right one (i.e. PS3: Start)
+ LEFT_THUMB, // Gamepad joystick pressed button left
+ RIGHT_THUMB, // Gamepad joystick pressed button right
+}
+
+// Gamepad axis
+GamepadAxis :: enum c.int {
+ LEFT_X = 0, // Gamepad left stick X axis
+ LEFT_Y = 1, // Gamepad left stick Y axis
+ RIGHT_X = 2, // Gamepad right stick X axis
+ RIGHT_Y = 3, // Gamepad right stick Y axis
+ LEFT_TRIGGER = 4, // Gamepad back trigger left, pressure level: [1..-1]
+ RIGHT_TRIGGER = 5, // Gamepad back trigger right, pressure level: [1..-1]
+}
+
+// Material map index
+MaterialMapIndex :: enum c.int {
+ ALBEDO = 0, // Albedo material (same as: MATERIAL_MAP_DIFFUSE)
+ METALNESS, // Metalness material (same as: MATERIAL_MAP_SPECULAR)
+ NORMAL, // Normal material
+ ROUGHNESS, // Roughness material
+ OCCLUSION, // Ambient occlusion material
+ EMISSION, // Emission material
+ HEIGHT, // Heightmap material
+ CUBEMAP, // Cubemap material (NOTE: Uses GL_TEXTURE_CUBE_MAP)
+ IRRADIANCE, // Irradiance material (NOTE: Uses GL_TEXTURE_CUBE_MAP)
+ PREFILTER, // Prefilter material (NOTE: Uses GL_TEXTURE_CUBE_MAP)
+ BRDF, // Brdf material
+}
+
+// MATERIAL_MAP_DIFFUSE :: MATERIAL_MAP_ALBEDO
+// MATERIAL_MAP_SPECULAR :: MATERIAL_MAP_METALNESS
+
+// Shader location index
+ShaderLocationIndex :: enum c.int {
+ VERTEX_POSITION = 0, // Shader location: vertex attribute: position
+ VERTEX_TEXCOORD01, // Shader location: vertex attribute: texcoord01
+ VERTEX_TEXCOORD02, // Shader location: vertex attribute: texcoord02
+ VERTEX_NORMAL, // Shader location: vertex attribute: normal
+ VERTEX_TANGENT, // Shader location: vertex attribute: tangent
+ VERTEX_COLOR, // Shader location: vertex attribute: color
+ MATRIX_MVP, // Shader location: matrix uniform: model-view-projection
+ MATRIX_VIEW, // Shader location: matrix uniform: view (camera transform)
+ MATRIX_PROJECTION, // Shader location: matrix uniform: projection
+ MATRIX_MODEL, // Shader location: matrix uniform: model (transform)
+ MATRIX_NORMAL, // Shader location: matrix uniform: normal
+ VECTOR_VIEW, // Shader location: vector uniform: view
+ COLOR_DIFFUSE, // Shader location: vector uniform: diffuse color
+ COLOR_SPECULAR, // Shader location: vector uniform: specular color
+ COLOR_AMBIENT, // Shader location: vector uniform: ambient color
+ MAP_ALBEDO, // Shader location: sampler2d texture: albedo (same as: SHADER_LOC_MAP_DIFFUSE)
+ MAP_METALNESS, // Shader location: sampler2d texture: metalness (same as: SHADER_LOC_MAP_SPECULAR)
+ MAP_NORMAL, // Shader location: sampler2d texture: normal
+ MAP_ROUGHNESS, // Shader location: sampler2d texture: roughness
+ MAP_OCCLUSION, // Shader location: sampler2d texture: occlusion
+ MAP_EMISSION, // Shader location: sampler2d texture: emission
+ MAP_HEIGHT, // Shader location: sampler2d texture: height
+ MAP_CUBEMAP, // Shader location: samplerCube texture: cubemap
+ MAP_IRRADIANCE, // Shader location: samplerCube texture: irradiance
+ MAP_PREFILTER, // Shader location: samplerCube texture: prefilter
+ MAP_BRDF, // Shader location: sampler2d texture: brdf
+ VERTEX_BONEIDS, // Shader location: vertex attribute: boneIds
+ VERTEX_BONEWEIGHTS, // Shader location: vertex attribute: boneWeights
+ BONE_MATRICES, // Shader location: array of matrices uniform: boneMatrices
+ VERTEX_INSTANCE_TX, // Shader location: vertex attribute: instanceTransform
+}
+
+// SHADER_LOC_MAP_DIFFUSE :: SHADER_LOC_MAP_ALBEDO
+// SHADER_LOC_MAP_SPECULAR :: SHADER_LOC_MAP_METALNESS
+
+// Shader uniform data type
+ShaderUniformDataType :: enum c.int {
+ FLOAT = 0, // Shader uniform type: float
+ VEC2, // Shader uniform type: vec2 (2 float)
+ VEC3, // Shader uniform type: vec3 (3 float)
+ VEC4, // Shader uniform type: vec4 (4 float)
+ INT, // Shader uniform type: int
+ IVEC2, // Shader uniform type: ivec2 (2 int)
+ IVEC3, // Shader uniform type: ivec3 (3 int)
+ IVEC4, // Shader uniform type: ivec4 (4 int)
+ UINT, // Shader uniform type: unsigned int
+ UIVEC2, // Shader uniform type: uivec2 (2 unsigned int)
+ UIVEC3, // Shader uniform type: uivec3 (3 unsigned int)
+ UIVEC4, // Shader uniform type: uivec4 (4 unsigned int)
+ SAMPLER2D, // Shader uniform type: sampler2d
+}
+
+// Shader attribute data types
+ShaderAttributeDataType :: enum c.int {
+ FLOAT = 0, // Shader attribute type: float
+ VEC2, // Shader attribute type: vec2 (2 float)
+ VEC3, // Shader attribute type: vec3 (3 float)
+ VEC4, // Shader attribute type: vec4 (4 float)
+}
+
+// Pixel formats
+// NOTE: Support depends on OpenGL version and platform
+PixelFormat :: enum c.int {
+ UNCOMPRESSED_GRAYSCALE = 1, // 8 bit per pixel (no alpha)
+ UNCOMPRESSED_GRAY_ALPHA, // 8*2 bpp (2 channels)
+ UNCOMPRESSED_R5G6B5, // 16 bpp
+ UNCOMPRESSED_R8G8B8, // 24 bpp
+ UNCOMPRESSED_R5G5B5A1, // 16 bpp (1 bit alpha)
+ UNCOMPRESSED_R4G4B4A4, // 16 bpp (4 bit alpha)
+ UNCOMPRESSED_R8G8B8A8, // 32 bpp
+ UNCOMPRESSED_R32, // 32 bpp (1 channel - float)
+ UNCOMPRESSED_R32G32B32, // 32*3 bpp (3 channels - float)
+ UNCOMPRESSED_R32G32B32A32, // 32*4 bpp (4 channels - float)
+ UNCOMPRESSED_R16, // 16 bpp (1 channel - half float)
+ UNCOMPRESSED_R16G16B16, // 16*3 bpp (3 channels - half float)
+ UNCOMPRESSED_R16G16B16A16, // 16*4 bpp (4 channels - half float)
+ COMPRESSED_DXT1_RGB, // 4 bpp (no alpha)
+ COMPRESSED_DXT1_RGBA, // 4 bpp (1 bit alpha)
+ COMPRESSED_DXT3_RGBA, // 8 bpp
+ COMPRESSED_DXT5_RGBA, // 8 bpp
+ COMPRESSED_ETC1_RGB, // 4 bpp
+ COMPRESSED_ETC2_RGB, // 4 bpp
+ COMPRESSED_ETC2_EAC_RGBA, // 8 bpp
+ COMPRESSED_PVRT_RGB, // 4 bpp
+ COMPRESSED_PVRT_RGBA, // 4 bpp
+ COMPRESSED_ASTC_4x4_RGBA, // 8 bpp
+ COMPRESSED_ASTC_8x8_RGBA, // 2 bpp
+}
+
+// Texture parameters: filter mode
+// NOTE 1: Filtering considers mipmaps if available in the texture
+// NOTE 2: Filter is accordingly set for minification and magnification
+TextureFilter :: enum c.int {
+ POINT = 0, // No filter, just pixel approximation
+ BILINEAR, // Linear filtering
+ TRILINEAR, // Trilinear filtering (linear with mipmaps)
+ ANISOTROPIC_4X, // Anisotropic filtering 4x
+ ANISOTROPIC_8X, // Anisotropic filtering 8x
+ ANISOTROPIC_16X, // Anisotropic filtering 16x
+}
+
+// Texture parameters: wrap mode
+TextureWrap :: enum c.int {
+ REPEAT = 0, // Repeats texture in tiled mode
+ CLAMP, // Clamps texture to edge pixel in tiled mode
+ MIRROR_REPEAT, // Mirrors and repeats the texture in tiled mode
+ MIRROR_CLAMP, // Mirrors and clamps to border the texture in tiled mode
+}
+
+// Cubemap layouts
+CubemapLayout :: enum c.int {
+ AUTO_DETECT = 0, // Automatically detect layout type
+ LINE_VERTICAL, // Layout is defined by a vertical line with faces
+ LINE_HORIZONTAL, // Layout is defined by a horizontal line with faces
+ CROSS_THREE_BY_FOUR, // Layout is defined by a 3x4 cross with cubemap faces
+ CROSS_FOUR_BY_THREE, // Layout is defined by a 4x3 cross with cubemap faces
+}
+
+// Font type, defines generation method
+FontType :: enum c.int {
+ DEFAULT = 0, // Default font generation, anti-aliased
+ BITMAP, // Bitmap font generation, no anti-aliasing
+ SDF, // SDF font generation, requires external shader
+}
+
+// Color blending modes (pre-defined)
+BlendMode :: enum c.int {
+ ALPHA = 0, // Blend textures considering alpha (default)
+ ADDITIVE, // Blend textures adding colors
+ MULTIPLIED, // Blend textures multiplying colors
+ ADD_COLORS, // Blend textures adding colors (alternative)
+ SUBTRACT_COLORS, // Blend textures subtracting colors (alternative)
+ ALPHA_PREMULTIPLY, // Blend premultiplied textures considering alpha
+ CUSTOM, // Blend textures using custom src/dst factors (use rlSetBlendFactors())
+ CUSTOM_SEPARATE, // Blend textures using custom rgb/alpha separate src/dst factors (use rlSetBlendFactorsSeparate())
+}
+
+// Gesture
+// NOTE: Provided as bit-wise flags to enable only desired gestures
+Gesture :: enum c.int {
+ TAP = 0, // Tap gesture
+ DOUBLETAP = 1, // Double tap gesture
+ HOLD = 2, // Hold gesture
+ DRAG = 3, // Drag gesture
+ SWIPE_RIGHT = 4, // Swipe right gesture
+ SWIPE_LEFT = 5, // Swipe left gesture
+ SWIPE_UP = 6, // Swipe up gesture
+ SWIPE_DOWN = 7, // Swipe down gesture
+ PINCH_IN = 8, // Pinch in gesture
+ PINCH_OUT = 9, // Pinch out gesture
+}
+
+Gestures :: distinct bit_set[Gesture; c.int]
+
+// Camera system modes
+CameraMode :: enum c.int {
+ CUSTOM = 0, // Camera custom, controlled by user (UpdateCamera() does nothing)
+ FREE, // Camera free mode
+ ORBITAL, // Camera orbital, around target, zoom supported
+ FIRST_PERSON, // Camera first person
+ THIRD_PERSON, // Camera third person
+}
+
+// Camera projection
+CameraProjection :: enum c.int {
+ PERSPECTIVE = 0, // Perspective projection
+ ORTHOGRAPHIC, // Orthographic projection
+}
+
+// N-patch layout
+NPatchLayout :: enum c.int {
+ NINE_PATCH = 0, // Npatch layout: 3x3 tiles
+ THREE_PATCH_VERTICAL, // Npatch layout: 1x3 tiles
+ THREE_PATCH_HORIZONTAL, // Npatch layout: 3x1 tiles
+}
+
+// Callbacks to hook some internal functions
+// WARNING: These callbacks are intended for advanced users
+TraceLogCallback :: proc "c" (c.int, cstring, ^c.va_list) // Logging: Redirect trace log messages
+
+LoadFileDataCallback :: proc "c" (cstring, ^c.int) -> ^c.uchar // FileIO: Load binary data
+
+SaveFileDataCallback :: proc "c" (cstring, rawptr, c.int) -> bool // FileIO: Save binary data
+
+LoadFileTextCallback :: proc "c" (cstring) -> cstring // FileIO: Load text data
+
+SaveFileTextCallback :: proc "c" (cstring, cstring) -> bool // FileIO: Save text data
+
+// Screen-space-related functions
+// GetMouseRay :: GetScreenToWorldRay // Compatibility hack for previous raylib versions
+
+//------------------------------------------------------------------------------------
+// Audio Loading and Playing Functions (Module: audio)
+//------------------------------------------------------------------------------------
+AudioCallback :: proc "c" (rawptr, c.uint)
+
+@(default_calling_convention="c", link_prefix="")
+foreign lib {
+ // Window-related functions
+ InitWindow :: proc(width: c.int, height: c.int, title: cstring) --- // Initialize window and OpenGL context
+ CloseWindow :: proc() --- // Close window and unload OpenGL context
+ WindowShouldClose :: proc() -> bool --- // Check if application should close (KEY_ESCAPE pressed or windows close icon clicked)
+ IsWindowReady :: proc() -> bool --- // Check if window has been initialized successfully
+ IsWindowFullscreen :: proc() -> bool --- // Check if window is currently fullscreen
+ IsWindowHidden :: proc() -> bool --- // Check if window is currently hidden
+ IsWindowMinimized :: proc() -> bool --- // Check if window is currently minimized
+ IsWindowMaximized :: proc() -> bool --- // Check if window is currently maximized
+ IsWindowFocused :: proc() -> bool --- // Check if window is currently focused
+ IsWindowResized :: proc() -> bool --- // Check if window has been resized last frame
+ IsWindowState :: proc(flag: c.uint) -> bool --- // Check if one specific window flag is enabled
+ SetWindowState :: proc(flags: c.uint) --- // Set window configuration state using flags
+ ClearWindowState :: proc(flags: c.uint) --- // Clear window configuration state flags
+ ToggleFullscreen :: proc() --- // Toggle window state: fullscreen/windowed, resizes monitor to match window resolution
+ ToggleBorderlessWindowed :: proc() --- // Toggle window state: borderless windowed, resizes window to match monitor resolution
+ MaximizeWindow :: proc() --- // Set window state: maximized, if resizable
+ MinimizeWindow :: proc() --- // Set window state: minimized, if resizable
+ RestoreWindow :: proc() --- // Set window state: not minimized/maximized
+ SetWindowIcon :: proc(image: Image) --- // Set icon for window (single image, RGBA 32bit)
+ SetWindowIcons :: proc(images: ^Image, count: c.int) --- // Set icon for window (multiple images, RGBA 32bit)
+ SetWindowTitle :: proc(title: cstring) --- // Set title for window
+ SetWindowPosition :: proc(x: c.int, y: c.int) --- // Set window position on screen
+ SetWindowMonitor :: proc(monitor: c.int) --- // Set monitor for the current window
+ SetWindowMinSize :: proc(width: c.int, height: c.int) --- // Set window minimum dimensions (for FLAG_WINDOW_RESIZABLE)
+ SetWindowMaxSize :: proc(width: c.int, height: c.int) --- // Set window maximum dimensions (for FLAG_WINDOW_RESIZABLE)
+ SetWindowSize :: proc(width: c.int, height: c.int) --- // Set window dimensions
+ SetWindowOpacity :: proc(opacity: f32) --- // Set window opacity [0.0f..1.0f]
+ SetWindowFocused :: proc() --- // Set window focused
+ GetWindowHandle :: proc() -> rawptr --- // Get native window handle
+ GetScreenWidth :: proc() -> c.int --- // Get current screen width
+ GetScreenHeight :: proc() -> c.int --- // Get current screen height
+ GetRenderWidth :: proc() -> c.int --- // Get current render width (it considers HiDPI)
+ GetRenderHeight :: proc() -> c.int --- // Get current render height (it considers HiDPI)
+ GetMonitorCount :: proc() -> c.int --- // Get number of connected monitors
+ GetCurrentMonitor :: proc() -> c.int --- // Get current monitor where window is placed
+ GetMonitorPosition :: proc(monitor: c.int) -> Vector2 --- // Get specified monitor position
+ GetMonitorWidth :: proc(monitor: c.int) -> c.int --- // Get specified monitor width (current video mode used by monitor)
+ GetMonitorHeight :: proc(monitor: c.int) -> c.int --- // Get specified monitor height (current video mode used by monitor)
+ GetMonitorPhysicalWidth :: proc(monitor: c.int) -> c.int --- // Get specified monitor physical width in millimetres
+ GetMonitorPhysicalHeight :: proc(monitor: c.int) -> c.int --- // Get specified monitor physical height in millimetres
+ GetMonitorRefreshRate :: proc(monitor: c.int) -> c.int --- // Get specified monitor refresh rate
+ GetWindowPosition :: proc() -> Vector2 --- // Get window position XY on monitor
+ GetWindowScaleDPI :: proc() -> Vector2 --- // Get window scale DPI factor
+ GetMonitorName :: proc(monitor: c.int) -> cstring --- // Get the human-readable, UTF-8 encoded name of the specified monitor
+ SetClipboardText :: proc(text: cstring) --- // Set clipboard text content
+ GetClipboardText :: proc() -> cstring --- // Get clipboard text content
+ GetClipboardImage :: proc() -> Image --- // Get clipboard image content
+ EnableEventWaiting :: proc() --- // Enable waiting for events on EndDrawing(), no automatic event polling
+ DisableEventWaiting :: proc() --- // Disable waiting for events on EndDrawing(), automatic events polling
+
+ // Cursor-related functions
+ ShowCursor :: proc() --- // Shows cursor
+ HideCursor :: proc() --- // Hides cursor
+ IsCursorHidden :: proc() -> bool --- // Check if cursor is not visible
+ EnableCursor :: proc() --- // Enables cursor (unlock cursor)
+ DisableCursor :: proc() --- // Disables cursor (lock cursor)
+ IsCursorOnScreen :: proc() -> bool --- // Check if cursor is on the screen
+
+ // Drawing-related functions
+ ClearBackground :: proc(color: Color) --- // Set background color (framebuffer clear color)
+ BeginDrawing :: proc() --- // Setup canvas (framebuffer) to start drawing
+ EndDrawing :: proc() --- // End canvas drawing and swap buffers (double buffering)
+ BeginMode2D :: proc(camera: Camera2D) --- // Begin 2D mode with custom camera (2D)
+ EndMode2D :: proc() --- // Ends 2D mode with custom camera
+ BeginMode3D :: proc(camera: Camera3D) --- // Begin 3D mode with custom camera (3D)
+ EndMode3D :: proc() --- // Ends 3D mode and returns to default 2D orthographic mode
+ BeginTextureMode :: proc(target: RenderTexture2D) --- // Begin drawing to render texture
+ EndTextureMode :: proc() --- // Ends drawing to render texture
+ BeginShaderMode :: proc(shader: Shader) --- // Begin custom shader drawing
+ EndShaderMode :: proc() --- // End custom shader drawing (use default shader)
+ BeginBlendMode :: proc(mode: c.int) --- // Begin blending mode (alpha, additive, multiplied, subtract, custom)
+ EndBlendMode :: proc() --- // End blending mode (reset to default: alpha blending)
+ BeginScissorMode :: proc(x: c.int, y: c.int, width: c.int, height: c.int) --- // Begin scissor mode (define screen area for following drawing)
+ EndScissorMode :: proc() --- // End scissor mode
+ BeginVrStereoMode :: proc(config: VrStereoConfig) --- // Begin stereo rendering (requires VR simulator)
+ EndVrStereoMode :: proc() --- // End stereo rendering (requires VR simulator)
+
+ // VR stereo config functions for VR simulator
+ LoadVrStereoConfig :: proc(device: VrDeviceInfo) -> VrStereoConfig --- // Load VR stereo config for VR simulator device parameters
+ UnloadVrStereoConfig :: proc(config: VrStereoConfig) --- // Unload VR stereo config
+
+ // Shader management functions
+ // NOTE: Shader functionality is not available on OpenGL 1.1
+ LoadShader :: proc(vsFileName: cstring, fsFileName: cstring) -> Shader --- // Load shader from files and bind default locations
+ LoadShaderFromMemory :: proc(vsCode: cstring, fsCode: cstring) -> Shader --- // Load shader from code strings and bind default locations
+ IsShaderValid :: proc(shader: Shader) -> bool --- // Check if a shader is valid (loaded on GPU)
+ GetShaderLocation :: proc(shader: Shader, uniformName: cstring) -> c.int --- // Get shader uniform location
+ GetShaderLocationAttrib :: proc(shader: Shader, attribName: cstring) -> c.int --- // Get shader attribute location
+ SetShaderValue :: proc(shader: Shader, locIndex: c.int, value: rawptr, uniformType: ShaderUniformDataType) --- // Set shader uniform value
+ SetShaderValueV :: proc(shader: Shader, locIndex: c.int, value: rawptr, uniformType: ShaderUniformDataType, count: c.int) --- // Set shader uniform value vector
+ SetShaderValueMatrix :: proc(shader: Shader, locIndex: c.int, mat: Matrix) --- // Set shader uniform value (matrix 4x4)
+ SetShaderValueTexture :: proc(shader: Shader, locIndex: c.int, texture: Texture2D) --- // Set shader uniform value and bind the texture (sampler2d)
+ UnloadShader :: proc(shader: Shader) --- // Unload shader from GPU memory (VRAM)
+ GetScreenToWorldRay :: proc(position: Vector2, camera: Camera) -> Ray --- // Get a ray trace from screen position (i.e mouse)
+ GetScreenToWorldRayEx :: proc(position: Vector2, camera: Camera, width: c.int, height: c.int) -> Ray --- // Get a ray trace from screen position (i.e mouse) in a viewport
+ GetWorldToScreen :: proc(position: Vector3, camera: Camera) -> Vector2 --- // Get the screen space position for a 3d world space position
+ GetWorldToScreenEx :: proc(position: Vector3, camera: Camera, width: c.int, height: c.int) -> Vector2 --- // Get size position for a 3d world space position
+ GetWorldToScreen2D :: proc(position: Vector2, camera: Camera2D) -> Vector2 --- // Get the screen space position for a 2d camera world space position
+ GetScreenToWorld2D :: proc(position: Vector2, camera: Camera2D) -> Vector2 --- // Get the world space position for a 2d camera screen space position
+ GetCameraMatrix :: proc(camera: Camera) -> Matrix --- // Get camera transform matrix (view matrix)
+ GetCameraMatrix2D :: proc(camera: Camera2D) -> Matrix --- // Get camera 2d transform matrix
+
+ // Timing-related functions
+ SetTargetFPS :: proc(fps: c.int) --- // Set target FPS (maximum)
+ GetFrameTime :: proc() -> f32 --- // Get time in seconds for last frame drawn (delta time)
+ GetTime :: proc() -> f64 --- // Get elapsed time in seconds since InitWindow()
+ GetFPS :: proc() -> c.int --- // Get current FPS
+
+ // Custom frame control functions
+ // NOTE: Those functions are intended for advanced users that want full control over the frame processing
+ // By default EndDrawing() does this job: draws everything + SwapScreenBuffer() + manage frame timing + PollInputEvents()
+ // To avoid that behaviour and control frame processes manually, enable in config.h: SUPPORT_CUSTOM_FRAME_CONTROL
+ SwapScreenBuffer :: proc() --- // Swap back buffer with front buffer (screen drawing)
+ PollInputEvents :: proc() --- // Register all input events
+ WaitTime :: proc(seconds: f64) --- // Wait for some time (halt program execution)
+
+ // Random values generation functions
+ SetRandomSeed :: proc(seed: c.uint) --- // Set the seed for the random number generator
+ GetRandomValue :: proc(min: c.int, max: c.int) -> c.int --- // Get a random value between min and max (both included)
+ LoadRandomSequence :: proc(count: c.uint, min: c.int, max: c.int) -> ^c.int --- // Load random values sequence, no values repeated
+ UnloadRandomSequence :: proc(sequence: ^c.int) --- // Unload random values sequence
+
+ // Misc. functions
+ TakeScreenshot :: proc(fileName: cstring) --- // Takes a screenshot of current screen (filename extension defines format)
+ SetConfigFlags :: proc(flags: ConfigFlags) --- // Setup init configuration flags (view FLAGS)
+ OpenURL :: proc(url: cstring) --- // Open URL with default system browser (if available)
+
+ // NOTE: Following functions implemented in module [utils]
+ //------------------------------------------------------------------
+ TraceLog :: proc(logLevel: c.int, text: cstring, #c_vararg _: ..any) --- // Show trace log messages (LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERROR...)
+ SetTraceLogLevel :: proc(logLevel: c.int) --- // Set the current threshold (minimum) log level
+ MemAlloc :: proc(size: c.uint) -> rawptr --- // Internal memory allocator
+ MemRealloc :: proc(ptr: rawptr, size: c.uint) -> rawptr --- // Internal memory reallocator
+ MemFree :: proc(ptr: rawptr) --- // Internal memory free
+
+ // Set custom callbacks
+ // WARNING: Callbacks setup is intended for advanced users
+ SetTraceLogCallback :: proc(callback: TraceLogCallback) --- // Set custom trace log
+ SetLoadFileDataCallback :: proc(callback: LoadFileDataCallback) --- // Set custom file binary data loader
+ SetSaveFileDataCallback :: proc(callback: SaveFileDataCallback) --- // Set custom file binary data saver
+ SetLoadFileTextCallback :: proc(callback: LoadFileTextCallback) --- // Set custom file text data loader
+ SetSaveFileTextCallback :: proc(callback: SaveFileTextCallback) --- // Set custom file text data saver
+
+ // Files management functions
+ LoadFileData :: proc(fileName: cstring, dataSize: ^c.int) -> ^c.uchar --- // Load file data as byte array (read)
+ UnloadFileData :: proc(data: ^c.uchar) --- // Unload file data allocated by LoadFileData()
+ SaveFileData :: proc(fileName: cstring, data: rawptr, dataSize: c.int) -> bool --- // Save data to file from byte array (write), returns true on success
+ ExportDataAsCode :: proc(data: ^c.uchar, dataSize: c.int, fileName: cstring) -> bool --- // Export data to code (.h), returns true on success
+ LoadFileText :: proc(fileName: cstring) -> cstring --- // Load text data from file (read), returns a '\0' terminated string
+ UnloadFileText :: proc(text: cstring) --- // Unload file text data allocated by LoadFileText()
+ SaveFileText :: proc(fileName: cstring, text: cstring) -> bool --- // Save text data to file (write), string must be '\0' terminated, returns true on success
+
+ // File system functions
+ FileExists :: proc(fileName: cstring) -> bool --- // Check if file exists
+ DirectoryExists :: proc(dirPath: cstring) -> bool --- // Check if a directory path exists
+ IsFileExtension :: proc(fileName: cstring, ext: cstring) -> bool --- // Check file extension (including point: .png, .wav)
+ GetFileLength :: proc(fileName: cstring) -> c.int --- // Get file length in bytes (NOTE: GetFileSize() conflicts with windows.h)
+ GetFileExtension :: proc(fileName: cstring) -> cstring --- // Get pointer to extension for a filename string (includes dot: '.png')
+ GetFileName :: proc(filePath: cstring) -> cstring --- // Get pointer to filename for a path string
+ GetFileNameWithoutExt :: proc(filePath: cstring) -> cstring --- // Get filename string without extension (uses static string)
+ GetDirectoryPath :: proc(filePath: cstring) -> cstring --- // Get full path for a given fileName with path (uses static string)
+ GetPrevDirectoryPath :: proc(dirPath: cstring) -> cstring --- // Get previous directory path for a given path (uses static string)
+ GetWorkingDirectory :: proc() -> cstring --- // Get current working directory (uses static string)
+ GetApplicationDirectory :: proc() -> cstring --- // Get the directory of the running application (uses static string)
+ MakeDirectory :: proc(dirPath: cstring) -> c.int --- // Create directories (including full path requested), returns 0 on success
+ ChangeDirectory :: proc(dir: cstring) -> bool --- // Change working directory, return true on success
+ IsPathFile :: proc(path: cstring) -> bool --- // Check if a given path is a file or a directory
+ IsFileNameValid :: proc(fileName: cstring) -> bool --- // Check if fileName is valid for the platform/OS
+ LoadDirectoryFiles :: proc(dirPath: cstring) -> FilePathList --- // Load directory filepaths
+ LoadDirectoryFilesEx :: proc(basePath: cstring, filter: cstring, scanSubdirs: bool) -> FilePathList --- // Load directory filepaths with extension filtering and recursive directory scan. Use 'DIR' in the filter string to include directories in the result
+ UnloadDirectoryFiles :: proc(files: FilePathList) --- // Unload filepaths
+ IsFileDropped :: proc() -> bool --- // Check if a file has been dropped into window
+ LoadDroppedFiles :: proc() -> FilePathList --- // Load dropped filepaths
+ UnloadDroppedFiles :: proc(files: FilePathList) --- // Unload dropped filepaths
+ GetFileModTime :: proc(fileName: cstring) -> c.long --- // Get file modification time (last write time)
+
+ // Compression/Encoding functionality
+ CompressData :: proc(data: ^c.uchar, dataSize: c.int, compDataSize: ^c.int) -> ^c.uchar --- // Compress data (DEFLATE algorithm), memory must be MemFree()
+ DecompressData :: proc(compData: ^c.uchar, compDataSize: c.int, dataSize: ^c.int) -> ^c.uchar --- // Decompress data (DEFLATE algorithm), memory must be MemFree()
+ EncodeDataBase64 :: proc(data: ^c.uchar, dataSize: c.int, outputSize: ^c.int) -> cstring --- // Encode data to Base64 string, memory must be MemFree()
+ DecodeDataBase64 :: proc(data: ^c.uchar, outputSize: ^c.int) -> ^c.uchar --- // Decode Base64 string data, memory must be MemFree()
+ ComputeCRC32 :: proc(data: ^c.uchar, dataSize: c.int) -> c.uint --- // Compute CRC32 hash code
+ ComputeMD5 :: proc(data: ^c.uchar, dataSize: c.int) -> ^c.uint --- // Compute MD5 hash code, returns static int[4] (16 bytes)
+ ComputeSHA1 :: proc(data: ^c.uchar, dataSize: c.int) -> ^c.uint --- // Compute SHA1 hash code, returns static int[5] (20 bytes)
+
+ // Automation events functionality
+ LoadAutomationEventList :: proc(fileName: cstring) -> AutomationEventList --- // Load automation events list from file, NULL for empty list, capacity = MAX_AUTOMATION_EVENTS
+ UnloadAutomationEventList :: proc(list: AutomationEventList) --- // Unload automation events list from file
+ ExportAutomationEventList :: proc(list: AutomationEventList, fileName: cstring) -> bool --- // Export automation events list as text file
+ SetAutomationEventList :: proc(list: ^AutomationEventList) --- // Set automation event list to record to
+ SetAutomationEventBaseFrame :: proc(frame: c.int) --- // Set automation event internal base frame to start recording
+ StartAutomationEventRecording :: proc() --- // Start recording automation events (AutomationEventList must be set)
+ StopAutomationEventRecording :: proc() --- // Stop recording automation events
+ PlayAutomationEvent :: proc(event: AutomationEvent) --- // Play a recorded automation event
+
+ // Input-related functions: keyboard
+ IsKeyPressed :: proc(key: KeyboardKey) -> bool --- // Check if a key has been pressed once
+ IsKeyPressedRepeat :: proc(key: KeyboardKey) -> bool --- // Check if a key has been pressed again
+ IsKeyDown :: proc(key: KeyboardKey) -> bool --- // Check if a key is being pressed
+ IsKeyReleased :: proc(key: KeyboardKey) -> bool --- // Check if a key has been released once
+ IsKeyUp :: proc(key: KeyboardKey) -> bool --- // Check if a key is NOT being pressed
+ GetKeyPressed :: proc() -> KeyboardKey --- // Get key pressed (keycode), call it multiple times for keys queued, returns 0 when the queue is empty
+ GetCharPressed :: proc() -> c.int --- // Get char pressed (unicode), call it multiple times for chars queued, returns 0 when the queue is empty
+ GetKeyName :: proc(key: KeyboardKey) -> cstring --- // Get name of a QWERTY key on the current keyboard layout (eg returns string 'q' for KEY_A on an AZERTY keyboard)
+ SetExitKey :: proc(key: KeyboardKey) --- // Set a custom key to exit program (default is ESC)
+
+ // Input-related functions: gamepads
+ IsGamepadAvailable :: proc(gamepad: c.int) -> bool --- // Check if a gamepad is available
+ GetGamepadName :: proc(gamepad: c.int) -> cstring --- // Get gamepad internal name id
+ IsGamepadButtonPressed :: proc(gamepad: c.int, button: c.int) -> bool --- // Check if a gamepad button has been pressed once
+ IsGamepadButtonDown :: proc(gamepad: c.int, button: c.int) -> bool --- // Check if a gamepad button is being pressed
+ IsGamepadButtonReleased :: proc(gamepad: c.int, button: c.int) -> bool --- // Check if a gamepad button has been released once
+ IsGamepadButtonUp :: proc(gamepad: c.int, button: c.int) -> bool --- // Check if a gamepad button is NOT being pressed
+ GetGamepadButtonPressed :: proc() -> c.int --- // Get the last gamepad button pressed
+ GetGamepadAxisCount :: proc(gamepad: c.int) -> c.int --- // Get gamepad axis count for a gamepad
+ GetGamepadAxisMovement :: proc(gamepad: c.int, axis: c.int) -> f32 --- // Get axis movement value for a gamepad axis
+ SetGamepadMappings :: proc(mappings: cstring) -> c.int --- // Set internal gamepad mappings (SDL_GameControllerDB)
+ SetGamepadVibration :: proc(gamepad: c.int, leftMotor: f32, rightMotor: f32, duration: f32) --- // Set gamepad vibration for both motors (duration in seconds)
+
+ // Input-related functions: mouse
+ IsMouseButtonPressed :: proc(button: MouseButton) -> bool --- // Check if a mouse button has been pressed once
+ IsMouseButtonDown :: proc(button: MouseButton) -> bool --- // Check if a mouse button is being pressed
+ IsMouseButtonReleased :: proc(button: MouseButton) -> bool --- // Check if a mouse button has been released once
+ IsMouseButtonUp :: proc(button: MouseButton) -> bool --- // Check if a mouse button is NOT being pressed
+ GetMouseX :: proc() -> c.int --- // Get mouse position X
+ GetMouseY :: proc() -> c.int --- // Get mouse position Y
+ GetMousePosition :: proc() -> Vector2 --- // Get mouse position XY
+ GetMouseDelta :: proc() -> Vector2 --- // Get mouse delta between frames
+ SetMousePosition :: proc(x: c.int, y: c.int) --- // Set mouse position XY
+ SetMouseOffset :: proc(offsetX: c.int, offsetY: c.int) --- // Set mouse offset
+ SetMouseScale :: proc(scaleX: f32, scaleY: f32) --- // Set mouse scaling
+ GetMouseWheelMove :: proc() -> f32 --- // Get mouse wheel movement for X or Y, whichever is larger
+ GetMouseWheelMoveV :: proc() -> Vector2 --- // Get mouse wheel movement for both X and Y
+ SetMouseCursor :: proc(cursor: c.int) --- // Set mouse cursor
+
+ // Input-related functions: touch
+ GetTouchX :: proc() -> c.int --- // Get touch position X for touch point 0 (relative to screen size)
+ GetTouchY :: proc() -> c.int --- // Get touch position Y for touch point 0 (relative to screen size)
+ GetTouchPosition :: proc(index: c.int) -> Vector2 --- // Get touch position XY for a touch point index (relative to screen size)
+ GetTouchPointId :: proc(index: c.int) -> c.int --- // Get touch point identifier for given index
+ GetTouchPointCount :: proc() -> c.int --- // Get number of touch points
+
+ //------------------------------------------------------------------------------------
+ // Gestures and Touch Handling Functions (Module: rgestures)
+ //------------------------------------------------------------------------------------
+ SetGesturesEnabled :: proc(flags: Gestures) --- // Enable a set of gestures using flags
+ IsGestureDetected :: proc(gesture: Gestures) -> bool --- // Check if a gesture have been detected
+ GetGestureDetected :: proc() -> c.int --- // Get latest detected gesture
+ GetGestureHoldDuration :: proc() -> f32 --- // Get gesture hold time in seconds
+ GetGestureDragVector :: proc() -> Vector2 --- // Get gesture drag vector
+ GetGestureDragAngle :: proc() -> f32 --- // Get gesture drag angle
+ GetGesturePinchVector :: proc() -> Vector2 --- // Get gesture pinch delta
+ GetGesturePinchAngle :: proc() -> f32 --- // Get gesture pinch angle
+
+ //------------------------------------------------------------------------------------
+ // Camera System Functions (Module: rcamera)
+ //------------------------------------------------------------------------------------
+ UpdateCamera :: proc(camera: ^Camera, mode: c.int) --- // Update camera position for selected mode
+ UpdateCameraPro :: proc(camera: ^Camera, movement: Vector3, rotation: Vector3, zoom: f32) --- // Update camera movement/rotation
+
+ //------------------------------------------------------------------------------------
+ // Basic Shapes Drawing Functions (Module: shapes)
+ //------------------------------------------------------------------------------------
+ // Set texture and rectangle to be used on shapes drawing
+ // NOTE: It can be useful when using basic shapes and one single font,
+ // defining a font char white rectangle would allow drawing everything in a single draw call
+ SetShapesTexture :: proc(texture: Texture2D, source: Rectangle) --- // Set texture and rectangle to be used on shapes drawing
+ GetShapesTexture :: proc() -> Texture2D --- // Get texture that is used for shapes drawing
+ GetShapesTextureRectangle :: proc() -> Rectangle --- // Get texture source rectangle that is used for shapes drawing
+
+ // Basic shapes drawing functions
+ DrawPixel :: proc(posX: c.int, posY: c.int, color: Color) --- // Draw a pixel using geometry [Can be slow, use with care]
+ DrawPixelV :: proc(position: Vector2, color: Color) --- // Draw a pixel using geometry (Vector version) [Can be slow, use with care]
+ DrawLine :: proc(startPosX: c.int, startPosY: c.int, endPosX: c.int, endPosY: c.int, color: Color) --- // Draw a line
+ DrawLineV :: proc(startPos: Vector2, endPos: Vector2, color: Color) --- // Draw a line (using gl lines)
+ DrawLineEx :: proc(startPos: Vector2, endPos: Vector2, thick: f32, color: Color) --- // Draw a line (using triangles/quads)
+ DrawLineStrip :: proc(points: ^Vector2, pointCount: c.int, color: Color) --- // Draw lines sequence (using gl lines)
+ DrawLineBezier :: proc(startPos: Vector2, endPos: Vector2, thick: f32, color: Color) --- // Draw line segment cubic-bezier in-out interpolation
+ DrawCircle :: proc(centerX: c.int, centerY: c.int, radius: f32, color: Color) --- // Draw a color-filled circle
+ DrawCircleSector :: proc(center: Vector2, radius: f32, startAngle: f32, endAngle: f32, segments: c.int, color: Color) --- // Draw a piece of a circle
+ DrawCircleSectorLines :: proc(center: Vector2, radius: f32, startAngle: f32, endAngle: f32, segments: c.int, color: Color) --- // Draw circle sector outline
+ DrawCircleGradient :: proc(centerX: c.int, centerY: c.int, radius: f32, inner: Color, outer: Color) --- // Draw a gradient-filled circle
+ DrawCircleV :: proc(center: Vector2, radius: f32, color: Color) --- // Draw a color-filled circle (Vector version)
+ DrawCircleLines :: proc(centerX: c.int, centerY: c.int, radius: f32, color: Color) --- // Draw circle outline
+ DrawCircleLinesV :: proc(center: Vector2, radius: f32, color: Color) --- // Draw circle outline (Vector version)
+ DrawEllipse :: proc(centerX: c.int, centerY: c.int, radiusH: f32, radiusV: f32, color: Color) --- // Draw ellipse
+ DrawEllipseLines :: proc(centerX: c.int, centerY: c.int, radiusH: f32, radiusV: f32, color: Color) --- // Draw ellipse outline
+ DrawRing :: proc(center: Vector2, innerRadius: f32, outerRadius: f32, startAngle: f32, endAngle: f32, segments: c.int, color: Color) --- // Draw ring
+ DrawRingLines :: proc(center: Vector2, innerRadius: f32, outerRadius: f32, startAngle: f32, endAngle: f32, segments: c.int, color: Color) --- // Draw ring outline
+ DrawRectangle :: proc(posX: c.int, posY: c.int, width: c.int, height: c.int, color: Color) --- // Draw a color-filled rectangle
+ DrawRectangleV :: proc(position: Vector2, size: Vector2, color: Color) --- // Draw a color-filled rectangle (Vector version)
+ DrawRectangleRec :: proc(rec: Rectangle, color: Color) --- // Draw a color-filled rectangle
+ DrawRectanglePro :: proc(rec: Rectangle, origin: Vector2, rotation: f32, color: Color) --- // Draw a color-filled rectangle with pro parameters
+ DrawRectangleGradientV :: proc(posX: c.int, posY: c.int, width: c.int, height: c.int, top: Color, bottom: Color) --- // Draw a vertical-gradient-filled rectangle
+ DrawRectangleGradientH :: proc(posX: c.int, posY: c.int, width: c.int, height: c.int, left: Color, right: Color) --- // Draw a horizontal-gradient-filled rectangle
+ DrawRectangleGradientEx :: proc(rec: Rectangle, topLeft: Color, bottomLeft: Color, topRight: Color, bottomRight: Color) --- // Draw a gradient-filled rectangle with custom vertex colors
+ DrawRectangleLines :: proc(posX: c.int, posY: c.int, width: c.int, height: c.int, color: Color) --- // Draw rectangle outline
+ DrawRectangleLinesEx :: proc(rec: Rectangle, lineThick: f32, color: Color) --- // Draw rectangle outline with extended parameters
+ DrawRectangleRounded :: proc(rec: Rectangle, roundness: f32, segments: c.int, color: Color) --- // Draw rectangle with rounded edges
+ DrawRectangleRoundedLines :: proc(rec: Rectangle, roundness: f32, segments: c.int, color: Color) --- // Draw rectangle lines with rounded edges
+ DrawRectangleRoundedLinesEx :: proc(rec: Rectangle, roundness: f32, segments: c.int, lineThick: f32, color: Color) --- // Draw rectangle with rounded edges outline
+ DrawTriangle :: proc(v1: Vector2, v2: Vector2, v3: Vector2, color: Color) --- // Draw a color-filled triangle (vertex in counter-clockwise order!)
+ DrawTriangleLines :: proc(v1: Vector2, v2: Vector2, v3: Vector2, color: Color) --- // Draw triangle outline (vertex in counter-clockwise order!)
+ DrawTriangleFan :: proc(points: ^Vector2, pointCount: c.int, color: Color) --- // Draw a triangle fan defined by points (first vertex is the center)
+ DrawTriangleStrip :: proc(points: ^Vector2, pointCount: c.int, color: Color) --- // Draw a triangle strip defined by points
+ DrawPoly :: proc(center: Vector2, sides: c.int, radius: f32, rotation: f32, color: Color) --- // Draw a regular polygon (Vector version)
+ DrawPolyLines :: proc(center: Vector2, sides: c.int, radius: f32, rotation: f32, color: Color) --- // Draw a polygon outline of n sides
+ DrawPolyLinesEx :: proc(center: Vector2, sides: c.int, radius: f32, rotation: f32, lineThick: f32, color: Color) --- // Draw a polygon outline of n sides with extended parameters
+
+ // Splines drawing functions
+ DrawSplineLinear :: proc(points: ^Vector2, pointCount: c.int, thick: f32, color: Color) --- // Draw spline: Linear, minimum 2 points
+ DrawSplineBasis :: proc(points: ^Vector2, pointCount: c.int, thick: f32, color: Color) --- // Draw spline: B-Spline, minimum 4 points
+ DrawSplineCatmullRom :: proc(points: ^Vector2, pointCount: c.int, thick: f32, color: Color) --- // Draw spline: Catmull-Rom, minimum 4 points
+ DrawSplineBezierQuadratic :: proc(points: ^Vector2, pointCount: c.int, thick: f32, color: Color) --- // Draw spline: Quadratic Bezier, minimum 3 points (1 control point): [p1, c2, p3, c4...]
+ DrawSplineBezierCubic :: proc(points: ^Vector2, pointCount: c.int, thick: f32, color: Color) --- // Draw spline: Cubic Bezier, minimum 4 points (2 control points): [p1, c2, c3, p4, c5, c6...]
+ DrawSplineSegmentLinear :: proc(p1: Vector2, p2: Vector2, thick: f32, color: Color) --- // Draw spline segment: Linear, 2 points
+ DrawSplineSegmentBasis :: proc(p1: Vector2, p2: Vector2, p3: Vector2, p4: Vector2, thick: f32, color: Color) --- // Draw spline segment: B-Spline, 4 points
+ DrawSplineSegmentCatmullRom :: proc(p1: Vector2, p2: Vector2, p3: Vector2, p4: Vector2, thick: f32, color: Color) --- // Draw spline segment: Catmull-Rom, 4 points
+ DrawSplineSegmentBezierQuadratic :: proc(p1: Vector2, c2: Vector2, p3: Vector2, thick: f32, color: Color) --- // Draw spline segment: Quadratic Bezier, 2 points, 1 control point
+ DrawSplineSegmentBezierCubic :: proc(p1: Vector2, c2: Vector2, c3: Vector2, p4: Vector2, thick: f32, color: Color) --- // Draw spline segment: Cubic Bezier, 2 points, 2 control points
+
+ // Spline segment point evaluation functions, for a given t [0.0f .. 1.0f]
+ GetSplinePointLinear :: proc(startPos: Vector2, endPos: Vector2, t: f32) -> Vector2 --- // Get (evaluate) spline point: Linear
+ GetSplinePointBasis :: proc(p1: Vector2, p2: Vector2, p3: Vector2, p4: Vector2, t: f32) -> Vector2 --- // Get (evaluate) spline point: B-Spline
+ GetSplinePointCatmullRom :: proc(p1: Vector2, p2: Vector2, p3: Vector2, p4: Vector2, t: f32) -> Vector2 --- // Get (evaluate) spline point: Catmull-Rom
+ GetSplinePointBezierQuad :: proc(p1: Vector2, c2: Vector2, p3: Vector2, t: f32) -> Vector2 --- // Get (evaluate) spline point: Quadratic Bezier
+ GetSplinePointBezierCubic :: proc(p1: Vector2, c2: Vector2, c3: Vector2, p4: Vector2, t: f32) -> Vector2 --- // Get (evaluate) spline point: Cubic Bezier
+
+ // Basic shapes collision detection functions
+ CheckCollisionRecs :: proc(rec1: Rectangle, rec2: Rectangle) -> bool --- // Check collision between two rectangles
+ CheckCollisionCircles :: proc(center1: Vector2, radius1: f32, center2: Vector2, radius2: f32) -> bool --- // Check collision between two circles
+ CheckCollisionCircleRec :: proc(center: Vector2, radius: f32, rec: Rectangle) -> bool --- // Check collision between circle and rectangle
+ CheckCollisionCircleLine :: proc(center: Vector2, radius: f32, p1: Vector2, p2: Vector2) -> bool --- // Check if circle collides with a line created betweeen two points [p1] and [p2]
+ CheckCollisionPointRec :: proc(point: Vector2, rec: Rectangle) -> bool --- // Check if point is inside rectangle
+ CheckCollisionPointCircle :: proc(point: Vector2, center: Vector2, radius: f32) -> bool --- // Check if point is inside circle
+ CheckCollisionPointTriangle :: proc(point: Vector2, p1: Vector2, p2: Vector2, p3: Vector2) -> bool --- // Check if point is inside a triangle
+ CheckCollisionPointLine :: proc(point: Vector2, p1: Vector2, p2: Vector2, threshold: c.int) -> bool --- // Check if point belongs to line created between two points [p1] and [p2] with defined margin in pixels [threshold]
+ CheckCollisionPointPoly :: proc(point: Vector2, points: ^Vector2, pointCount: c.int) -> bool --- // Check if point is within a polygon described by array of vertices
+ CheckCollisionLines :: proc(startPos1: Vector2, endPos1: Vector2, startPos2: Vector2, endPos2: Vector2, collisionPoint: ^Vector2) -> bool --- // Check the collision between two lines defined by two points each, returns collision point by reference
+ GetCollisionRec :: proc(rec1: Rectangle, rec2: Rectangle) -> Rectangle --- // Get collision rectangle for two rectangles collision
+
+ // Image loading functions
+ // NOTE: These functions do not require GPU access
+ LoadImage :: proc(fileName: cstring) -> Image --- // Load image from file into CPU memory (RAM)
+ LoadImageRaw :: proc(fileName: cstring, width: c.int, height: c.int, format: c.int, headerSize: c.int) -> Image --- // Load image from RAW file data
+ LoadImageAnim :: proc(fileName: cstring, frames: ^c.int) -> Image --- // Load image sequence from file (frames appended to image.data)
+ LoadImageAnimFromMemory :: proc(fileType: cstring, fileData: ^c.uchar, dataSize: c.int, frames: ^c.int) -> Image --- // Load image sequence from memory buffer
+ LoadImageFromMemory :: proc(fileType: cstring, fileData: ^c.uchar, dataSize: c.int) -> Image --- // Load image from memory buffer, fileType refers to extension: i.e. '.png'
+ LoadImageFromTexture :: proc(texture: Texture2D) -> Image --- // Load image from GPU texture data
+ LoadImageFromScreen :: proc() -> Image --- // Load image from screen buffer and (screenshot)
+ IsImageValid :: proc(image: Image) -> bool --- // Check if an image is valid (data and parameters)
+ UnloadImage :: proc(image: Image) --- // Unload image from CPU memory (RAM)
+ ExportImage :: proc(image: Image, fileName: cstring) -> bool --- // Export image data to file, returns true on success
+ ExportImageToMemory :: proc(image: Image, fileType: cstring, fileSize: ^c.int) -> ^c.uchar --- // Export image to memory buffer
+ ExportImageAsCode :: proc(image: Image, fileName: cstring) -> bool --- // Export image as code file defining an array of bytes, returns true on success
+
+ // Image generation functions
+ GenImageColor :: proc(width: c.int, height: c.int, color: Color) -> Image --- // Generate image: plain color
+ GenImageGradientLinear :: proc(width: c.int, height: c.int, direction: c.int, start: Color, end: Color) -> Image --- // Generate image: linear gradient, direction in degrees [0..360], 0=Vertical gradient
+ GenImageGradientRadial :: proc(width: c.int, height: c.int, density: f32, inner: Color, outer: Color) -> Image --- // Generate image: radial gradient
+ GenImageGradientSquare :: proc(width: c.int, height: c.int, density: f32, inner: Color, outer: Color) -> Image --- // Generate image: square gradient
+ GenImageChecked :: proc(width: c.int, height: c.int, checksX: c.int, checksY: c.int, col1: Color, col2: Color) -> Image --- // Generate image: checked
+ GenImageWhiteNoise :: proc(width: c.int, height: c.int, factor: f32) -> Image --- // Generate image: white noise
+ GenImagePerlinNoise :: proc(width: c.int, height: c.int, offsetX: c.int, offsetY: c.int, scale: f32) -> Image --- // Generate image: perlin noise
+ GenImageCellular :: proc(width: c.int, height: c.int, tileSize: c.int) -> Image --- // Generate image: cellular algorithm, bigger tileSize means bigger cells
+ GenImageText :: proc(width: c.int, height: c.int, text: cstring) -> Image --- // Generate image: grayscale image from text data
+
+ // Image manipulation functions
+ ImageCopy :: proc(image: Image) -> Image --- // Create an image duplicate (useful for transformations)
+ ImageFromImage :: proc(image: Image, rec: Rectangle) -> Image --- // Create an image from another image piece
+ ImageFromChannel :: proc(image: Image, selectedChannel: c.int) -> Image --- // Create an image from a selected channel of another image (GRAYSCALE)
+ ImageText :: proc(text: cstring, fontSize: c.int, color: Color) -> Image --- // Create an image from text (default font)
+ ImageTextEx :: proc(font: Font, text: cstring, fontSize: f32, spacing: f32, tint: Color) -> Image --- // Create an image from text (custom sprite font)
+ ImageFormat :: proc(image: ^Image, newFormat: c.int) --- // Convert image data to desired format
+ ImageToPOT :: proc(image: ^Image, fill: Color) --- // Convert image to POT (power-of-two)
+ ImageCrop :: proc(image: ^Image, crop: Rectangle) --- // Crop an image to a defined rectangle
+ ImageAlphaCrop :: proc(image: ^Image, threshold: f32) --- // Crop image depending on alpha value
+ ImageAlphaClear :: proc(image: ^Image, color: Color, threshold: f32) --- // Clear alpha channel to desired color
+ ImageAlphaMask :: proc(image: ^Image, alphaMask: Image) --- // Apply alpha mask to image
+ ImageAlphaPremultiply :: proc(image: ^Image) --- // Premultiply alpha channel
+ ImageBlurGaussian :: proc(image: ^Image, blurSize: c.int) --- // Apply Gaussian blur using a box blur approximation
+ ImageKernelConvolution :: proc(image: ^Image, kernel: ^f32, kernelSize: c.int) --- // Apply custom square convolution kernel to image
+ ImageResize :: proc(image: ^Image, newWidth: c.int, newHeight: c.int) --- // Resize image (Bicubic scaling algorithm)
+ ImageResizeNN :: proc(image: ^Image, newWidth: c.int, newHeight: c.int) --- // Resize image (Nearest-Neighbor scaling algorithm)
+ ImageResizeCanvas :: proc(image: ^Image, newWidth: c.int, newHeight: c.int, offsetX: c.int, offsetY: c.int, fill: Color) --- // Resize canvas and fill with color
+ ImageMipmaps :: proc(image: ^Image) --- // Compute all mipmap levels for a provided image
+ ImageDither :: proc(image: ^Image, rBpp: c.int, gBpp: c.int, bBpp: c.int, aBpp: c.int) --- // Dither image data to 16bpp or lower (Floyd-Steinberg dithering)
+ ImageFlipVertical :: proc(image: ^Image) --- // Flip image vertically
+ ImageFlipHorizontal :: proc(image: ^Image) --- // Flip image horizontally
+ ImageRotate :: proc(image: ^Image, degrees: c.int) --- // Rotate image by input angle in degrees (-359 to 359)
+ ImageRotateCW :: proc(image: ^Image) --- // Rotate image clockwise 90deg
+ ImageRotateCCW :: proc(image: ^Image) --- // Rotate image counter-clockwise 90deg
+ ImageColorTint :: proc(image: ^Image, color: Color) --- // Modify image color: tint
+ ImageColorInvert :: proc(image: ^Image) --- // Modify image color: invert
+ ImageColorGrayscale :: proc(image: ^Image) --- // Modify image color: grayscale
+ ImageColorContrast :: proc(image: ^Image, contrast: f32) --- // Modify image color: contrast (-100 to 100)
+ ImageColorBrightness :: proc(image: ^Image, brightness: c.int) --- // Modify image color: brightness (-255 to 255)
+ ImageColorReplace :: proc(image: ^Image, color: Color, replace: Color) --- // Modify image color: replace color
+ LoadImageColors :: proc(image: Image) -> ^Color --- // Load color data from image as a Color array (RGBA - 32bit)
+ LoadImagePalette :: proc(image: Image, maxPaletteSize: c.int, colorCount: ^c.int) -> ^Color --- // Load colors palette from image as a Color array (RGBA - 32bit)
+ UnloadImageColors :: proc(colors: ^Color) --- // Unload color data loaded with LoadImageColors()
+ UnloadImagePalette :: proc(colors: ^Color) --- // Unload colors palette loaded with LoadImagePalette()
+ GetImageAlphaBorder :: proc(image: Image, threshold: f32) -> Rectangle --- // Get image alpha border rectangle
+ GetImageColor :: proc(image: Image, x: c.int, y: c.int) -> Color --- // Get image pixel color at (x, y) position
+
+ // Image drawing functions
+ // NOTE: Image software-rendering functions (CPU)
+ ImageClearBackground :: proc(dst: ^Image, color: Color) --- // Clear image background with given color
+ ImageDrawPixel :: proc(dst: ^Image, posX: c.int, posY: c.int, color: Color) --- // Draw pixel within an image
+ ImageDrawPixelV :: proc(dst: ^Image, position: Vector2, color: Color) --- // Draw pixel within an image (Vector version)
+ ImageDrawLine :: proc(dst: ^Image, startPosX: c.int, startPosY: c.int, endPosX: c.int, endPosY: c.int, color: Color) --- // Draw line within an image
+ ImageDrawLineV :: proc(dst: ^Image, start: Vector2, end: Vector2, color: Color) --- // Draw line within an image (Vector version)
+ ImageDrawLineEx :: proc(dst: ^Image, start: Vector2, end: Vector2, thick: c.int, color: Color) --- // Draw a line defining thickness within an image
+ ImageDrawCircle :: proc(dst: ^Image, centerX: c.int, centerY: c.int, radius: c.int, color: Color) --- // Draw a filled circle within an image
+ ImageDrawCircleV :: proc(dst: ^Image, center: Vector2, radius: c.int, color: Color) --- // Draw a filled circle within an image (Vector version)
+ ImageDrawCircleLines :: proc(dst: ^Image, centerX: c.int, centerY: c.int, radius: c.int, color: Color) --- // Draw circle outline within an image
+ ImageDrawCircleLinesV :: proc(dst: ^Image, center: Vector2, radius: c.int, color: Color) --- // Draw circle outline within an image (Vector version)
+ ImageDrawRectangle :: proc(dst: ^Image, posX: c.int, posY: c.int, width: c.int, height: c.int, color: Color) --- // Draw rectangle within an image
+ ImageDrawRectangleV :: proc(dst: ^Image, position: Vector2, size: Vector2, color: Color) --- // Draw rectangle within an image (Vector version)
+ ImageDrawRectangleRec :: proc(dst: ^Image, rec: Rectangle, color: Color) --- // Draw rectangle within an image
+ ImageDrawRectangleLines :: proc(dst: ^Image, rec: Rectangle, thick: c.int, color: Color) --- // Draw rectangle lines within an image
+ ImageDrawTriangle :: proc(dst: ^Image, v1: Vector2, v2: Vector2, v3: Vector2, color: Color) --- // Draw triangle within an image
+ ImageDrawTriangleEx :: proc(dst: ^Image, v1: Vector2, v2: Vector2, v3: Vector2, c1: Color, c2: Color, c3: Color) --- // Draw triangle with interpolated colors within an image
+ ImageDrawTriangleLines :: proc(dst: ^Image, v1: Vector2, v2: Vector2, v3: Vector2, color: Color) --- // Draw triangle outline within an image
+ ImageDrawTriangleFan :: proc(dst: ^Image, points: ^Vector2, pointCount: c.int, color: Color) --- // Draw a triangle fan defined by points within an image (first vertex is the center)
+ ImageDrawTriangleStrip :: proc(dst: ^Image, points: ^Vector2, pointCount: c.int, color: Color) --- // Draw a triangle strip defined by points within an image
+ ImageDraw :: proc(dst: ^Image, src: Image, srcRec: Rectangle, dstRec: Rectangle, tint: Color) --- // Draw a source image within a destination image (tint applied to source)
+ ImageDrawText :: proc(dst: ^Image, text: cstring, posX: c.int, posY: c.int, fontSize: c.int, color: Color) --- // Draw text (using default font) within an image (destination)
+ ImageDrawTextEx :: proc(dst: ^Image, font: Font, text: cstring, position: Vector2, fontSize: f32, spacing: f32, tint: Color) --- // Draw text (custom sprite font) within an image (destination)
+
+ // Texture loading functions
+ // NOTE: These functions require GPU access
+ LoadTexture :: proc(fileName: cstring) -> Texture2D --- // Load texture from file into GPU memory (VRAM)
+ LoadTextureFromImage :: proc(image: Image) -> Texture2D --- // Load texture from image data
+ LoadTextureCubemap :: proc(image: Image, layout: c.int) -> TextureCubemap --- // Load cubemap from image, multiple image cubemap layouts supported
+ LoadRenderTexture :: proc(width: c.int, height: c.int) -> RenderTexture2D --- // Load texture for rendering (framebuffer)
+ IsTextureValid :: proc(texture: Texture2D) -> bool --- // Check if a texture is valid (loaded in GPU)
+ UnloadTexture :: proc(texture: Texture2D) --- // Unload texture from GPU memory (VRAM)
+ IsRenderTextureValid :: proc(target: RenderTexture2D) -> bool --- // Check if a render texture is valid (loaded in GPU)
+ UnloadRenderTexture :: proc(target: RenderTexture2D) --- // Unload render texture from GPU memory (VRAM)
+ UpdateTexture :: proc(texture: Texture2D, pixels: rawptr) --- // Update GPU texture with new data
+ UpdateTextureRec :: proc(texture: Texture2D, rec: Rectangle, pixels: rawptr) --- // Update GPU texture rectangle with new data
+
+ // Texture configuration functions
+ GenTextureMipmaps :: proc(texture: ^Texture2D) --- // Generate GPU mipmaps for a texture
+ SetTextureFilter :: proc(texture: Texture2D, filter: c.int) --- // Set texture scaling filter mode
+ SetTextureWrap :: proc(texture: Texture2D, wrap: c.int) --- // Set texture wrapping mode
+
+ // Texture drawing functions
+ DrawTexture :: proc(texture: Texture2D, posX: c.int, posY: c.int, tint: Color) --- // Draw a Texture2D
+ DrawTextureV :: proc(texture: Texture2D, position: Vector2, tint: Color) --- // Draw a Texture2D with position defined as Vector2
+ DrawTextureEx :: proc(texture: Texture2D, position: Vector2, rotation: f32, scale: f32, tint: Color) --- // Draw a Texture2D with extended parameters
+ DrawTextureRec :: proc(texture: Texture2D, source: Rectangle, position: Vector2, tint: Color) --- // Draw a part of a texture defined by a rectangle
+ DrawTexturePro :: proc(texture: Texture2D, source: Rectangle, dest: Rectangle, origin: Vector2, rotation: f32, tint: Color) --- // Draw a part of a texture defined by a rectangle with 'pro' parameters
+ DrawTextureNPatch :: proc(texture: Texture2D, nPatchInfo: NPatchInfo, dest: Rectangle, origin: Vector2, rotation: f32, tint: Color) --- // Draws a texture (or part of it) that stretches or shrinks nicely
+
+ // Color/pixel related functions
+ ColorIsEqual :: proc(col1: Color, col2: Color) -> bool --- // Check if two colors are equal
+ Fade :: proc(color: Color, alpha: f32) -> Color --- // Get color with alpha applied, alpha goes from 0.0f to 1.0f
+ ColorToInt :: proc(color: Color) -> c.int --- // Get hexadecimal value for a Color (0xRRGGBBAA)
+ ColorNormalize :: proc(color: Color) -> Vector4 --- // Get Color normalized as float [0..1]
+ ColorFromNormalized :: proc(normalized: Vector4) -> Color --- // Get Color from normalized values [0..1]
+ ColorToHSV :: proc(color: Color) -> Vector3 --- // Get HSV values for a Color, hue [0..360], saturation/value [0..1]
+ ColorFromHSV :: proc(hue: f32, saturation: f32, value: f32) -> Color --- // Get a Color from HSV values, hue [0..360], saturation/value [0..1]
+ ColorTint :: proc(color: Color, tint: Color) -> Color --- // Get color multiplied with another color
+ ColorBrightness :: proc(color: Color, factor: f32) -> Color --- // Get color with brightness correction, brightness factor goes from -1.0f to 1.0f
+ ColorContrast :: proc(color: Color, contrast: f32) -> Color --- // Get color with contrast correction, contrast values between -1.0f and 1.0f
+ ColorAlpha :: proc(color: Color, alpha: f32) -> Color --- // Get color with alpha applied, alpha goes from 0.0f to 1.0f
+ ColorAlphaBlend :: proc(dst: Color, src: Color, tint: Color) -> Color --- // Get src alpha-blended into dst color with tint
+ ColorLerp :: proc(color1: Color, color2: Color, factor: f32) -> Color --- // Get color lerp interpolation between two colors, factor [0.0f..1.0f]
+ GetColor :: proc(hexValue: c.uint) -> Color --- // Get Color structure from hexadecimal value
+ GetPixelColor :: proc(srcPtr: rawptr, format: c.int) -> Color --- // Get Color from a source pixel pointer of certain format
+ SetPixelColor :: proc(dstPtr: rawptr, color: Color, format: c.int) --- // Set color formatted into destination pixel pointer
+ GetPixelDataSize :: proc(width: c.int, height: c.int, format: c.int) -> c.int --- // Get pixel data size in bytes for certain format
+
+ // Font loading/unloading functions
+ GetFontDefault :: proc() -> Font --- // Get the default Font
+ LoadFont :: proc(fileName: cstring) -> Font --- // Load font from file into GPU memory (VRAM)
+ LoadFontEx :: proc(fileName: cstring, fontSize: c.int, codepoints: ^c.int, codepointCount: c.int) -> Font --- // Load font from file with extended parameters, use NULL for codepoints and 0 for codepointCount to load the default character set, font size is provided in pixels height
+ LoadFontFromImage :: proc(image: Image, key: Color, firstChar: c.int) -> Font --- // Load font from Image (XNA style)
+ LoadFontFromMemory :: proc(fileType: cstring, fileData: ^c.uchar, dataSize: c.int, fontSize: c.int, codepoints: ^c.int, codepointCount: c.int) -> Font --- // Load font from memory buffer, fileType refers to extension: i.e. '.ttf'
+ IsFontValid :: proc(font: Font) -> bool --- // Check if a font is valid (font data loaded, WARNING: GPU texture not checked)
+ LoadFontData :: proc(fileData: ^c.uchar, dataSize: c.int, fontSize: c.int, codepoints: ^c.int, codepointCount: c.int, type: c.int) -> ^GlyphInfo --- // Load font data for further use
+ GenImageFontAtlas :: proc(glyphs: ^GlyphInfo, glyphRecs: ^^Rectangle, glyphCount: c.int, fontSize: c.int, padding: c.int, packMethod: c.int) -> Image --- // Generate image font atlas using chars info
+ UnloadFontData :: proc(glyphs: ^GlyphInfo, glyphCount: c.int) --- // Unload font chars info data (RAM)
+ UnloadFont :: proc(font: Font) --- // Unload font from GPU memory (VRAM)
+ ExportFontAsCode :: proc(font: Font, fileName: cstring) -> bool --- // Export font as code file, returns true on success
+
+ // Text drawing functions
+ DrawFPS :: proc(posX: c.int, posY: c.int) --- // Draw current FPS
+ DrawText :: proc(text: cstring, posX: c.int, posY: c.int, fontSize: c.int, color: Color) --- // Draw text (using default font)
+ DrawTextEx :: proc(font: Font, text: cstring, position: Vector2, fontSize: f32, spacing: f32, tint: Color) --- // Draw text using font and additional parameters
+ DrawTextPro :: proc(font: Font, text: cstring, position: Vector2, origin: Vector2, rotation: f32, fontSize: f32, spacing: f32, tint: Color) --- // Draw text using Font and pro parameters (rotation)
+ DrawTextCodepoint :: proc(font: Font, codepoint: c.int, position: Vector2, fontSize: f32, tint: Color) --- // Draw one character (codepoint)
+ DrawTextCodepoints :: proc(font: Font, codepoints: ^c.int, codepointCount: c.int, position: Vector2, fontSize: f32, spacing: f32, tint: Color) --- // Draw multiple character (codepoint)
+
+ // Text font info functions
+ SetTextLineSpacing :: proc(spacing: c.int) --- // Set vertical line spacing when drawing with line-breaks
+ MeasureText :: proc(text: cstring, fontSize: c.int) -> c.int --- // Measure string width for default font
+ MeasureTextEx :: proc(font: Font, text: cstring, fontSize: f32, spacing: f32) -> Vector2 --- // Measure string size for Font
+ GetGlyphIndex :: proc(font: Font, codepoint: c.int) -> c.int --- // Get glyph index position in font for a codepoint (unicode character), fallback to '?' if not found
+ GetGlyphInfo :: proc(font: Font, codepoint: c.int) -> GlyphInfo --- // Get glyph font info data for a codepoint (unicode character), fallback to '?' if not found
+ GetGlyphAtlasRec :: proc(font: Font, codepoint: c.int) -> Rectangle --- // Get glyph rectangle in font atlas for a codepoint (unicode character), fallback to '?' if not found
+
+ // Text codepoints management functions (unicode characters)
+ LoadUTF8 :: proc(codepoints: ^c.int, length: c.int) -> cstring --- // Load UTF-8 text encoded from codepoints array
+ UnloadUTF8 :: proc(text: cstring) --- // Unload UTF-8 text encoded from codepoints array
+ LoadCodepoints :: proc(text: cstring, count: ^c.int) -> ^c.int --- // Load all codepoints from a UTF-8 text string, codepoints count returned by parameter
+ UnloadCodepoints :: proc(codepoints: ^c.int) --- // Unload codepoints data from memory
+ GetCodepointCount :: proc(text: cstring) -> c.int --- // Get total number of codepoints in a UTF-8 encoded string
+ GetCodepoint :: proc(text: cstring, codepointSize: ^c.int) -> c.int --- // Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure
+ GetCodepointNext :: proc(text: cstring, codepointSize: ^c.int) -> c.int --- // Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure
+ GetCodepointPrevious :: proc(text: cstring, codepointSize: ^c.int) -> c.int --- // Get previous codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure
+ CodepointToUTF8 :: proc(codepoint: c.int, utf8Size: ^c.int) -> cstring --- // Encode one codepoint into UTF-8 byte array (array length returned as parameter)
+
+ // Text strings management functions (no UTF-8 strings, only byte chars)
+ // WARNING 1: Most of these functions use internal static buffers, it's recommended to store returned data on user-side for re-use
+ // WARNING 2: Some strings allocate memory internally for the returned strings, those strings must be free by user using MemFree()
+ TextCopy :: proc(dst: cstring, src: cstring) -> c.int --- // Copy one string to another, returns bytes copied
+ TextIsEqual :: proc(text1: cstring, text2: cstring) -> bool --- // Check if two text string are equal
+ TextLength :: proc(text: cstring) -> c.uint --- // Get text length, checks for '\0' ending
+ TextFormat :: proc(text: cstring, #c_vararg _: ..any) -> cstring --- // Text formatting with variables (sprintf() style)
+ TextSubtext :: proc(text: cstring, position: c.int, length: c.int) -> cstring --- // Get a piece of a text string
+ TextReplace :: proc(text: cstring, replace: cstring, by: cstring) -> cstring --- // Replace text string (WARNING: memory must be freed!)
+ TextInsert :: proc(text: cstring, insert: cstring, position: c.int) -> cstring --- // Insert text in a position (WARNING: memory must be freed!)
+ TextJoin :: proc(textList: [^]cstring, count: c.int, delimiter: cstring) -> cstring --- // Join text strings with delimiter
+ TextSplit :: proc(text: cstring, delimiter: c.char, count: ^c.int) -> [^]cstring --- // Split text into multiple strings
+ TextAppend :: proc(text: cstring, append: cstring, position: ^c.int) --- // Append text at specific position and move cursor!
+ TextFindIndex :: proc(text: cstring, find: cstring) -> c.int --- // Find first text occurrence within a string
+ TextToUpper :: proc(text: cstring) -> cstring --- // Get upper case version of provided string
+ TextToLower :: proc(text: cstring) -> cstring --- // Get lower case version of provided string
+ TextToPascal :: proc(text: cstring) -> cstring --- // Get Pascal case notation version of provided string
+ TextToSnake :: proc(text: cstring) -> cstring --- // Get Snake case notation version of provided string
+ TextToCamel :: proc(text: cstring) -> cstring --- // Get Camel case notation version of provided string
+ TextToInteger :: proc(text: cstring) -> c.int --- // Get integer value from text
+ TextToFloat :: proc(text: cstring) -> f32 --- // Get float value from text
+
+ // Basic geometric 3D shapes drawing functions
+ DrawLine3D :: proc(startPos: Vector3, endPos: Vector3, color: Color) --- // Draw a line in 3D world space
+ DrawPoint3D :: proc(position: Vector3, color: Color) --- // Draw a point in 3D space, actually a small line
+ DrawCircle3D :: proc(center: Vector3, radius: f32, rotationAxis: Vector3, rotationAngle: f32, color: Color) --- // Draw a circle in 3D world space
+ DrawTriangle3D :: proc(v1: Vector3, v2: Vector3, v3: Vector3, color: Color) --- // Draw a color-filled triangle (vertex in counter-clockwise order!)
+ DrawTriangleStrip3D :: proc(points: ^Vector3, pointCount: c.int, color: Color) --- // Draw a triangle strip defined by points
+ DrawCube :: proc(position: Vector3, width: f32, height: f32, length: f32, color: Color) --- // Draw cube
+ DrawCubeV :: proc(position: Vector3, size: Vector3, color: Color) --- // Draw cube (Vector version)
+ DrawCubeWires :: proc(position: Vector3, width: f32, height: f32, length: f32, color: Color) --- // Draw cube wires
+ DrawCubeWiresV :: proc(position: Vector3, size: Vector3, color: Color) --- // Draw cube wires (Vector version)
+ DrawSphere :: proc(centerPos: Vector3, radius: f32, color: Color) --- // Draw sphere
+ DrawSphereEx :: proc(centerPos: Vector3, radius: f32, rings: c.int, slices: c.int, color: Color) --- // Draw sphere with extended parameters
+ DrawSphereWires :: proc(centerPos: Vector3, radius: f32, rings: c.int, slices: c.int, color: Color) --- // Draw sphere wires
+ DrawCylinder :: proc(position: Vector3, radiusTop: f32, radiusBottom: f32, height: f32, slices: c.int, color: Color) --- // Draw a cylinder/cone
+ DrawCylinderEx :: proc(startPos: Vector3, endPos: Vector3, startRadius: f32, endRadius: f32, sides: c.int, color: Color) --- // Draw a cylinder with base at startPos and top at endPos
+ DrawCylinderWires :: proc(position: Vector3, radiusTop: f32, radiusBottom: f32, height: f32, slices: c.int, color: Color) --- // Draw a cylinder/cone wires
+ DrawCylinderWiresEx :: proc(startPos: Vector3, endPos: Vector3, startRadius: f32, endRadius: f32, sides: c.int, color: Color) --- // Draw a cylinder wires with base at startPos and top at endPos
+ DrawCapsule :: proc(startPos: Vector3, endPos: Vector3, radius: f32, slices: c.int, rings: c.int, color: Color) --- // Draw a capsule with the center of its sphere caps at startPos and endPos
+ DrawCapsuleWires :: proc(startPos: Vector3, endPos: Vector3, radius: f32, slices: c.int, rings: c.int, color: Color) --- // Draw capsule wireframe with the center of its sphere caps at startPos and endPos
+ DrawPlane :: proc(centerPos: Vector3, size: Vector2, color: Color) --- // Draw a plane XZ
+ DrawRay :: proc(ray: Ray, color: Color) --- // Draw a ray line
+ DrawGrid :: proc(slices: c.int, spacing: f32) --- // Draw a grid (centered at (0, 0, 0))
+
+ // Model management functions
+ LoadModel :: proc(fileName: cstring) -> Model --- // Load model from files (meshes and materials)
+ LoadModelFromMesh :: proc(mesh: Mesh) -> Model --- // Load model from generated mesh (default material)
+ IsModelValid :: proc(model: Model) -> bool --- // Check if a model is valid (loaded in GPU, VAO/VBOs)
+ UnloadModel :: proc(model: Model) --- // Unload model (including meshes) from memory (RAM and/or VRAM)
+ GetModelBoundingBox :: proc(model: Model) -> BoundingBox --- // Compute model bounding box limits (considers all meshes)
+
+ // Model drawing functions
+ DrawModel :: proc(model: Model, position: Vector3, scale: f32, tint: Color) --- // Draw a model (with texture if set)
+ DrawModelEx :: proc(model: Model, position: Vector3, rotationAxis: Vector3, rotationAngle: f32, scale: Vector3, tint: Color) --- // Draw a model with extended parameters
+ DrawModelWires :: proc(model: Model, position: Vector3, scale: f32, tint: Color) --- // Draw a model wires (with texture if set)
+ DrawModelWiresEx :: proc(model: Model, position: Vector3, rotationAxis: Vector3, rotationAngle: f32, scale: Vector3, tint: Color) --- // Draw a model wires (with texture if set) with extended parameters
+ DrawModelPoints :: proc(model: Model, position: Vector3, scale: f32, tint: Color) --- // Draw a model as points
+ DrawModelPointsEx :: proc(model: Model, position: Vector3, rotationAxis: Vector3, rotationAngle: f32, scale: Vector3, tint: Color) --- // Draw a model as points with extended parameters
+ DrawBoundingBox :: proc(box: BoundingBox, color: Color) --- // Draw bounding box (wires)
+ DrawBillboard :: proc(camera: Camera, texture: Texture2D, position: Vector3, scale: f32, tint: Color) --- // Draw a billboard texture
+ DrawBillboardRec :: proc(camera: Camera, texture: Texture2D, source: Rectangle, position: Vector3, size: Vector2, tint: Color) --- // Draw a billboard texture defined by source
+ DrawBillboardPro :: proc(camera: Camera, texture: Texture2D, source: Rectangle, position: Vector3, up: Vector3, size: Vector2, origin: Vector2, rotation: f32, tint: Color) --- // Draw a billboard texture defined by source and rotation
+
+ // Mesh management functions
+ UploadMesh :: proc(mesh: ^Mesh, _dynamic: bool) --- // Upload mesh vertex data in GPU and provide VAO/VBO ids
+ UpdateMeshBuffer :: proc(mesh: Mesh, index: c.int, data: rawptr, dataSize: c.int, offset: c.int) --- // Update mesh vertex data in GPU for a specific buffer index
+ UnloadMesh :: proc(mesh: Mesh) --- // Unload mesh data from CPU and GPU
+ DrawMesh :: proc(mesh: Mesh, material: Material, transform: Matrix) --- // Draw a 3d mesh with material and transform
+ DrawMeshInstanced :: proc(mesh: Mesh, material: Material, transforms: ^Matrix, instances: c.int) --- // Draw multiple mesh instances with material and different transforms
+ GetMeshBoundingBox :: proc(mesh: Mesh) -> BoundingBox --- // Compute mesh bounding box limits
+ GenMeshTangents :: proc(mesh: ^Mesh) --- // Compute mesh tangents
+ ExportMesh :: proc(mesh: Mesh, fileName: cstring) -> bool --- // Export mesh data to file, returns true on success
+ ExportMeshAsCode :: proc(mesh: Mesh, fileName: cstring) -> bool --- // Export mesh as code file (.h) defining multiple arrays of vertex attributes
+
+ // Mesh generation functions
+ GenMeshPoly :: proc(sides: c.int, radius: f32) -> Mesh --- // Generate polygonal mesh
+ GenMeshPlane :: proc(width: f32, length: f32, resX: c.int, resZ: c.int) -> Mesh --- // Generate plane mesh (with subdivisions)
+ GenMeshCube :: proc(width: f32, height: f32, length: f32) -> Mesh --- // Generate cuboid mesh
+ GenMeshSphere :: proc(radius: f32, rings: c.int, slices: c.int) -> Mesh --- // Generate sphere mesh (standard sphere)
+ GenMeshHemiSphere :: proc(radius: f32, rings: c.int, slices: c.int) -> Mesh --- // Generate half-sphere mesh (no bottom cap)
+ GenMeshCylinder :: proc(radius: f32, height: f32, slices: c.int) -> Mesh --- // Generate cylinder mesh
+ GenMeshCone :: proc(radius: f32, height: f32, slices: c.int) -> Mesh --- // Generate cone/pyramid mesh
+ GenMeshTorus :: proc(radius: f32, size: f32, radSeg: c.int, sides: c.int) -> Mesh --- // Generate torus mesh
+ GenMeshKnot :: proc(radius: f32, size: f32, radSeg: c.int, sides: c.int) -> Mesh --- // Generate trefoil knot mesh
+ GenMeshHeightmap :: proc(heightmap: Image, size: Vector3) -> Mesh --- // Generate heightmap mesh from image data
+ GenMeshCubicmap :: proc(cubicmap: Image, cubeSize: Vector3) -> Mesh --- // Generate cubes-based map mesh from image data
+
+ // Material loading/unloading functions
+ LoadMaterials :: proc(fileName: cstring, materialCount: ^c.int) -> ^Material --- // Load materials from model file
+ LoadMaterialDefault :: proc() -> Material --- // Load default material (Supports: DIFFUSE, SPECULAR, NORMAL maps)
+ IsMaterialValid :: proc(material: Material) -> bool --- // Check if a material is valid (shader assigned, map textures loaded in GPU)
+ UnloadMaterial :: proc(material: Material) --- // Unload material from GPU memory (VRAM)
+ SetMaterialTexture :: proc(material: ^Material, mapType: c.int, texture: Texture2D) --- // Set texture for a material map type (MATERIAL_MAP_DIFFUSE, MATERIAL_MAP_SPECULAR...)
+ SetModelMeshMaterial :: proc(model: ^Model, meshId: c.int, materialId: c.int) --- // Set material for a mesh
+
+ // Model animations loading/unloading functions
+ LoadModelAnimations :: proc(fileName: cstring, animCount: ^c.int) -> ^ModelAnimation --- // Load model animations from file
+ UpdateModelAnimation :: proc(model: Model, anim: ModelAnimation, frame: c.int) --- // Update model animation pose (CPU)
+ UpdateModelAnimationBones :: proc(model: Model, anim: ModelAnimation, frame: c.int) --- // Update model animation mesh bone matrices (GPU skinning)
+ UnloadModelAnimation :: proc(anim: ModelAnimation) --- // Unload animation data
+ UnloadModelAnimations :: proc(animations: ^ModelAnimation, animCount: c.int) --- // Unload animation array data
+ IsModelAnimationValid :: proc(model: Model, anim: ModelAnimation) -> bool --- // Check model animation skeleton match
+
+ // Collision detection functions
+ CheckCollisionSpheres :: proc(center1: Vector3, radius1: f32, center2: Vector3, radius2: f32) -> bool --- // Check collision between two spheres
+ CheckCollisionBoxes :: proc(box1: BoundingBox, box2: BoundingBox) -> bool --- // Check collision between two bounding boxes
+ CheckCollisionBoxSphere :: proc(box: BoundingBox, center: Vector3, radius: f32) -> bool --- // Check collision between box and sphere
+ GetRayCollisionSphere :: proc(ray: Ray, center: Vector3, radius: f32) -> RayCollision --- // Get collision info between ray and sphere
+ GetRayCollisionBox :: proc(ray: Ray, box: BoundingBox) -> RayCollision --- // Get collision info between ray and box
+ GetRayCollisionMesh :: proc(ray: Ray, mesh: Mesh, transform: Matrix) -> RayCollision --- // Get collision info between ray and mesh
+ GetRayCollisionTriangle :: proc(ray: Ray, p1: Vector3, p2: Vector3, p3: Vector3) -> RayCollision --- // Get collision info between ray and triangle
+ GetRayCollisionQuad :: proc(ray: Ray, p1: Vector3, p2: Vector3, p3: Vector3, p4: Vector3) -> RayCollision --- // Get collision info between ray and quad
+
+ // Audio device management functions
+ InitAudioDevice :: proc() --- // Initialize audio device and context
+ CloseAudioDevice :: proc() --- // Close the audio device and context
+ IsAudioDeviceReady :: proc() -> bool --- // Check if audio device has been initialized successfully
+ SetMasterVolume :: proc(volume: f32) --- // Set master volume (listener)
+ GetMasterVolume :: proc() -> f32 --- // Get master volume (listener)
+
+ // Wave/Sound loading/unloading functions
+ LoadWave :: proc(fileName: cstring) -> Wave --- // Load wave data from file
+ LoadWaveFromMemory :: proc(fileType: cstring, fileData: ^c.uchar, dataSize: c.int) -> Wave --- // Load wave from memory buffer, fileType refers to extension: i.e. '.wav'
+ IsWaveValid :: proc(wave: Wave) -> bool --- // Checks if wave data is valid (data loaded and parameters)
+ LoadSound :: proc(fileName: cstring) -> Sound --- // Load sound from file
+ LoadSoundFromWave :: proc(wave: Wave) -> Sound --- // Load sound from wave data
+ LoadSoundAlias :: proc(source: Sound) -> Sound --- // Create a new sound that shares the same sample data as the source sound, does not own the sound data
+ IsSoundValid :: proc(sound: Sound) -> bool --- // Checks if a sound is valid (data loaded and buffers initialized)
+ UpdateSound :: proc(sound: Sound, data: rawptr, sampleCount: c.int) --- // Update sound buffer with new data
+ UnloadWave :: proc(wave: Wave) --- // Unload wave data
+ UnloadSound :: proc(sound: Sound) --- // Unload sound
+ UnloadSoundAlias :: proc(alias: Sound) --- // Unload a sound alias (does not deallocate sample data)
+ ExportWave :: proc(wave: Wave, fileName: cstring) -> bool --- // Export wave data to file, returns true on success
+ ExportWaveAsCode :: proc(wave: Wave, fileName: cstring) -> bool --- // Export wave sample data to code (.h), returns true on success
+
+ // Wave/Sound management functions
+ PlaySound :: proc(sound: Sound) --- // Play a sound
+ StopSound :: proc(sound: Sound) --- // Stop playing a sound
+ PauseSound :: proc(sound: Sound) --- // Pause a sound
+ ResumeSound :: proc(sound: Sound) --- // Resume a paused sound
+ IsSoundPlaying :: proc(sound: Sound) -> bool --- // Check if a sound is currently playing
+ SetSoundVolume :: proc(sound: Sound, volume: f32) --- // Set volume for a sound (1.0 is max level)
+ SetSoundPitch :: proc(sound: Sound, pitch: f32) --- // Set pitch for a sound (1.0 is base level)
+ SetSoundPan :: proc(sound: Sound, pan: f32) --- // Set pan for a sound (0.5 is center)
+ WaveCopy :: proc(wave: Wave) -> Wave --- // Copy a wave to a new wave
+ WaveCrop :: proc(wave: ^Wave, initFrame: c.int, finalFrame: c.int) --- // Crop a wave to defined frames range
+ WaveFormat :: proc(wave: ^Wave, sampleRate: c.int, sampleSize: c.int, channels: c.int) --- // Convert wave data to desired format
+ LoadWaveSamples :: proc(wave: Wave) -> ^f32 --- // Load samples data from wave as a 32bit float data array
+ UnloadWaveSamples :: proc(samples: ^f32) --- // Unload samples data loaded with LoadWaveSamples()
+
+ // Music management functions
+ LoadMusicStream :: proc(fileName: cstring) -> Music --- // Load music stream from file
+ LoadMusicStreamFromMemory :: proc(fileType: cstring, data: ^c.uchar, dataSize: c.int) -> Music --- // Load music stream from data
+ IsMusicValid :: proc(music: Music) -> bool --- // Checks if a music stream is valid (context and buffers initialized)
+ UnloadMusicStream :: proc(music: Music) --- // Unload music stream
+ PlayMusicStream :: proc(music: Music) --- // Start music playing
+ IsMusicStreamPlaying :: proc(music: Music) -> bool --- // Check if music is playing
+ UpdateMusicStream :: proc(music: Music) --- // Updates buffers for music streaming
+ StopMusicStream :: proc(music: Music) --- // Stop music playing
+ PauseMusicStream :: proc(music: Music) --- // Pause music playing
+ ResumeMusicStream :: proc(music: Music) --- // Resume playing paused music
+ SeekMusicStream :: proc(music: Music, position: f32) --- // Seek music to a position (in seconds)
+ SetMusicVolume :: proc(music: Music, volume: f32) --- // Set volume for music (1.0 is max level)
+ SetMusicPitch :: proc(music: Music, pitch: f32) --- // Set pitch for a music (1.0 is base level)
+ SetMusicPan :: proc(music: Music, pan: f32) --- // Set pan for a music (0.5 is center)
+ GetMusicTimeLength :: proc(music: Music) -> f32 --- // Get music time length (in seconds)
+ GetMusicTimePlayed :: proc(music: Music) -> f32 --- // Get current music time played (in seconds)
+
+ // AudioStream management functions
+ LoadAudioStream :: proc(sampleRate: c.uint, sampleSize: c.uint, channels: c.uint) -> AudioStream --- // Load audio stream (to stream raw audio pcm data)
+ IsAudioStreamValid :: proc(stream: AudioStream) -> bool --- // Checks if an audio stream is valid (buffers initialized)
+ UnloadAudioStream :: proc(stream: AudioStream) --- // Unload audio stream and free memory
+ UpdateAudioStream :: proc(stream: AudioStream, data: rawptr, frameCount: c.int) --- // Update audio stream buffers with data
+ IsAudioStreamProcessed :: proc(stream: AudioStream) -> bool --- // Check if any audio stream buffers requires refill
+ PlayAudioStream :: proc(stream: AudioStream) --- // Play audio stream
+ PauseAudioStream :: proc(stream: AudioStream) --- // Pause audio stream
+ ResumeAudioStream :: proc(stream: AudioStream) --- // Resume audio stream
+ IsAudioStreamPlaying :: proc(stream: AudioStream) -> bool --- // Check if audio stream is playing
+ StopAudioStream :: proc(stream: AudioStream) --- // Stop audio stream
+ SetAudioStreamVolume :: proc(stream: AudioStream, volume: f32) --- // Set volume for audio stream (1.0 is max level)
+ SetAudioStreamPitch :: proc(stream: AudioStream, pitch: f32) --- // Set pitch for audio stream (1.0 is base level)
+ SetAudioStreamPan :: proc(stream: AudioStream, pan: f32) --- // Set pan for audio stream (0.5 is centered)
+ SetAudioStreamBufferSizeDefault :: proc(size: c.int) --- // Default size for new audio streams
+ SetAudioStreamCallback :: proc(stream: AudioStream, callback: AudioCallback) --- // Audio thread callback to request new data
+ AttachAudioStreamProcessor :: proc(stream: AudioStream, processor: AudioCallback) --- // Attach audio stream processor to stream, receives the samples as 'float'
+ DetachAudioStreamProcessor :: proc(stream: AudioStream, processor: AudioCallback) --- // Detach audio stream processor from stream
+ AttachAudioMixedProcessor :: proc(processor: AudioCallback) --- // Attach audio stream processor to the entire audio pipeline, receives the samples as 'float'
+ DetachAudioMixedProcessor :: proc(processor: AudioCallback) --- // Detach audio stream processor from the entire audio pipeline
+}
diff --git a/odin-c-bindgen/examples/raylib/test/LICENSE b/odin-c-bindgen/examples/raylib/test/LICENSE
@@ -0,0 +1,7 @@
+Copyright (c) 2024 Karl Zylinski
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+\ No newline at end of file
diff --git a/odin-c-bindgen/examples/raylib/test/README.md b/odin-c-bindgen/examples/raylib/test/README.md
@@ -0,0 +1,3 @@
+This is the code for a video tutorial on how to make a Snake game using Odin and Raylib. Follow the video here: https://www.youtube.com/watch?v=lfiQNCNUifI
+
+
diff --git a/odin-c-bindgen/examples/raylib/test/body.png b/odin-c-bindgen/examples/raylib/test/body.png
Binary files differ.
diff --git a/odin-c-bindgen/examples/raylib/test/crash.wav b/odin-c-bindgen/examples/raylib/test/crash.wav
Binary files differ.
diff --git a/odin-c-bindgen/examples/raylib/test/eat.wav b/odin-c-bindgen/examples/raylib/test/eat.wav
Binary files differ.
diff --git a/odin-c-bindgen/examples/raylib/test/food.png b/odin-c-bindgen/examples/raylib/test/food.png
Binary files differ.
diff --git a/odin-c-bindgen/examples/raylib/test/head.png b/odin-c-bindgen/examples/raylib/test/head.png
Binary files differ.
diff --git a/odin-c-bindgen/examples/raylib/test/snake.odin b/odin-c-bindgen/examples/raylib/test/snake.odin
@@ -0,0 +1,195 @@
+package snake
+
+import rl "../raylib"
+import "core:math"
+import "core:fmt"
+
+WINDOW_SIZE :: 1000
+GRID_WIDTH :: 20
+CELL_SIZE :: 16
+CANVAS_SIZE :: GRID_WIDTH*CELL_SIZE
+TICK_RATE :: 0.13
+Vec2i :: [2]int
+MAX_SNAKE_LENGTH :: GRID_WIDTH*GRID_WIDTH
+
+snake: [MAX_SNAKE_LENGTH]Vec2i
+snake_length: int
+tick_timer: f32 = TICK_RATE
+move_direction: Vec2i
+game_over: bool
+food_pos: Vec2i
+
+place_food :: proc() {
+ occupied: [GRID_WIDTH][GRID_WIDTH]bool
+
+ for i in 0..<snake_length {
+ occupied[snake[i].x][snake[i].y] = true
+ }
+
+ free_cells := make([dynamic]Vec2i, context.temp_allocator)
+
+ for x in 0..<GRID_WIDTH {
+ for y in 0..<GRID_WIDTH {
+ if !occupied[x][y] {
+ append(&free_cells, Vec2i {x, y})
+ }
+ }
+ }
+
+ if len(free_cells) > 0 {
+ random_cell_index := rl.GetRandomValue(0, i32(len(free_cells) - 1))
+ food_pos = free_cells[random_cell_index]
+ }
+}
+
+restart :: proc() {
+ start_head_pos := Vec2i { GRID_WIDTH / 2, GRID_WIDTH / 2 }
+ snake[0] = start_head_pos
+ snake[1] = start_head_pos - {0, 1}
+ snake[2] = start_head_pos - {0, 2}
+ snake_length = 3
+ move_direction = {0, 1}
+ game_over = false
+ place_food()
+}
+
+main :: proc() {
+ rl.SetConfigFlags({.VSYNC_HINT})
+ rl.InitWindow(WINDOW_SIZE, WINDOW_SIZE, "Snake")
+ rl.InitAudioDevice()
+
+ restart()
+
+ food_sprite := rl.LoadTexture("food.png")
+ head_sprite := rl.LoadTexture("head.png")
+ body_sprite := rl.LoadTexture("body.png")
+ tail_sprite := rl.LoadTexture("tail.png")
+
+ eat_sound := rl.LoadSound("eat.wav")
+ crash_sound := rl.LoadSound("crash.wav")
+
+ for !rl.WindowShouldClose() {
+ if rl.IsKeyDown(.UP) {
+ move_direction = {0, -1}
+ }
+
+ if rl.IsKeyDown(.DOWN) {
+ move_direction = {0, 1}
+ }
+
+ if rl.IsKeyDown(.LEFT) {
+ move_direction = {-1, 0}
+ }
+
+ if rl.IsKeyDown(.RIGHT) {
+ move_direction = {1, 0}
+ }
+
+ if game_over {
+ if rl.IsKeyPressed(.ENTER) {
+ restart()
+ }
+ } else {
+ tick_timer -= rl.GetFrameTime()
+ }
+
+ if tick_timer <= 0 {
+ next_part_pos := snake[0]
+ snake[0] += move_direction
+ head_pos := snake[0]
+
+ if head_pos.x < 0 || head_pos.y < 0 || head_pos.x >= GRID_WIDTH || head_pos.y >= GRID_WIDTH {
+ game_over = true
+ rl.PlaySound(crash_sound)
+ }
+
+ for i in 1..<snake_length {
+ cur_pos := snake[i]
+
+ if cur_pos == head_pos {
+ game_over = true
+ rl.PlaySound(crash_sound)
+ }
+
+ snake[i] = next_part_pos
+ next_part_pos = cur_pos
+ }
+
+ if head_pos == food_pos {
+ snake_length += 1
+ snake[snake_length - 1] = next_part_pos
+ place_food()
+ rl.PlaySound(eat_sound)
+ }
+
+ tick_timer = TICK_RATE + tick_timer
+ }
+
+ rl.BeginDrawing()
+ rl.ClearBackground({76, 53, 83, 255})
+
+ camera := rl.Camera2D {
+ zoom = f32(WINDOW_SIZE) / CANVAS_SIZE
+ }
+
+ rl.BeginMode2D(camera)
+
+ rl.DrawTextureV(food_sprite, {f32(food_pos.x), f32(food_pos.y)}*CELL_SIZE, rl.WHITE)
+
+ for i in 0..<snake_length {
+ part_sprite := body_sprite
+ dir: Vec2i
+
+ if i == 0 {
+ part_sprite = head_sprite
+ dir = snake[i] - snake[i + 1]
+ } else if i == snake_length - 1 {
+ part_sprite = tail_sprite
+ dir = snake[i - 1] - snake[i]
+ } else {
+ dir = snake[i - 1] - snake[i]
+ }
+
+ rot := math.atan2(f32(dir.y), f32(dir.x)) * math.DEG_PER_RAD
+
+ source := rl.Rectangle {
+ 0, 0,
+ f32(part_sprite.width), f32(part_sprite.height),
+ }
+
+ dest := rl.Rectangle {
+ f32(snake[i].x)*CELL_SIZE + 0.5*CELL_SIZE,
+ f32(snake[i].y)*CELL_SIZE + 0.5*CELL_SIZE,
+ CELL_SIZE,
+ CELL_SIZE,
+ }
+
+ rl.DrawTexturePro(part_sprite, source, dest, {CELL_SIZE, CELL_SIZE}*0.5, rot, rl.WHITE)
+ }
+
+ if game_over {
+ rl.DrawText("Game Over!", 4, 4, 25, rl.RED)
+ rl.DrawText("Press Enter to play again", 4, 30, 15, rl.BLACK)
+ }
+
+ score := snake_length - 3
+ score_str := fmt.ctprintf("Score: %v", score)
+ rl.DrawText(score_str, 4, CANVAS_SIZE - 14, 10, rl.GRAY)
+
+ rl.EndMode2D()
+ rl.EndDrawing()
+
+ free_all(context.temp_allocator)
+ }
+
+ rl.UnloadTexture(head_sprite)
+ rl.UnloadTexture(food_sprite)
+ rl.UnloadTexture(body_sprite)
+ rl.UnloadTexture(tail_sprite)
+
+ rl.UnloadSound(eat_sound)
+ rl.UnloadSound(crash_sound)
+
+ rl.CloseAudioDevice()
+ rl.CloseWindow()
+}
+\ No newline at end of file
diff --git a/odin-c-bindgen/examples/raylib/test/tail.png b/odin-c-bindgen/examples/raylib/test/tail.png
Binary files differ.
diff --git a/odin-c-bindgen/examples/ufbx/.gitignore b/odin-c-bindgen/examples/ufbx/.gitignore
@@ -0,0 +1 @@
+ufbx/*.lib
diff --git a/odin-c-bindgen/examples/ufbx/README.md b/odin-c-bindgen/examples/ufbx/README.md
@@ -0,0 +1,3 @@
+There's a small test program in `test` that loads an FBX using these bindings and then displays it using Raylib.
+
+Note: The `.lib` file was compiled with `-DUFBX_REAL_IS_FLOAT=1`. The bindings are also generated with this config set. That makes the floats be of type `f32` instead of `f64`.
diff --git a/odin-c-bindgen/examples/ufbx/bindgen.sjson b/odin-c-bindgen/examples/ufbx/bindgen.sjson
@@ -0,0 +1,56 @@
+// See README.md in root of repository for documentation and more configuration options.
+
+inputs = [
+ "."
+]
+
+remove_type_prefix = "ufbx_"
+remove_function_prefix = "ufbx_"
+remove_macro_prefix = "UFBX_"
+import_lib = "ufbx.lib"
+package_name = "ufbx"
+output_folder = "ufbx"
+force_ada_case_types = true
+
+type_overrides = {
+ "ufbx_vec2" = "[2]Real"
+ "ufbx_vec3" = "[3]Real"
+ "ufbx_vec4" = "[4]Real"
+
+ // Assumes UFBX_REAL_IS_FLOAT to be set below, so that `Real :: f32`
+ "ufbx_quat" = "quaternion128"
+}
+
+clang_defines = {
+ "UFBX_REAL_IS_FLOAT" = "1"
+}
+
+struct_field_overrides = {
+ "ufbx_node_list.data" = "[^]"
+ "ufbx_face_list.data" = "[^]"
+ "ufbx_uint32_list.data" = "[^]"
+ "ufbx_vec2_list.data" = "[^]"
+ "ufbx_vec3_list.data" = "[^]"
+ "ufbx_vec4_list.data" = "[^]"
+ "ufbx_void_list.data" = "[^]rawptr"
+ "ufbx_bool_list.data" = "[^]"
+ "ufbx_real_list.data" = "[^]"
+ "ufbx_string_list.data" = "[^]"
+ "ufbx_dom_value_list.data" = "[^]"
+}
+
+procedure_type_overrides = {
+ "ufbx_generate_indices.streams" = "[^]"
+}
+
+rename = {
+ "ufbx_prop_flags" = "Prop_Flag"
+ "ufbx_transform_flags" = "Transform_Flag"
+ "ufbx_baked_key_flags" = "Baked_Key_Flag"
+}
+
+bit_setify = {
+ "ufbx_prop_flags" = "Prop_Flags"
+ "ufbx_transform_flags" = "Transform_Flags"
+ "ufbx_baked_key_flags" = "Baked_Key_Flags"
+}
diff --git a/odin-c-bindgen/examples/ufbx/test/box.fbx b/odin-c-bindgen/examples/ufbx/test/box.fbx
Binary files differ.
diff --git a/odin-c-bindgen/examples/ufbx/test/main.odin b/odin-c-bindgen/examples/ufbx/test/main.odin
@@ -0,0 +1,166 @@
+package ufbx_test
+
+import "../ufbx"
+import "core:fmt"
+import rl "vendor:raylib"
+import "core:math"
+
+main :: proc() {
+ rl.InitWindow(1280, 720, "ufbx test")
+ meshes := load_fbx_meshes("box.fbx")
+
+ camera := rl.Camera3D {
+ position = {2, 2, -5},
+ target = {0, 0, 0},
+ up = {0, 1, 0},
+ fovy = 70,
+ projection = .PERSPECTIVE,
+ }
+
+ default_material := rl.LoadMaterialDefault()
+
+ for !rl.WindowShouldClose() {
+ rl.BeginDrawing()
+ rl.ClearBackground(rl.SKYBLUE)
+ rl.BeginMode3D(camera)
+ t := f32(rl.GetTime())
+
+ for m in meshes {
+ pos := rl.MatrixTranslate(math.cos(t), math.sin(t*2+20), 0)
+ rot := rl.MatrixRotate({1, 2, 3}, t*3)
+ scl := rl.MatrixScale(1 + math.cos(t) * 0.5, 1 + math.sin(t*5+123) * 0.5, 1)
+ rl.DrawMesh(m, default_material, pos * rot * scl)
+ }
+
+ rl.EndMode3D()
+ rl.EndDrawing()
+ free_all(context.temp_allocator)
+ }
+
+ for m in meshes {
+ rl.UnloadMesh(m)
+ }
+
+ delete(meshes)
+ rl.CloseWindow()
+}
+
+/*
+Loads raylib meshes using ufbx.
+
+The returned array is allocated using `allocator`. The meshes themselves are
+allocated using raylib's allocator, destroy each using `rl.UnloadMesh(mesh)`.
+
+Does triangulation using `ufbx.triangulate_face`. Note that the triangulated
+faces are put into an `vertices` array. That's an intermediate array, used for
+de-duplicating vertices and also calculating indices using. That's all done
+using `ufbx.generate_indices`.
+*/
+load_fbx_meshes :: proc(filename: string, allocator := context.allocator, loc := #caller_location) -> []rl.Mesh {
+ opts: ufbx.Load_Opts
+ error: ufbx.Error
+ scene := ufbx.load_file(fmt.ctprint(filename), &opts, &error)
+
+ if scene == nil {
+ fmt.eprintf("Failed loading model %v, error: %v", filename, error.description.data)
+ return {}
+ }
+
+ res := make([dynamic]rl.Mesh, allocator, loc)
+
+ for i in 0..<scene.nodes.count {
+ node := scene.nodes.data[i]
+
+ if node.mesh == nil {
+ continue
+ }
+
+ m := node.mesh
+
+ Vertex :: struct {
+ pos: [3]f32,
+ normal: [3]f32,
+ texcoord: [2]f32,
+ color: [4]f32,
+ }
+
+ vertices := make([]Vertex, m.num_triangles * 3, context.temp_allocator)
+ num_vertices := 0
+ face_indices := make([]u32, m.max_face_triangles * 3, context.temp_allocator)
+
+ for fidx in 0..<m.faces.count {
+ f := m.faces.data[fidx]
+ num_face_triangles := ufbx.triangulate_face(raw_data(face_indices), len(face_indices), m, f)
+
+ for tidx in 0..<num_face_triangles*3 {
+ tris_idx := face_indices[tidx]
+
+ get_or_default :: proc(vertex: $T, idx: u32, default: $R) -> R {
+ if !vertex.exists {
+ return default
+ }
+
+ return vertex.values.data[vertex.indices.data[idx]]
+ }
+
+ vertices[num_vertices] = {
+ pos = m.vertex_position.values.data[m.vertex_position.indices.data[tris_idx]],
+ color = get_or_default(m.vertex_color, tris_idx, [4]f32 {1, 1, 1, 1}),
+ texcoord = get_or_default(m.vertex_uv, tris_idx, [2]f32 {0, 0}),
+ normal = get_or_default(m.vertex_normal, tris_idx, [3]f32 {0, 0, 0}),
+ }
+
+ num_vertices += 1
+ }
+ }
+
+ vertex_stream := ufbx.Vertex_Stream {
+ data = raw_data(vertices),
+ vertex_count = len(vertices),
+ vertex_size = size_of(Vertex),
+ }
+
+ num_indices := m.num_triangles * 3
+ indices := make([]u32, num_indices, context.temp_allocator)
+ num_vertices = int(ufbx.generate_indices(&vertex_stream, 1, raw_data(indices), num_indices, nil, nil))
+ vertices = vertices[:num_vertices]
+
+ rm := rl.Mesh {
+ triangleCount = i32(m.num_triangles),
+ vertexCount = i32(len(vertices)),
+ indices = ([^]u16)(rl.MemAlloc(u32(size_of(u16) * num_indices))),
+ vertices = ([^]f32)(rl.MemAlloc(u32(size_of(f32) * 3 * len(vertices)))),
+ colors = ([^]u8)(rl.MemAlloc(u32(size_of(u8) * 4 * len(vertices)))),
+ normals = ([^]f32)(rl.MemAlloc(u32(size_of(f32) * 3 * len(vertices)))),
+ texcoords = ([^]f32)(rl.MemAlloc(u32(size_of(f32) * 2 * len(vertices)))),
+ }
+
+ for i, iidx in indices {
+ rm.indices[iidx] = u16(i)
+ }
+
+ for v, vidx in vertices {
+ rm.vertices[vidx * 3 + 0] = v.pos.x
+ rm.vertices[vidx * 3 + 1] = v.pos.y
+ rm.vertices[vidx * 3 + 2] = v.pos.z
+
+ rm.normals[vidx * 3 + 0] = v.normal.x
+ rm.normals[vidx * 3 + 1] = v.normal.y
+ rm.normals[vidx * 3 + 2] = v.normal.z
+
+ rm.texcoords[vidx * 2 + 0] = v.texcoord.x
+ rm.texcoords[vidx * 2 + 1] = v.texcoord.y
+
+ rm.colors[vidx * 4 + 0] = u8(v.color.r*255)
+ rm.colors[vidx * 4 + 1] = u8(v.color.g*255)
+ rm.colors[vidx * 4 + 2] = u8(v.color.b*255)
+ rm.colors[vidx * 4 + 3] = u8(v.color.a*255)
+ }
+
+ rl.UploadMesh(&rm, false)
+ append(&res, rm)
+ }
+
+ ufbx.free_scene(scene)
+ return res[:]
+}
diff --git a/odin-c-bindgen/examples/ufbx/test/thing.fbx b/odin-c-bindgen/examples/ufbx/test/thing.fbx
Binary files differ.
diff --git a/odin-c-bindgen/examples/ufbx/ufbx.h b/odin-c-bindgen/examples/ufbx/ufbx.h
@@ -0,0 +1,6028 @@
+#ifndef UFBX_UFBX_H_INCLUDED
+#define UFBX_UFBX_H_INCLUDED
+
+// -- User configuration
+
+#if defined(UFBX_CONFIG_HEADER)
+ #include UFBX_CONFIG_HEADER
+#endif
+
+// -- Headers
+
+#if !defined(UFBX_NO_LIBC_TYPES)
+ #include <stdint.h>
+ #include <stddef.h>
+ #include <stdbool.h>
+#endif
+
+// -- Platform
+
+#ifndef UFBX_STDC
+ #if defined(__STDC_VERSION__)
+ #define UFBX_STDC __STDC_VERSION__
+ #else
+ #define UFBX_STDC 0
+ #endif
+#endif
+
+#ifndef UFBX_CPP
+ #if defined(__cplusplus)
+ #define UFBX_CPP __cplusplus
+ #else
+ #define UFBX_CPP 0
+ #endif
+#endif
+
+#ifndef UFBX_PLATFORM_MSC
+ #if !defined(UFBX_STANDARD_C) && defined(_MSC_VER)
+ #define UFBX_PLATFORM_MSC _MSC_VER
+ #else
+ #define UFBX_PLATFORM_MSC 0
+ #endif
+#endif
+
+#ifndef UFBX_PLATFORM_GNUC
+ #if !defined(UFBX_STANDARD_C) && defined(__GNUC__)
+ #define UFBX_PLATFORM_GNUC __GNUC__
+ #else
+ #define UFBX_PLATFORM_GNUC 0
+ #endif
+#endif
+
+#ifndef UFBX_CPP11
+ // MSVC does not advertise C++11 by default so we need special detection
+ #if UFBX_CPP >= 201103L || (UFBX_CPP > 0 && UFBX_PLATFORM_MSC >= 1900)
+ #define UFBX_CPP11 1
+ #else
+ #define UFBX_CPP11 0
+ #endif
+#endif
+
+#if defined(_MSC_VER)
+ #pragma warning(push)
+ #pragma warning(disable: 4061) // enumerator 'ENUM' in switch of enum 'enum' is not explicitly handled by a case label
+ #pragma warning(disable: 4201) // nonstandard extension used: nameless struct/union
+ #pragma warning(disable: 4505) // unreferenced local function has been removed
+ #pragma warning(disable: 4820) // type': 'N' bytes padding added after data member 'member'
+#elif defined(__clang__)
+ #pragma clang diagnostic push
+ #pragma clang diagnostic ignored "-Wpedantic"
+ #pragma clang diagnostic ignored "-Wpadded"
+ #if defined(__cplusplus)
+ #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
+ #pragma clang diagnostic ignored "-Wold-style-cast"
+ #endif
+#elif defined(__GNUC__)
+ #pragma GCC diagnostic push
+ #pragma GCC diagnostic ignored "-Wpedantic"
+ #pragma GCC diagnostic ignored "-Wpadded"
+ #if defined(__cplusplus)
+ #pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
+ #pragma GCC diagnostic ignored "-Wold-style-cast"
+ #else
+ #if __GNUC__ >= 5
+ #pragma GCC diagnostic ignored "-Wc90-c99-compat"
+ #pragma GCC diagnostic ignored "-Wc99-c11-compat"
+ #endif
+ #endif
+#endif
+
+#if UFBX_PLATFORM_MSC
+ #define ufbx_inline static __forceinline
+#elif UFBX_PLATFORM_GNUC
+ #define ufbx_inline static inline __attribute__((always_inline, unused))
+#else
+ #define ufbx_inline static
+#endif
+
+// Assertion function used in ufbx, defaults to C standard `assert()`.
+// You can define this to your custom preferred assert macro, but in that case
+// make sure that it is also used within `ufbx.c`.
+// Defining `UFBX_NO_ASSERT` to any value disables assertions.
+#ifndef ufbx_assert
+ #if defined(UFBX_NO_ASSERT) || defined(UFBX_NO_LIBC)
+ #define ufbx_assert(cond) (void)0
+ #else
+ #include <assert.h>
+ #define ufbx_assert(cond) assert(cond)
+ #endif
+#endif
+
+// Pointer may be `NULL`.
+#define ufbx_nullable
+
+// Changing this value from default or calling this function can lead into
+// breaking API guarantees.
+#define ufbx_unsafe
+
+// Linkage of the main ufbx API functions.
+// Defaults to nothing, or `static` if `UFBX_STATIC` is defined.
+// If you want to isolate ufbx to a single translation unit you can do the following:
+// #define UFBX_STATIC
+// #include "ufbx.h"
+// #include "ufbx.c"
+#ifndef ufbx_abi
+ #if defined(UFBX_STATIC)
+ #define ufbx_abi static
+ #else
+ #define ufbx_abi
+ #endif
+#endif
+
+// Linkage of the main ufbx data fields in the header.
+// Defaults to `extern`, or `static` if `UFBX_STATIC` is defined.
+#ifndef ufbx_abi_data
+ #if defined(UFBX_STATIC)
+ #define ufbx_abi_data static
+ #else
+ #define ufbx_abi_data extern
+ #endif
+#endif
+
+// Linkage of the main ufbx data fields in the source.
+// Defaults to nothing, or `static` if `UFBX_STATIC` is defined.
+#ifndef ufbx_abi_data_definition
+ #if defined(UFBX_STATIC)
+ #define ufbx_abi_data_def static
+ #else
+ #define ufbx_abi_data_def
+ #endif
+#endif
+
+// -- Configuration
+
+#ifndef UFBX_REAL_TYPE
+ #if defined(UFBX_REAL_IS_FLOAT)
+ #define UFBX_REAL_TYPE float
+ #else
+ #define UFBX_REAL_TYPE double
+ #endif
+#endif
+
+// Limits for embedded arrays within structures.
+#define UFBX_ERROR_STACK_MAX_DEPTH 8
+#define UFBX_PANIC_MESSAGE_LENGTH 128
+#define UFBX_ERROR_INFO_LENGTH 256
+
+// Number of thread groups to use if threading is enabled.
+// A thread group processes a number of tasks and is then waited and potentially
+// re-used later. In essence, this controls the granularity of threading.
+#define UFBX_THREAD_GROUP_COUNT 4
+
+// -- Language
+
+// bindgen-disable
+
+#if UFBX_CPP11
+
+template <typename T, typename U>
+struct ufbxi_type_is { };
+
+template <typename T>
+struct ufbxi_type_is<T, T> { using type = int; };
+
+template <typename T>
+struct ufbx_converter { };
+
+#define UFBX_CONVERSION_IMPL(p_name) \
+ template <typename T, typename S=typename ufbxi_type_is<T, decltype(ufbx_converter<T>::from(*(const p_name*)nullptr))>::type> \
+ operator T() const { return ufbx_converter<T>::from(*this); }
+
+#define UFBX_CONVERSION_TO_IMPL(p_name) \
+ template <typename T, typename S=typename ufbxi_type_is<p_name, decltype(ufbx_converter<T>::to(*(const T*)nullptr))>::type> \
+ p_name(const T &t) { *this = ufbx_converter<T>::to(t); }
+
+#define UFBX_CONVERSION_LIST_IMPL(p_name) \
+ template <typename T, typename S=typename ufbxi_type_is<T, decltype(ufbx_converter<T>::from_list((p_name*)nullptr, (size_t)0))>::type> \
+ operator T() const { return ufbx_converter<T>::from_list(data, count); }
+
+#else
+
+#define UFBX_CONVERSION_IMPL(p_name)
+#define UFBX_CONVERSION_TO_IMPL(p_name)
+#define UFBX_CONVERSION_LIST_IMPL(p_name)
+
+#endif
+
+#if defined(__cplusplus)
+ #define UFBX_LIST_TYPE(p_name, p_type) struct p_name { p_type *data; size_t count; \
+ p_type &operator[](size_t index) const { ufbx_assert(index < count); return data[index]; } \
+ p_type *begin() const { return data; } \
+ p_type *end() const { return data + count; } \
+ UFBX_CONVERSION_LIST_IMPL(p_type) \
+ }
+#else
+ #define UFBX_LIST_TYPE(p_name, p_type) typedef struct p_name { p_type *data; size_t count; } p_name
+#endif
+
+// This cannot be enabled automatically if supported as the source file may be
+// compiled with a different compiler using different settings than the header
+// consumers, in practice it should work but it causes issues such as #70.
+#if (UFBX_STDC >= 202311L || UFBX_CPP11) && defined(UFBX_USE_EXPLICIT_ENUM)
+ #define UFBX_ENUM_REPR : int
+ #define UFBX_ENUM_FORCE_WIDTH(p_prefix)
+ #define UFBX_FLAG_REPR : int
+ #define UFBX_FLAG_FORCE_WIDTH(p_prefix)
+ #define UFBX_HAS_FORCE_32BIT 0
+#else
+ #define UFBX_ENUM_REPR
+ #define UFBX_ENUM_FORCE_WIDTH(p_prefix) p_prefix##_FORCE_32BIT = 0x7fffffff
+ #define UFBX_FLAG_REPR
+ #define UFBX_FLAG_FORCE_WIDTH(p_prefix) p_prefix##_FORCE_32BIT = 0x7fffffff
+ #define UFBX_HAS_FORCE_32BIT 1
+#endif
+
+#define UFBX_ENUM_TYPE(p_name, p_prefix, p_last) \
+ enum { p_prefix##_COUNT = p_last + 1 }
+
+#if UFBX_CPP
+ #define UFBX_VERTEX_ATTRIB_IMPL(p_type) \
+ p_type &operator[](size_t index) const { ufbx_assert(index < indices.count); return values.data[indices.data[index]]; }
+#else
+ #define UFBX_VERTEX_ATTRIB_IMPL(p_type)
+#endif
+
+#if UFBX_CPP11
+ #define UFBX_CALLBACK_IMPL(p_name, p_fn, p_return, p_params, p_args) \
+ template <typename F> static p_return _cpp_adapter p_params { F &f = *static_cast<F*>(user); return f p_args; } \
+ p_name() = default; \
+ p_name(p_fn *f) : fn(f), user(nullptr) { } \
+ template <typename F> p_name(F *f) : fn(&_cpp_adapter<F>), user(static_cast<void*>(f)) { }
+#else
+ #define UFBX_CALLBACK_IMPL(p_name, p_fn, p_return, p_params, p_args)
+#endif
+
+// bindgen-enable
+
+// -- Version
+
+// Packing/unpacking for `UFBX_HEADER_VERSION` and `ufbx_source_version`.
+#define ufbx_pack_version(major, minor, patch) ((uint32_t)(major)*1000000u + (uint32_t)(minor)*1000u + (uint32_t)(patch))
+#define ufbx_version_major(version) ((uint32_t)(version)/1000000u%1000u)
+#define ufbx_version_minor(version) ((uint32_t)(version)/1000u%1000u)
+#define ufbx_version_patch(version) ((uint32_t)(version)%1000u)
+
+// Version of the ufbx header.
+// `UFBX_VERSION` is simply an alias of `UFBX_HEADER_VERSION`.
+// `ufbx_source_version` contains the version of the corresponding source file.
+// HINT: The version can be compared numerically to the result of `ufbx_pack_version()`,
+// for example `#if UFBX_VERSION >= ufbx_pack_version(0, 12, 0)`.
+#define UFBX_HEADER_VERSION ufbx_pack_version(0, 18, 0)
+#define UFBX_VERSION UFBX_HEADER_VERSION
+
+// -- Basic types
+
+// Main floating point type used everywhere in ufbx, defaults to `double`.
+// If you define `UFBX_REAL_IS_FLOAT` to any value, `ufbx_real` will be defined
+// as `float` instead.
+// You can also manually define `UFBX_REAL_TYPE` to any floating point type.
+typedef UFBX_REAL_TYPE ufbx_real;
+
+// Null-terminated UTF-8 encoded string within an FBX file
+typedef struct ufbx_string {
+ const char *data;
+ size_t length;
+
+ UFBX_CONVERSION_IMPL(ufbx_string)
+} ufbx_string;
+
+// Opaque byte buffer blob
+typedef struct ufbx_blob {
+ const void *data;
+ size_t size;
+
+ UFBX_CONVERSION_IMPL(ufbx_blob)
+} ufbx_blob;
+
+// 2D vector
+typedef struct ufbx_vec2 {
+ union {
+ struct { ufbx_real x, y; };
+ ufbx_real v[2];
+ };
+
+ UFBX_CONVERSION_IMPL(ufbx_vec2)
+} ufbx_vec2;
+
+// 3D vector
+typedef struct ufbx_vec3 {
+ union {
+ struct { ufbx_real x, y, z; };
+ ufbx_real v[3];
+ };
+
+ UFBX_CONVERSION_IMPL(ufbx_vec3)
+} ufbx_vec3;
+
+// 4D vector
+typedef struct ufbx_vec4 {
+ union {
+ struct { ufbx_real x, y, z, w; };
+ ufbx_real v[4];
+ };
+
+ UFBX_CONVERSION_IMPL(ufbx_vec4)
+} ufbx_vec4;
+
+// Quaternion
+typedef struct ufbx_quat {
+ union {
+ struct { ufbx_real x, y, z, w; };
+ ufbx_real v[4];
+ };
+
+ UFBX_CONVERSION_IMPL(ufbx_quat)
+} ufbx_quat;
+
+// Order in which Euler-angle rotation axes are applied for a transform
+// NOTE: The order in the name refers to the order of axes *applied*,
+// not the multiplication order: eg. `UFBX_ROTATION_ORDER_XYZ` is `Z*Y*X`
+// [TODO: Figure out what the spheric rotation order is...]
+typedef enum ufbx_rotation_order UFBX_ENUM_REPR {
+ UFBX_ROTATION_ORDER_XYZ,
+ UFBX_ROTATION_ORDER_XZY,
+ UFBX_ROTATION_ORDER_YZX,
+ UFBX_ROTATION_ORDER_YXZ,
+ UFBX_ROTATION_ORDER_ZXY,
+ UFBX_ROTATION_ORDER_ZYX,
+ UFBX_ROTATION_ORDER_SPHERIC,
+
+ UFBX_ENUM_FORCE_WIDTH(UFBX_ROTATION_ORDER)
+} ufbx_rotation_order;
+
+UFBX_ENUM_TYPE(ufbx_rotation_order, UFBX_ROTATION_ORDER, UFBX_ROTATION_ORDER_SPHERIC);
+
+// Explicit translation+rotation+scale transformation.
+// NOTE: Rotation is a quaternion, not Euler angles!
+typedef struct ufbx_transform {
+ ufbx_vec3 translation;
+ ufbx_quat rotation;
+ ufbx_vec3 scale;
+
+ UFBX_CONVERSION_IMPL(ufbx_transform)
+} ufbx_transform;
+
+// 4x3 matrix encoding an affine transformation.
+// `cols[0..2]` are the X/Y/Z basis vectors, `cols[3]` is the translation
+typedef struct ufbx_matrix {
+ union {
+ struct {
+ ufbx_real m00, m10, m20;
+ ufbx_real m01, m11, m21;
+ ufbx_real m02, m12, m22;
+ ufbx_real m03, m13, m23;
+ };
+ ufbx_vec3 cols[4];
+ ufbx_real v[12];
+ };
+
+ UFBX_CONVERSION_IMPL(ufbx_matrix)
+} ufbx_matrix;
+
+typedef struct ufbx_void_list {
+ void *data;
+ size_t count;
+} ufbx_void_list;
+
+UFBX_LIST_TYPE(ufbx_bool_list, bool);
+UFBX_LIST_TYPE(ufbx_uint32_list, uint32_t);
+UFBX_LIST_TYPE(ufbx_real_list, ufbx_real);
+UFBX_LIST_TYPE(ufbx_vec2_list, ufbx_vec2);
+UFBX_LIST_TYPE(ufbx_vec3_list, ufbx_vec3);
+UFBX_LIST_TYPE(ufbx_vec4_list, ufbx_vec4);
+UFBX_LIST_TYPE(ufbx_string_list, ufbx_string);
+
+// Sentinel value used to represent a missing index.
+#define UFBX_NO_INDEX ((uint32_t)~0u)
+
+// -- Document object model
+
+typedef enum ufbx_dom_value_type UFBX_ENUM_REPR {
+ UFBX_DOM_VALUE_NUMBER,
+ UFBX_DOM_VALUE_STRING,
+ UFBX_DOM_VALUE_ARRAY_I8,
+ UFBX_DOM_VALUE_ARRAY_I32,
+ UFBX_DOM_VALUE_ARRAY_I64,
+ UFBX_DOM_VALUE_ARRAY_F32,
+ UFBX_DOM_VALUE_ARRAY_F64,
+ UFBX_DOM_VALUE_ARRAY_RAW_STRING,
+ UFBX_DOM_VALUE_ARRAY_IGNORED,
+
+ UFBX_ENUM_FORCE_WIDTH(UFBX_DOM_VALUE_TYPE)
+} ufbx_dom_value_type;
+
+UFBX_ENUM_TYPE(ufbx_dom_value_type, UFBX_DOM_VALUE_TYPE, UFBX_DOM_VALUE_ARRAY_IGNORED);
+
+typedef struct ufbx_dom_node ufbx_dom_node;
+
+typedef struct ufbx_dom_value {
+ ufbx_dom_value_type type;
+ ufbx_string value_str;
+ ufbx_blob value_blob;
+ int64_t value_int;
+ double value_float;
+} ufbx_dom_value;
+
+UFBX_LIST_TYPE(ufbx_dom_node_list, ufbx_dom_node*);
+UFBX_LIST_TYPE(ufbx_dom_value_list, ufbx_dom_value);
+
+struct ufbx_dom_node {
+ ufbx_string name;
+ ufbx_dom_node_list children;
+ ufbx_dom_value_list values;
+};
+
+// -- Properties
+
+// FBX elements have properties which are arbitrary key/value pairs that can
+// have inherited default values or be animated. In most cases you don't need
+// to access these unless you need a feature not implemented directly in ufbx.
+// NOTE: Prefer using `ufbx_find_prop[_len](...)` to search for a property by
+// name as it can find it from the defaults if necessary.
+
+typedef struct ufbx_prop ufbx_prop;
+typedef struct ufbx_props ufbx_props;
+
+// Data type contained within the property. All the data fields are always
+// populated regardless of type, so there's no need to switch by type usually
+// eg. `prop->value_real` and `prop->value_int` have the same value (well, close)
+// if `prop->type == UFBX_PROP_INTEGER`. String values are not converted from/to.
+typedef enum ufbx_prop_type UFBX_ENUM_REPR {
+ UFBX_PROP_UNKNOWN,
+ UFBX_PROP_BOOLEAN,
+ UFBX_PROP_INTEGER,
+ UFBX_PROP_NUMBER,
+ UFBX_PROP_VECTOR,
+ UFBX_PROP_COLOR,
+ UFBX_PROP_COLOR_WITH_ALPHA,
+ UFBX_PROP_STRING,
+ UFBX_PROP_DATE_TIME,
+ UFBX_PROP_TRANSLATION,
+ UFBX_PROP_ROTATION,
+ UFBX_PROP_SCALING,
+ UFBX_PROP_DISTANCE,
+ UFBX_PROP_COMPOUND,
+ UFBX_PROP_BLOB,
+ UFBX_PROP_REFERENCE,
+
+ UFBX_ENUM_FORCE_WIDTH(UFBX_PROP_TYPE)
+} ufbx_prop_type;
+
+UFBX_ENUM_TYPE(ufbx_prop_type, UFBX_PROP_TYPE, UFBX_PROP_REFERENCE);
+
+// Property flags: Advanced information about properties, not usually needed.
+typedef enum ufbx_prop_flags UFBX_FLAG_REPR {
+ // Supports animation.
+ // NOTE: ufbx ignores this and allows animations on non-animatable properties.
+ UFBX_PROP_FLAG_ANIMATABLE = 0x1,
+
+ // User defined (custom) property.
+ UFBX_PROP_FLAG_USER_DEFINED = 0x2,
+
+ // Hidden in UI.
+ UFBX_PROP_FLAG_HIDDEN = 0x4,
+
+ // Disallow modification from UI for components.
+ UFBX_PROP_FLAG_LOCK_X = 0x10,
+ UFBX_PROP_FLAG_LOCK_Y = 0x20,
+ UFBX_PROP_FLAG_LOCK_Z = 0x40,
+ UFBX_PROP_FLAG_LOCK_W = 0x80,
+
+ // Disable animation from components.
+ UFBX_PROP_FLAG_MUTE_X = 0x100,
+ UFBX_PROP_FLAG_MUTE_Y = 0x200,
+ UFBX_PROP_FLAG_MUTE_Z = 0x400,
+ UFBX_PROP_FLAG_MUTE_W = 0x800,
+
+ // Property created by ufbx when an element has a connected `ufbx_anim_prop`
+ // but doesn't contain the `ufbx_prop` it's referring to.
+ // NOTE: The property may have been found in the templated defaults.
+ UFBX_PROP_FLAG_SYNTHETIC = 0x1000,
+
+ // The property has at least one `ufbx_anim_prop` in some layer.
+ UFBX_PROP_FLAG_ANIMATED = 0x2000,
+
+ // Used by `ufbx_evaluate_prop()` to indicate the the property was not found.
+ UFBX_PROP_FLAG_NOT_FOUND = 0x4000,
+
+ // The property is connected to another one.
+ // This use case is relatively rare so `ufbx_prop` does not track connections
+ // directly. You can find connections from `ufbx_element.connections_dst` where
+ // `ufbx_connection.dst_prop` is this property and `ufbx_connection.src_prop` is defined.
+ UFBX_PROP_FLAG_CONNECTED = 0x8000,
+
+ // The value of this property is undefined (represented as zero).
+ UFBX_PROP_FLAG_NO_VALUE = 0x10000,
+
+ // This property has been overridden by the user.
+ // See `ufbx_anim.prop_overrides` for more information.
+ UFBX_PROP_FLAG_OVERRIDDEN = 0x20000,
+
+ // Value type.
+ // `REAL/VEC2/VEC3/VEC4` are mutually exclusive but may coexist with eg. `STRING`
+ // in some rare cases where the string defines the unit for the vector.
+ UFBX_PROP_FLAG_VALUE_REAL = 0x100000,
+ UFBX_PROP_FLAG_VALUE_VEC2 = 0x200000,
+ UFBX_PROP_FLAG_VALUE_VEC3 = 0x400000,
+ UFBX_PROP_FLAG_VALUE_VEC4 = 0x800000,
+ UFBX_PROP_FLAG_VALUE_INT = 0x1000000,
+ UFBX_PROP_FLAG_VALUE_STR = 0x2000000,
+ UFBX_PROP_FLAG_VALUE_BLOB = 0x4000000,
+
+ UFBX_FLAG_FORCE_WIDTH(UFBX_PROP_FLAGS)
+} ufbx_prop_flags;
+
+// Single property with name/type/value.
+struct ufbx_prop {
+ ufbx_string name;
+
+ uint32_t _internal_key;
+
+ ufbx_prop_type type;
+ ufbx_prop_flags flags;
+
+ ufbx_string value_str;
+ ufbx_blob value_blob;
+ int64_t value_int;
+ union {
+ ufbx_real value_real_arr[4];
+ ufbx_real value_real;
+ ufbx_vec2 value_vec2;
+ ufbx_vec3 value_vec3;
+ ufbx_vec4 value_vec4;
+ };
+};
+
+UFBX_LIST_TYPE(ufbx_prop_list, ufbx_prop);
+
+// List of alphabetically sorted properties with potential defaults.
+// For animated objects in as scene from `ufbx_evaluate_scene()` this list
+// only has the animated properties, the originals are stored under `defaults`.
+struct ufbx_props {
+ ufbx_prop_list props;
+ size_t num_animated;
+
+ ufbx_nullable ufbx_props *defaults;
+};
+
+typedef struct ufbx_scene ufbx_scene;
+
+// -- Elements
+
+// Element is the lowest level representation of the FBX file in ufbx.
+// An element contains type, id, name, and properties (see `ufbx_props` above)
+// Elements may be connected to each other arbitrarily via `ufbx_connection`
+
+typedef struct ufbx_element ufbx_element;
+
+// Unknown
+typedef struct ufbx_unknown ufbx_unknown;
+
+// Nodes
+typedef struct ufbx_node ufbx_node;
+
+// Node attributes (common)
+typedef struct ufbx_mesh ufbx_mesh;
+typedef struct ufbx_light ufbx_light;
+typedef struct ufbx_camera ufbx_camera;
+typedef struct ufbx_bone ufbx_bone;
+typedef struct ufbx_empty ufbx_empty;
+
+// Node attributes (curves/surfaces)
+typedef struct ufbx_line_curve ufbx_line_curve;
+typedef struct ufbx_nurbs_curve ufbx_nurbs_curve;
+typedef struct ufbx_nurbs_surface ufbx_nurbs_surface;
+typedef struct ufbx_nurbs_trim_surface ufbx_nurbs_trim_surface;
+typedef struct ufbx_nurbs_trim_boundary ufbx_nurbs_trim_boundary;
+
+// Node attributes (advanced)
+typedef struct ufbx_procedural_geometry ufbx_procedural_geometry;
+typedef struct ufbx_stereo_camera ufbx_stereo_camera;
+typedef struct ufbx_camera_switcher ufbx_camera_switcher;
+typedef struct ufbx_marker ufbx_marker;
+typedef struct ufbx_lod_group ufbx_lod_group;
+
+// Deformers
+typedef struct ufbx_skin_deformer ufbx_skin_deformer;
+typedef struct ufbx_skin_cluster ufbx_skin_cluster;
+typedef struct ufbx_blend_deformer ufbx_blend_deformer;
+typedef struct ufbx_blend_channel ufbx_blend_channel;
+typedef struct ufbx_blend_shape ufbx_blend_shape;
+typedef struct ufbx_cache_deformer ufbx_cache_deformer;
+typedef struct ufbx_cache_file ufbx_cache_file;
+
+// Materials
+typedef struct ufbx_material ufbx_material;
+typedef struct ufbx_texture ufbx_texture;
+typedef struct ufbx_video ufbx_video;
+typedef struct ufbx_shader ufbx_shader;
+typedef struct ufbx_shader_binding ufbx_shader_binding;
+
+// Animation
+typedef struct ufbx_anim_stack ufbx_anim_stack;
+typedef struct ufbx_anim_layer ufbx_anim_layer;
+typedef struct ufbx_anim_value ufbx_anim_value;
+typedef struct ufbx_anim_curve ufbx_anim_curve;
+
+// Collections
+typedef struct ufbx_display_layer ufbx_display_layer;
+typedef struct ufbx_selection_set ufbx_selection_set;
+typedef struct ufbx_selection_node ufbx_selection_node;
+
+// Constraints
+typedef struct ufbx_character ufbx_character;
+typedef struct ufbx_constraint ufbx_constraint;
+
+// Audio
+typedef struct ufbx_audio_layer ufbx_audio_layer;
+typedef struct ufbx_audio_clip ufbx_audio_clip;
+
+// Miscellaneous
+typedef struct ufbx_pose ufbx_pose;
+typedef struct ufbx_metadata_object ufbx_metadata_object;
+
+UFBX_LIST_TYPE(ufbx_element_list, ufbx_element*);
+UFBX_LIST_TYPE(ufbx_unknown_list, ufbx_unknown*);
+UFBX_LIST_TYPE(ufbx_node_list, ufbx_node*);
+UFBX_LIST_TYPE(ufbx_mesh_list, ufbx_mesh*);
+UFBX_LIST_TYPE(ufbx_light_list, ufbx_light*);
+UFBX_LIST_TYPE(ufbx_camera_list, ufbx_camera*);
+UFBX_LIST_TYPE(ufbx_bone_list, ufbx_bone*);
+UFBX_LIST_TYPE(ufbx_empty_list, ufbx_empty*);
+UFBX_LIST_TYPE(ufbx_line_curve_list, ufbx_line_curve*);
+UFBX_LIST_TYPE(ufbx_nurbs_curve_list, ufbx_nurbs_curve*);
+UFBX_LIST_TYPE(ufbx_nurbs_surface_list, ufbx_nurbs_surface*);
+UFBX_LIST_TYPE(ufbx_nurbs_trim_surface_list, ufbx_nurbs_trim_surface*);
+UFBX_LIST_TYPE(ufbx_nurbs_trim_boundary_list, ufbx_nurbs_trim_boundary*);
+UFBX_LIST_TYPE(ufbx_procedural_geometry_list, ufbx_procedural_geometry*);
+UFBX_LIST_TYPE(ufbx_stereo_camera_list, ufbx_stereo_camera*);
+UFBX_LIST_TYPE(ufbx_camera_switcher_list, ufbx_camera_switcher*);
+UFBX_LIST_TYPE(ufbx_marker_list, ufbx_marker*);
+UFBX_LIST_TYPE(ufbx_lod_group_list, ufbx_lod_group*);
+UFBX_LIST_TYPE(ufbx_skin_deformer_list, ufbx_skin_deformer*);
+UFBX_LIST_TYPE(ufbx_skin_cluster_list, ufbx_skin_cluster*);
+UFBX_LIST_TYPE(ufbx_blend_deformer_list, ufbx_blend_deformer*);
+UFBX_LIST_TYPE(ufbx_blend_channel_list, ufbx_blend_channel*);
+UFBX_LIST_TYPE(ufbx_blend_shape_list, ufbx_blend_shape*);
+UFBX_LIST_TYPE(ufbx_cache_deformer_list, ufbx_cache_deformer*);
+UFBX_LIST_TYPE(ufbx_cache_file_list, ufbx_cache_file*);
+UFBX_LIST_TYPE(ufbx_material_list, ufbx_material*);
+UFBX_LIST_TYPE(ufbx_texture_list, ufbx_texture*);
+UFBX_LIST_TYPE(ufbx_video_list, ufbx_video*);
+UFBX_LIST_TYPE(ufbx_shader_list, ufbx_shader*);
+UFBX_LIST_TYPE(ufbx_shader_binding_list, ufbx_shader_binding*);
+UFBX_LIST_TYPE(ufbx_anim_stack_list, ufbx_anim_stack*);
+UFBX_LIST_TYPE(ufbx_anim_layer_list, ufbx_anim_layer*);
+UFBX_LIST_TYPE(ufbx_anim_value_list, ufbx_anim_value*);
+UFBX_LIST_TYPE(ufbx_anim_curve_list, ufbx_anim_curve*);
+UFBX_LIST_TYPE(ufbx_display_layer_list, ufbx_display_layer*);
+UFBX_LIST_TYPE(ufbx_selection_set_list, ufbx_selection_set*);
+UFBX_LIST_TYPE(ufbx_selection_node_list, ufbx_selection_node*);
+UFBX_LIST_TYPE(ufbx_character_list, ufbx_character*);
+UFBX_LIST_TYPE(ufbx_constraint_list, ufbx_constraint*);
+UFBX_LIST_TYPE(ufbx_audio_layer_list, ufbx_audio_layer*);
+UFBX_LIST_TYPE(ufbx_audio_clip_list, ufbx_audio_clip*);
+UFBX_LIST_TYPE(ufbx_pose_list, ufbx_pose*);
+UFBX_LIST_TYPE(ufbx_metadata_object_list, ufbx_metadata_object*);
+
+typedef enum ufbx_element_type UFBX_ENUM_REPR {
+ UFBX_ELEMENT_UNKNOWN, // < `ufbx_unknown`
+ UFBX_ELEMENT_NODE, // < `ufbx_node`
+ UFBX_ELEMENT_MESH, // < `ufbx_mesh`
+ UFBX_ELEMENT_LIGHT, // < `ufbx_light`
+ UFBX_ELEMENT_CAMERA, // < `ufbx_camera`
+ UFBX_ELEMENT_BONE, // < `ufbx_bone`
+ UFBX_ELEMENT_EMPTY, // < `ufbx_empty`
+ UFBX_ELEMENT_LINE_CURVE, // < `ufbx_line_curve`
+ UFBX_ELEMENT_NURBS_CURVE, // < `ufbx_nurbs_curve`
+ UFBX_ELEMENT_NURBS_SURFACE, // < `ufbx_nurbs_surface`
+ UFBX_ELEMENT_NURBS_TRIM_SURFACE, // < `ufbx_nurbs_trim_surface`
+ UFBX_ELEMENT_NURBS_TRIM_BOUNDARY, // < `ufbx_nurbs_trim_boundary`
+ UFBX_ELEMENT_PROCEDURAL_GEOMETRY, // < `ufbx_procedural_geometry`
+ UFBX_ELEMENT_STEREO_CAMERA, // < `ufbx_stereo_camera`
+ UFBX_ELEMENT_CAMERA_SWITCHER, // < `ufbx_camera_switcher`
+ UFBX_ELEMENT_MARKER, // < `ufbx_marker`
+ UFBX_ELEMENT_LOD_GROUP, // < `ufbx_lod_group`
+ UFBX_ELEMENT_SKIN_DEFORMER, // < `ufbx_skin_deformer`
+ UFBX_ELEMENT_SKIN_CLUSTER, // < `ufbx_skin_cluster`
+ UFBX_ELEMENT_BLEND_DEFORMER, // < `ufbx_blend_deformer`
+ UFBX_ELEMENT_BLEND_CHANNEL, // < `ufbx_blend_channel`
+ UFBX_ELEMENT_BLEND_SHAPE, // < `ufbx_blend_shape`
+ UFBX_ELEMENT_CACHE_DEFORMER, // < `ufbx_cache_deformer`
+ UFBX_ELEMENT_CACHE_FILE, // < `ufbx_cache_file`
+ UFBX_ELEMENT_MATERIAL, // < `ufbx_material`
+ UFBX_ELEMENT_TEXTURE, // < `ufbx_texture`
+ UFBX_ELEMENT_VIDEO, // < `ufbx_video`
+ UFBX_ELEMENT_SHADER, // < `ufbx_shader`
+ UFBX_ELEMENT_SHADER_BINDING, // < `ufbx_shader_binding`
+ UFBX_ELEMENT_ANIM_STACK, // < `ufbx_anim_stack`
+ UFBX_ELEMENT_ANIM_LAYER, // < `ufbx_anim_layer`
+ UFBX_ELEMENT_ANIM_VALUE, // < `ufbx_anim_value`
+ UFBX_ELEMENT_ANIM_CURVE, // < `ufbx_anim_curve`
+ UFBX_ELEMENT_DISPLAY_LAYER, // < `ufbx_display_layer`
+ UFBX_ELEMENT_SELECTION_SET, // < `ufbx_selection_set`
+ UFBX_ELEMENT_SELECTION_NODE, // < `ufbx_selection_node`
+ UFBX_ELEMENT_CHARACTER, // < `ufbx_character`
+ UFBX_ELEMENT_CONSTRAINT, // < `ufbx_constraint`
+ UFBX_ELEMENT_AUDIO_LAYER, // < `ufbx_audio_layer`
+ UFBX_ELEMENT_AUDIO_CLIP, // < `ufbx_audio_clip`
+ UFBX_ELEMENT_POSE, // < `ufbx_pose`
+ UFBX_ELEMENT_METADATA_OBJECT, // < `ufbx_metadata_object`
+
+ UFBX_ELEMENT_TYPE_FIRST_ATTRIB = UFBX_ELEMENT_MESH,
+ UFBX_ELEMENT_TYPE_LAST_ATTRIB = UFBX_ELEMENT_LOD_GROUP,
+
+ UFBX_ENUM_FORCE_WIDTH(UFBX_ELEMENT_TYPE)
+} ufbx_element_type;
+
+UFBX_ENUM_TYPE(ufbx_element_type, UFBX_ELEMENT_TYPE, UFBX_ELEMENT_METADATA_OBJECT);
+
+// Connection between two elements.
+// Source and destination are somewhat arbitrary but the destination is
+// often the "container" like a parent node or mesh containing a deformer.
+typedef struct ufbx_connection {
+ ufbx_element *src;
+ ufbx_element *dst;
+ ufbx_string src_prop;
+ ufbx_string dst_prop;
+} ufbx_connection;
+
+UFBX_LIST_TYPE(ufbx_connection_list, ufbx_connection);
+
+// Element "base-class" common to each element.
+// Some fields (like `connections_src`) are advanced and not visible
+// in the specialized element structs.
+// NOTE: The `element_id` value is consistent when loading the
+// _same_ file, but re-exporting the file will invalidate them.
+struct ufbx_element {
+ ufbx_string name;
+ ufbx_props props;
+ uint32_t element_id;
+ uint32_t typed_id;
+ ufbx_node_list instances;
+ ufbx_element_type type;
+ ufbx_connection_list connections_src;
+ ufbx_connection_list connections_dst;
+ ufbx_nullable ufbx_dom_node *dom_node;
+ ufbx_scene *scene;
+};
+
+// -- Unknown
+
+struct ufbx_unknown {
+ // Shared "base-class" header, see `ufbx_element`.
+ union { ufbx_element element; struct {
+ ufbx_string name;
+ ufbx_props props;
+ uint32_t element_id;
+ uint32_t typed_id;
+ }; };
+
+ // FBX format specific type information.
+ // In ASCII FBX format:
+ // super_type: ID, "type::name", "sub_type" { ... }
+ ufbx_string type;
+ ufbx_string super_type;
+ ufbx_string sub_type;
+};
+
+// -- Nodes
+
+// Inherit type specifies how hierarchial node transforms are combined.
+// This only affects the final scaling, as rotation and translation are always
+// inherited correctly.
+// NOTE: These don't map to `"InheritType"` property as there may be new ones for
+// compatibility with various exporters.
+typedef enum ufbx_inherit_mode UFBX_ENUM_REPR {
+
+ // Normal matrix composition of hierarchy: `R*S*r*s`.
+ // child.node_to_world = parent.node_to_world * child.node_to_parent;
+ UFBX_INHERIT_MODE_NORMAL,
+
+ // Ignore parent scale when computing the transform: `R*r*s`.
+ // ufbx_transform t = node.local_transform;
+ // t.translation *= parent.inherit_scale;
+ // t.scale *= node.inherit_scale_node.inherit_scale;
+ // child.node_to_world = parent.unscaled_node_to_world * t;
+ // Also known as "Segment scale compensate" in some software.
+ UFBX_INHERIT_MODE_IGNORE_PARENT_SCALE,
+
+ // Apply parent scale component-wise: `R*r*S*s`.
+ // ufbx_transform t = node.local_transform;
+ // t.translation *= parent.inherit_scale;
+ // t.scale *= node.inherit_scale_node.inherit_scale;
+ // child.node_to_world = parent.unscaled_node_to_world * t;
+ UFBX_INHERIT_MODE_COMPONENTWISE_SCALE,
+
+ UFBX_ENUM_FORCE_WIDTH(UFBX_INHERIT_MODE)
+} ufbx_inherit_mode;
+
+UFBX_ENUM_TYPE(ufbx_inherit_mode, UFBX_INHERIT_MODE, UFBX_INHERIT_MODE_COMPONENTWISE_SCALE);
+
+// Axis used to mirror transformations for handedness conversion.
+typedef enum ufbx_mirror_axis UFBX_ENUM_REPR {
+
+ UFBX_MIRROR_AXIS_NONE,
+ UFBX_MIRROR_AXIS_X,
+ UFBX_MIRROR_AXIS_Y,
+ UFBX_MIRROR_AXIS_Z,
+
+ UFBX_ENUM_FORCE_WIDTH(UFBX_MIRROR_AXIS)
+} ufbx_mirror_axis;
+
+UFBX_ENUM_TYPE(ufbx_mirror_axis, UFBX_MIRROR_AXIS, UFBX_MIRROR_AXIS_Z);
+
+// Nodes form the scene transformation hierarchy and can contain attached
+// elements such as meshes or lights. In normal cases a single `ufbx_node`
+// contains only a single attached element, so using `type/mesh/...` is safe.
+struct ufbx_node {
+ union { ufbx_element element; struct {
+ ufbx_string name;
+ ufbx_props props;
+ uint32_t element_id;
+ uint32_t typed_id;
+ }; };
+
+ // Node hierarchy
+
+ // Parent node containing this one if not root.
+ //
+ // Always non-`NULL` for non-root nodes unless
+ // `ufbx_load_opts.allow_nodes_out_of_root` is enabled.
+ ufbx_nullable ufbx_node *parent;
+
+ // List of child nodes parented to this node.
+ ufbx_node_list children;
+
+ // Common attached element type and typed pointers. Set to `NULL` if not in
+ // use, so checking `attrib_type` is not required.
+ //
+ // HINT: If you need less common attributes access `ufbx_node.attrib`, you
+ // can use utility functions like `ufbx_as_nurbs_curve(attrib)` to convert
+ // and check the attribute in one step.
+ ufbx_nullable ufbx_mesh *mesh;
+ ufbx_nullable ufbx_light *light;
+ ufbx_nullable ufbx_camera *camera;
+ ufbx_nullable ufbx_bone *bone;
+
+ // Less common attributes use these fields.
+ //
+ // Defined even if it is one of the above, eg. `ufbx_mesh`. In case there
+ // is multiple attributes this will be the first one.
+ ufbx_nullable ufbx_element *attrib;
+
+ // Geometry transform helper if one exists.
+ // See `UFBX_GEOMETRY_TRANSFORM_HANDLING_HELPER_NODES`.
+ ufbx_nullable ufbx_node *geometry_transform_helper;
+
+ // Scale helper if one exists.
+ // See `UFBX_INHERIT_MODE_HANDLING_HELPER_NODES`.
+ ufbx_nullable ufbx_node *scale_helper;
+
+ // `attrib->type` if `attrib` is defined, otherwise `UFBX_ELEMENT_UNKNOWN`.
+ ufbx_element_type attrib_type;
+
+ // List of _all_ attached attribute elements.
+ //
+ // In most cases there is only zero or one attributes per node, but if you
+ // have a very exotic FBX file nodes may have multiple attributes.
+ ufbx_element_list all_attribs;
+
+ // Local transform in parent, geometry transform is a non-inherited
+ // transform applied only to attachments like meshes
+ ufbx_inherit_mode inherit_mode;
+ ufbx_inherit_mode original_inherit_mode;
+ ufbx_transform local_transform;
+ ufbx_transform geometry_transform;
+
+ // Combined scale when using `UFBX_INHERIT_MODE_COMPONENTWISE_SCALE`.
+ // Contains `local_transform.scale` otherwise.
+ ufbx_vec3 inherit_scale;
+
+ // Node where scale is inherited from for `UFBX_INHERIT_MODE_COMPONENTWISE_SCALE`
+ // and even for `UFBX_INHERIT_MODE_IGNORE_PARENT_SCALE`.
+ // For componentwise-scale nodes, this will point to `parent`, for scale ignoring
+ // nodes this will point to the parent of the nearest componentwise-scaled node
+ // in the parent chain.
+ ufbx_nullable ufbx_node *inherit_scale_node;
+
+ // Raw Euler angles in degrees for those who want them
+
+ // Specifies the axis order `euler_rotation` is applied in.
+ ufbx_rotation_order rotation_order;
+ // Rotation around the local X/Y/Z axes in `rotation_order`.
+ // The angles are specified in degrees.
+ ufbx_vec3 euler_rotation;
+
+ // Matrices derived from the transformations, for transforming geometry
+ // prefer using `geometry_to_world` as that supports geometric transforms.
+
+ // Transform from this node to `parent` space.
+ // Equivalent to `ufbx_transform_to_matrix(&local_transform)`.
+ ufbx_matrix node_to_parent;
+ // Transform from this node to the world space, ie. multiplying all the
+ // `node_to_parent` matrices of the parent chain together.
+ ufbx_matrix node_to_world;
+ // Transform from the attribute to this node. Does not affect the transforms
+ // of `children`!
+ // Equivalent to `ufbx_transform_to_matrix(&geometry_transform)`.
+ ufbx_matrix geometry_to_node;
+ // Transform from attribute space to world space.
+ // Equivalent to `ufbx_matrix_mul(&node_to_world, &geometry_to_node)`.
+ ufbx_matrix geometry_to_world;
+ // Transform from this node to world space, ignoring self scaling.
+ ufbx_matrix unscaled_node_to_world;
+
+ // ufbx-specific adjustment for switching between coodrinate/unit systems.
+ // HINT: In most cases you don't need to deal with these as these are baked
+ // into all the transforms above and into `ufbx_evaluate_transform()`.
+ ufbx_vec3 adjust_pre_translation; // < Translation applied between parent and self
+ ufbx_quat adjust_pre_rotation; // < Rotation applied between parent and self
+ ufbx_real adjust_pre_scale; // < Scaling applied between parent and self
+ ufbx_quat adjust_post_rotation; // < Rotation applied in local space at the end
+ ufbx_real adjust_post_scale; // < Scaling applied in local space at the end
+ ufbx_real adjust_translation_scale; // < Scaling applied to translation only
+ ufbx_mirror_axis adjust_mirror_axis; // < Mirror translation and rotation on this axis
+
+ // Materials used by `mesh` or other `attrib`.
+ // There may be multiple copies of a single `ufbx_mesh` with different materials
+ // in the `ufbx_node` instances.
+ ufbx_material_list materials;
+
+ // Bind pose
+ ufbx_nullable ufbx_pose *bind_pose;
+
+ // Visibility state.
+ bool visible;
+
+ // True if this node is the implicit root node of the scene.
+ bool is_root;
+
+ // True if the node has a non-identity `geometry_transform`.
+ bool has_geometry_transform;
+
+ // If `true` the transform is adjusted by ufbx, not enabled by default.
+ // See `adjust_pre_rotation`, `adjust_pre_scale`, `adjust_post_rotation`,
+ // and `adjust_post_scale`.
+ bool has_adjust_transform;
+
+ // Scale is adjusted by root scale.
+ bool has_root_adjust_transform;
+
+ // True if this node is a synthetic geometry transform helper.
+ // See `UFBX_GEOMETRY_TRANSFORM_HANDLING_HELPER_NODES`.
+ bool is_geometry_transform_helper;
+
+ // True if the node is a synthetic scale compensation helper.
+ // See `UFBX_INHERIT_MODE_HANDLING_HELPER_NODES`.
+ bool is_scale_helper;
+
+ // Parent node to children that can compensate for parent scale.
+ bool is_scale_compensate_parent;
+
+ // How deep is this node in the parent hierarchy. Root node is at depth `0`
+ // and the immediate children of root at `1`.
+ uint32_t node_depth;
+};
+
+// Vertex attribute: All attributes are stored in a consistent indexed format
+// regardless of how it's actually stored in the file.
+//
+// `values` is a contiguous array of attribute values.
+// `indices` maps each mesh index into a value in the `values` array.
+//
+// If `unique_per_vertex` is set then the attribute is guaranteed to have a
+// single defined value per vertex accessible via:
+// attrib.values.data[attrib.indices.data[mesh->vertex_first_index[vertex_ix]]
+typedef struct ufbx_vertex_attrib {
+ // Is this attribute defined by the mesh.
+ bool exists;
+ // List of values the attribute uses.
+ ufbx_void_list values;
+ // Indices into `values[]`, indexed up to `ufbx_mesh.num_indices`.
+ ufbx_uint32_list indices;
+ // Number of `ufbx_real` entries per value.
+ size_t value_reals;
+ // `true` if this attribute is defined per vertex, instead of per index.
+ bool unique_per_vertex;
+ // Optional 4th 'W' component for the attribute.
+ // May be defined for the following:
+ // ufbx_mesh.vertex_normal
+ // ufbx_mesh.vertex_tangent / ufbx_uv_set.vertex_tangent
+ // ufbx_mesh.vertex_bitangent / ufbx_uv_set.vertex_bitangent
+ // NOTE: This is not loaded by default, set `ufbx_load_opts.retain_vertex_attrib_w`.
+ ufbx_real_list values_w;
+} ufbx_vertex_attrib;
+
+// 1D vertex attribute, see `ufbx_vertex_attrib` for information
+typedef struct ufbx_vertex_real {
+ bool exists;
+ ufbx_real_list values;
+ ufbx_uint32_list indices;
+ size_t value_reals;
+ bool unique_per_vertex;
+ ufbx_real_list values_w;
+
+ UFBX_VERTEX_ATTRIB_IMPL(ufbx_real)
+} ufbx_vertex_real;
+
+// 2D vertex attribute, see `ufbx_vertex_attrib` for information
+typedef struct ufbx_vertex_vec2 {
+ bool exists;
+ ufbx_vec2_list values;
+ ufbx_uint32_list indices;
+ size_t value_reals;
+ bool unique_per_vertex;
+ ufbx_real_list values_w;
+
+ UFBX_VERTEX_ATTRIB_IMPL(ufbx_vec2)
+} ufbx_vertex_vec2;
+
+// 3D vertex attribute, see `ufbx_vertex_attrib` for information
+typedef struct ufbx_vertex_vec3 {
+ bool exists;
+ ufbx_vec3_list values;
+ ufbx_uint32_list indices;
+ size_t value_reals;
+ bool unique_per_vertex;
+ ufbx_real_list values_w;
+
+ UFBX_VERTEX_ATTRIB_IMPL(ufbx_vec3)
+} ufbx_vertex_vec3;
+
+// 4D vertex attribute, see `ufbx_vertex_attrib` for information
+typedef struct ufbx_vertex_vec4 {
+ bool exists;
+ ufbx_vec4_list values;
+ ufbx_uint32_list indices;
+ size_t value_reals;
+ bool unique_per_vertex;
+ ufbx_real_list values_w;
+
+ UFBX_VERTEX_ATTRIB_IMPL(ufbx_vec4)
+} ufbx_vertex_vec4;
+
+// Vertex UV set/layer
+typedef struct ufbx_uv_set {
+ ufbx_string name;
+ uint32_t index;
+
+ // Vertex attributes, see `ufbx_mesh` attributes for more information
+ ufbx_vertex_vec2 vertex_uv; // < UV / texture coordinates
+ ufbx_vertex_vec3 vertex_tangent; // < (optional) Tangent vector in UV.x direction
+ ufbx_vertex_vec3 vertex_bitangent; // < (optional) Tangent vector in UV.y direction
+} ufbx_uv_set;
+
+// Vertex color set/layer
+typedef struct ufbx_color_set {
+ ufbx_string name;
+ uint32_t index;
+
+ // Vertex attributes, see `ufbx_mesh` attributes for more information
+ ufbx_vertex_vec4 vertex_color; // < Per-vertex RGBA color
+} ufbx_color_set;
+
+UFBX_LIST_TYPE(ufbx_uv_set_list, ufbx_uv_set);
+UFBX_LIST_TYPE(ufbx_color_set_list, ufbx_color_set);
+
+// Edge between two _indices_ in a mesh
+typedef struct ufbx_edge {
+ union {
+ struct { uint32_t a, b; };
+ uint32_t indices[2];
+ };
+} ufbx_edge;
+
+UFBX_LIST_TYPE(ufbx_edge_list, ufbx_edge);
+
+// Polygonal face with arbitrary number vertices, a single face contains a
+// contiguous range of mesh indices, eg. `{5,3}` would have indices 5, 6, 7
+//
+// NOTE: `num_indices` maybe less than 3 in which case the face is invalid!
+// [TODO #23: should probably remove the bad faces at load time]
+typedef struct ufbx_face {
+ uint32_t index_begin;
+ uint32_t num_indices;
+} ufbx_face;
+
+UFBX_LIST_TYPE(ufbx_face_list, ufbx_face);
+
+// Subset of mesh faces used by a single material or group.
+typedef struct ufbx_mesh_part {
+
+ // Index of the mesh part.
+ uint32_t index;
+
+ // Sub-set of the geometry
+ size_t num_faces; // < Number of faces (polygons)
+ size_t num_triangles; // < Number of triangles if triangulated
+
+ size_t num_empty_faces; // < Number of faces with zero vertices
+ size_t num_point_faces; // < Number of faces with a single vertex
+ size_t num_line_faces; // < Number of faces with two vertices
+
+ // Indices to `ufbx_mesh.faces[]`.
+ // Always contains `num_faces` elements.
+ ufbx_uint32_list face_indices;
+
+} ufbx_mesh_part;
+
+UFBX_LIST_TYPE(ufbx_mesh_part_list, ufbx_mesh_part);
+
+typedef struct ufbx_face_group {
+ int32_t id; // < Numerical ID for this group.
+ ufbx_string name; // < Name for the face group.
+} ufbx_face_group;
+
+UFBX_LIST_TYPE(ufbx_face_group_list, ufbx_face_group);
+
+typedef struct ufbx_subdivision_weight_range {
+ uint32_t weight_begin;
+ uint32_t num_weights;
+} ufbx_subdivision_weight_range;
+
+UFBX_LIST_TYPE(ufbx_subdivision_weight_range_list, ufbx_subdivision_weight_range);
+
+typedef struct ufbx_subdivision_weight {
+ ufbx_real weight;
+ uint32_t index;
+} ufbx_subdivision_weight;
+
+UFBX_LIST_TYPE(ufbx_subdivision_weight_list, ufbx_subdivision_weight);
+
+typedef struct ufbx_subdivision_result {
+ size_t result_memory_used;
+ size_t temp_memory_used;
+ size_t result_allocs;
+ size_t temp_allocs;
+
+ // Weights of vertices in the source model.
+ // Defined if `ufbx_subdivide_opts.evaluate_source_vertices` is set.
+ ufbx_subdivision_weight_range_list source_vertex_ranges;
+ ufbx_subdivision_weight_list source_vertex_weights;
+
+ // Weights of skin clusters in the source model.
+ // Defined if `ufbx_subdivide_opts.evaluate_skin_weights` is set.
+ ufbx_subdivision_weight_range_list skin_cluster_ranges;
+ ufbx_subdivision_weight_list skin_cluster_weights;
+
+} ufbx_subdivision_result;
+
+typedef enum ufbx_subdivision_display_mode UFBX_ENUM_REPR {
+ UFBX_SUBDIVISION_DISPLAY_DISABLED,
+ UFBX_SUBDIVISION_DISPLAY_HULL,
+ UFBX_SUBDIVISION_DISPLAY_HULL_AND_SMOOTH,
+ UFBX_SUBDIVISION_DISPLAY_SMOOTH,
+
+ UFBX_ENUM_FORCE_WIDTH(UFBX_SUBDIVISION_DISPLAY_MODE)
+} ufbx_subdivision_display_mode;
+
+UFBX_ENUM_TYPE(ufbx_subdivision_display_mode, UFBX_SUBDIVISION_DISPLAY_MODE, UFBX_SUBDIVISION_DISPLAY_SMOOTH);
+
+typedef enum ufbx_subdivision_boundary UFBX_ENUM_REPR {
+ UFBX_SUBDIVISION_BOUNDARY_DEFAULT,
+ UFBX_SUBDIVISION_BOUNDARY_LEGACY,
+ // OpenSubdiv: `VTX_BOUNDARY_EDGE_AND_CORNER` / `FVAR_LINEAR_CORNERS_ONLY`
+ UFBX_SUBDIVISION_BOUNDARY_SHARP_CORNERS,
+ // OpenSubdiv: `VTX_BOUNDARY_EDGE_ONLY` / `FVAR_LINEAR_NONE`
+ UFBX_SUBDIVISION_BOUNDARY_SHARP_NONE,
+ // OpenSubdiv: `FVAR_LINEAR_BOUNDARIES`
+ UFBX_SUBDIVISION_BOUNDARY_SHARP_BOUNDARY,
+ // OpenSubdiv: `FVAR_LINEAR_ALL`
+ UFBX_SUBDIVISION_BOUNDARY_SHARP_INTERIOR,
+
+ UFBX_ENUM_FORCE_WIDTH(UFBX_SUBDIVISION_BOUNDARY)
+} ufbx_subdivision_boundary;
+
+UFBX_ENUM_TYPE(ufbx_subdivision_boundary, UFBX_SUBDIVISION_BOUNDARY, UFBX_SUBDIVISION_BOUNDARY_SHARP_INTERIOR);
+
+// Polygonal mesh geometry.
+//
+// Example mesh with two triangles (x, z) and a quad (y).
+// The faces have a constant UV coordinate x/y/z.
+// The vertices have _per vertex_ normals that point up/down.
+//
+// ^ ^ ^
+// A---B-----C
+// |x / /|
+// | / y / |
+// |/ / z|
+// D-----E---F
+// v v v
+//
+// Attributes may have multiple values within a single vertex, for example a
+// UV seam vertex has two UV coordinates. Thus polygons are defined using
+// an index that counts each corner of each face polygon. If an attribute is
+// defined (even per-vertex) it will always have a valid `indices` array.
+//
+// {0,3} {3,4} {7,3} faces ({ index_begin, num_indices })
+// 0 1 2 3 4 5 6 7 8 9 index
+//
+// 0 1 3 1 2 4 3 2 4 5 vertex_indices[index]
+// A B D B C E D C E F vertices[vertex_indices[index]]
+//
+// 0 0 1 0 0 1 1 0 1 1 vertex_normal.indices[index]
+// ^ ^ v ^ ^ v v ^ v v vertex_normal.data[vertex_normal.indices[index]]
+//
+// 0 0 0 1 1 1 1 2 2 2 vertex_uv.indices[index]
+// x x x y y y y z z z vertex_uv.data[vertex_uv.indices[index]]
+//
+// Vertex position can also be accessed uniformly through an accessor:
+// 0 1 3 1 2 4 3 2 4 5 vertex_position.indices[index]
+// A B D B C E D C E F vertex_position.data[vertex_position.indices[index]]
+//
+// Some geometry data is specified per logical vertex. Vertex positions are
+// the only attribute that is guaranteed to be defined _uniquely_ per vertex.
+// Vertex attributes _may_ be defined per vertex if `unique_per_vertex == true`.
+// You can access the per-vertex values by first finding the first index that
+// refers to the given vertex.
+//
+// 0 1 2 3 4 5 vertex
+// A B C D E F vertices[vertex]
+//
+// 0 1 4 2 5 9 vertex_first_index[vertex]
+// 0 0 0 1 1 1 vertex_normal.indices[vertex_first_index[vertex]]
+// ^ ^ ^ v v v vertex_normal.data[vertex_normal.indices[vertex_first_index[vertex]]]
+//
+struct ufbx_mesh {
+ union { ufbx_element element; struct {
+ ufbx_string name;
+ ufbx_props props;
+ uint32_t element_id;
+ uint32_t typed_id;
+ ufbx_node_list instances;
+ }; };
+
+ // Number of "logical" vertices that would be treated as a single point,
+ // one vertex may be split to multiple indices for split attributes, eg. UVs
+ size_t num_vertices; // < Number of logical "vertex" points
+ size_t num_indices; // < Number of combiend vertex/attribute tuples
+ size_t num_faces; // < Number of faces (polygons) in the mesh
+ size_t num_triangles; // < Number of triangles if triangulated
+
+ // Number of edges in the mesh.
+ // NOTE: May be zero in valid meshes if the file doesn't contain edge adjacency data!
+ size_t num_edges;
+
+ size_t max_face_triangles; // < Maximum number of triangles in a face in this mesh
+
+ size_t num_empty_faces; // < Number of faces with zero vertices
+ size_t num_point_faces; // < Number of faces with a single vertex
+ size_t num_line_faces; // < Number of faces with two vertices
+
+ // Faces and optional per-face extra data
+ ufbx_face_list faces; // < Face index range
+ ufbx_bool_list face_smoothing; // < Should the face have soft normals
+ ufbx_uint32_list face_material; // < Indices to `ufbx_mesh.materials[]` and `ufbx_node.materials[]`
+ ufbx_uint32_list face_group; // < Face polygon group index, indices to `ufbx_mesh.face_groups[]`
+ ufbx_bool_list face_hole; // < Should the face be hidden as a "hole"
+
+ // Edges and optional per-edge extra data
+ ufbx_edge_list edges; // < Edge index range
+ ufbx_bool_list edge_smoothing; // < Should the edge have soft normals
+ ufbx_real_list edge_crease; // < Crease value for subdivision surfaces
+ ufbx_bool_list edge_visibility; // < Should the edge be visible
+
+ // Logical vertices and positions, alternatively you can use
+ // `vertex_position` for consistent interface with other attributes.
+ ufbx_uint32_list vertex_indices;
+ ufbx_vec3_list vertices;
+
+ // First index referring to a given vertex, `UFBX_NO_INDEX` if the vertex is unused.
+ ufbx_uint32_list vertex_first_index;
+
+ // Vertex attributes, see the comment over the struct.
+ //
+ // NOTE: Not all meshes have all attributes, in that case `indices/data == NULL`!
+ //
+ // NOTE: UV/tangent/bitangent and color are the from first sets,
+ // use `uv_sets/color_sets` to access the other layers.
+ ufbx_vertex_vec3 vertex_position; // < Vertex positions
+ ufbx_vertex_vec3 vertex_normal; // < (optional) Normal vectors, always defined if `ufbx_load_opts.generate_missing_normals`
+ ufbx_vertex_vec2 vertex_uv; // < (optional) UV / texture coordinates
+ ufbx_vertex_vec3 vertex_tangent; // < (optional) Tangent vector in UV.x direction
+ ufbx_vertex_vec3 vertex_bitangent; // < (optional) Tangent vector in UV.y direction
+ ufbx_vertex_vec4 vertex_color; // < (optional) Per-vertex RGBA color
+ ufbx_vertex_real vertex_crease; // < (optional) Crease value for subdivision surfaces
+
+ // Multiple named UV/color sets
+ // NOTE: The first set contains the same data as `vertex_uv/color`!
+ ufbx_uv_set_list uv_sets;
+ ufbx_color_set_list color_sets;
+
+ // Materials used by the mesh.
+ // NOTE: These can be wrong if you want to support per-instance materials!
+ // Use `ufbx_node.materials[]` to get the per-instance materials at the same indices.
+ ufbx_material_list materials;
+
+ // Face groups for this mesh.
+ ufbx_face_group_list face_groups;
+
+ // Segments that use a given material.
+ // Defined even if the mesh doesn't have any materials.
+ ufbx_mesh_part_list material_parts;
+
+ // Segments for each face group.
+ ufbx_mesh_part_list face_group_parts;
+
+ // Order of `material_parts` by first face that refers to it.
+ // Useful for compatibility with FBX SDK and various importers using it,
+ // as they use this material order by default.
+ ufbx_uint32_list material_part_usage_order;
+
+ // Skinned vertex positions, for efficiency the skinned positions are the
+ // same as the static ones for non-skinned meshes and `skinned_is_local`
+ // is set to true meaning you need to transform them manually using
+ // `ufbx_transform_position(&node->geometry_to_world, skinned_pos)`!
+ bool skinned_is_local;
+ ufbx_vertex_vec3 skinned_position;
+ ufbx_vertex_vec3 skinned_normal;
+
+ // Deformers
+ ufbx_skin_deformer_list skin_deformers;
+ ufbx_blend_deformer_list blend_deformers;
+ ufbx_cache_deformer_list cache_deformers;
+ ufbx_element_list all_deformers;
+
+ // Subdivision
+ uint32_t subdivision_preview_levels;
+ uint32_t subdivision_render_levels;
+ ufbx_subdivision_display_mode subdivision_display_mode;
+ ufbx_subdivision_boundary subdivision_boundary;
+ ufbx_subdivision_boundary subdivision_uv_boundary;
+
+ // The winding of the faces has been reversed.
+ bool reversed_winding;
+
+ // Normals have been generated instead of evaluated.
+ // Either from missing normals (via `ufbx_load_opts.generate_missing_normals`), skinning,
+ // tessellation, or subdivision.
+ bool generated_normals;
+
+ // Subdivision (result)
+ bool subdivision_evaluated;
+ ufbx_nullable ufbx_subdivision_result *subdivision_result;
+
+ // Tessellation (result)
+ bool from_tessellated_nurbs;
+};
+
+// The kind of light source
+typedef enum ufbx_light_type UFBX_ENUM_REPR {
+ // Single point at local origin, at `node->world_transform.position`
+ UFBX_LIGHT_POINT,
+ // Infinite directional light pointing locally towards `light->local_direction`
+ // For global: `ufbx_transform_direction(&node->node_to_world, light->local_direction)`
+ UFBX_LIGHT_DIRECTIONAL,
+ // Cone shaped light towards `light->local_direction`, between `light->inner/outer_angle`.
+ // For global: `ufbx_transform_direction(&node->node_to_world, light->local_direction)`
+ UFBX_LIGHT_SPOT,
+ // Area light, shape specified by `light->area_shape`
+ // TODO: Units?
+ UFBX_LIGHT_AREA,
+ // Volumetric light source
+ // TODO: How does this work
+ UFBX_LIGHT_VOLUME,
+
+ UFBX_ENUM_FORCE_WIDTH(UFBX_LIGHT_TYPE)
+} ufbx_light_type;
+
+UFBX_ENUM_TYPE(ufbx_light_type, UFBX_LIGHT_TYPE, UFBX_LIGHT_VOLUME);
+
+// How fast does the light intensity decay at a distance
+typedef enum ufbx_light_decay UFBX_ENUM_REPR {
+ UFBX_LIGHT_DECAY_NONE, // < 1 (no decay)
+ UFBX_LIGHT_DECAY_LINEAR, // < 1 / d
+ UFBX_LIGHT_DECAY_QUADRATIC, // < 1 / d^2 (physically accurate)
+ UFBX_LIGHT_DECAY_CUBIC, // < 1 / d^3
+
+ UFBX_ENUM_FORCE_WIDTH(UFBX_LIGHT_DECAY)
+} ufbx_light_decay;
+
+UFBX_ENUM_TYPE(ufbx_light_decay, UFBX_LIGHT_DECAY, UFBX_LIGHT_DECAY_CUBIC);
+
+typedef enum ufbx_light_area_shape UFBX_ENUM_REPR {
+ UFBX_LIGHT_AREA_SHAPE_RECTANGLE,
+ UFBX_LIGHT_AREA_SHAPE_SPHERE,
+
+ UFBX_ENUM_FORCE_WIDTH(UFBX_LIGHT_AREA_SHAPE)
+} ufbx_light_area_shape;
+
+UFBX_ENUM_TYPE(ufbx_light_area_shape, UFBX_LIGHT_AREA_SHAPE, UFBX_LIGHT_AREA_SHAPE_SPHERE);
+
+// Light source attached to a `ufbx_node`
+struct ufbx_light {
+ union { ufbx_element element; struct {
+ ufbx_string name;
+ ufbx_props props;
+ uint32_t element_id;
+ uint32_t typed_id;
+ ufbx_node_list instances;
+ }; };
+
+ // Color and intensity of the light, usually you want to use `color * intensity`
+ // NOTE: `intensity` is 0.01x of the property `"Intensity"` as that matches
+ // matches values in DCC programs before exporting.
+ ufbx_vec3 color;
+ ufbx_real intensity;
+
+ // Direction the light is aimed at in node's local space, usually -Y
+ ufbx_vec3 local_direction;
+
+ // Type of the light and shape parameters
+ ufbx_light_type type;
+ ufbx_light_decay decay;
+ ufbx_light_area_shape area_shape;
+ ufbx_real inner_angle;
+ ufbx_real outer_angle;
+
+ bool cast_light;
+ bool cast_shadows;
+};
+
+typedef enum ufbx_projection_mode UFBX_ENUM_REPR {
+ // Perspective projection.
+ UFBX_PROJECTION_MODE_PERSPECTIVE,
+
+ // Orthographic projection.
+ UFBX_PROJECTION_MODE_ORTHOGRAPHIC,
+
+ UFBX_ENUM_FORCE_WIDTH(UFBX_PROJECTION_MODE)
+} ufbx_projection_mode;
+
+UFBX_ENUM_TYPE(ufbx_projection_mode, UFBX_PROJECTION_MODE, UFBX_PROJECTION_MODE_ORTHOGRAPHIC);
+
+// Method of specifying the rendering resolution from properties
+// NOTE: Handled internally by ufbx, ignore unless you interpret `ufbx_props` directly!
+typedef enum ufbx_aspect_mode UFBX_ENUM_REPR {
+ // No defined resolution
+ UFBX_ASPECT_MODE_WINDOW_SIZE,
+ // `"AspectWidth"` and `"AspectHeight"` are relative to each other
+ UFBX_ASPECT_MODE_FIXED_RATIO,
+ // `"AspectWidth"` and `"AspectHeight"` are both pixels
+ UFBX_ASPECT_MODE_FIXED_RESOLUTION,
+ // `"AspectWidth"` is pixels, `"AspectHeight"` is relative to width
+ UFBX_ASPECT_MODE_FIXED_WIDTH,
+ // < `"AspectHeight"` is pixels, `"AspectWidth"` is relative to height
+ UFBX_ASPECT_MODE_FIXED_HEIGHT,
+
+ UFBX_ENUM_FORCE_WIDTH(UFBX_ASPECT_MODE)
+} ufbx_aspect_mode;
+
+UFBX_ENUM_TYPE(ufbx_aspect_mode, UFBX_ASPECT_MODE, UFBX_ASPECT_MODE_FIXED_HEIGHT);
+
+// Method of specifying the field of view from properties
+// NOTE: Handled internally by ufbx, ignore unless you interpret `ufbx_props` directly!
+typedef enum ufbx_aperture_mode UFBX_ENUM_REPR {
+ // Use separate `"FieldOfViewX"` and `"FieldOfViewY"` as horizontal/vertical FOV angles
+ UFBX_APERTURE_MODE_HORIZONTAL_AND_VERTICAL,
+ // Use `"FieldOfView"` as horizontal FOV angle, derive vertical angle via aspect ratio
+ UFBX_APERTURE_MODE_HORIZONTAL,
+ // Use `"FieldOfView"` as vertical FOV angle, derive horizontal angle via aspect ratio
+ UFBX_APERTURE_MODE_VERTICAL,
+ // Compute the field of view from the render gate size and focal length
+ UFBX_APERTURE_MODE_FOCAL_LENGTH,
+
+ UFBX_ENUM_FORCE_WIDTH(UFBX_APERTURE_MODE)
+} ufbx_aperture_mode;
+
+UFBX_ENUM_TYPE(ufbx_aperture_mode, UFBX_APERTURE_MODE, UFBX_APERTURE_MODE_FOCAL_LENGTH);
+
+// Method of specifying the render gate size from properties
+// NOTE: Handled internally by ufbx, ignore unless you interpret `ufbx_props` directly!
+typedef enum ufbx_gate_fit UFBX_ENUM_REPR {
+ // Use the film/aperture size directly as the render gate
+ UFBX_GATE_FIT_NONE,
+ // Fit the render gate to the height of the film, derive width from aspect ratio
+ UFBX_GATE_FIT_VERTICAL,
+ // Fit the render gate to the width of the film, derive height from aspect ratio
+ UFBX_GATE_FIT_HORIZONTAL,
+ // Fit the render gate so that it is fully contained within the film gate
+ UFBX_GATE_FIT_FILL,
+ // Fit the render gate so that it fully contains the film gate
+ UFBX_GATE_FIT_OVERSCAN,
+ // Stretch the render gate to match the film gate
+ // TODO: Does this differ from `UFBX_GATE_FIT_NONE`?
+ UFBX_GATE_FIT_STRETCH,
+
+ UFBX_ENUM_FORCE_WIDTH(UFBX_GATE_FIT)
+} ufbx_gate_fit;
+
+UFBX_ENUM_TYPE(ufbx_gate_fit, UFBX_GATE_FIT, UFBX_GATE_FIT_STRETCH);
+
+// Camera film/aperture size defaults
+// NOTE: Handled internally by ufbx, ignore unless you interpret `ufbx_props` directly!
+typedef enum ufbx_aperture_format UFBX_ENUM_REPR {
+ UFBX_APERTURE_FORMAT_CUSTOM, // < Use `"FilmWidth"` and `"FilmHeight"`
+ UFBX_APERTURE_FORMAT_16MM_THEATRICAL, // < 0.404 x 0.295 inches
+ UFBX_APERTURE_FORMAT_SUPER_16MM, // < 0.493 x 0.292 inches
+ UFBX_APERTURE_FORMAT_35MM_ACADEMY, // < 0.864 x 0.630 inches
+ UFBX_APERTURE_FORMAT_35MM_TV_PROJECTION, // < 0.816 x 0.612 inches
+ UFBX_APERTURE_FORMAT_35MM_FULL_APERTURE, // < 0.980 x 0.735 inches
+ UFBX_APERTURE_FORMAT_35MM_185_PROJECTION, // < 0.825 x 0.446 inches
+ UFBX_APERTURE_FORMAT_35MM_ANAMORPHIC, // < 0.864 x 0.732 inches (squeeze ratio: 2)
+ UFBX_APERTURE_FORMAT_70MM_PROJECTION, // < 2.066 x 0.906 inches
+ UFBX_APERTURE_FORMAT_VISTAVISION, // < 1.485 x 0.991 inches
+ UFBX_APERTURE_FORMAT_DYNAVISION, // < 2.080 x 1.480 inches
+ UFBX_APERTURE_FORMAT_IMAX, // < 2.772 x 2.072 inches
+
+ UFBX_ENUM_FORCE_WIDTH(UFBX_APERTURE_FORMAT)
+} ufbx_aperture_format;
+
+UFBX_ENUM_TYPE(ufbx_aperture_format, UFBX_APERTURE_FORMAT, UFBX_APERTURE_FORMAT_IMAX);
+
+typedef enum ufbx_coordinate_axis UFBX_ENUM_REPR {
+ UFBX_COORDINATE_AXIS_POSITIVE_X,
+ UFBX_COORDINATE_AXIS_NEGATIVE_X,
+ UFBX_COORDINATE_AXIS_POSITIVE_Y,
+ UFBX_COORDINATE_AXIS_NEGATIVE_Y,
+ UFBX_COORDINATE_AXIS_POSITIVE_Z,
+ UFBX_COORDINATE_AXIS_NEGATIVE_Z,
+ UFBX_COORDINATE_AXIS_UNKNOWN,
+
+ UFBX_ENUM_FORCE_WIDTH(UFBX_COORDINATE_AXIS)
+} ufbx_coordinate_axis;
+
+UFBX_ENUM_TYPE(ufbx_coordinate_axis, UFBX_COORDINATE_AXIS, UFBX_COORDINATE_AXIS_UNKNOWN);
+
+// Coordinate axes the scene is represented in.
+// NOTE: `front` is the _opposite_ from forward!
+typedef struct ufbx_coordinate_axes {
+ ufbx_coordinate_axis right;
+ ufbx_coordinate_axis up;
+ ufbx_coordinate_axis front;
+} ufbx_coordinate_axes;
+
+// Camera attached to a `ufbx_node`
+struct ufbx_camera {
+ union { ufbx_element element; struct {
+ ufbx_string name;
+ ufbx_props props;
+ uint32_t element_id;
+ uint32_t typed_id;
+ ufbx_node_list instances;
+ }; };
+
+ // Projection mode (perspective/orthographic).
+ ufbx_projection_mode projection_mode;
+
+ // If set to `true`, `resolution` represents actual pixel values, otherwise
+ // it's only useful for its aspect ratio.
+ bool resolution_is_pixels;
+
+ // Render resolution, either in pixels or arbitrary units, depending on above
+ ufbx_vec2 resolution;
+
+ // Horizontal/vertical field of view in degrees
+ // Valid if `projection_mode == UFBX_PROJECTION_MODE_PERSPECTIVE`.
+ ufbx_vec2 field_of_view_deg;
+
+ // Component-wise `tan(field_of_view_deg)`, also represents the size of the
+ // proection frustum slice at distance of 1.
+ // Valid if `projection_mode == UFBX_PROJECTION_MODE_PERSPECTIVE`.
+ ufbx_vec2 field_of_view_tan;
+
+ // Orthographic camera extents.
+ // Valid if `projection_mode == UFBX_PROJECTION_MODE_ORTHOGRAPHIC`.
+ ufbx_real orthographic_extent;
+
+ // Orthographic camera size.
+ // Valid if `projection_mode == UFBX_PROJECTION_MODE_ORTHOGRAPHIC`.
+ ufbx_vec2 orthographic_size;
+
+ // Size of the projection plane at distance 1.
+ // Equal to `field_of_view_tan` if perspective, `orthographic_size` if orthographic.
+ ufbx_vec2 projection_plane;
+
+ // Aspect ratio of the camera.
+ ufbx_real aspect_ratio;
+
+ // Near plane of the frustum in units from the camera.
+ ufbx_real near_plane;
+
+ // Far plane of the frustum in units from the camera.
+ ufbx_real far_plane;
+
+ // Coordinate system that the projection uses.
+ // FBX saves cameras with +X forward and +Y up, but you can override this using
+ // `ufbx_load_opts.target_camera_axes` and it will be reflected here.
+ ufbx_coordinate_axes projection_axes;
+
+ // Advanced properties used to compute the above
+ ufbx_aspect_mode aspect_mode;
+ ufbx_aperture_mode aperture_mode;
+ ufbx_gate_fit gate_fit;
+ ufbx_aperture_format aperture_format;
+ ufbx_real focal_length_mm; // < Focal length in millimeters
+ ufbx_vec2 film_size_inch; // < Film size in inches
+ ufbx_vec2 aperture_size_inch; // < Aperture/film gate size in inches
+ ufbx_real squeeze_ratio; // < Anamoprhic stretch ratio
+};
+
+// Bone attached to a `ufbx_node`, provides the logical length of the bone
+// but most interesting information is directly in `ufbx_node`.
+struct ufbx_bone {
+ union { ufbx_element element; struct {
+ ufbx_string name;
+ ufbx_props props;
+ uint32_t element_id;
+ uint32_t typed_id;
+ ufbx_node_list instances;
+ }; };
+
+ // Visual radius of the bone
+ ufbx_real radius;
+
+ // Length of the bone relative to the distance between two nodes
+ ufbx_real relative_length;
+
+ // Is the bone a root bone
+ bool is_root;
+};
+
+// Empty/NULL/locator connected to a node, actual details in `ufbx_node`
+struct ufbx_empty {
+ union { ufbx_element element; struct {
+ ufbx_string name;
+ ufbx_props props;
+ uint32_t element_id;
+ uint32_t typed_id;
+ ufbx_node_list instances;
+ }; };
+};
+
+// -- Node attributes (curves/surfaces)
+
+// Segment of a `ufbx_line_curve`, indices refer to `ufbx_line_curve.point_indices[]`
+typedef struct ufbx_line_segment {
+ uint32_t index_begin;
+ uint32_t num_indices;
+} ufbx_line_segment;
+
+UFBX_LIST_TYPE(ufbx_line_segment_list, ufbx_line_segment);
+
+struct ufbx_line_curve {
+ union { ufbx_element element; struct {
+ ufbx_string name;
+ ufbx_props props;
+ uint32_t element_id;
+ uint32_t typed_id;
+ ufbx_node_list instances;
+ }; };
+
+ ufbx_vec3 color;
+
+ ufbx_vec3_list control_points; // < List of possible values the line passes through
+ ufbx_uint32_list point_indices; // < Indices to `control_points[]` the line goes through
+
+ ufbx_line_segment_list segments;
+
+ // Tessellation (result)
+ bool from_tessellated_nurbs;
+};
+
+typedef enum ufbx_nurbs_topology UFBX_ENUM_REPR {
+ // The endpoints are not connected.
+ UFBX_NURBS_TOPOLOGY_OPEN,
+ // Repeats first `ufbx_nurbs_basis.order - 1` control points after the end.
+ UFBX_NURBS_TOPOLOGY_PERIODIC,
+ // Repeats the first control point after the end.
+ UFBX_NURBS_TOPOLOGY_CLOSED,
+
+ UFBX_ENUM_FORCE_WIDTH(UFBX_NURBS_TOPOLOGY)
+} ufbx_nurbs_topology;
+
+UFBX_ENUM_TYPE(ufbx_nurbs_topology, UFBX_NURBS_TOPOLOGY, UFBX_NURBS_TOPOLOGY_CLOSED);
+
+// NURBS basis functions for an axis
+typedef struct ufbx_nurbs_basis {
+
+ // Number of control points influencing a point on the curve/surface.
+ // Equal to the degree plus one.
+ uint32_t order;
+
+ // Topology (periodicity) of the dimension.
+ ufbx_nurbs_topology topology;
+
+ // Subdivision of the parameter range to control points.
+ ufbx_real_list knot_vector;
+
+ // Range for the parameter value.
+ ufbx_real t_min;
+ ufbx_real t_max;
+
+ // Parameter values of control points.
+ ufbx_real_list spans;
+
+ // `true` if this axis is two-dimensional.
+ bool is_2d;
+
+ // Number of control points that need to be copied to the end.
+ // This is just for convenience as it could be derived from `topology` and
+ // `order`. If for example `num_wrap_control_points == 3` you should repeat
+ // the first 3 control points after the end.
+ // HINT: You don't need to worry about this if you use ufbx functions
+ // like `ufbx_evaluate_nurbs_curve()` as they handle this internally.
+ size_t num_wrap_control_points;
+
+ // `true` if the parametrization is well defined.
+ bool valid;
+
+} ufbx_nurbs_basis;
+
+struct ufbx_nurbs_curve {
+ union { ufbx_element element; struct {
+ ufbx_string name;
+ ufbx_props props;
+ uint32_t element_id;
+ uint32_t typed_id;
+ ufbx_node_list instances;
+ }; };
+
+ // Basis in the U axis
+ ufbx_nurbs_basis basis;
+
+ // Linear array of control points
+ // NOTE: The control points are _not_ homogeneous, meaning you have to multiply
+ // them by `w` before evaluating the surface.
+ ufbx_vec4_list control_points;
+};
+
+struct ufbx_nurbs_surface {
+ union { ufbx_element element; struct {
+ ufbx_string name;
+ ufbx_props props;
+ uint32_t element_id;
+ uint32_t typed_id;
+ ufbx_node_list instances;
+ }; };
+
+ // Basis in the U/V axes
+ ufbx_nurbs_basis basis_u;
+ ufbx_nurbs_basis basis_v;
+
+ // Number of control points for the U/V axes
+ size_t num_control_points_u;
+ size_t num_control_points_v;
+
+ // 2D array of control points.
+ // Memory layout: `V * num_control_points_u + U`
+ // NOTE: The control points are _not_ homogeneous, meaning you have to multiply
+ // them by `w` before evaluating the surface.
+ ufbx_vec4_list control_points;
+
+ // How many segments tessellate each span in `ufbx_nurbs_basis.spans`.
+ uint32_t span_subdivision_u;
+ uint32_t span_subdivision_v;
+
+ // If `true` the resulting normals should be flipped when evaluated.
+ bool flip_normals;
+
+ // Material for the whole surface.
+ // NOTE: May be `NULL`!
+ ufbx_nullable ufbx_material *material;
+};
+
+struct ufbx_nurbs_trim_surface {
+ union { ufbx_element element; struct {
+ ufbx_string name;
+ ufbx_props props;
+ uint32_t element_id;
+ uint32_t typed_id;
+ ufbx_node_list instances;
+ }; };
+};
+
+struct ufbx_nurbs_trim_boundary {
+ union { ufbx_element element; struct {
+ ufbx_string name;
+ ufbx_props props;
+ uint32_t element_id;
+ uint32_t typed_id;
+ ufbx_node_list instances;
+ }; };
+};
+
+// -- Node attributes (advanced)
+
+struct ufbx_procedural_geometry {
+ union { ufbx_element element; struct {
+ ufbx_string name;
+ ufbx_props props;
+ uint32_t element_id;
+ uint32_t typed_id;
+ ufbx_node_list instances;
+ }; };
+};
+
+struct ufbx_stereo_camera {
+ union { ufbx_element element; struct {
+ ufbx_string name;
+ ufbx_props props;
+ uint32_t element_id;
+ uint32_t typed_id;
+ ufbx_node_list instances;
+ }; };
+
+ ufbx_nullable ufbx_camera *left;
+ ufbx_nullable ufbx_camera *right;
+};
+
+struct ufbx_camera_switcher {
+ union { ufbx_element element; struct {
+ ufbx_string name;
+ ufbx_props props;
+ uint32_t element_id;
+ uint32_t typed_id;
+ ufbx_node_list instances;
+ }; };
+};
+
+typedef enum ufbx_marker_type UFBX_ENUM_REPR {
+ UFBX_MARKER_UNKNOWN, // < Unknown marker type
+ UFBX_MARKER_FK_EFFECTOR, // < FK (Forward Kinematics) effector
+ UFBX_MARKER_IK_EFFECTOR, // < IK (Inverse Kinematics) effector
+
+ UFBX_ENUM_FORCE_WIDTH(UFBX_MARKER_TYPE)
+} ufbx_marker_type;
+
+UFBX_ENUM_TYPE(ufbx_marker_type, UFBX_MARKER_TYPE, UFBX_MARKER_IK_EFFECTOR);
+
+// Tracking marker for effectors
+struct ufbx_marker {
+ union { ufbx_element element; struct {
+ ufbx_string name;
+ ufbx_props props;
+ uint32_t element_id;
+ uint32_t typed_id;
+ ufbx_node_list instances;
+ }; };
+
+ // Type of the marker
+ ufbx_marker_type type;
+};
+
+// LOD level display mode.
+typedef enum ufbx_lod_display UFBX_ENUM_REPR {
+ UFBX_LOD_DISPLAY_USE_LOD, // < Display the LOD level if the distance is appropriate.
+ UFBX_LOD_DISPLAY_SHOW, // < Always display the LOD level.
+ UFBX_LOD_DISPLAY_HIDE, // < Never display the LOD level.
+
+ UFBX_ENUM_FORCE_WIDTH(UFBX_LOD_DISPLAY)
+} ufbx_lod_display;
+
+UFBX_ENUM_TYPE(ufbx_lod_display, UFBX_LOD_DISPLAY, UFBX_LOD_DISPLAY_HIDE);
+
+// Single LOD level within an LOD group.
+// Specifies properties of the Nth child of the _node_ containing the LOD group.
+typedef struct ufbx_lod_level {
+
+ // Minimum distance to show this LOD level.
+ // NOTE: In world units by default, or in screen percentage if
+ // `ufbx_lod_group.relative_distances` is set.
+ ufbx_real distance;
+
+ // LOD display mode.
+ // NOTE: Mostly for editing, you should probably ignore this
+ // unless making a modeling program.
+ ufbx_lod_display display;
+
+} ufbx_lod_level;
+
+UFBX_LIST_TYPE(ufbx_lod_level_list, ufbx_lod_level);
+
+// Group of LOD (Level of Detail) levels for an object.
+// The actual LOD models are defined in the parent `ufbx_node.children`.
+struct ufbx_lod_group {
+ union { ufbx_element element; struct {
+ ufbx_string name;
+ ufbx_props props;
+ uint32_t element_id;
+ uint32_t typed_id;
+ ufbx_node_list instances;
+ }; };
+
+ // If set to `true`, `ufbx_lod_level.distance` represents a screen size percentage.
+ bool relative_distances;
+
+ // LOD levels matching in order to `ufbx_node.children`.
+ ufbx_lod_level_list lod_levels;
+
+ // If set to `true` don't account for parent transform when computing the distance.
+ bool ignore_parent_transform;
+
+ // If `use_distance_limit` is enabled hide the group if the distance is not between
+ // `distance_limit_min` and `distance_limit_max`.
+ bool use_distance_limit;
+ ufbx_real distance_limit_min;
+ ufbx_real distance_limit_max;
+};
+
+// -- Deformers
+
+// Method to evaluate the skinning on a per-vertex level
+typedef enum ufbx_skinning_method UFBX_ENUM_REPR {
+ // Linear blend skinning: Blend transformation matrices by vertex weights
+ UFBX_SKINNING_METHOD_LINEAR,
+ // One vertex should have only one bone attached
+ UFBX_SKINNING_METHOD_RIGID,
+ // Convert the transformations to dual quaternions and blend in that space
+ UFBX_SKINNING_METHOD_DUAL_QUATERNION,
+ // Blend between `UFBX_SKINNING_METHOD_LINEAR` and `UFBX_SKINNING_METHOD_BLENDED_DQ_LINEAR`
+ // The blend weight can be found either per-vertex in `ufbx_skin_vertex.dq_weight`
+ // or in `ufbx_skin_deformer.dq_vertices/dq_weights` (indexed by vertex).
+ UFBX_SKINNING_METHOD_BLENDED_DQ_LINEAR,
+
+ UFBX_ENUM_FORCE_WIDTH(UFBX_SKINNING_METHOD)
+} ufbx_skinning_method;
+
+UFBX_ENUM_TYPE(ufbx_skinning_method, UFBX_SKINNING_METHOD, UFBX_SKINNING_METHOD_BLENDED_DQ_LINEAR);
+
+// Skin weight information for a single mesh vertex
+typedef struct ufbx_skin_vertex {
+
+ // Each vertex is influenced by weights from `ufbx_skin_deformer.weights[]`
+ // The weights are sorted by decreasing weight so you can take the first N
+ // weights to get a cheaper approximation of the vertex.
+ // NOTE: The weights are not guaranteed to be normalized!
+ uint32_t weight_begin; // < Index to start from in the `weights[]` array
+ uint32_t num_weights; // < Number of weights influencing the vertex
+
+ // Blend weight between Linear Blend Skinning (0.0) and Dual Quaternion (1.0).
+ // Should be used if `skinning_method == UFBX_SKINNING_METHOD_BLENDED_DQ_LINEAR`
+ ufbx_real dq_weight;
+
+} ufbx_skin_vertex;
+
+UFBX_LIST_TYPE(ufbx_skin_vertex_list, ufbx_skin_vertex);
+
+// Single per-vertex per-cluster weight, see `ufbx_skin_vertex`
+typedef struct ufbx_skin_weight {
+ uint32_t cluster_index; // < Index into `ufbx_skin_deformer.clusters[]`
+ ufbx_real weight; // < Amount this bone influence the vertex
+} ufbx_skin_weight;
+
+UFBX_LIST_TYPE(ufbx_skin_weight_list, ufbx_skin_weight);
+
+// Skin deformer specifies a binding between a logical set of bones (a skeleton)
+// and a mesh. Each bone is represented by a `ufbx_skin_cluster` that contains
+// the binding matrix and a `ufbx_node *bone` that has the current transformation.
+struct ufbx_skin_deformer {
+ union { ufbx_element element; struct {
+ ufbx_string name;
+ ufbx_props props;
+ uint32_t element_id;
+ uint32_t typed_id;
+ }; };
+
+ ufbx_skinning_method skinning_method;
+
+ // Clusters (bones) in the skin
+ ufbx_skin_cluster_list clusters;
+
+ // Per-vertex weight information
+ ufbx_skin_vertex_list vertices;
+ ufbx_skin_weight_list weights;
+
+ // Largest amount of weights a single vertex can have
+ size_t max_weights_per_vertex;
+
+ // Blend weights between Linear Blend Skinning (0.0) and Dual Quaternion (1.0).
+ // HINT: You probably want to use `vertices` and `ufbx_skin_vertex.dq_weight` instead!
+ // NOTE: These may be out-of-bounds for a given mesh, `vertices` is always safe.
+ size_t num_dq_weights;
+ ufbx_uint32_list dq_vertices;
+ ufbx_real_list dq_weights;
+};
+
+// Cluster of vertices bound to a single bone.
+struct ufbx_skin_cluster {
+ union { ufbx_element element; struct {
+ ufbx_string name;
+ ufbx_props props;
+ uint32_t element_id;
+ uint32_t typed_id;
+ }; };
+
+ // The bone node the cluster is attached to
+ // NOTE: Always valid if found from `ufbx_skin_deformer.clusters[]` unless
+ // `ufbx_load_opts.connect_broken_elements` is `true`.
+ ufbx_nullable ufbx_node *bone_node;
+
+ // Binding matrix from local mesh vertices to the bone
+ ufbx_matrix geometry_to_bone;
+
+ // Binding matrix from local mesh _node_ to the bone.
+ // NOTE: Prefer `geometry_to_bone` in most use cases!
+ ufbx_matrix mesh_node_to_bone;
+
+ // Matrix that specifies the rest/bind pose transform of the node,
+ // not generally needed for skinning, use `geometry_to_bone` instead.
+ ufbx_matrix bind_to_world;
+
+ // Precomputed matrix/transform that accounts for the current bone transform
+ // ie. `ufbx_matrix_mul(&cluster->bone->node_to_world, &cluster->geometry_to_bone)`
+ ufbx_matrix geometry_to_world;
+ ufbx_transform geometry_to_world_transform;
+
+ // Raw weights indexed by each _vertex_ of a mesh (not index!)
+ // HINT: It may be simpler to use `ufbx_skin_deformer.vertices[]/weights[]` instead!
+ // NOTE: These may be out-of-bounds for a given mesh, `ufbx_skin_deformer.vertices` is always safe.
+ size_t num_weights; // < Number of vertices in the cluster
+ ufbx_uint32_list vertices; // < Vertex indices in `ufbx_mesh.vertices[]`
+ ufbx_real_list weights; // < Per-vertex weight values
+};
+
+// Blend shape deformer can contain multiple channels (think of sliders between morphs)
+// that may optionally have in-between keyframes.
+struct ufbx_blend_deformer {
+ union { ufbx_element element; struct {
+ ufbx_string name;
+ ufbx_props props;
+ uint32_t element_id;
+ uint32_t typed_id;
+ }; };
+
+ // Independent morph targets of the deformer.
+ ufbx_blend_channel_list channels;
+};
+
+// Blend shape associated with a target weight in a series of morphs
+typedef struct ufbx_blend_keyframe {
+ // The target blend shape offsets.
+ ufbx_blend_shape *shape;
+
+ // Weight value at which to apply the keyframe at full strength
+ ufbx_real target_weight;
+
+ // The weight the shape should be currently applied with
+ ufbx_real effective_weight;
+} ufbx_blend_keyframe;
+
+UFBX_LIST_TYPE(ufbx_blend_keyframe_list, ufbx_blend_keyframe);
+
+// Blend channel consists of multiple morph-key targets that are interpolated.
+// In simple cases there will be only one keyframe that is the target shape.
+struct ufbx_blend_channel {
+ union { ufbx_element element; struct {
+ ufbx_string name;
+ ufbx_props props;
+ uint32_t element_id;
+ uint32_t typed_id;
+ }; };
+
+ // Current weight of the channel
+ ufbx_real weight;
+
+ // Key morph targets to blend between depending on `weight`
+ // In usual cases there's only one target per channel
+ ufbx_blend_keyframe_list keyframes;
+
+ // Final blend shape ignoring any intermediate blend shapes.
+ ufbx_nullable ufbx_blend_shape *target_shape;
+};
+
+// Blend shape target containing the actual vertex offsets
+struct ufbx_blend_shape {
+ union { ufbx_element element; struct {
+ ufbx_string name;
+ ufbx_props props;
+ uint32_t element_id;
+ uint32_t typed_id;
+ }; };
+
+ // Vertex offsets to apply over the base mesh
+ // NOTE: The `offset_vertices` may be out-of-bounds for a given mesh!
+ size_t num_offsets; // < Number of vertex offsets in the following arrays
+ ufbx_uint32_list offset_vertices; // < Indices to `ufbx_mesh.vertices[]`
+ ufbx_vec3_list position_offsets; // < Always specified per-vertex offsets
+ ufbx_vec3_list normal_offsets; // < Empty if not specified
+};
+
+typedef enum ufbx_cache_file_format UFBX_ENUM_REPR {
+ UFBX_CACHE_FILE_FORMAT_UNKNOWN, // < Unknown cache file format
+ UFBX_CACHE_FILE_FORMAT_PC2, // < .pc2 Point cache file
+ UFBX_CACHE_FILE_FORMAT_MC, // < .mc/.mcx Maya cache file
+
+ UFBX_ENUM_FORCE_WIDTH(UFBX_CACHE_FILE_FORMAT)
+} ufbx_cache_file_format;
+
+UFBX_ENUM_TYPE(ufbx_cache_file_format, UFBX_CACHE_FILE_FORMAT, UFBX_CACHE_FILE_FORMAT_MC);
+
+typedef enum ufbx_cache_data_format UFBX_ENUM_REPR {
+ UFBX_CACHE_DATA_FORMAT_UNKNOWN, // < Unknown data format
+ UFBX_CACHE_DATA_FORMAT_REAL_FLOAT, // < `float data[]`
+ UFBX_CACHE_DATA_FORMAT_VEC3_FLOAT, // < `struct { float x, y, z; } data[]`
+ UFBX_CACHE_DATA_FORMAT_REAL_DOUBLE, // < `double data[]`
+ UFBX_CACHE_DATA_FORMAT_VEC3_DOUBLE, // < `struct { double x, y, z; } data[]`
+
+ UFBX_ENUM_FORCE_WIDTH(UFBX_CACHE_DATA_FORMAT)
+} ufbx_cache_data_format;
+
+UFBX_ENUM_TYPE(ufbx_cache_data_format, UFBX_CACHE_DATA_FORMAT, UFBX_CACHE_DATA_FORMAT_VEC3_DOUBLE);
+
+typedef enum ufbx_cache_data_encoding UFBX_ENUM_REPR {
+ UFBX_CACHE_DATA_ENCODING_UNKNOWN, // < Unknown data encoding
+ UFBX_CACHE_DATA_ENCODING_LITTLE_ENDIAN, // < Contiguous little-endian array
+ UFBX_CACHE_DATA_ENCODING_BIG_ENDIAN, // < Contiguous big-endian array
+
+ UFBX_ENUM_FORCE_WIDTH(UFBX_CACHE_DATA_ENCODING)
+} ufbx_cache_data_encoding;
+
+UFBX_ENUM_TYPE(ufbx_cache_data_encoding, UFBX_CACHE_DATA_ENCODING, UFBX_CACHE_DATA_ENCODING_BIG_ENDIAN);
+
+// Known interpretations of geometry cache data.
+typedef enum ufbx_cache_interpretation UFBX_ENUM_REPR {
+ // Unknown interpretation, see `ufbx_cache_channel.interpretation_name` for more information.
+ UFBX_CACHE_INTERPRETATION_UNKNOWN,
+
+ // Generic "points" interpretation, FBX SDK default. Usually fine to interpret
+ // as vertex positions if no other cache channels are specified.
+ UFBX_CACHE_INTERPRETATION_POINTS,
+
+ // Vertex positions.
+ UFBX_CACHE_INTERPRETATION_VERTEX_POSITION,
+
+ // Vertex normals.
+ UFBX_CACHE_INTERPRETATION_VERTEX_NORMAL,
+
+ UFBX_ENUM_FORCE_WIDTH(UFBX_CACHE_INTERPRETATION)
+} ufbx_cache_interpretation;
+
+UFBX_ENUM_TYPE(ufbx_cache_interpretation, UFBX_CACHE_INTERPRETATION, UFBX_CACHE_INTERPRETATION_VERTEX_NORMAL);
+
+typedef struct ufbx_cache_frame {
+
+ // Name of the channel this frame belongs to.
+ ufbx_string channel;
+
+ // Time of this frame in seconds.
+ double time;
+
+ // Name of the file containing the data.
+ // The specified file may contain multiple frames, use `data_offset` etc. to
+ // read at the right position.
+ ufbx_string filename;
+
+ // Format of the wrapper file.
+ ufbx_cache_file_format file_format;
+
+ // Axis to mirror the read data by.
+ ufbx_mirror_axis mirror_axis;
+
+ // Factor to scale the geometry by.
+ ufbx_real scale_factor;
+
+ ufbx_cache_data_format data_format; // < Format of the data in the file
+ ufbx_cache_data_encoding data_encoding; // < Binary encoding of the data
+ uint64_t data_offset; // < Byte offset into the file
+ uint32_t data_count; // < Number of data elements
+ uint32_t data_element_bytes; // < Size of a single data element in bytes
+ uint64_t data_total_bytes; // < Size of the whole data blob in bytes
+} ufbx_cache_frame;
+
+UFBX_LIST_TYPE(ufbx_cache_frame_list, ufbx_cache_frame);
+
+typedef struct ufbx_cache_channel {
+
+ // Name of the geometry cache channel.
+ ufbx_string name;
+
+ // What does the data in this channel represent.
+ ufbx_cache_interpretation interpretation;
+
+ // Source name for `interpretation`, especially useful if `interpretation` is
+ // `UFBX_CACHE_INTERPRETATION_UNKNOWN`.
+ ufbx_string interpretation_name;
+
+ // List of frames belonging to this channel.
+ // Sorted by time (`ufbx_cache_frame.time`).
+ ufbx_cache_frame_list frames;
+
+ // Axis to mirror the frames by.
+ ufbx_mirror_axis mirror_axis;
+
+ // Factor to scale the geometry by.
+ ufbx_real scale_factor;
+
+} ufbx_cache_channel;
+
+UFBX_LIST_TYPE(ufbx_cache_channel_list, ufbx_cache_channel);
+
+typedef struct ufbx_geometry_cache {
+ ufbx_string root_filename;
+ ufbx_cache_channel_list channels;
+ ufbx_cache_frame_list frames;
+ ufbx_string_list extra_info;
+} ufbx_geometry_cache;
+
+struct ufbx_cache_deformer {
+ union { ufbx_element element; struct {
+ ufbx_string name;
+ ufbx_props props;
+ uint32_t element_id;
+ uint32_t typed_id;
+ }; };
+
+ ufbx_string channel;
+ ufbx_nullable ufbx_cache_file *file;
+
+ // Only valid if `ufbx_load_opts.load_external_files` is set!
+ ufbx_nullable ufbx_geometry_cache *external_cache;
+ ufbx_nullable ufbx_cache_channel *external_channel;
+};
+
+struct ufbx_cache_file {
+ union { ufbx_element element; struct {
+ ufbx_string name;
+ ufbx_props props;
+ uint32_t element_id;
+ uint32_t typed_id;
+ }; };
+
+ // Filename relative to the currently loaded file.
+ // HINT: If using functions other than `ufbx_load_file()`, you can provide
+ // `ufbx_load_opts.filename/raw_filename` to let ufbx resolve this.
+ ufbx_string filename;
+ // Absolute filename specified in the file.
+ ufbx_string absolute_filename;
+ // Relative filename specified in the file.
+ // NOTE: May be absolute if the file is saved in a different drive.
+ ufbx_string relative_filename;
+
+ // Filename relative to the loaded file, non-UTF-8 encoded.
+ // HINT: If using functions other than `ufbx_load_file()`, you can provide
+ // `ufbx_load_opts.filename/raw_filename` to let ufbx resolve this.
+ ufbx_blob raw_filename;
+ // Absolute filename specified in the file, non-UTF-8 encoded.
+ ufbx_blob raw_absolute_filename;
+ // Relative filename specified in the file, non-UTF-8 encoded.
+ // NOTE: May be absolute if the file is saved in a different drive.
+ ufbx_blob raw_relative_filename;
+
+ ufbx_cache_file_format format;
+
+ // Only valid if `ufbx_load_opts.load_external_files` is set!
+ ufbx_nullable ufbx_geometry_cache *external_cache;
+};
+
+// -- Materials
+
+// Material property, either specified with a constant value or a mapped texture
+typedef struct ufbx_material_map {
+
+ // Constant value or factor for the map.
+ // May be specified simultaneously with a texture, in this case most shading models
+ // use multiplicative tinting of the texture values.
+ union {
+ ufbx_real value_real;
+ ufbx_vec2 value_vec2;
+ ufbx_vec3 value_vec3;
+ ufbx_vec4 value_vec4;
+ };
+ int64_t value_int;
+
+ // Texture if connected, otherwise `NULL`.
+ // May be valid but "disabled" (application specific) if `texture_enabled == false`.
+ ufbx_nullable ufbx_texture *texture;
+
+ // `true` if the file has specified any of the values above.
+ // NOTE: The value may be set to a non-zero default even if `has_value == false`,
+ // for example missing factors are set to `1.0` if a color is defined.
+ bool has_value;
+
+ // Controls whether shading should use `texture`.
+ // NOTE: Some shading models allow this to be `true` even if `texture == NULL`.
+ bool texture_enabled;
+
+ // Set to `true` if this feature should be disabled (specific to shader type).
+ bool feature_disabled;
+
+ // Number of components in the value from 1 to 4 if defined, 0 if not.
+ uint8_t value_components;
+
+} ufbx_material_map;
+
+// Material feature
+typedef struct ufbx_material_feature_info {
+
+ // Whether the material model uses this feature or not.
+ // NOTE: The feature can be enabled but still not used if eg. the corresponding factor is at zero!
+ bool enabled;
+
+ // Explicitly enabled/disabled by the material.
+ bool is_explicit;
+
+} ufbx_material_feature_info;
+
+// Texture attached to an FBX property
+typedef struct ufbx_material_texture {
+ ufbx_string material_prop; // < Name of the property in `ufbx_material.props`
+ ufbx_string shader_prop; // < Shader-specific property mapping name
+
+ // Texture attached to the property.
+ ufbx_texture *texture;
+
+} ufbx_material_texture;
+
+UFBX_LIST_TYPE(ufbx_material_texture_list, ufbx_material_texture);
+
+// Shading model type
+typedef enum ufbx_shader_type UFBX_ENUM_REPR {
+ // Unknown shading model
+ UFBX_SHADER_UNKNOWN,
+ // FBX builtin diffuse material
+ UFBX_SHADER_FBX_LAMBERT,
+ // FBX builtin diffuse+specular material
+ UFBX_SHADER_FBX_PHONG,
+ // Open Shading Language standard surface
+ // https://github.com/Autodesk/standard-surface
+ UFBX_SHADER_OSL_STANDARD_SURFACE,
+ // Arnold standard surface
+ // https://docs.arnoldrenderer.com/display/A5AFMUG/Standard+Surface
+ UFBX_SHADER_ARNOLD_STANDARD_SURFACE,
+ // 3ds Max Physical Material
+ // https://knowledge.autodesk.com/support/3ds-max/learn-explore/caas/CloudHelp/cloudhelp/2022/ENU/3DSMax-Lighting-Shading/files/GUID-C1328905-7783-4917-AB86-FC3CC19E8972-htm.html
+ UFBX_SHADER_3DS_MAX_PHYSICAL_MATERIAL,
+ // 3ds Max PBR (Metal/Rough) material
+ // https://knowledge.autodesk.com/support/3ds-max/learn-explore/caas/CloudHelp/cloudhelp/2021/ENU/3DSMax-Lighting-Shading/files/GUID-A16234A5-6500-4662-8B20-A5EC9FE1B255-htm.html
+ UFBX_SHADER_3DS_MAX_PBR_METAL_ROUGH,
+ // 3ds Max PBR (Spec/Gloss) material
+ // https://knowledge.autodesk.com/support/3ds-max/learn-explore/caas/CloudHelp/cloudhelp/2021/ENU/3DSMax-Lighting-Shading/files/GUID-18087194-B2A6-43EF-9B80-8FD1736FAE52-htm.html
+ UFBX_SHADER_3DS_MAX_PBR_SPEC_GLOSS,
+ // 3ds glTF Material
+ // https://help.autodesk.com/view/3DSMAX/2023/ENU/?guid=GUID-7ABFB805-1D9F-417E-9C22-704BFDF160FA
+ UFBX_SHADER_GLTF_MATERIAL,
+ // 3ds OpenPBR Material
+ // https://help.autodesk.com/view/3DSMAX/2025/ENU/?guid=GUID-CD90329C-1E2B-4BBA-9285-3BB46253B9C2
+ UFBX_SHADER_OPENPBR_MATERIAL,
+ // Stingray ShaderFX shader graph.
+ // Contains a serialized `"ShaderGraph"` in `ufbx_props`.
+ UFBX_SHADER_SHADERFX_GRAPH,
+ // Variation of the FBX phong shader that can recover PBR properties like
+ // `metalness` or `roughness` from the FBX non-physical values.
+ // NOTE: Enable `ufbx_load_opts.use_blender_pbr_material`.
+ UFBX_SHADER_BLENDER_PHONG,
+ // Wavefront .mtl format shader (used by .obj files)
+ UFBX_SHADER_WAVEFRONT_MTL,
+
+ UFBX_ENUM_FORCE_WIDTH(UFBX_SHADER_TYPE)
+} ufbx_shader_type;
+
+UFBX_ENUM_TYPE(ufbx_shader_type, UFBX_SHADER_TYPE, UFBX_SHADER_WAVEFRONT_MTL);
+
+// FBX builtin material properties, matches maps in `ufbx_material_fbx_maps`
+typedef enum ufbx_material_fbx_map UFBX_ENUM_REPR {
+ UFBX_MATERIAL_FBX_DIFFUSE_FACTOR,
+ UFBX_MATERIAL_FBX_DIFFUSE_COLOR,
+ UFBX_MATERIAL_FBX_SPECULAR_FACTOR,
+ UFBX_MATERIAL_FBX_SPECULAR_COLOR,
+ UFBX_MATERIAL_FBX_SPECULAR_EXPONENT,
+ UFBX_MATERIAL_FBX_REFLECTION_FACTOR,
+ UFBX_MATERIAL_FBX_REFLECTION_COLOR,
+ UFBX_MATERIAL_FBX_TRANSPARENCY_FACTOR,
+ UFBX_MATERIAL_FBX_TRANSPARENCY_COLOR,
+ UFBX_MATERIAL_FBX_EMISSION_FACTOR,
+ UFBX_MATERIAL_FBX_EMISSION_COLOR,
+ UFBX_MATERIAL_FBX_AMBIENT_FACTOR,
+ UFBX_MATERIAL_FBX_AMBIENT_COLOR,
+ UFBX_MATERIAL_FBX_NORMAL_MAP,
+ UFBX_MATERIAL_FBX_BUMP,
+ UFBX_MATERIAL_FBX_BUMP_FACTOR,
+ UFBX_MATERIAL_FBX_DISPLACEMENT_FACTOR,
+ UFBX_MATERIAL_FBX_DISPLACEMENT,
+ UFBX_MATERIAL_FBX_VECTOR_DISPLACEMENT_FACTOR,
+ UFBX_MATERIAL_FBX_VECTOR_DISPLACEMENT,
+
+ UFBX_ENUM_FORCE_WIDTH(UFBX_MATERIAL_FBX_MAP)
+} ufbx_material_fbx_map;
+
+UFBX_ENUM_TYPE(ufbx_material_fbx_map, UFBX_MATERIAL_FBX_MAP, UFBX_MATERIAL_FBX_VECTOR_DISPLACEMENT);
+
+// Known PBR material properties, matches maps in `ufbx_material_pbr_maps`
+typedef enum ufbx_material_pbr_map UFBX_ENUM_REPR {
+ UFBX_MATERIAL_PBR_BASE_FACTOR,
+ UFBX_MATERIAL_PBR_BASE_COLOR,
+ UFBX_MATERIAL_PBR_ROUGHNESS,
+ UFBX_MATERIAL_PBR_METALNESS,
+ UFBX_MATERIAL_PBR_DIFFUSE_ROUGHNESS,
+ UFBX_MATERIAL_PBR_SPECULAR_FACTOR,
+ UFBX_MATERIAL_PBR_SPECULAR_COLOR,
+ UFBX_MATERIAL_PBR_SPECULAR_IOR,
+ UFBX_MATERIAL_PBR_SPECULAR_ANISOTROPY,
+ UFBX_MATERIAL_PBR_SPECULAR_ROTATION,
+ UFBX_MATERIAL_PBR_TRANSMISSION_FACTOR,
+ UFBX_MATERIAL_PBR_TRANSMISSION_COLOR,
+ UFBX_MATERIAL_PBR_TRANSMISSION_DEPTH,
+ UFBX_MATERIAL_PBR_TRANSMISSION_SCATTER,
+ UFBX_MATERIAL_PBR_TRANSMISSION_SCATTER_ANISOTROPY,
+ UFBX_MATERIAL_PBR_TRANSMISSION_DISPERSION,
+ UFBX_MATERIAL_PBR_TRANSMISSION_ROUGHNESS,
+ UFBX_MATERIAL_PBR_TRANSMISSION_EXTRA_ROUGHNESS,
+ UFBX_MATERIAL_PBR_TRANSMISSION_PRIORITY,
+ UFBX_MATERIAL_PBR_TRANSMISSION_ENABLE_IN_AOV,
+ UFBX_MATERIAL_PBR_SUBSURFACE_FACTOR,
+ UFBX_MATERIAL_PBR_SUBSURFACE_COLOR,
+ UFBX_MATERIAL_PBR_SUBSURFACE_RADIUS,
+ UFBX_MATERIAL_PBR_SUBSURFACE_SCALE,
+ UFBX_MATERIAL_PBR_SUBSURFACE_ANISOTROPY,
+ UFBX_MATERIAL_PBR_SUBSURFACE_TINT_COLOR,
+ UFBX_MATERIAL_PBR_SUBSURFACE_TYPE,
+ UFBX_MATERIAL_PBR_SHEEN_FACTOR,
+ UFBX_MATERIAL_PBR_SHEEN_COLOR,
+ UFBX_MATERIAL_PBR_SHEEN_ROUGHNESS,
+ UFBX_MATERIAL_PBR_COAT_FACTOR,
+ UFBX_MATERIAL_PBR_COAT_COLOR,
+ UFBX_MATERIAL_PBR_COAT_ROUGHNESS,
+ UFBX_MATERIAL_PBR_COAT_IOR,
+ UFBX_MATERIAL_PBR_COAT_ANISOTROPY,
+ UFBX_MATERIAL_PBR_COAT_ROTATION,
+ UFBX_MATERIAL_PBR_COAT_NORMAL,
+ UFBX_MATERIAL_PBR_COAT_AFFECT_BASE_COLOR,
+ UFBX_MATERIAL_PBR_COAT_AFFECT_BASE_ROUGHNESS,
+ UFBX_MATERIAL_PBR_THIN_FILM_FACTOR,
+ UFBX_MATERIAL_PBR_THIN_FILM_THICKNESS,
+ UFBX_MATERIAL_PBR_THIN_FILM_IOR,
+ UFBX_MATERIAL_PBR_EMISSION_FACTOR,
+ UFBX_MATERIAL_PBR_EMISSION_COLOR,
+ UFBX_MATERIAL_PBR_OPACITY,
+ UFBX_MATERIAL_PBR_INDIRECT_DIFFUSE,
+ UFBX_MATERIAL_PBR_INDIRECT_SPECULAR,
+ UFBX_MATERIAL_PBR_NORMAL_MAP,
+ UFBX_MATERIAL_PBR_TANGENT_MAP,
+ UFBX_MATERIAL_PBR_DISPLACEMENT_MAP,
+ UFBX_MATERIAL_PBR_MATTE_FACTOR,
+ UFBX_MATERIAL_PBR_MATTE_COLOR,
+ UFBX_MATERIAL_PBR_AMBIENT_OCCLUSION,
+ UFBX_MATERIAL_PBR_GLOSSINESS,
+ UFBX_MATERIAL_PBR_COAT_GLOSSINESS,
+ UFBX_MATERIAL_PBR_TRANSMISSION_GLOSSINESS,
+
+ UFBX_ENUM_FORCE_WIDTH(UFBX_MATERIAL_PBR_MAP)
+} ufbx_material_pbr_map;
+
+UFBX_ENUM_TYPE(ufbx_material_pbr_map, UFBX_MATERIAL_PBR_MAP, UFBX_MATERIAL_PBR_TRANSMISSION_GLOSSINESS);
+
+// Known material features
+typedef enum ufbx_material_feature UFBX_ENUM_REPR {
+ UFBX_MATERIAL_FEATURE_PBR,
+ UFBX_MATERIAL_FEATURE_METALNESS,
+ UFBX_MATERIAL_FEATURE_DIFFUSE,
+ UFBX_MATERIAL_FEATURE_SPECULAR,
+ UFBX_MATERIAL_FEATURE_EMISSION,
+ UFBX_MATERIAL_FEATURE_TRANSMISSION,
+ UFBX_MATERIAL_FEATURE_COAT,
+ UFBX_MATERIAL_FEATURE_SHEEN,
+ UFBX_MATERIAL_FEATURE_OPACITY,
+ UFBX_MATERIAL_FEATURE_AMBIENT_OCCLUSION,
+ UFBX_MATERIAL_FEATURE_MATTE,
+ UFBX_MATERIAL_FEATURE_UNLIT,
+ UFBX_MATERIAL_FEATURE_IOR,
+ UFBX_MATERIAL_FEATURE_DIFFUSE_ROUGHNESS,
+ UFBX_MATERIAL_FEATURE_TRANSMISSION_ROUGHNESS,
+ UFBX_MATERIAL_FEATURE_THIN_WALLED,
+ UFBX_MATERIAL_FEATURE_CAUSTICS,
+ UFBX_MATERIAL_FEATURE_EXIT_TO_BACKGROUND,
+ UFBX_MATERIAL_FEATURE_INTERNAL_REFLECTIONS,
+ UFBX_MATERIAL_FEATURE_DOUBLE_SIDED,
+ UFBX_MATERIAL_FEATURE_ROUGHNESS_AS_GLOSSINESS,
+ UFBX_MATERIAL_FEATURE_COAT_ROUGHNESS_AS_GLOSSINESS,
+ UFBX_MATERIAL_FEATURE_TRANSMISSION_ROUGHNESS_AS_GLOSSINESS,
+
+ UFBX_ENUM_FORCE_WIDTH(UFBX_MATERIAL_FEATURE)
+} ufbx_material_feature;
+
+UFBX_ENUM_TYPE(ufbx_material_feature, UFBX_MATERIAL_FEATURE, UFBX_MATERIAL_FEATURE_TRANSMISSION_ROUGHNESS_AS_GLOSSINESS);
+
+typedef struct ufbx_material_fbx_maps {
+ union {
+ ufbx_material_map maps[UFBX_MATERIAL_FBX_MAP_COUNT];
+ struct {
+ ufbx_material_map diffuse_factor;
+ ufbx_material_map diffuse_color;
+ ufbx_material_map specular_factor;
+ ufbx_material_map specular_color;
+ ufbx_material_map specular_exponent;
+ ufbx_material_map reflection_factor;
+ ufbx_material_map reflection_color;
+ ufbx_material_map transparency_factor;
+ ufbx_material_map transparency_color;
+ ufbx_material_map emission_factor;
+ ufbx_material_map emission_color;
+ ufbx_material_map ambient_factor;
+ ufbx_material_map ambient_color;
+ ufbx_material_map normal_map;
+ ufbx_material_map bump;
+ ufbx_material_map bump_factor;
+ ufbx_material_map displacement_factor;
+ ufbx_material_map displacement;
+ ufbx_material_map vector_displacement_factor;
+ ufbx_material_map vector_displacement;
+ };
+ };
+} ufbx_material_fbx_maps;
+
+typedef struct ufbx_material_pbr_maps {
+ union {
+ ufbx_material_map maps[UFBX_MATERIAL_PBR_MAP_COUNT];
+ struct {
+ ufbx_material_map base_factor;
+ ufbx_material_map base_color;
+ ufbx_material_map roughness;
+ ufbx_material_map metalness;
+ ufbx_material_map diffuse_roughness;
+ ufbx_material_map specular_factor;
+ ufbx_material_map specular_color;
+ ufbx_material_map specular_ior;
+ ufbx_material_map specular_anisotropy;
+ ufbx_material_map specular_rotation;
+ ufbx_material_map transmission_factor;
+ ufbx_material_map transmission_color;
+ ufbx_material_map transmission_depth;
+ ufbx_material_map transmission_scatter;
+ ufbx_material_map transmission_scatter_anisotropy;
+ ufbx_material_map transmission_dispersion;
+ ufbx_material_map transmission_roughness;
+ ufbx_material_map transmission_extra_roughness;
+ ufbx_material_map transmission_priority;
+ ufbx_material_map transmission_enable_in_aov;
+ ufbx_material_map subsurface_factor;
+ ufbx_material_map subsurface_color;
+ ufbx_material_map subsurface_radius;
+ ufbx_material_map subsurface_scale;
+ ufbx_material_map subsurface_anisotropy;
+ ufbx_material_map subsurface_tint_color;
+ ufbx_material_map subsurface_type;
+ ufbx_material_map sheen_factor;
+ ufbx_material_map sheen_color;
+ ufbx_material_map sheen_roughness;
+ ufbx_material_map coat_factor;
+ ufbx_material_map coat_color;
+ ufbx_material_map coat_roughness;
+ ufbx_material_map coat_ior;
+ ufbx_material_map coat_anisotropy;
+ ufbx_material_map coat_rotation;
+ ufbx_material_map coat_normal;
+ ufbx_material_map coat_affect_base_color;
+ ufbx_material_map coat_affect_base_roughness;
+ ufbx_material_map thin_film_factor;
+ ufbx_material_map thin_film_thickness;
+ ufbx_material_map thin_film_ior;
+ ufbx_material_map emission_factor;
+ ufbx_material_map emission_color;
+ ufbx_material_map opacity;
+ ufbx_material_map indirect_diffuse;
+ ufbx_material_map indirect_specular;
+ ufbx_material_map normal_map;
+ ufbx_material_map tangent_map;
+ ufbx_material_map displacement_map;
+ ufbx_material_map matte_factor;
+ ufbx_material_map matte_color;
+ ufbx_material_map ambient_occlusion;
+ ufbx_material_map glossiness;
+ ufbx_material_map coat_glossiness;
+ ufbx_material_map transmission_glossiness;
+ };
+ };
+} ufbx_material_pbr_maps;
+
+typedef struct ufbx_material_features {
+ union {
+ ufbx_material_feature_info features[UFBX_MATERIAL_FEATURE_COUNT];
+ struct {
+ ufbx_material_feature_info pbr;
+ ufbx_material_feature_info metalness;
+ ufbx_material_feature_info diffuse;
+ ufbx_material_feature_info specular;
+ ufbx_material_feature_info emission;
+ ufbx_material_feature_info transmission;
+ ufbx_material_feature_info coat;
+ ufbx_material_feature_info sheen;
+ ufbx_material_feature_info opacity;
+ ufbx_material_feature_info ambient_occlusion;
+ ufbx_material_feature_info matte;
+ ufbx_material_feature_info unlit;
+ ufbx_material_feature_info ior;
+ ufbx_material_feature_info diffuse_roughness;
+ ufbx_material_feature_info transmission_roughness;
+ ufbx_material_feature_info thin_walled;
+ ufbx_material_feature_info caustics;
+ ufbx_material_feature_info exit_to_background;
+ ufbx_material_feature_info internal_reflections;
+ ufbx_material_feature_info double_sided;
+ ufbx_material_feature_info roughness_as_glossiness;
+ ufbx_material_feature_info coat_roughness_as_glossiness;
+ ufbx_material_feature_info transmission_roughness_as_glossiness;
+ };
+ };
+} ufbx_material_features;
+
+// Surface material properties such as color, roughness, etc. Each property may
+// be optionally bound to an `ufbx_texture`.
+struct ufbx_material {
+ union { ufbx_element element; struct {
+ ufbx_string name;
+ ufbx_props props;
+ uint32_t element_id;
+ uint32_t typed_id;
+ }; };
+
+ // FBX builtin properties
+ // NOTE: These may be empty if the material is using a custom shader
+ ufbx_material_fbx_maps fbx;
+
+ // PBR material properties, defined for all shading models but may be
+ // somewhat approximate if `shader == NULL`.
+ ufbx_material_pbr_maps pbr;
+
+ // Material features, primarily applies to `pbr`.
+ ufbx_material_features features;
+
+ // Shading information
+ ufbx_shader_type shader_type; // < Always defined
+ ufbx_nullable ufbx_shader *shader; // < Optional extended shader information
+ ufbx_string shading_model_name; // < Often one of `{ "lambert", "phong", "unknown" }`
+
+ // Prefix before shader property names with trailing `|`.
+ // For example `"3dsMax|Parameters|"` where properties would have names like
+ // `"3dsMax|Parameters|base_color"`. You can ignore this if you use the built-in
+ // `ufbx_material_fbx_maps fbx` and `ufbx_material_pbr_maps pbr` structures.
+ ufbx_string shader_prop_prefix;
+
+ // All textures attached to the material, if you want specific maps if might be
+ // more convenient to use eg. `fbx.diffuse_color.texture` or `pbr.base_color.texture`
+ ufbx_material_texture_list textures; // < Sorted by `material_prop`
+};
+
+typedef enum ufbx_texture_type UFBX_ENUM_REPR {
+
+ // Texture associated with an image file/sequence. `texture->filename` and
+ // and `texture->relative_filename` contain the texture's path. If the file
+ // has embedded content `texture->content` may hold `texture->content_size`
+ // bytes of raw image data.
+ UFBX_TEXTURE_FILE,
+
+ // The texture consists of multiple texture layers blended together.
+ UFBX_TEXTURE_LAYERED,
+
+ // Reserved as these _should_ exist in FBX files.
+ UFBX_TEXTURE_PROCEDURAL,
+
+ // Node in a shader graph.
+ // Use `ufbx_texture.shader` for more information.
+ UFBX_TEXTURE_SHADER,
+
+ UFBX_ENUM_FORCE_WIDTH(UFBX_TEXTURE_TYPE)
+} ufbx_texture_type;
+
+UFBX_ENUM_TYPE(ufbx_texture_type, UFBX_TEXTURE_TYPE, UFBX_TEXTURE_SHADER);
+
+// Blend modes to combine layered textures with, compatible with common blend
+// mode definitions in many art programs. Simpler blend modes have equations
+// specified below where `src` is the layer to composite over `dst`.
+// See eg. https://www.w3.org/TR/2013/WD-compositing-1-20131010/#blendingseparable
+typedef enum ufbx_blend_mode UFBX_ENUM_REPR {
+ UFBX_BLEND_TRANSLUCENT, // < `src` effects result alpha
+ UFBX_BLEND_ADDITIVE, // < `src + dst`
+ UFBX_BLEND_MULTIPLY, // < `src * dst`
+ UFBX_BLEND_MULTIPLY_2X, // < `2 * src * dst`
+ UFBX_BLEND_OVER, // < `src * src_alpha + dst * (1-src_alpha)`
+ UFBX_BLEND_REPLACE, // < `src` Replace the contents
+ UFBX_BLEND_DISSOLVE, // < `random() + src_alpha >= 1.0 ? src : dst`
+ UFBX_BLEND_DARKEN, // < `min(src, dst)`
+ UFBX_BLEND_COLOR_BURN, // < `src > 0 ? 1 - min(1, (1-dst) / src) : 0`
+ UFBX_BLEND_LINEAR_BURN, // < `src + dst - 1`
+ UFBX_BLEND_DARKER_COLOR, // < `value(src) < value(dst) ? src : dst`
+ UFBX_BLEND_LIGHTEN, // < `max(src, dst)`
+ UFBX_BLEND_SCREEN, // < `1 - (1-src)*(1-dst)`
+ UFBX_BLEND_COLOR_DODGE, // < `src < 1 ? dst / (1 - src)` : (dst>0?1:0)`
+ UFBX_BLEND_LINEAR_DODGE, // < `src + dst`
+ UFBX_BLEND_LIGHTER_COLOR, // < `value(src) > value(dst) ? src : dst`
+ UFBX_BLEND_SOFT_LIGHT, // < https://www.w3.org/TR/2013/WD-compositing-1-20131010/#blendingsoftlight
+ UFBX_BLEND_HARD_LIGHT, // < https://www.w3.org/TR/2013/WD-compositing-1-20131010/#blendinghardlight
+ UFBX_BLEND_VIVID_LIGHT, // < Combination of `COLOR_DODGE` and `COLOR_BURN`
+ UFBX_BLEND_LINEAR_LIGHT, // < Combination of `LINEAR_DODGE` and `LINEAR_BURN`
+ UFBX_BLEND_PIN_LIGHT, // < Combination of `DARKEN` and `LIGHTEN`
+ UFBX_BLEND_HARD_MIX, // < Produces primary colors depending on similarity
+ UFBX_BLEND_DIFFERENCE, // < `abs(src - dst)`
+ UFBX_BLEND_EXCLUSION, // < `dst + src - 2 * src * dst`
+ UFBX_BLEND_SUBTRACT, // < `dst - src`
+ UFBX_BLEND_DIVIDE, // < `dst / src`
+ UFBX_BLEND_HUE, // < Replace hue
+ UFBX_BLEND_SATURATION, // < Replace saturation
+ UFBX_BLEND_COLOR, // < Replace hue and saturatio
+ UFBX_BLEND_LUMINOSITY, // < Replace value
+ UFBX_BLEND_OVERLAY, // < Same as `HARD_LIGHT` but with `src` and `dst` swapped
+
+ UFBX_ENUM_FORCE_WIDTH(UFBX_BLEND_MODE)
+} ufbx_blend_mode;
+
+UFBX_ENUM_TYPE(ufbx_blend_mode, UFBX_BLEND_MODE, UFBX_BLEND_OVERLAY);
+
+// Blend modes to combine layered textures with, compatible with common blend
+typedef enum ufbx_wrap_mode UFBX_ENUM_REPR {
+ UFBX_WRAP_REPEAT, // < Repeat the texture past the [0,1] range
+ UFBX_WRAP_CLAMP, // < Clamp the normalized texture coordinates to [0,1]
+
+ UFBX_ENUM_FORCE_WIDTH(UFBX_WRAP_MODE)
+} ufbx_wrap_mode;
+
+UFBX_ENUM_TYPE(ufbx_wrap_mode, UFBX_WRAP_MODE, UFBX_WRAP_CLAMP);
+
+// Single layer in a layered texture
+typedef struct ufbx_texture_layer {
+ ufbx_texture *texture; // < The inner texture to evaluate, never `NULL`
+ ufbx_blend_mode blend_mode; // < Equation to combine the layer to the background
+ ufbx_real alpha; // < Blend weight of this layer
+} ufbx_texture_layer;
+
+UFBX_LIST_TYPE(ufbx_texture_layer_list, ufbx_texture_layer);
+
+typedef enum ufbx_shader_texture_type UFBX_ENUM_REPR {
+ UFBX_SHADER_TEXTURE_UNKNOWN,
+
+ // Select an output of a multi-output shader.
+ // HINT: If this type is used the `ufbx_shader_texture.main_texture` and
+ // `ufbx_shader_texture.main_texture_output_index` fields are set.
+ UFBX_SHADER_TEXTURE_SELECT_OUTPUT,
+
+ // Open Shading Language (OSL) shader.
+ // https://github.com/AcademySoftwareFoundation/OpenShadingLanguage
+ UFBX_SHADER_TEXTURE_OSL,
+
+ UFBX_ENUM_FORCE_WIDTH(UFBX_SHADER_TEXTURE_TYPE)
+} ufbx_shader_texture_type;
+
+UFBX_ENUM_TYPE(ufbx_shader_texture_type, UFBX_SHADER_TEXTURE_TYPE, UFBX_SHADER_TEXTURE_OSL);
+
+// Input to a shader texture, see `ufbx_shader_texture`.
+typedef struct ufbx_shader_texture_input {
+
+ // Name of the input.
+ ufbx_string name;
+
+ // Constant value of the input.
+ union {
+ ufbx_real value_real;
+ ufbx_vec2 value_vec2;
+ ufbx_vec3 value_vec3;
+ ufbx_vec4 value_vec4;
+ };
+ int64_t value_int;
+ ufbx_string value_str;
+ ufbx_blob value_blob;
+
+ // Texture connected to this input.
+ ufbx_nullable ufbx_texture *texture;
+
+ // Index of the output to use if `texture` is a multi-output shader node.
+ int64_t texture_output_index;
+
+ // Controls whether shading should use `texture`.
+ // NOTE: Some shading models allow this to be `true` even if `texture == NULL`.
+ bool texture_enabled;
+
+ // Property representing this input.
+ ufbx_prop *prop;
+
+ // Property representing `texture`.
+ ufbx_nullable ufbx_prop *texture_prop;
+
+ // Property representing `texture_enabled`.
+ ufbx_nullable ufbx_prop *texture_enabled_prop;
+
+} ufbx_shader_texture_input;
+
+UFBX_LIST_TYPE(ufbx_shader_texture_input_list, ufbx_shader_texture_input);
+
+// Texture that emulates a shader graph node.
+// 3ds Max exports some materials as node graphs serialized to textures.
+// ufbx can parse a small subset of these, as normal maps are often hidden behind
+// some kind of bump node.
+// NOTE: These encode a lot of details of 3ds Max internals, not recommended for direct use.
+// HINT: `ufbx_texture.file_textures[]` contains a list of "real" textures that are connected
+// to the `ufbx_texture` that is pretending to be a shader node.
+typedef struct ufbx_shader_texture {
+
+ // Type of this shader node.
+ ufbx_shader_texture_type type;
+
+ // Name of the shader to use.
+ ufbx_string shader_name;
+
+ // 64-bit opaque identifier for the shader type.
+ uint64_t shader_type_id;
+
+ // Input values/textures (possibly further shader textures) to the shader.
+ // Sorted by `ufbx_shader_texture_input.name`.
+ ufbx_shader_texture_input_list inputs;
+
+ // Shader source code if found.
+ ufbx_string shader_source;
+ ufbx_blob raw_shader_source;
+
+ // Representative texture for this shader.
+ // Only specified if `main_texture.outputs[main_texture_output_index]` is semantically
+ // equivalent to this texture.
+ ufbx_texture *main_texture;
+
+ // Output index of `main_texture` if it is a multi-output shader.
+ int64_t main_texture_output_index;
+
+ // Prefix for properties related to this shader in `ufbx_texture`.
+ // NOTE: Contains the trailing '|' if not empty.
+ ufbx_string prop_prefix;
+
+} ufbx_shader_texture;
+
+// Unique texture within the file.
+typedef struct ufbx_texture_file {
+
+ // Index in `ufbx_scene.texture_files[]`.
+ uint32_t index;
+
+ // Paths to the resource.
+
+ // Filename relative to the currently loaded file.
+ // HINT: If using functions other than `ufbx_load_file()`, you can provide
+ // `ufbx_load_opts.filename/raw_filename` to let ufbx resolve this.
+ ufbx_string filename;
+ // Absolute filename specified in the file.
+ ufbx_string absolute_filename;
+ // Relative filename specified in the file.
+ // NOTE: May be absolute if the file is saved in a different drive.
+ ufbx_string relative_filename;
+
+ // Filename relative to the loaded file, non-UTF-8 encoded.
+ // HINT: If using functions other than `ufbx_load_file()`, you can provide
+ // `ufbx_load_opts.filename/raw_filename` to let ufbx resolve this.
+ ufbx_blob raw_filename;
+ // Absolute filename specified in the file, non-UTF-8 encoded.
+ ufbx_blob raw_absolute_filename;
+ // Relative filename specified in the file, non-UTF-8 encoded.
+ // NOTE: May be absolute if the file is saved in a different drive.
+ ufbx_blob raw_relative_filename;
+
+ // Optional embedded content blob, eg. raw .png format data
+ ufbx_blob content;
+
+} ufbx_texture_file;
+
+UFBX_LIST_TYPE(ufbx_texture_file_list, ufbx_texture_file);
+
+// Texture that controls material appearance
+struct ufbx_texture {
+ union { ufbx_element element; struct {
+ ufbx_string name;
+ ufbx_props props;
+ uint32_t element_id;
+ uint32_t typed_id;
+ }; };
+
+ // Texture type (file / layered / procedural / shader)
+ ufbx_texture_type type;
+
+ // FILE: Paths to the resource
+
+ // Filename relative to the currently loaded file.
+ // HINT: If using functions other than `ufbx_load_file()`, you can provide
+ // `ufbx_load_opts.filename/raw_filename` to let ufbx resolve this.
+ ufbx_string filename;
+ // Absolute filename specified in the file.
+ ufbx_string absolute_filename;
+ // Relative filename specified in the file.
+ // NOTE: May be absolute if the file is saved in a different drive.
+ ufbx_string relative_filename;
+
+ // Filename relative to the loaded file, non-UTF-8 encoded.
+ // HINT: If using functions other than `ufbx_load_file()`, you can provide
+ // `ufbx_load_opts.filename/raw_filename` to let ufbx resolve this.
+ ufbx_blob raw_filename;
+ // Absolute filename specified in the file, non-UTF-8 encoded.
+ ufbx_blob raw_absolute_filename;
+ // Relative filename specified in the file, non-UTF-8 encoded.
+ // NOTE: May be absolute if the file is saved in a different drive.
+ ufbx_blob raw_relative_filename;
+
+ // FILE: Optional embedded content blob, eg. raw .png format data
+ ufbx_blob content;
+
+ // FILE: Optional video texture
+ ufbx_nullable ufbx_video *video;
+
+ // FILE: Index into `ufbx_scene.texture_files[]` or `UFBX_NO_INDEX`.
+ uint32_t file_index;
+
+ // FILE: True if `file_index` has a valid value.
+ bool has_file;
+
+ // LAYERED: Inner texture layers, ordered from _bottom_ to _top_
+ ufbx_texture_layer_list layers;
+
+ // SHADER: Shader information
+ // NOTE: May be specified even if `type == UFBX_TEXTURE_FILE` if `ufbx_load_opts.disable_quirks`
+ // is _not_ specified. Some known shaders that represent files are interpreted as `UFBX_TEXTURE_FILE`.
+ ufbx_nullable ufbx_shader_texture *shader;
+
+ // List of file textures representing this texture.
+ // Defined even if `type == UFBX_TEXTURE_FILE` in which case the array contains only itself.
+ ufbx_texture_list file_textures;
+
+ // Name of the UV set to use
+ ufbx_string uv_set;
+
+ // Wrapping mode
+ ufbx_wrap_mode wrap_u;
+ ufbx_wrap_mode wrap_v;
+
+ // UV transform
+ bool has_uv_transform; // < Has a non-identity `transform` and derived matrices.
+ ufbx_transform uv_transform; // < Texture transformation in UV space
+ ufbx_matrix texture_to_uv; // < Matrix representation of `transform`
+ ufbx_matrix uv_to_texture; // < UV coordinate to normalized texture coordinate matrix
+};
+
+// TODO: Video textures
+struct ufbx_video {
+ union { ufbx_element element; struct {
+ ufbx_string name;
+ ufbx_props props;
+ uint32_t element_id;
+ uint32_t typed_id;
+ }; };
+
+ // Paths to the resource
+
+ // Filename relative to the currently loaded file.
+ // HINT: If using functions other than `ufbx_load_file()`, you can provide
+ // `ufbx_load_opts.filename/raw_filename` to let ufbx resolve this.
+ ufbx_string filename;
+ // Absolute filename specified in the file.
+ ufbx_string absolute_filename;
+ // Relative filename specified in the file.
+ // NOTE: May be absolute if the file is saved in a different drive.
+ ufbx_string relative_filename;
+
+ // Filename relative to the loaded file, non-UTF-8 encoded.
+ // HINT: If using functions other than `ufbx_load_file()`, you can provide
+ // `ufbx_load_opts.filename/raw_filename` to let ufbx resolve this.
+ ufbx_blob raw_filename;
+ // Absolute filename specified in the file, non-UTF-8 encoded.
+ ufbx_blob raw_absolute_filename;
+ // Relative filename specified in the file, non-UTF-8 encoded.
+ // NOTE: May be absolute if the file is saved in a different drive.
+ ufbx_blob raw_relative_filename;
+
+ // Optional embedded content blob
+ ufbx_blob content;
+};
+
+// Shader specifies a shading model and contains `ufbx_shader_binding` elements
+// that define how to interpret FBX properties in the shader.
+struct ufbx_shader {
+ union { ufbx_element element; struct {
+ ufbx_string name;
+ ufbx_props props;
+ uint32_t element_id;
+ uint32_t typed_id;
+ }; };
+
+ // Known shading model
+ ufbx_shader_type type;
+
+ // TODO: Expose actual properties here
+
+ // Bindings from FBX properties to the shader
+ // HINT: `ufbx_find_shader_prop()` translates shader properties to FBX properties
+ ufbx_shader_binding_list bindings;
+};
+
+// Binding from a material property to shader implementation
+typedef struct ufbx_shader_prop_binding {
+ ufbx_string shader_prop; // < Property name used by the shader implementation
+ ufbx_string material_prop; // < Property name inside `ufbx_material.props`
+} ufbx_shader_prop_binding;
+
+UFBX_LIST_TYPE(ufbx_shader_prop_binding_list, ufbx_shader_prop_binding);
+
+// Shader binding table
+struct ufbx_shader_binding {
+ union { ufbx_element element; struct {
+ ufbx_string name;
+ ufbx_props props;
+ uint32_t element_id;
+ uint32_t typed_id;
+ }; };
+
+ ufbx_shader_prop_binding_list prop_bindings; // < Sorted by `shader_prop`
+};
+
+// -- Animation
+
+typedef struct ufbx_prop_override {
+ uint32_t element_id;
+
+ uint32_t _internal_key;
+
+ ufbx_string prop_name;
+ ufbx_vec4 value;
+ ufbx_string value_str;
+ int64_t value_int;
+} ufbx_prop_override;
+
+UFBX_LIST_TYPE(ufbx_prop_override_list, ufbx_prop_override);
+
+typedef struct ufbx_transform_override {
+ uint32_t node_id;
+ ufbx_transform transform;
+} ufbx_transform_override;
+
+UFBX_LIST_TYPE(ufbx_transform_override_list, ufbx_transform_override);
+
+// Animation descriptor used for evaluating animation.
+// Usually obtained from `ufbx_scene` via either global animation `ufbx_scene.anim`,
+// per-stack animation `ufbx_anim_stack.anim` or per-layer animation `ufbx_anim_layer.anim`.
+//
+// For advanced usage you can use `ufbx_create_anim()` to create animation descriptors
+// with custom layers, property overrides, special flags, etc.
+typedef struct ufbx_anim {
+
+ // Time begin/end for the animation, both may be zero if absent.
+ double time_begin;
+ double time_end;
+
+ // List of layers in the animation.
+ ufbx_anim_layer_list layers;
+
+ // Optional overrides for weights for each layer in `layers[]`.
+ ufbx_real_list override_layer_weights;
+
+ // Sorted by `element_id, prop_name`
+ ufbx_prop_override_list prop_overrides;
+
+ // Sorted by `node_id`
+ ufbx_transform_override_list transform_overrides;
+
+ // Evaluate connected properties as if they would not be connected.
+ bool ignore_connections;
+
+ // Custom `ufbx_anim` created by `ufbx_create_anim()`.
+ bool custom;
+
+} ufbx_anim;
+
+struct ufbx_anim_stack {
+ union { ufbx_element element; struct {
+ ufbx_string name;
+ ufbx_props props;
+ uint32_t element_id;
+ uint32_t typed_id;
+ }; };
+
+ double time_begin;
+ double time_end;
+
+ ufbx_anim_layer_list layers;
+ ufbx_anim *anim;
+};
+
+typedef struct ufbx_anim_prop {
+ ufbx_element *element;
+
+ uint32_t _internal_key;
+
+ ufbx_string prop_name;
+ ufbx_anim_value *anim_value;
+} ufbx_anim_prop;
+
+UFBX_LIST_TYPE(ufbx_anim_prop_list, ufbx_anim_prop);
+
+struct ufbx_anim_layer {
+ union { ufbx_element element; struct {
+ ufbx_string name;
+ ufbx_props props;
+ uint32_t element_id;
+ uint32_t typed_id;
+ }; };
+
+ ufbx_real weight;
+ bool weight_is_animated;
+ bool blended;
+ bool additive;
+ bool compose_rotation;
+ bool compose_scale;
+
+ ufbx_anim_value_list anim_values;
+ ufbx_anim_prop_list anim_props; // < Sorted by `element,prop_name`
+
+ ufbx_anim *anim;
+
+ uint32_t _min_element_id;
+ uint32_t _max_element_id;
+ uint32_t _element_id_bitmask[4];
+};
+
+struct ufbx_anim_value {
+ union { ufbx_element element; struct {
+ ufbx_string name;
+ ufbx_props props;
+ uint32_t element_id;
+ uint32_t typed_id;
+ }; };
+
+ ufbx_vec3 default_value;
+ ufbx_nullable ufbx_anim_curve *curves[3];
+};
+
+// Animation curve segment interpolation mode between two keyframes
+typedef enum ufbx_interpolation UFBX_ENUM_REPR {
+ UFBX_INTERPOLATION_CONSTANT_PREV, // < Hold previous key value
+ UFBX_INTERPOLATION_CONSTANT_NEXT, // < Hold next key value
+ UFBX_INTERPOLATION_LINEAR, // < Linear interpolation between two keys
+ UFBX_INTERPOLATION_CUBIC, // < Cubic interpolation, see `ufbx_tangent`
+
+ UFBX_ENUM_FORCE_WIDTH(UFBX_INTERPOLATION)
+} ufbx_interpolation;
+
+UFBX_ENUM_TYPE(ufbx_interpolation, UFBX_INTERPOLATION, UFBX_INTERPOLATION_CUBIC);
+
+typedef enum ufbx_extrapolation_mode UFBX_ENUM_REPR {
+ UFBX_EXTRAPOLATION_CONSTANT, // < Use the value of the first/last keyframe
+ UFBX_EXTRAPOLATION_REPEAT, // < Repeat the whole animation curve
+ UFBX_EXTRAPOLATION_MIRROR, // < Repeat with mirroring
+ UFBX_EXTRAPOLATION_SLOPE, // < Use the tangent of the last keyframe to linearly extrapolate
+ UFBX_EXTRAPOLATION_REPEAT_RELATIVE, // < Repeat the animation curve but connect the first and last keyframe values
+
+ UFBX_ENUM_FORCE_WIDTH(UFBX_EXTRAPOLATION)
+} ufbx_extrapolation_mode;
+
+UFBX_ENUM_TYPE(ufbx_extrapolation_mode, UFBX_EXTRAPOLATION_MODE, UFBX_EXTRAPOLATION_REPEAT_RELATIVE);
+
+typedef struct ufbx_extrapolation {
+ ufbx_extrapolation_mode mode;
+
+ // Count used for repeating modes.
+ // Negative values mean infinite repetition.
+ int32_t repeat_count;
+} ufbx_extrapolation;
+
+// Tangent vector at a keyframe, may be split into left/right
+typedef struct ufbx_tangent {
+ float dx; // < Derivative in the time axis
+ float dy; // < Derivative in the (curve specific) value axis
+} ufbx_tangent;
+
+// Single real `value` at a specified `time`, interpolation between two keyframes
+// is determined by the `interpolation` field of the _previous_ key.
+// If `interpolation == UFBX_INTERPOLATION_CUBIC` the span is evaluated as a
+// cubic bezier curve through the following points:
+//
+// (prev->time, prev->value)
+// (prev->time + prev->right.dx, prev->value + prev->right.dy)
+// (next->time - next->left.dx, next->value - next->left.dy)
+// (next->time, next->value)
+//
+// HINT: You can use `ufbx_evaluate_curve(ufbx_anim_curve *curve, double time)`
+// rather than trying to manually handle all the interpolation modes.
+typedef struct ufbx_keyframe {
+ double time;
+ ufbx_real value;
+ ufbx_interpolation interpolation;
+ ufbx_tangent left;
+ ufbx_tangent right;
+} ufbx_keyframe;
+
+UFBX_LIST_TYPE(ufbx_keyframe_list, ufbx_keyframe);
+
+struct ufbx_anim_curve {
+ union { ufbx_element element; struct {
+ ufbx_string name;
+ ufbx_props props;
+ uint32_t element_id;
+ uint32_t typed_id;
+ }; };
+
+ // List of keyframes that define the curve.
+ ufbx_keyframe_list keyframes;
+
+ // Extrapolation before the curve.
+ ufbx_extrapolation pre_extrapolation;
+ // Extrapolation after the curve.
+ ufbx_extrapolation post_extrapolation;
+
+ // Value range for all the keyframes.
+ ufbx_real min_value;
+ ufbx_real max_value;
+
+ // Time range for all the keyframes.
+ double min_time;
+ double max_time;
+};
+
+// -- Collections
+
+// Collection of nodes to hide/freeze
+struct ufbx_display_layer {
+ union { ufbx_element element; struct {
+ ufbx_string name;
+ ufbx_props props;
+ uint32_t element_id;
+ uint32_t typed_id;
+ }; };
+
+ // Nodes included in the layer (exclusively at most one layer per node)
+ ufbx_node_list nodes;
+
+ // Layer state
+ bool visible; // < Contained nodes are visible
+ bool frozen; // < Contained nodes cannot be edited
+
+ ufbx_vec3 ui_color; // < Visual color for UI
+};
+
+// Named set of nodes/geometry features to select.
+struct ufbx_selection_set {
+ union { ufbx_element element; struct {
+ ufbx_string name;
+ ufbx_props props;
+ uint32_t element_id;
+ uint32_t typed_id;
+ }; };
+
+ // Included nodes and geometry features
+ ufbx_selection_node_list nodes;
+};
+
+// Selection state of a node, potentially contains vertex/edge/face selection as well.
+struct ufbx_selection_node {
+ union { ufbx_element element; struct {
+ ufbx_string name;
+ ufbx_props props;
+ uint32_t element_id;
+ uint32_t typed_id;
+ }; };
+
+ // Selection targets, possibly `NULL`
+ ufbx_nullable ufbx_node *target_node;
+ ufbx_nullable ufbx_mesh *target_mesh;
+ bool include_node; // < Is `target_node` included in the selection
+
+ // Indices to selected components.
+ // Guaranteed to be valid as per `ufbx_load_opts.index_error_handling`
+ // if `target_mesh` is not `NULL`.
+ ufbx_uint32_list vertices; // < Indices to `ufbx_mesh.vertices`
+ ufbx_uint32_list edges; // < Indices to `ufbx_mesh.edges`
+ ufbx_uint32_list faces; // < Indices to `ufbx_mesh.faces`
+};
+
+// -- Constraints
+
+struct ufbx_character {
+ union { ufbx_element element; struct {
+ ufbx_string name;
+ ufbx_props props;
+ uint32_t element_id;
+ uint32_t typed_id;
+ }; };
+};
+
+// Type of property constrain eg. position or look-at
+typedef enum ufbx_constraint_type UFBX_ENUM_REPR {
+ UFBX_CONSTRAINT_UNKNOWN,
+ UFBX_CONSTRAINT_AIM,
+ UFBX_CONSTRAINT_PARENT,
+ UFBX_CONSTRAINT_POSITION,
+ UFBX_CONSTRAINT_ROTATION,
+ UFBX_CONSTRAINT_SCALE,
+ // Inverse kinematic chain to a single effector `ufbx_constraint.ik_effector`
+ // `targets` optionally contains a list of pole targets!
+ UFBX_CONSTRAINT_SINGLE_CHAIN_IK,
+
+ UFBX_ENUM_FORCE_WIDTH(UFBX_CONSTRAINT_TYPE)
+} ufbx_constraint_type;
+
+UFBX_ENUM_TYPE(ufbx_constraint_type, UFBX_CONSTRAINT_TYPE, UFBX_CONSTRAINT_SINGLE_CHAIN_IK);
+
+// Target to follow with a constraint
+typedef struct ufbx_constraint_target {
+ ufbx_node *node; // < Target node reference
+ ufbx_real weight; // < Relative weight to other targets (does not always sum to 1)
+ ufbx_transform transform; // < Offset from the actual target
+} ufbx_constraint_target;
+
+UFBX_LIST_TYPE(ufbx_constraint_target_list, ufbx_constraint_target);
+
+// Method to determine the up vector in aim constraints
+typedef enum ufbx_constraint_aim_up_type UFBX_ENUM_REPR {
+ UFBX_CONSTRAINT_AIM_UP_SCENE, // < Align the up vector to the scene global up vector
+ UFBX_CONSTRAINT_AIM_UP_TO_NODE, // < Aim the up vector at `ufbx_constraint.aim_up_node`
+ UFBX_CONSTRAINT_AIM_UP_ALIGN_NODE, // < Copy the up vector from `ufbx_constraint.aim_up_node`
+ UFBX_CONSTRAINT_AIM_UP_VECTOR, // < Use `ufbx_constraint.aim_up_vector` as the up vector
+ UFBX_CONSTRAINT_AIM_UP_NONE, // < Don't align the up vector to anything
+
+ UFBX_ENUM_FORCE_WIDTH(UFBX_CONSTRAINT_AIM_UP_TYPE)
+} ufbx_constraint_aim_up_type;
+
+UFBX_ENUM_TYPE(ufbx_constraint_aim_up_type, UFBX_CONSTRAINT_AIM_UP_TYPE, UFBX_CONSTRAINT_AIM_UP_NONE);
+
+// Method to determine the up vector in aim constraints
+typedef enum ufbx_constraint_ik_pole_type UFBX_ENUM_REPR {
+ UFBX_CONSTRAINT_IK_POLE_VECTOR, // < Use towards calculated from `ufbx_constraint.targets`
+ UFBX_CONSTRAINT_IK_POLE_NODE, // < Use `ufbx_constraint.ik_pole_vector` directly
+
+ UFBX_ENUM_FORCE_WIDTH(UFBX_CONSTRAINT_IK_POLE_TYPE)
+} ufbx_constraint_ik_pole_type;
+
+UFBX_ENUM_TYPE(ufbx_constraint_ik_pole_type, UFBX_CONSTRAINT_IK_POLE_TYPE, UFBX_CONSTRAINT_IK_POLE_NODE);
+
+struct ufbx_constraint {
+ union { ufbx_element element; struct {
+ ufbx_string name;
+ ufbx_props props;
+ uint32_t element_id;
+ uint32_t typed_id;
+ }; };
+
+ // Type of constraint to use
+ ufbx_constraint_type type;
+ ufbx_string type_name;
+
+ // Node to be constrained
+ ufbx_nullable ufbx_node *node;
+
+ // List of weighted targets for the constraint (pole vectors for IK)
+ ufbx_constraint_target_list targets;
+
+ // State of the constraint
+ ufbx_real weight;
+ bool active;
+
+ // Translation/rotation/scale axes the constraint is applied to
+ bool constrain_translation[3];
+ bool constrain_rotation[3];
+ bool constrain_scale[3];
+
+ // Offset from the constrained position
+ ufbx_transform transform_offset;
+
+ // AIM: Target and up vectors
+ ufbx_vec3 aim_vector;
+ ufbx_constraint_aim_up_type aim_up_type;
+ ufbx_nullable ufbx_node *aim_up_node;
+ ufbx_vec3 aim_up_vector;
+
+ // SINGLE_CHAIN_IK: Target for the IK, `targets` contains pole vectors!
+ ufbx_nullable ufbx_node *ik_effector;
+ ufbx_nullable ufbx_node *ik_end_node;
+ ufbx_vec3 ik_pole_vector;
+};
+
+// -- Audio
+
+struct ufbx_audio_layer {
+ union { ufbx_element element; struct {
+ ufbx_string name;
+ ufbx_props props;
+ uint32_t element_id;
+ uint32_t typed_id;
+ }; };
+
+ // Clips contained in this layer.
+ ufbx_audio_clip_list clips;
+};
+
+struct ufbx_audio_clip {
+ union { ufbx_element element; struct {
+ ufbx_string name;
+ ufbx_props props;
+ uint32_t element_id;
+ uint32_t typed_id;
+ }; };
+
+ // Filename relative to the currently loaded file.
+ // HINT: If using functions other than `ufbx_load_file()`, you can provide
+ // `ufbx_load_opts.filename/raw_filename` to let ufbx resolve this.
+ ufbx_string filename;
+ // Absolute filename specified in the file.
+ ufbx_string absolute_filename;
+ // Relative filename specified in the file.
+ // NOTE: May be absolute if the file is saved in a different drive.
+ ufbx_string relative_filename;
+
+ // Filename relative to the loaded file, non-UTF-8 encoded.
+ // HINT: If using functions other than `ufbx_load_file()`, you can provide
+ // `ufbx_load_opts.filename/raw_filename` to let ufbx resolve this.
+ ufbx_blob raw_filename;
+ // Absolute filename specified in the file, non-UTF-8 encoded.
+ ufbx_blob raw_absolute_filename;
+ // Relative filename specified in the file, non-UTF-8 encoded.
+ // NOTE: May be absolute if the file is saved in a different drive.
+ ufbx_blob raw_relative_filename;
+
+ // Optional embedded content blob, eg. raw .png format data
+ ufbx_blob content;
+};
+
+// -- Miscellaneous
+
+typedef struct ufbx_bone_pose {
+
+ // Node to apply the pose to.
+ ufbx_node *bone_node;
+
+ // Matrix from node local space to world space.
+ ufbx_matrix bone_to_world;
+
+ // Matrix from node local space to parent space.
+ // NOTE: FBX only stores world transformations so this is approximated from
+ // the parent world transform.
+ ufbx_matrix bone_to_parent;
+
+} ufbx_bone_pose;
+
+UFBX_LIST_TYPE(ufbx_bone_pose_list, ufbx_bone_pose);
+
+struct ufbx_pose {
+ union { ufbx_element element; struct {
+ ufbx_string name;
+ ufbx_props props;
+ uint32_t element_id;
+ uint32_t typed_id;
+ }; };
+
+ // Set if this pose is marked as a bind pose.
+ bool is_bind_pose;
+
+ // List of bone poses.
+ // Sorted by `ufbx_node.typed_id`.
+ ufbx_bone_pose_list bone_poses;
+};
+
+struct ufbx_metadata_object {
+ union { ufbx_element element; struct {
+ ufbx_string name;
+ ufbx_props props;
+ uint32_t element_id;
+ uint32_t typed_id;
+ }; };
+};
+
+// -- Named elements
+
+typedef struct ufbx_name_element {
+ ufbx_string name;
+ ufbx_element_type type;
+
+ uint32_t _internal_key;
+
+ ufbx_element *element;
+} ufbx_name_element;
+
+UFBX_LIST_TYPE(ufbx_name_element_list, ufbx_name_element);
+
+// -- Scene
+
+// Scene is the root object loaded by ufbx that everything is accessed from.
+
+typedef enum ufbx_exporter UFBX_ENUM_REPR {
+ UFBX_EXPORTER_UNKNOWN,
+ UFBX_EXPORTER_FBX_SDK,
+ UFBX_EXPORTER_BLENDER_BINARY,
+ UFBX_EXPORTER_BLENDER_ASCII,
+ UFBX_EXPORTER_MOTION_BUILDER,
+
+ UFBX_ENUM_FORCE_WIDTH(UFBX_EXPORTER)
+} ufbx_exporter;
+
+UFBX_ENUM_TYPE(ufbx_exporter, UFBX_EXPORTER, UFBX_EXPORTER_MOTION_BUILDER);
+
+typedef struct ufbx_application {
+ ufbx_string vendor;
+ ufbx_string name;
+ ufbx_string version;
+} ufbx_application;
+
+typedef enum ufbx_file_format UFBX_ENUM_REPR {
+ UFBX_FILE_FORMAT_UNKNOWN, // < Unknown file format
+ UFBX_FILE_FORMAT_FBX, // < .fbx Kaydara/Autodesk FBX file
+ UFBX_FILE_FORMAT_OBJ, // < .obj Wavefront OBJ file
+ UFBX_FILE_FORMAT_MTL, // < .mtl Wavefront MTL (Material template library) file
+
+ UFBX_ENUM_FORCE_WIDTH(UFBX_FILE_FORMAT)
+} ufbx_file_format;
+
+UFBX_ENUM_TYPE(ufbx_file_format, UFBX_FILE_FORMAT, UFBX_FILE_FORMAT_MTL);
+
+typedef enum ufbx_warning_type UFBX_ENUM_REPR {
+ // Missing external file file (for example .mtl for Wavefront .obj file or a
+ // geometry cache)
+ UFBX_WARNING_MISSING_EXTERNAL_FILE,
+
+ // Loaded a Wavefront .mtl file derived from the filename instead of a proper
+ // `mtllib` statement.
+ UFBX_WARNING_IMPLICIT_MTL,
+
+ // Truncated array has been auto-expanded.
+ UFBX_WARNING_TRUNCATED_ARRAY,
+
+ // Geometry data has been defined but has no data.
+ UFBX_WARNING_MISSING_GEOMETRY_DATA,
+
+ // Duplicated connection between two elements that shouldn't have.
+ UFBX_WARNING_DUPLICATE_CONNECTION,
+
+ // Vertex 'W' attribute length differs from main attribute.
+ UFBX_WARNING_BAD_VERTEX_W_ATTRIBUTE,
+
+ // Missing polygon mapping type.
+ UFBX_WARNING_MISSING_POLYGON_MAPPING,
+
+ // Unsupported version, loaded but may be incorrect.
+ // If the loading fails `UFBX_ERROR_UNSUPPORTED_VERSION` is issued instead.
+ UFBX_WARNING_UNSUPPORTED_VERSION,
+
+ // Out-of-bounds index has been clamped to be in-bounds.
+ // HINT: You can use `ufbx_index_error_handling` to adjust behavior.
+ UFBX_WARNING_INDEX_CLAMPED,
+
+ // Non-UTF8 encoded strings.
+ // HINT: You can use `ufbx_unicode_error_handling` to adjust behavior.
+ UFBX_WARNING_BAD_UNICODE,
+
+ // Invalid base64-encoded embedded content ignored.
+ UFBX_WARNING_BAD_BASE64_CONTENT,
+
+ // Non-node element connected to root.
+ UFBX_WARNING_BAD_ELEMENT_CONNECTED_TO_ROOT,
+
+ // Duplicated object ID in the file, connections will be wrong.
+ UFBX_WARNING_DUPLICATE_OBJECT_ID,
+
+ // Empty face has been removed.
+ // Use `ufbx_load_opts.allow_empty_faces` if you want to allow them.
+ UFBX_WARNING_EMPTY_FACE_REMOVED,
+
+ // Unknown .obj file directive.
+ UFBX_WARNING_UNKNOWN_OBJ_DIRECTIVE,
+
+ // Warnings after this one are deduplicated.
+ // See `ufbx_warning.count` for how many times they happened.
+ UFBX_WARNING_TYPE_FIRST_DEDUPLICATED = UFBX_WARNING_INDEX_CLAMPED,
+
+ UFBX_ENUM_FORCE_WIDTH(UFBX_WARNING_TYPE)
+} ufbx_warning_type;
+
+UFBX_ENUM_TYPE(ufbx_warning_type, UFBX_WARNING_TYPE, UFBX_WARNING_UNKNOWN_OBJ_DIRECTIVE);
+
+// Warning about a non-fatal issue in the file.
+// Often contains information about issues that ufbx has corrected about the
+// file but it might indicate something is not working properly.
+typedef struct ufbx_warning {
+ // Type of the warning.
+ ufbx_warning_type type;
+ // Description of the warning.
+ ufbx_string description;
+ // The element related to this warning or `UFBX_NO_INDEX` if not related to a specific element.
+ uint32_t element_id;
+ // Number of times this warning was encountered.
+ size_t count;
+} ufbx_warning;
+
+UFBX_LIST_TYPE(ufbx_warning_list, ufbx_warning);
+
+typedef enum ufbx_thumbnail_format UFBX_ENUM_REPR {
+ UFBX_THUMBNAIL_FORMAT_UNKNOWN, // < Unknown format
+ UFBX_THUMBNAIL_FORMAT_RGB_24, // < 8-bit RGB pixels, in memory R,G,B
+ UFBX_THUMBNAIL_FORMAT_RGBA_32, // < 8-bit RGBA pixels, in memory R,G,B,A
+
+ UFBX_ENUM_FORCE_WIDTH(UFBX_THUMBNAIL_FORMAT)
+} ufbx_thumbnail_format;
+
+UFBX_ENUM_TYPE(ufbx_thumbnail_format, UFBX_THUMBNAIL_FORMAT, UFBX_THUMBNAIL_FORMAT_RGBA_32);
+
+// Specify how unit / coordinate system conversion should be performed.
+// Affects how `ufbx_load_opts.target_axes` and `ufbx_load_opts.target_unit_meters` work,
+// has no effect if neither is specified.
+typedef enum ufbx_space_conversion UFBX_ENUM_REPR {
+
+ // Store the space conversion transform in the root node.
+ // Sets `ufbx_node.local_transform` of the root node.
+ UFBX_SPACE_CONVERSION_TRANSFORM_ROOT,
+
+ // Perform the conversion by using "adjust" transforms.
+ // Compensates for the transforms using `ufbx_node.adjust_pre_rotation` and
+ // `ufbx_node.adjust_pre_scale`. You don't need to account for these unless
+ // you are manually building transforms from `ufbx_props`.
+ UFBX_SPACE_CONVERSION_ADJUST_TRANSFORMS,
+
+ // Perform the conversion by scaling geometry in addition to adjusting transforms.
+ // Compensates transforms like `UFBX_SPACE_CONVERSION_ADJUST_TRANSFORMS` but
+ // applies scaling to geometry as well.
+ UFBX_SPACE_CONVERSION_MODIFY_GEOMETRY,
+
+ UFBX_ENUM_FORCE_WIDTH(UFBX_SPACE_CONVERSION)
+} ufbx_space_conversion;
+
+UFBX_ENUM_TYPE(ufbx_space_conversion, UFBX_SPACE_CONVERSION, UFBX_SPACE_CONVERSION_MODIFY_GEOMETRY);
+
+// Embedded thumbnail in the file, valid if the dimensions are non-zero.
+typedef struct ufbx_thumbnail {
+ ufbx_props props;
+
+ // Extents of the thumbnail
+ uint32_t width;
+ uint32_t height;
+
+ // Format of `ufbx_thumbnail.data`.
+ ufbx_thumbnail_format format;
+
+ // Thumbnail pixel data, layout as contiguous rows from bottom to top.
+ // See `ufbx_thumbnail.format` for the pixel format.
+ ufbx_blob data;
+} ufbx_thumbnail;
+
+// Miscellaneous data related to the loaded file
+typedef struct ufbx_metadata {
+
+ // List of non-fatal warnings about the file.
+ // If you need to only check whether a specific warning was triggered you
+ // can use `ufbx_metadata.has_warning[]`.
+ ufbx_warning_list warnings;
+
+ // FBX ASCII file format.
+ bool ascii;
+
+ // FBX version in integer format, eg. 7400 for 7.4.
+ uint32_t version;
+
+ // File format of the source file.
+ ufbx_file_format file_format;
+
+ // Index arrays may contain `UFBX_NO_INDEX` instead of a valid index
+ // to indicate gaps.
+ bool may_contain_no_index;
+
+ // May contain meshes with no defined vertex position.
+ // NOTE: `ufbx_mesh.vertex_position.exists` may be `false`!
+ bool may_contain_missing_vertex_position;
+
+ // Arrays may contain items with `NULL` element references.
+ // See `ufbx_load_opts.connect_broken_elements`.
+ bool may_contain_broken_elements;
+
+ // Some API guarantees do not apply (depending on unsafe options used).
+ // Loaded with `ufbx_load_opts.allow_unsafe` enabled.
+ bool is_unsafe;
+
+ // Flag for each possible warning type.
+ // See `ufbx_metadata.warnings[]` for detailed warning information.
+ bool has_warning[UFBX_WARNING_TYPE_COUNT];
+
+ ufbx_string creator;
+ bool big_endian;
+
+ ufbx_string filename;
+ ufbx_string relative_root;
+
+ ufbx_blob raw_filename;
+ ufbx_blob raw_relative_root;
+
+ ufbx_exporter exporter;
+ uint32_t exporter_version;
+
+ ufbx_props scene_props;
+
+ ufbx_application original_application;
+ ufbx_application latest_application;
+
+ ufbx_thumbnail thumbnail;
+
+ bool geometry_ignored;
+ bool animation_ignored;
+ bool embedded_ignored;
+
+ size_t max_face_triangles;
+
+ size_t result_memory_used;
+ size_t temp_memory_used;
+ size_t result_allocs;
+ size_t temp_allocs;
+
+ size_t element_buffer_size;
+ size_t num_shader_textures;
+
+ ufbx_real bone_prop_size_unit;
+ bool bone_prop_limb_length_relative;
+
+ ufbx_real ortho_size_unit;
+
+ int64_t ktime_second; // < One second in internal KTime units
+
+ ufbx_string original_file_path;
+ ufbx_blob raw_original_file_path;
+
+ // Space conversion method used on the scene.
+ ufbx_space_conversion space_conversion;
+
+ // Transform that has been applied to root for axis/unit conversion.
+ ufbx_quat root_rotation;
+ ufbx_real root_scale;
+
+ // Axis that the scene has been mirrored by.
+ // All geometry has been mirrored in this axis.
+ ufbx_mirror_axis mirror_axis;
+
+ // Amount geometry has been scaled.
+ // See `UFBX_SPACE_CONVERSION_MODIFY_GEOMETRY`.
+ ufbx_real geometry_scale;
+
+} ufbx_metadata;
+
+typedef enum ufbx_time_mode UFBX_ENUM_REPR {
+ UFBX_TIME_MODE_DEFAULT,
+ UFBX_TIME_MODE_120_FPS,
+ UFBX_TIME_MODE_100_FPS,
+ UFBX_TIME_MODE_60_FPS,
+ UFBX_TIME_MODE_50_FPS,
+ UFBX_TIME_MODE_48_FPS,
+ UFBX_TIME_MODE_30_FPS,
+ UFBX_TIME_MODE_30_FPS_DROP,
+ UFBX_TIME_MODE_NTSC_DROP_FRAME,
+ UFBX_TIME_MODE_NTSC_FULL_FRAME,
+ UFBX_TIME_MODE_PAL,
+ UFBX_TIME_MODE_24_FPS,
+ UFBX_TIME_MODE_1000_FPS,
+ UFBX_TIME_MODE_FILM_FULL_FRAME,
+ UFBX_TIME_MODE_CUSTOM,
+ UFBX_TIME_MODE_96_FPS,
+ UFBX_TIME_MODE_72_FPS,
+ UFBX_TIME_MODE_59_94_FPS,
+
+ UFBX_ENUM_FORCE_WIDTH(UFBX_TIME_MODE)
+} ufbx_time_mode;
+
+UFBX_ENUM_TYPE(ufbx_time_mode, UFBX_TIME_MODE, UFBX_TIME_MODE_59_94_FPS);
+
+typedef enum ufbx_time_protocol UFBX_ENUM_REPR {
+ UFBX_TIME_PROTOCOL_SMPTE,
+ UFBX_TIME_PROTOCOL_FRAME_COUNT,
+ UFBX_TIME_PROTOCOL_DEFAULT,
+
+ UFBX_ENUM_FORCE_WIDTH(UFBX_TIME_PROTOCOL)
+} ufbx_time_protocol;
+
+UFBX_ENUM_TYPE(ufbx_time_protocol, UFBX_TIME_PROTOCOL, UFBX_TIME_PROTOCOL_DEFAULT);
+
+typedef enum ufbx_snap_mode UFBX_ENUM_REPR {
+ UFBX_SNAP_MODE_NONE,
+ UFBX_SNAP_MODE_SNAP,
+ UFBX_SNAP_MODE_PLAY,
+ UFBX_SNAP_MODE_SNAP_AND_PLAY,
+
+ UFBX_ENUM_FORCE_WIDTH(UFBX_SNAP_MODE)
+} ufbx_snap_mode;
+
+UFBX_ENUM_TYPE(ufbx_snap_mode, UFBX_SNAP_MODE, UFBX_SNAP_MODE_SNAP_AND_PLAY);
+
+// Global settings: Axes and time/unit scales
+typedef struct ufbx_scene_settings {
+ ufbx_props props;
+
+ // Mapping of X/Y/Z axes to world-space directions.
+ // HINT: Use `ufbx_load_opts.target_axes` to normalize this.
+ // NOTE: This contains the _original_ axes even if you supply `ufbx_load_opts.target_axes`.
+ ufbx_coordinate_axes axes;
+
+ // How many meters does a single world-space unit represent.
+ // FBX files usually default to centimeters, reported as `0.01` here.
+ // HINT: Use `ufbx_load_opts.target_unit_meters` to normalize this.
+ ufbx_real unit_meters;
+
+ // Frames per second the animation is defined at.
+ double frames_per_second;
+
+ ufbx_vec3 ambient_color;
+ ufbx_string default_camera;
+
+ // Animation user interface settings.
+ // HINT: Use `ufbx_scene_settings.frames_per_second` instead of interpreting these yourself.
+ ufbx_time_mode time_mode;
+ ufbx_time_protocol time_protocol;
+ ufbx_snap_mode snap_mode;
+
+ // Original settings (?)
+ ufbx_coordinate_axis original_axis_up;
+ ufbx_real original_unit_meters;
+} ufbx_scene_settings;
+
+struct ufbx_scene {
+ ufbx_metadata metadata;
+
+ // Global settings
+ ufbx_scene_settings settings;
+
+ // Node instances in the scene
+ ufbx_node *root_node;
+
+ // Default animation descriptor
+ ufbx_anim *anim;
+
+ union {
+ struct {
+ ufbx_unknown_list unknowns;
+
+ // Nodes
+ ufbx_node_list nodes;
+
+ // Node attributes (common)
+ ufbx_mesh_list meshes;
+ ufbx_light_list lights;
+ ufbx_camera_list cameras;
+ ufbx_bone_list bones;
+ ufbx_empty_list empties;
+
+ // Node attributes (curves/surfaces)
+ ufbx_line_curve_list line_curves;
+ ufbx_nurbs_curve_list nurbs_curves;
+ ufbx_nurbs_surface_list nurbs_surfaces;
+ ufbx_nurbs_trim_surface_list nurbs_trim_surfaces;
+ ufbx_nurbs_trim_boundary_list nurbs_trim_boundaries;
+
+ // Node attributes (advanced)
+ ufbx_procedural_geometry_list procedural_geometries;
+ ufbx_stereo_camera_list stereo_cameras;
+ ufbx_camera_switcher_list camera_switchers;
+ ufbx_marker_list markers;
+ ufbx_lod_group_list lod_groups;
+
+ // Deformers
+ ufbx_skin_deformer_list skin_deformers;
+ ufbx_skin_cluster_list skin_clusters;
+ ufbx_blend_deformer_list blend_deformers;
+ ufbx_blend_channel_list blend_channels;
+ ufbx_blend_shape_list blend_shapes;
+ ufbx_cache_deformer_list cache_deformers;
+ ufbx_cache_file_list cache_files;
+
+ // Materials
+ ufbx_material_list materials;
+ ufbx_texture_list textures;
+ ufbx_video_list videos;
+ ufbx_shader_list shaders;
+ ufbx_shader_binding_list shader_bindings;
+
+ // Animation
+ ufbx_anim_stack_list anim_stacks;
+ ufbx_anim_layer_list anim_layers;
+ ufbx_anim_value_list anim_values;
+ ufbx_anim_curve_list anim_curves;
+
+ // Collections
+ ufbx_display_layer_list display_layers;
+ ufbx_selection_set_list selection_sets;
+ ufbx_selection_node_list selection_nodes;
+
+ // Constraints
+ ufbx_character_list characters;
+ ufbx_constraint_list constraints;
+
+ // Audio
+ ufbx_audio_layer_list audio_layers;
+ ufbx_audio_clip_list audio_clips;
+
+ // Miscellaneous
+ ufbx_pose_list poses;
+ ufbx_metadata_object_list metadata_objects;
+ };
+
+ ufbx_element_list elements_by_type[UFBX_ELEMENT_TYPE_COUNT];
+ };
+
+ // Unique texture files referenced by the scene.
+ ufbx_texture_file_list texture_files;
+
+ // All elements and connections in the whole file
+ ufbx_element_list elements; // < Sorted by `id`
+ ufbx_connection_list connections_src; // < Sorted by `src,src_prop`
+ ufbx_connection_list connections_dst; // < Sorted by `dst,dst_prop`
+
+ // Elements sorted by name, type
+ ufbx_name_element_list elements_by_name;
+
+ // Enabled if `ufbx_load_opts.retain_dom == true`.
+ ufbx_nullable ufbx_dom_node *dom_root;
+};
+
+// -- Curves
+
+typedef struct ufbx_curve_point {
+ bool valid;
+ ufbx_vec3 position;
+ ufbx_vec3 derivative;
+} ufbx_curve_point;
+
+typedef struct ufbx_surface_point {
+ bool valid;
+ ufbx_vec3 position;
+ ufbx_vec3 derivative_u;
+ ufbx_vec3 derivative_v;
+} ufbx_surface_point;
+
+// -- Mesh topology
+
+typedef enum ufbx_topo_flags UFBX_FLAG_REPR {
+ UFBX_TOPO_NON_MANIFOLD = 0x1, // < Edge with three or more faces
+
+ UFBX_FLAG_FORCE_WIDTH(UFBX_TOPO_FLAGS)
+} ufbx_topo_flags;
+
+typedef struct ufbx_topo_edge {
+ uint32_t index; // < Starting index of the edge, always defined
+ uint32_t next; // < Ending index of the edge / next per-face `ufbx_topo_edge`, always defined
+ uint32_t prev; // < Previous per-face `ufbx_topo_edge`, always defined
+ uint32_t twin; // < `ufbx_topo_edge` on the opposite side, `UFBX_NO_INDEX` if not found
+ uint32_t face; // < Index into `mesh->faces[]`, always defined
+ uint32_t edge; // < Index into `mesh->edges[]`, `UFBX_NO_INDEX` if not found
+
+ ufbx_topo_flags flags;
+} ufbx_topo_edge;
+
+// Vertex data array for `ufbx_generate_indices()`.
+// NOTE: `ufbx_generate_indices()` compares the vertices using `memcmp()`, so
+// any padding should be cleared to zero.
+typedef struct ufbx_vertex_stream {
+ void *data; // < Data pointer of shape `char[vertex_count][vertex_size]`.
+ size_t vertex_count; // < Number of vertices in this stream, for sanity checking.
+ size_t vertex_size; // < Size of a vertex in bytes.
+} ufbx_vertex_stream;
+
+// -- Memory callbacks
+
+// You can optionally provide an allocator to ufbx, the default is to use the
+// CRT malloc/realloc/free
+
+// Allocate `size` bytes, must be at least 8 byte aligned
+typedef void *ufbx_alloc_fn(void *user, size_t size);
+
+// Reallocate `old_ptr` from `old_size` to `new_size`
+// NOTE: If omit `alloc_fn` and `free_fn` they will be translated to:
+// `alloc(size)` -> `realloc_fn(user, NULL, 0, size)`
+// `free_fn(ptr, size)` -> `realloc_fn(user, ptr, size, 0)`
+typedef void *ufbx_realloc_fn(void *user, void *old_ptr, size_t old_size, size_t new_size);
+
+// Free pointer `ptr` (of `size` bytes) returned by `alloc_fn` or `realloc_fn`
+typedef void ufbx_free_fn(void *user, void *ptr, size_t size);
+
+// Free the allocator itself
+typedef void ufbx_free_allocator_fn(void *user);
+
+// Allocator callbacks and user context
+// NOTE: The allocator will be stored to the loaded scene and will be called
+// again from `ufbx_free_scene()` so make sure `user` outlives that!
+// You can use `free_allocator_fn()` to free the allocator yourself.
+typedef struct ufbx_allocator {
+ // Callback functions, see `typedef`s above for information
+ ufbx_alloc_fn *alloc_fn;
+ ufbx_realloc_fn *realloc_fn;
+ ufbx_free_fn *free_fn;
+ ufbx_free_allocator_fn *free_allocator_fn;
+ void *user;
+} ufbx_allocator;
+
+typedef struct ufbx_allocator_opts {
+ // Allocator callbacks
+ ufbx_allocator allocator;
+
+ // Maximum number of bytes to allocate before failing
+ size_t memory_limit;
+
+ // Maximum number of allocations to attempt before failing
+ size_t allocation_limit;
+
+ // Threshold to swap from batched allocations to individual ones
+ // Defaults to 1MB if set to zero
+ // NOTE: If set to `1` ufbx will allocate everything in the smallest
+ // possible chunks which may be useful for debugging (eg. ASAN)
+ size_t huge_threshold;
+
+ // Maximum size of a single allocation containing sub-allocations.
+ // Defaults to 16MB if set to zero
+ // The maximum amount of wasted memory depends on `max_chunk_size` and
+ // `huge_threshold`: each chunk can waste up to `huge_threshold` bytes
+ // internally and the last chunk might be incomplete. So for example
+ // with the defaults we can waste around 1MB/16MB = 6.25% overall plus
+ // up to 32MB due to the two incomplete blocks. The actual amounts differ
+ // slightly as the chunks start out at 4kB and double in size each time,
+ // meaning that the maximum fixed overhead (up to 32MB with defaults) is
+ // at most ~30% of the total allocation size.
+ size_t max_chunk_size;
+
+} ufbx_allocator_opts;
+
+// -- IO callbacks
+
+// Try to read up to `size` bytes to `data`, return the amount of read bytes.
+// Return `SIZE_MAX` to indicate an IO error.
+typedef size_t ufbx_read_fn(void *user, void *data, size_t size);
+
+// Skip `size` bytes in the file.
+typedef bool ufbx_skip_fn(void *user, size_t size);
+
+// Get the size of the file.
+// Return `0` if unknown, `UINT64_MAX` if error.
+typedef uint64_t ufbx_size_fn(void *user);
+
+// Close the file
+typedef void ufbx_close_fn(void *user);
+
+typedef struct ufbx_stream {
+ ufbx_read_fn *read_fn; // < Required
+ ufbx_skip_fn *skip_fn; // < Optional: Will use `read_fn()` if missing
+ ufbx_size_fn *size_fn; // < Optional
+ ufbx_close_fn *close_fn; // < Optional
+
+ // Context passed to other functions
+ void *user;
+} ufbx_stream;
+
+typedef enum ufbx_open_file_type UFBX_ENUM_REPR {
+ UFBX_OPEN_FILE_MAIN_MODEL, // < Main model file
+ UFBX_OPEN_FILE_GEOMETRY_CACHE, // < Unknown geometry cache file
+ UFBX_OPEN_FILE_OBJ_MTL, // < .mtl material library file
+
+ UFBX_ENUM_FORCE_WIDTH(UFBX_OPEN_FILE_TYPE)
+} ufbx_open_file_type;
+
+UFBX_ENUM_TYPE(ufbx_open_file_type, UFBX_OPEN_FILE_TYPE, UFBX_OPEN_FILE_OBJ_MTL);
+
+typedef uintptr_t ufbx_open_file_context;
+
+typedef struct ufbx_open_file_info {
+ // Context that can be passed to the following functions to use a shared allocator:
+ // ufbx_open_file_ctx()
+ // ufbx_open_memory_ctx()
+ ufbx_open_file_context context;
+
+ // Kind of file to load.
+ ufbx_open_file_type type;
+
+ // Original filename in the file, not resolved or UTF-8 encoded.
+ // NOTE: Not necessarily NULL-terminated!
+ ufbx_blob original_filename;
+} ufbx_open_file_info;
+
+// Callback for opening an external file from the filesystem
+typedef bool ufbx_open_file_fn(void *user, ufbx_stream *stream, const char *path, size_t path_len, const ufbx_open_file_info *info);
+
+typedef struct ufbx_open_file_cb {
+ ufbx_open_file_fn *fn;
+ void *user;
+
+ UFBX_CALLBACK_IMPL(ufbx_open_file_cb, ufbx_open_file_fn, bool,
+ (void *user, ufbx_stream *stream, const char *path, size_t path_len, const ufbx_open_file_info *info),
+ (stream, path, path_len, info))
+} ufbx_open_file_cb;
+
+// Options for `ufbx_open_file()`.
+typedef struct ufbx_open_file_opts {
+ uint32_t _begin_zero;
+
+ // Allocator to allocate the memory with.
+ ufbx_allocator_opts allocator;
+
+ // The filename is guaranteed to be NULL-terminated.
+ ufbx_unsafe bool filename_null_terminated;
+
+ uint32_t _end_zero;
+} ufbx_open_file_opts;
+
+// Memory stream options
+typedef void ufbx_close_memory_fn(void *user, void *data, size_t data_size);
+
+typedef struct ufbx_close_memory_cb {
+ ufbx_close_memory_fn *fn;
+ void *user;
+
+ UFBX_CALLBACK_IMPL(ufbx_close_memory_cb, ufbx_close_memory_fn, void,
+ (void *user, void *data, size_t data_size),
+ (data, data_size))
+} ufbx_close_memory_cb;
+
+// Options for `ufbx_open_memory()`.
+typedef struct ufbx_open_memory_opts {
+ uint32_t _begin_zero;
+
+ // Allocator to allocate the memory with.
+ // NOTE: Used even if no copy is made to allocate a small metadata block.
+ ufbx_allocator_opts allocator;
+
+ // Do not copy the memory.
+ // You can use `close_cb` to free the memory when the stream is closed.
+ // NOTE: This means the provided data pointer is referenced after creating
+ // the memory stream, make sure the data stays valid until the stream is closed!
+ ufbx_unsafe bool no_copy;
+
+ // Callback to free the memory blob.
+ ufbx_close_memory_cb close_cb;
+
+ uint32_t _end_zero;
+} ufbx_open_memory_opts;
+
+// Detailed error stack frame.
+// NOTE: You must compile `ufbx.c` with `UFBX_ENABLE_ERROR_STACK` to enable the error stack.
+typedef struct ufbx_error_frame {
+ uint32_t source_line;
+ ufbx_string function;
+ ufbx_string description;
+} ufbx_error_frame;
+
+// Error causes (and `UFBX_ERROR_NONE` for no error).
+typedef enum ufbx_error_type UFBX_ENUM_REPR {
+
+ // No error, operation has been performed successfully.
+ UFBX_ERROR_NONE,
+
+ // Unspecified error, most likely caused by an invalid FBX file or a file
+ // that contains something ufbx can't handle.
+ UFBX_ERROR_UNKNOWN,
+
+ // File not found.
+ UFBX_ERROR_FILE_NOT_FOUND,
+
+ // Empty file.
+ UFBX_ERROR_EMPTY_FILE,
+
+ // External file not found.
+ // See `ufbx_load_opts.load_external_files` for more information.
+ UFBX_ERROR_EXTERNAL_FILE_NOT_FOUND,
+
+ // Out of memory (allocator returned `NULL`).
+ UFBX_ERROR_OUT_OF_MEMORY,
+
+ // `ufbx_allocator_opts.memory_limit` exhausted.
+ UFBX_ERROR_MEMORY_LIMIT,
+
+ // `ufbx_allocator_opts.allocation_limit` exhausted.
+ UFBX_ERROR_ALLOCATION_LIMIT,
+
+ // File ended abruptly.
+ UFBX_ERROR_TRUNCATED_FILE,
+
+ // IO read error.
+ // eg. returning `SIZE_MAX` from `ufbx_stream.read_fn` or stdio `ferror()` condition.
+ UFBX_ERROR_IO,
+
+ // User cancelled the loading via `ufbx_load_opts.progress_cb` returning `UFBX_PROGRESS_CANCEL`.
+ UFBX_ERROR_CANCELLED,
+
+ // Could not detect file format from file data or filename.
+ // HINT: You can supply it manually using `ufbx_load_opts.file_format` or use `ufbx_load_opts.filename`
+ // when using `ufbx_load_memory()` to let ufbx guess the format from the extension.
+ UFBX_ERROR_UNRECOGNIZED_FILE_FORMAT,
+
+ // Options struct (eg. `ufbx_load_opts`) is not cleared to zero.
+ // Make sure you initialize the structure to zero via eg.
+ // ufbx_load_opts opts = { 0 }; // C
+ // ufbx_load_opts opts = { }; // C++
+ UFBX_ERROR_UNINITIALIZED_OPTIONS,
+
+ // The vertex streams in `ufbx_generate_indices()` are empty.
+ UFBX_ERROR_ZERO_VERTEX_SIZE,
+
+ // Vertex stream passed to `ufbx_generate_indices()`.
+ UFBX_ERROR_TRUNCATED_VERTEX_STREAM,
+
+ // Invalid UTF-8 encountered in a file when loading with `UFBX_UNICODE_ERROR_HANDLING_ABORT_LOADING`.
+ UFBX_ERROR_INVALID_UTF8,
+
+ // Feature needed for the operation has been compiled out.
+ UFBX_ERROR_FEATURE_DISABLED,
+
+ // Attempting to tessellate an invalid NURBS object.
+ // See `ufbx_nurbs_basis.valid`.
+ UFBX_ERROR_BAD_NURBS,
+
+ // Out of bounds index in the file when loading with `UFBX_INDEX_ERROR_HANDLING_ABORT_LOADING`.
+ UFBX_ERROR_BAD_INDEX,
+
+ // Node is deeper than `ufbx_load_opts.node_depth_limit` in the hierarchy.
+ UFBX_ERROR_NODE_DEPTH_LIMIT,
+
+ // Error parsing ASCII array in a thread.
+ // Threaded ASCII parsing is slightly more strict than non-threaded, for cursed files,
+ // set `ufbx_load_opts.force_single_thread_ascii_parsing` to `true`.
+ UFBX_ERROR_THREADED_ASCII_PARSE,
+
+ // Unsafe options specified without enabling `ufbx_load_opts.allow_unsafe`.
+ UFBX_ERROR_UNSAFE_OPTIONS,
+
+ // Duplicated override property in `ufbx_create_anim()`
+ UFBX_ERROR_DUPLICATE_OVERRIDE,
+
+ // Unsupported file format version.
+ // ufbx still tries to load files with unsupported versions, see `UFBX_WARNING_UNSUPPORTED_VERSION`.
+ UFBX_ERROR_UNSUPPORTED_VERSION,
+
+ UFBX_ENUM_FORCE_WIDTH(UFBX_ERROR_TYPE)
+} ufbx_error_type;
+
+UFBX_ENUM_TYPE(ufbx_error_type, UFBX_ERROR_TYPE, UFBX_ERROR_UNSUPPORTED_VERSION);
+
+// Error description with detailed stack trace
+// HINT: You can use `ufbx_format_error()` for formatting the error
+typedef struct ufbx_error {
+
+ // Type of the error, or `UFBX_ERROR_NONE` if successful.
+ ufbx_error_type type;
+
+ // Description of the error type.
+ ufbx_string description;
+
+ // Internal error stack.
+ // NOTE: You must compile `ufbx.c` with `UFBX_ENABLE_ERROR_STACK` to enable the error stack.
+ uint32_t stack_size;
+ ufbx_error_frame stack[UFBX_ERROR_STACK_MAX_DEPTH];
+
+ // Additional error information, such as missing file filename.
+ // `info` is a NULL-terminated UTF-8 string containing `info_length` bytes, excluding the trailing `'\0'`.
+ size_t info_length;
+ char info[UFBX_ERROR_INFO_LENGTH];
+
+} ufbx_error;
+
+// -- Progress callbacks
+
+// Loading progress information.
+typedef struct ufbx_progress {
+ uint64_t bytes_read;
+ uint64_t bytes_total;
+} ufbx_progress;
+
+// Progress result returned from `ufbx_progress_fn()` callback.
+// Determines whether ufbx should continue or abort the loading.
+typedef enum ufbx_progress_result UFBX_ENUM_REPR {
+
+ // Continue loading the file.
+ UFBX_PROGRESS_CONTINUE = 0x100,
+
+ // Cancel loading and fail with `UFBX_ERROR_CANCELLED`.
+ UFBX_PROGRESS_CANCEL = 0x200,
+
+ UFBX_ENUM_FORCE_WIDTH(UFBX_PROGRESS_RESULT)
+} ufbx_progress_result;
+
+// Called periodically with the current progress.
+// Return `UFBX_PROGRESS_CANCEL` to cancel further processing.
+typedef ufbx_progress_result ufbx_progress_fn(void *user, const ufbx_progress *progress);
+
+typedef struct ufbx_progress_cb {
+ ufbx_progress_fn *fn;
+ void *user;
+
+ UFBX_CALLBACK_IMPL(ufbx_progress_cb, ufbx_progress_fn, ufbx_progress_result,
+ (void *user, const ufbx_progress *progress),
+ (progress))
+} ufbx_progress_cb;
+
+// -- Inflate
+
+typedef struct ufbx_inflate_input ufbx_inflate_input;
+typedef struct ufbx_inflate_retain ufbx_inflate_retain;
+
+// Source data/stream to decompress with `ufbx_inflate()`
+struct ufbx_inflate_input {
+ // Total size of the data in bytes
+ size_t total_size;
+
+ // (optional) Initial or complete data chunk
+ const void *data;
+ size_t data_size;
+
+ // (optional) Temporary buffer, defaults to 256b stack buffer
+ void *buffer;
+ size_t buffer_size;
+
+ // (optional) Streaming read function, concatenated after `data`
+ ufbx_read_fn *read_fn;
+ void *read_user;
+
+ // (optional) Progress reporting
+ ufbx_progress_cb progress_cb;
+ uint64_t progress_interval_hint; // < Bytes between progress report calls
+
+ // (optional) Change the progress scope
+ uint64_t progress_size_before;
+ uint64_t progress_size_after;
+
+ // (optional) No the DEFLATE header
+ bool no_header;
+
+ // (optional) No the Adler32 checksum
+ bool no_checksum;
+
+ // (optional) Force internal fast lookup bit amount
+ size_t internal_fast_bits;
+};
+
+// Persistent data between `ufbx_inflate()` calls
+// NOTE: You must set `initialized` to `false`, but `data` may be uninitialized
+struct ufbx_inflate_retain {
+ bool initialized;
+ uint64_t data[1024];
+};
+
+typedef enum ufbx_index_error_handling UFBX_ENUM_REPR {
+ // Clamp to a valid value.
+ UFBX_INDEX_ERROR_HANDLING_CLAMP,
+ // Set bad indices to `UFBX_NO_INDEX`.
+ // This is the recommended way if you need to deal with files with gaps in information.
+ // HINT: If you use this `ufbx_get_vertex_TYPE()` functions will return zero
+ // on invalid indices instead of failing.
+ UFBX_INDEX_ERROR_HANDLING_NO_INDEX,
+ // Fail loading entierely when encountering a bad index.
+ UFBX_INDEX_ERROR_HANDLING_ABORT_LOADING,
+ // Pass bad indices through as-is.
+ // Requires `ufbx_load_opts.allow_unsafe`.
+ // UNSAFE: Breaks any API guarantees regarding indexes being in bounds and makes
+ // `ufbx_get_vertex_TYPE()` memory-unsafe to use.
+ UFBX_INDEX_ERROR_HANDLING_UNSAFE_IGNORE,
+
+ UFBX_ENUM_FORCE_WIDTH(UFBX_INDEX_ERROR_HANDLING)
+} ufbx_index_error_handling;
+
+UFBX_ENUM_TYPE(ufbx_index_error_handling, UFBX_INDEX_ERROR_HANDLING, UFBX_INDEX_ERROR_HANDLING_UNSAFE_IGNORE);
+
+typedef enum ufbx_unicode_error_handling UFBX_ENUM_REPR {
+ // Replace errors with U+FFFD "Replacement Character"
+ UFBX_UNICODE_ERROR_HANDLING_REPLACEMENT_CHARACTER,
+ // Replace errors with '_' U+5F "Low Line"
+ UFBX_UNICODE_ERROR_HANDLING_UNDERSCORE,
+ // Replace errors with '?' U+3F "Question Mark"
+ UFBX_UNICODE_ERROR_HANDLING_QUESTION_MARK,
+ // Remove errors from the output
+ UFBX_UNICODE_ERROR_HANDLING_REMOVE,
+ // Fail loading on encountering an Unicode error
+ UFBX_UNICODE_ERROR_HANDLING_ABORT_LOADING,
+ // Ignore and pass-through non-UTF-8 string data.
+ // Requires `ufbx_load_opts.allow_unsafe`.
+ // UNSAFE: Breaks API guarantee that `ufbx_string` is UTF-8 encoded.
+ UFBX_UNICODE_ERROR_HANDLING_UNSAFE_IGNORE,
+
+ UFBX_ENUM_FORCE_WIDTH(UFBX_UNICODE_ERROR_HANDLING)
+} ufbx_unicode_error_handling;
+
+UFBX_ENUM_TYPE(ufbx_unicode_error_handling, UFBX_UNICODE_ERROR_HANDLING, UFBX_UNICODE_ERROR_HANDLING_UNSAFE_IGNORE);
+
+// How to handle FBX node geometry transforms.
+// FBX nodes can have "geometry transforms" that affect only the attached meshes,
+// but not the children. This is not allowed in many scene representations so
+// ufbx provides some ways to simplify them.
+// Geometry transforms can also be used to transform any other attributes such
+// as lights or cameras.
+typedef enum ufbx_geometry_transform_handling UFBX_ENUM_REPR {
+
+ // Preserve the geometry transforms as-is.
+ // To be correct for all files you have to use `ufbx_node.geometry_transform`,
+ // `ufbx_node.geometry_to_node`, or `ufbx_node.geometry_to_world` to compensate
+ // for any potential geometry transforms.
+ UFBX_GEOMETRY_TRANSFORM_HANDLING_PRESERVE,
+
+ // Add helper nodes between the nodes and geometry where needed.
+ // The created nodes have `ufbx_node.is_geometry_transform_helper` set and are
+ // named `ufbx_load_opts.geometry_transform_helper_name`.
+ UFBX_GEOMETRY_TRANSFORM_HANDLING_HELPER_NODES,
+
+ // Modify the geometry of meshes attached to nodes with geometry transforms.
+ // Will add helper nodes like `UFBX_GEOMETRY_TRANSFORM_HANDLING_HELPER_NODES` if
+ // necessary, for example if there are multiple instances of the same mesh with
+ // geometry transforms.
+ UFBX_GEOMETRY_TRANSFORM_HANDLING_MODIFY_GEOMETRY,
+
+ // Modify the geometry of meshes attached to nodes with geometry transforms.
+ // NOTE: This will not work correctly for instanced geometry.
+ UFBX_GEOMETRY_TRANSFORM_HANDLING_MODIFY_GEOMETRY_NO_FALLBACK,
+
+ UFBX_ENUM_FORCE_WIDTH(UFBX_GEOMETRY_TRANSFORM_HANDLING)
+} ufbx_geometry_transform_handling;
+
+UFBX_ENUM_TYPE(ufbx_geometry_transform_handling, UFBX_GEOMETRY_TRANSFORM_HANDLING, UFBX_GEOMETRY_TRANSFORM_HANDLING_MODIFY_GEOMETRY_NO_FALLBACK);
+
+// How to handle FBX transform inherit modes.
+typedef enum ufbx_inherit_mode_handling UFBX_ENUM_REPR {
+
+ // Preserve inherit mode in `ufbx_node.inherit_mode`.
+ // NOTE: To correctly handle all scenes you would need to handle the
+ // non-standard inherit modes.
+ UFBX_INHERIT_MODE_HANDLING_PRESERVE,
+
+ // Create scale helper nodes parented to nodes that need special inheritance.
+ // Scale helper nodes will have `ufbx_node.is_scale_helper` and parents of
+ // scale helpers will have `ufbx_node.scale_helper` pointing to it.
+ UFBX_INHERIT_MODE_HANDLING_HELPER_NODES,
+
+ // Attempt to compensate for bone scale by inversely scaling children.
+ // NOTE: This only works for uniform non-animated scaling, if scale is
+ // non-uniform or animated, ufbx will add scale helpers in the same way
+ // as `UFBX_INHERIT_MODE_HANDLING_HELPER_NODES`.
+ UFBX_INHERIT_MODE_HANDLING_COMPENSATE,
+
+ // Attempt to compensate for bone scale by inversely scaling children.
+ // Will never create helper nodes.
+ UFBX_INHERIT_MODE_HANDLING_COMPENSATE_NO_FALLBACK,
+
+ // Ignore non-standard inheritance modes.
+ // Forces all nodes to have `UFBX_INHERIT_MODE_NORMAL` regardless of the
+ // inherit mode specified in the file. This can be useful for emulating
+ // results from importers/programs that don't support inherit modes.
+ UFBX_INHERIT_MODE_HANDLING_IGNORE,
+
+ UFBX_ENUM_FORCE_WIDTH(UFBX_INHERIT_MODE_HANDLING)
+} ufbx_inherit_mode_handling;
+
+UFBX_ENUM_TYPE(ufbx_inherit_mode_handling, UFBX_INHERIT_MODE_HANDLING, UFBX_INHERIT_MODE_HANDLING_IGNORE);
+
+// How to handle FBX transform pivots.
+typedef enum ufbx_pivot_handling UFBX_ENUM_REPR {
+
+ // Take pivots into account when computing the transform.
+ UFBX_PIVOT_HANDLING_RETAIN,
+
+ // Translate objects to be located at their pivot.
+ // NOTE: Only applied if rotation and scaling pivots are equal.
+ // NOTE: Results in geometric translation. Use `ufbx_geometry_transform_handling`
+ // to interpret these in a standard scene graph.
+ UFBX_PIVOT_HANDLING_ADJUST_TO_PIVOT,
+
+ UFBX_ENUM_FORCE_WIDTH(UFBX_PIVOT_HANDLING)
+} ufbx_pivot_handling;
+
+UFBX_ENUM_TYPE(ufbx_pivot_handling, UFBX_PIVOT_HANDLING, UFBX_PIVOT_HANDLING_ADJUST_TO_PIVOT);
+
+typedef enum ufbx_baked_key_flags UFBX_FLAG_REPR {
+ // This keyframe represents a constant step from the left side
+ UFBX_BAKED_KEY_STEP_LEFT = 0x1,
+ // This keyframe represents a constant step from the right side
+ UFBX_BAKED_KEY_STEP_RIGHT = 0x2,
+ // This keyframe is the main part of a step
+ // Bordering either `UFBX_BAKED_KEY_STEP_LEFT` or `UFBX_BAKED_KEY_STEP_RIGHT`.
+ UFBX_BAKED_KEY_STEP_KEY = 0x4,
+ // This keyframe is a real keyframe in the source animation
+ UFBX_BAKED_KEY_KEYFRAME = 0x8,
+ // This keyframe has been reduced by maximum sample rate.
+ // See `ufbx_bake_opts.maximum_sample_rate`.
+ UFBX_BAKED_KEY_REDUCED = 0x10,
+
+ UFBX_FLAG_FORCE_WIDTH(UFBX_BAKED_KEY)
+} ufbx_baked_key_flags;
+
+typedef struct ufbx_baked_vec3 {
+ double time; // < Time of the keyframe, in seconds
+ ufbx_vec3 value; // < Value at `time`, can be linearly interpolated
+ ufbx_baked_key_flags flags; // < Additional information about the keyframe
+} ufbx_baked_vec3;
+
+UFBX_LIST_TYPE(ufbx_baked_vec3_list, ufbx_baked_vec3);
+
+typedef struct ufbx_baked_quat {
+ double time; // < Time of the keyframe, in seconds
+ ufbx_quat value; // < Value at `time`, can be (spherically) linearly interpolated
+ ufbx_baked_key_flags flags; // < Additional information about the keyframe
+} ufbx_baked_quat;
+
+UFBX_LIST_TYPE(ufbx_baked_quat_list, ufbx_baked_quat);
+
+// Baked transform animation for a single node.
+typedef struct ufbx_baked_node {
+
+ // Typed ID of the node, maps to `ufbx_scene.nodes[]`.
+ uint32_t typed_id;
+ // Element ID of the element, maps to `ufbx_scene.elements[]`.
+ uint32_t element_id;
+
+ // The translation channel has constant values for the whole animation.
+ bool constant_translation;
+ // The rotation channel has constant values for the whole animation.
+ bool constant_rotation;
+ // The scale channel has constant values for the whole animation.
+ bool constant_scale;
+
+ // Translation keys for the animation, maps to `ufbx_node.local_transform.translation`.
+ ufbx_baked_vec3_list translation_keys;
+ // Rotation keyframes, maps to `ufbx_node.local_transform.rotation`.
+ ufbx_baked_quat_list rotation_keys;
+ // Scale keyframes, maps to `ufbx_node.local_transform.scale`.
+ ufbx_baked_vec3_list scale_keys;
+
+} ufbx_baked_node;
+
+UFBX_LIST_TYPE(ufbx_baked_node_list, ufbx_baked_node);
+
+// Baked property animation.
+typedef struct ufbx_baked_prop {
+ // Name of the property, eg. `"Visibility"`.
+ ufbx_string name;
+ // The value of the property is constant for the whole animation.
+ bool constant_value;
+ // Property value keys.
+ ufbx_baked_vec3_list keys;
+} ufbx_baked_prop;
+
+UFBX_LIST_TYPE(ufbx_baked_prop_list, ufbx_baked_prop);
+
+// Baked property animation for a single element.
+typedef struct ufbx_baked_element {
+ // Element ID of the element, maps to `ufbx_scene.elements[]`.
+ uint32_t element_id;
+ // List of properties the animation modifies.
+ ufbx_baked_prop_list props;
+} ufbx_baked_element;
+
+UFBX_LIST_TYPE(ufbx_baked_element_list, ufbx_baked_element);
+
+typedef struct ufbx_baked_anim_metadata {
+ // Memory statistics
+ size_t result_memory_used;
+ size_t temp_memory_used;
+ size_t result_allocs;
+ size_t temp_allocs;
+} ufbx_baked_anim_metadata;
+
+// Animation baked into linearly interpolated keyframes.
+// See `ufbx_bake_anim()`.
+typedef struct ufbx_baked_anim {
+
+ // Nodes that are modified by the animation.
+ // Some nodes may be missing if the specified animation does not transform them.
+ // Conversely, some non-obviously animated nodes may be included as exporters
+ // often may add dummy keyframes for objects.
+ ufbx_baked_node_list nodes;
+
+ // Element properties modified by the animation.
+ ufbx_baked_element_list elements;
+
+ // Playback time range for the animation.
+ double playback_time_begin;
+ double playback_time_end;
+ double playback_duration;
+
+ // Keyframe time range.
+ double key_time_min;
+ double key_time_max;
+
+ // Additional bake information.
+ ufbx_baked_anim_metadata metadata;
+
+} ufbx_baked_anim;
+
+// -- Thread API
+
+// Internal thread pool handle.
+// Passed to `ufbx_thread_pool_run_task()` from an user thread to run ufbx tasks.
+// HINT: This context can store a user pointer via `ufbx_thread_pool_set_user_ptr()`.
+typedef uintptr_t ufbx_thread_pool_context;
+
+// Thread pool creation information from ufbx.
+typedef struct ufbx_thread_pool_info {
+ uint32_t max_concurrent_tasks;
+} ufbx_thread_pool_info;
+
+// Initialize the thread pool.
+// Return `true` on success.
+typedef bool ufbx_thread_pool_init_fn(void *user, ufbx_thread_pool_context ctx, const ufbx_thread_pool_info *info);
+
+// Run tasks `count` tasks in threads.
+// You must call `ufbx_thread_pool_run_task()` with indices `[start_index, start_index + count)`.
+// The threads are launched in batches indicated by `group`, see `UFBX_THREAD_GROUP_COUNT` for more information.
+// Ideally, you should run all the task indices in parallel within each `ufbx_thread_pool_run_fn()` call.
+typedef void ufbx_thread_pool_run_fn(void *user, ufbx_thread_pool_context ctx, uint32_t group, uint32_t start_index, uint32_t count);
+
+// Wait for previous tasks spawned in `ufbx_thread_pool_run_fn()` to finish.
+// `group` specifies the batch to wait for, `max_index` contains `start_index + count` from that group instance.
+typedef void ufbx_thread_pool_wait_fn(void *user, ufbx_thread_pool_context ctx, uint32_t group, uint32_t max_index);
+
+// Free the thread pool.
+typedef void ufbx_thread_pool_free_fn(void *user, ufbx_thread_pool_context ctx);
+
+// Thread pool interface.
+// See functions above for more information.
+//
+// Hypothetical example of calls, where `UFBX_THREAD_GROUP_COUNT=2` for simplicity:
+//
+// run_fn(group=0, start_index=0, count=4) -> t0 := threaded { ufbx_thread_pool_run_task(0..3) }
+// run_fn(group=1, start_index=4, count=10) -> t1 := threaded { ufbx_thread_pool_run_task(4..10) }
+// wait_fn(group=0, max_index=4) -> wait_threads(t0)
+// run_fn(group=0, start_index=10, count=15) -> t0 := threaded { ufbx_thread_pool_run_task(10..14) }
+// wait_fn(group=1, max_index=10) -> wait_threads(t1)
+// wait_fn(group=0, max_index=15) -> wait_threads(t0)
+//
+typedef struct ufbx_thread_pool {
+ ufbx_thread_pool_init_fn *init_fn; // < Optional
+ ufbx_thread_pool_run_fn *run_fn; // < Required
+ ufbx_thread_pool_wait_fn *wait_fn; // < Required
+ ufbx_thread_pool_free_fn *free_fn; // < Optional
+ void *user;
+} ufbx_thread_pool;
+
+// Thread pool options.
+typedef struct ufbx_thread_opts {
+
+ // Thread pool interface.
+ // HINT: You can use `extra/ufbx_os.h` to provide a thread pool.
+ ufbx_thread_pool pool;
+
+ // Maximum of tasks to have in-flight.
+ // Default: 2048
+ size_t num_tasks;
+
+ // Maximum amount of memory to use for batched threaded processing.
+ // Default: 32MB
+ // NOTE: The actual used memory usage might be higher, if there are individual tasks
+ // that rqeuire a high amount of memory.
+ size_t memory_limit;
+
+} ufbx_thread_opts;
+
+// Flags to control nanimation evaluation functions.
+typedef enum ufbx_evaluate_flags UFBX_FLAG_REPR {
+
+ // Do not extrapolate past the keyframes.
+ UFBX_EVALUATE_FLAG_NO_EXTRAPOLATION = 0x1,
+
+ UFBX_FLAG_FORCE_WIDTH(ufbx_evaluate_flags)
+} ufbx_evaluate_flags;
+
+// -- Main API
+
+// Options for `ufbx_load_file/memory/stream/stdio()`
+// NOTE: Initialize to zero with `{ 0 }` (C) or `{ }` (C++)
+typedef struct ufbx_load_opts {
+ uint32_t _begin_zero;
+
+ ufbx_allocator_opts temp_allocator; // < Allocator used during loading
+ ufbx_allocator_opts result_allocator; // < Allocator used for the final scene
+ ufbx_thread_opts thread_opts; // < Threading options
+
+ // Preferences
+ bool ignore_geometry; // < Do not load geometry datsa (vertices, indices, etc)
+ bool ignore_animation; // < Do not load animation curves
+ bool ignore_embedded; // < Do not load embedded content
+ bool ignore_all_content; // < Do not load any content (geometry, animation, embedded)
+
+ bool evaluate_skinning; // < Evaluate skinning (see ufbx_mesh.skinned_vertices)
+ bool evaluate_caches; // < Evaluate vertex caches (see ufbx_mesh.skinned_vertices)
+
+ // Try to open external files referenced by the main file automatically.
+ // Applies to geometry caches and .mtl files for OBJ.
+ // NOTE: This may be risky for untrusted data as the input files may contain
+ // references to arbitrary paths in the filesystem.
+ // NOTE: This only applies to files *implicitly* referenced by the scene, if
+ // you request additional files via eg. `ufbx_load_opts.obj_mtl_path` they
+ // are still loaded.
+ // NOTE: Will fail loading if any external files are not found by default, use
+ // `ufbx_load_opts.ignore_missing_external_files` to suppress this, in this case
+ // you can find the errors at `ufbx_metadata.warnings[]` as `UFBX_WARNING_MISSING_EXTERNAL_FILE`.
+ bool load_external_files;
+
+ // Don't fail loading if external files are not found.
+ bool ignore_missing_external_files;
+
+ // Don't compute `ufbx_skin_deformer` `vertices` and `weights` arrays saving
+ // a bit of memory and time if not needed
+ bool skip_skin_vertices;
+
+ // Skip computing `ufbx_mesh.material_parts[]` and `ufbx_mesh.face_group_parts[]`.
+ bool skip_mesh_parts;
+
+ // Clean-up skin weights by removing negative, zero and NAN weights.
+ bool clean_skin_weights;
+
+ // Read Blender materials as PBR values.
+ // Blender converts PBR materials to legacy FBX Phong materials in a deterministic way.
+ // If this setting is enabled, such materials will be read as `UFBX_SHADER_BLENDER_PHONG`,
+ // which means ufbx will be able to parse roughness and metallic textures.
+ bool use_blender_pbr_material;
+
+ // Don't adjust reading the FBX file depending on the detected exporter
+ bool disable_quirks;
+
+ // Don't allow partially broken FBX files to load
+ bool strict;
+
+ // Force ASCII parsing to use a single thread.
+ // The multi-threaded ASCII parsing is slightly more lenient as it ignores
+ // the self-reported size of ASCII arrays, that threaded parsing depends on.
+ bool force_single_thread_ascii_parsing;
+
+ // UNSAFE: If enabled allows using unsafe options that may fundamentally
+ // break the API guarantees.
+ ufbx_unsafe bool allow_unsafe;
+
+ // Specify how to handle broken indices.
+ ufbx_index_error_handling index_error_handling;
+
+ // Connect related elements even if they are broken. If `false` (default)
+ // `ufbx_skin_cluster` with a missing `bone` field are _not_ included in
+ // the `ufbx_skin_deformer.clusters[]` array for example.
+ bool connect_broken_elements;
+
+ // Allow nodes that are not connected in any way to the root. Conversely if
+ // disabled, all lone nodes will be parented under `ufbx_scene.root_node`.
+ bool allow_nodes_out_of_root;
+
+ // Allow meshes with no vertex position attribute.
+ // NOTE: If this is set `ufbx_mesh.vertex_position.exists` may be `false`.
+ bool allow_missing_vertex_position;
+
+ // Allow faces with zero indices.
+ bool allow_empty_faces;
+
+ // Generate vertex normals for a meshes that are missing normals.
+ // You can see if the normals have been generated from `ufbx_mesh.generated_normals`.
+ bool generate_missing_normals;
+
+ // Ignore `open_file_cb` when loading the main file.
+ bool open_main_file_with_default;
+
+ // Path separator character, defaults to '\' on Windows and '/' otherwise.
+ char path_separator;
+
+ // Maximum depth of the node hirerachy.
+ // Will fail with `UFBX_ERROR_NODE_DEPTH_LIMIT` if a node is deeper than this limit.
+ // NOTE: The default of 0 allows arbitrarily deep hierarchies. Be careful if using
+ // recursive algorithms without setting this limit.
+ uint32_t node_depth_limit;
+
+ // Estimated file size for progress reporting
+ uint64_t file_size_estimate;
+
+ // Buffer size in bytes to use for reading from files or IO callbacks
+ size_t read_buffer_size;
+
+ // Filename to use as a base for relative file paths if not specified using
+ // `ufbx_load_file()`. Use `length = SIZE_MAX` for NULL-terminated strings.
+ // `raw_filename` will be derived from this if empty.
+ ufbx_string filename;
+
+ // Raw non-UTF8 filename. Does not support NULL termination.
+ // `filename` will be derived from this if empty.
+ ufbx_blob raw_filename;
+
+ // Progress reporting
+ ufbx_progress_cb progress_cb;
+ uint64_t progress_interval_hint; // < Bytes between progress report calls
+
+ // External file callbacks (defaults to stdio.h)
+ ufbx_open_file_cb open_file_cb;
+
+ // How to handle geometry transforms in the nodes.
+ // See `ufbx_geometry_transform_handling` for an explanation.
+ ufbx_geometry_transform_handling geometry_transform_handling;
+
+ // How to handle unconventional transform inherit modes.
+ // See `ufbx_inherit_mode_handling` for an explanation.
+ ufbx_inherit_mode_handling inherit_mode_handling;
+
+ // How to handle pivots.
+ // See `ufbx_pivot_handling` for an explanation.
+ ufbx_pivot_handling pivot_handling;
+
+ // How to perform space conversion by `target_axes` and `target_unit_meters`.
+ // See `ufbx_space_conversion` for an explanation.
+ ufbx_space_conversion space_conversion;
+
+ // Axis used to mirror for conversion between left-handed and right-handed coordinates.
+ ufbx_mirror_axis handedness_conversion_axis;
+
+ // Do not change winding of faces when converting handedness.
+ bool handedness_conversion_retain_winding;
+
+ // Reverse winding of all faces.
+ // If `handedness_conversion_retain_winding` is not specified, mirrored meshes
+ // will retain their original winding.
+ bool reverse_winding;
+
+ // Apply an implicit root transformation to match axes.
+ // Used if `ufbx_coordinate_axes_valid(target_axes)`.
+ ufbx_coordinate_axes target_axes;
+
+ // Scale the scene so that one world-space unit is `target_unit_meters` meters.
+ // By default units are not scaled.
+ ufbx_real target_unit_meters;
+
+ // Target space for camera.
+ // By default FBX cameras point towards the positive X axis.
+ // Used if `ufbx_coordinate_axes_valid(target_camera_axes)`.
+ ufbx_coordinate_axes target_camera_axes;
+
+ // Target space for directed lights.
+ // By default FBX lights point towards the negative Y axis.
+ // Used if `ufbx_coordinate_axes_valid(target_light_axes)`.
+ ufbx_coordinate_axes target_light_axes;
+
+ // Name for dummy geometry transform helper nodes.
+ // See `UFBX_GEOMETRY_TRANSFORM_HANDLING_HELPER_NODES`.
+ ufbx_string geometry_transform_helper_name;
+
+ // Name for dummy scale helper nodes.
+ // See `UFBX_INHERIT_MODE_HANDLING_HELPER_NODES`.
+ ufbx_string scale_helper_name;
+
+ // Normalize vertex normals.
+ bool normalize_normals;
+
+ // Normalize tangents and bitangents.
+ bool normalize_tangents;
+
+ // Override for the root transform
+ bool use_root_transform;
+ ufbx_transform root_transform;
+
+ // Animation keyframe clamp threshold, only applies to specific interpolation modes.
+ double key_clamp_threshold;
+
+ // Specify how to handle Unicode errors in strings.
+ ufbx_unicode_error_handling unicode_error_handling;
+
+ // Retain the 'W' component of mesh normal/tangent/bitangent.
+ // See `ufbx_vertex_attrib.values_w`.
+ bool retain_vertex_attrib_w;
+
+ // Retain the raw document structure using `ufbx_dom_node`.
+ bool retain_dom;
+
+ // Force a specific file format instead of detecting it.
+ ufbx_file_format file_format;
+
+ // How far to read into the file to determine the file format.
+ // Default: 16kB
+ size_t file_format_lookahead;
+
+ // Do not attempt to detect file format from file content.
+ bool no_format_from_content;
+
+ // Do not attempt to detect file format from filename extension.
+ // ufbx primarily detects file format from the file header,
+ // this is just used as a fallback.
+ bool no_format_from_extension;
+
+ // (.obj) Try to find .mtl file with matching filename as the .obj file.
+ // Used if the file specified `mtllib` line is not found, eg. for a file called
+ // `model.obj` that contains the line `usemtl materials.mtl`, ufbx would first
+ // try to open `materials.mtl` and if that fails it tries to open `model.mtl`.
+ bool obj_search_mtl_by_filename;
+
+ // (.obj) Don't split geometry into meshes by object.
+ bool obj_merge_objects;
+
+ // (.obj) Don't split geometry into meshes by groups.
+ bool obj_merge_groups;
+
+ // (.obj) Force splitting groups even on object boundaries.
+ bool obj_split_groups;
+
+ // (.obj) Path to the .mtl file.
+ // Use `length = SIZE_MAX` for NULL-terminated strings.
+ // NOTE: This is used _instead_ of the one in the file even if not found
+ // and sidesteps `load_external_files` as it's _explicitly_ requested.
+ ufbx_string obj_mtl_path;
+
+ // (.obj) Data for the .mtl file.
+ ufbx_blob obj_mtl_data;
+
+ // The world unit in meters that .obj files are assumed to be in.
+ // .obj files do not define the working units. By default the unit scale
+ // is read as zero, and no unit conversion is performed.
+ ufbx_real obj_unit_meters;
+
+ // Coordinate space .obj files are assumed to be in.
+ // .obj files do not define the coordinate space they use. By default no
+ // coordinate space is assumed and no conversion is performed.
+ ufbx_coordinate_axes obj_axes;
+
+ uint32_t _end_zero;
+} ufbx_load_opts;
+
+// Options for `ufbx_evaluate_scene()`
+// NOTE: Initialize to zero with `{ 0 }` (C) or `{ }` (C++)
+typedef struct ufbx_evaluate_opts {
+ uint32_t _begin_zero;
+
+ ufbx_allocator_opts temp_allocator; // < Allocator used during evaluation
+ ufbx_allocator_opts result_allocator; // < Allocator used for the final scene
+
+ bool evaluate_skinning; // < Evaluate skinning (see ufbx_mesh.skinned_vertices)
+ bool evaluate_caches; // < Evaluate vertex caches (see ufbx_mesh.skinned_vertices)
+
+ // Evaluation flags.
+ // See `ufbx_evaluate_flags` for information.
+ uint32_t evaluate_flags;
+
+ // WARNING: Potentially unsafe! Try to open external files such as geometry caches
+ bool load_external_files;
+
+ // External file callbacks (defaults to stdio.h)
+ ufbx_open_file_cb open_file_cb;
+
+ uint32_t _end_zero;
+} ufbx_evaluate_opts;
+
+UFBX_LIST_TYPE(ufbx_const_uint32_list, const uint32_t);
+UFBX_LIST_TYPE(ufbx_const_real_list, const ufbx_real);
+
+typedef struct ufbx_prop_override_desc {
+ // Element (`ufbx_element.element_id`) to override the property from
+ uint32_t element_id;
+
+ // Property name to override.
+ ufbx_string prop_name;
+
+ // Override value, use `value.x` for scalars. `value_int` is initialized
+ // from `value.x` if zero so keep `value` zeroed even if you don't need it!
+ ufbx_vec4 value;
+ ufbx_string value_str;
+ int64_t value_int;
+} ufbx_prop_override_desc;
+
+UFBX_LIST_TYPE(ufbx_const_prop_override_desc_list, const ufbx_prop_override_desc);
+
+UFBX_LIST_TYPE(ufbx_const_transform_override_list, const ufbx_transform_override);
+
+typedef struct ufbx_anim_opts {
+ uint32_t _begin_zero;
+
+ // Animation layers indices.
+ // Corresponding to `ufbx_scene.anim_layers[]`, aka `ufbx_anim_layer.typed_id`.
+ ufbx_const_uint32_list layer_ids;
+
+ // Override layer weights, parallel to `ufbx_anim_opts.layer_ids[]`.
+ ufbx_const_real_list override_layer_weights;
+
+ // Property overrides.
+ // These allow you to override FBX properties, such as 'UFBX_Lcl_Rotation`.
+ ufbx_const_prop_override_desc_list prop_overrides;
+
+ // Transform overrides.
+ // These allow you to override individual nodes' `ufbx_node.local_transform`.
+ ufbx_const_transform_override_list transform_overrides;
+
+ // Ignore connected properties
+ bool ignore_connections;
+
+ ufbx_allocator_opts result_allocator; // < Allocator used to create the `ufbx_anim`
+
+ uint32_t _end_zero;
+} ufbx_anim_opts;
+
+// Specifies how to handle stepped tangents.
+typedef enum ufbx_bake_step_handling UFBX_ENUM_REPR {
+
+ // One millisecond default step duration, with potential extra slack for converting to `float`.
+ UFBX_BAKE_STEP_HANDLING_DEFAULT,
+
+ // Use a custom interpolation duration for the constant step.
+ // See `ufbx_bake_opts.step_custom_duration` and optionally `ufbx_bake_opts.step_custom_epsilon`.
+ UFBX_BAKE_STEP_HANDLING_CUSTOM_DURATION,
+
+ // Stepped keyframes are represented as keyframes at the exact same time.
+ // Use flags `UFBX_BAKED_KEY_STEP_LEFT` and `UFBX_BAKED_KEY_STEP_RIGHT` to differentiate
+ // between the primary key and edge limits.
+ UFBX_BAKE_STEP_HANDLING_IDENTICAL_TIME,
+
+ // Represent stepped keyframe times as the previous/next representable `double` value.
+ // Using this and robust linear interpolation will handle stepped tangents correctly
+ // without having to look at the key flags.
+ // NOTE: Casting these values to `float` or otherwise modifying them can collapse
+ // the keyframes to have the identical time.
+ UFBX_BAKE_STEP_HANDLING_ADJACENT_DOUBLE,
+
+ // Treat all stepped tangents as linearly interpolated.
+ UFBX_BAKE_STEP_HANDLING_IGNORE,
+
+ UFBX_ENUM_FORCE_WIDTH(ufbx_bake_step_handling)
+} ufbx_bake_step_handling;
+
+UFBX_ENUM_TYPE(ufbx_bake_step_handling, UFBX_BAKE_STEP_HANDLING, UFBX_BAKE_STEP_HANDLING_IGNORE);
+
+typedef struct ufbx_bake_opts {
+ uint32_t _begin_zero;
+
+ ufbx_allocator_opts temp_allocator; // < Allocator used during loading
+ ufbx_allocator_opts result_allocator; // < Allocator used for the final baked animation
+
+ // Move the keyframe times to start from zero regardless of the animation start time.
+ // For example, for an animation spanning between frames [30, 60] will be moved to
+ // [0, 30] in the baked animation.
+ // NOTE: This is in general not equivalent to subtracting `ufbx_anim.time_begin`
+ // from each keyframe, as this trimming is done exactly using internal FBX ticks.
+ bool trim_start_time;
+
+ // Samples per second to use for resampling non-linear animation.
+ // Default: 30
+ double resample_rate;
+
+ // Minimum sample rate to not resample.
+ // Many exporters resample animation by default. To avoid double-resampling
+ // keyframe rates higher or equal to this will not be resampled.
+ // Default: 19.5
+ double minimum_sample_rate;
+
+ // Maximum sample rate to use, this will remove keys if they are too close together.
+ // Default: unlimited
+ double maximum_sample_rate;
+
+ // Bake the raw versions of properties related to transforms.
+ bool bake_transform_props;
+
+ // Do not bake node transforms.
+ bool skip_node_transforms;
+
+ // Do not resample linear rotation keyframes.
+ // FBX interpolates rotation in Euler angles, so this might cause incorrect interpolation.
+ bool no_resample_rotation;
+
+ // Ignore layer weight animation.
+ bool ignore_layer_weight_animation;
+
+ // Maximum number of segments to generate from one keyframe.
+ // Default: 32
+ size_t max_keyframe_segments;
+
+ // How to handle stepped tangents.
+ ufbx_bake_step_handling step_handling;
+
+ // Interpolation duration used by `UFBX_BAKE_STEP_HANDLING_CUSTOM_DURATION`.
+ double step_custom_duration;
+
+ // Interpolation epsilon used by `UFBX_BAKE_STEP_HANDLING_CUSTOM_DURATION`.
+ // Defined as the minimum fractional decrease/increase in key time, ie.
+ // `time / (1.0 + step_custom_epsilon)` and `time * (1.0 + step_custom_epsilon)`.
+ double step_custom_epsilon;
+
+ // Flags passed to animation evaluation functions.
+ // See `ufbx_evaluate_flags`.
+ uint32_t evaluate_flags;
+
+ // Enable key reduction.
+ bool key_reduction_enabled;
+
+ // Enable key reduction for non-constant rotations.
+ // Assumes rotations will be interpolated using a spherical linear interpolation at runtime.
+ bool key_reduction_rotation;
+
+ // Threshold for reducing keys for linear segments.
+ // Default `0.000001`, use negative to disable.
+ double key_reduction_threshold;
+
+ // Maximum passes over the keys to reduce.
+ // Every pass can potentially halve the the amount of keys.
+ // Default: `4`
+ size_t key_reduction_passes;
+
+ uint32_t _end_zero;
+} ufbx_bake_opts;
+
+// Options for `ufbx_tessellate_nurbs_curve()`
+// NOTE: Initialize to zero with `{ 0 }` (C) or `{ }` (C++)
+typedef struct ufbx_tessellate_curve_opts {
+ uint32_t _begin_zero;
+
+ ufbx_allocator_opts temp_allocator; // < Allocator used during tessellation
+ ufbx_allocator_opts result_allocator; // < Allocator used for the final line curve
+
+ // How many segments tessellate each span in `ufbx_nurbs_basis.spans`.
+ size_t span_subdivision;
+
+ uint32_t _end_zero;
+} ufbx_tessellate_curve_opts;
+
+// Options for `ufbx_tessellate_nurbs_surface()`
+// NOTE: Initialize to zero with `{ 0 }` (C) or `{ }` (C++)
+typedef struct ufbx_tessellate_surface_opts {
+ uint32_t _begin_zero;
+
+ ufbx_allocator_opts temp_allocator; // < Allocator used during tessellation
+ ufbx_allocator_opts result_allocator; // < Allocator used for the final mesh
+
+ // How many segments tessellate each span in `ufbx_nurbs_basis.spans`.
+ // NOTE: Default is `4`, _not_ `ufbx_nurbs_surface.span_subdivision_u/v` as that
+ // would make it easy to create an FBX file with an absurdly high subdivision
+ // rate (similar to mesh subdivision). Please enforce copy the value yourself
+ // enforcing whatever limits you deem reasonable.
+ size_t span_subdivision_u;
+ size_t span_subdivision_v;
+
+ // Skip computing `ufbx_mesh.material_parts[]`
+ bool skip_mesh_parts;
+
+ uint32_t _end_zero;
+} ufbx_tessellate_surface_opts;
+
+// Options for `ufbx_subdivide_mesh()`
+// NOTE: Initialize to zero with `{ 0 }` (C) or `{ }` (C++)
+typedef struct ufbx_subdivide_opts {
+ uint32_t _begin_zero;
+
+ ufbx_allocator_opts temp_allocator; // < Allocator used during subdivision
+ ufbx_allocator_opts result_allocator; // < Allocator used for the final mesh
+
+ ufbx_subdivision_boundary boundary;
+ ufbx_subdivision_boundary uv_boundary;
+
+ // Do not generate normals
+ bool ignore_normals;
+
+ // Interpolate existing normals using the subdivision rules
+ // instead of generating new normals
+ bool interpolate_normals;
+
+ // Subdivide also tangent attributes
+ bool interpolate_tangents;
+
+ // Map subdivided vertices into weighted original vertices.
+ // NOTE: May be O(n^2) if `max_source_vertices` is not specified!
+ bool evaluate_source_vertices;
+
+ // Limit source vertices per subdivided vertex.
+ size_t max_source_vertices;
+
+ // Calculate bone influences over subdivided vertices (if applicable).
+ // NOTE: May be O(n^2) if `max_skin_weights` is not specified!
+ bool evaluate_skin_weights;
+
+ // Limit bone influences per subdivided vertex.
+ size_t max_skin_weights;
+
+ // Index of the skin deformer to use for `evaluate_skin_weights`.
+ size_t skin_deformer_index;
+
+ uint32_t _end_zero;
+} ufbx_subdivide_opts;
+
+// Options for `ufbx_load_geometry_cache()`
+// NOTE: Initialize to zero with `{ 0 }` (C) or `{ }` (C++)
+typedef struct ufbx_geometry_cache_opts {
+ uint32_t _begin_zero;
+
+ ufbx_allocator_opts temp_allocator; // < Allocator used during loading
+ ufbx_allocator_opts result_allocator; // < Allocator used for the final scene
+
+ // External file callbacks (defaults to stdio.h)
+ ufbx_open_file_cb open_file_cb;
+
+ // FPS value for converting frame times to seconds
+ double frames_per_second;
+
+ // Axis to mirror the geometry by.
+ ufbx_mirror_axis mirror_axis;
+
+ // Enable scaling `scale_factor` all geometry by.
+ bool use_scale_factor;
+
+ // Factor to scale the geometry by.
+ ufbx_real scale_factor;
+
+ uint32_t _end_zero;
+} ufbx_geometry_cache_opts;
+
+// Options for `ufbx_read_geometry_cache_TYPE()`
+// NOTE: Initialize to zero with `{ 0 }` (C) or `{ }` (C++)
+typedef struct ufbx_geometry_cache_data_opts {
+ uint32_t _begin_zero;
+
+ // External file callbacks (defaults to stdio.h)
+ ufbx_open_file_cb open_file_cb;
+
+ bool additive;
+ bool use_weight;
+ ufbx_real weight;
+
+ // Ignore scene transform.
+ bool ignore_transform;
+
+ uint32_t _end_zero;
+} ufbx_geometry_cache_data_opts;
+
+typedef struct ufbx_panic {
+ bool did_panic;
+ size_t message_length;
+ char message[UFBX_PANIC_MESSAGE_LENGTH];
+} ufbx_panic;
+
+// -- API
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Various zero/empty/identity values
+ufbx_abi_data const ufbx_string ufbx_empty_string;
+ufbx_abi_data const ufbx_blob ufbx_empty_blob;
+ufbx_abi_data const ufbx_matrix ufbx_identity_matrix;
+ufbx_abi_data const ufbx_transform ufbx_identity_transform;
+ufbx_abi_data const ufbx_vec2 ufbx_zero_vec2;
+ufbx_abi_data const ufbx_vec3 ufbx_zero_vec3;
+ufbx_abi_data const ufbx_vec4 ufbx_zero_vec4;
+ufbx_abi_data const ufbx_quat ufbx_identity_quat;
+
+// Commonly used coordinate axes.
+ufbx_abi_data const ufbx_coordinate_axes ufbx_axes_right_handed_y_up;
+ufbx_abi_data const ufbx_coordinate_axes ufbx_axes_right_handed_z_up;
+ufbx_abi_data const ufbx_coordinate_axes ufbx_axes_left_handed_y_up;
+ufbx_abi_data const ufbx_coordinate_axes ufbx_axes_left_handed_z_up;
+
+// Sizes of element types. eg `sizeof(ufbx_node)`
+ufbx_abi_data const size_t ufbx_element_type_size[UFBX_ELEMENT_TYPE_COUNT];
+
+// Version of the source file, comparable to `UFBX_HEADER_VERSION`
+ufbx_abi_data const uint32_t ufbx_source_version;
+
+
+// Practically always `true` (see below), if not you need to be careful with threads.
+//
+// Guaranteed to be `true` in _any_ of the following conditions:
+// - ufbx.c has been compiled using: GCC / Clang / MSVC / ICC / EMCC / TCC
+// - ufbx.c has been compiled as C++11 or later
+// - ufbx.c has been compiled as C11 or later with `<stdatomic.h>` support
+//
+// If `false` you can't call the following functions concurrently:
+// ufbx_evaluate_scene()
+// ufbx_free_scene()
+// ufbx_subdivide_mesh()
+// ufbx_tessellate_nurbs_surface()
+// ufbx_free_mesh()
+ufbx_abi bool ufbx_is_thread_safe(void);
+
+// Load a scene from a `size` byte memory buffer at `data`
+ufbx_abi ufbx_scene *ufbx_load_memory(
+ const void *data, size_t data_size,
+ const ufbx_load_opts *opts, ufbx_error *error);
+
+// Load a scene by opening a file named `filename`
+ufbx_abi ufbx_scene *ufbx_load_file(
+ const char *filename,
+ const ufbx_load_opts *opts, ufbx_error *error);
+ufbx_abi ufbx_scene *ufbx_load_file_len(
+ const char *filename, size_t filename_len,
+ const ufbx_load_opts *opts, ufbx_error *error);
+
+// Load a scene by reading from an `FILE *file` stream
+// NOTE: `file` is passed as a `void` pointer to avoid including <stdio.h>
+ufbx_abi ufbx_scene *ufbx_load_stdio(
+ void *file,
+ const ufbx_load_opts *opts, ufbx_error *error);
+
+// Load a scene by reading from an `FILE *file` stream with a prefix
+// NOTE: `file` is passed as a `void` pointer to avoid including <stdio.h>
+ufbx_abi ufbx_scene *ufbx_load_stdio_prefix(
+ void *file,
+ const void *prefix, size_t prefix_size,
+ const ufbx_load_opts *opts, ufbx_error *error);
+
+// Load a scene from a user-specified stream
+ufbx_abi ufbx_scene *ufbx_load_stream(
+ const ufbx_stream *stream,
+ const ufbx_load_opts *opts, ufbx_error *error);
+
+// Load a scene from a user-specified stream with a prefix
+ufbx_abi ufbx_scene *ufbx_load_stream_prefix(
+ const ufbx_stream *stream,
+ const void *prefix, size_t prefix_size,
+ const ufbx_load_opts *opts, ufbx_error *error);
+
+// Free a previously loaded or evaluated scene
+ufbx_abi void ufbx_free_scene(ufbx_scene *scene);
+
+// Increment `scene` refcount
+ufbx_abi void ufbx_retain_scene(ufbx_scene *scene);
+
+// Format a textual description of `error`.
+// Always produces a NULL-terminated string to `char dst[dst_size]`, truncating if
+// necessary. Returns the number of characters written not including the NULL terminator.
+ufbx_abi size_t ufbx_format_error(char *dst, size_t dst_size, const ufbx_error *error);
+
+// Query
+
+// Find a property `name` from `props`, returns `NULL` if not found.
+// Searches through `ufbx_props.defaults` as well.
+ufbx_abi ufbx_prop *ufbx_find_prop_len(const ufbx_props *props, const char *name, size_t name_len);
+ufbx_abi ufbx_prop *ufbx_find_prop(const ufbx_props *props, const char *name);
+
+// Utility functions for finding the value of a property, returns `def` if not found.
+// NOTE: For `ufbx_string` you need to ensure the lifetime of the default is
+// sufficient as no copy is made.
+ufbx_abi ufbx_real ufbx_find_real_len(const ufbx_props *props, const char *name, size_t name_len, ufbx_real def);
+ufbx_abi ufbx_real ufbx_find_real(const ufbx_props *props, const char *name, ufbx_real def);
+ufbx_abi ufbx_vec3 ufbx_find_vec3_len(const ufbx_props *props, const char *name, size_t name_len, ufbx_vec3 def);
+ufbx_abi ufbx_vec3 ufbx_find_vec3(const ufbx_props *props, const char *name, ufbx_vec3 def);
+ufbx_abi int64_t ufbx_find_int_len(const ufbx_props *props, const char *name, size_t name_len, int64_t def);
+ufbx_abi int64_t ufbx_find_int(const ufbx_props *props, const char *name, int64_t def);
+ufbx_abi bool ufbx_find_bool_len(const ufbx_props *props, const char *name, size_t name_len, bool def);
+ufbx_abi bool ufbx_find_bool(const ufbx_props *props, const char *name, bool def);
+ufbx_abi ufbx_string ufbx_find_string_len(const ufbx_props *props, const char *name, size_t name_len, ufbx_string def);
+ufbx_abi ufbx_string ufbx_find_string(const ufbx_props *props, const char *name, ufbx_string def);
+ufbx_abi ufbx_blob ufbx_find_blob_len(const ufbx_props *props, const char *name, size_t name_len, ufbx_blob def);
+ufbx_abi ufbx_blob ufbx_find_blob(const ufbx_props *props, const char *name, ufbx_blob def);
+
+// Find property in `props` with concatenated `parts[num_parts]`.
+ufbx_abi ufbx_prop *ufbx_find_prop_concat(const ufbx_props *props, const ufbx_string *parts, size_t num_parts);
+
+// Get an element connected to a property.
+ufbx_abi ufbx_element *ufbx_get_prop_element(const ufbx_element *element, const ufbx_prop *prop, ufbx_element_type type);
+
+// Find an element connected to a property by name.
+ufbx_abi ufbx_element *ufbx_find_prop_element_len(const ufbx_element *element, const char *name, size_t name_len, ufbx_element_type type);
+ufbx_abi ufbx_element *ufbx_find_prop_element(const ufbx_element *element, const char *name, ufbx_element_type type);
+
+// Find any element of type `type` in `scene` by `name`.
+// For example if you want to find `ufbx_material` named `Mat`:
+// (ufbx_material*)ufbx_find_element(scene, UFBX_ELEMENT_MATERIAL, "Mat");
+ufbx_abi ufbx_element *ufbx_find_element_len(const ufbx_scene *scene, ufbx_element_type type, const char *name, size_t name_len);
+ufbx_abi ufbx_element *ufbx_find_element(const ufbx_scene *scene, ufbx_element_type type, const char *name);
+
+// Find node in `scene` by `name` (shorthand for `ufbx_find_element(UFBX_ELEMENT_NODE)`).
+ufbx_abi ufbx_node *ufbx_find_node_len(const ufbx_scene *scene, const char *name, size_t name_len);
+ufbx_abi ufbx_node *ufbx_find_node(const ufbx_scene *scene, const char *name);
+
+// Find an animation stack in `scene` by `name` (shorthand for `ufbx_find_element(UFBX_ELEMENT_ANIM_STACK)`)
+ufbx_abi ufbx_anim_stack *ufbx_find_anim_stack_len(const ufbx_scene *scene, const char *name, size_t name_len);
+ufbx_abi ufbx_anim_stack *ufbx_find_anim_stack(const ufbx_scene *scene, const char *name);
+
+// Find a material in `scene` by `name` (shorthand for `ufbx_find_element(UFBX_ELEMENT_MATERIAL)`).
+ufbx_abi ufbx_material *ufbx_find_material_len(const ufbx_scene *scene, const char *name, size_t name_len);
+ufbx_abi ufbx_material *ufbx_find_material(const ufbx_scene *scene, const char *name);
+
+// Find a single animated property `prop` of `element` in `layer`.
+// Returns `NULL` if not found.
+ufbx_abi ufbx_anim_prop *ufbx_find_anim_prop_len(const ufbx_anim_layer *layer, const ufbx_element *element, const char *prop, size_t prop_len);
+ufbx_abi ufbx_anim_prop *ufbx_find_anim_prop(const ufbx_anim_layer *layer, const ufbx_element *element, const char *prop);
+
+// Find all animated properties of `element` in `layer`.
+ufbx_abi ufbx_anim_prop_list ufbx_find_anim_props(const ufbx_anim_layer *layer, const ufbx_element *element);
+
+// Get a matrix that transforms normals in the same way as Autodesk software.
+// NOTE: The resulting normals are slightly incorrect as this function deliberately
+// inverts geometric transformation wrong. For better results use
+// `ufbx_matrix_for_normals(&node->geometry_to_world)`.
+ufbx_abi ufbx_matrix ufbx_get_compatible_matrix_for_normals(const ufbx_node *node);
+
+// Utility
+
+// Decompress a DEFLATE compressed buffer.
+// Returns the decompressed size or a negative error code (see source for details).
+// NOTE: You must supply a valid `retain` with `ufbx_inflate_retain.initialized == false`
+// but the rest can be uninitialized.
+ufbx_abi ptrdiff_t ufbx_inflate(void *dst, size_t dst_size, const ufbx_inflate_input *input, ufbx_inflate_retain *retain);
+
+// Same as `ufbx_open_file()` but compatible with the callback in `ufbx_open_file_fn`.
+// The `user` parameter is actually not used here.
+ufbx_abi bool ufbx_default_open_file(void *user, ufbx_stream *stream, const char *path, size_t path_len, const ufbx_open_file_info *info);
+
+// Open a `ufbx_stream` from a file.
+// Use `path_len == SIZE_MAX` for NULL terminated string.
+ufbx_abi bool ufbx_open_file(ufbx_stream *stream, const char *path, size_t path_len, const ufbx_open_file_opts *opts, ufbx_error *error);
+ufbx_unsafe ufbx_abi bool ufbx_open_file_ctx(ufbx_stream *stream, ufbx_open_file_context ctx, const char *path, size_t path_len, const ufbx_open_file_opts *opts, ufbx_error *error);
+
+// NOTE: Uses the default ufbx allocator!
+ufbx_abi bool ufbx_open_memory(ufbx_stream *stream, const void *data, size_t data_size, const ufbx_open_memory_opts *opts, ufbx_error *error);
+ufbx_unsafe ufbx_abi bool ufbx_open_memory_ctx(ufbx_stream *stream, ufbx_open_file_context ctx, const void *data, size_t data_size, const ufbx_open_memory_opts *opts, ufbx_error *error);
+
+// Animation evaluation
+
+// Evaluate a single animation `curve` at a `time`.
+// Returns `default_value` only if `curve == NULL` or it has no keyframes.
+ufbx_abi ufbx_real ufbx_evaluate_curve(const ufbx_anim_curve *curve, double time, ufbx_real default_value);
+ufbx_abi ufbx_real ufbx_evaluate_curve_flags(const ufbx_anim_curve *curve, double time, ufbx_real default_value, uint32_t flags);
+
+// Evaluate a value from bundled animation curves.
+ufbx_abi ufbx_real ufbx_evaluate_anim_value_real(const ufbx_anim_value *anim_value, double time);
+ufbx_abi ufbx_vec3 ufbx_evaluate_anim_value_vec3(const ufbx_anim_value *anim_value, double time);
+ufbx_abi ufbx_real ufbx_evaluate_anim_value_real_flags(const ufbx_anim_value *anim_value, double time, uint32_t flags);
+ufbx_abi ufbx_vec3 ufbx_evaluate_anim_value_vec3_flags(const ufbx_anim_value *anim_value, double time, uint32_t flags);
+
+// Evaluate an animated property `name` from `element` at `time`.
+// NOTE: If the property is not found it will have the flag `UFBX_PROP_FLAG_NOT_FOUND`.
+ufbx_abi ufbx_prop ufbx_evaluate_prop_len(const ufbx_anim *anim, const ufbx_element *element, const char *name, size_t name_len, double time);
+ufbx_abi ufbx_prop ufbx_evaluate_prop(const ufbx_anim *anim, const ufbx_element *element, const char *name, double time);
+ufbx_abi ufbx_prop ufbx_evaluate_prop_len_flags(const ufbx_anim *anim, const ufbx_element *element, const char *name, size_t name_len, double time, uint32_t flags);
+ufbx_abi ufbx_prop ufbx_evaluate_prop_flags(const ufbx_anim *anim, const ufbx_element *element, const char *name, double time, uint32_t flags);
+
+// Evaluate all _animated_ properties of `element`.
+// HINT: This function returns an `ufbx_props` structure with the original properties as
+// `ufbx_props.defaults`. This lets you use `ufbx_find_prop/value()` for the results.
+ufbx_abi ufbx_props ufbx_evaluate_props(const ufbx_anim *anim, const ufbx_element *element, double time, ufbx_prop *buffer, size_t buffer_size);
+ufbx_abi ufbx_props ufbx_evaluate_props_flags(const ufbx_anim *anim, const ufbx_element *element, double time, ufbx_prop *buffer, size_t buffer_size, uint32_t flags);
+
+// Flags to control `ufbx_evaluate_transform_flags()`.
+typedef enum ufbx_transform_flags UFBX_FLAG_REPR {
+
+ // Ignore parent scale helper.
+ UFBX_TRANSFORM_FLAG_IGNORE_SCALE_HELPER = 0x1,
+
+ // Ignore componentwise scale.
+ // Note that if you don't specify this, ufbx will have to potentially
+ // evaluate the entire parent chain in the worst case.
+ UFBX_TRANSFORM_FLAG_IGNORE_COMPONENTWISE_SCALE = 0x2,
+
+ // Require explicit components
+ UFBX_TRANSFORM_FLAG_EXPLICIT_INCLUDES = 0x4,
+
+ // If `UFBX_TRANSFORM_FLAG_EXPLICIT_INCLUDES`: Evaluate `ufbx_transform.translation`.
+ UFBX_TRANSFORM_FLAG_INCLUDE_TRANSLATION = 0x10,
+ // If `UFBX_TRANSFORM_FLAG_EXPLICIT_INCLUDES`: Evaluate `ufbx_transform.rotation`.
+ UFBX_TRANSFORM_FLAG_INCLUDE_ROTATION = 0x20,
+ // If `UFBX_TRANSFORM_FLAG_EXPLICIT_INCLUDES`: Evaluate `ufbx_transform.scale`.
+ UFBX_TRANSFORM_FLAG_INCLUDE_SCALE = 0x40,
+
+ // Do not extrapolate keyframes.
+ // See `UFBX_EVALUATE_FLAG_NO_EXTRAPOLATION`.
+ UFBX_TRANSFORM_FLAG_NO_EXTRAPOLATION = 0x80,
+
+ UFBX_FLAG_FORCE_WIDTH(UFBX_TRANSFORM_FLAGS)
+} ufbx_transform_flags;
+
+// Evaluate the animated transform of a node given a time.
+// The returned transform is the local transform of the node (ie. relative to the parent),
+// comparable to `ufbx_node.local_transform`.
+ufbx_abi ufbx_transform ufbx_evaluate_transform(const ufbx_anim *anim, const ufbx_node *node, double time);
+ufbx_abi ufbx_transform ufbx_evaluate_transform_flags(const ufbx_anim *anim, const ufbx_node *node, double time, uint32_t flags);
+
+// Evaluate the blend shape weight of a blend channel.
+// NOTE: Return value uses `1.0` for full weight, instead of `100.0` that the internal property `UFBX_Weight` uses.
+ufbx_abi ufbx_real ufbx_evaluate_blend_weight(const ufbx_anim *anim, const ufbx_blend_channel *channel, double time);
+ufbx_abi ufbx_real ufbx_evaluate_blend_weight_flags(const ufbx_anim *anim, const ufbx_blend_channel *channel, double time, uint32_t flags);
+
+// Evaluate the whole `scene` at a specific `time` in the animation `anim`.
+// The returned scene behaves as if it had been exported at a specific time
+// in the specified animation, except that animated elements' properties contain
+// only the animated values, the original ones are in `props->defaults`.
+//
+// NOTE: The returned scene refers to the original `scene` so the original
+// scene cannot be freed until all evaluated scenes are freed.
+ufbx_abi ufbx_scene *ufbx_evaluate_scene(const ufbx_scene *scene, const ufbx_anim *anim, double time, const ufbx_evaluate_opts *opts, ufbx_error *error);
+
+// Create a custom animation descriptor.
+// `ufbx_anim_opts` is used to specify animation layers and weights.
+// HINT: You can also leave `ufbx_anim_opts.layer_ids[]` empty and only specify
+// overrides to evaluate the scene with different properties or local transforms.
+ufbx_abi ufbx_anim *ufbx_create_anim(const ufbx_scene *scene, const ufbx_anim_opts *opts, ufbx_error *error);
+
+// Free an animation returned by `ufbx_create_anim()`.
+ufbx_abi void ufbx_free_anim(ufbx_anim *anim);
+
+// Increase the animation reference count.
+ufbx_abi void ufbx_retain_anim(ufbx_anim *anim);
+
+// Animation baking
+
+// "Bake" an animation to linearly interpolated keyframes.
+// Composites the FBX transformation chain into quaternion rotations.
+ufbx_abi ufbx_baked_anim *ufbx_bake_anim(const ufbx_scene *scene, const ufbx_anim *anim, const ufbx_bake_opts *opts, ufbx_error *error);
+
+ufbx_abi void ufbx_retain_baked_anim(ufbx_baked_anim *bake);
+ufbx_abi void ufbx_free_baked_anim(ufbx_baked_anim *bake);
+
+ufbx_abi ufbx_baked_node *ufbx_find_baked_node_by_typed_id(ufbx_baked_anim *bake, uint32_t typed_id);
+ufbx_abi ufbx_baked_node *ufbx_find_baked_node(ufbx_baked_anim *bake, ufbx_node *node);
+
+ufbx_abi ufbx_baked_element *ufbx_find_baked_element_by_element_id(ufbx_baked_anim *bake, uint32_t element_id);
+ufbx_abi ufbx_baked_element *ufbx_find_baked_element(ufbx_baked_anim *bake, ufbx_element *element);
+
+// Evaluate baked animation `keyframes` at `time`.
+// Internally linearly interpolates between two adjacent keyframes.
+// Handles stepped tangents cleanly, which is not strictly necessary for custom interpolation.
+ufbx_abi ufbx_vec3 ufbx_evaluate_baked_vec3(ufbx_baked_vec3_list keyframes, double time);
+
+// Evaluate baked animation `keyframes` at `time`.
+// Internally spherically interpolates (`ufbx_quat_slerp()`) between two adjacent keyframes.
+// Handles stepped tangents cleanly, which is not strictly necessary for custom interpolation.
+ufbx_abi ufbx_quat ufbx_evaluate_baked_quat(ufbx_baked_quat_list keyframes, double time);
+
+// Poses
+
+// Retrieve the bone pose for `node`.
+// Returns `NULL` if the pose does not contain `node`.
+ufbx_abi ufbx_bone_pose *ufbx_get_bone_pose(const ufbx_pose *pose, const ufbx_node *node);
+
+// Materials
+
+// Find a texture for a given material FBX property.
+ufbx_abi ufbx_texture *ufbx_find_prop_texture_len(const ufbx_material *material, const char *name, size_t name_len);
+ufbx_abi ufbx_texture *ufbx_find_prop_texture(const ufbx_material *material, const char *name);
+
+// Find a texture for a given shader property.
+ufbx_abi ufbx_string ufbx_find_shader_prop_len(const ufbx_shader *shader, const char *name, size_t name_len);
+ufbx_abi ufbx_string ufbx_find_shader_prop(const ufbx_shader *shader, const char *name);
+
+// Map from a shader property to material property.
+ufbx_abi ufbx_shader_prop_binding_list ufbx_find_shader_prop_bindings_len(const ufbx_shader *shader, const char *name, size_t name_len);
+ufbx_abi ufbx_shader_prop_binding_list ufbx_find_shader_prop_bindings(const ufbx_shader *shader, const char *name);
+
+// Find an input in a shader texture.
+ufbx_abi ufbx_shader_texture_input *ufbx_find_shader_texture_input_len(const ufbx_shader_texture *shader, const char *name, size_t name_len);
+ufbx_abi ufbx_shader_texture_input *ufbx_find_shader_texture_input(const ufbx_shader_texture *shader, const char *name);
+
+// Math
+
+// Returns `true` if `axes` forms a valid coordinate space.
+ufbx_abi bool ufbx_coordinate_axes_valid(ufbx_coordinate_axes axes);
+
+// Vector math utility functions.
+ufbx_abi ufbx_vec3 ufbx_vec3_normalize(ufbx_vec3 v);
+
+// Quaternion math utility functions.
+ufbx_abi ufbx_real ufbx_quat_dot(ufbx_quat a, ufbx_quat b);
+ufbx_abi ufbx_quat ufbx_quat_mul(ufbx_quat a, ufbx_quat b);
+ufbx_abi ufbx_quat ufbx_quat_normalize(ufbx_quat q);
+ufbx_abi ufbx_quat ufbx_quat_fix_antipodal(ufbx_quat q, ufbx_quat reference);
+ufbx_abi ufbx_quat ufbx_quat_slerp(ufbx_quat a, ufbx_quat b, ufbx_real t);
+ufbx_abi ufbx_vec3 ufbx_quat_rotate_vec3(ufbx_quat q, ufbx_vec3 v);
+ufbx_abi ufbx_vec3 ufbx_quat_to_euler(ufbx_quat q, ufbx_rotation_order order);
+ufbx_abi ufbx_quat ufbx_euler_to_quat(ufbx_vec3 v, ufbx_rotation_order order);
+
+// Matrix math utility functions.
+ufbx_abi ufbx_matrix ufbx_matrix_mul(const ufbx_matrix *a, const ufbx_matrix *b);
+ufbx_abi ufbx_real ufbx_matrix_determinant(const ufbx_matrix *m);
+ufbx_abi ufbx_matrix ufbx_matrix_invert(const ufbx_matrix *m);
+
+// Get a matrix that can be used to transform geometry normals.
+// NOTE: You must normalize the normals after transforming them with this matrix,
+// eg. using `ufbx_vec3_normalize()`.
+// NOTE: This function flips the normals if the determinant is negative.
+ufbx_abi ufbx_matrix ufbx_matrix_for_normals(const ufbx_matrix *m);
+
+// Matrix transformation utilities.
+ufbx_abi ufbx_vec3 ufbx_transform_position(const ufbx_matrix *m, ufbx_vec3 v);
+ufbx_abi ufbx_vec3 ufbx_transform_direction(const ufbx_matrix *m, ufbx_vec3 v);
+
+// Conversions between `ufbx_matrix` and `ufbx_transform`.
+ufbx_abi ufbx_matrix ufbx_transform_to_matrix(const ufbx_transform *t);
+ufbx_abi ufbx_transform ufbx_matrix_to_transform(const ufbx_matrix *m);
+
+// Skinning
+
+// Get a matrix representing the deformation for a single vertex.
+// Returns `fallback` if the vertex is not skinned.
+ufbx_abi ufbx_matrix ufbx_catch_get_skin_vertex_matrix(ufbx_panic *panic, const ufbx_skin_deformer *skin, size_t vertex, const ufbx_matrix *fallback);
+ufbx_inline ufbx_matrix ufbx_get_skin_vertex_matrix(const ufbx_skin_deformer *skin, size_t vertex, const ufbx_matrix *fallback) {
+ return ufbx_catch_get_skin_vertex_matrix(NULL, skin, vertex, fallback);
+}
+
+// Resolve the index into `ufbx_blend_shape.position_offsets[]` given a vertex.
+// Returns `UFBX_NO_INDEX` if the vertex is not included in the blend shape.
+ufbx_abi uint32_t ufbx_get_blend_shape_offset_index(const ufbx_blend_shape *shape, size_t vertex);
+
+// Get the offset for a given vertex in the blend shape.
+// Returns `ufbx_zero_vec3` if the vertex is not a included in the blend shape.
+ufbx_abi ufbx_vec3 ufbx_get_blend_shape_vertex_offset(const ufbx_blend_shape *shape, size_t vertex);
+
+// Get the _current_ blend offset given a blend deformer.
+// NOTE: This depends on the current animated blend weight of the deformer.
+ufbx_abi ufbx_vec3 ufbx_get_blend_vertex_offset(const ufbx_blend_deformer *blend, size_t vertex);
+
+// Apply the blend shape with `weight` to given vertices.
+ufbx_abi void ufbx_add_blend_shape_vertex_offsets(const ufbx_blend_shape *shape, ufbx_vec3 *vertices, size_t num_vertices, ufbx_real weight);
+
+// Apply the blend deformer with `weight` to given vertices.
+// NOTE: This depends on the current animated blend weight of the deformer.
+ufbx_abi void ufbx_add_blend_vertex_offsets(const ufbx_blend_deformer *blend, ufbx_vec3 *vertices, size_t num_vertices, ufbx_real weight);
+
+// Curves/surfaces
+
+// Low-level utility to evaluate NURBS the basis functions.
+ufbx_abi size_t ufbx_evaluate_nurbs_basis(const ufbx_nurbs_basis *basis, ufbx_real u, ufbx_real *weights, size_t num_weights, ufbx_real *derivatives, size_t num_derivatives);
+
+// Evaluate a point on a NURBS curve given the parameter `u`.
+ufbx_abi ufbx_curve_point ufbx_evaluate_nurbs_curve(const ufbx_nurbs_curve *curve, ufbx_real u);
+
+// Evaluate a point on a NURBS surface given the parameter `u` and `v`.
+ufbx_abi ufbx_surface_point ufbx_evaluate_nurbs_surface(const ufbx_nurbs_surface *surface, ufbx_real u, ufbx_real v);
+
+// Tessellate a NURBS curve into a polyline.
+ufbx_abi ufbx_line_curve *ufbx_tessellate_nurbs_curve(const ufbx_nurbs_curve *curve, const ufbx_tessellate_curve_opts *opts, ufbx_error *error);
+
+// Tessellate a NURBS surface into a mesh.
+ufbx_abi ufbx_mesh *ufbx_tessellate_nurbs_surface(const ufbx_nurbs_surface *surface, const ufbx_tessellate_surface_opts *opts, ufbx_error *error);
+
+// Free a line returned by `ufbx_tessellate_nurbs_curve()`.
+ufbx_abi void ufbx_free_line_curve(ufbx_line_curve *curve);
+
+// Increase the refcount of the line.
+ufbx_abi void ufbx_retain_line_curve(ufbx_line_curve *curve);
+
+// Mesh Topology
+
+// Find the face that contains a given `index`.
+// Returns `UFBX_NO_INDEX` if out of bounds.
+ufbx_abi uint32_t ufbx_find_face_index(ufbx_mesh *mesh, size_t index);
+
+// Triangulate a mesh face, returning the number of triangles.
+// NOTE: You need to space for `(face.num_indices - 2) * 3 - 1` indices!
+// HINT: Using `ufbx_mesh.max_face_triangles * 3` is always safe.
+ufbx_abi uint32_t ufbx_catch_triangulate_face(ufbx_panic *panic, uint32_t *indices, size_t num_indices, const ufbx_mesh *mesh, ufbx_face face);
+ufbx_abi uint32_t ufbx_triangulate_face(uint32_t *indices, size_t num_indices, const ufbx_mesh *mesh, ufbx_face face);
+
+// Generate the half-edge representation of `mesh` to `topo[mesh->num_indices]`
+ufbx_abi void ufbx_catch_compute_topology(ufbx_panic *panic, const ufbx_mesh *mesh, ufbx_topo_edge *topo, size_t num_topo);
+ufbx_abi void ufbx_compute_topology(const ufbx_mesh *mesh, ufbx_topo_edge *topo, size_t num_topo);
+
+// Get the next/previous edge around a vertex
+// NOTE: Does not return the half-edge on the opposite side (ie. `topo[index].twin`)
+
+// Get the next half-edge in `topo`.
+ufbx_abi uint32_t ufbx_catch_topo_next_vertex_edge(ufbx_panic *panic, const ufbx_topo_edge *topo, size_t num_topo, uint32_t index);
+ufbx_abi uint32_t ufbx_topo_next_vertex_edge(const ufbx_topo_edge *topo, size_t num_topo, uint32_t index);
+
+// Get the previous half-edge in `topo`.
+ufbx_abi uint32_t ufbx_catch_topo_prev_vertex_edge(ufbx_panic *panic, const ufbx_topo_edge *topo, size_t num_topo, uint32_t index);
+ufbx_abi uint32_t ufbx_topo_prev_vertex_edge(const ufbx_topo_edge *topo, size_t num_topo, uint32_t index);
+
+// Calculate a normal for a given face.
+// The returned normal is weighted by face area.
+ufbx_abi ufbx_vec3 ufbx_catch_get_weighted_face_normal(ufbx_panic *panic, const ufbx_vertex_vec3 *positions, ufbx_face face);
+ufbx_abi ufbx_vec3 ufbx_get_weighted_face_normal(const ufbx_vertex_vec3 *positions, ufbx_face face);
+
+// Generate indices for normals from the topology.
+// Respects smoothing groups.
+ufbx_abi size_t ufbx_catch_generate_normal_mapping(ufbx_panic *panic, const ufbx_mesh *mesh,
+ const ufbx_topo_edge *topo, size_t num_topo,
+ uint32_t *normal_indices, size_t num_normal_indices, bool assume_smooth);
+ufbx_abi size_t ufbx_generate_normal_mapping(const ufbx_mesh *mesh,
+ const ufbx_topo_edge *topo, size_t num_topo,
+ uint32_t *normal_indices, size_t num_normal_indices, bool assume_smooth);
+
+// Compute normals given normal indices.
+// You can use `ufbx_generate_normal_mapping()` to generate the normal indices.
+ufbx_abi void ufbx_catch_compute_normals(ufbx_panic *panic, const ufbx_mesh *mesh, const ufbx_vertex_vec3 *positions,
+ const uint32_t *normal_indices, size_t num_normal_indices,
+ ufbx_vec3 *normals, size_t num_normals);
+ufbx_abi void ufbx_compute_normals(const ufbx_mesh *mesh, const ufbx_vertex_vec3 *positions,
+ const uint32_t *normal_indices, size_t num_normal_indices,
+ ufbx_vec3 *normals, size_t num_normals);
+
+// Subdivide a mesh using the Catmull-Clark subdivision `level` times.
+ufbx_abi ufbx_mesh *ufbx_subdivide_mesh(const ufbx_mesh *mesh, size_t level, const ufbx_subdivide_opts *opts, ufbx_error *error);
+
+// Free a mesh returned from `ufbx_subdivide_mesh()` or `ufbx_tessellate_nurbs_surface()`.
+ufbx_abi void ufbx_free_mesh(ufbx_mesh *mesh);
+
+// Increase the mesh reference count.
+ufbx_abi void ufbx_retain_mesh(ufbx_mesh *mesh);
+
+// Geometry caches
+
+// Load geometry cache information from a file.
+// As geometry caches can be massive, this does not actually read the data, but
+// only seeks through the files to form the metadata.
+ufbx_abi ufbx_geometry_cache *ufbx_load_geometry_cache(
+ const char *filename,
+ const ufbx_geometry_cache_opts *opts, ufbx_error *error);
+ufbx_abi ufbx_geometry_cache *ufbx_load_geometry_cache_len(
+ const char *filename, size_t filename_len,
+ const ufbx_geometry_cache_opts *opts, ufbx_error *error);
+
+// Free a geometry cache returned from `ufbx_load_geometry_cache()`.
+ufbx_abi void ufbx_free_geometry_cache(ufbx_geometry_cache *cache);
+// Increase the geometry cache reference count.
+ufbx_abi void ufbx_retain_geometry_cache(ufbx_geometry_cache *cache);
+
+// Read a frame from a geometry cache.
+ufbx_abi size_t ufbx_read_geometry_cache_real(const ufbx_cache_frame *frame, ufbx_real *data, size_t num_data, const ufbx_geometry_cache_data_opts *opts);
+ufbx_abi size_t ufbx_read_geometry_cache_vec3(const ufbx_cache_frame *frame, ufbx_vec3 *data, size_t num_data, const ufbx_geometry_cache_data_opts *opts);
+// Sample the a geometry cache channel, linearly blending between adjacent frames.
+ufbx_abi size_t ufbx_sample_geometry_cache_real(const ufbx_cache_channel *channel, double time, ufbx_real *data, size_t num_data, const ufbx_geometry_cache_data_opts *opts);
+ufbx_abi size_t ufbx_sample_geometry_cache_vec3(const ufbx_cache_channel *channel, double time, ufbx_vec3 *data, size_t num_data, const ufbx_geometry_cache_data_opts *opts);
+
+// DOM
+
+// Find a DOM node given a name.
+ufbx_abi ufbx_dom_node *ufbx_dom_find_len(const ufbx_dom_node *parent, const char *name, size_t name_len);
+ufbx_abi ufbx_dom_node *ufbx_dom_find(const ufbx_dom_node *parent, const char *name);
+
+// Utility
+
+// Generate an index buffer for a flat vertex buffer.
+// `streams` specifies one or more vertex data arrays, each stream must contain `num_indices` vertices.
+// This function compacts the data within `streams` in-place, writing the deduplicated indices to `indices`.
+ufbx_abi size_t ufbx_generate_indices(const ufbx_vertex_stream *streams, size_t num_streams, uint32_t *indices, size_t num_indices, const ufbx_allocator_opts *allocator, ufbx_error *error);
+
+// Thread pool
+
+// Run a single thread pool task.
+// See `ufbx_thread_pool_run_fn` for more information.
+ufbx_unsafe ufbx_abi void ufbx_thread_pool_run_task(ufbx_thread_pool_context ctx, uint32_t index);
+
+// Get or set an arbitrary user pointer for the thread pool context.
+// `ufbx_thread_pool_get_user_ptr()` returns `NULL` if unset.
+ufbx_unsafe ufbx_abi void ufbx_thread_pool_set_user_ptr(ufbx_thread_pool_context ctx, void *user_ptr);
+ufbx_unsafe ufbx_abi void *ufbx_thread_pool_get_user_ptr(ufbx_thread_pool_context ctx);
+
+// -- Inline API
+
+// Utility functions for reading geometry data for a single index.
+ufbx_abi ufbx_real ufbx_catch_get_vertex_real(ufbx_panic *panic, const ufbx_vertex_real *v, size_t index);
+ufbx_abi ufbx_vec2 ufbx_catch_get_vertex_vec2(ufbx_panic *panic, const ufbx_vertex_vec2 *v, size_t index);
+ufbx_abi ufbx_vec3 ufbx_catch_get_vertex_vec3(ufbx_panic *panic, const ufbx_vertex_vec3 *v, size_t index);
+ufbx_abi ufbx_vec4 ufbx_catch_get_vertex_vec4(ufbx_panic *panic, const ufbx_vertex_vec4 *v, size_t index);
+
+// Utility functions for reading geometry data for a single index.
+ufbx_inline ufbx_real ufbx_get_vertex_real(const ufbx_vertex_real *v, size_t index) { ufbx_assert(index < v->indices.count); return v->values.data[(int32_t)v->indices.data[index]]; }
+ufbx_inline ufbx_vec2 ufbx_get_vertex_vec2(const ufbx_vertex_vec2 *v, size_t index) { ufbx_assert(index < v->indices.count); return v->values.data[(int32_t)v->indices.data[index]]; }
+ufbx_inline ufbx_vec3 ufbx_get_vertex_vec3(const ufbx_vertex_vec3 *v, size_t index) { ufbx_assert(index < v->indices.count); return v->values.data[(int32_t)v->indices.data[index]]; }
+ufbx_inline ufbx_vec4 ufbx_get_vertex_vec4(const ufbx_vertex_vec4 *v, size_t index) { ufbx_assert(index < v->indices.count); return v->values.data[(int32_t)v->indices.data[index]]; }
+
+ufbx_abi ufbx_real ufbx_catch_get_vertex_w_vec3(ufbx_panic *panic, const ufbx_vertex_vec3 *v, size_t index);
+ufbx_inline ufbx_real ufbx_get_vertex_w_vec3(const ufbx_vertex_vec3 *v, size_t index) { ufbx_assert(index < v->indices.count); return v->values_w.count > 0 ? v->values_w.data[(int32_t)v->indices.data[index]] : 0.0f; }
+
+// Functions for converting an untyped `ufbx_element` to a concrete type.
+// Returns `NULL` if the element is not that type.
+ufbx_abi ufbx_unknown *ufbx_as_unknown(const ufbx_element *element);
+ufbx_abi ufbx_node *ufbx_as_node(const ufbx_element *element);
+ufbx_abi ufbx_mesh *ufbx_as_mesh(const ufbx_element *element);
+ufbx_abi ufbx_light *ufbx_as_light(const ufbx_element *element);
+ufbx_abi ufbx_camera *ufbx_as_camera(const ufbx_element *element);
+ufbx_abi ufbx_bone *ufbx_as_bone(const ufbx_element *element);
+ufbx_abi ufbx_empty *ufbx_as_empty(const ufbx_element *element);
+ufbx_abi ufbx_line_curve *ufbx_as_line_curve(const ufbx_element *element);
+ufbx_abi ufbx_nurbs_curve *ufbx_as_nurbs_curve(const ufbx_element *element);
+ufbx_abi ufbx_nurbs_surface *ufbx_as_nurbs_surface(const ufbx_element *element);
+ufbx_abi ufbx_nurbs_trim_surface *ufbx_as_nurbs_trim_surface(const ufbx_element *element);
+ufbx_abi ufbx_nurbs_trim_boundary *ufbx_as_nurbs_trim_boundary(const ufbx_element *element);
+ufbx_abi ufbx_procedural_geometry *ufbx_as_procedural_geometry(const ufbx_element *element);
+ufbx_abi ufbx_stereo_camera *ufbx_as_stereo_camera(const ufbx_element *element);
+ufbx_abi ufbx_camera_switcher *ufbx_as_camera_switcher(const ufbx_element *element);
+ufbx_abi ufbx_marker *ufbx_as_marker(const ufbx_element *element);
+ufbx_abi ufbx_lod_group *ufbx_as_lod_group(const ufbx_element *element);
+ufbx_abi ufbx_skin_deformer *ufbx_as_skin_deformer(const ufbx_element *element);
+ufbx_abi ufbx_skin_cluster *ufbx_as_skin_cluster(const ufbx_element *element);
+ufbx_abi ufbx_blend_deformer *ufbx_as_blend_deformer(const ufbx_element *element);
+ufbx_abi ufbx_blend_channel *ufbx_as_blend_channel(const ufbx_element *element);
+ufbx_abi ufbx_blend_shape *ufbx_as_blend_shape(const ufbx_element *element);
+ufbx_abi ufbx_cache_deformer *ufbx_as_cache_deformer(const ufbx_element *element);
+ufbx_abi ufbx_cache_file *ufbx_as_cache_file(const ufbx_element *element);
+ufbx_abi ufbx_material *ufbx_as_material(const ufbx_element *element);
+ufbx_abi ufbx_texture *ufbx_as_texture(const ufbx_element *element);
+ufbx_abi ufbx_video *ufbx_as_video(const ufbx_element *element);
+ufbx_abi ufbx_shader *ufbx_as_shader(const ufbx_element *element);
+ufbx_abi ufbx_shader_binding *ufbx_as_shader_binding(const ufbx_element *element);
+ufbx_abi ufbx_anim_stack *ufbx_as_anim_stack(const ufbx_element *element);
+ufbx_abi ufbx_anim_layer *ufbx_as_anim_layer(const ufbx_element *element);
+ufbx_abi ufbx_anim_value *ufbx_as_anim_value(const ufbx_element *element);
+ufbx_abi ufbx_anim_curve *ufbx_as_anim_curve(const ufbx_element *element);
+ufbx_abi ufbx_display_layer *ufbx_as_display_layer(const ufbx_element *element);
+ufbx_abi ufbx_selection_set *ufbx_as_selection_set(const ufbx_element *element);
+ufbx_abi ufbx_selection_node *ufbx_as_selection_node(const ufbx_element *element);
+ufbx_abi ufbx_character *ufbx_as_character(const ufbx_element *element);
+ufbx_abi ufbx_constraint *ufbx_as_constraint(const ufbx_element *element);
+ufbx_abi ufbx_audio_layer *ufbx_as_audio_layer(const ufbx_element *element);
+ufbx_abi ufbx_audio_clip *ufbx_as_audio_clip(const ufbx_element *element);
+ufbx_abi ufbx_pose *ufbx_as_pose(const ufbx_element *element);
+ufbx_abi ufbx_metadata_object *ufbx_as_metadata_object(const ufbx_element *element);
+
+#ifdef __cplusplus
+}
+#endif
+
+// bindgen-disable
+
+#if UFBX_CPP11
+
+struct ufbx_string_view {
+ const char *data;
+ size_t length;
+
+ ufbx_string_view() : data(nullptr), length(0) { }
+ ufbx_string_view(const char *data_, size_t length_) : data(data_), length(length_) { }
+ UFBX_CONVERSION_TO_IMPL(ufbx_string_view)
+};
+
+ufbx_inline ufbx_scene *ufbx_load_file(ufbx_string_view filename, const ufbx_load_opts *opts, ufbx_error *error) { return ufbx_load_file_len(filename.data, filename.length, opts, error); }
+ufbx_inline ufbx_prop *ufbx_find_prop(const ufbx_props *props, ufbx_string_view name) { return ufbx_find_prop_len(props, name.data, name.length); }
+ufbx_inline ufbx_real ufbx_find_real(const ufbx_props *props, ufbx_string_view name, ufbx_real def) { return ufbx_find_real_len(props, name.data, name.length, def); }
+ufbx_inline ufbx_vec3 ufbx_find_vec3(const ufbx_props *props, ufbx_string_view name, ufbx_vec3 def) { return ufbx_find_vec3_len(props, name.data, name.length, def); }
+ufbx_inline int64_t ufbx_find_int(const ufbx_props *props, ufbx_string_view name, int64_t def) { return ufbx_find_int_len(props, name.data, name.length, def); }
+ufbx_inline bool ufbx_find_bool(const ufbx_props *props, ufbx_string_view name, bool def) { return ufbx_find_bool_len(props, name.data, name.length, def); }
+ufbx_inline ufbx_string ufbx_find_string(const ufbx_props *props, ufbx_string_view name, ufbx_string def) { return ufbx_find_string_len(props, name.data, name.length, def); }
+ufbx_inline ufbx_blob ufbx_find_blob(const ufbx_props *props, ufbx_string_view name, ufbx_blob def) { return ufbx_find_blob_len(props, name.data, name.length, def); }
+ufbx_inline ufbx_element *ufbx_find_prop_element(const ufbx_element *element, ufbx_string_view name, ufbx_element_type type) { return ufbx_find_prop_element_len(element, name.data, name.length, type); }
+ufbx_inline ufbx_element *ufbx_find_element(const ufbx_scene *scene, ufbx_element_type type, ufbx_string_view name) { return ufbx_find_element_len(scene, type, name.data, name.length); }
+ufbx_inline ufbx_node *ufbx_find_node(const ufbx_scene *scene, ufbx_string_view name) { return ufbx_find_node_len(scene, name.data, name.length); }
+ufbx_inline ufbx_anim_stack *ufbx_find_anim_stack(const ufbx_scene *scene, ufbx_string_view name) { return ufbx_find_anim_stack_len(scene, name.data, name.length); }
+ufbx_inline ufbx_material *ufbx_find_material(const ufbx_scene *scene, ufbx_string_view name) { return ufbx_find_material_len(scene, name.data, name.length); }
+ufbx_inline ufbx_anim_prop *ufbx_find_anim_prop(const ufbx_anim_layer *layer, const ufbx_element *element, ufbx_string_view prop) { return ufbx_find_anim_prop_len(layer, element, prop.data, prop.length); }
+ufbx_inline ufbx_prop ufbx_evaluate_prop(const ufbx_anim *anim, const ufbx_element *element, ufbx_string_view name, double time) { return ufbx_evaluate_prop_len(anim, element, name.data, name.length, time); }
+ufbx_inline ufbx_texture *ufbx_find_prop_texture(const ufbx_material *material, ufbx_string_view name) { return ufbx_find_prop_texture_len(material, name.data, name.length); }
+ufbx_inline ufbx_string ufbx_find_shader_prop(const ufbx_shader *shader, ufbx_string_view name) { return ufbx_find_shader_prop_len(shader, name.data, name.length); }
+ufbx_inline ufbx_shader_prop_binding_list ufbx_find_shader_prop_bindings(const ufbx_shader *shader, ufbx_string_view name) { return ufbx_find_shader_prop_bindings_len(shader, name.data, name.length); }
+ufbx_inline ufbx_shader_texture_input *ufbx_find_shader_texture_input(const ufbx_shader_texture *shader, ufbx_string_view name) { return ufbx_find_shader_texture_input_len(shader, name.data, name.length); }
+ufbx_inline ufbx_geometry_cache *ufbx_load_geometry_cache(ufbx_string_view filename, const ufbx_geometry_cache_opts *opts, ufbx_error *error) { return ufbx_load_geometry_cache_len(filename.data, filename.length, opts, error); }
+ufbx_inline ufbx_dom_node *ufbx_dom_find(const ufbx_dom_node *parent, ufbx_string_view name) { return ufbx_dom_find_len(parent, name.data, name.length); }
+
+#endif
+
+#if UFBX_CPP11
+
+template <typename T>
+struct ufbx_type_traits { enum { valid = 0 }; };
+
+template<> struct ufbx_type_traits<ufbx_scene> {
+ enum { valid = 1 };
+ static void retain(ufbx_scene *ptr) { ufbx_retain_scene(ptr); }
+ static void free(ufbx_scene *ptr) { ufbx_free_scene(ptr); }
+};
+
+template<> struct ufbx_type_traits<ufbx_mesh> {
+ enum { valid = 1 };
+ static void retain(ufbx_mesh *ptr) { ufbx_retain_mesh(ptr); }
+ static void free(ufbx_mesh *ptr) { ufbx_free_mesh(ptr); }
+};
+
+template<> struct ufbx_type_traits<ufbx_line_curve> {
+ enum { valid = 1 };
+ static void retain(ufbx_line_curve *ptr) { ufbx_retain_line_curve(ptr); }
+ static void free(ufbx_line_curve *ptr) { ufbx_free_line_curve(ptr); }
+};
+
+template<> struct ufbx_type_traits<ufbx_geometry_cache> {
+ enum { valid = 1 };
+ static void retain(ufbx_geometry_cache *ptr) { ufbx_retain_geometry_cache(ptr); }
+ static void free(ufbx_geometry_cache *ptr) { ufbx_free_geometry_cache(ptr); }
+};
+
+template<> struct ufbx_type_traits<ufbx_anim> {
+ enum { valid = 1 };
+ static void retain(ufbx_anim *ptr) { ufbx_retain_anim(ptr); }
+ static void free(ufbx_anim *ptr) { ufbx_free_anim(ptr); }
+};
+
+template<> struct ufbx_type_traits<ufbx_baked_anim> {
+ enum { valid = 1 };
+ static void retain(ufbx_baked_anim *ptr) { ufbx_retain_baked_anim(ptr); }
+ static void free(ufbx_baked_anim *ptr) { ufbx_free_baked_anim(ptr); }
+};
+
+class ufbx_deleter {
+public:
+ template <typename T>
+ void operator()(T *ptr) const {
+ static_assert(ufbx_type_traits<T>::valid, "ufbx_deleter() unsupported for type");
+ ufbx_type_traits<T>::free(ptr);
+ }
+};
+
+// RAII wrapper over refcounted ufbx types.
+
+// Behaves like `std::unique_ptr<T>`.
+template <typename T>
+class ufbx_unique_ptr {
+ T *ptr;
+ using traits = ufbx_type_traits<T>;
+ static_assert(ufbx_type_traits<T>::valid, "ufbx_unique_ptr unsupported for type");
+public:
+ ufbx_unique_ptr() noexcept : ptr(nullptr) { }
+ explicit ufbx_unique_ptr(T *ptr_) noexcept : ptr(ptr_) { }
+ ufbx_unique_ptr(ufbx_unique_ptr &&ref) noexcept : ptr(ref.ptr) { ref.ptr = nullptr; }
+ ~ufbx_unique_ptr() { traits::free(ptr); }
+
+ ufbx_unique_ptr &operator=(ufbx_unique_ptr &&ref) noexcept {
+ if (&ref == this) return *this;
+ ptr = ref.ptr;
+ ref.ptr = nullptr;
+ return *this;
+ }
+
+ void reset(T *new_ptr=nullptr) noexcept {
+ traits::free(ptr);
+ ptr = new_ptr;
+ }
+
+ void swap(ufbx_unique_ptr &ref) noexcept {
+ T *tmp = ptr;
+ ptr = ref.ptr;
+ ref.ptr = tmp;
+ }
+
+ T &operator*() const noexcept { return *ptr; }
+ T *operator->() const noexcept { return ptr; }
+ T *get() const noexcept { return ptr; }
+ explicit operator bool() const noexcept { return ptr != nullptr; }
+};
+
+// Behaves like `std::shared_ptr<T>` except uses ufbx's internal reference counting,
+// so it is half the size of a standard `shared_ptr` but might be marginally slower.
+template <typename T>
+class ufbx_shared_ptr {
+ T *ptr;
+ using traits = ufbx_type_traits<T>;
+ static_assert(ufbx_type_traits<T>::valid, "ufbx_shared_ptr unsupported for type");
+public:
+
+ ufbx_shared_ptr() noexcept : ptr(nullptr) { }
+ explicit ufbx_shared_ptr(T *ptr_) noexcept : ptr(ptr_) { }
+ ufbx_shared_ptr(const ufbx_shared_ptr &ref) noexcept : ptr(ref.ptr) { traits::retain(ref.ptr); }
+ ufbx_shared_ptr(ufbx_shared_ptr &&ref) noexcept : ptr(ref.ptr) { ref.ptr = nullptr; }
+ ~ufbx_shared_ptr() { traits::free(ptr); }
+
+ ufbx_shared_ptr &operator=(const ufbx_shared_ptr &ref) noexcept {
+ if (&ref == this) return *this;
+ traits::free(ptr);
+ traits::retain(ref.ptr);
+ ptr = ref.ptr;
+ return *this;
+ }
+
+ ufbx_shared_ptr &operator=(ufbx_shared_ptr &&ref) noexcept {
+ if (&ref == this) return *this;
+ ptr = ref.ptr;
+ ref.ptr = nullptr;
+ return *this;
+ }
+
+ void reset(T *new_ptr=nullptr) noexcept {
+ traits::free(ptr);
+ ptr = new_ptr;
+ }
+
+ void swap(ufbx_shared_ptr &ref) noexcept {
+ T *tmp = ptr;
+ ptr = ref.ptr;
+ ref.ptr = tmp;
+ }
+
+ T &operator*() const noexcept { return *ptr; }
+ T *operator->() const noexcept { return ptr; }
+ T *get() const noexcept { return ptr; }
+ explicit operator bool() const noexcept { return ptr != nullptr; }
+};
+
+#endif
+// bindgen-enable
+
+// -- Properties
+
+// Names of common properties in `ufbx_props`.
+// Some of these differ from ufbx interpretations.
+
+// Local translation.
+// Used by: `ufbx_node`
+#define UFBX_Lcl_Translation "Lcl Translation"
+
+// Local rotation expressed in Euler degrees.
+// Used by: `ufbx_node`
+// The rotation order is defined by the `UFBX_RotationOrder` property.
+#define UFBX_Lcl_Rotation "Lcl Rotation"
+
+// Local scaling factor, 3D vector.
+// Used by: `ufbx_node`
+#define UFBX_Lcl_Scaling "Lcl Scaling"
+
+// Euler rotation interpretation, used by `UFBX_Lcl_Rotation`.
+// Used by: `ufbx_node`, enum value `ufbx_rotation_order`.
+#define UFBX_RotationOrder "RotationOrder"
+
+// Scaling pivot: point around which scaling is performed.
+// Used by: `ufbx_node`.
+#define UFBX_ScalingPivot "ScalingPivot"
+
+// Scaling pivot: point around which rotation is performed.
+// Used by: `ufbx_node`.
+#define UFBX_RotationPivot "RotationPivot"
+
+// Scaling offset: translation added after scaling is performed.
+// Used by: `ufbx_node`.
+#define UFBX_ScalingOffset "ScalingOffset"
+
+// Rotation offset: translation added after rotation is performed.
+// Used by: `ufbx_node`.
+#define UFBX_RotationOffset "RotationOffset"
+
+// Pre-rotation: Rotation applied _after_ `UFBX_Lcl_Rotation`.
+// Used by: `ufbx_node`.
+// Affected by `UFBX_RotationPivot` but not `UFBX_RotationOrder`.
+#define UFBX_PreRotation "PreRotation"
+
+// Post-rotation: Rotation applied _before_ `UFBX_Lcl_Rotation`.
+// Used by: `ufbx_node`.
+// Affected by `UFBX_RotationPivot` but not `UFBX_RotationOrder`.
+#define UFBX_PostRotation "PostRotation"
+
+// Controls whether the node should be displayed or not.
+// Used by: `ufbx_node`.
+#define UFBX_Visibility "Visibility"
+
+// Weight of an animation layer in percentage (100.0 being full).
+// Used by: `ufbx_anim_layer`.
+#define UFBX_Weight "Weight"
+
+// Blend shape deformation weight (100.0 being full).
+// Used by: `ufbx_blend_channel`.
+#define UFBX_DeformPercent "DeformPercent"
+
+#if defined(_MSC_VER)
+ #pragma warning(pop)
+#elif defined(__clang__)
+ #pragma clang diagnostic pop
+#elif defined(__GNUC__)
+ #pragma GCC diagnostic pop
+#endif
+
+#endif
diff --git a/odin-c-bindgen/examples/ufbx/ufbx.lib b/odin-c-bindgen/examples/ufbx/ufbx.lib
Binary files differ.
diff --git a/odin-c-bindgen/examples/ufbx/ufbx/ufbx.odin b/odin-c-bindgen/examples/ufbx/ufbx/ufbx.odin
@@ -0,0 +1,5698 @@
+package ufbx
+
+import "core:c"
+
+_ :: c
+
+foreign import lib "ufbx.lib"
+
+// STDC :: _Stdc_Version
+
+CPP :: 0
+
+// PLATFORM_MSC :: Msc_Ver
+
+PLATFORM_GNUC :: 0
+
+CPP11 :: 0
+
+// ufbx_inline :: Static _Forceinline
+
+// ufbx_abi_data :: Extern
+
+REAL_TYPE :: f32
+
+// Limits for embedded arrays within structures.
+ERROR_STACK_MAX_DEPTH :: 8
+PANIC_MESSAGE_LENGTH :: 128
+ERROR_INFO_LENGTH :: 256
+
+// Number of thread groups to use if threading is enabled.
+// A thread group processes a number of tasks and is then waited and potentially
+// re-used later. In essence, this controls the granularity of threading.
+THREAD_GROUP_COUNT :: 4
+
+HAS_FORCE_32BIT :: 1
+
+// Version of the ufbx header.
+// `UFBX_VERSION` is simply an alias of `UFBX_HEADER_VERSION`.
+// `ufbx_source_version` contains the version of the corresponding source file.
+// HINT: The version can be compared numerically to the result of `ufbx_pack_version()`,
+// for example `#if UFBX_VERSION >= ufbx_pack_version(0, 12, 0)`.
+HEADER_VERSION :: (u32)(0)*1000000 + (u32)(18)*1000 + (u32)(0)
+// VERSION :: Ufbx_Header_Version
+
+// Main floating point type used everywhere in ufbx, defaults to `double`.
+// If you define `UFBX_REAL_IS_FLOAT` to any value, `ufbx_real` will be defined
+// as `float` instead.
+// You can also manually define `UFBX_REAL_TYPE` to any floating point type.
+Real :: f32
+
+// Null-terminated UTF-8 encoded string within an FBX file
+String :: struct {
+ data: cstring,
+ length: c.size_t,
+}
+
+// Opaque byte buffer blob
+Blob :: struct {
+ data: rawptr,
+ size: c.size_t,
+}
+
+// 2D vector
+Vec2 :: [2]Real
+
+// 3D vector
+Vec3 :: [3]Real
+
+// 4D vector
+Vec4 :: [4]Real
+
+// Quaternion
+Quat :: quaternion128
+
+// Order in which Euler-angle rotation axes are applied for a transform
+// NOTE: The order in the name refers to the order of axes *applied*,
+// not the multiplication order: eg. `UFBX_ROTATION_ORDER_XYZ` is `Z*Y*X`
+// [TODO: Figure out what the spheric rotation order is...]
+Rotation_Order :: enum c.int {
+ XYZ,
+ XZY,
+ YZX,
+ YXZ,
+ ZXY,
+ ZYX,
+ SPHERIC,
+ FORCE_32BIT = 2147483647,
+}
+
+ROTATION_ORDER_COUNT :: 7
+
+// Explicit translation+rotation+scale transformation.
+// NOTE: Rotation is a quaternion, not Euler angles!
+Transform :: struct {
+ translation: Vec3,
+ rotation: Quat,
+ scale: Vec3,
+}
+
+// 4x3 matrix encoding an affine transformation.
+// `cols[0..2]` are the X/Y/Z basis vectors, `cols[3]` is the translation
+Matrix :: struct {
+ using _: struct #raw_union {
+ using _: struct {
+ m00, m10, m20: Real,
+ m01, m11, m21: Real,
+ m02, m12, m22: Real,
+ m03, m13, m23: Real,
+ },
+ cols: [4]Vec3,
+ v: [12]Real,
+ },
+}
+
+Void_List :: struct {
+ data: [^]rawptr,
+ count: c.size_t,
+}
+
+Bool_List :: struct {
+ data: [^]bool,
+ count: c.size_t,
+}
+
+Uint32_List :: struct {
+ data: [^]u32,
+ count: c.size_t,
+}
+
+Real_List :: struct {
+ data: [^]Real,
+ count: c.size_t,
+}
+
+Vec2_List :: struct {
+ data: [^]Vec2,
+ count: c.size_t,
+}
+
+Vec3_List :: struct {
+ data: [^]Vec3,
+ count: c.size_t,
+}
+
+Vec4_List :: struct {
+ data: [^]Vec4,
+ count: c.size_t,
+}
+
+String_List :: struct {
+ data: [^]String,
+ count: c.size_t,
+}
+
+// Sentinel value used to represent a missing index.
+// NO_INDEX :: (u32)~0
+
+// -- Document object model
+Dom_Value_Type :: enum c.int {
+ NUMBER,
+ STRING,
+ ARRAY_I8,
+ ARRAY_I32,
+ ARRAY_I64,
+ ARRAY_F32,
+ ARRAY_F64,
+ ARRAY_RAW_STRING,
+ ARRAY_IGNORED,
+ TYPE_FORCE_32BIT = 2147483647,
+}
+
+DOM_VALUE_TYPE_COUNT :: 9
+
+Dom_Value :: struct {
+ type: Dom_Value_Type,
+ value_str: String,
+ value_blob: Blob,
+ value_int: i64,
+ value_float: f64,
+}
+
+Dom_Node_List :: struct {
+ data: ^^Dom_Node,
+ count: c.size_t,
+}
+
+Dom_Value_List :: struct {
+ data: [^]Dom_Value,
+ count: c.size_t,
+}
+
+Dom_Node :: struct {
+ name: String,
+ children: Dom_Node_List,
+ values: Dom_Value_List,
+}
+
+// Data type contained within the property. All the data fields are always
+// populated regardless of type, so there's no need to switch by type usually
+// eg. `prop->value_real` and `prop->value_int` have the same value (well, close)
+// if `prop->type == UFBX_PROP_INTEGER`. String values are not converted from/to.
+Prop_Type :: enum c.int {
+ UNKNOWN,
+ BOOLEAN,
+ INTEGER,
+ NUMBER,
+ VECTOR,
+ COLOR,
+ COLOR_WITH_ALPHA,
+ STRING,
+ DATE_TIME,
+ TRANSLATION,
+ ROTATION,
+ SCALING,
+ DISTANCE,
+ COMPOUND,
+ BLOB,
+ REFERENCE,
+ TYPE_FORCE_32BIT = 2147483647,
+}
+
+PROP_TYPE_COUNT :: 16
+
+// Property flags: Advanced information about properties, not usually needed.
+Prop_Flag :: enum c.int {
+ // Supports animation.
+ // NOTE: ufbx ignores this and allows animations on non-animatable properties.
+ ANIMATABLE = 0,
+
+ // User defined (custom) property.
+ USER_DEFINED = 1,
+
+ // Hidden in UI.
+ HIDDEN = 2,
+
+ // Disallow modification from UI for components.
+ LOCK_X = 4,
+
+ // Disallow modification from UI for components.
+ LOCK_Y = 5,
+
+ // Disallow modification from UI for components.
+ LOCK_Z = 6,
+
+ // Disallow modification from UI for components.
+ LOCK_W = 7,
+
+ // Disable animation from components.
+ MUTE_X = 8,
+
+ // Disable animation from components.
+ MUTE_Y = 9,
+
+ // Disable animation from components.
+ MUTE_Z = 10,
+
+ // Disable animation from components.
+ MUTE_W = 11,
+
+ // Property created by ufbx when an element has a connected `ufbx_anim_prop`
+ // but doesn't contain the `ufbx_prop` it's referring to.
+ // NOTE: The property may have been found in the templated defaults.
+ SYNTHETIC = 12,
+
+ // The property has at least one `ufbx_anim_prop` in some layer.
+ ANIMATED = 13,
+
+ // Used by `ufbx_evaluate_prop()` to indicate the the property was not found.
+ NOT_FOUND = 14,
+
+ // The property is connected to another one.
+ // This use case is relatively rare so `ufbx_prop` does not track connections
+ // directly. You can find connections from `ufbx_element.connections_dst` where
+ // `ufbx_connection.dst_prop` is this property and `ufbx_connection.src_prop` is defined.
+ CONNECTED = 15,
+
+ // The value of this property is undefined (represented as zero).
+ NO_VALUE = 16,
+
+ // This property has been overridden by the user.
+ // See `ufbx_anim.prop_overrides` for more information.
+ OVERRIDDEN = 17,
+
+ // Value type.
+ // `REAL/VEC2/VEC3/VEC4` are mutually exclusive but may coexist with eg. `STRING`
+ // in some rare cases where the string defines the unit for the vector.
+ VALUE_REAL = 20,
+
+ // Value type.
+ // `REAL/VEC2/VEC3/VEC4` are mutually exclusive but may coexist with eg. `STRING`
+ // in some rare cases where the string defines the unit for the vector.
+ VALUE_VEC2 = 21,
+
+ // Value type.
+ // `REAL/VEC2/VEC3/VEC4` are mutually exclusive but may coexist with eg. `STRING`
+ // in some rare cases where the string defines the unit for the vector.
+ VALUE_VEC3 = 22,
+
+ // Value type.
+ // `REAL/VEC2/VEC3/VEC4` are mutually exclusive but may coexist with eg. `STRING`
+ // in some rare cases where the string defines the unit for the vector.
+ VALUE_VEC4 = 23,
+
+ // Value type.
+ // `REAL/VEC2/VEC3/VEC4` are mutually exclusive but may coexist with eg. `STRING`
+ // in some rare cases where the string defines the unit for the vector.
+ VALUE_INT = 24,
+
+ // Value type.
+ // `REAL/VEC2/VEC3/VEC4` are mutually exclusive but may coexist with eg. `STRING`
+ // in some rare cases where the string defines the unit for the vector.
+ VALUE_STR = 25,
+
+ // Value type.
+ // `REAL/VEC2/VEC3/VEC4` are mutually exclusive but may coexist with eg. `STRING`
+ // in some rare cases where the string defines the unit for the vector.
+ VALUE_BLOB = 26,
+}
+
+Prop_Flags :: distinct bit_set[Prop_Flag; c.int]
+
+PROP_FLAGS_FORCE_32BIT :: Prop_Flags { .ANIMATABLE, .USER_DEFINED, .HIDDEN, .LOCK_X, .LOCK_Y, .LOCK_Z, .LOCK_W, .MUTE_X, .MUTE_Y, .MUTE_Z, .MUTE_W, .SYNTHETIC, .ANIMATED, .NOT_FOUND, .CONNECTED, .NO_VALUE, .OVERRIDDEN, .VALUE_REAL, .VALUE_VEC2, .VALUE_VEC3, .VALUE_VEC4, .VALUE_INT, .VALUE_STR, .VALUE_BLOB }
+
+// Single property with name/type/value.
+Prop :: struct {
+ name: String,
+ _internal_key: u32,
+ type: Prop_Type,
+ flags: Prop_Flag,
+ value_str: String,
+ value_blob: Blob,
+ value_int: i64,
+ using _: struct #raw_union {
+ value_real_arr: [4]Real,
+ value_real: Real,
+ value_vec2: Vec2,
+ value_vec3: Vec3,
+ value_vec4: Vec4,
+ },
+}
+
+Prop_List :: struct {
+ data: ^Prop,
+ count: c.size_t,
+}
+
+// List of alphabetically sorted properties with potential defaults.
+// For animated objects in as scene from `ufbx_evaluate_scene()` this list
+// only has the animated properties, the originals are stored under `defaults`.
+Props :: struct {
+ props: Prop_List,
+ num_animated: c.size_t,
+ defaults: ^Props,
+}
+
+Element_List :: struct {
+ data: ^^Element,
+ count: c.size_t,
+}
+
+Unknown_List :: struct {
+ data: ^^Unknown,
+ count: c.size_t,
+}
+
+Node_List :: struct {
+ data: [^]^Node,
+ count: c.size_t,
+}
+
+Mesh_List :: struct {
+ data: ^^Mesh,
+ count: c.size_t,
+}
+
+Light_List :: struct {
+ data: ^^Light,
+ count: c.size_t,
+}
+
+Camera_List :: struct {
+ data: ^^Camera,
+ count: c.size_t,
+}
+
+Bone_List :: struct {
+ data: ^^Bone,
+ count: c.size_t,
+}
+
+Empty_List :: struct {
+ data: ^^Empty,
+ count: c.size_t,
+}
+
+Line_Curve_List :: struct {
+ data: ^^Line_Curve,
+ count: c.size_t,
+}
+
+Nurbs_Curve_List :: struct {
+ data: ^^Nurbs_Curve,
+ count: c.size_t,
+}
+
+Nurbs_Surface_List :: struct {
+ data: ^^Nurbs_Surface,
+ count: c.size_t,
+}
+
+Nurbs_Trim_Surface_List :: struct {
+ data: ^^Nurbs_Trim_Surface,
+ count: c.size_t,
+}
+
+Nurbs_Trim_Boundary_List :: struct {
+ data: ^^Nurbs_Trim_Boundary,
+ count: c.size_t,
+}
+
+Procedural_Geometry_List :: struct {
+ data: ^^Procedural_Geometry,
+ count: c.size_t,
+}
+
+Stereo_Camera_List :: struct {
+ data: ^^Stereo_Camera,
+ count: c.size_t,
+}
+
+Camera_Switcher_List :: struct {
+ data: ^^Camera_Switcher,
+ count: c.size_t,
+}
+
+Marker_List :: struct {
+ data: ^^Marker,
+ count: c.size_t,
+}
+
+Lod_Group_List :: struct {
+ data: ^^Lod_Group,
+ count: c.size_t,
+}
+
+Skin_Deformer_List :: struct {
+ data: ^^Skin_Deformer,
+ count: c.size_t,
+}
+
+Skin_Cluster_List :: struct {
+ data: ^^Skin_Cluster,
+ count: c.size_t,
+}
+
+Blend_Deformer_List :: struct {
+ data: ^^Blend_Deformer,
+ count: c.size_t,
+}
+
+Blend_Channel_List :: struct {
+ data: ^^Blend_Channel,
+ count: c.size_t,
+}
+
+Blend_Shape_List :: struct {
+ data: ^^Blend_Shape,
+ count: c.size_t,
+}
+
+Cache_Deformer_List :: struct {
+ data: ^^Cache_Deformer,
+ count: c.size_t,
+}
+
+Cache_File_List :: struct {
+ data: ^^Cache_File,
+ count: c.size_t,
+}
+
+Material_List :: struct {
+ data: ^^Material,
+ count: c.size_t,
+}
+
+Texture_List :: struct {
+ data: ^^Texture,
+ count: c.size_t,
+}
+
+Video_List :: struct {
+ data: ^^Video,
+ count: c.size_t,
+}
+
+Shader_List :: struct {
+ data: ^^Shader,
+ count: c.size_t,
+}
+
+Shader_Binding_List :: struct {
+ data: ^^Shader_Binding,
+ count: c.size_t,
+}
+
+Anim_Stack_List :: struct {
+ data: ^^Anim_Stack,
+ count: c.size_t,
+}
+
+Anim_Layer_List :: struct {
+ data: ^^Anim_Layer,
+ count: c.size_t,
+}
+
+Anim_Value_List :: struct {
+ data: ^^Anim_Value,
+ count: c.size_t,
+}
+
+Anim_Curve_List :: struct {
+ data: ^^Anim_Curve,
+ count: c.size_t,
+}
+
+Display_Layer_List :: struct {
+ data: ^^Display_Layer,
+ count: c.size_t,
+}
+
+Selection_Set_List :: struct {
+ data: ^^Selection_Set,
+ count: c.size_t,
+}
+
+Selection_Node_List :: struct {
+ data: ^^Selection_Node,
+ count: c.size_t,
+}
+
+Character_List :: struct {
+ data: ^^Character,
+ count: c.size_t,
+}
+
+Constraint_List :: struct {
+ data: ^^Constraint,
+ count: c.size_t,
+}
+
+Audio_Layer_List :: struct {
+ data: ^^Audio_Layer,
+ count: c.size_t,
+}
+
+Audio_Clip_List :: struct {
+ data: ^^Audio_Clip,
+ count: c.size_t,
+}
+
+Pose_List :: struct {
+ data: ^^Pose,
+ count: c.size_t,
+}
+
+Metadata_Object_List :: struct {
+ data: ^^Metadata_Object,
+ count: c.size_t,
+}
+
+Element_Type :: enum c.int {
+ UNKNOWN, // < `ufbx_unknown`
+ NODE, // < `ufbx_node`
+ MESH, // < `ufbx_mesh`
+ LIGHT, // < `ufbx_light`
+ CAMERA, // < `ufbx_camera`
+ BONE, // < `ufbx_bone`
+ EMPTY, // < `ufbx_empty`
+ LINE_CURVE, // < `ufbx_line_curve`
+ NURBS_CURVE, // < `ufbx_nurbs_curve`
+ NURBS_SURFACE, // < `ufbx_nurbs_surface`
+ NURBS_TRIM_SURFACE, // < `ufbx_nurbs_trim_surface`
+ NURBS_TRIM_BOUNDARY, // < `ufbx_nurbs_trim_boundary`
+ PROCEDURAL_GEOMETRY, // < `ufbx_procedural_geometry`
+ STEREO_CAMERA, // < `ufbx_stereo_camera`
+ CAMERA_SWITCHER, // < `ufbx_camera_switcher`
+ MARKER, // < `ufbx_marker`
+ LOD_GROUP, // < `ufbx_lod_group`
+ SKIN_DEFORMER, // < `ufbx_skin_deformer`
+ SKIN_CLUSTER, // < `ufbx_skin_cluster`
+ BLEND_DEFORMER, // < `ufbx_blend_deformer`
+ BLEND_CHANNEL, // < `ufbx_blend_channel`
+ BLEND_SHAPE, // < `ufbx_blend_shape`
+ CACHE_DEFORMER, // < `ufbx_cache_deformer`
+ CACHE_FILE, // < `ufbx_cache_file`
+ MATERIAL, // < `ufbx_material`
+ TEXTURE, // < `ufbx_texture`
+ VIDEO, // < `ufbx_video`
+ SHADER, // < `ufbx_shader`
+ SHADER_BINDING, // < `ufbx_shader_binding`
+ ANIM_STACK, // < `ufbx_anim_stack`
+ ANIM_LAYER, // < `ufbx_anim_layer`
+ ANIM_VALUE, // < `ufbx_anim_value`
+ ANIM_CURVE, // < `ufbx_anim_curve`
+ DISPLAY_LAYER, // < `ufbx_display_layer`
+ SELECTION_SET, // < `ufbx_selection_set`
+ SELECTION_NODE, // < `ufbx_selection_node`
+ CHARACTER, // < `ufbx_character`
+ CONSTRAINT, // < `ufbx_constraint`
+ AUDIO_LAYER, // < `ufbx_audio_layer`
+ AUDIO_CLIP, // < `ufbx_audio_clip`
+ POSE, // < `ufbx_pose`
+ METADATA_OBJECT, // < `ufbx_metadata_object`
+ TYPE_FIRST_ATTRIB = 2,
+ TYPE_LAST_ATTRIB = 16,
+ TYPE_FORCE_32BIT = 2147483647,
+}
+
+ELEMENT_TYPE_COUNT :: 42
+
+// Connection between two elements.
+// Source and destination are somewhat arbitrary but the destination is
+// often the "container" like a parent node or mesh containing a deformer.
+Connection :: struct {
+ src: ^Element,
+ dst: ^Element,
+ src_prop: String,
+ dst_prop: String,
+}
+
+Connection_List :: struct {
+ data: ^Connection,
+ count: c.size_t,
+}
+
+// Element "base-class" common to each element.
+// Some fields (like `connections_src`) are advanced and not visible
+// in the specialized element structs.
+// NOTE: The `element_id` value is consistent when loading the
+// _same_ file, but re-exporting the file will invalidate them.
+Element :: struct {
+ name: String,
+ props: Props,
+ element_id: u32,
+ typed_id: u32,
+ instances: Node_List,
+ type: Element_Type,
+ connections_src: Connection_List,
+ connections_dst: Connection_List,
+ dom_node: ^Dom_Node,
+ scene: ^Scene,
+}
+
+// -- Unknown
+Unknown :: struct {
+ // Shared "base-class" header, see `ufbx_element`.
+ using _: struct #raw_union {
+ element: Element,
+ using _: struct {
+ name: String,
+ props: Props,
+ element_id: u32,
+ typed_id: u32,
+ },
+ },
+
+ // FBX format specific type information.
+ // In ASCII FBX format:
+ // super_type: ID, "type::name", "sub_type" { ... }
+ type: String,
+ super_type: String,
+ sub_type: String,
+}
+
+// Inherit type specifies how hierarchial node transforms are combined.
+// This only affects the final scaling, as rotation and translation are always
+// inherited correctly.
+// NOTE: These don't map to `"InheritType"` property as there may be new ones for
+// compatibility with various exporters.
+Inherit_Mode :: enum c.int {
+ // Normal matrix composition of hierarchy: `R*S*r*s`.
+ // child.node_to_world = parent.node_to_world * child.node_to_parent;
+ NORMAL,
+
+ // Ignore parent scale when computing the transform: `R*r*s`.
+ // ufbx_transform t = node.local_transform;
+ // t.translation *= parent.inherit_scale;
+ // t.scale *= node.inherit_scale_node.inherit_scale;
+ // child.node_to_world = parent.unscaled_node_to_world * t;
+ // Also known as "Segment scale compensate" in some software.
+ IGNORE_PARENT_SCALE,
+
+ // Apply parent scale component-wise: `R*r*S*s`.
+ // ufbx_transform t = node.local_transform;
+ // t.translation *= parent.inherit_scale;
+ // t.scale *= node.inherit_scale_node.inherit_scale;
+ // child.node_to_world = parent.unscaled_node_to_world * t;
+ COMPONENTWISE_SCALE,
+ FORCE_32BIT = 2147483647, // Apply parent scale component-wise: `R*r*S*s`.
+ // ufbx_transform t = node.local_transform;
+ // t.translation *= parent.inherit_scale;
+ // t.scale *= node.inherit_scale_node.inherit_scale;
+ // child.node_to_world = parent.unscaled_node_to_world * t;
+}
+
+INHERIT_MODE_COUNT :: 3
+
+// Axis used to mirror transformations for handedness conversion.
+Mirror_Axis :: enum c.int {
+ NONE,
+ X,
+ Y,
+ Z,
+ FORCE_32BIT = 2147483647,
+}
+
+MIRROR_AXIS_COUNT :: 4
+
+// Nodes form the scene transformation hierarchy and can contain attached
+// elements such as meshes or lights. In normal cases a single `ufbx_node`
+// contains only a single attached element, so using `type/mesh/...` is safe.
+Node :: struct {
+ using _: struct #raw_union {
+ element: Element,
+ using _: struct {
+ name: String,
+ props: Props,
+ element_id: u32,
+ typed_id: u32,
+ },
+ },
+
+ // Parent node containing this one if not root.
+ //
+ // Always non-`NULL` for non-root nodes unless
+ // `ufbx_load_opts.allow_nodes_out_of_root` is enabled.
+ parent: ^Node,
+
+ // List of child nodes parented to this node.
+ children: Node_List,
+
+ // Common attached element type and typed pointers. Set to `NULL` if not in
+ // use, so checking `attrib_type` is not required.
+ //
+ // HINT: If you need less common attributes access `ufbx_node.attrib`, you
+ // can use utility functions like `ufbx_as_nurbs_curve(attrib)` to convert
+ // and check the attribute in one step.
+ mesh: ^Mesh,
+ light: ^Light,
+ camera: ^Camera,
+ bone: ^Bone,
+
+ // Less common attributes use these fields.
+ //
+ // Defined even if it is one of the above, eg. `ufbx_mesh`. In case there
+ // is multiple attributes this will be the first one.
+ attrib: ^Element,
+
+ // Geometry transform helper if one exists.
+ // See `UFBX_GEOMETRY_TRANSFORM_HANDLING_HELPER_NODES`.
+ geometry_transform_helper: ^Node,
+
+ // Scale helper if one exists.
+ // See `UFBX_INHERIT_MODE_HANDLING_HELPER_NODES`.
+ scale_helper: ^Node,
+
+ // `attrib->type` if `attrib` is defined, otherwise `UFBX_ELEMENT_UNKNOWN`.
+ attrib_type: Element_Type,
+
+ // List of _all_ attached attribute elements.
+ //
+ // In most cases there is only zero or one attributes per node, but if you
+ // have a very exotic FBX file nodes may have multiple attributes.
+ all_attribs: Element_List,
+
+ // Local transform in parent, geometry transform is a non-inherited
+ // transform applied only to attachments like meshes
+ inherit_mode: Inherit_Mode,
+ original_inherit_mode: Inherit_Mode,
+ local_transform: Transform,
+ geometry_transform: Transform,
+
+ // Combined scale when using `UFBX_INHERIT_MODE_COMPONENTWISE_SCALE`.
+ // Contains `local_transform.scale` otherwise.
+ inherit_scale: Vec3,
+
+ // Node where scale is inherited from for `UFBX_INHERIT_MODE_COMPONENTWISE_SCALE`
+ // and even for `UFBX_INHERIT_MODE_IGNORE_PARENT_SCALE`.
+ // For componentwise-scale nodes, this will point to `parent`, for scale ignoring
+ // nodes this will point to the parent of the nearest componentwise-scaled node
+ // in the parent chain.
+ inherit_scale_node: ^Node,
+
+ // Specifies the axis order `euler_rotation` is applied in.
+ rotation_order: Rotation_Order,
+
+ // Rotation around the local X/Y/Z axes in `rotation_order`.
+ // The angles are specified in degrees.
+ euler_rotation: Vec3,
+
+ // Transform from this node to `parent` space.
+ // Equivalent to `ufbx_transform_to_matrix(&local_transform)`.
+ node_to_parent: Matrix,
+
+ // Transform from this node to the world space, ie. multiplying all the
+ // `node_to_parent` matrices of the parent chain together.
+ node_to_world: Matrix,
+
+ // Transform from the attribute to this node. Does not affect the transforms
+ // of `children`!
+ // Equivalent to `ufbx_transform_to_matrix(&geometry_transform)`.
+ geometry_to_node: Matrix,
+
+ // Transform from attribute space to world space.
+ // Equivalent to `ufbx_matrix_mul(&node_to_world, &geometry_to_node)`.
+ geometry_to_world: Matrix,
+
+ // Transform from this node to world space, ignoring self scaling.
+ unscaled_node_to_world: Matrix,
+ adjust_pre_translation: Vec3, // < Translation applied between parent and self
+ adjust_pre_rotation: Quat, // < Rotation applied between parent and self
+ adjust_pre_scale: Real, // < Scaling applied between parent and self
+ adjust_post_rotation: Quat, // < Rotation applied in local space at the end
+ adjust_post_scale: Real, // < Scaling applied in local space at the end
+ adjust_translation_scale: Real, // < Scaling applied to translation only
+ adjust_mirror_axis: Mirror_Axis, // < Mirror translation and rotation on this axis
+
+ // Materials used by `mesh` or other `attrib`.
+ // There may be multiple copies of a single `ufbx_mesh` with different materials
+ // in the `ufbx_node` instances.
+ materials: Material_List,
+
+ // Bind pose
+ bind_pose: ^Pose,
+
+ // Visibility state.
+ visible: bool,
+
+ // True if this node is the implicit root node of the scene.
+ is_root: bool,
+
+ // True if the node has a non-identity `geometry_transform`.
+ has_geometry_transform: bool,
+
+ // If `true` the transform is adjusted by ufbx, not enabled by default.
+ // See `adjust_pre_rotation`, `adjust_pre_scale`, `adjust_post_rotation`,
+ // and `adjust_post_scale`.
+ has_adjust_transform: bool,
+
+ // Scale is adjusted by root scale.
+ has_root_adjust_transform: bool,
+
+ // True if this node is a synthetic geometry transform helper.
+ // See `UFBX_GEOMETRY_TRANSFORM_HANDLING_HELPER_NODES`.
+ is_geometry_transform_helper: bool,
+
+ // True if the node is a synthetic scale compensation helper.
+ // See `UFBX_INHERIT_MODE_HANDLING_HELPER_NODES`.
+ is_scale_helper: bool,
+
+ // Parent node to children that can compensate for parent scale.
+ is_scale_compensate_parent: bool,
+
+ // How deep is this node in the parent hierarchy. Root node is at depth `0`
+ // and the immediate children of root at `1`.
+ node_depth: u32,
+}
+
+// Vertex attribute: All attributes are stored in a consistent indexed format
+// regardless of how it's actually stored in the file.
+//
+// `values` is a contiguous array of attribute values.
+// `indices` maps each mesh index into a value in the `values` array.
+//
+// If `unique_per_vertex` is set then the attribute is guaranteed to have a
+// single defined value per vertex accessible via:
+// attrib.values.data[attrib.indices.data[mesh->vertex_first_index[vertex_ix]]
+Vertex_Attrib :: struct {
+ // Is this attribute defined by the mesh.
+ exists: bool,
+
+ // List of values the attribute uses.
+ values: Void_List,
+
+ // Indices into `values[]`, indexed up to `ufbx_mesh.num_indices`.
+ indices: Uint32_List,
+
+ // Number of `ufbx_real` entries per value.
+ value_reals: c.size_t,
+
+ // `true` if this attribute is defined per vertex, instead of per index.
+ unique_per_vertex: bool,
+
+ // Optional 4th 'W' component for the attribute.
+ // May be defined for the following:
+ // ufbx_mesh.vertex_normal
+ // ufbx_mesh.vertex_tangent / ufbx_uv_set.vertex_tangent
+ // ufbx_mesh.vertex_bitangent / ufbx_uv_set.vertex_bitangent
+ // NOTE: This is not loaded by default, set `ufbx_load_opts.retain_vertex_attrib_w`.
+ values_w: Real_List,
+}
+
+// 1D vertex attribute, see `ufbx_vertex_attrib` for information
+Vertex_Real :: struct {
+ exists: bool,
+ values: Real_List,
+ indices: Uint32_List,
+ value_reals: c.size_t,
+ unique_per_vertex: bool,
+ values_w: Real_List,
+}
+
+// 2D vertex attribute, see `ufbx_vertex_attrib` for information
+Vertex_Vec2 :: struct {
+ exists: bool,
+ values: Vec2_List,
+ indices: Uint32_List,
+ value_reals: c.size_t,
+ unique_per_vertex: bool,
+ values_w: Real_List,
+}
+
+// 3D vertex attribute, see `ufbx_vertex_attrib` for information
+Vertex_Vec3 :: struct {
+ exists: bool,
+ values: Vec3_List,
+ indices: Uint32_List,
+ value_reals: c.size_t,
+ unique_per_vertex: bool,
+ values_w: Real_List,
+}
+
+// 4D vertex attribute, see `ufbx_vertex_attrib` for information
+Vertex_Vec4 :: struct {
+ exists: bool,
+ values: Vec4_List,
+ indices: Uint32_List,
+ value_reals: c.size_t,
+ unique_per_vertex: bool,
+ values_w: Real_List,
+}
+
+// Vertex UV set/layer
+Uv_Set :: struct {
+ name: String,
+ index: u32,
+ vertex_uv: Vertex_Vec2, // < UV / texture coordinates
+ vertex_tangent: Vertex_Vec3, // < (optional) Tangent vector in UV.x direction
+ vertex_bitangent: Vertex_Vec3, // < (optional) Tangent vector in UV.y direction
+}
+
+// Vertex color set/layer
+Color_Set :: struct {
+ name: String,
+ index: u32,
+ vertex_color: Vertex_Vec4, // < Per-vertex RGBA color
+}
+
+Uv_Set_List :: struct {
+ data: ^Uv_Set,
+ count: c.size_t,
+}
+
+Color_Set_List :: struct {
+ data: ^Color_Set,
+ count: c.size_t,
+}
+
+// Edge between two _indices_ in a mesh
+Edge :: struct {
+ using _: struct #raw_union {
+ using _: struct {
+ a, b: u32,
+ },
+ indices: [2]u32,
+ },
+}
+
+Edge_List :: struct {
+ data: ^Edge,
+ count: c.size_t,
+}
+
+// Polygonal face with arbitrary number vertices, a single face contains a
+// contiguous range of mesh indices, eg. `{5,3}` would have indices 5, 6, 7
+//
+// NOTE: `num_indices` maybe less than 3 in which case the face is invalid!
+// [TODO #23: should probably remove the bad faces at load time]
+Face :: struct {
+ index_begin: u32,
+ num_indices: u32,
+}
+
+Face_List :: struct {
+ data: [^]Face,
+ count: c.size_t,
+}
+
+// Subset of mesh faces used by a single material or group.
+Mesh_Part :: struct {
+ // Index of the mesh part.
+ index: u32,
+ num_faces: c.size_t, // < Number of faces (polygons)
+ num_triangles: c.size_t, // < Number of triangles if triangulated
+ num_empty_faces: c.size_t, // < Number of faces with zero vertices
+ num_point_faces: c.size_t, // < Number of faces with a single vertex
+ num_line_faces: c.size_t, // < Number of faces with two vertices
+
+ // Indices to `ufbx_mesh.faces[]`.
+ // Always contains `num_faces` elements.
+ face_indices: Uint32_List,
+}
+
+Mesh_Part_List :: struct {
+ data: ^Mesh_Part,
+ count: c.size_t,
+}
+
+Face_Group :: struct {
+ id: i32, // < Numerical ID for this group.
+ name: String, // < Name for the face group.
+}
+
+Face_Group_List :: struct {
+ data: ^Face_Group,
+ count: c.size_t,
+}
+
+Subdivision_Weight_Range :: struct {
+ weight_begin: u32,
+ num_weights: u32,
+}
+
+Subdivision_Weight_Range_List :: struct {
+ data: ^Subdivision_Weight_Range,
+ count: c.size_t,
+}
+
+Subdivision_Weight :: struct {
+ weight: Real,
+ index: u32,
+}
+
+Subdivision_Weight_List :: struct {
+ data: ^Subdivision_Weight,
+ count: c.size_t,
+}
+
+Subdivision_Result :: struct {
+ result_memory_used: c.size_t,
+ temp_memory_used: c.size_t,
+ result_allocs: c.size_t,
+ temp_allocs: c.size_t,
+
+ // Weights of vertices in the source model.
+ // Defined if `ufbx_subdivide_opts.evaluate_source_vertices` is set.
+ source_vertex_ranges: Subdivision_Weight_Range_List,
+ source_vertex_weights: Subdivision_Weight_List,
+
+ // Weights of skin clusters in the source model.
+ // Defined if `ufbx_subdivide_opts.evaluate_skin_weights` is set.
+ skin_cluster_ranges: Subdivision_Weight_Range_List,
+ skin_cluster_weights: Subdivision_Weight_List,
+}
+
+Subdivision_Display_Mode :: enum c.int {
+ DISABLED,
+ HULL,
+ HULL_AND_SMOOTH,
+ SMOOTH,
+ MODE_FORCE_32BIT = 2147483647,
+}
+
+SUBDIVISION_DISPLAY_MODE_COUNT :: 4
+
+Subdivision_Boundary :: enum c.int {
+ DEFAULT,
+ LEGACY,
+
+ // OpenSubdiv: `VTX_BOUNDARY_EDGE_AND_CORNER` / `FVAR_LINEAR_CORNERS_ONLY`
+ SHARP_CORNERS,
+
+ // OpenSubdiv: `VTX_BOUNDARY_EDGE_ONLY` / `FVAR_LINEAR_NONE`
+ SHARP_NONE,
+
+ // OpenSubdiv: `FVAR_LINEAR_BOUNDARIES`
+ SHARP_BOUNDARY,
+
+ // OpenSubdiv: `FVAR_LINEAR_ALL`
+ SHARP_INTERIOR,
+ FORCE_32BIT = 2147483647, // OpenSubdiv: `FVAR_LINEAR_ALL`
+}
+
+SUBDIVISION_BOUNDARY_COUNT :: 6
+
+// Polygonal mesh geometry.
+//
+// Example mesh with two triangles (x, z) and a quad (y).
+// The faces have a constant UV coordinate x/y/z.
+// The vertices have _per vertex_ normals that point up/down.
+//
+// ^ ^ ^
+// A---B-----C
+// |x / /|
+// | / y / |
+// |/ / z|
+// D-----E---F
+// v v v
+//
+// Attributes may have multiple values within a single vertex, for example a
+// UV seam vertex has two UV coordinates. Thus polygons are defined using
+// an index that counts each corner of each face polygon. If an attribute is
+// defined (even per-vertex) it will always have a valid `indices` array.
+//
+// {0,3} {3,4} {7,3} faces ({ index_begin, num_indices })
+// 0 1 2 3 4 5 6 7 8 9 index
+//
+// 0 1 3 1 2 4 3 2 4 5 vertex_indices[index]
+// A B D B C E D C E F vertices[vertex_indices[index]]
+//
+// 0 0 1 0 0 1 1 0 1 1 vertex_normal.indices[index]
+// ^ ^ v ^ ^ v v ^ v v vertex_normal.data[vertex_normal.indices[index]]
+//
+// 0 0 0 1 1 1 1 2 2 2 vertex_uv.indices[index]
+// x x x y y y y z z z vertex_uv.data[vertex_uv.indices[index]]
+//
+// Vertex position can also be accessed uniformly through an accessor:
+// 0 1 3 1 2 4 3 2 4 5 vertex_position.indices[index]
+// A B D B C E D C E F vertex_position.data[vertex_position.indices[index]]
+//
+// Some geometry data is specified per logical vertex. Vertex positions are
+// the only attribute that is guaranteed to be defined _uniquely_ per vertex.
+// Vertex attributes _may_ be defined per vertex if `unique_per_vertex == true`.
+// You can access the per-vertex values by first finding the first index that
+// refers to the given vertex.
+//
+// 0 1 2 3 4 5 vertex
+// A B C D E F vertices[vertex]
+//
+// 0 1 4 2 5 9 vertex_first_index[vertex]
+// 0 0 0 1 1 1 vertex_normal.indices[vertex_first_index[vertex]]
+// ^ ^ ^ v v v vertex_normal.data[vertex_normal.indices[vertex_first_index[vertex]]]
+Mesh :: struct {
+ using _: struct #raw_union {
+ element: Element,
+ using _: struct {
+ name: String,
+ props: Props,
+ element_id: u32,
+ typed_id: u32,
+ instances: Node_List,
+ },
+ },
+ num_vertices: c.size_t, // < Number of logical "vertex" points
+ num_indices: c.size_t, // < Number of combiend vertex/attribute tuples
+ num_faces: c.size_t, // < Number of faces (polygons) in the mesh
+ num_triangles: c.size_t, // < Number of triangles if triangulated
+
+ // Number of edges in the mesh.
+ // NOTE: May be zero in valid meshes if the file doesn't contain edge adjacency data!
+ num_edges: c.size_t,
+ max_face_triangles: c.size_t, // < Maximum number of triangles in a face in this mesh
+ num_empty_faces: c.size_t, // < Number of faces with zero vertices
+ num_point_faces: c.size_t, // < Number of faces with a single vertex
+ num_line_faces: c.size_t, // < Number of faces with two vertices
+ faces: Face_List, // < Face index range
+ face_smoothing: Bool_List, // < Should the face have soft normals
+ face_material: Uint32_List, // < Indices to `ufbx_mesh.materials[]` and `ufbx_node.materials[]`
+ face_group: Uint32_List, // < Face polygon group index, indices to `ufbx_mesh.face_groups[]`
+ face_hole: Bool_List, // < Should the face be hidden as a "hole"
+ edges: Edge_List, // < Edge index range
+ edge_smoothing: Bool_List, // < Should the edge have soft normals
+ edge_crease: Real_List, // < Crease value for subdivision surfaces
+ edge_visibility: Bool_List, // < Should the edge be visible
+
+ // Logical vertices and positions, alternatively you can use
+ // `vertex_position` for consistent interface with other attributes.
+ vertex_indices: Uint32_List,
+ vertices: Vec3_List,
+
+ // First index referring to a given vertex, `UFBX_NO_INDEX` if the vertex is unused.
+ vertex_first_index: Uint32_List,
+ vertex_position: Vertex_Vec3, // < Vertex positions
+ vertex_normal: Vertex_Vec3, // < (optional) Normal vectors, always defined if `ufbx_load_opts.generate_missing_normals`
+ vertex_uv: Vertex_Vec2, // < (optional) UV / texture coordinates
+ vertex_tangent: Vertex_Vec3, // < (optional) Tangent vector in UV.x direction
+ vertex_bitangent: Vertex_Vec3, // < (optional) Tangent vector in UV.y direction
+ vertex_color: Vertex_Vec4, // < (optional) Per-vertex RGBA color
+ vertex_crease: Vertex_Real, // < (optional) Crease value for subdivision surfaces
+
+ // Multiple named UV/color sets
+ // NOTE: The first set contains the same data as `vertex_uv/color`!
+ uv_sets: Uv_Set_List,
+ color_sets: Color_Set_List,
+
+ // Materials used by the mesh.
+ // NOTE: These can be wrong if you want to support per-instance materials!
+ // Use `ufbx_node.materials[]` to get the per-instance materials at the same indices.
+ materials: Material_List,
+
+ // Face groups for this mesh.
+ face_groups: Face_Group_List,
+
+ // Segments that use a given material.
+ // Defined even if the mesh doesn't have any materials.
+ material_parts: Mesh_Part_List,
+
+ // Segments for each face group.
+ face_group_parts: Mesh_Part_List,
+
+ // Order of `material_parts` by first face that refers to it.
+ // Useful for compatibility with FBX SDK and various importers using it,
+ // as they use this material order by default.
+ material_part_usage_order: Uint32_List,
+
+ // Skinned vertex positions, for efficiency the skinned positions are the
+ // same as the static ones for non-skinned meshes and `skinned_is_local`
+ // is set to true meaning you need to transform them manually using
+ // `ufbx_transform_position(&node->geometry_to_world, skinned_pos)`!
+ skinned_is_local: bool,
+ skinned_position: Vertex_Vec3,
+ skinned_normal: Vertex_Vec3,
+
+ // Deformers
+ skin_deformers: Skin_Deformer_List,
+ blend_deformers: Blend_Deformer_List,
+ cache_deformers: Cache_Deformer_List,
+ all_deformers: Element_List,
+
+ // Subdivision
+ subdivision_preview_levels: u32,
+ subdivision_render_levels: u32,
+ subdivision_display_mode: Subdivision_Display_Mode,
+ subdivision_boundary: Subdivision_Boundary,
+ subdivision_uv_boundary: Subdivision_Boundary,
+
+ // The winding of the faces has been reversed.
+ reversed_winding: bool,
+
+ // Normals have been generated instead of evaluated.
+ // Either from missing normals (via `ufbx_load_opts.generate_missing_normals`), skinning,
+ // tessellation, or subdivision.
+ generated_normals: bool,
+
+ // Subdivision (result)
+ subdivision_evaluated: bool,
+ subdivision_result: ^Subdivision_Result,
+
+ // Tessellation (result)
+ from_tessellated_nurbs: bool,
+}
+
+// The kind of light source
+Light_Type :: enum c.int {
+ // Single point at local origin, at `node->world_transform.position`
+ POINT,
+
+ // Infinite directional light pointing locally towards `light->local_direction`
+ // For global: `ufbx_transform_direction(&node->node_to_world, light->local_direction)`
+ DIRECTIONAL,
+
+ // Cone shaped light towards `light->local_direction`, between `light->inner/outer_angle`.
+ // For global: `ufbx_transform_direction(&node->node_to_world, light->local_direction)`
+ SPOT,
+
+ // Area light, shape specified by `light->area_shape`
+ // TODO: Units?
+ AREA,
+
+ // Volumetric light source
+ // TODO: How does this work
+ VOLUME,
+ TYPE_FORCE_32BIT = 2147483647, // Volumetric light source
+ // TODO: How does this work
+}
+
+LIGHT_TYPE_COUNT :: 5
+
+// How fast does the light intensity decay at a distance
+Light_Decay :: enum c.int {
+ NONE, // < 1 (no decay)
+ LINEAR, // < 1 / d
+ QUADRATIC, // < 1 / d^2 (physically accurate)
+ CUBIC, // < 1 / d^3
+ FORCE_32BIT = 2147483647,
+}
+
+LIGHT_DECAY_COUNT :: 4
+
+Light_Area_Shape :: enum c.int {
+ RECTANGLE,
+ SPHERE,
+ FORCE_32BIT = 2147483647,
+}
+
+LIGHT_AREA_SHAPE_COUNT :: 2
+
+// Light source attached to a `ufbx_node`
+Light :: struct {
+ using _: struct #raw_union {
+ element: Element,
+ using _: struct {
+ name: String,
+ props: Props,
+ element_id: u32,
+ typed_id: u32,
+ instances: Node_List,
+ },
+ },
+
+ // Color and intensity of the light, usually you want to use `color * intensity`
+ // NOTE: `intensity` is 0.01x of the property `"Intensity"` as that matches
+ // matches values in DCC programs before exporting.
+ color: Vec3,
+ intensity: Real,
+
+ // Direction the light is aimed at in node's local space, usually -Y
+ local_direction: Vec3,
+
+ // Type of the light and shape parameters
+ type: Light_Type,
+ decay: Light_Decay,
+ area_shape: Light_Area_Shape,
+ inner_angle: Real,
+ outer_angle: Real,
+ cast_light: bool,
+ cast_shadows: bool,
+}
+
+Projection_Mode :: enum c.int {
+ // Perspective projection.
+ PERSPECTIVE,
+
+ // Orthographic projection.
+ ORTHOGRAPHIC,
+ FORCE_32BIT = 2147483647, // Orthographic projection.
+}
+
+PROJECTION_MODE_COUNT :: 2
+
+// Method of specifying the rendering resolution from properties
+// NOTE: Handled internally by ufbx, ignore unless you interpret `ufbx_props` directly!
+Aspect_Mode :: enum c.int {
+ // No defined resolution
+ WINDOW_SIZE,
+
+ // `"AspectWidth"` and `"AspectHeight"` are relative to each other
+ FIXED_RATIO,
+
+ // `"AspectWidth"` and `"AspectHeight"` are both pixels
+ FIXED_RESOLUTION,
+
+ // `"AspectWidth"` is pixels, `"AspectHeight"` is relative to width
+ FIXED_WIDTH,
+
+ // < `"AspectHeight"` is pixels, `"AspectWidth"` is relative to height
+ FIXED_HEIGHT,
+ FORCE_32BIT = 2147483647, // < `"AspectHeight"` is pixels, `"AspectWidth"` is relative to height
+}
+
+ASPECT_MODE_COUNT :: 5
+
+// Method of specifying the field of view from properties
+// NOTE: Handled internally by ufbx, ignore unless you interpret `ufbx_props` directly!
+Aperture_Mode :: enum c.int {
+ // Use separate `"FieldOfViewX"` and `"FieldOfViewY"` as horizontal/vertical FOV angles
+ HORIZONTAL_AND_VERTICAL,
+
+ // Use `"FieldOfView"` as horizontal FOV angle, derive vertical angle via aspect ratio
+ HORIZONTAL,
+
+ // Use `"FieldOfView"` as vertical FOV angle, derive horizontal angle via aspect ratio
+ VERTICAL,
+
+ // Compute the field of view from the render gate size and focal length
+ FOCAL_LENGTH,
+ FORCE_32BIT = 2147483647, // Compute the field of view from the render gate size and focal length
+}
+
+APERTURE_MODE_COUNT :: 4
+
+// Method of specifying the render gate size from properties
+// NOTE: Handled internally by ufbx, ignore unless you interpret `ufbx_props` directly!
+Gate_Fit :: enum c.int {
+ // Use the film/aperture size directly as the render gate
+ NONE,
+
+ // Fit the render gate to the height of the film, derive width from aspect ratio
+ VERTICAL,
+
+ // Fit the render gate to the width of the film, derive height from aspect ratio
+ HORIZONTAL,
+
+ // Fit the render gate so that it is fully contained within the film gate
+ FILL,
+
+ // Fit the render gate so that it fully contains the film gate
+ OVERSCAN,
+
+ // Stretch the render gate to match the film gate
+ // TODO: Does this differ from `UFBX_GATE_FIT_NONE`?
+ STRETCH,
+ FORCE_32BIT = 2147483647, // Stretch the render gate to match the film gate
+ // TODO: Does this differ from `UFBX_GATE_FIT_NONE`?
+}
+
+GATE_FIT_COUNT :: 6
+
+// Camera film/aperture size defaults
+// NOTE: Handled internally by ufbx, ignore unless you interpret `ufbx_props` directly!
+Aperture_Format :: enum c.int {
+ CUSTOM, // < Use `"FilmWidth"` and `"FilmHeight"`
+ _16MM_THEATRICAL, // < 0.404 x 0.295 inches
+ SUPER_16MM, // < 0.493 x 0.292 inches
+ _35MM_ACADEMY, // < 0.864 x 0.630 inches
+ _35MM_TV_PROJECTION, // < 0.816 x 0.612 inches
+ _35MM_FULL_APERTURE, // < 0.980 x 0.735 inches
+ _35MM_185_PROJECTION, // < 0.825 x 0.446 inches
+ _35MM_ANAMORPHIC, // < 0.864 x 0.732 inches (squeeze ratio: 2)
+ _70MM_PROJECTION, // < 2.066 x 0.906 inches
+ VISTAVISION, // < 1.485 x 0.991 inches
+ DYNAVISION, // < 2.080 x 1.480 inches
+ IMAX, // < 2.772 x 2.072 inches
+ FORCE_32BIT = 2147483647,
+}
+
+APERTURE_FORMAT_COUNT :: 12
+
+Coordinate_Axis :: enum c.int {
+ POSITIVE_X,
+ NEGATIVE_X,
+ POSITIVE_Y,
+ NEGATIVE_Y,
+ POSITIVE_Z,
+ NEGATIVE_Z,
+ UNKNOWN,
+ FORCE_32BIT = 2147483647,
+}
+
+COORDINATE_AXIS_COUNT :: 7
+
+// Coordinate axes the scene is represented in.
+// NOTE: `front` is the _opposite_ from forward!
+Coordinate_Axes :: struct {
+ right: Coordinate_Axis,
+ up: Coordinate_Axis,
+ front: Coordinate_Axis,
+}
+
+// Camera attached to a `ufbx_node`
+Camera :: struct {
+ using _: struct #raw_union {
+ element: Element,
+ using _: struct {
+ name: String,
+ props: Props,
+ element_id: u32,
+ typed_id: u32,
+ instances: Node_List,
+ },
+ },
+
+ // Projection mode (perspective/orthographic).
+ projection_mode: Projection_Mode,
+
+ // If set to `true`, `resolution` represents actual pixel values, otherwise
+ // it's only useful for its aspect ratio.
+ resolution_is_pixels: bool,
+
+ // Render resolution, either in pixels or arbitrary units, depending on above
+ resolution: Vec2,
+
+ // Horizontal/vertical field of view in degrees
+ // Valid if `projection_mode == UFBX_PROJECTION_MODE_PERSPECTIVE`.
+ field_of_view_deg: Vec2,
+
+ // Component-wise `tan(field_of_view_deg)`, also represents the size of the
+ // proection frustum slice at distance of 1.
+ // Valid if `projection_mode == UFBX_PROJECTION_MODE_PERSPECTIVE`.
+ field_of_view_tan: Vec2,
+
+ // Orthographic camera extents.
+ // Valid if `projection_mode == UFBX_PROJECTION_MODE_ORTHOGRAPHIC`.
+ orthographic_extent: Real,
+
+ // Orthographic camera size.
+ // Valid if `projection_mode == UFBX_PROJECTION_MODE_ORTHOGRAPHIC`.
+ orthographic_size: Vec2,
+
+ // Size of the projection plane at distance 1.
+ // Equal to `field_of_view_tan` if perspective, `orthographic_size` if orthographic.
+ projection_plane: Vec2,
+
+ // Aspect ratio of the camera.
+ aspect_ratio: Real,
+
+ // Near plane of the frustum in units from the camera.
+ near_plane: Real,
+
+ // Far plane of the frustum in units from the camera.
+ far_plane: Real,
+
+ // Coordinate system that the projection uses.
+ // FBX saves cameras with +X forward and +Y up, but you can override this using
+ // `ufbx_load_opts.target_camera_axes` and it will be reflected here.
+ projection_axes: Coordinate_Axes,
+
+ // Advanced properties used to compute the above
+ aspect_mode: Aspect_Mode,
+ aperture_mode: Aperture_Mode,
+ gate_fit: Gate_Fit,
+ aperture_format: Aperture_Format,
+ focal_length_mm: Real, // < Focal length in millimeters
+ film_size_inch: Vec2, // < Film size in inches
+ aperture_size_inch: Vec2, // < Aperture/film gate size in inches
+ squeeze_ratio: Real, // < Anamoprhic stretch ratio
+}
+
+// Bone attached to a `ufbx_node`, provides the logical length of the bone
+// but most interesting information is directly in `ufbx_node`.
+Bone :: struct {
+ using _: struct #raw_union {
+ element: Element,
+ using _: struct {
+ name: String,
+ props: Props,
+ element_id: u32,
+ typed_id: u32,
+ instances: Node_List,
+ },
+ },
+
+ // Visual radius of the bone
+ radius: Real,
+
+ // Length of the bone relative to the distance between two nodes
+ relative_length: Real,
+
+ // Is the bone a root bone
+ is_root: bool,
+}
+
+// Empty/NULL/locator connected to a node, actual details in `ufbx_node`
+Empty :: struct {
+ using _: struct #raw_union {
+ element: Element,
+ using _: struct {
+ name: String,
+ props: Props,
+ element_id: u32,
+ typed_id: u32,
+ instances: Node_List,
+ },
+ },
+}
+
+// Segment of a `ufbx_line_curve`, indices refer to `ufbx_line_curve.point_indices[]`
+Line_Segment :: struct {
+ index_begin: u32,
+ num_indices: u32,
+}
+
+Line_Segment_List :: struct {
+ data: ^Line_Segment,
+ count: c.size_t,
+}
+
+Line_Curve :: struct {
+ using _: struct #raw_union {
+ element: Element,
+ using _: struct {
+ name: String,
+ props: Props,
+ element_id: u32,
+ typed_id: u32,
+ instances: Node_List,
+ },
+ },
+ color: Vec3,
+ control_points: Vec3_List, // < List of possible values the line passes through
+ point_indices: Uint32_List, // < Indices to `control_points[]` the line goes through
+ segments: Line_Segment_List,
+
+ // Tessellation (result)
+ from_tessellated_nurbs: bool,
+}
+
+Nurbs_Topology :: enum c.int {
+ // The endpoints are not connected.
+ OPEN,
+
+ // Repeats first `ufbx_nurbs_basis.order - 1` control points after the end.
+ PERIODIC,
+
+ // Repeats the first control point after the end.
+ CLOSED,
+ FORCE_32BIT = 2147483647, // Repeats the first control point after the end.
+}
+
+NURBS_TOPOLOGY_COUNT :: 3
+
+// NURBS basis functions for an axis
+Nurbs_Basis :: struct {
+ // Number of control points influencing a point on the curve/surface.
+ // Equal to the degree plus one.
+ order: u32,
+
+ // Topology (periodicity) of the dimension.
+ topology: Nurbs_Topology,
+
+ // Subdivision of the parameter range to control points.
+ knot_vector: Real_List,
+
+ // Range for the parameter value.
+ t_min: Real,
+ t_max: Real,
+
+ // Parameter values of control points.
+ spans: Real_List,
+
+ // `true` if this axis is two-dimensional.
+ is_2d: bool,
+
+ // Number of control points that need to be copied to the end.
+ // This is just for convenience as it could be derived from `topology` and
+ // `order`. If for example `num_wrap_control_points == 3` you should repeat
+ // the first 3 control points after the end.
+ // HINT: You don't need to worry about this if you use ufbx functions
+ // like `ufbx_evaluate_nurbs_curve()` as they handle this internally.
+ num_wrap_control_points: c.size_t,
+
+ // `true` if the parametrization is well defined.
+ valid: bool,
+}
+
+Nurbs_Curve :: struct {
+ using _: struct #raw_union {
+ element: Element,
+ using _: struct {
+ name: String,
+ props: Props,
+ element_id: u32,
+ typed_id: u32,
+ instances: Node_List,
+ },
+ },
+
+ // Basis in the U axis
+ basis: Nurbs_Basis,
+
+ // Linear array of control points
+ // NOTE: The control points are _not_ homogeneous, meaning you have to multiply
+ // them by `w` before evaluating the surface.
+ control_points: Vec4_List,
+}
+
+Nurbs_Surface :: struct {
+ using _: struct #raw_union {
+ element: Element,
+ using _: struct {
+ name: String,
+ props: Props,
+ element_id: u32,
+ typed_id: u32,
+ instances: Node_List,
+ },
+ },
+
+ // Basis in the U/V axes
+ basis_u: Nurbs_Basis,
+ basis_v: Nurbs_Basis,
+
+ // Number of control points for the U/V axes
+ num_control_points_u: c.size_t,
+ num_control_points_v: c.size_t,
+
+ // 2D array of control points.
+ // Memory layout: `V * num_control_points_u + U`
+ // NOTE: The control points are _not_ homogeneous, meaning you have to multiply
+ // them by `w` before evaluating the surface.
+ control_points: Vec4_List,
+
+ // How many segments tessellate each span in `ufbx_nurbs_basis.spans`.
+ span_subdivision_u: u32,
+ span_subdivision_v: u32,
+
+ // If `true` the resulting normals should be flipped when evaluated.
+ flip_normals: bool,
+
+ // Material for the whole surface.
+ // NOTE: May be `NULL`!
+ material: ^Material,
+}
+
+Nurbs_Trim_Surface :: struct {
+ using _: struct #raw_union {
+ element: Element,
+ using _: struct {
+ name: String,
+ props: Props,
+ element_id: u32,
+ typed_id: u32,
+ instances: Node_List,
+ },
+ },
+}
+
+Nurbs_Trim_Boundary :: struct {
+ using _: struct #raw_union {
+ element: Element,
+ using _: struct {
+ name: String,
+ props: Props,
+ element_id: u32,
+ typed_id: u32,
+ instances: Node_List,
+ },
+ },
+}
+
+// -- Node attributes (advanced)
+Procedural_Geometry :: struct {
+ using _: struct #raw_union {
+ element: Element,
+ using _: struct {
+ name: String,
+ props: Props,
+ element_id: u32,
+ typed_id: u32,
+ instances: Node_List,
+ },
+ },
+}
+
+Stereo_Camera :: struct {
+ using _: struct #raw_union {
+ element: Element,
+ using _: struct {
+ name: String,
+ props: Props,
+ element_id: u32,
+ typed_id: u32,
+ instances: Node_List,
+ },
+ },
+ left: ^Camera,
+ right: ^Camera,
+}
+
+Camera_Switcher :: struct {
+ using _: struct #raw_union {
+ element: Element,
+ using _: struct {
+ name: String,
+ props: Props,
+ element_id: u32,
+ typed_id: u32,
+ instances: Node_List,
+ },
+ },
+}
+
+Marker_Type :: enum c.int {
+ UNKNOWN, // < Unknown marker type
+ FK_EFFECTOR, // < FK (Forward Kinematics) effector
+ IK_EFFECTOR, // < IK (Inverse Kinematics) effector
+ TYPE_FORCE_32BIT = 2147483647,
+}
+
+MARKER_TYPE_COUNT :: 3
+
+// Tracking marker for effectors
+Marker :: struct {
+ using _: struct #raw_union {
+ element: Element,
+ using _: struct {
+ name: String,
+ props: Props,
+ element_id: u32,
+ typed_id: u32,
+ instances: Node_List,
+ },
+ },
+
+ // Type of the marker
+ type: Marker_Type,
+}
+
+// LOD level display mode.
+Lod_Display :: enum c.int {
+ USE_LOD, // < Display the LOD level if the distance is appropriate.
+ SHOW, // < Always display the LOD level.
+ HIDE, // < Never display the LOD level.
+ FORCE_32BIT = 2147483647,
+}
+
+LOD_DISPLAY_COUNT :: 3
+
+// Single LOD level within an LOD group.
+// Specifies properties of the Nth child of the _node_ containing the LOD group.
+Lod_Level :: struct {
+ // Minimum distance to show this LOD level.
+ // NOTE: In world units by default, or in screen percentage if
+ // `ufbx_lod_group.relative_distances` is set.
+ distance: Real,
+
+ // LOD display mode.
+ // NOTE: Mostly for editing, you should probably ignore this
+ // unless making a modeling program.
+ display: Lod_Display,
+}
+
+Lod_Level_List :: struct {
+ data: ^Lod_Level,
+ count: c.size_t,
+}
+
+// Group of LOD (Level of Detail) levels for an object.
+// The actual LOD models are defined in the parent `ufbx_node.children`.
+Lod_Group :: struct {
+ using _: struct #raw_union {
+ element: Element,
+ using _: struct {
+ name: String,
+ props: Props,
+ element_id: u32,
+ typed_id: u32,
+ instances: Node_List,
+ },
+ },
+
+ // If set to `true`, `ufbx_lod_level.distance` represents a screen size percentage.
+ relative_distances: bool,
+
+ // LOD levels matching in order to `ufbx_node.children`.
+ lod_levels: Lod_Level_List,
+
+ // If set to `true` don't account for parent transform when computing the distance.
+ ignore_parent_transform: bool,
+
+ // If `use_distance_limit` is enabled hide the group if the distance is not between
+ // `distance_limit_min` and `distance_limit_max`.
+ use_distance_limit: bool,
+ distance_limit_min: Real,
+ distance_limit_max: Real,
+}
+
+// Method to evaluate the skinning on a per-vertex level
+Skinning_Method :: enum c.int {
+ // Linear blend skinning: Blend transformation matrices by vertex weights
+ LINEAR,
+
+ // One vertex should have only one bone attached
+ RIGID,
+
+ // Convert the transformations to dual quaternions and blend in that space
+ DUAL_QUATERNION,
+
+ // Blend between `UFBX_SKINNING_METHOD_LINEAR` and `UFBX_SKINNING_METHOD_BLENDED_DQ_LINEAR`
+ // The blend weight can be found either per-vertex in `ufbx_skin_vertex.dq_weight`
+ // or in `ufbx_skin_deformer.dq_vertices/dq_weights` (indexed by vertex).
+ BLENDED_DQ_LINEAR,
+ FORCE_32BIT = 2147483647, // Blend between `UFBX_SKINNING_METHOD_LINEAR` and `UFBX_SKINNING_METHOD_BLENDED_DQ_LINEAR`
+ // The blend weight can be found either per-vertex in `ufbx_skin_vertex.dq_weight`
+ // or in `ufbx_skin_deformer.dq_vertices/dq_weights` (indexed by vertex).
+}
+
+SKINNING_METHOD_COUNT :: 4
+
+// Skin weight information for a single mesh vertex
+Skin_Vertex :: struct {
+ weight_begin: u32, // < Index to start from in the `weights[]` array
+ num_weights: u32, // < Number of weights influencing the vertex
+
+ // Blend weight between Linear Blend Skinning (0.0) and Dual Quaternion (1.0).
+ // Should be used if `skinning_method == UFBX_SKINNING_METHOD_BLENDED_DQ_LINEAR`
+ dq_weight: Real,
+}
+
+Skin_Vertex_List :: struct {
+ data: ^Skin_Vertex,
+ count: c.size_t,
+}
+
+// Single per-vertex per-cluster weight, see `ufbx_skin_vertex`
+Skin_Weight :: struct {
+ cluster_index: u32, // < Index into `ufbx_skin_deformer.clusters[]`
+ weight: Real, // < Amount this bone influence the vertex
+}
+
+Skin_Weight_List :: struct {
+ data: ^Skin_Weight,
+ count: c.size_t,
+}
+
+// Skin deformer specifies a binding between a logical set of bones (a skeleton)
+// and a mesh. Each bone is represented by a `ufbx_skin_cluster` that contains
+// the binding matrix and a `ufbx_node *bone` that has the current transformation.
+Skin_Deformer :: struct {
+ using _: struct #raw_union {
+ element: Element,
+ using _: struct {
+ name: String,
+ props: Props,
+ element_id: u32,
+ typed_id: u32,
+ },
+ },
+ skinning_method: Skinning_Method,
+
+ // Clusters (bones) in the skin
+ clusters: Skin_Cluster_List,
+
+ // Per-vertex weight information
+ vertices: Skin_Vertex_List,
+ weights: Skin_Weight_List,
+
+ // Largest amount of weights a single vertex can have
+ max_weights_per_vertex: c.size_t,
+
+ // Blend weights between Linear Blend Skinning (0.0) and Dual Quaternion (1.0).
+ // HINT: You probably want to use `vertices` and `ufbx_skin_vertex.dq_weight` instead!
+ // NOTE: These may be out-of-bounds for a given mesh, `vertices` is always safe.
+ num_dq_weights: c.size_t,
+ dq_vertices: Uint32_List,
+ dq_weights: Real_List,
+}
+
+// Cluster of vertices bound to a single bone.
+Skin_Cluster :: struct {
+ using _: struct #raw_union {
+ element: Element,
+ using _: struct {
+ name: String,
+ props: Props,
+ element_id: u32,
+ typed_id: u32,
+ },
+ },
+
+ // The bone node the cluster is attached to
+ // NOTE: Always valid if found from `ufbx_skin_deformer.clusters[]` unless
+ // `ufbx_load_opts.connect_broken_elements` is `true`.
+ bone_node: ^Node,
+
+ // Binding matrix from local mesh vertices to the bone
+ geometry_to_bone: Matrix,
+
+ // Binding matrix from local mesh _node_ to the bone.
+ // NOTE: Prefer `geometry_to_bone` in most use cases!
+ mesh_node_to_bone: Matrix,
+
+ // Matrix that specifies the rest/bind pose transform of the node,
+ // not generally needed for skinning, use `geometry_to_bone` instead.
+ bind_to_world: Matrix,
+
+ // Precomputed matrix/transform that accounts for the current bone transform
+ // ie. `ufbx_matrix_mul(&cluster->bone->node_to_world, &cluster->geometry_to_bone)`
+ geometry_to_world: Matrix,
+ geometry_to_world_transform: Transform,
+ num_weights: c.size_t, // < Number of vertices in the cluster
+ vertices: Uint32_List, // < Vertex indices in `ufbx_mesh.vertices[]`
+ weights: Real_List, // < Per-vertex weight values
+}
+
+// Blend shape deformer can contain multiple channels (think of sliders between morphs)
+// that may optionally have in-between keyframes.
+Blend_Deformer :: struct {
+ using _: struct #raw_union {
+ element: Element,
+ using _: struct {
+ name: String,
+ props: Props,
+ element_id: u32,
+ typed_id: u32,
+ },
+ },
+
+ // Independent morph targets of the deformer.
+ channels: Blend_Channel_List,
+}
+
+// Blend shape associated with a target weight in a series of morphs
+Blend_Keyframe :: struct {
+ // The target blend shape offsets.
+ shape: ^Blend_Shape,
+
+ // Weight value at which to apply the keyframe at full strength
+ target_weight: Real,
+
+ // The weight the shape should be currently applied with
+ effective_weight: Real,
+}
+
+Blend_Keyframe_List :: struct {
+ data: ^Blend_Keyframe,
+ count: c.size_t,
+}
+
+// Blend channel consists of multiple morph-key targets that are interpolated.
+// In simple cases there will be only one keyframe that is the target shape.
+Blend_Channel :: struct {
+ using _: struct #raw_union {
+ element: Element,
+ using _: struct {
+ name: String,
+ props: Props,
+ element_id: u32,
+ typed_id: u32,
+ },
+ },
+
+ // Current weight of the channel
+ weight: Real,
+
+ // Key morph targets to blend between depending on `weight`
+ // In usual cases there's only one target per channel
+ keyframes: Blend_Keyframe_List,
+
+ // Final blend shape ignoring any intermediate blend shapes.
+ target_shape: ^Blend_Shape,
+}
+
+// Blend shape target containing the actual vertex offsets
+Blend_Shape :: struct {
+ using _: struct #raw_union {
+ element: Element,
+ using _: struct {
+ name: String,
+ props: Props,
+ element_id: u32,
+ typed_id: u32,
+ },
+ },
+ num_offsets: c.size_t, // < Number of vertex offsets in the following arrays
+ offset_vertices: Uint32_List, // < Indices to `ufbx_mesh.vertices[]`
+ position_offsets: Vec3_List, // < Always specified per-vertex offsets
+ normal_offsets: Vec3_List, // < Empty if not specified
+}
+
+Cache_File_Format :: enum c.int {
+ UNKNOWN, // < Unknown cache file format
+ PC2, // < .pc2 Point cache file
+ MC, // < .mc/.mcx Maya cache file
+ FORCE_32BIT = 2147483647,
+}
+
+CACHE_FILE_FORMAT_COUNT :: 3
+
+Cache_Data_Format :: enum c.int {
+ UNKNOWN, // < Unknown data format
+ REAL_FLOAT, // < `float data[]`
+ VEC3_FLOAT, // < `struct { float x, y, z; } data[]`
+ REAL_DOUBLE, // < `double data[]`
+ VEC3_DOUBLE, // < `struct { double x, y, z; } data[]`
+ FORCE_32BIT = 2147483647,
+}
+
+CACHE_DATA_FORMAT_COUNT :: 5
+
+Cache_Data_Encoding :: enum c.int {
+ UNKNOWN, // < Unknown data encoding
+ LITTLE_ENDIAN, // < Contiguous little-endian array
+ BIG_ENDIAN, // < Contiguous big-endian array
+ FORCE_32BIT = 2147483647,
+}
+
+CACHE_DATA_ENCODING_COUNT :: 3
+
+// Known interpretations of geometry cache data.
+Cache_Interpretation :: enum c.int {
+ // Unknown interpretation, see `ufbx_cache_channel.interpretation_name` for more information.
+ UNKNOWN,
+
+ // Generic "points" interpretation, FBX SDK default. Usually fine to interpret
+ // as vertex positions if no other cache channels are specified.
+ POINTS,
+
+ // Vertex positions.
+ VERTEX_POSITION,
+
+ // Vertex normals.
+ VERTEX_NORMAL,
+ FORCE_32BIT = 2147483647, // Vertex normals.
+}
+
+CACHE_INTERPRETATION_COUNT :: 4
+
+Cache_Frame :: struct {
+ // Name of the channel this frame belongs to.
+ channel: String,
+
+ // Time of this frame in seconds.
+ time: f64,
+
+ // Name of the file containing the data.
+ // The specified file may contain multiple frames, use `data_offset` etc. to
+ // read at the right position.
+ filename: String,
+
+ // Format of the wrapper file.
+ file_format: Cache_File_Format,
+
+ // Axis to mirror the read data by.
+ mirror_axis: Mirror_Axis,
+
+ // Factor to scale the geometry by.
+ scale_factor: Real,
+ data_format: Cache_Data_Format, // < Format of the data in the file
+ data_encoding: Cache_Data_Encoding, // < Binary encoding of the data
+ data_offset: u64, // < Byte offset into the file
+ data_count: u32, // < Number of data elements
+ data_element_bytes: u32, // < Size of a single data element in bytes
+ data_total_bytes: u64, // < Size of the whole data blob in bytes
+}
+
+Cache_Frame_List :: struct {
+ data: ^Cache_Frame,
+ count: c.size_t,
+}
+
+Cache_Channel :: struct {
+ // Name of the geometry cache channel.
+ name: String,
+
+ // What does the data in this channel represent.
+ interpretation: Cache_Interpretation,
+
+ // Source name for `interpretation`, especially useful if `interpretation` is
+ // `UFBX_CACHE_INTERPRETATION_UNKNOWN`.
+ interpretation_name: String,
+
+ // List of frames belonging to this channel.
+ // Sorted by time (`ufbx_cache_frame.time`).
+ frames: Cache_Frame_List,
+
+ // Axis to mirror the frames by.
+ mirror_axis: Mirror_Axis,
+
+ // Factor to scale the geometry by.
+ scale_factor: Real,
+}
+
+Cache_Channel_List :: struct {
+ data: ^Cache_Channel,
+ count: c.size_t,
+}
+
+Geometry_Cache :: struct {
+ root_filename: String,
+ channels: Cache_Channel_List,
+ frames: Cache_Frame_List,
+ extra_info: String_List,
+}
+
+Cache_Deformer :: struct {
+ using _: struct #raw_union {
+ element: Element,
+ using _: struct {
+ name: String,
+ props: Props,
+ element_id: u32,
+ typed_id: u32,
+ },
+ },
+ channel: String,
+ file: ^Cache_File,
+
+ // Only valid if `ufbx_load_opts.load_external_files` is set!
+ external_cache: ^Geometry_Cache,
+ external_channel: ^Cache_Channel,
+}
+
+Cache_File :: struct {
+ using _: struct #raw_union {
+ element: Element,
+ using _: struct {
+ name: String,
+ props: Props,
+ element_id: u32,
+ typed_id: u32,
+ },
+ },
+
+ // Filename relative to the currently loaded file.
+ // HINT: If using functions other than `ufbx_load_file()`, you can provide
+ // `ufbx_load_opts.filename/raw_filename` to let ufbx resolve this.
+ filename: String,
+
+ // Absolute filename specified in the file.
+ absolute_filename: String,
+
+ // Relative filename specified in the file.
+ // NOTE: May be absolute if the file is saved in a different drive.
+ relative_filename: String,
+
+ // Filename relative to the loaded file, non-UTF-8 encoded.
+ // HINT: If using functions other than `ufbx_load_file()`, you can provide
+ // `ufbx_load_opts.filename/raw_filename` to let ufbx resolve this.
+ raw_filename: Blob,
+
+ // Absolute filename specified in the file, non-UTF-8 encoded.
+ raw_absolute_filename: Blob,
+
+ // Relative filename specified in the file, non-UTF-8 encoded.
+ // NOTE: May be absolute if the file is saved in a different drive.
+ raw_relative_filename: Blob,
+ format: Cache_File_Format,
+
+ // Only valid if `ufbx_load_opts.load_external_files` is set!
+ external_cache: ^Geometry_Cache,
+}
+
+// Material property, either specified with a constant value or a mapped texture
+Material_Map :: struct {
+ // Constant value or factor for the map.
+ // May be specified simultaneously with a texture, in this case most shading models
+ // use multiplicative tinting of the texture values.
+ using _: struct #raw_union {
+ value_real: Real,
+ value_vec2: Vec2,
+ value_vec3: Vec3,
+ value_vec4: Vec4,
+ },
+ value_int: i64,
+
+ // Texture if connected, otherwise `NULL`.
+ // May be valid but "disabled" (application specific) if `texture_enabled == false`.
+ texture: ^Texture,
+
+ // `true` if the file has specified any of the values above.
+ // NOTE: The value may be set to a non-zero default even if `has_value == false`,
+ // for example missing factors are set to `1.0` if a color is defined.
+ has_value: bool,
+
+ // Controls whether shading should use `texture`.
+ // NOTE: Some shading models allow this to be `true` even if `texture == NULL`.
+ texture_enabled: bool,
+
+ // Set to `true` if this feature should be disabled (specific to shader type).
+ feature_disabled: bool,
+
+ // Number of components in the value from 1 to 4 if defined, 0 if not.
+ value_components: u8,
+}
+
+// Material feature
+Material_Feature_Info :: struct {
+ // Whether the material model uses this feature or not.
+ // NOTE: The feature can be enabled but still not used if eg. the corresponding factor is at zero!
+ enabled: bool,
+
+ // Explicitly enabled/disabled by the material.
+ is_explicit: bool,
+}
+
+// Texture attached to an FBX property
+Material_Texture :: struct {
+ material_prop: String, // < Name of the property in `ufbx_material.props`
+ shader_prop: String, // < Shader-specific property mapping name
+
+ // Texture attached to the property.
+ texture: ^Texture,
+}
+
+Material_Texture_List :: struct {
+ data: ^Material_Texture,
+ count: c.size_t,
+}
+
+// Shading model type
+Shader_Type :: enum c.int {
+ // Unknown shading model
+ UNKNOWN,
+
+ // FBX builtin diffuse material
+ FBX_LAMBERT,
+
+ // FBX builtin diffuse+specular material
+ FBX_PHONG,
+
+ // Open Shading Language standard surface
+ // https://github.com/Autodesk/standard-surface
+ OSL_STANDARD_SURFACE,
+
+ // Arnold standard surface
+ // https://docs.arnoldrenderer.com/display/A5AFMUG/Standard+Surface
+ ARNOLD_STANDARD_SURFACE,
+
+ // 3ds Max Physical Material
+ // https://knowledge.autodesk.com/support/3ds-max/learn-explore/caas/CloudHelp/cloudhelp/2022/ENU/3DSMax-Lighting-Shading/files/GUID-C1328905-7783-4917-AB86-FC3CC19E8972-htm.html
+ _3DS_MAX_PHYSICAL_MATERIAL,
+
+ // 3ds Max PBR (Metal/Rough) material
+ // https://knowledge.autodesk.com/support/3ds-max/learn-explore/caas/CloudHelp/cloudhelp/2021/ENU/3DSMax-Lighting-Shading/files/GUID-A16234A5-6500-4662-8B20-A5EC9FE1B255-htm.html
+ _3DS_MAX_PBR_METAL_ROUGH,
+
+ // 3ds Max PBR (Spec/Gloss) material
+ // https://knowledge.autodesk.com/support/3ds-max/learn-explore/caas/CloudHelp/cloudhelp/2021/ENU/3DSMax-Lighting-Shading/files/GUID-18087194-B2A6-43EF-9B80-8FD1736FAE52-htm.html
+ _3DS_MAX_PBR_SPEC_GLOSS,
+
+ // 3ds glTF Material
+ // https://help.autodesk.com/view/3DSMAX/2023/ENU/?guid=GUID-7ABFB805-1D9F-417E-9C22-704BFDF160FA
+ GLTF_MATERIAL,
+
+ // 3ds OpenPBR Material
+ // https://help.autodesk.com/view/3DSMAX/2025/ENU/?guid=GUID-CD90329C-1E2B-4BBA-9285-3BB46253B9C2
+ OPENPBR_MATERIAL,
+
+ // Stingray ShaderFX shader graph.
+ // Contains a serialized `"ShaderGraph"` in `ufbx_props`.
+ SHADERFX_GRAPH,
+
+ // Variation of the FBX phong shader that can recover PBR properties like
+ // `metalness` or `roughness` from the FBX non-physical values.
+ // NOTE: Enable `ufbx_load_opts.use_blender_pbr_material`.
+ BLENDER_PHONG,
+
+ // Wavefront .mtl format shader (used by .obj files)
+ WAVEFRONT_MTL,
+ TYPE_FORCE_32BIT = 2147483647, // Wavefront .mtl format shader (used by .obj files)
+}
+
+SHADER_TYPE_COUNT :: 13
+
+// FBX builtin material properties, matches maps in `ufbx_material_fbx_maps`
+Material_Fbx_Map :: enum c.int {
+ DIFFUSE_FACTOR,
+ DIFFUSE_COLOR,
+ SPECULAR_FACTOR,
+ SPECULAR_COLOR,
+ SPECULAR_EXPONENT,
+ REFLECTION_FACTOR,
+ REFLECTION_COLOR,
+ TRANSPARENCY_FACTOR,
+ TRANSPARENCY_COLOR,
+ EMISSION_FACTOR,
+ EMISSION_COLOR,
+ AMBIENT_FACTOR,
+ AMBIENT_COLOR,
+ NORMAL_MAP,
+ BUMP,
+ BUMP_FACTOR,
+ DISPLACEMENT_FACTOR,
+ DISPLACEMENT,
+ VECTOR_DISPLACEMENT_FACTOR,
+ VECTOR_DISPLACEMENT,
+ MAP_FORCE_32BIT = 2147483647,
+}
+
+MATERIAL_FBX_MAP_COUNT :: 20
+
+// Known PBR material properties, matches maps in `ufbx_material_pbr_maps`
+Material_Pbr_Map :: enum c.int {
+ BASE_FACTOR,
+ BASE_COLOR,
+ ROUGHNESS,
+ METALNESS,
+ DIFFUSE_ROUGHNESS,
+ SPECULAR_FACTOR,
+ SPECULAR_COLOR,
+ SPECULAR_IOR,
+ SPECULAR_ANISOTROPY,
+ SPECULAR_ROTATION,
+ TRANSMISSION_FACTOR,
+ TRANSMISSION_COLOR,
+ TRANSMISSION_DEPTH,
+ TRANSMISSION_SCATTER,
+ TRANSMISSION_SCATTER_ANISOTROPY,
+ TRANSMISSION_DISPERSION,
+ TRANSMISSION_ROUGHNESS,
+ TRANSMISSION_EXTRA_ROUGHNESS,
+ TRANSMISSION_PRIORITY,
+ TRANSMISSION_ENABLE_IN_AOV,
+ SUBSURFACE_FACTOR,
+ SUBSURFACE_COLOR,
+ SUBSURFACE_RADIUS,
+ SUBSURFACE_SCALE,
+ SUBSURFACE_ANISOTROPY,
+ SUBSURFACE_TINT_COLOR,
+ SUBSURFACE_TYPE,
+ SHEEN_FACTOR,
+ SHEEN_COLOR,
+ SHEEN_ROUGHNESS,
+ COAT_FACTOR,
+ COAT_COLOR,
+ COAT_ROUGHNESS,
+ COAT_IOR,
+ COAT_ANISOTROPY,
+ COAT_ROTATION,
+ COAT_NORMAL,
+ COAT_AFFECT_BASE_COLOR,
+ COAT_AFFECT_BASE_ROUGHNESS,
+ THIN_FILM_FACTOR,
+ THIN_FILM_THICKNESS,
+ THIN_FILM_IOR,
+ EMISSION_FACTOR,
+ EMISSION_COLOR,
+ OPACITY,
+ INDIRECT_DIFFUSE,
+ INDIRECT_SPECULAR,
+ NORMAL_MAP,
+ TANGENT_MAP,
+ DISPLACEMENT_MAP,
+ MATTE_FACTOR,
+ MATTE_COLOR,
+ AMBIENT_OCCLUSION,
+ GLOSSINESS,
+ COAT_GLOSSINESS,
+ TRANSMISSION_GLOSSINESS,
+ MAP_FORCE_32BIT = 2147483647,
+}
+
+MATERIAL_PBR_MAP_COUNT :: 56
+
+// Known material features
+Material_Feature :: enum c.int {
+ PBR,
+ METALNESS,
+ DIFFUSE,
+ SPECULAR,
+ EMISSION,
+ TRANSMISSION,
+ COAT,
+ SHEEN,
+ OPACITY,
+ AMBIENT_OCCLUSION,
+ MATTE,
+ UNLIT,
+ IOR,
+ DIFFUSE_ROUGHNESS,
+ TRANSMISSION_ROUGHNESS,
+ THIN_WALLED,
+ CAUSTICS,
+ EXIT_TO_BACKGROUND,
+ INTERNAL_REFLECTIONS,
+ DOUBLE_SIDED,
+ ROUGHNESS_AS_GLOSSINESS,
+ COAT_ROUGHNESS_AS_GLOSSINESS,
+ TRANSMISSION_ROUGHNESS_AS_GLOSSINESS,
+ FORCE_32BIT = 2147483647,
+}
+
+MATERIAL_FEATURE_COUNT :: 23
+
+Material_Fbx_Maps :: struct {
+ using _: struct #raw_union {
+ maps: [20]Material_Map,
+ using _: struct {
+ diffuse_factor: Material_Map,
+ diffuse_color: Material_Map,
+ specular_factor: Material_Map,
+ specular_color: Material_Map,
+ specular_exponent: Material_Map,
+ reflection_factor: Material_Map,
+ reflection_color: Material_Map,
+ transparency_factor: Material_Map,
+ transparency_color: Material_Map,
+ emission_factor: Material_Map,
+ emission_color: Material_Map,
+ ambient_factor: Material_Map,
+ ambient_color: Material_Map,
+ normal_map: Material_Map,
+ bump: Material_Map,
+ bump_factor: Material_Map,
+ displacement_factor: Material_Map,
+ displacement: Material_Map,
+ vector_displacement_factor: Material_Map,
+ vector_displacement: Material_Map,
+ },
+ },
+}
+
+Material_Pbr_Maps :: struct {
+ using _: struct #raw_union {
+ maps: [56]Material_Map,
+ using _: struct {
+ base_factor: Material_Map,
+ base_color: Material_Map,
+ roughness: Material_Map,
+ metalness: Material_Map,
+ diffuse_roughness: Material_Map,
+ specular_factor: Material_Map,
+ specular_color: Material_Map,
+ specular_ior: Material_Map,
+ specular_anisotropy: Material_Map,
+ specular_rotation: Material_Map,
+ transmission_factor: Material_Map,
+ transmission_color: Material_Map,
+ transmission_depth: Material_Map,
+ transmission_scatter: Material_Map,
+ transmission_scatter_anisotropy: Material_Map,
+ transmission_dispersion: Material_Map,
+ transmission_roughness: Material_Map,
+ transmission_extra_roughness: Material_Map,
+ transmission_priority: Material_Map,
+ transmission_enable_in_aov: Material_Map,
+ subsurface_factor: Material_Map,
+ subsurface_color: Material_Map,
+ subsurface_radius: Material_Map,
+ subsurface_scale: Material_Map,
+ subsurface_anisotropy: Material_Map,
+ subsurface_tint_color: Material_Map,
+ subsurface_type: Material_Map,
+ sheen_factor: Material_Map,
+ sheen_color: Material_Map,
+ sheen_roughness: Material_Map,
+ coat_factor: Material_Map,
+ coat_color: Material_Map,
+ coat_roughness: Material_Map,
+ coat_ior: Material_Map,
+ coat_anisotropy: Material_Map,
+ coat_rotation: Material_Map,
+ coat_normal: Material_Map,
+ coat_affect_base_color: Material_Map,
+ coat_affect_base_roughness: Material_Map,
+ thin_film_factor: Material_Map,
+ thin_film_thickness: Material_Map,
+ thin_film_ior: Material_Map,
+ emission_factor: Material_Map,
+ emission_color: Material_Map,
+ opacity: Material_Map,
+ indirect_diffuse: Material_Map,
+ indirect_specular: Material_Map,
+ normal_map: Material_Map,
+ tangent_map: Material_Map,
+ displacement_map: Material_Map,
+ matte_factor: Material_Map,
+ matte_color: Material_Map,
+ ambient_occlusion: Material_Map,
+ glossiness: Material_Map,
+ coat_glossiness: Material_Map,
+ transmission_glossiness: Material_Map,
+ },
+ },
+}
+
+Material_Features :: struct {
+ using _: struct #raw_union {
+ features: [23]Material_Feature_Info,
+ using _: struct {
+ pbr: Material_Feature_Info,
+ metalness: Material_Feature_Info,
+ diffuse: Material_Feature_Info,
+ specular: Material_Feature_Info,
+ emission: Material_Feature_Info,
+ transmission: Material_Feature_Info,
+ coat: Material_Feature_Info,
+ sheen: Material_Feature_Info,
+ opacity: Material_Feature_Info,
+ ambient_occlusion: Material_Feature_Info,
+ matte: Material_Feature_Info,
+ unlit: Material_Feature_Info,
+ ior: Material_Feature_Info,
+ diffuse_roughness: Material_Feature_Info,
+ transmission_roughness: Material_Feature_Info,
+ thin_walled: Material_Feature_Info,
+ caustics: Material_Feature_Info,
+ exit_to_background: Material_Feature_Info,
+ internal_reflections: Material_Feature_Info,
+ double_sided: Material_Feature_Info,
+ roughness_as_glossiness: Material_Feature_Info,
+ coat_roughness_as_glossiness: Material_Feature_Info,
+ transmission_roughness_as_glossiness: Material_Feature_Info,
+ },
+ },
+}
+
+// Surface material properties such as color, roughness, etc. Each property may
+// be optionally bound to an `ufbx_texture`.
+Material :: struct {
+ using _: struct #raw_union {
+ element: Element,
+ using _: struct {
+ name: String,
+ props: Props,
+ element_id: u32,
+ typed_id: u32,
+ },
+ },
+
+ // FBX builtin properties
+ // NOTE: These may be empty if the material is using a custom shader
+ fbx: Material_Fbx_Maps,
+
+ // PBR material properties, defined for all shading models but may be
+ // somewhat approximate if `shader == NULL`.
+ pbr: Material_Pbr_Maps,
+
+ // Material features, primarily applies to `pbr`.
+ features: Material_Features,
+ shader_type: Shader_Type, // < Always defined
+ shader: ^Shader, // < Optional extended shader information
+ shading_model_name: String, // < Often one of `{ "lambert", "phong", "unknown" }`
+
+ // Prefix before shader property names with trailing `|`.
+ // For example `"3dsMax|Parameters|"` where properties would have names like
+ // `"3dsMax|Parameters|base_color"`. You can ignore this if you use the built-in
+ // `ufbx_material_fbx_maps fbx` and `ufbx_material_pbr_maps pbr` structures.
+ shader_prop_prefix: String,
+ textures: Material_Texture_List, // < Sorted by `material_prop`
+}
+
+Texture_Type :: enum c.int {
+ // Texture associated with an image file/sequence. `texture->filename` and
+ // and `texture->relative_filename` contain the texture's path. If the file
+ // has embedded content `texture->content` may hold `texture->content_size`
+ // bytes of raw image data.
+ FILE,
+
+ // The texture consists of multiple texture layers blended together.
+ LAYERED,
+
+ // Reserved as these _should_ exist in FBX files.
+ PROCEDURAL,
+
+ // Node in a shader graph.
+ // Use `ufbx_texture.shader` for more information.
+ SHADER,
+ TYPE_FORCE_32BIT = 2147483647, // Node in a shader graph.
+ // Use `ufbx_texture.shader` for more information.
+}
+
+TEXTURE_TYPE_COUNT :: 4
+
+// Blend modes to combine layered textures with, compatible with common blend
+// mode definitions in many art programs. Simpler blend modes have equations
+// specified below where `src` is the layer to composite over `dst`.
+// See eg. https://www.w3.org/TR/2013/WD-compositing-1-20131010/#blendingseparable
+Blend_Mode :: enum c.int {
+ TRANSLUCENT, // < `src` effects result alpha
+ ADDITIVE, // < `src + dst`
+ MULTIPLY, // < `src * dst`
+ MULTIPLY_2X, // < `2 * src * dst`
+ OVER, // < `src * src_alpha + dst * (1-src_alpha)`
+ REPLACE, // < `src` Replace the contents
+ DISSOLVE, // < `random() + src_alpha >= 1.0 ? src : dst`
+ DARKEN, // < `min(src, dst)`
+ COLOR_BURN, // < `src > 0 ? 1 - min(1, (1-dst) / src) : 0`
+ LINEAR_BURN, // < `src + dst - 1`
+ DARKER_COLOR, // < `value(src) < value(dst) ? src : dst`
+ LIGHTEN, // < `max(src, dst)`
+ SCREEN, // < `1 - (1-src)*(1-dst)`
+ COLOR_DODGE, // < `src < 1 ? dst / (1 - src)` : (dst>0?1:0)`
+ LINEAR_DODGE, // < `src + dst`
+ LIGHTER_COLOR, // < `value(src) > value(dst) ? src : dst`
+ SOFT_LIGHT, // < https://www.w3.org/TR/2013/WD-compositing-1-20131010/#blendingsoftlight
+ HARD_LIGHT, // < https://www.w3.org/TR/2013/WD-compositing-1-20131010/#blendinghardlight
+ VIVID_LIGHT, // < Combination of `COLOR_DODGE` and `COLOR_BURN`
+ LINEAR_LIGHT, // < Combination of `LINEAR_DODGE` and `LINEAR_BURN`
+ PIN_LIGHT, // < Combination of `DARKEN` and `LIGHTEN`
+ HARD_MIX, // < Produces primary colors depending on similarity
+ DIFFERENCE, // < `abs(src - dst)`
+ EXCLUSION, // < `dst + src - 2 * src * dst`
+ SUBTRACT, // < `dst - src`
+ DIVIDE, // < `dst / src`
+ HUE, // < Replace hue
+ SATURATION, // < Replace saturation
+ COLOR, // < Replace hue and saturatio
+ LUMINOSITY, // < Replace value
+ OVERLAY, // < Same as `HARD_LIGHT` but with `src` and `dst` swapped
+ MODE_FORCE_32BIT = 2147483647,
+}
+
+BLEND_MODE_COUNT :: 31
+
+// Blend modes to combine layered textures with, compatible with common blend
+Wrap_Mode :: enum c.int {
+ REPEAT, // < Repeat the texture past the [0,1] range
+ CLAMP, // < Clamp the normalized texture coordinates to [0,1]
+ MODE_FORCE_32BIT = 2147483647,
+}
+
+WRAP_MODE_COUNT :: 2
+
+// Single layer in a layered texture
+Texture_Layer :: struct {
+ texture: ^Texture, // < The inner texture to evaluate, never `NULL`
+ blend_mode: Blend_Mode, // < Equation to combine the layer to the background
+ alpha: Real, // < Blend weight of this layer
+}
+
+Texture_Layer_List :: struct {
+ data: ^Texture_Layer,
+ count: c.size_t,
+}
+
+Shader_Texture_Type :: enum c.int {
+ UNKNOWN,
+
+ // Select an output of a multi-output shader.
+ // HINT: If this type is used the `ufbx_shader_texture.main_texture` and
+ // `ufbx_shader_texture.main_texture_output_index` fields are set.
+ SELECT_OUTPUT,
+
+ // Open Shading Language (OSL) shader.
+ // https://github.com/AcademySoftwareFoundation/OpenShadingLanguage
+ OSL,
+ TYPE_FORCE_32BIT = 2147483647, // Open Shading Language (OSL) shader.
+ // https://github.com/AcademySoftwareFoundation/OpenShadingLanguage
+}
+
+SHADER_TEXTURE_TYPE_COUNT :: 3
+
+// Input to a shader texture, see `ufbx_shader_texture`.
+Shader_Texture_Input :: struct {
+ // Name of the input.
+ name: String,
+
+ // Constant value of the input.
+ using _: struct #raw_union {
+ value_real: Real,
+ value_vec2: Vec2,
+ value_vec3: Vec3,
+ value_vec4: Vec4,
+ },
+ value_int: i64,
+ value_str: String,
+ value_blob: Blob,
+
+ // Texture connected to this input.
+ texture: ^Texture,
+
+ // Index of the output to use if `texture` is a multi-output shader node.
+ texture_output_index: i64,
+
+ // Controls whether shading should use `texture`.
+ // NOTE: Some shading models allow this to be `true` even if `texture == NULL`.
+ texture_enabled: bool,
+
+ // Property representing this input.
+ prop: ^Prop,
+
+ // Property representing `texture`.
+ texture_prop: ^Prop,
+
+ // Property representing `texture_enabled`.
+ texture_enabled_prop: ^Prop,
+}
+
+Shader_Texture_Input_List :: struct {
+ data: ^Shader_Texture_Input,
+ count: c.size_t,
+}
+
+// Texture that emulates a shader graph node.
+// 3ds Max exports some materials as node graphs serialized to textures.
+// ufbx can parse a small subset of these, as normal maps are often hidden behind
+// some kind of bump node.
+// NOTE: These encode a lot of details of 3ds Max internals, not recommended for direct use.
+// HINT: `ufbx_texture.file_textures[]` contains a list of "real" textures that are connected
+// to the `ufbx_texture` that is pretending to be a shader node.
+Shader_Texture :: struct {
+ // Type of this shader node.
+ type: Shader_Texture_Type,
+
+ // Name of the shader to use.
+ shader_name: String,
+
+ // 64-bit opaque identifier for the shader type.
+ shader_type_id: u64,
+
+ // Input values/textures (possibly further shader textures) to the shader.
+ // Sorted by `ufbx_shader_texture_input.name`.
+ inputs: Shader_Texture_Input_List,
+
+ // Shader source code if found.
+ shader_source: String,
+ raw_shader_source: Blob,
+
+ // Representative texture for this shader.
+ // Only specified if `main_texture.outputs[main_texture_output_index]` is semantically
+ // equivalent to this texture.
+ main_texture: ^Texture,
+
+ // Output index of `main_texture` if it is a multi-output shader.
+ main_texture_output_index: i64,
+
+ // Prefix for properties related to this shader in `ufbx_texture`.
+ // NOTE: Contains the trailing '|' if not empty.
+ prop_prefix: String,
+}
+
+// Unique texture within the file.
+Texture_File :: struct {
+ // Index in `ufbx_scene.texture_files[]`.
+ index: u32,
+
+ // Filename relative to the currently loaded file.
+ // HINT: If using functions other than `ufbx_load_file()`, you can provide
+ // `ufbx_load_opts.filename/raw_filename` to let ufbx resolve this.
+ filename: String,
+
+ // Absolute filename specified in the file.
+ absolute_filename: String,
+
+ // Relative filename specified in the file.
+ // NOTE: May be absolute if the file is saved in a different drive.
+ relative_filename: String,
+
+ // Filename relative to the loaded file, non-UTF-8 encoded.
+ // HINT: If using functions other than `ufbx_load_file()`, you can provide
+ // `ufbx_load_opts.filename/raw_filename` to let ufbx resolve this.
+ raw_filename: Blob,
+
+ // Absolute filename specified in the file, non-UTF-8 encoded.
+ raw_absolute_filename: Blob,
+
+ // Relative filename specified in the file, non-UTF-8 encoded.
+ // NOTE: May be absolute if the file is saved in a different drive.
+ raw_relative_filename: Blob,
+
+ // Optional embedded content blob, eg. raw .png format data
+ content: Blob,
+}
+
+Texture_File_List :: struct {
+ data: ^Texture_File,
+ count: c.size_t,
+}
+
+// Texture that controls material appearance
+Texture :: struct {
+ using _: struct #raw_union {
+ element: Element,
+ using _: struct {
+ name: String,
+ props: Props,
+ element_id: u32,
+ typed_id: u32,
+ },
+ },
+
+ // Texture type (file / layered / procedural / shader)
+ type: Texture_Type,
+
+ // Filename relative to the currently loaded file.
+ // HINT: If using functions other than `ufbx_load_file()`, you can provide
+ // `ufbx_load_opts.filename/raw_filename` to let ufbx resolve this.
+ filename: String,
+
+ // Absolute filename specified in the file.
+ absolute_filename: String,
+
+ // Relative filename specified in the file.
+ // NOTE: May be absolute if the file is saved in a different drive.
+ relative_filename: String,
+
+ // Filename relative to the loaded file, non-UTF-8 encoded.
+ // HINT: If using functions other than `ufbx_load_file()`, you can provide
+ // `ufbx_load_opts.filename/raw_filename` to let ufbx resolve this.
+ raw_filename: Blob,
+
+ // Absolute filename specified in the file, non-UTF-8 encoded.
+ raw_absolute_filename: Blob,
+
+ // Relative filename specified in the file, non-UTF-8 encoded.
+ // NOTE: May be absolute if the file is saved in a different drive.
+ raw_relative_filename: Blob,
+
+ // FILE: Optional embedded content blob, eg. raw .png format data
+ content: Blob,
+
+ // FILE: Optional video texture
+ video: ^Video,
+
+ // FILE: Index into `ufbx_scene.texture_files[]` or `UFBX_NO_INDEX`.
+ file_index: u32,
+
+ // FILE: True if `file_index` has a valid value.
+ has_file: bool,
+
+ // LAYERED: Inner texture layers, ordered from _bottom_ to _top_
+ layers: Texture_Layer_List,
+
+ // SHADER: Shader information
+ // NOTE: May be specified even if `type == UFBX_TEXTURE_FILE` if `ufbx_load_opts.disable_quirks`
+ // is _not_ specified. Some known shaders that represent files are interpreted as `UFBX_TEXTURE_FILE`.
+ shader: ^Shader_Texture,
+
+ // List of file textures representing this texture.
+ // Defined even if `type == UFBX_TEXTURE_FILE` in which case the array contains only itself.
+ file_textures: Texture_List,
+
+ // Name of the UV set to use
+ uv_set: String,
+
+ // Wrapping mode
+ wrap_u: Wrap_Mode,
+ wrap_v: Wrap_Mode,
+ has_uv_transform: bool, // < Has a non-identity `transform` and derived matrices.
+ uv_transform: Transform, // < Texture transformation in UV space
+ texture_to_uv: Matrix, // < Matrix representation of `transform`
+ uv_to_texture: Matrix, // < UV coordinate to normalized texture coordinate matrix
+}
+
+// TODO: Video textures
+Video :: struct {
+ using _: struct #raw_union {
+ element: Element,
+ using _: struct {
+ name: String,
+ props: Props,
+ element_id: u32,
+ typed_id: u32,
+ },
+ },
+
+ // Filename relative to the currently loaded file.
+ // HINT: If using functions other than `ufbx_load_file()`, you can provide
+ // `ufbx_load_opts.filename/raw_filename` to let ufbx resolve this.
+ filename: String,
+
+ // Absolute filename specified in the file.
+ absolute_filename: String,
+
+ // Relative filename specified in the file.
+ // NOTE: May be absolute if the file is saved in a different drive.
+ relative_filename: String,
+
+ // Filename relative to the loaded file, non-UTF-8 encoded.
+ // HINT: If using functions other than `ufbx_load_file()`, you can provide
+ // `ufbx_load_opts.filename/raw_filename` to let ufbx resolve this.
+ raw_filename: Blob,
+
+ // Absolute filename specified in the file, non-UTF-8 encoded.
+ raw_absolute_filename: Blob,
+
+ // Relative filename specified in the file, non-UTF-8 encoded.
+ // NOTE: May be absolute if the file is saved in a different drive.
+ raw_relative_filename: Blob,
+
+ // Optional embedded content blob
+ content: Blob,
+}
+
+// Shader specifies a shading model and contains `ufbx_shader_binding` elements
+// that define how to interpret FBX properties in the shader.
+Shader :: struct {
+ using _: struct #raw_union {
+ element: Element,
+ using _: struct {
+ name: String,
+ props: Props,
+ element_id: u32,
+ typed_id: u32,
+ },
+ },
+
+ // Known shading model
+ type: Shader_Type,
+
+ // Bindings from FBX properties to the shader
+ // HINT: `ufbx_find_shader_prop()` translates shader properties to FBX properties
+ bindings: Shader_Binding_List,
+}
+
+// Binding from a material property to shader implementation
+Shader_Prop_Binding :: struct {
+ shader_prop: String, // < Property name used by the shader implementation
+ material_prop: String, // < Property name inside `ufbx_material.props`
+}
+
+Shader_Prop_Binding_List :: struct {
+ data: ^Shader_Prop_Binding,
+ count: c.size_t,
+}
+
+// Shader binding table
+Shader_Binding :: struct {
+ using _: struct #raw_union {
+ element: Element,
+ using _: struct {
+ name: String,
+ props: Props,
+ element_id: u32,
+ typed_id: u32,
+ },
+ },
+ prop_bindings: Shader_Prop_Binding_List, // < Sorted by `shader_prop`
+}
+
+// -- Animation
+Prop_Override :: struct {
+ element_id: u32,
+ _internal_key: u32,
+ prop_name: String,
+ value: Vec4,
+ value_str: String,
+ value_int: i64,
+}
+
+Prop_Override_List :: struct {
+ data: ^Prop_Override,
+ count: c.size_t,
+}
+
+Transform_Override :: struct {
+ node_id: u32,
+ transform: Transform,
+}
+
+Transform_Override_List :: struct {
+ data: ^Transform_Override,
+ count: c.size_t,
+}
+
+// Animation descriptor used for evaluating animation.
+// Usually obtained from `ufbx_scene` via either global animation `ufbx_scene.anim`,
+// per-stack animation `ufbx_anim_stack.anim` or per-layer animation `ufbx_anim_layer.anim`.
+//
+// For advanced usage you can use `ufbx_create_anim()` to create animation descriptors
+// with custom layers, property overrides, special flags, etc.
+Anim :: struct {
+ // Time begin/end for the animation, both may be zero if absent.
+ time_begin: f64,
+ time_end: f64,
+
+ // List of layers in the animation.
+ layers: Anim_Layer_List,
+
+ // Optional overrides for weights for each layer in `layers[]`.
+ override_layer_weights: Real_List,
+
+ // Sorted by `element_id, prop_name`
+ prop_overrides: Prop_Override_List,
+
+ // Sorted by `node_id`
+ transform_overrides: Transform_Override_List,
+
+ // Evaluate connected properties as if they would not be connected.
+ ignore_connections: bool,
+
+ // Custom `ufbx_anim` created by `ufbx_create_anim()`.
+ custom: bool,
+}
+
+Anim_Stack :: struct {
+ using _: struct #raw_union {
+ element: Element,
+ using _: struct {
+ name: String,
+ props: Props,
+ element_id: u32,
+ typed_id: u32,
+ },
+ },
+ time_begin: f64,
+ time_end: f64,
+ layers: Anim_Layer_List,
+ anim: ^Anim,
+}
+
+Anim_Prop :: struct {
+ element: ^Element,
+ _internal_key: u32,
+ prop_name: String,
+ anim_value: ^Anim_Value,
+}
+
+Anim_Prop_List :: struct {
+ data: ^Anim_Prop,
+ count: c.size_t,
+}
+
+Anim_Layer :: struct {
+ using _: struct #raw_union {
+ element: Element,
+ using _: struct {
+ name: String,
+ props: Props,
+ element_id: u32,
+ typed_id: u32,
+ },
+ },
+ weight: Real,
+ weight_is_animated: bool,
+ blended: bool,
+ additive: bool,
+ compose_rotation: bool,
+ compose_scale: bool,
+ anim_values: Anim_Value_List,
+ anim_props: Anim_Prop_List, // < Sorted by `element,prop_name`
+ anim: ^Anim,
+ _min_element_id: u32,
+ _max_element_id: u32,
+ _element_id_bitmask: [4]u32,
+}
+
+Anim_Value :: struct {
+ using _: struct #raw_union {
+ element: Element,
+ using _: struct {
+ name: String,
+ props: Props,
+ element_id: u32,
+ typed_id: u32,
+ },
+ },
+ default_value: Vec3,
+ curves: [3]^Anim_Curve,
+}
+
+// Animation curve segment interpolation mode between two keyframes
+Interpolation :: enum c.int {
+ CONSTANT_PREV, // < Hold previous key value
+ CONSTANT_NEXT, // < Hold next key value
+ LINEAR, // < Linear interpolation between two keys
+ CUBIC, // < Cubic interpolation, see `ufbx_tangent`
+ FORCE_32BIT = 2147483647,
+}
+
+INTERPOLATION_COUNT :: 4
+
+Extrapolation_Mode :: enum c.int {
+ CONSTANT, // < Use the value of the first/last keyframe
+ REPEAT, // < Repeat the whole animation curve
+ MIRROR, // < Repeat with mirroring
+ SLOPE, // < Use the tangent of the last keyframe to linearly extrapolate
+ REPEAT_RELATIVE, // < Repeat the animation curve but connect the first and last keyframe values
+ FORCE_32BIT = 2147483647,
+}
+
+EXTRAPOLATION_MODE_COUNT :: 5
+
+Extrapolation :: struct {
+ mode: Extrapolation_Mode,
+
+ // Count used for repeating modes.
+ // Negative values mean infinite repetition.
+ repeat_count: i32,
+}
+
+// Tangent vector at a keyframe, may be split into left/right
+Tangent :: struct {
+ dx: f32, // < Derivative in the time axis
+ dy: f32, // < Derivative in the (curve specific) value axis
+}
+
+// Single real `value` at a specified `time`, interpolation between two keyframes
+// is determined by the `interpolation` field of the _previous_ key.
+// If `interpolation == UFBX_INTERPOLATION_CUBIC` the span is evaluated as a
+// cubic bezier curve through the following points:
+//
+// (prev->time, prev->value)
+// (prev->time + prev->right.dx, prev->value + prev->right.dy)
+// (next->time - next->left.dx, next->value - next->left.dy)
+// (next->time, next->value)
+//
+// HINT: You can use `ufbx_evaluate_curve(ufbx_anim_curve *curve, double time)`
+// rather than trying to manually handle all the interpolation modes.
+Keyframe :: struct {
+ time: f64,
+ value: Real,
+ interpolation: Interpolation,
+ left: Tangent,
+ right: Tangent,
+}
+
+Keyframe_List :: struct {
+ data: ^Keyframe,
+ count: c.size_t,
+}
+
+Anim_Curve :: struct {
+ using _: struct #raw_union {
+ element: Element,
+ using _: struct {
+ name: String,
+ props: Props,
+ element_id: u32,
+ typed_id: u32,
+ },
+ },
+
+ // List of keyframes that define the curve.
+ keyframes: Keyframe_List,
+
+ // Extrapolation before the curve.
+ pre_extrapolation: Extrapolation,
+
+ // Extrapolation after the curve.
+ post_extrapolation: Extrapolation,
+
+ // Value range for all the keyframes.
+ min_value: Real,
+ max_value: Real,
+
+ // Time range for all the keyframes.
+ min_time: f64,
+ max_time: f64,
+}
+
+// Collection of nodes to hide/freeze
+Display_Layer :: struct {
+ using _: struct #raw_union {
+ element: Element,
+ using _: struct {
+ name: String,
+ props: Props,
+ element_id: u32,
+ typed_id: u32,
+ },
+ },
+
+ // Nodes included in the layer (exclusively at most one layer per node)
+ nodes: Node_List,
+ visible: bool, // < Contained nodes are visible
+ frozen: bool, // < Contained nodes cannot be edited
+ ui_color: Vec3, // < Visual color for UI
+}
+
+// Named set of nodes/geometry features to select.
+Selection_Set :: struct {
+ using _: struct #raw_union {
+ element: Element,
+ using _: struct {
+ name: String,
+ props: Props,
+ element_id: u32,
+ typed_id: u32,
+ },
+ },
+
+ // Included nodes and geometry features
+ nodes: Selection_Node_List,
+}
+
+// Selection state of a node, potentially contains vertex/edge/face selection as well.
+Selection_Node :: struct {
+ using _: struct #raw_union {
+ element: Element,
+ using _: struct {
+ name: String,
+ props: Props,
+ element_id: u32,
+ typed_id: u32,
+ },
+ },
+
+ // Selection targets, possibly `NULL`
+ target_node: ^Node,
+ target_mesh: ^Mesh,
+ include_node: bool, // < Is `target_node` included in the selection
+ vertices: Uint32_List, // < Indices to `ufbx_mesh.vertices`
+ edges: Uint32_List, // < Indices to `ufbx_mesh.edges`
+ faces: Uint32_List, // < Indices to `ufbx_mesh.faces`
+}
+
+// -- Constraints
+Character :: struct {
+ using _: struct #raw_union {
+ element: Element,
+ using _: struct {
+ name: String,
+ props: Props,
+ element_id: u32,
+ typed_id: u32,
+ },
+ },
+}
+
+// Type of property constrain eg. position or look-at
+Constraint_Type :: enum c.int {
+ UNKNOWN,
+ AIM,
+ PARENT,
+ POSITION,
+ ROTATION,
+ SCALE,
+
+ // Inverse kinematic chain to a single effector `ufbx_constraint.ik_effector`
+ // `targets` optionally contains a list of pole targets!
+ SINGLE_CHAIN_IK,
+ TYPE_FORCE_32BIT = 2147483647, // Inverse kinematic chain to a single effector `ufbx_constraint.ik_effector`
+ // `targets` optionally contains a list of pole targets!
+}
+
+CONSTRAINT_TYPE_COUNT :: 7
+
+// Target to follow with a constraint
+Constraint_Target :: struct {
+ node: ^Node, // < Target node reference
+ weight: Real, // < Relative weight to other targets (does not always sum to 1)
+ transform: Transform, // < Offset from the actual target
+}
+
+Constraint_Target_List :: struct {
+ data: ^Constraint_Target,
+ count: c.size_t,
+}
+
+// Method to determine the up vector in aim constraints
+Constraint_Aim_Up_Type :: enum c.int {
+ SCENE, // < Align the up vector to the scene global up vector
+ TO_NODE, // < Aim the up vector at `ufbx_constraint.aim_up_node`
+ ALIGN_NODE, // < Copy the up vector from `ufbx_constraint.aim_up_node`
+ VECTOR, // < Use `ufbx_constraint.aim_up_vector` as the up vector
+ NONE, // < Don't align the up vector to anything
+ TYPE_FORCE_32BIT = 2147483647,
+}
+
+CONSTRAINT_AIM_UP_TYPE_COUNT :: 5
+
+// Method to determine the up vector in aim constraints
+Constraint_Ik_Pole_Type :: enum c.int {
+ VECTOR, // < Use towards calculated from `ufbx_constraint.targets`
+ NODE, // < Use `ufbx_constraint.ik_pole_vector` directly
+ TYPE_FORCE_32BIT = 2147483647,
+}
+
+CONSTRAINT_IK_POLE_TYPE_COUNT :: 2
+
+Constraint :: struct {
+ using _: struct #raw_union {
+ element: Element,
+ using _: struct {
+ name: String,
+ props: Props,
+ element_id: u32,
+ typed_id: u32,
+ },
+ },
+
+ // Type of constraint to use
+ type: Constraint_Type,
+ type_name: String,
+
+ // Node to be constrained
+ node: ^Node,
+
+ // List of weighted targets for the constraint (pole vectors for IK)
+ targets: Constraint_Target_List,
+
+ // State of the constraint
+ weight: Real,
+ active: bool,
+
+ // Translation/rotation/scale axes the constraint is applied to
+ constrain_translation: [3]bool,
+ constrain_rotation: [3]bool,
+ constrain_scale: [3]bool,
+
+ // Offset from the constrained position
+ transform_offset: Transform,
+
+ // AIM: Target and up vectors
+ aim_vector: Vec3,
+ aim_up_type: Constraint_Aim_Up_Type,
+ aim_up_node: ^Node,
+ aim_up_vector: Vec3,
+
+ // SINGLE_CHAIN_IK: Target for the IK, `targets` contains pole vectors!
+ ik_effector: ^Node,
+ ik_end_node: ^Node,
+ ik_pole_vector: Vec3,
+}
+
+// -- Audio
+Audio_Layer :: struct {
+ using _: struct #raw_union {
+ element: Element,
+ using _: struct {
+ name: String,
+ props: Props,
+ element_id: u32,
+ typed_id: u32,
+ },
+ },
+
+ // Clips contained in this layer.
+ clips: Audio_Clip_List,
+}
+
+Audio_Clip :: struct {
+ using _: struct #raw_union {
+ element: Element,
+ using _: struct {
+ name: String,
+ props: Props,
+ element_id: u32,
+ typed_id: u32,
+ },
+ },
+
+ // Filename relative to the currently loaded file.
+ // HINT: If using functions other than `ufbx_load_file()`, you can provide
+ // `ufbx_load_opts.filename/raw_filename` to let ufbx resolve this.
+ filename: String,
+
+ // Absolute filename specified in the file.
+ absolute_filename: String,
+
+ // Relative filename specified in the file.
+ // NOTE: May be absolute if the file is saved in a different drive.
+ relative_filename: String,
+
+ // Filename relative to the loaded file, non-UTF-8 encoded.
+ // HINT: If using functions other than `ufbx_load_file()`, you can provide
+ // `ufbx_load_opts.filename/raw_filename` to let ufbx resolve this.
+ raw_filename: Blob,
+
+ // Absolute filename specified in the file, non-UTF-8 encoded.
+ raw_absolute_filename: Blob,
+
+ // Relative filename specified in the file, non-UTF-8 encoded.
+ // NOTE: May be absolute if the file is saved in a different drive.
+ raw_relative_filename: Blob,
+
+ // Optional embedded content blob, eg. raw .png format data
+ content: Blob,
+}
+
+// -- Miscellaneous
+Bone_Pose :: struct {
+ // Node to apply the pose to.
+ bone_node: ^Node,
+
+ // Matrix from node local space to world space.
+ bone_to_world: Matrix,
+
+ // Matrix from node local space to parent space.
+ // NOTE: FBX only stores world transformations so this is approximated from
+ // the parent world transform.
+ bone_to_parent: Matrix,
+}
+
+Bone_Pose_List :: struct {
+ data: ^Bone_Pose,
+ count: c.size_t,
+}
+
+Pose :: struct {
+ using _: struct #raw_union {
+ element: Element,
+ using _: struct {
+ name: String,
+ props: Props,
+ element_id: u32,
+ typed_id: u32,
+ },
+ },
+
+ // Set if this pose is marked as a bind pose.
+ is_bind_pose: bool,
+
+ // List of bone poses.
+ // Sorted by `ufbx_node.typed_id`.
+ bone_poses: Bone_Pose_List,
+}
+
+Metadata_Object :: struct {
+ using _: struct #raw_union {
+ element: Element,
+ using _: struct {
+ name: String,
+ props: Props,
+ element_id: u32,
+ typed_id: u32,
+ },
+ },
+}
+
+// -- Named elements
+Name_Element :: struct {
+ name: String,
+ type: Element_Type,
+ _internal_key: u32,
+ element: ^Element,
+}
+
+Name_Element_List :: struct {
+ data: ^Name_Element,
+ count: c.size_t,
+}
+
+// Scene is the root object loaded by ufbx that everything is accessed from.
+Exporter :: enum c.int {
+ UNKNOWN,
+ FBX_SDK,
+ BLENDER_BINARY,
+ BLENDER_ASCII,
+ MOTION_BUILDER,
+ FORCE_32BIT = 2147483647,
+}
+
+EXPORTER_COUNT :: 5
+
+Application :: struct {
+ vendor: String,
+ name: String,
+ version: String,
+}
+
+File_Format :: enum c.int {
+ UNKNOWN, // < Unknown file format
+ FBX, // < .fbx Kaydara/Autodesk FBX file
+ OBJ, // < .obj Wavefront OBJ file
+ MTL, // < .mtl Wavefront MTL (Material template library) file
+ FORCE_32BIT = 2147483647,
+}
+
+FILE_FORMAT_COUNT :: 4
+
+Warning_Type :: enum c.int {
+ // Missing external file file (for example .mtl for Wavefront .obj file or a
+ // geometry cache)
+ MISSING_EXTERNAL_FILE,
+
+ // Loaded a Wavefront .mtl file derived from the filename instead of a proper
+ // `mtllib` statement.
+ IMPLICIT_MTL,
+
+ // Truncated array has been auto-expanded.
+ TRUNCATED_ARRAY,
+
+ // Geometry data has been defined but has no data.
+ MISSING_GEOMETRY_DATA,
+
+ // Duplicated connection between two elements that shouldn't have.
+ DUPLICATE_CONNECTION,
+
+ // Vertex 'W' attribute length differs from main attribute.
+ BAD_VERTEX_W_ATTRIBUTE,
+
+ // Missing polygon mapping type.
+ MISSING_POLYGON_MAPPING,
+
+ // Unsupported version, loaded but may be incorrect.
+ // If the loading fails `UFBX_ERROR_UNSUPPORTED_VERSION` is issued instead.
+ UNSUPPORTED_VERSION,
+
+ // Out-of-bounds index has been clamped to be in-bounds.
+ // HINT: You can use `ufbx_index_error_handling` to adjust behavior.
+ INDEX_CLAMPED,
+
+ // Non-UTF8 encoded strings.
+ // HINT: You can use `ufbx_unicode_error_handling` to adjust behavior.
+ BAD_UNICODE,
+
+ // Invalid base64-encoded embedded content ignored.
+ BAD_BASE64_CONTENT,
+
+ // Non-node element connected to root.
+ BAD_ELEMENT_CONNECTED_TO_ROOT,
+
+ // Duplicated object ID in the file, connections will be wrong.
+ DUPLICATE_OBJECT_ID,
+
+ // Empty face has been removed.
+ // Use `ufbx_load_opts.allow_empty_faces` if you want to allow them.
+ EMPTY_FACE_REMOVED,
+
+ // Unknown .obj file directive.
+ UNKNOWN_OBJ_DIRECTIVE,
+
+ // Warnings after this one are deduplicated.
+ // See `ufbx_warning.count` for how many times they happened.
+ TYPE_FIRST_DEDUPLICATED = 8,
+ TYPE_FORCE_32BIT = 2147483647, // Warnings after this one are deduplicated.
+ // See `ufbx_warning.count` for how many times they happened.
+}
+
+WARNING_TYPE_COUNT :: 15
+
+// Warning about a non-fatal issue in the file.
+// Often contains information about issues that ufbx has corrected about the
+// file but it might indicate something is not working properly.
+Warning :: struct {
+ // Type of the warning.
+ type: Warning_Type,
+
+ // Description of the warning.
+ description: String,
+
+ // The element related to this warning or `UFBX_NO_INDEX` if not related to a specific element.
+ element_id: u32,
+
+ // Number of times this warning was encountered.
+ count: c.size_t,
+}
+
+Warning_List :: struct {
+ data: ^Warning,
+ count: c.size_t,
+}
+
+Thumbnail_Format :: enum c.int {
+ UNKNOWN, // < Unknown format
+ RGB_24, // < 8-bit RGB pixels, in memory R,G,B
+ RGBA_32, // < 8-bit RGBA pixels, in memory R,G,B,A
+ FORCE_32BIT = 2147483647,
+}
+
+THUMBNAIL_FORMAT_COUNT :: 3
+
+// Specify how unit / coordinate system conversion should be performed.
+// Affects how `ufbx_load_opts.target_axes` and `ufbx_load_opts.target_unit_meters` work,
+// has no effect if neither is specified.
+Space_Conversion :: enum c.int {
+ // Store the space conversion transform in the root node.
+ // Sets `ufbx_node.local_transform` of the root node.
+ TRANSFORM_ROOT,
+
+ // Perform the conversion by using "adjust" transforms.
+ // Compensates for the transforms using `ufbx_node.adjust_pre_rotation` and
+ // `ufbx_node.adjust_pre_scale`. You don't need to account for these unless
+ // you are manually building transforms from `ufbx_props`.
+ ADJUST_TRANSFORMS,
+
+ // Perform the conversion by scaling geometry in addition to adjusting transforms.
+ // Compensates transforms like `UFBX_SPACE_CONVERSION_ADJUST_TRANSFORMS` but
+ // applies scaling to geometry as well.
+ MODIFY_GEOMETRY,
+ FORCE_32BIT = 2147483647, // Perform the conversion by scaling geometry in addition to adjusting transforms.
+ // Compensates transforms like `UFBX_SPACE_CONVERSION_ADJUST_TRANSFORMS` but
+ // applies scaling to geometry as well.
+}
+
+SPACE_CONVERSION_COUNT :: 3
+
+// Embedded thumbnail in the file, valid if the dimensions are non-zero.
+Thumbnail :: struct {
+ props: Props,
+
+ // Extents of the thumbnail
+ width: u32,
+ height: u32,
+
+ // Format of `ufbx_thumbnail.data`.
+ format: Thumbnail_Format,
+
+ // Thumbnail pixel data, layout as contiguous rows from bottom to top.
+ // See `ufbx_thumbnail.format` for the pixel format.
+ data: Blob,
+}
+
+// Miscellaneous data related to the loaded file
+Metadata :: struct {
+ // List of non-fatal warnings about the file.
+ // If you need to only check whether a specific warning was triggered you
+ // can use `ufbx_metadata.has_warning[]`.
+ warnings: Warning_List,
+
+ // FBX ASCII file format.
+ ascii: bool,
+
+ // FBX version in integer format, eg. 7400 for 7.4.
+ version: u32,
+
+ // File format of the source file.
+ file_format: File_Format,
+
+ // Index arrays may contain `UFBX_NO_INDEX` instead of a valid index
+ // to indicate gaps.
+ may_contain_no_index: bool,
+
+ // May contain meshes with no defined vertex position.
+ // NOTE: `ufbx_mesh.vertex_position.exists` may be `false`!
+ may_contain_missing_vertex_position: bool,
+
+ // Arrays may contain items with `NULL` element references.
+ // See `ufbx_load_opts.connect_broken_elements`.
+ may_contain_broken_elements: bool,
+
+ // Some API guarantees do not apply (depending on unsafe options used).
+ // Loaded with `ufbx_load_opts.allow_unsafe` enabled.
+ is_unsafe: bool,
+
+ // Flag for each possible warning type.
+ // See `ufbx_metadata.warnings[]` for detailed warning information.
+ has_warning: [15]bool,
+ creator: String,
+ big_endian: bool,
+ filename: String,
+ relative_root: String,
+ raw_filename: Blob,
+ raw_relative_root: Blob,
+ exporter: Exporter,
+ exporter_version: u32,
+ scene_props: Props,
+ original_application: Application,
+ latest_application: Application,
+ thumbnail: Thumbnail,
+ geometry_ignored: bool,
+ animation_ignored: bool,
+ embedded_ignored: bool,
+ max_face_triangles: c.size_t,
+ result_memory_used: c.size_t,
+ temp_memory_used: c.size_t,
+ result_allocs: c.size_t,
+ temp_allocs: c.size_t,
+ element_buffer_size: c.size_t,
+ num_shader_textures: c.size_t,
+ bone_prop_size_unit: Real,
+ bone_prop_limb_length_relative: bool,
+ ortho_size_unit: Real,
+ ktime_second: i64, // < One second in internal KTime units
+ original_file_path: String,
+ raw_original_file_path: Blob,
+
+ // Space conversion method used on the scene.
+ space_conversion: Space_Conversion,
+
+ // Transform that has been applied to root for axis/unit conversion.
+ root_rotation: Quat,
+ root_scale: Real,
+
+ // Axis that the scene has been mirrored by.
+ // All geometry has been mirrored in this axis.
+ mirror_axis: Mirror_Axis,
+
+ // Amount geometry has been scaled.
+ // See `UFBX_SPACE_CONVERSION_MODIFY_GEOMETRY`.
+ geometry_scale: Real,
+}
+
+Time_Mode :: enum c.int {
+ DEFAULT,
+ _120_FPS,
+ _100_FPS,
+ _60_FPS,
+ _50_FPS,
+ _48_FPS,
+ _30_FPS,
+ _30_FPS_DROP,
+ NTSC_DROP_FRAME,
+ NTSC_FULL_FRAME,
+ PAL,
+ _24_FPS,
+ _1000_FPS,
+ FILM_FULL_FRAME,
+ CUSTOM,
+ _96_FPS,
+ _72_FPS,
+ _59_94_FPS,
+ FORCE_32BIT = 2147483647,
+}
+
+TIME_MODE_COUNT :: 18
+
+Time_Protocol :: enum c.int {
+ SMPTE,
+ FRAME_COUNT,
+ DEFAULT,
+ FORCE_32BIT = 2147483647,
+}
+
+TIME_PROTOCOL_COUNT :: 3
+
+Snap_Mode :: enum c.int {
+ NONE,
+ SNAP,
+ PLAY,
+ SNAP_AND_PLAY,
+ FORCE_32BIT = 2147483647,
+}
+
+SNAP_MODE_COUNT :: 4
+
+// Global settings: Axes and time/unit scales
+Scene_Settings :: struct {
+ props: Props,
+
+ // Mapping of X/Y/Z axes to world-space directions.
+ // HINT: Use `ufbx_load_opts.target_axes` to normalize this.
+ // NOTE: This contains the _original_ axes even if you supply `ufbx_load_opts.target_axes`.
+ axes: Coordinate_Axes,
+
+ // How many meters does a single world-space unit represent.
+ // FBX files usually default to centimeters, reported as `0.01` here.
+ // HINT: Use `ufbx_load_opts.target_unit_meters` to normalize this.
+ unit_meters: Real,
+
+ // Frames per second the animation is defined at.
+ frames_per_second: f64,
+ ambient_color: Vec3,
+ default_camera: String,
+
+ // Animation user interface settings.
+ // HINT: Use `ufbx_scene_settings.frames_per_second` instead of interpreting these yourself.
+ time_mode: Time_Mode,
+ time_protocol: Time_Protocol,
+ snap_mode: Snap_Mode,
+
+ // Original settings (?)
+ original_axis_up: Coordinate_Axis,
+ original_unit_meters: Real,
+}
+
+Scene :: struct {
+ metadata: Metadata,
+
+ // Global settings
+ settings: Scene_Settings,
+
+ // Node instances in the scene
+ root_node: ^Node,
+
+ // Default animation descriptor
+ anim: ^Anim,
+ using _: struct #raw_union {
+ using _: struct {
+ unknowns: Unknown_List,
+
+ // Nodes
+ nodes: Node_List,
+
+ // Node attributes (common)
+ meshes: Mesh_List,
+ lights: Light_List,
+ cameras: Camera_List,
+ bones: Bone_List,
+ empties: Empty_List,
+
+ // Node attributes (curves/surfaces)
+ line_curves: Line_Curve_List,
+ nurbs_curves: Nurbs_Curve_List,
+ nurbs_surfaces: Nurbs_Surface_List,
+ nurbs_trim_surfaces: Nurbs_Trim_Surface_List,
+ nurbs_trim_boundaries: Nurbs_Trim_Boundary_List,
+
+ // Node attributes (advanced)
+ procedural_geometries: Procedural_Geometry_List,
+ stereo_cameras: Stereo_Camera_List,
+ camera_switchers: Camera_Switcher_List,
+ markers: Marker_List,
+ lod_groups: Lod_Group_List,
+
+ // Deformers
+ skin_deformers: Skin_Deformer_List,
+ skin_clusters: Skin_Cluster_List,
+ blend_deformers: Blend_Deformer_List,
+ blend_channels: Blend_Channel_List,
+ blend_shapes: Blend_Shape_List,
+ cache_deformers: Cache_Deformer_List,
+ cache_files: Cache_File_List,
+
+ // Materials
+ materials: Material_List,
+ textures: Texture_List,
+ videos: Video_List,
+ shaders: Shader_List,
+ shader_bindings: Shader_Binding_List,
+
+ // Animation
+ anim_stacks: Anim_Stack_List,
+ anim_layers: Anim_Layer_List,
+ anim_values: Anim_Value_List,
+ anim_curves: Anim_Curve_List,
+
+ // Collections
+ display_layers: Display_Layer_List,
+ selection_sets: Selection_Set_List,
+ selection_nodes: Selection_Node_List,
+
+ // Constraints
+ characters: Character_List,
+ constraints: Constraint_List,
+
+ // Audio
+ audio_layers: Audio_Layer_List,
+ audio_clips: Audio_Clip_List,
+
+ // Miscellaneous
+ poses: Pose_List,
+ metadata_objects: Metadata_Object_List,
+ },
+ elements_by_type: [42]Element_List,
+ },
+
+ // Unique texture files referenced by the scene.
+ texture_files: Texture_File_List,
+ elements: Element_List, // < Sorted by `id`
+ connections_src: Connection_List, // < Sorted by `src,src_prop`
+ connections_dst: Connection_List, // < Sorted by `dst,dst_prop`
+
+ // Elements sorted by name, type
+ elements_by_name: Name_Element_List,
+
+ // Enabled if `ufbx_load_opts.retain_dom == true`.
+ dom_root: ^Dom_Node,
+}
+
+// -- Curves
+Curve_Point :: struct {
+ valid: bool,
+ position: Vec3,
+ derivative: Vec3,
+}
+
+Surface_Point :: struct {
+ valid: bool,
+ position: Vec3,
+ derivative_u: Vec3,
+ derivative_v: Vec3,
+}
+
+// -- Mesh topology
+Topo_Flags :: enum c.int {
+ NON_MANIFOLD = 1, // < Edge with three or more faces
+ FLAGS_FORCE_32BIT = 2147483647,
+}
+
+Topo_Edge :: struct {
+ index: u32, // < Starting index of the edge, always defined
+ next: u32, // < Ending index of the edge / next per-face `ufbx_topo_edge`, always defined
+ prev: u32, // < Previous per-face `ufbx_topo_edge`, always defined
+ twin: u32, // < `ufbx_topo_edge` on the opposite side, `UFBX_NO_INDEX` if not found
+ face: u32, // < Index into `mesh->faces[]`, always defined
+ edge: u32, // < Index into `mesh->edges[]`, `UFBX_NO_INDEX` if not found
+ flags: Topo_Flags,
+}
+
+// Vertex data array for `ufbx_generate_indices()`.
+// NOTE: `ufbx_generate_indices()` compares the vertices using `memcmp()`, so
+// any padding should be cleared to zero.
+Vertex_Stream :: struct {
+ data: rawptr, // < Data pointer of shape `char[vertex_count][vertex_size]`.
+ vertex_count: c.size_t, // < Number of vertices in this stream, for sanity checking.
+ vertex_size: c.size_t, // < Size of a vertex in bytes.
+}
+
+// Allocate `size` bytes, must be at least 8 byte aligned
+Alloc_Fn :: proc "c" (rawptr, c.size_t) -> rawptr
+
+// Reallocate `old_ptr` from `old_size` to `new_size`
+// NOTE: If omit `alloc_fn` and `free_fn` they will be translated to:
+// `alloc(size)` -> `realloc_fn(user, NULL, 0, size)`
+// `free_fn(ptr, size)` -> `realloc_fn(user, ptr, size, 0)`
+Realloc_Fn :: proc "c" (rawptr, rawptr, c.size_t, c.size_t) -> rawptr
+
+// Free pointer `ptr` (of `size` bytes) returned by `alloc_fn` or `realloc_fn`
+Free_Fn :: proc "c" (rawptr, rawptr, c.size_t)
+
+// Free the allocator itself
+Free_Allocator_Fn :: proc "c" (rawptr)
+
+// Allocator callbacks and user context
+// NOTE: The allocator will be stored to the loaded scene and will be called
+// again from `ufbx_free_scene()` so make sure `user` outlives that!
+// You can use `free_allocator_fn()` to free the allocator yourself.
+Allocator :: struct {
+ // Callback functions, see `typedef`s above for information
+ alloc_fn: Alloc_Fn,
+ realloc_fn: Realloc_Fn,
+ free_fn: Free_Fn,
+ free_allocator_fn: Free_Allocator_Fn,
+ user: rawptr,
+}
+
+Allocator_Opts :: struct {
+ // Allocator callbacks
+ allocator: Allocator,
+
+ // Maximum number of bytes to allocate before failing
+ memory_limit: c.size_t,
+
+ // Maximum number of allocations to attempt before failing
+ allocation_limit: c.size_t,
+
+ // Threshold to swap from batched allocations to individual ones
+ // Defaults to 1MB if set to zero
+ // NOTE: If set to `1` ufbx will allocate everything in the smallest
+ // possible chunks which may be useful for debugging (eg. ASAN)
+ huge_threshold: c.size_t,
+
+ // Maximum size of a single allocation containing sub-allocations.
+ // Defaults to 16MB if set to zero
+ // The maximum amount of wasted memory depends on `max_chunk_size` and
+ // `huge_threshold`: each chunk can waste up to `huge_threshold` bytes
+ // internally and the last chunk might be incomplete. So for example
+ // with the defaults we can waste around 1MB/16MB = 6.25% overall plus
+ // up to 32MB due to the two incomplete blocks. The actual amounts differ
+ // slightly as the chunks start out at 4kB and double in size each time,
+ // meaning that the maximum fixed overhead (up to 32MB with defaults) is
+ // at most ~30% of the total allocation size.
+ max_chunk_size: c.size_t,
+}
+
+// Try to read up to `size` bytes to `data`, return the amount of read bytes.
+// Return `SIZE_MAX` to indicate an IO error.
+Read_Fn :: proc "c" (rawptr, rawptr, c.size_t) -> c.size_t
+
+// Skip `size` bytes in the file.
+Skip_Fn :: proc "c" (rawptr, c.size_t) -> bool
+
+// Get the size of the file.
+// Return `0` if unknown, `UINT64_MAX` if error.
+Size_Fn :: proc "c" (rawptr) -> u64
+
+// Close the file
+Close_Fn :: proc "c" (rawptr)
+
+Stream :: struct {
+ read_fn: Read_Fn, // < Required
+ skip_fn: Skip_Fn, // < Optional: Will use `read_fn()` if missing
+ size_fn: Size_Fn, // < Optional
+ close_fn: Close_Fn, // < Optional
+
+ // Context passed to other functions
+ user: rawptr,
+}
+
+Open_File_Type :: enum c.int {
+ MAIN_MODEL, // < Main model file
+ GEOMETRY_CACHE, // < Unknown geometry cache file
+ OBJ_MTL, // < .mtl material library file
+ TYPE_FORCE_32BIT = 2147483647,
+}
+
+OPEN_FILE_TYPE_COUNT :: 3
+
+Open_File_Context :: c.uintptr_t
+
+Open_File_Info :: struct {
+ // Context that can be passed to the following functions to use a shared allocator:
+ // ufbx_open_file_ctx()
+ // ufbx_open_memory_ctx()
+ _context: Open_File_Context,
+
+ // Kind of file to load.
+ type: Open_File_Type,
+
+ // Original filename in the file, not resolved or UTF-8 encoded.
+ // NOTE: Not necessarily NULL-terminated!
+ original_filename: Blob,
+}
+
+// Callback for opening an external file from the filesystem
+Open_File_Fn :: proc "c" (rawptr, ^Stream, cstring, c.size_t, ^Open_File_Info) -> bool
+
+Open_File_Cb :: struct {
+ fn: Open_File_Fn,
+ user: rawptr,
+}
+
+// Options for `ufbx_open_file()`.
+Open_File_Opts :: struct {
+ _begin_zero: u32,
+
+ // Allocator to allocate the memory with.
+ allocator: Allocator_Opts,
+
+ // The filename is guaranteed to be NULL-terminated.
+ filename_null_terminated: bool,
+ _end_zero: u32,
+}
+
+// Memory stream options
+Close_Memory_Fn :: proc "c" (rawptr, rawptr, c.size_t)
+
+Close_Memory_Cb :: struct {
+ fn: Close_Memory_Fn,
+ user: rawptr,
+}
+
+// Options for `ufbx_open_memory()`.
+Open_Memory_Opts :: struct {
+ _begin_zero: u32,
+
+ // Allocator to allocate the memory with.
+ // NOTE: Used even if no copy is made to allocate a small metadata block.
+ allocator: Allocator_Opts,
+
+ // Do not copy the memory.
+ // You can use `close_cb` to free the memory when the stream is closed.
+ // NOTE: This means the provided data pointer is referenced after creating
+ // the memory stream, make sure the data stays valid until the stream is closed!
+ no_copy: bool,
+
+ // Callback to free the memory blob.
+ close_cb: Close_Memory_Cb,
+ _end_zero: u32,
+}
+
+// Detailed error stack frame.
+// NOTE: You must compile `ufbx.c` with `UFBX_ENABLE_ERROR_STACK` to enable the error stack.
+Error_Frame :: struct {
+ source_line: u32,
+ function: String,
+ description: String,
+}
+
+// Error causes (and `UFBX_ERROR_NONE` for no error).
+Error_Type :: enum c.int {
+ // No error, operation has been performed successfully.
+ NONE,
+
+ // Unspecified error, most likely caused by an invalid FBX file or a file
+ // that contains something ufbx can't handle.
+ UNKNOWN,
+
+ // File not found.
+ FILE_NOT_FOUND,
+
+ // Empty file.
+ EMPTY_FILE,
+
+ // External file not found.
+ // See `ufbx_load_opts.load_external_files` for more information.
+ EXTERNAL_FILE_NOT_FOUND,
+
+ // Out of memory (allocator returned `NULL`).
+ OUT_OF_MEMORY,
+
+ // `ufbx_allocator_opts.memory_limit` exhausted.
+ MEMORY_LIMIT,
+
+ // `ufbx_allocator_opts.allocation_limit` exhausted.
+ ALLOCATION_LIMIT,
+
+ // File ended abruptly.
+ TRUNCATED_FILE,
+
+ // IO read error.
+ // eg. returning `SIZE_MAX` from `ufbx_stream.read_fn` or stdio `ferror()` condition.
+ IO,
+
+ // User cancelled the loading via `ufbx_load_opts.progress_cb` returning `UFBX_PROGRESS_CANCEL`.
+ CANCELLED,
+
+ // Could not detect file format from file data or filename.
+ // HINT: You can supply it manually using `ufbx_load_opts.file_format` or use `ufbx_load_opts.filename`
+ // when using `ufbx_load_memory()` to let ufbx guess the format from the extension.
+ UNRECOGNIZED_FILE_FORMAT,
+
+ // Options struct (eg. `ufbx_load_opts`) is not cleared to zero.
+ // Make sure you initialize the structure to zero via eg.
+ // ufbx_load_opts opts = { 0 }; // C
+ // ufbx_load_opts opts = { }; // C++
+ UNINITIALIZED_OPTIONS,
+
+ // The vertex streams in `ufbx_generate_indices()` are empty.
+ ZERO_VERTEX_SIZE,
+
+ // Vertex stream passed to `ufbx_generate_indices()`.
+ TRUNCATED_VERTEX_STREAM,
+
+ // Invalid UTF-8 encountered in a file when loading with `UFBX_UNICODE_ERROR_HANDLING_ABORT_LOADING`.
+ INVALID_UTF8,
+
+ // Feature needed for the operation has been compiled out.
+ FEATURE_DISABLED,
+
+ // Attempting to tessellate an invalid NURBS object.
+ // See `ufbx_nurbs_basis.valid`.
+ BAD_NURBS,
+
+ // Out of bounds index in the file when loading with `UFBX_INDEX_ERROR_HANDLING_ABORT_LOADING`.
+ BAD_INDEX,
+
+ // Node is deeper than `ufbx_load_opts.node_depth_limit` in the hierarchy.
+ NODE_DEPTH_LIMIT,
+
+ // Error parsing ASCII array in a thread.
+ // Threaded ASCII parsing is slightly more strict than non-threaded, for cursed files,
+ // set `ufbx_load_opts.force_single_thread_ascii_parsing` to `true`.
+ THREADED_ASCII_PARSE,
+
+ // Unsafe options specified without enabling `ufbx_load_opts.allow_unsafe`.
+ UNSAFE_OPTIONS,
+
+ // Duplicated override property in `ufbx_create_anim()`
+ DUPLICATE_OVERRIDE,
+
+ // Unsupported file format version.
+ // ufbx still tries to load files with unsupported versions, see `UFBX_WARNING_UNSUPPORTED_VERSION`.
+ UNSUPPORTED_VERSION,
+ TYPE_FORCE_32BIT = 2147483647, // Unsupported file format version.
+ // ufbx still tries to load files with unsupported versions, see `UFBX_WARNING_UNSUPPORTED_VERSION`.
+}
+
+ERROR_TYPE_COUNT :: 24
+
+// Error description with detailed stack trace
+// HINT: You can use `ufbx_format_error()` for formatting the error
+Error :: struct {
+ // Type of the error, or `UFBX_ERROR_NONE` if successful.
+ type: Error_Type,
+
+ // Description of the error type.
+ description: String,
+
+ // Internal error stack.
+ // NOTE: You must compile `ufbx.c` with `UFBX_ENABLE_ERROR_STACK` to enable the error stack.
+ stack_size: u32,
+ stack: [8]Error_Frame,
+
+ // Additional error information, such as missing file filename.
+ // `info` is a NULL-terminated UTF-8 string containing `info_length` bytes, excluding the trailing `'\0'`.
+ info_length: c.size_t,
+ info: [256]c.char,
+}
+
+// Loading progress information.
+Progress :: struct {
+ bytes_read: u64,
+ bytes_total: u64,
+}
+
+// Progress result returned from `ufbx_progress_fn()` callback.
+// Determines whether ufbx should continue or abort the loading.
+Progress_Result :: enum c.int {
+ // Continue loading the file.
+ CONTINUE = 256,
+
+ // Cancel loading and fail with `UFBX_ERROR_CANCELLED`.
+ CANCEL = 512,
+ RESULT_FORCE_32BIT = 2147483647, // Cancel loading and fail with `UFBX_ERROR_CANCELLED`.
+}
+
+// Called periodically with the current progress.
+// Return `UFBX_PROGRESS_CANCEL` to cancel further processing.
+Progress_Fn :: proc "c" (rawptr, ^Progress) -> Progress_Result
+
+Progress_Cb :: struct {
+ fn: Progress_Fn,
+ user: rawptr,
+}
+
+// Source data/stream to decompress with `ufbx_inflate()`
+Inflate_Input :: struct {
+ // Total size of the data in bytes
+ total_size: c.size_t,
+
+ // (optional) Initial or complete data chunk
+ data: rawptr,
+ data_size: c.size_t,
+
+ // (optional) Temporary buffer, defaults to 256b stack buffer
+ buffer: rawptr,
+ buffer_size: c.size_t,
+
+ // (optional) Streaming read function, concatenated after `data`
+ read_fn: Read_Fn,
+ read_user: rawptr,
+
+ // (optional) Progress reporting
+ progress_cb: Progress_Cb,
+ progress_interval_hint: u64, // < Bytes between progress report calls
+
+ // (optional) Change the progress scope
+ progress_size_before: u64,
+ progress_size_after: u64,
+
+ // (optional) No the DEFLATE header
+ no_header: bool,
+
+ // (optional) No the Adler32 checksum
+ no_checksum: bool,
+
+ // (optional) Force internal fast lookup bit amount
+ internal_fast_bits: c.size_t,
+}
+
+// Persistent data between `ufbx_inflate()` calls
+// NOTE: You must set `initialized` to `false`, but `data` may be uninitialized
+Inflate_Retain :: struct {
+ initialized: bool,
+ data: [1024]u64,
+}
+
+Index_Error_Handling :: enum c.int {
+ // Clamp to a valid value.
+ CLAMP,
+
+ // Set bad indices to `UFBX_NO_INDEX`.
+ // This is the recommended way if you need to deal with files with gaps in information.
+ // HINT: If you use this `ufbx_get_vertex_TYPE()` functions will return zero
+ // on invalid indices instead of failing.
+ NO_INDEX,
+
+ // Fail loading entierely when encountering a bad index.
+ ABORT_LOADING,
+
+ // Pass bad indices through as-is.
+ // Requires `ufbx_load_opts.allow_unsafe`.
+ // UNSAFE: Breaks any API guarantees regarding indexes being in bounds and makes
+ // `ufbx_get_vertex_TYPE()` memory-unsafe to use.
+ UNSAFE_IGNORE,
+ FORCE_32BIT = 2147483647, // Pass bad indices through as-is.
+ // Requires `ufbx_load_opts.allow_unsafe`.
+ // UNSAFE: Breaks any API guarantees regarding indexes being in bounds and makes
+ // `ufbx_get_vertex_TYPE()` memory-unsafe to use.
+}
+
+INDEX_ERROR_HANDLING_COUNT :: 4
+
+Unicode_Error_Handling :: enum c.int {
+ // Replace errors with U+FFFD "Replacement Character"
+ REPLACEMENT_CHARACTER,
+
+ // Replace errors with '_' U+5F "Low Line"
+ UNDERSCORE,
+
+ // Replace errors with '?' U+3F "Question Mark"
+ QUESTION_MARK,
+
+ // Remove errors from the output
+ REMOVE,
+
+ // Fail loading on encountering an Unicode error
+ ABORT_LOADING,
+
+ // Ignore and pass-through non-UTF-8 string data.
+ // Requires `ufbx_load_opts.allow_unsafe`.
+ // UNSAFE: Breaks API guarantee that `ufbx_string` is UTF-8 encoded.
+ UNSAFE_IGNORE,
+ FORCE_32BIT = 2147483647, // Ignore and pass-through non-UTF-8 string data.
+ // Requires `ufbx_load_opts.allow_unsafe`.
+ // UNSAFE: Breaks API guarantee that `ufbx_string` is UTF-8 encoded.
+}
+
+UNICODE_ERROR_HANDLING_COUNT :: 6
+
+// How to handle FBX node geometry transforms.
+// FBX nodes can have "geometry transforms" that affect only the attached meshes,
+// but not the children. This is not allowed in many scene representations so
+// ufbx provides some ways to simplify them.
+// Geometry transforms can also be used to transform any other attributes such
+// as lights or cameras.
+Geometry_Transform_Handling :: enum c.int {
+ // Preserve the geometry transforms as-is.
+ // To be correct for all files you have to use `ufbx_node.geometry_transform`,
+ // `ufbx_node.geometry_to_node`, or `ufbx_node.geometry_to_world` to compensate
+ // for any potential geometry transforms.
+ PRESERVE,
+
+ // Add helper nodes between the nodes and geometry where needed.
+ // The created nodes have `ufbx_node.is_geometry_transform_helper` set and are
+ // named `ufbx_load_opts.geometry_transform_helper_name`.
+ HELPER_NODES,
+
+ // Modify the geometry of meshes attached to nodes with geometry transforms.
+ // Will add helper nodes like `UFBX_GEOMETRY_TRANSFORM_HANDLING_HELPER_NODES` if
+ // necessary, for example if there are multiple instances of the same mesh with
+ // geometry transforms.
+ MODIFY_GEOMETRY,
+
+ // Modify the geometry of meshes attached to nodes with geometry transforms.
+ // NOTE: This will not work correctly for instanced geometry.
+ MODIFY_GEOMETRY_NO_FALLBACK,
+ FORCE_32BIT = 2147483647, // Modify the geometry of meshes attached to nodes with geometry transforms.
+ // NOTE: This will not work correctly for instanced geometry.
+}
+
+GEOMETRY_TRANSFORM_HANDLING_COUNT :: 4
+
+// How to handle FBX transform inherit modes.
+Inherit_Mode_Handling :: enum c.int {
+ // Preserve inherit mode in `ufbx_node.inherit_mode`.
+ // NOTE: To correctly handle all scenes you would need to handle the
+ // non-standard inherit modes.
+ PRESERVE,
+
+ // Create scale helper nodes parented to nodes that need special inheritance.
+ // Scale helper nodes will have `ufbx_node.is_scale_helper` and parents of
+ // scale helpers will have `ufbx_node.scale_helper` pointing to it.
+ HELPER_NODES,
+
+ // Attempt to compensate for bone scale by inversely scaling children.
+ // NOTE: This only works for uniform non-animated scaling, if scale is
+ // non-uniform or animated, ufbx will add scale helpers in the same way
+ // as `UFBX_INHERIT_MODE_HANDLING_HELPER_NODES`.
+ COMPENSATE,
+
+ // Attempt to compensate for bone scale by inversely scaling children.
+ // Will never create helper nodes.
+ COMPENSATE_NO_FALLBACK,
+
+ // Ignore non-standard inheritance modes.
+ // Forces all nodes to have `UFBX_INHERIT_MODE_NORMAL` regardless of the
+ // inherit mode specified in the file. This can be useful for emulating
+ // results from importers/programs that don't support inherit modes.
+ IGNORE,
+ FORCE_32BIT = 2147483647, // Ignore non-standard inheritance modes.
+ // Forces all nodes to have `UFBX_INHERIT_MODE_NORMAL` regardless of the
+ // inherit mode specified in the file. This can be useful for emulating
+ // results from importers/programs that don't support inherit modes.
+}
+
+INHERIT_MODE_HANDLING_COUNT :: 5
+
+// How to handle FBX transform pivots.
+Pivot_Handling :: enum c.int {
+ // Take pivots into account when computing the transform.
+ RETAIN,
+
+ // Translate objects to be located at their pivot.
+ // NOTE: Only applied if rotation and scaling pivots are equal.
+ // NOTE: Results in geometric translation. Use `ufbx_geometry_transform_handling`
+ // to interpret these in a standard scene graph.
+ ADJUST_TO_PIVOT,
+ FORCE_32BIT = 2147483647, // Translate objects to be located at their pivot.
+ // NOTE: Only applied if rotation and scaling pivots are equal.
+ // NOTE: Results in geometric translation. Use `ufbx_geometry_transform_handling`
+ // to interpret these in a standard scene graph.
+}
+
+PIVOT_HANDLING_COUNT :: 2
+
+Baked_Key_Flag :: enum c.int {
+ // This keyframe represents a constant step from the left side
+ STEP_LEFT = 0,
+
+ // This keyframe represents a constant step from the right side
+ STEP_RIGHT = 1,
+
+ // This keyframe is the main part of a step
+ // Bordering either `UFBX_BAKED_KEY_STEP_LEFT` or `UFBX_BAKED_KEY_STEP_RIGHT`.
+ STEP_KEY = 2,
+
+ // This keyframe is a real keyframe in the source animation
+ KEYFRAME = 3,
+
+ // This keyframe has been reduced by maximum sample rate.
+ // See `ufbx_bake_opts.maximum_sample_rate`.
+ REDUCED = 4,
+}
+
+Baked_Key_Flags :: distinct bit_set[Baked_Key_Flag; c.int]
+
+BAKED_KEY_FORCE_32BIT :: Baked_Key_Flags { .STEP_LEFT, .STEP_RIGHT, .STEP_KEY, .KEYFRAME, .REDUCED }
+
+Baked_Vec3 :: struct {
+ time: f64, // < Time of the keyframe, in seconds
+ value: Vec3, // < Value at `time`, can be linearly interpolated
+ flags: Baked_Key_Flag, // < Additional information about the keyframe
+}
+
+Baked_Vec3_List :: struct {
+ data: ^Baked_Vec3,
+ count: c.size_t,
+}
+
+Baked_Quat :: struct {
+ time: f64, // < Time of the keyframe, in seconds
+ value: Quat, // < Value at `time`, can be (spherically) linearly interpolated
+ flags: Baked_Key_Flag, // < Additional information about the keyframe
+}
+
+Baked_Quat_List :: struct {
+ data: ^Baked_Quat,
+ count: c.size_t,
+}
+
+// Baked transform animation for a single node.
+Baked_Node :: struct {
+ // Typed ID of the node, maps to `ufbx_scene.nodes[]`.
+ typed_id: u32,
+
+ // Element ID of the element, maps to `ufbx_scene.elements[]`.
+ element_id: u32,
+
+ // The translation channel has constant values for the whole animation.
+ constant_translation: bool,
+
+ // The rotation channel has constant values for the whole animation.
+ constant_rotation: bool,
+
+ // The scale channel has constant values for the whole animation.
+ constant_scale: bool,
+
+ // Translation keys for the animation, maps to `ufbx_node.local_transform.translation`.
+ translation_keys: Baked_Vec3_List,
+
+ // Rotation keyframes, maps to `ufbx_node.local_transform.rotation`.
+ rotation_keys: Baked_Quat_List,
+
+ // Scale keyframes, maps to `ufbx_node.local_transform.scale`.
+ scale_keys: Baked_Vec3_List,
+}
+
+Baked_Node_List :: struct {
+ data: ^Baked_Node,
+ count: c.size_t,
+}
+
+// Baked property animation.
+Baked_Prop :: struct {
+ // Name of the property, eg. `"Visibility"`.
+ name: String,
+
+ // The value of the property is constant for the whole animation.
+ constant_value: bool,
+
+ // Property value keys.
+ keys: Baked_Vec3_List,
+}
+
+Baked_Prop_List :: struct {
+ data: ^Baked_Prop,
+ count: c.size_t,
+}
+
+// Baked property animation for a single element.
+Baked_Element :: struct {
+ // Element ID of the element, maps to `ufbx_scene.elements[]`.
+ element_id: u32,
+
+ // List of properties the animation modifies.
+ props: Baked_Prop_List,
+}
+
+Baked_Element_List :: struct {
+ data: ^Baked_Element,
+ count: c.size_t,
+}
+
+Baked_Anim_Metadata :: struct {
+ // Memory statistics
+ result_memory_used: c.size_t,
+ temp_memory_used: c.size_t,
+ result_allocs: c.size_t,
+ temp_allocs: c.size_t,
+}
+
+// Animation baked into linearly interpolated keyframes.
+// See `ufbx_bake_anim()`.
+Baked_Anim :: struct {
+ // Nodes that are modified by the animation.
+ // Some nodes may be missing if the specified animation does not transform them.
+ // Conversely, some non-obviously animated nodes may be included as exporters
+ // often may add dummy keyframes for objects.
+ nodes: Baked_Node_List,
+
+ // Element properties modified by the animation.
+ elements: Baked_Element_List,
+
+ // Playback time range for the animation.
+ playback_time_begin: f64,
+ playback_time_end: f64,
+ playback_duration: f64,
+
+ // Keyframe time range.
+ key_time_min: f64,
+ key_time_max: f64,
+
+ // Additional bake information.
+ metadata: Baked_Anim_Metadata,
+}
+
+// Internal thread pool handle.
+// Passed to `ufbx_thread_pool_run_task()` from an user thread to run ufbx tasks.
+// HINT: This context can store a user pointer via `ufbx_thread_pool_set_user_ptr()`.
+Thread_Pool_Context :: c.uintptr_t
+
+// Thread pool creation information from ufbx.
+Thread_Pool_Info :: struct {
+ max_concurrent_tasks: u32,
+}
+
+// Initialize the thread pool.
+// Return `true` on success.
+Thread_Pool_Init_Fn :: proc "c" (rawptr, Thread_Pool_Context, ^Thread_Pool_Info) -> bool
+
+// Run tasks `count` tasks in threads.
+// You must call `ufbx_thread_pool_run_task()` with indices `[start_index, start_index + count)`.
+// The threads are launched in batches indicated by `group`, see `UFBX_THREAD_GROUP_COUNT` for more information.
+// Ideally, you should run all the task indices in parallel within each `ufbx_thread_pool_run_fn()` call.
+Thread_Pool_Run_Fn :: proc "c" (rawptr, Thread_Pool_Context, u32, u32, u32)
+
+// Wait for previous tasks spawned in `ufbx_thread_pool_run_fn()` to finish.
+// `group` specifies the batch to wait for, `max_index` contains `start_index + count` from that group instance.
+Thread_Pool_Wait_Fn :: proc "c" (rawptr, Thread_Pool_Context, u32, u32)
+
+// Free the thread pool.
+Thread_Pool_Free_Fn :: proc "c" (rawptr, Thread_Pool_Context)
+
+// Thread pool interface.
+// See functions above for more information.
+//
+// Hypothetical example of calls, where `UFBX_THREAD_GROUP_COUNT=2` for simplicity:
+//
+// run_fn(group=0, start_index=0, count=4) -> t0 := threaded { ufbx_thread_pool_run_task(0..3) }
+// run_fn(group=1, start_index=4, count=10) -> t1 := threaded { ufbx_thread_pool_run_task(4..10) }
+// wait_fn(group=0, max_index=4) -> wait_threads(t0)
+// run_fn(group=0, start_index=10, count=15) -> t0 := threaded { ufbx_thread_pool_run_task(10..14) }
+// wait_fn(group=1, max_index=10) -> wait_threads(t1)
+// wait_fn(group=0, max_index=15) -> wait_threads(t0)
+Thread_Pool :: struct {
+ init_fn: Thread_Pool_Init_Fn, // < Optional
+ run_fn: Thread_Pool_Run_Fn, // < Required
+ wait_fn: Thread_Pool_Wait_Fn, // < Required
+ free_fn: Thread_Pool_Free_Fn, // < Optional
+ user: rawptr,
+}
+
+// Thread pool options.
+Thread_Opts :: struct {
+ // Thread pool interface.
+ // HINT: You can use `extra/ufbx_os.h` to provide a thread pool.
+ pool: Thread_Pool,
+
+ // Maximum of tasks to have in-flight.
+ // Default: 2048
+ num_tasks: c.size_t,
+
+ // Maximum amount of memory to use for batched threaded processing.
+ // Default: 32MB
+ // NOTE: The actual used memory usage might be higher, if there are individual tasks
+ // that rqeuire a high amount of memory.
+ memory_limit: c.size_t,
+}
+
+// Flags to control nanimation evaluation functions.
+Evaluate_Flags :: enum c.int {
+ // Do not extrapolate past the keyframes.
+ UFBX_EVALUATE_FLAG_NO_EXTRAPOLATION = 1,
+ ufbx_evaluate_flags_FORCE_32BIT = 2147483647, // Do not extrapolate past the keyframes.
+}
+
+// Options for `ufbx_load_file/memory/stream/stdio()`
+// NOTE: Initialize to zero with `{ 0 }` (C) or `{ }` (C++)
+Load_Opts :: struct {
+ _begin_zero: u32,
+ temp_allocator: Allocator_Opts, // < Allocator used during loading
+ result_allocator: Allocator_Opts, // < Allocator used for the final scene
+ thread_opts: Thread_Opts, // < Threading options
+ ignore_geometry: bool, // < Do not load geometry datsa (vertices, indices, etc)
+ ignore_animation: bool, // < Do not load animation curves
+ ignore_embedded: bool, // < Do not load embedded content
+ ignore_all_content: bool, // < Do not load any content (geometry, animation, embedded)
+ evaluate_skinning: bool, // < Evaluate skinning (see ufbx_mesh.skinned_vertices)
+ evaluate_caches: bool, // < Evaluate vertex caches (see ufbx_mesh.skinned_vertices)
+
+ // Try to open external files referenced by the main file automatically.
+ // Applies to geometry caches and .mtl files for OBJ.
+ // NOTE: This may be risky for untrusted data as the input files may contain
+ // references to arbitrary paths in the filesystem.
+ // NOTE: This only applies to files *implicitly* referenced by the scene, if
+ // you request additional files via eg. `ufbx_load_opts.obj_mtl_path` they
+ // are still loaded.
+ // NOTE: Will fail loading if any external files are not found by default, use
+ // `ufbx_load_opts.ignore_missing_external_files` to suppress this, in this case
+ // you can find the errors at `ufbx_metadata.warnings[]` as `UFBX_WARNING_MISSING_EXTERNAL_FILE`.
+ load_external_files: bool,
+
+ // Don't fail loading if external files are not found.
+ ignore_missing_external_files: bool,
+
+ // Don't compute `ufbx_skin_deformer` `vertices` and `weights` arrays saving
+ // a bit of memory and time if not needed
+ skip_skin_vertices: bool,
+
+ // Skip computing `ufbx_mesh.material_parts[]` and `ufbx_mesh.face_group_parts[]`.
+ skip_mesh_parts: bool,
+
+ // Clean-up skin weights by removing negative, zero and NAN weights.
+ clean_skin_weights: bool,
+
+ // Read Blender materials as PBR values.
+ // Blender converts PBR materials to legacy FBX Phong materials in a deterministic way.
+ // If this setting is enabled, such materials will be read as `UFBX_SHADER_BLENDER_PHONG`,
+ // which means ufbx will be able to parse roughness and metallic textures.
+ use_blender_pbr_material: bool,
+
+ // Don't adjust reading the FBX file depending on the detected exporter
+ disable_quirks: bool,
+
+ // Don't allow partially broken FBX files to load
+ strict: bool,
+
+ // Force ASCII parsing to use a single thread.
+ // The multi-threaded ASCII parsing is slightly more lenient as it ignores
+ // the self-reported size of ASCII arrays, that threaded parsing depends on.
+ force_single_thread_ascii_parsing: bool,
+
+ // UNSAFE: If enabled allows using unsafe options that may fundamentally
+ // break the API guarantees.
+ allow_unsafe: bool,
+
+ // Specify how to handle broken indices.
+ index_error_handling: Index_Error_Handling,
+
+ // Connect related elements even if they are broken. If `false` (default)
+ // `ufbx_skin_cluster` with a missing `bone` field are _not_ included in
+ // the `ufbx_skin_deformer.clusters[]` array for example.
+ connect_broken_elements: bool,
+
+ // Allow nodes that are not connected in any way to the root. Conversely if
+ // disabled, all lone nodes will be parented under `ufbx_scene.root_node`.
+ allow_nodes_out_of_root: bool,
+
+ // Allow meshes with no vertex position attribute.
+ // NOTE: If this is set `ufbx_mesh.vertex_position.exists` may be `false`.
+ allow_missing_vertex_position: bool,
+
+ // Allow faces with zero indices.
+ allow_empty_faces: bool,
+
+ // Generate vertex normals for a meshes that are missing normals.
+ // You can see if the normals have been generated from `ufbx_mesh.generated_normals`.
+ generate_missing_normals: bool,
+
+ // Ignore `open_file_cb` when loading the main file.
+ open_main_file_with_default: bool,
+
+ // Path separator character, defaults to '\' on Windows and '/' otherwise.
+ path_separator: c.char,
+
+ // Maximum depth of the node hirerachy.
+ // Will fail with `UFBX_ERROR_NODE_DEPTH_LIMIT` if a node is deeper than this limit.
+ // NOTE: The default of 0 allows arbitrarily deep hierarchies. Be careful if using
+ // recursive algorithms without setting this limit.
+ node_depth_limit: u32,
+
+ // Estimated file size for progress reporting
+ file_size_estimate: u64,
+
+ // Buffer size in bytes to use for reading from files or IO callbacks
+ read_buffer_size: c.size_t,
+
+ // Filename to use as a base for relative file paths if not specified using
+ // `ufbx_load_file()`. Use `length = SIZE_MAX` for NULL-terminated strings.
+ // `raw_filename` will be derived from this if empty.
+ filename: String,
+
+ // Raw non-UTF8 filename. Does not support NULL termination.
+ // `filename` will be derived from this if empty.
+ raw_filename: Blob,
+
+ // Progress reporting
+ progress_cb: Progress_Cb,
+ progress_interval_hint: u64, // < Bytes between progress report calls
+
+ // External file callbacks (defaults to stdio.h)
+ open_file_cb: Open_File_Cb,
+
+ // How to handle geometry transforms in the nodes.
+ // See `ufbx_geometry_transform_handling` for an explanation.
+ geometry_transform_handling: Geometry_Transform_Handling,
+
+ // How to handle unconventional transform inherit modes.
+ // See `ufbx_inherit_mode_handling` for an explanation.
+ inherit_mode_handling: Inherit_Mode_Handling,
+
+ // How to handle pivots.
+ // See `ufbx_pivot_handling` for an explanation.
+ pivot_handling: Pivot_Handling,
+
+ // How to perform space conversion by `target_axes` and `target_unit_meters`.
+ // See `ufbx_space_conversion` for an explanation.
+ space_conversion: Space_Conversion,
+
+ // Axis used to mirror for conversion between left-handed and right-handed coordinates.
+ handedness_conversion_axis: Mirror_Axis,
+
+ // Do not change winding of faces when converting handedness.
+ handedness_conversion_retain_winding: bool,
+
+ // Reverse winding of all faces.
+ // If `handedness_conversion_retain_winding` is not specified, mirrored meshes
+ // will retain their original winding.
+ reverse_winding: bool,
+
+ // Apply an implicit root transformation to match axes.
+ // Used if `ufbx_coordinate_axes_valid(target_axes)`.
+ target_axes: Coordinate_Axes,
+
+ // Scale the scene so that one world-space unit is `target_unit_meters` meters.
+ // By default units are not scaled.
+ target_unit_meters: Real,
+
+ // Target space for camera.
+ // By default FBX cameras point towards the positive X axis.
+ // Used if `ufbx_coordinate_axes_valid(target_camera_axes)`.
+ target_camera_axes: Coordinate_Axes,
+
+ // Target space for directed lights.
+ // By default FBX lights point towards the negative Y axis.
+ // Used if `ufbx_coordinate_axes_valid(target_light_axes)`.
+ target_light_axes: Coordinate_Axes,
+
+ // Name for dummy geometry transform helper nodes.
+ // See `UFBX_GEOMETRY_TRANSFORM_HANDLING_HELPER_NODES`.
+ geometry_transform_helper_name: String,
+
+ // Name for dummy scale helper nodes.
+ // See `UFBX_INHERIT_MODE_HANDLING_HELPER_NODES`.
+ scale_helper_name: String,
+
+ // Normalize vertex normals.
+ normalize_normals: bool,
+
+ // Normalize tangents and bitangents.
+ normalize_tangents: bool,
+
+ // Override for the root transform
+ use_root_transform: bool,
+ root_transform: Transform,
+
+ // Animation keyframe clamp threshold, only applies to specific interpolation modes.
+ key_clamp_threshold: f64,
+
+ // Specify how to handle Unicode errors in strings.
+ unicode_error_handling: Unicode_Error_Handling,
+
+ // Retain the 'W' component of mesh normal/tangent/bitangent.
+ // See `ufbx_vertex_attrib.values_w`.
+ retain_vertex_attrib_w: bool,
+
+ // Retain the raw document structure using `ufbx_dom_node`.
+ retain_dom: bool,
+
+ // Force a specific file format instead of detecting it.
+ file_format: File_Format,
+
+ // How far to read into the file to determine the file format.
+ // Default: 16kB
+ file_format_lookahead: c.size_t,
+
+ // Do not attempt to detect file format from file content.
+ no_format_from_content: bool,
+
+ // Do not attempt to detect file format from filename extension.
+ // ufbx primarily detects file format from the file header,
+ // this is just used as a fallback.
+ no_format_from_extension: bool,
+
+ // (.obj) Try to find .mtl file with matching filename as the .obj file.
+ // Used if the file specified `mtllib` line is not found, eg. for a file called
+ // `model.obj` that contains the line `usemtl materials.mtl`, ufbx would first
+ // try to open `materials.mtl` and if that fails it tries to open `model.mtl`.
+ obj_search_mtl_by_filename: bool,
+
+ // (.obj) Don't split geometry into meshes by object.
+ obj_merge_objects: bool,
+
+ // (.obj) Don't split geometry into meshes by groups.
+ obj_merge_groups: bool,
+
+ // (.obj) Force splitting groups even on object boundaries.
+ obj_split_groups: bool,
+
+ // (.obj) Path to the .mtl file.
+ // Use `length = SIZE_MAX` for NULL-terminated strings.
+ // NOTE: This is used _instead_ of the one in the file even if not found
+ // and sidesteps `load_external_files` as it's _explicitly_ requested.
+ obj_mtl_path: String,
+
+ // (.obj) Data for the .mtl file.
+ obj_mtl_data: Blob,
+
+ // The world unit in meters that .obj files are assumed to be in.
+ // .obj files do not define the working units. By default the unit scale
+ // is read as zero, and no unit conversion is performed.
+ obj_unit_meters: Real,
+
+ // Coordinate space .obj files are assumed to be in.
+ // .obj files do not define the coordinate space they use. By default no
+ // coordinate space is assumed and no conversion is performed.
+ obj_axes: Coordinate_Axes,
+ _end_zero: u32,
+}
+
+// Options for `ufbx_evaluate_scene()`
+// NOTE: Initialize to zero with `{ 0 }` (C) or `{ }` (C++)
+Evaluate_Opts :: struct {
+ _begin_zero: u32,
+ temp_allocator: Allocator_Opts, // < Allocator used during evaluation
+ result_allocator: Allocator_Opts, // < Allocator used for the final scene
+ evaluate_skinning: bool, // < Evaluate skinning (see ufbx_mesh.skinned_vertices)
+ evaluate_caches: bool, // < Evaluate vertex caches (see ufbx_mesh.skinned_vertices)
+
+ // Evaluation flags.
+ // See `ufbx_evaluate_flags` for information.
+ evaluate_flags: u32,
+
+ // WARNING: Potentially unsafe! Try to open external files such as geometry caches
+ load_external_files: bool,
+
+ // External file callbacks (defaults to stdio.h)
+ open_file_cb: Open_File_Cb,
+ _end_zero: u32,
+}
+
+Const_Uint32_List :: struct {
+ data: ^u32,
+ count: c.size_t,
+}
+
+Const_Real_List :: struct {
+ data: ^Real,
+ count: c.size_t,
+}
+
+Prop_Override_Desc :: struct {
+ // Element (`ufbx_element.element_id`) to override the property from
+ element_id: u32,
+
+ // Property name to override.
+ prop_name: String,
+
+ // Override value, use `value.x` for scalars. `value_int` is initialized
+ // from `value.x` if zero so keep `value` zeroed even if you don't need it!
+ value: Vec4,
+ value_str: String,
+ value_int: i64,
+}
+
+Const_Prop_Override_Desc_List :: struct {
+ data: ^Prop_Override_Desc,
+ count: c.size_t,
+}
+
+Const_Transform_Override_List :: struct {
+ data: ^Transform_Override,
+ count: c.size_t,
+}
+
+Anim_Opts :: struct {
+ _begin_zero: u32,
+
+ // Animation layers indices.
+ // Corresponding to `ufbx_scene.anim_layers[]`, aka `ufbx_anim_layer.typed_id`.
+ layer_ids: Const_Uint32_List,
+
+ // Override layer weights, parallel to `ufbx_anim_opts.layer_ids[]`.
+ override_layer_weights: Const_Real_List,
+
+ // Property overrides.
+ // These allow you to override FBX properties, such as 'UFBX_Lcl_Rotation`.
+ prop_overrides: Const_Prop_Override_Desc_List,
+
+ // Transform overrides.
+ // These allow you to override individual nodes' `ufbx_node.local_transform`.
+ transform_overrides: Const_Transform_Override_List,
+
+ // Ignore connected properties
+ ignore_connections: bool,
+ result_allocator: Allocator_Opts, // < Allocator used to create the `ufbx_anim`
+ _end_zero: u32,
+}
+
+// Specifies how to handle stepped tangents.
+Bake_Step_Handling :: enum c.int {
+ // One millisecond default step duration, with potential extra slack for converting to `float`.
+ UFBX_BAKE_STEP_HANDLING_DEFAULT,
+
+ // Use a custom interpolation duration for the constant step.
+ // See `ufbx_bake_opts.step_custom_duration` and optionally `ufbx_bake_opts.step_custom_epsilon`.
+ UFBX_BAKE_STEP_HANDLING_CUSTOM_DURATION,
+
+ // Stepped keyframes are represented as keyframes at the exact same time.
+ // Use flags `UFBX_BAKED_KEY_STEP_LEFT` and `UFBX_BAKED_KEY_STEP_RIGHT` to differentiate
+ // between the primary key and edge limits.
+ UFBX_BAKE_STEP_HANDLING_IDENTICAL_TIME,
+
+ // Represent stepped keyframe times as the previous/next representable `double` value.
+ // Using this and robust linear interpolation will handle stepped tangents correctly
+ // without having to look at the key flags.
+ // NOTE: Casting these values to `float` or otherwise modifying them can collapse
+ // the keyframes to have the identical time.
+ UFBX_BAKE_STEP_HANDLING_ADJACENT_DOUBLE,
+
+ // Treat all stepped tangents as linearly interpolated.
+ UFBX_BAKE_STEP_HANDLING_IGNORE,
+ ufbx_bake_step_handling_FORCE_32BIT = 2147483647, // Treat all stepped tangents as linearly interpolated.
+}
+
+BAKE_STEP_HANDLING_COUNT :: 5
+
+Bake_Opts :: struct {
+ _begin_zero: u32,
+ temp_allocator: Allocator_Opts, // < Allocator used during loading
+ result_allocator: Allocator_Opts, // < Allocator used for the final baked animation
+
+ // Move the keyframe times to start from zero regardless of the animation start time.
+ // For example, for an animation spanning between frames [30, 60] will be moved to
+ // [0, 30] in the baked animation.
+ // NOTE: This is in general not equivalent to subtracting `ufbx_anim.time_begin`
+ // from each keyframe, as this trimming is done exactly using internal FBX ticks.
+ trim_start_time: bool,
+
+ // Samples per second to use for resampling non-linear animation.
+ // Default: 30
+ resample_rate: f64,
+
+ // Minimum sample rate to not resample.
+ // Many exporters resample animation by default. To avoid double-resampling
+ // keyframe rates higher or equal to this will not be resampled.
+ // Default: 19.5
+ minimum_sample_rate: f64,
+
+ // Maximum sample rate to use, this will remove keys if they are too close together.
+ // Default: unlimited
+ maximum_sample_rate: f64,
+
+ // Bake the raw versions of properties related to transforms.
+ bake_transform_props: bool,
+
+ // Do not bake node transforms.
+ skip_node_transforms: bool,
+
+ // Do not resample linear rotation keyframes.
+ // FBX interpolates rotation in Euler angles, so this might cause incorrect interpolation.
+ no_resample_rotation: bool,
+
+ // Ignore layer weight animation.
+ ignore_layer_weight_animation: bool,
+
+ // Maximum number of segments to generate from one keyframe.
+ // Default: 32
+ max_keyframe_segments: c.size_t,
+
+ // How to handle stepped tangents.
+ step_handling: Bake_Step_Handling,
+
+ // Interpolation duration used by `UFBX_BAKE_STEP_HANDLING_CUSTOM_DURATION`.
+ step_custom_duration: f64,
+
+ // Interpolation epsilon used by `UFBX_BAKE_STEP_HANDLING_CUSTOM_DURATION`.
+ // Defined as the minimum fractional decrease/increase in key time, ie.
+ // `time / (1.0 + step_custom_epsilon)` and `time * (1.0 + step_custom_epsilon)`.
+ step_custom_epsilon: f64,
+
+ // Flags passed to animation evaluation functions.
+ // See `ufbx_evaluate_flags`.
+ evaluate_flags: u32,
+
+ // Enable key reduction.
+ key_reduction_enabled: bool,
+
+ // Enable key reduction for non-constant rotations.
+ // Assumes rotations will be interpolated using a spherical linear interpolation at runtime.
+ key_reduction_rotation: bool,
+
+ // Threshold for reducing keys for linear segments.
+ // Default `0.000001`, use negative to disable.
+ key_reduction_threshold: f64,
+
+ // Maximum passes over the keys to reduce.
+ // Every pass can potentially halve the the amount of keys.
+ // Default: `4`
+ key_reduction_passes: c.size_t,
+ _end_zero: u32,
+}
+
+// Options for `ufbx_tessellate_nurbs_curve()`
+// NOTE: Initialize to zero with `{ 0 }` (C) or `{ }` (C++)
+Tessellate_Curve_Opts :: struct {
+ _begin_zero: u32,
+ temp_allocator: Allocator_Opts, // < Allocator used during tessellation
+ result_allocator: Allocator_Opts, // < Allocator used for the final line curve
+
+ // How many segments tessellate each span in `ufbx_nurbs_basis.spans`.
+ span_subdivision: c.size_t,
+ _end_zero: u32,
+}
+
+// Options for `ufbx_tessellate_nurbs_surface()`
+// NOTE: Initialize to zero with `{ 0 }` (C) or `{ }` (C++)
+Tessellate_Surface_Opts :: struct {
+ _begin_zero: u32,
+ temp_allocator: Allocator_Opts, // < Allocator used during tessellation
+ result_allocator: Allocator_Opts, // < Allocator used for the final mesh
+
+ // How many segments tessellate each span in `ufbx_nurbs_basis.spans`.
+ // NOTE: Default is `4`, _not_ `ufbx_nurbs_surface.span_subdivision_u/v` as that
+ // would make it easy to create an FBX file with an absurdly high subdivision
+ // rate (similar to mesh subdivision). Please enforce copy the value yourself
+ // enforcing whatever limits you deem reasonable.
+ span_subdivision_u: c.size_t,
+ span_subdivision_v: c.size_t,
+
+ // Skip computing `ufbx_mesh.material_parts[]`
+ skip_mesh_parts: bool,
+ _end_zero: u32,
+}
+
+// Options for `ufbx_subdivide_mesh()`
+// NOTE: Initialize to zero with `{ 0 }` (C) or `{ }` (C++)
+Subdivide_Opts :: struct {
+ _begin_zero: u32,
+ temp_allocator: Allocator_Opts, // < Allocator used during subdivision
+ result_allocator: Allocator_Opts, // < Allocator used for the final mesh
+ boundary: Subdivision_Boundary,
+ uv_boundary: Subdivision_Boundary,
+
+ // Do not generate normals
+ ignore_normals: bool,
+
+ // Interpolate existing normals using the subdivision rules
+ // instead of generating new normals
+ interpolate_normals: bool,
+
+ // Subdivide also tangent attributes
+ interpolate_tangents: bool,
+
+ // Map subdivided vertices into weighted original vertices.
+ // NOTE: May be O(n^2) if `max_source_vertices` is not specified!
+ evaluate_source_vertices: bool,
+
+ // Limit source vertices per subdivided vertex.
+ max_source_vertices: c.size_t,
+
+ // Calculate bone influences over subdivided vertices (if applicable).
+ // NOTE: May be O(n^2) if `max_skin_weights` is not specified!
+ evaluate_skin_weights: bool,
+
+ // Limit bone influences per subdivided vertex.
+ max_skin_weights: c.size_t,
+
+ // Index of the skin deformer to use for `evaluate_skin_weights`.
+ skin_deformer_index: c.size_t,
+ _end_zero: u32,
+}
+
+// Options for `ufbx_load_geometry_cache()`
+// NOTE: Initialize to zero with `{ 0 }` (C) or `{ }` (C++)
+Geometry_Cache_Opts :: struct {
+ _begin_zero: u32,
+ temp_allocator: Allocator_Opts, // < Allocator used during loading
+ result_allocator: Allocator_Opts, // < Allocator used for the final scene
+
+ // External file callbacks (defaults to stdio.h)
+ open_file_cb: Open_File_Cb,
+
+ // FPS value for converting frame times to seconds
+ frames_per_second: f64,
+
+ // Axis to mirror the geometry by.
+ mirror_axis: Mirror_Axis,
+
+ // Enable scaling `scale_factor` all geometry by.
+ use_scale_factor: bool,
+
+ // Factor to scale the geometry by.
+ scale_factor: Real,
+ _end_zero: u32,
+}
+
+// Options for `ufbx_read_geometry_cache_TYPE()`
+// NOTE: Initialize to zero with `{ 0 }` (C) or `{ }` (C++)
+Geometry_Cache_Data_Opts :: struct {
+ _begin_zero: u32,
+
+ // External file callbacks (defaults to stdio.h)
+ open_file_cb: Open_File_Cb,
+ additive: bool,
+ use_weight: bool,
+ weight: Real,
+
+ // Ignore scene transform.
+ ignore_transform: bool,
+ _end_zero: u32,
+}
+
+Panic :: struct {
+ did_panic: bool,
+ message_length: c.size_t,
+ message: [128]c.char,
+}
+
+// Flags to control `ufbx_evaluate_transform_flags()`.
+Transform_Flag :: enum c.int {
+ // Ignore parent scale helper.
+ IGNORE_SCALE_HELPER = 0,
+
+ // Ignore componentwise scale.
+ // Note that if you don't specify this, ufbx will have to potentially
+ // evaluate the entire parent chain in the worst case.
+ IGNORE_COMPONENTWISE_SCALE = 1,
+
+ // Require explicit components
+ EXPLICIT_INCLUDES = 2,
+
+ // If `UFBX_TRANSFORM_FLAG_EXPLICIT_INCLUDES`: Evaluate `ufbx_transform.translation`.
+ INCLUDE_TRANSLATION = 4,
+
+ // If `UFBX_TRANSFORM_FLAG_EXPLICIT_INCLUDES`: Evaluate `ufbx_transform.rotation`.
+ INCLUDE_ROTATION = 5,
+
+ // If `UFBX_TRANSFORM_FLAG_EXPLICIT_INCLUDES`: Evaluate `ufbx_transform.scale`.
+ INCLUDE_SCALE = 6,
+
+ // Do not extrapolate keyframes.
+ // See `UFBX_EVALUATE_FLAG_NO_EXTRAPOLATION`.
+ NO_EXTRAPOLATION = 7,
+}
+
+Transform_Flags :: distinct bit_set[Transform_Flag; c.int]
+
+TRANSFORM_FLAGS_FORCE_32BIT :: Transform_Flags { .IGNORE_SCALE_HELPER, .IGNORE_COMPONENTWISE_SCALE, .EXPLICIT_INCLUDES, .INCLUDE_TRANSLATION, .INCLUDE_ROTATION, .INCLUDE_SCALE, .NO_EXTRAPOLATION }
+
+// bindgen-enable
+
+// -- Properties
+
+// Names of common properties in `ufbx_props`.
+// Some of these differ from ufbx interpretations.
+
+// Local translation.
+// Used by: `ufbx_node`
+Lcl_Translation :: "Lcl Translation"
+
+// Local rotation expressed in Euler degrees.
+// Used by: `ufbx_node`
+// The rotation order is defined by the `UFBX_RotationOrder` property.
+Lcl_Rotation :: "Lcl Rotation"
+
+// Local scaling factor, 3D vector.
+// Used by: `ufbx_node`
+Lcl_Scaling :: "Lcl Scaling"
+
+// Euler rotation interpretation, used by `UFBX_Lcl_Rotation`.
+// Used by: `ufbx_node`, enum value `ufbx_rotation_order`.
+RotationOrder :: "RotationOrder"
+
+// Scaling pivot: point around which scaling is performed.
+// Used by: `ufbx_node`.
+ScalingPivot :: "ScalingPivot"
+
+// Scaling pivot: point around which rotation is performed.
+// Used by: `ufbx_node`.
+RotationPivot :: "RotationPivot"
+
+// Scaling offset: translation added after scaling is performed.
+// Used by: `ufbx_node`.
+ScalingOffset :: "ScalingOffset"
+
+// Rotation offset: translation added after rotation is performed.
+// Used by: `ufbx_node`.
+RotationOffset :: "RotationOffset"
+
+// Pre-rotation: Rotation applied _after_ `UFBX_Lcl_Rotation`.
+// Used by: `ufbx_node`.
+// Affected by `UFBX_RotationPivot` but not `UFBX_RotationOrder`.
+PreRotation :: "PreRotation"
+
+// Post-rotation: Rotation applied _before_ `UFBX_Lcl_Rotation`.
+// Used by: `ufbx_node`.
+// Affected by `UFBX_RotationPivot` but not `UFBX_RotationOrder`.
+PostRotation :: "PostRotation"
+
+// Controls whether the node should be displayed or not.
+// Used by: `ufbx_node`.
+Visibility :: "Visibility"
+
+// Weight of an animation layer in percentage (100.0 being full).
+// Used by: `ufbx_anim_layer`.
+Weight :: "Weight"
+
+// Blend shape deformation weight (100.0 being full).
+// Used by: `ufbx_blend_channel`.
+DeformPercent :: "DeformPercent"
+@(default_calling_convention="c", link_prefix="ufbx_")
+foreign lib {
+ // Practically always `true` (see below), if not you need to be careful with threads.
+ //
+ // Guaranteed to be `true` in _any_ of the following conditions:
+ // - ufbx.c has been compiled using: GCC / Clang / MSVC / ICC / EMCC / TCC
+ // - ufbx.c has been compiled as C++11 or later
+ // - ufbx.c has been compiled as C11 or later with `<stdatomic.h>` support
+ //
+ // If `false` you can't call the following functions concurrently:
+ // ufbx_evaluate_scene()
+ // ufbx_free_scene()
+ // ufbx_subdivide_mesh()
+ // ufbx_tessellate_nurbs_surface()
+ // ufbx_free_mesh()
+ is_thread_safe :: proc() -> bool ---
+
+ // Load a scene from a `size` byte memory buffer at `data`
+ load_memory :: proc(data: rawptr, data_size: c.size_t, opts: ^Load_Opts, error: ^Error) -> ^Scene ---
+
+ // Load a scene by opening a file named `filename`
+ load_file :: proc(filename: cstring, opts: ^Load_Opts, error: ^Error) -> ^Scene ---
+ load_file_len :: proc(filename: cstring, filename_len: c.size_t, opts: ^Load_Opts, error: ^Error) -> ^Scene ---
+
+ // Load a scene by reading from an `FILE *file` stream
+ // NOTE: `file` is passed as a `void` pointer to avoid including <stdio.h>
+ load_stdio :: proc(file: rawptr, opts: ^Load_Opts, error: ^Error) -> ^Scene ---
+
+ // Load a scene by reading from an `FILE *file` stream with a prefix
+ // NOTE: `file` is passed as a `void` pointer to avoid including <stdio.h>
+ load_stdio_prefix :: proc(file: rawptr, prefix: rawptr, prefix_size: c.size_t, opts: ^Load_Opts, error: ^Error) -> ^Scene ---
+
+ // Load a scene from a user-specified stream
+ load_stream :: proc(stream: ^Stream, opts: ^Load_Opts, error: ^Error) -> ^Scene ---
+
+ // Load a scene from a user-specified stream with a prefix
+ load_stream_prefix :: proc(stream: ^Stream, prefix: rawptr, prefix_size: c.size_t, opts: ^Load_Opts, error: ^Error) -> ^Scene ---
+
+ // Free a previously loaded or evaluated scene
+ free_scene :: proc(scene: ^Scene) ---
+
+ // Increment `scene` refcount
+ retain_scene :: proc(scene: ^Scene) ---
+
+ // Format a textual description of `error`.
+ // Always produces a NULL-terminated string to `char dst[dst_size]`, truncating if
+ // necessary. Returns the number of characters written not including the NULL terminator.
+ format_error :: proc(dst: cstring, dst_size: c.size_t, error: ^Error) -> c.size_t ---
+
+ // Find a property `name` from `props`, returns `NULL` if not found.
+ // Searches through `ufbx_props.defaults` as well.
+ find_prop_len :: proc(props: ^Props, name: cstring, name_len: c.size_t) -> ^Prop ---
+ find_prop :: proc(props: ^Props, name: cstring) -> ^Prop ---
+
+ // Utility functions for finding the value of a property, returns `def` if not found.
+ // NOTE: For `ufbx_string` you need to ensure the lifetime of the default is
+ // sufficient as no copy is made.
+ find_real_len :: proc(props: ^Props, name: cstring, name_len: c.size_t, def: Real) -> Real ---
+ find_real :: proc(props: ^Props, name: cstring, def: Real) -> Real ---
+ find_vec3_len :: proc(props: ^Props, name: cstring, name_len: c.size_t, def: Vec3) -> Vec3 ---
+ find_vec3 :: proc(props: ^Props, name: cstring, def: Vec3) -> Vec3 ---
+ find_int_len :: proc(props: ^Props, name: cstring, name_len: c.size_t, def: i64) -> i64 ---
+ find_int :: proc(props: ^Props, name: cstring, def: i64) -> i64 ---
+ find_bool_len :: proc(props: ^Props, name: cstring, name_len: c.size_t, def: bool) -> bool ---
+ find_bool :: proc(props: ^Props, name: cstring, def: bool) -> bool ---
+ find_string_len :: proc(props: ^Props, name: cstring, name_len: c.size_t, def: String) -> String ---
+ find_string :: proc(props: ^Props, name: cstring, def: String) -> String ---
+ find_blob_len :: proc(props: ^Props, name: cstring, name_len: c.size_t, def: Blob) -> Blob ---
+ find_blob :: proc(props: ^Props, name: cstring, def: Blob) -> Blob ---
+
+ // Find property in `props` with concatenated `parts[num_parts]`.
+ find_prop_concat :: proc(props: ^Props, parts: ^String, num_parts: c.size_t) -> ^Prop ---
+
+ // Get an element connected to a property.
+ get_prop_element :: proc(element: ^Element, prop: ^Prop, type: Element_Type) -> ^Element ---
+
+ // Find an element connected to a property by name.
+ find_prop_element_len :: proc(element: ^Element, name: cstring, name_len: c.size_t, type: Element_Type) -> ^Element ---
+ find_prop_element :: proc(element: ^Element, name: cstring, type: Element_Type) -> ^Element ---
+
+ // Find any element of type `type` in `scene` by `name`.
+ // For example if you want to find `ufbx_material` named `Mat`:
+ // (ufbx_material*)ufbx_find_element(scene, UFBX_ELEMENT_MATERIAL, "Mat");
+ find_element_len :: proc(scene: ^Scene, type: Element_Type, name: cstring, name_len: c.size_t) -> ^Element ---
+ find_element :: proc(scene: ^Scene, type: Element_Type, name: cstring) -> ^Element ---
+
+ // Find node in `scene` by `name` (shorthand for `ufbx_find_element(UFBX_ELEMENT_NODE)`).
+ find_node_len :: proc(scene: ^Scene, name: cstring, name_len: c.size_t) -> ^Node ---
+ find_node :: proc(scene: ^Scene, name: cstring) -> ^Node ---
+
+ // Find an animation stack in `scene` by `name` (shorthand for `ufbx_find_element(UFBX_ELEMENT_ANIM_STACK)`)
+ find_anim_stack_len :: proc(scene: ^Scene, name: cstring, name_len: c.size_t) -> ^Anim_Stack ---
+ find_anim_stack :: proc(scene: ^Scene, name: cstring) -> ^Anim_Stack ---
+
+ // Find a material in `scene` by `name` (shorthand for `ufbx_find_element(UFBX_ELEMENT_MATERIAL)`).
+ find_material_len :: proc(scene: ^Scene, name: cstring, name_len: c.size_t) -> ^Material ---
+ find_material :: proc(scene: ^Scene, name: cstring) -> ^Material ---
+
+ // Find a single animated property `prop` of `element` in `layer`.
+ // Returns `NULL` if not found.
+ find_anim_prop_len :: proc(layer: ^Anim_Layer, element: ^Element, prop: cstring, prop_len: c.size_t) -> ^Anim_Prop ---
+ find_anim_prop :: proc(layer: ^Anim_Layer, element: ^Element, prop: cstring) -> ^Anim_Prop ---
+
+ // Find all animated properties of `element` in `layer`.
+ find_anim_props :: proc(layer: ^Anim_Layer, element: ^Element) -> Anim_Prop_List ---
+
+ // Get a matrix that transforms normals in the same way as Autodesk software.
+ // NOTE: The resulting normals are slightly incorrect as this function deliberately
+ // inverts geometric transformation wrong. For better results use
+ // `ufbx_matrix_for_normals(&node->geometry_to_world)`.
+ get_compatible_matrix_for_normals :: proc(node: ^Node) -> Matrix ---
+
+ // Decompress a DEFLATE compressed buffer.
+ // Returns the decompressed size or a negative error code (see source for details).
+ // NOTE: You must supply a valid `retain` with `ufbx_inflate_retain.initialized == false`
+ // but the rest can be uninitialized.
+ inflate :: proc(dst: rawptr, dst_size: c.size_t, input: ^Inflate_Input, retain: ^Inflate_Retain) -> c.ptrdiff_t ---
+
+ // Same as `ufbx_open_file()` but compatible with the callback in `ufbx_open_file_fn`.
+ // The `user` parameter is actually not used here.
+ default_open_file :: proc(user: rawptr, stream: ^Stream, path: cstring, path_len: c.size_t, info: ^Open_File_Info) -> bool ---
+
+ // Open a `ufbx_stream` from a file.
+ // Use `path_len == SIZE_MAX` for NULL terminated string.
+ open_file :: proc(stream: ^Stream, path: cstring, path_len: c.size_t, opts: ^Open_File_Opts, error: ^Error) -> bool ---
+ open_file_ctx :: proc(stream: ^Stream, ctx: Open_File_Context, path: cstring, path_len: c.size_t, opts: ^Open_File_Opts, error: ^Error) -> bool ---
+
+ // NOTE: Uses the default ufbx allocator!
+ open_memory :: proc(stream: ^Stream, data: rawptr, data_size: c.size_t, opts: ^Open_Memory_Opts, error: ^Error) -> bool ---
+ open_memory_ctx :: proc(stream: ^Stream, ctx: Open_File_Context, data: rawptr, data_size: c.size_t, opts: ^Open_Memory_Opts, error: ^Error) -> bool ---
+
+ // Evaluate a single animation `curve` at a `time`.
+ // Returns `default_value` only if `curve == NULL` or it has no keyframes.
+ evaluate_curve :: proc(curve: ^Anim_Curve, time: f64, default_value: Real) -> Real ---
+ evaluate_curve_flags :: proc(curve: ^Anim_Curve, time: f64, default_value: Real, flags: u32) -> Real ---
+
+ // Evaluate a value from bundled animation curves.
+ evaluate_anim_value_real :: proc(anim_value: ^Anim_Value, time: f64) -> Real ---
+ evaluate_anim_value_vec3 :: proc(anim_value: ^Anim_Value, time: f64) -> Vec3 ---
+ evaluate_anim_value_real_flags :: proc(anim_value: ^Anim_Value, time: f64, flags: u32) -> Real ---
+ evaluate_anim_value_vec3_flags :: proc(anim_value: ^Anim_Value, time: f64, flags: u32) -> Vec3 ---
+
+ // Evaluate an animated property `name` from `element` at `time`.
+ // NOTE: If the property is not found it will have the flag `UFBX_PROP_FLAG_NOT_FOUND`.
+ evaluate_prop_len :: proc(anim: ^Anim, element: ^Element, name: cstring, name_len: c.size_t, time: f64) -> Prop ---
+ evaluate_prop :: proc(anim: ^Anim, element: ^Element, name: cstring, time: f64) -> Prop ---
+ evaluate_prop_len_flags :: proc(anim: ^Anim, element: ^Element, name: cstring, name_len: c.size_t, time: f64, flags: u32) -> Prop ---
+ evaluate_prop_flags :: proc(anim: ^Anim, element: ^Element, name: cstring, time: f64, flags: u32) -> Prop ---
+
+ // Evaluate all _animated_ properties of `element`.
+ // HINT: This function returns an `ufbx_props` structure with the original properties as
+ // `ufbx_props.defaults`. This lets you use `ufbx_find_prop/value()` for the results.
+ evaluate_props :: proc(anim: ^Anim, element: ^Element, time: f64, buffer: ^Prop, buffer_size: c.size_t) -> Props ---
+ evaluate_props_flags :: proc(anim: ^Anim, element: ^Element, time: f64, buffer: ^Prop, buffer_size: c.size_t, flags: u32) -> Props ---
+
+ // Evaluate the animated transform of a node given a time.
+ // The returned transform is the local transform of the node (ie. relative to the parent),
+ // comparable to `ufbx_node.local_transform`.
+ evaluate_transform :: proc(anim: ^Anim, node: ^Node, time: f64) -> Transform ---
+ evaluate_transform_flags :: proc(anim: ^Anim, node: ^Node, time: f64, flags: u32) -> Transform ---
+
+ // Evaluate the blend shape weight of a blend channel.
+ // NOTE: Return value uses `1.0` for full weight, instead of `100.0` that the internal property `UFBX_Weight` uses.
+ evaluate_blend_weight :: proc(anim: ^Anim, channel: ^Blend_Channel, time: f64) -> Real ---
+ evaluate_blend_weight_flags :: proc(anim: ^Anim, channel: ^Blend_Channel, time: f64, flags: u32) -> Real ---
+
+ // Evaluate the whole `scene` at a specific `time` in the animation `anim`.
+ // The returned scene behaves as if it had been exported at a specific time
+ // in the specified animation, except that animated elements' properties contain
+ // only the animated values, the original ones are in `props->defaults`.
+ //
+ // NOTE: The returned scene refers to the original `scene` so the original
+ // scene cannot be freed until all evaluated scenes are freed.
+ evaluate_scene :: proc(scene: ^Scene, anim: ^Anim, time: f64, opts: ^Evaluate_Opts, error: ^Error) -> ^Scene ---
+
+ // Create a custom animation descriptor.
+ // `ufbx_anim_opts` is used to specify animation layers and weights.
+ // HINT: You can also leave `ufbx_anim_opts.layer_ids[]` empty and only specify
+ // overrides to evaluate the scene with different properties or local transforms.
+ create_anim :: proc(scene: ^Scene, opts: ^Anim_Opts, error: ^Error) -> ^Anim ---
+
+ // Free an animation returned by `ufbx_create_anim()`.
+ free_anim :: proc(anim: ^Anim) ---
+
+ // Increase the animation reference count.
+ retain_anim :: proc(anim: ^Anim) ---
+
+ // "Bake" an animation to linearly interpolated keyframes.
+ // Composites the FBX transformation chain into quaternion rotations.
+ bake_anim :: proc(scene: ^Scene, anim: ^Anim, opts: ^Bake_Opts, error: ^Error) -> ^Baked_Anim ---
+ retain_baked_anim :: proc(bake: ^Baked_Anim) ---
+ free_baked_anim :: proc(bake: ^Baked_Anim) ---
+ find_baked_node_by_typed_id :: proc(bake: ^Baked_Anim, typed_id: u32) -> ^Baked_Node ---
+ find_baked_node :: proc(bake: ^Baked_Anim, node: ^Node) -> ^Baked_Node ---
+ find_baked_element_by_element_id :: proc(bake: ^Baked_Anim, element_id: u32) -> ^Baked_Element ---
+ find_baked_element :: proc(bake: ^Baked_Anim, element: ^Element) -> ^Baked_Element ---
+
+ // Evaluate baked animation `keyframes` at `time`.
+ // Internally linearly interpolates between two adjacent keyframes.
+ // Handles stepped tangents cleanly, which is not strictly necessary for custom interpolation.
+ evaluate_baked_vec3 :: proc(keyframes: Baked_Vec3_List, time: f64) -> Vec3 ---
+
+ // Evaluate baked animation `keyframes` at `time`.
+ // Internally spherically interpolates (`ufbx_quat_slerp()`) between two adjacent keyframes.
+ // Handles stepped tangents cleanly, which is not strictly necessary for custom interpolation.
+ evaluate_baked_quat :: proc(keyframes: Baked_Quat_List, time: f64) -> Quat ---
+
+ // Retrieve the bone pose for `node`.
+ // Returns `NULL` if the pose does not contain `node`.
+ get_bone_pose :: proc(pose: ^Pose, node: ^Node) -> ^Bone_Pose ---
+
+ // Find a texture for a given material FBX property.
+ find_prop_texture_len :: proc(material: ^Material, name: cstring, name_len: c.size_t) -> ^Texture ---
+ find_prop_texture :: proc(material: ^Material, name: cstring) -> ^Texture ---
+
+ // Find a texture for a given shader property.
+ find_shader_prop_len :: proc(shader: ^Shader, name: cstring, name_len: c.size_t) -> String ---
+ find_shader_prop :: proc(shader: ^Shader, name: cstring) -> String ---
+
+ // Map from a shader property to material property.
+ find_shader_prop_bindings_len :: proc(shader: ^Shader, name: cstring, name_len: c.size_t) -> Shader_Prop_Binding_List ---
+ find_shader_prop_bindings :: proc(shader: ^Shader, name: cstring) -> Shader_Prop_Binding_List ---
+
+ // Find an input in a shader texture.
+ find_shader_texture_input_len :: proc(shader: ^Shader_Texture, name: cstring, name_len: c.size_t) -> ^Shader_Texture_Input ---
+ find_shader_texture_input :: proc(shader: ^Shader_Texture, name: cstring) -> ^Shader_Texture_Input ---
+
+ // Returns `true` if `axes` forms a valid coordinate space.
+ coordinate_axes_valid :: proc(axes: Coordinate_Axes) -> bool ---
+
+ // Vector math utility functions.
+ vec3_normalize :: proc(v: Vec3) -> Vec3 ---
+
+ // Quaternion math utility functions.
+ quat_dot :: proc(a: Quat, b: Quat) -> Real ---
+ quat_mul :: proc(a: Quat, b: Quat) -> Quat ---
+ quat_normalize :: proc(q: Quat) -> Quat ---
+ quat_fix_antipodal :: proc(q: Quat, reference: Quat) -> Quat ---
+ quat_slerp :: proc(a: Quat, b: Quat, t: Real) -> Quat ---
+ quat_rotate_vec3 :: proc(q: Quat, v: Vec3) -> Vec3 ---
+ quat_to_euler :: proc(q: Quat, order: Rotation_Order) -> Vec3 ---
+ euler_to_quat :: proc(v: Vec3, order: Rotation_Order) -> Quat ---
+
+ // Matrix math utility functions.
+ matrix_mul :: proc(a: ^Matrix, b: ^Matrix) -> Matrix ---
+ matrix_determinant :: proc(m: ^Matrix) -> Real ---
+ matrix_invert :: proc(m: ^Matrix) -> Matrix ---
+
+ // Get a matrix that can be used to transform geometry normals.
+ // NOTE: You must normalize the normals after transforming them with this matrix,
+ // eg. using `ufbx_vec3_normalize()`.
+ // NOTE: This function flips the normals if the determinant is negative.
+ matrix_for_normals :: proc(m: ^Matrix) -> Matrix ---
+
+ // Matrix transformation utilities.
+ transform_position :: proc(m: ^Matrix, v: Vec3) -> Vec3 ---
+ transform_direction :: proc(m: ^Matrix, v: Vec3) -> Vec3 ---
+
+ // Conversions between `ufbx_matrix` and `ufbx_transform`.
+ transform_to_matrix :: proc(t: ^Transform) -> Matrix ---
+ matrix_to_transform :: proc(m: ^Matrix) -> Transform ---
+
+ // Get a matrix representing the deformation for a single vertex.
+ // Returns `fallback` if the vertex is not skinned.
+ catch_get_skin_vertex_matrix :: proc(panic: ^Panic, skin: ^Skin_Deformer, vertex: c.size_t, fallback: ^Matrix) -> Matrix ---
+ get_skin_vertex_matrix :: proc(skin: ^Skin_Deformer, vertex: c.size_t, fallback: ^Matrix) -> Matrix ---
+
+ // Resolve the index into `ufbx_blend_shape.position_offsets[]` given a vertex.
+ // Returns `UFBX_NO_INDEX` if the vertex is not included in the blend shape.
+ get_blend_shape_offset_index :: proc(shape: ^Blend_Shape, vertex: c.size_t) -> u32 ---
+
+ // Get the offset for a given vertex in the blend shape.
+ // Returns `ufbx_zero_vec3` if the vertex is not a included in the blend shape.
+ get_blend_shape_vertex_offset :: proc(shape: ^Blend_Shape, vertex: c.size_t) -> Vec3 ---
+
+ // Get the _current_ blend offset given a blend deformer.
+ // NOTE: This depends on the current animated blend weight of the deformer.
+ get_blend_vertex_offset :: proc(blend: ^Blend_Deformer, vertex: c.size_t) -> Vec3 ---
+
+ // Apply the blend shape with `weight` to given vertices.
+ add_blend_shape_vertex_offsets :: proc(shape: ^Blend_Shape, vertices: ^Vec3, num_vertices: c.size_t, weight: Real) ---
+
+ // Apply the blend deformer with `weight` to given vertices.
+ // NOTE: This depends on the current animated blend weight of the deformer.
+ add_blend_vertex_offsets :: proc(blend: ^Blend_Deformer, vertices: ^Vec3, num_vertices: c.size_t, weight: Real) ---
+
+ // Low-level utility to evaluate NURBS the basis functions.
+ evaluate_nurbs_basis :: proc(basis: ^Nurbs_Basis, u: Real, weights: ^Real, num_weights: c.size_t, derivatives: ^Real, num_derivatives: c.size_t) -> c.size_t ---
+
+ // Evaluate a point on a NURBS curve given the parameter `u`.
+ evaluate_nurbs_curve :: proc(curve: ^Nurbs_Curve, u: Real) -> Curve_Point ---
+
+ // Evaluate a point on a NURBS surface given the parameter `u` and `v`.
+ evaluate_nurbs_surface :: proc(surface: ^Nurbs_Surface, u: Real, v: Real) -> Surface_Point ---
+
+ // Tessellate a NURBS curve into a polyline.
+ tessellate_nurbs_curve :: proc(curve: ^Nurbs_Curve, opts: ^Tessellate_Curve_Opts, error: ^Error) -> ^Line_Curve ---
+
+ // Tessellate a NURBS surface into a mesh.
+ tessellate_nurbs_surface :: proc(surface: ^Nurbs_Surface, opts: ^Tessellate_Surface_Opts, error: ^Error) -> ^Mesh ---
+
+ // Free a line returned by `ufbx_tessellate_nurbs_curve()`.
+ free_line_curve :: proc(curve: ^Line_Curve) ---
+
+ // Increase the refcount of the line.
+ retain_line_curve :: proc(curve: ^Line_Curve) ---
+
+ // Find the face that contains a given `index`.
+ // Returns `UFBX_NO_INDEX` if out of bounds.
+ find_face_index :: proc(mesh: ^Mesh, index: c.size_t) -> u32 ---
+
+ // Triangulate a mesh face, returning the number of triangles.
+ // NOTE: You need to space for `(face.num_indices - 2) * 3 - 1` indices!
+ // HINT: Using `ufbx_mesh.max_face_triangles * 3` is always safe.
+ catch_triangulate_face :: proc(panic: ^Panic, indices: ^u32, num_indices: c.size_t, mesh: ^Mesh, face: Face) -> u32 ---
+ triangulate_face :: proc(indices: ^u32, num_indices: c.size_t, mesh: ^Mesh, face: Face) -> u32 ---
+
+ // Generate the half-edge representation of `mesh` to `topo[mesh->num_indices]`
+ catch_compute_topology :: proc(panic: ^Panic, mesh: ^Mesh, topo: ^Topo_Edge, num_topo: c.size_t) ---
+ compute_topology :: proc(mesh: ^Mesh, topo: ^Topo_Edge, num_topo: c.size_t) ---
+
+ // Get the next half-edge in `topo`.
+ catch_topo_next_vertex_edge :: proc(panic: ^Panic, topo: ^Topo_Edge, num_topo: c.size_t, index: u32) -> u32 ---
+ topo_next_vertex_edge :: proc(topo: ^Topo_Edge, num_topo: c.size_t, index: u32) -> u32 ---
+
+ // Get the previous half-edge in `topo`.
+ catch_topo_prev_vertex_edge :: proc(panic: ^Panic, topo: ^Topo_Edge, num_topo: c.size_t, index: u32) -> u32 ---
+ topo_prev_vertex_edge :: proc(topo: ^Topo_Edge, num_topo: c.size_t, index: u32) -> u32 ---
+
+ // Calculate a normal for a given face.
+ // The returned normal is weighted by face area.
+ catch_get_weighted_face_normal :: proc(panic: ^Panic, positions: ^Vertex_Vec3, face: Face) -> Vec3 ---
+ get_weighted_face_normal :: proc(positions: ^Vertex_Vec3, face: Face) -> Vec3 ---
+
+ // Generate indices for normals from the topology.
+ // Respects smoothing groups.
+ catch_generate_normal_mapping :: proc(panic: ^Panic, mesh: ^Mesh, topo: ^Topo_Edge, num_topo: c.size_t, normal_indices: ^u32, num_normal_indices: c.size_t, assume_smooth: bool) -> c.size_t ---
+ generate_normal_mapping :: proc(mesh: ^Mesh, topo: ^Topo_Edge, num_topo: c.size_t, normal_indices: ^u32, num_normal_indices: c.size_t, assume_smooth: bool) -> c.size_t ---
+
+ // Compute normals given normal indices.
+ // You can use `ufbx_generate_normal_mapping()` to generate the normal indices.
+ catch_compute_normals :: proc(panic: ^Panic, mesh: ^Mesh, positions: ^Vertex_Vec3, normal_indices: ^u32, num_normal_indices: c.size_t, normals: ^Vec3, num_normals: c.size_t) ---
+ compute_normals :: proc(mesh: ^Mesh, positions: ^Vertex_Vec3, normal_indices: ^u32, num_normal_indices: c.size_t, normals: ^Vec3, num_normals: c.size_t) ---
+
+ // Subdivide a mesh using the Catmull-Clark subdivision `level` times.
+ subdivide_mesh :: proc(mesh: ^Mesh, level: c.size_t, opts: ^Subdivide_Opts, error: ^Error) -> ^Mesh ---
+
+ // Free a mesh returned from `ufbx_subdivide_mesh()` or `ufbx_tessellate_nurbs_surface()`.
+ free_mesh :: proc(mesh: ^Mesh) ---
+
+ // Increase the mesh reference count.
+ retain_mesh :: proc(mesh: ^Mesh) ---
+
+ // Load geometry cache information from a file.
+ // As geometry caches can be massive, this does not actually read the data, but
+ // only seeks through the files to form the metadata.
+ load_geometry_cache :: proc(filename: cstring, opts: ^Geometry_Cache_Opts, error: ^Error) -> ^Geometry_Cache ---
+ load_geometry_cache_len :: proc(filename: cstring, filename_len: c.size_t, opts: ^Geometry_Cache_Opts, error: ^Error) -> ^Geometry_Cache ---
+
+ // Free a geometry cache returned from `ufbx_load_geometry_cache()`.
+ free_geometry_cache :: proc(cache: ^Geometry_Cache) ---
+
+ // Increase the geometry cache reference count.
+ retain_geometry_cache :: proc(cache: ^Geometry_Cache) ---
+
+ // Read a frame from a geometry cache.
+ read_geometry_cache_real :: proc(frame: ^Cache_Frame, data: ^Real, num_data: c.size_t, opts: ^Geometry_Cache_Data_Opts) -> c.size_t ---
+ read_geometry_cache_vec3 :: proc(frame: ^Cache_Frame, data: ^Vec3, num_data: c.size_t, opts: ^Geometry_Cache_Data_Opts) -> c.size_t ---
+
+ // Sample the a geometry cache channel, linearly blending between adjacent frames.
+ sample_geometry_cache_real :: proc(channel: ^Cache_Channel, time: f64, data: ^Real, num_data: c.size_t, opts: ^Geometry_Cache_Data_Opts) -> c.size_t ---
+ sample_geometry_cache_vec3 :: proc(channel: ^Cache_Channel, time: f64, data: ^Vec3, num_data: c.size_t, opts: ^Geometry_Cache_Data_Opts) -> c.size_t ---
+
+ // Find a DOM node given a name.
+ dom_find_len :: proc(parent: ^Dom_Node, name: cstring, name_len: c.size_t) -> ^Dom_Node ---
+ dom_find :: proc(parent: ^Dom_Node, name: cstring) -> ^Dom_Node ---
+
+ // Generate an index buffer for a flat vertex buffer.
+ // `streams` specifies one or more vertex data arrays, each stream must contain `num_indices` vertices.
+ // This function compacts the data within `streams` in-place, writing the deduplicated indices to `indices`.
+ generate_indices :: proc(streams: [^]Vertex_Stream, num_streams: c.size_t, indices: ^u32, num_indices: c.size_t, allocator: ^Allocator_Opts, error: ^Error) -> c.size_t ---
+
+ // Run a single thread pool task.
+ // See `ufbx_thread_pool_run_fn` for more information.
+ thread_pool_run_task :: proc(ctx: Thread_Pool_Context, index: u32) ---
+
+ // Get or set an arbitrary user pointer for the thread pool context.
+ // `ufbx_thread_pool_get_user_ptr()` returns `NULL` if unset.
+ thread_pool_set_user_ptr :: proc(ctx: Thread_Pool_Context, user_ptr: rawptr) ---
+ thread_pool_get_user_ptr :: proc(ctx: Thread_Pool_Context) -> rawptr ---
+
+ // Utility functions for reading geometry data for a single index.
+ catch_get_vertex_real :: proc(panic: ^Panic, v: ^Vertex_Real, index: c.size_t) -> Real ---
+ catch_get_vertex_vec2 :: proc(panic: ^Panic, v: ^Vertex_Vec2, index: c.size_t) -> Vec2 ---
+ catch_get_vertex_vec3 :: proc(panic: ^Panic, v: ^Vertex_Vec3, index: c.size_t) -> Vec3 ---
+ catch_get_vertex_vec4 :: proc(panic: ^Panic, v: ^Vertex_Vec4, index: c.size_t) -> Vec4 ---
+
+ // Utility functions for reading geometry data for a single index.
+ get_vertex_real :: proc(v: ^Vertex_Real, index: c.size_t) -> Real ---
+ get_vertex_vec2 :: proc(v: ^Vertex_Vec2, index: c.size_t) -> Vec2 ---
+ get_vertex_vec3 :: proc(v: ^Vertex_Vec3, index: c.size_t) -> Vec3 ---
+ get_vertex_vec4 :: proc(v: ^Vertex_Vec4, index: c.size_t) -> Vec4 ---
+ catch_get_vertex_w_vec3 :: proc(panic: ^Panic, v: ^Vertex_Vec3, index: c.size_t) -> Real ---
+ get_vertex_w_vec3 :: proc(v: ^Vertex_Vec3, index: c.size_t) -> Real ---
+
+ // Functions for converting an untyped `ufbx_element` to a concrete type.
+ // Returns `NULL` if the element is not that type.
+ as_unknown :: proc(element: ^Element) -> ^Unknown ---
+ as_node :: proc(element: ^Element) -> ^Node ---
+ as_mesh :: proc(element: ^Element) -> ^Mesh ---
+ as_light :: proc(element: ^Element) -> ^Light ---
+ as_camera :: proc(element: ^Element) -> ^Camera ---
+ as_bone :: proc(element: ^Element) -> ^Bone ---
+ as_empty :: proc(element: ^Element) -> ^Empty ---
+ as_line_curve :: proc(element: ^Element) -> ^Line_Curve ---
+ as_nurbs_curve :: proc(element: ^Element) -> ^Nurbs_Curve ---
+ as_nurbs_surface :: proc(element: ^Element) -> ^Nurbs_Surface ---
+ as_nurbs_trim_surface :: proc(element: ^Element) -> ^Nurbs_Trim_Surface ---
+ as_nurbs_trim_boundary :: proc(element: ^Element) -> ^Nurbs_Trim_Boundary ---
+ as_procedural_geometry :: proc(element: ^Element) -> ^Procedural_Geometry ---
+ as_stereo_camera :: proc(element: ^Element) -> ^Stereo_Camera ---
+ as_camera_switcher :: proc(element: ^Element) -> ^Camera_Switcher ---
+ as_marker :: proc(element: ^Element) -> ^Marker ---
+ as_lod_group :: proc(element: ^Element) -> ^Lod_Group ---
+ as_skin_deformer :: proc(element: ^Element) -> ^Skin_Deformer ---
+ as_skin_cluster :: proc(element: ^Element) -> ^Skin_Cluster ---
+ as_blend_deformer :: proc(element: ^Element) -> ^Blend_Deformer ---
+ as_blend_channel :: proc(element: ^Element) -> ^Blend_Channel ---
+ as_blend_shape :: proc(element: ^Element) -> ^Blend_Shape ---
+ as_cache_deformer :: proc(element: ^Element) -> ^Cache_Deformer ---
+ as_cache_file :: proc(element: ^Element) -> ^Cache_File ---
+ as_material :: proc(element: ^Element) -> ^Material ---
+ as_texture :: proc(element: ^Element) -> ^Texture ---
+ as_video :: proc(element: ^Element) -> ^Video ---
+ as_shader :: proc(element: ^Element) -> ^Shader ---
+ as_shader_binding :: proc(element: ^Element) -> ^Shader_Binding ---
+ as_anim_stack :: proc(element: ^Element) -> ^Anim_Stack ---
+ as_anim_layer :: proc(element: ^Element) -> ^Anim_Layer ---
+ as_anim_value :: proc(element: ^Element) -> ^Anim_Value ---
+ as_anim_curve :: proc(element: ^Element) -> ^Anim_Curve ---
+ as_display_layer :: proc(element: ^Element) -> ^Display_Layer ---
+ as_selection_set :: proc(element: ^Element) -> ^Selection_Set ---
+ as_selection_node :: proc(element: ^Element) -> ^Selection_Node ---
+ as_character :: proc(element: ^Element) -> ^Character ---
+ as_constraint :: proc(element: ^Element) -> ^Constraint ---
+ as_audio_layer :: proc(element: ^Element) -> ^Audio_Layer ---
+ as_audio_clip :: proc(element: ^Element) -> ^Audio_Clip ---
+ as_pose :: proc(element: ^Element) -> ^Pose ---
+ as_metadata_object :: proc(element: ^Element) -> ^Metadata_Object ---
+}
diff --git a/odin-c-bindgen/src/bindgen.odin b/odin-c-bindgen/src/bindgen.odin
@@ -0,0 +1,2808 @@
+/*
+Generates Odin bindings from C code.
+
+Usage:
+bindgen folder_with_headers_inside
+
+The folder can contain a `bindgen.sjson` file tha can be used to do overrides
+and configure the generation. See the examples folder for how to do that.
+*/
+
+#+feature dynamic-literals
+
+package bindgen
+
+import "core:fmt"
+import "core:os"
+import "core:os/os2"
+import "core:strings"
+import "core:path/filepath"
+import "core:math/bits"
+import "core:encoding/json"
+import "core:strconv"
+import "core:unicode/utf8"
+import "core:unicode"
+import "core:slice"
+import vmem "core:mem/virtual"
+
+Struct_Field :: struct {
+ names: [dynamic]string,
+ type: string,
+ anon_struct_type: Maybe(Struct),
+ anon_using: bool,
+ comment: string,
+ comment_before: bool,
+ original_line: int,
+}
+
+Struct :: struct {
+ original_name: string,
+ name: string,
+ id: string,
+ fields: []Struct_Field,
+ comment: string,
+ is_union: bool,
+ is_forward_declare: bool,
+}
+
+Function_Parameter :: struct {
+ name: string,
+ type: string,
+}
+
+Function :: struct {
+ original_name: string,
+ name: string,
+
+ // if non-empty, then use this will be the link name used in bindings
+ link_name: string,
+
+ parameters: []Function_Parameter,
+ return_type: string,
+ comment: string,
+ comment_before: bool,
+ variadic: bool,
+ post_comment: string,
+}
+
+Enum_Member :: struct {
+ name: string,
+ value: Maybe(int),
+ comment: string,
+ comment_before: bool,
+}
+
+Enum :: struct {
+ original_name: string,
+ name: string,
+ id: string,
+ members: []Enum_Member,
+ comment: string,
+}
+
+Typedef :: struct {
+ original_name: string,
+ name: string,
+ type: string,
+ pre_comment: string,
+ side_comment: string,
+}
+
+Macro :: struct {
+ original_name: string,
+ name: string,
+ val: string,
+ comment: string,
+ side_comment: string,
+ whitespace_after_name: int,
+ whitespace_before_side_comment: int,
+}
+
+Declaration_Variant :: union {
+ Struct,
+ Function,
+ Enum,
+ Typedef,
+ Macro,
+}
+
+Declaration :: struct {
+ // Used for sorting the declarations. They may be added out-of-order due to macros
+ // coming in from a separate code path.
+ line: int,
+
+ // The original idx in `s.decls`. This is for tie-breaking when line is the same.
+ original_idx: int,
+
+ variant: Declaration_Variant,
+}
+
+get_parameter_type :: proc(s: ^Gen_State, v: json.Value) -> (type: string, ok: bool) {
+ t := json_get(v, "type.qualType", json.String) or_return
+
+ if is_c_type(t) {
+ s.needs_import_c = true
+ }
+
+ if is_libc_type(t) {
+ s.needs_import_libc = true
+ }
+
+ if is_posix_type(t) {
+ s.needs_import_posix = true
+ }
+
+ return t, true
+}
+
+get_return_type :: proc(v: json.Value) -> (type: string, ok: bool) {
+ qual_type := json_get(v, "type.qualType", json.String) or_return
+ end := strings.index(qual_type, "(")
+ t := qual_type
+
+ if end != -1 {
+ t = qual_type[:end]
+ }
+
+ t = strings.trim_space(t)
+
+ if t == "void" {
+ return "", false
+ }
+
+ return t, true
+}
+
+find_comment_after_semicolon :: proc(start_offset: int, s: ^Gen_State) -> (side_comment: string, ok: bool) {
+ comment_start: int
+ semicolon_pos: int
+ for i in start_offset..<len(s.source) {
+ if s.source[i] == ';' {
+ semicolon_pos = i
+ }
+
+ if semicolon_pos > 0 && i+2 < len(s.source) {
+ // Comments after proc starting with `//` are not picked up, but
+ // those with `///` are picked up.
+ if s.source[i] == '/' && s.source[i + 1] == '/' && s.source[i + 2] != '/' {
+ comment_start = i
+ }
+ }
+
+ if s.source[i] == '\n' {
+ if comment_start != 0 {
+ side_comment = s.source[comment_start:i]
+ ok = true
+ }
+ return
+ }
+ }
+ return
+}
+
+// used to get comments to the right of macros
+find_comment_at_line_end :: proc(str: string) -> (string, int) {
+ spaces_counter := 0
+ for c, i in str {
+ if c == ' ' {
+ spaces_counter += 1
+ } else if c == '/' && i + 1 < len(str) && (str[i + 1] == '/' || str[i + 1] == '*') {
+ return str[i:], spaces_counter
+ } else {
+ spaces_counter = 0
+ }
+ }
+
+ return "", 0
+}
+
+parse_struct_decl :: proc(s: ^Gen_State, decl: json.Value) -> (res: Struct, ok: bool) {
+ out_fields: [dynamic]Struct_Field
+ comment: string
+
+ if inner, fields_ok := json_get_array(decl, "inner"); fields_ok {
+ anonymous_struct_types: [dynamic]json.Object
+ for &i in inner {
+ i_kind := json_get_string(i, "kind") or_continue
+ if i_kind == "RecordDecl" {
+ append(&anonymous_struct_types, i.(json.Object))
+ }
+ }
+
+ prev_line := 0
+ prev_idx := -1
+ for &i in inner {
+ if loc, loc_ok := json_get_object(i, "loc"); loc_ok {
+ if lline, lline_ok := json_get_int(loc, "line"); lline_ok {
+ prev_line = lline
+ }
+ }
+
+ i_kind := json_get_string(i, "kind") or_continue
+ if i_kind == "FieldDecl" {
+ field_name, field_name_exists := json_get_string(i, "name")
+ field_type := get_parameter_type(s, i) or_continue
+ field_anon_struct_type: Maybe(Struct)
+
+ is_implicit := json_check_bool(i, "isImplicit")
+
+
+ has_unnamed_type: bool
+ unnamed_type_line: int
+ unnamed_type_col: int
+ anon_using: bool
+
+
+ ANON_STRUCT_MARKER :: "struct (unnamed struct at "
+ ANON_UNION_MARKER :: "union (unnamed union at "
+
+ is_anon_struct := strings.has_prefix(field_type, ANON_STRUCT_MARKER)
+ is_anon_union := strings.has_prefix(field_type, ANON_UNION_MARKER)
+
+ if is_anon_struct || is_anon_union {
+ location := is_anon_struct ? field_type[len(ANON_STRUCT_MARKER):len(field_type)-1] : field_type[len(ANON_UNION_MARKER):len(field_type)-1]
+
+ loc_parts := strings.split(location, ":")
+ assert(len(loc_parts) == 3)
+
+ has_unnamed_type = true
+ unnamed_type_line = strconv.atoi(loc_parts[1])
+ unnamed_type_col = strconv.atoi(loc_parts[2])
+ } else if is_implicit {
+ LOC_START_MARKER :: "anonymous at "
+ loc_start := strings.index(field_type, LOC_START_MARKER)
+
+ if loc_start != -1 {
+ location := field_type[loc_start + len(LOC_START_MARKER):len(field_type)-1]
+ loc_parts := strings.split(location, ":")
+ assert(len(loc_parts) == 3)
+
+ anon_using = true
+ has_unnamed_type = true
+ unnamed_type_line = strconv.atoi(loc_parts[1])
+ unnamed_type_col = strconv.atoi(loc_parts[2])
+ }
+ }
+
+ if has_unnamed_type {
+ for a in anonymous_struct_types {
+ aloc := json_get(a, "loc", json.Object) or_continue
+ aline := json_get(aloc, "line", json.Integer) or_else i64(prev_line)
+ acol := json_get(aloc, "col", json.Integer) or_continue
+
+ if unnamed_type_line == int(aline) && unnamed_type_col == int(acol) {
+ if anon_struct, anon_struct_ok := parse_struct_decl(s, a); anon_struct_ok {
+ field_anon_struct_type = anon_struct
+ }
+ }
+ }
+ }
+
+ field_comment: string
+ field_comment_before: bool
+ field_line, field_line_ok := json_get_int(i, "loc.line")
+
+ if field_inner, field_inner_ok := json_get_array(i, "inner"); field_inner_ok {
+ for &fi in field_inner {
+ fi_kind := json_get_string(fi, "kind") or_continue
+
+ if fi_kind == "FullComment" {
+ com, com_line, com_line_ok, comment_ok := get_comment_with_line(fi, s)
+
+ if comment_ok {
+ field_comment = com
+
+ if com_line_ok {
+ field_comment_before = !field_line_ok || com_line < int(field_line)
+ }
+ }
+ }
+ }
+ }
+
+ merge: bool
+ if field_name_exists && prev_idx != -1 {
+ prev := &out_fields[prev_idx]
+
+ if field_line_ok {
+ if prev.original_line == field_line && prev.type == field_type {
+ merge = true
+ }
+ } else if prev.type == field_type {
+ merge = true
+ }
+ }
+
+ if merge {
+ assert(prev_idx != -1, "Merge requested by prev_idx == -1")
+ prev := &out_fields[prev_idx]
+ append(&prev.names, field_name)
+ } else {
+ prev_idx = len(out_fields)
+ f := Struct_Field {
+ type = field_type,
+ anon_struct_type = field_anon_struct_type,
+ anon_using = anon_using,
+ comment = field_comment,
+ comment_before = field_comment_before,
+ original_line = field_line,
+ }
+
+ if field_name_exists {
+ append(&f.names, field_name)
+ }
+
+ append(&out_fields, f)
+
+ if is_c_type(field_type) {
+ s.needs_import_c = true
+ } else if is_libc_type(field_type) {
+ s.needs_import_libc = true
+ } else if is_posix_type(field_type) {
+ s.needs_import_posix = true
+ }
+ }
+ } else if i_kind == "FullComment" {
+ comment, _ = get_comment(i, s)
+ }
+ }
+ }
+
+ res = {
+ comment = comment,
+ fields = out_fields[:],
+ is_union = (json_get_string(decl, "tagUsed") or_else "") == "union",
+ is_forward_declare = !json_check_bool(decl, "completeDefinition"),
+ }
+
+ ok = true
+
+ return
+}
+
+Macro_Type :: enum {
+ Valueless,
+ Constant_Expression,
+ Multivalue,
+ Function,
+}
+
+Macro_Token :: struct {
+ type: Macro_Type,
+ name: string,
+ values: []string,
+}
+
+trim_encapsulating_parens :: proc(s: string) -> string {
+ str := strings.trim_space(s)
+ // There could be an arbitrary number of parentheses inside the string so we repeat until we're sure we've removed all the outer ones.
+ for str[0] == '(' && str[len(str) - 1] == ')' {
+ parens := 1
+ i := 1
+ // It's important to make sure that the parens at the begining and end are a pair.
+ // For example without the check a cast statement like `(type)(value)` would have its outer parens removed.
+ for ; parens > 0 && i < len(str); i += 1 {
+ if str[i] == '(' {
+ parens += 1
+ } else if str[i] == ')' {
+ parens -= 1
+ }
+ }
+ // If parens is 0 before reaching the end of the string then the starting parenthesis doesn't pair with the ending one.
+ // If parens doesn't reach 0 then we have unbalanced parenthesis. Maybe we should error here but I'm just going to ignore it for now.
+ // It's not our responsibility to ensure the code is valid C. Also clang should have produced an error if there was a syntax error.
+ if i == len(str) && parens == 0 {
+ str = strings.trim_space(str[1: len(str) - 1])
+ } else {
+ break
+ }
+ }
+ return str
+}
+
+parse_value :: proc(s: string) -> (r: []string, type: Macro_Type = .Constant_Expression) {
+ str := strings.trim_space(s)
+
+ ret: [dynamic]string
+ grouping_delimiter := 0
+ tracker := 0
+ for i := 0; i < len(str); i += 1 {
+ switch str[i] {
+ // We track grouping delimiters so we can ignore commas and spaces inside them such as {10, 20, 30}.
+ case '(', '{', '[':
+ grouping_delimiter += 1
+ case ')', '}', ']':
+ grouping_delimiter -= 1
+ case ',':
+ if grouping_delimiter == 0 {
+ tmp := strings.trim_space(s[tracker:i])
+ if len(tmp) > 0 {
+ append(&ret, tmp)
+ }
+ tracker = i + 1
+ type = .Multivalue
+ }
+ case ' ':
+ if grouping_delimiter == 0 {
+ tmp := strings.trim_space(s[tracker:i])
+ if len(tmp) > 0 {
+ append(&ret, tmp)
+ }
+ tracker = i + 1
+ }
+ case '"':
+ // We need to find the end of the string. We can't just use `strings.index` because the string can contain escaped quotes.
+ for i < len(str) {
+ i += 1
+ if str[i] == '"' {
+ break
+ } else if str[i] == '\\' {
+ i += 1 // Skip the escaped character
+ }
+ }
+ if grouping_delimiter == 0 {
+ tmp := strings.trim_space(str[tracker:i+1])
+ if len(tmp) > 0 {
+ append(&ret, tmp)
+ }
+ tracker = i + 1
+ }
+ }
+ }
+ tmp := strings.trim_space(s[tracker:])
+ if len(tmp) > 0 {
+ append(&ret, tmp)
+ }
+
+ return ret[:], type
+}
+
+char_type :: proc(c: u8) -> enum {
+ Char,
+ Num,
+ Quote,
+ Other,
+} {
+ if (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' {
+ return .Char
+ } else if (c >= '0' && c <= '9') || c == '.' { // Assume '.' is decimal point
+ return .Num
+ } else if c == '"' {
+ return .Quote
+ }
+ return .Other
+}
+
+parse_macro :: proc(s: string) -> (macro_token: Macro_Token) {
+ line := strings.trim_space(s)
+ i := 0
+ for; i < len(line); i += 1 {
+ switch line[i] {
+ case '(': // Function-like macro
+ macro_token.type = .Function
+ macro_token.name = line[:i]
+
+ fn_end := strings.index(line, ")") // fn macros can only have one `(` and `)`
+ params := strings.split(line[i + 1:fn_end], ",")
+
+ for ¶m in params {
+ param = strings.trim_space(param)
+ }
+
+ parse_number :: proc(str: string, start: u32, b: ^strings.Builder, names: []string) -> u32 {
+ end := start + 1
+ for; end < u32(len(str)); end += 1 {
+ if char_type(str[end]) == .Other { // Assume chars are a type suffix or 'b'/'x' for binary and hex (We'll validate these later)
+ break
+ }
+ }
+ strings.write_string(b, str[start:end])
+ return end
+ }
+
+ parse_name :: proc(str: string, start: u32, b: ^strings.Builder, names: []string) -> u32 {
+ end := start + 1
+ for; end < u32(len(str)); end += 1 {
+ if char_type(str[end]) == .Other {
+ break
+ }
+ }
+
+ for name, i in names {
+ if str[start:end] == name {
+ // This will allow me to index the parameter with the number of the parameter similar to pythons "{0}" formatting.
+ // We can't copy python syntax exactly here because C uses {} for initializers and we don't want to confuse the two.
+ strings.write_string(b, "${")
+ strings.write_int(b, i)
+ strings.write_string(b, "}$")
+ return end
+ }
+ }
+ strings.write_string(b, str[start:end])
+ return end
+ }
+
+ b := strings.builder_make()
+ value := strings.trim_space(line[fn_end+1:])
+
+ // We go throught the string and replace all the parameters with `${index}$`.
+ for i: u32 = 0; i < u32(len(value)); i += 1 {
+ if char_type(value[i]) == .Num {
+ i = parse_number(value, i, &b, params) - 1
+ } else if char_type(value[i]) == .Char {
+ i = parse_name(value, i, &b, params) - 1
+ } else {
+ strings.write_byte(&b, value[i])
+ }
+ }
+ macro_token.values = make([]string, 1)
+ macro_token.values[0] = strings.to_string(b)
+ return
+ case ' ': // Non function-like macro
+ macro_token.name = line[:i]
+ macro_token.values, macro_token.type = parse_value(trim_encapsulating_parens(line[i:]))
+ return
+ }
+ }
+ macro_token = {
+ name = line,
+ type = .Valueless,
+ }
+ return
+}
+
+File_Macro :: struct {
+ line: int,
+ macro_name: string,
+ comment: string,
+ side_comment: string,
+ whitespace_after_name: int,
+ whitespace_before_side_comment: int,
+}
+
+// Parses the file and finds all the macros that are defined in it.
+parse_file_macros :: proc(s: ^Gen_State) -> map[string]File_Macro {
+ defined := make(map[string]File_Macro)
+
+ file_lines := strings.split_lines(s.source)
+ for i := 0; i < len(file_lines); i += 1 {
+ line_idx := i
+ line := strings.trim_space(file_lines[i])
+
+ if len(line) == 0 { // Don't parse empty lines
+ continue
+ }
+
+ for line[len(line)-1] == '\\' { // Backaslash means to treat the next line as part of this line
+ i += 1
+ line = fmt.tprintf("%v %v", strings.trim_space(line[:len(line)-1]), strings.trim_space(file_lines[i]))
+ }
+
+ if strings.has_prefix(line, "#define") { // #define macroName keyValue
+ l := strings.trim_prefix(line, "#define")
+ l = strings.trim_space(l)
+
+ end_of_name := strings.index(l, " ")
+
+ if end_of_name == -1 {
+ end_of_name = strings.index(l, "\t")
+ }
+
+ // Macro parameter list start
+ first_left_paren := strings.index(l, "(")
+
+ if first_left_paren != -1 && first_left_paren < end_of_name {
+ end_of_name = first_left_paren
+ }
+
+ if end_of_name == -1 {
+ continue
+ }
+
+ name := l[:end_of_name]
+
+ if name in defined {
+ continue
+ }
+
+ whitespace_after_name := 0
+
+ for c in l[end_of_name:] {
+ if c == ' ' {
+ whitespace_after_name += 1
+ } else {
+ break
+ }
+ }
+
+ side_comment, side_comment_align_whitespace := find_comment_at_line_end(line)
+
+ cbidx := i - 1
+ cb_block_comment := false
+ comment_start := -1
+ comment_end := -1
+
+ for cbidx >= 0 {
+ cbl := file_lines[cbidx]
+ cbl_trim := strings.trim_space(cbl)
+
+ if strings.has_prefix(cbl_trim, "/*") && cb_block_comment {
+ // TODO: this doesn't account for the case of a multiline block comment that begins at the end of a non-comment line
+ comment_start = cbidx
+ break
+ } else if cb_block_comment {
+ // block comment interior, continue
+ } else if strings.has_suffix(cbl_trim, "*/") {
+ if comment_end == -1 {
+ comment_end = cbidx
+ }
+ cb_block_comment = true
+ if strings.has_prefix(cbl_trim, "/*") {
+ // block comment starts on same line
+ cb_block_comment = false
+ comment_start = cbidx
+ break
+ } else if strings.contains(cbl_trim, "/*") {
+ // this is actually the side comment for another line, discard comment and break
+ cb_block_comment = false
+ break
+ }
+ } else if strings.has_prefix(cbl_trim, "//") {
+ if comment_end == -1 {
+ comment_end = cbidx
+ }
+
+ comment_start = cbidx
+ } else if cbl_trim != "" {
+ break
+ }
+
+ cbidx -= 1
+ }
+
+ comment: string
+
+ if comment_start != -1 && comment_end != -1 {
+ comment_builder := strings.builder_make()
+
+ for comment_line_idx in comment_start..=comment_end {
+ strings.write_string(&comment_builder, file_lines[comment_line_idx])
+ strings.write_rune(&comment_builder, '\n')
+ }
+
+ comment = strings.to_string(comment_builder)
+ }
+
+ defined[name] = File_Macro {
+ macro_name = name,
+ line = line_idx,
+ comment = comment,
+ side_comment = side_comment,
+ whitespace_before_side_comment = side_comment_align_whitespace,
+ whitespace_after_name = whitespace_after_name,
+ }
+ }
+ }
+
+ return defined
+}
+
+// This function runs clangs preprocessor to get all the macros that are defined during compilation
+parse_clang_macros :: proc(s: ^Gen_State, input: string) -> (map[string]Macro_Token) {
+ command := [dynamic]string {
+ "clang", "-dM", "-E", input,
+ }
+
+ for include in s.clang_include_paths {
+ append(&command, fmt.tprintf("-I%v", include))
+ }
+
+ for k, v in s.clang_defines {
+ append(&command, fmt.tprintf("-D%v=%v", k, v))
+ }
+
+ process_desc := os2.Process_Desc {
+ command = command[:],
+ }
+
+ state, sout, serr, err := os2.process_exec(process_desc, context.allocator)
+
+ if err != nil {
+ fmt.panicf("Error generating macro dump. Error: %v", err)
+ }
+
+ if len(serr) > 0 {
+ fmt.eprintln(string(serr))
+ fmt.eprintfln("Aborting generation for %v", input)
+ return nil
+ }
+
+ ensure(state.success, "Failed running clang")
+
+ input_filename := filepath.base(input)
+ output_stem := filepath.stem(input_filename)
+ output_filename := fmt.tprintf("%v/%v.odin", s.output_folder, output_stem)
+
+ if s.debug_dump_macros {
+ os.write_entire_file(fmt.tprintf("%v-macro_dump.h", output_filename), sout)
+ }
+
+ tokenized_macros: map[string]Macro_Token
+ macro_lines := strings.split_lines(string(sout))
+
+ for i := 0; i < len(macro_lines); i += 1 {
+ line := strings.trim_space(macro_lines[i])
+
+ if len(line) == 0 { // Don't parse empty lines
+ continue
+ }
+
+ for line[len(line)-1] == '\\' { // Backaslash means to treat the next line as part of this line
+ i += 1
+ line = fmt.tprintf("%v %v", strings.trim_space(line[:len(line)-1]), strings.trim_space(macro_lines[i]))
+ }
+
+ if strings.has_prefix(line, "#define") {
+ token := parse_macro(line[len("#define "):])
+ if token.type == .Valueless {
+ continue // We don't care about valueless macros
+ }
+ tokenized_macros[token.name] = token
+ }
+ }
+
+ return tokenized_macros
+}
+
+parse_pystring :: proc(s: string, params: []string) -> string {
+ b := strings.builder_make(context.temp_allocator)
+ index := 0
+ for i := strings.index(s[index:], "${"); i != -1; i = strings.index(s[index:], "${") {
+ start_brace := i + index
+ end_brace := strings.index(s[start_brace:], "}$")
+ if end_brace == -1 {
+ break // No closing brace found. The macro is malformed.
+ }
+ end_brace += start_brace
+ param_index := strconv.atoi(s[start_brace+2:end_brace])
+ if param_index < 0 || param_index >= len(params) {
+ break // Invalid parameter index
+ }
+
+ strings.write_string(&b, s[index:index+i])
+ strings.write_string(&b, params[param_index])
+ index = end_brace + 2
+ }
+ strings.write_string(&b, s[index:])
+ return strings.to_string(b)
+}
+
+parse_macros :: proc(s: ^Gen_State, input: string) {
+ // First we find all macros in the file, then we also fetch them through clang, so they
+ // respect the preprocesor defines etc.
+ defined_from_file := parse_file_macros(s)
+ tokenized_macros := parse_clang_macros(s, input)
+
+ expand_fn_macro :: proc(value: ^string, name_start, name_end: int, macro_token: Macro_Token, macros: ^map[string]Macro_Token) -> string {
+ params_start := name_end
+ for ; params_start < len(value); params_start += 1 { // Finds first parenthesis after the macro name
+ if value[params_start] == '(' {
+ break
+ } else if value[params_start] != ' ' {
+ return value^
+ }
+ }
+ if params_start == len(value) {
+ return value^
+ }
+
+ params_start += 1
+ params_end := params_start
+ parens := 1
+ for ; parens > 0 && params_end < len(value); params_end += 1 {
+ if value[params_end] == '(' {
+ parens += 1
+ } else if value[params_end] == ')' {
+ parens -= 1
+ }
+ }
+ if parens != 0 {
+ return value^
+ }
+ params_end -= 1
+
+ params, _ := parse_value(value[params_start:params_end])
+ for param_index := 0; param_index < len(params); param_index += 1 {
+ // If our param contains a macro we need to expand it first.
+ strs := check_param_for_macro_and_expand(¶ms[param_index], macros)
+ if len(strs) > 0 {
+ new_params := make([]string, len(params) + len(strs) - 1)
+ for i := 0; i < param_index; i += 1 {
+ new_params[i] = params[i]
+ }
+ for i := 0; i < len(strs); i += 1 {
+ new_params[param_index + i] = strs[i]
+ }
+ for i := 0; i < len(params) - param_index - 1; i += 1 {
+ new_params[param_index + len(strs) + i] = params[param_index + i + 1]
+ }
+ params = new_params // It might not be safe to do this while looping over the array.
+ }
+ }
+
+ ret := fmt.tprintf("%s%s", value[:name_start], parse_pystring(macro_token.values[0], params))
+ if params_end + 1 < len(value) {
+ ret = fmt.tprintf("%s%s", ret, value[params_end + 1:])
+ }
+ return ret
+ }
+
+ check_for_macro :: proc(value: ^string, macros: ^map[string]Macro_Token) -> (int, int) {
+ for i := 0; i < len(value); i += 1 {
+ if char_type(value[i]) != .Char {
+ continue
+ }
+
+ name_start := i
+ for ; i < len(value); i += 1 {
+ if char_type(value[i]) == .Other {
+ break
+ }
+ }
+
+ if _, exists := macros[value[name_start:i]]; exists {
+ return name_start, i
+ }
+ }
+ return -1, -1
+ }
+
+ check_param_for_macro_and_expand :: proc(value: ^string, macros: ^map[string]Macro_Token) -> []string {
+ name_start, name_end := check_for_macro(value, macros)
+ if name_start == -1 {
+ return nil
+ }
+ if macro, _ := macros[value[name_start:name_end]]; macro.type == .Function {
+ value^ = expand_fn_macro(value, name_start, name_end, macro, macros)
+ } else if macro.type == .Multivalue {
+ return macro.values
+ }
+ return nil
+ }
+
+ check_value_for_macro_and_expand :: proc(value: ^string, macros: ^map[string]Macro_Token) {
+ name_start, name_end := check_for_macro(value, macros)
+ if name_start == -1 {
+ return
+ }
+
+ macro_name := value[name_start:name_end]
+ if macro_token, _ := macros[macro_name]; macro_token.type == .Function {
+ value^ = expand_fn_macro(value, name_start, name_end, macro_token, macros)
+ if value^ == macro_name {
+ return
+ }
+ check_value_for_macro_and_expand(value, macros)
+ } else if macro_token.type == .Multivalue {
+ tmp := fmt.tprintf("%s%s", value[:name_start], strings.join(macro_token.values, ", ", context.temp_allocator))
+ if name_end < len(value) {
+ tmp = fmt.tprintf("%s%s", tmp, value[name_end:])
+ }
+ value^ = tmp
+ check_value_for_macro_and_expand(value, macros)
+ }
+ }
+
+ for _, ¯o in tokenized_macros {
+ if macro.type != .Constant_Expression {
+ continue
+ }
+
+ // I'm not a fan of this way of checking if the macro is defined in the file. Feel free to suggest better ways to do this.
+ file_macro, defined := defined_from_file[macro.name]
+
+ if !defined {
+ continue
+ }
+
+ for &value in macro.values {
+ check_value_for_macro_and_expand(&value, &tokenized_macros)
+ }
+
+ append(&s.decls, Declaration {
+ line = file_macro.line,
+ original_idx = len(s.decls),
+ variant = Macro {
+ original_name = macro.name,
+ val = strings.join(macro.values, " "),
+ comment = file_macro.comment,
+ side_comment = file_macro.side_comment,
+ whitespace_after_name = file_macro.whitespace_after_name,
+ whitespace_before_side_comment = file_macro.whitespace_before_side_comment,
+ },
+ })
+ }
+}
+
+parse_decl :: proc(s: ^Gen_State, decl: json.Value, line: int) {
+ if json_has(decl, "loc.includedFrom") {
+ return
+ }
+
+ if json_has(decl, "loc.expansionLoc.includedFrom") {
+ return
+ }
+
+ if json_check_bool(decl, "isImplicit") {
+ return
+ }
+
+ kind, kind_ok := json_get_string(decl, "kind")
+
+ if !kind_ok {
+ return
+ }
+
+ id, id_ok := json_get_string(decl, "id")
+
+ if !id_ok {
+ return
+ }
+
+ if kind == "FunctionDecl" {
+ name, name_ok := json_get_string(decl, "name")
+
+ if !name_ok {
+ return
+ }
+
+ _, line_ok := json_get_int(decl, "loc.line")
+
+ if !line_ok {
+ return
+ }
+
+ return_type, has_return_type := get_return_type(decl)
+ if has_return_type {
+ if is_c_type(return_type) {
+ s.needs_import_c = true
+ } else if is_libc_type(return_type) {
+ s.needs_import_libc = true
+ } else if is_posix_type(return_type) {
+ s.needs_import_posix = true
+ }
+ }
+
+ out_params: [dynamic]Function_Parameter
+ comment: string
+ comment_before: bool
+
+ if params, params_ok := json_get_array(decl, "inner"); params_ok {
+ for &p in params {
+ pkind := json_get_string(p, "kind") or_continue
+
+ if pkind == "ParmVarDecl" {
+ // Empty name is OK. It's an unnamed parameter.
+ param_name, _ := json_get_string(p, "name")
+ param_type := get_parameter_type(s, p) or_continue
+ append(&out_params, Function_Parameter {
+ name = param_name,
+ type = param_type,
+ })
+ } else if pkind == "FullComment" {
+ com, com_line, com_line_ok, comment_ok := get_comment_with_line(p, s)
+
+ if comment_ok {
+ comment = com
+
+ if com_line_ok {
+ comment_before = com_line < int(line)
+ }
+ }
+ }
+ }
+ }
+
+ side_comment: string
+
+ if end_offset, end_offset_ok := json_get_int(decl, "range.end.offset"); end_offset_ok {
+ side_comment, _ = find_comment_after_semicolon(end_offset, s)
+ }
+ append(&s.decls, Declaration {
+ line = line,
+ original_idx = len(s.decls),
+ variant = Function {
+ original_name = name,
+ parameters = out_params[:],
+ return_type = has_return_type ? return_type : "",
+ comment = comment,
+ comment_before = comment_before,
+ post_comment = side_comment,
+ variadic = json_check_bool(decl, "variadic"),
+ },
+ })
+ } else if kind == "RecordDecl" {
+ name, _ := json_get_string(decl, "name")
+
+ if struct_decl, struct_decl_ok := parse_struct_decl(s, decl); struct_decl_ok {
+ struct_decl.original_name = name
+ struct_decl.id = id
+
+ if name != "" {
+ if forward_idx, forward_declared := s.symbol_indices[name]; forward_declared {
+ s.decls[forward_idx] = {}
+ }
+
+ s.symbol_indices[name] = len(s.decls)
+ }
+ append(&s.decls, Declaration { line = line, original_idx = len(s.decls), variant = struct_decl })
+ }
+ } else if kind == "TypedefDecl" {
+ type, type_ok := get_parameter_type(s, decl)
+
+ if !type_ok {
+ return
+ }
+
+ name, _ := json_get_string(decl, "name")
+ _, line_ok := json_get_int(decl, "loc.line")
+ pre_comment: string
+ side_comment: string
+
+ if typedef_inner, typedef_inner_ok := json_get_array(decl, "inner"); typedef_inner_ok {
+ for &i in typedef_inner {
+ inner_kind := json_get_string(i, "kind") or_continue
+
+ if inner_kind == "ElaboratedType" {
+ if typedeffed_id, typedeffed_id_ok := json_get_string(i, "ownedTagDecl.id"); typedeffed_id_ok {
+ type = typedeffed_id
+ }
+ } else if inner_kind == "FullComment" {
+ comment, comment_line, comment_line_ok, comment_ok := get_comment_with_line(i, s)
+
+ if comment_ok {
+ if comment_line_ok && line_ok && comment_line >= line {
+ side_comment = comment
+ } else {
+ pre_comment = comment
+ }
+ }
+ }
+ }
+ }
+
+ if end_offset, end_offset_ok := json_get_int(decl, "range.end.offset"); end_offset_ok {
+ side_comment, _ = find_comment_after_semicolon(end_offset, s)
+ }
+
+ s.typedefs[type] = name
+ append(&s.decls, Declaration {
+ line = line,
+ original_idx = len(s.decls),
+ variant = Typedef {
+ original_name = name,
+ type = type,
+ pre_comment = pre_comment,
+ side_comment = side_comment,
+ },
+ })
+ } else if kind == "EnumDecl" {
+ name, _ := json_get_string(decl, "name")
+ comment: string
+ out_members: [dynamic]Enum_Member
+
+ s.needs_import_c = true // enums all use c.int
+
+ if inner, inner_ok := json_get_array(decl, "inner"); inner_ok {
+ for &m in inner {
+ inner_kind := json_get_string(m, "kind") or_continue
+
+ if inner_kind == "EnumConstantDecl" {
+ member_name := json_get_string(m, "name") or_continue
+ member_value: Maybe(int)
+ member_comment: string
+ member_comment_before: bool
+ member_line, member_line_ok := json_get_int(m, "loc.line")
+
+ if values, values_ok := json_get_array(m, "inner"); values_ok {
+ for &vv in values {
+ value_kind := json_get_string(vv, "kind") or_continue
+
+ if value_kind == "ConstantExpr" {
+ value := json_get_string(vv, "value") or_continue
+ member_value = strconv.atoi(value)
+ } else if value_kind == "FullComment" {
+ com, com_line, com_line_ok, comment_ok := get_comment_with_line(vv, s)
+
+ if comment_ok {
+ member_comment = com
+
+ if com_line_ok && member_line_ok {
+ member_comment_before = com_line < int(member_line)
+ }
+ }
+ }
+ }
+ }
+
+ append(&out_members, Enum_Member {
+ name = member_name,
+ value = member_value,
+ comment = member_comment,
+ comment_before = member_comment_before,
+ })
+ } else if inner_kind == "FullComment" {
+ comment, _ = get_comment(m, s)
+ }
+ }
+ }
+
+ append(&s.decls, Declaration {
+ line = line,
+ original_idx = len(s.decls),
+ variant = Enum {
+ original_name = name,
+ id = id,
+ comment = comment,
+ members = out_members[:],
+ },
+ })
+ }
+}
+
+get_comment_with_line :: proc(v: json.Value, s: ^Gen_State) -> (comment: string, line: int, line_ok: bool, ok: bool) {
+ comment, ok = get_comment(v, s)
+ if line_i64, line_i64_ok := json_get(v, "loc.line", json.Integer); line_i64_ok {
+ line = int(line_i64)
+ line_ok = true
+ }
+ return
+}
+
+get_comment :: proc(v: json.Value, s: ^Gen_State) -> (comment: string, ok: bool) {
+ begin := int(json_get(v, "range.begin.offset", json.Integer) or_return)
+ end := int(json_get(v, "range.end.offset", json.Integer) or_return)
+
+ // This makes sure to add in the starting `//` and any ending `*/` that clang
+ // might not have included in the comment.
+
+ double_slash_found := false
+
+ for idx := int(begin); idx >= 0; idx -= 1 {
+ if idx + 2 >= len(s.source) {
+ continue
+ }
+
+ cur := s.source[idx:idx+2]
+ if cur == "//" {
+ begin = idx
+ double_slash_found = true
+ }
+
+ if cur == "/*" {
+ begin = idx
+ break
+ }
+
+ if s.source[idx] == '\n' && double_slash_found {
+ break
+ }
+ }
+
+ cmt := s.source[begin:end+1]
+
+ num_block_openings := strings.count(cmt, "/*")
+ num_block_closing := strings.count(cmt, "*/")
+
+ if num_block_openings != num_block_closing {
+ for idx in end..<len(s.source) - 2 {
+ cur := s.source[idx:idx+2]
+
+ if cur == "*/" {
+ end = idx+1
+ break
+ }
+ }
+ }
+
+ return s.source[begin:end+1], true
+}
+
+trim_prefix :: proc(s: string, p: string) -> string {
+ return strings.trim_prefix(strings.trim_prefix(s, p), "_")
+}
+
+// Types that would need `import "core:c/libc"`. Please add and send in a Pull Request if you needed
+// to add anything here!
+is_libc_type :: proc(t: string) -> bool{
+ base_type := strings.trim_suffix(t, "*")
+ base_type = strings.trim_space(base_type)
+
+ switch t {
+ case "time_t":
+ return true
+ }
+
+ return false
+}
+
+// Types that would need "import 'core:sys/posix'". Please add and send in a Pull Request if you
+// needed to add anything here!
+is_posix_type :: proc(t:string) -> bool {
+ base_type := strings.trim_suffix(t,"*")
+ base_type = strings.trim_space(base_type)
+ switch t {
+ case "dev_t" : return true
+ case "blkcnt_t": return true
+ case "blksize_t" : return true
+ case "clock_t" : return true
+ case "clockid_t": return true
+ case "fsblkcnt_t" : return true
+ case "off_t" : return true
+ case "gid_t": return true
+ case "pid_t": return true
+ case "timespec": return true
+ }
+ return false
+}
+
+is_c_type :: proc(t: string) -> bool{
+ base_type := strings.trim_suffix(t, "*")
+ base_type = strings.trim_space(base_type)
+ return base_type in c_type_mapping
+}
+
+// This is probably missing some built-in C types (or common types that come
+// from stdint.h etc). Please add and send in a Pull Request if you needed to
+// add anything here!
+c_type_mapping := map[string]string {
+ "char" = "c.char",
+
+ "signed char" = "c.schar",
+ "short" = "c.short",
+ "int" = "c.int",
+ "long" = "c.long",
+ "long long" = "c.longlong",
+
+ "unsigned char" = "c.uchar",
+ "unsigned short" = "c.ushort",
+ "unsigned int" = "c.uint",
+ "unsigned long" = "c.ulong",
+ "unsigned long long" = "c.ulonglong",
+
+ "bool" = "bool",
+ "Bool" = "bool", // I don't know why this needs to have a capital B, but it does.
+ "BOOL" = "bool", // bool is sometimes a macro for BOOL
+ "_Bool" = "bool",
+
+ "size_t" = "c.size_t",
+ "ssize_t" = "c.ssize_t",
+ "wchar_t" = "c.wchar_t",
+
+ "float" = "f32",
+ "double" = "f64",
+ // I think clang changes this to something else so this might not work.
+ // I tried testing it but I couldn't get the complex type working in C.
+ "float complex" = "complex64",
+ "double complex" = "complex128",
+
+ "int8_t" = "i8",
+ "uint8_t" = "u8",
+ "int16_t" = "i16",
+ "uint16_t" = "u16",
+ "int32_t" = "i32",
+ "uint32_t" = "u32",
+ "int64_t" = "i64",
+ "uint64_t" = "u64",
+
+ "int_least8_t" = "i8",
+ "uint_least8_t" = "u8",
+ "int_least16_t" = "i16",
+ "uint_least16_t" = "u16",
+ "int_least32_t" = "i32",
+ "uint_least32_t" = "u32",
+ "int_least64_t" = "i64",
+ "uint_least64_t" = "u64",
+
+ // These type could change base on the platform.
+ "int_fast8_t" = "c.int_fast8_t",
+ "uint_fast8_t" = "c.uint_fast8_t",
+ "int_fast16_t" = "c.int_fast16_t",
+ "uint_fast16_t" = "c.uint_fast16_t",
+ "int_fast32_t" = "c.int_fast32_t",
+ "uint_fast32_t" = "c.uint_fast32_t",
+ "int_fast64_t" = "c.int_fast64_t",
+ "uint_fast64_t" = "c.uint_fast64_t",
+
+ "intptr_t" = "c.intptr_t",
+ "uintptr_t" = "c.uintptr_t",
+ "ptrdiff_t" = "c.ptrdiff_t",
+
+ "intmax_t" = "c.intmax_t",
+ "uintmax_t" = "c.uintmax_t",
+}
+
+// For translating type names in procedure parameters and struct fields.
+translate_type :: proc(s: Gen_State, t: string, override: bool) -> string {
+ t := t
+ t = strings.trim_space(t)
+
+ // Treat as function typedef
+ if strings.contains(t, "(") && strings.contains(t, ")") && !strings.contains(t, ")[") {
+ delimiter := strings.index(t, "(*)(")
+ remainder_start := delimiter + 4
+
+ if delimiter == -1 {
+ delimiter = strings.index(t, "(")
+ remainder_start = delimiter + 1
+ }
+
+ return_type := translate_type(s, t[:delimiter], false)
+
+ func_builder := strings.builder_make()
+
+ strings.write_string(&func_builder, `proc "c" (`)
+
+ // We find the closing parenthesis for the function parameters.
+ // We assume anything after the closing parenthesis is a compiler
+ // attribute or something else that we don't care about.
+ paren_count := 1
+ remainder_end := remainder_start
+ for i := remainder_start; i < len(t); i += 1 {
+ if t[i] == '(' {
+ paren_count += 1
+ } else if t[i] == ')' {
+ paren_count -= 1
+ }
+
+ if paren_count == 0 {
+ remainder_end = i
+ break
+ }
+ }
+
+ if paren_count != 0 {
+ fmt.panicf("Unmatched parentheses in type: %v", t)
+ }
+
+ remainder := t[remainder_start:remainder_end]
+
+ first := true
+
+ for param_type in strings.split_iterator(&remainder, ",") {
+ if first {
+ first = false
+ } else {
+ strings.write_string(&func_builder, ", ")
+ }
+ strings.write_string(&func_builder, translate_type(s, strings.trim_space(param_type), false))
+ }
+
+ if return_type == "void" {
+ strings.write_string(&func_builder, ")")
+ } else {
+ strings.write_string(&func_builder, fmt.tprintf(") -> %v", return_type))
+ }
+
+ return strings.to_string(func_builder)
+ }
+
+ // This type usually means "an array of strings"
+ if t == "const char *const *" || t == "char *const *" || t == "const char **" || t == "char **" {
+ return "[^]cstring"
+ }
+
+ if t == "const char *" || t == "char *" {
+ return "cstring"
+ }
+
+ if t == "va_list" || t == "struct __va_list_tag *" {
+ return "^c.va_list"
+ }
+
+ // Tokenize the type and skip over some parameter type keywords that have no meaning in Odin.
+ type_tokens: [dynamic]string
+ token_start := 0
+ num_ptrs := 0
+
+ for s, idx in t {
+ tok: string
+
+ if strings.is_space(s) {
+ tok = t[token_start:idx]
+ token_start = idx + utf8.rune_size(s)
+ } else if s == '*' {
+ tok = t[token_start:idx]
+ token_start = idx + utf8.rune_size(s)
+ num_ptrs += 1
+ } else if idx == len(t) - 1{
+ tok = t[token_start:idx + 1]
+ }
+
+ if len(tok) > 0 {
+ if tok == "const" {
+ continue
+ }
+
+ if tok == "struct" {
+ continue
+ }
+
+ if tok == "enum" {
+ continue
+ }
+
+ append(&type_tokens, tok)
+ }
+ }
+
+ t = strings.join(type_tokens[:], " ")
+
+ // A hack to check if something is an array of arrays. Then it will appear as `(*)[3] etc. But
+ // the code above removes the `*`, so we check for `( )[`
+ t_original := t
+ multi_array := strings.index(t_original, "( )[")
+ array_start := strings.index(t_original, "[")
+ array_end := strings.last_index(t_original, "]")
+
+ if multi_array != -1 {
+ t = t[:multi_array]
+ } else if array_start != -1 {
+ t = t[:array_start]
+ }
+
+ // check maps against this in case the header has a type which is exactly [prefix][mapped c type]
+ t_prefixed := strings.trim_space(t)
+ if t != s.remove_type_prefix {
+ t = trim_prefix(t, s.remove_type_prefix)
+ }
+
+ t = strings.trim_space(t)
+
+ if is_c_type(t_prefixed) {
+ t = c_type_mapping[t]
+ } else if is_libc_type(t_prefixed) {
+ t = fmt.tprintf("libc.%v", t)
+ } else if is_posix_type(t_prefixed) {
+ t = fmt.tprintf("posix.%v",t)
+ } else if rename, exists := s.rename[t_prefixed]; exists {
+ t = vet_name(rename)
+ } else if s.force_ada_case_types && t != "void" {
+ // It makes sense, in the case we can't find the type, to just follow our naming rules and
+ // hope the type is defined somewhere else.
+ t = vet_name(strings.to_ada_case(t))
+ } else {
+ t = vet_name(t)
+ }
+
+ b := strings.builder_make()
+
+ if array_start != -1 {
+ if multi_array != -1 {
+ strings.write_string(&b, "[^]")
+ }
+ strings.write_string(&b, t_original[array_start:array_end + 1])
+ }
+
+ if num_ptrs > 0 {
+ if t == "void" {
+ t = "rawptr"
+ num_ptrs -= 1
+ }
+
+ if t in s.type_is_proc {
+ num_ptrs -= 1
+ }
+
+ if multi_array != -1 {
+ num_ptrs -= 1
+ }
+ }
+
+ if num_ptrs > 0 && override {
+ strings.write_string(&b, "[^]")
+ num_ptrs -= 1
+ }
+
+ for num_ptrs > 0 {
+ strings.write_string(&b, "^")
+ num_ptrs -= 1
+ }
+
+ strings.write_string(&b, t)
+ return strings.to_string(b)
+}
+
+// Keywords in Odin that don't exist in C. The `_` is there so we can return it
+// without allocating memory (we compare to the slice [1:])
+VET_NAMES :: [?]string {
+ "_rune",
+ "_import",
+ "_foreign",
+ "_package",
+ "_typeid",
+ "_when",
+ "_where",
+ "_in",
+ "_not_in",
+ "_fallthrough",
+ "_defer",
+ "_proc",
+ "_bit_set",
+ "_bit_field",
+ "_map",
+ "_dynamic",
+ "_auto_cast",
+ "_cast",
+ "_transmute",
+ "_distinct",
+ "_using",
+ "_context",
+ "_or_else",
+ "_or_return",
+ "_or_break",
+ "_or_continue",
+ "_asm",
+ "_inline",
+ "_no_inline",
+ "_matrix",
+ "_string",
+
+ // Because we import these three
+ "_c",
+ "_libc",
+ "_posix",
+}
+
+vet_name :: proc(s: string) -> string {
+ for v in VET_NAMES {
+ if s == v[1:] {
+ return v
+ }
+ }
+
+ return s
+}
+
+add_to_set :: proc(s: ^map[$T]struct{}, v: T) {
+ s[v] = {}
+}
+
+fp :: fmt.fprint
+fpln :: fmt.fprintln
+fpf :: fmt.fprintf
+fpfln :: fmt.fprintfln
+
+Config :: struct {
+ inputs: []string,
+ ignore_inputs: []string,
+ output_folder: string,
+ package_name: string,
+ required_prefix: string,
+
+ // deprecated: use remove_xxx_prefix
+ remove_prefix: string,
+
+ remove_type_prefix: string,
+ remove_function_prefix: string,
+ remove_macro_prefix: string,
+ import_lib: string,
+ imports_file: string,
+ clang_include_paths: []string,
+ clang_defines: map[string]string,
+ force_ada_case_types: bool,
+ debug_dump_json_ast: bool,
+ debug_dump_macros: bool,
+
+ opaque_types: []string,
+ rename: map[string]string,
+
+ // deprecated: use rename
+ rename_types: map[string]string,
+
+ type_overrides: map[string]string,
+ struct_field_overrides: map[string]string,
+ procedure_type_overrides: map[string]string,
+ bit_setify: map[string]string,
+ inject_before: map[string]string,
+}
+
+Gen_State :: struct {
+ using config: Config,
+
+ source: string,
+ decls: [dynamic]Declaration,
+ defines: map[string]string,
+ symbol_indices: map[string]int,
+ typedefs: map[string]string,
+ created_symbols: map[string]struct{},
+ type_is_proc: map[string]struct{},
+ opaque_type_lookup: map[string]struct{},
+ created_types: map[string]struct{},
+ needs_import_c: bool,
+ needs_import_libc: bool,
+ needs_import_posix: bool,
+}
+
+gen :: proc(input: string, c: Config) {
+ // Everything allocated within this call to `gen` is allocated on a single
+ // arena, which is destroyed when this procedure ends.
+
+ gen_arena: vmem.Arena
+ defer vmem.arena_destroy(&gen_arena)
+ context.allocator = vmem.arena_allocator(&gen_arena)
+ context.temp_allocator = vmem.arena_allocator(&gen_arena)
+
+ s := Gen_State {
+ config = c,
+ }
+
+ for ot in c.opaque_types {
+ // For quick lookup
+ add_to_set(&s.opaque_type_lookup, ot)
+ }
+
+ //
+ // Run clang and produce an AST in json format that describes the headers.
+ //
+
+ command := [dynamic]string {
+ "clang", "-Xclang", "-ast-dump=json", "-fparse-all-comments", "-c", input,
+ }
+
+ for include in c.clang_include_paths {
+ append(&command, fmt.tprintf("-I%v", include))
+ }
+
+ for k, v in c.clang_defines {
+ append(&command, fmt.tprintf("-D%v=%v", k, v))
+ }
+
+ process_desc := os2.Process_Desc {
+ command = command[:],
+ }
+
+ state, sout, serr, err := os2.process_exec(process_desc, context.allocator)
+
+ if err != nil {
+ if err == .Not_Exist {
+ panic("Could not find clang. Do you have clang installed and in your path?")
+ }
+
+ fmt.panicf("Error generating ast dump. Error: %v", err)
+ }
+
+ if len(serr) > 0 {
+ fmt.eprintln(string(serr))
+ fmt.eprintfln("Aborting generation for %v", input)
+ return
+ }
+
+ ensure(state.success, "Failed running clang")
+
+ input_filename := filepath.base(input)
+ output_stem := filepath.stem(input_filename)
+ output_filename := fmt.tprintf("%v/%v.odin", s.output_folder, output_stem)
+
+ if s.debug_dump_json_ast {
+ os.write_entire_file(fmt.tprintf("%v-debug_dump.json", output_filename), sout)
+ }
+
+ json_in, json_in_err := json.parse(sout, parse_integers = true)
+
+ if json_in_err != nil {
+ fmt.eprintfln("Error parsing json: %v. %v", json_in_err, string(serr))
+ fmt.eprintfln("Aborting generation for %v", input)
+ return
+ }
+
+ // We use the header source text to extract some comments.
+ source_data, source_data_ok := os.read_entire_file(input)
+ fmt.ensuref(source_data_ok, "Failed reading source file: %v", input)
+ s.source = string(source_data)
+
+ parse_macros(&s, input) // Parse macros so we can add them as constants in Odin
+
+ inner := json_in.(json.Object)["inner"].(json.Array)
+
+ //
+ // Turn the JSON into an intermediate format (parse_decls will append stuff
+ // to s.decls)
+ //
+
+ line := 0
+
+ for &in_decl in inner {
+ // Some decls don't have a line, in that case we send in the most recent line instead.
+ if cur_line, cur_line_ok := json_get_int(in_decl, "loc.line"); cur_line_ok {
+ line = cur_line
+ }
+
+ if s.required_prefix != "" {
+ if name, name_ok := json_get_string(in_decl, "name"); name_ok {
+ if !strings.has_prefix(name, s.required_prefix) {
+ continue
+ }
+ }
+ }
+
+ parse_decl(&s, in_decl, line)
+ }
+
+ slice.sort_by(s.decls[:], proc(i, j: Declaration) -> bool {
+ if i.line == j.line {
+ return i.original_idx < j.original_idx
+ }
+ return i.line < j.line
+ })
+
+ //
+ // Use the stuff in `s` and `s.decl` to write out the bindings.
+ //
+
+ f, f_err := os.open(output_filename, os.O_WRONLY | os.O_CREATE | os.O_TRUNC, 0o644)
+
+ fmt.ensuref(f_err == nil, "Failed opening %v", output_filename)
+ defer os.close(f)
+
+ // Extract any big comment at top of file (clang doesn't see these)
+ {
+ src := strings.trim_space(s.source)
+ in_block := false
+
+ top_comment_loop: for ll in strings.split_lines_iterator(&src) {
+ l := strings.trim_space(ll)
+
+ if in_block {
+ fpln(f, l)
+ if strings.contains(l, "*/") {
+ in_block = false
+ }
+ } else {
+ if len(l) < 2 {
+ continue
+ }
+
+ switch l[:2] {
+ case "//":
+ fpln(f, l)
+ case "/*":
+ in_block = !strings.contains(l, "*/")
+ fpln(f, l)
+ case:
+ break top_comment_loop
+ }
+ }
+ }
+ }
+
+ fpf(f, "package %v\n\n", s.package_name)
+
+ if s.needs_import_c {
+ fpln(f, `import "core:c"`)
+ }
+
+ if s.needs_import_libc {
+ fpln(f, `import "core:c/libc"`)
+ }
+
+ if(s.needs_import_posix) {
+ fpln(f,`import "core:sys/posix"`)
+ }
+
+ fp(f, "\n")
+
+ if s.needs_import_c {
+ fpln(f, "_ :: c")
+ }
+ if s.needs_import_libc {
+ fpln(f, "_ :: libc")
+ }
+ if s.needs_import_posix {
+ fpln(f,"_ :: posix")
+ }
+
+ fp(f, "\n")
+
+ if s.imports_file != "" {
+ top_code, top_code_ok := os.read_entire_file(s.imports_file)
+ fmt.ensuref(top_code_ok, "Failed to load %v", s.imports_file)
+ fp(f, string(top_code))
+ } else if s.import_lib != "" {
+ fpf(f, `foreign import lib "%v"`, s.import_lib)
+ }
+
+ fp(f, "\n\n")
+
+ output_comment :: proc(f: os.Handle, c: string, indent := "") {
+ ci := c
+ for l in strings.split_lines_iterator(&ci) {
+ fp(f, indent)
+ fpln(f, strings.trim_space(l))
+ }
+ }
+
+ //
+ // Figure out all type names
+ //
+
+ for &decl in s.decls {
+ du := &decl.variant
+ switch &d in du {
+ case Struct:
+ name := d.original_name
+
+ // This is really ugly, if you can simplify this, please do.
+ if typedef, has_typedef := s.typedefs[d.id]; has_typedef {
+ d.original_name = typedef
+ name = typedef
+ if replacement, has_replacement := s.rename[name]; has_replacement {
+ name = replacement
+ } else {
+ name = trim_prefix(name, s.remove_type_prefix)
+ if s.force_ada_case_types {
+ name = strings.to_ada_case(name)
+ }
+ }
+ add_to_set(&s.created_symbols, name)
+ } else if replacement, has_replacement := s.rename[name]; has_replacement {
+ name = replacement
+ } else {
+ name = trim_prefix(name, s.remove_type_prefix)
+
+ if s.force_ada_case_types {
+ name = strings.to_ada_case(name)
+ }
+ }
+
+ d.name = vet_name(name)
+ add_to_set(&s.created_types, d.name)
+ case Function:
+ name := d.original_name
+
+ if replacement, has_replacement := s.rename[name]; has_replacement {
+ d.link_name = d.original_name
+ name = replacement
+ } else {
+ name = trim_prefix(name, s.remove_function_prefix)
+ }
+
+ d.name = vet_name(name)
+ case Enum:
+ name := d.original_name
+
+ if typedef, has_typedef := s.typedefs[d.id]; has_typedef {
+ d.original_name = typedef
+ name = typedef
+ if replacement, has_replacement := s.rename[name]; has_replacement {
+ name = replacement
+ } else {
+ name = trim_prefix(name, s.remove_type_prefix)
+ if s.force_ada_case_types {
+ name = strings.to_ada_case(name)
+ }
+ }
+ add_to_set(&s.created_symbols, name)
+ } else if replacement, has_replacement := s.rename[name]; has_replacement {
+ name = replacement
+ } else {
+ name = trim_prefix(name, s.remove_type_prefix)
+
+ if s.force_ada_case_types {
+ name = strings.to_ada_case(name)
+ }
+ }
+
+ d.name = vet_name(name)
+ add_to_set(&s.created_types, d.name)
+ case Typedef:
+ name := d.original_name
+
+ if is_c_type(name) {
+ continue
+ }
+
+ if replacement, has_replacement := s.rename[name]; has_replacement {
+ name = replacement
+ } else {
+ name = trim_prefix(name, s.remove_type_prefix)
+
+ if s.force_ada_case_types {
+ name = strings.to_ada_case(name)
+ }
+ }
+
+ d.name = vet_name(name)
+ add_to_set(&s.created_types, d.name)
+ case Macro:
+ name := d.original_name
+
+ if replacement, has_replacement := s.rename[name]; has_replacement {
+ name = replacement
+ } else {
+ name = trim_prefix(name, s.remove_macro_prefix)
+ }
+
+ d.name = vet_name(name)
+ add_to_set(&s.created_types, d.name)
+ }
+ }
+
+ for _, b in s.bit_setify {
+ add_to_set(&s.created_types, b)
+ }
+
+ for &decl, decl_idx in s.decls {
+ du := &decl.variant
+ switch d in du {
+ case Struct:
+ n := d.name
+
+ if d.is_forward_declare {
+ if d.original_name in s.opaque_type_lookup && d.id not_in s.typedefs {
+ output_comment(f, d.comment)
+ fpf(f, "%v :: struct {{}}\n\n", n)
+ }
+
+ break
+ }
+
+ output_comment(f, d.comment)
+
+ if inject, has_injection := s.inject_before[d.original_name]; has_injection {
+ fpf(f, "%v\n\n", inject)
+ }
+
+ fp(f, n)
+ fp(f, " :: ")
+
+ if override, override_ok := s.type_overrides[d.original_name]; override_ok {
+ fp(f, override)
+ fp(f, "\n\n")
+ break
+ }
+
+ output_struct :: proc(s: Gen_State, d: Struct, indent: int, n: string) -> string {
+ w := strings.builder_make()
+ ws :: strings.write_string
+ ws(&w, "struct ")
+
+ if d.is_union {
+ ws(&w, "#raw_union ")
+ }
+
+ ws(&w, "{\n")
+
+ longest_field_name_with_side_comment: int
+
+ for &field in d.fields {
+ if _, anon_struct_ok := field.anon_struct_type.?; anon_struct_ok {
+ continue
+ }
+
+ field_len: int
+ for fn, nidx in field.names {
+ if nidx != 0 {
+ field_len += 2 // for comma and space
+ }
+
+ field_len += len(vet_name(fn))
+ }
+ if (field.comment == "" || !field.comment_before) && field_len > longest_field_name_with_side_comment {
+ longest_field_name_with_side_comment = field_len
+ }
+ }
+
+ Formatted_Field :: struct {
+ field: string,
+ comment: string,
+ comment_before: bool,
+ }
+
+ fields: [dynamic]Formatted_Field
+
+ for &field in d.fields {
+ b := strings.builder_make()
+
+ override_key: string
+
+ if field.anon_using {
+ strings.write_string(&b, "using _: ")
+ } else {
+ for fn, nidx in field.names {
+ if nidx != 0 {
+ strings.write_string(&b, ", ")
+ }
+
+ strings.write_string(&b, vet_name(fn))
+ }
+
+ names_len := strings.builder_len(b)
+ override_key = fmt.tprintf("%s.%s", d.original_name, strings.to_string(b))
+ strings.write_string(&b, ": ")
+
+ if !field.comment_before {
+ // Padding between name and =
+ for _ in 0..<longest_field_name_with_side_comment-names_len {
+ strings.write_rune(&b, ' ')
+ }
+ }
+ }
+
+ field_type: string
+
+ if field_type_override, has_field_type_override := s.struct_field_overrides[override_key]; override_key != "" && has_field_type_override {
+ if field_type_override == "[^]" {
+ // Change first `^` for `[^]`
+ field_type = translate_type(s, field.type, true)
+ } else {
+ field_type = field_type_override
+ }
+ } else {
+ field_type = translate_type(s, field.type, false)
+ }
+
+ comment := field.comment
+ comment_before := field.comment_before
+
+ if anon_struct, anon_struct_ok := field.anon_struct_type.?; anon_struct_ok {
+ if anon_struct.comment != "" {
+ comment = anon_struct.comment
+ comment_before = true
+ }
+
+ field_type = output_struct(s, anon_struct, indent + 1, n)
+ }
+
+ strings.write_string(&b, field_type)
+
+ append(&fields, Formatted_Field {
+ field = strings.to_string(b),
+ comment = comment,
+ comment_before = comment_before,
+ })
+ }
+
+ longest_field_with_side_comment: int
+
+ for &field in fields {
+ if field.comment != "" && !field.comment_before {
+ longest_field_with_side_comment = max(len(field.field), longest_field_with_side_comment)
+ }
+ }
+
+ for &field, field_idx in fields {
+ has_comment := field.comment != ""
+ comment_before := field.comment_before
+
+ if has_comment && comment_before {
+ if field_idx != 0 {
+ ws(&w, "\n")
+ }
+
+ ci := field.comment
+ for l in strings.split_lines_iterator(&ci) {
+ for _ in 0..<indent+1 {
+ ws(&w, "\t")
+ }
+ ws(&w, strings.trim_space(l))
+ ws(&w, "\n")
+ }
+ }
+
+ for _ in 0..<indent+1 {
+ ws(&w, "\t")
+ }
+ ws(&w, field.field)
+ ws(&w, ",")
+
+ if has_comment && !comment_before {
+ // Padding in front of comment
+ for _ in 0..<(longest_field_with_side_comment - len(field.field)) {
+ ws(&w, " ")
+ }
+
+ ws(&w, " ")
+ ws(&w, field.comment)
+ }
+
+ ws(&w, "\n")
+ }
+
+ for _ in 0..<indent {
+ ws(&w, "\t")
+ }
+ ws(&w, "}")
+ return strings.to_string(w)
+ }
+
+ fp(f, output_struct(s, d, 0, n))
+ fp(f, "\n\n")
+ case Enum:
+ output_comment(f, d.comment)
+
+ name := d.name
+
+ // It has no name, turn it into a bunch of constants
+ if name == "" {
+ for &m in d.members {
+ mn := m.name
+
+ if strings.has_prefix(strings.to_lower(mn), strings.to_lower(s.remove_type_prefix)) {
+ mn = mn[len(s.remove_type_prefix):]
+
+ if strings.has_prefix(mn, "_") {
+ mn = mn[1:]
+ }
+ }
+
+ fpf(f, "%v :: %v\n\n", mn, m.value)
+ }
+
+ break
+ }
+
+ fp(f, name)
+ fp(f, " :: enum c.int {\n")
+
+ bit_set_name, bit_setify := s.bit_setify[d.original_name]
+ bit_set_all_constant: string
+
+ overlap_length := 0
+ longest_name := 0
+
+ all_has_value := true
+
+ if len(d.members) > 1 {
+ overlap_length_source := d.members[0].name
+ overlap_length = len(overlap_length_source)
+ longest_name = overlap_length
+
+ if d.members[0].value == nil {
+ all_has_value = false
+ }
+
+ for idx in 1..<len(d.members) {
+ if (d.members[idx].value == -1 || d.members[idx].value == 2147483647) && bit_setify {
+ continue
+ }
+
+ mn := d.members[idx].name
+ length := strings.prefix_length(mn, overlap_length_source)
+
+ if length < overlap_length {
+ overlap_length = length
+ overlap_length_source = mn
+ }
+
+ longest_name = max(len(mn), longest_name)
+
+ if d.members[idx].value == nil {
+ all_has_value = false
+ }
+ }
+ }
+
+ Formatted_Member :: struct {
+ name: string,
+ member: string,
+ comment: string,
+ comment_before: bool,
+ }
+
+ members: [dynamic]Formatted_Member
+
+ for &m in d.members {
+ if (m.value == -1 || m.value == 2147483647) && bit_setify {
+ bit_set_all_constant = m.name
+ continue
+ }
+
+ if m.value == 0 && bit_setify {
+ continue
+ }
+
+ b := strings.builder_make()
+
+ name_without_overlap := m.name[overlap_length:]
+
+ // Remove any leading underscores.
+ for ; name_without_overlap[0] == '_'; name_without_overlap = name_without_overlap[1:] {}
+
+ // First letter is number... Can't have that!
+ if len(name_without_overlap) > 0 && unicode.is_number(utf8.rune_at(name_without_overlap, 0)) {
+ name_without_overlap = fmt.tprintf("_%v", name_without_overlap)
+ }
+
+ strings.write_string(&b, name_without_overlap)
+
+ suffix_pad := all_has_value ? longest_name - len(name_without_overlap) - overlap_length : 0
+
+ if vv, v_ok := m.value.?; v_ok {
+ if !m.comment_before {
+ for _ in 0..<suffix_pad {
+ // Padding between name and `=`
+ strings.write_rune(&b, ' ')
+ }
+ }
+
+ val_string: string
+
+ if bit_setify {
+ v := u32(vv)
+ assert(v != 0)
+
+ // Note the `log2`... This turns a value such as `64`
+ // into `6`, which is what it should be for a bit_set.
+ val_string = fmt.tprintf(" = %v", bits.log2(v))
+
+ } else {
+ val_string = fmt.tprintf(" = %v", vv)
+ }
+
+ strings.write_string(&b, val_string)
+ }
+
+ append(&members, Formatted_Member {
+ name = name_without_overlap,
+ member = strings.to_string(b),
+ comment = m.comment,
+ comment_before = m.comment_before,
+ })
+ }
+
+ longest_member_name_with_side_comment: int
+
+ for &m in members {
+ if m.comment != "" && !m.comment_before && len(m.member) > longest_member_name_with_side_comment {
+ longest_member_name_with_side_comment = len(m.member)
+ }
+ }
+
+ for &m, m_idx in members {
+ has_comment := m.comment != ""
+ comment_before := m.comment_before
+
+ if has_comment && comment_before {
+ if m_idx != 0 {
+ fp(f, "\n")
+ }
+ output_comment(f, m.comment, "\t")
+ }
+
+ fp(f, "\t")
+ fp(f, m.member)
+ fp(f, ",")
+
+ if has_comment && !comment_before {
+ for _ in 0..<(longest_member_name_with_side_comment - len(m.member)) {
+ // Padding in front of comment
+ fp(f, " ")
+ }
+
+ fpf(f, " %v", m.comment)
+ }
+
+ fp(f, '\n')
+ }
+
+ fp(f, "}\n\n")
+
+ if bit_setify {
+ fpf(f, "%v :: distinct bit_set[%v; c.int]\n\n", bit_set_name, name)
+
+ // In case there is a typedef for this in the code.
+ add_to_set(&s.created_symbols, bit_set_name)
+
+ // There was a member with value `-1`... That means all bits are
+ // set. Create a bit_set constant with all variants set.
+ if bit_set_all_constant != "" {
+ all_constant := strings.to_screaming_snake_case(trim_prefix(strings.to_lower(bit_set_all_constant), strings.to_lower(s.remove_type_prefix)))
+
+ fpf(f, "%v :: %v {{ ", all_constant, bit_set_name)
+
+ for &m, i in members {
+ fpf(f, ".%v", m.name)
+
+ if i != len(members) - 1 {
+ fp(f, ", ")
+ }
+ }
+
+ fp(f, " }\n\n")
+ }
+ }
+
+ case Function:
+ // handled later. This makes all procs end up at bottom, after types.
+
+ case Typedef:
+ n := d.name
+
+ if d.original_name in s.opaque_type_lookup {
+ if d.pre_comment != "" {
+ output_comment(f, d.pre_comment)
+ }
+ fpf(f, "%v :: struct {{}}", n)
+
+ if d.side_comment != "" {
+ fp(f, ' ')
+ fp(f, d.side_comment)
+ }
+
+ fp(f, "\n\n")
+ continue
+ }
+
+ if n in s.created_symbols || strings.has_prefix(d.type, "0x") {
+ continue
+ }
+
+ if translate_type(s, d.type, false) == d.name {
+ continue
+ }
+
+ if d.pre_comment != "" {
+ output_comment(f, d.pre_comment)
+ }
+
+ fp(f, n)
+
+ fp(f, " :: ")
+
+ if override, override_ok := s.type_overrides[d.original_name]; override_ok {
+ fp(f, override)
+
+ if d.side_comment != "" {
+ output_comment(f, d.side_comment)
+ }
+
+ fp(f, "\n\n")
+ continue
+ }
+
+ type := d.type
+
+ if strings.has_prefix(type, "struct ") {
+ // This is a weird case -- I used this for opaque types in the
+ // beginning, but opaque types are now handled by
+ // `s.opaque_type_lookup`, so perhaps this isn't needed anymore?
+ fp(f, "struct {}")
+ } else if strings.contains(type, "(") && strings.contains(type, ")") {
+ // function pointer typedef
+ fp(f, translate_type(s, type, false))
+ add_to_set(&s.type_is_proc, n)
+ } else {
+ fpf(f, "%v", translate_type(s, type, false))
+ }
+
+ if d.side_comment != "" {
+ fp(f, ' ')
+ fp(f, d.side_comment)
+ }
+
+ fp(f, "\n\n")
+
+ case Macro:
+ val := d.val
+
+ comment_out := false
+
+ val = trim_encapsulating_parens(val)
+ b := strings.builder_make()
+ for i := 0; i < len(val); i += 1 {
+ switch char_type(val[i]) {
+ case .Char:
+ // Parsing text here is quite annoying. Is it a type? Is it another macro? Maybe it's an enum field. We don't know.
+ // My implementation will check if it's a built-in type or a macro. If it's neither we are going to assume it's a user-defined type.
+ // As discussed here https://github.com/karl-zylinski/odin-c-bindgen/pull/27 we dont know all the defined types so figuring out what it is isn't always possible.
+ start := i
+ for ; i < len(val); i += 1 {
+ if char_type(val[i]) != .Char && char_type(val[i]) != .Num {
+ break
+ }
+ }
+ if val[start:i] in s.defines {
+ strings.write_string(&b, trim_prefix(val[start:i], s.remove_macro_prefix))
+ } else if type, exists := c_type_mapping[val[start:i]]; exists {
+ strings.write_string(&b, type)
+ } else if _, exists = s.created_types[trim_prefix(val[start:i], s.remove_type_prefix)]; exists {
+ strings.write_string(&b, val[start:i])
+ } else {
+ comment_out = true
+
+ if s.force_ada_case_types {
+ strings.write_string(&b, strings.to_ada_case(trim_prefix(val[start:i], s.remove_type_prefix)))
+ } else {
+ strings.write_string(&b, trim_prefix(val[start:i], s.remove_type_prefix))
+ }
+ }
+ i -= 1
+ case .Num:
+ suffix_index := 0
+ start := i
+ is_prefixed := false
+
+ if i + 1 < len(val) {
+ prefix := val[i:i+2]
+
+ if prefix == "0x" || prefix == "0b" {
+ is_prefixed = true
+ i += 2
+ }
+ }
+
+ if is_prefixed {
+ // 0x0ULL and 0xFFFF both need to work. This branch makes sure that only things which contain letter above `f` are
+ // treated as suffixes, which I think is true for 0x and 0b constants.
+ for ; i < len(val); i += 1 {
+ type := char_type(val[i])
+
+ if type == .Char && suffix_index == 0 && ((val[i] > 'f' && val[i] < 'z') || (val[i] > 'F' && val[i] <= 'Z')) {
+ suffix_index = i
+ } else if type != .Num && type != .Char {
+ break
+ }
+ }
+ } else {
+ // Make 0.3f become 0.3
+ for ; i < len(val); i += 1 {
+ type := char_type(val[i])
+
+ if type == .Char && suffix_index == 0 {
+ suffix_index = i
+ } else if type != .Num && type != .Char {
+ break
+ }
+ }
+ }
+
+ strings.write_string(&b, val[start:suffix_index > 0 ? suffix_index : i])
+ i -= 1
+ case .Quote:
+ start := i
+ for i += 1; i < len(val); i += 1 {
+ if val[i] == '\\' {
+ i += 1
+ continue
+ } else if val[i] == '"' {
+ break
+ }
+ }
+ strings.write_string(&b, val[start:i + 1])
+ case .Other:
+ if val[i] == '~' || val[i] == '#' {
+ comment_out = true
+ }
+
+ strings.write_byte(&b, val[i])
+ }
+ }
+
+ value_string := strings.to_string(b)
+ if value_string == "{}" || value_string == "{0}" {
+ continue
+ }
+
+ if d.comment != "" {
+ fp(f, d.comment)
+ }
+
+ if comment_out {
+ fp(f, "// ")
+ }
+ fpf(f, "%v%*s:: %v", d.name, max(d.whitespace_after_name, 1), "", value_string)
+
+ if d.side_comment != "" {
+ fpf(f, "%*s%v", d.whitespace_before_side_comment, "", d.side_comment)
+ }
+
+ fp(f, "\n")
+
+ if decl_idx < len(s.decls) - 1 {
+ next := &s.decls[decl_idx + 1]
+
+ _, next_is_macro := next.variant.(Macro)
+
+ if !next_is_macro || next.line != decl.line + 1 {
+ fp(f, "\n")
+ }
+ }
+ }
+ }
+
+ //
+ // Turn functions into groups that are separated by comments. If a comment
+ // is before a function then it is used as a "group". If comments are to the
+ // right of a function, then the group continues.
+ //
+ // Everything within a group shares the same padding between the name and
+ // the `::`
+ //
+
+ Function_Group :: struct {
+ header_comment: string,
+ functions: [dynamic]Function,
+ }
+
+ groups: [dynamic]Function_Group
+ curr_group: Function_Group
+
+ for &decl in s.decls {
+ du := &decl.variant
+ if f, f_ok := du.(Function); f_ok {
+ if f.comment != "" {
+ if len(curr_group.functions) > 0 {
+ append(&groups, curr_group)
+ }
+
+ curr_group = {
+ header_comment = f.comment,
+ }
+ }
+
+ append(&curr_group.functions, f)
+ }
+ }
+
+ if len(curr_group.functions) > 0 {
+ append(&groups, curr_group)
+ }
+
+ if len(groups) > 0 {
+ fmt.fprintfln(f, `@(default_calling_convention="c", link_prefix="%v")`, s.remove_function_prefix)
+ fmt.fprintln(f, "foreign lib {")
+
+ for &g, gidx in groups {
+ if g.header_comment != "" {
+ if gidx != 0 {
+ fp(f, "\n")
+ }
+
+ output_comment(f, g.header_comment, "\t")
+ }
+
+ longest_function_name: int
+
+ for &d in g.functions {
+ if len(d.name) > longest_function_name {
+ longest_function_name = len(d.name)
+ }
+ }
+
+ Formatted_Function :: struct {
+ function: string,
+ post_comment: string,
+ attributes: []string,
+ }
+
+ formatted_functions: [dynamic]Formatted_Function
+
+ for &d in g.functions {
+ b := strings.builder_make()
+ attributes := make([dynamic]string)
+
+ w :: strings.write_string
+
+ if d.link_name != "" {
+ append(&attributes, fmt.tprintf("link_name=\"%s\"", d.link_name))
+ }
+
+ w(&b, d.name)
+
+ for _ in 0..<longest_function_name-len(d.name) {
+ strings.write_rune(&b, ' ')
+ }
+
+ w(&b, " :: proc(")
+
+ for &p, i in d.parameters {
+ n := vet_name(p.name)
+
+ type: string
+ type_override_key := fmt.tprintf("%v.%v", d.original_name, n)
+
+ if type_override, type_override_ok := s.procedure_type_overrides[type_override_key]; type_override_ok {
+ switch type_override {
+ case "#by_ptr":
+ type = strings.trim_prefix(translate_type(s, p.type, false), "^")
+ w(&b, "#by_ptr ")
+ case "[^]":
+ type = translate_type(s, p.type, true)
+ case:
+ type = type_override
+ }
+ } else {
+ type = translate_type(s, p.type, false)
+ }
+
+ // Empty name means unnamed parameter. Drop the colon.
+ if len(n) != 0 {
+ w(&b, n)
+ w(&b, ": ")
+ } else {
+ w(&b, "_: ")
+ }
+
+ w(&b, type)
+
+ if i != len(d.parameters) - 1 {
+ w(&b, ", ")
+ } else {
+ if d.variadic {
+ w(&b,", #c_vararg _: ..any")
+ }
+ }
+ }
+
+ w(&b, ")")
+
+ if d.return_type != "" {
+ w(&b, " -> ")
+
+ return_type: string
+
+ if override, override_ok := s.procedure_type_overrides[d.original_name]; override_ok {
+ switch override {
+ case "[^]":
+ return_type = translate_type(s, d.return_type, true)
+ case:
+ return_type = override
+ }
+ } else {
+ return_type = translate_type(s, d.return_type, false)
+ }
+
+ w(&b, return_type)
+ }
+
+ w(&b, " ---")
+
+ append(&formatted_functions, Formatted_Function {
+ function = strings.to_string(b),
+ post_comment = d.post_comment,
+ attributes = attributes[:],
+ })
+ }
+
+ longest_formatted_function: int
+
+ for &ff in formatted_functions {
+ if len(ff.function) < 90 && len(ff.function) > longest_formatted_function {
+ longest_formatted_function = len(ff.function)
+ }
+ }
+
+ for &ff in formatted_functions {
+ if len(ff.attributes) > 0 {
+ fp(f, "\t")
+ fp(f, fmt.tprintf("@(%s)", strings.join(ff.attributes[:], ", ")))
+ fp(f, "\n")
+ }
+ fp(f, "\t")
+ fp(f, ff.function)
+
+ if ff.post_comment != "" {
+ for _ in 0..<(longest_formatted_function-len(ff.function)) {
+ fp(f, ' ')
+ }
+
+ fp(f, ' ')
+ fp(f, ff.post_comment)
+ }
+
+ fp(f, "\n")
+ }
+ }
+
+ fmt.fprintln(f, "}")
+ }
+}
+
+main :: proc() {
+ permanent_arena: vmem.Arena
+ permanent_allocator := vmem.arena_allocator(&permanent_arena)
+ context.allocator = permanent_allocator
+ context.temp_allocator = permanent_allocator
+
+ ensure(len(os.args) == 2, "Usage: bindgen directory")
+ input_arg := os.args[1]
+
+ config_filename := "bindgen.sjson"
+ config_dir: string
+ if strings.has_suffix(input_arg, ".sjson") && os.is_file(input_arg) {
+ config_filename = filepath.base(input_arg)
+ config_dir = filepath.dir(input_arg, context.temp_allocator)
+ } else if os.is_dir(input_arg) {
+ config_dir = input_arg
+ } else {
+ fmt.panicf("%v is not a directory nor a valid config file", input_arg)
+ }
+
+ // Config file is optional
+ config: Config
+
+ default_output_folder := "output"
+ default_package_name := "pkg"
+
+ if input_dir, input_dir_err := os2.open(input_arg); input_dir_err == nil {
+ if stat, stat_err := input_dir.fstat(input_dir, context.allocator); stat_err == nil {
+ default_output_folder = stat.name
+ default_package_name = stat.name
+ }
+ }
+
+ if err := os.set_current_directory(config_dir); err != nil {
+ fmt.panicf("failed to set current working directory: %v", err)
+ }
+
+ if os.is_file(config_filename) {
+ if config_data, config_data_ok := os.read_entire_file(config_filename); config_data_ok {
+ config_err := json.unmarshal(config_data, &config, .SJSON)
+ fmt.ensuref(config_err == nil, "Failed parsing config %v: %v", config_filename, config_err)
+ } else {
+ fmt.ensuref(config_data_ok, "Failed parsing config %v", config_filename)
+ }
+ } else {
+ config.inputs = {
+ ".",
+ }
+ }
+
+ if config.output_folder == "" {
+ config.output_folder = default_output_folder
+ }
+
+ if config.package_name == "" {
+ config.package_name = default_package_name
+ }
+
+ if config.remove_prefix != "" {
+ panic("Error in bindgen.sjson: remove_prefix has been split into remove_function_prefix and remove_type_prefix")
+ }
+
+ if len(config.rename_types) > 0 {
+ panic("Error in bindgen.sjson: rename_types has been renamed to rename")
+ }
+
+ input_files: [dynamic]string
+
+ for i in config.inputs {
+ if os.is_dir(i) {
+ input_folder, input_folder_err := os2.open(i)
+ fmt.ensuref(input_folder_err == nil, "Failed opening folder %v: %v", i, input_folder_err)
+ iter := os2.read_directory_iterator_create(input_folder)
+
+ for f in os2.read_directory_iterator(&iter) {
+ if f.type != .Regular || slice.contains(config.ignore_inputs, f.name) {
+ continue
+ }
+
+ append(&input_files, fmt.tprintf("%v/%v", i, f.name))
+ }
+
+ os2.close(input_folder)
+ } else if os.is_file(i) {
+ append(&input_files, i)
+ } else {
+ fmt.eprintfln("%v is neither directory or .h file", i)
+ }
+ }
+
+ if config.output_folder != "" && !os2.exists(config.output_folder) {
+ make_dir_err := os2.make_directory_all(config.output_folder)
+ fmt.ensuref(make_dir_err == nil, "Failed creating output directory %v: %v", config.output_folder, make_dir_err)
+ }
+
+ for i in input_files {
+ ext := filepath.ext(i)
+ switch ext {
+ case ".h":
+ gen(i, config)
+ case ".odin", ".lib", ".a", ".dll", ".dylib":
+ // Bring along odin and library files
+ name := filepath.base(i)
+ os2.copy_file(fmt.tprintf("%v/%v", config.output_folder, name), i)
+ }
+ }
+}
diff --git a/odin-c-bindgen/src/json_helpers.odin b/odin-c-bindgen/src/json_helpers.odin
@@ -0,0 +1,106 @@
+package bindgen
+
+import "core:encoding/json"
+import "core:strings"
+import "core:strconv"
+
+_ :: strings
+_ :: strconv
+
+json_check_bool :: proc(v: json.Value, key: string) -> bool {
+ val, _ := json_get(v, key, json.Boolean)
+ return val
+}
+
+json_get_string :: proc(v: json.Value, key: string) -> (str: string, ok: bool) {
+ return json_get(v, key, json.String)
+}
+
+json_get_array :: proc(v: json.Value, key: string) -> (arr: json.Array, ok: bool) {
+ return json_get(v, key, json.Array)
+}
+
+json_get_int :: proc(v: json.Value, key: string) -> (res: int, ok: bool) {
+ i, i_ok := json_get(v, key, json.Integer)
+ return int(i), i_ok
+}
+
+json_get_object :: proc(v: json.Value, key: string) -> (arr: json.Object, ok: bool) {
+ return json_get(v, key, json.Object)
+}
+
+json_get :: proc(v: json.Value, key: string, $T: typeid) -> (res: T, ok: bool) {
+ key_iter := key
+ cur := v
+
+ for k in strings.split_iterator(&key_iter, ".") {
+ is_index := false
+ index: int
+ if i, i_ok := strconv.parse_int(k, 10); i_ok {
+ is_index = true
+ index = i
+ }
+
+ if is_index {
+ if arr, is_arr := cur.(json.Array); is_arr {
+ if index < 0 || index >= len(arr) {
+ return {}, false
+ }
+
+ cur = arr[index]
+ } else {
+ return {}, false
+ }
+ } else {
+ if obj, is_obj := cur.(json.Object); is_obj {
+ if child, child_ok := obj[k]; child_ok {
+ cur = child
+ } else {
+ return {}, false
+ }
+ } else {
+ return {}, false
+ }
+ }
+ }
+
+ return cur.(T)
+}
+
+json_has :: proc(v: json.Value, key: string) -> (ok: bool) {
+ key_iter := key
+ cur := v
+
+ for k in strings.split_iterator(&key_iter, ".") {
+ is_index := false
+ index: int
+ if i, i_ok := strconv.parse_int(k, 10); i_ok {
+ is_index = true
+ index = i
+ }
+
+ if is_index {
+ if arr, is_arr := cur.(json.Array); is_arr {
+ if index < 0 || index >= len(arr) {
+ return false
+ }
+
+ cur = arr[index]
+ } else {
+ return false
+ }
+ } else {
+ if obj, is_obj := cur.(json.Object); is_obj {
+ if child, child_ok := obj[k]; child_ok {
+ cur = child
+ } else {
+ return false
+ }
+ } else {
+ return false
+ }
+ }
+ }
+
+ return true
+}
+\ No newline at end of file
diff --git a/odin-c-bindgen/src/test.odin b/odin-c-bindgen/src/test.odin
@@ -0,0 +1,242 @@
+#+feature dynamic-literals
+
+
+package bindgen
+
+
+import "core:fmt"
+import vmem "core:mem/virtual"
+import "core:os/os2"
+import "core:testing"
+
+
+// These are just some simple tests that I wrote while working on the macro parser. They are not exhaustive, but they do cover some of the basic functionality.
+// Feel free to add more tests/test cases as you see fit. I haven't added any test cases for malformed macros but it's not our problem to deal with bad c code.
+@(test)
+test_parse_value :: proc(t: ^testing.T) {
+ gen_arena: vmem.Arena
+ defer vmem.arena_destroy(&gen_arena)
+ context.allocator = vmem.arena_allocator(&gen_arena)
+ context.temp_allocator = vmem.arena_allocator(&gen_arena)
+
+ ret, type := parse_value("5")
+ testing.expect_value(t, type, Macro_Type.Constant_Expression)
+ compare_arrays(ret, []string{"5"}, t)
+
+ ret, type = parse_value("10 + 5")
+ testing.expect_value(t, type, Macro_Type.Constant_Expression)
+ compare_arrays(ret, []string{"10", "+", "5"}, t)
+
+ ret, type = parse_value("\"test\", 5")
+ testing.expect_value(t, type, Macro_Type.Multivalue)
+ compare_arrays(ret, []string{"\"test\"", "5"}, t)
+
+ ret, type = parse_value("(10 + 5)")
+ testing.expect_value(t, type, Macro_Type.Constant_Expression)
+ compare_arrays(ret, []string{"(10 + 5)"}, t)
+}
+
+compare_arrays :: #force_inline proc(a, b: []string, t: ^testing.T) {
+ if testing.expect_value(t, len(a), len(b)) == false {
+ return
+ }
+ for i in 0 ..< len(a) {
+ testing.expect_value(t, a[i], b[i])
+ }
+}
+
+@(test)
+test_parse_macro :: proc(t: ^testing.T) {
+ gen_arena: vmem.Arena
+ defer vmem.arena_destroy(&gen_arena)
+ context.allocator = vmem.arena_allocator(&gen_arena)
+ context.temp_allocator = vmem.arena_allocator(&gen_arena)
+
+ macro_token := parse_macro("VALUELESS_MACRO")
+ testing.expect_value(t, macro_token.type, Macro_Type.Valueless)
+ testing.expect_value(t, macro_token.name, "VALUELESS_MACRO")
+ compare_arrays(macro_token.values, []string{}, t)
+
+
+ macro_token = parse_macro("CONSTANT 5")
+ testing.expect_value(t, macro_token.type, Macro_Type.Constant_Expression)
+ testing.expect_value(t, macro_token.name, "CONSTANT")
+ compare_arrays(macro_token.values, []string{"5"}, t)
+
+
+ macro_token = parse_macro("CONSTANT 01")
+ testing.expect_value(t, macro_token.type, Macro_Type.Constant_Expression)
+ testing.expect_value(t, macro_token.name, "CONSTANT")
+ compare_arrays(macro_token.values, []string{"01"}, t)
+
+
+ macro_token = parse_macro("CONSTANT 0b0001")
+ testing.expect_value(t, macro_token.type, Macro_Type.Constant_Expression)
+ testing.expect_value(t, macro_token.name, "CONSTANT")
+ compare_arrays(macro_token.values, []string{"0b0001"}, t)
+
+
+ macro_token = parse_macro("CONSTANT 0x0001")
+ testing.expect_value(t, macro_token.type, Macro_Type.Constant_Expression)
+ testing.expect_value(t, macro_token.name, "CONSTANT")
+ compare_arrays(macro_token.values, []string{"0x0001"}, t)
+
+
+ macro_token = parse_macro("CONSTANT \"String\"")
+ testing.expect_value(t, macro_token.type, Macro_Type.Constant_Expression)
+ testing.expect_value(t, macro_token.name, "CONSTANT")
+ compare_arrays(macro_token.values, []string{"\"String\""}, t)
+
+ macro_token = parse_macro("FUNCTION(x) (x)")
+ testing.expect_value(t, macro_token.type, Macro_Type.Function)
+ testing.expect_value(t, macro_token.name, "FUNCTION")
+ compare_arrays(macro_token.values, []string{"(${0}$)"}, t)
+
+ macro_token = parse_macro("FUNCTION(x, y) (x + y)")
+ testing.expect_value(t, macro_token.type, Macro_Type.Function)
+ testing.expect_value(t, macro_token.name, "FUNCTION")
+ compare_arrays(macro_token.values, []string{"(${0}$ + ${1}$)"}, t)
+
+ macro_token = parse_macro("FUNCTION(x, y, z) (z - x + y)")
+ testing.expect_value(t, macro_token.type, Macro_Type.Function)
+ testing.expect_value(t, macro_token.name, "FUNCTION")
+ compare_arrays(macro_token.values, []string{"(${2}$ - ${0}$ + ${1}$)"}, t)
+}
+
+
+@(test)
+test_parse_pystring :: proc(t: ^testing.T) {
+ s := parse_pystring("(${2}$ - ${0}$ + ${1}$)", {"10", "20", "30"})
+ testing.expect_value(t, s, "(30 - 10 + 20)")
+
+ s = parse_pystring("{${2}$ - ${0}$ + ${1}$}", {"10", "20", "30"})
+ testing.expect_value(t, s, "{30 - 10 + 20}")
+
+ s = parse_pystring("${0}$ ${1}$", {"hello", "world"})
+ testing.expect_value(t, s, "hello world")
+}
+
+
+@(test)
+test_parse_file_macros :: proc(t: ^testing.T) {
+ gen_arena: vmem.Arena
+ defer vmem.arena_destroy(&gen_arena)
+ context.allocator = vmem.arena_allocator(&gen_arena)
+ context.temp_allocator = vmem.arena_allocator(&gen_arena)
+
+ s: Gen_State = {
+ source = "#define FIVE 5",
+ }
+ macros := parse_file_macros(&s)
+ expected := []string{"FIVE"}
+
+ for e in expected {
+ testing.expect(t, e in macros)
+ }
+
+ s = {
+ source = "#define FIVE 5\n#define TEN 10\n#define TWENTY 20",
+ }
+ macros = parse_file_macros(&s)
+ expected = []string{"FIVE", "TEN", "TWENTY"}
+ for e in expected {
+ testing.expect(t, e in macros)
+ }
+
+ s = {
+ source = "#define ADD(x, y) (x + y)\n#define SUB(x, y) (x - y)\n",
+ }
+ macros = parse_file_macros(&s)
+ expected = []string{"ADD", "SUB"}
+ for e in expected {
+ testing.expect(t, e in macros)
+ }
+}
+
+
+@(test)
+test_parse_clang_macros :: proc(t: ^testing.T) {
+ gen_arena: vmem.Arena
+ defer vmem.arena_destroy(&gen_arena)
+ context.allocator = vmem.arena_allocator(&gen_arena)
+ context.temp_allocator = vmem.arena_allocator(&gen_arena)
+
+ s: Gen_State = {}
+ macros_map := parse_clang_macros(&s, "test/test.h")
+ macro_tokens := []Macro_Token {
+ {type = .Function, name = "SUB", values = {"(float) (${0}$) - (float)(${1}$)"}},
+ {type = .Function, name = "NEST", values = {"NEST1(${0}$)"}},
+ {type = .Function, name = "NEST1", values = {"NEST2(${0}$)"}},
+ {type = .Function, name = "NEST2", values = {"(${0}$)"}},
+ {type = .Constant_Expression, name = "ARRAY", values = {"{1}"}},
+ {type = .Function, name = "FUNC", values = {"(${0}$ + ${1}$ + ${2}$)"}},
+ {type = .Multivalue, name = "FUNC_TEST", values = {"1", "2", "3"}},
+ {type = .Constant_Expression, name = "FUNC_TEST_RESULT", values = {"FUNC(FUNC_TEST)"}},
+ {type = .Constant_Expression, name = "FALSE", values = {"!", "true"}},
+ {type = .Constant_Expression, name = "TRUE", values = {"!false"}},
+ {type = .Multivalue, name = "MULT_VAL", values = {"10", "20", "30"}},
+ {type = .Constant_Expression, name = "ARRAY_TEST", values = {"{FUNC_TEST}"}},
+ {type = .Constant_Expression, name = "NO_INDEX", values = {"(uint32_t)0"}},
+ {type = .Constant_Expression, name = "VALUE", values = {"20010"}},
+ {type = .Constant_Expression, name = "VALUE_STRING", values = {"#VALUE"}},
+ {type = .Constant_Expression, name = "CINDEX_VERSION_MAJOR", values = {"0"}},
+ {type = .Constant_Expression, name = "CINDEX_VERSION_MINOR", values = {"64"}},
+ {
+ type = .Constant_Expression,
+ name = "CINDEX_VERSION_STRING",
+ values = {"#CINDEX_VERSION_MAJOR", "\".\"", "#CINDEX_VERSION_MINOR"},
+ },
+ }
+ for ¯o_token in macro_tokens {
+ macro, found := macros_map[macro_token.name]
+ testing.expect_value(t, found, true)
+ testing.expect_value(t, macro.name, macro_token.name)
+ testing.expect_value(t, macro.type, macro_token.type)
+ compare_arrays(macro.values, macro_token.values, t)
+ }
+}
+
+
+@(test)
+test_parse_macros :: proc(t: ^testing.T) {
+ gen_arena: vmem.Arena
+ defer vmem.arena_destroy(&gen_arena)
+ context.allocator = vmem.arena_allocator(&gen_arena)
+ context.temp_allocator = vmem.arena_allocator(&gen_arena)
+
+ // Why ../ here and not in test_parse_clang_macros? IDK but I tested them both and that's how it is.
+ data, err := os2.read_entire_file("../test/test.h", context.allocator)
+ if err != nil {
+ fmt.print("Error reading file: %s\n", err)
+ return
+ }
+
+ s := Gen_State {
+ source = string(data),
+ }
+ parse_macros(&s, "../test/test.h")
+
+ expected_macros := map[string]string {
+ "ARRAY" = "{1}",
+ "FIVE_SUB_TWO" = "(float) (5) - (float)(2)",
+ "UNNEST" = "(\"Test\")",
+ "LIGHTGRAY" = "(Color){ 200, 200, 200, 255 }",
+ "FUNC_TEST_RESULT" = "(1 + 2 + 3)",
+ "FALSE" = "! true",
+ "TRUE" = "!false",
+ "ARRAY_TEST" = "{1, 2, 3}",
+ "UFBX_HEADER_VERSION" = "((uint32_t)(0)*1000000u + (uint32_t)(18)*1000u + (uint32_t)(0))",
+ "NO_INDEX" = "(uint32_t)0",
+ "VALUE" = "20010",
+ "VALUE_STRING" = "#20010", // TODO: This needs to become \"20010\"
+ "CINDEX_VERSION_MAJOR" = "0",
+ "CINDEX_VERSION_MINOR" = "64",
+ "CINDEX_VERSION_STRING" = "#CINDEX_VERSION_MAJOR \".\" #CINDEX_VERSION_MINOR", // TODO: This needs to become \"0.64\"
+ }
+ testing.expect_value(t, len(s.defines), len(expected_macros))
+ for name, expected_value in expected_macros {
+ value, found := s.defines[name]
+ testing.expect_value(t, found, true)
+ testing.expect_value(t, value, expected_value)
+ }
+}
diff --git a/odin-c-bindgen/test/test.h b/odin-c-bindgen/test/test.h
@@ -0,0 +1,96 @@
+#pragma GCC push_options
+#pragma GCC optimize ("O0")
+
+#include <time.h>
+#include <stdbool.h>
+
+#define NEST(x) NEST1(x)
+#define NEST1(x) NEST2(x)
+#define NEST2(x) (x)
+
+#define SUB(x, y) (float) (x) - (float)(y)
+
+#define FIVE_SUB_TWO SUB(5, 2)
+#define UNNEST NEST("Test")
+
+#define ARRAY {1}
+
+#define CLITERAL(type) (type)
+#define LIGHTGRAY CLITERAL(Color){ 200, 200, 200, 255 }
+
+#define FUNC(x, y, z) (x + y + z)
+#define FUNC_TEST 1, 2, 3
+#define FUNC_TEST_RESULT FUNC(FUNC_TEST)
+
+#define ARRAY_TEST {FUNC_TEST}
+
+#define FALSE ! true
+#define TRUE !false
+
+#define MULT_VAL (10, 20, 30)
+
+#define ufbx_pack_version(major, minor, patch) ((uint32_t)(major)*1000000u + (uint32_t)(minor)*1000u + (uint32_t)(patch))
+#define UFBX_HEADER_VERSION ufbx_pack_version(0, 18, 0)
+#define FUNC_ALIAS ufbx_pack_version
+
+#define NO_INDEX (uint32_t)0
+
+#define VALUE 20010
+#define VALUE_STRING #VALUE
+
+#define CINDEX_VERSION_MAJOR 0
+#define CINDEX_VERSION_MINOR 64
+
+#define CINDEX_VERSION_STRING #CINDEX_VERSION_MAJOR "." #CINDEX_VERSION_MINOR
+
+// line comment one line
+#define LINE_COMMENT_ONE 1
+
+// line comment two lines
+// the other line
+#define LINE_COMMENT_TWO 2
+
+/* block comment one line */
+#define BLOCK_COMMENT_ONE 1
+
+/* block comment two lines
+ * the other line
+ */
+#define BLOCK_COMMENT_TWO 2
+
+#define END_LINE_COMMENT 12 // end line comment
+#define END_LINE_BLOCK_COMMENT 34 /* end line block comment */
+#define BELOW_BLOCK_COMMENT 56 /* inline block comment on the line above */
+
+#define BLOCK_ABOVE_SECTION 78
+
+////////////////////////////////////////////////////////////////////////////////
+//// Section header
+////////////////////////////////////////////////////////////////////////////////
+
+#define SECTIONED_ONE (1u << 0u) /* end line */
+#define SECTIONED_TWO (1u << 1u) /* end line */
+
+struct Color {
+ int r;
+ int g;
+ int b;
+ int a;
+};
+
+struct HasBool {
+ bool a;
+};
+
+typedef time_t my_time;
+
+// Should add a bindgen.sjson with `remove_type_prefix = "test_"
+// typedef struct test_time_t {
+// int seconds;
+// } test_time_t;
+
+typedef int simple_typedef;
+
+typedef void void_typedef;
+
+#pragma GCC pop_options
diff --git a/odin-c-bindgen/test/test/test.odin b/odin-c-bindgen/test/test/test.odin
@@ -0,0 +1,85 @@
+package test
+
+import "core:c"
+import "core:c/libc"
+
+_ :: c
+_ :: libc
+
+
+
+FIVE_SUB_TWO :: (f32) (5) - (f32)(2)
+UNNEST :: "Test"
+
+ARRAY :: {1}
+
+LIGHTGRAY :: (Color){ 200, 200, 200, 255 }
+
+FUNC_TEST_RESULT :: 1 + 2 + 3
+
+ARRAY_TEST :: {1, 2, 3}
+
+// FALSE :: ! true
+// TRUE :: !false
+
+UFBX_HEADER_VERSION :: (u32)(0)*1000000 + (u32)(18)*1000 + (u32)(0)
+// FUNC_ALIAS :: ufbx_pack_version
+
+NO_INDEX :: (u32)0
+
+VALUE :: 20010
+// VALUE_STRING :: #VALUE
+
+CINDEX_VERSION_MAJOR :: 0
+CINDEX_VERSION_MINOR :: 64
+
+// CINDEX_VERSION_STRING :: #CINDEX_VERSION_MAJOR "." #CINDEX_VERSION_MINOR
+
+// line comment one line
+LINE_COMMENT_ONE :: 1
+
+// line comment two lines
+// the other line
+LINE_COMMENT_TWO :: 2
+
+/* block comment one line */
+BLOCK_COMMENT_ONE :: 1
+
+/* block comment two lines
+ * the other line
+ */
+BLOCK_COMMENT_TWO :: 2
+
+END_LINE_COMMENT :: 12 // end line comment
+END_LINE_BLOCK_COMMENT :: 34 /* end line block comment */
+BELOW_BLOCK_COMMENT :: 56 /* inline block comment on the line above */
+
+BLOCK_ABOVE_SECTION :: 78
+
+////////////////////////////////////////////////////////////////////////////////
+//// Section header
+////////////////////////////////////////////////////////////////////////////////
+SECTIONED_ONE :: 1 << 0 /* end line */
+SECTIONED_TWO :: 1 << 1 /* end line */
+
+Color :: struct {
+ r: i32,
+ g: i32,
+ b: i32,
+ a: i32,
+}
+
+HasBool :: struct {
+ a: bool,
+}
+
+my_time :: libc.time_t
+
+// Should add a bindgen.sjson with `remove_type_prefix = "test_"
+// typedef struct test_time_t {
+// int seconds;
+// } test_time_t;
+simple_typedef :: i32
+
+void_typedef :: struct {}
+