Particle Info node for Cycles. This can be used to access particle information in...
authorLukas Toenne <lukas.toenne@googlemail.com>
Fri, 8 Jun 2012 16:17:57 +0000 (16:17 +0000)
committerLukas Toenne <lukas.toenne@googlemail.com>
Fri, 8 Jun 2012 16:17:57 +0000 (16:17 +0000)
The particle data is stored in a separate texture if any of the dupli objects uses particle info nodes in shaders. To map dupli objects onto particles the store an additional particle_index value, which is different from the simple dupli object index (only visible particles, also works for particle dupli groups mode).

Some simple use cases on the code.blender.org blog:
http://code.blender.org/index.php/2012/05/particle-info-node/

25 files changed:
intern/cycles/blender/CMakeLists.txt
intern/cycles/blender/blender_object.cpp
intern/cycles/blender/blender_particles.cpp [new file with mode: 0644]
intern/cycles/blender/blender_shader.cpp
intern/cycles/blender/blender_sync.h
intern/cycles/kernel/kernel_object.h
intern/cycles/kernel/kernel_textures.h
intern/cycles/kernel/kernel_types.h
intern/cycles/kernel/svm/svm.h
intern/cycles/kernel/svm/svm_geometry.h
intern/cycles/kernel/svm/svm_types.h
intern/cycles/render/nodes.cpp
intern/cycles/render/nodes.h
intern/cycles/render/object.cpp
intern/cycles/render/object.h
intern/cycles/render/scene.h
source/blender/blenkernel/BKE_node.h
source/blender/blenkernel/intern/anim.c
source/blender/blenkernel/intern/node.c
source/blender/makesdna/DNA_object_types.h
source/blender/makesrna/intern/rna_nodetree_types.h
source/blender/makesrna/intern/rna_object.c
source/blender/nodes/CMakeLists.txt
source/blender/nodes/NOD_shader.h
source/blender/nodes/shader/nodes/node_shader_particle_info.c [new file with mode: 0644]

index 003c6c84f8f9192befff0f11abf3e25a42abb45a..a8c7eef89fa5f095213ada83731cdd3e47270a10 100644 (file)
@@ -22,6 +22,7 @@ set(SRC
        blender_camera.cpp
        blender_mesh.cpp
        blender_object.cpp
+       blender_particles.cpp
        blender_python.cpp
        blender_session.cpp
        blender_shader.cpp
index cea3b0256bdcfd1ea7069c1d7b918b92b5d2c7c8..b70491a7b8236fbf30b75a7f05a6202afdbc087d 100644 (file)
@@ -192,7 +192,7 @@ void BlenderSync::sync_background_light()
 
 /* Object */
 
-void BlenderSync::sync_object(BL::Object b_parent, int b_index, BL::Object b_ob, Transform& tfm, uint layer_flag, int motion)
+void BlenderSync::sync_object(BL::Object b_parent, int b_index, BL::Object b_ob, Transform& tfm, uint layer_flag, int motion, int particle_id)
 {
        /* light is handled separately */
        if(object_is_light(b_ob)) {
@@ -270,6 +270,12 @@ void BlenderSync::sync_object(BL::Object b_parent, int b_index, BL::Object b_ob,
                        object->visibility &= ~PATH_RAY_CAMERA;
                }
 
+               object->particle_id = particle_id;
+
+               /* particle sync */
+               if (object_use_particles(b_ob))
+                       sync_particles(object, b_ob);
+       
                object->tag_update(scene);
        }
 }
@@ -292,54 +298,51 @@ void BlenderSync::sync_objects(BL::SpaceView3D b_v3d, int motion)
        /* object loop */
        BL::Scene::objects_iterator b_ob;
        BL::Scene b_sce = b_scene;
+       int particle_offset = 0;
 
        for(; b_sce; b_sce = b_sce.background_set()) {
                for(b_sce.objects.begin(b_ob); b_ob != b_sce.objects.end(); ++b_ob) {
                        bool hide = (render_layer.use_viewport_visibility)? b_ob->hide(): b_ob->hide_render();
                        uint ob_layer = get_layer(b_ob->layers());
+                       hide = hide || !(ob_layer & scene_layer);
+
+                       if(!hide) {
+
+                               int num_particles = object_count_particles(*b_ob);
 
-                       if(!hide && (ob_layer & scene_layer)) {
                                if(b_ob->is_duplicator()) {
+                                       hide = true;    /* duplicators hidden by default */
+
                                        /* dupli objects */
                                        object_create_duplilist(*b_ob, b_scene);
 
                                        BL::Object::dupli_list_iterator b_dup;
-                                       int b_index = 0;
-
                                        for(b_ob->dupli_list.begin(b_dup); b_dup != b_ob->dupli_list.end(); ++b_dup) {
                                                Transform tfm = get_transform(b_dup->matrix());
                                                BL::Object b_dup_ob = b_dup->object();
                                                bool dup_hide = (b_v3d)? b_dup_ob.hide(): b_dup_ob.hide_render();
 
-                                               if(!(b_dup->hide() || dup_hide))
-                                                       sync_object(*b_ob, b_index, b_dup_ob, tfm, ob_layer, motion);
-
-                                               b_index++;
+                                               if(!(b_dup->hide() || dup_hide)) {
+                                                       sync_object(*b_ob, b_dup->index(), b_dup_ob, tfm, ob_layer, motion, b_dup->particle_index() + particle_offset);
+                                               }
                                        }
 
                                        object_free_duplilist(*b_ob);
-
-                                       hide = true;
                                }
 
                                /* check if we should render or hide particle emitter */
                                BL::Object::particle_systems_iterator b_psys;
-                               bool render_emitter = false;
-
-                               for(b_ob->particle_systems.begin(b_psys); b_psys != b_ob->particle_systems.end(); ++b_psys) {
-                                       if(b_psys->settings().use_render_emitter()) {
+                               for(b_ob->particle_systems.begin(b_psys); b_psys != b_ob->particle_systems.end(); ++b_psys)
+                                       if(b_psys->settings().use_render_emitter())
                                                hide = false;
-                                               render_emitter = true;
-                                       }
-                                       else if(!render_emitter)
-                                               hide = true;
-                               }
 
                                if(!hide) {
                                        /* object itself */
                                        Transform tfm = get_transform(b_ob->matrix_world());
-                                       sync_object(*b_ob, 0, *b_ob, tfm, ob_layer, motion);
+                                       sync_object(*b_ob, 0, *b_ob, tfm, ob_layer, motion, 0);
                                }
+
+                               particle_offset += num_particles;
                        }
                }
        }
diff --git a/intern/cycles/blender/blender_particles.cpp b/intern/cycles/blender/blender_particles.cpp
new file mode 100644 (file)
index 0000000..8a208cb
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2011, Blender Foundation.
+ *
+ * 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.
+ */
+
+#include "object.h"
+
+#include "mesh.h"
+#include "blender_sync.h"
+#include "blender_util.h"
+
+#include "util_foreach.h"
+
+CCL_NAMESPACE_BEGIN
+
+/* Utilities */
+
+
+/* Particles Sync */
+
+bool BlenderSync::object_use_particles(BL::Object b_ob)
+{
+       /* Particle data is only needed for
+        * a) Billboard render mode if object's own material uses particle info
+        * b) object/group render mode if any dupli object's material uses particle info
+        *
+        * Note: Meshes have to be synced at this point!
+        */
+       bool use_particles = false;
+       
+       BL::Object::particle_systems_iterator b_psys;
+       for (b_ob.particle_systems.begin(b_psys); b_psys != b_ob.particle_systems.end(); ++b_psys) {
+               switch (b_psys->settings().render_type()) {
+               /* XXX not implemented yet! 
+                * billboards/strands would become part of the mesh data (?),
+                * so the mesh attributes would store whether particle info is required.
+                */
+               #if 0
+               case BL::ParticleSettings::render_type_BILLBOARD:
+               case BL::ParticleSettings::render_type_PATH: {  /* for strand rendering */
+                       BL::ID key = (BKE_object_is_modified(b_ob))? b_ob: b_ob.data();
+                       Mesh *mesh = mesh_map.find(key);
+                       if (mesh) {
+                               use_particles |= mesh->need_attribute(scene, ATTR_STD_PARTICLE);
+                       }
+                       break;
+               }
+               #endif
+               
+               case BL::ParticleSettings::render_type_OBJECT: {
+                       BL::Object b_dupli_ob = b_psys->settings().dupli_object();
+                       if (b_dupli_ob) {
+                               BL::ID key = (BKE_object_is_modified(b_dupli_ob))? b_dupli_ob: b_dupli_ob.data();
+                               Mesh *mesh = mesh_map.find(key);
+                               if (mesh) {
+                                       use_particles |= mesh->need_attribute(scene, ATTR_STD_PARTICLE);
+                               }
+                       }
+                       break;
+               }
+               
+               case BL::ParticleSettings::render_type_GROUP: {
+                       BL::Group b_dupli_group = b_psys->settings().dupli_group();
+                       if (b_dupli_group) {
+                               BL::Group::objects_iterator b_gob;
+                               for (b_dupli_group.objects.begin(b_gob); b_gob != b_dupli_group.objects.end(); ++b_gob) {
+                                       BL::ID key = (BKE_object_is_modified(*b_gob))? *b_gob: b_gob->data();
+                                       Mesh *mesh = mesh_map.find(key);
+                                       if (mesh) {
+                                               use_particles |= mesh->need_attribute(scene, ATTR_STD_PARTICLE);
+                                       }
+                               }
+                       }
+                       break;
+               }
+               }
+       }
+       
+       return use_particles;
+}
+
+static bool use_particle_system(BL::ParticleSystem b_psys)
+{
+       /* only use duplicator particles? disabled particle info for
+        * halo and billboard to reduce particle count.
+        * Probably not necessary since particles don't contain a huge amount
+        * of data compared to other textures.
+        */
+       #if 0
+       int render_type = b_psys->settings().render_type();
+       return (render_type == BL::ParticleSettings::render_type_OBJECT
+               || render_type == BL::ParticleSettings::render_type_GROUP);
+       #endif
+       
+       return true;
+}
+
+static bool use_particle(BL::Particle b_pa)
+{
+       return b_pa.is_exist() && b_pa.is_visible() && b_pa.alive_state()==BL::Particle::alive_state_ALIVE;
+}
+
+int BlenderSync::object_count_particles(BL::Object b_ob)
+{
+       int tot = 0;
+       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 (use_particle_system(*b_psys)) {
+                       BL::ParticleSystem::particles_iterator b_pa;
+                       for(b_psys->particles.begin(b_pa); b_pa != b_psys->particles.end(); ++b_pa) {
+                               if(use_particle(*b_pa))
+                                       ++tot;
+                       }
+               }
+       }
+       return tot;
+}
+
+void BlenderSync::sync_particles(Object *ob, BL::Object b_ob)
+{
+       int tot = object_count_particles(b_ob);
+       
+       ob->particles.clear();
+       ob->particles.reserve(tot);
+       
+       int index;
+       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 (use_particle_system(*b_psys)) {
+                       BL::ParticleSystem::particles_iterator b_pa;
+                       for(b_psys->particles.begin(b_pa), index=0; b_pa != b_psys->particles.end(); ++b_pa, ++index) {
+                               if(use_particle(*b_pa)) {
+                                       Particle pa;
+                                       
+                                       pa.age = b_scene.frame_current() - b_pa->birth_time();
+                                       pa.lifetime = b_pa->lifetime();
+                                       
+                                       ob->particles.push_back(pa);
+                               }
+                       }
+               }
+       }
+}
+
+CCL_NAMESPACE_END
index 130b73a28085f5264ccb7ab3bce527caf434d578..a7be0a8fb5472737991033f3d7fb629154111e09 100644 (file)
@@ -337,6 +337,10 @@ static ShaderNode *add_node(BL::BlendData b_data, BL::Scene b_scene, ShaderGraph
                        node = new ObjectInfoNode();
                        break;
                }
+               case BL::ShaderNode::type_PARTICLE_INFO: {
+                       node = new ParticleInfoNode();
+                       break;
+               }
                case BL::ShaderNode::type_TEX_IMAGE: {
                        BL::ShaderNodeTexImage b_image_node(b_node);
                        BL::Image b_image(b_image_node.image());
index bc6258d35ac0ca4740410aa2bb647bd1b868511e..8c31c4b86baa62419e51137e10cfe316fb7963b6 100644 (file)
@@ -80,17 +80,20 @@ private:
 
        void sync_nodes(Shader *shader, BL::ShaderNodeTree b_ntree);
        Mesh *sync_mesh(BL::Object b_ob, bool object_updated);
-       void sync_object(BL::Object b_parent, int b_index, BL::Object b_object, Transform& tfm, uint layer_flag, int motion);
+       void sync_object(BL::Object b_parent, int b_index, BL::Object b_object, Transform& tfm, uint layer_flag, int motion, int particle_id);
        void sync_light(BL::Object b_parent, int b_index, BL::Object b_ob, Transform& tfm);
        void sync_background_light();
        void sync_mesh_motion(BL::Object b_ob, Mesh *mesh, int motion);
        void sync_camera_motion(BL::Object b_ob, int motion);
+       void sync_particles(Object *ob, BL::Object b_ob);
 
        /* util */
        void find_shader(BL::ID id, vector<uint>& used_shaders, int default_shader);
        bool BKE_object_is_modified(BL::Object b_ob);
        bool object_is_mesh(BL::Object b_ob);
        bool object_is_light(BL::Object b_ob);
+       bool object_use_particles(BL::Object b_ob);
+       int object_count_particles(BL::Object b_ob);
 
        /* variables */
        BL::BlendData b_data;
index 05c45c490d851dedc2e99592116b16247973c504..18e0b1e8a8771f3439b13c15da5dec88cf4e31bc 100644 (file)
@@ -154,10 +154,35 @@ __device_inline float object_random_number(KernelGlobals *kg, int object)
        return f.z;
 }
 
+__device_inline uint object_particle_id(KernelGlobals *kg, int object)
+{
+       if(object == ~0)
+               return 0.0f;
+
+       int offset = object*OBJECT_SIZE + OBJECT_PROPERTIES;
+       float4 f = kernel_tex_fetch(__objects, offset);
+       return __float_as_int(f.w);
+}
+
 __device int shader_pass_id(KernelGlobals *kg, ShaderData *sd)
 {
        return kernel_tex_fetch(__shader_flag, (sd->shader & SHADER_MASK)*2 + 1);
 }
 
+__device float particle_age(KernelGlobals *kg, int particle)
+{
+       int offset = particle*PARTICLE_SIZE;
+       float4 f = kernel_tex_fetch(__particles, offset);
+       return f.x;
+}
+
+__device float particle_lifetime(KernelGlobals *kg, int particle)
+{
+       int offset = particle*PARTICLE_SIZE;
+       float4 f = kernel_tex_fetch(__particles, offset);
+       return f.y;
+}
+
+
 CCL_NAMESPACE_END
 
index 482f886df3e863dc920fa191bf567bb31a3769a7..c1b8eed3dff52c54f610f4fb466e28286825bd21 100644 (file)
@@ -52,6 +52,9 @@ KERNEL_TEX(float4, texture_float4, __light_data)
 KERNEL_TEX(float2, texture_float2, __light_background_marginal_cdf)
 KERNEL_TEX(float2, texture_float2, __light_background_conditional_cdf)
 
+/* particles */
+KERNEL_TEX(float4, texture_float4, __particles)
+
 /* shaders */
 KERNEL_TEX(uint4, texture_uint4, __svm_nodes)
 KERNEL_TEX(uint, texture_uint, __shader_flag)
index 4fd57a5f2b371b581456ea3adadbc563eee6f3db..77a800b0e674731775ec9d1729f2a2137f48a716 100644 (file)
@@ -33,6 +33,7 @@ CCL_NAMESPACE_BEGIN
 #define LIGHT_SIZE                     4
 #define FILTER_TABLE_SIZE      256
 #define RAMP_TABLE_SIZE                256
+#define PARTICLE_SIZE          1
 #define TIME_INVALID           FLT_MAX
 
 /* device capabilities */
@@ -359,6 +360,7 @@ typedef enum AttributeStandard {
        ATTR_STD_POSITION_UNDISPLACED,
        ATTR_STD_MOTION_PRE,
        ATTR_STD_MOTION_POST,
+       ATTR_STD_PARTICLE,
        ATTR_STD_NUM,
 
        ATTR_STD_NOT_FOUND = ~0
index 6a05639beb936c5ff50ba3d0c6bb0437df28f2bc..8901e5e9628cbc7f948356c38a6bc691524e2984 100644 (file)
@@ -269,6 +269,9 @@ __device_noinline void svm_eval_nodes(KernelGlobals *kg, ShaderData *sd, ShaderT
                        case NODE_OBJECT_INFO:
                                svm_node_object_info(kg, sd, stack, node.y, node.z);
                                break;
+                       case NODE_PARTICLE_INFO:
+                               svm_node_particle_info(kg, sd, stack, node.y, node.z);
+                               break;
 #endif
                        case NODE_CONVERT:
                                svm_node_convert(sd, stack, node.y, node.z, node.w);
index dab19983946b6c3eec76e0465d9e2694e72b4fc8..88127b56474c7bd04b6dac60d48e4357fced46e8 100644 (file)
@@ -94,5 +94,27 @@ __device void svm_node_object_info(KernelGlobals *kg, ShaderData *sd, float *sta
        stack_store_float(stack, out_offset, data);
 }
 
+/* Particle Info */
+
+__device void svm_node_particle_info(KernelGlobals *kg, ShaderData *sd, float *stack, uint type, uint out_offset)
+{
+       float data;
+
+       switch(type) {
+               case NODE_INFO_PAR_AGE: {
+                       uint particle_id = object_particle_id(kg, sd->object);
+                       data = particle_age(kg, particle_id);
+                       stack_store_float(stack, out_offset, data);
+                       break;
+               }
+               case NODE_INFO_PAR_LIFETIME: {
+                       uint particle_id = object_particle_id(kg, sd->object);
+                       data = particle_lifetime(kg, particle_id);
+                       stack_store_float(stack, out_offset, data);
+                       break;
+               }
+       }
+}
+
 CCL_NAMESPACE_END
 
index 97849736ef687c5fa18db58b531fbfc387ef8843..2d756d57f4145c82fe486cb072959d4b8dcb7425 100644 (file)
@@ -93,7 +93,8 @@ typedef enum NodeType {
        NODE_RGB_CURVES = 6000,
        NODE_MIN_MAX = 6100,
        NODE_LIGHT_FALLOFF = 6200,
-       NODE_OBJECT_INFO = 6300
+       NODE_OBJECT_INFO = 6300,
+       NODE_PARTICLE_INFO = 6400
 } NodeType;
 
 typedef enum NodeAttributeType {
@@ -117,6 +118,11 @@ typedef enum NodeObjectInfo {
        NODE_INFO_OB_RANDOM
 } NodeObjectInfo;
 
+typedef enum NodeParticleInfo {
+       NODE_INFO_PAR_AGE,
+       NODE_INFO_PAR_LIFETIME
+} NodeParticleInfo;
+
 typedef enum NodeLightPath {
        NODE_LP_camera = 0,
        NODE_LP_shadow,
index 16c6b07261af4222647ce82bf9125e1e7addf30b..1c9eeacddbe27048e8aa2be482d71c305a3ffab7 100644 (file)
@@ -1789,6 +1789,47 @@ void ObjectInfoNode::compile(OSLCompiler& compiler)
        compiler.add(this, "node_object_info");
 }
 
+/* Particle Info */
+
+ParticleInfoNode::ParticleInfoNode()
+: ShaderNode("particle_info")
+{
+       add_output("Age", SHADER_SOCKET_FLOAT);
+       add_output("Lifetime", SHADER_SOCKET_FLOAT);
+}
+
+void ParticleInfoNode::attributes(AttributeRequestSet *attributes)
+{
+       if(!output("Age")->links.empty())
+               attributes->add(ATTR_STD_PARTICLE);
+       if(!output("Lifetime")->links.empty())
+               attributes->add(ATTR_STD_PARTICLE);
+
+       ShaderNode::attributes(attributes);
+}
+
+void ParticleInfoNode::compile(SVMCompiler& compiler)
+{
+       ShaderOutput *out;
+       
+       out = output("Age");
+       if(!out->links.empty()) {
+               compiler.stack_assign(out);
+               compiler.add_node(NODE_PARTICLE_INFO, NODE_INFO_PAR_AGE, out->stack_offset);
+       }
+       
+       out = output("Lifetime");
+       if(!out->links.empty()) {
+               compiler.stack_assign(out);
+               compiler.add_node(NODE_PARTICLE_INFO, NODE_INFO_PAR_LIFETIME, out->stack_offset);
+       }
+}
+
+void ParticleInfoNode::compile(OSLCompiler& compiler)
+{
+       compiler.add(this, "node_particle_info");
+}
+
 /* Value */
 
 ValueNode::ValueNode()
index 2d0d58d1e949805dcadee2578f05209d2f388183..650758a611371a67d3121544d6e94d4db83479ce 100644 (file)
@@ -290,6 +290,12 @@ public:
        SHADER_NODE_CLASS(ObjectInfoNode)
 };
 
+class ParticleInfoNode : public ShaderNode {
+public:
+       SHADER_NODE_CLASS(ParticleInfoNode)
+       void attributes(AttributeRequestSet *attributes);
+};
+
 class ValueNode : public ShaderNode {
 public:
        SHADER_NODE_CLASS(ValueNode)
index 5c7e48a38eb04ad4fd595053d4d6930a6f512420..259a0d25bccdb505f8cc454a4c76c4c534bd8a46 100644 (file)
@@ -25,6 +25,7 @@
 #include "util_foreach.h"
 #include "util_map.h"
 #include "util_progress.h"
+#include "util_vector.h"
 
 CCL_NAMESPACE_BEGIN
 
@@ -38,6 +39,7 @@ Object::Object()
        visibility = ~0;
        random_id = 0;
        pass_id = 0;
+       particle_id = 0;
        bounds = BoundBox::empty;
        motion.pre = transform_identity();
        motion.post = transform_identity();
@@ -200,7 +202,7 @@ void ObjectManager::device_update_transforms(Device *device, DeviceScene *dscene
 
                memcpy(&objects[offset], &tfm, sizeof(float4)*3);
                memcpy(&objects[offset+3], &itfm, sizeof(float4)*3);
-               objects[offset+6] = make_float4(surface_area, pass_id, random_number, 0.0f);
+               objects[offset+6] = make_float4(surface_area, pass_id, random_number, __int_as_float(ob->particle_id));
 
                if(need_motion == Scene::MOTION_PASS) {
                        /* motion transformations, is world/object space depending if mesh
@@ -246,6 +248,38 @@ void ObjectManager::device_update_transforms(Device *device, DeviceScene *dscene
        device->tex_alloc("__object_flag", dscene->object_flag);
 }
 
+void ObjectManager::device_update_particles(Device *device, DeviceScene *dscene, Scene *scene, Progress& progress)
+{
+       /* count particles.
+        * adds one dummy particle at the beginning to avoid invalid lookups,
+        * in case a shader uses particle info without actual particle data.
+        */
+       int num_particles = 1;
+       foreach(Object *ob, scene->objects)
+               num_particles += ob->particles.size();
+       
+       float4 *particles = dscene->particles.resize(PARTICLE_SIZE*num_particles);
+       
+       /* dummy particle */
+       particles[0] = make_float4(0.0f, 0.0f, 0.0f, 0.0f);
+       
+       int i = 1;
+       foreach(Object *ob, scene->objects) {
+               foreach(Particle &pa, ob->particles) {
+                       /* pack in texture */
+                       int offset = i*PARTICLE_SIZE;
+                       
+                       particles[offset] = make_float4(pa.age, pa.lifetime, 0.0f, 0.0f);
+                       
+                       i++;
+                       
+                       if(progress.get_cancel()) return;
+               }
+       }
+       
+       device->tex_alloc("__particles", dscene->particles);
+}
+
 void ObjectManager::device_update(Device *device, DeviceScene *dscene, Scene *scene, Progress& progress)
 {
        if(!need_update)
@@ -271,6 +305,11 @@ void ObjectManager::device_update(Device *device, DeviceScene *dscene, Scene *sc
 
        if(progress.get_cancel()) return;
 
+       progress.set_status("Updating Objects", "Copying Particles to device");
+       device_update_particles(device, dscene, scene, progress);
+       
+       if(progress.get_cancel()) return;
+       
        need_update = false;
 }
 
@@ -281,6 +320,9 @@ void ObjectManager::device_free(Device *device, DeviceScene *dscene)
 
        device->tex_free(dscene->object_flag);
        dscene->object_flag.clear();
+       
+       device->tex_free(dscene->particles);
+       dscene->particles.clear();
 }
 
 void ObjectManager::apply_static_transforms(Scene *scene, Progress& progress)
index a3bd748a8a4eeb03e644a21555d95327a39a1b9f..6d674731b07e7d38f914b573455f3798d2fdbdd4 100644 (file)
@@ -35,6 +35,11 @@ struct Transform;
 
 /* Object */
 
+struct Particle {
+       float age;
+       float lifetime;
+};
+
 class Object {
 public:
        Mesh *mesh;
@@ -49,6 +54,9 @@ public:
        bool use_motion;
        bool use_holdout;
 
+       int particle_id;
+       vector<Particle> particles;
+
        Object();
        ~Object();
 
@@ -69,6 +77,7 @@ public:
 
        void device_update(Device *device, DeviceScene *dscene, Scene *scene, Progress& progress);
        void device_update_transforms(Device *device, DeviceScene *dscene, Scene *scene, Progress& progress);
+       void device_update_particles(Device *device, DeviceScene *dscene, Scene *scene, Progress& progress);
        void device_free(Device *device, DeviceScene *dscene);
 
        void tag_update(Scene *scene);
index 90bc47d5c8ec1677c6ec4f5f794e5b88ad628828..8b9944cb76e27b2c4fd7d87d103b1e503321dba7 100644 (file)
@@ -82,6 +82,9 @@ public:
        device_vector<float2> light_background_marginal_cdf;
        device_vector<float2> light_background_conditional_cdf;
 
+       /* particles */
+       device_vector<float4> particles;
+
        /* shaders */
        device_vector<uint4> svm_nodes;
        device_vector<uint> shader_flag;
index 5fbd58e26a54c06b6397d9d4836512b9d2c6685a..b1e5fabc456e9edf632a71e5e7e53cc6bac9db94 100644 (file)
@@ -527,6 +527,7 @@ struct ShadeResult;
 #define SH_NODE_BRIGHTCONTRAST                 165
 #define SH_NODE_LIGHT_FALLOFF                  166
 #define SH_NODE_OBJECT_INFO                            167
+#define SH_NODE_PARTICLE_INFO           168
 
 /* custom defines options for Material node */
 #define SH_NODE_MAT_DIFF   1
index f73221066b14d3579fdc086fe6e019de44c4cb82..701ce6c829908a3d53038e9820164a502ad93567 100644 (file)
@@ -75,7 +75,7 @@
 /* --------------------- */
 /* forward declarations */
 
-static void object_duplilist_recursive(ID *id, Scene *scene, Object *ob, ListBase *duplilist, float par_space_mat[][4], int level, int animated);
+static void object_duplilist_recursive(ID *id, Scene *scene, Object *ob, ListBase *duplilist, float par_space_mat[][4], int par_index, int level, int animated);
 
 /* ******************************************************************** */
 /* Animation Visualisation */
@@ -699,7 +699,7 @@ int where_on_path(Object *ob, float ctime, float vec[4], float dir[3], float qua
 /* ******************************************************************** */
 /* Dupli-Geometry */
 
-static DupliObject *new_dupli_object(ListBase *lb, Object *ob, float mat[][4], int lay, int index, int type, int animated)
+static DupliObject *new_dupli_object(ListBase *lb, Object *ob, float mat[][4], int lay, int index, int par_index, int type, int animated)
 {
        DupliObject *dob = MEM_callocN(sizeof(DupliObject), "dupliobject");
        
@@ -709,6 +709,7 @@ static DupliObject *new_dupli_object(ListBase *lb, Object *ob, float mat[][4], i
        copy_m4_m4(dob->omat, ob->obmat);
        dob->origlay = ob->lay;
        dob->index = index;
+       dob->particle_index = par_index;
        dob->type = type;
        dob->animated = (type == OB_DUPLIGROUP) && animated;
        ob->lay = lay;
@@ -716,7 +717,7 @@ static DupliObject *new_dupli_object(ListBase *lb, Object *ob, float mat[][4], i
        return dob;
 }
 
-static void group_duplilist(ListBase *lb, Scene *scene, Object *ob, int level, int animated)
+static void group_duplilist(ListBase *lb, Scene *scene, Object *ob, int par_index, int level, int animated)
 {
        DupliObject *dob;
        Group *group;
@@ -748,7 +749,7 @@ static void group_duplilist(ListBase *lb, Scene *scene, Object *ob, int level, i
                                mult_m4_m4m4(mat, ob->obmat, go->ob->obmat);
                        }
                        
-                       dob = new_dupli_object(lb, go->ob, mat, ob->lay, 0, OB_DUPLIGROUP, animated);
+                       dob = new_dupli_object(lb, go->ob, mat, ob->lay, 0, par_index, OB_DUPLIGROUP, animated);
 
                        /* check the group instance and object layers match, also that the object visible flags are ok. */
                        if ((dob->origlay & group->layer) == 0 ||
@@ -763,14 +764,14 @@ static void group_duplilist(ListBase *lb, Scene *scene, Object *ob, int level, i
 
                        if (go->ob->transflag & OB_DUPLI) {
                                copy_m4_m4(dob->ob->obmat, dob->mat);
-                               object_duplilist_recursive(&group->id, scene, go->ob, lb, ob->obmat, level + 1, animated);
+                               object_duplilist_recursive(&group->id, scene, go->ob, lb, ob->obmat, par_index, level + 1, animated);
                                copy_m4_m4(dob->ob->obmat, dob->omat);
                        }
                }
        }
 }
 
-static void frames_duplilist(ListBase *lb, Scene *scene, Object *ob, int level, int animated)
+static void frames_duplilist(ListBase *lb, Scene *scene, Object *ob, int par_index, int level, int animated)
 {
        extern int enable_cu_speed; /* object.c */
        Object copyob;
@@ -818,7 +819,7 @@ static void frames_duplilist(ListBase *lb, Scene *scene, Object *ob, int level,
                        BKE_animsys_evaluate_animdata(scene, &ob->id, ob->adt, (float)scene->r.cfra, ADT_RECALC_ANIM); /* ob-eval will do drivers, so we don't need to do them */
                        BKE_object_where_is_calc_time(scene, ob, (float)scene->r.cfra);
                        
-                       dob = new_dupli_object(lb, ob, ob->obmat, ob->lay, scene->r.cfra, OB_DUPLIFRAMES, animated);
+                       dob = new_dupli_object(lb, ob, ob->obmat, ob->lay, scene->r.cfra, par_index, OB_DUPLIFRAMES, animated);
                        copy_m4_m4(dob->omat, copyob.obmat);
                }
        }
@@ -849,6 +850,7 @@ typedef struct vertexDupliData {
        Scene *scene;
        Object *ob, *par;
        float (*orco)[3];
+       int par_index;
 } vertexDupliData;
 
 /* ------------- */
@@ -885,7 +887,7 @@ static void vertex_dupli__mapFunc(void *userData, int index, const float co[3],
 
        origlay = vdd->ob->lay;
        
-       dob = new_dupli_object(vdd->lb, vdd->ob, obmat, vdd->par->lay, index, OB_DUPLIVERTS, vdd->animated);
+       dob = new_dupli_object(vdd->lb, vdd->ob, obmat, vdd->par->lay, index, vdd->par_index, OB_DUPLIVERTS, vdd->animated);
 
        /* restore the original layer so that each dupli will have proper dob->origlay */
        vdd->ob->lay = origlay;
@@ -897,12 +899,12 @@ static void vertex_dupli__mapFunc(void *userData, int index, const float co[3],
                float tmpmat[4][4];
                copy_m4_m4(tmpmat, vdd->ob->obmat);
                copy_m4_m4(vdd->ob->obmat, obmat); /* pretend we are really this mat */
-               object_duplilist_recursive((ID *)vdd->id, vdd->scene, vdd->ob, vdd->lb, obmat, vdd->level + 1, vdd->animated);
+               object_duplilist_recursive((ID *)vdd->id, vdd->scene, vdd->ob, vdd->lb, obmat, vdd->par_index, vdd->level + 1, vdd->animated);
                copy_m4_m4(vdd->ob->obmat, tmpmat);
        }
 }
 
-static void vertex_duplilist(ListBase *lb, ID *id, Scene *scene, Object *par, float par_space_mat[][4], int level, int animated)
+static void vertex_duplilist(ListBase *lb, ID *id, Scene *scene, Object *par, float par_space_mat[][4], int par_index, int level, int animated)
 {
        Object *ob, *ob_iter;
        Mesh *me = par->data;
@@ -986,6 +988,7 @@ static void vertex_duplilist(ListBase *lb, ID *id, Scene *scene, Object *par, fl
                                        vdd.scene = scene;
                                        vdd.par = par;
                                        copy_m4_m4(vdd.pmat, pmat);
+                                       vdd.par_index = par_index;
                                        
                                        /* mballs have a different dupli handling */
                                        if (ob->type != OB_MBALL) ob->flag |= OB_DONE;  /* doesnt render */
@@ -1024,7 +1027,7 @@ static void vertex_duplilist(ListBase *lb, ID *id, Scene *scene, Object *par, fl
        dm->release(dm);
 }
 
-static void face_duplilist(ListBase *lb, ID *id, Scene *scene, Object *par, float par_space_mat[][4], int level, int animated)
+static void face_duplilist(ListBase *lb, ID *id, Scene *scene, Object *par, float par_space_mat[][4], int par_index, int level, int animated)
 {
        Object *ob, *ob_iter;
        Base *base = NULL;
@@ -1171,7 +1174,7 @@ static void face_duplilist(ListBase *lb, ID *id, Scene *scene, Object *par, floa
                                                copy_m4_m4(tmat, obmat);
                                                mul_m4_m4m3(obmat, tmat, mat);
                                                
-                                               dob = new_dupli_object(lb, ob, obmat, par->lay, a, OB_DUPLIFACES, animated);
+                                               dob = new_dupli_object(lb, ob, obmat, par->lay, a, par_index, OB_DUPLIFACES, animated);
                                                if (G.rendering) {
                                                        w = 1.0f / (float)mp->totloop;
 
@@ -1194,7 +1197,7 @@ static void face_duplilist(ListBase *lb, ID *id, Scene *scene, Object *par, floa
                                                        float tmpmat[4][4];
                                                        copy_m4_m4(tmpmat, ob->obmat);
                                                        copy_m4_m4(ob->obmat, obmat); /* pretend we are really this mat */
-                                                       object_duplilist_recursive((ID *)id, scene, ob, lb, ob->obmat, level + 1, animated);
+                                                       object_duplilist_recursive((ID *)id, scene, ob, lb, ob->obmat, par_index, level + 1, animated);
                                                        copy_m4_m4(ob->obmat, tmpmat);
                                                }
                                        }
@@ -1214,7 +1217,7 @@ static void face_duplilist(ListBase *lb, ID *id, Scene *scene, Object *par, floa
        dm->release(dm);
 }
 
-static void new_particle_duplilist(ListBase *lb, ID *id, Scene *scene, Object *par, float par_space_mat[][4], ParticleSystem *psys, int level, int animated)
+static void new_particle_duplilist(ListBase *lb, ID *id, Scene *scene, Object *par, float par_space_mat[][4], int UNUSED(par_index), ParticleSystem *psys, int level, int animated)
 {
        GroupObject *go;
        Object *ob = NULL, **oblist = NULL, obcopy, *obcopylist = NULL;
@@ -1228,7 +1231,7 @@ static void new_particle_duplilist(ListBase *lb, ID *id, Scene *scene, Object *p
        float ctime, pa_time, scale = 1.0f;
        float tmat[4][4], mat[4][4], pamat[4][4], vec[3], size = 0.0;
        float (*obmat)[4], (*oldobmat)[4];
-       int a, b, counter, hair = 0;
+       int a, b, counter, index, hair = 0;
        int totpart, totchild, totgroup = 0 /*, pa_num */;
 
        int no_draw_flag = PARS_UNEXIST;
@@ -1342,6 +1345,7 @@ static void new_particle_duplilist(ListBase *lb, ID *id, Scene *scene, Object *p
                else
                        a = totpart;
 
+               index = 0;
                for (pa = psys->particles, counter = 0; a < totpart + totchild; a++, pa++, counter++) {
                        if (a < totpart) {
                                /* handle parent particle */
@@ -1437,7 +1441,7 @@ static void new_particle_duplilist(ListBase *lb, ID *id, Scene *scene, Object *p
                                        else
                                                copy_m4_m4(mat, tmat);
 
-                                       dob = new_dupli_object(lb, go->ob, mat, par->lay, counter, OB_DUPLIPARTS, animated);
+                                       dob = new_dupli_object(lb, go->ob, mat, par->lay, counter, index, OB_DUPLIPARTS, animated);
                                        copy_m4_m4(dob->omat, obcopylist[b].obmat);
                                        if (G.rendering)
                                                psys_get_dupli_texture(psys, part, sim.psmd, pa, cpa, dob->uv, dob->orco);
@@ -1479,11 +1483,14 @@ static void new_particle_duplilist(ListBase *lb, ID *id, Scene *scene, Object *p
                                if (part->draw & PART_DRAW_GLOBAL_OB)
                                        add_v3_v3v3(mat[3], mat[3], vec);
 
-                               dob = new_dupli_object(lb, ob, mat, ob->lay, counter, GS(id->name) == ID_GR ? OB_DUPLIGROUP : OB_DUPLIPARTS, animated);
+                               dob = new_dupli_object(lb, ob, mat, ob->lay, counter, index, GS(id->name) == ID_GR ? OB_DUPLIGROUP : OB_DUPLIPARTS, animated);
                                copy_m4_m4(dob->omat, oldobmat);
                                if (G.rendering)
                                        psys_get_dupli_texture(psys, part, sim.psmd, pa, cpa, dob->uv, dob->orco);
                        }
+
+                       /* only counts visible particles */
+                       ++index;
                }
 
                /* restore objects since they were changed in BKE_object_where_is_calc_time */
@@ -1530,7 +1537,7 @@ static Object *find_family_object(Object **obar, char *family, char ch)
 }
 
 
-static void font_duplilist(ListBase *lb, Scene *scene, Object *par, int level, int animated)
+static void font_duplilist(ListBase *lb, Scene *scene, Object *par, int par_index, int level, int animated)
 {
        Object *ob, *obar[256] = {NULL};
        Curve *cu;
@@ -1569,7 +1576,7 @@ static void font_duplilist(ListBase *lb, Scene *scene, Object *par, int level, i
                        copy_m4_m4(obmat, par->obmat);
                        copy_v3_v3(obmat[3], vec);
                        
-                       new_dupli_object(lb, ob, obmat, par->lay, a, OB_DUPLIVERTS, animated);
+                       new_dupli_object(lb, ob, obmat, par->lay, a, par_index, OB_DUPLIVERTS, animated);
                }
        }
        
@@ -1578,7 +1585,7 @@ static void font_duplilist(ListBase *lb, Scene *scene, Object *par, int level, i
 
 /* ------------- */
 
-static void object_duplilist_recursive(ID *id, Scene *scene, Object *ob, ListBase *duplilist, float par_space_mat[][4], int level, int animated)
+static void object_duplilist_recursive(ID *id, Scene *scene, Object *ob, ListBase *duplilist, float par_space_mat[][4], int par_index, int level, int animated)
 {      
        if ((ob->transflag & OB_DUPLI) == 0)
                return;
@@ -1598,31 +1605,31 @@ static void object_duplilist_recursive(ID *id, Scene *scene, Object *ob, ListBas
        if (ob->transflag & OB_DUPLIPARTS) {
                ParticleSystem *psys = ob->particlesystem.first;
                for (; psys; psys = psys->next)
-                       new_particle_duplilist(duplilist, id, scene, ob, par_space_mat, psys, level + 1, animated);
+                       new_particle_duplilist(duplilist, id, scene, ob, par_space_mat, par_index, psys, level + 1, animated);
        }
        else if (ob->transflag & OB_DUPLIVERTS) {
                if (ob->type == OB_MESH) {
-                       vertex_duplilist(duplilist, id, scene, ob, par_space_mat, level + 1, animated);
+                       vertex_duplilist(duplilist, id, scene, ob, par_space_mat, par_index, level + 1, animated);
                }
                else if (ob->type == OB_FONT) {
                        if (GS(id->name) == ID_SCE) { /* TODO - support dupligroups */
-                               font_duplilist(duplilist, scene, ob, level + 1, animated);
+                               font_duplilist(duplilist, scene, ob, par_index, level + 1, animated);
                        }
                }
        }
        else if (ob->transflag & OB_DUPLIFACES) {
                if (ob->type == OB_MESH)
-                       face_duplilist(duplilist, id, scene, ob, par_space_mat, level + 1, animated);
+                       face_duplilist(duplilist, id, scene, ob, par_space_mat, par_index, level + 1, animated);
        }
        else if (ob->transflag & OB_DUPLIFRAMES) {
                if (GS(id->name) == ID_SCE) { /* TODO - support dupligroups */
-                       frames_duplilist(duplilist, scene, ob, level + 1, animated);
+                       frames_duplilist(duplilist, scene, ob, par_index, level + 1, animated);
                }
        }
        else if (ob->transflag & OB_DUPLIGROUP) {
                DupliObject *dob;
                
-               group_duplilist(duplilist, scene, ob, level + 1, animated); /* now recursive */
+               group_duplilist(duplilist, scene, ob, par_index, level + 1, animated); /* now recursive */
 
                if (level == 0) {
                        for (dob = duplilist->first; dob; dob = dob->next)
@@ -1638,7 +1645,7 @@ ListBase *object_duplilist(Scene *sce, Object *ob)
 {
        ListBase *duplilist = MEM_mallocN(sizeof(ListBase), "duplilist");
        duplilist->first = duplilist->last = NULL;
-       object_duplilist_recursive((ID *)sce, sce, ob, duplilist, NULL, 0, 0);
+       object_duplilist_recursive((ID *)sce, sce, ob, duplilist, NULL, 0, 0, 0);
        return duplilist;
 }
 
index 86a1f715c3c0aea150cc8457383c7f7e43b9c987..d62e91dbde5042b27fd763041769b813fcecf29a 100644 (file)
@@ -1956,6 +1956,7 @@ static void registerShaderNodes(bNodeTreeType *ttype)
        register_node_type_sh_fresnel(ttype);
        register_node_type_sh_layer_weight(ttype);
        register_node_type_sh_tex_coord(ttype);
+       register_node_type_sh_particle_info(ttype);
 
        register_node_type_sh_background(ttype);
        register_node_type_sh_bsdf_diffuse(ttype);
index 44d7ec660f2c69ac405ca5bdd3ef2c78f291752e..24d33b49db2e674f3ffb21b2b1fd6be5f4d3e426 100644 (file)
@@ -301,6 +301,15 @@ typedef struct DupliObject {
 
        short type; /* from Object.transflag */
        char no_draw, animated;
+
+       /* Lowest-level particle index.
+        * Note: This is needed for particle info in shaders.
+        * Otherwise dupli groups in particle systems would override the
+        * index value from higher dupli levels. Would be nice to have full generic access
+        * to all dupli levels somehow, but for now this should cover most use-cases.
+        */
+       int particle_index;
+       int pad;
 } DupliObject;
 
 /* **************** OBJECT ********************* */
index 2b8050adca7a6c2b6071c914446a30c02f729a44..3981afe53490aee308ce63f57dbb41f2a20bd6d4 100644 (file)
@@ -80,6 +80,7 @@ DefNode( ShaderNode,     SH_NODE_NEW_GEOMETRY,       0,                      "NE
 DefNode( ShaderNode,     SH_NODE_LIGHT_PATH,         0,                      "LIGHT_PATH",         LightPath,        "Light Path",        ""       )
 DefNode( ShaderNode,     SH_NODE_LIGHT_FALLOFF,      0,                      "LIGHT_FALLOFF",      LightFalloff,     "Light Falloff",     ""       )
 DefNode( ShaderNode,     SH_NODE_OBJECT_INFO,        0,                      "OBJECT_INFO",        ObjectInfo,       "Object Info",       ""       )
+DefNode( ShaderNode,     SH_NODE_PARTICLE_INFO,      0,                      "PARTICLE_INFO",      ParticleInfo,     "Particle Info",     ""       )
 DefNode( ShaderNode,     SH_NODE_TEX_IMAGE,          def_sh_tex_image,       "TEX_IMAGE",          TexImage,         "Image Texture",     ""       )
 DefNode( ShaderNode,     SH_NODE_TEX_ENVIRONMENT,    def_sh_tex_environment, "TEX_ENVIRONMENT",    TexEnvironment,   "Environment Texture",""      )
 DefNode( ShaderNode,     SH_NODE_TEX_SKY,            def_sh_tex_sky,         "TEX_SKY",            TexSky,           "Sky Texture",       ""       )
index 7e3b368aa64cbd2bf3e3099d5c89a2cc7ecd7619..0399a8ee60db1a2ceb43cee19bf827ed899adc0e 100644 (file)
@@ -2545,6 +2545,16 @@ static void rna_def_dupli_object(BlenderRNA *brna)
        RNA_def_property_clear_flag(prop, PROP_ANIMATABLE | PROP_EDITABLE);
        RNA_def_property_ui_text(prop, "Hide", "Don't show dupli object in viewport or render");
 
+       prop = RNA_def_property(srna, "index", PROP_INT, PROP_NONE);
+       RNA_def_property_int_sdna(prop, NULL, "index");
+       RNA_def_property_clear_flag(prop, PROP_ANIMATABLE | PROP_EDITABLE);
+       RNA_def_property_ui_text(prop, "Index", "Index in the lowest-level dupli list");
+       
+       prop = RNA_def_property(srna, "particle_index", PROP_INT, PROP_NONE);
+       RNA_def_property_int_sdna(prop, NULL, "particle_index");
+       RNA_def_property_clear_flag(prop, PROP_ANIMATABLE | PROP_EDITABLE);
+       RNA_def_property_ui_text(prop, "Particle Index", "Index in the lowest-level particle dupli list");
+
        /* TODO: DupliObject has more properties that can be wrapped */
 }
 
index c637e3606f1a08300d895d0364684064f005a96b..8b2a5ebd263662ad5421e1e53b3bc039fb9134e8 100644 (file)
@@ -159,6 +159,7 @@ set(SRC
        shader/nodes/node_shader_light_path.c
        shader/nodes/node_shader_light_falloff.c
        shader/nodes/node_shader_object_info.c
+       shader/nodes/node_shader_particle_info.c
        shader/nodes/node_shader_mix_shader.c
        shader/nodes/node_shader_add_shader.c
        shader/nodes/node_shader_output_lamp.c
index dd6d25380b4445effc735d4eb1a552ab26c87d7d..6b0001819537ad36618aff5c62e462742c762da4 100644 (file)
@@ -78,6 +78,7 @@ void register_node_type_sh_object_info(struct bNodeTreeType *ttype);
 void register_node_type_sh_fresnel(struct bNodeTreeType *ttype);
 void register_node_type_sh_layer_weight(struct bNodeTreeType *ttype);
 void register_node_type_sh_tex_coord(struct bNodeTreeType *ttype);
+void register_node_type_sh_particle_info(struct bNodeTreeType *ttype);
 
 void register_node_type_sh_background(struct bNodeTreeType *ttype);
 void register_node_type_sh_bsdf_diffuse(struct bNodeTreeType *ttype);
diff --git a/source/blender/nodes/shader/nodes/node_shader_particle_info.c b/source/blender/nodes/shader/nodes/node_shader_particle_info.c
new file mode 100644 (file)
index 0000000..6456742
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * ***** 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) 2005 Blender Foundation.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "../node_shader_util.h"
+
+static bNodeSocketTemplate outputs[] = {
+        { SOCK_FLOAT,  0, "Age" },
+        { SOCK_FLOAT,  0, "Lifetime" },
+        { -1, 0, "" }
+};
+
+/* node type definition */
+void register_node_type_sh_particle_info(bNodeTreeType *ttype)
+{
+        static bNodeType ntype;
+
+        node_type_base(ttype, &ntype, SH_NODE_PARTICLE_INFO, "Particle Info", NODE_CLASS_INPUT, 0);
+        node_type_compatibility(&ntype, NODE_NEW_SHADING);
+        node_type_socket_templates(&ntype, NULL, outputs);
+        node_type_size(&ntype, 150, 60, 200);
+
+        nodeRegisterType(ttype, &ntype);
+}
+