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