Changeset View
Changeset View
Standalone View
Standalone View
source/blender/alembic/intern/abc_reader_curves.cc.orig
- This file was added.
| /* | |||||
| * 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) 2016 Kévin Dietrich. | |||||
| * All rights reserved. | |||||
| */ | |||||
| /** \file | |||||
| * \ingroup balembic | |||||
| */ | |||||
| #include "abc_reader_curves.h" | |||||
| #include "abc_reader_transform.h" | |||||
| #include "abc_util.h" | |||||
| #include <cstdio> | |||||
| #include "MEM_guardedalloc.h" | |||||
| extern "C" { | |||||
| #include "DNA_curve_types.h" | |||||
| #include "DNA_object_types.h" | |||||
| #include "BLI_listbase.h" | |||||
| #include "BKE_curve.h" | |||||
| #include "BKE_mesh.h" | |||||
| #include "BKE_object.h" | |||||
| } | |||||
| using Alembic::Abc::FloatArraySamplePtr; | |||||
| using Alembic::Abc::Int32ArraySamplePtr; | |||||
| using Alembic::Abc::P3fArraySamplePtr; | |||||
| using Alembic::Abc::PropertyHeader; | |||||
| using Alembic::Abc::UcharArraySamplePtr; | |||||
| using Alembic::AbcGeom::CurvePeriodicity; | |||||
| using Alembic::AbcGeom::ICompoundProperty; | |||||
| using Alembic::AbcGeom::ICurves; | |||||
| using Alembic::AbcGeom::ICurvesSchema; | |||||
| using Alembic::AbcGeom::IFloatGeomParam; | |||||
| using Alembic::AbcGeom::IInt16Property; | |||||
| using Alembic::AbcGeom::ISampleSelector; | |||||
| using Alembic::AbcGeom::kWrapExisting; | |||||
| <<<<<<< HEAD:source/blender/alembic/intern/abc_reader_curves.cc | |||||
| ======= | |||||
| using Alembic::AbcGeom::OCompoundProperty; | |||||
| using Alembic::AbcGeom::OCurves; | |||||
| using Alembic::AbcGeom::OCurvesSchema; | |||||
| using Alembic::AbcGeom::OInt16Property; | |||||
| using Alembic::AbcGeom::ON3fGeomParam; | |||||
| using Alembic::AbcGeom::OV2fGeomParam; | |||||
| #define ABC_CURVE_RESOLUTION_U_PROPNAME "blender:resolution" | |||||
| /* ************************************************************************** */ | |||||
| AbcCurveWriter::AbcCurveWriter(Object *ob, | |||||
| AbcTransformWriter *parent, | |||||
| uint32_t time_sampling, | |||||
| ExportSettings &settings) | |||||
| : AbcObjectWriter(ob, time_sampling, settings, parent) | |||||
| { | |||||
| OCurves curves(parent->alembicXform(), m_name, m_time_sampling); | |||||
| m_schema = curves.getSchema(); | |||||
| Curve *cu = static_cast<Curve *>(m_object->data); | |||||
| OCompoundProperty user_props = m_schema.getUserProperties(); | |||||
| OInt16Property user_prop_resolu(user_props, ABC_CURVE_RESOLUTION_U_PROPNAME); | |||||
| user_prop_resolu.set(cu->resolu); | |||||
| } | |||||
| void AbcCurveWriter::do_write() | |||||
| { | |||||
| Curve *curve = static_cast<Curve *>(m_object->data); | |||||
| std::vector<Imath::V3f> verts; | |||||
| std::vector<int32_t> vert_counts; | |||||
| std::vector<float> widths; | |||||
| std::vector<float> weights; | |||||
| std::vector<float> knots; | |||||
| std::vector<uint8_t> orders; | |||||
| Imath::V3f temp_vert; | |||||
| Alembic::AbcGeom::BasisType curve_basis; | |||||
| Alembic::AbcGeom::CurveType curve_type; | |||||
| Alembic::AbcGeom::CurvePeriodicity periodicity; | |||||
| Nurb *nurbs = static_cast<Nurb *>(curve->nurb.first); | |||||
| for (; nurbs; nurbs = nurbs->next) { | |||||
| if (nurbs->bp) { | |||||
| curve_basis = Alembic::AbcGeom::kNoBasis; | |||||
| curve_type = Alembic::AbcGeom::kVariableOrder; | |||||
| const int totpoint = nurbs->pntsu * nurbs->pntsv; | |||||
| const BPoint *point = nurbs->bp; | |||||
| for (int i = 0; i < totpoint; i++, point++) { | |||||
| copy_yup_from_zup(temp_vert.getValue(), point->vec); | |||||
| verts.push_back(temp_vert); | |||||
| weights.push_back(point->vec[3]); | |||||
| widths.push_back(point->radius); | |||||
| } | |||||
| } | |||||
| else if (nurbs->bezt) { | |||||
| curve_basis = Alembic::AbcGeom::kBezierBasis; | |||||
| curve_type = Alembic::AbcGeom::kCubic; | |||||
| const int totpoint = nurbs->pntsu; | |||||
| const BezTriple *bezier = nurbs->bezt; | |||||
| /* TODO(kevin): store info about handles, Alembic doesn't have this. */ | |||||
| for (int i = 0; i < totpoint; i++, bezier++) { | |||||
| copy_yup_from_zup(temp_vert.getValue(), bezier->vec[1]); | |||||
| verts.push_back(temp_vert); | |||||
| widths.push_back(bezier->radius); | |||||
| } | |||||
| } | |||||
| if ((nurbs->flagu & CU_NURB_ENDPOINT) != 0) { | |||||
| periodicity = Alembic::AbcGeom::kNonPeriodic; | |||||
| } | |||||
| else if ((nurbs->flagu & CU_NURB_CYCLIC) != 0) { | |||||
| periodicity = Alembic::AbcGeom::kPeriodic; | |||||
| /* Duplicate the start points to indicate that the curve is actually | |||||
| * cyclic since other software need those. | |||||
| */ | |||||
| for (int i = 0; i < nurbs->orderu; i++) { | |||||
| verts.push_back(verts[i]); | |||||
| } | |||||
| } | |||||
| if (nurbs->knotsu != NULL) { | |||||
| const size_t num_knots = KNOTSU(nurbs); | |||||
| /* Add an extra knot at the beginning and end of the array since most apps | |||||
| * require/expect them. */ | |||||
| knots.resize(num_knots + 2); | |||||
| for (int i = 0; i < num_knots; i++) { | |||||
| knots[i + 1] = nurbs->knotsu[i]; | |||||
| } | |||||
| if ((nurbs->flagu & CU_NURB_CYCLIC) != 0) { | |||||
| knots[0] = nurbs->knotsu[0]; | |||||
| knots[num_knots - 1] = nurbs->knotsu[num_knots - 1]; | |||||
| } | |||||
| else { | |||||
| knots[0] = (2.0f * nurbs->knotsu[0] - nurbs->knotsu[1]); | |||||
| knots[num_knots - 1] = (2.0f * nurbs->knotsu[num_knots - 1] - | |||||
| nurbs->knotsu[num_knots - 2]); | |||||
| } | |||||
| } | |||||
| orders.push_back(nurbs->orderu); | |||||
| vert_counts.push_back(verts.size()); | |||||
| } | |||||
| Alembic::AbcGeom::OFloatGeomParam::Sample width_sample; | |||||
| width_sample.setVals(widths); | |||||
| m_sample = OCurvesSchema::Sample(verts, | |||||
| vert_counts, | |||||
| curve_type, | |||||
| periodicity, | |||||
| width_sample, | |||||
| OV2fGeomParam::Sample(), /* UVs */ | |||||
| ON3fGeomParam::Sample(), /* normals */ | |||||
| curve_basis, | |||||
| weights, | |||||
| orders, | |||||
| knots); | |||||
| m_sample.setSelfBounds(bounds()); | |||||
| m_schema.set(m_sample); | |||||
| } | |||||
| AbcCurveMeshWriter::AbcCurveMeshWriter(Object *ob, | |||||
| AbcTransformWriter *parent, | |||||
| uint32_t time_sampling, | |||||
| ExportSettings &settings) | |||||
| : AbcGenericMeshWriter(ob, parent, time_sampling, settings) | |||||
| { | |||||
| } | |||||
| Mesh *AbcCurveMeshWriter::getEvaluatedMesh(Scene * /*scene_eval*/, | |||||
| Object *ob_eval, | |||||
| bool &r_needsfree) | |||||
| { | |||||
| Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob_eval); | |||||
| if (mesh_eval != NULL) { | |||||
| /* Mesh_eval only exists when generative modifiers are in use. */ | |||||
| r_needsfree = false; | |||||
| return mesh_eval; | |||||
| } | |||||
| r_needsfree = true; | |||||
| return BKE_mesh_new_nomain_from_curve(ob_eval); | |||||
| } | |||||
| /* ************************************************************************** */ | |||||
| >>>>>>> Objects: make evaluated data runtime storage usable for types other than mesh:source/blender/alembic/intern/abc_curves.cc | |||||
| AbcCurveReader::AbcCurveReader(const Alembic::Abc::IObject &object, ImportSettings &settings) | |||||
| : AbcObjectReader(object, settings) | |||||
| { | |||||
| ICurves abc_curves(object, kWrapExisting); | |||||
| m_curves_schema = abc_curves.getSchema(); | |||||
| get_min_max_time(m_iobject, m_curves_schema, m_min_time, m_max_time); | |||||
| } | |||||
| bool AbcCurveReader::valid() const | |||||
| { | |||||
| return m_curves_schema.valid(); | |||||
| } | |||||
| bool AbcCurveReader::accepts_object_type( | |||||
| const Alembic::AbcCoreAbstract::ObjectHeader &alembic_header, | |||||
| const Object *const ob, | |||||
| const char **err_str) const | |||||
| { | |||||
| if (!Alembic::AbcGeom::ICurves::matches(alembic_header)) { | |||||
| *err_str = | |||||
| "Object type mismatch, Alembic object path pointed to Curves when importing, but not any " | |||||
| "more."; | |||||
| return false; | |||||
| } | |||||
| if (ob->type != OB_CURVE) { | |||||
| *err_str = "Object type mismatch, Alembic object path points to Curves."; | |||||
| return false; | |||||
| } | |||||
| return true; | |||||
| } | |||||
| void AbcCurveReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel) | |||||
| { | |||||
| Curve *cu = BKE_curve_add(bmain, m_data_name.c_str(), OB_CURVE); | |||||
| cu->flag |= CU_DEFORM_FILL | CU_3D; | |||||
| cu->actvert = CU_ACT_NONE; | |||||
| cu->resolu = 1; | |||||
| ICompoundProperty user_props = m_curves_schema.getUserProperties(); | |||||
| if (user_props) { | |||||
| const PropertyHeader *header = user_props.getPropertyHeader(ABC_CURVE_RESOLUTION_U_PROPNAME); | |||||
| if (header != NULL && header->isScalar() && IInt16Property::matches(*header)) { | |||||
| IInt16Property resolu(user_props, header->getName()); | |||||
| cu->resolu = resolu.getValue(sample_sel); | |||||
| } | |||||
| } | |||||
| m_object = BKE_object_add_only_object(bmain, OB_CURVE, m_object_name.c_str()); | |||||
| m_object->data = cu; | |||||
| read_curve_sample(cu, m_curves_schema, sample_sel); | |||||
| if (has_animations(m_curves_schema, m_settings)) { | |||||
| addCacheModifier(); | |||||
| } | |||||
| } | |||||
| void AbcCurveReader::read_curve_sample(Curve *cu, | |||||
| const ICurvesSchema &schema, | |||||
| const ISampleSelector &sample_sel) | |||||
| { | |||||
| ICurvesSchema::Sample smp; | |||||
| try { | |||||
| smp = schema.getValue(sample_sel); | |||||
| } | |||||
| catch (Alembic::Util::Exception &ex) { | |||||
| printf("Alembic: error reading curve sample for '%s/%s' at time %f: %s\n", | |||||
| m_iobject.getFullName().c_str(), | |||||
| schema.getName().c_str(), | |||||
| sample_sel.getRequestedTime(), | |||||
| ex.what()); | |||||
| return; | |||||
| } | |||||
| const Int32ArraySamplePtr num_vertices = smp.getCurvesNumVertices(); | |||||
| const P3fArraySamplePtr positions = smp.getPositions(); | |||||
| const FloatArraySamplePtr weights = smp.getPositionWeights(); | |||||
| const FloatArraySamplePtr knots = smp.getKnots(); | |||||
| const CurvePeriodicity periodicity = smp.getWrap(); | |||||
| const UcharArraySamplePtr orders = smp.getOrders(); | |||||
| const IFloatGeomParam widths_param = schema.getWidthsParam(); | |||||
| FloatArraySamplePtr radiuses; | |||||
| if (widths_param.valid()) { | |||||
| IFloatGeomParam::Sample wsample = widths_param.getExpandedValue(sample_sel); | |||||
| radiuses = wsample.getVals(); | |||||
| } | |||||
| int knot_offset = 0; | |||||
| size_t idx = 0; | |||||
| for (size_t i = 0; i < num_vertices->size(); i++) { | |||||
| const int num_verts = (*num_vertices)[i]; | |||||
| Nurb *nu = static_cast<Nurb *>(MEM_callocN(sizeof(Nurb), "abc_getnurb")); | |||||
| nu->resolu = cu->resolu; | |||||
| nu->resolv = cu->resolv; | |||||
| nu->pntsu = num_verts; | |||||
| nu->pntsv = 1; | |||||
| nu->flag |= CU_SMOOTH; | |||||
| switch (smp.getType()) { | |||||
| case Alembic::AbcGeom::kCubic: | |||||
| nu->orderu = 4; | |||||
| break; | |||||
| case Alembic::AbcGeom::kVariableOrder: | |||||
| if (orders && orders->size() > i) { | |||||
| nu->orderu = static_cast<short>((*orders)[i]); | |||||
| break; | |||||
| } | |||||
| ATTR_FALLTHROUGH; | |||||
| case Alembic::AbcGeom::kLinear: | |||||
| default: | |||||
| nu->orderu = 2; | |||||
| } | |||||
| if (periodicity == Alembic::AbcGeom::kNonPeriodic) { | |||||
| nu->flagu |= CU_NURB_ENDPOINT; | |||||
| } | |||||
| else if (periodicity == Alembic::AbcGeom::kPeriodic) { | |||||
| nu->flagu |= CU_NURB_CYCLIC; | |||||
| /* Check the number of points which overlap, we don't have | |||||
| * overlapping points in Blender, but other software do use them to | |||||
| * indicate that a curve is actually cyclic. Usually the number of | |||||
| * overlapping points is equal to the order/degree of the curve. | |||||
| */ | |||||
| const int start = idx; | |||||
| const int end = idx + num_verts; | |||||
| int overlap = 0; | |||||
| for (int j = start, k = end - nu->orderu; j < nu->orderu; j++, k++) { | |||||
| const Imath::V3f &p1 = (*positions)[j]; | |||||
| const Imath::V3f &p2 = (*positions)[k]; | |||||
| if (p1 != p2) { | |||||
| break; | |||||
| } | |||||
| overlap++; | |||||
| } | |||||
| /* TODO: Special case, need to figure out how it coincides with knots. */ | |||||
| if (overlap == 0 && num_verts > 2 && (*positions)[start] == (*positions)[end - 1]) { | |||||
| overlap = 1; | |||||
| } | |||||
| /* There is no real cycles. */ | |||||
| if (overlap == 0) { | |||||
| nu->flagu &= ~CU_NURB_CYCLIC; | |||||
| nu->flagu |= CU_NURB_ENDPOINT; | |||||
| } | |||||
| nu->pntsu -= overlap; | |||||
| } | |||||
| const bool do_weights = (weights != NULL) && (weights->size() > 1); | |||||
| float weight = 1.0f; | |||||
| const bool do_radius = (radiuses != NULL) && (radiuses->size() > 1); | |||||
| float radius = (radiuses && radiuses->size() == 1) ? (*radiuses)[0] : 1.0f; | |||||
| nu->type = CU_NURBS; | |||||
| nu->bp = static_cast<BPoint *>(MEM_callocN(sizeof(BPoint) * nu->pntsu, "abc_getnurb")); | |||||
| BPoint *bp = nu->bp; | |||||
| for (int j = 0; j < nu->pntsu; j++, bp++, idx++) { | |||||
| const Imath::V3f &pos = (*positions)[idx]; | |||||
| if (do_radius) { | |||||
| radius = (*radiuses)[idx]; | |||||
| } | |||||
| if (do_weights) { | |||||
| weight = (*weights)[idx]; | |||||
| } | |||||
| copy_zup_from_yup(bp->vec, pos.getValue()); | |||||
| bp->vec[3] = weight; | |||||
| bp->f1 = SELECT; | |||||
| bp->radius = radius; | |||||
| bp->weight = 1.0f; | |||||
| } | |||||
| if (knots && knots->size() != 0) { | |||||
| nu->knotsu = static_cast<float *>( | |||||
| MEM_callocN(KNOTSU(nu) * sizeof(float), "abc_setsplineknotsu")); | |||||
| /* TODO: second check is temporary, for until the check for cycles is rock solid. */ | |||||
| if (periodicity == Alembic::AbcGeom::kPeriodic && (KNOTSU(nu) == knots->size() - 2)) { | |||||
| /* Skip first and last knots. */ | |||||
| for (size_t i = 1; i < knots->size() - 1; i++) { | |||||
| nu->knotsu[i - 1] = (*knots)[knot_offset + i]; | |||||
| } | |||||
| } | |||||
| else { | |||||
| /* TODO: figure out how to use the knots array from other | |||||
| * software in this case. */ | |||||
| BKE_nurb_knot_calc_u(nu); | |||||
| } | |||||
| knot_offset += knots->size(); | |||||
| } | |||||
| else { | |||||
| BKE_nurb_knot_calc_u(nu); | |||||
| } | |||||
| BLI_addtail(BKE_curve_nurbs_get(cu), nu); | |||||
| } | |||||
| } | |||||
| /* NOTE: Alembic only stores data about control points, but the Mesh | |||||
| * passed from the cache modifier contains the displist, which has more data | |||||
| * than the control points, so to avoid corrupting the displist we modify the | |||||
| * object directly and create a new Mesh from that. Also we might need to | |||||
| * create new or delete existing NURBS in the curve. | |||||
| */ | |||||
| Mesh *AbcCurveReader::read_mesh(Mesh *existing_mesh, | |||||
| const ISampleSelector &sample_sel, | |||||
| int /*read_flag*/, | |||||
| const char **err_str) | |||||
| { | |||||
| ICurvesSchema::Sample sample; | |||||
| try { | |||||
| sample = m_curves_schema.getValue(sample_sel); | |||||
| } | |||||
| catch (Alembic::Util::Exception &ex) { | |||||
| *err_str = "Error reading curve sample; more detail on the console"; | |||||
| printf("Alembic: error reading curve sample for '%s/%s' at time %f: %s\n", | |||||
| m_iobject.getFullName().c_str(), | |||||
| m_curves_schema.getName().c_str(), | |||||
| sample_sel.getRequestedTime(), | |||||
| ex.what()); | |||||
| return existing_mesh; | |||||
| } | |||||
| const P3fArraySamplePtr &positions = sample.getPositions(); | |||||
| const Int32ArraySamplePtr num_vertices = sample.getCurvesNumVertices(); | |||||
| int vertex_idx = 0; | |||||
| int curve_idx; | |||||
| Curve *curve = static_cast<Curve *>(m_object->data); | |||||
| const int curve_count = BLI_listbase_count(&curve->nurb); | |||||
| bool same_topology = curve_count == num_vertices->size(); | |||||
| if (same_topology) { | |||||
| Nurb *nurbs = static_cast<Nurb *>(curve->nurb.first); | |||||
| for (curve_idx = 0; nurbs; nurbs = nurbs->next, curve_idx++) { | |||||
| const int num_in_alembic = (*num_vertices)[curve_idx]; | |||||
| const int num_in_blender = nurbs->pntsu; | |||||
| if (num_in_alembic != num_in_blender) { | |||||
| same_topology = false; | |||||
| break; | |||||
| } | |||||
| } | |||||
| } | |||||
| if (!same_topology) { | |||||
| BKE_nurbList_free(&curve->nurb); | |||||
| read_curve_sample(curve, m_curves_schema, sample_sel); | |||||
| } | |||||
| else { | |||||
| Nurb *nurbs = static_cast<Nurb *>(curve->nurb.first); | |||||
| for (curve_idx = 0; nurbs; nurbs = nurbs->next, curve_idx++) { | |||||
| const int totpoint = (*num_vertices)[curve_idx]; | |||||
| if (nurbs->bp) { | |||||
| BPoint *point = nurbs->bp; | |||||
| for (int i = 0; i < totpoint; i++, point++, vertex_idx++) { | |||||
| const Imath::V3f &pos = (*positions)[vertex_idx]; | |||||
| copy_zup_from_yup(point->vec, pos.getValue()); | |||||
| } | |||||
| } | |||||
| else if (nurbs->bezt) { | |||||
| BezTriple *bezier = nurbs->bezt; | |||||
| for (int i = 0; i < totpoint; i++, bezier++, vertex_idx++) { | |||||
| const Imath::V3f &pos = (*positions)[vertex_idx]; | |||||
| copy_zup_from_yup(bezier->vec[1], pos.getValue()); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| return BKE_mesh_new_nomain_from_curve(m_object); | |||||
| } | |||||