diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c18960..053992e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,12 +4,67 @@ All notable changes to Box2Dxt are documented here. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -The native shim's ABI is tracked separately by `b2Version()` (currently `2`). +The native shim's ABI is tracked separately by `b2Version()` (currently `3`). ## [Unreleased] ### Added +- **Full Box2D v3.1.0 live-object API.** The binding now exposes essentially the + whole engine surface a script needs — ~240 new shim functions, each with a + `b2…` extension wrapper and, where it helps, a friendly `b2k…` Kit helper: + - **Sensors.** Non-solid trigger zones via a new **shape-def builder** + (`b2ShapeDefSensor`, `b2ShapeDefFilter`, `b2ShapeDefEnable*Events`, + `b2ShapeDefMaterialId` — one-shot options applied to the next shape). Sensor + overlaps arrive as `b2World_GetSensorEvents` (`b2SensorsUpdate` + accessors). + The Kit adds `b2kAddSensor` and `on b2kSensorEnter` / `on b2kSensorExit`. + - **Collision filtering.** `b2SetShapeFilter` + category/mask/group getters + (32 layers). The Kit adds a named-layer registry: `b2kDefineLayer`, + `b2kSetCategory`, `b2kSetMask`, `b2kSetCollisionGroup`, `b2kNoCollide`. + - **Chains.** Smooth multi-segment terrain (`b2CreateChain` + builder, + materials, segment enumeration; new chain handle table). Kit: `b2kChain`, + `b2kSmoothGround`. + - **More joints.** The **motor** joint (`b2MotorJoint`, drive a body to an + offset pose) and the **filter** joint (`b2FilterJoint`, disable a single + pair's collision), the generic joint surface (type, bodies, anchors, + constraint force/torque, collide-connected, wake), and the **complete + per-joint get/set** surface for all six joint types (springs, limits, + motors, readouts). Kit: `b2kMotorTo`. + - **World queries.** Overlap (AABB / point / circle / shape), ray-cast-**all** + (sorted), and shape-cast, surfaced through one shared result buffer + (`b2QueryCount`/`b2QueryBody`/…). Kit: `b2kOverlap`, `b2kOverlapCircle`, + `b2kRayHitAll`. + - **Events.** Contact **hit** events (impact point/normal/speed), **sensor** + events, and bulk **body-move** events (`b2BodiesUpdate` — an efficient way to + read every moved transform in one call). + - **World tuning / info.** Restitution & hit-event thresholds, contact/joint + tuning, max linear speed, warm-starting/speculative toggles, gravity getter, + `b2WorldExplode` (native blast), and profile/counters. Kit passthroughs plus + `b2kProfile`; `b2kExplode` is now native (old behaviour kept as + `b2kExplodeLegacy`). + - **Body extras.** World/local point & vector transforms, velocity-at-point, + force/impulse **at a point**, full mass-data get/set + `ApplyMassFromShapes`, + rotational inertia, local centre, kinematic target transform, sleep-enable, + per-body event flags, AABB, and shape/joint enumeration. + - **Shape extras.** Runtime geometry get/set for every shape type, material & + event-flag get/set, per-shape ray cast, AABB, closest point, mass data, and + sensor-overlap polling. +- **Kit completeness pass.** The Kit (`b2k…`) now covers the full core `b2…` + surface — every capability the binding exposes has a friendly + pixel/degree/screen-coordinate helper. New handlers: + - **Read state:** `b2kPosition(ctrl)` (body centre as `x,y` screen pixels — + the position partner of `b2kVelocity`), `b2kWorldCenter(ctrl)` (centre of + mass), `b2kGravityScale(ctrl)` and `b2kDamping(ctrl)` (getters for the + existing setters), and `b2kControlContains(ctrl, x, y)` (a rotation-aware + point-in-shape test for a single control). + - **Act:** `b2kAngularImpulse(ctrl, imp)` — a one-shot, mass-aware angular + kick, the rotational partner of `b2kImpulse`. + - **Contacts (polling):** `b2kContactCount()` / `b2kContactA(i)` / + `b2kContactB(i)` and the `b2kEndContact…` equivalents, so a `b2kFrame` + handler can read each frame's touch pairs without registering a contact + target. + - **Loader:** `b2kVersion()` returns the native ABI version for a Kit-only + "extension loaded and in sync" check. - **Multiply tool.** Alongside Duplicate, a new **Multiply** tool asks how many copies you want (1–50) and drops them in a tidy grid — each an independent copy of the part you clicked (same size, colour, material and settings). Great @@ -42,6 +97,14 @@ The native shim's ABI is tracked separately by `b2Version()` (currently `2`). ### Changed +- **ABI bumped 2 → 3** (`b2Version()` now returns `3`). The change is purely + additive — every existing `b2…` handler keeps its signature — but the prebuilt + libraries must be rebuilt (CI regenerates them on release). **Note:** collision + filter category/mask bits are exposed as 32-bit (32 layers), not Box2D's full + 64-bit, because xTalk numbers carry 32 unsigned bits cleanly. Pre-solve / custom + callbacks and Box2D's standalone math/geometry helpers remain intentionally + unwrapped (no safe mid-step FFI callback into xTalk; the math operates on raw + structs xTalk already handles). - **Slicker tool palette.** Section headers are brighter and sit over a hairline rule; tool buttons are left-aligned, light up on hover, and keep their accent when selected — a more polished, professional left sidebar. diff --git a/docs/api-reference.md b/docs/api-reference.md index 6c6fdf2..ed076ea 100644 --- a/docs/api-reference.md +++ b/docs/api-reference.md @@ -12,7 +12,7 @@ when you need something the Kit doesn't expose. - Distances are **metres**, angles are **radians**. Convert to pixels/degrees at draw time. - Body type codes: `0` static, `1` kinematic, `2` dynamic. -- `b2Version()` → int returns the shim ABI version (currently `2`) — call it once +- `b2Version()` → int returns the shim ABI version (currently `3`) — call it once as a load/version check that the extension and native library are in sync. - [World](#world) @@ -115,6 +115,102 @@ the two body handles for each (indices are **1-based**). | `b2ContactBeginBodyA(i)` / `b2ContactBeginBodyB(i)` → body | The pair that started touching. | | `b2ContactEndBodyA(i)` / `b2ContactEndBodyB(i)` → body | The pair that stopped touching. | +## Shape-def builder (sensors, filtering, event flags) + +Set any of these **before** creating a shape (`b2AddBox`/`Circle`/`Capsule`/ +`Polygon`/`b2CreateChain`); they apply to the **next** shape only, then reset — +just like the polygon vertex builder. This adds sensors, collision filters, and +per-event flags to every existing creator without new variants. + +| Handler | Purpose | +|---------|---------| +| `b2ShapeDefSensor(flag)` | Make the next shape a non-solid **sensor** (overlap events, no collision). | +| `b2ShapeDefFilter(category, mask, group)` | Collision filter for the next shape (category/mask are 32-bit). | +| `b2ShapeDefEnableContactEvents(flag)` / `b2ShapeDefEnableSensorEvents(flag)` / `b2ShapeDefEnableHitEvents(flag)` / `b2ShapeDefEnablePreSolveEvents(flag)` | Per-event flags for the next shape. | +| `b2ShapeDefMaterialId(id)` | User material id for the next shape. | +| `b2ShapeDefReset()` | Clear any pending options explicitly. | + +## World tuning & info + +| Handler | Purpose | +|---------|---------| +| `b2WorldGravityX(world)` / `b2WorldGravityY(world)` | Read the gravity vector. | +| `b2SetRestitutionThreshold` / `b2RestitutionThreshold(world)` | Speed below which collisions stop bouncing. | +| `b2SetHitEventThreshold` / `b2HitEventThreshold(world)` | Approach speed above which a contact reports a hit event. | +| `b2SetContactTuning(world, hertz, damping, pushSpeed)` / `b2SetJointTuning(world, hertz, damping)` | Solver softness. | +| `b2SetMaximumLinearSpeed` / `b2MaximumLinearSpeed(world)` | Clamp body speed. | +| `b2EnableWarmStarting` / `b2IsWarmStartingEnabled` · `b2EnableSpeculative` · `b2IsSleepingEnabled` / `b2IsContinuousEnabled` | World toggles. | +| `b2AwakeBodyCount(world)` | Number of awake bodies. | +| `b2WorldExplode(world, x, y, radius, falloff, impulsePerLength)` | Native radial impulse, shape-perimeter aware. | +| `b2WorldProfileUpdate(world)` → `b2WorldProfileStep/Pairs/Collide/Solve/Refit/Sensors()` | Per-step timing (ms). | +| `b2WorldCountersUpdate(world)` → `b2WorldBodyCount/ShapeCount/ContactCount/JointCount/IslandCount()` | World object counts. | + +## Body — transforms, mass, enumeration + +| Handler | Purpose | +|---------|---------| +| `b2BodyWorldPointX/Y` · `b2BodyLocalPointX/Y` · `b2BodyWorldVectorX/Y` · `b2BodyLocalVectorX/Y` | Local↔world point & vector transforms. | +| `b2BodyWorldPointVelocityX/Y` · `b2BodyLocalPointVelocityX/Y` | Velocity of a point on the body. | +| `b2ApplyForceAt(body, fx, fy, px, py, wake)` / `b2ApplyImpulseAt(...)` | Force / impulse at a world point (adds torque). | +| `b2BodyRotationalInertia` · `b2BodyLocalCenterX/Y` | Inertia and local centre of mass. | +| `b2BodyMassDataUpdate(body)` → `b2MassDataMass/CenterX/CenterY/Inertia()` | Read mass data. | +| `b2SetMassData(body, mass, cx, cy, inertia)` / `b2ApplyMassFromShapes(body)` | Override / recompute mass. | +| `b2SetTargetTransform(body, x, y, angle, timeStep)` | Drive a kinematic body to a pose. | +| `b2EnableSleep` / `b2BodyIsSleepEnabled` · `b2BodyIsFixedRotation` · `b2BodyEnableContactEvents` / `b2BodyEnableHitEvents` | Per-body flags. | +| `b2BodyAABBUpdate(body)` → `b2AABBLowerX/LowerY/UpperX/UpperY()` | Body AABB. | +| `b2BodyShapeCount(body)` → `b2BodyShapeAt(i)` · `b2BodyJointCount(body)` → `b2BodyJointAt(i)` | Enumerate a body's shapes / joints (1-based). | + +## Shape — filter, geometry, material, queries + +| Handler | Purpose | +|---------|---------| +| `b2ShapeType` · `b2ShapeIsSensor` · `b2ShapeDensity/Friction/Restitution` · `b2ShapeMaterialId` / `b2SetShapeMaterialId` | Read type / material. | +| `b2SetShapeFilter(shape, category, mask, group)` · `b2ShapeFilterCategory/Mask/Group(shape)` | Collision filtering (32-bit bits). | +| `b2ShapeEnableSensorEvents` / `…Contact…` / `…Hit…` / `…PreSolve…` + the matching `…EventsEnabled` getters | Per-shape event flags. | +| `b2ShapeCircleUpdate/Capsule…/Segment…/PolygonUpdate(shape)` + their `…X/Y/Radius/VertexX/VertexY` readers | Read a shape's geometry. | +| `b2SetShapeCircle/Capsule/Segment(shape, …)` · `b2SetShapePolygon(shape)` (uses the vertex builder) | Replace a shape's geometry in place. | +| `b2ShapeRayCast(shape, x1, y1, x2, y2)` → `b2ShapeRayX/Y/NormalX/NormalY/Fraction()` | Ray cast against one shape. | +| `b2ShapeAABBUpdate` (→ `b2AABB…`) · `b2ShapeClosestPointX/Y` · `b2ShapeMassDataUpdate` (→ `b2MassData…`) | Bounds / closest point / mass. | +| `b2ShapeSensorCapacity` · `b2ShapeSensorOverlapsUpdate(shape)` → `b2ShapeSensorOverlapCount()` / `b2ShapeSensorOverlapAt(i)` | Poll shapes overlapping a sensor. | + +## Chains (smooth terrain) + +| Handler | Purpose | +|---------|---------| +| `b2ChainBegin()` → `b2ChainAddPoint(x, y)` … → `b2CreateChain(body, loop, friction, restitution)` → chain | Build a chain (≥ 4 points; loop closes it). A non-loop chain's first & last points are ghost vertices (n points → n−3 collidable segments). | +| `b2DestroyChain(chain)` · `b2ChainIsValid(chain)` | Lifetime. | +| `b2SetChainFriction/Restitution` + getters · `b2ChainSegmentCount(chain)` → `b2ChainSegmentAt(i)` | Tune / enumerate segments. | + +## Joints — generic, new types, full per-joint control + +| Handler | Purpose | +|---------|---------| +| `b2JointType` · `b2JointBodyA/B` · `b2JointLocalAnchorAX/AY/BX/BY` · `b2JointCollideConnected` / `b2SetJointCollideConnected` · `b2JointConstraintForceX/Y` · `b2JointConstraintTorque` · `b2JointWakeBodies` | Generic joint surface (constraint force/torque is handy for breakable joints). | +| `b2MotorJoint(world, bodyA, bodyB, offsetX, offsetY, angularOffset, maxForce, maxTorque, correction, collide)` + `b2MotorSet/Get…` | **Motor joint** — drive bodyB to an offset pose from bodyA. | +| `b2FilterJoint(world, bodyA, bodyB)` | **Filter joint** — disable collision between exactly these two bodies. | +| `b2Revolute…` / `b2Prismatic…` / `b2Distance…` / `b2Weld…` / `b2Wheel…` / `b2Mouse…` | The **complete** per-joint get/set surface for all six existing joint types: spring enable/hertz/damping, limit enable/lower/upper, motor enable/speed/force\|torque, and readouts (angle, translation, speed, current length, reference angle, target). | + +## World queries (overlap / ray-cast-all / shape-cast) + +Each query runs, stashes its hits in one shared buffer, and returns the count; +then read rows **1-based**. (`b2CastRayClosest` / `b2BodyAtPoint` above remain for +single-result use.) + +| Handler | Purpose | +|---------|---------| +| `b2OverlapAABB(world, x1, y1, x2, y2)` · `b2OverlapPoint(world, x, y)` · `b2OverlapCircle(world, cx, cy, r)` · `b2OverlapShape(world, radius)` (uses the vertex builder) | Find shapes overlapping a region. | +| `b2RayCastAll(world, x1, y1, x2, y2)` | Every shape along a ray, sorted near→far. | +| `b2ShapeCast(world, radius, dx, dy)` | Sweep a proxy (built from the vertex builder) and gather hits. | +| `b2QueryCount()` · `b2QueryBody(i)` / `b2QueryShape(i)` · `b2QueryX/Y(i)` · `b2QueryNormalX/Y(i)` · `b2QueryFraction(i)` | Read the shared result rows. | + +## Events — hit, sensor, body-move + +| Handler | Purpose | +|---------|---------| +| `b2ContactHitCount()` · `b2ContactHitBodyA/B(i)` · `b2ContactHitX/Y(i)` · `b2ContactHitNormalX/Y(i)` · `b2ContactHitSpeed(i)` | **Hit** events (snapshotted by `b2ContactsUpdate`; needs hit events enabled). | +| `b2SensorsUpdate(world)` → `b2SensorBeginCount/EndCount()` · `b2SensorBeginSensorShape/VisitorShape(i)` · `b2SensorEndSensorShape/VisitorShape(i)` | **Sensor** events (shape handles; both shapes need sensor events). | +| `b2BodiesUpdate(world)` → `b2BodyMoveCount()` · `b2BodyMoveBody(i)` · `b2BodyMoveX/Y/Angle(i)` · `b2BodyMoveFellAsleep(i)` | **Body-move** events — read every moved transform in one call (efficient bulk sync). | + ## Notes and gotchas **Units.** Box2D is tuned for **MKS units**; keep moving objects roughly @@ -137,6 +233,9 @@ rendering into an LCB widget canvas, rather than one control per body. **Extending the binding.** See [architecture.md](architecture.md#extending-the-binding) for the step-by-step recipe (add a `b2lc_*` C function, a `foreign handler`, and -a public wrapper; bump `LC_ABI_VERSION`; rebuild). Box2D v3 also offers chains, -sensors, hit/pre-solve contact events, and shape casts — all reachable the same -way. +a public wrapper; bump `LC_ABI_VERSION`; rebuild). As of ABI `3` the binding +covers the full Box2D v3.1 **live-object** surface (chains, sensors, filtering, +hit & body-move events, shape casts, motor/filter joints, world tuning, mass +data, …). What's intentionally **not** wrapped: pre-solve / custom-filter +callbacks (no safe way to call back into xTalk mid-step) and Box2D's standalone +math/geometry/TOI helpers (they operate on raw structs, not world objects). diff --git a/docs/architecture.md b/docs/architecture.md index 37d8828..8398d02 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -90,7 +90,7 @@ non-deterministic. ## The ABI version The shim exports `b2lc_abi_version()`, surfaced to scripts as `b2Version()`. It -returns the integer `LC_ABI_VERSION` defined in `src/box2d_lc.c` (currently `2`). +returns the integer `LC_ABI_VERSION` defined in `src/box2d_lc.c` (currently `3`). Use it as a load/version sanity check, and **bump it whenever the exported ABI changes** so the `.lcb` and native library can't silently drift apart. @@ -113,7 +113,28 @@ Exposing more of Box2D is mechanical. To add a handler: 4. **Rebuild** the native library (see [building.md](building.md)) and reload the extension. -Box2D v3 also offers chains, sensors, hit/pre-solve contact events, and shape -casts — all reachable the same way when you need them. Add a smoke-test -assertion in `tests/smoke_test.c` for anything non-trivial so CI exercises it on -every platform. +Add a smoke-test assertion in `tests/smoke_test.c` for anything non-trivial so CI +exercises it on every platform. + +As of ABI `3` the binding already covers the full Box2D v3.1 **live-object** +surface. The newer additions reuse a few shared shim patterns worth knowing when +you extend further: + +- A **shape-def "pending overrides"** struct lets the existing shape creators gain + sensors / filters / event-flags / materials without new variants — set options + with `b2lc_shapedef_*`, and `fill_shape_def` applies them to the next shape then + resets (one-shot, like the polygon vertex builder). +- A **chains handle table** (the same `DEFINE_TABLE` macro) plus a point-cloud + builder mirrors the polygon path. +- **Queries** are callback-based upstream; the shim runs them with a small C + callback that pushes hits into one **shared result buffer**, then exposes a + count + indexed getters — the same idea as the ray/contact stashes. +- **Sensor / hit / body-move events** reuse the growable snapshot pattern of the + contact events. + +What stays **intentionally unwrapped**: pre-solve / custom-filter / friction / +restitution callbacks (there is no safe way to call back into xTalk mid-step over +the LCB FFI), and Box2D's standalone math / geometry / TOI / manifold helpers +(they operate on raw structs, which xTalk handles itself). Filter category/mask +bits are exposed as **32-bit** (xTalk doubles carry 32 unsigned bits cleanly), so +scripts get 32 collision layers rather than Box2D's full 64. diff --git a/docs/getting-started.md b/docs/getting-started.md index 5ca2679..4acb75c 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -58,7 +58,7 @@ Open the Message Box and run: put b2Version() ``` -You should see `2` (the shim ABI version). If you get an error instead, the +You should see `3` (the shim ABI version). If you get an error instead, the extension didn't load or the native library can't be found — jump to [Troubleshooting](#troubleshooting). @@ -130,7 +130,7 @@ scenes at runtime, so there's nothing to lay out. The demo is **self-contained**: it bundles a copy of the Kit, so it runs from a single paste — no separate setup. -1. Make sure the extension is loaded and `put b2Version()` returns `2`. +1. Make sure the extension is loaded and `put b2Version()` returns `3`. 2. Paste the whole of [`examples/box2dxt-demo.livecodescript`](../examples/box2dxt-demo.livecodescript) into a stack's script (*Object → Stack Script*). If you previously pasted a diff --git a/docs/kit-reference.md b/docs/kit-reference.md index ec1ef60..c241940 100644 --- a/docs/kit-reference.md +++ b/docs/kit-reference.md @@ -8,7 +8,7 @@ demo's performance practices built in. **Setup:** paste the Kit into your card/stack script (or save it as a library stack and `start using` it). It requires the `box2dxt` extension loaded -(`put b2Version()` should return `2`). +(`put b2Version()` should return `3`). - [60-second start](#60-second-start) - [World & loop](#world--loop) @@ -65,6 +65,7 @@ b2kStart | `b2kWall x1, y1, x2, y2` | A static collision segment between two screen points (custom walls, ramps, ledges). Invisible — draw your own graphic to match. | | `b2kClear` | Remove all bodies/controls the Kit created, keep the world. | | `b2kTeardown` | Stop and destroy the world and all Kit state. | +| `b2kVersion()` | Native shim ABI version (`3`) — a load / in-sync check from Kit-only code. | ## Configuration @@ -137,6 +138,7 @@ dynamic/static where a handler takes an optional `dyn` flag. | `b2kForce ctrl, fx, fy` | Apply a continuous force (call each frame for thrust/wind). | | `b2kImpulse ctrl, ix, iy` | One-shot impulse, mass-aware (heavier bodies move less). | | `b2kTorque ctrl, torque` | Continuous turning force (call each frame); +/- sets direction. | +| `b2kAngularImpulse ctrl, imp` | One-shot turning impulse, mass-aware (the angular partner of `b2kImpulse`). | | `b2kSetVelocity ctrl, vx, vy` | Set linear velocity (px/s). | | `b2kSpin ctrl, degPerSec` | Set angular velocity. | | `b2kSpinBy ctrl, degPerSec` | Add to the current angular velocity. | @@ -149,10 +151,14 @@ dynamic/static where a handler takes an optional `dyn` flag. | Handler | Returns | |---------|---------| | `b2kBodyOf(ctrl)` | The underlying `b2…` body handle. | +| `b2kPosition(ctrl)` | Body centre as `x,y` in screen pixels (the position partner of `b2kVelocity`). | +| `b2kWorldCenter(ctrl)` | Centre of mass as `x,y` in screen pixels (the true pivot for spin/torque). | | `b2kVelocity(ctrl)` | `vx,vy` in px/s. | | `b2kSpeed(ctrl)` | Scalar speed (px/s). | | `b2kAngle(ctrl)` | Rotation in degrees. | | `b2kMass(ctrl)` | Body mass (kg, sim units). | +| `b2kGravityScale(ctrl)` | Per-body gravity multiplier (getter for `b2kSetGravityScale`). | +| `b2kDamping(ctrl)` | Drag as `linear,angular` (getter for `b2kSetDamping`). | | `b2kBodyType(ctrl)` | Body type as a word: `static` / `kinematic` / `dynamic`. | | `b2kBodyCount()` | How many bodies the Kit is tracking. | | `b2kAwakeCount()` | How many dynamic bodies are currently awake (active). | @@ -161,6 +167,7 @@ dynamic/static where a handler takes an optional `dyn` flag. | `b2kIsBullet(ctrl)` | Whether continuous (CCD) collision is on. | | `b2kIsEnabled(ctrl)` | Whether the body is in the simulation. | | `b2kControlAt(x, y)` | The control whose body covers a screen point. | +| `b2kControlContains(ctrl, x, y)` | True if a screen point is inside this control's actual (rotated) collision shape. | | `b2kRayHit(x1, y1, x2, y2)` | True if a ray hits; then read the result functions below. | | `b2kRayHitX()` / `b2kRayHitY()` | Hit point in screen pixels. | | `b2kRayHitNormalX()` / `b2kRayHitNormalY()` | Surface normal at the hit (screen-oriented unit vector). | @@ -213,6 +220,67 @@ helpers or to `b2kRemoveJoint`. | `on b2kEndContact pCtrlA, pCtrlB` | Sent when two attached controls **stop** touching. | | `on b2kFrame` | Sent to your `b2kFrameTarget` once per simulated frame (after bodies sync) — use it for motors, input, and custom drawing. | +**Polling contacts** — instead of (or alongside) the messages above, read this +frame's touch pairs directly, e.g. from `on b2kFrame`. Indices are 1-based; each +accessor returns a control (empty for a wall, the ground, or any untracked body). + +| Handler | Returns | +|---------|---------| +| `b2kContactCount()` | How many pairs **began** touching this frame. | +| `b2kContactA(i)` / `b2kContactB(i)` | The two controls of the *i*-th begin-touch pair. | +| `b2kEndContactCount()` | How many pairs **stopped** touching this frame. | +| `b2kEndContactA(i)` / `b2kEndContactB(i)` | The two controls of the *i*-th end-touch pair. | + +## Sensors (trigger zones) + +Non-solid fixtures that report overlaps but never block. The Kit enables sensor +events on every body it creates, so sensors detect them automatically. + +| Handler | Purpose | +|---------|---------| +| `b2kAddSensor ctrl [,shape]` → body | Attach a static sensor (`shape` = `box`/`ball`/`capsule`). | +| `on b2kSensorEnter pSensorCtrl, pVisitorCtrl` | Sent to your `b2kContactTarget` when a body enters a sensor. | +| `on b2kSensorExit pSensorCtrl, pVisitorCtrl` | Sent when it leaves. | +| `b2kSensorCount()` / `b2kSensorEnterSensor(i)` / `b2kSensorEnterVisitor(i)` | Poll this frame's enters (1-based); `…Exit…` for leaves. | + +## Collision filtering (named layers) + +Up to 32 layers. Two shapes collide only if **each** one's category is in the +other's mask (and no shared negative group forbids it). + +| Handler | Purpose | +|---------|---------| +| `b2kDefineLayer name` → bit | Define/fetch a named layer. | +| `b2kSetCategory ctrl, layers` | Set which layer(s) a control *is* (comma/space list of names or numbers). | +| `b2kSetMask ctrl, layers` | Set which layer(s) it collides *with*. | +| `b2kSetCollisionGroup ctrl, n` | Negative = never collide with same group; positive = always. | +| `b2kNoCollide ctrlA, ctrlB` → joint | Stop just these two from colliding (a filter joint). | + +## Chains & terrain + +| Handler | Purpose | +|---------|---------| +| `b2kChain pointList [,loop]` → chain | Smooth static terrain from a list of `x,y` screen points (≥ 4). No inner corners for fast bodies to catch on. Invisible — draw a matching graphic. | +| `b2kSmoothGround pointList` → chain | An open chain (alias for `b2kChain …, false`). | + +## Region & ray queries + +| Handler | Returns | +|---------|---------| +| `b2kOverlap x1, y1, x2, y2` | Newline list of controls whose body overlaps a screen rect. | +| `b2kOverlapCircle x, y, r` | …overlapping a screen circle. | +| `b2kRayHitAll x1, y1, x2, y2` | Every control a ray crosses, nearest-first (vs `b2kRayHit`'s single closest). | + +## Motors & tuning + +| Handler | Purpose | +|---------|---------| +| `b2kMotorTo mover, ref, dxPx, dyPx, deg [,maxF, maxT]` → joint | Drive `mover` toward a pose offset from `ref` (a moving anchor; empty = world). | +| `b2kExplode x, y [,radius] [,power]` | Native radial blast (shape-aware, affects all dynamic bodies). `b2kExplodeLegacy` keeps the old velocity-based feel. | +| `b2kSetRestitutionThreshold px/s` · `b2kSetContactTuning hz, damp, pushPx` · `b2kSetJointTuning hz, damp` · `b2kSetMaxSpeed px/s` · `b2kEnableWarmStarting flag` | World solver tuning. | +| `b2kProfile()` | `"totalStep,collide,solve"` ms for the last step (a perf HUD). | +| `b2kAwakeBodyCount()` | Awake dynamic bodies (native count). | + ## Tips - Keep moving objects a sensible on-screen size (default scale 40 px/m, so diff --git a/examples/box2dxt-contraption-builder.livecodescript b/examples/box2dxt-contraption-builder.livecodescript index ac0a189..50a0703 100644 --- a/examples/box2dxt-contraption-builder.livecodescript +++ b/examples/box2dxt-contraption-builder.livecodescript @@ -9,7 +9,7 @@ -- # # -- # HOW TO RUN # -- # 1. Load the box2dxt.lcb extension and put the box2dxt native # --- # library where it can be found. Check: put b2Version() -- 2 # +-- # library where it can be found. Check: put b2Version() -- 3 # -- # 2. Paste THIS WHOLE FILE into a stack's script # -- # (Object > Stack Script). # -- # 3. Open/close the card, or run startCB in the Message Box. # @@ -34,7 +34,7 @@ -- PIXELS, SCREEN coordinates and DEGREES; the kit converts to Box2D's -- metres / radians / y-up for you, runs the world, and moves your controls. -- Runs in OpenXTalk (OXT); compatible with LiveCode 9.6.3+. --- Requires the box2dxt extension loaded (put b2Version() should return 2). +-- Requires the box2dxt extension loaded (put b2Version() should return 3). -- -- ===================== 60-SECOND START ============================= -- on openCard diff --git a/examples/box2dxt-demo.livecodescript b/examples/box2dxt-demo.livecodescript index 21a6c48..f381c52 100644 --- a/examples/box2dxt-demo.livecodescript +++ b/examples/box2dxt-demo.livecodescript @@ -3,7 +3,7 @@ -- # # -- # HOW TO RUN # -- # 1. Load the box2dxt.lcb extension and put the box2dxt native # --- # library where it can be found. Check: put b2Version() -- 2 # +-- # library where it can be found. Check: put b2Version() -- 3 # -- # 2. Paste THIS WHOLE FILE into a stack`s script # -- # (Object > Stack Script). # -- # 3. Open/close the card, or run startBox2DDemo in the # @@ -24,7 +24,7 @@ -- PIXELS, SCREEN coordinates and DEGREES; the kit converts to Box2D's -- metres / radians / y-up for you, runs the world, and moves your controls. -- Runs in OpenXTalk (OXT); compatible with LiveCode 9.6.3+. --- Requires the box2dxt extension loaded (put b2Version() should return 2). +-- Requires the box2dxt extension loaded (put b2Version() should return 3). -- -- ===================== 60-SECOND START ============================= -- on openCard diff --git a/src/box2d_lc.c b/src/box2d_lc.c index fa0545a..00d14e4 100644 --- a/src/box2d_lc.c +++ b/src/box2d_lc.c @@ -46,7 +46,7 @@ #endif /* Bump when the exported ABI changes so the LCB side can sanity-check. */ -#define LC_ABI_VERSION 2 +#define LC_ABI_VERSION 3 /* ------------------------------------------------------------------ */ /* Generic handle tables. Handles are 1-based; 0 == null/invalid. */ @@ -95,6 +95,7 @@ DEFINE_TABLE(worlds, b2WorldId) DEFINE_TABLE(bodies, b2BodyId) DEFINE_TABLE(shapes, b2ShapeId) DEFINE_TABLE(joints, b2JointId) +DEFINE_TABLE(chains, b2ChainId) /* small helpers */ static inline b2Vec2 v2(double x, double y) { b2Vec2 v; v.x = (float)x; v.y = (float)y; return v; } @@ -243,6 +244,23 @@ LC_API void b2lc_body_disable(int b) { b2BodyId id = bodies /* ------------------------------------------------------------------ */ /* Shapes (attached to a body) */ /* ------------------------------------------------------------------ */ +/* Pending shape-def overrides: optional extras a script can set just before the + * next shape creation (sensor flag, collision filter, per-event flags, material + * id). They are applied by fill_shape_def() and then reset, so they are strictly + * one-shot — exactly like the b2lc_poly_* vertex builder. Tri-state ints use -1 + * for "leave the default"; "have*" flags mark a value that was explicitly set. */ +static struct { + int sensor, enableContact, enableSensorEv, enableHitEv, enablePreSolve; + int haveFilter; double catBits, maskBits; int groupIndex; + int haveMaterialId, materialId; +} s_sd = { -1, -1, -1, -1, -1, 0, 0.0, 0.0, 0, 0, 0 }; /* tri-states start at "leave default" */ + +static void reset_shapedef(void) { + s_sd.sensor = s_sd.enableContact = s_sd.enableSensorEv = -1; + s_sd.enableHitEv = s_sd.enablePreSolve = -1; + s_sd.haveFilter = 0; s_sd.haveMaterialId = 0; +} + static void fill_shape_def(b2ShapeDef *sd, double density, double friction, double restitution) { *sd = b2DefaultShapeDef(); sd->density = (float)density; @@ -251,8 +269,33 @@ static void fill_shape_def(b2ShapeDef *sd, double density, double friction, doub sd->enableContactEvents = true; /* off by default in v3.1; on so b2ContactsUpdate() works out of the box for collision detection */ + /* apply any pending one-shot overrides, then clear them */ + if (s_sd.sensor >= 0) sd->isSensor = s_sd.sensor ? true : false; + if (s_sd.enableContact >= 0) sd->enableContactEvents = s_sd.enableContact ? true : false; + if (s_sd.enableSensorEv >= 0) sd->enableSensorEvents = s_sd.enableSensorEv ? true : false; + if (s_sd.enableHitEv >= 0) sd->enableHitEvents = s_sd.enableHitEv ? true : false; + if (s_sd.enablePreSolve >= 0) sd->enablePreSolveEvents = s_sd.enablePreSolve ? true : false; + if (s_sd.haveFilter) { + sd->filter.categoryBits = (uint64_t)(uint32_t)s_sd.catBits; + sd->filter.maskBits = (uint64_t)(uint32_t)s_sd.maskBits; + sd->filter.groupIndex = s_sd.groupIndex; + } + if (s_sd.haveMaterialId) sd->material.userMaterialId = s_sd.materialId; + reset_shapedef(); } +/* shape-def builder setters (one-shot; consumed by the next shape creation) */ +LC_API void b2lc_shapedef_reset(void) { reset_shapedef(); } +LC_API void b2lc_shapedef_set_sensor(int f) { s_sd.sensor = f ? 1 : 0; } +LC_API void b2lc_shapedef_set_enable_contact_events(int f) { s_sd.enableContact = f ? 1 : 0; } +LC_API void b2lc_shapedef_set_enable_sensor_events(int f) { s_sd.enableSensorEv = f ? 1 : 0; } +LC_API void b2lc_shapedef_set_enable_hit_events(int f) { s_sd.enableHitEv = f ? 1 : 0; } +LC_API void b2lc_shapedef_set_enable_presolve_events(int f){ s_sd.enablePreSolve = f ? 1 : 0; } +LC_API void b2lc_shapedef_set_filter(double cat, double mask, int group) { + s_sd.haveFilter = 1; s_sd.catBits = cat; s_sd.maskBits = mask; s_sd.groupIndex = group; +} +LC_API void b2lc_shapedef_set_material_id(int id) { s_sd.haveMaterialId = 1; s_sd.materialId = id; } + /* register a freshly created shape, stamping its handle into userData */ static int register_shape(b2ShapeId id) { int h = shapes_add(id); @@ -335,6 +378,17 @@ LC_API int b2lc_shape_test_point(int s, double x, double y) { b2ShapeId id = sh /* ------------------------------------------------------------------ */ /* Joints */ /* ------------------------------------------------------------------ */ +/* register a freshly created joint, stamping its handle into userData so the + * generic getters (GetBodyA/B), body->joint enumeration and round-trips work. */ +static int register_joint(b2JointId id) { + int h = joints_add(id); + b2Joint_SetUserData(id, h2ud(h)); + return h; +} +static int joint_handle_of(b2JointId j) { + return b2Joint_IsValid(j) ? ud2h(b2Joint_GetUserData(j)) : 0; +} + LC_API int b2lc_joint_revolute(int w, int bA, int bB, double ax, double ay, double bx, double by, int collide) { b2WorldId wid = worlds_get(w); @@ -346,7 +400,7 @@ LC_API int b2lc_joint_revolute(int w, int bA, int bB, jd.localAnchorA = v2(ax, ay); jd.localAnchorB = v2(bx, by); jd.collideConnected = collide ? true : false; - return joints_add(b2CreateRevoluteJoint(wid, &jd)); + return register_joint(b2CreateRevoluteJoint(wid, &jd)); } LC_API void b2lc_revolute_enable_limit(int j, int enable, double lowerRad, double upperRad) { b2JointId jj = joints_get(j); @@ -376,7 +430,7 @@ LC_API int b2lc_joint_distance(int w, int bA, int bB, jd.localAnchorB = v2(bx, by); jd.length = (float)length; jd.collideConnected = collide ? true : false; - return joints_add(b2CreateDistanceJoint(wid, &jd)); + return register_joint(b2CreateDistanceJoint(wid, &jd)); } LC_API void b2lc_distance_set_length(int j, double length) { b2JointId jj = joints_get(j); @@ -411,7 +465,7 @@ LC_API int b2lc_joint_weld(int w, int bA, int bB, jd.localAnchorB = v2(bx, by); jd.referenceAngle = (float)refAngle; jd.collideConnected = collide ? true : false; - return joints_add(b2CreateWeldJoint(wid, &jd)); + return register_joint(b2CreateWeldJoint(wid, &jd)); } LC_API void b2lc_weld_set_stiffness(int j, double linHertz, double linDamping, double angHertz, double angDamping) { b2JointId jj = joints_get(j); @@ -437,7 +491,7 @@ LC_API int b2lc_joint_prismatic(int w, int bA, int bB, jd.localAxisA = b2Normalize(v2(axisX, axisY)); jd.referenceAngle = (float)refAngle; jd.collideConnected = collide ? true : false; - return joints_add(b2CreatePrismaticJoint(wid, &jd)); + return register_joint(b2CreatePrismaticJoint(wid, &jd)); } LC_API void b2lc_prismatic_enable_limit(int j, int enable, double lower, double upper) { b2JointId jj = joints_get(j); @@ -468,7 +522,7 @@ LC_API int b2lc_joint_wheel(int w, int bA, int bB, jd.localAnchorB = v2(bx, by); jd.localAxisA = b2Normalize(v2(axisX, axisY)); jd.collideConnected = collide ? true : false; - return joints_add(b2CreateWheelJoint(wid, &jd)); + return register_joint(b2CreateWheelJoint(wid, &jd)); } LC_API void b2lc_wheel_enable_spring(int j, int enable, double hertz, double dampingRatio) { b2JointId jj = joints_get(j); @@ -499,7 +553,7 @@ LC_API int b2lc_joint_mouse(int w, int bA, int bB, jd.hertz = (float)hertz; jd.dampingRatio = (float)dampingRatio; jd.maxForce = (float)maxForce; - return joints_add(b2CreateMouseJoint(wid, &jd)); + return register_joint(b2CreateMouseJoint(wid, &jd)); } LC_API void b2lc_mouse_set_target(int j, double tx, double ty) { b2JointId jj = joints_get(j); @@ -576,6 +630,10 @@ LC_API int b2lc_body_at_point(int w, double x, double y) { static int *s_begin = NULL; static int s_beginCap = 0, s_beginCnt = 0; static int *s_end = NULL; static int s_endCap = 0, s_endCnt = 0; +/* hit events (enableHitEvents): impact point/normal/approach-speed per pair */ +typedef struct { int a, b; double px, py, nx, ny, speed; } LcHit; +static LcHit *s_hit = NULL; static int s_hitCap = 0, s_hitCnt = 0; + static int *ensure_cap(int *arr, int *cap, int needPairs) { if (needPairs * 2 > *cap) { *cap = needPairs * 2; @@ -585,7 +643,7 @@ static int *ensure_cap(int *arr, int *cap, int needPairs) { } LC_API int b2lc_contacts_update(int w) { - s_beginCnt = s_endCnt = 0; + s_beginCnt = s_endCnt = s_hitCnt = 0; b2WorldId wid = worlds_get(w); if (!b2World_IsValid(wid)) return 0; b2ContactEvents ev = b2World_GetContactEvents(wid); @@ -604,6 +662,19 @@ LC_API int b2lc_contacts_update(int w) { } s_endCnt = ev.endCount; + if (ev.hitCount > s_hitCap) { + s_hitCap = ev.hitCount; + s_hit = (LcHit *)realloc(s_hit, (size_t)s_hitCap * sizeof(LcHit)); + } + for (int i = 0; i < ev.hitCount; i++) { + s_hit[i].a = body_handle_of_shape(ev.hitEvents[i].shapeIdA); + s_hit[i].b = body_handle_of_shape(ev.hitEvents[i].shapeIdB); + s_hit[i].px = ev.hitEvents[i].point.x; s_hit[i].py = ev.hitEvents[i].point.y; + s_hit[i].nx = ev.hitEvents[i].normal.x; s_hit[i].ny = ev.hitEvents[i].normal.y; + s_hit[i].speed = ev.hitEvents[i].approachSpeed; + } + s_hitCnt = ev.hitCount; + return s_beginCnt; } LC_API int b2lc_contact_begin_count(void) { return s_beginCnt; } @@ -612,3 +683,603 @@ LC_API int b2lc_contact_begin_a(int i) { return (i >= 0 && i < s_beginCnt) ? s_b LC_API int b2lc_contact_begin_b(int i) { return (i >= 0 && i < s_beginCnt) ? s_begin[2 * i + 1] : 0; } LC_API int b2lc_contact_end_a(int i) { return (i >= 0 && i < s_endCnt) ? s_end[2 * i] : 0; } LC_API int b2lc_contact_end_b(int i) { return (i >= 0 && i < s_endCnt) ? s_end[2 * i + 1] : 0; } + +/* ================================================================== */ +/* ABI v3 additions: the full Box2D v3.1.0 live-object surface. */ +/* Everything below reuses the patterns above (scalar getters, the */ +/* poly/chain input builders, the ray/contact output stashes, growable */ +/* snapshots, handle tables). No new FFI machinery. */ +/* ================================================================== */ + +/* id -> integer-handle helpers (mirror body_handle_of_shape for bodies/shapes) */ +static int body_h(b2BodyId b) { return b2Body_IsValid(b) ? ud2h(b2Body_GetUserData(b)) : 0; } +static int shape_h(b2ShapeId s) { return b2Shape_IsValid(s) ? ud2h(b2Shape_GetUserData(s)) : 0; } + +/* ---- contact HIT events (snapshotted by b2lc_contacts_update) ------ */ +LC_API int b2lc_contact_hit_count(void) { return s_hitCnt; } +LC_API int b2lc_contact_hit_a(int i) { return (i >= 0 && i < s_hitCnt) ? s_hit[i].a : 0; } +LC_API int b2lc_contact_hit_b(int i) { return (i >= 0 && i < s_hitCnt) ? s_hit[i].b : 0; } +LC_API double b2lc_contact_hit_x(int i) { return (i >= 0 && i < s_hitCnt) ? s_hit[i].px : 0.0; } +LC_API double b2lc_contact_hit_y(int i) { return (i >= 0 && i < s_hitCnt) ? s_hit[i].py : 0.0; } +LC_API double b2lc_contact_hit_nx(int i) { return (i >= 0 && i < s_hitCnt) ? s_hit[i].nx : 0.0; } +LC_API double b2lc_contact_hit_ny(int i) { return (i >= 0 && i < s_hitCnt) ? s_hit[i].ny : 0.0; } +LC_API double b2lc_contact_hit_speed(int i) { return (i >= 0 && i < s_hitCnt) ? s_hit[i].speed : 0.0; } + +/* ---- sensor events ------------------------------------------------- */ +/* begin/end touch pairs are (sensorShape, visitorShape) — sensors are a + * shape-level concept, so these expose SHAPE handles (not bodies). */ +static int *s_senB = NULL; static int s_senBCap = 0, s_senBCnt = 0; +static int *s_senE = NULL; static int s_senECap = 0, s_senECnt = 0; + +LC_API int b2lc_sensors_update(int w) { + s_senBCnt = s_senECnt = 0; + b2WorldId wid = worlds_get(w); + if (!b2World_IsValid(wid)) return 0; + b2SensorEvents ev = b2World_GetSensorEvents(wid); + s_senB = ensure_cap(s_senB, &s_senBCap, ev.beginCount); + for (int i = 0; i < ev.beginCount; i++) { + s_senB[2 * i] = shape_h(ev.beginEvents[i].sensorShapeId); + s_senB[2 * i + 1] = shape_h(ev.beginEvents[i].visitorShapeId); + } + s_senBCnt = ev.beginCount; + s_senE = ensure_cap(s_senE, &s_senECap, ev.endCount); + for (int i = 0; i < ev.endCount; i++) { + s_senE[2 * i] = shape_h(ev.endEvents[i].sensorShapeId); /* may be destroyed -> 0 */ + s_senE[2 * i + 1] = shape_h(ev.endEvents[i].visitorShapeId); + } + s_senECnt = ev.endCount; + return s_senBCnt; +} +LC_API int b2lc_sensor_begin_count(void) { return s_senBCnt; } +LC_API int b2lc_sensor_end_count(void) { return s_senECnt; } +LC_API int b2lc_sensor_begin_sensor(int i) { return (i >= 0 && i < s_senBCnt) ? s_senB[2 * i] : 0; } +LC_API int b2lc_sensor_begin_visitor(int i) { return (i >= 0 && i < s_senBCnt) ? s_senB[2 * i + 1] : 0; } +LC_API int b2lc_sensor_end_sensor(int i) { return (i >= 0 && i < s_senECnt) ? s_senE[2 * i] : 0; } +LC_API int b2lc_sensor_end_visitor(int i) { return (i >= 0 && i < s_senECnt) ? s_senE[2 * i + 1] : 0; } + +/* ---- body MOVE events (efficient bulk transform readback) ---------- */ +typedef struct { int body; double x, y, angle; int asleep; } LcMove; +static LcMove *s_move = NULL; static int s_moveCap = 0, s_moveCnt = 0; + +LC_API int b2lc_bodies_update(int w) { + s_moveCnt = 0; + b2WorldId wid = worlds_get(w); + if (!b2World_IsValid(wid)) return 0; + b2BodyEvents ev = b2World_GetBodyEvents(wid); + if (ev.moveCount > s_moveCap) { + s_moveCap = ev.moveCount; + s_move = (LcMove *)realloc(s_move, (size_t)s_moveCap * sizeof(LcMove)); + } + for (int i = 0; i < ev.moveCount; i++) { + b2Transform t = ev.moveEvents[i].transform; + s_move[i].body = ud2h(ev.moveEvents[i].userData); /* the stored handle */ + s_move[i].x = t.p.x; + s_move[i].y = t.p.y; + s_move[i].angle = atan2((double)t.q.s, (double)t.q.c); + s_move[i].asleep = ev.moveEvents[i].fellAsleep ? 1 : 0; + } + s_moveCnt = ev.moveCount; + return s_moveCnt; +} +LC_API int b2lc_body_move_count(void) { return s_moveCnt; } +LC_API int b2lc_body_move_body(int i) { return (i >= 0 && i < s_moveCnt) ? s_move[i].body : 0; } +LC_API double b2lc_body_move_x(int i) { return (i >= 0 && i < s_moveCnt) ? s_move[i].x : 0.0; } +LC_API double b2lc_body_move_y(int i) { return (i >= 0 && i < s_moveCnt) ? s_move[i].y : 0.0; } +LC_API double b2lc_body_move_angle(int i) { return (i >= 0 && i < s_moveCnt) ? s_move[i].angle : 0.0; } +LC_API int b2lc_body_move_asleep(int i) { return (i >= 0 && i < s_moveCnt) ? s_move[i].asleep : 0; } + +/* ------------------------------------------------------------------ */ +/* World tuning + info + native explode + profile/counters */ +/* ------------------------------------------------------------------ */ +LC_API double b2lc_world_gravity_x(int w) { b2WorldId id = worlds_get(w); return b2World_IsValid(id) ? (double)b2World_GetGravity(id).x : 0.0; } +LC_API double b2lc_world_gravity_y(int w) { b2WorldId id = worlds_get(w); return b2World_IsValid(id) ? (double)b2World_GetGravity(id).y : 0.0; } +LC_API void b2lc_world_set_restitution_threshold(int w, double v) { b2WorldId id = worlds_get(w); if (b2World_IsValid(id)) b2World_SetRestitutionThreshold(id, (float)v); } +LC_API double b2lc_world_restitution_threshold(int w) { b2WorldId id = worlds_get(w); return b2World_IsValid(id) ? (double)b2World_GetRestitutionThreshold(id) : 0.0; } +LC_API void b2lc_world_set_hit_event_threshold(int w, double v) { b2WorldId id = worlds_get(w); if (b2World_IsValid(id)) b2World_SetHitEventThreshold(id, (float)v); } +LC_API double b2lc_world_hit_event_threshold(int w) { b2WorldId id = worlds_get(w); return b2World_IsValid(id) ? (double)b2World_GetHitEventThreshold(id) : 0.0; } +LC_API void b2lc_world_set_contact_tuning(int w, double hertz, double damping, double pushSpeed) { b2WorldId id = worlds_get(w); if (b2World_IsValid(id)) b2World_SetContactTuning(id, (float)hertz, (float)damping, (float)pushSpeed); } +LC_API void b2lc_world_set_joint_tuning(int w, double hertz, double damping) { b2WorldId id = worlds_get(w); if (b2World_IsValid(id)) b2World_SetJointTuning(id, (float)hertz, (float)damping); } +LC_API void b2lc_world_set_maximum_linear_speed(int w, double v) { b2WorldId id = worlds_get(w); if (b2World_IsValid(id)) b2World_SetMaximumLinearSpeed(id, (float)v); } +LC_API double b2lc_world_maximum_linear_speed(int w) { b2WorldId id = worlds_get(w); return b2World_IsValid(id) ? (double)b2World_GetMaximumLinearSpeed(id) : 0.0; } +LC_API void b2lc_world_enable_warm_starting(int w, int f) { b2WorldId id = worlds_get(w); if (b2World_IsValid(id)) b2World_EnableWarmStarting(id, f ? true : false); } +LC_API int b2lc_world_is_warm_starting(int w) { b2WorldId id = worlds_get(w); return (b2World_IsValid(id) && b2World_IsWarmStartingEnabled(id)) ? 1 : 0; } +LC_API void b2lc_world_enable_speculative(int w, int f) { b2WorldId id = worlds_get(w); if (b2World_IsValid(id)) b2World_EnableSpeculative(id, f ? true : false); } +LC_API int b2lc_world_is_sleeping_enabled(int w) { b2WorldId id = worlds_get(w); return (b2World_IsValid(id) && b2World_IsSleepingEnabled(id)) ? 1 : 0; } +LC_API int b2lc_world_is_continuous_enabled(int w) { b2WorldId id = worlds_get(w); return (b2World_IsValid(id) && b2World_IsContinuousEnabled(id)) ? 1 : 0; } +LC_API int b2lc_world_awake_body_count(int w) { b2WorldId id = worlds_get(w); return b2World_IsValid(id) ? b2World_GetAwakeBodyCount(id) : 0; } + +LC_API void b2lc_world_explode(int w, double x, double y, double radius, double falloff, double impulsePerLength) { + b2WorldId id = worlds_get(w); + if (!b2World_IsValid(id)) return; + b2ExplosionDef ed = b2DefaultExplosionDef(); + ed.position = v2(x, y); + ed.radius = (float)radius; + ed.falloff = (float)falloff; + ed.impulsePerLength = (float)impulsePerLength; + b2World_Explode(id, &ed); +} + +static b2Profile s_profile; +LC_API void b2lc_world_profile_update(int w) { b2WorldId id = worlds_get(w); if (b2World_IsValid(id)) s_profile = b2World_GetProfile(id); else memset(&s_profile, 0, sizeof s_profile); } +LC_API double b2lc_world_profile_step(void) { return (double)s_profile.step; } +LC_API double b2lc_world_profile_pairs(void) { return (double)s_profile.pairs; } +LC_API double b2lc_world_profile_collide(void) { return (double)s_profile.collide; } +LC_API double b2lc_world_profile_solve(void) { return (double)s_profile.solve; } +LC_API double b2lc_world_profile_refit(void) { return (double)s_profile.refit; } +LC_API double b2lc_world_profile_sensors(void) { return (double)s_profile.sensors; } + +static b2Counters s_counters; +LC_API void b2lc_world_counters_update(int w) { b2WorldId id = worlds_get(w); if (b2World_IsValid(id)) s_counters = b2World_GetCounters(id); else memset(&s_counters, 0, sizeof s_counters); } +LC_API int b2lc_world_count_bodies(void) { return s_counters.bodyCount; } +LC_API int b2lc_world_count_shapes(void) { return s_counters.shapeCount; } +LC_API int b2lc_world_count_contacts(void) { return s_counters.contactCount; } +LC_API int b2lc_world_count_joints(void) { return s_counters.jointCount; } +LC_API int b2lc_world_count_islands(void) { return s_counters.islandCount; } + +/* ------------------------------------------------------------------ */ +/* Body: transforms, velocity-at-point, force/impulse-at-point, mass */ +/* ------------------------------------------------------------------ */ +LC_API double b2lc_body_world_point_x(int b, double lx, double ly) { b2BodyId id = bodies_get(b); return b2Body_IsValid(id) ? (double)b2Body_GetWorldPoint(id, v2(lx, ly)).x : 0.0; } +LC_API double b2lc_body_world_point_y(int b, double lx, double ly) { b2BodyId id = bodies_get(b); return b2Body_IsValid(id) ? (double)b2Body_GetWorldPoint(id, v2(lx, ly)).y : 0.0; } +LC_API double b2lc_body_local_point_x(int b, double wx, double wy) { b2BodyId id = bodies_get(b); return b2Body_IsValid(id) ? (double)b2Body_GetLocalPoint(id, v2(wx, wy)).x : 0.0; } +LC_API double b2lc_body_local_point_y(int b, double wx, double wy) { b2BodyId id = bodies_get(b); return b2Body_IsValid(id) ? (double)b2Body_GetLocalPoint(id, v2(wx, wy)).y : 0.0; } +LC_API double b2lc_body_world_vector_x(int b, double lx, double ly) { b2BodyId id = bodies_get(b); return b2Body_IsValid(id) ? (double)b2Body_GetWorldVector(id, v2(lx, ly)).x : 0.0; } +LC_API double b2lc_body_world_vector_y(int b, double lx, double ly) { b2BodyId id = bodies_get(b); return b2Body_IsValid(id) ? (double)b2Body_GetWorldVector(id, v2(lx, ly)).y : 0.0; } +LC_API double b2lc_body_local_vector_x(int b, double wx, double wy) { b2BodyId id = bodies_get(b); return b2Body_IsValid(id) ? (double)b2Body_GetLocalVector(id, v2(wx, wy)).x : 0.0; } +LC_API double b2lc_body_local_vector_y(int b, double wx, double wy) { b2BodyId id = bodies_get(b); return b2Body_IsValid(id) ? (double)b2Body_GetLocalVector(id, v2(wx, wy)).y : 0.0; } +LC_API double b2lc_body_world_point_velocity_x(int b, double wx, double wy) { b2BodyId id = bodies_get(b); return b2Body_IsValid(id) ? (double)b2Body_GetWorldPointVelocity(id, v2(wx, wy)).x : 0.0; } +LC_API double b2lc_body_world_point_velocity_y(int b, double wx, double wy) { b2BodyId id = bodies_get(b); return b2Body_IsValid(id) ? (double)b2Body_GetWorldPointVelocity(id, v2(wx, wy)).y : 0.0; } +LC_API double b2lc_body_local_point_velocity_x(int b, double lx, double ly) { b2BodyId id = bodies_get(b); return b2Body_IsValid(id) ? (double)b2Body_GetLocalPointVelocity(id, v2(lx, ly)).x : 0.0; } +LC_API double b2lc_body_local_point_velocity_y(int b, double lx, double ly) { b2BodyId id = bodies_get(b); return b2Body_IsValid(id) ? (double)b2Body_GetLocalPointVelocity(id, v2(lx, ly)).y : 0.0; } + +LC_API void b2lc_body_apply_force_at(int b, double fx, double fy, double px, double py, int wake) { b2BodyId id = bodies_get(b); if (b2Body_IsValid(id)) b2Body_ApplyForce(id, v2(fx, fy), v2(px, py), wake ? true : false); } +LC_API void b2lc_body_apply_impulse_at(int b, double ix, double iy, double px, double py, int wake) { b2BodyId id = bodies_get(b); if (b2Body_IsValid(id)) b2Body_ApplyLinearImpulse(id, v2(ix, iy), v2(px, py), wake ? true : false); } + +LC_API double b2lc_body_rotational_inertia(int b) { b2BodyId id = bodies_get(b); return b2Body_IsValid(id) ? (double)b2Body_GetRotationalInertia(id) : 0.0; } +LC_API double b2lc_body_local_center_x(int b) { b2BodyId id = bodies_get(b); return b2Body_IsValid(id) ? (double)b2Body_GetLocalCenterOfMass(id).x : 0.0; } +LC_API double b2lc_body_local_center_y(int b) { b2BodyId id = bodies_get(b); return b2Body_IsValid(id) ? (double)b2Body_GetLocalCenterOfMass(id).y : 0.0; } + +/* mass-data stash (shared by body and shape mass-data getters) */ +static b2MassData s_md; +LC_API void b2lc_body_mass_data_update(int b) { b2BodyId id = bodies_get(b); if (b2Body_IsValid(id)) s_md = b2Body_GetMassData(id); else memset(&s_md, 0, sizeof s_md); } +LC_API double b2lc_md_mass(void) { return (double)s_md.mass; } +LC_API double b2lc_md_center_x(void) { return (double)s_md.center.x; } +LC_API double b2lc_md_center_y(void) { return (double)s_md.center.y; } +LC_API double b2lc_md_inertia(void) { return (double)s_md.rotationalInertia; } +LC_API void b2lc_body_set_mass_data(int b, double mass, double cx, double cy, double inertia) { + b2BodyId id = bodies_get(b); + if (!b2Body_IsValid(id)) return; + b2MassData md; md.mass = (float)mass; md.center = v2(cx, cy); md.rotationalInertia = (float)inertia; + b2Body_SetMassData(id, md); +} +LC_API void b2lc_body_apply_mass_from_shapes(int b) { b2BodyId id = bodies_get(b); if (b2Body_IsValid(id)) b2Body_ApplyMassFromShapes(id); } + +LC_API void b2lc_body_set_target_transform(int b, double x, double y, double angle, double dt) { + b2BodyId id = bodies_get(b); + if (!b2Body_IsValid(id)) return; + b2Transform t; t.p = v2(x, y); t.q = rotOf(angle); + b2Body_SetTargetTransform(id, t, (float)dt); +} + +LC_API void b2lc_body_enable_sleep(int b, int f) { b2BodyId id = bodies_get(b); if (b2Body_IsValid(id)) b2Body_EnableSleep(id, f ? true : false); } +LC_API int b2lc_body_is_sleep_enabled(int b) { b2BodyId id = bodies_get(b); return (b2Body_IsValid(id) && b2Body_IsSleepEnabled(id)) ? 1 : 0; } +LC_API int b2lc_body_is_fixed_rotation(int b) { b2BodyId id = bodies_get(b); return (b2Body_IsValid(id) && b2Body_IsFixedRotation(id)) ? 1 : 0; } +LC_API void b2lc_body_enable_contact_events(int b, int f) { b2BodyId id = bodies_get(b); if (b2Body_IsValid(id)) b2Body_EnableContactEvents(id, f ? true : false); } +LC_API void b2lc_body_enable_hit_events(int b, int f) { b2BodyId id = bodies_get(b); if (b2Body_IsValid(id)) b2Body_EnableHitEvents(id, f ? true : false); } + +/* AABB stash (shared by body and shape AABB getters) */ +static b2AABB s_aabb; +LC_API void b2lc_body_aabb_update(int b) { b2BodyId id = bodies_get(b); if (b2Body_IsValid(id)) s_aabb = b2Body_ComputeAABB(id); else memset(&s_aabb, 0, sizeof s_aabb); } +LC_API double b2lc_aabb_lower_x(void) { return (double)s_aabb.lowerBound.x; } +LC_API double b2lc_aabb_lower_y(void) { return (double)s_aabb.lowerBound.y; } +LC_API double b2lc_aabb_upper_x(void) { return (double)s_aabb.upperBound.x; } +LC_API double b2lc_aabb_upper_y(void) { return (double)s_aabb.upperBound.y; } + +/* body shape / joint enumeration (call _count to snapshot, then _at by index) */ +static b2ShapeId *s_bShapes = NULL; static int s_bShapesCap = 0, s_bShapeCnt = 0; +static b2JointId *s_bJoints = NULL; static int s_bJointsCap = 0, s_bJointCnt = 0; +LC_API int b2lc_body_shape_count(int b) { + b2BodyId id = bodies_get(b); + if (!b2Body_IsValid(id)) { s_bShapeCnt = 0; return 0; } + int n = b2Body_GetShapeCount(id); + if (n > s_bShapesCap) { s_bShapesCap = n; s_bShapes = (b2ShapeId *)realloc(s_bShapes, (size_t)n * sizeof(b2ShapeId)); } + s_bShapeCnt = (n > 0) ? b2Body_GetShapes(id, s_bShapes, n) : 0; + return s_bShapeCnt; +} +LC_API int b2lc_body_shape_at(int i) { return (i >= 0 && i < s_bShapeCnt) ? shape_h(s_bShapes[i]) : 0; } +LC_API int b2lc_body_joint_count(int b) { + b2BodyId id = bodies_get(b); + if (!b2Body_IsValid(id)) { s_bJointCnt = 0; return 0; } + int n = b2Body_GetJointCount(id); + if (n > s_bJointsCap) { s_bJointsCap = n; s_bJoints = (b2JointId *)realloc(s_bJoints, (size_t)n * sizeof(b2JointId)); } + s_bJointCnt = (n > 0) ? b2Body_GetJoints(id, s_bJoints, n) : 0; + return s_bJointCnt; +} +LC_API int b2lc_body_joint_at(int i) { return (i >= 0 && i < s_bJointCnt) ? joint_handle_of(s_bJoints[i]) : 0; } + +/* ------------------------------------------------------------------ */ +/* Shape: type/material/filter getters, event flags, geometry get/set, */ +/* per-shape ray cast, AABB, closest point, mass data, sensor overlaps */ +/* ------------------------------------------------------------------ */ +LC_API int b2lc_shape_type(int s) { b2ShapeId id = shapes_get(s); return b2Shape_IsValid(id) ? (int)b2Shape_GetType(id) : 0; } +LC_API int b2lc_shape_is_sensor(int s) { b2ShapeId id = shapes_get(s); return (b2Shape_IsValid(id) && b2Shape_IsSensor(id)) ? 1 : 0; } +LC_API double b2lc_shape_density(int s) { b2ShapeId id = shapes_get(s); return b2Shape_IsValid(id) ? (double)b2Shape_GetDensity(id) : 0.0; } +LC_API double b2lc_shape_friction(int s) { b2ShapeId id = shapes_get(s); return b2Shape_IsValid(id) ? (double)b2Shape_GetFriction(id) : 0.0; } +LC_API double b2lc_shape_restitution(int s) { b2ShapeId id = shapes_get(s); return b2Shape_IsValid(id) ? (double)b2Shape_GetRestitution(id) : 0.0; } +LC_API int b2lc_shape_material_id(int s) { b2ShapeId id = shapes_get(s); return b2Shape_IsValid(id) ? b2Shape_GetMaterial(id) : 0; } +LC_API void b2lc_shape_set_material_id(int s, int m) { b2ShapeId id = shapes_get(s); if (b2Shape_IsValid(id)) b2Shape_SetMaterial(id, m); } + +LC_API void b2lc_shape_set_filter(int s, double cat, double mask, int group) { + b2ShapeId id = shapes_get(s); + if (!b2Shape_IsValid(id)) return; + b2Filter f; f.categoryBits = (uint64_t)(uint32_t)cat; f.maskBits = (uint64_t)(uint32_t)mask; f.groupIndex = group; + b2Shape_SetFilter(id, f); +} +LC_API double b2lc_shape_filter_category(int s) { b2ShapeId id = shapes_get(s); return b2Shape_IsValid(id) ? (double)(uint32_t)b2Shape_GetFilter(id).categoryBits : 0.0; } +LC_API double b2lc_shape_filter_mask(int s) { b2ShapeId id = shapes_get(s); return b2Shape_IsValid(id) ? (double)(uint32_t)b2Shape_GetFilter(id).maskBits : 0.0; } +LC_API int b2lc_shape_filter_group(int s) { b2ShapeId id = shapes_get(s); return b2Shape_IsValid(id) ? b2Shape_GetFilter(id).groupIndex : 0; } + +LC_API void b2lc_shape_enable_sensor_events(int s, int f) { b2ShapeId id = shapes_get(s); if (b2Shape_IsValid(id)) b2Shape_EnableSensorEvents(id, f ? true : false); } +LC_API int b2lc_shape_are_sensor_events_enabled(int s) { b2ShapeId id = shapes_get(s); return (b2Shape_IsValid(id) && b2Shape_AreSensorEventsEnabled(id)) ? 1 : 0; } +LC_API void b2lc_shape_enable_contact_events(int s, int f) { b2ShapeId id = shapes_get(s); if (b2Shape_IsValid(id)) b2Shape_EnableContactEvents(id, f ? true : false); } +LC_API int b2lc_shape_are_contact_events_enabled(int s) { b2ShapeId id = shapes_get(s); return (b2Shape_IsValid(id) && b2Shape_AreContactEventsEnabled(id)) ? 1 : 0; } +LC_API void b2lc_shape_enable_hit_events(int s, int f) { b2ShapeId id = shapes_get(s); if (b2Shape_IsValid(id)) b2Shape_EnableHitEvents(id, f ? true : false); } +LC_API int b2lc_shape_are_hit_events_enabled(int s) { b2ShapeId id = shapes_get(s); return (b2Shape_IsValid(id) && b2Shape_AreHitEventsEnabled(id)) ? 1 : 0; } +LC_API void b2lc_shape_enable_presolve_events(int s, int f) { b2ShapeId id = shapes_get(s); if (b2Shape_IsValid(id)) b2Shape_EnablePreSolveEvents(id, f ? true : false); } + +/* geometry getters: circle/capsule/segment share a small scalar stash */ +static double s_geom[5]; +LC_API void b2lc_shape_circle_update(int s) { b2ShapeId id = shapes_get(s); if (b2Shape_IsValid(id) && b2Shape_GetType(id) == b2_circleShape) { b2Circle c = b2Shape_GetCircle(id); s_geom[0] = c.center.x; s_geom[1] = c.center.y; s_geom[2] = c.radius; } } +LC_API double b2lc_shape_circle_x(void) { return s_geom[0]; } +LC_API double b2lc_shape_circle_y(void) { return s_geom[1]; } +LC_API double b2lc_shape_circle_radius(void) { return s_geom[2]; } +LC_API void b2lc_shape_capsule_update(int s) { b2ShapeId id = shapes_get(s); if (b2Shape_IsValid(id) && b2Shape_GetType(id) == b2_capsuleShape) { b2Capsule c = b2Shape_GetCapsule(id); s_geom[0] = c.center1.x; s_geom[1] = c.center1.y; s_geom[2] = c.center2.x; s_geom[3] = c.center2.y; s_geom[4] = c.radius; } } +LC_API double b2lc_shape_capsule_x1(void) { return s_geom[0]; } +LC_API double b2lc_shape_capsule_y1(void) { return s_geom[1]; } +LC_API double b2lc_shape_capsule_x2(void) { return s_geom[2]; } +LC_API double b2lc_shape_capsule_y2(void) { return s_geom[3]; } +LC_API double b2lc_shape_capsule_radius(void) { return s_geom[4]; } +LC_API void b2lc_shape_segment_update(int s) { b2ShapeId id = shapes_get(s); if (b2Shape_IsValid(id) && b2Shape_GetType(id) == b2_segmentShape) { b2Segment g = b2Shape_GetSegment(id); s_geom[0] = g.point1.x; s_geom[1] = g.point1.y; s_geom[2] = g.point2.x; s_geom[3] = g.point2.y; } } +LC_API double b2lc_shape_segment_x1(void) { return s_geom[0]; } +LC_API double b2lc_shape_segment_y1(void) { return s_geom[1]; } +LC_API double b2lc_shape_segment_x2(void) { return s_geom[2]; } +LC_API double b2lc_shape_segment_y2(void) { return s_geom[3]; } + +/* polygon geometry readback (up to 8 verts + radius) */ +static b2Polygon s_polyRead; +LC_API int b2lc_shape_polygon_update(int s) { b2ShapeId id = shapes_get(s); if (b2Shape_IsValid(id) && b2Shape_GetType(id) == b2_polygonShape) { s_polyRead = b2Shape_GetPolygon(id); return s_polyRead.count; } s_polyRead.count = 0; return 0; } +LC_API int b2lc_shape_polygon_count(void) { return s_polyRead.count; } +LC_API double b2lc_shape_polygon_vx(int i) { return (i >= 0 && i < s_polyRead.count) ? (double)s_polyRead.vertices[i].x : 0.0; } +LC_API double b2lc_shape_polygon_vy(int i) { return (i >= 0 && i < s_polyRead.count) ? (double)s_polyRead.vertices[i].y : 0.0; } +LC_API double b2lc_shape_polygon_radius(void) { return (double)s_polyRead.radius; } + +/* geometry setters */ +LC_API void b2lc_shape_set_circle(int s, double cx, double cy, double r) { b2ShapeId id = shapes_get(s); if (!b2Shape_IsValid(id)) return; b2Circle c; c.center = v2(cx, cy); c.radius = (float)r; b2Shape_SetCircle(id, &c); } +LC_API void b2lc_shape_set_capsule(int s, double x1, double y1, double x2, double y2, double r) { b2ShapeId id = shapes_get(s); if (!b2Shape_IsValid(id)) return; b2Capsule c; c.center1 = v2(x1, y1); c.center2 = v2(x2, y2); c.radius = (float)r; b2Shape_SetCapsule(id, &c); } +LC_API void b2lc_shape_set_segment(int s, double x1, double y1, double x2, double y2) { b2ShapeId id = shapes_get(s); if (!b2Shape_IsValid(id)) return; b2Segment g; g.point1 = v2(x1, y1); g.point2 = v2(x2, y2); b2Shape_SetSegment(id, &g); } +LC_API void b2lc_shape_set_polygon(int s) { b2ShapeId id = shapes_get(s); if (!b2Shape_IsValid(id) || s_polyCnt < 3) return; b2Hull hull = b2ComputeHull(s_poly, s_polyCnt); if (hull.count < 3) return; b2Polygon p = b2MakePolygon(&hull, 0.0f); b2Shape_SetPolygon(id, &p); } + +/* per-shape ray cast (stash) */ +static int s_shapeRayHit = 0; +static double s_shapeRay[5]; /* px, py, nx, ny, fraction */ +LC_API int b2lc_shape_raycast(int s, double x1, double y1, double x2, double y2) { + s_shapeRayHit = 0; memset(s_shapeRay, 0, sizeof s_shapeRay); + b2ShapeId id = shapes_get(s); + if (!b2Shape_IsValid(id)) return 0; + b2RayCastInput in; in.origin = v2(x1, y1); in.translation = v2(x2 - x1, y2 - y1); in.maxFraction = 1.0f; + b2CastOutput out = b2Shape_RayCast(id, &in); + if (out.hit) { + s_shapeRayHit = 1; + s_shapeRay[0] = out.point.x; s_shapeRay[1] = out.point.y; + s_shapeRay[2] = out.normal.x; s_shapeRay[3] = out.normal.y; + s_shapeRay[4] = out.fraction; + } + return s_shapeRayHit; +} +LC_API double b2lc_shape_ray_x(void) { return s_shapeRay[0]; } +LC_API double b2lc_shape_ray_y(void) { return s_shapeRay[1]; } +LC_API double b2lc_shape_ray_normal_x(void) { return s_shapeRay[2]; } +LC_API double b2lc_shape_ray_normal_y(void) { return s_shapeRay[3]; } +LC_API double b2lc_shape_ray_fraction(void) { return s_shapeRay[4]; } + +LC_API void b2lc_shape_aabb_update(int s) { b2ShapeId id = shapes_get(s); if (b2Shape_IsValid(id)) s_aabb = b2Shape_GetAABB(id); else memset(&s_aabb, 0, sizeof s_aabb); } +LC_API double b2lc_shape_closest_point_x(int s, double tx, double ty) { b2ShapeId id = shapes_get(s); return b2Shape_IsValid(id) ? (double)b2Shape_GetClosestPoint(id, v2(tx, ty)).x : 0.0; } +LC_API double b2lc_shape_closest_point_y(int s, double tx, double ty) { b2ShapeId id = shapes_get(s); return b2Shape_IsValid(id) ? (double)b2Shape_GetClosestPoint(id, v2(tx, ty)).y : 0.0; } +LC_API void b2lc_shape_mass_data_update(int s) { b2ShapeId id = shapes_get(s); if (b2Shape_IsValid(id)) s_md = b2Shape_GetMassData(id); else memset(&s_md, 0, sizeof s_md); } + +/* sensor overlaps (poll which shapes currently sit inside a sensor) */ +static b2ShapeId *s_ov = NULL; static int s_ovCap = 0, s_ovCnt = 0; +LC_API int b2lc_shape_sensor_capacity(int s) { b2ShapeId id = shapes_get(s); return b2Shape_IsValid(id) ? b2Shape_GetSensorCapacity(id) : 0; } +LC_API int b2lc_shape_sensor_overlaps_update(int s) { + s_ovCnt = 0; + b2ShapeId id = shapes_get(s); + if (!b2Shape_IsValid(id)) return 0; + int cap = b2Shape_GetSensorCapacity(id); + if (cap > s_ovCap) { s_ovCap = cap; s_ov = (b2ShapeId *)realloc(s_ov, (size_t)cap * sizeof(b2ShapeId)); } + s_ovCnt = (cap > 0) ? b2Shape_GetSensorOverlaps(id, s_ov, cap) : 0; + return s_ovCnt; +} +LC_API int b2lc_shape_sensor_overlap_count(void) { return s_ovCnt; } +LC_API int b2lc_shape_sensor_overlap_at(int i) { return (i >= 0 && i < s_ovCnt) ? shape_h(s_ov[i]) : 0; } + +/* ------------------------------------------------------------------ */ +/* Chains (smooth multi-segment terrain). New handle table. */ +/* ------------------------------------------------------------------ */ +#define LC_MAX_CHAIN 4096 +static b2Vec2 s_chain[LC_MAX_CHAIN]; +static int s_chainCnt = 0; +LC_API void b2lc_chain_begin(void) { s_chainCnt = 0; } +LC_API void b2lc_chain_add_point(double x, double y) { if (s_chainCnt < LC_MAX_CHAIN) s_chain[s_chainCnt++] = v2(x, y); } +LC_API int b2lc_chain_create(int b, int isLoop, double friction, double restitution) { + b2BodyId body = bodies_get(b); + if (!b2Body_IsValid(body) || s_chainCnt < 4) return 0; + b2ChainDef cd = b2DefaultChainDef(); + cd.points = s_chain; + cd.count = s_chainCnt; + b2SurfaceMaterial mat = b2DefaultSurfaceMaterial(); + mat.friction = (float)friction; + mat.restitution = (float)restitution; + cd.materials = &mat; + cd.materialCount = 1; + cd.isLoop = isLoop ? true : false; + cd.enableSensorEvents = true; /* let sensors detect terrain */ + if (s_sd.haveFilter) { + cd.filter.categoryBits = (uint64_t)(uint32_t)s_sd.catBits; + cd.filter.maskBits = (uint64_t)(uint32_t)s_sd.maskBits; + cd.filter.groupIndex = s_sd.groupIndex; + } + b2ChainId id = b2CreateChain(body, &cd); + reset_shapedef(); + return chains_add(id); +} +LC_API void b2lc_chain_destroy(int c) { b2ChainId id = chains_get(c); if (b2Chain_IsValid(id)) b2DestroyChain(id); chains_free_handle(c); } +LC_API int b2lc_chain_is_valid(int c) { return b2Chain_IsValid(chains_get(c)) ? 1 : 0; } +LC_API void b2lc_chain_set_friction(int c, double f) { b2ChainId id = chains_get(c); if (b2Chain_IsValid(id)) b2Chain_SetFriction(id, (float)f); } +LC_API double b2lc_chain_friction(int c) { b2ChainId id = chains_get(c); return b2Chain_IsValid(id) ? (double)b2Chain_GetFriction(id) : 0.0; } +LC_API void b2lc_chain_set_restitution(int c, double r) { b2ChainId id = chains_get(c); if (b2Chain_IsValid(id)) b2Chain_SetRestitution(id, (float)r); } +LC_API double b2lc_chain_restitution(int c) { b2ChainId id = chains_get(c); return b2Chain_IsValid(id) ? (double)b2Chain_GetRestitution(id) : 0.0; } +static b2ShapeId *s_chainSeg = NULL; static int s_chainSegCap = 0, s_chainSegCnt = 0; +LC_API int b2lc_chain_segment_count(int c) { + b2ChainId id = chains_get(c); + if (!b2Chain_IsValid(id)) { s_chainSegCnt = 0; return 0; } + int n = b2Chain_GetSegmentCount(id); + if (n > s_chainSegCap) { s_chainSegCap = n; s_chainSeg = (b2ShapeId *)realloc(s_chainSeg, (size_t)n * sizeof(b2ShapeId)); } + s_chainSegCnt = (n > 0) ? b2Chain_GetSegments(id, s_chainSeg, n) : 0; + return s_chainSegCnt; +} +LC_API int b2lc_chain_segment_at(int i) { return (i >= 0 && i < s_chainSegCnt) ? shape_h(s_chainSeg[i]) : 0; } + +/* ------------------------------------------------------------------ */ +/* Joints: generic surface */ +/* ------------------------------------------------------------------ */ +LC_API int b2lc_joint_type(int j) { b2JointId id = joints_get(j); return b2Joint_IsValid(id) ? (int)b2Joint_GetType(id) : 0; } +LC_API int b2lc_joint_body_a(int j) { b2JointId id = joints_get(j); return b2Joint_IsValid(id) ? body_h(b2Joint_GetBodyA(id)) : 0; } +LC_API int b2lc_joint_body_b(int j) { b2JointId id = joints_get(j); return b2Joint_IsValid(id) ? body_h(b2Joint_GetBodyB(id)) : 0; } +LC_API double b2lc_joint_local_anchor_a_x(int j) { b2JointId id = joints_get(j); return b2Joint_IsValid(id) ? (double)b2Joint_GetLocalAnchorA(id).x : 0.0; } +LC_API double b2lc_joint_local_anchor_a_y(int j) { b2JointId id = joints_get(j); return b2Joint_IsValid(id) ? (double)b2Joint_GetLocalAnchorA(id).y : 0.0; } +LC_API double b2lc_joint_local_anchor_b_x(int j) { b2JointId id = joints_get(j); return b2Joint_IsValid(id) ? (double)b2Joint_GetLocalAnchorB(id).x : 0.0; } +LC_API double b2lc_joint_local_anchor_b_y(int j) { b2JointId id = joints_get(j); return b2Joint_IsValid(id) ? (double)b2Joint_GetLocalAnchorB(id).y : 0.0; } +LC_API int b2lc_joint_get_collide_connected(int j) { b2JointId id = joints_get(j); return (b2Joint_IsValid(id) && b2Joint_GetCollideConnected(id)) ? 1 : 0; } +LC_API void b2lc_joint_set_collide_connected(int j, int f){ b2JointId id = joints_get(j); if (b2Joint_IsValid(id)) b2Joint_SetCollideConnected(id, f ? true : false); } +LC_API double b2lc_joint_constraint_force_x(int j) { b2JointId id = joints_get(j); return b2Joint_IsValid(id) ? (double)b2Joint_GetConstraintForce(id).x : 0.0; } +LC_API double b2lc_joint_constraint_force_y(int j) { b2JointId id = joints_get(j); return b2Joint_IsValid(id) ? (double)b2Joint_GetConstraintForce(id).y : 0.0; } +LC_API double b2lc_joint_constraint_torque(int j) { b2JointId id = joints_get(j); return b2Joint_IsValid(id) ? (double)b2Joint_GetConstraintTorque(id) : 0.0; } +LC_API void b2lc_joint_wake_bodies(int j) { b2JointId id = joints_get(j); if (b2Joint_IsValid(id)) b2Joint_WakeBodies(id); } + +/* ---- Motor joint (drive bodyB to an offset pose from bodyA) -------- */ +LC_API int b2lc_joint_motor(int w, int bA, int bB, double offX, double offY, double angOff, + double maxForce, double maxTorque, double corr, int collide) { + b2WorldId wid = worlds_get(w); + b2BodyId a = bodies_get(bA), b = bodies_get(bB); + if (!b2World_IsValid(wid) || !b2Body_IsValid(a) || !b2Body_IsValid(b)) return 0; + b2MotorJointDef jd = b2DefaultMotorJointDef(); + jd.bodyIdA = a; jd.bodyIdB = b; + jd.linearOffset = v2(offX, offY); + jd.angularOffset = (float)angOff; + jd.maxForce = (float)maxForce; + jd.maxTorque = (float)maxTorque; + jd.correctionFactor = (float)corr; + jd.collideConnected = collide ? true : false; + return register_joint(b2CreateMotorJoint(wid, &jd)); +} +LC_API void b2lc_motor_set_linear_offset(int j, double x, double y) { b2JointId id = joints_get(j); if (b2Joint_IsValid(id)) b2MotorJoint_SetLinearOffset(id, v2(x, y)); } +LC_API double b2lc_motor_linear_offset_x(int j) { b2JointId id = joints_get(j); return b2Joint_IsValid(id) ? (double)b2MotorJoint_GetLinearOffset(id).x : 0.0; } +LC_API double b2lc_motor_linear_offset_y(int j) { b2JointId id = joints_get(j); return b2Joint_IsValid(id) ? (double)b2MotorJoint_GetLinearOffset(id).y : 0.0; } +LC_API void b2lc_motor_set_angular_offset(int j, double a) { b2JointId id = joints_get(j); if (b2Joint_IsValid(id)) b2MotorJoint_SetAngularOffset(id, (float)a); } +LC_API double b2lc_motor_angular_offset(int j) { b2JointId id = joints_get(j); return b2Joint_IsValid(id) ? (double)b2MotorJoint_GetAngularOffset(id) : 0.0; } +LC_API void b2lc_motor_set_max_force(int j, double f) { b2JointId id = joints_get(j); if (b2Joint_IsValid(id)) b2MotorJoint_SetMaxForce(id, (float)f); } +LC_API double b2lc_motor_max_force(int j) { b2JointId id = joints_get(j); return b2Joint_IsValid(id) ? (double)b2MotorJoint_GetMaxForce(id) : 0.0; } +LC_API void b2lc_motor_set_max_torque(int j, double t) { b2JointId id = joints_get(j); if (b2Joint_IsValid(id)) b2MotorJoint_SetMaxTorque(id, (float)t); } +LC_API double b2lc_motor_max_torque(int j) { b2JointId id = joints_get(j); return b2Joint_IsValid(id) ? (double)b2MotorJoint_GetMaxTorque(id) : 0.0; } +LC_API void b2lc_motor_set_correction_factor(int j, double c) { b2JointId id = joints_get(j); if (b2Joint_IsValid(id)) b2MotorJoint_SetCorrectionFactor(id, (float)c); } +LC_API double b2lc_motor_correction_factor(int j) { b2JointId id = joints_get(j); return b2Joint_IsValid(id) ? (double)b2MotorJoint_GetCorrectionFactor(id) : 0.0; } + +/* ---- Filter joint (disable collision between two specific bodies) -- */ +LC_API int b2lc_joint_filter(int w, int bA, int bB) { + b2WorldId wid = worlds_get(w); + b2BodyId a = bodies_get(bA), b = bodies_get(bB); + if (!b2World_IsValid(wid) || !b2Body_IsValid(a) || !b2Body_IsValid(b)) return 0; + b2FilterJointDef jd = b2DefaultFilterJointDef(); + jd.bodyIdA = a; jd.bodyIdB = b; + return register_joint(b2CreateFilterJoint(wid, &jd)); +} + +/* ---- Revolute joint: granular spring/limit/motor get+set ----------- */ +LC_API void b2lc_revolute_enable_spring(int j, int f) { b2JointId id = joints_get(j); if (b2Joint_IsValid(id)) b2RevoluteJoint_EnableSpring(id, f ? true : false); } +LC_API int b2lc_revolute_is_spring_enabled(int j) { b2JointId id = joints_get(j); return (b2Joint_IsValid(id) && b2RevoluteJoint_IsSpringEnabled(id)) ? 1 : 0; } +LC_API void b2lc_revolute_set_spring_hertz(int j, double hz) { b2JointId id = joints_get(j); if (b2Joint_IsValid(id)) b2RevoluteJoint_SetSpringHertz(id, (float)hz); } +LC_API double b2lc_revolute_spring_hertz(int j) { b2JointId id = joints_get(j); return b2Joint_IsValid(id) ? (double)b2RevoluteJoint_GetSpringHertz(id) : 0.0; } +LC_API void b2lc_revolute_set_spring_damping(int j, double d){ b2JointId id = joints_get(j); if (b2Joint_IsValid(id)) b2RevoluteJoint_SetSpringDampingRatio(id, (float)d); } +LC_API double b2lc_revolute_spring_damping(int j) { b2JointId id = joints_get(j); return b2Joint_IsValid(id) ? (double)b2RevoluteJoint_GetSpringDampingRatio(id) : 0.0; } +LC_API int b2lc_revolute_is_limit_enabled(int j) { b2JointId id = joints_get(j); return (b2Joint_IsValid(id) && b2RevoluteJoint_IsLimitEnabled(id)) ? 1 : 0; } +LC_API double b2lc_revolute_lower_limit(int j) { b2JointId id = joints_get(j); return b2Joint_IsValid(id) ? (double)b2RevoluteJoint_GetLowerLimit(id) : 0.0; } +LC_API double b2lc_revolute_upper_limit(int j) { b2JointId id = joints_get(j); return b2Joint_IsValid(id) ? (double)b2RevoluteJoint_GetUpperLimit(id) : 0.0; } +LC_API int b2lc_revolute_is_motor_enabled(int j) { b2JointId id = joints_get(j); return (b2Joint_IsValid(id) && b2RevoluteJoint_IsMotorEnabled(id)) ? 1 : 0; } +LC_API void b2lc_revolute_set_motor_speed(int j, double s) { b2JointId id = joints_get(j); if (b2Joint_IsValid(id)) b2RevoluteJoint_SetMotorSpeed(id, (float)s); } +LC_API double b2lc_revolute_motor_speed(int j) { b2JointId id = joints_get(j); return b2Joint_IsValid(id) ? (double)b2RevoluteJoint_GetMotorSpeed(id) : 0.0; } +LC_API double b2lc_revolute_motor_torque(int j) { b2JointId id = joints_get(j); return b2Joint_IsValid(id) ? (double)b2RevoluteJoint_GetMotorTorque(id) : 0.0; } +LC_API void b2lc_revolute_set_max_motor_torque(int j, double t){ b2JointId id = joints_get(j); if (b2Joint_IsValid(id)) b2RevoluteJoint_SetMaxMotorTorque(id, (float)t); } +LC_API double b2lc_revolute_max_motor_torque(int j) { b2JointId id = joints_get(j); return b2Joint_IsValid(id) ? (double)b2RevoluteJoint_GetMaxMotorTorque(id) : 0.0; } + +/* ---- Prismatic joint: granular spring/limit/motor get+set ---------- */ +LC_API void b2lc_prismatic_enable_spring(int j, int f) { b2JointId id = joints_get(j); if (b2Joint_IsValid(id)) b2PrismaticJoint_EnableSpring(id, f ? true : false); } +LC_API int b2lc_prismatic_is_spring_enabled(int j) { b2JointId id = joints_get(j); return (b2Joint_IsValid(id) && b2PrismaticJoint_IsSpringEnabled(id)) ? 1 : 0; } +LC_API void b2lc_prismatic_set_spring_hertz(int j, double hz) { b2JointId id = joints_get(j); if (b2Joint_IsValid(id)) b2PrismaticJoint_SetSpringHertz(id, (float)hz); } +LC_API double b2lc_prismatic_spring_hertz(int j) { b2JointId id = joints_get(j); return b2Joint_IsValid(id) ? (double)b2PrismaticJoint_GetSpringHertz(id) : 0.0; } +LC_API void b2lc_prismatic_set_spring_damping(int j, double d){ b2JointId id = joints_get(j); if (b2Joint_IsValid(id)) b2PrismaticJoint_SetSpringDampingRatio(id, (float)d); } +LC_API double b2lc_prismatic_spring_damping(int j) { b2JointId id = joints_get(j); return b2Joint_IsValid(id) ? (double)b2PrismaticJoint_GetSpringDampingRatio(id) : 0.0; } +LC_API int b2lc_prismatic_is_limit_enabled(int j) { b2JointId id = joints_get(j); return (b2Joint_IsValid(id) && b2PrismaticJoint_IsLimitEnabled(id)) ? 1 : 0; } +LC_API double b2lc_prismatic_lower_limit(int j) { b2JointId id = joints_get(j); return b2Joint_IsValid(id) ? (double)b2PrismaticJoint_GetLowerLimit(id) : 0.0; } +LC_API double b2lc_prismatic_upper_limit(int j) { b2JointId id = joints_get(j); return b2Joint_IsValid(id) ? (double)b2PrismaticJoint_GetUpperLimit(id) : 0.0; } +LC_API int b2lc_prismatic_is_motor_enabled(int j) { b2JointId id = joints_get(j); return (b2Joint_IsValid(id) && b2PrismaticJoint_IsMotorEnabled(id)) ? 1 : 0; } +LC_API void b2lc_prismatic_set_motor_speed(int j, double s) { b2JointId id = joints_get(j); if (b2Joint_IsValid(id)) b2PrismaticJoint_SetMotorSpeed(id, (float)s); } +LC_API double b2lc_prismatic_motor_speed(int j) { b2JointId id = joints_get(j); return b2Joint_IsValid(id) ? (double)b2PrismaticJoint_GetMotorSpeed(id) : 0.0; } +LC_API double b2lc_prismatic_motor_force(int j) { b2JointId id = joints_get(j); return b2Joint_IsValid(id) ? (double)b2PrismaticJoint_GetMotorForce(id) : 0.0; } +LC_API double b2lc_prismatic_max_motor_force(int j) { b2JointId id = joints_get(j); return b2Joint_IsValid(id) ? (double)b2PrismaticJoint_GetMaxMotorForce(id) : 0.0; } +LC_API double b2lc_prismatic_speed(int j) { b2JointId id = joints_get(j); return b2Joint_IsValid(id) ? (double)b2PrismaticJoint_GetSpeed(id) : 0.0; } + +/* ---- Distance joint: granular spring/limit/motor get+set ----------- */ +LC_API int b2lc_distance_is_spring_enabled(int j) { b2JointId id = joints_get(j); return (b2Joint_IsValid(id) && b2DistanceJoint_IsSpringEnabled(id)) ? 1 : 0; } +LC_API double b2lc_distance_spring_hertz(int j) { b2JointId id = joints_get(j); return b2Joint_IsValid(id) ? (double)b2DistanceJoint_GetSpringHertz(id) : 0.0; } +LC_API double b2lc_distance_spring_damping(int j) { b2JointId id = joints_get(j); return b2Joint_IsValid(id) ? (double)b2DistanceJoint_GetSpringDampingRatio(id) : 0.0; } +LC_API int b2lc_distance_is_limit_enabled(int j) { b2JointId id = joints_get(j); return (b2Joint_IsValid(id) && b2DistanceJoint_IsLimitEnabled(id)) ? 1 : 0; } +LC_API double b2lc_distance_min_length(int j) { b2JointId id = joints_get(j); return b2Joint_IsValid(id) ? (double)b2DistanceJoint_GetMinLength(id) : 0.0; } +LC_API double b2lc_distance_max_length(int j) { b2JointId id = joints_get(j); return b2Joint_IsValid(id) ? (double)b2DistanceJoint_GetMaxLength(id) : 0.0; } +LC_API double b2lc_distance_current_length(int j) { b2JointId id = joints_get(j); return b2Joint_IsValid(id) ? (double)b2DistanceJoint_GetCurrentLength(id) : 0.0; } +LC_API void b2lc_distance_enable_motor(int j, int f) { b2JointId id = joints_get(j); if (b2Joint_IsValid(id)) b2DistanceJoint_EnableMotor(id, f ? true : false); } +LC_API int b2lc_distance_is_motor_enabled(int j) { b2JointId id = joints_get(j); return (b2Joint_IsValid(id) && b2DistanceJoint_IsMotorEnabled(id)) ? 1 : 0; } +LC_API void b2lc_distance_set_motor_speed(int j, double s) { b2JointId id = joints_get(j); if (b2Joint_IsValid(id)) b2DistanceJoint_SetMotorSpeed(id, (float)s); } +LC_API double b2lc_distance_motor_speed(int j) { b2JointId id = joints_get(j); return b2Joint_IsValid(id) ? (double)b2DistanceJoint_GetMotorSpeed(id) : 0.0; } +LC_API void b2lc_distance_set_max_motor_force(int j, double f){ b2JointId id = joints_get(j); if (b2Joint_IsValid(id)) b2DistanceJoint_SetMaxMotorForce(id, (float)f); } +LC_API double b2lc_distance_max_motor_force(int j) { b2JointId id = joints_get(j); return b2Joint_IsValid(id) ? (double)b2DistanceJoint_GetMaxMotorForce(id) : 0.0; } +LC_API double b2lc_distance_motor_force(int j) { b2JointId id = joints_get(j); return b2Joint_IsValid(id) ? (double)b2DistanceJoint_GetMotorForce(id) : 0.0; } + +/* ---- Weld joint: reference angle + per-axis stiffness getters ------ */ +LC_API double b2lc_weld_reference_angle(int j) { b2JointId id = joints_get(j); return b2Joint_IsValid(id) ? (double)b2WeldJoint_GetReferenceAngle(id) : 0.0; } +LC_API void b2lc_weld_set_reference_angle(int j, double a){ b2JointId id = joints_get(j); if (b2Joint_IsValid(id)) b2WeldJoint_SetReferenceAngle(id, (float)a); } +LC_API double b2lc_weld_linear_hertz(int j) { b2JointId id = joints_get(j); return b2Joint_IsValid(id) ? (double)b2WeldJoint_GetLinearHertz(id) : 0.0; } +LC_API double b2lc_weld_linear_damping(int j) { b2JointId id = joints_get(j); return b2Joint_IsValid(id) ? (double)b2WeldJoint_GetLinearDampingRatio(id) : 0.0; } +LC_API double b2lc_weld_angular_hertz(int j) { b2JointId id = joints_get(j); return b2Joint_IsValid(id) ? (double)b2WeldJoint_GetAngularHertz(id) : 0.0; } +LC_API double b2lc_weld_angular_damping(int j) { b2JointId id = joints_get(j); return b2Joint_IsValid(id) ? (double)b2WeldJoint_GetAngularDampingRatio(id) : 0.0; } + +/* ---- Wheel joint: granular spring/limit/motor get+set -------------- */ +LC_API int b2lc_wheel_is_spring_enabled(int j) { b2JointId id = joints_get(j); return (b2Joint_IsValid(id) && b2WheelJoint_IsSpringEnabled(id)) ? 1 : 0; } +LC_API double b2lc_wheel_spring_hertz(int j) { b2JointId id = joints_get(j); return b2Joint_IsValid(id) ? (double)b2WheelJoint_GetSpringHertz(id) : 0.0; } +LC_API double b2lc_wheel_spring_damping(int j) { b2JointId id = joints_get(j); return b2Joint_IsValid(id) ? (double)b2WheelJoint_GetSpringDampingRatio(id) : 0.0; } +LC_API void b2lc_wheel_enable_limit(int j, int f) { b2JointId id = joints_get(j); if (b2Joint_IsValid(id)) b2WheelJoint_EnableLimit(id, f ? true : false); } +LC_API int b2lc_wheel_is_limit_enabled(int j) { b2JointId id = joints_get(j); return (b2Joint_IsValid(id) && b2WheelJoint_IsLimitEnabled(id)) ? 1 : 0; } +LC_API void b2lc_wheel_set_limits(int j, double lo, double hi){ b2JointId id = joints_get(j); if (b2Joint_IsValid(id)) b2WheelJoint_SetLimits(id, (float)lo, (float)hi); } +LC_API double b2lc_wheel_lower_limit(int j) { b2JointId id = joints_get(j); return b2Joint_IsValid(id) ? (double)b2WheelJoint_GetLowerLimit(id) : 0.0; } +LC_API double b2lc_wheel_upper_limit(int j) { b2JointId id = joints_get(j); return b2Joint_IsValid(id) ? (double)b2WheelJoint_GetUpperLimit(id) : 0.0; } +LC_API int b2lc_wheel_is_motor_enabled(int j) { b2JointId id = joints_get(j); return (b2Joint_IsValid(id) && b2WheelJoint_IsMotorEnabled(id)) ? 1 : 0; } +LC_API double b2lc_wheel_motor_speed(int j) { b2JointId id = joints_get(j); return b2Joint_IsValid(id) ? (double)b2WheelJoint_GetMotorSpeed(id) : 0.0; } +LC_API double b2lc_wheel_motor_torque(int j) { b2JointId id = joints_get(j); return b2Joint_IsValid(id) ? (double)b2WheelJoint_GetMotorTorque(id) : 0.0; } +LC_API double b2lc_wheel_max_motor_torque(int j) { b2JointId id = joints_get(j); return b2Joint_IsValid(id) ? (double)b2WheelJoint_GetMaxMotorTorque(id) : 0.0; } + +/* ---- Mouse joint: target + spring getters/setters ------------------ */ +LC_API double b2lc_mouse_target_x(int j) { b2JointId id = joints_get(j); return b2Joint_IsValid(id) ? (double)b2MouseJoint_GetTarget(id).x : 0.0; } +LC_API double b2lc_mouse_target_y(int j) { b2JointId id = joints_get(j); return b2Joint_IsValid(id) ? (double)b2MouseJoint_GetTarget(id).y : 0.0; } +LC_API void b2lc_mouse_set_spring_hertz(int j, double hz) { b2JointId id = joints_get(j); if (b2Joint_IsValid(id)) b2MouseJoint_SetSpringHertz(id, (float)hz); } +LC_API double b2lc_mouse_spring_hertz(int j) { b2JointId id = joints_get(j); return b2Joint_IsValid(id) ? (double)b2MouseJoint_GetSpringHertz(id) : 0.0; } +LC_API void b2lc_mouse_set_spring_damping(int j, double d) { b2JointId id = joints_get(j); if (b2Joint_IsValid(id)) b2MouseJoint_SetSpringDampingRatio(id, (float)d); } +LC_API double b2lc_mouse_spring_damping(int j) { b2JointId id = joints_get(j); return b2Joint_IsValid(id) ? (double)b2MouseJoint_GetSpringDampingRatio(id) : 0.0; } +LC_API void b2lc_mouse_set_max_force(int j, double f) { b2JointId id = joints_get(j); if (b2Joint_IsValid(id)) b2MouseJoint_SetMaxForce(id, (float)f); } +LC_API double b2lc_mouse_max_force(int j) { b2JointId id = joints_get(j); return b2Joint_IsValid(id) ? (double)b2MouseJoint_GetMaxForce(id) : 0.0; } + +/* ------------------------------------------------------------------ */ +/* World queries: overlap / ray-cast-all / shape-cast. */ +/* Box2D's queries are callback-based; we collect hits into one shared */ +/* growable buffer (a C callback pushes each), then expose count + */ +/* indexed getters (the same idea as the ray/contact stashes above). */ +/* ------------------------------------------------------------------ */ +typedef struct { int body, shape; double px, py, nx, ny, frac; } LcQRow; +static LcQRow *s_q = NULL; static int s_qCap = 0, s_qCnt = 0; +static void q_reset(void) { s_qCnt = 0; } +static void q_push(b2ShapeId sh, double px, double py, double nx, double ny, double f) { + if (s_qCnt >= s_qCap) { + s_qCap = s_qCap ? s_qCap * 2 : 64; + s_q = (LcQRow *)realloc(s_q, (size_t)s_qCap * sizeof(LcQRow)); + } + s_q[s_qCnt].shape = shape_h(sh); + s_q[s_qCnt].body = body_handle_of_shape(sh); + s_q[s_qCnt].px = px; s_q[s_qCnt].py = py; + s_q[s_qCnt].nx = nx; s_q[s_qCnt].ny = ny; s_q[s_qCnt].frac = f; + s_qCnt++; +} +static bool overlap_cb(b2ShapeId shapeId, void *ctx) { (void)ctx; q_push(shapeId, 0, 0, 0, 0, 0); return true; } +static b2Vec2 s_qPoint; +static bool overlap_point_cb(b2ShapeId shapeId, void *ctx) { + (void)ctx; + if (b2Shape_TestPoint(shapeId, s_qPoint)) q_push(shapeId, s_qPoint.x, s_qPoint.y, 0, 0, 0); + return true; +} +static float cast_cb(b2ShapeId shapeId, b2Vec2 point, b2Vec2 normal, float fraction, void *ctx) { + (void)ctx; q_push(shapeId, point.x, point.y, normal.x, normal.y, fraction); return 1.0f; /* gather all */ +} +static int q_cmp_frac(const void *a, const void *b) { + double fa = ((const LcQRow *)a)->frac, fb = ((const LcQRow *)b)->frac; + return (fa < fb) ? -1 : (fa > fb) ? 1 : 0; +} + +LC_API int b2lc_query_overlap_aabb(int w, double x1, double y1, double x2, double y2) { + q_reset(); + b2WorldId wid = worlds_get(w); + if (!b2World_IsValid(wid)) return 0; + b2AABB aabb; + aabb.lowerBound = v2(x1 < x2 ? x1 : x2, y1 < y2 ? y1 : y2); + aabb.upperBound = v2(x1 > x2 ? x1 : x2, y1 > y2 ? y1 : y2); + b2World_OverlapAABB(wid, aabb, b2DefaultQueryFilter(), overlap_cb, NULL); + return s_qCnt; +} +LC_API int b2lc_query_overlap_point(int w, double x, double y) { + q_reset(); + b2WorldId wid = worlds_get(w); + if (!b2World_IsValid(wid)) return 0; + s_qPoint = v2(x, y); + const float e = 0.001f; + b2AABB aabb; aabb.lowerBound = v2(x - e, y - e); aabb.upperBound = v2(x + e, y + e); + b2World_OverlapAABB(wid, aabb, b2DefaultQueryFilter(), overlap_point_cb, NULL); + return s_qCnt; +} +LC_API int b2lc_query_overlap_circle(int w, double cx, double cy, double r) { + q_reset(); + b2WorldId wid = worlds_get(w); + if (!b2World_IsValid(wid)) return 0; + b2Vec2 pt = v2(cx, cy); + b2ShapeProxy proxy = b2MakeProxy(&pt, 1, (float)r); + b2World_OverlapShape(wid, &proxy, b2DefaultQueryFilter(), overlap_cb, NULL); + return s_qCnt; +} +LC_API int b2lc_query_overlap_shape(int w, double radius) { + q_reset(); + b2WorldId wid = worlds_get(w); + if (!b2World_IsValid(wid) || s_polyCnt < 1) return 0; + b2ShapeProxy proxy = b2MakeProxy(s_poly, s_polyCnt, (float)radius); + b2World_OverlapShape(wid, &proxy, b2DefaultQueryFilter(), overlap_cb, NULL); + return s_qCnt; +} +LC_API int b2lc_query_raycast_all(int w, double x1, double y1, double x2, double y2) { + q_reset(); + b2WorldId wid = worlds_get(w); + if (!b2World_IsValid(wid)) return 0; + b2World_CastRay(wid, v2(x1, y1), v2(x2 - x1, y2 - y1), b2DefaultQueryFilter(), cast_cb, NULL); + if (s_qCnt > 1) qsort(s_q, (size_t)s_qCnt, sizeof(LcQRow), q_cmp_frac); + return s_qCnt; +} +LC_API int b2lc_query_shapecast(int w, double radius, double dx, double dy) { + q_reset(); + b2WorldId wid = worlds_get(w); + if (!b2World_IsValid(wid) || s_polyCnt < 1) return 0; + b2ShapeProxy proxy = b2MakeProxy(s_poly, s_polyCnt, (float)radius); + b2World_CastShape(wid, &proxy, v2(dx, dy), b2DefaultQueryFilter(), cast_cb, NULL); + if (s_qCnt > 1) qsort(s_q, (size_t)s_qCnt, sizeof(LcQRow), q_cmp_frac); + return s_qCnt; +} +LC_API int b2lc_query_count(void) { return s_qCnt; } +LC_API int b2lc_query_body(int i) { return (i >= 0 && i < s_qCnt) ? s_q[i].body : 0; } +LC_API int b2lc_query_shape(int i) { return (i >= 0 && i < s_qCnt) ? s_q[i].shape : 0; } +LC_API double b2lc_query_x(int i) { return (i >= 0 && i < s_qCnt) ? s_q[i].px : 0.0; } +LC_API double b2lc_query_y(int i) { return (i >= 0 && i < s_qCnt) ? s_q[i].py : 0.0; } +LC_API double b2lc_query_normal_x(int i) { return (i >= 0 && i < s_qCnt) ? s_q[i].nx : 0.0; } +LC_API double b2lc_query_normal_y(int i) { return (i >= 0 && i < s_qCnt) ? s_q[i].ny : 0.0; } +LC_API double b2lc_query_fraction(int i) { return (i >= 0 && i < s_qCnt) ? s_q[i].frac : 0.0; } diff --git a/src/box2dxt-kit.livecodescript b/src/box2dxt-kit.livecodescript index 4f0d1dc..8eb1f9d 100644 --- a/src/box2dxt-kit.livecodescript +++ b/src/box2dxt-kit.livecodescript @@ -5,7 +5,7 @@ -- PIXELS, SCREEN coordinates and DEGREES; the kit converts to Box2D's -- metres / radians / y-up for you, runs the world, and moves your controls. -- Runs in OpenXTalk (OXT); compatible with LiveCode 9.6.3+. --- Requires the box2dxt extension loaded (put b2Version() should return 2). +-- Requires the box2dxt extension loaded (put b2Version() should return 3). -- -- ===================== 60-SECOND START ============================= -- on openCard @@ -34,12 +34,15 @@ -- b2kSetGravityScale ctrl,s · b2kSetDamping ctrl,lin[,ang] · b2kWake ctrl · -- b2kSetType ctrl,name · b2kSetStatic/Dynamic/Kinematic ctrl · -- b2kEnable/Disable ctrl --- ACT b2kPush ctrl,dvx,dvy · b2kForce ctrl,fx,fy · b2kSetVelocity ctrl,vx,vy · --- b2kSpin ctrl,degPerSec · b2kSpinBy ctrl,degPerSec · b2kMoveTo ctrl,x,y[,deg] +-- ACT b2kPush ctrl,dvx,dvy · b2kImpulse ctrl,ix,iy · b2kForce ctrl,fx,fy · +-- b2kSetVelocity ctrl,vx,vy · b2kSpin ctrl,deg/s · b2kSpinBy ctrl,deg/s · +-- b2kTorque ctrl,t · b2kAngularImpulse ctrl,imp · b2kMoveTo ctrl,x,y[,deg] · -- b2kExplode x,y[,radius][,power] · b2kRemove ctrl --- GET b2kBodyOf(ctrl) · b2kVelocity(ctrl) · b2kSpeed(ctrl) · b2kAngle(ctrl) · --- b2kMass(ctrl) · b2kBodyType(ctrl) · b2kIsAwake(ctrl) · b2kControlAt(x,y) · --- b2kBodyCount() · b2kAwakeCount() · b2kToWorldX/Y(px) · b2kToScreenX/Y(m) · +-- GET b2kBodyOf(ctrl) · b2kPosition(ctrl) · b2kWorldCenter(ctrl) · b2kVelocity(ctrl) · +-- b2kSpeed(ctrl) · b2kAngle(ctrl) · b2kMass(ctrl) · b2kBodyType(ctrl) · +-- b2kGravityScale(ctrl) · b2kDamping(ctrl) · b2kIsAwake(ctrl) · b2kControlAt(x,y) · +-- b2kControlContains(ctrl,x,y) · b2kBodyCount() · b2kAwakeCount() · +-- b2kToWorldX/Y(px) · b2kToScreenX/Y(m) · -- b2kRayHit(x1,y1,x2,y2) + b2kRayHitX/Y() · b2kRayHitNormalX/Y() · b2kRayDist() -- JOINTS b2kHinge ctrlA,ctrlB,x,y · b2kWeld ctrlA,ctrlB · b2kRope ctrlA,ctrlB[,len] -- b2kSlider ctrlA,ctrlB,axisDeg · b2kWheel chassis,wheel,x,y[,axisDeg] @@ -51,6 +54,17 @@ -- DRAG b2kGrab(x,y) -> control · b2kRelease -- EVENTS on b2kContact pCtrlA,pCtrlB · on b2kEndContact pCtrlA,pCtrlB -- (long ids; empty for walls/ground) · on b2kFrame (once per frame) +-- poll instead: b2kContactCount()+b2kContactA/B(i) · b2kEndContactCount()+… +-- SENSOR b2kAddSensor ctrl[,shape] · on b2kSensorEnter pSensor,pVisitor · +-- on b2kSensorExit pSensor,pVisitor · b2kSensorCount()+EnterSensor/Visitor(i) +-- FILTER b2kDefineLayer name · b2kSetCategory ctrl,layers · b2kSetMask ctrl,layers · +-- b2kSetCollisionGroup ctrl,n · b2kNoCollide ctrlA,ctrlB +-- TERRAIN b2kChain pointList[,loop] · b2kSmoothGround pointList (>=4 screen points) +-- QUERY+ b2kOverlap x1,y1,x2,y2 · b2kOverlapCircle x,y,r · b2kRayHitAll x1,y1,x2,y2 +-- MOTOR b2kMotorTo mover,ref,dxPx,dyPx,deg[,maxF,maxT] · b2kExplode (native blast) +-- TUNE b2kSetRestitutionThreshold px/s · b2kSetContactTuning hz,damp,pushPx · +-- b2kSetJointTuning hz,damp · b2kSetMaxSpeed px/s · b2kEnableWarmStarting f · +-- b2kProfile() · b2kAwakeBodyCount() -- -- Tips: pass controls by reference (the long id of ... is safest). Keep moving -- objects a sensible on-screen size (default scale 40 px/m, so ~4-400 px). @@ -70,6 +84,9 @@ local sRad -- ctrlRef -> radius (metres) for "ball" local sImgAngle -- ctrlRef -> last applied angle (deg) for "image" local sStatic -- ctrlRef -> true for bodies that never move local sSpawned -- ctrlRef -> true if the kit created the control +local sSensor -- ctrlRef -> true for sensor (non-solid trigger) bodies +local sLayers -- collision-layer name -> bit value +local sNextBit -- next free collision-layer bit local sDragJoint, sDragAnchor, sDragging local sContactObj -- object that receives b2kContact messages local sFrameObj -- object that receives an on b2kFrame message each frame @@ -93,6 +110,8 @@ command b2kSetup pGravityX, pGravityY put (the height of this card) - 40 into sOriginY put 4 into sSub b2kResetTables + put empty into sLayers + put 1 into sNextBit put 0 into sDragJoint put false into sDragging put false into sRunning @@ -109,6 +128,7 @@ command b2kResetTables put empty into sImgAngle put empty into sStatic put empty into sSpawned + put empty into sSensor end b2kResetTables command b2kTeardown @@ -249,6 +269,12 @@ function b2kWorld return sWorld end b2kWorld +-- Sanity check from kit-only code: returns the native shim's ABI version (3), +-- i.e. that the box2dxt extension and its native library are loaded and in sync. +function b2kVersion + return b2Version() +end b2kVersion + -- ===================================================================== -- Coordinate conversion (sim Y up; screen Y down) -- ===================================================================== @@ -291,6 +317,7 @@ command b2kAddBox pControl, pDynamic if tGraphic or (tImage and pDynamic) then put false into tFixed else put true into tFixed put b2NewBody(sWorld, tType, tWx, tWy, 0, false, tFixed) into tBody + b2ShapeDefEnableSensorEvents true -- so sensors can detect this shape put b2AddBox(tBody, tHw, tHh, 1.0, 0.4, 0.2) into sShapeH[tRef] b2kRegister tRef, tBody, (not pDynamic) if tGraphic and pDynamic then @@ -316,6 +343,7 @@ command b2kAddBall pControl, pDynamic put b2kToWorldX(item 1 of the loc of pControl) into tWx put b2kToWorldY(item 2 of the loc of pControl) into tWy put b2NewBody(sWorld, tType, tWx, tWy, 0, false, false) into tBody + b2ShapeDefEnableSensorEvents true -- so sensors can detect this shape put b2AddCircle(tBody, 0, 0, tRad, 1.0, 0.4, 0.4) into sShapeH[tRef] b2kRegister tRef, tBody, (not pDynamic) if word 1 of tRef is "graphic" then put "ball" into sRender[tRef] @@ -351,6 +379,7 @@ command b2kAddCapsule pControl, pDynamic if tGraphic or (tImage and pDynamic) then put false into tFixed else put true into tFixed put b2NewBody(sWorld, tType, tWx, tWy, 0, false, tFixed) into tBody + b2ShapeDefEnableSensorEvents true -- so sensors can detect this shape if tHoriz then put b2AddCapsule(tBody, - tL, 0, tL, 0, tR, 1.0, 0.4, 0.2) into sShapeH[tRef] else @@ -397,6 +426,7 @@ command b2kAddPolygon pControl, pDynamic b2DestroyBody tBody return 0 end if + b2ShapeDefEnableSensorEvents true -- so sensors can detect this shape put b2AddPolygon(tBody, 1.0, 0.4, 0.2) into sShapeH[tRef] b2kRegister tRef, tBody, (not pDynamic) put "poly" into sRender[tRef] @@ -633,6 +663,13 @@ command b2kTorque pControl, pTorque b2ApplyTorque sBody[the long id of pControl], pTorque, true end b2kTorque +-- A one-shot twist (a sharp angular kick), mass-aware: bodies with more +-- rotational inertia turn less. b2kSpinBy sets the spin directly; this is the +-- impulse equivalent, and the angular partner of b2kImpulse. +command b2kAngularImpulse pControl, pImpulse + b2ApplyAngularImpulse sBody[the long id of pControl], pImpulse, true +end b2kAngularImpulse + -- Change a body's type at runtime: "static", "kinematic", or "dynamic". command b2kSetType pControl, pType local tRef, tCode @@ -695,6 +732,7 @@ command b2kRemove pControl delete variable sImgAngle[tRef] delete variable sStatic[tRef] delete variable sSpawned[tRef] + delete variable sSensor[tRef] -- If the kit created this control (b2kSpawn...), delete it too, so removing -- a body never leaves a dead graphic behind. Attached controls are left alone. if tWasSpawned then @@ -704,8 +742,21 @@ command b2kRemove pControl end if end b2kRemove --- A radial "blast" that kicks nearby dynamic bodies outward. +-- A radial "blast" that kicks nearby dynamic bodies outward. Uses Box2D's native +-- b2World_Explode: it's shape-perimeter aware (so a wide plank catches more blast +-- than a small ball) and affects EVERY dynamic body in range, not just kit ones. +-- pPowerPx is the impulse per unit length of facing surface; pRadiusPx the reach. command b2kExplode pScreenX, pScreenY, pRadiusPx, pPowerPx + if pRadiusPx is empty then put 180 into pRadiusPx + if pPowerPx is empty then put 900 into pPowerPx + if sWorld is empty then exit b2kExplode + b2WorldExplode sWorld, b2kToWorldX(pScreenX), b2kToWorldY(pScreenY), \ + pRadiusPx / sScale, (pRadiusPx / sScale) * 0.5, pPowerPx / sScale +end b2kExplode + +-- The original velocity-based blast (pre-v3): kicks only kit-tracked dynamic +-- bodies, ignoring their size. Kept for callers that relied on the old feel. +command b2kExplodeLegacy pScreenX, pScreenY, pRadiusPx, pPowerPx local cx, cy, tRadM, tRef, b, dx, dy, d, f if pRadiusPx is empty then put 180 into pRadiusPx if pPowerPx is empty then put 900 into pPowerPx @@ -724,7 +775,7 @@ command b2kExplode pScreenX, pScreenY, pRadiusPx, pPowerPx b2SetAwake b, true end if end repeat -end b2kExplode +end b2kExplodeLegacy -- ===================================================================== -- Getters @@ -733,6 +784,23 @@ function b2kBodyOf pControl return sBody[the long id of pControl] end b2kBodyOf +-- A body's position in SCREEN pixels as "x,y" (the position partner of +-- b2kVelocity). Controls already follow their body, but this reads the exact, +-- sub-pixel centre — handy for aiming, distance checks, or mixing with b2… +function b2kPosition pControl + local b + put sBody[the long id of pControl] into b + return b2kToScreenX(b2BodyX(b)) & "," & b2kToScreenY(b2BodyY(b)) +end b2kPosition + +-- A body's centre of mass in SCREEN pixels as "x,y". Differs from b2kPosition +-- for off-centre or compound shapes; it's the true pivot for spin and torque. +function b2kWorldCenter pControl + local b + put sBody[the long id of pControl] into b + return b2kToScreenX(b2BodyWorldCenterX(b)) & "," & b2kToScreenY(b2BodyWorldCenterY(b)) +end b2kWorldCenter + function b2kVelocity pControl local b put sBody[the long id of pControl] into b @@ -770,6 +838,18 @@ function b2kMass pControl return b2BodyMass(sBody[the long id of pControl]) end b2kMass +-- The body's per-body gravity multiplier (the getter for b2kSetGravityScale). +function b2kGravityScale pControl + return b2BodyGravityScale(sBody[the long id of pControl]) +end b2kGravityScale + +-- The body's drag as "linear,angular" (the getter for b2kSetDamping). +function b2kDamping pControl + local b + put sBody[the long id of pControl] into b + return b2BodyLinearDamping(b) & "," & b2BodyAngularDamping(b) +end b2kDamping + -- Body type as a word: "static", "kinematic", or "dynamic" (mirrors b2kSetType). function b2kBodyType pControl local tCode @@ -799,6 +879,16 @@ function b2kControlAt pScreenX, pScreenY return sCtrl[b2BodyAtPoint(sWorld, b2kToWorldX(pScreenX), b2kToWorldY(pScreenY))] end b2kControlAt +-- True if SCREEN point (x,y) is inside this control's actual collision shape — +-- rotation- and shape-aware, unlike a bounding-box test. b2kControlAt searches +-- every body; this tests one control you already have (e.g. a precise click hit). +function b2kControlContains pControl, pScreenX, pScreenY + local tRef + put the long id of pControl into tRef + if sShapeH[tRef] is empty then return false + return b2ShapeTestPoint(sShapeH[tRef], b2kToWorldX(pScreenX), b2kToWorldY(pScreenY)) +end b2kControlContains + -- Cast a ray between two screen points; returns the control hit (or empty). -- Then b2kRayHitX() / b2kRayHitY() give the hit point in screen pixels. function b2kRayHit pX1, pY1, pX2, pY2 @@ -843,6 +933,37 @@ function b2kRayDist return sRayDist end b2kRayDist +-- ---- contacts, polling style ---------------------------------------- +-- The kit also SENDS `on b2kContact` / `on b2kEndContact` to your +-- b2kContactTarget. These functions instead let you POLL the same per-frame +-- snapshot (e.g. from `on b2kFrame`) without registering a target. The snapshot +-- refreshes once per simulated step; indices are 1-based. Each accessor returns +-- the touching control's long id (empty for a wall, the ground, or any body the +-- kit isn't tracking). +function b2kContactCount + return b2ContactBeginCount() +end b2kContactCount + +function b2kContactA pIndex + return sCtrl[b2ContactBeginBodyA(pIndex)] +end b2kContactA + +function b2kContactB pIndex + return sCtrl[b2ContactBeginBodyB(pIndex)] +end b2kContactB + +function b2kEndContactCount + return b2ContactEndCount() +end b2kEndContactCount + +function b2kEndContactA pIndex + return sCtrl[b2ContactEndBodyA(pIndex)] +end b2kEndContactA + +function b2kEndContactB pIndex + return sCtrl[b2ContactEndBodyB(pIndex)] +end b2kEndContactB + -- ===================================================================== -- Joints (return a joint handle; pass it to b2kMotor / b2kRemoveJoint) -- ===================================================================== @@ -1053,6 +1174,219 @@ function b2kLocalAnchor pBody, pWx, pWy return (dx * c - dy * s) & "," & (dx * s + dy * c) end b2kLocalAnchor +-- ===================================================================== +-- Sensors (non-solid trigger zones) +-- ===================================================================== +-- Attach a SENSOR fixture to a control: it reports overlaps but never blocks. +-- Static by default (a fixed trigger zone); pShape is "box" (default), "ball" or +-- "capsule". While the loop runs, overlaps are delivered to your b2kContactTarget +-- as `on b2kSensorEnter pSensorCtrl, pVisitorCtrl` / `on b2kSensorExit ...`. The +-- kit enables sensor events on every body it creates, so sensors detect them. +command b2kAddSensor pControl, pShape + local tRef + put the long id of pControl into tRef + b2ShapeDefSensor true + b2ShapeDefEnableSensorEvents true + switch pShape + case "ball" + b2kAddBall pControl, false + break + case "capsule" + b2kAddCapsule pControl, false + break + default + b2kAddBox pControl, false + end switch + put true into sSensor[tRef] + return sBody[tRef] +end b2kAddSensor + +-- Polling alternative to the on b2kSensorEnter/Exit messages (read in on b2kFrame). +-- Counts/indices are 1-based; each returns a control (empty for untracked bodies). +function b2kSensorCount + return b2SensorBeginCount() +end b2kSensorCount +function b2kSensorEnterSensor pIndex + return sCtrl[b2ShapeBody(b2SensorBeginSensorShape(pIndex))] +end b2kSensorEnterSensor +function b2kSensorEnterVisitor pIndex + return sCtrl[b2ShapeBody(b2SensorBeginVisitorShape(pIndex))] +end b2kSensorEnterVisitor +function b2kSensorExitCount + return b2SensorEndCount() +end b2kSensorExitCount +function b2kSensorExitSensor pIndex + return sCtrl[b2ShapeBody(b2SensorEndSensorShape(pIndex))] +end b2kSensorExitSensor +function b2kSensorExitVisitor pIndex + return sCtrl[b2ShapeBody(b2SensorEndVisitorShape(pIndex))] +end b2kSensorExitVisitor + +-- ===================================================================== +-- Collision filtering (named layers; up to 32) +-- ===================================================================== +-- Define (or fetch) a named collision layer, returning its bit value. Then +-- b2kSetCategory / b2kSetMask take a comma- or space-separated list of layer +-- names (or raw numbers). Two shapes collide only if EACH one's category appears +-- in the other's mask (and no shared negative group forbids it). +command b2kDefineLayer pName + if sLayers[pName] is not empty then return sLayers[pName] + if sNextBit is empty or sNextBit < 1 then put 1 into sNextBit + put sNextBit into sLayers[pName] + put sNextBit * 2 into sNextBit + return sLayers[pName] +end b2kDefineLayer + +function b2kLayerBits pLayers + local tBits, tL + put 0 into tBits + replace comma with space in pLayers + repeat for each word tL in pLayers + if tL is empty then next repeat + if tL is a number then put tBits bitOr (tL) into tBits + else put tBits bitOr b2kDefineLayer(tL) into tBits + end repeat + return tBits +end b2kLayerBits + +command b2kSetCategory pControl, pLayers + local s + put sShapeH[the long id of pControl] into s + if s is empty then exit b2kSetCategory + b2SetShapeFilter s, b2kLayerBits(pLayers), b2ShapeFilterMask(s), b2ShapeFilterGroup(s) +end b2kSetCategory + +command b2kSetMask pControl, pLayers + local s + put sShapeH[the long id of pControl] into s + if s is empty then exit b2kSetMask + b2SetShapeFilter s, b2ShapeFilterCategory(s), b2kLayerBits(pLayers), b2ShapeFilterGroup(s) +end b2kSetMask + +-- Group index: bodies sharing a NEGATIVE group never collide; a POSITIVE group +-- always collide. Overrides category/mask. 0 (default) = use category/mask. +command b2kSetCollisionGroup pControl, pGroup + local s + put sShapeH[the long id of pControl] into s + if s is empty then exit b2kSetCollisionGroup + b2SetShapeFilter s, b2ShapeFilterCategory(s), b2ShapeFilterMask(s), pGroup +end b2kSetCollisionGroup + +-- ===================================================================== +-- Chains (smooth terrain) and world queries +-- ===================================================================== +-- A smooth static chain (ground/terrain) from a list of SCREEN points ("x,y" per +-- line). Unlike joined box/segment edges it has no inner corners for fast bodies +-- to catch on. Needs >= 4 points; pLoop true closes it. Invisible (draw a matching +-- graphic). Returns a chain handle (b2DestroyChain to remove). +command b2kChain pPoints, pLoop + local tBody, p + if sWorld is empty then return 0 + put b2NewStaticBody(sWorld, 0, 0) into tBody + b2ChainBegin + repeat for each line p in pPoints + if p is empty then next repeat + b2ChainAddPoint b2kToWorldX(item 1 of p), b2kToWorldY(item 2 of p) + end repeat + return b2CreateChain(tBody, (pLoop is true), 0.6, 0.1) +end b2kChain + +command b2kSmoothGround pPoints + return b2kChain(pPoints, false) +end b2kSmoothGround + +-- Controls whose body overlaps a screen RECT (newline-separated long ids). +function b2kOverlap pX1, pY1, pX2, pY2 + if sWorld is empty then return empty + return b2kQueryToControls(b2OverlapAABB(sWorld, b2kToWorldX(pX1), b2kToWorldY(pY1), b2kToWorldX(pX2), b2kToWorldY(pY2))) +end b2kOverlap + +-- Controls whose body overlaps a screen circle. +function b2kOverlapCircle pX, pY, pRadiusPx + if sWorld is empty then return empty + return b2kQueryToControls(b2OverlapCircle(sWorld, b2kToWorldX(pX), b2kToWorldY(pY), pRadiusPx / sScale)) +end b2kOverlapCircle + +-- Every control a ray crosses, nearest-first (vs b2kRayHit's single closest). +function b2kRayHitAll pX1, pY1, pX2, pY2 + if sWorld is empty then return empty + return b2kQueryToControls(b2RayCastAll(sWorld, b2kToWorldX(pX1), b2kToWorldY(pY1), b2kToWorldX(pX2), b2kToWorldY(pY2))) +end b2kRayHitAll + +-- Shared: turn the current query result (count) into a newline list of controls. +function b2kQueryToControls pCount + local i, c, tList + put empty into tList + repeat with i = 1 to pCount + put sCtrl[b2QueryBody(i)] into c + if c is not empty then put c & cr after tList + end repeat + return tList +end b2kQueryToControls + +-- ===================================================================== +-- Motor & filter joints +-- ===================================================================== +-- Drive pMover toward a target pose relative to pRef (a moving anchor; empty = +-- the world). Offsets are in screen pixels / degrees from pRef's centre. Great for +-- followers, lifts and character motion. Returns a joint handle (b2kRemoveJoint). +command b2kMotorTo pMover, pRef, pOffsetXPx, pOffsetYPx, pAngleDeg, pMaxForce, pMaxTorque + local bMover, bRef + put sBody[the long id of pMover] into bMover + if pRef is empty then put sDragAnchor into bRef + else put sBody[the long id of pRef] into bRef + if pAngleDeg is empty then put 0 into pAngleDeg + if pMaxForce is empty then put 1000 into pMaxForce + if pMaxTorque is empty then put 1000 into pMaxTorque + return b2MotorJoint(sWorld, bRef, bMover, pOffsetXPx / sScale, - pOffsetYPx / sScale, \ + pAngleDeg * kPI / 180, pMaxForce, pMaxTorque, 0.3, false) +end b2kMotorTo + +-- Stop two specific controls from colliding with each other (without changing how +-- either collides with anything else). Returns a joint handle (b2kRemoveJoint). +command b2kNoCollide pCtrlA, pCtrlB + return b2FilterJoint(sWorld, sBody[the long id of pCtrlA], sBody[the long id of pCtrlB]) +end b2kNoCollide + +-- ===================================================================== +-- World tuning (advanced) + perf readout +-- ===================================================================== +-- Below this approach speed (px/sec) collisions stop bouncing, even if bouncy. +command b2kSetRestitutionThreshold pPxPerSec + if sWorld is not empty then b2SetRestitutionThreshold sWorld, pPxPerSec / sScale +end b2kSetRestitutionThreshold + +-- Contact softness: stiffness (hertz), damping ratio, max separation-fix speed (px/sec). +command b2kSetContactTuning pHertz, pDamping, pPushPx + if sWorld is not empty then b2SetContactTuning sWorld, pHertz, pDamping, pPushPx / sScale +end b2kSetContactTuning + +command b2kSetJointTuning pHertz, pDamping + if sWorld is not empty then b2SetJointTuning sWorld, pHertz, pDamping +end b2kSetJointTuning + +-- Clamp how fast any body can move (px/sec) — stops runaway speeds. +command b2kSetMaxSpeed pPxPerSec + if sWorld is not empty then b2SetMaximumLinearSpeed sWorld, pPxPerSec / sScale +end b2kSetMaxSpeed + +command b2kEnableWarmStarting pFlag + if sWorld is not empty then b2EnableWarmStarting sWorld, (pFlag is true) +end b2kEnableWarmStarting + +-- A quick perf readout in milliseconds: "totalStep,collide,solve" for the last step. +function b2kProfile + if sWorld is empty then return empty + b2WorldProfileUpdate sWorld + return b2WorldProfileStep() & "," & b2WorldProfileCollide() & "," & b2WorldProfileSolve() +end b2kProfile + +-- How many dynamic bodies are awake right now (native count, vs b2kAwakeCount). +function b2kAwakeBodyCount + if sWorld is empty then return 0 + return b2AwakeBodyCount(sWorld) +end b2kAwakeBodyCount + -- ===================================================================== -- Dragging: call b2kGrab from mouseDown and b2kRelease from mouseUp. -- ===================================================================== @@ -1100,6 +1434,7 @@ on b2kStep pGen lock screen b2kSync b2kDispatchContacts + b2kDispatchSensors if sFrameObj is not empty then dispatch "b2kFrame" to sFrameObj unlock screen -- one redraw per frame: sync + events + app drawing end if @@ -1116,6 +1451,7 @@ command b2kStepOnce lock screen b2kSync b2kDispatchContacts + b2kDispatchSensors if sFrameObj is not empty then dispatch "b2kFrame" to sFrameObj unlock screen end b2kStepOnce @@ -1244,3 +1580,22 @@ command b2kDispatchContacts dispatch "b2kEndContact" to sContactObj with tA, tB end repeat end b2kDispatchContacts + +-- Snapshot sensor begin/end overlaps each frame and (if a target is set) deliver +-- them as on b2kSensorEnter / on b2kSensorExit. Sensor and visitor shapes are +-- mapped back to their controls via the shape's body. +command b2kDispatchSensors + local tN, i, tS, tV + put b2SensorsUpdate(sWorld) into tN + if sContactObj is empty then exit b2kDispatchSensors + repeat with i = 1 to tN + put sCtrl[b2ShapeBody(b2SensorBeginSensorShape(i))] into tS + put sCtrl[b2ShapeBody(b2SensorBeginVisitorShape(i))] into tV + dispatch "b2kSensorEnter" to sContactObj with tS, tV + end repeat + repeat with i = 1 to b2SensorEndCount() + put sCtrl[b2ShapeBody(b2SensorEndSensorShape(i))] into tS + put sCtrl[b2ShapeBody(b2SensorEndVisitorShape(i))] into tV + dispatch "b2kSensorExit" to sContactObj with tS, tV + end repeat +end b2kDispatchSensors diff --git a/src/box2dxt.lcb b/src/box2dxt.lcb index 2ef780f..4a640d9 100644 --- a/src/box2dxt.lcb +++ b/src/box2dxt.lcb @@ -128,6 +128,317 @@ private foreign handler _contact_begin_b(in pI as CInt) returns CInt binds to "c private foreign handler _contact_end_a(in pI as CInt) returns CInt binds to "c:box2dxt>b2lc_contact_end_a!cdecl" private foreign handler _contact_end_b(in pI as CInt) returns CInt binds to "c:box2dxt>b2lc_contact_end_b!cdecl" +-- ===== ABI v3 additions: foreign bindings =========================== +-- shape-def builder (one-shot extras applied by the next shape creation) +private foreign handler _shapedef_reset() returns nothing binds to "c:box2dxt>b2lc_shapedef_reset!cdecl" +private foreign handler _shapedef_set_sensor(in pF as CInt) returns nothing binds to "c:box2dxt>b2lc_shapedef_set_sensor!cdecl" +private foreign handler _shapedef_set_enable_contact_events(in pF as CInt) returns nothing binds to "c:box2dxt>b2lc_shapedef_set_enable_contact_events!cdecl" +private foreign handler _shapedef_set_enable_sensor_events(in pF as CInt) returns nothing binds to "c:box2dxt>b2lc_shapedef_set_enable_sensor_events!cdecl" +private foreign handler _shapedef_set_enable_hit_events(in pF as CInt) returns nothing binds to "c:box2dxt>b2lc_shapedef_set_enable_hit_events!cdecl" +private foreign handler _shapedef_set_enable_presolve_events(in pF as CInt) returns nothing binds to "c:box2dxt>b2lc_shapedef_set_enable_presolve_events!cdecl" +private foreign handler _shapedef_set_filter(in pCat as CDouble, in pMask as CDouble, in pGroup as CInt) returns nothing binds to "c:box2dxt>b2lc_shapedef_set_filter!cdecl" +private foreign handler _shapedef_set_material_id(in pId as CInt) returns nothing binds to "c:box2dxt>b2lc_shapedef_set_material_id!cdecl" + +-- contact hit events +private foreign handler _contact_hit_count() returns CInt binds to "c:box2dxt>b2lc_contact_hit_count!cdecl" +private foreign handler _contact_hit_a(in pI as CInt) returns CInt binds to "c:box2dxt>b2lc_contact_hit_a!cdecl" +private foreign handler _contact_hit_b(in pI as CInt) returns CInt binds to "c:box2dxt>b2lc_contact_hit_b!cdecl" +private foreign handler _contact_hit_x(in pI as CInt) returns CDouble binds to "c:box2dxt>b2lc_contact_hit_x!cdecl" +private foreign handler _contact_hit_y(in pI as CInt) returns CDouble binds to "c:box2dxt>b2lc_contact_hit_y!cdecl" +private foreign handler _contact_hit_nx(in pI as CInt) returns CDouble binds to "c:box2dxt>b2lc_contact_hit_nx!cdecl" +private foreign handler _contact_hit_ny(in pI as CInt) returns CDouble binds to "c:box2dxt>b2lc_contact_hit_ny!cdecl" +private foreign handler _contact_hit_speed(in pI as CInt) returns CDouble binds to "c:box2dxt>b2lc_contact_hit_speed!cdecl" + +-- sensor events +private foreign handler _sensors_update(in pW as CInt) returns CInt binds to "c:box2dxt>b2lc_sensors_update!cdecl" +private foreign handler _sensor_begin_count() returns CInt binds to "c:box2dxt>b2lc_sensor_begin_count!cdecl" +private foreign handler _sensor_end_count() returns CInt binds to "c:box2dxt>b2lc_sensor_end_count!cdecl" +private foreign handler _sensor_begin_sensor(in pI as CInt) returns CInt binds to "c:box2dxt>b2lc_sensor_begin_sensor!cdecl" +private foreign handler _sensor_begin_visitor(in pI as CInt) returns CInt binds to "c:box2dxt>b2lc_sensor_begin_visitor!cdecl" +private foreign handler _sensor_end_sensor(in pI as CInt) returns CInt binds to "c:box2dxt>b2lc_sensor_end_sensor!cdecl" +private foreign handler _sensor_end_visitor(in pI as CInt) returns CInt binds to "c:box2dxt>b2lc_sensor_end_visitor!cdecl" + +-- body move events +private foreign handler _bodies_update(in pW as CInt) returns CInt binds to "c:box2dxt>b2lc_bodies_update!cdecl" +private foreign handler _body_move_count() returns CInt binds to "c:box2dxt>b2lc_body_move_count!cdecl" +private foreign handler _body_move_body(in pI as CInt) returns CInt binds to "c:box2dxt>b2lc_body_move_body!cdecl" +private foreign handler _body_move_x(in pI as CInt) returns CDouble binds to "c:box2dxt>b2lc_body_move_x!cdecl" +private foreign handler _body_move_y(in pI as CInt) returns CDouble binds to "c:box2dxt>b2lc_body_move_y!cdecl" +private foreign handler _body_move_angle(in pI as CInt) returns CDouble binds to "c:box2dxt>b2lc_body_move_angle!cdecl" +private foreign handler _body_move_asleep(in pI as CInt) returns CInt binds to "c:box2dxt>b2lc_body_move_asleep!cdecl" + +-- world tuning / info / explode / profile / counters +private foreign handler _world_gravity_x(in pW as CInt) returns CDouble binds to "c:box2dxt>b2lc_world_gravity_x!cdecl" +private foreign handler _world_gravity_y(in pW as CInt) returns CDouble binds to "c:box2dxt>b2lc_world_gravity_y!cdecl" +private foreign handler _world_set_restitution_threshold(in pW as CInt, in pV as CDouble) returns nothing binds to "c:box2dxt>b2lc_world_set_restitution_threshold!cdecl" +private foreign handler _world_restitution_threshold(in pW as CInt) returns CDouble binds to "c:box2dxt>b2lc_world_restitution_threshold!cdecl" +private foreign handler _world_set_hit_event_threshold(in pW as CInt, in pV as CDouble) returns nothing binds to "c:box2dxt>b2lc_world_set_hit_event_threshold!cdecl" +private foreign handler _world_hit_event_threshold(in pW as CInt) returns CDouble binds to "c:box2dxt>b2lc_world_hit_event_threshold!cdecl" +private foreign handler _world_set_contact_tuning(in pW as CInt, in pHz as CDouble, in pDamp as CDouble, in pPush as CDouble) returns nothing binds to "c:box2dxt>b2lc_world_set_contact_tuning!cdecl" +private foreign handler _world_set_joint_tuning(in pW as CInt, in pHz as CDouble, in pDamp as CDouble) returns nothing binds to "c:box2dxt>b2lc_world_set_joint_tuning!cdecl" +private foreign handler _world_set_maximum_linear_speed(in pW as CInt, in pV as CDouble) returns nothing binds to "c:box2dxt>b2lc_world_set_maximum_linear_speed!cdecl" +private foreign handler _world_maximum_linear_speed(in pW as CInt) returns CDouble binds to "c:box2dxt>b2lc_world_maximum_linear_speed!cdecl" +private foreign handler _world_enable_warm_starting(in pW as CInt, in pF as CInt) returns nothing binds to "c:box2dxt>b2lc_world_enable_warm_starting!cdecl" +private foreign handler _world_is_warm_starting(in pW as CInt) returns CInt binds to "c:box2dxt>b2lc_world_is_warm_starting!cdecl" +private foreign handler _world_enable_speculative(in pW as CInt, in pF as CInt) returns nothing binds to "c:box2dxt>b2lc_world_enable_speculative!cdecl" +private foreign handler _world_is_sleeping_enabled(in pW as CInt) returns CInt binds to "c:box2dxt>b2lc_world_is_sleeping_enabled!cdecl" +private foreign handler _world_is_continuous_enabled(in pW as CInt) returns CInt binds to "c:box2dxt>b2lc_world_is_continuous_enabled!cdecl" +private foreign handler _world_awake_body_count(in pW as CInt) returns CInt binds to "c:box2dxt>b2lc_world_awake_body_count!cdecl" +private foreign handler _world_explode(in pW as CInt, in pX as CDouble, in pY as CDouble, in pR as CDouble, in pFalloff as CDouble, in pImpulse as CDouble) returns nothing binds to "c:box2dxt>b2lc_world_explode!cdecl" +private foreign handler _world_profile_update(in pW as CInt) returns nothing binds to "c:box2dxt>b2lc_world_profile_update!cdecl" +private foreign handler _world_profile_step() returns CDouble binds to "c:box2dxt>b2lc_world_profile_step!cdecl" +private foreign handler _world_profile_pairs() returns CDouble binds to "c:box2dxt>b2lc_world_profile_pairs!cdecl" +private foreign handler _world_profile_collide() returns CDouble binds to "c:box2dxt>b2lc_world_profile_collide!cdecl" +private foreign handler _world_profile_solve() returns CDouble binds to "c:box2dxt>b2lc_world_profile_solve!cdecl" +private foreign handler _world_profile_refit() returns CDouble binds to "c:box2dxt>b2lc_world_profile_refit!cdecl" +private foreign handler _world_profile_sensors() returns CDouble binds to "c:box2dxt>b2lc_world_profile_sensors!cdecl" +private foreign handler _world_counters_update(in pW as CInt) returns nothing binds to "c:box2dxt>b2lc_world_counters_update!cdecl" +private foreign handler _world_count_bodies() returns CInt binds to "c:box2dxt>b2lc_world_count_bodies!cdecl" +private foreign handler _world_count_shapes() returns CInt binds to "c:box2dxt>b2lc_world_count_shapes!cdecl" +private foreign handler _world_count_contacts() returns CInt binds to "c:box2dxt>b2lc_world_count_contacts!cdecl" +private foreign handler _world_count_joints() returns CInt binds to "c:box2dxt>b2lc_world_count_joints!cdecl" +private foreign handler _world_count_islands() returns CInt binds to "c:box2dxt>b2lc_world_count_islands!cdecl" + +-- body: transforms / velocity-at-point / force-at-point / mass / enum +private foreign handler _body_world_point_x(in pB as CInt, in pLx as CDouble, in pLy as CDouble) returns CDouble binds to "c:box2dxt>b2lc_body_world_point_x!cdecl" +private foreign handler _body_world_point_y(in pB as CInt, in pLx as CDouble, in pLy as CDouble) returns CDouble binds to "c:box2dxt>b2lc_body_world_point_y!cdecl" +private foreign handler _body_local_point_x(in pB as CInt, in pWx as CDouble, in pWy as CDouble) returns CDouble binds to "c:box2dxt>b2lc_body_local_point_x!cdecl" +private foreign handler _body_local_point_y(in pB as CInt, in pWx as CDouble, in pWy as CDouble) returns CDouble binds to "c:box2dxt>b2lc_body_local_point_y!cdecl" +private foreign handler _body_world_vector_x(in pB as CInt, in pLx as CDouble, in pLy as CDouble) returns CDouble binds to "c:box2dxt>b2lc_body_world_vector_x!cdecl" +private foreign handler _body_world_vector_y(in pB as CInt, in pLx as CDouble, in pLy as CDouble) returns CDouble binds to "c:box2dxt>b2lc_body_world_vector_y!cdecl" +private foreign handler _body_local_vector_x(in pB as CInt, in pWx as CDouble, in pWy as CDouble) returns CDouble binds to "c:box2dxt>b2lc_body_local_vector_x!cdecl" +private foreign handler _body_local_vector_y(in pB as CInt, in pWx as CDouble, in pWy as CDouble) returns CDouble binds to "c:box2dxt>b2lc_body_local_vector_y!cdecl" +private foreign handler _body_world_point_velocity_x(in pB as CInt, in pWx as CDouble, in pWy as CDouble) returns CDouble binds to "c:box2dxt>b2lc_body_world_point_velocity_x!cdecl" +private foreign handler _body_world_point_velocity_y(in pB as CInt, in pWx as CDouble, in pWy as CDouble) returns CDouble binds to "c:box2dxt>b2lc_body_world_point_velocity_y!cdecl" +private foreign handler _body_local_point_velocity_x(in pB as CInt, in pLx as CDouble, in pLy as CDouble) returns CDouble binds to "c:box2dxt>b2lc_body_local_point_velocity_x!cdecl" +private foreign handler _body_local_point_velocity_y(in pB as CInt, in pLx as CDouble, in pLy as CDouble) returns CDouble binds to "c:box2dxt>b2lc_body_local_point_velocity_y!cdecl" +private foreign handler _body_apply_force_at(in pB as CInt, in pFx as CDouble, in pFy as CDouble, in pPx as CDouble, in pPy as CDouble, in pWake as CInt) returns nothing binds to "c:box2dxt>b2lc_body_apply_force_at!cdecl" +private foreign handler _body_apply_impulse_at(in pB as CInt, in pIx as CDouble, in pIy as CDouble, in pPx as CDouble, in pPy as CDouble, in pWake as CInt) returns nothing binds to "c:box2dxt>b2lc_body_apply_impulse_at!cdecl" +private foreign handler _body_rotational_inertia(in pB as CInt) returns CDouble binds to "c:box2dxt>b2lc_body_rotational_inertia!cdecl" +private foreign handler _body_local_center_x(in pB as CInt) returns CDouble binds to "c:box2dxt>b2lc_body_local_center_x!cdecl" +private foreign handler _body_local_center_y(in pB as CInt) returns CDouble binds to "c:box2dxt>b2lc_body_local_center_y!cdecl" +private foreign handler _body_mass_data_update(in pB as CInt) returns nothing binds to "c:box2dxt>b2lc_body_mass_data_update!cdecl" +private foreign handler _md_mass() returns CDouble binds to "c:box2dxt>b2lc_md_mass!cdecl" +private foreign handler _md_center_x() returns CDouble binds to "c:box2dxt>b2lc_md_center_x!cdecl" +private foreign handler _md_center_y() returns CDouble binds to "c:box2dxt>b2lc_md_center_y!cdecl" +private foreign handler _md_inertia() returns CDouble binds to "c:box2dxt>b2lc_md_inertia!cdecl" +private foreign handler _body_set_mass_data(in pB as CInt, in pMass as CDouble, in pCx as CDouble, in pCy as CDouble, in pInertia as CDouble) returns nothing binds to "c:box2dxt>b2lc_body_set_mass_data!cdecl" +private foreign handler _body_apply_mass_from_shapes(in pB as CInt) returns nothing binds to "c:box2dxt>b2lc_body_apply_mass_from_shapes!cdecl" +private foreign handler _body_set_target_transform(in pB as CInt, in pX as CDouble, in pY as CDouble, in pAngle as CDouble, in pDt as CDouble) returns nothing binds to "c:box2dxt>b2lc_body_set_target_transform!cdecl" +private foreign handler _body_enable_sleep(in pB as CInt, in pF as CInt) returns nothing binds to "c:box2dxt>b2lc_body_enable_sleep!cdecl" +private foreign handler _body_is_sleep_enabled(in pB as CInt) returns CInt binds to "c:box2dxt>b2lc_body_is_sleep_enabled!cdecl" +private foreign handler _body_is_fixed_rotation(in pB as CInt) returns CInt binds to "c:box2dxt>b2lc_body_is_fixed_rotation!cdecl" +private foreign handler _body_enable_contact_events(in pB as CInt, in pF as CInt) returns nothing binds to "c:box2dxt>b2lc_body_enable_contact_events!cdecl" +private foreign handler _body_enable_hit_events(in pB as CInt, in pF as CInt) returns nothing binds to "c:box2dxt>b2lc_body_enable_hit_events!cdecl" +private foreign handler _body_aabb_update(in pB as CInt) returns nothing binds to "c:box2dxt>b2lc_body_aabb_update!cdecl" +private foreign handler _aabb_lower_x() returns CDouble binds to "c:box2dxt>b2lc_aabb_lower_x!cdecl" +private foreign handler _aabb_lower_y() returns CDouble binds to "c:box2dxt>b2lc_aabb_lower_y!cdecl" +private foreign handler _aabb_upper_x() returns CDouble binds to "c:box2dxt>b2lc_aabb_upper_x!cdecl" +private foreign handler _aabb_upper_y() returns CDouble binds to "c:box2dxt>b2lc_aabb_upper_y!cdecl" +private foreign handler _body_shape_count(in pB as CInt) returns CInt binds to "c:box2dxt>b2lc_body_shape_count!cdecl" +private foreign handler _body_shape_at(in pI as CInt) returns CInt binds to "c:box2dxt>b2lc_body_shape_at!cdecl" +private foreign handler _body_joint_count(in pB as CInt) returns CInt binds to "c:box2dxt>b2lc_body_joint_count!cdecl" +private foreign handler _body_joint_at(in pI as CInt) returns CInt binds to "c:box2dxt>b2lc_body_joint_at!cdecl" + +-- shape: type / material / filter / event flags / geometry / queries +private foreign handler _shape_type(in pS as CInt) returns CInt binds to "c:box2dxt>b2lc_shape_type!cdecl" +private foreign handler _shape_is_sensor(in pS as CInt) returns CInt binds to "c:box2dxt>b2lc_shape_is_sensor!cdecl" +private foreign handler _shape_density(in pS as CInt) returns CDouble binds to "c:box2dxt>b2lc_shape_density!cdecl" +private foreign handler _shape_friction(in pS as CInt) returns CDouble binds to "c:box2dxt>b2lc_shape_friction!cdecl" +private foreign handler _shape_restitution(in pS as CInt) returns CDouble binds to "c:box2dxt>b2lc_shape_restitution!cdecl" +private foreign handler _shape_material_id(in pS as CInt) returns CInt binds to "c:box2dxt>b2lc_shape_material_id!cdecl" +private foreign handler _shape_set_material_id(in pS as CInt, in pM as CInt) returns nothing binds to "c:box2dxt>b2lc_shape_set_material_id!cdecl" +private foreign handler _shape_set_filter(in pS as CInt, in pCat as CDouble, in pMask as CDouble, in pGroup as CInt) returns nothing binds to "c:box2dxt>b2lc_shape_set_filter!cdecl" +private foreign handler _shape_filter_category(in pS as CInt) returns CDouble binds to "c:box2dxt>b2lc_shape_filter_category!cdecl" +private foreign handler _shape_filter_mask(in pS as CInt) returns CDouble binds to "c:box2dxt>b2lc_shape_filter_mask!cdecl" +private foreign handler _shape_filter_group(in pS as CInt) returns CInt binds to "c:box2dxt>b2lc_shape_filter_group!cdecl" +private foreign handler _shape_enable_sensor_events(in pS as CInt, in pF as CInt) returns nothing binds to "c:box2dxt>b2lc_shape_enable_sensor_events!cdecl" +private foreign handler _shape_are_sensor_events_enabled(in pS as CInt) returns CInt binds to "c:box2dxt>b2lc_shape_are_sensor_events_enabled!cdecl" +private foreign handler _shape_enable_contact_events(in pS as CInt, in pF as CInt) returns nothing binds to "c:box2dxt>b2lc_shape_enable_contact_events!cdecl" +private foreign handler _shape_are_contact_events_enabled(in pS as CInt) returns CInt binds to "c:box2dxt>b2lc_shape_are_contact_events_enabled!cdecl" +private foreign handler _shape_enable_hit_events(in pS as CInt, in pF as CInt) returns nothing binds to "c:box2dxt>b2lc_shape_enable_hit_events!cdecl" +private foreign handler _shape_are_hit_events_enabled(in pS as CInt) returns CInt binds to "c:box2dxt>b2lc_shape_are_hit_events_enabled!cdecl" +private foreign handler _shape_enable_presolve_events(in pS as CInt, in pF as CInt) returns nothing binds to "c:box2dxt>b2lc_shape_enable_presolve_events!cdecl" +private foreign handler _shape_circle_update(in pS as CInt) returns nothing binds to "c:box2dxt>b2lc_shape_circle_update!cdecl" +private foreign handler _shape_circle_x() returns CDouble binds to "c:box2dxt>b2lc_shape_circle_x!cdecl" +private foreign handler _shape_circle_y() returns CDouble binds to "c:box2dxt>b2lc_shape_circle_y!cdecl" +private foreign handler _shape_circle_radius() returns CDouble binds to "c:box2dxt>b2lc_shape_circle_radius!cdecl" +private foreign handler _shape_capsule_update(in pS as CInt) returns nothing binds to "c:box2dxt>b2lc_shape_capsule_update!cdecl" +private foreign handler _shape_capsule_x1() returns CDouble binds to "c:box2dxt>b2lc_shape_capsule_x1!cdecl" +private foreign handler _shape_capsule_y1() returns CDouble binds to "c:box2dxt>b2lc_shape_capsule_y1!cdecl" +private foreign handler _shape_capsule_x2() returns CDouble binds to "c:box2dxt>b2lc_shape_capsule_x2!cdecl" +private foreign handler _shape_capsule_y2() returns CDouble binds to "c:box2dxt>b2lc_shape_capsule_y2!cdecl" +private foreign handler _shape_capsule_radius() returns CDouble binds to "c:box2dxt>b2lc_shape_capsule_radius!cdecl" +private foreign handler _shape_segment_update(in pS as CInt) returns nothing binds to "c:box2dxt>b2lc_shape_segment_update!cdecl" +private foreign handler _shape_segment_x1() returns CDouble binds to "c:box2dxt>b2lc_shape_segment_x1!cdecl" +private foreign handler _shape_segment_y1() returns CDouble binds to "c:box2dxt>b2lc_shape_segment_y1!cdecl" +private foreign handler _shape_segment_x2() returns CDouble binds to "c:box2dxt>b2lc_shape_segment_x2!cdecl" +private foreign handler _shape_segment_y2() returns CDouble binds to "c:box2dxt>b2lc_shape_segment_y2!cdecl" +private foreign handler _shape_polygon_update(in pS as CInt) returns CInt binds to "c:box2dxt>b2lc_shape_polygon_update!cdecl" +private foreign handler _shape_polygon_count() returns CInt binds to "c:box2dxt>b2lc_shape_polygon_count!cdecl" +private foreign handler _shape_polygon_vx(in pI as CInt) returns CDouble binds to "c:box2dxt>b2lc_shape_polygon_vx!cdecl" +private foreign handler _shape_polygon_vy(in pI as CInt) returns CDouble binds to "c:box2dxt>b2lc_shape_polygon_vy!cdecl" +private foreign handler _shape_polygon_radius() returns CDouble binds to "c:box2dxt>b2lc_shape_polygon_radius!cdecl" +private foreign handler _shape_set_circle(in pS as CInt, in pCx as CDouble, in pCy as CDouble, in pR as CDouble) returns nothing binds to "c:box2dxt>b2lc_shape_set_circle!cdecl" +private foreign handler _shape_set_capsule(in pS as CInt, in pX1 as CDouble, in pY1 as CDouble, in pX2 as CDouble, in pY2 as CDouble, in pR as CDouble) returns nothing binds to "c:box2dxt>b2lc_shape_set_capsule!cdecl" +private foreign handler _shape_set_segment(in pS as CInt, in pX1 as CDouble, in pY1 as CDouble, in pX2 as CDouble, in pY2 as CDouble) returns nothing binds to "c:box2dxt>b2lc_shape_set_segment!cdecl" +private foreign handler _shape_set_polygon(in pS as CInt) returns nothing binds to "c:box2dxt>b2lc_shape_set_polygon!cdecl" +private foreign handler _shape_raycast(in pS as CInt, in pX1 as CDouble, in pY1 as CDouble, in pX2 as CDouble, in pY2 as CDouble) returns CInt binds to "c:box2dxt>b2lc_shape_raycast!cdecl" +private foreign handler _shape_ray_x() returns CDouble binds to "c:box2dxt>b2lc_shape_ray_x!cdecl" +private foreign handler _shape_ray_y() returns CDouble binds to "c:box2dxt>b2lc_shape_ray_y!cdecl" +private foreign handler _shape_ray_normal_x() returns CDouble binds to "c:box2dxt>b2lc_shape_ray_normal_x!cdecl" +private foreign handler _shape_ray_normal_y() returns CDouble binds to "c:box2dxt>b2lc_shape_ray_normal_y!cdecl" +private foreign handler _shape_ray_fraction() returns CDouble binds to "c:box2dxt>b2lc_shape_ray_fraction!cdecl" +private foreign handler _shape_aabb_update(in pS as CInt) returns nothing binds to "c:box2dxt>b2lc_shape_aabb_update!cdecl" +private foreign handler _shape_closest_point_x(in pS as CInt, in pTx as CDouble, in pTy as CDouble) returns CDouble binds to "c:box2dxt>b2lc_shape_closest_point_x!cdecl" +private foreign handler _shape_closest_point_y(in pS as CInt, in pTx as CDouble, in pTy as CDouble) returns CDouble binds to "c:box2dxt>b2lc_shape_closest_point_y!cdecl" +private foreign handler _shape_mass_data_update(in pS as CInt) returns nothing binds to "c:box2dxt>b2lc_shape_mass_data_update!cdecl" +private foreign handler _shape_sensor_capacity(in pS as CInt) returns CInt binds to "c:box2dxt>b2lc_shape_sensor_capacity!cdecl" +private foreign handler _shape_sensor_overlaps_update(in pS as CInt) returns CInt binds to "c:box2dxt>b2lc_shape_sensor_overlaps_update!cdecl" +private foreign handler _shape_sensor_overlap_count() returns CInt binds to "c:box2dxt>b2lc_shape_sensor_overlap_count!cdecl" +private foreign handler _shape_sensor_overlap_at(in pI as CInt) returns CInt binds to "c:box2dxt>b2lc_shape_sensor_overlap_at!cdecl" + +-- chains +private foreign handler _chain_begin() returns nothing binds to "c:box2dxt>b2lc_chain_begin!cdecl" +private foreign handler _chain_add_point(in pX as CDouble, in pY as CDouble) returns nothing binds to "c:box2dxt>b2lc_chain_add_point!cdecl" +private foreign handler _chain_create(in pB as CInt, in pLoop as CInt, in pFriction as CDouble, in pRestitution as CDouble) returns CInt binds to "c:box2dxt>b2lc_chain_create!cdecl" +private foreign handler _chain_destroy(in pC as CInt) returns nothing binds to "c:box2dxt>b2lc_chain_destroy!cdecl" +private foreign handler _chain_is_valid(in pC as CInt) returns CInt binds to "c:box2dxt>b2lc_chain_is_valid!cdecl" +private foreign handler _chain_set_friction(in pC as CInt, in pF as CDouble) returns nothing binds to "c:box2dxt>b2lc_chain_set_friction!cdecl" +private foreign handler _chain_friction(in pC as CInt) returns CDouble binds to "c:box2dxt>b2lc_chain_friction!cdecl" +private foreign handler _chain_set_restitution(in pC as CInt, in pR as CDouble) returns nothing binds to "c:box2dxt>b2lc_chain_set_restitution!cdecl" +private foreign handler _chain_restitution(in pC as CInt) returns CDouble binds to "c:box2dxt>b2lc_chain_restitution!cdecl" +private foreign handler _chain_segment_count(in pC as CInt) returns CInt binds to "c:box2dxt>b2lc_chain_segment_count!cdecl" +private foreign handler _chain_segment_at(in pI as CInt) returns CInt binds to "c:box2dxt>b2lc_chain_segment_at!cdecl" + +-- joints: generic +private foreign handler _joint_type(in pJ as CInt) returns CInt binds to "c:box2dxt>b2lc_joint_type!cdecl" +private foreign handler _joint_body_a(in pJ as CInt) returns CInt binds to "c:box2dxt>b2lc_joint_body_a!cdecl" +private foreign handler _joint_body_b(in pJ as CInt) returns CInt binds to "c:box2dxt>b2lc_joint_body_b!cdecl" +private foreign handler _joint_local_anchor_a_x(in pJ as CInt) returns CDouble binds to "c:box2dxt>b2lc_joint_local_anchor_a_x!cdecl" +private foreign handler _joint_local_anchor_a_y(in pJ as CInt) returns CDouble binds to "c:box2dxt>b2lc_joint_local_anchor_a_y!cdecl" +private foreign handler _joint_local_anchor_b_x(in pJ as CInt) returns CDouble binds to "c:box2dxt>b2lc_joint_local_anchor_b_x!cdecl" +private foreign handler _joint_local_anchor_b_y(in pJ as CInt) returns CDouble binds to "c:box2dxt>b2lc_joint_local_anchor_b_y!cdecl" +private foreign handler _joint_get_collide_connected(in pJ as CInt) returns CInt binds to "c:box2dxt>b2lc_joint_get_collide_connected!cdecl" +private foreign handler _joint_set_collide_connected(in pJ as CInt, in pF as CInt) returns nothing binds to "c:box2dxt>b2lc_joint_set_collide_connected!cdecl" +private foreign handler _joint_constraint_force_x(in pJ as CInt) returns CDouble binds to "c:box2dxt>b2lc_joint_constraint_force_x!cdecl" +private foreign handler _joint_constraint_force_y(in pJ as CInt) returns CDouble binds to "c:box2dxt>b2lc_joint_constraint_force_y!cdecl" +private foreign handler _joint_constraint_torque(in pJ as CInt) returns CDouble binds to "c:box2dxt>b2lc_joint_constraint_torque!cdecl" +private foreign handler _joint_wake_bodies(in pJ as CInt) returns nothing binds to "c:box2dxt>b2lc_joint_wake_bodies!cdecl" + +-- joints: motor + filter +private foreign handler _joint_motor(in pW as CInt, in pBa as CInt, in pBb as CInt, in pOffX as CDouble, in pOffY as CDouble, in pAngOff as CDouble, in pMaxForce as CDouble, in pMaxTorque as CDouble, in pCorr as CDouble, in pCollide as CInt) returns CInt binds to "c:box2dxt>b2lc_joint_motor!cdecl" +private foreign handler _motor_set_linear_offset(in pJ as CInt, in pX as CDouble, in pY as CDouble) returns nothing binds to "c:box2dxt>b2lc_motor_set_linear_offset!cdecl" +private foreign handler _motor_linear_offset_x(in pJ as CInt) returns CDouble binds to "c:box2dxt>b2lc_motor_linear_offset_x!cdecl" +private foreign handler _motor_linear_offset_y(in pJ as CInt) returns CDouble binds to "c:box2dxt>b2lc_motor_linear_offset_y!cdecl" +private foreign handler _motor_set_angular_offset(in pJ as CInt, in pA as CDouble) returns nothing binds to "c:box2dxt>b2lc_motor_set_angular_offset!cdecl" +private foreign handler _motor_angular_offset(in pJ as CInt) returns CDouble binds to "c:box2dxt>b2lc_motor_angular_offset!cdecl" +private foreign handler _motor_set_max_force(in pJ as CInt, in pF as CDouble) returns nothing binds to "c:box2dxt>b2lc_motor_set_max_force!cdecl" +private foreign handler _motor_max_force(in pJ as CInt) returns CDouble binds to "c:box2dxt>b2lc_motor_max_force!cdecl" +private foreign handler _motor_set_max_torque(in pJ as CInt, in pT as CDouble) returns nothing binds to "c:box2dxt>b2lc_motor_set_max_torque!cdecl" +private foreign handler _motor_max_torque(in pJ as CInt) returns CDouble binds to "c:box2dxt>b2lc_motor_max_torque!cdecl" +private foreign handler _motor_set_correction_factor(in pJ as CInt, in pC as CDouble) returns nothing binds to "c:box2dxt>b2lc_motor_set_correction_factor!cdecl" +private foreign handler _motor_correction_factor(in pJ as CInt) returns CDouble binds to "c:box2dxt>b2lc_motor_correction_factor!cdecl" +private foreign handler _joint_filter(in pW as CInt, in pBa as CInt, in pBb as CInt) returns CInt binds to "c:box2dxt>b2lc_joint_filter!cdecl" + +-- joints: revolute granular +private foreign handler _revolute_enable_spring(in pJ as CInt, in pF as CInt) returns nothing binds to "c:box2dxt>b2lc_revolute_enable_spring!cdecl" +private foreign handler _revolute_is_spring_enabled(in pJ as CInt) returns CInt binds to "c:box2dxt>b2lc_revolute_is_spring_enabled!cdecl" +private foreign handler _revolute_set_spring_hertz(in pJ as CInt, in pHz as CDouble) returns nothing binds to "c:box2dxt>b2lc_revolute_set_spring_hertz!cdecl" +private foreign handler _revolute_spring_hertz(in pJ as CInt) returns CDouble binds to "c:box2dxt>b2lc_revolute_spring_hertz!cdecl" +private foreign handler _revolute_set_spring_damping(in pJ as CInt, in pD as CDouble) returns nothing binds to "c:box2dxt>b2lc_revolute_set_spring_damping!cdecl" +private foreign handler _revolute_spring_damping(in pJ as CInt) returns CDouble binds to "c:box2dxt>b2lc_revolute_spring_damping!cdecl" +private foreign handler _revolute_is_limit_enabled(in pJ as CInt) returns CInt binds to "c:box2dxt>b2lc_revolute_is_limit_enabled!cdecl" +private foreign handler _revolute_lower_limit(in pJ as CInt) returns CDouble binds to "c:box2dxt>b2lc_revolute_lower_limit!cdecl" +private foreign handler _revolute_upper_limit(in pJ as CInt) returns CDouble binds to "c:box2dxt>b2lc_revolute_upper_limit!cdecl" +private foreign handler _revolute_is_motor_enabled(in pJ as CInt) returns CInt binds to "c:box2dxt>b2lc_revolute_is_motor_enabled!cdecl" +private foreign handler _revolute_set_motor_speed(in pJ as CInt, in pS as CDouble) returns nothing binds to "c:box2dxt>b2lc_revolute_set_motor_speed!cdecl" +private foreign handler _revolute_motor_speed(in pJ as CInt) returns CDouble binds to "c:box2dxt>b2lc_revolute_motor_speed!cdecl" +private foreign handler _revolute_motor_torque(in pJ as CInt) returns CDouble binds to "c:box2dxt>b2lc_revolute_motor_torque!cdecl" +private foreign handler _revolute_set_max_motor_torque(in pJ as CInt, in pT as CDouble) returns nothing binds to "c:box2dxt>b2lc_revolute_set_max_motor_torque!cdecl" +private foreign handler _revolute_max_motor_torque(in pJ as CInt) returns CDouble binds to "c:box2dxt>b2lc_revolute_max_motor_torque!cdecl" + +-- joints: prismatic granular +private foreign handler _prismatic_enable_spring(in pJ as CInt, in pF as CInt) returns nothing binds to "c:box2dxt>b2lc_prismatic_enable_spring!cdecl" +private foreign handler _prismatic_is_spring_enabled(in pJ as CInt) returns CInt binds to "c:box2dxt>b2lc_prismatic_is_spring_enabled!cdecl" +private foreign handler _prismatic_set_spring_hertz(in pJ as CInt, in pHz as CDouble) returns nothing binds to "c:box2dxt>b2lc_prismatic_set_spring_hertz!cdecl" +private foreign handler _prismatic_spring_hertz(in pJ as CInt) returns CDouble binds to "c:box2dxt>b2lc_prismatic_spring_hertz!cdecl" +private foreign handler _prismatic_set_spring_damping(in pJ as CInt, in pD as CDouble) returns nothing binds to "c:box2dxt>b2lc_prismatic_set_spring_damping!cdecl" +private foreign handler _prismatic_spring_damping(in pJ as CInt) returns CDouble binds to "c:box2dxt>b2lc_prismatic_spring_damping!cdecl" +private foreign handler _prismatic_is_limit_enabled(in pJ as CInt) returns CInt binds to "c:box2dxt>b2lc_prismatic_is_limit_enabled!cdecl" +private foreign handler _prismatic_lower_limit(in pJ as CInt) returns CDouble binds to "c:box2dxt>b2lc_prismatic_lower_limit!cdecl" +private foreign handler _prismatic_upper_limit(in pJ as CInt) returns CDouble binds to "c:box2dxt>b2lc_prismatic_upper_limit!cdecl" +private foreign handler _prismatic_is_motor_enabled(in pJ as CInt) returns CInt binds to "c:box2dxt>b2lc_prismatic_is_motor_enabled!cdecl" +private foreign handler _prismatic_set_motor_speed(in pJ as CInt, in pS as CDouble) returns nothing binds to "c:box2dxt>b2lc_prismatic_set_motor_speed!cdecl" +private foreign handler _prismatic_motor_speed(in pJ as CInt) returns CDouble binds to "c:box2dxt>b2lc_prismatic_motor_speed!cdecl" +private foreign handler _prismatic_motor_force(in pJ as CInt) returns CDouble binds to "c:box2dxt>b2lc_prismatic_motor_force!cdecl" +private foreign handler _prismatic_max_motor_force(in pJ as CInt) returns CDouble binds to "c:box2dxt>b2lc_prismatic_max_motor_force!cdecl" +private foreign handler _prismatic_speed(in pJ as CInt) returns CDouble binds to "c:box2dxt>b2lc_prismatic_speed!cdecl" + +-- joints: distance granular +private foreign handler _distance_is_spring_enabled(in pJ as CInt) returns CInt binds to "c:box2dxt>b2lc_distance_is_spring_enabled!cdecl" +private foreign handler _distance_spring_hertz(in pJ as CInt) returns CDouble binds to "c:box2dxt>b2lc_distance_spring_hertz!cdecl" +private foreign handler _distance_spring_damping(in pJ as CInt) returns CDouble binds to "c:box2dxt>b2lc_distance_spring_damping!cdecl" +private foreign handler _distance_is_limit_enabled(in pJ as CInt) returns CInt binds to "c:box2dxt>b2lc_distance_is_limit_enabled!cdecl" +private foreign handler _distance_min_length(in pJ as CInt) returns CDouble binds to "c:box2dxt>b2lc_distance_min_length!cdecl" +private foreign handler _distance_max_length(in pJ as CInt) returns CDouble binds to "c:box2dxt>b2lc_distance_max_length!cdecl" +private foreign handler _distance_current_length(in pJ as CInt) returns CDouble binds to "c:box2dxt>b2lc_distance_current_length!cdecl" +private foreign handler _distance_enable_motor(in pJ as CInt, in pF as CInt) returns nothing binds to "c:box2dxt>b2lc_distance_enable_motor!cdecl" +private foreign handler _distance_is_motor_enabled(in pJ as CInt) returns CInt binds to "c:box2dxt>b2lc_distance_is_motor_enabled!cdecl" +private foreign handler _distance_set_motor_speed(in pJ as CInt, in pS as CDouble) returns nothing binds to "c:box2dxt>b2lc_distance_set_motor_speed!cdecl" +private foreign handler _distance_motor_speed(in pJ as CInt) returns CDouble binds to "c:box2dxt>b2lc_distance_motor_speed!cdecl" +private foreign handler _distance_set_max_motor_force(in pJ as CInt, in pF as CDouble) returns nothing binds to "c:box2dxt>b2lc_distance_set_max_motor_force!cdecl" +private foreign handler _distance_max_motor_force(in pJ as CInt) returns CDouble binds to "c:box2dxt>b2lc_distance_max_motor_force!cdecl" +private foreign handler _distance_motor_force(in pJ as CInt) returns CDouble binds to "c:box2dxt>b2lc_distance_motor_force!cdecl" + +-- joints: weld +private foreign handler _weld_reference_angle(in pJ as CInt) returns CDouble binds to "c:box2dxt>b2lc_weld_reference_angle!cdecl" +private foreign handler _weld_set_reference_angle(in pJ as CInt, in pA as CDouble) returns nothing binds to "c:box2dxt>b2lc_weld_set_reference_angle!cdecl" +private foreign handler _weld_linear_hertz(in pJ as CInt) returns CDouble binds to "c:box2dxt>b2lc_weld_linear_hertz!cdecl" +private foreign handler _weld_linear_damping(in pJ as CInt) returns CDouble binds to "c:box2dxt>b2lc_weld_linear_damping!cdecl" +private foreign handler _weld_angular_hertz(in pJ as CInt) returns CDouble binds to "c:box2dxt>b2lc_weld_angular_hertz!cdecl" +private foreign handler _weld_angular_damping(in pJ as CInt) returns CDouble binds to "c:box2dxt>b2lc_weld_angular_damping!cdecl" + +-- joints: wheel granular +private foreign handler _wheel_is_spring_enabled(in pJ as CInt) returns CInt binds to "c:box2dxt>b2lc_wheel_is_spring_enabled!cdecl" +private foreign handler _wheel_spring_hertz(in pJ as CInt) returns CDouble binds to "c:box2dxt>b2lc_wheel_spring_hertz!cdecl" +private foreign handler _wheel_spring_damping(in pJ as CInt) returns CDouble binds to "c:box2dxt>b2lc_wheel_spring_damping!cdecl" +private foreign handler _wheel_enable_limit(in pJ as CInt, in pF as CInt) returns nothing binds to "c:box2dxt>b2lc_wheel_enable_limit!cdecl" +private foreign handler _wheel_is_limit_enabled(in pJ as CInt) returns CInt binds to "c:box2dxt>b2lc_wheel_is_limit_enabled!cdecl" +private foreign handler _wheel_set_limits(in pJ as CInt, in pLo as CDouble, in pHi as CDouble) returns nothing binds to "c:box2dxt>b2lc_wheel_set_limits!cdecl" +private foreign handler _wheel_lower_limit(in pJ as CInt) returns CDouble binds to "c:box2dxt>b2lc_wheel_lower_limit!cdecl" +private foreign handler _wheel_upper_limit(in pJ as CInt) returns CDouble binds to "c:box2dxt>b2lc_wheel_upper_limit!cdecl" +private foreign handler _wheel_is_motor_enabled(in pJ as CInt) returns CInt binds to "c:box2dxt>b2lc_wheel_is_motor_enabled!cdecl" +private foreign handler _wheel_motor_speed(in pJ as CInt) returns CDouble binds to "c:box2dxt>b2lc_wheel_motor_speed!cdecl" +private foreign handler _wheel_motor_torque(in pJ as CInt) returns CDouble binds to "c:box2dxt>b2lc_wheel_motor_torque!cdecl" +private foreign handler _wheel_max_motor_torque(in pJ as CInt) returns CDouble binds to "c:box2dxt>b2lc_wheel_max_motor_torque!cdecl" + +-- joints: mouse granular +private foreign handler _mouse_target_x(in pJ as CInt) returns CDouble binds to "c:box2dxt>b2lc_mouse_target_x!cdecl" +private foreign handler _mouse_target_y(in pJ as CInt) returns CDouble binds to "c:box2dxt>b2lc_mouse_target_y!cdecl" +private foreign handler _mouse_set_spring_hertz(in pJ as CInt, in pHz as CDouble) returns nothing binds to "c:box2dxt>b2lc_mouse_set_spring_hertz!cdecl" +private foreign handler _mouse_spring_hertz(in pJ as CInt) returns CDouble binds to "c:box2dxt>b2lc_mouse_spring_hertz!cdecl" +private foreign handler _mouse_set_spring_damping(in pJ as CInt, in pD as CDouble) returns nothing binds to "c:box2dxt>b2lc_mouse_set_spring_damping!cdecl" +private foreign handler _mouse_spring_damping(in pJ as CInt) returns CDouble binds to "c:box2dxt>b2lc_mouse_spring_damping!cdecl" +private foreign handler _mouse_set_max_force(in pJ as CInt, in pF as CDouble) returns nothing binds to "c:box2dxt>b2lc_mouse_set_max_force!cdecl" +private foreign handler _mouse_max_force(in pJ as CInt) returns CDouble binds to "c:box2dxt>b2lc_mouse_max_force!cdecl" + +-- world queries (overlap / ray-cast-all / shape-cast) +private foreign handler _query_overlap_aabb(in pW as CInt, in pX1 as CDouble, in pY1 as CDouble, in pX2 as CDouble, in pY2 as CDouble) returns CInt binds to "c:box2dxt>b2lc_query_overlap_aabb!cdecl" +private foreign handler _query_overlap_point(in pW as CInt, in pX as CDouble, in pY as CDouble) returns CInt binds to "c:box2dxt>b2lc_query_overlap_point!cdecl" +private foreign handler _query_overlap_circle(in pW as CInt, in pCx as CDouble, in pCy as CDouble, in pR as CDouble) returns CInt binds to "c:box2dxt>b2lc_query_overlap_circle!cdecl" +private foreign handler _query_overlap_shape(in pW as CInt, in pRadius as CDouble) returns CInt binds to "c:box2dxt>b2lc_query_overlap_shape!cdecl" +private foreign handler _query_raycast_all(in pW as CInt, in pX1 as CDouble, in pY1 as CDouble, in pX2 as CDouble, in pY2 as CDouble) returns CInt binds to "c:box2dxt>b2lc_query_raycast_all!cdecl" +private foreign handler _query_shapecast(in pW as CInt, in pRadius as CDouble, in pDx as CDouble, in pDy as CDouble) returns CInt binds to "c:box2dxt>b2lc_query_shapecast!cdecl" +private foreign handler _query_count() returns CInt binds to "c:box2dxt>b2lc_query_count!cdecl" +private foreign handler _query_body(in pI as CInt) returns CInt binds to "c:box2dxt>b2lc_query_body!cdecl" +private foreign handler _query_shape(in pI as CInt) returns CInt binds to "c:box2dxt>b2lc_query_shape!cdecl" +private foreign handler _query_x(in pI as CInt) returns CDouble binds to "c:box2dxt>b2lc_query_x!cdecl" +private foreign handler _query_y(in pI as CInt) returns CDouble binds to "c:box2dxt>b2lc_query_y!cdecl" +private foreign handler _query_normal_x(in pI as CInt) returns CDouble binds to "c:box2dxt>b2lc_query_normal_x!cdecl" +private foreign handler _query_normal_y(in pI as CInt) returns CDouble binds to "c:box2dxt>b2lc_query_normal_y!cdecl" +private foreign handler _query_fraction(in pI as CInt) returns CDouble binds to "c:box2dxt>b2lc_query_fraction!cdecl" + -- small bool -> int helper (no foreign call, so no unsafe needed) private handler bi(in pB as Boolean) returns Integer if pB then @@ -819,4 +1130,1828 @@ public handler b2ContactEndBodyB(in pIndex as Integer) returns Integer return tR end handler +-- ===================================================================== +-- ABI v3 additions: public API +-- ===================================================================== + +-- ---- shape-def builder (one-shot; consumed by the next shape creation) ---- +public handler b2ShapeDefReset() + unsafe + _shapedef_reset() + end unsafe +end handler +public handler b2ShapeDefSensor(in pFlag as Boolean) + unsafe + _shapedef_set_sensor(bi(pFlag)) + end unsafe +end handler +public handler b2ShapeDefEnableContactEvents(in pFlag as Boolean) + unsafe + _shapedef_set_enable_contact_events(bi(pFlag)) + end unsafe +end handler +public handler b2ShapeDefEnableSensorEvents(in pFlag as Boolean) + unsafe + _shapedef_set_enable_sensor_events(bi(pFlag)) + end unsafe +end handler +public handler b2ShapeDefEnableHitEvents(in pFlag as Boolean) + unsafe + _shapedef_set_enable_hit_events(bi(pFlag)) + end unsafe +end handler +public handler b2ShapeDefEnablePreSolveEvents(in pFlag as Boolean) + unsafe + _shapedef_set_enable_presolve_events(bi(pFlag)) + end unsafe +end handler +public handler b2ShapeDefFilter(in pCategory as Number, in pMask as Number, in pGroup as Integer) + unsafe + _shapedef_set_filter(pCategory, pMask, pGroup) + end unsafe +end handler +public handler b2ShapeDefMaterialId(in pId as Integer) + unsafe + _shapedef_set_material_id(pId) + end unsafe +end handler + +-- ---- contact HIT events (snapshotted by b2ContactsUpdate; 1-based) ---- +public handler b2ContactHitCount() returns Integer + variable tR as Integer + unsafe + put _contact_hit_count() into tR + end unsafe + return tR +end handler +public handler b2ContactHitBodyA(in pIndex as Integer) returns Integer + variable tR as Integer + unsafe + put _contact_hit_a(pIndex - 1) into tR + end unsafe + return tR +end handler +public handler b2ContactHitBodyB(in pIndex as Integer) returns Integer + variable tR as Integer + unsafe + put _contact_hit_b(pIndex - 1) into tR + end unsafe + return tR +end handler +public handler b2ContactHitX(in pIndex as Integer) returns Number + variable tR as Number + unsafe + put _contact_hit_x(pIndex - 1) into tR + end unsafe + return tR +end handler +public handler b2ContactHitY(in pIndex as Integer) returns Number + variable tR as Number + unsafe + put _contact_hit_y(pIndex - 1) into tR + end unsafe + return tR +end handler +public handler b2ContactHitNormalX(in pIndex as Integer) returns Number + variable tR as Number + unsafe + put _contact_hit_nx(pIndex - 1) into tR + end unsafe + return tR +end handler +public handler b2ContactHitNormalY(in pIndex as Integer) returns Number + variable tR as Number + unsafe + put _contact_hit_ny(pIndex - 1) into tR + end unsafe + return tR +end handler +public handler b2ContactHitSpeed(in pIndex as Integer) returns Number + variable tR as Number + unsafe + put _contact_hit_speed(pIndex - 1) into tR + end unsafe + return tR +end handler + +-- ---- sensor events (shape handles; 1-based) ---- +public handler b2SensorsUpdate(in pWorld as Integer) returns Integer + variable tR as Integer + unsafe + put _sensors_update(pWorld) into tR + end unsafe + return tR +end handler +public handler b2SensorBeginCount() returns Integer + variable tR as Integer + unsafe + put _sensor_begin_count() into tR + end unsafe + return tR +end handler +public handler b2SensorEndCount() returns Integer + variable tR as Integer + unsafe + put _sensor_end_count() into tR + end unsafe + return tR +end handler +public handler b2SensorBeginSensorShape(in pIndex as Integer) returns Integer + variable tR as Integer + unsafe + put _sensor_begin_sensor(pIndex - 1) into tR + end unsafe + return tR +end handler +public handler b2SensorBeginVisitorShape(in pIndex as Integer) returns Integer + variable tR as Integer + unsafe + put _sensor_begin_visitor(pIndex - 1) into tR + end unsafe + return tR +end handler +public handler b2SensorEndSensorShape(in pIndex as Integer) returns Integer + variable tR as Integer + unsafe + put _sensor_end_sensor(pIndex - 1) into tR + end unsafe + return tR +end handler +public handler b2SensorEndVisitorShape(in pIndex as Integer) returns Integer + variable tR as Integer + unsafe + put _sensor_end_visitor(pIndex - 1) into tR + end unsafe + return tR +end handler + +-- ---- body MOVE events (bulk transform readback; 1-based) ---- +public handler b2BodiesUpdate(in pWorld as Integer) returns Integer + variable tR as Integer + unsafe + put _bodies_update(pWorld) into tR + end unsafe + return tR +end handler +public handler b2BodyMoveCount() returns Integer + variable tR as Integer + unsafe + put _body_move_count() into tR + end unsafe + return tR +end handler +public handler b2BodyMoveBody(in pIndex as Integer) returns Integer + variable tR as Integer + unsafe + put _body_move_body(pIndex - 1) into tR + end unsafe + return tR +end handler +public handler b2BodyMoveX(in pIndex as Integer) returns Number + variable tR as Number + unsafe + put _body_move_x(pIndex - 1) into tR + end unsafe + return tR +end handler +public handler b2BodyMoveY(in pIndex as Integer) returns Number + variable tR as Number + unsafe + put _body_move_y(pIndex - 1) into tR + end unsafe + return tR +end handler +public handler b2BodyMoveAngle(in pIndex as Integer) returns Number + variable tR as Number + unsafe + put _body_move_angle(pIndex - 1) into tR + end unsafe + return tR +end handler +public handler b2BodyMoveFellAsleep(in pIndex as Integer) returns Boolean + variable tR as Integer + unsafe + put _body_move_asleep(pIndex - 1) into tR + end unsafe + return tR is 1 +end handler + +-- ---- world: tuning / info / explode / profile / counters ---- +public handler b2WorldGravityX(in pWorld as Integer) returns Number + variable tR as Number + unsafe + put _world_gravity_x(pWorld) into tR + end unsafe + return tR +end handler +public handler b2WorldGravityY(in pWorld as Integer) returns Number + variable tR as Number + unsafe + put _world_gravity_y(pWorld) into tR + end unsafe + return tR +end handler +public handler b2SetRestitutionThreshold(in pWorld as Integer, in pValue as Number) + unsafe + _world_set_restitution_threshold(pWorld, pValue) + end unsafe +end handler +public handler b2RestitutionThreshold(in pWorld as Integer) returns Number + variable tR as Number + unsafe + put _world_restitution_threshold(pWorld) into tR + end unsafe + return tR +end handler +public handler b2SetHitEventThreshold(in pWorld as Integer, in pValue as Number) + unsafe + _world_set_hit_event_threshold(pWorld, pValue) + end unsafe +end handler +public handler b2HitEventThreshold(in pWorld as Integer) returns Number + variable tR as Number + unsafe + put _world_hit_event_threshold(pWorld) into tR + end unsafe + return tR +end handler +public handler b2SetContactTuning(in pWorld as Integer, in pHertz as Number, in pDamping as Number, in pPushSpeed as Number) + unsafe + _world_set_contact_tuning(pWorld, pHertz, pDamping, pPushSpeed) + end unsafe +end handler +public handler b2SetJointTuning(in pWorld as Integer, in pHertz as Number, in pDamping as Number) + unsafe + _world_set_joint_tuning(pWorld, pHertz, pDamping) + end unsafe +end handler +public handler b2SetMaximumLinearSpeed(in pWorld as Integer, in pValue as Number) + unsafe + _world_set_maximum_linear_speed(pWorld, pValue) + end unsafe +end handler +public handler b2MaximumLinearSpeed(in pWorld as Integer) returns Number + variable tR as Number + unsafe + put _world_maximum_linear_speed(pWorld) into tR + end unsafe + return tR +end handler +public handler b2EnableWarmStarting(in pWorld as Integer, in pFlag as Boolean) + unsafe + _world_enable_warm_starting(pWorld, bi(pFlag)) + end unsafe +end handler +public handler b2IsWarmStartingEnabled(in pWorld as Integer) returns Boolean + variable tR as Integer + unsafe + put _world_is_warm_starting(pWorld) into tR + end unsafe + return tR is 1 +end handler +public handler b2EnableSpeculative(in pWorld as Integer, in pFlag as Boolean) + unsafe + _world_enable_speculative(pWorld, bi(pFlag)) + end unsafe +end handler +public handler b2IsSleepingEnabled(in pWorld as Integer) returns Boolean + variable tR as Integer + unsafe + put _world_is_sleeping_enabled(pWorld) into tR + end unsafe + return tR is 1 +end handler +public handler b2IsContinuousEnabled(in pWorld as Integer) returns Boolean + variable tR as Integer + unsafe + put _world_is_continuous_enabled(pWorld) into tR + end unsafe + return tR is 1 +end handler +public handler b2AwakeBodyCount(in pWorld as Integer) returns Integer + variable tR as Integer + unsafe + put _world_awake_body_count(pWorld) into tR + end unsafe + return tR +end handler +public handler b2WorldExplode(in pWorld as Integer, in pX as Number, in pY as Number, in pRadius as Number, in pFalloff as Number, in pImpulsePerLength as Number) + unsafe + _world_explode(pWorld, pX, pY, pRadius, pFalloff, pImpulsePerLength) + end unsafe +end handler +public handler b2WorldProfileUpdate(in pWorld as Integer) + unsafe + _world_profile_update(pWorld) + end unsafe +end handler +public handler b2WorldProfileStep() returns Number + variable tR as Number + unsafe + put _world_profile_step() into tR + end unsafe + return tR +end handler +public handler b2WorldProfilePairs() returns Number + variable tR as Number + unsafe + put _world_profile_pairs() into tR + end unsafe + return tR +end handler +public handler b2WorldProfileCollide() returns Number + variable tR as Number + unsafe + put _world_profile_collide() into tR + end unsafe + return tR +end handler +public handler b2WorldProfileSolve() returns Number + variable tR as Number + unsafe + put _world_profile_solve() into tR + end unsafe + return tR +end handler +public handler b2WorldProfileRefit() returns Number + variable tR as Number + unsafe + put _world_profile_refit() into tR + end unsafe + return tR +end handler +public handler b2WorldProfileSensors() returns Number + variable tR as Number + unsafe + put _world_profile_sensors() into tR + end unsafe + return tR +end handler +public handler b2WorldCountersUpdate(in pWorld as Integer) + unsafe + _world_counters_update(pWorld) + end unsafe +end handler +public handler b2WorldBodyCount() returns Integer + variable tR as Integer + unsafe + put _world_count_bodies() into tR + end unsafe + return tR +end handler +public handler b2WorldShapeCount() returns Integer + variable tR as Integer + unsafe + put _world_count_shapes() into tR + end unsafe + return tR +end handler +public handler b2WorldContactCount() returns Integer + variable tR as Integer + unsafe + put _world_count_contacts() into tR + end unsafe + return tR +end handler +public handler b2WorldJointCount() returns Integer + variable tR as Integer + unsafe + put _world_count_joints() into tR + end unsafe + return tR +end handler +public handler b2WorldIslandCount() returns Integer + variable tR as Integer + unsafe + put _world_count_islands() into tR + end unsafe + return tR +end handler + +-- ---- body: transforms / velocity-at-point / force-at-point / mass / enum ---- +public handler b2BodyWorldPointX(in pBody as Integer, in pLocalX as Number, in pLocalY as Number) returns Number + variable tR as Number + unsafe + put _body_world_point_x(pBody, pLocalX, pLocalY) into tR + end unsafe + return tR +end handler +public handler b2BodyWorldPointY(in pBody as Integer, in pLocalX as Number, in pLocalY as Number) returns Number + variable tR as Number + unsafe + put _body_world_point_y(pBody, pLocalX, pLocalY) into tR + end unsafe + return tR +end handler +public handler b2BodyLocalPointX(in pBody as Integer, in pWorldX as Number, in pWorldY as Number) returns Number + variable tR as Number + unsafe + put _body_local_point_x(pBody, pWorldX, pWorldY) into tR + end unsafe + return tR +end handler +public handler b2BodyLocalPointY(in pBody as Integer, in pWorldX as Number, in pWorldY as Number) returns Number + variable tR as Number + unsafe + put _body_local_point_y(pBody, pWorldX, pWorldY) into tR + end unsafe + return tR +end handler +public handler b2BodyWorldVectorX(in pBody as Integer, in pLocalX as Number, in pLocalY as Number) returns Number + variable tR as Number + unsafe + put _body_world_vector_x(pBody, pLocalX, pLocalY) into tR + end unsafe + return tR +end handler +public handler b2BodyWorldVectorY(in pBody as Integer, in pLocalX as Number, in pLocalY as Number) returns Number + variable tR as Number + unsafe + put _body_world_vector_y(pBody, pLocalX, pLocalY) into tR + end unsafe + return tR +end handler +public handler b2BodyLocalVectorX(in pBody as Integer, in pWorldX as Number, in pWorldY as Number) returns Number + variable tR as Number + unsafe + put _body_local_vector_x(pBody, pWorldX, pWorldY) into tR + end unsafe + return tR +end handler +public handler b2BodyLocalVectorY(in pBody as Integer, in pWorldX as Number, in pWorldY as Number) returns Number + variable tR as Number + unsafe + put _body_local_vector_y(pBody, pWorldX, pWorldY) into tR + end unsafe + return tR +end handler +public handler b2BodyWorldPointVelocityX(in pBody as Integer, in pWorldX as Number, in pWorldY as Number) returns Number + variable tR as Number + unsafe + put _body_world_point_velocity_x(pBody, pWorldX, pWorldY) into tR + end unsafe + return tR +end handler +public handler b2BodyWorldPointVelocityY(in pBody as Integer, in pWorldX as Number, in pWorldY as Number) returns Number + variable tR as Number + unsafe + put _body_world_point_velocity_y(pBody, pWorldX, pWorldY) into tR + end unsafe + return tR +end handler +public handler b2BodyLocalPointVelocityX(in pBody as Integer, in pLocalX as Number, in pLocalY as Number) returns Number + variable tR as Number + unsafe + put _body_local_point_velocity_x(pBody, pLocalX, pLocalY) into tR + end unsafe + return tR +end handler +public handler b2BodyLocalPointVelocityY(in pBody as Integer, in pLocalX as Number, in pLocalY as Number) returns Number + variable tR as Number + unsafe + put _body_local_point_velocity_y(pBody, pLocalX, pLocalY) into tR + end unsafe + return tR +end handler +public handler b2ApplyForceAt(in pBody as Integer, in pFx as Number, in pFy as Number, in pPointX as Number, in pPointY as Number, in pWake as Boolean) + unsafe + _body_apply_force_at(pBody, pFx, pFy, pPointX, pPointY, bi(pWake)) + end unsafe +end handler +public handler b2ApplyImpulseAt(in pBody as Integer, in pIx as Number, in pIy as Number, in pPointX as Number, in pPointY as Number, in pWake as Boolean) + unsafe + _body_apply_impulse_at(pBody, pIx, pIy, pPointX, pPointY, bi(pWake)) + end unsafe +end handler +public handler b2BodyRotationalInertia(in pBody as Integer) returns Number + variable tR as Number + unsafe + put _body_rotational_inertia(pBody) into tR + end unsafe + return tR +end handler +public handler b2BodyLocalCenterX(in pBody as Integer) returns Number + variable tR as Number + unsafe + put _body_local_center_x(pBody) into tR + end unsafe + return tR +end handler +public handler b2BodyLocalCenterY(in pBody as Integer) returns Number + variable tR as Number + unsafe + put _body_local_center_y(pBody) into tR + end unsafe + return tR +end handler +public handler b2BodyMassDataUpdate(in pBody as Integer) + unsafe + _body_mass_data_update(pBody) + end unsafe +end handler +public handler b2MassDataMass() returns Number + variable tR as Number + unsafe + put _md_mass() into tR + end unsafe + return tR +end handler +public handler b2MassDataCenterX() returns Number + variable tR as Number + unsafe + put _md_center_x() into tR + end unsafe + return tR +end handler +public handler b2MassDataCenterY() returns Number + variable tR as Number + unsafe + put _md_center_y() into tR + end unsafe + return tR +end handler +public handler b2MassDataInertia() returns Number + variable tR as Number + unsafe + put _md_inertia() into tR + end unsafe + return tR +end handler +public handler b2SetMassData(in pBody as Integer, in pMass as Number, in pCenterX as Number, in pCenterY as Number, in pInertia as Number) + unsafe + _body_set_mass_data(pBody, pMass, pCenterX, pCenterY, pInertia) + end unsafe +end handler +public handler b2ApplyMassFromShapes(in pBody as Integer) + unsafe + _body_apply_mass_from_shapes(pBody) + end unsafe +end handler +public handler b2SetTargetTransform(in pBody as Integer, in pX as Number, in pY as Number, in pAngle as Number, in pTimeStep as Number) + unsafe + _body_set_target_transform(pBody, pX, pY, pAngle, pTimeStep) + end unsafe +end handler +public handler b2EnableSleep(in pBody as Integer, in pFlag as Boolean) + unsafe + _body_enable_sleep(pBody, bi(pFlag)) + end unsafe +end handler +public handler b2BodyIsSleepEnabled(in pBody as Integer) returns Boolean + variable tR as Integer + unsafe + put _body_is_sleep_enabled(pBody) into tR + end unsafe + return tR is 1 +end handler +public handler b2BodyIsFixedRotation(in pBody as Integer) returns Boolean + variable tR as Integer + unsafe + put _body_is_fixed_rotation(pBody) into tR + end unsafe + return tR is 1 +end handler +public handler b2BodyEnableContactEvents(in pBody as Integer, in pFlag as Boolean) + unsafe + _body_enable_contact_events(pBody, bi(pFlag)) + end unsafe +end handler +public handler b2BodyEnableHitEvents(in pBody as Integer, in pFlag as Boolean) + unsafe + _body_enable_hit_events(pBody, bi(pFlag)) + end unsafe +end handler +public handler b2BodyAABBUpdate(in pBody as Integer) + unsafe + _body_aabb_update(pBody) + end unsafe +end handler +public handler b2AABBLowerX() returns Number + variable tR as Number + unsafe + put _aabb_lower_x() into tR + end unsafe + return tR +end handler +public handler b2AABBLowerY() returns Number + variable tR as Number + unsafe + put _aabb_lower_y() into tR + end unsafe + return tR +end handler +public handler b2AABBUpperX() returns Number + variable tR as Number + unsafe + put _aabb_upper_x() into tR + end unsafe + return tR +end handler +public handler b2AABBUpperY() returns Number + variable tR as Number + unsafe + put _aabb_upper_y() into tR + end unsafe + return tR +end handler +public handler b2BodyShapeCount(in pBody as Integer) returns Integer + variable tR as Integer + unsafe + put _body_shape_count(pBody) into tR + end unsafe + return tR +end handler +public handler b2BodyShapeAt(in pIndex as Integer) returns Integer + variable tR as Integer + unsafe + put _body_shape_at(pIndex - 1) into tR + end unsafe + return tR +end handler +public handler b2BodyJointCount(in pBody as Integer) returns Integer + variable tR as Integer + unsafe + put _body_joint_count(pBody) into tR + end unsafe + return tR +end handler +public handler b2BodyJointAt(in pIndex as Integer) returns Integer + variable tR as Integer + unsafe + put _body_joint_at(pIndex - 1) into tR + end unsafe + return tR +end handler + +-- ---- shape: type / material / filter / event flags / geometry / queries ---- +public handler b2ShapeType(in pShape as Integer) returns Integer + variable tR as Integer + unsafe + put _shape_type(pShape) into tR + end unsafe + return tR +end handler +public handler b2ShapeIsSensor(in pShape as Integer) returns Boolean + variable tR as Integer + unsafe + put _shape_is_sensor(pShape) into tR + end unsafe + return tR is 1 +end handler +public handler b2ShapeDensity(in pShape as Integer) returns Number + variable tR as Number + unsafe + put _shape_density(pShape) into tR + end unsafe + return tR +end handler +public handler b2ShapeFriction(in pShape as Integer) returns Number + variable tR as Number + unsafe + put _shape_friction(pShape) into tR + end unsafe + return tR +end handler +public handler b2ShapeRestitution(in pShape as Integer) returns Number + variable tR as Number + unsafe + put _shape_restitution(pShape) into tR + end unsafe + return tR +end handler +public handler b2ShapeMaterialId(in pShape as Integer) returns Integer + variable tR as Integer + unsafe + put _shape_material_id(pShape) into tR + end unsafe + return tR +end handler +public handler b2SetShapeMaterialId(in pShape as Integer, in pId as Integer) + unsafe + _shape_set_material_id(pShape, pId) + end unsafe +end handler +public handler b2SetShapeFilter(in pShape as Integer, in pCategory as Number, in pMask as Number, in pGroup as Integer) + unsafe + _shape_set_filter(pShape, pCategory, pMask, pGroup) + end unsafe +end handler +public handler b2ShapeFilterCategory(in pShape as Integer) returns Number + variable tR as Number + unsafe + put _shape_filter_category(pShape) into tR + end unsafe + return tR +end handler +public handler b2ShapeFilterMask(in pShape as Integer) returns Number + variable tR as Number + unsafe + put _shape_filter_mask(pShape) into tR + end unsafe + return tR +end handler +public handler b2ShapeFilterGroup(in pShape as Integer) returns Integer + variable tR as Integer + unsafe + put _shape_filter_group(pShape) into tR + end unsafe + return tR +end handler +public handler b2ShapeEnableSensorEvents(in pShape as Integer, in pFlag as Boolean) + unsafe + _shape_enable_sensor_events(pShape, bi(pFlag)) + end unsafe +end handler +public handler b2ShapeSensorEventsEnabled(in pShape as Integer) returns Boolean + variable tR as Integer + unsafe + put _shape_are_sensor_events_enabled(pShape) into tR + end unsafe + return tR is 1 +end handler +public handler b2ShapeEnableContactEvents(in pShape as Integer, in pFlag as Boolean) + unsafe + _shape_enable_contact_events(pShape, bi(pFlag)) + end unsafe +end handler +public handler b2ShapeContactEventsEnabled(in pShape as Integer) returns Boolean + variable tR as Integer + unsafe + put _shape_are_contact_events_enabled(pShape) into tR + end unsafe + return tR is 1 +end handler +public handler b2ShapeEnableHitEvents(in pShape as Integer, in pFlag as Boolean) + unsafe + _shape_enable_hit_events(pShape, bi(pFlag)) + end unsafe +end handler +public handler b2ShapeHitEventsEnabled(in pShape as Integer) returns Boolean + variable tR as Integer + unsafe + put _shape_are_hit_events_enabled(pShape) into tR + end unsafe + return tR is 1 +end handler +public handler b2ShapeEnablePreSolveEvents(in pShape as Integer, in pFlag as Boolean) + unsafe + _shape_enable_presolve_events(pShape, bi(pFlag)) + end unsafe +end handler +public handler b2ShapeCircleUpdate(in pShape as Integer) + unsafe + _shape_circle_update(pShape) + end unsafe +end handler +public handler b2ShapeCircleX() returns Number + variable tR as Number + unsafe + put _shape_circle_x() into tR + end unsafe + return tR +end handler +public handler b2ShapeCircleY() returns Number + variable tR as Number + unsafe + put _shape_circle_y() into tR + end unsafe + return tR +end handler +public handler b2ShapeCircleRadius() returns Number + variable tR as Number + unsafe + put _shape_circle_radius() into tR + end unsafe + return tR +end handler +public handler b2ShapeCapsuleUpdate(in pShape as Integer) + unsafe + _shape_capsule_update(pShape) + end unsafe +end handler +public handler b2ShapeCapsuleX1() returns Number + variable tR as Number + unsafe + put _shape_capsule_x1() into tR + end unsafe + return tR +end handler +public handler b2ShapeCapsuleY1() returns Number + variable tR as Number + unsafe + put _shape_capsule_y1() into tR + end unsafe + return tR +end handler +public handler b2ShapeCapsuleX2() returns Number + variable tR as Number + unsafe + put _shape_capsule_x2() into tR + end unsafe + return tR +end handler +public handler b2ShapeCapsuleY2() returns Number + variable tR as Number + unsafe + put _shape_capsule_y2() into tR + end unsafe + return tR +end handler +public handler b2ShapeCapsuleRadius() returns Number + variable tR as Number + unsafe + put _shape_capsule_radius() into tR + end unsafe + return tR +end handler +public handler b2ShapeSegmentUpdate(in pShape as Integer) + unsafe + _shape_segment_update(pShape) + end unsafe +end handler +public handler b2ShapeSegmentX1() returns Number + variable tR as Number + unsafe + put _shape_segment_x1() into tR + end unsafe + return tR +end handler +public handler b2ShapeSegmentY1() returns Number + variable tR as Number + unsafe + put _shape_segment_y1() into tR + end unsafe + return tR +end handler +public handler b2ShapeSegmentX2() returns Number + variable tR as Number + unsafe + put _shape_segment_x2() into tR + end unsafe + return tR +end handler +public handler b2ShapeSegmentY2() returns Number + variable tR as Number + unsafe + put _shape_segment_y2() into tR + end unsafe + return tR +end handler +public handler b2ShapePolygonUpdate(in pShape as Integer) returns Integer + variable tR as Integer + unsafe + put _shape_polygon_update(pShape) into tR + end unsafe + return tR +end handler +public handler b2ShapePolygonCount() returns Integer + variable tR as Integer + unsafe + put _shape_polygon_count() into tR + end unsafe + return tR +end handler +public handler b2ShapePolygonVertexX(in pIndex as Integer) returns Number + variable tR as Number + unsafe + put _shape_polygon_vx(pIndex - 1) into tR + end unsafe + return tR +end handler +public handler b2ShapePolygonVertexY(in pIndex as Integer) returns Number + variable tR as Number + unsafe + put _shape_polygon_vy(pIndex - 1) into tR + end unsafe + return tR +end handler +public handler b2ShapePolygonRadius() returns Number + variable tR as Number + unsafe + put _shape_polygon_radius() into tR + end unsafe + return tR +end handler +public handler b2SetShapeCircle(in pShape as Integer, in pCx as Number, in pCy as Number, in pRadius as Number) + unsafe + _shape_set_circle(pShape, pCx, pCy, pRadius) + end unsafe +end handler +public handler b2SetShapeCapsule(in pShape as Integer, in pX1 as Number, in pY1 as Number, in pX2 as Number, in pY2 as Number, in pRadius as Number) + unsafe + _shape_set_capsule(pShape, pX1, pY1, pX2, pY2, pRadius) + end unsafe +end handler +public handler b2SetShapeSegment(in pShape as Integer, in pX1 as Number, in pY1 as Number, in pX2 as Number, in pY2 as Number) + unsafe + _shape_set_segment(pShape, pX1, pY1, pX2, pY2) + end unsafe +end handler +public handler b2SetShapePolygon(in pShape as Integer) + unsafe + _shape_set_polygon(pShape) + end unsafe +end handler +public handler b2ShapeRayCast(in pShape as Integer, in pX1 as Number, in pY1 as Number, in pX2 as Number, in pY2 as Number) returns Boolean + variable tR as Integer + unsafe + put _shape_raycast(pShape, pX1, pY1, pX2, pY2) into tR + end unsafe + return tR is 1 +end handler +public handler b2ShapeRayX() returns Number + variable tR as Number + unsafe + put _shape_ray_x() into tR + end unsafe + return tR +end handler +public handler b2ShapeRayY() returns Number + variable tR as Number + unsafe + put _shape_ray_y() into tR + end unsafe + return tR +end handler +public handler b2ShapeRayNormalX() returns Number + variable tR as Number + unsafe + put _shape_ray_normal_x() into tR + end unsafe + return tR +end handler +public handler b2ShapeRayNormalY() returns Number + variable tR as Number + unsafe + put _shape_ray_normal_y() into tR + end unsafe + return tR +end handler +public handler b2ShapeRayFraction() returns Number + variable tR as Number + unsafe + put _shape_ray_fraction() into tR + end unsafe + return tR +end handler +public handler b2ShapeAABBUpdate(in pShape as Integer) + unsafe + _shape_aabb_update(pShape) + end unsafe +end handler +public handler b2ShapeClosestPointX(in pShape as Integer, in pTargetX as Number, in pTargetY as Number) returns Number + variable tR as Number + unsafe + put _shape_closest_point_x(pShape, pTargetX, pTargetY) into tR + end unsafe + return tR +end handler +public handler b2ShapeClosestPointY(in pShape as Integer, in pTargetX as Number, in pTargetY as Number) returns Number + variable tR as Number + unsafe + put _shape_closest_point_y(pShape, pTargetX, pTargetY) into tR + end unsafe + return tR +end handler +public handler b2ShapeMassDataUpdate(in pShape as Integer) + unsafe + _shape_mass_data_update(pShape) + end unsafe +end handler +public handler b2ShapeSensorCapacity(in pShape as Integer) returns Integer + variable tR as Integer + unsafe + put _shape_sensor_capacity(pShape) into tR + end unsafe + return tR +end handler +public handler b2ShapeSensorOverlapsUpdate(in pShape as Integer) returns Integer + variable tR as Integer + unsafe + put _shape_sensor_overlaps_update(pShape) into tR + end unsafe + return tR +end handler +public handler b2ShapeSensorOverlapCount() returns Integer + variable tR as Integer + unsafe + put _shape_sensor_overlap_count() into tR + end unsafe + return tR +end handler +public handler b2ShapeSensorOverlapAt(in pIndex as Integer) returns Integer + variable tR as Integer + unsafe + put _shape_sensor_overlap_at(pIndex - 1) into tR + end unsafe + return tR +end handler + +-- ---- chains ---- +public handler b2ChainBegin() + unsafe + _chain_begin() + end unsafe +end handler +public handler b2ChainAddPoint(in pX as Number, in pY as Number) + unsafe + _chain_add_point(pX, pY) + end unsafe +end handler +public handler b2CreateChain(in pBody as Integer, in pLoop as Boolean, in pFriction as Number, in pRestitution as Number) returns Integer + variable tR as Integer + unsafe + put _chain_create(pBody, bi(pLoop), pFriction, pRestitution) into tR + end unsafe + return tR +end handler +public handler b2DestroyChain(in pChain as Integer) + unsafe + _chain_destroy(pChain) + end unsafe +end handler +public handler b2ChainIsValid(in pChain as Integer) returns Boolean + variable tR as Integer + unsafe + put _chain_is_valid(pChain) into tR + end unsafe + return tR is 1 +end handler +public handler b2SetChainFriction(in pChain as Integer, in pFriction as Number) + unsafe + _chain_set_friction(pChain, pFriction) + end unsafe +end handler +public handler b2ChainFriction(in pChain as Integer) returns Number + variable tR as Number + unsafe + put _chain_friction(pChain) into tR + end unsafe + return tR +end handler +public handler b2SetChainRestitution(in pChain as Integer, in pRestitution as Number) + unsafe + _chain_set_restitution(pChain, pRestitution) + end unsafe +end handler +public handler b2ChainRestitution(in pChain as Integer) returns Number + variable tR as Number + unsafe + put _chain_restitution(pChain) into tR + end unsafe + return tR +end handler +public handler b2ChainSegmentCount(in pChain as Integer) returns Integer + variable tR as Integer + unsafe + put _chain_segment_count(pChain) into tR + end unsafe + return tR +end handler +public handler b2ChainSegmentAt(in pIndex as Integer) returns Integer + variable tR as Integer + unsafe + put _chain_segment_at(pIndex - 1) into tR + end unsafe + return tR +end handler + +-- ---- joints: generic ---- +public handler b2JointType(in pJoint as Integer) returns Integer + variable tR as Integer + unsafe + put _joint_type(pJoint) into tR + end unsafe + return tR +end handler +public handler b2JointBodyA(in pJoint as Integer) returns Integer + variable tR as Integer + unsafe + put _joint_body_a(pJoint) into tR + end unsafe + return tR +end handler +public handler b2JointBodyB(in pJoint as Integer) returns Integer + variable tR as Integer + unsafe + put _joint_body_b(pJoint) into tR + end unsafe + return tR +end handler +public handler b2JointLocalAnchorAX(in pJoint as Integer) returns Number + variable tR as Number + unsafe + put _joint_local_anchor_a_x(pJoint) into tR + end unsafe + return tR +end handler +public handler b2JointLocalAnchorAY(in pJoint as Integer) returns Number + variable tR as Number + unsafe + put _joint_local_anchor_a_y(pJoint) into tR + end unsafe + return tR +end handler +public handler b2JointLocalAnchorBX(in pJoint as Integer) returns Number + variable tR as Number + unsafe + put _joint_local_anchor_b_x(pJoint) into tR + end unsafe + return tR +end handler +public handler b2JointLocalAnchorBY(in pJoint as Integer) returns Number + variable tR as Number + unsafe + put _joint_local_anchor_b_y(pJoint) into tR + end unsafe + return tR +end handler +public handler b2JointCollideConnected(in pJoint as Integer) returns Boolean + variable tR as Integer + unsafe + put _joint_get_collide_connected(pJoint) into tR + end unsafe + return tR is 1 +end handler +public handler b2SetJointCollideConnected(in pJoint as Integer, in pFlag as Boolean) + unsafe + _joint_set_collide_connected(pJoint, bi(pFlag)) + end unsafe +end handler +public handler b2JointConstraintForceX(in pJoint as Integer) returns Number + variable tR as Number + unsafe + put _joint_constraint_force_x(pJoint) into tR + end unsafe + return tR +end handler +public handler b2JointConstraintForceY(in pJoint as Integer) returns Number + variable tR as Number + unsafe + put _joint_constraint_force_y(pJoint) into tR + end unsafe + return tR +end handler +public handler b2JointConstraintTorque(in pJoint as Integer) returns Number + variable tR as Number + unsafe + put _joint_constraint_torque(pJoint) into tR + end unsafe + return tR +end handler +public handler b2JointWakeBodies(in pJoint as Integer) + unsafe + _joint_wake_bodies(pJoint) + end unsafe +end handler + +-- ---- joints: motor + filter ---- +public handler b2MotorJoint(in pWorld as Integer, in pBodyA as Integer, in pBodyB as Integer, in pOffsetX as Number, in pOffsetY as Number, in pAngularOffset as Number, in pMaxForce as Number, in pMaxTorque as Number, in pCorrectionFactor as Number, in pCollide as Boolean) returns Integer + variable tR as Integer + unsafe + put _joint_motor(pWorld, pBodyA, pBodyB, pOffsetX, pOffsetY, pAngularOffset, pMaxForce, pMaxTorque, pCorrectionFactor, bi(pCollide)) into tR + end unsafe + return tR +end handler +public handler b2MotorSetLinearOffset(in pJoint as Integer, in pX as Number, in pY as Number) + unsafe + _motor_set_linear_offset(pJoint, pX, pY) + end unsafe +end handler +public handler b2MotorLinearOffsetX(in pJoint as Integer) returns Number + variable tR as Number + unsafe + put _motor_linear_offset_x(pJoint) into tR + end unsafe + return tR +end handler +public handler b2MotorLinearOffsetY(in pJoint as Integer) returns Number + variable tR as Number + unsafe + put _motor_linear_offset_y(pJoint) into tR + end unsafe + return tR +end handler +public handler b2MotorSetAngularOffset(in pJoint as Integer, in pAngle as Number) + unsafe + _motor_set_angular_offset(pJoint, pAngle) + end unsafe +end handler +public handler b2MotorAngularOffset(in pJoint as Integer) returns Number + variable tR as Number + unsafe + put _motor_angular_offset(pJoint) into tR + end unsafe + return tR +end handler +public handler b2MotorSetMaxForce(in pJoint as Integer, in pForce as Number) + unsafe + _motor_set_max_force(pJoint, pForce) + end unsafe +end handler +public handler b2MotorMaxForce(in pJoint as Integer) returns Number + variable tR as Number + unsafe + put _motor_max_force(pJoint) into tR + end unsafe + return tR +end handler +public handler b2MotorSetMaxTorque(in pJoint as Integer, in pTorque as Number) + unsafe + _motor_set_max_torque(pJoint, pTorque) + end unsafe +end handler +public handler b2MotorMaxTorque(in pJoint as Integer) returns Number + variable tR as Number + unsafe + put _motor_max_torque(pJoint) into tR + end unsafe + return tR +end handler +public handler b2MotorSetCorrectionFactor(in pJoint as Integer, in pFactor as Number) + unsafe + _motor_set_correction_factor(pJoint, pFactor) + end unsafe +end handler +public handler b2MotorCorrectionFactor(in pJoint as Integer) returns Number + variable tR as Number + unsafe + put _motor_correction_factor(pJoint) into tR + end unsafe + return tR +end handler +public handler b2FilterJoint(in pWorld as Integer, in pBodyA as Integer, in pBodyB as Integer) returns Integer + variable tR as Integer + unsafe + put _joint_filter(pWorld, pBodyA, pBodyB) into tR + end unsafe + return tR +end handler + +-- ---- joints: revolute granular ---- +public handler b2RevoluteEnableSpring(in pJoint as Integer, in pFlag as Boolean) + unsafe + _revolute_enable_spring(pJoint, bi(pFlag)) + end unsafe +end handler +public handler b2RevoluteIsSpringEnabled(in pJoint as Integer) returns Boolean + variable tR as Integer + unsafe + put _revolute_is_spring_enabled(pJoint) into tR + end unsafe + return tR is 1 +end handler +public handler b2RevoluteSetSpringHertz(in pJoint as Integer, in pHertz as Number) + unsafe + _revolute_set_spring_hertz(pJoint, pHertz) + end unsafe +end handler +public handler b2RevoluteSpringHertz(in pJoint as Integer) returns Number + variable tR as Number + unsafe + put _revolute_spring_hertz(pJoint) into tR + end unsafe + return tR +end handler +public handler b2RevoluteSetSpringDamping(in pJoint as Integer, in pDamping as Number) + unsafe + _revolute_set_spring_damping(pJoint, pDamping) + end unsafe +end handler +public handler b2RevoluteSpringDamping(in pJoint as Integer) returns Number + variable tR as Number + unsafe + put _revolute_spring_damping(pJoint) into tR + end unsafe + return tR +end handler +public handler b2RevoluteIsLimitEnabled(in pJoint as Integer) returns Boolean + variable tR as Integer + unsafe + put _revolute_is_limit_enabled(pJoint) into tR + end unsafe + return tR is 1 +end handler +public handler b2RevoluteLowerLimit(in pJoint as Integer) returns Number + variable tR as Number + unsafe + put _revolute_lower_limit(pJoint) into tR + end unsafe + return tR +end handler +public handler b2RevoluteUpperLimit(in pJoint as Integer) returns Number + variable tR as Number + unsafe + put _revolute_upper_limit(pJoint) into tR + end unsafe + return tR +end handler +public handler b2RevoluteIsMotorEnabled(in pJoint as Integer) returns Boolean + variable tR as Integer + unsafe + put _revolute_is_motor_enabled(pJoint) into tR + end unsafe + return tR is 1 +end handler +public handler b2RevoluteSetMotorSpeed(in pJoint as Integer, in pSpeed as Number) + unsafe + _revolute_set_motor_speed(pJoint, pSpeed) + end unsafe +end handler +public handler b2RevoluteMotorSpeed(in pJoint as Integer) returns Number + variable tR as Number + unsafe + put _revolute_motor_speed(pJoint) into tR + end unsafe + return tR +end handler +public handler b2RevoluteMotorTorque(in pJoint as Integer) returns Number + variable tR as Number + unsafe + put _revolute_motor_torque(pJoint) into tR + end unsafe + return tR +end handler +public handler b2RevoluteSetMaxMotorTorque(in pJoint as Integer, in pTorque as Number) + unsafe + _revolute_set_max_motor_torque(pJoint, pTorque) + end unsafe +end handler +public handler b2RevoluteMaxMotorTorque(in pJoint as Integer) returns Number + variable tR as Number + unsafe + put _revolute_max_motor_torque(pJoint) into tR + end unsafe + return tR +end handler + +-- ---- joints: prismatic granular ---- +public handler b2PrismaticEnableSpring(in pJoint as Integer, in pFlag as Boolean) + unsafe + _prismatic_enable_spring(pJoint, bi(pFlag)) + end unsafe +end handler +public handler b2PrismaticIsSpringEnabled(in pJoint as Integer) returns Boolean + variable tR as Integer + unsafe + put _prismatic_is_spring_enabled(pJoint) into tR + end unsafe + return tR is 1 +end handler +public handler b2PrismaticSetSpringHertz(in pJoint as Integer, in pHertz as Number) + unsafe + _prismatic_set_spring_hertz(pJoint, pHertz) + end unsafe +end handler +public handler b2PrismaticSpringHertz(in pJoint as Integer) returns Number + variable tR as Number + unsafe + put _prismatic_spring_hertz(pJoint) into tR + end unsafe + return tR +end handler +public handler b2PrismaticSetSpringDamping(in pJoint as Integer, in pDamping as Number) + unsafe + _prismatic_set_spring_damping(pJoint, pDamping) + end unsafe +end handler +public handler b2PrismaticSpringDamping(in pJoint as Integer) returns Number + variable tR as Number + unsafe + put _prismatic_spring_damping(pJoint) into tR + end unsafe + return tR +end handler +public handler b2PrismaticIsLimitEnabled(in pJoint as Integer) returns Boolean + variable tR as Integer + unsafe + put _prismatic_is_limit_enabled(pJoint) into tR + end unsafe + return tR is 1 +end handler +public handler b2PrismaticLowerLimit(in pJoint as Integer) returns Number + variable tR as Number + unsafe + put _prismatic_lower_limit(pJoint) into tR + end unsafe + return tR +end handler +public handler b2PrismaticUpperLimit(in pJoint as Integer) returns Number + variable tR as Number + unsafe + put _prismatic_upper_limit(pJoint) into tR + end unsafe + return tR +end handler +public handler b2PrismaticIsMotorEnabled(in pJoint as Integer) returns Boolean + variable tR as Integer + unsafe + put _prismatic_is_motor_enabled(pJoint) into tR + end unsafe + return tR is 1 +end handler +public handler b2PrismaticSetMotorSpeed(in pJoint as Integer, in pSpeed as Number) + unsafe + _prismatic_set_motor_speed(pJoint, pSpeed) + end unsafe +end handler +public handler b2PrismaticMotorSpeed(in pJoint as Integer) returns Number + variable tR as Number + unsafe + put _prismatic_motor_speed(pJoint) into tR + end unsafe + return tR +end handler +public handler b2PrismaticMotorForce(in pJoint as Integer) returns Number + variable tR as Number + unsafe + put _prismatic_motor_force(pJoint) into tR + end unsafe + return tR +end handler +public handler b2PrismaticMaxMotorForce(in pJoint as Integer) returns Number + variable tR as Number + unsafe + put _prismatic_max_motor_force(pJoint) into tR + end unsafe + return tR +end handler +public handler b2PrismaticSpeed(in pJoint as Integer) returns Number + variable tR as Number + unsafe + put _prismatic_speed(pJoint) into tR + end unsafe + return tR +end handler + +-- ---- joints: distance granular ---- +public handler b2DistanceIsSpringEnabled(in pJoint as Integer) returns Boolean + variable tR as Integer + unsafe + put _distance_is_spring_enabled(pJoint) into tR + end unsafe + return tR is 1 +end handler +public handler b2DistanceSpringHertz(in pJoint as Integer) returns Number + variable tR as Number + unsafe + put _distance_spring_hertz(pJoint) into tR + end unsafe + return tR +end handler +public handler b2DistanceSpringDamping(in pJoint as Integer) returns Number + variable tR as Number + unsafe + put _distance_spring_damping(pJoint) into tR + end unsafe + return tR +end handler +public handler b2DistanceIsLimitEnabled(in pJoint as Integer) returns Boolean + variable tR as Integer + unsafe + put _distance_is_limit_enabled(pJoint) into tR + end unsafe + return tR is 1 +end handler +public handler b2DistanceMinLength(in pJoint as Integer) returns Number + variable tR as Number + unsafe + put _distance_min_length(pJoint) into tR + end unsafe + return tR +end handler +public handler b2DistanceMaxLength(in pJoint as Integer) returns Number + variable tR as Number + unsafe + put _distance_max_length(pJoint) into tR + end unsafe + return tR +end handler +public handler b2DistanceCurrentLength(in pJoint as Integer) returns Number + variable tR as Number + unsafe + put _distance_current_length(pJoint) into tR + end unsafe + return tR +end handler +public handler b2DistanceEnableMotor(in pJoint as Integer, in pFlag as Boolean) + unsafe + _distance_enable_motor(pJoint, bi(pFlag)) + end unsafe +end handler +public handler b2DistanceIsMotorEnabled(in pJoint as Integer) returns Boolean + variable tR as Integer + unsafe + put _distance_is_motor_enabled(pJoint) into tR + end unsafe + return tR is 1 +end handler +public handler b2DistanceSetMotorSpeed(in pJoint as Integer, in pSpeed as Number) + unsafe + _distance_set_motor_speed(pJoint, pSpeed) + end unsafe +end handler +public handler b2DistanceMotorSpeed(in pJoint as Integer) returns Number + variable tR as Number + unsafe + put _distance_motor_speed(pJoint) into tR + end unsafe + return tR +end handler +public handler b2DistanceSetMaxMotorForce(in pJoint as Integer, in pForce as Number) + unsafe + _distance_set_max_motor_force(pJoint, pForce) + end unsafe +end handler +public handler b2DistanceMaxMotorForce(in pJoint as Integer) returns Number + variable tR as Number + unsafe + put _distance_max_motor_force(pJoint) into tR + end unsafe + return tR +end handler +public handler b2DistanceMotorForce(in pJoint as Integer) returns Number + variable tR as Number + unsafe + put _distance_motor_force(pJoint) into tR + end unsafe + return tR +end handler + +-- ---- joints: weld ---- +public handler b2WeldReferenceAngle(in pJoint as Integer) returns Number + variable tR as Number + unsafe + put _weld_reference_angle(pJoint) into tR + end unsafe + return tR +end handler +public handler b2WeldSetReferenceAngle(in pJoint as Integer, in pAngle as Number) + unsafe + _weld_set_reference_angle(pJoint, pAngle) + end unsafe +end handler +public handler b2WeldLinearHertz(in pJoint as Integer) returns Number + variable tR as Number + unsafe + put _weld_linear_hertz(pJoint) into tR + end unsafe + return tR +end handler +public handler b2WeldLinearDamping(in pJoint as Integer) returns Number + variable tR as Number + unsafe + put _weld_linear_damping(pJoint) into tR + end unsafe + return tR +end handler +public handler b2WeldAngularHertz(in pJoint as Integer) returns Number + variable tR as Number + unsafe + put _weld_angular_hertz(pJoint) into tR + end unsafe + return tR +end handler +public handler b2WeldAngularDamping(in pJoint as Integer) returns Number + variable tR as Number + unsafe + put _weld_angular_damping(pJoint) into tR + end unsafe + return tR +end handler + +-- ---- joints: wheel granular ---- +public handler b2WheelIsSpringEnabled(in pJoint as Integer) returns Boolean + variable tR as Integer + unsafe + put _wheel_is_spring_enabled(pJoint) into tR + end unsafe + return tR is 1 +end handler +public handler b2WheelSpringHertz(in pJoint as Integer) returns Number + variable tR as Number + unsafe + put _wheel_spring_hertz(pJoint) into tR + end unsafe + return tR +end handler +public handler b2WheelSpringDamping(in pJoint as Integer) returns Number + variable tR as Number + unsafe + put _wheel_spring_damping(pJoint) into tR + end unsafe + return tR +end handler +public handler b2WheelEnableLimit(in pJoint as Integer, in pFlag as Boolean) + unsafe + _wheel_enable_limit(pJoint, bi(pFlag)) + end unsafe +end handler +public handler b2WheelIsLimitEnabled(in pJoint as Integer) returns Boolean + variable tR as Integer + unsafe + put _wheel_is_limit_enabled(pJoint) into tR + end unsafe + return tR is 1 +end handler +public handler b2WheelSetLimits(in pJoint as Integer, in pLower as Number, in pUpper as Number) + unsafe + _wheel_set_limits(pJoint, pLower, pUpper) + end unsafe +end handler +public handler b2WheelLowerLimit(in pJoint as Integer) returns Number + variable tR as Number + unsafe + put _wheel_lower_limit(pJoint) into tR + end unsafe + return tR +end handler +public handler b2WheelUpperLimit(in pJoint as Integer) returns Number + variable tR as Number + unsafe + put _wheel_upper_limit(pJoint) into tR + end unsafe + return tR +end handler +public handler b2WheelIsMotorEnabled(in pJoint as Integer) returns Boolean + variable tR as Integer + unsafe + put _wheel_is_motor_enabled(pJoint) into tR + end unsafe + return tR is 1 +end handler +public handler b2WheelMotorSpeed(in pJoint as Integer) returns Number + variable tR as Number + unsafe + put _wheel_motor_speed(pJoint) into tR + end unsafe + return tR +end handler +public handler b2WheelMotorTorque(in pJoint as Integer) returns Number + variable tR as Number + unsafe + put _wheel_motor_torque(pJoint) into tR + end unsafe + return tR +end handler +public handler b2WheelMaxMotorTorque(in pJoint as Integer) returns Number + variable tR as Number + unsafe + put _wheel_max_motor_torque(pJoint) into tR + end unsafe + return tR +end handler + +-- ---- joints: mouse granular ---- +public handler b2MouseTargetX(in pJoint as Integer) returns Number + variable tR as Number + unsafe + put _mouse_target_x(pJoint) into tR + end unsafe + return tR +end handler +public handler b2MouseTargetY(in pJoint as Integer) returns Number + variable tR as Number + unsafe + put _mouse_target_y(pJoint) into tR + end unsafe + return tR +end handler +public handler b2MouseSetSpringHertz(in pJoint as Integer, in pHertz as Number) + unsafe + _mouse_set_spring_hertz(pJoint, pHertz) + end unsafe +end handler +public handler b2MouseSpringHertz(in pJoint as Integer) returns Number + variable tR as Number + unsafe + put _mouse_spring_hertz(pJoint) into tR + end unsafe + return tR +end handler +public handler b2MouseSetSpringDamping(in pJoint as Integer, in pDamping as Number) + unsafe + _mouse_set_spring_damping(pJoint, pDamping) + end unsafe +end handler +public handler b2MouseSpringDamping(in pJoint as Integer) returns Number + variable tR as Number + unsafe + put _mouse_spring_damping(pJoint) into tR + end unsafe + return tR +end handler +public handler b2MouseSetMaxForce(in pJoint as Integer, in pForce as Number) + unsafe + _mouse_set_max_force(pJoint, pForce) + end unsafe +end handler +public handler b2MouseMaxForce(in pJoint as Integer) returns Number + variable tR as Number + unsafe + put _mouse_max_force(pJoint) into tR + end unsafe + return tR +end handler + +-- ---- world queries (overlap / ray-cast-all / shape-cast); results 1-based ---- +public handler b2OverlapAABB(in pWorld as Integer, in pX1 as Number, in pY1 as Number, in pX2 as Number, in pY2 as Number) returns Integer + variable tR as Integer + unsafe + put _query_overlap_aabb(pWorld, pX1, pY1, pX2, pY2) into tR + end unsafe + return tR +end handler +public handler b2OverlapPoint(in pWorld as Integer, in pX as Number, in pY as Number) returns Integer + variable tR as Integer + unsafe + put _query_overlap_point(pWorld, pX, pY) into tR + end unsafe + return tR +end handler +public handler b2OverlapCircle(in pWorld as Integer, in pCx as Number, in pCy as Number, in pRadius as Number) returns Integer + variable tR as Integer + unsafe + put _query_overlap_circle(pWorld, pCx, pCy, pRadius) into tR + end unsafe + return tR +end handler +public handler b2OverlapShape(in pWorld as Integer, in pRadius as Number) returns Integer + variable tR as Integer + unsafe + put _query_overlap_shape(pWorld, pRadius) into tR + end unsafe + return tR +end handler +public handler b2RayCastAll(in pWorld as Integer, in pX1 as Number, in pY1 as Number, in pX2 as Number, in pY2 as Number) returns Integer + variable tR as Integer + unsafe + put _query_raycast_all(pWorld, pX1, pY1, pX2, pY2) into tR + end unsafe + return tR +end handler +public handler b2ShapeCast(in pWorld as Integer, in pRadius as Number, in pDx as Number, in pDy as Number) returns Integer + variable tR as Integer + unsafe + put _query_shapecast(pWorld, pRadius, pDx, pDy) into tR + end unsafe + return tR +end handler +public handler b2QueryCount() returns Integer + variable tR as Integer + unsafe + put _query_count() into tR + end unsafe + return tR +end handler +public handler b2QueryBody(in pIndex as Integer) returns Integer + variable tR as Integer + unsafe + put _query_body(pIndex - 1) into tR + end unsafe + return tR +end handler +public handler b2QueryShape(in pIndex as Integer) returns Integer + variable tR as Integer + unsafe + put _query_shape(pIndex - 1) into tR + end unsafe + return tR +end handler +public handler b2QueryX(in pIndex as Integer) returns Number + variable tR as Number + unsafe + put _query_x(pIndex - 1) into tR + end unsafe + return tR +end handler +public handler b2QueryY(in pIndex as Integer) returns Number + variable tR as Number + unsafe + put _query_y(pIndex - 1) into tR + end unsafe + return tR +end handler +public handler b2QueryNormalX(in pIndex as Integer) returns Number + variable tR as Number + unsafe + put _query_normal_x(pIndex - 1) into tR + end unsafe + return tR +end handler +public handler b2QueryNormalY(in pIndex as Integer) returns Number + variable tR as Number + unsafe + put _query_normal_y(pIndex - 1) into tR + end unsafe + return tR +end handler +public handler b2QueryFraction(in pIndex as Integer) returns Number + variable tR as Number + unsafe + put _query_fraction(pIndex - 1) into tR + end unsafe + return tR +end handler + end library diff --git a/tests/smoke_test.c b/tests/smoke_test.c index 1ead249..14343bb 100644 --- a/tests/smoke_test.c +++ b/tests/smoke_test.c @@ -42,6 +42,35 @@ extern int b2lc_contact_begin_count(void); extern int b2lc_contact_begin_a(int); extern int b2lc_contact_begin_b(int); +/* ---- ABI v3 entry points exercised below ---- */ +extern void b2lc_shapedef_set_sensor(int); +extern void b2lc_shapedef_set_enable_sensor_events(int); +extern void b2lc_shapedef_set_filter(double, double, int); +extern int b2lc_sensors_update(int); +extern int b2lc_sensor_begin_count(void); +extern int b2lc_sensor_begin_sensor(int); +extern int b2lc_sensor_begin_visitor(int); +extern int b2lc_joint_motor(int, int, int, double, double, double, double, double, double, int); +extern int b2lc_query_raycast_all(int, double, double, double, double); +extern int b2lc_query_count(void); +extern int b2lc_query_body(int); +extern double b2lc_query_fraction(int); +extern void b2lc_world_explode(int, double, double, double, double, double); +extern int b2lc_bodies_update(int); +extern int b2lc_body_move_count(void); +extern int b2lc_body_move_body(int); +extern double b2lc_body_move_y(int); +extern void b2lc_body_mass_data_update(int); +extern double b2lc_md_mass(void); +extern void b2lc_body_set_mass_data(int, double, double, double, double); +extern void b2lc_body_apply_mass_from_shapes(int); +extern void b2lc_chain_begin(void); +extern void b2lc_chain_add_point(double, double); +extern int b2lc_chain_create(int, int, double, double); +extern int b2lc_chain_segment_count(int); +extern int b2lc_shape_add_circle(int, double, double, double, double, double, double); +extern double b2lc_body_vx(int); + static int g_pass = 0, g_fail = 0; static void check(const char *name, int ok) { printf(" [%s] %s\n", ok ? "PASS" : "FAIL", name); @@ -50,7 +79,7 @@ static void check(const char *name, int ok) { int main(void) { printf("box2dxt ABI version = %d\n", b2lc_abi_version()); - check("ABI version is 2", b2lc_abi_version() == 2); + check("ABI version is 3", b2lc_abi_version() == 3); int w = b2lc_world_create(0.0, -10.0, 1, 1); /* gravity, sleep + CCD on */ check("world handle valid", w > 0); @@ -138,6 +167,121 @@ int main(void) { b2lc_world_destroy(w); check("world destroyed cleanly", 1); + /* ================= ABI v3 features (isolated world) ================= */ + int w2 = b2lc_world_create(0.0, -10.0, 1, 1); + check("v3 world created", w2 > 0); + + /* sensors: a static sensor box; a dynamic circle falls THROUGH it and + triggers a begin-touch sensor event (events need BOTH shapes enabled). */ + int senBody = b2lc_body_create(w2, 0, 0.0, 0.0, 0.0, 0, 0); + b2lc_shapedef_set_sensor(1); + b2lc_shapedef_set_enable_sensor_events(1); + int sensorShape = b2lc_shape_add_box(senBody, 1.0, 0.2, 0.0, 0.0, 0.0); + check("sensor shape created", sensorShape > 0); + int visitor = b2lc_body_create(w2, 2, 0.0, 3.0, 0.0, 0, 0); + b2lc_shapedef_set_enable_sensor_events(1); + b2lc_shape_add_circle(visitor, 0.0, 0.0, 0.25, 1.0, 0.0, 0.0); + int sawSensor = 0; + for (int i = 0; i < 120; i++) { + b2lc_world_step(w2, 1.0 / 60.0, 4); + int sn = b2lc_sensors_update(w2); + for (int k = 0; k < sn; k++) + if (b2lc_sensor_begin_sensor(k) == sensorShape && b2lc_sensor_begin_visitor(k) > 0) sawSensor = 1; + } + check("sensor begin-touch fires when a body enters the sensor", sawSensor == 1); + + /* collision filtering: two boxes at the same spot with mutually-exclusive + category/mask never generate a contact between them. */ + int fA = b2lc_body_create(w2, 2, 5.0, 1.0, 0.0, 0, 1); + b2lc_shapedef_set_filter(1.0, 1.0, 0); + b2lc_shape_add_box(fA, 0.5, 0.5, 1.0, 0.3, 0.0); + int fB = b2lc_body_create(w2, 2, 5.0, 1.0, 0.0, 0, 1); + b2lc_shapedef_set_filter(2.0, 2.0, 0); + b2lc_shape_add_box(fB, 0.5, 0.5, 1.0, 0.3, 0.0); + int filteredContact = 0; + for (int i = 0; i < 60; i++) { + b2lc_world_step(w2, 1.0 / 60.0, 4); + int cn = b2lc_contacts_update(w2); + for (int k = 0; k < cn; k++) { + int a = b2lc_contact_begin_a(k), bb = b2lc_contact_begin_b(k); + if ((a == fA && bb == fB) || (a == fB && bb == fA)) filteredContact = 1; + } + } + check("filtered boxes never collide (category/mask)", filteredContact == 0); + + /* chain ground (points ordered right-to-left so the solid side faces up) + catches a falling box; an open 4-point chain has 3 segments. */ + int chainBody = b2lc_body_create(w2, 0, 0.0, -5.0, 0.0, 0, 0); + b2lc_chain_begin(); + b2lc_chain_add_point(12.0, 0.0); + b2lc_chain_add_point(7.0, 0.0); + b2lc_chain_add_point(3.0, 0.0); + b2lc_chain_add_point(-3.0, 0.0); + b2lc_chain_add_point(-7.0, 0.0); + b2lc_chain_add_point(-12.0, 0.0); + int chain = b2lc_chain_create(chainBody, 0, 0.8, 0.0); + check("chain created", chain > 0); + /* a non-loop chain treats its first & last points as ghost vertices, so an + n-point open chain yields n-3 collidable segments (6 -> 3). */ + check("open 6-point chain has 3 collidable segments", b2lc_chain_segment_count(chain) == 3); + int chainBox = b2lc_body_create(w2, 2, 0.0, -2.0, 0.0, 0, 0); + b2lc_shape_add_box(chainBox, 0.5, 0.5, 1.0, 0.5, 0.0); + for (int i = 0; i < 180; i++) b2lc_world_step(w2, 1.0 / 60.0, 4); + check("box dropped on a chain doesn't fall through", b2lc_body_y(chainBox) > -6.0); + + /* motor joint drives a dynamic body toward a linear offset from an anchor */ + int mAnchor = b2lc_body_create(w2, 0, -20.0, 0.0, 0.0, 0, 0); + int mBody = b2lc_body_create(w2, 2, -20.0, 0.0, 0.0, 0, 1); + b2lc_shape_add_box(mBody, 0.3, 0.3, 1.0, 0.3, 0.0); + int motor = b2lc_joint_motor(w2, mAnchor, mBody, 3.0, 0.0, 0.0, 10000.0, 10000.0, 0.3, 0); + check("motor joint created", motor > 0); + double mx0 = b2lc_body_x(mBody); + for (int i = 0; i < 120; i++) b2lc_world_step(w2, 1.0 / 60.0, 4); + check("motor joint moved body toward its linear offset", b2lc_body_x(mBody) > mx0 + 1.0); + + /* ray-cast-all returns every shape along the ray, sorted near->far */ + int rc1 = b2lc_body_create(w2, 0, 30.0, 1.0, 0.0, 0, 0); b2lc_shape_add_box(rc1, 0.5, 0.5, 0.0, 0.0, 0.0); + int rc2 = b2lc_body_create(w2, 0, 30.0, 3.0, 0.0, 0, 0); b2lc_shape_add_box(rc2, 0.5, 0.5, 0.0, 0.0, 0.0); + int rc3 = b2lc_body_create(w2, 0, 30.0, 5.0, 0.0, 0, 0); b2lc_shape_add_box(rc3, 0.5, 0.5, 0.0, 0.0, 0.0); + int nhits = b2lc_query_raycast_all(w2, 30.0, -1.0, 30.0, 7.0); + check("ray-cast-all hit all three stacked boxes", nhits == 3); + int sorted = 1; + for (int k = 1; k < nhits; k++) if (b2lc_query_fraction(k) < b2lc_query_fraction(k - 1)) sorted = 0; + check("ray-cast-all hits are sorted by fraction", sorted == 1); + + /* native explosion scatters nearby dynamic bodies outward */ + int ex1 = b2lc_body_create(w2, 2, 49.0, 0.0, 0.0, 0, 0); b2lc_shape_add_circle(ex1, 0, 0, 0.3, 1.0, 0.3, 0.0); + int ex2 = b2lc_body_create(w2, 2, 51.0, 0.0, 0.0, 0, 0); b2lc_shape_add_circle(ex2, 0, 0, 0.3, 1.0, 0.3, 0.0); + b2lc_world_explode(w2, 50.0, 0.0, 5.0, 1.0, 20.0); + double ex1x0 = b2lc_body_x(ex1), ex2x0 = b2lc_body_x(ex2); + for (int i = 0; i < 10; i++) b2lc_world_step(w2, 1.0 / 60.0, 4); + check("explosion pushes the left body further left", b2lc_body_x(ex1) < ex1x0); + check("explosion pushes the right body further right", b2lc_body_x(ex2) > ex2x0); + + /* body-move events report the bodies that moved this step */ + int mv = b2lc_body_create(w2, 2, 70.0, 10.0, 0.0, 0, 0); + b2lc_shape_add_box(mv, 0.5, 0.5, 1.0, 0.3, 0.0); + b2lc_world_step(w2, 1.0 / 60.0, 4); + int mvcount = b2lc_bodies_update(w2); + int sawMove = 0; + for (int k = 0; k < mvcount; k++) if (b2lc_body_move_body(k) == mv) sawMove = 1; + check("body-move events report a falling body", sawMove == 1); + + /* mass data: read computed, set explicit, then recompute from shapes */ + int massBody = b2lc_body_create(w2, 2, 80.0, 0.0, 0.0, 0, 0); + b2lc_shape_add_box(massBody, 0.5, 0.5, 1.0, 0.3, 0.0); + b2lc_body_mass_data_update(massBody); + check("mass-data update reads ~1kg", fabs(b2lc_md_mass() - 1.0) < 0.05); + b2lc_body_set_mass_data(massBody, 5.0, 0.0, 0.0, 1.0); + b2lc_body_mass_data_update(massBody); + check("set-mass-data sticks", fabs(b2lc_md_mass() - 5.0) < 0.01); + b2lc_body_apply_mass_from_shapes(massBody); + b2lc_body_mass_data_update(massBody); + check("apply-mass-from-shapes restores computed mass", fabs(b2lc_md_mass() - 1.0) < 0.05); + + b2lc_world_destroy(w2); + check("v3 world destroyed cleanly", 1); + printf("\n==== %d passed, %d failed ====\n", g_pass, g_fail); return g_fail == 0 ? 0 : 1; }