Browse Source

Initial commit

Lucien Greathouse 1 year ago
commit
39b33067d3
15 changed files with 938 additions and 0 deletions
  1. 2 0
      .gitignore
  2. 3 0
      .gitmodules
  3. 19 0
      CMakeLists.txt
  4. 350 0
      Cargo.lock
  5. 3 0
      Cargo.toml
  6. 297 0
      JoltC/Enums.h
  7. 3 0
      JoltC/Functions.h
  8. 8 0
      JoltC/JoltC.cpp
  9. 4 0
      JoltC/JoltC.h
  10. 47 0
      JoltC/Test.cpp
  11. 1 0
      JoltPhysics
  12. 21 0
      LICENSE
  13. 28 0
      README.md
  14. 14 0
      generate/Cargo.toml
  15. 138 0
      generate/src/main.rs

+ 2 - 0
.gitignore

@@ -0,0 +1,2 @@
+/build*
+/target

+ 3 - 0
.gitmodules

@@ -0,0 +1,3 @@
+[submodule "JoltPhysics"]
+	path = JoltPhysics
+	url = ../../jrouwe/JoltPhysics.git

+ 19 - 0
CMakeLists.txt

@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 3.16 FATAL_ERROR)
+
+project(JoltC VERSION 0.1
+    DESCRIPTION "C Wrapper for Jolt Physics"
+    LANGUAGES CXX)
+
+add_library(joltc STATIC
+    JoltC/JoltC.h
+    JoltC/JoltC.cpp
+    JoltC/Enums.h
+    JoltC/Functions.h
+    JoltC/Test.cpp
+)
+
+target_compile_features(joltc PUBLIC cxx_std_17)
+target_include_directories(joltc PUBLIC . JoltPhysics)
+target_link_libraries(joltc PUBLIC JoltPhysics)
+
+add_subdirectory(JoltPhysics/Build)

+ 350 - 0
Cargo.lock

@@ -0,0 +1,350 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "aho-corasick"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "anstream"
+version = "0.6.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb"
+dependencies = [
+ "anstyle",
+ "anstyle-parse",
+ "anstyle-query",
+ "anstyle-wincon",
+ "colorchoice",
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc"
+
+[[package]]
+name = "anstyle-parse"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c"
+dependencies = [
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle-query"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648"
+dependencies = [
+ "windows-sys",
+]
+
+[[package]]
+name = "anstyle-wincon"
+version = "3.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7"
+dependencies = [
+ "anstyle",
+ "windows-sys",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.82"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519"
+
+[[package]]
+name = "autocfg"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80"
+
+[[package]]
+name = "clap"
+version = "4.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0"
+dependencies = [
+ "clap_builder",
+ "clap_derive",
+]
+
+[[package]]
+name = "clap_builder"
+version = "4.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "clap_lex",
+ "strsim",
+]
+
+[[package]]
+name = "clap_derive"
+version = "4.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "clap_lex"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
+
+[[package]]
+name = "colorchoice"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
+
+[[package]]
+name = "fs-err"
+version = "2.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "88a41f105fe1d5b6b34b2055e3dc59bb79b46b48b2040b9e6c7b4b5de097aa41"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "generate"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "clap",
+ "fs-err",
+ "heck",
+ "regex",
+ "walkdir",
+]
+
+[[package]]
+name = "heck"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+
+[[package]]
+name = "memchr"
+version = "2.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.80"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a56dea16b0a29e94408b9aa5e2940a4eedbd128a1ba20e8f7ae60fd3d465af0e"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "regex"
+version = "1.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
+
+[[package]]
+name = "same-file"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "strsim"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
+
+[[package]]
+name = "syn"
+version = "2.0.59"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4a6531ffc7b071655e4ce2e04bd464c4830bb585a61cabb96cf808f05172615a"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+
+[[package]]
+name = "utf8parse"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
+
+[[package]]
+name = "walkdir"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
+dependencies = [
+ "same-file",
+ "winapi-util",
+]
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_gnullvm",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"

+ 3 - 0
Cargo.toml

@@ -0,0 +1,3 @@
+[workspace]
+resolver = "2"
+members = ["generate"]

+ 297 - 0
JoltC/Enums.h

@@ -0,0 +1,297 @@
+#pragma once
+
+#include <stdint.h>
+
+#ifndef ENSURE_TESTS
+    #define ENSURE_EQUAL(a, b)
+    #define ENSURE_ENUM_EQ(a, b)
+    #define ENSURE_SIZE_ALIGN(a, b)
+    #define ENSURE_FIELD(a, b, c, d)
+#endif
+
+// JPC_JobSystem_Create()
+enum JPC_JobSystemConstants {
+    JPC_MAX_PHYSICS_JOBS     = 2048,
+    JPC_MAX_PHYSICS_BARRIERS = 8
+};
+
+ENSURE_EQUAL(JPC_MAX_PHYSICS_JOBS, JPH::cMaxPhysicsJobs);
+ENSURE_EQUAL(JPC_MAX_PHYSICS_BARRIERS, JPH::cMaxPhysicsBarriers);
+
+typedef enum JPC_ShapeType: uint8_t {
+    JPC_SHAPE_TYPE_CONVEX,
+    JPC_SHAPE_TYPE_COMPOUND,
+    JPC_SHAPE_TYPE_DECORATED,
+    JPC_SHAPE_TYPE_MESH,
+    JPC_SHAPE_TYPE_HEIGHT_FIELD,
+    JPC_SHAPE_TYPE_SOFTBODY,
+    JPC_SHAPE_TYPE_USER1,
+    JPC_SHAPE_TYPE_USER2,
+    JPC_SHAPE_TYPE_USER3,
+    JPC_SHAPE_TYPE_USER4,
+} JPC_ShapeType;
+
+ENSURE_ENUM_EQ(JPC_SHAPE_TYPE_CONVEX, JPH::EShapeType::Convex)
+ENSURE_ENUM_EQ(JPC_SHAPE_TYPE_COMPOUND, JPH::EShapeType::Compound)
+ENSURE_ENUM_EQ(JPC_SHAPE_TYPE_DECORATED, JPH::EShapeType::Decorated)
+ENSURE_ENUM_EQ(JPC_SHAPE_TYPE_MESH, JPH::EShapeType::Mesh)
+ENSURE_ENUM_EQ(JPC_SHAPE_TYPE_HEIGHT_FIELD, JPH::EShapeType::HeightField)
+ENSURE_ENUM_EQ(JPC_SHAPE_TYPE_USER1, JPH::EShapeType::User1)
+ENSURE_ENUM_EQ(JPC_SHAPE_TYPE_USER2, JPH::EShapeType::User2)
+ENSURE_ENUM_EQ(JPC_SHAPE_TYPE_USER3, JPH::EShapeType::User3)
+ENSURE_ENUM_EQ(JPC_SHAPE_TYPE_USER4, JPH::EShapeType::User4)
+
+typedef enum JPC_ShapeSubType: uint8_t {
+    JPC_SHAPE_SUB_TYPE_SPHERE,
+    JPC_SHAPE_SUB_TYPE_BOX,
+    JPC_SHAPE_SUB_TYPE_TRIANGLE,
+    JPC_SHAPE_SUB_TYPE_CAPSULE,
+    JPC_SHAPE_SUB_TYPE_TAPEREDCAPSULE,
+    JPC_SHAPE_SUB_TYPE_CYLINDER,
+    JPC_SHAPE_SUB_TYPE_CONVEX_HULL,
+    JPC_SHAPE_SUB_TYPE_STATIC_COMPOUND,
+    JPC_SHAPE_SUB_TYPE_MUTABLE_COMPOUND,
+    JPC_SHAPE_SUB_TYPE_ROTATED_TRANSLATED,
+    JPC_SHAPE_SUB_TYPE_SCALED,
+    JPC_SHAPE_SUB_TYPE_OFFSET_CENTER_OF_MASS,
+    JPC_SHAPE_SUB_TYPE_MESH,
+    JPC_SHAPE_SUB_TYPE_HEIGHT_FIELD,
+    JPC_SHAPE_SUB_TYPE_SOFT_BODY,
+    JPC_SHAPE_SUB_TYPE_USER1,
+    JPC_SHAPE_SUB_TYPE_USER2,
+    JPC_SHAPE_SUB_TYPE_USER3,
+    JPC_SHAPE_SUB_TYPE_USER4,
+    JPC_SHAPE_SUB_TYPE_USER5,
+    JPC_SHAPE_SUB_TYPE_USER6,
+    JPC_SHAPE_SUB_TYPE_USER7,
+    JPC_SHAPE_SUB_TYPE_USER8,
+    JPC_SHAPE_SUB_TYPE_USER_CONVEX1,
+    JPC_SHAPE_SUB_TYPE_USER_CONVEX2,
+    JPC_SHAPE_SUB_TYPE_USER_CONVEX3,
+    JPC_SHAPE_SUB_TYPE_USER_CONVEX4,
+    JPC_SHAPE_SUB_TYPE_USER_CONVEX5,
+    JPC_SHAPE_SUB_TYPE_USER_CONVEX6,
+    JPC_SHAPE_SUB_TYPE_USER_CONVEX7,
+    JPC_SHAPE_SUB_TYPE_USER_CONVEX8,
+} JPC_ShapeSubType;
+
+ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_SPHERE, JPH::EShapeSubType::Sphere)
+ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_BOX, JPH::EShapeSubType::Box)
+ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_TRIANGLE, JPH::EShapeSubType::Triangle)
+ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_CAPSULE, JPH::EShapeSubType::Capsule)
+ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_TAPEREDCAPSULE, JPH::EShapeSubType::TaperedCapsule)
+ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_CYLINDER, JPH::EShapeSubType::Cylinder)
+ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_CONVEX_HULL, JPH::EShapeSubType::ConvexHull)
+ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_STATIC_COMPOUND, JPH::EShapeSubType::StaticCompound)
+ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_MUTABLE_COMPOUND, JPH::EShapeSubType::MutableCompound)
+ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_ROTATED_TRANSLATED, JPH::EShapeSubType::RotatedTranslated)
+ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_SCALED, JPH::EShapeSubType::Scaled)
+ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_OFFSET_CENTER_OF_MASS, JPH::EShapeSubType::OffsetCenterOfMass)
+ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_MESH, JPH::EShapeSubType::Mesh)
+ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_HEIGHT_FIELD, JPH::EShapeSubType::HeightField)
+ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_SOFT_BODY, JPH::EShapeSubType::SoftBody)
+ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_USER1, JPH::EShapeSubType::User1)
+ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_USER2, JPH::EShapeSubType::User2)
+ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_USER3, JPH::EShapeSubType::User3)
+ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_USER4, JPH::EShapeSubType::User4)
+ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_USER5, JPH::EShapeSubType::User5)
+ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_USER6, JPH::EShapeSubType::User6)
+ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_USER7, JPH::EShapeSubType::User7)
+ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_USER8, JPH::EShapeSubType::User8)
+ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_USER_CONVEX1, JPH::EShapeSubType::UserConvex1)
+ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_USER_CONVEX2, JPH::EShapeSubType::UserConvex2)
+ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_USER_CONVEX3, JPH::EShapeSubType::UserConvex3)
+ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_USER_CONVEX4, JPH::EShapeSubType::UserConvex4)
+ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_USER_CONVEX5, JPH::EShapeSubType::UserConvex5)
+ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_USER_CONVEX6, JPH::EShapeSubType::UserConvex6)
+ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_USER_CONVEX7, JPH::EShapeSubType::UserConvex7)
+ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_USER_CONVEX8, JPH::EShapeSubType::UserConvex8)
+
+typedef enum JPC_PhysicsUpdateError: uint32_t {
+    JPC_PHYSICS_UPDATE_ERROR_NONE                     = 0,
+    JPC_PHYSICS_UPDATE_ERROR_MANIFOLD_CACHE_FULL      = 1 << 0,
+    JPC_PHYSICS_UPDATE_ERROR_BODY_PAIR_CACHE_FULL     = 1 << 1,
+    JPC_PHYSICS_UPDATE_ERROR_CONTACT_CONSTRAINTS_FULL = 1 << 2,
+} JPC_PhysicsUpdateError;
+
+ENSURE_ENUM_EQ(JPC_PHYSICS_UPDATE_ERROR_NONE, JPH::EPhysicsUpdateError::None)
+ENSURE_ENUM_EQ(JPC_PHYSICS_UPDATE_ERROR_MANIFOLD_CACHE_FULL, JPH::EPhysicsUpdateError::ManifoldCacheFull)
+ENSURE_ENUM_EQ(JPC_PHYSICS_UPDATE_ERROR_BODY_PAIR_CACHE_FULL, JPH::EPhysicsUpdateError::BodyPairCacheFull)
+ENSURE_ENUM_EQ(JPC_PHYSICS_UPDATE_ERROR_CONTACT_CONSTRAINTS_FULL, JPH::EPhysicsUpdateError::ContactConstraintsFull)
+
+typedef enum JPC_ConstraintType: uint32_t {
+    JPC_CONSTRAINT_TYPE_CONSTRAINT,
+    JPC_CONSTRAINT_TYPE_TWO_BODY_CONSTRAINT,
+} JPC_ConstraintType;
+
+ENSURE_ENUM_EQ(JPC_CONSTRAINT_TYPE_CONSTRAINT, JPH::EConstraintType::Constraint)
+ENSURE_ENUM_EQ(JPC_CONSTRAINT_TYPE_TWO_BODY_CONSTRAINT, JPH::EConstraintType::TwoBodyConstraint)
+
+typedef enum JPC_ConstraintSubType: uint32_t {
+    JPC_CONSTRAINT_SUB_TYPE_FIXED,
+    JPC_CONSTRAINT_SUB_TYPE_POINT,
+    JPC_CONSTRAINT_SUB_TYPE_HINGE,
+    JPC_CONSTRAINT_SUB_TYPE_SLIDER,
+    JPC_CONSTRAINT_SUB_TYPE_DISTANCE,
+    JPC_CONSTRAINT_SUB_TYPE_CONE,
+    JPC_CONSTRAINT_SUB_TYPE_SWING_TWIST,
+    JPC_CONSTRAINT_SUB_TYPE_SIX_DOF,
+    JPC_CONSTRAINT_SUB_TYPE_PATH,
+    JPC_CONSTRAINT_SUB_TYPE_VEHICLE,
+    JPC_CONSTRAINT_SUB_TYPE_RACK_AND_PINION,
+    JPC_CONSTRAINT_SUB_TYPE_GEAR,
+    JPC_CONSTRAINT_SUB_TYPE_PULLEY,
+    JPC_CONSTRAINT_SUB_TYPE_USER1,
+    JPC_CONSTRAINT_SUB_TYPE_USER2,
+    JPC_CONSTRAINT_SUB_TYPE_USER3,
+    JPC_CONSTRAINT_SUB_TYPE_USER4,
+} JPC_ConstraintSubType;
+
+ENSURE_ENUM_EQ(JPC_CONSTRAINT_SUB_TYPE_FIXED, JPH::EConstraintSubType::Fixed)
+ENSURE_ENUM_EQ(JPC_CONSTRAINT_SUB_TYPE_POINT, JPH::EConstraintSubType::Point)
+ENSURE_ENUM_EQ(JPC_CONSTRAINT_SUB_TYPE_HINGE, JPH::EConstraintSubType::Hinge)
+ENSURE_ENUM_EQ(JPC_CONSTRAINT_SUB_TYPE_SLIDER, JPH::EConstraintSubType::Slider)
+ENSURE_ENUM_EQ(JPC_CONSTRAINT_SUB_TYPE_DISTANCE, JPH::EConstraintSubType::Distance)
+ENSURE_ENUM_EQ(JPC_CONSTRAINT_SUB_TYPE_CONE, JPH::EConstraintSubType::Cone)
+ENSURE_ENUM_EQ(JPC_CONSTRAINT_SUB_TYPE_SWING_TWIST, JPH::EConstraintSubType::SwingTwist)
+ENSURE_ENUM_EQ(JPC_CONSTRAINT_SUB_TYPE_SIX_DOF, JPH::EConstraintSubType::SixDOF)
+ENSURE_ENUM_EQ(JPC_CONSTRAINT_SUB_TYPE_PATH, JPH::EConstraintSubType::Path)
+ENSURE_ENUM_EQ(JPC_CONSTRAINT_SUB_TYPE_VEHICLE, JPH::EConstraintSubType::Vehicle)
+ENSURE_ENUM_EQ(JPC_CONSTRAINT_SUB_TYPE_RACK_AND_PINION, JPH::EConstraintSubType::RackAndPinion)
+ENSURE_ENUM_EQ(JPC_CONSTRAINT_SUB_TYPE_GEAR, JPH::EConstraintSubType::Gear)
+ENSURE_ENUM_EQ(JPC_CONSTRAINT_SUB_TYPE_PULLEY, JPH::EConstraintSubType::Pulley)
+ENSURE_ENUM_EQ(JPC_CONSTRAINT_SUB_TYPE_USER1, JPH::EConstraintSubType::User1)
+ENSURE_ENUM_EQ(JPC_CONSTRAINT_SUB_TYPE_USER2, JPH::EConstraintSubType::User2)
+ENSURE_ENUM_EQ(JPC_CONSTRAINT_SUB_TYPE_USER3, JPH::EConstraintSubType::User3)
+ENSURE_ENUM_EQ(JPC_CONSTRAINT_SUB_TYPE_USER4, JPH::EConstraintSubType::User4)
+
+typedef enum JPC_ConstraintSpace: uint32_t {
+    JPC_CONSTRAINT_SPACE_LOCAL_TO_BODY_COM,
+    JPC_CONSTRAINT_SPACE_WORLD_SPACE,
+} JPC_ConstraintSpace;
+
+ENSURE_ENUM_EQ(JPC_CONSTRAINT_SPACE_LOCAL_TO_BODY_COM, JPH::EConstraintSpace::LocalToBodyCOM)
+ENSURE_ENUM_EQ(JPC_CONSTRAINT_SPACE_WORLD_SPACE, JPH::EConstraintSpace::WorldSpace)
+
+typedef enum JPC_MotionType: uint8_t {
+    JPC_MOTION_TYPE_STATIC,
+    JPC_MOTION_TYPE_KINEMATIC,
+    JPC_MOTION_TYPE_DYNAMIC,
+} JPC_MotionType;
+
+ENSURE_ENUM_EQ(JPC_MOTION_TYPE_STATIC,    JPH::EMotionType::Static)
+ENSURE_ENUM_EQ(JPC_MOTION_TYPE_KINEMATIC, JPH::EMotionType::Kinematic)
+ENSURE_ENUM_EQ(JPC_MOTION_TYPE_DYNAMIC,   JPH::EMotionType::Dynamic)
+
+typedef enum JPC_MotionQuality: uint8_t {
+    JPC_MOTION_QUALITY_DISCRETE,
+    JPC_MOTION_QUALITY_LINEAR_CAST,
+} JPC_MotionQuality;
+
+ENSURE_ENUM_EQ(JPC_MOTION_QUALITY_DISCRETE,    JPH::EMotionQuality::Discrete)
+ENSURE_ENUM_EQ(JPC_MOTION_QUALITY_LINEAR_CAST, JPH::EMotionQuality::LinearCast)
+
+typedef enum JPC_OverrideMassProperties: uint8_t {
+    JPC_OVERRIDE_MASS_PROPS_CALC_MASS_INERTIA,
+    JPC_OVERRIDE_MASS_PROPS_CALC_INERTIA,
+    JPC_OVERRIDE_MASS_PROPS_MASS_INERTIA_PROVIDED,
+} JPC_OverrideMassProperties;
+
+ENSURE_ENUM_EQ(JPC_OVERRIDE_MASS_PROPS_CALC_MASS_INERTIA,
+               JPH::EOverrideMassProperties::CalculateMassAndInertia);
+ENSURE_ENUM_EQ(JPC_OVERRIDE_MASS_PROPS_CALC_INERTIA,
+               JPH::EOverrideMassProperties::CalculateInertia);
+ENSURE_ENUM_EQ(JPC_OVERRIDE_MASS_PROPS_MASS_INERTIA_PROVIDED,
+               JPH::EOverrideMassProperties::MassAndInertiaProvided);
+
+typedef enum JPC_GroundState: uint32_t {
+    JPC_CHARACTER_GROUND_STATE_ON_GROUND,
+    JPC_CHARACTER_GROUND_STATE_ON_STEEP_GROUND,
+    JPC_CHARACTER_GROUND_STATE_NOT_SUPPORTED,
+    JPC_CHARACTER_GROUND_STATE_IN_AIR,
+} JPC_GroundState;
+
+// ENSURE_ENUM_EQ(JPC_CHARACTER_GROUND_STATE_ON_GROUND, JPH::EGroundState::OnGround)
+// ENSURE_ENUM_EQ(JPC_CHARACTER_GROUND_STATE_ON_STEEP_GROUND, JPH::EGroundState::OnSteepGround)
+// ENSURE_ENUM_EQ(JPC_CHARACTER_GROUND_STATE_NOT_SUPPORTED, JPH::EGroundState::NotSupported)
+// ENSURE_ENUM_EQ(JPC_CHARACTER_GROUND_STATE_IN_AIR, JPH::EGroundState::InAir)
+
+typedef enum JPC_Activation: uint32_t {
+    JPC_ACTIVATION_ACTIVATE      = 0,
+    JPC_ACTIVATION_DONT_ACTIVATE = 1,
+} JPC_Activation;
+
+ENSURE_ENUM_EQ(JPC_ACTIVATION_ACTIVATE,      JPH::EActivation::Activate)
+ENSURE_ENUM_EQ(JPC_ACTIVATION_DONT_ACTIVATE, JPH::EActivation::DontActivate)
+
+typedef enum JPC_ValidateResult: uint32_t {
+    JPC_VALIDATE_RESULT_ACCEPT_ALL_CONTACTS,
+    JPC_VALIDATE_RESULT_ACCEPT_CONTACT,
+    JPC_VALIDATE_RESULT_REJECT_CONTACT,
+    JPC_VALIDATE_RESULT_REJECT_ALL_CONTACTS,
+} JPC_ValidateResult;
+
+ENSURE_ENUM_EQ(JPC_VALIDATE_RESULT_ACCEPT_ALL_CONTACTS,
+               JPH::ValidateResult::AcceptAllContactsForThisBodyPair);
+ENSURE_ENUM_EQ(JPC_VALIDATE_RESULT_ACCEPT_CONTACT,
+               JPH::ValidateResult::AcceptContact);
+ENSURE_ENUM_EQ(JPC_VALIDATE_RESULT_REJECT_CONTACT,
+               JPH::ValidateResult::RejectContact);
+ENSURE_ENUM_EQ(JPC_VALIDATE_RESULT_REJECT_ALL_CONTACTS,
+               JPH::ValidateResult::RejectAllContactsForThisBodyPair);
+
+typedef enum JPC_BackFaceMode: uint8_t {
+    JPC_BACK_FACE_IGNORE,
+    JPC_BACK_FACE_COLLIDE,
+} JPC_BackFaceMode;
+
+ENSURE_ENUM_EQ(JPC_BACK_FACE_IGNORE, JPH::EBackFaceMode::IgnoreBackFaces)
+ENSURE_ENUM_EQ(JPC_BACK_FACE_COLLIDE, JPH::EBackFaceMode::CollideWithBackFaces)
+
+typedef enum JPC_BodyType: uint8_t {
+    JPC_BODY_TYPE_RIGID_BODY = 0,
+    JPC_BODY_TYPE_SOFT_BODY  = 1,
+} JPC_BodyType;
+
+ENSURE_ENUM_EQ(JPC_BODY_TYPE_RIGID_BODY, JPH::EBodyType::RigidBody)
+ENSURE_ENUM_EQ(JPC_BODY_TYPE_SOFT_BODY, JPH::EBodyType::SoftBody)
+
+typedef enum JPC_AllowedDOFs: uint8_t {
+    JPC_ALLOWED_DOFS_NONE         = 0b000000,
+    JPC_ALLOWED_DOFS_ALL          = 0b111111,
+    JPC_ALLOWED_DOFS_TRANSLATIONX = 0b000001,
+    JPC_ALLOWED_DOFS_TRANSLATIONY = 0b000010,
+    JPC_ALLOWED_DOFS_TRANSLATIONZ = 0b000100,
+    JPC_ALLOWED_DOFS_ROTATIONX    = 0b001000,
+    JPC_ALLOWED_DOFS_ROTATIONY    = 0b010000,
+    JPC_ALLOWED_DOFS_ROTATIONZ    = 0b100000,
+    JPC_ALLOWED_DOFS_PLANE2D      = JPC_ALLOWED_DOFS_TRANSLATIONX | JPC_ALLOWED_DOFS_TRANSLATIONY | JPC_ALLOWED_DOFS_ROTATIONZ,
+} JPC_AllowedDOFs;
+
+ENSURE_ENUM_EQ(JPC_ALLOWED_DOFS_NONE, JPH::EAllowedDOFs::None)
+ENSURE_ENUM_EQ(JPC_ALLOWED_DOFS_ALL, JPH::EAllowedDOFs::All)
+ENSURE_ENUM_EQ(JPC_ALLOWED_DOFS_TRANSLATIONX, JPH::EAllowedDOFs::TranslationX)
+ENSURE_ENUM_EQ(JPC_ALLOWED_DOFS_TRANSLATIONY, JPH::EAllowedDOFs::TranslationY)
+ENSURE_ENUM_EQ(JPC_ALLOWED_DOFS_TRANSLATIONZ, JPH::EAllowedDOFs::TranslationZ)
+ENSURE_ENUM_EQ(JPC_ALLOWED_DOFS_ROTATIONX, JPH::EAllowedDOFs::RotationX)
+ENSURE_ENUM_EQ(JPC_ALLOWED_DOFS_ROTATIONY, JPH::EAllowedDOFs::RotationY)
+ENSURE_ENUM_EQ(JPC_ALLOWED_DOFS_ROTATIONZ, JPH::EAllowedDOFs::RotationZ)
+ENSURE_ENUM_EQ(JPC_ALLOWED_DOFS_PLANE2D, JPH::EAllowedDOFs::Plane2D)
+
+typedef enum JPC_Features: uint32_t {
+    JPC_FEATURE_DOUBLE_PRECISION = (1 << 0),
+    JPC_FEATURE_NEON = (1 << 1),
+    JPC_FEATURE_SSE = (1 << 2),
+    JPC_FEATURE_SSE4_1 = (1 << 3),
+    JPC_FEATURE_SSE4_2 = (1 << 4),
+    JPC_FEATURE_AVX = (1 << 5),
+    JPC_FEATURE_AVX2 = (1 << 6),
+    JPC_FEATURE_AVX512 = (1 << 7),
+    JPC_FEATURE_F16C = (1 << 8),
+    JPC_FEATURE_LZCNT = (1 << 9),
+    JPC_FEATURE_TZCNT = (1 << 10),
+    JPC_FEATURE_FMADD = (1 << 11),
+    JPC_FEATURE_PLATFORM_DETERMINISTIC = (1 << 12),
+    JPC_FEATURE_FLOATING_POINT_EXCEPTIONS = (1 << 13),
+    JPC_FEATURE_DEBUG = (1 << 14),
+} JPC_Features;

+ 3 - 0
JoltC/Functions.h

@@ -0,0 +1,3 @@
+#pragma once
+
+void JPC_RegisterDefaultAllocator();

+ 8 - 0
JoltC/JoltC.cpp

@@ -0,0 +1,8 @@
+#include "JoltC/JoltC.h"
+
+#include "Jolt/Jolt.h"
+#include "Jolt/Core/Memory.h"
+
+void JPC_RegisterDefaultAllocator() {
+	JPH::RegisterDefaultAllocator();
+}

+ 4 - 0
JoltC/JoltC.h

@@ -0,0 +1,4 @@
+#pragma once
+
+#include "JoltC/Enums.h"
+#include "JoltC/Functions.h"

+ 47 - 0
JoltC/Test.cpp

@@ -0,0 +1,47 @@
+#include <assert.h>
+#include <stddef.h>
+#include <type_traits>
+
+#include "Jolt/Jolt.h"
+#include "Jolt/Physics/Body/BodyCreationSettings.h"
+#include "Jolt/Physics/Body/BodyType.h"
+#include "Jolt/Physics/Body/MotionQuality.h"
+#include "Jolt/Physics/Body/MotionType.h"
+#include "Jolt/Physics/Character/CharacterBase.h"
+#include "Jolt/Physics/Collision/BackFaceMode.h"
+#include "Jolt/Physics/Collision/ContactListener.h"
+#include "Jolt/Physics/Collision/Shape/Shape.h"
+#include "Jolt/Physics/Constraints/Constraint.h"
+#include "Jolt/Physics/EActivation.h"
+#include "Jolt/Physics/EPhysicsUpdateError.h"
+#include "Jolt/Physics/PhysicsSettings.h"
+#include "Jolt/Physics/Body/AllowedDOFs.h"
+
+template<typename E>
+constexpr auto to_integral(E e) -> typename std::underlying_type<E>::type 
+{
+    return static_cast<typename std::underlying_type<E>::type>(e);
+}
+
+#define ENSURE_TESTS
+
+#define ENSURE_EQUAL(c_const, cpp_const) \
+    static_assert(c_const == cpp_const, #c_const " did not match " #cpp_const);
+
+#define ENSURE_ENUM_EQ(c_const, cpp_enum) \
+    static_assert(c_const == to_integral(cpp_enum), #c_const " did not match " #cpp_enum); \
+    static_assert(sizeof(c_const) == sizeof(cpp_enum), #c_const " did not have same size as " #cpp_enum);
+
+#define ENSURE_SIZE_ALIGN(type0, type1) \
+    static_assert(sizeof(type0) == sizeof(type1)); \
+    static_assert(alignof(type0) == alignof(type1));
+
+#define unsafe_offsetof(st, m) ((size_t) ( (char *)&((st *)(0))->m - (char *)0 ))
+#define unsafe_fieldtype(st, m) decltype((st *)(0)->m)
+
+#define ENSURE_FIELD(type0, field0, type1, field1) \
+    static_assert(unsafe_offsetof(type0, field0) == unsafe_offsetof(type1, field1), \
+        #type0 "." #field0 " did not have same offset as " #type1 "." #field1); \
+    ENSURE_SIZE_ALIGN(unsafe_fieldtype(type0, field0), unsafe_fieldtype(type1, field1));
+
+#include "JoltC/JoltC.h"

+ 1 - 0
JoltPhysics

@@ -0,0 +1 @@
+Subproject commit f2d1175432f8225450dea252322ba2dbaa83a370

+ 21 - 0
LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Portions Copyright (c) 2024 Second Half Games
+
+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.

+ 28 - 0
README.md

@@ -0,0 +1,28 @@
+# JoltC
+C wrapper for [Jolt Physics](https://github.com/jrouwe/JoltPhysics).
+
+## Building
+Use CMake:
+
+```bash
+cmake -B build
+cmake --build build
+```
+
+## Other C Wrapper
+Other C wrappers for Jolt Physics include:
+- "JoltC", part of the [zphysics] Zig library started by [Michal Ziulek][michal-ziulek]
+- "JoltC", part of the [jolt-rs] Rust library started by [cohaereo] and a fork of the zphysics C wrapper
+- "joltc", part of the [JoltPhysicsSharp] C# library started by [Amer Koleci][amerkoleci]
+
+The goal of this project is to be the first C wrapper around Jolt Physics that is not part of a larger binding project and to eliminate sources of unsoundness. It's intended to be useful for any other language-specific bindings and to reduce the need to duplicate work.
+
+## License
+See [LICENSE](LICENSE) for more details.
+
+[zphysics]: https://github.com/zig-gamedev/zig-gamedev/tree/main/libs/zphysics
+[jolt-rs]: https://github.com/cohaereo/jolt-rs
+[JoltPhysicsSharp]: https://github.com/amerkoleci/JoltPhysicsSharp
+[michal-ziulek]: https://github.com/michal-z
+[amerkoleci]: https://github.com/amerkoleci
+[cohaereo]: https://github.com/cohaereo

+ 14 - 0
generate/Cargo.toml

@@ -0,0 +1,14 @@
+[package]
+name = "generate"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+anyhow = "1.0.82"
+clap = { version = "4.5.4", features = ["derive"] }
+fs-err = "2.11.0"
+heck = "0.5.0"
+regex = "1.10.4"
+walkdir = "2.5.0"

+ 138 - 0
generate/src/main.rs

@@ -0,0 +1,138 @@
+use std::fmt::Write;
+
+use anyhow::Context;
+use fs_err as fs;
+use heck::AsShoutySnakeCase;
+use regex::Regex;
+use walkdir::WalkDir;
+
+static PREFIX: &str = "JPC";
+
+fn main() {
+    if let Err(err) = generate() {
+        eprintln!("Fatal error: {err:?}");
+    }
+}
+
+fn generate() -> anyhow::Result<()> {
+    let mut output = String::new();
+
+    for entry in WalkDir::new("JoltPhysics/Jolt") {
+        let entry = entry?;
+        let file_name = entry
+            .file_name()
+            .to_str()
+            .context("invalid UTF-8 in file name")?;
+
+        if file_name.ends_with(".h") {
+            println!("Processing {file_name}...");
+
+            let header = fs::read_to_string(entry.path())?;
+
+            convert_enums(&header, &mut output);
+            convert_classes(&header, &mut output);
+        }
+    }
+
+    eprintln!();
+    eprintln!();
+    eprintln!();
+    eprintln!("{output}");
+
+    Ok(())
+}
+
+fn convert_classes(header: &str, output: &mut String) {
+    let class = Regex::new(r"^\s*class\s*JPH_EXPORT\s*(\w+)").unwrap();
+    let field = Regex::new(r"^([\w<>])").unwrap();
+
+    struct WipClass {
+        cpp_name: String,
+        c_name: String,
+    }
+
+    let mut current_class = None;
+
+    for line in header.lines() {
+        if current_class.is_none() {
+            if let Some(captures) = class.captures(line) {
+                let name = &captures[1];
+                println!("Found exported class: {name}");
+
+                current_class = Some(WipClass {
+                    cpp_name: name.to_owned(),
+                    c_name: format!("{PREFIX}_{name}"),
+                });
+            }
+
+            continue;
+        }
+
+        if let Some(current) = &current_class {}
+    }
+}
+
+fn convert_enums(header: &str, output: &mut String) {
+    struct WipEnum {
+        cpp_name: String,
+        c_name: String,
+    }
+
+    let enum_class = Regex::new(r"^\s*enum\s+class\s+(\w+)(?:\s*:\s*(\w+))?").unwrap();
+    let enum_member = Regex::new(r"^\s*(\w+)(?:\s*=\s*(.+?),)?").unwrap();
+    let end_block = Regex::new(r"^\s*}").unwrap();
+
+    let mut current_enum = None;
+
+    for line in header.lines() {
+        if current_enum.is_none() {
+            if let Some(captures) = enum_class.captures(line) {
+                let mut name = &captures[1];
+                let repr = captures.get(2).map(|m| m.as_str());
+
+                // EShapeSubType => ShapeSubType
+                if name.starts_with('E') && name.as_bytes()[1].is_ascii_uppercase() {
+                    name = &name[1..];
+                }
+
+                let c_name = format!("{PREFIX}_{}", AsShoutySnakeCase(name));
+
+                if let Some(repr) = repr {
+                    writeln!(output, "typedef enum {c_name} : {repr} {{").unwrap();
+                } else {
+                    writeln!(output, "typedef enum {c_name} {{").unwrap();
+                }
+
+                current_enum = Some(WipEnum {
+                    cpp_name: name.to_owned(),
+                    c_name,
+                });
+
+                continue;
+            }
+
+            continue;
+        }
+
+        if let Some(current) = &current_enum {
+            if end_block.is_match(line) {
+                writeln!(output, "}} {};", current.c_name).unwrap();
+                current_enum = None;
+                continue;
+            }
+
+            if let Some(captures) = enum_member.captures(line) {
+                let name = &captures[1];
+                let value = captures.get(2).map(|m| m.as_str());
+
+                let c_name = format!("{}_{}", current.c_name, AsShoutySnakeCase(name));
+
+                if let Some(value) = value {
+                    writeln!(output, "    {} = {value},", c_name).unwrap();
+                } else {
+                    writeln!(output, "    {},", c_name).unwrap();
+                }
+            }
+        }
+    }
+}