forked from bartvdbraak/blender
Particle Info node for Cycles. This can be used to access particle information in material shaders for dupli objects. For now only the particle Age and individual Lifetime (in frames) are supported, more attributes can be added when needed.
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/
This commit is contained in:
parent
82d3d9f2ba
commit
5e1bbde01d
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
158
intern/cycles/blender/blender_particles.cpp
Normal file
158
intern/cycles/blender/blender_particles.cpp
Normal file
@ -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
|
@ -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());
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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 ********************* */
|
||||
|
@ -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", "" )
|
||||
|
@ -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 */
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user