Blender V4.3
blender/pointcloud.cpp
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
2 *
3 * SPDX-License-Identifier: Apache-2.0 */
4
5#include <optional>
6
7#include "scene/attribute.h"
8#include "scene/pointcloud.h"
9#include "scene/scene.h"
10
12#include "blender/sync.h"
13#include "blender/util.h"
14
15#include "util/color.h"
16#include "util/foreach.h"
17#include "util/hash.h"
18
19#include "BKE_attribute.hh"
20#include "BKE_attribute_math.hh"
21#include "BKE_pointcloud.hh"
22
24
26 const blender::Span<blender::float3> b_attribute,
27 const float motion_scale)
28{
29 const int num_points = pointcloud->get_points().size();
30
31 /* Override motion steps to fixed number. */
32 pointcloud->set_motion_steps(3);
33
34 /* Find or add attribute */
35 float3 *P = pointcloud->get_points().data();
36 float *radius = pointcloud->get_radius().data();
38
39 if (!attr_mP) {
40 attr_mP = pointcloud->attributes.add(ATTR_STD_MOTION_VERTEX_POSITION);
41 }
42
43 /* Only export previous and next frame, we don't have any in between data. */
44 float motion_times[2] = {-1.0f, 1.0f};
45 for (int step = 0; step < 2; step++) {
46 const float relative_time = motion_times[step] * 0.5f * motion_scale;
47 float4 *mP = attr_mP->data_float4() + step * num_points;
48
49 for (int i = 0; i < num_points; i++) {
50 float3 Pi = P[i] + make_float3(b_attribute[i][0], b_attribute[i][1], b_attribute[i][2]) *
51 relative_time;
52 mP[i] = make_float4(Pi.x, Pi.y, Pi.z, radius[i]);
53 }
54 }
55}
56
57static void copy_attributes(PointCloud *pointcloud,
58 const ::PointCloud &b_pointcloud,
59 const bool need_motion,
60 const float motion_scale)
61{
62 const blender::bke::AttributeAccessor b_attributes = b_pointcloud.attributes();
63 if (b_attributes.domain_size(blender::bke::AttrDomain::Point) == 0) {
64 return;
65 }
66
67 AttributeSet &attributes = pointcloud->attributes;
68 static const ustring u_velocity("velocity");
69 b_attributes.foreach_attribute([&](const blender::bke::AttributeIter &iter) {
70 const ustring name{std::string_view(iter.name)};
71
72 if (need_motion && name == u_velocity) {
73 const blender::VArraySpan b_attr = *iter.get<blender::float3>();
74 attr_create_motion_from_velocity(pointcloud, b_attr, motion_scale);
75 }
76
77 if (attributes.find(name)) {
78 return;
79 }
80
81 const blender::bke::GAttributeReader b_attr = iter.get();
83 using BlenderT = decltype(dummy);
84 using Converter = typename ccl::AttributeConverter<BlenderT>;
85 using CyclesT = typename Converter::CyclesT;
86 if constexpr (!std::is_void_v<CyclesT>) {
87 Attribute *attr = attributes.add(name, Converter::type_desc, ATTR_ELEMENT_VERTEX);
88 CyclesT *data = reinterpret_cast<CyclesT *>(attr->data());
89
90 const blender::VArraySpan src = b_attr.varray.typed<BlenderT>();
91 for (const int i : src.index_range()) {
92 data[i] = Converter::convert(src[i]);
93 }
94 }
95 });
96 });
97}
98
99static void export_pointcloud(Scene *scene,
100 PointCloud *pointcloud,
101 const ::PointCloud &b_pointcloud,
102 const bool need_motion,
103 const float motion_scale)
104{
105 const blender::Span<blender::float3> b_positions = b_pointcloud.positions();
106 const blender::VArraySpan b_radius = *b_pointcloud.attributes().lookup<float>(
108
109 pointcloud->resize(b_positions.size());
110
111 float3 *points = pointcloud->get_points().data();
112
113 for (const int i : b_positions.index_range()) {
114 points[i] = make_float3(b_positions[i][0], b_positions[i][1], b_positions[i][2]);
115 }
116
117 float *radius = pointcloud->get_radius().data();
118 if (!b_radius.is_empty()) {
119 std::copy(b_radius.data(), b_radius.data() + b_positions.size(), radius);
120 }
121 else {
122 std::fill(radius, radius + b_positions.size(), 0.01f);
123 }
124
125 int *shader = pointcloud->get_shader().data();
126 std::fill(shader, shader + b_positions.size(), 0);
127
128 if (pointcloud->need_attribute(scene, ATTR_STD_POINT_RANDOM)) {
129 Attribute *attr_random = pointcloud->attributes.add(ATTR_STD_POINT_RANDOM);
130 float *data = attr_random->data_float();
131 for (const int i : b_positions.index_range()) {
132 data[i] = hash_uint2_to_float(i, 0);
133 }
134 }
135
136 copy_attributes(pointcloud, b_pointcloud, need_motion, motion_scale);
137}
138
139static void export_pointcloud_motion(PointCloud *pointcloud,
140 const ::PointCloud &b_pointcloud,
141 int motion_step)
142{
143 /* Find or add attribute. */
145 bool new_attribute = false;
146
147 if (!attr_mP) {
148 attr_mP = pointcloud->attributes.add(ATTR_STD_MOTION_VERTEX_POSITION);
149 new_attribute = true;
150 }
151
152 const int num_points = pointcloud->num_points();
153 /* Point cloud attributes are stored as float4 with the radius in the w element.
154 * This is explicit now as float3 is no longer interchangeable with float4 as it
155 * is packed now. */
156 float4 *mP = attr_mP->data_float4() + motion_step * num_points;
157 bool have_motion = false;
158 const array<float3> &pointcloud_points = pointcloud->get_points();
159
160 const blender::Span<blender::float3> b_positions = b_pointcloud.positions();
161 const blender::VArraySpan b_radius = *b_pointcloud.attributes().lookup<float>(
163
164 for (int i = 0; i < std::min<int>(num_points, b_positions.size()); i++) {
165 const float3 P = make_float3(b_positions[i][0], b_positions[i][1], b_positions[i][2]);
166 const float radius = b_radius.is_empty() ? 0.01f : b_radius[i];
167 mP[i] = make_float4(P.x, P.y, P.z, radius);
168 have_motion = have_motion || (P != pointcloud_points[i]);
169 }
170
171 /* In case of new attribute, we verify if there really was any motion. */
172 if (new_attribute) {
173 if (b_positions.size() != num_points || !have_motion) {
175 }
176 else if (motion_step > 0) {
177 /* Motion, fill up previous steps that we might have skipped because
178 * they had no motion, but we need them anyway now. */
179 for (int step = 0; step < motion_step; step++) {
180 pointcloud->copy_center_to_motion_step(step);
181 }
182 }
183 }
184
185 /* Export attributes */
186 copy_attributes(pointcloud, b_pointcloud, false, 0.0f);
187}
188
189void BlenderSync::sync_pointcloud(PointCloud *pointcloud, BObjectInfo &b_ob_info)
190{
191 size_t old_numpoints = pointcloud->num_points();
192
193 array<Node *> used_shaders = pointcloud->get_used_shaders();
194
195 PointCloud new_pointcloud;
196 new_pointcloud.set_used_shaders(used_shaders);
197
198 /* TODO: add option to filter out points in the view layer. */
199 BL::PointCloud b_pointcloud(b_ob_info.object_data);
200 /* Motion blur attribute is relative to seconds, we need it relative to frames. */
201 const bool need_motion = object_need_motion_attribute(b_ob_info, scene);
202 const float motion_scale = (need_motion) ?
203 scene->motion_shutter_time() /
204 (b_scene.render().fps() / b_scene.render().fps_base()) :
205 0.0f;
206 export_pointcloud(scene,
207 &new_pointcloud,
208 *static_cast<const ::PointCloud *>(b_pointcloud.ptr.data),
209 need_motion,
210 motion_scale);
211
212 /* Update original sockets. */
213 for (const SocketType &socket : new_pointcloud.type->inputs) {
214 /* Those sockets are updated in sync_object, so do not modify them. */
215 if (socket.name == "use_motion_blur" || socket.name == "motion_steps" ||
216 socket.name == "used_shaders")
217 {
218 continue;
219 }
220 pointcloud->set_value(socket, new_pointcloud, socket);
221 }
222
223 pointcloud->attributes.clear();
224 foreach (Attribute &attr, new_pointcloud.attributes.attributes) {
225 pointcloud->attributes.attributes.push_back(std::move(attr));
226 }
227
228 /* Tag update. */
229 const bool rebuild = (pointcloud && old_numpoints != pointcloud->num_points());
230 pointcloud->tag_update(scene, rebuild);
231}
232
233void BlenderSync::sync_pointcloud_motion(PointCloud *pointcloud,
234 BObjectInfo &b_ob_info,
235 int motion_step)
236{
237 /* Skip if nothing exported. */
238 if (pointcloud->num_points() == 0) {
239 return;
240 }
241
242 /* Export deformed coordinates. */
243 if (ccl::BKE_object_is_deform_modified(b_ob_info, b_scene, preview)) {
244 /* PointCloud object. */
245 BL::PointCloud b_pointcloud(b_ob_info.object_data);
247 pointcloud, *static_cast<const ::PointCloud *>(b_pointcloud.ptr.data), motion_step);
248 }
249 else {
250 /* No deformation on this frame, copy coordinates if other frames did have it. */
251 pointcloud->copy_center_to_motion_step(motion_step);
252 }
253}
254
General operations for point clouds.
struct PointCloud PointCloud
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Generate a perturbed normal from an RGB normal map image Typically used for faking highly detailed surfaces Generate an OSL shader from a file or text data block Image Sample an image file as a texture Gabor Generate Gabor noise Gradient Generate interpolated color and intensity values based on the input vector Magic Generate a psychedelic color texture Voronoi Generate Worley noise based on the distance to random points Typically used to generate textures such as or biological cells Brick Generate a procedural texture producing bricks Texture Retrieve multiple types of texture coordinates nTypically used as inputs for texture nodes Vector Convert a or normal between and object coordinate space Combine Create a color from its and value channels Color Attribute
static void export_pointcloud(Scene *scene, PointCloud *pointcloud, const ::PointCloud &b_pointcloud, const bool need_motion, const float motion_scale)
static void export_pointcloud_motion(PointCloud *pointcloud, const ::PointCloud &b_pointcloud, int motion_step)
static void copy_attributes(PointCloud *pointcloud, const ::PointCloud &b_pointcloud, const bool need_motion, const float motion_scale)
static CCL_NAMESPACE_BEGIN void attr_create_motion_from_velocity(PointCloud *pointcloud, const blender::Span< blender::float3 > b_attribute, const float motion_scale)
Attribute * add(ustring name, TypeDesc type, AttributeElement element)
list< Attribute > attributes
Attribute * find(ustring name) const
void remove(ustring name)
void clear(bool preserve_voxel_data=false)
float * data_float()
float4 * data_float4()
void tag_update(Scene *scene, bool rebuild)
bool need_attribute(Scene *scene, AttributeStandard std)
AttributeSet attributes
constexpr const T * data() const
Definition BLI_span.hh:216
constexpr int64_t size() const
Definition BLI_span.hh:253
constexpr IndexRange index_range() const
Definition BLI_span.hh:402
constexpr bool is_empty() const
Definition BLI_span.hh:261
void foreach_attribute(const FunctionRef< void(const AttributeIter &)> fn) const
int domain_size(const AttrDomain domain) const
GAttributeReader get() const
static bool object_need_motion_attribute(BObjectInfo &b_ob_info, Scene *scene)
#define CCL_NAMESPACE_END
ccl_device_forceinline float4 make_float4(const float x, const float y, const float z, const float w)
ccl_device_forceinline float3 make_float3(const float x, const float y, const float z)
ccl_device_inline float hash_uint2_to_float(uint kx, uint ky)
Definition hash.h:141
@ ATTR_STD_POINT_RANDOM
@ ATTR_STD_MOTION_VERTEX_POSITION
void convert_to_static_type(const CPPType &cpp_type, const Func &func)
VecBase< float, 3 > float3
vector< SocketType, std::allocator< SocketType > > inputs
Definition node_type.h:125
const NodeType * type
Definition graph/node.h:178
void set_value(const SocketType &input, const Node &other, const SocketType &other_input)
size_t num_points() const
void resize(int numpoints)
void copy_center_to_motion_step(const int motion_step)
ustring name
Definition node_type.h:79
float z
Definition sky_float3.h:27
float y
Definition sky_float3.h:27
float x
Definition sky_float3.h:27
VecBase< float, 4 > float4