04-03-2019, 06:20 AM
Hooray, it's awesome!
...but there are things that could definitely be improved, just to move away from Particle Flow's dismal legacy, and those improvements would probably break a lot of users' setups, so I'm going to get into them ASAP.
...but there are things that could definitely be improved, just to move away from Particle Flow's dismal legacy, and those improvements would probably break a lot of users' setups, so I'm going to get into them ASAP.
- There doesn't seem to be a dt. There's "framestep", but for anything like a proper simulation workflow it's imperative that the only unit of time is the second, and that simulation timestep can be decoupled from your framerate. It follows that time-related values like velocities and accelerations and birth-rates should always be in units per second, not per frame.
- Is the integration scheme at least semi-implicit Euler, ie: symplectic? Many effects have a significantly different result (and not just in terms of accuracy) if you change the timestep, which is undesirable, because we should only be changing the timestep when troubleshooting.
- It seems like tyFlow doesn't store acceleration per particle. Does this mean that velocity is always modified directly? That's going to lead to a lot of accuracy and stability problems, and it'll make it harder to parallelise any interactions between particles, or guarantee deterministic output. It's not necessary to cache acceleration, but it's necessary to have an acceleration channel, and for forces to accumulate there before being added to velocity. Velocities in sims can be interdependent, but accelerations generally cannot, so if everything goes into acceleration before velocity, less can go wrong.
- In the Slow operator, you have "slow by surface proximity". You also have a "follow" option in the Surface Force operator. These should probably be combined, because most of the time, we'll want surfaces to drag particles around (including stopping them), and combining the two manually doesn't quite work as expected.
- There's no air resistance? The Slow operator doesn't take air-speed or a particle's shape into account, so we can't do leaves or snow or cloth realistically, without scripting it every time.
- There should be a global Air thing, because we've never had air before, and this is the opportunity to make the best air in the industry. So instead of wind as a force, instead of turbulence as a force, as an alternative to the Slow operator, you should be able to pipe any & all of those things (Surface Force, Point Force, legacy spacewarps etc.) into an Air object, which represents the air everything is moving through and can have its velocity sampled anywhere. At any given point in space the Air would return an absolute velocity (the sum or a composite of everything fed into it), but unless you set it to water-like density, it wouldn't have a lot of mass to push things around. Then events could have an Airforce operator that just samples the global Air object and responds appropriately, taking per-particle mass into account, and an approximation (bbox, convex hull etc.) of the particle shape. This would basically replace all other wind-like forces for users wanting realism.
- Anywhere you have an "attraction parameter" with a falloff, you should also have a "repulsion" parameter with a separate falloff. That allows particles to stick to things (including each other) without just smashing into them, so you can make compressible gases, bouncy force-fields etc., while only calculating the neighbourhoods etc. once per frame.
- Anywhere you have a falloff, there should be a way to specify the falloff curve. Often, a cubic or similar sigmoid is preferable to inverse square.
- Display of bindings etc. and DrawMarker in scripts show the particle position (ie: GetPos()) lagging behind the particle mesh. That's confusing to look at and it's not much fun having to compensate for it every time we use DrawMarker.
- The UI has a mixture of spinners using percentages (0% to 100%) and spinners using floats (0.0 to 1.0). For example in Particle Physics, stiffness is in the range of 0.0 to 1.0 but friction is a percentage. Could we just lose all the percentage spinners and have spinners that go from 0.0 to 1.0?
- The UI has a mixture of Max units (like inches or centimetres) and plain old floats, where they should only be plain old floats. For example, if the scene is set to use imperial measurements, the Particle Bind operator's mass multiplier is specified in inches, which makes no sense.
- Anything that has "variation" needs a distribution function. For most uses, an exponent would be sufficient (eg: lerp(min_val, max_val, powf(GetRandFloat(sInx, 0.0f, 1.0f), exponent))), but there would probably be cases where users would want a custom distribution curve. This is scriptable but it'd mean having to script just about everything that initialises a particle's size, colour, velocity etc. and that'd get unwieldy.
- Interparticle friction in Particle Physics has been implemented in a way that doesn't preserve angular momentum. This means lumps of viscous stuff can't tumble! The trick to proper interparticle friction is to make sure it only applies along the vector between the two particle positions, by scaling it with the dot product of (vel2 - vel1) and (pos2 - pos1) so it has no effect at glancing angles.
- Particle Physics in general seems to be badly affected by not storing acceleration separately from velocity. With no other forces acting on it, a particle system should produce no net motion - but with friction enabled, I guess you're modifying local velocity while you're reading neighbour velocities, because it adds up to a small, spurious net motion. That's how you end up with a non-deterministic system that doesn't conserve momentum.
- Case-insensitive scripting would be very much appreciated, or just plain lower-case aliases for all the keywords. Who's going to remember that "Invert()" is capitalised but "inverted" is not? Or that "hitPoint" is lower camelcase but "OrigLength" is upper camelcase, and "rest_length" uses underscore instead? Or that "id" means "ID"?
- The Script operator would benefit from some more conventional versions of vector & matrix methods, like cross(a,b) instead of Point3.Cross(a,b), because users will want to paste in code from other APIs etc. where vector types are native, like OpenCL, OpenGL, GLM, CUDA C and so on.
- I don't know how you're solving springs & constraints, but to make them thread-safe, you either need to store an accumulated force or displacement in the constraint itself, which you apply to the particles in another loop. A lot of the literature on constraint solvers says it's okay to just let constraints iteratively modify particle positions or velocities directly, but that doesn't work in a predictable way if you parallelise the solver, which is why you have to accumulate & apply, instead.
- Dual quaternions could be a more efficient way of storing particle transforms. Matrices and scales and rotations work, too, but dualquat transforms are inherently rigid (orthogonal etc.), and they only require 8 floats each.