1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2020 Blender Foundation.
17  * All rights reserved.
18  */
19 
20 #include "abc_archive.h"
21 
22 #include "BKE_blender_version.h"
23 #include "BKE_main.h"
24 #include "BKE_scene.h"
25 
26 #include "DEG_depsgraph_query.h"
27 
28 #include "DNA_scene_types.h"
29 
30 #include <Alembic/AbcCoreOgawa/All.h>
31 #include <Alembic/AbcGeom/All.h>
32 
33 #ifdef WIN32
34 #  include "BLI_path_util.h"
35 #  include "BLI_string.h"
36 
37 #  include "utfconv.h"
38 #endif
39 
40 namespace blender::io::alembic {
41 
42 using Alembic::Abc::ErrorHandler;
43 using Alembic::Abc::kWrapExisting;
44 using Alembic::Abc::MetaData;
45 using Alembic::Abc::OArchive;
46 using Alembic::Abc::TimeSampling;
47 using Alembic::Abc::TimeSamplingPtr;
48 using Alembic::Abc::TimeSamplingType;
49 
create_abc_metadata(const Main * bmain,double scene_fps)50 static MetaData create_abc_metadata(const Main *bmain, double scene_fps)
51 {
52   MetaData abc_metadata;
53 
54   std::string abc_user_description(bmain->name);
55   if (abc_user_description.empty()) {
56     abc_user_description = "unknown";
57   }
58 
59   abc_metadata.set(Alembic::Abc::kApplicationNameKey, "Blender");
60   abc_metadata.set(Alembic::Abc::kUserDescriptionKey, abc_user_description);
61   abc_metadata.set("blender_version", std::string("v") + BKE_blender_version_string());
62   abc_metadata.set("FramesPerTimeUnit", std::to_string(scene_fps));
63 
64   time_t raw_time;
65   time(&raw_time);
66   char buffer[128];
67 
68 #if defined _WIN32 || defined _WIN64
69   ctime_s(buffer, 128, &raw_time);
70 #else
71   ctime_r(&raw_time, buffer);
72 #endif
73 
74   const std::size_t buffer_len = strlen(buffer);
75   if (buffer_len > 0 && buffer[buffer_len - 1] == '\n') {
76     buffer[buffer_len - 1] = '\0';
77   }
78 
79   abc_metadata.set(Alembic::Abc::kDateWrittenKey, buffer);
80   return abc_metadata;
81 }
82 
create_archive(std::ofstream * abc_ostream,const std::string & filename,MetaData & abc_metadata)83 static OArchive *create_archive(std::ofstream *abc_ostream,
84                                 const std::string &filename,
85                                 MetaData &abc_metadata)
86 {
87   /* Use stream to support unicode character paths on Windows. */
88 #ifdef WIN32
89   char filename_cstr[FILE_MAX];
90   BLI_strncpy(filename_cstr, filename.c_str(), FILE_MAX);
91 
92   UTF16_ENCODE(filename_cstr);
93   std::wstring wstr(filename_cstr_16);
94   abc_ostream->open(wstr.c_str(), std::ios::out | std::ios::binary);
95   UTF16_UN_ENCODE(filename_cstr);
96 #else
97   abc_ostream->open(filename, std::ios::out | std::ios::binary);
98 #endif
99 
100   ErrorHandler::Policy policy = ErrorHandler::kThrowPolicy;
101 
102   Alembic::AbcCoreOgawa::WriteArchive archive_writer;
103   return new OArchive(archive_writer(abc_ostream, abc_metadata), kWrapExisting, policy);
104 }
105 
106 /* Construct list of shutter samples.
107  *
108  * These are taken from the interval [shutter open, shutter close),
109  * uniformly sampled with 'nr_of_samples' samples.
110  *
111  * TODO(Sybren): test that the above interval is indeed half-open.
112  *
113  * If 'time_relative' is true, samples are returned as time (in seconds) from params.frame_start.
114  * If 'time_relative' is false, samples are returned as fractional frames from 0.
115  * */
get_shutter_samples(double scene_fps,const AlembicExportParams & params,int nr_of_samples,bool time_relative,std::vector<double> & r_samples)116 static void get_shutter_samples(double scene_fps,
117                                 const AlembicExportParams &params,
118                                 int nr_of_samples,
119                                 bool time_relative,
120                                 std::vector<double> &r_samples)
121 {
122   int frame_offset = time_relative ? params.frame_start : 0;
123   double time_factor = time_relative ? scene_fps : 1.0;
124   double shutter_open = params.shutter_open;
125   double shutter_close = params.shutter_close;
126   double time_inc = (shutter_close - shutter_open) / nr_of_samples;
127 
128   /* sample between shutter open & close */
129   for (int sample = 0; sample < nr_of_samples; sample++) {
130     double sample_time = shutter_open + time_inc * sample;
131     double time = (frame_offset + sample_time) / time_factor;
132 
133     r_samples.push_back(time);
134   }
135 }
136 
create_time_sampling(double scene_fps,const AlembicExportParams & params,int nr_of_samples)137 static TimeSamplingPtr create_time_sampling(double scene_fps,
138                                             const AlembicExportParams &params,
139                                             int nr_of_samples)
140 {
141   std::vector<double> samples;
142 
143   if (params.frame_start == params.frame_end) {
144     return TimeSamplingPtr(new TimeSampling());
145   }
146 
147   get_shutter_samples(scene_fps, params, nr_of_samples, true, samples);
148 
149   TimeSamplingType ts(static_cast<uint32_t>(samples.size()), 1.0 / scene_fps);
150   return TimeSamplingPtr(new TimeSampling(ts, samples));
151 }
152 
get_frames(double scene_fps,const AlembicExportParams & params,unsigned int nr_of_samples,std::set<double> & r_frames)153 static void get_frames(double scene_fps,
154                        const AlembicExportParams &params,
155                        unsigned int nr_of_samples,
156                        std::set<double> &r_frames)
157 {
158   /* Get one set of shutter samples, then add those around each frame to export. */
159   std::vector<double> shutter_samples;
160   get_shutter_samples(scene_fps, params, nr_of_samples, false, shutter_samples);
161 
162   for (double frame = params.frame_start; frame <= params.frame_end; frame += 1.0) {
163     for (size_t j = 0; j < nr_of_samples; j++) {
164       r_frames.insert(frame + shutter_samples[j]);
165     }
166   }
167 }
168 
169 /* ****************************************************************** */
170 
ABCArchive(const Main * bmain,const Scene * scene,AlembicExportParams params,std::string filename)171 ABCArchive::ABCArchive(const Main *bmain,
172                        const Scene *scene,
173                        AlembicExportParams params,
174                        std::string filename)
175     : archive(nullptr)
176 {
177   double scene_fps = FPS;
178   MetaData abc_metadata = create_abc_metadata(bmain, scene_fps);
179 
180   /* Create the Archive. */
181   archive = create_archive(&abc_ostream_, filename, abc_metadata);
182 
183   /* Create time samples for transforms and shapes. */
184   TimeSamplingPtr ts_xform;
185   TimeSamplingPtr ts_shapes;
186 
187   ts_xform = create_time_sampling(scene_fps, params, params.frame_samples_xform);
188   time_sampling_index_transforms_ = archive->addTimeSampling(*ts_xform);
189 
190   const bool export_animation = params.frame_start != params.frame_end;
191   if (!export_animation || params.frame_samples_shape == params.frame_samples_xform) {
192     ts_shapes = ts_xform;
193     time_sampling_index_shapes_ = time_sampling_index_transforms_;
194   }
195   else {
196     ts_shapes = create_time_sampling(scene_fps, params, params.frame_samples_shape);
197     time_sampling_index_shapes_ = archive->addTimeSampling(*ts_shapes);
198   }
199 
200   /* Construct the frames to export. */
201   get_frames(scene_fps, params, params.frame_samples_xform, xform_frames_);
202   get_frames(scene_fps, params, params.frame_samples_shape, shape_frames_);
203 
204   /* Merge all frames to get the final set of frames to export. */
205   export_frames_.insert(xform_frames_.begin(), xform_frames_.end());
206   export_frames_.insert(shape_frames_.begin(), shape_frames_.end());
207 
208   abc_archive_bbox_ = Alembic::AbcGeom::CreateOArchiveBounds(*archive,
209                                                              time_sampling_index_transforms_);
210 }
211 
~ABCArchive()212 ABCArchive::~ABCArchive()
213 {
214   delete archive;
215 }
216 
time_sampling_index_transforms() const217 uint32_t ABCArchive::time_sampling_index_transforms() const
218 {
219   return time_sampling_index_transforms_;
220 }
221 
time_sampling_index_shapes() const222 uint32_t ABCArchive::time_sampling_index_shapes() const
223 {
224   return time_sampling_index_shapes_;
225 }
226 
frames_begin() const227 ABCArchive::Frames::const_iterator ABCArchive::frames_begin() const
228 {
229   return export_frames_.begin();
230 }
frames_end() const231 ABCArchive::Frames::const_iterator ABCArchive::frames_end() const
232 {
233   return export_frames_.end();
234 }
total_frame_count() const235 size_t ABCArchive::total_frame_count() const
236 {
237   return export_frames_.size();
238 }
239 
is_xform_frame(double frame) const240 bool ABCArchive::is_xform_frame(double frame) const
241 {
242   return xform_frames_.find(frame) != xform_frames_.end();
243 }
is_shape_frame(double frame) const244 bool ABCArchive::is_shape_frame(double frame) const
245 {
246   return shape_frames_.find(frame) != shape_frames_.end();
247 }
export_subset_for_frame(double frame) const248 ExportSubset ABCArchive::export_subset_for_frame(double frame) const
249 {
250   ExportSubset subset;
251   subset.transforms = is_xform_frame(frame);
252   subset.shapes = is_shape_frame(frame);
253   return subset;
254 }
255 
update_bounding_box(const Imath::Box3d & bounds)256 void ABCArchive::update_bounding_box(const Imath::Box3d &bounds)
257 {
258   abc_archive_bbox_.set(bounds);
259 }
260 
261 }  // namespace blender::io::alembic
262