diff options
author | Dalai Felinto <dfelinto@gmail.com> | 2016-10-20 20:44:51 +0300 |
---|---|---|
committer | Dalai Felinto <dfelinto@gmail.com> | 2016-10-20 20:44:51 +0300 |
commit | 6f81e808e3e4d9ab6a82fbe27c95e180cb7de170 (patch) | |
tree | eb93e9dd15d6c0b400a7d9fc20e4d03ba24deee2 /source | |
parent | d0989e538effc1c8b5e948224beaad25ab4bd569 (diff) | |
parent | 2b240b0430787814316f3b3166453c6d85b017f3 (diff) |
Merge commit '2b240b0430787814316f3b3166453c6d85b017f3' into pbr-merge
Conflicts:
release/datafiles/splash.png
release/datafiles/splash_2x.png
source/blender/gpu/CMakeLists.txt
source/blender/gpu/intern/gpu_shader.c
Diffstat (limited to 'source')
298 files changed, 14376 insertions, 2190 deletions
diff --git a/source/blender/CMakeLists.txt b/source/blender/CMakeLists.txt index e36f9e2b43e..6f2b78e0845 100644 --- a/source/blender/CMakeLists.txt +++ b/source/blender/CMakeLists.txt @@ -31,6 +31,7 @@ set(SRC_DNA_INC ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_armature_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_boid_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_brush_types.h + ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_cachefile_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_camera_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_cloth_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_color_types.h @@ -153,3 +154,6 @@ if(WITH_FREESTYLE) add_subdirectory(freestyle) endif() +if(WITH_ALEMBIC) + add_subdirectory(alembic) +endif() diff --git a/source/blender/alembic/ABC_alembic.h b/source/blender/alembic/ABC_alembic.h new file mode 100644 index 00000000000..cf121f8488c --- /dev/null +++ b/source/blender/alembic/ABC_alembic.h @@ -0,0 +1,110 @@ +/* + * ***** 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. + * + * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __ABC_ALEMBIC_H__ +#define __ABC_ALEMBIC_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +struct bContext; +struct DerivedMesh; +struct ListBase; +struct Object; +struct Scene; + +typedef struct AbcArchiveHandle AbcArchiveHandle; + +enum { + ABC_ARCHIVE_OGAWA = 0, + ABC_ARCHIVE_HDF5 = 1, +}; + +int ABC_get_version(void); + +struct AlembicExportParams { + double frame_start; + double frame_end; + + double frame_step_xform; + double frame_step_shape; + + double shutter_open; + double shutter_close; + + /* bools */ + unsigned int selected_only : 1; + unsigned int uvs : 1; + unsigned int normals : 1; + unsigned int vcolors : 1; + unsigned int apply_subdiv : 1; + unsigned int flatten_hierarchy : 1; + unsigned int visible_layers_only : 1; + unsigned int renderable_only : 1; + unsigned int face_sets : 1; + unsigned int use_subdiv_schema : 1; + unsigned int packuv : 1; + + unsigned int compression_type : 1; + float global_scale; +}; + +void ABC_export( + struct Scene *scene, + struct bContext *C, + const char *filepath, + const struct AlembicExportParams *params); + +void ABC_import(struct bContext *C, + const char *filepath, + float scale, + bool is_sequence, + bool set_frame_range, + int sequence_len, + int offset, + bool validate_meshes); + +AbcArchiveHandle *ABC_create_handle(const char *filename, struct ListBase *object_paths); + +void ABC_free_handle(AbcArchiveHandle *handle); + +void ABC_get_transform(AbcArchiveHandle *handle, + struct Object *ob, + const char *object_path, + float r_mat[4][4], + float time, + float scale); + +struct DerivedMesh *ABC_read_mesh(AbcArchiveHandle *handle, + struct Object *ob, + struct DerivedMesh *dm, + const char *object_path, + const float time, + const char **err_str, + int flags); + +#ifdef __cplusplus +} +#endif + +#endif /* __ABC_ALEMBIC_H__ */ diff --git a/source/blender/alembic/CMakeLists.txt b/source/blender/alembic/CMakeLists.txt new file mode 100644 index 00000000000..0b6b2433bd0 --- /dev/null +++ b/source/blender/alembic/CMakeLists.txt @@ -0,0 +1,82 @@ +# ***** 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) 2006, Blender Foundation +# All rights reserved. +# +# The Original Code is: all of this file. +# +# Contributor(s): Kevin Dietrich. +# +# ***** END GPL LICENSE BLOCK ***** + +set(INC + . + ../blenkernel + ../blenlib + ../blenloader + ../editors/include + ../makesdna + ../makesrna + ../windowmanager + ../../../intern/guardedalloc + ../../../intern/utfconv +) + +set(INC_SYS + ${ALEMBIC_INCLUDE_DIRS} + ${HDF5_INCLUDE_DIRS} + ${OPENEXR_INCLUDE_DIRS} +) +if(APPLE OR WIN32) + list(APPEND INC_SYS + ${BOOST_INCLUDE_DIR} + ) +endif() + +set(SRC + intern/abc_camera.cc + intern/abc_customdata.cc + intern/abc_curves.cc + intern/abc_exporter.cc + intern/abc_hair.cc + intern/abc_mesh.cc + intern/abc_nurbs.cc + intern/abc_object.cc + intern/abc_points.cc + intern/abc_transform.cc + intern/abc_util.cc + intern/alembic_capi.cc + + ABC_alembic.h + intern/abc_camera.h + intern/abc_customdata.h + intern/abc_curves.h + intern/abc_exporter.h + intern/abc_hair.h + intern/abc_mesh.h + intern/abc_nurbs.h + intern/abc_object.h + intern/abc_points.h + intern/abc_transform.h + intern/abc_util.h +) + +if(WITH_ALEMBIC_HDF5) + add_definitions(-DWITH_ALEMBIC_HDF5) +endif() + +blender_add_lib(bf_alembic "${SRC}" "${INC}" "${INC_SYS}") diff --git a/source/blender/alembic/intern/abc_camera.cc b/source/blender/alembic/intern/abc_camera.cc new file mode 100644 index 00000000000..5c34ec1391f --- /dev/null +++ b/source/blender/alembic/intern/abc_camera.cc @@ -0,0 +1,162 @@ +/* + * ***** 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. + * + * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "abc_camera.h" + +#include "abc_transform.h" +#include "abc_util.h" + +extern "C" { +#include "DNA_camera_types.h" +#include "DNA_object_types.h" + +#include "BKE_camera.h" +#include "BKE_object.h" + +#include "BLI_math.h" +#include "BLI_string.h" +} + +using Alembic::AbcGeom::ICamera; +using Alembic::AbcGeom::ICompoundProperty; +using Alembic::AbcGeom::IFloatProperty; +using Alembic::AbcGeom::ISampleSelector; + +using Alembic::AbcGeom::OCamera; +using Alembic::AbcGeom::OFloatProperty; + +using Alembic::AbcGeom::CameraSample; +using Alembic::AbcGeom::kWrapExisting; + +/* ************************************************************************** */ + +AbcCameraWriter::AbcCameraWriter(Scene *scene, + Object *ob, + AbcTransformWriter *parent, + uint32_t time_sampling, + ExportSettings &settings) + : AbcObjectWriter(scene, ob, time_sampling, settings, parent) +{ + OCamera camera(parent->alembicXform(), m_name, m_time_sampling); + m_camera_schema = camera.getSchema(); + + m_custom_data_container = m_camera_schema.getUserProperties(); + m_stereo_distance = OFloatProperty(m_custom_data_container, "stereoDistance", m_time_sampling); + m_eye_separation = OFloatProperty(m_custom_data_container, "eyeSeparation", m_time_sampling); +} + +void AbcCameraWriter::do_write() +{ + Camera *cam = static_cast<Camera *>(m_object->data); + + m_stereo_distance.set(cam->stereo.convergence_distance); + m_eye_separation.set(cam->stereo.interocular_distance); + + const double apperture_x = cam->sensor_x / 10.0; + const double apperture_y = cam->sensor_y / 10.0; + const double film_aspect = apperture_x / apperture_y; + + m_camera_sample.setFocalLength(cam->lens); + m_camera_sample.setHorizontalAperture(apperture_x); + m_camera_sample.setVerticalAperture(apperture_y); + m_camera_sample.setHorizontalFilmOffset(apperture_x * cam->shiftx); + m_camera_sample.setVerticalFilmOffset(apperture_y * cam->shifty * film_aspect); + m_camera_sample.setNearClippingPlane(cam->clipsta); + m_camera_sample.setFarClippingPlane(cam->clipend); + + if (cam->dof_ob) { + Imath::V3f v(m_object->loc[0] - cam->dof_ob->loc[0], + m_object->loc[1] - cam->dof_ob->loc[1], + m_object->loc[2] - cam->dof_ob->loc[2]); + m_camera_sample.setFocusDistance(v.length()); + } + else { + m_camera_sample.setFocusDistance(cam->gpu_dof.focus_distance); + } + + /* Blender camera does not have an fstop param, so try to find a custom prop + * instead. */ + m_camera_sample.setFStop(cam->gpu_dof.fstop); + + m_camera_sample.setLensSqueezeRatio(1.0); + m_camera_schema.set(m_camera_sample); +} + +/* ************************************************************************** */ + +AbcCameraReader::AbcCameraReader(const Alembic::Abc::IObject &object, ImportSettings &settings) + : AbcObjectReader(object, settings) +{ + ICamera abc_cam(m_iobject, kWrapExisting); + m_schema = abc_cam.getSchema(); + + get_min_max_time(m_iobject, m_schema, m_min_time, m_max_time); +} + +bool AbcCameraReader::valid() const +{ + return m_schema.valid(); +} + +void AbcCameraReader::readObjectData(Main *bmain, float time) +{ + Camera *bcam = static_cast<Camera *>(BKE_camera_add(bmain, "abc_camera")); + + ISampleSelector sample_sel(time); + CameraSample cam_sample; + m_schema.get(cam_sample, sample_sel); + + ICompoundProperty customDataContainer = m_schema.getUserProperties(); + + if (customDataContainer.valid() && + customDataContainer.getPropertyHeader("stereoDistance") && + customDataContainer.getPropertyHeader("eyeSeparation")) + { + IFloatProperty convergence_plane(customDataContainer, "stereoDistance"); + IFloatProperty eye_separation(customDataContainer, "eyeSeparation"); + + bcam->stereo.interocular_distance = eye_separation.getValue(sample_sel); + bcam->stereo.convergence_distance = convergence_plane.getValue(sample_sel); + } + + const float lens = cam_sample.getFocalLength(); + const float apperture_x = cam_sample.getHorizontalAperture(); + const float apperture_y = cam_sample.getVerticalAperture(); + const float h_film_offset = cam_sample.getHorizontalFilmOffset(); + const float v_film_offset = cam_sample.getVerticalFilmOffset(); + const float film_aspect = apperture_x / apperture_y; + + bcam->lens = lens; + bcam->sensor_x = apperture_x * 10; + bcam->sensor_y = apperture_y * 10; + bcam->shiftx = h_film_offset / apperture_x; + bcam->shifty = v_film_offset / apperture_y / film_aspect; + bcam->clipsta = max_ff(0.1f, cam_sample.getNearClippingPlane()); + bcam->clipend = cam_sample.getFarClippingPlane(); + bcam->gpu_dof.focus_distance = cam_sample.getFocusDistance(); + bcam->gpu_dof.fstop = cam_sample.getFStop(); + + BLI_strncpy(bcam->id.name + 2, m_data_name.c_str(), m_data_name.size() + 1); + + m_object = BKE_object_add_only_object(bmain, OB_CAMERA, m_object_name.c_str()); + m_object->data = bcam; +} diff --git a/source/blender/alembic/intern/abc_camera.h b/source/blender/alembic/intern/abc_camera.h new file mode 100644 index 00000000000..fafb4d3eb39 --- /dev/null +++ b/source/blender/alembic/intern/abc_camera.h @@ -0,0 +1,61 @@ +/* + * ***** 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. + * + * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __ABC_CAMERA_H__ +#define __ABC_CAMERA_H__ + +#include "abc_object.h" + +/* ************************************************************************** */ + +class AbcCameraWriter : public AbcObjectWriter { + Alembic::AbcGeom::OCameraSchema m_camera_schema; + Alembic::AbcGeom::CameraSample m_camera_sample; + Alembic::AbcGeom::OCompoundProperty m_custom_data_container; + Alembic::AbcGeom::OFloatProperty m_stereo_distance; + Alembic::AbcGeom::OFloatProperty m_eye_separation; + +public: + AbcCameraWriter(Scene *scene, + Object *ob, + AbcTransformWriter *parent, + uint32_t time_sampling, + ExportSettings &settings); + +private: + virtual void do_write(); +}; + +/* ************************************************************************** */ + +class AbcCameraReader : public AbcObjectReader { + Alembic::AbcGeom::ICameraSchema m_schema; + +public: + AbcCameraReader(const Alembic::Abc::IObject &object, ImportSettings &settings); + + bool valid() const; + + void readObjectData(Main *bmain, float time); +}; + +#endif /* __ABC_CAMERA_H__ */
\ No newline at end of file diff --git a/source/blender/alembic/intern/abc_curves.cc b/source/blender/alembic/intern/abc_curves.cc new file mode 100644 index 00000000000..2b54741a5c5 --- /dev/null +++ b/source/blender/alembic/intern/abc_curves.cc @@ -0,0 +1,355 @@ +/* + * ***** 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) 2016 Kévin Dietrich. + * All rights reserved. + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +#include "abc_curves.h" + +#include <cstdio> + +#include "abc_transform.h" +#include "abc_util.h" + +extern "C" { +#include "MEM_guardedalloc.h" + +#include "DNA_curve_types.h" +#include "DNA_object_types.h" + +#include "BLI_listbase.h" + +#include "BKE_curve.h" +#include "BKE_object.h" + +#include "ED_curve.h" +} + +using Alembic::Abc::IInt32ArrayProperty; +using Alembic::Abc::Int32ArraySamplePtr; +using Alembic::Abc::FloatArraySamplePtr; +using Alembic::Abc::P3fArraySamplePtr; +using Alembic::Abc::UcharArraySamplePtr; + +using Alembic::AbcGeom::ICurves; +using Alembic::AbcGeom::ICurvesSchema; +using Alembic::AbcGeom::IFloatGeomParam; +using Alembic::AbcGeom::ISampleSelector; +using Alembic::AbcGeom::kWrapExisting; +using Alembic::AbcGeom::CurvePeriodicity; + +using Alembic::AbcGeom::OCurves; +using Alembic::AbcGeom::OCurvesSchema; +using Alembic::AbcGeom::ON3fGeomParam; +using Alembic::AbcGeom::OV2fGeomParam; + +/* ************************************************************************** */ + +AbcCurveWriter::AbcCurveWriter(Scene *scene, + Object *ob, + AbcTransformWriter *parent, + uint32_t time_sampling, + ExportSettings &settings) + : AbcObjectWriter(scene, ob, time_sampling, settings, parent) +{ + OCurves curves(parent->alembicXform(), m_name, m_time_sampling); + m_schema = curves.getSchema(); +} + +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::kLinear; + + const int totpoint = nurbs->pntsu * nurbs->pntsv; + + const BPoint *point = nurbs->bp; + + for (int i = 0; i < totpoint; ++i, ++point) { + copy_zup_yup(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_zup_yup(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 beggining 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 + 1); + 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); +} + +/* ************************************************************************** */ + +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(); +} + +void AbcCurveReader::readObjectData(Main *bmain, float time) +{ + 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; + + 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, time); + + if (has_animations(m_curves_schema, m_settings)) { + addCacheModifier(); + } +} + +/* ************************************************************************** */ + +void read_curve_sample(Curve *cu, const ICurvesSchema &schema, const float time) +{ + const ISampleSelector sample_sel(time); + ICurvesSchema::Sample smp = schema.getValue(sample_sel); + 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; + + nu->orderu = num_verts; + + if (smp.getType() == Alembic::AbcGeom::kCubic) { + nu->orderu = 3; + } + else if (orders && orders->size() > i) { + nu->orderu = static_cast<short>((*orders)[i] - 1); + } + + 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_yup_zup(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); + } +} diff --git a/source/blender/alembic/intern/abc_curves.h b/source/blender/alembic/intern/abc_curves.h new file mode 100644 index 00000000000..ee47f1931ea --- /dev/null +++ b/source/blender/alembic/intern/abc_curves.h @@ -0,0 +1,65 @@ +/* + * ***** 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) 2016 Kévin Dietrich. + * All rights reserved. + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +#ifndef __ABC_CURVES_H__ +#define __ABC_CURVES_H__ + +#include "abc_object.h" + +struct Curve; + +/* ************************************************************************** */ + +class AbcCurveWriter : public AbcObjectWriter { + Alembic::AbcGeom::OCurvesSchema m_schema; + Alembic::AbcGeom::OCurvesSchema::Sample m_sample; + +public: + AbcCurveWriter(Scene *scene, + Object *ob, + AbcTransformWriter *parent, + uint32_t time_sampling, + ExportSettings &settings); + + void do_write(); +}; + +/* ************************************************************************** */ + +class AbcCurveReader : public AbcObjectReader { + Alembic::AbcGeom::ICurvesSchema m_curves_schema; + +public: + AbcCurveReader(const Alembic::Abc::IObject &object, ImportSettings &settings); + + bool valid() const; + + void readObjectData(Main *bmain, float time); +}; + +/* ************************************************************************** */ + +void read_curve_sample(Curve *cu, const Alembic::AbcGeom::ICurvesSchema &schema, const float time); + +#endif /* __ABC_CURVES_H__ */
\ No newline at end of file diff --git a/source/blender/alembic/intern/abc_customdata.cc b/source/blender/alembic/intern/abc_customdata.cc new file mode 100644 index 00000000000..ebf1b2ba96e --- /dev/null +++ b/source/blender/alembic/intern/abc_customdata.cc @@ -0,0 +1,379 @@ +/* + * ***** 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) 2016 Kévin Dietrich. + * All rights reserved. + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +#include "abc_customdata.h" + +#include <Alembic/AbcGeom/All.h> +#include <algorithm> + +extern "C" { +#include "DNA_customdata_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_customdata.h" +} + +/* NOTE: for now only UVs and Vertex Colors are supported for streaming. + * Although Alembic only allows for a single UV layer per {I|O}Schema, and does + * not have a vertex color concept, there is a convention between DCCs to write + * such data in a way that lets other DCC know what they are for. See comments + * in the write code for the conventions. */ + +using Alembic::AbcGeom::kVertexScope; +using Alembic::AbcGeom::kFacevaryingScope; + +using Alembic::Abc::C4fArraySample; +using Alembic::Abc::UInt32ArraySample; +using Alembic::Abc::V2fArraySample; + +using Alembic::AbcGeom::OV2fGeomParam; +using Alembic::AbcGeom::OC4fGeomParam; + +static void get_uvs(const CDStreamConfig &config, + std::vector<Imath::V2f> &uvs, + std::vector<uint32_t> &uvidx, + void *cd_data) +{ + MLoopUV *mloopuv_array = static_cast<MLoopUV *>(cd_data); + + if (!mloopuv_array) { + return; + } + + const int num_poly = config.totpoly; + MPoly *polygons = config.mpoly; + + if (!config.pack_uvs) { + int cnt = 0; + uvidx.resize(config.totloop); + uvs.resize(config.totloop); + + for (int i = 0; i < num_poly; ++i) { + MPoly ¤t_poly = polygons[i]; + MLoopUV *loopuvpoly = mloopuv_array + current_poly.loopstart + current_poly.totloop; + + for (int j = 0; j < current_poly.totloop; ++j, ++cnt) { + --loopuvpoly; + + uvidx[cnt] = cnt; + uvs[cnt][0] = loopuvpoly->uv[0]; + uvs[cnt][1] = loopuvpoly->uv[1]; + } + } + } + else { + for (int i = 0; i < num_poly; ++i) { + MPoly ¤t_poly = polygons[i]; + MLoopUV *loopuvpoly = mloopuv_array + current_poly.loopstart + current_poly.totloop; + + for (int j = 0; j < current_poly.totloop; ++j) { + loopuvpoly--; + Imath::V2f uv(loopuvpoly->uv[0], loopuvpoly->uv[1]); + + std::vector<Imath::V2f>::iterator it = std::find(uvs.begin(), uvs.end(), uv); + + if (it == uvs.end()) { + uvidx.push_back(uvs.size()); + uvs.push_back(uv); + } + else { + uvidx.push_back(std::distance(uvs.begin(), it)); + } + } + } + } +} + +const char *get_uv_sample(UVSample &sample, const CDStreamConfig &config, CustomData *data) +{ + const int active_uvlayer = CustomData_get_active_layer(data, CD_MLOOPUV); + + if (active_uvlayer < 0) { + return ""; + } + + void *cd_data = CustomData_get_layer_n(data, CD_MLOOPUV, active_uvlayer); + + get_uvs(config, sample.uvs, sample.indices, cd_data); + + return CustomData_get_layer_name(data, CD_MLOOPUV, active_uvlayer); +} + +/* Convention to write UVs: + * - V2fGeomParam on the arbGeomParam + * - set scope as face varying + * - (optional due to its behaviour) tag as UV using Alembic::AbcGeom::SetIsUV + */ +static void write_uv(const OCompoundProperty &prop, const CDStreamConfig &config, void *data, const char *name) +{ + std::vector<uint32_t> indices; + std::vector<Imath::V2f> uvs; + + get_uvs(config, uvs, indices, data); + + if (indices.empty() || uvs.empty()) { + return; + } + + OV2fGeomParam param(prop, name, true, kFacevaryingScope, 1); + + OV2fGeomParam::Sample sample( + V2fArraySample(&uvs.front(), uvs.size()), + UInt32ArraySample(&indices.front(), indices.size()), + kFacevaryingScope); + + param.set(sample); +} + +/* Convention to write Vertex Colors: + * - C3fGeomParam/C4fGeomParam on the arbGeomParam + * - set scope as vertex varying + */ +static void write_mcol(const OCompoundProperty &prop, const CDStreamConfig &config, void *data, const char *name) +{ + const float cscale = 1.0f / 255.0f; + MPoly *polys = config.mpoly; + MLoop *mloops = config.mloop; + MCol *cfaces = static_cast<MCol *>(data); + + std::vector<Imath::C4f> buffer(config.totvert); + + Imath::C4f col; + + for (int i = 0; i < config.totpoly; ++i) { + MPoly *p = &polys[i]; + MCol *cface = &cfaces[p->loopstart + p->totloop]; + MLoop *mloop = &mloops[p->loopstart + p->totloop]; + + for (int j = 0; j < p->totloop; ++j) { + cface--; + mloop--; + + col[0] = cface->a * cscale; + col[1] = cface->r * cscale; + col[2] = cface->g * cscale; + col[3] = cface->b * cscale; + + buffer[mloop->v] = col; + } + } + + OC4fGeomParam param(prop, name, true, kFacevaryingScope, 1); + + OC4fGeomParam::Sample sample( + C4fArraySample(&buffer.front(), buffer.size()), + kVertexScope); + + param.set(sample); +} + +void write_custom_data(const OCompoundProperty &prop, const CDStreamConfig &config, CustomData *data, int data_type) +{ + CustomDataType cd_data_type = static_cast<CustomDataType>(data_type); + + if (!CustomData_has_layer(data, cd_data_type)) { + return; + } + + const int active_layer = CustomData_get_active_layer(data, cd_data_type); + const int tot_layers = CustomData_number_of_layers(data, cd_data_type); + + for (int i = 0; i < tot_layers; ++i) { + void *cd_data = CustomData_get_layer_n(data, cd_data_type, i); + const char *name = CustomData_get_layer_name(data, cd_data_type, i); + + if (cd_data_type == CD_MLOOPUV) { + /* Already exported. */ + if (i == active_layer) { + continue; + } + + write_uv(prop, config, cd_data, name); + } + else if (cd_data_type == CD_MLOOPCOL) { + write_mcol(prop, config, cd_data, name); + } + } +} + +/* ************************************************************************** */ + +using Alembic::Abc::C3fArraySamplePtr; +using Alembic::Abc::C4fArraySamplePtr; +using Alembic::Abc::PropertyHeader; + +using Alembic::AbcGeom::IC3fGeomParam; +using Alembic::AbcGeom::IC4fGeomParam; +using Alembic::AbcGeom::IV2fGeomParam; + +static void read_mcols(const CDStreamConfig &config, void *data, + const C3fArraySamplePtr &c3f_ptr, const C4fArraySamplePtr &c4f_ptr) +{ + MCol *cfaces = static_cast<MCol *>(data); + MPoly *polys = config.mpoly; + MLoop *mloops = config.mloop; + + if (c3f_ptr) { + for (int i = 0; i < config.totpoly; ++i) { + MPoly *p = &polys[i]; + MCol *cface = &cfaces[p->loopstart + p->totloop]; + MLoop *mloop = &mloops[p->loopstart + p->totloop]; + + for (int j = 0; j < p->totloop; ++j) { + cface--; + mloop--; + const Imath::C3f &color = (*c3f_ptr)[mloop->v]; + cface->a = FTOCHAR(color[0]); + cface->r = FTOCHAR(color[1]); + cface->g = FTOCHAR(color[2]); + cface->b = 255; + } + } + } + else if (c4f_ptr) { + for (int i = 0; i < config.totpoly; ++i) { + MPoly *p = &polys[i]; + MCol *cface = &cfaces[p->loopstart + p->totloop]; + MLoop *mloop = &mloops[p->loopstart + p->totloop]; + + for (int j = 0; j < p->totloop; ++j) { + cface--; + mloop--; + const Imath::C4f &color = (*c4f_ptr)[mloop->v]; + cface->a = FTOCHAR(color[0]); + cface->r = FTOCHAR(color[1]); + cface->g = FTOCHAR(color[2]); + cface->b = FTOCHAR(color[3]); + } + } + } +} + +static void read_uvs(const CDStreamConfig &config, void *data, + const Alembic::AbcGeom::V2fArraySamplePtr &uvs, + const Alembic::AbcGeom::UInt32ArraySamplePtr &indices) +{ + MPoly *mpolys = config.mpoly; + MLoopUV *mloopuvs = static_cast<MLoopUV *>(data); + + unsigned int uv_index, loop_index; + + for (int i = 0; i < config.totpoly; ++i) { + MPoly &poly = mpolys[i]; + + for (int f = 0; f < poly.totloop; ++f) { + loop_index = poly.loopstart + f; + uv_index = (*indices)[loop_index]; + const Imath::V2f &uv = (*uvs)[uv_index]; + + MLoopUV &loopuv = mloopuvs[loop_index]; + loopuv.uv[0] = uv[0]; + loopuv.uv[1] = uv[1]; + } + } +} + +static void read_custom_data_ex(const ICompoundProperty &prop, + const PropertyHeader &prop_header, + const CDStreamConfig &config, + const Alembic::Abc::ISampleSelector &iss, + int data_type) +{ + if (data_type == CD_MLOOPCOL) { + C3fArraySamplePtr c3f_ptr = C3fArraySamplePtr(); + C4fArraySamplePtr c4f_ptr = C4fArraySamplePtr(); + + if (IC3fGeomParam::matches(prop_header)) { + IC3fGeomParam color_param(prop, prop_header.getName()); + IC3fGeomParam::Sample sample; + color_param.getIndexed(sample, iss); + + c3f_ptr = sample.getVals(); + } + else if (IC4fGeomParam::matches(prop_header)) { + IC4fGeomParam color_param(prop, prop_header.getName()); + IC4fGeomParam::Sample sample; + color_param.getIndexed(sample, iss); + + c4f_ptr = sample.getVals(); + } + + void *cd_data = config.add_customdata_cb(config.user_data, + prop_header.getName().c_str(), + data_type); + + read_mcols(config, cd_data, c3f_ptr, c4f_ptr); + } + else if (data_type == CD_MLOOPUV) { + IV2fGeomParam uv_param(prop, prop_header.getName()); + IV2fGeomParam::Sample sample; + uv_param.getIndexed(sample, iss); + + if (uv_param.getScope() != kFacevaryingScope) { + return; + } + + void *cd_data = config.add_customdata_cb(config.user_data, + prop_header.getName().c_str(), + data_type); + + read_uvs(config, cd_data, sample.getVals(), sample.getIndices()); + } +} + +void read_custom_data(const ICompoundProperty &prop, const CDStreamConfig &config, const Alembic::Abc::ISampleSelector &iss) +{ + if (!prop.valid()) { + return; + } + + int num_uvs = 0; + int num_colors = 0; + + const size_t num_props = prop.getNumProperties(); + + for (size_t i = 0; i < num_props; ++i) { + const Alembic::Abc::PropertyHeader &prop_header = prop.getPropertyHeader(i); + + /* Read UVs according to convention. */ + if (IV2fGeomParam::matches(prop_header) && Alembic::AbcGeom::isUV(prop_header)) { + if (++num_uvs > MAX_MTFACE) { + continue; + } + + read_custom_data_ex(prop, prop_header, config, iss, CD_MLOOPUV); + continue; + } + + /* Read vertex colors according to convention. */ + if (IC3fGeomParam::matches(prop_header) || IC4fGeomParam::matches(prop_header)) { + if (++num_colors > MAX_MCOL) { + continue; + } + + read_custom_data_ex(prop, prop_header, config, iss, CD_MLOOPCOL); + continue; + } + } +} diff --git a/source/blender/alembic/intern/abc_customdata.h b/source/blender/alembic/intern/abc_customdata.h new file mode 100644 index 00000000000..3b16c0d9796 --- /dev/null +++ b/source/blender/alembic/intern/abc_customdata.h @@ -0,0 +1,93 @@ +/* + * ***** 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) 2016 Kévin Dietrich. + * All rights reserved. + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +#ifndef __ABC_CUSTOMDATA_H__ +#define __ABC_CUSTOMDATA_H__ + +#include <Alembic/Abc/All.h> + +struct CustomData; +struct MLoop; +struct MLoopUV; +struct MPoly; +struct MVert; + +using Alembic::Abc::ICompoundProperty; +using Alembic::Abc::OCompoundProperty; + +struct UVSample { + std::vector<Imath::V2f> uvs; + std::vector<uint32_t> indices; +}; + +struct CDStreamConfig { + MLoop *mloop; + int totloop; + + MPoly *mpoly; + int totpoly; + + MVert *mvert; + int totvert; + + MLoopUV *mloopuv; + + CustomData *loopdata; + + bool pack_uvs; + + /* TODO(kevin): might need a better way to handle adding and/or updating + * custom datas such that it updates the custom data holder and its pointers + * properly. */ + void *user_data; + void *(*add_customdata_cb)(void *user_data, const char *name, int data_type); + + CDStreamConfig() + : mloop(NULL) + , totloop(0) + , mpoly(NULL) + , totpoly(0) + , totvert(0) + , pack_uvs(false) + , user_data(NULL) + , add_customdata_cb(NULL) + {} +}; + +/* Get the UVs for the main UV property on a OSchema. + * Returns the name of the UV layer. + * + * For now the active layer is used, maybe needs a better way to choose this. */ +const char *get_uv_sample(UVSample &sample, const CDStreamConfig &config, CustomData *data); + +void write_custom_data(const OCompoundProperty &prop, + const CDStreamConfig &config, + CustomData *data, + int data_type); + +void read_custom_data(const ICompoundProperty &prop, + const CDStreamConfig &config, + const Alembic::Abc::ISampleSelector &iss); + +#endif /* __ABC_CUSTOMDATA_H__ */ diff --git a/source/blender/alembic/intern/abc_exporter.cc b/source/blender/alembic/intern/abc_exporter.cc new file mode 100644 index 00000000000..764b1533309 --- /dev/null +++ b/source/blender/alembic/intern/abc_exporter.cc @@ -0,0 +1,654 @@ +/* + * ***** 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. + * + * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "abc_exporter.h" + +#include <cmath> + +#ifdef WITH_ALEMBIC_HDF5 +# include <Alembic/AbcCoreHDF5/All.h> +#endif + +#include <Alembic/AbcCoreOgawa/All.h> + +#ifdef WIN32 +# include "utfconv.h" +#endif + +#include "abc_camera.h" +#include "abc_curves.h" +#include "abc_hair.h" +#include "abc_mesh.h" +#include "abc_nurbs.h" +#include "abc_points.h" +#include "abc_transform.h" +#include "abc_util.h" + +extern "C" { +#include "DNA_camera_types.h" +#include "DNA_curve_types.h" +#include "DNA_mesh_types.h" +#include "DNA_modifier_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_space_types.h" /* for FILE_MAX */ + +#include "BLI_string.h" + +#ifdef WIN32 +/* needed for MSCV because of snprintf from BLI_string */ +# include "BLI_winstuff.h" +#endif + +#include "BKE_anim.h" +#include "BKE_global.h" +#include "BKE_idprop.h" +#include "BKE_main.h" +#include "BKE_modifier.h" +#include "BKE_particle.h" +#include "BKE_scene.h" +} + +using Alembic::Abc::TimeSamplingPtr; +using Alembic::Abc::OBox3dProperty; + + +/* ************************************************************************** */ + +/* This kinda duplicates CreateArchiveWithInfo, but Alembic does not seem to + * have a version supporting streams. */ +static Alembic::Abc::OArchive create_archive(std::ostream *ostream, + const std::string &filename, + const std::string &scene_name, + const Alembic::Abc::Argument &arg0, + const Alembic::Abc::Argument &arg1, + bool ogawa) +{ + Alembic::Abc::MetaData md = GetMetaData(arg0, arg1); + md.set(Alembic::Abc::kApplicationNameKey, "Blender"); + md.set(Alembic::Abc::kUserDescriptionKey, scene_name); + + time_t raw_time; + time(&raw_time); + char buffer[128]; + +#if defined _WIN32 || defined _WIN64 + ctime_s(buffer, 128, &raw_time); +#else + ctime_r(&raw_time, buffer); +#endif + + const std::size_t buffer_len = strlen(buffer); + if (buffer_len > 0 && buffer[buffer_len - 1] == '\n') { + buffer[buffer_len - 1] = '\0'; + } + + md.set(Alembic::Abc::kDateWrittenKey, buffer); + + Alembic::Abc::ErrorHandler::Policy policy = GetErrorHandlerPolicyFromArgs(arg0, arg1); + +#ifdef WITH_ALEMBIC_HDF5 + if (!ogawa) { + return Alembic::Abc::OArchive(Alembic::AbcCoreHDF5::WriteArchive(), filename, md, policy); + } +#else + static_cast<void>(filename); + static_cast<void>(ogawa); +#endif + + Alembic::AbcCoreOgawa::WriteArchive archive_writer; + return Alembic::Abc::OArchive(archive_writer(ostream, md), Alembic::Abc::kWrapExisting, policy); +} + +/* ************************************************************************** */ + +ExportSettings::ExportSettings() + : scene(NULL) + , selected_only(false) + , visible_layers_only(false) + , renderable_only(false) + , frame_start(1) + , frame_end(1) + , frame_step_xform(1) + , frame_step_shape(1) + , shutter_open(0.0) + , shutter_close(1.0) + , global_scale(1.0f) + , flatten_hierarchy(false) + , export_normals(false) + , export_uvs(false) + , export_vcols(false) + , export_face_sets(false) + , export_vweigths(false) + , apply_subdiv(false) + , use_subdiv_schema(false) + , export_child_hairs(true) + , export_ogawa(true) + , pack_uv(false) + , do_convert_axis(false) +{} + +static bool object_is_smoke_sim(Object *ob) +{ + ModifierData *md = modifiers_findByType(ob, eModifierType_Smoke); + + if (md) { + SmokeModifierData *smd = reinterpret_cast<SmokeModifierData *>(md); + return (smd->type == MOD_SMOKE_TYPE_DOMAIN); + } + + return false; +} + +static bool object_is_shape(Object *ob) +{ + switch (ob->type) { + case OB_MESH: + if (object_is_smoke_sim(ob)) { + return false; + } + + return true; + case OB_CURVE: + case OB_SURF: + case OB_CAMERA: + return true; + default: + return false; + } +} + +static bool export_object(const ExportSettings * const settings, Object *ob) +{ + if (settings->selected_only && !parent_selected(ob)) { + return false; + } + + if (settings->visible_layers_only && !(settings->scene->lay & ob->lay)) { + return false; + } + + if (settings->renderable_only && (ob->restrictflag & OB_RESTRICT_RENDER)) { + return false; + } + + return true; +} + +/* ************************************************************************** */ + +AbcExporter::AbcExporter(Scene *scene, const char *filename, ExportSettings &settings) + : m_settings(settings) + , m_filename(filename) + , m_trans_sampling_index(0) + , m_shape_sampling_index(0) + , m_scene(scene) +{} + +AbcExporter::~AbcExporter() +{ + std::map<std::string, AbcTransformWriter*>::iterator it, e; + for (it = m_xforms.begin(), e = m_xforms.end(); it != e; ++it) { + delete it->second; + } + + for (int i = 0, e = m_shapes.size(); i != e; ++i) { + delete m_shapes[i]; + } +} + +void AbcExporter::getShutterSamples(double step, bool time_relative, + std::vector<double> &samples) +{ + samples.clear(); + + const double time_factor = time_relative ? m_scene->r.frs_sec : 1.0; + const double shutter_open = m_settings.shutter_open; + const double shutter_close = m_settings.shutter_close; + + /* sample all frame */ + if (shutter_open == 0.0 && shutter_close == 1.0) { + for (double t = 0; t < 1.0; t += step) { + samples.push_back((t + m_settings.frame_start) / time_factor); + } + } + else { + /* sample between shutter open & close */ + const int nsamples = std::max((1.0 / step) - 1.0, 1.0); + const double time_inc = (shutter_close - shutter_open) / nsamples; + + for (double t = shutter_open; t <= shutter_close; t += time_inc) { + samples.push_back((t + m_settings.frame_start) / time_factor); + } + } +} + +Alembic::Abc::TimeSamplingPtr AbcExporter::createTimeSampling(double step) +{ + TimeSamplingPtr time_sampling; + std::vector<double> samples; + + if (m_settings.frame_start == m_settings.frame_end) { + time_sampling.reset(new Alembic::Abc::TimeSampling()); + return time_sampling; + } + + getShutterSamples(step, true, samples); + + Alembic::Abc::TimeSamplingType ts(static_cast<uint32_t>(samples.size()), 1.0 / m_scene->r.frs_sec); + time_sampling.reset(new Alembic::Abc::TimeSampling(ts, samples)); + + return time_sampling; +} + +void AbcExporter::getFrameSet(double step, std::set<double> &frames) +{ + frames.clear(); + + std::vector<double> shutter_samples; + + getShutterSamples(step, false, shutter_samples); + + for (int frame = m_settings.frame_start; frame <= m_settings.frame_end; ++frame) { + for (int j = 0, e = shutter_samples.size(); j < e; ++j) { + frames.insert(frame + shutter_samples[j]); + } + } +} + +void AbcExporter::operator()(Main *bmain, float &progress, bool &was_canceled) +{ + std::string scene_name; + + if (bmain->name[0] != '\0') { + char scene_file_name[FILE_MAX]; + BLI_strncpy(scene_file_name, bmain->name, FILE_MAX); + scene_name = scene_file_name; + } + else { + scene_name = "untitled"; + } + + Scene *scene = m_scene; + const int fps = FPS; + char buf[16]; + snprintf(buf, 15, "%d", fps); + const std::string str_fps = buf; + + Alembic::AbcCoreAbstract::MetaData md; + md.set("FramesPerTimeUnit", str_fps); + + Alembic::Abc::Argument arg(md); + + /* Use stream to support unicode character paths on Windows. */ + if (m_settings.export_ogawa) { +#ifdef WIN32 + UTF16_ENCODE(m_filename); + std::wstring wstr(m_filename_16); + m_out_file.open(wstr.c_str(), std::ios::out | std::ios::binary); + UTF16_UN_ENCODE(m_filename); +#else + m_out_file.open(m_filename, std::ios::out | std::ios::binary); +#endif + } + + m_archive = create_archive(&m_out_file, + m_filename, + scene_name, + Alembic::Abc::ErrorHandler::kThrowPolicy, + arg, + m_settings.export_ogawa); + + /* Create time samplings for transforms and shapes. */ + + TimeSamplingPtr trans_time = createTimeSampling(m_settings.frame_step_xform); + + m_trans_sampling_index = m_archive.addTimeSampling(*trans_time); + + TimeSamplingPtr shape_time; + + if ((m_settings.frame_step_shape == m_settings.frame_step_xform) || + (m_settings.frame_start == m_settings.frame_end)) + { + shape_time = trans_time; + m_shape_sampling_index = m_trans_sampling_index; + } + else { + shape_time = createTimeSampling(m_settings.frame_step_shape); + m_shape_sampling_index = m_archive.addTimeSampling(*shape_time); + } + + OBox3dProperty archive_bounds_prop = Alembic::AbcGeom::CreateOArchiveBounds(m_archive, m_trans_sampling_index); + + if (m_settings.flatten_hierarchy) { + createTransformWritersFlat(); + } + else { + createTransformWritersHierarchy(bmain->eval_ctx); + } + + createShapeWriters(bmain->eval_ctx); + + /* Make a list of frames to export. */ + + std::set<double> xform_frames; + getFrameSet(m_settings.frame_step_xform, xform_frames); + + std::set<double> shape_frames; + getFrameSet(m_settings.frame_step_shape, shape_frames); + + /* Merge all frames needed. */ + + std::set<double> frames(xform_frames); + frames.insert(shape_frames.begin(), shape_frames.end()); + + /* Export all frames. */ + + std::set<double>::const_iterator begin = frames.begin(); + std::set<double>::const_iterator end = frames.end(); + + const float size = static_cast<float>(frames.size()); + size_t i = 0; + + for (; begin != end; ++begin) { + progress = (++i / size); + + if (G.is_break) { + was_canceled = true; + break; + } + + const double frame = *begin; + + /* 'frame' is offset by start frame, so need to cancel the offset. */ + setCurrentFrame(bmain, frame - m_settings.frame_start); + + if (shape_frames.count(frame) != 0) { + for (int i = 0, e = m_shapes.size(); i != e; ++i) { + m_shapes[i]->write(); + } + } + + if (xform_frames.count(frame) == 0) { + continue; + } + + std::map<std::string, AbcTransformWriter *>::iterator xit, xe; + for (xit = m_xforms.begin(), xe = m_xforms.end(); xit != xe; ++xit) { + xit->second->write(); + } + + /* Save the archive 's bounding box. */ + Imath::Box3d bounds; + + for (xit = m_xforms.begin(), xe = m_xforms.end(); xit != xe; ++xit) { + Imath::Box3d box = xit->second->bounds(); + bounds.extendBy(box); + } + + archive_bounds_prop.set(bounds); + } +} + +void AbcExporter::createTransformWritersHierarchy(EvaluationContext *eval_ctx) +{ + Base *base = static_cast<Base *>(m_scene->base.first); + + while (base) { + Object *ob = base->object; + + if (export_object(&m_settings, ob)) { + switch(ob->type) { + case OB_LAMP: + case OB_LATTICE: + case OB_MBALL: + case OB_SPEAKER: + /* We do not export transforms for objects of these classes. */ + break; + + default: + exploreTransform(eval_ctx, ob, ob->parent, NULL); + } + } + + base = base->next; + } +} + +void AbcExporter::createTransformWritersFlat() +{ + Base *base = static_cast<Base *>(m_scene->base.first); + + while (base) { + Object *ob = base->object; + + if (export_object(&m_settings, ob) && object_is_shape(ob)) { + std::string name = get_id_name(ob); + m_xforms[name] = new AbcTransformWriter(ob, m_archive.getTop(), 0, m_trans_sampling_index, m_settings); + } + + base = base->next; + } +} + +void AbcExporter::exploreTransform(EvaluationContext *eval_ctx, Object *ob, Object *parent, Object *dupliObParent) +{ + createTransformWriter(ob, parent, dupliObParent); + + ListBase *lb = object_duplilist(eval_ctx, m_scene, ob); + + if (lb) { + DupliObject *link = static_cast<DupliObject *>(lb->first); + Object *dupli_ob = NULL; + Object *dupli_parent = NULL; + + while (link) { + if (link->type == OB_DUPLIGROUP) { + dupli_ob = link->ob; + dupli_parent = (dupli_ob->parent) ? dupli_ob->parent : ob; + + exploreTransform(eval_ctx, dupli_ob, dupli_parent, ob); + } + + link = link->next; + } + } + + free_object_duplilist(lb); +} + +void AbcExporter::createTransformWriter(Object *ob, Object *parent, Object *dupliObParent) +{ + const std::string name = get_object_dag_path_name(ob, dupliObParent); + + /* check if we have already created a transform writer for this object */ + if (m_xforms.find(name) != m_xforms.end()){ + std::cerr << "xform " << name << " already exists\n"; + return; + } + + AbcTransformWriter *parent_xform = NULL; + + if (parent) { + const std::string parentname = get_object_dag_path_name(parent, dupliObParent); + parent_xform = getXForm(parentname); + + if (!parent_xform) { + if (parent->parent) { + createTransformWriter(parent, parent->parent, dupliObParent); + } + else { + createTransformWriter(parent, dupliObParent, dupliObParent); + } + + parent_xform = getXForm(parentname); + } + } + + if (parent_xform) { + m_xforms[name] = new AbcTransformWriter(ob, parent_xform->alembicXform(), parent_xform, m_trans_sampling_index, m_settings); + m_xforms[name]->setParent(parent); + } + else { + m_xforms[name] = new AbcTransformWriter(ob, m_archive.getTop(), NULL, m_trans_sampling_index, m_settings); + } +} + +void AbcExporter::createShapeWriters(EvaluationContext *eval_ctx) +{ + Base *base = static_cast<Base *>(m_scene->base.first); + + while (base) { + Object *ob = base->object; + exploreObject(eval_ctx, ob, NULL); + + base = base->next; + } +} + +void AbcExporter::exploreObject(EvaluationContext *eval_ctx, Object *ob, Object *dupliObParent) +{ + ListBase *lb = object_duplilist(eval_ctx, m_scene, ob); + + createShapeWriter(ob, dupliObParent); + + if (lb) { + DupliObject *dupliob = static_cast<DupliObject *>(lb->first); + + while (dupliob) { + if (dupliob->type == OB_DUPLIGROUP) { + exploreObject(eval_ctx, dupliob->ob, ob); + } + + dupliob = dupliob->next; + } + } + + free_object_duplilist(lb); +} + +void AbcExporter::createShapeWriter(Object *ob, Object *dupliObParent) +{ + if (!object_is_shape(ob)) { + return; + } + + if (!export_object(&m_settings, ob)) { + return; + } + + std::string name; + + if (m_settings.flatten_hierarchy) { + name = get_id_name(ob); + } + else { + name = get_object_dag_path_name(ob, dupliObParent); + } + + AbcTransformWriter *xform = getXForm(name); + + if (!xform) { + std::cerr << __func__ << ": xform " << name << " is NULL\n"; + return; + } + + ParticleSystem *psys = static_cast<ParticleSystem *>(ob->particlesystem.first); + + for (; psys; psys = psys->next) { + if (!psys_check_enabled(ob, psys, G.is_rendering) || !psys->part) { + continue; + } + + if (psys->part->type == PART_HAIR) { + m_settings.export_child_hairs = true; + m_shapes.push_back(new AbcHairWriter(m_scene, ob, xform, m_shape_sampling_index, m_settings, psys)); + } + else if (psys->part->type == PART_EMITTER) { + m_shapes.push_back(new AbcPointsWriter(m_scene, ob, xform, m_shape_sampling_index, m_settings, psys)); + } + } + + switch(ob->type) { + case OB_MESH: + { + Mesh *me = static_cast<Mesh *>(ob->data); + + if (!me || me->totvert == 0) { + return; + } + + m_shapes.push_back(new AbcMeshWriter(m_scene, ob, xform, m_shape_sampling_index, m_settings)); + break; + } + case OB_SURF: + { + Curve *cu = static_cast<Curve *>(ob->data); + + if (!cu) { + return; + } + + m_shapes.push_back(new AbcNurbsWriter(m_scene, ob, xform, m_shape_sampling_index, m_settings)); + break; + } + case OB_CURVE: + { + Curve *cu = static_cast<Curve *>(ob->data); + + if (!cu) { + return; + } + + m_shapes.push_back(new AbcCurveWriter(m_scene, ob, xform, m_shape_sampling_index, m_settings)); + break; + } + case OB_CAMERA: + { + Camera *cam = static_cast<Camera *>(ob->data); + + if (cam->type == CAM_PERSP) { + m_shapes.push_back(new AbcCameraWriter(m_scene, ob, xform, m_shape_sampling_index, m_settings)); + } + + break; + } + } +} + +AbcTransformWriter *AbcExporter::getXForm(const std::string &name) +{ + std::map<std::string, AbcTransformWriter *>::iterator it = m_xforms.find(name); + + if (it == m_xforms.end()) { + return NULL; + } + + return it->second; +} + +void AbcExporter::setCurrentFrame(Main *bmain, double t) +{ + m_scene->r.cfra = std::floor(t); + m_scene->r.subframe = t - m_scene->r.cfra; + BKE_scene_update_for_newframe(bmain->eval_ctx, bmain, m_scene, m_scene->lay); +} diff --git a/source/blender/alembic/intern/abc_exporter.h b/source/blender/alembic/intern/abc_exporter.h new file mode 100644 index 00000000000..6c242f973c4 --- /dev/null +++ b/source/blender/alembic/intern/abc_exporter.h @@ -0,0 +1,114 @@ +/* + * ***** 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. + * + * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __ABC_EXPORTER_H__ +#define __ABC_EXPORTER_H__ + +#include <Alembic/Abc/All.h> +#include <fstream> +#include <map> +#include <set> +#include <vector> + +class AbcObjectWriter; +class AbcTransformWriter; + +struct EvaluationContext; +struct Main; +struct Object; +struct Scene; + +struct ExportSettings { + ExportSettings(); + + Scene *scene; + + bool selected_only; + bool visible_layers_only; + bool renderable_only; + + double frame_start, frame_end; + double frame_step_xform; + double frame_step_shape; + double shutter_open; + double shutter_close; + float global_scale; + + bool flatten_hierarchy; + + bool export_normals; + bool export_uvs; + bool export_vcols; + bool export_face_sets; + bool export_vweigths; + + bool apply_subdiv; + bool use_subdiv_schema; + bool export_child_hairs; + bool export_ogawa; + bool pack_uv; + + bool do_convert_axis; + float convert_matrix[3][3]; +}; + +class AbcExporter { + ExportSettings &m_settings; + + const char *m_filename; + + std::ofstream m_out_file; + Alembic::Abc::OArchive m_archive; + unsigned int m_trans_sampling_index, m_shape_sampling_index; + + Scene *m_scene; + + std::map<std::string, AbcTransformWriter *> m_xforms; + std::vector<AbcObjectWriter *> m_shapes; + +public: + AbcExporter(Scene *scene, const char *filename, ExportSettings &settings); + ~AbcExporter(); + + void operator()(Main *bmain, float &progress, bool &was_canceled); + +private: + void getShutterSamples(double step, bool time_relative, std::vector<double> &samples); + + Alembic::Abc::TimeSamplingPtr createTimeSampling(double step); + + void getFrameSet(double step, std::set<double> &frames); + + void createTransformWritersHierarchy(EvaluationContext *eval_ctx); + void createTransformWritersFlat(); + void createTransformWriter(Object *ob, Object *parent, Object *dupliObParent); + void exploreTransform(EvaluationContext *eval_ctx, Object *ob, Object *parent, Object *dupliObParent = NULL); + void exploreObject(EvaluationContext *eval_ctx, Object *ob, Object *dupliObParent); + void createShapeWriters(EvaluationContext *eval_ctx); + void createShapeWriter(Object *ob, Object *dupliObParent); + + AbcTransformWriter *getXForm(const std::string &name); + + void setCurrentFrame(Main *bmain, double t); +}; + +#endif /* __ABC_EXPORTER_H__ */ diff --git a/source/blender/alembic/intern/abc_hair.cc b/source/blender/alembic/intern/abc_hair.cc new file mode 100644 index 00000000000..45bf9b7ab8a --- /dev/null +++ b/source/blender/alembic/intern/abc_hair.cc @@ -0,0 +1,290 @@ +/* + * ***** 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. + * + * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "abc_hair.h" + +#include <cstdio> + +#include "abc_transform.h" +#include "abc_util.h" + +extern "C" { +#include "MEM_guardedalloc.h" + +#include "DNA_modifier_types.h" + +#include "BLI_listbase.h" +#include "BLI_math_geom.h" + +#include "BKE_DerivedMesh.h" +#include "BKE_object.h" +#include "BKE_particle.h" +} + +using Alembic::Abc::P3fArraySamplePtr; + +using Alembic::AbcGeom::OCurves; +using Alembic::AbcGeom::OCurvesSchema; +using Alembic::AbcGeom::ON3fGeomParam; +using Alembic::AbcGeom::OV2fGeomParam; + +/* ************************************************************************** */ + +AbcHairWriter::AbcHairWriter(Scene *scene, + Object *ob, + AbcTransformWriter *parent, + uint32_t time_sampling, + ExportSettings &settings, + ParticleSystem *psys) + : AbcObjectWriter(scene, ob, time_sampling, settings, parent) +{ + m_psys = psys; + + OCurves curves(parent->alembicXform(), m_name, m_time_sampling); + m_schema = curves.getSchema(); +} + +void AbcHairWriter::do_write() +{ + if (!m_psys) { + return; + } + + ParticleSystemModifierData *psmd = psys_get_modifier(m_object, m_psys); + + if (!psmd->dm_final) { + return; + } + + DerivedMesh *dm = mesh_create_derived_view(m_scene, m_object, CD_MASK_MESH); + DM_ensure_tessface(dm); + DM_update_tessface_data(dm); + + std::vector<Imath::V3f> verts; + std::vector<int32_t> hvertices; + std::vector<Imath::V2f> uv_values; + std::vector<Imath::V3f> norm_values; + + if (m_psys->pathcache) { + ParticleSettings *part = m_psys->part; + + write_hair_sample(dm, part, verts, norm_values, uv_values, hvertices); + + if (m_settings.export_child_hairs && m_psys->childcache) { + write_hair_child_sample(dm, part, verts, norm_values, uv_values, hvertices); + } + } + + dm->release(dm); + + Alembic::Abc::P3fArraySample iPos(verts); + m_sample = OCurvesSchema::Sample(iPos, hvertices); + m_sample.setBasis(Alembic::AbcGeom::kNoBasis); + m_sample.setType(Alembic::AbcGeom::kLinear); + m_sample.setWrap(Alembic::AbcGeom::kNonPeriodic); + + if (!uv_values.empty()) { + OV2fGeomParam::Sample uv_smp; + uv_smp.setVals(uv_values); + m_sample.setUVs(uv_smp); + } + + if (!norm_values.empty()) { + ON3fGeomParam::Sample norm_smp; + norm_smp.setVals(norm_values); + m_sample.setNormals(norm_smp); + } + + m_sample.setSelfBounds(bounds()); + m_schema.set(m_sample); +} + +void AbcHairWriter::write_hair_sample(DerivedMesh *dm, + ParticleSettings *part, + std::vector<Imath::V3f> &verts, + std::vector<Imath::V3f> &norm_values, + std::vector<Imath::V2f> &uv_values, + std::vector<int32_t> &hvertices) +{ + /* Get untransformed vertices, there's a xform under the hair. */ + float inv_mat[4][4]; + invert_m4_m4_safe(inv_mat, m_object->obmat); + + MTFace *mtface = static_cast<MTFace *>(CustomData_get_layer(&dm->faceData, CD_MTFACE)); + MFace *mface = dm->getTessFaceArray(dm); + MVert *mverts = dm->getVertArray(dm); + + if (!mtface || !mface) { + std::fprintf(stderr, "Warning, no UV set found for underlying geometry.\n"); + } + + ParticleData * pa = m_psys->particles; + int k; + + ParticleCacheKey **cache = m_psys->pathcache; + ParticleCacheKey *path; + float normal[3]; + Imath::V3f tmp_nor; + + for (int p = 0; p < m_psys->totpart; ++p, ++pa) { + /* underlying info for faces-only emission */ + path = cache[p]; + + if (part->from == PART_FROM_FACE && mtface) { + const int num = pa->num_dmcache >= 0 ? pa->num_dmcache : pa->num; + + if (num < dm->getNumTessFaces(dm)) { + MFace *face = static_cast<MFace *>(dm->getTessFaceData(dm, num, CD_MFACE)); + MTFace *tface = mtface + num; + + if (mface) { + float r_uv[2], mapfw[4], vec[3]; + + psys_interpolate_uvs(tface, face->v4, pa->fuv, r_uv); + uv_values.push_back(Imath::V2f(r_uv[0], r_uv[1])); + + psys_interpolate_face(mverts, face, tface, NULL, mapfw, vec, normal, NULL, NULL, NULL, NULL); + + copy_zup_yup(tmp_nor.getValue(), normal); + norm_values.push_back(tmp_nor); + } + } + else { + std::fprintf(stderr, "Particle to faces overflow (%d/%d)\n", num, dm->getNumTessFaces(dm)); + } + } + else if (part->from == PART_FROM_VERT && mtface) { + /* vertex id */ + const int num = (pa->num_dmcache >= 0) ? pa->num_dmcache : pa->num; + + /* iterate over all faces to find a corresponding underlying UV */ + for (int n = 0; n < dm->getNumTessFaces(dm); ++n) { + MFace *face = static_cast<MFace *>(dm->getTessFaceData(dm, n, CD_MFACE)); + MTFace *tface = mtface + n; + unsigned int vtx[4]; + vtx[0] = face->v1; + vtx[1] = face->v2; + vtx[2] = face->v3; + vtx[3] = face->v4; + bool found = false; + + for (int o = 0; o < 4; ++o) { + if (o > 2 && vtx[o] == 0) { + break; + } + + if (vtx[o] == num) { + uv_values.push_back(Imath::V2f(tface->uv[o][0], tface->uv[o][1])); + + MVert *mv = mverts + vtx[o]; + + normal_short_to_float_v3(normal, mv->no); + copy_zup_yup(tmp_nor.getValue(), normal); + norm_values.push_back(tmp_nor); + found = true; + break; + } + } + + if (found) { + break; + } + } + } + + int steps = path->segments + 1; + hvertices.push_back(steps); + + for (k = 0; k < steps; ++k) { + float vert[3]; + copy_v3_v3(vert, path->co); + mul_m4_v3(inv_mat, vert); + + /* Convert Z-up to Y-up. */ + verts.push_back(Imath::V3f(vert[0], vert[2], -vert[1])); + + ++path; + } + } +} + +void AbcHairWriter::write_hair_child_sample(DerivedMesh *dm, + ParticleSettings *part, + std::vector<Imath::V3f> &verts, + std::vector<Imath::V3f> &norm_values, + std::vector<Imath::V2f> &uv_values, + std::vector<int32_t> &hvertices) +{ + /* Get untransformed vertices, there's a xform under the hair. */ + float inv_mat[4][4]; + invert_m4_m4_safe(inv_mat, m_object->obmat); + + MTFace *mtface = static_cast<MTFace *>(CustomData_get_layer(&dm->faceData, CD_MTFACE)); + MFace *mface = dm->getTessFaceArray(dm); + MVert *mverts = dm->getVertArray(dm); + + if (!mtface || !mface) { + std::fprintf(stderr, "Warning, no UV set found for underlying geometry.\n"); + } + + ParticleCacheKey **cache = m_psys->childcache; + ParticleCacheKey *path; + + ChildParticle *pc = m_psys->child; + + for (int p = 0; p < m_psys->totchild; ++p, ++pc) { + path = cache[p]; + + if (part->from == PART_FROM_FACE) { + const int num = pc->num; + + MFace *face = static_cast<MFace *>(dm->getTessFaceData(dm, num, CD_MFACE)); + MTFace *tface = mtface + num; + + if (mface && mtface) { + float r_uv[2], tmpnor[3], mapfw[4], vec[3]; + + psys_interpolate_uvs(tface, face->v4, pc->fuv, r_uv); + uv_values.push_back(Imath::V2f(r_uv[0], r_uv[1])); + + psys_interpolate_face(mverts, face, tface, NULL, mapfw, vec, tmpnor, NULL, NULL, NULL, NULL); + + /* Convert Z-up to Y-up. */ + norm_values.push_back(Imath::V3f(tmpnor[0], tmpnor[2], -tmpnor[1])); + } + } + + int steps = path->segments + 1; + hvertices.push_back(steps); + + for (int k = 0; k < steps; ++k) { + float vert[3]; + copy_v3_v3(vert, path->co); + mul_m4_v3(inv_mat, vert); + + /* Convert Z-up to Y-up. */ + verts.push_back(Imath::V3f(vert[0], vert[2], -vert[1])); + + ++path; + } + } +} diff --git a/source/blender/alembic/intern/abc_hair.h b/source/blender/alembic/intern/abc_hair.h new file mode 100644 index 00000000000..d132b60be12 --- /dev/null +++ b/source/blender/alembic/intern/abc_hair.h @@ -0,0 +1,66 @@ +/* + * ***** 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. + * + * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __ABC_HAIR_H__ +#define __ABC_HAIR_H__ + +#include "abc_object.h" + +struct DerivedMesh; +struct ParticleSettings; +struct ParticleSystem; + +/* ************************************************************************** */ + +class AbcHairWriter : public AbcObjectWriter { + ParticleSystem *m_psys; + + Alembic::AbcGeom::OCurvesSchema m_schema; + Alembic::AbcGeom::OCurvesSchema::Sample m_sample; + +public: + AbcHairWriter(Scene *scene, + Object *ob, + AbcTransformWriter *parent, + uint32_t time_sampling, + ExportSettings &settings, + ParticleSystem *psys); + +private: + virtual void do_write(); + + void write_hair_sample(DerivedMesh *dm, + ParticleSettings *part, + std::vector<Imath::V3f> &verts, + std::vector<Imath::V3f> &norm_values, + std::vector<Imath::V2f> &uv_values, + std::vector<int32_t> &hvertices); + + void write_hair_child_sample(DerivedMesh *dm, + ParticleSettings *part, + std::vector<Imath::V3f> &verts, + std::vector<Imath::V3f> &norm_values, + std::vector<Imath::V2f> &uv_values, + std::vector<int32_t> &hvertices); +}; + +#endif /* __ABC_HAIR_H__ */ diff --git a/source/blender/alembic/intern/abc_mesh.cc b/source/blender/alembic/intern/abc_mesh.cc new file mode 100644 index 00000000000..b80b7c0c4c8 --- /dev/null +++ b/source/blender/alembic/intern/abc_mesh.cc @@ -0,0 +1,1215 @@ +/* + * ***** 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. + * + * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "abc_mesh.h" + +#include <algorithm> + +#include "abc_transform.h" +#include "abc_util.h" + +extern "C" { +#include "DNA_material_types.h" +#include "DNA_mesh_types.h" +#include "DNA_modifier_types.h" +#include "DNA_object_fluidsim.h" +#include "DNA_object_types.h" + +#include "BLI_math_geom.h" +#include "BLI_string.h" + +#include "BKE_depsgraph.h" +#include "BKE_DerivedMesh.h" +#include "BKE_main.h" +#include "BKE_material.h" +#include "BKE_mesh.h" +#include "BKE_modifier.h" +#include "BKE_object.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "ED_mesh.h" +} + +using Alembic::Abc::FloatArraySample; +using Alembic::Abc::ICompoundProperty; +using Alembic::Abc::Int32ArraySample; +using Alembic::Abc::Int32ArraySamplePtr; +using Alembic::Abc::P3fArraySamplePtr; +using Alembic::Abc::V2fArraySample; +using Alembic::Abc::V3fArraySample; +using Alembic::Abc::C4fArraySample; + +using Alembic::AbcGeom::IFaceSet; +using Alembic::AbcGeom::IFaceSetSchema; +using Alembic::AbcGeom::IObject; +using Alembic::AbcGeom::IPolyMesh; +using Alembic::AbcGeom::IPolyMeshSchema; +using Alembic::AbcGeom::ISampleSelector; +using Alembic::AbcGeom::ISubD; +using Alembic::AbcGeom::ISubDSchema; +using Alembic::AbcGeom::IV2fGeomParam; + +using Alembic::AbcGeom::OArrayProperty; +using Alembic::AbcGeom::OBoolProperty; +using Alembic::AbcGeom::OC3fArrayProperty; +using Alembic::AbcGeom::OC3fGeomParam; +using Alembic::AbcGeom::OC4fGeomParam; +using Alembic::AbcGeom::OCompoundProperty; +using Alembic::AbcGeom::OFaceSet; +using Alembic::AbcGeom::OFaceSetSchema; +using Alembic::AbcGeom::OFloatGeomParam; +using Alembic::AbcGeom::OInt32GeomParam; +using Alembic::AbcGeom::ON3fArrayProperty; +using Alembic::AbcGeom::ON3fGeomParam; +using Alembic::AbcGeom::OPolyMesh; +using Alembic::AbcGeom::OPolyMeshSchema; +using Alembic::AbcGeom::OSubD; +using Alembic::AbcGeom::OSubDSchema; +using Alembic::AbcGeom::OV2fGeomParam; +using Alembic::AbcGeom::OV3fGeomParam; + +using Alembic::AbcGeom::kFacevaryingScope; +using Alembic::AbcGeom::kVaryingScope; +using Alembic::AbcGeom::kVertexScope; +using Alembic::AbcGeom::kWrapExisting; +using Alembic::AbcGeom::UInt32ArraySample; +using Alembic::AbcGeom::N3fArraySamplePtr; +using Alembic::AbcGeom::IN3fGeomParam; + +/* ************************************************************************** */ + +/* NOTE: Alembic's polygon winding order is clockwise, to match with Renderman. */ + +static void get_vertices(DerivedMesh *dm, std::vector<Imath::V3f> &points) +{ + points.clear(); + points.resize(dm->getNumVerts(dm)); + + MVert *verts = dm->getVertArray(dm); + + for (int i = 0, e = dm->getNumVerts(dm); i < e; ++i) { + copy_zup_yup(points[i].getValue(), verts[i].co); + } +} + +static void get_topology(DerivedMesh *dm, + std::vector<int32_t> &poly_verts, + std::vector<int32_t> &loop_counts, + bool &smooth_normal) +{ + const int num_poly = dm->getNumPolys(dm); + const int num_loops = dm->getNumLoops(dm); + MLoop *mloop = dm->getLoopArray(dm); + MPoly *mpoly = dm->getPolyArray(dm); + + poly_verts.clear(); + loop_counts.clear(); + poly_verts.reserve(num_loops); + loop_counts.reserve(num_poly); + + /* NOTE: data needs to be written in the reverse order. */ + for (int i = 0; i < num_poly; ++i) { + MPoly &poly = mpoly[i]; + loop_counts.push_back(poly.totloop); + + smooth_normal |= ((poly.flag & ME_SMOOTH) != 0); + + MLoop *loop = mloop + poly.loopstart + (poly.totloop - 1); + + for (int j = 0; j < poly.totloop; ++j, --loop) { + poly_verts.push_back(loop->v); + } + } +} + +static void get_material_indices(DerivedMesh *dm, std::vector<int32_t> &indices) +{ + indices.clear(); + indices.reserve(dm->getNumTessFaces(dm)); + + MPoly *mpolys = dm->getPolyArray(dm); + + for (int i = 1, e = dm->getNumPolys(dm); i < e; ++i) { + MPoly *mpoly = &mpolys[i]; + indices.push_back(mpoly->mat_nr); + } +} + +static void get_creases(DerivedMesh *dm, + std::vector<int32_t> &indices, + std::vector<int32_t> &lengths, + std::vector<float> &sharpnesses) +{ + const float factor = 1.0f / 255.0f; + + indices.clear(); + lengths.clear(); + sharpnesses.clear(); + + MEdge *edge = dm->getEdgeArray(dm); + + for (int i = 0, e = dm->getNumEdges(dm); i < e; ++i) { + const float sharpness = static_cast<float>(edge[i].crease) * factor; + + if (sharpness != 0.0f) { + indices.push_back(edge[i].v1); + indices.push_back(edge[i].v2); + sharpnesses.push_back(sharpness); + } + } + + lengths.resize(sharpnesses.size(), 2); +} + +static void get_vertex_normals(DerivedMesh *dm, std::vector<Imath::V3f> &normals) +{ + normals.clear(); + normals.resize(dm->getNumVerts(dm)); + + MVert *verts = dm->getVertArray(dm); + float no[3]; + + for (int i = 0, e = dm->getNumVerts(dm); i < e; ++i) { + normal_short_to_float_v3(no, verts[i].no); + copy_zup_yup(normals[i].getValue(), no); + } +} + +static void get_loop_normals(DerivedMesh *dm, std::vector<Imath::V3f> &normals) +{ + MPoly *mpoly = dm->getPolyArray(dm); + MPoly *mp = mpoly; + + MLoop *mloop = dm->getLoopArray(dm); + MLoop *ml = mloop; + + MVert *verts = dm->getVertArray(dm); + + const float (*lnors)[3] = static_cast<float(*)[3]>(dm->getLoopDataArray(dm, CD_NORMAL)); + + normals.clear(); + normals.resize(dm->getNumLoops(dm)); + + unsigned loop_index = 0; + + /* NOTE: data needs to be written in the reverse order. */ + + if (lnors) { + for (int i = 0, e = dm->getNumPolys(dm); i < e; ++i, ++mp) { + ml = mloop + mp->loopstart + (mp->totloop - 1); + + for (int j = 0; j < mp->totloop; --ml, ++j, ++loop_index) { + const int index = ml->v; + copy_zup_yup(normals[loop_index].getValue(), lnors[index]); + } + } + } + else { + float no[3]; + + for (int i = 0, e = dm->getNumPolys(dm); i < e; ++i, ++mp) { + ml = mloop + mp->loopstart + (mp->totloop - 1); + + /* Flat shaded, use common normal for all verts. */ + if ((mp->flag & ME_SMOOTH) == 0) { + BKE_mesh_calc_poly_normal(mp, ml - (mp->totloop - 1), verts, no); + + for (int j = 0; j < mp->totloop; --ml, ++j, ++loop_index) { + copy_zup_yup(normals[loop_index].getValue(), no); + } + } + else { + /* Smooth shaded, use individual vert normals. */ + for (int j = 0; j < mp->totloop; --ml, ++j, ++loop_index) { + normal_short_to_float_v3(no, verts[ml->v].no); + copy_zup_yup(normals[loop_index].getValue(), no); + } + } + } + } +} + +/* *************** Modifiers *************** */ + +/* check if the mesh is a subsurf, ignoring disabled modifiers and + * displace if it's after subsurf. */ +static ModifierData *get_subsurf_modifier(Scene *scene, Object *ob) +{ + ModifierData *md = static_cast<ModifierData *>(ob->modifiers.last); + + for (; md; md = md->prev) { + if (!modifier_isEnabled(scene, md, eModifierMode_Render)) { + continue; + } + + if (md->type == eModifierType_Subsurf) { + SubsurfModifierData *smd = reinterpret_cast<SubsurfModifierData*>(md); + + if (smd->subdivType == ME_CC_SUBSURF) { + return md; + } + } + + /* mesh is not a subsurf. break */ + if ((md->type != eModifierType_Displace) && (md->type != eModifierType_ParticleSystem)) { + return NULL; + } + } + + return NULL; +} + +static ModifierData *get_liquid_sim_modifier(Scene *scene, Object *ob) +{ + ModifierData *md = modifiers_findByType(ob, eModifierType_Fluidsim); + + if (md && (modifier_isEnabled(scene, md, eModifierMode_Render))) { + FluidsimModifierData *fsmd = reinterpret_cast<FluidsimModifierData *>(md); + + if (fsmd->fss && fsmd->fss->type == OB_FLUIDSIM_DOMAIN) { + return md; + } + } + + return NULL; +} + +/* ************************************************************************** */ + +AbcMeshWriter::AbcMeshWriter(Scene *scene, + Object *ob, + AbcTransformWriter *parent, + uint32_t time_sampling, + ExportSettings &settings) + : AbcObjectWriter(scene, ob, time_sampling, settings, parent) +{ + m_is_animated = isAnimated(); + m_subsurf_mod = NULL; + m_has_per_face_materials = false; + m_is_subd = false; + + /* If the object is static, use the default static time sampling. */ + if (!m_is_animated) { + time_sampling = 0; + } + + if (!m_settings.apply_subdiv) { + m_subsurf_mod = get_subsurf_modifier(m_scene, m_object); + m_is_subd = (m_subsurf_mod != NULL); + } + + m_is_liquid = (get_liquid_sim_modifier(m_scene, m_object) != NULL); + + while (parent->alembicXform().getChildHeader(m_name)) { + m_name.append("_"); + } + + if (m_settings.use_subdiv_schema && m_is_subd) { + OSubD subd(parent->alembicXform(), m_name, m_time_sampling); + m_subdiv_schema = subd.getSchema(); + } + else { + OPolyMesh mesh(parent->alembicXform(), m_name, m_time_sampling); + m_mesh_schema = mesh.getSchema(); + + OCompoundProperty typeContainer = m_mesh_schema.getUserProperties(); + OBoolProperty type(typeContainer, "meshtype"); + type.set(m_is_subd); + } +} + +AbcMeshWriter::~AbcMeshWriter() +{ + if (m_subsurf_mod) { + m_subsurf_mod->mode &= ~eModifierMode_DisableTemporary; + } +} + +bool AbcMeshWriter::isAnimated() const +{ + /* Check if object has shape keys. */ + Mesh *me = static_cast<Mesh *>(m_object->data); + + if (me->key) { + return true; + } + + /* Test modifiers. */ + ModifierData *md = static_cast<ModifierData *>(m_object->modifiers.first); + + while (md) { + if (md->type != eModifierType_Subsurf) { + return true; + } + + md = md->next; + } + + return false; +} + +void AbcMeshWriter::do_write() +{ + /* We have already stored a sample for this object. */ + if (!m_first_frame && !m_is_animated) + return; + + DerivedMesh *dm = getFinalMesh(); + + try { + if (m_settings.use_subdiv_schema && m_subdiv_schema.valid()) { + writeSubD(dm); + } + else { + writeMesh(dm); + } + + freeMesh(dm); + } + catch (...) { + freeMesh(dm); + throw; + } +} + +void AbcMeshWriter::writeMesh(DerivedMesh *dm) +{ + std::vector<Imath::V3f> points, normals; + std::vector<int32_t> poly_verts, loop_counts; + + bool smooth_normal = false; + + get_vertices(dm, points); + get_topology(dm, poly_verts, loop_counts, smooth_normal); + + if (m_first_frame) { + writeCommonData(dm, m_mesh_schema); + } + + m_mesh_sample = OPolyMeshSchema::Sample(V3fArraySample(points), + Int32ArraySample(poly_verts), + Int32ArraySample(loop_counts)); + + UVSample sample; + if (m_settings.export_uvs) { + const char *name = get_uv_sample(sample, m_custom_data_config, &dm->loopData); + + if (!sample.indices.empty() && !sample.uvs.empty()) { + OV2fGeomParam::Sample uv_sample; + uv_sample.setVals(V2fArraySample(sample.uvs)); + uv_sample.setIndices(UInt32ArraySample(sample.indices)); + uv_sample.setScope(kFacevaryingScope); + + m_mesh_schema.setUVSourceName(name); + m_mesh_sample.setUVs(uv_sample); + } + + write_custom_data(m_mesh_schema.getArbGeomParams(), m_custom_data_config, &dm->loopData, CD_MLOOPUV); + } + + if (m_settings.export_normals) { + if (smooth_normal) { + get_loop_normals(dm, normals); + } + else { + get_vertex_normals(dm, normals); + } + + ON3fGeomParam::Sample normals_sample; + if (!normals.empty()) { + normals_sample.setScope((smooth_normal) ? kFacevaryingScope : kVertexScope); + normals_sample.setVals(V3fArraySample(normals)); + } + + m_mesh_sample.setNormals(normals_sample); + } + + if (m_is_liquid) { + std::vector<Imath::V3f> velocities; + getVelocities(dm, velocities); + + m_mesh_sample.setVelocities(V3fArraySample(velocities)); + } + + m_mesh_sample.setSelfBounds(bounds()); + + m_mesh_schema.set(m_mesh_sample); + + writeArbGeoParams(dm); +} + +void AbcMeshWriter::writeSubD(DerivedMesh *dm) +{ + std::vector<float> crease_sharpness; + std::vector<Imath::V3f> points; + std::vector<int32_t> poly_verts, loop_counts; + std::vector<int32_t> crease_indices, crease_lengths; + + bool smooth_normal = false; + + get_vertices(dm, points); + get_topology(dm, poly_verts, loop_counts, smooth_normal); + get_creases(dm, crease_indices, crease_lengths, crease_sharpness); + + if (m_first_frame) { + /* create materials' face_sets */ + writeCommonData(dm, m_subdiv_schema); + } + + m_subdiv_sample = OSubDSchema::Sample(V3fArraySample(points), + Int32ArraySample(poly_verts), + Int32ArraySample(loop_counts)); + + UVSample sample; + if (m_settings.export_uvs) { + const char *name = get_uv_sample(sample, m_custom_data_config, &dm->loopData); + + if (!sample.indices.empty() && !sample.uvs.empty()) { + OV2fGeomParam::Sample uv_sample; + uv_sample.setVals(V2fArraySample(sample.uvs)); + uv_sample.setIndices(UInt32ArraySample(sample.indices)); + uv_sample.setScope(kFacevaryingScope); + + m_subdiv_schema.setUVSourceName(name); + m_subdiv_sample.setUVs(uv_sample); + } + + write_custom_data(m_subdiv_schema.getArbGeomParams(), m_custom_data_config, &dm->loopData, CD_MLOOPUV); + } + + if (!crease_indices.empty()) { + m_subdiv_sample.setCreaseIndices(Int32ArraySample(crease_indices)); + m_subdiv_sample.setCreaseLengths(Int32ArraySample(crease_lengths)); + m_subdiv_sample.setCreaseSharpnesses(FloatArraySample(crease_sharpness)); + } + + m_subdiv_sample.setSelfBounds(bounds()); + m_subdiv_schema.set(m_subdiv_sample); + + writeArbGeoParams(dm); +} + +template <typename Schema> +void AbcMeshWriter::writeCommonData(DerivedMesh *dm, Schema &schema) +{ + std::map< std::string, std::vector<int32_t> > geo_groups; + getGeoGroups(dm, geo_groups); + + std::map< std::string, std::vector<int32_t> >::iterator it; + for (it = geo_groups.begin(); it != geo_groups.end(); ++it) { + OFaceSet face_set = schema.createFaceSet(it->first); + OFaceSetSchema::Sample samp; + samp.setFaces(Int32ArraySample(it->second)); + face_set.getSchema().set(samp); + } +} + +DerivedMesh *AbcMeshWriter::getFinalMesh() +{ + /* We don't want subdivided mesh data */ + if (m_subsurf_mod) { + m_subsurf_mod->mode |= eModifierMode_DisableTemporary; + } + + DerivedMesh *dm = mesh_create_derived_render(m_scene, m_object, CD_MASK_MESH); + + if (m_subsurf_mod) { + m_subsurf_mod->mode &= ~eModifierMode_DisableTemporary; + } + + m_custom_data_config.pack_uvs = m_settings.pack_uv; + m_custom_data_config.mpoly = dm->getPolyArray(dm); + m_custom_data_config.mloop = dm->getLoopArray(dm); + m_custom_data_config.totpoly = dm->getNumPolys(dm); + m_custom_data_config.totloop = dm->getNumLoops(dm); + m_custom_data_config.totvert = dm->getNumVerts(dm); + + return dm; +} + +void AbcMeshWriter::freeMesh(DerivedMesh *dm) +{ + dm->release(dm); +} + +void AbcMeshWriter::writeArbGeoParams(DerivedMesh *dm) +{ + if (m_is_liquid) { + /* We don't need anything more for liquid meshes. */ + return; + } + + if (m_settings.export_vcols) { + if (m_subdiv_schema.valid()) { + write_custom_data(m_subdiv_schema.getArbGeomParams(), m_custom_data_config, &dm->loopData, CD_MLOOPCOL); + } + else { + write_custom_data(m_mesh_schema.getArbGeomParams(), m_custom_data_config, &dm->loopData, CD_MLOOPCOL); + } + } + + if (m_first_frame && m_has_per_face_materials) { + std::vector<int32_t> material_indices; + + if (m_settings.export_face_sets) { + get_material_indices(dm, material_indices); + + OFaceSetSchema::Sample samp; + samp.setFaces(Int32ArraySample(material_indices)); + m_face_set.getSchema().set(samp); + } + } +} + +void AbcMeshWriter::getVelocities(DerivedMesh *dm, std::vector<Imath::V3f> &vels) +{ + const int totverts = dm->getNumVerts(dm); + + vels.clear(); + vels.resize(totverts); + + ModifierData *md = get_liquid_sim_modifier(m_scene, m_object); + FluidsimModifierData *fmd = reinterpret_cast<FluidsimModifierData *>(md); + FluidsimSettings *fss = fmd->fss; + + if (fss->meshVelocities) { + float *mesh_vels = reinterpret_cast<float *>(fss->meshVelocities); + + for (int i = 0; i < totverts; ++i) { + copy_zup_yup(vels[i].getValue(), mesh_vels); + mesh_vels += 3; + } + } + else { + std::fill(vels.begin(), vels.end(), Imath::V3f(0.0f)); + } +} + +void AbcMeshWriter::getGeoGroups( + DerivedMesh *dm, + std::map<std::string, std::vector<int32_t> > &geo_groups) +{ + const int num_poly = dm->getNumPolys(dm); + MPoly *polygons = dm->getPolyArray(dm); + + for (int i = 0; i < num_poly; ++i) { + MPoly ¤t_poly = polygons[i]; + short mnr = current_poly.mat_nr; + + Material *mat = give_current_material(m_object, mnr + 1); + + if (!mat) { + continue; + } + + std::string name = get_id_name(&mat->id); + + if (geo_groups.find(name) == geo_groups.end()) { + std::vector<int32_t> faceArray; + geo_groups[name] = faceArray; + } + + geo_groups[name].push_back(i); + } + + if (geo_groups.size() == 0) { + Material *mat = give_current_material(m_object, 1); + + std::string name = (mat) ? get_id_name(&mat->id) : "default"; + + std::vector<int32_t> faceArray; + + for (int i = 0, e = dm->getNumTessFaces(dm); i < e; ++i) { + faceArray.push_back(i); + } + + geo_groups[name] = faceArray; + } +} + +/* ************************************************************************** */ + +/* Some helpers for mesh generation */ +namespace utils { + +void mesh_add_verts(Mesh *mesh, size_t len) +{ + if (len == 0) { + return; + } + + const int totvert = mesh->totvert + len; + CustomData vdata; + CustomData_copy(&mesh->vdata, &vdata, CD_MASK_MESH, CD_DEFAULT, totvert); + CustomData_copy_data(&mesh->vdata, &vdata, 0, 0, mesh->totvert); + + if (!CustomData_has_layer(&vdata, CD_MVERT)) { + CustomData_add_layer(&vdata, CD_MVERT, CD_CALLOC, NULL, totvert); + } + + CustomData_free(&mesh->vdata, mesh->totvert); + mesh->vdata = vdata; + BKE_mesh_update_customdata_pointers(mesh, false); + + mesh->totvert = totvert; +} + +static void mesh_add_mloops(Mesh *mesh, size_t len) +{ + if (len == 0) { + return; + } + + /* new face count */ + const int totloops = mesh->totloop + len; + + CustomData ldata; + CustomData_copy(&mesh->ldata, &ldata, CD_MASK_MESH, CD_DEFAULT, totloops); + CustomData_copy_data(&mesh->ldata, &ldata, 0, 0, mesh->totloop); + + if (!CustomData_has_layer(&ldata, CD_MLOOP)) { + CustomData_add_layer(&ldata, CD_MLOOP, CD_CALLOC, NULL, totloops); + } + + CustomData_free(&mesh->ldata, mesh->totloop); + mesh->ldata = ldata; + BKE_mesh_update_customdata_pointers(mesh, false); + + mesh->totloop = totloops; +} + +static void mesh_add_mpolygons(Mesh *mesh, size_t len) +{ + if (len == 0) { + return; + } + + const int totpolys = mesh->totpoly + len; + + CustomData pdata; + CustomData_copy(&mesh->pdata, &pdata, CD_MASK_MESH, CD_DEFAULT, totpolys); + CustomData_copy_data(&mesh->pdata, &pdata, 0, 0, mesh->totpoly); + + if (!CustomData_has_layer(&pdata, CD_MPOLY)) { + CustomData_add_layer(&pdata, CD_MPOLY, CD_CALLOC, NULL, totpolys); + } + + CustomData_free(&mesh->pdata, mesh->totpoly); + mesh->pdata = pdata; + BKE_mesh_update_customdata_pointers(mesh, false); + + mesh->totpoly = totpolys; +} + +static void build_mat_map(const Main *bmain, std::map<std::string, Material *> &mat_map) +{ + Material *material = static_cast<Material *>(bmain->mat.first); + + for (; material; material = static_cast<Material *>(material->id.next)) { + mat_map[material->id.name + 2] = material; + } +} + +static void assign_materials(Main *bmain, Object *ob, const std::map<std::string, int> &mat_index_map) +{ + bool can_assign = true; + std::map<std::string, int>::const_iterator it = mat_index_map.begin(); + + int matcount = 0; + for (; it != mat_index_map.end(); ++it, ++matcount) { + if (!BKE_object_material_slot_add(ob)) { + can_assign = false; + break; + } + } + + /* TODO(kevin): use global map? */ + std::map<std::string, Material *> mat_map; + build_mat_map(bmain, mat_map); + + std::map<std::string, Material *>::iterator mat_iter; + + if (can_assign) { + it = mat_index_map.begin(); + + for (; it != mat_index_map.end(); ++it) { + std::string mat_name = it->first; + mat_iter = mat_map.find(mat_name.c_str()); + + Material *assigned_name; + + if (mat_iter == mat_map.end()) { + assigned_name = BKE_material_add(bmain, mat_name.c_str()); + mat_map[mat_name] = assigned_name; + } + else { + assigned_name = mat_iter->second; + } + + assign_material(ob, assigned_name, it->second, BKE_MAT_ASSIGN_OBJECT); + } + } +} + +} /* namespace utils */ + +/* ************************************************************************** */ + +using Alembic::AbcGeom::UInt32ArraySamplePtr; +using Alembic::AbcGeom::V2fArraySamplePtr; + +struct AbcMeshData { + Int32ArraySamplePtr face_indices; + Int32ArraySamplePtr face_counts; + + P3fArraySamplePtr positions; + + N3fArraySamplePtr vertex_normals; + N3fArraySamplePtr face_normals; + + V2fArraySamplePtr uvs; + UInt32ArraySamplePtr uvs_indices; +}; + +static void *add_customdata_cb(void *user_data, const char *name, int data_type) +{ + Mesh *mesh = static_cast<Mesh *>(user_data); + CustomDataType cd_data_type = static_cast<CustomDataType>(data_type); + void *cd_ptr = NULL; + + int index = -1; + if (cd_data_type == CD_MLOOPUV) { + index = ED_mesh_uv_texture_add(mesh, name, true); + cd_ptr = CustomData_get_layer(&mesh->ldata, cd_data_type); + } + else if (cd_data_type == CD_MLOOPCOL) { + index = ED_mesh_color_add(mesh, name, true); + cd_ptr = CustomData_get_layer(&mesh->ldata, cd_data_type); + } + + if (index == -1) { + return NULL; + } + + return cd_ptr; +} + +CDStreamConfig create_config(Mesh *mesh) +{ + CDStreamConfig config; + + config.mvert = mesh->mvert; + config.mpoly = mesh->mpoly; + config.mloop = mesh->mloop; + config.totpoly = mesh->totpoly; + config.totloop = mesh->totloop; + config.user_data = mesh; + config.loopdata = &mesh->ldata; + config.add_customdata_cb = add_customdata_cb; + + return config; +} + +static void read_mverts(CDStreamConfig &config, const AbcMeshData &mesh_data) +{ + MVert *mverts = config.mvert; + const P3fArraySamplePtr &positions = mesh_data.positions; + const N3fArraySamplePtr &normals = mesh_data.vertex_normals; + + read_mverts(mverts, positions, normals); +} + +void read_mverts(MVert *mverts, const P3fArraySamplePtr &positions, const N3fArraySamplePtr &normals) +{ + for (int i = 0; i < positions->size(); ++i) { + MVert &mvert = mverts[i]; + Imath::V3f pos_in = (*positions)[i]; + + copy_yup_zup(mvert.co, pos_in.getValue()); + + mvert.bweight = 0; + + if (normals) { + Imath::V3f nor_in = (*normals)[i]; + + short no[3]; + normal_float_to_short_v3(no, nor_in.getValue()); + + copy_yup_zup(mvert.no, no); + } + } +} + +static void read_mpolys(CDStreamConfig &config, const AbcMeshData &mesh_data) +{ + MPoly *mpolys = config.mpoly; + MLoop *mloops = config.mloop; + MLoopUV *mloopuvs = config.mloopuv; + + const Int32ArraySamplePtr &face_indices = mesh_data.face_indices; + const Int32ArraySamplePtr &face_counts = mesh_data.face_counts; + const V2fArraySamplePtr &uvs = mesh_data.uvs; + const UInt32ArraySamplePtr &uvs_indices = mesh_data.uvs_indices; + const N3fArraySamplePtr &normals = mesh_data.face_normals; + + const bool do_uvs = (mloopuvs && uvs && uvs_indices) && (uvs_indices->size() == face_indices->size()); + unsigned int loop_index = 0; + unsigned int rev_loop_index = 0; + unsigned int uv_index = 0; + + for (int i = 0; i < face_counts->size(); ++i) { + const int face_size = (*face_counts)[i]; + + MPoly &poly = mpolys[i]; + poly.loopstart = loop_index; + poly.totloop = face_size; + + if (normals != NULL) { + poly.flag |= ME_SMOOTH; + } + + /* NOTE: Alembic data is stored in the reverse order. */ + rev_loop_index = loop_index + (face_size - 1); + + for (int f = 0; f < face_size; ++f, ++loop_index, --rev_loop_index) { + MLoop &loop = mloops[rev_loop_index]; + loop.v = (*face_indices)[loop_index]; + + if (do_uvs) { + MLoopUV &loopuv = mloopuvs[rev_loop_index]; + + uv_index = (*uvs_indices)[loop_index]; + loopuv.uv[0] = (*uvs)[uv_index][0]; + loopuv.uv[1] = (*uvs)[uv_index][1]; + } + } + } +} + +ABC_INLINE void read_uvs_params(CDStreamConfig &config, + AbcMeshData &abc_data, + const IV2fGeomParam &uv, + const ISampleSelector &selector) +{ + if (!uv.valid()) { + return; + } + + IV2fGeomParam::Sample uvsamp; + uv.getIndexed(uvsamp, selector); + + abc_data.uvs = uvsamp.getVals(); + abc_data.uvs_indices = uvsamp.getIndices(); + + if (abc_data.uvs_indices->size() == config.totloop) { + std::string name = Alembic::Abc::GetSourceName(uv.getMetaData()); + + /* According to the convention, primary UVs should have had their name + * set using Alembic::Abc::SetSourceName, but you can't expect everyone + * to follow it! :) */ + if (name.empty()) { + name = uv.getName(); + } + + void *cd_ptr = config.add_customdata_cb(config.user_data, name.c_str(), CD_MLOOPUV); + config.mloopuv = static_cast<MLoopUV *>(cd_ptr); + } +} + +/* TODO(kevin): normals from Alembic files are not read in anymore, this is due + * to the fact that there are many issues that are not so easy to solve, mainly + * regarding the way normals are handled in Blender (MPoly.flag vs loop normals). + */ +ABC_INLINE void read_normals_params(AbcMeshData &abc_data, + const IN3fGeomParam &normals, + const ISampleSelector &selector) +{ + if (!normals.valid()) { + return; + } + + IN3fGeomParam::Sample normsamp = normals.getExpandedValue(selector); + + if (normals.getScope() == kFacevaryingScope) { + abc_data.face_normals = normsamp.getVals(); + } + else if ((normals.getScope() == kVertexScope) || (normals.getScope() == kVaryingScope)) { + abc_data.vertex_normals = N3fArraySamplePtr(); + } +} + +/* ************************************************************************** */ + +AbcMeshReader::AbcMeshReader(const IObject &object, ImportSettings &settings) + : AbcObjectReader(object, settings) +{ + m_settings->read_flag |= MOD_MESHSEQ_READ_ALL; + + IPolyMesh ipoly_mesh(m_iobject, kWrapExisting); + m_schema = ipoly_mesh.getSchema(); + + get_min_max_time(m_iobject, m_schema, m_min_time, m_max_time); +} + +bool AbcMeshReader::valid() const +{ + return m_schema.valid(); +} + +void AbcMeshReader::readObjectData(Main *bmain, float time) +{ + Mesh *mesh = BKE_mesh_add(bmain, m_data_name.c_str()); + + m_object = BKE_object_add_only_object(bmain, OB_MESH, m_object_name.c_str()); + m_object->data = mesh; + + const ISampleSelector sample_sel(time); + const IPolyMeshSchema::Sample sample = m_schema.getValue(sample_sel); + + const P3fArraySamplePtr &positions = sample.getPositions(); + const Int32ArraySamplePtr &face_indices = sample.getFaceIndices(); + const Int32ArraySamplePtr &face_counts = sample.getFaceCounts(); + + utils::mesh_add_verts(mesh, positions->size()); + utils::mesh_add_mpolygons(mesh, face_counts->size()); + utils::mesh_add_mloops(mesh, face_indices->size()); + + m_mesh_data = create_config(mesh); + + bool has_smooth_normals = false; + read_mesh_sample(m_settings, m_schema, sample_sel, m_mesh_data, has_smooth_normals); + + BKE_mesh_calc_normals(mesh); + BKE_mesh_calc_edges(mesh, false, false); + + if (m_settings->validate_meshes) { + BKE_mesh_validate(mesh, false, false); + } + + readFaceSetsSample(bmain, mesh, 0, sample_sel); + + if (has_animations(m_schema, m_settings)) { + addCacheModifier(); + } +} + +void AbcMeshReader::readFaceSetsSample(Main *bmain, Mesh *mesh, size_t poly_start, + const ISampleSelector &sample_sel) +{ + std::vector<std::string> face_sets; + m_schema.getFaceSetNames(face_sets); + + if (face_sets.empty()) { + return; + } + + std::map<std::string, int> mat_map; + int current_mat = 0; + + for (int i = 0; i < face_sets.size(); ++i) { + const std::string &grp_name = face_sets[i]; + + if (mat_map.find(grp_name) == mat_map.end()) { + mat_map[grp_name] = 1 + current_mat++; + } + + const int assigned_mat = mat_map[grp_name]; + + const IFaceSet faceset = m_schema.getFaceSet(grp_name); + + if (!faceset.valid()) { + continue; + } + + const IFaceSetSchema face_schem = faceset.getSchema(); + const IFaceSetSchema::Sample face_sample = face_schem.getValue(sample_sel); + const Int32ArraySamplePtr group_faces = face_sample.getFaces(); + const size_t num_group_faces = group_faces->size(); + + for (size_t l = 0; l < num_group_faces; l++) { + size_t pos = (*group_faces)[l] + poly_start; + + if (pos >= mesh->totpoly) { + std::cerr << "Faceset overflow on " << faceset.getName() << '\n'; + break; + } + + MPoly &poly = mesh->mpoly[pos]; + poly.mat_nr = assigned_mat - 1; + } + } + + utils::assign_materials(bmain, m_object, mat_map); +} + +void read_mesh_sample(ImportSettings *settings, + const IPolyMeshSchema &schema, + const ISampleSelector &selector, + CDStreamConfig &config, + bool &do_normals) +{ + const IPolyMeshSchema::Sample sample = schema.getValue(selector); + + AbcMeshData abc_mesh_data; + abc_mesh_data.face_counts = sample.getFaceCounts(); + abc_mesh_data.face_indices = sample.getFaceIndices(); + abc_mesh_data.positions = sample.getPositions(); + + read_normals_params(abc_mesh_data, schema.getNormalsParam(), selector); + + do_normals = (abc_mesh_data.face_normals != NULL); + + if ((settings->read_flag & MOD_MESHSEQ_READ_UV) != 0) { + read_uvs_params(config, abc_mesh_data, schema.getUVsParam(), selector); + } + + if ((settings->read_flag & MOD_MESHSEQ_READ_VERT) != 0) { + read_mverts(config, abc_mesh_data); + } + + if ((settings->read_flag & MOD_MESHSEQ_READ_POLY) != 0) { + read_mpolys(config, abc_mesh_data); + } + + if ((settings->read_flag & (MOD_MESHSEQ_READ_UV | MOD_MESHSEQ_READ_COLOR)) != 0) { + read_custom_data(schema.getArbGeomParams(), config, selector); + } + + /* TODO: face sets */ +} + +/* ************************************************************************** */ + +ABC_INLINE MEdge *find_edge(MEdge *edges, int totedge, int v1, int v2) +{ + for (int i = 0, e = totedge; i < e; ++i) { + MEdge &edge = edges[i]; + + if (edge.v1 == v1 && edge.v2 == v2) { + return &edge; + } + } + + return NULL; +} + +AbcSubDReader::AbcSubDReader(const IObject &object, ImportSettings &settings) + : AbcObjectReader(object, settings) +{ + m_settings->read_flag |= MOD_MESHSEQ_READ_ALL; + + ISubD isubd_mesh(m_iobject, kWrapExisting); + m_schema = isubd_mesh.getSchema(); + + get_min_max_time(m_iobject, m_schema, m_min_time, m_max_time); +} + +bool AbcSubDReader::valid() const +{ + return m_schema.valid(); +} + +void AbcSubDReader::readObjectData(Main *bmain, float time) +{ + Mesh *mesh = BKE_mesh_add(bmain, m_data_name.c_str()); + + m_object = BKE_object_add_only_object(bmain, OB_MESH, m_object_name.c_str()); + m_object->data = mesh; + + const ISampleSelector sample_sel(time); + const ISubDSchema::Sample sample = m_schema.getValue(sample_sel); + + const P3fArraySamplePtr &positions = sample.getPositions(); + const Int32ArraySamplePtr &face_indices = sample.getFaceIndices(); + const Int32ArraySamplePtr &face_counts = sample.getFaceCounts(); + + utils::mesh_add_verts(mesh, positions->size()); + utils::mesh_add_mpolygons(mesh, face_counts->size()); + utils::mesh_add_mloops(mesh, face_indices->size()); + + m_mesh_data = create_config(mesh); + + read_subd_sample(m_settings, m_schema, sample_sel, m_mesh_data); + + Int32ArraySamplePtr indices = sample.getCreaseIndices(); + Alembic::Abc::FloatArraySamplePtr sharpnesses = sample.getCreaseSharpnesses(); + + MEdge *edges = mesh->medge; + + if (indices && sharpnesses) { + for (int i = 0, s = 0, e = indices->size(); i < e; i += 2, ++s) { + MEdge *edge = find_edge(edges, mesh->totedge, (*indices)[i], (*indices)[i + 1]); + + if (edge) { + edge->crease = FTOCHAR((*sharpnesses)[s]); + } + } + + mesh->cd_flag |= ME_CDFLAG_EDGE_CREASE; + } + + BKE_mesh_calc_normals(mesh); + BKE_mesh_calc_edges(mesh, false, false); + + if (m_settings->validate_meshes) { + BKE_mesh_validate(mesh, false, false); + } + + if (has_animations(m_schema, m_settings)) { + addCacheModifier(); + } +} + +void read_subd_sample(ImportSettings *settings, + const ISubDSchema &schema, + const ISampleSelector &selector, + CDStreamConfig &config) +{ + const ISubDSchema::Sample sample = schema.getValue(selector); + + AbcMeshData abc_mesh_data; + abc_mesh_data.face_counts = sample.getFaceCounts(); + abc_mesh_data.face_indices = sample.getFaceIndices(); + abc_mesh_data.vertex_normals = N3fArraySamplePtr(); + abc_mesh_data.face_normals = N3fArraySamplePtr(); + abc_mesh_data.positions = sample.getPositions(); + + if ((settings->read_flag & MOD_MESHSEQ_READ_UV) != 0) { + read_uvs_params(config, abc_mesh_data, schema.getUVsParam(), selector); + } + + if ((settings->read_flag & MOD_MESHSEQ_READ_VERT) != 0) { + read_mverts(config, abc_mesh_data); + } + + if ((settings->read_flag & MOD_MESHSEQ_READ_POLY) != 0) { + read_mpolys(config, abc_mesh_data); + } + + if ((settings->read_flag & (MOD_MESHSEQ_READ_UV | MOD_MESHSEQ_READ_COLOR)) != 0) { + read_custom_data(schema.getArbGeomParams(), config, selector); + } + + /* TODO: face sets */ +} diff --git a/source/blender/alembic/intern/abc_mesh.h b/source/blender/alembic/intern/abc_mesh.h new file mode 100644 index 00000000000..9dc222e5206 --- /dev/null +++ b/source/blender/alembic/intern/abc_mesh.h @@ -0,0 +1,152 @@ +/* + * ***** 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. + * + * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __ABC_MESH_H__ +#define __ABC_MESH_H__ + +#include "abc_customdata.h" +#include "abc_object.h" + +struct DerivedMesh; +struct Mesh; +struct ModifierData; + +/* ************************************************************************** */ + +class AbcMeshWriter : public AbcObjectWriter { + Alembic::AbcGeom::OPolyMeshSchema m_mesh_schema; + Alembic::AbcGeom::OPolyMeshSchema::Sample m_mesh_sample; + + Alembic::AbcGeom::OSubDSchema m_subdiv_schema; + Alembic::AbcGeom::OSubDSchema::Sample m_subdiv_sample; + + bool m_has_per_face_materials; + Alembic::AbcGeom::OFaceSet m_face_set; + Alembic::Abc::OArrayProperty m_mat_indices; + + bool m_is_animated; + ModifierData *m_subsurf_mod; + + CDStreamConfig m_custom_data_config; + + bool m_is_liquid; + bool m_is_subd; + +public: + AbcMeshWriter(Scene *scene, + Object *ob, + AbcTransformWriter *parent, + uint32_t time_sampling, + ExportSettings &settings); + + ~AbcMeshWriter(); + +private: + virtual void do_write(); + + bool isAnimated() const; + + void writeMesh(DerivedMesh *dm); + void writeSubD(DerivedMesh *dm); + + void getMeshInfo(DerivedMesh *dm, std::vector<float> &points, + std::vector<int32_t> &facePoints, + std::vector<int32_t> &faceCounts, + std::vector<int32_t> &creaseIndices, + std::vector<int32_t> &creaseLengths, + std::vector<float> &creaseSharpness); + + DerivedMesh *getFinalMesh(); + void freeMesh(DerivedMesh *dm); + + void getMaterialIndices(DerivedMesh *dm, std::vector<int32_t> &indices); + + void writeArbGeoParams(DerivedMesh *dm); + void getGeoGroups(DerivedMesh *dm, std::map<std::string, std::vector<int32_t> > &geoGroups); + + /* fluid surfaces support */ + void getVelocities(DerivedMesh *dm, std::vector<Imath::V3f> &vels); + + template <typename Schema> + void writeCommonData(DerivedMesh *dm, Schema &schema); +}; + +/* ************************************************************************** */ + +class AbcMeshReader : public AbcObjectReader { + Alembic::AbcGeom::IPolyMeshSchema m_schema; + + CDStreamConfig m_mesh_data; + +public: + AbcMeshReader(const Alembic::Abc::IObject &object, ImportSettings &settings); + + bool valid() const; + + void readObjectData(Main *bmain, float time); + +private: + void readFaceSetsSample(Main *bmain, Mesh *mesh, size_t poly_start, + const Alembic::AbcGeom::ISampleSelector &sample_sel); +}; + +void read_mesh_sample(ImportSettings *settings, + const Alembic::AbcGeom::IPolyMeshSchema &schema, + const Alembic::AbcGeom::ISampleSelector &selector, + CDStreamConfig &config, + bool &do_normals); + +/* ************************************************************************** */ + +class AbcSubDReader : public AbcObjectReader { + Alembic::AbcGeom::ISubDSchema m_schema; + + CDStreamConfig m_mesh_data; + +public: + AbcSubDReader(const Alembic::Abc::IObject &object, ImportSettings &settings); + + bool valid() const; + + void readObjectData(Main *bmain, float time); +}; + +void read_subd_sample(ImportSettings *settings, + const Alembic::AbcGeom::ISubDSchema &schema, + const Alembic::AbcGeom::ISampleSelector &selector, + CDStreamConfig &config); + +/* ************************************************************************** */ + +namespace utils { + +void mesh_add_verts(struct Mesh *mesh, size_t len); + +} + +void read_mverts(MVert *mverts, + const Alembic::AbcGeom::P3fArraySamplePtr &positions, + const Alembic::AbcGeom::N3fArraySamplePtr &normals); + +CDStreamConfig create_config(Mesh *mesh); + +#endif /* __ABC_MESH_H__ */ diff --git a/source/blender/alembic/intern/abc_nurbs.cc b/source/blender/alembic/intern/abc_nurbs.cc new file mode 100644 index 00000000000..4f57dfdae9e --- /dev/null +++ b/source/blender/alembic/intern/abc_nurbs.cc @@ -0,0 +1,367 @@ +/* + * ***** 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. + * + * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "abc_nurbs.h" + +#include "abc_transform.h" +#include "abc_util.h" + +extern "C" { +#include "MEM_guardedalloc.h" + +#include "DNA_curve_types.h" +#include "DNA_object_types.h" + +#include "BLI_listbase.h" +#include "BLI_math.h" +#include "BLI_string.h" + +#include "BKE_curve.h" +#include "BKE_object.h" +} + +using Alembic::AbcGeom::bool_t; +using Alembic::AbcGeom::FloatArraySample; +using Alembic::AbcGeom::FloatArraySamplePtr; +using Alembic::AbcGeom::MetaData; +using Alembic::AbcGeom::P3fArraySamplePtr; +using Alembic::AbcGeom::kWrapExisting; + +using Alembic::AbcGeom::IBoolProperty; +using Alembic::AbcGeom::ICompoundProperty; +using Alembic::AbcGeom::INuPatch; +using Alembic::AbcGeom::INuPatchSchema; +using Alembic::AbcGeom::IObject; +using Alembic::AbcGeom::ISampleSelector; + +using Alembic::AbcGeom::OBoolProperty; +using Alembic::AbcGeom::OCompoundProperty; +using Alembic::AbcGeom::ONuPatch; +using Alembic::AbcGeom::ONuPatchSchema; + +/* ************************************************************************** */ + +AbcNurbsWriter::AbcNurbsWriter(Scene *scene, + Object *ob, + AbcTransformWriter *parent, + uint32_t time_sampling, + ExportSettings &settings) + : AbcObjectWriter(scene, ob, time_sampling, settings, parent) +{ + m_is_animated = isAnimated(); + + /* if the object is static, use the default static time sampling */ + if (!m_is_animated) { + m_time_sampling = 0; + } + + Curve *curve = static_cast<Curve *>(m_object->data); + size_t numNurbs = BLI_listbase_count(&curve->nurb); + + for (size_t i = 0; i < numNurbs; ++i) { + std::stringstream str; + str << m_name << '_' << i; + + while (parent->alembicXform().getChildHeader(str.str())) { + str << "_"; + } + + ONuPatch nurbs(parent->alembicXform(), str.str().c_str(), m_time_sampling); + m_nurbs_schema.push_back(nurbs.getSchema()); + } +} + +bool AbcNurbsWriter::isAnimated() const +{ + /* check if object has shape keys */ + Curve *cu = static_cast<Curve *>(m_object->data); + return (cu->key != NULL); +} + +static void get_knots(std::vector<float> &knots, const int num_knots, float *nu_knots) +{ + if (num_knots <= 1) { + return; + } + + /* Add an extra knot at the beggining and end of the array since most apps + * require/expect them. */ + knots.reserve(num_knots + 2); + + knots.push_back(0.0f); + + for (int i = 0; i < num_knots; ++i) { + knots.push_back(nu_knots[i]); + } + + knots[0] = 2.0f * knots[1] - knots[2]; + knots.push_back(2.0f * knots[num_knots] - knots[num_knots - 1]); +} + +void AbcNurbsWriter::do_write() +{ + /* we have already stored a sample for this object. */ + if (!m_first_frame && !m_is_animated) { + return; + } + + if (!ELEM(m_object->type, OB_SURF, OB_CURVE)) { + return; + } + + Curve *curve = static_cast<Curve *>(m_object->data); + ListBase *nulb; + + if (m_object->curve_cache->deformed_nurbs.first != NULL) { + nulb = &m_object->curve_cache->deformed_nurbs; + } + else { + nulb = BKE_curve_nurbs_get(curve); + } + + size_t count = 0; + for (Nurb *nu = static_cast<Nurb *>(nulb->first); nu; nu = nu->next, ++count) { + std::vector<float> knotsU; + get_knots(knotsU, KNOTSU(nu), nu->knotsu); + + std::vector<float> knotsV; + get_knots(knotsV, KNOTSV(nu), nu->knotsv); + + const int size = nu->pntsu * nu->pntsv; + std::vector<Imath::V3f> positions(size); + std::vector<float> weights(size); + + const BPoint *bp = nu->bp; + + for (int i = 0; i < size; ++i, ++bp) { + copy_zup_yup(positions[i].getValue(), bp->vec); + weights[i] = bp->vec[3]; + } + + ONuPatchSchema::Sample sample; + sample.setUOrder(nu->orderu + 1); + sample.setVOrder(nu->orderv + 1); + sample.setPositions(positions); + sample.setPositionWeights(weights); + sample.setUKnot(FloatArraySample(knotsU)); + sample.setVKnot(FloatArraySample(knotsV)); + sample.setNu(nu->pntsu); + sample.setNv(nu->pntsv); + + /* TODO(kevin): to accomodate other software we should duplicate control + * points to indicate that a NURBS is cyclic. */ + OCompoundProperty user_props = m_nurbs_schema[count].getUserProperties(); + + if ((nu->flagu & CU_NURB_ENDPOINT) != 0) { + OBoolProperty prop(user_props, "endpoint_u"); + prop.set(true); + } + + if ((nu->flagv & CU_NURB_ENDPOINT) != 0) { + OBoolProperty prop(user_props, "endpoint_v"); + prop.set(true); + } + + if ((nu->flagu & CU_NURB_CYCLIC) != 0) { + OBoolProperty prop(user_props, "cyclic_u"); + prop.set(true); + } + + if ((nu->flagv & CU_NURB_CYCLIC) != 0) { + OBoolProperty prop(user_props, "cyclic_v"); + prop.set(true); + } + + m_nurbs_schema[count].set(sample); + } +} + +/* ************************************************************************** */ + +AbcNurbsReader::AbcNurbsReader(const IObject &object, ImportSettings &settings) + : AbcObjectReader(object, settings) +{ + getNurbsPatches(m_iobject); + get_min_max_time(m_iobject, m_schemas[0].first, m_min_time, m_max_time); +} + +bool AbcNurbsReader::valid() const +{ + if (m_schemas.empty()) { + return false; + } + + std::vector< std::pair<INuPatchSchema, IObject> >::const_iterator it; + for (it = m_schemas.begin(); it != m_schemas.end(); ++it) { + const INuPatchSchema &schema = it->first; + + if (!schema.valid()) { + return false; + } + } + + return true; +} + +static bool set_knots(const FloatArraySamplePtr &knots, float *&nu_knots) +{ + if (!knots || knots->size() == 0) { + return false; + } + + /* Skip first and last knots, as they are used for padding. */ + const size_t num_knots = knots->size() - 2; + nu_knots = static_cast<float *>(MEM_callocN(num_knots * sizeof(float), "abc_setsplineknotsu")); + + for (size_t i = 0; i < num_knots; ++i) { + nu_knots[i] = (*knots)[i + 1]; + } + + return true; +} + +void AbcNurbsReader::readObjectData(Main *bmain, float time) +{ + Curve *cu = static_cast<Curve *>(BKE_curve_add(bmain, "abc_curve", OB_SURF)); + cu->actvert = CU_ACT_NONE; + + std::vector< std::pair<INuPatchSchema, IObject> >::iterator it; + + for (it = m_schemas.begin(); it != m_schemas.end(); ++it) { + Nurb *nu = static_cast<Nurb *>(MEM_callocN(sizeof(Nurb), "abc_getnurb")); + nu->flag = CU_SMOOTH; + nu->type = CU_NURBS; + nu->resolu = cu->resolu; + nu->resolv = cu->resolv; + + const ISampleSelector sample_sel(time); + const INuPatchSchema &schema = it->first; + const INuPatchSchema::Sample smp = schema.getValue(sample_sel); + + nu->orderu = smp.getUOrder() - 1; + nu->orderv = smp.getVOrder() - 1; + nu->pntsu = smp.getNumU(); + nu->pntsv = smp.getNumV(); + + /* Read positions and weights. */ + + const P3fArraySamplePtr positions = smp.getPositions(); + const FloatArraySamplePtr weights = smp.getPositionWeights(); + + const size_t num_points = positions->size(); + + nu->bp = static_cast<BPoint *>(MEM_callocN(num_points * sizeof(BPoint), "abc_setsplinetype")); + + BPoint *bp = nu->bp; + float posw_in = 1.0f; + + for (int i = 0; i < num_points; ++i, ++bp) { + const Imath::V3f &pos_in = (*positions)[i]; + + if (weights) { + posw_in = (*weights)[i]; + } + + copy_yup_zup(bp->vec, pos_in.getValue()); + bp->vec[3] = posw_in; + bp->f1 = SELECT; + bp->radius = 1.0f; + bp->weight = 1.0f; + } + + /* Read knots. */ + + if (!set_knots(smp.getUKnot(), nu->knotsu)) { + BKE_nurb_knot_calc_u(nu); + } + + if (!set_knots(smp.getVKnot(), nu->knotsv)) { + BKE_nurb_knot_calc_v(nu); + } + + /* Read flags. */ + + ICompoundProperty user_props = schema.getUserProperties(); + + if (has_property(user_props, "enpoint_u")) { + nu->flagu |= CU_NURB_ENDPOINT; + } + + if (has_property(user_props, "enpoint_v")) { + nu->flagv |= CU_NURB_ENDPOINT; + } + + if (has_property(user_props, "cyclic_u")) { + nu->flagu |= CU_NURB_CYCLIC; + } + + if (has_property(user_props, "cyclic_v")) { + nu->flagv |= CU_NURB_CYCLIC; + } + + BLI_addtail(BKE_curve_nurbs_get(cu), nu); + } + + BLI_strncpy(cu->id.name + 2, m_data_name.c_str(), m_data_name.size() + 1); + + m_object = BKE_object_add_only_object(bmain, OB_SURF, m_object_name.c_str()); + m_object->data = cu; +} + +void AbcNurbsReader::getNurbsPatches(const IObject &obj) +{ + if (!obj.valid()) { + return; + } + + const int num_children = obj.getNumChildren(); + + if (num_children == 0) { + INuPatch abc_nurb(obj, kWrapExisting); + INuPatchSchema schem = abc_nurb.getSchema(); + m_schemas.push_back(std::pair<INuPatchSchema, IObject>(schem, obj)); + return; + } + + for (int i = 0; i < num_children; ++i) { + bool ok = true; + IObject child(obj, obj.getChildHeader(i).getName()); + + if (!m_name.empty() && child.valid() && !begins_with(child.getFullName(), m_name)) { + ok = false; + } + + if (!child.valid()) { + continue; + } + + const MetaData &md = child.getMetaData(); + + if (INuPatch::matches(md) && ok) { + INuPatch abc_nurb(child, kWrapExisting); + INuPatchSchema schem = abc_nurb.getSchema(); + m_schemas.push_back(std::pair<INuPatchSchema, IObject>(schem, child)); + } + + getNurbsPatches(child); + } +} diff --git a/source/blender/alembic/intern/abc_nurbs.h b/source/blender/alembic/intern/abc_nurbs.h new file mode 100644 index 00000000000..1b2e7a8391f --- /dev/null +++ b/source/blender/alembic/intern/abc_nurbs.h @@ -0,0 +1,63 @@ +/* + * ***** 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. + * + * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __ABC_NURBS_H__ +#define __ABC_NURBS_H__ + +#include "abc_object.h" + +/* ************************************************************************** */ + +class AbcNurbsWriter : public AbcObjectWriter { + std::vector<Alembic::AbcGeom::ONuPatchSchema> m_nurbs_schema; + bool m_is_animated; + +public: + AbcNurbsWriter(Scene *scene, + Object *ob, + AbcTransformWriter *parent, + uint32_t time_sampling, + ExportSettings &settings); + +private: + virtual void do_write(); + + bool isAnimated() const; +}; + +/* ************************************************************************** */ + +class AbcNurbsReader : public AbcObjectReader { + std::vector< std::pair<Alembic::AbcGeom::INuPatchSchema, Alembic::Abc::IObject> > m_schemas; + +public: + AbcNurbsReader(const Alembic::Abc::IObject &object, ImportSettings &settings); + + bool valid() const; + + void readObjectData(Main *bmain, float time); + +private: + void getNurbsPatches(const Alembic::Abc::IObject &obj); +}; + +#endif /* __ABC_NURBS_H__ */ diff --git a/source/blender/alembic/intern/abc_object.cc b/source/blender/alembic/intern/abc_object.cc new file mode 100644 index 00000000000..5b7b85f10ea --- /dev/null +++ b/source/blender/alembic/intern/abc_object.cc @@ -0,0 +1,238 @@ +/* + * ***** 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. + * + * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "abc_object.h" + +#include "abc_util.h" + +extern "C" { +#include "DNA_cachefile_types.h" +#include "DNA_constraint_types.h" +#include "DNA_modifier_types.h" +#include "DNA_object_types.h" +#include "DNA_space_types.h" /* for FILE_MAX */ + +#include "BKE_constraint.h" +#include "BKE_depsgraph.h" +#include "BKE_idprop.h" +#include "BKE_library.h" +#include "BKE_modifier.h" +#include "BKE_object.h" + +#include "BLI_listbase.h" +#include "BLI_math.h" +#include "BLI_string.h" +} + +using Alembic::AbcGeom::IObject; +using Alembic::AbcGeom::IXform; +using Alembic::AbcGeom::IXformSchema; + +using Alembic::AbcGeom::OCompoundProperty; +using Alembic::AbcGeom::ODoubleArrayProperty; +using Alembic::AbcGeom::ODoubleProperty; +using Alembic::AbcGeom::OFloatArrayProperty; +using Alembic::AbcGeom::OFloatProperty; +using Alembic::AbcGeom::OInt32ArrayProperty; +using Alembic::AbcGeom::OInt32Property; +using Alembic::AbcGeom::OStringArrayProperty; +using Alembic::AbcGeom::OStringProperty; + +/* ************************************************************************** */ + +AbcObjectWriter::AbcObjectWriter(Scene *scene, + Object *ob, + uint32_t time_sampling, + ExportSettings &settings, + AbcObjectWriter *parent) + : m_object(ob) + , m_settings(settings) + , m_scene(scene) + , m_time_sampling(time_sampling) + , m_first_frame(true) +{ + m_name = get_id_name(m_object) + "Shape"; + + if (parent) { + parent->addChild(this); + } +} + +AbcObjectWriter::~AbcObjectWriter() +{} + +void AbcObjectWriter::addChild(AbcObjectWriter *child) +{ + m_children.push_back(child); +} + +Imath::Box3d AbcObjectWriter::bounds() +{ + BoundBox *bb = BKE_object_boundbox_get(this->m_object); + + if (!bb) { + if (this->m_object->type != OB_CAMERA) { + std::cerr << "Boundbox is null!\n"; + } + + return Imath::Box3d(); + } + + /* Convert Z-up to Y-up. */ + this->m_bounds.min.x = bb->vec[0][0]; + this->m_bounds.min.y = bb->vec[0][2]; + this->m_bounds.min.z = -bb->vec[0][1]; + + this->m_bounds.max.x = bb->vec[6][0]; + this->m_bounds.max.y = bb->vec[6][2]; + this->m_bounds.max.z = -bb->vec[6][1]; + + return this->m_bounds; +} + +void AbcObjectWriter::write() +{ + do_write(); + m_first_frame = false; +} + +/* ************************************************************************** */ + +AbcObjectReader::AbcObjectReader(const IObject &object, ImportSettings &settings) + : m_name("") + , m_object_name("") + , m_data_name("") + , m_object(NULL) + , m_iobject(object) + , m_settings(&settings) + , m_min_time(std::numeric_limits<chrono_t>::max()) + , m_max_time(std::numeric_limits<chrono_t>::min()) +{ + m_name = object.getFullName(); + std::vector<std::string> parts; + split(m_name, '/', parts); + + if (parts.size() >= 2) { + m_object_name = parts[parts.size() - 2]; + m_data_name = parts[parts.size() - 1]; + } + else { + m_object_name = m_data_name = parts[parts.size() - 1]; + } +} + +AbcObjectReader::~AbcObjectReader() +{} + +const IObject &AbcObjectReader::iobject() const +{ + return m_iobject; +} + +Object *AbcObjectReader::object() const +{ + return m_object; +} + +void AbcObjectReader::readObjectMatrix(const float time) +{ + IXform ixform; + bool has_alembic_parent = false; + + /* Check that we have an empty object (locator, bone head/tail...). */ + if (IXform::matches(m_iobject.getMetaData())) { + ixform = 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 + * transformations added to the matrix (rotation/scale) are only applied + * to root objects. The way objects and their hierarchy are created will + * need to be revisited at some point but for now this seems to do the + * trick. + * + * Explanation of the trick: + * The first getParent() will return this object's transformation matrix. + * 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. */ + else { + return; + } + + const IXformSchema &schema(ixform.getSchema()); + + if (!schema.valid()) { + return; + } + + Alembic::AbcGeom::ISampleSelector sample_sel(time); + Alembic::AbcGeom::XformSample xs; + schema.get(xs, sample_sel); + + create_input_transform(sample_sel, ixform, m_object, m_object->obmat, m_settings->scale, has_alembic_parent); + + invert_m4_m4(m_object->imat, m_object->obmat); + + BKE_object_apply_mat4(m_object, m_object->obmat, false, false); + + if (!schema.isConstant()) { + bConstraint *con = BKE_constraint_add_for_object(m_object, NULL, CONSTRAINT_TYPE_TRANSFORM_CACHE); + bTransformCacheConstraint *data = static_cast<bTransformCacheConstraint *>(con->data); + BLI_strncpy(data->object_path, m_iobject.getFullName().c_str(), FILE_MAX); + + data->cache_file = m_settings->cache_file; + id_us_plus(&data->cache_file->id); + } +} + +void AbcObjectReader::addCacheModifier() const +{ + ModifierData *md = modifier_new(eModifierType_MeshSequenceCache); + BLI_addtail(&m_object->modifiers, md); + + MeshSeqCacheModifierData *mcmd = reinterpret_cast<MeshSeqCacheModifierData *>(md); + + mcmd->cache_file = m_settings->cache_file; + id_us_plus(&mcmd->cache_file->id); + + BLI_strncpy(mcmd->object_path, m_iobject.getFullName().c_str(), FILE_MAX); +} + +chrono_t AbcObjectReader::minTime() const +{ + return m_min_time; +} + +chrono_t AbcObjectReader::maxTime() const +{ + return m_max_time; +} diff --git a/source/blender/alembic/intern/abc_object.h b/source/blender/alembic/intern/abc_object.h new file mode 100644 index 00000000000..2e885f296b1 --- /dev/null +++ b/source/blender/alembic/intern/abc_object.h @@ -0,0 +1,169 @@ +/* + * ***** 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. + * + * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __ABC_OBJECT_H__ +#define __ABC_OBJECT_H__ + +#include <Alembic/Abc/All.h> +#include <Alembic/AbcGeom/All.h> + +#include "abc_exporter.h" + +extern "C" { +#include "DNA_ID.h" +} + +class AbcTransformWriter; + +struct Main; +struct Object; + +/* ************************************************************************** */ + +class AbcObjectWriter { +protected: + Object *m_object; + ExportSettings &m_settings; + + Scene *m_scene; + uint32_t m_time_sampling; + + Imath::Box3d m_bounds; + std::vector<AbcObjectWriter *> m_children; + + std::vector< std::pair<std::string, IDProperty *> > m_props; + + bool m_first_frame; + std::string m_name; + +public: + AbcObjectWriter(Scene *scene, + Object *ob, + uint32_t time_sampling, + ExportSettings &settings, + AbcObjectWriter *parent = NULL); + + virtual ~AbcObjectWriter(); + + void addChild(AbcObjectWriter *child); + + virtual Imath::Box3d bounds(); + + void write(); + +private: + virtual void do_write() = 0; +}; + +/* ************************************************************************** */ + +class CacheFile; + +struct ImportSettings { + bool do_convert_mat; + float conversion_mat[4][4]; + + int from_up; + int from_forward; + float scale; + bool is_sequence; + bool set_frame_range; + + /* Length and frame offset of file sequences. */ + int sequence_len; + int offset; + + /* From MeshSeqCacheModifierData.read_flag */ + int read_flag; + + bool validate_meshes; + + CacheFile *cache_file; + + ImportSettings() + : do_convert_mat(false) + , from_up(0) + , from_forward(0) + , scale(1.0f) + , is_sequence(false) + , set_frame_range(false) + , sequence_len(1) + , offset(0) + , read_flag(0) + , validate_meshes(false) + , cache_file(NULL) + {} +}; + +template <typename Schema> +static bool has_animations(Schema &schema, ImportSettings *settings) +{ + if (settings->is_sequence) { + return true; + } + + if (!schema.isConstant()) { + return true; + } + + return false; +} + +/* ************************************************************************** */ + +using Alembic::AbcCoreAbstract::chrono_t; + +class AbcObjectReader { +protected: + std::string m_name; + std::string m_object_name; + std::string m_data_name; + Object *m_object; + Alembic::Abc::IObject m_iobject; + + ImportSettings *m_settings; + + chrono_t m_min_time; + chrono_t m_max_time; + +public: + explicit AbcObjectReader(const Alembic::Abc::IObject &object, ImportSettings &settings); + + virtual ~AbcObjectReader(); + + const Alembic::Abc::IObject &iobject() const; + + Object *object() const; + + virtual bool valid() const = 0; + + virtual void readObjectData(Main *bmain, float time) = 0; + + void readObjectMatrix(const float time); + + void addCacheModifier() const; + + chrono_t minTime() const; + chrono_t maxTime() const; +}; + +#endif /* __ABC_OBJECT_H__ */ diff --git a/source/blender/alembic/intern/abc_points.cc b/source/blender/alembic/intern/abc_points.cc new file mode 100644 index 00000000000..322bb906bf5 --- /dev/null +++ b/source/blender/alembic/intern/abc_points.cc @@ -0,0 +1,199 @@ +/* + * ***** 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) 2016 Kévin Dietrich. + * All rights reserved. + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +#include "abc_points.h" + +#include "abc_mesh.h" +#include "abc_transform.h" +#include "abc_util.h" + +extern "C" { +#include "DNA_mesh_types.h" +#include "DNA_object_types.h" + +#include "BKE_lattice.h" +#include "BKE_mesh.h" +#include "BKE_object.h" +#include "BKE_particle.h" +#include "BKE_scene.h" + +#include "BLI_math.h" +} + +using Alembic::AbcGeom::kVertexScope; +using Alembic::AbcGeom::kWrapExisting; +using Alembic::AbcGeom::P3fArraySamplePtr; +using Alembic::AbcGeom::N3fArraySamplePtr; + +using Alembic::AbcGeom::ICompoundProperty; +using Alembic::AbcGeom::IN3fArrayProperty; +using Alembic::AbcGeom::IPoints; +using Alembic::AbcGeom::IPointsSchema; +using Alembic::AbcGeom::ISampleSelector; + +using Alembic::AbcGeom::OPoints; +using Alembic::AbcGeom::OPointsSchema; + +/* ************************************************************************** */ + +AbcPointsWriter::AbcPointsWriter(Scene *scene, + Object *ob, + AbcTransformWriter *parent, + uint32_t time_sampling, + ExportSettings &settings, + ParticleSystem *psys) + : AbcObjectWriter(scene, ob, time_sampling, settings, parent) +{ + m_psys = psys; + + OPoints points(parent->alembicXform(), m_name, m_time_sampling); + m_schema = points.getSchema(); +} + +void AbcPointsWriter::do_write() +{ + if (!m_psys) { + return; + } + + std::vector<Imath::V3f> points; + std::vector<Imath::V3f> velocities; + std::vector<float> widths; + std::vector<uint64_t> ids; + + ParticleKey state; + + ParticleSimulationData sim; + sim.scene = m_scene; + sim.ob = m_object; + sim.psys = m_psys; + + m_psys->lattice_deform_data = psys_create_lattice_deform_data(&sim); + + uint64_t index = 0; + for (int p = 0; p < m_psys->totpart; p++) { + float pos[3], vel[3]; + + if (m_psys->particles[p].flag & (PARS_NO_DISP | PARS_UNEXIST)) { + continue; + } + + state.time = BKE_scene_frame_get(m_scene); + + if (psys_get_particle_state(&sim, p, &state, 0) == 0) { + continue; + } + + /* location */ + mul_v3_m4v3(pos, m_object->imat, state.co); + + /* velocity */ + sub_v3_v3v3(vel, state.co, m_psys->particles[p].prev_state.co); + + /* Convert Z-up to Y-up. */ + points.push_back(Imath::V3f(pos[0], pos[2], -pos[1])); + velocities.push_back(Imath::V3f(vel[0], vel[2], -vel[1])); + widths.push_back(m_psys->particles[p].size); + ids.push_back(index++); + } + + if (m_psys->lattice_deform_data) { + end_latt_deform(m_psys->lattice_deform_data); + m_psys->lattice_deform_data = NULL; + } + + Alembic::Abc::P3fArraySample psample(points); + Alembic::Abc::UInt64ArraySample idsample(ids); + Alembic::Abc::V3fArraySample vsample(velocities); + Alembic::Abc::FloatArraySample wsample_array(widths); + Alembic::AbcGeom::OFloatGeomParam::Sample wsample(wsample_array, kVertexScope); + + m_sample = OPointsSchema::Sample(psample, idsample, vsample, wsample); + m_sample.setSelfBounds(bounds()); + + m_schema.set(m_sample); +} + +/* ************************************************************************** */ + +AbcPointsReader::AbcPointsReader(const Alembic::Abc::IObject &object, ImportSettings &settings) + : AbcObjectReader(object, settings) +{ + IPoints ipoints(m_iobject, kWrapExisting); + m_schema = ipoints.getSchema(); + get_min_max_time(m_iobject, m_schema, m_min_time, m_max_time); +} + +bool AbcPointsReader::valid() const +{ + return m_schema.valid(); +} + +void AbcPointsReader::readObjectData(Main *bmain, float time) +{ + Mesh *mesh = BKE_mesh_add(bmain, m_data_name.c_str()); + + const ISampleSelector sample_sel(time); + m_sample = m_schema.getValue(sample_sel); + + const P3fArraySamplePtr &positions = m_sample.getPositions(); + utils::mesh_add_verts(mesh, positions->size()); + + CDStreamConfig config = create_config(mesh); + read_points_sample(m_schema, sample_sel, config, time); + + if (m_settings->validate_meshes) { + BKE_mesh_validate(mesh, false, false); + } + + m_object = BKE_object_add_only_object(bmain, OB_MESH, m_object_name.c_str()); + m_object->data = mesh; + + if (has_animations(m_schema, m_settings)) { + addCacheModifier(); + } +} + +void read_points_sample(const IPointsSchema &schema, + const ISampleSelector &selector, + CDStreamConfig &config, + float time) +{ + Alembic::AbcGeom::IPointsSchema::Sample sample = schema.getValue(selector); + + const P3fArraySamplePtr &positions = sample.getPositions(); + + ICompoundProperty prop = schema.getArbGeomParams(); + N3fArraySamplePtr vnormals; + + if (has_property(prop, "N")) { + const IN3fArrayProperty &normals_prop = IN3fArrayProperty(prop, "N", time); + + if (normals_prop) { + vnormals = normals_prop.getValue(selector); + } + } + + read_mverts(config.mvert, positions, vnormals); +} diff --git a/source/blender/alembic/intern/abc_points.h b/source/blender/alembic/intern/abc_points.h new file mode 100644 index 00000000000..51f3103bd8b --- /dev/null +++ b/source/blender/alembic/intern/abc_points.h @@ -0,0 +1,70 @@ +/* + * ***** 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) 2016 Kévin Dietrich. + * All rights reserved. + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +#ifndef __ABC_POINTS_H__ +#define __ABC_POINTS_H__ + +#include "abc_object.h" +#include "abc_customdata.h" + +class ParticleSystem; + +/* ************************************************************************** */ + +class AbcPointsWriter : public AbcObjectWriter { + Alembic::AbcGeom::OPointsSchema m_schema; + Alembic::AbcGeom::OPointsSchema::Sample m_sample; + ParticleSystem *m_psys; + +public: + AbcPointsWriter(Scene *scene, + Object *ob, + AbcTransformWriter *parent, + uint32_t time_sampling, + ExportSettings &settings, + ParticleSystem *psys); + + void do_write(); +}; + +/* ************************************************************************** */ + +class AbcPointsReader : public AbcObjectReader { + Alembic::AbcGeom::IPointsSchema m_schema; + Alembic::AbcGeom::IPointsSchema::Sample m_sample; + +public: + AbcPointsReader(const Alembic::Abc::IObject &object, ImportSettings &settings); + + bool valid() const; + + void readObjectData(Main *bmain, float time); +}; + +void read_points_sample(const Alembic::AbcGeom::IPointsSchema &schema, + const Alembic::AbcGeom::ISampleSelector &selector, + CDStreamConfig &config, + float time); + +#endif /* __ABC_POINTS_H__ */ diff --git a/source/blender/alembic/intern/abc_transform.cc b/source/blender/alembic/intern/abc_transform.cc new file mode 100644 index 00000000000..7f8984f9970 --- /dev/null +++ b/source/blender/alembic/intern/abc_transform.cc @@ -0,0 +1,152 @@ +/* + * ***** 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. + * + * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "abc_transform.h" + +#include <OpenEXR/ImathBoxAlgo.h> + +#include "abc_util.h" + +extern "C" { +#include "DNA_object_types.h" + +#include "BLI_math.h" + +#include "BKE_object.h" +} + +using Alembic::AbcGeom::OObject; +using Alembic::AbcGeom::OXform; + +/* ************************************************************************** */ + +static bool has_parent_camera(Object *ob) +{ + if (!ob->parent) { + return false; + } + + Object *parent = ob->parent; + + if (parent->type == OB_CAMERA) { + return true; + } + + return has_parent_camera(parent); +} + +/* ************************************************************************** */ + +AbcTransformWriter::AbcTransformWriter(Object *ob, + const OObject &abc_parent, + AbcTransformWriter *parent, + unsigned int time_sampling, + ExportSettings &settings) + : AbcObjectWriter(NULL, ob, time_sampling, settings, parent) +{ + m_is_animated = hasAnimation(m_object); + m_parent = NULL; + + if (!m_is_animated) { + time_sampling = 0; + } + + m_xform = OXform(abc_parent, get_id_name(m_object), time_sampling); + m_schema = m_xform.getSchema(); +} + +void AbcTransformWriter::do_write() +{ + if (m_first_frame) { + m_visibility = Alembic::AbcGeom::CreateVisibilityProperty(m_xform, m_xform.getSchema().getTimeSampling()); + } + + m_visibility.set(!(m_object->restrictflag & OB_RESTRICT_VIEW)); + + if (!m_first_frame && !m_is_animated) { + return; + } + + float mat[4][4]; + create_transform_matrix(m_object, mat); + + /* Only apply rotation to root camera, parenting will propagate it. */ + if (m_object->type == OB_CAMERA && !has_parent_camera(m_object)) { + float rot_mat[4][4]; + unit_m4(rot_mat); + rotate_m4(rot_mat, 'X', -M_PI_2); + mul_m4_m4m4(mat, mat, rot_mat); + } + + if (!m_object->parent) { + /* Only apply scaling to root objects, parenting will propagate it. */ + float scale_mat[4][4]; + scale_m4_fl(scale_mat, m_settings.global_scale); + mul_m4_m4m4(mat, mat, scale_mat); + mul_v3_fl(mat[3], m_settings.global_scale); + } + + m_matrix = convert_matrix(mat); + + m_sample.setMatrix(m_matrix); + m_schema.set(m_sample); +} + +Imath::Box3d AbcTransformWriter::bounds() +{ + Imath::Box3d bounds; + + for (int i = 0; i < m_children.size(); ++i) { + Imath::Box3d box(m_children[i]->bounds()); + bounds.extendBy(box); + } + + return Imath::transform(bounds, m_matrix); +} + +bool AbcTransformWriter::hasAnimation(Object */*ob*/) const +{ + /* TODO(kevin): implement this. */ + return true; +} + +/* ************************************************************************** */ + +AbcEmptyReader::AbcEmptyReader(const Alembic::Abc::IObject &object, ImportSettings &settings) + : AbcObjectReader(object, settings) +{ + Alembic::AbcGeom::IXform xform(object, Alembic::AbcGeom::kWrapExisting); + m_schema = xform.getSchema(); + + get_min_max_time(m_iobject, m_schema, m_min_time, m_max_time); +} + +bool AbcEmptyReader::valid() const +{ + return m_schema.valid(); +} + +void AbcEmptyReader::readObjectData(Main *bmain, float /*time*/) +{ + m_object = BKE_object_add_only_object(bmain, OB_EMPTY, m_object_name.c_str()); + m_object->data = NULL; +} diff --git a/source/blender/alembic/intern/abc_transform.h b/source/blender/alembic/intern/abc_transform.h new file mode 100644 index 00000000000..6a3aae216f2 --- /dev/null +++ b/source/blender/alembic/intern/abc_transform.h @@ -0,0 +1,73 @@ +/* + * ***** 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. + * + * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __ABC_TRANSFORM_H__ +#define __ABC_TRANSFORM_H__ + +#include "abc_object.h" + +#include <Alembic/AbcGeom/All.h> + +/* ************************************************************************** */ + +class AbcTransformWriter : public AbcObjectWriter { + Alembic::AbcGeom::OXform m_xform; + Alembic::AbcGeom::OXformSchema m_schema; + Alembic::AbcGeom::XformSample m_sample; + Alembic::AbcGeom::OVisibilityProperty m_visibility; + Alembic::Abc::M44d m_matrix; + + bool m_is_animated; + Object *m_parent; + bool m_visible; + +public: + AbcTransformWriter(Object *ob, + const Alembic::AbcGeom::OObject &abc_parent, + AbcTransformWriter *parent, + unsigned int time_sampling, + ExportSettings &settings); + + Alembic::AbcGeom::OXform &alembicXform() { return m_xform;} + virtual Imath::Box3d bounds(); + void setParent(Object *p) { m_parent = p; } + +private: + virtual void do_write(); + + bool hasAnimation(Object *ob) const; +}; + +/* ************************************************************************** */ + +class AbcEmptyReader : public AbcObjectReader { + Alembic::AbcGeom::IXformSchema m_schema; + +public: + AbcEmptyReader(const Alembic::Abc::IObject &object, ImportSettings &settings); + + bool valid() const; + + void readObjectData(Main *bmain, float time); +}; + +#endif /* __ABC_TRANSFORM_H__ */ diff --git a/source/blender/alembic/intern/abc_util.cc b/source/blender/alembic/intern/abc_util.cc new file mode 100644 index 00000000000..fbab0bcdf34 --- /dev/null +++ b/source/blender/alembic/intern/abc_util.cc @@ -0,0 +1,437 @@ +/* + * ***** 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. + * + * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "abc_util.h" + +#include <algorithm> + +extern "C" { +#include "DNA_object_types.h" + +#include "BLI_math.h" +} + +std::string get_id_name(Object *ob) +{ + if (!ob) { + return ""; + } + + return get_id_name(&ob->id); +} + +std::string get_id_name(ID *id) +{ + std::string name(id->name + 2); + std::replace(name.begin(), name.end(), ' ', '_'); + std::replace(name.begin(), name.end(), '.', '_'); + std::replace(name.begin(), name.end(), ':', '_'); + + return name; +} + +std::string get_object_dag_path_name(Object *ob, Object *dupli_parent) +{ + std::string name = get_id_name(ob); + + Object *p = ob->parent; + + while (p) { + name = get_id_name(p) + "/" + name; + p = p->parent; + } + + if (dupli_parent && (ob != dupli_parent)) { + name = get_id_name(dupli_parent) + "/" + name; + } + + return name; +} + +bool object_selected(Object *ob) +{ + return ob->flag & SELECT; +} + +bool parent_selected(Object *ob) +{ + if (object_selected(ob)) { + return true; + } + + bool do_export = false; + + Object *parent = ob->parent; + + while (parent != NULL) { + if (object_selected(parent)) { + do_export = true; + break; + } + + parent = parent->parent; + } + + return do_export; +} + +Imath::M44d convert_matrix(float mat[4][4]) +{ + Imath::M44d m; + + for (int i = 0; i < 4; ++i) { + for (int j = 0; j < 4; ++j) { + m[i][j] = mat[i][j]; + } + } + + return m; +} + +void split(const std::string &s, const char delim, std::vector<std::string> &tokens) +{ + tokens.clear(); + + std::stringstream ss(s); + std::string item; + + while (std::getline(ss, item, delim)) { + if (!item.empty()) { + tokens.push_back(item); + } + } +} + +/* Create a rotation matrix for each axis from euler angles. + * 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_z_mat[3][3], const float euler[3], const bool to_yup) +{ + const float rx = euler[0]; + const float ry = (to_yup) ? euler[2] : -euler[2]; + const float rz = (to_yup) ? -euler[1] : euler[1]; + + unit_m3(rot_x_mat); + unit_m3(rot_y_mat); + unit_m3(rot_z_mat); + + rot_x_mat[1][1] = cos(rx); + rot_x_mat[2][1] = -sin(rx); + rot_x_mat[1][2] = sin(rx); + rot_x_mat[2][2] = cos(rx); + + rot_y_mat[2][2] = cos(ry); + rot_y_mat[0][2] = -sin(ry); + rot_y_mat[2][0] = sin(ry); + rot_y_mat[0][0] = cos(ry); + + rot_z_mat[0][0] = cos(rz); + rot_z_mat[1][0] = -sin(rz); + rot_z_mat[0][1] = sin(rz); + rot_z_mat[1][1] = cos(rz); +} + +/* Recompute transform matrix of object in new coordinate system + * (from Y-Up to Z-Up). */ +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 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); + + /* Compute rotation matrix. */ + + /* Extract location, rotation, and scale from matrix. */ + mat4_to_loc_rot_size(loc, rot, scale, r_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, false); + + /* 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); + + /* Add rotation matrix to transformation matrix. */ + copy_m4_m3(transform_mat, rot_mat); + + /* Add translation to transformation matrix. */ + copy_yup_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); + + copy_m4_m4(r_mat, transform_mat); +} + +void create_input_transform(const Alembic::AbcGeom::ISampleSelector &sample_sel, + const Alembic::AbcGeom::IXform &ixform, Object *ob, + float r_mat[4][4], float scale, bool has_alembic_parent) +{ + + const Alembic::AbcGeom::IXformSchema &ixform_schema = ixform.getSchema(); + Alembic::AbcGeom::XformSample xs; + ixform_schema.get(xs, sample_sel); + const Imath::M44d &xform = xs.getMatrix(); + + for (int i = 0; i < 4; ++i) { + for (int j = 0; j < 4; ++j) { + r_mat[i][j] = xform[i][j]; + } + } + + if (ob->type == OB_CAMERA) { + float cam_to_yup[4][4]; + unit_m4(cam_to_yup); + rotate_m4(cam_to_yup, 'X', M_PI_2); + mul_m4_m4m4(r_mat, r_mat, cam_to_yup); + } + + create_transform_matrix(r_mat); + + 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). */ +void create_transform_matrix(Object *obj, float transform_mat[4][4]) +{ + float rot_mat[3][3], rot[3][3], scale_mat[4][4], invmat[4][4], 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. */ + if (obj->parent) { + invert_m4_m4(invmat, obj->parent->obmat); + mul_m4_m4m4(mat, invmat, obj->obmat); + } + else { + copy_m4_m4(mat, obj->obmat); + } + + /* 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_zup_yup(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) +{ + if (!prop.valid()) { + return false; + } + + return prop.getPropertyHeader(name) != NULL; +} diff --git a/source/blender/alembic/intern/abc_util.h b/source/blender/alembic/intern/abc_util.h new file mode 100644 index 00000000000..648570f5f27 --- /dev/null +++ b/source/blender/alembic/intern/abc_util.h @@ -0,0 +1,137 @@ +/* + * ***** 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. + * + * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __ABC_UTIL_H__ +#define __ABC_UTIL_H__ + +#include <Alembic/Abc/All.h> +#include <Alembic/AbcGeom/All.h> + +#ifdef _MSC_VER +# define ABC_INLINE static __forceinline +#else +# define ABC_INLINE static inline +#endif + +using Alembic::Abc::chrono_t; + +class ImportSettings; + +struct ID; +struct Object; + +std::string get_id_name(ID *id); +std::string get_id_name(Object *ob); +std::string get_object_dag_path_name(Object *ob, Object *dupli_parent); + +bool object_selected(Object *ob); +bool parent_selected(Object *ob); + +Imath::M44d convert_matrix(float mat[4][4]); +void create_transform_matrix(float r_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); + +template<class TContainer> +bool begins_with(const TContainer &input, const TContainer &match) +{ + return input.size() >= match.size() + && std::equal(match.begin(), match.end(), input.begin()); +} + +void create_input_transform(const Alembic::AbcGeom::ISampleSelector &sample_sel, + const Alembic::AbcGeom::IXform &ixform, Object *ob, + float r_mat[4][4], float scale, bool has_alembic_parent = false); + +template <typename Schema> +void get_min_max_time_ex(const Schema &schema, chrono_t &min, chrono_t &max) +{ + const Alembic::Abc::TimeSamplingPtr &time_samp = schema.getTimeSampling(); + + if (!schema.isConstant()) { + const size_t num_samps = schema.getNumSamples(); + + if (num_samps > 0) { + const chrono_t min_time = time_samp->getSampleTime(0); + min = std::min(min, min_time); + + const chrono_t max_time = time_samp->getSampleTime(num_samps - 1); + max = std::max(max, max_time); + } + } +} + +template <typename Schema> +void get_min_max_time(const Alembic::AbcGeom::IObject &object, const Schema &schema, chrono_t &min, chrono_t &max) +{ + get_min_max_time_ex(schema, min, max); + + const Alembic::AbcGeom::IObject &parent = object.getParent(); + if (parent.valid() && Alembic::AbcGeom::IXform::matches(parent.getMetaData())) { + Alembic::AbcGeom::IXform xform(parent, Alembic::AbcGeom::kWrapExisting); + get_min_max_time_ex(xform.getSchema(), min, max); + } +} + +bool has_property(const Alembic::Abc::ICompoundProperty &prop, const std::string &name); + +/* ************************** */ + +/* TODO(kevin): for now keeping these transformations hardcoded to make sure + * everything works properly, and also because Alembic is almost exclusively + * used in Y-up software, but eventually they'll be set by the user in the UI + * like other importers/exporters do, to support other axis. */ + +/* Copy from Y-up to Z-up. */ + +ABC_INLINE void copy_yup_zup(float zup[3], const float yup[3]) +{ + zup[0] = yup[0]; + zup[1] = -yup[2]; + zup[2] = yup[1]; +} + +ABC_INLINE void copy_yup_zup(short zup[3], const short yup[3]) +{ + zup[0] = yup[0]; + zup[1] = -yup[2]; + zup[2] = yup[1]; +} + +/* Copy from Z-up to Y-up. */ + +ABC_INLINE void copy_zup_yup(float yup[3], const float zup[3]) +{ + yup[0] = zup[0]; + yup[1] = zup[2]; + yup[2] = -zup[1]; +} + +ABC_INLINE void copy_zup_yup(short yup[3], const short zup[3]) +{ + yup[0] = zup[0]; + yup[1] = zup[2]; + yup[2] = -zup[1]; +} + +#endif /* __ABC_UTIL_H__ */ diff --git a/source/blender/alembic/intern/alembic_capi.cc b/source/blender/alembic/intern/alembic_capi.cc new file mode 100644 index 00000000000..d057cc341f6 --- /dev/null +++ b/source/blender/alembic/intern/alembic_capi.cc @@ -0,0 +1,1196 @@ +/* + * ***** 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. + * + * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "../ABC_alembic.h" + +#ifdef WITH_ALEMBIC_HDF5 +# include <Alembic/AbcCoreHDF5/All.h> +#endif + +#include <Alembic/AbcCoreOgawa/All.h> +#include <Alembic/AbcMaterial/IMaterial.h> + +#include <fstream> + +#ifdef WIN32 +# include "utfconv.h" +#endif + +#include "abc_camera.h" +#include "abc_curves.h" +#include "abc_hair.h" +#include "abc_mesh.h" +#include "abc_nurbs.h" +#include "abc_points.h" +#include "abc_transform.h" +#include "abc_util.h" + +extern "C" { +#include "MEM_guardedalloc.h" + +#include "DNA_cachefile_types.h" +#include "DNA_curve_types.h" +#include "DNA_modifier_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "BKE_cachefile.h" +#include "BKE_cdderivedmesh.h" +#include "BKE_context.h" +#include "BKE_curve.h" +#include "BKE_depsgraph.h" +#include "BKE_global.h" +#include "BKE_library.h" +#include "BKE_main.h" +#include "BKE_scene.h" + +/* SpaceType struct has a member called 'new' which obviously conflicts with C++ + * so temporarily redefining the new keyword to make it compile. */ +#define new extern_new +#include "BKE_screen.h" +#undef new + +#include "BLI_fileops.h" +#include "BLI_ghash.h" +#include "BLI_listbase.h" +#include "BLI_math.h" +#include "BLI_path_util.h" +#include "BLI_string.h" + +#include "WM_api.h" +#include "WM_types.h" +} + +using Alembic::Abc::Int32ArraySamplePtr; +using Alembic::Abc::ObjectHeader; + +using Alembic::AbcGeom::ErrorHandler; +using Alembic::AbcGeom::Exception; +using Alembic::AbcGeom::MetaData; +using Alembic::AbcGeom::P3fArraySamplePtr; +using Alembic::AbcGeom::kWrapExisting; + +using Alembic::AbcGeom::IArchive; +using Alembic::AbcGeom::ICamera; +using Alembic::AbcGeom::ICurves; +using Alembic::AbcGeom::ICurvesSchema; +using Alembic::AbcGeom::IFaceSet; +using Alembic::AbcGeom::ILight; +using Alembic::AbcGeom::INuPatch; +using Alembic::AbcGeom::IObject; +using Alembic::AbcGeom::IPoints; +using Alembic::AbcGeom::IPointsSchema; +using Alembic::AbcGeom::IPolyMesh; +using Alembic::AbcGeom::IPolyMeshSchema; +using Alembic::AbcGeom::ISampleSelector; +using Alembic::AbcGeom::ISubD; +using Alembic::AbcGeom::IV2fGeomParam; +using Alembic::AbcGeom::IXform; +using Alembic::AbcGeom::IXformSchema; +using Alembic::AbcGeom::N3fArraySamplePtr; +using Alembic::AbcGeom::XformSample; +using Alembic::AbcGeom::ICompoundProperty; +using Alembic::AbcGeom::IN3fArrayProperty; +using Alembic::AbcGeom::IN3fGeomParam; +using Alembic::AbcGeom::V3fArraySamplePtr; + +using Alembic::AbcMaterial::IMaterial; + +static IArchive open_archive(const std::string &filename, + const std::vector<std::istream *> &input_streams, + bool &is_hdf5) +{ + try { + is_hdf5 = false; + Alembic::AbcCoreOgawa::ReadArchive archive_reader(input_streams); + + return IArchive(archive_reader(filename), + kWrapExisting, + ErrorHandler::kThrowPolicy); + } + catch (const Exception &e) { + std::cerr << e.what() << '\n'; + +#ifdef WITH_ALEMBIC_HDF5 + try { + is_hdf5 = true; + Alembic::AbcCoreAbstract::ReadArraySampleCachePtr cache_ptr; + + return IArchive(Alembic::AbcCoreHDF5::ReadArchive(), + filename.c_str(), ErrorHandler::kThrowPolicy, + cache_ptr); + } + catch (const Exception &) { + std::cerr << e.what() << '\n'; + return IArchive(); + } +#else + return IArchive(); +#endif + } + + return IArchive(); +} + +/* Wrapper around an archive to be able to use streams so that unicode paths + * work on Windows (T49112), and to make sure the input stream remains valid as + * long as the archive is open. */ +class ArchiveWrapper { + IArchive m_archive; + std::ifstream m_infile; + std::vector<std::istream *> m_streams; + +public: + explicit ArchiveWrapper(const char *filename) + { +#ifdef WIN32 + UTF16_ENCODE(filename); + std::wstring wstr(filename_16); + m_infile.open(wstr.c_str(), std::ios::in | std::ios::binary); + UTF16_UN_ENCODE(filename); +#else + m_infile.open(filename, std::ios::in | std::ios::binary); +#endif + + m_streams.push_back(&m_infile); + + bool is_hdf5; + m_archive = open_archive(filename, m_streams, is_hdf5); + + /* We can't open an HDF5 file from a stream, so close it. */ + if (is_hdf5) { + m_infile.close(); + m_streams.clear(); + } + } + + bool valid() const + { + return m_archive.valid(); + } + + IObject getTop() + { + return m_archive.getTop(); + } +}; + +struct AbcArchiveHandle { + int unused; +}; + +ABC_INLINE ArchiveWrapper *archive_from_handle(AbcArchiveHandle *handle) +{ + return reinterpret_cast<ArchiveWrapper *>(handle); +} + +ABC_INLINE AbcArchiveHandle *handle_from_archive(ArchiveWrapper *archive) +{ + return reinterpret_cast<AbcArchiveHandle *>(archive); +} + +//#define USE_NURBS + +/* NOTE: this function is similar to visit_objects below, need to keep them in + * sync. */ +static void gather_objects_paths(const IObject &object, ListBase *object_paths) +{ + if (!object.valid()) { + return; + } + + for (int i = 0; i < object.getNumChildren(); ++i) { + IObject child = object.getChild(i); + + if (!child.valid()) { + continue; + } + + bool get_path = false; + + const MetaData &md = child.getMetaData(); + + if (IXform::matches(md)) { + /* 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")) { + get_path = 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())) { + 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; + } + else { + assert(false); + } + + if (get_path) { + AlembicObjectPath *abc_path = static_cast<AlembicObjectPath *>( + 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); + } +} + +AbcArchiveHandle *ABC_create_handle(const char *filename, ListBase *object_paths) +{ + ArchiveWrapper *archive = new ArchiveWrapper(filename); + + if (!archive->valid()) { + delete archive; + return NULL; + } + + if (object_paths) { + gather_objects_paths(archive->getTop(), object_paths); + } + + return handle_from_archive(archive); +} + +void ABC_free_handle(AbcArchiveHandle *handle) +{ + delete archive_from_handle(handle); +} + +int ABC_get_version() +{ + return ALEMBIC_LIBRARY_VERSION; +} + +static void find_iobject(const IObject &object, IObject &ret, + const std::string &path) +{ + if (!object.valid()) { + return; + } + + std::vector<std::string> tokens; + split(path, '/', tokens); + + IObject tmp = object; + + std::vector<std::string>::iterator iter; + for (iter = tokens.begin(); iter != tokens.end(); ++iter) { + IObject child = tmp.getChild(*iter); + tmp = child; + } + + ret = tmp; +} + +struct ExportJobData { + Scene *scene; + Main *bmain; + + char filename[1024]; + ExportSettings settings; + + short *stop; + short *do_update; + float *progress; + + bool was_canceled; +}; + +static void export_startjob(void *customdata, short *stop, short *do_update, float *progress) +{ + ExportJobData *data = static_cast<ExportJobData *>(customdata); + + data->stop = stop; + data->do_update = do_update; + data->progress = progress; + + /* XXX annoying hack: needed to prevent data corruption when changing + * scene frame in separate threads + */ + G.is_rendering = true; + BKE_spacedata_draw_locks(true); + + G.is_break = false; + + try { + Scene *scene = data->scene; + AbcExporter exporter(scene, data->filename, data->settings); + + const int orig_frame = CFRA; + + data->was_canceled = false; + exporter(data->bmain, *data->progress, data->was_canceled); + + if (CFRA != orig_frame) { + CFRA = orig_frame; + + BKE_scene_update_for_newframe(data->bmain->eval_ctx, data->bmain, + scene, scene->lay); + } + } + catch (const std::exception &e) { + std::cerr << "Abc Export error: " << e.what() << '\n'; + } + catch (...) { + std::cerr << "Abc Export error\n"; + } +} + +static void export_endjob(void *customdata) +{ + ExportJobData *data = static_cast<ExportJobData *>(customdata); + + if (data->was_canceled && BLI_exists(data->filename)) { + BLI_delete(data->filename, false, false); + } + + G.is_rendering = false; + BKE_spacedata_draw_locks(false); +} + +void ABC_export( + Scene *scene, + bContext *C, + const char *filepath, + const struct AlembicExportParams *params) +{ + ExportJobData *job = static_cast<ExportJobData *>(MEM_mallocN(sizeof(ExportJobData), "ExportJobData")); + job->scene = scene; + job->bmain = CTX_data_main(C); + BLI_strncpy(job->filename, filepath, 1024); + + job->settings.scene = job->scene; + job->settings.frame_start = params->frame_start; + job->settings.frame_end = params->frame_end; + job->settings.frame_step_xform = params->frame_step_xform; + job->settings.frame_step_shape = params->frame_step_shape; + job->settings.shutter_open = params->shutter_open; + job->settings.shutter_close = params->shutter_close; + job->settings.selected_only = params->selected_only; + job->settings.export_face_sets = params->face_sets; + job->settings.export_normals = params->normals; + job->settings.export_uvs = params->uvs; + job->settings.export_vcols = params->vcolors; + job->settings.apply_subdiv = params->apply_subdiv; + job->settings.flatten_hierarchy = params->flatten_hierarchy; + job->settings.visible_layers_only = params->visible_layers_only; + job->settings.renderable_only = params->renderable_only; + job->settings.use_subdiv_schema = params->use_subdiv_schema; + job->settings.export_ogawa = (params->compression_type == ABC_ARCHIVE_OGAWA); + job->settings.pack_uv = params->packuv; + job->settings.global_scale = params->global_scale; + + if (job->settings.frame_start > job->settings.frame_end) { + std::swap(job->settings.frame_start, job->settings.frame_end); + } + + wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C), + CTX_wm_window(C), + job->scene, + "Alembic Export", + WM_JOB_PROGRESS, + WM_JOB_TYPE_ALEMBIC); + + /* setup job */ + WM_jobs_customdata_set(wm_job, job, MEM_freeN); + WM_jobs_timer(wm_job, 0.1, NC_SCENE | ND_FRAME, NC_SCENE | ND_FRAME); + WM_jobs_callbacks(wm_job, export_startjob, NULL, NULL, export_endjob); + + WM_jobs_start(CTX_wm_manager(C), wm_job); +} + +/* ********************** Import file ********************** */ + +static void visit_object(const IObject &object, + std::vector<AbcObjectReader *> &readers, + GHash *parent_map, + ImportSettings &settings) +{ + if (!object.valid()) { + return; + } + + for (int i = 0; i < object.getNumChildren(); ++i) { + IObject child = object.getChild(i); + + if (!child.valid()) { + continue; + } + + AbcObjectReader *reader = NULL; + + const MetaData &md = child.getMetaData(); + + 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 { + assert(false); + } + + if (reader) { + readers.push_back(reader); + + 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); + } +} + +enum { + ABC_NO_ERROR = 0, + ABC_ARCHIVE_FAIL, +}; + +struct ImportJobData { + Main *bmain; + Scene *scene; + + char filename[1024]; + ImportSettings settings; + + GHash *parent_map; + std::vector<AbcObjectReader *> readers; + + short *stop; + short *do_update; + float *progress; + + char error_code; + bool was_cancelled; +}; + +ABC_INLINE bool is_mesh_and_strands(const IObject &object) +{ + if (object.getNumChildren() != 2) { + return false; + } + + bool has_mesh = false; + bool has_curve = false; + + for (int i = 0; i < object.getNumChildren(); ++i) { + const IObject &child = object.getChild(i); + + if (!child.valid()) { + continue; + } + + const MetaData &md = child.getMetaData(); + + if (IPolyMesh::matches(md)) { + has_mesh = true; + } + else if (ISubD::matches(md)) { + has_mesh = true; + } + else if (ICurves::matches(md)) { + has_curve = true; + } + } + + return has_mesh && has_curve; +} + +static void import_startjob(void *user_data, short *stop, short *do_update, float *progress) +{ + ImportJobData *data = static_cast<ImportJobData *>(user_data); + + data->stop = stop; + data->do_update = do_update; + data->progress = progress; + + ArchiveWrapper *archive = new ArchiveWrapper(data->filename); + + if (!archive->valid()) { + delete archive; + data->error_code = ABC_ARCHIVE_FAIL; + return; + } + + CacheFile *cache_file = static_cast<CacheFile *>(BKE_cachefile_add(data->bmain, BLI_path_basename(data->filename))); + + /* Decrement the ID ref-count because it is going to be incremented for each + * modifier and constraint that it will be attached to, so since currently + * it is not used by anyone, its use count will off by one. */ + id_us_min(&cache_file->id); + + cache_file->is_sequence = data->settings.is_sequence; + cache_file->scale = data->settings.scale; + cache_file->handle = handle_from_archive(archive); + BLI_strncpy(cache_file->filepath, data->filename, 1024); + + data->settings.cache_file = cache_file; + + *data->do_update = true; + *data->progress = 0.05f; + + data->parent_map = BLI_ghash_str_new("alembic parent ghash"); + + /* Parse Alembic Archive. */ + + visit_object(archive->getTop(), data->readers, data->parent_map, data->settings); + + if (G.is_break) { + data->was_cancelled = true; + return; + } + + *data->do_update = true; + *data->progress = 0.1f; + + /* Create objects and set scene frame range. */ + + const float size = static_cast<float>(data->readers.size()); + size_t i = 0; + + chrono_t min_time = std::numeric_limits<chrono_t>::max(); + chrono_t max_time = std::numeric_limits<chrono_t>::min(); + + std::vector<AbcObjectReader *>::iterator iter; + for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) { + AbcObjectReader *reader = *iter; + + if (reader->valid()) { + reader->readObjectData(data->bmain, 0.0f); + reader->readObjectMatrix(0.0f); + + min_time = std::min(min_time, reader->minTime()); + max_time = std::max(max_time, reader->maxTime()); + } + + *data->progress = 0.1f + 0.6f * (++i / size); + *data->do_update = true; + + if (G.is_break) { + data->was_cancelled = true; + return; + } + } + + if (data->settings.set_frame_range) { + Scene *scene = data->scene; + + if (data->settings.is_sequence) { + SFRA = data->settings.offset; + EFRA = SFRA + (data->settings.sequence_len - 1); + CFRA = SFRA; + } + else if (min_time < max_time) { + SFRA = min_time * FPS; + EFRA = max_time * FPS; + CFRA = SFRA; + } + } + + /* Setup parentship. */ + + i = 0; + for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) { + const AbcObjectReader *reader = *iter; + const AbcObjectReader *parent_reader = NULL; + const IObject &iobject = reader->iobject(); + + IObject parent = iobject.getParent(); + + 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(); + } + } + + parent_reader = reinterpret_cast<AbcObjectReader *>( + 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; + } + } + + *data->progress = 0.7f + 0.3f * (++i / size); + *data->do_update = true; + + if (G.is_break) { + data->was_cancelled = true; + return; + } + } +} + +static void import_endjob(void *user_data) +{ + ImportJobData *data = static_cast<ImportJobData *>(user_data); + + std::vector<AbcObjectReader *>::iterator iter; + + /* Delete objects on cancelation. */ + if (data->was_cancelled) { + for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) { + Object *ob = (*iter)->object(); + + if (ob->data) { + BKE_libblock_free_us(data->bmain, ob->data); + ob->data = NULL; + } + + BKE_libblock_free_us(data->bmain, ob); + } + } + else { + /* Add object to scene. */ + BKE_scene_base_deselect_all(data->scene); + + for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) { + Object *ob = (*iter)->object(); + ob->lay = data->scene->lay; + + BKE_scene_base_add(data->scene, ob); + + DAG_id_tag_update_ex(data->bmain, &ob->id, OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME); + } + + DAG_relations_tag_update(data->bmain); + } + + for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) { + delete *iter; + } + + if (data->parent_map) { + BLI_ghash_free(data->parent_map, NULL, NULL); + } + + switch (data->error_code) { + default: + case ABC_NO_ERROR: + break; + case ABC_ARCHIVE_FAIL: + WM_report(RPT_ERROR, "Could not open Alembic archive for reading! See console for detail."); + break; + } + + WM_main_add_notifier(NC_SCENE | ND_FRAME, data->scene); +} + +static void import_freejob(void *user_data) +{ + ImportJobData *data = static_cast<ImportJobData *>(user_data); + delete data; +} + +void ABC_import(bContext *C, const char *filepath, float scale, bool is_sequence, bool set_frame_range, int sequence_len, int offset, bool validate_meshes) +{ + /* Using new here since MEM_* funcs do not call ctor to properly initialize + * data. */ + ImportJobData *job = new ImportJobData(); + job->bmain = CTX_data_main(C); + job->scene = CTX_data_scene(C); + BLI_strncpy(job->filename, filepath, 1024); + + job->settings.scale = scale; + job->settings.is_sequence = is_sequence; + job->settings.set_frame_range = set_frame_range; + job->settings.sequence_len = sequence_len; + job->settings.offset = offset; + job->settings.validate_meshes = validate_meshes; + job->parent_map = NULL; + job->error_code = ABC_NO_ERROR; + job->was_cancelled = false; + + G.is_break = false; + + wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C), + CTX_wm_window(C), + job->scene, + "Alembic Import", + WM_JOB_PROGRESS, + WM_JOB_TYPE_ALEMBIC); + + /* setup job */ + WM_jobs_customdata_set(wm_job, job, import_freejob); + WM_jobs_timer(wm_job, 0.1, NC_SCENE | ND_FRAME, NC_SCENE | ND_FRAME); + WM_jobs_callbacks(wm_job, import_startjob, NULL, NULL, import_endjob); + + WM_jobs_start(CTX_wm_manager(C), wm_job); +} + +/* ******************************* */ + +void ABC_get_transform(AbcArchiveHandle *handle, Object *ob, const char *object_path, float r_mat[4][4], float time, float scale) +{ + ArchiveWrapper *archive = archive_from_handle(handle); + + if (!archive || !archive->valid()) { + return; + } + + IObject tmp; + find_iobject(archive->getTop(), tmp, object_path); + + IXform ixform; + + if (IXform::matches(tmp.getHeader())) { + ixform = IXform(tmp, kWrapExisting); + } + else { + ixform = IXform(tmp.getParent(), kWrapExisting); + } + + IXformSchema schema = ixform.getSchema(); + + if (!schema.valid()) { + return; + } + + ISampleSelector sample_sel(time); + + create_input_transform(sample_sel, ixform, ob, r_mat, scale); +} + +/* ***************************************** */ + +static bool check_smooth_poly_flag(DerivedMesh *dm) +{ + MPoly *mpolys = dm->getPolyArray(dm); + + for (int i = 0, e = dm->getNumPolys(dm); i < e; ++i) { + MPoly &poly = mpolys[i]; + + if ((poly.flag & ME_SMOOTH) != 0) { + return true; + } + } + + return false; +} + +static void set_smooth_poly_flag(DerivedMesh *dm) +{ + MPoly *mpolys = dm->getPolyArray(dm); + + for (int i = 0, e = dm->getNumPolys(dm); i < e; ++i) { + MPoly &poly = mpolys[i]; + poly.flag |= ME_SMOOTH; + } +} + +static void *add_customdata_cb(void *user_data, const char *name, int data_type) +{ + DerivedMesh *dm = static_cast<DerivedMesh *>(user_data); + CustomDataType cd_data_type = static_cast<CustomDataType>(data_type); + void *cd_ptr = NULL; + + if (ELEM(cd_data_type, CD_MLOOPUV, CD_MLOOPCOL)) { + cd_ptr = CustomData_get_layer_named(dm->getLoopDataLayout(dm), cd_data_type, name); + + if (cd_ptr == NULL) { + cd_ptr = CustomData_add_layer_named(dm->getLoopDataLayout(dm), + cd_data_type, + CD_DEFAULT, + NULL, + dm->getNumLoops(dm), + name); + } + } + + return cd_ptr; +} + +ABC_INLINE CDStreamConfig get_config(DerivedMesh *dm) +{ + CDStreamConfig config; + + config.user_data = dm; + config.mvert = dm->getVertArray(dm); + config.mloop = dm->getLoopArray(dm); + config.mpoly = dm->getPolyArray(dm); + config.totloop = dm->getNumLoops(dm); + config.totpoly = dm->getNumPolys(dm); + config.loopdata = dm->getLoopDataLayout(dm); + config.add_customdata_cb = add_customdata_cb; + + return config; +} + +static DerivedMesh *read_mesh_sample(DerivedMesh *dm, const IObject &iobject, const float time, int read_flag) +{ + IPolyMesh mesh(iobject, kWrapExisting); + IPolyMeshSchema schema = mesh.getSchema(); + ISampleSelector sample_sel(time); + const IPolyMeshSchema::Sample sample = schema.getValue(sample_sel); + + const P3fArraySamplePtr &positions = sample.getPositions(); + const Alembic::Abc::Int32ArraySamplePtr &face_indices = sample.getFaceIndices(); + const Alembic::Abc::Int32ArraySamplePtr &face_counts = sample.getFaceCounts(); + + DerivedMesh *new_dm = NULL; + + /* Only read point data when streaming meshes, unless we need to create new ones. */ + ImportSettings settings; + settings.read_flag |= read_flag; + + if (dm->getNumVerts(dm) != positions->size()) { + new_dm = CDDM_from_template(dm, + positions->size(), + 0, + 0, + face_indices->size(), + face_counts->size()); + + settings.read_flag |= MOD_MESHSEQ_READ_ALL; + } + + CDStreamConfig config = get_config(new_dm ? new_dm : dm); + + bool do_normals = false; + read_mesh_sample(&settings, schema, sample_sel, config, do_normals); + + if (new_dm) { + /* Check if we had ME_SMOOTH flag set to restore it. */ + if (!do_normals && check_smooth_poly_flag(dm)) { + set_smooth_poly_flag(new_dm); + } + + CDDM_calc_normals(new_dm); + CDDM_calc_edges(new_dm); + + return new_dm; + } + + if (do_normals) { + CDDM_calc_normals(dm); + } + + return dm; +} + +using Alembic::AbcGeom::ISubDSchema; + +static DerivedMesh *read_subd_sample(DerivedMesh *dm, const IObject &iobject, const float time, int read_flag) +{ + ISubD mesh(iobject, kWrapExisting); + ISubDSchema schema = mesh.getSchema(); + ISampleSelector sample_sel(time); + const ISubDSchema::Sample sample = schema.getValue(sample_sel); + + const P3fArraySamplePtr &positions = sample.getPositions(); + const Alembic::Abc::Int32ArraySamplePtr &face_indices = sample.getFaceIndices(); + const Alembic::Abc::Int32ArraySamplePtr &face_counts = sample.getFaceCounts(); + + DerivedMesh *new_dm = NULL; + + ImportSettings settings; + settings.read_flag |= read_flag; + + if (dm->getNumVerts(dm) != positions->size()) { + new_dm = CDDM_from_template(dm, + positions->size(), + 0, + 0, + face_indices->size(), + face_counts->size()); + + settings.read_flag |= MOD_MESHSEQ_READ_ALL; + } + + /* Only read point data when streaming meshes, unless we need to create new ones. */ + CDStreamConfig config = get_config(new_dm ? new_dm : dm); + read_subd_sample(&settings, schema, sample_sel, config); + + if (new_dm) { + /* Check if we had ME_SMOOTH flag set to restore it. */ + if (check_smooth_poly_flag(dm)) { + set_smooth_poly_flag(new_dm); + } + + CDDM_calc_normals(new_dm); + CDDM_calc_edges(new_dm); + + return new_dm; + } + + return dm; +} + +static DerivedMesh *read_points_sample(DerivedMesh *dm, const IObject &iobject, const float time) +{ + IPoints points(iobject, kWrapExisting); + IPointsSchema schema = points.getSchema(); + ISampleSelector sample_sel(time); + const IPointsSchema::Sample sample = schema.getValue(sample_sel); + + const P3fArraySamplePtr &positions = sample.getPositions(); + + DerivedMesh *new_dm = NULL; + + if (dm->getNumVerts(dm) != positions->size()) { + new_dm = CDDM_new(positions->size(), 0, 0, 0, 0); + } + + CDStreamConfig config = get_config(new_dm ? new_dm : dm); + read_points_sample(schema, sample_sel, config, time); + + return new_dm ? new_dm : dm; +} + +/* NOTE: Alembic only stores data about control points, but the DerivedMesh + * 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 DerivedMesh from that. Also we might need to + * create new or delete existing NURBS in the curve. + */ +static DerivedMesh *read_curves_sample(Object *ob, const IObject &iobject, const float time) +{ + ICurves points(iobject, kWrapExisting); + ICurvesSchema schema = points.getSchema(); + ISampleSelector sample_sel(time); + const ICurvesSchema::Sample sample = schema.getValue(sample_sel); + + const P3fArraySamplePtr &positions = sample.getPositions(); + const Int32ArraySamplePtr num_vertices = sample.getCurvesNumVertices(); + + int vertex_idx = 0; + int curve_idx = 0; + Curve *curve = static_cast<Curve *>(ob->data); + + const int curve_count = BLI_listbase_count(&curve->nurb); + + if (curve_count != num_vertices->size()) { + BKE_nurbList_free(&curve->nurb); + read_curve_sample(curve, schema, time); + } + else { + Nurb *nurbs = static_cast<Nurb *>(curve->nurb.first); + for (; 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_yup_zup(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_yup_zup(bezier->vec[1], pos.getValue()); + } + } + } + } + + return CDDM_from_curve(ob); +} + +DerivedMesh *ABC_read_mesh(AbcArchiveHandle *handle, + Object *ob, + DerivedMesh *dm, + const char *object_path, + const float time, + const char **err_str, + int read_flag) +{ + ArchiveWrapper *archive = archive_from_handle(handle); + + if (!archive || !archive->valid()) { + *err_str = "Invalid archive!"; + return NULL; + } + + IObject iobject; + find_iobject(archive->getTop(), iobject, object_path); + + if (!iobject.valid()) { + *err_str = "Invalid object: verify object path"; + return NULL; + } + + const ObjectHeader &header = iobject.getHeader(); + + if (IPolyMesh::matches(header)) { + if (ob->type != OB_MESH) { + *err_str = "Object type mismatch: object path points to a mesh!"; + return NULL; + } + + return read_mesh_sample(dm, iobject, time, read_flag); + } + else if (ISubD::matches(header)) { + if (ob->type != OB_MESH) { + *err_str = "Object type mismatch: object path points to a subdivision mesh!"; + return NULL; + } + + return read_subd_sample(dm, iobject, time, read_flag); + } + else if (IPoints::matches(header)) { + if (ob->type != OB_MESH) { + *err_str = "Object type mismatch: object path points to a point cloud (requires a mesh object)!"; + return NULL; + } + + return read_points_sample(dm, iobject, time); + } + else if (ICurves::matches(header)) { + if (ob->type != OB_CURVE) { + *err_str = "Object type mismatch: object path points to a curve!"; + return NULL; + } + + return read_curves_sample(ob, iobject, time); + } + + *err_str = "Unsupported object type: verify object path"; // or poke developer + return NULL; +} diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 483fefbd89c..189340db618 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -27,8 +27,8 @@ /* these lines are grep'd, watch out for our not-so-awesome regex * and keep comment above the defines. * Use STRINGIFY() rather than defining with quotes */ -#define BLENDER_VERSION 277 -#define BLENDER_SUBVERSION 3 +#define BLENDER_VERSION 278 +#define BLENDER_SUBVERSION 0 /* Several breakages with 270, e.g. constraint deg vs rad */ #define BLENDER_MINVERSION 270 #define BLENDER_MINSUBVERSION 6 @@ -37,7 +37,7 @@ /* can be left blank, otherwise a,b,c... etc with no quotes */ #define BLENDER_VERSION_CHAR /* alpha/beta/rc/release, docs use this */ -#define BLENDER_VERSION_CYCLE alpha +#define BLENDER_VERSION_CYCLE rc extern char versionstr[]; /* from blender.c */ diff --git a/source/blender/blenkernel/BKE_cachefile.h b/source/blender/blenkernel/BKE_cachefile.h new file mode 100644 index 00000000000..7a9744ef9d6 --- /dev/null +++ b/source/blender/blenkernel/BKE_cachefile.h @@ -0,0 +1,69 @@ +/* + * ***** 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) 2016 Blender Foundation. + * All rights reserved. + * + * Contributor(s): Kevin Dietrich. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __BKE_CACHEFILE_H__ +#define __BKE_CACHEFILE_H__ + +/** \file BKE_cachefile.h + * \ingroup bke + */ + +#ifdef __cplusplus +extern "C" { +#endif + +struct CacheFile; +struct Main; +struct Scene; + +void BKE_cachefiles_init(void); + +void *BKE_cachefile_add(struct Main *bmain, const char *name); + +void BKE_cachefile_init(struct CacheFile *cache_file); + +void BKE_cachefile_free(struct CacheFile *cache_file); + +struct CacheFile *BKE_cachefile_copy(struct Main *bmain, struct CacheFile *cache_file); + +void BKE_cachefile_make_local(struct Main *bmain, struct CacheFile *cache_file, const bool lib_local); + +void BKE_cachefile_reload(const struct Main *bmain, struct CacheFile *cache_file); + +void BKE_cachefile_ensure_handle(const struct Main *bmain, struct CacheFile *cache_file); + +void BKE_cachefile_update_frame(struct Main *bmain, struct Scene *scene, float ctime, const float fps); + +bool BKE_cachefile_filepath_get( + const struct Main *bmain, const struct CacheFile *cache_file, float frame, + char r_filename[1024]); + +float BKE_cachefile_time_offset(struct CacheFile *cache_file, const float time, const float fps); + +#ifdef __cplusplus +} +#endif + +#endif /* __BKE_CACHEFILE_H__ */ diff --git a/source/blender/blenkernel/BKE_collision.h b/source/blender/blenkernel/BKE_collision.h index d5b4a584ec6..8fedcd4ab06 100644 --- a/source/blender/blenkernel/BKE_collision.h +++ b/source/blender/blenkernel/BKE_collision.h @@ -146,6 +146,10 @@ void collision_get_collider_velocity(float vel_old[3], float vel_new[3], struct ///////////////////////////////////////////////// // used in effect.c ///////////////////////////////////////////////// + +/* explicit control over layer mask and dupli recursion */ +struct Object **get_collisionobjects_ext(struct Scene *scene, struct Object *self, struct Group *group, int layer, unsigned int *numcollobj, unsigned int modifier_type, bool dupli); + struct Object **get_collisionobjects(struct Scene *scene, struct Object *self, struct Group *group, unsigned int *numcollobj, unsigned int modifier_type); typedef struct ColliderCache { diff --git a/source/blender/blenkernel/BKE_context.h b/source/blender/blenkernel/BKE_context.h index 30ac310c2df..4da6a61cbfa 100644 --- a/source/blender/blenkernel/BKE_context.h +++ b/source/blender/blenkernel/BKE_context.h @@ -39,6 +39,7 @@ extern "C" { struct ARegion; struct bScreen; +struct CacheFile; struct ListBase; struct Main; struct Object; @@ -271,6 +272,8 @@ struct Text *CTX_data_edit_text(const bContext *C); struct MovieClip *CTX_data_edit_movieclip(const bContext *C); struct Mask *CTX_data_edit_mask(const bContext *C); +struct CacheFile *CTX_data_edit_cachefile(const bContext *C); + int CTX_data_selected_nodes(const bContext *C, ListBase *list); struct EditBone *CTX_data_active_bone(const bContext *C); diff --git a/source/blender/blenkernel/BKE_effect.h b/source/blender/blenkernel/BKE_effect.h index f8fee444d91..b934ec7166d 100644 --- a/source/blender/blenkernel/BKE_effect.h +++ b/source/blender/blenkernel/BKE_effect.h @@ -110,7 +110,7 @@ typedef struct EffectorCache { } EffectorCache; void free_partdeflect(struct PartDeflect *pd); -struct ListBase *pdInitEffectors(struct Scene *scene, struct Object *ob_src, struct ParticleSystem *psys_src, struct EffectorWeights *weights, bool precalc); +struct ListBase *pdInitEffectors(struct Scene *scene, struct Object *ob_src, struct ParticleSystem *psys_src, struct EffectorWeights *weights, bool for_simulation); void pdEndEffectors(struct ListBase **effectors); void pdPrecalculateEffectors(struct ListBase *effectors); void pdDoEffectors(struct ListBase *effectors, struct ListBase *colliders, struct EffectorWeights *weights, struct EffectedPoint *point, float *force, float *impulse); diff --git a/source/blender/blenkernel/BKE_gpencil.h b/source/blender/blenkernel/BKE_gpencil.h index cbf167de25c..ab8b83f8271 100644 --- a/source/blender/blenkernel/BKE_gpencil.h +++ b/source/blender/blenkernel/BKE_gpencil.h @@ -43,36 +43,36 @@ struct Main; /* ------------ Grease-Pencil API ------------------ */ -void free_gpencil_stroke(struct bGPDstroke *gps); -bool free_gpencil_strokes(struct bGPDframe *gpf); -void free_gpencil_frames(struct bGPDlayer *gpl); -void free_gpencil_layers(struct ListBase *list); -void free_gpencil_brushes(struct ListBase *list); -void free_gpencil_palettes(struct ListBase *list); +void BKE_gpencil_free_stroke(struct bGPDstroke *gps); +bool BKE_gpencil_free_strokes(struct bGPDframe *gpf); +void BKE_gpencil_free_frames(struct bGPDlayer *gpl); +void BKE_gpencil_free_layers(struct ListBase *list); +void BKE_gpencil_free_brushes(struct ListBase *list); +void BKE_gpencil_free_palettes(struct ListBase *list); void BKE_gpencil_free(struct bGPdata *gpd, bool free_palettes); -void gpencil_stroke_sync_selection(struct bGPDstroke *gps); +void BKE_gpencil_stroke_sync_selection(struct bGPDstroke *gps); -struct bGPDframe *gpencil_frame_addnew(struct bGPDlayer *gpl, int cframe); -struct bGPDframe *gpencil_frame_addcopy(struct bGPDlayer *gpl, int cframe); -struct bGPDlayer *gpencil_layer_addnew(struct bGPdata *gpd, const char *name, bool setactive); -struct bGPdata *gpencil_data_addnew(const char name[]); +struct bGPDframe *BKE_gpencil_frame_addnew(struct bGPDlayer *gpl, int cframe); +struct bGPDframe *BKE_gpencil_frame_addcopy(struct bGPDlayer *gpl, int cframe); +struct bGPDlayer *BKE_gpencil_layer_addnew(struct bGPdata *gpd, const char *name, bool setactive); +struct bGPdata *BKE_gpencil_data_addnew(const char name[]); -struct bGPDframe *gpencil_frame_duplicate(const struct bGPDframe *gpf_src); -struct bGPDlayer *gpencil_layer_duplicate(const struct bGPDlayer *gpl_src); -struct bGPdata *gpencil_data_duplicate(struct Main *bmain, struct bGPdata *gpd, bool internal_copy); +struct bGPDframe *BKE_gpencil_frame_duplicate(const struct bGPDframe *gpf_src); +struct bGPDlayer *BKE_gpencil_layer_duplicate(const struct bGPDlayer *gpl_src); +struct bGPdata *BKE_gpencil_data_duplicate(struct Main *bmain, struct bGPdata *gpd, bool internal_copy); void BKE_gpencil_make_local(struct Main *bmain, struct bGPdata *gpd, const bool lib_local); -void gpencil_frame_delete_laststroke(struct bGPDlayer *gpl, struct bGPDframe *gpf); +void BKE_gpencil_frame_delete_laststroke(struct bGPDlayer *gpl, struct bGPDframe *gpf); -struct bGPDpalette *gpencil_palette_addnew(struct bGPdata *gpd, const char *name, bool setactive); -struct bGPDpalette *gpencil_palette_duplicate(const struct bGPDpalette *palette_src); -struct bGPDpalettecolor *gpencil_palettecolor_addnew(struct bGPDpalette *palette, const char *name, bool setactive); +struct bGPDpalette *BKE_gpencil_palette_addnew(struct bGPdata *gpd, const char *name, bool setactive); +struct bGPDpalette *BKE_gpencil_palette_duplicate(const struct bGPDpalette *palette_src); +struct bGPDpalettecolor *BKE_gpencil_palettecolor_addnew(struct bGPDpalette *palette, const char *name, bool setactive); -struct bGPDbrush *gpencil_brush_addnew(struct ToolSettings *ts, const char *name, bool setactive); -struct bGPDbrush *gpencil_brush_duplicate(const struct bGPDbrush *brush_src); -void gpencil_brush_init_presets(struct ToolSettings *ts); +struct bGPDbrush *BKE_gpencil_brush_addnew(struct ToolSettings *ts, const char *name, bool setactive); +struct bGPDbrush *BKE_gpencil_brush_duplicate(const struct bGPDbrush *brush_src); +void BKE_gpencil_brush_init_presets(struct ToolSettings *ts); /* Stroke and Fill - Alpha Visibility Threshold */ @@ -94,28 +94,28 @@ typedef enum eGP_GetFrame_Mode { GP_GETFRAME_ADD_COPY = 2 } eGP_GetFrame_Mode; -struct bGPDframe *gpencil_layer_getframe(struct bGPDlayer *gpl, int cframe, eGP_GetFrame_Mode addnew); +struct bGPDframe *BKE_gpencil_layer_getframe(struct bGPDlayer *gpl, int cframe, eGP_GetFrame_Mode addnew); struct bGPDframe *BKE_gpencil_layer_find_frame(struct bGPDlayer *gpl, int cframe); -bool gpencil_layer_delframe(struct bGPDlayer *gpl, struct bGPDframe *gpf); - -struct bGPDlayer *gpencil_layer_getactive(struct bGPdata *gpd); -void gpencil_layer_setactive(struct bGPdata *gpd, struct bGPDlayer *active); -void gpencil_layer_delete(struct bGPdata *gpd, struct bGPDlayer *gpl); - -struct bGPDbrush *gpencil_brush_getactive(struct ToolSettings *ts); -void gpencil_brush_setactive(struct ToolSettings *ts, struct bGPDbrush *active); -void gpencil_brush_delete(struct ToolSettings *ts, struct bGPDbrush *palette); - -struct bGPDpalette *gpencil_palette_getactive(struct bGPdata *gpd); -void gpencil_palette_setactive(struct bGPdata *gpd, struct bGPDpalette *active); -void gpencil_palette_delete(struct bGPdata *gpd, struct bGPDpalette *palette); -void gpencil_palette_change_strokes(struct bGPdata *gpd); - -struct bGPDpalettecolor *gpencil_palettecolor_getactive(struct bGPDpalette *palette); -void gpencil_palettecolor_setactive(struct bGPDpalette *palette, struct bGPDpalettecolor *active); -void gpencil_palettecolor_delete(struct bGPDpalette *palette, struct bGPDpalettecolor *palcolor); -struct bGPDpalettecolor *gpencil_palettecolor_getbyname(struct bGPDpalette *palette, char *name); -void gpencil_palettecolor_changename(struct bGPdata *gpd, char *oldname, const char *newname); -void gpencil_palettecolor_delete_strokes(struct bGPdata *gpd, char *name); +bool BKE_gpencil_layer_delframe(struct bGPDlayer *gpl, struct bGPDframe *gpf); + +struct bGPDlayer *BKE_gpencil_layer_getactive(struct bGPdata *gpd); +void BKE_gpencil_layer_setactive(struct bGPdata *gpd, struct bGPDlayer *active); +void BKE_gpencil_layer_delete(struct bGPdata *gpd, struct bGPDlayer *gpl); + +struct bGPDbrush *BKE_gpencil_brush_getactive(struct ToolSettings *ts); +void BKE_gpencil_brush_setactive(struct ToolSettings *ts, struct bGPDbrush *active); +void BKE_gpencil_brush_delete(struct ToolSettings *ts, struct bGPDbrush *palette); + +struct bGPDpalette *BKE_gpencil_palette_getactive(struct bGPdata *gpd); +void BKE_gpencil_palette_setactive(struct bGPdata *gpd, struct bGPDpalette *active); +void BKE_gpencil_palette_delete(struct bGPdata *gpd, struct bGPDpalette *palette); +void BKE_gpencil_palette_change_strokes(struct bGPdata *gpd); + +struct bGPDpalettecolor *BKE_gpencil_palettecolor_getactive(struct bGPDpalette *palette); +void BKE_gpencil_palettecolor_setactive(struct bGPDpalette *palette, struct bGPDpalettecolor *active); +void BKE_gpencil_palettecolor_delete(struct bGPDpalette *palette, struct bGPDpalettecolor *palcolor); +struct bGPDpalettecolor *BKE_gpencil_palettecolor_getbyname(struct bGPDpalette *palette, char *name); +void BKE_gpencil_palettecolor_changename(struct bGPdata *gpd, char *oldname, const char *newname); +void BKE_gpencil_palettecolor_delete_strokes(struct bGPdata *gpd, char *name); #endif /* __BKE_GPENCIL_H__ */ diff --git a/source/blender/blenkernel/BKE_idcode.h b/source/blender/blenkernel/BKE_idcode.h index 6de0efe2709..964a49435f1 100644 --- a/source/blender/blenkernel/BKE_idcode.h +++ b/source/blender/blenkernel/BKE_idcode.h @@ -42,6 +42,8 @@ bool BKE_idcode_is_valid(short idcode); int BKE_idcode_to_idfilter(const short idcode); short BKE_idcode_from_idfilter(const int idfilter); +int BKE_idcode_to_index(const short idcode); + /** * Return an ID code and steps the index forward 1. * diff --git a/source/blender/blenkernel/BKE_library.h b/source/blender/blenkernel/BKE_library.h index e32bc2ffb20..e49019fcfae 100644 --- a/source/blender/blenkernel/BKE_library.h +++ b/source/blender/blenkernel/BKE_library.h @@ -94,7 +94,7 @@ void id_clear_lib_data_ex(struct Main *bmain, struct ID *id, const bool id_in_ma struct ListBase *which_libbase(struct Main *mainlib, short type); -#define MAX_LIBARRAY 34 +#define MAX_LIBARRAY 35 int set_listbasepointers(struct Main *main, struct ListBase *lb[MAX_LIBARRAY]); /* Main API */ diff --git a/source/blender/blenkernel/BKE_main.h b/source/blender/blenkernel/BKE_main.h index 44e4da4e0a3..a4f5c425282 100644 --- a/source/blender/blenkernel/BKE_main.h +++ b/source/blender/blenkernel/BKE_main.h @@ -42,6 +42,8 @@ */ #include "DNA_listBase.h" +#include "BKE_library.h" + #ifdef __cplusplus extern "C" { #endif @@ -102,8 +104,9 @@ typedef struct Main { ListBase movieclip; ListBase mask; ListBase linestyle; + ListBase cachefiles; - char id_tag_update[256]; + char id_tag_update[MAX_LIBARRAY]; /* Evaluation context used by viewport */ struct EvaluationContext *eval_ctx; diff --git a/source/blender/blenkernel/BKE_material.h b/source/blender/blenkernel/BKE_material.h index df739996c54..8ae5c2b3c45 100644 --- a/source/blender/blenkernel/BKE_material.h +++ b/source/blender/blenkernel/BKE_material.h @@ -49,7 +49,7 @@ void BKE_material_free(struct Material *ma); void BKE_material_free_ex(struct Material *ma, bool do_id_user); void test_object_materials(struct Object *ob, struct ID *id); void test_all_objects_materials(struct Main *bmain, struct ID *id); -void BKE_material_resize_object(struct Object *ob, const short totcol, bool do_id_user); +void BKE_material_resize_object(struct Main *bmain, struct Object *ob, const short totcol, bool do_id_user); void BKE_material_init(struct Material *ma); void BKE_material_remap_object(struct Object *ob, const unsigned int *remap); void BKE_material_remap_object_calc(struct Object *ob_dst, struct Object *ob_src, short *remap_src_to_dst); @@ -90,10 +90,10 @@ void BKE_texpaint_slot_refresh_cache(struct Scene *scene, struct Material *ma); void BKE_texpaint_slots_refresh_object(struct Scene *scene, struct Object *ob); /* rna api */ -void BKE_material_resize_id(struct ID *id, short totcol, bool do_id_user); -void BKE_material_append_id(struct ID *id, struct Material *ma); -struct Material *BKE_material_pop_id(struct ID *id, int index, bool update_data); /* index is an int because of RNA */ -void BKE_material_clear_id(struct ID *id, bool update_data); +void BKE_material_resize_id(struct Main *bmain, struct ID *id, short totcol, bool do_id_user); +void BKE_material_append_id(struct Main *bmain, struct ID *id, struct Material *ma); +struct Material *BKE_material_pop_id(struct Main *bmain, struct ID *id, int index, bool update_data); /* index is an int because of RNA */ +void BKE_material_clear_id(struct Main *bmain, struct ID *id, bool update_data); /* rendering */ void init_render_material(struct Material *, int, float *); diff --git a/source/blender/blenkernel/BKE_particle.h b/source/blender/blenkernel/BKE_particle.h index 37831728e6f..b3e3968ca9b 100644 --- a/source/blender/blenkernel/BKE_particle.h +++ b/source/blender/blenkernel/BKE_particle.h @@ -63,6 +63,8 @@ struct BVHTreeRay; struct BVHTreeRayHit; struct EdgeHash; +#define PARTICLE_COLLISION_MAX_COLLISIONS 10 + #define PARTICLE_P ParticleData * pa; int p #define LOOP_PARTICLES for (p = 0, pa = psys->particles; p < psys->totpart; p++, pa++) #define LOOP_EXISTING_PARTICLES for (p = 0, pa = psys->particles; p < psys->totpart; p++, pa++) if (!(pa->flag & PARS_UNEXIST)) @@ -205,8 +207,7 @@ typedef struct ParticleCollisionElement { typedef struct ParticleCollision { struct Object *current; struct Object *hit; - struct Object *prev; - struct Object *skip; + struct Object *skip[PARTICLE_COLLISION_MAX_COLLISIONS+1]; struct Object *emitter; struct CollisionModifierData *md; // collision modifier for current object; @@ -218,7 +219,7 @@ typedef struct ParticleCollision { float original_ray_length; //original length of co2-co1, needed for collision time evaluation - int prev_index; + int skip_count; ParticleCollisionElement pce; diff --git a/source/blender/blenkernel/BKE_pointcache.h b/source/blender/blenkernel/BKE_pointcache.h index 8238ea64242..02f6c435ee2 100644 --- a/source/blender/blenkernel/BKE_pointcache.h +++ b/source/blender/blenkernel/BKE_pointcache.h @@ -304,7 +304,7 @@ void BKE_ptcache_mem_pointers_incr(struct PTCacheMem *pm); int BKE_ptcache_mem_pointers_seek(int point_index, struct PTCacheMem *pm); /* Main cache reading call. */ -int BKE_ptcache_read(PTCacheID *pid, float cfra); +int BKE_ptcache_read(PTCacheID *pid, float cfra, bool no_extrapolate_old); /* Main cache writing call. */ int BKE_ptcache_write(PTCacheID *pid, unsigned int cfra); diff --git a/source/blender/blenkernel/BKE_sequencer.h b/source/blender/blenkernel/BKE_sequencer.h index 30bb6954019..811e9136fc9 100644 --- a/source/blender/blenkernel/BKE_sequencer.h +++ b/source/blender/blenkernel/BKE_sequencer.h @@ -477,4 +477,6 @@ struct ImBuf *BKE_sequencer_render_mask_input( int cfra, int fra_offset, bool make_float); void BKE_sequencer_color_balance_apply(struct StripColorBalance *cb, struct ImBuf *ibuf, float mul, bool make_float, struct ImBuf *mask_input); +void BKE_sequencer_all_free_anim_ibufs(int cfra); + #endif /* __BKE_SEQUENCER_H__ */ diff --git a/source/blender/blenkernel/BKE_tracking.h b/source/blender/blenkernel/BKE_tracking.h index 1938bb08395..30873567297 100644 --- a/source/blender/blenkernel/BKE_tracking.h +++ b/source/blender/blenkernel/BKE_tracking.h @@ -277,9 +277,9 @@ void BKE_tracking_detect_harris(struct MovieTracking *tracking, struct ListBase bool place_outside_layer); /* **** 2D stabilization **** */ -void BKE_tracking_stabilization_data_get(struct MovieTracking *tracking, int framenr, int width, int height, +void BKE_tracking_stabilization_data_get(struct MovieClip *clip, int framenr, int width, int height, float translation[2], float *scale, float *angle); -struct ImBuf *BKE_tracking_stabilize_frame(struct MovieTracking *tracking, int framenr, struct ImBuf *ibuf, +struct ImBuf *BKE_tracking_stabilize_frame(struct MovieClip *clip, int framenr, struct ImBuf *ibuf, float translation[2], float *scale, float *angle); void BKE_tracking_stabilization_data_to_mat4(int width, int height, float aspect, float translation[2], float scale, float angle, float mat[4][4]); diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index b7ff81d603b..157c4408d6a 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -81,6 +81,7 @@ set(SRC intern/brush.c intern/bullet.c intern/bvhutils.c + intern/cachefile.c intern/camera.c intern/cdderivedmesh.c intern/cloth.c @@ -207,6 +208,7 @@ set(SRC BKE_brush.h BKE_bullet.h BKE_bvhutils.h + BKE_cachefile.h BKE_camera.h BKE_ccg.h BKE_cdderivedmesh.h @@ -500,6 +502,13 @@ if(WITH_FREESTYLE) add_definitions(-DWITH_FREESTYLE) endif() +if(WITH_ALEMBIC) + list(APPEND INC + ../alembic + ) + add_definitions(-DWITH_ALEMBIC) +endif() + if(WITH_OPENSUBDIV) add_definitions(-DWITH_OPENSUBDIV) list(APPEND INC_SYS diff --git a/source/blender/blenkernel/depsgraph_private.h b/source/blender/blenkernel/depsgraph_private.h index 7b3199efb41..69ca75836d9 100644 --- a/source/blender/blenkernel/depsgraph_private.h +++ b/source/blender/blenkernel/depsgraph_private.h @@ -34,6 +34,11 @@ #include "DNA_constraint_types.h" #include "BKE_constraint.h" +struct Scene; +struct Group; +struct EffectorWeights; +struct ModifierData; + /* **** DAG relation types *** */ /* scene link to object */ @@ -152,6 +157,11 @@ DagNode *dag_get_node(DagForest *forest, void *fob); DagNode *dag_get_sub_node(DagForest *forest, void *fob); void dag_add_relation(DagForest *forest, DagNode *fob1, DagNode *fob2, short rel, const char *name); +typedef bool (*DagCollobjFilterFunction)(struct Object *obj, struct ModifierData *md); + +void dag_add_collision_relations(DagForest *dag, struct Scene *scene, Object *ob, DagNode *node, struct Group *group, int layer, unsigned int modifier_type, DagCollobjFilterFunction fn, bool dupli, const char *name); +void dag_add_forcefield_relations(DagForest *dag, struct Scene *scene, Object *ob, DagNode *node, struct EffectorWeights *eff, bool add_absorption, int skip_forcefield, const char *name); + void graph_print_queue(DagNodeQueue *nqueue); void graph_print_queue_dist(DagNodeQueue *nqueue); void graph_print_adj_list(DagForest *dag); diff --git a/source/blender/blenkernel/intern/DerivedMesh.c b/source/blender/blenkernel/intern/DerivedMesh.c index 79e500f8ceb..714417ae29e 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.c +++ b/source/blender/blenkernel/intern/DerivedMesh.c @@ -2282,7 +2282,7 @@ static void editbmesh_calc_modifiers( { ModifierData *md, *previewmd = NULL; float (*deformedVerts)[3] = NULL; - CustomDataMask mask, previewmask = 0, append_mask = 0; + CustomDataMask mask = 0, previewmask = 0, append_mask = 0; DerivedMesh *dm = NULL, *orcodm = NULL; int i, numVerts = 0, cageIndex = modifiers_getCageIndex(scene, ob, NULL, 1); CDMaskLink *datamasks, *curr; @@ -2565,11 +2565,15 @@ static void editbmesh_calc_modifiers( * we'll be using GPU backend of OpenSubdiv. This is so * playback performance is kept as high as possible. */ -static bool calc_modifiers_skip_orco(const Object *ob) +static bool calc_modifiers_skip_orco(Scene *scene, + const Object *ob, + bool use_render_params) { - const ModifierData *last_md = ob->modifiers.last; + ModifierData *last_md = ob->modifiers.last; + const int required_mode = use_render_params ? eModifierMode_Render : eModifierMode_Realtime; if (last_md != NULL && - last_md->type == eModifierType_Subsurf) + last_md->type == eModifierType_Subsurf && + modifier_isEnabled(scene, last_md, required_mode)) { SubsurfModifierData *smd = (SubsurfModifierData *)last_md; /* TODO(sergey): Deduplicate this with checks from subsurf_ccg.c. */ @@ -2589,7 +2593,7 @@ static void mesh_build_data( BKE_object_sculpt_modifiers_changed(ob); #ifdef WITH_OPENSUBDIV - if (calc_modifiers_skip_orco(ob)) { + if (calc_modifiers_skip_orco(scene, ob, false)) { dataMask &= ~(CD_MASK_ORCO | CD_MASK_PREVIEW_MCOL); } #endif @@ -2624,7 +2628,7 @@ static void editbmesh_build_data(Scene *scene, Object *obedit, BMEditMesh *em, C BKE_editmesh_free_derivedmesh(em); #ifdef WITH_OPENSUBDIV - if (calc_modifiers_skip_orco(obedit)) { + if (calc_modifiers_skip_orco(scene, obedit, false)) { dataMask &= ~(CD_MASK_ORCO | CD_MASK_PREVIEW_MCOL); } #endif diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c index 477c7036762..e3764adb969 100644 --- a/source/blender/blenkernel/intern/anim_sys.c +++ b/source/blender/blenkernel/intern/anim_sys.c @@ -97,6 +97,7 @@ bool id_type_can_have_animdata(const short id_type) case ID_MC: case ID_MSK: case ID_GD: + case ID_CF: return true; /* no AnimData */ @@ -1160,6 +1161,9 @@ void BKE_animdata_main_cb(Main *mainptr, ID_AnimData_Edit_Callback func, void *u /* grease pencil */ ANIMDATA_IDS_CB(mainptr->gpencil.first); + + /* cache files */ + ANIMDATA_IDS_CB(mainptr->cachefiles.first); } /* Fix all RNA-Paths throughout the database (directly access the Global.main version) @@ -1250,6 +1254,9 @@ void BKE_animdata_fix_paths_rename_all(ID *ref_id, const char *prefix, const cha /* grease pencil */ RENAMEFIX_ANIM_IDS(mainptr->gpencil.first); + + /* cache files */ + RENAMEFIX_ANIM_IDS(mainptr->cachefiles.first); /* scenes */ RENAMEFIX_ANIM_NODETREE_IDS(mainptr->scene.first, Scene); @@ -1506,7 +1513,7 @@ static bool animsys_store_rna_setting( if (array_len && array_index >= array_len) { if (G.debug & G_DEBUG) { printf("Animato: Invalid array index. ID = '%s', '%s[%d]', array length is %d\n", - (ptr && ptr->id.data) ? (((ID *)ptr->id.data)->name + 2) : "<No ID>", + (ptr->id.data) ? (((ID *)ptr->id.data)->name + 2) : "<No ID>", path, array_index, array_len - 1); } } @@ -2873,6 +2880,9 @@ void BKE_animsys_evaluate_all_animation(Main *main, Scene *scene, float ctime) /* grease pencil */ EVAL_ANIM_IDS(main->gpencil.first, ADT_RECALC_ANIM); + + /* cache files */ + EVAL_ANIM_IDS(main->cachefiles.first, ADT_RECALC_ANIM); /* objects */ /* ADT_RECALC_ANIM doesn't need to be supplied here, since object AnimData gets diff --git a/source/blender/blenkernel/intern/armature_update.c b/source/blender/blenkernel/intern/armature_update.c index aebd564ca58..3bc81a69c86 100644 --- a/source/blender/blenkernel/intern/armature_update.c +++ b/source/blender/blenkernel/intern/armature_update.c @@ -617,9 +617,9 @@ void BKE_pose_eval_bone(EvaluationContext *UNUSED(eval_ctx), /* pass */ } else { - /* TODO(sergey): Use time source node for time. */ - float ctime = BKE_scene_frame_get(scene); /* not accurate... */ if ((pchan->flag & POSE_DONE) == 0) { + /* TODO(sergey): Use time source node for time. */ + float ctime = BKE_scene_frame_get(scene); /* not accurate... */ BKE_pose_where_is_bone(scene, ob, pchan, ctime, 1); } } @@ -641,8 +641,8 @@ void BKE_pose_constraints_evaluate(EvaluationContext *UNUSED(eval_ctx), /* IK are being solved separately/ */ } else { - float ctime = BKE_scene_frame_get(scene); /* not accurate... */ if ((pchan->flag & POSE_DONE) == 0) { + float ctime = BKE_scene_frame_get(scene); /* not accurate... */ BKE_pose_where_is_bone(scene, ob, pchan, ctime, 1); } } diff --git a/source/blender/blenkernel/intern/bpath.c b/source/blender/blenkernel/intern/bpath.c index a708c59fa97..487b8ffa2b5 100644 --- a/source/blender/blenkernel/intern/bpath.c +++ b/source/blender/blenkernel/intern/bpath.c @@ -48,6 +48,7 @@ #include "MEM_guardedalloc.h" #include "DNA_brush_types.h" +#include "DNA_cachefile_types.h" #include "DNA_image_types.h" #include "DNA_mesh_types.h" #include "DNA_modifier_types.h" @@ -653,6 +654,12 @@ void BKE_bpath_traverse_id(Main *bmain, ID *id, BPathVisitor visit_cb, const int rewrite_path_fixed(clip->name, visit_cb, absbase, bpath_user_data); break; } + case ID_CF: + { + CacheFile *cache_file = (CacheFile *)id; + rewrite_path_fixed(cache_file->filepath, visit_cb, absbase, bpath_user_data); + break; + } default: /* Nothing to do for other IDs that don't contain file paths. */ break; diff --git a/source/blender/blenkernel/intern/cachefile.c b/source/blender/blenkernel/intern/cachefile.c new file mode 100644 index 00000000000..502f1d53ab2 --- /dev/null +++ b/source/blender/blenkernel/intern/cachefile.c @@ -0,0 +1,193 @@ +/* + * ***** 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) 2016 Blender Foundation. + * All rights reserved. + * + * Contributor(s): Kevin Dietrich. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/blenkernel/intern/cachefile.c + * \ingroup bke + */ + +#include "DNA_anim_types.h" +#include "DNA_cachefile_types.h" +#include "DNA_scene_types.h" + +#include "BLI_fileops.h" +#include "BLI_listbase.h" +#include "BLI_path_util.h" +#include "BLI_string.h" +#include "BLI_threads.h" +#include "BLI_utildefines.h" + +#include "BKE_animsys.h" +#include "BKE_cachefile.h" +#include "BKE_global.h" +#include "BKE_library.h" +#include "BKE_main.h" +#include "BKE_scene.h" + +#ifdef WITH_ALEMBIC +# include "ABC_alembic.h" +#endif + +static SpinLock spin; + +void BKE_cachefiles_init(void) +{ + BLI_spin_init(&spin); +} + +void *BKE_cachefile_add(Main *bmain, const char *name) +{ + CacheFile *cache_file = BKE_libblock_alloc(bmain, ID_CF, name); + + BKE_cachefile_init(cache_file); + + return cache_file; +} + +void BKE_cachefile_init(CacheFile *cache_file) +{ + cache_file->handle = NULL; + cache_file->filepath[0] = '\0'; + cache_file->override_frame = false; + cache_file->frame = 0.0f; + cache_file->is_sequence = false; + cache_file->scale = 1.0f; + cache_file->handle_mutex = BLI_mutex_alloc(); +} + +/** Free (or release) any data used by this cachefile (does not free the cachefile itself). */ +void BKE_cachefile_free(CacheFile *cache_file) +{ + BKE_animdata_free((ID *)cache_file, false); + +#ifdef WITH_ALEMBIC + ABC_free_handle(cache_file->handle); +#endif + + BLI_mutex_free(cache_file->handle_mutex); + BLI_freelistN(&cache_file->object_paths); +} + +CacheFile *BKE_cachefile_copy(Main *bmain, CacheFile *cache_file) +{ + CacheFile *new_cache_file = BKE_libblock_copy(bmain, &cache_file->id); + new_cache_file->handle = NULL; + + BLI_listbase_clear(&cache_file->object_paths); + + BKE_id_copy_ensure_local(bmain, &cache_file->id, &new_cache_file->id); + + return new_cache_file; +} + +void BKE_cachefile_make_local(Main *bmain, CacheFile *cache_file, const bool lib_local) +{ + BKE_id_make_local_generic(bmain, &cache_file->id, true, lib_local); +} + +void BKE_cachefile_reload(const Main *bmain, CacheFile *cache_file) +{ + char filepath[FILE_MAX]; + + BLI_strncpy(filepath, cache_file->filepath, sizeof(filepath)); + BLI_path_abs(filepath, ID_BLEND_PATH(bmain, &cache_file->id)); + +#ifdef WITH_ALEMBIC + if (cache_file->handle) { + ABC_free_handle(cache_file->handle); + } + + cache_file->handle = ABC_create_handle(filepath, &cache_file->object_paths); +#endif +} + +void BKE_cachefile_ensure_handle(const Main *bmain, CacheFile *cache_file) +{ + BLI_spin_lock(&spin); + if (cache_file->handle_mutex == NULL) { + cache_file->handle_mutex = BLI_mutex_alloc(); + } + BLI_spin_unlock(&spin); + + BLI_mutex_lock(cache_file->handle_mutex); + + if (cache_file->handle == NULL) { + BKE_cachefile_reload(bmain, cache_file); + } + + BLI_mutex_unlock(cache_file->handle_mutex); +} + +void BKE_cachefile_update_frame(Main *bmain, Scene *scene, const float ctime, const float fps) +{ + CacheFile *cache_file; + char filename[FILE_MAX]; + + for (cache_file = bmain->cachefiles.first; cache_file; cache_file = cache_file->id.next) { + /* Execute drivers only, as animation has already been done. */ + BKE_animsys_evaluate_animdata(scene, &cache_file->id, cache_file->adt, ctime, ADT_RECALC_DRIVERS); + + if (!cache_file->is_sequence) { + continue; + } + + const float time = BKE_cachefile_time_offset(cache_file, ctime, fps); + + if (BKE_cachefile_filepath_get(bmain, cache_file, time, filename)) { +#ifdef WITH_ALEMBIC + ABC_free_handle(cache_file->handle); + cache_file->handle = ABC_create_handle(filename, NULL); +#endif + } + } +} + +bool BKE_cachefile_filepath_get( + const Main *bmain, const CacheFile *cache_file, float frame, + char r_filepath[FILE_MAX]) +{ + BLI_strncpy(r_filepath, cache_file->filepath, FILE_MAX); + BLI_path_abs(r_filepath, ID_BLEND_PATH(bmain, &cache_file->id)); + + int fframe; + int frame_len; + + if (cache_file->is_sequence && BLI_path_frame_get(r_filepath, &fframe, &frame_len)) { + char ext[32]; + BLI_path_frame_strip(r_filepath, true, ext); + BLI_path_frame(r_filepath, frame, frame_len); + BLI_ensure_extension(r_filepath, FILE_MAX, ext); + + /* TODO(kevin): store sequence range? */ + return BLI_exists(r_filepath); + } + + return true; +} + +float BKE_cachefile_time_offset(CacheFile *cache_file, const float time, const float fps) +{ + const float frame = (cache_file->override_frame ? cache_file->frame : time); + return cache_file->is_sequence ? frame : frame / fps; +} diff --git a/source/blender/blenkernel/intern/cdderivedmesh.c b/source/blender/blenkernel/intern/cdderivedmesh.c index 159d5b82a6c..2d06bc88aa7 100644 --- a/source/blender/blenkernel/intern/cdderivedmesh.c +++ b/source/blender/blenkernel/intern/cdderivedmesh.c @@ -687,20 +687,24 @@ static void cdDM_drawMappedFaces( const int orig = (index_mp_to_orig) ? index_mp_to_orig[i] : i; bool is_hidden; - if (use_hide) { - if (flag & DM_DRAW_SELECT_USE_EDITMODE) { - BMFace *efa = BM_face_at_index(bm, orig); - is_hidden = BM_elem_flag_test(efa, BM_ELEM_HIDDEN) != 0; + if (orig != ORIGINDEX_NONE) { + if (use_hide) { + if (flag & DM_DRAW_SELECT_USE_EDITMODE) { + BMFace *efa = BM_face_at_index(bm, orig); + is_hidden = BM_elem_flag_test(efa, BM_ELEM_HIDDEN) != 0; + } + else { + is_hidden = (me->mpoly[orig].flag & ME_HIDE) != 0; + } + + if (!is_hidden) { + GPU_select_index_get(orig + 1, &selcol); + } } else { - is_hidden = (me->mpoly[orig].flag & ME_HIDE) != 0; - } - - if ((orig != ORIGINDEX_NONE) && !is_hidden) GPU_select_index_get(orig + 1, &selcol); + } } - else if (orig != ORIGINDEX_NONE) - GPU_select_index_get(orig + 1, &selcol); for (j = 0; j < mpoly->totloop; j++) fi_map[start_element++] = selcol; @@ -3402,7 +3406,7 @@ void CDDM_calc_edges(DerivedMesh *dm) BLI_edgehashIterator_getKey(ehi, &med->v1, &med->v2); j = GET_INT_FROM_POINTER(BLI_edgehashIterator_getValue(ehi)); - if (j == 0) { + if (j == 0 || !eindex) { med->flag = ME_EDGEDRAW | ME_EDGERENDER; *index = ORIGINDEX_NONE; } diff --git a/source/blender/blenkernel/intern/cloth.c b/source/blender/blenkernel/intern/cloth.c index 681b93172b2..28ef3f6f248 100644 --- a/source/blender/blenkernel/intern/cloth.c +++ b/source/blender/blenkernel/intern/cloth.c @@ -81,8 +81,10 @@ void cloth_init(ClothModifierData *clmd ) clmd->sim_parms->gravity[1] = 0.0; clmd->sim_parms->gravity[2] = -9.81; clmd->sim_parms->structural = 15.0; + clmd->sim_parms->max_struct = 15.0; clmd->sim_parms->shear = 15.0; clmd->sim_parms->bending = 0.5; + clmd->sim_parms->max_bend = 0.5; clmd->sim_parms->bending_damping = 0.5; clmd->sim_parms->Cdis = 5.0; clmd->sim_parms->Cvi = 1.0; @@ -449,9 +451,12 @@ void clothModifier_do(ClothModifierData *clmd, Scene *scene, Object *ob, Derived } /* try to read from cache */ - cache_result = BKE_ptcache_read(&pid, (float)framenr+scene->r.subframe); + bool can_simulate = (framenr == clmd->clothObject->last_frame+1) && !(cache->flag & PTCACHE_BAKED); - if (cache_result == PTCACHE_READ_EXACT || cache_result == PTCACHE_READ_INTERPOLATED) { + cache_result = BKE_ptcache_read(&pid, (float)framenr+scene->r.subframe, can_simulate); + + if (cache_result == PTCACHE_READ_EXACT || cache_result == PTCACHE_READ_INTERPOLATED || + (!can_simulate && cache_result == PTCACHE_READ_OLD)) { BKE_cloth_solver_set_positions(clmd); cloth_to_object (ob, clmd, vertexCos); @@ -473,7 +478,7 @@ void clothModifier_do(ClothModifierData *clmd, Scene *scene, Object *ob, Derived return; } - if (framenr!=clmd->clothObject->last_frame+1) + if (!can_simulate) return; /* if on second frame, write cache for first frame */ diff --git a/source/blender/blenkernel/intern/collision.c b/source/blender/blenkernel/intern/collision.c index 8cac856b560..35a7aafdbde 100644 --- a/source/blender/blenkernel/intern/collision.c +++ b/source/blender/blenkernel/intern/collision.c @@ -503,12 +503,13 @@ static void add_collision_object(Object ***objs, unsigned int *numobj, unsigned // return all collision objects in scene // collision object will exclude self -Object **get_collisionobjects(Scene *scene, Object *self, Group *group, unsigned int *numcollobj, unsigned int modifier_type) +Object **get_collisionobjects_ext(Scene *scene, Object *self, Group *group, int layer, unsigned int *numcollobj, unsigned int modifier_type, bool dupli) { Base *base; Object **objs; GroupObject *go; unsigned int numobj= 0, maxobj= 100; + int level = dupli ? 0 : 1; objs= MEM_callocN(sizeof(Object *)*maxobj, "CollisionObjectsArray"); @@ -516,16 +517,14 @@ Object **get_collisionobjects(Scene *scene, Object *self, Group *group, unsigned if (group) { /* use specified group */ for (go= group->gobject.first; go; go= go->next) - add_collision_object(&objs, &numobj, &maxobj, go->ob, self, 0, modifier_type); + add_collision_object(&objs, &numobj, &maxobj, go->ob, self, level, modifier_type); } else { Scene *sce_iter; /* add objects in same layer in scene */ for (SETLOOPER(scene, sce_iter, base)) { - /* Need to check for active layers, too. - Otherwise this check fails if the objects are not on the same layer - DG */ - if ((base->lay & self->lay) || (base->lay & scene->lay)) - add_collision_object(&objs, &numobj, &maxobj, base->object, self, 0, modifier_type); + if ( base->lay & layer ) + add_collision_object(&objs, &numobj, &maxobj, base->object, self, level, modifier_type); } } @@ -535,6 +534,13 @@ Object **get_collisionobjects(Scene *scene, Object *self, Group *group, unsigned return objs; } +Object **get_collisionobjects(Scene *scene, Object *self, Group *group, unsigned int *numcollobj, unsigned int modifier_type) +{ + /* Need to check for active layers, too. + Otherwise this check fails if the objects are not on the same layer - DG */ + return get_collisionobjects_ext(scene, self, group, self->lay | scene->lay, numcollobj, modifier_type, true); +} + static void add_collider_cache_object(ListBase **objs, Object *ob, Object *self, int level) { CollisionModifierData *cmd= NULL; diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c index 4c9ddd495e3..c4afa58b7d3 100644 --- a/source/blender/blenkernel/intern/constraint.c +++ b/source/blender/blenkernel/intern/constraint.c @@ -46,6 +46,7 @@ #include "BLT_translation.h" #include "DNA_armature_types.h" +#include "DNA_cachefile_types.h" #include "DNA_constraint_types.h" #include "DNA_modifier_types.h" #include "DNA_object_types.h" @@ -63,6 +64,7 @@ #include "BKE_anim.h" /* for the curve calculation part */ #include "BKE_armature.h" #include "BKE_bvhutils.h" +#include "BKE_cachefile.h" #include "BKE_camera.h" #include "BKE_constraint.h" #include "BKE_curve.h" @@ -86,6 +88,10 @@ # include "BPY_extern.h" #endif +#ifdef WITH_ALEMBIC +# include "ABC_alembic.h" +#endif + /* ---------------------------------------------------------------------------- */ /* Useful macros for testing various common flag combinations */ @@ -4333,6 +4339,84 @@ static bConstraintTypeInfo CTI_OBJECTSOLVER = { objectsolver_evaluate /* evaluate */ }; +/* ----------- Transform Cache ------------- */ + +static void transformcache_id_looper(bConstraint *con, ConstraintIDFunc func, void *userdata) +{ + bTransformCacheConstraint *data = con->data; + func(con, (ID **)&data->cache_file, true, userdata); +} + +static void transformcache_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *targets) +{ +#ifdef WITH_ALEMBIC + bTransformCacheConstraint *data = con->data; + Scene *scene = cob->scene; + + CacheFile *cache_file = data->cache_file; + + if (!cache_file) { + return; + } + + const float frame = BKE_scene_frame_get(scene); + const float time = BKE_cachefile_time_offset(cache_file, frame, FPS); + + BKE_cachefile_ensure_handle(G.main, cache_file); + + ABC_get_transform(cache_file->handle, cob->ob, data->object_path, + cob->matrix, time, cache_file->scale); +#else + UNUSED_VARS(con, cob); +#endif + + UNUSED_VARS(targets); +} + +static void transformcache_copy(bConstraint *con, bConstraint *srccon) +{ + bTransformCacheConstraint *src = srccon->data; + bTransformCacheConstraint *dst = con->data; + + BLI_strncpy(dst->object_path, src->object_path, sizeof(dst->object_path)); + dst->cache_file = src->cache_file; + + if (dst->cache_file) { + id_us_plus(&dst->cache_file->id); + } +} + +static void transformcache_free(bConstraint *con) +{ + bTransformCacheConstraint *data = con->data; + + if (data->cache_file) { + id_us_min(&data->cache_file->id); + } +} + +static void transformcache_new_data(void *cdata) +{ + bTransformCacheConstraint *data = (bTransformCacheConstraint *)cdata; + + data->cache_file = NULL; +} + +static bConstraintTypeInfo CTI_TRANSFORM_CACHE = { + CONSTRAINT_TYPE_TRANSFORM_CACHE, /* type */ + sizeof(bTransformCacheConstraint), /* size */ + "Transform Cache", /* name */ + "bTransformCacheConstraint", /* struct name */ + transformcache_free, /* free data */ + transformcache_id_looper, /* id looper */ + transformcache_copy, /* copy data */ + transformcache_new_data, /* new data */ + NULL, /* get constraint targets */ + NULL, /* flush constraint targets */ + NULL, /* get target matrix */ + transformcache_evaluate /* evaluate */ +}; + /* ************************* Constraints Type-Info *************************** */ /* All of the constraints api functions use bConstraintTypeInfo structs to carry out * and operations that involve constraint specific code. @@ -4374,6 +4458,7 @@ static void constraints_init_typeinfo(void) constraintsTypeInfo[26] = &CTI_FOLLOWTRACK; /* Follow Track Constraint */ constraintsTypeInfo[27] = &CTI_CAMERASOLVER; /* Camera Solver Constraint */ constraintsTypeInfo[28] = &CTI_OBJECTSOLVER; /* Object Solver Constraint */ + constraintsTypeInfo[29] = &CTI_TRANSFORM_CACHE; /* Transform Cache Constraint */ } /* This function should be used for getting the appropriate type-info when only diff --git a/source/blender/blenkernel/intern/context.c b/source/blender/blenkernel/intern/context.c index 9d5a95df838..926ca8da192 100644 --- a/source/blender/blenkernel/intern/context.c +++ b/source/blender/blenkernel/intern/context.c @@ -1067,6 +1067,11 @@ struct EditBone *CTX_data_active_bone(const bContext *C) return ctx_data_pointer_get(C, "active_bone"); } +struct CacheFile *CTX_data_edit_cachefile(const bContext *C) +{ + return ctx_data_pointer_get(C, "edit_cachefile"); +} + int CTX_data_selected_bones(const bContext *C, ListBase *list) { return ctx_data_collection_get(C, "selected_bones", list); diff --git a/source/blender/blenkernel/intern/depsgraph.c b/source/blender/blenkernel/intern/depsgraph.c index 8ea65bf92c1..5f8332dcf0c 100644 --- a/source/blender/blenkernel/intern/depsgraph.c +++ b/source/blender/blenkernel/intern/depsgraph.c @@ -46,6 +46,7 @@ #include "DNA_anim_types.h" #include "DNA_camera_types.h" +#include "DNA_cachefile_types.h" #include "DNA_group_types.h" #include "DNA_lamp_types.h" #include "DNA_lattice_types.h" @@ -58,14 +59,18 @@ #include "DNA_windowmanager_types.h" #include "DNA_movieclip_types.h" #include "DNA_mask_types.h" +#include "DNA_modifier_types.h" +#include "DNA_rigidbody_types.h" #include "BKE_anim.h" #include "BKE_animsys.h" #include "BKE_action.h" #include "BKE_DerivedMesh.h" +#include "BKE_collision.h" #include "BKE_effect.h" #include "BKE_fcurve.h" #include "BKE_global.h" +#include "BKE_idcode.h" #include "BKE_image.h" #include "BKE_key.h" #include "BKE_library.h" @@ -448,49 +453,51 @@ static void dag_add_lamp_driver_relations(DagForest *dag, DagNode *node, Lamp *l la->id.tag &= ~LIB_TAG_DOIT; } -static void check_and_create_collision_relation(DagForest *dag, Object *ob, DagNode *node, Object *ob1, int skip_forcefield, bool no_collision) +static void create_collision_relation(DagForest *dag, DagNode *node, Object *ob1, const char *name) { - DagNode *node2; - if (ob1->pd && (ob1->pd->deflect || ob1->pd->forcefield) && (ob1 != ob)) { - if ((skip_forcefield && ob1->pd->forcefield == skip_forcefield) || (no_collision && ob1->pd->forcefield == 0)) - return; - node2 = dag_get_node(dag, ob1); - dag_add_relation(dag, node2, node, DAG_RL_DATA_DATA | DAG_RL_OB_DATA, "Field Collision"); + DagNode *node2 = dag_get_node(dag, ob1); + dag_add_relation(dag, node2, node, DAG_RL_DATA_DATA | DAG_RL_OB_DATA, name); +} + +void dag_add_collision_relations(DagForest *dag, Scene *scene, Object *ob, DagNode *node, Group *group, int layer, unsigned int modifier_type, DagCollobjFilterFunction fn, bool dupli, const char *name) +{ + unsigned int numcollobj; + Object **collobjs = get_collisionobjects_ext(scene, ob, group, layer, &numcollobj, modifier_type, dupli); + + for (unsigned int i = 0; i < numcollobj; i++) { + Object *ob1 = collobjs[i]; + + if (!fn || fn(ob1, modifiers_findByType(ob1, modifier_type))) { + create_collision_relation(dag, node, ob1, name); + } } + + if (collobjs) + MEM_freeN(collobjs); } -static void dag_add_collision_field_relation(DagForest *dag, Scene *scene, Object *ob, DagNode *node, int skip_forcefield, bool no_collision) +void dag_add_forcefield_relations(DagForest *dag, Scene *scene, Object *ob, DagNode *node, EffectorWeights *effector_weights, bool add_absorption, int skip_forcefield, const char *name) { - Base *base; - ParticleSystem *particle_system; + ListBase *effectors = pdInitEffectors(scene, ob, NULL, effector_weights, false); - for (particle_system = ob->particlesystem.first; - particle_system; - particle_system = particle_system->next) - { - EffectorWeights *effector_weights = particle_system->part->effector_weights; - if (effector_weights->group) { - GroupObject *group_object; + if (effectors) { + for (EffectorCache *eff = effectors->first; eff; eff = eff->next) { + if (eff->ob != ob && eff->pd->forcefield != skip_forcefield) { + create_collision_relation(dag, node, eff->ob, name); - for (group_object = effector_weights->group->gobject.first; - group_object; - group_object = group_object->next) - { - if ((group_object->ob->lay & ob->lay)) { - check_and_create_collision_relation(dag, ob, node, group_object->ob, skip_forcefield, no_collision); + if (eff->pd->forcefield == PFIELD_SMOKEFLOW && eff->pd->f_source) { + create_collision_relation(dag, node, eff->pd->f_source, "Smoke Force Domain"); + } + + if (add_absorption && (eff->pd->flag & PFIELD_VISIBILITY)) { + /* Actual code uses get_collider_cache */ + dag_add_collision_relations(dag, scene, ob, node, NULL, eff->ob->lay, eModifierType_Collision, NULL, true, "Force Absorption"); } } } } - /* would be nice to have a list of colliders here - * so for now walk all objects in scene check 'same layer rule' */ - for (base = scene->base.first; base; base = base->next) { - if ((base->lay & ob->lay)) { - Object *ob1 = base->object; - check_and_create_collision_relation(dag, ob, node, ob1, skip_forcefield, no_collision); - } - } + pdEndEffectors(&effectors); } static void build_dag_object(DagForest *dag, DagNode *scenenode, Main *bmain, Scene *scene, Object *ob, int mask) @@ -641,23 +648,13 @@ static void build_dag_object(DagForest *dag, DagNode *scenenode, Main *bmain, Sc } } - /* softbody collision */ + /* rigidbody force fields */ if ((ob->type == OB_MESH) || (ob->type == OB_CURVE) || (ob->type == OB_LATTICE)) { - if (ob->particlesystem.first || - modifiers_isModifierEnabled(ob, eModifierType_Softbody) || - modifiers_isModifierEnabled(ob, eModifierType_Cloth) || - modifiers_isModifierEnabled(ob, eModifierType_DynamicPaint)) - { - dag_add_collision_field_relation(dag, scene, ob, node, 0, false); /* TODO: use effectorweight->group */ - } - else if (modifiers_isModifierEnabled(ob, eModifierType_Smoke)) { - dag_add_collision_field_relation(dag, scene, ob, node, PFIELD_SMOKEFLOW, false); - } - else if (ob->rigidbody_object) { - dag_add_collision_field_relation(dag, scene, ob, node, 0, true); + if (ob->rigidbody_object && scene->rigidbody_world) { + dag_add_forcefield_relations(dag, scene, ob, node, scene->rigidbody_world->effector_weights, true, 0, "Force Field"); } } - + /* object data drivers */ if (ob->data) { AnimData *adt = BKE_animdata_from_id((ID *)ob->data); @@ -761,8 +758,6 @@ static void build_dag_object(DagForest *dag, DagNode *scenenode, Main *bmain, Sc BoidRule *rule = NULL; BoidState *state = NULL; ParticleSettings *part = psys->part; - ListBase *effectors = NULL; - EffectorCache *eff; if (part->adt) { dag_add_driver_relation(part->adt, dag, node, 1); @@ -801,18 +796,12 @@ static void build_dag_object(DagForest *dag, DagNode *scenenode, Main *bmain, Sc } } - effectors = pdInitEffectors(scene, ob, psys, part->effector_weights, false); - - if (effectors) { - for (eff = effectors->first; eff; eff = eff->next) { - if (eff->psys) { - node2 = dag_get_node(dag, eff->ob); - dag_add_relation(dag, node2, node, DAG_RL_DATA_DATA | DAG_RL_OB_DATA, "Particle Field"); - } - } + if (part->type != PART_HAIR) { + /* Actual code uses get_collider_cache */ + dag_add_collision_relations(dag, scene, ob, node, part->collision_group, ob->lay, eModifierType_Collision, NULL, true, "Particle Collision"); } - pdEndEffectors(&effectors); + dag_add_forcefield_relations(dag, scene, ob, node, part->effector_weights, part->type == PART_HAIR, 0, "Particle Force Field"); if (part->boids) { for (state = part->boids->states.first; state; state = state->next) { @@ -2173,7 +2162,12 @@ static void dag_object_time_update_flags(Main *bmain, Scene *scene, Object *ob) if (cti) { /* special case for camera tracking -- it doesn't use targets to define relations */ - if (ELEM(cti->type, CONSTRAINT_TYPE_FOLLOWTRACK, CONSTRAINT_TYPE_CAMERASOLVER, CONSTRAINT_TYPE_OBJECTSOLVER)) { + if (ELEM(cti->type, + CONSTRAINT_TYPE_FOLLOWTRACK, + CONSTRAINT_TYPE_CAMERASOLVER, + CONSTRAINT_TYPE_OBJECTSOLVER, + CONSTRAINT_TYPE_TRANSFORM_CACHE)) + { ob->recalc |= OB_RECALC_OB; } else if (cti->get_constraint_targets) { @@ -2799,9 +2793,7 @@ void DAG_ids_flush_tagged(Main *bmain) ListBase *lb = lbarray[a]; ID *id = lb->first; - /* we tag based on first ID type character to avoid - * looping over all ID's in case there are no tags */ - if (id && bmain->id_tag_update[id->name[0]]) { + if (id && bmain->id_tag_update[BKE_idcode_to_index(GS(id->name))]) { for (; id; id = id->next) { if (id->tag & (LIB_TAG_ID_RECALC | LIB_TAG_ID_RECALC_DATA)) { @@ -2841,9 +2833,7 @@ void DAG_ids_check_recalc(Main *bmain, Scene *scene, bool time) ListBase *lb = lbarray[a]; ID *id = lb->first; - /* we tag based on first ID type character to avoid - * looping over all ID's in case there are no tags */ - if (id && bmain->id_tag_update[id->name[0]]) { + if (id && bmain->id_tag_update[BKE_idcode_to_index(GS(id->name))]) { updated = true; break; } @@ -2924,9 +2914,7 @@ void DAG_ids_clear_recalc(Main *bmain) ListBase *lb = lbarray[a]; ID *id = lb->first; - /* we tag based on first ID type character to avoid - * looping over all ID's in case there are no tags */ - if (id && bmain->id_tag_update[id->name[0]]) { + if (id && bmain->id_tag_update[BKE_idcode_to_index(GS(id->name))]) { for (; id; id = id->next) { if (id->tag & (LIB_TAG_ID_RECALC | LIB_TAG_ID_RECALC_DATA)) id->tag &= ~(LIB_TAG_ID_RECALC | LIB_TAG_ID_RECALC_DATA); @@ -3001,6 +2989,33 @@ void DAG_id_tag_update_ex(Main *bmain, ID *id, short flag) /* BLI_assert(!"invalid flag for this 'idtype'"); */ } } + else if (GS(id->name) == ID_CF) { + for (Object *ob = bmain->object.first; ob; ob = ob->id.next) { + ModifierData *md = modifiers_findByType(ob, eModifierType_MeshSequenceCache); + + if (md) { + MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *)md; + + if (mcmd->cache_file && (&mcmd->cache_file->id == id)) { + ob->recalc |= OB_RECALC_DATA; + continue; + } + } + + for (bConstraint *con = ob->constraints.first; con; con = con->next) { + if (con->type != CONSTRAINT_TYPE_TRANSFORM_CACHE) { + continue; + } + + bTransformCacheConstraint *data = con->data; + + if (data->cache_file && (&data->cache_file->id == id)) { + ob->recalc |= OB_RECALC_DATA; + break; + } + } + } + } } void DAG_id_tag_update(ID *id, short flag) @@ -3020,12 +3035,12 @@ void DAG_id_type_tag(Main *bmain, short idtype) DAG_id_type_tag(bmain, ID_SCE); } - bmain->id_tag_update[((char *)&idtype)[0]] = 1; + bmain->id_tag_update[BKE_idcode_to_index(idtype)] = 1; } int DAG_id_type_tagged(Main *bmain, short idtype) { - return bmain->id_tag_update[((char *)&idtype)[0]]; + return bmain->id_tag_update[BKE_idcode_to_index(idtype)]; } #if 0 // UNUSED diff --git a/source/blender/blenkernel/intern/dynamicpaint.c b/source/blender/blenkernel/intern/dynamicpaint.c index d35de6fc5d3..24425720bd2 100644 --- a/source/blender/blenkernel/intern/dynamicpaint.c +++ b/source/blender/blenkernel/intern/dynamicpaint.c @@ -2015,11 +2015,13 @@ static void dynamicPaint_frameUpdate(DynamicPaintModifierData *pmd, Scene *scene } /* try to read from cache */ - if (BKE_ptcache_read(&pid, (float)scene->r.cfra)) { + bool can_simulate = ((int)scene->r.cfra == current_frame) && !(cache->flag & PTCACHE_BAKED); + + if (BKE_ptcache_read(&pid, (float)scene->r.cfra, can_simulate)) { BKE_ptcache_validate(cache, (int)scene->r.cfra); } /* if read failed and we're on surface range do recalculate */ - else if ((int)scene->r.cfra == current_frame && !(cache->flag & PTCACHE_BAKED)) { + else if (can_simulate) { /* calculate surface frame */ canvas->flags |= MOD_DPAINT_BAKING; dynamicPaint_calculateFrame(surface, scene, ob, current_frame); diff --git a/source/blender/blenkernel/intern/effect.c b/source/blender/blenkernel/intern/effect.c index 5090e46f1fc..7e6897a2858 100644 --- a/source/blender/blenkernel/intern/effect.c +++ b/source/blender/blenkernel/intern/effect.c @@ -155,15 +155,20 @@ static EffectorCache *new_effector_cache(Scene *scene, Object *ob, ParticleSyste eff->frame = -1; return eff; } -static void add_object_to_effectors(ListBase **effectors, Scene *scene, EffectorWeights *weights, Object *ob, Object *ob_src) +static void add_object_to_effectors(ListBase **effectors, Scene *scene, EffectorWeights *weights, Object *ob, Object *ob_src, bool for_simulation) { EffectorCache *eff = NULL; - if ( ob == ob_src || weights->weight[ob->pd->forcefield] == 0.0f ) + if ( ob == ob_src ) return; - if (ob->pd->shape == PFIELD_SHAPE_POINTS && !ob->derivedFinal ) - return; + if (for_simulation) { + if (weights->weight[ob->pd->forcefield] == 0.0f ) + return; + + if (ob->pd->shape == PFIELD_SHAPE_POINTS && !ob->derivedFinal ) + return; + } if (*effectors == NULL) *effectors = MEM_callocN(sizeof(ListBase), "effectors list"); @@ -175,7 +180,7 @@ static void add_object_to_effectors(ListBase **effectors, Scene *scene, Effector BLI_addtail(*effectors, eff); } -static void add_particles_to_effectors(ListBase **effectors, Scene *scene, EffectorWeights *weights, Object *ob, ParticleSystem *psys, ParticleSystem *psys_src) +static void add_particles_to_effectors(ListBase **effectors, Scene *scene, EffectorWeights *weights, Object *ob, ParticleSystem *psys, ParticleSystem *psys_src, bool for_simulation) { ParticleSettings *part= psys->part; @@ -185,14 +190,14 @@ static void add_particles_to_effectors(ListBase **effectors, Scene *scene, Effec if ( psys == psys_src && (part->flag & PART_SELF_EFFECT) == 0) return; - if ( part->pd && part->pd->forcefield && weights->weight[part->pd->forcefield] != 0.0f) { + if ( part->pd && part->pd->forcefield && (!for_simulation || weights->weight[part->pd->forcefield] != 0.0f)) { if (*effectors == NULL) *effectors = MEM_callocN(sizeof(ListBase), "effectors list"); BLI_addtail(*effectors, new_effector_cache(scene, ob, psys, part->pd)); } - if (part->pd2 && part->pd2->forcefield && weights->weight[part->pd2->forcefield] != 0.0f) { + if (part->pd2 && part->pd2->forcefield && (!for_simulation || weights->weight[part->pd2->forcefield] != 0.0f)) { if (*effectors == NULL) *effectors = MEM_callocN(sizeof(ListBase), "effectors list"); @@ -202,7 +207,7 @@ static void add_particles_to_effectors(ListBase **effectors, Scene *scene, Effec /* returns ListBase handle with objects taking part in the effecting */ ListBase *pdInitEffectors(Scene *scene, Object *ob_src, ParticleSystem *psys_src, - EffectorWeights *weights, bool precalc) + EffectorWeights *weights, bool for_simulation) { Base *base; unsigned int layer= ob_src->lay; @@ -214,13 +219,13 @@ ListBase *pdInitEffectors(Scene *scene, Object *ob_src, ParticleSystem *psys_src for (go= weights->group->gobject.first; go; go= go->next) { if ( (go->ob->lay & layer) ) { if ( go->ob->pd && go->ob->pd->forcefield ) - add_object_to_effectors(&effectors, scene, weights, go->ob, ob_src); + add_object_to_effectors(&effectors, scene, weights, go->ob, ob_src, for_simulation); if ( go->ob->particlesystem.first ) { ParticleSystem *psys= go->ob->particlesystem.first; for ( ; psys; psys=psys->next ) - add_particles_to_effectors(&effectors, scene, weights, go->ob, psys, psys_src); + add_particles_to_effectors(&effectors, scene, weights, go->ob, psys, psys_src, for_simulation); } } } @@ -229,19 +234,19 @@ ListBase *pdInitEffectors(Scene *scene, Object *ob_src, ParticleSystem *psys_src for (base = scene->base.first; base; base= base->next) { if ( (base->lay & layer) ) { if ( base->object->pd && base->object->pd->forcefield ) - add_object_to_effectors(&effectors, scene, weights, base->object, ob_src); + add_object_to_effectors(&effectors, scene, weights, base->object, ob_src, for_simulation); if ( base->object->particlesystem.first ) { ParticleSystem *psys= base->object->particlesystem.first; for ( ; psys; psys=psys->next ) - add_particles_to_effectors(&effectors, scene, weights, base->object, psys, psys_src); + add_particles_to_effectors(&effectors, scene, weights, base->object, psys, psys_src, for_simulation); } } } } - if (precalc) + if (for_simulation) pdPrecalculateEffectors(effectors); return effectors; diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c index 083e0d050e6..2242113b79b 100644 --- a/source/blender/blenkernel/intern/gpencil.c +++ b/source/blender/blenkernel/intern/gpencil.c @@ -60,7 +60,7 @@ /* --------- Memory Management ------------ */ /* free stroke, doesn't unlink from any listbase */ -void free_gpencil_stroke(bGPDstroke *gps) +void BKE_gpencil_free_stroke(bGPDstroke *gps) { if (gps == NULL) { return; @@ -76,7 +76,7 @@ void free_gpencil_stroke(bGPDstroke *gps) } /* Free strokes belonging to a gp-frame */ -bool free_gpencil_strokes(bGPDframe *gpf) +bool BKE_gpencil_free_strokes(bGPDframe *gpf) { bGPDstroke *gps_next; bool changed = (BLI_listbase_is_empty(&gpf->strokes) == false); @@ -84,7 +84,7 @@ bool free_gpencil_strokes(bGPDframe *gpf) /* free strokes */ for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps_next) { gps_next = gps->next; - free_gpencil_stroke(gps); + BKE_gpencil_free_stroke(gps); } BLI_listbase_clear(&gpf->strokes); @@ -92,7 +92,7 @@ bool free_gpencil_strokes(bGPDframe *gpf) } /* Free all of a gp-layer's frames */ -void free_gpencil_frames(bGPDlayer *gpl) +void BKE_gpencil_free_frames(bGPDlayer *gpl) { bGPDframe *gpf_next; @@ -104,7 +104,7 @@ void free_gpencil_frames(bGPDlayer *gpl) gpf_next = gpf->next; /* free strokes and their associated memory */ - free_gpencil_strokes(gpf); + BKE_gpencil_free_strokes(gpf); BLI_freelinkN(&gpl->frames, gpf); } gpl->actframe = NULL; @@ -123,7 +123,7 @@ static void free_gpencil_colors(bGPDpalette *palette) } /* Free all of the gp-palettes and colors */ -void free_gpencil_palettes(ListBase *list) +void BKE_gpencil_free_palettes(ListBase *list) { bGPDpalette *palette_next; @@ -144,7 +144,7 @@ void free_gpencil_palettes(ListBase *list) } /* Free all of the gp-brushes for a viewport (list should be &gpd->brushes or so) */ -void free_gpencil_brushes(ListBase *list) +void BKE_gpencil_free_brushes(ListBase *list) { bGPDbrush *brush_next; @@ -173,7 +173,7 @@ void free_gpencil_brushes(ListBase *list) } /* Free all of the gp-layers for a viewport (list should be &gpd->layers or so) */ -void free_gpencil_layers(ListBase *list) +void BKE_gpencil_free_layers(ListBase *list) { bGPDlayer *gpl_next; @@ -185,7 +185,7 @@ void free_gpencil_layers(ListBase *list) gpl_next = gpl->next; /* free layers and their data */ - free_gpencil_frames(gpl); + BKE_gpencil_free_frames(gpl); BLI_freelinkN(list, gpl); } } @@ -196,18 +196,18 @@ void BKE_gpencil_free(bGPdata *gpd, bool free_palettes) BKE_animdata_free(&gpd->id, false); /* free layers */ - free_gpencil_layers(&gpd->layers); + BKE_gpencil_free_layers(&gpd->layers); /* free palettes */ if (free_palettes) { - free_gpencil_palettes(&gpd->palettes); + BKE_gpencil_free_palettes(&gpd->palettes); } } /* -------- Container Creation ---------- */ /* add a new gp-frame to the given layer */ -bGPDframe *gpencil_frame_addnew(bGPDlayer *gpl, int cframe) +bGPDframe *BKE_gpencil_frame_addnew(bGPDlayer *gpl, int cframe) { bGPDframe *gpf = NULL, *gf = NULL; short state = 0; @@ -259,7 +259,7 @@ bGPDframe *gpencil_frame_addnew(bGPDlayer *gpl, int cframe) } /* add a copy of the active gp-frame to the given layer */ -bGPDframe *gpencil_frame_addcopy(bGPDlayer *gpl, int cframe) +bGPDframe *BKE_gpencil_frame_addcopy(bGPDlayer *gpl, int cframe) { bGPDframe *new_frame; bool found = false; @@ -271,11 +271,11 @@ bGPDframe *gpencil_frame_addcopy(bGPDlayer *gpl, int cframe) } else if (gpl->actframe == NULL) { /* no active frame, so just create a new one from scratch */ - return gpencil_frame_addnew(gpl, cframe); + return BKE_gpencil_frame_addnew(gpl, cframe); } /* Create a copy of the frame */ - new_frame = gpencil_frame_duplicate(gpl->actframe); + new_frame = BKE_gpencil_frame_duplicate(gpl->actframe); /* Find frame to insert it before */ for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { @@ -290,7 +290,7 @@ bGPDframe *gpencil_frame_addcopy(bGPDlayer *gpl, int cframe) /* This only happens when we're editing with framelock on... * - Delete the new frame and don't do anything else here... */ - free_gpencil_strokes(new_frame); + BKE_gpencil_free_strokes(new_frame); MEM_freeN(new_frame); new_frame = NULL; @@ -314,7 +314,7 @@ bGPDframe *gpencil_frame_addcopy(bGPDlayer *gpl, int cframe) } /* add a new gp-layer and make it the active layer */ -bGPDlayer *gpencil_layer_addnew(bGPdata *gpd, const char *name, bool setactive) +bGPDlayer *BKE_gpencil_layer_addnew(bGPdata *gpd, const char *name, bool setactive) { bGPDlayer *gpl; @@ -353,14 +353,14 @@ bGPDlayer *gpencil_layer_addnew(bGPdata *gpd, const char *name, bool setactive) /* make this one the active one */ if (setactive) - gpencil_layer_setactive(gpd, gpl); + BKE_gpencil_layer_setactive(gpd, gpl); /* return layer */ return gpl; } /* add a new gp-palette and make it the active */ -bGPDpalette *gpencil_palette_addnew(bGPdata *gpd, const char *name, bool setactive) +bGPDpalette *BKE_gpencil_palette_addnew(bGPdata *gpd, const char *name, bool setactive) { bGPDpalette *palette; @@ -383,7 +383,7 @@ bGPDpalette *gpencil_palette_addnew(bGPdata *gpd, const char *name, bool setacti /* make this one the active one */ if (setactive) { - gpencil_palette_setactive(gpd, palette); + BKE_gpencil_palette_setactive(gpd, palette); } /* return palette */ @@ -391,11 +391,11 @@ bGPDpalette *gpencil_palette_addnew(bGPdata *gpd, const char *name, bool setacti } /* create a set of default drawing brushes with predefined presets */ -void gpencil_brush_init_presets(ToolSettings *ts) +void BKE_gpencil_brush_init_presets(ToolSettings *ts) { bGPDbrush *brush; /* Basic brush */ - brush = gpencil_brush_addnew(ts, "Basic", true); + brush = BKE_gpencil_brush_addnew(ts, "Basic", true); brush->thickness = 3.0f; brush->flag &= ~GP_BRUSH_USE_RANDOM_PRESSURE; brush->draw_sensitivity = 1.0f; @@ -419,7 +419,7 @@ void gpencil_brush_init_presets(ToolSettings *ts) brush->draw_random_sub = 0.0f; /* Pencil brush */ - brush = gpencil_brush_addnew(ts, "Pencil", false); + brush = BKE_gpencil_brush_addnew(ts, "Pencil", false); brush->thickness = 7.0f; brush->flag &= ~GP_BRUSH_USE_RANDOM_PRESSURE; brush->draw_sensitivity = 1.0f; @@ -443,7 +443,7 @@ void gpencil_brush_init_presets(ToolSettings *ts) brush->draw_random_sub = 0.0f; /* Ink brush */ - brush = gpencil_brush_addnew(ts, "Ink", false); + brush = BKE_gpencil_brush_addnew(ts, "Ink", false); brush->thickness = 7.0f; brush->flag &= ~GP_BRUSH_USE_RANDOM_PRESSURE; brush->draw_sensitivity = 1.6f; @@ -467,7 +467,7 @@ void gpencil_brush_init_presets(ToolSettings *ts) brush->draw_random_sub = 0.0f; /* Ink Noise brush */ - brush = gpencil_brush_addnew(ts, "Ink noise", false); + brush = BKE_gpencil_brush_addnew(ts, "Ink noise", false); brush->thickness = 6.0f; brush->flag |= GP_BRUSH_USE_RANDOM_PRESSURE; brush->draw_sensitivity = 1.611f; @@ -491,7 +491,7 @@ void gpencil_brush_init_presets(ToolSettings *ts) brush->draw_random_sub = 0.0f; /* Marker brush */ - brush = gpencil_brush_addnew(ts, "Marker", false); + brush = BKE_gpencil_brush_addnew(ts, "Marker", false); brush->thickness = 10.0f; brush->flag &= ~GP_BRUSH_USE_RANDOM_PRESSURE; brush->draw_sensitivity = 2.0f; @@ -515,7 +515,7 @@ void gpencil_brush_init_presets(ToolSettings *ts) brush->draw_random_sub = 0.0f; /* Crayon brush */ - brush = gpencil_brush_addnew(ts, "Crayon", false); + brush = BKE_gpencil_brush_addnew(ts, "Crayon", false); brush->thickness = 10.0f; brush->flag &= ~GP_BRUSH_USE_RANDOM_PRESSURE; brush->draw_sensitivity = 3.0f; @@ -541,7 +541,7 @@ void gpencil_brush_init_presets(ToolSettings *ts) } /* add a new gp-brush and make it the active */ -bGPDbrush *gpencil_brush_addnew(ToolSettings *ts, const char *name, bool setactive) +bGPDbrush *BKE_gpencil_brush_addnew(ToolSettings *ts, const char *name, bool setactive) { bGPDbrush *brush; @@ -577,7 +577,7 @@ bGPDbrush *gpencil_brush_addnew(ToolSettings *ts, const char *name, bool setacti /* make this one the active one */ if (setactive) { - gpencil_brush_setactive(ts, brush); + BKE_gpencil_brush_setactive(ts, brush); } /* return brush */ @@ -585,7 +585,7 @@ bGPDbrush *gpencil_brush_addnew(ToolSettings *ts, const char *name, bool setacti } /* add a new gp-palettecolor and make it the active */ -bGPDpalettecolor *gpencil_palettecolor_addnew(bGPDpalette *palette, const char *name, bool setactive) +bGPDpalettecolor *BKE_gpencil_palettecolor_addnew(bGPDpalette *palette, const char *name, bool setactive) { bGPDpalettecolor *palcolor; @@ -612,7 +612,7 @@ bGPDpalettecolor *gpencil_palettecolor_addnew(bGPDpalette *palette, const char * /* make this one the active one */ if (setactive) { - gpencil_palettecolor_setactive(palette, palcolor); + BKE_gpencil_palettecolor_setactive(palette, palcolor); } /* return palette color */ @@ -620,7 +620,7 @@ bGPDpalettecolor *gpencil_palettecolor_addnew(bGPDpalette *palette, const char * } /* add a new gp-datablock */ -bGPdata *gpencil_data_addnew(const char name[]) +bGPdata *BKE_gpencil_data_addnew(const char name[]) { bGPdata *gpd; @@ -641,7 +641,7 @@ bGPdata *gpencil_data_addnew(const char name[]) /* -------- Data Duplication ---------- */ /* make a copy of a given gpencil frame */ -bGPDframe *gpencil_frame_duplicate(const bGPDframe *gpf_src) +bGPDframe *BKE_gpencil_frame_duplicate(const bGPDframe *gpf_src) { bGPDstroke *gps_dst; bGPDframe *gpf_dst; @@ -671,7 +671,7 @@ bGPDframe *gpencil_frame_duplicate(const bGPDframe *gpf_src) } /* make a copy of a given gpencil brush */ -bGPDbrush *gpencil_brush_duplicate(const bGPDbrush *brush_src) +bGPDbrush *BKE_gpencil_brush_duplicate(const bGPDbrush *brush_src) { bGPDbrush *brush_dst; @@ -693,7 +693,7 @@ bGPDbrush *gpencil_brush_duplicate(const bGPDbrush *brush_src) } /* make a copy of a given gpencil palette */ -bGPDpalette *gpencil_palette_duplicate(const bGPDpalette *palette_src) +bGPDpalette *BKE_gpencil_palette_duplicate(const bGPDpalette *palette_src) { bGPDpalette *palette_dst; const bGPDpalettecolor *palcolor_src; @@ -720,7 +720,7 @@ bGPDpalette *gpencil_palette_duplicate(const bGPDpalette *palette_src) return palette_dst; } /* make a copy of a given gpencil layer */ -bGPDlayer *gpencil_layer_duplicate(const bGPDlayer *gpl_src) +bGPDlayer *BKE_gpencil_layer_duplicate(const bGPDlayer *gpl_src) { const bGPDframe *gpf_src; bGPDframe *gpf_dst; @@ -739,7 +739,7 @@ bGPDlayer *gpencil_layer_duplicate(const bGPDlayer *gpl_src) BLI_listbase_clear(&gpl_dst->frames); for (gpf_src = gpl_src->frames.first; gpf_src; gpf_src = gpf_src->next) { /* make a copy of source frame */ - gpf_dst = gpencil_frame_duplicate(gpf_src); + gpf_dst = BKE_gpencil_frame_duplicate(gpf_src); BLI_addtail(&gpl_dst->frames, gpf_dst); /* if source frame was the current layer's 'active' frame, reassign that too */ @@ -752,7 +752,7 @@ bGPDlayer *gpencil_layer_duplicate(const bGPDlayer *gpl_src) } /* make a copy of a given gpencil datablock */ -bGPdata *gpencil_data_duplicate(Main *bmain, bGPdata *gpd_src, bool internal_copy) +bGPdata *BKE_gpencil_data_duplicate(Main *bmain, bGPdata *gpd_src, bool internal_copy) { const bGPDlayer *gpl_src; bGPDlayer *gpl_dst; @@ -777,7 +777,7 @@ bGPdata *gpencil_data_duplicate(Main *bmain, bGPdata *gpd_src, bool internal_cop BLI_listbase_clear(&gpd_dst->layers); for (gpl_src = gpd_src->layers.first; gpl_src; gpl_src = gpl_src->next) { /* make a copy of source layer and its data */ - gpl_dst = gpencil_layer_duplicate(gpl_src); + gpl_dst = BKE_gpencil_layer_duplicate(gpl_src); BLI_addtail(&gpd_dst->layers, gpl_dst); } if (!internal_copy) { @@ -785,7 +785,7 @@ bGPdata *gpencil_data_duplicate(Main *bmain, bGPdata *gpd_src, bool internal_cop bGPDpalette *palette_src, *palette_dst; BLI_listbase_clear(&gpd_dst->palettes); for (palette_src = gpd_src->palettes.first; palette_src; palette_src = palette_src->next) { - palette_dst = gpencil_palette_duplicate(palette_src); + palette_dst = BKE_gpencil_palette_duplicate(palette_src); BLI_addtail(&gpd_dst->palettes, palette_dst); } } @@ -802,7 +802,7 @@ void BKE_gpencil_make_local(Main *bmain, bGPdata *gpd, const bool lib_local) /* -------- GP-Stroke API --------- */ /* ensure selection status of stroke is in sync with its points */ -void gpencil_stroke_sync_selection(bGPDstroke *gps) +void BKE_gpencil_stroke_sync_selection(bGPDstroke *gps) { bGPDspoint *pt; int i; @@ -827,7 +827,7 @@ void gpencil_stroke_sync_selection(bGPDstroke *gps) /* -------- GP-Frame API ---------- */ /* delete the last stroke of the given frame */ -void gpencil_frame_delete_laststroke(bGPDlayer *gpl, bGPDframe *gpf) +void BKE_gpencil_frame_delete_laststroke(bGPDlayer *gpl, bGPDframe *gpf) { bGPDstroke *gps = (gpf) ? gpf->strokes.last : NULL; int cfra = (gpf) ? gpf->framenum : 0; /* assume that the current frame was not locked */ @@ -843,8 +843,8 @@ void gpencil_frame_delete_laststroke(bGPDlayer *gpl, bGPDframe *gpf) /* if frame has no strokes after this, delete it */ if (BLI_listbase_is_empty(&gpf->strokes)) { - gpencil_layer_delframe(gpl, gpf); - gpencil_layer_getframe(gpl, cfra, 0); + BKE_gpencil_layer_delframe(gpl, gpf); + BKE_gpencil_layer_getframe(gpl, cfra, 0); } } @@ -892,7 +892,7 @@ bGPDframe *BKE_gpencil_layer_find_frame(bGPDlayer *gpl, int cframe) * - this sets the layer's actframe var (if allowed to) * - extension beyond range (if first gp-frame is after all frame in interest and cannot add) */ -bGPDframe *gpencil_layer_getframe(bGPDlayer *gpl, int cframe, eGP_GetFrame_Mode addnew) +bGPDframe *BKE_gpencil_layer_getframe(bGPDlayer *gpl, int cframe, eGP_GetFrame_Mode addnew) { bGPDframe *gpf = NULL; short found = 0; @@ -931,9 +931,9 @@ bGPDframe *gpencil_layer_getframe(bGPDlayer *gpl, int cframe, eGP_GetFrame_Mode if ((found) && (gpf->framenum == cframe)) gpl->actframe = gpf; else if (addnew == GP_GETFRAME_ADD_COPY) - gpl->actframe = gpencil_frame_addcopy(gpl, cframe); + gpl->actframe = BKE_gpencil_frame_addcopy(gpl, cframe); else - gpl->actframe = gpencil_frame_addnew(gpl, cframe); + gpl->actframe = BKE_gpencil_frame_addnew(gpl, cframe); } else if (found) gpl->actframe = gpf; @@ -953,9 +953,9 @@ bGPDframe *gpencil_layer_getframe(bGPDlayer *gpl, int cframe, eGP_GetFrame_Mode if ((found) && (gpf->framenum == cframe)) gpl->actframe = gpf; else if (addnew == GP_GETFRAME_ADD_COPY) - gpl->actframe = gpencil_frame_addcopy(gpl, cframe); + gpl->actframe = BKE_gpencil_frame_addcopy(gpl, cframe); else - gpl->actframe = gpencil_frame_addnew(gpl, cframe); + gpl->actframe = BKE_gpencil_frame_addnew(gpl, cframe); } else if (found) gpl->actframe = gpf; @@ -992,7 +992,7 @@ bGPDframe *gpencil_layer_getframe(bGPDlayer *gpl, int cframe, eGP_GetFrame_Mode if ((found) && (gpf->framenum == cframe)) gpl->actframe = gpf; else - gpl->actframe = gpencil_frame_addnew(gpl, cframe); + gpl->actframe = BKE_gpencil_frame_addnew(gpl, cframe); } else if (found) gpl->actframe = gpf; @@ -1005,7 +1005,7 @@ bGPDframe *gpencil_layer_getframe(bGPDlayer *gpl, int cframe, eGP_GetFrame_Mode else { /* currently no frames (add if allowed to) */ if (addnew) - gpl->actframe = gpencil_frame_addnew(gpl, cframe); + gpl->actframe = BKE_gpencil_frame_addnew(gpl, cframe); else { /* don't do anything... this may be when no frames yet! */ /* gpl->actframe should still be NULL */ @@ -1017,7 +1017,7 @@ bGPDframe *gpencil_layer_getframe(bGPDlayer *gpl, int cframe, eGP_GetFrame_Mode } /* delete the given frame from a layer */ -bool gpencil_layer_delframe(bGPDlayer *gpl, bGPDframe *gpf) +bool BKE_gpencil_layer_delframe(bGPDlayer *gpl, bGPDframe *gpf) { bool changed = false; @@ -1034,14 +1034,14 @@ bool gpencil_layer_delframe(bGPDlayer *gpl, bGPDframe *gpf) gpl->actframe = NULL; /* free the frame and its data */ - changed = free_gpencil_strokes(gpf); + changed = BKE_gpencil_free_strokes(gpf); BLI_freelinkN(&gpl->frames, gpf); return changed; } /* get the active gp-layer for editing */ -bGPDlayer *gpencil_layer_getactive(bGPdata *gpd) +bGPDlayer *BKE_gpencil_layer_getactive(bGPdata *gpd) { bGPDlayer *gpl; @@ -1060,7 +1060,7 @@ bGPDlayer *gpencil_layer_getactive(bGPdata *gpd) } /* set the active gp-layer */ -void gpencil_layer_setactive(bGPdata *gpd, bGPDlayer *active) +void BKE_gpencil_layer_setactive(bGPdata *gpd, bGPDlayer *active) { bGPDlayer *gpl; @@ -1077,20 +1077,20 @@ void gpencil_layer_setactive(bGPdata *gpd, bGPDlayer *active) } /* delete the active gp-layer */ -void gpencil_layer_delete(bGPdata *gpd, bGPDlayer *gpl) +void BKE_gpencil_layer_delete(bGPdata *gpd, bGPDlayer *gpl) { /* error checking */ if (ELEM(NULL, gpd, gpl)) return; /* free layer */ - free_gpencil_frames(gpl); + BKE_gpencil_free_frames(gpl); BLI_freelinkN(&gpd->layers, gpl); } /* ************************************************** */ /* get the active gp-brush for editing */ -bGPDbrush *gpencil_brush_getactive(ToolSettings *ts) +bGPDbrush *BKE_gpencil_brush_getactive(ToolSettings *ts) { bGPDbrush *brush; @@ -1111,7 +1111,7 @@ bGPDbrush *gpencil_brush_getactive(ToolSettings *ts) } /* set the active gp-brush */ -void gpencil_brush_setactive(ToolSettings *ts, bGPDbrush *active) +void BKE_gpencil_brush_setactive(ToolSettings *ts, bGPDbrush *active) { bGPDbrush *brush; @@ -1130,7 +1130,7 @@ void gpencil_brush_setactive(ToolSettings *ts, bGPDbrush *active) } /* delete the active gp-brush */ -void gpencil_brush_delete(ToolSettings *ts, bGPDbrush *brush) +void BKE_gpencil_brush_delete(ToolSettings *ts, bGPDbrush *brush) { /* error checking */ if (ELEM(NULL, ts, brush)) { @@ -1154,7 +1154,7 @@ void gpencil_brush_delete(ToolSettings *ts, bGPDbrush *brush) /* ************************************************** */ /* get the active gp-palette for editing */ -bGPDpalette *gpencil_palette_getactive(bGPdata *gpd) +bGPDpalette *BKE_gpencil_palette_getactive(bGPdata *gpd) { bGPDpalette *palette; @@ -1174,7 +1174,7 @@ bGPDpalette *gpencil_palette_getactive(bGPdata *gpd) } /* set the active gp-palette */ -void gpencil_palette_setactive(bGPdata *gpd, bGPDpalette *active) +void BKE_gpencil_palette_setactive(bGPdata *gpd, bGPDpalette *active) { bGPDpalette *palette; @@ -1191,11 +1191,11 @@ void gpencil_palette_setactive(bGPdata *gpd, bGPDpalette *active) /* set as active one */ active->flag |= PL_PALETTE_ACTIVE; /* force color recalc */ - gpencil_palette_change_strokes(gpd); + BKE_gpencil_palette_change_strokes(gpd); } /* delete the active gp-palette */ -void gpencil_palette_delete(bGPdata *gpd, bGPDpalette *palette) +void BKE_gpencil_palette_delete(bGPdata *gpd, bGPDpalette *palette) { /* error checking */ if (ELEM(NULL, gpd, palette)) { @@ -1206,11 +1206,11 @@ void gpencil_palette_delete(bGPdata *gpd, bGPDpalette *palette) free_gpencil_colors(palette); BLI_freelinkN(&gpd->palettes, palette); /* force color recalc */ - gpencil_palette_change_strokes(gpd); + BKE_gpencil_palette_change_strokes(gpd); } /* Set all strokes to recalc the palette color */ -void gpencil_palette_change_strokes(bGPdata *gpd) +void BKE_gpencil_palette_change_strokes(bGPdata *gpd) { bGPDlayer *gpl; bGPDframe *gpf; @@ -1227,7 +1227,7 @@ void gpencil_palette_change_strokes(bGPdata *gpd) /* get the active gp-palettecolor for editing */ -bGPDpalettecolor *gpencil_palettecolor_getactive(bGPDpalette *palette) +bGPDpalettecolor *BKE_gpencil_palettecolor_getactive(bGPDpalette *palette) { bGPDpalettecolor *palcolor; @@ -1247,7 +1247,7 @@ bGPDpalettecolor *gpencil_palettecolor_getactive(bGPDpalette *palette) return NULL; } /* get the gp-palettecolor looking for name */ -bGPDpalettecolor *gpencil_palettecolor_getbyname(bGPDpalette *palette, char *name) +bGPDpalettecolor *BKE_gpencil_palettecolor_getbyname(bGPDpalette *palette, char *name) { /* error checking */ if (ELEM(NULL, palette, name)) { @@ -1258,7 +1258,7 @@ bGPDpalettecolor *gpencil_palettecolor_getbyname(bGPDpalette *palette, char *nam } /* Change color name in all strokes */ -void gpencil_palettecolor_changename(bGPdata *gpd, char *oldname, const char *newname) +void BKE_gpencil_palettecolor_changename(bGPdata *gpd, char *oldname, const char *newname) { bGPDlayer *gpl; bGPDframe *gpf; @@ -1268,7 +1268,7 @@ void gpencil_palettecolor_changename(bGPdata *gpd, char *oldname, const char *ne for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { for (gps = gpf->strokes.first; gps; gps = gps->next) { if (STREQ(gps->colorname, oldname)) { - strcpy(gps->colorname, newname); + BLI_strncpy(gps->colorname, newname, sizeof(gps->colorname)); } } } @@ -1277,7 +1277,7 @@ void gpencil_palettecolor_changename(bGPdata *gpd, char *oldname, const char *ne } /* Delete all strokes of the color */ -void gpencil_palettecolor_delete_strokes(struct bGPdata *gpd, char *name) +void BKE_gpencil_palettecolor_delete_strokes(struct bGPdata *gpd, char *name) { bGPDlayer *gpl; bGPDframe *gpf; @@ -1300,7 +1300,7 @@ void gpencil_palettecolor_delete_strokes(struct bGPdata *gpd, char *name) } /* set the active gp-palettecolor */ -void gpencil_palettecolor_setactive(bGPDpalette *palette, bGPDpalettecolor *active) +void BKE_gpencil_palettecolor_setactive(bGPDpalette *palette, bGPDpalettecolor *active) { bGPDpalettecolor *palcolor; @@ -1319,7 +1319,7 @@ void gpencil_palettecolor_setactive(bGPDpalette *palette, bGPDpalettecolor *acti } /* delete the active gp-palettecolor */ -void gpencil_palettecolor_delete(bGPDpalette *palette, bGPDpalettecolor *palcolor) +void BKE_gpencil_palettecolor_delete(bGPDpalette *palette, bGPDpalettecolor *palcolor) { /* error checking */ if (ELEM(NULL, palette, palcolor)) { diff --git a/source/blender/blenkernel/intern/idcode.c b/source/blender/blenkernel/intern/idcode.c index 90b3713e47c..70d037d85f3 100644 --- a/source/blender/blenkernel/intern/idcode.c +++ b/source/blender/blenkernel/intern/idcode.c @@ -60,6 +60,7 @@ static IDType idtypes[] = { { ID_AR, "Armature", "armatures", BLT_I18NCONTEXT_ID_ARMATURE, IDTYPE_FLAGS_ISLINKABLE }, { ID_BR, "Brush", "brushes", BLT_I18NCONTEXT_ID_BRUSH, IDTYPE_FLAGS_ISLINKABLE }, { ID_CA, "Camera", "cameras", BLT_I18NCONTEXT_ID_CAMERA, IDTYPE_FLAGS_ISLINKABLE }, + { ID_CF, "CacheFile", "cache_files", BLT_I18NCONTEXT_ID_CACHEFILE, IDTYPE_FLAGS_ISLINKABLE }, { ID_CU, "Curve", "curves", BLT_I18NCONTEXT_ID_CURVE, IDTYPE_FLAGS_ISLINKABLE }, { ID_GD, "GPencil", "grease_pencil", BLT_I18NCONTEXT_ID_GPENCIL, IDTYPE_FLAGS_ISLINKABLE }, /* rename gpencil */ { ID_GR, "Group", "groups", BLT_I18NCONTEXT_ID_GROUP, IDTYPE_FLAGS_ISLINKABLE }, @@ -184,6 +185,7 @@ int BKE_idcode_to_idfilter(const short idcode) CASE_IDFILTER(AR); CASE_IDFILTER(BR); CASE_IDFILTER(CA); + CASE_IDFILTER(CF); CASE_IDFILTER(CU); CASE_IDFILTER(GD); CASE_IDFILTER(GR); @@ -227,6 +229,7 @@ short BKE_idcode_from_idfilter(const int idfilter) CASE_IDFILTER(AR); CASE_IDFILTER(BR); CASE_IDFILTER(CA); + CASE_IDFILTER(CF); CASE_IDFILTER(CU); CASE_IDFILTER(GD); CASE_IDFILTER(GR); @@ -259,6 +262,56 @@ short BKE_idcode_from_idfilter(const int idfilter) } /** + * Convert an idcode into an index (e.g. ID_OB -> INDEX_ID_OB). + */ +int BKE_idcode_to_index(const short idcode) +{ +#define CASE_IDINDEX(_id) case ID_##_id: return INDEX_ID_##_id + + switch ((ID_Type)idcode) { + CASE_IDINDEX(AC); + CASE_IDINDEX(AR); + CASE_IDINDEX(BR); + CASE_IDINDEX(CA); + CASE_IDINDEX(CF); + CASE_IDINDEX(CU); + CASE_IDINDEX(GD); + CASE_IDINDEX(GR); + CASE_IDINDEX(IM); + CASE_IDINDEX(KE); + CASE_IDINDEX(IP); + CASE_IDINDEX(LA); + CASE_IDINDEX(LI); + CASE_IDINDEX(LS); + CASE_IDINDEX(LT); + CASE_IDINDEX(MA); + CASE_IDINDEX(MB); + CASE_IDINDEX(MC); + CASE_IDINDEX(ME); + CASE_IDINDEX(MSK); + CASE_IDINDEX(NT); + CASE_IDINDEX(OB); + CASE_IDINDEX(PA); + CASE_IDINDEX(PAL); + CASE_IDINDEX(PC); + CASE_IDINDEX(SCE); + CASE_IDINDEX(SCR); + CASE_IDINDEX(SPK); + CASE_IDINDEX(SO); + CASE_IDINDEX(TE); + CASE_IDINDEX(TXT); + CASE_IDINDEX(VF); + CASE_IDINDEX(WM); + CASE_IDINDEX(WO); + } + + BLI_assert(0); + return -1; + +#undef CASE_IDINDEX +} + +/** * Convert an idcode into a name (plural). * * \param idcode: The code to convert. diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index ea28dabb945..8a9cb73c8c9 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -436,6 +436,7 @@ static void copy_image_packedfiles(ListBase *lb_dst, const ListBase *lb_src) Image *BKE_image_copy(Main *bmain, Image *ima) { Image *nima = image_alloc(bmain, ima->id.name + 2, ima->source, ima->type); + ima->id.newid = &nima->id; BLI_strncpy(nima->name, ima->name, sizeof(ima->name)); @@ -1865,7 +1866,7 @@ void BKE_image_stamp_buf( display = IMB_colormanagement_display_get_named(display_device); if (stamp_data_template == NULL) { - stampdata(scene, camera, &stamp_data, 1); + stampdata(scene, camera, &stamp_data, (scene->r.stamp & R_STAMP_HIDE_LABELS) == 0); } else { stampdata_from_template(&stamp_data, scene, stamp_data_template); diff --git a/source/blender/blenkernel/intern/lamp.c b/source/blender/blenkernel/intern/lamp.c index e9d039ad480..d098366aef4 100644 --- a/source/blender/blenkernel/intern/lamp.c +++ b/source/blender/blenkernel/intern/lamp.c @@ -154,8 +154,6 @@ Lamp *localize_lamp(Lamp *la) if (lan->mtex[a]) { lan->mtex[a] = MEM_mallocN(sizeof(MTex), "localize_lamp"); memcpy(lan->mtex[a], la->mtex[a], sizeof(MTex)); - /* free lamp decrements */ - id_us_plus((ID *)lan->mtex[a]->tex); } } diff --git a/source/blender/blenkernel/intern/library.c b/source/blender/blenkernel/intern/library.c index d3f20859f06..6d94cd28b31 100644 --- a/source/blender/blenkernel/intern/library.c +++ b/source/blender/blenkernel/intern/library.c @@ -45,6 +45,7 @@ #include "DNA_anim_types.h" #include "DNA_armature_types.h" #include "DNA_brush_types.h" +#include "DNA_cachefile_types.h" #include "DNA_camera_types.h" #include "DNA_group_types.h" #include "DNA_gpencil_types.h" @@ -56,6 +57,7 @@ #include "DNA_material_types.h" #include "DNA_mesh_types.h" #include "DNA_meta_types.h" +#include "DNA_modifier_types.h" #include "DNA_movieclip_types.h" #include "DNA_mask_types.h" #include "DNA_node_types.h" @@ -81,6 +83,7 @@ #include "BKE_bpath.h" #include "BKE_brush.h" #include "BKE_camera.h" +#include "BKE_cachefile.h" #include "BKE_context.h" #include "BKE_curve.h" #include "BKE_depsgraph.h" @@ -332,8 +335,10 @@ void BKE_id_make_local_generic(Main *bmain, ID *id, const bool id_in_mainlist, c */ bool id_make_local(Main *bmain, ID *id, const bool test, const bool lib_local) { - if (id->tag & LIB_TAG_INDIRECT) + /* We don't care whether ID is directly or indirectly linked in case we are making a whole lib local... */ + if (!lib_local && (id->tag & LIB_TAG_INDIRECT)) { return false; + } switch ((ID_Type)GS(id->name)) { case ID_SCE: @@ -423,6 +428,9 @@ bool id_make_local(Main *bmain, ID *id, const bool test, const bool lib_local) case ID_PC: if (!test) BKE_paint_curve_make_local(bmain, (PaintCurve *)id, lib_local); return true; + case ID_CF: + if (!test) BKE_cachefile_make_local(bmain, (CacheFile *)id, lib_local); + return true; case ID_SCR: case ID_LI: case ID_KE: @@ -510,7 +518,7 @@ bool id_copy(Main *bmain, ID *id, ID **newid, bool test) if (!test) *newid = (ID *)BKE_particlesettings_copy(bmain, (ParticleSettings *)id); return true; case ID_GD: - if (!test) *newid = (ID *)gpencil_data_duplicate(bmain, (bGPdata *)id, false); + if (!test) *newid = (ID *)BKE_gpencil_data_duplicate(bmain, (bGPdata *)id, false); return true; case ID_MC: if (!test) *newid = (ID *)BKE_movieclip_copy(bmain, (MovieClip *)id); @@ -527,6 +535,9 @@ bool id_copy(Main *bmain, ID *id, ID **newid, bool test) case ID_PC: if (!test) *newid = (ID *)BKE_paint_curve_copy(bmain, (PaintCurve *)id); return true; + case ID_CF: + if (!test) *newid = (ID *)BKE_cachefile_copy(bmain, (CacheFile *)id); + return true; case ID_SCE: case ID_LI: case ID_SCR: @@ -639,6 +650,8 @@ ListBase *which_libbase(Main *mainlib, short type) return &(mainlib->palettes); case ID_PC: return &(mainlib->paintcurves); + case ID_CF: + return &(mainlib->cachefiles); } return NULL; } @@ -740,59 +753,56 @@ void BKE_main_lib_objects_recalc_all(Main *bmain) * \note MAX_LIBARRAY define should match this code */ int set_listbasepointers(Main *main, ListBase **lb) { - int a = 0; - /* BACKWARDS! also watch order of free-ing! (mesh<->mat), first items freed last. * This is important because freeing data decreases usercounts of other datablocks, * if this data is its self freed it can crash. */ - lb[a++] = &(main->library); /* Libraries may be accessed from pretty much any other ID... */ - lb[a++] = &(main->ipo); - lb[a++] = &(main->action); /* moved here to avoid problems when freeing with animato (aligorith) */ - lb[a++] = &(main->key); - lb[a++] = &(main->gpencil); /* referenced by nodes, objects, view, scene etc, before to free after. */ - lb[a++] = &(main->nodetree); - lb[a++] = &(main->image); - lb[a++] = &(main->tex); - lb[a++] = &(main->mat); - lb[a++] = &(main->vfont); + lb[INDEX_ID_LI] = &(main->library); /* Libraries may be accessed from pretty much any other ID... */ + lb[INDEX_ID_IP] = &(main->ipo); + lb[INDEX_ID_AC] = &(main->action); /* moved here to avoid problems when freeing with animato (aligorith) */ + lb[INDEX_ID_KE] = &(main->key); + lb[INDEX_ID_GD] = &(main->gpencil); /* referenced by nodes, objects, view, scene etc, before to free after. */ + lb[INDEX_ID_NT] = &(main->nodetree); + lb[INDEX_ID_IM] = &(main->image); + lb[INDEX_ID_TE] = &(main->tex); + lb[INDEX_ID_MA] = &(main->mat); + lb[INDEX_ID_VF] = &(main->vfont); /* Important!: When adding a new object type, * the specific data should be inserted here */ - lb[a++] = &(main->armature); - - lb[a++] = &(main->mesh); - lb[a++] = &(main->curve); - lb[a++] = &(main->mball); - - lb[a++] = &(main->latt); - lb[a++] = &(main->lamp); - lb[a++] = &(main->camera); - - lb[a++] = &(main->text); - lb[a++] = &(main->sound); - lb[a++] = &(main->group); - lb[a++] = &(main->palettes); - lb[a++] = &(main->paintcurves); - lb[a++] = &(main->brush); - lb[a++] = &(main->particle); - lb[a++] = &(main->speaker); - - lb[a++] = &(main->world); - lb[a++] = &(main->movieclip); - lb[a++] = &(main->screen); - lb[a++] = &(main->object); - lb[a++] = &(main->linestyle); /* referenced by scenes */ - lb[a++] = &(main->scene); - lb[a++] = &(main->wm); - lb[a++] = &(main->mask); + lb[INDEX_ID_AR] = &(main->armature); + + lb[INDEX_ID_CF] = &(main->cachefiles); + lb[INDEX_ID_ME] = &(main->mesh); + lb[INDEX_ID_CU] = &(main->curve); + lb[INDEX_ID_MB] = &(main->mball); + + lb[INDEX_ID_LT] = &(main->latt); + lb[INDEX_ID_LA] = &(main->lamp); + lb[INDEX_ID_CA] = &(main->camera); + + lb[INDEX_ID_TXT] = &(main->text); + lb[INDEX_ID_SO] = &(main->sound); + lb[INDEX_ID_GR] = &(main->group); + lb[INDEX_ID_PAL] = &(main->palettes); + lb[INDEX_ID_PC] = &(main->paintcurves); + lb[INDEX_ID_BR] = &(main->brush); + lb[INDEX_ID_PA] = &(main->particle); + lb[INDEX_ID_SPK] = &(main->speaker); + + lb[INDEX_ID_WO] = &(main->world); + lb[INDEX_ID_MC] = &(main->movieclip); + lb[INDEX_ID_SCR] = &(main->screen); + lb[INDEX_ID_OB] = &(main->object); + lb[INDEX_ID_LS] = &(main->linestyle); /* referenced by scenes */ + lb[INDEX_ID_SCE] = &(main->scene); + lb[INDEX_ID_WM] = &(main->wm); + lb[INDEX_ID_MSK] = &(main->mask); - lb[a] = NULL; - - BLI_assert(a + 1 == MAX_LIBARRAY); + lb[INDEX_ID_NULL] = NULL; - return a; + return (MAX_LIBARRAY - 1); } /* *********** ALLOC AND FREE ***************** @@ -913,6 +923,9 @@ void *BKE_libblock_alloc_notest(short type) case ID_PC: id = MEM_callocN(sizeof(PaintCurve), "Paint Curve"); break; + case ID_CF: + id = MEM_callocN(sizeof(CacheFile), "Cache File"); + break; } return id; } @@ -1039,6 +1052,9 @@ void BKE_libblock_init_empty(ID *id) case ID_LS: BKE_linestyle_init((FreestyleLineStyle *)id); break; + case ID_CF: + BKE_cachefile_init((CacheFile *)id); + break; case ID_KE: /* Shapekeys are a complex topic too - they depend on their 'user' data type... * They are not linkable, though, so it should never reach here anyway. */ @@ -1226,6 +1242,7 @@ void BKE_main_free(Main *mainvar) case 31: BKE_libblock_free_ex(mainvar, id, false); break; case 32: BKE_libblock_free_ex(mainvar, id, false); break; case 33: BKE_libblock_free_ex(mainvar, id, false); break; + case 34: BKE_libblock_free_ex(mainvar, id, false); break; default: BLI_assert(0); break; @@ -1639,22 +1656,20 @@ void BKE_library_make_local(Main *bmain, const Library *lib, const bool untagged for (a = set_listbasepointers(bmain, lbarray); a--; ) { id = lbarray[a]->first; - if (!id || !BKE_idcode_is_linkable(GS(id->name))) { - /* Do not explicitly make local non-linkable IDs (shapekeys, in fact), they are assumed to be handled - * by real datablocks responsible of them. */ - continue; - } + /* Do not explicitly make local non-linkable IDs (shapekeys, in fact), they are assumed to be handled + * by real datablocks responsible of them. */ + const bool do_skip = (id && !BKE_idcode_is_linkable(GS(id->name))); for (; id; id = id_next) { id->newid = NULL; id_next = id->next; /* id is possibly being inserted again */ - + /* The check on the second line (LIB_TAG_PRE_EXISTING) is done so its * possible to tag data you don't want to be made local, used for * appending data, so any libdata already linked wont become local * (very nasty to discover all your links are lost after appending) * */ - if (id->tag & (LIB_TAG_EXTERN | LIB_TAG_INDIRECT | LIB_TAG_NEW) && + if (!do_skip && id->tag & (LIB_TAG_EXTERN | LIB_TAG_INDIRECT | LIB_TAG_NEW) && ((untagged_only == false) || !(id->tag & LIB_TAG_PRE_EXISTING))) { if (lib == NULL || id->lib == lib) { diff --git a/source/blender/blenkernel/intern/library_query.c b/source/blender/blenkernel/intern/library_query.c index 90dcd96341e..5e4f5f160eb 100644 --- a/source/blender/blenkernel/intern/library_query.c +++ b/source/blender/blenkernel/intern/library_query.c @@ -305,7 +305,7 @@ void BKE_library_foreach_ID_link(ID *id, LibraryIDLinkCallback callback, void *u library_foreach_animationData(&data, adt); } - switch (GS(id->name)) { + switch ((ID_Type)GS(id->name)) { case ID_LI: { Library *lib = (Library *) id; @@ -846,6 +846,25 @@ void BKE_library_foreach_ID_link(ID *id, LibraryIDLinkCallback callback, void *u } break; } + + /* Nothing needed for those... */ + case ID_IM: + case ID_VF: + case ID_TXT: + case ID_SO: + case ID_AR: + case ID_AC: + case ID_GD: + case ID_WM: + case ID_PAL: + case ID_PC: + case ID_CF: + break; + + /* Deprecated. */ + case ID_IP: + break; + } } while ((id = (flag & IDWALK_RECURSE) ? BLI_LINKSTACK_POP(data.ids_todo) : NULL)); @@ -888,7 +907,7 @@ bool BKE_library_idtype_can_use_idtype(const short id_type_owner, const short id return id_type_can_have_animdata(id_type_owner); } - switch (id_type_owner) { + switch ((ID_Type)id_type_owner) { case ID_LI: return ELEM(id_type_used, ID_LI); case ID_SCE: @@ -947,9 +966,24 @@ bool BKE_library_idtype_can_use_idtype(const short id_type_owner, const short id return ELEM(id_type_used, ID_MC); /* WARNING! mask->parent.id, not typed. */ case ID_LS: return (ELEM(id_type_used, ID_TE, ID_OB) || BKE_library_idtype_can_use_idtype(ID_NT, id_type_used)); - default: + case ID_IM: + case ID_VF: + case ID_TXT: + case ID_SO: + case ID_AR: + case ID_AC: + case ID_GD: + case ID_WM: + case ID_PAL: + case ID_PC: + case ID_CF: + /* Those types never use/reference other IDs... */ + return false; + case ID_IP: + /* Deprecated... */ return false; } + return false; } diff --git a/source/blender/blenkernel/intern/library_remap.c b/source/blender/blenkernel/intern/library_remap.c index a7b93cf3cb1..9b89a2fb807 100644 --- a/source/blender/blenkernel/intern/library_remap.c +++ b/source/blender/blenkernel/intern/library_remap.c @@ -38,6 +38,7 @@ #include "DNA_armature_types.h" #include "DNA_brush_types.h" #include "DNA_camera_types.h" +#include "DNA_cachefile_types.h" #include "DNA_group_types.h" #include "DNA_gpencil_types.h" #include "DNA_ipo_types.h" @@ -69,6 +70,7 @@ #include "BKE_armature.h" #include "BKE_brush.h" #include "BKE_camera.h" +#include "BKE_cachefile.h" #include "BKE_curve.h" #include "BKE_depsgraph.h" #include "BKE_fcurve.h" @@ -812,6 +814,9 @@ void BKE_libblock_free_ex(Main *bmain, void *idv, const bool do_id_user) case ID_PC: BKE_paint_curve_free((PaintCurve *)id); break; + case ID_CF: + BKE_cachefile_free((CacheFile *)id); + break; } /* avoid notifying on removed data */ @@ -870,8 +875,8 @@ void BKE_libblock_delete(Main *bmain, void *idv) BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); /* First tag all datablocks directly from target lib. - * Note that we go forward here, since we want to check dependencies before users (e.g. meshes before objects). - * Avoids to have to loop twice. */ + * Note that we go forward here, since we want to check dependencies before users (e.g. meshes before objects). + * Avoids to have to loop twice. */ for (i = 0; i < base_count; i++) { ListBase *lb = lbarray[i]; ID *id; diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c index 470108545b8..54945242fe4 100644 --- a/source/blender/blenkernel/intern/material.c +++ b/source/blender/blenkernel/intern/material.c @@ -58,6 +58,7 @@ #include "BKE_animsys.h" #include "BKE_displist.h" #include "BKE_global.h" +#include "BKE_depsgraph.h" #include "BKE_icons.h" #include "BKE_image.h" #include "BKE_library.h" @@ -398,7 +399,7 @@ static void material_data_index_clear_id(ID *id) } } -void BKE_material_resize_id(struct ID *id, short totcol, bool do_id_user) +void BKE_material_resize_id(Main *bmain, ID *id, short totcol, bool do_id_user) { Material ***matar = give_matarar_id(id); short *totcolp = give_totcolp_id(id); @@ -424,9 +425,11 @@ void BKE_material_resize_id(struct ID *id, short totcol, bool do_id_user) *matar = MEM_recallocN(*matar, sizeof(void *) * totcol); } *totcolp = totcol; + + DAG_relations_tag_update(bmain); } -void BKE_material_append_id(ID *id, Material *ma) +void BKE_material_append_id(Main *bmain, ID *id, Material *ma) { Material ***matar; if ((matar = give_matarar_id(id))) { @@ -439,11 +442,12 @@ void BKE_material_append_id(ID *id, Material *ma) (*matar)[(*totcol)++] = ma; id_us_plus((ID *)ma); - test_all_objects_materials(G.main, id); + test_all_objects_materials(bmain, id); + DAG_relations_tag_update(bmain); } } -Material *BKE_material_pop_id(ID *id, int index_i, bool update_data) +Material *BKE_material_pop_id(Main *bmain, ID *id, int index_i, bool update_data) { short index = (short)index_i; Material *ret = NULL; @@ -472,13 +476,15 @@ Material *BKE_material_pop_id(ID *id, int index_i, bool update_data) /* decrease mat_nr index */ material_data_index_remove_id(id, index); } + + DAG_relations_tag_update(bmain); } } return ret; } -void BKE_material_clear_id(struct ID *id, bool update_data) +void BKE_material_clear_id(Main *bmain, ID *id, bool update_data) { Material ***matar; if ((matar = give_matarar_id(id))) { @@ -497,6 +503,8 @@ void BKE_material_clear_id(struct ID *id, bool update_data) /* decrease mat_nr index */ material_data_index_clear_id(id); } + + DAG_relations_tag_update(bmain); } } @@ -553,7 +561,7 @@ Material *give_node_material(Material *ma) return NULL; } -void BKE_material_resize_object(Object *ob, const short totcol, bool do_id_user) +void BKE_material_resize_object(Main *bmain, Object *ob, const short totcol, bool do_id_user) { Material **newmatar; char *newmatbits; @@ -590,6 +598,8 @@ void BKE_material_resize_object(Object *ob, const short totcol, bool do_id_user) ob->totcol = totcol; if (ob->totcol && ob->actcol == 0) ob->actcol = 1; if (ob->actcol > ob->totcol) ob->actcol = ob->totcol; + + DAG_relations_tag_update(bmain); } void test_object_materials(Object *ob, ID *id) @@ -601,7 +611,7 @@ void test_object_materials(Object *ob, ID *id) return; } - BKE_material_resize_object(ob, *totcol, false); + BKE_material_resize_object(G.main, ob, *totcol, false); } void test_all_objects_materials(Main *bmain, ID *id) @@ -617,7 +627,7 @@ void test_all_objects_materials(Main *bmain, ID *id) BKE_main_lock(bmain); for (ob = bmain->object.first; ob; ob = ob->id.next) { if (ob->data == id) { - BKE_material_resize_object(ob, *totcol, false); + BKE_material_resize_object(bmain, ob, *totcol, false); } } BKE_main_unlock(bmain); @@ -1881,7 +1891,7 @@ static short mesh_getmaterialnumber(Mesh *me, Material *ma) /* append material */ static short mesh_addmaterial(Mesh *me, Material *ma) { - BKE_material_append_id(&me->id, NULL); + BKE_material_append_id(G.main, &me->id, NULL); me->mat[me->totcol - 1] = ma; id_us_plus(&ma->id); @@ -2020,7 +2030,7 @@ static void convert_tfacematerial(Main *main, Material *ma) /* remove material from mesh */ for (a = 0; a < me->totcol; ) { if (me->mat[a] == ma) { - BKE_material_pop_id(&me->id, a, true); + BKE_material_pop_id(main, &me->id, a, true); } else { a++; diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c index 733e9030056..ba3aef81514 100644 --- a/source/blender/blenkernel/intern/mesh.c +++ b/source/blender/blenkernel/intern/mesh.c @@ -2197,8 +2197,10 @@ Mesh *BKE_mesh_new_from_object( { Mesh *tmpmesh; Curve *tmpcu = NULL, *copycu; - int render = settings == eModifierMode_Render, i; - int cage = !apply_modifiers; + int i; + const bool render = (settings == eModifierMode_Render); + const bool cage = !apply_modifiers; + bool do_mat_id_us = true; /* perform the mesh extraction based on type */ switch (ob->type) { @@ -2268,6 +2270,12 @@ Mesh *BKE_mesh_new_from_object( BKE_mesh_texspace_copy_from_object(tmpmesh, ob); BKE_libblock_free_us(bmain, tmpobj); + + /* XXX The curve to mesh conversion is convoluted... But essentially, BKE_mesh_from_nurbs_displist() + * already transfers the ownership of materials from the temp copy of the Curve ID to the new + * Mesh ID, so we do not want to increase materials' usercount later. */ + do_mat_id_us = false; + break; } @@ -2315,8 +2323,11 @@ Mesh *BKE_mesh_new_from_object( if (cage) { /* copies the data */ tmpmesh = BKE_mesh_copy(bmain, ob->data); - /* if not getting the original caged mesh, get final derived mesh */ + + /* XXX BKE_mesh_copy() already handles materials usercount. */ + do_mat_id_us = false; } + /* if not getting the original caged mesh, get final derived mesh */ else { /* Make a dummy mesh, saves copying */ DerivedMesh *dm; @@ -2360,7 +2371,7 @@ Mesh *BKE_mesh_new_from_object( tmpmesh->mat[i] = ob->matbits[i] ? ob->mat[i] : tmpcu->mat[i]; - if (tmpmesh->mat[i]) { + if (do_mat_id_us && tmpmesh->mat[i]) { id_us_plus(&tmpmesh->mat[i]->id); } } @@ -2379,7 +2390,7 @@ Mesh *BKE_mesh_new_from_object( /* are we an object material or data based? */ tmpmesh->mat[i] = ob->matbits[i] ? ob->mat[i] : tmpmb->mat[i]; - if (tmpmesh->mat[i]) { + if (do_mat_id_us && tmpmesh->mat[i]) { id_us_plus(&tmpmesh->mat[i]->id); } } @@ -2399,7 +2410,7 @@ Mesh *BKE_mesh_new_from_object( /* are we an object material or data based? */ tmpmesh->mat[i] = ob->matbits[i] ? ob->mat[i] : origmesh->mat[i]; - if (tmpmesh->mat[i]) { + if (do_mat_id_us && tmpmesh->mat[i]) { id_us_plus(&tmpmesh->mat[i]->id); } } diff --git a/source/blender/blenkernel/intern/movieclip.c b/source/blender/blenkernel/intern/movieclip.c index 0d362086134..6794a8e8f93 100644 --- a/source/blender/blenkernel/intern/movieclip.c +++ b/source/blender/blenkernel/intern/movieclip.c @@ -1033,7 +1033,7 @@ static ImBuf *get_stable_cached_frame(MovieClip *clip, MovieClipUser *user, ImBu stableibuf = cache->stabilized.ibuf; - BKE_tracking_stabilization_data_get(&clip->tracking, clip_framenr, stableibuf->x, stableibuf->y, tloc, &tscale, &tangle); + BKE_tracking_stabilization_data_get(clip, clip_framenr, stableibuf->x, stableibuf->y, tloc, &tscale, &tangle); /* check for stabilization parameters */ if (tscale != cache->stabilized.scale || @@ -1057,7 +1057,7 @@ static ImBuf *put_stabilized_frame_to_cache(MovieClip *clip, MovieClipUser *user float tloc[2], tscale, tangle; int clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(clip, framenr); - stableibuf = BKE_tracking_stabilize_frame(&clip->tracking, clip_framenr, ibuf, tloc, &tscale, &tangle); + stableibuf = BKE_tracking_stabilize_frame(clip, clip_framenr, ibuf, tloc, &tscale, &tangle); copy_v2_v2(cache->stabilized.loc, tloc); @@ -1247,8 +1247,6 @@ static void free_buffers(MovieClip *clip) IMB_free_anim(clip->anim); clip->anim = NULL; } - - BKE_animdata_free((ID *) clip, false); } void BKE_movieclip_clear_cache(MovieClip *clip) @@ -1270,8 +1268,6 @@ void BKE_movieclip_reload(MovieClip *clip) /* clear cache */ free_buffers(clip); - clip->tracking.stabilization.ok = false; - /* update clip source */ detect_clip_source(clip); @@ -1489,6 +1485,7 @@ void BKE_movieclip_free(MovieClip *clip) free_buffers(clip); BKE_tracking_free(&clip->tracking); + BKE_animdata_free((ID *) clip, false); } MovieClip *BKE_movieclip_copy(Main *bmain, MovieClip *clip) diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index 84e7b855592..b03eaa5bf85 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -1148,7 +1148,7 @@ Object *BKE_object_copy_ex(Main *bmain, Object *ob, bool copy_caches) /* increase user numbers */ id_us_plus((ID *)obn->data); id_us_plus((ID *)obn->gpd); - id_lib_extern((ID *)obn->dup_group); + id_us_plus((ID *)obn->dup_group); for (a = 0; a < obn->totcol; a++) id_us_plus((ID *)obn->mat[a]); diff --git a/source/blender/blenkernel/intern/object_dupli.c b/source/blender/blenkernel/intern/object_dupli.c index 21a0246353e..14cc5ec0849 100644 --- a/source/blender/blenkernel/intern/object_dupli.c +++ b/source/blender/blenkernel/intern/object_dupli.c @@ -221,31 +221,39 @@ static void make_child_duplis(const DupliContext *ctx, void *userdata, MakeChild if (ctx->group) { unsigned int lay = ctx->group->layer; + int groupid = 0; GroupObject *go; - for (go = ctx->group->gobject.first; go; go = go->next) { + for (go = ctx->group->gobject.first; go; go = go->next, groupid++) { Object *ob = go->ob; if ((ob->lay & lay) && ob != obedit && is_child(ob, parent)) { + DupliContext pctx; + copy_dupli_context(&pctx, ctx, ctx->object, NULL, groupid, false); + /* mballs have a different dupli handling */ if (ob->type != OB_MBALL) ob->flag |= OB_DONE; /* doesnt render */ - make_child_duplis_cb(ctx, userdata, ob); + make_child_duplis_cb(&pctx, userdata, ob); } } } else { unsigned int lay = ctx->scene->lay; + int baseid = 0; Base *base; - for (base = ctx->scene->base.first; base; base = base->next) { + for (base = ctx->scene->base.first; base; base = base->next, baseid++) { Object *ob = base->object; if ((base->lay & lay) && ob != obedit && is_child(ob, parent)) { + DupliContext pctx; + copy_dupli_context(&pctx, ctx, ctx->object, NULL, baseid, false); + /* mballs have a different dupli handling */ if (ob->type != OB_MBALL) ob->flag |= OB_DONE; /* doesnt render */ - make_child_duplis_cb(ctx, userdata, ob); + make_child_duplis_cb(&pctx, userdata, ob); } } } diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c index 42b818b35a5..aecaf8537bb 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -3172,6 +3172,9 @@ void object_remove_particle_system(Scene *UNUSED(scene), Object *ob) /* clear particle system */ BLI_remlink(&ob->particlesystem, psys); + if (psys->part) { + id_us_min(&psys->part->id); + } psys_free(ob, psys); if (ob->particlesystem.first) @@ -4050,13 +4053,16 @@ void psys_get_dupli_texture(ParticleSystem *psys, ParticleSettings *part, uv[0] = uv[1] = 0.f; + /* Grid distribution doesn't support UV or emit from vertex mode */ + bool is_grid = (part->distr == PART_DISTR_GRID && part->from != PART_FROM_VERT); + if (cpa) { if ((part->childtype == PART_CHILD_FACES) && (psmd->dm_final != NULL)) { CustomData *mtf_data = psmd->dm_final->getTessFaceDataLayout(psmd->dm_final); const int uv_idx = CustomData_get_render_layer(mtf_data, CD_MTFACE); mtface = CustomData_get_layer_n(mtf_data, CD_MTFACE, uv_idx); - if (mtface) { + if (mtface && !is_grid) { mface = psmd->dm_final->getTessFaceData(psmd->dm_final, cpa->num, CD_MFACE); mtface += cpa->num; psys_interpolate_uvs(mtface, mface->v4, cpa->fuv, uv); @@ -4070,7 +4076,7 @@ void psys_get_dupli_texture(ParticleSystem *psys, ParticleSettings *part, } } - if ((part->from == PART_FROM_FACE) && (psmd->dm_final != NULL)) { + if ((part->from == PART_FROM_FACE) && (psmd->dm_final != NULL) && !is_grid) { CustomData *mtf_data = psmd->dm_final->getTessFaceDataLayout(psmd->dm_final); const int uv_idx = CustomData_get_render_layer(mtf_data, CD_MTFACE); mtface = CustomData_get_layer_n(mtf_data, CD_MTFACE, uv_idx); diff --git a/source/blender/blenkernel/intern/particle_system.c b/source/blender/blenkernel/intern/particle_system.c index 8e3e2f5d6d0..b4e951ce04a 100644 --- a/source/blender/blenkernel/intern/particle_system.c +++ b/source/blender/blenkernel/intern/particle_system.c @@ -2183,7 +2183,6 @@ static void basic_rotate(ParticleSettings *part, ParticleData *pa, float dfra, f * http://en.wikipedia.org/wiki/Newton's_method * ************************************************/ -#define COLLISION_MAX_COLLISIONS 10 #define COLLISION_MIN_RADIUS 0.001f #define COLLISION_MIN_DISTANCE 0.0001f #define COLLISION_ZERO 0.00001f @@ -2570,10 +2569,6 @@ void BKE_psys_collision_neartest_cb(void *userdata, int index, const BVHTreeRay pce.inside = 0; pce.index = index; - /* don't collide with same face again */ - if (col->hit == col->current && col->pce.index == index && col->pce.tot == 3) - return; - collision = collision_sphere_to_tri(col, ray->radius, &pce, &t); if (col->pce.inside == 0) { collision += collision_sphere_to_edges(col, ray->radius, &pce, &t); @@ -2609,8 +2604,17 @@ static int collision_detect(ParticleData *pa, ParticleCollision *col, BVHTreeRay hit->dist = col->original_ray_length = 0.000001f; for (coll = colliders->first; coll; coll=coll->next) { - /* for boids: don't check with current ground object */ - if (coll->ob == col->skip) + /* for boids: don't check with current ground object; also skip if permeated */ + bool skip = false; + + for (int i = 0; i < col->skip_count; i++) { + if (coll->ob == col->skip[i]) { + skip = true; + break; + } + } + + if (skip) continue; /* particles should not collide with emitter at birth */ @@ -2746,7 +2750,7 @@ static int collision_response(ParticleData *pa, ParticleCollision *col, BVHTreeR if (through==0 && ((vc_dot>0.0f && v0_dot>0.0f && vc_dot>v0_dot) || (vc_dot<0.0f && v0_dot<0.0f && vc_dot<v0_dot))) mul_v3_v3fl(v0_nor, pce->nor, vc_dot); else if (v0_dot > 0.f) - mul_v3_v3fl(v0_nor, pce->nor, vc_dot + (through ? -1.0f : 1.0f) * v0_dot); + mul_v3_v3fl(v0_nor, pce->nor, vc_dot + v0_dot); else mul_v3_v3fl(v0_nor, pce->nor, vc_dot + (through ? 1.0f : -1.0f) * v0_dot); @@ -2801,8 +2805,10 @@ static int collision_response(ParticleData *pa, ParticleCollision *col, BVHTreeR col->f = f; } - col->prev = col->hit; - col->prev_index = hit->index; + /* if permeability random roll succeeded, disable collider for this sim step */ + if (through) { + col->skip[col->skip_count++] = col->hit; + } return 1; } @@ -2863,16 +2869,16 @@ static void collision_check(ParticleSimulationData *sim, int p, float dfra, floa if (part->phystype == PART_PHYS_BOIDS && part->boids->options & BOID_ALLOW_LAND) { col.boid = 1; col.boid_z = pa->state.co[2]; - col.skip = pa->boid->ground; + col.skip[col.skip_count++] = pa->boid->ground; } /* 10 iterations to catch multiple collisions */ - while (collision_count < COLLISION_MAX_COLLISIONS) { + while (collision_count < PARTICLE_COLLISION_MAX_COLLISIONS) { if (collision_detect(pa, &col, &hit, sim->colliders)) { collision_count++; - if (collision_count == COLLISION_MAX_COLLISIONS) + if (collision_count == PARTICLE_COLLISION_MAX_COLLISIONS) collision_fail(pa, &col); else if (collision_response(pa, &col, &hit, part->flag & PART_DIE_ON_COL, part->flag & PART_ROT_DYN)==0) return; @@ -3919,7 +3925,7 @@ static void system_step(ParticleSimulationData *sim, float cfra, const bool use_ /* 2. try to read from the cache */ if (pid) { - int cache_result = BKE_ptcache_read(pid, cache_cfra); + int cache_result = BKE_ptcache_read(pid, cache_cfra, true); if (ELEM(cache_result, PTCACHE_READ_EXACT, PTCACHE_READ_INTERPOLATED)) { cached_step(sim, cfra); diff --git a/source/blender/blenkernel/intern/pointcache.c b/source/blender/blenkernel/intern/pointcache.c index 69a98c06000..30eb8dcb287 100644 --- a/source/blender/blenkernel/intern/pointcache.c +++ b/source/blender/blenkernel/intern/pointcache.c @@ -1137,13 +1137,13 @@ static int ptcache_smoke_openvdb_read(struct OpenVDBReader *reader, void *smoke_ OpenVDB_import_grid_fl(reader, "density", &dens, sds->res_wt); - if (fluid_fields & SM_ACTIVE_FIRE) { + if (cache_fields & SM_ACTIVE_FIRE) { OpenVDB_import_grid_fl(reader, "flame", &flame, sds->res_wt); OpenVDB_import_grid_fl(reader, "fuel", &fuel, sds->res_wt); OpenVDB_import_grid_fl(reader, "react", &react, sds->res_wt); } - if (fluid_fields & SM_ACTIVE_COLORS) { + if (cache_fields & SM_ACTIVE_COLORS) { OpenVDB_import_grid_vec(reader, "color", &r, &g, &b, sds->res_wt); } @@ -2632,7 +2632,7 @@ static int ptcache_interpolate(PTCacheID *pid, float cfra, int cfra1, int cfra2) } /* reads cache from disk or memory */ /* possible to get old or interpolated result */ -int BKE_ptcache_read(PTCacheID *pid, float cfra) +int BKE_ptcache_read(PTCacheID *pid, float cfra, bool no_extrapolate_old) { int cfrai = (int)floor(cfra), cfra1=0, cfra2=0; int ret = 0; @@ -2658,10 +2658,17 @@ int BKE_ptcache_read(PTCacheID *pid, float cfra) return 0; /* don't read old cache if already simulated past cached frame */ - if (cfra1 == 0 && cfra2 && cfra2 <= pid->cache->simframe) - return 0; - if (cfra1 && cfra1 == cfra2) - return 0; + if (no_extrapolate_old) { + if (cfra1 == 0 && cfra2 && cfra2 <= pid->cache->simframe) + return 0; + if (cfra1 && cfra1 == cfra2) + return 0; + } + else { + /* avoid calling interpolate between the same frame values */ + if (cfra1 && cfra1 == cfra2) + cfra1 = 0; + } if (cfra1) { if (pid->file_type == PTCACHE_FILE_OPENVDB && pid->read_openvdb_stream) { diff --git a/source/blender/blenkernel/intern/rigidbody.c b/source/blender/blenkernel/intern/rigidbody.c index 0f1f9b4bdf7..c3ae5736aa9 100644 --- a/source/blender/blenkernel/intern/rigidbody.c +++ b/source/blender/blenkernel/intern/rigidbody.c @@ -1560,14 +1560,16 @@ void BKE_rigidbody_do_simulation(Scene *scene, float ctime) /* try to read from cache */ // RB_TODO deal with interpolated, old and baked results - if (BKE_ptcache_read(&pid, ctime)) { + bool can_simulate = (ctime == rbw->ltime + 1) && !(cache->flag & PTCACHE_BAKED); + + if (BKE_ptcache_read(&pid, ctime, can_simulate)) { BKE_ptcache_validate(cache, (int)ctime); rbw->ltime = ctime; return; } /* advance simulation, we can only step one frame forward */ - if (ctime == rbw->ltime + 1 && !(cache->flag & PTCACHE_BAKED)) { + if (can_simulate) { /* write cache for first frame when on second frame */ if (rbw->ltime == startframe && (cache->flag & PTCACHE_OUTDATED || cache->last_exact == 0)) { BKE_ptcache_write(&pid, startframe); diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index 4a1d279545e..acf6a313989 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -65,6 +65,7 @@ #include "BKE_animsys.h" #include "BKE_action.h" #include "BKE_armature.h" +#include "BKE_cachefile.h" #include "BKE_colortools.h" #include "BKE_depsgraph.h" #include "BKE_editmesh.h" @@ -161,7 +162,6 @@ Scene *BKE_scene_copy(Main *bmain, Scene *sce, int type) if (type == SCE_COPY_EMPTY) { ListBase rl, rv; - /* XXX. main should become an arg */ scen = BKE_scene_add(bmain, sce->id.name + 2); rl = scen->r.layers; @@ -290,7 +290,7 @@ Scene *BKE_scene_copy(Main *bmain, Scene *sce, int type) /* duplicate Grease Pencil Drawing Brushes */ BLI_listbase_clear(&ts->gp_brushes); for (bGPDbrush *brush = sce->toolsettings->gp_brushes.first; brush; brush = brush->next) { - bGPDbrush *newbrush = gpencil_brush_duplicate(brush); + bGPDbrush *newbrush = BKE_gpencil_brush_duplicate(brush); BLI_addtail(&ts->gp_brushes, newbrush); } @@ -342,7 +342,7 @@ Scene *BKE_scene_copy(Main *bmain, Scene *sce, int type) /* grease pencil */ if (scen->gpd) { if (type == SCE_COPY_FULL) { - scen->gpd = gpencil_data_duplicate(bmain, scen->gpd, false); + scen->gpd = BKE_gpencil_data_duplicate(bmain, scen->gpd, false); } else if (type == SCE_COPY_EMPTY) { scen->gpd = NULL; @@ -441,7 +441,7 @@ void BKE_scene_free(Scene *sce) MEM_freeN(sce->toolsettings->uvsculpt); } /* free Grease Pencil Drawing Brushes */ - free_gpencil_brushes(&sce->toolsettings->gp_brushes); + BKE_gpencil_free_brushes(&sce->toolsettings->gp_brushes); BLI_freelistN(&sce->toolsettings->gp_brushes); BKE_paint_free(&sce->toolsettings->imapaint.paint); @@ -1559,10 +1559,11 @@ static void print_threads_statistics(ThreadedObjectUpdateState *state) #else finish_time = PIL_check_seconds_timer(); tot_thread = BLI_system_thread_count(); + int total_objects = 0; for (i = 0; i < tot_thread; i++) { - int total_objects = 0; - double total_time = 0.0; + int thread_total_objects = 0; + double thread_total_time = 0.0; StatisicsEntry *entry; if (state->has_updated_objects) { @@ -1571,11 +1572,14 @@ static void print_threads_statistics(ThreadedObjectUpdateState *state) entry; entry = entry->next) { - total_objects++; - total_time += entry->duration; + thread_total_objects++; + thread_total_time += entry->duration; } - printf("Thread %d: total %d objects in %f sec.\n", i, total_objects, total_time); + printf("Thread %d: total %d objects in %f sec.\n", + i, + thread_total_objects, + thread_total_time); for (entry = state->statistics[i].first; entry; @@ -1583,12 +1587,16 @@ static void print_threads_statistics(ThreadedObjectUpdateState *state) { printf(" %s in %f sec\n", entry->object->id.name + 2, entry->duration); } + + total_objects += thread_total_objects; } BLI_freelistN(&state->statistics[i]); } if (state->has_updated_objects) { - printf("Scene update in %f sec\n", finish_time - state->base_time); + printf("Scene updated %d objects in %f sec\n", + total_objects, + finish_time - state->base_time); } #endif } @@ -1926,6 +1934,9 @@ void BKE_scene_update_for_newframe_ex(EvaluationContext *eval_ctx, Main *bmain, BKE_mask_evaluate_all_masks(bmain, ctime, true); + /* Update animated cache files for modifiers. */ + BKE_cachefile_update_frame(bmain, sce, ctime, (((double)sce->r.frs_sec) / (double)sce->r.frs_sec_base)); + #ifdef POSE_ANIMATION_WORKAROUND scene_armature_depsgraph_workaround(bmain); #endif diff --git a/source/blender/blenkernel/intern/sequencer.c b/source/blender/blenkernel/intern/sequencer.c index 6067a8b2d9b..c240aa27343 100644 --- a/source/blender/blenkernel/intern/sequencer.c +++ b/source/blender/blenkernel/intern/sequencer.c @@ -1656,6 +1656,9 @@ static bool seq_proxy_get_fname(Editing *ed, Sequence *seq, int cfra, int render else if ((proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_DIR) && (proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_FILE)) { BLI_strncpy(dir, seq->strip->proxy->dir, sizeof(dir)); } + else if (proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_FILE) { + BLI_strncpy(dir, seq->strip->proxy->dir, sizeof(dir)); + } else if (sanim && sanim->anim && (proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_DIR)) { char fname[FILE_MAXFILE]; BLI_strncpy(dir, seq->strip->proxy->dir, sizeof(dir)); @@ -1675,13 +1678,21 @@ static bool seq_proxy_get_fname(Editing *ed, Sequence *seq, int cfra, int render if (view_id > 0) BLI_snprintf(suffix, sizeof(suffix), "_%d", view_id); - if (proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_FILE && sanim && sanim->anim && + if (proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_FILE && ed->proxy_storage != SEQ_EDIT_PROXY_DIR_STORAGE) { - BLI_join_dirfile(name, PROXY_MAXFILE, - dir, proxy->file); - BLI_path_abs(name, G.main->name); - BLI_snprintf(name, PROXY_MAXFILE, "%s_%s", name, suffix); + char fname[FILE_MAXFILE]; + BLI_join_dirfile(fname, PROXY_MAXFILE, dir, proxy->file); + BLI_path_abs(fname, G.main->name); + if (suffix[0] != '\0') { + /* TODO(sergey): This will actually append suffix after extension + * which is weird but how was originally coded in multiview branch. + */ + BLI_snprintf(name, PROXY_MAXFILE, "%s_%s", fname, suffix); + } + else { + BLI_strncpy(name, fname, PROXY_MAXFILE); + } return true; } @@ -5595,3 +5606,31 @@ int BKE_sequencer_find_next_prev_edit( return best_frame; } + +static void sequencer_all_free_anim_ibufs(ListBase *seqbase, int cfra) +{ + for (Sequence *seq = seqbase->first; seq != NULL; seq = seq->next) { + if (seq->enddisp < cfra || seq->startdisp > cfra) { + BKE_sequence_free_anim(seq); + } + if (seq->type == SEQ_TYPE_META) { + sequencer_all_free_anim_ibufs(&seq->seqbase, cfra); + } + } +} + +void BKE_sequencer_all_free_anim_ibufs(int cfra) +{ + BKE_sequencer_cache_cleanup(); + for (Scene *scene = G.main->scene.first; + scene != NULL; + scene = scene->id.next) + { + Editing *ed = BKE_sequencer_editing_get(scene, false); + if (ed == NULL) { + /* Ignore scenes without sequencer. */ + continue; + } + sequencer_all_free_anim_ibufs(&ed->seqbase, cfra); + } +} diff --git a/source/blender/blenkernel/intern/smoke.c b/source/blender/blenkernel/intern/smoke.c index 43569f9ded2..f622d541f3a 100644 --- a/source/blender/blenkernel/intern/smoke.c +++ b/source/blender/blenkernel/intern/smoke.c @@ -2737,19 +2737,18 @@ static void smokeModifier_process(SmokeModifierData *smd, Scene *scene, Object * return; } + /* only calculate something when we advanced a single frame */ + /* don't simulate if viewing start frame, but scene frame is not real start frame */ + bool can_simulate = (framenr == (int)smd->time + 1) && (framenr == scene->r.cfra); + /* try to read from cache */ - if (BKE_ptcache_read(&pid, (float)framenr) == PTCACHE_READ_EXACT) { + if (BKE_ptcache_read(&pid, (float)framenr, can_simulate) == PTCACHE_READ_EXACT) { BKE_ptcache_validate(cache, framenr); smd->time = framenr; return; } - /* only calculate something when we advanced a single frame */ - if (framenr != (int)smd->time + 1) - return; - - /* don't simulate if viewing start frame, but scene frame is not real start frame */ - if (framenr != scene->r.cfra) + if (!can_simulate) return; #ifdef DEBUG_TIME @@ -3052,9 +3051,15 @@ float smoke_get_velocity_at(struct Object *ob, float position[3], float velocity int smoke_get_data_flags(SmokeDomainSettings *sds) { int flags = 0; - if (smoke_has_heat(sds->fluid)) flags |= SM_ACTIVE_HEAT; - if (smoke_has_fuel(sds->fluid)) flags |= SM_ACTIVE_FIRE; - if (smoke_has_colors(sds->fluid)) flags |= SM_ACTIVE_COLORS; + + if (sds->fluid) { + if (smoke_has_heat(sds->fluid)) + flags |= SM_ACTIVE_HEAT; + if (smoke_has_fuel(sds->fluid)) + flags |= SM_ACTIVE_FIRE; + if (smoke_has_colors(sds->fluid)) + flags |= SM_ACTIVE_COLORS; + } return flags; } diff --git a/source/blender/blenkernel/intern/softbody.c b/source/blender/blenkernel/intern/softbody.c index 901926dd8d4..03cf33083da 100644 --- a/source/blender/blenkernel/intern/softbody.c +++ b/source/blender/blenkernel/intern/softbody.c @@ -3719,9 +3719,12 @@ void sbObjectStep(Scene *scene, Object *ob, float cfra, float (*vertexCos)[3], i } /* try to read from cache */ - cache_result = BKE_ptcache_read(&pid, (float)framenr+scene->r.subframe); + bool can_simulate = (framenr == sb->last_frame+1) && !(cache->flag & PTCACHE_BAKED); - if (cache_result == PTCACHE_READ_EXACT || cache_result == PTCACHE_READ_INTERPOLATED) { + cache_result = BKE_ptcache_read(&pid, (float)framenr+scene->r.subframe, can_simulate); + + if (cache_result == PTCACHE_READ_EXACT || cache_result == PTCACHE_READ_INTERPOLATED || + (!can_simulate && cache_result == PTCACHE_READ_OLD)) { softbody_to_object(ob, vertexCos, numVerts, sb->local); BKE_ptcache_validate(cache, framenr); @@ -3742,7 +3745,7 @@ void sbObjectStep(Scene *scene, Object *ob, float cfra, float (*vertexCos)[3], i return; } - if (framenr!=sb->last_frame+1) + if (!can_simulate) return; /* if on second frame, write cache for first frame */ diff --git a/source/blender/blenkernel/intern/subsurf_ccg.c b/source/blender/blenkernel/intern/subsurf_ccg.c index e876bf43809..60f1eb6b98c 100644 --- a/source/blender/blenkernel/intern/subsurf_ccg.c +++ b/source/blender/blenkernel/intern/subsurf_ccg.c @@ -2707,7 +2707,6 @@ static void ccgDM_drawFacesSolid(DerivedMesh *dm, float (*partial_redraw_planes) GPU_vertex_setup(dm); GPU_normal_setup(dm); GPU_triangle_setup(dm); - glShadeModel(GL_SMOOTH); for (a = 0; a < dm->drawObject->totmaterial; a++) { if (!setMaterial || setMaterial(dm->drawObject->materials[a].mat_nr + 1, NULL)) { GPU_buffer_draw_elements(dm->drawObject->triangles, GL_TRIANGLES, dm->drawObject->materials[a].start, @@ -2811,8 +2810,6 @@ static void ccgDM_drawMappedFacesGLSL(DerivedMesh *dm, } #endif - glShadeModel(GL_SMOOTH); - CCG_key_top_level(&key, ss); ccgdm_pbvh_update(ccgdm); @@ -2965,6 +2962,7 @@ static void ccgDM_drawMappedFacesGLSL(DerivedMesh *dm, } } + glShadeModel(GL_SMOOTH); #undef PASSATTRIB } else { @@ -3170,8 +3168,6 @@ static void ccgDM_drawMappedFacesGLSL(DerivedMesh *dm, MEM_freeN(mat_orig_to_new); MEM_freeN(matconv); } - - glShadeModel(GL_SMOOTH); } static void ccgDM_drawFacesGLSL(DerivedMesh *dm, DMSetMaterial setMaterial) @@ -3369,6 +3365,7 @@ static void ccgDM_drawMappedFacesMat(DerivedMesh *dm, } } + glShadeModel(GL_SMOOTH); #undef PASSATTRIB } @@ -3503,7 +3500,6 @@ static void ccgDM_drawFacesTex_common(DerivedMesh *dm, next_actualFace = 0; - glShadeModel(GL_SMOOTH); /* lastFlag = 0; */ /* UNUSED */ for (mat_index = 0; mat_index < dm->drawObject->totmaterial; mat_index++) { GPUBufferMaterial *bufmat = dm->drawObject->materials + mat_index; @@ -3681,8 +3677,8 @@ static void ccgDM_drawMappedFaces(DerivedMesh *dm, if (do_draw) { glShadeModel(draw_smooth ? GL_SMOOTH : GL_FLAT); ccgSubSurf_drawGLMesh(ss, true, -1, -1); + glShadeModel(GL_SMOOTH); } - glShadeModel(GL_SMOOTH); return; } #endif @@ -3744,10 +3740,6 @@ static void ccgDM_drawMappedFaces(DerivedMesh *dm, GPU_basic_shader_stipple(GPU_SHADER_STIPPLE_QUARTTONE); } - /* no need to set shading mode to flat because - * normals are already used to change shading */ - glShadeModel(GL_SMOOTH); - for (S = 0; S < numVerts; S++) { CCGElem *faceGridData = ccgSubSurf_getFaceGridDataArray(ss, f, S); if (ln) { diff --git a/source/blender/blenkernel/intern/tracking.c b/source/blender/blenkernel/intern/tracking.c index a56fc0f9abe..a86606f1099 100644 --- a/source/blender/blenkernel/intern/tracking.c +++ b/source/blender/blenkernel/intern/tracking.c @@ -241,13 +241,9 @@ static void tracking_reconstruction_copy( /* Copy stabilization structure. */ static void tracking_stabilization_copy( - MovieTrackingStabilization *stabilization_dst, MovieTrackingStabilization *stabilization_src, - GHash *tracks_mapping) + MovieTrackingStabilization *stabilization_dst, MovieTrackingStabilization *stabilization_src) { *stabilization_dst = *stabilization_src; - if (stabilization_src->rot_track) { - stabilization_dst->rot_track = BLI_ghash_lookup(tracks_mapping, stabilization_src->rot_track); - } } /* Copy tracking object. */ @@ -284,7 +280,7 @@ void BKE_tracking_copy(MovieTracking *tracking_dst, MovieTracking *tracking_src) tracking_tracks_copy(&tracking_dst->tracks, &tracking_src->tracks, tracks_mapping); tracking_plane_tracks_copy(&tracking_dst->plane_tracks, &tracking_src->plane_tracks, tracks_mapping); tracking_reconstruction_copy(&tracking_dst->reconstruction, &tracking_src->reconstruction); - tracking_stabilization_copy(&tracking_dst->stabilization, &tracking_src->stabilization, tracks_mapping); + tracking_stabilization_copy(&tracking_dst->stabilization, &tracking_src->stabilization); if (tracking_src->act_track) { tracking_dst->act_track = BLI_ghash_lookup(tracks_mapping, tracking_src->act_track); } @@ -316,7 +312,7 @@ void BKE_tracking_copy(MovieTracking *tracking_dst, MovieTracking *tracking_src) } /* Initialize motion tracking settings to default values, - * used when new movie clip datablock is creating. + * used when new movie clip datablock is created. */ void BKE_tracking_settings_init(MovieTracking *tracking) { @@ -334,10 +330,22 @@ void BKE_tracking_settings_init(MovieTracking *tracking) tracking->settings.object_distance = 1; tracking->stabilization.scaleinf = 1.0f; + tracking->stabilization.anchor_frame = 1; + zero_v2(tracking->stabilization.target_pos); + tracking->stabilization.target_rot = 0.0f; + tracking->stabilization.scale = 1.0f; + + tracking->stabilization.act_track = 0; + tracking->stabilization.act_rot_track = 0; + tracking->stabilization.tot_track = 0; + tracking->stabilization.tot_rot_track = 0; + + tracking->stabilization.scaleinf = 1.0f; tracking->stabilization.locinf = 1.0f; tracking->stabilization.rotinf = 1.0f; tracking->stabilization.maxscale = 2.0f; tracking->stabilization.filter = TRACKING_FILTER_BILINEAR; + tracking->stabilization.flag |= TRACKING_SHOW_STAB_TRACKS; BKE_tracking_object_add(tracking, "Camera"); } @@ -552,6 +560,7 @@ MovieTrackingTrack *BKE_tracking_track_add(MovieTracking *tracking, ListBase *tr track->flag = settings->default_flag; track->algorithm_flag = settings->default_algorithm_flag; track->weight = settings->default_weight; + track->weight_stab = settings->default_weight; memset(&marker, 0, sizeof(marker)); marker.pos[0] = x; @@ -590,6 +599,12 @@ MovieTrackingTrack *BKE_tracking_track_duplicate(MovieTrackingTrack *track) new_track->markers = MEM_dupallocN(new_track->markers); + /* Orevent duplicate from being used for 2D stabilization. + * If necessary, it shall be added explicitly. + */ + new_track->flag &= ~TRACK_USE_2D_STAB; + new_track->flag &= ~TRACK_USE_2D_STAB_ROT; + return new_track; } diff --git a/source/blender/blenkernel/intern/tracking_stabilize.c b/source/blender/blenkernel/intern/tracking_stabilize.c index eb224020977..df42f253fdf 100644 --- a/source/blender/blenkernel/intern/tracking_stabilize.c +++ b/source/blender/blenkernel/intern/tracking_stabilize.c @@ -21,6 +21,7 @@ * Contributor(s): Blender Foundation, * Sergey Sharybin * Keir Mierle + * Ichthyostega * * ***** END GPL LICENSE BLOCK ***** */ @@ -28,292 +29,1484 @@ /** \file blender/blenkernel/intern/tracking_stabilize.c * \ingroup bke * - * This file contains implementation of 2D frame stabilization. + * This file contains implementation of 2D image stabilization. */ #include <limits.h> #include "DNA_movieclip_types.h" +#include "DNA_scene_types.h" +#include "DNA_anim_types.h" +#include "RNA_access.h" #include "BLI_utildefines.h" +#include "BLI_sort_utils.h" +#include "BLI_math_vector.h" #include "BLI_math.h" #include "BKE_tracking.h" +#include "BKE_movieclip.h" +#include "BKE_fcurve.h" +#include "BLI_ghash.h" +#include "MEM_guardedalloc.h" #include "IMB_imbuf_types.h" #include "IMB_imbuf.h" -/* Calculate median point of markers of tracks marked as used for - * 2D stabilization. - * - * NOTE: frame number should be in clip space, not scene space + +/* == Parameterization constants == */ + +/* When measuring the scale changes relative to the rotation pivot point, it + * might happen accidentally that a probe point (tracking point), which doesn't + * actually move on a circular path, gets very close to the pivot point, causing + * the measured scale contribution to go toward infinity. We damp this undesired + * effect by adding a bias (floor) to the measured distances, which will + * dominate very small distances and thus cause the corresponding track's + * contribution to diminish. + * Measurements happen in normalized (0...1) coordinates within a frame. + */ +static float SCALE_ERROR_LIMIT_BIAS = 0.01f; + +/* When to consider a track as completely faded out. + * This is used in conjunction with the "disabled" flag of the track + * to determine start positions, end positions and gaps + */ +static float EPSILON_WEIGHT = 0.005f; + + + +/* == private working data == */ + +/* Per track baseline for stabilization, defined at reference frame. + * A track's reference frame is chosen as close as possible to the (global) + * anchor_frame. Baseline holds the constant part of each track's contribution + * to the observed movement; it is calculated at initialization pass, using the + * measurement value at reference frame plus the average contribution to fill + * the gap between global anchor_frame and the reference frame for this track. + * This struct with private working data is associated to the local call context + * via `StabContext::private_track_data` */ -static bool stabilization_median_point_get(MovieTracking *tracking, int framenr, float median[2]) +typedef struct TrackStabilizationBase { + float stabilization_offset_base[2]; + + /* measured relative to translated pivot */ + float stabilization_rotation_base[2][2]; + + /* measured relative to translated pivot */ + float stabilization_scale_base; + + bool is_init_for_stabilization; + FCurve *track_weight_curve; +} TrackStabilizationBase; + +/* Tracks are reordered for initialization, starting as close as possible to + * anchor_frame + */ +typedef struct TrackInitOrder { + int sort_value; + int reference_frame; + MovieTrackingTrack *data; +} TrackInitOrder; + +/* Per frame private working data, for accessing possibly animated values. */ +typedef struct StabContext { + MovieClip *clip; + MovieTracking *tracking; + MovieTrackingStabilization *stab; + GHash *private_track_data; + FCurve *locinf; + FCurve *rotinf; + FCurve *scaleinf; + FCurve *target_pos[2]; + FCurve *target_rot; + FCurve *target_scale; + bool use_animation; +} StabContext; + + +static TrackStabilizationBase *access_stabilization_baseline_data( + StabContext *ctx, + MovieTrackingTrack *track) { - bool ok = false; - float min[2], max[2]; - MovieTrackingTrack *track; + return BLI_ghash_lookup(ctx->private_track_data, track); +} - INIT_MINMAX2(min, max); +static void attach_stabilization_baseline_data( + StabContext *ctx, + MovieTrackingTrack *track, + TrackStabilizationBase *private_data) +{ + return BLI_ghash_insert(ctx->private_track_data, track, private_data); +} - track = tracking->tracks.first; - while (track) { - if (track->flag & TRACK_USE_2D_STAB) { - MovieTrackingMarker *marker = BKE_tracking_marker_get(track, framenr); +static void discard_stabilization_baseline_data(void *val) +{ + if (val != NULL) { + MEM_freeN(val); + } +} + + +/* == access animated values for given frame == */ + +static FCurve *retrieve_stab_animation(MovieClip *clip, + const char *data_path, + int idx) +{ + return id_data_find_fcurve(&clip->id, + &clip->tracking.stabilization, + &RNA_MovieTrackingStabilization, + data_path, + idx, + NULL); +} + +static FCurve *retrieve_track_weight_animation(MovieClip *clip, + MovieTrackingTrack *track) +{ + return id_data_find_fcurve(&clip->id, + track, + &RNA_MovieTrackingTrack, + "weight_stab", + 0, + NULL); +} + +static float fetch_from_fcurve(FCurve *animationCurve, + int framenr, + StabContext *ctx, + float default_value) +{ + if (ctx && ctx->use_animation && animationCurve) { + int scene_framenr = BKE_movieclip_remap_clip_to_scene_frame(ctx->clip, + framenr); + return evaluate_fcurve(animationCurve, scene_framenr); + } + return default_value; +} + + +static float get_animated_locinf(StabContext *ctx, int framenr) +{ + return fetch_from_fcurve(ctx->locinf, framenr, ctx, ctx->stab->locinf); +} + +static float get_animated_rotinf(StabContext *ctx, int framenr) +{ + return fetch_from_fcurve(ctx->rotinf, framenr, ctx, ctx->stab->rotinf); +} + +static float get_animated_scaleinf(StabContext *ctx, int framenr) +{ + return fetch_from_fcurve(ctx->scaleinf, framenr, ctx, ctx->stab->scaleinf); +} + +static void get_animated_target_pos(StabContext *ctx, + int framenr, + float target_pos[2]) +{ + target_pos[0] = fetch_from_fcurve(ctx->target_pos[0], + framenr, + ctx, + ctx->stab->target_pos[0]); + target_pos[1] = fetch_from_fcurve(ctx->target_pos[1], + framenr, + ctx, + ctx->stab->target_pos[1]); +} + +static float get_animated_target_rot(StabContext *ctx, int framenr) +{ + return fetch_from_fcurve(ctx->target_rot, + framenr, + ctx, + ctx->stab->target_rot); +} + +static float get_animated_target_scale(StabContext *ctx, int framenr) +{ + return fetch_from_fcurve(ctx->target_scale, framenr, ctx, ctx->stab->scale); +} + +static float get_animated_weight(StabContext *ctx, + MovieTrackingTrack *track, + int framenr) +{ + TrackStabilizationBase *working_data = + access_stabilization_baseline_data(ctx, track); + if (working_data && working_data->track_weight_curve) { + int scene_framenr = BKE_movieclip_remap_clip_to_scene_frame(ctx->clip, + framenr); + return evaluate_fcurve(working_data->track_weight_curve, scene_framenr); + } + /* Use weight at global 'current frame' as fallback default. */ + return track->weight_stab; +} + +static void use_values_from_fcurves(StabContext *ctx, bool toggle) +{ + if (ctx != NULL) { + ctx->use_animation = toggle; + } +} + + +/* Prepare per call private working area. + * Used for access to possibly animated values: retrieve available F-curves. + */ +static StabContext *initialize_stabilization_working_context(MovieClip *clip) +{ + StabContext *ctx = MEM_callocN(sizeof(StabContext), + "2D stabilization animation runtime data"); + ctx->clip = clip; + ctx->tracking = &clip->tracking; + ctx->stab = &clip->tracking.stabilization; + ctx->private_track_data = BLI_ghash_ptr_new( + "2D stabilization per track private working data"); + ctx->locinf = retrieve_stab_animation(clip, "influence_location", 0); + ctx->rotinf = retrieve_stab_animation(clip, "influence_rotation", 0); + ctx->scaleinf = retrieve_stab_animation(clip, "influence_scale", 0); + ctx->target_pos[0] = retrieve_stab_animation(clip, "target_pos", 0); + ctx->target_pos[1] = retrieve_stab_animation(clip, "target_pos", 1); + ctx->target_rot = retrieve_stab_animation(clip, "target_rot", 0); + ctx->target_scale = retrieve_stab_animation(clip, "target_zoom", 0); + ctx->use_animation = true; + return ctx; +} + +/* Discard all private working data attached to this call context. + * NOTE: We allocate the record for the per track baseline contribution + * locally for each call context (i.e. call to + * BKE_tracking_stabilization_data_get() + * Thus it is correct to discard all allocations found within the + * corresponding _local_ GHash + */ +static void discard_stabilization_working_context(StabContext *ctx) +{ + if (ctx != NULL) { + BLI_ghash_free(ctx->private_track_data, + NULL, + discard_stabilization_baseline_data); + MEM_freeN(ctx); + } +} + +static bool is_init_for_stabilization(StabContext *ctx, + MovieTrackingTrack *track) +{ + TrackStabilizationBase *working_data = + access_stabilization_baseline_data(ctx, track); + return (working_data != NULL && working_data->is_init_for_stabilization); +} + +static bool is_usable_for_stabilization(StabContext *ctx, + MovieTrackingTrack *track) +{ + return (track->flag & TRACK_USE_2D_STAB) && + is_init_for_stabilization(ctx, track); +} + +static bool is_effectively_disabled(StabContext *ctx, + MovieTrackingTrack *track, + MovieTrackingMarker *marker) +{ + return (marker->flag & MARKER_DISABLED) || + (EPSILON_WEIGHT > get_animated_weight(ctx, track, marker->framenr)); +} + + +static int search_closest_marker_index(MovieTrackingTrack *track, + int ref_frame) +{ + MovieTrackingMarker *markers = track->markers; + int end = track->markersnr; + int i = track->last_marker; + + i = MAX2(0, i); + i = MIN2(i, end - 1); + for ( ; i < end - 1 && markers[i].framenr <= ref_frame; ++i); + for ( ; 0 < i && markers[i].framenr > ref_frame; --i); + + track->last_marker = i; + return i; +} + +static void retrieve_next_higher_usable_frame(StabContext *ctx, + MovieTrackingTrack *track, + int i, + int ref_frame, + int *next_higher) +{ + MovieTrackingMarker *markers = track->markers; + int end = track->markersnr; + BLI_assert(0 <= i && i < end); + + while (i < end && + (markers[i].framenr < ref_frame || + is_effectively_disabled(ctx, track, &markers[i]))) + { + ++i; + } + if (i < end && markers[i].framenr < *next_higher) { + BLI_assert(markers[i].framenr >= ref_frame); + *next_higher = markers[i].framenr; + } +} - minmax_v2v2_v2(min, max, marker->pos); +static void retrieve_next_lower_usable_frame(StabContext *ctx, + MovieTrackingTrack *track, + int i, + int ref_frame, + int *next_lower) +{ + MovieTrackingMarker *markers = track->markers; + BLI_assert(0 <= i && i < track->markersnr); + while (i >= 0 && + (markers[i].framenr > ref_frame || + is_effectively_disabled(ctx, track, &markers[i]))) + { + --i; + } + if (0 <= i && markers[i].framenr > *next_lower) { + BLI_assert(markers[i].framenr <= ref_frame); + *next_lower = markers[i].framenr; + } +} - ok = true; +/* Find closest frames with usable stabilization data. + * A frame counts as _usable_ when there is at least one track marked for + * translation stabilization, which has an enabled tracking marker at this very + * frame. We search both for the next lower and next higher position, to allow + * the caller to interpolate gaps and to extrapolate at the ends of the + * definition range. + * + * NOTE: Regarding performance note that the individual tracks will cache the + * last search position. + */ +static void find_next_working_frames(StabContext *ctx, + int framenr, + int *next_lower, + int *next_higher) +{ + for (MovieTrackingTrack *track = ctx->tracking->tracks.first; + track != NULL; + track = track->next) + { + if (is_usable_for_stabilization(ctx, track)) { + int startpoint = search_closest_marker_index(track, framenr); + retrieve_next_higher_usable_frame(ctx, + track, + startpoint, + framenr, + next_higher); + retrieve_next_lower_usable_frame(ctx, + track, + startpoint, + framenr, + next_lower); } + } +} - track = track->next; + +/* Find active (enabled) marker closest to the reference frame. */ +static MovieTrackingMarker *get_closest_marker(StabContext *ctx, + MovieTrackingTrack *track, + int ref_frame) +{ + int next_lower = MINAFRAME; + int next_higher = MAXFRAME; + int i = search_closest_marker_index(track, ref_frame); + retrieve_next_higher_usable_frame(ctx, track, i, ref_frame, &next_higher); + retrieve_next_lower_usable_frame(ctx, track, i, ref_frame, &next_lower); + + if ((next_higher - ref_frame) < (ref_frame - next_lower)) { + return BKE_tracking_marker_get_exact(track, next_higher); } + else { + return BKE_tracking_marker_get_exact(track, next_lower); + } +} - median[0] = (max[0] + min[0]) / 2.0f; - median[1] = (max[1] + min[1]) / 2.0f; - return ok; +/* Retrieve tracking data, if available and applicable for this frame. + * The returned weight value signals the validity; data recorded for this + * tracking marker on the exact requested frame is output with the full weight + * of this track, while gaps in the data sequence cause the weight to go to zero. + */ +static MovieTrackingMarker *get_tracking_data_point( + StabContext *ctx, + MovieTrackingTrack *track, + int framenr, + float *r_weight) +{ + MovieTrackingMarker *marker = BKE_tracking_marker_get_exact(track, framenr); + if (marker != NULL && !(marker->flag & MARKER_DISABLED)) { + *r_weight = get_animated_weight(ctx, track, framenr); + return marker; + } + else { + /* No marker at this frame (=gap) or marker disabled. */ + *r_weight = 0.0f; + return NULL; + } } -/* Calculate stabilization data (translation, scale and rotation) from - * given median of first and current frame medians, tracking data and - * frame number. + +/* Define the reference point for rotation/scale measurement and compensation. + * The stabilizator works by assuming the image was distorted by a affine linear + * transform, i.e. it was rotated and stretched around this reference point + * (pivot point) and then shifted laterally. Any scale and orientation changes + * will be picked up relative to this point. And later the image will be + * stabilized by rotating around this point. The result can only be as + * accurate as this pivot point actually matches the real rotation center + * of the actual movements. Thus any scheme to define a pivot point is + * always guesswork. * - * NOTE: frame number should be in clip space, not scene space + * As a simple default, we use the weighted average of the location markers + * of the current frame as pivot point. TODO It is planned to add further + * options, like e.g. anchoring the pivot point at the canvas. Moreover, + * it is planned to allow for a user controllable offset. */ -static void stabilization_calculate_data(MovieTracking *tracking, int framenr, int width, int height, - const float firstmedian[2], const float median[2], - float translation[2], float *scale, float *angle) +static void setup_pivot(const float ref_pos[2], float r_pivot[2]) { - MovieTrackingStabilization *stab = &tracking->stabilization; + zero_v2(r_pivot); /* TODO: add an animated offset position here. */ + add_v2_v2(r_pivot, ref_pos); +} - *scale = (stab->scale - 1.0f) * stab->scaleinf + 1.0f; - *angle = 0.0f; - translation[0] = (firstmedian[0] - median[0]) * width * (*scale); - translation[1] = (firstmedian[1] - median[1]) * height * (*scale); +/* Calculate the contribution of a single track at the time position (frame) of + * the given marker. Each track has a local reference frame, which is as close + * as possible to the global anchor_frame. Thus the translation contribution is + * comprised of the offset relative to the image position at that reference + * frame, plus a guess of the contribution for the time span between the + * anchor_frame and the local reference frame of this track. The constant part + * of this contribution is precomputed initially. At the anchor_frame, by + * definition the contribution of all tracks is zero, keeping the frame in place. + * + * track_ref is per track baseline contribution at reference frame; filled in at + * initialization + * marker is tracking data to use as contribution for current frame. + * result_offset is a total cumulated contribution of this track, + * relative to the stabilization anchor_frame, + * in normalized (0...1) coordinates. + */ +static void translation_contribution(TrackStabilizationBase *track_ref, + MovieTrackingMarker *marker, + float result_offset[2]) +{ + add_v2_v2v2(result_offset, + track_ref->stabilization_offset_base, + marker->pos); +} + +/* Similar to the ::translation_contribution(), the rotation contribution is + * comprised of the contribution by this individual track, and the averaged + * contribution from anchor_frame to the ref point of this track. + * - Contribution is in terms of angles, -pi < angle < +pi, and all averaging + * happens in this domain. + * - Yet the actual measurement happens as vector between pivot and the current + * tracking point + * - Currently we use the center of frame as approximation for the rotation pivot + * point. + * - Moreover, the pivot point has to be compensated for the already determined + * shift offset, in order to get the pure rotation around the pivot. + * To turn this into a _contribution_, the likewise corrected angle at the + * reference frame has to be subtracted, to get only the pure angle difference + * this tracking point has captured. + * - To get from vectors to angles, we have to go through an arcus tangens, + * which involves the issue of the definition range: the resulting angles will + * flip by 360deg when the measured vector passes from the 2nd to the third + * quadrant, thus messing up the average calculation. Since _any_ tracking + * point might be used, these problems are quite common in practice. + * - Thus we perform the subtraction of the reference and the addition of the + * baseline contribution in polar coordinates as simple addition of angles; + * since these parts are fixed, we can bake them into a rotation matrix. + * With this approach, the border of the arcus tangens definition range will + * be reached only, when the _whole_ contribution approaches +- 180deg, + * meaning we've already tilted the frame upside down. This situation is way + * less common and can be tolerated. + * - As an additional feature, when activated, also changes in image scale + * relative to the rotation center can be picked up. To handle those values + * in the same framework, we average the scales as logarithms. + * + * aspect is a total aspect ratio of the undistorted image (includes fame and + * pixel aspect). The function returns a quality factor, which can be used + * to damp the contributions of points in close proximity to the pivot point, + * since such contributions might be dominated by rounding errors and thus + * poison the calculated average. When the quality factor goes towards zero, + * the weight of this contribution should be reduced accordingly. + */ +static float rotation_contribution(TrackStabilizationBase *track_ref, + MovieTrackingMarker *marker, + const float aspect, + const float pivot[2], + float *result_angle, + float *result_scale) +{ + float len, quality; + float pos[2]; + sub_v2_v2v2(pos, marker->pos, pivot); - mul_v2_fl(translation, stab->locinf); + pos[0] *= aspect; + mul_m2v2(track_ref->stabilization_rotation_base, pos); - if ((stab->flag & TRACKING_STABILIZE_ROTATION) && stab->rot_track && stab->rotinf) { - MovieTrackingMarker *marker; - float a[2], b[2]; - float x0 = (float)width / 2.0f, y0 = (float)height / 2.0f; - float x = median[0] * width, y = median[1] * height; + *result_angle = atan2f(pos[1],pos[0]); - marker = BKE_tracking_marker_get(stab->rot_track, 1); - sub_v2_v2v2(a, marker->pos, firstmedian); - a[0] *= width; - a[1] *= height; + len = len_v2(pos); - marker = BKE_tracking_marker_get(stab->rot_track, framenr); - sub_v2_v2v2(b, marker->pos, median); - b[0] *= width; - b[1] *= height; + /* prevent points very close to the pivot point from poisoning the result */ + quality = 1 - expf(-len*len / SCALE_ERROR_LIMIT_BIAS*SCALE_ERROR_LIMIT_BIAS); + len += SCALE_ERROR_LIMIT_BIAS; - *angle = -atan2f(a[0] * b[1] - a[1] * b[0], a[0] * b[0] + a[1] * b[1]); - *angle *= stab->rotinf; + *result_scale = len * track_ref->stabilization_scale_base; + BLI_assert(0.0 < *result_scale); - /* convert to rotation around image center */ - translation[0] -= (x0 + (x - x0) * cosf(*angle) - (y - y0) * sinf(*angle) - x) * (*scale); - translation[1] -= (y0 + (x - x0) * sinf(*angle) + (y - y0) * cosf(*angle) - y) * (*scale); - } + return quality; } -/* Calculate factor of a scale, which will eliminate black areas - * appearing on the frame caused by frame translation. + +/* Workaround to allow for rotation around an arbitrary pivot point. + * Currently, the public API functions do not support this flexibility. + * Rather, rotation will always be applied around a fixed origin. + * As a workaround, we shift the image after rotation to match the + * desired rotation centre. And since this offset needs to be applied + * after the rotation and scaling, we can collapse it with the + * translation compensation, which is also a lateral shift (offset). + * The offset to apply is intended_pivot - rotated_pivot */ -static float stabilization_calculate_autoscale_factor(MovieTracking *tracking, int width, int height) +static void compensate_rotation_center(const int size, float aspect, + const float angle, + const float scale, + const float pivot[2], + float result_translation[2]) { - float firstmedian[2]; + const float origin[2] = {0.5f*aspect*size, 0.5f*size}; + float intended_pivot[2], rotated_pivot[2]; + float rotation_mat[2][2]; + + copy_v2_v2(intended_pivot, pivot); + copy_v2_v2(rotated_pivot, pivot); + rotate_m2(rotation_mat, +angle); + sub_v2_v2(rotated_pivot, origin); + mul_m2v2(rotation_mat, rotated_pivot); + mul_v2_fl(rotated_pivot, scale); + add_v2_v2(rotated_pivot, origin); + add_v2_v2(result_translation, intended_pivot); + sub_v2_v2(result_translation, rotated_pivot); +} + + +/* Weighted average of the per track cumulated contributions at given frame. + * Returns truth if all desired calculations could be done and all averages are + * available. + * + * NOTE: Even if the result is not `true`, the returned translation and angle + * are always sensible and as good as can be. Especially in the + * initialization phase we might not be able to get any average (yet) or + * get only a translation value. Since initialization visits tracks in a + * specific order, starting from anchor_frame, the result is logically + * correct non the less. But under normal operation conditions, + * a result of `false` should disable the stabilization function + */ +static bool average_track_contributions(StabContext *ctx, + int framenr, + float aspect, + float r_translation[2], + float r_pivot[2], + float *r_angle, + float *r_scale_step) +{ + bool ok; + float weight_sum; + MovieTrackingTrack *track; + MovieTracking *tracking = ctx->tracking; MovieTrackingStabilization *stab = &tracking->stabilization; - float aspect = tracking->camera.pixel_aspect; - - /* Early output if stabilization data is already up-to-date. */ - if (stab->ok) - return stab->scale; - - /* See comment in BKE_tracking_stabilization_data_get about first frame. */ - if (stabilization_median_point_get(tracking, 1, firstmedian)) { - int sfra = INT_MAX, efra = INT_MIN, cfra; - float scale = 1.0f; - MovieTrackingTrack *track; - - stab->scale = 1.0f; - - /* Calculate frame range of tracks used for stabilization. */ - track = tracking->tracks.first; - while (track) { - if (track->flag & TRACK_USE_2D_STAB || - ((stab->flag & TRACKING_STABILIZE_ROTATION) && track == stab->rot_track)) - { - sfra = min_ii(sfra, track->markers[0].framenr); - efra = max_ii(efra, track->markers[track->markersnr - 1].framenr); - } + float ref_pos[2]; + BLI_assert(stab->flag & TRACKING_2D_STABILIZATION); - track = track->next; + zero_v2(r_translation); + *r_scale_step = 0.0f; /* logarithm */ + *r_angle = 0.0f; + + zero_v2(ref_pos); + + ok = false; + weight_sum = 0.0f; + for (track = tracking->tracks.first; track; track = track->next) { + if (!is_init_for_stabilization(ctx, track)) { + continue; + } + if (track->flag & TRACK_USE_2D_STAB) { + float weight = 0.0f; + MovieTrackingMarker *marker = get_tracking_data_point(ctx, + track, + framenr, + &weight); + if (marker) { + TrackStabilizationBase *stabilization_base = + access_stabilization_baseline_data(ctx, track); + BLI_assert(stabilization_base != NULL); + float offset[2]; + weight_sum += weight; + translation_contribution(stabilization_base, marker, offset); + r_translation[0] += weight * offset[0]; + r_translation[1] += weight * offset[1]; + ref_pos[0] += weight * marker->pos[0]; + ref_pos[1] += weight * marker->pos[1]; + ok |= (weight_sum > EPSILON_WEIGHT); + } } + } + if (!ok) { + return false; + } + + ref_pos[0] /= weight_sum; + ref_pos[1] /= weight_sum; + r_translation[0] /= weight_sum; + r_translation[1] /= weight_sum; + setup_pivot(ref_pos, r_pivot); + + if (!(stab->flag & TRACKING_STABILIZE_ROTATION)) { + return ok; + } - /* For every frame we calculate scale factor needed to eliminate black - * area and choose largest scale factor as final one. + ok = false; + weight_sum = 0.0f; + for (track = tracking->tracks.first; track; track = track->next) { + if (!is_init_for_stabilization(ctx, track)) { + continue; + } + if (track->flag & TRACK_USE_2D_STAB_ROT) { + float weight = 0.0f; + MovieTrackingMarker *marker = get_tracking_data_point(ctx, + track, + framenr, + &weight); + if (marker) { + TrackStabilizationBase *stabilization_base = + access_stabilization_baseline_data(ctx, track); + BLI_assert(stabilization_base != NULL); + float rotation, scale, quality; + quality = rotation_contribution(stabilization_base, + marker, + aspect, + r_pivot, + &rotation, + &scale); + weight *= quality; + weight_sum += weight; + *r_angle += rotation * weight; + if (stab->flag & TRACKING_STABILIZE_SCALE) { + *r_scale_step += logf(scale) * weight; + } + else { + *r_scale_step = 0; + } + ok |= (weight_sum > EPSILON_WEIGHT); + } + } + } + if (ok) { + *r_scale_step /= weight_sum; + *r_angle /= weight_sum; + } + else { + /* We reach this point because translation could be calculated, + * but rotation/scale found no data to work on. */ - for (cfra = sfra; cfra <= efra; cfra++) { - float median[2]; - float translation[2], angle, tmp_scale; - int i; - float mat[4][4]; - float points[4][2] = {{0.0f, 0.0f}, {0.0f, height}, {width, height}, {width, 0.0f}}; - float si, co; + *r_scale_step = 0.0f; + *r_angle = 0.0f; + } + return true; +} - stabilization_median_point_get(tracking, cfra, median); - stabilization_calculate_data(tracking, cfra, width, height, firstmedian, median, translation, - &tmp_scale, &angle); +/* Calculate weight center of location tracks for given frame. + * This function performs similar calculations as average_track_contributions(), + * but does not require the tracks to be initialized for stabilisation. Moreover, + * when there is no usable tracking data for the given frame number, data from + * a neighbouring frame is used. Thus this function can be used to calculate + * a starting point on initialization. + */ +static void average_marker_positions(StabContext *ctx, int framenr, float r_ref_pos[2]) +{ + bool ok = false; + float weight_sum; + MovieTrackingTrack *track; + MovieTracking *tracking = ctx->tracking; - BKE_tracking_stabilization_data_to_mat4(width, height, aspect, translation, 1.0f, angle, mat); + zero_v2(r_ref_pos); + weight_sum = 0.0f; + for (track = tracking->tracks.first; track; track = track->next) { + if (track->flag & TRACK_USE_2D_STAB) { + float weight = 0.0f; + MovieTrackingMarker *marker = + get_tracking_data_point(ctx, track, framenr, &weight); + if (marker) { + weight_sum += weight; + r_ref_pos[0] += weight * marker->pos[0]; + r_ref_pos[1] += weight * marker->pos[1]; + ok |= (weight_sum > EPSILON_WEIGHT); + } + } + } + if (ok) { + r_ref_pos[0] /= weight_sum; + r_ref_pos[1] /= weight_sum; + } else { + /* No usable tracking data on any track on this frame. + * Use data from neighbouring frames to extrapolate... + */ + int next_lower = MINAFRAME; + int next_higher = MAXFRAME; + use_values_from_fcurves(ctx, true); + for (track = tracking->tracks.first; track; track = track->next) { + /* Note: we deliberately do not care if this track + * is already initialized for stabilisation */ + if (track->flag & TRACK_USE_2D_STAB) { + int startpoint = search_closest_marker_index(track, framenr); + retrieve_next_higher_usable_frame(ctx, + track, + startpoint, + framenr, + &next_higher); + retrieve_next_lower_usable_frame(ctx, + track, + startpoint, + framenr, + &next_lower); + } + } + if (next_lower >= MINFRAME) { + /* use next usable frame to the left. + * Also default to this frame when we're in a gap */ + average_marker_positions(ctx, next_lower, r_ref_pos); - si = sinf(angle); - co = cosf(angle); + } else if (next_higher < MAXFRAME) { + average_marker_positions(ctx, next_higher, r_ref_pos); + } + use_values_from_fcurves(ctx, false); + } +} - for (i = 0; i < 4; i++) { - int j; - float a[3] = {0.0f, 0.0f, 0.0f}, b[3] = {0.0f, 0.0f, 0.0f}; - copy_v3_v3(a, points[i]); - copy_v3_v3(b, points[(i + 1) % 4]); +/* Linear interpolation of data retrieved at two measurement points. + * This function is used to fill gaps in the middle of the covered area, + * at frames without any usable tracks for stabilization. + * + * framenr is a position to interpolate for. + * frame_a is a valid measurement point below framenr + * frame_b is a valid measurement point above framenr + * Returns truth if both measurements could actually be retrieved. + * Otherwise output parameters remain unaltered + */ +static bool interpolate_averaged_track_contributions(StabContext *ctx, + int framenr, + int frame_a, + int frame_b, + const float aspect, + float r_translation[2], + float r_pivot[2], + float *r_angle, + float *r_scale_step) +{ + float t, s; + float trans_a[2], trans_b[2]; + float angle_a, angle_b; + float scale_a, scale_b; + float pivot_a[2], pivot_b[2]; + bool success = false; + + BLI_assert(frame_a <= frame_b); + BLI_assert(frame_a <= framenr); + BLI_assert(framenr <= frame_b); + + t = ((float)framenr - frame_a) / (frame_b - frame_a); + s = 1.0f - t; + + success = average_track_contributions(ctx, frame_a, aspect, trans_a, pivot_a, &angle_a, &scale_a); + if (!success) { + return false; + } + success = average_track_contributions(ctx, frame_b, aspect, trans_b, pivot_b, &angle_b, &scale_b); + if (!success) { + return false; + } - mul_m4_v3(mat, a); - mul_m4_v3(mat, b); + interp_v2_v2v2(r_translation, trans_a, trans_b, t); + interp_v2_v2v2(r_pivot, pivot_a, pivot_b, t); + *r_scale_step = s * scale_a + t * scale_b; + *r_angle = s * angle_a + t * angle_b; + return true; +} - for (j = 0; j < 4; j++) { - float point[3] = {points[j][0], points[j][1], 0.0f}; - float v1[3], v2[3]; - sub_v3_v3v3(v1, b, a); - sub_v3_v3v3(v2, point, a); +/* Reorder tracks starting with those providing a tracking data frame + * closest to the global anchor_frame. Tracks with a gap at anchor_frame or + * starting farer away from anchor_frame altogether will be visited later. + * This allows to build up baseline contributions incrementally. + * + * order is an array for sorting the tracks. Must be of suitable size to hold + * all tracks. + * Returns number of actually usable tracks, can be less than the overall number + * of tracks. + * + * NOTE: After returning, the order array holds entries up to the number of + * usable tracks, appropriately sorted starting with the closest tracks. + * Initialization includes disabled tracks, since they might be enabled + * through automation later. + */ +static int establish_track_initialization_order(StabContext *ctx, + TrackInitOrder *order) +{ + size_t tracknr = 0; + MovieTrackingTrack *track; + MovieTracking *tracking = ctx->tracking; + int anchor_frame = tracking->stabilization.anchor_frame; - if (cross_v2v2(v1, v2) >= 0.0f) { - const float rotDx[4][2] = {{1.0f, 0.0f}, {0.0f, -1.0f}, {-1.0f, 0.0f}, {0.0f, 1.0f}}; - const float rotDy[4][2] = {{0.0f, 1.0f}, {1.0f, 0.0f}, {0.0f, -1.0f}, {-1.0f, 0.0f}}; + for (track = tracking->tracks.first; track != NULL; track = track->next) { + MovieTrackingMarker *marker; + order[tracknr].data = track; + marker = get_closest_marker(ctx, track, anchor_frame); + if (marker != NULL && + (track->flag & (TRACK_USE_2D_STAB | TRACK_USE_2D_STAB_ROT))) + { + order[tracknr].sort_value = abs(marker->framenr - anchor_frame); + order[tracknr].reference_frame = marker->framenr; + ++tracknr; + } + } + if (tracknr) { + qsort(order, tracknr, sizeof(TrackInitOrder), BLI_sortutil_cmp_int); + } + return tracknr; +} - float dx = translation[0] * rotDx[j][0] + translation[1] * rotDx[j][1], - dy = translation[0] * rotDy[j][0] + translation[1] * rotDy[j][1]; - float w, h, E, F, G, H, I, J, K, S; +/* Setup the constant part of this track's contribution to the determined frame + * movement. Tracks usually don't provide tracking data for every frame. Thus, + * for determining data at a given frame, we split up the contribution into a + * part covered by actual measurements on this track, and the initial gap + * between this track's reference frame and the global anchor_frame. + * The (missing) data for the gap can be substituted by the average offset + * observed by the other tracks covering the gap. This approximation doesn't + * introduce wrong data, but it records data with incorrect weight. A totally + * correct solution would require us to average the contribution per frame, and + * then integrate stepwise over all frames -- which of course would be way more + * expensive, especially for longer clips. To the contrary, our solution + * cumulates the total contribution per track and averages afterwards over all + * tracks; it can thus be calculated just based on the data of a single frame, + * plus the "baseline" for the reference frame, which is what we are computing + * here. + * + * Since we're averaging _contributions_, we have to calculate the _difference_ + * of the measured position at current frame and the position at the reference + * frame. But the "reference" part of this difference is constant and can thus + * be packed together with the baseline contribution into a single precomputed + * vector per track. + * + * In case of the rotation contribution, the principle is the same, but we have + * to compensate for the already determined translation and measure the pure + * rotation, simply because this is how we model the offset: shift plus rotation + * around the shifted rotation center. To circumvent problems with the + * definition range of the arcus tangens function, we perform this baseline + * addition and reference angle subtraction in polar coordinates and bake this + * operation into a precomputed rotation matrix. + * + * track is a track to be initialized to initialize + * reference_frame is a local frame for this track, the closest pick to the + * global anchor_frame. + * aspect is a total aspect ratio of the undistorted image (includes fame and + * pixel aspect). + * target_pos is a possibly animated target position as set by the user for + * the reference_frame + * average_translation is a value observed by the _other_ tracks for the gap + * between reference_frame and anchor_frame. This + * average must not contain contributions of frames + * not yet initialized + * average_angle in a similar way, the rotation value observed by the + * _other_ tracks. + * average_scale_step is an image scale factor observed on average by the other + * tracks for this frame. This value is recorded and + * averaged as logarithm. The recorded scale changes + * are damped for very small contributions, to limit + * the effect of probe points approaching the pivot + * too closely. + * + * NOTE: when done, this track is marked as initialized + */ +static void initialize_track_for_stabilization(StabContext *ctx, + MovieTrackingTrack *track, + int reference_frame, + float aspect, + const float average_translation[2], + const float pivot[2], + const float average_angle, + const float average_scale_step) +{ + float pos[2], angle, len; + TrackStabilizationBase *local_data = + access_stabilization_baseline_data(ctx, track); + MovieTrackingMarker *marker = + BKE_tracking_marker_get_exact(track, reference_frame); + /* Logic for initialization order ensures there *is* a marker on that + * very frame. + */ + BLI_assert(marker != NULL); + BLI_assert(local_data != NULL); - if (j % 2) { - w = (float)height / 2.0f; - h = (float)width / 2.0f; - } - else { - w = (float)width / 2.0f; - h = (float)height / 2.0f; - } + /* Per track baseline value for translation. */ + sub_v2_v2v2(local_data->stabilization_offset_base, + average_translation, + marker->pos); - E = -w * co + h * si; - F = -h * co - w * si; + /* Per track baseline value for rotation. */ + sub_v2_v2v2(pos, marker->pos, pivot); - if ((i % 2) == (j % 2)) { - G = -w * co - h * si; - H = h * co - w * si; - } - else { - G = w * co + h * si; - H = -h * co + w * si; - } + pos[0] *= aspect; + angle = average_angle - atan2f(pos[1],pos[0]); + rotate_m2(local_data->stabilization_rotation_base, angle); - I = F - H; - J = G - E; - K = G * F - E * H; + /* Per track baseline value for zoom. */ + len = len_v2(pos) + SCALE_ERROR_LIMIT_BIAS; + local_data->stabilization_scale_base = expf(average_scale_step) / len; - S = (-w * I - h * J) / (dx * I + dy * J + K); + local_data->is_init_for_stabilization = true; +} - scale = max_ff(scale, S); - } - } - } + +static void initialize_all_tracks(StabContext *ctx, float aspect) +{ + size_t i, track_cnt = 0; + MovieClip *clip = ctx->clip; + MovieTracking *tracking = ctx->tracking; + MovieTrackingTrack *track; + TrackInitOrder *order; + + /* Attempt to start initialization at anchor_frame. + * By definition, offset contribution is zero there. + */ + int reference_frame = tracking->stabilization.anchor_frame; + float average_angle=0, average_scale_step=0; + float average_translation[2], average_pos[2], pivot[2]; + zero_v2(average_translation); + zero_v2(pivot); + + /* Initialize private working data. */ + for (track = tracking->tracks.first; track != NULL; track = track->next) { + TrackStabilizationBase *local_data = + access_stabilization_baseline_data(ctx, track); + if (!local_data) { + local_data = MEM_callocN(sizeof(TrackStabilizationBase), + "2D stabilization per track baseline data"); + attach_stabilization_baseline_data(ctx, track, local_data); } + BLI_assert(local_data != NULL); + local_data->track_weight_curve = retrieve_track_weight_animation(clip, + track); + local_data->is_init_for_stabilization = false; - stab->scale = scale; + ++track_cnt; + } + if (!track_cnt) { + return; + } - if (stab->maxscale > 0.0f) - stab->scale = min_ff(stab->scale, stab->maxscale); + order = MEM_mallocN(track_cnt * sizeof(TrackInitOrder), + "stabilization track order"); + if (!order) { + return; } - else { - stab->scale = 1.0f; + + track_cnt = establish_track_initialization_order(ctx, order); + if (track_cnt == 0) { + goto cleanup; } - stab->ok = true; + /* starting point for pivot, before having initialized any track */ + average_marker_positions(ctx, reference_frame, average_pos); + setup_pivot(average_pos, pivot); + + for (i = 0; i < track_cnt; ++i) { + track = order[i].data; + if (reference_frame != order[i].reference_frame) { + reference_frame = order[i].reference_frame; + average_track_contributions(ctx, + reference_frame, + aspect, + average_translation, + pivot, + &average_angle, + &average_scale_step); + } + initialize_track_for_stabilization(ctx, + track, + reference_frame, + aspect, + average_translation, + pivot, + average_angle, + average_scale_step); + } - return stab->scale; +cleanup: + MEM_freeN(order); } -/* Get stabilization data (translation, scaling and angle) for a given frame. + +/* Retrieve the measurement of frame movement by averaging contributions of + * active tracks. * - * NOTE: frame number should be in clip space, not scene space + * translation is a measurement in normalized 0..1 coordinates. + * angle is a measurement in radians -pi..+pi counter clockwise relative to + * translation compensated frame center + * scale_step is a measurement of image scale changes, in logarithmic scale + * (zero means scale == 1) + * Returns calculation enabled and all data retrieved as expected for this frame. + * + * NOTE: when returning `false`, output parameters are reset to neutral values. */ -void BKE_tracking_stabilization_data_get(MovieTracking *tracking, int framenr, int width, int height, - float translation[2], float *scale, float *angle) +static bool stabilization_determine_offset_for_frame(StabContext *ctx, + int framenr, + float aspect, + float r_translation[2], + float r_pivot[2], + float *r_angle, + float *r_scale_step) { - float firstmedian[2], median[2]; - MovieTrackingStabilization *stab = &tracking->stabilization; + bool success = false; /* Early output if stabilization is disabled. */ - if ((stab->flag & TRACKING_2D_STABILIZATION) == 0) { - zero_v2(translation); - *scale = 1.0f; - *angle = 0.0f; + if ((ctx->stab->flag & TRACKING_2D_STABILIZATION) == 0) { + zero_v2(r_translation); + *r_scale_step = 0.0f; + *r_angle = 0.0f; + return false; + } - return; + success = average_track_contributions(ctx, + framenr, + aspect, + r_translation, + r_pivot, + r_angle, + r_scale_step); + if (!success) { + /* Try to hold extrapolated settings beyond the definition range + * and to interpolate in gaps without any usable tracking data + * to prevent sudden jump to image zero position. + */ + int next_lower = MINAFRAME; + int next_higher = MAXFRAME; + use_values_from_fcurves(ctx, true); + find_next_working_frames(ctx, framenr, &next_lower, &next_higher); + if (next_lower >= MINFRAME && next_higher < MAXFRAME) { + success = interpolate_averaged_track_contributions(ctx, + framenr, + next_lower, + next_higher, + aspect, + r_translation, + r_pivot, + r_angle, + r_scale_step); + } + else if (next_higher < MAXFRAME) { + /* Before start of stabilized range: extrapolate start point + * settings. + */ + success = average_track_contributions(ctx, + next_higher, + aspect, + r_translation, + r_pivot, + r_angle, + r_scale_step); + } + else if (next_lower >= MINFRAME) { + /* After end of stabilized range: extrapolate end point settings. */ + success = average_track_contributions(ctx, + next_lower, + aspect, + r_translation, + r_pivot, + r_angle, + r_scale_step); + } + use_values_from_fcurves(ctx, false); } + return success; +} - /* Even if tracks does not start at frame 1, their position will - * be estimated at this frame, which will give reasonable result - * in most of cases. - * - * However, it's still better to replace this with real first - * frame number at which tracks are appearing. +/* Calculate stabilization data (translation, scale and rotation) from given raw + * measurements. Result is in absolute image dimensions (expanded image, square + * pixels), includes automatic or manual scaling and compensates for a target + * frame position, if given. + * + * size is a size of the expanded image, the width in pixels is size * aspect. + * aspect is a ratio (width / height) of the effective canvas (square pixels). + * do_compensate denotes whether to actually output values necessary to + * _compensate_ the determined frame movement. + * Otherwise, the effective target movement is returned. + */ +static void stabilization_calculate_data(StabContext *ctx, + int framenr, + int size, + float aspect, + bool do_compensate, + float scale_step, + float r_translation[2], + float r_pivot[2], + float *r_scale, + float *r_angle) +{ + float target_pos[2], target_scale; + float scaleinf = get_animated_scaleinf(ctx, framenr); + + if (ctx->stab->flag & TRACKING_STABILIZE_SCALE) { + *r_scale = expf(scale_step * scaleinf); /* Averaged in log scale */ + } else { + *r_scale = 1.0f; + } + + mul_v2_fl(r_translation, get_animated_locinf(ctx, framenr)); + *r_angle *= get_animated_rotinf(ctx, framenr); + + /* Compensate for a target frame position. + * This allows to follow tracking / panning shots in a semi manual fashion, + * when animating the settings for the target frame position. */ - if (stabilization_median_point_get(tracking, 1, firstmedian)) { - stabilization_median_point_get(tracking, framenr, median); + get_animated_target_pos(ctx, framenr, target_pos); + sub_v2_v2(r_translation, target_pos); + *r_angle -= get_animated_target_rot(ctx,framenr); + target_scale = get_animated_target_scale(ctx,framenr); + if (target_scale != 0.0f) { + *r_scale /= target_scale; + /* target_scale is an expected/intended reference zoom value */ + } + + /* Convert from relative to absolute coordinates, square pixels. */ + r_translation[0] *= (float)size * aspect; + r_translation[1] *= (float)size; + r_pivot[0] *= (float)size * aspect; + r_pivot[1] *= (float)size; + + /* Output measured data, or inverse of the measured values for + * compensation? + */ + if (do_compensate) { + mul_v2_fl(r_translation, -1.0f); + *r_angle *= -1.0f; + if (*r_scale != 0.0f) { + *r_scale = 1.0f / *r_scale; + } + } +} + +static void stabilization_data_to_mat4(float pixel_aspect, + const float pivot[2], + const float translation[2], + float scale, + float angle, + float r_mat[4][4]) +{ + float translation_mat[4][4], rotation_mat[4][4], scale_mat[4][4], + pivot_mat[4][4], inv_pivot_mat[4][4], + aspect_mat[4][4], inv_aspect_mat[4][4]; + float scale_vector[3] = {scale, scale, 1.0f}; - if ((stab->flag & TRACKING_AUTOSCALE) == 0) - stab->scale = 1.0f; + unit_m4(translation_mat); + unit_m4(rotation_mat); + unit_m4(scale_mat); + unit_m4(aspect_mat); + unit_m4(pivot_mat); + unit_m4(inv_pivot_mat); - if (!stab->ok) { - if (stab->flag & TRACKING_AUTOSCALE) - stabilization_calculate_autoscale_factor(tracking, width, height); + /* aspect ratio correction matrix */ + aspect_mat[0][0] /= pixel_aspect; + invert_m4_m4(inv_aspect_mat, aspect_mat); - stabilization_calculate_data(tracking, framenr, width, height, firstmedian, median, - translation, scale, angle); + add_v2_v2(pivot_mat[3], pivot); + sub_v2_v2(inv_pivot_mat[3], pivot); - stab->ok = true; + size_to_mat4(scale_mat, scale_vector); /* scale matrix */ + add_v2_v2(translation_mat[3], translation); /* translation matrix */ + rotate_m4(rotation_mat, 'Z', angle); /* rotation matrix */ + + /* Compose transformation matrix. */ + mul_m4_series(r_mat, aspect_mat, translation_mat, + pivot_mat, scale_mat, rotation_mat, inv_pivot_mat, + inv_aspect_mat); +} + +/* Calculate scale factor necessary to eliminate black image areas + * caused by the compensating movements of the stabilizator. + * This function visits every frame where stabilisation data is + * available and determines the factor for this frame. The overall + * largest factor found is returned as result. + * + * NOTE: all tracks need to be initialized before calling this function. + */ +static float calculate_autoscale_factor(StabContext *ctx, + int size, + float aspect) +{ + MovieTrackingStabilization *stab = ctx->stab; + float pixel_aspect = ctx->tracking->camera.pixel_aspect; + int height = size, width = aspect*size; + + int sfra = INT_MAX, efra = INT_MIN, cfra; + float scale = 1.0f, scale_step = 0.0f; + MovieTrackingTrack *track; + + /* Calculate maximal frame range of tracks where stabilization is active. */ + for (track = ctx->tracking->tracks.first; track; track = track->next) { + if ((track->flag & TRACK_USE_2D_STAB) || + ((stab->flag & TRACKING_STABILIZE_ROTATION) && + (track->flag & TRACK_USE_2D_STAB_ROT))) + { + int first_frame = track->markers[0].framenr; + int last_frame = track->markers[track->markersnr - 1].framenr; + sfra = min_ii(sfra, first_frame); + efra = max_ii(efra, last_frame); } - else { - stabilization_calculate_data(tracking, framenr, width, height, firstmedian, median, - translation, scale, angle); + } + + use_values_from_fcurves(ctx, true); + for (cfra = sfra; cfra <= efra; cfra++) { + float translation[2], pivot[2], angle, tmp_scale; + float mat[4][4]; + const float points[4][2] = {{0.0f, 0.0f}, + {0.0f, height}, + {width, height}, + {width, 0.0f}}; + const bool do_compensate = true; + /* Calculate stabilization parameters for the current frame. */ + stabilization_determine_offset_for_frame(ctx, + cfra, + aspect, + translation, + pivot, + &angle, + &scale_step); + stabilization_calculate_data(ctx, + cfra, + size, + aspect, + do_compensate, + scale_step, + translation, + pivot, + &tmp_scale, + &angle); + /* Compose transformation matrix. */ + /* NOTE: Here we operate in NON-COMPENSATED coordinates, meaning we have + * to construct transformation matrix using proper pivot point. + * Compensation for that will happen later on. + */ + stabilization_data_to_mat4(pixel_aspect, + pivot, + translation, + tmp_scale, + angle, + mat); + /* Investigate the transformed border lines for this frame; + * find out, where it cuts the original frame. + */ + for (int edge_index = 0; edge_index < 4; edge_index++) { + /* Calculate coordinates of stabilized frame edge points. + * Use matrix multiplication here so we operate in homogeneous + * coordinates. + */ + float stable_edge_p1[3], stable_edge_p2[3]; + copy_v2_v2(stable_edge_p1, points[edge_index]); + copy_v2_v2(stable_edge_p2, points[(edge_index + 1) % 4]); + stable_edge_p1[2] = stable_edge_p2[2] = 0.0f; + mul_m4_v3(mat, stable_edge_p1); + mul_m4_v3(mat, stable_edge_p2); + /* Now we iterate over all original frame corners (we call them + * 'point' here) to see if there's black area between stabilized + * frame edge and original point. + */ + for (int point_index = 0; point_index < 4; point_index++) { + const float point[3] = {points[point_index][0], + points[point_index][1], + 0.0f}; + /* Calculate vector which goes from first edge point to + * second one. + */ + float stable_edge_vec[3]; + sub_v3_v3v3(stable_edge_vec, stable_edge_p2, stable_edge_p1); + /* Calculate vector which connects current frame point to + * first edge point. + */ + float point_to_edge_start_vec[3]; + sub_v3_v3v3(point_to_edge_start_vec, point, stable_edge_p1); + /* Use this two vectors to check whether frame point is inside + * of the stabilized frame or not. + * If the point is inside, there is no black area happening + * and no scaling required for it. + */ + if (cross_v2v2(stable_edge_vec, point_to_edge_start_vec) >= 0.0f) { + /* We are scaling around motion-compensated pivot point. */ + float scale_pivot[2]; + add_v2_v2v2(scale_pivot, pivot, translation); + /* Calculate line which goes via `point` and parallel to + * the stabilized frame edge. This line is coming via + * `point` and `point2` at the end. + */ + float point2[2]; + add_v2_v2v2(point2, point, stable_edge_vec); + /* Calculate actual distance between pivot point and + * the stabilized frame edge. Then calculate distance + * between pivot point and line which goes via actual + * corner and is parallel to the edge. + * + * Dividing one by another will give us required scale + * factor to get rid of black areas. + */ + float real_dist = dist_to_line_v2(scale_pivot, + stable_edge_p1, + stable_edge_p2); + float required_dist = dist_to_line_v2(scale_pivot, + point, + point2); + const float S = required_dist / real_dist; + scale = max_ff(scale, S); + } + } } } + if (stab->maxscale > 0.0f) { + scale = min_ff(scale, stab->maxscale); + } + use_values_from_fcurves(ctx, false); + + return scale; +} + + +/* Prepare working data and determine reference point for each track. + * + * NOTE: These calculations _could_ be cached and reused for all frames of the + * same clip. However, since proper initialization depends on (weight) + * animation and setup of tracks, ensuring consistency of cached init data + * turns out to be tricky, hard to maintain and generally not worth the + * effort. Thus we'll re-initialize on every frame. + */ +static StabContext *init_stabilizer(MovieClip *clip, int size, float aspect) +{ + StabContext *ctx = initialize_stabilization_working_context(clip); + BLI_assert(ctx != NULL); + initialize_all_tracks(ctx, aspect); + if (ctx->stab->flag & TRACKING_AUTOSCALE) { + ctx->stab->scale = 1.0; + ctx->stab->scale = calculate_autoscale_factor(ctx, size, aspect); + } + /* By default, just use values for the global current frame. */ + use_values_from_fcurves(ctx, false); + return ctx; +} + + +/* === public interface functions === */ + +/* Get stabilization data (translation, scaling and angle) for a given frame. + * Returned data describes how to compensate the detected movement, but with any + * chosen scale factor already applied and any target frame position already + * compensated. In case stabilization fails or is disabled, neutral values are + * returned. + * + * framenr is a frame number, relative to the clip (not relative to the scene + * timeline) + * width is an effective width of the canvas (square pixels), used to scale the + * determined translation + * + * Outputs: + * - translation of the lateral shift, absolute canvas coordinates + * (square pixels). + * - scale of the scaling to apply + * - angle of the rotation angle, relative to the frame center + */ +/* TODO(sergey): Use r_ prefix for output parameters here. */ +void BKE_tracking_stabilization_data_get(MovieClip *clip, + int framenr, + int width, + int height, + float translation[2], + float *scale, + float *angle) +{ + StabContext *ctx = NULL; + MovieTracking *tracking = &clip->tracking; + bool enabled = (tracking->stabilization.flag & TRACKING_2D_STABILIZATION); + /* Might become a parameter of a stabilization compositor node. */ + bool do_compensate = true; + float scale_step = 0.0f; + float pixel_aspect = tracking->camera.pixel_aspect; + float aspect = (float)width * pixel_aspect / height; + int size = height; + float pivot[2]; + + if (enabled) { + ctx = init_stabilizer(clip, size, aspect); + } + + if (enabled && + stabilization_determine_offset_for_frame(ctx, + framenr, + aspect, + translation, + pivot, + angle, + &scale_step)) + { + stabilization_calculate_data(ctx, + framenr, + size, + aspect, + do_compensate, + scale_step, + translation, + pivot, + scale, + angle); + compensate_rotation_center(size, + aspect, + *angle, + *scale, + pivot, + translation); + } else { zero_v2(translation); *scale = 1.0f; *angle = 0.0f; } + discard_stabilization_working_context(ctx); } -/* Stabilize given image buffer using stabilization data for - * a specified frame number. +/* Stabilize given image buffer using stabilization data for a specified + * frame number. * - * NOTE: frame number should be in clip space, not scene space + * NOTE: frame number should be in clip space, not scene space. */ -ImBuf *BKE_tracking_stabilize_frame(MovieTracking *tracking, int framenr, ImBuf *ibuf, - float translation[2], float *scale, float *angle) +/* TODO(sergey): Use r_ prefix for output parameters here. */ +ImBuf *BKE_tracking_stabilize_frame(MovieClip *clip, + int framenr, + ImBuf *ibuf, + float translation[2], + float *scale, + float *angle) { float tloc[2], tscale, tangle; + MovieTracking *tracking = &clip->tracking; MovieTrackingStabilization *stab = &tracking->stabilization; ImBuf *tmpibuf; int width = ibuf->x, height = ibuf->y; - float aspect = tracking->camera.pixel_aspect; + float pixel_aspect = tracking->camera.pixel_aspect; float mat[4][4]; int j, filter = tracking->stabilization.filter; void (*interpolation)(struct ImBuf *, struct ImBuf *, float, float, int, int) = NULL; @@ -349,8 +1542,12 @@ ImBuf *BKE_tracking_stabilize_frame(MovieTracking *tracking, int framenr, ImBuf tmpibuf = IMB_allocImBuf(ibuf->x, ibuf->y, ibuf->planes, ibuf_flags); /* Calculate stabilization matrix. */ - BKE_tracking_stabilization_data_get(tracking, framenr, width, height, tloc, &tscale, &tangle); - BKE_tracking_stabilization_data_to_mat4(ibuf->x, ibuf->y, aspect, tloc, tscale, tangle, mat); + BKE_tracking_stabilization_data_get(clip, framenr, width, height, tloc, &tscale, &tangle); + BKE_tracking_stabilization_data_to_mat4(ibuf->x, ibuf->y, pixel_aspect, tloc, tscale, tangle, mat); + + /* The following code visits each nominal target grid position + * and picks interpolated data "backwards" from source. + * thus we need the inverse of the transformation to apply. */ invert_m4(mat); if (filter == TRACKING_FILTER_NEAREST) @@ -397,48 +1594,44 @@ ImBuf *BKE_tracking_stabilize_frame(MovieTracking *tracking, int framenr, ImBuf return tmpibuf; } -/* Get 4x4 transformation matrix which corresponds to - * stabilization data and used for easy coordinate - * transformation. + +/* Build a 4x4 transformation matrix based on the given 2D stabilization data. + * mat is a 4x4 matrix in homogeneous coordinates, adapted to the + * final image buffer size and compensated for pixel aspect ratio, + * ready for direct OpenGL drawing. * - * NOTE: The reason it is 4x4 matrix is because it's - * used for OpenGL drawing directly. + * TODO(sergey): The signature of this function should be changed. we actually + * don't need the dimensions of the image buffer. Instead we + * should consider to provide the pivot point of the rotation as a + * further stabilization data parameter. */ -void BKE_tracking_stabilization_data_to_mat4(int width, int height, float aspect, - float translation[2], float scale, float angle, - float mat[4][4]) +void BKE_tracking_stabilization_data_to_mat4(int buffer_width, + int buffer_height, + float pixel_aspect, + float translation[2], + float scale, + float angle, + float r_mat[4][4]) { - float translation_mat[4][4], rotation_mat[4][4], scale_mat[4][4], - center_mat[4][4], inv_center_mat[4][4], - aspect_mat[4][4], inv_aspect_mat[4][4]; - float scale_vector[3] = {scale, scale, scale}; - - unit_m4(translation_mat); - unit_m4(rotation_mat); - unit_m4(scale_mat); - unit_m4(center_mat); - unit_m4(aspect_mat); - - /* aspect ratio correction matrix */ - aspect_mat[0][0] = 1.0f / aspect; - invert_m4_m4(inv_aspect_mat, aspect_mat); - - /* image center as rotation center - * - * Rotation matrix is constructing in a way rotation happens around image center, - * and it's matter of calculating translation in a way, that applying translation - * after rotation would make it so rotation happens around median point of tracks - * used for translation stabilization. + /* Since we cannot receive the real pivot point coordinates (API limitation), + * we perform the rotation/scale around the center of frame. + * Then we correct by an additional shift, which was calculated in + * compensate_rotation_center() and "sneaked in" as additional offset + * in the translation parameter. This works, since translation needs to be + * applied after rotation/scale anyway. Thus effectively the image gets + * rotated around the desired pivot point */ - center_mat[3][0] = (float)width / 2.0f; - center_mat[3][1] = (float)height / 2.0f; - invert_m4_m4(inv_center_mat, center_mat); - - size_to_mat4(scale_mat, scale_vector); /* scale matrix */ - add_v2_v2(translation_mat[3], translation); /* translation matrix */ - rotate_m4(rotation_mat, 'Z', angle); /* rotation matrix */ - - /* compose transformation matrix */ - mul_m4_series(mat, translation_mat, center_mat, aspect_mat, rotation_mat, inv_aspect_mat, - scale_mat, inv_center_mat); + /* TODO(sergey) pivot shouldn't be calculated here, rather received + * as a parameter. + */ + float pivot[2]; + pivot[0] = 0.5f * pixel_aspect * buffer_width; + pivot[1] = 0.5f * buffer_height; + /* Compose transformation matrix. */ + stabilization_data_to_mat4(pixel_aspect, + pivot, + translation, + scale, + angle, + r_mat); } diff --git a/source/blender/blenkernel/intern/world.c b/source/blender/blenkernel/intern/world.c index a73eb834299..0e2e9ac75c2 100644 --- a/source/blender/blenkernel/intern/world.c +++ b/source/blender/blenkernel/intern/world.c @@ -167,8 +167,6 @@ World *localize_world(World *wrld) if (wrld->mtex[a]) { wrldn->mtex[a] = MEM_mallocN(sizeof(MTex), "localize_world"); memcpy(wrldn->mtex[a], wrld->mtex[a], sizeof(MTex)); - /* free world decrements */ - id_us_plus((ID *)wrldn->mtex[a]->tex); } } diff --git a/source/blender/blenkernel/intern/writeffmpeg.c b/source/blender/blenkernel/intern/writeffmpeg.c index 9dbc045f1b0..de55a1977bf 100644 --- a/source/blender/blenkernel/intern/writeffmpeg.c +++ b/source/blender/blenkernel/intern/writeffmpeg.c @@ -1393,7 +1393,7 @@ int BKE_ffmpeg_property_add_string(RenderData *rd, const char *type, const char avcodec_get_context_defaults3(&c, NULL); - strncpy(name_, str, sizeof(name_)); + BLI_strncpy(name_, str, sizeof(name_)); name = name_; while (*name == ' ') name++; diff --git a/source/blender/blenlib/BLI_system.h b/source/blender/blenlib/BLI_system.h index cb8cb6f5a0d..f51b9623803 100644 --- a/source/blender/blenlib/BLI_system.h +++ b/source/blender/blenlib/BLI_system.h @@ -21,15 +21,14 @@ #ifndef __BLI_SYSTEM_H__ #define __BLI_SYSTEM_H__ +#include <stdio.h> + /** \file BLI_system.h * \ingroup bli */ int BLI_cpu_support_sse2(void); - -#if defined(NDEBUG) || !defined(__BLI_UTILDEFINES_H__) void BLI_system_backtrace(FILE *fp); -#endif /* getpid */ #ifdef WIN32 diff --git a/source/blender/blenlib/BLI_utildefines.h b/source/blender/blenlib/BLI_utildefines.h index d504e503c68..746eb922c65 100644 --- a/source/blender/blenlib/BLI_utildefines.h +++ b/source/blender/blenlib/BLI_utildefines.h @@ -633,7 +633,7 @@ extern bool BLI_memory_is_zero(const void *arr, const size_t arr_size); * for aborting need to define WITH_ASSERT_ABORT */ #ifndef NDEBUG -extern void BLI_system_backtrace(FILE *fp); +# include "BLI_system.h" # ifdef WITH_ASSERT_ABORT # define _BLI_DUMMY_ABORT abort # else diff --git a/source/blender/blenlib/intern/array_store.c b/source/blender/blenlib/intern/array_store.c index 6000c1a680c..21ddddad32e 100644 --- a/source/blender/blenlib/intern/array_store.c +++ b/source/blender/blenlib/intern/array_store.c @@ -579,7 +579,7 @@ static void bchunk_list_calc_trim_len( /** * Append and don't manage merging small chunks. */ -static bool bchunk_list_append_only( +static void bchunk_list_append_only( BArrayMemory *bs_mem, BChunkList *chunk_list, BChunk *chunk) { @@ -588,7 +588,6 @@ static bool bchunk_list_append_only( cref->link = chunk; chunk_list->chunk_refs_len += 1; chunk->users += 1; - return chunk; } /** diff --git a/source/blender/blenlib/intern/math_geom.c b/source/blender/blenlib/intern/math_geom.c index 40454a93ec8..dd30f267f78 100644 --- a/source/blender/blenlib/intern/math_geom.c +++ b/source/blender/blenlib/intern/math_geom.c @@ -2892,8 +2892,8 @@ void plot_line_v2v2i(const int p1[2], const int p2[2], bool (*callback)(int, int int x2 = p2[0]; int y2 = p2[1]; - signed char ix; - signed char iy; + int ix; + int iy; /* if x1 == x2 or y1 == y2, then it does not matter what we set here */ int delta_x = (x2 > x1 ? ((void)(ix = 1), x2 - x1) : ((void)(ix = -1), x1 - x2)) << 1; diff --git a/source/blender/blenlib/intern/system.c b/source/blender/blenlib/intern/system.c index 5d1bdd6d978..898075e651e 100644 --- a/source/blender/blenlib/intern/system.c +++ b/source/blender/blenlib/intern/system.c @@ -25,6 +25,7 @@ #include <stdio.h> #include <stdlib.h> +#include "BLI_utildefines.h" #include "BLI_system.h" #include "MEM_guardedalloc.h" diff --git a/source/blender/blenloader/CMakeLists.txt b/source/blender/blenloader/CMakeLists.txt index 479d3a15e6c..8cb9ef837b2 100644 --- a/source/blender/blenloader/CMakeLists.txt +++ b/source/blender/blenloader/CMakeLists.txt @@ -77,6 +77,13 @@ if(WITH_CODEC_FFMPEG) add_definitions(-DWITH_FFMPEG) endif() +if(WITH_ALEMBIC) + list(APPEND INC + ../alembic + ) + add_definitions(-DWITH_ALEMBIC) +endif() + blender_add_lib(bf_blenloader "${SRC}" "${INC}" "${INC_SYS}") # needed so writefile.c can use dna_type_offsets.h diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 6aaeabb4096..96b5bb757ea 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -59,6 +59,7 @@ #include "DNA_actuator_types.h" #include "DNA_brush_types.h" #include "DNA_camera_types.h" +#include "DNA_cachefile_types.h" #include "DNA_cloth_types.h" #include "DNA_controller_types.h" #include "DNA_constraint_types.h" @@ -114,6 +115,7 @@ #include "BKE_action.h" #include "BKE_armature.h" #include "BKE_brush.h" +#include "BKE_cachefile.h" #include "BKE_cloth.h" #include "BKE_constraint.h" #include "BKE_context.h" @@ -2691,6 +2693,38 @@ static void direct_link_animdata(FileData *fd, AnimData *adt) adt->actstrip = newdataadr(fd, adt->actstrip); } +/* ************ READ CACHEFILES *************** */ + +static void lib_link_cachefiles(FileData *fd, Main *bmain) +{ + CacheFile *cache_file; + + /* only link ID pointers */ + for (cache_file = bmain->cachefiles.first; cache_file; cache_file = cache_file->id.next) { + if (cache_file->id.tag & LIB_TAG_NEED_LINK) { + cache_file->id.tag &= ~LIB_TAG_NEED_LINK; + } + + BLI_listbase_clear(&cache_file->object_paths); + cache_file->handle = NULL; + cache_file->handle_mutex = NULL; + + if (cache_file->adt) { + lib_link_animdata(fd, &cache_file->id, cache_file->adt); + } + } +} + +static void direct_link_cachefile(FileData *fd, CacheFile *cache_file) +{ + cache_file->handle = NULL; + cache_file->handle_mutex = NULL; + + /* relink animdata */ + cache_file->adt = newdataadr(fd, cache_file->adt); + direct_link_animdata(fd, cache_file->adt); +} + /* ************ READ MOTION PATHS *************** */ /* direct data for cache */ @@ -4743,7 +4777,7 @@ static void lib_link_object(FileData *fd, Main *main) /* Only expand so as not to loose any object materials that might be set. */ if (totcol_data && (*totcol_data > ob->totcol)) { /* printf("'%s' %d -> %d\n", ob->id.name, ob->totcol, *totcol_data); */ - BKE_material_resize_object(ob, *totcol_data, false); + BKE_material_resize_object(main, ob, *totcol_data, false); } } @@ -5277,6 +5311,9 @@ static void direct_link_object(FileData *fd, Object *ob) */ ob->recalc = 0; + /* XXX This should not be needed - but seems like it can happen in some cases, so for now play safe... */ + ob->proxy_from = NULL; + /* loading saved files with editmode enabled works, but for undo we like * to stay in object mode during undo presses so keep editmode disabled. * @@ -6675,10 +6712,13 @@ void blo_lib_link_screen_restore(Main *newmain, bScreen *curscreen, Scene *cursc * since it gets initialized later */ sima->iuser.scene = NULL; - sima->scopes.waveform_1 = NULL; - sima->scopes.waveform_2 = NULL; - sima->scopes.waveform_3 = NULL; - sima->scopes.vecscope = NULL; +#if 0 + /* Those are allocated and freed by space code, no need to handle them here. */ + MEM_SAFE_FREE(sima->scopes.waveform_1); + MEM_SAFE_FREE(sima->scopes.waveform_2); + MEM_SAFE_FREE(sima->scopes.waveform_3); + MEM_SAFE_FREE(sima->scopes.vecscope); +#endif sima->scopes.ok = 0; /* NOTE: pre-2.5, this was local data not lib data, but now we need this as lib data @@ -7464,7 +7504,7 @@ static void direct_link_movieclip(FileData *fd, MovieClip *clip) clip->tracking_context = NULL; clip->tracking.stats = NULL; - clip->tracking.stabilization.ok = 0; + /* Needed for proper versioning, will be NULL for all newer files anyway. */ clip->tracking.stabilization.rot_track = newdataadr(fd, clip->tracking.stabilization.rot_track); clip->tracking.dopesheet.ok = 0; @@ -7930,6 +7970,7 @@ static const char *dataname(short id_code) case ID_MC: return "Data from MC"; case ID_MSK: return "Data from MSK"; case ID_LS: return "Data from LS"; + case ID_CF: return "Data from CF"; } return "Data from Lib Block"; @@ -8181,6 +8222,9 @@ static BHead *read_libblock(FileData *fd, Main *main, BHead *bhead, const short case ID_PC: direct_link_paint_curve(fd, (PaintCurve *)id); break; + case ID_CF: + direct_link_cachefile(fd, (CacheFile *)id); + break; } oldnewmap_free_unused(fd->datamap); @@ -8374,6 +8418,7 @@ static void lib_link_all(FileData *fd, Main *main) lib_link_mask(fd, main); lib_link_linestyle(fd, main); lib_link_gpencil(fd, main); + lib_link_cachefiles(fd, main); lib_link_mesh(fd, main); /* as last: tpage images with users at zero */ @@ -9484,6 +9529,13 @@ static void expand_camera(FileData *fd, Main *mainvar, Camera *ca) expand_animdata(fd, mainvar, ca->adt); } +static void expand_cachefile(FileData *fd, Main *mainvar, CacheFile *cache_file) +{ + if (cache_file->adt) { + expand_animdata(fd, mainvar, cache_file->adt); + } +} + static void expand_speaker(FileData *fd, Main *mainvar, Speaker *spk) { expand_doit(fd, mainvar, spk->sound); @@ -9679,6 +9731,9 @@ void BLO_expand_main(void *fdhandle, Main *mainvar) case ID_GD: expand_gpencil(fd, mainvar, (bGPdata *)id); break; + case ID_CF: + expand_cachefile(fd, mainvar, (CacheFile *)id); + break; } do_it = true; diff --git a/source/blender/blenloader/intern/versioning_270.c b/source/blender/blenloader/intern/versioning_270.c index 7e811d9e203..eaa7d487862 100644 --- a/source/blender/blenloader/intern/versioning_270.c +++ b/source/blender/blenloader/intern/versioning_270.c @@ -63,6 +63,7 @@ #include "BKE_scene.h" #include "BKE_sequencer.h" #include "BKE_screen.h" +#include "BKE_tracking.h" #include "BKE_gpencil.h" #include "BLI_math.h" @@ -75,6 +76,23 @@ #include "MEM_guardedalloc.h" +/** + * Setup rotation stabilization from ancient single track spec. + * Former Version of 2D stabilization used a single tracking marker to determine the rotation + * to be compensated. Now several tracks can contribute to rotation detection and this feature + * is enabled by the MovieTrackingTrack#flag on a per track base. + */ +static void migrate_single_rot_stabilization_track_settings(MovieTrackingStabilization *stab) +{ + if (stab->rot_track) { + if (!(stab->rot_track->flag & TRACK_USE_2D_STAB_ROT)) { + stab->tot_rot_track++; + stab->rot_track->flag |= TRACK_USE_2D_STAB_ROT; + } + } + stab->rot_track = NULL; /* this field is now ignored */ +} + static void do_version_constraints_radians_degrees_270_1(ListBase *lb) { bConstraint *con; @@ -1269,7 +1287,7 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main) if (!MAIN_VERSION_ATLEAST(main, 277, 3)) { /* ------- init of grease pencil initialization --------------- */ - if (!DNA_struct_elem_find(fd->filesdna, "bGPDstroke", "bGPDpalettecolor", "palcolor")) { + if (!DNA_struct_elem_find(fd->filesdna, "bGPDstroke", "bGPDpalettecolor", "*palcolor")) { for (Scene *scene = main->scene.first; scene; scene = scene->id.next) { ToolSettings *ts = scene->toolsettings; /* initialize use position for sculpt brushes */ @@ -1293,7 +1311,7 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main) for (Scene *scene = main->scene.first; scene; scene = scene->id.next) { ToolSettings *ts = scene->toolsettings; if (BLI_listbase_is_empty(&ts->gp_brushes)) { - gpencil_brush_init_presets(ts); + BKE_gpencil_brush_init_presets(ts); } } } @@ -1303,45 +1321,98 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main) for (bGPdata *gpd = main->gpencil.first; gpd; gpd = gpd->id.next) { if (BLI_listbase_is_empty(&gpd->palettes)) { /* create palette */ - bGPDpalette *palette = gpencil_palette_addnew(gpd, "GP_Palette", true); + bGPDpalette *palette = BKE_gpencil_palette_addnew(gpd, "GP_Palette", true); for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { /* create color using layer name */ - bGPDpalettecolor *palcolor = gpencil_palettecolor_addnew(palette, gpl->info, true); + bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_addnew(palette, gpl->info, true); if (palcolor != NULL) { /* set color attributes */ copy_v4_v4(palcolor->color, gpl->color); copy_v4_v4(palcolor->fill, gpl->fill); - palcolor->flag = gpl->flag; + + if (gpl->flag & GP_LAYER_HIDE) palcolor->flag |= PC_COLOR_HIDE; + if (gpl->flag & GP_LAYER_LOCKED) palcolor->flag |= PC_COLOR_LOCKED; + if (gpl->flag & GP_LAYER_ONIONSKIN) palcolor->flag |= PC_COLOR_ONIONSKIN; + if (gpl->flag & GP_LAYER_VOLUMETRIC) palcolor->flag |= PC_COLOR_VOLUMETRIC; + if (gpl->flag & GP_LAYER_HQ_FILL) palcolor->flag |= PC_COLOR_HQ_FILL; + /* set layer opacity to 1 */ gpl->opacity = 1.0f; + /* set tint color */ ARRAY_SET_ITEMS(gpl->tintcolor, 0.0f, 0.0f, 0.0f, 0.0f); - + + /* flush relevant layer-settings to strokes */ for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { /* set stroke to palette and force recalculation */ - strcpy(gps->colorname, gpl->info); + BLI_strncpy(gps->colorname, gpl->info, sizeof(gps->colorname)); gps->palcolor = NULL; gps->flag |= GP_STROKE_RECALC_COLOR; gps->thickness = gpl->thickness; + /* set alpha strength to 1 */ for (int i = 0; i < gps->totpoints; i++) { gps->points[i].strength = 1.0f; } - } } } + /* set thickness to 0 (now it is a factor to override stroke thickness) */ gpl->thickness = 0.0f; } /* set first color as active */ if (palette->colors.first) - gpencil_palettecolor_setactive(palette, palette->colors.first); + BKE_gpencil_palettecolor_setactive(palette, palette->colors.first); } } } /* ------- end of grease pencil initialization --------------- */ } + if (!MAIN_VERSION_ATLEAST(main, 278, 0)) { + if (!DNA_struct_elem_find(fd->filesdna, "MovieTrackingTrack", "float", "weight_stab")) { + MovieClip *clip; + for (clip = main->movieclip.first; clip; clip = clip->id.next) { + MovieTracking *tracking = &clip->tracking; + MovieTrackingObject *tracking_object; + for (tracking_object = tracking->objects.first; + tracking_object != NULL; + tracking_object = tracking_object->next) + { + ListBase *tracksbase = BKE_tracking_object_get_tracks(tracking, tracking_object); + MovieTrackingTrack *track; + for (track = tracksbase->first; + track != NULL; + track = track->next) + { + track->weight_stab = track->weight; + } + } + } + } + + if (!DNA_struct_elem_find(fd->filesdna, "MovieTrackingStabilization", "int", "tot_rot_track")) { + MovieClip *clip; + for (clip = main->movieclip.first; clip != NULL; clip = clip->id.next) { + if (clip->tracking.stabilization.rot_track) { + migrate_single_rot_stabilization_track_settings(&clip->tracking.stabilization); + } + if (clip->tracking.stabilization.scale == 0.0f) { + /* ensure init. + * Was previously used for autoscale only, + * now used always (as "target scale") */ + clip->tracking.stabilization.scale = 1.0f; + } + /* blender prefers 1-based frame counting; + * thus using frame 1 as reference typically works best */ + clip->tracking.stabilization.anchor_frame = 1; + /* by default show the track lists expanded, to improve "discoverability" */ + clip->tracking.stabilization.flag |= TRACKING_SHOW_STAB_TRACKS; + /* deprecated, not used anymore */ + clip->tracking.stabilization.ok = false; + } + } + } } diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index 2404271bf25..8e797e38de3 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -49,7 +49,7 @@ * <bh.len> int, len data after BHead * <bh.old> void, old pointer * <bh.SDNAnr> int - * <bh.nr> int, in case of array: amount of structs + * <bh.nr> int, in case of array: number of structs * data * ... * ... @@ -107,6 +107,7 @@ #include "DNA_armature_types.h" #include "DNA_actuator_types.h" #include "DNA_brush_types.h" +#include "DNA_cachefile_types.h" #include "DNA_camera_types.h" #include "DNA_cloth_types.h" #include "DNA_constraint_types.h" @@ -3893,6 +3894,21 @@ static void write_linestyles(WriteData *wd, ListBase *idbase) } } +static void write_cachefiles(WriteData *wd, ListBase *idbase) +{ + CacheFile *cache_file; + + for (cache_file = idbase->first; cache_file; cache_file = cache_file->id.next) { + if (cache_file->id.us > 0 || wd->current) { + writestruct(wd, ID_CF, CacheFile, 1, cache_file); + + if (cache_file->adt) { + write_animdata(wd, cache_file->adt); + } + } + } +} + /* Keep it last of write_foodata functions. */ static void write_libraries(WriteData *wd, Main *main) { @@ -4090,6 +4106,7 @@ static bool write_file_handle( write_paintcurves(wd, &mainvar->paintcurves); write_gpencils(wd, &mainvar->gpencil); write_linestyles(wd, &mainvar->linestyle); + write_cachefiles(wd, &mainvar->cachefiles); write_libraries(wd, mainvar->next); /* So changes above don't cause a 'DNA1' to be detected as changed on undo. */ diff --git a/source/blender/blentranslation/BLT_translation.h b/source/blender/blentranslation/BLT_translation.h index 3838e8c827c..1d76077c9f1 100644 --- a/source/blender/blentranslation/BLT_translation.h +++ b/source/blender/blentranslation/BLT_translation.h @@ -120,6 +120,7 @@ bool BLT_lang_is_ime_supported(void); #define BLT_I18NCONTEXT_ID_ARMATURE "Armature" #define BLT_I18NCONTEXT_ID_BRUSH "Brush" #define BLT_I18NCONTEXT_ID_CAMERA "Camera" +#define BLT_I18NCONTEXT_ID_CACHEFILE "CacheFile" #define BLT_I18NCONTEXT_ID_CURVE "Curve" #define BLT_I18NCONTEXT_ID_FREESTYLELINESTYLE "FreestyleLineStyle" #define BLT_I18NCONTEXT_ID_GPENCIL "GPencil" @@ -171,6 +172,7 @@ typedef struct { BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_ARMATURE, "id_armature"), \ BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_BRUSH, "id_brush"), \ BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_CAMERA, "id_camera"), \ + BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_CACHEFILE, "id_cachefile"), \ BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_CURVE, "id_curve"), \ BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_FREESTYLELINESTYLE, "id_fs_linestyle"), \ BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_GPENCIL, "id_gpencil"), \ diff --git a/source/blender/bmesh/tools/bmesh_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c index b647f5a667d..1dfb9dee8eb 100644 --- a/source/blender/bmesh/tools/bmesh_bevel.c +++ b/source/blender/bmesh/tools/bmesh_bevel.c @@ -3563,7 +3563,7 @@ static void find_bevel_edge_order(BMesh *bm, BevVert *bv, BMEdge *first_bme) BLI_assert(first_bme != NULL); bv->edges[i].e = first_bme; BM_BEVEL_EDGE_TAG_ENABLE(first_bme); - if (fast_bevel_edge_order(bv)) + if (i == 0 && fast_bevel_edge_order(bv)) break; i = bevel_edge_order_extend(bm, bv, i); i++; @@ -3588,6 +3588,7 @@ static void find_bevel_edge_order(BMesh *bm, BevVert *bv, BMEdge *first_bme) e2 = (i == bv->edgecount - 1) ? &bv->edges[0] : &bv->edges[i + 1]; bme = e->e; bme2 = e2->e; + BLI_assert(bme != NULL); BM_ITER_ELEM(l, &iter, bme, BM_LOOPS_OF_EDGE) { f = l->f; if ((l->prev->e == bme2 || l->next->e == bme2) && !e->fnext && !e2->fprev) diff --git a/source/blender/bmesh/tools/bmesh_decimate_collapse.c b/source/blender/bmesh/tools/bmesh_decimate_collapse.c index 52a0df5b9d1..372d341f223 100644 --- a/source/blender/bmesh/tools/bmesh_decimate_collapse.c +++ b/source/blender/bmesh/tools/bmesh_decimate_collapse.c @@ -430,7 +430,7 @@ static int *bm_edge_symmetry_map(BMesh *bm, unsigned int symmetry_axis, float li tree = BLI_kdtree_new(bm->totedge); - etable = MEM_mallocN(sizeof(BMEdge **) * bm->totedge, __func__); + etable = MEM_mallocN(sizeof(*etable) * bm->totedge, __func__); edge_symmetry_map = MEM_mallocN(sizeof(*edge_symmetry_map) * bm->totedge, __func__); BM_ITER_MESH_INDEX (e, &iter, bm, BM_EDGES_OF_MESH, i) { diff --git a/source/blender/collada/TransformWriter.cpp b/source/blender/collada/TransformWriter.cpp index e205608a365..b16e2e2b0d3 100644 --- a/source/blender/collada/TransformWriter.cpp +++ b/source/blender/collada/TransformWriter.cpp @@ -49,6 +49,7 @@ void TransformWriter::add_node_transform(COLLADASW::Node& node, float mat[4][4], double dmat[4][4]; UnitConverter *converter = new UnitConverter(); converter->mat4_to_dae_double(dmat, local); + delete converter; TransformBase::decompose(local, loc, rot, NULL, scale); diff --git a/source/blender/compositor/nodes/COM_MovieClipNode.cpp b/source/blender/compositor/nodes/COM_MovieClipNode.cpp index 933223dacac..b3f1b5a4458 100644 --- a/source/blender/compositor/nodes/COM_MovieClipNode.cpp +++ b/source/blender/compositor/nodes/COM_MovieClipNode.cpp @@ -91,7 +91,7 @@ void MovieClipNode::convertToOperations(NodeConverter &converter, const Composit if (stab->flag & TRACKING_2D_STABILIZATION) { int clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(movieClip, context.getFramenumber()); - BKE_tracking_stabilization_data_get(&movieClip->tracking, clip_framenr, ibuf->x, ibuf->y, loc, &scale, &angle); + BKE_tracking_stabilization_data_get(movieClip, clip_framenr, ibuf->x, ibuf->y, loc, &scale, &angle); } } diff --git a/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cpp b/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cpp index 76afedf4b2a..ca3c5f7b069 100644 --- a/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cpp +++ b/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cpp @@ -20,6 +20,8 @@ * Monique Dewanchand */ +#include <stdlib.h> + #include "COM_DoubleEdgeMaskOperation.h" #include "BLI_math.h" #include "DNA_node_types.h" @@ -1151,12 +1153,13 @@ void DoubleEdgeMaskOperation::doDoubleEdgeMask(float *imask, float *omask, float if (true) { // if both input sockets have some data coming in... - t = (this->getWidth() * this->getHeight()) - 1; // determine size of the frame + rw = this->getWidth(); // width of a row of pixels + t = (rw * this->getHeight()) - 1; // determine size of the frame + memset(res, 0, sizeof(float) * (t + 1)); // clear output buffer (not all pixels will be written later) lres = (unsigned int *)res; // unsigned int pointer to output buffer (for bit level ops) limask = (unsigned int *)imask; // unsigned int pointer to input mask (for bit level ops) lomask = (unsigned int *)omask; // unsigned int pointer to output mask (for bit level ops) - rw = this->getWidth(); // width of a row of pixels /* diff --git a/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cpp b/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cpp index 5ddf15f7684..41f7da7c49f 100644 --- a/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cpp +++ b/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cpp @@ -47,7 +47,7 @@ void MovieClipAttributeOperation::executePixelSampled(float output[4], angle = 0.0f; if (this->m_clip) { int clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(this->m_clip, this->m_framenumber); - BKE_tracking_stabilization_data_get(&this->m_clip->tracking, clip_framenr, getWidth(), getHeight(), loc, &scale, &angle); + BKE_tracking_stabilization_data_get(this->m_clip, clip_framenr, getWidth(), getHeight(), loc, &scale, &angle); } switch (this->m_attribute) { case MCA_SCALE: diff --git a/source/blender/depsgraph/DEG_depsgraph_build.h b/source/blender/depsgraph/DEG_depsgraph_build.h index 49b648c7dae..0945da439ef 100644 --- a/source/blender/depsgraph/DEG_depsgraph_build.h +++ b/source/blender/depsgraph/DEG_depsgraph_build.h @@ -42,6 +42,8 @@ struct Depsgraph; struct Main; struct Scene; +struct Group; +struct EffectorWeights; #ifdef __cplusplus extern "C" { @@ -79,6 +81,7 @@ void DEG_scene_graph_free(struct Scene *scene); */ struct DepsNodeHandle; +struct CacheFile; struct Object; typedef enum eDepsSceneComponentType { @@ -100,15 +103,23 @@ typedef enum eDepsObjectComponentType { DEG_OB_COMP_EVAL_PARTICLES, /* Particle Systems Component */ DEG_OB_COMP_SHADING, /* Material Shading Component */ + DEG_OB_COMP_CACHE, /* Cache Component */ } eDepsObjectComponentType; void DEG_add_scene_relation(struct DepsNodeHandle *node, struct Scene *scene, eDepsSceneComponentType component, const char *description); void DEG_add_object_relation(struct DepsNodeHandle *node, struct Object *ob, eDepsObjectComponentType component, const char *description); void DEG_add_bone_relation(struct DepsNodeHandle *handle, struct Object *ob, const char *bone_name, eDepsObjectComponentType component, const char *description); +void DEG_add_object_cache_relation(struct DepsNodeHandle *handle, struct CacheFile *cache_file, eDepsObjectComponentType component, const char *description); /* TODO(sergey): Remove once all geometry update is granular. */ void DEG_add_special_eval_flag(struct Depsgraph *graph, struct ID *id, short flag); +/* Utility functions for physics modifiers */ +typedef bool (*DEG_CollobjFilterFunction)(struct Object *obj, struct ModifierData *md); + +void DEG_add_collision_relations(struct DepsNodeHandle *handle, struct Scene *scene, Object *ob, struct Group *group, int layer, unsigned int modifier_type, DEG_CollobjFilterFunction fn, bool dupli, const char *name); +void DEG_add_forcefield_relations(struct DepsNodeHandle *handle, struct Scene *scene, Object *ob, struct EffectorWeights *eff, bool add_absorption, int skip_forcefield, const char *name); + /* ************************************************ */ #ifdef __cplusplus diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc index a397b48e19c..1812384440f 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc @@ -46,6 +46,7 @@ extern "C" { #include "DNA_action_types.h" #include "DNA_anim_types.h" #include "DNA_armature_types.h" +#include "DNA_cachefile_types.h" #include "DNA_camera_types.h" #include "DNA_constraint_types.h" #include "DNA_curve_types.h" @@ -339,6 +340,14 @@ void DepsgraphNodeBuilder::build_scene(Main *bmain, Scene *scene) if (scene->gpd) { build_gpencil(scene->gpd); } + + /* cache files */ + for (CacheFile *cachefile = static_cast<CacheFile *>(bmain->cachefiles.first); + cachefile; + cachefile = static_cast<CacheFile *>(cachefile->id.next)) + { + build_cachefile(cachefile); + } } void DepsgraphNodeBuilder::build_group(Scene *scene, @@ -1259,4 +1268,18 @@ void DepsgraphNodeBuilder::build_gpencil(bGPdata *gpd) build_animdata(gpd_id); } +void DepsgraphNodeBuilder::build_cachefile(CacheFile *cache_file) +{ + ID *cache_file_id = &cache_file->id; + + add_component_node(cache_file_id, DEPSNODE_TYPE_CACHE); + + add_operation_node(cache_file_id, DEPSNODE_TYPE_CACHE, + DEPSOP_TYPE_EXEC, NULL, + DEG_OPCODE_PLACEHOLDER, "Cache File Update"); + + add_id_node(cache_file_id); + build_animdata(cache_file_id); +} + } // namespace DEG diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h index 6ee0b8406a1..f378f076804 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h @@ -33,6 +33,7 @@ #include "intern/depsgraph_types.h" struct Base; +struct CacheFile; struct bGPdata; struct ListBase; struct GHash; @@ -144,6 +145,7 @@ struct DepsgraphNodeBuilder { void build_world(World *world); void build_compositor(Scene *scene); void build_gpencil(bGPdata *gpd); + void build_cachefile(CacheFile *cache_file); protected: Main *m_bmain; diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index 42b8260c05a..2148a3501d8 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -47,6 +47,7 @@ extern "C" { #include "DNA_anim_types.h" #include "DNA_armature_types.h" #include "DNA_camera_types.h" +#include "DNA_cachefile_types.h" #include "DNA_constraint_types.h" #include "DNA_curve_types.h" #include "DNA_effect_types.h" @@ -64,6 +65,7 @@ extern "C" { #include "DNA_scene_types.h" #include "DNA_texture_types.h" #include "DNA_world_types.h" +#include "DNA_object_force.h" #include "BKE_action.h" #include "BKE_armature.h" @@ -71,6 +73,7 @@ extern "C" { #include "BKE_constraint.h" #include "BKE_curve.h" #include "BKE_effect.h" +#include "BKE_collision.h" #include "BKE_fcurve.h" #include "BKE_group.h" #include "BKE_key.h" @@ -242,6 +245,69 @@ void DepsgraphRelationBuilder::add_operation_relation( } } +void DepsgraphRelationBuilder::add_collision_relations(const OperationKey &key, Scene *scene, Object *ob, Group *group, int layer, bool dupli, const char *name) +{ + unsigned int numcollobj; + Object **collobjs = get_collisionobjects_ext(scene, ob, group, layer, &numcollobj, eModifierType_Collision, dupli); + + for (unsigned int i = 0; i < numcollobj; i++) + { + Object *ob1 = collobjs[i]; + + ComponentKey trf_key(&ob1->id, DEPSNODE_TYPE_TRANSFORM); + add_relation(trf_key, key, DEPSREL_TYPE_STANDARD, name); + + ComponentKey coll_key(&ob1->id, DEPSNODE_TYPE_GEOMETRY); + add_relation(coll_key, key, DEPSREL_TYPE_STANDARD, name); + } + + if (collobjs) + MEM_freeN(collobjs); +} + +void DepsgraphRelationBuilder::add_forcefield_relations(const OperationKey &key, Scene *scene, Object *ob, ParticleSystem *psys, EffectorWeights *eff, bool add_absorption, const char *name) +{ + ListBase *effectors = pdInitEffectors(scene, ob, psys, eff, false); + + if (effectors) { + for (EffectorCache *eff = (EffectorCache *)effectors->first; eff; eff = eff->next) { + if (eff->ob != ob) { + ComponentKey eff_key(&eff->ob->id, DEPSNODE_TYPE_TRANSFORM); + add_relation(eff_key, key, DEPSREL_TYPE_STANDARD, name); + } + + if (eff->psys) { + if (eff->ob != ob) { + ComponentKey eff_key(&eff->ob->id, DEPSNODE_TYPE_EVAL_PARTICLES); + add_relation(eff_key, key, DEPSREL_TYPE_STANDARD, name); + + /* TODO: remove this when/if EVAL_PARTICLES is sufficient for up to date particles */ + ComponentKey mod_key(&eff->ob->id, DEPSNODE_TYPE_GEOMETRY); + add_relation(mod_key, key, DEPSREL_TYPE_STANDARD, name); + } + else if (eff->psys != psys) { + OperationKey eff_key(&eff->ob->id, DEPSNODE_TYPE_EVAL_PARTICLES, DEG_OPCODE_PSYS_EVAL, eff->psys->name); + add_relation(eff_key, key, DEPSREL_TYPE_STANDARD, name); + } + } + + if (eff->pd->forcefield == PFIELD_SMOKEFLOW && eff->pd->f_source) { + ComponentKey trf_key(&eff->pd->f_source->id, DEPSNODE_TYPE_TRANSFORM); + add_relation(trf_key, key, DEPSREL_TYPE_STANDARD, "Smoke Force Domain"); + + ComponentKey eff_key(&eff->pd->f_source->id, DEPSNODE_TYPE_GEOMETRY); + add_relation(eff_key, key, DEPSREL_TYPE_STANDARD, "Smoke Force Domain"); + } + + if (add_absorption && (eff->pd->flag & PFIELD_VISIBILITY)) { + add_collision_relations(key, scene, ob, NULL, eff->ob->lay, true, "Force Absorption"); + } + } + } + + pdEndEffectors(&effectors); +} + /* **** Functions to build relations between entities **** */ void DepsgraphRelationBuilder::build_scene(Main *bmain, Scene *scene) @@ -599,6 +665,18 @@ void DepsgraphRelationBuilder::build_constraints(Scene *scene, ID *id, eDepsNode TimeSourceKey time_src_key; add_relation(time_src_key, constraint_op_key, DEPSREL_TYPE_TIME, "[TimeSrc -> Animation]"); } + else if (cti->type == CONSTRAINT_TYPE_TRANSFORM_CACHE) { + /* TODO(kevin): This is more a TimeSource -> CacheFile -> Constraint dependency chain. */ + TimeSourceKey time_src_key; + add_relation(time_src_key, constraint_op_key, DEPSREL_TYPE_TIME, "[TimeSrc -> Animation]"); + + bTransformCacheConstraint *data = (bTransformCacheConstraint *)con->data; + + if (data->cache_file) { + ComponentKey cache_key(&data->cache_file->id, DEPSNODE_TYPE_CACHE); + add_relation(cache_key, constraint_op_key, DEPSREL_TYPE_CACHE, cti->name); + } + } else if (cti->get_constraint_targets) { ListBase targets = {NULL, NULL}; cti->get_constraint_targets(con, &targets); @@ -1124,20 +1202,13 @@ void DepsgraphRelationBuilder::build_particles(Scene *scene, Object *ob) } #endif - /* effectors */ - ListBase *effectors = pdInitEffectors(scene, ob, psys, part->effector_weights, false); - - if (effectors) { - for (EffectorCache *eff = (EffectorCache *)effectors->first; eff; eff = eff->next) { - if (eff->psys) { - // XXX: DAG_RL_DATA_DATA | DAG_RL_OB_DATA - ComponentKey eff_key(&eff->ob->id, DEPSNODE_TYPE_GEOMETRY); // xxx: particles instead? - add_relation(eff_key, psys_key, DEPSREL_TYPE_STANDARD, "Particle Field"); - } - } + /* collisions */ + if (part->type != PART_HAIR) { + add_collision_relations(psys_key, scene, ob, part->collision_group, ob->lay, true, "Particle Collision"); } - pdEndEffectors(&effectors); + /* effectors */ + add_forcefield_relations(psys_key, scene, ob, psys, part->effector_weights, part->type == PART_HAIR, "Particle Field"); /* boids */ if (part->boids) { diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.h b/source/blender/depsgraph/intern/builder/deg_builder_relations.h index ce6d2c961fd..46e65d464a4 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.h @@ -63,6 +63,8 @@ struct bConstraint; struct Scene; struct Tex; struct World; +struct EffectorWeights; +struct ParticleSystem; struct PropertyRNA; @@ -244,6 +246,9 @@ struct DepsgraphRelationBuilder void build_compositor(Scene *scene); void build_gpencil(ID *owner, bGPdata *gpd); + void add_collision_relations(const OperationKey &key, Scene *scene, Object *ob, Group *group, int layer, bool dupli, const char *name); + void add_forcefield_relations(const OperationKey &key, Scene *scene, Object *ob, ParticleSystem *psys, EffectorWeights *eff, bool add_absorption, const char *name); + template <typename KeyType> OperationDepsNode *find_operation_node(const KeyType &key); diff --git a/source/blender/depsgraph/intern/debug/deg_debug_graphviz.cc b/source/blender/depsgraph/intern/debug/deg_debug_graphviz.cc index 9088e3bf403..70cd5f11a47 100644 --- a/source/blender/depsgraph/intern/debug/deg_debug_graphviz.cc +++ b/source/blender/depsgraph/intern/debug/deg_debug_graphviz.cc @@ -90,6 +90,7 @@ static const int deg_debug_node_type_color_map[][2] = { {DEPSNODE_TYPE_GEOMETRY, 8}, {DEPSNODE_TYPE_SEQUENCER, 9}, {DEPSNODE_TYPE_SHADING, 10}, + {DEPSNODE_TYPE_CACHE, 11}, {-1, 0} }; #endif @@ -401,6 +402,7 @@ static void deg_debug_graphviz_node(const DebugContext &ctx, case DEPSNODE_TYPE_EVAL_POSE: case DEPSNODE_TYPE_BONE: case DEPSNODE_TYPE_SHADING: + case DEPSNODE_TYPE_CACHE: case DEPSNODE_TYPE_EVAL_PARTICLES: { ComponentDepsNode *comp_node = (ComponentDepsNode *)node; diff --git a/source/blender/depsgraph/intern/depsgraph_build.cc b/source/blender/depsgraph/intern/depsgraph_build.cc index b1271c39851..7a3b19e82c6 100644 --- a/source/blender/depsgraph/intern/depsgraph_build.cc +++ b/source/blender/depsgraph/intern/depsgraph_build.cc @@ -33,13 +33,18 @@ #include "MEM_guardedalloc.h" extern "C" { +#include "DNA_cachefile_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "DNA_object_force.h" #include "BLI_utildefines.h" #include "BLI_ghash.h" #include "BKE_main.h" +#include "BKE_collision.h" +#include "BKE_effect.h" +#include "BKE_modifier.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_debug.h" @@ -89,6 +94,7 @@ static DEG::eDepsNode_Type deg_build_object_component_type( case DEG_OB_COMP_BONE: return DEG::DEPSNODE_TYPE_BONE; case DEG_OB_COMP_EVAL_PARTICLES: return DEG::DEPSNODE_TYPE_EVAL_PARTICLES; case DEG_OB_COMP_SHADING: return DEG::DEPSNODE_TYPE_SHADING; + case DEG_OB_COMP_CACHE: return DEG::DEPSNODE_TYPE_CACHE; } return DEG::DEPSNODE_TYPE_UNDEFINED; } @@ -126,6 +132,20 @@ void DEG_add_object_relation(DepsNodeHandle *handle, description); } +void DEG_add_object_cache_relation(DepsNodeHandle *handle, + CacheFile *cache_file, + eDepsObjectComponentType component, + const char *description) +{ + DEG::eDepsNode_Type type = deg_build_object_component_type(component); + DEG::ComponentKey comp_key(&cache_file->id, type); + DEG::DepsNodeHandle *deg_handle = get_handle(handle); + deg_handle->builder->add_node_handle_relation(comp_key, + deg_handle, + DEG::DEPSREL_TYPE_CACHE, + description); +} + void DEG_add_bone_relation(DepsNodeHandle *handle, Object *ob, const char *bone_name, @@ -288,3 +308,52 @@ void DEG_scene_graph_free(Scene *scene) scene->depsgraph = NULL; } } + +void DEG_add_collision_relations(DepsNodeHandle *handle, Scene *scene, Object *ob, Group *group, int layer, unsigned int modifier_type, DEG_CollobjFilterFunction fn, bool dupli, const char *name) +{ + unsigned int numcollobj; + Object **collobjs = get_collisionobjects_ext(scene, ob, group, layer, &numcollobj, modifier_type, dupli); + + for (unsigned int i = 0; i < numcollobj; i++) { + Object *ob1 = collobjs[i]; + + if (!fn || fn(ob1, modifiers_findByType(ob1, (ModifierType)modifier_type))) { + DEG_add_object_relation(handle, ob1, DEG_OB_COMP_TRANSFORM, name); + DEG_add_object_relation(handle, ob1, DEG_OB_COMP_GEOMETRY, name); + } + } + + if (collobjs) + MEM_freeN(collobjs); +} + +void DEG_add_forcefield_relations(DepsNodeHandle *handle, Scene *scene, Object *ob, EffectorWeights *effector_weights, bool add_absorption, int skip_forcefield, const char *name) +{ + ListBase *effectors = pdInitEffectors(scene, ob, NULL, effector_weights, false); + + if (effectors) { + for (EffectorCache *eff = (EffectorCache*)effectors->first; eff; eff = eff->next) { + if (eff->ob != ob && eff->pd->forcefield != skip_forcefield) { + DEG_add_object_relation(handle, eff->ob, DEG_OB_COMP_TRANSFORM, name); + + if (eff->psys) { + DEG_add_object_relation(handle, eff->ob, DEG_OB_COMP_EVAL_PARTICLES, name); + + /* TODO: remove this when/if EVAL_PARTICLES is sufficient for up to date particles */ + DEG_add_object_relation(handle, eff->ob, DEG_OB_COMP_GEOMETRY, name); + } + + if (eff->pd->forcefield == PFIELD_SMOKEFLOW && eff->pd->f_source) { + DEG_add_object_relation(handle, eff->pd->f_source, DEG_OB_COMP_TRANSFORM, "Smoke Force Domain"); + DEG_add_object_relation(handle, eff->pd->f_source, DEG_OB_COMP_GEOMETRY, "Smoke Force Domain"); + } + + if (add_absorption && (eff->pd->flag & PFIELD_VISIBILITY)) { + DEG_add_collision_relations(handle, scene, ob, NULL, eff->ob->lay, eModifierType_Collision, NULL, true, "Force Absorption"); + } + } + } + } + + pdEndEffectors(&effectors); +} diff --git a/source/blender/depsgraph/intern/depsgraph_query.cc b/source/blender/depsgraph/intern/depsgraph_query.cc index cac4eaae215..7f2f6a65f5e 100644 --- a/source/blender/depsgraph/intern/depsgraph_query.cc +++ b/source/blender/depsgraph/intern/depsgraph_query.cc @@ -33,6 +33,7 @@ #include "MEM_guardedalloc.h" extern "C" { +#include "BKE_idcode.h" #include "BKE_main.h" #include "DEG_depsgraph_query.h" @@ -42,7 +43,7 @@ extern "C" { bool DEG_id_type_tagged(Main *bmain, short idtype) { - return bmain->id_tag_update[((unsigned char *)&idtype)[0]] != 0; + return bmain->id_tag_update[BKE_idcode_to_index(idtype)] != 0; } short DEG_get_eval_flags_for_id(Depsgraph *graph, ID *id) diff --git a/source/blender/depsgraph/intern/depsgraph_tag.cc b/source/blender/depsgraph/intern/depsgraph_tag.cc index ea5afaab3f7..b7b62bd59f9 100644 --- a/source/blender/depsgraph/intern/depsgraph_tag.cc +++ b/source/blender/depsgraph/intern/depsgraph_tag.cc @@ -44,6 +44,7 @@ extern "C" { #include "BLI_task.h" +#include "BKE_idcode.h" #include "BKE_library.h" #include "BKE_main.h" #include "BKE_node.h" @@ -259,10 +260,8 @@ void DEG_id_type_tag(Main *bmain, short idtype) DEG_id_type_tag(bmain, ID_WO); DEG_id_type_tag(bmain, ID_SCE); } - /* We tag based on first ID type character to avoid - * looping over all ID's in case there are no tags. - */ - bmain->id_tag_update[((unsigned char *)&idtype)[0]] = 1; + + bmain->id_tag_update[BKE_idcode_to_index(idtype)] = 1; } /* Recursively push updates out to all nodes dependent on this, @@ -373,10 +372,7 @@ void DEG_ids_check_recalc(Main *bmain, Scene *scene, bool time) ListBase *lb = lbarray[a]; ID *id = (ID *)lb->first; - /* We tag based on first ID type character to avoid - * looping over all ID's in case there are no tags. - */ - if (id && bmain->id_tag_update[(unsigned char)id->name[0]]) { + if (id && bmain->id_tag_update[BKE_idcode_to_index(GS(id->name))]) { updated = true; break; } @@ -401,10 +397,7 @@ void DEG_ids_clear_recalc(Main *bmain) ListBase *lb = lbarray[a]; ID *id = (ID *)lb->first; - /* We tag based on first ID type character to avoid - * looping over all ID's in case there are no tags. - */ - if (id && bmain->id_tag_update[(unsigned char)id->name[0]]) { + if (id && bmain->id_tag_update[BKE_idcode_to_index(GS(id->name))]) { for (; id; id = (ID *)id->next) { id->tag &= ~(LIB_TAG_ID_RECALC | LIB_TAG_ID_RECALC_DATA); diff --git a/source/blender/depsgraph/intern/depsgraph_types.h b/source/blender/depsgraph/intern/depsgraph_types.h index 7516ccbfdc2..effd34a0eb9 100644 --- a/source/blender/depsgraph/intern/depsgraph_types.h +++ b/source/blender/depsgraph/intern/depsgraph_types.h @@ -133,6 +133,8 @@ typedef enum eDepsNode_Type { DEPSNODE_TYPE_EVAL_PARTICLES = 23, /* Material Shading Component */ DEPSNODE_TYPE_SHADING = 24, + /* Cache Component */ + DEPSNODE_TYPE_CACHE = 25, } eDepsNode_Type; /* Identifiers for common operations (as an enum). */ @@ -330,6 +332,9 @@ typedef enum eDepsRelation_Type { /* relationship is used to trigger editor/screen updates */ DEPSREL_TYPE_UPDATE_UI, + + /* cache dependency */ + DEPSREL_TYPE_CACHE, } eDepsRelation_Type; } // namespace DEG diff --git a/source/blender/depsgraph/intern/eval/deg_eval.cc b/source/blender/depsgraph/intern/eval/deg_eval.cc index c3fd202d832..f8cca5393e2 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval.cc @@ -304,7 +304,7 @@ static void schedule_node(TaskPool *pool, Depsgraph *graph, unsigned int layers, deg_task_run_func, node, false, - TASK_PRIORITY_LOW, + TASK_PRIORITY_HIGH, thread_id); } } diff --git a/source/blender/depsgraph/intern/eval/deg_eval_flush.cc b/source/blender/depsgraph/intern/eval/deg_eval_flush.cc index f9e1504b3ce..7c6c25bef0d 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_flush.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_flush.cc @@ -33,7 +33,7 @@ #include "intern/eval/deg_eval_flush.h" // TODO(sergey): Use some sort of wrapper. -#include <queue> +#include <deque> extern "C" { #include "DNA_object_types.h" @@ -71,7 +71,7 @@ void lib_id_recalc_data_tag(Main *bmain, ID *id) } /* namespace */ -typedef std::queue<OperationDepsNode *> FlushQueue; +typedef std::deque<OperationDepsNode *> FlushQueue; static void flush_init_func(void *data_v, int i) { @@ -122,20 +122,60 @@ void deg_graph_flush_updates(Main *bmain, Depsgraph *graph) */ GSET_FOREACH_BEGIN(OperationDepsNode *, node, graph->entry_tags) { - queue.push(node); + queue.push_back(node); node->scheduled = true; } GSET_FOREACH_END(); + int num_flushed_objects = 0; while (!queue.empty()) { OperationDepsNode *node = queue.front(); - queue.pop(); + queue.pop_front(); for (;;) { node->flag |= DEPSOP_FLAG_NEEDS_UPDATE; ComponentDepsNode *comp_node = node->owner; IDDepsNode *id_node = comp_node->owner; + + ID *id = id_node->id; + if(id_node->done == 0) { + deg_editors_id_update(bmain, id); + lib_id_recalc_tag(bmain, id); + /* TODO(sergey): For until we've got proper data nodes in the graph. */ + lib_id_recalc_data_tag(bmain, id); + } + + if(comp_node->done == 0) { + Object *object = NULL; + if (GS(id->name) == ID_OB) { + object = (Object *)id; + if(id_node->done == 0) { + ++num_flushed_objects; + } + } + foreach (OperationDepsNode *op, comp_node->operations) { + op->flag |= DEPSOP_FLAG_NEEDS_UPDATE; + } + if (object != NULL) { + /* This code is used to preserve those areas which does + * direct object update, + * + * Plus it ensures visibility changes and relations and + * layers visibility update has proper flags to work with. + */ + if (comp_node->type == DEPSNODE_TYPE_ANIMATION) { + object->recalc |= OB_RECALC_TIME; + } + else if (comp_node->type == DEPSNODE_TYPE_TRANSFORM) { + object->recalc |= OB_RECALC_OB; + } + else { + object->recalc |= OB_RECALC_DATA; + } + } + } + id_node->done = 1; comp_node->done = 1; @@ -154,7 +194,7 @@ void deg_graph_flush_updates(Main *bmain, Depsgraph *graph) foreach (DepsRelation *rel, node->outlinks) { OperationDepsNode *to_node = (OperationDepsNode *)rel->to; if (to_node->scheduled == false) { - queue.push(to_node); + queue.push_front(to_node); to_node->scheduled = true; } } @@ -162,52 +202,7 @@ void deg_graph_flush_updates(Main *bmain, Depsgraph *graph) } } } - - GHASH_FOREACH_BEGIN(DEG::IDDepsNode *, id_node, graph->id_hash) - { - if (id_node->done == 1) { - ID *id = id_node->id; - Object *object = NULL; - - if (GS(id->name) == ID_OB) { - object = (Object *)id; - } - - deg_editors_id_update(bmain, id_node->id); - - lib_id_recalc_tag(bmain, id_node->id); - /* TODO(sergey): For until we've got proper data nodes in the graph. */ - lib_id_recalc_data_tag(bmain, id_node->id); - - GHASH_FOREACH_BEGIN(const ComponentDepsNode *, comp_node, id_node->components) - { - if (comp_node->done) { - foreach (OperationDepsNode *op, comp_node->operations) { - op->flag |= DEPSOP_FLAG_NEEDS_UPDATE; - } - if (object != NULL) { - /* This code is used to preserve those areas which does - * direct object update, - * - * Plus it ensures visibility changes and relations and - * layers visibility update has proper flags to work with. - */ - if (comp_node->type == DEPSNODE_TYPE_ANIMATION) { - object->recalc |= OB_RECALC_TIME; - } - else if (comp_node->type == DEPSNODE_TYPE_TRANSFORM) { - object->recalc |= OB_RECALC_OB; - } - else { - object->recalc |= OB_RECALC_DATA; - } - } - } - } - GHASH_FOREACH_END(); - } - } - GHASH_FOREACH_END(); + DEG_DEBUG_PRINTF("Update flushed to %d objects\n", num_flushed_objects); } static void graph_clear_func(void *data_v, int i) diff --git a/source/blender/depsgraph/intern/nodes/deg_node_component.cc b/source/blender/depsgraph/intern/nodes/deg_node_component.cc index 5832c458896..01f33b6368b 100644 --- a/source/blender/depsgraph/intern/nodes/deg_node_component.cc +++ b/source/blender/depsgraph/intern/nodes/deg_node_component.cc @@ -366,6 +366,11 @@ static DepsNodeFactoryImpl<ParticlesComponentDepsNode> DNTI_EVAL_PARTICLES; DEG_DEPSNODE_DEFINE(ShadingComponentDepsNode, DEPSNODE_TYPE_SHADING, "Shading Component"); static DepsNodeFactoryImpl<ShadingComponentDepsNode> DNTI_SHADING; +/* Cache Component Defines ============================ */ + +DEG_DEPSNODE_DEFINE(CacheComponentDepsNode, DEPSNODE_TYPE_CACHE, "Cache Component"); +static DepsNodeFactoryImpl<CacheComponentDepsNode> DNTI_CACHE; + /* Node Types Register =================================== */ @@ -383,6 +388,8 @@ void deg_register_component_depsnodes() deg_register_node_typeinfo(&DNTI_EVAL_PARTICLES); deg_register_node_typeinfo(&DNTI_SHADING); + + deg_register_node_typeinfo(&DNTI_CACHE); } } // namespace DEG diff --git a/source/blender/depsgraph/intern/nodes/deg_node_component.h b/source/blender/depsgraph/intern/nodes/deg_node_component.h index acccb1cdcd4..7dec8eaaa90 100644 --- a/source/blender/depsgraph/intern/nodes/deg_node_component.h +++ b/source/blender/depsgraph/intern/nodes/deg_node_component.h @@ -209,6 +209,10 @@ struct ShadingComponentDepsNode : public ComponentDepsNode { DEG_DEPSNODE_DECLARE; }; +struct CacheComponentDepsNode : public ComponentDepsNode { + DEG_DEPSNODE_DECLARE; +}; + void deg_register_component_depsnodes(); diff --git a/source/blender/editors/CMakeLists.txt b/source/blender/editors/CMakeLists.txt index 084006ce277..1559512d713 100644 --- a/source/blender/editors/CMakeLists.txt +++ b/source/blender/editors/CMakeLists.txt @@ -19,6 +19,10 @@ # ***** END GPL LICENSE BLOCK ***** if(WITH_BLENDER) + if(WITH_INPUT_NDOF) + add_definitions(-DWITH_INPUT_NDOF) + endif() + add_subdirectory(animation) add_subdirectory(armature) add_subdirectory(curve) diff --git a/source/blender/editors/animation/anim_channels_defines.c b/source/blender/editors/animation/anim_channels_defines.c index 752544f65e1..d727ea0a957 100644 --- a/source/blender/editors/animation/anim_channels_defines.c +++ b/source/blender/editors/animation/anim_channels_defines.c @@ -40,6 +40,7 @@ #include "DNA_anim_types.h" #include "DNA_armature_types.h" +#include "DNA_cachefile_types.h" #include "DNA_camera_types.h" #include "DNA_object_types.h" #include "DNA_particle_types.h" @@ -1578,6 +1579,88 @@ static bAnimChannelType ACF_DSTEX = /* Camera Expander ------------------------------------------- */ // TODO: just get this from RNA? +static int acf_dscachefile_icon(bAnimListElem *ale) +{ + UNUSED_VARS(ale); + return ICON_FILE; +} + +/* get the appropriate flag(s) for the setting when it is valid */ +static int acf_dscachefile_setting_flag(bAnimContext *ac, eAnimChannel_Settings setting, bool *neg) +{ + /* clear extra return data first */ + *neg = false; + + switch (setting) { + case ACHANNEL_SETTING_EXPAND: /* expanded */ + return CACHEFILE_DS_EXPAND; + + case ACHANNEL_SETTING_MUTE: /* mute (only in NLA) */ + return ADT_NLA_EVAL_OFF; + + case ACHANNEL_SETTING_VISIBLE: /* visible (only in Graph Editor) */ + *neg = true; + return ADT_CURVES_NOT_VISIBLE; + + case ACHANNEL_SETTING_SELECT: /* selected */ + return ADT_UI_SELECTED; + + default: /* unsupported */ + return 0; + } + + UNUSED_VARS(ac); +} + +/* get pointer to the setting */ +static void *acf_dscachefile_setting_ptr(bAnimListElem *ale, eAnimChannel_Settings setting, short *type) +{ + CacheFile *cache_file = (CacheFile *)ale->data; + + /* clear extra return data first */ + *type = 0; + + switch (setting) { + case ACHANNEL_SETTING_EXPAND: /* expanded */ + return GET_ACF_FLAG_PTR(cache_file->flag, type); + + case ACHANNEL_SETTING_SELECT: /* selected */ + case ACHANNEL_SETTING_MUTE: /* muted (for NLA only) */ + case ACHANNEL_SETTING_VISIBLE: /* visible (for Graph Editor only) */ + if (cache_file->adt) { + return GET_ACF_FLAG_PTR(cache_file->adt->flag, type); + } + + return NULL; + + default: /* unsupported */ + return NULL; + } +} + +/* CacheFile expander type define. */ +static bAnimChannelType ACF_DSCACHEFILE = +{ + "Cache File Expander", /* type name */ + ACHANNEL_ROLE_EXPANDER, /* role */ + + acf_generic_dataexpand_color, /* backdrop color */ + acf_generic_dataexpand_backdrop, /* backdrop */ + acf_generic_indention_1, /* indent level */ + acf_generic_basic_offset, /* offset */ + + acf_generic_idblock_name, /* name */ + acf_generic_idfill_name_prop, /* name prop */ + acf_dscachefile_icon, /* icon */ + + acf_generic_dataexpand_setting_valid, /* has setting */ + acf_dscachefile_setting_flag, /* flag for setting */ + acf_dscachefile_setting_ptr /* pointer for setting */ +}; + +/* Camera Expander ------------------------------------------- */ + +// TODO: just get this from RNA? static int acf_dscam_icon(bAnimListElem *UNUSED(ale)) { return ICON_CAMERA_DATA; @@ -3388,6 +3471,7 @@ static void ANIM_init_channel_typeinfo_data(void) animchannelTypeInfo[type++] = &ACF_DSMAT; /* Material Channel */ animchannelTypeInfo[type++] = &ACF_DSLAM; /* Lamp Channel */ animchannelTypeInfo[type++] = &ACF_DSCAM; /* Camera Channel */ + animchannelTypeInfo[type++] = &ACF_DSCACHEFILE; /* CacheFile Channel */ animchannelTypeInfo[type++] = &ACF_DSCUR; /* Curve Channel */ animchannelTypeInfo[type++] = &ACF_DSSKEY; /* ShapeKey Channel */ animchannelTypeInfo[type++] = &ACF_DSWOR; /* World Channel */ @@ -3817,7 +3901,12 @@ static void achannel_setting_flush_widget_cb(bContext *C, void *ale_npoin, void /* send notifiers before doing anything else... */ WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL); - + + /* verify that we have a channel to operate on. */ + if (!ale_setting) { + return; + } + if (ale_setting->type == ANIMTYPE_GPLAYER) WM_event_add_notifier(C, NC_GPENCIL | ND_DATA, NULL); @@ -3825,17 +3914,13 @@ static void achannel_setting_flush_widget_cb(bContext *C, void *ale_npoin, void if (ANIM_animdata_get_context(C, &ac) == 0) return; - /* verify that we have a channel to operate on, and that it has all we need */ - if (ale_setting) { - /* check if the setting is on... */ - on = ANIM_channel_setting_get(&ac, ale_setting, setting); - - /* on == -1 means setting not found... */ - if (on == -1) - return; - } - else + /* check if the setting is on... */ + on = ANIM_channel_setting_get(&ac, ale_setting, setting); + + /* on == -1 means setting not found... */ + if (on == -1) { return; + } /* get all channels that can possibly be chosen - but ignore hierarchy */ filter = ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_CHANNELS; diff --git a/source/blender/editors/animation/anim_channels_edit.c b/source/blender/editors/animation/anim_channels_edit.c index af9b0a176f5..cb65a9aecad 100644 --- a/source/blender/editors/animation/anim_channels_edit.c +++ b/source/blender/editors/animation/anim_channels_edit.c @@ -120,6 +120,7 @@ void ANIM_set_active_channel(bAnimContext *ac, void *data, eAnimCont_Types datat case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */ case ANIMTYPE_DSLAM: case ANIMTYPE_DSCAM: + case ANIMTYPE_DSCACHEFILE: case ANIMTYPE_DSCUR: case ANIMTYPE_DSSKEY: case ANIMTYPE_DSWOR: @@ -175,6 +176,7 @@ void ANIM_set_active_channel(bAnimContext *ac, void *data, eAnimCont_Types datat case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */ case ANIMTYPE_DSLAM: case ANIMTYPE_DSCAM: + case ANIMTYPE_DSCACHEFILE: case ANIMTYPE_DSCUR: case ANIMTYPE_DSSKEY: case ANIMTYPE_DSWOR: @@ -275,6 +277,7 @@ void ANIM_deselect_anim_channels(bAnimContext *ac, void *data, eAnimCont_Types d case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */ case ANIMTYPE_DSLAM: case ANIMTYPE_DSCAM: + case ANIMTYPE_DSCACHEFILE: case ANIMTYPE_DSCUR: case ANIMTYPE_DSSKEY: case ANIMTYPE_DSWOR: @@ -370,6 +373,7 @@ void ANIM_deselect_anim_channels(bAnimContext *ac, void *data, eAnimCont_Types d case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */ case ANIMTYPE_DSLAM: case ANIMTYPE_DSCAM: + case ANIMTYPE_DSCACHEFILE: case ANIMTYPE_DSCUR: case ANIMTYPE_DSSKEY: case ANIMTYPE_DSWOR: @@ -1686,7 +1690,7 @@ static int animchannels_delete_exec(bContext *C, wmOperator *UNUSED(op)) bGPDlayer *gpl = (bGPDlayer *)ale->data; /* try to delete the layer's data and the layer itself */ - free_gpencil_frames(gpl); + BKE_gpencil_free_frames(gpl); BLI_freelinkN(&gpd->layers, gpl); break; } @@ -2716,6 +2720,7 @@ static int mouse_anim_channels(bContext *C, bAnimContext *ac, int channel_index, case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */ case ANIMTYPE_DSLAM: case ANIMTYPE_DSCAM: + case ANIMTYPE_DSCACHEFILE: case ANIMTYPE_DSCUR: case ANIMTYPE_DSSKEY: case ANIMTYPE_DSWOR: diff --git a/source/blender/editors/animation/anim_filter.c b/source/blender/editors/animation/anim_filter.c index 88d96c531e0..5cd305f69f5 100644 --- a/source/blender/editors/animation/anim_filter.c +++ b/source/blender/editors/animation/anim_filter.c @@ -53,6 +53,7 @@ #include "DNA_anim_types.h" #include "DNA_armature_types.h" #include "DNA_camera_types.h" +#include "DNA_cachefile_types.h" #include "DNA_lamp_types.h" #include "DNA_lattice_types.h" #include "DNA_linestyle_types.h" @@ -199,6 +200,16 @@ static bool actedit_get_context(bAnimContext *ac, SpaceAction *saction) ac->mode = saction->mode; return true; + + case SACTCONT_CACHEFILE: /* Cache File */ /* XXX review how this mode is handled... */ + /* update scene-pointer (no need to check for pinning yet, as not implemented) */ + saction->ads.source = (ID *)ac->scene; + + ac->datatype = ANIMCONT_CHANNEL; + ac->data = &saction->ads; + + ac->mode = saction->mode; + return true; case SACTCONT_MASK: /* Mask */ /* XXX review how this mode is handled... */ { @@ -660,6 +671,19 @@ static bAnimListElem *make_new_animlistelem(void *data, short datatype, ID *owne ale->adt = BKE_animdata_from_id(data); break; } + case ANIMTYPE_DSCACHEFILE: + { + CacheFile *cache_file = (CacheFile *)data; + AnimData *adt = cache_file->adt; + + ale->flag = FILTER_CACHEFILE_OBJD(cache_file); + + ale->key_data = (adt) ? adt->action : NULL; + ale->datatype = ALE_ACT; + + ale->adt = BKE_animdata_from_id(data); + break; + } case ANIMTYPE_DSCUR: { Curve *cu = (Curve *)data; @@ -1751,6 +1775,42 @@ static size_t animdata_filter_ds_gpencil(bAnimContext *ac, ListBase *anim_data, return items; } +/* Helper for Cache File data integrated with main DopeSheet */ +static size_t animdata_filter_ds_cachefile(bAnimContext *ac, ListBase *anim_data, bDopeSheet *ads, CacheFile *cache_file, int filter_mode) +{ + ListBase tmp_data = {NULL, NULL}; + size_t tmp_items = 0; + size_t items = 0; + + /* add relevant animation channels for Cache File */ + BEGIN_ANIMFILTER_SUBCHANNELS(FILTER_CACHEFILE_OBJD(cache_file)) + { + /* add animation channels */ + tmp_items += animfilter_block_data(ac, &tmp_data, ads, &cache_file->id, filter_mode); + } + END_ANIMFILTER_SUBCHANNELS; + + /* did we find anything? */ + if (tmp_items) { + /* include data-expand widget first */ + if (filter_mode & ANIMFILTER_LIST_CHANNELS) { + /* check if filtering by active status */ + // XXX: active check here needs checking + if (ANIMCHANNEL_ACTIVEOK(cache_file)) { + ANIMCHANNEL_NEW_CHANNEL(cache_file, ANIMTYPE_DSCACHEFILE, cache_file); + } + } + + /* now add the list of collected channels */ + BLI_movelisttolist(anim_data, &tmp_data); + BLI_assert(BLI_listbase_is_empty(&tmp_data)); + items += tmp_items; + } + + /* return the number of items added to the list */ + return items; +} + /* Helper for Mask Editing - mask layers */ static size_t animdata_filter_mask_data(ListBase *anim_data, Mask *mask, const int filter_mode) { @@ -2839,6 +2899,12 @@ static size_t animdata_filter_dopesheet(bAnimContext *ac, ListBase *anim_data, b filter_mode |= ANIMFILTER_SELEDIT; } + /* Cache files level animations (frame duration and such). */ + CacheFile *cache_file = G.main->cachefiles.first; + for (; cache_file; cache_file = cache_file->id.next) { + items += animdata_filter_ds_cachefile(ac, anim_data, ads, cache_file, filter_mode); + } + /* scene-linked animation - e.g. world, compositing nodes, scene anim (including sequencer currently) */ items += animdata_filter_dopesheet_scene(ac, anim_data, ads, scene, filter_mode); @@ -2950,7 +3016,11 @@ static size_t animdata_filter_animchan(bAnimContext *ac, ListBase *anim_data, bD case ANIMTYPE_OBJECT: items += animdata_filter_dopesheet_ob(ac, anim_data, ads, channel->data, filter_mode); break; - + + case ANIMTYPE_DSCACHEFILE: + items += animdata_filter_ds_cachefile(ac, anim_data, ads, channel->data, filter_mode); + break; + case ANIMTYPE_ANIMDATA: items += animfilter_block_data(ac, anim_data, ads, channel->id, filter_mode); break; diff --git a/source/blender/editors/animation/keyframes_draw.c b/source/blender/editors/animation/keyframes_draw.c index 6e776953356..5f675e690b9 100644 --- a/source/blender/editors/animation/keyframes_draw.c +++ b/source/blender/editors/animation/keyframes_draw.c @@ -44,6 +44,7 @@ #include "BLI_utildefines.h" #include "DNA_anim_types.h" +#include "DNA_cachefile_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_gpencil_types.h" @@ -965,6 +966,37 @@ void ob_to_keylist(bDopeSheet *ads, Object *ob, DLRBT_Tree *keys, DLRBT_Tree *bl ANIM_animdata_freelist(&anim_data); } +void cachefile_to_keylist(bDopeSheet *ads, CacheFile *cache_file, DLRBT_Tree *keys, DLRBT_Tree *blocks) +{ + if (cache_file == NULL) { + return; + } + + /* create a dummy wrapper data to work with */ + bAnimListElem dummychan = {NULL}; + dummychan.type = ANIMTYPE_DSCACHEFILE; + dummychan.data = cache_file; + dummychan.id = &cache_file->id; + dummychan.adt = cache_file->adt; + + bAnimContext ac = {NULL}; + ac.ads = ads; + ac.data = &dummychan; + ac.datatype = ANIMCONT_CHANNEL; + + /* get F-Curves to take keyframes from */ + ListBase anim_data = { NULL, NULL }; + int filter = ANIMFILTER_DATA_VISIBLE; // curves only + ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); + + /* loop through each F-Curve, grabbing the keyframes */ + for (bAnimListElem *ale = anim_data.first; ale; ale = ale->next) { + fcurve_to_keylist(ale->adt, ale->data, keys, blocks); + } + + ANIM_animdata_freelist(&anim_data); +} + void fcurve_to_keylist(AnimData *adt, FCurve *fcu, DLRBT_Tree *keys, DLRBT_Tree *blocks) { BezTriple *bezt; diff --git a/source/blender/editors/gpencil/drawgpencil.c b/source/blender/editors/gpencil/drawgpencil.c index bd09616243b..4ef76f5ee25 100644 --- a/source/blender/editors/gpencil/drawgpencil.c +++ b/source/blender/editors/gpencil/drawgpencil.c @@ -1359,7 +1359,7 @@ static void gp_draw_data_layers( continue; /* get frame to draw */ - gpf = gpencil_layer_getframe(gpl, cfra, 0); + gpf = BKE_gpencil_layer_getframe(gpl, cfra, 0); if (gpf == NULL) continue; @@ -1533,7 +1533,7 @@ static void gp_draw_data_all(Scene *scene, bGPdata *gpd, int offsx, int offsy, i if (gpd_source) { ToolSettings *ts = scene->toolsettings; - bGPDbrush *brush = gpencil_brush_getactive(ts); + bGPDbrush *brush = BKE_gpencil_brush_getactive(ts); if (brush != NULL) { gp_draw_data(brush, ts->gp_sculpt.alpha, gpd_source, offsx, offsy, winx, winy, cfra, dflag); @@ -1546,7 +1546,7 @@ static void gp_draw_data_all(Scene *scene, bGPdata *gpd, int offsx, int offsy, i * if gpd_source == gpd, we don't have any object/track data and we can skip */ if (gpd_source == NULL || (gpd_source && gpd_source != gpd)) { ToolSettings *ts = scene->toolsettings; - bGPDbrush *brush = gpencil_brush_getactive(ts); + bGPDbrush *brush = BKE_gpencil_brush_getactive(ts); if (brush != NULL) { gp_draw_data(brush, ts->gp_sculpt.alpha, gpd, offsx, offsy, winx, winy, cfra, dflag); diff --git a/source/blender/editors/gpencil/editaction_gpencil.c b/source/blender/editors/gpencil/editaction_gpencil.c index 738496a67c6..bd4856f1b93 100644 --- a/source/blender/editors/gpencil/editaction_gpencil.c +++ b/source/blender/editors/gpencil/editaction_gpencil.c @@ -253,7 +253,7 @@ bool ED_gplayer_frames_delete(bGPDlayer *gpl) gpfn = gpf->next; if (gpf->flag & GP_FRAME_SELECT) - changed |= gpencil_layer_delframe(gpl, gpf); + changed |= BKE_gpencil_layer_delframe(gpl, gpf); } return changed; @@ -277,7 +277,7 @@ void ED_gplayer_frames_duplicate(bGPDlayer *gpl) bGPDframe *gpfd; /* duplicate frame, and deselect self */ - gpfd = gpencil_frame_duplicate(gpf); + gpfd = BKE_gpencil_frame_duplicate(gpf); gpf->flag &= ~GP_FRAME_SELECT; BLI_insertlinkafter(&gpl->frames, gpf, gpfd); @@ -323,7 +323,7 @@ static int gp_anim_copy_cfra = 0; /* This function frees any MEM_calloc'ed copy/paste buffer data */ void ED_gpencil_anim_copybuf_free(void) { - free_gpencil_layers(&gp_anim_copybuf); + BKE_gpencil_free_layers(&gp_anim_copybuf); BLI_listbase_clear(&gp_anim_copybuf); gp_anim_copy_firstframe = 999999999; @@ -364,7 +364,7 @@ bool ED_gpencil_anim_copybuf_copy(bAnimContext *ac) /* if frame is selected, make duplicate it and its strokes */ if (gpf->flag & GP_FRAME_SELECT) { /* make a copy of this frame */ - bGPDframe *new_frame = gpencil_frame_duplicate(gpf); + bGPDframe *new_frame = BKE_gpencil_frame_duplicate(gpf); BLI_addtail(&copied_frames, new_frame); /* extend extents for keyframes encountered */ @@ -475,7 +475,7 @@ bool ED_gpencil_anim_copybuf_paste(bAnimContext *ac, const short offset_mode) gpfs->framenum += offset; /* get frame to copy data into (if no frame returned, then just ignore) */ - gpf = gpencil_layer_getframe(gpld, gpfs->framenum, 1); + gpf = BKE_gpencil_layer_getframe(gpld, gpfs->framenum, 1); if (gpf) { bGPDstroke *gps, *gpsn; @@ -498,7 +498,7 @@ bool ED_gpencil_anim_copybuf_paste(bAnimContext *ac, const short offset_mode) /* if no strokes (i.e. new frame) added, free gpf */ if (BLI_listbase_is_empty(&gpf->strokes)) - gpencil_layer_delframe(gpld, gpf); + BKE_gpencil_layer_delframe(gpld, gpf); } /* unapply offset from buffer-frame */ diff --git a/source/blender/editors/gpencil/gpencil_brush.c b/source/blender/editors/gpencil/gpencil_brush.c index 1bb3b7e1ae7..fcb2ce02bde 100644 --- a/source/blender/editors/gpencil/gpencil_brush.c +++ b/source/blender/editors/gpencil/gpencil_brush.c @@ -841,7 +841,7 @@ static void gp_brush_clone_add(bContext *C, tGP_BrushEditData *gso) Scene *scene = gso->scene; bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); - bGPDframe *gpf = gpencil_layer_getframe(gpl, CFRA, true); + bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, CFRA, true); bGPDstroke *gps; float delta[3]; @@ -1185,7 +1185,7 @@ static void gpsculpt_brush_init_stroke(tGP_BrushEditData *gso) */ // XXX: should this be allowed when framelock is enabled? if (gpf->framenum != cfra) { - gpencil_frame_addcopy(gpl, cfra); + BKE_gpencil_frame_addcopy(gpl, cfra); } } } diff --git a/source/blender/editors/gpencil/gpencil_convert.c b/source/blender/editors/gpencil/gpencil_convert.c index 95ea13c399a..c502ed1aa83 100644 --- a/source/blender/editors/gpencil/gpencil_convert.c +++ b/source/blender/editors/gpencil/gpencil_convert.c @@ -1123,7 +1123,7 @@ static void gp_layer_to_curve(bContext *C, ReportList *reports, bGPdata *gpd, bG struct Main *bmain = CTX_data_main(C); View3D *v3d = CTX_wm_view3d(C); /* may be NULL */ Scene *scene = CTX_data_scene(C); - bGPDframe *gpf = gpencil_layer_getframe(gpl, CFRA, 0); + bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, CFRA, 0); bGPDstroke *gps, *prev_gps = NULL; Object *ob; Curve *cu; @@ -1235,7 +1235,7 @@ static bool gp_convert_check_has_valid_timing(bContext *C, bGPDlayer *gpl, wmOpe int i; bool valid = true; - if (!gpl || !(gpf = gpencil_layer_getframe(gpl, CFRA, 0)) || !(gps = gpf->strokes.first)) + if (!gpl || !(gpf = BKE_gpencil_layer_getframe(gpl, CFRA, 0)) || !(gps = gpf->strokes.first)) return false; do { @@ -1292,8 +1292,8 @@ static int gp_convert_poll(bContext *C) * and if we are not in edit mode! */ return ((sa && sa->spacetype == SPACE_VIEW3D) && - (gpl = gpencil_layer_getactive(gpd)) && - (gpf = gpencil_layer_getframe(gpl, CFRA, 0)) && + (gpl = BKE_gpencil_layer_getactive(gpd)) && + (gpf = BKE_gpencil_layer_getframe(gpl, CFRA, 0)) && (gpf->strokes.first) && (scene->obedit == NULL)); } @@ -1302,7 +1302,7 @@ static int gp_convert_layer_exec(bContext *C, wmOperator *op) { PropertyRNA *prop = RNA_struct_find_property(op->ptr, "use_timing_data"); bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDlayer *gpl = gpencil_layer_getactive(gpd); + bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); Scene *scene = CTX_data_scene(C); const int mode = RNA_enum_get(op->ptr, "type"); const bool norm_weights = RNA_boolean_get(op->ptr, "use_normalize_weights"); diff --git a/source/blender/editors/gpencil/gpencil_data.c b/source/blender/editors/gpencil/gpencil_data.c index e915446e461..9560ab188a4 100644 --- a/source/blender/editors/gpencil/gpencil_data.c +++ b/source/blender/editors/gpencil/gpencil_data.c @@ -75,9 +75,6 @@ #include "gpencil_intern.h" -/* maximum sizes of gp-session buffer */ -#define GP_STROKE_BUFFER_MAX 5000 - /* ************************************************ */ /* Datablock Operators */ @@ -97,7 +94,7 @@ static int gp_data_add_exec(bContext *C, wmOperator *op) bGPdata *gpd = (*gpd_ptr); id_us_min(&gpd->id); - *gpd_ptr = gpencil_data_addnew(DATA_("GPencil")); + *gpd_ptr = BKE_gpencil_data_addnew(DATA_("GPencil")); } /* notifiers */ @@ -184,10 +181,10 @@ static int gp_layer_add_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } if (*gpd_ptr == NULL) - *gpd_ptr = gpencil_data_addnew(DATA_("GPencil")); + *gpd_ptr = BKE_gpencil_data_addnew(DATA_("GPencil")); /* add new layer now */ - gpencil_layer_addnew(*gpd_ptr, DATA_("GP_Layer"), true); + BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("GP_Layer"), true); /* notifiers */ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); @@ -214,7 +211,7 @@ void GPENCIL_OT_layer_add(wmOperatorType *ot) static int gp_layer_remove_exec(bContext *C, wmOperator *op) { bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDlayer *gpl = gpencil_layer_getactive(gpd); + bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); /* sanity checks */ if (ELEM(NULL, gpd, gpl)) @@ -230,12 +227,12 @@ static int gp_layer_remove_exec(bContext *C, wmOperator *op) * - if this is the only layer, this naturally becomes NULL */ if (gpl->prev) - gpencil_layer_setactive(gpd, gpl->prev); + BKE_gpencil_layer_setactive(gpd, gpl->prev); else - gpencil_layer_setactive(gpd, gpl->next); + BKE_gpencil_layer_setactive(gpd, gpl->next); /* delete the layer now... */ - gpencil_layer_delete(gpd, gpl); + BKE_gpencil_layer_delete(gpd, gpl); /* notifiers */ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); @@ -267,7 +264,7 @@ enum { static int gp_layer_move_exec(bContext *C, wmOperator *op) { bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDlayer *gpl = gpencil_layer_getactive(gpd); + bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); int direction = RNA_enum_get(op->ptr, "type"); @@ -321,7 +318,7 @@ void GPENCIL_OT_layer_move(wmOperatorType *ot) static int gp_layer_copy_exec(bContext *C, wmOperator *UNUSED(op)) { bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDlayer *gpl = gpencil_layer_getactive(gpd); + bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); bGPDlayer *new_layer; /* sanity checks */ @@ -329,12 +326,12 @@ static int gp_layer_copy_exec(bContext *C, wmOperator *UNUSED(op)) return OPERATOR_CANCELLED; /* make copy of layer, and add it immediately after the existing layer */ - new_layer = gpencil_layer_duplicate(gpl); + new_layer = BKE_gpencil_layer_duplicate(gpl); BLI_insertlinkafter(&gpd->layers, gpl, new_layer); /* ensure new layer has a unique name, and is now the active layer */ BLI_uniquename(&gpd->layers, new_layer, DATA_("GP_Layer"), '.', offsetof(bGPDlayer, info), sizeof(new_layer->info)); - gpencil_layer_setactive(gpd, new_layer); + BKE_gpencil_layer_setactive(gpd, new_layer); /* notifiers */ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); @@ -362,7 +359,7 @@ void GPENCIL_OT_layer_duplicate(wmOperatorType *ot) static int gp_hide_exec(bContext *C, wmOperator *op) { bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDlayer *layer = gpencil_layer_getactive(gpd); + bGPDlayer *layer = BKE_gpencil_layer_getactive(gpd); bool unselected = RNA_boolean_get(op->ptr, "unselected"); /* sanity checks */ @@ -530,7 +527,7 @@ void GPENCIL_OT_unlock_all(wmOperatorType *ot) static int gp_isolate_layer_exec(bContext *C, wmOperator *op) { bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDlayer *layer = gpencil_layer_getactive(gpd); + bGPDlayer *layer = BKE_gpencil_layer_getactive(gpd); bGPDlayer *gpl; int flags = GP_LAYER_LOCKED; bool isolate = false; @@ -606,7 +603,7 @@ void GPENCIL_OT_layer_isolate(wmOperatorType *ot) static int gp_merge_layer_exec(bContext *C, wmOperator *op) { bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDlayer *gpl_current = gpencil_layer_getactive(gpd); + bGPDlayer *gpl_current = BKE_gpencil_layer_getactive(gpd); bGPDlayer *gpl_next = gpl_current->next; if (ELEM(NULL, gpd, gpl_current, gpl_next)) { @@ -626,13 +623,13 @@ static int gp_merge_layer_exec(bContext *C, wmOperator *op) bGPDframe *frame = BLI_ghash_lookup(gh_frames_cur, SET_INT_IN_POINTER(gpf->framenum)); if (!frame) { /* nothing found, create new */ - frame = gpencil_frame_addnew(gpl_current, gpf->framenum); + frame = BKE_gpencil_frame_addnew(gpl_current, gpf->framenum); } /* add to tail all strokes */ BLI_movelisttolist(&frame->strokes, &gpf->strokes); } /* Now delete next layer */ - gpencil_layer_delete(gpd, gpl_next); + BKE_gpencil_layer_delete(gpd, gpl_next); BLI_ghash_free(gh_frames_cur, NULL, NULL); /* notifiers */ @@ -681,7 +678,7 @@ static int gp_layer_change_exec(bContext *C, wmOperator *op) /* Get layer or create new one */ if (layer_num == -1) { /* Create layer */ - gpl = gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true); + gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true); } else { /* Try to get layer */ @@ -694,7 +691,7 @@ static int gp_layer_change_exec(bContext *C, wmOperator *op) } /* Set active layer */ - gpencil_layer_setactive(gpd, gpl); + BKE_gpencil_layer_setactive(gpd, gpl); /* updates */ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); @@ -736,7 +733,7 @@ enum { static int gp_stroke_arrange_exec(bContext *C, wmOperator *op) { bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDlayer *gpl = gpencil_layer_getactive(gpd); + bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); bGPDstroke *gps; /* sanity checks */ @@ -747,7 +744,7 @@ static int gp_stroke_arrange_exec(bContext *C, wmOperator *op) bGPDframe *gpf = gpl->actframe; /* temp listbase to store selected strokes */ ListBase selected = {NULL}; - const int direction = RNA_enum_get(op->ptr, "type"); + const int direction = RNA_enum_get(op->ptr, "direction"); /* verify if any selected stroke is in the extreme of the stack and select to move */ for (gps = gpf->strokes.first; gps; gps = gps->next) { @@ -861,8 +858,8 @@ static int gp_stroke_change_color_exec(bContext *C, wmOperator *UNUSED(op)) return OPERATOR_CANCELLED; } - palette = gpencil_palette_getactive(gpd); - color = gpencil_palettecolor_getactive(palette); + palette = BKE_gpencil_palette_getactive(gpd); + color = BKE_gpencil_palettecolor_getactive(palette); if (ELEM(NULL, palette, color)) { return OPERATOR_CANCELLED; } @@ -882,9 +879,9 @@ static int gp_stroke_change_color_exec(bContext *C, wmOperator *UNUSED(op)) continue; /* asign new color (only if different) */ - if (STREQ(gps->colorname, color->info) == false) { - strcpy(gps->colorname, color->info); - gps->flag |= GP_STROKE_RECALC_COLOR; + if ((STREQ(gps->colorname, color->info) == false) || (gps->palcolor != color)) { + BLI_strncpy(gps->colorname, color->info, sizeof(gps->colorname)); + gps->palcolor = color; } } } @@ -919,7 +916,7 @@ static int gp_stroke_lock_color_exec(bContext *C, wmOperator *UNUSED(op)) if (ELEM(NULL, gpd)) return OPERATOR_CANCELLED; - palette = gpencil_palette_getactive(gpd); + palette = BKE_gpencil_palette_getactive(gpd); if (ELEM(NULL, palette)) return OPERATOR_CANCELLED; @@ -965,430 +962,6 @@ void GPENCIL_OT_stroke_lock_color(wmOperatorType *ot) ot->poll = gp_active_layer_poll; } -/* ******************* Apply layer thickness change to Strokes ************************** */ - -static int gp_stroke_apply_thickness_exec(bContext *C, wmOperator *UNUSED(op)) -{ - bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDlayer *gpl = gpencil_layer_getactive(gpd); - - /* sanity checks */ - if (ELEM(NULL, gpd, gpl, gpl->frames.first)) - return OPERATOR_CANCELLED; - - /* loop all strokes */ - for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { - /* Apply thickness */ - gps->thickness = gps->thickness + gpl->thickness; - } - } - /* clear value */ - gpl->thickness = 0.0f; - - /* notifiers */ - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - - return OPERATOR_FINISHED; -} - -void GPENCIL_OT_stroke_apply_thickness(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Apply Stroke Thickness"; - ot->idname = "GPENCIL_OT_stroke_apply_thickness"; - ot->description = "Apply the thickness change of the layer to its strokes"; - - /* api callbacks */ - ot->exec = gp_stroke_apply_thickness_exec; - ot->poll = gp_active_layer_poll; -} - -/* ******************* Close Strokes ************************** */ - -enum { - GP_STROKE_CYCLIC_CLOSE = 1, - GP_STROKE_CYCLIC_OPEN = 2, - GP_STROKE_CYCLIC_TOGGLE = 3 -}; - -static int gp_stroke_cyclical_set_exec(bContext *C, wmOperator *op) -{ - bGPdata *gpd = ED_gpencil_data_get_active(C); - const int type = RNA_enum_get(op->ptr, "type"); - - /* sanity checks */ - if (ELEM(NULL, gpd)) - return OPERATOR_CANCELLED; - - /* loop all selected strokes */ - CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) - { - for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) { - bGPDpalettecolor *palcolor = gps->palcolor; - - /* skip strokes that are not selected or invalid for current view */ - if (((gps->flag & GP_STROKE_SELECT) == 0) || ED_gpencil_stroke_can_use(C, gps) == false) - continue; - /* skip hidden or locked colors */ - if (!palcolor || (palcolor->flag & PC_COLOR_HIDE) || (palcolor->flag & PC_COLOR_LOCKED)) - continue; - - switch (type) { - case GP_STROKE_CYCLIC_CLOSE: - /* Close all (enable) */ - gps->flag |= GP_STROKE_CYCLIC; - break; - case GP_STROKE_CYCLIC_OPEN: - /* Open all (disable) */ - gps->flag &= ~GP_STROKE_CYCLIC; - break; - case GP_STROKE_CYCLIC_TOGGLE: - /* Just toggle flag... */ - gps->flag ^= GP_STROKE_CYCLIC; - break; - default: - BLI_assert(0); - break; - } - } - } - CTX_DATA_END; - - /* notifiers */ - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - - return OPERATOR_FINISHED; -} - -/** - * Similar to #CURVE_OT_cyclic_toggle or #MASK_OT_cyclic_toggle, but with - * option to force opened/closed strokes instead of just toggle behavior. - */ -void GPENCIL_OT_stroke_cyclical_set(wmOperatorType *ot) -{ - static EnumPropertyItem cyclic_type[] = { - {GP_STROKE_CYCLIC_CLOSE, "CLOSE", 0, "Close all", ""}, - {GP_STROKE_CYCLIC_OPEN, "OPEN", 0, "Open all", ""}, - {GP_STROKE_CYCLIC_TOGGLE, "TOGGLE", 0, "Toggle", ""}, - {0, NULL, 0, NULL, NULL} - }; - - /* identifiers */ - ot->name = "Set Cyclical State"; - ot->idname = "GPENCIL_OT_stroke_cyclical_set"; - ot->description = "Close or open the selected stroke adding an edge from last to first point"; - - /* api callbacks */ - ot->exec = gp_stroke_cyclical_set_exec; - ot->poll = gp_active_layer_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - ot->prop = RNA_def_enum(ot->srna, "type", cyclic_type, GP_STROKE_CYCLIC_TOGGLE, "Type", ""); -} - -/* ******************* Stroke join ************************** */ - -/* Helper: flip stroke */ -static void gpencil_flip_stroke(bGPDstroke *gps) -{ - bGPDspoint pt, *point, *point2; - int end = gps->totpoints - 1; - - for (int i = 0; i < gps->totpoints / 2; i++) { - /* save first point */ - point = &gps->points[i]; - pt.x = point->x; - pt.y = point->y; - pt.z = point->z; - pt.flag = point->flag; - pt.pressure = point->pressure; - pt.strength = point->strength; - pt.time = point->time; - - /* replace first point with last point */ - point2 = &gps->points[end]; - point->x = point2->x; - point->y = point2->y; - point->z = point2->z; - point->flag = point2->flag; - point->pressure = point2->pressure; - point->strength = point2->strength; - point->time = point2->time; - - /* replace last point with first saved before */ - point = &gps->points[end]; - point->x = pt.x; - point->y = pt.y; - point->z = pt.z; - point->flag = pt.flag; - point->pressure = pt.pressure; - point->strength = pt.strength; - point->time = pt.time; - - end--; - } -} - -/* Helper: copy point between strokes */ -static void gpencil_stroke_copy_point(bGPDstroke *gps, bGPDspoint *point, float delta[3], - float pressure, float strength, float deltatime) -{ - bGPDspoint *newpoint; - - gps->points = MEM_reallocN(gps->points, sizeof(bGPDspoint) * (gps->totpoints + 1)); - gps->totpoints++; - - newpoint = &gps->points[gps->totpoints - 1]; - newpoint->x = point->x * delta[0]; - newpoint->y = point->y * delta[1]; - newpoint->z = point->z * delta[2]; - newpoint->flag = point->flag; - newpoint->pressure = pressure; - newpoint->strength = strength; - newpoint->time = point->time + deltatime; -} - -/* Helper: join two strokes using the shortest distance (reorder stroke if necessary ) */ -static void gpencil_stroke_join_strokes(bGPDstroke *gps_a, bGPDstroke *gps_b) -{ - bGPDspoint point, *pt; - int i; - float delta[3] = {1.0f, 1.0f, 1.0f}; - float deltatime = 0.0f; - - /* sanity checks */ - if (ELEM(NULL, gps_a, gps_b)) - return; - - if ((gps_a->totpoints == 0) || (gps_b->totpoints == 0)) - return; - - /* define start and end points of each stroke */ - float sa[3], sb[3], ea[3], eb[3]; - pt = &gps_a->points[0]; - copy_v3_v3(sa, &pt->x); - - pt = &gps_a->points[gps_a->totpoints - 1]; - copy_v3_v3(ea, &pt->x); - - pt = &gps_b->points[0]; - copy_v3_v3(sb, &pt->x); - - pt = &gps_b->points[gps_b->totpoints - 1]; - copy_v3_v3(eb, &pt->x); - /* review if need flip stroke B */ - float ea_sb = len_squared_v3v3(ea, sb); - float ea_eb = len_squared_v3v3(ea, eb); - /* flip if distance to end point is shorter */ - if (ea_eb < ea_sb) { - gpencil_flip_stroke(gps_b); - } - - /* 1st: add one tail point to start invisible area */ - point = gps_a->points[gps_a->totpoints - 1]; - deltatime = point.time; - gpencil_stroke_copy_point(gps_a, &point, delta, 0.0f, 0.0f, 0.0f); - - /* 2nd: add one head point to finish invisible area */ - point = gps_b->points[0]; - gpencil_stroke_copy_point(gps_a, &point, delta, 0.0f, 0.0f, deltatime); - - /* 3rd: add all points */ - for (i = 0, pt = gps_b->points; i < gps_b->totpoints && pt; i++, pt++) { - /* check if still room in buffer */ - if (gps_a->totpoints <= GP_STROKE_BUFFER_MAX - 2) { - gpencil_stroke_copy_point(gps_a, pt, delta, pt->pressure, pt->strength, deltatime); - } - } -} - -enum { - GP_STROKE_JOIN = -1, - GP_STROKE_JOINCOPY = 1 -}; - -static int gp_stroke_join_exec(bContext *C, wmOperator *op) -{ - bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDlayer *activegpl = gpencil_layer_getactive(gpd); - bGPDstroke *gps, *gpsn; - bGPDpalette *palette = gpencil_palette_getactive(gpd); - bGPDpalettecolor *palcolor = gpencil_palettecolor_getactive(palette); - - bGPDframe *gpf_a = NULL; - bGPDstroke *stroke_a = NULL; - bGPDstroke *stroke_b = NULL; - bGPDstroke *new_stroke = NULL; - - int type = RNA_enum_get(op->ptr, "type"); - - /* sanity checks */ - if (ELEM(NULL, gpd)) - return OPERATOR_CANCELLED; - - if (activegpl->flag & GP_LAYER_LOCKED) - return OPERATOR_CANCELLED; - - BLI_assert(ELEM(type, GP_STROKE_JOIN, GP_STROKE_JOINCOPY)); - - - /* read all selected strokes */ - bool first = false; - CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) - { - bGPDframe *gpf = gpl->actframe; - for (gps = gpf->strokes.first; gps; gps = gpsn) { - gpsn = gps->next; - if (gps->flag & GP_STROKE_SELECT) { - /* skip strokes that are invalid for current view */ - if (ED_gpencil_stroke_can_use(C, gps) == false) { - continue; - } - /* check if the color is editable */ - if (ED_gpencil_stroke_color_use(gpl, gps) == false) { - continue; - } - /* to join strokes, cyclic must be disabled */ - gps->flag &= ~GP_STROKE_CYCLIC; - /* saves first frame and stroke */ - if (!first) { - first = true; - gpf_a = gpf; - stroke_a = gps; - } - else { - stroke_b = gps; - /* create a new stroke if was not created before (only created if something to join) */ - if (new_stroke == NULL) { - new_stroke = MEM_dupallocN(stroke_a); - new_stroke->points = MEM_dupallocN(stroke_a->points); - new_stroke->triangles = NULL; - new_stroke->tot_triangles = 0; - new_stroke->flag |= GP_STROKE_RECALC_CACHES; - /* if new, set current color */ - if (type == GP_STROKE_JOINCOPY) { - new_stroke->palcolor = palcolor; - strcpy(new_stroke->colorname, palcolor->info); - new_stroke->flag |= GP_STROKE_RECALC_COLOR; - } - } - /* join new_stroke and stroke B. New stroke will contain all the previous data */ - gpencil_stroke_join_strokes(new_stroke, stroke_b); - - /* if join only, delete old strokes */ - if (type == GP_STROKE_JOIN) { - if (stroke_a) { - BLI_insertlinkbefore(&gpf_a->strokes, stroke_a, new_stroke); - BLI_remlink(&gpf->strokes, stroke_a); - free_gpencil_stroke(stroke_a); - stroke_a = NULL; - } - if (stroke_b) { - BLI_remlink(&gpf->strokes, stroke_b); - free_gpencil_stroke(stroke_b); - stroke_b = NULL; - } - } - } - } - } - } - CTX_DATA_END; - /* add new stroke if was not added before */ - if (type == GP_STROKE_JOINCOPY) { - if (new_stroke) { - /* Add a new frame if needed */ - if (activegpl->actframe == NULL) - activegpl->actframe = gpencil_frame_addnew(activegpl, gpf_a->framenum); - - BLI_addtail(&activegpl->actframe->strokes, new_stroke); - } - } - - /* notifiers */ - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - - return OPERATOR_FINISHED; -} - -void GPENCIL_OT_stroke_join(wmOperatorType *ot) -{ - static EnumPropertyItem join_type[] = { - {GP_STROKE_JOIN, "JOIN", 0, "Join", ""}, - {GP_STROKE_JOINCOPY, "JOINCOPY", 0, "Join and Copy", ""}, - {0, NULL, 0, NULL, NULL} - }; - - /* identifiers */ - ot->name = "Join Strokes"; - ot->idname = "GPENCIL_OT_stroke_join"; - ot->description = "Join selected strokes (optionally as new stroke)"; - - /* api callbacks */ - ot->exec = gp_stroke_join_exec; - ot->poll = gp_active_layer_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - ot->prop = RNA_def_enum(ot->srna, "type", join_type, GP_STROKE_JOIN, "Type", ""); -} - -/* ******************* Stroke flip ************************** */ - -static int gp_stroke_flip_exec(bContext *C, wmOperator *UNUSED(op)) -{ - bGPdata *gpd = ED_gpencil_data_get_active(C); - - /* sanity checks */ - if (ELEM(NULL, gpd)) - return OPERATOR_CANCELLED; - - /* read all selected strokes */ - CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) - { - bGPDframe *gpf = gpl->actframe; - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { - if (gps->flag & GP_STROKE_SELECT) { - /* skip strokes that are invalid for current view */ - if (ED_gpencil_stroke_can_use(C, gps) == false) { - continue; - } - /* check if the color is editable */ - if (ED_gpencil_stroke_color_use(gpl, gps) == false) { - continue; - } - /* flip stroke */ - gpencil_flip_stroke(gps); - } - } - } - CTX_DATA_END; - - /* notifiers */ - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - - return OPERATOR_FINISHED; -} - -void GPENCIL_OT_stroke_flip(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Flip Stroke"; - ot->idname = "GPENCIL_OT_stroke_flip"; - ot->description = "Change drawing direction of selected strokes"; - - /* api callbacks */ - ot->exec = gp_stroke_flip_exec; - ot->poll = gp_active_layer_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - /* ************************************************ */ /* Drawing Brushes Operators */ @@ -1405,7 +978,7 @@ static int gp_brush_add_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } /* add new brush now */ - gpencil_brush_addnew(ts, DATA_("GP_Brush"), true); + BKE_gpencil_brush_addnew(ts, DATA_("GP_Brush"), true); /* notifiers */ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); @@ -1432,14 +1005,14 @@ void GPENCIL_OT_brush_add(wmOperatorType *ot) static int gp_brush_remove_exec(bContext *C, wmOperator *op) { ToolSettings *ts = CTX_data_tool_settings(C); - bGPDbrush *brush = gpencil_brush_getactive(ts); + bGPDbrush *brush = BKE_gpencil_brush_getactive(ts); /* sanity checks */ if (ELEM(NULL, ts, brush)) return OPERATOR_CANCELLED; - if (BLI_listbase_count(&ts->gp_brushes) < 2) { - BKE_report(op->reports, RPT_ERROR, "Grease Pencil needs a brush. Unable to delete brush"); + if (BLI_listbase_count_ex(&ts->gp_brushes, 2) < 2) { + BKE_report(op->reports, RPT_ERROR, "Grease Pencil needs a brush, unable to delete the last one"); return OPERATOR_CANCELLED; } @@ -1449,12 +1022,12 @@ static int gp_brush_remove_exec(bContext *C, wmOperator *op) * - if this is the only brush, this naturally becomes NULL */ if (brush->prev) - gpencil_brush_setactive(ts, brush->prev); + BKE_gpencil_brush_setactive(ts, brush->prev); else - gpencil_brush_setactive(ts, brush->next); + BKE_gpencil_brush_setactive(ts, brush->next); /* delete the brush now... */ - gpencil_brush_delete(ts, brush); + BKE_gpencil_brush_delete(ts, brush); /* notifiers */ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); @@ -1465,7 +1038,7 @@ static int gp_brush_remove_exec(bContext *C, wmOperator *op) void GPENCIL_OT_brush_remove(wmOperatorType *ot) { /* identifiers */ - ot->name = "Remove brush"; + ot->name = "Remove Brush"; ot->idname = "GPENCIL_OT_brush_remove"; ot->description = "Remove active Grease Pencil drawing brush"; @@ -1501,7 +1074,7 @@ static int gp_brush_change_exec(bContext *C, wmOperator *op) /* Get brush or create new one */ if (brush_num == -1) { /* Create brush */ - brush = gpencil_brush_addnew(ts, DATA_("GP_Brush"), true); + brush = BKE_gpencil_brush_addnew(ts, DATA_("GP_Brush"), true); } else { /* Try to get brush */ @@ -1514,7 +1087,7 @@ static int gp_brush_change_exec(bContext *C, wmOperator *op) } /* Set active brush */ - gpencil_brush_setactive(ts, brush); + BKE_gpencil_brush_setactive(ts, brush); /* updates */ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); @@ -1552,7 +1125,7 @@ enum { static int gp_brush_move_exec(bContext *C, wmOperator *op) { ToolSettings *ts = CTX_data_tool_settings(C); - bGPDbrush *brush = gpencil_brush_getactive(ts); + bGPDbrush *brush = BKE_gpencil_brush_getactive(ts); int direction = RNA_enum_get(op->ptr, "type"); @@ -1610,7 +1183,7 @@ void GPENCIL_OT_brush_move(wmOperatorType *ot) static int gp_brush_presets_create_exec(bContext *C, wmOperator *UNUSED(op)) { ToolSettings *ts = CTX_data_tool_settings(C); - gpencil_brush_init_presets(ts); + BKE_gpencil_brush_init_presets(ts); /* notifiers */ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); @@ -1645,7 +1218,7 @@ static int gp_brush_copy_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - bGPDbrush *brush = gpencil_brush_getactive(ts); + bGPDbrush *brush = BKE_gpencil_brush_getactive(ts); bGPDbrush *newbrush; /* sanity checks */ @@ -1653,7 +1226,7 @@ static int gp_brush_copy_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; /* create a brush and duplicate data */ - newbrush = gpencil_brush_addnew(ts, brush->info, true); + newbrush = BKE_gpencil_brush_addnew(ts, brush->info, true); newbrush->thickness = brush->thickness; newbrush->draw_smoothfac = brush->draw_smoothfac; newbrush->draw_smoothlvl = brush->draw_smoothlvl; @@ -1677,7 +1250,7 @@ static int gp_brush_copy_exec(bContext *C, wmOperator *op) newbrush->cur_strength = curvemapping_copy(brush->cur_strength); newbrush->cur_jitter = curvemapping_copy(brush->cur_jitter); - gpencil_brush_setactive(ts, newbrush); + BKE_gpencil_brush_setactive(ts, newbrush); /* notifiers */ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); @@ -1718,7 +1291,7 @@ static int gp_brush_select_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - gpencil_brush_setactive(ts, brush); + BKE_gpencil_brush_setactive(ts, brush); /* notifiers */ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); @@ -1760,10 +1333,10 @@ static int gp_palette_add_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } if (*gpd_ptr == NULL) - *gpd_ptr = gpencil_data_addnew(DATA_("GPencil")); + *gpd_ptr = BKE_gpencil_data_addnew(DATA_("GPencil")); /* add new palette now */ - gpencil_palette_addnew(*gpd_ptr, DATA_("GP_Palette"), true); + BKE_gpencil_palette_addnew(*gpd_ptr, DATA_("GP_Palette"), true); /* notifiers */ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); @@ -1790,14 +1363,14 @@ void GPENCIL_OT_palette_add(wmOperatorType *ot) static int gp_palette_remove_exec(bContext *C, wmOperator *op) { bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDpalette *palette = gpencil_palette_getactive(gpd); + bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); /* sanity checks */ if (ELEM(NULL, gpd, palette)) return OPERATOR_CANCELLED; - if (BLI_listbase_count(&gpd->palettes) < 2) { - BKE_report(op->reports, RPT_ERROR, "Grease Pencil needs a palette. Unable to delete palette"); + if (BLI_listbase_count_ex(&gpd->palettes, 2) < 2) { + BKE_report(op->reports, RPT_ERROR, "Grease Pencil needs a palette, unable to delete the last one"); return OPERATOR_CANCELLED; } @@ -1807,12 +1380,12 @@ static int gp_palette_remove_exec(bContext *C, wmOperator *op) * - if this is the only palette, this naturally becomes NULL */ if (palette->prev) - gpencil_palette_setactive(gpd, palette->prev); + BKE_gpencil_palette_setactive(gpd, palette->prev); else - gpencil_palette_setactive(gpd, palette->next); + BKE_gpencil_palette_setactive(gpd, palette->next); /* delete the palette now... */ - gpencil_palette_delete(gpd, palette); + BKE_gpencil_palette_delete(gpd, palette); /* notifiers */ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); @@ -1859,7 +1432,7 @@ static int gp_palette_change_exec(bContext *C, wmOperator *op) /* Get palette or create new one */ if (palette_num == -1) { /* Create palette */ - palette = gpencil_palette_addnew(gpd, DATA_("GP_Palette"), true); + palette = BKE_gpencil_palette_addnew(gpd, DATA_("GP_Palette"), true); } else { /* Try to get palette */ @@ -1872,7 +1445,7 @@ static int gp_palette_change_exec(bContext *C, wmOperator *op) } /* Set active palette */ - gpencil_palette_setactive(gpd, palette); + BKE_gpencil_palette_setactive(gpd, palette); /* updates */ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); @@ -1911,7 +1484,7 @@ static int gp_palette_lock_layer_exec(bContext *C, wmOperator *UNUSED(op)) if (ELEM(NULL, gpd)) return OPERATOR_CANCELLED; - palette = gpencil_palette_getactive(gpd); + palette = BKE_gpencil_palette_getactive(gpd); if (ELEM(NULL, palette)) return OPERATOR_CANCELLED; @@ -1972,15 +1545,15 @@ static int gp_palettecolor_add_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } if (*gpd_ptr == NULL) - *gpd_ptr = gpencil_data_addnew(DATA_("GPencil")); + *gpd_ptr = BKE_gpencil_data_addnew(DATA_("GPencil")); /* verify palette */ - bGPDpalette *palette = gpencil_palette_getactive(*gpd_ptr); + bGPDpalette *palette = BKE_gpencil_palette_getactive(*gpd_ptr); if (palette == NULL) - palette = gpencil_palette_addnew(*gpd_ptr, DATA_("GP_Palette"), true); + palette = BKE_gpencil_palette_addnew(*gpd_ptr, DATA_("GP_Palette"), true); /* add new palette color now */ - gpencil_palettecolor_addnew(palette, DATA_("Color"), true); + BKE_gpencil_palettecolor_addnew(palette, DATA_("Color"), true); /* notifiers */ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); @@ -2007,8 +1580,8 @@ void GPENCIL_OT_palettecolor_add(wmOperatorType *ot) static int gp_palettecolor_remove_exec(bContext *C, wmOperator *UNUSED(op)) { bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDpalette *palette = gpencil_palette_getactive(gpd); - bGPDpalettecolor *color = gpencil_palettecolor_getactive(palette); + bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); + bGPDpalettecolor *color = BKE_gpencil_palettecolor_getactive(palette); /* sanity checks */ if (ELEM(NULL, gpd, palette, color)) @@ -2019,15 +1592,15 @@ static int gp_palettecolor_remove_exec(bContext *C, wmOperator *UNUSED(op)) * - if this is the only color, this naturally becomes NULL */ if (color->prev) - gpencil_palettecolor_setactive(palette, color->prev); + BKE_gpencil_palettecolor_setactive(palette, color->prev); else - gpencil_palettecolor_setactive(palette, color->next); + BKE_gpencil_palettecolor_setactive(palette, color->next); /* delete the strokes */ - gpencil_palettecolor_delete_strokes(gpd, color->info); + BKE_gpencil_palettecolor_delete_strokes(gpd, color->info); /* delete the palette color now... */ - gpencil_palettecolor_delete(palette, color); + BKE_gpencil_palettecolor_delete(palette, color); /* notifiers */ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); @@ -2054,8 +1627,8 @@ void GPENCIL_OT_palettecolor_remove(wmOperatorType *ot) static int gp_isolate_palettecolor_exec(bContext *C, wmOperator *op) { bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDpalette *palette = gpencil_palette_getactive(gpd); - bGPDpalettecolor *active_color = gpencil_palettecolor_getactive(palette); + bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); + bGPDpalettecolor *active_color = BKE_gpencil_palettecolor_getactive(palette); bGPDpalettecolor *palcolor; int flags = PC_COLOR_LOCKED; @@ -2131,8 +1704,8 @@ void GPENCIL_OT_palettecolor_isolate(wmOperatorType *ot) static int gp_palettecolor_hide_exec(bContext *C, wmOperator *op) { bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDpalette *palette = gpencil_palette_getactive(gpd); - bGPDpalettecolor *palcolor = gpencil_palettecolor_getactive(palette); + bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); + bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette); bool unselected = RNA_boolean_get(op->ptr, "unselected"); @@ -2190,7 +1763,7 @@ static int gp_palettecolor_reveal_poll(bContext *C) static int gp_palettecolor_reveal_exec(bContext *C, wmOperator *UNUSED(op)) { bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDpalette *palette = gpencil_palette_getactive(gpd); + bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); bGPDpalettecolor *palcolor; /* sanity checks */ @@ -2228,7 +1801,7 @@ void GPENCIL_OT_palettecolor_reveal(wmOperatorType *ot) static int gp_palettecolor_lock_all_exec(bContext *C, wmOperator *UNUSED(op)) { bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDpalette *palette = gpencil_palette_getactive(gpd); + bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); bGPDpalettecolor *palcolor; /* sanity checks */ @@ -2266,7 +1839,7 @@ void GPENCIL_OT_palettecolor_lock_all(wmOperatorType *ot) static int gp_palettecolor_unlock_all_exec(bContext *C, wmOperator *UNUSED(op)) { bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDpalette *palette = gpencil_palette_getactive(gpd); + bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); bGPDpalettecolor *palcolor; /* sanity checks */ @@ -2309,8 +1882,8 @@ enum { static int gp_palettecolor_move_exec(bContext *C, wmOperator *op) { bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDpalette *palette = gpencil_palette_getactive(gpd); - bGPDpalettecolor *palcolor = gpencil_palettecolor_getactive(palette); + bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); + bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette); int direction = RNA_enum_get(op->ptr, "direction"); @@ -2367,8 +1940,8 @@ void GPENCIL_OT_palettecolor_move(wmOperatorType *ot) static int gp_palettecolor_select_exec(bContext *C, wmOperator *UNUSED(op)) { bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDpalette *palette = gpencil_palette_getactive(gpd); - bGPDpalettecolor *palcolor = gpencil_palettecolor_getactive(palette); + bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); + bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette); /* sanity checks */ if (ELEM(NULL, gpd, palette, palcolor)) @@ -2426,8 +1999,8 @@ void GPENCIL_OT_palettecolor_select(wmOperatorType *ot) static int gp_palettecolor_copy_exec(bContext *C, wmOperator *UNUSED(op)) { bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDpalette *palette = gpencil_palette_getactive(gpd); - bGPDpalettecolor *palcolor = gpencil_palettecolor_getactive(palette); + bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); + bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette); bGPDpalettecolor *newcolor; /* sanity checks */ @@ -2435,7 +2008,7 @@ static int gp_palettecolor_copy_exec(bContext *C, wmOperator *UNUSED(op)) return OPERATOR_CANCELLED; /* create a new color and duplicate data */ - newcolor = gpencil_palettecolor_addnew(palette, palcolor->info, true); + newcolor = BKE_gpencil_palettecolor_addnew(palette, palcolor->info, true); copy_v4_v4(newcolor->color, palcolor->color); copy_v4_v4(newcolor->fill, palcolor->fill); newcolor->flag = palcolor->flag; diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index 621ebea6603..9f700e8716c 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -72,6 +72,7 @@ #include "ED_gpencil.h" #include "ED_object.h" +#include "ED_screen.h" #include "ED_view3d.h" #include "gpencil_intern.h" @@ -207,7 +208,7 @@ static void gp_duplicate_points(const bGPDstroke *gps, ListBase *new_strokes, co /* make a stupid copy first of the entire stroke (to get the flags too) */ gpsd = MEM_dupallocN(gps); - strcpy(gpsd->tmp_layerinfo, layername); /* saves original layer name */ + BLI_strncpy(gpsd->tmp_layerinfo, layername, sizeof(gpsd->tmp_layerinfo)); /* saves original layer name */ /* initialize triangle memory - will be calculated on next redraw */ gpsd->triangles = NULL; @@ -266,7 +267,7 @@ static int gp_duplicate_exec(bContext *C, wmOperator *op) /* make direct copies of the stroke and its points */ gpsd = MEM_dupallocN(gps); - strcpy(gpsd->tmp_layerinfo, gpl->info); + BLI_strncpy(gpsd->tmp_layerinfo, gpl->info, sizeof(gpsd->tmp_layerinfo)); gpsd->points = MEM_dupallocN(gps->points); /* triangle information - will be calculated on next redraw */ @@ -386,7 +387,7 @@ static int gp_strokes_copy_exec(bContext *C, wmOperator *op) /* make direct copies of the stroke and its points */ gpsd = MEM_dupallocN(gps); - strcpy(gpsd->tmp_layerinfo, gpl->info); /* saves original layer name */ + BLI_strncpy(gpsd->tmp_layerinfo, gpl->info, sizeof(gpsd->tmp_layerinfo)); /* saves original layer name */ gpsd->points = MEM_dupallocN(gps->points); /* triangles cache - will be recalculated on next redraw */ @@ -431,16 +432,18 @@ void GPENCIL_OT_copy(wmOperatorType *ot) static int gp_strokes_paste_poll(bContext *C) { - /* 1) Must have GP layer to paste to... + /* 1) Must have GP datablock to paste to + * - We don't need to have an active layer though, as that can easily get added + * - If the active layer is locked, we can't paste there, but that should prompt a warning instead * 2) Copy buffer must at least have something (though it may be the wrong sort...) */ - return (CTX_data_active_gpencil_layer(C) != NULL) && (!BLI_listbase_is_empty(&gp_strokes_copypastebuf)); + return (ED_gpencil_data_get_active(C) != NULL) && (!BLI_listbase_is_empty(&gp_strokes_copypastebuf)); } -enum { +typedef enum eGP_PasteMode { GP_COPY_ONLY = -1, GP_COPY_MERGE = 1 -}; +} eGP_PasteMode; static int gp_strokes_paste_exec(bContext *C, wmOperator *op) { @@ -448,9 +451,9 @@ static int gp_strokes_paste_exec(bContext *C, wmOperator *op) bGPdata *gpd = ED_gpencil_data_get_active(C); bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); /* only use active for copy merge */ bGPDframe *gpf; - - int type = RNA_enum_get(op->ptr, "type"); - + + eGP_PasteMode type = RNA_enum_get(op->ptr, "type"); + /* check for various error conditions */ if (gpd == NULL) { BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data"); @@ -462,7 +465,7 @@ static int gp_strokes_paste_exec(bContext *C, wmOperator *op) } else if (gpl == NULL) { /* no active layer - let's just create one */ - gpl = gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true); + gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true); } else if ((gpencil_layer_is_editable(gpl) == false) && (type == GP_COPY_MERGE)) { BKE_report(op->reports, RPT_ERROR, "Can not paste strokes when active layer is hidden or locked"); @@ -507,39 +510,37 @@ static int gp_strokes_paste_exec(bContext *C, wmOperator *op) } CTX_DATA_END; - /* Ensure we have a frame to draw into - * NOTE: Since this is an op which creates strokes, - * we are obliged to add a new frame if one - * doesn't exist already - */ - - bGPDstroke *gps; - /* Copy each stroke into the layer */ - for (gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) { - if (ED_gpencil_stroke_can_use(C, gps)) { - /* need to verify if layer exist nad frame */ - if (type != GP_COPY_MERGE) { - gpl = BLI_findstring(&gpd->layers, gps->tmp_layerinfo, offsetof(bGPDlayer, info)); - if (gpl == NULL) { - /* no layer - use active (only if layer deleted before paste) */ - gpl = CTX_data_active_gpencil_layer(C); - } - } - gpf = gpencil_layer_getframe(gpl, CFRA, true); - if (gpf) { - bGPDstroke *new_stroke = MEM_dupallocN(gps); - new_stroke->tmp_layerinfo[0] = '\0'; - - new_stroke->points = MEM_dupallocN(gps->points); - - new_stroke->flag |= GP_STROKE_RECALC_CACHES; - new_stroke->triangles = NULL; - - new_stroke->next = new_stroke->prev = NULL; - BLI_addtail(&gpf->strokes, new_stroke); + for (bGPDstroke *gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) { + if (ED_gpencil_stroke_can_use(C, gps)) { + /* Need to verify if layer exists */ + if (type != GP_COPY_MERGE) { + gpl = BLI_findstring(&gpd->layers, gps->tmp_layerinfo, offsetof(bGPDlayer, info)); + if (gpl == NULL) { + /* no layer - use active (only if layer deleted before paste) */ + gpl = CTX_data_active_gpencil_layer(C); } } + + /* Ensure we have a frame to draw into + * NOTE: Since this is an op which creates strokes, + * we are obliged to add a new frame if one + * doesn't exist already + */ + gpf = BKE_gpencil_layer_getframe(gpl, CFRA, true); + if (gpf) { + bGPDstroke *new_stroke = MEM_dupallocN(gps); + new_stroke->tmp_layerinfo[0] = '\0'; + + new_stroke->points = MEM_dupallocN(gps->points); + + new_stroke->flag |= GP_STROKE_RECALC_CACHES; + new_stroke->triangles = NULL; + + new_stroke->next = new_stroke->prev = NULL; + BLI_addtail(&gpf->strokes, new_stroke); + } } + } /* updates */ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); @@ -554,7 +555,7 @@ void GPENCIL_OT_paste(wmOperatorType *ot) {GP_COPY_MERGE, "MERGE", 0, "Merge", ""}, {0, NULL, 0, NULL, NULL} }; - + /* identifiers */ ot->name = "Paste Strokes"; ot->idname = "GPENCIL_OT_paste"; @@ -566,7 +567,8 @@ void GPENCIL_OT_paste(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - + + /* properties */ ot->prop = RNA_def_enum(ot->srna, "type", copy_type, 0, "Type", ""); } @@ -597,7 +599,7 @@ static int gp_move_to_layer_exec(bContext *C, wmOperator *op) /* Get layer or create new one */ if (layer_num == -1) { /* Create layer */ - target_layer = gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true); + target_layer = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true); } else { /* Try to get layer */ @@ -642,7 +644,7 @@ static int gp_move_to_layer_exec(bContext *C, wmOperator *op) /* Paste them all in one go */ if (strokes.first) { Scene *scene = CTX_data_scene(C); - bGPDframe *gpf = gpencil_layer_getframe(target_layer, CFRA, true); + bGPDframe *gpf = BKE_gpencil_layer_getframe(target_layer, CFRA, true); BLI_movelisttolist(&gpf->strokes, &strokes); BLI_assert((strokes.first == strokes.last) && (strokes.first == NULL)); @@ -679,7 +681,7 @@ void GPENCIL_OT_move_to_layer(wmOperatorType *ot) static int gp_actframe_delete_poll(bContext *C) { bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDlayer *gpl = gpencil_layer_getactive(gpd); + bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); /* only if there's an active layer with an active frame */ return (gpl && gpl->actframe); @@ -690,8 +692,8 @@ static int gp_actframe_delete_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDlayer *gpl = gpencil_layer_getactive(gpd); - bGPDframe *gpf = gpencil_layer_getframe(gpl, CFRA, 0); + bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); + bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, CFRA, 0); /* if there's no existing Grease-Pencil data there, add some */ if (gpd == NULL) { @@ -704,7 +706,7 @@ static int gp_actframe_delete_exec(bContext *C, wmOperator *op) } /* delete it... */ - gpencil_layer_delframe(gpl, gpf); + BKE_gpencil_layer_delframe(gpl, gpf); /* notifiers */ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); @@ -746,13 +748,13 @@ static int gp_actframe_delete_all_exec(bContext *C, wmOperator *op) CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) { /* try to get the "active" frame - but only if it actually occurs on this frame */ - bGPDframe *gpf = gpencil_layer_getframe(gpl, CFRA, 0); + bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, CFRA, 0); if (gpf == NULL) continue; /* delete it... */ - gpencil_layer_delframe(gpl, gpf); + BKE_gpencil_layer_delframe(gpl, gpf); /* we successfully modified something */ success = true; @@ -1188,40 +1190,35 @@ static int gp_snap_poll(bContext *C) static int gp_snap_to_grid(bContext *C, wmOperator *UNUSED(op)) { + bGPdata *gpd = ED_gpencil_data_get_active(C); RegionView3D *rv3d = CTX_wm_region_data(C); const float gridf = rv3d->gridview; - bGPdata *gpd = ED_gpencil_data_get_active(C); - float diff_mat[4][4]; - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { /* only editable and visible layers are considered */ if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { - + bGPDframe *gpf = gpl->actframe; + float diff_mat[4][4]; + /* calculate difference matrix if parent object */ if (gpl->parent != NULL) { ED_gpencil_parent_location(gpl, diff_mat); } - - bGPDframe *gpf = gpl->actframe; - bGPDstroke *gps; - for (gps = gpf->strokes.first; gps; gps = gps->next) { + + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + bGPDspoint *pt; + int i; + /* skip strokes that are invalid for current view */ if (ED_gpencil_stroke_can_use(C, gps) == false) continue; /* check if the color is editable */ - if (ED_gpencil_stroke_color_use(gpl, gps) == false) { + if (ED_gpencil_stroke_color_use(gpl, gps) == false) continue; - } - - bGPDspoint *pt; - int i; - - // TOOD: if entire stroke is selected, offset entire stroke by same amount? - + + // TODO: if entire stroke is selected, offset entire stroke by same amount? for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - - /* only if point is selected.. */ + /* only if point is selected */ if (pt->flag & GP_SPOINT_SELECT) { if (gpl->parent == NULL) { pt->x = gridf * floorf(0.5f + pt->x / gridf); @@ -1232,19 +1229,17 @@ static int gp_snap_to_grid(bContext *C, wmOperator *UNUSED(op)) /* apply parent transformations */ float fpt[3]; mul_v3_m4v3(fpt, diff_mat, &pt->x); - + fpt[0] = gridf * floorf(0.5f + fpt[0] / gridf); fpt[1] = gridf * floorf(0.5f + fpt[1] / gridf); fpt[2] = gridf * floorf(0.5f + fpt[2] / gridf); - + /* return data */ copy_v3_v3(&pt->x, fpt); gp_apply_parent_point(gpl, pt); } - } } - } } } @@ -1272,49 +1267,46 @@ void GPENCIL_OT_snap_to_grid(wmOperatorType *ot) static int gp_snap_to_cursor(bContext *C, wmOperator *op) { + bGPdata *gpd = ED_gpencil_data_get_active(C); + Scene *scene = CTX_data_scene(C); View3D *v3d = CTX_wm_view3d(C); - + const bool use_offset = RNA_boolean_get(op->ptr, "use_offset"); const float *cursor_global = ED_view3d_cursor3d_get(scene, v3d); - - bGPdata *gpd = ED_gpencil_data_get_active(C); - float diff_mat[4][4]; - + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { /* only editable and visible layers are considered */ if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { - + bGPDframe *gpf = gpl->actframe; + float diff_mat[4][4]; + /* calculate difference matrix if parent object */ if (gpl->parent != NULL) { ED_gpencil_parent_location(gpl, diff_mat); } - - bGPDframe *gpf = gpl->actframe; - bGPDstroke *gps; - for (gps = gpf->strokes.first; gps; gps = gps->next) { + + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + bGPDspoint *pt; + int i; + /* skip strokes that are invalid for current view */ if (ED_gpencil_stroke_can_use(C, gps) == false) continue; /* check if the color is editable */ - if (ED_gpencil_stroke_color_use(gpl, gps) == false) { + if (ED_gpencil_stroke_color_use(gpl, gps) == false) continue; - } - - bGPDspoint *pt; - int i; - /* only continue if this stroke is selected (editable doesn't guarantee this)... */ if ((gps->flag & GP_STROKE_SELECT) == 0) continue; - + if (use_offset) { float offset[3]; - + /* compute offset from first point of stroke to cursor */ /* TODO: Allow using midpoint instead? */ sub_v3_v3v3(offset, cursor_global, &gps->points->x); - + /* apply offset to all points in the stroke */ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { add_v3_v3(&pt->x, offset); @@ -1331,9 +1323,8 @@ static int gp_snap_to_cursor(bContext *C, wmOperator *op) } } } - - } + } } @@ -1364,6 +1355,8 @@ void GPENCIL_OT_snap_to_cursor(wmOperatorType *ot) static int gp_snap_cursor_to_sel(bContext *C, wmOperator *UNUSED(op)) { + bGPdata *gpd = ED_gpencil_data_get_active(C); + Scene *scene = CTX_data_scene(C); View3D *v3d = CTX_wm_view3d(C); @@ -1375,36 +1368,31 @@ static int gp_snap_cursor_to_sel(bContext *C, wmOperator *UNUSED(op)) INIT_MINMAX(min, max); /* calculate midpoints from selected points */ - bGPdata *gpd = ED_gpencil_data_get_active(C); - float diff_mat[4][4]; - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { /* only editable and visible layers are considered */ if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { - + bGPDframe *gpf = gpl->actframe; + float diff_mat[4][4]; + /* calculate difference matrix if parent object */ if (gpl->parent != NULL) { ED_gpencil_parent_location(gpl, diff_mat); } - - bGPDframe *gpf = gpl->actframe; - bGPDstroke *gps; - for (gps = gpf->strokes.first; gps; gps = gps->next) { + + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + bGPDspoint *pt; + int i; + /* skip strokes that are invalid for current view */ if (ED_gpencil_stroke_can_use(C, gps) == false) continue; /* check if the color is editable */ - if (ED_gpencil_stroke_color_use(gpl, gps) == false) { + if (ED_gpencil_stroke_color_use(gpl, gps) == false) continue; - } - - bGPDspoint *pt; - int i; - /* only continue if this stroke is selected (editable doesn't guarantee this)... */ if ((gps->flag & GP_STROKE_SELECT) == 0) continue; - + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { if (pt->flag & GP_SPOINT_SELECT) { if (gpl->parent == NULL) { @@ -1415,14 +1403,14 @@ static int gp_snap_cursor_to_sel(bContext *C, wmOperator *UNUSED(op)) /* apply parent transformations */ float fpt[3]; mul_v3_m4v3(fpt, diff_mat, &pt->x); - + add_v3_v3(centroid, fpt); minmax_v3v3_v3(min, max, fpt); } count++; } } - + } } } @@ -1455,5 +1443,529 @@ void GPENCIL_OT_snap_cursor_to_selected(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } +/* ******************* Apply layer thickness change to strokes ************************** */ + +static int gp_stroke_apply_thickness_exec(bContext *C, wmOperator *UNUSED(op)) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); + + /* sanity checks */ + if (ELEM(NULL, gpd, gpl, gpl->frames.first)) + return OPERATOR_CANCELLED; + + /* loop all strokes */ + for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + /* Apply thickness */ + gps->thickness = gps->thickness + gpl->thickness; + } + } + /* clear value */ + gpl->thickness = 0.0f; + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_stroke_apply_thickness(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Apply Stroke Thickness"; + ot->idname = "GPENCIL_OT_stroke_apply_thickness"; + ot->description = "Apply the thickness change of the layer to its strokes"; + + /* api callbacks */ + ot->exec = gp_stroke_apply_thickness_exec; + ot->poll = gp_active_layer_poll; +} + +/* ******************* Close Strokes ************************** */ + +enum { + GP_STROKE_CYCLIC_CLOSE = 1, + GP_STROKE_CYCLIC_OPEN = 2, + GP_STROKE_CYCLIC_TOGGLE = 3 +}; + +static int gp_stroke_cyclical_set_exec(bContext *C, wmOperator *op) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + const int type = RNA_enum_get(op->ptr, "type"); + + /* sanity checks */ + if (ELEM(NULL, gpd)) + return OPERATOR_CANCELLED; + + /* loop all selected strokes */ + CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) + { + for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) { + bGPDpalettecolor *palcolor = gps->palcolor; + + /* skip strokes that are not selected or invalid for current view */ + if (((gps->flag & GP_STROKE_SELECT) == 0) || ED_gpencil_stroke_can_use(C, gps) == false) + continue; + /* skip hidden or locked colors */ + if (!palcolor || (palcolor->flag & PC_COLOR_HIDE) || (palcolor->flag & PC_COLOR_LOCKED)) + continue; + + switch (type) { + case GP_STROKE_CYCLIC_CLOSE: + /* Close all (enable) */ + gps->flag |= GP_STROKE_CYCLIC; + break; + case GP_STROKE_CYCLIC_OPEN: + /* Open all (disable) */ + gps->flag &= ~GP_STROKE_CYCLIC; + break; + case GP_STROKE_CYCLIC_TOGGLE: + /* Just toggle flag... */ + gps->flag ^= GP_STROKE_CYCLIC; + break; + default: + BLI_assert(0); + break; + } + } + } + CTX_DATA_END; + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +/** + * Similar to #CURVE_OT_cyclic_toggle or #MASK_OT_cyclic_toggle, but with + * option to force opened/closed strokes instead of just toggle behavior. + */ +void GPENCIL_OT_stroke_cyclical_set(wmOperatorType *ot) +{ + static EnumPropertyItem cyclic_type[] = { + {GP_STROKE_CYCLIC_CLOSE, "CLOSE", 0, "Close all", ""}, + {GP_STROKE_CYCLIC_OPEN, "OPEN", 0, "Open all", ""}, + {GP_STROKE_CYCLIC_TOGGLE, "TOGGLE", 0, "Toggle", ""}, + {0, NULL, 0, NULL, NULL} + }; + + /* identifiers */ + ot->name = "Set Cyclical State"; + ot->idname = "GPENCIL_OT_stroke_cyclical_set"; + ot->description = "Close or open the selected stroke adding an edge from last to first point"; + + /* api callbacks */ + ot->exec = gp_stroke_cyclical_set_exec; + ot->poll = gp_active_layer_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + ot->prop = RNA_def_enum(ot->srna, "type", cyclic_type, GP_STROKE_CYCLIC_TOGGLE, "Type", ""); +} + +/* ******************* Stroke join ************************** */ + +/* Helper: flip stroke */ +static void gpencil_flip_stroke(bGPDstroke *gps) +{ + int end = gps->totpoints - 1; + + for (int i = 0; i < gps->totpoints / 2; i++) { + bGPDspoint *point, *point2; + bGPDspoint pt; + + /* save first point */ + point = &gps->points[i]; + pt.x = point->x; + pt.y = point->y; + pt.z = point->z; + pt.flag = point->flag; + pt.pressure = point->pressure; + pt.strength = point->strength; + pt.time = point->time; + + /* replace first point with last point */ + point2 = &gps->points[end]; + point->x = point2->x; + point->y = point2->y; + point->z = point2->z; + point->flag = point2->flag; + point->pressure = point2->pressure; + point->strength = point2->strength; + point->time = point2->time; + + /* replace last point with first saved before */ + point = &gps->points[end]; + point->x = pt.x; + point->y = pt.y; + point->z = pt.z; + point->flag = pt.flag; + point->pressure = pt.pressure; + point->strength = pt.strength; + point->time = pt.time; + + end--; + } +} + +/* Helper: copy point between strokes */ +static void gpencil_stroke_copy_point(bGPDstroke *gps, bGPDspoint *point, float delta[3], + float pressure, float strength, float deltatime) +{ + bGPDspoint *newpoint; + + gps->points = MEM_reallocN(gps->points, sizeof(bGPDspoint) * (gps->totpoints + 1)); + gps->totpoints++; + + newpoint = &gps->points[gps->totpoints - 1]; + newpoint->x = point->x * delta[0]; + newpoint->y = point->y * delta[1]; + newpoint->z = point->z * delta[2]; + newpoint->flag = point->flag; + newpoint->pressure = pressure; + newpoint->strength = strength; + newpoint->time = point->time + deltatime; +} + +/* Helper: join two strokes using the shortest distance (reorder stroke if necessary ) */ +static void gpencil_stroke_join_strokes(bGPDstroke *gps_a, bGPDstroke *gps_b, const bool leave_gaps) +{ + bGPDspoint point; + bGPDspoint *pt; + int i; + float delta[3] = {1.0f, 1.0f, 1.0f}; + float deltatime = 0.0f; + + /* sanity checks */ + if (ELEM(NULL, gps_a, gps_b)) + return; + + if ((gps_a->totpoints == 0) || (gps_b->totpoints == 0)) + return; + + /* define start and end points of each stroke */ + float sa[3], sb[3], ea[3], eb[3]; + pt = &gps_a->points[0]; + copy_v3_v3(sa, &pt->x); + + pt = &gps_a->points[gps_a->totpoints - 1]; + copy_v3_v3(ea, &pt->x); + + pt = &gps_b->points[0]; + copy_v3_v3(sb, &pt->x); + + pt = &gps_b->points[gps_b->totpoints - 1]; + copy_v3_v3(eb, &pt->x); + + /* review if need flip stroke B */ + float ea_sb = len_squared_v3v3(ea, sb); + float ea_eb = len_squared_v3v3(ea, eb); + /* flip if distance to end point is shorter */ + if (ea_eb < ea_sb) { + gpencil_flip_stroke(gps_b); + } + + /* don't visibly link the first and last points? */ + if (leave_gaps) { + /* 1st: add one tail point to start invisible area */ + point = gps_a->points[gps_a->totpoints - 1]; + deltatime = point.time; + gpencil_stroke_copy_point(gps_a, &point, delta, 0.0f, 0.0f, 0.0f); + + /* 2nd: add one head point to finish invisible area */ + point = gps_b->points[0]; + gpencil_stroke_copy_point(gps_a, &point, delta, 0.0f, 0.0f, deltatime); + } + + /* 3rd: add all points */ + for (i = 0, pt = gps_b->points; i < gps_b->totpoints && pt; i++, pt++) { + /* check if still room in buffer */ + if (gps_a->totpoints <= GP_STROKE_BUFFER_MAX - 2) { + gpencil_stroke_copy_point(gps_a, pt, delta, pt->pressure, pt->strength, deltatime); + } + } +} + +static int gp_stroke_join_exec(bContext *C, wmOperator *op) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDlayer *activegpl = BKE_gpencil_layer_getactive(gpd); + bGPDstroke *gps, *gpsn; + bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); + bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette); + + bGPDframe *gpf_a = NULL; + bGPDstroke *stroke_a = NULL; + bGPDstroke *stroke_b = NULL; + bGPDstroke *new_stroke = NULL; + + const int type = RNA_enum_get(op->ptr, "type"); + const bool leave_gaps = RNA_boolean_get(op->ptr, "leave_gaps"); + + /* sanity checks */ + if (ELEM(NULL, gpd)) + return OPERATOR_CANCELLED; + + if (activegpl->flag & GP_LAYER_LOCKED) + return OPERATOR_CANCELLED; + + BLI_assert(ELEM(type, GP_STROKE_JOIN, GP_STROKE_JOINCOPY)); + + + /* read all selected strokes */ + bool first = false; + CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) + { + bGPDframe *gpf = gpl->actframe; + for (gps = gpf->strokes.first; gps; gps = gpsn) { + gpsn = gps->next; + if (gps->flag & GP_STROKE_SELECT) { + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) { + continue; + } + /* check if the color is editable */ + if (ED_gpencil_stroke_color_use(gpl, gps) == false) { + continue; + } + + /* to join strokes, cyclic must be disabled */ + gps->flag &= ~GP_STROKE_CYCLIC; + + /* saves first frame and stroke */ + if (!first) { + first = true; + gpf_a = gpf; + stroke_a = gps; + } + else { + stroke_b = gps; + + /* create a new stroke if was not created before (only created if something to join) */ + if (new_stroke == NULL) { + new_stroke = MEM_dupallocN(stroke_a); + new_stroke->points = MEM_dupallocN(stroke_a->points); + new_stroke->triangles = NULL; + new_stroke->tot_triangles = 0; + new_stroke->flag |= GP_STROKE_RECALC_CACHES; + + /* if new, set current color */ + if (type == GP_STROKE_JOINCOPY) { + new_stroke->palcolor = palcolor; + BLI_strncpy(new_stroke->colorname, palcolor->info, sizeof(new_stroke->colorname)); + new_stroke->flag |= GP_STROKE_RECALC_COLOR; + } + } + + /* join new_stroke and stroke B. New stroke will contain all the previous data */ + gpencil_stroke_join_strokes(new_stroke, stroke_b, leave_gaps); + + /* if join only, delete old strokes */ + if (type == GP_STROKE_JOIN) { + if (stroke_a) { + BLI_insertlinkbefore(&gpf_a->strokes, stroke_a, new_stroke); + BLI_remlink(&gpf->strokes, stroke_a); + BKE_gpencil_free_stroke(stroke_a); + stroke_a = NULL; + } + if (stroke_b) { + BLI_remlink(&gpf->strokes, stroke_b); + BKE_gpencil_free_stroke(stroke_b); + stroke_b = NULL; + } + } + } + } + } + } + CTX_DATA_END; + + /* add new stroke if was not added before */ + if (type == GP_STROKE_JOINCOPY) { + if (new_stroke) { + /* Add a new frame if needed */ + if (activegpl->actframe == NULL) + activegpl->actframe = BKE_gpencil_frame_addnew(activegpl, gpf_a->framenum); + + BLI_addtail(&activegpl->actframe->strokes, new_stroke); + } + } + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_stroke_join(wmOperatorType *ot) +{ + static EnumPropertyItem join_type[] = { + {GP_STROKE_JOIN, "JOIN", 0, "Join", ""}, + {GP_STROKE_JOINCOPY, "JOINCOPY", 0, "Join and Copy", ""}, + {0, NULL, 0, NULL, NULL} + }; + + /* identifiers */ + ot->name = "Join Strokes"; + ot->idname = "GPENCIL_OT_stroke_join"; + ot->description = "Join selected strokes (optionally as new stroke)"; + + /* api callbacks */ + ot->exec = gp_stroke_join_exec; + ot->poll = gp_active_layer_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + ot->prop = RNA_def_enum(ot->srna, "type", join_type, GP_STROKE_JOIN, "Type", ""); + RNA_def_boolean(ot->srna, "leave_gaps", false, "Leave Gaps", "Leave gaps between joined strokes instead of linking them"); +} + +/* ******************* Stroke flip ************************** */ + +static int gp_stroke_flip_exec(bContext *C, wmOperator *UNUSED(op)) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + + /* sanity checks */ + if (ELEM(NULL, gpd)) + return OPERATOR_CANCELLED; + + /* read all selected strokes */ + CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) + { + bGPDframe *gpf = gpl->actframe; + if (gpf == NULL) + continue; + + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + if (gps->flag & GP_STROKE_SELECT) { + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) { + continue; + } + /* check if the color is editable */ + if (ED_gpencil_stroke_color_use(gpl, gps) == false) { + continue; + } + + /* flip stroke */ + gpencil_flip_stroke(gps); + } + } + } + CTX_DATA_END; + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_stroke_flip(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Flip Stroke"; + ot->idname = "GPENCIL_OT_stroke_flip"; + ot->description = "Change direction of the points of the selected strokes"; + + /* api callbacks */ + ot->exec = gp_stroke_flip_exec; + ot->poll = gp_active_layer_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* ***************** Reproject Strokes ********************** */ + +static int gp_strokes_reproject_poll(bContext *C) +{ + /* 2 Requirements: + * - 1) Editable GP data + * - 2) 3D View only (2D editors don't have projection issues) + */ + return (gp_stroke_edit_poll(C) && ED_operator_view3d_active(C)); +} + +static int gp_strokes_reproject_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Scene *scene = CTX_data_scene(C); + GP_SpaceConversion gsc = {NULL}; + + /* init space conversion stuff */ + gp_point_conversion_init(C, &gsc); + + /* Go through each editable + selected stroke, adjusting each of its points one by one... */ + GP_EDITABLE_STROKES_BEGIN(C, gpl, gps) + { + if (gps->flag & GP_STROKE_SELECT) { + bGPDspoint *pt; + int i; + float inverse_diff_mat[4][4]; + + /* Compute inverse matrix for unapplying parenting once instead of doing per-point */ + /* TODO: add this bit to the iteration macro? */ + if (gpl->parent) { + invert_m4_m4(inverse_diff_mat, diff_mat); + } + + /* Adjust each point */ + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + float xy[2]; + + /* 3D to Screenspace */ + /* Note: We can't use gp_point_to_xy() here because that uses ints for the screenspace + * coordinates, resulting in lost precision, which in turn causes stairstepping + * artifacts in the final points. + */ + if (gpl->parent == NULL) { + gp_point_to_xy_fl(&gsc, gps, pt, &xy[0], &xy[1]); + } + else { + bGPDspoint pt2; + gp_point_to_parent_space(pt, diff_mat, &pt2); + gp_point_to_xy_fl(&gsc, gps, &pt2, &xy[0], &xy[1]); + } + + /* Project screenspace back to 3D space (from current perspective) + * so that all points have been treated the same way + */ + gp_point_xy_to_3d(&gsc, scene, xy, &pt->x); + + /* Unapply parent corrections */ + if (gpl->parent) { + mul_m4_v3(inverse_diff_mat, &pt->x); + } + } + } + } + GP_EDITABLE_STROKES_END; + + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_reproject(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Reproject Strokes"; + ot->idname = "GPENCIL_OT_reproject"; + ot->description = "Reproject the selected strokes from the current viewpoint to get all points on the same plane again " + "(e.g. to fix problems from accidental 3D cursor movement, or viewport changes)"; + + /* callbacks */ + ot->exec = gp_strokes_reproject_exec; + ot->poll = gp_strokes_reproject_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} /* ************************************************ */ diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h index 0ff0878d4ce..4178d49d652 100644 --- a/source/blender/editors/gpencil/gpencil_intern.h +++ b/source/blender/editors/gpencil/gpencil_intern.h @@ -101,6 +101,18 @@ void gp_point_to_xy(GP_SpaceConversion *settings, struct bGPDstroke *gps, struct int *r_x, int *r_y); /** + * Convert a Grease Pencil coordinate (i.e. can be 2D or 3D) to screenspace (2D) + * + * Just like gp_point_to_xy(), except the resulting coordinates are floats not ints. + * Use this version to solve "stair-step" artifacts which may arise when roundtripping the calculations. + * + * \param[out] r_x The screen-space x-coordinate of the point + * \param[out] r_y The screen-space y-coordinate of the point + */ +void gp_point_to_xy_fl(GP_SpaceConversion *gsc, bGPDstroke *gps, bGPDspoint *pt, + float *r_x, float *r_y); + +/** * Convert point to parent space * * \param pt Original point @@ -183,7 +195,7 @@ void gp_subdivide_stroke(bGPDstroke *gps, const int new_totpoints); /** * Add randomness to stroke * \param gps Stroke data -* \param brsuh Brush data +* \param brush Brush data */ void gp_randomize_stroke(bGPDstroke *gps, bGPDbrush *brush); @@ -199,6 +211,7 @@ EnumPropertyItem *ED_gpencil_brushes_enum_itemf(bContext *C, PointerRNA *UNUSED( /* Enums of GP palettes */ EnumPropertyItem *ED_gpencil_palettes_enum_itemf(bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free); + /* ***************************************************** */ /* Operator Defines */ @@ -206,7 +219,7 @@ EnumPropertyItem *ED_gpencil_palettes_enum_itemf(bContext *C, PointerRNA *UNUSED void GPENCIL_OT_draw(struct wmOperatorType *ot); -/* Paint Modes for operator*/ +/* Paint Modes for operator */ typedef enum eGPencil_PaintModes { GP_PAINTMODE_DRAW = 0, GP_PAINTMODE_ERASER, @@ -214,6 +227,9 @@ typedef enum eGPencil_PaintModes { GP_PAINTMODE_DRAW_POLY } eGPencil_PaintModes; +/* maximum sizes of gp-session buffer */ +#define GP_STROKE_BUFFER_MAX 5000 + /* stroke editing ----- */ void GPENCIL_OT_editmode_toggle(struct wmOperatorType *ot); @@ -246,6 +262,7 @@ void GPENCIL_OT_snap_to_cursor(struct wmOperatorType *ot); void GPENCIL_OT_snap_cursor_to_selected(struct wmOperatorType *ot); void GPENCIL_OT_snap_cursor_to_center(struct wmOperatorType *ot); +void GPENCIL_OT_reproject(struct wmOperatorType *ot); /* stroke sculpting -- */ @@ -275,6 +292,11 @@ void GPENCIL_OT_active_frames_delete_all(struct wmOperatorType *ot); void GPENCIL_OT_convert(struct wmOperatorType *ot); +enum { + GP_STROKE_JOIN = -1, + GP_STROKE_JOINCOPY = 1 +}; + void GPENCIL_OT_stroke_arrange(struct wmOperatorType *ot); void GPENCIL_OT_stroke_change_color(struct wmOperatorType *ot); void GPENCIL_OT_stroke_lock_color(struct wmOperatorType *ot); @@ -295,10 +317,10 @@ void GPENCIL_OT_palette_add(struct wmOperatorType *ot); void GPENCIL_OT_palette_remove(struct wmOperatorType *ot); void GPENCIL_OT_palette_change(struct wmOperatorType *ot); void GPENCIL_OT_palette_lock_layer(struct wmOperatorType *ot); + void GPENCIL_OT_palettecolor_add(struct wmOperatorType *ot); void GPENCIL_OT_palettecolor_remove(struct wmOperatorType *ot); void GPENCIL_OT_palettecolor_isolate(struct wmOperatorType *ot); - void GPENCIL_OT_palettecolor_hide(struct wmOperatorType *ot); void GPENCIL_OT_palettecolor_reveal(struct wmOperatorType *ot); void GPENCIL_OT_palettecolor_lock_all(struct wmOperatorType *ot); @@ -313,7 +335,7 @@ void gpencil_undo_init(struct bGPdata *gpd); void gpencil_undo_push(struct bGPdata *gpd); void gpencil_undo_finish(void); -/******************************************************* */ +/* ****************************************************** */ /* FILTERED ACTION DATA - TYPES ---> XXX DEPRECEATED OLD ANIM SYSTEM CODE! */ /* XXX - TODO: replace this with the modern bAnimListElem... */ @@ -335,7 +357,7 @@ typedef struct bActListElem { short ownertype; /* type of owner */ } bActListElem; -/******************************************************* */ +/* ****************************************************** */ /* FILTER ACTION DATA - METHODS/TYPES */ /* filtering flags - under what circumstances should a channel be added */ @@ -358,6 +380,9 @@ typedef enum ACTCONT_TYPES { ACTCONT_GPENCIL } ACTCONT_TYPES; +/* ****************************************************** */ +/* Stroke Iteration Utilities */ + /** * Iterate over all editable strokes in the current context, * stopping on each usable layer + stroke pair (i.e. gpl and gps) @@ -393,4 +418,6 @@ typedef enum ACTCONT_TYPES { CTX_DATA_END; \ } (void)0 +/* ****************************************************** */ + #endif /* __GPENCIL_INTERN_H__ */ diff --git a/source/blender/editors/gpencil/gpencil_ops.c b/source/blender/editors/gpencil/gpencil_ops.c index 6bbb8f7c965..ae1c5554521 100644 --- a/source/blender/editors/gpencil/gpencil_ops.c +++ b/source/blender/editors/gpencil/gpencil_ops.c @@ -240,6 +240,12 @@ static void ed_keymap_gpencil_editing(wmKeyConfig *keyconf) WM_keymap_add_item(keymap, "GPENCIL_OT_active_frames_delete_all", XKEY, KM_PRESS, KM_SHIFT, 0); + /* join strokes */ + WM_keymap_add_item(keymap, "GPENCIL_OT_stroke_join", JKEY, KM_PRESS, KM_CTRL, 0); + + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_stroke_join", JKEY, KM_PRESS, KM_CTRL | KM_SHIFT, 0); + RNA_enum_set(kmi->ptr, "type", GP_STROKE_JOINCOPY); + /* copy + paste */ WM_keymap_add_item(keymap, "GPENCIL_OT_copy", CKEY, KM_PRESS, KM_CTRL, 0); WM_keymap_add_item(keymap, "GPENCIL_OT_paste", VKEY, KM_PRESS, KM_CTRL, 0); @@ -369,6 +375,8 @@ void ED_operatortypes_gpencil(void) WM_operatortype_append(GPENCIL_OT_snap_to_cursor); WM_operatortype_append(GPENCIL_OT_snap_cursor_to_selected); + WM_operatortype_append(GPENCIL_OT_reproject); + WM_operatortype_append(GPENCIL_OT_brush_paint); /* Editing (Buttons) ------------ */ diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index 403d632b5da..cc45cbd82af 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -164,9 +164,6 @@ typedef struct tGPsdata { /* ------ */ -/* maximum sizes of gp-session buffer */ -#define GP_STROKE_BUFFER_MAX 5000 - /* Macros for accessing sensitivity thresholds... */ /* minimum number of pixels mouse should move before new point created */ #define MIN_MANHATTEN_PX (U.gp_manhattendist) @@ -544,7 +541,7 @@ static short gp_stroke_addpoint(tGPsdata *p, const int mval[2], float pressure, } else if (p->paintmode == GP_PAINTMODE_DRAW_POLY) { - bGPDlayer *gpl = gpencil_layer_getactive(gpd); + bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); /* get pointer to destination point */ pt = (tGPspoint *)(gpd->sbuffer); @@ -692,6 +689,7 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) bGPDspoint *pt; tGPspoint *ptc; bGPDbrush *brush = p->brush; + ToolSettings *ts = p->scene->toolsettings; int i, totelem; /* since strokes are so fine, when using their depth we need a margin otherwise they might get missed */ @@ -920,13 +918,21 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) MEM_freeN(depth_arr); } /* Save palette color */ - bGPDpalette *palette = gpencil_palette_getactive(p->gpd); - bGPDpalettecolor *palcolor = gpencil_palettecolor_getactive(palette); + bGPDpalette *palette = BKE_gpencil_palette_getactive(p->gpd); + bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette); gps->palcolor = palcolor; - strcpy(gps->colorname, palcolor->info); + BLI_strncpy(gps->colorname, palcolor->info, sizeof(gps->colorname)); - /* add stroke to frame */ - BLI_addtail(&p->gpf->strokes, gps); + /* add stroke to frame, usually on tail of the listbase, but if on back is enabled the stroke is added on listbase head + * because the drawing order is inverse and the head stroke is the first to draw. This is very useful for artist + * when drawing the background + */ + if ((ts->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) && (p->paintmode != GP_PAINTMODE_DRAW_POLY)) { + BLI_addhead(&p->gpf->strokes, gps); + } + else { + BLI_addtail(&p->gpf->strokes, gps); + } gp_stroke_added_enable(p); } @@ -1221,7 +1227,7 @@ static bGPDpalettecolor *gp_create_new_color(bGPDpalette *palette) { bGPDpalettecolor *palcolor; - palcolor = gpencil_palettecolor_addnew(palette, DATA_("Color"), true); + palcolor = BKE_gpencil_palettecolor_addnew(palette, DATA_("Color"), true); return palcolor; } @@ -1234,12 +1240,12 @@ static void gp_init_drawing_brush(ToolSettings *ts, tGPsdata *p) /* if not exist, create a new one */ if (BLI_listbase_is_empty(&ts->gp_brushes)) { /* create new brushes */ - gpencil_brush_init_presets(ts); - brush = gpencil_brush_getactive(ts); + BKE_gpencil_brush_init_presets(ts); + brush = BKE_gpencil_brush_getactive(ts); } else { /* Use the current */ - brush = gpencil_brush_getactive(ts); + brush = BKE_gpencil_brush_getactive(ts); } /* be sure curves are initializated */ curvemapping_initialize(brush->cur_sensitivity); @@ -1263,23 +1269,23 @@ static void gp_init_palette(tGPsdata *p) /* if not exist, create a new palette */ if (BLI_listbase_is_empty(&gpd->palettes)) { /* create new palette */ - palette = gpencil_palette_addnew(gpd, DATA_("GP_Palette"), true); + palette = BKE_gpencil_palette_addnew(gpd, DATA_("GP_Palette"), true); /* now create a default color */ palcolor = gp_create_new_color(palette); } else { /* Use the current palette and color */ - palette = gpencil_palette_getactive(gpd); + palette = BKE_gpencil_palette_getactive(gpd); /* the palette needs one color */ if (BLI_listbase_is_empty(&palette->colors)) { palcolor = gp_create_new_color(palette); } else { - palcolor = gpencil_palettecolor_getactive(palette); + palcolor = BKE_gpencil_palettecolor_getactive(palette); } /* in some situations can be null, so use first */ if (palcolor == NULL) { - gpencil_palettecolor_setactive(palette, palette->colors.first); + BKE_gpencil_palettecolor_setactive(palette, palette->colors.first); palcolor = palette->colors.first; } } @@ -1439,7 +1445,7 @@ static bool gp_session_initdata(bContext *C, tGPsdata *p) else { /* if no existing GPencil block exists, add one */ if (*gpd_ptr == NULL) - *gpd_ptr = gpencil_data_addnew("GPencil"); + *gpd_ptr = BKE_gpencil_data_addnew("GPencil"); p->gpd = *gpd_ptr; } @@ -1514,9 +1520,9 @@ static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode) ToolSettings *ts = scene->toolsettings; /* get active layer (or add a new one if non-existent) */ - p->gpl = gpencil_layer_getactive(p->gpd); + p->gpl = BKE_gpencil_layer_getactive(p->gpd); if (p->gpl == NULL) { - p->gpl = gpencil_layer_addnew(p->gpd, "GP_Layer", true); + p->gpl = BKE_gpencil_layer_addnew(p->gpd, "GP_Layer", true); if (p->custom_color[3]) copy_v3_v3(p->gpl->color, p->custom_color); @@ -1544,7 +1550,7 @@ static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode) /* Add a new frame if needed (and based off the active frame, * as we need some existing strokes to erase) */ - gpl->actframe = gpencil_layer_getframe(gpl, CFRA, GP_GETFRAME_ADD_COPY); + gpl->actframe = BKE_gpencil_layer_getframe(gpl, CFRA, GP_GETFRAME_ADD_COPY); /* XXX: we omit GP_FRAME_PAINT here for now, * as it is only really useful for doing @@ -1580,7 +1586,7 @@ static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode) else add_frame_mode = GP_GETFRAME_ADD_NEW; - p->gpf = gpencil_layer_getframe(p->gpl, CFRA, add_frame_mode); + p->gpf = BKE_gpencil_layer_getframe(p->gpl, CFRA, add_frame_mode); if (p->gpf == NULL) { p->status = GP_STATUS_ERROR; @@ -2278,10 +2284,33 @@ static void gpencil_stroke_end(wmOperator *op) p->gpf = NULL; } +/* Move last stroke in the listbase to the head to be drawn below all previous strokes in the layer */ +static void gpencil_move_last_stroke_to_back(bContext *C) +{ + /* move last stroke (the polygon) to head of the listbase stroke to draw on back of all previous strokes */ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); + + /* sanity checks */ + if (ELEM(NULL, gpd, gpl, gpl->actframe)) { + return; + } + + bGPDframe *gpf = gpl->actframe; + bGPDstroke *gps = gpf->strokes.last; + if (ELEM(NULL, gps)) { + return; + } + + BLI_remlink(&gpf->strokes, gps); + BLI_insertlinkbefore(&gpf->strokes, gpf->strokes.first, gps); +} + /* events handling during interactive drawing part of operator */ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) { tGPsdata *p = op->customdata; + ToolSettings *ts = CTX_data_tool_settings(C); int estate = OPERATOR_PASS_THROUGH; /* default exit state - pass through to support MMB view nav, etc. */ /* if (event->type == NDOF_MOTION) @@ -2334,6 +2363,14 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) if (ELEM(event->type, RETKEY, PADENTER, ESCKEY, SPACEKEY, EKEY)) { /* exit() ends the current stroke before cleaning up */ /* printf("\t\tGP - end of paint op + end of stroke\n"); */ + /* if drawing polygon and enable on back, must move stroke */ + if (ts) { + if ((ts->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) && (p->paintmode == GP_PAINTMODE_DRAW_POLY)) { + if (p->flags & GP_PAINTFLAG_STROKEADDED) { + gpencil_move_last_stroke_to_back(C); + } + } + } p->status = GP_STATUS_DONE; estate = OPERATOR_FINISHED; } @@ -2390,9 +2427,14 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) } else { /* printf("\t\tGP - end of stroke + op\n"); */ - /* disable paint session */ - p->scene->toolsettings->gpencil_flags &= ~GP_TOOL_FLAG_PAINTSESSIONS_ON; - + /* if drawing polygon and enable on back, must move stroke */ + if (ts) { + if ((ts->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) && (p->paintmode == GP_PAINTMODE_DRAW_POLY)) { + if (p->flags & GP_PAINTFLAG_STROKEADDED) { + gpencil_move_last_stroke_to_back(C); + } + } + } p->status = GP_STATUS_DONE; estate = OPERATOR_FINISHED; } @@ -2422,9 +2464,6 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) in_bounds = true; } else { - /* disable paint session */ - p->scene->toolsettings->gpencil_flags &= ~GP_TOOL_FLAG_PAINTSESSIONS_ON; - /* Out of bounds, or invalid in some other way */ p->status = GP_STATUS_ERROR; estate = OPERATOR_CANCELLED; @@ -2441,9 +2480,6 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) in_bounds = BLI_rcti_isect_pt_v(®ion_rect, event->mval); } else { - /* disable paint session */ - p->scene->toolsettings->gpencil_flags &= ~GP_TOOL_FLAG_PAINTSESSIONS_ON; - /* No region */ p->status = GP_STATUS_ERROR; estate = OPERATOR_CANCELLED; @@ -2471,9 +2507,6 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) p = gpencil_stroke_begin(C, op); if (p->status == GP_STATUS_ERROR) { - /* disable paint session */ - p->scene->toolsettings->gpencil_flags &= ~GP_TOOL_FLAG_PAINTSESSIONS_ON; - estate = OPERATOR_CANCELLED; } } @@ -2482,9 +2515,14 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) * NOTE: Don't eter this case if an error occurred while finding the * region (as above) */ - /* disable paint session */ - p->scene->toolsettings->gpencil_flags &= ~GP_TOOL_FLAG_PAINTSESSIONS_ON; - + /* if drawing polygon and enable on back, must move stroke */ + if (ts) { + if ((ts->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) && (p->paintmode == GP_PAINTMODE_DRAW_POLY)) { + if (p->flags & GP_PAINTFLAG_STROKEADDED) { + gpencil_move_last_stroke_to_back(C); + } + } + } p->status = GP_STATUS_DONE; estate = OPERATOR_FINISHED; } diff --git a/source/blender/editors/gpencil/gpencil_select.c b/source/blender/editors/gpencil/gpencil_select.c index 612b35aa608..45dbde80284 100644 --- a/source/blender/editors/gpencil/gpencil_select.c +++ b/source/blender/editors/gpencil/gpencil_select.c @@ -36,6 +36,7 @@ #include "MEM_guardedalloc.h" #include "BLI_blenlib.h" +#include "BLI_ghash.h" #include "BLI_lasso.h" #include "BLI_utildefines.h" #include "BLI_math_vector.h" @@ -253,6 +254,9 @@ typedef enum eGP_SelectGrouped { /* Select strokes in the same layer */ GP_SEL_SAME_LAYER = 0, + /* Select strokes with the same color */ + GP_SEL_SAME_COLOR = 1, + /* TODO: All with same prefix - Useful for isolating all layers for a particular character for instance */ /* TODO: All with same appearance - colour/opacity/volumetric/fills ? */ } eGP_SelectGrouped; @@ -266,7 +270,7 @@ static void gp_select_same_layer(bContext *C) CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) { - bGPDframe *gpf = gpencil_layer_getframe(gpl, CFRA, 0); + bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, CFRA, 0); bGPDstroke *gps; bool found = false; @@ -302,6 +306,43 @@ static void gp_select_same_layer(bContext *C) CTX_DATA_END; } +/* Select all strokes with same colors as selected ones */ +static void gp_select_same_color(bContext *C) +{ + /* First, build set containing all the colors of selected strokes + * - We use the palette names, so that we can select all strokes with one + * (potentially missing) color, and remap them to something else + */ + GSet *selected_colors = BLI_gset_str_new("GP Selected Colors"); + + CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) + { + if (gps->flag & GP_STROKE_SELECT) { + /* add instead of insert here, otherwise the uniqueness check gets skipped, + * and we get many duplicate entries... + */ + BLI_gset_add(selected_colors, gps->colorname); + } + } + CTX_DATA_END; + + /* Second, select any visible stroke that uses these colors */ + CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) + { + if (BLI_gset_haskey(selected_colors, gps->colorname)) { + /* select this stroke */ + bGPDspoint *pt; + int i; + + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + pt->flag |= GP_SPOINT_SELECT; + } + + gps->flag |= GP_STROKE_SELECT; + } + } + CTX_DATA_END; +} /* ----------------------------------- */ @@ -314,6 +355,9 @@ static int gpencil_select_grouped_exec(bContext *C, wmOperator *op) case GP_SEL_SAME_LAYER: gp_select_same_layer(C); break; + case GP_SEL_SAME_COLOR: + gp_select_same_color(C); + break; default: BLI_assert(!"unhandled select grouped gpencil mode"); @@ -329,6 +373,7 @@ void GPENCIL_OT_select_grouped(wmOperatorType *ot) { static EnumPropertyItem prop_select_grouped_types[] = { {GP_SEL_SAME_LAYER, "LAYER", 0, "Layer", "Shared layers"}, + {GP_SEL_SAME_COLOR, "COLOR", 0, "Color", "Shared colors"}, {0, NULL, 0, NULL, NULL} }; @@ -338,7 +383,7 @@ void GPENCIL_OT_select_grouped(wmOperatorType *ot) ot->description = "Select all strokes with similar characteristics"; /* callbacks */ - //ot->invoke = WM_menu_invoke; + ot->invoke = WM_menu_invoke; ot->exec = gpencil_select_grouped_exec; ot->poll = gpencil_select_poll; @@ -709,7 +754,7 @@ static bool gp_stroke_do_circle_sel( } /* Ensure that stroke selection is in sync with its points */ - gpencil_stroke_sync_selection(gps); + BKE_gpencil_stroke_sync_selection(gps); } return changed; @@ -871,7 +916,7 @@ static int gpencil_border_select_exec(bContext *C, wmOperator *op) } /* Ensure that stroke selection is in sync with its points */ - gpencil_stroke_sync_selection(gps); + BKE_gpencil_stroke_sync_selection(gps); } GP_EDITABLE_STROKES_END; @@ -981,7 +1026,7 @@ static int gpencil_lasso_select_exec(bContext *C, wmOperator *op) } /* Ensure that stroke selection is in sync with its points */ - gpencil_stroke_sync_selection(gps); + BKE_gpencil_stroke_sync_selection(gps); } GP_EDITABLE_STROKES_END; @@ -1152,7 +1197,7 @@ static int gpencil_select_exec(bContext *C, wmOperator *op) hit_point->flag &= ~GP_SPOINT_SELECT; /* ensure that stroke is selected correctly */ - gpencil_stroke_sync_selection(hit_stroke); + BKE_gpencil_stroke_sync_selection(hit_stroke); } } diff --git a/source/blender/editors/gpencil/gpencil_undo.c b/source/blender/editors/gpencil/gpencil_undo.c index 1d7582eb18b..793ed2a07d0 100644 --- a/source/blender/editors/gpencil/gpencil_undo.c +++ b/source/blender/editors/gpencil/gpencil_undo.c @@ -100,14 +100,14 @@ int ED_undo_gpencil_step(bContext *C, int step, const char *name) bGPdata *gpd = *gpd_ptr; bGPDlayer *gpl, *gpld; - free_gpencil_layers(&gpd->layers); + BKE_gpencil_free_layers(&gpd->layers); /* copy layers */ BLI_listbase_clear(&gpd->layers); for (gpl = new_gpd->layers.first; gpl; gpl = gpl->next) { /* make a copy of source layer and its data */ - gpld = gpencil_layer_duplicate(gpl); + gpld = BKE_gpencil_layer_duplicate(gpl); BLI_addtail(&gpd->layers, gpld); } } @@ -153,7 +153,7 @@ void gpencil_undo_push(bGPdata *gpd) /* create new undo node */ undo_node = MEM_callocN(sizeof(bGPundonode), "gpencil undo node"); - undo_node->gpd = gpencil_data_duplicate(G.main, gpd, true); + undo_node->gpd = BKE_gpencil_data_duplicate(G.main, gpd, true); cur_node = undo_node; diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c index f2c542da0aa..564ba639983 100644 --- a/source/blender/editors/gpencil/gpencil_utils.c +++ b/source/blender/editors/gpencil/gpencil_utils.c @@ -225,7 +225,7 @@ bool ED_gpencil_has_keyframe_v3d(Scene *scene, Object *ob, int cfra) /* just check both for now... */ // XXX: this could get confusing (e.g. if only on the object, but other places don't show this) if (scene->gpd) { - bGPDlayer *gpl = gpencil_layer_getactive(scene->gpd); + bGPDlayer *gpl = BKE_gpencil_layer_getactive(scene->gpd); if (gpl) { if (gpl->actframe) { // XXX: assumes that frame has been fetched already @@ -239,7 +239,7 @@ bool ED_gpencil_has_keyframe_v3d(Scene *scene, Object *ob, int cfra) } if (ob && ob->gpd) { - bGPDlayer *gpl = gpencil_layer_getactive(ob->gpd); + bGPDlayer *gpl = BKE_gpencil_layer_getactive(ob->gpd); if (gpl) { if (gpl->actframe) { // XXX: assumes that frame has been fetched already @@ -269,7 +269,7 @@ int gp_add_poll(bContext *C) int gp_active_layer_poll(bContext *C) { bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDlayer *gpl = gpencil_layer_getactive(gpd); + bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); return (gpl != NULL); } @@ -278,7 +278,7 @@ int gp_active_layer_poll(bContext *C) int gp_active_brush_poll(bContext *C) { ToolSettings *ts = CTX_data_tool_settings(C); - bGPDbrush *brush = gpencil_brush_getactive(ts); + bGPDbrush *brush = BKE_gpencil_brush_getactive(ts); return (brush != NULL); } @@ -287,7 +287,7 @@ int gp_active_brush_poll(bContext *C) int gp_active_palette_poll(bContext *C) { bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDpalette *palette = gpencil_palette_getactive(gpd); + bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); return (palette != NULL); } @@ -296,8 +296,8 @@ int gp_active_palette_poll(bContext *C) int gp_active_palettecolor_poll(bContext *C) { bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDpalette *palette = gpencil_palette_getactive(gpd); - bGPDpalettecolor *palcolor = gpencil_palettecolor_getactive(palette); + bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); + bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette); return (palcolor != NULL); } @@ -470,20 +470,20 @@ bGPDpalettecolor *ED_gpencil_stroke_getcolor(bGPdata *gpd, bGPDstroke *gps) return gps->palcolor; /* get palette */ - palette = gpencil_palette_getactive(gpd); + palette = BKE_gpencil_palette_getactive(gpd); if (palette == NULL) { - palette = gpencil_palette_addnew(gpd, DATA_("GP_Palette"), true); + palette = BKE_gpencil_palette_addnew(gpd, DATA_("GP_Palette"), true); } /* get color */ - palcolor = gpencil_palettecolor_getbyname(palette, gps->colorname); + palcolor = BKE_gpencil_palettecolor_getbyname(palette, gps->colorname); if (palcolor == NULL) { if (gps->palcolor == NULL) { - palcolor = gpencil_palettecolor_addnew(palette, DATA_("Color"), true); + palcolor = BKE_gpencil_palettecolor_addnew(palette, DATA_("Color"), true); /* set to a different color */ ARRAY_SET_ITEMS(palcolor->color, 1.0f, 0.0f, 1.0f, 0.9f); } else { - palcolor = gpencil_palettecolor_addnew(palette, gps->colorname, true); + palcolor = BKE_gpencil_palettecolor_addnew(palette, gps->colorname, true); /* set old color and attributes */ bGPDpalettecolor *gpscolor = gps->palcolor; copy_v4_v4(palcolor->color, gpscolor->color); @@ -628,6 +628,63 @@ void gp_point_to_xy(GP_SpaceConversion *gsc, bGPDstroke *gps, bGPDspoint *pt, } } +/* Convert Grease Pencil points to screen-space values (as floats) + * WARNING: This assumes that the caller has already checked whether the stroke in question can be drawn + */ +void gp_point_to_xy_fl(GP_SpaceConversion *gsc, bGPDstroke *gps, bGPDspoint *pt, + float *r_x, float *r_y) +{ + ARegion *ar = gsc->ar; + View2D *v2d = gsc->v2d; + rctf *subrect = gsc->subrect; + float xyval[2]; + + /* sanity checks */ + BLI_assert(!(gps->flag & GP_STROKE_3DSPACE) || (gsc->sa->spacetype == SPACE_VIEW3D)); + BLI_assert(!(gps->flag & GP_STROKE_2DSPACE) || (gsc->sa->spacetype != SPACE_VIEW3D)); + + + if (gps->flag & GP_STROKE_3DSPACE) { + if (ED_view3d_project_float_global(ar, &pt->x, xyval, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) { + *r_x = xyval[0]; + *r_y = xyval[1]; + } + else { + *r_x = 0.0f; + *r_y = 0.0f; + } + } + else if (gps->flag & GP_STROKE_2DSPACE) { + float vec[3] = {pt->x, pt->y, 0.0f}; + int t_x, t_y; + + mul_m4_v3(gsc->mat, vec); + UI_view2d_view_to_region_clip(v2d, vec[0], vec[1], &t_x, &t_y); + + if ((t_x == t_y) && (t_x == V2D_IS_CLIPPED)) { + /* XXX: Or should we just always use the values as-is? */ + *r_x = 0.0f; + *r_y = 0.0f; + } + else { + *r_x = (float)t_x; + *r_y = (float)t_y; + } + } + else { + if (subrect == NULL) { + /* normal 3D view (or view space) */ + *r_x = (pt->x / 100.0f * ar->winx); + *r_y = (pt->y / 100.0f * ar->winy); + } + else { + /* camera view, use subrect */ + *r_x = ((pt->x / 100.0f) * BLI_rctf_size_x(subrect)) + subrect->xmin; + *r_y = ((pt->y / 100.0f) * BLI_rctf_size_y(subrect)) + subrect->ymin; + } + } +} + /** * Project screenspace coordinates to 3D-space * @@ -883,10 +940,12 @@ void gp_randomize_stroke(bGPDstroke *gps, bGPDbrush *brush) float normal[3]; cross_v3_v3v3(normal, v1, v2); normalize_v3(normal); + /* get orthogonal vector to plane to rotate random effect */ float ortho[3]; cross_v3_v3v3(ortho, v1, normal); normalize_v3(ortho); + /* Read all points and apply shift vector (first and last point not modified) */ for (int i = 1; i < gps->totpoints - 1; ++i) { bGPDspoint *pt = &gps->points[i]; @@ -955,8 +1014,8 @@ bool ED_gpencil_stroke_minmax( } return changed; } -/* Dynamic Enums of GP Brushes */ +/* Dynamic Enums of GP Brushes */ EnumPropertyItem *ED_gpencil_brushes_enum_itemf( bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free) @@ -990,8 +1049,8 @@ EnumPropertyItem *ED_gpencil_brushes_enum_itemf( return item; } -/* Dynamic Enums of GP Palettes */ +/* Dynamic Enums of GP Palettes */ EnumPropertyItem *ED_gpencil_palettes_enum_itemf( bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free) diff --git a/source/blender/editors/include/ED_anim_api.h b/source/blender/editors/include/ED_anim_api.h index 0940f594482..bfd89e90fce 100644 --- a/source/blender/editors/include/ED_anim_api.h +++ b/source/blender/editors/include/ED_anim_api.h @@ -156,6 +156,7 @@ typedef enum eAnim_ChannelType { ANIMTYPE_DSMAT, ANIMTYPE_DSLAM, ANIMTYPE_DSCAM, + ANIMTYPE_DSCACHEFILE, ANIMTYPE_DSCUR, ANIMTYPE_DSSKEY, ANIMTYPE_DSWOR, @@ -275,6 +276,7 @@ typedef enum eAnimFilter_Flags { #define FILTER_MAT_OBJD(ma) (CHECK_TYPE_INLINE(ma, Material *), ((ma->flag & MA_DS_EXPAND))) #define FILTER_LAM_OBJD(la) (CHECK_TYPE_INLINE(la, Lamp *), ((la->flag & LA_DS_EXPAND))) #define FILTER_CAM_OBJD(ca) (CHECK_TYPE_INLINE(ca, Camera *), ((ca->flag & CAM_DS_EXPAND))) +#define FILTER_CACHEFILE_OBJD(cf) (CHECK_TYPE_INLINE(cf, CacheFile *), ((cf->flag & CACHEFILE_DS_EXPAND))) #define FILTER_CUR_OBJD(cu) (CHECK_TYPE_INLINE(cu, Curve *), ((cu->flag & CU_DS_EXPAND))) #define FILTER_PART_OBJD(part) (CHECK_TYPE_INLINE(part, ParticleSettings *), ((part->flag & PART_DS_EXPAND))) #define FILTER_MBALL_OBJD(mb) (CHECK_TYPE_INLINE(mb, MetaBall *), ((mb->flag2 & MB_DS_EXPAND))) diff --git a/source/blender/editors/include/ED_keyframes_draw.h b/source/blender/editors/include/ED_keyframes_draw.h index b1f3f012e09..c478a8b17e5 100644 --- a/source/blender/editors/include/ED_keyframes_draw.h +++ b/source/blender/editors/include/ED_keyframes_draw.h @@ -34,6 +34,7 @@ struct bAnimContext; struct AnimData; +struct CacheFile; struct FCurve; struct bDopeSheet; struct bAction; @@ -141,6 +142,8 @@ void agroup_to_keylist(struct AnimData *adt, struct bActionGroup *agrp, struct D void action_to_keylist(struct AnimData *adt, struct bAction *act, struct DLRBT_Tree *keys, struct DLRBT_Tree *blocks); /* Object */ void ob_to_keylist(struct bDopeSheet *ads, struct Object *ob, struct DLRBT_Tree *keys, struct DLRBT_Tree *blocks); +/* Cache File */ +void cachefile_to_keylist(struct bDopeSheet *ads, struct CacheFile *cache_file, struct DLRBT_Tree *keys, struct DLRBT_Tree *blocks); /* Scene */ void scene_to_keylist(struct bDopeSheet *ads, struct Scene *sce, struct DLRBT_Tree *keys, struct DLRBT_Tree *blocks); /* DopeSheet Summary */ diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index f77e795adca..49e5845e3ca 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -393,6 +393,8 @@ struct uiLayout *UI_popup_menu_layout(uiPopupMenu *head); void UI_popup_menu_reports(struct bContext *C, struct ReportList *reports) ATTR_NONNULL(); int UI_popup_menu_invoke(struct bContext *C, const char *idname, struct ReportList *reports) ATTR_NONNULL(1, 2); +void UI_popup_menu_retval_set(const uiBlock *block, const int retval, const bool enable); + /* Pie menus */ typedef struct uiPieMenu uiPieMenu; @@ -945,6 +947,7 @@ void uiTemplateReportsBanner(uiLayout *layout, struct bContext *C); void uiTemplateKeymapItemProperties(uiLayout *layout, struct PointerRNA *ptr); void uiTemplateComponentMenu(uiLayout *layout, struct PointerRNA *ptr, const char *propname, const char *name); void uiTemplateNodeSocket(uiLayout *layout, struct bContext *C, float *color); +void uiTemplateCacheFile(uiLayout *layout, struct bContext *C, struct PointerRNA *ptr, const char *propname); /* Default UIList class name, keep in sync with its declaration in bl_ui/__init__.py */ #define UI_UL_DEFAULT_CLASS_NAME "UI_UL_list" diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 79831cefde6..4972e16bf2e 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -5415,6 +5415,7 @@ static bool ui_numedit_but_HSVCUBE( return changed; } +#ifdef WITH_INPUT_NDOF static void ui_ndofedit_but_HSVCUBE( uiBut *but, uiHandleButtonData *data, const wmNDOFMotionData *ndof, @@ -5487,6 +5488,7 @@ static void ui_ndofedit_but_HSVCUBE( copy_v3_v3(data->vec, rgb); ui_but_v3_set(but, data->vec); } +#endif /* WITH_INPUT_NDOF */ static int ui_do_but_HSVCUBE( bContext *C, uiBlock *block, uiBut *but, @@ -5514,6 +5516,7 @@ static int ui_do_but_HSVCUBE( return WM_UI_HANDLER_BREAK; } +#ifdef WITH_INPUT_NDOF else if (event->type == NDOF_MOTION) { const wmNDOFMotionData *ndof = event->customdata; const enum eSnapType snap = ui_event_to_snap(event); @@ -5525,6 +5528,7 @@ static int ui_do_but_HSVCUBE( return WM_UI_HANDLER_BREAK; } +#endif /* WITH_INPUT_NDOF */ /* XXX hardcoded keymap check.... */ else if (event->type == BACKSPACEKEY && event->val == KM_PRESS) { if (ELEM(but->a1, UI_GRAD_V_ALT, UI_GRAD_L_ALT)) { @@ -5680,6 +5684,7 @@ static bool ui_numedit_but_HSVCIRCLE( return changed; } +#ifdef WITH_INPUT_NDOF static void ui_ndofedit_but_HSVCIRCLE( uiBut *but, uiHandleButtonData *data, const wmNDOFMotionData *ndof, @@ -5750,7 +5755,7 @@ static void ui_ndofedit_but_HSVCIRCLE( ui_but_v3_set(but, data->vec); } - +#endif /* WITH_INPUT_NDOF */ static int ui_do_but_HSVCIRCLE( bContext *C, uiBlock *block, uiBut *but, @@ -5778,6 +5783,7 @@ static int ui_do_but_HSVCIRCLE( return WM_UI_HANDLER_BREAK; } +#ifdef WITH_INPUT_NDOF else if (event->type == NDOF_MOTION) { const enum eSnapType snap = ui_event_to_snap(event); const wmNDOFMotionData *ndof = event->customdata; @@ -5789,6 +5795,7 @@ static int ui_do_but_HSVCIRCLE( return WM_UI_HANDLER_BREAK; } +#endif /* WITH_INPUT_NDOF */ /* XXX hardcoded keymap check.... */ else if (event->type == BACKSPACEKEY && event->val == KM_PRESS) { int len; @@ -9914,6 +9921,17 @@ static int ui_handle_menus_recursive( return retval; } +/** + * Allow setting menu return value from externals. E.g. WM might need to do this for exiting files correctly. + */ +void UI_popup_menu_retval_set(const uiBlock *block, const int retval, const bool enable) +{ + uiPopupBlockHandle *menu = block->handle; + if (menu) { + menu->menuretval = enable ? (menu->menuretval | retval) : (menu->menuretval & retval); + } +} + /* *************** UI event handlers **************** */ static int ui_region_handler(bContext *C, const wmEvent *event, void *UNUSED(userdata)) @@ -10156,7 +10174,11 @@ static void ui_popup_handler_remove(bContext *C, void *userdata) { uiPopupBlockHandle *menu = userdata; - if (menu->cancel_func) { + /* More correct would be to expect UI_RETURN_CANCEL here, but not wanting to + * cancel when removing handlers because of file exit is a rare exception. + * So instead of setting cancel flag for all menus before removing handlers, + * just explicitly flag menu with UI_RETURN_OK to avoid cancelling it. */ + if ((menu->menuretval & UI_RETURN_OK) == 0 && menu->cancel_func) { menu->cancel_func(C, menu->popup_arg); } diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c index 4107414a240..22a450d2523 100644 --- a/source/blender/editors/interface/interface_icons.c +++ b/source/blender/editors/interface/interface_icons.c @@ -1541,6 +1541,8 @@ int UI_idcode_icon_get(const int idcode) return ICON_BRUSH_DATA; case ID_CA: return ICON_CAMERA_DATA; + case ID_CF: + return ICON_FILE; case ID_CU: return ICON_CURVE_DATA; case ID_GD: diff --git a/source/blender/editors/interface/interface_regions.c b/source/blender/editors/interface/interface_regions.c index c621fcf493d..c507401b9a0 100644 --- a/source/blender/editors/interface/interface_regions.c +++ b/source/blender/editors/interface/interface_regions.c @@ -1380,6 +1380,7 @@ static void ui_searchbox_region_draw_cb__operator(const bContext *UNUSED(C), ARe rect_pre.xmax = rect_post.xmin = rect.xmin + ((rect.xmax - rect.xmin) / 4); /* widget itself */ + /* NOTE: i18n messages extracting tool does the same, please keep it in sync. */ { wmOperatorType *ot = data->items.pointers[a]; @@ -1400,7 +1401,8 @@ static void ui_searchbox_region_draw_cb__operator(const bContext *UNUSED(C), ARe } rect_pre.xmax += 4; /* sneaky, avoid showing ugly margin */ - ui_draw_menu_item(&data->fstyle, &rect_pre, text_pre, data->items.icons[a], state, false); + ui_draw_menu_item(&data->fstyle, &rect_pre, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, text_pre), + data->items.icons[a], state, false); ui_draw_menu_item(&data->fstyle, &rect_post, data->items.names[a], 0, state, data->use_sep); } diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 58cadf5587a..50dd219b53c 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -32,6 +32,7 @@ #include "MEM_guardedalloc.h" +#include "DNA_cachefile_types.h" #include "DNA_node_types.h" #include "DNA_scene_types.h" #include "DNA_object_types.h" @@ -325,7 +326,9 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event) } else { if (id) { + Main *bmain = CTX_data_main(C); id_single_user(C, id, &template->ptr, template->prop); + DAG_relations_tag_update(bmain); } } } @@ -368,6 +371,7 @@ static const char *template_id_browse_tip(StructRNA *type) case ID_MSK: return N_("Browse Mask to be linked"); case ID_PAL: return N_("Browse Palette Data to be linked"); case ID_PC: return N_("Browse Paint Curve Data to be linked"); + case ID_CF: return N_("Browse Cache Files to be linked"); } } return N_("Browse ID data to be linked"); @@ -872,8 +876,8 @@ static uiLayout *draw_modifier( /* mode enabling buttons */ UI_block_align_begin(block); - /* Softbody not allowed in this situation, enforce! */ - if (((md->type != eModifierType_Softbody && md->type != eModifierType_Collision) || !(ob->pd && ob->pd->deflect)) && + /* Collision and Surface are always enabled, hide buttons! */ + if (((md->type != eModifierType_Collision) || !(ob->pd && ob->pd->deflect)) && (md->type != eModifierType_Surface) ) { uiItemR(row, &ptr, "show_render", 0, "", ICON_NONE); @@ -3848,3 +3852,73 @@ void uiTemplateNodeSocket(uiLayout *layout, bContext *UNUSED(C), float *color) UI_block_align_end(block); } + +/********************************* Cache File *********************************/ + +void uiTemplateCacheFile(uiLayout *layout, bContext *C, PointerRNA *ptr, const char *propname) +{ + if (!ptr->data) { + return; + } + + PropertyRNA *prop = RNA_struct_find_property(ptr, propname); + + if (!prop) { + printf("%s: property not found: %s.%s\n", + __func__, RNA_struct_identifier(ptr->type), propname); + return; + } + + if (RNA_property_type(prop) != PROP_POINTER) { + printf("%s: expected pointer property for %s.%s\n", + __func__, RNA_struct_identifier(ptr->type), propname); + return; + } + + PointerRNA fileptr = RNA_property_pointer_get(ptr, prop); + CacheFile *file = fileptr.data; + + uiLayoutSetContextPointer(layout, "edit_cachefile", &fileptr); + + uiTemplateID(layout, C, ptr, propname, NULL, "CACHEFILE_OT_open", NULL); + + if (!file) { + return; + } + + uiLayout *row = uiLayoutRow(layout, false); + uiBlock *block = uiLayoutGetBlock(row); + uiDefBut(block, UI_BTYPE_LABEL, 0, IFACE_("File Path:"), 0, 19, 145, 19, NULL, 0, 0, 0, 0, ""); + + row = uiLayoutRow(layout, false); + uiLayout *split = uiLayoutSplit(row, 0.0f, false); + row = uiLayoutRow(split, true); + + uiItemR(row, &fileptr, "filepath", 0, "", ICON_NONE); + uiItemO(row, "", ICON_FILE_REFRESH, "cachefile.reload"); + + row = uiLayoutRow(layout, false); + uiItemR(row, &fileptr, "is_sequence", 0, "Is Sequence", ICON_NONE); + + row = uiLayoutRow(layout, false); + uiItemR(row, &fileptr, "override_frame", 0, "Override Frame", ICON_NONE); + + row = uiLayoutRow(layout, false); + uiLayoutSetEnabled(row, RNA_boolean_get(&fileptr, "override_frame")); + uiItemR(row, &fileptr, "frame", 0, "Frame", ICON_NONE); + + row = uiLayoutRow(layout, false); + uiItemL(row, IFACE_("Manual Transform:"), ICON_NONE); + + row = uiLayoutRow(layout, false); + uiItemR(row, &fileptr, "scale", 0, "Scale", ICON_NONE); + + /* TODO: unused for now, so no need to expose. */ +#if 0 + row = uiLayoutRow(layout, false); + uiItemR(row, &fileptr, "forward_axis", 0, "Forward Axis", ICON_NONE); + + row = uiLayoutRow(layout, false); + uiItemR(row, &fileptr, "up_axis", 0, "Up Axis", ICON_NONE); +#endif +} diff --git a/source/blender/editors/interface/view2d_ops.c b/source/blender/editors/interface/view2d_ops.c index f60fed398ec..d2b2f12c1a5 100644 --- a/source/blender/editors/interface/view2d_ops.c +++ b/source/blender/editors/interface/view2d_ops.c @@ -127,6 +127,7 @@ static int view_pan_init(bContext *C, wmOperator *op) return 1; } +#ifdef WITH_INPUT_NDOF static int view_pan_poll(bContext *C) { ARegion *ar = CTX_wm_region(C); @@ -144,6 +145,7 @@ static int view_pan_poll(bContext *C) /* view can pan */ return 1; } +#endif /* apply transform to view (i.e. adjust 'cur' rect) */ static void view_pan_apply_ex(bContext *C, v2dViewPanData *vpd, float dx, float dy) @@ -1296,7 +1298,7 @@ static void VIEW2D_OT_zoom_border(wmOperatorType *ot) WM_operator_properties_gesture_border(ot, false); } - +#ifdef WITH_INPUT_NDOF static int view2d_ndof_invoke(bContext *C, wmOperator *op, const wmEvent *event) { if (event->type != NDOF_MOTION) { @@ -1369,6 +1371,7 @@ static void VIEW2D_OT_ndof(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_LOCK_BYPASS; } +#endif /* WITH_INPUT_NDOF */ /* ********************************************************* */ /* SMOOTH VIEW */ @@ -2067,7 +2070,9 @@ void ED_operatortypes_view2d(void) WM_operatortype_append(VIEW2D_OT_zoom); WM_operatortype_append(VIEW2D_OT_zoom_border); +#ifdef WITH_INPUT_NDOF WM_operatortype_append(VIEW2D_OT_ndof); +#endif WM_operatortype_append(VIEW2D_OT_smoothview); @@ -2097,7 +2102,9 @@ void ED_keymap_view2d(wmKeyConfig *keyconf) WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_down", WHEELDOWNMOUSE, KM_PRESS, KM_SHIFT, 0); WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_up", WHEELUPMOUSE, KM_PRESS, KM_SHIFT, 0); +#ifdef WITH_INPUT_NDOF WM_keymap_add_item(keymap, "VIEW2D_OT_ndof", NDOF_MOTION, 0, 0, 0); +#endif /* zoom - single step */ WM_keymap_add_item(keymap, "VIEW2D_OT_zoom_out", WHEELOUTMOUSE, KM_PRESS, 0, 0); diff --git a/source/blender/editors/io/CMakeLists.txt b/source/blender/editors/io/CMakeLists.txt index 828cb494eab..b3bbce939a5 100644 --- a/source/blender/editors/io/CMakeLists.txt +++ b/source/blender/editors/io/CMakeLists.txt @@ -28,6 +28,8 @@ set(INC ../../makesrna ../../windowmanager ../../collada + ../../alembic + ../../../../intern/guardedalloc ) set(INC_SYS @@ -35,9 +37,13 @@ set(INC_SYS ) set(SRC + io_alembic.c + io_cache.c io_collada.c io_ops.c + io_alembic.h + io_cache.h io_collada.h io_ops.h ) @@ -46,6 +52,14 @@ if(WITH_OPENCOLLADA) add_definitions(-DWITH_COLLADA) endif() +if(WITH_ALEMBIC) + add_definitions(-DWITH_ALEMBIC) + + if(WITH_ALEMBIC_HDF5) + add_definitions(-DWITH_ALEMBIC_HDF5) + endif() +endif() + if(WITH_INTERNATIONAL) add_definitions(-DWITH_INTERNATIONAL) endif() diff --git a/source/blender/editors/io/io_alembic.c b/source/blender/editors/io/io_alembic.c new file mode 100644 index 00000000000..cd75983e0a0 --- /dev/null +++ b/source/blender/editors/io/io_alembic.c @@ -0,0 +1,484 @@ +/* + * ***** 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) 2016 Blender Foundation. + * All rights reserved. + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +#ifdef WITH_ALEMBIC + +/* needed for directory lookup */ +#ifndef WIN32 +# include <dirent.h> +#else +# include "BLI_winstuff.h" +#endif + +#include "MEM_guardedalloc.h" + +#include "DNA_mesh_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_space_types.h" + +#include "BKE_context.h" +#include "BKE_global.h" +#include "BKE_main.h" +#include "BKE_report.h" + +#include "BLI_listbase.h" +#include "BLI_math_vector.h" +#include "BLI_path_util.h" +#include "BLI_string.h" +#include "BLI_utildefines.h" + +#include "BLT_translation.h" + +#include "RNA_access.h" +#include "RNA_define.h" +#include "RNA_enum_types.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "io_alembic.h" + +#include "ABC_alembic.h" + +static int wm_alembic_export_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + if (!RNA_struct_property_is_set(op->ptr, "filepath")) { + Main *bmain = CTX_data_main(C); + char filepath[FILE_MAX]; + + if (bmain->name[0] == '\0') { + BLI_strncpy(filepath, "untitled", sizeof(filepath)); + } + else { + BLI_strncpy(filepath, bmain->name, sizeof(filepath)); + } + + BLI_replace_extension(filepath, sizeof(filepath), ".abc"); + RNA_string_set(op->ptr, "filepath", filepath); + } + + WM_event_add_fileselect(C, op); + + return OPERATOR_RUNNING_MODAL; + + UNUSED_VARS(event); +} + +static int wm_alembic_export_exec(bContext *C, wmOperator *op) +{ + if (!RNA_struct_property_is_set(op->ptr, "filepath")) { + BKE_report(op->reports, RPT_ERROR, "No filename given"); + return OPERATOR_CANCELLED; + } + + char filename[FILE_MAX]; + RNA_string_get(op->ptr, "filepath", filename); + + const struct AlembicExportParams params = { + .frame_start = RNA_int_get(op->ptr, "start"), + .frame_end = RNA_int_get(op->ptr, "end"), + + .frame_step_xform = 1.0 / (double)RNA_int_get(op->ptr, "xsamples"), + .frame_step_shape = 1.0 / (double)RNA_int_get(op->ptr, "gsamples"), + + .shutter_open = RNA_float_get(op->ptr, "sh_open"), + .shutter_close = RNA_float_get(op->ptr, "sh_close"), + + .selected_only = RNA_boolean_get(op->ptr, "selected"), + .uvs = RNA_boolean_get(op->ptr, "uvs"), + .normals = RNA_boolean_get(op->ptr, "normals"), + .vcolors = RNA_boolean_get(op->ptr, "vcolors"), + .apply_subdiv = RNA_boolean_get(op->ptr, "apply_subdiv"), + .flatten_hierarchy = RNA_boolean_get(op->ptr, "flatten"), + .visible_layers_only = RNA_boolean_get(op->ptr, "visible_layers_only"), + .renderable_only = RNA_boolean_get(op->ptr, "renderable_only"), + .face_sets = RNA_boolean_get(op->ptr, "face_sets"), + .use_subdiv_schema = RNA_boolean_get(op->ptr, "subdiv_schema"), + .compression_type = RNA_enum_get(op->ptr, "compression_type"), + .packuv = RNA_boolean_get(op->ptr, "packuv"), + + .global_scale = RNA_float_get(op->ptr, "global_scale"), + }; + + ABC_export(CTX_data_scene(C), C, filename, ¶ms); + + return OPERATOR_FINISHED; +} + +static void ui_alembic_export_settings(uiLayout *layout, PointerRNA *imfptr) +{ + uiLayout *box; + uiLayout *row; + +#ifdef WITH_ALEMBIC_HDF5 + box = uiLayoutBox(layout); + row = uiLayoutRow(box, false); + uiItemL(row, IFACE_("Archive Options:"), ICON_NONE); + + row = uiLayoutRow(box, false); + uiItemR(row, imfptr, "compression_type", 0, NULL, ICON_NONE); +#endif + + box = uiLayoutBox(layout); + row = uiLayoutRow(box, false); + uiItemL(row, IFACE_("Manual Transform:"), ICON_NONE); + + row = uiLayoutRow(box, false); + uiItemR(row, imfptr, "global_scale", 0, NULL, ICON_NONE); + + /* Scene Options */ + box = uiLayoutBox(layout); + row = uiLayoutRow(box, false); + uiItemL(row, IFACE_("Scene Options:"), ICON_SCENE_DATA); + + row = uiLayoutRow(box, false); + uiItemR(row, imfptr, "start", 0, NULL, ICON_NONE); + + row = uiLayoutRow(box, false); + uiItemR(row, imfptr, "end", 0, NULL, ICON_NONE); + + row = uiLayoutRow(box, false); + uiItemR(row, imfptr, "xsamples", 0, NULL, ICON_NONE); + + row = uiLayoutRow(box, false); + uiItemR(row, imfptr, "gsamples", 0, NULL, ICON_NONE); + + row = uiLayoutRow(box, false); + uiItemR(row, imfptr, "sh_open", 0, NULL, ICON_NONE); + + row = uiLayoutRow(box, false); + uiItemR(row, imfptr, "sh_close", 0, NULL, ICON_NONE); + + row = uiLayoutRow(box, false); + uiItemR(row, imfptr, "selected", 0, NULL, ICON_NONE); + + row = uiLayoutRow(box, false); + uiItemR(row, imfptr, "renderable_only", 0, NULL, ICON_NONE); + + row = uiLayoutRow(box, false); + uiItemR(row, imfptr, "visible_layers_only", 0, NULL, ICON_NONE); + + row = uiLayoutRow(box, false); + uiItemR(row, imfptr, "flatten", 0, NULL, ICON_NONE); + + /* Object Data */ + box = uiLayoutBox(layout); + row = uiLayoutRow(box, false); + uiItemL(row, IFACE_("Object Options:"), ICON_OBJECT_DATA); + + row = uiLayoutRow(box, false); + uiItemR(row, imfptr, "uvs", 0, NULL, ICON_NONE); + + row = uiLayoutRow(box, false); + uiItemR(row, imfptr, "packuv", 0, NULL, ICON_NONE); + uiLayoutSetEnabled(row, RNA_boolean_get(imfptr, "uvs")); + + row = uiLayoutRow(box, false); + uiItemR(row, imfptr, "normals", 0, NULL, ICON_NONE); + + row = uiLayoutRow(box, false); + uiItemR(row, imfptr, "vcolors", 0, NULL, ICON_NONE); + + row = uiLayoutRow(box, false); + uiItemR(row, imfptr, "face_sets", 0, NULL, ICON_NONE); + + row = uiLayoutRow(box, false); + uiItemR(row, imfptr, "subdiv_schema", 0, NULL, ICON_NONE); + + row = uiLayoutRow(box, false); + uiItemR(row, imfptr, "apply_subdiv", 0, NULL, ICON_NONE); +} + +static void wm_alembic_export_draw(bContext *UNUSED(C), wmOperator *op) +{ + PointerRNA ptr; + + RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr); + ui_alembic_export_settings(op->layout, &ptr); +} + +static bool wm_alembic_export_check(bContext *UNUSED(C), wmOperator *op) +{ + char filepath[FILE_MAX]; + RNA_string_get(op->ptr, "filepath", filepath); + + if (!BLI_testextensie(filepath, ".abc")) { + BLI_ensure_extension(filepath, FILE_MAX, ".abc"); + RNA_string_set(op->ptr, "filepath", filepath); + return true; + } + + return false; +} + +void WM_OT_alembic_export(wmOperatorType *ot) +{ + ot->name = "Export Alembic"; + ot->description = "Export current scene in an Alembic archive"; + ot->idname = "WM_OT_alembic_export"; + + ot->invoke = wm_alembic_export_invoke; + ot->exec = wm_alembic_export_exec; + ot->poll = WM_operator_winactive; + ot->ui = wm_alembic_export_draw; + ot->check = wm_alembic_export_check; + + WM_operator_properties_filesel(ot, FILE_TYPE_FOLDER | FILE_TYPE_ALEMBIC, + FILE_BLENDER, FILE_SAVE, WM_FILESEL_FILEPATH, + FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); + + RNA_def_int(ot->srna, "start", 1, INT_MIN, INT_MAX, + "Start Frame", "Start Frame", INT_MIN, INT_MAX); + + RNA_def_int(ot->srna, "end", 1, INT_MIN, INT_MAX, + "End Frame", "End Frame", INT_MIN, INT_MAX); + + RNA_def_int(ot->srna, "xsamples", 1, 1, 128, + "Transform Samples", "Number of times per frame transformations are sampled", 1, 128); + + RNA_def_int(ot->srna, "gsamples", 1, 1, 128, + "Geometry Samples", "Number of times per frame object data are sampled", 1, 128); + + RNA_def_float(ot->srna, "sh_open", 0.0f, -1.0f, 1.0f, + "Shutter Open", "Time at which the shutter is open", -1.0f, 1.0f); + + RNA_def_float(ot->srna, "sh_close", 1.0f, -1.0f, 1.0f, + "Shutter Close", "Time at which the shutter is closed", -1.0f, 1.0f); + + RNA_def_boolean(ot->srna, "selected", 0, + "Selected Objects Only", "Export only selected objects"); + + RNA_def_boolean(ot->srna, "renderable_only", 1, + "Renderable Objects Only", + "Export only objects marked renderable in the outliner"); + + RNA_def_boolean(ot->srna, "visible_layers_only", 0, + "Visible Layers Only", "Export only objects in visible layers"); + + RNA_def_boolean(ot->srna, "flatten", 0, + "Flatten Hierarchy", + "Do not preserve objects' parent/children relationship"); + + RNA_def_boolean(ot->srna, "uvs", 1, "UVs", "Export UVs"); + + RNA_def_boolean(ot->srna, "packuv", 1, "Pack UV Islands", + "Export UVs with packed island"); + + RNA_def_boolean(ot->srna, "normals", 1, "Normals", "Export normals"); + + RNA_def_boolean(ot->srna, "vcolors", 0, "Vertex Colors", "Export vertex colors"); + + RNA_def_boolean(ot->srna, "face_sets", 0, "Face Sets", "Export per face shading group assignments"); + + RNA_def_boolean(ot->srna, "subdiv_schema", 0, + "Use Subdivision Schema", + "Export meshes using Alembic's subdivision schema"); + + RNA_def_boolean(ot->srna, "apply_subdiv", 0, + "Apply Subsurf", "Export subdivision surfaces as meshes"); + + RNA_def_enum(ot->srna, "compression_type", rna_enum_abc_compression_items, + ABC_ARCHIVE_OGAWA, "Compression", ""); + + RNA_def_float(ot->srna, "global_scale", 1.0f, 0.0001f, 1000.0f, "Scale", + "Value by which to enlarge or shrink the objects with respect to the world's origin", + 0.0001f, 1000.0f); +} + +/* ************************************************************************** */ + +/* TODO(kevin): check on de-duplicating all this with code in image_ops.c */ + +typedef struct CacheFrame { + struct CacheFrame *next, *prev; + int framenr; +} CacheFrame; + +static int cmp_frame(const void *a, const void *b) +{ + const CacheFrame *frame_a = a; + const CacheFrame *frame_b = b; + + if (frame_a->framenr < frame_b->framenr) return -1; + if (frame_a->framenr > frame_b->framenr) return 1; + return 0; +} + +static int get_sequence_len(char *filename, int *ofs) +{ + int frame; + int numdigit; + + if (!BLI_path_frame_get(filename, &frame, &numdigit)) { + return 1; + } + + char path[FILE_MAX]; + BLI_split_dir_part(filename, path, FILE_MAX); + + DIR *dir = opendir(path); + + const char *ext = ".abc"; + const char *basename = BLI_path_basename(filename); + const int len = strlen(basename) - (numdigit + strlen(ext)); + + ListBase frames; + BLI_listbase_clear(&frames); + + struct dirent *fname; + while ((fname = readdir(dir)) != NULL) { + /* do we have the right extension? */ + if (!strstr(fname->d_name, ext)) { + continue; + } + + if (!STREQLEN(basename, fname->d_name, len)) { + continue; + } + + CacheFrame *cache_frame = MEM_callocN(sizeof(CacheFrame), "abc_frame"); + + BLI_path_frame_get(fname->d_name, &cache_frame->framenr, &numdigit); + + BLI_addtail(&frames, cache_frame); + } + + closedir(dir); + + BLI_listbase_sort(&frames, cmp_frame); + + CacheFrame *cache_frame = frames.first; + + if (cache_frame) { + int frame_curr = cache_frame->framenr; + (*ofs) = frame_curr; + + while (cache_frame && (cache_frame->framenr == frame_curr)) { + ++frame_curr; + cache_frame = cache_frame->next; + } + + BLI_freelistN(&frames); + + return frame_curr - (*ofs); + } + + return 1; +} + +/* ************************************************************************** */ + +static void ui_alembic_import_settings(uiLayout *layout, PointerRNA *imfptr) +{ + uiLayout *box = uiLayoutBox(layout); + uiLayout *row = uiLayoutRow(box, false); + uiItemL(row, IFACE_("Manual Transform:"), ICON_NONE); + + row = uiLayoutRow(box, false); + uiItemR(row, imfptr, "scale", 0, NULL, ICON_NONE); + + box = uiLayoutBox(layout); + row = uiLayoutRow(box, false); + uiItemL(row, IFACE_("Options:"), ICON_NONE); + + row = uiLayoutRow(box, false); + uiItemR(row, imfptr, "set_frame_range", 0, NULL, ICON_NONE); + + row = uiLayoutRow(box, false); + uiItemR(row, imfptr, "is_sequence", 0, NULL, ICON_NONE); + + row = uiLayoutRow(box, false); + uiItemR(row, imfptr, "validate_meshes", 0, NULL, ICON_NONE); +} + +static void wm_alembic_import_draw(bContext *UNUSED(C), wmOperator *op) +{ + PointerRNA ptr; + + RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr); + ui_alembic_import_settings(op->layout, &ptr); +} + +static int wm_alembic_import_exec(bContext *C, wmOperator *op) +{ + if (!RNA_struct_property_is_set(op->ptr, "filepath")) { + BKE_report(op->reports, RPT_ERROR, "No filename given"); + return OPERATOR_CANCELLED; + } + + char filename[FILE_MAX]; + RNA_string_get(op->ptr, "filepath", filename); + + const float scale = RNA_float_get(op->ptr, "scale"); + const bool is_sequence = RNA_boolean_get(op->ptr, "is_sequence"); + const bool set_frame_range = RNA_boolean_get(op->ptr, "set_frame_range"); + const bool validate_meshes = RNA_boolean_get(op->ptr, "validate_meshes"); + + int offset = 0; + int sequence_len = 1; + + if (is_sequence) { + sequence_len = get_sequence_len(filename, &offset); + } + + ABC_import(C, filename, scale, is_sequence, set_frame_range, sequence_len, offset, validate_meshes); + + return OPERATOR_FINISHED; +} + +void WM_OT_alembic_import(wmOperatorType *ot) +{ + ot->name = "Import Alembic"; + ot->description = "Load an Alembic archive"; + ot->idname = "WM_OT_alembic_import"; + + ot->invoke = WM_operator_filesel; + ot->exec = wm_alembic_import_exec; + ot->poll = WM_operator_winactive; + ot->ui = wm_alembic_import_draw; + + WM_operator_properties_filesel(ot, FILE_TYPE_FOLDER | FILE_TYPE_ALEMBIC, + FILE_BLENDER, FILE_SAVE, WM_FILESEL_FILEPATH, + FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); + + RNA_def_float(ot->srna, "scale", 1.0f, 0.0001f, 1000.0f, "Scale", + "Value by which to enlarge or shrink the objects with respect to the world's origin", + 0.0001f, 1000.0f); + + RNA_def_boolean(ot->srna, "set_frame_range", true, + "Set Frame Range", + "If checked, update scene's start and end frame to match those of the Alembic archive"); + + RNA_def_boolean(ot->srna, "validate_meshes", 0, + "Validate Meshes", "Check imported mesh objects for invalid data (slow)"); + + RNA_def_boolean(ot->srna, "is_sequence", false, "Is Sequence", + "Set to true if the cache is split into separate files"); +} + +#endif diff --git a/source/blender/editors/io/io_alembic.h b/source/blender/editors/io/io_alembic.h new file mode 100644 index 00000000000..5eefabef4be --- /dev/null +++ b/source/blender/editors/io/io_alembic.h @@ -0,0 +1,37 @@ +/* + * ***** 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) 2016 Blender Foundation. + * All rights reserved. + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +#ifndef __IO_ALEMBIC_H__ +#define __IO_ALEMBIC_H__ + +/** \file blender/editors/io/io_alembic.h + * \ingroup editor/io + */ + +struct wmOperatorType; + +void WM_OT_alembic_export(struct wmOperatorType *ot); +void WM_OT_alembic_import(struct wmOperatorType *ot); + +#endif /* __IO_ALEMBIC_H__ */ diff --git a/source/blender/editors/io/io_cache.c b/source/blender/editors/io/io_cache.c new file mode 100644 index 00000000000..c5eea94f5e1 --- /dev/null +++ b/source/blender/editors/io/io_cache.c @@ -0,0 +1,163 @@ +/* + * ***** 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) 2016 Blender Foundation. + * All rights reserved. + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_cachefile_types.h" +#include "DNA_space_types.h" + +#include "BLI_listbase.h" +#include "BLI_path_util.h" +#include "BLI_string.h" + +#include "BKE_cachefile.h" +#include "BKE_context.h" +#include "BKE_global.h" +#include "BKE_library.h" +#include "BKE_main.h" +#include "BKE_report.h" + +#include "RNA_access.h" + +#include "UI_interface.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "io_cache.h" + +static void cachefile_init(bContext *C, wmOperator *op) +{ + PropertyPointerRNA *pprop; + + op->customdata = pprop = MEM_callocN(sizeof(PropertyPointerRNA), "OpenPropertyPointerRNA"); + UI_context_active_but_prop_get_templateID(C, &pprop->ptr, &pprop->prop); +} + +static int cachefile_open_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + if (!RNA_struct_property_is_set(op->ptr, "filepath")) { + char filepath[FILE_MAX]; + BLI_strncpy(filepath, G.main->name, sizeof(filepath)); + BLI_replace_extension(filepath, sizeof(filepath), ".abc"); + RNA_string_set(op->ptr, "filepath", filepath); + } + + cachefile_init(C, op); + + WM_event_add_fileselect(C, op); + + return OPERATOR_RUNNING_MODAL; + + UNUSED_VARS(event); +} + +static void open_cancel(bContext *UNUSED(C), wmOperator *op) +{ + MEM_freeN(op->customdata); + op->customdata = NULL; +} + +static int cachefile_open_exec(bContext *C, wmOperator *op) +{ + if (!RNA_struct_property_is_set(op->ptr, "filepath")) { + BKE_report(op->reports, RPT_ERROR, "No filename given"); + return OPERATOR_CANCELLED; + } + + char filename[FILE_MAX]; + RNA_string_get(op->ptr, "filepath", filename); + + Main *bmain = CTX_data_main(C); + + CacheFile *cache_file = BKE_libblock_alloc(bmain, ID_CF, BLI_path_basename(filename)); + BLI_strncpy(cache_file->filepath, filename, FILE_MAX); + BKE_cachefile_reload(bmain, cache_file); + + /* hook into UI */ + PropertyPointerRNA *pprop = op->customdata; + + if (pprop->prop) { + /* when creating new ID blocks, use is already 1, but RNA + * pointer se also increases user, so this compensates it */ + id_us_min(&cache_file->id); + + PointerRNA idptr; + RNA_id_pointer_create(&cache_file->id, &idptr); + RNA_property_pointer_set(&pprop->ptr, pprop->prop, idptr); + RNA_property_update(C, &pprop->ptr, pprop->prop); + } + + MEM_freeN(op->customdata); + + return OPERATOR_FINISHED; +} + +void CACHEFILE_OT_open(wmOperatorType *ot) +{ + ot->name = "Open Cache File"; + ot->description = "Load a cache file"; + ot->idname = "CACHEFILE_OT_open"; + + ot->invoke = cachefile_open_invoke; + ot->exec = cachefile_open_exec; + ot->cancel = open_cancel; + + WM_operator_properties_filesel(ot, FILE_TYPE_ALEMBIC | FILE_TYPE_FOLDER, + FILE_BLENDER, FILE_SAVE, WM_FILESEL_FILEPATH, + FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); +} + +/* ***************************** Reload Operator **************************** */ + +static int cachefile_reload_exec(bContext *C, wmOperator *op) +{ + CacheFile *cache_file = CTX_data_edit_cachefile(C); + + if (!cache_file) { + return OPERATOR_CANCELLED; + } + + Main *bmain = CTX_data_main(C); + + BLI_listbase_clear(&cache_file->object_paths); + BKE_cachefile_reload(bmain, cache_file); + + return OPERATOR_FINISHED; + + UNUSED_VARS(op); +} + +void CACHEFILE_OT_reload(wmOperatorType *ot) +{ + ot->name = "Refresh Archive"; + ot->description = "Update objects paths list with new data from the archive"; + ot->idname = "CACHEFILE_OT_reload"; + + /* api callbacks */ + ot->exec = cachefile_reload_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} diff --git a/source/blender/editors/io/io_cache.h b/source/blender/editors/io/io_cache.h new file mode 100644 index 00000000000..ea270c2aba1 --- /dev/null +++ b/source/blender/editors/io/io_cache.h @@ -0,0 +1,37 @@ +/* + * ***** 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) 2016 Blender Foundation. + * All rights reserved. + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +#ifndef __IO_CACHE_H__ +#define __IO_CACHE_H__ + +/** \file blender/editors/io/io_cache.h + * \ingroup editor/io + */ + +struct wmOperatorType; + +void CACHEFILE_OT_open(struct wmOperatorType *ot); +void CACHEFILE_OT_reload(struct wmOperatorType *ot); + +#endif /* __IO_CACHE_H__ */ diff --git a/source/blender/editors/io/io_collada.c b/source/blender/editors/io/io_collada.c index d62651cef81..8659100df87 100644 --- a/source/blender/editors/io/io_collada.c +++ b/source/blender/editors/io/io_collada.c @@ -280,6 +280,20 @@ static void wm_collada_export_draw(bContext *UNUSED(C), wmOperator *op) uiCollada_exportSettings(op->layout, &ptr); } +static bool wm_collada_export_check(bContext *UNUSED(C), wmOperator *op) +{ + char filepath[FILE_MAX]; + RNA_string_get(op->ptr, "filepath", filepath); + + if (!BLI_testextensie(filepath, ".dae")) { + BLI_ensure_extension(filepath, FILE_MAX, ".dae"); + RNA_string_set(op->ptr, "filepath", filepath); + return true; + } + + return false; +} + void WM_OT_collada_export(wmOperatorType *ot) { static EnumPropertyItem prop_bc_export_mesh_type[] = { @@ -302,6 +316,7 @@ void WM_OT_collada_export(wmOperatorType *ot) ot->invoke = wm_collada_export_invoke; ot->exec = wm_collada_export_exec; ot->poll = WM_operator_winactive; + ot->check = wm_collada_export_check; ot->flag |= OPTYPE_PRESET; diff --git a/source/blender/editors/io/io_ops.c b/source/blender/editors/io/io_ops.c index a70a51a60be..d1e933517a9 100644 --- a/source/blender/editors/io/io_ops.c +++ b/source/blender/editors/io/io_ops.c @@ -30,11 +30,18 @@ #include "io_ops.h" /* own include */ +#include "WM_api.h" + #ifdef WITH_COLLADA # include "io_collada.h" -# include "WM_api.h" #endif +#ifdef WITH_ALEMBIC +# include "io_alembic.h" +#endif + +#include "io_cache.h" + void ED_operatortypes_io(void) { #ifdef WITH_COLLADA @@ -42,4 +49,11 @@ void ED_operatortypes_io(void) WM_operatortype_append(WM_OT_collada_export); WM_operatortype_append(WM_OT_collada_import); #endif +#ifdef WITH_ALEMBIC + WM_operatortype_append(WM_OT_alembic_import); + WM_operatortype_append(WM_OT_alembic_export); +#endif + + WM_operatortype_append(CACHEFILE_OT_open); + WM_operatortype_append(CACHEFILE_OT_reload); } diff --git a/source/blender/editors/mesh/editmesh_bevel.c b/source/blender/editors/mesh/editmesh_bevel.c index 302ca407add..a81add7a86e 100644 --- a/source/blender/editors/mesh/editmesh_bevel.c +++ b/source/blender/editors/mesh/editmesh_bevel.c @@ -145,6 +145,7 @@ static bool edbm_bevel_init(bContext *C, wmOperator *op, const bool is_modal) opdata->em = em; opdata->is_modal = is_modal; opdata->value_mode = OFFSET_VALUE; + opdata->segments = (float) RNA_int_get(op->ptr, "segments"); pixels_per_inch = U.dpi * U.pixelsize; for (i = 0; i < NUM_VALUE_KINDS; i++) { diff --git a/source/blender/editors/mesh/editmesh_intersect.c b/source/blender/editors/mesh/editmesh_intersect.c index 281a8b2a02d..de93211bec4 100644 --- a/source/blender/editors/mesh/editmesh_intersect.c +++ b/source/blender/editors/mesh/editmesh_intersect.c @@ -439,7 +439,7 @@ static void bm_face_split_by_edges_island_connect( LinkNode *e_link, const int e_link_len, MemArena *mem_arena_edgenet) { - BMEdge **edge_arr = BLI_memarena_alloc(mem_arena_edgenet, sizeof(BMEdge **) * e_link_len); + BMEdge **edge_arr = BLI_memarena_alloc(mem_arena_edgenet, sizeof(*edge_arr) * e_link_len); int edge_arr_len = 0; while (e_link) { diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index 1a14fad8650..7e31deba2c7 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -3080,7 +3080,7 @@ static void bm_mesh_hflag_flush_vert(BMesh *bm, const char hflag) * \note This could be used for split-by-material for non mesh types. * \note This could take material data from another object or args. */ -static void mesh_separate_material_assign_mat_nr(Object *ob, const short mat_nr) +static void mesh_separate_material_assign_mat_nr(Main *bmain, Object *ob, const short mat_nr) { ID *obdata = ob->data; @@ -3116,18 +3116,20 @@ static void mesh_separate_material_assign_mat_nr(Object *ob, const short mat_nr) ma_obdata = NULL; } - BKE_material_clear_id(obdata, true); - BKE_material_resize_object(ob, 1, true); - BKE_material_resize_id(obdata, 1, true); + BKE_material_clear_id(bmain, obdata, true); + BKE_material_resize_object(bmain, ob, 1, true); + BKE_material_resize_id(bmain, obdata, 1, true); ob->mat[0] = ma_ob; + id_us_plus((ID *)ma_ob); ob->matbits[0] = matbit; (*matarar)[0] = ma_obdata; + id_us_plus((ID *)ma_obdata); } else { - BKE_material_clear_id(obdata, true); - BKE_material_resize_object(ob, 0, true); - BKE_material_resize_id(obdata, 0, true); + BKE_material_clear_id(bmain, obdata, true); + BKE_material_resize_object(bmain, ob, 0, true); + BKE_material_resize_id(bmain, obdata, 0, true); } } @@ -3162,7 +3164,7 @@ static bool mesh_separate_material(Main *bmain, Scene *scene, Base *base_old, BM /* leave the current object with some materials */ if (tot == bm_old->totface) { - mesh_separate_material_assign_mat_nr(base_old->object, mat_nr); + mesh_separate_material_assign_mat_nr(bmain, base_old->object, mat_nr); /* since we're in editmode, must set faces here */ BM_ITER_MESH (f, &iter, bm_old, BM_FACES_OF_MESH) { @@ -3174,7 +3176,7 @@ static bool mesh_separate_material(Main *bmain, Scene *scene, Base *base_old, BM /* Move selection into a separate object */ base_new = mesh_separate_tagged(bmain, scene, base_old, bm_old); if (base_new) { - mesh_separate_material_assign_mat_nr(base_new->object, mat_nr); + mesh_separate_material_assign_mat_nr(bmain, base_new->object, mat_nr); } result |= (base_new != NULL); diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index 57e08d36f85..0cb91fd0269 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -999,7 +999,7 @@ static int group_instance_add_exec(bContext *C, wmOperator *op) Object *ob = ED_object_add_type(C, OB_EMPTY, group->id.name + 2, loc, rot, false, layer); ob->dup_group = group; ob->transflag |= OB_DUPLIGROUP; - id_lib_extern(&group->id); + id_us_plus(&group->id); /* works without this except if you try render right after, see: 22027 */ DAG_relations_tag_update(bmain); diff --git a/source/blender/editors/object/object_constraint.c b/source/blender/editors/object/object_constraint.c index db8a4c1960f..59d78f13ccb 100644 --- a/source/blender/editors/object/object_constraint.c +++ b/source/blender/editors/object/object_constraint.c @@ -416,6 +416,13 @@ static void test_constraint(Object *owner, bPoseChannel *pchan, bConstraint *con if ((data->flag & CAMERASOLVER_ACTIVECLIP) == 0 && (data->clip == NULL)) con->flag |= CONSTRAINT_DISABLE; } + else if (con->type == CONSTRAINT_TYPE_TRANSFORM_CACHE) { + bTransformCacheConstraint *data = con->data; + + if ((data->cache_file == NULL) || (data->object_path[0] == '\0')) { + con->flag |= CONSTRAINT_DISABLE; + } + } /* Check targets for constraints */ if (check_targets && cti && cti->get_constraint_targets) { diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c index 3dc7d8ebd4b..111afcdc7a7 100644 --- a/source/blender/editors/object/object_edit.c +++ b/source/blender/editors/object/object_edit.c @@ -910,7 +910,7 @@ static void copy_attr(Main *bmain, Scene *scene, View3D *v3d, short event) base->object->dup_group = ob->dup_group; if (ob->dup_group) - id_lib_extern(&ob->dup_group->id); + id_us_plus(&ob->dup_group->id); } else if (event == 7) { /* mass */ base->object->mass = ob->mass; diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index ff2accf9d82..82c6a14eb7f 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -1382,7 +1382,7 @@ static int move_to_layer_exec(bContext *C, wmOperator *op) /* upper byte is used for local view */ local = base->lay & 0xFF000000; base->lay = lay + local; - base->object->lay = lay; + base->object->lay = base->lay; /* if (base->object->type == OB_LAMP) is_lamp = true; */ } CTX_DATA_END; @@ -1609,7 +1609,7 @@ static int make_links_data_exec(bContext *C, wmOperator *op) case MAKE_LINKS_DUPLIGROUP: ob_dst->dup_group = ob_src->dup_group; if (ob_dst->dup_group) { - id_lib_extern(&ob_dst->dup_group->id); + id_us_plus(&ob_dst->dup_group->id); ob_dst->transflag |= OB_DUPLIGROUP; } break; @@ -1764,10 +1764,13 @@ static void single_object_users(Main *bmain, Scene *scene, View3D *v3d, const in /* copy already clears */ } /* remap gpencil parenting */ - bGPdata *gpd = scene->gpd; - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { - if (gpl->parent == ob) { - gpl->parent = obn; + + if (scene->gpd) { + bGPdata *gpd = scene->gpd; + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + if (gpl->parent == ob) { + gpl->parent = obn; + } } } @@ -2103,6 +2106,7 @@ void ED_object_single_users(Main *bmain, Scene *scene, const bool full, const bo } BKE_main_id_clear_newpoins(bmain); + DAG_relations_tag_update(bmain); } /******************************* Make Local ***********************************/ diff --git a/source/blender/editors/render/render_preview.c b/source/blender/editors/render/render_preview.c index b4f3426677a..ddbf59b2cf7 100644 --- a/source/blender/editors/render/render_preview.c +++ b/source/blender/editors/render/render_preview.c @@ -526,7 +526,7 @@ static Scene *preview_prepare_scene(Main *bmain, Scene *scene, ID *id, int id_ty BKE_node_preview_init_tree(origwrld->nodetree, sp->sizex, sp->sizey, true); } } - + return sce; } @@ -863,8 +863,6 @@ static void shader_preview_free(void *customdata) /* get rid of copied world */ BLI_remlink(&pr_main->world, sp->worldcopy); - /* T32865 - we need to unlink the texture copies, unlike for materials */ - BKE_libblock_relink_ex(pr_main, sp->worldcopy, NULL, NULL, true); BKE_world_free(sp->worldcopy); properties = IDP_GetProperties((ID *)sp->worldcopy, false); @@ -881,7 +879,6 @@ static void shader_preview_free(void *customdata) /* get rid of copied lamp */ BLI_remlink(&pr_main->lamp, sp->lampcopy); - BKE_libblock_relink_ex(pr_main, sp->lampcopy, NULL, NULL, true); BKE_lamp_free(sp->lampcopy); properties = IDP_GetProperties((ID *)sp->lampcopy, false); diff --git a/source/blender/editors/screen/screen_context.c b/source/blender/editors/screen/screen_context.c index 22d95d77d55..34c51914027 100644 --- a/source/blender/editors/screen/screen_context.c +++ b/source/blender/editors/screen/screen_context.c @@ -117,7 +117,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult } else if (CTX_data_equals(member, "visible_objects") || CTX_data_equals(member, "visible_bases")) { const unsigned int lay = context_layers(sc, scene, sa); - int visible_objects = CTX_data_equals(member, "visible_objects"); + const bool visible_objects = CTX_data_equals(member, "visible_objects"); for (base = scene->base.first; base; base = base->next) { if (((base->object->restrictflag & OB_RESTRICT_VIEW) == 0) && (base->lay & lay)) { @@ -132,7 +132,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult } else if (CTX_data_equals(member, "selectable_objects") || CTX_data_equals(member, "selectable_bases")) { const unsigned int lay = context_layers(sc, scene, sa); - int selectable_objects = CTX_data_equals(member, "selectable_objects"); + const bool selectable_objects = CTX_data_equals(member, "selectable_objects"); for (base = scene->base.first; base; base = base->next) { if (base->lay & lay) { @@ -149,7 +149,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult } else if (CTX_data_equals(member, "selected_objects") || CTX_data_equals(member, "selected_bases")) { const unsigned int lay = context_layers(sc, scene, sa); - int selected_objects = CTX_data_equals(member, "selected_objects"); + const bool selected_objects = CTX_data_equals(member, "selected_objects"); for (base = scene->base.first; base; base = base->next) { if ((base->flag & SELECT) && (base->lay & lay)) { @@ -164,7 +164,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult } else if (CTX_data_equals(member, "selected_editable_objects") || CTX_data_equals(member, "selected_editable_bases")) { const unsigned int lay = context_layers(sc, scene, sa); - int selected_editable_objects = CTX_data_equals(member, "selected_editable_objects"); + const bool selected_editable_objects = CTX_data_equals(member, "selected_editable_objects"); for (base = scene->base.first; base; base = base->next) { if ((base->flag & SELECT) && (base->lay & lay)) { @@ -183,7 +183,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult } else if (CTX_data_equals(member, "editable_objects") || CTX_data_equals(member, "editable_bases")) { const unsigned int lay = context_layers(sc, scene, sa); - int editable_objects = CTX_data_equals(member, "editable_objects"); + const bool editable_objects = CTX_data_equals(member, "editable_objects"); /* Visible + Editable, but not necessarily selected */ for (base = scene->base.first; base; base = base->next) { @@ -202,7 +202,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult else if (CTX_data_equals(member, "visible_bones") || CTX_data_equals(member, "editable_bones")) { bArmature *arm = (obedit && obedit->type == OB_ARMATURE) ? obedit->data : NULL; EditBone *ebone, *flipbone = NULL; - int editable_bones = CTX_data_equals(member, "editable_bones"); + const bool editable_bones = CTX_data_equals(member, "editable_bones"); if (arm && arm->edbo) { /* Attention: X-Axis Mirroring is also handled here... */ @@ -244,7 +244,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult else if (CTX_data_equals(member, "selected_bones") || CTX_data_equals(member, "selected_editable_bones")) { bArmature *arm = (obedit && obedit->type == OB_ARMATURE) ? obedit->data : NULL; EditBone *ebone, *flipbone = NULL; - int selected_editable_bones = CTX_data_equals(member, "selected_editable_bones"); + const bool selected_editable_bones = CTX_data_equals(member, "selected_editable_bones"); if (arm && arm->edbo) { /* Attention: X-Axis Mirroring is also handled here... */ @@ -467,7 +467,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult bGPdata *gpd = ED_gpencil_data_get_active_direct((ID *)sc, scene, sa, obact); if (gpd) { - bGPDlayer *gpl = gpencil_layer_getactive(gpd); + bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); if (gpl) { CTX_data_pointer_set(result, &gpd->id, &RNA_GPencilLayer, gpl); @@ -480,7 +480,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult bGPdata *gpd = ED_gpencil_data_get_active_direct((ID *)sc, scene, sa, obact); if (gpd) { - bGPDpalette *palette = gpencil_palette_getactive(gpd); + bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); if (palette) { CTX_data_pointer_set(result, &gpd->id, &RNA_GPencilPalette, palette); @@ -493,10 +493,10 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult bGPdata *gpd = ED_gpencil_data_get_active_direct((ID *)sc, scene, sa, obact); if (gpd) { - bGPDpalette *palette = gpencil_palette_getactive(gpd); + bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); if (palette) { - bGPDpalettecolor *palcolor = gpencil_palettecolor_getactive(palette); + bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette); if (palcolor) { CTX_data_pointer_set(result, &gpd->id, &RNA_GPencilPaletteColor, palcolor); return 1; @@ -506,7 +506,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult } else if (CTX_data_equals(member, "active_gpencil_brush")) { /* XXX: see comment for gpencil_data case... */ - bGPDbrush *brush = gpencil_brush_getactive(scene->toolsettings); + bGPDbrush *brush = BKE_gpencil_brush_getactive(scene->toolsettings); if (brush) { CTX_data_pointer_set(result, NULL, &RNA_GPencilBrush, brush); @@ -518,7 +518,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult bGPdata *gpd = ED_gpencil_data_get_active_direct((ID *)sc, scene, sa, obact); if (gpd) { - bGPDlayer *gpl = gpencil_layer_getactive(gpd); + bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); if (gpl) { CTX_data_pointer_set(result, &gpd->id, &RNA_GPencilLayer, gpl->actframe); diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c index 1431958501d..05270dbfa09 100644 --- a/source/blender/editors/sculpt_paint/paint_stroke.c +++ b/source/blender/editors/sculpt_paint/paint_stroke.c @@ -88,7 +88,6 @@ typedef struct PaintStroke { /* Cached values */ ViewContext vc; - bglMats mats; Brush *brush; UnifiedPaintSettings *ups; @@ -675,8 +674,6 @@ PaintStroke *paint_stroke_new(bContext *C, float zoomx, zoomy; view3d_set_viewcontext(C, &stroke->vc); - if (stroke->vc.v3d) - view3d_get_transformation(stroke->vc.ar, stroke->vc.rv3d, stroke->vc.obact, &stroke->mats); stroke->get_location = get_location; stroke->test_start = test_start; @@ -1102,12 +1099,14 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event) paint_stroke_add_sample(p, stroke, event->mval[0], event->mval[1], pressure); paint_stroke_sample_average(stroke, &sample_average); +#ifdef WITH_INPUT_NDOF /* let NDOF motion pass through to the 3D view so we can paint and rotate simultaneously! * this isn't perfect... even when an extra MOUSEMOVE is spoofed, the stroke discards it * since the 2D deltas are zero -- code in this file needs to be updated to use the * post-NDOF_MOTION MOUSEMOVE */ if (event->type == NDOF_MOTION) return OPERATOR_PASS_THROUGH; +#endif /* one time initialization */ if (!stroke->stroke_init) { diff --git a/source/blender/editors/sound/sound_ops.c b/source/blender/editors/sound/sound_ops.c index 03f2e146b7d..ac3fc769ea1 100644 --- a/source/blender/editors/sound/sound_ops.c +++ b/source/blender/editors/sound/sound_ops.c @@ -348,10 +348,10 @@ static int sound_mixdown_exec(bContext *C, wmOperator *op) BLI_path_abs(filename, bmain->name); if (split) - result = AUD_mixdown_per_channel(scene->sound_scene, SFRA * specs.rate / FPS, (EFRA - SFRA) * specs.rate / FPS, + result = AUD_mixdown_per_channel(scene->sound_scene, SFRA * specs.rate / FPS, (EFRA - SFRA + 1) * specs.rate / FPS, accuracy, filename, specs, container, codec, bitrate); else - result = AUD_mixdown(scene->sound_scene, SFRA * specs.rate / FPS, (EFRA - SFRA) * specs.rate / FPS, + result = AUD_mixdown(scene->sound_scene, SFRA * specs.rate / FPS, (EFRA - SFRA + 1) * specs.rate / FPS, accuracy, filename, specs, container, codec, bitrate); if (result) { diff --git a/source/blender/editors/space_action/action_edit.c b/source/blender/editors/space_action/action_edit.c index 55b087c40e7..f8db35e2311 100644 --- a/source/blender/editors/space_action/action_edit.c +++ b/source/blender/editors/space_action/action_edit.c @@ -750,7 +750,7 @@ static void insert_gpencil_keys(bAnimContext *ac, short mode) /* insert gp frames */ for (ale = anim_data.first; ale; ale = ale->next) { bGPDlayer *gpl = (bGPDlayer *)ale->data; - gpencil_layer_getframe(gpl, CFRA, add_frame_mode); + BKE_gpencil_layer_getframe(gpl, CFRA, add_frame_mode); } ANIM_animdata_update(ac, &anim_data); diff --git a/source/blender/editors/space_action/action_ops.c b/source/blender/editors/space_action/action_ops.c index a261202b690..718a4fd3c38 100644 --- a/source/blender/editors/space_action/action_ops.c +++ b/source/blender/editors/space_action/action_ops.c @@ -240,7 +240,9 @@ static void action_keymap_keyframes(wmKeyConfig *keyconf, wmKeyMap *keymap) /* auto-set range */ WM_keymap_add_item(keymap, "ACTION_OT_previewrange_set", PKEY, KM_PRESS, KM_CTRL | KM_ALT, 0); WM_keymap_add_item(keymap, "ACTION_OT_view_all", HOMEKEY, KM_PRESS, 0, 0); +#ifdef WITH_INPUT_NDOF WM_keymap_add_item(keymap, "ACTION_OT_view_all", NDOF_BUTTON_FIT, KM_PRESS, 0, 0); +#endif WM_keymap_add_item(keymap, "ACTION_OT_view_selected", PADPERIOD, KM_PRESS, 0, 0); WM_keymap_add_item(keymap, "ACTION_OT_view_frame", PAD0, KM_PRESS, 0, 0); diff --git a/source/blender/editors/space_clip/clip_intern.h b/source/blender/editors/space_clip/clip_intern.h index 2a5d959bb84..14393c6968b 100644 --- a/source/blender/editors/space_clip/clip_intern.h +++ b/source/blender/editors/space_clip/clip_intern.h @@ -106,7 +106,9 @@ void CLIP_OT_change_frame(wmOperatorType *ot); void CLIP_OT_rebuild_proxy(struct wmOperatorType *ot); void CLIP_OT_mode_set(struct wmOperatorType *ot); +#ifdef WITH_INPUT_NDOF void CLIP_OT_view_ndof(struct wmOperatorType *ot); +#endif void CLIP_OT_prefetch(struct wmOperatorType *ot); @@ -185,7 +187,10 @@ void CLIP_OT_detect_features(struct wmOperatorType *ot); void CLIP_OT_stabilize_2d_add(struct wmOperatorType *ot); void CLIP_OT_stabilize_2d_remove(struct wmOperatorType *ot); void CLIP_OT_stabilize_2d_select(struct wmOperatorType *ot); -void CLIP_OT_stabilize_2d_set_rotation(struct wmOperatorType *ot); + +void CLIP_OT_stabilize_2d_rotation_add(struct wmOperatorType *ot); +void CLIP_OT_stabilize_2d_rotation_remove(struct wmOperatorType *ot); +void CLIP_OT_stabilize_2d_rotation_select(struct wmOperatorType *ot); void CLIP_OT_clean_tracks(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_clip/clip_ops.c b/source/blender/editors/space_clip/clip_ops.c index 83876ae2669..6778a0b1805 100644 --- a/source/blender/editors/space_clip/clip_ops.c +++ b/source/blender/editors/space_clip/clip_ops.c @@ -1405,6 +1405,7 @@ void CLIP_OT_mode_set(wmOperatorType *ot) RNA_def_enum(ot->srna, "mode", rna_enum_clip_editor_mode_items, SC_MODE_TRACKING, "Mode", ""); } +#ifdef WITH_INPUT_NDOF /********************** NDOF operator *********************/ /* Combined pan/zoom from a 3D mouse device. @@ -1451,6 +1452,7 @@ void CLIP_OT_view_ndof(wmOperatorType *ot) ot->invoke = clip_view_ndof_invoke; ot->poll = ED_space_clip_view_clip_poll; } +#endif /* WITH_INPUT_NDOF */ /********************** Prefetch operator *********************/ diff --git a/source/blender/editors/space_clip/clip_utils.c b/source/blender/editors/space_clip/clip_utils.c index 5964e9a898b..547c2fba66f 100644 --- a/source/blender/editors/space_clip/clip_utils.c +++ b/source/blender/editors/space_clip/clip_utils.c @@ -175,21 +175,14 @@ void clip_graph_tracking_iterate(SpaceClip *sc, bool selected_only, bool include void clip_delete_track(bContext *C, MovieClip *clip, MovieTrackingTrack *track) { MovieTracking *tracking = &clip->tracking; - MovieTrackingStabilization *stab = &tracking->stabilization; MovieTrackingTrack *act_track = BKE_tracking_track_get_active(tracking); ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking); - bool has_bundle = false, update_stab = false; + bool has_bundle = false; char track_name_escaped[MAX_NAME], prefix[MAX_NAME * 2]; if (track == act_track) tracking->act_track = NULL; - if (track == stab->rot_track) { - stab->rot_track = NULL; - - update_stab = true; - } - /* handle reconstruction display in 3d viewport */ if (track->flag & TRACK_HAS_BUNDLE) has_bundle = true; @@ -207,8 +200,7 @@ void clip_delete_track(bContext *C, MovieClip *clip, MovieTrackingTrack *track) WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip); - if (update_stab) { - tracking->stabilization.ok = false; + if (track->flag & (TRACK_USE_2D_STAB | TRACK_USE_2D_STAB_ROT)) { WM_event_add_notifier(C, NC_MOVIECLIP | ND_DISPLAY, clip); } diff --git a/source/blender/editors/space_clip/space_clip.c b/source/blender/editors/space_clip/space_clip.c index 396d71f0a20..05e69968e35 100644 --- a/source/blender/editors/space_clip/space_clip.c +++ b/source/blender/editors/space_clip/space_clip.c @@ -225,18 +225,6 @@ static void clip_scopes_check_gpencil_change(ScrArea *sa) } } -static void clip_stabilization_tag_refresh(ScrArea *sa) -{ - SpaceClip *sc = (SpaceClip *) sa->spacedata.first; - MovieClip *clip = ED_space_clip_get_clip(sc); - - if (clip) { - MovieTrackingStabilization *stab = &clip->tracking.stabilization; - - stab->ok = false; - } -} - /* ******************** default callbacks for clip space ***************** */ static SpaceLink *clip_new(const bContext *C) @@ -368,7 +356,6 @@ static void clip_listener(bScreen *UNUSED(sc), ScrArea *sa, wmNotifier *wmn) case NA_REMOVED: case NA_EDITED: case NA_EVALUATED: - clip_stabilization_tag_refresh(sa); /* fall-through */ case NA_SELECTED: @@ -412,7 +399,6 @@ static void clip_listener(bScreen *UNUSED(sc), ScrArea *sa, wmNotifier *wmn) case NC_SPACE: if (wmn->data == ND_SPACE_CLIP) { clip_scopes_tag_refresh(sa); - clip_stabilization_tag_refresh(sa); ED_area_tag_redraw(sa); } break; @@ -443,7 +429,9 @@ static void clip_operatortypes(void) WM_operatortype_append(CLIP_OT_change_frame); WM_operatortype_append(CLIP_OT_rebuild_proxy); WM_operatortype_append(CLIP_OT_mode_set); +#ifdef WITH_INPUT_NDOF WM_operatortype_append(CLIP_OT_view_ndof); +#endif WM_operatortype_append(CLIP_OT_prefetch); WM_operatortype_append(CLIP_OT_set_scene_frames); WM_operatortype_append(CLIP_OT_cursor_set); @@ -457,7 +445,7 @@ static void clip_operatortypes(void) /* navigation */ WM_operatortype_append(CLIP_OT_frame_jump); - /* foorage */ + /* set optical center to frame center */ WM_operatortype_append(CLIP_OT_set_center_principal); /* selection */ @@ -505,7 +493,9 @@ static void clip_operatortypes(void) WM_operatortype_append(CLIP_OT_stabilize_2d_add); WM_operatortype_append(CLIP_OT_stabilize_2d_remove); WM_operatortype_append(CLIP_OT_stabilize_2d_select); - WM_operatortype_append(CLIP_OT_stabilize_2d_set_rotation); + WM_operatortype_append(CLIP_OT_stabilize_2d_rotation_add); + WM_operatortype_append(CLIP_OT_stabilize_2d_rotation_remove); + WM_operatortype_append(CLIP_OT_stabilize_2d_rotation_select); /* clean-up */ WM_operatortype_append(CLIP_OT_clear_track_path); @@ -634,8 +624,10 @@ static void clip_keymap(struct wmKeyConfig *keyconf) WM_keymap_add_item(keymap, "CLIP_OT_view_selected", PADPERIOD, KM_PRESS, 0, 0); +#ifdef WITH_INPUT_NDOF WM_keymap_add_item(keymap, "CLIP_OT_view_all", NDOF_BUTTON_FIT, KM_PRESS, 0, 0); WM_keymap_add_item(keymap, "CLIP_OT_view_ndof", NDOF_MOTION, 0, 0, 0); +#endif /* jump to special frame */ kmi = WM_keymap_add_item(keymap, "CLIP_OT_frame_jump", LEFTARROWKEY, KM_PRESS, KM_CTRL | KM_SHIFT, 0); @@ -791,7 +783,9 @@ static void clip_keymap(struct wmKeyConfig *keyconf) /* view */ WM_keymap_add_item(keymap, "CLIP_OT_graph_view_all", HOMEKEY, KM_PRESS, 0, 0); +#ifdef WITH_INPUT_NDOF WM_keymap_add_item(keymap, "CLIP_OT_graph_view_all", NDOF_BUTTON_FIT, KM_PRESS, 0, 0); +#endif WM_keymap_add_item(keymap, "CLIP_OT_graph_center_current_frame", PADPERIOD, KM_PRESS, 0, 0); kmi = WM_keymap_add_item(keymap, "WM_OT_context_toggle", LKEY, KM_PRESS, 0, 0); @@ -822,7 +816,9 @@ static void clip_keymap(struct wmKeyConfig *keyconf) RNA_boolean_set(kmi->ptr, "extend", true); /* toggle */ WM_keymap_add_item(keymap, "CLIP_OT_dopesheet_view_all", HOMEKEY, KM_PRESS, 0, 0); +#ifdef WITH_INPUT_NDOF WM_keymap_add_item(keymap, "CLIP_OT_dopesheet_view_all", NDOF_BUTTON_FIT, KM_PRESS, 0, 0); +#endif } const char *clip_context_dir[] = {"edit_movieclip", "edit_mask", NULL}; diff --git a/source/blender/editors/space_clip/tracking_ops.c b/source/blender/editors/space_clip/tracking_ops.c index 61bfa5b315b..d28cbe5fb1d 100644 --- a/source/blender/editors/space_clip/tracking_ops.c +++ b/source/blender/editors/space_clip/tracking_ops.c @@ -1509,8 +1509,10 @@ static int join_tracks_exec(bContext *C, wmOperator *op) SpaceClip *sc = CTX_wm_space_clip(C); MovieClip *clip = ED_space_clip_get_clip(sc); MovieTracking *tracking = &clip->tracking; + MovieTrackingStabilization *stab = &tracking->stabilization; ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking); ListBase *plane_tracks_base = BKE_tracking_get_active_plane_tracks(tracking); + bool update_stabilization = false; MovieTrackingTrack *act_track = BKE_tracking_track_get_active(tracking); if (act_track == NULL) { @@ -1528,8 +1530,23 @@ static int join_tracks_exec(bContext *C, wmOperator *op) if (TRACK_VIEW_SELECTED(sc, track) && track != act_track) { BKE_tracking_tracks_join(tracking, act_track, track); - if (tracking->stabilization.rot_track == track) { - tracking->stabilization.rot_track = act_track; + if (track->flag & TRACK_USE_2D_STAB) { + update_stabilization = true; + if ((act_track->flag & TRACK_USE_2D_STAB) == 0) { + act_track->flag |= TRACK_USE_2D_STAB; + } else { + stab->tot_track--; + } + BLI_assert(0 <= stab->tot_track); + } + if (track->flag & TRACK_USE_2D_STAB_ROT) { + update_stabilization = true; + if ((act_track->flag & TRACK_USE_2D_STAB_ROT) == 0) { + act_track->flag |= TRACK_USE_2D_STAB_ROT; + } else { + stab->tot_rot_track--; + } + BLI_assert(0 <= stab->tot_rot_track); } for (MovieTrackingPlaneTrack *plane_track = plane_tracks_base->first; @@ -1551,6 +1568,10 @@ static int join_tracks_exec(bContext *C, wmOperator *op) } } + if (update_stabilization) { + WM_event_add_notifier(C, NC_MOVIECLIP | ND_DISPLAY, clip); + } + GSetIterator gs_iter; int framenr = ED_space_clip_get_clip_frame_number(sc); GSET_ITER (gs_iter, point_tracks) { diff --git a/source/blender/editors/space_clip/tracking_ops_stabilize.c b/source/blender/editors/space_clip/tracking_ops_stabilize.c index 8d6173e1cea..35b1aead343 100644 --- a/source/blender/editors/space_clip/tracking_ops_stabilize.c +++ b/source/blender/editors/space_clip/tracking_ops_stabilize.c @@ -84,7 +84,6 @@ static int stabilize_2d_add_exec(bContext *C, wmOperator *UNUSED(op)) } if (update) { - stab->ok = 0; DAG_id_tag_update(&clip->id, 0); WM_event_add_notifier(C, NC_MOVIECLIP | ND_DISPLAY, clip); } @@ -96,7 +95,7 @@ void CLIP_OT_stabilize_2d_add(wmOperatorType *ot) { /* identifiers */ ot->name = "Add Stabilization Tracks"; - ot->description = "Add selected tracks to 2D stabilization tool"; + ot->description = "Add selected tracks to 2D translation stabilization"; ot->idname = "CLIP_OT_stabilize_2d_add"; /* api callbacks */ @@ -139,7 +138,6 @@ static int stabilize_2d_remove_exec(bContext *C, wmOperator *UNUSED(op)) } if (update) { - stab->ok = 0; DAG_id_tag_update(&clip->id, 0); WM_event_add_notifier(C, NC_MOVIECLIP | ND_DISPLAY, clip); } @@ -151,7 +149,7 @@ void CLIP_OT_stabilize_2d_remove(wmOperatorType *ot) { /* identifiers */ ot->name = "Remove Stabilization Track"; - ot->description = "Remove selected track from stabilization"; + ot->description = "Remove selected track from translation stabilization"; ot->idname = "CLIP_OT_stabilize_2d_remove"; /* api callbacks */ @@ -193,7 +191,7 @@ void CLIP_OT_stabilize_2d_select(wmOperatorType *ot) { /* identifiers */ ot->name = "Select Stabilization Tracks"; - ot->description = "Select tracks which are used for stabilization"; + ot->description = "Select tracks which are used for translation stabilization"; ot->idname = "CLIP_OT_stabilize_2d_select"; /* api callbacks */ @@ -204,20 +202,85 @@ void CLIP_OT_stabilize_2d_select(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/***************** set 2d stabilization rotation track operator ****************/ +/********************** add 2d stabilization tracks for rotation operator ****************/ -static int stabilize_2d_set_rotation_exec(bContext *C, wmOperator *UNUSED(op)) +static int stabilize_2d_rotation_add_exec(bContext *C, wmOperator *UNUSED(op)) { SpaceClip *sc = CTX_wm_space_clip(C); MovieClip *clip = ED_space_clip_get_clip(sc); MovieTracking *tracking = &clip->tracking; - MovieTrackingTrack *act_track = BKE_tracking_track_get_active(tracking); + ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking); + MovieTrackingStabilization *stab = &tracking->stabilization; + + bool update = false; + for (MovieTrackingTrack *track = tracksbase->first; + track != NULL; + track = track->next) + { + if (TRACK_VIEW_SELECTED(sc, track) && + (track->flag & TRACK_USE_2D_STAB_ROT) == 0) + { + track->flag |= TRACK_USE_2D_STAB_ROT; + stab->tot_rot_track++; + update = true; + } + } + + if (update) { + DAG_id_tag_update(&clip->id, 0); + WM_event_add_notifier(C, NC_MOVIECLIP | ND_DISPLAY, clip); + } + + return OPERATOR_FINISHED; +} + +void CLIP_OT_stabilize_2d_rotation_add(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add Stabilization Rotation Tracks"; + ot->description = "Add selected tracks to 2D rotation stabilization"; + ot->idname = "CLIP_OT_stabilize_2d_rotation_add"; + + /* api callbacks */ + ot->exec = stabilize_2d_rotation_add_exec; + ot->poll = stabilize_2d_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/********************** remove 2d stabilization tracks for rotation operator *************/ + +static int stabilize_2d_rotation_remove_exec(bContext *C, wmOperator *UNUSED(op)) +{ + SpaceClip *sc = CTX_wm_space_clip(C); + MovieClip *clip = ED_space_clip_get_clip(sc); + MovieTracking *tracking = &clip->tracking; + MovieTrackingStabilization *stab = &tracking->stabilization; + ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking); + int a = 0; + bool update = false; - if (act_track != NULL) { - MovieTrackingStabilization *stab = &tracking->stabilization; - stab->rot_track = act_track; - stab->ok = 0; + for (MovieTrackingTrack *track = tracksbase->first; + track != NULL; + track = track->next) + { + if (track->flag & TRACK_USE_2D_STAB_ROT) { + if (a == stab->act_rot_track) { + track->flag &= ~TRACK_USE_2D_STAB_ROT; + stab->act_rot_track--; + stab->tot_rot_track--; + if (stab->act_rot_track < 0) { + stab->act_rot_track = 0; + } + update = true; + break; + } + a++; + } + } + if (update) { DAG_id_tag_update(&clip->id, 0); WM_event_add_notifier(C, NC_MOVIECLIP | ND_DISPLAY, clip); } @@ -225,18 +288,60 @@ static int stabilize_2d_set_rotation_exec(bContext *C, wmOperator *UNUSED(op)) return OPERATOR_FINISHED; } -void CLIP_OT_stabilize_2d_set_rotation(wmOperatorType *ot) +void CLIP_OT_stabilize_2d_rotation_remove(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Remove Stabilization Rotation Track"; + ot->description = "Remove selected track from rotation stabilization"; + ot->idname = "CLIP_OT_stabilize_2d_rotation_remove"; + + /* api callbacks */ + ot->exec = stabilize_2d_rotation_remove_exec; + ot->poll = stabilize_2d_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/********************** select 2d stabilization rotation tracks operator *****************/ + +static int stabilize_2d_rotation_select_exec(bContext *C, wmOperator *UNUSED(op)) +{ + SpaceClip *sc = CTX_wm_space_clip(C); + MovieClip *clip = ED_space_clip_get_clip(sc); + MovieTracking *tracking = &clip->tracking; + ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking); + bool update = false; + + for (MovieTrackingTrack *track = tracksbase->first; + track != NULL; + track = track->next) + { + if (track->flag & TRACK_USE_2D_STAB_ROT) { + BKE_tracking_track_flag_set(track, TRACK_AREA_ALL, SELECT); + update = true; + } + } + + if (update) { + WM_event_add_notifier(C, NC_MOVIECLIP | ND_SELECT, clip); + } + + return OPERATOR_FINISHED; +} + +void CLIP_OT_stabilize_2d_rotation_select(wmOperatorType *ot) { /* identifiers */ - ot->name = "Set Rotation Track"; - ot->description = "Use active track to compensate rotation when " - "doing 2D stabilization"; - ot->idname = "CLIP_OT_stabilize_2d_set_rotation"; + ot->name = "Select Stabilization Rotation Tracks"; + ot->description = "Select tracks which are used for rotation stabilization"; + ot->idname = "CLIP_OT_stabilize_2d_rotation_select"; /* api callbacks */ - ot->exec = stabilize_2d_set_rotation_exec; + ot->exec = stabilize_2d_rotation_select_exec; ot->poll = stabilize_2d_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } + diff --git a/source/blender/editors/space_file/file_intern.h b/source/blender/editors/space_file/file_intern.h index a55b18a2212..71e38f72a7a 100644 --- a/source/blender/editors/space_file/file_intern.h +++ b/source/blender/editors/space_file/file_intern.h @@ -128,7 +128,6 @@ void file_panels_register(struct ARegionType *art); /* file_utils.c */ void file_tile_boundbox(const ARegion *ar, FileLayout *layout, const int file, rcti *r_bounds); -bool file_is_dir(struct SpaceFile *sfile, const char *path); #endif /* __FILE_INTERN_H__ */ diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c index c42ff120102..9f5e98d2431 100644 --- a/source/blender/editors/space_file/file_ops.c +++ b/source/blender/editors/space_file/file_ops.c @@ -1894,7 +1894,7 @@ void file_directory_enter_handle(bContext *C, void *UNUSED(arg_unused), void *UN file_expand_directory(C); /* special case, user may have pasted a filepath into the directory */ - if (!file_is_dir(sfile, sfile->params->dir)) { + if (!filelist_is_dir(sfile->files, sfile->params->dir)) { char tdir[FILE_MAX_LIBEXTRA]; char *group, *name; @@ -1920,7 +1920,7 @@ void file_directory_enter_handle(bContext *C, void *UNUSED(arg_unused), void *UN BLI_cleanup_dir(G.main->name, sfile->params->dir); - if (file_is_dir(sfile, sfile->params->dir)) { + if (filelist_is_dir(sfile->files, sfile->params->dir)) { /* if directory exists, enter it immediately */ ED_file_change_dir(C); @@ -1993,7 +1993,7 @@ void file_filename_enter_handle(bContext *C, void *UNUSED(arg_unused), void *arg BLI_join_dirfile(filepath, sizeof(sfile->params->dir), sfile->params->dir, sfile->params->file); /* if directory, open it and empty filename field */ - if (file_is_dir(sfile, filepath)) { + if (filelist_is_dir(sfile->files, filepath)) { BLI_cleanup_dir(G.main->name, filepath); BLI_strncpy(sfile->params->dir, filepath, sizeof(sfile->params->dir)); sfile->params->file[0] = '\0'; diff --git a/source/blender/editors/space_file/file_utils.c b/source/blender/editors/space_file/file_utils.c index f19e301064d..c1caf5ae8ac 100644 --- a/source/blender/editors/space_file/file_utils.c +++ b/source/blender/editors/space_file/file_utils.c @@ -48,17 +48,3 @@ void file_tile_boundbox(const ARegion *ar, FileLayout *layout, const int file, r BLI_rcti_init(r_bounds, xmin, xmin + layout->tile_w + layout->tile_border_x, ymax - layout->tile_h - layout->tile_border_y, ymax); } - -/* Cannot directly use BLI_is_dir in libloading context... */ -bool file_is_dir(struct SpaceFile *sfile, const char *path) -{ - if (sfile->params->type == FILE_LOADLIB) { - char tdir[FILE_MAX_LIBEXTRA]; - char *name; - if (BLO_library_path_explode(sfile->params->dir, tdir, NULL, &name) && BLI_is_file(tdir)) { - /* .blend file itself and group are considered as directories, not final datablock names. */ - return name ? false : true; - } - } - return BLI_is_dir(path); -} diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index 5e9eb1f9207..14719322bf7 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -309,8 +309,9 @@ typedef struct FileList { struct BlendHandle *libfiledata; - /* Set given path as root directory, may change given string in place to a valid value. */ - void (*checkdirf)(struct FileList *, char *); + /* Set given path as root directory, if last bool is true may change given string in place to a valid value. + * Returns True if valid dir. */ + bool (*checkdirf)(struct FileList *, char *, const bool); /* Fill filelist (to be called by read job). */ void (*read_jobf)(struct FileList *, const char *, short *, short *, float *, ThreadMutex *); @@ -920,6 +921,8 @@ static int filelist_geticon_ex( return ICON_FILE_BLANK; else if (typeflag & FILE_TYPE_COLLADA) return ICON_FILE_BLANK; + else if (typeflag & FILE_TYPE_ALEMBIC) + return ICON_FILE_BLANK; else if (typeflag & FILE_TYPE_TEXT) return ICON_FILE_TEXT; else if (typeflag & FILE_TYPE_BLENDERLIB) { @@ -940,24 +943,37 @@ int filelist_geticon(struct FileList *filelist, const int index, const bool is_m /* ********** Main ********** */ -static void filelist_checkdir_dir(struct FileList *UNUSED(filelist), char *r_dir) +static bool filelist_checkdir_dir(struct FileList *UNUSED(filelist), char *r_dir, const bool do_change) { - BLI_make_exist(r_dir); + if (do_change) { + BLI_make_exist(r_dir); + return true; + } + else { + return BLI_is_dir(r_dir); + } } -static void filelist_checkdir_lib(struct FileList *UNUSED(filelist), char *r_dir) +static bool filelist_checkdir_lib(struct FileList *UNUSED(filelist), char *r_dir, const bool do_change) { - char dir[FILE_MAX_LIBEXTRA]; - if (!BLO_library_path_explode(r_dir, dir, NULL, NULL)) { + char tdir[FILE_MAX_LIBEXTRA]; + char *name; + + const bool is_valid = (BLI_is_dir(r_dir) || + (BLO_library_path_explode(r_dir, tdir, NULL, &name) && BLI_is_file(tdir) && !name)); + + if (do_change && !is_valid) { /* if not a valid library, we need it to be a valid directory! */ BLI_make_exist(r_dir); + return true; } + return is_valid; } -static void filelist_checkdir_main(struct FileList *filelist, char *r_dir) +static bool filelist_checkdir_main(struct FileList *filelist, char *r_dir, const bool do_change) { /* TODO */ - filelist_checkdir_lib(filelist, r_dir); + return filelist_checkdir_lib(filelist, r_dir, do_change); } static void filelist_entry_clear(FileDirEntry *entry) @@ -1376,6 +1392,11 @@ const char *filelist_dir(struct FileList *filelist) return filelist->filelist.root; } +bool filelist_is_dir(struct FileList *filelist, const char *path) +{ + return filelist->checkdirf(filelist, (char *)path, false); +} + /** * May modify in place given r_dir, which is expected to be FILE_MAX_LIBEXTRA length. */ @@ -1384,7 +1405,7 @@ void filelist_setdir(struct FileList *filelist, char *r_dir) BLI_assert(strlen(r_dir) < FILE_MAX_LIBEXTRA); BLI_cleanup_dir(G.main->name, r_dir); - filelist->checkdirf(filelist, r_dir); + BLI_assert(filelist->checkdirf(filelist, r_dir, true)); if (!STREQ(filelist->filelist.root, r_dir)) { BLI_strncpy(filelist->filelist.root, r_dir, sizeof(filelist->filelist.root)); @@ -1952,6 +1973,9 @@ int ED_path_extension_type(const char *path) else if (BLI_testextensie(path, ".dae")) { return FILE_TYPE_COLLADA; } + else if (BLI_testextensie(path, ".abc")) { + return FILE_TYPE_ALEMBIC; + } else if (BLI_testextensie_array(path, imb_ext_image) || (G.have_quicktime && BLI_testextensie_array(path, imb_ext_image_qt))) { @@ -2004,6 +2028,8 @@ int ED_file_extension_icon(const char *path) return ICON_FILE_BLANK; case FILE_TYPE_COLLADA: return ICON_FILE_BLANK; + case FILE_TYPE_ALEMBIC: + return ICON_FILE_BLANK; case FILE_TYPE_TEXT: return ICON_FILE_TEXT; default: diff --git a/source/blender/editors/space_file/filelist.h b/source/blender/editors/space_file/filelist.h index d70faab1d6a..f4304681780 100644 --- a/source/blender/editors/space_file/filelist.h +++ b/source/blender/editors/space_file/filelist.h @@ -86,6 +86,7 @@ void filelist_clear_ex(struct FileList *filelist, const bool do_c void filelist_free(struct FileList *filelist); const char * filelist_dir(struct FileList *filelist); +bool filelist_is_dir(struct FileList *filelist, const char *path); void filelist_setdir(struct FileList *filelist, char *r_dir); int filelist_files_ensure(struct FileList *filelist); diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c index ab33d452d3c..7abe5ff5070 100644 --- a/source/blender/editors/space_file/filesel.c +++ b/source/blender/editors/space_file/filesel.c @@ -185,6 +185,8 @@ short ED_fileselect_set_params(SpaceFile *sfile) params->filter |= RNA_property_boolean_get(op->ptr, prop) ? FILE_TYPE_BTX : 0; if ((prop = RNA_struct_find_property(op->ptr, "filter_collada"))) params->filter |= RNA_property_boolean_get(op->ptr, prop) ? FILE_TYPE_COLLADA : 0; + if ((prop = RNA_struct_find_property(op->ptr, "filter_alembic"))) + params->filter |= RNA_property_boolean_get(op->ptr, prop) ? FILE_TYPE_ALEMBIC : 0; if ((prop = RNA_struct_find_property(op->ptr, "filter_glob"))) { /* Protection against pyscripts not setting proper size limit... */ char *tmp = RNA_property_string_get_alloc( @@ -213,7 +215,7 @@ short ED_fileselect_set_params(SpaceFile *sfile) FILTER_ID_GR | FILTER_ID_IM | FILTER_ID_LA | FILTER_ID_LS | FILTER_ID_LT | FILTER_ID_MA | FILTER_ID_MB | FILTER_ID_MC | FILTER_ID_ME | FILTER_ID_MSK | FILTER_ID_NT | FILTER_ID_OB | FILTER_ID_PA | FILTER_ID_PAL | FILTER_ID_PC | FILTER_ID_SCE | FILTER_ID_SPK | FILTER_ID_SO | - FILTER_ID_TE | FILTER_ID_TXT | FILTER_ID_VF | FILTER_ID_WO; + FILTER_ID_TE | FILTER_ID_TXT | FILTER_ID_VF | FILTER_ID_WO | FILTER_ID_CF; if (U.uiflag & USER_HIDE_DOT) { params->flag |= FILE_HIDE_DOT; @@ -590,7 +592,7 @@ void ED_file_change_dir(bContext *C) sfile->params->filter_search[0] = '\0'; sfile->params->active_file = -1; - if (!file_is_dir(sfile, sfile->params->dir)) { + if (!filelist_is_dir(sfile->files, sfile->params->dir)) { BLI_strncpy(sfile->params->dir, filelist_dir(sfile->files), sizeof(sfile->params->dir)); /* could return but just refresh the current dir */ } diff --git a/source/blender/editors/space_graph/graph_ops.c b/source/blender/editors/space_graph/graph_ops.c index 478dbd3d9c0..75f0da83e77 100644 --- a/source/blender/editors/space_graph/graph_ops.c +++ b/source/blender/editors/space_graph/graph_ops.c @@ -632,7 +632,9 @@ static void graphedit_keymap_keyframes(wmKeyConfig *keyconf, wmKeyMap *keymap) /* auto-set range */ WM_keymap_add_item(keymap, "GRAPH_OT_previewrange_set", PKEY, KM_PRESS, KM_CTRL | KM_ALT, 0); WM_keymap_add_item(keymap, "GRAPH_OT_view_all", HOMEKEY, KM_PRESS, 0, 0); +#ifdef WITH_INPUT_NDOF WM_keymap_add_item(keymap, "GRAPH_OT_view_all", NDOF_BUTTON_FIT, KM_PRESS, 0, 0); +#endif WM_keymap_add_item(keymap, "GRAPH_OT_view_selected", PADPERIOD, KM_PRESS, 0, 0); WM_keymap_add_item(keymap, "GRAPH_OT_view_frame", PAD0, KM_PRESS, 0, 0); diff --git a/source/blender/editors/space_graph/space_graph.c b/source/blender/editors/space_graph/space_graph.c index 2582ba4be8d..b35d1b2c777 100644 --- a/source/blender/editors/space_graph/space_graph.c +++ b/source/blender/editors/space_graph/space_graph.c @@ -687,7 +687,7 @@ static void graph_id_remap(ScrArea *UNUSED(sa), SpaceLink *slink, ID *old_id, ID return; } - if ((ID *)sgraph->ads->filter_grp == old_id) { + if (sgraph->ads && (ID *)sgraph->ads->filter_grp == old_id) { sgraph->ads->filter_grp = (Group *)new_id; } } diff --git a/source/blender/editors/space_image/image_intern.h b/source/blender/editors/space_image/image_intern.h index 69993c3be65..52d04ad4956 100644 --- a/source/blender/editors/space_image/image_intern.h +++ b/source/blender/editors/space_image/image_intern.h @@ -64,7 +64,9 @@ void IMAGE_OT_view_zoom_in(struct wmOperatorType *ot); void IMAGE_OT_view_zoom_out(struct wmOperatorType *ot); void IMAGE_OT_view_zoom_ratio(struct wmOperatorType *ot); void IMAGE_OT_view_zoom_border(struct wmOperatorType *ot); +#ifdef WITH_INPUT_NDOF void IMAGE_OT_view_ndof(struct wmOperatorType *ot); +#endif void IMAGE_OT_new(struct wmOperatorType *ot); void IMAGE_OT_open(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index 1158e692182..1f591b5fb35 100644 --- a/source/blender/editors/space_image/image_ops.c +++ b/source/blender/editors/space_image/image_ops.c @@ -656,6 +656,7 @@ void IMAGE_OT_view_zoom(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_HIDDEN); } +#ifdef WITH_INPUT_NDOF /********************** NDOF operator *********************/ /* Combined pan/zoom from a 3D mouse device. @@ -705,6 +706,7 @@ void IMAGE_OT_view_ndof(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_LOCK_BYPASS; } +#endif /* WITH_INPUT_NDOF */ /********************** view all operator *********************/ @@ -1611,13 +1613,16 @@ static int save_image_options_init(SaveImageOptions *simopts, SpaceImage *sima, if (ima->source == IMA_SRC_GENERATED) { simopts->im_format.imtype = R_IMF_IMTYPE_PNG; simopts->im_format.compress = ibuf->foptions.quality; + simopts->im_format.planes = ibuf->planes; } else { BKE_imbuf_to_image_format(&simopts->im_format, ibuf); } - } - simopts->im_format.planes = ibuf->planes; + /* use the multiview image settings as the default */ + simopts->im_format.stereo3d_format = *ima->stereo3d_format; + simopts->im_format.views_format = ima->views_format; + } //simopts->subimtype = scene->r.subimtype; /* XXX - this is lame, we need to make these available too! */ @@ -1658,10 +1663,6 @@ static int save_image_options_init(SaveImageOptions *simopts, SpaceImage *sima, } } - /* use the multiview image settings as the default */ - simopts->im_format.stereo3d_format = *ima->stereo3d_format; - simopts->im_format.views_format = ima->views_format; - /* color management */ BKE_color_managed_display_settings_copy(&simopts->im_format.display_settings, &scene->display_settings); BKE_color_managed_view_settings_copy(&simopts->im_format.view_settings, &scene->view_settings); diff --git a/source/blender/editors/space_image/space_image.c b/source/blender/editors/space_image/space_image.c index 35a658eac23..6ddf78290aa 100644 --- a/source/blender/editors/space_image/space_image.c +++ b/source/blender/editors/space_image/space_image.c @@ -235,7 +235,9 @@ static void image_operatortypes(void) WM_operatortype_append(IMAGE_OT_view_zoom_out); WM_operatortype_append(IMAGE_OT_view_zoom_ratio); WM_operatortype_append(IMAGE_OT_view_zoom_border); +#ifdef WITH_INPUT_NDOF WM_operatortype_append(IMAGE_OT_view_ndof); +#endif WM_operatortype_append(IMAGE_OT_new); WM_operatortype_append(IMAGE_OT_open); @@ -296,8 +298,10 @@ static void image_keymap(struct wmKeyConfig *keyconf) WM_keymap_add_item(keymap, "IMAGE_OT_view_pan", MIDDLEMOUSE, KM_PRESS, KM_SHIFT, 0); WM_keymap_add_item(keymap, "IMAGE_OT_view_pan", MOUSEPAN, 0, 0, 0); +#ifdef WITH_INPUT_NDOF WM_keymap_add_item(keymap, "IMAGE_OT_view_all", NDOF_BUTTON_FIT, KM_PRESS, 0, 0); // or view selected? WM_keymap_add_item(keymap, "IMAGE_OT_view_ndof", NDOF_MOTION, 0, 0, 0); +#endif WM_keymap_add_item(keymap, "IMAGE_OT_view_zoom_in", WHEELINMOUSE, KM_PRESS, 0, 0); WM_keymap_add_item(keymap, "IMAGE_OT_view_zoom_out", WHEELOUTMOUSE, KM_PRESS, 0, 0); diff --git a/source/blender/editors/space_logic/space_logic.c b/source/blender/editors/space_logic/space_logic.c index 69966e9bf34..12ca141128b 100644 --- a/source/blender/editors/space_logic/space_logic.c +++ b/source/blender/editors/space_logic/space_logic.c @@ -186,7 +186,9 @@ static void logic_keymap(struct wmKeyConfig *keyconf) WM_keymap_add_menu(keymap, "LOGIC_MT_logicbricks_add", AKEY, KM_PRESS, KM_SHIFT, 0); WM_keymap_add_item(keymap, "LOGIC_OT_view_all", HOMEKEY, KM_PRESS, 0, 0); +#ifdef WITH_INPUT_NDOF WM_keymap_add_item(keymap, "LOGIC_OT_view_all", NDOF_BUTTON_FIT, KM_PRESS, 0, 0); +#endif } static void logic_refresh(const bContext *UNUSED(C), ScrArea *UNUSED(sa)) diff --git a/source/blender/editors/space_nla/nla_buttons.c b/source/blender/editors/space_nla/nla_buttons.c index 9032d286933..3243579f7d0 100644 --- a/source/blender/editors/space_nla/nla_buttons.c +++ b/source/blender/editors/space_nla/nla_buttons.c @@ -131,6 +131,7 @@ bool nla_panel_context(const bContext *C, PointerRNA *adt_ptr, PointerRNA *nlt_p case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */ case ANIMTYPE_DSLAM: case ANIMTYPE_DSCAM: + case ANIMTYPE_DSCACHEFILE: case ANIMTYPE_DSCUR: case ANIMTYPE_DSSKEY: case ANIMTYPE_DSWOR: diff --git a/source/blender/editors/space_nla/nla_channels.c b/source/blender/editors/space_nla/nla_channels.c index 9e73e03a664..4f30c049d9d 100644 --- a/source/blender/editors/space_nla/nla_channels.c +++ b/source/blender/editors/space_nla/nla_channels.c @@ -170,6 +170,7 @@ static int mouse_nla_channels(bContext *C, bAnimContext *ac, float x, int channe case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */ case ANIMTYPE_DSLAM: case ANIMTYPE_DSCAM: + case ANIMTYPE_DSCACHEFILE: case ANIMTYPE_DSCUR: case ANIMTYPE_DSSKEY: case ANIMTYPE_DSWOR: diff --git a/source/blender/editors/space_nla/nla_ops.c b/source/blender/editors/space_nla/nla_ops.c index 386950ead3a..48037a10b2d 100644 --- a/source/blender/editors/space_nla/nla_ops.c +++ b/source/blender/editors/space_nla/nla_ops.c @@ -242,7 +242,9 @@ static void nla_keymap_main(wmKeyConfig *keyconf, wmKeyMap *keymap) WM_keymap_add_item(keymap, "NLA_OT_previewrange_set", PKEY, KM_PRESS, KM_CTRL | KM_ALT, 0); WM_keymap_add_item(keymap, "NLA_OT_view_all", HOMEKEY, KM_PRESS, 0, 0); +#ifdef WITH_INPUT_NDOF WM_keymap_add_item(keymap, "NLA_OT_view_all", NDOF_BUTTON_FIT, KM_PRESS, 0, 0); +#endif WM_keymap_add_item(keymap, "NLA_OT_view_selected", PADPERIOD, KM_PRESS, 0, 0); WM_keymap_add_item(keymap, "NLA_OT_view_frame", PAD0, KM_PRESS, 0, 0); diff --git a/source/blender/editors/space_node/node_draw.c b/source/blender/editors/space_node/node_draw.c index d9c51e427c8..ab40c55b59d 100644 --- a/source/blender/editors/space_node/node_draw.c +++ b/source/blender/editors/space_node/node_draw.c @@ -1327,8 +1327,10 @@ void drawnodespace(const bContext *C, ARegion *ar) path = snode->treepath.last; /* update tree path name (drawn in the bottom left) */ - if (snode->id && UNLIKELY(!STREQ(path->node_name, snode->id->name + 2))) { - BLI_strncpy(path->node_name, snode->id->name + 2, sizeof(path->node_name)); + ID *name_id = (path->nodetree && path->nodetree != snode->nodetree) ? &path->nodetree->id : snode->id; + + if (name_id && UNLIKELY(!STREQ(path->node_name, name_id->name + 2))) { + BLI_strncpy(path->node_name, name_id->name + 2, sizeof(path->node_name)); } /* current View2D center, will be set temporarily for parent node trees */ diff --git a/source/blender/editors/space_node/node_ops.c b/source/blender/editors/space_node/node_ops.c index 7788173a8ee..5118d52efc4 100644 --- a/source/blender/editors/space_node/node_ops.c +++ b/source/blender/editors/space_node/node_ops.c @@ -295,7 +295,9 @@ void node_keymap(struct wmKeyConfig *keyconf) WM_keymap_add_item(keymap, "NODE_OT_hide_socket_toggle", HKEY, KM_PRESS, KM_CTRL, 0); WM_keymap_add_item(keymap, "NODE_OT_view_all", HOMEKEY, KM_PRESS, 0, 0); +#ifdef WITH_INPUT_NDOF WM_keymap_add_item(keymap, "NODE_OT_view_all", NDOF_BUTTON_FIT, KM_PRESS, 0, 0); +#endif WM_keymap_add_item(keymap, "NODE_OT_view_selected", PADPERIOD, KM_PRESS, 0, 0); kmi = WM_keymap_add_item(keymap, "NODE_OT_select_border", BKEY, KM_PRESS, 0, 0); diff --git a/source/blender/editors/space_node/node_relationships.c b/source/blender/editors/space_node/node_relationships.c index ea3869ef387..5f592431558 100644 --- a/source/blender/editors/space_node/node_relationships.c +++ b/source/blender/editors/space_node/node_relationships.c @@ -276,25 +276,16 @@ static bNodeSocket *best_socket_input(bNodeTree *ntree, bNode *node, int num, in return NULL; } -static int snode_autoconnect_input(SpaceNode *snode, bNode *node_fr, bNodeSocket *sock_fr, bNode *node_to, bNodeSocket *sock_to, int replace) +static bool snode_autoconnect_input(SpaceNode *snode, bNode *node_fr, bNodeSocket *sock_fr, bNode *node_to, bNodeSocket *sock_to, int replace) { bNodeTree *ntree = snode->edittree; - bNodeLink *link; /* then we can connect */ if (replace) nodeRemSocketLinks(ntree, sock_to); - link = nodeAddLink(ntree, node_fr, sock_fr, node_to, sock_to); - /* validate the new link */ - ntreeUpdateTree(G.main, ntree); - if (!(link->flag & NODE_LINK_VALID)) { - nodeRemLink(ntree, link); - return 0; - } - - snode_update(snode, node_to); - return 1; + nodeAddLink(ntree, node_fr, sock_fr, node_to, sock_to); + return true; } static void snode_autoconnect(SpaceNode *snode, const bool allow_multiple, const bool replace) diff --git a/source/blender/editors/space_node/node_templates.c b/source/blender/editors/space_node/node_templates.c index 09594ab543c..ec525e684b0 100644 --- a/source/blender/editors/space_node/node_templates.c +++ b/source/blender/editors/space_node/node_templates.c @@ -428,6 +428,20 @@ static int ui_node_item_name_compare(const void *a, const void *b) return BLI_natstrcmp(type_a->ui_name, type_b->ui_name); } +static bool ui_node_item_special_poll(const bNodeTree *UNUSED(ntree), + const bNodeType *ntype) +{ + if (STREQ(ntype->idname, "ShaderNodeUVAlongStroke")) { + /* TODO(sergey): Currently we don't have Freestyle nodes edited from + * the buttons context, so can ignore it's nodes completely. + * + * However, we might want to do some extra checks here later. + */ + return false; + } + return true; +} + static void ui_node_menu_column(NodeLinkArg *arg, int nclass, const char *cname) { bNodeTree *ntree = arg->ntree; @@ -452,11 +466,17 @@ static void ui_node_menu_column(NodeLinkArg *arg, int nclass, const char *cname) BLI_array_declare(sorted_ntypes); NODE_TYPES_BEGIN(ntype) { - if (compatibility && !(ntype->compatibility & compatibility)) + if (compatibility && !(ntype->compatibility & compatibility)) { continue; + } - if (ntype->nclass != nclass) + if (ntype->nclass != nclass) { continue; + } + + if (!ui_node_item_special_poll(ntree, ntype)) { + continue; + } BLI_array_append(sorted_ntypes, ntype); } diff --git a/source/blender/editors/space_outliner/outliner_draw.c b/source/blender/editors/space_outliner/outliner_draw.c index b57462df53b..d4553b650c5 100644 --- a/source/blender/editors/space_outliner/outliner_draw.c +++ b/source/blender/editors/space_outliner/outliner_draw.c @@ -1173,6 +1173,8 @@ static void tselem_draw_icon(uiBlock *block, int xmax, float x, float y, TreeSto UI_icon_draw(x, y, ICON_MOD_TRIANGULATE); break; case eModifierType_MeshCache: UI_icon_draw(x, y, ICON_MOD_MESHDEFORM); break; /* XXX, needs own icon */ + case eModifierType_MeshSequenceCache: + UI_icon_draw(x, y, ICON_MOD_MESHDEFORM); break; /* XXX, needs own icon */ case eModifierType_Wireframe: UI_icon_draw(x, y, ICON_MOD_WIREFRAME); break; case eModifierType_LaplacianDeform: diff --git a/source/blender/editors/space_outliner/outliner_intern.h b/source/blender/editors/space_outliner/outliner_intern.h index d2666cd0b6d..ca037cb20cc 100644 --- a/source/blender/editors/space_outliner/outliner_intern.h +++ b/source/blender/editors/space_outliner/outliner_intern.h @@ -62,7 +62,7 @@ typedef struct TreeElement { #define TREESTORE_ID_TYPE(_id) \ (ELEM(GS((_id)->name), ID_SCE, ID_LI, ID_OB, ID_ME, ID_CU, ID_MB, ID_NT, ID_MA, ID_TE, ID_IM, ID_LT, ID_LA, ID_CA) || \ ELEM(GS((_id)->name), ID_KE, ID_WO, ID_SPK, ID_GR, ID_AR, ID_AC, ID_BR, ID_PA, ID_GD, ID_LS) || \ - ELEM(GS((_id)->name), ID_SCR, ID_WM, ID_TXT, ID_VF, ID_SO)) /* Only in 'blendfile' mode ... :/ */ + ELEM(GS((_id)->name), ID_SCR, ID_WM, ID_TXT, ID_VF, ID_SO, ID_CF)) /* Only in 'blendfile' mode ... :/ */ /* TreeElement->flag */ #define TE_ACTIVE 1 diff --git a/source/blender/editors/space_outliner/outliner_tree.c b/source/blender/editors/space_outliner/outliner_tree.c index b22e6595caf..96bab3d5c1e 100644 --- a/source/blender/editors/space_outliner/outliner_tree.c +++ b/source/blender/editors/space_outliner/outliner_tree.c @@ -38,6 +38,7 @@ #include "DNA_armature_types.h" #include "DNA_constraint_types.h" #include "DNA_camera_types.h" +#include "DNA_cachefile_types.h" #include "DNA_gpencil_types.h" #include "DNA_group_types.h" #include "DNA_key_types.h" @@ -746,6 +747,16 @@ static void outliner_add_id_contents(SpaceOops *soops, TreeElement *te, TreeStor outliner_add_element(soops, &te->subtree, ca, te, TSE_ANIM_DATA, 0); break; } + case ID_CF: + { + CacheFile *cache_file = (CacheFile *)id; + + if (outliner_animdata_test(cache_file->adt)) { + outliner_add_element(soops, &te->subtree, cache_file, te, TSE_ANIM_DATA, 0); + } + + break; + } case ID_LA: { Lamp *la = (Lamp *)id; diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index adb7cf4940c..e1768e4aedc 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -471,7 +471,7 @@ static void draw_seq_handle(View2D *v2d, Sequence *seq, const float handsize_cla } /* draw info text on a sequence strip */ -static void draw_seq_text(View2D *v2d, Sequence *seq, float x1, float x2, float y1, float y2, const unsigned char background_col[3]) +static void draw_seq_text(View2D *v2d, SpaceSeq *sseq, Sequence *seq, float x1, float x2, float y1, float y2, const unsigned char background_col[3]) { rctf rect; char str[32 + FILE_MAX]; @@ -540,7 +540,12 @@ static void draw_seq_text(View2D *v2d, Sequence *seq, float x1, float x2, float name, seq->len); } else if (seq->type == SEQ_TYPE_SOUND_RAM) { - if (seq->sound) { + /* If a waveform is drawn, we don't want to overlay it with text, + * as it would make both hard to read. */ + if ((sseq->flag & SEQ_ALL_WAVEFORMS) || (seq->flag & SEQ_AUDIO_DRAW_WAVEFORM)) { + str[0] = 0; + str_len = 0; + } else if (seq->sound) { str_len = BLI_snprintf(str, sizeof(str), "%s: %s | %d", name, seq->sound->name, seq->len); } @@ -870,7 +875,7 @@ static void draw_seq_strip(const bContext *C, SpaceSeq *sseq, Scene *scene, AReg /* nice text here would require changing the view matrix for texture text */ if ((x2 - x1) / pixelx > 32) { - draw_seq_text(v2d, seq, x1, x2, y1, y2, background_col); + draw_seq_text(v2d, sseq, seq, x1, x2, y1, y2, background_col); } } diff --git a/source/blender/editors/space_sequencer/sequencer_ops.c b/source/blender/editors/space_sequencer/sequencer_ops.c index 655e029cfdd..a3cfcae77b8 100644 --- a/source/blender/editors/space_sequencer/sequencer_ops.c +++ b/source/blender/editors/space_sequencer/sequencer_ops.c @@ -201,7 +201,9 @@ void sequencer_keymap(wmKeyConfig *keyconf) WM_keymap_add_item(keymap, "SEQUENCER_OT_meta_separate", GKEY, KM_PRESS, KM_ALT, 0); WM_keymap_add_item(keymap, "SEQUENCER_OT_view_all", HOMEKEY, KM_PRESS, 0, 0); +#ifdef WITH_INPUT_NDOF WM_keymap_add_item(keymap, "SEQUENCER_OT_view_all", NDOF_BUTTON_FIT, KM_PRESS, 0, 0); +#endif WM_keymap_add_item(keymap, "SEQUENCER_OT_view_selected", PADPERIOD, KM_PRESS, 0, 0); WM_keymap_add_item(keymap, "SEQUENCER_OT_view_frame", PAD0, KM_PRESS, 0, 0); @@ -340,7 +342,9 @@ void sequencer_keymap(wmKeyConfig *keyconf) /* Preview Region ----------------------------------------------------------- */ keymap = WM_keymap_find(keyconf, "SequencerPreview", SPACE_SEQ, 0); WM_keymap_add_item(keymap, "SEQUENCER_OT_view_all_preview", HOMEKEY, KM_PRESS, 0, 0); +#ifdef WITH_INPUT_NDOF WM_keymap_add_item(keymap, "SEQUENCER_OT_view_all_preview", NDOF_BUTTON_FIT, KM_PRESS, 0, 0); +#endif WM_keymap_add_item(keymap, "SEQUENCER_OT_view_ghost_border", OKEY, KM_PRESS, 0, 0); diff --git a/source/blender/editors/space_time/space_time.c b/source/blender/editors/space_time/space_time.c index 525d42a1965..15eb154c757 100644 --- a/source/blender/editors/space_time/space_time.c +++ b/source/blender/editors/space_time/space_time.c @@ -32,7 +32,10 @@ #include <string.h> #include <stdio.h> +#include "DNA_cachefile_types.h" +#include "DNA_constraint_types.h" #include "DNA_gpencil_types.h" +#include "DNA_modifier_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" @@ -42,7 +45,10 @@ #include "BLI_dlrbTree.h" #include "BLI_utildefines.h" +#include "BKE_constraint.h" #include "BKE_context.h" +#include "BKE_main.h" +#include "BKE_modifier.h" #include "BKE_screen.h" #include "BKE_pointcache.h" @@ -320,6 +326,9 @@ static void time_draw_idblock_keyframes(View2D *v2d, ID *id, short onlysel) case ID_GD: gpencil_to_keylist(&ads, (bGPdata *)id, &keys); break; + case ID_CF: + cachefile_to_keylist(&ads, (CacheFile *)id, &keys, NULL); + break; } /* build linked-list for searching */ @@ -344,6 +353,56 @@ static void time_draw_idblock_keyframes(View2D *v2d, ID *id, short onlysel) BLI_dlrbTree_free(&keys); } +static void time_draw_caches_keyframes(Main *bmain, Scene *scene, View2D *v2d, bool onlysel) +{ + CacheFile *cache_file; + + for (cache_file = bmain->cachefiles.first; + cache_file; + cache_file = cache_file->id.next) + { + cache_file->draw_flag &= ~CACHEFILE_KEYFRAME_DRAWN; + } + + for (Base *base = scene->base.first; base; base = base->next) { + Object *ob = base->object; + + ModifierData *md = modifiers_findByType(ob, eModifierType_MeshSequenceCache); + + if (md) { + MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *)md; + + cache_file = mcmd->cache_file; + + if (!cache_file || (cache_file->draw_flag & CACHEFILE_KEYFRAME_DRAWN) != 0) { + continue; + } + + cache_file->draw_flag |= CACHEFILE_KEYFRAME_DRAWN; + + time_draw_idblock_keyframes(v2d, (ID *)cache_file, onlysel); + } + + for (bConstraint *con = ob->constraints.first; con; con = con->next) { + if (con->type != CONSTRAINT_TYPE_TRANSFORM_CACHE) { + continue; + } + + bTransformCacheConstraint *data = con->data; + + cache_file = data->cache_file; + + if (!cache_file || (cache_file->draw_flag & CACHEFILE_KEYFRAME_DRAWN) != 0) { + continue; + } + + cache_file->draw_flag |= CACHEFILE_KEYFRAME_DRAWN; + + time_draw_idblock_keyframes(v2d, (ID *)cache_file, onlysel); + } + } +} + /* draw keyframe lines for timeline */ static void time_draw_keyframes(const bContext *C, ARegion *ar) { @@ -354,7 +413,11 @@ static void time_draw_keyframes(const bContext *C, ARegion *ar) /* set this for all keyframe lines once and for all */ glLineWidth(1.0); - + + /* draw cache files keyframes (if available) */ + UI_ThemeColor(TH_TIME_KEYFRAME); + time_draw_caches_keyframes(CTX_data_main(C), scene, v2d, onlysel); + /* draw grease pencil keyframes (if available) */ UI_ThemeColor(TH_TIME_GP_KEYFRAME); if (scene->gpd) { @@ -750,6 +813,7 @@ void ED_spacetype_time(void) art->draw = time_main_region_draw; art->listener = time_main_region_listener; art->keymap = time_keymap; + art->lock = 1; /* Due to pointcache, see T4960. */ BLI_addhead(&st->regiontypes, art); /* regions: header */ diff --git a/source/blender/editors/space_time/time_ops.c b/source/blender/editors/space_time/time_ops.c index 7dd45327352..872793128f0 100644 --- a/source/blender/editors/space_time/time_ops.c +++ b/source/blender/editors/space_time/time_ops.c @@ -219,7 +219,9 @@ void time_keymap(wmKeyConfig *keyconf) WM_keymap_add_item(keymap, "TIME_OT_start_frame_set", SKEY, KM_PRESS, 0, 0); WM_keymap_add_item(keymap, "TIME_OT_end_frame_set", EKEY, KM_PRESS, 0, 0); WM_keymap_add_item(keymap, "TIME_OT_view_all", HOMEKEY, KM_PRESS, 0, 0); +#ifdef WITH_INPUT_NDOF WM_keymap_add_item(keymap, "TIME_OT_view_all", NDOF_BUTTON_FIT, KM_PRESS, 0, 0); +#endif WM_keymap_add_item(keymap, "TIME_OT_view_frame", PAD0, KM_PRESS, 0, 0); } diff --git a/source/blender/editors/space_view3d/drawmesh.c b/source/blender/editors/space_view3d/drawmesh.c index 614289997c1..d273f898f63 100644 --- a/source/blender/editors/space_view3d/drawmesh.c +++ b/source/blender/editors/space_view3d/drawmesh.c @@ -1015,12 +1015,18 @@ static void draw_mesh_textured_old(Scene *scene, View3D *v3d, RegionView3D *rv3d else { userData.me = NULL; - if ((ob->mode & OB_MODE_ALL_PAINT) == 0) { + /* if ((ob->mode & OB_MODE_ALL_PAINT) == 0) */ { /* Note: this isn't efficient and runs on every redraw, * its needed so material colors are used for vertex colors. * In the future we will likely remove 'texface' so, just avoid running this where possible, - * (when vertex paint or weight paint are used). */ + * (when vertex paint or weight paint are used). + * + * Note 2: We disable optimization for now since it causes T48788 + * and it is now too close to release to do something smarter. + * + * TODO(sergey): Find some real solution here. + */ update_tface_color_layer(dm, !(ob->mode & OB_MODE_TEXTURE_PAINT)); } diff --git a/source/blender/editors/space_view3d/drawvolume.c b/source/blender/editors/space_view3d/drawvolume.c index e93d840eddd..06677ef4476 100644 --- a/source/blender/editors/space_view3d/drawvolume.c +++ b/source/blender/editors/space_view3d/drawvolume.c @@ -288,6 +288,108 @@ static int create_view_aligned_slices(VolumeSlicer *slicer, return num_points; } +static void bind_shader(SmokeDomainSettings *sds, GPUShader *shader, GPUTexture *tex_spec, + bool use_fire, const float min[3], + const float ob_sizei[3], const float invsize[3]) +{ + int invsize_location = GPU_shader_get_uniform(shader, "invsize"); + int ob_sizei_location = GPU_shader_get_uniform(shader, "ob_sizei"); + int min_location = GPU_shader_get_uniform(shader, "min_location"); + + int soot_location; + int stepsize_location; + int densityscale_location; + int spec_location, flame_location; + int shadow_location, actcol_location; + + if (use_fire) { + spec_location = GPU_shader_get_uniform(shader, "spectrum_texture"); + flame_location = GPU_shader_get_uniform(shader, "flame_texture"); + } + else { + shadow_location = GPU_shader_get_uniform(shader, "shadow_texture"); + actcol_location = GPU_shader_get_uniform(shader, "active_color"); + soot_location = GPU_shader_get_uniform(shader, "soot_texture"); + stepsize_location = GPU_shader_get_uniform(shader, "step_size"); + densityscale_location = GPU_shader_get_uniform(shader, "density_scale"); + } + + GPU_shader_bind(shader); + + if (use_fire) { + GPU_texture_bind(sds->tex_flame, 2); + GPU_shader_uniform_texture(shader, flame_location, sds->tex_flame); + + GPU_texture_bind(tex_spec, 3); + GPU_shader_uniform_texture(shader, spec_location, tex_spec); + } + else { + float density_scale = 10.0f; + + GPU_shader_uniform_vector(shader, stepsize_location, 1, 1, &sds->dx); + GPU_shader_uniform_vector(shader, densityscale_location, 1, 1, &density_scale); + + GPU_texture_bind(sds->tex, 0); + GPU_shader_uniform_texture(shader, soot_location, sds->tex); + + GPU_texture_bind(sds->tex_shadow, 1); + GPU_shader_uniform_texture(shader, shadow_location, sds->tex_shadow); + + float active_color[3] = { 0.9, 0.9, 0.9 }; + if ((sds->active_fields & SM_ACTIVE_COLORS) == 0) + mul_v3_v3(active_color, sds->active_color); + GPU_shader_uniform_vector(shader, actcol_location, 3, 1, active_color); + } + + GPU_shader_uniform_vector(shader, min_location, 3, 1, min); + GPU_shader_uniform_vector(shader, ob_sizei_location, 3, 1, ob_sizei); + GPU_shader_uniform_vector(shader, invsize_location, 3, 1, invsize); +} + +static void unbind_shader(SmokeDomainSettings *sds, GPUTexture *tex_spec, bool use_fire) +{ + GPU_shader_unbind(); + + GPU_texture_unbind(sds->tex); + + if (use_fire) { + GPU_texture_unbind(sds->tex_flame); + GPU_texture_unbind(tex_spec); + GPU_texture_free(tex_spec); + } + else { + GPU_texture_unbind(sds->tex_shadow); + } +} + +static void draw_buffer(SmokeDomainSettings *sds, GPUShader *shader, const VolumeSlicer *slicer, + const float ob_sizei[3], const float invsize[3], const int num_points, const bool do_fire) +{ + GPUTexture *tex_spec = (do_fire) ? create_flame_spectrum_texture() : NULL; + + GLuint vertex_buffer; + glGenBuffers(1, &vertex_buffer); + glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer); + glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 3 * num_points, &slicer->verts[0][0], GL_STATIC_DRAW); + + bind_shader(sds, shader, tex_spec, do_fire, slicer->min, ob_sizei, invsize); + + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(3, GL_FLOAT, 0, NULL); + + glDrawArrays(GL_TRIANGLES, 0, num_points); + + glDisableClientState(GL_VERTEX_ARRAY); + + unbind_shader(sds, tex_spec, do_fire); + + /* cleanup */ + + glBindBuffer(GL_ARRAY_BUFFER, 0); + + glDeleteBuffers(1, &vertex_buffer); +} + void draw_smoke_volume(SmokeDomainSettings *sds, Object *ob, const float min[3], const float max[3], const float viewnormal[3]) @@ -299,14 +401,23 @@ void draw_smoke_volume(SmokeDomainSettings *sds, Object *ob, const bool use_fire = (sds->active_fields & SM_ACTIVE_FIRE) && sds->tex_flame; - GPUShader *shader = GPU_shader_get_builtin_shader( - (use_fire) ? GPU_SHADER_SMOKE_FIRE : GPU_SHADER_SMOKE); + GPUShader *shader = GPU_shader_get_builtin_shader(GPU_SHADER_SMOKE); if (!shader) { fprintf(stderr, "Unable to create GLSL smoke shader.\n"); return; } + GPUShader *fire_shader = NULL; + if (use_fire) { + fire_shader = GPU_shader_get_builtin_shader(GPU_SHADER_SMOKE_FIRE); + + if (!fire_shader) { + fprintf(stderr, "Unable to create GLSL fire shader.\n"); + return; + } + } + const float ob_sizei[3] = { 1.0f / fabsf(ob->size[0]), 1.0f / fabsf(ob->size[1]), @@ -320,50 +431,6 @@ void draw_smoke_volume(SmokeDomainSettings *sds, Object *ob, TIMEIT_START(draw); #endif - /* setup smoke shader */ - - int soot_location = GPU_shader_get_uniform(shader, "soot_texture"); - int spec_location = GPU_shader_get_uniform(shader, "spectrum_texture"); - int shadow_location = GPU_shader_get_uniform(shader, "shadow_texture"); - int flame_location = GPU_shader_get_uniform(shader, "flame_texture"); - int actcol_location = GPU_shader_get_uniform(shader, "active_color"); - int stepsize_location = GPU_shader_get_uniform(shader, "step_size"); - int densityscale_location = GPU_shader_get_uniform(shader, "density_scale"); - int invsize_location = GPU_shader_get_uniform(shader, "invsize"); - int ob_sizei_location = GPU_shader_get_uniform(shader, "ob_sizei"); - int min_location = GPU_shader_get_uniform(shader, "min_location"); - - GPU_shader_bind(shader); - - GPU_texture_bind(sds->tex, 0); - GPU_shader_uniform_texture(shader, soot_location, sds->tex); - - GPU_texture_bind(sds->tex_shadow, 1); - GPU_shader_uniform_texture(shader, shadow_location, sds->tex_shadow); - - GPUTexture *tex_spec = NULL; - - if (use_fire) { - GPU_texture_bind(sds->tex_flame, 2); - GPU_shader_uniform_texture(shader, flame_location, sds->tex_flame); - - tex_spec = create_flame_spectrum_texture(); - GPU_texture_bind(tex_spec, 3); - GPU_shader_uniform_texture(shader, spec_location, tex_spec); - } - - float active_color[3] = { 0.9, 0.9, 0.9 }; - float density_scale = 10.0f; - if ((sds->active_fields & SM_ACTIVE_COLORS) == 0) - mul_v3_v3(active_color, sds->active_color); - - GPU_shader_uniform_vector(shader, actcol_location, 3, 1, active_color); - GPU_shader_uniform_vector(shader, stepsize_location, 1, 1, &sds->dx); - GPU_shader_uniform_vector(shader, densityscale_location, 1, 1, &density_scale); - GPU_shader_uniform_vector(shader, min_location, 3, 1, min); - GPU_shader_uniform_vector(shader, ob_sizei_location, 3, 1, ob_sizei); - GPU_shader_uniform_vector(shader, invsize_location, 3, 1, invsize); - /* setup slicing information */ const int max_slices = 256; @@ -379,48 +446,32 @@ void draw_smoke_volume(SmokeDomainSettings *sds, Object *ob, /* setup buffer and draw */ - int gl_depth = 0, gl_blend = 0; + int gl_depth = 0, gl_blend = 0, gl_depth_write = 0; glGetBooleanv(GL_BLEND, (GLboolean *)&gl_blend); glGetBooleanv(GL_DEPTH_TEST, (GLboolean *)&gl_depth); + glGetBooleanv(GL_DEPTH_WRITEMASK, (GLboolean *)&gl_depth_write); glEnable(GL_DEPTH_TEST); + glDepthMask(GL_FALSE); glEnable(GL_BLEND); - glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - - GLuint vertex_buffer; - glGenBuffers(1, &vertex_buffer); - glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer); - glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 3 * num_points, &slicer.verts[0][0], GL_STATIC_DRAW); - glEnableClientState(GL_VERTEX_ARRAY); - glVertexPointer(3, GL_FLOAT, 0, NULL); - - glDrawArrays(GL_TRIANGLES, 0, num_points); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + draw_buffer(sds, shader, &slicer, ob_sizei, invsize, num_points, false); - glDisableClientState(GL_VERTEX_ARRAY); + /* Draw fire separately (T47639). */ + if (use_fire) { + glBlendFunc(GL_ONE, GL_ONE); + draw_buffer(sds, fire_shader, &slicer, ob_sizei, invsize, num_points, true); + } #ifdef DEBUG_DRAW_TIME printf("Draw Time: %f\n", (float)TIMEIT_VALUE(draw)); TIMEIT_END(draw); #endif - /* cleanup */ - - glBindBuffer(GL_ARRAY_BUFFER, 0); - glDeleteBuffers(1, &vertex_buffer); - - GPU_texture_unbind(sds->tex); - GPU_texture_unbind(sds->tex_shadow); - - if (use_fire) { - GPU_texture_unbind(sds->tex_flame); - GPU_texture_unbind(tex_spec); - GPU_texture_free(tex_spec); - } - MEM_freeN(slicer.verts); - GPU_shader_unbind(); + glDepthMask(gl_depth_write); if (!gl_blend) { glDisable(GL_BLEND); diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index 6463c137c2b..6a58b41a34f 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -647,6 +647,7 @@ static void draw_view_axis(RegionView3D *rv3d, rcti *rect) glDisable(GL_BLEND); } +#ifdef WITH_INPUT_NDOF /* draw center and axis of rotation for ongoing 3D mouse navigation */ static void draw_rotation_guide(RegionView3D *rv3d) { @@ -751,6 +752,7 @@ static void draw_rotation_guide(RegionView3D *rv3d) glDisable(GL_POINT_SMOOTH); glDepthMask(1); } +#endif /* WITH_INPUT_NDOF */ static void draw_view_icon(RegionView3D *rv3d, rcti *rect) { @@ -951,9 +953,11 @@ static void draw_selected_name(Scene *scene, Object *ob, rcti *rect) UI_ThemeColor(TH_TEXT_HI); } else { - /* no object */ - /* color is always white */ - UI_ThemeColor(TH_TEXT_HI); + /* no object */ + if (ED_gpencil_has_keyframe_v3d(scene, NULL, cfra)) + UI_ThemeColor(TH_TIME_GP_KEYFRAME); + else + UI_ThemeColor(TH_TEXT_HI); } if (markern) { @@ -4352,10 +4356,11 @@ static void view3d_main_region_draw_objects(const bContext *C, Scene *scene, Vie BDR_drawSketch(C); } +#ifdef WITH_INPUT_NDOF if ((U.ndof_flag & NDOF_SHOW_GUIDE) && ((rv3d->viewlock & RV3D_LOCKED) == 0) && (rv3d->persp != RV3D_CAMOB)) /* TODO: draw something else (but not this) during fly mode */ draw_rotation_guide(rv3d); - +#endif } static bool is_cursor_visible(Scene *scene) diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index 215d90a8878..9b8ca2d26da 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -1329,6 +1329,8 @@ void VIEW3D_OT_rotate(wmOperatorType *ot) ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR; } +#ifdef WITH_INPUT_NDOF + /** \name NDOF Utility Functions * \{ */ @@ -1894,6 +1896,8 @@ void VIEW3D_OT_ndof_all(struct wmOperatorType *ot) ot->flag = 0; } +#endif /* WITH_INPUT_NDOF */ + /* ************************ viewmove ******************************** */ diff --git a/source/blender/editors/space_view3d/view3d_fly.c b/source/blender/editors/space_view3d/view3d_fly.c index 31377d0fce8..04a6aa215f4 100644 --- a/source/blender/editors/space_view3d/view3d_fly.c +++ b/source/blender/editors/space_view3d/view3d_fly.c @@ -26,8 +26,10 @@ /* defines VIEW3D_OT_fly modal operator */ -//#define NDOF_FLY_DEBUG -//#define NDOF_FLY_DRAW_TOOMUCH /* is this needed for ndof? - commented so redraw doesnt thrash - campbell */ +#ifdef WITH_INPUT_NDOF +//# define NDOF_FLY_DEBUG +//# define NDOF_FLY_DRAW_TOOMUCH /* is this needed for ndof? - commented so redraw doesnt thrash - campbell */ +#endif /* WITH_INPUT_NDOF */ #include "DNA_object_types.h" @@ -203,7 +205,10 @@ typedef struct FlyInfo { int mval[2]; /* latest 2D mouse values */ int center_mval[2]; /* center mouse values */ float width, height; /* camera viewport dimensions */ + +#ifdef WITH_INPUT_NDOF wmNDOFMotionData *ndof; /* latest 3D mouse values */ +#endif /* fly state state */ float speed; /* the speed the view is moving per redraw */ @@ -381,7 +386,10 @@ static bool initFlyInfo(bContext *C, FlyInfo *fly, wmOperator *op, const wmEvent fly->timer = WM_event_add_timer(CTX_wm_manager(C), win, TIMER, 0.01f); copy_v2_v2_int(fly->mval, event->mval); + +#ifdef WITH_INPUT_NDOF fly->ndof = NULL; +#endif fly->time_lastdraw = fly->time_lastwheel = PIL_check_seconds_timer(); @@ -449,8 +457,10 @@ static int flyEnd(bContext *C, FlyInfo *fly) rv3d->rflag &= ~RV3D_NAVIGATING; +#ifdef WITH_INPUT_NDOF if (fly->ndof) MEM_freeN(fly->ndof); +#endif if (fly->state == FLY_CONFIRM) { MEM_freeN(fly); @@ -469,6 +479,7 @@ static void flyEvent(bContext *C, wmOperator *op, FlyInfo *fly, const wmEvent *e else if (event->type == MOUSEMOVE) { copy_v2_v2_int(fly->mval, event->mval); } +#ifdef WITH_INPUT_NDOF else if (event->type == NDOF_MOTION) { /* do these automagically get delivered? yes. */ // puts("ndof motion detected in fly mode!"); @@ -478,15 +489,15 @@ static void flyEvent(bContext *C, wmOperator *op, FlyInfo *fly, const wmEvent *e switch (incoming_ndof->progress) { case P_STARTING: /* start keeping track of 3D mouse position */ -#ifdef NDOF_FLY_DEBUG +# ifdef NDOF_FLY_DEBUG puts("start keeping track of 3D mouse position"); -#endif +# endif /* fall-through */ case P_IN_PROGRESS: /* update 3D mouse position */ -#ifdef NDOF_FLY_DEBUG +# ifdef NDOF_FLY_DEBUG putchar('.'); fflush(stdout); -#endif +# endif if (fly->ndof == NULL) { // fly->ndof = MEM_mallocN(sizeof(wmNDOFMotionData), tag_name); fly->ndof = MEM_dupallocN(incoming_ndof); @@ -498,9 +509,9 @@ static void flyEvent(bContext *C, wmOperator *op, FlyInfo *fly, const wmEvent *e break; case P_FINISHING: /* stop keeping track of 3D mouse position */ -#ifdef NDOF_FLY_DEBUG +# ifdef NDOF_FLY_DEBUG puts("stop keeping track of 3D mouse position"); -#endif +# endif if (fly->ndof) { MEM_freeN(fly->ndof); // free(fly->ndof); @@ -513,6 +524,7 @@ static void flyEvent(bContext *C, wmOperator *op, FlyInfo *fly, const wmEvent *e break; /* should always be one of the above 3 */ } } +#endif /* WITH_INPUT_NDOF */ /* handle modal keymap first */ else if (event->type == EVT_MODAL_MAP) { switch (event->val) { @@ -959,6 +971,7 @@ static int flyApply(bContext *C, FlyInfo *fly) return OPERATOR_FINISHED; } +#ifdef WITH_INPUT_NDOF static void flyApply_ndof(bContext *C, FlyInfo *fly) { Object *lock_ob = ED_view3d_cameracontrol_object_get(fly->v3d_camera_control); @@ -977,6 +990,7 @@ static void flyApply_ndof(bContext *C, FlyInfo *fly) } } } +#endif /* WITH_INPUT_NDOF */ static int fly_invoke(bContext *C, wmOperator *op, const wmEvent *event) { @@ -1023,12 +1037,15 @@ static int fly_modal(bContext *C, wmOperator *op, const wmEvent *event) flyEvent(C, op, fly, event); +#ifdef WITH_INPUT_NDOF if (fly->ndof) { /* 3D mouse overrules [2D mouse + timer] */ if (event->type == NDOF_MOTION) { flyApply_ndof(C, fly); } } - else if (event->type == TIMER && event->customdata == fly->timer) { + else +#endif /* WITH_INPUT_NDOF */ + if (event->type == TIMER && event->customdata == fly->timer) { flyApply(C, fly); } diff --git a/source/blender/editors/space_view3d/view3d_intern.h b/source/blender/editors/space_view3d/view3d_intern.h index bdd2702a6ce..314dadbaa52 100644 --- a/source/blender/editors/space_view3d/view3d_intern.h +++ b/source/blender/editors/space_view3d/view3d_intern.h @@ -46,7 +46,6 @@ struct bContext; struct bMotionPath; struct bPoseChannel; struct Mesh; -struct wmNDOFMotionData; struct wmOperatorType; struct wmWindowManager; struct wmKeyConfig; @@ -76,10 +75,12 @@ void VIEW3D_OT_dolly(struct wmOperatorType *ot); void VIEW3D_OT_zoom_camera_1_to_1(struct wmOperatorType *ot); void VIEW3D_OT_move(struct wmOperatorType *ot); void VIEW3D_OT_rotate(struct wmOperatorType *ot); +#ifdef WITH_INPUT_NDOF void VIEW3D_OT_ndof_orbit(struct wmOperatorType *ot); void VIEW3D_OT_ndof_orbit_zoom(struct wmOperatorType *ot); void VIEW3D_OT_ndof_pan(struct wmOperatorType *ot); void VIEW3D_OT_ndof_all(struct wmOperatorType *ot); +#endif /* WITH_INPUT_NDOF */ void VIEW3D_OT_view_all(struct wmOperatorType *ot); void VIEW3D_OT_viewnumpad(struct wmOperatorType *ot); void VIEW3D_OT_view_selected(struct wmOperatorType *ot); @@ -111,11 +112,15 @@ void view3d_orbit_apply_dyn_ofs( float r_ofs[3], const float ofs_old[3], const float viewquat_old[4], const float viewquat_new[4], const float dyn_ofs[3]); +#ifdef WITH_INPUT_NDOF +struct wmNDOFMotionData; + void view3d_ndof_fly( const struct wmNDOFMotionData *ndof, struct View3D *v3d, struct RegionView3D *rv3d, const bool use_precision, const short protectflag, bool *r_has_translate, bool *r_has_rotate); +#endif /* WITH_INPUT_NDOF */ /* view3d_fly.c */ void view3d_keymap(struct wmKeyConfig *keyconf); diff --git a/source/blender/editors/space_view3d/view3d_ops.c b/source/blender/editors/space_view3d/view3d_ops.c index b273f46fca3..cfeb8af280e 100644 --- a/source/blender/editors/space_view3d/view3d_ops.c +++ b/source/blender/editors/space_view3d/view3d_ops.c @@ -164,10 +164,12 @@ void view3d_operatortypes(void) WM_operatortype_append(VIEW3D_OT_zoom); WM_operatortype_append(VIEW3D_OT_zoom_camera_1_to_1); WM_operatortype_append(VIEW3D_OT_dolly); +#ifdef WITH_INPUT_NDOF WM_operatortype_append(VIEW3D_OT_ndof_orbit_zoom); WM_operatortype_append(VIEW3D_OT_ndof_orbit); WM_operatortype_append(VIEW3D_OT_ndof_pan); WM_operatortype_append(VIEW3D_OT_ndof_all); +#endif /* WITH_INPUT_NDOF */ WM_operatortype_append(VIEW3D_OT_view_all); WM_operatortype_append(VIEW3D_OT_viewnumpad); WM_operatortype_append(VIEW3D_OT_view_orbit); @@ -363,7 +365,7 @@ void view3d_keymap(wmKeyConfig *keyconf) WM_keymap_add_item(keymap, "VIEW3D_OT_localview", PADSLASHKEY, KM_PRESS, 0, 0); - /* NDOF: begin */ +#ifdef WITH_INPUT_NDOF /* note: positioned here so keymaps show keyboard keys if assigned */ /* 3D mouse */ WM_keymap_add_item(keymap, "VIEW3D_OT_ndof_orbit_zoom", NDOF_MOTION, 0, 0, 0); @@ -392,8 +394,7 @@ void view3d_keymap(wmKeyConfig *keyconf) kmi = WM_keymap_add_item(keymap, "VIEW3D_OT_viewnumpad", NDOF_BUTTON_TOP, KM_PRESS, KM_SHIFT, 0); RNA_enum_set(kmi->ptr, "type", RV3D_VIEW_TOP); RNA_boolean_set(kmi->ptr, "align_active", true); - /* NDOF: end */ - +#endif /* WITH_INPUT_NDOF */ /* layers, shift + alt are properties set in invoke() */ RNA_int_set(WM_keymap_add_item(keymap, "VIEW3D_OT_layers", ACCENTGRAVEKEY, KM_PRESS, 0, 0)->ptr, "nr", 0); diff --git a/source/blender/editors/space_view3d/view3d_ruler.c b/source/blender/editors/space_view3d/view3d_ruler.c index a55849e5633..3c13ab9d595 100644 --- a/source/blender/editors/space_view3d/view3d_ruler.c +++ b/source/blender/editors/space_view3d/view3d_ruler.c @@ -297,23 +297,36 @@ static bool view3d_ruler_to_gpencil(bContext *C, RulerInfo *ruler_info) bGPDlayer *gpl; bGPDframe *gpf; bGPDstroke *gps; + bGPDpalette *palette; + bGPDpalettecolor *palcolor; RulerItem *ruler_item; const char *ruler_name = RULER_ID; bool changed = false; if (scene->gpd == NULL) { - scene->gpd = gpencil_data_addnew("GPencil"); + scene->gpd = BKE_gpencil_data_addnew("GPencil"); } gpl = BLI_findstring(&scene->gpd->layers, ruler_name, offsetof(bGPDlayer, info)); if (gpl == NULL) { - gpl = gpencil_layer_addnew(scene->gpd, ruler_name, false); + gpl = BKE_gpencil_layer_addnew(scene->gpd, ruler_name, false); gpl->thickness = 1; gpl->flag |= GP_LAYER_HIDE; } - gpf = gpencil_layer_getframe(gpl, CFRA, true); - free_gpencil_strokes(gpf); + /* try to get active palette or create a new one */ + palette = BKE_gpencil_palette_getactive(scene->gpd); + if (palette == NULL) { + palette = BKE_gpencil_palette_addnew(scene->gpd, DATA_("GP_Palette"), true); + } + /* try to get color with the ruler name or create a new one */ + palcolor = BKE_gpencil_palettecolor_getbyname(palette, (char *)ruler_name); + if (palcolor == NULL) { + palcolor = BKE_gpencil_palettecolor_addnew(palette, (char *)ruler_name, true); + } + + gpf = BKE_gpencil_layer_getframe(gpl, CFRA, true); + BKE_gpencil_free_strokes(gpf); for (ruler_item = ruler_info->items.first; ruler_item; ruler_item = ruler_item->next) { bGPDspoint *pt; @@ -342,6 +355,10 @@ static bool view3d_ruler_to_gpencil(bContext *C, RulerInfo *ruler_info) } } gps->flag = GP_STROKE_3DSPACE; + gps->thickness = 3; + /* assign color to stroke */ + BLI_strncpy(gps->colorname, palcolor->info, sizeof(gps->colorname)); + gps->palcolor = palcolor; BLI_addtail(&gpf->strokes, gps); changed = true; } @@ -360,7 +377,7 @@ static bool view3d_ruler_from_gpencil(bContext *C, RulerInfo *ruler_info) gpl = BLI_findstring(&scene->gpd->layers, ruler_name, offsetof(bGPDlayer, info)); if (gpl) { bGPDframe *gpf; - gpf = gpencil_layer_getframe(gpl, CFRA, false); + gpf = BKE_gpencil_layer_getframe(gpl, CFRA, false); if (gpf) { bGPDstroke *gps; for (gps = gpf->strokes.first; gps; gps = gps->next) { diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c index a460d8900b4..3239d07553f 100644 --- a/source/blender/editors/space_view3d/view3d_select.c +++ b/source/blender/editors/space_view3d/view3d_select.c @@ -1954,6 +1954,9 @@ static int do_armature_box_select(ViewContext *vc, rcti *rect, bool select, bool int index = buffer[(4 * a) + 3]; if (index != -1) { ebone = BLI_findlink(arm->edbo, index & ~(BONESEL_ANY)); + if ((index & 0xFFFF0000) == 0) { + continue; + } if ((select == false) || ((ebone->flag & BONE_UNSELECTABLE) == 0)) { if (index & BONESEL_TIP) { ebone->flag |= BONE_DONE; diff --git a/source/blender/editors/space_view3d/view3d_walk.c b/source/blender/editors/space_view3d/view3d_walk.c index c9e4bb301b8..17c08ed4205 100644 --- a/source/blender/editors/space_view3d/view3d_walk.c +++ b/source/blender/editors/space_view3d/view3d_walk.c @@ -59,8 +59,10 @@ #include "view3d_intern.h" /* own include */ -//#define NDOF_WALK_DEBUG -//#define NDOF_WALK_DRAW_TOOMUCH /* is this needed for ndof? - commented so redraw doesnt thrash - campbell */ +#ifdef WITH_INPUT_NDOF +//# define NDOF_WALK_DEBUG +//# define NDOF_WALK_DRAW_TOOMUCH /* is this needed for ndof? - commented so redraw doesnt thrash - campbell */ +#endif #define USE_TABLET_SUPPORT @@ -254,7 +256,10 @@ typedef struct WalkInfo { int prev_mval[2]; /* previous 2D mouse values */ int center_mval[2]; /* center mouse values */ int moffset[2]; + +#ifdef WITH_INPUT_NDOF wmNDOFMotionData *ndof; /* latest 3D mouse values */ +#endif /* walk state state */ float base_speed; /* the base speed without run/slow down modifications */ @@ -572,7 +577,9 @@ static bool initWalkInfo(bContext *C, WalkInfo *walk, wmOperator *op) walk->timer = WM_event_add_timer(CTX_wm_manager(C), win, TIMER, 0.01f); +#ifdef WITH_INPUT_NDOF walk->ndof = NULL; +#endif walk->time_lastdraw = PIL_check_seconds_timer(); @@ -639,8 +646,10 @@ static int walkEnd(bContext *C, WalkInfo *walk) rv3d->rflag &= ~RV3D_NAVIGATING; +#ifdef WITH_INPUT_NDOF if (walk->ndof) MEM_freeN(walk->ndof); +#endif /* restore the cursor */ WM_cursor_modal_restore(win); @@ -743,6 +752,7 @@ static void walkEvent(bContext *C, wmOperator *op, WalkInfo *walk, const wmEvent } } } +#ifdef WITH_INPUT_NDOF else if (event->type == NDOF_MOTION) { /* do these automagically get delivered? yes. */ // puts("ndof motion detected in walk mode!"); @@ -752,15 +762,15 @@ static void walkEvent(bContext *C, wmOperator *op, WalkInfo *walk, const wmEvent switch (incoming_ndof->progress) { case P_STARTING: /* start keeping track of 3D mouse position */ -#ifdef NDOF_WALK_DEBUG +# ifdef NDOF_WALK_DEBUG puts("start keeping track of 3D mouse position"); -#endif +# endif /* fall-through */ case P_IN_PROGRESS: /* update 3D mouse position */ -#ifdef NDOF_WALK_DEBUG +# ifdef NDOF_WALK_DEBUG putchar('.'); fflush(stdout); -#endif +# endif if (walk->ndof == NULL) { // walk->ndof = MEM_mallocN(sizeof(wmNDOFMotionData), tag_name); walk->ndof = MEM_dupallocN(incoming_ndof); @@ -772,9 +782,9 @@ static void walkEvent(bContext *C, wmOperator *op, WalkInfo *walk, const wmEvent break; case P_FINISHING: /* stop keeping track of 3D mouse position */ -#ifdef NDOF_WALK_DEBUG +# ifdef NDOF_WALK_DEBUG puts("stop keeping track of 3D mouse position"); -#endif +# endif if (walk->ndof) { MEM_freeN(walk->ndof); // free(walk->ndof); @@ -789,6 +799,7 @@ static void walkEvent(bContext *C, wmOperator *op, WalkInfo *walk, const wmEvent break; /* should always be one of the above 3 */ } } +#endif /* WITH_INPUT_NDOF */ /* handle modal keymap first */ else if (event->type == EVT_MODAL_MAP) { switch (event->val) { @@ -1323,6 +1334,7 @@ static int walkApply(bContext *C, wmOperator *op, WalkInfo *walk) #undef WALK_BOOST_FACTOR } +#ifdef WITH_INPUT_NDOF static void walkApply_ndof(bContext *C, WalkInfo *walk) { Object *lock_ob = ED_view3d_cameracontrol_object_get(walk->v3d_camera_control); @@ -1341,6 +1353,7 @@ static void walkApply_ndof(bContext *C, WalkInfo *walk) } } } +#endif /* WITH_INPUT_NDOF */ /****** walk operator ******/ static int walk_invoke(bContext *C, wmOperator *op, const wmEvent *event) @@ -1388,12 +1401,15 @@ static int walk_modal(bContext *C, wmOperator *op, const wmEvent *event) walkEvent(C, op, walk, event); +#ifdef WITH_INPUT_NDOF if (walk->ndof) { /* 3D mouse overrules [2D mouse + timer] */ if (event->type == NDOF_MOTION) { walkApply_ndof(C, walk); } } - else if (event->type == TIMER && event->customdata == walk->timer) { + else +#endif /* WITH_INPUT_NDOF */ + if (event->type == TIMER && event->customdata == walk->timer) { walkApply(C, op, walk); } diff --git a/source/blender/editors/transform/transform_conversions.c b/source/blender/editors/transform/transform_conversions.c index ad2b40bfef8..9c266890d6d 100644 --- a/source/blender/editors/transform/transform_conversions.c +++ b/source/blender/editors/transform/transform_conversions.c @@ -3292,7 +3292,7 @@ static void posttrans_gpd_clean(bGPdata *gpd) for (gpf = gpl->frames.first; gpf; gpf = gpfn) { gpfn = gpf->next; if (gpfn && gpf->framenum == gpfn->framenum) { - gpencil_layer_delframe(gpl, gpf); + BKE_gpencil_layer_delframe(gpl, gpf); } } } @@ -7755,7 +7755,7 @@ static void createTransGPencil(bContext *C, TransInfo *t) */ // XXX: should this be allowed when framelock is enabled? if (gpf->framenum != cfra) { - gpf = gpencil_frame_addcopy(gpl, cfra); + gpf = BKE_gpencil_frame_addcopy(gpl, cfra); /* in some weird situations (framelock enabled) return NULL */ if (gpf == NULL) { continue; diff --git a/source/blender/editors/transform/transform_ops.c b/source/blender/editors/transform/transform_ops.c index 6e399d9fde3..cbe58ddf586 100644 --- a/source/blender/editors/transform/transform_ops.c +++ b/source/blender/editors/transform/transform_ops.c @@ -386,7 +386,7 @@ static int transform_modal(bContext *C, wmOperator *op, const wmEvent *event) TransInfo *t = op->customdata; const enum TfmMode mode_prev = t->mode; -#if 0 +#if defined(WITH_INPUT_NDOF) && 0 // stable 2D mouse coords map to different 3D coords while the 3D mouse is active // in other words, 2D deltas are no longer good enough! // disable until individual 'transformers' behave better diff --git a/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp b/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp index ea5a55731c3..1f5e2b63bfa 100644 --- a/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp +++ b/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp @@ -807,6 +807,7 @@ void BlenderFileLoader::insertShapeNode(ObjectInstanceRen *obi, int id) // sets the id of the rep rep->setId(Id(id, 0)); rep->setName(obi->ob->id.name + 2); + rep->setLibraryPath(obi->ob->id.lib ? obi->ob->id.lib->name : NULL); const BBox<Vec3r> bbox = BBox<Vec3r>(Vec3r(ls.minBBox[0], ls.minBBox[1], ls.minBBox[2]), Vec3r(ls.maxBBox[0], ls.maxBBox[1], ls.maxBBox[2])); diff --git a/source/blender/freestyle/intern/python/BPy_ViewShape.cpp b/source/blender/freestyle/intern/python/BPy_ViewShape.cpp index 253bf278478..f2f53159fcf 100644 --- a/source/blender/freestyle/intern/python/BPy_ViewShape.cpp +++ b/source/blender/freestyle/intern/python/BPy_ViewShape.cpp @@ -296,6 +296,19 @@ static PyObject *ViewShape_name_get(BPy_ViewShape *self, void *UNUSED(closure)) return PyUnicode_FromString(self->vs->getName()); } +PyDoc_STRVAR(ViewShape_library_path_doc, +"The library path of the ViewShape.\n" +"\n" +":type: str, or None if the ViewShape is not part of a library"); + +static PyObject *ViewShape_library_path_get(BPy_ViewShape *self, void *UNUSED(closure)) +{ + const char *name = self->vs->getLibraryPath(); + if (!name) + Py_RETURN_NONE; + return PyUnicode_FromString(name); +} + PyDoc_STRVAR(ViewShape_id_doc, "The Id of this ViewShape.\n" "\n" @@ -313,6 +326,7 @@ static PyGetSetDef BPy_ViewShape_getseters[] = { (char *)ViewShape_vertices_doc, NULL}, {(char *)"edges", (getter)ViewShape_edges_get, (setter)ViewShape_edges_set, (char *)ViewShape_edges_doc, NULL}, {(char *)"name", (getter)ViewShape_name_get, (setter)NULL, (char *)ViewShape_name_doc, NULL}, + {(char *)"library_path", (getter)ViewShape_library_path_get, (setter)NULL, (char *)ViewShape_library_path_doc, NULL}, {(char *)"id", (getter)ViewShape_id_get, (setter)NULL, (char *)ViewShape_id_doc, NULL}, {NULL, NULL, NULL, NULL, NULL} /* Sentinel */ }; diff --git a/source/blender/freestyle/intern/scene_graph/Rep.h b/source/blender/freestyle/intern/scene_graph/Rep.h index c5036cdb153..773eb2d3278 100644 --- a/source/blender/freestyle/intern/scene_graph/Rep.h +++ b/source/blender/freestyle/intern/scene_graph/Rep.h @@ -48,6 +48,8 @@ public: inline Rep() : BaseObject() { _Id = 0; + _Name = 0; + _LibraryPath = 0; _FrsMaterial = 0; } @@ -55,6 +57,7 @@ public: { _Id = iBrother._Id; _Name = iBrother._Name; + _LibraryPath = iBrother._LibraryPath; if (0 == iBrother._FrsMaterial) _FrsMaterial = 0; else @@ -68,6 +71,7 @@ public: std::swap(_BBox, ioOther._BBox); std::swap(_Id, ioOther._Id); std::swap(_Name, ioOther._Name); + std::swap(_LibraryPath, ioOther._LibraryPath); std::swap(_FrsMaterial, ioOther._FrsMaterial); } @@ -76,6 +80,7 @@ public: if (&iBrother != this) { _Id = iBrother._Id; _Name = iBrother._Name; + _LibraryPath = iBrother._LibraryPath; if (0 == iBrother._FrsMaterial) { _FrsMaterial = 0; } @@ -132,6 +137,11 @@ public: return _Name; } + inline const char *getLibraryPath() const + { + return _LibraryPath; + } + inline const FrsMaterial *frs_material() const { return _FrsMaterial; @@ -153,7 +163,12 @@ public: _Name = name; } - inline void setFrsMaterial(const FrsMaterial& iMaterial) + inline void setLibraryPath(const char *path) + { + _LibraryPath = path; + } + + inline void setFrsMaterial(const FrsMaterial& iMaterial) { _FrsMaterial = new FrsMaterial(iMaterial); } @@ -162,6 +177,7 @@ private: BBox<Vec3f> _BBox; Id _Id; const char *_Name; + const char *_LibraryPath; FrsMaterial *_FrsMaterial; }; diff --git a/source/blender/freestyle/intern/stroke/StrokeRep.cpp b/source/blender/freestyle/intern/stroke/StrokeRep.cpp index ab06e207331..028127897ff 100644 --- a/source/blender/freestyle/intern/stroke/StrokeRep.cpp +++ b/source/blender/freestyle/intern/stroke/StrokeRep.cpp @@ -135,11 +135,11 @@ void Strip::createStrip (const vector<StrokeVertex*>& iStrokeVertices) int orientationErrors = 0; //special case of first vertex - v = iStrokeVertices.begin(); + v2 = v = iStrokeVertices.begin(); + ++v2; sv = *v; vPrev = v; //in case the stroke has only 2 vertices; - ++v; - sv2 = *v; + sv2 = *v2; Vec2r dir(sv2->getPoint() - sv->getPoint()); Vec2r orthDir(-dir[1], dir[0]); if (orthDir.norm() > ZERO) @@ -189,11 +189,7 @@ void Strip::createStrip (const vector<StrokeVertex*>& iStrokeVertices) int i = 2; // 2 because we have already processed the first vertex - for (vend = iStrokeVertices.end(); v != vend; ++v) { - v2 = v; - ++v2; - if (v2 == vend) - break; + for (vend = iStrokeVertices.end(), ++v, ++v2; v2 != vend; vPrev = v++, ++v2) { sv = (*v); sv2 = (*v2); svPrev = (*vPrev); @@ -289,8 +285,6 @@ void Strip::createStrip (const vector<StrokeVertex*>& iStrokeVertices) { _vertices[i - 1]->setPoint2d(p - thickness[0] * stripDir); } - - vPrev = v; } // end of for //special case of last vertex diff --git a/source/blender/freestyle/intern/view_map/BoxGrid.cpp b/source/blender/freestyle/intern/view_map/BoxGrid.cpp index ae22a26ec9b..34fa2b14379 100644 --- a/source/blender/freestyle/intern/view_map/BoxGrid.cpp +++ b/source/blender/freestyle/intern/view_map/BoxGrid.cpp @@ -77,11 +77,11 @@ BoxGrid::Iterator::Iterator (BoxGrid& grid, Vec3r& center, real /*epsilon*/) // Find target cell _cell = grid.findCell(_target); #if BOX_GRID_LOGGING - if (G.debug & G_DEBUG_FREESTYLE) { - cout << "Searching for occluders of edge centered at " << _target << " in cell [" << - 1_cell->boundary[0] << ", " << _cell->boundary[1] << ", " << _cell->boundary[2] << - ", " << _cell->boundary[3] << "] (" << _cell->faces.size() << " occluders)" << endl; - } + if (G.debug & G_DEBUG_FREESTYLE) { + cout << "Searching for occluders of edge centered at " << _target << " in cell [" << + 1_cell->boundary[0] << ", " << _cell->boundary[1] << ", " << _cell->boundary[2] << + ", " << _cell->boundary[3] << "] (" << _cell->faces.size() << " occluders)" << endl; + } #endif // Set iterator diff --git a/source/blender/freestyle/intern/view_map/Silhouette.h b/source/blender/freestyle/intern/view_map/Silhouette.h index b9924e6ad95..9d373107bfa 100644 --- a/source/blender/freestyle/intern/view_map/Silhouette.h +++ b/source/blender/freestyle/intern/view_map/Silhouette.h @@ -1416,6 +1416,7 @@ private: vector<FEdge*> _edgesList; // list of all edges Id _Id; const char *_Name; + const char *_LibraryPath; BBox<Vec3r> _BBox; vector<FrsMaterial> _FrsMaterials; @@ -1436,6 +1437,7 @@ public: _importance = 0.0f; _ViewShape = NULL; _Name = NULL; + _LibraryPath = NULL; } /*! Copy constructor */ @@ -1444,6 +1446,7 @@ public: userdata = NULL; _Id = iBrother._Id; _Name = iBrother._Name; + _LibraryPath = iBrother._LibraryPath; _BBox = iBrother.bbox(); _FrsMaterials = iBrother._FrsMaterials; _importance = iBrother._importance; @@ -1893,6 +1896,12 @@ public: return _Name; } + /*! Returns the library path of the Shape. */ + inline const char *getLibraryPath() const + { + return _LibraryPath; + } + /* Modififers */ /*! Sets the Id of the shape.*/ inline void setId(Id id) @@ -1906,6 +1915,12 @@ public: _Name = name; } + /*! Sets the library path of the shape.*/ + inline void setLibraryPath(const char *path) + { + _LibraryPath = path; + } + /*! Sets the list of materials for the shape */ inline void setFrsMaterials(const vector<FrsMaterial>& iMaterials) { diff --git a/source/blender/freestyle/intern/view_map/ViewMap.h b/source/blender/freestyle/intern/view_map/ViewMap.h index 74297e1dbfd..8b73c8aac3a 100644 --- a/source/blender/freestyle/intern/view_map/ViewMap.h +++ b/source/blender/freestyle/intern/view_map/ViewMap.h @@ -1565,12 +1565,18 @@ public: return _SShape->getId(); } - /*! Returns the ViewShape id. */ + /*! Returns the ViewShape name. */ inline const char *getName() const { return _SShape->getName(); } + /*! Returns the ViewShape library path. */ + inline const char *getLibraryPath() const + { + return _SShape->getLibraryPath(); + } + /* modifiers */ /*! Sets the SShape on top of which the ViewShape is built. */ inline void setSShape(SShape *iSShape) diff --git a/source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp b/source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp index 9ca021475b2..380bb0dd3ca 100644 --- a/source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp +++ b/source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp @@ -1204,6 +1204,7 @@ void ViewMapBuilder::computeInitialViewEdges(WingedEdge& we) psShape = new SShape; psShape->setId((*it)->GetId()); psShape->setName((*it)->getName()); + psShape->setLibraryPath((*it)->getLibraryPath()); psShape->setFrsMaterials((*it)->frs_materials()); // FIXME // create the view shape @@ -2098,7 +2099,7 @@ int ViewMapBuilder::ComputeRayCastingVisibility(FEdge *fe, Grid *iGrid, real eps } // Find occludee - FindOccludee(fe, iGrid, epsilon, oaPolygon, timestamp, u, center, edge, origin, faceVertices); + FindOccludee(fe, iGrid, epsilon, oaPolygon, timestamp, u, center, origin, edge, faceVertices); return qi; } diff --git a/source/blender/freestyle/intern/winged_edge/WEdge.cpp b/source/blender/freestyle/intern/winged_edge/WEdge.cpp index 99aa2d22239..7bec5ba1d6e 100644 --- a/source/blender/freestyle/intern/winged_edge/WEdge.cpp +++ b/source/blender/freestyle/intern/winged_edge/WEdge.cpp @@ -471,6 +471,7 @@ WShape::WShape(WShape& iBrother) { _Id = iBrother.GetId(); _Name = iBrother._Name; + _LibraryPath = iBrother._LibraryPath; _FrsMaterials = iBrother._FrsMaterials; #if 0 _meanEdgeSize = iBrother._meanEdgeSize; diff --git a/source/blender/freestyle/intern/winged_edge/WEdge.h b/source/blender/freestyle/intern/winged_edge/WEdge.h index 8001342775b..14109fba843 100644 --- a/source/blender/freestyle/intern/winged_edge/WEdge.h +++ b/source/blender/freestyle/intern/winged_edge/WEdge.h @@ -1025,6 +1025,7 @@ protected: vector<WFace *> _FaceList; int _Id; const char *_Name; + const char *_LibraryPath; static unsigned _SceneCurrentId; #if 0 Vec3f _min; @@ -1043,6 +1044,8 @@ public: #endif _Id = _SceneCurrentId; _SceneCurrentId++; + _Name = 0; + _LibraryPath = 0; } /*! copy constructor */ @@ -1127,6 +1130,11 @@ public: return _Name; } + inline const char *getLibraryPath() const + { + return _LibraryPath; + } + /*! modifiers */ static inline void setCurrentId(const unsigned id) { @@ -1176,6 +1184,11 @@ public: _Name = name; } + inline void setLibraryPath(const char *path) + { + _LibraryPath = path; + } + /*! designed to build a specialized WFace for use in MakeFace */ virtual WFace *instanciateFace() const { diff --git a/source/blender/freestyle/intern/winged_edge/WXEdgeBuilder.cpp b/source/blender/freestyle/intern/winged_edge/WXEdgeBuilder.cpp index 78773a9680d..dfdeedef2e1 100644 --- a/source/blender/freestyle/intern/winged_edge/WXEdgeBuilder.cpp +++ b/source/blender/freestyle/intern/winged_edge/WXEdgeBuilder.cpp @@ -42,6 +42,7 @@ void WXEdgeBuilder::visitIndexedFaceSet(IndexedFaceSet& ifs) } shape->setId(ifs.getId().getFirst()); shape->setName(ifs.getName()); + shape->setLibraryPath(ifs.getLibraryPath()); //ifs.setId(shape->GetId()); } diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index 6bb94b334b5..d00efc3979c 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -93,6 +93,7 @@ set(SRC shaders/gpu_shader_vsm_store_vert.glsl shaders/gpu_shader_fx_depth_resolve.glsl shaders/gpu_shader_fx_colormanage_frag.glsl + shaders/gpu_shader_fire_frag.glsl shaders/gpu_shader_smoke_frag.glsl shaders/gpu_shader_smoke_vert.glsl shaders/gpu_shader_probe_sh_compute_frag.glsl @@ -123,6 +124,7 @@ set(SRC ) data_to_c_simple(shaders/gpu_shader_geometry.glsl SRC) +data_to_c_simple(shaders/gpu_shader_fire_frag.glsl SRC) data_to_c_simple(shaders/gpu_shader_smoke_frag.glsl SRC) data_to_c_simple(shaders/gpu_shader_smoke_vert.glsl SRC) data_to_c_simple(shaders/gpu_shader_material.glsl SRC) diff --git a/source/blender/gpu/GPU_debug.h b/source/blender/gpu/GPU_debug.h index 2c1728bfff1..61b2bc591ce 100644 --- a/source/blender/gpu/GPU_debug.h +++ b/source/blender/gpu/GPU_debug.h @@ -34,8 +34,6 @@ #include "GPU_glew.h" -#include "BLI_utildefines.h" - #ifdef __cplusplus extern "C" { #endif @@ -62,7 +60,7 @@ void GPU_assert_no_gl_errors(const char *file, int line, const char *str); /* inserts a debug marker message for the debug context messaging system */ -void GPU_string_marker(size_t size, const char *str); +void GPU_string_marker(const char *str); #ifdef __cplusplus } diff --git a/source/blender/gpu/intern/gpu_codegen.c b/source/blender/gpu/intern/gpu_codegen.c index 56d6f74bbf0..aeb656b238b 100644 --- a/source/blender/gpu/intern/gpu_codegen.c +++ b/source/blender/gpu/intern/gpu_codegen.c @@ -908,7 +908,7 @@ static char *code_generate_geometry(ListBase *nodes, bool use_opensubdiv) if (input->attribtype == CD_MTFACE) { BLI_dynstr_appendf( ds, - "\tINTERP_FACE_VARYING_2(var%d, " + "\tINTERP_FACE_VARYING_ATT_2(var%d, " "int(texelFetch(FVarDataOffsetBuffer, fvar%d_offset).r), st);\n", input->attribid, input->attribid); diff --git a/source/blender/gpu/intern/gpu_debug.c b/source/blender/gpu/intern/gpu_debug.c index be9285727fe..d632e767ca9 100644 --- a/source/blender/gpu/intern/gpu_debug.c +++ b/source/blender/gpu/intern/gpu_debug.c @@ -29,7 +29,9 @@ * \ingroup gpu */ +#include "BLI_utildefines.h" #include "BLI_sys_types.h" +#include "BLI_system.h" #include "BKE_global.h" @@ -159,17 +161,73 @@ const char *gpuErrorString(GLenum err) #endif +static const char* source_name(GLenum source) +{ + switch (source) { + case GL_DEBUG_SOURCE_API: return "API"; + case GL_DEBUG_SOURCE_WINDOW_SYSTEM: return "window system"; + case GL_DEBUG_SOURCE_SHADER_COMPILER: return "shader compiler"; + case GL_DEBUG_SOURCE_THIRD_PARTY: return "3rd party"; + case GL_DEBUG_SOURCE_APPLICATION: return "application"; + case GL_DEBUG_SOURCE_OTHER: return "other"; + default: return "???"; + } +} + +static const char* message_type_name(GLenum message) +{ + switch (message) { + case GL_DEBUG_TYPE_ERROR: return "error"; + case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: return "deprecated behavior"; + case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: return "undefined behavior"; + case GL_DEBUG_TYPE_PORTABILITY: return "portability"; + case GL_DEBUG_TYPE_PERFORMANCE: return "performance"; + case GL_DEBUG_TYPE_OTHER: return "other"; + case GL_DEBUG_TYPE_MARKER: return "marker"; /* KHR has this, ARB does not */ + default: return "???"; + } +} + +static const char* category_name_amd(GLenum category) +{ + switch (category) { + case GL_DEBUG_CATEGORY_API_ERROR_AMD: return "API error"; + case GL_DEBUG_CATEGORY_WINDOW_SYSTEM_AMD: return "window system"; + case GL_DEBUG_CATEGORY_DEPRECATION_AMD: return "deprecated behavior"; + case GL_DEBUG_CATEGORY_UNDEFINED_BEHAVIOR_AMD: return "undefined behavior"; + case GL_DEBUG_CATEGORY_PERFORMANCE_AMD: return "performance"; + case GL_DEBUG_CATEGORY_SHADER_COMPILER_AMD: return "shader compiler"; + case GL_DEBUG_CATEGORY_APPLICATION_AMD: return "application"; + case GL_DEBUG_CATEGORY_OTHER_AMD: return "other"; + default: return "???"; + } +} + + static void APIENTRY gpu_debug_proc( GLenum source, GLenum type, GLuint UNUSED(id), - GLenum UNUSED(severity), GLsizei UNUSED(length), + GLenum severity, GLsizei UNUSED(length), const GLchar *message, const GLvoid *UNUSED(userParm)) { - if (source == GL_DEBUG_SOURCE_API && type == GL_DEBUG_TYPE_ERROR) { - fprintf(stderr, "GL: %s\n", message); - fflush(stderr); + if (type == GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR) { + /* Blender 2.7x uses OpenGL 2.1, we don't care if features are deprecated */ + return; } - else if (G.debug_value == 20) { - fprintf(stderr, "GL: %s\n", message); + + bool backtrace = false; + + switch (severity) { + case GL_DEBUG_SEVERITY_HIGH: + backtrace = true; + /* fall through */ + case GL_DEBUG_SEVERITY_MEDIUM: + case GL_DEBUG_SEVERITY_LOW: + case GL_DEBUG_SEVERITY_NOTIFICATION: /* KHR has this, ARB does not */ + fprintf(stderr, "GL %s %s: %s\n", source_name(source), message_type_name(type), message); + } + + if (backtrace) { + BLI_system_backtrace(stderr); fflush(stderr); } } @@ -177,11 +235,30 @@ static void APIENTRY gpu_debug_proc( #ifndef GLEW_ES_ONLY static void APIENTRY gpu_debug_proc_amd( - GLuint UNUSED(id), GLenum UNUSED(category), - GLenum UNUSED(severity), GLsizei UNUSED(length), + GLuint UNUSED(id), GLenum category, + GLenum severity, GLsizei UNUSED(length), const GLchar *message, GLvoid *UNUSED(userParm)) { - fprintf(stderr, "GL: %s\n", message); + if (category == GL_DEBUG_CATEGORY_DEPRECATION_AMD) { + /* Blender 2.7x uses OpenGL 2.1, we don't care if features are deprecated */ + return; + } + + bool backtrace = false; + + switch (severity) { + case GL_DEBUG_SEVERITY_HIGH: + backtrace = true; + /* fall through */ + case GL_DEBUG_SEVERITY_MEDIUM: + case GL_DEBUG_SEVERITY_LOW: + fprintf(stderr, "GL %s: %s\n", category_name_amd(category), message); + } + + if (backtrace) { + BLI_system_backtrace(stderr); + fflush(stderr); + } } #endif @@ -194,36 +271,44 @@ void gpu_debug_init(void) #if !defined(WITH_GLEW_ES) && !defined(GLEW_ES_ONLY) if (GLEW_VERSION_4_3) { + fprintf(stderr, "Using OpenGL 4.3 debug facilities\n"); glEnable(GL_DEBUG_OUTPUT); + glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); glDebugMessageCallback((GLDEBUGPROC)gpu_debug_proc, mxGetCurrentContext()); glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, GL_TRUE); - GPU_string_marker(sizeof(success), success); + GPU_string_marker(success); return; } #endif if (GLEW_KHR_debug) { #ifndef GLEW_ES_ONLY + fprintf(stderr, "Using KHR_debug extension\n"); + glEnable(GL_DEBUG_OUTPUT); + glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); glDebugMessageCallback((GLDEBUGPROC)gpu_debug_proc, mxGetCurrentContext()); glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, GL_TRUE); - GPU_string_marker(sizeof(success), success); + GPU_string_marker(success); #endif return; } #ifndef GLEW_ES_ONLY if (GLEW_ARB_debug_output) { + fprintf(stderr, "Using ARB_debug_output extension\n"); + glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); glDebugMessageCallbackARB((GLDEBUGPROCARB)gpu_debug_proc, mxGetCurrentContext()); glDebugMessageControlARB(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, GL_TRUE); - GPU_string_marker(sizeof(success), success); + GPU_string_marker(success); return; } if (GLEW_AMD_debug_output) { + fprintf(stderr, "Using AMD_debug_output extension\n"); glDebugMessageCallbackAMD(gpu_debug_proc_amd, mxGetCurrentContext()); glDebugMessageEnableAMD(GL_DONT_CARE, GL_DONT_CARE, 0, NULL, GL_TRUE); - GPU_string_marker(sizeof(success), success); + GPU_string_marker(success); return; } @@ -271,14 +356,14 @@ void gpu_debug_exit(void) return; } -void GPU_string_marker(size_t length, const char *buf) +void GPU_string_marker(const char *buf) { #ifndef WITH_GLEW_ES #ifndef GLEW_ES_ONLY if (GLEW_VERSION_4_3) { glDebugMessageInsert( GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_MARKER, 0, - GL_DEBUG_SEVERITY_NOTIFICATION, length, buf); + GL_DEBUG_SEVERITY_NOTIFICATION, -1, buf); return; } @@ -289,7 +374,7 @@ void GPU_string_marker(size_t length, const char *buf) #ifndef GLEW_ES_ONLY glDebugMessageInsert( GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_MARKER, 0, - GL_DEBUG_SEVERITY_NOTIFICATION, length, buf); + GL_DEBUG_SEVERITY_NOTIFICATION, -1, buf); #endif return; } @@ -298,7 +383,7 @@ void GPU_string_marker(size_t length, const char *buf) if (GLEW_ARB_debug_output) { glDebugMessageInsertARB( GL_DEBUG_SOURCE_APPLICATION_ARB, GL_DEBUG_TYPE_OTHER_ARB, 0, - GL_DEBUG_SEVERITY_LOW_ARB, length, buf); + GL_DEBUG_SEVERITY_LOW_ARB, -1, buf); return; } @@ -306,19 +391,17 @@ void GPU_string_marker(size_t length, const char *buf) if (GLEW_AMD_debug_output) { glDebugMessageInsertAMD( GL_DEBUG_CATEGORY_APPLICATION_AMD, GL_DEBUG_SEVERITY_LOW_AMD, 0, - length, buf); + 0, buf); return; } if (GLEW_GREMEDY_string_marker) { - glStringMarkerGREMEDY(length, buf); + glStringMarkerGREMEDY(0, buf); return; } #endif - - return; } void GPU_print_error_debug(const char *str) diff --git a/source/blender/gpu/intern/gpu_shader.c b/source/blender/gpu/intern/gpu_shader.c index 1d7a99311f8..226595200ca 100644 --- a/source/blender/gpu/intern/gpu_shader.c +++ b/source/blender/gpu/intern/gpu_shader.c @@ -45,6 +45,7 @@ #define MAX_EXT_DEFINE_LENGTH 1024 /* Non-generated shaders */ +extern char datatoc_gpu_shader_fire_frag_glsl[]; extern char datatoc_gpu_shader_smoke_vert_glsl[]; extern char datatoc_gpu_shader_smoke_frag_glsl[]; extern char datatoc_gpu_shader_vsm_store_vert_glsl[]; @@ -690,8 +691,8 @@ GPUShader *GPU_shader_get_builtin_shader(GPUBuiltinShader shader) case GPU_SHADER_SMOKE_FIRE: if (!GG.shaders.smoke_fire) GG.shaders.smoke_fire = GPU_shader_create( - datatoc_gpu_shader_smoke_vert_glsl, datatoc_gpu_shader_smoke_frag_glsl, - NULL, NULL, "#define USE_FIRE;\n", 0, 0, 0); + datatoc_gpu_shader_smoke_vert_glsl, datatoc_gpu_shader_fire_frag_glsl, + NULL, NULL, NULL, 0, 0, 0); retval = GG.shaders.smoke_fire; break; case GPU_SHADER_DISPLAY_SH: diff --git a/source/blender/gpu/intern/gpu_texture.c b/source/blender/gpu/intern/gpu_texture.c index 37f874ee512..e6a9db5059a 100644 --- a/source/blender/gpu/intern/gpu_texture.c +++ b/source/blender/gpu/intern/gpu_texture.c @@ -58,7 +58,8 @@ struct GPUTexture { int number; /* number for multitexture binding */ int refcount; /* reference count */ GLenum target; /* GL_TEXTURE_* */ - GLenum target_base; /* same as target, (but no multisample) */ + GLenum target_base; /* same as target, (but no multisample) + * use it for unbinding */ GLuint bindcode; /* opengl identifier for texture */ int fromblender; /* we got the texture from Blender */ @@ -484,6 +485,9 @@ GPUTexture *GPU_texture_from_blender(Image *ima, ImageUser *iuser, int textarget GLint bindcode = GPU_verify_image(ima, iuser, textarget, 0, 0, mipmap, is_data, do_clip); GPU_update_image_time(ima, time); + /* see GPUInput::textarget: it can take two values - GL_TEXTURE_2D and GL_TEXTURE_CUBE_MAP + * these values are correct for glDisable, so textarget can be safely used in + * GPU_texture_bind/GPU_texture_unbind through tex->target_base */ if (textarget == GL_TEXTURE_2D) gputt = TEXTARGET_TEXTURE_2D; else @@ -500,7 +504,7 @@ GPUTexture *GPU_texture_from_blender(Image *ima, ImageUser *iuser, int textarget tex->number = -1; tex->refcount = 1; tex->target = textarget; - tex->target_base = GL_TEXTURE_2D; + tex->target_base = textarget; tex->fromblender = 1; ima->gputexture[gputt] = tex; @@ -916,11 +920,11 @@ void GPU_texture_bind(GPUTexture *tex, int number) GLenum arbnumber = (GLenum)((GLuint)GL_TEXTURE0 + number); if (number != 0) glActiveTexture(arbnumber); if (tex->bindcode != 0) { - glBindTexture(tex->target, tex->bindcode); + glBindTexture(tex->target_base, tex->bindcode); } else - GPU_invalid_tex_bind(tex->target); - glEnable(tex->target); + GPU_invalid_tex_bind(tex->target_base); + glEnable(tex->target_base); if (number != 0) glActiveTexture(GL_TEXTURE0); tex->number = number; @@ -942,8 +946,6 @@ void GPU_texture_unbind(GPUTexture *tex) GLenum arbnumber = (GLenum)((GLuint)GL_TEXTURE0 + tex->number); if (tex->number != 0) glActiveTexture(arbnumber); - glBindTexture(tex->target, 0); - glDisable(tex->target); glBindTexture(tex->target_base, 0); glDisable(tex->target_base); if (tex->number != 0) glActiveTexture(GL_TEXTURE0); diff --git a/source/blender/gpu/shaders/gpu_shader_fire_frag.glsl b/source/blender/gpu/shaders/gpu_shader_fire_frag.glsl new file mode 100644 index 00000000000..3819203bcd9 --- /dev/null +++ b/source/blender/gpu/shaders/gpu_shader_fire_frag.glsl @@ -0,0 +1,17 @@ + +varying vec3 coords; + +uniform sampler3D flame_texture; +uniform sampler1D spectrum_texture; + +void main() +{ + float flame = texture3D(flame_texture, coords).r; + vec4 emission = texture1D(spectrum_texture, flame); + + vec4 color; + color.rgb = emission.a * emission.rgb; + color.a = emission.a; + + gl_FragColor = color; +} diff --git a/source/blender/gpu/shaders/gpu_shader_geometry.glsl b/source/blender/gpu/shaders/gpu_shader_geometry.glsl index 6f063883e37..fe630dbeddb 100644 --- a/source/blender/gpu/shaders/gpu_shader_geometry.glsl +++ b/source/blender/gpu/shaders/gpu_shader_geometry.glsl @@ -31,6 +31,18 @@ uniform int osd_fvar_count; tessCoord.t); \ } +#ifdef USE_NEW_SHADING +# define INTERP_FACE_VARYING_ATT_2(result, fvarOffset, tessCoord) \ + { \ + vec2 tmp; \ + INTERP_FACE_VARYING_2(tmp, fvarOffset, tessCoord); \ + result = vec3(tmp, 0); \ + } +#else +# define INTERP_FACE_VARYING_ATT_2(result, fvarOffset, tessCoord) \ + INTERP_FACE_VARYING_2(result, fvarOffset, tessCoord) +#endif + uniform samplerBuffer FVarDataBuffer; uniform isamplerBuffer FVarDataOffsetBuffer; diff --git a/source/blender/gpu/shaders/gpu_shader_material.glsl b/source/blender/gpu/shaders/gpu_shader_material.glsl index 84fedf803c7..2b506acabef 100644 --- a/source/blender/gpu/shaders/gpu_shader_material.glsl +++ b/source/blender/gpu/shaders/gpu_shader_material.glsl @@ -2196,11 +2196,11 @@ void shade_madd_clamped(vec4 col, vec4 col1, vec4 col2, out vec4 outcol) outcol = col + max(col1 * col2, vec4(0.0, 0.0, 0.0, 0.0)); } -void env_apply(vec4 col, vec4 hor, vec4 zen, vec4 f, mat4 vm, vec3 vn, out vec4 outcol) +void env_apply(vec4 col, vec3 hor, vec3 zen, vec4 f, mat4 vm, vec3 vn, out vec4 outcol) { vec3 vv = normalize(vm[2].xyz); float skyfac = 0.5 * (1.0 + dot(vn, -vv)); - outcol = col + f * mix(hor, zen, skyfac); + outcol = col + f * vec4(mix(hor, zen, skyfac), 0); } void shade_maddf(vec4 col, float f, vec4 col1, out vec4 outcol) diff --git a/source/blender/gpu/shaders/gpu_shader_smoke_frag.glsl b/source/blender/gpu/shaders/gpu_shader_smoke_frag.glsl index 4d1feb5c83e..fd790009e02 100644 --- a/source/blender/gpu/shaders/gpu_shader_smoke_frag.glsl +++ b/source/blender/gpu/shaders/gpu_shader_smoke_frag.glsl @@ -8,11 +8,6 @@ uniform float density_scale; uniform sampler3D soot_texture; uniform sampler3D shadow_texture; -#ifdef USE_FIRE -uniform sampler3D flame_texture; -uniform sampler1D spectrum_texture; -#endif - void main() { /* compute color and density from volume texture */ @@ -37,12 +32,5 @@ void main() /* premultiply alpha */ vec4 color = vec4(soot_alpha * soot_color, soot_alpha); -#ifdef USE_FIRE - /* fire */ - float flame = texture3D(flame_texture, coords).r; - vec4 emission = texture1D(spectrum_texture, flame); - color.rgb += (1 - color.a) * emission.a * emission.rgb; -#endif - gl_FragColor = color; } diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index 07df94ee332..5c1bfc229da 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -247,6 +247,7 @@ typedef enum ID_Type { ID_LS = MAKE_ID2('L', 'S'), /* FreestyleLineStyle */ ID_PAL = MAKE_ID2('P', 'L'), /* Palette */ ID_PC = MAKE_ID2('P', 'C'), /* PaintCurve */ + ID_CF = MAKE_ID2('C', 'F'), /* CacheFile */ } ID_Type; /* Only used as 'placeholder' in .blend files for directly linked datablocks. */ @@ -377,6 +378,47 @@ enum { FILTER_ID_VF = (1 << 25), FILTER_ID_WO = (1 << 26), FILTER_ID_PA = (1 << 27), + FILTER_ID_CF = (1 << 28), +}; + +/* IMPORTANT: this enum matches the order currently use in set_lisbasepointers, + * keep them in sync! */ +enum { + INDEX_ID_LI = 0, + INDEX_ID_IP, + INDEX_ID_AC, + INDEX_ID_KE, + INDEX_ID_GD, + INDEX_ID_NT, + INDEX_ID_IM, + INDEX_ID_TE, + INDEX_ID_MA, + INDEX_ID_VF, + INDEX_ID_AR, + INDEX_ID_CF, + INDEX_ID_ME, + INDEX_ID_CU, + INDEX_ID_MB, + INDEX_ID_LT, + INDEX_ID_LA, + INDEX_ID_CA, + INDEX_ID_TXT, + INDEX_ID_SO, + INDEX_ID_GR, + INDEX_ID_PAL, + INDEX_ID_PC, + INDEX_ID_BR, + INDEX_ID_PA, + INDEX_ID_SPK, + INDEX_ID_WO, + INDEX_ID_MC, + INDEX_ID_SCR, + INDEX_ID_OB, + INDEX_ID_LS, + INDEX_ID_SCE, + INDEX_ID_WM, + INDEX_ID_MSK, + INDEX_ID_NULL, }; #ifdef __cplusplus diff --git a/source/blender/makesdna/DNA_action_types.h b/source/blender/makesdna/DNA_action_types.h index 9a19606d6c8..f3df9090d41 100644 --- a/source/blender/makesdna/DNA_action_types.h +++ b/source/blender/makesdna/DNA_action_types.h @@ -694,7 +694,9 @@ typedef enum eAnimEdit_Context { /* dopesheet (default) */ SACTCONT_DOPESHEET = 3, /* mask */ - SACTCONT_MASK = 4 + SACTCONT_MASK = 4, + /* cache file */ + SACTCONT_CACHEFILE = 5, } eAnimEdit_Context; /* SpaceAction AutoSnap Settings (also used by other Animation Editors) */ diff --git a/source/blender/makesdna/DNA_cachefile_types.h b/source/blender/makesdna/DNA_cachefile_types.h new file mode 100644 index 00000000000..dd47d63fc19 --- /dev/null +++ b/source/blender/makesdna/DNA_cachefile_types.h @@ -0,0 +1,85 @@ +/* + * ***** 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) 2016 Blender Foundation. + * All rights reserved. + * + * Contributor(s): Kevin Dietrich. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file DNA_cachefile_types.h + * \ingroup DNA + */ + +#ifndef __DNA_CACHEFILE_TYPES_H__ +#define __DNA_CACHEFILE_TYPES_H__ + +#include "DNA_ID.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/* CacheFile::flag */ +enum { + CACHEFILE_DS_EXPAND = (1 << 0), +}; + +/* CacheFile::draw_flag */ +enum { + CACHEFILE_KEYFRAME_DRAWN = (1 << 0), +}; + +typedef struct AlembicObjectPath { + struct AlembicObjectPath *next, *prev; + + char path[1024]; /* 1024 = FILE_MAX, might use PATH_MAX in the future. */ +} AlembicObjectPath; + +typedef struct CacheFile { + ID id; + struct AnimData *adt; + + struct AbcArchiveHandle *handle; + void *handle_mutex; + + /* Paths of the objects inside of the Alembic archive referenced by this + * CacheFile. */ + ListBase object_paths; + + char filepath[1024]; /* 1024 = FILE_MAX */ + + char is_sequence; + char forward_axis; + char up_axis; + char override_frame; + + float scale; + float frame; /* The frame/time to lookup in the cache file. */ + + short flag; /* Animation flag. */ + short draw_flag; +} CacheFile; + +#ifdef __cplusplus +} +#endif + +#endif /* __DNA_CACHEFILE_TYPES_H__ */ diff --git a/source/blender/makesdna/DNA_constraint_types.h b/source/blender/makesdna/DNA_constraint_types.h index 5fcd374b21f..fc4e7de73f5 100644 --- a/source/blender/makesdna/DNA_constraint_types.h +++ b/source/blender/makesdna/DNA_constraint_types.h @@ -458,6 +458,12 @@ typedef struct bObjectSolverConstraint { struct Object *camera; } bObjectSolverConstraint; +/* Transform matrix cache constraint */ +typedef struct bTransformCacheConstraint { + struct CacheFile *cache_file; + char object_path[1024]; /* FILE_MAX */ +} bTransformCacheConstraint; + /* ------------------------------------------ */ /* bConstraint->type @@ -494,6 +500,7 @@ typedef enum eBConstraint_Types { CONSTRAINT_TYPE_FOLLOWTRACK = 26, /* Follow Track Constraint */ CONSTRAINT_TYPE_CAMERASOLVER = 27, /* Camera Solver Constraint */ CONSTRAINT_TYPE_OBJECTSOLVER = 28, /* Object Solver Constraint */ + CONSTRAINT_TYPE_TRANSFORM_CACHE = 29, /* Transform Cache Constraint */ /* NOTE: no constraints are allowed to be added after this */ NUM_CONSTRAINT_TYPES diff --git a/source/blender/makesdna/DNA_genfile.h b/source/blender/makesdna/DNA_genfile.h index a2981c0aa76..9e9ab974b01 100644 --- a/source/blender/makesdna/DNA_genfile.h +++ b/source/blender/makesdna/DNA_genfile.h @@ -100,6 +100,7 @@ void *DNA_struct_reconstruct( int DNA_elem_array_size(const char *str); int DNA_elem_offset(struct SDNA *sdna, const char *stype, const char *vartype, const char *name); +bool DNA_struct_find(const struct SDNA *sdna, const char *stype); bool DNA_struct_elem_find(const struct SDNA *sdna, const char *stype, const char *vartype, const char *name); diff --git a/source/blender/makesdna/DNA_gpencil_types.h b/source/blender/makesdna/DNA_gpencil_types.h index f1546053c5c..773d203bdb3 100644 --- a/source/blender/makesdna/DNA_gpencil_types.h +++ b/source/blender/makesdna/DNA_gpencil_types.h @@ -298,9 +298,8 @@ typedef struct bGPdata { char pad[6]; /* padding for compiler alignment error */ short sflag; /* settings for palette color */ - /* saved paletes and brushes */ + /* saved palettes */ ListBase palettes; - //ListBase brushes; } bGPdata; /* bGPdata->flag */ diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h index bbc8edf4344..0424dc98a25 100644 --- a/source/blender/makesdna/DNA_modifier_types.h +++ b/source/blender/makesdna/DNA_modifier_types.h @@ -85,6 +85,7 @@ typedef enum ModifierType { eModifierType_DataTransfer = 49, eModifierType_NormalEdit = 50, eModifierType_CorrectiveSmooth = 51, + eModifierType_MeshSequenceCache = 52, NUM_MODIFIER_TYPES } ModifierType; @@ -1541,4 +1542,25 @@ enum { MOD_NORMALEDIT_MIX_MUL = 3, }; +typedef struct MeshSeqCacheModifierData { + ModifierData modifier; + + struct CacheFile *cache_file; + char object_path[1024]; /* 1024 = FILE_MAX */ + + char read_flag; + char pad[7]; +} MeshSeqCacheModifierData; + +/* MeshSeqCacheModifierData.read_flag */ +enum { + MOD_MESHSEQ_READ_VERT = (1 << 0), + MOD_MESHSEQ_READ_POLY = (1 << 1), + MOD_MESHSEQ_READ_UV = (1 << 2), + MOD_MESHSEQ_READ_COLOR = (1 << 3), +}; + +#define MOD_MESHSEQ_READ_ALL \ + (MOD_MESHSEQ_READ_VERT | MOD_MESHSEQ_READ_POLY | MOD_MESHSEQ_READ_UV | MOD_MESHSEQ_READ_COLOR) + #endif /* __DNA_MODIFIER_TYPES_H__ */ diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h index 7c2308860da..85903cabd1a 100644 --- a/source/blender/makesdna/DNA_object_types.h +++ b/source/blender/makesdna/DNA_object_types.h @@ -349,7 +349,7 @@ typedef struct DupliObject { /* persistent identifier for a dupli object, for inter-frame matching of * objects with motion blur, or inter-update matching for syncing */ - int persistent_id[8]; /* MAX_DUPLI_RECUR */ + int persistent_id[16]; /* 2*MAX_DUPLI_RECUR */ /* particle this dupli was generated from */ struct ParticleSystem *particle_system; diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index 90e8d8b8270..9f18a702aef 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -1738,9 +1738,11 @@ typedef struct Scene { #define R_STAMP_CAMERALENS 0x0800 #define R_STAMP_STRIPMETA 0x1000 #define R_STAMP_MEMORY 0x2000 +#define R_STAMP_HIDE_LABELS 0x4000 #define R_STAMP_ALL (R_STAMP_TIME|R_STAMP_FRAME|R_STAMP_DATE|R_STAMP_CAMERA|R_STAMP_SCENE| \ R_STAMP_NOTE|R_STAMP_MARKER|R_STAMP_FILENAME|R_STAMP_SEQSTRIP| \ - R_STAMP_RENDERTIME|R_STAMP_CAMERALENS|R_STAMP_MEMORY) + R_STAMP_RENDERTIME|R_STAMP_CAMERALENS|R_STAMP_MEMORY| \ + R_STAMP_HIDE_LABELS) /* alphamode */ #define R_ADDSKY 0 @@ -2088,6 +2090,8 @@ typedef enum eGPencil_Flags { GP_TOOL_FLAG_PAINTSESSIONS_ON = (1 << 0), /* When creating new frames, the last frame gets used as the basis for the new one */ GP_TOOL_FLAG_RETAIN_LAST = (1 << 1), + /* Add the strokes below all strokes in the layer */ + GP_TOOL_FLAG_PAINT_ONBACK = (1 << 2) } eGPencil_Flags; /* toolsettings->gpencil_src */ diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index 4ce0f369ebd..41188c2412f 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -738,6 +738,7 @@ typedef enum eFileSel_File_Types { FILE_TYPE_COLLADA = (1 << 13), FILE_TYPE_OPERATOR = (1 << 14), /* from filter_glob operator property */ FILE_TYPE_APPLICATIONBUNDLE = (1 << 15), + FILE_TYPE_ALEMBIC = (1 << 16), FILE_TYPE_DIR = (1 << 30), /* An FS directory (i.e. S_ISDIR on its path is true). */ FILE_TYPE_BLENDERLIB = (1 << 31), diff --git a/source/blender/makesdna/DNA_tracking_types.h b/source/blender/makesdna/DNA_tracking_types.h index 9888b735b8b..42b72c1ff93 100644 --- a/source/blender/makesdna/DNA_tracking_types.h +++ b/source/blender/makesdna/DNA_tracking_types.h @@ -158,7 +158,10 @@ typedef struct MovieTrackingTrack { * Used to prevent jumps of the camera when tracks are appearing or * disappearing. */ - float weight, pad; + float weight; + + /* track weight especially for 2D stabilization */ + float weight_stab; } MovieTrackingTrack; typedef struct MovieTrackingPlaneMarker { @@ -250,19 +253,24 @@ typedef struct MovieTrackingSettings { typedef struct MovieTrackingStabilization { int flag; - int tot_track, act_track; /* total number and index of active track in list */ + int tot_track, act_track; /* total number of translation tracks and index of active track in list */ + int tot_rot_track, act_rot_track; /* total number of rotation tracks and index of active track in list */ /* 2d stabilization */ float maxscale; /* max auto-scale factor */ - MovieTrackingTrack *rot_track; /* track used to stabilize rotation */ + MovieTrackingTrack *rot_track DNA_DEPRECATED; /* use TRACK_USE_2D_STAB_ROT on individual tracks instead */ + + int anchor_frame; /* reference point to anchor stabilization offset */ + float target_pos[2]; /* expected target position of frame after raw stabilization, will be subtracted */ + float target_rot; /* expected target rotation of frame after raw stabilization, will be compensated */ + float scale; /* zoom factor known to be present on original footage. Also used for autoscale */ float locinf, scaleinf, rotinf; /* influence on location, scale and rotation */ int filter; /* filter used for pixel interpolation */ - /* some pre-computing run-time variables */ - int ok; /* are precomputed values and scaled buf relevant? */ - float scale; /* autoscale factor */ + /* initialization and run-time data */ + int ok DNA_DEPRECATED; /* Without effect now, we initialize on every frame. Formerly used for caching of init values */ } MovieTrackingStabilization; typedef struct MovieTrackingReconstruction { @@ -386,7 +394,8 @@ enum { TRACK_USE_2D_STAB = (1 << 8), TRACK_PREVIEW_GRAYSCALE = (1 << 9), TRACK_DOPE_SEL = (1 << 10), - TRACK_PREVIEW_ALPHA = (1 << 11) + TRACK_PREVIEW_ALPHA = (1 << 11), + TRACK_USE_2D_STAB_ROT = (1 << 12) }; /* MovieTrackingTrack->motion_model */ @@ -452,7 +461,9 @@ enum { enum { TRACKING_2D_STABILIZATION = (1 << 0), TRACKING_AUTOSCALE = (1 << 1), - TRACKING_STABILIZE_ROTATION = (1 << 2) + TRACKING_STABILIZE_ROTATION = (1 << 2), + TRACKING_STABILIZE_SCALE = (1 << 3), + TRACKING_SHOW_STAB_TRACKS = (1 << 5) }; /* MovieTrackingStrabilization->filter */ diff --git a/source/blender/makesdna/intern/dna_genfile.c b/source/blender/makesdna/intern/dna_genfile.c index 6a41591e051..96085a79eff 100644 --- a/source/blender/makesdna/intern/dna_genfile.c +++ b/source/blender/makesdna/intern/dna_genfile.c @@ -1294,6 +1294,11 @@ int DNA_elem_offset(SDNA *sdna, const char *stype, const char *vartype, const ch return (int)((intptr_t)cp); } +bool DNA_struct_find(const SDNA *sdna, const char *stype) +{ + return DNA_struct_find_nr(sdna, stype) != -1; +} + bool DNA_struct_elem_find(const SDNA *sdna, const char *stype, const char *vartype, const char *name) { const int SDNAnr = DNA_struct_find_nr(sdna, stype); diff --git a/source/blender/makesdna/intern/makesdna.c b/source/blender/makesdna/intern/makesdna.c index b78299316e1..2cea8715a65 100644 --- a/source/blender/makesdna/intern/makesdna.c +++ b/source/blender/makesdna/intern/makesdna.c @@ -129,6 +129,7 @@ static const char *includefiles[] = { "DNA_rigidbody_types.h", "DNA_freestyle_types.h", "DNA_linestyle_types.h", + "DNA_cachefile_types.h", /* see comment above before editing! */ /* empty string to indicate end of includefiles */ @@ -1340,4 +1341,5 @@ int main(int argc, char **argv) #include "DNA_rigidbody_types.h" #include "DNA_freestyle_types.h" #include "DNA_linestyle_types.h" +#include "DNA_cachefile_types.h" /* end of list */ diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index 62e2018c67d..9cbe132282f 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -95,6 +95,8 @@ extern StructRNA RNA_Brush; extern StructRNA RNA_BrushTextureSlot; extern StructRNA RNA_BuildModifier; extern StructRNA RNA_MeshCacheModifier; +extern StructRNA RNA_MeshSequenceCacheModifier; +extern StructRNA RNA_CacheFile; extern StructRNA RNA_Camera; extern StructRNA RNA_CastModifier; extern StructRNA RNA_ChildOfConstraint; @@ -420,6 +422,7 @@ extern StructRNA RNA_MovieClipSequence; extern StructRNA RNA_MovieTracking; extern StructRNA RNA_MovieTrackingObject; extern StructRNA RNA_MovieTrackingTrack; +extern StructRNA RNA_MovieTrackingStabilization; extern StructRNA RNA_MulticamSequence; extern StructRNA RNA_MultiresModifier; extern StructRNA RNA_MusgraveTexture; diff --git a/source/blender/makesrna/RNA_enum_types.h b/source/blender/makesrna/RNA_enum_types.h index 7ae3d552916..1c9b3593d17 100644 --- a/source/blender/makesrna/RNA_enum_types.h +++ b/source/blender/makesrna/RNA_enum_types.h @@ -198,6 +198,8 @@ extern EnumPropertyItem rna_enum_dt_mix_mode_items[]; extern EnumPropertyItem rna_enum_dt_layers_select_src_items[]; extern EnumPropertyItem rna_enum_dt_layers_select_dst_items[]; +extern EnumPropertyItem rna_enum_abc_compression_items[]; + /* API calls */ int rna_node_tree_type_to_enum(struct bNodeTreeType *typeinfo); diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt index 7bfac9d0605..0f3ea27a7f9 100644 --- a/source/blender/makesrna/intern/CMakeLists.txt +++ b/source/blender/makesrna/intern/CMakeLists.txt @@ -38,6 +38,7 @@ set(DEFSRC rna_armature.c rna_boid.c rna_brush.c + rna_cachefile.c rna_camera.c rna_cloth.c rna_color.c @@ -290,6 +291,13 @@ if(WITH_INTERNATIONAL) add_definitions(-DWITH_INTERNATIONAL) endif() +if(WITH_ALEMBIC) + list(APPEND INC + ../../alembic + ) + add_definitions(-DWITH_ALEMBIC) +endif() + if(WITH_BULLET) list(APPEND INC ../../../../intern/rigidbody @@ -319,6 +327,10 @@ if(WITH_OPENVDB) endif() endif() +if(WITH_INPUT_NDOF) + add_definitions(-DWITH_INPUT_NDOF) +endif() + # Build makesrna executable blender_include_dirs( . diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c index f9a46409aea..569c1ee5f3f 100644 --- a/source/blender/makesrna/intern/makesrna.c +++ b/source/blender/makesrna/intern/makesrna.c @@ -50,7 +50,7 @@ #ifndef NDEBUG void BLI_system_backtrace(FILE *fp) { - (void)fp; + (void)fp; } #endif @@ -3301,6 +3301,7 @@ static RNAProcessItem PROCESS_ITEMS[] = { {"rna_armature.c", "rna_armature_api.c", RNA_def_armature}, {"rna_boid.c", NULL, RNA_def_boid}, {"rna_brush.c", NULL, RNA_def_brush}, + {"rna_cachefile.c", NULL, RNA_def_cachefile}, {"rna_camera.c", "rna_camera_api.c", RNA_def_camera}, {"rna_cloth.c", NULL, RNA_def_cloth}, {"rna_color.c", NULL, RNA_def_color}, diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c index 8ad6713192a..ab124b361f1 100644 --- a/source/blender/makesrna/intern/rna_ID.c +++ b/source/blender/makesrna/intern/rna_ID.c @@ -52,6 +52,7 @@ EnumPropertyItem rna_enum_id_type_items[] = { {ID_AR, "ARMATURE", ICON_ARMATURE_DATA, "Armature", ""}, {ID_BR, "BRUSH", ICON_BRUSH_DATA, "Brush", ""}, {ID_CA, "CAMERA", ICON_CAMERA_DATA, "Camera", ""}, + {ID_CF, "CACHEFILE", ICON_FILE, "Cache File", ""}, {ID_CU, "CURVE", ICON_CURVE_DATA, "Curve", ""}, {ID_VF, "FONT", ICON_FONT_DATA, "Font", ""}, {ID_GD, "GREASEPENCIL", ICON_GREASEPENCIL, "Grease Pencil", ""}, @@ -139,6 +140,7 @@ short RNA_type_to_ID_code(StructRNA *type) if (RNA_struct_is_a(type, &RNA_Action)) return ID_AC; if (RNA_struct_is_a(type, &RNA_Armature)) return ID_AR; if (RNA_struct_is_a(type, &RNA_Brush)) return ID_BR; + if (RNA_struct_is_a(type, &RNA_CacheFile)) return ID_CF; if (RNA_struct_is_a(type, &RNA_Camera)) return ID_CA; if (RNA_struct_is_a(type, &RNA_Curve)) return ID_CU; if (RNA_struct_is_a(type, &RNA_GreasePencil)) return ID_GD; @@ -179,6 +181,7 @@ StructRNA *ID_code_to_RNA_type(short idcode) case ID_AR: return &RNA_Armature; case ID_BR: return &RNA_Brush; case ID_CA: return &RNA_Camera; + case ID_CF: return &RNA_CacheFile; case ID_CU: return &RNA_Curve; case ID_GD: return &RNA_GreasePencil; case ID_GR: return &RNA_Group; @@ -383,15 +386,15 @@ int rna_IDMaterials_assign_int(PointerRNA *ptr, int key, const PointerRNA *assig } } -static void rna_IDMaterials_append_id(ID *id, Material *ma) +static void rna_IDMaterials_append_id(ID *id, Main *bmain, Material *ma) { - BKE_material_append_id(id, ma); + BKE_material_append_id(bmain, id, ma); WM_main_add_notifier(NC_OBJECT | ND_DRAW, id); WM_main_add_notifier(NC_OBJECT | ND_OB_SHADING, id); } -static Material *rna_IDMaterials_pop_id(ID *id, ReportList *reports, int index_i, int remove_material_slot) +static Material *rna_IDMaterials_pop_id(ID *id, Main *bmain, ReportList *reports, int index_i, int remove_material_slot) { Material *ma; short *totcol = give_totcolp_id(id); @@ -405,7 +408,7 @@ static Material *rna_IDMaterials_pop_id(ID *id, ReportList *reports, int index_i return NULL; } - ma = BKE_material_pop_id(id, index_i, remove_material_slot); + ma = BKE_material_pop_id(bmain, id, index_i, remove_material_slot); if (*totcol == totcol_orig) { BKE_report(reports, RPT_ERROR, "No material to removed"); @@ -421,7 +424,7 @@ static Material *rna_IDMaterials_pop_id(ID *id, ReportList *reports, int index_i static void rna_IDMaterials_clear_id(ID *id, int remove_material_slot) { - BKE_material_clear_id(id, remove_material_slot); + BKE_material_clear_id(G.main, id, remove_material_slot); DAG_id_tag_update(id, OB_RECALC_DATA); WM_main_add_notifier(NC_OBJECT | ND_DRAW, id); @@ -817,12 +820,13 @@ static void rna_def_ID_materials(BlenderRNA *brna) RNA_def_struct_ui_text(srna, "ID Materials", "Collection of materials"); func = RNA_def_function(srna, "append", "rna_IDMaterials_append_id"); + RNA_def_function_flag(func, FUNC_USE_MAIN); RNA_def_function_ui_description(func, "Add a new material to the data block"); parm = RNA_def_pointer(func, "material", "Material", "", "Material to add"); RNA_def_property_flag(parm, PROP_REQUIRED); func = RNA_def_function(srna, "pop", "rna_IDMaterials_pop_id"); - RNA_def_function_flag(func, FUNC_USE_REPORTS); + RNA_def_function_flag(func, FUNC_USE_REPORTS | FUNC_USE_MAIN); RNA_def_function_ui_description(func, "Remove a material from the data block"); parm = RNA_def_int(func, "index", -1, -MAXMAT, MAXMAT, "", "Index of material to remove", 0, MAXMAT); RNA_def_boolean(func, "update_data", 0, "", "Update data by re-adjusting the material slots assigned"); diff --git a/source/blender/makesrna/intern/rna_cachefile.c b/source/blender/makesrna/intern/rna_cachefile.c new file mode 100644 index 00000000000..7249ebd5feb --- /dev/null +++ b/source/blender/makesrna/intern/rna_cachefile.c @@ -0,0 +1,169 @@ +/* + * ***** 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) 2016 Blender Foundation. + * All rights reserved. + * + * Contributor(s): Kevin Dietrich. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "DNA_cachefile_types.h" +#include "DNA_scene_types.h" + +#include "RNA_access.h" +#include "RNA_define.h" +#include "RNA_enum_types.h" + +#include "rna_internal.h" + +#ifdef RNA_RUNTIME + +#include "BKE_cachefile.h" +#include "BKE_depsgraph.h" + +#include "DEG_depsgraph.h" + +#include "WM_api.h" +#include "WM_types.h" + +#ifdef WITH_ALEMBIC +# include "../../../alembic/ABC_alembic.h" +#endif + +static void rna_CacheFile_update(Main *bmain, Scene *scene, PointerRNA *ptr) +{ + CacheFile *cache_file = (CacheFile *)ptr->data; + + DAG_id_tag_update(&cache_file->id, 0); + WM_main_add_notifier(NC_OBJECT | ND_DRAW, NULL); + + UNUSED_VARS(bmain, scene); +} + +static void rna_CacheFile_update_handle(Main *bmain, Scene *scene, PointerRNA *ptr) +{ + CacheFile *cache_file = ptr->data; + + BKE_cachefile_reload(bmain, cache_file); + + rna_CacheFile_update(bmain, scene, ptr); +} + +static void rna_CacheFile_object_paths_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) +{ + CacheFile *cache_file = (CacheFile *)ptr->data; + rna_iterator_listbase_begin(iter, &cache_file->object_paths, NULL); +} + +#else + +/* cachefile.object_paths */ +static void rna_def_alembic_object_path(BlenderRNA *brna) +{ + StructRNA *srna = RNA_def_struct(brna, "AlembicObjectPath", NULL); + RNA_def_struct_sdna(srna, "AlembicObjectPath"); + RNA_def_struct_ui_text(srna, "Object Path", "Path of an object inside of an Alembic archive"); + RNA_def_struct_ui_icon(srna, ICON_NONE); + + PropertyRNA *prop = RNA_def_property(srna, "path", PROP_STRING, PROP_NONE); + RNA_def_property_ui_text(prop, "Path", "Object path"); + RNA_def_struct_name_property(srna, prop); +} + +/* cachefile.object_paths */ +static void rna_def_cachefile_object_paths(BlenderRNA *brna, PropertyRNA *cprop) +{ + RNA_def_property_srna(cprop, "AlembicObjectPaths"); + StructRNA *srna = RNA_def_struct(brna, "AlembicObjectPaths", NULL); + RNA_def_struct_sdna(srna, "CacheFile"); + RNA_def_struct_ui_text(srna, "Object Paths", "Collection of object paths"); +} + +static void rna_def_cachefile(BlenderRNA *brna) +{ + StructRNA *srna = RNA_def_struct(brna, "CacheFile", "ID"); + RNA_def_struct_sdna(srna, "CacheFile"); + RNA_def_struct_ui_text(srna, "CacheFile", ""); + RNA_def_struct_ui_icon(srna, ICON_FILE); + + PropertyRNA *prop = RNA_def_property(srna, "filepath", PROP_STRING, PROP_FILEPATH); + RNA_def_property_ui_text(prop, "File Path", "Path to external displacements file"); + RNA_def_property_update(prop, 0, "rna_CacheFile_update_handle"); + + prop = RNA_def_property(srna, "is_sequence", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_ui_text(prop, "Sequence", "Whether the cache is separated in a series of files"); + RNA_def_property_update(prop, 0, "rna_CacheFile_update"); + + /* ----------------- For Scene time ------------------- */ + + prop = RNA_def_property(srna, "override_frame", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_ui_text(prop, "Override Frame", + "Whether to use a custom frame for looking up data in the cache file," + " instead of using the current scene frame"); + RNA_def_property_update(prop, 0, "rna_CacheFile_update"); + + prop = RNA_def_property(srna, "frame", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "frame"); + RNA_def_property_range(prop, -MAXFRAME, MAXFRAME); + RNA_def_property_ui_text(prop, "Frame", "The time to use for looking up the data in the cache file," + " or to determine which file to use in a file sequence"); + RNA_def_property_update(prop, 0, "rna_CacheFile_update"); + + /* ----------------- Axis Conversion ----------------- */ + + prop = RNA_def_property(srna, "forward_axis", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "forward_axis"); + RNA_def_property_enum_items(prop, rna_enum_object_axis_items); + RNA_def_property_ui_text(prop, "Forward", ""); + RNA_def_property_update(prop, 0, "rna_CacheFile_update"); + + prop = RNA_def_property(srna, "up_axis", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "up_axis"); + RNA_def_property_enum_items(prop, rna_enum_object_axis_items); + RNA_def_property_ui_text(prop, "Up", ""); + RNA_def_property_update(prop, 0, "rna_CacheFile_update"); + + prop = RNA_def_property(srna, "scale", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "scale"); + RNA_def_property_range(prop, 0.0001f, 1000.0f); + RNA_def_property_ui_text(prop, "Scale", "Value by which to enlarge or shrink the object with respect to the world's origin" + " (only applicable through a Transform Cache constraint)"); + RNA_def_property_update(prop, 0, "rna_CacheFile_update"); + + /* object paths */ + prop = RNA_def_property(srna, "object_paths", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_sdna(prop, NULL, "object_paths", NULL); + RNA_def_property_collection_funcs(prop, "rna_CacheFile_object_paths_begin", "rna_iterator_listbase_next", + "rna_iterator_listbase_end", "rna_iterator_listbase_get", + NULL, NULL, NULL, NULL); + RNA_def_property_struct_type(prop, "AlembicObjectPath"); + RNA_def_property_srna(prop, "AlembicObjectPaths"); + RNA_def_property_ui_text(prop, "Object Paths", "Paths of the objects inside the Alembic archive"); + rna_def_cachefile_object_paths(brna, prop); + + rna_def_animdata_common(srna); +} + +void RNA_def_cachefile(BlenderRNA *brna) +{ + rna_def_cachefile(brna); + rna_def_alembic_object_path(brna); +} + +#endif diff --git a/source/blender/makesrna/intern/rna_cloth.c b/source/blender/makesrna/intern/rna_cloth.c index 781e44c9ed6..d8bcbc2cc72 100644 --- a/source/blender/makesrna/intern/rna_cloth.c +++ b/source/blender/makesrna/intern/rna_cloth.c @@ -56,6 +56,12 @@ static void rna_cloth_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerR WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, ob); } +static void rna_cloth_dependency_update(Main *bmain, Scene *scene, PointerRNA *ptr) +{ + DAG_relations_tag_update(bmain); + rna_cloth_update(bmain, scene, ptr); +} + static void rna_cloth_pinning_changed(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) { Object *ob = (Object *)ptr->id.data; @@ -68,6 +74,16 @@ static void rna_cloth_pinning_changed(Main *UNUSED(bmain), Scene *UNUSED(scene), WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, ob); } +static void rna_ClothSettings_bending_set(struct PointerRNA *ptr, float value) +{ + ClothSimSettings *settings = (ClothSimSettings *)ptr->data; + + settings->bending = value; + + /* check for max clipping */ + if (value > settings->max_bend) + settings->max_bend = value; +} static void rna_ClothSettings_max_bend_set(struct PointerRNA *ptr, float value) { @@ -80,6 +96,17 @@ static void rna_ClothSettings_max_bend_set(struct PointerRNA *ptr, float value) settings->max_bend = value; } +static void rna_ClothSettings_structural_set(struct PointerRNA *ptr, float value) +{ + ClothSimSettings *settings = (ClothSimSettings *)ptr->data; + + settings->structural = value; + + /* check for max clipping */ + if (value > settings->max_struct) + settings->max_struct = value; +} + static void rna_ClothSettings_max_struct_set(struct PointerRNA *ptr, float value) { ClothSimSettings *settings = (ClothSimSettings *)ptr->data; @@ -493,6 +520,7 @@ static void rna_def_cloth_sim_settings(BlenderRNA *brna) prop = RNA_def_property(srna, "structural_stiffness", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "structural"); RNA_def_property_range(prop, 0.0f, 10000.0f); + RNA_def_property_float_funcs(prop, NULL, "rna_ClothSettings_structural_set", NULL); RNA_def_property_ui_text(prop, "Structural Stiffness", "Overall stiffness of structure"); RNA_def_property_update(prop, 0, "rna_cloth_update"); @@ -521,6 +549,7 @@ static void rna_def_cloth_sim_settings(BlenderRNA *brna) prop = RNA_def_property(srna, "bending_stiffness", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "bending"); RNA_def_property_range(prop, 0.0f, 10000.0f); + RNA_def_property_float_funcs(prop, NULL, "rna_ClothSettings_bending_set", NULL); RNA_def_property_ui_text(prop, "Bending Stiffness", "Wrinkle coefficient (higher = less smaller but more big wrinkles)"); RNA_def_property_update(prop, 0, "rna_cloth_update"); @@ -706,7 +735,7 @@ static void rna_def_cloth_collision_settings(BlenderRNA *brna) prop = RNA_def_property(srna, "group", PROP_POINTER, PROP_NONE); RNA_def_property_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Collision Group", "Limit colliders to this Group"); - RNA_def_property_update(prop, 0, "rna_cloth_update"); + RNA_def_property_update(prop, 0, "rna_cloth_dependency_update"); prop = RNA_def_property(srna, "vertex_group_self_collisions", PROP_STRING, PROP_NONE); RNA_def_property_string_funcs(prop, "rna_CollSettings_selfcol_vgroup_get", "rna_CollSettings_selfcol_vgroup_length", diff --git a/source/blender/makesrna/intern/rna_constraint.c b/source/blender/makesrna/intern/rna_constraint.c index 98560bf3452..db3f76f3cfc 100644 --- a/source/blender/makesrna/intern/rna_constraint.c +++ b/source/blender/makesrna/intern/rna_constraint.c @@ -72,6 +72,8 @@ EnumPropertyItem rna_enum_constraint_type_items[] = { "Compensate for scaling one axis by applying suitable scaling to the other two axes"}, {CONSTRAINT_TYPE_TRANSFORM, "TRANSFORM", ICON_CONSTRAINT_DATA, "Transformation", "Use one transform property from target to control another (or same) property on owner"}, + {CONSTRAINT_TYPE_TRANSFORM_CACHE, "TRANSFORM_CACHE", ICON_CONSTRAINT_DATA, "Transform Cache", + "Look up the transformation matrix from an external file"}, {0, "", 0, N_("Tracking"), ""}, {CONSTRAINT_TYPE_CLAMPTO, "CLAMP_TO", ICON_CONSTRAINT_DATA, "Clamp To", "Restrict movements to lie along a curve by remapping location along curve's longest axis"}, @@ -214,6 +216,8 @@ static StructRNA *rna_ConstraintType_refine(struct PointerRNA *ptr) return &RNA_CameraSolverConstraint; case CONSTRAINT_TYPE_OBJECTSOLVER: return &RNA_ObjectSolverConstraint; + case CONSTRAINT_TYPE_TRANSFORM_CACHE: + return &RNA_TransformCacheConstraint; default: return &RNA_UnknownType; } @@ -2571,6 +2575,27 @@ static void rna_def_constraint_object_solver(BlenderRNA *brna) "rna_Constraint_cameraObject_poll"); } +static void rna_def_constraint_transform_cache(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "TransformCacheConstraint", "Constraint"); + RNA_def_struct_ui_text(srna, "Transform Cache Constraint", "Look up transformation from an external file"); + RNA_def_struct_sdna_from(srna, "bTransformCacheConstraint", "data"); + + prop = RNA_def_property(srna, "cache_file", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "cache_file"); + RNA_def_property_struct_type(prop, "CacheFile"); + RNA_def_property_ui_text(prop, "Cache File", ""); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK); + RNA_def_property_update(prop, 0, "rna_Constraint_dependency_update"); + + prop = RNA_def_property(srna, "object_path", PROP_STRING, PROP_NONE); + RNA_def_property_ui_text(prop, "Object Path", "Path to the object in the Alembic archive used to lookup the transform matrix"); + RNA_def_property_update(prop, 0, "rna_Constraint_update"); +} + /* base struct for constraints */ void RNA_def_constraint(BlenderRNA *brna) { @@ -2687,6 +2712,7 @@ void RNA_def_constraint(BlenderRNA *brna) rna_def_constraint_follow_track(brna); rna_def_constraint_camera_solver(brna); rna_def_constraint_object_solver(brna); + rna_def_constraint_transform_cache(brna); } #endif diff --git a/source/blender/makesrna/intern/rna_curve.c b/source/blender/makesrna/intern/rna_curve.c index fb4ff6f4856..f4fb30e0793 100644 --- a/source/blender/makesrna/intern/rna_curve.c +++ b/source/blender/makesrna/intern/rna_curve.c @@ -1273,7 +1273,7 @@ static void rna_def_curve_spline_bezpoints(BlenderRNA *brna, PropertyRNA *cprop) RNA_def_property_srna(cprop, "SplineBezierPoints"); srna = RNA_def_struct(brna, "SplineBezierPoints", NULL); RNA_def_struct_sdna(srna, "Nurb"); - RNA_def_struct_ui_text(srna, "Spline Bezier Points", "Collection of spline bezirt points"); + RNA_def_struct_ui_text(srna, "Spline Bezier Points", "Collection of spline Bezier points"); func = RNA_def_function(srna, "add", "rna_Curve_spline_bezpoints_add"); RNA_def_function_ui_description(func, "Add a number of points to this spline"); diff --git a/source/blender/makesrna/intern/rna_gpencil.c b/source/blender/makesrna/intern/rna_gpencil.c index 619eecad8e5..7eaf8b65902 100644 --- a/source/blender/makesrna/intern/rna_gpencil.c +++ b/source/blender/makesrna/intern/rna_gpencil.c @@ -323,7 +323,7 @@ static void rna_GPencil_active_layer_set(PointerRNA *ptr, PointerRNA value) static int rna_GPencil_active_layer_index_get(PointerRNA *ptr) { bGPdata *gpd = (bGPdata *)ptr->id.data; - bGPDlayer *gpl = gpencil_layer_getactive(gpd); + bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); return BLI_findindex(&gpd->layers, gpl); } @@ -333,7 +333,7 @@ static void rna_GPencil_active_layer_index_set(PointerRNA *ptr, int value) bGPdata *gpd = (bGPdata *)ptr->id.data; bGPDlayer *gpl = BLI_findlink(&gpd->layers, value); - gpencil_layer_setactive(gpd, gpl); + BKE_gpencil_layer_setactive(gpd, gpl); } static void rna_GPencil_active_layer_index_range(PointerRNA *ptr, int *min, int *max, int *softmin, int *softmax) @@ -366,7 +366,7 @@ static void rna_GPencil_use_onion_skinning_set(PointerRNA *ptr, const int value) /* set new value */ if (value) { /* enable on active layer (it's the one that's most likely to be of interest right now) */ - gpl = gpencil_layer_getactive(gpd); + gpl = BKE_gpencil_layer_getactive(gpd); if (gpl) { gpl->flag |= GP_LAYER_ONIONSKIN; } @@ -435,16 +435,27 @@ static void rna_GPencil_stroke_point_select_set(PointerRNA *ptr, const int value pt->flag &= ~GP_SPOINT_SELECT; /* Check if the stroke should be selected or not... */ - gpencil_stroke_sync_selection(gps); + BKE_gpencil_stroke_sync_selection(gps); } } -static void rna_GPencil_stroke_point_add(bGPDstroke *stroke, int count) +static void rna_GPencil_stroke_point_add(bGPDstroke *stroke, int count, float pressure, float strength) { if (count > 0) { + /* create space at the end of the array for extra points */ stroke->points = MEM_recallocN_id(stroke->points, sizeof(bGPDspoint) * (stroke->totpoints + count), "gp_stroke_points"); + + /* init the pressure and strength values so that old scripts won't need to + * be modified to give these initial values... + */ + for (int i = 0; i < count; i++) { + bGPDspoint *pt = stroke->points + (stroke->totpoints + i); + pt->pressure = pressure; + pt->strength = strength; + } + stroke->totpoints += count; } } @@ -482,7 +493,9 @@ static void rna_GPencil_stroke_point_pop(bGPDstroke *stroke, ReportList *reports static bGPDstroke *rna_GPencil_stroke_new(bGPDframe *frame, const char *colorname) { bGPDstroke *stroke = MEM_callocN(sizeof(bGPDstroke), "gp_stroke"); - strcpy(stroke->colorname, colorname); + if (colorname) { + BLI_strncpy(stroke->colorname, colorname, sizeof(stroke->colorname)); + } stroke->palcolor = NULL; stroke->flag |= GP_STROKE_RECALC_COLOR; BLI_addtail(&frame->strokes, stroke); @@ -534,7 +547,7 @@ static bGPDframe *rna_GPencil_frame_new(bGPDlayer *layer, ReportList *reports, i return NULL; } - frame = gpencil_frame_addnew(layer, frame_number); + frame = BKE_gpencil_frame_addnew(layer, frame_number); WM_main_add_notifier(NC_GPENCIL | NA_EDITED, NULL); @@ -549,7 +562,7 @@ static void rna_GPencil_frame_remove(bGPDlayer *layer, ReportList *reports, Poin return; } - gpencil_layer_delframe(layer, frame); + BKE_gpencil_layer_delframe(layer, frame); RNA_POINTER_INVALIDATE(frame_ptr); WM_main_add_notifier(NC_GPENCIL | NA_EDITED, NULL); @@ -557,7 +570,7 @@ static void rna_GPencil_frame_remove(bGPDlayer *layer, ReportList *reports, Poin static bGPDframe *rna_GPencil_frame_copy(bGPDlayer *layer, bGPDframe *src) { - bGPDframe *frame = gpencil_frame_duplicate(src); + bGPDframe *frame = BKE_gpencil_frame_duplicate(src); while (BKE_gpencil_layer_find_frame(layer, frame->framenum)) { frame->framenum++; @@ -572,7 +585,7 @@ static bGPDframe *rna_GPencil_frame_copy(bGPDlayer *layer, bGPDframe *src) static bGPDlayer *rna_GPencil_layer_new(bGPdata *gpd, const char *name, int setactive) { - bGPDlayer *gl = gpencil_layer_addnew(gpd, name, setactive != 0); + bGPDlayer *gl = BKE_gpencil_layer_addnew(gpd, name, setactive != 0); WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL); @@ -587,7 +600,7 @@ static void rna_GPencil_layer_remove(bGPdata *gpd, ReportList *reports, PointerR return; } - gpencil_layer_delete(gpd, layer); + BKE_gpencil_layer_delete(gpd, layer); RNA_POINTER_INVALIDATE(layer_ptr); WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL); @@ -595,21 +608,21 @@ static void rna_GPencil_layer_remove(bGPdata *gpd, ReportList *reports, PointerR static void rna_GPencil_frame_clear(bGPDframe *frame) { - free_gpencil_strokes(frame); + BKE_gpencil_free_strokes(frame); WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL); } static void rna_GPencil_layer_clear(bGPDlayer *layer) { - free_gpencil_frames(layer); + BKE_gpencil_free_frames(layer); WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL); } static void rna_GPencil_clear(bGPdata *gpd) { - free_gpencil_layers(&gpd->layers); + BKE_gpencil_free_layers(&gpd->layers); WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL); } @@ -617,7 +630,7 @@ static void rna_GPencil_clear(bGPdata *gpd) /* Palettes */ static bGPDpalette *rna_GPencil_palette_new(bGPdata *gpd, const char *name, int setactive) { - bGPDpalette *palette = gpencil_palette_addnew(gpd, name, setactive != 0); + bGPDpalette *palette = BKE_gpencil_palette_addnew(gpd, name, setactive != 0); WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL); @@ -632,7 +645,7 @@ static void rna_GPencil_palette_remove(bGPdata *gpd, ReportList *reports, Pointe return; } - gpencil_palette_delete(gpd, palette); + BKE_gpencil_palette_delete(gpd, palette); RNA_POINTER_INVALIDATE(palette_ptr); WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL); @@ -675,7 +688,7 @@ static void rna_GPencil_active_palette_set(PointerRNA *ptr, PointerRNA value) } } /* force color recalc */ - gpencil_palette_change_strokes(gpd); + BKE_gpencil_palette_change_strokes(gpd); WM_main_add_notifier(NC_GPENCIL | NA_EDITED, NULL); } @@ -684,7 +697,7 @@ static void rna_GPencil_active_palette_set(PointerRNA *ptr, PointerRNA value) static int rna_GPencilPalette_index_get(PointerRNA *ptr) { bGPdata *gpd = (bGPdata *)ptr->id.data; - bGPDpalette *palette = gpencil_palette_getactive(gpd); + bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); return BLI_findindex(&gpd->palettes, palette); } @@ -694,7 +707,7 @@ static void rna_GPencilPalette_index_set(PointerRNA *ptr, int value) bGPdata *gpd = (bGPdata *)ptr->id.data; bGPDpalette *palette = BLI_findlink(&gpd->palettes, value); - gpencil_palette_setactive(gpd, palette); + BKE_gpencil_palette_setactive(gpd, palette); WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL); } @@ -712,7 +725,7 @@ static void rna_GPencilPalette_index_range(PointerRNA *ptr, int *min, int *max, /* Palette colors */ static bGPDpalettecolor *rna_GPencilPalette_color_new(bGPDpalette *palette) { - bGPDpalettecolor *color = gpencil_palettecolor_addnew(palette, DATA_("Color"), true); + bGPDpalettecolor *color = BKE_gpencil_palettecolor_addnew(palette, DATA_("Color"), true); return color; } @@ -726,7 +739,7 @@ static void rna_GPencilPalette_color_remove(bGPDpalette *palette, ReportList *re return; } - gpencil_palettecolor_delete(palette, color); + BKE_gpencil_palettecolor_delete(palette, color); RNA_POINTER_INVALIDATE(color_ptr); WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL); @@ -755,7 +768,7 @@ static void rna_GPencilPalette_active_color_set(PointerRNA *ptr, PointerRNA valu bGPDpalette *palette = (bGPDpalette *)ptr->data; bGPDpalettecolor *color = value.data; - gpencil_palettecolor_setactive(palette, color); + BKE_gpencil_palettecolor_setactive(palette, color); } static void rna_GPencilPalette_info_set(PointerRNA *ptr, const char *value) @@ -773,7 +786,7 @@ static void rna_GPencilPalette_info_set(PointerRNA *ptr, const char *value) static char *rna_GPencilPalette_color_path(PointerRNA *ptr) { bGPdata *gpd = ptr->id.data; - bGPDpalette *palette = gpencil_palette_getactive(gpd); + bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); bGPDpalettecolor *palcolor = ptr->data; char name_palette[sizeof(palette->info) * 2]; @@ -788,11 +801,11 @@ static char *rna_GPencilPalette_color_path(PointerRNA *ptr) static void rna_GPencilPaletteColor_info_set(PointerRNA *ptr, const char *value) { bGPdata *gpd = ptr->id.data; - bGPDpalette *palette = gpencil_palette_getactive(gpd); + bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); bGPDpalettecolor *palcolor = ptr->data; /* rename all strokes */ - gpencil_palettecolor_changename(gpd, palcolor->info, value); + BKE_gpencil_palettecolor_changename(gpd, palcolor->info, value); /* copy the new name into the name slot */ BLI_strncpy_utf8(palcolor->info, value, sizeof(palcolor->info)); @@ -824,7 +837,7 @@ static int rna_GPencilPaletteColor_is_fill_visible_get(PointerRNA *ptr) static int rna_GPencilPaletteColor_index_get(PointerRNA *ptr) { bGPDpalette *palette = (bGPDpalette *)ptr->data; - bGPDpalettecolor *pcolor = gpencil_palettecolor_getactive(palette); + bGPDpalettecolor *pcolor = BKE_gpencil_palettecolor_getactive(palette); return BLI_findindex(&palette->colors, pcolor); } @@ -833,7 +846,7 @@ static void rna_GPencilPaletteColor_index_set(PointerRNA *ptr, int value) { bGPDpalette *palette = (bGPDpalette *)ptr->data; bGPDpalettecolor *pcolor = BLI_findlink(&palette->colors, value); - gpencil_palettecolor_setactive(palette, pcolor); + BKE_gpencil_palettecolor_setactive(palette, pcolor); } static void rna_GPencilPaletteColor_index_range(PointerRNA *ptr, int *min, int *max, int *softmin, int *softmax) @@ -886,9 +899,7 @@ static void rna_def_gpencil_stroke_point(BlenderRNA *brna) static void rna_def_gpencil_stroke_points_api(BlenderRNA *brna, PropertyRNA *cprop) { StructRNA *srna; - FunctionRNA *func; - /* PropertyRNA *parm; */ RNA_def_property_srna(cprop, "GPencilStrokePoints"); srna = RNA_def_struct(brna, "GPencilStrokePoints", NULL); @@ -898,6 +909,8 @@ static void rna_def_gpencil_stroke_points_api(BlenderRNA *brna, PropertyRNA *cpr func = RNA_def_function(srna, "add", "rna_GPencil_stroke_point_add"); RNA_def_function_ui_description(func, "Add a new grease pencil stroke point"); RNA_def_int(func, "count", 1, 0, INT_MAX, "Number", "Number of points to add to the stroke", 0, INT_MAX); + RNA_def_float(func, "pressure", 1.0f, 0.0f, 1.0f, "Pressure", "Pressure for newly created points", 0.0f, 1.0f); + RNA_def_float(func, "strength", 1.0f, 0.0f, 1.0f, "Strength", "Color intensity (alpha factor) for newly created points", 0.0f, 1.0f); func = RNA_def_function(srna, "pop", "rna_GPencil_stroke_point_pop"); RNA_def_function_ui_description(func, "Remove a grease pencil stroke point"); @@ -918,19 +931,19 @@ static void rna_def_gpencil_triangle(BlenderRNA *brna) /* point v1 */ prop = RNA_def_property(srna, "v1", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "v1"); - RNA_def_property_ui_text(prop, "v1", "First triangle vertice index"); + RNA_def_property_ui_text(prop, "v1", "First triangle vertex index"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); /* point v2 */ prop = RNA_def_property(srna, "v2", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "v2"); - RNA_def_property_ui_text(prop, "v2", "Second triangle vertice index"); + RNA_def_property_ui_text(prop, "v2", "Second triangle vertex index"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); /* point v3 */ prop = RNA_def_property(srna, "v3", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "v3"); - RNA_def_property_ui_text(prop, "v3", "Third triangle vertice index"); + RNA_def_property_ui_text(prop, "v3", "Third triangle vertex index"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); } @@ -1145,7 +1158,8 @@ static void rna_def_gpencil_layer(BlenderRNA *brna) // TODO: replace these with a "draw type" combo (i.e. strokes only, filled strokes, strokes + fills, volumetric)? prop = RNA_def_property(srna, "use_volumetric_strokes", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LAYER_VOLUMETRIC); - RNA_def_property_ui_text(prop, "Volumetric Strokes", "Draw strokes as a series of circular blobs, resulting in a volumetric effect"); + RNA_def_property_ui_text(prop, "Volumetric Strokes", + "Draw strokes as a series of circular blobs, resulting in a volumetric effect"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); prop = RNA_def_property(srna, "opacity", PROP_FLOAT, PROP_NONE); @@ -1174,7 +1188,7 @@ static void rna_def_gpencil_layer(BlenderRNA *brna) RNA_def_property_int_sdna(prop, NULL, "thickness"); //RNA_def_property_range(prop, 1, 10); /* 10 px limit comes from Windows OpenGL limits for natively-drawn strokes */ RNA_def_property_int_funcs(prop, NULL, NULL, "rna_GPencilLayer_line_width_range"); - RNA_def_property_ui_text(prop, "Thickness", "Thickness change to apply current strokes (in pixels)"); + RNA_def_property_ui_text(prop, "Thickness", "Thickness change to apply to current strokes (in pixels)"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); /* Onion-Skinning */ @@ -1241,8 +1255,8 @@ static void rna_def_gpencil_layer(BlenderRNA *brna) prop = RNA_def_property(srna, "unlock_color", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LAYER_UNLOCK_COLOR); RNA_def_property_ui_icon(prop, ICON_RESTRICT_COLOR_OFF, 1); - RNA_def_property_ui_text(prop, "Unlock color", "Unprotect colors selected from further editing " - "and/or frame changes"); + RNA_def_property_ui_text(prop, "Unlock Color", + "Unprotect selected colors from further editing and/or frame changes"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); @@ -1299,7 +1313,7 @@ static void rna_def_gpencil_layer(BlenderRNA *brna) RNA_def_property_float_sdna(prop, NULL, "inverse"); RNA_def_property_multi_array(prop, 2, rna_matrix_dimsize_4x4); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_ui_text(prop, "MatrixInverse", "Parent inverse transformation matrix"); + RNA_def_property_ui_text(prop, "Inverse Matrix", "Parent inverse transformation matrix"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); /* read only parented flag */ @@ -1381,8 +1395,9 @@ static void rna_def_gpencil_palettecolor(BlenderRNA *brna) RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); /* Name */ - prop = RNA_def_property(srna, "info", PROP_STRING, PROP_NONE); - RNA_def_property_ui_text(prop, "Info", "Color name"); + prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "info"); + RNA_def_property_ui_text(prop, "Name", "Color name"); RNA_def_property_string_funcs(prop, NULL, NULL, "rna_GPencilPaletteColor_info_set"); RNA_def_struct_name_property(srna, prop); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h index 161e19f581c..364aa9ba939 100644 --- a/source/blender/makesrna/intern/rna_internal.h +++ b/source/blender/makesrna/intern/rna_internal.h @@ -133,6 +133,7 @@ void RNA_def_armature(struct BlenderRNA *brna); void RNA_def_actuator(struct BlenderRNA *brna); void RNA_def_boid(struct BlenderRNA *brna); void RNA_def_brush(struct BlenderRNA *brna); +void RNA_def_cachefile(struct BlenderRNA *brna); void RNA_def_camera(struct BlenderRNA *brna); void RNA_def_cloth(struct BlenderRNA *brna); void RNA_def_color(struct BlenderRNA *brna); @@ -332,6 +333,7 @@ void RNA_def_main_gpencil(BlenderRNA *brna, PropertyRNA *cprop); void RNA_def_main_movieclips(BlenderRNA *brna, PropertyRNA *cprop); void RNA_def_main_masks(BlenderRNA *brna, PropertyRNA *cprop); void RNA_def_main_linestyles(BlenderRNA *brna, PropertyRNA *cprop); +void RNA_def_main_cachefiles(BlenderRNA *brna, PropertyRNA *cprop); /* ID Properties */ diff --git a/source/blender/makesrna/intern/rna_lamp.c b/source/blender/makesrna/intern/rna_lamp.c index e4e3699f301..51709d3137c 100644 --- a/source/blender/makesrna/intern/rna_lamp.c +++ b/source/blender/makesrna/intern/rna_lamp.c @@ -546,7 +546,7 @@ static void rna_def_lamp_shadow(StructRNA *srna, int spot, int area) prop = RNA_def_property(srna, "shadow_method", PROP_ENUM, PROP_NONE); RNA_def_property_enum_bitflag_sdna(prop, NULL, "mode"); RNA_def_property_enum_items(prop, (spot) ? prop_spot_shadow_items : prop_shadow_items); - RNA_def_property_update(prop, 0, "rna_Lamp_update"); + RNA_def_property_update(prop, 0, "rna_Lamp_draw_update"); prop = RNA_def_property(srna, "shadow_buffer_size", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "bufsize"); diff --git a/source/blender/makesrna/intern/rna_linestyle.c b/source/blender/makesrna/intern/rna_linestyle.c index d8cc61bf906..9b28009d161 100644 --- a/source/blender/makesrna/intern/rna_linestyle.c +++ b/source/blender/makesrna/intern/rna_linestyle.c @@ -1499,14 +1499,15 @@ static void rna_def_freestyle_thickness_modifiers(BlenderRNA *brna, PropertyRNA RNA_def_property_srna(cprop, "LineStyleThicknessModifiers"); srna = RNA_def_struct(brna, "LineStyleThicknessModifiers", NULL); RNA_def_struct_sdna(srna, "FreestyleLineStyle"); - RNA_def_struct_ui_text(srna, "Thickness Modifiers", "Thickness modifiers for changing line thicknesss"); + RNA_def_struct_ui_text(srna, "Thickness Modifiers", "Thickness modifiers for changing line thickness"); func = RNA_def_function(srna, "new", "rna_LineStyle_thickness_modifier_add"); RNA_def_function_ui_description(func, "Add a thickness modifier to line style"); RNA_def_function_flag(func, FUNC_USE_REPORTS); parm = RNA_def_string(func, "name", "ThicknessModifier", 0, "", "New name for the thickness modifier (not unique)"); RNA_def_property_flag(parm, PROP_REQUIRED); - parm = RNA_def_enum(func, "type", rna_enum_linestyle_thickness_modifier_type_items, 0, "", "Thickness modifier type to add"); + parm = RNA_def_enum(func, "type", rna_enum_linestyle_thickness_modifier_type_items, 0, + "", "Thickness modifier type to add"); RNA_def_property_flag(parm, PROP_REQUIRED); parm = RNA_def_pointer(func, "modifier", "LineStyleThicknessModifier", "", "Newly added thickness modifier"); RNA_def_function_return(func, parm); @@ -1528,14 +1529,15 @@ static void rna_def_freestyle_geometry_modifiers(BlenderRNA *brna, PropertyRNA * RNA_def_property_srna(cprop, "LineStyleGeometryModifiers"); srna = RNA_def_struct(brna, "LineStyleGeometryModifiers", NULL); RNA_def_struct_sdna(srna, "FreestyleLineStyle"); - RNA_def_struct_ui_text(srna, "Geometry Modifiers", "Geometry modifiers for changing line geometrys"); + RNA_def_struct_ui_text(srna, "Geometry Modifiers", "Geometry modifiers for changing line geometries"); func = RNA_def_function(srna, "new", "rna_LineStyle_geometry_modifier_add"); RNA_def_function_ui_description(func, "Add a geometry modifier to line style"); RNA_def_function_flag(func, FUNC_USE_REPORTS); parm = RNA_def_string(func, "name", "GeometryModifier", 0, "", "New name for the geometry modifier (not unique)"); RNA_def_property_flag(parm, PROP_REQUIRED); - parm = RNA_def_enum(func, "type", rna_enum_linestyle_geometry_modifier_type_items, 0, "", "Geometry modifier type to add"); + parm = RNA_def_enum(func, "type", rna_enum_linestyle_geometry_modifier_type_items, 0, + "", "Geometry modifier type to add"); RNA_def_property_flag(parm, PROP_REQUIRED); parm = RNA_def_pointer(func, "modifier", "LineStyleGeometryModifier", "", "Newly added geometry modifier"); RNA_def_function_return(func, parm); diff --git a/source/blender/makesrna/intern/rna_main.c b/source/blender/makesrna/intern/rna_main.c index e0538d1c05b..3392d0d9b3a 100644 --- a/source/blender/makesrna/intern/rna_main.c +++ b/source/blender/makesrna/intern/rna_main.c @@ -281,6 +281,12 @@ static void rna_Main_linestyle_begin(CollectionPropertyIterator *iter, PointerRN rna_iterator_listbase_begin(iter, &bmain->linestyle, NULL); } +static void rna_Main_cachefiles_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) +{ + Main *bmain = (Main *)ptr->data; + rna_iterator_listbase_begin(iter, &bmain->cachefiles, NULL); +} + static void rna_Main_version_get(PointerRNA *ptr, int *value) { Main *bmain = (Main *)ptr->data; @@ -354,6 +360,7 @@ void RNA_def_main(BlenderRNA *brna) {"movieclips", "MovieClip", "rna_Main_movieclips_begin", "Movie Clips", "Movie Clip datablocks", RNA_def_main_movieclips}, {"masks", "Mask", "rna_Main_masks_begin", "Masks", "Masks datablocks", RNA_def_main_masks}, {"linestyles", "FreestyleLineStyle", "rna_Main_linestyle_begin", "Line Styles", "Line Style datablocks", RNA_def_main_linestyles}, + {"cache_files", "CacheFile", "rna_Main_cachefiles_begin", "Cache Files", "Cache Files datablocks", RNA_def_main_cachefiles}, {NULL, NULL, NULL, NULL, NULL, NULL} }; diff --git a/source/blender/makesrna/intern/rna_main_api.c b/source/blender/makesrna/intern/rna_main_api.c index 0083207efd0..7c627e72fd5 100644 --- a/source/blender/makesrna/intern/rna_main_api.c +++ b/source/blender/makesrna/intern/rna_main_api.c @@ -535,6 +535,7 @@ RNA_MAIN_ID_TAG_FUNCS_DEF(gpencil, gpencil, ID_GD) RNA_MAIN_ID_TAG_FUNCS_DEF(movieclips, movieclip, ID_MC) RNA_MAIN_ID_TAG_FUNCS_DEF(masks, mask, ID_MSK) RNA_MAIN_ID_TAG_FUNCS_DEF(linestyle, linestyle, ID_LS) +RNA_MAIN_ID_TAG_FUNCS_DEF(cachefiles, cachefiles, ID_CF) #undef RNA_MAIN_ID_TAG_FUNCS_DEF @@ -1548,6 +1549,21 @@ void RNA_def_main_palettes(BlenderRNA *brna, PropertyRNA *cprop) RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_boolean_funcs(prop, "rna_Main_palettes_is_updated_get", NULL); } +void RNA_def_main_cachefiles(BlenderRNA *brna, PropertyRNA *cprop) +{ + RNA_def_property_srna(cprop, "BlendDataCacheFiles"); + StructRNA *srna = RNA_def_struct(brna, "BlendDataCacheFiles", NULL); + RNA_def_struct_sdna(srna, "Main"); + RNA_def_struct_ui_text(srna, "Main Cache Files", "Collection of cache files"); + + FunctionRNA *func = RNA_def_function(srna, "tag", "rna_Main_cachefiles_tag"); + PropertyRNA *parm = RNA_def_boolean(func, "value", 0, "Value", ""); + RNA_def_property_flag(parm, PROP_REQUIRED); + + PropertyRNA *prop = RNA_def_property(srna, "is_updated", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_boolean_funcs(prop, "rna_Main_cachefiles_is_updated_get", NULL); +} void RNA_def_main_gpencil(BlenderRNA *brna, PropertyRNA *cprop) { StructRNA *srna; @@ -1564,7 +1580,7 @@ void RNA_def_main_gpencil(BlenderRNA *brna, PropertyRNA *cprop) parm = RNA_def_boolean(func, "value", 0, "Value", ""); RNA_def_property_flag(parm, PROP_REQUIRED); - func = RNA_def_function(srna, "new", "gpencil_data_addnew"); + func = RNA_def_function(srna, "new", "BKE_gpencil_data_addnew"); RNA_def_function_flag(func, FUNC_NO_SELF); parm = RNA_def_string(func, "name", "GreasePencil", 0, "", "New name for the data-block"); RNA_def_property_flag(parm, PROP_REQUIRED); diff --git a/source/blender/makesrna/intern/rna_material.c b/source/blender/makesrna/intern/rna_material.c index e4f9f856db7..2a8cc073e22 100644 --- a/source/blender/makesrna/intern/rna_material.c +++ b/source/blender/makesrna/intern/rna_material.c @@ -1889,7 +1889,7 @@ void RNA_def_material(BlenderRNA *brna) prop = RNA_def_property(srna, "pass_index", PROP_INT, PROP_UNSIGNED); RNA_def_property_int_sdna(prop, NULL, "index"); - RNA_def_property_ui_text(prop, "Pass Index", "Index number for the IndexMA render pass"); + RNA_def_property_ui_text(prop, "Pass Index", "Index number for the \"Material Index\" render pass"); RNA_def_property_update(prop, NC_OBJECT, "rna_Material_update"); /* flags */ diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c index a23ef6eaa82..0b55c19c374 100644 --- a/source/blender/makesrna/intern/rna_modifier.c +++ b/source/blender/makesrna/intern/rna_modifier.c @@ -30,6 +30,7 @@ #include <stdlib.h> #include "DNA_armature_types.h" +#include "DNA_cachefile_types.h" #include "DNA_mesh_types.h" #include "DNA_modifier_types.h" #include "DNA_object_types.h" @@ -65,6 +66,7 @@ EnumPropertyItem rna_enum_object_modifier_type_items[] = { {0, "", 0, N_("Modify"), ""}, {eModifierType_DataTransfer, "DATA_TRANSFER", ICON_MOD_DATA_TRANSFER, "Data Transfer", ""}, {eModifierType_MeshCache, "MESH_CACHE", ICON_MOD_MESHDEFORM, "Mesh Cache", ""}, + {eModifierType_MeshSequenceCache, "MESH_SEQUENCE_CACHE", ICON_MOD_MESHDEFORM, "Mesh Sequence Cache", ""}, {eModifierType_NormalEdit, "NORMAL_EDIT", ICON_MOD_NORMALEDIT, "Normal Edit", ""}, {eModifierType_UVProject, "UV_PROJECT", ICON_MOD_UVPROJECT, "UV Project", ""}, {eModifierType_UVWarp, "UV_WARP", ICON_MOD_UVPROJECT, "UV Warp", ""}, @@ -281,6 +283,7 @@ EnumPropertyItem rna_enum_axis_flag_xyz_items[] = { #include "DNA_curve_types.h" #include "DNA_smoke_types.h" +#include "BKE_cachefile.h" #include "BKE_context.h" #include "BKE_depsgraph.h" #include "BKE_library.h" @@ -288,6 +291,10 @@ EnumPropertyItem rna_enum_axis_flag_xyz_items[] = { #include "BKE_object.h" #include "BKE_particle.h" +#ifdef WITH_ALEMBIC +# include "ABC_alembic.h" +#endif + static void rna_UVProject_projectors_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) { UVProjectModifierData *uvp = (UVProjectModifierData *)ptr->data; @@ -399,6 +406,8 @@ static StructRNA *rna_Modifier_refine(struct PointerRNA *ptr) return &RNA_NormalEditModifier; case eModifierType_CorrectiveSmooth: return &RNA_CorrectiveSmoothModifier; + case eModifierType_MeshSequenceCache: + return &RNA_MeshSequenceCacheModifier; /* Default */ case eModifierType_None: case eModifierType_ShapeKey: @@ -4216,6 +4225,42 @@ static void rna_def_modifier_meshcache(BlenderRNA *brna) RNA_def_property_update(prop, 0, "rna_Modifier_update"); } +static void rna_def_modifier_meshseqcache(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "MeshSequenceCacheModifier", "Modifier"); + RNA_def_struct_ui_text(srna, "Cache Modifier", "Cache Mesh"); + RNA_def_struct_sdna(srna, "MeshSeqCacheModifierData"); + RNA_def_struct_ui_icon(srna, ICON_MOD_MESHDEFORM); /* XXX, needs own icon */ + + prop = RNA_def_property(srna, "cache_file", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "cache_file"); + RNA_def_property_struct_type(prop, "CacheFile"); + RNA_def_property_ui_text(prop, "Cache File", ""); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK); + RNA_def_property_update(prop, 0, "rna_Modifier_dependency_update"); + + prop = RNA_def_property(srna, "object_path", PROP_STRING, PROP_NONE); + RNA_def_property_ui_text(prop, "Object Path", "Path to the object in the Alembic archive used to lookup geometric data"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + static EnumPropertyItem read_flag_items[] = { + {MOD_MESHSEQ_READ_VERT, "VERT", 0, "Vertex", ""}, + {MOD_MESHSEQ_READ_POLY, "POLY", 0, "Faces", ""}, + {MOD_MESHSEQ_READ_UV, "UV", 0, "UV", ""}, + {MOD_MESHSEQ_READ_COLOR, "COLOR", 0, "Color", ""}, + {0, NULL, 0, NULL, NULL} + }; + + prop = RNA_def_property(srna, "read_data", PROP_ENUM, PROP_NONE); + RNA_def_property_flag(prop, PROP_ENUM_FLAG); + RNA_def_property_enum_sdna(prop, NULL, "read_flag"); + RNA_def_property_enum_items(prop, read_flag_items); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); +} + static void rna_def_modifier_laplaciandeform(BlenderRNA *brna) { StructRNA *srna; @@ -4745,6 +4790,7 @@ void RNA_def_modifier(BlenderRNA *brna) rna_def_modifier_wireframe(brna); rna_def_modifier_datatransfer(brna); rna_def_modifier_normaledit(brna); + rna_def_modifier_meshseqcache(brna); } #endif diff --git a/source/blender/makesrna/intern/rna_movieclip.c b/source/blender/makesrna/intern/rna_movieclip.c index 19d78361019..15411f85ba3 100644 --- a/source/blender/makesrna/intern/rna_movieclip.c +++ b/source/blender/makesrna/intern/rna_movieclip.c @@ -347,6 +347,8 @@ static void rna_def_movieclip(BlenderRNA *brna) RNA_def_property_pointer_sdna(prop, NULL, "colorspace_settings"); RNA_def_property_struct_type(prop, "ColorManagedInputColorspaceSettings"); RNA_def_property_ui_text(prop, "Color Space Settings", "Input color space settings"); + + rna_def_animdata_common(srna); } void RNA_def_movieclip(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 2f9e6741677..a37fe049e72 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -5391,13 +5391,13 @@ static void def_cmp_double_edge_mask(StructRNA *srna) }; prop = RNA_def_property(srna, "inner_mode", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_sdna(prop, NULL, "custom2"); + RNA_def_property_enum_sdna(prop, NULL, "custom1"); RNA_def_property_enum_items(prop, InnerEdgeMode_items); RNA_def_property_ui_text(prop, "Inner Edge Mode", ""); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); prop = RNA_def_property(srna, "edge_mode", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_sdna(prop, NULL, "custom1"); + RNA_def_property_enum_sdna(prop, NULL, "custom2"); RNA_def_property_enum_items(prop, BufEdgeMode_items); RNA_def_property_ui_text(prop, "Buffer Edge Mode", ""); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c index 32f7e5f0226..194bb1eaa14 100644 --- a/source/blender/makesrna/intern/rna_object.c +++ b/source/blender/makesrna/intern/rna_object.c @@ -2652,7 +2652,7 @@ static void rna_def_object(BlenderRNA *brna) /* render */ prop = RNA_def_property(srna, "pass_index", PROP_INT, PROP_UNSIGNED); RNA_def_property_int_sdna(prop, NULL, "index"); - RNA_def_property_ui_text(prop, "Pass Index", "Index number for the IndexOB render pass"); + RNA_def_property_ui_text(prop, "Pass Index", "Index number for the \"Object Index\" render pass"); RNA_def_property_update(prop, NC_OBJECT, "rna_Object_internal_update"); prop = RNA_def_property(srna, "color", PROP_FLOAT, PROP_COLOR); diff --git a/source/blender/makesrna/intern/rna_object_force.c b/source/blender/makesrna/intern/rna_object_force.c index f05813043ca..1d89f7535c4 100644 --- a/source/blender/makesrna/intern/rna_object_force.c +++ b/source/blender/makesrna/intern/rna_object_force.c @@ -730,6 +730,11 @@ static void rna_softbody_update(Main *UNUSED(bmain), Scene *UNUSED(scene), Point WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, ob); } +static void rna_softbody_dependency_update(Main *bmain, Scene *scene, PointerRNA *ptr) +{ + DAG_relations_tag_update(bmain); + rna_softbody_update(bmain, scene, ptr); +} static EnumPropertyItem *rna_Effector_shape_itemf(bContext *UNUSED(C), PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *UNUSED(r_free)) @@ -1378,7 +1383,7 @@ static void rna_def_field(BlenderRNA *brna) prop = RNA_def_property(srna, "use_absorption", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", PFIELD_VISIBILITY); RNA_def_property_ui_text(prop, "Absorption", "Force gets absorbed by collision objects"); - RNA_def_property_update(prop, 0, "rna_FieldSettings_update"); + RNA_def_property_update(prop, 0, "rna_FieldSettings_dependency_update"); prop = RNA_def_property(srna, "use_multiple_springs", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", PFIELD_MULTIPLE_SPRINGS); @@ -1855,7 +1860,7 @@ static void rna_def_softbody(BlenderRNA *brna) prop = RNA_def_property(srna, "collision_group", PROP_POINTER, PROP_NONE); RNA_def_property_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Collision Group", "Limit colliders to this Group"); - RNA_def_property_update(prop, 0, "rna_softbody_update"); + RNA_def_property_update(prop, 0, "rna_softbody_dependency_update"); prop = RNA_def_property(srna, "effector_weights", PROP_POINTER, PROP_NONE); RNA_def_property_pointer_sdna(prop, NULL, "effector_weights"); diff --git a/source/blender/makesrna/intern/rna_particle.c b/source/blender/makesrna/intern/rna_particle.c index eabf41cb332..5e3fa4b467d 100644 --- a/source/blender/makesrna/intern/rna_particle.c +++ b/source/blender/makesrna/intern/rna_particle.c @@ -630,6 +630,12 @@ static void rna_Particle_reset(Main *bmain, Scene *scene, PointerRNA *ptr) particle_recalc(bmain, scene, ptr, PSYS_RECALC_RESET); } +static void rna_Particle_reset_dependency(Main *bmain, Scene *scene, PointerRNA *ptr) +{ + DAG_relations_tag_update(bmain); + rna_Particle_reset(bmain, scene, ptr); +} + static void rna_Particle_change_type(Main *bmain, Scene *scene, PointerRNA *ptr) { particle_recalc(bmain, scene, ptr, PSYS_RECALC_RESET | PSYS_RECALC_TYPE); @@ -2744,7 +2750,7 @@ static void rna_def_particle_settings(BlenderRNA *brna) prop = RNA_def_property(srna, "collision_group", PROP_POINTER, PROP_NONE); RNA_def_property_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Collision Group", "Limit colliders to this Group"); - RNA_def_property_update(prop, 0, "rna_Particle_reset"); + RNA_def_property_update(prop, 0, "rna_Particle_reset_dependency"); /* global physical properties */ prop = RNA_def_property(srna, "drag_factor", PROP_FLOAT, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 19f25ff1ee9..fef9435b567 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -454,7 +454,7 @@ EnumPropertyItem rna_enum_bake_pass_filter_type_items[] = { /* Grease pencil Drawing Brushes */ static bGPDbrush *rna_GPencil_brush_new(ToolSettings *ts, const char *name, int setactive) { - bGPDbrush *brush = gpencil_brush_addnew(ts, name, setactive != 0); + bGPDbrush *brush = BKE_gpencil_brush_addnew(ts, name, setactive != 0); WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL); @@ -469,13 +469,13 @@ static void rna_GPencil_brush_remove(ToolSettings *ts, ReportList *reports, Poin return; } - gpencil_brush_delete(ts, brush); + BKE_gpencil_brush_delete(ts, brush); RNA_POINTER_INVALIDATE(brush_ptr); WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL); } -static PointerRNA rna_GPencil_active_brush_get(PointerRNA *ptr) +static PointerRNA rna_GPencilBrushes_active_get(PointerRNA *ptr) { ToolSettings *ts = (ToolSettings *) ptr->data; @@ -494,7 +494,7 @@ static PointerRNA rna_GPencil_active_brush_get(PointerRNA *ptr) return rna_pointer_inherit_refine(ptr, NULL, NULL); } -static void rna_GPencil_active_brush_set(PointerRNA *ptr, PointerRNA value) +static void rna_GPencilBrushes_active_set(PointerRNA *ptr, PointerRNA value) { ToolSettings *ts = (ToolSettings *) ptr->data; @@ -511,25 +511,25 @@ static void rna_GPencil_active_brush_set(PointerRNA *ptr, PointerRNA value) WM_main_add_notifier(NC_GPENCIL | NA_EDITED, NULL); } -static int rna_GPencilBrush_index_get(PointerRNA *ptr) +static int rna_GPencilBrushes_index_get(PointerRNA *ptr) { ToolSettings *ts = (ToolSettings *) ptr->data; - bGPDbrush *brush = gpencil_brush_getactive(ts); + bGPDbrush *brush = BKE_gpencil_brush_getactive(ts); return BLI_findindex(&ts->gp_brushes, brush); } -static void rna_GPencilBrush_index_set(PointerRNA *ptr, int value) +static void rna_GPencilBrushes_index_set(PointerRNA *ptr, int value) { ToolSettings *ts = (ToolSettings *) ptr->data; bGPDbrush *brush = BLI_findlink(&ts->gp_brushes, value); - gpencil_brush_setactive(ts, brush); + BKE_gpencil_brush_setactive(ts, brush); WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL); } -static void rna_GPencilBrush_index_range(PointerRNA *ptr, int *min, int *max, int *softmin, int *softmax) +static void rna_GPencilBrushes_index_range(PointerRNA *ptr, int *min, int *max, int *softmin, int *softmax) { ToolSettings *ts = (ToolSettings *) ptr->data; @@ -542,7 +542,7 @@ static void rna_GPencilBrush_index_range(PointerRNA *ptr, int *min, int *max, in static void rna_GPencilBrush_name_set(PointerRNA *ptr, const char *value) { - ToolSettings *ts = (ToolSettings *) ptr->data; + ToolSettings *ts = ((Scene *) ptr->id.data)->toolsettings; bGPDbrush *brush = ptr->data; /* copy the new name into the name slot */ @@ -2103,8 +2103,8 @@ static void rna_def_gpencil_brush(BlenderRNA *brna) srna = RNA_def_struct(brna, "GPencilBrush", NULL); RNA_def_struct_sdna(srna, "bGPDbrush"); - RNA_def_struct_ui_text(srna, "Grease Pencil Brush", "Collection of brushes being used to control the " - "line style of new strokes"); + RNA_def_struct_ui_text(srna, "Grease Pencil Brush", + "Collection of brushes being used to control the line style of new strokes"); RNA_def_struct_ui_icon(srna, ICON_BRUSH_DATA); /* Name */ @@ -2161,39 +2161,42 @@ static void rna_def_gpencil_brush(BlenderRNA *brna) /* Angle when brush is full size */ prop = RNA_def_property(srna, "angle", PROP_FLOAT, PROP_ANGLE); RNA_def_property_float_sdna(prop, NULL, "draw_angle"); - RNA_def_property_range(prop, 0.0f, M_PI_2); - RNA_def_property_ui_text(prop, "Angle", "Angle of drawing when brush has full size"); + RNA_def_property_range(prop, -M_PI_2, M_PI_2); + RNA_def_property_ui_text(prop, "Angle", + "Direction of the stroke at which brush gives maximal thickness " + "(0° for horizontal)"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); /* Factor to change brush size depending of angle */ prop = RNA_def_property(srna, "angle_factor", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "draw_angle_factor"); RNA_def_property_range(prop, 0.0f, 1.0f); - RNA_def_property_ui_text(prop, "Angle Factor", "Factor to apply when the brush rotate of its full size"); + RNA_def_property_ui_text(prop, "Angle Factor", + "Reduce brush thickness by this factor when stroke is perpendicular to 'Angle' direction"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); /* Smoothing factor for new strokes */ prop = RNA_def_property(srna, "pen_smooth_factor", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "draw_smoothfac"); RNA_def_property_range(prop, 0.0, 2.0f); - RNA_def_property_ui_text(prop, "Smooth", "Amount of smoothing to apply to newly created strokes, to " - "reduce jitter/noise"); + RNA_def_property_ui_text(prop, "Smooth", + "Amount of smoothing to apply to newly created strokes, to reduce jitter/noise"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); /* Iterations of the Smoothing factor */ prop = RNA_def_property(srna, "pen_smooth_steps", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "draw_smoothlvl"); RNA_def_property_range(prop, 1, 3); - RNA_def_property_ui_text(prop, "Iterations", "Number of times to smooth newly created strokes [+ reason/effect " - "of using higher values of this property]"); + RNA_def_property_ui_text(prop, "Iterations", + "Number of times to smooth newly created strokes"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); /* Subdivision level for new strokes */ prop = RNA_def_property(srna, "pen_subdivision_steps", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "sublevel"); RNA_def_property_range(prop, 0, 3); - RNA_def_property_ui_text(prop, "Subdivision Steps", "Number of times to subdivide newly created strokes, for " - "less jagged strokes"); + RNA_def_property_ui_text(prop, "Subdivision Steps", + "Number of times to subdivide newly created strokes, for less jagged strokes"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); /* Curves for pressure */ @@ -2249,7 +2252,7 @@ static void rna_def_gpencil_brush(BlenderRNA *brna) } /* Grease Pencil Drawing Brushes API */ -static void rna_def_gpencil_brushes_api(BlenderRNA *brna, PropertyRNA *cprop) +static void rna_def_gpencil_brushes(BlenderRNA *brna, PropertyRNA *cprop) { StructRNA *srna; PropertyRNA *prop; @@ -2279,16 +2282,16 @@ static void rna_def_gpencil_brushes_api(BlenderRNA *brna, PropertyRNA *cprop) prop = RNA_def_property(srna, "active", PROP_POINTER, PROP_NONE); RNA_def_property_struct_type(prop, "GPencilBrush"); - RNA_def_property_pointer_funcs(prop, "rna_GPencil_active_brush_get", "rna_GPencil_active_brush_set", NULL, NULL); + RNA_def_property_pointer_funcs(prop, "rna_GPencilBrushes_active_get", "rna_GPencilBrushes_active_set", NULL, NULL); RNA_def_property_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Active Brush", "Current active brush"); prop = RNA_def_property(srna, "active_index", PROP_INT, PROP_UNSIGNED); RNA_def_property_int_funcs(prop, - "rna_GPencilBrush_index_get", - "rna_GPencilBrush_index_set", - "rna_GPencilBrush_index_range"); - RNA_def_property_ui_text(prop, "Active brush Index", "Index of active brush"); + "rna_GPencilBrushes_index_get", + "rna_GPencilBrushes_index_set", + "rna_GPencilBrushes_index_range"); + RNA_def_property_ui_text(prop, "Active Brush Index", "Index of active brush"); } static void rna_def_transform_orientation(BlenderRNA *brna) @@ -2611,6 +2614,12 @@ static void rna_def_tool_settings(BlenderRNA *brna) "are included as the basis for the new one"); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + prop = RNA_def_property(srna, "use_gpencil_draw_onback", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "gpencil_flags", GP_TOOL_FLAG_PAINT_ONBACK); + RNA_def_property_ui_text(prop, "Draw Strokes on Back", + "When draw new strokes, the new stroke is drawn below of all strokes in the layer"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + prop = RNA_def_property(srna, "grease_pencil_source", PROP_ENUM, PROP_NONE); RNA_def_property_enum_bitflag_sdna(prop, NULL, "gpencil_src"); RNA_def_property_enum_items(prop, gpencil_source_3d_items); @@ -2628,7 +2637,7 @@ static void rna_def_tool_settings(BlenderRNA *brna) RNA_def_property_collection_sdna(prop, NULL, "gp_brushes", NULL); RNA_def_property_struct_type(prop, "GPencilBrush"); RNA_def_property_ui_text(prop, "Grease Pencil Brushes", "Grease Pencil drawing brushes"); - rna_def_gpencil_brushes_api(brna, prop); + rna_def_gpencil_brushes(brna, prop); /* Grease Pencil - 3D View Stroke Placement */ prop = RNA_def_property(srna, "gpencil_stroke_placement_view3d", PROP_ENUM, PROP_NONE); @@ -6290,6 +6299,11 @@ static void rna_def_scene_render_data(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Stamp Output", "Render the stamp info text in the rendered image"); RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL); + prop = RNA_def_property(srna, "use_stamp_labels", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "stamp", R_STAMP_HIDE_LABELS); + RNA_def_property_ui_text(prop, "Stamp Labels", "Draw stamp labels (\"Camera\" in front of camera name, etc.)"); + RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL); + prop = RNA_def_property(srna, "use_stamp_strip_meta", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "stamp", R_STAMP_STRIPMETA); RNA_def_property_ui_text(prop, "Strip Metadata", "Use metadata from the strips in the sequencer"); diff --git a/source/blender/makesrna/intern/rna_scene_api.c b/source/blender/makesrna/intern/rna_scene_api.c index 1d3537dc9a0..519f28bea24 100644 --- a/source/blender/makesrna/intern/rna_scene_api.c +++ b/source/blender/makesrna/intern/rna_scene_api.c @@ -37,6 +37,7 @@ #include "BLI_path_util.h" #include "RNA_define.h" +#include "RNA_enum_types.h" #include "DNA_anim_types.h" #include "DNA_object_types.h" @@ -44,6 +45,18 @@ #include "rna_internal.h" /* own include */ +#ifdef WITH_ALEMBIC +# include "../../alembic/ABC_alembic.h" +#endif + +EnumPropertyItem rna_enum_abc_compression_items[] = { +#ifdef WITH_ALEMBIC + { ABC_ARCHIVE_OGAWA, "OGAWA", 0, "Ogawa", "" }, + { ABC_ARCHIVE_HDF5, "HDF5", 0, "HDF5", "" }, +#endif + { 0, NULL, 0, NULL, NULL } +}; + #ifdef RNA_RUNTIME #include "BKE_animsys.h" @@ -173,6 +186,73 @@ static void rna_Scene_ray_cast( } } +#ifdef WITH_ALEMBIC + +static void rna_Scene_alembic_export( + Scene *scene, + bContext *C, + const char *filepath, + int frame_start, + int frame_end, + int xform_samples, + int geom_samples, + float shutter_open, + float shutter_close, + int selected_only, + int uvs, + int normals, + int vcolors, + int apply_subdiv, + int flatten_hierarchy, + int visible_layers_only, + int renderable_only, + int face_sets, + int use_subdiv_schema, + int compression_type, + int packuv, + float scale) +{ +/* We have to enable allow_threads, because we may change scene frame number + * during export. */ +#ifdef WITH_PYTHON + BPy_BEGIN_ALLOW_THREADS; +#endif + + const struct AlembicExportParams params = { + .frame_start = frame_start, + .frame_end = frame_end, + + .frame_step_xform = 1.0 / (double)xform_samples, + .frame_step_shape = 1.0 / (double)geom_samples, + + .shutter_open = shutter_open, + .shutter_close = shutter_close, + + .selected_only = selected_only, + .uvs = uvs, + .normals = normals, + .vcolors = vcolors, + .apply_subdiv = apply_subdiv, + .flatten_hierarchy = flatten_hierarchy, + .visible_layers_only = visible_layers_only, + .renderable_only = renderable_only, + .face_sets = face_sets, + .use_subdiv_schema = use_subdiv_schema, + .compression_type = compression_type, + .packuv = packuv, + + .global_scale = scale, + }; + + ABC_export(scene, C, filepath, ¶ms); + +#ifdef WITH_PYTHON + BPy_END_ALLOW_THREADS; +#endif +} + +#endif + #ifdef WITH_COLLADA /* don't remove this, as COLLADA exporting cannot be done through operators in render() callback. */ #include "../../collada/collada.h" @@ -198,8 +278,8 @@ static void rna_Scene_collada_export( int use_object_instantiation, int use_blender_profile, int sort_by_name, - int export_transformation_type, - int open_sim) + int open_sim, + int export_transformation_type) { collada_export(scene, filepath, apply_modifiers, export_mesh_type, selected, include_children, include_armatures, include_shapekeys, deform_bones_only, @@ -296,6 +376,37 @@ void RNA_api_scene(StructRNA *srna) RNA_def_function_ui_description(func, "Export to collada file"); #endif + +#ifdef WITH_ALEMBIC + func = RNA_def_function(srna, "alembic_export", "rna_Scene_alembic_export"); + RNA_def_function_ui_description(func, "Export to Alembic file"); + + parm = RNA_def_string(func, "filepath", NULL, FILE_MAX, "File Path", "File path to write Alembic file"); + RNA_def_property_flag(parm, PROP_REQUIRED); + RNA_def_property_subtype(parm, PROP_FILEPATH); /* allow non utf8 */ + + RNA_def_int(func, "frame_start", 1, INT_MIN, INT_MAX, "Start", "Start Frame", INT_MIN, INT_MAX); + RNA_def_int(func, "frame_end", 1, INT_MIN, INT_MAX, "End", "End Frame", INT_MIN, INT_MAX); + RNA_def_int(func, "xform_samples", 1, 1, 128, "Xform samples", "Transform samples per frame", 1, 128); + RNA_def_int(func, "geom_samples", 1, 1, 128, "Geom samples", "Geometry samples per frame", 1, 128); + RNA_def_float(func, "shutter_open", 0.0f, -1.0f, 1.0f, "Shutter open", "", -1.0f, 1.0f); + RNA_def_float(func, "shutter_close", 1.0f, -1.0f, 1.0f, "Shutter close", "", -1.0f, 1.0f); + RNA_def_boolean(func, "selected_only" , 0, "Selected only", "Export only selected objects"); + RNA_def_boolean(func, "uvs" , 1, "UVs", "Export UVs"); + RNA_def_boolean(func, "normals" , 1, "Normals", "Export cormals"); + RNA_def_boolean(func, "vcolors" , 0, "Vertex colors", "Export vertex colors"); + RNA_def_boolean(func, "apply_subdiv" , 1, "Subsurfs as meshes", "Export subdivision surfaces as meshes"); + RNA_def_boolean(func, "flatten" , 0, "Flatten hierarchy", "Flatten hierarchy"); + RNA_def_boolean(func, "visible_layers_only" , 0, "Visible layers only", "Export only objects in visible layers"); + RNA_def_boolean(func, "renderable_only" , 0, "Renderable objects only", "Export only objects marked renderable in the outliner"); + RNA_def_boolean(func, "face_sets" , 0, "Facesets", "Export face sets"); + RNA_def_boolean(func, "subdiv_schema", 0, "Use Alembic subdivision Schema", "Use Alembic subdivision Schema"); + RNA_def_enum(func, "compression_type", rna_enum_abc_compression_items, 0, "Compression", ""); + RNA_def_boolean(func, "packuv" , 0, "Export with packed UV islands", "Export with packed UV islands"); + RNA_def_float(func, "scale", 1.0f, 0.0001f, 1000.0f, "Scale", "Value by which to enlarge or shrink the objects with respect to the world's origin", 0.0001f, 1000.0f); + + RNA_def_function_flag(func, FUNC_USE_CONTEXT); +#endif } diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.c b/source/blender/makesrna/intern/rna_sculpt_paint.c index acde2e0957e..a8e80dbc4a3 100644 --- a/source/blender/makesrna/intern/rna_sculpt_paint.c +++ b/source/blender/makesrna/intern/rna_sculpt_paint.c @@ -1024,17 +1024,17 @@ static void rna_def_gpencil_sculpt(BlenderRNA *brna) prop = RNA_def_property(srna, "affect_position", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSHEDIT_FLAG_APPLY_POSITION); - RNA_def_property_ui_text(prop, "Affect position", "The brush affects the position of the point"); + RNA_def_property_ui_text(prop, "Affect Position", "The brush affects the position of the point"); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); prop = RNA_def_property(srna, "affect_strength", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSHEDIT_FLAG_APPLY_STRENGTH); - RNA_def_property_ui_text(prop, "Affect strength", "The brush affects the color strength of the point"); + RNA_def_property_ui_text(prop, "Affect Strength", "The brush affects the color strength of the point"); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); prop = RNA_def_property(srna, "affect_thickness", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSHEDIT_FLAG_APPLY_THICKNESS); - RNA_def_property_ui_text(prop, "Affect thickness", "The brush affects the thickness of the point"); + RNA_def_property_ui_text(prop, "Affect Thickness", "The brush affects the thickness of the point"); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); prop = RNA_def_property(srna, "selection_alpha", PROP_FLOAT, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index d6c650545b2..074fc9f68bd 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -3494,6 +3494,7 @@ static void rna_def_space_dopesheet(BlenderRNA *brna) {SACTCONT_SHAPEKEY, "SHAPEKEY", ICON_SHAPEKEY_DATA, "Shape Key Editor", "Edit keyframes in active object's Shape Keys action"}, {SACTCONT_GPENCIL, "GPENCIL", ICON_GREASEPENCIL, "Grease Pencil", "Edit timings for all Grease Pencil sketches in file"}, {SACTCONT_MASK, "MASK", ICON_MOD_MASK, "Mask", "Edit timings for Mask Editor splines"}, + {SACTCONT_CACHEFILE, "CACHEFILE", ICON_FILE, "Cache File", "Edit timings for Cache File data-blocks"}, {0, NULL, 0, NULL, NULL} }; @@ -3938,6 +3939,7 @@ static void rna_def_fileselect_params(BlenderRNA *brna) {FILTER_ID_AR, "ARMATURE", ICON_ARMATURE_DATA, "Armatures", "Show/hide Armature data-blocks"}, {FILTER_ID_BR, "BRUSH", ICON_BRUSH_DATA, "Brushes", "Show/hide Brushes data-blocks"}, {FILTER_ID_CA, "CAMERA", ICON_CAMERA_DATA, "Cameras", "Show/hide Camera data-blocks"}, + {FILTER_ID_CF, "CACHEFILE", ICON_FILE, "Cache Files", "Show/hide Cache File data-blocks"}, {FILTER_ID_CU, "CURVE", ICON_CURVE_DATA, "Curves", "Show/hide Curve data-blocks"}, {FILTER_ID_GD, "GREASE_PENCIL", ICON_GREASEPENCIL, "Grease Pencil", "Show/hide Grease pencil data-blocks"}, {FILTER_ID_GR, "GROUP", ICON_GROUP, "Groups", "Show/hide Group data-blocks"}, @@ -3983,7 +3985,7 @@ static void rna_def_fileselect_params(BlenderRNA *brna) "IMAGE", ICON_IMAGE_DATA, "Images & Sounds", "Show/hide images, movie clips, sounds and masks"}, {FILTER_ID_CA | FILTER_ID_LA | FILTER_ID_SPK | FILTER_ID_WO, "ENVIRONMENT", ICON_WORLD_DATA, "Environment", "Show/hide worlds, lamps, cameras and speakers"}, - {FILTER_ID_BR | FILTER_ID_GD | FILTER_ID_PA | FILTER_ID_PAL | FILTER_ID_PC | FILTER_ID_TXT | FILTER_ID_VF, + {FILTER_ID_BR | FILTER_ID_GD | FILTER_ID_PA | FILTER_ID_PAL | FILTER_ID_PC | FILTER_ID_TXT | FILTER_ID_VF | FILTER_ID_CF, "MISC", ICON_GREASEPENCIL, "Miscellaneous", "Show/hide other data types"}, {0, NULL, 0, NULL, NULL} }; diff --git a/source/blender/makesrna/intern/rna_tracking.c b/source/blender/makesrna/intern/rna_tracking.c index 2564bdb800f..2340345c1c6 100644 --- a/source/blender/makesrna/intern/rna_tracking.c +++ b/source/blender/makesrna/intern/rna_tracking.c @@ -394,6 +394,16 @@ static int rna_track_2d_stabilization(CollectionPropertyIterator *UNUSED(iter), return 0; } +static int rna_track_2d_stabilization_rotation(CollectionPropertyIterator *UNUSED(iter), void *data) +{ + MovieTrackingTrack *track = (MovieTrackingTrack *)data; + + if ((track->flag & TRACK_USE_2D_STAB_ROT) == 0) + return 1; + + return 0; +} + static void rna_tracking_stabTracks_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) { MovieClip *clip = (MovieClip *)ptr->id.data; @@ -421,23 +431,36 @@ static void rna_tracking_stabTracks_active_index_range(PointerRNA *ptr, int *min *max = max_ii(0, clip->tracking.stabilization.tot_track - 1); } -static void rna_tracking_resetIntrinsics(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) +static void rna_tracking_stabRotTracks_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) { MovieClip *clip = (MovieClip *)ptr->id.data; - MovieTracking *tracking = &clip->tracking; + rna_iterator_listbase_begin(iter, &clip->tracking.tracks, rna_track_2d_stabilization_rotation); +} - if (tracking->camera.intrinsics) { - BKE_tracking_distortion_free(tracking->camera.intrinsics); - tracking->camera.intrinsics = NULL; - } +static int rna_tracking_stabRotTracks_active_index_get(PointerRNA *ptr) +{ + MovieClip *clip = (MovieClip *)ptr->id.data; + return clip->tracking.stabilization.act_rot_track; } -static void rna_tracking_flushUpdate(Main *UNUSED(bmain), Scene *scene, PointerRNA *ptr) +static void rna_tracking_stabRotTracks_active_index_set(PointerRNA *ptr, int value) +{ + MovieClip *clip = (MovieClip *)ptr->id.data; + clip->tracking.stabilization.act_rot_track = value; +} + +static void rna_tracking_stabRotTracks_active_index_range(PointerRNA *ptr, int *min, int *max, + int *UNUSED(softmin), int *UNUSED(softmax)) { MovieClip *clip = (MovieClip *)ptr->id.data; - MovieTrackingStabilization *stab = &clip->tracking.stabilization; - stab->ok = 0; + *min = 0; + *max = max_ii(0, clip->tracking.stabilization.tot_rot_track - 1); +} + +static void rna_tracking_flushUpdate(Main *UNUSED(bmain), Scene *scene, PointerRNA *ptr) +{ + MovieClip *clip = (MovieClip *)ptr->id.data; nodeUpdateID(scene->nodetree, &clip->id); @@ -446,6 +469,17 @@ static void rna_tracking_flushUpdate(Main *UNUSED(bmain), Scene *scene, PointerR DAG_id_tag_update(&clip->id, 0); } +static void rna_tracking_resetIntrinsics(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) +{ + MovieClip *clip = (MovieClip *)ptr->id.data; + MovieTracking *tracking = &clip->tracking; + + if (tracking->camera.intrinsics) { + BKE_tracking_distortion_free(tracking->camera.intrinsics); + tracking->camera.intrinsics = NULL; + } +} + static void rna_trackingObject_tracks_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) { MovieTrackingObject *object = (MovieTrackingObject *)ptr->data; @@ -1495,6 +1529,12 @@ static void rna_def_trackingTrack(BlenderRNA *brna) RNA_def_property_range(prop, 0.0f, 1.0f); RNA_def_property_ui_text(prop, "Weight", "Influence of this track on a final solution"); + /* weight_stab */ + prop = RNA_def_property(srna, "weight_stab", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "weight_stab"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "Stab Weight", "Influence of this track on 2D stabilization"); + /* offset */ prop = RNA_def_property(srna, "offset", PROP_FLOAT, PROP_TRANSLATION); RNA_def_property_array(prop, 2); @@ -1634,15 +1674,15 @@ static void rna_def_trackingStabilization(BlenderRNA *brna) PropertyRNA *prop; static EnumPropertyItem filter_items[] = { - {TRACKING_FILTER_NEAREST, "NEAREST", 0, "Nearest", ""}, - {TRACKING_FILTER_BILINEAR, "BILINEAR", 0, "Bilinear", ""}, - {TRACKING_FILTER_BICUBIC, "BICUBIC", 0, "Bicubic", ""}, + {TRACKING_FILTER_NEAREST, "NEAREST", 0, "Nearest", "No interpolation, use nearest neighbor pixel"}, + {TRACKING_FILTER_BILINEAR, "BILINEAR", 0, "Bilinear", "Simple interpolation between adjacent pixels"}, + {TRACKING_FILTER_BICUBIC, "BICUBIC", 0, "Bicubic", "High quality pixel interpolation"}, {0, NULL, 0, NULL, NULL} }; srna = RNA_def_struct(brna, "MovieTrackingStabilization", NULL); RNA_def_struct_path_func(srna, "rna_trackingStabilization_path"); - RNA_def_struct_ui_text(srna, "Movie tracking stabilization data", "Match-moving stabilization data for tracking"); + RNA_def_struct_ui_text(srna, "Movie tracking stabilization data", "2D stabilization based on tracking markers"); /* 2d stabilization */ prop = RNA_def_property(srna, "use_2d_stabilization", PROP_BOOLEAN, PROP_NONE); @@ -1651,22 +1691,30 @@ static void rna_def_trackingStabilization(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Use 2D stabilization", "Use 2D stabilization for footage"); RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, "rna_tracking_flushUpdate"); + /* use_stabilize_rotation */ + prop = RNA_def_property(srna, "use_stabilize_rotation", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", TRACKING_STABILIZE_ROTATION); + RNA_def_property_ui_text(prop, "Stabilize Rotation", "Stabilize detected rotation around center of frame"); + RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, "rna_tracking_flushUpdate"); + + /* use_stabilize_scale */ + prop = RNA_def_property(srna, "use_stabilize_scale", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", TRACKING_STABILIZE_SCALE); + RNA_def_property_ui_text(prop, "Stabilize Scale", "Compensate any scale changes relative to center of rotation"); + RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, "rna_tracking_flushUpdate"); + /* tracks */ prop = RNA_def_property(srna, "tracks", PROP_COLLECTION, PROP_NONE); RNA_def_property_collection_funcs(prop, "rna_tracking_stabTracks_begin", "rna_iterator_listbase_next", "rna_iterator_listbase_end", "rna_iterator_listbase_get", NULL, NULL, NULL, NULL); RNA_def_property_struct_type(prop, "MovieTrackingTrack"); - RNA_def_property_ui_text(prop, "Tracks", "Collection of tracks used for stabilization"); + RNA_def_property_ui_text(prop, "Translation Tracks", + "Collection of tracks used for 2D stabilization (translation)"); RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, "rna_tracking_flushUpdate"); - /* rotation track */ - prop = RNA_def_property(srna, "rotation_track", PROP_POINTER, PROP_NONE); - RNA_def_property_pointer_sdna(prop, NULL, "rot_track"); - RNA_def_property_flag(prop, PROP_EDITABLE); - RNA_def_property_ui_text(prop, "Rotation Track", "Track used to compensate rotation"); - RNA_def_property_update(prop, NC_MOVIECLIP | NA_EDITED, "rna_tracking_flushUpdate"); - /* active track index */ prop = RNA_def_property(srna, "active_track_index", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "act_track"); @@ -1674,7 +1722,65 @@ static void rna_def_trackingStabilization(BlenderRNA *brna) RNA_def_property_int_funcs(prop, "rna_tracking_stabTracks_active_index_get", "rna_tracking_stabTracks_active_index_set", "rna_tracking_stabTracks_active_index_range"); - RNA_def_property_ui_text(prop, "Active Track Index", "Index of active track in stabilization tracks list"); + RNA_def_property_ui_text(prop, "Active Track Index", + "Index of active track in translation stabilization tracks list"); + + /* tracks used for rotation stabilization */ + prop = RNA_def_property(srna, "rotation_tracks", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_funcs(prop, "rna_tracking_stabRotTracks_begin", "rna_iterator_listbase_next", + "rna_iterator_listbase_end", "rna_iterator_listbase_get", + NULL, NULL, NULL, NULL); + RNA_def_property_struct_type(prop, "MovieTrackingTrack"); + RNA_def_property_ui_text(prop, "Rotation Tracks", "Collection of tracks used for 2D stabilization (translation)"); + RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, "rna_tracking_flushUpdate"); + + /* active rotation track index */ + prop = RNA_def_property(srna, "active_rotation_track_index", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "act_rot_track"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_int_funcs(prop, "rna_tracking_stabRotTracks_active_index_get", + "rna_tracking_stabRotTracks_active_index_set", + "rna_tracking_stabRotTracks_active_index_range"); + RNA_def_property_ui_text(prop, "Active Rotation Track Index", + "Index of active track in rotation stabilization tracks list"); + + /* anchor frame */ + prop = RNA_def_property(srna, "anchor_frame", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "anchor_frame"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_range(prop, MINFRAME, MAXFRAME); + RNA_def_property_ui_text(prop, "Anchor Frame", + "Reference point to anchor stabilization " + "(other frames will be adjusted relative to this frame's position)"); + RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, "rna_tracking_flushUpdate"); + + /* target position */ + prop = RNA_def_property(srna, "target_position", PROP_FLOAT, PROP_TRANSLATION); + RNA_def_property_array(prop, 2); + RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, 3); /* increment in steps of 0.01 and show 3 digit after point */ + RNA_def_property_float_sdna(prop, NULL, "target_pos"); + RNA_def_property_ui_text(prop, "Expected Position", + "Known relative offset of original shot, will be subtracted " + "(e.g. for panning shot, can be animated)"); + RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, NULL); + + /* target rotation */ + prop = RNA_def_property(srna, "target_rotation", PROP_FLOAT, PROP_ANGLE); + RNA_def_property_float_sdna(prop, NULL, "target_rot"); + RNA_def_property_range(prop, -FLT_MAX, FLT_MAX); + RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 10.0f, 3); + RNA_def_property_ui_text(prop, "Expected Rotation", + "Rotation present on original shot, will be compensated (e.g. for deliberate tilting)"); + RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, NULL); + + /* target scale */ + prop = RNA_def_property(srna, "target_scale", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "scale"); + RNA_def_property_range(prop, FLT_EPSILON, FLT_MAX); + RNA_def_property_ui_range(prop, 0.01f, 10.0f, 0.001f, 3); /* increment in steps of 0.001. Show 3 digit after point */ + RNA_def_property_ui_text(prop, "Expected Scale", + "Explicitly scale resulting frame to compensate zoom of original shot"); + RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, "rna_tracking_flushUpdate"); /* autoscale */ prop = RNA_def_property(srna, "use_autoscale", PROP_BOOLEAN, PROP_NONE); @@ -1705,13 +1811,6 @@ static void rna_def_trackingStabilization(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Scale Influence", "Influence of stabilization algorithm on footage scale"); RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, "rna_tracking_flushUpdate"); - /* use_stabilize_rotation */ - prop = RNA_def_property(srna, "use_stabilize_rotation", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", TRACKING_STABILIZE_ROTATION); - RNA_def_property_ui_text(prop, "Stabilize Rotation", "Stabilize horizon line on the shot"); - RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, "rna_tracking_flushUpdate"); - /* influence_rotation */ prop = RNA_def_property(srna, "influence_rotation", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_sdna(prop, NULL, "rotinf"); @@ -1723,8 +1822,16 @@ static void rna_def_trackingStabilization(BlenderRNA *brna) prop = RNA_def_property(srna, "filter_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "filter"); RNA_def_property_enum_items(prop, filter_items); - RNA_def_property_ui_text(prop, "Filter", "Method to use to filter stabilization"); + RNA_def_property_ui_text(prop, "Interpolate", + "Interpolation to use for sub-pixel shifts and rotations due to stabilization"); RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, "rna_tracking_flushUpdate"); + + /* UI display : show participating tracks */ + prop = RNA_def_property(srna, "show_tracks_expanded", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", TRACKING_SHOW_STAB_TRACKS); + RNA_def_property_ui_text(prop, "Show Tracks", "Show UI list of tracks participating in stabilization"); + RNA_def_property_ui_icon(prop, ICON_TRIA_RIGHT, 1); } static void rna_def_reconstructedCamera(BlenderRNA *brna) @@ -1987,7 +2094,7 @@ static void rna_def_trackingObjects(BlenderRNA *brna, PropertyRNA *cprop) RNA_def_property_srna(cprop, "MovieTrackingObjects"); srna = RNA_def_struct(brna, "MovieTrackingObjects", NULL); RNA_def_struct_sdna(srna, "MovieTracking"); - RNA_def_struct_ui_text(srna, "Movie Objects", "Collection of movie trackingobjects"); + RNA_def_struct_ui_text(srna, "Movie Objects", "Collection of movie tracking objects"); func = RNA_def_function(srna, "new", "rna_trackingObject_new"); RNA_def_function_ui_description(func, "Add tracking object to this movie clip"); diff --git a/source/blender/makesrna/intern/rna_ui_api.c b/source/blender/makesrna/intern/rna_ui_api.c index 80777f57811..a751c414d83 100644 --- a/source/blender/makesrna/intern/rna_ui_api.c +++ b/source/blender/makesrna/intern/rna_ui_api.c @@ -912,6 +912,11 @@ void RNA_api_ui_layout(StructRNA *srna) RNA_def_function_ui_description(func, "Node Socket Icon"); RNA_def_function_flag(func, FUNC_USE_CONTEXT); RNA_def_float_array(func, "color", 4, node_socket_color_default, 0.0f, 1.0f, "Color", "", 0.0f, 1.0f); + + func = RNA_def_function(srna, "template_cache_file", "uiTemplateCacheFile"); + RNA_def_function_ui_description(func, "Item(s). User interface for selecting cache files and their source paths"); + RNA_def_function_flag(func, FUNC_USE_CONTEXT); + api_ui_item_rna_common(func); } #endif diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index cbf7bb4d5e0..29c0e948a4c 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -292,11 +292,13 @@ static void rna_userdef_autokeymode_set(PointerRNA *ptr, int value) } } +#ifdef WITH_INPUT_NDOF static void rna_userdef_ndof_deadzone_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) { UserDef *userdef = ptr->data; WM_ndof_deadzone_set(userdef->ndof_deadzone); } +#endif static void rna_userdef_timecode_style_set(PointerRNA *ptr, int value) { @@ -4333,6 +4335,7 @@ static void rna_def_userdef_input(BlenderRNA *brna) {0, NULL, 0, NULL, NULL} }; +#ifdef WITH_INPUT_NDOF static EnumPropertyItem ndof_view_navigation_items[] = { {0, "FREE", 0, "Free", "Use full 6 degrees of freedom by default"}, {NDOF_MODE_ORBIT, "ORBIT", 0, "Orbit", "Orbit about the view center by default"}, @@ -4344,6 +4347,7 @@ static void rna_def_userdef_input(BlenderRNA *brna) {0, "TRACKBALL", 0, "Trackball", "Use trackball style rotation in the viewport"}, {0, NULL, 0, NULL, NULL} }; +#endif /* WITH_INPUT_NDOF */ static EnumPropertyItem view_zoom_styles[] = { {USER_ZOOM_CONT, "CONTINUE", 0, "Continue", "Old style zoom, continues while moving mouse up or down"}, @@ -4421,6 +4425,7 @@ static void rna_def_userdef_input(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Tweak Threshold", "Number of pixels you have to drag before tweak event is triggered"); +#ifdef WITH_INPUT_NDOF /* 3D mouse settings */ /* global options */ prop = RNA_def_property(srna, "ndof_sensitivity", PROP_FLOAT, PROP_NONE); @@ -4501,6 +4506,13 @@ static void rna_def_userdef_input(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "ndof_flag", NDOF_FLY_HELICOPTER); RNA_def_property_ui_text(prop, "Helicopter Mode", "Device up/down directly controls your Z position"); + /* let Python know whether NDOF is enabled */ + prop = RNA_def_boolean(srna, "use_ndof", true, "", ""); +#else + prop = RNA_def_boolean(srna, "use_ndof", false, "", ""); +#endif /* WITH_INPUT_NDOF */ + RNA_def_property_flag(prop, PROP_IDPROPERTY); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); prop = RNA_def_property(srna, "mouse_double_click_time", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "dbl_click_time"); diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c index 026d2e209a3..90081a93188 100644 --- a/source/blender/makesrna/intern/rna_wm.c +++ b/source/blender/makesrna/intern/rna_wm.c @@ -167,7 +167,7 @@ static EnumPropertyItem event_ndof_type_items[] = { {NDOF_BUTTON_C, "NDOF_BUTTON_C", 0, "Button C", ""}, {0, NULL, 0, NULL, NULL} }; -#endif +#endif /* RNA_RUNTIME */ /* not returned: CAPSLOCKKEY, UNKNOWNKEY */ EnumPropertyItem rna_enum_event_type_items[] = { @@ -1935,15 +1935,15 @@ static void rna_def_wm_keyconfigs(BlenderRNA *brna, PropertyRNA *cprop) prop = RNA_def_property(srna, "addon", PROP_POINTER, PROP_NEVER_NULL); RNA_def_property_pointer_sdna(prop, NULL, "addonconf"); RNA_def_property_struct_type(prop, "KeyConfig"); - RNA_def_property_ui_text(prop, "Addon Key Configuration", - "Key configuration that can be extended by addons, and is added to the active " + RNA_def_property_ui_text(prop, "Add-on Key Configuration", + "Key configuration that can be extended by add-ons, and is added to the active " "configuration when handling events"); prop = RNA_def_property(srna, "user", PROP_POINTER, PROP_NEVER_NULL); RNA_def_property_pointer_sdna(prop, NULL, "userconf"); RNA_def_property_struct_type(prop, "KeyConfig"); RNA_def_property_ui_text(prop, "User Key Configuration", - "Final key configuration that combines keymaps from the active and addon configurations, " + "Final key configuration that combines keymaps from the active and add-on configurations, " "and can be edited by the user"); RNA_api_keyconfigs(srna); diff --git a/source/blender/modifiers/CMakeLists.txt b/source/blender/modifiers/CMakeLists.txt index 0de7676e8f8..b8ebb375a48 100644 --- a/source/blender/modifiers/CMakeLists.txt +++ b/source/blender/modifiers/CMakeLists.txt @@ -73,6 +73,7 @@ set(SRC intern/MOD_meshcache_pc2.c intern/MOD_meshcache_util.c intern/MOD_meshdeform.c + intern/MOD_meshsequencecache.c intern/MOD_mirror.c intern/MOD_multires.c intern/MOD_none.c @@ -112,6 +113,13 @@ set(SRC intern/MOD_weightvg_util.h ) +if(WITH_ALEMBIC) + add_definitions(-DWITH_ALEMBIC) + list(APPEND INC + ../alembic + ) +endif() + if(WITH_MOD_BOOLEAN) add_definitions(-DWITH_MOD_BOOLEAN) list(APPEND SRC diff --git a/source/blender/modifiers/MOD_modifiertypes.h b/source/blender/modifiers/MOD_modifiertypes.h index a5d96759952..4c881445893 100644 --- a/source/blender/modifiers/MOD_modifiertypes.h +++ b/source/blender/modifiers/MOD_modifiertypes.h @@ -84,6 +84,7 @@ extern ModifierTypeInfo modifierType_Wireframe; extern ModifierTypeInfo modifierType_DataTransfer; extern ModifierTypeInfo modifierType_NormalEdit; extern ModifierTypeInfo modifierType_CorrectiveSmooth; +extern ModifierTypeInfo modifierType_MeshSequenceCache; /* MOD_util.c */ void modifier_type_init(ModifierTypeInfo *types[]); diff --git a/source/blender/modifiers/intern/MOD_cloth.c b/source/blender/modifiers/intern/MOD_cloth.c index 6cc2f097be8..d15a6fcb1c8 100644 --- a/source/blender/modifiers/intern/MOD_cloth.c +++ b/source/blender/modifiers/intern/MOD_cloth.c @@ -123,19 +123,11 @@ static void updateDepgraph(ModifierData *md, DagForest *forest, { ClothModifierData *clmd = (ClothModifierData *) md; - Base *base; - if (clmd) { - for (base = scene->base.first; base; base = base->next) { - Object *ob1 = base->object; - if (ob1 != ob) { - CollisionModifierData *coll_clmd = (CollisionModifierData *)modifiers_findByType(ob1, eModifierType_Collision); - if (coll_clmd) { - DagNode *curNode = dag_get_node(forest, ob1); - dag_add_relation(forest, curNode, obNode, DAG_RL_DATA_DATA | DAG_RL_OB_DATA, "Cloth Collision"); - } - } - } + /* Actual code uses get_collisionobjects */ + dag_add_collision_relations(forest, scene, ob, obNode, clmd->coll_parms->group, ob->lay|scene->lay, eModifierType_Collision, NULL, true, "Cloth Collision"); + + dag_add_forcefield_relations(forest, scene, ob, obNode, clmd->sim_parms->effector_weights, true, 0, "Cloth Field"); } } @@ -147,16 +139,10 @@ static void updateDepsgraph(ModifierData *md, { ClothModifierData *clmd = (ClothModifierData *)md; if (clmd != NULL) { - Base *base; - for (base = scene->base.first; base; base = base->next) { - Object *ob1 = base->object; - if (ob1 != ob) { - CollisionModifierData *coll_clmd = (CollisionModifierData *)modifiers_findByType(ob1, eModifierType_Collision); - if (coll_clmd) { - DEG_add_object_relation(node, ob1, DEG_OB_COMP_TRANSFORM, "Cloth Modifier"); - } - } - } + /* Actual code uses get_collisionobjects */ + DEG_add_collision_relations(node, scene, ob, clmd->coll_parms->group, ob->lay|scene->lay, eModifierType_Collision, NULL, true, "Cloth Collision"); + + DEG_add_forcefield_relations(node, scene, ob, clmd->sim_parms->effector_weights, true, 0, "Cloth Field"); } } diff --git a/source/blender/modifiers/intern/MOD_dynamicpaint.c b/source/blender/modifiers/intern/MOD_dynamicpaint.c index edf959f42c6..bde20e56748 100644 --- a/source/blender/modifiers/intern/MOD_dynamicpaint.c +++ b/source/blender/modifiers/intern/MOD_dynamicpaint.c @@ -114,6 +114,11 @@ static DerivedMesh *applyModifier(ModifierData *md, Object *ob, return dm; } +static bool is_brush_cb(Object *UNUSED(ob), ModifierData *pmd) +{ + return ((DynamicPaintModifierData*)pmd)->brush != NULL; +} + static void updateDepgraph(ModifierData *md, DagForest *forest, struct Main *UNUSED(bmain), struct Scene *scene, @@ -124,16 +129,13 @@ static void updateDepgraph(ModifierData *md, DagForest *forest, /* add relation from canvases to all brush objects */ if (pmd && pmd->canvas) { - Base *base = scene->base.first; - - for (; base; base = base->next) { - DynamicPaintModifierData *pmd2 = - (DynamicPaintModifierData *)modifiers_findByType(base->object, eModifierType_DynamicPaint); - - if (pmd2 && pmd2->brush && ob != base->object) { - DagNode *brushNode = dag_get_node(forest, base->object); - dag_add_relation(forest, brushNode, obNode, DAG_RL_DATA_DATA | DAG_RL_OB_DATA, "Dynamic Paint Brush"); + for (DynamicPaintSurface *surface = pmd->canvas->surfaces.first; surface; surface = surface->next) { + if (surface->effect & MOD_DPAINT_EFFECT_DO_DRIP) { + dag_add_forcefield_relations(forest, scene, ob, obNode, surface->effector_weights, true, 0, "Dynamic Paint Field"); } + + /* Actual code uses custom loop over group/scene without layer checks in dynamicPaint_doStep */ + dag_add_collision_relations(forest, scene, ob, obNode, surface->brush_group, -1, eModifierType_DynamicPaint, is_brush_cb, false, "Dynamic Paint Brush"); } } } @@ -147,13 +149,13 @@ static void updateDepsgraph(ModifierData *md, DynamicPaintModifierData *pmd = (DynamicPaintModifierData *)md; /* Add relation from canvases to all brush objects. */ if (pmd->canvas != NULL) { - Base *base = scene->base.first; - for (; base; base = base->next) { - DynamicPaintModifierData *pmd2 = - (DynamicPaintModifierData *)modifiers_findByType(base->object, eModifierType_DynamicPaint); - if (pmd2 && pmd2->brush && ob != base->object) { - DEG_add_object_relation(node, base->object, DEG_OB_COMP_TRANSFORM, "Dynamic Paint Brush"); + for (DynamicPaintSurface *surface = pmd->canvas->surfaces.first; surface; surface = surface->next) { + if (surface->effect & MOD_DPAINT_EFFECT_DO_DRIP) { + DEG_add_forcefield_relations(node, scene, ob, surface->effector_weights, true, 0, "Dynamic Paint Field"); } + + /* Actual code uses custom loop over group/scene without layer checks in dynamicPaint_doStep */ + DEG_add_collision_relations(node, scene, ob, surface->brush_group, -1, eModifierType_DynamicPaint, is_brush_cb, false, "Dynamic Paint Brush"); } } } diff --git a/source/blender/modifiers/intern/MOD_meshsequencecache.c b/source/blender/modifiers/intern/MOD_meshsequencecache.c new file mode 100644 index 00000000000..355ac9563dd --- /dev/null +++ b/source/blender/modifiers/intern/MOD_meshsequencecache.c @@ -0,0 +1,197 @@ +/* + * ***** 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. + * + * Contributor(s): Campbell Barton + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/modifiers/intern/MOD_meshsequencecache.c + * \ingroup modifiers + */ + +#include "DNA_cachefile_types.h" +#include "DNA_modifier_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "BKE_cachefile.h" +#include "BKE_DerivedMesh.h" +#include "BKE_global.h" +#include "BKE_library.h" +#include "BKE_library_query.h" +#include "BKE_scene.h" + +#include "depsgraph_private.h" +#include "DEG_depsgraph_build.h" + +#include "MOD_modifiertypes.h" + +#ifdef WITH_ALEMBIC +# include "ABC_alembic.h" +#endif + +static void initData(ModifierData *md) +{ + MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *)md; + + mcmd->cache_file = NULL; + mcmd->object_path[0] = '\0'; + mcmd->read_flag = MOD_MESHSEQ_READ_ALL; +} + +static void copyData(ModifierData *md, ModifierData *target) +{ +#if 0 + MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *)md; +#endif + MeshSeqCacheModifierData *tmcmd = (MeshSeqCacheModifierData *)target; + + modifier_copyData_generic(md, target); + + if (tmcmd->cache_file) { + id_us_plus(&tmcmd->cache_file->id); + } +} + +static void freeData(ModifierData *md) +{ + MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *) md; + + if (mcmd->cache_file) { + id_us_min(&mcmd->cache_file->id); + } +} + +static bool isDisabled(ModifierData *md, int UNUSED(useRenderParams)) +{ + MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *) md; + + /* leave it up to the modifier to check the file is valid on calculation */ + return (mcmd->cache_file == NULL) || (mcmd->object_path[0] == '\0'); +} + +static DerivedMesh *applyModifier(ModifierData *md, Object *ob, + DerivedMesh *dm, + ModifierApplyFlag flag) +{ +#ifdef WITH_ALEMBIC + MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *) md; + + Scene *scene = md->scene; + const float frame = BKE_scene_frame_get(scene); + const float time = BKE_cachefile_time_offset(mcmd->cache_file, frame, FPS); + + const char *err_str = NULL; + + CacheFile *cache_file = mcmd->cache_file; + + BKE_cachefile_ensure_handle(G.main, cache_file); + + DerivedMesh *result = ABC_read_mesh(cache_file->handle, + ob, + dm, + mcmd->object_path, + time, + &err_str, + mcmd->read_flag); + + if (err_str) { + modifier_setError(md, "%s", err_str); + } + + return result ? result : dm; + UNUSED_VARS(flag); +#else + return dm; + UNUSED_VARS(md, ob, flag); +#endif +} + +static bool dependsOnTime(ModifierData *md) +{ + UNUSED_VARS(md); + return true; +} + +static void foreachIDLink(ModifierData *md, Object *ob, + IDWalkFunc walk, void *userData) +{ + MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *) md; + + walk(userData, ob, (ID **)&mcmd->cache_file, IDWALK_USER); +} + + +static void updateDepgraph(ModifierData *md, DagForest *forest, + struct Main *bmain, + struct Scene *scene, + Object *ob, DagNode *obNode) +{ + MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *) md; + + if (mcmd->cache_file != NULL) { + DagNode *curNode = dag_get_node(forest, mcmd->cache_file); + + dag_add_relation(forest, curNode, obNode, + DAG_RL_DATA_DATA | DAG_RL_OB_DATA, "Cache File Modifier"); + } + + UNUSED_VARS(bmain, scene, ob); +} + +static void updateDepsgraph(ModifierData *md, + struct Main *bmain, + struct Scene *scene, + Object *ob, + struct DepsNodeHandle *node) +{ + MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *) md; + + if (mcmd->cache_file != NULL) { + DEG_add_object_cache_relation(node, mcmd->cache_file, DEG_OB_COMP_CACHE, "Mesh Cache File"); + } + + UNUSED_VARS(bmain, scene, ob); +} + +ModifierTypeInfo modifierType_MeshSequenceCache = { + /* name */ "Mesh Sequence Cache", + /* structName */ "MeshSeqCacheModifierData", + /* structSize */ sizeof(MeshSeqCacheModifierData), + /* type */ eModifierTypeType_Constructive, + /* flags */ eModifierTypeFlag_AcceptsMesh | + eModifierTypeFlag_AcceptsCVs, + /* copyData */ copyData, + /* deformVerts */ NULL, + /* deformMatrices */ NULL, + /* deformVertsEM */ NULL, + /* deformMatricesEM */ NULL, + /* applyModifier */ applyModifier, + /* applyModifierEM */ NULL, + /* initData */ initData, + /* requiredDataMask */ NULL, + /* freeData */ freeData, + /* isDisabled */ isDisabled, + /* updateDepgraph */ updateDepgraph, + /* updateDepsgraph */ updateDepsgraph, + /* dependsOnTime */ dependsOnTime, + /* dependsOnNormals */ NULL, + /* foreachObjectLink */ NULL, + /* foreachIDLink */ foreachIDLink, + /* foreachTexLink */ NULL, +}; diff --git a/source/blender/modifiers/intern/MOD_smoke.c b/source/blender/modifiers/intern/MOD_smoke.c index 237d4cc6718..f04d7432a8f 100644 --- a/source/blender/modifiers/intern/MOD_smoke.c +++ b/source/blender/modifiers/intern/MOD_smoke.c @@ -117,219 +117,48 @@ static bool dependsOnTime(ModifierData *UNUSED(md)) return true; } -static void update_depsgraph_flow_coll_object(DagForest *forest, - DagNode *obNode, - Object *object2) +static bool is_flow_cb(Object *UNUSED(ob), ModifierData *md) { - SmokeModifierData *smd; - if ((object2->id.tag & LIB_TAG_DOIT) == 0) { - return; - } - object2->id.tag &= ~LIB_TAG_DOIT; - smd = (SmokeModifierData *)modifiers_findByType(object2, eModifierType_Smoke); - if (smd && (((smd->type & MOD_SMOKE_TYPE_FLOW) && smd->flow) || - ((smd->type & MOD_SMOKE_TYPE_COLL) && smd->coll))) - { - DagNode *curNode = dag_get_node(forest, object2); - dag_add_relation(forest, curNode, obNode, DAG_RL_DATA_DATA | DAG_RL_OB_DATA, "Smoke Flow/Coll"); - } - if ((object2->transflag & OB_DUPLIGROUP) && object2->dup_group) { - GroupObject *go; - for (go = object2->dup_group->gobject.first; - go != NULL; - go = go->next) - { - if (go->ob == NULL) { - continue; - } - update_depsgraph_flow_coll_object(forest, obNode, go->ob); - } - } + SmokeModifierData *smd = (SmokeModifierData *) md; + return (smd->type & MOD_SMOKE_TYPE_FLOW) && smd->flow; } -static void update_depsgraph_field_source_object(DagForest *forest, - DagNode *obNode, - Object *object, - Object *object2) +static bool is_coll_cb(Object *UNUSED(ob), ModifierData *md) { - if ((object2->id.tag & LIB_TAG_DOIT) == 0) { - return; - } - object2->id.tag &= ~LIB_TAG_DOIT; - if (object2->pd && object2->pd->forcefield == PFIELD_SMOKEFLOW && object2->pd->f_source == object) { - DagNode *node2 = dag_get_node(forest, object2); - dag_add_relation(forest, obNode, node2, DAG_RL_DATA_DATA | DAG_RL_OB_DATA, "Field Source Object"); - } - if ((object2->transflag & OB_DUPLIGROUP) && object2->dup_group) { - GroupObject *go; - for (go = object2->dup_group->gobject.first; - go != NULL; - go = go->next) - { - if (go->ob == NULL) { - continue; - } - update_depsgraph_field_source_object(forest, obNode, object, go->ob); - } - } + SmokeModifierData *smd = (SmokeModifierData *) md; + return (smd->type & MOD_SMOKE_TYPE_COLL) && smd->coll; } static void updateDepgraph(ModifierData *md, DagForest *forest, - struct Main *bmain, + struct Main *UNUSED(bmain), struct Scene *scene, struct Object *ob, DagNode *obNode) { SmokeModifierData *smd = (SmokeModifierData *) md; - Base *base; if (smd && (smd->type & MOD_SMOKE_TYPE_DOMAIN) && smd->domain) { - if (smd->domain->fluid_group || smd->domain->coll_group) { - GroupObject *go = NULL; - - if (smd->domain->fluid_group) - for (go = smd->domain->fluid_group->gobject.first; go; go = go->next) { - if (go->ob) { - SmokeModifierData *smd2 = (SmokeModifierData *)modifiers_findByType(go->ob, eModifierType_Smoke); - - /* check for initialized smoke object */ - if (smd2 && (smd2->type & MOD_SMOKE_TYPE_FLOW) && smd2->flow) { - DagNode *curNode = dag_get_node(forest, go->ob); - dag_add_relation(forest, curNode, obNode, DAG_RL_DATA_DATA | DAG_RL_OB_DATA, "Smoke Flow"); - } - } - } - - if (smd->domain->coll_group) - for (go = smd->domain->coll_group->gobject.first; go; go = go->next) { - if (go->ob) { - SmokeModifierData *smd2 = (SmokeModifierData *)modifiers_findByType(go->ob, eModifierType_Smoke); - - /* check for initialized smoke object */ - if (smd2 && (smd2->type & MOD_SMOKE_TYPE_COLL) && smd2->coll) { - DagNode *curNode = dag_get_node(forest, go->ob); - dag_add_relation(forest, curNode, obNode, DAG_RL_DATA_DATA | DAG_RL_OB_DATA, "Smoke Coll"); - } - } - } - } - else { - BKE_main_id_tag_listbase(&bmain->object, LIB_TAG_DOIT, true); - base = scene->base.first; - for (; base; base = base->next) { - update_depsgraph_flow_coll_object(forest, obNode, base->object); - } - } - /* add relation to all "smoke flow" force fields */ - base = scene->base.first; - BKE_main_id_tag_listbase(&bmain->object, LIB_TAG_DOIT, true); - for (; base; base = base->next) { - update_depsgraph_field_source_object(forest, obNode, ob, base->object); - } - } -} + /* Actual code uses get_collisionobjects */ + dag_add_collision_relations(forest, scene, ob, obNode, smd->domain->fluid_group, ob->lay|scene->lay, eModifierType_Smoke, is_flow_cb, true, "Smoke Flow"); + dag_add_collision_relations(forest, scene, ob, obNode, smd->domain->coll_group, ob->lay|scene->lay, eModifierType_Smoke, is_coll_cb, true, "Smoke Coll"); -static void update_depsgraph_flow_coll_object_new(struct DepsNodeHandle *node, - Object *object2) -{ - SmokeModifierData *smd; - if ((object2->id.tag & LIB_TAG_DOIT) == 0) { - return; - } - object2->id.tag &= ~LIB_TAG_DOIT; - smd = (SmokeModifierData *)modifiers_findByType(object2, eModifierType_Smoke); - if (smd && (((smd->type & MOD_SMOKE_TYPE_FLOW) && smd->flow) || - ((smd->type & MOD_SMOKE_TYPE_COLL) && smd->coll))) - { - DEG_add_object_relation(node, object2, DEG_OB_COMP_TRANSFORM, "Smoke Flow/Coll"); - DEG_add_object_relation(node, object2, DEG_OB_COMP_GEOMETRY, "Smoke Flow/Coll"); - } - if ((object2->transflag & OB_DUPLIGROUP) && object2->dup_group) { - GroupObject *go; - for (go = object2->dup_group->gobject.first; - go != NULL; - go = go->next) - { - if (go->ob == NULL) { - continue; - } - update_depsgraph_flow_coll_object_new(node, go->ob); - } - } -} - -static void update_depsgraph_field_source_object_new(struct DepsNodeHandle *node, - Object *object, - Object *object2) -{ - if ((object2->id.tag & LIB_TAG_DOIT) == 0) { - return; - } - object2->id.tag &= ~LIB_TAG_DOIT; - if (object2->pd && object2->pd->forcefield == PFIELD_SMOKEFLOW && object2->pd->f_source == object) { - DEG_add_object_relation(node, object2, DEG_OB_COMP_TRANSFORM, "Field Source Object"); - DEG_add_object_relation(node, object2, DEG_OB_COMP_GEOMETRY, "Field Source Object"); - } - if ((object2->transflag & OB_DUPLIGROUP) && object2->dup_group) { - GroupObject *go; - for (go = object2->dup_group->gobject.first; - go != NULL; - go = go->next) - { - if (go->ob == NULL) { - continue; - } - update_depsgraph_field_source_object_new(node, object, go->ob); - } + dag_add_forcefield_relations(forest, scene, ob, obNode, smd->domain->effector_weights, true, PFIELD_SMOKEFLOW, "Smoke Force Field"); } } static void updateDepsgraph(ModifierData *md, - struct Main *bmain, + struct Main *UNUSED(bmain), struct Scene *scene, Object *ob, struct DepsNodeHandle *node) { SmokeModifierData *smd = (SmokeModifierData *)md; - Base *base; + if (smd && (smd->type & MOD_SMOKE_TYPE_DOMAIN) && smd->domain) { - if (smd->domain->fluid_group || smd->domain->coll_group) { - GroupObject *go = NULL; - if (smd->domain->fluid_group != NULL) { - for (go = smd->domain->fluid_group->gobject.first; go; go = go->next) { - if (go->ob != NULL) { - SmokeModifierData *smd2 = (SmokeModifierData *)modifiers_findByType(go->ob, eModifierType_Smoke); - /* Check for initialized smoke object. */ - if (smd2 && (smd2->type & MOD_SMOKE_TYPE_FLOW) && smd2->flow) { - DEG_add_object_relation(node, go->ob, DEG_OB_COMP_TRANSFORM, "Smoke Flow"); - } - } - } - } - if (smd->domain->coll_group != NULL) { - for (go = smd->domain->coll_group->gobject.first; go; go = go->next) { - if (go->ob != NULL) { - SmokeModifierData *smd2 = (SmokeModifierData *)modifiers_findByType(go->ob, eModifierType_Smoke); - /* Check for initialized smoke object. */ - if (smd2 && (smd2->type & MOD_SMOKE_TYPE_COLL) && smd2->coll) { - DEG_add_object_relation(node, go->ob, DEG_OB_COMP_TRANSFORM, "Smoke Coll"); - } - } - } - } - } - else { - BKE_main_id_tag_listbase(&bmain->object, LIB_TAG_DOIT, true); - base = scene->base.first; - for (; base; base = base->next) { - update_depsgraph_flow_coll_object_new(node, base->object); - } - } - /* add relation to all "smoke flow" force fields */ - base = scene->base.first; - BKE_main_id_tag_listbase(&bmain->object, LIB_TAG_DOIT, true); - for (; base; base = base->next) { - update_depsgraph_field_source_object_new(node, ob, base->object); - } + /* Actual code uses get_collisionobjects */ + DEG_add_collision_relations(node, scene, ob, smd->domain->fluid_group, ob->lay|scene->lay, eModifierType_Smoke, is_flow_cb, true, "Smoke Flow"); + DEG_add_collision_relations(node, scene, ob, smd->domain->coll_group, ob->lay|scene->lay, eModifierType_Smoke, is_coll_cb, true, "Smoke Coll"); + + DEG_add_forcefield_relations(node, scene, ob, smd->domain->effector_weights, true, PFIELD_SMOKEFLOW, "Smoke Force Field"); } } diff --git a/source/blender/modifiers/intern/MOD_softbody.c b/source/blender/modifiers/intern/MOD_softbody.c index 98a1412d0c6..17adc7f1520 100644 --- a/source/blender/modifiers/intern/MOD_softbody.c +++ b/source/blender/modifiers/intern/MOD_softbody.c @@ -35,6 +35,7 @@ #include <stdio.h> #include "DNA_scene_types.h" +#include "DNA_object_force.h" #include "BLI_utildefines.h" @@ -42,6 +43,9 @@ #include "BKE_particle.h" #include "BKE_softbody.h" +#include "depsgraph_private.h" +#include "DEG_depsgraph_build.h" + #include "MOD_modifiertypes.h" static void deformVerts(ModifierData *md, Object *ob, @@ -58,6 +62,31 @@ static bool dependsOnTime(ModifierData *UNUSED(md)) return true; } +static void updateDepgraph(ModifierData *UNUSED(md), DagForest *forest, + struct Main *UNUSED(bmain), + Scene *scene, Object *ob, DagNode *obNode) +{ + if (ob->soft) { + /* Actual code uses ccd_build_deflector_hash */ + dag_add_collision_relations(forest, scene, ob, obNode, ob->soft->collision_group, ob->lay, eModifierType_Collision, NULL, false, "Softbody Collision"); + + dag_add_forcefield_relations(forest, scene, ob, obNode, ob->soft->effector_weights, true, 0, "Softbody Field"); + } +} + +static void updateDepsgraph(ModifierData *UNUSED(md), + struct Main *UNUSED(bmain), + struct Scene *scene, + Object *ob, + struct DepsNodeHandle *node) +{ + if (ob->soft) { + /* Actual code uses ccd_build_deflector_hash */ + DEG_add_collision_relations(node, scene, ob, ob->soft->collision_group, ob->lay, eModifierType_Collision, NULL, false, "Softbody Collision"); + + DEG_add_forcefield_relations(node, scene, ob, ob->soft->effector_weights, true, 0, "Softbody Field"); + } +} ModifierTypeInfo modifierType_Softbody = { /* name */ "Softbody", @@ -80,8 +109,8 @@ ModifierTypeInfo modifierType_Softbody = { /* requiredDataMask */ NULL, /* freeData */ NULL, /* isDisabled */ NULL, - /* updateDepgraph */ NULL, - /* updateDepsgraph */ NULL, + /* updateDepgraph */ updateDepgraph, + /* updateDepsgraph */ updateDepsgraph, /* dependsOnTime */ dependsOnTime, /* dependsOnNormals */ NULL, /* foreachObjectLink */ NULL, diff --git a/source/blender/modifiers/intern/MOD_util.c b/source/blender/modifiers/intern/MOD_util.c index f9291fb077f..93414562ccf 100644 --- a/source/blender/modifiers/intern/MOD_util.c +++ b/source/blender/modifiers/intern/MOD_util.c @@ -286,5 +286,6 @@ void modifier_type_init(ModifierTypeInfo *types[]) INIT_TYPE(DataTransfer); INIT_TYPE(NormalEdit); INIT_TYPE(CorrectiveSmooth); + INIT_TYPE(MeshSequenceCache); #undef INIT_TYPE } diff --git a/source/blender/nodes/shader/nodes/node_shader_material.c b/source/blender/nodes/shader/nodes/node_shader_material.c index 8b21b1ff33b..6850cdbf6ea 100644 --- a/source/blender/nodes/shader/nodes/node_shader_material.c +++ b/source/blender/nodes/shader/nodes/node_shader_material.c @@ -223,12 +223,27 @@ static void node_shader_init_material(bNodeTree *UNUSED(ntree), bNode *node) /* XXX this is also done as a local static function in gpu_codegen.c, * but we need this to hack around the crappy material node. */ -static GPUNodeLink *gpu_get_input_link(GPUNodeStack *in) +static GPUNodeLink *gpu_get_input_link(GPUMaterial *mat, GPUNodeStack *in) { - if (in->link) + if (in->link) { return in->link; - else - return GPU_uniform(in->vec); + } + else { + GPUNodeLink *result = NULL; + + /* note GPU_uniform() is only intended to be used as a parameter to + * GPU_link(), returning it directly results in leaks or double frees */ + if (in->type == GPU_FLOAT) + GPU_link(mat, "set_value", GPU_uniform(in->vec), &result); + else if (in->type == GPU_VEC3) + GPU_link(mat, "set_rgb", GPU_uniform(in->vec), &result); + else if (in->type == GPU_VEC4) + GPU_link(mat, "set_rgba", GPU_uniform(in->vec), &result); + else + BLI_assert(0); + + return result; + } } static int gpu_shader_material(GPUMaterial *mat, bNode *node, bNodeExecData *UNUSED(execdata), GPUNodeStack *in, GPUNodeStack *out) @@ -251,18 +266,18 @@ static int gpu_shader_material(GPUMaterial *mat, bNode *node, bNodeExecData *UNU /* write values */ if (hasinput[MAT_IN_COLOR]) - shi.rgb = gpu_get_input_link(&in[MAT_IN_COLOR]); + shi.rgb = gpu_get_input_link(mat, &in[MAT_IN_COLOR]); if (hasinput[MAT_IN_SPEC]) - shi.specrgb = gpu_get_input_link(&in[MAT_IN_SPEC]); + shi.specrgb = gpu_get_input_link(mat, &in[MAT_IN_SPEC]); if (hasinput[MAT_IN_REFL]) - shi.refl = gpu_get_input_link(&in[MAT_IN_REFL]); + shi.refl = gpu_get_input_link(mat, &in[MAT_IN_REFL]); /* retrieve normal */ if (hasinput[MAT_IN_NORMAL]) { GPUNodeLink *tmp; - shi.vn = gpu_get_input_link(&in[MAT_IN_NORMAL]); + shi.vn = gpu_get_input_link(mat, &in[MAT_IN_NORMAL]); if (GPU_material_use_world_space_shading(mat)) { GPU_link(mat, "vec_math_negate", shi.vn, &shi.vn); GPU_link(mat, "direction_transform_m4v3", shi.vn, GPU_builtin(GPU_VIEW_MATRIX), &shi.vn); @@ -276,15 +291,15 @@ static int gpu_shader_material(GPUMaterial *mat, bNode *node, bNodeExecData *UNU if (node->type == SH_NODE_MATERIAL_EXT) { if (hasinput[MAT_IN_MIR]) - shi.mir = gpu_get_input_link(&in[MAT_IN_MIR]); + shi.mir = gpu_get_input_link(mat, &in[MAT_IN_MIR]); if (hasinput[MAT_IN_AMB]) - shi.amb = gpu_get_input_link(&in[MAT_IN_AMB]); + shi.amb = gpu_get_input_link(mat, &in[MAT_IN_AMB]); if (hasinput[MAT_IN_EMIT]) - shi.emit = gpu_get_input_link(&in[MAT_IN_EMIT]); + shi.emit = gpu_get_input_link(mat, &in[MAT_IN_EMIT]); if (hasinput[MAT_IN_SPECTRA]) - shi.spectra = gpu_get_input_link(&in[MAT_IN_SPECTRA]); + shi.spectra = gpu_get_input_link(mat, &in[MAT_IN_SPECTRA]); if (hasinput[MAT_IN_ALPHA]) - shi.alpha = gpu_get_input_link(&in[MAT_IN_ALPHA]); + shi.alpha = gpu_get_input_link(mat, &in[MAT_IN_ALPHA]); } GPU_shaderesult_set(&shi, &shr); /* clears shr */ diff --git a/source/blender/python/intern/CMakeLists.txt b/source/blender/python/intern/CMakeLists.txt index 2715d2c5992..038c1e7eb10 100644 --- a/source/blender/python/intern/CMakeLists.txt +++ b/source/blender/python/intern/CMakeLists.txt @@ -49,6 +49,7 @@ set(SRC gpu_offscreen.c bpy.c bpy_app.c + bpy_app_alembic.c bpy_app_build_options.c bpy_app_ffmpeg.c bpy_app_handlers.c @@ -82,6 +83,7 @@ set(SRC gpu.h bpy.h bpy_app.h + bpy_app_alembic.h bpy_app_build_options.h bpy_app_ffmpeg.h bpy_app_handlers.h @@ -264,6 +266,10 @@ if(WITH_OPENCOLLADA) add_definitions(-DWITH_COLLADA) endif() +if(WITH_ALEMBIC) + add_definitions(-DWITH_ALEMBIC) +endif() + if(WITH_OPENCOLORIO) add_definitions(-DWITH_OCIO) endif() @@ -275,6 +281,13 @@ if(WITH_OPENVDB) ) endif() +if(WITH_ALEMBIC) + add_definitions(-DWITH_ALEMBIC) + list(APPEND INC + ../../alembic + ) +endif() + if(WITH_OPENIMAGEIO) add_definitions(-DWITH_OPENIMAGEIO) list(APPEND INC diff --git a/source/blender/python/intern/bpy_app.c b/source/blender/python/intern/bpy_app.c index 727d980b182..78cb537cccd 100644 --- a/source/blender/python/intern/bpy_app.c +++ b/source/blender/python/intern/bpy_app.c @@ -33,6 +33,7 @@ #include "bpy_app.h" +#include "bpy_app_alembic.h" #include "bpy_app_ffmpeg.h" #include "bpy_app_ocio.h" #include "bpy_app_oiio.h" @@ -104,6 +105,7 @@ static PyStructSequence_Field app_info_fields[] = { {(char *)"build_system", (char *)"Build system used"}, /* submodules */ + {(char *)"alembic", (char *)"Alembic library information backend"}, {(char *)"ffmpeg", (char *)"FFmpeg library information backend"}, {(char *)"ocio", (char *)"OpenColorIO library information backend"}, {(char *)"oiio", (char *)"OpenImageIO library information backend"}, @@ -182,6 +184,7 @@ static PyObject *make_app_info(void) SetBytesItem("Unknown"); #endif + SetObjItem(BPY_app_alembic_struct()); SetObjItem(BPY_app_ffmpeg_struct()); SetObjItem(BPY_app_ocio_struct()); SetObjItem(BPY_app_oiio_struct()); diff --git a/source/blender/python/intern/bpy_app_alembic.c b/source/blender/python/intern/bpy_app_alembic.c new file mode 100644 index 00000000000..90e6a02b418 --- /dev/null +++ b/source/blender/python/intern/bpy_app_alembic.c @@ -0,0 +1,113 @@ +/* + * ***** 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) 2016 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Kevin Dietrich + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/python/intern/bpy_app_alembic.c + * \ingroup pythonintern + */ + +#include <Python.h> +#include "BLI_utildefines.h" + +#include "bpy_app_alembic.h" + +#ifdef WITH_ALEMBIC +# include "ABC_alembic.h" +#endif + +static PyTypeObject BlenderAppABCType; + +static PyStructSequence_Field app_alembic_info_fields[] = { + {(char *)"supported", (char *)"Boolean, True when Blender is built with Alembic support"}, + {(char *)"version", (char *)"The Alembic version as a tuple of 3 numbers"}, + {(char *)"version_string", (char *)"The Alembic version formatted as a string"}, + {NULL} +}; + +static PyStructSequence_Desc app_alembic_info_desc = { + (char *)"bpy.app.alembic", /* name */ + (char *)"This module contains information about Alembic blender is linked against", /* doc */ + app_alembic_info_fields, /* fields */ + ARRAY_SIZE(app_alembic_info_fields) - 1 +}; + +static PyObject *make_alembic_info(void) +{ + PyObject *alembic_info = PyStructSequence_New(&BlenderAppABCType); + + if (alembic_info == NULL) { + return NULL; + } + + int pos = 0; + +#ifndef WITH_ALEMBIC +# define SetStrItem(str) \ + PyStructSequence_SET_ITEM(alembic_info, pos++, PyUnicode_FromString(str)) +#endif + +#define SetObjItem(obj) \ + PyStructSequence_SET_ITEM(alembic_info, pos++, obj) + +#ifdef WITH_ALEMBIC + const int curversion = ABC_get_version(); + const int major = curversion / 10000; + const int minor = (curversion / 100) - (major * 100); + const int patch = curversion - ((curversion / 100 ) * 100); + + SetObjItem(PyBool_FromLong(1)); + SetObjItem(Py_BuildValue("(iii)", major, minor, patch)); + SetObjItem(PyUnicode_FromFormat("%2d, %2d, %2d", major, minor, patch)); +#else + SetObjItem(PyBool_FromLong(0)); + SetObjItem(Py_BuildValue("(iii)", 0, 0, 0)); + SetStrItem("Unknown"); +#endif + + if (PyErr_Occurred()) { + Py_CLEAR(alembic_info); + return NULL; + } + +#undef SetStrItem +#undef SetObjItem + + return alembic_info; +} + +PyObject *BPY_app_alembic_struct(void) +{ + PyStructSequence_InitType(&BlenderAppABCType, &app_alembic_info_desc); + + PyObject *ret = make_alembic_info(); + + /* prevent user from creating new instances */ + BlenderAppABCType.tp_init = NULL; + BlenderAppABCType.tp_new = NULL; + BlenderAppABCType.tp_hash = (hashfunc)_Py_HashPointer; /* without this we can't do set(sys.modules) [#29635] */ + + return ret; +} diff --git a/source/blender/python/intern/bpy_app_alembic.h b/source/blender/python/intern/bpy_app_alembic.h new file mode 100644 index 00000000000..8cc647a77df --- /dev/null +++ b/source/blender/python/intern/bpy_app_alembic.h @@ -0,0 +1,38 @@ +/* + * ***** 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) 2016 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Kevin Dietrich + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/python/intern/bpy_app_alembic.h + * \ingroup pythonintern + */ + +#ifndef __BPY_APP_ALEMBIC_H__ +#define __BPY_APP_ALEMBIC_H__ + +PyObject *BPY_app_alembic_struct(void); + +#endif /* __BPY_APP_ALEMBIC_H__ */ + diff --git a/source/blender/python/intern/bpy_app_build_options.c b/source/blender/python/intern/bpy_app_build_options.c index 4c186aab100..a6b98567a9a 100644 --- a/source/blender/python/intern/bpy_app_build_options.c +++ b/source/blender/python/intern/bpy_app_build_options.c @@ -69,6 +69,7 @@ static PyStructSequence_Field app_builtopts_info_fields[] = { {(char *)"player", NULL}, {(char *)"openmp", NULL}, {(char *)"openvdb", NULL}, + {(char *)"alembic", NULL}, {NULL} }; @@ -303,6 +304,12 @@ static PyObject *make_builtopts_info(void) SetObjIncref(Py_False); #endif +#ifdef WITH_ALEMBIC + SetObjIncref(Py_True); +#else + SetObjIncref(Py_False); +#endif + #undef SetObjIncref return builtopts_info; diff --git a/source/blender/python/intern/bpy_library_write.c b/source/blender/python/intern/bpy_library_write.c index f582cebb260..bf91253141a 100644 --- a/source/blender/python/intern/bpy_library_write.c +++ b/source/blender/python/intern/bpy_library_write.c @@ -50,7 +50,7 @@ PyDoc_STRVAR(bpy_lib_write_doc, -".. method:: write(filepath, datablocks, relative_remap=False, fake_user=False)\n" +".. method:: write(filepath, datablocks, relative_remap=False, fake_user=False, compress=False)\n" "\n" " Write data-blocks into a blend file.\n" "\n" diff --git a/source/blender/render/intern/raytrace/rayobject.cpp b/source/blender/render/intern/raytrace/rayobject.cpp index 2104315dc00..c16ef5500df 100644 --- a/source/blender/render/intern/raytrace/rayobject.cpp +++ b/source/blender/render/intern/raytrace/rayobject.cpp @@ -138,16 +138,81 @@ MALWAYS_INLINE int vlr_check_bake(Isect *is, ObjectInstanceRen *obi, VlakRen *UN /* Ray Triangle/Quad Intersection */ -MALWAYS_INLINE int isec_tri_quad(float start[3], const struct IsectRayPrecalc *isect_precalc, RayFace *face, float r_uv[2], float *lambda) +static bool isect_ray_tri_watertight_no_sign_check_v3( + const float ray_origin[3], const struct IsectRayPrecalc *isect_precalc, + const float v0[3], const float v1[3], const float v2[3], + float *r_lambda, float r_uv[2]) +{ + const int kx = isect_precalc->kx; + const int ky = isect_precalc->ky; + const int kz = isect_precalc->kz; + const float sx = isect_precalc->sx; + const float sy = isect_precalc->sy; + const float sz = isect_precalc->sz; + + /* Calculate vertices relative to ray origin. */ + const float a[3] = {v0[0] - ray_origin[0], v0[1] - ray_origin[1], v0[2] - ray_origin[2]}; + const float b[3] = {v1[0] - ray_origin[0], v1[1] - ray_origin[1], v1[2] - ray_origin[2]}; + const float c[3] = {v2[0] - ray_origin[0], v2[1] - ray_origin[1], v2[2] - ray_origin[2]}; + + const float a_kx = a[kx], a_ky = a[ky], a_kz = a[kz]; + const float b_kx = b[kx], b_ky = b[ky], b_kz = b[kz]; + const float c_kx = c[kx], c_ky = c[ky], c_kz = c[kz]; + + /* Perform shear and scale of vertices. */ + const float ax = a_kx - sx * a_kz; + const float ay = a_ky - sy * a_kz; + const float bx = b_kx - sx * b_kz; + const float by = b_ky - sy * b_kz; + const float cx = c_kx - sx * c_kz; + const float cy = c_ky - sy * c_kz; + + /* Calculate scaled barycentric coordinates. */ + const float u = cx * by - cy * bx; + const float v = ax * cy - ay * cx; + const float w = bx * ay - by * ax; + float det; + + if ((u < 0.0f || v < 0.0f || w < 0.0f) && + (u > 0.0f || v > 0.0f || w > 0.0f)) + { + return false; + } + + /* Calculate determinant. */ + det = u + v + w; + if (UNLIKELY(det == 0.0f)) { + return false; + } + else { + /* Calculate scaled z-coordinates of vertices and use them to calculate + * the hit distance. + */ + const float t = (u * a_kz + v * b_kz + w * c_kz) * sz; + /* Normalize u, v and t. */ + const float inv_det = 1.0f / det; + if (r_uv) { + r_uv[0] = u * inv_det; + r_uv[1] = v * inv_det; + } + *r_lambda = t * inv_det; + return true; + } +} + +MALWAYS_INLINE int isec_tri_quad(const float start[3], + const struct IsectRayPrecalc *isect_precalc, + const RayFace *face, + float r_uv[2], float *r_lambda) { float uv[2], l; if (isect_ray_tri_watertight_v3(start, isect_precalc, face->v1, face->v2, face->v3, &l, uv)) { /* check if intersection is within ray length */ - if (l > -RE_RAYTRACE_EPSILON && l < *lambda) { + if (l > -RE_RAYTRACE_EPSILON && l < *r_lambda) { r_uv[0] = -uv[0]; r_uv[1] = -uv[1]; - *lambda = l; + *r_lambda = l; return 1; } } @@ -156,10 +221,10 @@ MALWAYS_INLINE int isec_tri_quad(float start[3], const struct IsectRayPrecalc *i if (RE_rayface_isQuad(face)) { if (isect_ray_tri_watertight_v3(start, isect_precalc, face->v1, face->v3, face->v4, &l, uv)) { /* check if intersection is within ray length */ - if (l > -RE_RAYTRACE_EPSILON && l < *lambda) { + if (l > -RE_RAYTRACE_EPSILON && l < *r_lambda) { r_uv[0] = -uv[0]; r_uv[1] = -uv[1]; - *lambda = l; + *r_lambda = l; return 2; } } @@ -170,24 +235,25 @@ MALWAYS_INLINE int isec_tri_quad(float start[3], const struct IsectRayPrecalc *i /* Simpler yes/no Ray Triangle/Quad Intersection */ -MALWAYS_INLINE int isec_tri_quad_neighbour(float start[3], float dir[3], RayFace *face) +MALWAYS_INLINE int isec_tri_quad_neighbour(const float start[3], + const float dir[3], + const RayFace *face) { float r[3]; struct IsectRayPrecalc isect_precalc; float uv[2], l; - negate_v3_v3(r, dir); /* note, different than above function */ isect_ray_tri_watertight_v3_precalc(&isect_precalc, r); - if (isect_ray_tri_watertight_v3(start, &isect_precalc, face->v1, face->v2, face->v3, &l, uv)) { + if (isect_ray_tri_watertight_no_sign_check_v3(start, &isect_precalc, face->v1, face->v2, face->v3, &l, uv)) { return 1; } /* intersect second triangle in quad */ if (RE_rayface_isQuad(face)) { - if (isect_ray_tri_watertight_v3(start, &isect_precalc, face->v1, face->v3, face->v4, &l, uv)) { + if (isect_ray_tri_watertight_no_sign_check_v3(start, &isect_precalc, face->v1, face->v3, face->v4, &l, uv)) { return 2; } } diff --git a/source/blender/render/intern/raytrace/rayobject_rtbuild.cpp b/source/blender/render/intern/raytrace/rayobject_rtbuild.cpp index 02a49fc3c8f..724a809077e 100644 --- a/source/blender/render/intern/raytrace/rayobject_rtbuild.cpp +++ b/source/blender/render/intern/raytrace/rayobject_rtbuild.cpp @@ -42,6 +42,10 @@ #include "BLI_math.h" #include "BLI_utildefines.h" +#if __cplusplus >= 201103L +using std::isfinite; +#endif + static bool selected_node(RTBuilder::Object *node) { return node->selected; diff --git a/source/blender/render/intern/source/pipeline.c b/source/blender/render/intern/source/pipeline.c index 604693f486e..9f1ae4a96e0 100644 --- a/source/blender/render/intern/source/pipeline.c +++ b/source/blender/render/intern/source/pipeline.c @@ -2785,6 +2785,7 @@ static void do_render_all_options(Render *re) /* ensure no images are in memory from previous animated sequences */ BKE_image_all_free_anim_ibufs(re->r.cfra); + BKE_sequencer_all_free_anim_ibufs(re->r.cfra); if (RE_engine_render(re, 1)) { /* in this case external render overrides all */ @@ -3058,6 +3059,13 @@ bool RE_is_rendering_allowed(Scene *scene, Object *camera_override, ReportList * } #endif + if (RE_seq_render_active(scene, &scene->r)) { + if (scene->r.mode & R_BORDER) { + BKE_report(reports, RPT_ERROR, "Border rendering is not supported by sequencer"); + return false; + } + } + /* layer flag tests */ if (!render_scene_has_layers_to_render(scene)) { BKE_report(reports, RPT_ERROR, "All render layers are disabled"); diff --git a/source/blender/windowmanager/CMakeLists.txt b/source/blender/windowmanager/CMakeLists.txt index 01188cb7f65..b6245a8c0d1 100644 --- a/source/blender/windowmanager/CMakeLists.txt +++ b/source/blender/windowmanager/CMakeLists.txt @@ -146,6 +146,10 @@ if(WITH_OPENSUBDIV) add_definitions(-DWITH_OPENSUBDIV) endif() +if(WITH_INPUT_NDOF) + add_definitions(-DWITH_INPUT_NDOF) +endif() + if(WIN32) if(WITH_INPUT_IME) add_definitions(-DWITH_INPUT_IME) diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index 493fe82ff80..2b82f1becb3 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -64,7 +64,10 @@ struct wmDrag; struct ImBuf; struct ImageFormatData; struct ARegion; + +#ifdef WITH_INPUT_NDOF struct wmNDOFMotionData; +#endif typedef struct wmJob wmJob; @@ -186,9 +189,10 @@ void WM_event_add_mousemove(struct bContext *C); bool WM_modal_tweak_exit(const struct wmEvent *event, int tweak_event); bool WM_event_is_absolute(const struct wmEvent *event); +#ifdef WITH_INPUT_NDOF /* 3D mouse */ void WM_ndof_deadzone_set(float deadzone); - +#endif /* notifiers */ void WM_event_add_notifier(const struct bContext *C, unsigned int type, void *reference); void WM_main_add_notifier(unsigned int type, void *reference); @@ -441,6 +445,7 @@ enum { WM_JOB_TYPE_SEQ_BUILD_PREVIEW, WM_JOB_TYPE_POINTCACHE, WM_JOB_TYPE_DPAINT_BAKE, + WM_JOB_TYPE_ALEMBIC, /* add as needed, screencast, seq proxy build * if having hard coded values is a problem */ }; @@ -497,11 +502,13 @@ bool write_crash_blend(void); /* Lock the interface for any communication */ void WM_set_locked_interface(struct wmWindowManager *wm, bool lock); +#ifdef WITH_INPUT_NDOF void WM_event_ndof_pan_get(const struct wmNDOFMotionData *ndof, float r_pan[3], const bool use_zoom); void WM_event_ndof_rotate_get(const struct wmNDOFMotionData *ndof, float r_rot[3]); float WM_event_ndof_to_axis_angle(const struct wmNDOFMotionData *ndof, float axis[3]); void WM_event_ndof_to_quat(const struct wmNDOFMotionData *ndof, float q[4]); +#endif /* WITH_INPUT_NDOF */ float WM_event_tablet_data(const struct wmEvent *event, int *pen_flip, float tilt[2]); bool WM_event_is_tablet(const struct wmEvent *event); diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index 3c0e99bddd0..0fe3e8a0fcf 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -485,6 +485,7 @@ typedef enum { /* motion progress, for modal handlers */ P_FINISHED } wmProgress; +#ifdef WITH_INPUT_NDOF typedef struct wmNDOFMotionData { /* awfully similar to GHOST_TEventNDOFMotionData... */ /* Each component normally ranges from -1 to +1, but can exceed that. @@ -496,6 +497,7 @@ typedef struct wmNDOFMotionData { float dt; /* time since previous NDOF Motion event */ wmProgress progress; /* is this the first event, the last, or one of many in between? */ } wmNDOFMotionData; +#endif /* WITH_INPUT_NDOF */ typedef struct wmTimer { struct wmTimer *next, *prev; diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index 01dc77127d2..77d74399769 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -567,6 +567,7 @@ void WM_event_print(const wmEvent *event) BLI_str_utf8_size(event->utf8_buf), event->utf8_buf, event->keymap_idname, (const void *)event); +#ifdef WITH_INPUT_NDOF if (ISNDOF(event->type)) { const wmNDOFMotionData *ndof = event->customdata; if (event->type == NDOF_MOTION) { @@ -577,6 +578,7 @@ void WM_event_print(const wmEvent *event) /* ndof buttons printed already */ } } +#endif /* WITH_INPUT_NDOF */ if (event->tablet_data) { const wmTabletData *wmtab = event->tablet_data; @@ -613,10 +615,12 @@ bool WM_event_is_absolute(const wmEvent *event) return (event->tablet_data != NULL); } +#ifdef WITH_INPUT_NDOF void WM_ndof_deadzone_set(float deadzone) { GHOST_setNDOFDeadZone(deadzone); } +#endif static void wm_add_reports(ReportList *reports) { @@ -2423,9 +2427,11 @@ void wm_event_do_handlers(bContext *C) /* for regions having custom cursors */ wm_paintcursor_test(C, event); } +#ifdef WITH_INPUT_NDOF else if (event->type == NDOF_MOTION) { win->addmousemove = true; } +#endif for (sa = win->screen->areabase.first; sa; sa = sa->next) { /* after restoring a screen from SCREENMAXIMIZED we have to wait @@ -3026,6 +3032,7 @@ static void update_tablet_data(wmWindow *win, wmEvent *event) } } +#ifdef WITH_INPUT_NDOF /* adds customdata to event */ static void attach_ndof_data(wmEvent *event, const GHOST_TEventNDOFMotionData *ghost) { @@ -3052,6 +3059,7 @@ static void attach_ndof_data(wmEvent *event, const GHOST_TEventNDOFMotionData *g event->customdata = data; event->customdatafree = 1; } +#endif /* WITH_INPUT_NDOF */ /* imperfect but probably usable... draw/enable drags to other windows */ static wmWindow *wm_event_cursor_other_windows(wmWindowManager *wm, wmWindow *win, wmEvent *event) @@ -3439,6 +3447,7 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int U break; } +#ifdef WITH_INPUT_NDOF case GHOST_kEventNDOFMotion: { event.type = NDOF_MOTION; @@ -3474,6 +3483,7 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int U break; } +#endif /* WITH_INPUT_NDOF */ case GHOST_kEventUnknown: case GHOST_kNumEventTypes: @@ -3545,6 +3555,7 @@ void WM_set_locked_interface(wmWindowManager *wm, bool lock) } +#ifdef WITH_INPUT_NDOF /* -------------------------------------------------------------------- */ /* NDOF */ @@ -3587,6 +3598,7 @@ void WM_event_ndof_to_quat(const struct wmNDOFMotionData *ndof, float q[4]) angle = WM_event_ndof_to_axis_angle(ndof, axis); axis_angle_to_quat(q, axis, angle); } +#endif /* WITH_INPUT_NDOF */ /* if this is a tablet event, return tablet pressure and set *pen_flip * to 1 if the eraser tool is being used, 0 otherwise */ diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index 3022d865460..73622cda483 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -198,8 +198,11 @@ void WM_init(bContext *C, int argc, const char **argv) BLT_lang_set(NULL); if (!G.background) { + +#ifdef WITH_INPUT_NDOF /* sets 3D mouse deadzone */ WM_ndof_deadzone_set(U.ndof_deadzone); +#endif GPU_init(); diff --git a/source/blender/windowmanager/intern/wm_operator_props.c b/source/blender/windowmanager/intern/wm_operator_props.c index 7ec2aea73e1..18836f34c99 100644 --- a/source/blender/windowmanager/intern/wm_operator_props.c +++ b/source/blender/windowmanager/intern/wm_operator_props.c @@ -97,6 +97,8 @@ void WM_operator_properties_filesel( RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); prop = RNA_def_boolean(ot->srna, "filter_collada", (filter & FILE_TYPE_COLLADA) != 0, "Filter COLLADA files", ""); RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, "filter_alembic", (filter & FILE_TYPE_ALEMBIC) != 0, "Filter Alembic files", ""); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); prop = RNA_def_boolean(ot->srna, "filter_folder", (filter & FILE_TYPE_FOLDER) != 0, "Filter folders", ""); RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); prop = RNA_def_boolean(ot->srna, "filter_blenlib", (filter & FILE_TYPE_BLENDERLIB) != 0, "Filter Blender IDs", ""); diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index 78273615602..0c137221856 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -1369,12 +1369,13 @@ typedef struct wmOpPopUp { /* Only invoked by OK button in popups created with wm_block_dialog_create() */ static void dialog_exec_cb(bContext *C, void *arg1, void *arg2) { - wmWindowManager *wm = CTX_wm_manager(C); - wmWindow *win = CTX_wm_window(C); - wmOpPopUp *data = arg1; uiBlock *block = arg2; + /* Explicitly set UI_RETURN_OK flag, otherwise the menu might be cancelled + * in case WM_operator_call_ex exits/reloads the current file (T49199). */ + UI_popup_menu_retval_set(block, UI_RETURN_OK, true); + WM_operator_call_ex(C, data->op, true); /* let execute handle freeing it */ @@ -1384,8 +1385,18 @@ static void dialog_exec_cb(bContext *C, void *arg1, void *arg2) /* in this case, wm_operator_ui_popup_cancel wont run */ MEM_freeN(data); + /* get context data *after* WM_operator_call_ex which might have closed the current file and changed context */ + wmWindowManager *wm = CTX_wm_manager(C); + wmWindow *win = CTX_wm_window(C); + /* check window before 'block->handle' incase the - * popup execution closed the window and freed the block. see T44688. */ + * popup execution closed the window and freed the block. see T44688. + */ + /* Post 2.78 TODO: Check if this fix and others related to T44688 are still + * needed or can be improved now that requesting context data has been corrected + * (see above). We're close to release so not a good time for experiments. + * -- Julian + */ if (BLI_findindex(&wm->windows, win) != -1) { UI_popup_block_close(C, win, block); } @@ -4348,7 +4359,6 @@ void wm_window_keymap(wmKeyConfig *keyconf) { wmKeyMap *keymap = WM_keymap_find(keyconf, "Window", 0, 0); wmKeyMapItem *kmi; - const char *data_path; /* note, this doesn't replace existing keymap items */ WM_keymap_verify_item(keymap, "WM_OT_window_duplicate", WKEY, KM_PRESS, KM_CTRL | KM_ALT, 0); @@ -4386,7 +4396,9 @@ void wm_window_keymap(wmKeyConfig *keyconf) /* menus that can be accessed anywhere in blender */ WM_keymap_verify_item(keymap, "WM_OT_search_menu", SPACEKEY, KM_PRESS, 0, 0); +#ifdef WITH_INPUT_NDOF WM_keymap_add_menu(keymap, "USERPREF_MT_ndof_settings", NDOF_BUTTON_MENU, KM_PRESS, 0, 0); +#endif /* Space switching */ kmi = WM_keymap_add_item(keymap, "WM_OT_context_set_enum", F2KEY, KM_PRESS, KM_SHIFT, 0); /* new in 2.5x, was DXF export */ @@ -4433,8 +4445,9 @@ void wm_window_keymap(wmKeyConfig *keyconf) RNA_string_set(kmi->ptr, "data_path", "area.type"); RNA_string_set(kmi->ptr, "value", "DOPESHEET_EDITOR"); +#ifdef WITH_INPUT_NDOF /* ndof speed */ - data_path = "user_preferences.inputs.ndof_sensitivity"; + const char *data_path = "user_preferences.inputs.ndof_sensitivity"; kmi = WM_keymap_add_item(keymap, "WM_OT_context_scale_float", NDOF_BUTTON_PLUS, KM_PRESS, 0, 0); RNA_string_set(kmi->ptr, "data_path", data_path); RNA_float_set(kmi->ptr, "value", 1.1f); @@ -4450,9 +4463,7 @@ void wm_window_keymap(wmKeyConfig *keyconf) kmi = WM_keymap_add_item(keymap, "WM_OT_context_scale_float", NDOF_BUTTON_MINUS, KM_PRESS, KM_SHIFT, 0); RNA_string_set(kmi->ptr, "data_path", data_path); RNA_float_set(kmi->ptr, "value", 1.0f / 1.5f); - data_path = NULL; - (void)data_path; - +#endif /* WITH_INPUT_NDOF */ gesture_circle_modal_keymap(keyconf); gesture_border_modal_keymap(keyconf); diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index b68b607f48b..2d43c47679d 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -69,6 +69,8 @@ #include "ED_screen.h" #include "ED_fileselect.h" +#include "UI_interface.h" + #include "PIL_time.h" #include "GPU_draw.h" @@ -1194,18 +1196,30 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr break; } case GHOST_kEventNativeResolutionChange: - // printf("change, pixel size %f\n", GHOST_GetNativePixelSize(win->ghostwin)); - + { + // only update if the actual pixel size changes + float prev_pixelsize = U.pixelsize; U.pixelsize = wm_window_pixelsize(win); - BKE_blender_userdef_refresh(); - wm_window_make_drawable(wm, win); - wm_draw_window_clear(win); + if (U.pixelsize != prev_pixelsize) { + BKE_blender_userdef_refresh(); + + // close all popups since they are positioned with the pixel + // size baked in and it's difficult to correct them + wmWindow *oldWindow = CTX_wm_window(C); + CTX_wm_window_set(C, win); + UI_popup_handlers_remove_all(C, &win->modalhandlers); + CTX_wm_window_set(C, oldWindow); - WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL); - WM_event_add_notifier(C, NC_WINDOW | NA_EDITED, NULL); + wm_window_make_drawable(wm, win); + wm_draw_window_clear(win); + + WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL); + WM_event_add_notifier(C, NC_WINDOW | NA_EDITED, NULL); + } break; + } case GHOST_kEventTrackpad: { GHOST_TEventTrackpadData *pd = data; diff --git a/source/blenderplayer/CMakeLists.txt b/source/blenderplayer/CMakeLists.txt index ca841954b16..58bebc66a3e 100644 --- a/source/blenderplayer/CMakeLists.txt +++ b/source/blenderplayer/CMakeLists.txt @@ -58,7 +58,7 @@ if(WIN32 AND NOT UNIX) blenderplayer ${EXETYPE} bad_level_call_stubs/stubs.c ${CMAKE_SOURCE_DIR}/release/windows/icons/winblender.rc) - + WINDOWS_SIGN_TARGET(blenderplayer) install(TARGETS blenderplayer COMPONENT Blenderplayer DESTINATION ".") @@ -233,6 +233,10 @@ endif() list(APPEND BLENDER_SORTED_LIBS bf_intern_openvdb) endif() + if(WITH_ALEMBIC) + list(APPEND BLENDER_SORTED_LIBS bf_alembic) + endif() + foreach(SORTLIB ${BLENDER_SORTED_LIBS}) set(REMLIB ${SORTLIB}) foreach(SEARCHLIB ${BLENDER_LINK_LIBS}) diff --git a/source/blenderplayer/bad_level_call_stubs/CMakeLists.txt b/source/blenderplayer/bad_level_call_stubs/CMakeLists.txt index 1d681d28589..0e570e19258 100644 --- a/source/blenderplayer/bad_level_call_stubs/CMakeLists.txt +++ b/source/blenderplayer/bad_level_call_stubs/CMakeLists.txt @@ -70,4 +70,8 @@ if(WITH_FREESTYLE) add_definitions(-DWITH_FREESTYLE) endif() +if(WITH_INPUT_NDOF) + add_definitions(-DWITH_INPUT_NDOF) +endif() + blender_add_lib_nolist(blenkernel_blc "${SRC}" "${INC}" "${INC_SYS}") diff --git a/source/blenderplayer/bad_level_call_stubs/stubs.c b/source/blenderplayer/bad_level_call_stubs/stubs.c index bd67462fb94..d8a4ddc8d4f 100644 --- a/source/blenderplayer/bad_level_call_stubs/stubs.c +++ b/source/blenderplayer/bad_level_call_stubs/stubs.c @@ -45,6 +45,7 @@ struct ARegion; struct ARegionType; struct BMEditMesh; struct Base; +struct bContext; struct BoundBox; struct Brush; struct CSG_FaceIteratorDescriptor; @@ -322,7 +323,22 @@ void WM_cursor_modal_restore(struct wmWindow *win) RET_NONE void WM_cursor_time(struct wmWindow *win, int nr) RET_NONE void WM_cursor_warp(struct wmWindow *win, int x, int y) RET_NONE -void WM_ndof_deadzone_set(float deadzone) RET_NONE +struct wmJob *WM_jobs_get(struct wmWindowManager *wm, struct wmWindow *win, void *owner, const char *name, int flag, int job_type) RET_NULL +void WM_jobs_customdata_set(struct wmJob *job, void *customdata, void (*free)(void *)) RET_NONE +void WM_jobs_timer(struct wmJob *job, double timestep, unsigned int note, unsigned int endnote) RET_NONE + +void WM_jobs_callbacks(struct wmJob *job, + void (*startjob)(void *, short *, short *, float *), + void (*initjob)(void *), + void (*update)(void *), + void (*endjob)(void *)) RET_NONE + +void WM_jobs_start(struct wmWindowManager *wm, struct wmJob *job) RET_NONE +void WM_report(ReportType type, const char *message) RET_NONE + +#ifdef WITH_INPUT_NDOF + void WM_ndof_deadzone_set(float deadzone) RET_NONE +#endif void WM_uilisttype_init(void) RET_NONE struct uiListType *WM_uilisttype_find(const char *idname, bool quiet) RET_NULL @@ -623,6 +639,7 @@ void uiTemplateComponentMenu(struct uiLayout *layout, struct PointerRNA *ptr, co void uiTemplateNodeSocket(struct uiLayout *layout, struct bContext *C, float *color) RET_NONE void uiTemplatePalette(struct uiLayout *layout, struct PointerRNA *ptr, const char *propname, int color) RET_NONE void uiTemplateImageStereo3d(struct uiLayout *layout, struct PointerRNA *stereo3d_format_ptr) RET_NONE +void uiTemplateCacheFile(uiLayout *layout, struct bContext *C, struct PointerRNA *ptr, const char *propname) RET_NONE /* rna render */ struct RenderResult *RE_engine_begin_result(RenderEngine *engine, int x, int y, int w, int h, const char *layername, const char *viewname) RET_NULL diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt index 7acea43d1f5..fc02dfda9d1 100644 --- a/source/creator/CMakeLists.txt +++ b/source/creator/CMakeLists.txt @@ -263,6 +263,7 @@ if(WITH_PYTHON_MODULE) else() add_executable(blender ${EXETYPE} ${SRC}) + WINDOWS_SIGN_TARGET(blender) endif() if(WITH_BUILDINFO) diff --git a/source/creator/creator.c b/source/creator/creator.c index bfb1dd94dd4..a59a45f885c 100644 --- a/source/creator/creator.c +++ b/source/creator/creator.c @@ -54,6 +54,7 @@ #include "BKE_appdir.h" #include "BKE_blender.h" #include "BKE_brush.h" +#include "BKE_cachefile.h" #include "BKE_context.h" #include "BKE_depsgraph.h" /* for DAG_init */ #include "BKE_font.h" @@ -357,6 +358,7 @@ int main( BKE_blender_globals_init(); /* blender.c */ IMB_init(); + BKE_cachefiles_init(); BKE_images_init(); BKE_modifier_init(); DAG_init(); diff --git a/source/gameengine/GamePlayer/ghost/CMakeLists.txt b/source/gameengine/GamePlayer/ghost/CMakeLists.txt index 6c09af33f9e..577e25d6198 100644 --- a/source/gameengine/GamePlayer/ghost/CMakeLists.txt +++ b/source/gameengine/GamePlayer/ghost/CMakeLists.txt @@ -81,6 +81,10 @@ if(WIN32) blender_include_dirs(../../../../intern/utfconv) endif() +if(WITH_INPUT_NDOF) + add_definitions(-DWITH_INPUT_NDOF) +endif(WITH_INPUT_NDOF) + if(WITH_CODEC_FFMPEG) add_definitions(-DWITH_FFMPEG) endif() diff --git a/source/gameengine/GamePlayer/ghost/GPG_ghost.cpp b/source/gameengine/GamePlayer/ghost/GPG_ghost.cpp index d033afacc08..edbbf93bf7a 100644 --- a/source/gameengine/GamePlayer/ghost/GPG_ghost.cpp +++ b/source/gameengine/GamePlayer/ghost/GPG_ghost.cpp @@ -61,6 +61,7 @@ extern "C" #include "DNA_scene_types.h" #include "DNA_userdef_types.h" +#include "DNA_genfile.h" #include "BLO_readfile.h" #include "BLO_runtime.h" @@ -492,6 +493,8 @@ int main( // freeing up GPU_Textures works correctly. BLI_threadapi_init(); + DNA_sdna_current_init(); + RNA_init(); init_nodesystem(); diff --git a/source/gameengine/Ketsji/KX_NavMeshObject.cpp b/source/gameengine/Ketsji/KX_NavMeshObject.cpp index b8907ca5c6b..5beda2e038a 100644 --- a/source/gameengine/Ketsji/KX_NavMeshObject.cpp +++ b/source/gameengine/Ketsji/KX_NavMeshObject.cpp @@ -306,7 +306,12 @@ bool KX_NavMeshObject::BuildNavMesh() || vertsPerPoly<3) { printf("Can't build navigation mesh data for object:%s\n", m_name.ReadPtr()); - if (vertices) delete[] vertices; + if (vertices) { + delete[] vertices; + } + if (dvertices) { + delete[] dvertices; + } return false; } diff --git a/source/tools b/source/tools -Subproject 373945d0978b6601c55c9d5879e0f488b18515c +Subproject 896c5f78952adb2d091d28c65086d46992dabda |