Revert particle system and point cache removal in blender2.8 branch.
authorLukas Tönne <lukas.toenne@gmail.com>
Wed, 28 Dec 2016 16:30:58 +0000 (17:30 +0100)
committerLukas Tönne <lukas.toenne@gmail.com>
Wed, 28 Dec 2016 16:30:58 +0000 (17:30 +0100)
This reverts commit 5aa19be91263a249ffae75573e3b32f24269d890 and b4a721af694817fa921b119df83d33ede7d7fed0.

Due to postponement of particle system rewrite it was decided to put particle code
back into the 2.8 branch for the time being.

226 files changed:
intern/cycles/blender/CMakeLists.txt
intern/cycles/blender/addon/properties.py
intern/cycles/blender/addon/ui.py
intern/cycles/blender/blender_curves.cpp
intern/cycles/blender/blender_object.cpp
intern/cycles/blender/blender_particles.cpp [new file with mode: 0644]
intern/cycles/blender/blender_sync.cpp
intern/cycles/blender/blender_sync.h
intern/cycles/blender/blender_util.h
intern/elbeem/extern/elbeem.h
release/scripts/presets/hair_dynamics/default.py [new file with mode: 0644]
release/scripts/startup/bl_operators/object_quick_effects.py
release/scripts/startup/bl_operators/presets.py
release/scripts/startup/bl_operators/view3d.py
release/scripts/startup/bl_ui/__init__.py
release/scripts/startup/bl_ui/properties_data_modifier.py
release/scripts/startup/bl_ui/properties_paint_common.py
release/scripts/startup/bl_ui/properties_particle.py [new file with mode: 0644]
release/scripts/startup/bl_ui/properties_physics_cloth.py
release/scripts/startup/bl_ui/properties_physics_common.py
release/scripts/startup/bl_ui/properties_physics_dynamicpaint.py
release/scripts/startup/bl_ui/properties_physics_smoke.py
release/scripts/startup/bl_ui/properties_physics_softbody.py
release/scripts/startup/bl_ui/properties_scene.py
release/scripts/startup/bl_ui/properties_texture.py
release/scripts/startup/bl_ui/space_dopesheet.py
release/scripts/startup/bl_ui/space_time.py
release/scripts/startup/bl_ui/space_userpref.py
release/scripts/startup/bl_ui/space_view3d.py
release/scripts/startup/bl_ui/space_view3d_toolbar.py
source/blender/CMakeLists.txt
source/blender/alembic/intern/abc_exporter.cc
source/blender/alembic/intern/abc_hair.cc
source/blender/alembic/intern/abc_hair.h
source/blender/alembic/intern/abc_mesh.cc
source/blender/alembic/intern/abc_points.cc
source/blender/alembic/intern/abc_points.h
source/blender/blenkernel/BKE_boids.h [new file with mode: 0644]
source/blender/blenkernel/BKE_cloth.h
source/blender/blenkernel/BKE_context.h
source/blender/blenkernel/BKE_dynamicpaint.h
source/blender/blenkernel/BKE_effect.h
source/blender/blenkernel/BKE_library.h
source/blender/blenkernel/BKE_main.h
source/blender/blenkernel/BKE_modifier.h
source/blender/blenkernel/BKE_object.h
source/blender/blenkernel/BKE_particle.h [new file with mode: 0644]
source/blender/blenkernel/BKE_pointcache.h [new file with mode: 0644]
source/blender/blenkernel/BKE_rigidbody.h
source/blender/blenkernel/BKE_sca.h
source/blender/blenkernel/BKE_softbody.h
source/blender/blenkernel/BKE_texture.h
source/blender/blenkernel/CMakeLists.txt
source/blender/blenkernel/intern/anim.c
source/blender/blenkernel/intern/anim_sys.c
source/blender/blenkernel/intern/boids.c [new file with mode: 0644]
source/blender/blenkernel/intern/bpath.c
source/blender/blenkernel/intern/cloth.c
source/blender/blenkernel/intern/context.c
source/blender/blenkernel/intern/depsgraph.c
source/blender/blenkernel/intern/dynamicpaint.c
source/blender/blenkernel/intern/effect.c
source/blender/blenkernel/intern/fluidsim.c
source/blender/blenkernel/intern/group.c
source/blender/blenkernel/intern/idcode.c
source/blender/blenkernel/intern/ipo.c
source/blender/blenkernel/intern/library.c
source/blender/blenkernel/intern/library_query.c
source/blender/blenkernel/intern/library_remap.c
source/blender/blenkernel/intern/modifier.c
source/blender/blenkernel/intern/object.c
source/blender/blenkernel/intern/object_deform.c
source/blender/blenkernel/intern/object_dupli.c
source/blender/blenkernel/intern/object_update.c
source/blender/blenkernel/intern/particle.c [new file with mode: 0644]
source/blender/blenkernel/intern/particle_child.c [new file with mode: 0644]
source/blender/blenkernel/intern/particle_distribute.c [new file with mode: 0644]
source/blender/blenkernel/intern/particle_system.c [new file with mode: 0644]
source/blender/blenkernel/intern/pointcache.c [new file with mode: 0644]
source/blender/blenkernel/intern/rigidbody.c
source/blender/blenkernel/intern/scene.c
source/blender/blenkernel/intern/smoke.c
source/blender/blenkernel/intern/softbody.c
source/blender/blenkernel/intern/texture.c
source/blender/blenloader/intern/readfile.c
source/blender/blenloader/intern/versioning_250.c
source/blender/blenloader/intern/versioning_260.c
source/blender/blenloader/intern/versioning_270.c
source/blender/blenloader/intern/versioning_defaults.c
source/blender/blenloader/intern/versioning_legacy.c
source/blender/blenloader/intern/writefile.c
source/blender/blentranslation/BLT_translation.h
source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
source/blender/depsgraph/intern/builder/deg_builder_relations.cc
source/blender/depsgraph/intern/builder/deg_builder_relations.h
source/blender/depsgraph/intern/depsgraph_build.cc
source/blender/depsgraph/intern/depsgraph_tag.cc
source/blender/editors/animation/anim_channels_defines.c
source/blender/editors/animation/anim_channels_edit.c
source/blender/editors/animation/anim_filter.c
source/blender/editors/include/ED_anim_api.h
source/blender/editors/include/ED_buttons.h
source/blender/editors/include/ED_object.h
source/blender/editors/include/ED_particle.h [new file with mode: 0644]
source/blender/editors/include/ED_physics.h
source/blender/editors/interface/interface_icons.c
source/blender/editors/interface/interface_templates.c
source/blender/editors/object/object_add.c
source/blender/editors/object/object_edit.c
source/blender/editors/object/object_intern.h
source/blender/editors/object/object_modifier.c
source/blender/editors/object/object_ops.c
source/blender/editors/object/object_relations.c
source/blender/editors/object/object_select.c
source/blender/editors/physics/CMakeLists.txt
source/blender/editors/physics/particle_boids.c [new file with mode: 0644]
source/blender/editors/physics/particle_edit.c [new file with mode: 0644]
source/blender/editors/physics/particle_object.c [new file with mode: 0644]
source/blender/editors/physics/physics_intern.h
source/blender/editors/physics/physics_ops.c
source/blender/editors/physics/physics_pointcache.c [new file with mode: 0644]
source/blender/editors/render/render_shading.c
source/blender/editors/screen/screen_context.c
source/blender/editors/sculpt_paint/paint_vertex.c
source/blender/editors/space_buttons/buttons_context.c
source/blender/editors/space_buttons/buttons_texture.c
source/blender/editors/space_buttons/space_buttons.c
source/blender/editors/space_file/filesel.c
source/blender/editors/space_info/info_stats.c
source/blender/editors/space_nla/nla_buttons.c
source/blender/editors/space_nla/nla_channels.c
source/blender/editors/space_outliner/outliner_draw.c
source/blender/editors/space_outliner/outliner_intern.h
source/blender/editors/space_outliner/outliner_select.c
source/blender/editors/space_outliner/outliner_tree.c
source/blender/editors/space_time/space_time.c
source/blender/editors/space_view3d/drawobject.c
source/blender/editors/space_view3d/drawvolume.c
source/blender/editors/space_view3d/space_view3d.c
source/blender/editors/space_view3d/view3d_draw_legacy.c
source/blender/editors/space_view3d/view3d_edit.c
source/blender/editors/space_view3d/view3d_header.c
source/blender/editors/space_view3d/view3d_intern.h
source/blender/editors/space_view3d/view3d_select.c
source/blender/editors/transform/transform.c
source/blender/editors/transform/transform.h
source/blender/editors/transform/transform_conversions.c
source/blender/editors/transform/transform_generics.c
source/blender/editors/transform/transform_manipulator.c
source/blender/editors/transform/transform_orientations.c
source/blender/editors/transform/transform_snap.c
source/blender/editors/transform/transform_snap_object.c
source/blender/editors/util/CMakeLists.txt
source/blender/editors/util/undo.c
source/blender/gpu/GPU_material.h
source/blender/gpu/intern/gpu_codegen.c
source/blender/gpu/intern/gpu_draw.c
source/blender/gpu/intern/gpu_material.c
source/blender/makesdna/DNA_ID.h
source/blender/makesdna/DNA_boid_types.h [new file with mode: 0644]
source/blender/makesdna/DNA_dynamicpaint_types.h
source/blender/makesdna/DNA_ipo_types.h
source/blender/makesdna/DNA_modifier_types.h
source/blender/makesdna/DNA_object_fluidsim.h
source/blender/makesdna/DNA_object_force.h
source/blender/makesdna/DNA_object_types.h
source/blender/makesdna/DNA_outliner_types.h
source/blender/makesdna/DNA_particle_types.h [new file with mode: 0644]
source/blender/makesdna/DNA_rigidbody_types.h
source/blender/makesdna/DNA_scene_types.h
source/blender/makesdna/DNA_smoke_types.h
source/blender/makesdna/DNA_space_types.h
source/blender/makesdna/DNA_userdef_types.h
source/blender/makesdna/intern/makesdna.c
source/blender/makesrna/RNA_access.h
source/blender/makesrna/RNA_enum_types.h
source/blender/makesrna/intern/CMakeLists.txt
source/blender/makesrna/intern/makesrna.c
source/blender/makesrna/intern/rna_ID.c
source/blender/makesrna/intern/rna_action.c
source/blender/makesrna/intern/rna_boid.c [new file with mode: 0644]
source/blender/makesrna/intern/rna_color.c
source/blender/makesrna/intern/rna_context.c
source/blender/makesrna/intern/rna_dynamicpaint.c
source/blender/makesrna/intern/rna_fluidsim.c
source/blender/makesrna/intern/rna_internal.h
source/blender/makesrna/intern/rna_main.c
source/blender/makesrna/intern/rna_main_api.c
source/blender/makesrna/intern/rna_modifier.c
source/blender/makesrna/intern/rna_nodetree.c
source/blender/makesrna/intern/rna_object.c
source/blender/makesrna/intern/rna_object_api.c
source/blender/makesrna/intern/rna_object_force.c
source/blender/makesrna/intern/rna_particle.c [new file with mode: 0644]
source/blender/makesrna/intern/rna_rigidbody.c
source/blender/makesrna/intern/rna_scene.c
source/blender/makesrna/intern/rna_sculpt_paint.c
source/blender/makesrna/intern/rna_smoke.c
source/blender/makesrna/intern/rna_space.c
source/blender/makesrna/intern/rna_texture.c
source/blender/makesrna/intern/rna_userdef.c
source/blender/modifiers/CMakeLists.txt
source/blender/modifiers/MOD_modifiertypes.h
source/blender/modifiers/intern/MOD_build.c
source/blender/modifiers/intern/MOD_cloth.c
source/blender/modifiers/intern/MOD_collision.c
source/blender/modifiers/intern/MOD_explode.c
source/blender/modifiers/intern/MOD_laplaciandeform.c
source/blender/modifiers/intern/MOD_particleinstance.c [new file with mode: 0644]
source/blender/modifiers/intern/MOD_particlesystem.c [new file with mode: 0644]
source/blender/modifiers/intern/MOD_shapekey.c
source/blender/modifiers/intern/MOD_smooth.c
source/blender/modifiers/intern/MOD_softbody.c
source/blender/modifiers/intern/MOD_solidify.c
source/blender/modifiers/intern/MOD_util.c
source/blender/nodes/shader/nodes/node_shader_particle_info.c
source/blender/render/intern/source/convertblender.c
source/blender/render/intern/source/pipeline.c
source/blender/render/intern/source/pointdensity.c
source/blender/render/intern/source/renderdatabase.c
source/blender/render/intern/source/shadeinput.c
source/blender/render/intern/source/voxeldata.c
source/blender/windowmanager/WM_types.h
source/blenderplayer/bad_level_call_stubs/stubs.c
source/creator/creator.c
source/gameengine/Ketsji/BL_BlenderShader.cpp

index f964f2d1f9e2dd80f81f74b87734f6da9d27660a..b57502b3b141a8bcd5d8951c2031641124a63ae9 100644 (file)
@@ -26,6 +26,7 @@ set(SRC
        blender_mesh.cpp
        blender_object.cpp
        blender_object_cull.cpp
+       blender_particles.cpp
        blender_curves.cpp
        blender_logging.cpp
        blender_python.cpp
index cbff5a537dc6a5b4177194555de650648aded5e4..3616b13e75109fcae2e67cc4aa22188675ddf2aa 100644 (file)
@@ -1137,6 +1137,49 @@ class CyclesCurveRenderSettings(bpy.types.PropertyGroup):
         del bpy.types.Scene.cycles_curves
 
 
+class CyclesCurveSettings(bpy.types.PropertyGroup):
+    @classmethod
+    def register(cls):
+        bpy.types.ParticleSettings.cycles = PointerProperty(
+                name="Cycles Hair Settings",
+                description="Cycles hair settings",
+                type=cls,
+                )
+        cls.radius_scale = FloatProperty(
+                name="Radius Scaling",
+                description="Multiplier of width properties",
+                min=0.0, max=1000.0,
+                default=0.01,
+                )
+        cls.root_width = FloatProperty(
+                name="Root Size",
+                description="Strand's width at root",
+                min=0.0, max=1000.0,
+                default=1.0,
+                )
+        cls.tip_width = FloatProperty(
+                name="Tip Multiplier",
+                description="Strand's width at tip",
+                min=0.0, max=1000.0,
+                default=0.0,
+                )
+        cls.shape = FloatProperty(
+                name="Strand Shape",
+                description="Strand shape parameter",
+                min=-1.0, max=1.0,
+                default=0.0,
+                )
+        cls.use_closetip = BoolProperty(
+                name="Close tip",
+                description="Set tip radius to zero",
+                default=True,
+                )
+
+    @classmethod
+    def unregister(cls):
+        del bpy.types.ParticleSettings.cycles
+
+
 class CyclesDeviceSettings(bpy.types.PropertyGroup):
     @classmethod
     def register(cls):
@@ -1248,6 +1291,7 @@ def register():
     bpy.utils.register_class(CyclesMeshSettings)
     bpy.utils.register_class(CyclesObjectSettings)
     bpy.utils.register_class(CyclesCurveRenderSettings)
+    bpy.utils.register_class(CyclesCurveSettings)
     bpy.utils.register_class(CyclesDeviceSettings)
     bpy.utils.register_class(CyclesPreferences)
 
@@ -1262,5 +1306,6 @@ def unregister():
     bpy.utils.unregister_class(CyclesObjectSettings)
     bpy.utils.unregister_class(CyclesVisibilitySettings)
     bpy.utils.unregister_class(CyclesCurveRenderSettings)
+    bpy.utils.unregister_class(CyclesCurveSettings)
     bpy.utils.unregister_class(CyclesDeviceSettings)
     bpy.utils.unregister_class(CyclesPreferences)
index 95731562c79a0375d5d8e7e9b091179855c5bce8..acca64148525a2ccdcc3a910a024580af755191d 100644 (file)
@@ -1360,6 +1360,37 @@ class CyclesTexture_PT_colors(CyclesButtonsPanel, Panel):
             layout.template_color_ramp(mapping, "color_ramp", expand=True)
 
 
+class CyclesParticle_PT_textures(CyclesButtonsPanel, Panel):
+    bl_label = "Textures"
+    bl_context = "particle"
+    bl_options = {'DEFAULT_CLOSED'}
+
+    @classmethod
+    def poll(cls, context):
+        psys = context.particle_system
+        return psys and CyclesButtonsPanel.poll(context)
+
+    def draw(self, context):
+        layout = self.layout
+
+        psys = context.particle_system
+        part = psys.settings
+
+        row = layout.row()
+        row.template_list("TEXTURE_UL_texslots", "", part, "texture_slots", part, "active_texture_index", rows=2)
+
+        col = row.column(align=True)
+        col.operator("texture.slot_move", text="", icon='TRIA_UP').type = 'UP'
+        col.operator("texture.slot_move", text="", icon='TRIA_DOWN').type = 'DOWN'
+        col.menu("TEXTURE_MT_specials", icon='DOWNARROW_HLT', text="")
+
+        if not part.active_texture:
+            layout.template_ID(part, "active_texture", new="texture.new")
+        else:
+            slot = part.texture_slots[part.active_texture_index]
+            layout.template_ID(slot, "texture", new="texture.new")
+
+
 class CyclesRender_PT_CurveRendering(CyclesButtonsPanel, Panel):
     bl_label = "Cycles Hair Rendering"
     bl_context = "particle"
@@ -1508,6 +1539,37 @@ class CyclesRender_PT_debug(CyclesButtonsPanel, Panel):
         col.prop(cscene, "debug_use_opencl_debug", text="Debug")
 
 
+class CyclesParticle_PT_CurveSettings(CyclesButtonsPanel, Panel):
+    bl_label = "Cycles Hair Settings"
+    bl_context = "particle"
+
+    @classmethod
+    def poll(cls, context):
+        scene = context.scene
+        ccscene = scene.cycles_curves
+        psys = context.particle_system
+        use_curves = ccscene.use_curves and psys
+        return CyclesButtonsPanel.poll(context) and use_curves and psys.settings.type == 'HAIR'
+
+    def draw(self, context):
+        layout = self.layout
+
+        psys = context.particle_settings
+        cpsys = psys.cycles
+
+        row = layout.row()
+        row.prop(cpsys, "shape", text="Shape")
+
+        layout.label(text="Thickness:")
+        row = layout.row()
+        row.prop(cpsys, "root_width", text="Root")
+        row.prop(cpsys, "tip_width", text="Tip")
+
+        row = layout.row()
+        row.prop(cpsys, "radius_scale", text="Scaling")
+        row.prop(cpsys, "use_closetip", text="Close tip")
+
+
 class CyclesScene_PT_simplify(CyclesButtonsPanel, Panel):
     bl_label = "Simplify"
     bl_context = "scene"
@@ -1533,6 +1595,12 @@ class CyclesScene_PT_simplify(CyclesButtonsPanel, Panel):
         row.prop(rd, "simplify_subdivision_render", text="Render")
 
 
+        col = layout.column(align=True)
+        col.label(text="Child Particles")
+        row = col.row(align=True)
+        row.prop(rd, "simplify_child_particles", text="Viewport")
+        row.prop(rd, "simplify_child_particles_render", text="Render")
+
         col = layout.column(align=True)
         split = col.split()
         sub = split.column()
index 7b9d4f2ecdfe88e1d9cd721a88f1301df816d2fe..378ae67f0c74173da45d623a36ca0ebf0d393f8c 100644 (file)
@@ -37,6 +37,9 @@ void curveinterp_v3_v3v3v3v3(float3 *p, float3 *v1, float3 *v2, float3 *v3, floa
 void interp_weights(float t, float data[4]);
 float shaperadius(float shape, float root, float tip, float time);
 void InterpolateKeySegments(int seg, int segno, int key, int curve, float3 *keyloc, float *time, ParticleCurveData *CData);
+bool ObtainCacheParticleUV(Mesh *mesh, BL::Mesh *b_mesh, BL::Object *b_ob, ParticleCurveData *CData, bool background, int uv_num);
+bool ObtainCacheParticleVcol(Mesh *mesh, BL::Mesh *b_mesh, BL::Object *b_ob, ParticleCurveData *CData, bool background, int vcol_num);
+bool ObtainCacheParticleData(Mesh *mesh, BL::Mesh *b_mesh, BL::Object *b_ob, ParticleCurveData *CData, bool background);
 void ExportCurveSegments(Scene *scene, Mesh *mesh, ParticleCurveData *CData);
 void ExportCurveTrianglePlanes(Mesh *mesh, ParticleCurveData *CData,
                                float3 RotCam, bool is_ortho);
@@ -116,6 +119,220 @@ void InterpolateKeySegments(int seg, int segno, int key, int curve, float3 *keyl
                curveinterp_v3_v3v3v3v3(keyloc, &ckey_loc1, &ckey_loc2, &ckey_loc3, &ckey_loc4, t);
 }
 
+bool ObtainCacheParticleData(Mesh *mesh, BL::Mesh *b_mesh, BL::Object *b_ob, ParticleCurveData *CData, bool background)
+{
+       int curvenum = 0;
+       int keyno = 0;
+
+       if(!(mesh && b_mesh && b_ob && CData))
+               return false;
+
+       Transform tfm = get_transform(b_ob->matrix_world());
+       Transform itfm = transform_quick_inverse(tfm);
+
+       BL::Object::modifiers_iterator b_mod;
+       for(b_ob->modifiers.begin(b_mod); b_mod != b_ob->modifiers.end(); ++b_mod) {
+               if((b_mod->type() == b_mod->type_PARTICLE_SYSTEM) && (background ? b_mod->show_render() : b_mod->show_viewport())) {
+                       BL::ParticleSystemModifier psmd((const PointerRNA)b_mod->ptr);
+                       BL::ParticleSystem b_psys((const PointerRNA)psmd.particle_system().ptr);
+                       BL::ParticleSettings b_part((const PointerRNA)b_psys.settings().ptr);
+
+                       if((b_part.render_type() == BL::ParticleSettings::render_type_PATH) && (b_part.type() == BL::ParticleSettings::type_HAIR)) {
+                               int shader = clamp(b_part.material()-1, 0, mesh->used_shaders.size()-1);
+                               int draw_step = background ? b_part.render_step() : b_part.draw_step();
+                               int totparts = b_psys.particles.length();
+                               int totchild = background ? b_psys.child_particles.length() : (int)((float)b_psys.child_particles.length() * (float)b_part.draw_percentage() / 100.0f);
+                               int totcurves = totchild;
+                               
+                               if(b_part.child_type() == 0 || totchild == 0)
+                                       totcurves += totparts;
+
+                               if(totcurves == 0)
+                                       continue;
+
+                               int ren_step = (1 << draw_step) + 1;
+                               if(b_part.kink() == BL::ParticleSettings::kink_SPIRAL)
+                                       ren_step += b_part.kink_extra_steps();
+
+                               PointerRNA cpsys = RNA_pointer_get(&b_part.ptr, "cycles");
+
+                               CData->psys_firstcurve.push_back_slow(curvenum);
+                               CData->psys_curvenum.push_back_slow(totcurves);
+                               CData->psys_shader.push_back_slow(shader);
+
+                               float radius = get_float(cpsys, "radius_scale") * 0.5f;
+       
+                               CData->psys_rootradius.push_back_slow(radius * get_float(cpsys, "root_width"));
+                               CData->psys_tipradius.push_back_slow(radius * get_float(cpsys, "tip_width"));
+                               CData->psys_shape.push_back_slow(get_float(cpsys, "shape"));
+                               CData->psys_closetip.push_back_slow(get_boolean(cpsys, "use_closetip"));
+
+                               int pa_no = 0;
+                               if(!(b_part.child_type() == 0) && totchild != 0)
+                                       pa_no = totparts;
+
+                               int num_add = (totparts+totchild - pa_no);
+                               CData->curve_firstkey.reserve(CData->curve_firstkey.size() + num_add);
+                               CData->curve_keynum.reserve(CData->curve_keynum.size() + num_add);
+                               CData->curve_length.reserve(CData->curve_length.size() + num_add);
+                               CData->curvekey_co.reserve(CData->curvekey_co.size() + num_add*ren_step);
+                               CData->curvekey_time.reserve(CData->curvekey_time.size() + num_add*ren_step);
+
+                               for(; pa_no < totparts+totchild; pa_no++) {
+                                       int keynum = 0;
+                                       CData->curve_firstkey.push_back_slow(keyno);
+                                       
+                                       float curve_length = 0.0f;
+                                       float3 pcKey;
+                                       for(int step_no = 0; step_no < ren_step; step_no++) {
+                                               float nco[3];
+                                               b_psys.co_hair(*b_ob, pa_no, step_no, nco);
+                                               float3 cKey = make_float3(nco[0], nco[1], nco[2]);
+                                               cKey = transform_point(&itfm, cKey);
+                                               if(step_no > 0) {
+                                                       float step_length = len(cKey - pcKey);
+                                                       if(step_length == 0.0f)
+                                                               continue;
+                                                       curve_length += step_length;
+                                               }
+                                               CData->curvekey_co.push_back_slow(cKey);
+                                               CData->curvekey_time.push_back_slow(curve_length);
+                                               pcKey = cKey;
+                                               keynum++;
+                                       }
+                                       keyno += keynum;
+
+                                       CData->curve_keynum.push_back_slow(keynum);
+                                       CData->curve_length.push_back_slow(curve_length);
+                                       curvenum++;
+                               }
+                       }
+               }
+       }
+
+       return true;
+}
+
+bool ObtainCacheParticleUV(Mesh *mesh, BL::Mesh *b_mesh, BL::Object *b_ob, ParticleCurveData *CData, bool background, int uv_num)
+{
+       if(!(mesh && b_mesh && b_ob && CData))
+               return false;
+
+       CData->curve_uv.clear();
+
+       BL::Object::modifiers_iterator b_mod;
+       for(b_ob->modifiers.begin(b_mod); b_mod != b_ob->modifiers.end(); ++b_mod) {
+               if((b_mod->type() == b_mod->type_PARTICLE_SYSTEM) && (background ? b_mod->show_render() : b_mod->show_viewport())) {
+                       BL::ParticleSystemModifier psmd((const PointerRNA)b_mod->ptr);
+                       BL::ParticleSystem b_psys((const PointerRNA)psmd.particle_system().ptr);
+                       BL::ParticleSettings b_part((const PointerRNA)b_psys.settings().ptr);
+
+                       if((b_part.render_type() == BL::ParticleSettings::render_type_PATH) && (b_part.type() == BL::ParticleSettings::type_HAIR)) {
+                               int totparts = b_psys.particles.length();
+                               int totchild = background ? b_psys.child_particles.length() : (int)((float)b_psys.child_particles.length() * (float)b_part.draw_percentage() / 100.0f);
+                               int totcurves = totchild;
+                               
+                               if(b_part.child_type() == 0 || totchild == 0)
+                                       totcurves += totparts;
+
+                               if(totcurves == 0)
+                                       continue;
+
+                               int pa_no = 0;
+                               if(!(b_part.child_type() == 0) && totchild != 0)
+                                       pa_no = totparts;
+
+                               int num_add = (totparts+totchild - pa_no);
+                               CData->curve_uv.reserve(CData->curve_uv.size() + num_add);
+
+                               BL::ParticleSystem::particles_iterator b_pa;
+                               b_psys.particles.begin(b_pa);
+                               for(; pa_no < totparts+totchild; pa_no++) {
+                                       /* Add UVs */
+                                       BL::Mesh::tessface_uv_textures_iterator l;
+                                       b_mesh->tessface_uv_textures.begin(l);
+
+                                       float3 uv = make_float3(0.0f, 0.0f, 0.0f);
+                                       if(b_mesh->tessface_uv_textures.length())
+                                               b_psys.uv_on_emitter(psmd, *b_pa, pa_no, uv_num, &uv.x);
+                                       CData->curve_uv.push_back_slow(uv);
+
+                                       if(pa_no < totparts && b_pa != b_psys.particles.end())
+                                               ++b_pa;
+                               }
+                       }
+               }
+       }
+
+       return true;
+}
+
+bool ObtainCacheParticleVcol(Mesh *mesh, BL::Mesh *b_mesh, BL::Object *b_ob, ParticleCurveData *CData, bool background, int vcol_num)
+{
+       if(!(mesh && b_mesh && b_ob && CData))
+               return false;
+
+       CData->curve_vcol.clear();
+
+       BL::Object::modifiers_iterator b_mod;
+       for(b_ob->modifiers.begin(b_mod); b_mod != b_ob->modifiers.end(); ++b_mod) {
+               if((b_mod->type() == b_mod->type_PARTICLE_SYSTEM) && (background ? b_mod->show_render() : b_mod->show_viewport())) {
+                       BL::ParticleSystemModifier psmd((const PointerRNA)b_mod->ptr);
+                       BL::ParticleSystem b_psys((const PointerRNA)psmd.particle_system().ptr);
+                       BL::ParticleSettings b_part((const PointerRNA)b_psys.settings().ptr);
+
+                       if((b_part.render_type() == BL::ParticleSettings::render_type_PATH) && (b_part.type() == BL::ParticleSettings::type_HAIR)) {
+                               int totparts = b_psys.particles.length();
+                               int totchild = background ? b_psys.child_particles.length() : (int)((float)b_psys.child_particles.length() * (float)b_part.draw_percentage() / 100.0f);
+                               int totcurves = totchild;
+                               
+                               if(b_part.child_type() == 0 || totchild == 0)
+                                       totcurves += totparts;
+
+                               if(totcurves == 0)
+                                       continue;
+
+                               int pa_no = 0;
+                               if(!(b_part.child_type() == 0) && totchild != 0)
+                                       pa_no = totparts;
+
+                               int num_add = (totparts+totchild - pa_no);
+                               CData->curve_vcol.reserve(CData->curve_vcol.size() + num_add);
+
+                               BL::ParticleSystem::particles_iterator b_pa;
+                               b_psys.particles.begin(b_pa);
+                               for(; pa_no < totparts+totchild; pa_no++) {
+                                       /* Add vertex colors */
+                                       BL::Mesh::tessface_vertex_colors_iterator l;
+                                       b_mesh->tessface_vertex_colors.begin(l);
+
+                                       float3 vcol = make_float3(0.0f, 0.0f, 0.0f);
+                                       if(b_mesh->tessface_vertex_colors.length())
+                                               b_psys.mcol_on_emitter(psmd, *b_pa, pa_no, vcol_num, &vcol.x);
+                                       CData->curve_vcol.push_back_slow(vcol);
+
+                                       if(pa_no < totparts && b_pa != b_psys.particles.end())
+                                               ++b_pa;
+                               }
+                       }
+               }
+       }
+
+       return true;
+}
+
+static void set_resolution(BL::Object *b_ob, BL::Scene *scene, bool render)
+{
+       BL::Object::modifiers_iterator b_mod;
+       for(b_ob->modifiers.begin(b_mod); b_mod != b_ob->modifiers.end(); ++b_mod) {
+               if((b_mod->type() == b_mod->type_PARTICLE_SYSTEM) && ((b_mod->show_viewport()) || (b_mod->show_render()))) {
+                       BL::ParticleSystemModifier psmd((const PointerRNA)b_mod->ptr);
+                       BL::ParticleSystem b_psys((const PointerRNA)psmd.particle_system().ptr);
+                       b_psys.set_resolution(*scene, *b_ob, (render)? 2: 1);
+               }
+       }
+}
+
 void ExportCurveTrianglePlanes(Mesh *mesh, ParticleCurveData *CData,
                                float3 RotCam, bool is_ortho)
 {
@@ -620,6 +837,20 @@ void BlenderSync::sync_curve_settings()
        }
 
        if(curve_system_manager->modified_mesh(prev_curve_system_manager)) {
+               BL::BlendData::objects_iterator b_ob;
+
+               for(b_data.objects.begin(b_ob); b_ob != b_data.objects.end(); ++b_ob) {
+                       if(object_is_mesh(*b_ob)) {
+                               BL::Object::particle_systems_iterator b_psys;
+                               for(b_ob->particle_systems.begin(b_psys); b_psys != b_ob->particle_systems.end(); ++b_psys) {
+                                       if((b_psys->settings().render_type()==BL::ParticleSettings::render_type_PATH)&&(b_psys->settings().type()==BL::ParticleSettings::type_HAIR)) {
+                                               BL::ID key = BKE_object_is_modified(*b_ob)? *b_ob: b_ob->data();
+                                               mesh_map.set_recalc(key);
+                                               object_map.set_recalc(*b_ob);
+                                       }
+                               }
+                       }
+               }
        }
 
        if(curve_system_manager->modified(prev_curve_system_manager))
@@ -644,7 +875,7 @@ void BlenderSync::sync_curves(Mesh *mesh,
        /* obtain general settings */
        bool use_curves = scene->curve_system_manager->use_curves;
 
-       if(!use_curves) {
+       if(!(use_curves && b_ob.mode() != b_ob.mode_PARTICLE_EDIT)) {
                if(!motion)
                        mesh->compute_bounds();
                return;
@@ -661,6 +892,11 @@ void BlenderSync::sync_curves(Mesh *mesh,
 
        ParticleCurveData CData;
 
+       if(!preview)
+               set_resolution(&b_ob, &b_scene, true);
+
+       ObtainCacheParticleData(mesh, &b_mesh, &b_ob, &CData, !preview);
+
        /* add hair geometry to mesh */
        if(primitive == CURVE_TRIANGLES) {
                if(triangle_method == CURVE_CAMERA_TRIANGLES) {
@@ -728,6 +964,8 @@ void BlenderSync::sync_curves(Mesh *mesh,
                        if(!mesh->need_attribute(scene, ustring(l->name().c_str())))
                                continue;
 
+                       ObtainCacheParticleVcol(mesh, &b_mesh, &b_ob, &CData, !preview, vcol_num);
+
                        if(primitive == CURVE_TRIANGLES) {
                                Attribute *attr_vcol = mesh->attributes.add(
                                        ustring(l->name().c_str()), TypeDesc::TypeColor, ATTR_ELEMENT_CORNER_BYTE);
@@ -767,6 +1005,8 @@ void BlenderSync::sync_curves(Mesh *mesh,
                        if(mesh->need_attribute(scene, name) || mesh->need_attribute(scene, std)) {
                                Attribute *attr_uv;
 
+                               ObtainCacheParticleUV(mesh, &b_mesh, &b_ob, &CData, !preview, uv_num);
+
                                if(primitive == CURVE_TRIANGLES) {
                                        if(active_render)
                                                attr_uv = mesh->attributes.add(std, name);
@@ -797,6 +1037,9 @@ void BlenderSync::sync_curves(Mesh *mesh,
                }
        }
 
+       if(!preview)
+               set_resolution(&b_ob, &b_scene, false);
+
        mesh->compute_bounds();
 }
 
index 5133134a6c5cc450d1c5dd959128dff11b86962d..637cf7abda8723188ebe9b6313b175187b10e3c6 100644 (file)
@@ -418,9 +418,29 @@ static bool object_render_hide(BL::Object& b_ob,
                                bool parent_hide,
                                bool& hide_triangles)
 {
+       /* check if we should render or hide particle emitter */
+       BL::Object::particle_systems_iterator b_psys;
+
+       bool hair_present = false;
+       bool show_emitter = false;
+       bool hide_emitter = false;
        bool hide_as_dupli_parent = false;
        bool hide_as_dupli_child_original = false;
 
+       for(b_ob.particle_systems.begin(b_psys); b_psys != b_ob.particle_systems.end(); ++b_psys) {
+               if((b_psys->settings().render_type() == BL::ParticleSettings::render_type_PATH) &&
+                  (b_psys->settings().type()==BL::ParticleSettings::type_HAIR))
+                       hair_present = true;
+
+               if(b_psys->settings().use_render_emitter())
+                       show_emitter = true;
+               else
+                       hide_emitter = true;
+       }
+
+       if(show_emitter)
+               hide_emitter = false;
+
        /* duplicators hidden by default, except dupliframes which duplicate self */
        if(b_ob.is_duplicator())
                if(top_level || b_ob.dupli_type() != BL::Object::dupli_type_FRAMES)
@@ -440,9 +460,17 @@ static bool object_render_hide(BL::Object& b_ob,
                parent = parent.parent();
        }
        
-       hide_triangles = false;
+       hide_triangles = hide_emitter;
 
-       return (hide_as_dupli_parent || hide_as_dupli_child_original);
+       if(show_emitter) {
+               return false;
+       }
+       else if(hair_present) {
+               return hide_as_dupli_child_original;
+       }
+       else {
+               return (hide_as_dupli_parent || hide_as_dupli_child_original);
+       }
 }
 
 static bool object_render_hide_duplis(BL::Object& b_ob)
@@ -465,6 +493,7 @@ void BlenderSync::sync_objects(BL::SpaceView3D& b_v3d, float motion_time)
                light_map.pre_sync();
                mesh_map.pre_sync();
                object_map.pre_sync();
+               particle_system_map.pre_sync();
                motion_times.clear();
        }
        else {
@@ -526,15 +555,22 @@ void BlenderSync::sync_objects(BL::SpaceView3D& b_v3d, float motion_time)
                                                        BL::Array<int, OBJECT_PERSISTENT_ID_SIZE> persistent_id = b_dup->persistent_id();
 
                                                        /* sync object and mesh or light data */
-                                                       sync_object(b_ob,
-                                                                   persistent_id.data,
-                                                                   *b_dup,
-                                                                   tfm,
-                                                                   ob_layer,
-                                                                   motion_time,
-                                                                   hide_tris,
-                                                                   culling,
-                                                                   &use_portal);
+                                                       Object *object = sync_object(b_ob,
+                                                                                    persistent_id.data,
+                                                                                    *b_dup,
+                                                                                    tfm,
+                                                                                    ob_layer,
+                                                                                    motion_time,
+                                                                                    hide_tris,
+                                                                                    culling,
+                                                                                    &use_portal);
+
+                                                       /* sync possible particle data, note particle_id
+                                                        * starts counting at 1, first is dummy particle */
+                                                       if(!motion && object) {
+                                                               sync_dupli_particle(b_ob, *b_dup, object);
+                                                       }
+
                                                }
                                        }
 
@@ -576,6 +612,8 @@ void BlenderSync::sync_objects(BL::SpaceView3D& b_v3d, float motion_time)
                        scene->mesh_manager->tag_update(scene);
                if(object_map.post_sync())
                        scene->object_manager->tag_update(scene);
+               if(particle_system_map.post_sync())
+                       scene->particle_system_manager->tag_update(scene);
        }
 
        if(motion)
diff --git a/intern/cycles/blender/blender_particles.cpp b/intern/cycles/blender/blender_particles.cpp
new file mode 100644 (file)
index 0000000..dd2900a
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2011-2013 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "mesh.h"
+#include "object.h"
+#include "particles.h"
+
+#include "blender_sync.h"
+#include "blender_util.h"
+
+#include "util_foreach.h"
+
+CCL_NAMESPACE_BEGIN
+
+/* Utilities */
+
+bool BlenderSync::sync_dupli_particle(BL::Object& b_ob,
+                                      BL::DupliObject& b_dup,
+                                      Object *object)
+{
+       /* test if this dupli was generated from a particle sytem */
+       BL::ParticleSystem b_psys = b_dup.particle_system();
+       if(!b_psys)
+               return false;
+
+       object->hide_on_missing_motion = true;
+
+       /* test if we need particle data */
+       if(!object->mesh->need_attribute(scene, ATTR_STD_PARTICLE))
+               return false;
+
+       /* don't handle child particles yet */
+       BL::Array<int, OBJECT_PERSISTENT_ID_SIZE> persistent_id = b_dup.persistent_id();
+
+       if(persistent_id[0] >= b_psys.particles.length())
+               return false;
+
+       /* find particle system */
+       ParticleSystemKey key(b_ob, persistent_id);
+       ParticleSystem *psys;
+
+       bool first_use = !particle_system_map.is_used(key);
+       bool need_update = particle_system_map.sync(&psys, b_ob, b_dup.object(), key);
+
+       /* no update needed? */
+       if(!need_update && !object->mesh->need_update && !scene->object_manager->need_update)
+               return true;
+
+       /* first time used in this sync loop? clear and tag update */
+       if(first_use) {
+               psys->particles.clear();
+               psys->tag_update(scene);
+       }
+
+       /* add particle */
+       BL::Particle b_pa = b_psys.particles[persistent_id[0]];
+       Particle pa;
+       
+       pa.index = persistent_id[0];
+       pa.age = b_scene.frame_current() - b_pa.birth_time();
+       pa.lifetime = b_pa.lifetime();
+       pa.location = get_float3(b_pa.location());
+       pa.rotation = get_float4(b_pa.rotation());
+       pa.size = b_pa.size();
+       pa.velocity = get_float3(b_pa.velocity());
+       pa.angular_velocity = get_float3(b_pa.angular_velocity());
+
+       psys->particles.push_back_slow(pa);
+
+       if(object->particle_index != psys->particles.size() - 1)
+               scene->object_manager->tag_update(scene);
+       object->particle_system = psys;
+       object->particle_index = psys->particles.size() - 1;
+
+       /* return that this object has particle data */
+       return true;
+}
+
+CCL_NAMESPACE_END
index cfb32651c508f62081cabc531e5b49ccbb129396..38b2ce19e8aa7e81e6a1c0f4cbf45c37119f39f7 100644 (file)
@@ -56,6 +56,7 @@ BlenderSync::BlenderSync(BL::RenderEngine& b_engine,
   object_map(&scene->objects),
   mesh_map(&scene->meshes),
   light_map(&scene->lights),
+  particle_system_map(&scene->particle_systems),
   world_map(NULL),
   world_recalc(false),
   scene(scene),
@@ -143,6 +144,12 @@ bool BlenderSync::sync_recalc()
                        if(b_ob->is_updated_data() || b_ob->data().is_updated())
                                light_map.set_recalc(*b_ob);
                }
+               
+               if(b_ob->is_updated_data()) {
+                       BL::Object::particle_systems_iterator b_psys;
+                       for(b_ob->particle_systems.begin(b_psys); b_psys != b_ob->particle_systems.end(); ++b_psys)
+                               particle_system_map.set_recalc(*b_ob);
+               }
        }
 
        BL::BlendData::meshes_iterator b_mesh;
@@ -176,6 +183,7 @@ bool BlenderSync::sync_recalc()
                object_map.has_recalc() ||
                light_map.has_recalc() ||
                mesh_map.has_recalc() ||
+               particle_system_map.has_recalc() ||
                BlendDataObjects_is_updated_get(&b_data.ptr) ||
                world_recalc;
 
index 08e0a9bd82f3ac9ef2e66aedcb40d26039f925e0..6984cbda2596787ace976f5e0a2e5394deabe366 100644 (file)
@@ -139,6 +139,11 @@ private:
                                int width, int height,
                                float motion_time);
 
+       /* particles */
+       bool sync_dupli_particle(BL::Object& b_ob,
+                                BL::DupliObject& b_dup,
+                                Object *object);
+
        /* Images. */
        void sync_images();
 
@@ -157,6 +162,7 @@ private:
        id_map<ObjectKey, Object> object_map;
        id_map<void*, Mesh> mesh_map;
        id_map<ObjectKey, Light> light_map;
+       id_map<ParticleSystemKey, ParticleSystem> particle_system_map;
        set<Mesh*> mesh_synced;
        set<Mesh*> mesh_motion_synced;
        set<float> motion_times;
index ba696a83867ef0a842b5cd0e6120e1dd02c3f7fa..f17a61f0ac88c74f99d72d4f6643c22117734c22 100644 (file)
@@ -754,6 +754,33 @@ struct ObjectKey {
        }
 };
 
+/* Particle System Key */
+
+struct ParticleSystemKey {
+       void *ob;
+       int id[OBJECT_PERSISTENT_ID_SIZE];
+
+       ParticleSystemKey(void *ob_, int id_[OBJECT_PERSISTENT_ID_SIZE])
+       : ob(ob_)
+       {
+               if(id_)
+                       memcpy(id, id_, sizeof(id));
+               else
+                       memset(id, 0, sizeof(id));
+       }
+
+       bool operator<(const ParticleSystemKey& k) const
+       {
+               /* first id is particle index, we don't compare that */
+               if(ob < k.ob)
+                       return true;
+               else if(ob == k.ob)
+                       return memcmp(id+1, k.id+1, sizeof(int)*(OBJECT_PERSISTENT_ID_SIZE-1)) < 0;
+
+               return false;
+       }
+};
+
 CCL_NAMESPACE_END
 
 #endif /* __BLENDER_UTIL_H__ */
index 3a4c18ab1893b845c119b5fb6d4185b1dc984a72..bd50b6f08dcc0251ee9db11e3ecd609619a81d53 100644 (file)
@@ -111,7 +111,7 @@ typedef struct elbeemSimulationSettings {
 #define OB_FLUIDSIM_OBSTACLE    8
 #define OB_FLUIDSIM_INFLOW      16
 #define OB_FLUIDSIM_OUTFLOW     32
-#define OB_FLUIDSIM_PARTICLE    64 /* DEPRECATED */
+#define OB_FLUIDSIM_PARTICLE    64
 #define OB_FLUIDSIM_CONTROL    128
 
 // defines for elbeemMesh->obstacleType below (low bits) high bits (>=64) are reserved for mFsSurfGenSetting flags which are defined in solver_class.h
diff --git a/release/scripts/presets/hair_dynamics/default.py b/release/scripts/presets/hair_dynamics/default.py
new file mode 100644 (file)
index 0000000..830d28a
--- /dev/null
@@ -0,0 +1,17 @@
+import bpy
+psys = bpy.context.particle_system
+cloth = bpy.context.particle_system.cloth
+settings = bpy.context.particle_system.cloth.settings
+collision = bpy.context.particle_system.cloth.collision_settings
+
+settings.quality = 5
+settings.mass = 0.30000001192092896
+settings.bending_stiffness = 0.5
+psys.settings.bending_random = 0.0
+settings.bending_damping = 0.5
+settings.air_damping = 1.0
+settings.internal_friction = 0.0
+settings.density_target = 0.0
+settings.density_strength = 0.0
+settings.voxel_cell_size = 0.10000000149011612
+settings.pin_stiffness = 0.0
index 3e1bb54f04382a01221c95f159e92c1a13e90542..cdab380bb9c094c929506422c1e0de28d5059780 100644 (file)
@@ -47,6 +47,239 @@ def object_ensure_material(obj, mat_name):
     return mat
 
 
+class QuickFur(Operator):
+    bl_idname = "object.quick_fur"
+    bl_label = "Quick Fur"
+    bl_options = {'REGISTER', 'UNDO'}
+
+    density = EnumProperty(
+            name="Fur Density",
+            items=(('LIGHT', "Light", ""),
+                   ('MEDIUM', "Medium", ""),
+                   ('HEAVY', "Heavy", "")),
+            default='MEDIUM',
+            )
+    view_percentage = IntProperty(
+            name="View %",
+            min=1, max=100,
+            soft_min=1, soft_max=100,
+            default=10,
+            )
+    length = FloatProperty(
+            name="Length",
+            min=0.001, max=100,
+            soft_min=0.01, soft_max=10,
+            default=0.1,
+            )
+
+    def execute(self, context):
+        fake_context = context.copy()
+        mesh_objects = [obj for obj in context.selected_objects
+                        if obj.type == 'MESH' and obj.mode == 'OBJECT']
+
+        if not mesh_objects:
+            self.report({'ERROR'}, "Select at least one mesh object")
+            return {'CANCELLED'}
+
+        mat = bpy.data.materials.new("Fur Material")
+        mat.strand.tip_size = 0.25
+        mat.strand.blend_distance = 0.5
+
+        for obj in mesh_objects:
+            fake_context["object"] = obj
+            bpy.ops.object.particle_system_add(fake_context)
+
+            psys = obj.particle_systems[-1]
+            psys.settings.type = 'HAIR'
+
+            if self.density == 'LIGHT':
+                psys.settings.count = 100
+            elif self.density == 'MEDIUM':
+                psys.settings.count = 1000
+            elif self.density == 'HEAVY':
+                psys.settings.count = 10000
+
+            psys.settings.child_nbr = self.view_percentage
+            psys.settings.hair_length = self.length
+            psys.settings.use_strand_primitive = True
+            psys.settings.use_hair_bspline = True
+            psys.settings.child_type = 'INTERPOLATED'
+
+            obj.data.materials.append(mat)
+            psys.settings.material = len(obj.data.materials)
+
+        return {'FINISHED'}
+
+
+class QuickExplode(Operator):
+    bl_idname = "object.quick_explode"
+    bl_label = "Quick Explode"
+    bl_options = {'REGISTER', 'UNDO'}
+
+    style = EnumProperty(
+            name="Explode Style",
+            items=(('EXPLODE', "Explode", ""),
+                   ('BLEND', "Blend", "")),
+            default='EXPLODE',
+            )
+    amount = IntProperty(
+            name="Amount of pieces",
+            min=2, max=10000,
+            soft_min=2, soft_max=10000,
+            default=100,
+            )
+    frame_duration = IntProperty(
+            name="Duration",
+            min=1, max=300000,
+            soft_min=1, soft_max=10000,
+            default=50,
+            )
+
+    frame_start = IntProperty(
+            name="Start Frame",
+            min=1, max=300000,
+            soft_min=1, soft_max=10000,
+            default=1,
+            )
+    frame_end = IntProperty(
+            name="End Frame",
+            min=1, max=300000,
+            soft_min=1, soft_max=10000,
+            default=10,
+            )
+
+    velocity = FloatProperty(
+            name="Outwards Velocity",
+            min=0, max=300000,
+            soft_min=0, soft_max=10,
+            default=1,
+            )
+
+    fade = BoolProperty(
+            name="Fade",
+            description="Fade the pieces over time",
+            default=True,
+            )
+
+    def execute(self, context):
+        fake_context = context.copy()
+        obj_act = context.active_object
+
+        if obj_act is None or obj_act.type != 'MESH':
+            self.report({'ERROR'}, "Active object is not a mesh")
+            return {'CANCELLED'}
+
+        mesh_objects = [obj for obj in context.selected_objects
+                        if obj.type == 'MESH' and obj != obj_act]
+        mesh_objects.insert(0, obj_act)
+
+        if self.style == 'BLEND' and len(mesh_objects) != 2:
+            self.report({'ERROR'}, "Select two mesh objects")
+            self.style = 'EXPLODE'
+            return {'CANCELLED'}
+        elif not mesh_objects:
+            self.report({'ERROR'}, "Select at least one mesh object")
+            return {'CANCELLED'}
+
+        for obj in mesh_objects:
+            if obj.particle_systems:
+                self.report({'ERROR'},
+                            "Object %r already has a "
+                            "particle system" % obj.name)
+
+                return {'CANCELLED'}
+
+        if self.fade:
+            tex = bpy.data.textures.new("Explode fade", 'BLEND')
+            tex.use_color_ramp = True
+
+            if self.style == 'BLEND':
+                tex.color_ramp.elements[0].position = 0.333
+                tex.color_ramp.elements[1].position = 0.666
+
+            tex.color_ramp.elements[0].color[3] = 1.0
+            tex.color_ramp.elements[1].color[3] = 0.0
+
+        if self.style == 'BLEND':
+            from_obj = mesh_objects[1]
+            to_obj = mesh_objects[0]
+
+        for obj in mesh_objects:
+            fake_context["object"] = obj
+            bpy.ops.object.particle_system_add(fake_context)
+
+            settings = obj.particle_systems[-1].settings
+            settings.count = self.amount
+            settings.frame_start = self.frame_start
+            settings.frame_end = self.frame_end - self.frame_duration
+            settings.lifetime = self.frame_duration
+            settings.normal_factor = self.velocity
+            settings.render_type = 'NONE'
+
+            explode = obj.modifiers.new(name='Explode', type='EXPLODE')
+            explode.use_edge_cut = True
+
+            if self.fade:
+                explode.show_dead = False
+                uv = obj.data.uv_textures.new("Explode fade")
+                explode.particle_uv = uv.name
+
+                mat = object_ensure_material(obj, "Explode Fade")
+
+                mat.use_transparency = True
+                mat.use_transparent_shadows = True
+                mat.alpha = 0.0
+                mat.specular_alpha = 0.0
+
+                tex_slot = mat.texture_slots.add()
+
+                tex_slot.texture = tex
+                tex_slot.texture_coords = 'UV'
+                tex_slot.uv_layer = uv.name
+
+                tex_slot.use_map_alpha = True
+
+                if self.style == 'BLEND':
+                    if obj == to_obj:
+                        tex_slot.alpha_factor = -1.0
+                        elem = tex.color_ramp.elements[1]
+                    else:
+                        elem = tex.color_ramp.elements[0]
+                    # Keep already defined alpha!
+                    elem.color[:3] = mat.diffuse_color
+                else:
+                    tex_slot.use_map_color_diffuse = False
+
+            if self.style == 'BLEND':
+                settings.physics_type = 'KEYED'
+                settings.use_emit_random = False
+                settings.rotation_mode = 'NOR'
+
+                psys = obj.particle_systems[-1]
+
+                fake_context["particle_system"] = obj.particle_systems[-1]
+                bpy.ops.particle.new_target(fake_context)
+                bpy.ops.particle.new_target(fake_context)
+
+                if obj == from_obj:
+                    psys.targets[1].object = to_obj
+                else:
+                    psys.targets[0].object = from_obj
+                    settings.normal_factor = -self.velocity
+                    explode.show_unborn = False
+                    explode.show_dead = True
+            else:
+                settings.factor_random = self.velocity
+                settings.angular_velocity_factor = self.velocity / 10.0
+
+        return {'FINISHED'}
+
+    def invoke(self, context, event):
+        self.frame_start = context.scene.frame_current
+        self.frame_end = self.frame_start + self.frame_duration
+        return self.execute(context)
+
+
 def obj_bb_minmax(obj, min_co, max_co):
     for i in range(0, 8):
         bb_vec = obj.matrix_world * Vector(obj.bound_box[i])
index ea2794195e3cda2e1c4e1f5bc8c9d9cff34ca23b..e01e509b2927db1c12c07a85b4a56351521a983c 100644 (file)
@@ -384,6 +384,36 @@ class AddPresetFluid(AddPresetBase, Operator):
     preset_subdir = "fluid"
 
 
+class AddPresetHairDynamics(AddPresetBase, Operator):
+    """Add or remove a Hair Dynamics Preset"""
+    bl_idname = "particle.hair_dynamics_preset_add"
+    bl_label = "Add Hair Dynamics Preset"
+    preset_menu = "PARTICLE_MT_hair_dynamics_presets"
+
+    preset_defines = [
+        "psys = bpy.context.particle_system",
+        "cloth = bpy.context.particle_system.cloth",
+        "settings = bpy.context.particle_system.cloth.settings",
+        "collision = bpy.context.particle_system.cloth.collision_settings",
+    ]
+
+    preset_subdir = "hair_dynamics"
+
+    preset_values = [
+        "settings.quality",
+        "settings.mass",
+        "settings.bending_stiffness",
+        "psys.settings.bending_random",
+        "settings.bending_damping",
+        "settings.air_damping",
+        "settings.internal_friction",
+        "settings.density_target",
+        "settings.density_strength",
+        "settings.voxel_cell_size",
+        "settings.pin_stiffness",
+        ]
+
+
 class AddPresetSunSky(AddPresetBase, Operator):
     """Add or remove a Sky & Atmosphere Preset"""
     bl_idname = "lamp.sunsky_preset_add"
index 1ef9354ed0eca41015e8ac8ebda47a1a99710ee6..df4a93bb87faefe88e8589bc34f3d5415e8f87a2 100644 (file)
@@ -199,6 +199,8 @@ class VIEW3D_OT_select_or_deselect_all(Operator):
                         bpy.ops.armature.select_all(action='DESELECT')
                 elif active_object.mode == 'POSE':
                     bpy.ops.pose.select_all(action='DESELECT')
+                elif active_object.mode == 'PARTICLE_EDIT':
+                    bpy.ops.particle.select_all(action='DESELECT')
                 else:
                     bpy.ops.object.select_all(action='DESELECT')
             else:
index ac1503921095114e0582a3d3d063f83e90b22f51..2389be6787d2dfc5cd7f213918e357b754a147f5 100644 (file)
@@ -47,6 +47,7 @@ _modules = [
     "properties_object",
     "properties_paint_common",
     "properties_grease_pencil_common",
+    "properties_particle",
     "properties_physics_cloth",
     "properties_physics_common",
     "properties_physics_dynamicpaint",
index ad357b109272550893bda11bec2d1fd1b8282520..d66fb08bcd67b9c055dfbfccf48dabb06ff78aec 100644 (file)
@@ -695,6 +695,40 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel):
 
         col = split.column()
 
+    def PARTICLE_INSTANCE(self, layout, ob, md):
+        layout.prop(md, "object")
+        layout.prop(md, "particle_system_index", text="Particle System")
+
+        split = layout.split()
+        col = split.column()
+        col.label(text="Create From:")
+        col.prop(md, "use_normal")
+        col.prop(md, "use_children")
+        col.prop(md, "use_size")
+
+        col = split.column()
+        col.label(text="Show Particles When:")
+        col.prop(md, "show_alive")
+        col.prop(md, "show_unborn")
+        col.prop(md, "show_dead")
+
+        layout.separator()
+
+        layout.prop(md, "use_path", text="Create Along Paths")
+
+        split = layout.split()
+        split.active = md.use_path
+        col = split.column()
+        col.row().prop(md, "axis", expand=True)
+        col.prop(md, "use_preserve_shape")
+
+        col = split.column()
+        col.prop(md, "position", slider=True)
+        col.prop(md, "random_position", text="Random", slider=True)
+
+    def PARTICLE_SYSTEM(self, layout, ob, md):
+        layout.label(text="Settings can be found inside the Particle context")
+
     def SCREW(self, layout, ob, md):
         split = layout.split()
 
index c7bae0d87d645738bf6c96cbe4b5ab6606de8529..09a3a19cbcee0ed011542817932684ff0d7fffe9 100644 (file)
@@ -40,6 +40,8 @@ class UnifiedPaintPanel:
                 return toolsettings.image_paint
 
             return None
+        elif context.particle_edit_object:
+            return toolsettings.particle_edit
 
         return None
 
diff --git a/release/scripts/startup/bl_ui/properties_particle.py b/release/scripts/startup/bl_ui/properties_particle.py
new file mode 100644 (file)
index 0000000..4e2666d
--- /dev/null
@@ -0,0 +1,1408 @@
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software Foundation,
+#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+# <pep8 compliant>
+import bpy
+from bpy.types import Panel, Menu
+from rna_prop_ui import PropertyPanel
+from bpy.app.translations import pgettext_iface as iface_
+
+from bl_ui.properties_physics_common import (
+        point_cache_ui,
+        effector_weights_ui,
+        basic_force_field_settings_ui,
+        basic_force_field_falloff_ui,
+        )
+
+
+def particle_panel_enabled(context, psys):
+    if psys is None:
+        return True
+    phystype = psys.settings.physics_type
+    if psys.settings.type in {'EMITTER', 'REACTOR'} and phystype in {'NO', 'KEYED'}:
+        return True
+    else:
+        return (psys.point_cache.is_baked is False) and (not psys.is_edited) and (not context.particle_system_editable)
+
+
+def particle_panel_poll(cls, context):
+    psys = context.particle_system
+    engine = context.scene.render.engine
+    settings = 0
+
+    if psys:
+        settings = psys.settings
+    elif isinstance(context.space_data.pin_id, bpy.types.ParticleSettings):
+        settings = context.space_data.pin_id
+
+    if not settings:
+        return False
+
+    return settings.is_fluid is False and (engine in cls.COMPAT_ENGINES)
+
+
+def particle_get_settings(context):
+    if context.particle_system:
+        return context.particle_system.settings
+    elif isinstance(context.space_data.pin_id, bpy.types.ParticleSettings):
+        return context.space_data.pin_id
+    return None
+
+
+class PARTICLE_MT_specials(Menu):
+    bl_label = "Particle Specials"
+    COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'}
+
+    def draw(self, context):
+        layout = self.layout
+
+        props = layout.operator("particle.copy_particle_systems", text="Copy Active to Selected Objects")
+        props.use_active = True
+        props.remove_target_particles = False
+
+        props = layout.operator("particle.copy_particle_systems", text="Copy All to Selected Objects")
+        props.use_active = False
+        props.remove_target_particles = True
+
+        layout.operator("particle.duplicate_particle_system")
+
+
+class PARTICLE_MT_hair_dynamics_presets(Menu):
+    bl_label = "Hair Dynamics Presets"
+    preset_subdir = "hair_dynamics"
+    preset_operator = "script.execute_preset"
+    COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'}
+    draw = Menu.draw_preset
+
+
+class ParticleButtonsPanel:
+    bl_space_type = 'PROPERTIES'
+    bl_region_type = 'WINDOW'
+    bl_context = "particle"
+
+    @classmethod
+    def poll(cls, context):
+        return particle_panel_poll(cls, context)
+
+
+def find_modifier(ob, psys):
+    for md in ob.modifiers:
+        if md.type == 'PARTICLE_SYSTEM':
+            if md.particle_system == psys:
+                return md
+
+
+class PARTICLE_UL_particle_systems(bpy.types.UIList):
+    def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index, flt_flag):
+        ob = data
+        psys = item
+
+        if self.layout_type in {'DEFAULT', 'COMPACT'}:
+            md = find_modifier(ob, psys)
+
+            layout.prop(psys, "name", text="", emboss=False, icon_value=icon)
+            if md:
+                layout.prop(md, "show_render", emboss=False, icon_only=True, icon='RESTRICT_RENDER_OFF' if md.show_render else 'RESTRICT_RENDER_ON')
+                layout.prop(md, "show_viewport", emboss=False, icon_only=True, icon='RESTRICT_VIEW_OFF' if md.show_viewport else 'RESTRICT_VIEW_ON')
+
+        elif self.layout_type == 'GRID':
+            layout.alignment = 'CENTER'
+            layout.label(text="", icon_value=icon)
+
+
+class PARTICLE_PT_context_particles(ParticleButtonsPanel, Panel):
+    bl_label = ""
+    bl_options = {'HIDE_HEADER'}
+    COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'}
+
+    @classmethod
+    def poll(cls, context):
+        engine = context.scene.render.engine
+        return (context.particle_system or context.object or context.space_data.pin_id) and (engine in cls.COMPAT_ENGINES)
+
+    def draw(self, context):
+        layout = self.layout
+
+        if context.scene.render.engine == 'BLENDER_GAME':
+            layout.label("Not available in the Game Engine")
+            return
+
+        ob = context.object
+        psys = context.particle_system
+        part = 0
+
+        if ob:
+            row = layout.row()
+
+            row.template_list("PARTICLE_UL_particle_systems", "particle_systems", ob, "particle_systems",
+                              ob.particle_systems, "active_index", rows=1)
+
+            col = row.column(align=True)
+            col.operator("object.particle_system_add", icon='ZOOMIN', text="")
+            col.operator("object.particle_system_remove", icon='ZOOMOUT', text="")
+            col.menu("PARTICLE_MT_specials", icon='DOWNARROW_HLT', text="")
+
+        if psys is None:
+            part = particle_get_settings(context)
+
+            layout.operator("object.particle_system_add", icon='ZOOMIN', text="New")
+
+            if part is None:
+                return
+
+            layout.template_ID(context.space_data, "pin_id")
+
+            if part.is_fluid:
+                layout.label(text="Settings used for fluid")
+                return
+
+            layout.prop(part, "type", text="Type")
+
+        elif not psys.settings:
+            split = layout.split(percentage=0.32)
+
+            col = split.column()
+            col.label(text="Settings:")
+
+            col = split.column()
+            col.template_ID(psys, "settings", new="particle.new")
+        else:
+            part = psys.settings
+
+            split = layout.split(percentage=0.32)
+            col = split.column()
+            if part.is_fluid is False:
+                col.label(text="Settings:")
+                col.label(text="Type:")
+
+            col = split.column()
+            if part.is_fluid is False:
+                row = col.row()
+                row.enabled = particle_panel_enabled(context, psys)
+                row.template_ID(psys, "settings", new="particle.new")
+
+            if part.is_fluid:
+                layout.label(text=iface_("%d fluid particles for this frame") % part.count, translate=False)
+                return
+
+            row = col.row()
+            row.enabled = particle_panel_enabled(context, psys)
+            row.prop(part, "type", text="")
+            row.prop(psys, "seed")
+
+        if part:
+            split = layout.split(percentage=0.65)
+            if part.type == 'HAIR':
+                if psys is not None and psys.is_edited:
+                    split.operator("particle.edited_clear", text="Free Edit")
+                else:
+                    row = split.row()
+                    row.enabled = particle_panel_enabled(context, psys)
+                    row.prop(part, "regrow_hair")
+                    row.prop(part, "use_advanced_hair")
+                row = split.row()
+                row.enabled = particle_panel_enabled(context, psys)
+                row.prop(part, "hair_step")
+                if psys is not None and psys.is_edited:
+                    if psys.is_global_hair:
+                        row = layout.row(align=True)
+                        row.operator("particle.connect_hair").all = False
+                        row.operator("particle.connect_hair", text="Connect All").all = True
+                    else:
+                        row = layout.row(align=True)
+                        row.operator("particle.disconnect_hair").all = False
+                        row.operator("particle.disconnect_hair", text="Disconnect All").all = True
+            elif psys is not None and part.type == 'REACTOR':
+                split.enabled = particle_panel_enabled(context, psys)
+                split.prop(psys, "reactor_target_object")
+                split.prop(psys, "reactor_target_particle_system", text="Particle System")
+
+
+class PARTICLE_PT_emission(ParticleButtonsPanel, Panel):
+    bl_label = "Emission"
+    COMPAT_ENGINES = {'BLENDER_RENDER'}
+
+    @classmethod
+    def poll(cls, context):
+        psys = context.particle_system
+        settings = particle_get_settings(context)
+
+        if settings is None:
+            return False
+        if settings.is_fluid:
+            return False
+        if particle_panel_poll(PARTICLE_PT_emission, context):
+            return psys is None or not context.particle_system.point_cache.use_external
+        return False
+
+    def draw(self, context):
+        layout = self.layout
+
+        psys = context.particle_system
+        part = particle_get_settings(context)
+
+        layout.enabled = particle_panel_enabled(context, psys) and (psys is None or not psys.has_multiple_caches)
+
+        row = layout.row()
+        row.active = part.emit_from == 'VERT' or part.distribution != 'GRID'
+        row.prop(part, "count")
+
+        if part.type == 'HAIR':
+            row.prop(part, "hair_length")
+            if not part.use_advanced_hair:
+                row = layout.row()
+                row.prop(part, "use_modifier_stack")
+                return
+
+        if part.type != 'HAIR':
+            split = layout.split()
+
+            col = split.column(align=True)
+            col.prop(part, "frame_start")
+            col.prop(part, "frame_end")
+
+            col = split.column(align=True)
+            col.prop(part, "lifetime")
+            col.prop(part, "lifetime_random", slider=True)
+
+        layout.label(text="Emit From:")
+        layout.prop(part, "emit_from", expand=True)
+
+        row = layout.row()
+        if part.emit_from == 'VERT':
+            row.prop(part, "use_emit_random")
+        elif part.distribution == 'GRID':
+            row.prop(part, "invert_grid")
+            row.prop(part, "hexagonal_grid")
+        else:
+            row.prop(part, "use_emit_random")
+            row.prop(part, "use_even_distribution")
+
+        if part.emit_from == 'FACE' or part.emit_from == 'VOLUME':
+            layout.prop(part, "distribution", expand=True)
+
+            row = layout.row()
+            if part.distribution == 'JIT':
+                row.prop(part, "userjit", text="Particles/Face")
+                row.prop(part, "jitter_factor", text="Jittering Amount", slider=True)
+            elif part.distribution == 'GRID':
+                row.prop(part, "grid_resolution")
+                row.prop(part, "grid_random", text="Random", slider=True)
+
+        row = layout.row()
+        row.prop(part, "use_modifier_stack")
+
+
+class PARTICLE_PT_hair_dynamics(ParticleButtonsPanel, Panel):
+    bl_label = "Hair dynamics"
+    bl_options = {'DEFAULT_CLOSED'}
+    COMPAT_ENGINES = {'BLENDER_RENDER'}
+
+    @classmethod
+    def poll(cls, context):
+        psys = context.particle_system
+        engine = context.scene.render.engine
+        if psys is None:
+            return False
+        if psys.settings is None:
+            return False
+        return psys.settings.type == 'HAIR' and (engine in cls.COMPAT_ENGINES)
+
+    def draw_header(self, context):
+        psys = context.particle_system
+        self.layout.prop(psys, "use_hair_dynamics", text="")
+
+    def draw(self, context):
+        layout = self.layout
+
+        psys = context.particle_system
+
+        if not psys.cloth:
+            return
+
+        cloth_md = psys.cloth
+        cloth = cloth_md.settings
+        result = cloth_md.solver_result
+
+        layout.enabled = psys.use_hair_dynamics and psys.point_cache.is_baked is False
+
+        row = layout.row(align=True)
+        row.menu("PARTICLE_MT_hair_dynamics_presets", text=bpy.types.PARTICLE_MT_hair_dynamics_presets.bl_label)
+        row.operator("particle.hair_dynamics_preset_add", text="", icon='ZOOMIN')
+        row.operator("particle.hair_dynamics_preset_add", text="", icon='ZOOMOUT').remove_active = True
+
+        split = layout.column()
+
+        col = split.column()
+        col.label(text="Structure")
+        col.prop(cloth, "mass")
+        sub = col.column(align=True)
+        subsub = sub.row(align=True)
+        subsub.prop(cloth, "bending_stiffness", text="Stiffness")
+        subsub.prop(psys.settings, "bending_random", text="Random")
+        sub.prop(cloth, "bending_damping", text="Damping")
+        # XXX has no noticeable effect with stiff hair structure springs
+        #col.prop(cloth, "spring_damping", text="Damping")
+
+        split.separator()
+
+        col = split.column()
+        col.label(text="Volume")
+        col.prop(cloth, "air_damping", text="Air Drag")
+        col.prop(cloth, "internal_friction", slider=True)
+        sub = col.column(align=True)
+        sub.prop(cloth, "density_target", text="Density Target")
+        sub.prop(cloth, "density_strength", slider=True, text="Strength")
+        col.prop(cloth, "voxel_cell_size")
+
+        split.separator()
+
+        col = split.column()
+        col.label(text="Pinning")
+        col.prop(cloth, "pin_stiffness", text="Goal Strength")
+
+        split.separator()
+
+        col = split.column()
+        col.label(text="Quality:")
+        col.prop(cloth, "quality", text="Steps", slider=True)
+
+        row = col.row()
+        row.prop(psys.settings, "show_hair_grid", text="HairGrid")
+
+        if result:
+            box = layout.box()
+
+            if not result.status:
+                label = " "
+                icon = 'NONE'
+            elif result.status == {'SUCCESS'}:
+                label = "Success"
+                icon = 'NONE'
+            elif result.status - {'SUCCESS'} == {'NO_CONVERGENCE'}:
+                label = "No Convergence"
+                icon = 'ERROR'
+            else:
+                label = "ERROR"
+                icon = 'ERROR'
+            box.label(label, icon=icon)
+            box.label("Iterations: %d .. %d (avg. %d)" % (result.min_iterations, result.max_iterations, result.avg_iterations))
+            box.label("Error: %.5f .. %.5f (avg. %.5f)" % (result.min_error, result.max_error, result.avg_error))
+
+
+class PARTICLE_PT_cache(ParticleButtonsPanel, Panel):
+    bl_label = "Cache"
+    bl_options = {'DEFAULT_CLOSED'}
+    COMPAT_ENGINES = {'BLENDER_RENDER'}
+
+    @classmethod
+    def poll(cls, context):
+        psys = context.particle_system
+        engine = context.scene.render.engine
+        if psys is None:
+            return False
+        if psys.settings is None:
+            return False
+        if psys.settings.is_fluid:
+            return False
+        phystype = psys.settings.physics_type
+        if phystype == 'NO' or phystype == 'KEYED':
+            return False
+        return (psys.settings.type in {'EMITTER', 'REACTOR'} or (psys.settings.type == 'HAIR' and (psys.use_hair_dynamics or psys.point_cache.is_baked))) and engine in cls.COMPAT_ENGINES
+
+    def draw(self, context):
+        psys = context.particle_system
+
+        point_cache_ui(self, context, psys.point_cache, True, 'HAIR' if (psys.settings.type == 'HAIR') else 'PSYS')
+
+
+class PARTICLE_PT_velocity(ParticleButtonsPanel, Panel):
+    bl_label = "Velocity"
+    COMPAT_ENGINES = {'BLENDER_RENDER'}
+
+    @classmethod
+    def poll(cls, context):
+        if particle_panel_poll(PARTICLE_PT_velocity, context):
+            psys = context.particle_system
+            settings = particle_get_settings(context)
+
+            if settings.type == 'HAIR' and not settings.use_advanced_hair:
+                return False
+            return settings.physics_type != 'BOIDS' and (psys is None or not psys.point_cache.use_external)
+        else:
+            return False
+
+    def draw(self, context):
+        layout = self.layout
+
+        psys = context.particle_system
+        part = particle_get_settings(context)
+
+        layout.enabled = particle_panel_enabled(context, psys)
+
+        split = layout.split()
+
+        col = split.column()
+        col.label(text="Emitter Geometry:")
+        col.prop(part, "normal_factor")
+        sub = col.column(align=True)
+        sub.prop(part, "tangent_factor")
+        sub.prop(part, "tangent_phase", slider=True)
+
+        col = split.column()
+        col.label(text="Emitter Object:")
+        col.prop(part, "object_align_factor", text="")
+
+        layout.label(text="Other:")
+        row = layout.row()
+        if part.emit_from == 'PARTICLE':
+            row.prop(part, "particle_factor")
+        else:
+            row.prop(part, "object_factor", slider=True)
+        row.prop(part, "factor_random")
+
+        #if part.type=='REACTOR':
+        #    sub.prop(part, "reactor_factor")
+        #    sub.prop(part, "reaction_shape", slider=True)
+
+
+class PARTICLE_PT_rotation(ParticleButtonsPanel, Panel):
+    bl_label = "Rotation"
+    bl_options = {'DEFAULT_CLOSED'}
+    COMPAT_ENGINES = {'BLENDER_RENDER'}
+
+    @classmethod
+    def poll(cls, context):
+        if particle_panel_poll(PARTICLE_PT_rotation, context):
+            psys = context.particle_system
+            settings = particle_get_settings(context)
+
+            if settings.type == 'HAIR' and not settings.use_advanced_hair:
+                return False
+            return settings.physics_type != 'BOIDS' and (psys is None or not psys.point_cache.use_external)
+        else:
+            return False
+
+    def draw_header(self, context):
+        psys = context.particle_system
+        if psys:
+            part = psys.settings
+        else:
+            part = context.space_data.pin_id
+
+        self.layout.prop(part, "use_rotations", text="")
+
+    def draw(self, context):
+        layout = self.layout
+
+        psys = context.particle_system
+        if psys:
+            part = psys.settings
+        else:
+            part = context.space_data.pin_id
+
+        layout.enabled = particle_panel_enabled(context, psys) and part.use_rotations
+
+        layout.label(text="Initial Orientation:")
+
+        split = layout.split()
+
+        col = split.column(align=True)
+        col.prop(part, "rotation_mode", text="")
+        col.prop(part, "rotation_factor_random", slider=True, text="Random")
+
+        col = split.column(align=True)
+        col.prop(part, "phase_factor", slider=True)
+        col.prop(part, "phase_factor_random", text="Random", slider=True)
+
+        if part.type != 'HAIR':
+            layout.label(text="Angular Velocity:")
+
+            split = layout.split()
+
+            col = split.column(align=True)
+            col.prop(part, "angular_velocity_mode", text="")
+            sub = col.column(align=True)
+            sub.active = part.angular_velocity_mode != 'NONE'
+            sub.prop(part, "angular_velocity_factor", text="")
+
+            col = split.column()
+            col.prop(part, "use_dynamic_rotation")
+
+
+class PARTICLE_PT_physics(ParticleButtonsPanel, Panel):
+    bl_label = "Physics"
+    COMPAT_ENGINES = {'BLENDER_RENDER'}
+
+    @classmethod
+    def poll(cls, context):
+        if particle_panel_poll(PARTICLE_PT_physics, context):
+            psys = context.particle_system
+            settings = particle_get_settings(context)
+
+            if settings.type == 'HAIR' and not settings.use_advanced_hair:
+                return False
+            return psys is None or not psys.point_cache.use_external
+        else:
+            return False
+
+    def draw(self, context):
+        layout = self.layout
+
+        psys = context.particle_system
+        part = particle_get_settings(context)
+
+        layout.enabled = particle_panel_enabled(context, psys)
+
+        layout.prop(part, "physics_type", expand=True)
+
+        row = layout.row()
+        col = row.column(align=True)
+        col.prop(part, "particle_size")
+        col.prop(part, "size_random", slider=True)
+
+        if part.physics_type != 'NO':
+            col = row.column(align=True)
+            col.prop(part, "mass")
+            col.prop(part, "use_multiply_size_mass", text="Multiply mass with size")
+
+        if part.physics_type in {'NEWTON', 'FLUID'}:
+            split = layout.split()
+
+            col = split.column()
+            col.label(text="Forces:")
+            col.prop(part, "brownian_factor")
+            col.prop(part, "drag_factor", slider=True)
+            col.prop(part, "damping", slider=True)
+
+            col = split.column()
+            col.label(text="Integration:")
+            col.prop(part, "integrator", text="")
+            col.prop(part, "timestep")
+            sub = col.row()
+            sub.prop(part, "subframes")
+            supports_courant = part.physics_type == 'FLUID'
+            subsub = sub.row()
+            subsub.enabled = supports_courant
+            subsub.prop(part, "use_adaptive_subframes", text="")
+            if supports_courant and part.use_adaptive_subframes:
+                col.prop(part, "courant_target", text="Threshold")
+
+            row = layout.row()
+            row.prop(part, "use_size_deflect")
+            row.prop(part, "use_die_on_collision")
+
+            layout.prop(part, "collision_group")
+
+            if part.physics_type == 'FLUID':
+                fluid = part.fluid
+
+                split = layout.split()
+                sub = split.row()
+                sub.prop(fluid, "solver", expand=True)
+
+                split = layout.split()
+
+                col = split.column()
+                col.label(text="Fluid properties:")
+                col.prop(fluid, "stiffness", text="Stiffness")
+                col.prop(fluid, "linear_viscosity", text="Viscosity")
+                col.prop(fluid, "buoyancy", text="Buoyancy", slider=True)
+
+                col = split.column()
+                col.label(text="Advanced:")
+
+                if fluid.solver == 'DDR':
+                    sub = col.row()
+                    sub.prop(fluid, "repulsion", slider=fluid.factor_repulsion)
+                    sub.prop(fluid, "factor_repulsion", text="")
+
+                    sub = col.row()
+                    sub.prop(fluid, "stiff_viscosity", slider=fluid.factor_stiff_viscosity)
+                    sub.prop(fluid, "factor_stiff_viscosity", text="")
+
+                sub = col.row()
+                sub.prop(fluid, "fluid_radius", slider=fluid.factor_radius)
+                sub.prop(fluid, "factor_radius", text="")
+
+                sub = col.row()
+                sub.prop(fluid, "rest_density", slider=fluid.use_factor_density)
+                sub.prop(fluid, "use_factor_density", text="")
+
+                if fluid.solver == 'CLASSICAL':
+                    # With the classical solver, it is possible to calculate the
+                    # spacing between particles when the fluid is at rest. This
+                    # makes it easier to set stable initial conditions.
+                    particle_volume = part.mass / fluid.rest_density
+                    spacing = pow(particle_volume, 1.0 / 3.0)
+                    sub = col.row()
+                    sub.label(text="Spacing: %g" % spacing)
+
+                elif fluid.solver == 'DDR':
+                    split = layout.split()
+
+                    col = split.column()
+                    col.label(text="Springs:")
+                    col.prop(fluid, "spring_force", text="Force")
+                    col.prop(fluid, "use_viscoelastic_springs")
+                    sub = col.column(align=True)
+                    sub.active = fluid.use_viscoelastic_springs
+                    sub.prop(fluid, "yield_ratio", slider=True)
+                    sub.prop(fluid, "plasticity", slider=True)
+
+                    col = split.column()
+                    col.label(text="Advanced:")
+                    sub = col.row()
+                    sub.prop(fluid, "rest_length", slider=fluid.factor_rest_length)
+                    sub.prop(fluid, "factor_rest_length", text="")
+                    col.label(text="")
+                    sub = col.column()
+                    sub.active = fluid.use_viscoelastic_springs
+                    sub.prop(fluid, "use_initial_rest_length")
+                    sub.prop(fluid, "spring_frames", text="Frames")
+
+        elif part.physics_type == 'KEYED':
+            split = layout.split()
+            sub = split.column()
+
+            row = layout.row()
+            col = row.column()
+            col.active = not psys.use_keyed_timing
+            col.prop(part, "keyed_loops", text="Loops")
+            if psys:
+                row.prop(psys, "use_keyed_timing", text="Use Timing")
+
+            layout.label(text="Keys:")
+        elif part.physics_type == 'BOIDS':
+            boids = part.boids
+
+            row = layout.row()
+            row.prop(boids, "use_flight")
+            row.prop(boids, "use_land")
+            row.prop(boids, "use_climb")
+
+            split = layout.split()
+
+            col = split.column(align=True)
+            col.active = boids.use_flight
+            col.prop(boids, "air_speed_max")
+            col.prop(boids, "air_speed_min", slider=True)
+            col.prop(boids, "air_acc_max", slider=True)
+            col.prop(boids, "air_ave_max", slider=True)
+            col.prop(boids, "air_personal_space")
+            row = col.row(align=True)
+            row.active = (boids.use_land or boids.use_climb) and boids.use_flight
+            row.prop(boids, "land_smooth")
+
+            col = split.column(align=True)
+            col.active = boids.use_land or boids.use_climb
+            col.prop(boids, "land_speed_max")
+            col.prop(boids, "land_jump_speed")
+            col.prop(boids, "land_acc_max", slider=True)
+            col.prop(boids, "land_ave_max", slider=True)
+            col.prop(boids, "land_personal_space")
+            col.prop(boids, "land_stick_force")
+
+            layout.prop(part, "collision_group")
+
+            split = layout.split()
+
+            col = split.column(align=True)
+            col.label(text="Battle:")
+            col.prop(boids, "health")
+            col.prop(boids, "strength")
+            col.prop(boids, "aggression")
+            col.prop(boids, "accuracy")
+            col.prop(boids, "range")
+
+            col = split.column()
+            col.label(text="Misc:")
+            col.prop(boids, "bank", slider=True)
+            col.prop(boids, "pitch", slider=True)
+            col.prop(boids, "height", slider=True)
+
+        if psys and part.physics_type in {'KEYED', 'BOIDS', 'FLUID'}:
+            if part.physics_type == 'BOIDS':
+                layout.label(text="Relations:")
+            elif part.physics_type == 'FLUID':
+                layout.label(text="Fluid interaction:")
+
+            row = layout.row()
+            row.template_list("UI_UL_list", "particle_targets", psys, "targets", psys, "active_particle_target_index", rows=4)
+
+            col = row.column()
+            sub = col.row()
+            subsub = sub.column(align=True)
+            subsub.operator("particle.new_target", icon='ZOOMIN', text="")
+            subsub.operator("particle.target_remove", icon='ZOOMOUT', text="")
+            sub = col.row()
+            subsub = sub.column(align=True)
+            subsub.operator("particle.target_move_up", icon='MOVE_UP_VEC', text="")
+            subsub.operator("particle.target_move_down", icon='MOVE_DOWN_VEC', text="")
+
+            key = psys.active_particle_target
+            if key:
+                row = layout.row()
+                if part.physics_type == 'KEYED':
+                    col = row.column()
+                    #doesn't work yet
+                    #col.alert = key.valid
+                    col.prop(key, "object", text="")
+                    col.prop(key, "system", text="System")
+                    col = row.column()
+                    col.active = psys.use_keyed_timing
+                    col.prop(key, "time")
+                    col.prop(key, "duration")
+                elif part.physics_type == 'BOIDS':
+                    sub = row.row()
+                    #doesn't work yet
+                    #sub.alert = key.valid
+                    sub.prop(key, "object", text="")
+                    sub.prop(key, "system", text="System")
+
+                    layout.prop(key, "alliance", expand=True)
+                elif part.physics_type == 'FLUID':
+                    sub = row.row()
+                    #doesn't work yet
+                    #sub.alert = key.valid
+                    sub.prop(key, "object", text="")
+                    sub.prop(key, "system", text="System")
+
+
+class PARTICLE_PT_boidbrain(ParticleButtonsPanel, Panel):
+    bl_label = "Boid Brain"
+    COMPAT_ENGINES = {'BLENDER_RENDER'}
+
+    @classmethod
+    def poll(cls, context):
+        psys = context.particle_system
+        settings = particle_get_settings(context)
+        engine = context.scene.render.engine
+
+        if settings is None:
+            return False
+        if psys is not None and psys.point_cache.use_external:
+            return False
+        return settings.physics_type == 'BOIDS' and engine in cls.COMPAT_ENGINES
+
+    def draw(self, context):
+        layout = self.layout
+
+        boids = particle_get_settings(context).boids
+
+        layout.enabled = particle_panel_enabled(context, context.particle_system)
+
+        # Currently boids can only use the first state so these are commented out for now.
+        #row = layout.row()
+        #row.template_list("UI_UL_list", "particle_boids", boids, "states",
+        #                  boids, "active_boid_state_index", compact="True")
+        #col = row.row()
+        #sub = col.row(align=True)
+        #sub.operator("boid.state_add", icon='ZOOMIN', text="")
+        #sub.operator("boid.state_del", icon='ZOOMOUT', text="")
+        #sub = row.row(align=True)
+        #sub.operator("boid.state_move_up", icon='MOVE_UP_VEC', text="")
+        #sub.operator("boid.state_move_down", icon='MOVE_DOWN_VEC', text="")
+
+        state = boids.active_boid_state
+
+        #layout.prop(state, "name", text="State name")
+
+        row = layout.row()
+        row.prop(state, "ruleset_type")
+        if state.ruleset_type == 'FUZZY':
+            row.prop(state, "rule_fuzzy", slider=True)
+        else:
+            row.label(text="")
+
+        row = layout.row()
+        row.template_list("UI_UL_list", "particle_boids_rules", state, "rules", state, "active_boid_rule_index", rows=4)
+
+        col = row.column()
+        sub = col.row()
+        subsub = sub.column(align=True)
+        subsub.operator_menu_enum("boid.rule_add", "type", icon='ZOOMIN', text="")
+        subsub.operator("boid.rule_del", icon='ZOOMOUT', text="")
+        sub = col.row()
+        subsub = sub.column(align=True)
+        subsub.operator("boid.rule_move_up", icon='MOVE_UP_VEC', text="")
+        subsub.operator("boid.rule_move_down", icon='MOVE_DOWN_VEC', text="")
+
+        rule = state.active_boid_rule
+
+        if rule:
+            row = layout.row()
+            row.prop(rule, "name", text="")
+            #somebody make nice icons for boids here please! -jahka
+            row.prop(rule, "use_in_air", icon='MOVE_UP_VEC', text="")
+            row.prop(rule, "use_on_land", icon='MOVE_DOWN_VEC', text="")
+
+            row = layout.row()
+
+            if rule.type == 'GOAL':
+                row.prop(rule, "object")
+                row = layout.row()
+                row.prop(rule, "use_predict")
+            elif rule.type == 'AVOID':
+                row.prop(rule, "object")
+                row = layout.row()
+                row.prop(rule, "use_predict")
+                row.prop(rule, "fear_factor")
+            elif rule.type == 'FOLLOW_PATH':
+                row.label(text="Not yet functional")
+            elif rule.type == 'AVOID_COLLISION':
+                row.prop(rule, "use_avoid")
+                row.prop(rule, "use_avoid_collision")
+                row.prop(rule, "look_ahead")
+            elif rule.type == 'FOLLOW_LEADER':
+                row.prop(rule, "object", text="")
+                row.prop(rule, "distance")
+                row = layout.row()
+                row.prop(rule, "use_line")
+                sub = row.row()
+                sub.active = rule.line
+                sub.prop(rule, "queue_count")
+            elif rule.type == 'AVERAGE_SPEED':
+                row.prop(rule, "speed", slider=True)
+                row.prop(rule, "wander", slider=True)
+                row.prop(rule, "level", slider=True)
+            elif rule.type == 'FIGHT':
+                row.prop(rule, "distance")
+                row.prop(rule, "flee_distance")
+
+
+class PARTICLE_PT_render(ParticleButtonsPanel, Panel):
+    bl_label = "Render"
+    COMPAT_ENGINES = {'BLENDER_RENDER'}
+
+    @classmethod
+    def poll(cls, context):
+        settings = particle_get_settings(context)
+        engine = context.scene.render.engine
+        if settings is None:
+            return False
+
+        return engine in cls.COMPAT_ENGINES
+
+    def draw(self, context):
+        layout = self.layout
+
+        psys = context.particle_system
+        part = particle_get_settings(context)
+
+        if psys:
+            row = layout.row()
+            if part.render_type in {'OBJECT', 'GROUP'}:
+                row.enabled = False
+            row.prop(part, "material_slot", text="")
+            row.prop(psys, "parent")
+
+        split = layout.split()
+
+        col = split.column()
+        col.prop(part, "use_render_emitter")
+        col.prop(part, "use_parent_particles")
+
+        col = split.column()
+        col.prop(part, "show_unborn")
+        col.prop(part, "use_dead")
+
+        layout.prop(part, "render_type", expand=True)
+
+        split = layout.split()
+
+        col = split.column()
+
+        if part.render_type == 'LINE':
+            col.prop(part, "line_length_tail")
+            col.prop(part, "line_length_head")
+
+            split.prop(part, "use_velocity_length")
+        elif part.render_type == 'PATH':
+            col.prop(part, "use_strand_primitive")
+            sub = col.column()
+            sub.active = (part.use_strand_primitive is False)
+            sub.prop(part, "use_render_adaptive")
+            sub = col.column()
+            sub.active = part.use_render_adaptive or part.use_strand_primitive is True
+            sub.prop(part, "adaptive_angle")
+            sub = col.column()
+            sub.active = (part.use_render_adaptive is True and part.use_strand_primitive is False)
+            sub.prop(part, "adaptive_pixel")
+            col.prop(part, "use_hair_bspline")
+            col.prop(part, "render_step", text="Steps")
+
+            col = split.column()
+            col.label(text="Timing:")
+            col.prop(part, "use_absolute_path_time")
+
+            if part.type == 'HAIR' or psys.point_cache.is_baked:
+                col.prop(part, "path_start", text="Start", slider=not part.use_absolute_path_time)
+            else:
+                col.prop(part, "trail_count")
+
+            col.prop(part, "path_end", text="End", slider=not part.use_absolute_path_time)
+            col.prop(part, "length_random", text="Random", slider=True)
+
+            row = layout.row()
+            col = row.column()
+
+            if part.type == 'HAIR' and part.use_strand_primitive is True and part.child_type == 'INTERPOLATED':
+                layout.prop(part, "use_simplify")
+                if part.use_simplify is True:
+                    row = layout.row()
+                    row.prop(part, "simplify_refsize")
+                    row.prop(part, "simplify_rate")
+                    row.prop(part, "simplify_transition")
+                    row = layout.row()
+                    row.prop(part, "use_simplify_viewport")
+                    sub = row.row()
+                    sub.active = part.use_simplify_viewport is True
+                    sub.prop(part, "simplify_viewport")
+
+        elif part.render_type == 'OBJECT':
+            col.prop(part, "dupli_object")
+            sub = col.row()
+            sub.prop(part, "use_global_dupli")
+            sub.prop(part, "use_rotation_dupli")
+            sub.prop(part, "use_scale_dupli")
+        elif part.render_type == 'GROUP':
+            col.prop(part, "dupli_group")
+            split = layout.split()
+
+            col = split.column()
+            col.prop(part, "use_whole_group")
+            sub = col.column()
+            sub.active = (part.use_whole_group is False)
+            sub.prop(part, "use_group_pick_random")
+            sub.prop(part, "use_group_count")
+
+            col = split.column()
+            sub = col.column()
+            sub.active = (part.use_whole_group is False)
+            sub.prop(part, "use_global_dupli")
+            sub.prop(part, "use_rotation_dupli")
+            sub.prop(part, "use_scale_dupli")
+
+            if part.use_group_count and not part.use_whole_group:
+                row = layout.row()
+                row.template_list("UI_UL_list", "particle_dupli_weights", part, "dupli_weights",
+                                  part, "active_dupliweight_index")
+
+                col = row.column()
+                sub = col.row()
+                subsub = sub.column(align=True)
+                subsub.operator("particle.dupliob_copy", icon='ZOOMIN', text="")
+                subsub.operator("particle.dupliob_remove", icon='ZOOMOUT', text="")
+                subsub.operator("particle.dupliob_move_up", icon='MOVE_UP_VEC', text="")
+                subsub.operator("particle.dupliob_move_down", icon='MOVE_DOWN_VEC', text="")
+
+                weight = part.active_dupliweight
+                if weight:
+                    row = layout.row()
+                    row.prop(weight, "count")
+
+        elif part.render_type == 'BILLBOARD':
+            ob = context.object
+
+            col.label(text="Align:")
+
+            row = layout.row()
+            row.prop(part, "billboard_align", expand=True)
+            row.prop(part, "lock_billboard", text="Lock")
+            row = layout.row()
+            row.prop(part, "billboard_object")
+
+            row = layout.row()
+            col = row.column(align=True)
+            col.label(text="Tilt:")
+            col.prop(part, "billboard_tilt", text="Angle", slider=True)
+            col.prop(part, "billboard_tilt_random", text="Random", slider=True)
+            col = row.column()
+            col.prop(part, "billboard_offset")
+
+            row = layout.row()
+            col = row.column()
+            col.prop(part, "billboard_size", text="Scale")
+            if part.billboard_align == 'VEL':
+                col = row.column(align=True)
+                col.label("Velocity Scale:")
+                col.prop(part, "billboard_velocity_head", text="Head")
+                col.prop(part, "billboard_velocity_tail", text="Tail")
+
+            if psys:
+                col = layout.column()
+                col.prop_search(psys, "billboard_normal_uv", ob.data, "uv_textures")
+                col.prop_search(psys, "billboard_time_index_uv", ob.data, "uv_textures")
+
+            split = layout.split(percentage=0.33)
+            split.label(text="Split UVs:")
+            split.prop(part, "billboard_uv_split", text="Number of splits")
+
+            if psys:
+                col = layout.column()
+                col.active = part.billboard_uv_split > 1
+                col.prop_search(psys, "billboard_split_uv", ob.data, "uv_textures")
+
+            row = col.row()
+            row.label(text="Animate:")
+            row.prop(part, "billboard_animation", text="")
+            row.label(text="Offset:")
+            row.prop(part, "billboard_offset_split", text="")
+
+        if part.render_type == 'HALO' or part.render_type == 'LINE' or part.render_type == 'BILLBOARD':
+            row = layout.row()
+            col = row.column()
+            col.prop(part, "trail_count")
+            if part.trail_count > 1:
+                col.prop(part, "use_absolute_path_time", text="Length in frames")
+                col = row.column()
+                col.prop(part, "path_end", text="Length", slider=not part.use_absolute_path_time)
+                col.prop(part, "length_random", text="Random", slider=True)
+            else:
+                col = row.column()
+                col.label(text="")
+
+        if part.render_type in {'OBJECT', 'GROUP'} and not part.use_advanced_hair:
+            row = layout.row(align=True)
+            row.prop(part, "particle_size")
+            row.prop(part, "size_random", slider=True)
+
+
+class PARTICLE_PT_draw(ParticleButtonsPanel, Panel):
+    bl_label = "Display"
+    bl_options = {'DEFAULT_CLOSED'}
+    COMPAT_ENGINES = {'BLENDER_RENDER'}
+
+    @classmethod
+    def poll(cls, context):
+        settings = particle_get_settings(context)
+        engine = context.scene.render.engine
+        if settings is None:
+            return False
+        return engine in cls.COMPAT_ENGINES
+
+    def draw(self, context):
+        layout = self.layout
+
+        psys = context.particle_system
+        part = particle_get_settings(context)
+
+        row = layout.row()
+        row.prop(part, "draw_method", expand=True)
+        row.prop(part, "show_guide_hairs")
+
+        if part.draw_method == 'NONE' or (part.render_type == 'NONE' and part.draw_method == 'RENDER'):
+            return
+
+        path = (part.render_type == 'PATH' and part.draw_method == 'RENDER') or part.draw_method == 'PATH'
+
+        row = layout.row()
+        row.prop(part, "draw_percentage", slider=True)
+        if part.draw_method != 'RENDER' or part.render_type == 'HALO':
+            row.prop(part, "draw_size")
+        else:
+            row.label(text="")
+
+        if part.draw_percentage != 100 and psys is not None:
+            if part.type == 'HAIR':
+                if psys.use_hair_dynamics and psys.point_cache.is_baked is False:
+                    layout.row().label(text="Display percentage makes dynamics inaccurate without baking!")
+            else:
+                phystype = part.physics_type
+                if phystype != 'NO' and phystype != 'KEYED' and psys.point_cache.is_baked is False:
+                    layout.row().label(text="Display percentage makes dynamics inaccurate without baking!")
+
+        row = layout.row()
+        col = row.column()
+        col.prop(part, "show_size")
+        col.prop(part, "show_velocity")
+        col.prop(part, "show_number")
+        if part.physics_type == 'BOIDS':
+            col.prop(part, "show_health")
+
+        col = row.column(align=True)
+        col.label(text="Color:")
+        col.prop(part, "draw_color", text="")
+        sub = col.row(align=True)
+        sub.active = (part.draw_color in {'VELOCITY', 'ACCELERATION'})
+        sub.prop(part, "color_maximum", text="Max")
+
+        if path:
+            col.prop(part, "draw_step")
+
+
+class PARTICLE_PT_children(ParticleButtonsPanel, Panel):
+    bl_label = "Children"
+    bl_options = {'DEFAULT_CLOSED'}
+    COMPAT_ENGINES = {'BLENDER_RENDER'}
+
+    @classmethod
+    def poll(cls, context):
+        return particle_panel_poll(cls, context)
+
+    def draw(self, context):
+        layout = self.layout
+
+        psys = context.particle_system
+        part = particle_get_settings(context)
+
+        layout.row().prop(part, "child_type", expand=True)
+
+        if part.child_type == 'NONE':
+            return
+
+        row = layout.row()
+
+        col = row.column(align=True)
+        col.prop(part, "child_nbr", text="Display")
+        col.prop(part, "rendered_child_count", text="Render")
+
+        if part.child_type == 'INTERPOLATED':
+            col = row.column()
+            if psys:
+                col.prop(psys, "child_seed", text="Seed")
+            col.prop(part, "virtual_parents", slider=True)
+            col.prop(part, "create_long_hair_children")
+        else:
+            col = row.column(align=True)
+            col.prop(part, "child_size", text="Size")
+            col.prop(part, "child_size_random", text="Random")
+
+        split = layout.split()
+
+        col = split.column()
+        col.label(text="Effects:")
+
+        sub = col.column(align=True)
+        sub.prop(part, "use_clump_curve")
+        if part.use_clump_curve:
+            sub.template_curve_mapping(part, "clump_curve")
+        else:
+            sub.prop(part, "clump_factor", slider=True)
+            sub.prop(part, "clump_shape", slider=True)
+        sub = col.column(align=True)
+        sub.prop(part, "use_clump_noise")
+        subsub = sub.column()
+        subsub.enabled = part.use_clump_noise
+        subsub.prop(part, "clump_noise_size")
+
+        sub = col.column(align=True)
+        sub.prop(part, "child_length", slider=True)
+        sub.prop(part, "child_length_threshold", slider=True)
+
+        if part.child_type == 'SIMPLE':
+            sub = col.column(align=True)
+            sub.prop(part, "child_radius", text="Radius")
+            sub.prop(part, "child_roundness", text="Roundness", slider=True)
+            if psys:
+                sub.prop(psys, "child_seed", text="Seed")
+        elif part.virtual_parents > 0.0:
+            sub = col.column(align=True)
+            sub.label(text="Parting not")
+            sub.label(text="available with")
+            sub.label(text="virtual parents")
+        else:
+            sub = col.column(align=True)
+            sub.prop(part, "child_parting_factor", text="Parting", slider=True)
+            sub.prop(part, "child_parting_min", text="Min")
+            sub.prop(part, "child_parting_max", text="Max")
+
+        col = split.column()
+
+        col.prop(part, "use_roughness_curve")
+        if part.use_roughness_curve:
+            sub = col.column()
+            sub.template_curve_mapping(part, "roughness_curve")
+            sub.prop(part, "roughness_1", text="Roughness")
+            sub.prop(part, "roughness_1_size", text="Size")
+        else:
+            col.label(text="Roughness:")
+
+            sub = col.column(align=True)
+            sub.prop(part, "roughness_1", text="Uniform")
+            sub.prop(part, "roughness_1_size", text="Size")
+
+            sub = col.column(align=True)
+            sub.prop(part, "roughness_endpoint", "Endpoint")
+            sub.prop(part, "roughness_end_shape")
+
+            sub = col.column(align=True)
+            sub.prop(part, "roughness_2", text="Random")
+            sub.prop(part, "roughness_2_size", text="Size")
+            sub.prop(part, "roughness_2_threshold", slider=True)
+
+        layout.row().label(text="Kink:")
+        layout.row().prop(part, "kink", expand=True)
+
+        split = layout.split()
+        split.active = part.kink != 'NO'
+
+        if part.kink == 'SPIRAL':
+            col = split.column()
+            sub = col.column(align=True)
+            sub.prop(part, "kink_amplitude", text="Radius")
+            sub.prop(part, "kink_amplitude_random", text="Random", slider=True)
+            sub = col.column(align=True)
+            sub.prop(part, "kink_axis")
+            sub.prop(part, "kink_axis_random", text="Random", slider=True)
+            col = split.column(align=True)
+            col.prop(part, "kink_frequency", text="Frequency")
+            col.prop(part, "kink_shape", text="Shape", slider=True)
+            col.prop(part, "kink_extra_steps", text="Steps")
+        else:
+            col = split.column()
+            sub = col.column(align=True)
+            sub.prop(part, "kink_amplitude")
+            sub.prop(part, "kink_amplitude_clump", text="Clump", slider=True)
+            col.prop(part, "kink_flat", slider=True)
+            col = split.column(align=True)
+            col.prop(part, "kink_frequency")
+            col.prop(part, "kink_shape", slider=True)
+
+
+class PARTICLE_PT_field_weights(ParticleButtonsPanel, Panel):
+    bl_label = "Field Weights"
+    bl_options = {'DEFAULT_CLOSED'}
+    COMPAT_ENGINES = {'BLENDER_RENDER'}
+
+    @classmethod
+    def poll(cls, context):
+        return particle_panel_poll(cls, context)
+
+    def draw(self, context):
+        part = particle_get_settings(context)
+        effector_weights_ui(self, context, part.effector_weights, 'PSYS')
+
+        if part.type == 'HAIR':
+            row = self.layout.row()
+            row.prop(part.effector_weights, "apply_to_hair_growing")
+            row.prop(part, "apply_effector_to_children")
+            row = self.layout.row()
+            row.prop(part, "effect_hair", slider=True)
+
+
+class PARTICLE_PT_force_fields(ParticleButtonsPanel, Panel):
+    bl_label = "Force Field Settings"
+    bl_options = {'DEFAULT_CLOSED'}
+    COMPAT_ENGINES = {'BLENDER_RENDER'}
+
+    def draw(self, context):
+        layout = self.layout
+
+        part = particle_get_settings(context)
+
+        row = layout.row()
+        row.prop(part, "use_self_effect")
+        row.prop(part, "effector_amount", text="Amount")
+
+        split = layout.split(percentage=0.2)
+        split.label(text="Type 1:")
+        split.prop(part.force_field_1, "type", text="")
+        basic_force_field_settings_ui(self, context, part.force_field_1)
+        if part.force_field_1.type != 'NONE':
+            layout.label(text="Falloff:")
+        basic_force_field_falloff_ui(self, context, part.force_field_1)
+
+        if part.force_field_1.type != 'NONE':
+            layout.label(text="")
+
+        split = layout.split(percentage=0.2)
+        split.label(text="Type 2:")
+        split.prop(part.force_field_2, "type", text="")
+        basic_force_field_settings_ui(self, context, part.force_field_2)
+        if part.force_field_2.type != 'NONE':
+            layout.label(text="Falloff:")
+        basic_force_field_falloff_ui(self, context, part.force_field_2)
+
+
+class PARTICLE_PT_vertexgroups(ParticleButtonsPanel, Panel):
+    bl_label = "Vertex Groups"
+    bl_options = {'DEFAULT_CLOSED'}
+    COMPAT_ENGINES = {'BLENDER_RENDER'}
+
+    @classmethod
+    def poll(cls, context):
+        if context.particle_system is None:
+            return False
+        return particle_panel_poll(cls, context)
+
+    def draw(self, context):
+        layout = self.layout
+
+        ob = context.object
+        psys = context.particle_system
+
+        col = layout.column()
+        row = col.row(align=True)
+        row.prop_search(psys, "vertex_group_density", ob, "vertex_groups", text="Density")
+        row.prop(psys, "invert_vertex_group_density", text="", toggle=True, icon='ARROW_LEFTRIGHT')
+
+        row = col.row(align=True)
+        row.prop_search(psys, "vertex_group_length", ob, "vertex_groups", text="Length")
+        row.prop(psys, "invert_vertex_group_length", text="", toggle=True, icon='ARROW_LEFTRIGHT')
+
+        row = col.row(align=True)
+        row.prop_search(psys, "vertex_group_clump", ob, "vertex_groups", text="Clump")
+        row.prop(psys, "invert_vertex_group_clump", text="", toggle=True, icon='ARROW_LEFTRIGHT')
+
+        row = col.row(align=True)
+        row.prop_search(psys, "vertex_group_kink", ob, "vertex_groups", text="Kink")
+        row.prop(psys, "invert_vertex_group_kink", text="", toggle=True, icon='ARROW_LEFTRIGHT')
+
+        row = col.row(align=True)
+        row.prop_search(psys, "vertex_group_roughness_1", ob, "vertex_groups", text="Roughness 1")
+        row.prop(psys, "invert_vertex_group_roughness_1", text="", toggle=True, icon='ARROW_LEFTRIGHT')
+
+        row = col.row(align=True)
+        row.prop_search(psys, "vertex_group_roughness_2", ob, "vertex_groups", text="Roughness 2")
+        row.prop(psys, "invert_vertex_group_roughness_2", text="", toggle=True, icon='ARROW_LEFTRIGHT')
+
+        row = col.row(align=True)
+        row.prop_search(psys, "vertex_group_roughness_end", ob, "vertex_groups", text="Roughness End")
+        row.prop(psys, "invert_vertex_group_roughness_end", text="", toggle=True, icon='ARROW_LEFTRIGHT')
+
+        # Commented out vertex groups don't work and are still waiting for better implementation
+        # row = layout.row()
+        # row.prop_search(psys, "vertex_group_velocity", ob, "vertex_groups", text="Velocity")
+        # row.prop(psys, "invert_vertex_group_velocity", text="")
+
+        # row = layout.row()
+        # row.prop_search(psys, "vertex_group_size", ob, "vertex_groups", text="Size")
+        # row.prop(psys, "invert_vertex_group_size", text="")
+
+        # row = layout.row()
+        # row.prop_search(psys, "vertex_group_tangent", ob, "vertex_groups", text="Tangent")
+        # row.prop(psys, "invert_vertex_group_tangent", text="")
+
+        # row = layout.row()
+        # row.prop_search(psys, "vertex_group_rotation", ob, "vertex_groups", text="Rotation")
+        # row.prop(psys, "invert_vertex_group_rotation", text="")
+
+        # row = layout.row()
+        # row.prop_search(psys, "vertex_group_field", ob, "vertex_groups", text="Field")
+        # row.prop(psys, "invert_vertex_group_field", text="")
+
+
+class PARTICLE_PT_custom_props(ParticleButtonsPanel, PropertyPanel, Panel):
+    COMPAT_ENGINES = {'BLENDER_RENDER'}
+    _context_path = "particle_system.settings"
+    _property_type = bpy.types.ParticleSettings
+
+if __name__ == "__main__":  # only for live edit.
+    bpy.utils.register_module(__name__)
index 0362cc42371ef97a26e7378525cf66ca20eb42fe..54581c9276d83dbb32a8868aa1b48918b7ba2c4c 100644 (file)
@@ -21,13 +21,13 @@ import bpy
 from bpy.types import Menu, Panel
 
 from bl_ui.properties_physics_common import (
+        point_cache_ui,
         effector_weights_ui,
         )
 
 
 def cloth_panel_enabled(md):
-    return True
-    #return md.point_cache.is_baked is False
+    return md.point_cache.is_baked is False
 
 
 class CLOTH_MT_presets(Menu):
@@ -132,6 +132,16 @@ class PHYSICS_PT_cloth(PhysicButtonsPanel, Panel):
             sub.prop_search(cloth, "rest_shape_key", key, "key_blocks", text="")
 
 
+class PHYSICS_PT_cloth_cache(PhysicButtonsPanel, Panel):
+    bl_label = "Cloth Cache"
+    bl_options = {'DEFAULT_CLOSED'}
+    COMPAT_ENGINES = {'BLENDER_RENDER'}
+
+    def draw(self, context):
+        md = context.cloth
+        point_cache_ui(self, context, md.point_cache, cloth_panel_enabled(md), 'CLOTH')
+
+
 class PHYSICS_PT_cloth_collision(PhysicButtonsPanel, Panel):
     bl_label = "Cloth Collision"
     bl_options = {'DEFAULT_CLOSED'}
index ea4bbc76f4321b1449138c2255df11dc2ca2ec37..277b59d187de1734c71ff5f67b7422baec4b1bfc 100644 (file)
@@ -98,6 +98,113 @@ class PHYSICS_PT_add(PhysicButtonsPanel, Panel):
                             'CONSTRAINT')  # RB_TODO needs better icon
 
 
+# cache-type can be 'PSYS' 'HAIR' 'SMOKE' etc
+
+def point_cache_ui(self, context, cache, enabled, cachetype):
+    layout = self.layout
+
+    layout.context_pointer_set("point_cache", cache)
+
+    if not cachetype == 'RIGID_BODY':
+        row = layout.row()
+        row.template_list("UI_UL_list", "point_caches", cache, "point_caches",
+                          cache.point_caches, "active_index", rows=1)
+        col = row.column(align=True)
+        col.operator("ptcache.add", icon='ZOOMIN', text="")
+        col.operator("ptcache.remove", icon='ZOOMOUT', text="")
+
+    row = layout.row()
+    if cachetype in {'PSYS', 'HAIR', 'SMOKE'}:
+        row.prop(cache, "use_external")
+
+        if cachetype == 'SMOKE':
+            row.prop(cache, "use_library_path", "Use Lib Path")
+
+    if cache.use_external:
+        split = layout.split(percentage=0.35)
+        col = split.column()
+        col.label(text="Index Number:")
+        col.label(text="File Path:")
+
+        col = split.column()
+        col.prop(cache, "index", text="")
+        col.prop(cache, "filepath", text="")
+
+        cache_info = cache.info
+        if cache_info:
+            layout.label(text=cache_info)
+    else:
+        if cachetype in {'SMOKE', 'DYNAMIC_PAINT'}:
+            if not bpy.data.is_saved:
+                layout.label(text="Cache is disabled until the file is saved")
+                layout.enabled = False
+
+    if not cache.use_external or cachetype == 'SMOKE':
+        row = layout.row(align=True)
+
+        if cachetype not in {'PSYS', 'DYNAMIC_PAINT'}:
+            row.enabled = enabled
+            row.prop(cache, "frame_start")
+            row.prop(cache, "frame_end")
+        if cachetype not in {'SMOKE', 'CLOTH', 'DYNAMIC_PAINT', 'RIGID_BODY'}:
+            row.prop(cache, "frame_step")
+
+        if cachetype != 'SMOKE':
+            layout.label(text=cache.info)
+
+        can_bake = True
+
+        if cachetype not in {'SMOKE', 'DYNAMIC_PAINT', 'RIGID_BODY'}:
+            split = layout.split()
+            split.enabled = enabled and bpy.data.is_saved
+
+            col = split.column()
+            col.prop(cache, "use_disk_cache")
+
+            col = split.column()
+            col.active = cache.use_disk_cache
+            col.prop(cache, "use_library_path", "Use Lib Path")
+
+            row = layout.row()
+            row.enabled = enabled and bpy.data.is_saved
+            row.active = cache.use_disk_cache
+            row.label(text="Compression:")
+            row.prop(cache, "compression", expand=True)
+
+            layout.separator()
+
+            if cache.id_data.library and not cache.use_disk_cache:
+                can_bake = False
+
+                col = layout.column(align=True)
+                col.label(text="Linked object baking requires Disk Cache to be enabled", icon='INFO')
+        else:
+            layout.separator()
+
+        split = layout.split()
+        split.active = can_bake
+
+        col = split.column()
+
+        if cache.is_baked is True:
+            col.operator("ptcache.free_bake", text="Free Bake")
+        else:
+            col.operator("ptcache.bake", text="Bake").bake = True
+
+        sub = col.row()
+        sub.enabled = (cache.is_frame_skip or cache.is_outdated) and enabled
+        sub.operator("ptcache.bake", text="Calculate To Frame").bake = False
+
+        sub = col.column()
+        sub.enabled = enabled
+        sub.operator("ptcache.bake_from_cache", text="Current Cache to Bake")
+
+        col = split.column()
+        col.operator("ptcache.bake_all", text="Bake All Dynamics").bake = True
+        col.operator("ptcache.free_bake_all", text="Free All Bakes")
+        col.operator("ptcache.bake_all", text="Update All To Frame").bake = False
+
+
 def effector_weights_ui(self, context, weights, weight_type):
     layout = self.layout
 
index b36404632242193a4507d36e77d4d97059209715..6c3a3246cf6988216df9800e3fab03ffc47deca8 100644 (file)
@@ -21,6 +21,7 @@ import bpy
 from bpy.types import Panel, UIList
 
 from bl_ui.properties_physics_common import (
+        point_cache_ui,
         effector_weights_ui,
         )
 
@@ -124,8 +125,10 @@ class PHYSICS_PT_dynamic_paint(PhysicButtonsPanel, Panel):
 
                 col = split.column()
                 if not use_shading_nodes:
-                    col.prop(brush, "use_material")
-                if brush.use_material and not use_shading_nodes:
+                    sub = col.column()
+                    sub.active = (brush.paint_source != 'PARTICLE_SYSTEM')
+                    sub.prop(brush, "use_material")
+                if brush.use_material and brush.paint_source != 'PARTICLE_SYSTEM' and not use_shading_nodes:
                     col.prop(brush, "material", text="")
                     col.prop(brush, "paint_alpha", text="Alpha Factor")
                 else:
@@ -390,6 +393,29 @@ class PHYSICS_PT_dp_effects(PhysicButtonsPanel, Panel):
             row.prop(surface, "shrink_speed")
 
 
+class PHYSICS_PT_dp_cache(PhysicButtonsPanel, Panel):
+    bl_label = "Dynamic Paint Cache"
+    bl_options = {'DEFAULT_CLOSED'}
+    COMPAT_ENGINES = {'BLENDER_RENDER'}
+
+    @classmethod
+    def poll(cls, context):
+        md = context.dynamic_paint
+        rd = context.scene.render
+        return (md and
+                md.ui_type == 'CANVAS' and
+                md.canvas_settings and
+                md.canvas_settings.canvas_surfaces.active and
+                md.canvas_settings.canvas_surfaces.active.is_cache_user and
+                (rd.engine in cls.COMPAT_ENGINES))
+
+    def draw(self, context):
+        surface = context.dynamic_paint.canvas_settings.canvas_surfaces.active
+        cache = surface.point_cache
+
+        point_cache_ui(self, context, cache, (cache.is_baked is False), 'DYNAMIC_PAINT')
+
+
 class PHYSICS_PT_dp_brush_source(PhysicButtonsPanel, Panel):
     bl_label = "Dynamic Paint Source"
     COMPAT_ENGINES = {'BLENDER_RENDER'}
@@ -410,6 +436,16 @@ class PHYSICS_PT_dp_brush_source(PhysicButtonsPanel, Panel):
         col = split.column()
         col.prop(brush, "paint_source")
 
+        if brush.paint_source == 'PARTICLE_SYSTEM':
+            col.prop_search(brush, "particle_system", ob, "particle_systems", text="")
+            if brush.particle_system:
+                col.label(text="Particle effect:")
+                sub = col.column()
+                sub.active = not brush.use_particle_radius
+                sub.prop(brush, "solid_radius", text="Solid Radius")
+                col.prop(brush, "use_particle_radius", text="Use Particle's Radius")
+                col.prop(brush, "smooth_radius", text="Smooth radius")
+
         if brush.paint_source in {'DISTANCE', 'VOLUME_DISTANCE', 'POINT'}:
             col.prop(brush, "paint_distance", text="Paint Distance")
             split = layout.row().split(percentage=0.4)
index 2be61e362cc6599c8bbd36e7f94a7af4913e586f..ee9135b9dbff92211b67fe8e874f05cf6bc9725a 100644 (file)
@@ -21,6 +21,7 @@ import bpy
 from bpy.types import Panel
 
 from bl_ui.properties_physics_common import (
+        point_cache_ui,
         effector_weights_ui,
         )
 
@@ -58,6 +59,8 @@ class PHYSICS_PT_smoke(PhysicButtonsPanel, Panel):
 
             split = layout.split()
 
+            split.enabled = not domain.point_cache.is_baked
+
             col = split.column()
             col.label(text="Resolution:")
             col.prop(domain, "resolution_max", text="Divisions")
@@ -88,7 +91,14 @@ class PHYSICS_PT_smoke(PhysicButtonsPanel, Panel):
                 col = split.column()
                 col.label(text="Flow Source:")
                 col.prop(flow, "smoke_flow_source", expand=False, text="")
-                if flow.smoke_flow_source == 'MESH':
+                if flow.smoke_flow_source == 'PARTICLES':
+                    col.label(text="Particle System:")
+                    col.prop_search(flow, "particle_system", ob, "particle_systems", text="")
+                    col.prop(flow, "use_particle_size", text="Set Size")
+                    sub = col.column()
+                    sub.active = flow.use_particle_size
+                    sub.prop(flow, "particle_size")
+                else:
                     col.prop(flow, "surface_distance")
                     col.prop(flow, "volume_density")
 
@@ -173,6 +183,7 @@ class PHYSICS_PT_smoke_fire(PhysicButtonsPanel, Panel):
         domain = context.smoke.domain_settings
 
         split = layout.split()
+        split.enabled = not domain.point_cache.is_baked
 
         col = split.column(align=True)
         col.label(text="Reaction:")
@@ -209,6 +220,7 @@ class PHYSICS_PT_smoke_adaptive_domain(PhysicButtonsPanel, Panel):
         layout.active = domain.use_adaptive_domain
 
         split = layout.split()
+        split.enabled = (not domain.point_cache.is_baked)
 
         col = split.column(align=True)
         col.label(text="Resolution:")
@@ -244,6 +256,7 @@ class PHYSICS_PT_smoke_highres(PhysicButtonsPanel, Panel):
         layout.active = md.use_high_resolution
 
         split = layout.split()
+        split.enabled = not md.point_cache.is_baked
 
         col = split.column()
         col.label(text="Resolution:")
@@ -302,17 +315,27 @@ class PHYSICS_PT_smoke_cache(PhysicButtonsPanel, Panel):
     def draw(self, context):
         layout = self.layout
 
-        if not bpy.app.build_options.openvdb:
-            layout.label("Built without OpenVDB support")
-            return
-
         domain = context.smoke.domain_settings
-
-        layout.label(text="Compression:")
-        layout.prop(domain, "openvdb_cache_compress_type", expand=True)
-        row = layout.row()
-        row.label("Data Depth:")
-        row.prop(domain, "data_depth", expand=True, text="Data Depth")
+        cache_file_format = domain.cache_file_format
+
+        layout.prop(domain, "cache_file_format")
+
+        if cache_file_format == 'POINTCACHE':
+            layout.label(text="Compression:")
+            layout.prop(domain, "point_cache_compress_type", expand=True)
+        elif cache_file_format == 'OPENVDB':
+            if not bpy.app.build_options.openvdb:
+                layout.label("Built without OpenVDB support")
+                return
+
+            layout.label(text="Compression:")
+            layout.prop(domain, "openvdb_cache_compress_type", expand=True)
+            row = layout.row()
+            row.label("Data Depth:")
+            row.prop(domain, "data_depth", expand=True, text="Data Depth")
+
+        cache = domain.point_cache
+        point_cache_ui(self, context, cache, (cache.is_baked is False), 'SMOKE')
 
 
 class PHYSICS_PT_smoke_field_weights(PhysicButtonsPanel, Panel):
index e873bb40013cd916cb311d7c444bf874eecc9451..a458af739f2abe2c84eb35dc84dc1bdcbf81ed43 100644 (file)
@@ -21,13 +21,13 @@ import bpy
 from bpy.types import Panel
 
 from bl_ui.properties_physics_common import (
+        point_cache_ui,
         effector_weights_ui,
         )
 
 
 def softbody_panel_enabled(md):
-    return True
-    #return (md.point_cache.is_baked is False)
+    return (md.point_cache.is_baked is False)
 
 
 class PhysicButtonsPanel:
@@ -71,6 +71,16 @@ class PHYSICS_PT_softbody(PhysicButtonsPanel, Panel):
         layout.prop(softbody, "collision_group")
 
 
+class PHYSICS_PT_softbody_cache(PhysicButtonsPanel, Panel):
+    bl_label = "Soft Body Cache"
+    bl_options = {'DEFAULT_CLOSED'}
+    COMPAT_ENGINES = {'BLENDER_RENDER'}
+
+    def draw(self, context):
+        md = context.soft_body
+        point_cache_ui(self, context, md.point_cache, softbody_panel_enabled(md), 'SOFTBODY')
+
+
 class PHYSICS_PT_softbody_goal(PhysicButtonsPanel, Panel):
     bl_label = "Soft Body Goal"
     bl_options = {'DEFAULT_CLOSED'}
index 7b7e2367a42493be1ddb7521f639a34e5012ffb6..d6253ec7fbc743bc8af7699aee5f3c52b10e74d0 100644 (file)
@@ -27,6 +27,7 @@ from bpy.types import (
 from rna_prop_ui import PropertyPanel
 
 from bl_ui.properties_physics_common import (
+        point_cache_ui,
         effector_weights_ui,
         )
 
@@ -370,6 +371,24 @@ class SCENE_PT_rigid_body_world(SceneButtonsPanel, Panel):
             col.prop(rbw, "solver_iterations", text="Solver Iterations")
 
 
+class SCENE_PT_rigid_body_cache(SceneButtonsPanel, Panel):
+    bl_label = "Rigid Body Cache"
+    bl_options = {'DEFAULT_CLOSED'}
+    COMPAT_ENGINES = {'BLENDER_RENDER'}
+
+    @classmethod
+    def poll(cls, context):
+        rd = context.scene.render
+        scene = context.scene
+        return scene and scene.rigidbody_world and (rd.engine in cls.COMPAT_ENGINES)
+
+    def draw(self, context):
+        scene = context.scene
+        rbw = scene.rigidbody_world
+
+        point_cache_ui(self, context, rbw.point_cache, rbw.point_cache.is_baked is False and rbw.enabled, 'RIGID_BODY')
+
+
 class SCENE_PT_rigid_body_field_weights(SceneButtonsPanel, Panel):
     bl_label = "Rigid Body Field Weights"
     bl_options = {'DEFAULT_CLOSED'}
@@ -408,10 +427,12 @@ class SCENE_PT_simplify(SceneButtonsPanel, Panel):
         col = split.column()
         col.label(text="Viewport:")
         col.prop(rd, "simplify_subdivision", text="Subdivision")
+        col.prop(rd, "simplify_child_particles", text="Child Particles")
 
         col = split.column()
         col.label(text="Render:")
         col.prop(rd, "simplify_subdivision_render", text="Subdivision")
+        col.prop(rd, "simplify_child_particles_render", text="Child Particles")
         col.prop(rd, "simplify_shadow_samples", text="Shadow Samples")
         col.prop(rd, "simplify_ao_sss", text="AO and SSS")
         col.prop(rd, "use_simplify_triangulate")
index 9bbef2ebdaf8b2a145bdf0d2fec28321a16cb334..caf19a9e46909c912993fddb05a8f531f4de7287 100644 (file)
@@ -26,6 +26,7 @@ from bpy.types import (
         Lamp,
         Material,
         Object,
+        ParticleSettings,
         Texture,
         World,
         )
@@ -100,6 +101,9 @@ def context_tex_datablock(context):
     if idblock:
         return idblock
 
+    if context.particle_system:
+        idblock = context.particle_system.settings
+
     return idblock
 
 
@@ -138,6 +142,8 @@ class TEXTURE_PT_context_texture(TextureButtonsPanel, Panel):
                  context.lamp or
                  context.texture or
                  context.line_style or
+                 context.particle_system or
+                 isinstance(context.space_data.pin_id, ParticleSettings) or
                  context.texture_user) and
                 (engine in cls.COMPAT_ENGINES))
 
@@ -805,7 +811,18 @@ class TEXTURE_PT_pointdensity(TextureButtonsPanel, Panel):
         split = layout.split()
 
         col = split.column()
-        if pd.point_source == 'OBJECT':
+        if pd.point_source == 'PARTICLE_SYSTEM':
+            col.label(text="Object:")
+            col.prop(pd, "object", text="")
+
+            sub = col.column()
+            sub.enabled = bool(pd.object)
+            if pd.object:
+                sub.label(text="System:")
+                sub.prop_search(pd, "particle_system", pd.object, "particle_systems", text="")
+            sub.label(text="Cache:")
+            sub.prop(pd, "particle_cache_space", text="")
+        else:
             col.label(text="Object:")
             col.prop(pd, "object", text="")
             col.label(text="Cache:")
@@ -814,7 +831,13 @@ class TEXTURE_PT_pointdensity(TextureButtonsPanel, Panel):
         col.separator()
 
         col.label(text="Color Source:")
-        if pd.point_source == 'OBJECT':
+        if pd.point_source == 'PARTICLE_SYSTEM':
+            col.prop(pd, "particle_color_source", text="")
+            if pd.particle_color_source in {'PARTICLE_SPEED', 'PARTICLE_VELOCITY'}:
+                col.prop(pd, "speed_scale")
+            if pd.particle_color_source in {'PARTICLE_SPEED', 'PARTICLE_AGE'}:
+                layout.template_color_ramp(pd, "color_ramp", expand=True)
+        else:
             col.prop(pd, "vertex_color_source", text="")
             if pd.vertex_color_source == 'VERTEX_COLOR':
                 if pd.object and pd.object.data:
@@ -831,6 +854,8 @@ class TEXTURE_PT_pointdensity(TextureButtonsPanel, Panel):
         col.prop(pd, "falloff", text="")
         if pd.falloff == 'SOFT':
             col.prop(pd, "falloff_soft")
+        if pd.falloff == 'PARTICLE_VELOCITY':
+            col.prop(pd, "falloff_speed_scale")
 
         col.prop(pd, "use_falloff_curve")
 
@@ -1122,6 +1147,35 @@ class TEXTURE_PT_influence(TextureSlotPanel, Panel):
             col = split.column()
             factor_but(col, "use_map_zenith_up", "zenith_up_factor", "Zenith Up")
             factor_but(col, "use_map_zenith_down", "zenith_down_factor", "Zenith Down")
+        elif isinstance(idblock, ParticleSettings):
+            split = layout.split()
+
+            col = split.column()
+            col.label(text="General:")
+            factor_but(col, "use_map_time", "time_factor", "Time")
+            factor_but(col, "use_map_life", "life_factor", "Lifetime")
+            factor_but(col, "use_map_density", "density_factor", "Density")
+            factor_but(col, "use_map_size", "size_factor", "Size")
+
+            col = split.column()
+            col.label(text="Physics:")
+            factor_but(col, "use_map_velocity", "velocity_factor", "Velocity")
+            factor_but(col, "use_map_damp", "damp_factor", "Damp")
+            factor_but(col, "use_map_gravity", "gravity_factor", "Gravity")
+            factor_but(col, "use_map_field", "field_factor", "Force Fields")
+
+            layout.label(text="Hair:")
+
+            split = layout.split()
+
+            col = split.column()
+            factor_but(col, "use_map_length", "length_factor", "Length")
+            factor_but(col, "use_map_clump", "clump_factor", "Clump")
+
+            col = split.column()
+            factor_but(col, "use_map_kink_amp", "kink_amp_factor", "Kink Amplitude")
+            factor_but(col, "use_map_kink_freq", "kink_freq_factor", "Kink Frequency")
+            factor_but(col, "use_map_rough", "rough_factor", "Rough")
 
         elif isinstance(idblock, FreestyleLineStyle):
             split = layout.split()
@@ -1133,17 +1187,18 @@ class TEXTURE_PT_influence(TextureSlotPanel, Panel):
 
         layout.separator()
 
-        split = layout.split()
+        if not isinstance(idblock, ParticleSettings):
+            split = layout.split()
 
-        col = split.column()
-        col.prop(tex, "blend_type", text="Blend")
-        col.prop(tex, "use_rgb_to_intensity")
-        # color is used on gray-scale textures even when use_rgb_to_intensity is disabled.
-        col.prop(tex, "color", text="")
+            col = split.column()
+            col.prop(tex, "blend_type", text="Blend")
+            col.prop(tex, "use_rgb_to_intensity")
+            # color is used on gray-scale textures even when use_rgb_to_intensity is disabled.
+            col.prop(tex, "color", text="")
 
-        col = split.column()
-        col.prop(tex, "invert", text="Negative")
-        col.prop(tex, "use_stencil")
+            col = split.column()
+            col.prop(tex, "invert", text="Negative")
+            col.prop(tex, "use_stencil")
 
         if isinstance(idblock, Material) or isinstance(idblock, World):
             col.prop(tex, "default_value", text="DVar", slider=True)
index be9752a463e05fbfd886e4da32ae54f035883e10..4d365c8dc08e4b5ede08069ebc037e6676de1659 100644 (file)
@@ -92,6 +92,8 @@ def dopesheet_filter(layout, context, genericFiltersOnly=False):
                 row.prop(dopesheet, "show_lattices", text="")
             if bpy.data.armatures:
                 row.prop(dopesheet, "show_armatures", text="")
+            if bpy.data.particles:
+                row.prop(dopesheet, "show_particles", text="")
             if bpy.data.speakers:
                 row.prop(dopesheet, "show_speakers", text="")
             if bpy.data.linestyles:
index 894be977822952646f2ad35af19e5a30e141ce2c..508e62e4f56d5562e7eee390cd1a10849a35ced2 100644 (file)
@@ -171,6 +171,7 @@ class TIME_MT_cache(Menu):
         col = layout.column()
         col.enabled = st.show_cache
         col.prop(st, "cache_softbody")
+        col.prop(st, "cache_particles")
         col.prop(st, "cache_cloth")
         col.prop(st, "cache_smoke")
         col.prop(st, "cache_dynamicpaint")
index 94d7777ad1b92e72fc317d17b8645c43707dd3b8..e5b6a94c1abd883c972975460e9b1a3e24a8496c 100644 (file)
@@ -371,6 +371,7 @@ class USERPREF_PT_edit(Panel):
         col.prop(edit, "use_duplicate_texture", text="Texture")
         #col.prop(edit, "use_duplicate_fcurve", text="F-Curve")
         col.prop(edit, "use_duplicate_action", text="Action")
+        col.prop(edit, "use_duplicate_particle", text="Particle")
 
 
 class USERPREF_PT_system(Panel):
index 2739248391a47b533626f757f1671712815ef330..2798a2fd0d0959e9212ca0be092ece0b1f7ce91f 100644 (file)
@@ -49,9 +49,12 @@ class VIEW3D_HT_header(Header):
 
         if obj:
             mode = obj.mode
+            # Particle edit
+            if mode == 'PARTICLE_EDIT':
+                row.prop(toolsettings.particle_edit, "select_mode", text="", expand=True)
 
             # Occlude geometry
-            if ((view.viewport_shade not in {'BOUNDBOX', 'WIREFRAME'} and (mode == 'EDIT' and obj.type == 'MESH')) or
+            if ((view.viewport_shade not in {'BOUNDBOX', 'WIREFRAME'} and (mode == 'PARTICLE_EDIT' or (mode == 'EDIT' and obj.type == 'MESH'))) or
                     (mode == 'WEIGHT_PAINT')):
                 row.prop(view, "use_occlude_geometry", text="")
 
@@ -61,7 +64,7 @@ class VIEW3D_HT_header(Header):
                 row.prop(toolsettings, "proportional_edit", icon_only=True)
                 if toolsettings.proportional_edit != 'DISABLED':
                     row.prop(toolsettings, "proportional_edit_falloff", icon_only=True)
-            elif mode == 'EDIT':
+            elif mode in {'EDIT', 'PARTICLE_EDIT'}:
                 row = layout.row(align=True)
                 row.prop(toolsettings, "proportional_edit", icon_only=True)
                 if toolsettings.proportional_edit != 'DISABLED':
@@ -700,6 +703,35 @@ class VIEW3D_MT_select_pose(Menu):
         layout.operator("object.select_pattern", text="Select Pattern...")
 
 
+class VIEW3D_MT_select_particle(Menu):
+    bl_label = "Select"
+
+    def draw(self, context):
+        layout = self.layout
+
+        layout.operator("view3d.select_border")
+
+        layout.separator()
+
+        layout.operator("particle.select_all").action = 'TOGGLE'
+        layout.operator("particle.select_linked")
+        layout.operator("particle.select_all", text="Inverse").action = 'INVERT'
+
+        layout.separator()
+
+        layout.operator("particle.select_more")
+        layout.operator("particle.select_less")
+
+        layout.separator()
+
+        layout.operator("particle.select_random")
+
+        layout.separator()
+
+        layout.operator("particle.select_roots", text="Roots")
+        layout.operator("particle.select_tips", text="Tips")
+
+
 class VIEW3D_MT_edit_mesh_select_similar(Menu):
     bl_label = "Select Similar"
 
@@ -1890,8 +1922,87 @@ class VIEW3D_MT_hide_mask(Menu):
         props = layout.operator("paint.mask_lasso_gesture", text="Lasso Mask")
 
 
+# ********** Particle menu **********
+
+
+class VIEW3D_MT_particle(Menu):
+    bl_label = "Particle"
+
+    def draw(self, context):
+        layout = self.layout
+
+        particle_edit = context.tool_settings.particle_edit
+
+        layout.operator("ed.undo")
+        layout.operator("ed.redo")
+        layout.operator("ed.undo_history")
+
+        layout.separator()
+
+        layout.operator("particle.mirror")
+
+        layout.separator()
+
+        layout.operator("particle.remove_doubles")
+        layout.operator("particle.delete")
+
+        if particle_edit.select_mode == 'POINT':
+            layout.operator("particle.subdivide")
+
         layout.operator("particle.unify_length")
+        layout.operator("particle.rekey")
+        layout.operator("particle.weight_set")
+
+        layout.separator()
+
+        layout.menu("VIEW3D_MT_particle_showhide")
+
+
+class VIEW3D_MT_particle_specials(Menu):
+    bl_label = "Specials"
+
+    def draw(self, context):
+        layout = self.layout
+
+        particle_edit = context.tool_settings.particle_edit
+
+        layout.operator("particle.rekey")
+        layout.operator("particle.delete")
+        layout.operator("particle.remove_doubles")
         layout.operator("particle.unify_length")
+
+        if particle_edit.select_mode == 'POINT':
+            layout.operator("particle.subdivide")
+
+        layout.operator("particle.weight_set")
+        layout.separator()
+
+        layout.operator("particle.mirror")
+
+        if particle_edit.select_mode == 'POINT':
+            layout.separator()
+            layout.operator("particle.select_roots")
+            layout.operator("particle.select_tips")
+
+            layout.separator()
+
+            layout.operator("particle.select_random")
+
+            layout.separator()
+
+            layout.operator("particle.select_more")
+            layout.operator("particle.select_less")
+
+            layout.separator()
+
+            layout.operator("particle.select_all").action = 'TOGGLE'
+            layout.operator("particle.select_linked")
+            layout.operator("particle.select_all", text="Inverse").action = 'INVERT'
+
+
+class VIEW3D_MT_particle_showhide(ShowHideMenu, Menu):
+    _operator_name = "particle"
+
 # ********** Pose Menu **********
 
 
index 1671e26e8d476b39cb434664a7e82eee27563fb4..3eb76a3b0f92d5c9f391d69d383075ff53f6c7c0 100644 (file)
@@ -943,12 +943,37 @@ class VIEW3D_PT_tools_brush(Panel, View3DPaintPanel):
         settings = self.paint_settings(context)
         brush = settings.brush
 
-        col = layout.split().column()
-        col.template_ID_preview(settings, "brush", new="brush.add", rows=3, cols=8)
+        if not context.particle_edit_object:
+            col = layout.split().column()
+            col.template_ID_preview(settings, "brush", new="brush.add", rows=3, cols=8)
+
+        # Particle Mode #
+        if context.particle_edit_object:
+            tool = settings.tool
+
+            layout.column().prop(settings, "tool", expand=True)
+
+            if tool != 'NONE':
+                col = layout.column()
+                col.prop(brush, "size", slider=True)
+                if tool != 'ADD':
+                    col.prop(brush, "strength", slider=True)
+
+            if tool == 'ADD':
+                col.prop(brush, "count")
+                col = layout.column()
+                col.prop(settings, "use_default_interpolate")
+                col.prop(brush, "steps", slider=True)
+                col.prop(settings, "default_key_count", slider=True)
+            elif tool == 'LENGTH':
+                layout.prop(brush, "length_mode", expand=True)
+            elif tool == 'PUFF':
+                layout.prop(brush, "puff_mode", expand=True)
+                layout.prop(brush, "use_puff_volume")
 
         # Sculpt Mode #
 
-        if context.sculpt_object and brush:
+        elif context.sculpt_object and brush:
             capabilities = brush.sculpt_capabilities
 
             col = layout.column()
@@ -1633,7 +1658,7 @@ class VIEW3D_PT_tools_brush_appearance(Panel, View3DPaintPanel):
     @classmethod
     def poll(cls, context):
         settings = cls.paint_settings(context)
-        return (settings is not None)
+        return (settings is not None) and (not isinstance(settings, bpy.types.ParticleEdit))
 
     def draw(self, context):
         layout = self.layout
@@ -1856,6 +1881,78 @@ class VIEW3D_MT_tools_projectpaint_stencil(Menu):
             props.value = i
 
 
+class VIEW3D_PT_tools_particlemode(View3DPanel, Panel):
+    """Default tools for particle mode"""
+    bl_context = "particlemode"
+    bl_label = "Options"
+    bl_category = "Tools"
+
+    def draw(self, context):
+        layout = self.layout
+
+        pe = context.tool_settings.particle_edit
+        ob = pe.object
+
+        layout.prop(pe, "type", text="")
+
+        ptcache = None
+
+        if pe.type == 'PARTICLES':
+            if ob.particle_systems:
+                if len(ob.particle_systems) > 1:
+                    layout.template_list("UI_UL_list", "particle_systems", ob, "particle_systems",
+                                         ob.particle_systems, "active_index", rows=2, maxrows=3)
+
+                ptcache = ob.particle_systems.active.point_cache
+        else:
+            for md in ob.modifiers:
+                if md.type == pe.type:
+                    ptcache = md.point_cache
+
+        if ptcache and len(ptcache.point_caches) > 1:
+            layout.template_list("UI_UL_list", "particles_point_caches", ptcache, "point_caches",
+                                 ptcache.point_caches, "active_index", rows=2, maxrows=3)
+
+        if not pe.is_editable:
+            layout.label(text="Point cache must be baked")
+            layout.label(text="in memory to enable editing!")
+
+        col = layout.column(align=True)
+        if pe.is_hair:
+            col.active = pe.is_editable
+            col.prop(pe, "use_emitter_deflect", text="Deflect emitter")
+            sub = col.row(align=True)
+            sub.active = pe.use_emitter_deflect
+            sub.prop(pe, "emitter_distance", text="Distance")
+
+        col = layout.column(align=True)
+        col.active = pe.is_editable
+        col.label(text="Keep:")
+        col.prop(pe, "use_preserve_length", text="Lengths")
+        col.prop(pe, "use_preserve_root", text="Root")
+        if not pe.is_hair:
+            col.label(text="Correct:")
+            col.prop(pe, "use_auto_velocity", text="Velocity")
+        col.prop(ob.data, "use_mirror_x")
+
+        col.prop(pe, "shape_object")
+        col.operator("particle.shape_cut")
+
+        col = layout.column(align=True)
+        col.active = pe.is_editable
+        col.label(text="Draw:")
+        col.prop(pe, "draw_step", text="Path Steps")
+        if pe.is_hair:
+            col.prop(pe, "show_particles", text="Children")
+        else:
+            if pe.type == 'PARTICLES':
+                col.prop(pe, "show_particles", text="Particles")
+            col.prop(pe, "use_fade_time")
+            sub = col.row(align=True)
+            sub.active = pe.use_fade_time
+            sub.prop(pe, "fade_frames", slider=True)
+
+
 # Grease Pencil drawing tools
 class VIEW3D_PT_tools_grease_pencil_draw(GreasePencilDrawingToolsPanel, Panel):
     bl_space_type = 'VIEW_3D'
index d49ceb1636b4bf3979e1638d61e4a1831bd161bb..6f2b78e084529a69af4bef6dc81f39a06ed00e40 100644 (file)
@@ -29,6 +29,7 @@ set(SRC_DNA_INC
        ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_actuator_types.h
        ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_anim_types.h
        ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_armature_types.h
+       ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_boid_types.h
        ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_brush_types.h
        ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_cachefile_types.h
        ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_camera_types.h
@@ -67,6 +68,7 @@ set(SRC_DNA_INC
        ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_object_types.h
        ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_outliner_types.h
        ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_packedFile_types.h
+       ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_particle_types.h
        ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_property_types.h
        ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_rigidbody_types.h
        ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_scene_types.h
index 92fee170e2992ab6cd21284287c09edd956ea98b..ff8b0442ab682c38a0d7e92b4145fdb27efe128f 100644 (file)
@@ -55,6 +55,7 @@ extern "C" {
 #include "BKE_idprop.h"
 #include "BKE_main.h"
 #include "BKE_modifier.h"
+#include "BKE_particle.h"
 #include "BKE_scene.h"
 }
 
@@ -501,6 +502,22 @@ void AbcExporter::createShapeWriter(Object *ob, Object *dupliObParent)
                return;
        }
 
+       ParticleSystem *psys = static_cast<ParticleSystem *>(ob->particlesystem.first);
+
+       for (; psys; psys = psys->next) {
+               if (!psys_check_enabled(ob, psys, G.is_rendering) || !psys->part) {
+                       continue;
+               }
+
+               if (psys->part->type == PART_HAIR) {
+                       m_settings.export_child_hairs = true;
+                       m_shapes.push_back(new AbcHairWriter(m_scene, ob, xform, m_shape_sampling_index, m_settings, psys));
+               }
+               else if (psys->part->type == PART_EMITTER) {
+                       m_shapes.push_back(new AbcPointsWriter(m_scene, ob, xform, m_shape_sampling_index, m_settings, psys));
+               }
+       }
+
        switch(ob->type) {
                case OB_MESH:
                {
index f10cfbadda8347efc02c79e667b6d151b818dbb6..14bcf6731ea51cb5d7e001c9e05c7b13eb955bc6 100644 (file)
@@ -37,6 +37,7 @@ extern "C" {
 
 #include "BKE_DerivedMesh.h"
 #include "BKE_object.h"
+#include "BKE_particle.h"
 }
 
 using Alembic::Abc::P3fArraySamplePtr;
@@ -53,15 +54,13 @@ AbcHairWriter::AbcHairWriter(Scene *scene,
                              AbcTransformWriter *parent,
                              uint32_t time_sampling,
                              ExportSettings &settings,
-                             void *UNUSED(psys))
+                             ParticleSystem *psys)
     : AbcObjectWriter(scene, ob, time_sampling, settings, parent)
 {
-       m_psys = NULL; // = psys;
+       m_psys = psys;
 
-#if 0
        OCurves curves(parent->alembicXform(), psys->name, m_time_sampling);
        m_schema = curves.getSchema();
-#endif
 }
 
 void AbcHairWriter::do_write()
@@ -69,7 +68,7 @@ void AbcHairWriter::do_write()
        if (!m_psys) {
                return;
        }
-#if 0
+
        ParticleSystemModifierData *psmd = psys_get_modifier(m_object, m_psys);
 
        if (!psmd->dm_final) {
@@ -117,17 +116,15 @@ void AbcHairWriter::do_write()
 
        m_sample.setSelfBounds(bounds());
        m_schema.set(m_sample);
-#endif
 }
 
 void AbcHairWriter::write_hair_sample(DerivedMesh *dm,
-                                      void *part,
+                                      ParticleSettings *part,
                                       std::vector<Imath::V3f> &verts,
                                       std::vector<Imath::V3f> &norm_values,
                                       std::vector<Imath::V2f> &uv_values,
                                       std::vector<int32_t> &hvertices)
 {
-#if 0
        /* Get untransformed vertices, there's a xform under the hair. */
        float inv_mat[4][4];
        invert_m4_m4_safe(inv_mat, m_object->obmat);
@@ -228,17 +225,15 @@ void AbcHairWriter::write_hair_sample(DerivedMesh *dm,
                        ++path;
                }
        }
-#endif
 }
 
 void AbcHairWriter::write_hair_child_sample(DerivedMesh *dm,
-                                            void *part,
+                                            ParticleSettings *part,
                                             std::vector<Imath::V3f> &verts,
                                             std::vector<Imath::V3f> &norm_values,
                                             std::vector<Imath::V2f> &uv_values,
                                             std::vector<int32_t> &hvertices)
 {
-#if 0
        /* Get untransformed vertices, there's a xform under the hair. */
        float inv_mat[4][4];
        invert_m4_m4_safe(inv_mat, m_object->obmat);
@@ -292,5 +287,4 @@ void AbcHairWriter::write_hair_child_sample(DerivedMesh *dm,
                        ++path;
                }
        }
-#endif
 }
index bbd5f2c43616a09fc0e1bf5cb94ff0a4d8ea9649..d132b60be12fcd6f180254b8e3f72f637d136950 100644 (file)
 #include "abc_object.h"
 
 struct DerivedMesh;
+struct ParticleSettings;
+struct ParticleSystem;
 
 /* ************************************************************************** */
 
 class AbcHairWriter : public AbcObjectWriter {
-       /*ParticleSystem*/ void *m_psys;
+       ParticleSystem *m_psys;
 
        Alembic::AbcGeom::OCurvesSchema m_schema;
        Alembic::AbcGeom::OCurvesSchema::Sample m_sample;
@@ -41,20 +43,20 @@ public:
                      AbcTransformWriter *parent,
                      uint32_t time_sampling,
                      ExportSettings &settings,
-                     /*ParticleSystem*/void *psys);
+                     ParticleSystem *psys);
 
 private:
        virtual void do_write();
 
        void write_hair_sample(DerivedMesh *dm,
-                              /*ParticleSettings*/ void *part,
+                              ParticleSettings *part,
                               std::vector<Imath::V3f> &verts,
                               std::vector<Imath::V3f> &norm_values,
                               std::vector<Imath::V2f> &uv_values,
                               std::vector<int32_t> &hvertices);
 
        void write_hair_child_sample(DerivedMesh *dm,
-                                    /*ParticleSettings*/ void *part,
+                                    ParticleSettings *part,
                                     std::vector<Imath::V3f> &verts,
                                     std::vector<Imath::V3f> &norm_values,
                                     std::vector<Imath::V2f> &uv_values,
index ad0d0a430c15eef95669202154081b5e0374accd..bdd75f93189c26a6140d7ed875d487278fd4a2e1 100644 (file)
@@ -262,7 +262,7 @@ static ModifierData *get_subsurf_modifier(Scene *scene, Object *ob)
                }
 
                /* mesh is not a subsurf. break */
-               if ((md->type != eModifierType_Displace) /*&& (md->type != eModifierType_ParticleSystem)*/) {
+               if ((md->type != eModifierType_Displace) && (md->type != eModifierType_ParticleSystem)) {
                        return NULL;
                }
        }
index 6602c7e5a855f17e6cf7126c697325e6063f714f..4c78f3e83c7e72cb6fd624f86d4cd30907ef8b4a 100644 (file)
@@ -36,6 +36,7 @@ extern "C" {
 #include "BKE_lattice.h"
 #include "BKE_mesh.h"
 #include "BKE_object.h"
+#include "BKE_particle.h"
 #include "BKE_scene.h"
 
 #include "BLI_math.h"
@@ -62,15 +63,13 @@ AbcPointsWriter::AbcPointsWriter(Scene *scene,
                                     AbcTransformWriter *parent,
                                     uint32_t time_sampling,
                                     ExportSettings &settings,
-                                    void *UNUSED(psys))
+                                    ParticleSystem *psys)
     : AbcObjectWriter(scene, ob, time_sampling, settings, parent)
 {
-       m_psys = NULL; // = psys;
+       m_psys = psys;
 
-#if 0
        OPoints points(parent->alembicXform(), psys->name, m_time_sampling);
        m_schema = points.getSchema();
-#endif
 }
 
 void AbcPointsWriter::do_write()
@@ -78,7 +77,7 @@ void AbcPointsWriter::do_write()
        if (!m_psys) {
                return;
        }
-#if 0
+
        std::vector<Imath::V3f> points;
        std::vector<Imath::V3f> velocities;
        std::vector<float> widths;
@@ -135,7 +134,6 @@ void AbcPointsWriter::do_write()
        m_sample.setSelfBounds(bounds());
 
        m_schema.set(m_sample);
-#endif
 }
 
 /* ************************************************************************** */
index 9864917f47713f04fa666d6bf05055c70b9f9672..cb68dbca4d5023a4666e46f24d235b5825857328 100644 (file)
 #include "abc_object.h"
 #include "abc_customdata.h"
 
+struct ParticleSystem;
+
 /* ************************************************************************** */
 
 class AbcPointsWriter : public AbcObjectWriter {
        Alembic::AbcGeom::OPointsSchema m_schema;
        Alembic::AbcGeom::OPointsSchema::Sample m_sample;
-       /*ParticleSystem*/ void *m_psys;
+       ParticleSystem *m_psys;
 
 public:
        AbcPointsWriter(Scene *scene,
@@ -41,7 +43,7 @@ public:
                        AbcTransformWriter *parent,
                        uint32_t time_sampling,
                        ExportSettings &settings,
-                       /*ParticleSystem*/ void *psys);
+                       ParticleSystem *psys);
 
        void do_write();
 };
diff --git a/source/blender/blenkernel/BKE_boids.h b/source/blender/blenkernel/BKE_boids.h
new file mode 100644 (file)
index 0000000..582cd0c
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2009 by Janne Karhu.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __BKE_BOIDS_H__
+#define __BKE_BOIDS_H__
+
+/** \file BKE_boids.h
+ *  \ingroup bke
+ *  \since 2009
+ *  \author Janne Karhu
+ */
+
+#include "DNA_boid_types.h"
+
+struct RNG;
+
+typedef struct BoidBrainData {
+       struct ParticleSimulationData *sim;
+       struct ParticleSettings *part;
+       float timestep, cfra, dfra;
+       float wanted_co[3], wanted_speed;
+
+       /* Goal stuff */
+       struct Object *goal_ob;
+       float goal_co[3];
+       float goal_nor[3];
+       float goal_priority;
+
+       struct RNG *rng;
+} BoidBrainData;
+
+void boids_precalc_rules(struct ParticleSettings *part, float cfra);
+void boid_brain(BoidBrainData *bbd, int p, struct ParticleData *pa);
+void boid_body(BoidBrainData *bbd, struct ParticleData *pa);
+void boid_default_settings(BoidSettings *boids);
+BoidRule *boid_new_rule(int type);
+BoidState *boid_new_state(BoidSettings *boids);
+BoidState *boid_duplicate_state(BoidSettings *boids, BoidState *state);
+void boid_free_settings(BoidSettings *boids);
+BoidSettings *boid_copy_settings(BoidSettings *boids);
+BoidState *boid_get_current_state(BoidSettings *boids);
+#endif
index 56d9b2439fb7d88a10c0bb834fb8a0bde95e8f66..36330242f184b5776e2a23aa59a5e679b23435aa 100644 (file)
@@ -237,6 +237,9 @@ int cloth_uses_vgroup(struct ClothModifierData *clmd);
 void bvhtree_update_from_cloth(struct ClothModifierData *clmd, bool moving);
 void bvhselftree_update_from_cloth(struct ClothModifierData *clmd, bool moving);
 
+// needed for button_object.c
+void cloth_clear_cache (struct Object *ob, struct ClothModifierData *clmd, float framenr );
+
 // needed for cloth.c
 int cloth_add_spring (struct ClothModifierData *clmd, unsigned int indexA, unsigned int indexB, float restlength, int spring_type);
 
index c6795f87eabaff8f934d382a7e7b9a2d741fac71..4da6a61cbfacb8eaa933c9f7e272f8c3c1bd3d28 100644 (file)
@@ -109,6 +109,7 @@ enum {
        CTX_MODE_PAINT_WEIGHT,
        CTX_MODE_PAINT_VERTEX,
        CTX_MODE_PAINT_TEXTURE,
+       CTX_MODE_PARTICLE,
        CTX_MODE_OBJECT
 };
 
index c552a618cd848164dbf7bbb0b7f3cdc484978020..5abb53d4c5268ada9f91bd85581a31f98060aee9 100644 (file)
@@ -73,6 +73,7 @@ void dynamicPaint_freeCanvas(struct DynamicPaintModifierData *pmd);
 void dynamicPaint_freeBrush(struct DynamicPaintModifierData *pmd);
 void dynamicPaint_freeSurfaceData(struct DynamicPaintSurface *surface);
 
+void dynamicPaint_cacheUpdateFrames(struct DynamicPaintSurface *surface);
 bool  dynamicPaint_surfaceHasColorPreview(struct DynamicPaintSurface *surface);
 bool dynamicPaint_outputLayerExists(struct DynamicPaintSurface *surface, struct Object *ob, int output);
 void dynamicPaintSurface_updateType(struct DynamicPaintSurface *surface);
index 0645bc1e00e804c521639d1f87ba9bc53c51b65a..aa45132cbe988e25534ac4a1acb7c9a80fc37374 100644 (file)
@@ -41,7 +41,9 @@ struct Object;
 struct Scene;
 struct ListBase;
 struct Group;
-struct PointCacheKey;
+struct ParticleSimulationData;
+struct ParticleData;
+struct ParticleKey;
 
 struct EffectorWeights *BKE_add_effector_weights(struct Group *group);
 struct PartDeflect *object_add_collision_fields(int type);
@@ -93,6 +95,7 @@ typedef struct EffectorCache {
 
        struct Scene *scene;
        struct Object *ob;
+       struct ParticleSystem *psys;
        struct SurfaceModifierData *surmd;
        
        struct PartDeflect *pd;
@@ -107,14 +110,17 @@ typedef struct EffectorCache {
 } EffectorCache;
 
 void            free_partdeflect(struct PartDeflect *pd);
-struct ListBase *pdInitEffectors(struct Scene *scene, struct Object *ob_src, struct EffectorWeights *weights, bool for_simulation);
+struct ListBase *pdInitEffectors(struct Scene *scene, struct Object *ob_src, struct ParticleSystem *psys_src, struct EffectorWeights *weights, bool for_simulation);
 void            pdEndEffectors(struct ListBase **effectors);
 void            pdPrecalculateEffectors(struct ListBase *effectors);
 void            pdDoEffectors(struct ListBase *effectors, struct ListBase *colliders, struct EffectorWeights *weights, struct EffectedPoint *point, float *force, float *impulse);
 
+void pd_point_from_particle(struct ParticleSimulationData *sim, struct ParticleData *pa, struct ParticleKey *state, struct EffectedPoint *point);
 void pd_point_from_loc(struct Scene *scene, float *loc, float *vel, int index, struct EffectedPoint *point);
 void pd_point_from_soft(struct Scene *scene, float *loc, float *vel, int index, struct EffectedPoint *point);
 
+/* needed for boids */
+float effector_falloff(struct EffectorCache *eff, struct EffectorData *efd, struct EffectedPoint *point, struct EffectorWeights *weights);
 int closest_point_on_surface(SurfaceModifierData *surmd, const float co[3], float surface_co[3], float surface_nor[3], float surface_vel[3]);
 int get_effector_data(struct EffectorCache *eff, struct EffectorData *efd, struct EffectedPoint *point, int real_velocity);
 
index d3f98f2989ae0e1448f1054a62a08b6995c5e62c..2d9c35f7fd0a7e97c8f5ec921e63bc4ae6767065 100644 (file)
@@ -95,7 +95,7 @@ void id_clear_lib_data_ex(struct Main *bmain, struct ID *id, const bool id_in_ma
 
 struct ListBase *which_libbase(struct Main *mainlib, short type);
 
-#define MAX_LIBARRAY    34
+#define MAX_LIBARRAY    35
 int set_listbasepointers(struct Main *main, struct ListBase *lb[MAX_LIBARRAY]);
 
 /* Main API */
index 7eba01e6d5ad9f055d99ad91600e82742bc15d31..a4f5c42528254c65279e8943037f80f0594b86e3 100644 (file)
@@ -96,6 +96,7 @@ typedef struct Main {
        ListBase action;
        ListBase nodetree;
        ListBase brush;
+       ListBase particle;
        ListBase palettes;
        ListBase paintcurves;
        ListBase wm;
index ab0b120faf3bdfa9df27c69c25c877ff23dd2409..f6c08909d2312e103b5f13fdd75ffa4c18959dcb 100644 (file)
@@ -374,6 +374,7 @@ int           modifiers_getCageIndex(struct Scene *scene, struct Object *ob,
 bool          modifiers_isModifierEnabled(struct Object *ob, int modifierType);
 bool          modifiers_isSoftbodyEnabled(struct Object *ob);
 bool          modifiers_isClothEnabled(struct Object *ob);
+bool          modifiers_isParticleEnabled(struct Object *ob);
 
 struct Object *modifiers_isDeformedByArmature(struct Object *ob);
 struct Object *modifiers_isDeformedByLattice(struct Object *ob);
index 29e8433652666c6129dee1396fc0f24977567625..cf07a178fe8b9514cf1e1af259e3adbfb27d203c 100644 (file)
@@ -55,7 +55,10 @@ void BKE_object_workob_calc_parent(struct Scene *scene, struct Object *ob, struc
 void BKE_object_transform_copy(struct Object *ob_tar, const struct Object *ob_src);
 struct SoftBody *copy_softbody(const struct SoftBody *sb, bool copy_caches);
 struct BulletSoftBody *copy_bulletsoftbody(struct BulletSoftBody *sb);
+struct ParticleSystem *BKE_object_copy_particlesystem(struct ParticleSystem *psys);
+void BKE_object_copy_particlesystems(struct Object *ob_dst, const struct Object *ob_src);
 void BKE_object_copy_softbody(struct Object *ob_dst, const struct Object *ob_src);
+void BKE_object_free_particlesystems(struct Object *ob);
 void BKE_object_free_softbody(struct Object *ob);
 void BKE_object_free_bulletsoftbody(struct Object *ob);
 void BKE_object_free_curve_cache(struct Object *ob);
diff --git a/source/blender/blenkernel/BKE_particle.h b/source/blender/blenkernel/BKE_particle.h
new file mode 100644 (file)
index 0000000..b3e3968
--- /dev/null
@@ -0,0 +1,481 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2007 by Janne Karhu.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Adaptive time step
+ * Classical SPH
+ * Copyright 2011-2012 AutoCRC
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __BKE_PARTICLE_H__
+#define __BKE_PARTICLE_H__
+
+/** \file BKE_particle.h
+ *  \ingroup bke
+ */
+
+#include "BLI_utildefines.h"
+
+#include "DNA_particle_types.h"
+#include "DNA_object_types.h"
+
+#include "BKE_customdata.h"
+
+struct ParticleSystemModifierData;
+struct ParticleSystem;
+struct ParticleKey;
+struct ParticleSettings;
+
+struct Main;
+struct Object;
+struct Scene;
+struct DerivedMesh;
+struct ModifierData;
+struct MTFace;
+struct MCol;
+struct MFace;
+struct MVert;
+struct LatticeDeformData;
+struct LinkNode;
+struct KDTree;
+struct RNG;
+struct BVHTreeRay;
+struct BVHTreeRayHit; 
+struct EdgeHash;
+
+#define PARTICLE_COLLISION_MAX_COLLISIONS 10
+
+#define PARTICLE_P              ParticleData * pa; int p
+#define LOOP_PARTICLES  for (p = 0, pa = psys->particles; p < psys->totpart; p++, pa++)
+#define LOOP_EXISTING_PARTICLES for (p = 0, pa = psys->particles; p < psys->totpart; p++, pa++) if (!(pa->flag & PARS_UNEXIST))
+#define LOOP_SHOWN_PARTICLES for (p = 0, pa = psys->particles; p < psys->totpart; p++, pa++) if (!(pa->flag & (PARS_UNEXIST | PARS_NO_DISP)))
+/* OpenMP: Can only advance one variable within loop definition. */
+#define LOOP_DYNAMIC_PARTICLES for (p = 0; p < psys->totpart; p++) if ((pa = psys->particles + p)->state.time > 0.0f)
+
+/* fast but sure way to get the modifier*/
+#define PARTICLE_PSMD ParticleSystemModifierData * psmd = sim->psmd ? sim->psmd : psys_get_modifier(sim->ob, sim->psys)
+
+/* common stuff that many particle functions need */
+typedef struct ParticleSimulationData {
+       struct Scene *scene;
+       struct Object *ob;
+       struct ParticleSystem *psys;
+       struct ParticleSystemModifierData *psmd;
+       struct ListBase *colliders;
+       /* Courant number. This is used to implement an adaptive time step. Only the
+        * maximum value per time step is important. Only sph_integrate makes use of
+        * this at the moment. Other solvers could, too. */
+       float courant_num;
+} ParticleSimulationData;
+
+typedef struct SPHData {
+       ParticleSystem *psys[10];
+       ParticleData *pa;
+       float mass;
+       struct EdgeHash *eh;
+       float *gravity;
+       float hfac;
+       /* Average distance to neighbours (other particles in the support domain),
+        * for calculating the Courant number (adaptive time step). */
+       int pass;
+       float element_size;
+       float flow[3];
+
+       /* Integrator callbacks. This allows different SPH implementations. */
+       void (*force_cb) (void *sphdata_v, ParticleKey *state, float *force, float *impulse);
+       void (*density_cb) (void *rangedata_v, int index, const float co[3], float squared_dist);
+} SPHData;
+
+typedef struct ParticleTexture {
+       float ivel;                           /* used in reset */
+       float time, life, exist, size;        /* used in init */
+       float damp, gravity, field;           /* used in physics */
+       float length, clump, kink_freq, kink_amp, effector;  /* used in path caching */
+       float rough1, rough2, roughe;         /* used in path caching */
+} ParticleTexture;
+
+typedef struct ParticleSeam {
+       float v0[3], v1[3];
+       float nor[3], dir[3], tan[3];
+       float length2;
+} ParticleSeam;
+
+typedef struct ParticleCacheKey {
+       float co[3];
+       float vel[3];
+       float rot[4];
+       float col[3];
+       float time;
+       int segments;
+} ParticleCacheKey;
+
+typedef struct ParticleThreadContext {
+       /* shared */
+       struct ParticleSimulationData sim;
+       struct DerivedMesh *dm;
+       struct Material *ma;
+
+       /* distribution */
+       struct KDTree *tree;
+
+       struct ParticleSeam *seams;
+       int totseam;
+
+       float *jit, *jitoff, *weight;
+       float maxweight;
+       int *index, *skip, jitlevel;
+
+       int cfrom, distr;
+
+       struct ParticleData *tpars;
+
+       /* path caching */
+       bool editupdate;
+       int between, segments, extra_segments;
+       int totchild, totparent, parent_pass;
+
+       float cfra;
+
+       float *vg_length, *vg_clump, *vg_kink;
+       float *vg_rough1, *vg_rough2, *vg_roughe;
+       float *vg_effector;
+
+       struct CurveMapping *clumpcurve;
+       struct CurveMapping *roughcurve;
+} ParticleThreadContext;
+
+typedef struct ParticleTask {
+       ParticleThreadContext *ctx;
+       struct RNG *rng, *rng_path;
+       int begin, end;
+} ParticleTask;
+
+typedef struct ParticleBillboardData {
+       struct Object *ob;
+       float vec[3], vel[3];
+       float offset[2];
+       float size[2];
+       float tilt, random, time;
+       int uv[3];
+       int lock, num;
+       int totnum;
+       int lifetime;
+       short align, uv_split, anim, split_offset;
+} ParticleBillboardData;
+
+typedef struct ParticleCollisionElement {
+       /* pointers to original data */
+       float *x[3], *v[3];
+
+       /* values interpolated from original data*/
+       float x0[3], x1[3], x2[3], p[3];
+       
+       /* results for found intersection point */
+       float nor[3], vel[3], uv[2];
+
+       /* count of original data (1-4) */
+       int tot;
+
+       /* index of the collision face */
+       int index;
+
+       /* flags for inversed normal / particle already inside element at start */
+       short inv_nor, inside;
+} ParticleCollisionElement;
+
+/* container for moving data between deflet_particle and particle_intersect_face */
+typedef struct ParticleCollision {
+       struct Object *current;
+       struct Object *hit;
+       struct Object *skip[PARTICLE_COLLISION_MAX_COLLISIONS+1];
+       struct Object *emitter;
+
+       struct CollisionModifierData *md; // collision modifier for current object;
+
+       float f;    // time factor of previous collision, needed for substracting face velocity
+       float fac1, fac2;
+
+       float cfra, old_cfra;
+
+       float original_ray_length; //original length of co2-co1, needed for collision time evaluation
+
+       int skip_count;
+
+       ParticleCollisionElement pce;
+
+       /* total_time is the amount of time in this subframe
+        * inv_total_time is the opposite
+        * inv_timestep is the inverse of the amount of time in this frame */
+       float total_time, inv_total_time, inv_timestep;
+
+       float radius;
+       float co1[3], co2[3];
+       float ve1[3], ve2[3];
+
+       float acc[3], boid_z;
+
+       int boid;
+} ParticleCollision;
+
+typedef struct ParticleDrawData {
+       float *vdata, *vd;      /* vertice data */
+       float *ndata, *nd;      /* normal data */
+       float *cdata, *cd;      /* color data */
+       float *vedata, *ved;    /* velocity data */
+       float *ma_col;
+       int tot_vec_size, flag;
+       int totpoint, totve;
+} ParticleDrawData;
+
+#define PARTICLE_DRAW_DATA_UPDATED  1
+
+#define PSYS_FRAND_COUNT    1024
+extern unsigned int PSYS_FRAND_SEED_OFFSET[PSYS_FRAND_COUNT];
+extern unsigned int PSYS_FRAND_SEED_MULTIPLIER[PSYS_FRAND_COUNT];
+extern float PSYS_FRAND_BASE[PSYS_FRAND_COUNT];
+
+void psys_init_rng(void);
+
+BLI_INLINE float psys_frand(ParticleSystem *psys, unsigned int seed)
+{
+       /* XXX far from ideal, this simply scrambles particle random numbers a bit
+        * to avoid obvious correlations.
+        * Can't use previous psys->frand arrays because these require initialization
+        * inside psys_check_enabled, which wreaks havok in multithreaded depgraph updates.
+        */
+       unsigned int offset = PSYS_FRAND_SEED_OFFSET[psys->seed % PSYS_FRAND_COUNT];
+       unsigned int multiplier = PSYS_FRAND_SEED_MULTIPLIER[psys->seed % PSYS_FRAND_COUNT];
+       return PSYS_FRAND_BASE[(offset + seed * multiplier) % PSYS_FRAND_COUNT];
+}
+
+BLI_INLINE void psys_frand_vec(ParticleSystem *psys, unsigned int seed, float vec[3])
+{
+       unsigned int offset = PSYS_FRAND_SEED_OFFSET[psys->seed % PSYS_FRAND_COUNT];
+       unsigned int multiplier = PSYS_FRAND_SEED_MULTIPLIER[psys->seed % PSYS_FRAND_COUNT];
+       vec[0] = PSYS_FRAND_BASE[(offset + (seed + 0) * multiplier) % PSYS_FRAND_COUNT];
+       vec[1] = PSYS_FRAND_BASE[(offset + (seed + 1) * multiplier) % PSYS_FRAND_COUNT];
+       vec[2] = PSYS_FRAND_BASE[(offset + (seed + 2) * multiplier) % PSYS_FRAND_COUNT];
+}
+
+/* ----------- functions needed outside particlesystem ---------------- */
+/* particle.c */
+int count_particles(struct ParticleSystem *psys);
+int count_particles_mod(struct ParticleSystem *psys, int totgr, int cur);
+
+int psys_get_child_number(struct Scene *scene, struct ParticleSystem *psys);
+int psys_get_tot_child(struct Scene *scene, struct ParticleSystem *psys);
+
+struct ParticleSystem *psys_get_current(struct Object *ob);
+/* for rna */
+short psys_get_current_num(struct Object *ob);
+void psys_set_current_num(Object *ob, int index);
+/* UNUSED */
+// struct Object *psys_find_object(struct Scene *scene, struct ParticleSystem *psys);
+
+struct LatticeDeformData *psys_create_lattice_deform_data(struct ParticleSimulationData *sim);
+
+bool psys_in_edit_mode(struct Scene *scene, struct ParticleSystem *psys);
+bool psys_check_enabled(struct Object *ob, struct ParticleSystem *psys, const bool use_render_params);
+bool psys_check_edited(struct ParticleSystem *psys);
+
+void psys_check_group_weights(struct ParticleSettings *part);
+int psys_uses_gravity(struct ParticleSimulationData *sim);
+
+/* free */
+void BKE_particlesettings_free(struct ParticleSettings *part);
+void psys_free_path_cache(struct ParticleSystem *psys, struct PTCacheEdit *edit);
+void psys_free(struct Object *ob, struct ParticleSystem *psys);
+
+void psys_render_set(struct Object *ob, struct ParticleSystem *psys, float viewmat[4][4], float winmat[4][4], int winx, int winy, int timeoffset);
+void psys_render_restore(struct Object *ob, struct ParticleSystem *psys);
+bool psys_render_simplify_params(struct ParticleSystem *psys, struct ChildParticle *cpa, float *params);
+
+void psys_interpolate_uvs(const struct MTFace *tface, int quad, const float w[4], float uvco[2]);
+void psys_interpolate_mcol(const struct MCol *mcol, int quad, const float w[4], struct MCol *mc);
+
+void copy_particle_key(struct ParticleKey *to, struct ParticleKey *from, int time);
+
+CustomDataMask psys_emitter_customdata_mask(struct ParticleSystem *psys);
+void psys_particle_on_emitter(struct ParticleSystemModifierData *psmd, int distr, int index, int index_dmcache,
+                              float fuv[4], float foffset, float vec[3], float nor[3],
+                              float utan[3], float vtan[3], float orco[3], float ornor[3]);
+struct ParticleSystemModifierData *psys_get_modifier(struct Object *ob, struct ParticleSystem *psys);
+
+struct ModifierData *object_add_particle_system(struct Scene *scene, struct Object *ob, const char *name);
+void object_remove_particle_system(struct Scene *scene, struct Object *ob);
+struct ParticleSettings *psys_new_settings(const char *name, struct Main *main);
+struct ParticleSettings *BKE_particlesettings_copy(struct Main *bmain, struct ParticleSettings *part);
+void BKE_particlesettings_make_local(struct Main *bmain, struct ParticleSettings *part, const bool lib_local);
+
+void psys_reset(struct ParticleSystem *psys, int mode);
+
+void psys_find_parents(struct ParticleSimulationData *sim, const bool use_render_params);
+
+void psys_cache_paths(struct ParticleSimulationData *sim, float cfra, const bool use_render_params);
+void psys_cache_edit_paths(struct Scene *scene, struct Object *ob, struct PTCacheEdit *edit, float cfra, const bool use_render_params);
+void psys_cache_child_paths(struct ParticleSimulationData *sim, float cfra, const bool editupdate, const bool use_render_params);
+int do_guides(struct ParticleSettings *part, struct ListBase *effectors, ParticleKey *state, int pa_num, float time);
+void precalc_guides(struct ParticleSimulationData *sim, struct ListBase *effectors);
+float psys_get_timestep(struct ParticleSimulationData *sim);
+float psys_get_child_time(struct ParticleSystem *psys, struct ChildParticle *cpa, float cfra, float *birthtime, float *dietime);
+float psys_get_child_size(struct ParticleSystem *psys, struct ChildParticle *cpa, float cfra, float *pa_time);
+void psys_get_particle_on_path(struct ParticleSimulationData *sim, int pa_num, struct ParticleKey *state, const bool vel);
+int psys_get_particle_state(struct ParticleSimulationData *sim, int p, struct ParticleKey *state, int always);
+
+/* child paths */
+void BKE_particlesettings_clump_curve_init(struct ParticleSettings *part);
+void BKE_particlesettings_rough_curve_init(struct ParticleSettings *part);
+void psys_apply_child_modifiers(struct ParticleThreadContext *ctx, struct ListBase *modifiers,
+                                struct ChildParticle *cpa, struct ParticleTexture *ptex, const float orco[3], const float ornor[3], float hairmat[4][4],
+                                struct ParticleCacheKey *keys, struct ParticleCacheKey *parent_keys, const float parent_orco[3]);
+
+void psys_sph_init(struct ParticleSimulationData *sim, struct SPHData *sphdata);
+void psys_sph_finalise(struct SPHData *sphdata);
+void psys_sph_density(struct BVHTree *tree, struct SPHData *data, float co[3], float vars[2]);
+
+/* for anim.c */
+void psys_get_dupli_texture(struct ParticleSystem *psys, struct ParticleSettings *part,
+                            struct ParticleSystemModifierData *psmd, struct ParticleData *pa, struct ChildParticle *cpa,
+                            float uv[2], float orco[3]);
+void psys_get_dupli_path_transform(struct ParticleSimulationData *sim, struct ParticleData *pa, struct ChildParticle *cpa,
+                                   struct ParticleCacheKey *cache, float mat[4][4], float *scale);
+
+void psys_thread_context_init(struct ParticleThreadContext *ctx, struct ParticleSimulationData *sim);
+void psys_thread_context_free(struct ParticleThreadContext *ctx);
+void psys_tasks_create(struct ParticleThreadContext *ctx, int startpart, int endpart, struct ParticleTask **r_tasks, int *r_numtasks);
+void psys_tasks_free(struct ParticleTask *tasks, int numtasks);
+
+void psys_make_billboard(ParticleBillboardData *bb, float xvec[3], float yvec[3], float zvec[3], float center[3]);
+void psys_apply_hair_lattice(struct Scene *scene, struct Object *ob, struct ParticleSystem *psys);
+
+/* particle_system.c */
+struct ParticleSystem *psys_get_target_system(struct Object *ob, struct ParticleTarget *pt);
+void psys_count_keyed_targets(struct ParticleSimulationData *sim);
+void psys_update_particle_tree(struct ParticleSystem *psys, float cfra);
+void psys_changed_type(struct Object *ob, struct ParticleSystem *psys);
+
+void psys_make_temp_pointcache(struct Object *ob, struct ParticleSystem *psys);
+void psys_get_pointcache_start_end(struct Scene *scene, ParticleSystem *psys, int *sfra, int *efra);
+
+void psys_check_boid_data(struct ParticleSystem *psys);
+
+void psys_get_birth_coords(struct ParticleSimulationData *sim, struct ParticleData *pa, struct ParticleKey *state, float dtime, float cfra);
+
+void particle_system_update(struct Scene *scene, struct Object *ob, struct ParticleSystem *psys, const bool use_render_params);
+
+/* Callback format for performing operations on ID-pointers for particle systems */
+typedef void (*ParticleSystemIDFunc)(struct ParticleSystem *psys, struct ID **idpoin, void *userdata, int cd_flag);
+
+void BKE_particlesystem_id_loop(struct ParticleSystem *psys, ParticleSystemIDFunc func, void *userdata);
+
+/* ----------- functions needed only inside particlesystem ------------ */
+/* particle.c */
+void psys_disable_all(struct Object *ob);
+void psys_enable_all(struct Object *ob);
+
+void free_hair(struct Object *ob, struct ParticleSystem *psys, int dynamics);
+void free_keyed_keys(struct ParticleSystem *psys);
+void psys_free_particles(struct ParticleSystem *psys);
+void psys_free_children(struct ParticleSystem *psys);
+
+void psys_interpolate_particle(short type, struct ParticleKey keys[4], float dt, struct ParticleKey *result, bool velocity);
+void psys_vec_rot_to_face(struct DerivedMesh *dm, struct ParticleData *pa, float vec[3]);
+void psys_mat_hair_to_object(struct Object *ob, struct DerivedMesh *dm, short from, struct ParticleData *pa, float hairmat[4][4]);
+void psys_mat_hair_to_global(struct Object *ob, struct DerivedMesh *dm, short from, struct ParticleData *pa, float hairmat[4][4]);
+void psys_mat_hair_to_orco(struct Object *ob, struct DerivedMesh *dm, short from, struct ParticleData *pa, float hairmat[4][4]);
+
+float psys_get_dietime_from_cache(struct PointCache *cache, int index);
+
+void psys_free_pdd(struct ParticleSystem *psys);
+
+float *psys_cache_vgroup(struct DerivedMesh *dm, struct ParticleSystem *psys, int vgroup);
+void psys_get_texture(struct ParticleSimulationData *sim, struct ParticleData *pa, struct ParticleTexture *ptex, int event, float cfra);
+void psys_interpolate_face(struct MVert *mvert, struct MFace *mface, struct MTFace *tface,
+                           float (*orcodata)[3], float w[4], float vec[3], float nor[3], float utan[3], float vtan[3],
+                           float orco[3], float ornor[3]);
+float psys_particle_value_from_verts(struct DerivedMesh *dm, short from, struct ParticleData *pa, float *values);
+void psys_get_from_key(struct ParticleKey *key, float loc[3], float vel[3], float rot[4], float *time);
+
+/* BLI_bvhtree_ray_cast callback */
+void BKE_psys_collision_neartest_cb(void *userdata, int index, const struct BVHTreeRay *ray, struct BVHTreeRayHit *hit);
+void psys_particle_on_dm(struct DerivedMesh *dm_final, int from, int index, int index_dmcache,
+                         const float fw[4], float foffset, float vec[3], float nor[3], float utan[3], float vtan[3],
+                         float orco[3], float ornor[3]);
+
+/* particle_system.c */
+void distribute_particles(struct ParticleSimulationData *sim, int from);
+void initialize_particle(struct ParticleSimulationData *sim, struct ParticleData *pa);
+void psys_calc_dmcache(struct Object *ob, struct DerivedMesh *dm_final, struct DerivedMesh *dm_deformed, struct ParticleSystem *psys);
+int psys_particle_dm_face_lookup(struct DerivedMesh *dm_final, struct DerivedMesh *dm_deformed, int findex, const float fw[4], struct LinkNode **poly_nodes);
+
+void reset_particle(struct ParticleSimulationData *sim, struct ParticleData *pa, float dtime, float cfra);
+
+float psys_get_current_display_percentage(struct ParticleSystem *psys);
+
+typedef struct ParticleRenderElem {
+       int curchild, totchild, reduce;
+       float lambda, t, scalemin, scalemax;
+} ParticleRenderElem;
+
+typedef struct ParticleRenderData {
+       ChildParticle *child;
+       ParticleCacheKey **pathcache;
+       ParticleCacheKey **childcache;
+       ListBase pathcachebufs, childcachebufs;
+       int totchild, totcached, totchildcache;
+       struct DerivedMesh *dm;
+       int totdmvert, totdmedge, totdmface;
+
+       float mat[4][4];
+       float viewmat[4][4], winmat[4][4];
+       int winx, winy;
+
+       int do_simplify;
+       int timeoffset;
+       ParticleRenderElem *elems;
+
+       /* ORIGINDEX */
+       const int *index_mf_to_mpoly;
+       const int *index_mp_to_orig;
+} ParticleRenderData;
+
+/* psys_reset */
+#define PSYS_RESET_ALL          1
+#define PSYS_RESET_DEPSGRAPH    2
+/* #define PSYS_RESET_CHILDREN  3 */ /*UNUSED*/
+#define PSYS_RESET_CACHE_MISS   4
+
+/* index_dmcache */
+#define DMCACHE_NOTFOUND    -1
+#define DMCACHE_ISCHILD     -2
+
+/* **** Depsgraph evaluation **** */
+
+struct EvaluationContext;
+
+void BKE_particle_system_eval(struct EvaluationContext *eval_ctx,
+                              struct Scene *scene,
+                              struct Object *ob,
+                              struct ParticleSystem *psys);
+
+#endif
diff --git a/source/blender/blenkernel/BKE_pointcache.h b/source/blender/blenkernel/BKE_pointcache.h
new file mode 100644 (file)
index 0000000..02f6c43
--- /dev/null
@@ -0,0 +1,348 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software  Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2006 Blender Foundation.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): Campbell Barton <ideasman42@gmail.com>
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __BKE_POINTCACHE_H__
+#define __BKE_POINTCACHE_H__
+
+/** \file BKE_pointcache.h
+ *  \ingroup bke
+ */
+
+#include "DNA_ID.h"
+#include "DNA_dynamicpaint_types.h"
+#include "DNA_object_force.h"
+#include "DNA_boid_types.h"
+#include <stdio.h> /* for FILE */
+
+/* Point cache clearing option, for BKE_ptcache_id_clear, before
+ * and after are non inclusive (they wont remove the cfra) */
+#define PTCACHE_CLEAR_ALL       0
+#define PTCACHE_CLEAR_FRAME     1
+#define PTCACHE_CLEAR_BEFORE    2
+#define PTCACHE_CLEAR_AFTER     3
+
+/* Point cache reset options */
+#define PTCACHE_RESET_DEPSGRAPH     0
+#define PTCACHE_RESET_BAKED         1
+#define PTCACHE_RESET_OUTDATED      2
+/* #define PTCACHE_RESET_FREE                  3 */ /*UNUSED*/
+
+/* Add the blendfile name after blendcache_ */
+#define PTCACHE_EXT ".bphys"
+#define PTCACHE_PATH "blendcache_"
+
+/* File open options, for BKE_ptcache_file_open */
+#define PTCACHE_FILE_READ   0
+#define PTCACHE_FILE_WRITE  1
+#define PTCACHE_FILE_UPDATE 2
+
+/* PTCacheID types */
+#define PTCACHE_TYPE_SOFTBODY           0
+#define PTCACHE_TYPE_PARTICLES          1
+#define PTCACHE_TYPE_CLOTH              2
+#define PTCACHE_TYPE_SMOKE_DOMAIN       3
+#define PTCACHE_TYPE_SMOKE_HIGHRES      4
+#define PTCACHE_TYPE_DYNAMICPAINT       5
+#define PTCACHE_TYPE_RIGIDBODY          6
+
+/* high bits reserved for flags that need to be stored in file */
+#define PTCACHE_TYPEFLAG_COMPRESS       (1 << 16)
+#define PTCACHE_TYPEFLAG_EXTRADATA      (1 << 17)
+
+#define PTCACHE_TYPEFLAG_TYPEMASK           0x0000FFFF
+#define PTCACHE_TYPEFLAG_FLAGMASK           0xFFFF0000
+
+/* PTCache read return code */
+#define PTCACHE_READ_EXACT              1
+#define PTCACHE_READ_INTERPOLATED       2
+#define PTCACHE_READ_OLD                3
+
+/* Structs */
+struct ClothModifierData;
+struct ListBase;
+struct Main;
+struct Object;
+struct ParticleKey;
+struct ParticleSystem;
+struct PointCache;
+struct Scene;
+struct SmokeModifierData;
+struct SoftBody;
+struct RigidBodyWorld;
+
+struct OpenVDBReader;
+struct OpenVDBWriter;
+
+/* temp structure for read/write */
+typedef struct PTCacheData {
+       unsigned int index;
+       float loc[3];
+       float vel[3];
+       float rot[4];
+       float ave[3];
+       float size;
+       float times[3];
+       struct BoidData boids;
+} PTCacheData;
+
+typedef struct PTCacheFile {
+       FILE *fp;
+
+       int frame, old_format;
+       unsigned int totpoint, type;
+       unsigned int data_types, flag;
+
+       struct PTCacheData data;
+       void *cur[BPHYS_TOT_DATA];
+} PTCacheFile;
+
+#define PTCACHE_VEL_PER_SEC     1
+
+enum {
+       PTCACHE_FILE_PTCACHE = 0,
+       PTCACHE_FILE_OPENVDB = 1,
+};
+
+typedef struct PTCacheID {
+       struct PTCacheID *next, *prev;
+
+       struct Scene *scene;
+       struct Object *ob;
+       void *calldata;
+       unsigned int type, file_type;
+       unsigned int stack_index;
+       unsigned int flag;
+
+       unsigned int default_step;
+       unsigned int max_step;
+
+       /* flags defined in DNA_object_force.h */
+       unsigned int data_types, info_types;
+
+       /* copies point data to cache data */
+       int (*write_point)(int index, void *calldata, void **data, int cfra);
+       /* copies cache cata to point data */
+       void (*read_point)(int index, void *calldata, void **data, float cfra, float *old_data);
+       /* interpolated between previously read point data and cache data */
+       void (*interpolate_point)(int index, void *calldata, void **data, float cfra, float cfra1, float cfra2, float *old_data);
+
+       /* copies point data to cache data */
+       int (*write_stream)(PTCacheFile *pf, void *calldata);
+       /* copies cache cata to point data */
+       int (*read_stream)(PTCacheFile *pf, void *calldata);
+
+       /* copies point data to cache data */
+       int (*write_openvdb_stream)(struct OpenVDBWriter *writer, void *calldata);
+       /* copies cache cata to point data */
+       int (*read_openvdb_stream)(struct OpenVDBReader *reader, void *calldata);
+
+       /* copies custom extradata to cache data */
+       void (*write_extra_data)(void *calldata, struct PTCacheMem *pm, int cfra);
+       /* copies custom extradata to cache data */
+       void (*read_extra_data)(void *calldata, struct PTCacheMem *pm, float cfra);
+       /* copies custom extradata to cache data */
+       void (*interpolate_extra_data)(void *calldata, struct PTCacheMem *pm, float cfra, float cfra1, float cfra2);
+
+       /* total number of simulated points (the cfra parameter is just for using same function pointer with totwrite) */
+       int (*totpoint)(void *calldata, int cfra);
+       /* report error if number of points does not match */
+       void (*error)(void *calldata, const char *message);
+       /* number of points written for current cache frame */
+       int (*totwrite)(void *calldata, int cfra);
+
+       int (*write_header)(PTCacheFile *pf);
+       int (*read_header)(PTCacheFile *pf);
+
+       struct PointCache *cache;
+       /* used for setting the current cache from ptcaches list */
+       struct PointCache **cache_ptr;
+       struct ListBase *ptcaches;
+} PTCacheID;
+
+typedef struct PTCacheBaker {
+       struct Main *main;
+       struct Scene *scene;
+       int bake;
+       int render;
+       int anim_init;
+       int quick_step;
+       struct PTCacheID pid;
+
+       void (*update_progress)(void *data, float progress, int *cancel);
+       void *bake_job;
+} PTCacheBaker;
+
+/* PTCacheEditKey->flag */
+#define PEK_SELECT      1
+#define PEK_TAG         2
+#define PEK_HIDE        4
+#define PEK_USE_WCO     8
+
+typedef struct PTCacheEditKey {
+       float *co;
+       float *vel;
+       float *rot;
+       float *time;
+
+       float world_co[3];
+       float ftime;
+       float length;
+       short flag;
+} PTCacheEditKey;
+
+/* PTCacheEditPoint->flag */
+#define PEP_TAG             1
+#define PEP_EDIT_RECALC     2
+#define PEP_TRANSFORM       4
+#define PEP_HIDE            8
+
+typedef struct PTCacheEditPoint {
+       struct PTCacheEditKey *keys;
+       int totkey;
+       short flag;
+} PTCacheEditPoint;
+
+typedef struct PTCacheUndo {
+       struct PTCacheUndo *next, *prev;
+       struct PTCacheEditPoint *points;
+
+       /* particles stuff */
+       struct ParticleData *particles;
+       struct KDTree *emitter_field;
+       float *emitter_cosnos;
+       int psys_flag;
+
+       /* cache stuff */
+       struct ListBase mem_cache;
+
+       int totpoint;
+       char name[64];
+} PTCacheUndo;
+
+typedef struct PTCacheEdit {
+       ListBase undo;
+       struct PTCacheUndo *curundo;
+       PTCacheEditPoint *points;
+
+       struct PTCacheID pid;
+
+       /* particles stuff */
+       struct ParticleSystem *psys;
+       struct KDTree *emitter_field;
+       float *emitter_cosnos; /* localspace face centers and normals (average of its verts), from the derived mesh */
+       int *mirror_cache;
+
+       struct ParticleCacheKey **pathcache;    /* path cache (runtime) */
+       ListBase pathcachebufs;
+
+       int totpoint, totframes, totcached, edited;
+
+       unsigned char sel_col[3];
+       unsigned char nosel_col[3];
+} PTCacheEdit;
+
+/* Particle functions */
+void BKE_ptcache_make_particle_key(struct ParticleKey *key, int index, void **data, float time);
+
+/**************** Creating ID's ****************************/
+void BKE_ptcache_id_from_softbody(PTCacheID *pid, struct Object *ob, struct SoftBody *sb);
+void BKE_ptcache_id_from_particles(PTCacheID *pid, struct Object *ob, struct ParticleSystem *psys);
+void BKE_ptcache_id_from_cloth(PTCacheID *pid, struct Object *ob, struct ClothModifierData *clmd);
+void BKE_ptcache_id_from_smoke(PTCacheID *pid, struct Object *ob, struct SmokeModifierData *smd);
+void BKE_ptcache_id_from_dynamicpaint(PTCacheID *pid, struct Object *ob, struct DynamicPaintSurface *surface);
+void BKE_ptcache_id_from_rigidbody(PTCacheID *pid, struct Object *ob, struct RigidBodyWorld *rbw);
+
+void BKE_ptcache_ids_from_object(struct ListBase *lb, struct Object *ob, struct Scene *scene, int duplis);
+
+/***************** Global funcs ****************************/
+void BKE_ptcache_remove(void);
+
+/************ ID specific functions ************************/
+void    BKE_ptcache_id_clear(PTCacheID *id, int mode, unsigned int cfra);
+int     BKE_ptcache_id_exist(PTCacheID *id, int cfra);
+int     BKE_ptcache_id_reset(struct Scene *scene, PTCacheID *id, int mode);
+void    BKE_ptcache_id_time(PTCacheID *pid, struct Scene *scene, float cfra, int *startframe, int *endframe, float *timescale);
+int     BKE_ptcache_object_reset(struct Scene *scene, struct Object *ob, int mode);
+
+void BKE_ptcache_update_info(PTCacheID *pid);
+
+/*********** General cache reading/writing ******************/
+
+/* Size of cache data type. */
+int     BKE_ptcache_data_size(int data_type);
+
+/* Is point with indes in memory cache */
+int BKE_ptcache_mem_index_find(struct PTCacheMem *pm, unsigned int index);
+
+/* Memory cache read/write helpers. */
+void BKE_ptcache_mem_pointers_init(struct PTCacheMem *pm);
+void BKE_ptcache_mem_pointers_incr(struct PTCacheMem *pm);
+int  BKE_ptcache_mem_pointers_seek(int point_index, struct PTCacheMem *pm);
+
+/* Main cache reading call. */
+int     BKE_ptcache_read(PTCacheID *pid, float cfra, bool no_extrapolate_old);
+
+/* Main cache writing call. */
+int     BKE_ptcache_write(PTCacheID *pid, unsigned int cfra);
+
+/******************* Allocate & free ***************/
+struct PointCache *BKE_ptcache_add(struct ListBase *ptcaches);
+void BKE_ptcache_free_mem(struct ListBase *mem_cache);
+void BKE_ptcache_free(struct PointCache *cache);
+void BKE_ptcache_free_list(struct ListBase *ptcaches);
+struct PointCache *BKE_ptcache_copy_list(struct ListBase *ptcaches_new, const struct ListBase *ptcaches_old, bool copy_data);
+
+/********************** Baking *********************/
+
+/* Bakes cache with cache_step sized jumps in time, not accurate but very fast. */
+void BKE_ptcache_quick_cache_all(struct Main *bmain, struct Scene *scene);
+
+/* Bake cache or simulate to current frame with settings defined in the baker. */
+void BKE_ptcache_bake(struct PTCacheBaker *baker);
+
+/* Convert disk cache to memory cache. */
+void BKE_ptcache_disk_to_mem(struct PTCacheID *pid);
+
+/* Convert memory cache to disk cache. */
+void BKE_ptcache_mem_to_disk(struct PTCacheID *pid);
+
+/* Convert disk cache to memory cache and vice versa. Clears the cache that was converted. */
+void BKE_ptcache_toggle_disk_cache(struct PTCacheID *pid);
+
+/* Rename all disk cache files with a new name. Doesn't touch the actual content of the files. */
+void BKE_ptcache_disk_cache_rename(struct PTCacheID *pid, const char *name_src, const char *name_dst);
+
+/* Loads simulation from external (disk) cache files. */
+void BKE_ptcache_load_external(struct PTCacheID *pid);
+
+/* Set correct flags after successful simulation step */
+void BKE_ptcache_validate(struct PointCache *cache, int framenr);
+
+/* Set correct flags after unsuccessful simulation step */
+void BKE_ptcache_invalidate(struct PointCache *cache);
+
+#endif
index 20b16b6cc2d248a7eecdc1413846fef5d8906d1a..965a97f08baa1fe9e6d38b0f1d5b7e5a2ca25984 100644 (file)
@@ -98,6 +98,7 @@ void BKE_rigidbody_remove_constraint(struct Scene *scene, struct Object *ob);
 void BKE_rigidbody_aftertrans_update(struct Object *ob, float loc[3], float rot[3], float quat[4], float rotAxis[3], float rotAngle);
 void BKE_rigidbody_sync_transforms(struct RigidBodyWorld *rbw, struct Object *ob, float ctime);
 bool BKE_rigidbody_check_sim_running(struct RigidBodyWorld *rbw, float ctime);
+void BKE_rigidbody_cache_reset(struct RigidBodyWorld *rbw);
 void BKE_rigidbody_rebuild_world(struct Scene *scene, float ctime);
 void BKE_rigidbody_do_simulation(struct Scene *scene, float ctime);
 
index 2993540bb4f830028ad757bf0d0287b9fb815bc7..a504f1bac3d97210f2e200582d2a709686c160f8 100644 (file)
@@ -86,7 +86,7 @@ void BKE_sca_controllers_id_loop(struct ListBase *contlist, SCAControllerIDFunc
 void BKE_sca_actuators_id_loop(struct ListBase *atclist, SCAActuatorIDFunc func, void *userdata);
 
 
-const char *sca_state_name_get(struct Object *ob, short bit);
+const char *sca_state_name_get(Object *ob, short bit);
 
 #endif
 
index 4bce0cd609cb904cd3fc16f8513badc01ed96191..486fe8ed5a8396cf2dbd2fd4199583f926a80d54 100644 (file)
@@ -68,7 +68,7 @@ extern void             sbObjectToSoftbody(struct Object *ob);
 /* pass NULL to unlink again */
 extern void             sbSetInterruptCallBack(int (*f)(void));
 
-extern void             SB_estimate_transform(struct Object *ob, float lloc[3], float lrot[3][3], float lscale[3][3]);
+extern void             SB_estimate_transform(Object *ob, float lloc[3], float lrot[3][3], float lscale[3][3]);
 
 
 #endif
index df1fd945eb49a9e48fbc5f0bed8e9279e5443696..1c5ea946f59ad60b310a3f3d3fa140716b32083e 100644 (file)
@@ -47,6 +47,7 @@ struct Main;
 struct Material;
 struct MTex;
 struct OceanTex;
+struct ParticleSettings;
 struct PointDensity;
 struct Tex;
 struct TexMapping;
@@ -86,6 +87,7 @@ struct Tex *give_current_lamp_texture(struct Lamp *la);
 struct Tex *give_current_linestyle_texture(struct FreestyleLineStyle *linestyle);
 struct Tex *give_current_world_texture(struct World *world);
 struct Tex *give_current_brush_texture(struct Brush *br);
+struct Tex *give_current_particle_texture(struct ParticleSettings *part);
 
 struct bNode *give_current_material_texture_node(struct Material *ma);
 
@@ -97,6 +99,7 @@ void set_current_world_texture(struct World *wo, struct Tex *tex);
 void set_current_material_texture(struct Material *ma, struct Tex *tex);
 void set_current_lamp_texture(struct Lamp *la, struct Tex *tex);
 void set_current_linestyle_texture(struct FreestyleLineStyle *linestyle, struct Tex *tex);
+void set_current_particle_texture(struct ParticleSettings *part, struct Tex *tex);
 
 bool has_current_material_texture(struct Material *ma);
 
index ca55ba0226a7f11a8f6c6234f63325683b11895d..157c4408d6ac8713ebfe8b9bc959ed499da62e5a 100644 (file)
@@ -76,6 +76,7 @@ set(SRC
        intern/blender_undo.c
        intern/blendfile.c
        intern/bmfont.c
+       intern/boids.c
        intern/bpath.c
        intern/brush.c
        intern/bullet.c
@@ -147,8 +148,13 @@ set(SRC
        intern/outliner_treehash.c
        intern/packedFile.c
        intern/paint.c
+       intern/particle.c
+       intern/particle_child.c
+       intern/particle_distribute.c
+       intern/particle_system.c
        intern/pbvh.c
        intern/pbvh_bmesh.c
+       intern/pointcache.c
        intern/property.c
        intern/report.c
        intern/rigidbody.c
@@ -197,6 +203,7 @@ set(SRC
        BKE_blendfile.h
        BKE_bmfont.h
        BKE_bmfont_types.h
+       BKE_boids.h
        BKE_bpath.h
        BKE_brush.h
        BKE_bullet.h
@@ -261,7 +268,9 @@ set(SRC
        BKE_outliner_treehash.h
        BKE_packedFile.h
        BKE_paint.h
+       BKE_particle.h
        BKE_pbvh.h
+       BKE_pointcache.h
        BKE_property.h
        BKE_report.h
        BKE_rigidbody.h
index 17d76bb290a923f00ba970078cf0d61c34a5f5a4..7d3d12ac1120fa8e234d0110ddbab8440f601599 100644 (file)
@@ -41,7 +41,6 @@
 #include "DNA_anim_types.h"
 #include "DNA_armature_types.h"
 #include "DNA_key_types.h"
-#include "DNA_object_types.h"
 #include "DNA_scene_types.h"
 
 #include "BKE_curve.h"
@@ -50,6 +49,7 @@
 #include "BKE_key.h"
 #include "BKE_main.h"
 #include "BKE_object.h"
+#include "BKE_particle.h"
 #include "BKE_scene.h"
 #include "BKE_anim.h"
 #include "BKE_report.h"
index 9bf4eba0f7ae5e35b202b7e0884bd2c0a3b58055..212793923927c4423fb6a7fe5d6f832cff9efdc8 100644 (file)
@@ -88,6 +88,7 @@ bool id_type_can_have_animdata(const short id_type)
                case ID_OB:
                case ID_ME: case ID_MB: case ID_CU: case ID_AR: case ID_LT:
                case ID_KE:
+               case ID_PA:
                case ID_MA: case ID_TE: case ID_NT:
                case ID_LA: case ID_CA: case ID_WO:
                case ID_LS:
@@ -1136,6 +1137,9 @@ void BKE_animdata_main_cb(Main *mainptr, ID_AnimData_Edit_Callback func, void *u
        /* meshes */
        ANIMDATA_IDS_CB(mainptr->mesh.first);
        
+       /* particles */
+       ANIMDATA_IDS_CB(mainptr->particle.first);
+
        /* speakers */
        ANIMDATA_IDS_CB(mainptr->speaker.first);
 
@@ -1229,6 +1233,9 @@ void BKE_animdata_fix_paths_rename_all(ID *ref_id, const char *prefix, const cha
        /* meshes */
        RENAMEFIX_ANIM_IDS(mainptr->mesh.first);
        
+       /* particles */
+       RENAMEFIX_ANIM_IDS(mainptr->particle.first);
+
        /* speakers */
        RENAMEFIX_ANIM_IDS(mainptr->speaker.first);
 
@@ -2861,6 +2868,9 @@ void BKE_animsys_evaluate_all_animation(Main *main, Scene *scene, float ctime)
        /* meshes */
        EVAL_ANIM_IDS(main->mesh.first, ADT_RECALC_ANIM);
        
+       /* particles */
+       EVAL_ANIM_IDS(main->particle.first, ADT_RECALC_ANIM);
+       
        /* speakers */
        EVAL_ANIM_IDS(main->speaker.first, ADT_RECALC_ANIM);
 
diff --git a/source/blender/blenkernel/intern/boids.c b/source/blender/blenkernel/intern/boids.c
new file mode 100644 (file)
index 0000000..b4bc83b
--- /dev/null
@@ -0,0 +1,1618 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2009 by Janne Karhu.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/blenkernel/intern/boids.c
+ *  \ingroup bke
+ */
+
+
+#include <string.h>
+#include <math.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_object_force.h"
+#include "DNA_scene_types.h"
+
+#include "BLI_rand.h"
+#include "BLI_math.h"
+#include "BLI_blenlib.h"
+#include "BLI_kdtree.h"
+#include "BLI_utildefines.h"
+
+#include "BKE_collision.h"
+#include "BKE_effect.h"
+#include "BKE_boids.h"
+#include "BKE_particle.h"
+
+#include "BKE_modifier.h"
+
+#include "RNA_enum_types.h"
+
+typedef struct BoidValues {
+       float max_speed, max_acc;
+       float max_ave, min_speed;
+       float personal_space, jump_speed;
+} BoidValues;
+
+static int apply_boid_rule(BoidBrainData *bbd, BoidRule *rule, BoidValues *val, ParticleData *pa, float fuzziness);
+
+static int rule_none(BoidRule *UNUSED(rule), BoidBrainData *UNUSED(data), BoidValues *UNUSED(val), ParticleData *UNUSED(pa))
+{
+       return 0;
+}
+
+static int rule_goal_avoid(BoidRule *rule, BoidBrainData *bbd, BoidValues *val, ParticleData *pa)
+{
+       BoidRuleGoalAvoid *gabr = (BoidRuleGoalAvoid*) rule;
+       BoidSettings *boids = bbd->part->boids;
+       BoidParticle *bpa = pa->boid;
+       EffectedPoint epoint;
+       ListBase *effectors = bbd->sim->psys->effectors;
+       EffectorCache *cur, *eff = NULL;
+       EffectorCache temp_eff;
+       EffectorData efd, cur_efd;
+       float mul = (rule->type == eBoidRuleType_Avoid ? 1.0 : -1.0);
+       float priority = 0.0f, len = 0.0f;
+       int ret = 0;
+
+       int p = 0;
+       efd.index = cur_efd.index = &p;
+
+       pd_point_from_particle(bbd->sim, pa, &pa->state, &epoint);
+
+       /* first find out goal/predator with highest priority */
+       if (effectors) for (cur = effectors->first; cur; cur=cur->next) {
+               Object *eob = cur->ob;
+               PartDeflect *pd = cur->pd;
+
+               if (gabr->ob && (rule->type != eBoidRuleType_Goal || gabr->ob != bpa->ground)) {
+                       if (gabr->ob == eob) {
+                               /* TODO: effectors with multiple points */
+                               if (get_effector_data(cur, &efd, &epoint, 0)) {
+                                       if (cur->pd && cur->pd->forcefield == PFIELD_BOID)
+                                               priority = mul * pd->f_strength * effector_falloff(cur, &efd, &epoint, bbd->part->effector_weights);
+                                       else
+                                               priority = 1.0;
+
+                                       eff = cur;
+                               }
+                               break;
+                       }
+               }
+               else if (rule->type == eBoidRuleType_Goal && eob == bpa->ground) {
+                       /* skip current object */
+               }
+               else if (pd->forcefield == PFIELD_BOID && mul * pd->f_strength > 0.0f && get_effector_data(cur, &cur_efd, &epoint, 0)) {
+                       float temp = mul * pd->f_strength * effector_falloff(cur, &cur_efd, &epoint, bbd->part->effector_weights);
+
+                       if (temp == 0.0f) {
+                               /* do nothing */
+                       }
+                       else if (temp > priority) {
+                               priority = temp;
+                               eff = cur;
+                               efd = cur_efd;
+                               len = efd.distance;
+                       }
+                       /* choose closest object with same priority */
+                       else if (temp == priority && efd.distance < len) {
+                               eff = cur;
+                               efd = cur_efd;
+                               len = efd.distance;
+                       }
+               }
+       }
+
+       /* if the object doesn't have effector data we have to fake it */
+       if (eff == NULL && gabr->ob) {
+               memset(&temp_eff, 0, sizeof(EffectorCache));
+               temp_eff.ob = gabr->ob;
+               temp_eff.scene = bbd->sim->scene;
+               eff = &temp_eff;
+               get_effector_data(eff, &efd, &epoint, 0);
+               priority = 1.0f;
+       }
+
+       /* then use that effector */
+       if (priority > (rule->type==eBoidRuleType_Avoid ? gabr->fear_factor : 0.0f)) { /* with avoid, factor is "fear factor" */
+               Object *eob = eff->ob;
+               PartDeflect *pd = eff->pd;
+               float surface = (pd && pd->shape == PFIELD_SHAPE_SURFACE) ? 1.0f : 0.0f;
+
+               if (gabr->options & BRULE_GOAL_AVOID_PREDICT) {
+                       /* estimate future location of target */
+                       get_effector_data(eff, &efd, &epoint, 1);
+
+                       mul_v3_fl(efd.vel, efd.distance / (val->max_speed * bbd->timestep));
+                       add_v3_v3(efd.loc, efd.vel);
+                       sub_v3_v3v3(efd.vec_to_point, pa->prev_state.co, efd.loc);
+                       efd.distance = len_v3(efd.vec_to_point);
+               }
+
+               if (rule->type == eBoidRuleType_Goal && boids->options & BOID_ALLOW_CLIMB && surface!=0.0f) {
+                       if (!bbd->goal_ob || bbd->goal_priority < priority) {
+                               bbd->goal_ob = eob;
+                               copy_v3_v3(bbd->goal_co, efd.loc);
+                               copy_v3_v3(bbd->goal_nor, efd.nor);
+                       }
+               }
+               else if (rule->type == eBoidRuleType_Avoid && bpa->data.mode == eBoidMode_Climbing &&
+                       priority > 2.0f * gabr->fear_factor) {
+                       /* detach from surface and try to fly away from danger */
+                       negate_v3_v3(efd.vec_to_point, bpa->gravity);
+               }
+
+               copy_v3_v3(bbd->wanted_co, efd.vec_to_point);
+               mul_v3_fl(bbd->wanted_co, mul);
+
+               bbd->wanted_speed = val->max_speed * priority;
+
+               /* with goals factor is approach velocity factor */
+               if (rule->type == eBoidRuleType_Goal && boids->landing_smoothness > 0.0f) {
+                       float len2 = 2.0f*len_v3(pa->prev_state.vel);
+
+                       surface *= pa->size * boids->height;
+
+                       if (len2 > 0.0f && efd.distance - surface < len2) {
+                               len2 = (efd.distance - surface)/len2;
+                               bbd->wanted_speed *= powf(len2, boids->landing_smoothness);
+                       }
+               }
+
+               ret = 1;
+       }
+
+       return ret;
+}
+
+static int rule_avoid_collision(BoidRule *rule, BoidBrainData *bbd, BoidValues *val, ParticleData *pa)
+{
+       const int raycast_flag = BVH_RAYCAST_DEFAULT & ~(BVH_RAYCAST_WATERTIGHT);
+       BoidRuleAvoidCollision *acbr = (BoidRuleAvoidCollision*) rule;
+       KDTreeNearest *ptn = NULL;
+       ParticleTarget *pt;
+       BoidParticle *bpa = pa->boid;
+       ColliderCache *coll;
+       float vec[3] = {0.0f, 0.0f, 0.0f}, loc[3] = {0.0f, 0.0f, 0.0f};
+       float co1[3], vel1[3], co2[3], vel2[3];
+       float  len, t, inp, t_min = 2.0f;
+       int n, neighbors = 0, nearest = 0;
+       int ret = 0;
+
+       //check deflector objects first
+       if (acbr->options & BRULE_ACOLL_WITH_DEFLECTORS && bbd->sim->colliders) {
+               ParticleCollision col;
+               BVHTreeRayHit hit;
+               float radius = val->personal_space * pa->size, ray_dir[3];
+
+               memset(&col, 0, sizeof(ParticleCollision));
+
+               copy_v3_v3(col.co1, pa->prev_state.co);
+               add_v3_v3v3(col.co2, pa->prev_state.co, pa->prev_state.vel);
+               sub_v3_v3v3(ray_dir, col.co2, col.co1);
+               mul_v3_fl(ray_dir, acbr->look_ahead);
+               col.f = 0.0f;
+               hit.index = -1;
+               hit.dist = col.original_ray_length = normalize_v3(ray_dir);
+
+               /* find out closest deflector object */
+               for (coll = bbd->sim->colliders->first; coll; coll=coll->next) {
+                       /* don't check with current ground object */
+                       if (coll->ob == bpa->ground)
+                               continue;
+
+                       col.current = coll->ob;
+                       col.md = coll->collmd;
+
+                       if (col.md && col.md->bvhtree) {
+                               BLI_bvhtree_ray_cast_ex(
+                                       col.md->bvhtree, col.co1, ray_dir, radius, &hit,
+                                       BKE_psys_collision_neartest_cb, &col, raycast_flag);
+                       }
+               }
+               /* then avoid that object */
+               if (hit.index>=0) {
+                       t = hit.dist/col.original_ray_length;
+
+                       /* avoid head-on collision */
+                       if (dot_v3v3(col.pce.nor, pa->prev_state.ave) < -0.99f) {
+                               /* don't know why, but uneven range [0.0, 1.0] */
+                               /* works much better than even [-1.0, 1.0] */
+                               bbd->wanted_co[0] = BLI_rng_get_float(bbd->rng);
+                               bbd->wanted_co[1] = BLI_rng_get_float(bbd->rng);
+                               bbd->wanted_co[2] = BLI_rng_get_float(bbd->rng);
+                       }
+                       else {
+                               copy_v3_v3(bbd->wanted_co, col.pce.nor);
+                       }
+
+                       mul_v3_fl(bbd->wanted_co, (1.0f - t) * val->personal_space * pa->size);
+
+                       bbd->wanted_speed = sqrtf(t) * len_v3(pa->prev_state.vel);
+                       bbd->wanted_speed = MAX2(bbd->wanted_speed, val->min_speed);
+
+                       return 1;
+               }
+       }
+
+       //check boids in own system
+       if (acbr->options & BRULE_ACOLL_WITH_BOIDS) {
+               neighbors = BLI_kdtree_range_search__normal(
+                       bbd->sim->psys->tree, pa->prev_state.co, pa->prev_state.ave,
+                       &ptn, acbr->look_ahead * len_v3(pa->prev_state.vel));
+               if (neighbors > 1) for (n=1; n<neighbors; n++) {
+                       copy_v3_v3(co1, pa->prev_state.co);
+                       copy_v3_v3(vel1, pa->prev_state.vel);
+                       copy_v3_v3(co2, (bbd->sim->psys->particles + ptn[n].index)->prev_state.co);
+                       copy_v3_v3(vel2, (bbd->sim->psys->particles + ptn[n].index)->prev_state.vel);
+
+                       sub_v3_v3v3(loc, co1, co2);
+
+                       sub_v3_v3v3(vec, vel1, vel2);
+                       
+                       inp = dot_v3v3(vec, vec);
+
+                       /* velocities not parallel */
+                       if (inp != 0.0f) {
+                               t = -dot_v3v3(loc, vec)/inp;
+                               /* cpa is not too far in the future so investigate further */
+                               if (t > 0.0f && t < t_min) {
+                                       madd_v3_v3fl(co1, vel1, t);
+                                       madd_v3_v3fl(co2, vel2, t);
+                                       
+                                       sub_v3_v3v3(vec, co2, co1);
+
+                                       len = normalize_v3(vec);
+
+                                       /* distance of cpa is close enough */
+                                       if (len < 2.0f * val->personal_space * pa->size) {
+                                               t_min = t;
+
+                                               mul_v3_fl(vec, len_v3(vel1));
+                                               mul_v3_fl(vec, (2.0f - t)/2.0f);
+                                               sub_v3_v3v3(bbd->wanted_co, vel1, vec);
+                                               bbd->wanted_speed = len_v3(bbd->wanted_co);
+                                               ret = 1;
+                                       }
+                               }
+                       }
+               }
+       }
+       if (ptn) { MEM_freeN(ptn); ptn=NULL; }
+
+       /* check boids in other systems */
+       for (pt=bbd->sim->psys->targets.first; pt; pt=pt->next) {
+               ParticleSystem *epsys = psys_get_target_system(bbd->sim->ob, pt);
+
+               if (epsys) {
+                       BLI_assert(epsys->tree != NULL);
+                       neighbors = BLI_kdtree_range_search__normal(
+                               epsys->tree, pa->prev_state.co, pa->prev_state.ave,
+                               &ptn, acbr->look_ahead * len_v3(pa->prev_state.vel));
+
+                       if (neighbors > 0) for (n=0; n<neighbors; n++) {
+                               copy_v3_v3(co1, pa->prev_state.co);
+                               copy_v3_v3(vel1, pa->prev_state.vel);
+                               copy_v3_v3(co2, (epsys->particles + ptn[n].index)->prev_state.co);
+                               copy_v3_v3(vel2, (epsys->particles + ptn[n].index)->prev_state.vel);
+
+                               sub_v3_v3v3(loc, co1, co2);
+
+                               sub_v3_v3v3(vec, vel1, vel2);
+                               
+                               inp = dot_v3v3(vec, vec);
+
+                               /* velocities not parallel */
+                               if (inp != 0.0f) {
+                                       t = -dot_v3v3(loc, vec)/inp;
+                                       /* cpa is not too far in the future so investigate further */
+                                       if (t > 0.0f && t < t_min) {
+                                               madd_v3_v3fl(co1, vel1, t);
+                                               madd_v3_v3fl(co2, vel2, t);
+                                               
+                                               sub_v3_v3v3(vec, co2, co1);
+
+                                               len = normalize_v3(vec);
+
+                                               /* distance of cpa is close enough */
+                                               if (len < 2.0f * val->personal_space * pa->size) {
+                                                       t_min = t;
+
+                                                       mul_v3_fl(vec, len_v3(vel1));
+                                                       mul_v3_fl(vec, (2.0f - t)/2.0f);
+                                                       sub_v3_v3v3(bbd->wanted_co, vel1, vec);
+                                                       bbd->wanted_speed = len_v3(bbd->wanted_co);
+                                                       ret = 1;
+                                               }
+                                       }
+                               }
+                       }
+
+                       if (ptn) { MEM_freeN(ptn); ptn=NULL; }
+               }
+       }
+
+
+       if (ptn && nearest==0)
+               MEM_freeN(ptn);
+
+       return ret;
+}
+static int rule_separate(BoidRule *UNUSED(rule), BoidBrainData *bbd, BoidValues *val, ParticleData *pa)
+{
+       KDTreeNearest *ptn = NULL;
+       ParticleTarget *pt;
+       float len = 2.0f * val->personal_space * pa->size + 1.0f;
+       float vec[3] = {0.0f, 0.0f, 0.0f};
+       int neighbors = BLI_kdtree_range_search(
+                   bbd->sim->psys->tree, pa->prev_state.co,
+                   &ptn, 2.0f * val->personal_space * pa->size);
+       int ret = 0;
+
+       if (neighbors > 1 && ptn[1].dist!=0.0f) {
+               sub_v3_v3v3(vec, pa->prev_state.co, bbd->sim->psys->particles[ptn[1].index].state.co);
+               mul_v3_fl(vec, (2.0f * val->personal_space * pa->size - ptn[1].dist) / ptn[1].dist);
+               add_v3_v3(bbd->wanted_co, vec);
+               bbd->wanted_speed = val->max_speed;
+               len = ptn[1].dist;
+               ret = 1;
+       }
+       if (ptn) { MEM_freeN(ptn); ptn=NULL; }
+
+       /* check other boid systems */
+       for (pt=bbd->sim->psys->targets.first; pt; pt=pt->next) {
+               ParticleSystem *epsys = psys_get_target_system(bbd->sim->ob, pt);
+
+               if (epsys) {
+                       neighbors = BLI_kdtree_range_search(
+                               epsys->tree, pa->prev_state.co,
+                               &ptn, 2.0f * val->personal_space * pa->size);
+                       
+                       if (neighbors > 0 && ptn[0].dist < len) {
+                               sub_v3_v3v3(vec, pa->prev_state.co, ptn[0].co);
+                               mul_v3_fl(vec, (2.0f * val->personal_space * pa->size - ptn[0].dist) / ptn[1].dist);
+                               add_v3_v3(bbd->wanted_co, vec);
+                               bbd->wanted_speed = val->max_speed;
+                               len = ptn[0].dist;
+                               ret = 1;
+                       }
+
+                       if (ptn) { MEM_freeN(ptn); ptn=NULL; }
+               }
+       }
+       return ret;
+}
+static int rule_flock(BoidRule *UNUSED(rule), BoidBrainData *bbd, BoidValues *UNUSED(val), ParticleData *pa)
+{
+       KDTreeNearest ptn[11];
+       float vec[3] = {0.0f, 0.0f, 0.0f}, loc[3] = {0.0f, 0.0f, 0.0f};
+       int neighbors = BLI_kdtree_find_nearest_n__normal(bbd->sim->psys->tree, pa->state.co, pa->prev_state.ave, ptn, 11);
+       int n;
+       int ret = 0;
+
+       if (neighbors > 1) {
+               for (n=1; n<neighbors; n++) {
+                       add_v3_v3(loc, bbd->sim->psys->particles[ptn[n].index].prev_state.co);
+                       add_v3_v3(vec, bbd->sim->psys->particles[ptn[n].index].prev_state.vel);
+               }
+
+               mul_v3_fl(loc, 1.0f/((float)neighbors - 1.0f));
+               mul_v3_fl(vec, 1.0f/((float)neighbors - 1.0f));
+
+               sub_v3_v3(loc, pa->prev_state.co);
+               sub_v3_v3(vec, pa->prev_state.vel);
+
+               add_v3_v3(bbd->wanted_co, vec);
+               add_v3_v3(bbd->wanted_co, loc);
+               bbd->wanted_speed = len_v3(bbd->wanted_co);
+
+               ret = 1;
+       }
+       return ret;
+}
+static int rule_follow_leader(BoidRule *rule, BoidBrainData *bbd, BoidValues *val, ParticleData *pa)
+{
+       BoidRuleFollowLeader *flbr = (BoidRuleFollowLeader*) rule;
+       float vec[3] = {0.0f, 0.0f, 0.0f}, loc[3] = {0.0f, 0.0f, 0.0f};
+       float mul, len;
+       int n = (flbr->queue_size <= 1) ? bbd->sim->psys->totpart : flbr->queue_size;
+       int i, ret = 0, p = pa - bbd->sim->psys->particles;
+
+       if (flbr->ob) {
+               float vec2[3], t;
+
+               /* first check we're not blocking the leader */
+               sub_v3_v3v3(vec, flbr->loc, flbr->oloc);
+               mul_v3_fl(vec, 1.0f/bbd->timestep);
+
+               sub_v3_v3v3(loc, pa->prev_state.co, flbr->oloc);
+
+               mul = dot_v3v3(vec, vec);
+
+               /* leader is not moving */
+               if (mul < 0.01f) {
+                       len = len_v3(loc);
+                       /* too close to leader */
+                       if (len < 2.0f * val->personal_space * pa->size) {
+                               copy_v3_v3(bbd->wanted_co, loc);
+                               bbd->wanted_speed = val->max_speed;
+                               return 1;
+                       }
+               }
+               else {
+                       t = dot_v3v3(loc, vec)/mul;
+
+                       /* possible blocking of leader in near future */
+                       if (t > 0.0f && t < 3.0f) {
+                               copy_v3_v3(vec2, vec);
+                               mul_v3_fl(vec2, t);
+
+                               sub_v3_v3v3(vec2, loc, vec2);
+
+                               len = len_v3(vec2);
+
+                               if (len < 2.0f * val->personal_space * pa->size) {
+                                       copy_v3_v3(bbd->wanted_co, vec2);
+                                       bbd->wanted_speed = val->max_speed * (3.0f - t)/3.0f;
+                                       return 1;
+                               }
+                       }
+               }
+
+               /* not blocking so try to follow leader */
+               if (p && flbr->options & BRULE_LEADER_IN_LINE) {
+                       copy_v3_v3(vec, bbd->sim->psys->particles[p-1].prev_state.vel);
+                       copy_v3_v3(loc, bbd->sim->psys->particles[p-1].prev_state.co);
+               }
+               else {
+                       copy_v3_v3(loc, flbr->oloc);
+                       sub_v3_v3v3(vec, flbr->loc, flbr->oloc);
+                       mul_v3_fl(vec, 1.0f/bbd->timestep);
+               }
+               
+               /* fac is seconds behind leader */
+               madd_v3_v3fl(loc, vec, -flbr->distance);
+
+               sub_v3_v3v3(bbd->wanted_co, loc, pa->prev_state.co);
+               bbd->wanted_speed = len_v3(bbd->wanted_co);
+                       
+               ret = 1;
+       }
+       else if (p % n) {
+               float vec2[3], t, t_min = 3.0f;
+
+               /* first check we're not blocking any leaders */
+               for (i = 0; i< bbd->sim->psys->totpart; i+=n) {
+                       copy_v3_v3(vec, bbd->sim->psys->particles[i].prev_state.vel);
+
+                       sub_v3_v3v3(loc, pa->prev_state.co, bbd->sim->psys->particles[i].prev_state.co);
+
+                       mul = dot_v3v3(vec, vec);
+
+                       /* leader is not moving */
+                       if (mul < 0.01f) {
+                               len = len_v3(loc);
+                               /* too close to leader */
+                               if (len < 2.0f * val->personal_space * pa->size) {
+                                       copy_v3_v3(bbd->wanted_co, loc);
+                                       bbd->wanted_speed = val->max_speed;
+                                       return 1;
+                               }
+                       }
+                       else {
+                               t = dot_v3v3(loc, vec)/mul;
+
+                               /* possible blocking of leader in near future */
+                               if (t > 0.0f && t < t_min) {
+                                       copy_v3_v3(vec2, vec);
+                                       mul_v3_fl(vec2, t);
+
+                                       sub_v3_v3v3(vec2, loc, vec2);
+
+                                       len = len_v3(vec2);
+
+                                       if (len < 2.0f * val->personal_space * pa->size) {
+                                               t_min = t;
+                                               copy_v3_v3(bbd->wanted_co, loc);
+                                               bbd->wanted_speed = val->max_speed * (3.0f - t)/3.0f;
+                                               ret = 1;
+                                       }
+                               }
+                       }
+               }
+
+               if (ret) return 1;
+
+               /* not blocking so try to follow leader */
+               if (flbr->options & BRULE_LEADER_IN_LINE) {
+                       copy_v3_v3(vec, bbd->sim->psys->particles[p-1].prev_state.vel);
+                       copy_v3_v3(loc, bbd->sim->psys->particles[p-1].prev_state.co);
+               }
+               else {
+                       copy_v3_v3(vec, bbd->sim->psys->particles[p - p%n].prev_state.vel);
+                       copy_v3_v3(loc, bbd->sim->psys->particles[p - p%n].prev_state.co);
+               }
+               
+               /* fac is seconds behind leader */
+               madd_v3_v3fl(loc, vec, -flbr->distance);
+
+               sub_v3_v3v3(bbd->wanted_co, loc, pa->prev_state.co);
+               bbd->wanted_speed = len_v3(bbd->wanted_co);
+               
+               ret = 1;
+       }
+
+       return ret;
+}
+static int rule_average_speed(BoidRule *rule, BoidBrainData *bbd, BoidValues *val, ParticleData *pa)
+{
+       BoidParticle *bpa = pa->boid;
+       BoidRuleAverageSpeed *asbr = (BoidRuleAverageSpeed*)rule;
+       float vec[3] = {0.0f, 0.0f, 0.0f};
+
+       if (asbr->wander > 0.0f) {
+               /* abuse pa->r_ave for wandering */
+               bpa->wander[0] += asbr->wander * (-1.0f + 2.0f * BLI_rng_get_float(bbd->rng));
+               bpa->wander[1] += asbr->wander * (-1.0f + 2.0f * BLI_rng_get_float(bbd->rng));
+               bpa->wander[2] += asbr->wander * (-1.0f + 2.0f * BLI_rng_get_float(bbd->rng));
+
+               normalize_v3(bpa->wander);
+
+               copy_v3_v3(vec, bpa->wander);
+
+               mul_qt_v3(pa->prev_state.rot, vec);
+
+               copy_v3_v3(bbd->wanted_co, pa->prev_state.ave);
+
+               mul_v3_fl(bbd->wanted_co, 1.1f);
+
+               add_v3_v3(bbd->wanted_co, vec);
+
+               /* leveling */
+               if (asbr->level > 0.0f && psys_uses_gravity(bbd->sim)) {
+                       project_v3_v3v3(vec, bbd->wanted_co, bbd->sim->scene->physics_settings.gravity);
+                       mul_v3_fl(vec, asbr->level);
+                       sub_v3_v3(bbd->wanted_co, vec);
+               }
+       }
+       else {
+               copy_v3_v3(bbd->wanted_co, pa->prev_state.ave);
+
+               /* may happen at birth */
+               if (dot_v2v2(bbd->wanted_co, bbd->wanted_co)==0.0f) {
+                       bbd->wanted_co[0] = 2.0f*(0.5f - BLI_rng_get_float(bbd->rng));
+                       bbd->wanted_co[1] = 2.0f*(0.5f - BLI_rng_get_float(bbd->rng));
+                       bbd->wanted_co[2] = 2.0f*(0.5f - BLI_rng_get_float(bbd->rng));
+               }
+               
+               /* leveling */
+               if (asbr->level > 0.0f && psys_uses_gravity(bbd->sim)) {
+                       project_v3_v3v3(vec, bbd->wanted_co, bbd->sim->scene->physics_settings.gravity);
+                       mul_v3_fl(vec, asbr->level);
+                       sub_v3_v3(bbd->wanted_co, vec);
+               }
+
+       }
+       bbd->wanted_speed = asbr->speed * val->max_speed;
+       
+       return 1;
+}
+static int rule_fight(BoidRule *rule, BoidBrainData *bbd, BoidValues *val, ParticleData *pa)
+{
+       BoidRuleFight *fbr = (BoidRuleFight*)rule;
+       KDTreeNearest *ptn = NULL;
+       ParticleTarget *pt;
+       ParticleData *epars;
+       ParticleData *enemy_pa = NULL;
+       BoidParticle *bpa;
+       /* friends & enemies */
+       float closest_enemy[3] = {0.0f, 0.0f, 0.0f};
+       float closest_dist = fbr->distance + 1.0f;
+       float f_strength = 0.0f, e_strength = 0.0f;
+       float health = 0.0f;
+       int n, ret = 0;
+
+       /* calculate own group strength */
+       int neighbors = BLI_kdtree_range_search(
+                   bbd->sim->psys->tree, pa->prev_state.co,
+                   &ptn, fbr->distance);
+       for (n=0; n<neighbors; n++) {
+               bpa = bbd->sim->psys->particles[ptn[n].index].boid;
+               health += bpa->data.health;
+       }
+
+       f_strength += bbd->part->boids->strength * health;
+
+       if (ptn) { MEM_freeN(ptn); ptn=NULL; }
+
+       /* add other friendlies and calculate enemy strength and find closest enemy */
+       for (pt=bbd->sim->psys->targets.first; pt; pt=pt->next) {
+               ParticleSystem *epsys = psys_get_target_system(bbd->sim->ob, pt);
+               if (epsys) {
+                       epars = epsys->particles;
+
+                       neighbors = BLI_kdtree_range_search(
+                               epsys->tree, pa->prev_state.co,
+                               &ptn, fbr->distance);
+                       
+                       health = 0.0f;
+
+                       for (n=0; n<neighbors; n++) {
+                               bpa = epars[ptn[n].index].boid;
+                               health += bpa->data.health;
+
+                               if (n==0 && pt->mode==PTARGET_MODE_ENEMY && ptn[n].dist < closest_dist) {
+                                       copy_v3_v3(closest_enemy, ptn[n].co);
+                                       closest_dist = ptn[n].dist;
+                                       enemy_pa = epars + ptn[n].index;
+                               }
+                       }
+                       if (pt->mode==PTARGET_MODE_ENEMY)
+                               e_strength += epsys->part->boids->strength * health;
+                       else if (pt->mode==PTARGET_MODE_FRIEND)
+                               f_strength += epsys->part->boids->strength * health;
+
+                       if (ptn) { MEM_freeN(ptn); ptn=NULL; }
+               }
+       }
+       /* decide action if enemy presence found */
+       if (e_strength > 0.0f) {
+               sub_v3_v3v3(bbd->wanted_co, closest_enemy, pa->prev_state.co);
+
+               /* attack if in range */
+               if (closest_dist <= bbd->part->boids->range + pa->size + enemy_pa->size) {
+                       float damage = BLI_rng_get_float(bbd->rng);
+                       float enemy_dir[3];
+
+                       normalize_v3_v3(enemy_dir, bbd->wanted_co);
+
+                       /* fight mode */
+                       bbd->wanted_speed = 0.0f;
+
+                       /* must face enemy to fight */
+                       if (dot_v3v3(pa->prev_state.ave, enemy_dir)>0.5f) {
+                               bpa = enemy_pa->boid;
+                               bpa->data.health -= bbd->part->boids->strength * bbd->timestep * ((1.0f-bbd->part->boids->accuracy)*damage + bbd->part->boids->accuracy);
+                       }
+               }
+               else {
+                       /* approach mode */
+                       bbd->wanted_speed = val->max_speed;
+               }
+
+               /* check if boid doesn't want to fight */
+               bpa = pa->boid;
+               if (bpa->data.health/bbd->part->boids->health * bbd->part->boids->aggression < e_strength / f_strength) {
+                       /* decide to flee */
+                       if (closest_dist < fbr->flee_distance * fbr->distance) {
+                               negate_v3(bbd->wanted_co);
+                               bbd->wanted_speed = val->max_speed;
+                       }
+                       else { /* wait for better odds */
+                               bbd->wanted_speed = 0.0f;
+                       }
+               }
+
+               ret = 1;
+       }
+
+       return ret;
+}
+
+typedef int (*boid_rule_cb)(BoidRule *rule, BoidBrainData *data, BoidValues *val, ParticleData *pa);
+
+static boid_rule_cb boid_rules[] = {
+       rule_none,
+       rule_goal_avoid,
+       rule_goal_avoid,
+       rule_avoid_collision,
+       rule_separate,
+       rule_flock,
+       rule_follow_leader,
+       rule_average_speed,
+       rule_fight,
+       //rule_help,
+       //rule_protect,
+       //rule_hide,
+       //rule_follow_path,
+       //rule_follow_wall
+};
+
+static void set_boid_values(BoidValues *val, BoidSettings *boids, ParticleData *pa)
+{
+       BoidParticle *bpa = pa->boid;
+
+       if (ELEM(bpa->data.mode, eBoidMode_OnLand, eBoidMode_Climbing)) {
+               val->max_speed = boids->land_max_speed * bpa->data.health/boids->health;
+               val->max_acc = boids->land_max_acc * val->max_speed;
+               val->max_ave = boids->land_max_ave * (float)M_PI * bpa->data.health/boids->health;
+               val->min_speed = 0.0f; /* no minimum speed on land */
+               val->personal_space = boids->land_personal_space;
+               val->jump_speed = boids->land_jump_speed * bpa->data.health/boids->health;
+       }
+       else {
+               val->max_speed = boids->air_max_speed * bpa->data.health/boids->health;
+               val->max_acc = boids->air_max_acc * val->max_speed;
+               val->max_ave = boids->air_max_ave * (float)M_PI * bpa->data.health/boids->health;
+               val->min_speed = boids->air_min_speed * boids->air_max_speed;
+               val->personal_space = boids->air_personal_space;
+               val->jump_speed = 0.0f; /* no jumping in air */
+       }
+}
+
+static Object *boid_find_ground(BoidBrainData *bbd, ParticleData *pa, float ground_co[3], float ground_nor[3])
+{
+       const int raycast_flag = BVH_RAYCAST_DEFAULT & ~(BVH_RAYCAST_WATERTIGHT);
+       BoidParticle *bpa = pa->boid;
+
+       if (bpa->data.mode == eBoidMode_Climbing) {
+               SurfaceModifierData *surmd = NULL;
+               float x[3], v[3];
+               
+               surmd = (SurfaceModifierData *)modifiers_findByType(bpa->ground, eModifierType_Surface );
+
+               /* take surface velocity into account */
+               closest_point_on_surface(surmd, pa->state.co, x, NULL, v);
+               add_v3_v3(x, v);
+
+               /* get actual position on surface */
+               closest_point_on_surface(surmd, x, ground_co, ground_nor, NULL);
+
+               return bpa->ground;
+       }
+       else {
+               float zvec[3] = {0.0f, 0.0f, 2000.0f};
+               ParticleCollision col;
+               ColliderCache *coll;
+               BVHTreeRayHit hit;
+               float radius = 0.0f, t, ray_dir[3];
+
+               if (!bbd->sim->colliders)
+                       return NULL;
+
+               memset(&col, 0, sizeof(ParticleCollision));
+
+               /* first try to find below boid */
+               copy_v3_v3(col.co1, pa->state.co);
+               sub_v3_v3v3(col.co2, pa->state.co, zvec);
+               sub_v3_v3v3(ray_dir, col.co2, col.co1);
+               col.f = 0.0f;
+               hit.index = -1;
+               hit.dist = col.original_ray_length = normalize_v3(ray_dir);
+               col.pce.inside = 0;
+
+               for (coll = bbd->sim->colliders->first; coll; coll = coll->next) {
+                       col.current = coll->ob;
+                       col.md = coll->collmd;
+                       col.fac1 = col.fac2 = 0.f;
+
+                       if (col.md && col.md->bvhtree) {
+                               BLI_bvhtree_ray_cast_ex(
+                                       col.md->bvhtree, col.co1, ray_dir, radius, &hit,
+                                       BKE_psys_collision_neartest_cb, &col, raycast_flag);
+                       }
+               }
+               /* then use that object */
+               if (hit.index>=0) {
+                       t = hit.dist/col.original_ray_length;
+                       interp_v3_v3v3(ground_co, col.co1, col.co2, t);
+                       normalize_v3_v3(ground_nor, col.pce.nor);
+                       return col.hit;
+               }
+
+               /* couldn't find below, so find upmost deflector object */
+               add_v3_v3v3(col.co1, pa->state.co, zvec);
+               sub_v3_v3v3(col.co2, pa->state.co, zvec);
+               sub_v3_v3(col.co2, zvec);
+               sub_v3_v3v3(ray_dir, col.co2, col.co1);
+               col.f = 0.0f;
+               hit.index = -1;
+               hit.dist = col.original_ray_length = normalize_v3(ray_dir);
+
+               for (coll = bbd->sim->colliders->first; coll; coll = coll->next) {
+                       col.current = coll->ob;
+                       col.md = coll->collmd;
+
+                       if (col.md && col.md->bvhtree) {
+                               BLI_bvhtree_ray_cast_ex(
+                                       col.md->bvhtree, col.co1, ray_dir, radius, &hit,
+                                       BKE_psys_collision_neartest_cb, &col, raycast_flag);
+                       }
+               }
+               /* then use that object */
+               if (hit.index>=0) {
+                       t = hit.dist/col.original_ray_length;
+                       interp_v3_v3v3(ground_co, col.co1, col.co2, t);
+                       normalize_v3_v3(ground_nor, col.pce.nor);
+                       return col.hit;
+               }
+
+               /* default to z=0 */
+               copy_v3_v3(ground_co, pa->state.co);
+               ground_co[2] = 0;
+               ground_nor[0] = ground_nor[1] = 0.0f;
+               ground_nor[2] = 1.0f;
+               return NULL;
+       }
+}
+static int boid_rule_applies(ParticleData *pa, BoidSettings *UNUSED(boids), BoidRule *rule)
+{
+       BoidParticle *bpa = pa->boid;
+
+       if (rule==NULL)
+               return 0;
+       
+       if (ELEM(bpa->data.mode, eBoidMode_OnLand, eBoidMode_Climbing) && rule->flag & BOIDRULE_ON_LAND)
+               return 1;
+       
+       if (bpa->data.mode==eBoidMode_InAir && rule->flag & BOIDRULE_IN_AIR)
+               return 1;
+
+       return 0;
+}
+void boids_precalc_rules(ParticleSettings *part, float cfra)
+{
+       BoidState *state = part->boids->states.first;
+       BoidRule *rule;
+       for (; state; state=state->next) {
+               for (rule = state->rules.first; rule; rule=rule->next) {
+                       if (rule->type==eBoidRuleType_FollowLeader) {
+                               BoidRuleFollowLeader *flbr = (BoidRuleFollowLeader*) rule;
+
+                               if (flbr->ob && flbr->cfra != cfra) {
+                                       /* save object locations for velocity calculations */
+                                       copy_v3_v3(flbr->oloc, flbr->loc);
+                                       copy_v3_v3(flbr->loc, flbr->ob->obmat[3]);
+                                       flbr->cfra = cfra;
+                               }
+                       }
+               }
+       }
+}
+static void boid_climb(BoidSettings *boids, ParticleData *pa, float *surface_co, float *surface_nor)
+{
+       BoidParticle *bpa = pa->boid;
+       float nor[3], vel[3];
+       copy_v3_v3(nor, surface_nor);
+
+       /* gather apparent gravity */
+       madd_v3_v3fl(bpa->gravity, surface_nor, -1.0f);
+       normalize_v3(bpa->gravity);
+
+       /* raise boid it's size from surface */
+       mul_v3_fl(nor, pa->size * boids->height);
+       add_v3_v3v3(pa->state.co, surface_co, nor);
+
+       /* remove normal component from velocity */
+       project_v3_v3v3(vel, pa->state.vel, surface_nor);
+       sub_v3_v3v3(pa->state.vel, pa->state.vel, vel);
+}
+static float boid_goal_signed_dist(float *boid_co, float *goal_co, float *goal_nor)
+{
+       float vec[3];
+
+       sub_v3_v3v3(vec, boid_co, goal_co);
+
+       return dot_v3v3(vec, goal_nor);
+}
+/* wanted_co is relative to boid location */
+static int apply_boid_rule(BoidBrainData *bbd, BoidRule *rule, BoidValues *val, ParticleData *pa, float fuzziness)
+{
+       if (rule==NULL)
+               return 0;
+
+       if (boid_rule_applies(pa, bbd->part->boids, rule)==0)
+               return 0;
+
+       if (boid_rules[rule->type](rule, bbd, val, pa)==0)
+               return 0;
+
+       if (fuzziness < 0.0f || compare_len_v3v3(bbd->wanted_co, pa->prev_state.vel, fuzziness * len_v3(pa->prev_state.vel))==0)
+               return 1;
+       else
+               return 0;
+}
+static BoidState *get_boid_state(BoidSettings *boids, ParticleData *pa)
+{
+       BoidState *state = boids->states.first;
+       BoidParticle *bpa = pa->boid;
+
+       for (; state; state=state->next) {
+               if (state->id==bpa->data.state_id)
+                       return state;
+       }
+
+       /* for some reason particle isn't at a valid state */
+       state = boids->states.first;
+       if (state)
+               bpa->data.state_id = state->id;
+
+       return state;
+}
+//static int boid_condition_is_true(BoidCondition *cond)
+//{
+//     /* TODO */
+//     return 0;
+//}
+
+/* determines the velocity the boid wants to have */
+void boid_brain(BoidBrainData *bbd, int p, ParticleData *pa)
+{
+       BoidRule *rule;
+       BoidSettings *boids = bbd->part->boids;
+       BoidValues val;
+       BoidState *state = get_boid_state(boids, pa);
+       BoidParticle *bpa = pa->boid;
+       ParticleSystem *psys = bbd->sim->psys;
+       int rand;
+       //BoidCondition *cond;
+
+       if (bpa->data.health <= 0.0f) {
+               pa->alive = PARS_DYING;
+               pa->dietime = bbd->cfra;
+               return;
+       }
+
+       //planned for near future
+       //cond = state->conditions.first;
+       //for (; cond; cond=cond->next) {
+       //      if (boid_condition_is_true(cond)) {
+       //              pa->boid->state_id = cond->state_id;
+       //              state = get_boid_state(boids, pa);
+       //              break; /* only first true condition is used */
+       //      }
+       //}
+
+       zero_v3(bbd->wanted_co);
+       bbd->wanted_speed = 0.0f;
+
+       /* create random seed for every particle & frame */
+       rand = (int)(psys_frand(psys, psys->seed + p) * 1000);
+       rand = (int)(psys_frand(psys, (int)bbd->cfra + rand) * 1000);
+
+       set_boid_values(&val, bbd->part->boids, pa);
+
+       /* go through rules */
+       switch (state->ruleset_type) {
+               case eBoidRulesetType_Fuzzy:
+               {
+                       for (rule = state->rules.first; rule; rule = rule->next) {
+                               if (apply_boid_rule(bbd, rule, &val, pa, state->rule_fuzziness))
+                                       break; /* only first nonzero rule that comes through fuzzy rule is applied */
+                       }
+                       break;
+               }
+               case eBoidRulesetType_Random:
+               {
+                       /* use random rule for each particle (always same for same particle though) */
+                       const int n = BLI_listbase_count(&state->rules);
+                       if (n) {
+                               rule = BLI_findlink(&state->rules, rand % n);
+                               apply_boid_rule(bbd, rule, &val, pa, -1.0);
+                       }
+                       break;
+               }
+               case eBoidRulesetType_Average:
+               {
+                       float wanted_co[3] = {0.0f, 0.0f, 0.0f}, wanted_speed = 0.0f;
+                       int n = 0;
+                       for (rule = state->rules.first; rule; rule=rule->next) {
+                               if (apply_boid_rule(bbd, rule, &val, pa, -1.0f)) {
+                                       add_v3_v3(wanted_co, bbd->wanted_co);
+                                       wanted_speed += bbd->wanted_speed;
+                                       n++;
+                                       zero_v3(bbd->wanted_co);
+                                       bbd->wanted_speed = 0.0f;
+                               }
+                       }
+
+                       if (n > 1) {
+                               mul_v3_fl(wanted_co, 1.0f/(float)n);
+                               wanted_speed /= (float)n;
+                       }
+
+                       copy_v3_v3(bbd->wanted_co, wanted_co);
+                       bbd->wanted_speed = wanted_speed;
+                       break;
+               }
+
+       }
+
+       /* decide on jumping & liftoff */
+       if (bpa->data.mode == eBoidMode_OnLand) {
+               /* fuzziness makes boids capable of misjudgement */
+               float mul = 1.0f + state->rule_fuzziness;
+               
+               if (boids->options & BOID_ALLOW_FLIGHT && bbd->wanted_co[2] > 0.0f) {
+                       float cvel[3], dir[3];
+
+                       copy_v3_v3(dir, pa->prev_state.ave);
+                       normalize_v2(dir);
+
+                       copy_v3_v3(cvel, bbd->wanted_co);
+                       normalize_v2(cvel);
+
+                       if (dot_v2v2(cvel, dir) > 0.95f / mul)
+                               bpa->data.mode = eBoidMode_Liftoff;
+               }
+               else if (val.jump_speed > 0.0f) {
+                       float jump_v[3];
+                       int jump = 0;
+
+                       /* jump to get to a location */
+                       if (bbd->wanted_co[2] > 0.0f) {
+                               float cvel[3], dir[3];
+                               float z_v, ground_v, cur_v;
+                               float len;
+
+                               copy_v3_v3(dir, pa->prev_state.ave);
+                               normalize_v2(dir);
+
+                               copy_v3_v3(cvel, bbd->wanted_co);
+                               normalize_v2(cvel);
+
+                               len = len_v2(pa->prev_state.vel);
+
+                               /* first of all, are we going in a suitable direction? */
+                               /* or at a suitably slow speed */
+                               if (dot_v2v2(cvel, dir) > 0.95f / mul || len <= state->rule_fuzziness) {
+                                       /* try to reach goal at highest point of the parabolic path */
+                                       cur_v = len_v2(pa->prev_state.vel);
+                                       z_v = sasqrt(-2.0f * bbd->sim->scene->physics_settings.gravity[2] * bbd->wanted_co[2]);
+                                       ground_v = len_v2(bbd->wanted_co)*sasqrt(-0.5f * bbd->sim->scene->physics_settings.gravity[2] / bbd->wanted_co[2]);
+
+                                       len = sasqrt((ground_v-cur_v)*(ground_v-cur_v) + z_v*z_v);
+
+                                       if (len < val.jump_speed * mul || bbd->part->boids->options & BOID_ALLOW_FLIGHT) {
+                                               jump = 1;
+
+                                               len = MIN2(len, val.jump_speed);
+
+                                               copy_v3_v3(jump_v, dir);
+                                               jump_v[2] = z_v;
+                                               mul_v3_fl(jump_v, ground_v);
+
+                                               normalize_v3(jump_v);
+                                               mul_v3_fl(jump_v, len);
+                                               add_v2_v2v2(jump_v, jump_v, pa->prev_state.vel);
+                                       }
+                               }
+                       }
+
+                       /* jump to go faster */
+                       if (jump == 0 && val.jump_speed > val.max_speed && bbd->wanted_speed > val.max_speed) {
+                               
+                       }
+
+                       if (jump) {
+                               copy_v3_v3(pa->prev_state.vel, jump_v);
+                               bpa->data.mode = eBoidMode_Falling;
+                       }
+               }
+       }
+}
+/* tries to realize the wanted velocity taking all constraints into account */
+void boid_body(BoidBrainData *bbd, ParticleData *pa)
+{
+       BoidSettings *boids = bbd->part->boids;
+       BoidParticle *bpa = pa->boid;
+       BoidValues val;
+       EffectedPoint epoint;
+       float acc[3] = {0.0f, 0.0f, 0.0f}, tan_acc[3], nor_acc[3];
+       float dvec[3], bvec[3];
+       float new_dir[3], new_speed;
+       float old_dir[3], old_speed;
+       float wanted_dir[3];
+       float q[4], mat[3][3]; /* rotation */
+       float ground_co[3] = {0.0f, 0.0f, 0.0f}, ground_nor[3] = {0.0f, 0.0f, 1.0f};
+       float force[3] = {0.0f, 0.0f, 0.0f};
+       float pa_mass=bbd->part->mass, dtime=bbd->dfra*bbd->timestep;
+
+       set_boid_values(&val, boids, pa);
+
+       /* make sure there's something in new velocity, location & rotation */
+       copy_particle_key(&pa->state, &pa->prev_state, 0);
+
+       if (bbd->part->flag & PART_SIZEMASS)
+               pa_mass*=pa->size;
+
+       /* if boids can't fly they fall to the ground */
+       if ((boids->options & BOID_ALLOW_FLIGHT)==0 && ELEM(bpa->data.mode, eBoidMode_OnLand, eBoidMode_Climbing)==0 && psys_uses_gravity(bbd->sim))
+               bpa->data.mode = eBoidMode_Falling;
+
+       if (bpa->data.mode == eBoidMode_Falling) {
+               /* Falling boids are only effected by gravity. */
+               acc[2] = bbd->sim->scene->physics_settings.gravity[2];
+       }
+       else {
+               /* figure out acceleration */
+               float landing_level = 2.0f;
+               float level = landing_level + 1.0f;
+               float new_vel[3];
+
+               if (bpa->data.mode == eBoidMode_Liftoff) {
+                       bpa->data.mode = eBoidMode_InAir;
+                       bpa->ground = boid_find_ground(bbd, pa, ground_co, ground_nor);
+               }
+               else if (bpa->data.mode == eBoidMode_InAir && boids->options & BOID_ALLOW_LAND) {
+                       /* auto-leveling & landing if close to ground */
+
+                       bpa->ground = boid_find_ground(bbd, pa, ground_co, ground_nor);
+                       
+                       /* level = how many particle sizes above ground */
+                       level = (pa->prev_state.co[2] - ground_co[2])/(2.0f * pa->size) - 0.5f;
+
+                       landing_level = - boids->landing_smoothness * pa->prev_state.vel[2] * pa_mass;
+
+                       if (pa->prev_state.vel[2] < 0.0f) {
+                               if (level < 1.0f) {
+                                       bbd->wanted_co[0] = bbd->wanted_co[1] = bbd->wanted_co[2] = 0.0f;