Atom Posted July 6, 2017 Share Posted July 6, 2017 (edited) Hi All, I came across some python code that generates a basic transfer from the Houdini particle system to the Blender 2.78 particle system. I tweaked it up a bit. The concept is that Houdini writes Blender format .bphys files that can then be referenced as an external cache from within Blender. The Blender particle system is very crude and as to date the script can only transfer particle location and velocity. I am throwing this up here as a frame work if anyone has any interest in pursuing this kind of binary file construction from within Houdini python. Other cache transfers might be possible as well. Checkout the example HIP file for setup. To export the cache simply play the timeline. Make sure to set the output path in the script, manually, to a valid folder that you can browse to from within Blender 2.78. Blender (age honored) Houdini (reaping off, age generated but not displayed) Oddities... The Blender particle system seems to want all particle to exist on frame #1. Then it manages what appears via unborn and dead flags over time. This means Reaping particles out of Houdini should be turned off. This will insure that unique particle ids are provided throughout time, otherwise you can get some velocity spikes in Blender if a particle id is reused and it was one place on one frame and then is suddenly in another location because of id reuse. Tip: Because Blender expects all particles to exist on frame 1 make sure you are not on frame #1, but somewhere in the middle of the simulation when you browse to the external cache. Otherwise Blender may only make the number of particles that exist on frame 1 for the entire cache. This is typically wrong and not what you want because particle counts grow over time. In order for Blender to locate the cache you must Double Click on the blank cache entry and name it houdinipoints. You can use a different name, but that is the default generated by the python script. # Export Blender .bphys file from a Houdini particle system or point based object. # https://developer.blender.org/diffusion/B/browse/master/source/blender/blenkernel/intern/pointcache.c # Original code found: gui2one # https://blender.stackexchange.com/questions/47814/bphys-files-sp%C3%A9cifications # (c) 2017 Atom. import struct node = hou.pwd() geo = node.geometry() # Set output path and partial name here. cache_name = r"C:\Users\Admin\Desktop\bphys_cache\houdinipoints" point_count = len(geo.points()) frame_current = int(hou.frame()) cache_index = 0 # Attribute defaults, if they don't exist on points. birth = 0 life = 32 age = 0 # Check for presence of attributes. a_P = geo.findPointAttrib("P") a_v = geo.findPointAttrib("v") a_age = geo.findPointAttrib("age") a_life = geo.findPointAttrib("life") a_birth = geo.findPointAttrib("birth_time") #Note: Constructed by nodes, not part of the default popnet output. # Write frame #0 only on rewind to 1. if frame_current ==1: # Frame #0 file has a different format that other frames. cache_filename = cache_name+"_{0:06d}_{1:02d}.bphys".format(0,cache_index) file_handle = open(cache_filename, "wb") # Use for frame #0 .bphys frame. dataType = 1 # 1= particles. data = struct.pack("8c","B","P","H","Y","S","I","C","S") data += struct.pack("III",dataType,point_count,64) for (i,point) in enumerate(geo.points()): if a_life: life = point.attribValue(a_life) if a_age: age = point.attribValue(a_age) if a_birth: birth_time = point.attribValue(a_birth) # time, lifetime, dietime. data += struct.pack('fff', hou.timeToFrame(hou.time()),hou.timeToFrame(life+birth_time),hou.timeToFrame(life)) file_handle.write(data) file_handle.close cache_filename = cache_name+"_{0:06d}_{1:02d}.bphys".format(frame_current,cache_index) file_handle = open(cache_filename, "wb") # Use for all other .bphys frame #s. dataType = 1 # 1= particles. data = struct.pack("8c","B","P","H","Y","S","I","C","S") data += struct.pack("III",dataType,point_count,111) # Type + Bitwise data section types. for (i,point) in enumerate(geo.points()): local_P = point.attribValue(a_P) local_v = point.attribValue(a_v) #Index data += struct.pack('I',i) #Location data += struct.pack('fff',local_P[0], local_P[2]*-1, local_P[1]) # Y negative 1 scale. YZ swapped? #Velocity data += struct.pack('fff',local_v[0], local_v[2]*-1, local_v[1]) # Y negative 1 scale. YZ swapped? #Rotation data += struct.pack('ffff',0.0,0.0,0.0,0.0) # Values don't seem to transfer..? #Avelocity or Cloth? #data += struct.pack('fff',0.0,0.0,0.0) ## no values for now #frame # for this frame. (seems kind of redundant...) data += struct.pack('f',frame_current) # recommended to be the same as the cache frame number. #times if a_life: life = point.attribValue(a_life) if a_birth: birth_time = point.attribValue(a_birth) data += struct.pack('fff',hou.timeToFrame(hou.time()),hou.timeToFrame(life+birth_time),hou.timeToFrame(life)) ## time, die_time, life_time file_handle.write(data) file_handle.close ap_write_bphys_particles_070617.hipnc Edited July 6, 2017 by Atom 3 Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.