# Metaballs script

• Wow, some serious thought went into that script, uh? Some 6,000 lines of code! A typical programmer can do final tested and deployed 10 lines of code per day, so that was probably work of years!

From what I could figure out by reading the code (I didn't try to run it), it builds physical CR2 and PP3 geometry files (bold!!!) for actors (=body parts), then it builds a figure which I assume consist of several small geometric shapes as these body parts. Then it runs a simulation for the position/rotation of these particles, and keys particles to that.

As far I could find it does no collisions, so that might be a limitation, but the idea of building these particles as body parts (if I'm right about how the thing works) as ingenuous, as it avoids pollution of the timeline.

You think that script works as is in PP11, anomalous? We could detect the figure generated by it and then use the particles as field generators for metaballs; that doesn't seem hard at all.

• Also it's true it does no collisions? I couldn't find that in the code, but how knows. Aren't collisions a desirable thing for particles?

• Hmm... say, what would we do to generate a field of particles? The idea of generating a figure is awesome, but you can really do the same with 100 objects parented to another one.

Say a big box ParticlesField, and 100 (or how many) child small boxes/spheres/sprites Particle_N. Initially all Particle_N are invisible. Then N spheres called Emitter_N, which randomly make some Particle_N visible and assign to their position.

Then add some effects, which are really just invisible props. Say
ConicPusher_N gives some acceleration to particles in some specific direction in a solid angle and range; say BoxPusher_N does the same, but now on a box field (gravity is just a very large box pusher); say SphericPusher_N does the same for a sphere, but now the direction is radial; say Twister_N creates a rotational (= divergent field) around a cylinder. Each one of these set as having an acceleration a, bouncer b, range r, minimum velocity v0 and maximum velocity v1

So a waterfall kind of thingie would just be a few emiters at the top to generate water particles; a BoxPusher_1 on X direction with a=99999, v1=10 to make all particles go orderly in X direction at fixed speed; then at the end of BoxPusher_1 put a BoxPusher_2 on -Y direction with a=1, to make the particles fall down. Then at the bottom of the waterfall put a BoxPusher_3 on +Y direction with b=0.9, r=0.01 so that particles will invert their Y speed at the very bottom and bounce a few times once they get out of the field of BoxPusher_3.

Is that a field of particles? All these equations are realy pretty simple (just newtonian mechanics). I have no clue how these particle simulations work. But I suspect it wouldn't be so simplistic.

Now... if one were to add collisions, then things would become much more interesting...

• Or, better yet, not collisions, but pressure... pressure is just

sum (1/dist(particle_N, this_particle)^2)

that creates a force against the direction of pressure. That's a simple calculation too, and should make the particles more interesting, as they will push against each other and make themselves spread out.

Then add a little friction on the dynamic equations of the particles to make sure particles will not shoot out of the world... hmm...

• Something like

``````make all particles free
for each frame
for each emitter
if random then find a free particle, unfree it and put it in this location
for each particle
forceXYZ = 0
forceXYZ += drag forceXYZ
for each particle
if particle within distance, forceXYZ += backpressureXYZ
for each pusher
for each particle
if particle is within range of pusher
forceXYZ += pusherXYZ
bounceXYZ = values from pusher if set
for each particle
calculate speedXYZ
calculate positionXYZ
``````

It's very simplistic, is it not?

• Oh, something like

``````   if particle has aged, hide it and make it free
``````

• Now, one could also use a Eater_N thingie. If the particle comes close, it eats it, make it invisible and frees it for use in simulation again.

That way, even if that's simplistic, one could make a waterfall with rocks, ending in a river with a whilrpool, like this:

The brown thingies are the emitters, the orange is an eater, the green thingies are box pushers (one with speed limit, one gravity field, several bouncers for the rocks, river bottom and river sides) and one twister for the whirlpool.

Each one of these guys have a simple field equations, but put together they might create something amusing for the user, specially if you put like 10,000 cubes in that simulation.

• Alright, back to Metaballs.

Version 0.02 is available at

Now it's a single script that does it all. All the menu options work now. The tiny cube is changed for a sphere that more closely resembles the actual size of the metaball with strength = 0.5.

Only the spherical field function is implemented yet. Don't try to create other metaball types (cube, etc ...), as these are still under work.

Notice I added an option to the menu - Run Metaball (1 Frame); that will redo the metasurface only at the current frame; I found that to be useful during positioning of the metaballs in the current frame.

• To be sure - testers wanted!!

• @fbs7 Uuuhh, that seems to be the previous version, and doesn't incorporate any of the fixes I sent you for renderer root node and multiple output nodes.

• Let me see if I uploaded the wrong file; I got these files:

``````########################################################################################################
#
# This script creates a dynamic isosurface
# 20171207  fbs                 First version
# 20171219	GeoffIX (an0malaus)	Fixed ShaderTree root node setting for multiple root nodes and ConnectTo()
#								issue for nodes with multiple outputs only connecting the first output.

...
# Check whether root node and renderer root node
if node1.IsRoot():
for rendEngCode in [ poser.kRenderEngineCodeFIREFLY,poser.kRenderEngineCodeSUPERFLY ]:
if node1 == tree1.RendererRootNode( rendEngCode ):
tree2.SetRendererRootNode( rendEngCode,node2 )

...
# Internal and external names seem to always be the same [Except for cycles nodes!]
# So mixing their use
...
# Found input
if nodeInputInput1 is not None:
nodeIIName1 = nodeInputInput1.InternalName()
nodeIOName1 = nodeInputOutput1.InternalName()
nodeInputInput2 = tree2.NodeByInternalName( nodeIIName1 )

# Handle multi-output node connections
for nodeInputOutput2 in nodeInputInput2.Outputs():
if nodeInputOutput2.InternalName() == nodeInputOutput1.InternalName():
nodeInputOutput2.ConnectToInput( nodeInput2 )
break
``````

• Hmm... Google Drive got me the wrong link... the file in the link is indeed old... fixing that... Sorry for that.

• This should be the right one, V0.02:

Apologies

• @fbs7 well, it wouldn't be Google, if you didn't have to search for something ;-)

• I noticed a bug. Sometimes it calculates the first frame incorrectly. It must be something about SetFrame(). Checking that.

• @fbs7 the other thing I probably forgot to mention was to set the isoField and metaball props' "Visible In Render", "Visible In Camera", and "Casts Shadows" attributes to off. Since they're not part of the materials, they won't get copied to the isoSurfaces, and you generally won't want the field or the metaballs visible when rendering. (I like using a superfly volumetric water shader)

which gives me this (before creating the metasurface)

Interestingly, I'm seeing significant offset of the isoSurface from the isoField boundaries when I "Run Metaballs (1 frame)":

And after increasing the bottom right metaball's strength from 0.5 to 1.0, I get:

Is that expected (The surface offset from the field boundaries, I mean)?

That implies (to me) that there's a field gradient within the cube, rather than a sharp cutoff. I'm probably misunderstanding though.

• @fbs7 what if the field grid coordinates are scaled from the centre of the cube by 0.5/resolution? That would mean that the field would display as full strength right up to the boundaries of the cube, but couldn't go beyond it because the strength at (and outside) the field is zero, by definition.

• Hmm... I guess I can add 1 to upper and lower limit of the grid scans, and adjust this block with some ifs for when the +1 vertices are beyond boundary:

``````							gV0 = lattice[nX, nY, nZ]
gV1 = lattice[nX1,nY, nZ]
gV2 = lattice[nX1,nY, nZ1]
gV3 = lattice[nX, nY, nZ1]
gV4 = lattice[nX, nY1,nZ]
gV5 = lattice[nX1,nY1,nZ]
gV6 = lattice[nX1,nY1,nZ1]
gV7 = lattice[nX, nY1,nZ1]
``````

• How about the menu; are these options good or should we change them?

• @fbs7 menus were fine, though I got nothing from the meta-Cone ;-). Haven't tried the Meta-Line or Meta-Surface buttons yet, as I had some "meatSpace" errands to run. The only gripe I'd have is that due to running two monitors, with the MetaBall window dragged onto the secondary monitor (to the left of the main workspace monitor), the Metaball type window always gets created on the primary monitor at a fixed location. I know this is nothing to do with the primary script functionality, but it would be nice if the script could remember positional preferences for the two windows; at the very least within a single session, if not from session to session, which would require manipulating a preference file.

I have a PoserPrefs library module I've developed for saving and loading preferences which I can PM you, if you're interested.