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) {
929 attribute.element = ATTR_ELEMENT_CORNER_BYTE;
930 }
931 else {
932 if (param.getScope() == kVaryingScope || param.getScope() == kFacevaryingScope) {
933 attribute.element = ATTR_ELEMENT_CORNER;
934 }
935 else {
936 attribute.element = ATTR_ELEMENT_VERTEX;
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]
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
bool get_cancel() const
Definition progress.h:93
T * resize(size_t newsize)
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)
DEGForeachIDComponentCallback callback
#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[]
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
To convert_value(const From 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)
long long TypeDesc
unsigned int uint32_t
Definition stdint.h:80
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