Tics is a Rigid Body Physics Library.
If you came here from my thesis and want to look at the Geometric Algebra implementation, you should go back to commit 2bb376a0e6475f3ed2ead2e03fb818270a6aa55d .
cmake -S . -B build/
cmake --build build/
# Run demos
cd build/bin/
./playgroundCompiler choice: clang is sometimes faster with OpenMP. gcc is faster for SIMD. gcc seems to be the better choice.
cmake -S . -B build_release/ -DCMAKE_BUILD_TYPE=Release -DTICS_ENABLE_DEBUG_VIEW=OFF -DCMAKE_C_COMPILER=gcc
cmake --build build_release/ --config Release
# Run demos
cd build_release/bin
./playgroundTo build the tics static library without any extra stuff:
cmake -S . -B build_lib/ -DTICS_BUILD_DEMOS=OFF -DTICS_BUILD_TESTS=OFF -DTICS_ENABLE_DEBUG_VIEW=OFF -DTICS_ENABLE_PROFILER=OFF -DTICS_BUILD_TOOL_GLB2C=OFF
cmake --build build_lib/see tests/README.md
Blick is Tics' debug visualization tool. It is enabled by default and automatically spawns a separate window with its own debug view into the physics world. It can draw many debug primitives (like points, arrows, AABBs) that can be used to visualize positions, velocities, intersections. Unlike traditional debug drawing, Blick runs as a separate process, communicating via shared memory. This solves debugging frustrations:
- You can pause the simulation in a conventional debugger and the Blick window remains responsive and you can move the camera around and inspect the frozen scene. You can even step through your application and, for example, watch the bounding volume hierarchy grow.
- If the physics engine crashes, Blick stays open, preserving the final state. You can inspect the "crime scene" to understand exactly what caused the crash.
- You can visualize simulations that don't include a graphics context themself, such as tests.
- Rendering debug primitives happens asynchronously. The simulation is not slowed down by the rendering overhead of drawing thousands of contact points or AABBs.
It's created for Tics, but could be used to debug other 3D applications too.
Unfortunately, Blick is currently Unix-only
glb2c is a tool that converts a glb file into a bunch of arrays in C syntax that can be pasted into your source code. Allows quickly embedding models without loading models at runtime.
cmake -S . -B build/ -DTICS_BUILD_TOOL_GLB2C=ON
cmake --build build/
# Run. You can also specify multiple glb files
./build/bin/glb2c models/cube.glb- Remove TICS_GA (Rip)
- Port to C
- Create wrapper tics_math.h
- Replace Terathon math with own implementation
- Update the public interface to a C API and create wrapper around the C++ implementation
- Rewrite (gradually rot out the C++ internals from the inside)
- Data-Oriented
tics_world- Replace std::vector with
stb_ds.hor manual malloc - "Swap and Pop"
- Replace std::vector with
- world step
- Port dynamics
- Port collision detection
- Port collision response
- Impulse solver
- Position solver
- Data-Oriented
- Tooling
- Debug Visualizer that runs as a separate process and inspects the running simulation using POSIX shared memory. Enables live viewing of debug data with a controllable camera while the physics process is being debugged with a conventional debugger.
- Add protocol for it (inter process communication)
- Create a fake debug drawer to test it with
- Actually make it draw stuff (points and lines)
- Make it a standalone tool (Blick)
- Implement all primitives
- Add functionality to assign meshes to shape ids (called by application code). Otherwise we can't really render meshes, because tics only stores points
- Layers: Objects have a layer that can be toggled in the viewer
- Draw transparent shapes correctly without z-buffering (and with add alpha blending?)
- Configuration if it should auto-close on crash of main app
- Make it platform independent
- Toggle between face culling options
- tool that can convert glb to c arrays
- Simple text-only profiler using macros to define zones in the code
- Debug Visualizer that runs as a separate process and inspects the running simulation using POSIX shared memory. Enables live viewing of debug data with a controllable camera while the physics process is being debugged with a conventional debugger.
- Improve collision response to be more stable
- no sudden jumping objects, no spinning
- Sort collisions so the result is deterministic (even with Multi-Threading)
- Change to this loop
- 1. Apply forces (gravity, friction) to velocity.
- 2. Collision detection
- 3. Collision response. Calculate impulses required to correct velocity. Apply impulses immediately to velocity.
- 4. Position integration (kinematics, as in not based on forces or impulses but only on velocity).
- do resolve_penetrations after applying velocites so we don't double correct. needs check if objects are still penetrating by storing local a and b and transforming again.
- Is this a good idea? since we are teleporting we might break stuff. maybe baumgarte stabilization is better.
- Improve resting contacts
- Add Iteration loop (sequential impulses) to improve objects in touch with multiple objects (stack boxes)
- Add Warm Starting? Would improve resting contacts, but requires storing collisions across steps.
- Add restitution threshold: collisions with a low relative velocity are treated as resting -> velocity = 0
- On certain collision cases report multiple collisions (improves resting collisions). All cases:
- edge vs edge (parallel): 2 points. project one edge onto the other and find overlapping segment endpoints.
- edge vs face: 2 points. clip edge against boundaries of face (Sutherland-Hodgman?)
- face vs face: 3+ points (polygon, maybe limit to some number?). clip one face against other (Sutherland-Hodgman)
- Testing
- Setup
- Test public API
- Test dynamics
- Test collision detection
- More collision detection tests
- edge vs edge
- parallel face vs face (resting contact, should this return multiple contact points?)
- touching: objects only touch - should consistently report a hit (implemented with skin width)
- shape completely inside another
- needle-plate: a very tiny object against another very big object
- Test collision response
- Portability
- Remove explicit SIMD (its not faster than autovec anyways)
- Use Function Multiversioning for autovectorized functions
__attribute__((target_clones("avx2","sse2","default"))) - compmile with
-march=x86-64to be explicit about compatibility (guarantees SSE, but function multiversioning also provides AVX versions)
- Performance Optimization
- Separate list for static and rigid bodies (beneficial for broadphase integration, only update AABBs for rigid bodies)
- On convex hull import, remove duplicate vertices. Enables importing flat shaded geometry without performance penalty.
- tightly packed vertex data for shapes
- SoA instead of AoS for bodies
- Broadphase
- structure of array for AABBs (cache-locality)
- SIMD
- individual arrays for min_x, min_y, min_z, max_x, max_y, max_z
- check 1 obj against 8 in 1 cycle
- list of dynamic indices -> outer loop: dynamic indices, inner loop: all indices
- first: dynamic = rigid bodies
- then: dynamic = objects that actually moved
- Multi-Threading Narrow Phase
- Improve GJK
- https://dl.acm.org/doi/10.1145/3072959.3083724
- "GJK algorithms are often used incrementally in simulation systems and video games. In this mode, the final simplex from a previous solution is used as the initial guess in the next iteration"
- Features
- collision shapes
- sphere
- sphere vs sphere
- sphere vs convex: use gjk for point (sphere center) vs convex, then use the result to find collision points (instead of EPA). or if center is inside convex shape, use EPA.
- sphere
- combine shapes (enables concave shapes)
- apply_impulse function that applies impulse at specific location
- setters and getters for rigid body and static body properties
- on_collision_enter + on_collision_exit
- Areas (only detect collisions)
- Bodies that are moved externally, but can push rigid bodies
- Teleporting and swapping shapes -> check for intersection and reposition if required (position solver?)
- physics materials
- collision shapes