feat(physics): a walker (Jolt CharacterVirtual with an inner body) and the verbs a boat and a guest's copy need - force, torque, angular impulse, spin, set_pose
Five new tests: stand and walk at the asked speed, a low stone is a step and a tall one a wall, a jump up and back, a crate shoved aside, a hull turned by a torque and carried by a force. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
b44b88e00e
commit
c7a18fd4f0
12 changed files with 334 additions and 2 deletions
|
|
@ -62,3 +62,26 @@ JPH_SHIM int jph_body_buoyancy(void *w, int id, float sy, float nx, float ny, fl
|
|||
Vec3(cx, 0.0f, cz), ww->sys.GetGravity(), dt);
|
||||
return in ? 1 : 0;
|
||||
}
|
||||
|
||||
// a push kept up over the next step (an oar's stroke, a paddle), and a twist
|
||||
JPH_SHIM void jph_body_force(void *w, int id, float x, float y, float z) {
|
||||
static_cast<World *>(w)->sys.GetBodyInterface().AddForce(bid(id), Vec3(x, y, z));
|
||||
}
|
||||
JPH_SHIM void jph_body_torque(void *w, int id, float x, float y, float z) {
|
||||
static_cast<World *>(w)->sys.GetBodyInterface().AddTorque(bid(id), Vec3(x, y, z));
|
||||
}
|
||||
JPH_SHIM void jph_body_angular_impulse(void *w, int id, float x, float y, float z) {
|
||||
static_cast<World *>(w)->sys.GetBodyInterface().AddAngularImpulse(bid(id), Vec3(x, y, z));
|
||||
}
|
||||
// how fast it turns (x, y, z), into out[3]
|
||||
JPH_SHIM void jph_body_spin(void *w, int id, float *out) {
|
||||
Vec3 a = static_cast<World *>(w)->sys.GetBodyInterface().GetAngularVelocity(bid(id));
|
||||
out[0] = a.GetX(); out[1] = a.GetY(); out[2] = a.GetZ();
|
||||
}
|
||||
// where a body is going, set whole: a guest's copy of the host's body, moved to what the host said
|
||||
JPH_SHIM void jph_body_set_pose(void *w, int id, float x, float y, float z, float qx, float qy, float qz, float qw,
|
||||
float vx, float vy, float vz) {
|
||||
BodyInterface &bi = static_cast<World *>(w)->sys.GetBodyInterface();
|
||||
bi.SetPositionAndRotation(bid(id), RVec3(x, y, z), Quat(qx, qy, qz, qw).Normalized(), EActivation::Activate);
|
||||
bi.SetLinearVelocity(bid(id), Vec3(vx, vy, vz));
|
||||
}
|
||||
|
|
|
|||
87
packages/ludic.physics/native/shim/jph_character.inl
Normal file
87
packages/ludic.physics/native/shim/jph_character.inl
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
// jph_character.inl - a walking body: Jolt's CharacterVirtual, stepped by the game, with an inner
|
||||
// rigid body on the moving layer so dynamic things are shoved and a ray can find it. Its feet are
|
||||
// its origin. The game says how fast it wants to go across and whether it jumps; Jolt does the
|
||||
// rest - gravity, landing, stairs, slopes, sticking to the ground going down.
|
||||
|
||||
struct Walker {
|
||||
CharacterVirtual *c;
|
||||
float step_up;
|
||||
float stick;
|
||||
};
|
||||
|
||||
// r wide, h tall, feet at (x, y, z); a slope steeper than max_slope degrees is not ground
|
||||
JPH_SHIM void *jph_char_new(void *w, float r, float h, float x, float y, float z, float max_slope, float mass) {
|
||||
World *ww = static_cast<World *>(w);
|
||||
float half = (h - 2.0f * r) * 0.5f;
|
||||
if (half < 0.01f) half = 0.01f;
|
||||
RefConst<Shape> cap = CapsuleShapeSettings(half, r).Create().Get();
|
||||
RefConst<Shape> sh = RotatedTranslatedShapeSettings(Vec3(0.0f, half + r, 0.0f), Quat::sIdentity(), cap).Create().Get();
|
||||
CharacterVirtualSettings st;
|
||||
st.mShape = sh;
|
||||
st.mInnerBodyShape = sh;
|
||||
st.mInnerBodyLayer = L_MOVING;
|
||||
st.mMaxSlopeAngle = DegreesToRadians(max_slope);
|
||||
st.mMass = mass;
|
||||
st.mMaxStrength = mass * 9.81f * 2.0f;
|
||||
st.mSupportingVolume = Plane(Vec3::sAxisY(), -r);
|
||||
Walker *wk = new Walker{ new CharacterVirtual(&st, RVec3(x, y, z), Quat::sIdentity(), 0, &ww->sys), 0.55f, 0.5f };
|
||||
return wk;
|
||||
}
|
||||
|
||||
JPH_SHIM void jph_char_free(void *k) {
|
||||
Walker *wk = static_cast<Walker *>(k);
|
||||
delete wk->c;
|
||||
delete wk;
|
||||
}
|
||||
|
||||
// how high a step it takes without a jump, and how far it follows the ground down
|
||||
JPH_SHIM void jph_char_limits(void *k, float step_up, float stick) {
|
||||
Walker *wk = static_cast<Walker *>(k);
|
||||
wk->step_up = step_up;
|
||||
wk->stick = stick;
|
||||
}
|
||||
|
||||
// one step of dt: (vx, vz) the speed it wants across; jump > 0 leaves the ground at that speed up
|
||||
JPH_SHIM int jph_char_step(void *w, void *k, float dt, float vx, float vz, float jump) {
|
||||
World *ww = static_cast<World *>(w);
|
||||
Walker *wk = static_cast<Walker *>(k);
|
||||
CharacterVirtual *c = wk->c;
|
||||
Vec3 g = ww->sys.GetGravity();
|
||||
bool ground = c->GetGroundState() == CharacterBase::EGroundState::OnGround;
|
||||
float vy = c->GetLinearVelocity().GetY() + g.GetY() * dt;
|
||||
Vec3 v(vx, vy, vz);
|
||||
if (ground) {
|
||||
v = Vec3(vx, 0.0f, vz) + c->GetGroundVelocity();
|
||||
if (jump > 0.0f) v.SetY(jump);
|
||||
}
|
||||
c->SetLinearVelocity(v);
|
||||
CharacterVirtual::ExtendedUpdateSettings u;
|
||||
u.mWalkStairsStepUp = Vec3(0.0f, wk->step_up, 0.0f);
|
||||
u.mStickToFloorStepDown = Vec3(0.0f, -wk->stick, 0.0f);
|
||||
IgnoreSingleBodyFilter self(c->GetInnerBodyID());
|
||||
c->ExtendedUpdate(dt, g, u, ww->sys.GetDefaultBroadPhaseLayerFilter(L_MOVING), ww->sys.GetDefaultLayerFilter(L_MOVING),
|
||||
self, {}, ww->temp);
|
||||
return int(c->GetGroundState());
|
||||
}
|
||||
|
||||
// position, velocity, the ground state (0 on ground, 1 too steep, 2 touching but unsupported,
|
||||
// 3 in the air), the ground normal's y, and the inner body's id - into out[9]
|
||||
JPH_SHIM void jph_char_read(void *k, float *out) {
|
||||
CharacterVirtual *c = static_cast<Walker *>(k)->c;
|
||||
RVec3 p = c->GetPosition();
|
||||
Vec3 v = c->GetLinearVelocity();
|
||||
float vals[9] = { float(p.GetX()), float(p.GetY()), float(p.GetZ()), v.GetX(), v.GetY(), v.GetZ(),
|
||||
float(int(c->GetGroundState())), c->GetGroundNormal().GetY(),
|
||||
float(int(c->GetInnerBodyID().GetIndexAndSequenceNumber())) };
|
||||
for (int i = 0; i < 9; i++) out[i] = vals[i];
|
||||
}
|
||||
|
||||
// put it somewhere, standing still (a teleport, a map swap, getting off a boat)
|
||||
JPH_SHIM void jph_char_place(void *w, void *k, float x, float y, float z) {
|
||||
World *ww = static_cast<World *>(w);
|
||||
CharacterVirtual *c = static_cast<Walker *>(k)->c;
|
||||
c->SetPosition(RVec3(x, y, z));
|
||||
c->SetLinearVelocity(Vec3::sZero());
|
||||
IgnoreSingleBodyFilter self(c->GetInnerBodyID());
|
||||
c->RefreshContacts(ww->sys.GetDefaultBroadPhaseLayerFilter(L_MOVING), ww->sys.GetDefaultLayerFilter(L_MOVING), self, {}, ww->temp);
|
||||
}
|
||||
|
|
@ -22,6 +22,8 @@
|
|||
#include <Jolt/Physics/Collision/CollideShape.h>
|
||||
#include <Jolt/Physics/Collision/CollisionCollectorImpl.h>
|
||||
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
||||
#include <Jolt/Physics/Character/CharacterVirtual.h>
|
||||
#include <Jolt/Physics/Body/BodyFilter.h>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <cfloat>
|
||||
|
|
@ -126,3 +128,4 @@ JPH_SHIM int jph_init(void) {
|
|||
#include "jph_shapes.inl"
|
||||
#include "jph_bodies.inl"
|
||||
#include "jph_queries.inl"
|
||||
#include "jph_character.inl"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue