Blender V4.3
alembic_read.cpp
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2021-2022 Blender Foundation
2 *
3 * SPDX-License-Identifier: Apache-2.0 */
4
6#include "scene/alembic.h"
7#include "scene/mesh.h"
8
9#include "util/color.h"
10#include "util/progress.h"
11
12#ifdef WITH_ALEMBIC
13
14using namespace Alembic::AbcGeom;
15
17
18static float3 make_float3_from_yup(const V3f &v)
19{
20 return make_float3(v.x, -v.z, v.y);
21}
22
23/* get the sample times to load data for the given the start and end frame of the procedural */
24static set<chrono_t> get_relevant_sample_times(AlembicProcedural *proc,
25 const TimeSampling &time_sampling,
26 size_t num_samples)
27{
28 set<chrono_t> result;
29
30 if (num_samples < 2) {
31 result.insert(0.0);
32 return result;
33 }
34
35 double start_frame;
36 double end_frame;
37
38 if (proc->get_use_prefetch()) {
39 // load the data for the entire animation
40 start_frame = static_cast<double>(proc->get_start_frame());
41 end_frame = static_cast<double>(proc->get_end_frame());
42 }
43 else {
44 // load the data for the current frame
45 start_frame = static_cast<double>(proc->get_frame());
46 end_frame = start_frame;
47 }
48
49 const double frame_rate = static_cast<double>(proc->get_frame_rate());
50 const double start_time = start_frame / frame_rate;
51 const double end_time = (end_frame + 1) / frame_rate;
52
53 const size_t start_index = time_sampling.getFloorIndex(start_time, num_samples).first;
54 const size_t end_index = time_sampling.getCeilIndex(end_time, num_samples).first;
55
56 for (size_t i = start_index; i < end_index; ++i) {
57 result.insert(time_sampling.getSampleTime(i));
58 }
59
60 return result;
61}
62
63/* Main function to read data, this will iterate over all the relevant sample times for the
64 * duration of the requested animation, and call the DataReadingFunc for each of those sample time.
65 */
66template<typename Params, typename DataReadingFunc>
67static void read_data_loop(AlembicProcedural *proc,
68 CachedData &cached_data,
69 const Params &params,
70 DataReadingFunc &&func,
71 Progress &progress)
72{
73 const std::set<chrono_t> times = get_relevant_sample_times(
74 proc, *params.time_sampling, params.num_samples);
75
76 cached_data.set_time_sampling(*params.time_sampling);
77
78 for (chrono_t time : times) {
79 if (progress.get_cancel()) {
80 return;
81 }
82
83 func(cached_data, params, time);
84 }
85}
86
87/* Polygon Mesh Geometries. */
88
89/* Compute the vertex normals in case none are present in the IPolyMeshSchema, this is mostly used
90 * to avoid computing them in the GeometryManager in order to speed up data updates. */
91static void compute_vertex_normals(CachedData &cache, double current_time)
92{
93 if (cache.vertices.size() == 0) {
94 return;
95 }
96
97 CachedData::CachedAttribute &attr_normal = cache.add_attribute(
98 ustring("N"), cache.vertices.get_time_sampling());
99 attr_normal.std = ATTR_STD_VERTEX_NORMAL;
100 attr_normal.element = ATTR_ELEMENT_VERTEX;
101 attr_normal.type_desc = TypeNormal;
102
103 const array<float3> *vertices =
104 cache.vertices.data_for_time_no_check(current_time).get_data_or_null();
105 const array<int3> *triangles =
106 cache.triangles.data_for_time_no_check(current_time).get_data_or_null();
107
108 if (!vertices || !triangles) {
109 attr_normal.data.add_no_data(current_time);
110 return;
111 }
112
113 array<char> attr_data(vertices->size() * sizeof(float3));
114 float3 *attr_ptr = reinterpret_cast<float3 *>(attr_data.data());
115 memset(attr_ptr, 0, vertices->size() * sizeof(float3));
116
117 for (size_t t = 0; t < triangles->size(); ++t) {
118 const int3 tri_int3 = triangles->data()[t];
119 Mesh::Triangle tri{};
120 tri.v[0] = tri_int3[0];
121 tri.v[1] = tri_int3[1];
122 tri.v[2] = tri_int3[2];
123
124 const float3 tri_N = tri.compute_normal(vertices->data());
125
126 for (int v = 0; v < 3; ++v) {
127 attr_ptr[tri_int3[v]] += tri_N;
128 }
129 }
130
131 for (size_t v = 0; v < vertices->size(); ++v) {
132 attr_ptr[v] = normalize(attr_ptr[v]);
133 }
134
135 attr_normal.data.add_data(attr_data, current_time);
136}
137
138static void add_normals(const Int32ArraySamplePtr face_indices,
139 const IN3fGeomParam &normals,
140 double time,
141 CachedData &cached_data)
142{
143 switch (normals.getScope()) {
144 case kFacevaryingScope: {
145 const ISampleSelector iss = ISampleSelector(time);
146 const IN3fGeomParam::Sample sample = normals.getExpandedValue(iss);
147
148 if (!sample.valid()) {
149 return;
150 }
151
152 CachedData::CachedAttribute &attr = cached_data.add_attribute(ustring(normals.getName()),
153 *normals.getTimeSampling());
154 attr.std = ATTR_STD_VERTEX_NORMAL;
155
156 const array<float3> *vertices =
157 cached_data.vertices.data_for_time_no_check(time).get_data_or_null();
158
159 if (!vertices) {
160 return;
161 }
162
164 data.resize(vertices->size() * sizeof(float3));
165
166 float3 *data_float3 = reinterpret_cast<float3 *>(data.data());
167
168 const int *face_indices_array = face_indices->get();
169 const N3fArraySamplePtr values = sample.getVals();
170
171 for (size_t i = 0; i < face_indices->size(); ++i) {
172 int point_index = face_indices_array[i];
173 data_float3[point_index] = make_float3_from_yup(values->get()[i]);
174 }
175
176 attr.data.add_data(data, time);
177 break;
178 }
179 case kVaryingScope:
180 case kVertexScope: {
181 const ISampleSelector iss = ISampleSelector(time);
182 const IN3fGeomParam::Sample sample = normals.getExpandedValue(iss);
183
184 if (!sample.valid()) {
185 return;
186 }
187
188 CachedData::CachedAttribute &attr = cached_data.add_attribute(ustring(normals.getName()),
189 *normals.getTimeSampling());
190 attr.std = ATTR_STD_VERTEX_NORMAL;
191
192 const array<float3> *vertices =
193 cached_data.vertices.data_for_time_no_check(time).get_data_or_null();
194
195 if (!vertices) {
196 return;
197 }
198
200 data.resize(vertices->size() * sizeof(float3));
201
202 float3 *data_float3 = reinterpret_cast<float3 *>(data.data());
203
204 const Imath::V3f *values = sample.getVals()->get();
205
206 for (size_t i = 0; i < vertices->size(); ++i) {
207 data_float3[i] = make_float3_from_yup(values[i]);
208 }
209
210 attr.data.add_data(data, time);
211
212 break;
213 }
214 default: {
215 break;
216 }
217 }
218}
219
220static void add_positions(const P3fArraySamplePtr positions, double time, CachedData &cached_data)
221{
222 if (!positions) {
223 return;
224 }
225
226 array<float3> vertices;
227 vertices.reserve(positions->size());
228
229 for (size_t i = 0; i < positions->size(); i++) {
230 V3f f = positions->get()[i];
231 vertices.push_back_reserved(make_float3_from_yup(f));
232 }
233
234 cached_data.vertices.add_data(vertices, time);
235}
236
237static void add_triangles(const Int32ArraySamplePtr face_counts,
238 const Int32ArraySamplePtr face_indices,
239 double time,
240 CachedData &cached_data,
241 const array<int> &polygon_to_shader)
242{
243 if (!face_counts || !face_indices) {
244 return;
245 }
246
247 const size_t num_faces = face_counts->size();
248 const int *face_counts_array = face_counts->get();
249 const int *face_indices_array = face_indices->get();
250
251 size_t num_triangles = 0;
252 for (size_t i = 0; i < face_counts->size(); i++) {
253 num_triangles += face_counts_array[i] - 2;
254 }
255
256 array<int> shader;
257 array<int3> triangles;
258 array<int> uv_loops;
259 shader.reserve(num_triangles);
260 triangles.reserve(num_triangles);
261 uv_loops.reserve(num_triangles * 3);
262 int index_offset = 0;
263
264 for (size_t i = 0; i < num_faces; i++) {
265 int current_shader = 0;
266
267 if (!polygon_to_shader.empty()) {
268 current_shader = polygon_to_shader[i];
269 }
270
271 for (int j = 0; j < face_counts_array[i] - 2; j++) {
272 int v0 = face_indices_array[index_offset];
273 int v1 = face_indices_array[index_offset + j + 1];
274 int v2 = face_indices_array[index_offset + j + 2];
275
276 shader.push_back_reserved(current_shader);
277
278 /* Alembic orders the loops following the RenderMan convention, so need to go in reverse. */
279 triangles.push_back_reserved(make_int3(v2, v1, v0));
280 uv_loops.push_back_reserved(index_offset + j + 2);
281 uv_loops.push_back_reserved(index_offset + j + 1);
282 uv_loops.push_back_reserved(index_offset);
283 }
284
285 index_offset += face_counts_array[i];
286 }
287
288 cached_data.triangles.add_data(triangles, time);
289 cached_data.uv_loops.add_data(uv_loops, time);
290 cached_data.shader.add_data(shader, time);
291}
292
293static array<int> compute_polygon_to_shader_map(
294 const Int32ArraySamplePtr &face_counts,
295 const vector<FaceSetShaderIndexPair> &face_set_shader_index,
296 ISampleSelector sample_sel)
297{
298 if (face_set_shader_index.empty()) {
299 return {};
300 }
301
302 if (!face_counts) {
303 return {};
304 }
305
306 if (face_counts->size() == 0) {
307 return {};
308 }
309
310 array<int> polygon_to_shader(face_counts->size());
311
312 for (const FaceSetShaderIndexPair &pair : face_set_shader_index) {
313 const IFaceSet &face_set = pair.face_set;
314 const IFaceSetSchema face_schem = face_set.getSchema();
315 const IFaceSetSchema::Sample face_sample = face_schem.getValue(sample_sel);
316 const Int32ArraySamplePtr group_faces = face_sample.getFaces();
317 const size_t num_group_faces = group_faces->size();
318
319 for (size_t l = 0; l < num_group_faces; l++) {
320 size_t pos = (*group_faces)[l];
321
322 if (pos >= polygon_to_shader.size()) {
323 continue;
324 }
325
326 polygon_to_shader[pos] = pair.shader_index;
327 }
328 }
329
330 return polygon_to_shader;
331}
332
333static void read_poly_mesh_geometry(CachedData &cached_data,
334 const PolyMeshSchemaData &data,
335 chrono_t time)
336{
337 const ISampleSelector iss = ISampleSelector(time);
338
339 add_positions(data.positions.getValue(iss), time, cached_data);
340
341 const Int32ArraySamplePtr face_counts = data.face_counts.getValue(iss);
342 const Int32ArraySamplePtr face_indices = data.face_indices.getValue(iss);
343
344 /* Only copy triangles for other frames if the topology is changing over time as well. */
345 if (data.topology_variance != kHomogeneousTopology || cached_data.triangles.size() == 0) {
346 bool do_triangles = true;
347
348 /* Compare key with last one to check whether the topology changed. */
349 if (cached_data.triangles.size() > 0) {
350 const ArraySample::Key key = face_indices->getKey();
351
352 if (key == cached_data.triangles.key1) {
353 do_triangles = false;
354 }
355
356 cached_data.triangles.key1 = key;
357 }
358
359 if (do_triangles) {
360 const array<int> polygon_to_shader = compute_polygon_to_shader_map(
361 face_counts, data.shader_face_sets, iss);
362 add_triangles(face_counts, face_indices, time, cached_data, polygon_to_shader);
363 }
364 else {
365 cached_data.triangles.reuse_data_for_last_time(time);
366 cached_data.uv_loops.reuse_data_for_last_time(time);
367 cached_data.shader.reuse_data_for_last_time(time);
368 }
369
370 /* Initialize the first key. */
371 if (data.topology_variance != kHomogeneousTopology && cached_data.triangles.size() == 1) {
372 cached_data.triangles.key1 = face_indices->getKey();
373 }
374 }
375
376 if (data.normals.valid()) {
377 add_normals(face_indices, data.normals, time, cached_data);
378 }
379 else {
380 compute_vertex_normals(cached_data, time);
381 }
382}
383
384void read_geometry_data(AlembicProcedural *proc,
385 CachedData &cached_data,
386 const PolyMeshSchemaData &data,
387 Progress &progress)
388{
389 read_data_loop(proc, cached_data, data, read_poly_mesh_geometry, progress);
390}
391
392/* Subdivision Geometries */
393
394static void add_subd_polygons(CachedData &cached_data, const SubDSchemaData &data, chrono_t time)
395{
396 const ISampleSelector iss = ISampleSelector(time);
397
398 const Int32ArraySamplePtr face_counts = data.face_counts.getValue(iss);
399 const Int32ArraySamplePtr face_indices = data.face_indices.getValue(iss);
400
401 array<int> subd_start_corner;
402 array<int> shader;
403 array<int> subd_num_corners;
404 array<bool> subd_smooth;
405 array<int> subd_ptex_offset;
406 array<int> subd_face_corners;
407 array<int> uv_loops;
408
409 const size_t num_faces = face_counts->size();
410 const int *face_counts_array = face_counts->get();
411 const int *face_indices_array = face_indices->get();
412
413 int num_ngons = 0;
414 int num_corners = 0;
415 for (size_t i = 0; i < face_counts->size(); i++) {
416 num_ngons += (face_counts_array[i] == 4 ? 0 : 1);
417 num_corners += face_counts_array[i];
418 }
419
420 subd_start_corner.reserve(num_faces);
421 subd_num_corners.reserve(num_faces);
422 subd_smooth.reserve(num_faces);
423 subd_ptex_offset.reserve(num_faces);
424 shader.reserve(num_faces);
425 subd_face_corners.reserve(num_corners);
426 uv_loops.reserve(num_corners);
427
428 int start_corner = 0;
429 int current_shader = 0;
430 int ptex_offset = 0;
431
432 const array<int> polygon_to_shader = compute_polygon_to_shader_map(
433 face_counts, data.shader_face_sets, iss);
434
435 for (size_t i = 0; i < face_counts->size(); i++) {
436 num_corners = face_counts_array[i];
437
438 if (!polygon_to_shader.empty()) {
439 current_shader = polygon_to_shader[i];
440 }
441
442 subd_start_corner.push_back_reserved(start_corner);
443 subd_num_corners.push_back_reserved(num_corners);
444
445 for (int j = 0; j < num_corners; ++j) {
446 subd_face_corners.push_back_reserved(face_indices_array[start_corner + j]);
447 uv_loops.push_back_reserved(start_corner + j);
448 }
449
450 shader.push_back_reserved(current_shader);
451 subd_smooth.push_back_reserved(1);
452 subd_ptex_offset.push_back_reserved(ptex_offset);
453
454 ptex_offset += (num_corners == 4 ? 1 : num_corners);
455
456 start_corner += num_corners;
457 }
458
459 cached_data.shader.add_data(shader, time);
460 cached_data.subd_start_corner.add_data(subd_start_corner, time);
461 cached_data.subd_num_corners.add_data(subd_num_corners, time);
462 cached_data.subd_smooth.add_data(subd_smooth, time);
463 cached_data.subd_ptex_offset.add_data(subd_ptex_offset, time);
464 cached_data.subd_face_corners.add_data(subd_face_corners, time);
465 cached_data.num_ngons.add_data(num_ngons, time);
466 cached_data.uv_loops.add_data(uv_loops, time);
467}
468
469static void add_subd_edge_creases(CachedData &cached_data,
470 const SubDSchemaData &data,
471 chrono_t time)
472{
473 if (!(data.crease_indices.valid() && data.crease_lengths.valid() &&
474 data.crease_sharpnesses.valid()))
475 {
476 return;
477 }
478
479 const ISampleSelector iss = ISampleSelector(time);
480
481 const Int32ArraySamplePtr creases_length = data.crease_lengths.getValue(iss);
482 const Int32ArraySamplePtr creases_indices = data.crease_indices.getValue(iss);
483 const FloatArraySamplePtr creases_sharpnesses = data.crease_sharpnesses.getValue(iss);
484
485 if (creases_length && creases_indices && creases_sharpnesses) {
486 array<int> creases_edge;
487 array<float> creases_weight;
488
489 creases_edge.reserve(creases_sharpnesses->size() * 2);
490 creases_weight.reserve(creases_sharpnesses->size());
491
492 int length_offset = 0;
493 int weight_offset = 0;
494 for (size_t c = 0; c < creases_length->size(); ++c) {
495 const int crease_length = creases_length->get()[c];
496
497 for (size_t j = 0; j < crease_length - 1; ++j) {
498 creases_edge.push_back_reserved(creases_indices->get()[length_offset + j]);
499 creases_edge.push_back_reserved(creases_indices->get()[length_offset + j + 1]);
500 creases_weight.push_back_reserved(creases_sharpnesses->get()[weight_offset++]);
501 }
502
503 length_offset += crease_length;
504 }
505
506 cached_data.subd_creases_edge.add_data(creases_edge, time);
507 cached_data.subd_creases_weight.add_data(creases_weight, time);
508 }
509}
510
511static void add_subd_vertex_creases(CachedData &cached_data,
512 const SubDSchemaData &data,
513 chrono_t time)
514{
515 if (!(data.corner_indices.valid() && data.crease_sharpnesses.valid())) {
516 return;
517 }
518
519 const ISampleSelector iss = ISampleSelector(time);
520 const Int32ArraySamplePtr creases_indices = data.crease_indices.getValue(iss);
521 const FloatArraySamplePtr creases_sharpnesses = data.crease_sharpnesses.getValue(iss);
522
523 if (!(creases_indices && creases_sharpnesses) ||
524 creases_indices->size() != creases_sharpnesses->size())
525 {
526 return;
527 }
528
529 array<float> sharpnesses;
530 sharpnesses.reserve(creases_indices->size());
532 indices.reserve(creases_indices->size());
533
534 for (size_t i = 0; i < creases_indices->size(); i++) {
535 indices.push_back_reserved((*creases_indices)[i]);
536 sharpnesses.push_back_reserved((*creases_sharpnesses)[i]);
537 }
538
539 cached_data.subd_vertex_crease_indices.add_data(indices, time);
540 cached_data.subd_vertex_crease_weights.add_data(sharpnesses, time);
541}
542
543static void read_subd_geometry(CachedData &cached_data, const SubDSchemaData &data, chrono_t time)
544{
545 const ISampleSelector iss = ISampleSelector(time);
546
547 add_positions(data.positions.getValue(iss), time, cached_data);
548
549 if (data.topology_variance != kHomogeneousTopology || cached_data.shader.size() == 0) {
550 add_subd_polygons(cached_data, data, time);
551 add_subd_edge_creases(cached_data, data, time);
552 add_subd_vertex_creases(cached_data, data, time);
553 }
554}
555
556void read_geometry_data(AlembicProcedural *proc,
557 CachedData &cached_data,
558 const SubDSchemaData &data,
559 Progress &progress)
560{
561 read_data_loop(proc, cached_data, data, read_subd_geometry, progress);
562}
563
564/* Curve Geometries. */
565
566static void read_curves_data(CachedData &cached_data, const CurvesSchemaData &data, chrono_t time)
567{
568 const ISampleSelector iss = ISampleSelector(time);
569
570 const Int32ArraySamplePtr curves_num_vertices = data.num_vertices.getValue(iss);
571 const P3fArraySamplePtr position = data.positions.getValue(iss);
572
573 FloatArraySamplePtr radiuses;
574
575 if (data.widths.valid()) {
576 IFloatGeomParam::Sample wsample = data.widths.getExpandedValue(iss);
577 radiuses = wsample.getVals();
578 }
579
580 const bool do_radius = (radiuses != nullptr) && (radiuses->size() > 1);
581 float radius = (radiuses && radiuses->size() == 1) ? (*radiuses)[0] : data.default_radius;
582
583 array<float3> curve_keys;
584 array<float> curve_radius;
585 array<int> curve_first_key;
586 array<int> curve_shader;
587
588 const bool is_homogeneous = data.topology_variance == kHomogeneousTopology;
589
590 curve_keys.reserve(position->size());
591 curve_radius.reserve(position->size());
592 curve_first_key.reserve(curves_num_vertices->size());
593 curve_shader.reserve(curves_num_vertices->size());
594
595 int offset = 0;
596 for (size_t i = 0; i < curves_num_vertices->size(); i++) {
597 const int num_vertices = curves_num_vertices->get()[i];
598
599 for (int j = 0; j < num_vertices; j++) {
600 const V3f &f = position->get()[offset + j];
601 // todo(@kevindietrich): we are reading too much data?
602 curve_keys.push_back_slow(make_float3_from_yup(f));
603
604 if (do_radius) {
605 radius = (*radiuses)[offset + j];
606 }
607
608 curve_radius.push_back_slow(radius * data.radius_scale);
609 }
610
611 if (!is_homogeneous || cached_data.curve_first_key.size() == 0) {
612 curve_first_key.push_back_reserved(offset);
613 curve_shader.push_back_reserved(0);
614 }
615
616 offset += num_vertices;
617 }
618
619 cached_data.curve_keys.add_data(curve_keys, time);
620 cached_data.curve_radius.add_data(curve_radius, time);
621
622 if (!is_homogeneous || cached_data.curve_first_key.size() == 0) {
623 cached_data.curve_first_key.add_data(curve_first_key, time);
624 cached_data.curve_shader.add_data(curve_shader, time);
625 }
626}
627
628void read_geometry_data(AlembicProcedural *proc,
629 CachedData &cached_data,
630 const CurvesSchemaData &data,
631 Progress &progress)
632{
633 read_data_loop(proc, cached_data, data, read_curves_data, progress);
634}
635
636/* Points Geometries. */
637
638static void read_points_data(CachedData &cached_data, const PointsSchemaData &data, chrono_t time)
639{
640 const ISampleSelector iss = ISampleSelector(time);
641
642 const P3fArraySamplePtr position = data.positions.getValue(iss);
643 FloatArraySamplePtr radiuses;
644
645 array<float3> a_positions;
646 array<float> a_radius;
647 array<int> a_shader;
648 a_positions.reserve(position->size());
649 a_radius.reserve(position->size());
650 a_shader.reserve(position->size());
651
652 if (data.radiuses.valid()) {
653 IFloatGeomParam::Sample wsample = data.radiuses.getExpandedValue(iss);
654 radiuses = wsample.getVals();
655 }
656
657 const bool do_radius = (radiuses != nullptr) && (radiuses->size() > 1);
658 float radius = (radiuses && radiuses->size() == 1) ? (*radiuses)[0] : data.default_radius;
659
660 int offset = 0;
661 for (size_t i = 0; i < position->size(); i++) {
662 const V3f &f = position->get()[offset + i];
663 a_positions.push_back_slow(make_float3_from_yup(f));
664
665 if (do_radius) {
666 radius = (*radiuses)[offset + i];
667 }
668 a_radius.push_back_slow(radius * data.radius_scale);
669
670 a_shader.push_back_slow((int)0);
671 }
672
673 cached_data.points.add_data(a_positions, time);
674 cached_data.radiuses.add_data(a_radius, time);
675 cached_data.points_shader.add_data(a_shader, time);
676}
677
678void read_geometry_data(AlembicProcedural *proc,
679 CachedData &cached_data,
680 const PointsSchemaData &data,
681 Progress &progress)
682{
683 read_data_loop(proc, cached_data, data, read_points_data, progress);
684}
685/* Attributes conversions. */
686
687/* Type traits for converting between Alembic and Cycles types.
688 */
689
690template<typename T> struct value_type_converter {
691 using cycles_type = float;
692 /* Use `TypeDesc::FLOAT` instead of `TypeFloat` to work around a compiler bug in gcc 11. */
693 static constexpr TypeDesc type_desc = TypeDesc::FLOAT;
694 static constexpr const char *type_name = "float (default)";
695
696 static cycles_type convert_value(T value)
697 {
698 return static_cast<float>(value);
699 }
700};
701
702template<> struct value_type_converter<Imath::V2f> {
703 using cycles_type = float2;
704 static constexpr TypeDesc type_desc = TypeFloat2;
705 static constexpr const char *type_name = "float2";
706
707 static cycles_type convert_value(Imath::V2f value)
708 {
709 return make_float2(value.x, value.y);
710 }
711};
712
713template<> struct value_type_converter<Imath::V3f> {
714 using cycles_type = float3;
715 static constexpr TypeDesc type_desc = TypeVector;
716 static constexpr const char *type_name = "float3";
717
718 static cycles_type convert_value(Imath::V3f value)
719 {
720 return make_float3_from_yup(value);
721 }
722};
723
724template<> struct value_type_converter<Imath::C3f> {
725 using cycles_type = uchar4;
726 static constexpr TypeDesc type_desc = TypeRGBA;
727 static constexpr const char *type_name = "rgb";
728
729 static cycles_type convert_value(Imath::C3f value)
730 {
731 return color_float_to_byte(make_float3(value.x, value.y, value.z));
732 }
733};
734
735template<> struct value_type_converter<Imath::C4f> {
736 using cycles_type = uchar4;
737 static constexpr TypeDesc type_desc = TypeRGBA;
738 static constexpr const char *type_name = "rgba";
739
740 static cycles_type convert_value(Imath::C4f value)
741 {
742 return color_float4_to_uchar4(make_float4(value.r, value.g, value.b, value.a));
743 }
744};
745
746/* Main function used to read attributes of any type. */
747template<typename TRAIT>
748static void process_attribute(CachedData &cache,
749 CachedData::CachedAttribute &attribute,
750 GeometryScope scope,
751 const typename ITypedGeomParam<TRAIT>::Sample &sample,
752 double time)
753{
754 using abc_type = typename TRAIT::value_type;
755 using cycles_type = typename value_type_converter<abc_type>::cycles_type;
756
757 const TypedArraySample<TRAIT> &values = *sample.getVals();
758
759 switch (scope) {
760 case kConstantScope:
761 case kVertexScope: {
762 const array<float3> *vertices =
763 cache.vertices.data_for_time_no_check(time).get_data_or_null();
764
765 if (!vertices) {
766 attribute.data.add_no_data(time);
767 return;
768 }
769
770 if (vertices->size() != values.size()) {
771 attribute.data.add_no_data(time);
772 return;
773 }
774
775 array<char> data(vertices->size() * sizeof(cycles_type));
776
777 cycles_type *pod_typed_data = reinterpret_cast<cycles_type *>(data.data());
778
779 for (size_t i = 0; i < values.size(); ++i) {
780 *pod_typed_data++ = value_type_converter<abc_type>::convert_value(values[i]);
781 }
782
783 attribute.data.add_data(data, time);
784 break;
785 }
786 case kVaryingScope: {
787 const array<int3> *triangles =
788 cache.triangles.data_for_time_no_check(time).get_data_or_null();
789
790 if (!triangles) {
791 attribute.data.add_no_data(time);
792 return;
793 }
794
795 array<char> data(triangles->size() * 3 * sizeof(cycles_type));
796
797 cycles_type *pod_typed_data = reinterpret_cast<cycles_type *>(data.data());
798
799 for (const int3 &tri : *triangles) {
800 *pod_typed_data++ = value_type_converter<abc_type>::convert_value(values[tri.x]);
801 *pod_typed_data++ = value_type_converter<abc_type>::convert_value(values[tri.y]);
802 *pod_typed_data++ = value_type_converter<abc_type>::convert_value(values[tri.z]);
803 }
804
805 attribute.data.add_data(data, time);
806 break;
807 }
808 default: {
809 break;
810 }
811 }
812}
813
814/* UVs are processed separately as their indexing is based on loops, instead of vertices or
815 * corners. */
816static void process_uvs(CachedData &cache,
817 CachedData::CachedAttribute &attribute,
818 GeometryScope scope,
819 const IV2fGeomParam::Sample &sample,
820 double time)
821{
822 if (scope != kFacevaryingScope && scope != kVaryingScope && scope != kVertexScope) {
823 return;
824 }
825
826 const array<int> *uv_loops = cache.uv_loops.data_for_time_no_check(time).get_data_or_null();
827
828 /* It's ok to not have loop indices, as long as the scope is not face-varying. */
829 if (!uv_loops && scope == kFacevaryingScope) {
830 return;
831 }
832
833 const array<int3> *triangles = cache.triangles.data_for_time_no_check(time).get_data_or_null();
834 const array<int> *corners =
835 cache.subd_face_corners.data_for_time_no_check(time).get_data_or_null();
836
838 if (triangles) {
839 data.resize(triangles->size() * 3 * sizeof(float2));
840 }
841 else if (corners) {
842 data.resize(corners->size() * sizeof(float2));
843 }
844 else {
845 return;
846 }
847
848 float2 *data_float2 = reinterpret_cast<float2 *>(data.data());
849
850 const uint32_t *indices = sample.getIndices()->get();
851 const V2f *values = sample.getVals()->get();
852
853 if (scope == kFacevaryingScope) {
854 for (const int uv_loop_index : *uv_loops) {
855 const uint32_t index = indices[uv_loop_index];
856 *data_float2++ = make_float2(values[index][0], values[index][1]);
857 }
858 }
859 else if (scope == kVaryingScope || scope == kVertexScope) {
860 if (triangles) {
861 for (size_t i = 0; i < triangles->size(); i++) {
862 const int3 t = (*triangles)[i];
863 *data_float2++ = make_float2(values[t.x][0], values[t.x][1]);
864 *data_float2++ = make_float2(values[t.y][0], values[t.y][1]);
865 *data_float2++ = make_float2(values[t.z][0], values[t.z][1]);
866 }
867 }
868 else if (corners) {
869 for (size_t i = 0; i < corners->size(); i++) {
870 const int c = (*corners)[i];
871 *data_float2++ = make_float2(values[c][0], values[c][1]);
872 }
873 }
874 }
875
876 attribute.data.add_data(data, time);
877}
878
879/* Type of the function used to parse one time worth of data, either process_uvs or
880 * process_attribute_generic. */
881template<typename TRAIT>
882using process_callback_type = void (*)(CachedData &,
883 CachedData::CachedAttribute &,
884 GeometryScope,
885 const typename ITypedGeomParam<TRAIT>::Sample &,
886 double);
887
888/* Main loop to process the attributes, this will look at the given param's TimeSampling and
889 * extract data based on which frame time is requested by the procedural and execute the callback
890 * for each of those requested time. */
891template<typename TRAIT>
892static void read_attribute_loop(AlembicProcedural *proc,
893 CachedData &cache,
894 const ITypedGeomParam<TRAIT> &param,
895 process_callback_type<TRAIT> callback,
896 Progress &progress,
898{
899 const std::set<chrono_t> times = get_relevant_sample_times(
900 proc, *param.getTimeSampling(), param.getNumSamples());
901
902 if (times.empty()) {
903 return;
904 }
905
906 std::string name = param.getName();
907
908 if (std == ATTR_STD_UV) {
909 std::string uv_source_name = Alembic::Abc::GetSourceName(param.getMetaData());
910
911 /* According to the convention, primary UVs should have had their name
912 * set using Alembic::Abc::SetSourceName, but you can't expect everyone
913 * to follow it! :) */
914 if (!uv_source_name.empty()) {
915 name = uv_source_name;
916 }
917 }
918
919 CachedData::CachedAttribute &attribute = cache.add_attribute(ustring(name),
920 *param.getTimeSampling());
921
922 using abc_type = typename TRAIT::value_type;
923
924 attribute.data.set_time_sampling(*param.getTimeSampling());
925 attribute.std = std;
926 attribute.type_desc = value_type_converter<abc_type>::type_desc;
927
928 if (attribute.type_desc == TypeRGBA) {
930 }
931 else {
932 if (param.getScope() == kVaryingScope || param.getScope() == kFacevaryingScope) {
934 }
935 else {
937 }
938 }
939
940 for (const chrono_t time : times) {
941 if (progress.get_cancel()) {
942 return;
943 }
944
945 ISampleSelector iss = ISampleSelector(time);
946 typename ITypedGeomParam<TRAIT>::Sample sample;
947 param.getIndexed(sample, iss);
948
949 if (!sample.valid()) {
950 continue;
951 }
952
953 if (!sample.getVals()) {
954 attribute.data.add_no_data(time);
955 continue;
956 }
957
958 /* Check whether we already loaded constant data. */
959 if (attribute.data.size() != 0) {
960 if (param.isConstant()) {
961 return;
962 }
963
964 const ArraySample::Key indices_key = sample.getIndices()->getKey();
965 const ArraySample::Key values_key = sample.getVals()->getKey();
966
967 const bool is_same_as_last_time = (indices_key == attribute.data.key1 &&
968 values_key == attribute.data.key2);
969
970 attribute.data.key1 = indices_key;
971 attribute.data.key2 = values_key;
972
973 if (is_same_as_last_time) {
974 attribute.data.reuse_data_for_last_time(time);
975 continue;
976 }
977 }
978
979 callback(cache, attribute, param.getScope(), sample, time);
980 }
981}
982
983/* Attributes requests. */
984
985/* This structure is used to tell which ICoumpoundProperty the PropertyHeader comes from, as we
986 * need the parent when downcasting to the proper type. */
987struct PropHeaderAndParent {
988 const PropertyHeader *prop;
989 ICompoundProperty parent;
990};
991
992/* Parse the ICompoundProperty to look for properties whose names appear in the
993 * AttributeRequestSet. This also looks into any child ICompoundProperty of the given
994 * ICompoundProperty. If no property of the given name is found, let it be that way, Cycles will
995 * use a zero value for the missing attribute. */
996static void parse_requested_attributes_recursive(const AttributeRequestSet &requested_attributes,
997 const ICompoundProperty &arb_geom_params,
998 vector<PropHeaderAndParent> &requested_properties)
999{
1000 if (!arb_geom_params.valid()) {
1001 return;
1002 }
1003
1004 for (const AttributeRequest &req : requested_attributes.requests) {
1005 const PropertyHeader *property_header = arb_geom_params.getPropertyHeader(req.name.c_str());
1006
1007 if (!property_header) {
1008 continue;
1009 }
1010
1011 requested_properties.push_back({property_header, arb_geom_params});
1012 }
1013
1014 /* Look into children compound properties. */
1015 for (size_t i = 0; i < arb_geom_params.getNumProperties(); ++i) {
1016 const PropertyHeader &property_header = arb_geom_params.getPropertyHeader(i);
1017
1018 if (property_header.isCompound()) {
1019 ICompoundProperty compound_property = ICompoundProperty(arb_geom_params,
1020 property_header.getName());
1021 parse_requested_attributes_recursive(
1022 requested_attributes, compound_property, requested_properties);
1023 }
1024 }
1025}
1026
1027/* Main entry point for parsing requested attributes from an ICompoundProperty, this exists so that
1028 * we can simply return the list of properties instead of allocating it on the stack and passing it
1029 * as a parameter. */
1030static vector<PropHeaderAndParent> parse_requested_attributes(
1031 const AttributeRequestSet &requested_attributes, const ICompoundProperty &arb_geom_params)
1032{
1033 vector<PropHeaderAndParent> requested_properties;
1034 parse_requested_attributes_recursive(
1035 requested_attributes, arb_geom_params, requested_properties);
1036 return requested_properties;
1037}
1038
1039/* Read the attributes requested by the shaders from the archive. This will recursively find named
1040 * attributes from the AttributeRequestSet in the ICompoundProperty and any of its compound child.
1041 * The attributes are added to the CachedData's attribute list. For each attribute we will try to
1042 * deduplicate data across consecutive frames. */
1043void read_attributes(AlembicProcedural *proc,
1044 CachedData &cache,
1045 const ICompoundProperty &arb_geom_params,
1046 const IV2fGeomParam &default_uvs_param,
1047 const AttributeRequestSet &requested_attributes,
1048 Progress &progress)
1049{
1050 if (default_uvs_param.valid()) {
1051 /* Only the default UVs should be treated as the standard UV attribute. */
1052 read_attribute_loop(proc, cache, default_uvs_param, process_uvs, progress, ATTR_STD_UV);
1053 }
1054
1055 vector<PropHeaderAndParent> requested_properties = parse_requested_attributes(
1056 requested_attributes, arb_geom_params);
1057
1058 for (const PropHeaderAndParent &prop_and_parent : requested_properties) {
1059 if (progress.get_cancel()) {
1060 return;
1061 }
1062
1063 const PropertyHeader *prop = prop_and_parent.prop;
1064 const ICompoundProperty &parent = prop_and_parent.parent;
1065
1066 if (IBoolGeomParam::matches(*prop)) {
1067 const IBoolGeomParam &param = IBoolGeomParam(parent, prop->getName());
1068 read_attribute_loop(proc, cache, param, process_attribute<BooleanTPTraits>, progress);
1069 }
1070 else if (IInt32GeomParam::matches(*prop)) {
1071 const IInt32GeomParam &param = IInt32GeomParam(parent, prop->getName());
1072 read_attribute_loop(proc, cache, param, process_attribute<Int32TPTraits>, progress);
1073 }
1074 else if (IFloatGeomParam::matches(*prop)) {
1075 const IFloatGeomParam &param = IFloatGeomParam(parent, prop->getName());
1076 read_attribute_loop(proc, cache, param, process_attribute<Float32TPTraits>, progress);
1077 }
1078 else if (IV2fGeomParam::matches(*prop)) {
1079 const IV2fGeomParam &param = IV2fGeomParam(parent, prop->getName());
1080 if (Alembic::AbcGeom::isUV(*prop)) {
1081 read_attribute_loop(proc, cache, param, process_uvs, progress);
1082 }
1083 else {
1084 read_attribute_loop(proc, cache, param, process_attribute<V2fTPTraits>, progress);
1085 }
1086 }
1087 else if (IV3fGeomParam::matches(*prop)) {
1088 const IV3fGeomParam &param = IV3fGeomParam(parent, prop->getName());
1089 read_attribute_loop(proc, cache, param, process_attribute<V3fTPTraits>, progress);
1090 }
1091 else if (IN3fGeomParam::matches(*prop)) {
1092 const IN3fGeomParam &param = IN3fGeomParam(parent, prop->getName());
1093 read_attribute_loop(proc, cache, param, process_attribute<N3fTPTraits>, progress);
1094 }
1095 else if (IC3fGeomParam::matches(*prop)) {
1096 const IC3fGeomParam &param = IC3fGeomParam(parent, prop->getName());
1097 read_attribute_loop(proc, cache, param, process_attribute<C3fTPTraits>, progress);
1098 }
1099 else if (IC4fGeomParam::matches(*prop)) {
1100 const IC4fGeomParam &param = IC4fGeomParam(parent, prop->getName());
1101 read_attribute_loop(proc, cache, param, process_attribute<C4fTPTraits>, progress);
1102 }
1103 }
1104
1105 cache.invalidate_last_loaded_time(true);
1106}
1107
1109
1110#endif
typedef double(DMatrix)[4][4]
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 Retrieve a color attribute
ATTR_WARN_UNUSED_RESULT const BMVert * v2
ATTR_WARN_UNUSED_RESULT const BMLoop * l
ATTR_WARN_UNUSED_RESULT const BMVert * v
SIMD_FORCE_INLINE btVector3 & normalize()
Normalize this vector x^2 + y^2 + z^2 = 1.
Definition btVector3.h:303
VecBase< float, 3 > float3
vector< AttributeRequest > requests
bool get_cancel() const
Definition progress.h:93
size_t size() const
size_t empty() const
void push_back_reserved(const T &t)
void reserve(size_t newcapacity)
void push_back_slow(const T &t)
VecBase< float, 2 > float2
#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_forceinline float2 make_float2(const float x, const float y)
ccl_device_forceinline int3 make_int3(const int x, const int y, const int z)
draw_view in_light_buf[] float
static ushort indices[]
static float normals[][3]
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
AttributeStandard
@ ATTR_STD_UV
@ ATTR_STD_VERTEX_NORMAL
@ ATTR_STD_NONE
@ ATTR_ELEMENT_CORNER_BYTE
@ ATTR_ELEMENT_CORNER
@ ATTR_ELEMENT_VERTEX
#define T
To convert_value(const From value)
void index(const bNode &, void *r_value)
void position(const bNode &, void *r_value)
static constexpr TypeDesc TypeRGBA(TypeDesc::FLOAT, TypeDesc::VEC4, TypeDesc::COLOR)
CCL_NAMESPACE_BEGIN static OIIO_NAMESPACE_USING constexpr TypeDesc TypeFloat2(TypeDesc::FLOAT, TypeDesc::VEC2)
blender::VecBase< uint8_t, 4 > uchar4
long long TypeDesc
unsigned int uint32_t
Definition stdint.h:80
float3 compute_normal(const float3 *verts) const
ccl_device uchar4 color_float4_to_uchar4(float4 c)
Definition util/color.h:39
ccl_device uchar4 color_float_to_byte(float3 c)
Definition util/color.h:28