diff options
author | Brecht Van Lommel <brecht@blender.org> | 2021-12-01 19:30:46 +0300 |
---|---|---|
committer | Brecht Van Lommel <brecht@blender.org> | 2021-12-16 22:54:04 +0300 |
commit | 35b1e9fc3acd6db565e7e54252a4a4152d8343d9 (patch) | |
tree | 8599df3b2be192d1fe19d30a8afb7e8d8729499e /intern/cycles/blender | |
parent | 2229179faa44e2c685b4eabd0c51d4c7d5c1f193 (diff) |
Cycles: pointcloud rendering
This add support for rendering of the point cloud object in Blender, as a native
geometry type in Cycles that is more memory and time efficient than instancing
sphere meshes. This can be useful for rendering sand, water splashes, particles,
motion graphics, etc.
Points are currently always rendered as spheres, with backface culling. More
shapes are likely to be added later, but this is the most important one and can
be customized with shaders.
For CPU rendering the Embree primitive is used, for GPU there is our own
intersection code. Motion blur is suppored. Volumes inside points are not
currently supported.
Implemented with help from:
* Kévin Dietrich: Alembic procedural integration
* Patrick Mourse: OptiX integration
* Josh Whelchel: update for cycles-x changes
Ref T92573
Differential Revision: https://developer.blender.org/D9887
Diffstat (limited to 'intern/cycles/blender')
-rw-r--r-- | intern/cycles/blender/CMakeLists.txt | 1 | ||||
-rw-r--r-- | intern/cycles/blender/addon/ui.py | 2 | ||||
-rw-r--r-- | intern/cycles/blender/geometry.cpp | 16 | ||||
-rw-r--r-- | intern/cycles/blender/object.cpp | 5 | ||||
-rw-r--r-- | intern/cycles/blender/pointcloud.cpp | 253 | ||||
-rw-r--r-- | intern/cycles/blender/sync.h | 6 |
6 files changed, 279 insertions, 4 deletions
diff --git a/intern/cycles/blender/CMakeLists.txt b/intern/cycles/blender/CMakeLists.txt index 1f05b1aa234..fe7d0b89bb0 100644 --- a/intern/cycles/blender/CMakeLists.txt +++ b/intern/cycles/blender/CMakeLists.txt @@ -40,6 +40,7 @@ set(SRC object_cull.cpp output_driver.cpp particles.cpp + pointcloud.cpp curves.cpp logging.cpp python.cpp diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py index fd86d75a301..e4fbc898070 100644 --- a/intern/cycles/blender/addon/ui.py +++ b/intern/cycles/blender/addon/ui.py @@ -1020,7 +1020,7 @@ class CYCLES_OBJECT_PT_motion_blur(CyclesButtonsPanel, Panel): def poll(cls, context): ob = context.object if CyclesButtonsPanel.poll(context) and ob: - if ob.type in {'MESH', 'CURVE', 'CURVE', 'SURFACE', 'FONT', 'META', 'CAMERA'}: + if ob.type in {'MESH', 'CURVE', 'CURVE', 'SURFACE', 'FONT', 'META', 'CAMERA', 'HAIR', 'POINTCLOUD'}: return True if ob.instance_type == 'COLLECTION' and ob.instance_collection: return True diff --git a/intern/cycles/blender/geometry.cpp b/intern/cycles/blender/geometry.cpp index a5f36a3a17d..78c803b7adb 100644 --- a/intern/cycles/blender/geometry.cpp +++ b/intern/cycles/blender/geometry.cpp @@ -19,6 +19,7 @@ #include "scene/hair.h" #include "scene/mesh.h" #include "scene/object.h" +#include "scene/pointcloud.h" #include "scene/volume.h" #include "blender/sync.h" @@ -39,6 +40,10 @@ static Geometry::Type determine_geom_type(BObjectInfo &b_ob_info, bool use_parti return Geometry::HAIR; } + if (b_ob_info.object_data.is_a(&RNA_PointCloud)) { + return Geometry::POINTCLOUD; + } + if (b_ob_info.object_data.is_a(&RNA_Volume) || (b_ob_info.object_data == b_ob_info.real_object.data() && object_fluid_gas_domain_find(b_ob_info.real_object))) { @@ -111,6 +116,9 @@ Geometry *BlenderSync::sync_geometry(BL::Depsgraph &b_depsgraph, else if (geom_type == Geometry::VOLUME) { geom = scene->create_node<Volume>(); } + else if (geom_type == Geometry::POINTCLOUD) { + geom = scene->create_node<PointCloud>(); + } else { geom = scene->create_node<Mesh>(); } @@ -170,6 +178,10 @@ Geometry *BlenderSync::sync_geometry(BL::Depsgraph &b_depsgraph, Volume *volume = static_cast<Volume *>(geom); sync_volume(b_ob_info, volume); } + else if (geom_type == Geometry::POINTCLOUD) { + PointCloud *pointcloud = static_cast<PointCloud *>(geom); + sync_pointcloud(pointcloud, b_ob_info); + } else { Mesh *mesh = static_cast<Mesh *>(geom); sync_mesh(b_depsgraph, b_ob_info, mesh); @@ -231,6 +243,10 @@ void BlenderSync::sync_geometry_motion(BL::Depsgraph &b_depsgraph, object_fluid_gas_domain_find(b_ob_info.real_object)) { /* No volume motion blur support yet. */ } + else if (b_ob_info.object_data.is_a(&RNA_PointCloud)) { + PointCloud *pointcloud = static_cast<PointCloud *>(geom); + sync_pointcloud_motion(pointcloud, b_ob_info, motion_step); + } else { Mesh *mesh = static_cast<Mesh *>(geom); sync_mesh_motion(b_depsgraph, b_ob_info, mesh, motion_step); diff --git a/intern/cycles/blender/object.cpp b/intern/cycles/blender/object.cpp index d6c52f7faf9..86314d3b196 100644 --- a/intern/cycles/blender/object.cpp +++ b/intern/cycles/blender/object.cpp @@ -72,7 +72,8 @@ bool BlenderSync::object_is_geometry(BObjectInfo &b_ob_info) BL::Object::type_enum type = b_ob_info.iter_object.type(); - if (type == BL::Object::type_VOLUME || type == BL::Object::type_HAIR) { + if (type == BL::Object::type_VOLUME || type == BL::Object::type_HAIR || + type == BL::Object::type_POINTCLOUD) { /* Will be exported attached to mesh. */ return true; } @@ -206,7 +207,7 @@ Object *BlenderSync::sync_object(BL::Depsgraph &b_depsgraph, return NULL; } - /* only interested in object that we can create meshes from */ + /* only interested in object that we can create geometry from */ if (!object_is_geometry(b_ob_info)) { return NULL; } diff --git a/intern/cycles/blender/pointcloud.cpp b/intern/cycles/blender/pointcloud.cpp new file mode 100644 index 00000000000..a9e616a468f --- /dev/null +++ b/intern/cycles/blender/pointcloud.cpp @@ -0,0 +1,253 @@ +/* + * 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 "scene/pointcloud.h" +#include "scene/attribute.h" +#include "scene/scene.h" + +#include "blender/sync.h" +#include "blender/util.h" + +#include "util/foreach.h" +#include "util/hash.h" + +CCL_NAMESPACE_BEGIN + +template<typename TypeInCycles, typename GetValueAtIndex> +static void fill_generic_attribute(BL::PointCloud &b_pointcloud, + TypeInCycles *data, + const GetValueAtIndex &get_value_at_index) +{ + const int num_points = b_pointcloud.points.length(); + for (int i = 0; i < num_points; i++) { + data[i] = get_value_at_index(i); + } +} + +static void copy_attributes(PointCloud *pointcloud, BL::PointCloud b_pointcloud) +{ + AttributeSet &attributes = pointcloud->attributes; + for (BL::Attribute &b_attribute : b_pointcloud.attributes) { + const ustring name{b_attribute.name().c_str()}; + + if (attributes.find(name)) { + continue; + } + + const AttributeElement element = ATTR_ELEMENT_VERTEX; + const BL::Attribute::data_type_enum b_data_type = b_attribute.data_type(); + switch (b_data_type) { + case BL::Attribute::data_type_FLOAT: { + BL::FloatAttribute b_float_attribute{b_attribute}; + Attribute *attr = attributes.add(name, TypeFloat, element); + float *data = attr->data_float(); + fill_generic_attribute( + b_pointcloud, data, [&](int i) { return b_float_attribute.data[i].value(); }); + break; + } + case BL::Attribute::data_type_BOOLEAN: { + BL::BoolAttribute b_bool_attribute{b_attribute}; + Attribute *attr = attributes.add(name, TypeFloat, element); + float *data = attr->data_float(); + fill_generic_attribute( + b_pointcloud, data, [&](int i) { return (float)b_bool_attribute.data[i].value(); }); + break; + } + case BL::Attribute::data_type_INT: { + BL::IntAttribute b_int_attribute{b_attribute}; + Attribute *attr = attributes.add(name, TypeFloat, element); + float *data = attr->data_float(); + fill_generic_attribute( + b_pointcloud, data, [&](int i) { return (float)b_int_attribute.data[i].value(); }); + break; + } + case BL::Attribute::data_type_FLOAT_VECTOR: { + BL::FloatVectorAttribute b_vector_attribute{b_attribute}; + Attribute *attr = attributes.add(name, TypeVector, element); + float3 *data = attr->data_float3(); + fill_generic_attribute(b_pointcloud, data, [&](int i) { + BL::Array<float, 3> v = b_vector_attribute.data[i].vector(); + return make_float3(v[0], v[1], v[2]); + }); + break; + } + case BL::Attribute::data_type_FLOAT_COLOR: { + BL::FloatColorAttribute b_color_attribute{b_attribute}; + Attribute *attr = attributes.add(name, TypeRGBA, element); + float4 *data = attr->data_float4(); + fill_generic_attribute(b_pointcloud, data, [&](int i) { + BL::Array<float, 4> v = b_color_attribute.data[i].color(); + return make_float4(v[0], v[1], v[2], v[3]); + }); + break; + } + case BL::Attribute::data_type_FLOAT2: { + BL::Float2Attribute b_float2_attribute{b_attribute}; + Attribute *attr = attributes.add(name, TypeFloat2, element); + float2 *data = attr->data_float2(); + fill_generic_attribute(b_pointcloud, data, [&](int i) { + BL::Array<float, 2> v = b_float2_attribute.data[i].vector(); + return make_float2(v[0], v[1]); + }); + break; + } + default: + /* Not supported. */ + break; + } + } +} + +static void export_pointcloud(Scene *scene, PointCloud *pointcloud, BL::PointCloud b_pointcloud) +{ + /* TODO: optimize so we can straight memcpy arrays from Blender? */ + + /* Add requested attributes. */ + Attribute *attr_random = NULL; + if (pointcloud->need_attribute(scene, ATTR_STD_POINT_RANDOM)) { + attr_random = pointcloud->attributes.add(ATTR_STD_POINT_RANDOM); + } + + /* Reserve memory. */ + const int num_points = b_pointcloud.points.length(); + pointcloud->reserve(num_points); + + /* Export points. */ + BL::PointCloud::points_iterator b_point_iter; + for (b_pointcloud.points.begin(b_point_iter); b_point_iter != b_pointcloud.points.end(); + ++b_point_iter) { + BL::Point b_point = *b_point_iter; + const float3 co = get_float3(b_point.co()); + const float radius = b_point.radius(); + pointcloud->add_point(co, radius); + + /* Random number per point. */ + if (attr_random != NULL) { + attr_random->add(hash_uint2_to_float(b_point.index(), 0)); + } + } + + /* Export attributes */ + copy_attributes(pointcloud, b_pointcloud); +} + +static void export_pointcloud_motion(PointCloud *pointcloud, + BL::PointCloud b_pointcloud, + int motion_step) +{ + /* Find or add attribute. */ + Attribute *attr_mP = pointcloud->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); + bool new_attribute = false; + + if (!attr_mP) { + attr_mP = pointcloud->attributes.add(ATTR_STD_MOTION_VERTEX_POSITION); + new_attribute = true; + } + + /* Export motion points. */ + const int num_points = pointcloud->num_points(); + float3 *mP = attr_mP->data_float3() + motion_step * num_points; + bool have_motion = false; + int num_motion_points = 0; + const array<float3> &pointcloud_points = pointcloud->get_points(); + + BL::PointCloud::points_iterator b_point_iter; + for (b_pointcloud.points.begin(b_point_iter); b_point_iter != b_pointcloud.points.end(); + ++b_point_iter) { + BL::Point b_point = *b_point_iter; + + if (num_motion_points < num_points) { + float3 P = get_float3(b_point.co()); + P.w = b_point.radius(); + mP[num_motion_points] = P; + have_motion = have_motion || (P != pointcloud_points[num_motion_points]); + num_motion_points++; + } + } + + /* In case of new attribute, we verify if there really was any motion. */ + if (new_attribute) { + if (num_motion_points != num_points || !have_motion) { + pointcloud->attributes.remove(ATTR_STD_MOTION_VERTEX_POSITION); + } + else if (motion_step > 0) { + /* Motion, fill up previous steps that we might have skipped because + * they had no motion, but we need them anyway now. */ + for (int step = 0; step < motion_step; step++) { + pointcloud->copy_center_to_motion_step(step); + } + } + } + + /* Export attributes */ + copy_attributes(pointcloud, b_pointcloud); +} + +void BlenderSync::sync_pointcloud(PointCloud *pointcloud, BObjectInfo &b_ob_info) +{ + size_t old_numpoints = pointcloud->num_points(); + + array<Node *> used_shaders = pointcloud->get_used_shaders(); + + PointCloud new_pointcloud; + new_pointcloud.set_used_shaders(used_shaders); + + /* TODO: add option to filter out points in the view layer. */ + BL::PointCloud b_pointcloud(b_ob_info.object_data); + export_pointcloud(scene, &new_pointcloud, b_pointcloud); + + /* update original sockets */ + for (const SocketType &socket : new_pointcloud.type->inputs) { + /* Those sockets are updated in sync_object, so do not modify them. */ + if (socket.name == "use_motion_blur" || socket.name == "motion_steps" || + socket.name == "used_shaders") { + continue; + } + pointcloud->set_value(socket, new_pointcloud, socket); + } + + pointcloud->attributes.clear(); + foreach (Attribute &attr, new_pointcloud.attributes.attributes) { + pointcloud->attributes.attributes.push_back(std::move(attr)); + } + + /* tag update */ + const bool rebuild = (pointcloud && old_numpoints != pointcloud->num_points()); + pointcloud->tag_update(scene, rebuild); +} + +void BlenderSync::sync_pointcloud_motion(PointCloud *pointcloud, + BObjectInfo &b_ob_info, + int motion_step) +{ + /* Skip if nothing exported. */ + if (pointcloud->num_points() == 0) { + return; + } + + /* Export deformed coordinates. */ + if (ccl::BKE_object_is_deform_modified(b_ob_info, b_scene, preview)) { + /* PointCloud object. */ + BL::PointCloud b_pointcloud(b_ob_info.object_data); + export_pointcloud_motion(pointcloud, b_pointcloud, motion_step); + } + else { + /* No deformation on this frame, copy coordinates if other frames did have it. */ + pointcloud->copy_center_to_motion_step(motion_step); + } +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/blender/sync.h b/intern/cycles/blender/sync.h index 2db4125ba1c..d074f90bb1b 100644 --- a/intern/cycles/blender/sync.h +++ b/intern/cycles/blender/sync.h @@ -169,12 +169,16 @@ class BlenderSync { Hair *hair, BL::Mesh &b_mesh, BObjectInfo &b_ob_info, bool motion, int motion_step = 0); bool object_has_particle_hair(BL::Object b_ob); + /* Point Cloud */ + void sync_pointcloud(PointCloud *pointcloud, BObjectInfo &b_ob_info); + void sync_pointcloud_motion(PointCloud *pointcloud, BObjectInfo &b_ob_info, int motion_step = 0); + /* Camera */ void sync_camera_motion( BL::RenderSettings &b_render, BL::Object &b_ob, int width, int height, float motion_time); /* Geometry */ - Geometry *sync_geometry(BL::Depsgraph &b_depsgrpah, + Geometry *sync_geometry(BL::Depsgraph &b_depsgraph, BObjectInfo &b_ob_info, bool object_updated, bool use_particle_hair, |