__author__ = 'thoth'
import struct
import os.path
# http://blender.stackexchange.com/questions/15577/how-to-extract-convert-data-from-blender-cache-files-bphys-into-a-human-readabl
class CacheRowReader:
# BPHYS_DATA_* from DNA_object_force.h
column_words = (1,3,3,4,3,1,3,5)
column_formats = ("i", "fff", "fff", "ffff", "fff", "f", "fff", "fffff")
def __init__(self, flavor, count, data_type_flags):
self.count = count
self.flavor = flavor
self.data_type_flags = data_type_flags
rec_len=0
unpack_format = ""
for i in range(len(CacheRowReader.column_words)):
if 0 != (data_type_flags&(1<<i)):
rec_len += 4 * CacheRowReader.column_words[i]
unpack_format += CacheRowReader.column_formats[i]
self.rec_len = rec_len
self.unpack_format = unpack_format
@classmethod
def parse(cls, f):
magic = f.read(8)
if magic != b'BPHYSICS':
raise Exception("not a blender physics cache")
flavor = f.read(12)
(flavor,count,data_type_flags) = struct.unpack("iii", flavor)
#print( "%d\t%d\t%d"%(flavor,count,data_type_flags))
return CacheRowReader(flavor, count, data_type_flags)
def read_row(self, f):
"""
:param f: a file opened with "rb" (binary) mode
:return:
"""
raw = f.read(self.rec_len)
if raw is None or len(raw)==0:
return None
if len(raw) != self.rec_len:
raise Exception("short read (%d<%d)"%( len(raw), self.rec_len))
columns = struct.unpack(self.unpack_format, raw)
rval = dict()
cursor =0
# a lot of these clauses are untested. Feel free to leave me a comment on the stackexchange answer.
if 0 != (self.data_type_flags&1):
rval['index'] = columns[cursor]
cursor +=1
if 0 != self.data_type_flags&2:
rval['location'] = columns[cursor:cursor+3]
rval['smoke_low'] = rval['location']
cursor +=3
if 0 != self.data_type_flags&4:
rval['velocity'] = columns[cursor:cursor+3]
rval['smoke_high'] = rval['velocity']
cursor +=3
if 0 != self.data_type_flags&8:
rval['dynamicpaint'] = rval['rotation'] = columns[cursor:cursor+4]
cursor +=4
if 0 != self.data_type_flags&0x10:
rval['xconst'] = rval['avelocity'] = columns[cursor:cursor+3]
cursor +=3
if 0 != self.data_type_flags&0x20:
rval['size'] = columns[cursor]
cursor +=1
if 0 != self.data_type_flags&0x40:
rval['times'] = columns[cursor:cursor+3]
cursor +=3
if 0 != self.data_type_flags&0x80:
rval['boids'] = columns[cursor:cursor+5]
cursor +=5
return rval
def dump_one_file(fname):
f = open(fname, "rb")
reader = CacheRowReader.parse(f)
print("%d\t%d\t%d"%(reader.flavor, reader.count, reader.data_type_flags))
while True:
row = reader.read_row(f)
if row is None:
break
print(row)
def dump_times_file(fname):
f = open(fname, "rb")
reader = CacheRowReader.parse(f)
while True:
row = reader.read_row(f)
if row is None:
break
print(row)
def maybe_discard(scn, name):
obj = bpy.data.objects.get(name)
if obj is None:
return
obj.name = "discard"
try:
scn.objects.unlink(obj)
except:
pass
def curve_from_particle_track(scn, path_pattern):
i=0
tracks = []
while True:
fname = path_pattern%i
#print(fname)
if not os.path.isfile(fname):
break
f = open(fname, "rb")
if f is None:
break
reader = CacheRowReader.parse(f)
if 0 == (reader.data_type_flags &1):
pass # this is probably the first cache file with only time info, let's ignore it
else:
while True:
row = reader.read_row(f)
if row is None: break
#print(row)
idx = row['index']
while len(tracks) <= idx:
tracks.append( [] )
tracks[idx].append(row['location'])
f.close()
i+=1
#print(i)
#print (tracks)
name = "tracks"
curve = bpy.data.curves.new(name, 'CURVE')
for i in range(len(tracks)):
locations = tracks[i]
if len(locations) < 1:
continue
spline = curve.splines.new('POLY')
spline.points.add(len(locations) - len(spline.points))
for j in range(len(locations)):
#print(locations[j])
spline.points[j].co[0:3] = locations[j]
maybe_discard(scn, name)
obj = bpy.data.objects.new(name, curve)
scn.objects.link(obj)
if True:
fname = "/home/thoth/art/ornaments-in-space/blendcache_ornaments-in-space/loop_000001_00.bphys"
#fname = "/home/thoth/art/ornaments-in-space/blendcache_ornaments-in-space/loop_000000_00.bphys"
#fname = "/home/thoth/art/ornaments-in-space/blendcache_ornaments-in-space/7061727469636C6520656D69747465722E303031_000000_00.bphys"
dump_one_file(fname)
elif True:
dump_one_file("/var/tmp/blendcache_particles/bacon_000000_00.bphys")
elif True:
dump_one_file("/var/tmp/blendcache_particle/pants_000045_00.bphys")
elif False:
dump_times_file("/var/tmp/blendcache_particle/pants_000000_00.bphys")
else:
curve_from_particle_track(bpy.context.scene, "/var/tmp/blendcache_particle/pants_%06d_00.bphys")
|
Blender python API quick-start
Syntax highlighting by Pygments.