Closed bjornbytes closed 6 months ago
lovr.physics.newWorld
! I think it's there because LÖVE did it that way, but 95% of the time I don't care about gravity and just want to use the default of -9.81 or whatever. And it's the first thing you have to say when you create a world, which is annoying when you're usually interested in setting the tag list. I'd rather just call :setGravity
after making the world.World:update
callback function, World:computeOverlaps
, World:overlaps
, and World:collide
. This currently serves as A) collision filtering and B) collision detection, but it's super awkward. Jolt has callbacks for ignoring contacts and detecting when they're added/persisted/removed. This seems better.World:raycast
takes an origin and an endpoint, not an origin and a direction which might be more intuitive?World:get/setResponseTime
World:get/setTightness
World:get/setLinearDamping
World:get/setAngularDamping
World:is/setSleepingAllowed
Collider:setLinear/AngularDamping
When an API-friendly but incomplete physics engine is put in front of me versus an API-unfriendly but fully-featured physics engine, I'd rather use the one that's fully functional but API-unfriendly. Because the former can directly complete the task, while the latter, although good-looking, cannot complete many tasks.
On love2d, it can basically achieve what box2d can do, at least the content in the box2d document can be achieved. I just hope that lovr can also try its best to output the complete functions of the physics engine, instead of just making the API simple and beautiful.
@xiejiangzhi I also don't want a toy engine. Jolt is still growing and we will add more features along the way. We should still take this opportunity to remove some of accumulated complexity to make the API more orthogonal and tidier.
@bjornbytes
MotionProperties.SetInverseInertia
.While we are changing the API, we should also adapt the querying. The jolt integration supports full shape casting (any shape, shape moving through the space).
I would suggest to keep just one raycasting function, having three is confusing.
The biggest hurdle is multiple shapes per body. Jolt supports them through compound shape. We have few options:
Collider:addShape
API, with one compound shape to each colliderCompoundShape:addShape
in lovr API; rework the Collider APISometime in the future we could add more features:
- The inertia seems to be doable through the
MotionProperties.SetInverseInertia
.
Thanks, didn't know about this.
While we are changing the API, we should also adapt the querying. The jolt integration supports full shape casting (any shape, shape moving through the space).
Yes, would definitely like shape querying and shape casting!
I would suggest to keep just one raycasting function, having three is confusing.
They're pretty convenient and efficient when you're just trying to find the closest shape, which is common. And it would be good to have some way to hook into Jolt's "early out" params for casts, though I haven't wrapped my head around them yet...
The biggest hurdle is multiple shapes per body. Jolt supports them through compound shape.
Yeah this seems complicated and important. Another related issue I noticed is that the Jolt shapes are immutable (can't change radius/size/etc. of shapes once they're created).
There's been some discussion in chat (1, 2) around how multiple shapes is kind of annoying. Merging shapes and colliders somehow would be pretty cool, gotta think about it more.
CompoundShape seems good...it might require you to do if shape:type() == 'CompoundShape'
and use recursion in places (e.g. drawing colliders in phywire), but that's probably not a dealbreaker.
I came up with a few more small things while reading through the PR today:
So far I'm kinda leaning away from the CompoundShape idea as well as the merged BoxCollider/SphereCollider/CompoundCollider/etc. object.
For CompoundCollider
you still need a way to add/remove shapes and get/set their properties, and Shape objects are best for that. And if you're going to have Shape objects anyway, you may as well separate them from the Collider object so that all the methods are in one place, you can reassign a Shape to a collider, etc.
For CompoundShape
, it's nothing more than a list of shapes with per-shape transforms. I think it's friendlier for LÖVR to manage this for you internally, rather than having this weird "shape that's not a shape" object. So basically, current API is pretty good... Unless I just have stockholm syndrome or something.
The implementation for Jolt is kind of annoying. We would have to switch between CompoundShape depending on number of attached shapes, maybe recreate shapes or add "decorator" shapes if some of their properties get modified. Are there any reasons it couldn't work though?
Yes, we can continue with the current API around Colliders and Shapes. We can also test using the CompoundShape even for one-shape colliders and see if we lose any performance. It would simplify things. But the switching between shape primitives and compound shapes is also doable.
Re:
Back to raycasting for a moment. I said before that the physics_jolt.c doesn't stop at endpoint but taking another look at Jolt's side, the ray takes into account the direction vector length and correctly stops at endpoint. I believe lovr always uses the term "direction" as a normalized vector (I found Quat(Vec3)
, Curve:getTangent()
, World:getLocal/WorldVector()
). Using "direction" for directed distance vector in this case would be a bit confusing IMO. I would prefer to use the "endpoint" for both the raycast and spherecast because it better captures the intention.
All cast functions should also accept an optional tag
parameter, currently only two variants have it. The result of raycastAny
is not deterministic, it would have to be quite a bit faster than raycastClosest
to earn its place.
The returning from casting early feature is a tad too complicated for me, with the C++ -> C -> Lua callback -> C -> C++ roundtrip. I would rather we collect all results and instead provide the guarantee to users that they are sorted. We can still limit the number of results with tags and the short casting distance, and the raycastClosest
variant. What do you think?
From chat:
World:queryTriangle
, which was proposed before in #689Here's my up-to-date list:
CompoundShape
or otherwise supporting multiple shapes per collider in Jolt (#762)lovr.physics.newWorld
takes options table or just the tag listWorld:get/setResponseTime
and World:get/setTightness
(and joint methods for it)World:get/setLinearDamping
and World:get/setAngularDamping
(maybe)World:getTagMask
?)World:raycastAny
World:update
instead of separate accessor (maybe)Collider:get/setMotionQuality
(discrete vs. continuous collision checking)Collider:applyLinearImpulse/applyAngularimpulse
I did a deeper dive on queries and came up with the following design:
With args:
Notes:
filter
, which can be a single tag string or a number representing a bitmask of tags (World:getTagMask(...tags)
will return one for you)World:raycast
defaults to closest hit now, that seems like the most common useWorld:raycastAny
...I think I'm the only one that likes it but it's nice to have the option to early-exitWorld:collidePoint
, this test is cheaper than a 0-direction raycast (esp against e.g. ConvexHull)World:shapecast
/World:raycast
or World:castRay
/World:castShape
. Second one is better but I really like the way "raycast" looks for some reason.World:queryBox
and World:querySphere
methods are going to be broadphase queries -- they do quick checks to see if collider bounding boxes overlap the shape. This can be used to quickly exit before doing a more expensive test.
World:querySphere(light.position, light.radius, 'dynamic')
check to see if it needs to be regenerated.Shape:raycast
and Shape:collidePoint
are nice if you just need collision and don't need a full physics simulation. For example, you could create a MeshShape for a Model and do raycasts to enable drawing on the mesh with a pen.A simpler raycast API would be:
collider, x, y, z, nx, ny, nz, shape = World:raycast(start, end, filter)
World:raycast(start, end, filter, function(collider, x, y, z, nx, ny, nz, shape) end)
true
from the callback to tell the physics engine to early exit.Thought about doing a "hit fraction" system but it's too complicated. It does allow for a "custom closest hit" type thing but I think it's okay to omit this.
This way, we can get away with having only a single raycast function.
Simplifying further (similar to Josip's previous suggestions)
That would leave us with the following for all queries:
World:raycast(origin, direction, filter, callback)
World:shapecast(shape, position, scale, orientation, direction, filter, callback)
World:queryBox(position, size, orientation, filter, callback)
World:querySphere(position, radius, filter, callback)
It's nice and compact, but I hope it's not too confusing to document all the "tricks" that are supported.
From chat: we should add degree of freedom restriction. There's SixDOF joint, but a simpler approach might be the allowedDofs
param of MotionProperties::SetMassProperties
(world space only though).
Example API might be Collider:setLockedAxes([translation], [rotation])
where each one can be strings with "xyz" chars.
Ok pretty much all of the ideas here have been implemented, so I'm going to close this.
With #735 open and high likelihood that LÖVR will switch to Jolt soon, we should start thinking about ways that the Lua API for lovr.physics can be improved! The goals would be to A) make it easier to use, B) make it align better with Jolt. This issue can serve as a place to collect all of the ideas.