Blender V4.3
abc_archive.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2020 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "abc_archive.h"
6
8#include "BKE_main.hh"
9
10#include "DNA_scene_types.h"
11
12#include <Alembic/Abc/ArchiveInfo.h>
13#include <Alembic/Abc/ErrorHandler.h>
14#include <Alembic/Abc/Foundation.h>
15#include <Alembic/Abc/OArchive.h>
16#include <Alembic/AbcCoreAbstract/MetaData.h>
17#include <Alembic/AbcCoreAbstract/TimeSampling.h>
18#include <Alembic/AbcCoreAbstract/TimeSamplingType.h>
19#include <Alembic/AbcCoreOgawa/ReadWrite.h>
20#include <Alembic/AbcGeom/ArchiveBounds.h>
21
22#ifdef WIN32
23# include "BLI_path_utils.hh"
24# include "BLI_string.h"
25
26# include "utfconv.hh"
27#endif
28
30
31using Alembic::Abc::ErrorHandler;
32using Alembic::Abc::kWrapExisting;
33using Alembic::Abc::MetaData;
34using Alembic::Abc::OArchive;
35using Alembic::Abc::TimeSampling;
36using Alembic::Abc::TimeSamplingPtr;
37using Alembic::Abc::TimeSamplingType;
38
39static MetaData create_abc_metadata(const Main *bmain, double scene_fps)
40{
41 MetaData abc_metadata;
42
43 std::string abc_user_description(bmain->filepath);
44 if (abc_user_description.empty()) {
45 abc_user_description = "unknown";
46 }
47
48 abc_metadata.set(Alembic::Abc::kApplicationNameKey, "Blender");
49 abc_metadata.set(Alembic::Abc::kUserDescriptionKey, abc_user_description);
50 abc_metadata.set("blender_version", std::string("v") + BKE_blender_version_string());
51 abc_metadata.set("FramesPerTimeUnit", std::to_string(scene_fps));
52
53 time_t raw_time;
54 time(&raw_time);
55 char buffer[128];
56
57#if defined _WIN32 || defined _WIN64
58 ctime_s(buffer, 128, &raw_time);
59#else
60 ctime_r(&raw_time, buffer);
61#endif
62
63 const std::size_t buffer_len = strlen(buffer);
64 if (buffer_len > 0 && buffer[buffer_len - 1] == '\n') {
65 buffer[buffer_len - 1] = '\0';
66 }
67
68 abc_metadata.set(Alembic::Abc::kDateWrittenKey, buffer);
69 return abc_metadata;
70}
71
72static OArchive *create_archive(std::ofstream *abc_ostream,
73 const std::string &filepath,
74 MetaData &abc_metadata)
75{
76 /* Use stream to support unicode character paths on Windows. */
77#ifdef WIN32
78 char filepath_cstr[FILE_MAX];
79 BLI_strncpy(filepath_cstr, filepath.c_str(), FILE_MAX);
80
81 UTF16_ENCODE(filepath_cstr);
82 std::wstring wstr(filepath_cstr_16);
83 abc_ostream->open(wstr.c_str(), std::ios::out | std::ios::binary);
84 UTF16_UN_ENCODE(filepath_cstr);
85#else
86 abc_ostream->open(filepath, std::ios::out | std::ios::binary);
87#endif
88
89 ErrorHandler::Policy policy = ErrorHandler::kThrowPolicy;
90
91 Alembic::AbcCoreOgawa::WriteArchive archive_writer;
92 return new OArchive(archive_writer(abc_ostream, abc_metadata), kWrapExisting, policy);
93}
94
95/* Construct list of shutter samples.
96 *
97 * These are taken from the interval [shutter open, shutter close),
98 * uniformly sampled with 'nr_of_samples' samples.
99 *
100 * TODO(Sybren): test that the above interval is indeed half-open.
101 *
102 * If 'time_relative' is true, samples are returned as time (in seconds) from params.frame_start.
103 * If 'time_relative' is false, samples are returned as fractional frames from 0.
104 */
105static void get_shutter_samples(double scene_fps,
107 int nr_of_samples,
108 bool time_relative,
109 std::vector<double> &r_samples)
110{
111 double frame_offset = time_relative ? params.frame_start : 0.0;
112 double time_factor = time_relative ? scene_fps : 1.0;
113 double shutter_open = params.shutter_open;
114 double shutter_close = params.shutter_close;
115 double time_inc = (shutter_close - shutter_open) / nr_of_samples;
116
117 /* sample between shutter open & close */
118 for (int sample = 0; sample < nr_of_samples; sample++) {
119 double sample_time = shutter_open + time_inc * sample;
120 double time = (frame_offset + sample_time) / time_factor;
121
122 r_samples.push_back(time);
123 }
124}
125
126static TimeSamplingPtr create_time_sampling(double scene_fps,
128 int nr_of_samples)
129{
130 std::vector<double> samples;
131
132 if (params.frame_start == params.frame_end) {
133 return TimeSamplingPtr(new TimeSampling()); // NOLINT: modernize-make-shared
134 }
135
136 get_shutter_samples(scene_fps, params, nr_of_samples, true, samples);
137
138 TimeSamplingType ts(uint32_t(samples.size()), 1.0 / scene_fps);
139 return TimeSamplingPtr(new TimeSampling(ts, samples)); // NOLINT: modernize-make-shared
140}
141
142static void get_frames(double scene_fps,
144 uint nr_of_samples,
145 std::set<double> &r_frames)
146{
147 /* Get one set of shutter samples, then add those around each frame to export. */
148 std::vector<double> shutter_samples;
149 get_shutter_samples(scene_fps, params, nr_of_samples, false, shutter_samples);
150
151 for (double frame = params.frame_start; frame <= params.frame_end; frame += 1.0) {
152 for (size_t j = 0; j < nr_of_samples; j++) {
153 r_frames.insert(frame + shutter_samples[j]);
154 }
155 }
156}
157
158/* ****************************************************************** */
159
161 const Scene *scene,
163 const std::string &filepath)
164 : archive(nullptr)
165{
166 double scene_fps = FPS;
167 MetaData abc_metadata = create_abc_metadata(bmain, scene_fps);
168
169 /* Create the Archive. */
170 archive = create_archive(&abc_ostream_, filepath, abc_metadata);
171
172 /* Create time samples for transforms and shapes. */
173 TimeSamplingPtr ts_xform;
174 TimeSamplingPtr ts_shapes;
175
176 ts_xform = create_time_sampling(scene_fps, params, params.frame_samples_xform);
177 time_sampling_index_transforms_ = archive->addTimeSampling(*ts_xform);
178
179 const bool export_animation = params.frame_start != params.frame_end;
180 if (!export_animation || params.frame_samples_shape == params.frame_samples_xform) {
181 ts_shapes = ts_xform;
182 time_sampling_index_shapes_ = time_sampling_index_transforms_;
183 }
184 else {
185 ts_shapes = create_time_sampling(scene_fps, params, params.frame_samples_shape);
186 time_sampling_index_shapes_ = archive->addTimeSampling(*ts_shapes);
187 }
188
189 /* Construct the frames to export. */
190 get_frames(scene_fps, params, params.frame_samples_xform, xform_frames_);
191 get_frames(scene_fps, params, params.frame_samples_shape, shape_frames_);
192
193 /* Merge all frames to get the final set of frames to export. */
194 export_frames_.insert(xform_frames_.begin(), xform_frames_.end());
195 export_frames_.insert(shape_frames_.begin(), shape_frames_.end());
196
197 abc_archive_bbox_ = Alembic::AbcGeom::CreateOArchiveBounds(*archive,
198 time_sampling_index_transforms_);
199}
200
202{
203 delete archive;
204}
205
207{
208 return time_sampling_index_transforms_;
209}
210
212{
213 return time_sampling_index_shapes_;
214}
215
216ABCArchive::Frames::const_iterator ABCArchive::frames_begin() const
217{
218 return export_frames_.begin();
219}
220ABCArchive::Frames::const_iterator ABCArchive::frames_end() const
221{
222 return export_frames_.end();
223}
225{
226 return export_frames_.size();
227}
228
229bool ABCArchive::is_xform_frame(double frame) const
230{
231 return xform_frames_.find(frame) != xform_frames_.end();
232}
233bool ABCArchive::is_shape_frame(double frame) const
234{
235 return shape_frames_.find(frame) != shape_frames_.end();
236}
238{
239 ExportSubset subset;
240 subset.transforms = is_xform_frame(frame);
241 subset.shapes = is_shape_frame(frame);
242 return subset;
243}
244
245void ABCArchive::update_bounding_box(const Imath::Box3d &bounds)
246{
247 abc_archive_bbox_.set(bounds);
248}
249
250} // namespace blender::io::alembic
const char * BKE_blender_version_string(void)
Definition blender.cc:139
#define FILE_MAX
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_NONNULL(1
unsigned int uint
#define FPS
Frames::const_iterator frames_end() const
bool is_xform_frame(double frame) const
ExportSubset export_subset_for_frame(double frame) const
bool is_shape_frame(double frame) const
Frames::const_iterator frames_begin() const
uint32_t time_sampling_index_shapes() const
ABCArchive(const Main *bmain, const Scene *scene, AlembicExportParams params, const std::string &filepath)
Alembic::Abc::OArchive * archive
Definition abc_archive.h:34
void update_bounding_box(const Imath::Box3d &bounds)
uint32_t time_sampling_index_transforms() const
double time
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
static void get_frames(double scene_fps, const AlembicExportParams &params, uint nr_of_samples, std::set< double > &r_frames)
static MetaData create_abc_metadata(const Main *bmain, double scene_fps)
static OArchive * create_archive(std::ofstream *abc_ostream, const std::string &filepath, MetaData &abc_metadata)
static TimeSamplingPtr create_time_sampling(double scene_fps, const AlembicExportParams &params, int nr_of_samples)
static void get_shutter_samples(double scene_fps, const AlembicExportParams &params, int nr_of_samples, bool time_relative, std::vector< double > &r_samples)
unsigned int uint32_t
Definition stdint.h:80
char filepath[1024]
Definition BKE_main.hh:136
#define UTF16_ENCODE(in8str)
Definition utfconv.hh:80
#define UTF16_UN_ENCODE(in8str)
Definition utfconv.hh:84