Write custom properties (aka ID properties) to Alembic, to the `.userProperties` compound property. Manifest Task: https://developer.blender.org/T50725 Scalar properties (so single-value/non-array properties) are written as single-element array properties to Alembic. This is also what's done by Houdini and Maya exporters, so it seems to be the standard way of doing things. It also simplifies the implementation. Two-dimensional arrays are flattened by concatenating all the numbers into a single array. This is because ID properties have a limited type system. This means that a 3x3 "matrix" could just as well be a list of three 3D vectors. Alembic has two container properties to store custom data: - `.userProperties`, which is meant for properties that aren't necessarily understood by other software packages, and - `.arbGeomParams`, which can contain the same kind of data as `.userProperties`, but can also specify that these vary per face of a mesh. This property is mostly intended for renderers. Most industry packages write their custom data to `.arbGeomParams`. However, given their goals I feel that `.userProperties` is the more appropriate one for Blender's ID Properties. The code is a bit more involved than I would have liked. An `ABCAbstractWriter` has a `uniqueptr` to its `CustomPropertiesExporter`, but the `CustomPropertiesExporter` also has a pointer back to its owning `ABCAbstractWriter`. It's the latter pointer that I'm not too happy with, but it has a reason. Getting the aforementioned `.userProperties` from the Alembic library will automatically create it if it doesn't exist already. If it's not used to actually add custom properties to, it will crash the Alembic CLI tools (and maybe others too). This is what the pointer back to the `ABCAbstractWriter` is used for: to get the `.userProperties` at the last moment, when it's 100% sure at least one custom property will be written. Differential Revision: https://developer.blender.org/D8869 Reviewed by: sergey, dbystedt
112 lines
3.6 KiB
C++
112 lines
3.6 KiB
C++
/*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
/** \file
|
|
* \ingroup balembic
|
|
*/
|
|
|
|
#include "abc_writer_camera.h"
|
|
#include "abc_hierarchy_iterator.h"
|
|
|
|
#include "BKE_camera.h"
|
|
|
|
#include "BLI_assert.h"
|
|
|
|
#include "DNA_camera_types.h"
|
|
#include "DNA_scene_types.h"
|
|
|
|
#include "CLG_log.h"
|
|
static CLG_LogRef LOG = {"io.alembic"};
|
|
|
|
namespace blender::io::alembic {
|
|
|
|
using Alembic::AbcGeom::CameraSample;
|
|
using Alembic::AbcGeom::OCamera;
|
|
using Alembic::AbcGeom::OFloatProperty;
|
|
|
|
ABCCameraWriter::ABCCameraWriter(const ABCWriterConstructorArgs &args) : ABCAbstractWriter(args)
|
|
{
|
|
}
|
|
|
|
bool ABCCameraWriter::is_supported(const HierarchyContext *context) const
|
|
{
|
|
Camera *camera = static_cast<Camera *>(context->object->data);
|
|
return camera->type == CAM_PERSP;
|
|
}
|
|
|
|
void ABCCameraWriter::create_alembic_objects(const HierarchyContext * /*context*/)
|
|
{
|
|
CLOG_INFO(&LOG, 2, "exporting %s", args_.abc_path.c_str());
|
|
abc_camera_ = OCamera(args_.abc_parent, args_.abc_name, timesample_index_);
|
|
abc_camera_schema_ = abc_camera_.getSchema();
|
|
|
|
abc_custom_data_container_ = abc_camera_schema_.getUserProperties();
|
|
abc_stereo_distance_ = OFloatProperty(
|
|
abc_custom_data_container_, "stereoDistance", timesample_index_);
|
|
abc_eye_separation_ = OFloatProperty(
|
|
abc_custom_data_container_, "eyeSeparation", timesample_index_);
|
|
}
|
|
|
|
Alembic::Abc::OObject ABCCameraWriter::get_alembic_object() const
|
|
{
|
|
return abc_camera_;
|
|
}
|
|
|
|
Alembic::Abc::OCompoundProperty ABCCameraWriter::abc_prop_for_custom_props()
|
|
{
|
|
return abc_schema_prop_for_custom_props(abc_camera_schema_);
|
|
}
|
|
|
|
void ABCCameraWriter::do_write(HierarchyContext &context)
|
|
{
|
|
Camera *cam = static_cast<Camera *>(context.object->data);
|
|
|
|
abc_stereo_distance_.set(cam->stereo.convergence_distance);
|
|
abc_eye_separation_.set(cam->stereo.interocular_distance);
|
|
|
|
const double apperture_x = cam->sensor_x / 10.0;
|
|
const double apperture_y = cam->sensor_y / 10.0;
|
|
const double film_aspect = apperture_x / apperture_y;
|
|
|
|
CameraSample camera_sample;
|
|
camera_sample.setFocalLength(cam->lens);
|
|
camera_sample.setHorizontalAperture(apperture_x);
|
|
camera_sample.setVerticalAperture(apperture_y);
|
|
camera_sample.setHorizontalFilmOffset(apperture_x * cam->shiftx);
|
|
camera_sample.setVerticalFilmOffset(apperture_y * cam->shifty * film_aspect);
|
|
camera_sample.setNearClippingPlane(cam->clip_start);
|
|
camera_sample.setFarClippingPlane(cam->clip_end);
|
|
|
|
if (cam->dof.focus_object) {
|
|
Imath::V3f v(context.object->loc[0] - cam->dof.focus_object->loc[0],
|
|
context.object->loc[1] - cam->dof.focus_object->loc[1],
|
|
context.object->loc[2] - cam->dof.focus_object->loc[2]);
|
|
camera_sample.setFocusDistance(v.length());
|
|
}
|
|
else {
|
|
camera_sample.setFocusDistance(cam->dof.focus_distance);
|
|
}
|
|
|
|
/* Blender camera does not have an fstop param, so try to find a custom prop
|
|
* instead. */
|
|
camera_sample.setFStop(cam->dof.aperture_fstop);
|
|
|
|
camera_sample.setLensSqueezeRatio(1.0);
|
|
abc_camera_schema_.set(camera_sample);
|
|
}
|
|
|
|
} // namespace blender::io::alembic
|