Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Changed List to Array, elm-linear-algebra for vecs, and animationframe for time #1

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

TheSeamau5
Copy link

Hey there,

First of all, I'd like to say that your physics engine is really cool. I hope you don't mind if I hacked into it a little bit.

I made a couple of changes which, at least on my machine (5 yr old macbook pro), have made the example run around twice as fast. Basically, on chrome, I get a consistent 30fps and it takes a while before the fan kicks in.

The changes I've made:

  • Switched from using Lists to using Arrays
  • Switch to elm-linear-algebra for vector math
  • Switch from Time.fps to AnimationFrame.frame

I've noticed that each of these changes made the example run faster (although, you should probably try it yourself) and together the example ran about twice as fast.

I do have to note that in order to switch from using lists to arrays, and since I don't fully understand the internals of the algorithms used (I'm not an expert on physics engines by any means), I did change one small bit.

Basically, if you look at src/BoxesAndBubblesEngine.elm, I changed slightly how the collide function works. Instead of modifying both bodies when resolving the collision, I'm only modifying one of them. That's mainly because of my limitations as a programmer and all the pattern-matching on lists made me go "huh?". Also, in order to get the "tail recursion", I make use of trampolines. So, you'll notice that the collide function calls trampoline. This did make the code easier but perhaps not completely faithful to the original. The simulation does compute different values, but it doesn't seem to be significantly more "right" or more "wrong".

Also, I have to note, that if you switch from Animation.frame to Time.fps, you'll start noticing tunnelling. Small objects will just fall off the ground if they move too fast. I think it has more to do with the fact that collision doesn't look ahead in time for potential future collisions rather than because I switch from updating just one of the bodies rather than both. So, having Animation.frame makes things both faster and hides this artifact in the example. (I consider that a win to some extent... a hack to be sure... but a winning hack nonetheless :D )

Aside from that, note that I haven't changed a single function. I just switched the types of vectors and lists. I tried to make the new code look as much as the old code as possible so as not to confuse you.

I don't know if you're interested in this pull request or even accept them, but I'll just put it here in any case.

Awesome engine btw,
Hassan Hayat

Where appropriate, inside the engine, lists were replaced by arrays and
the previously defined Vec2 type was replaced by elm-linear-algebra's
Vec2.

No algorithmic changes were made to the engine.

Result:

* 2x performance gain for the example
* new instabilities uncovered/introduced by the change

Todo :

* Reduce reliance on pattern matching which causes numerous useless
conversions to tuples and back

* Find cause of stability issues
Example now reaches 30fps regularly.
@jastice
Copy link
Owner

jastice commented Jan 21, 2015

Cool! Thanks for becoming involved. I will probably review and try the changes in the next few days and merge them. If you'd like to work on more extensive improvements I'd be happy to work with you. Thanks for the detailed comments on your changes, too.

I see that the linear algebra package now uses native functions. That probably improves the performance, and is a good case for replacing the included functions.
I'm probably removing the AnimationFrame dependency and just add it as a tip in the documentation, since I want to keep dependencies minimal and it's only used in the example. Good find though, I wasn't aware of it.

I have to say I'm far from an expert on physics engines. This was made from laboriously translating a C++ tutorial into functional style ;)

@TheSeamau5
Copy link
Author

I have to commend you on porting C++ code to Elm. I tried the other day to hook up box2d to Elm because I wanted to try to make a small platformer example and didn't feel like writing a physics engine. I thought that it would be just as simple as writing a native wrapper on top of box2d and call the functions from Elm, but, it turns out that Box2d is inherently too stateful. As in, suddenly my code had nonsense and my variables (which I thought were immutable) became mutable. And, no matter how many tricks I tried playing, Box2d keeps a strong internal state so it just seemed to hard.

Which is why I decided to google around and bumped into your engine. I think it that a physics engine where everything is just immutable values and pure functions is a promising thing. I mean, it becomes trivial to just rewind time or serialize your entire gamestate (including the position of every physics body and particle).

Also, I'm in still in the early learning phase of functional programming and I wonder how far you can push functional programming. Especially with something as computationally demanding as a physics engine.

@TheSeamau5
Copy link
Author

So, I'll take up your offer on collaborating on the project. I think it would be cool if we can get to the point where we have a demo where there's an explosion and particles fly everywhere, and then you hit the pause button, and pull the slider, and the particles fall back in place. I think that that would blow people's minds and we'd be like, "uh...nah...nothing to it. it's super easy"

So, I've been looking around for the kinds of improvements you see in popular physics engines and I think that the following is a list I'm considering pursuing:

  • Change from Array to using something like a Quadtree or some spatial partitioning data structure so that we don't have to test if a body collides with every other body, just with the neighboring bodies.
  • Include velocity in the collision detection in order to consider potential future collisions (to avoid tunnelling)
  • Allow for bodies to "sleep" when they're not moving so as not to make checks on bodies that are at rest needlessly.
  • Change from using "infinite mass" to having a body type which is either Static or Dynamic. This would somewhat help in not doing physics updates needlessly just by checking if the type is static or dynamic and be able to easily switch between the two by just setting a flag (think the ability to have platforms that can move when you solve a puzzle for example)

@jastice
Copy link
Owner

jastice commented Jan 21, 2015

I did consider adapting Box2d, but essentially I figured I'd have a lot of those problems you mentioned.

The functional style makes a lot of sense for physics engines in my opinion. When viewing time as discrete steps, a function of the previous point in time is the natural way to model it for me. I think the code turned out a lot cleaner than the C++ version too :)

Elm as a language is probably not optimized enough yet to do serious physics, but we can probably get at least a usable engine for basic games.

I like your suggestions. Perhaps we can start with adding the missing functionality listed under "it doesn't do", along with the collision prediction, since a lot of it is necessary for use beyond simple animations.

I'm a bit busy the next 2 weeks or so, but lets keep in touch. If you're on Twitter, I am @ebenwert there.

@jastice
Copy link
Owner

jastice commented Jan 22, 2015

Possibly for reference: https://github.com/sebcrozet/nphysics

@TheSeamau5
Copy link
Author

Cool! The library is awesome. I see that they divided up into several libraries (physics and collision detection). I think it would be nice to do the same. I think there are great opportunities for reuse (even with other applications like graphics or just web development).

If I understand well what they're doing in nphysics, they define different constraints (like, the bodies can't interpenetrate and so on...) and then they apply a solver (in nphysics's case, they use Gauss Seidel... I quickly googled around and it seems like it's a standard algorithm used). In theory, again, if I understand all of this correctly, this would allow use different constraints to create different kinds of physical interaction.

something like so:

type alias Constraint = Body -> Body -> Bool

-- this constraint says that two bodies may not interpenetrate
cannotInterpenetrate : Constraint

-- solve takes a list of constraints and two bodies and
-- figures out what those bodies must become given the constraints
solve : List Constraint -> Body -> Body -> (Body, Body)

But, to not get too much ahead of ourselves, I think it'll be great to stea...(cough)...take inspiration from the algorithms used in nphysics. Plus, it's much easier to adapt rust code to c++. It reads way more nicely. I think between nphysics and ncollide, there's a lot of work that could be done. I'm gonna try and hack a few things together and see if that'll work.

But, like you said, first let's start incrementally and add the features that are currently missing under the todo list. I think the big one to add would be to switch from infinite mass to using static vs dynamic body types. This would solve your immovable object vs unstoppable force problem. Then, we can add arbitrary polygons and allow for rotation. I think once we do these things, we'll have a clearer picture on how the engine will evolve

@TheSeamau5
Copy link
Author

Oh, and by the way, I'm also on Twitter @TheSeaMouse. I rarely use it but I do get the notifications, so if you send me a message I'll see it.

@jastice
Copy link
Owner

jastice commented Jan 23, 2015

Oh okay. Do you use any instant messenger?

@david-crespo
Copy link

Any progress here? This package is wonderful by the way.

@jastice
Copy link
Owner

jastice commented Sep 18, 2015

Thanks, David!
Unfortunately this PR had a few little bugs that neither of us got around to fixing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants