[Durian-svn] [3205] news scripts to save/load the weights of the active hair object.

joe institute at blender.org
Tue May 11 20:48:25 CEST 2010


Revision: 3205
          https://blenderinstitute.dyndns.org/durian-svn/?do=log&project=durian&path=/&rev=3205
Author:   joe
Date:     2010-05-11 20:48:24 +0200 (Tue, 11 May 2010)
Log Message:
-----------
news scripts to save/load the weights of the active hair object.  to use, select the object you wish to export (or import data onto) then select the particle system you wish to use (if you have multiple).  also added small example comment in finals_config.py as to how to use custom hair weight maps in finals_config.py.

Modified Paths:
--------------
    pro/scripts/modules/finals_config.py

Added Paths:
-----------
    pro/scripts/io/
    pro/scripts/io/hair_weight_export.py
    pro/scripts/io/hair_weight_import.py
    pro/scripts/modules/hairutils.py

Added: pro/scripts/io/hair_weight_export.py
===================================================================
--- pro/scripts/io/hair_weight_export.py	                        (rev 0)
+++ pro/scripts/io/hair_weight_export.py	2010-05-11 18:48:24 UTC (rev 3205)
@@ -0,0 +1,69 @@
+from bpy.props import *
+import bpy, os, sys
+sys.path.append("/d/pro/scripts/modules")
+import hairutils
+
+class ExportHairWeights(bpy.types.Operator):
+    '''Export the active particle system's hair weights'''
+    bl_idname = "export.export_hair_weights"
+    bl_label = "Export Hair Weights"
+
+    # List of operator properties, the attributes will be assigned
+    # to the class instance from the operator settings before calling.
+
+    path = StringProperty(name="File Path", description="File path used for exporting", maxlen=1024, default="")
+
+    filename= StringProperty(name="File Name", description="File name used for exporting", maxlen=1024, default="", options={'HIDDEN'})
+    directory = StringProperty(name="Directory", description="Directory used for exporting", maxlen=1024, default="", options={'HIDDEN'})
+    check_existing = BoolProperty(name="Check Existing", description="Check and warn on overwriting existing files", default=True, options={'HIDDEN'})
+
+    def poll(self, context):
+        return context.active_object != None
+
+    def execute(self, context):
+        if not self.properties.path:
+            raise Exception("filename not set")
+        
+        try:
+            file = open(self.properties.path, "wb")
+        except:
+            raise Exception("Could not open file " + repr(self.properties.path) + " for writing!")
+            return {'CANCELLED'}
+        
+        if not context.active_object:
+            raise Exception("Must select an object")
+            return {'CANCELLED'}
+        if not context.active_object.active_particle_system:
+            raise Exception("The active selected object must have an active particle system")
+            return {'CANCELLED'}
+
+        psys = context.active_object.active_particle_system
+        buf = hairutils.save_hair_weights(psys)
+    
+        file.write(buf)
+        file.flush()
+        file.close()
+
+        return {'FINISHED'}
+    
+    def invoke(self, context, event):
+        wm = context.manager
+        wm.add_fileselect(self)
+
+        return {'RUNNING_MODAL'}
+
+def menu_func(self, context):
+    default_path = bpy.data.filename.replace(".blend", ".hairw")
+    self.layout.operator(ExportHairWeights.bl_idname, text="Particle Hair Weigts (.hairw)").path = default_path
+
+def register():
+    bpy.types.register(ExportHairWeights)
+    bpy.types.INFO_MT_file_export.append(menu_func)
+def unregister():
+    bpy.types.unregister(ExportHairWeights)
+    bpy.types.INFO_MT_file_export.remove(menu_func)
+
+if __name__ == "__main__":
+    register()
+
+

Added: pro/scripts/io/hair_weight_import.py
===================================================================
--- pro/scripts/io/hair_weight_import.py	                        (rev 0)
+++ pro/scripts/io/hair_weight_import.py	2010-05-11 18:48:24 UTC (rev 3205)
@@ -0,0 +1,72 @@
+from bpy.props import *
+import bpy, os, sys
+sys.path.append("/d/pro/scripts/modules")
+import hairutils
+
+class ImportHairWeights(bpy.types.Operator):
+    '''Import hair weights onto the active particle system'''
+    bl_idname = "import.import_hair_weights"
+    bl_label = "Import Hair Weights"
+
+    # List of operator properties, the attributes will be assigned
+    # to the class instance from the operator settings before calling.
+
+    path = StringProperty(name="File Path", description="File path used for exporting", maxlen=1024, default="")
+
+    filename= StringProperty(name="File Name", description="File name used for exporting", maxlen=1024, default="", options={'HIDDEN'})
+    directory = StringProperty(name="Directory", description="Directory used for exporting", maxlen=1024, default="", options={'HIDDEN'})
+    check_existing = BoolProperty(name="Check Existing", description="Check and warn on overwriting existing files", default=False, options={'HIDDEN'})
+
+    def poll(self, context):
+        return context.active_object != None
+
+    def execute(self, context):
+        if not self.properties.path:
+            raise Exception("filename not set")
+        
+        try:
+            file = open(self.properties.path, "rb")
+        except:
+            raise Exception("Could not open file " + repr(self.properties.path) + " for writing!")
+            return {'CANCELLED'}
+        
+        if not context.active_object:
+            raise Exception("Must select an object")
+            return {'CANCELLED'}
+        if not context.active_object.active_particle_system:
+            raise Exception("The active selected object must have an active particle system")
+            return {'CANCELLED'}
+
+        state = hairutils.ob_no_psys_edit(context.active_object)
+        psys = context.active_object.active_particle_system
+
+        buf = file.read()
+        hairutils.load_hair_weights(buf, psys)
+
+        file.close()
+        hairutils.ob_restore_psys_edit(context.active_object, state)
+
+        return {'FINISHED'}
+    
+    def invoke(self, context, event):
+        wm = context.manager
+        wm.add_fileselect(self)
+
+        return {'RUNNING_MODAL'}
+
+def menu_func(self, context):
+    self.layout.operator(ImportHairWeights.bl_idname, text="Particle Hair Weigts (.hairw)")
+
+def register():
+    bpy.types.register(ImportHairWeights)
+    bpy.types.INFO_MT_file_import.append(menu_func)
+
+
+def unregister():
+    bpy.types.unregister(ImportHairWeights)
+    bpy.types.INFO_MT_file_import.remove(menu_func)
+
+if __name__ == "__main__":
+    register()
+
+

Modified: pro/scripts/modules/finals_config.py
===================================================================
--- pro/scripts/modules/finals_config.py	2010-05-11 18:44:31 UTC (rev 3204)
+++ pro/scripts/modules/finals_config.py	2010-05-11 18:48:24 UTC (rev 3205)
@@ -123,7 +123,13 @@
         else:
             coll.enable_collision = coll.enable_self_collision = False
         
+        def load_hair_weights(name):
+            path = base_dir + os.sep + name
 
+            f = open(path, "rb")
+            hairutils.load_hair_weights(f, psys)
+            f.close()
+
         def sintal_preset_hair_run():
             '''
             When changing settings here BE VERY CAREFUL!
@@ -142,7 +148,14 @@
             psys.cloth.settings.mass = 0.015 # was 0.01
             psys.cloth.settings.internal_friction = 0.1 # 0.9 makes hair too symmatrical
 
-
+        """
+        example of loading a hair weight file:
+        
+        load_hair_weights("02_d.hairw")
+        
+        this will load 02_d.hairw, which must be in the same
+        directory as the shot (e.g. scenes/02-whatever/).
+        """
         # Per Blend Sintel Stuff...
         if base_name.startswith("02.b"):
             psys.cloth.settings.pin_stiffness = 30

Added: pro/scripts/modules/hairutils.py
===================================================================
--- pro/scripts/modules/hairutils.py	                        (rev 0)
+++ pro/scripts/modules/hairutils.py	2010-05-11 18:48:24 UTC (rev 3205)
@@ -0,0 +1,86 @@
+import bpy, sys
+
+def ob_no_psys_edit(ob):
+	if ob.mode == 'PARTICLE_EDIT':
+		bpy.ops.object.mode_set()
+		return 1
+	return 0
+
+def ob_restore_psys_edit(ob, state):
+	if state:
+		bpy.ops.object.mode_set(mode='PARTICLE_EDIT')
+
+def save_hair_weights(psys):
+	print (psys)
+	
+	w = []
+	for p in psys.particles:
+		w.append([])
+		for k in p.hair:
+			w[-1].append(k.weight)
+	
+	s = b""
+	i = 0
+	for p in w:
+		s += bytes(repr(i), "ascii") + b"|"
+		for k in p:
+			s += bytes(repr(k), "ascii") + b";"
+			
+		i += 1
+		s += b"\n"
+		
+	return s
+
+class HairLoadException (RuntimeError):
+	pass
+
+def load_hair_weights(buf, psys):
+	w = []
+	for l in buf.split(b"\n"):
+		i = 0
+		num = b""
+		while i < len(l):
+			if l[i:i+1] == b"|":
+				w.append([])
+				num = b""
+			elif l[i:i+1] == b";":
+				w[-1].append(float(num))
+				num = b""
+			else:
+				num += l[i:i+1]
+			
+			i += 1
+	
+	if len(w) != len(psys.particles):
+		raise HairLoadException("Number of hairs differs in weight file from active object!")
+		return
+	
+	ps = psys.particles
+	for i in range(len(w)):
+		if not ps[i].hair:
+			raise HairLoadException("Particle system lacks hair keys")
+			return
+		
+		ks = w[i]
+		p = ps[i]
+		for j in range(len(ks)):
+			if j >= len(ks):
+				print("Warning: one or more hair strands in object had more keys then in weight file")
+				break
+			elif j >= len(p.hair):
+				print("Warning: weight file had more keys then one or more hair strands")
+				break
+            
+			sys.stdout.flush()
+			p.hair[j].weight = ks[j]
+		
+if __name__ == "__main__":
+	ob = bpy.context.active_object
+	if ob and ob.active_particle_system:
+		state = ob_no_psys_edit(ob)
+		
+		buf = save_hair_weights(ob.active_particle_system);
+		load_hair_weights(buf, ob.active_particle_system);
+		
+		ob_restore_psys_edit(ob, state)
+



More information about the Durian-svn mailing list