Merge branch 'master' into blender2.8
# Conflicts: # source/blender/alembic/intern/abc_exporter.h # source/blender/alembic/intern/abc_util.cc
This commit is contained in:
@@ -595,6 +595,7 @@ function(SETUP_BLENDER_SORTED_LIBS)
|
|||||||
bf_freestyle
|
bf_freestyle
|
||||||
bf_ikplugin
|
bf_ikplugin
|
||||||
bf_modifiers
|
bf_modifiers
|
||||||
|
bf_alembic
|
||||||
bf_bmesh
|
bf_bmesh
|
||||||
bf_gpu
|
bf_gpu
|
||||||
bf_draw
|
bf_draw
|
||||||
@@ -615,7 +616,6 @@ function(SETUP_BLENDER_SORTED_LIBS)
|
|||||||
bf_imbuf_openimageio
|
bf_imbuf_openimageio
|
||||||
bf_imbuf_dds
|
bf_imbuf_dds
|
||||||
bf_collada
|
bf_collada
|
||||||
bf_alembic
|
|
||||||
bf_intern_elbeem
|
bf_intern_elbeem
|
||||||
bf_intern_memutil
|
bf_intern_memutil
|
||||||
bf_intern_guardedalloc
|
bf_intern_guardedalloc
|
||||||
|
@@ -560,6 +560,9 @@ static void attr_create_pointiness(Scene *scene,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const int num_verts = b_mesh.vertices.length();
|
const int num_verts = b_mesh.vertices.length();
|
||||||
|
if(num_verts == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
/* STEP 1: Find out duplicated vertices and point duplicates to a single
|
/* STEP 1: Find out duplicated vertices and point duplicates to a single
|
||||||
* original vertex.
|
* original vertex.
|
||||||
*/
|
*/
|
||||||
@@ -1164,8 +1167,8 @@ void BlenderSync::sync_mesh_motion(BL::Object& b_ob,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* skip empty meshes */
|
/* skip empty meshes */
|
||||||
size_t numverts = mesh->verts.size();
|
const size_t numverts = mesh->verts.size();
|
||||||
size_t numkeys = mesh->curve_keys.size();
|
const size_t numkeys = mesh->curve_keys.size();
|
||||||
|
|
||||||
if(!numverts && !numkeys)
|
if(!numverts && !numkeys)
|
||||||
return;
|
return;
|
||||||
@@ -1223,13 +1226,12 @@ void BlenderSync::sync_mesh_motion(BL::Object& b_ob,
|
|||||||
|
|
||||||
/* TODO(sergey): Perform preliminary check for number of verticies. */
|
/* TODO(sergey): Perform preliminary check for number of verticies. */
|
||||||
if(numverts) {
|
if(numverts) {
|
||||||
/* find attributes */
|
/* Find attributes. */
|
||||||
Attribute *attr_mP = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
|
Attribute *attr_mP = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
|
||||||
Attribute *attr_mN = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_NORMAL);
|
Attribute *attr_mN = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_NORMAL);
|
||||||
Attribute *attr_N = mesh->attributes.find(ATTR_STD_VERTEX_NORMAL);
|
Attribute *attr_N = mesh->attributes.find(ATTR_STD_VERTEX_NORMAL);
|
||||||
bool new_attribute = false;
|
bool new_attribute = false;
|
||||||
|
/* Add new attributes if they don't exist already. */
|
||||||
/* add new attributes if they don't exist already */
|
|
||||||
if(!attr_mP) {
|
if(!attr_mP) {
|
||||||
attr_mP = mesh->attributes.add(ATTR_STD_MOTION_VERTEX_POSITION);
|
attr_mP = mesh->attributes.add(ATTR_STD_MOTION_VERTEX_POSITION);
|
||||||
if(attr_N)
|
if(attr_N)
|
||||||
@@ -1237,22 +1239,21 @@ void BlenderSync::sync_mesh_motion(BL::Object& b_ob,
|
|||||||
|
|
||||||
new_attribute = true;
|
new_attribute = true;
|
||||||
}
|
}
|
||||||
|
/* Load vertex data from mesh. */
|
||||||
/* load vertex data from mesh */
|
|
||||||
float3 *mP = attr_mP->data_float3() + time_index*numverts;
|
float3 *mP = attr_mP->data_float3() + time_index*numverts;
|
||||||
float3 *mN = (attr_mN)? attr_mN->data_float3() + time_index*numverts: NULL;
|
float3 *mN = (attr_mN)? attr_mN->data_float3() + time_index*numverts: NULL;
|
||||||
|
/* NOTE: We don't copy more that existing amount of vertices to prevent
|
||||||
|
* possible memory corruption.
|
||||||
|
*/
|
||||||
BL::Mesh::vertices_iterator v;
|
BL::Mesh::vertices_iterator v;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
for(b_mesh.vertices.begin(v); v != b_mesh.vertices.end() && i < numverts; ++v, ++i) {
|
for(b_mesh.vertices.begin(v); v != b_mesh.vertices.end() && i < numverts; ++v, ++i) {
|
||||||
mP[i] = get_float3(v->co());
|
mP[i] = get_float3(v->co());
|
||||||
if(mN)
|
if(mN)
|
||||||
mN[i] = get_float3(v->normal());
|
mN[i] = get_float3(v->normal());
|
||||||
}
|
}
|
||||||
|
|
||||||
/* in case of new attribute, we verify if there really was any motion */
|
|
||||||
if(new_attribute) {
|
if(new_attribute) {
|
||||||
|
/* In case of new attribute, we verify if there really was any motion. */
|
||||||
if(b_mesh.vertices.length() != numverts ||
|
if(b_mesh.vertices.length() != numverts ||
|
||||||
memcmp(mP, &mesh->verts[0], sizeof(float3)*numverts) == 0)
|
memcmp(mP, &mesh->verts[0], sizeof(float3)*numverts) == 0)
|
||||||
{
|
{
|
||||||
@@ -1275,7 +1276,6 @@ void BlenderSync::sync_mesh_motion(BL::Object& b_ob,
|
|||||||
* they had no motion, but we need them anyway now */
|
* they had no motion, but we need them anyway now */
|
||||||
float3 *P = &mesh->verts[0];
|
float3 *P = &mesh->verts[0];
|
||||||
float3 *N = (attr_N)? attr_N->data_float3(): NULL;
|
float3 *N = (attr_N)? attr_N->data_float3(): NULL;
|
||||||
|
|
||||||
for(int step = 0; step < time_index; step++) {
|
for(int step = 0; step < time_index; step++) {
|
||||||
memcpy(attr_mP->data_float3() + step*numverts, P, sizeof(float3)*numverts);
|
memcpy(attr_mP->data_float3() + step*numverts, P, sizeof(float3)*numverts);
|
||||||
if(attr_mN)
|
if(attr_mN)
|
||||||
@@ -1283,6 +1283,16 @@ void BlenderSync::sync_mesh_motion(BL::Object& b_ob,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
if(b_mesh.vertices.length() != numverts) {
|
||||||
|
VLOG(1) << "Topology differs, discarding motion blur for object "
|
||||||
|
<< b_ob.name() << " at time " << time_index;
|
||||||
|
memcpy(mP, &mesh->verts[0], sizeof(float3)*numverts);
|
||||||
|
if(mN != NULL) {
|
||||||
|
memcpy(mN, attr_N->data_float3(), sizeof(float3)*numverts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* hair motion */
|
/* hair motion */
|
||||||
|
@@ -856,7 +856,7 @@ int2 CPUSplitKernel::split_kernel_local_size()
|
|||||||
}
|
}
|
||||||
|
|
||||||
int2 CPUSplitKernel::split_kernel_global_size(device_memory& /*kg*/, device_memory& /*data*/, DeviceTask * /*task*/) {
|
int2 CPUSplitKernel::split_kernel_global_size(device_memory& /*kg*/, device_memory& /*data*/, DeviceTask * /*task*/) {
|
||||||
return make_int2(64, 1);
|
return make_int2(1, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t CPUSplitKernel::state_buffer_size(device_memory& kernel_globals, device_memory& /*data*/, size_t num_threads) {
|
uint64_t CPUSplitKernel::state_buffer_size(device_memory& kernel_globals, device_memory& /*data*/, size_t num_threads) {
|
||||||
|
@@ -151,7 +151,8 @@ bool DeviceSplitKernel::path_trace(DeviceTask *task,
|
|||||||
/* Calculate max groups */
|
/* Calculate max groups */
|
||||||
|
|
||||||
/* Denotes the maximum work groups possible w.r.t. current requested tile size. */
|
/* Denotes the maximum work groups possible w.r.t. current requested tile size. */
|
||||||
unsigned int max_work_groups = num_global_elements / WORK_POOL_SIZE + 1;
|
unsigned int work_pool_size = (device->info.type == DEVICE_CPU) ? WORK_POOL_SIZE_CPU : WORK_POOL_SIZE_GPU;
|
||||||
|
unsigned int max_work_groups = num_global_elements / work_pool_size + 1;
|
||||||
|
|
||||||
/* Allocate work_pool_wgs memory. */
|
/* Allocate work_pool_wgs memory. */
|
||||||
work_pool_wgs.resize(max_work_groups * sizeof(unsigned int));
|
work_pool_wgs.resize(max_work_groups * sizeof(unsigned int));
|
||||||
@@ -256,10 +257,8 @@ bool DeviceSplitKernel::path_trace(DeviceTask *task,
|
|||||||
activeRaysAvailable = false;
|
activeRaysAvailable = false;
|
||||||
|
|
||||||
for(int rayStateIter = 0; rayStateIter < global_size[0] * global_size[1]; ++rayStateIter) {
|
for(int rayStateIter = 0; rayStateIter < global_size[0] * global_size[1]; ++rayStateIter) {
|
||||||
int8_t state = ray_state.get_data()[rayStateIter];
|
if(!IS_STATE(ray_state.get_data(), rayStateIter, RAY_INACTIVE)) {
|
||||||
|
if(IS_STATE(ray_state.get_data(), rayStateIter, RAY_INVALID)) {
|
||||||
if(state != RAY_INACTIVE) {
|
|
||||||
if(state == RAY_INVALID) {
|
|
||||||
/* Something went wrong, abort to avoid looping endlessly. */
|
/* Something went wrong, abort to avoid looping endlessly. */
|
||||||
device->set_error("Split kernel error: invalid ray state");
|
device->set_error("Split kernel error: invalid ray state");
|
||||||
return false;
|
return false;
|
||||||
|
@@ -281,6 +281,7 @@ void OpenCLDeviceBase::OpenCLProgram::add_log(string msg, bool debug)
|
|||||||
}
|
}
|
||||||
else if(!debug) {
|
else if(!debug) {
|
||||||
printf("%s\n", msg.c_str());
|
printf("%s\n", msg.c_str());
|
||||||
|
fflush(stdout);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
VLOG(2) << msg;
|
VLOG(2) << msg;
|
||||||
|
@@ -422,9 +422,9 @@ ccl_device_inline bool shadow_blocked(KernelGlobals *kg,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#ifdef __SHADOW_TRICKS__
|
#ifdef __SHADOW_TRICKS__
|
||||||
const int skip_object = state->catcher_object;
|
const int skip_object = state->catcher_object;
|
||||||
#else
|
#else
|
||||||
const int skip_object = OBJECT_NONE;
|
const int skip_object = OBJECT_NONE;
|
||||||
#endif
|
#endif
|
||||||
/* Do actual shadow shading. */
|
/* Do actual shadow shading. */
|
||||||
/* First of all, we check if integrator requires transparent shadows.
|
/* First of all, we check if integrator requires transparent shadows.
|
||||||
|
@@ -56,7 +56,13 @@ CCL_NAMESPACE_BEGIN
|
|||||||
|
|
||||||
#define VOLUME_STACK_SIZE 16
|
#define VOLUME_STACK_SIZE 16
|
||||||
|
|
||||||
#define WORK_POOL_SIZE 64
|
#define WORK_POOL_SIZE_GPU 64
|
||||||
|
#define WORK_POOL_SIZE_CPU 1
|
||||||
|
#ifdef __KERNEL_GPU__
|
||||||
|
# define WORK_POOL_SIZE WORK_POOL_SIZE_GPU
|
||||||
|
#else
|
||||||
|
# define WORK_POOL_SIZE WORK_POOL_SIZE_CPU
|
||||||
|
#endif
|
||||||
|
|
||||||
/* device capabilities */
|
/* device capabilities */
|
||||||
#ifdef __KERNEL_CPU__
|
#ifdef __KERNEL_CPU__
|
||||||
|
@@ -42,18 +42,6 @@ bool starts_with(const std::string &str,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ltrim(const std::string &str) {
|
|
||||||
std::string result = str;
|
|
||||||
result.erase(0, result.find_first_not_of(" \t\r\n"));
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string rtrim(const std::string &str) {
|
|
||||||
std::string result = str;
|
|
||||||
result.erase(result.find_last_not_of(" \t\r\n") + 1);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string trim(const std::string &str) {
|
std::string trim(const std::string &str) {
|
||||||
std::string result = str;
|
std::string result = str;
|
||||||
result.erase(0, result.find_first_not_of(" \t\r\n"));
|
result.erase(0, result.find_first_not_of(" \t\r\n"));
|
||||||
|
@@ -154,11 +154,13 @@ AbcExporter::AbcExporter(Scene *scene, const char *filename, ExportSettings &set
|
|||||||
|
|
||||||
AbcExporter::~AbcExporter()
|
AbcExporter::~AbcExporter()
|
||||||
{
|
{
|
||||||
std::map<std::string, AbcTransformWriter*>::iterator it, e;
|
/* Free xforms map */
|
||||||
for (it = m_xforms.begin(), e = m_xforms.end(); it != e; ++it) {
|
m_xforms_type::iterator it_x, e_x;
|
||||||
delete it->second;
|
for (it_x = m_xforms.begin(), e_x = m_xforms.end(); it_x != e_x; ++it_x) {
|
||||||
|
delete it_x->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Free shapes vector */
|
||||||
for (int i = 0, e = m_shapes.size(); i != e; ++i) {
|
for (int i = 0, e = m_shapes.size(); i != e; ++i) {
|
||||||
delete m_shapes[i];
|
delete m_shapes[i];
|
||||||
}
|
}
|
||||||
@@ -323,7 +325,7 @@ void AbcExporter::operator()(Main *bmain, float &progress, bool &was_canceled)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::map<std::string, AbcTransformWriter *>::iterator xit, xe;
|
m_xforms_type::iterator xit, xe;
|
||||||
for (xit = m_xforms.begin(), xe = m_xforms.end(); xit != xe; ++xit) {
|
for (xit = m_xforms.begin(), xe = m_xforms.end(); xit != xe; ++xit) {
|
||||||
xit->second->write();
|
xit->second->write();
|
||||||
}
|
}
|
||||||
@@ -404,7 +406,7 @@ void AbcExporter::exploreTransform(EvaluationContext *eval_ctx, Base *ob_base, O
|
|||||||
free_object_duplilist(lb);
|
free_object_duplilist(lb);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AbcExporter::createTransformWriter(Object *ob, Object *parent, Object *dupliObParent)
|
AbcTransformWriter * AbcExporter::createTransformWriter(Object *ob, Object *parent, Object *dupliObParent)
|
||||||
{
|
{
|
||||||
const std::string name = get_object_dag_path_name(ob, dupliObParent);
|
const std::string name = get_object_dag_path_name(ob, dupliObParent);
|
||||||
|
|
||||||
@@ -413,44 +415,47 @@ void AbcExporter::createTransformWriter(Object *ob, Object *parent, Object *dupl
|
|||||||
BLI_assert(ob != dupliObParent);
|
BLI_assert(ob != dupliObParent);
|
||||||
|
|
||||||
/* check if we have already created a transform writer for this object */
|
/* check if we have already created a transform writer for this object */
|
||||||
if (getXForm(name) != NULL) {
|
AbcTransformWriter *my_writer = getXForm(name);
|
||||||
ABC_LOG(m_settings.logger) << "xform " << name << " already exists!\n";
|
if (my_writer != NULL){
|
||||||
return;
|
return my_writer;
|
||||||
}
|
}
|
||||||
|
|
||||||
AbcTransformWriter *parent_xform = NULL;
|
AbcTransformWriter *parent_writer = NULL;
|
||||||
|
Alembic::Abc::OObject alembic_parent;
|
||||||
|
|
||||||
if (parent) {
|
if (parent) {
|
||||||
const std::string parentname = get_object_dag_path_name(parent, dupliObParent);
|
/* Since there are so many different ways to find parents (as evident
|
||||||
parent_xform = getXForm(parentname);
|
* in the number of conditions below), we can't really look up the
|
||||||
|
* parent by name. We'll just call createTransformWriter(), which will
|
||||||
if (!parent_xform) {
|
* return the parent's AbcTransformWriter pointer. */
|
||||||
if (parent->parent) {
|
if (parent->parent) {
|
||||||
createTransformWriter(parent, parent->parent, dupliObParent);
|
parent_writer = createTransformWriter(parent, parent->parent, dupliObParent);
|
||||||
}
|
}
|
||||||
else if (parent == dupliObParent) {
|
else if (parent == dupliObParent) {
|
||||||
if (dupliObParent->parent == NULL) {
|
if (dupliObParent->parent == NULL) {
|
||||||
createTransformWriter(parent, NULL, NULL);
|
parent_writer = createTransformWriter(parent, NULL, NULL);
|
||||||
}
|
|
||||||
else {
|
|
||||||
createTransformWriter(parent, dupliObParent->parent, dupliObParent->parent);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
createTransformWriter(parent, dupliObParent, dupliObParent);
|
parent_writer = createTransformWriter(parent, dupliObParent->parent, dupliObParent->parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
parent_xform = getXForm(parentname);
|
|
||||||
}
|
}
|
||||||
}
|
else {
|
||||||
|
parent_writer = createTransformWriter(parent, dupliObParent, dupliObParent);
|
||||||
|
}
|
||||||
|
|
||||||
if (parent_xform) {
|
BLI_assert(parent_writer);
|
||||||
m_xforms[name] = new AbcTransformWriter(ob, parent_xform->alembicXform(), parent_xform, m_trans_sampling_index, m_settings);
|
alembic_parent = parent_writer->alembicXform();
|
||||||
m_xforms[name]->setParent(parent);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
m_xforms[name] = new AbcTransformWriter(ob, m_writer->archive().getTop(), NULL, m_trans_sampling_index, m_settings);
|
/* Parentless objects still have the "top object" as parent
|
||||||
|
* in Alembic. */
|
||||||
|
alembic_parent = m_writer->archive().getTop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
my_writer = new AbcTransformWriter(ob, alembic_parent, parent_writer,
|
||||||
|
m_trans_sampling_index, m_settings);
|
||||||
|
m_xforms[name] = my_writer;
|
||||||
|
return my_writer;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AbcExporter::createShapeWriters(EvaluationContext *eval_ctx)
|
void AbcExporter::createShapeWriters(EvaluationContext *eval_ctx)
|
||||||
|
@@ -92,7 +92,10 @@ class AbcExporter {
|
|||||||
|
|
||||||
ArchiveWriter *m_writer;
|
ArchiveWriter *m_writer;
|
||||||
|
|
||||||
std::map<std::string, AbcTransformWriter *> m_xforms;
|
/* mapping from name to transform writer */
|
||||||
|
typedef std::map<std::string, AbcTransformWriter *> m_xforms_type;
|
||||||
|
m_xforms_type m_xforms;
|
||||||
|
|
||||||
std::vector<AbcObjectWriter *> m_shapes;
|
std::vector<AbcObjectWriter *> m_shapes;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -110,7 +113,7 @@ private:
|
|||||||
|
|
||||||
void createTransformWritersHierarchy(EvaluationContext *eval_ctx);
|
void createTransformWritersHierarchy(EvaluationContext *eval_ctx);
|
||||||
void createTransformWritersFlat();
|
void createTransformWritersFlat();
|
||||||
void createTransformWriter(Object *ob, Object *parent, Object *dupliObParent);
|
AbcTransformWriter * createTransformWriter(Object *ob, Object *parent, Object *dupliObParent);
|
||||||
void exploreTransform(EvaluationContext *eval_ctx, Base *ob_base, Object *parent, Object *dupliObParent);
|
void exploreTransform(EvaluationContext *eval_ctx, Base *ob_base, Object *parent, Object *dupliObParent);
|
||||||
void exploreObject(EvaluationContext *eval_ctx, Base *ob_base, Object *dupliObParent);
|
void exploreObject(EvaluationContext *eval_ctx, Base *ob_base, Object *dupliObParent);
|
||||||
void createShapeWriters(EvaluationContext *eval_ctx);
|
void createShapeWriters(EvaluationContext *eval_ctx);
|
||||||
|
@@ -126,6 +126,7 @@ AbcObjectReader::AbcObjectReader(const IObject &object, ImportSettings &settings
|
|||||||
, m_min_time(std::numeric_limits<chrono_t>::max())
|
, m_min_time(std::numeric_limits<chrono_t>::max())
|
||||||
, m_max_time(std::numeric_limits<chrono_t>::min())
|
, m_max_time(std::numeric_limits<chrono_t>::min())
|
||||||
, m_refcount(0)
|
, m_refcount(0)
|
||||||
|
, parent_reader(NULL)
|
||||||
{
|
{
|
||||||
m_name = object.getFullName();
|
m_name = object.getFullName();
|
||||||
std::vector<std::string> parts;
|
std::vector<std::string> parts;
|
||||||
@@ -213,7 +214,7 @@ Imath::M44d get_matrix(const IXformSchema &schema, const float time)
|
|||||||
return s0.getMatrix();
|
return s0.getMatrix();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AbcObjectReader::readObjectMatrix(const float time)
|
void AbcObjectReader::setupObjectTransform(const float time)
|
||||||
{
|
{
|
||||||
bool is_constant = false;
|
bool is_constant = false;
|
||||||
|
|
||||||
@@ -235,49 +236,88 @@ void AbcObjectReader::readObjectMatrix(const float time)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AbcObjectReader::read_matrix(float mat[4][4], const float time, const float scale, bool &is_constant)
|
Alembic::AbcGeom::IXform AbcObjectReader::xform()
|
||||||
{
|
{
|
||||||
IXform ixform;
|
|
||||||
bool has_alembic_parent = false;
|
|
||||||
|
|
||||||
/* Check that we have an empty object (locator, bone head/tail...). */
|
/* Check that we have an empty object (locator, bone head/tail...). */
|
||||||
if (IXform::matches(m_iobject.getMetaData())) {
|
if (IXform::matches(m_iobject.getMetaData())) {
|
||||||
ixform = IXform(m_iobject, Alembic::AbcGeom::kWrapExisting);
|
return IXform(m_iobject, Alembic::AbcGeom::kWrapExisting);
|
||||||
|
|
||||||
/* See comment below. */
|
|
||||||
has_alembic_parent = m_iobject.getParent().getParent().valid();
|
|
||||||
}
|
}
|
||||||
/* Check that we have an object with actual data. */
|
|
||||||
else if (IXform::matches(m_iobject.getParent().getMetaData())) {
|
|
||||||
ixform = IXform(m_iobject.getParent(), Alembic::AbcGeom::kWrapExisting);
|
|
||||||
|
|
||||||
/* This is a bit hackish, but we need to make sure that extra
|
/* Check that we have an object with actual data, in which case the
|
||||||
* transformations added to the matrix (rotation/scale) are only applied
|
* parent Alembic object should contain the transform. */
|
||||||
* to root objects. The way objects and their hierarchy are created will
|
IObject abc_parent = m_iobject.getParent();
|
||||||
* need to be revisited at some point but for now this seems to do the
|
|
||||||
* trick.
|
/* The archive's top object can be recognised by not having a parent. */
|
||||||
*
|
if (abc_parent.getParent()
|
||||||
* Explanation of the trick:
|
&& IXform::matches(abc_parent.getMetaData())) {
|
||||||
* The first getParent() will return this object's transformation matrix.
|
return IXform(abc_parent, Alembic::AbcGeom::kWrapExisting);
|
||||||
* The second getParent() will get the parent of the transform, but this
|
|
||||||
* might be the archive root ('/') which is valid, so we go passed it to
|
|
||||||
* make sure that there is no parent.
|
|
||||||
*/
|
|
||||||
has_alembic_parent = m_iobject.getParent().getParent().getParent().valid();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Should not happen. */
|
/* Should not happen. */
|
||||||
else {
|
std::cerr << "AbcObjectReader::xform(): "
|
||||||
|
<< "unable to find IXform for Alembic object '"
|
||||||
|
<< m_iobject.getFullName() << "'\n";
|
||||||
|
BLI_assert(false);
|
||||||
|
|
||||||
|
return IXform();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbcObjectReader::read_matrix(float r_mat[4][4], const float time,
|
||||||
|
const float scale, bool &is_constant)
|
||||||
|
{
|
||||||
|
IXform ixform = xform();
|
||||||
|
if (!ixform) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const IXformSchema &schema(ixform.getSchema());
|
const IXformSchema & schema(ixform.getSchema());
|
||||||
|
|
||||||
if (!schema.valid()) {
|
if (!schema.valid()) {
|
||||||
|
std::cerr << "Alembic object " << ixform.getFullName()
|
||||||
|
<< " has an invalid schema." << std::endl;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool has_alembic_parent;
|
||||||
|
IObject ixform_parent = ixform.getParent();
|
||||||
|
if (!ixform_parent.getParent()) {
|
||||||
|
/* The archive top object certainly is not a transform itself, so handle
|
||||||
|
* it as "no parent". */
|
||||||
|
has_alembic_parent = false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
has_alembic_parent = ixform_parent && schema.getInheritsXforms();
|
||||||
|
|
||||||
|
if (has_alembic_parent && m_object->parent == NULL) {
|
||||||
|
/* TODO Sybren: This happened in some files. I think I solved it,
|
||||||
|
* but I'll leave this check in here anyway until we've tested it
|
||||||
|
* more thoroughly. Better than crashing on a null parent anyway. */
|
||||||
|
std::cerr << "Alembic object " << m_iobject.getFullName()
|
||||||
|
<< " with transform " << ixform.getFullName()
|
||||||
|
<< " has an Alembic parent but no parent Blender object."
|
||||||
|
<< std::endl;
|
||||||
|
has_alembic_parent = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const Imath::M44d matrix = get_matrix(schema, time);
|
const Imath::M44d matrix = get_matrix(schema, time);
|
||||||
convert_matrix(matrix, m_object, mat, scale, has_alembic_parent);
|
convert_matrix(matrix, m_object, r_mat);
|
||||||
|
|
||||||
|
if (has_alembic_parent) {
|
||||||
|
/* In this case, the matrix in Alembic is in local coordinates, so
|
||||||
|
* convert to world matrix. To prevent us from reading and accumulating
|
||||||
|
* all parent matrices in the Alembic file, we assume that the Blender
|
||||||
|
* parent object is already updated for the current timekey, and use its
|
||||||
|
* world matrix. */
|
||||||
|
BLI_assert(m_object->parent);
|
||||||
|
mul_m4_m4m4(r_mat, m_object->parent->obmat, r_mat);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* Only apply scaling to root objects, parenting will propagate it. */
|
||||||
|
float scale_mat[4][4];
|
||||||
|
scale_m4_fl(scale_mat, scale);
|
||||||
|
scale_mat[3][3] = scale; /* scale translations too */
|
||||||
|
mul_m4_m4m4(r_mat, r_mat, scale_mat);
|
||||||
|
}
|
||||||
|
|
||||||
is_constant = schema.isConstant();
|
is_constant = schema.isConstant();
|
||||||
}
|
}
|
||||||
|
@@ -117,15 +117,7 @@ struct ImportSettings {
|
|||||||
template <typename Schema>
|
template <typename Schema>
|
||||||
static bool has_animations(Schema &schema, ImportSettings *settings)
|
static bool has_animations(Schema &schema, ImportSettings *settings)
|
||||||
{
|
{
|
||||||
if (settings->is_sequence) {
|
return settings->is_sequence || !schema.isConstant();
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!schema.isConstant()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************** */
|
/* ************************************************************************** */
|
||||||
@@ -151,6 +143,9 @@ protected:
|
|||||||
* modifiers and/or constraints. */
|
* modifiers and/or constraints. */
|
||||||
int m_refcount;
|
int m_refcount;
|
||||||
|
|
||||||
|
public:
|
||||||
|
AbcObjectReader *parent_reader;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit AbcObjectReader(const Alembic::Abc::IObject &object, ImportSettings &settings);
|
explicit AbcObjectReader(const Alembic::Abc::IObject &object, ImportSettings &settings);
|
||||||
|
|
||||||
@@ -158,9 +153,21 @@ public:
|
|||||||
|
|
||||||
const Alembic::Abc::IObject &iobject() const;
|
const Alembic::Abc::IObject &iobject() const;
|
||||||
|
|
||||||
|
typedef std::vector<AbcObjectReader *> ptr_vector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the transform of this object. This can be the Alembic object
|
||||||
|
* itself (in case of an Empty) or it can be the parent Alembic object.
|
||||||
|
*/
|
||||||
|
virtual Alembic::AbcGeom::IXform xform();
|
||||||
|
|
||||||
Object *object() const;
|
Object *object() const;
|
||||||
void object(Object *ob);
|
void object(Object *ob);
|
||||||
|
|
||||||
|
const std::string & name() const { return m_name; }
|
||||||
|
const std::string & object_name() const { return m_object_name; }
|
||||||
|
const std::string & data_name() const { return m_data_name; }
|
||||||
|
|
||||||
virtual bool valid() const = 0;
|
virtual bool valid() const = 0;
|
||||||
|
|
||||||
virtual void readObjectData(Main *bmain, float time) = 0;
|
virtual void readObjectData(Main *bmain, float time) = 0;
|
||||||
@@ -173,7 +180,8 @@ public:
|
|||||||
return dm;
|
return dm;
|
||||||
}
|
}
|
||||||
|
|
||||||
void readObjectMatrix(const float time);
|
/** Reads the object matrix and sets up an object transform if animated. */
|
||||||
|
void setupObjectTransform(const float time);
|
||||||
|
|
||||||
void addCacheModifier();
|
void addCacheModifier();
|
||||||
|
|
||||||
@@ -184,7 +192,8 @@ public:
|
|||||||
void incref();
|
void incref();
|
||||||
void decref();
|
void decref();
|
||||||
|
|
||||||
void read_matrix(float mat[4][4], const float time, const float scale, bool &is_constant);
|
void read_matrix(float r_mat[4][4], const float time,
|
||||||
|
const float scale, bool &is_constant);
|
||||||
};
|
};
|
||||||
|
|
||||||
Imath::M44d get_matrix(const Alembic::AbcGeom::IXformSchema &schema, const float time);
|
Imath::M44d get_matrix(const Alembic::AbcGeom::IXformSchema &schema, const float time);
|
||||||
|
@@ -64,7 +64,6 @@ AbcTransformWriter::AbcTransformWriter(Object *ob,
|
|||||||
: AbcObjectWriter(NULL, ob, time_sampling, settings, parent)
|
: AbcObjectWriter(NULL, ob, time_sampling, settings, parent)
|
||||||
{
|
{
|
||||||
m_is_animated = hasAnimation(m_object);
|
m_is_animated = hasAnimation(m_object);
|
||||||
m_parent = NULL;
|
|
||||||
|
|
||||||
if (!m_is_animated) {
|
if (!m_is_animated) {
|
||||||
time_sampling = 0;
|
time_sampling = 0;
|
||||||
@@ -86,26 +85,28 @@ void AbcTransformWriter::do_write()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
float mat[4][4];
|
float yup_mat[4][4];
|
||||||
create_transform_matrix(m_object, mat);
|
create_transform_matrix(m_object, yup_mat);
|
||||||
|
|
||||||
/* Only apply rotation to root camera, parenting will propagate it. */
|
/* Only apply rotation to root camera, parenting will propagate it. */
|
||||||
if (m_object->type == OB_CAMERA && !has_parent_camera(m_object)) {
|
if (m_object->type == OB_CAMERA && !has_parent_camera(m_object)) {
|
||||||
float rot_mat[4][4];
|
float rot_mat[4][4];
|
||||||
axis_angle_to_mat4_single(rot_mat, 'X', -M_PI_2);
|
axis_angle_to_mat4_single(rot_mat, 'X', -M_PI_2);
|
||||||
mul_m4_m4m4(mat, mat, rot_mat);
|
mul_m4_m4m4(yup_mat, yup_mat, rot_mat);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_object->parent) {
|
if (!m_object->parent) {
|
||||||
/* Only apply scaling to root objects, parenting will propagate it. */
|
/* Only apply scaling to root objects, parenting will propagate it. */
|
||||||
|
/* TODO Sybren: when we're exporting as "flat", i.e. non-hierarchial,
|
||||||
|
* we should apply the scale even when the object has a parent
|
||||||
|
* Blender Object. */
|
||||||
float scale_mat[4][4];
|
float scale_mat[4][4];
|
||||||
scale_m4_fl(scale_mat, m_settings.global_scale);
|
scale_m4_fl(scale_mat, m_settings.global_scale);
|
||||||
mul_m4_m4m4(mat, mat, scale_mat);
|
scale_mat[3][3] = m_settings.global_scale; /* also scale translation */
|
||||||
mul_v3_fl(mat[3], m_settings.global_scale);
|
mul_m4_m4m4(yup_mat, yup_mat, scale_mat);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_matrix = convert_matrix(mat);
|
m_matrix = convert_matrix(yup_mat);
|
||||||
|
|
||||||
m_sample.setMatrix(m_matrix);
|
m_sample.setMatrix(m_matrix);
|
||||||
m_schema.set(m_sample);
|
m_schema.set(m_sample);
|
||||||
}
|
}
|
||||||
@@ -133,6 +134,10 @@ bool AbcTransformWriter::hasAnimation(Object * /*ob*/) const
|
|||||||
AbcEmptyReader::AbcEmptyReader(const Alembic::Abc::IObject &object, ImportSettings &settings)
|
AbcEmptyReader::AbcEmptyReader(const Alembic::Abc::IObject &object, ImportSettings &settings)
|
||||||
: AbcObjectReader(object, settings)
|
: AbcObjectReader(object, settings)
|
||||||
{
|
{
|
||||||
|
/* Empties have no data. It makes the import of Alembic files easier to
|
||||||
|
* understand when we name the empty after its name in Alembic. */
|
||||||
|
m_object_name = object.getName();
|
||||||
|
|
||||||
Alembic::AbcGeom::IXform xform(object, Alembic::AbcGeom::kWrapExisting);
|
Alembic::AbcGeom::IXform xform(object, Alembic::AbcGeom::kWrapExisting);
|
||||||
m_schema = xform.getSchema();
|
m_schema = xform.getSchema();
|
||||||
|
|
||||||
@@ -146,6 +151,7 @@ bool AbcEmptyReader::valid() const
|
|||||||
|
|
||||||
void AbcEmptyReader::readObjectData(Main *bmain, float /*time*/)
|
void AbcEmptyReader::readObjectData(Main *bmain, float /*time*/)
|
||||||
{
|
{
|
||||||
m_object = BKE_object_add_only_object(bmain, OB_EMPTY, m_data_name.c_str());
|
m_object = BKE_object_add_only_object(bmain, OB_EMPTY,
|
||||||
|
m_object_name.c_str());
|
||||||
m_object->data = NULL;
|
m_object->data = NULL;
|
||||||
}
|
}
|
||||||
|
@@ -37,7 +37,6 @@ class AbcTransformWriter : public AbcObjectWriter {
|
|||||||
Alembic::Abc::M44d m_matrix;
|
Alembic::Abc::M44d m_matrix;
|
||||||
|
|
||||||
bool m_is_animated;
|
bool m_is_animated;
|
||||||
Object *m_parent;
|
|
||||||
bool m_visible;
|
bool m_visible;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -49,7 +48,6 @@ public:
|
|||||||
|
|
||||||
Alembic::AbcGeom::OXform &alembicXform() { return m_xform;}
|
Alembic::AbcGeom::OXform &alembicXform() { return m_xform;}
|
||||||
virtual Imath::Box3d bounds();
|
virtual Imath::Box3d bounds();
|
||||||
void setParent(Object *p) { m_parent = p; }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual void do_write();
|
virtual void do_write();
|
||||||
|
@@ -42,7 +42,7 @@ extern "C" {
|
|||||||
#include "PIL_time.h"
|
#include "PIL_time.h"
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string get_id_name(Object *ob)
|
std::string get_id_name(const Object * const ob)
|
||||||
{
|
{
|
||||||
if (!ob) {
|
if (!ob) {
|
||||||
return "";
|
return "";
|
||||||
@@ -51,7 +51,7 @@ std::string get_id_name(Object *ob)
|
|||||||
return get_id_name(&ob->id);
|
return get_id_name(&ob->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string get_id_name(ID *id)
|
std::string get_id_name(const ID * const id)
|
||||||
{
|
{
|
||||||
std::string name(id->name + 2);
|
std::string name(id->name + 2);
|
||||||
std::replace(name.begin(), name.end(), ' ', '_');
|
std::replace(name.begin(), name.end(), ' ', '_');
|
||||||
@@ -61,7 +61,6 @@ std::string get_id_name(ID *id)
|
|||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief get_object_dag_path_name returns the name under which the object
|
* @brief get_object_dag_path_name returns the name under which the object
|
||||||
* will be exported in the Alembic file. It is of the form
|
* will be exported in the Alembic file. It is of the form
|
||||||
@@ -71,7 +70,7 @@ std::string get_id_name(ID *id)
|
|||||||
* @param dupli_parent
|
* @param dupli_parent
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
std::string get_object_dag_path_name(Object *ob, Object *dupli_parent)
|
std::string get_object_dag_path_name(const Object * const ob, Object *dupli_parent)
|
||||||
{
|
{
|
||||||
std::string name = get_id_name(ob);
|
std::string name = get_id_name(ob);
|
||||||
|
|
||||||
@@ -121,15 +120,28 @@ void split(const std::string &s, const char delim, std::vector<std::string> &tok
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Create a rotation matrix for each axis from euler angles.
|
void create_swapped_rotation_matrix(
|
||||||
* Euler angles are swaped to change coordinate system. */
|
|
||||||
static void create_rotation_matrix(
|
|
||||||
float rot_x_mat[3][3], float rot_y_mat[3][3],
|
float rot_x_mat[3][3], float rot_y_mat[3][3],
|
||||||
float rot_z_mat[3][3], const float euler[3], const bool to_yup)
|
float rot_z_mat[3][3], const float euler[3],
|
||||||
|
AbcAxisSwapMode mode)
|
||||||
{
|
{
|
||||||
const float rx = euler[0];
|
const float rx = euler[0];
|
||||||
const float ry = (to_yup) ? euler[2] : -euler[2];
|
float ry;
|
||||||
const float rz = (to_yup) ? -euler[1] : euler[1];
|
float rz;
|
||||||
|
|
||||||
|
/* Apply transformation */
|
||||||
|
switch(mode) {
|
||||||
|
case ABC_ZUP_FROM_YUP:
|
||||||
|
ry = -euler[2];
|
||||||
|
rz = euler[1];
|
||||||
|
break;
|
||||||
|
case ABC_YUP_FROM_ZUP:
|
||||||
|
ry = euler[2];
|
||||||
|
rz = -euler[1];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
BLI_assert(false);
|
||||||
|
}
|
||||||
|
|
||||||
unit_m3(rot_x_mat);
|
unit_m3(rot_x_mat);
|
||||||
unit_m3(rot_y_mat);
|
unit_m3(rot_y_mat);
|
||||||
@@ -151,58 +163,70 @@ static void create_rotation_matrix(
|
|||||||
rot_z_mat[1][1] = cos(rz);
|
rot_z_mat[1][1] = cos(rz);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Recompute transform matrix of object in new coordinate system
|
/* Convert matrix from Z=up to Y=up or vice versa. Use yup_mat = zup_mat for in-place conversion. */
|
||||||
* (from Y-Up to Z-Up). */
|
void copy_m44_axis_swap(float dst_mat[4][4], float src_mat[4][4], AbcAxisSwapMode mode)
|
||||||
void create_transform_matrix(float r_mat[4][4])
|
|
||||||
{
|
{
|
||||||
float rot_mat[3][3], rot[3][3], scale_mat[4][4], invmat[4][4], transform_mat[4][4];
|
float dst_rot[3][3], src_rot[3][3], dst_scale_mat[4][4];
|
||||||
float rot_x_mat[3][3], rot_y_mat[3][3], rot_z_mat[3][3];
|
float rot_x_mat[3][3], rot_y_mat[3][3], rot_z_mat[3][3];
|
||||||
float loc[3], scale[3], euler[3];
|
float src_trans[3], dst_scale[3], src_scale[3], euler[3];
|
||||||
|
|
||||||
zero_v3(loc);
|
zero_v3(src_trans);
|
||||||
zero_v3(scale);
|
zero_v3(dst_scale);
|
||||||
|
zero_v3(src_scale);
|
||||||
zero_v3(euler);
|
zero_v3(euler);
|
||||||
unit_m3(rot);
|
unit_m3(src_rot);
|
||||||
unit_m3(rot_mat);
|
unit_m3(dst_rot);
|
||||||
unit_m4(scale_mat);
|
unit_m4(dst_scale_mat);
|
||||||
unit_m4(transform_mat);
|
|
||||||
unit_m4(invmat);
|
|
||||||
|
|
||||||
/* Compute rotation matrix. */
|
/* We assume there is no sheer component and no homogeneous scaling component. */
|
||||||
|
BLI_assert(fabs(src_mat[0][3]) < 2 * FLT_EPSILON);
|
||||||
|
BLI_assert(fabs(src_mat[1][3]) < 2 * FLT_EPSILON);
|
||||||
|
BLI_assert(fabs(src_mat[2][3]) < 2 * FLT_EPSILON);
|
||||||
|
BLI_assert(fabs(src_mat[3][3] - 1.0f) < 2 * FLT_EPSILON);
|
||||||
|
|
||||||
/* Extract location, rotation, and scale from matrix. */
|
/* Extract translation, rotation, and scale form matrix. */
|
||||||
mat4_to_loc_rot_size(loc, rot, scale, r_mat);
|
mat4_to_loc_rot_size(src_trans, src_rot, src_scale, src_mat);
|
||||||
|
|
||||||
/* Get euler angles from rotation matrix. */
|
/* Get euler angles from rotation matrix. */
|
||||||
mat3_to_eulO(euler, ROT_MODE_XYZ, rot);
|
mat3_to_eulO(euler, ROT_MODE_XZY, src_rot);
|
||||||
|
|
||||||
/* Create X, Y, Z rotation matrices from euler angles. */
|
/* Create X, Y, Z rotation matrices from euler angles. */
|
||||||
create_rotation_matrix(rot_x_mat, rot_y_mat, rot_z_mat, euler, false);
|
create_swapped_rotation_matrix(rot_x_mat, rot_y_mat, rot_z_mat, euler, mode);
|
||||||
|
|
||||||
/* Concatenate rotation matrices. */
|
/* Concatenate rotation matrices. */
|
||||||
mul_m3_m3m3(rot_mat, rot_mat, rot_y_mat);
|
mul_m3_m3m3(dst_rot, dst_rot, rot_z_mat);
|
||||||
mul_m3_m3m3(rot_mat, rot_mat, rot_z_mat);
|
mul_m3_m3m3(dst_rot, dst_rot, rot_y_mat);
|
||||||
mul_m3_m3m3(rot_mat, rot_mat, rot_x_mat);
|
mul_m3_m3m3(dst_rot, dst_rot, rot_x_mat);
|
||||||
|
|
||||||
/* Add rotation matrix to transformation matrix. */
|
mat3_to_eulO(euler, ROT_MODE_XZY, dst_rot);
|
||||||
copy_m4_m3(transform_mat, rot_mat);
|
|
||||||
|
|
||||||
/* Add translation to transformation matrix. */
|
/* Start construction of dst_mat from rotation matrix */
|
||||||
copy_zup_from_yup(transform_mat[3], loc);
|
unit_m4(dst_mat);
|
||||||
|
copy_m4_m3(dst_mat, dst_rot);
|
||||||
|
|
||||||
/* Create scale matrix. */
|
/* Apply translation */
|
||||||
scale_mat[0][0] = scale[0];
|
switch(mode) {
|
||||||
scale_mat[1][1] = scale[2];
|
case ABC_ZUP_FROM_YUP:
|
||||||
scale_mat[2][2] = scale[1];
|
copy_zup_from_yup(dst_mat[3], src_trans);
|
||||||
|
break;
|
||||||
|
case ABC_YUP_FROM_ZUP:
|
||||||
|
copy_yup_from_zup(dst_mat[3], src_trans);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
BLI_assert(false);
|
||||||
|
}
|
||||||
|
|
||||||
/* Add scale to transformation matrix. */
|
/* Apply scale matrix. Swaps y and z, but does not
|
||||||
mul_m4_m4m4(transform_mat, transform_mat, scale_mat);
|
* negate like translation does. */
|
||||||
|
dst_scale[0] = src_scale[0];
|
||||||
|
dst_scale[1] = src_scale[2];
|
||||||
|
dst_scale[2] = src_scale[1];
|
||||||
|
|
||||||
copy_m4_m4(r_mat, transform_mat);
|
size_to_mat4(dst_scale_mat, dst_scale);
|
||||||
|
mul_m4_m4m4(dst_mat, dst_mat, dst_scale_mat);
|
||||||
}
|
}
|
||||||
|
|
||||||
void convert_matrix(const Imath::M44d &xform, Object *ob,
|
void convert_matrix(const Imath::M44d &xform, Object *ob, float r_mat[4][4])
|
||||||
float r_mat[4][4], float scale, bool has_alembic_parent)
|
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 4; ++i) {
|
for (int i = 0; i < 4; ++i) {
|
||||||
for (int j = 0; j < 4; ++j) {
|
for (int j = 0; j < 4; ++j) {
|
||||||
@@ -216,207 +240,29 @@ void convert_matrix(const Imath::M44d &xform, Object *ob,
|
|||||||
mul_m4_m4m4(r_mat, r_mat, cam_to_yup);
|
mul_m4_m4m4(r_mat, r_mat, cam_to_yup);
|
||||||
}
|
}
|
||||||
|
|
||||||
create_transform_matrix(r_mat);
|
copy_m44_axis_swap(r_mat, r_mat, ABC_ZUP_FROM_YUP);
|
||||||
|
|
||||||
if (ob->parent) {
|
|
||||||
mul_m4_m4m4(r_mat, ob->parent->obmat, r_mat);
|
|
||||||
}
|
|
||||||
/* TODO(kevin) */
|
|
||||||
else if (!has_alembic_parent) {
|
|
||||||
/* Only apply scaling to root objects, parenting will propagate it. */
|
|
||||||
float scale_mat[4][4];
|
|
||||||
scale_m4_fl(scale_mat, scale);
|
|
||||||
mul_m4_m4m4(r_mat, r_mat, scale_mat);
|
|
||||||
mul_v3_fl(r_mat[3], scale);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Recompute transform matrix of object in new coordinate system (from Z-Up to Y-Up). */
|
/* Recompute transform matrix of object in new coordinate system
|
||||||
void create_transform_matrix(Object *obj, float transform_mat[4][4])
|
* (from Z-Up to Y-Up). */
|
||||||
|
void create_transform_matrix(Object *obj, float r_yup_mat[4][4])
|
||||||
{
|
{
|
||||||
float rot_mat[3][3], rot[3][3], scale_mat[4][4], invmat[4][4], mat[4][4];
|
float zup_mat[4][4];
|
||||||
float rot_x_mat[3][3], rot_y_mat[3][3], rot_z_mat[3][3];
|
|
||||||
float loc[3], scale[3], euler[3];
|
|
||||||
|
|
||||||
zero_v3(loc);
|
|
||||||
zero_v3(scale);
|
|
||||||
zero_v3(euler);
|
|
||||||
unit_m3(rot);
|
|
||||||
unit_m3(rot_mat);
|
|
||||||
unit_m4(scale_mat);
|
|
||||||
unit_m4(transform_mat);
|
|
||||||
unit_m4(invmat);
|
|
||||||
unit_m4(mat);
|
|
||||||
|
|
||||||
/* get local matrix. */
|
/* get local matrix. */
|
||||||
|
/* TODO Sybren: when we're exporting as "flat", i.e. non-hierarchial,
|
||||||
|
* we should export the world matrix even when the object has a parent
|
||||||
|
* Blender Object. */
|
||||||
if (obj->parent) {
|
if (obj->parent) {
|
||||||
invert_m4_m4(invmat, obj->parent->obmat);
|
/* Note that this produces another matrix than the local matrix, due to
|
||||||
mul_m4_m4m4(mat, invmat, obj->obmat);
|
* constraints and modifiers as well as the obj->parentinv matrix. */
|
||||||
|
invert_m4_m4(obj->parent->imat, obj->parent->obmat);
|
||||||
|
mul_m4_m4m4(zup_mat, obj->parent->imat, obj->obmat);
|
||||||
|
copy_m44_axis_swap(r_yup_mat, zup_mat, ABC_YUP_FROM_ZUP);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
copy_m4_m4(mat, obj->obmat);
|
copy_m44_axis_swap(r_yup_mat, obj->obmat, ABC_YUP_FROM_ZUP);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Compute rotation matrix. */
|
|
||||||
switch (obj->rotmode) {
|
|
||||||
case ROT_MODE_AXISANGLE:
|
|
||||||
{
|
|
||||||
/* Get euler angles from axis angle rotation. */
|
|
||||||
axis_angle_to_eulO(euler, ROT_MODE_XYZ, obj->rotAxis, obj->rotAngle);
|
|
||||||
|
|
||||||
/* Create X, Y, Z rotation matrices from euler angles. */
|
|
||||||
create_rotation_matrix(rot_x_mat, rot_y_mat, rot_z_mat, euler, true);
|
|
||||||
|
|
||||||
/* Concatenate rotation matrices. */
|
|
||||||
mul_m3_m3m3(rot_mat, rot_mat, rot_y_mat);
|
|
||||||
mul_m3_m3m3(rot_mat, rot_mat, rot_z_mat);
|
|
||||||
mul_m3_m3m3(rot_mat, rot_mat, rot_x_mat);
|
|
||||||
|
|
||||||
/* Extract location and scale from matrix. */
|
|
||||||
mat4_to_loc_rot_size(loc, rot, scale, mat);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ROT_MODE_QUAT:
|
|
||||||
{
|
|
||||||
float q[4];
|
|
||||||
copy_v4_v4(q, obj->quat);
|
|
||||||
|
|
||||||
/* Swap axis. */
|
|
||||||
q[2] = obj->quat[3];
|
|
||||||
q[3] = -obj->quat[2];
|
|
||||||
|
|
||||||
/* Compute rotation matrix from quaternion. */
|
|
||||||
quat_to_mat3(rot_mat, q);
|
|
||||||
|
|
||||||
/* Extract location and scale from matrix. */
|
|
||||||
mat4_to_loc_rot_size(loc, rot, scale, mat);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ROT_MODE_XYZ:
|
|
||||||
{
|
|
||||||
/* Extract location, rotation, and scale form matrix. */
|
|
||||||
mat4_to_loc_rot_size(loc, rot, scale, mat);
|
|
||||||
|
|
||||||
/* Get euler angles from rotation matrix. */
|
|
||||||
mat3_to_eulO(euler, ROT_MODE_XYZ, rot);
|
|
||||||
|
|
||||||
/* Create X, Y, Z rotation matrices from euler angles. */
|
|
||||||
create_rotation_matrix(rot_x_mat, rot_y_mat, rot_z_mat, euler, true);
|
|
||||||
|
|
||||||
/* Concatenate rotation matrices. */
|
|
||||||
mul_m3_m3m3(rot_mat, rot_mat, rot_y_mat);
|
|
||||||
mul_m3_m3m3(rot_mat, rot_mat, rot_z_mat);
|
|
||||||
mul_m3_m3m3(rot_mat, rot_mat, rot_x_mat);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ROT_MODE_XZY:
|
|
||||||
{
|
|
||||||
/* Extract location, rotation, and scale form matrix. */
|
|
||||||
mat4_to_loc_rot_size(loc, rot, scale, mat);
|
|
||||||
|
|
||||||
/* Get euler angles from rotation matrix. */
|
|
||||||
mat3_to_eulO(euler, ROT_MODE_XZY, rot);
|
|
||||||
|
|
||||||
/* Create X, Y, Z rotation matrices from euler angles. */
|
|
||||||
create_rotation_matrix(rot_x_mat, rot_y_mat, rot_z_mat, euler, true);
|
|
||||||
|
|
||||||
/* Concatenate rotation matrices. */
|
|
||||||
mul_m3_m3m3(rot_mat, rot_mat, rot_z_mat);
|
|
||||||
mul_m3_m3m3(rot_mat, rot_mat, rot_y_mat);
|
|
||||||
mul_m3_m3m3(rot_mat, rot_mat, rot_x_mat);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ROT_MODE_YXZ:
|
|
||||||
{
|
|
||||||
/* Extract location, rotation, and scale form matrix. */
|
|
||||||
mat4_to_loc_rot_size(loc, rot, scale, mat);
|
|
||||||
|
|
||||||
/* Get euler angles from rotation matrix. */
|
|
||||||
mat3_to_eulO(euler, ROT_MODE_YXZ, rot);
|
|
||||||
|
|
||||||
/* Create X, Y, Z rotation matrices from euler angles. */
|
|
||||||
create_rotation_matrix(rot_x_mat, rot_y_mat, rot_z_mat, euler, true);
|
|
||||||
|
|
||||||
/* Concatenate rotation matrices. */
|
|
||||||
mul_m3_m3m3(rot_mat, rot_mat, rot_y_mat);
|
|
||||||
mul_m3_m3m3(rot_mat, rot_mat, rot_x_mat);
|
|
||||||
mul_m3_m3m3(rot_mat, rot_mat, rot_z_mat);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ROT_MODE_YZX:
|
|
||||||
{
|
|
||||||
/* Extract location, rotation, and scale form matrix. */
|
|
||||||
mat4_to_loc_rot_size(loc, rot, scale, mat);
|
|
||||||
|
|
||||||
/* Get euler angles from rotation matrix. */
|
|
||||||
mat3_to_eulO(euler, ROT_MODE_YZX, rot);
|
|
||||||
|
|
||||||
/* Create X, Y, Z rotation matrices from euler angles. */
|
|
||||||
create_rotation_matrix(rot_x_mat, rot_y_mat, rot_z_mat, euler, true);
|
|
||||||
|
|
||||||
/* Concatenate rotation matrices. */
|
|
||||||
mul_m3_m3m3(rot_mat, rot_mat, rot_x_mat);
|
|
||||||
mul_m3_m3m3(rot_mat, rot_mat, rot_y_mat);
|
|
||||||
mul_m3_m3m3(rot_mat, rot_mat, rot_z_mat);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ROT_MODE_ZXY:
|
|
||||||
{
|
|
||||||
/* Extract location, rotation, and scale form matrix. */
|
|
||||||
mat4_to_loc_rot_size(loc, rot, scale, mat);
|
|
||||||
|
|
||||||
/* Get euler angles from rotation matrix. */
|
|
||||||
mat3_to_eulO(euler, ROT_MODE_ZXY, rot);
|
|
||||||
|
|
||||||
/* Create X, Y, Z rotation matrices from euler angles. */
|
|
||||||
create_rotation_matrix(rot_x_mat, rot_y_mat, rot_z_mat, euler, true);
|
|
||||||
|
|
||||||
/* Concatenate rotation matrices. */
|
|
||||||
mul_m3_m3m3(rot_mat, rot_mat, rot_z_mat);
|
|
||||||
mul_m3_m3m3(rot_mat, rot_mat, rot_x_mat);
|
|
||||||
mul_m3_m3m3(rot_mat, rot_mat, rot_y_mat);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ROT_MODE_ZYX:
|
|
||||||
{
|
|
||||||
/* Extract location, rotation, and scale form matrix. */
|
|
||||||
mat4_to_loc_rot_size(loc, rot, scale, mat);
|
|
||||||
|
|
||||||
/* Get euler angles from rotation matrix. */
|
|
||||||
mat3_to_eulO(euler, ROT_MODE_ZYX, rot);
|
|
||||||
|
|
||||||
/* Create X, Y, Z rotation matrices from euler angles. */
|
|
||||||
create_rotation_matrix(rot_x_mat, rot_y_mat, rot_z_mat, euler, true);
|
|
||||||
|
|
||||||
/* Concatenate rotation matrices. */
|
|
||||||
mul_m3_m3m3(rot_mat, rot_mat, rot_x_mat);
|
|
||||||
mul_m3_m3m3(rot_mat, rot_mat, rot_z_mat);
|
|
||||||
mul_m3_m3m3(rot_mat, rot_mat, rot_y_mat);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Add rotation matrix to transformation matrix. */
|
|
||||||
copy_m4_m3(transform_mat, rot_mat);
|
|
||||||
|
|
||||||
/* Add translation to transformation matrix. */
|
|
||||||
copy_yup_from_zup(transform_mat[3], loc);
|
|
||||||
|
|
||||||
/* Create scale matrix. */
|
|
||||||
scale_mat[0][0] = scale[0];
|
|
||||||
scale_mat[1][1] = scale[2];
|
|
||||||
scale_mat[2][2] = scale[1];
|
|
||||||
|
|
||||||
/* Add scale to transformation matrix. */
|
|
||||||
mul_m4_m4m4(transform_mat, transform_mat, scale_mat);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool has_property(const Alembic::Abc::ICompoundProperty &prop, const std::string &name)
|
bool has_property(const Alembic::Abc::ICompoundProperty &prop, const std::string &name)
|
||||||
@@ -509,7 +355,10 @@ AbcObjectReader *create_reader(const Alembic::AbcGeom::IObject &object, ImportSe
|
|||||||
reader = new AbcCurveReader(object, settings);
|
reader = new AbcCurveReader(object, settings);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
assert(false);
|
std::cerr << "Alembic: unknown how to handle objects of schema "
|
||||||
|
<< md.get("schemaObjTitle")
|
||||||
|
<< ", skipping object "
|
||||||
|
<< object.getFullName() << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
return reader;
|
return reader;
|
||||||
|
@@ -32,6 +32,11 @@
|
|||||||
# define ABC_INLINE static inline
|
# define ABC_INLINE static inline
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The CacheReader struct is only used for anonymous pointers,
|
||||||
|
* to interface between C and C++ code. This library only creates
|
||||||
|
* pointers to AbcObjectReader (or subclasses thereof).
|
||||||
|
*/
|
||||||
struct CacheReader {
|
struct CacheReader {
|
||||||
int unused;
|
int unused;
|
||||||
};
|
};
|
||||||
@@ -45,15 +50,14 @@ struct ID;
|
|||||||
struct Object;
|
struct Object;
|
||||||
struct Base;
|
struct Base;
|
||||||
|
|
||||||
std::string get_id_name(ID *id);
|
std::string get_id_name(const ID * const id);
|
||||||
std::string get_id_name(Object *ob);
|
std::string get_id_name(const Object * const ob);
|
||||||
std::string get_object_dag_path_name(Object *ob, Object *dupli_parent);
|
std::string get_object_dag_path_name(const Object * const ob, Object *dupli_parent);
|
||||||
|
|
||||||
bool object_selected(const Base * const ob_base);
|
bool object_selected(const Base * const ob_base);
|
||||||
|
|
||||||
Imath::M44d convert_matrix(float mat[4][4]);
|
Imath::M44d convert_matrix(float mat[4][4]);
|
||||||
void create_transform_matrix(float r_mat[4][4]);
|
void create_transform_matrix(Object *obj, float r_transform_mat[4][4]);
|
||||||
void create_transform_matrix(Object *obj, float transform_mat[4][4]);
|
|
||||||
|
|
||||||
void split(const std::string &s, const char delim, std::vector<std::string> &tokens);
|
void split(const std::string &s, const char delim, std::vector<std::string> &tokens);
|
||||||
|
|
||||||
@@ -64,8 +68,7 @@ bool begins_with(const TContainer &input, const TContainer &match)
|
|||||||
&& std::equal(match.begin(), match.end(), input.begin());
|
&& std::equal(match.begin(), match.end(), input.begin());
|
||||||
}
|
}
|
||||||
|
|
||||||
void convert_matrix(const Imath::M44d &xform, Object *ob,
|
void convert_matrix(const Imath::M44d &xform, Object *ob, float r_mat[4][4]);
|
||||||
float r_mat[4][4], float scale, bool has_alembic_parent = false);
|
|
||||||
|
|
||||||
template <typename Schema>
|
template <typename Schema>
|
||||||
void get_min_max_time_ex(const Schema &schema, chrono_t &min, chrono_t &max)
|
void get_min_max_time_ex(const Schema &schema, chrono_t &min, chrono_t &max)
|
||||||
@@ -118,34 +121,54 @@ AbcObjectReader *create_reader(const Alembic::AbcGeom::IObject &object, ImportSe
|
|||||||
|
|
||||||
ABC_INLINE void copy_zup_from_yup(float zup[3], const float yup[3])
|
ABC_INLINE void copy_zup_from_yup(float zup[3], const float yup[3])
|
||||||
{
|
{
|
||||||
|
const float old_yup1 = yup[1]; /* in case zup == yup */
|
||||||
zup[0] = yup[0];
|
zup[0] = yup[0];
|
||||||
zup[1] = -yup[2];
|
zup[1] = -yup[2];
|
||||||
zup[2] = yup[1];
|
zup[2] = old_yup1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ABC_INLINE void copy_zup_from_yup(short zup[3], const short yup[3])
|
ABC_INLINE void copy_zup_from_yup(short zup[3], const short yup[3])
|
||||||
{
|
{
|
||||||
|
const short old_yup1 = yup[1]; /* in case zup == yup */
|
||||||
zup[0] = yup[0];
|
zup[0] = yup[0];
|
||||||
zup[1] = -yup[2];
|
zup[1] = -yup[2];
|
||||||
zup[2] = yup[1];
|
zup[2] = old_yup1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Copy from Z-up to Y-up. */
|
/* Copy from Z-up to Y-up. */
|
||||||
|
|
||||||
ABC_INLINE void copy_yup_from_zup(float yup[3], const float zup[3])
|
ABC_INLINE void copy_yup_from_zup(float yup[3], const float zup[3])
|
||||||
{
|
{
|
||||||
|
const float old_zup1 = zup[1]; /* in case yup == zup */
|
||||||
yup[0] = zup[0];
|
yup[0] = zup[0];
|
||||||
yup[1] = zup[2];
|
yup[1] = zup[2];
|
||||||
yup[2] = -zup[1];
|
yup[2] = -old_zup1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ABC_INLINE void copy_yup_from_zup(short yup[3], const short zup[3])
|
ABC_INLINE void copy_yup_from_zup(short yup[3], const short zup[3])
|
||||||
{
|
{
|
||||||
|
const short old_zup1 = zup[1]; /* in case yup == zup */
|
||||||
yup[0] = zup[0];
|
yup[0] = zup[0];
|
||||||
yup[1] = zup[2];
|
yup[1] = zup[2];
|
||||||
yup[2] = -zup[1];
|
yup[2] = -old_zup1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Names are given in (dst, src) order, just like
|
||||||
|
* the parameters of copy_m44_axis_swap() */
|
||||||
|
typedef enum {
|
||||||
|
ABC_ZUP_FROM_YUP = 1,
|
||||||
|
ABC_YUP_FROM_ZUP = 2,
|
||||||
|
} AbcAxisSwapMode;
|
||||||
|
|
||||||
|
/* Create a rotation matrix for each axis from euler angles.
|
||||||
|
* Euler angles are swaped to change coordinate system. */
|
||||||
|
void create_swapped_rotation_matrix(
|
||||||
|
float rot_x_mat[3][3], float rot_y_mat[3][3],
|
||||||
|
float rot_z_mat[3][3], const float euler[3],
|
||||||
|
AbcAxisSwapMode mode);
|
||||||
|
|
||||||
|
void copy_m44_axis_swap(float dst_mat[4][4], float src_mat[4][4], AbcAxisSwapMode mode);
|
||||||
|
|
||||||
/* *************************** */
|
/* *************************** */
|
||||||
|
|
||||||
#undef ABC_DEBUG_TIME
|
#undef ABC_DEBUG_TIME
|
||||||
|
@@ -21,6 +21,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "../ABC_alembic.h"
|
#include "../ABC_alembic.h"
|
||||||
|
#include <boost/foreach.hpp>
|
||||||
|
|
||||||
#include <Alembic/AbcMaterial/IMaterial.h>
|
#include <Alembic/AbcMaterial/IMaterial.h>
|
||||||
|
|
||||||
@@ -122,91 +123,62 @@ ABC_INLINE AbcArchiveHandle *handle_from_archive(ArchiveReader *archive)
|
|||||||
|
|
||||||
/* NOTE: this function is similar to visit_objects below, need to keep them in
|
/* NOTE: this function is similar to visit_objects below, need to keep them in
|
||||||
* sync. */
|
* sync. */
|
||||||
static void gather_objects_paths(const IObject &object, ListBase *object_paths)
|
static bool gather_objects_paths(const IObject &object, ListBase *object_paths)
|
||||||
{
|
{
|
||||||
if (!object.valid()) {
|
if (!object.valid()) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < object.getNumChildren(); ++i) {
|
|
||||||
IObject child = object.getChild(i);
|
|
||||||
|
|
||||||
if (!child.valid()) {
|
size_t children_claiming_this_object = 0;
|
||||||
continue;
|
size_t num_children = object.getNumChildren();
|
||||||
}
|
|
||||||
|
|
||||||
bool get_path = false;
|
for (size_t i = 0; i < num_children; ++i) {
|
||||||
|
bool child_claims_this_object = gather_objects_paths(object.getChild(i), object_paths);
|
||||||
|
children_claiming_this_object += child_claims_this_object ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
const MetaData &md = child.getMetaData();
|
const MetaData &md = object.getMetaData();
|
||||||
|
bool get_path = false;
|
||||||
|
bool parent_is_part_of_this_object = false;
|
||||||
|
|
||||||
if (IXform::matches(md)) {
|
if (!object.getParent()) {
|
||||||
/* Check whether or not this object is a Maya locator, which is
|
/* The root itself is not an object we should import. */
|
||||||
* similar to empties used as parent object in Blender. */
|
}
|
||||||
if (has_property(child.getProperties(), "locator")) {
|
else if (IXform::matches(md)) {
|
||||||
get_path = true;
|
if (has_property(object.getProperties(), "locator")) {
|
||||||
}
|
|
||||||
else {
|
|
||||||
/* Avoid creating an empty object if the child of this transform
|
|
||||||
* is not a transform (that is an empty). */
|
|
||||||
if (child.getNumChildren() == 1) {
|
|
||||||
if (IXform::matches(child.getChild(0).getMetaData())) {
|
|
||||||
get_path = true;
|
|
||||||
}
|
|
||||||
#if 0
|
|
||||||
else {
|
|
||||||
std::cerr << "Skipping " << child.getFullName() << '\n';
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
get_path = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (IPolyMesh::matches(md)) {
|
|
||||||
get_path = true;
|
|
||||||
}
|
|
||||||
else if (ISubD::matches(md)) {
|
|
||||||
get_path = true;
|
|
||||||
}
|
|
||||||
else if (INuPatch::matches(md)) {
|
|
||||||
#ifdef USE_NURBS
|
|
||||||
get_path = true;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
else if (ICamera::matches(md)) {
|
|
||||||
get_path = true;
|
|
||||||
}
|
|
||||||
else if (IPoints::matches(md)) {
|
|
||||||
get_path = true;
|
|
||||||
}
|
|
||||||
else if (IMaterial::matches(md)) {
|
|
||||||
/* Pass for now. */
|
|
||||||
}
|
|
||||||
else if (ILight::matches(md)) {
|
|
||||||
/* Pass for now. */
|
|
||||||
}
|
|
||||||
else if (IFaceSet::matches(md)) {
|
|
||||||
/* Pass, those are handled in the mesh reader. */
|
|
||||||
}
|
|
||||||
else if (ICurves::matches(md)) {
|
|
||||||
get_path = true;
|
get_path = true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
assert(false);
|
get_path = children_claiming_this_object == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (get_path) {
|
/* Transforms are never "data" for their parent. */
|
||||||
AlembicObjectPath *abc_path = static_cast<AlembicObjectPath *>(
|
parent_is_part_of_this_object = false;
|
||||||
MEM_callocN(sizeof(AlembicObjectPath), "AlembicObjectPath"));
|
|
||||||
|
|
||||||
BLI_strncpy(abc_path->path, child.getFullName().c_str(), PATH_MAX);
|
|
||||||
|
|
||||||
BLI_addtail(object_paths, abc_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
gather_objects_paths(child, object_paths);
|
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
/* These types are "data" for their parent. */
|
||||||
|
get_path =
|
||||||
|
IPolyMesh::matches(md) ||
|
||||||
|
ISubD::matches(md) ||
|
||||||
|
#ifdef USE_NURBS
|
||||||
|
INuPatch::matches(md) ||
|
||||||
|
#endif
|
||||||
|
ICamera::matches(md) ||
|
||||||
|
IPoints::matches(md) ||
|
||||||
|
ICurves::matches(md);
|
||||||
|
parent_is_part_of_this_object = get_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (get_path) {
|
||||||
|
void *abc_path_void = MEM_callocN(sizeof(AlembicObjectPath), "AlembicObjectPath");
|
||||||
|
AlembicObjectPath *abc_path = static_cast<AlembicObjectPath *>(abc_path_void);
|
||||||
|
|
||||||
|
BLI_strncpy(abc_path->path, object.getFullName().c_str(), PATH_MAX);
|
||||||
|
BLI_addtail(object_paths, abc_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent_is_part_of_this_object;
|
||||||
}
|
}
|
||||||
|
|
||||||
AbcArchiveHandle *ABC_create_handle(const char *filename, ListBase *object_paths)
|
AbcArchiveHandle *ABC_create_handle(const char *filename, ListBase *object_paths)
|
||||||
@@ -414,114 +386,208 @@ void ABC_export(
|
|||||||
|
|
||||||
/* ********************** Import file ********************** */
|
/* ********************** Import file ********************** */
|
||||||
|
|
||||||
static void visit_object(const IObject &object,
|
/**
|
||||||
std::vector<AbcObjectReader *> &readers,
|
* Generates an AbcObjectReader for this Alembic object and its children.
|
||||||
GHash *parent_map,
|
*
|
||||||
ImportSettings &settings)
|
* \param object The Alembic IObject to visit.
|
||||||
|
* \param readers The created AbcObjectReader * will be appended to this vector.
|
||||||
|
* \param settings Import settings, not used directly but passed to the
|
||||||
|
* AbcObjectReader subclass constructors.
|
||||||
|
* \param r_assign_as_parent Return parameter, contains a list of reader
|
||||||
|
* pointers, whose parent pointer should still be set.
|
||||||
|
* This is filled when this call to visit_object() didn't create
|
||||||
|
* a reader that should be the parent.
|
||||||
|
* \return A pair of boolean and reader pointer. The boolean indicates whether
|
||||||
|
* this IObject claims its parent as part of the same object
|
||||||
|
* (for example an IPolyMesh object would claim its parent, as the mesh
|
||||||
|
* is interpreted as the object's data, and the parent IXform as its
|
||||||
|
* Blender object). The pointer is the AbcObjectReader that represents
|
||||||
|
* the IObject parameter.
|
||||||
|
*
|
||||||
|
* NOTE: this function is similar to gather_object_paths above, need to keep
|
||||||
|
* them in sync. */
|
||||||
|
static std::pair<bool, AbcObjectReader *> visit_object(
|
||||||
|
const IObject &object,
|
||||||
|
AbcObjectReader::ptr_vector &readers,
|
||||||
|
ImportSettings &settings,
|
||||||
|
AbcObjectReader::ptr_vector &r_assign_as_parent)
|
||||||
{
|
{
|
||||||
|
const std::string & full_name = object.getFullName();
|
||||||
|
|
||||||
if (!object.valid()) {
|
if (!object.valid()) {
|
||||||
return;
|
std::cerr << " - "
|
||||||
|
<< full_name
|
||||||
|
<< ": object is invalid, skipping it and all its children.\n";
|
||||||
|
return std::make_pair(false, static_cast<AbcObjectReader *>(NULL));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < object.getNumChildren(); ++i) {
|
/* The interpretation of data by the children determine the role of this
|
||||||
IObject child = object.getChild(i);
|
* object. This is especially important for Xform objects, as they can be
|
||||||
|
* either part of a Blender object or a Blender object (Empty) themselves.
|
||||||
|
*/
|
||||||
|
size_t children_claiming_this_object = 0;
|
||||||
|
size_t num_children = object.getNumChildren();
|
||||||
|
AbcObjectReader::ptr_vector claiming_child_readers;
|
||||||
|
AbcObjectReader::ptr_vector nonclaiming_child_readers;
|
||||||
|
AbcObjectReader::ptr_vector assign_as_parent;
|
||||||
|
for (size_t i = 0; i < num_children; ++i) {
|
||||||
|
const IObject ichild = object.getChild(i);
|
||||||
|
|
||||||
if (!child.valid()) {
|
/* TODO: When we only support C++11, use std::tie() instead. */
|
||||||
continue;
|
std::pair<bool, AbcObjectReader *> child_result;
|
||||||
}
|
child_result = visit_object(ichild, readers, settings, assign_as_parent);
|
||||||
|
|
||||||
AbcObjectReader *reader = NULL;
|
bool child_claims_this_object = child_result.first;
|
||||||
|
AbcObjectReader *child_reader = child_result.second;
|
||||||
|
|
||||||
const MetaData &md = child.getMetaData();
|
if (child_reader == NULL) {
|
||||||
|
BLI_assert(!child_claims_this_object);
|
||||||
if (IXform::matches(md)) {
|
|
||||||
bool create_xform = false;
|
|
||||||
|
|
||||||
/* Check whether or not this object is a Maya locator, which is
|
|
||||||
* similar to empties used as parent object in Blender. */
|
|
||||||
if (has_property(child.getProperties(), "locator")) {
|
|
||||||
create_xform = true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
/* Avoid creating an empty object if the child of this transform
|
|
||||||
* is not a transform (that is an empty). */
|
|
||||||
if (child.getNumChildren() == 1) {
|
|
||||||
if (IXform::matches(child.getChild(0).getMetaData())) {
|
|
||||||
create_xform = true;
|
|
||||||
}
|
|
||||||
#if 0
|
|
||||||
else {
|
|
||||||
std::cerr << "Skipping " << child.getFullName() << '\n';
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
create_xform = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (create_xform) {
|
|
||||||
reader = new AbcEmptyReader(child, settings);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (IPolyMesh::matches(md)) {
|
|
||||||
reader = new AbcMeshReader(child, settings);
|
|
||||||
}
|
|
||||||
else if (ISubD::matches(md)) {
|
|
||||||
reader = new AbcSubDReader(child, settings);
|
|
||||||
}
|
|
||||||
else if (INuPatch::matches(md)) {
|
|
||||||
#ifdef USE_NURBS
|
|
||||||
/* TODO(kevin): importing cyclic NURBS from other software crashes
|
|
||||||
* at the moment. This is due to the fact that NURBS in other
|
|
||||||
* software have duplicated points which causes buffer overflows in
|
|
||||||
* Blender. Need to figure out exactly how these points are
|
|
||||||
* duplicated, in all cases (cyclic U, cyclic V, and cyclic UV).
|
|
||||||
* Until this is fixed, disabling NURBS reading. */
|
|
||||||
reader = new AbcNurbsReader(child, settings);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
else if (ICamera::matches(md)) {
|
|
||||||
reader = new AbcCameraReader(child, settings);
|
|
||||||
}
|
|
||||||
else if (IPoints::matches(md)) {
|
|
||||||
reader = new AbcPointsReader(child, settings);
|
|
||||||
}
|
|
||||||
else if (IMaterial::matches(md)) {
|
|
||||||
/* Pass for now. */
|
|
||||||
}
|
|
||||||
else if (ILight::matches(md)) {
|
|
||||||
/* Pass for now. */
|
|
||||||
}
|
|
||||||
else if (IFaceSet::matches(md)) {
|
|
||||||
/* Pass, those are handled in the mesh reader. */
|
|
||||||
}
|
|
||||||
else if (ICurves::matches(md)) {
|
|
||||||
reader = new AbcCurveReader(child, settings);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
assert(false);
|
if (child_claims_this_object) {
|
||||||
|
claiming_child_readers.push_back(child_reader);
|
||||||
|
} else {
|
||||||
|
nonclaiming_child_readers.push_back(child_reader);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reader) {
|
children_claiming_this_object += child_claims_this_object ? 1 : 0;
|
||||||
readers.push_back(reader);
|
|
||||||
reader->incref();
|
|
||||||
|
|
||||||
AlembicObjectPath *abc_path = static_cast<AlembicObjectPath *>(
|
|
||||||
MEM_callocN(sizeof(AlembicObjectPath), "AlembicObjectPath"));
|
|
||||||
|
|
||||||
BLI_strncpy(abc_path->path, child.getFullName().c_str(), PATH_MAX);
|
|
||||||
|
|
||||||
BLI_addtail(&settings.cache_file->object_paths, abc_path);
|
|
||||||
|
|
||||||
/* Cast to `void *` explicitly to avoid compiler errors because it
|
|
||||||
* is a `const char *` which the compiler cast to `const void *`
|
|
||||||
* instead of the expected `void *`. */
|
|
||||||
BLI_ghash_insert(parent_map, (void *)child.getFullName().c_str(), reader);
|
|
||||||
}
|
|
||||||
|
|
||||||
visit_object(child, readers, parent_map, settings);
|
|
||||||
}
|
}
|
||||||
|
BLI_assert(children_claiming_this_object == claiming_child_readers.size());
|
||||||
|
|
||||||
|
AbcObjectReader *reader = NULL;
|
||||||
|
const MetaData &md = object.getMetaData();
|
||||||
|
bool parent_is_part_of_this_object = false;
|
||||||
|
|
||||||
|
if (!object.getParent()) {
|
||||||
|
/* The root itself is not an object we should import. */
|
||||||
|
}
|
||||||
|
else if (IXform::matches(md)) {
|
||||||
|
bool create_empty;
|
||||||
|
|
||||||
|
/* An xform can either be a Blender Object (if it contains a mesh, for
|
||||||
|
* example), but it can also be an Empty. Its correct translation to
|
||||||
|
* Blender's data model depends on its children. */
|
||||||
|
|
||||||
|
/* Check whether or not this object is a Maya locator, which is
|
||||||
|
* similar to empties used as parent object in Blender. */
|
||||||
|
if (has_property(object.getProperties(), "locator")) {
|
||||||
|
create_empty = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
create_empty = claiming_child_readers.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (create_empty) {
|
||||||
|
reader = new AbcEmptyReader(object, settings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (IPolyMesh::matches(md)) {
|
||||||
|
reader = new AbcMeshReader(object, settings);
|
||||||
|
parent_is_part_of_this_object = true;
|
||||||
|
}
|
||||||
|
else if (ISubD::matches(md)) {
|
||||||
|
reader = new AbcSubDReader(object, settings);
|
||||||
|
parent_is_part_of_this_object = true;
|
||||||
|
}
|
||||||
|
else if (INuPatch::matches(md)) {
|
||||||
|
#ifdef USE_NURBS
|
||||||
|
/* TODO(kevin): importing cyclic NURBS from other software crashes
|
||||||
|
* at the moment. This is due to the fact that NURBS in other
|
||||||
|
* software have duplicated points which causes buffer overflows in
|
||||||
|
* Blender. Need to figure out exactly how these points are
|
||||||
|
* duplicated, in all cases (cyclic U, cyclic V, and cyclic UV).
|
||||||
|
* Until this is fixed, disabling NURBS reading. */
|
||||||
|
reader = new AbcNurbsReader(object, settings);
|
||||||
|
parent_is_part_of_this_object = true;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else if (ICamera::matches(md)) {
|
||||||
|
reader = new AbcCameraReader(object, settings);
|
||||||
|
parent_is_part_of_this_object = true;
|
||||||
|
}
|
||||||
|
else if (IPoints::matches(md)) {
|
||||||
|
reader = new AbcPointsReader(object, settings);
|
||||||
|
parent_is_part_of_this_object = true;
|
||||||
|
}
|
||||||
|
else if (IMaterial::matches(md)) {
|
||||||
|
/* Pass for now. */
|
||||||
|
}
|
||||||
|
else if (ILight::matches(md)) {
|
||||||
|
/* Pass for now. */
|
||||||
|
}
|
||||||
|
else if (IFaceSet::matches(md)) {
|
||||||
|
/* Pass, those are handled in the mesh reader. */
|
||||||
|
}
|
||||||
|
else if (ICurves::matches(md)) {
|
||||||
|
reader = new AbcCurveReader(object, settings);
|
||||||
|
parent_is_part_of_this_object = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
std::cerr << "Alembic object " << full_name
|
||||||
|
<< " is of unsupported schema type '"
|
||||||
|
<< object.getMetaData().get("schemaObjTitle") << "'"
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reader) {
|
||||||
|
/* We have created a reader, which should imply that this object is
|
||||||
|
* not claimed as part of any child Alembic object. */
|
||||||
|
BLI_assert(claiming_child_readers.empty());
|
||||||
|
|
||||||
|
readers.push_back(reader);
|
||||||
|
reader->incref();
|
||||||
|
|
||||||
|
AlembicObjectPath *abc_path = static_cast<AlembicObjectPath *>(
|
||||||
|
MEM_callocN(sizeof(AlembicObjectPath), "AlembicObjectPath"));
|
||||||
|
BLI_strncpy(abc_path->path, full_name.c_str(), PATH_MAX);
|
||||||
|
BLI_addtail(&settings.cache_file->object_paths, abc_path);
|
||||||
|
|
||||||
|
/* We can now assign this reader as parent for our children. */
|
||||||
|
if (nonclaiming_child_readers.size() + assign_as_parent.size() > 0) {
|
||||||
|
/* TODO: When we only support C++11, use for (a: b) instead. */
|
||||||
|
BOOST_FOREACH(AbcObjectReader *child_reader, nonclaiming_child_readers) {
|
||||||
|
child_reader->parent_reader = reader;
|
||||||
|
}
|
||||||
|
BOOST_FOREACH(AbcObjectReader *child_reader, assign_as_parent) {
|
||||||
|
child_reader->parent_reader = reader;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (object.getParent()) {
|
||||||
|
if (claiming_child_readers.size() > 0) {
|
||||||
|
/* The first claiming child will serve just fine as parent to
|
||||||
|
* our non-claiming children. Since all claiming children share
|
||||||
|
* the same XForm, it doesn't really matter which one we pick. */
|
||||||
|
AbcObjectReader *claiming_child = claiming_child_readers[0];
|
||||||
|
BOOST_FOREACH(AbcObjectReader *child_reader, nonclaiming_child_readers) {
|
||||||
|
child_reader->parent_reader = claiming_child;
|
||||||
|
}
|
||||||
|
BOOST_FOREACH(AbcObjectReader *child_reader, assign_as_parent) {
|
||||||
|
child_reader->parent_reader = claiming_child;
|
||||||
|
}
|
||||||
|
/* Claiming children should have our parent set as their parent. */
|
||||||
|
BOOST_FOREACH(AbcObjectReader *child_reader, claiming_child_readers) {
|
||||||
|
r_assign_as_parent.push_back(child_reader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* This object isn't claimed by any child, and didn't produce
|
||||||
|
* a reader. Odd situation, could be the top Alembic object, or
|
||||||
|
* an unsupported Alembic schema. Delegate to our parent. */
|
||||||
|
BOOST_FOREACH(AbcObjectReader *child_reader, claiming_child_readers) {
|
||||||
|
r_assign_as_parent.push_back(child_reader);
|
||||||
|
}
|
||||||
|
BOOST_FOREACH(AbcObjectReader *child_reader, nonclaiming_child_readers) {
|
||||||
|
r_assign_as_parent.push_back(child_reader);
|
||||||
|
}
|
||||||
|
BOOST_FOREACH(AbcObjectReader *child_reader, assign_as_parent) {
|
||||||
|
r_assign_as_parent.push_back(child_reader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::make_pair(parent_is_part_of_this_object, reader);
|
||||||
}
|
}
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
@@ -536,7 +602,6 @@ struct ImportJobData {
|
|||||||
char filename[1024];
|
char filename[1024];
|
||||||
ImportSettings settings;
|
ImportSettings settings;
|
||||||
|
|
||||||
GHash *parent_map;
|
|
||||||
std::vector<AbcObjectReader *> readers;
|
std::vector<AbcObjectReader *> readers;
|
||||||
|
|
||||||
short *stop;
|
short *stop;
|
||||||
@@ -613,11 +678,12 @@ static void import_startjob(void *user_data, short *stop, short *do_update, floa
|
|||||||
*data->do_update = true;
|
*data->do_update = true;
|
||||||
*data->progress = 0.05f;
|
*data->progress = 0.05f;
|
||||||
|
|
||||||
data->parent_map = BLI_ghash_str_new("alembic parent ghash");
|
|
||||||
|
|
||||||
/* Parse Alembic Archive. */
|
/* Parse Alembic Archive. */
|
||||||
|
AbcObjectReader::ptr_vector assign_as_parent;
|
||||||
|
visit_object(archive->getTop(), data->readers, data->settings, assign_as_parent);
|
||||||
|
|
||||||
visit_object(archive->getTop(), data->readers, data->parent_map, data->settings);
|
/* There shouldn't be any orphans. */
|
||||||
|
BLI_assert(assign_as_parent.size() == 0);
|
||||||
|
|
||||||
if (G.is_break) {
|
if (G.is_break) {
|
||||||
data->was_cancelled = true;
|
data->was_cancelled = true;
|
||||||
@@ -641,13 +707,16 @@ static void import_startjob(void *user_data, short *stop, short *do_update, floa
|
|||||||
|
|
||||||
if (reader->valid()) {
|
if (reader->valid()) {
|
||||||
reader->readObjectData(data->bmain, 0.0f);
|
reader->readObjectData(data->bmain, 0.0f);
|
||||||
reader->readObjectMatrix(0.0f);
|
|
||||||
|
|
||||||
min_time = std::min(min_time, reader->minTime());
|
min_time = std::min(min_time, reader->minTime());
|
||||||
max_time = std::max(max_time, reader->maxTime());
|
max_time = std::max(max_time, reader->maxTime());
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
std::cerr << "Object " << reader->name() << " in Alembic file "
|
||||||
|
<< data->filename << " is invalid.\n";
|
||||||
|
}
|
||||||
|
|
||||||
*data->progress = 0.1f + 0.6f * (++i / size);
|
*data->progress = 0.1f + 0.3f * (++i / size);
|
||||||
*data->do_update = true;
|
*data->do_update = true;
|
||||||
|
|
||||||
if (G.is_break) {
|
if (G.is_break) {
|
||||||
@@ -671,39 +740,25 @@ static void import_startjob(void *user_data, short *stop, short *do_update, floa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Setup parentship. */
|
/* Setup parenthood. */
|
||||||
|
|
||||||
i = 0;
|
|
||||||
for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) {
|
for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) {
|
||||||
const AbcObjectReader *reader = *iter;
|
const AbcObjectReader *reader = *iter;
|
||||||
const AbcObjectReader *parent_reader = NULL;
|
const AbcObjectReader *parent_reader = reader->parent_reader;
|
||||||
const IObject &iobject = reader->iobject();
|
Object *ob = reader->object();
|
||||||
|
|
||||||
IObject parent = iobject.getParent();
|
if (parent_reader == NULL) {
|
||||||
|
ob->parent = NULL;
|
||||||
if (!IXform::matches(iobject.getHeader())) {
|
|
||||||
/* In the case of an non XForm node, the parent is the transform
|
|
||||||
* matrix of the data itself, so we get the its grand parent.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Special case with object only containing a mesh and some strands,
|
|
||||||
* we want both objects to be parented to the same object. */
|
|
||||||
if (!is_mesh_and_strands(parent)) {
|
|
||||||
parent = parent.getParent();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
parent_reader = reinterpret_cast<AbcObjectReader *>(
|
ob->parent = parent_reader->object();
|
||||||
BLI_ghash_lookup(data->parent_map, parent.getFullName().c_str()));
|
|
||||||
|
|
||||||
if (parent_reader) {
|
|
||||||
Object *parent = parent_reader->object();
|
|
||||||
|
|
||||||
if (parent != NULL && reader->object() != parent) {
|
|
||||||
Object *ob = reader->object();
|
|
||||||
ob->parent = parent;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Setup transformations and constraints. */
|
||||||
|
i = 0;
|
||||||
|
for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) {
|
||||||
|
AbcObjectReader *reader = *iter;
|
||||||
|
reader->setupObjectTransform(0.0f);
|
||||||
|
|
||||||
*data->progress = 0.7f + 0.3f * (++i / size);
|
*data->progress = 0.7f + 0.3f * (++i / size);
|
||||||
*data->do_update = true;
|
*data->do_update = true;
|
||||||
@@ -728,10 +783,9 @@ static void import_endjob(void *user_data)
|
|||||||
for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) {
|
for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) {
|
||||||
Object *ob = (*iter)->object();
|
Object *ob = (*iter)->object();
|
||||||
|
|
||||||
if (ob->data) {
|
/* It's possible that cancellation occured between the creation of
|
||||||
BKE_libblock_free_us(data->bmain, ob->data);
|
* the reader and the creation of the Blender object. */
|
||||||
ob->data = NULL;
|
if (ob == NULL) continue;
|
||||||
}
|
|
||||||
|
|
||||||
BKE_libblock_free_us(data->bmain, ob);
|
BKE_libblock_free_us(data->bmain, ob);
|
||||||
}
|
}
|
||||||
@@ -761,10 +815,6 @@ static void import_endjob(void *user_data)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data->parent_map) {
|
|
||||||
BLI_ghash_free(data->parent_map, NULL, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (data->error_code) {
|
switch (data->error_code) {
|
||||||
default:
|
default:
|
||||||
case ABC_NO_ERROR:
|
case ABC_NO_ERROR:
|
||||||
@@ -798,7 +848,6 @@ void ABC_import(bContext *C, const char *filepath, float scale, bool is_sequence
|
|||||||
job->settings.sequence_len = sequence_len;
|
job->settings.sequence_len = sequence_len;
|
||||||
job->settings.offset = offset;
|
job->settings.offset = offset;
|
||||||
job->settings.validate_meshes = validate_meshes;
|
job->settings.validate_meshes = validate_meshes;
|
||||||
job->parent_map = NULL;
|
|
||||||
job->error_code = ABC_NO_ERROR;
|
job->error_code = ABC_NO_ERROR;
|
||||||
job->was_cancelled = false;
|
job->was_cancelled = false;
|
||||||
|
|
||||||
|
@@ -2403,15 +2403,20 @@ static void bmesh_kernel_vert_separate__cleanup(BMesh *bm, LinkNode *edges_separ
|
|||||||
do {
|
do {
|
||||||
LinkNode *n_orig = edges_separate->link;
|
LinkNode *n_orig = edges_separate->link;
|
||||||
do {
|
do {
|
||||||
BMEdge *e_orig = n_orig->link;
|
LinkNode *n_prev = n_orig;
|
||||||
LinkNode *n_step = n_orig->next;
|
LinkNode *n_step = n_orig->next;
|
||||||
|
BMEdge *e_orig = n_orig->link;
|
||||||
do {
|
do {
|
||||||
BMEdge *e = n_step->link;
|
BMEdge *e = n_step->link;
|
||||||
BLI_assert(e != e_orig);
|
BLI_assert(e != e_orig);
|
||||||
if ((e->v1 == e_orig->v1) && (e->v2 == e_orig->v2)) {
|
if ((e->v1 == e_orig->v1) && (e->v2 == e_orig->v2) &&
|
||||||
BM_edge_splice(bm, e_orig, e);
|
BM_edge_splice(bm, e_orig, e))
|
||||||
|
{
|
||||||
|
/* don't visit again */
|
||||||
|
n_prev->next = n_step->next;
|
||||||
}
|
}
|
||||||
} while ((n_step = n_step->next));
|
} while ((n_prev = n_step),
|
||||||
|
(n_step = n_step->next));
|
||||||
|
|
||||||
} while ((n_orig = n_orig->next) && n_orig->next);
|
} while ((n_orig = n_orig->next) && n_orig->next);
|
||||||
} while ((edges_separate = edges_separate->next));
|
} while ((edges_separate = edges_separate->next));
|
||||||
|
@@ -1566,7 +1566,7 @@ void BM_mesh_calc_uvs_cone(
|
|||||||
|
|
||||||
BLI_assert(cd_loop_uv_offset != -1); /* caller is responsible for ensuring the mesh has UVs */
|
BLI_assert(cd_loop_uv_offset != -1); /* caller is responsible for ensuring the mesh has UVs */
|
||||||
|
|
||||||
x = 0.0f;
|
x = 1.0f;
|
||||||
y = 1.0f - uv_height;
|
y = 1.0f - uv_height;
|
||||||
|
|
||||||
BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
|
BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
|
||||||
@@ -1580,7 +1580,7 @@ void BM_mesh_calc_uvs_cone(
|
|||||||
|
|
||||||
switch (loop_index) {
|
switch (loop_index) {
|
||||||
case 0:
|
case 0:
|
||||||
x += uv_width;
|
/* Continue in the last position */
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
y += uv_height;
|
y += uv_height;
|
||||||
@@ -1598,8 +1598,6 @@ void BM_mesh_calc_uvs_cone(
|
|||||||
luv->uv[0] = x;
|
luv->uv[0] = x;
|
||||||
luv->uv[1] = y;
|
luv->uv[1] = y;
|
||||||
}
|
}
|
||||||
|
|
||||||
x += uv_width;
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
/* top or bottom face - so unwrap it by transforming back to a circle and using the X/Y coords */
|
/* top or bottom face - so unwrap it by transforming back to a circle and using the X/Y coords */
|
||||||
|
@@ -111,7 +111,7 @@ static DerivedMesh *applyModifier(ModifierData *md, Object *ob,
|
|||||||
|
|
||||||
if (!mcmd->reader) {
|
if (!mcmd->reader) {
|
||||||
mcmd->reader = CacheReader_open_alembic_object(cache_file->handle,
|
mcmd->reader = CacheReader_open_alembic_object(cache_file->handle,
|
||||||
mcmd->reader,
|
NULL,
|
||||||
ob,
|
ob,
|
||||||
mcmd->object_path);
|
mcmd->object_path);
|
||||||
if (!mcmd->reader) {
|
if (!mcmd->reader) {
|
||||||
|
@@ -14,5 +14,7 @@ if(WITH_GTESTS)
|
|||||||
add_subdirectory(blenlib)
|
add_subdirectory(blenlib)
|
||||||
add_subdirectory(guardedalloc)
|
add_subdirectory(guardedalloc)
|
||||||
add_subdirectory(bmesh)
|
add_subdirectory(bmesh)
|
||||||
|
if(WITH_ALEMBIC)
|
||||||
|
add_subdirectory(alembic)
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
51
tests/gtests/alembic/CMakeLists.txt
Normal file
51
tests/gtests/alembic/CMakeLists.txt
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
# ***** BEGIN GPL LICENSE BLOCK *****
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
#
|
||||||
|
# The Original Code is Copyright (C) 2014, Blender Foundation
|
||||||
|
# All rights reserved.
|
||||||
|
#
|
||||||
|
# Contributor(s): Sybren A. Stüvel
|
||||||
|
#
|
||||||
|
# ***** END GPL LICENSE BLOCK *****
|
||||||
|
|
||||||
|
set(INC
|
||||||
|
.
|
||||||
|
..
|
||||||
|
../../../source/blender/blenlib
|
||||||
|
../../../source/blender/alembic
|
||||||
|
${ALEMBIC_INCLUDE_DIRS}
|
||||||
|
${BOOST_INCLUDE_DIR}
|
||||||
|
${HDF5_INCLUDE_DIRS}
|
||||||
|
${OPENEXR_INCLUDE_DIRS}
|
||||||
|
)
|
||||||
|
|
||||||
|
include_directories(${INC})
|
||||||
|
|
||||||
|
setup_libdirs()
|
||||||
|
get_property(BLENDER_SORTED_LIBS GLOBAL PROPERTY BLENDER_SORTED_LIBS_PROP)
|
||||||
|
|
||||||
|
if(WITH_BUILDINFO)
|
||||||
|
set(_buildinfo_src "$<TARGET_OBJECTS:buildinfoobj>")
|
||||||
|
else()
|
||||||
|
set(_buildinfo_src "")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# For motivation on doubling BLENDER_SORTED_LIBS, see ../bmesh/CMakeLists.txt
|
||||||
|
BLENDER_SRC_GTEST(abc_matrix "abc_matrix_test.cc;${_buildinfo_src}" "${BLENDER_SORTED_LIBS};${BLENDER_SORTED_LIBS}")
|
||||||
|
|
||||||
|
unset(_buildinfo_src)
|
||||||
|
|
||||||
|
setup_liblinks(abc_matrix_test)
|
282
tests/gtests/alembic/abc_matrix_test.cc
Normal file
282
tests/gtests/alembic/abc_matrix_test.cc
Normal file
@@ -0,0 +1,282 @@
|
|||||||
|
#include "testing/testing.h"
|
||||||
|
|
||||||
|
// Keep first since utildefines defines AT which conflicts with fucking STL
|
||||||
|
#include "intern/abc_util.h"
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include "BLI_utildefines.h"
|
||||||
|
#include "BLI_math.h"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST(abc_matrix, CreateRotationMatrixY_YfromZ) {
|
||||||
|
// Input variables
|
||||||
|
float rot_x_mat[3][3];
|
||||||
|
float rot_y_mat[3][3];
|
||||||
|
float rot_z_mat[3][3];
|
||||||
|
float euler[3] = {0.f, M_PI_4, 0.f};
|
||||||
|
|
||||||
|
// Construct expected matrices
|
||||||
|
float unit[3][3];
|
||||||
|
float rot_z_min_quart_pi[3][3]; // rotation of -pi/4 radians over z-axis
|
||||||
|
|
||||||
|
unit_m3(unit);
|
||||||
|
unit_m3(rot_z_min_quart_pi);
|
||||||
|
rot_z_min_quart_pi[0][0] = M_SQRT1_2;
|
||||||
|
rot_z_min_quart_pi[0][1] = -M_SQRT1_2;
|
||||||
|
rot_z_min_quart_pi[1][0] = M_SQRT1_2;
|
||||||
|
rot_z_min_quart_pi[1][1] = M_SQRT1_2;
|
||||||
|
|
||||||
|
// Run tests
|
||||||
|
create_swapped_rotation_matrix(rot_x_mat, rot_y_mat, rot_z_mat, euler,
|
||||||
|
ABC_YUP_FROM_ZUP);
|
||||||
|
|
||||||
|
EXPECT_M3_NEAR(rot_x_mat, unit, 1e-5f);
|
||||||
|
EXPECT_M3_NEAR(rot_y_mat, unit, 1e-5f);
|
||||||
|
EXPECT_M3_NEAR(rot_z_mat, rot_z_min_quart_pi, 1e-5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(abc_matrix, CreateRotationMatrixZ_YfromZ) {
|
||||||
|
// Input variables
|
||||||
|
float rot_x_mat[3][3];
|
||||||
|
float rot_y_mat[3][3];
|
||||||
|
float rot_z_mat[3][3];
|
||||||
|
float euler[3] = {0.f, 0.f, M_PI_4};
|
||||||
|
|
||||||
|
// Construct expected matrices
|
||||||
|
float unit[3][3];
|
||||||
|
float rot_y_quart_pi[3][3]; // rotation of pi/4 radians over y-axis
|
||||||
|
|
||||||
|
unit_m3(unit);
|
||||||
|
unit_m3(rot_y_quart_pi);
|
||||||
|
rot_y_quart_pi[0][0] = M_SQRT1_2;
|
||||||
|
rot_y_quart_pi[0][2] = -M_SQRT1_2;
|
||||||
|
rot_y_quart_pi[2][0] = M_SQRT1_2;
|
||||||
|
rot_y_quart_pi[2][2] = M_SQRT1_2;
|
||||||
|
|
||||||
|
// Run tests
|
||||||
|
create_swapped_rotation_matrix(rot_x_mat, rot_y_mat, rot_z_mat, euler,
|
||||||
|
ABC_YUP_FROM_ZUP);
|
||||||
|
|
||||||
|
EXPECT_M3_NEAR(rot_x_mat, unit, 1e-5f);
|
||||||
|
EXPECT_M3_NEAR(rot_y_mat, rot_y_quart_pi, 1e-5f);
|
||||||
|
EXPECT_M3_NEAR(rot_z_mat, unit, 1e-5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(abc_matrix, CreateRotationMatrixXYZ_YfromZ) {
|
||||||
|
// Input variables
|
||||||
|
float rot_x_mat[3][3];
|
||||||
|
float rot_y_mat[3][3];
|
||||||
|
float rot_z_mat[3][3];
|
||||||
|
// in degrees: X=10, Y=20, Z=30
|
||||||
|
float euler[3] = {0.17453292012214f, 0.34906581044197f, 0.52359879016876f};
|
||||||
|
|
||||||
|
// Construct expected matrices
|
||||||
|
float rot_x_p10[3][3]; // rotation of +10 degrees over x-axis
|
||||||
|
float rot_y_p30[3][3]; // rotation of +30 degrees over y-axis
|
||||||
|
float rot_z_m20[3][3]; // rotation of -20 degrees over z-axis
|
||||||
|
|
||||||
|
unit_m3(rot_x_p10);
|
||||||
|
rot_x_p10[1][1] = 0.9848077297210693f;
|
||||||
|
rot_x_p10[1][2] = 0.1736481785774231f;
|
||||||
|
rot_x_p10[2][1] = -0.1736481785774231f;
|
||||||
|
rot_x_p10[2][2] = 0.9848077297210693f;
|
||||||
|
|
||||||
|
unit_m3(rot_y_p30);
|
||||||
|
rot_y_p30[0][0] = 0.8660253882408142f;
|
||||||
|
rot_y_p30[0][2] = -0.5f;
|
||||||
|
rot_y_p30[2][0] = 0.5f;
|
||||||
|
rot_y_p30[2][2] = 0.8660253882408142f;
|
||||||
|
|
||||||
|
unit_m3(rot_z_m20);
|
||||||
|
rot_z_m20[0][0] = 0.9396926164627075f;
|
||||||
|
rot_z_m20[0][1] = -0.3420201241970062f;
|
||||||
|
rot_z_m20[1][0] = 0.3420201241970062f;
|
||||||
|
rot_z_m20[1][1] = 0.9396926164627075f;
|
||||||
|
|
||||||
|
// Run tests
|
||||||
|
create_swapped_rotation_matrix(rot_x_mat, rot_y_mat, rot_z_mat, euler,
|
||||||
|
ABC_YUP_FROM_ZUP);
|
||||||
|
|
||||||
|
EXPECT_M3_NEAR(rot_x_mat, rot_x_p10, 1e-5f);
|
||||||
|
EXPECT_M3_NEAR(rot_y_mat, rot_y_p30, 1e-5f);
|
||||||
|
EXPECT_M3_NEAR(rot_z_mat, rot_z_m20, 1e-5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(abc_matrix, CreateRotationMatrixXYZ_ZfromY) {
|
||||||
|
// Input variables
|
||||||
|
float rot_x_mat[3][3];
|
||||||
|
float rot_y_mat[3][3];
|
||||||
|
float rot_z_mat[3][3];
|
||||||
|
// in degrees: X=10, Y=20, Z=30
|
||||||
|
float euler[3] = {0.1745329201221466f, 0.3490658104419708f, 0.5235987901687622f};
|
||||||
|
|
||||||
|
// Construct expected matrices
|
||||||
|
float rot_x_p10[3][3]; // rotation of +10 degrees over x-axis
|
||||||
|
float rot_y_m30[3][3]; // rotation of -30 degrees over y-axis
|
||||||
|
float rot_z_p20[3][3]; // rotation of +20 degrees over z-axis
|
||||||
|
|
||||||
|
unit_m3(rot_x_p10);
|
||||||
|
rot_x_p10[1][1] = 0.9848077297210693f;
|
||||||
|
rot_x_p10[1][2] = 0.1736481785774231f;
|
||||||
|
rot_x_p10[2][1] = -0.1736481785774231f;
|
||||||
|
rot_x_p10[2][2] = 0.9848077297210693f;
|
||||||
|
|
||||||
|
unit_m3(rot_y_m30);
|
||||||
|
rot_y_m30[0][0] = 0.8660253882408142f;
|
||||||
|
rot_y_m30[0][2] = 0.5f;
|
||||||
|
rot_y_m30[2][0] = -0.5f;
|
||||||
|
rot_y_m30[2][2] = 0.8660253882408142f;
|
||||||
|
|
||||||
|
unit_m3(rot_z_p20);
|
||||||
|
rot_z_p20[0][0] = 0.9396926164627075f;
|
||||||
|
rot_z_p20[0][1] = 0.3420201241970062f;
|
||||||
|
rot_z_p20[1][0] = -0.3420201241970062f;
|
||||||
|
rot_z_p20[1][1] = 0.9396926164627075f;
|
||||||
|
|
||||||
|
// Run tests
|
||||||
|
create_swapped_rotation_matrix(rot_x_mat, rot_y_mat, rot_z_mat, euler,
|
||||||
|
ABC_ZUP_FROM_YUP);
|
||||||
|
|
||||||
|
EXPECT_M3_NEAR(rot_x_mat, rot_x_p10, 1e-5f);
|
||||||
|
EXPECT_M3_NEAR(rot_y_mat, rot_y_m30, 1e-5f);
|
||||||
|
EXPECT_M3_NEAR(rot_z_mat, rot_z_p20, 1e-5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(abc_matrix, CopyM44AxisSwap_YfromZ) {
|
||||||
|
float result[4][4];
|
||||||
|
|
||||||
|
/* Construct an input matrix that performs a rotation like the tests
|
||||||
|
* above. This matrix was created by rotating a cube in Blender over
|
||||||
|
* (X=10, Y=20, Z=30 degrees in XYZ order) and translating over (1, 2, 3) */
|
||||||
|
float input[4][4] = {
|
||||||
|
{ 0.81379765272f, 0.4698463380336f, -0.342020124197f, 0.f},
|
||||||
|
{-0.44096961617f, 0.8825641274452f, 0.163175910711f, 0.f},
|
||||||
|
{ 0.37852230668f, 0.0180283170193f, 0.925416588783f, 0.f},
|
||||||
|
{1.f, 2.f, 3.f, 1.f},
|
||||||
|
};
|
||||||
|
|
||||||
|
copy_m44_axis_swap(result, input, ABC_YUP_FROM_ZUP);
|
||||||
|
|
||||||
|
/* Check the resulting rotation & translation. */
|
||||||
|
float trans[4] = {1.f, 3.f, -2.f, 1.f};
|
||||||
|
EXPECT_V4_NEAR(trans, result[3], 1e-5f);
|
||||||
|
|
||||||
|
/* This matrix was created by rotating a cube in Blender over
|
||||||
|
* (X=10, Y=30, Z=-20 degrees in XZY order) and translating over (1, 3, -2) */
|
||||||
|
float expect[4][4] = {
|
||||||
|
{0.813797652721f, -0.342020124197f, -0.469846338033f, 0.f},
|
||||||
|
{0.378522306680f, 0.925416588783f, -0.018028317019f, 0.f},
|
||||||
|
{0.440969616174f, -0.163175910711f, 0.882564127445f, 0.f},
|
||||||
|
{1.f, 3.f, -2.f, 1.f},
|
||||||
|
};
|
||||||
|
EXPECT_M4_NEAR(expect, result, 1e-5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(abc_matrix, CopyM44AxisSwapWithScale_YfromZ) {
|
||||||
|
float result[4][4];
|
||||||
|
|
||||||
|
/* Construct an input matrix that performs a rotation like the tests
|
||||||
|
* above. This matrix was created by rotating a cube in Blender over
|
||||||
|
* (X=10, Y=20, Z=30 degrees in XYZ order), translating over (1, 2, 3),
|
||||||
|
* and scaling by (4, 5, 6). */
|
||||||
|
float input[4][4] = {
|
||||||
|
{ 3.25519061088f, 1.8793853521347f, -1.368080496788f, 0.f},
|
||||||
|
{-2.20484805107f, 4.4128208160400f, 0.815879583358f, 0.f},
|
||||||
|
{ 2.27113389968f, 0.1081698983907f, 5.552499771118f, 0.f},
|
||||||
|
{1.f, 2.f, 3.f, 1.f},
|
||||||
|
};
|
||||||
|
|
||||||
|
copy_m44_axis_swap(result, input, ABC_YUP_FROM_ZUP);
|
||||||
|
|
||||||
|
/* This matrix was created by rotating a cube in Blender over
|
||||||
|
* (X=10, Y=30, Z=-20 degrees in XZY order), translating over (1, 3, -2)
|
||||||
|
* and scaling over (4, 6, 5). */
|
||||||
|
float expect[4][4] = {
|
||||||
|
{3.255190610885f, -1.368080496788f, -1.879385352134f, 0.f},
|
||||||
|
{2.271133899688f, 5.552499771118f, -0.108169898390f, 0.f},
|
||||||
|
{2.204848051071f, -0.815879583358f, 4.412820816040f, 0.f},
|
||||||
|
{1.f, 3.f, -2.f, 1.f},
|
||||||
|
};
|
||||||
|
EXPECT_M4_NEAR(expect, result, 1e-5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(abc_matrix, CopyM44AxisSwap_ZfromY) {
|
||||||
|
float result[4][4];
|
||||||
|
|
||||||
|
/* This matrix was created by rotating a cube in Blender over
|
||||||
|
* (X=10, Y=30, Z=-20 degrees in XZY order) and translating over (1, 3, -2) */
|
||||||
|
float input[4][4] = {
|
||||||
|
{0.813797652721f, -0.342020124197f, -0.469846338033f, 0.f},
|
||||||
|
{0.378522306680f, 0.925416588783f, -0.018028317019f, 0.f},
|
||||||
|
{0.440969616174f, -0.163175910711f, 0.882564127445f, 0.f},
|
||||||
|
{1.f, 3.f, -2.f, 1.f},
|
||||||
|
};
|
||||||
|
|
||||||
|
copy_m44_axis_swap(result, input, ABC_ZUP_FROM_YUP);
|
||||||
|
|
||||||
|
/* This matrix was created by rotating a cube in Blender over
|
||||||
|
* (X=10, Y=20, Z=30 degrees in XYZ order) and translating over (1, 2, 3) */
|
||||||
|
float expect[4][4] = {
|
||||||
|
{0.813797652721f, 0.469846338033f, -0.342020124197f, 0.f},
|
||||||
|
{-0.44096961617f, 0.882564127445f, 0.163175910711f, 0.f},
|
||||||
|
{0.378522306680f, 0.018028317019f, 0.925416588783f, 0.f},
|
||||||
|
{1.f, 2.f, 3.f, 1.f},
|
||||||
|
};
|
||||||
|
|
||||||
|
EXPECT_M4_NEAR(expect, result, 1e-5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(abc_matrix, CopyM44AxisSwapWithScale_ZfromY) {
|
||||||
|
float result[4][4];
|
||||||
|
|
||||||
|
/* This matrix was created by rotating a cube in Blender over
|
||||||
|
* (X=10, Y=30, Z=-20 degrees in XZY order), translating over (1, 3, -2)
|
||||||
|
* and scaling over (4, 6, 5). */
|
||||||
|
float input[4][4] = {
|
||||||
|
{3.2551906108f, -1.36808049678f, -1.879385352134f, 0.f},
|
||||||
|
{2.2711338996f, 5.55249977111f, -0.108169898390f, 0.f},
|
||||||
|
{2.2048480510f, -0.81587958335f, 4.412820816040f, 0.f},
|
||||||
|
{1.f, 3.f, -2.f, 1.f},
|
||||||
|
};
|
||||||
|
|
||||||
|
copy_m44_axis_swap(result, input, ABC_ZUP_FROM_YUP);
|
||||||
|
|
||||||
|
/* This matrix was created by rotating a cube in Blender over
|
||||||
|
* (X=10, Y=20, Z=30 degrees in XYZ order), translating over (1, 2, 3),
|
||||||
|
* and scaling by (4, 5, 6). */
|
||||||
|
float expect[4][4] = {
|
||||||
|
{3.25519061088f, 1.879385352134f, -1.36808049678f, 0.f},
|
||||||
|
{-2.2048480510f, 4.412820816040f, 0.81587958335f, 0.f},
|
||||||
|
{2.27113389968f, 0.108169898390f, 5.55249977111f, 0.f},
|
||||||
|
{1.f, 2.f, 3.f, 1.f},
|
||||||
|
};
|
||||||
|
|
||||||
|
EXPECT_M4_NEAR(expect, result, 1e-5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(abc_matrix, CopyM44AxisSwapWithScale_gimbal_ZfromY) {
|
||||||
|
float result[4][4];
|
||||||
|
|
||||||
|
/* This matrix represents a rotation over (-90, -0, -0) degrees,
|
||||||
|
* and a translation over (-0, -0.1, -0). It is in Y=up. */
|
||||||
|
float input[4][4] = {
|
||||||
|
{ 1.000f, 0.000f, 0.000f, 0.000f},
|
||||||
|
{ 0.000f, 0.000f,-1.000f, 0.000f},
|
||||||
|
{ 0.000f, 1.000f, 0.000f, 0.000f},
|
||||||
|
{-0.000f,-0.100f,-0.000f, 1.000f},
|
||||||
|
};
|
||||||
|
|
||||||
|
copy_m44_axis_swap(result, input, ABC_ZUP_FROM_YUP);
|
||||||
|
|
||||||
|
/* Since the rotation is only over the X-axis, it should not change.
|
||||||
|
* The translation does change. */
|
||||||
|
float expect[4][4] = {
|
||||||
|
{ 1.000f, 0.000f, 0.000f, 0.000f},
|
||||||
|
{ 0.000f, 0.000f,-1.000f, 0.000f},
|
||||||
|
{ 0.000f, 1.000f, 0.000f, 0.000f},
|
||||||
|
{-0.000f, 0.000f,-0.100f, 1.000f},
|
||||||
|
};
|
||||||
|
|
||||||
|
EXPECT_M4_NEAR(expect, result, 1e-5f);
|
||||||
|
}
|
@@ -12,6 +12,29 @@
|
|||||||
EXPECT_NEAR(a[2], b[2], eps); \
|
EXPECT_NEAR(a[2], b[2], eps); \
|
||||||
} (void) 0
|
} (void) 0
|
||||||
|
|
||||||
|
#define EXPECT_V4_NEAR(a, b, eps) \
|
||||||
|
{ \
|
||||||
|
EXPECT_NEAR(a[0], b[0], eps); \
|
||||||
|
EXPECT_NEAR(a[1], b[1], eps); \
|
||||||
|
EXPECT_NEAR(a[2], b[2], eps); \
|
||||||
|
EXPECT_NEAR(a[3], b[3], eps); \
|
||||||
|
} (void) 0
|
||||||
|
|
||||||
|
#define EXPECT_M3_NEAR(a, b, eps) \
|
||||||
|
do { \
|
||||||
|
EXPECT_V3_NEAR(a[0], b[0], eps); \
|
||||||
|
EXPECT_V3_NEAR(a[1], b[1], eps); \
|
||||||
|
EXPECT_V3_NEAR(a[2], b[2], eps); \
|
||||||
|
} while(false);
|
||||||
|
|
||||||
|
#define EXPECT_M4_NEAR(a, b, eps) \
|
||||||
|
do { \
|
||||||
|
EXPECT_V3_NEAR(a[0], b[0], eps); \
|
||||||
|
EXPECT_V3_NEAR(a[1], b[1], eps); \
|
||||||
|
EXPECT_V3_NEAR(a[2], b[2], eps); \
|
||||||
|
EXPECT_V4_NEAR(a[3], b[3], eps); \
|
||||||
|
} while(false);
|
||||||
|
|
||||||
#define EXPECT_MATRIX_NEAR(a, b, tolerance) \
|
#define EXPECT_MATRIX_NEAR(a, b, tolerance) \
|
||||||
do { \
|
do { \
|
||||||
bool dims_match = (a.rows() == b.rows()) && (a.cols() == b.cols()); \
|
bool dims_match = (a.rows() == b.rows()) && (a.cols() == b.cols()); \
|
||||||
|
Reference in New Issue
Block a user