Metaballs script



  • Wow, that's pretty neat!!

    A beautiful stack trace, indeed; one could even get a glimpse of what Poser is doing by forcing these exceptions!

    movie loop -> computer scene transformations (probably a camera thingie) -> apply parameters to actors -> calculate local matrices for the actors -> (grabs in our callback) -> set geometry -> crash

    Maybe we really can't call SetGeometry() within any of the callbacks... but at the very bottom of the trace it seems it's handling some event... let's see if there's some undocumented event callback that shows when a frame has changed... hmm....



  • Hmm.. check this:

    def eventCallback(scene, code):
    	if code <> 16:
    		print 'Code = ',code
    
    ...
    scene.SetEventCallback(eventCallback)
    

    It will display a continuous series of code = 16, even when nothing's happening. No idea yet what is that 16. Whenever you change frames manually, it sends a code = 17.



  • So, 1 = kEventCodeKEYSCHANGED, 16 = undocumented... but 16 keeps happening all the time, so it must be Draw or something like that. Let's see how the thing behaves if I hook on event = 16, haha...



  • @fbs7 from my Poser Pro 11.0.8.34338 Python 2.7.2 Shell

    >>> poser.kEventCodeACTORADDED
    64
    >>> poser.kEventCodeACTORDELETED
    4
    >>> poser.kEventCodeACTORSELECTIONCHANGED
    8
    >>> poser.kEventCodeANIMSETSCHANGED
    4096
    >>> poser.kEventCodeITEMRENAMED
    256
    >>> poser.kEventCodeKEYSCHANGED
    1
    >>> poser.kEventCodePARMADDED
    128
    >>> poser.kEventCodePARMDELETED
    32
    >>> poser.kEventCodeSCENECLOSING
    1048576
    >>> poser.kEventCodeSETUPMODE
    8192
    

    Looking at one of @bagginsbill 's Parmatic scripts, he uses the explicit constant 16 and it's bitwise inverse ~16 in his event decoding callback, so yes, it's undocumented. My guess would be it coincides with a "ProcessSomeEvents" or idle moment.



  • Followed the instructions to the letter this time, except before pressing play, I manually incremented through all the frames beforehand and Poser then refused to crash. Last time I had just closed a largish scene, and this attempt was immediately after launching Poser.

    Applied a SuperFly volumetric glass shader to the isoSurface and made the isoField prop invisible in Render and Camera then rendered this sequence with low samples (10).



  • @fbs7 just noticed if I rotate the isoField prop, which the metaBall props are parented to, during the animation frames, the isoSurface does not follow the field's orientation, but acts as though there had been no isoField rotation, probably due to the UNIVERSE parenting during the isoSurface geometry creation.

    What are the possibilities of using a mesh as the field delimiter? Are the field constraints based on coordinate axis limits, or are you doing volume containment calculations?

    I know I should answer my own questions by looking at the code, but I'm about to nod off, so I'll look again tomorrow.



  • @anomalaus said in Metaballs script:

    @fbs7 just noticed if I rotate the isoField prop, which the metaBall props are parented to, during the animation frames, the isoSurface does not follow the field's orientation, but acts as though there had been no isoField rotation, probably due to the UNIVERSE parenting during the isoSurface geometry creation.

    What are the possibilities of using a mesh as the field delimiter? Are the field constraints based on coordinate axis limits, or are you doing volume containment calculations?

    I know I should answer my own questions by looking at the code, but I'm about to nod off, so I'll look again tomorrow.

    Yeah, that's the part I commented out, as I thought it might be a cause for the crashes. The code is easy to put back in, though; it's just two lines.



  • @anomalaus said in Metaballs script:

    @fbs7 from my Poser Pro 11.0.8.34338 Python 2.7.2 Shell

    Looking at one of @bagginsbill 's Parmatic scripts, he uses the explicit constant 16 and it's bitwise inverse ~16 in his event decoding callback, so yes, it's undocumented. My guess would be it coincides with a "ProcessSomeEvents" or idle moment.

    I tried to hook into that constant 16 to do the mesh update, because the event processing is way down in the loop sequence in the stack you posted, so it should have a better memory setup I thought, but... alas... the thing stops issuing 16 (or any other events) while it's in the run preview loop.

    So hooling in event processing will not do. I'll do one last attempt to change the darned SetGeometry() to be CreatePropFromGeometry() instead, as I don't remember crashes with that, but I fear it will kill the preview speed, as there's no single function to call to copy a material tree - it will have to be node by node I think.



  • The only other alternative I see is to create 100 props during baking (instead of in the callback), as set visibility. But then the user will be left with 100 static props around.

    I'm thinking that direction would involve having 4 scripts, probably:

    Create Metaball
    Create Surfaces (which was Run Metaball)
    Hide/Unhide Surfaces
    Remove Surfaces

    So instead of being dynamic the whole thing is static; but then you add 100 (or more) objects in the hierarchy window (although all of them are parented the same), and you have 100 things to save/load in the file...



  • On the other hand, because everything would be static, one would be able to drag and drop across the timeline without having to recalculate the simulation.

    Also it should be compatible with any renderer, given that now you just have 100 props around.



  • Alright, I can't manipulate the parameter "Visible" before it becomes animating parameter:

    >>> scene = poser.Scene()
    >>> a = scene.Actor('IsoField')
    >>> a.Parameter('Visible')
    >>> b = a.Parameter('Visible')
    >>> print b
    None
    

    Once Visible is set to animating in the GUI then the print b returns a parameter, but not before. But, there's no API to make the "Visible" property animating. So can't do it.

    Of course can still do within a callback, but once burned by the callback thingie, twice scared of it. So let's see how it works if I set scale = 0, X/Y/Z = 1000000 for the points where the pre-generated props should not be visible...



  • So enough of that! Time for me to stop complaining and start programming. Just a change a direction.



  • Fast enough; this is the timeline under the new approach. Each surface prop is created, made visible, then gets out of the way by setting position to 1,000,000 and scale to 0:

    0_1513477098011_temp.png

    So that will contaminate the timeline, but there will be another script to clean up the metaballs so they get out of the timeline.

    Now, the effect of all surfaces by frame showing up in the timeline may be avoided by creating a dummy figure (say create a cube cube, make it all transparent, setup room, back to pose room, parent the isoField prop to it, then save to library so that you can reuse that). As figures show in only one line in the timeline, however many children they have, this should avoid the timeline polution.

    I'd do that in the script if I could, but there's no way to create a figure out of nothing, that I could find in the manual.

    So I'm not too worried about that.



  • This is the test for this part; this is a 20 frame animation with static props; it can be saved and loaded and the animation will survive; should be compatible with any renderer, I think; my memory utilization was 340 MB before animation and 360 MB after animation, so should be 1 MB per frame, I guess (will increase for bigger resolutions). This may be a problem or not, not sure about that. But preview playback should be at full speed, because now all the props are pre-generated.



  • Now, to copy the shader tree. This will probably be some work. Let's see if I get how this thing is organized:

    Actor 1:N Materials (but we'll ignore all materials and use only 'Preview')
    Material 1:N Material Layers
    Material Layers 1:1 Shader Tree
    Shader Tree 1:N Shader Tree Nodes
    Shader Tree Node 1:N Inputs

    That seems to be it. So should be 3 loops, I guess.



  • Btw, the reason for using only Preview is that the surfaces are dynamic, so there's no way to set them to have multiple materials. Plus there's no CreateMaterial that I could find, so there's no way to create materials not already in the surface, and that means Preview only.



  • first step:

    				# Copying the materials from isoField to IsoSurfaceN
    				mat1 = isoField.Material('Preview')
    				mat2 = isoSurface.Material('Preview')
    				print mat1,mat2
    


  • Step 2, copying layers, tested ok:

    				# Copying the materials from isoField to IsoSurfaceN
    				mat1 = isoField.Material('Preview')
    				mat2 = isoSurface.Material('Preview')
    				for layer1 in mat1.Layers():
    					try:
    						layerName = layer1.Name()
    						layer2 = mat2.LayerByName( layerName )
    					except:
    						mat2.CreateLayer( layerName )
    						layer2 = mat2.LayerByName( layerName )
    					print layer1,layer2
    


  • Step 3, finding the material trees; worked around another API bug (sigh). Tested ok:

    				# Copying the materials from isoField to IsoSurfaceN
    				mat1 = isoField.Material('Preview')
    				mat2 = isoSurface.Material('Preview')
    				for layer1 in mat1.Layers():
    					try:
    						layerName = layer1.Name()
    						layer2 = mat2.LayerByName( layerName )
    						print 'Got layer ',layerName
    					except:
    						mat2.CreateLayer( layerName )
    						layer2 = mat2.LayerByName( layerName )
    						print 'Created layer ',layerName
    
    					# There's a bug in the API; need to retrieve ShaderTree() via
    					# the material before you can retrieve it from the layer for newly
    					# created props. Oh well. Don't remove the two dummy line below
    					
    					dummyTree = mat2.ShaderTree()
    					tree1 = layer1.ShaderTree()
    					tree2 = layer2.ShaderTree()
    					print tree1, tree2
    


  • Step 3, copied the node types and locations; now the materials at target have the same nodes as source, but no connections and have default values in all inputs:

    				# Copying the materials from isoField to IsoSurfaceN
    				mat1 = isoField.Material('Preview')
    				mat2 = isoSurface.Material('Preview')
    				for layer1 in mat1.Layers():
    					
    					# Find the material layer in target or create it
    					try:
    						layerName = layer1.Name()
    						layer2 = mat2.LayerByName( layerName )
    					except:
    						mat2.CreateLayer( layerName )
    						layer2 = mat2.LayerByName( layerName )
    
    					# There's a bug in the API; need to retrieve ShaderTree() via
    					# the material before you can retrieve it from the layer for newly
    					# created props. Oh well. Don't remove the two dummy line below
    					
    					dummyTree = mat2.ShaderTree()
    					tree1 = layer1.ShaderTree()
    					tree2 = layer2.ShaderTree()
    
    					# Remove all nodes in the target tree
    					for node2 in tree2.Nodes():
    						node2.Delete()
    
    					# Now create all nodes in the target tree
    					for node1 in tree1.Nodes():
    						
    						node2 = tree2.CreateNode( node1.Type() )
    						node2.SetLocation( node1.Location()[0], node1.Location()d[1] )
    						print node1, node2