diff options
author | Bastien Montagne <montagne29@wanadoo.fr> | 2016-08-06 13:25:24 +0300 |
---|---|---|
committer | Bastien Montagne <montagne29@wanadoo.fr> | 2016-08-06 13:45:03 +0300 |
commit | 9843921288307be33fc39450586ff9ad226829a1 (patch) | |
tree | de6e0ddd71b48d036bf5525e62edf10a57d3238e /source/blender | |
parent | 4571fdde0ecfdebac6a9374364b05be74233aca5 (diff) | |
parent | 28c3bdf50bd62b510fdbd88a5dcb1c40f8726c20 (diff) |
Merge branch 'master' into blender2.8
Conflicts:
release/scripts/startup/bl_ui/properties_particle.py
release/scripts/startup/bl_ui/properties_physics_cloth.py
release/scripts/startup/bl_ui/properties_physics_dynamicpaint.py
release/scripts/startup/bl_ui/properties_physics_softbody.py
source/blender/blenkernel/BKE_library.h
source/blender/blenkernel/BKE_particle.h
source/blender/blenkernel/intern/cloth.c
source/blender/blenkernel/intern/library.c
source/blender/blenkernel/intern/library_query.c
source/blender/blenkernel/intern/particle_system.c
source/blender/blenkernel/intern/scene.c
source/blender/blenkernel/intern/softbody.c
source/blender/blenloader/intern/readfile.c
source/blender/blenloader/intern/versioning_270.c
source/blender/editors/space_file/filesel.c
source/blender/editors/space_outliner/outliner_intern.h
source/blender/makesdna/DNA_ID.h
source/blender/makesdna/DNA_object_force.h
source/blender/makesdna/DNA_particle_types.h
source/blender/makesrna/intern/rna_particle.c
source/blender/makesrna/intern/rna_sculpt_paint.c
source/blender/makesrna/intern/rna_smoke.c
source/blender/makesrna/intern/rna_space.c
Diffstat (limited to 'source/blender')
235 files changed, 17577 insertions, 1793 deletions
diff --git a/source/blender/CMakeLists.txt b/source/blender/CMakeLists.txt index ca103e4f53e..d49ceb1636b 100644 --- a/source/blender/CMakeLists.txt +++ b/source/blender/CMakeLists.txt @@ -30,6 +30,7 @@ set(SRC_DNA_INC ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_anim_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_armature_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 @@ -151,3 +152,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..f579a3b200a --- /dev/null +++ b/source/blender/alembic/CMakeLists.txt @@ -0,0 +1,81 @@ +# ***** 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 +) + +set(INC_SYS + ${ALEMBIC_INCLUDE_DIRS} + ${HDF5_INCLUDE_DIRS} + ${OPENEXR_INCLUDE_DIRS} +) +if(APPLE) + 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..38a6d3d33cf --- /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_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..4e1e4e7e490 --- /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_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..127e8853789 --- /dev/null +++ b/source/blender/alembic/intern/abc_exporter.cc @@ -0,0 +1,600 @@ +/* + * ***** 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> + +#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_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; + +/* ************************************************************************** */ + +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 / 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 / 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); + +#ifdef WITH_ALEMBIC_HDF5 + if (!m_settings.export_ogawa) { + m_archive = Alembic::Abc::CreateArchiveWithInfo(Alembic::AbcCoreHDF5::WriteArchive(), + m_filename, + "Blender", + scene_name, + Alembic::Abc::ErrorHandler::kThrowPolicy, + arg); + } + else +#endif + { + m_archive = Alembic::Abc::CreateArchiveWithInfo(Alembic::AbcCoreOgawa::WriteArchive(), + m_filename, + "Blender", + scene_name, + Alembic::Abc::ErrorHandler::kThrowPolicy, + arg); + } + + /* 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; + } + + double f = *begin; + setCurrentFrame(bmain, f); + + if (shape_frames.count(f) != 0) { + for (int i = 0, e = m_shapes.size(); i != e; ++i) { + m_shapes[i]->write(); + } + } + + if (xform_frames.count(f) == 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..070eb9ea81a --- /dev/null +++ b/source/blender/alembic/intern/abc_exporter.h @@ -0,0 +1,112 @@ +/* + * ***** 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 <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; + + 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..f1c7b6b3aa3 --- /dev/null +++ b/source/blender/alembic/intern/abc_mesh.cc @@ -0,0 +1,1213 @@ +/* + * ***** 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_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_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..a3c18ad6301 --- /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_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..fa5b71ac094 --- /dev/null +++ b/source/blender/alembic/intern/abc_points.cc @@ -0,0 +1,198 @@ +/* + * ***** 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 "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_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..3326ae0ed84 --- /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_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..688a25d85f6 --- /dev/null +++ b/source/blender/alembic/intern/abc_util.h @@ -0,0 +1,125 @@ +/* + * ***** 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(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); + } + } +} + +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..0e96ac22e11 --- /dev/null +++ b/source/blender/alembic/intern/alembic_capi.cc @@ -0,0 +1,1136 @@ +/* + * ***** 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 "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; + +struct AbcArchiveHandle { + int unused; +}; + +ABC_INLINE IArchive *archive_from_handle(AbcArchiveHandle *handle) +{ + return reinterpret_cast<IArchive *>(handle); +} + +ABC_INLINE AbcArchiveHandle *handle_from_archive(IArchive *archive) +{ + return reinterpret_cast<AbcArchiveHandle *>(archive); +} + +static IArchive *open_archive(const std::string &filename) +{ + Alembic::AbcCoreAbstract::ReadArraySampleCachePtr cache_ptr; + IArchive *archive; + + try { + archive = new IArchive(Alembic::AbcCoreOgawa::ReadArchive(), + filename.c_str(), ErrorHandler::kThrowPolicy, + cache_ptr); + } + catch (const Exception &e) { + std::cerr << e.what() << '\n'; + +#ifdef WITH_ALEMBIC_HDF5 + try { + archive = new IArchive(Alembic::AbcCoreHDF5::ReadArchive(), + filename.c_str(), ErrorHandler::kThrowPolicy, + cache_ptr); + } + catch (const Exception &) { + std::cerr << e.what() << '\n'; + return NULL; + } +#else + return NULL; +#endif + } + + return 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) +{ + IArchive *archive = open_archive(filename); + + if (!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; + + IArchive *archive = open_archive(data->filename); + + if (!archive || !archive->valid()) { + 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) +{ + IArchive *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 has_loop_normals = false; + read_mesh_sample(&settings, schema, sample_sel, config, has_loop_normals); + + if (new_dm) { + /* Check if we had ME_SMOOTH flag set to restore it. */ + if (!has_loop_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; + } + + 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) +{ + IArchive *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_animsys.h b/source/blender/blenkernel/BKE_animsys.h index 983f3ce22b8..00ea323f934 100644 --- a/source/blender/blenkernel/BKE_animsys.h +++ b/source/blender/blenkernel/BKE_animsys.h @@ -37,6 +37,7 @@ struct Main; struct AnimData; struct KeyingSet; struct KS_Path; +struct PathResolvedRNA; struct bContext; struct PointerRNA; diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 618b36c5851..483fefbd89c 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -28,7 +28,7 @@ * and keep comment above the defines. * Use STRINGIFY() rather than defining with quotes */ #define BLENDER_VERSION 277 -#define BLENDER_SUBVERSION 1 +#define BLENDER_SUBVERSION 3 /* Several breakages with 270, e.g. constraint deg vs rad */ #define BLENDER_MINVERSION 270 #define BLENDER_MINSUBVERSION 6 diff --git a/source/blender/blenkernel/BKE_cachefile.h b/source/blender/blenkernel/BKE_cachefile.h new file mode 100644 index 00000000000..51b161f1a06 --- /dev/null +++ b/source/blender/blenkernel/BKE_cachefile.h @@ -0,0 +1,67 @@ +/* + * ***** 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_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_cloth.h b/source/blender/blenkernel/BKE_cloth.h index ddc9fa530de..56d9b2439fb 100644 --- a/source/blender/blenkernel/BKE_cloth.h +++ b/source/blender/blenkernel/BKE_cloth.h @@ -171,6 +171,7 @@ typedef enum { CLOTH_SIMSETTINGS_FLAG_CCACHE_EDIT = (1 << 12), /* edit cache in editmode */ CLOTH_SIMSETTINGS_FLAG_NO_SPRING_COMPRESS = (1 << 13), /* don't allow spring compression */ CLOTH_SIMSETTINGS_FLAG_SEW = (1 << 14), /* pull ends of loose edges together */ + CLOTH_SIMSETTINGS_FLAG_DYNAMIC_BASEMESH = (1 << 15), /* make simulation respect deformations in the base object */ } CLOTH_SIMSETTINGS_FLAGS; /* COLLISION FLAGS */ diff --git a/source/blender/blenkernel/BKE_colortools.h b/source/blender/blenkernel/BKE_colortools.h index e5d348031e9..5b4f5910821 100644 --- a/source/blender/blenkernel/BKE_colortools.h +++ b/source/blender/blenkernel/BKE_colortools.h @@ -61,7 +61,7 @@ void curvemap_reset(struct CurveMap *cuma, const struct rctf void curvemap_remove(struct CurveMap *cuma, const short flag); bool curvemap_remove_point(struct CurveMap *cuma, struct CurveMapPoint *cmp); struct CurveMapPoint *curvemap_insert(struct CurveMap *cuma, float x, float y); -void curvemap_sethandle(struct CurveMap *cuma, int type); +void curvemap_handle_set(struct CurveMap *cuma, int type); void curvemapping_changed(struct CurveMapping *cumap, const bool rem_doubles); void curvemapping_changed_all(struct CurveMapping *cumap); diff --git a/source/blender/blenkernel/BKE_context.h b/source/blender/blenkernel/BKE_context.h index 87122da3245..c6795f87eab 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; @@ -58,6 +59,9 @@ struct bPoseChannel; struct bGPdata; struct bGPDlayer; struct bGPDframe; +struct bGPDpalette; +struct bGPDpalettecolor; +struct bGPDbrush; struct wmWindow; struct wmWindowManager; struct SpaceText; @@ -267,6 +271,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); @@ -282,6 +288,9 @@ int CTX_data_visible_pose_bones(const bContext *C, ListBase *list); struct bGPdata *CTX_data_gpencil_data(const bContext *C); struct bGPDlayer *CTX_data_active_gpencil_layer(const bContext *C); struct bGPDframe *CTX_data_active_gpencil_frame(const bContext *C); +struct bGPDpalette *CTX_data_active_gpencil_palette(const bContext *C); +struct bGPDpalettecolor *CTX_data_active_gpencil_palettecolor(const bContext *C); +struct bGPDbrush *CTX_data_active_gpencil_brush(const bContext *C); int CTX_data_visible_gpencil_layers(const bContext *C, ListBase *list); int CTX_data_editable_gpencil_layers(const bContext *C, ListBase *list); int CTX_data_editable_gpencil_strokes(const bContext *C, ListBase *list); diff --git a/source/blender/blenkernel/BKE_fcurve.h b/source/blender/blenkernel/BKE_fcurve.h index bb4eb652ae2..3a45097efc5 100644 --- a/source/blender/blenkernel/BKE_fcurve.h +++ b/source/blender/blenkernel/BKE_fcurve.h @@ -48,6 +48,7 @@ struct AnimData; struct bAction; struct BezTriple; struct StructRNA; +struct PathResolvedRNA; struct PointerRNA; struct PropertyRNA; @@ -107,7 +108,7 @@ bool driver_get_variable_property( struct ChannelDriver *driver, struct DriverTarget *dtar, struct PointerRNA *r_ptr, struct PropertyRNA **r_prop, int *r_index); -float evaluate_driver(struct ChannelDriver *driver, const float evaltime); +float evaluate_driver(struct PathResolvedRNA *anim_rna, struct ChannelDriver *driver, const float evaltime); /* ************** F-Curve Modifiers *************** */ @@ -278,8 +279,9 @@ void correct_bezpart(float v1[2], float v2[2], float v3[2], float v4[2]); /* evaluate fcurve */ float evaluate_fcurve(struct FCurve *fcu, float evaltime); +float evaluate_fcurve_driver(struct PathResolvedRNA *anim_rna, struct FCurve *fcu, float evaltime); /* evaluate fcurve and store value */ -float calculate_fcurve(struct FCurve *fcu, float evaltime); +float calculate_fcurve(struct PathResolvedRNA *anim_rna, struct FCurve *fcu, float evaltime); /* ************* F-Curve Samples API ******************** */ diff --git a/source/blender/blenkernel/BKE_gpencil.h b/source/blender/blenkernel/BKE_gpencil.h index e9e3cd3b16e..ab8b83f8271 100644 --- a/source/blender/blenkernel/BKE_gpencil.h +++ b/source/blender/blenkernel/BKE_gpencil.h @@ -31,38 +31,53 @@ * \author Joshua Leung */ +struct ToolSettings; struct ListBase; struct bGPdata; struct bGPDlayer; struct bGPDframe; struct bGPDstroke; +struct bGPDpalette; +struct bGPDpalettecolor; struct Main; /* ------------ Grease-Pencil API ------------------ */ -bool free_gpencil_strokes(struct bGPDframe *gpf); -void free_gpencil_frames(struct bGPDlayer *gpl); -void free_gpencil_layers(struct ListBase *list); -void BKE_gpencil_free(struct bGPdata *gpd); +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(struct bGPDframe *src); -struct bGPDlayer *gpencil_layer_duplicate(struct bGPDlayer *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 *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 *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 */ #define GPENCIL_ALPHA_OPACITY_THRESH 0.001f +#define GPENCIL_STRENGTH_MIN 0.003f bool gpencil_layer_is_editable(const struct bGPDlayer *gpl); @@ -79,12 +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); +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_library.h b/source/blender/blenkernel/BKE_library.h index 09b4881aac8..e32bc2ffb20 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 33 +#define MAX_LIBARRAY 34 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 d25202f2991..6487954844b 100644 --- a/source/blender/blenkernel/BKE_main.h +++ b/source/blender/blenkernel/BKE_main.h @@ -101,6 +101,7 @@ typedef struct Main { ListBase movieclip; ListBase mask; ListBase linestyle; + ListBase cachefiles; char id_tag_update[256]; diff --git a/source/blender/blenkernel/BKE_movieclip.h b/source/blender/blenkernel/BKE_movieclip.h index 3d963ac109c..3237c146bc5 100644 --- a/source/blender/blenkernel/BKE_movieclip.h +++ b/source/blender/blenkernel/BKE_movieclip.h @@ -41,6 +41,9 @@ struct MovieDistortion; void BKE_movieclip_free(struct MovieClip *clip); +struct MovieClip *BKE_movieclip_copy(struct Main *bmain, struct MovieClip *clip); +void BKE_movieclip_make_local(struct Main *bmain, struct MovieClip *clip, const bool lib_local); + struct MovieClip *BKE_movieclip_file_add(struct Main *bmain, const char *name); struct MovieClip *BKE_movieclip_file_add_exists_ex(struct Main *bmain, const char *name, bool *r_exists); struct MovieClip *BKE_movieclip_file_add_exists(struct Main *bmain, const char *name); diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index bf1cfb263eb..0a3cc950f32 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -99,6 +99,8 @@ void BKE_paint_set_overlay_override(enum OverlayFlags flag); /* palettes */ void BKE_palette_free(struct Palette *palette); struct Palette *BKE_palette_add(struct Main *bmain, const char *name); +struct Palette *BKE_palette_copy(struct Main *bmain, struct Palette *palette); +void BKE_palette_make_local(struct Main *bmain, struct Palette *palette, const bool lib_local); struct PaletteColor *BKE_palette_color_add(struct Palette *palette); bool BKE_palette_is_empty(const struct Palette *palette); void BKE_palette_color_remove(struct Palette *palette, struct PaletteColor *color); @@ -107,6 +109,8 @@ void BKE_palette_clear(struct Palette *palette); /* paint curves */ struct PaintCurve *BKE_paint_curve_add(struct Main *bmain, const char *name); void BKE_paint_curve_free(struct PaintCurve *pc); +struct PaintCurve *BKE_paint_curve_copy(struct Main *bmain, struct PaintCurve *pc); +void BKE_paint_curve_make_local(struct Main *bmain, struct PaintCurve *pc, const bool lib_local); void BKE_paint_init(struct Scene *sce, PaintMode mode, const char col[3]); void BKE_paint_free(struct Paint *p); diff --git a/source/blender/blenkernel/BKE_tracking.h b/source/blender/blenkernel/BKE_tracking.h index 2ca88425c29..1938bb08395 100644 --- a/source/blender/blenkernel/BKE_tracking.h +++ b/source/blender/blenkernel/BKE_tracking.h @@ -52,6 +52,7 @@ struct rcti; /* **** Common functions **** */ void BKE_tracking_free(struct MovieTracking *tracking); +void BKE_tracking_copy(struct MovieTracking *tracking_dst, struct MovieTracking *tracking_src); void BKE_tracking_settings_init(struct MovieTracking *tracking); diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 74930dbea4a..ca55ba0226a 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -80,6 +80,7 @@ set(SRC intern/brush.c intern/bullet.c intern/bvhutils.c + intern/cachefile.c intern/camera.c intern/cdderivedmesh.c intern/cloth.c @@ -200,6 +201,7 @@ set(SRC BKE_brush.h BKE_bullet.h BKE_bvhutils.h + BKE_cachefile.h BKE_camera.h BKE_ccg.h BKE_cdderivedmesh.h @@ -491,6 +493,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/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c index fa156e95d46..7ad2b118e72 100644 --- a/source/blender/blenkernel/intern/anim_sys.c +++ b/source/blender/blenkernel/intern/anim_sys.c @@ -96,6 +96,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 */ @@ -1156,6 +1157,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) @@ -1243,6 +1247,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); @@ -1475,161 +1482,193 @@ static bool animsys_remap_path(AnimMapper *UNUSED(remap), char *path, char **dst return false; } +static bool animsys_store_rna_setting( + PointerRNA *ptr, AnimMapper *remap, + /* typically 'fcu->rna_path', 'fcu->array_index' */ + const char *rna_path, const int array_index, + PathResolvedRNA *r_result) +{ + bool success = false; + + char *path = NULL; + bool free_path; + + /* get path, remapped as appropriate to work in its new environment */ + free_path = animsys_remap_path(remap, (char *)rna_path, &path); + + /* write value to setting */ + if (path) { + /* get property to write to */ + if (RNA_path_resolve_property(ptr, path, &r_result->ptr, &r_result->prop)) { + if ((ptr->id.data == NULL) || RNA_property_animateable(&r_result->ptr, r_result->prop)) { + int array_len = RNA_property_array_length(&r_result->ptr, r_result->prop); + + 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>", + path, array_index, array_len - 1); + } + } + else { + r_result->prop_index = array_len ? array_index : -1; + success = true; + } + } + } + else { + /* failed to get path */ + /* XXX don't tag as failed yet though, as there are some legit situations (Action Constraint) + * where some channels will not exist, but shouldn't lock up Action */ + if (G.debug & G_DEBUG) { + printf("Animato: Invalid path. ID = '%s', '%s[%d]'\n", + (ptr->id.data) ? (((ID *)ptr->id.data)->name + 2) : "<No ID>", + path, array_index); + } + } + } + + /* free temp path-info */ + if (free_path) { + MEM_freeN((void *)path); + } + + return success; +} + /* less than 1.0 evaluates to false, use epsilon to avoid float error */ #define ANIMSYS_FLOAT_AS_BOOL(value) ((value) > ((1.0f - FLT_EPSILON))) /* Write the given value to a setting using RNA, and return success */ -static bool animsys_write_rna_setting(PointerRNA *ptr, char *path, int array_index, float value) +static bool animsys_write_rna_setting(PathResolvedRNA *anim_rna, const float value) { - PropertyRNA *prop; - PointerRNA new_ptr; + PropertyRNA *prop = anim_rna->prop; + PointerRNA *ptr = &anim_rna->ptr; + int array_index = anim_rna->prop_index; - //printf("%p %s %i %f\n", ptr, path, array_index, value); - - /* get property to write to */ - if (RNA_path_resolve_property(ptr, path, &new_ptr, &prop)) { - /* set value for animatable numerical values only - * HACK: some local F-Curves (e.g. those on NLA Strips) are evaluated - * without an ID provided, which causes the animateable test to fail! - */ - if (RNA_property_animateable(&new_ptr, prop) || (ptr->id.data == NULL)) { - int array_len = RNA_property_array_length(&new_ptr, prop); - bool written = false; - - 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>", - path, array_index, array_len - 1); + /* caller must ensure this is animatable */ + BLI_assert(RNA_property_animateable(ptr, prop) || ptr->id.data == NULL); + + /* set value for animatable numerical values only + * HACK: some local F-Curves (e.g. those on NLA Strips) are evaluated + * without an ID provided, which causes the animateable test to fail! + */ + bool written = false; + + switch (RNA_property_type(prop)) { + case PROP_BOOLEAN: + { + const int value_coerce = ANIMSYS_FLOAT_AS_BOOL(value); + if (array_index != -1) { + if (RNA_property_boolean_get_index(ptr, prop, array_index) != value_coerce) { + RNA_property_boolean_set_index(ptr, prop, array_index, value_coerce); + written = true; } - - return false; } - - switch (RNA_property_type(prop)) { - case PROP_BOOLEAN: - if (array_len) { - if (RNA_property_boolean_get_index(&new_ptr, prop, array_index) != ANIMSYS_FLOAT_AS_BOOL(value)) { - RNA_property_boolean_set_index(&new_ptr, prop, array_index, ANIMSYS_FLOAT_AS_BOOL(value)); - written = true; - } - } - else { - if (RNA_property_boolean_get(&new_ptr, prop) != ANIMSYS_FLOAT_AS_BOOL(value)) { - RNA_property_boolean_set(&new_ptr, prop, ANIMSYS_FLOAT_AS_BOOL(value)); - written = true; - } - } - break; - case PROP_INT: - if (array_len) { - if (RNA_property_int_get_index(&new_ptr, prop, array_index) != (int)value) { - RNA_property_int_set_index(&new_ptr, prop, array_index, (int)value); - written = true; - } - } - else { - if (RNA_property_int_get(&new_ptr, prop) != (int)value) { - RNA_property_int_set(&new_ptr, prop, (int)value); - written = true; - } - } - break; - case PROP_FLOAT: - if (array_len) { - if (RNA_property_float_get_index(&new_ptr, prop, array_index) != value) { - RNA_property_float_set_index(&new_ptr, prop, array_index, value); - written = true; - } - } - else { - if (RNA_property_float_get(&new_ptr, prop) != value) { - RNA_property_float_set(&new_ptr, prop, value); - written = true; - } - } - break; - case PROP_ENUM: - if (RNA_property_enum_get(&new_ptr, prop) != (int)value) { - RNA_property_enum_set(&new_ptr, prop, (int)value); - written = true; - } - break; - default: - /* nothing can be done here... so it is unsuccessful? */ - return false; + else { + if (RNA_property_boolean_get(ptr, prop) != value_coerce) { + RNA_property_boolean_set(ptr, prop, value_coerce); + written = true; + } } - - /* RNA property update disabled for now - [#28525] [#28690] [#28774] [#28777] */ -#if 0 - /* buffer property update for later flushing */ - if (written && RNA_property_update_check(prop)) { - short skip_updates_hack = 0; - - /* optimization hacks: skip property updates for those properties - * for we know that which the updates in RNA were really just for - * flushing property editing via UI/Py - */ - if (new_ptr.type == &RNA_PoseBone) { - /* bone transforms - update pose (i.e. tag depsgraph) */ - skip_updates_hack = 1; + break; + } + case PROP_INT: + { + const int value_coerce = (int)value; + if (array_index != -1) { + if (RNA_property_int_get_index(ptr, prop, array_index) != value_coerce) { + RNA_property_int_set_index(ptr, prop, array_index, value_coerce); + written = true; } - - if (skip_updates_hack == 0) - RNA_property_update_cache_add(&new_ptr, prop); } -#endif - - /* as long as we don't do property update, we still tag datablock - * as having been updated. this flag does not cause any updates to - * be run, it's for e.g. render engines to synchronize data */ - if (written && new_ptr.id.data) { - ID *id = new_ptr.id.data; - - /* for cases like duplifarmes it's only a temporary so don't - * notify anyone of updates */ - if (!(id->tag & LIB_TAG_ANIM_NO_RECALC)) { - id->tag |= LIB_TAG_ID_RECALC; - DAG_id_type_tag(G.main, GS(id->name)); + else { + if (RNA_property_int_get(ptr, prop) != value_coerce) { + RNA_property_int_set(ptr, prop, value_coerce); + written = true; } } + break; } - - /* successful */ - return true; + case PROP_FLOAT: + { + if (array_index != -1) { + if (RNA_property_float_get_index(ptr, prop, array_index) != value) { + RNA_property_float_set_index(ptr, prop, array_index, value); + written = true; + } + } + else { + if (RNA_property_float_get(ptr, prop) != value) { + RNA_property_float_set(ptr, prop, value); + written = true; + } + } + break; + } + case PROP_ENUM: + { + const int value_coerce = (int)value; + if (RNA_property_enum_get(ptr, prop) != value_coerce) { + RNA_property_enum_set(ptr, prop, value_coerce); + written = true; + } + break; + } + default: + /* nothing can be done here... so it is unsuccessful? */ + return false; } - else { - /* failed to get path */ - /* XXX don't tag as failed yet though, as there are some legit situations (Action Constraint) - * where some channels will not exist, but shouldn't lock up Action */ - if (G.debug & G_DEBUG) { - printf("Animato: Invalid path. ID = '%s', '%s[%d]'\n", - (ptr->id.data) ? (((ID *)ptr->id.data)->name + 2) : "<No ID>", - path, array_index); + + /* RNA property update disabled for now - [#28525] [#28690] [#28774] [#28777] */ +#if 0 + /* buffer property update for later flushing */ + if (written && RNA_property_update_check(prop)) { + short skip_updates_hack = 0; + + /* optimization hacks: skip property updates for those properties + * for we know that which the updates in RNA were really just for + * flushing property editing via UI/Py + */ + if (new_ptr.type == &RNA_PoseBone) { + /* bone transforms - update pose (i.e. tag depsgraph) */ + skip_updates_hack = 1; + } + + if (skip_updates_hack == 0) + RNA_property_update_cache_add(ptr, prop); + } +#endif + + /* as long as we don't do property update, we still tag datablock + * as having been updated. this flag does not cause any updates to + * be run, it's for e.g. render engines to synchronize data */ + if (written && ptr->id.data) { + ID *id = ptr->id.data; + + /* for cases like duplifarmes it's only a temporary so don't + * notify anyone of updates */ + if (!(id->tag & LIB_TAG_ANIM_NO_RECALC)) { + id->tag |= LIB_TAG_ID_RECALC; + DAG_id_type_tag(G.main, GS(id->name)); } - return false; } + + /* successful */ + return true; } /* Simple replacement based data-setting of the FCurve using RNA */ bool BKE_animsys_execute_fcurve(PointerRNA *ptr, AnimMapper *remap, FCurve *fcu, float curval) { - char *path = NULL; - bool free_path = false; + PathResolvedRNA anim_rna; bool ok = false; - - /* get path, remapped as appropriate to work in its new environment */ - free_path = animsys_remap_path(remap, fcu->rna_path, &path); - - /* write value to setting */ - if (path) - ok = animsys_write_rna_setting(ptr, path, fcu->array_index, curval); - - /* free temp path-info */ - if (free_path) - MEM_freeN(path); - + + if (animsys_store_rna_setting(ptr, remap, fcu->rna_path, fcu->array_index, &anim_rna)) { + ok = animsys_write_rna_setting(&anim_rna, curval); + } + /* return whether we were successful */ return ok; } @@ -1647,8 +1686,11 @@ static void animsys_evaluate_fcurves(PointerRNA *ptr, ListBase *list, AnimMapper if ((fcu->grp == NULL) || (fcu->grp->flag & AGRP_MUTED) == 0) { /* check if this curve should be skipped */ if ((fcu->flag & (FCURVE_MUTED | FCURVE_DISABLED)) == 0) { - const float curval = calculate_fcurve(fcu, ctime); - BKE_animsys_execute_fcurve(ptr, remap, fcu, curval); + PathResolvedRNA anim_rna; + if (animsys_store_rna_setting(ptr, remap, fcu->rna_path, fcu->array_index, &anim_rna)) { + const float curval = calculate_fcurve(&anim_rna, fcu, ctime); + animsys_write_rna_setting(&anim_rna, curval); + } } } } @@ -1677,8 +1719,12 @@ static void animsys_evaluate_drivers(PointerRNA *ptr, AnimData *adt, float ctime /* evaluate this using values set already in other places * NOTE: for 'layering' option later on, we should check if we should remove old value before adding * new to only be done when drivers only changed */ - const float curval = calculate_fcurve(fcu, ctime); - ok = BKE_animsys_execute_fcurve(ptr, NULL, fcu, curval); + + PathResolvedRNA anim_rna; + if (animsys_store_rna_setting(ptr, NULL, fcu->rna_path, fcu->array_index, &anim_rna)) { + const float curval = calculate_fcurve(&anim_rna, fcu, ctime); + ok = animsys_write_rna_setting(&anim_rna, curval); + } /* clear recalc flag */ driver->flag &= ~DRIVER_FLAG_RECALC; @@ -1746,8 +1792,11 @@ void animsys_evaluate_action_group(PointerRNA *ptr, bAction *act, bActionGroup * for (fcu = agrp->channels.first; (fcu) && (fcu->grp == agrp); fcu = fcu->next) { /* check if this curve should be skipped */ if ((fcu->flag & (FCURVE_MUTED | FCURVE_DISABLED)) == 0) { - const float curval = calculate_fcurve(fcu, ctime); - BKE_animsys_execute_fcurve(ptr, remap, fcu, curval); + PathResolvedRNA anim_rna; + if (animsys_store_rna_setting(ptr, remap, fcu->rna_path, fcu->array_index, &anim_rna)) { + const float curval = calculate_fcurve(&anim_rna, fcu, ctime); + animsys_write_rna_setting(&anim_rna, curval); + } } } } @@ -2605,8 +2654,12 @@ static void animsys_evaluate_overrides(PointerRNA *ptr, AnimData *adt) AnimOverride *aor; /* for each override, simply execute... */ - for (aor = adt->overrides.first; aor; aor = aor->next) - animsys_write_rna_setting(ptr, aor->rna_path, aor->array_index, aor->value); + for (aor = adt->overrides.first; aor; aor = aor->next) { + PathResolvedRNA anim_rna; + if (animsys_store_rna_setting(ptr, NULL, aor->rna_path, aor->array_index, &anim_rna)) { + animsys_write_rna_setting(&anim_rna, aor->value); + } + } } /* ***************************************** */ @@ -2817,6 +2870,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 @@ -2878,8 +2934,13 @@ void BKE_animsys_eval_driver(EvaluationContext *eval_ctx, * NOTE: for 'layering' option later on, we should check if we should remove old value before adding * new to only be done when drivers only changed */ //printf("\told val = %f\n", fcu->curval); - const float curval = calculate_fcurve(fcu, eval_ctx->ctime); - ok = BKE_animsys_execute_fcurve(&id_ptr, NULL, fcu, curval); + + PathResolvedRNA anim_rna; + if (animsys_store_rna_setting(&id_ptr, NULL, fcu->rna_path, fcu->array_index, &anim_rna)) { + const float curval = calculate_fcurve(&anim_rna, fcu, eval_ctx->ctime); + ok = animsys_write_rna_setting(&anim_rna, curval); + } + //printf("\tnew val = %f\n", fcu->curval); /* clear recalc flag */ diff --git a/source/blender/blenkernel/intern/bpath.c b/source/blender/blenkernel/intern/bpath.c index f3ff0f253e5..4121bde4d0b 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" @@ -618,6 +619,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..16f263791db --- /dev/null +++ b/source/blender/blenkernel/intern/cachefile.c @@ -0,0 +1,173 @@ +/* + * ***** 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_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 + +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; +} + +/** 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_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) +{ + if (cache_file->handle == NULL) { + BKE_cachefile_reload(bmain, cache_file); + } +} + +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..d257a1cfcae 100644 --- a/source/blender/blenkernel/intern/cdderivedmesh.c +++ b/source/blender/blenkernel/intern/cdderivedmesh.c @@ -3402,7 +3402,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 fa0960515b4..4ab6c82b2c2 100644 --- a/source/blender/blenkernel/intern/cloth.c +++ b/source/blender/blenkernel/intern/cloth.c @@ -57,6 +57,7 @@ static void cloth_to_object (Object *ob, ClothModifierData *clmd, float (*verte static void cloth_from_mesh ( ClothModifierData *clmd, DerivedMesh *dm ); static int cloth_from_object(Object *ob, ClothModifierData *clmd, DerivedMesh *dm, float framenr, int first); static void cloth_update_springs( ClothModifierData *clmd ); +static void cloth_update_verts( Object *ob, ClothModifierData *clmd, DerivedMesh *dm ); static void cloth_update_spring_lengths( ClothModifierData *clmd, DerivedMesh *dm ); static int cloth_build_springs ( ClothModifierData *clmd, DerivedMesh *dm ); static void cloth_apply_vgroup ( ClothModifierData *clmd, DerivedMesh *dm ); @@ -95,6 +96,7 @@ void cloth_init(ClothModifierData *clmd ) clmd->sim_parms->avg_spring_len = 0.0; clmd->sim_parms->presets = 2; /* cotton as start setting */ clmd->sim_parms->timescale = 1.0f; /* speed factor, describes how fast cloth moves */ + clmd->sim_parms->time_scale = 1.0f; /* multiplies cloth speed */ clmd->sim_parms->reset = 0; clmd->sim_parms->vel_damping = 1.0f; /* 1.0 = no damping, 0.0 = fully dampened */ @@ -345,10 +347,13 @@ static int do_step_cloth(Object *ob, ClothModifierData *clmd, DerivedMesh *resul effectors = pdInitEffectors(clmd->scene, ob, clmd->sim_parms->effector_weights, true); + if (clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_DYNAMIC_BASEMESH ) + cloth_update_verts ( ob, clmd, result ); + /* Support for dynamic vertex groups, changing from frame to frame */ cloth_apply_vgroup ( clmd, result ); - if ( clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_SEW ) + if ( clmd->sim_parms->flags & (CLOTH_SIMSETTINGS_FLAG_SEW | CLOTH_SIMSETTINGS_FLAG_DYNAMIC_BASEMESH) ) cloth_update_spring_lengths ( clmd, result ); cloth_update_springs( clmd ); @@ -376,7 +381,7 @@ void clothModifier_do(ClothModifierData *clmd, Scene *scene, Object *ob, Derived clmd->scene= scene; /* nice to pass on later :) */ - clmd->sim_parms->timescale= 1.0f; + clmd->sim_parms->timescale = 1.0f; if (clmd->sim_parms->reset || (clmd->clothObject && dm->getNumVerts(dm) != clmd->clothObject->mvert_num)) { clmd->sim_parms->reset = 0; @@ -729,7 +734,7 @@ static int cloth_from_object(Object *ob, ClothModifierData *clmd, DerivedMesh *d clmd->clothObject->springs = NULL; clmd->clothObject->numsprings = -1; - if ( clmd->sim_parms->shapekey_rest ) + if ( clmd->sim_parms->shapekey_rest && !(clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_DYNAMIC_BASEMESH ) ) shapekey_rest = dm->getVertDataArray ( dm, CD_CLOTH_ORCO ); mvert = dm->getVertArray (dm); @@ -1105,6 +1110,20 @@ static void cloth_update_springs( ClothModifierData *clmd ) cloth_hair_update_bending_targets(clmd); } +/* Update rest verts, for dynamically deformable cloth */ +static void cloth_update_verts( Object *ob, ClothModifierData *clmd, DerivedMesh *dm ) +{ + unsigned int i = 0; + MVert *mvert = dm->getVertArray (dm); + ClothVertex *verts = clmd->clothObject->verts; + + /* vertex count is already ensured to match */ + for ( i = 0; i < dm->getNumVerts(dm); i++, verts++ ) { + copy_v3_v3(verts->xrest, mvert[i].co); + mul_m4_v3(ob->obmat, verts->xrest); + } +} + /* Update spring rest lenght, for dynamically deformable cloth */ static void cloth_update_spring_lengths( ClothModifierData *clmd, DerivedMesh *dm ) { diff --git a/source/blender/blenkernel/intern/colortools.c b/source/blender/blenkernel/intern/colortools.c index 53a74024c51..4f3ffed41bc 100644 --- a/source/blender/blenkernel/intern/colortools.c +++ b/source/blender/blenkernel/intern/colortools.c @@ -293,8 +293,8 @@ void curvemap_reset(CurveMap *cuma, const rctf *clipr, int preset, int slope) cuma->curve[1].x = clipr->xmax; cuma->curve[1].y = clipr->ymin; if (slope == CURVEMAP_SLOPE_POS_NEG) { - cuma->curve[0].flag |= CUMA_VECTOR; - cuma->curve[1].flag |= CUMA_VECTOR; + cuma->curve[0].flag |= CUMA_HANDLE_VECTOR; + cuma->curve[1].flag |= CUMA_HANDLE_VECTOR; } break; case CURVE_PRESET_SHARP: @@ -391,15 +391,25 @@ void curvemap_reset(CurveMap *cuma, const rctf *clipr, int preset, int slope) } } -/* if type==1: vector, else auto */ -void curvemap_sethandle(CurveMap *cuma, int type) +/** + * \param type: eBezTriple_Handle + */ +void curvemap_handle_set(CurveMap *cuma, int type) { int a; for (a = 0; a < cuma->totpoint; a++) { if (cuma->curve[a].flag & CUMA_SELECT) { - if (type) cuma->curve[a].flag |= CUMA_VECTOR; - else cuma->curve[a].flag &= ~CUMA_VECTOR; + cuma->curve[a].flag &= ~(CUMA_HANDLE_VECTOR | CUMA_HANDLE_AUTO_ANIM); + if (type == HD_VECT) { + cuma->curve[a].flag |= CUMA_HANDLE_VECTOR; + } + else if (type == HD_AUTO_ANIM) { + cuma->curve[a].flag |= CUMA_HANDLE_AUTO_ANIM; + } + else { + /* pass */ + } } } } @@ -457,7 +467,7 @@ static void calchandle_curvemap( if (len_a == 0.0f) len_a = 1.0f; if (len_b == 0.0f) len_b = 1.0f; - if (bezt->h1 == HD_AUTO || bezt->h2 == HD_AUTO) { /* auto */ + if (ELEM(bezt->h1, HD_AUTO, HD_AUTO_ANIM) || ELEM(bezt->h2, HD_AUTO, HD_AUTO_ANIM)) { /* auto */ float tvec[2]; tvec[0] = dvec_b[0] / len_b + dvec_a[0] / len_a; tvec[1] = dvec_b[1] / len_b + dvec_a[1] / len_a; @@ -465,13 +475,57 @@ static void calchandle_curvemap( len = len_v2(tvec) * 2.5614f; if (len != 0.0f) { - if (bezt->h1 == HD_AUTO) { + if (ELEM(bezt->h1, HD_AUTO, HD_AUTO_ANIM)) { len_a /= len; madd_v2_v2v2fl(p2_h1, p2, tvec, -len_a); + + if ((bezt->h1 == HD_AUTO_ANIM) && next && prev) { /* keep horizontal if extrema */ + const float ydiff1 = prev->vec[1][1] - bezt->vec[1][1]; + const float ydiff2 = next->vec[1][1] - bezt->vec[1][1]; + if ((ydiff1 <= 0.0f && ydiff2 <= 0.0f) || + (ydiff1 >= 0.0f && ydiff2 >= 0.0f)) + { + bezt->vec[0][1] = bezt->vec[1][1]; + } + else { /* handles should not be beyond y coord of two others */ + if (ydiff1 <= 0.0f) { + if (prev->vec[1][1] > bezt->vec[0][1]) { + bezt->vec[0][1] = prev->vec[1][1]; + } + } + else { + if (prev->vec[1][1] < bezt->vec[0][1]) { + bezt->vec[0][1] = prev->vec[1][1]; + } + } + } + } } - if (bezt->h2 == HD_AUTO) { + if (ELEM(bezt->h2, HD_AUTO, HD_AUTO_ANIM)) { len_b /= len; madd_v2_v2v2fl(p2_h2, p2, tvec, len_b); + + if ((bezt->h2 == HD_AUTO_ANIM) && next && prev) { /* keep horizontal if extrema */ + const float ydiff1 = prev->vec[1][1] - bezt->vec[1][1]; + const float ydiff2 = next->vec[1][1] - bezt->vec[1][1]; + if ((ydiff1 <= 0.0f && ydiff2 <= 0.0f)|| + (ydiff1 >= 0.0f && ydiff2 >= 0.0f)) + { + bezt->vec[2][1] = bezt->vec[1][1]; + } + else { /* handles should not be beyond y coord of two others */ + if (ydiff1 <= 0.0f) { + if (next->vec[1][1] < bezt->vec[2][1]) { + bezt->vec[2][1] = next->vec[1][1]; + } + } + else { + if (next->vec[1][1] > bezt->vec[2][1]) { + bezt->vec[2][1] = next->vec[1][1]; + } + } + } + } } } } @@ -540,10 +594,15 @@ static void curvemap_make_table(CurveMap *cuma, const rctf *clipr) cuma->maxtable = max_ff(cuma->maxtable, cmp[a].x); bezt[a].vec[1][0] = cmp[a].x; bezt[a].vec[1][1] = cmp[a].y; - if (cmp[a].flag & CUMA_VECTOR) + if (cmp[a].flag & CUMA_HANDLE_VECTOR) { bezt[a].h1 = bezt[a].h2 = HD_VECT; - else + } + else if (cmp[a].flag & CUMA_HANDLE_AUTO_ANIM) { + bezt[a].h1 = bezt[a].h2 = HD_AUTO_ANIM; + } + else { bezt[a].h1 = bezt[a].h2 = HD_AUTO; + } } const BezTriple *bezt_prev = NULL; @@ -773,12 +832,12 @@ void curvemapping_changed(CurveMapping *cumap, const bool rem_doubles) dy = cmp[a].y - cmp[a + 1].y; if (sqrtf(dx * dx + dy * dy) < thresh) { if (a == 0) { - cmp[a + 1].flag |= CUMA_VECTOR; + cmp[a + 1].flag |= CUMA_HANDLE_VECTOR; if (cmp[a + 1].flag & CUMA_SELECT) cmp[a].flag |= CUMA_SELECT; } else { - cmp[a].flag |= CUMA_VECTOR; + cmp[a].flag |= CUMA_HANDLE_VECTOR; if (cmp[a].flag & CUMA_SELECT) cmp[a + 1].flag |= CUMA_SELECT; } diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c index 4c9ddd495e3..70fdd4aa72e 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,73 @@ 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, false, userdata); +} + +static void transformcache_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *targets) +{ +#ifdef WITH_ALEMBIC + bTransformCacheConstraint *data = con->data; + Scene *scene = cob->scene; + + const float frame = BKE_scene_frame_get(scene); + const float time = BKE_cachefile_time_offset(data->cache_file, frame, FPS); + + CacheFile *cache_file = data->cache_file; + + 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 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 */ + NULL, /* 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 +4447,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 43612c877ea..f7268a91310 100644 --- a/source/blender/blenkernel/intern/context.c +++ b/source/blender/blenkernel/intern/context.c @@ -1066,6 +1066,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); @@ -1111,6 +1116,21 @@ bGPDlayer *CTX_data_active_gpencil_layer(const bContext *C) return ctx_data_pointer_get(C, "active_gpencil_layer"); } +bGPDpalette *CTX_data_active_gpencil_palette(const bContext *C) +{ + return ctx_data_pointer_get(C, "active_gpencil_palette"); +} + +bGPDpalettecolor *CTX_data_active_gpencil_palettecolor(const bContext *C) +{ + return ctx_data_pointer_get(C, "active_gpencil_palettecolor"); +} + +bGPDbrush *CTX_data_active_gpencil_brush(const bContext *C) +{ + return ctx_data_pointer_get(C, "active_gpencil_brush"); +} + bGPDframe *CTX_data_active_gpencil_frame(const bContext *C) { return ctx_data_pointer_get(C, "active_gpencil_frame"); diff --git a/source/blender/blenkernel/intern/depsgraph.c b/source/blender/blenkernel/intern/depsgraph.c index f06f7f55699..85ecf495805 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" @@ -2019,7 +2020,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) { @@ -2784,6 +2790,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) diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c index 395161aa6ed..a89d423e7a6 100644 --- a/source/blender/blenkernel/intern/fcurve.c +++ b/source/blender/blenkernel/intern/fcurve.c @@ -1859,7 +1859,7 @@ float driver_get_variable_value(ChannelDriver *driver, DriverVar *dvar) * - "evaltime" is the frame at which F-Curve is being evaluated * - has to return a float value */ -float evaluate_driver(ChannelDriver *driver, const float evaltime) +float evaluate_driver(PathResolvedRNA *anim_rna, ChannelDriver *driver, const float evaltime) { DriverVar *dvar; @@ -1944,7 +1944,9 @@ float evaluate_driver(ChannelDriver *driver, const float evaltime) * - on errors it reports, then returns 0.0f */ BLI_mutex_lock(&python_driver_lock); - driver->curval = BPY_driver_exec(driver, evaltime); + + driver->curval = BPY_driver_exec(anim_rna, driver, evaltime); + BLI_mutex_unlock(&python_driver_lock); } #else /* WITH_PYTHON*/ @@ -2599,25 +2601,64 @@ static float fcurve_eval_samples(FCurve *fcu, FPoint *fpts, float evaltime) /* Evaluate and return the value of the given F-Curve at the specified frame ("evaltime") * Note: this is also used for drivers */ -float evaluate_fcurve(FCurve *fcu, float evaltime) +static float evaluate_fcurve_ex(FCurve *fcu, float evaltime, float cvalue) { FModifierStackStorage *storage; - float cvalue = 0.0f; float devaltime; + + /* evaluate modifiers which modify time to evaluate the base curve at */ + storage = evaluate_fmodifiers_storage_new(&fcu->modifiers); + devaltime = evaluate_time_fmodifiers(storage, &fcu->modifiers, fcu, cvalue, evaltime); + + /* evaluate curve-data + * - 'devaltime' instead of 'evaltime', as this is the time that the last time-modifying + * F-Curve modifier on the stack requested the curve to be evaluated at + */ + if (fcu->bezt) + cvalue = fcurve_eval_keyframes(fcu, fcu->bezt, devaltime); + else if (fcu->fpt) + cvalue = fcurve_eval_samples(fcu, fcu->fpt, devaltime); + + /* evaluate modifiers */ + evaluate_value_fmodifiers(storage, &fcu->modifiers, fcu, &cvalue, devaltime); + + evaluate_fmodifiers_storage_free(storage); + + /* if curve can only have integral values, perform truncation (i.e. drop the decimal part) + * here so that the curve can be sampled correctly + */ + if (fcu->flag & FCURVE_INT_VALUES) + cvalue = floorf(cvalue + 0.5f); - /* if there is a driver (only if this F-Curve is acting as 'driver'), evaluate it to find value to use as "evaltime" + /* return evaluated value */ + return cvalue; +} + +float evaluate_fcurve(FCurve *fcu, float evaltime) +{ + BLI_assert(fcu->driver == NULL); + + return evaluate_fcurve_ex(fcu, evaltime, 0.0); +} + +float evaluate_fcurve_driver(PathResolvedRNA *anim_rna, FCurve *fcu, float evaltime) +{ + BLI_assert(fcu->driver != NULL); + float cvalue = 0.0f; + + /* if there is a driver (only if this F-Curve is acting as 'driver'), evaluate it to find value to use as "evaltime" * since drivers essentially act as alternative input (i.e. in place of 'time') for F-Curves */ if (fcu->driver) { /* evaltime now serves as input for the curve */ - evaltime = evaluate_driver(fcu->driver, evaltime); - + evaltime = evaluate_driver(anim_rna, fcu->driver, evaltime); + /* only do a default 1-1 mapping if it's unlikely that anything else will set a value... */ if (fcu->totvert == 0) { FModifier *fcm; bool do_linear = true; - - /* out-of-range F-Modifiers will block, as will those which just plain overwrite the values + + /* out-of-range F-Modifiers will block, as will those which just plain overwrite the values * XXX: additive is a bit more dicey; it really depends then if things are in range or not... */ for (fcm = fcu->modifiers.first; fcm; fcm = fcm->next) { @@ -2634,7 +2675,7 @@ float evaluate_fcurve(FCurve *fcu, float evaltime) do_linear = false; } } - + /* only copy over results if none of the modifiers disagreed with this */ if (do_linear) { cvalue = evaltime; @@ -2642,36 +2683,11 @@ float evaluate_fcurve(FCurve *fcu, float evaltime) } } - /* evaluate modifiers which modify time to evaluate the base curve at */ - storage = evaluate_fmodifiers_storage_new(&fcu->modifiers); - devaltime = evaluate_time_fmodifiers(storage, &fcu->modifiers, fcu, cvalue, evaltime); - - /* evaluate curve-data - * - 'devaltime' instead of 'evaltime', as this is the time that the last time-modifying - * F-Curve modifier on the stack requested the curve to be evaluated at - */ - if (fcu->bezt) - cvalue = fcurve_eval_keyframes(fcu, fcu->bezt, devaltime); - else if (fcu->fpt) - cvalue = fcurve_eval_samples(fcu, fcu->fpt, devaltime); - - /* evaluate modifiers */ - evaluate_value_fmodifiers(storage, &fcu->modifiers, fcu, &cvalue, devaltime); - - evaluate_fmodifiers_storage_free(storage); - - /* if curve can only have integral values, perform truncation (i.e. drop the decimal part) - * here so that the curve can be sampled correctly - */ - if (fcu->flag & FCURVE_INT_VALUES) - cvalue = floorf(cvalue + 0.5f); - - /* return evaluated value */ - return cvalue; + return evaluate_fcurve_ex(fcu, evaltime, cvalue); } /* Calculate the value of the given F-Curve at the given frame, and set its curval */ -float calculate_fcurve(FCurve *fcu, float evaltime) +float calculate_fcurve(PathResolvedRNA *anim_rna, FCurve *fcu, float evaltime) { /* only calculate + set curval (overriding the existing value) if curve has * any data which warrants this... @@ -2680,7 +2696,13 @@ float calculate_fcurve(FCurve *fcu, float evaltime) list_has_suitable_fmodifier(&fcu->modifiers, 0, FMI_TYPE_GENERATE_CURVE)) { /* calculate and set curval (evaluates driver too if necessary) */ - float curval = evaluate_fcurve(fcu, evaltime); + float curval; + if (fcu->driver) { + curval = evaluate_fcurve_driver(anim_rna, fcu, evaltime); + } + else { + curval = evaluate_fcurve(fcu, evaltime); + } fcu->curval = curval; /* debug display only, not thread safe! */ return curval; } diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c index 8621da0d42e..e4bac0a947a 100644 --- a/source/blender/blenkernel/intern/gpencil.c +++ b/source/blender/blenkernel/intern/gpencil.c @@ -18,7 +18,7 @@ * The Original Code is Copyright (C) 2008, Blender Foundation * This is a new part of Blender * - * Contributor(s): Joshua Leung + * Contributor(s): Joshua Leung, Antonio Vazquez * * ***** END GPL LICENSE BLOCK ***** */ @@ -44,10 +44,12 @@ #include "DNA_gpencil_types.h" #include "DNA_userdef_types.h" +#include "DNA_scene_types.h" #include "BKE_animsys.h" #include "BKE_global.h" #include "BKE_gpencil.h" +#include "BKE_colortools.h" #include "BKE_library.h" #include "BKE_main.h" @@ -57,76 +59,155 @@ /* --------- Memory Management ------------ */ +/* free stroke, doesn't unlink from any listbase */ +void BKE_gpencil_free_stroke(bGPDstroke *gps) +{ + if (gps == NULL) { + return; + } + + /* free stroke memory arrays, then stroke itself */ + if (gps->points) + MEM_freeN(gps->points); + if (gps->triangles) + MEM_freeN(gps->triangles); + + MEM_freeN(gps); +} + /* Free strokes belonging to a gp-frame */ -bool free_gpencil_strokes(bGPDframe *gpf) +bool BKE_gpencil_free_strokes(bGPDframe *gpf) { - bGPDstroke *gps, *gpsn; + bGPDstroke *gps_next; bool changed = (BLI_listbase_is_empty(&gpf->strokes) == false); /* free strokes */ - for (gps = gpf->strokes.first; gps; gps = gpsn) { - gpsn = gps->next; - - /* free stroke memory arrays, then stroke itself */ - if (gps->points) MEM_freeN(gps->points); - if (gps->triangles) MEM_freeN(gps->triangles); - BLI_freelinkN(&gpf->strokes, gps); + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps_next) { + gps_next = gps->next; + BKE_gpencil_free_stroke(gps); } + BLI_listbase_clear(&gpf->strokes); return changed; } /* Free all of a gp-layer's frames */ -void free_gpencil_frames(bGPDlayer *gpl) +void BKE_gpencil_free_frames(bGPDlayer *gpl) { - bGPDframe *gpf, *gpfn; + bGPDframe *gpf_next; /* error checking */ if (gpl == NULL) return; /* free frames */ - for (gpf = gpl->frames.first; gpf; gpf = gpfn) { - gpfn = gpf->next; + for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf_next) { + 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; } +/* Free all of a gp-colors */ +static void free_gpencil_colors(bGPDpalette *palette) +{ + /* error checking */ + if (palette == NULL) { + return; + } + + /* free colors */ + BLI_freelistN(&palette->colors); +} + +/* Free all of the gp-palettes and colors */ +void BKE_gpencil_free_palettes(ListBase *list) +{ + bGPDpalette *palette_next; + + /* error checking */ + if (list == NULL) { + return; + } + + /* delete palettes */ + for (bGPDpalette *palette = list->first; palette; palette = palette_next) { + palette_next = palette->next; + /* free palette colors */ + free_gpencil_colors(palette); + + MEM_freeN(palette); + } + BLI_listbase_clear(list); +} + +/* Free all of the gp-brushes for a viewport (list should be &gpd->brushes or so) */ +void BKE_gpencil_free_brushes(ListBase *list) +{ + bGPDbrush *brush_next; + + /* error checking */ + if (list == NULL) { + return; + } + + /* delete brushes */ + for (bGPDbrush *brush = list->first; brush; brush = brush_next) { + brush_next = brush->next; + /* free curves */ + if (brush->cur_sensitivity) { + curvemapping_free(brush->cur_sensitivity); + } + if (brush->cur_strength) { + curvemapping_free(brush->cur_strength); + } + if (brush->cur_jitter) { + curvemapping_free(brush->cur_jitter); + } + + MEM_freeN(brush); + } + BLI_listbase_clear(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, *gpln; + bGPDlayer *gpl_next; /* error checking */ if (list == NULL) return; /* delete layers */ - for (gpl = list->first; gpl; gpl = gpln) { - gpln = gpl->next; + for (bGPDlayer *gpl = list->first; gpl; gpl = gpl_next) { + gpl_next = gpl->next; /* free layers and their data */ - free_gpencil_frames(gpl); + BKE_gpencil_free_frames(gpl); BLI_freelinkN(list, gpl); } } -/* Free all of GPencil datablock's related data, but not the block itself */ /** Free (or release) any data used by this grease pencil (does not free the gpencil itself). */ -void BKE_gpencil_free(bGPdata *gpd) +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) { + 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; @@ -178,9 +259,9 @@ 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, *gpf; + bGPDframe *new_frame; bool found = false; /* Error checking/handling */ @@ -190,14 +271,14 @@ 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 (gpf = gpl->frames.first; gpf; gpf = gpf->next) { + for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { if (gpf->framenum > cframe) { /* Add it here */ BLI_insertlinkbefore(&gpl->frames, gpf, new_frame); @@ -209,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; @@ -233,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; @@ -249,8 +330,11 @@ bGPDlayer *gpencil_layer_addnew(bGPdata *gpd, const char *name, bool setactive) /* set basic settings */ copy_v4_v4(gpl->color, U.gpencil_new_layer_col); - gpl->thickness = 3; - + /* Since GPv2 thickness must be 0 */ + gpl->thickness = 0; + + gpl->opacity = 1.0f; + /* onion-skinning settings */ if (gpd->flag & GP_DATA_SHOW_ONIONSKINS) gpl->flag |= GP_LAYER_ONIONSKIN; @@ -263,23 +347,280 @@ bGPDlayer *gpencil_layer_addnew(bGPdata *gpd, const char *name, bool setactive) /* high quality fill by default */ gpl->flag |= GP_LAYER_HQ_FILL; - /* default smooth iterations */ - gpl->draw_smoothlvl = 1; - /* auto-name */ BLI_strncpy(gpl->info, name, sizeof(gpl->info)); BLI_uniquename(&gpd->layers, gpl, DATA_("GP_Layer"), '.', offsetof(bGPDlayer, info), sizeof(gpl->info)); /* 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 *BKE_gpencil_palette_addnew(bGPdata *gpd, const char *name, bool setactive) +{ + bGPDpalette *palette; + + /* check that list is ok */ + if (gpd == NULL) { + return NULL; + } + + /* allocate memory and add to end of list */ + palette = MEM_callocN(sizeof(bGPDpalette), "bGPDpalette"); + + /* add to datablock */ + BLI_addtail(&gpd->palettes, palette); + + /* set basic settings */ + /* auto-name */ + BLI_strncpy(palette->info, name, sizeof(palette->info)); + BLI_uniquename(&gpd->palettes, palette, DATA_("GP_Palette"), '.', offsetof(bGPDpalette, info), + sizeof(palette->info)); + + /* make this one the active one */ + if (setactive) { + BKE_gpencil_palette_setactive(gpd, palette); + } + + /* return palette */ + return palette; +} + +/* create a set of default drawing brushes with predefined presets */ +void BKE_gpencil_brush_init_presets(ToolSettings *ts) +{ + bGPDbrush *brush; + /* Basic brush */ + brush = BKE_gpencil_brush_addnew(ts, "Basic", true); + brush->thickness = 3.0f; + brush->flag &= ~GP_BRUSH_USE_RANDOM_PRESSURE; + brush->draw_sensitivity = 1.0f; + brush->flag |= GP_BRUSH_USE_PRESSURE; + + brush->flag &= ~GP_BRUSH_USE_RANDOM_STRENGTH; + brush->draw_strength = 1.0f; + brush->flag |= ~GP_BRUSH_USE_STENGTH_PRESSURE; + + brush->draw_random_press = 0.0f; + + brush->draw_jitter = 0.0f; + brush->flag |= GP_BRUSH_USE_JITTER_PRESSURE; + + brush->draw_angle = 0.0f; + brush->draw_angle_factor = 0.0f; + + brush->draw_smoothfac = 0.0f; + brush->draw_smoothlvl = 1; + brush->sublevel = 0; + brush->draw_random_sub = 0.0f; + + /* Pencil brush */ + brush = BKE_gpencil_brush_addnew(ts, "Pencil", false); + brush->thickness = 7.0f; + brush->flag &= ~GP_BRUSH_USE_RANDOM_PRESSURE; + brush->draw_sensitivity = 1.0f; + brush->flag |= GP_BRUSH_USE_PRESSURE; + + brush->flag &= ~GP_BRUSH_USE_RANDOM_STRENGTH; + brush->draw_strength = 0.7f; + brush->flag |= GP_BRUSH_USE_STENGTH_PRESSURE; + + brush->draw_random_press = 0.0f; + + brush->draw_jitter = 0.0f; + brush->flag |= GP_BRUSH_USE_JITTER_PRESSURE; + + brush->draw_angle = 0.0f; + brush->draw_angle_factor = 0.0f; + + brush->draw_smoothfac = 1.0f; + brush->draw_smoothlvl = 2; + brush->sublevel = 2; + brush->draw_random_sub = 0.0f; + + /* Ink brush */ + brush = BKE_gpencil_brush_addnew(ts, "Ink", false); + brush->thickness = 7.0f; + brush->flag &= ~GP_BRUSH_USE_RANDOM_PRESSURE; + brush->draw_sensitivity = 1.6f; + brush->flag |= GP_BRUSH_USE_PRESSURE; + + brush->flag &= ~GP_BRUSH_USE_RANDOM_STRENGTH; + brush->draw_strength = 1.0f; + brush->flag &= ~GP_BRUSH_USE_STENGTH_PRESSURE; + + brush->draw_random_press = 0.0f; + + brush->draw_jitter = 0.0f; + brush->flag |= GP_BRUSH_USE_JITTER_PRESSURE; + + brush->draw_angle = 0.0f; + brush->draw_angle_factor = 0.0f; + + brush->draw_smoothfac = 1.1f; + brush->draw_smoothlvl = 2; + brush->sublevel = 2; + brush->draw_random_sub = 0.0f; + + /* Ink Noise brush */ + 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; + brush->flag |= GP_BRUSH_USE_PRESSURE; + + brush->flag &= ~GP_BRUSH_USE_RANDOM_STRENGTH; + brush->draw_strength = 1.0f; + brush->flag |= GP_BRUSH_USE_STENGTH_PRESSURE; + + brush->draw_random_press = 1.0f; + + brush->draw_jitter = 0.0f; + brush->flag |= GP_BRUSH_USE_JITTER_PRESSURE; + + brush->draw_angle = 0.0f; + brush->draw_angle_factor = 0.0f; + + brush->draw_smoothfac = 1.1f; + brush->draw_smoothlvl = 2; + brush->sublevel = 2; + brush->draw_random_sub = 0.0f; + + /* Marker brush */ + brush = BKE_gpencil_brush_addnew(ts, "Marker", false); + brush->thickness = 10.0f; + brush->flag &= ~GP_BRUSH_USE_RANDOM_PRESSURE; + brush->draw_sensitivity = 2.0f; + brush->flag &= ~GP_BRUSH_USE_PRESSURE; + + brush->flag &= ~GP_BRUSH_USE_RANDOM_STRENGTH; + brush->draw_strength = 1.0f; + brush->flag &= ~GP_BRUSH_USE_STENGTH_PRESSURE; + + brush->draw_random_press = 0.0f; + + brush->draw_jitter = 0.0f; + brush->flag |= GP_BRUSH_USE_JITTER_PRESSURE; + + brush->draw_angle = M_PI_4; /* 45 degrees */ + brush->draw_angle_factor = 1.0f; + + brush->draw_smoothfac = 1.0f; + brush->draw_smoothlvl = 2; + brush->sublevel = 2; + brush->draw_random_sub = 0.0f; + + /* Crayon brush */ + brush = BKE_gpencil_brush_addnew(ts, "Crayon", false); + brush->thickness = 10.0f; + brush->flag &= ~GP_BRUSH_USE_RANDOM_PRESSURE; + brush->draw_sensitivity = 3.0f; + brush->flag &= ~GP_BRUSH_USE_PRESSURE; + + brush->flag &= ~GP_BRUSH_USE_RANDOM_STRENGTH; + brush->draw_strength = 0.140f; + brush->flag |= GP_BRUSH_USE_STENGTH_PRESSURE; + + brush->draw_random_press = 0.0f; + + brush->draw_jitter = 0.0f; + brush->flag |= GP_BRUSH_USE_JITTER_PRESSURE; + + brush->draw_angle = 0.0f; + brush->draw_angle_factor = 0.0f; + + brush->draw_smoothfac = 0.0f; + brush->draw_smoothlvl = 1; + brush->sublevel = 2; + brush->draw_random_sub = 0.5f; + +} + +/* add a new gp-brush and make it the active */ +bGPDbrush *BKE_gpencil_brush_addnew(ToolSettings *ts, const char *name, bool setactive) +{ + bGPDbrush *brush; + + /* check that list is ok */ + if (ts == NULL) { + return NULL; + } + + /* allocate memory and add to end of list */ + brush = MEM_callocN(sizeof(bGPDbrush), "bGPDbrush"); + + /* add to datablock */ + BLI_addtail(&ts->gp_brushes, brush); + + /* set basic settings */ + brush->thickness = 3; + brush->draw_smoothlvl = 1; + brush->flag |= GP_BRUSH_USE_PRESSURE; + brush->draw_sensitivity = 1.0f; + brush->draw_strength = 1.0f; + brush->flag |= GP_BRUSH_USE_STENGTH_PRESSURE; + brush->draw_jitter = 0.0f; + brush->flag |= GP_BRUSH_USE_JITTER_PRESSURE; + + /* curves */ + brush->cur_sensitivity = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); + brush->cur_strength = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); + brush->cur_jitter = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); + + /* auto-name */ + BLI_strncpy(brush->info, name, sizeof(brush->info)); + BLI_uniquename(&ts->gp_brushes, brush, DATA_("GP_Brush"), '.', offsetof(bGPDbrush, info), sizeof(brush->info)); + + /* make this one the active one */ + if (setactive) { + BKE_gpencil_brush_setactive(ts, brush); + } + + /* return brush */ + return brush; +} + +/* add a new gp-palettecolor and make it the active */ +bGPDpalettecolor *BKE_gpencil_palettecolor_addnew(bGPDpalette *palette, const char *name, bool setactive) +{ + bGPDpalettecolor *palcolor; + + /* check that list is ok */ + if (palette == NULL) { + return NULL; + } + + /* allocate memory and add to end of list */ + palcolor = MEM_callocN(sizeof(bGPDpalettecolor), "bGPDpalettecolor"); + + /* add to datablock */ + BLI_addtail(&palette->colors, palcolor); + + /* set basic settings */ + palcolor->flag |= PC_COLOR_HQ_FILL; + copy_v4_v4(palcolor->color, U.gpencil_new_layer_col); + ARRAY_SET_ITEMS(palcolor->fill, 1.0f, 1.0f, 1.0f); + + /* auto-name */ + BLI_strncpy(palcolor->info, name, sizeof(palcolor->info)); + BLI_uniquename(&palette->colors, palcolor, DATA_("Color"), '.', offsetof(bGPDpalettecolor, info), + sizeof(palcolor->info)); + + /* make this one the active one */ + if (setactive) { + BKE_gpencil_palettecolor_setactive(palette, palcolor); + } + + /* return palette color */ + return palcolor; +} + /* add a new gp-datablock */ -bGPdata *gpencil_data_addnew(const char name[]) +bGPdata *BKE_gpencil_data_addnew(const char name[]) { bGPdata *gpd; @@ -300,94 +641,157 @@ bGPdata *gpencil_data_addnew(const char name[]) /* -------- Data Duplication ---------- */ /* make a copy of a given gpencil frame */ -bGPDframe *gpencil_frame_duplicate(bGPDframe *src) +bGPDframe *BKE_gpencil_frame_duplicate(const bGPDframe *gpf_src) { - bGPDstroke *gps, *gpsd; - bGPDframe *dst; + bGPDstroke *gps_dst; + bGPDframe *gpf_dst; /* error checking */ - if (src == NULL) + if (gpf_src == NULL) { return NULL; + } /* make a copy of the source frame */ - dst = MEM_dupallocN(src); - dst->prev = dst->next = NULL; + gpf_dst = MEM_dupallocN(gpf_src); + gpf_dst->prev = gpf_dst->next = NULL; /* copy strokes */ - BLI_listbase_clear(&dst->strokes); - for (gps = src->strokes.first; gps; gps = gps->next) { + BLI_listbase_clear(&gpf_dst->strokes); + for (bGPDstroke *gps_src = gpf_src->strokes.first; gps_src; gps_src = gps_src->next) { /* make copy of source stroke, then adjust pointer to points too */ - gpsd = MEM_dupallocN(gps); - gpsd->points = MEM_dupallocN(gps->points); - gpsd->triangles = MEM_dupallocN(gps->triangles); - gpsd->flag |= GP_STROKE_RECALC_CACHES; - BLI_addtail(&dst->strokes, gpsd); + gps_dst = MEM_dupallocN(gps_src); + gps_dst->points = MEM_dupallocN(gps_src->points); + gps_dst->triangles = MEM_dupallocN(gps_src->triangles); + gps_dst->flag |= GP_STROKE_RECALC_CACHES; + BLI_addtail(&gpf_dst->strokes, gps_dst); } /* return new frame */ - return dst; + return gpf_dst; +} + +/* make a copy of a given gpencil brush */ +bGPDbrush *BKE_gpencil_brush_duplicate(const bGPDbrush *brush_src) +{ + bGPDbrush *brush_dst; + + /* error checking */ + if (brush_src == NULL) { + return NULL; + } + + /* make a copy of source brush */ + brush_dst = MEM_dupallocN(brush_src); + brush_dst->prev = brush_dst->next = NULL; + /* make a copy of curves */ + brush_dst->cur_sensitivity = curvemapping_copy(brush_src->cur_sensitivity); + brush_dst->cur_strength = curvemapping_copy(brush_src->cur_strength); + brush_dst->cur_jitter = curvemapping_copy(brush_src->cur_jitter); + + /* return new brush */ + return brush_dst; } +/* make a copy of a given gpencil palette */ +bGPDpalette *BKE_gpencil_palette_duplicate(const bGPDpalette *palette_src) +{ + bGPDpalette *palette_dst; + const bGPDpalettecolor *palcolor_src; + bGPDpalettecolor *palcolord_dst; + + /* error checking */ + if (palette_src == NULL) { + return NULL; + } + + /* make a copy of source palette */ + palette_dst = MEM_dupallocN(palette_src); + palette_dst->prev = palette_dst->next = NULL; + + /* copy colors */ + BLI_listbase_clear(&palette_dst->colors); + for (palcolor_src = palette_src->colors.first; palcolor_src; palcolor_src = palcolor_src->next) { + /* make a copy of source */ + palcolord_dst = MEM_dupallocN(palcolor_src); + BLI_addtail(&palette_dst->colors, palcolord_dst); + } + + /* return new palette */ + return palette_dst; +} /* make a copy of a given gpencil layer */ -bGPDlayer *gpencil_layer_duplicate(bGPDlayer *src) +bGPDlayer *BKE_gpencil_layer_duplicate(const bGPDlayer *gpl_src) { - bGPDframe *gpf, *gpfd; - bGPDlayer *dst; + const bGPDframe *gpf_src; + bGPDframe *gpf_dst; + bGPDlayer *gpl_dst; /* error checking */ - if (src == NULL) + if (gpl_src == NULL) { return NULL; + } /* make a copy of source layer */ - dst = MEM_dupallocN(src); - dst->prev = dst->next = NULL; + gpl_dst = MEM_dupallocN(gpl_src); + gpl_dst->prev = gpl_dst->next = NULL; /* copy frames */ - BLI_listbase_clear(&dst->frames); - for (gpf = src->frames.first; gpf; gpf = gpf->next) { + 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 */ - gpfd = gpencil_frame_duplicate(gpf); - BLI_addtail(&dst->frames, gpfd); + 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 */ - if (gpf == dst->actframe) - dst->actframe = gpfd; + if (gpf_src == gpl_dst->actframe) + gpl_dst->actframe = gpf_dst; } /* return new layer */ - return dst; + return gpl_dst; } /* make a copy of a given gpencil datablock */ -bGPdata *gpencil_data_duplicate(Main *bmain, bGPdata *src, bool internal_copy) +bGPdata *BKE_gpencil_data_duplicate(Main *bmain, bGPdata *gpd_src, bool internal_copy) { - bGPDlayer *gpl, *gpld; - bGPdata *dst; - + const bGPDlayer *gpl_src; + bGPDlayer *gpl_dst; + bGPdata *gpd_dst; + /* error checking */ - if (src == NULL) + if (gpd_src == NULL) { return NULL; + } /* make a copy of the base-data */ if (internal_copy) { /* make a straight copy for undo buffers used during stroke drawing */ - dst = MEM_dupallocN(src); + gpd_dst = MEM_dupallocN(gpd_src); } else { /* make a copy when others use this */ - dst = BKE_libblock_copy(bmain, &src->id); + gpd_dst = BKE_libblock_copy(bmain, &gpd_src->id); } /* copy layers */ - BLI_listbase_clear(&dst->layers); - for (gpl = src->layers.first; gpl; gpl = gpl->next) { + 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 */ - gpld = gpencil_layer_duplicate(gpl); - BLI_addtail(&dst->layers, gpld); + gpl_dst = BKE_gpencil_layer_duplicate(gpl_src); + BLI_addtail(&gpd_dst->layers, gpl_dst); + } + if (!internal_copy) { + /* copy palettes */ + 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 = BKE_gpencil_palette_duplicate(palette_src); + BLI_addtail(&gpd_dst->palettes, palette_dst); + } } /* return new */ - return dst; + return gpd_dst; } void BKE_gpencil_make_local(Main *bmain, bGPdata *gpd, const bool lib_local) @@ -398,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; @@ -423,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 */ @@ -439,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); } } @@ -458,7 +862,7 @@ bool gpencil_layer_is_editable(const bGPDlayer *gpl) /* Opacity must be sufficiently high that it is still "visible" * Otherwise, it's not really "visible" to the user, so no point editing... */ - if ((gpl->color[3] > GPENCIL_ALPHA_OPACITY_THRESH) || (gpl->fill[3] > GPENCIL_ALPHA_OPACITY_THRESH)) { + if (gpl->opacity > GPENCIL_ALPHA_OPACITY_THRESH) { return true; } } @@ -488,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; @@ -527,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; @@ -549,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; @@ -588,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; @@ -601,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 */ @@ -613,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; @@ -630,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; @@ -656,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; @@ -673,15 +1077,255 @@ 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 *BKE_gpencil_brush_getactive(ToolSettings *ts) +{ + bGPDbrush *brush; + + /* error checking */ + if (ELEM(NULL, ts, ts->gp_brushes.first)) { + return NULL; + } + + /* loop over brushes until found (assume only one active) */ + for (brush = ts->gp_brushes.first; brush; brush = brush->next) { + if (brush->flag & GP_BRUSH_ACTIVE) { + return brush; + } + } + + /* no active brush found */ + return NULL; +} + +/* set the active gp-brush */ +void BKE_gpencil_brush_setactive(ToolSettings *ts, bGPDbrush *active) +{ + bGPDbrush *brush; + + /* error checking */ + if (ELEM(NULL, ts, ts->gp_brushes.first, active)) { + return; + } + + /* loop over brushes deactivating all */ + for (brush = ts->gp_brushes.first; brush; brush = brush->next) { + brush->flag &= ~GP_BRUSH_ACTIVE; + } + + /* set as active one */ + active->flag |= GP_BRUSH_ACTIVE; +} + +/* delete the active gp-brush */ +void BKE_gpencil_brush_delete(ToolSettings *ts, bGPDbrush *brush) +{ + /* error checking */ + if (ELEM(NULL, ts, brush)) { + return; + } + + /* free curves */ + if (brush->cur_sensitivity) { + curvemapping_free(brush->cur_sensitivity); + } + if (brush->cur_strength) { + curvemapping_free(brush->cur_strength); + } + if (brush->cur_jitter) { + curvemapping_free(brush->cur_jitter); + } + + /* free */ + BLI_freelinkN(&ts->gp_brushes, brush); +} + +/* ************************************************** */ +/* get the active gp-palette for editing */ +bGPDpalette *BKE_gpencil_palette_getactive(bGPdata *gpd) +{ + bGPDpalette *palette; + + /* error checking */ + if (ELEM(NULL, gpd, gpd->palettes.first)) { + return NULL; + } + + /* loop over palettes until found (assume only one active) */ + for (palette = gpd->palettes.first; palette; palette = palette->next) { + if (palette->flag & PL_PALETTE_ACTIVE) + return palette; + } + + /* no active palette found */ + return NULL; +} + +/* set the active gp-palette */ +void BKE_gpencil_palette_setactive(bGPdata *gpd, bGPDpalette *active) +{ + bGPDpalette *palette; + + /* error checking */ + if (ELEM(NULL, gpd, gpd->palettes.first, active)) { + return; + } + + /* loop over palettes deactivating all */ + for (palette = gpd->palettes.first; palette; palette = palette->next) { + palette->flag &= ~PL_PALETTE_ACTIVE; + } + + /* set as active one */ + active->flag |= PL_PALETTE_ACTIVE; + /* force color recalc */ + BKE_gpencil_palette_change_strokes(gpd); +} + +/* delete the active gp-palette */ +void BKE_gpencil_palette_delete(bGPdata *gpd, bGPDpalette *palette) +{ + /* error checking */ + if (ELEM(NULL, gpd, palette)) { + return; + } + + /* free colors */ + free_gpencil_colors(palette); + BLI_freelinkN(&gpd->palettes, palette); + /* force color recalc */ + BKE_gpencil_palette_change_strokes(gpd); +} + +/* Set all strokes to recalc the palette color */ +void BKE_gpencil_palette_change_strokes(bGPdata *gpd) +{ + bGPDlayer *gpl; + bGPDframe *gpf; + bGPDstroke *gps; + + for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { + for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { + for (gps = gpf->strokes.first; gps; gps = gps->next) { + gps->flag |= GP_STROKE_RECALC_COLOR; + } + } + } +} + + +/* get the active gp-palettecolor for editing */ +bGPDpalettecolor *BKE_gpencil_palettecolor_getactive(bGPDpalette *palette) +{ + bGPDpalettecolor *palcolor; + + /* error checking */ + if (ELEM(NULL, palette, palette->colors.first)) { + return NULL; + } + + /* loop over colors until found (assume only one active) */ + for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { + if (palcolor->flag & PC_COLOR_ACTIVE) { + return palcolor; + } + } + + /* no active color found */ + return NULL; +} +/* get the gp-palettecolor looking for name */ +bGPDpalettecolor *BKE_gpencil_palettecolor_getbyname(bGPDpalette *palette, char *name) +{ + /* error checking */ + if (ELEM(NULL, palette, name)) { + return NULL; + } + + return BLI_findstring(&palette->colors, name, offsetof(bGPDpalettecolor, info)); +} + +/* Change color name in all strokes */ +void BKE_gpencil_palettecolor_changename(bGPdata *gpd, char *oldname, const char *newname) +{ + bGPDlayer *gpl; + bGPDframe *gpf; + bGPDstroke *gps; + + for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { + 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); + } + } + } + } + +} + +/* Delete all strokes of the color */ +void BKE_gpencil_palettecolor_delete_strokes(struct bGPdata *gpd, char *name) +{ + bGPDlayer *gpl; + bGPDframe *gpf; + bGPDstroke *gps, *gpsn; + + for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { + for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { + for (gps = gpf->strokes.first; gps; gps = gpsn) { + gpsn = gps->next; + + if (STREQ(gps->colorname, name)) { + if (gps->points) MEM_freeN(gps->points); + if (gps->triangles) MEM_freeN(gps->triangles); + BLI_freelinkN(&gpf->strokes, gps); + } + } + } + } + +} + +/* set the active gp-palettecolor */ +void BKE_gpencil_palettecolor_setactive(bGPDpalette *palette, bGPDpalettecolor *active) +{ + bGPDpalettecolor *palcolor; + + /* error checking */ + if (ELEM(NULL, palette, palette->colors.first, active)) { + return; + } + + /* loop over colors deactivating all */ + for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { + palcolor->flag &= ~PC_COLOR_ACTIVE; + } + + /* set as active one */ + active->flag |= PC_COLOR_ACTIVE; +} + +/* delete the active gp-palettecolor */ +void BKE_gpencil_palettecolor_delete(bGPDpalette *palette, bGPDpalettecolor *palcolor) +{ + /* error checking */ + if (ELEM(NULL, palette, palcolor)) { + return; + } + + /* free */ + BLI_freelinkN(&palette->colors, palcolor); +} diff --git a/source/blender/blenkernel/intern/idcode.c b/source/blender/blenkernel/intern/idcode.c index c7a346d49ec..ddc4fb37848 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 }, @@ -183,6 +184,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); @@ -225,6 +227,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); diff --git a/source/blender/blenkernel/intern/idprop.c b/source/blender/blenkernel/intern/idprop.c index 86d010f5f7c..b2641b110f8 100644 --- a/source/blender/blenkernel/intern/idprop.c +++ b/source/blender/blenkernel/intern/idprop.c @@ -940,17 +940,18 @@ IDProperty *IDP_New(const char type, const IDPropertyTemplate *val, const char * prop->subtype = IDP_STRING_SUB_BYTE; } else { - if (st == NULL) { + if (st == NULL || val->string.len <= 1) { prop->data.pointer = MEM_mallocN(DEFAULT_ALLOC_FOR_NULL_STRINGS, "id property string 1"); *IDP_String(prop) = '\0'; prop->totallen = DEFAULT_ALLOC_FOR_NULL_STRINGS; prop->len = 1; /*NULL string, has len of 1 to account for null byte.*/ } else { - int stlen = (int)strlen(st) + 1; - prop->data.pointer = MEM_mallocN((size_t)stlen, "id property string 3"); - prop->len = prop->totallen = stlen; - memcpy(prop->data.pointer, st, (size_t)stlen); + BLI_assert((int)val->string.len <= (int)strlen(st) + 1); + prop->data.pointer = MEM_mallocN((size_t)val->string.len, "id property string 3"); + memcpy(prop->data.pointer, st, (size_t)val->string.len - 1); + IDP_String(prop)[val->string.len - 1] = '\0'; + prop->len = prop->totallen = val->string.len; } prop->subtype = IDP_STRING_SUB_UTF8; } diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index ea28dabb945..626d389ac2d 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)); diff --git a/source/blender/blenkernel/intern/library.c b/source/blender/blenkernel/intern/library.c index 558c3bb0326..933622eed83 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" @@ -103,8 +106,10 @@ #include "BKE_main.h" #include "BKE_mball.h" #include "BKE_mask.h" +#include "BKE_movieclip.h" #include "BKE_node.h" #include "BKE_object.h" +#include "BKE_paint.h" #include "BKE_packedFile.h" #include "BKE_sound.h" #include "BKE_speaker.h" @@ -318,7 +323,6 @@ void BKE_id_make_local_generic(Main *bmain, ID *id, const bool id_in_mainlist, c } } } - } /** @@ -330,16 +334,16 @@ 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 (GS(id->name)) { + switch ((ID_Type)GS(id->name)) { case ID_SCE: /* Partially implemented (has no copy...). */ if (!test) BKE_scene_make_local(bmain, (Scene *)id, lib_local); return true; - case ID_LI: - return false; /* can't be linked */ case ID_OB: if (!test) BKE_object_make_local(bmain, (Object *)id, lib_local); return true; @@ -373,15 +377,9 @@ bool id_make_local(Main *bmain, ID *id, const bool test, const bool lib_local) case ID_SPK: if (!test) BKE_speaker_make_local(bmain, (Speaker *)id, lib_local); return true; - case ID_IP: - return false; /* deprecated */ - case ID_KE: - return false; /* can't be linked */ case ID_WO: if (!test) BKE_world_make_local(bmain, (World *)id, lib_local); return true; - case ID_SCR: - return false; /* can't be linked */ case ID_VF: /* Partially implemented (has no copy...). */ if (!test) BKE_vfont_make_local(bmain, (VFont *)id, lib_local); @@ -408,17 +406,34 @@ bool id_make_local(Main *bmain, ID *id, const bool test, const bool lib_local) case ID_BR: if (!test) BKE_brush_make_local(bmain, (Brush *)id, lib_local); return true; - case ID_WM: - return false; /* can't be linked */ case ID_GD: if (!test) BKE_gpencil_make_local(bmain, (bGPdata *)id, lib_local); return true; + case ID_MC: + if (!test) BKE_movieclip_make_local(bmain, (MovieClip *)id, lib_local); + return true; case ID_MSK: if (!test) BKE_mask_make_local(bmain, (Mask *)id, lib_local); return true; case ID_LS: if (!test) BKE_linestyle_make_local(bmain, (FreestyleLineStyle *)id, lib_local); return true; + case ID_PAL: + if (!test) BKE_palette_make_local(bmain, (Palette *)id, lib_local); + return true; + 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: + case ID_WM: + return false; /* can't be linked */ + case ID_IP: + return false; /* deprecated */ } return false; @@ -437,11 +452,7 @@ bool id_copy(Main *bmain, ID *id, ID **newid, bool test) /* conventions: * - make shallow copy, only this ID block * - id.us of the new ID is set to 1 */ - switch (GS(id->name)) { - case ID_SCE: - return false; /* can't be copied from here */ - case ID_LI: - return false; /* can't be copied from here */ + switch ((ID_Type)GS(id->name)) { case ID_OB: if (!test) *newid = (ID *)BKE_object_copy(bmain, (Object *)id); return true; @@ -475,23 +486,15 @@ bool id_copy(Main *bmain, ID *id, ID **newid, bool test) case ID_CA: if (!test) *newid = (ID *)BKE_camera_copy(bmain, (Camera *)id); return true; - case ID_IP: - return false; /* deprecated */ case ID_KE: if (!test) *newid = (ID *)BKE_key_copy(bmain, (Key *)id); return true; case ID_WO: if (!test) *newid = (ID *)BKE_world_copy(bmain, (World *)id); return true; - case ID_SCR: - return false; /* can't be copied from here */ - case ID_VF: - return false; /* not implemented */ case ID_TXT: if (!test) *newid = (ID *)BKE_text_copy(bmain, (Text *)id); return true; - case ID_SO: - return false; /* not implemented */ case ID_GR: if (!test) *newid = (ID *)BKE_group_copy(bmain, (Group *)id); return true; @@ -507,10 +510,11 @@ bool id_copy(Main *bmain, ID *id, ID **newid, bool test) case ID_BR: if (!test) *newid = (ID *)BKE_brush_copy(bmain, (Brush *)id); return true; - case ID_WM: - return false; /* can't be copied from here */ 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); return true; case ID_MSK: if (!test) *newid = (ID *)BKE_mask_copy(bmain, (Mask *)id); @@ -518,6 +522,25 @@ bool id_copy(Main *bmain, ID *id, ID **newid, bool test) case ID_LS: if (!test) *newid = (ID *)BKE_linestyle_copy(bmain, (FreestyleLineStyle *)id); return true; + case ID_PAL: + if (!test) *newid = (ID *)BKE_palette_copy(bmain, (Palette *)id); + return true; + 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: + case ID_WM: + return false; /* can't be copied from here */ + case ID_VF: + case ID_SO: + return false; /* not implemented */ + case ID_IP: + return false; /* deprecated */ } return false; @@ -553,7 +576,7 @@ bool id_single_user(bContext *C, ID *id, PointerRNA *ptr, PropertyRNA *prop) ListBase *which_libbase(Main *mainlib, short type) { - switch (type) { + switch ((ID_Type)type) { case ID_SCE: return &(mainlib->scene); case ID_LI: @@ -618,6 +641,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; } @@ -741,6 +766,7 @@ int set_listbasepointers(Main *main, ListBase **lb) lb[a++] = &(main->armature); + lb[a++] = &(main->cachefiles); lb[a++] = &(main->mesh); lb[a++] = &(main->curve); lb[a++] = &(main->mball); @@ -791,7 +817,7 @@ void *BKE_libblock_alloc_notest(short type) { ID *id = NULL; - switch (type) { + switch ((ID_Type)type) { case ID_SCE: id = MEM_callocN(sizeof(Scene), "scene"); break; @@ -888,6 +914,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; } @@ -925,7 +954,7 @@ void *BKE_libblock_alloc(Main *bmain, short type, const char *name) void BKE_libblock_init_empty(ID *id) { /* Note that only ID types that are not valid when filled of zero should have a callback here. */ - switch (GS(id->name)) { + switch ((ID_Type)GS(id->name)) { case ID_SCE: BKE_scene_init((Scene *)id); break; @@ -969,15 +998,6 @@ void BKE_libblock_init_empty(ID *id) case ID_CA: BKE_camera_init((Camera *)id); break; - case ID_IP: - /* Should not be needed - animation from lib pre-2.5 is broken anyway. */ - BLI_assert(0); - 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. */ - BLI_assert(0); - break; case ID_WO: BKE_world_init((World *)id); break; @@ -1011,10 +1031,6 @@ void BKE_libblock_init_empty(ID *id) case ID_PC: /* Nothing to do. */ break; - case ID_WM: - /* We should never reach this. */ - BLI_assert(0); - break; case ID_GD: /* Nothing to do. */ break; @@ -1024,6 +1040,24 @@ 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. */ + BLI_assert(0); + break; + case ID_WM: + /* We should never reach this. */ + BLI_assert(0); + break; + case ID_IP: + /* Should not be needed - animation from lib pre-2.5 is broken anyway. */ + BLI_assert(0); + break; + default: + BLI_assert(0); /* Should never reach this point... */ } } @@ -1607,7 +1641,13 @@ void BKE_library_make_local(Main *bmain, const Library *lib, const bool untagged int a; for (a = set_listbasepointers(bmain, lbarray); a--; ) { - for (id = lbarray[a]->first; id; id = id_next) { + id = lbarray[a]->first; + + /* 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 */ @@ -1616,7 +1656,7 @@ void BKE_library_make_local(Main *bmain, const Library *lib, const bool untagged * 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 17c90286a5b..eb71a42fb77 100644 --- a/source/blender/blenkernel/intern/library_query.c +++ b/source/blender/blenkernel/intern/library_query.c @@ -295,7 +295,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; @@ -480,6 +480,14 @@ void BKE_library_foreach_ID_link(ID *id, LibraryIDLinkCallback callback, void *u modifiers_foreachIDLink(object, library_foreach_modifiersForeachIDLink, &data); BKE_constraints_id_loop(&object->constraints, library_foreach_constraintObjectLooper, &data); + if (object->soft) { + CALLBACK_INVOKE(object->soft->collision_group, IDWALK_NOP); + + if (object->soft->effector_weights) { + CALLBACK_INVOKE(object->soft->effector_weights->group, IDWALK_NOP); + } + } + BKE_sca_sensors_id_loop(&object->sensors, library_foreach_sensorsObjectLooper, &data); BKE_sca_controllers_id_loop(&object->controllers, library_foreach_controllersObjectLooper, &data); BKE_sca_actuators_id_loop(&object->actuators, library_foreach_actuatorsObjectLooper, &data); @@ -616,6 +624,10 @@ void BKE_library_foreach_ID_link(ID *id, LibraryIDLinkCallback callback, void *u case ID_KE: { + /* XXX Only ID pointer from shapekeys is the 'from' one, which is not actually ID usage. + * Maybe we should even nuke it from here, not 100% sure yet... + * (see also foreach_libblock_id_users_callback). + */ Key *key = (Key *) id; CALLBACK_INVOKE_ID(key->from, IDWALK_NOP); break; @@ -764,6 +776,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)); @@ -880,10 +911,18 @@ typedef struct IDUsersIter { int count_direct, count_indirect; /* Set by callback. */ } IDUsersIter; -static int foreach_libblock_id_users_callback(void *user_data, ID *UNUSED(self_id), ID **id_p, int cb_flag) +static int foreach_libblock_id_users_callback(void *user_data, ID *self_id, ID **id_p, int cb_flag) { IDUsersIter *iter = user_data; + /* XXX This is actually some kind of hack... + * Issue is, only ID pointer from shapekeys is the 'from' one, which is not actually ID usage. + * Maybe we should even nuke it from BKE_library_foreach_ID_link, not 100% sure yet... + */ + if (GS(self_id->name) == ID_KE) { + return IDWALK_RET_NOP; + } + if (*id_p && (*id_p == iter->id)) { #if 0 printf("%s uses %s (refcounted: %d, userone: %d, used_one: %d, used_one_active: %d, indirect_usage: %d)\n", diff --git a/source/blender/blenkernel/intern/library_remap.c b/source/blender/blenkernel/intern/library_remap.c index 92652fcc1eb..4afce3b5f85 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" @@ -791,7 +793,7 @@ void BKE_libblock_free_ex(Main *bmain, void *idv, const bool do_id_user) free_windowmanager_cb(NULL, (wmWindowManager *)id); break; case ID_GD: - BKE_gpencil_free((bGPdata *)id); + BKE_gpencil_free((bGPdata *)id, true); break; case ID_MC: BKE_movieclip_free((MovieClip *)id); @@ -808,6 +810,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 */ diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c index 0be32c9b84c..470108545b8 100644 --- a/source/blender/blenkernel/intern/material.c +++ b/source/blender/blenkernel/intern/material.c @@ -742,17 +742,18 @@ void assign_material(Object *ob, Material *ma, short act, int assign_type) if (mao) id_us_min(&mao->id); ob->mat[act - 1] = ma; + test_object_materials(ob, ob->data); } else { /* in data */ mao = (*matarar)[act - 1]; if (mao) id_us_min(&mao->id); (*matarar)[act - 1] = ma; + test_all_objects_materials(G.main, ob->data); /* Data may be used by several objects... */ } if (ma) id_us_plus(&ma->id); - test_object_materials(ob, ob->data); } diff --git a/source/blender/blenkernel/intern/mesh_evaluate.c b/source/blender/blenkernel/intern/mesh_evaluate.c index 1c86fbcfe8e..fa113ef5eef 100644 --- a/source/blender/blenkernel/intern/mesh_evaluate.c +++ b/source/blender/blenkernel/intern/mesh_evaluate.c @@ -434,7 +434,7 @@ MLoopNorSpace *BKE_lnor_space_create(MLoopNorSpaceArray *lnors_spacearr) } /* This threshold is a bit touchy (usual float precision issue), this value seems OK. */ -#define LNOR_SPACE_TRIGO_THRESHOLD (1.0f - 1e-6f) +#define LNOR_SPACE_TRIGO_THRESHOLD (1.0f - 1e-4f) /* Should only be called once. * Beware, this modifies ref_vec and other_vec in place! diff --git a/source/blender/blenkernel/intern/movieclip.c b/source/blender/blenkernel/intern/movieclip.c index f99457a4c26..0d362086134 100644 --- a/source/blender/blenkernel/intern/movieclip.c +++ b/source/blender/blenkernel/intern/movieclip.c @@ -1491,6 +1491,32 @@ void BKE_movieclip_free(MovieClip *clip) BKE_tracking_free(&clip->tracking); } +MovieClip *BKE_movieclip_copy(Main *bmain, MovieClip *clip) +{ + MovieClip *clip_new; + + clip_new = BKE_libblock_copy(bmain, &clip->id); + + clip_new->anim = NULL; + clip_new->cache = NULL; + + BKE_tracking_copy(&clip_new->tracking, &clip->tracking); + clip_new->tracking_context = NULL; + + id_us_plus((ID *)clip_new->gpd); + + BKE_color_managed_colorspace_settings_copy(&clip_new->colorspace_settings, &clip->colorspace_settings); + + BKE_id_copy_ensure_local(bmain, &clip->id, &clip_new->id); + + return clip_new; +} + +void BKE_movieclip_make_local(Main *bmain, MovieClip *clip, const bool lib_local) +{ + BKE_id_make_local_generic(bmain, &clip->id, true, lib_local); +} + float BKE_movieclip_remap_scene_to_clip_frame(MovieClip *clip, float framenr) { return framenr - (float) clip->start_frame + 1.0f; diff --git a/source/blender/blenkernel/intern/object_dupli.c b/source/blender/blenkernel/intern/object_dupli.c index 4716f8dc40a..75f20c7e1d3 100644 --- a/source/blender/blenkernel/intern/object_dupli.c +++ b/source/blender/blenkernel/intern/object_dupli.c @@ -80,7 +80,6 @@ typedef struct DupliContext { int persistent_id[MAX_DUPLI_RECUR]; int level; - int index; const struct DupliGenerator *gen; diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c index 8c1502643c5..53675f33a69 100644 --- a/source/blender/blenkernel/intern/paint.c +++ b/source/blender/blenkernel/intern/paint.c @@ -314,6 +314,26 @@ PaintCurve *BKE_paint_curve_add(Main *bmain, const char *name) return pc; } +PaintCurve *BKE_paint_curve_copy(Main *bmain, PaintCurve *pc) +{ + PaintCurve *pc_new; + + pc_new = BKE_libblock_copy(bmain, &pc->id); + + if (pc->tot_points != 0) { + pc_new->points = MEM_dupallocN(pc->points); + } + + BKE_id_copy_ensure_local(bmain, &pc->id, &pc_new->id); + + return pc_new; +} + +void BKE_paint_curve_make_local(Main *bmain, PaintCurve *pc, const bool lib_local) +{ + BKE_id_make_local_generic(bmain, &pc->id, true, lib_local); +} + Palette *BKE_paint_palette(Paint *p) { return p ? p->palette : NULL; @@ -376,6 +396,24 @@ Palette *BKE_palette_add(Main *bmain, const char *name) return palette; } +Palette *BKE_palette_copy(Main *bmain, Palette *palette) +{ + Palette *palette_new; + + palette_new = BKE_libblock_copy(bmain, &palette->id); + + BLI_duplicatelist(&palette_new->colors, &palette->colors); + + BKE_id_copy_ensure_local(bmain, &palette->id, &palette_new->id); + + return palette_new; +} + +void BKE_palette_make_local(Main *bmain, Palette *palette, bool lib_local) +{ + BKE_id_make_local_generic(bmain, &palette->id, true, lib_local); +} + /** Free (or release) any data used by this palette (does not free the palette itself). */ void BKE_palette_free(Palette *palette) { diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index a75522d5573..c3e34347e55 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -49,6 +49,7 @@ #include "DNA_space_types.h" #include "DNA_view3d_types.h" #include "DNA_windowmanager_types.h" +#include "DNA_gpencil_types.h" #include "BLI_math.h" #include "BLI_blenlib.h" @@ -64,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" @@ -285,6 +287,13 @@ Scene *BKE_scene_copy(Main *bmain, Scene *sce, int type) BKE_paint_copy(&ts->imapaint.paint, &ts->imapaint.paint); ts->imapaint.paintcursor = NULL; id_us_plus((ID *)ts->imapaint.stencil); + /* 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 = BKE_gpencil_brush_duplicate(brush); + BLI_addtail(&ts->gp_brushes, newbrush); + } + } /* make a private copy of the avicodecdata */ @@ -333,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; @@ -431,6 +440,10 @@ void BKE_scene_free(Scene *sce) BKE_paint_free(&sce->toolsettings->uvsculpt->paint); MEM_freeN(sce->toolsettings->uvsculpt); } + /* free Grease Pencil Drawing Brushes */ + BKE_gpencil_free_brushes(&sce->toolsettings->gp_brushes); + BLI_freelistN(&sce->toolsettings->gp_brushes); + BKE_paint_free(&sce->toolsettings->imapaint.paint); MEM_freeN(sce->toolsettings); @@ -744,6 +757,11 @@ void BKE_scene_init(Scene *sce) gp_brush->strength = 0.5f; gp_brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF; + gp_brush = &gset->brush[GP_EDITBRUSH_TYPE_STRENGTH]; + gp_brush->size = 25; + gp_brush->strength = 0.5f; + gp_brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF; + gp_brush = &gset->brush[GP_EDITBRUSH_TYPE_GRAB]; gp_brush->size = 50; gp_brush->strength = 0.3f; @@ -1889,6 +1907,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/smoke.c b/source/blender/blenkernel/intern/smoke.c index 8e7b66ef183..98bea27ecb9 100644 --- a/source/blender/blenkernel/intern/smoke.c +++ b/source/blender/blenkernel/intern/smoke.c @@ -691,6 +691,7 @@ typedef struct ObstaclesFromDMData { bool has_velocity; float *vert_vel; float *velocityX, *velocityY, *velocityZ; + int *num_obstacles; } ObstaclesFromDMData; static void obstacles_from_derivedmesh_task_cb(void *userdata, const int z) @@ -739,8 +740,10 @@ static void obstacles_from_derivedmesh_task_cb(void *userdata, const int z) /* tag obstacle cells */ data->obstacle_map[index] = 1; - if (data->has_velocity) + if (data->has_velocity) { data->obstacle_map[index] |= 8; + data->num_obstacles[index]++; + } } } } @@ -748,7 +751,7 @@ static void obstacles_from_derivedmesh_task_cb(void *userdata, const int z) static void obstacles_from_derivedmesh( Object *coll_ob, SmokeDomainSettings *sds, SmokeCollSettings *scs, - unsigned char *obstacle_map, float *velocityX, float *velocityY, float *velocityZ, float dt) + unsigned char *obstacle_map, float *velocityX, float *velocityY, float *velocityZ, int *num_obstacles, float dt) { if (!scs->dm) return; { @@ -819,7 +822,8 @@ static void obstacles_from_derivedmesh( .sds = sds, .mvert = mvert, .mloop = mloop, .looptri = looptri, .tree = &treeData, .obstacle_map = obstacle_map, .has_velocity = has_velocity, .vert_vel = vert_vel, - .velocityX = velocityX, .velocityY = velocityY, .velocityZ = velocityZ + .velocityX = velocityX, .velocityY = velocityY, .velocityZ = velocityZ, + .num_obstacles = num_obstacles }; BLI_task_parallel_range( sds->res_min[2], sds->res_max[2], &data, obstacles_from_derivedmesh_task_cb, true); @@ -855,6 +859,8 @@ static void update_obstacles(Scene *scene, Object *ob, SmokeDomainSettings *sds, float *b = smoke_get_color_b(sds->fluid); unsigned int z; + int *num_obstacles = MEM_callocN(sizeof(int) * sds->res[0] * sds->res[1] * sds->res[2], "smoke_num_obstacles"); + smoke_get_ob_velocity(sds->fluid, &velx, &vely, &velz); // TODO: delete old obstacle flags @@ -884,7 +890,7 @@ static void update_obstacles(Scene *scene, Object *ob, SmokeDomainSettings *sds, if ((smd2->type & MOD_SMOKE_TYPE_COLL) && smd2->coll) { SmokeCollSettings *scs = smd2->coll; - obstacles_from_derivedmesh(collob, sds, scs, obstacles, velx, vely, velz, dt); + obstacles_from_derivedmesh(collob, sds, scs, obstacles, velx, vely, velz, num_obstacles, dt); } } @@ -910,7 +916,15 @@ static void update_obstacles(Scene *scene, Object *ob, SmokeDomainSettings *sds, b[z] = 0; } } + /* average velocities from multiple obstacles in one cell */ + if (num_obstacles[z]) { + velx[z] /= num_obstacles[z]; + vely[z] /= num_obstacles[z]; + velz[z] /= num_obstacles[z]; + } } + + MEM_freeN(num_obstacles); } /********************************************************** diff --git a/source/blender/blenkernel/intern/softbody.c b/source/blender/blenkernel/intern/softbody.c index fc3daa132c6..f4986f7daba 100644 --- a/source/blender/blenkernel/intern/softbody.c +++ b/source/blender/blenkernel/intern/softbody.c @@ -64,6 +64,7 @@ variables on the UI for now #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_object_force.h" +#include "DNA_group_types.h" #include "BLI_math.h" #include "BLI_utildefines.h" @@ -496,59 +497,98 @@ static void ccd_mesh_free(ccd_Mesh *ccdm) } } -static void ccd_build_deflector_hash(Scene *scene, Object *vertexowner, GHash *hash) +static void ccd_build_deflector_hash_single(GHash *hash, Object *ob) +{ + /* only with deflecting set */ + if (ob->pd && ob->pd->deflect) { + void **val_p; + if (!BLI_ghash_ensure_p(hash, ob, &val_p)) { + ccd_Mesh *ccdmesh = ccd_mesh_make(ob); + *val_p = ccdmesh; + } + } +} + +/** + * \note group overrides scene when not NULL. + */ +static void ccd_build_deflector_hash(Scene *scene, Group *group, Object *vertexowner, GHash *hash) { - Base *base= scene->base.first; Object *ob; if (!hash) return; - while (base) { - /*Only proceed for mesh object in same layer */ - if (base->object->type==OB_MESH && (base->lay & vertexowner->lay)) { - ob= base->object; - if ((vertexowner) && (ob == vertexowner)) { - /* if vertexowner is given we don't want to check collision with owner object */ - base = base->next; + + if (group) { + /* Explicit collision group */ + for (GroupObject *go = group->gobject.first; go; go = go->next) { + ob = go->ob; + + if (ob == vertexowner || ob->type != OB_MESH) continue; - } - /*+++ only with deflecting set */ - if (ob->pd && ob->pd->deflect && BLI_ghash_lookup(hash, ob) == NULL) { - ccd_Mesh *ccdmesh = ccd_mesh_make(ob); - BLI_ghash_insert(hash, ob, ccdmesh); - }/*--- only with deflecting set */ + ccd_build_deflector_hash_single(hash, ob); + } + } + else { + for (Base *base = scene->base.first; base; base = base->next) { + /*Only proceed for mesh object in same layer */ + if (base->object->type == OB_MESH && (base->lay & vertexowner->lay)) { + ob= base->object; + if ((vertexowner) && (ob == vertexowner)) { + /* if vertexowner is given we don't want to check collision with owner object */ + continue; + } - }/* mesh && layer*/ - base = base->next; - } /* while (base) */ + ccd_build_deflector_hash_single(hash, ob); + } + } + } } -static void ccd_update_deflector_hash(Scene *scene, Object *vertexowner, GHash *hash) +static void ccd_update_deflector_hash_single(GHash *hash, Object *ob) +{ + if (ob->pd && ob->pd->deflect) { + ccd_Mesh *ccdmesh = BLI_ghash_lookup(hash, ob); + if (ccdmesh) { + ccd_mesh_update(ob, ccdmesh); + } + } +} + +/** + * \note group overrides scene when not NULL. + */ +static void ccd_update_deflector_hash(Scene *scene, Group *group, Object *vertexowner, GHash *hash) { - Base *base= scene->base.first; Object *ob; if ((!hash) || (!vertexowner)) return; - while (base) { - /*Only proceed for mesh object in same layer */ - if (base->object->type==OB_MESH && (base->lay & vertexowner->lay)) { - ob= base->object; - if (ob == vertexowner) { - /* if vertexowner is given we don't want to check collision with owner object */ - base = base->next; + + if (group) { + /* Explicit collision group */ + for (GroupObject *go = group->gobject.first; go; go = go->next) { + ob = go->ob; + + if (ob == vertexowner || ob->type != OB_MESH) continue; - } - /*+++ only with deflecting set */ - if (ob->pd && ob->pd->deflect) { - ccd_Mesh *ccdmesh = BLI_ghash_lookup(hash, ob); - if (ccdmesh) - ccd_mesh_update(ob, ccdmesh); - }/*--- only with deflecting set */ + ccd_update_deflector_hash_single(hash, ob); + } + } + else { + for (Base *base = scene->base.first; base; base = base->next) { + /*Only proceed for mesh object in same layer */ + if (base->object->type == OB_MESH && (base->lay & vertexowner->lay)) { + ob= base->object; + if (ob == vertexowner) { + /* if vertexowner is given we don't want to check collision with owner object */ + continue; + } - }/* mesh && layer*/ - base = base->next; - } /* while (base) */ + ccd_update_deflector_hash_single(hash, ob); + } + } + } } @@ -934,22 +974,32 @@ static void free_softbody_intern(SoftBody *sb) /* +++ dependency information functions*/ -static int are_there_deflectors(Scene *scene, unsigned int layer) +/** + * \note group overrides scene when not NULL. + */ +static bool are_there_deflectors(Scene *scene, Group *group, unsigned int layer) { - Base *base; - - for (base = scene->base.first; base; base= base->next) { - if ( (base->lay & layer) && base->object->pd) { - if (base->object->pd->deflect) + if (group) { + for (GroupObject *go = group->gobject.first; go; go = go->next) { + if (go->ob->pd && go->ob->pd->deflect) return 1; } } + else { + for (Base *base = scene->base.first; base; base= base->next) { + if ( (base->lay & layer) && base->object->pd) { + if (base->object->pd->deflect) + return 1; + } + } + } + return 0; } -static int query_external_colliders(Scene *scene, Object *me) +static int query_external_colliders(Scene *scene, Group *group, Object *me) { - return(are_there_deflectors(scene, me->lay)); + return(are_there_deflectors(scene, group, me->lay)); } /* --- dependency information functions*/ @@ -2197,7 +2247,7 @@ static void softbody_calc_forcesEx(Scene *scene, Object *ob, float forcetime, fl /* gravity = sb->grav * sb_grav_force_scale(ob); */ /* UNUSED */ /* check conditions for various options */ - do_deflector= query_external_colliders(scene, ob); + do_deflector= query_external_colliders(scene, sb->collision_group, ob); /* do_selfcollision=((ob->softflag & OB_SB_EDGES) && (sb->bspring)&& (ob->softflag & OB_SB_SELF)); */ /* UNUSED */ do_springcollision=do_deflector && (ob->softflag & OB_SB_EDGES) &&(ob->softflag & OB_SB_EDGECOLL); do_aero=((sb->aeroedge)&& (ob->softflag & OB_SB_EDGES)); @@ -2261,7 +2311,7 @@ static void softbody_calc_forces(Scene *scene, Object *ob, float forcetime, floa } /* check conditions for various options */ - do_deflector= query_external_colliders(scene, ob); + do_deflector= query_external_colliders(scene, sb->collision_group, ob); do_selfcollision=((ob->softflag & OB_SB_EDGES) && (sb->bspring)&& (ob->softflag & OB_SB_SELF)); do_springcollision=do_deflector && (ob->softflag & OB_SB_EDGES) &&(ob->softflag & OB_SB_EDGECOLL); do_aero=((sb->aeroedge)&& (ob->softflag & OB_SB_EDGES)); @@ -3468,11 +3518,11 @@ static void softbody_step(Scene *scene, Object *ob, SoftBody *sb, float dtime) */ if (dtime < 0 || dtime > 10.5f) return; - ccd_update_deflector_hash(scene, ob, sb->scratch->colliderhash); + ccd_update_deflector_hash(scene, sb->collision_group, ob, sb->scratch->colliderhash); if (sb->scratch->needstobuildcollider) { - if (query_external_colliders(scene, ob)) { - ccd_build_deflector_hash(scene, ob, sb->scratch->colliderhash); + if (query_external_colliders(scene, sb->collision_group, ob)) { + ccd_build_deflector_hash(scene, sb->collision_group, ob, sb->scratch->colliderhash); } sb->scratch->needstobuildcollider=0; } @@ -3592,9 +3642,7 @@ void sbObjectStep(Scene *scene, Object *ob, float cfra, float (*vertexCos)[3], i { SoftBody *sb= ob->soft; float dtime, timescale = 1.0f; - int framedelta, framenr = (int)cfra, startframe = scene->r.sfra, endframe = scene->r.efra; - - framedelta = 1; + int framedelta = 1, framenr = (int)cfra, startframe = scene->r.sfra, endframe = scene->r.efra; /* check for changes in mesh, should only happen in case the mesh * structure changes during an animation */ diff --git a/source/blender/blenkernel/intern/tracking.c b/source/blender/blenkernel/intern/tracking.c index 3b76e456ff7..a56fc0f9abe 100644 --- a/source/blender/blenkernel/intern/tracking.c +++ b/source/blender/blenkernel/intern/tracking.c @@ -44,6 +44,7 @@ #include "DNA_scene_types.h" #include "BLI_utildefines.h" +#include "BLI_ghash.h" #include "BLI_math.h" #include "BLI_math_base.h" #include "BLI_listbase.h" @@ -55,6 +56,7 @@ #include "BKE_fcurve.h" #include "BKE_tracking.h" +#include "BKE_library.h" #include "BKE_movieclip.h" #include "BKE_object.h" #include "BKE_scene.h" @@ -186,6 +188,133 @@ void BKE_tracking_free(MovieTracking *tracking) tracking_dopesheet_free(&tracking->dopesheet); } +/* Copy the whole list of tracks. */ +static void tracking_tracks_copy(ListBase *tracks_dst, ListBase *tracks_src, GHash *tracks_mapping) +{ + MovieTrackingTrack *track_dst, *track_src; + + BLI_listbase_clear(tracks_dst); + BLI_ghash_clear(tracks_mapping, NULL, NULL); + + for (track_src = tracks_src->first; track_src != NULL; track_src = track_src->next) { + track_dst = MEM_dupallocN(track_src); + if (track_src->markers) { + track_dst->markers = MEM_dupallocN(track_src->markers); + } + id_us_plus(&track_dst->gpd->id); + BLI_addtail(tracks_dst, track_dst); + BLI_ghash_insert(tracks_mapping, track_src, track_dst); + } +} + +/* copy the whole list of plane tracks (need whole MovieTracking structures due to embedded pointers to tracks). + * WARNING: implies tracking_[dst/src] and their tracks have already been copied. */ +static void tracking_plane_tracks_copy(ListBase *plane_tracks_dst, ListBase *plane_tracks_src, GHash *tracks_mapping) +{ + MovieTrackingPlaneTrack *plane_track_dst, *plane_track_src; + + BLI_listbase_clear(plane_tracks_dst); + + for (plane_track_src = plane_tracks_src->first; plane_track_src != NULL; plane_track_src = plane_track_src->next) { + plane_track_dst = MEM_dupallocN(plane_tracks_src); + if (plane_track_src->markers) { + plane_track_dst->markers = MEM_dupallocN(plane_track_src->markers); + } + plane_track_dst->point_tracks = MEM_mallocN(sizeof(*plane_track_dst->point_tracks) * plane_track_dst->point_tracksnr, __func__); + for (int i = 0; i < plane_track_dst->point_tracksnr; i++) { + plane_track_dst->point_tracks[i] = BLI_ghash_lookup(tracks_mapping, plane_track_src->point_tracks[i]); + } + id_us_plus(&plane_track_dst->image->id); + BLI_addtail(plane_tracks_dst, plane_track_dst); + } +} + +/* Copy reconstruction structure. */ +static void tracking_reconstruction_copy( + MovieTrackingReconstruction *reconstruction_dst, MovieTrackingReconstruction *reconstruction_src) +{ + *reconstruction_dst = *reconstruction_src; + if (reconstruction_src->cameras) { + reconstruction_dst->cameras = MEM_dupallocN(reconstruction_src->cameras); + } +} + +/* Copy stabilization structure. */ +static void tracking_stabilization_copy( + MovieTrackingStabilization *stabilization_dst, MovieTrackingStabilization *stabilization_src, + GHash *tracks_mapping) +{ + *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. */ +static void tracking_object_copy( + MovieTrackingObject *object_dst, MovieTrackingObject *object_src, GHash *tracks_mapping) +{ + *object_dst = *object_src; + tracking_tracks_copy(&object_dst->tracks, &object_src->tracks, tracks_mapping); + tracking_plane_tracks_copy(&object_dst->plane_tracks, &object_src->plane_tracks, tracks_mapping); + tracking_reconstruction_copy(&object_dst->reconstruction, &object_src->reconstruction); +} + +/* Copy list of tracking objects. */ +static void tracking_objects_copy(ListBase *objects_dst, ListBase *objects_src, GHash *tracks_mapping) +{ + MovieTrackingObject *object_dst, *object_src; + + BLI_listbase_clear(objects_dst); + + for (object_src = objects_src->first; object_src != NULL; object_src = object_src->next) { + object_dst = MEM_mallocN(sizeof(*object_dst), __func__); + tracking_object_copy(object_dst, object_src, tracks_mapping); + BLI_addtail(objects_dst, object_dst); + } +} + +/* Copy tracking structure content. */ +void BKE_tracking_copy(MovieTracking *tracking_dst, MovieTracking *tracking_src) +{ + GHash *tracks_mapping = BLI_ghash_ptr_new(__func__); + + *tracking_dst = *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); + if (tracking_src->act_track) { + tracking_dst->act_track = BLI_ghash_lookup(tracks_mapping, tracking_src->act_track); + } + if (tracking_src->act_plane_track) { + MovieTrackingPlaneTrack *plane_track_src, *plane_track_dst; + for (plane_track_src = tracking_src->plane_tracks.first, plane_track_dst = tracking_dst->plane_tracks.first; + !ELEM(NULL, plane_track_src, plane_track_dst); + plane_track_src = plane_track_src->next, plane_track_dst = plane_track_dst->next) + { + if (plane_track_src == tracking_src->act_plane_track) { + tracking_dst->act_plane_track = plane_track_dst; + break; + } + } + } + + /* Warning! Will override tracks_mapping. */ + tracking_objects_copy(&tracking_dst->objects, &tracking_src->objects, tracks_mapping); + + /* Those remaining are runtime data, they will be reconstructed as needed, do not bother copying them. */ + tracking_dst->dopesheet.ok = false; + BLI_listbase_clear(&tracking_dst->dopesheet.channels); + BLI_listbase_clear(&tracking_dst->dopesheet.coverage_segments); + + tracking_dst->camera.intrinsics = NULL; + tracking_dst->stats = NULL; + + BLI_ghash_free(tracks_mapping, NULL, NULL); +} + /* Initialize motion tracking settings to default values, * used when new movie clip datablock is creating. */ diff --git a/source/blender/blenlib/intern/BLI_heap.c b/source/blender/blenlib/intern/BLI_heap.c index 0a8dafc2dc1..d7fd1caa8da 100644 --- a/source/blender/blenlib/intern/BLI_heap.c +++ b/source/blender/blenlib/intern/BLI_heap.c @@ -55,9 +55,10 @@ struct HeapNode_Chunk { * or we allocate past the reserved number. * * \note Optimize number for 64kb allocs. + * \note keep type in sync with tot_nodes in heap_node_alloc_chunk. */ #define HEAP_CHUNK_DEFAULT_NUM \ - ((MEM_SIZE_OPTIMAL((1 << 16) - sizeof(struct HeapNode_Chunk))) / sizeof(HeapNode)) + ((unsigned int)((MEM_SIZE_OPTIMAL((1 << 16) - sizeof(struct HeapNode_Chunk))) / sizeof(HeapNode))) struct Heap { unsigned int size; diff --git a/source/blender/blenlib/intern/scanfill_utils.c b/source/blender/blenlib/intern/scanfill_utils.c index 059d5cb05a4..3b6ab99ae86 100644 --- a/source/blender/blenlib/intern/scanfill_utils.c +++ b/source/blender/blenlib/intern/scanfill_utils.c @@ -257,7 +257,7 @@ static bool scanfill_preprocess_self_isect( if (UNLIKELY(e_ls == NULL)) { /* only happens in very rare cases (entirely overlapping splines). - * in this case se can't do much useful. but at least don't crash */ + * in this case we can't do much useful. but at least don't crash */ continue; } 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 80597b844d9..cb67fe0dbed 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" @@ -142,7 +144,7 @@ #include "BKE_sequencer.h" #include "BKE_outliner_treehash.h" #include "BKE_sound.h" - +#include "BKE_colortools.h" #include "NOD_common.h" #include "NOD_socket.h" @@ -2689,6 +2691,36 @@ 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; + + 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; + + /* relink animdata */ + cache_file->adt = newdataadr(fd, cache_file->adt); + direct_link_animdata(fd, cache_file->adt); +} + /* ************ READ MOTION PATHS *************** */ /* direct data for cache */ @@ -4554,8 +4586,11 @@ static void lib_link_object(FileData *fd, Main *main) if (ob->pd) lib_link_partdeflect(fd, &ob->id, ob->pd); - if (ob->soft) + if (ob->soft) { + ob->soft->collision_group = newlibadr(fd, ob->id.lib, ob->soft->collision_group); + ob->soft->effector_weights->group = newlibadr(fd, ob->id.lib, ob->soft->effector_weights->group); + } lib_link_modifiers(fd, ob); @@ -5488,6 +5523,22 @@ static void direct_link_scene(FileData *fd, Scene *sce) sce->toolsettings->wpaint->wpaint_prev = NULL; sce->toolsettings->wpaint->tot = 0; } + /* relink grease pencil drawing brushes */ + link_list(fd, &sce->toolsettings->gp_brushes); + for (bGPDbrush *brush = sce->toolsettings->gp_brushes.first; brush; brush = brush->next) { + brush->cur_sensitivity = newdataadr(fd, brush->cur_sensitivity); + if (brush->cur_sensitivity) { + direct_link_curvemapping(fd, brush->cur_sensitivity); + } + brush->cur_strength = newdataadr(fd, brush->cur_strength); + if (brush->cur_strength) { + direct_link_curvemapping(fd, brush->cur_strength); + } + brush->cur_jitter = newdataadr(fd, brush->cur_jitter); + if (brush->cur_jitter) { + direct_link_curvemapping(fd, brush->cur_jitter); + } + } } if (sce->ed) { @@ -5763,7 +5814,8 @@ static void direct_link_gpencil(FileData *fd, bGPdata *gpd) bGPDlayer *gpl; bGPDframe *gpf; bGPDstroke *gps; - + bGPDpalette *palette; + /* we must firstly have some grease-pencil data to link! */ if (gpd == NULL) return; @@ -5771,11 +5823,19 @@ static void direct_link_gpencil(FileData *fd, bGPdata *gpd) /* relink animdata */ gpd->adt = newdataadr(fd, gpd->adt); direct_link_animdata(fd, gpd->adt); - + + /* relink palettes */ + link_list(fd, &gpd->palettes); + for (palette = gpd->palettes.first; palette; palette = palette->next) { + link_list(fd, &palette->colors); + } + /* relink layers */ link_list(fd, &gpd->layers); for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { + /* parent */ + gpl->parent = newlibadr(fd, gpd->id.lib, gpl->parent); /* relink frames */ link_list(fd, &gpl->frames); gpl->actframe = newdataadr(fd, gpl->actframe); @@ -5788,9 +5848,12 @@ static void direct_link_gpencil(FileData *fd, bGPdata *gpd) gps->points = newdataadr(fd, gps->points); /* the triangulation is not saved, so need to be recalculated */ - gps->flag |= GP_STROKE_RECALC_CACHES; gps->triangles = NULL; gps->tot_triangles = 0; + gps->flag |= GP_STROKE_RECALC_CACHES; + /* the color pointer is not saved, so need to be recalculated using the color name */ + gps->palcolor = NULL; + gps->flag |= GP_STROKE_RECALC_COLOR; } } } @@ -5947,10 +6010,8 @@ static void lib_link_screen(FileData *fd, Main *main) snode->id = newlibadr(fd, sc->id.lib, snode->id); snode->from = newlibadr(fd, sc->id.lib, snode->from); - if (snode->id) { - ntree = ntreeFromID(snode->id); - snode->nodetree = ntree ? ntree : newlibadr_us(fd, sc->id.lib, snode->nodetree); - } + ntree = snode->id ? ntreeFromID(snode->id) : NULL; + snode->nodetree = ntree ? ntree : newlibadr_us(fd, sc->id.lib, snode->nodetree); for (path = snode->treepath.first; path; path = path->next) { if (path == snode->treepath.first) { @@ -6329,11 +6390,8 @@ void blo_lib_link_screen_restore(Main *newmain, bScreen *curscreen, Scene *cursc snode->id = restore_pointer_by_name(id_map, snode->id, USER_REAL); snode->from = restore_pointer_by_name(id_map, snode->from, USER_IGNORE); - if (snode->id) { - ntree = ntreeFromID(snode->id); - snode->nodetree = ntree ? ntree : - restore_pointer_by_name(id_map, (ID *)snode->nodetree, USER_REAL); - } + ntree = snode->id ? ntreeFromID(snode->id) : NULL; + snode->nodetree = ntree ? ntree : restore_pointer_by_name(id_map, (ID *)snode->nodetree, USER_REAL); for (path = snode->treepath.first; path; path = path->next) { if (path == snode->treepath.first) { @@ -7497,6 +7555,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"; @@ -7745,6 +7804,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); @@ -7937,6 +7999,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 */ @@ -8797,7 +8860,7 @@ static void expand_object(FileData *fd, Main *mainvar, Object *ob) expand_doit(fd, mainvar, ob->proxy); if (ob->proxy_group) expand_doit(fd, mainvar, ob->proxy_group); - + for (sens = ob->sensors.first; sens; sens = sens->next) { if (sens->type == SENS_MESSAGE) { bMessageSensor *ms = sens->data; @@ -8876,8 +8939,18 @@ static void expand_object(FileData *fd, Main *mainvar, Object *ob) } } - if (ob->pd && ob->pd->tex) + if (ob->pd) { expand_doit(fd, mainvar, ob->pd->tex); + expand_doit(fd, mainvar, ob->pd->f_source); + } + + if (ob->soft) { + expand_doit(fd, mainvar, ob->soft->collision_group); + + if (ob->soft->effector_weights) { + expand_doit(fd, mainvar, ob->soft->effector_weights->group); + } + } if (ob->rigidbody_constraint) { expand_doit(fd, mainvar, ob->rigidbody_constraint->ob1); @@ -8977,6 +9050,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); @@ -9169,6 +9249,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 f67ce629a00..c102140acda 100644 --- a/source/blender/blenloader/intern/versioning_270.c +++ b/source/blender/blenloader/intern/versioning_270.c @@ -62,6 +62,7 @@ #include "BKE_scene.h" #include "BKE_sequencer.h" #include "BKE_screen.h" +#include "BKE_gpencil.h" #include "BLI_math.h" #include "BLI_listbase.h" @@ -993,15 +994,6 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main) } if (!MAIN_VERSION_ATLEAST(main, 277, 1)) { - /* init grease pencil smooth level iterations */ - for (bGPdata *gpd = main->gpencil.first; gpd; gpd = gpd->id.next) { - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { - if (gpl->draw_smoothlvl == 0) { - gpl->draw_smoothlvl = 1; - } - } - } - for (bScreen *screen = main->screen.first; screen; screen = screen->id.next) { for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) { for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) { @@ -1110,7 +1102,7 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main) for (Camera *camera = main->camera.first; camera != NULL; camera = camera->id.next) { if (camera->stereo.pole_merge_angle_from == 0.0f && - camera->stereo.pole_merge_angle_to == 0.0f) + camera->stereo.pole_merge_angle_to == 0.0f) { camera->stereo.pole_merge_angle_from = DEG2RADF(60.0f); camera->stereo.pole_merge_angle_to = DEG2RADF(75.0f); @@ -1149,5 +1141,96 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main) br->flag |= BRUSH_ACCUMULATE; } } + + if (!DNA_struct_elem_find(fd->filesdna, "ClothSimSettings", "float", "time_scale")) { + Object *ob; + ModifierData *md; + for (ob = main->object.first; ob; ob = ob->id.next) { + for (md = ob->modifiers.first; md; md = md->next) { + if (md->type == eModifierType_Cloth) { + ClothModifierData *clmd = (ClothModifierData *)md; + clmd->sim_parms->time_scale = 1.0f; + } + } + } + } } + + if (!MAIN_VERSION_ATLEAST(main, 277, 3)) { + /* ------- init of grease pencil initialization --------------- */ + 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 */ + ts->gp_sculpt.flag |= GP_BRUSHEDIT_FLAG_APPLY_POSITION; + /* initialize selected vertices alpha factor */ + ts->gp_sculpt.alpha = 1.0f; + + /* new strength sculpt brush */ + if (ts->gp_sculpt.brush[0].size >= 11) { + GP_BrushEdit_Settings *gset = &ts->gp_sculpt; + GP_EditBrush_Data *brush; + + brush = &gset->brush[GP_EDITBRUSH_TYPE_STRENGTH]; + brush->size = 25; + brush->strength = 0.5f; + brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF; + } + } + /* create a default grease pencil drawing brushes set */ + if (!BLI_listbase_is_empty(&main->gpencil)) { + for (Scene *scene = main->scene.first; scene; scene = scene->id.next) { + ToolSettings *ts = scene->toolsettings; + if (BLI_listbase_is_empty(&ts->gp_brushes)) { + BKE_gpencil_brush_init_presets(ts); + } + } + } + /* Convert Grease Pencil to new palettes/brushes + * Loop all strokes and create the palette and all colors + */ + for (bGPdata *gpd = main->gpencil.first; gpd; gpd = gpd->id.next) { + if (BLI_listbase_is_empty(&gpd->palettes)) { + /* create palette */ + 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 = 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; + /* 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); + + 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); + 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) + BKE_gpencil_palettecolor_setactive(palette, palette->colors.first); + } + } + } + /* ------- end of grease pencil initialization --------------- */ + } + } diff --git a/source/blender/blenloader/intern/versioning_defaults.c b/source/blender/blenloader/intern/versioning_defaults.c index 5798b62b689..01ef5d6a606 100644 --- a/source/blender/blenloader/intern/versioning_defaults.c +++ b/source/blender/blenloader/intern/versioning_defaults.c @@ -108,6 +108,11 @@ void BLO_update_defaults_startup_blend(Main *bmain) brush->strength = 0.5f; brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF; + brush = &gset->brush[GP_EDITBRUSH_TYPE_STRENGTH]; + brush->size = 25; + brush->strength = 0.5f; + brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF; + brush = &gset->brush[GP_EDITBRUSH_TYPE_GRAB]; brush->size = 50; brush->strength = 0.3f; diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index c9f4858e545..12389dc1e7f 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -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" @@ -2445,6 +2446,20 @@ static void write_scenes(WriteData *wd, ListBase *scebase) writestruct(wd, DATA, UvSculpt, 1, tos->uvsculpt); write_paint(wd, &tos->uvsculpt->paint); } + /* write grease-pencil drawing brushes to file */ + writelist(wd, DATA, bGPDbrush, &tos->gp_brushes); + for (bGPDbrush *brush = tos->gp_brushes.first; brush; brush = brush->next) { + if (brush->cur_sensitivity) { + write_curvemapping(wd, brush->cur_sensitivity); + } + if (brush->cur_strength) { + write_curvemapping(wd, brush->cur_strength); + } + if (brush->cur_jitter) { + write_curvemapping(wd, brush->cur_jitter); + } + } + write_paint(wd, &tos->imapaint.paint); @@ -2606,6 +2621,7 @@ static void write_gpencils(WriteData *wd, ListBase *lb) bGPDlayer *gpl; bGPDframe *gpf; bGPDstroke *gps; + bGPDpalette *palette; for (gpd = lb->first; gpd; gpd = gpd->id.next) { if (gpd->id.us > 0 || wd->current) { @@ -2632,6 +2648,11 @@ static void write_gpencils(WriteData *wd, ListBase *lb) } } } + /* write grease-pencil palettes */ + writelist(wd, DATA, bGPDpalette, &gpd->palettes); + for (palette = gpd->palettes.first; palette; palette = palette->next) { + writelist(wd, DATA, bGPDpalettecolor, &palette->colors); + } } } @@ -3633,6 +3654,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) { @@ -3829,6 +3865,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 e7538ec5324..5aa49cddfb0 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" @@ -170,6 +171,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/intern/bmesh_queries.c b/source/blender/bmesh/intern/bmesh_queries.c index 9b074dc4db4..b5f9575aff5 100644 --- a/source/blender/bmesh/intern/bmesh_queries.c +++ b/source/blender/bmesh/intern/bmesh_queries.c @@ -2065,7 +2065,7 @@ bool BM_face_exists_multi(BMVert **varr, BMEdge **earr, int len) } /* 2) loop over non-boundary edges that use boundary verts, - * check each have 2 tagges faces connected (faces that only use 'varr' verts) */ + * check each have 2 tagged faces connected (faces that only use 'varr' verts) */ ok = true; for (i = 0; i < len; i++) { BM_ITER_ELEM (e, &fiter, varr[i], BM_EDGES_OF_VERT) { diff --git a/source/blender/compositor/nodes/COM_FilterNode.cpp b/source/blender/compositor/nodes/COM_FilterNode.cpp index 7493f24ba6b..e8b08ce2ce1 100644 --- a/source/blender/compositor/nodes/COM_FilterNode.cpp +++ b/source/blender/compositor/nodes/COM_FilterNode.cpp @@ -49,7 +49,7 @@ void FilterNode::convertToOperations(NodeConverter &converter, const CompositorC operation->set3x3Filter(-1, -1, -1, -1, 9, -1, -1, -1, -1); break; case CMP_FILT_LAPLACE: - operation = new ConvolutionFilterOperation(); + operation = new ConvolutionEdgeFilterOperation(); operation->set3x3Filter(-1 / 8.0f, -1 / 8.0f, -1 / 8.0f, -1 / 8.0f, 1.0f, -1 / 8.0f, -1 / 8.0f, -1 / 8.0f, -1 / 8.0f); break; case CMP_FILT_SOBEL: diff --git a/source/blender/depsgraph/DEG_depsgraph_build.h b/source/blender/depsgraph/DEG_depsgraph_build.h index 49b648c7dae..0209e94debe 100644 --- a/source/blender/depsgraph/DEG_depsgraph_build.h +++ b/source/blender/depsgraph/DEG_depsgraph_build.h @@ -79,6 +79,7 @@ void DEG_scene_graph_free(struct Scene *scene); */ struct DepsNodeHandle; +struct CacheFile; struct Object; typedef enum eDepsSceneComponentType { @@ -100,11 +101,13 @@ 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); diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc index 9364c86196e..c470e6fb239 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" @@ -337,6 +338,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, @@ -1212,4 +1221,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 72005880161..6ca03ea7b63 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" @@ -592,6 +593,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); 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..ae830da1252 100644 --- a/source/blender/depsgraph/intern/depsgraph_build.cc +++ b/source/blender/depsgraph/intern/depsgraph_build.cc @@ -33,6 +33,7 @@ #include "MEM_guardedalloc.h" extern "C" { +#include "DNA_cachefile_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" @@ -89,6 +90,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 +128,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, 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/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/animation/anim_channels_defines.c b/source/blender/editors/animation/anim_channels_defines.c index 9a5a3ec2aa5..eaae9532889 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_screen_types.h" @@ -1577,6 +1578,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; @@ -3310,6 +3393,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 */ @@ -4129,6 +4213,8 @@ void ANIM_channel_draw_widgets(const bContext *C, bAnimContext *ac, bAnimListEle offset += ICON_WIDTH; } else if (ale->type == ANIMTYPE_GPLAYER) { +#if 0 + /* XXX: Maybe need a better design */ /* color swatch for layer color */ bGPDlayer *gpl = (bGPDlayer *)ale->data; PointerRNA ptr; @@ -4137,7 +4223,6 @@ void ANIM_channel_draw_widgets(const bContext *C, bAnimContext *ac, bAnimListEle RNA_pointer_create(ale->id, &RNA_GPencilLayer, ale->data, &ptr); UI_block_align_begin(block); - UI_block_emboss_set(block, RNA_boolean_get(&ptr, "is_stroke_visible") ? UI_EMBOSS : UI_EMBOSS_NONE); uiDefButR(block, UI_BTYPE_COLOR, 1, "", offset, yminc, w, ICON_WIDTH, &ptr, "color", -1, @@ -4147,11 +4232,11 @@ void ANIM_channel_draw_widgets(const bContext *C, bAnimContext *ac, bAnimListEle uiDefButR(block, UI_BTYPE_COLOR, 1, "", offset + w, yminc, w, ICON_WIDTH, &ptr, "fill_color", -1, 0, 0, 0, 0, gpl->info); - UI_block_emboss_set(block, UI_EMBOSS_NONE); UI_block_align_end(block); - + offset += ICON_WIDTH; +#endif } } diff --git a/source/blender/editors/animation/anim_channels_edit.c b/source/blender/editors/animation/anim_channels_edit.c index edc258003fb..20e5bec44a3 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: @@ -174,6 +175,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: @@ -273,6 +275,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: @@ -367,6 +370,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: @@ -1682,7 +1686,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; } @@ -2712,6 +2716,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 26f2a3ebf9c..df7b6c53b97 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" @@ -198,6 +199,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... */ { @@ -659,6 +670,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; @@ -1737,6 +1761,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) { @@ -2767,6 +2827,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); @@ -2878,7 +2944,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/animation/keyframing.c b/source/blender/editors/animation/keyframing.c index 0c0f54f0179..98be77b491f 100644 --- a/source/blender/editors/animation/keyframing.c +++ b/source/blender/editors/animation/keyframing.c @@ -930,11 +930,18 @@ bool insert_keyframe_direct(ReportList *reports, PointerRNA ptr, PropertyRNA *pr /* update F-Curve flags to ensure proper behaviour for property type */ update_autoflags_fcurve_direct(fcu, prop); - + /* adjust frame on which to add keyframe */ if ((flag & INSERTKEY_DRIVER) && (fcu->driver)) { - /* for making it easier to add corrective drivers... */ - cfra = evaluate_driver(fcu->driver, cfra); + PathResolvedRNA anim_rna; + + if (RNA_path_resolved_create(&ptr, prop, fcu->array_index, &anim_rna)) { + /* for making it easier to add corrective drivers... */ + cfra = evaluate_driver(&anim_rna, fcu->driver, cfra); + } + else { + cfra = 0.0f; + } } /* obtain value to give keyframe */ diff --git a/source/blender/editors/gpencil/drawgpencil.c b/source/blender/editors/gpencil/drawgpencil.c index 79a2c494239..4ef76f5ee25 100644 --- a/source/blender/editors/gpencil/drawgpencil.c +++ b/source/blender/editors/gpencil/drawgpencil.c @@ -18,7 +18,7 @@ * The Original Code is Copyright (C) 2008, Blender Foundation * This is a new part of Blender * - * Contributor(s): Joshua Leung + * Contributor(s): Joshua Leung, Antonio Vazquez * * ***** END GPL LICENSE BLOCK ***** */ @@ -52,6 +52,7 @@ #include "DNA_space_types.h" #include "DNA_view3d_types.h" #include "DNA_userdef_types.h" +#include "DNA_object_types.h" #include "BKE_context.h" #include "BKE_global.h" @@ -94,9 +95,32 @@ typedef enum eDrawStrokeFlags { #define GP_DRAWTHICKNESS_SPECIAL 3 /* ----- Tool Buffer Drawing ------ */ +/* helper function to set color of buffer point */ +static void gp_set_tpoint_color(tGPspoint *pt, float ink[4]) +{ + float alpha = ink[3] * pt->strength; + CLAMP(alpha, GPENCIL_STRENGTH_MIN, 1.0f); + glColor4f(ink[0], ink[1], ink[2], alpha); +} + +/* helper function to set color of point */ +static void gp_set_point_color(bGPDspoint *pt, float ink[4]) +{ + float alpha = ink[3] * pt->strength; + CLAMP(alpha, GPENCIL_STRENGTH_MIN, 1.0f); + glColor4f(ink[0], ink[1], ink[2], alpha); +} + +/* helper function to set color and point */ +static void gp_set_color_and_tpoint(tGPspoint *pt, float ink[4]) +{ + gp_set_tpoint_color(pt, ink); + glVertex2iv(&pt->x); +} /* draw stroke defined in buffer (simple ogl lines/points for now, as dotted lines) */ -static void gp_draw_stroke_buffer(tGPspoint *points, int totpoints, short thickness, short dflag, short sflag) +static void gp_draw_stroke_buffer(tGPspoint *points, int totpoints, short thickness, + short dflag, short sflag, float ink[4]) { tGPspoint *pt; int i; @@ -113,7 +137,8 @@ static void gp_draw_stroke_buffer(tGPspoint *points, int totpoints, short thickn /* if drawing a single point, draw it larger */ glPointSize((float)(thickness + 2) * points->pressure); glBegin(GL_POINTS); - glVertex2iv(&points->x); + + gp_set_color_and_tpoint(points, ink); glEnd(); } else if (sflag & GP_STROKE_ERASER) { @@ -138,15 +163,18 @@ static void gp_draw_stroke_buffer(tGPspoint *points, int totpoints, short thickn glBegin(GL_LINE_STRIP); /* need to roll-back one point to ensure that there are no gaps in the stroke */ - if (i != 0) glVertex2iv(&(pt - 1)->x); + if (i != 0) { + gp_set_color_and_tpoint((pt - 1), ink); + } /* now the point we want... */ - glVertex2iv(&pt->x); + gp_set_color_and_tpoint(pt, ink); oldpressure = pt->pressure; } - else - glVertex2iv(&pt->x); + else { + gp_set_color_and_tpoint(pt, ink); + } } glEnd(); @@ -155,37 +183,35 @@ static void gp_draw_stroke_buffer(tGPspoint *points, int totpoints, short thickn } /* --------- 2D Stroke Drawing Helpers --------- */ - -/* helper function to calculate x-y drawing coordinates for 2D points */ -static void gp_calc_2d_stroke_xy(bGPDspoint *pt, short sflag, int offsx, int offsy, int winx, int winy, float r_co[2]) +/* change in parameter list */ +static void gp_calc_2d_stroke_fxy(float pt[3], short sflag, int offsx, int offsy, int winx, int winy, float r_co[2]) { if (sflag & GP_STROKE_2DSPACE) { - r_co[0] = pt->x; - r_co[1] = pt->y; + r_co[0] = pt[0]; + r_co[1] = pt[1]; } else if (sflag & GP_STROKE_2DIMAGE) { - const float x = (float)((pt->x * winx) + offsx); - const float y = (float)((pt->y * winy) + offsy); - + const float x = (float)((pt[0] * winx) + offsx); + const float y = (float)((pt[1] * winy) + offsy); + r_co[0] = x; r_co[1] = y; } else { - const float x = (float)(pt->x / 100 * winx) + offsx; - const float y = (float)(pt->y / 100 * winy) + offsy; - + const float x = (float)(pt[0] / 100 * winx) + offsx; + const float y = (float)(pt[1] / 100 * winy) + offsy; + r_co[0] = x; r_co[1] = y; } } - /* ----------- Volumetric Strokes --------------- */ /* draw a 2D buffer stroke in "volumetric" style * NOTE: the stroke buffer doesn't have any coordinate offsets/transforms */ static void gp_draw_stroke_volumetric_buffer(tGPspoint *points, int totpoints, short thickness, - short dflag, short UNUSED(sflag)) + short dflag, short UNUSED(sflag), float ink[4]) { GLUquadricObj *qobj = gluNewQuadric(); float modelview[4][4]; @@ -216,6 +242,7 @@ static void gp_draw_stroke_volumetric_buffer(tGPspoint *points, int totpoints, s glLoadMatrixf((float *)modelview); /* draw the disk using the current state... */ + gp_set_tpoint_color(pt, ink); gluDisk(qobj, 0.0, pt->pressure * thickness, 32, 1); @@ -229,7 +256,8 @@ static void gp_draw_stroke_volumetric_buffer(tGPspoint *points, int totpoints, s /* draw a 2D strokes in "volumetric" style */ static void gp_draw_stroke_volumetric_2d(bGPDspoint *points, int totpoints, short thickness, short dflag, short sflag, - int offsx, int offsy, int winx, int winy) + int offsx, int offsy, int winx, int winy, + float diff_mat[4][4], float ink[4]) { GLUquadricObj *qobj = gluNewQuadric(); float modelview[4][4]; @@ -238,7 +266,7 @@ static void gp_draw_stroke_volumetric_2d(bGPDspoint *points, int totpoints, shor bGPDspoint *pt; int i; - + float fpt[3]; /* HACK: We need a scale factor for the drawing in the image editor, * which seems to use 1 unit as it's maximum size, whereas everything @@ -256,10 +284,14 @@ static void gp_draw_stroke_volumetric_2d(bGPDspoint *points, int totpoints, shor glPushMatrix(); for (i = 0, pt = points; i < totpoints; i++, pt++) { + /* color of point */ + gp_set_point_color(pt, ink); + /* set the transformed position */ float co[2]; - gp_calc_2d_stroke_xy(pt, sflag, offsx, offsy, winx, winy, co); + mul_v3_m4v3(fpt, diff_mat, &pt->x); + gp_calc_2d_stroke_fxy(fpt, sflag, offsx, offsy, winx, winy, co); translate_m4(modelview, co[0], co[1], 0.0f); glLoadMatrixf((float *)modelview); @@ -276,8 +308,9 @@ static void gp_draw_stroke_volumetric_2d(bGPDspoint *points, int totpoints, shor } /* draw a 3D stroke in "volumetric" style */ -static void gp_draw_stroke_volumetric_3d(bGPDspoint *points, int totpoints, short thickness, - short UNUSED(dflag), short UNUSED(sflag)) +static void gp_draw_stroke_volumetric_3d( + bGPDspoint *points, int totpoints, short thickness, + short UNUSED(dflag), short UNUSED(sflag), float diff_mat[4][4], float ink[4]) { GLUquadricObj *qobj = gluNewQuadric(); @@ -286,7 +319,7 @@ static void gp_draw_stroke_volumetric_3d(bGPDspoint *points, int totpoints, shor bGPDspoint *pt; int i; - + float fpt[3]; /* Get the basic modelview matrix we use for performing calculations */ glGetFloatv(GL_MODELVIEW_MATRIX, (float *)base_modelview); @@ -305,8 +338,13 @@ static void gp_draw_stroke_volumetric_3d(bGPDspoint *points, int totpoints, shor glPushMatrix(); for (i = 0, pt = points; i < totpoints && pt; i++, pt++) { + /* color of point */ + gp_set_point_color(pt, ink); + + mul_v3_m4v3(fpt, diff_mat, &pt->x); + /* apply translation to base_modelview, so that the translated point is put in the right place */ - translate_m4(base_modelview, pt->x, pt->y, pt->z); + translate_m4(base_modelview, fpt[0], fpt[1], fpt[2]); /* copy the translation component to the billboard matrix we're going to use, * then reset the base matrix to the original values so that we can do the same @@ -378,9 +416,9 @@ static void gp_stroke_2d_flat(bGPDspoint *points, int totpoints, float(*points2d static void gp_triangulate_stroke_fill(bGPDstroke *gps) { BLI_assert(gps->totpoints >= 3); - gps->tot_triangles = gps->totpoints - 2; - + /* allocate memory for temporary areas */ + gps->tot_triangles = gps->totpoints - 2; unsigned int (*tmp_triangles)[3] = MEM_mallocN(sizeof(*tmp_triangles) * gps->tot_triangles, "GP Stroke temp triangulation"); float (*points2d)[2] = MEM_mallocN(sizeof(*points2d) * gps->totpoints, "GP Stroke temp 2d points"); @@ -390,6 +428,8 @@ static void gp_triangulate_stroke_fill(bGPDstroke *gps) gp_stroke_2d_flat(gps->points, gps->totpoints, points2d, &direction); BLI_polyfill_calc((const float(*)[2])points2d, (unsigned int)gps->totpoints, direction, (unsigned int(*)[3])tmp_triangles); + /* Number of triangles */ + gps->tot_triangles = gps->totpoints - 2; /* save triangulation data in stroke cache */ if (gps->tot_triangles > 0) { if (gps->triangles == NULL) { @@ -399,9 +439,7 @@ static void gp_triangulate_stroke_fill(bGPDstroke *gps) gps->triangles = MEM_recallocN(gps->triangles, sizeof(*gps->triangles) * gps->tot_triangles); } - int i; - - for (i = 0; i < gps->tot_triangles; i++) { + for (int i = 0; i < gps->tot_triangles; i++) { bGPDtriangle *stroke_triangle = &gps->triangles[i]; stroke_triangle->v1 = tmp_triangles[i][0]; stroke_triangle->v2 = tmp_triangles[i][1]; @@ -428,21 +466,27 @@ static void gp_triangulate_stroke_fill(bGPDstroke *gps) /* draw fills for shapes */ -static void gp_draw_stroke_fill(bGPDstroke *gps, short UNUSED(thickness), short dflag, int offsx, int offsy, int winx, int winy) +static void gp_draw_stroke_fill( + bGPdata *gpd, bGPDstroke *gps, + int offsx, int offsy, int winx, int winy, float diff_mat[4][4]) { + bGPDpalettecolor *palcolor; + int i; + float fpt[3]; + BLI_assert(gps->totpoints >= 3); - + + palcolor = ED_gpencil_stroke_getcolor(gpd, gps); + /* Triangulation fill if high quality flag is enabled */ - if (dflag & GP_DRAWDATA_HQ_FILL) { + if (palcolor->flag & PC_COLOR_HQ_FILL) { bGPDtriangle *stroke_triangle; bGPDspoint *pt; - int i; - + /* Calculate triangles cache for filling area (must be done only after changes) */ if ((gps->flag & GP_STROKE_RECALC_CACHES) || (gps->tot_triangles == 0) || (gps->triangles == NULL)) { gp_triangulate_stroke_fill(gps); } - /* Draw all triangles for filling the polygon (cache must be calculated before) */ BLI_assert(gps->tot_triangles >= 1); glBegin(GL_TRIANGLES); @@ -450,32 +494,33 @@ static void gp_draw_stroke_fill(bGPDstroke *gps, short UNUSED(thickness), short if (gps->flag & GP_STROKE_3DSPACE) { /* vertex 1 */ pt = &gps->points[stroke_triangle->v1]; - glVertex3fv(&pt->x); - + mul_v3_m4v3(fpt, diff_mat, &pt->x); + glVertex3fv(fpt); /* vertex 2 */ pt = &gps->points[stroke_triangle->v2]; - glVertex3fv(&pt->x); - + mul_v3_m4v3(fpt, diff_mat, &pt->x); + glVertex3fv(fpt); /* vertex 3 */ pt = &gps->points[stroke_triangle->v3]; - glVertex3fv(&pt->x); + mul_v3_m4v3(fpt, diff_mat, &pt->x); + glVertex3fv(fpt); } else { float co[2]; - /* vertex 1 */ pt = &gps->points[stroke_triangle->v1]; - gp_calc_2d_stroke_xy(pt, gps->flag, offsx, offsy, winx, winy, co); + mul_v3_m4v3(fpt, diff_mat, &pt->x); + gp_calc_2d_stroke_fxy(fpt, gps->flag, offsx, offsy, winx, winy, co); glVertex2fv(co); - /* vertex 2 */ pt = &gps->points[stroke_triangle->v2]; - gp_calc_2d_stroke_xy(pt, gps->flag, offsx, offsy, winx, winy, co); + mul_v3_m4v3(fpt, diff_mat, &pt->x); + gp_calc_2d_stroke_fxy(fpt, gps->flag, offsx, offsy, winx, winy, co); glVertex2fv(co); - /* vertex 3 */ pt = &gps->points[stroke_triangle->v3]; - gp_calc_2d_stroke_xy(pt, gps->flag, offsx, offsy, winx, winy, co); + mul_v3_m4v3(fpt, diff_mat, &pt->x); + gp_calc_2d_stroke_fxy(fpt, gps->flag, offsx, offsy, winx, winy, co); glVertex2fv(co); } } @@ -483,30 +528,31 @@ static void gp_draw_stroke_fill(bGPDstroke *gps, short UNUSED(thickness), short } else { /* As an initial implementation, we use the OpenGL filled polygon drawing - * here since it's the easiest option to implement for this case. It does - * come with limitations (notably for concave shapes), though it works well - * enough for many simple situations. - * - * We keep this legacy implementation around despite now having the high quality - * fills, as this is necessary for keeping everything working nicely for files - * created using old versions of Blender which may have depended on the artifacts - * the old fills created. - */ + * here since it's the easiest option to implement for this case. It does + * come with limitations (notably for concave shapes), though it shouldn't + * be much of an issue in most cases. + * + * We keep this legacy implementation around despite now having the high quality + * fills, as this is necessary for keeping everything working nicely for files + * created using old versions of Blender which may have depended on the artifacts + * the old fills created. + */ bGPDspoint *pt; - int i; - + glBegin(GL_POLYGON); for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { if (gps->flag & GP_STROKE_3DSPACE) { - glVertex3fv(&pt->x); + mul_v3_m4v3(fpt, diff_mat, &pt->x); + glVertex3fv(fpt); } else { float co[2]; - - gp_calc_2d_stroke_xy(pt, gps->flag, offsx, offsy, winx, winy, co); + mul_v3_m4v3(fpt, diff_mat, &pt->x); + gp_calc_2d_stroke_fxy(fpt, gps->flag, offsx, offsy, winx, winy, co); glVertex2fv(co); } } + glEnd(); } } @@ -514,23 +560,33 @@ static void gp_draw_stroke_fill(bGPDstroke *gps, short UNUSED(thickness), short /* ----- Existing Strokes Drawing (3D and Point) ------ */ /* draw a given stroke - just a single dot (only one point) */ -static void gp_draw_stroke_point(bGPDspoint *points, short thickness, short dflag, short sflag, - int offsx, int offsy, int winx, int winy) +static void gp_draw_stroke_point( + bGPDspoint *points, short thickness, short dflag, short sflag, + int offsx, int offsy, int winx, int winy, float diff_mat[4][4], float ink[4]) { + float fpt[3]; + bGPDspoint *pt = &points[0]; + + /* color of point */ + gp_set_point_color(pt, ink); + /* set point thickness (since there's only one of these) */ glPointSize((float)(thickness + 2) * points->pressure); + /* get final position using parent matrix */ + mul_v3_m4v3(fpt, diff_mat, &pt->x); + /* draw point */ if (sflag & GP_STROKE_3DSPACE) { glBegin(GL_POINTS); - glVertex3fv(&points->x); + glVertex3fv(fpt); glEnd(); } else { float co[2]; /* get coordinates of point */ - gp_calc_2d_stroke_xy(points, sflag, offsx, offsy, winx, winy, co); + gp_calc_2d_stroke_fxy(fpt, sflag, offsx, offsy, winx, winy, co); /* if thickness is less than GP_DRAWTHICKNESS_SPECIAL, simple dot looks ok * - also mandatory in if Image Editor 'image-based' dot @@ -559,16 +615,21 @@ static void gp_draw_stroke_point(bGPDspoint *points, short thickness, short dfla } /* draw a given stroke in 3d (i.e. in 3d-space), using simple ogl lines */ -static void gp_draw_stroke_3d(bGPDspoint *points, int totpoints, short thickness, bool debug, short UNUSED(sflag)) +static void gp_draw_stroke_3d(bGPDspoint *points, int totpoints, short thickness, bool debug, + short UNUSED(sflag), float diff_mat[4][4], float ink[4], bool cyclic) { - bGPDspoint *pt; + bGPDspoint *pt, *pt2; float curpressure = points[0].pressure; int i; - + float fpt[3]; + float cyclic_fpt[3]; + /* draw stroke curve */ glLineWidth(max_ff(curpressure * thickness, 1.0f)); glBegin(GL_LINE_STRIP); for (i = 0, pt = points; i < totpoints && pt; i++, pt++) { + gp_set_point_color(pt, ink); + /* if there was a significant pressure change, stop the curve, change the thickness of the stroke, * and continue drawing again (since line-width cannot change in middle of GL_LINE_STRIP) * Note: we want more visible levels of pressures when thickness is bigger. @@ -580,15 +641,29 @@ static void gp_draw_stroke_3d(bGPDspoint *points, int totpoints, short thickness glBegin(GL_LINE_STRIP); /* need to roll-back one point to ensure that there are no gaps in the stroke */ - if (i != 0) glVertex3fv(&(pt - 1)->x); + if (i != 0) { + pt2 = pt - 1; + mul_v3_m4v3(fpt, diff_mat, &pt2->x); + glVertex3fv(fpt); + } /* now the point we want... */ - glVertex3fv(&pt->x); + mul_v3_m4v3(fpt, diff_mat, &pt->x); + glVertex3fv(fpt); } else { - glVertex3fv(&pt->x); + mul_v3_m4v3(fpt, diff_mat, &pt->x); + glVertex3fv(fpt); + } + /* saves first point to use in cyclic */ + if (i == 0) { + copy_v3_v3(cyclic_fpt, fpt); } } + /* if cyclic draw line to first point */ + if (cyclic) { + glVertex3fv(cyclic_fpt); + } glEnd(); /* draw debug points of curve on top? */ @@ -597,9 +672,12 @@ static void gp_draw_stroke_3d(bGPDspoint *points, int totpoints, short thickness glPointSize((float)(thickness + 2)); glBegin(GL_POINTS); - for (i = 0, pt = points; i < totpoints && pt; i++, pt++) - glVertex3fv(&pt->x); + for (i = 0, pt = points; i < totpoints && pt; i++, pt++) { + mul_v3_m4v3(fpt, diff_mat, &pt->x); + glVertex3fv(fpt); + } glEnd(); + } } @@ -607,7 +685,7 @@ static void gp_draw_stroke_3d(bGPDspoint *points, int totpoints, short thickness /* draw a given stroke in 2d */ static void gp_draw_stroke_2d(bGPDspoint *points, int totpoints, short thickness_s, short dflag, short sflag, - bool debug, int offsx, int offsy, int winx, int winy) + bool debug, int offsx, int offsy, int winx, int winy, float diff_mat[4][4], float ink[4]) { /* otherwise thickness is twice that of the 3D view */ float thickness = (float)thickness_s * 0.5f; @@ -625,6 +703,7 @@ static void gp_draw_stroke_2d(bGPDspoint *points, int totpoints, short thickness bGPDspoint *pt1, *pt2; float pm[2]; int i; + float fpt[3]; glShadeModel(GL_FLAT); glBegin(GL_QUADS); @@ -635,10 +714,13 @@ static void gp_draw_stroke_2d(bGPDspoint *points, int totpoints, short thickness float m1[2], m2[2]; /* gradient and normal */ float mt[2], sc[2]; /* gradient for thickness, point for end-cap */ float pthick; /* thickness at segment point */ - + /* get x and y coordinates from points */ - gp_calc_2d_stroke_xy(pt1, sflag, offsx, offsy, winx, winy, s0); - gp_calc_2d_stroke_xy(pt2, sflag, offsx, offsy, winx, winy, s1); + mul_v3_m4v3(fpt, diff_mat, &pt1->x); + gp_calc_2d_stroke_fxy(fpt, sflag, offsx, offsy, winx, winy, s0); + + mul_v3_m4v3(fpt, diff_mat, &pt2->x); + gp_calc_2d_stroke_fxy(fpt, sflag, offsx, offsy, winx, winy, s1); /* calculate gradient and normal - 'angle'=(ny/nx) */ m1[1] = s1[1] - s0[1]; @@ -650,6 +732,9 @@ static void gp_draw_stroke_2d(bGPDspoint *points, int totpoints, short thickness /* always use pressure from first point here */ pthick = (pt1->pressure * thickness * scalefac); + /* color of point */ + gp_set_point_color(pt1, ink); + /* if the first segment, start of segment is segment's normal */ if (i == 0) { /* draw start cap first @@ -725,6 +810,9 @@ static void gp_draw_stroke_2d(bGPDspoint *points, int totpoints, short thickness /* for once, we use second point's pressure (otherwise it won't be drawn) */ pthick = (pt2->pressure * thickness * scalefac); + /* color of point */ + gp_set_point_color(pt2, ink); + /* calculate points for end of segment */ mt[0] = m2[0] * pthick; mt[1] = m2[1] * pthick; @@ -770,14 +858,15 @@ static void gp_draw_stroke_2d(bGPDspoint *points, int totpoints, short thickness if (debug) { bGPDspoint *pt; int i; - + float fpt[3]; + glPointSize((float)(thickness_s + 2)); glBegin(GL_POINTS); for (i = 0, pt = points; i < totpoints && pt; i++, pt++) { float co[2]; - - gp_calc_2d_stroke_xy(pt, sflag, offsx, offsy, winx, winy, co); + mul_v3_m4v3(fpt, diff_mat, &pt->x); + gp_calc_2d_stroke_fxy(fpt, sflag, offsx, offsy, winx, winy, co); glVertex2fv(co); } glEnd(); @@ -818,26 +907,45 @@ static bool gp_can_draw_stroke(const bGPDstroke *gps, const int dflag) } /* draw a set of strokes */ -static void gp_draw_strokes(bGPDframe *gpf, int offsx, int offsy, int winx, int winy, int dflag, - bool debug, short lthick, const float color[4], const float fill_color[4]) +static void gp_draw_strokes( + bGPdata *gpd, bGPDframe *gpf, int offsx, int offsy, int winx, int winy, int dflag, + bool debug, short lthick, const float opacity, const float tintcolor[4], + const bool onion, const bool custonion, float diff_mat[4][4]) { bGPDstroke *gps; - + float tcolor[4]; + float tfill[4]; + short sthickness; + float ink[4]; + for (gps = gpf->strokes.first; gps; gps = gps->next) { /* check if stroke can be drawn */ - if (gp_can_draw_stroke(gps, dflag) == false) + if (gp_can_draw_stroke(gps, dflag) == false) { continue; - + } + /* check if the color is visible */ + bGPDpalettecolor *palcolor = ED_gpencil_stroke_getcolor(gpd, gps); + if ((palcolor == NULL) || + (palcolor->flag & PC_COLOR_HIDE) || + /* if onion and ghost flag do not draw*/ + (onion && (palcolor->flag & PC_COLOR_ONIONSKIN))) + { + continue; + } + + /* calculate thickness */ + sthickness = gps->thickness + lthick; + /* check which stroke-drawer to use */ if (dflag & GP_DRAWDATA_ONLY3D) { const int no_xray = (dflag & GP_DRAWDATA_NO_XRAY); int mask_orig = 0; - + if (no_xray) { glGetIntegerv(GL_DEPTH_WRITEMASK, &mask_orig); glDepthMask(0); glEnable(GL_DEPTH_TEST); - + /* first arg is normally rv3d->dist, but this isn't * available here and seems to work quite well without */ bglPolygonOffset(1.0f, 1.0f); @@ -846,34 +954,65 @@ static void gp_draw_strokes(bGPDframe *gpf, int offsx, int offsy, int winx, int glPolygonOffset(-1.0f, -1.0f); #endif } - + /* 3D Fill */ - if ((dflag & GP_DRAWDATA_FILL) && (gps->totpoints >= 3)) { - glColor4fv(fill_color); - gp_draw_stroke_fill(gps, lthick, dflag, offsx, offsy, winx, winy); + //if ((dflag & GP_DRAWDATA_FILL) && (gps->totpoints >= 3)) { + if (gps->totpoints >= 3) { + /* set color using palette, tint color and opacity */ + interp_v3_v3v3(tfill, palcolor->fill, tintcolor, tintcolor[3]); + tfill[3] = palcolor->fill[3] * opacity; + if (tfill[3] > GPENCIL_ALPHA_OPACITY_THRESH) { + if (!onion) { + glColor4fv(tfill); + } + else { + if (custonion) { + glColor4fv(tintcolor); + } + else { + ARRAY_SET_ITEMS(tfill, UNPACK3(palcolor->fill), tintcolor[3]); + glColor4fv(tfill); + } + } + gp_draw_stroke_fill(gpd, gps, offsx, offsy, winx, winy, diff_mat); + } } - + /* 3D Stroke */ - glColor4fv(color); - - if (dflag & GP_DRAWDATA_VOLUMETRIC) { + /* set color using palette, tint color and opacity */ + if (!onion) { + interp_v3_v3v3(tcolor, palcolor->color, tintcolor, tintcolor[3]); + tcolor[3] = palcolor->color[3] * opacity; + copy_v4_v4(ink, tcolor); + } + else { + if (custonion) { + copy_v4_v4(ink, tintcolor); + } + else { + ARRAY_SET_ITEMS(tcolor, palcolor->color[0], palcolor->color[1], palcolor->color[2], opacity); + copy_v4_v4(ink, tcolor); + } + } + if (palcolor->flag & PC_COLOR_VOLUMETRIC) { /* volumetric stroke drawing */ - gp_draw_stroke_volumetric_3d(gps->points, gps->totpoints, lthick, dflag, gps->flag); + gp_draw_stroke_volumetric_3d(gps->points, gps->totpoints, sthickness, dflag, gps->flag, diff_mat, ink); } else { /* 3D Lines - OpenGL primitives-based */ if (gps->totpoints == 1) { - gp_draw_stroke_point(gps->points, lthick, dflag, gps->flag, offsx, offsy, winx, winy); + gp_draw_stroke_point(gps->points, sthickness, dflag, gps->flag, offsx, offsy, winx, winy, + diff_mat, ink); } else { - gp_draw_stroke_3d(gps->points, gps->totpoints, lthick, debug, gps->flag); + gp_draw_stroke_3d(gps->points, gps->totpoints, sthickness, debug, gps->flag, + diff_mat, ink, gps->flag & GP_STROKE_CYCLIC); } } - if (no_xray) { glDepthMask(mask_orig); glDisable(GL_DEPTH_TEST); - + bglPolygonOffset(0.0, 0.0); #if 0 glDisable(GL_POLYGON_OFFSET_LINE); @@ -883,25 +1022,58 @@ static void gp_draw_strokes(bGPDframe *gpf, int offsx, int offsy, int winx, int } else { /* 2D - Fill */ - if ((dflag & GP_DRAWDATA_FILL) && (gps->totpoints >= 3)) { - glColor4fv(fill_color); - gp_draw_stroke_fill(gps, lthick, dflag, offsx, offsy, winx, winy); + if (gps->totpoints >= 3) { + /* set color using palette, tint color and opacity */ + interp_v3_v3v3(tfill, palcolor->fill, tintcolor, tintcolor[3]); + tfill[3] = palcolor->fill[3] * opacity; + if (tfill[3] > GPENCIL_ALPHA_OPACITY_THRESH) { + if (!onion) { + glColor4fv(tfill); + } + else { + if (custonion) { + glColor4fv(tintcolor); + } + else { + ARRAY_SET_ITEMS(tfill, palcolor->fill[0], palcolor->fill[1], palcolor->fill[2], + tintcolor[3]); + glColor4fv(tfill); + } + } + gp_draw_stroke_fill(gpd, gps, offsx, offsy, winx, winy, diff_mat); + } } - + /* 2D Strokes... */ - glColor4fv(color); - - if (dflag & GP_DRAWDATA_VOLUMETRIC) { + /* set color using palette, tint color and opacity */ + if (!onion) { + interp_v3_v3v3(tcolor, palcolor->color, tintcolor, tintcolor[3]); + tcolor[3] = palcolor->color[3] * opacity; + copy_v4_v4(ink, tcolor); + } + else { + if (custonion) { + copy_v4_v4(ink, tintcolor); + } + else { + ARRAY_SET_ITEMS(tcolor, palcolor->color[0], palcolor->color[1], palcolor->color[2], opacity); + copy_v4_v4(ink, tcolor); + } + } + if (palcolor->flag & PC_COLOR_VOLUMETRIC) { /* blob/disk-based "volumetric" drawing */ - gp_draw_stroke_volumetric_2d(gps->points, gps->totpoints, lthick, dflag, gps->flag, offsx, offsy, winx, winy); + gp_draw_stroke_volumetric_2d(gps->points, gps->totpoints, sthickness, dflag, gps->flag, + offsx, offsy, winx, winy, diff_mat, ink); } else { /* normal 2D strokes */ if (gps->totpoints == 1) { - gp_draw_stroke_point(gps->points, lthick, dflag, gps->flag, offsx, offsy, winx, winy); + gp_draw_stroke_point(gps->points, sthickness, dflag, gps->flag, offsx, offsy, winx, winy, + diff_mat, ink); } else { - gp_draw_stroke_2d(gps->points, gps->totpoints, lthick, dflag, gps->flag, debug, offsx, offsy, winx, winy); + gp_draw_stroke_2d(gps->points, gps->totpoints, sthickness, dflag, gps->flag, debug, + offsx, offsy, winx, winy, diff_mat, ink); } } } @@ -909,13 +1081,19 @@ static void gp_draw_strokes(bGPDframe *gpf, int offsx, int offsy, int winx, int } /* Draw selected verts for strokes being edited */ -static void gp_draw_strokes_edit(bGPDframe *gpf, int offsx, int offsy, int winx, int winy, short dflag, const float tcolor[3]) +static void gp_draw_strokes_edit( + bGPdata *gpd, bGPDframe *gpf, int offsx, int offsy, int winx, int winy, short dflag, + short lflag, float diff_mat[4][4], float alpha) { bGPDstroke *gps; + /* if alpha 0 do not draw */ + if (alpha == 0.0f) + return; + const bool no_xray = (dflag & GP_DRAWDATA_NO_XRAY) != 0; int mask_orig = 0; - + /* set up depth masks... */ if (dflag & GP_DRAWDATA_ONLY3D) { if (no_xray) { @@ -939,7 +1117,8 @@ static void gp_draw_strokes_edit(bGPDframe *gpf, int offsx, int offsy, int winx, bGPDspoint *pt; float vsize, bsize; int i; - + float fpt[3]; + /* check if stroke can be drawn */ if (gp_can_draw_stroke(gps, dflag) == false) continue; @@ -951,6 +1130,19 @@ static void gp_draw_strokes_edit(bGPDframe *gpf, int offsx, int offsy, int winx, if ((gps->flag & GP_STROKE_SELECT) == 0) continue; + /* verify palette color lock */ + { + bGPDpalettecolor *palcolor = ED_gpencil_stroke_getcolor(gpd, gps); + if (palcolor != NULL) { + if (palcolor->flag & PC_COLOR_HIDE) { + continue; + } + if (((lflag & GP_LAYER_UNLOCK_COLOR) == 0) && (palcolor->flag & PC_COLOR_LOCKED)) { + continue; + } + } + } + /* Get size of verts: * - The selected state needs to be larger than the unselected state so that * they stand out more. @@ -966,25 +1158,23 @@ static void gp_draw_strokes_edit(bGPDframe *gpf, int offsx, int offsy, int winx, } /* First Pass: Draw all the verts (i.e. these become the unselected state) */ - if (tcolor != NULL) { - /* for now, we assume that the base color of the points is not too close to the real color */ - glColor3fv(tcolor); - } - else { - /* this doesn't work well with the default theme and black strokes... */ - UI_ThemeColor(TH_GP_VERTEX); - } + /* for now, we assume that the base color of the points is not too close to the real color */ + /* set color using palette */ + bGPDpalettecolor *palcolor = ED_gpencil_stroke_getcolor(gpd, gps); + glColor3fv(palcolor->color); + glPointSize(bsize); glBegin(GL_POINTS); for (i = 0, pt = gps->points; i < gps->totpoints && pt; i++, pt++) { if (gps->flag & GP_STROKE_3DSPACE) { - glVertex3fv(&pt->x); + mul_v3_m4v3(fpt, diff_mat, &pt->x); + glVertex3fv(fpt); } else { float co[2]; - - gp_calc_2d_stroke_xy(pt, gps->flag, offsx, offsy, winx, winy, co); + mul_v3_m4v3(fpt, diff_mat, &pt->x); + gp_calc_2d_stroke_fxy(fpt, gps->flag, offsx, offsy, winx, winy, co); glVertex2fv(co); } } @@ -992,24 +1182,54 @@ static void gp_draw_strokes_edit(bGPDframe *gpf, int offsx, int offsy, int winx, /* Second Pass: Draw only verts which are selected */ - UI_ThemeColor(TH_GP_VERTEX_SELECT); + float curColor[4]; + UI_GetThemeColor3fv(TH_GP_VERTEX_SELECT, curColor); + glColor4f(curColor[0], curColor[1], curColor[2], alpha); + glPointSize(vsize); glBegin(GL_POINTS); for (i = 0, pt = gps->points; i < gps->totpoints && pt; i++, pt++) { if (pt->flag & GP_SPOINT_SELECT) { if (gps->flag & GP_STROKE_3DSPACE) { - glVertex3fv(&pt->x); + mul_v3_m4v3(fpt, diff_mat, &pt->x); + glVertex3fv(fpt); } else { float co[2]; - gp_calc_2d_stroke_xy(pt, gps->flag, offsx, offsy, winx, winy, co); + mul_v3_m4v3(fpt, diff_mat, &pt->x); + gp_calc_2d_stroke_fxy(fpt, gps->flag, offsx, offsy, winx, winy, co); glVertex2fv(co); } } } glEnd(); + + /* Draw start and end point if enabled stroke direction hint */ + if ((gpd->flag & GP_DATA_SHOW_DIRECTION) && (gps->totpoints > 1)) { + bGPDspoint *p; + + glPointSize(vsize + 4); + glBegin(GL_POINTS); + + /* start point in green bigger */ + glColor3f(0.0f, 1.0f, 0.0f); + p = &gps->points[0]; + mul_v3_m4v3(fpt, diff_mat, &p->x); + glVertex3fv(fpt); + glEnd(); + + /* end point in red smaller */ + glPointSize(vsize + 1); + glBegin(GL_POINTS); + + glColor3f(1.0f, 0.0f, 0.0f); + p = &gps->points[gps->totpoints - 1]; + mul_v3_m4v3(fpt, diff_mat, &p->x); + glVertex3fv(fpt); + glEnd(); + } } @@ -1031,18 +1251,20 @@ static void gp_draw_strokes_edit(bGPDframe *gpf, int offsx, int offsy, int winx, /* ----- General Drawing ------ */ /* draw onion-skinning for a layer */ -static void gp_draw_onionskins(bGPDlayer *gpl, bGPDframe *gpf, int offsx, int offsy, int winx, int winy, - int UNUSED(cfra), int dflag, bool debug, short lthick) +static void gp_draw_onionskins( + bGPdata *gpd, bGPDlayer *gpl, bGPDframe *gpf, int offsx, int offsy, int winx, int winy, + int UNUSED(cfra), int dflag, bool debug, float diff_mat[4][4]) { - const float alpha = gpl->color[3]; + const float default_color[3] = {UNPACK3(U.gpencil_new_layer_col)}; + const float alpha = 1.0f; float color[4]; - + /* 1) Draw Previous Frames First */ if (gpl->flag & GP_LAYER_GHOST_PREVCOL) { copy_v3_v3(color, gpl->gcolor_prev); } else { - copy_v3_v3(color, gpl->color); + copy_v3_v3(color, default_color); } if (gpl->gstep > 0) { @@ -1056,7 +1278,8 @@ static void gp_draw_onionskins(bGPDlayer *gpl, bGPDframe *gpf, int offsx, int of /* alpha decreases with distance from curframe index */ fac = 1.0f - ((float)(gpf->framenum - gf->framenum) / (float)(gpl->gstep + 1)); color[3] = alpha * fac * 0.66f; - gp_draw_strokes(gf, offsx, offsy, winx, winy, dflag, debug, lthick, color, color); + gp_draw_strokes(gpd, gf, offsx, offsy, winx, winy, dflag, debug, gpl->thickness, 1.0f, color, + true, gpl->flag & GP_LAYER_GHOST_PREVCOL, diff_mat); } else break; @@ -1066,7 +1289,8 @@ static void gp_draw_onionskins(bGPDlayer *gpl, bGPDframe *gpf, int offsx, int of /* draw the strokes for the ghost frames (at half of the alpha set by user) */ if (gpf->prev) { color[3] = (alpha / 7); - gp_draw_strokes(gpf->prev, offsx, offsy, winx, winy, dflag, debug, lthick, color, color); + gp_draw_strokes(gpd, gpf->prev, offsx, offsy, winx, winy, dflag, debug, gpl->thickness, 1.0f, color, + true, gpl->flag & GP_LAYER_GHOST_PREVCOL, diff_mat); } } else { @@ -1079,7 +1303,7 @@ static void gp_draw_onionskins(bGPDlayer *gpl, bGPDframe *gpf, int offsx, int of copy_v3_v3(color, gpl->gcolor_next); } else { - copy_v3_v3(color, gpl->color); + copy_v3_v3(color, default_color); } if (gpl->gstep_next > 0) { @@ -1093,7 +1317,8 @@ static void gp_draw_onionskins(bGPDlayer *gpl, bGPDframe *gpf, int offsx, int of /* alpha decreases with distance from curframe index */ fac = 1.0f - ((float)(gf->framenum - gpf->framenum) / (float)(gpl->gstep_next + 1)); color[3] = alpha * fac * 0.66f; - gp_draw_strokes(gf, offsx, offsy, winx, winy, dflag, debug, lthick, color, color); + gp_draw_strokes(gpd, gf, offsx, offsy, winx, winy, dflag, debug, gpl->thickness, 1.0f, color, + true, gpl->flag & GP_LAYER_GHOST_NEXTCOL, diff_mat); } else break; @@ -1103,34 +1328,38 @@ static void gp_draw_onionskins(bGPDlayer *gpl, bGPDframe *gpf, int offsx, int of /* draw the strokes for the ghost frames (at half of the alpha set by user) */ if (gpf->next) { color[3] = (alpha / 4); - gp_draw_strokes(gpf->next, offsx, offsy, winx, winy, dflag, debug, lthick, color, color); + gp_draw_strokes(gpd, gpf->next, offsx, offsy, winx, winy, dflag, debug, gpl->thickness, 1.0f, color, + true, gpl->flag & GP_LAYER_GHOST_NEXTCOL, diff_mat); } } else { /* don't draw - disabled */ } - /* 3) restore alpha */ - glColor4fv(gpl->color); } /* loop over gpencil data layers, drawing them */ -static void gp_draw_data_layers(bGPdata *gpd, int offsx, int offsy, int winx, int winy, int cfra, int dflag) +static void gp_draw_data_layers( + bGPDbrush *brush, float alpha, bGPdata *gpd, + int offsx, int offsy, int winx, int winy, int cfra, int dflag) { bGPDlayer *gpl; - + float diff_mat[4][4]; + for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { bGPDframe *gpf; - + /* calculate parent position */ + ED_gpencil_parent_location(gpl, diff_mat); + bool debug = (gpl->flag & GP_LAYER_DRAWDEBUG) ? true : false; - short lthick = gpl->thickness; + short lthick = brush->thickness + gpl->thickness; /* don't draw layer if hidden */ if (gpl->flag & GP_LAYER_HIDE) continue; /* get frame to draw */ - gpf = gpencil_layer_getframe(gpl, cfra, 0); + gpf = BKE_gpencil_layer_getframe(gpl, cfra, 0); if (gpf == NULL) continue; @@ -1155,9 +1384,6 @@ static void gp_draw_data_layers(bGPdata *gpd, int offsx, int offsy, int winx, in /* HQ fills... */ GP_DRAWFLAG_APPLY((gpl->flag & GP_LAYER_HQ_FILL), GP_DRAWDATA_HQ_FILL); - /* fill strokes... */ - // XXX: this is not a very good limit - GP_DRAWFLAG_APPLY((gpl->fill[3] > GPENCIL_ALPHA_OPACITY_THRESH), GP_DRAWDATA_FILL); #undef GP_DRAWFLAG_APPLY /* draw 'onionskins' (frame left + right) */ @@ -1165,11 +1391,12 @@ static void gp_draw_data_layers(bGPdata *gpd, int offsx, int offsy, int winx, in /* Drawing method - only immediately surrounding (gstep = 0), * or within a frame range on either side (gstep > 0) */ - gp_draw_onionskins(gpl, gpf, offsx, offsy, winx, winy, cfra, dflag, debug, lthick); + gp_draw_onionskins(gpd, gpl, gpf, offsx, offsy, winx, winy, cfra, dflag, debug, diff_mat); } /* draw the strokes already in active frame */ - gp_draw_strokes(gpf, offsx, offsy, winx, winy, dflag, debug, lthick, gpl->color, gpl->fill); + gp_draw_strokes(gpd, gpf, offsx, offsy, winx, winy, dflag, debug, gpl->thickness, + gpl->opacity, gpl->tintcolor, false, false, diff_mat); /* Draw verts of selected strokes * - when doing OpenGL renders, we don't want to be showing these, as that ends up flickering @@ -1183,8 +1410,7 @@ static void gp_draw_data_layers(bGPdata *gpd, int offsx, int offsy, int winx, in (gpl->flag & GP_LAYER_LOCKED) == 0 && (gpd->flag & GP_DATA_STROKE_EDITMODE)) { - gp_draw_strokes_edit(gpf, offsx, offsy, winx, winy, dflag, - (gpl->color[3] < 0.95f) ? gpl->color : NULL); + gp_draw_strokes_edit(gpd, gpf, offsx, offsy, winx, winy, dflag, gpl->flag, diff_mat, alpha); } /* Check if may need to draw the active stroke cache, only if this layer is the active layer @@ -1194,7 +1420,7 @@ static void gp_draw_data_layers(bGPdata *gpd, int offsx, int offsy, int winx, in (gpf->flag & GP_FRAME_PAINT)) { /* Set color for drawing buffer stroke - since this may not be set yet */ - glColor4fv(gpl->color); + // glColor4fv(gpl->color); /* Buffer stroke needs to be drawn with a different linestyle * to help differentiate them from normal strokes. @@ -1202,11 +1428,12 @@ static void gp_draw_data_layers(bGPdata *gpd, int offsx, int offsy, int winx, in * It should also be noted that sbuffer contains temporary point types * i.e. tGPspoints NOT bGPDspoints */ - if (gpl->flag & GP_LAYER_VOLUMETRIC) { - gp_draw_stroke_volumetric_buffer(gpd->sbuffer, gpd->sbuffer_size, lthick, dflag, gpd->sbuffer_sflag); + if (gpd->sflag & PC_COLOR_VOLUMETRIC) { + gp_draw_stroke_volumetric_buffer(gpd->sbuffer, gpd->sbuffer_size, lthick, + dflag, gpd->sbuffer_sflag, gpd->scolor); } else { - gp_draw_stroke_buffer(gpd->sbuffer, gpd->sbuffer_size, lthick, dflag, gpd->sbuffer_sflag); + gp_draw_stroke_buffer(gpd->sbuffer, gpd->sbuffer_size, lthick, dflag, gpd->sbuffer_sflag, gpd->scolor); } } } @@ -1258,7 +1485,9 @@ static void gp_draw_status_text(bGPdata *gpd, ARegion *ar) } /* draw grease-pencil datablock */ -static void gp_draw_data(bGPdata *gpd, int offsx, int offsy, int winx, int winy, int cfra, int dflag) +static void gp_draw_data( + bGPDbrush *brush, float alpha, bGPdata *gpd, + int offsx, int offsy, int winx, int winy, int cfra, int dflag) { /* reset line drawing style (in case previous user didn't reset) */ setlinestyle(0); @@ -1276,7 +1505,7 @@ static void gp_draw_data(bGPdata *gpd, int offsx, int offsy, int winx, int winy, glEnable(GL_BLEND); /* draw! */ - gp_draw_data_layers(gpd, offsx, offsy, winx, winy, cfra, dflag); + gp_draw_data_layers(brush, alpha, gpd, offsx, offsy, winx, winy, cfra, dflag); /* turn off alpha blending, then smooth lines */ glDisable(GL_BLEND); // alpha blending @@ -1303,14 +1532,25 @@ static void gp_draw_data_all(Scene *scene, bGPdata *gpd, int offsx, int offsy, i } if (gpd_source) { - gp_draw_data(gpd_source, offsx, offsy, winx, winy, cfra, dflag); + ToolSettings *ts = scene->toolsettings; + 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); + } + } } /* scene/clip data has already been drawn, only object/track data is drawn here * 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)) { - gp_draw_data(gpd, offsx, offsy, winx, winy, cfra, dflag); + ToolSettings *ts = scene->toolsettings; + 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); + } } } @@ -1479,6 +1719,7 @@ void ED_gpencil_draw_view3d(wmWindowManager *wm, Scene *scene, View3D *v3d, AReg /* draw it! */ gp_draw_data_all(scene, gpd, offsx, offsy, winx, winy, CFRA, dflag, v3d->spacetype); + } void ED_gpencil_draw_ex(Scene *scene, bGPdata *gpd, int winx, int winy, const int cfra, const char spacetype) 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 0271afd6827..fcb2ce02bde 100644 --- a/source/blender/editors/gpencil/gpencil_brush.c +++ b/source/blender/editors/gpencil/gpencil_brush.c @@ -18,7 +18,7 @@ * The Original Code is Copyright (C) 2015, Blender Foundation * This is a new part of Blender * - * Contributor(s): Joshua Leung + * Contributor(s): Joshua Leung, Antonio Vazquez * * ***** END GPL LICENSE BLOCK ***** * @@ -51,6 +51,7 @@ #include "DNA_space_types.h" #include "DNA_view3d_types.h" #include "DNA_gpencil_types.h" +#include "DNA_object_types.h" #include "BKE_context.h" #include "BKE_global.h" @@ -223,9 +224,26 @@ static bool gp_brush_smooth_apply(tGP_BrushEditData *gso, bGPDstroke *gps, int i GP_EditBrush_Data *brush = gso->brush; float inf = gp_brush_influence_calc(gso, radius, co); bool affect_pressure = (brush->flag & GP_EDITBRUSH_FLAG_SMOOTH_PRESSURE) != 0; - + /* need one flag enabled by default */ + if ((gso->settings->flag & (GP_BRUSHEDIT_FLAG_APPLY_POSITION | + GP_BRUSHEDIT_FLAG_APPLY_STRENGTH | + GP_BRUSHEDIT_FLAG_APPLY_THICKNESS)) == 0) + { + gso->settings->flag |= GP_BRUSHEDIT_FLAG_APPLY_POSITION; + } + /* perform smoothing */ - return gp_smooth_stroke(gps, i, inf, affect_pressure); + if (gso->settings->flag & GP_BRUSHEDIT_FLAG_APPLY_POSITION) { + gp_smooth_stroke(gps, i, inf, affect_pressure); + } + if (gso->settings->flag & GP_BRUSHEDIT_FLAG_APPLY_STRENGTH) { + gp_smooth_stroke_strength(gps, i, inf); + } + if (gso->settings->flag & GP_BRUSHEDIT_FLAG_APPLY_THICKNESS) { + gp_smooth_stroke_thickness(gps, i, inf); + } + + return true; } /* ----------------------------------------------- */ @@ -268,6 +286,41 @@ static bool gp_brush_thickness_apply(tGP_BrushEditData *gso, bGPDstroke *gps, in /* ----------------------------------------------- */ +/* Color Strength Brush */ + +/* Make color more or less transparent by the specified amounts */ +static bool gp_brush_strength_apply( + tGP_BrushEditData *gso, bGPDstroke *gps, int i, + const int radius, const int co[2]) +{ + bGPDspoint *pt = gps->points + i; + float inf; + + /* Compute strength of effect + * - We divide the strength by 10, so that users can set "sane" values. + * Otherwise, good default values are in the range of 0.093 + */ + inf = gp_brush_influence_calc(gso, radius, co) / 10.0f; + + /* apply */ + // XXX: this is much too strong, and it should probably do some smoothing with the surrounding stuff + if (gp_brush_invert_check(gso)) { + /* make line thinner - reduce stroke pressure */ + pt->strength -= inf; + } + else { + /* make line thicker - increase stroke pressure */ + pt->strength += inf; + } + + /* Strength should stay within [0.0, 1.0] */ + CLAMP(pt->strength, 0.0f, 1.0f); + + return true; +} + + +/* ----------------------------------------------- */ /* Grab Brush */ /* Custom data per stroke for the Grab Brush @@ -373,11 +426,12 @@ static void gp_brush_grab_calc_dvec(tGP_BrushEditData *gso) } /* Apply grab transform to all relevant points of the affected strokes */ -static void gp_brush_grab_apply_cached(tGP_BrushEditData *gso, bGPDstroke *gps) +static void gp_brush_grab_apply_cached( + tGP_BrushEditData *gso, bGPDstroke *gps, bool parented, float diff_mat[4][4]) { tGPSB_Grab_StrokeData *data = BLI_ghash_lookup(gso->stroke_customdata, gps); int i; - + /* Apply dvec to all of the stored points */ for (i = 0; i < data->size; i++) { bGPDspoint *pt = &gps->points[data->points[i]]; @@ -385,9 +439,23 @@ static void gp_brush_grab_apply_cached(tGP_BrushEditData *gso, bGPDstroke *gps) /* adjust the amount of displacement to apply */ mul_v3_v3fl(delta, gso->dvec, data->weights[i]); + if (!parented) { + /* apply */ + add_v3_v3(&pt->x, delta); + } + else { + float fpt[3]; + /* apply transformation */ + mul_v3_m4v3(fpt, diff_mat, &pt->x); + /* apply */ + add_v3_v3(fpt, delta); + copy_v3_v3(&pt->x, fpt); + /* undo transformation to the init parent position */ + float inverse_diff_mat[4][4]; + invert_m4_m4(inverse_diff_mat, diff_mat); + mul_m4_v3(inverse_diff_mat, &pt->x); + } - /* apply */ - add_v3_v3(&pt->x, delta); } } @@ -592,62 +660,92 @@ static bool gp_brush_randomize_apply(tGP_BrushEditData *gso, bGPDstroke *gps, in */ const float inf = gp_brush_influence_calc(gso, radius, co) / 2.0f; const float fac = BLI_frand() * inf; - - /* Jitter is applied perpendicular to the mouse movement vector - * - We compute all effects in screenspace (since it's easier) - * and then project these to get the points/distances in - * viewspace as needed - */ - float mvec[2], svec[2]; - - /* mouse movement in ints -> floats */ - mvec[0] = (float)(gso->mval[0] - gso->mval_prev[0]); - mvec[1] = (float)(gso->mval[1] - gso->mval_prev[1]); - - /* rotate mvec by 90 degrees... */ - svec[0] = -mvec[1]; - svec[1] = mvec[0]; - - //printf("svec = %f %f, ", svec[0], svec[1]); - - /* scale the displacement by the random displacement, and apply */ - if (BLI_frand() > 0.5f) { - mul_v2_fl(svec, -fac); - } - else { - mul_v2_fl(svec, fac); + /* need one flag enabled by default */ + if ((gso->settings->flag & (GP_BRUSHEDIT_FLAG_APPLY_POSITION | + GP_BRUSHEDIT_FLAG_APPLY_STRENGTH | + GP_BRUSHEDIT_FLAG_APPLY_THICKNESS)) == 0) + { + gso->settings->flag |= GP_BRUSHEDIT_FLAG_APPLY_POSITION; } - - //printf("%f %f (%f), nco = {%f %f}, co = %d %d\n", svec[0], svec[1], fac, nco[0], nco[1], co[0], co[1]); - - /* convert to dataspace */ - if (gps->flag & GP_STROKE_3DSPACE) { - /* 3D: Project to 3D space */ - if (gso->sa->spacetype == SPACE_VIEW3D) { - bool flip; - RegionView3D *rv3d = gso->ar->regiondata; - float zfac = ED_view3d_calc_zfac(rv3d, &pt->x, &flip); - if (flip == false) { - float dvec[3]; - ED_view3d_win_to_delta(gso->gsc.ar, svec, dvec, zfac); - add_v3_v3(&pt->x, dvec); + + /* apply random to position */ + if (gso->settings->flag & GP_BRUSHEDIT_FLAG_APPLY_POSITION) { + /* Jitter is applied perpendicular to the mouse movement vector + * - We compute all effects in screenspace (since it's easier) + * and then project these to get the points/distances in + * viewspace as needed + */ + float mvec[2], svec[2]; + + /* mouse movement in ints -> floats */ + mvec[0] = (float)(gso->mval[0] - gso->mval_prev[0]); + mvec[1] = (float)(gso->mval[1] - gso->mval_prev[1]); + + /* rotate mvec by 90 degrees... */ + svec[0] = -mvec[1]; + svec[1] = mvec[0]; + + /* scale the displacement by the random displacement, and apply */ + if (BLI_frand() > 0.5f) { + mul_v2_fl(svec, -fac); + } + else { + mul_v2_fl(svec, fac); + } + + //printf("%f %f (%f), nco = {%f %f}, co = %d %d\n", svec[0], svec[1], fac, nco[0], nco[1], co[0], co[1]); + + /* convert to dataspace */ + if (gps->flag & GP_STROKE_3DSPACE) { + /* 3D: Project to 3D space */ + if (gso->sa->spacetype == SPACE_VIEW3D) { + bool flip; + RegionView3D *rv3d = gso->ar->regiondata; + float zfac = ED_view3d_calc_zfac(rv3d, &pt->x, &flip); + if (flip == false) { + float dvec[3]; + ED_view3d_win_to_delta(gso->gsc.ar, svec, dvec, zfac); + add_v3_v3(&pt->x, dvec); + } + } + else { + /* ERROR */ + BLI_assert("3D stroke being sculpted in non-3D view"); } } else { - /* ERROR */ - BLI_assert("3D stroke being sculpted in non-3D view"); + /* 2D: As-is */ + // XXX: v2d scaling/offset? + float nco[2]; + nco[0] = (float)co[0] + svec[0]; + nco[1] = (float)co[1] + svec[1]; + + copy_v2_v2(&pt->x, nco); } } - else { - /* 2D: As-is */ - // XXX: v2d scaling/offset? - float nco[2]; - nco[0] = (float)co[0] + svec[0]; - nco[1] = (float)co[1] + svec[1]; - - copy_v2_v2(&pt->x, nco); + /* apply random to strength */ + if (gso->settings->flag & GP_BRUSHEDIT_FLAG_APPLY_STRENGTH) { + if (BLI_frand() > 0.5f) { + pt->strength += fac; + } + else { + pt->strength -= fac; + } + CLAMP_MIN(pt->strength, 0.0f); + CLAMP_MAX(pt->strength, 1.0f); } - + /* apply random to thickness (use pressure) */ + if (gso->settings->flag & GP_BRUSHEDIT_FLAG_APPLY_THICKNESS) { + if (BLI_frand() > 0.5f) { + pt->pressure += fac; + } + else { + pt->pressure -= fac; + } + /* only limit lower value */ + CLAMP_MIN(pt->pressure, 0.0f); + } + /* done */ return true; } @@ -743,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]; @@ -1087,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); } } } @@ -1099,7 +1197,9 @@ static void gpsculpt_brush_init_stroke(tGP_BrushEditData *gso) /* Apply ----------------------------------------------- */ /* Apply brush operation to points in this stroke */ -static bool gpsculpt_brush_do_stroke(tGP_BrushEditData *gso, bGPDstroke *gps, GP_BrushApplyCb apply) +static bool gpsculpt_brush_do_stroke( + tGP_BrushEditData *gso, bGPDstroke *gps, bool parented, + float diff_mat[4][4], GP_BrushApplyCb apply) { GP_SpaceConversion *gsc = &gso->gsc; rcti *rect = &gso->brush_rect; @@ -1111,9 +1211,16 @@ static bool gpsculpt_brush_do_stroke(tGP_BrushEditData *gso, bGPDstroke *gps, GP int i; bool include_last = false; bool changed = false; - + if (gps->totpoints == 1) { - gp_point_to_xy(gsc, gps, gps->points, &pc1[0], &pc1[1]); + if (!parented) { + gp_point_to_xy(gsc, gps, gps->points, &pc1[0], &pc1[1]); + } + else { + bGPDspoint pt_temp; + gp_point_to_parent_space(gps->points, diff_mat, &pt_temp); + gp_point_to_xy(gsc, gps, &pt_temp, &pc1[0], &pc1[1]); + } /* do boundbox check first */ if ((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) { @@ -1140,10 +1247,20 @@ static bool gpsculpt_brush_do_stroke(tGP_BrushEditData *gso, bGPDstroke *gps, GP continue; } } - - gp_point_to_xy(gsc, gps, pt1, &pc1[0], &pc1[1]); - gp_point_to_xy(gsc, gps, pt2, &pc2[0], &pc2[1]); - + if (!parented) { + gp_point_to_xy(gsc, gps, pt1, &pc1[0], &pc1[1]); + gp_point_to_xy(gsc, gps, pt2, &pc2[0], &pc2[1]); + } + else { + bGPDspoint npt; + gp_point_to_parent_space(pt1, diff_mat, &npt); + gp_point_to_xy(gsc, gps, &npt, &pc1[0], &pc1[1]); + + gp_point_to_parent_space(pt2, diff_mat, &npt); + gp_point_to_xy(gsc, gps, &npt, &pc2[0], &pc2[1]); + } + + /* Check that point segment of the boundbox of the selection stroke */ if (((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) || ((!ELEM(V2D_IS_CLIPPED, pc2[0], pc2[1])) && BLI_rcti_isect_pt(rect, pc2[0], pc2[1]))) @@ -1228,76 +1345,105 @@ static bool gpsculpt_brush_apply_standard(bContext *C, tGP_BrushEditData *gso) /* Find visible strokes, and perform operations on those if hit */ - CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) + float diff_mat[4][4]; + bool parented = false; + + CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) { - switch (gso->brush_type) { - case GP_EDITBRUSH_TYPE_SMOOTH: /* Smooth strokes */ - { - changed |= gpsculpt_brush_do_stroke(gso, gps, gp_brush_smooth_apply); - break; - } - - case GP_EDITBRUSH_TYPE_THICKNESS: /* Adjust stroke thickness */ - { - changed |= gpsculpt_brush_do_stroke(gso, gps, gp_brush_thickness_apply); - break; + /* calculate difference matrix if parent object */ + if (gpl->parent != NULL) { + ED_gpencil_parent_location(gpl, diff_mat); + parented = true; + } + else { + parented = false; + } + + bGPDframe *gpf = gpl->actframe; + bGPDstroke *gps; + for (gps = gpf->strokes.first; gps; gps = gps->next) { + /* 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; } - - case GP_EDITBRUSH_TYPE_GRAB: /* Grab points */ - { - if (gso->first) { - /* First time this brush stroke is being applied: - * 1) Prepare data buffers (init/clear) for this stroke - * 2) Use the points now under the cursor - */ - gp_brush_grab_stroke_init(gso, gps); - changed |= gpsculpt_brush_do_stroke(gso, gps, gp_brush_grab_store_points); + + switch (gso->brush_type) { + case GP_EDITBRUSH_TYPE_SMOOTH: /* Smooth strokes */ + { + changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_smooth_apply); + break; } - else { - /* Apply effect to the stored points */ - gp_brush_grab_apply_cached(gso, gps); - changed |= true; + + case GP_EDITBRUSH_TYPE_THICKNESS: /* Adjust stroke thickness */ + { + changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_thickness_apply); + break; } - break; - } - - case GP_EDITBRUSH_TYPE_PUSH: /* Push points */ - { - changed |= gpsculpt_brush_do_stroke(gso, gps, gp_brush_push_apply); - break; - } - - case GP_EDITBRUSH_TYPE_PINCH: /* Pinch points */ - { - changed |= gpsculpt_brush_do_stroke(gso, gps, gp_brush_pinch_apply); - break; - } - - case GP_EDITBRUSH_TYPE_TWIST: /* Twist points around midpoint */ - { - changed |= gpsculpt_brush_do_stroke(gso, gps, gp_brush_twist_apply); - break; + + case GP_EDITBRUSH_TYPE_STRENGTH: /* Adjust stroke color strength */ + { + changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_strength_apply); + break; + } + + case GP_EDITBRUSH_TYPE_GRAB: /* Grab points */ + { + if (gso->first) { + /* First time this brush stroke is being applied: + * 1) Prepare data buffers (init/clear) for this stroke + * 2) Use the points now under the cursor + */ + gp_brush_grab_stroke_init(gso, gps); + changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_grab_store_points); + } + else { + /* Apply effect to the stored points */ + gp_brush_grab_apply_cached(gso, gps, parented, diff_mat); + changed |= true; + } + break; + } + + case GP_EDITBRUSH_TYPE_PUSH: /* Push points */ + { + changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_push_apply); + break; + } + + case GP_EDITBRUSH_TYPE_PINCH: /* Pinch points */ + { + changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_pinch_apply); + break; + } + + case GP_EDITBRUSH_TYPE_TWIST: /* Twist points around midpoint */ + { + changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_twist_apply); + break; + } + + case GP_EDITBRUSH_TYPE_RANDOMIZE: /* Apply jitter */ + { + changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_randomize_apply); + break; + } + + default: + printf("ERROR: Unknown type of GPencil Sculpt brush - %u\n", gso->brush_type); + break; } - - case GP_EDITBRUSH_TYPE_RANDOMIZE: /* Apply jitter */ - { - changed |= gpsculpt_brush_do_stroke(gso, gps, gp_brush_randomize_apply); - break; + /* Triangulation must be calculated if changed */ + if (changed) { + gps->flag |= GP_STROKE_RECALC_CACHES; + gps->tot_triangles = 0; } - - default: - printf("ERROR: Unknown type of GPencil Sculpt brush - %u\n", gso->brush_type); - break; - } - - /* Triangulation must be calculated if changed */ - if (changed) { - gps->flag |= GP_STROKE_RECALC_CACHES; - gps->tot_triangles = 0; } } CTX_DATA_END; - + return changed; } @@ -1441,6 +1587,11 @@ static int gpsculpt_brush_invoke(bContext *C, wmOperator *op, const wmEvent *eve needs_timer = true; break; + case GP_EDITBRUSH_TYPE_STRENGTH: + brush_rate = 0.01f; // XXX: hardcoded + needs_timer = true; + break; + case GP_EDITBRUSH_TYPE_PINCH: brush_rate = 0.001f; // XXX: hardcoded needs_timer = true; diff --git a/source/blender/editors/gpencil/gpencil_convert.c b/source/blender/editors/gpencil/gpencil_convert.c index c47985ebc1b..c502ed1aa83 100644 --- a/source/blender/editors/gpencil/gpencil_convert.c +++ b/source/blender/editors/gpencil/gpencil_convert.c @@ -142,12 +142,31 @@ static EnumPropertyItem *rna_GPConvert_mode_items(bContext *UNUSED(C), PointerRN /* convert the coordinates from the given stroke point into 3d-coordinates * - assumes that the active space is the 3D-View */ -static void gp_strokepoint_convertcoords(bContext *C, bGPDstroke *gps, bGPDspoint *pt, float p3d[3], rctf *subrect) +static void gp_strokepoint_convertcoords( + bContext *C, bGPDlayer *gpl, bGPDstroke *gps, bGPDspoint *source_pt, + float p3d[3], const rctf *subrect) { Scene *scene = CTX_data_scene(C); View3D *v3d = CTX_wm_view3d(C); ARegion *ar = CTX_wm_region(C); - + bGPDspoint mypt, *pt; + + float diff_mat[4][4]; + pt = &mypt; + + /* calculate difference matrix if parent object */ + if (gpl->parent == NULL) { + copy_v3_v3(&pt->x, &source_pt->x); + } + else { + /* apply parent transform */ + float fpt[3]; + ED_gpencil_parent_location(gpl, diff_mat); + mul_v3_m4v3(fpt, diff_mat, &source_pt->x); + copy_v3_v3(&pt->x, fpt); + } + + if (gps->flag & GP_STROKE_3DSPACE) { /* directly use 3d-coordinates */ copy_v3_v3(p3d, &pt->x); @@ -628,7 +647,7 @@ static void gp_stroke_to_path(bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Curv bp = &nu->bp[old_nbp - 1]; /* First point */ - gp_strokepoint_convertcoords(C, gps, gps->points, p, subrect); + gp_strokepoint_convertcoords(C, gpl, gps, gps->points, p, subrect); if (prev_bp) { interp_v3_v3v3(p1, bp->vec, prev_bp->vec, -GAP_DFAC); if (do_gtd) { @@ -649,7 +668,7 @@ static void gp_stroke_to_path(bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Curv /* Second point */ /* Note dt2 is always negative, which marks the gap. */ if (gps->totpoints > 1) { - gp_strokepoint_convertcoords(C, gps, gps->points + 1, next_p, subrect); + gp_strokepoint_convertcoords(C, gpl, gps, gps->points + 1, next_p, subrect); interp_v3_v3v3(p2, p, next_p, -GAP_DFAC); if (do_gtd) { dt2 = interpf(gps->points[1].time, gps->points[0].time, -GAP_DFAC); @@ -670,9 +689,9 @@ static void gp_stroke_to_path(bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Curv float p[3], next_p[3]; float dt = 0.0f; - gp_strokepoint_convertcoords(C, gps, gps->points, p, subrect); + gp_strokepoint_convertcoords(C, gpl, gps, gps->points, p, subrect); if (gps->totpoints > 1) { - gp_strokepoint_convertcoords(C, gps, gps->points + 1, next_p, subrect); + gp_strokepoint_convertcoords(C, gpl, gps, gps->points + 1, next_p, subrect); interp_v3_v3v3(p, p, next_p, -GAP_DFAC); if (do_gtd) { dt = interpf(gps->points[1].time, gps->points[0].time, -GAP_DFAC); @@ -701,10 +720,10 @@ static void gp_stroke_to_path(bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Curv i++, pt++, bp++) { float p[3]; - float width = pt->pressure * gpl->thickness * WIDTH_CORR_FAC; + float width = pt->pressure * (gps->thickness + gpl->thickness) * WIDTH_CORR_FAC; /* get coordinates to add at */ - gp_strokepoint_convertcoords(C, gps, pt, p, subrect); + gp_strokepoint_convertcoords(C, gpl, gps, pt, p, subrect); gp_stroke_to_path_add_point(gtd, bp, p, (prev_bp) ? prev_bp->vec : p, do_gtd, gps->inittime, pt->time, width, rad_fac, minmax_weights); @@ -816,12 +835,12 @@ static void gp_stroke_to_bezier(bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Cu /* get initial coordinates */ pt = gps->points; if (tot) { - gp_strokepoint_convertcoords(C, gps, pt, (stitch) ? p3d_prev : p3d_cur, subrect); + gp_strokepoint_convertcoords(C, gpl, gps, pt, (stitch) ? p3d_prev : p3d_cur, subrect); if (tot > 1) { - gp_strokepoint_convertcoords(C, gps, pt + 1, (stitch) ? p3d_cur : p3d_next, subrect); + gp_strokepoint_convertcoords(C, gpl, gps, pt + 1, (stitch) ? p3d_cur : p3d_next, subrect); } if (stitch && tot > 2) { - gp_strokepoint_convertcoords(C, gps, pt + 2, p3d_next, subrect); + gp_strokepoint_convertcoords(C, gpl, gps, pt + 2, p3d_next, subrect); } } @@ -940,7 +959,7 @@ static void gp_stroke_to_bezier(bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Cu /* add points */ for (i = stitch ? 1 : 0, bezt = &nu->bezt[old_nbezt]; i < tot; i++, pt++, bezt++) { - float width = pt->pressure * gpl->thickness * WIDTH_CORR_FAC; + float width = pt->pressure * (gps->thickness + gpl->thickness) * WIDTH_CORR_FAC; if (i || old_nbezt) { interp_v3_v3v3(h1, p3d_cur, p3d_prev, BEZT_HANDLE_FAC); @@ -964,7 +983,7 @@ static void gp_stroke_to_bezier(bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Cu copy_v3_v3(p3d_cur, p3d_next); if (i + 2 < tot) { - gp_strokepoint_convertcoords(C, gps, pt + 2, p3d_next, subrect); + gp_strokepoint_convertcoords(C, gpl, gps, pt + 2, p3d_next, subrect); } prev_bezt = bezt; @@ -1104,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; @@ -1216,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 { @@ -1273,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)); } @@ -1283,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 746497f0ff5..6f2ebbed7d7 100644 --- a/source/blender/editors/gpencil/gpencil_data.c +++ b/source/blender/editors/gpencil/gpencil_data.c @@ -18,7 +18,7 @@ * The Original Code is Copyright (C) 2008, Blender Foundation, Joshua Leung * This is a new part of Blender * - * Contributor(s): Joshua Leung + * Contributor(s): Joshua Leung, Antonio Vazquez * * ***** END GPL LICENSE BLOCK ***** * @@ -40,6 +40,8 @@ #include "BLI_blenlib.h" #include "BLI_utildefines.h" +#include "BLI_ghash.h" +#include "BLI_math.h" #include "BLT_translation.h" @@ -57,6 +59,7 @@ #include "BKE_report.h" #include "BKE_scene.h" #include "BKE_screen.h" +#include "BKE_colortools.h" #include "UI_interface.h" #include "UI_resources.h" @@ -72,6 +75,8 @@ #include "gpencil_intern.h" +/* maximum sizes of gp-session buffer */ +#define GP_STROKE_BUFFER_MAX 5000 /* ************************************************ */ /* Datablock Operators */ @@ -92,7 +97,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 */ @@ -179,10 +184,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); @@ -209,7 +214,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)) @@ -225,12 +230,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); @@ -262,7 +267,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"); @@ -316,7 +321,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 */ @@ -324,12 +329,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); @@ -357,7 +362,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 */ @@ -525,7 +530,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; @@ -596,6 +601,61 @@ void GPENCIL_OT_layer_isolate(wmOperatorType *ot) "In addition to toggling the editability, also affect the visibility"); } +/* ********************** Merge Layer with the next layer **************************** */ + +static int gp_merge_layer_exec(bContext *C, wmOperator *op) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDlayer *gpl_current = BKE_gpencil_layer_getactive(gpd); + bGPDlayer *gpl_next = gpl_current->next; + + if (ELEM(NULL, gpd, gpl_current, gpl_next)) { + BKE_report(op->reports, RPT_ERROR, "No layers to merge"); + return OPERATOR_CANCELLED; + } + + /* Collect frames of gpl_current in hash table to avoid O(n^2) lookups */ + GHash *gh_frames_cur = BLI_ghash_int_new_ex(__func__, 64); + for (bGPDframe *gpf = gpl_current->frames.first; gpf; gpf = gpf->next) { + BLI_ghash_insert(gh_frames_cur, SET_INT_IN_POINTER(gpf->framenum), gpf); + } + + /* read all frames from next layer */ + for (bGPDframe *gpf = gpl_next->frames.first; gpf; gpf = gpf->next) { + /* try to find frame in active layer */ + bGPDframe *frame = BLI_ghash_lookup(gh_frames_cur, SET_INT_IN_POINTER(gpf->framenum)); + if (!frame) { + /* nothing found, create new */ + frame = BKE_gpencil_frame_addnew(gpl_current, gpf->framenum); + } + /* add to tail all strokes */ + BLI_movelisttolist(&frame->strokes, &gpf->strokes); + } + /* Now delete next layer */ + BKE_gpencil_layer_delete(gpd, gpl_next); + BLI_ghash_free(gh_frames_cur, NULL, NULL); + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_layer_merge(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Merge Down"; + ot->idname = "GPENCIL_OT_layer_merge"; + ot->description = "Merge the current layer with the layer below"; + + /* callbacks */ + ot->exec = gp_merge_layer_exec; + ot->poll = gp_active_layer_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + /* ********************** Change Layer ***************************** */ static int gp_layer_change_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(evt)) @@ -621,7 +681,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 */ @@ -634,7 +694,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); @@ -663,3 +723,1741 @@ void GPENCIL_OT_layer_change(wmOperatorType *ot) } /* ************************************************ */ + +/* ******************* Arrange Stroke Up/Down in drawing order ************************** */ + +enum { + GP_STROKE_MOVE_UP = -1, + GP_STROKE_MOVE_DOWN = 1, + GP_STROKE_MOVE_TOP = 2, + GP_STROKE_MOVE_BOTTOM = 3 +}; + +static int gp_stroke_arrange_exec(bContext *C, wmOperator *op) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); + bGPDstroke *gps; + + /* sanity checks */ + if (ELEM(NULL, gpd, gpl, gpl->actframe)) { + return OPERATOR_CANCELLED; + } + + bGPDframe *gpf = gpl->actframe; + /* temp listbase to store selected strokes */ + ListBase selected = {NULL}; + const int direction = RNA_enum_get(op->ptr, "type"); + + /* 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) { + /* only if selected */ + 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; + } + /* some stroke is already at front*/ + if ((direction == GP_STROKE_MOVE_TOP) || (direction == GP_STROKE_MOVE_UP)) { + if (gps == gpf->strokes.last) { + BKE_report(op->reports, RPT_ERROR, "Some selected stroke is already on top"); + return OPERATOR_CANCELLED; + } + } + /* some stroke is already at botom */ + if ((direction == GP_STROKE_MOVE_BOTTOM) || (direction == GP_STROKE_MOVE_DOWN)) { + if (gps == gpf->strokes.first) { + BKE_report(op->reports, RPT_ERROR, "Some selected stroke is already on bottom"); + return OPERATOR_CANCELLED; + } + } + /* add to list */ + BLI_addtail(&selected, BLI_genericNodeN(gps)); + } + } + + /* Now do the movement of the stroke */ + switch (direction) { + /* Bring to Front */ + case GP_STROKE_MOVE_TOP: + for (LinkData *link = selected.first; link; link = link->next) { + gps = link->data; + BLI_remlink(&gpf->strokes, gps); + BLI_insertlinkafter(&gpf->strokes, gpf->strokes.last, gps); + } + break; + /* Bring Forward */ + case GP_STROKE_MOVE_UP: + for (LinkData *link = selected.last; link; link = link->prev) { + gps = link->data; + BLI_remlink(&gpf->strokes, gps); + BLI_insertlinkafter(&gpf->strokes, gps->next, gps); + } + break; + /* Send Backward */ + case GP_STROKE_MOVE_DOWN: + for (LinkData *link = selected.first; link; link = link->next) { + gps = link->data; + BLI_remlink(&gpf->strokes, gps); + BLI_insertlinkbefore(&gpf->strokes, gps->prev, gps); + } + break; + /* Send to Back */ + case GP_STROKE_MOVE_BOTTOM: + for (LinkData *link = selected.last; link; link = link->prev) { + gps = link->data; + BLI_remlink(&gpf->strokes, gps); + BLI_insertlinkbefore(&gpf->strokes, gpf->strokes.first, gps); + } + break; + default: + BLI_assert(0); + break; + } + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_stroke_arrange(wmOperatorType *ot) +{ + static EnumPropertyItem slot_move[] = { + {GP_STROKE_MOVE_UP, "UP", 0, "Bring Forward", ""}, + {GP_STROKE_MOVE_DOWN, "DOWN", 0, "Send Backward", ""}, + {GP_STROKE_MOVE_TOP, "TOP", 0, "Bring to Front", ""}, + {GP_STROKE_MOVE_BOTTOM, "BOTTOM", 0, "Send to Back", ""}, + {0, NULL, 0, NULL, NULL } + }; + + /* identifiers */ + ot->name = "Arrange Stroke"; + ot->idname = "GPENCIL_OT_stroke_arrange"; + ot->description = "Arrange selected strokes up/down in the drawing order of the active layer"; + + /* api callbacks */ + ot->exec = gp_stroke_arrange_exec; + ot->poll = gp_active_layer_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + ot->prop = RNA_def_enum(ot->srna, "direction", slot_move, GP_STROKE_MOVE_UP, "Direction", ""); +} +/* ******************* Move Stroke to new color ************************** */ + +static int gp_stroke_change_color_exec(bContext *C, wmOperator *UNUSED(op)) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDpalette *palette; + bGPDpalettecolor *color; + + /* sanity checks */ + if (ELEM(NULL, gpd)) { + return OPERATOR_CANCELLED; + } + + palette = BKE_gpencil_palette_getactive(gpd); + color = BKE_gpencil_palettecolor_getactive(palette); + if (ELEM(NULL, palette, color)) { + return OPERATOR_CANCELLED; + } + + /* loop all strokes */ + 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)) { + for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) { + /* only if selected */ + 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; + + /* asign new color (only if different) */ + if (STREQ(gps->colorname, color->info) == false) { + strcpy(gps->colorname, color->info); + gps->flag |= GP_STROKE_RECALC_COLOR; + } + } + } + } + } + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_stroke_change_color(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Change Stroke Color"; + ot->idname = "GPENCIL_OT_stroke_change_color"; + ot->description = "Move selected strokes to active color"; + + /* api callbacks */ + ot->exec = gp_stroke_change_color_exec; + ot->poll = gp_active_layer_poll; +} + +/* ******************* Lock color of non selected Strokes colors ************************** */ + +static int gp_stroke_lock_color_exec(bContext *C, wmOperator *UNUSED(op)) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDpalette *palette; + + /* sanity checks */ + if (ELEM(NULL, gpd)) + return OPERATOR_CANCELLED; + + palette = BKE_gpencil_palette_getactive(gpd); + if (ELEM(NULL, palette)) + return OPERATOR_CANCELLED; + + /* first lock all colors */ + for (bGPDpalettecolor *palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { + palcolor->flag |= PC_COLOR_LOCKED; + } + + /* loop all selected strokes and unlock any color */ + 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)) { + for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) { + /* only if selected */ + if (gps->flag & GP_STROKE_SELECT) { + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) { + continue; + } + /* unlock color */ + if (gps->palcolor != NULL) { + gps->palcolor->flag &= ~PC_COLOR_LOCKED; + } + } + } + } + } + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_stroke_lock_color(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Lock Unused Colors"; + ot->idname = "GPENCIL_OT_stroke_lock_color"; + ot->description = "Lock any color not used in any selected stroke"; + + /* api callbacks */ + ot->exec = gp_stroke_lock_color_exec; + 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 = 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; + + 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, const bool leave_gaps) +{ + 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); + } + + /* 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; + 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, 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; + + 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; + 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; +} + +/* ************************************************ */ +/* Drawing Brushes Operators */ + +/* ******************* Add New Brush ************************ */ + +/* add new brush - wrapper around API */ +static int gp_brush_add_exec(bContext *C, wmOperator *op) +{ + ToolSettings *ts = CTX_data_tool_settings(C); + + /* if there's no existing container */ + if (ts == NULL) { + BKE_report(op->reports, RPT_ERROR, "Nowhere for brush data to go"); + return OPERATOR_CANCELLED; + } + /* add new brush now */ + BKE_gpencil_brush_addnew(ts, DATA_("GP_Brush"), true); + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_brush_add(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add Brush"; + ot->idname = "GPENCIL_OT_brush_add"; + ot->description = "Add new Grease Pencil drawing brush for the active Grease Pencil datablock"; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* callbacks */ + ot->exec = gp_brush_add_exec; + ot->poll = gp_add_poll; +} + +/* ******************* Remove Active Brush ************************* */ + +static int gp_brush_remove_exec(bContext *C, wmOperator *op) +{ + ToolSettings *ts = CTX_data_tool_settings(C); + 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"); + return OPERATOR_CANCELLED; + } + + + /* make the brush before this the new active brush + * - use the one after if this is the first + * - if this is the only brush, this naturally becomes NULL + */ + if (brush->prev) + BKE_gpencil_brush_setactive(ts, brush->prev); + else + BKE_gpencil_brush_setactive(ts, brush->next); + + /* delete the brush now... */ + BKE_gpencil_brush_delete(ts, brush); + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_brush_remove(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Remove brush"; + ot->idname = "GPENCIL_OT_brush_remove"; + ot->description = "Remove active Grease Pencil drawing brush"; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* callbacks */ + ot->exec = gp_brush_remove_exec; + ot->poll = gp_active_brush_poll; +} + +/* ********************** Change Brush ***************************** */ + +static int gp_brush_change_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(evt)) +{ + uiPopupMenu *pup; + uiLayout *layout; + + /* call the menu, which will call this operator again, hence the canceled */ + pup = UI_popup_menu_begin(C, op->type->name, ICON_NONE); + layout = UI_popup_menu_layout(pup); + uiItemsEnumO(layout, "GPENCIL_OT_brush_change", "brush"); + UI_popup_menu_end(C, pup); + + return OPERATOR_INTERFACE; +} + +static int gp_brush_change_exec(bContext *C, wmOperator *op) +{ + ToolSettings *ts = CTX_data_tool_settings(C); + bGPDbrush *brush = NULL; + int brush_num = RNA_enum_get(op->ptr, "brush"); + + /* Get brush or create new one */ + if (brush_num == -1) { + /* Create brush */ + brush = BKE_gpencil_brush_addnew(ts, DATA_("GP_Brush"), true); + } + else { + /* Try to get brush */ + brush = BLI_findlink(&ts->gp_brushes, brush_num); + + if (brush == NULL) { + BKE_reportf(op->reports, RPT_ERROR, "Cannot change to non-existent brush (index = %d)", brush_num); + return OPERATOR_CANCELLED; + } + } + + /* Set active brush */ + BKE_gpencil_brush_setactive(ts, brush); + + /* updates */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_brush_change(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Change Brush"; + ot->idname = "GPENCIL_OT_brush_change"; + ot->description = "Change active Grease Pencil drawing brush"; + + /* callbacks */ + ot->invoke = gp_brush_change_invoke; + ot->exec = gp_brush_change_exec; + ot->poll = gp_active_brush_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* gp brush to use (dynamic enum) */ + ot->prop = RNA_def_enum(ot->srna, "brush", DummyRNA_DEFAULT_items, 0, "Grease Pencil Brush", ""); + RNA_def_enum_funcs(ot->prop, ED_gpencil_brushes_enum_itemf); +} + +/* ******************* Move Brush Up/Down ************************** */ + +enum { + GP_BRUSH_MOVE_UP = -1, + GP_BRUSH_MOVE_DOWN = 1 +}; + +static int gp_brush_move_exec(bContext *C, wmOperator *op) +{ + ToolSettings *ts = CTX_data_tool_settings(C); + bGPDbrush *brush = BKE_gpencil_brush_getactive(ts); + + int direction = RNA_enum_get(op->ptr, "type"); + + /* sanity checks */ + if (ELEM(NULL, ts, brush)) { + return OPERATOR_CANCELLED; + } + + /* up or down? */ + if (direction == GP_BRUSH_MOVE_UP) { + /* up */ + BLI_remlink(&ts->gp_brushes, brush); + BLI_insertlinkbefore(&ts->gp_brushes, brush->prev, brush); + } + else if (direction == GP_BRUSH_MOVE_DOWN) { + /* down */ + BLI_remlink(&ts->gp_brushes, brush); + BLI_insertlinkafter(&ts->gp_brushes, brush->next, brush); + } + else { + BLI_assert(0); + } + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_brush_move(wmOperatorType *ot) +{ + static EnumPropertyItem slot_move[] = { + {GP_BRUSH_MOVE_UP, "UP", 0, "Up", ""}, + {GP_BRUSH_MOVE_DOWN, "DOWN", 0, "Down", ""}, + {0, NULL, 0, NULL, NULL } + }; + + /* identifiers */ + ot->name = "Move Brush"; + ot->idname = "GPENCIL_OT_brush_move"; + ot->description = "Move the active Grease Pencil drawing brush up/down in the list"; + + /* api callbacks */ + ot->exec = gp_brush_move_exec; + ot->poll = gp_active_brush_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + ot->prop = RNA_def_enum(ot->srna, "type", slot_move, GP_BRUSH_MOVE_UP, "Type", ""); +} + +/* ******************* Brush create presets ************************** */ + +static int gp_brush_presets_create_exec(bContext *C, wmOperator *UNUSED(op)) +{ + ToolSettings *ts = CTX_data_tool_settings(C); + BKE_gpencil_brush_init_presets(ts); + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_brush_presets_create(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Create Preset Brushes"; + ot->idname = "GPENCIL_OT_brush_presets_create"; + ot->description = "Create a set of predefined Grease Pencil drawing brushes"; + + /* api callbacks */ + ot->exec = gp_brush_presets_create_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + +} + +/* ***************** Copy Brush ************************ */ + +static int gp_brush_copy_exec(bContext *C, wmOperator *op) +{ + ToolSettings *ts = CTX_data_tool_settings(C); + + /* if there's no existing container */ + if (ts == NULL) { + BKE_report(op->reports, RPT_ERROR, "Nowhere for brush data to go"); + return OPERATOR_CANCELLED; + } + + bGPDbrush *brush = BKE_gpencil_brush_getactive(ts); + bGPDbrush *newbrush; + + /* sanity checks */ + if (ELEM(NULL, brush)) + return OPERATOR_CANCELLED; + + /* create a brush and duplicate data */ + 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; + newbrush->sublevel = brush->sublevel; + newbrush->flag = brush->flag; + newbrush->draw_sensitivity = brush->draw_sensitivity; + newbrush->draw_strength = brush->draw_strength; + newbrush->draw_jitter = brush->draw_jitter; + newbrush->draw_angle = brush->draw_angle; + newbrush->draw_angle_factor = brush->draw_angle_factor; + newbrush->draw_random_press = brush->draw_random_press; + newbrush->draw_random_sub = brush->draw_random_sub; + + /* free automatic curves created by default (replaced by copy) */ + curvemapping_free(newbrush->cur_sensitivity); + curvemapping_free(newbrush->cur_strength); + curvemapping_free(newbrush->cur_jitter); + + /* make a copy of curves */ + newbrush->cur_sensitivity = curvemapping_copy(brush->cur_sensitivity); + newbrush->cur_strength = curvemapping_copy(brush->cur_strength); + newbrush->cur_jitter = curvemapping_copy(brush->cur_jitter); + + BKE_gpencil_brush_setactive(ts, newbrush); + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_brush_copy(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Copy Brush"; + ot->idname = "GPENCIL_OT_brush_copy"; + ot->description = "Copy current Grease Pencil drawing brush"; + + /* callbacks */ + ot->exec = gp_brush_copy_exec; + ot->poll = gp_active_brush_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* ***************** Select Brush ************************ */ + +static int gp_brush_select_exec(bContext *C, wmOperator *op) +{ + ToolSettings *ts = CTX_data_tool_settings(C); + + /* if there's no existing container */ + if (ts == NULL) { + BKE_report(op->reports, RPT_ERROR, "Nowhere to go"); + return OPERATOR_CANCELLED; + } + + const int index = RNA_int_get(op->ptr, "index"); + bGPDbrush *brush = BLI_findlink(&ts->gp_brushes, index); + /* sanity checks */ + if (ELEM(NULL, brush)) { + return OPERATOR_CANCELLED; + } + + BKE_gpencil_brush_setactive(ts, brush); + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_brush_select(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Select Brush"; + ot->idname = "GPENCIL_OT_brush_select"; + ot->description = "Select a Grease Pencil drawing brush"; + + /* callbacks */ + ot->exec = gp_brush_select_exec; + ot->poll = gp_active_brush_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "Index of Drawing Brush", 0, INT_MAX); +} + +/* ************************************************ */ +/* Palette Operators */ + +/* ******************* Add New Palette ************************ */ + +/* add new palette - wrapper around API */ +static int gp_palette_add_exec(bContext *C, wmOperator *op) +{ + bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL); + + /* if there's no existing Grease-Pencil data there, add some */ + if (gpd_ptr == NULL) { + BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go"); + return OPERATOR_CANCELLED; + } + if (*gpd_ptr == NULL) + *gpd_ptr = BKE_gpencil_data_addnew(DATA_("GPencil")); + + /* add new palette now */ + BKE_gpencil_palette_addnew(*gpd_ptr, DATA_("GP_Palette"), true); + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_palette_add(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add Palette"; + ot->idname = "GPENCIL_OT_palette_add"; + ot->description = "Add new Grease Pencil palette for the active Grease Pencil datablock"; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* callbacks */ + ot->exec = gp_palette_add_exec; + ot->poll = gp_add_poll; +} + +/* ******************* Remove Active Palette ************************* */ + +static int gp_palette_remove_exec(bContext *C, wmOperator *op) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + 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"); + return OPERATOR_CANCELLED; + } + + + /* make the palette before this the new active palette + * - use the one after if this is the first + * - if this is the only palette, this naturally becomes NULL + */ + if (palette->prev) + BKE_gpencil_palette_setactive(gpd, palette->prev); + else + BKE_gpencil_palette_setactive(gpd, palette->next); + + /* delete the palette now... */ + BKE_gpencil_palette_delete(gpd, palette); + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_palette_remove(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Remove palette"; + ot->idname = "GPENCIL_OT_palette_remove"; + ot->description = "Remove active Grease Pencil palette"; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* callbacks */ + ot->exec = gp_palette_remove_exec; + ot->poll = gp_active_palette_poll; +} + +/* ********************** Change Palette ***************************** */ + +static int gp_palette_change_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(evt)) +{ + uiPopupMenu *pup; + uiLayout *layout; + + /* call the menu, which will call this operator again, hence the canceled */ + pup = UI_popup_menu_begin(C, op->type->name, ICON_NONE); + layout = UI_popup_menu_layout(pup); + uiItemsEnumO(layout, "GPENCIL_OT_palette_change", "palette"); + UI_popup_menu_end(C, pup); + + return OPERATOR_INTERFACE; +} + +static int gp_palette_change_exec(bContext *C, wmOperator *op) +{ + bGPdata *gpd = CTX_data_gpencil_data(C); + bGPDpalette *palette = NULL; + int palette_num = RNA_enum_get(op->ptr, "palette"); + + /* Get palette or create new one */ + if (palette_num == -1) { + /* Create palette */ + palette = BKE_gpencil_palette_addnew(gpd, DATA_("GP_Palette"), true); + } + else { + /* Try to get palette */ + palette = BLI_findlink(&gpd->palettes, palette_num); + + if (palette == NULL) { + BKE_reportf(op->reports, RPT_ERROR, "Cannot change to non-existent palette (index = %d)", palette_num); + return OPERATOR_CANCELLED; + } + } + + /* Set active palette */ + BKE_gpencil_palette_setactive(gpd, palette); + + /* updates */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_palette_change(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Change Palette"; + ot->idname = "GPENCIL_OT_palette_change"; + ot->description = "Change active Grease Pencil palette"; + + /* callbacks */ + ot->invoke = gp_palette_change_invoke; + ot->exec = gp_palette_change_exec; + ot->poll = gp_active_palette_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* gp palette to use (dynamic enum) */ + ot->prop = RNA_def_enum(ot->srna, "palette", DummyRNA_DEFAULT_items, 0, "Grease Pencil Palette", ""); + RNA_def_enum_funcs(ot->prop, ED_gpencil_palettes_enum_itemf); +} + +/* ******************* Lock and hide any color non used in current layer ************************** */ + +static int gp_palette_lock_layer_exec(bContext *C, wmOperator *UNUSED(op)) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDpalette *palette; + + /* sanity checks */ + if (ELEM(NULL, gpd)) + return OPERATOR_CANCELLED; + + palette = BKE_gpencil_palette_getactive(gpd); + if (ELEM(NULL, palette)) + return OPERATOR_CANCELLED; + + /* first lock and hide all colors */ + for (bGPDpalettecolor *palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { + palcolor->flag |= PC_COLOR_LOCKED; + palcolor->flag |= PC_COLOR_HIDE; + } + + /* loop all selected strokes and unlock any color used in active layer */ + 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) && (gpl->flag & GP_LAYER_ACTIVE)) { + for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) { + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) + continue; + + /* unlock/unhide color if not unlocked before */ + if (gps->palcolor != NULL) { + gps->palcolor->flag &= ~PC_COLOR_LOCKED; + gps->palcolor->flag &= ~PC_COLOR_HIDE; + } + } + } + } + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_palette_lock_layer(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Disable Unused Layer Colors"; + ot->idname = "GPENCIL_OT_palette_lock_layer"; + ot->description = "Lock and hide any color not used in any layer"; + + /* api callbacks */ + ot->exec = gp_palette_lock_layer_exec; + ot->poll = gp_active_layer_poll; +} + +/* ************************************************ */ +/* Palette Colors Operators */ + +/* ******************* Add New Palette ************************ */ + +/* add new palette - wrapper around API */ +static int gp_palettecolor_add_exec(bContext *C, wmOperator *op) +{ + bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL); + + /* if there's no existing Grease-Pencil data there, add some */ + if (gpd_ptr == NULL) { + BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go"); + return OPERATOR_CANCELLED; + } + if (*gpd_ptr == NULL) + *gpd_ptr = BKE_gpencil_data_addnew(DATA_("GPencil")); + + /* verify palette */ + bGPDpalette *palette = BKE_gpencil_palette_getactive(*gpd_ptr); + if (palette == NULL) + palette = BKE_gpencil_palette_addnew(*gpd_ptr, DATA_("GP_Palette"), true); + + /* add new palette color now */ + BKE_gpencil_palettecolor_addnew(palette, DATA_("Color"), true); + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_palettecolor_add(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add Palette Color"; + ot->idname = "GPENCIL_OT_palettecolor_add"; + ot->description = "Add new Grease Pencil palette color for the active Grease Pencil datablock"; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* callbacks */ + ot->exec = gp_palettecolor_add_exec; + ot->poll = gp_add_poll; +} + +/* ******************* Remove Active Palette color ************************* */ + +static int gp_palettecolor_remove_exec(bContext *C, wmOperator *UNUSED(op)) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); + bGPDpalettecolor *color = BKE_gpencil_palettecolor_getactive(palette); + + /* sanity checks */ + if (ELEM(NULL, gpd, palette, color)) + return OPERATOR_CANCELLED; + + /* make the palette color before this the new active color + * - use the one after if this is the first + * - if this is the only color, this naturally becomes NULL + */ + if (color->prev) + BKE_gpencil_palettecolor_setactive(palette, color->prev); + else + BKE_gpencil_palettecolor_setactive(palette, color->next); + + /* delete the strokes */ + BKE_gpencil_palettecolor_delete_strokes(gpd, color->info); + + /* delete the palette color now... */ + BKE_gpencil_palettecolor_delete(palette, color); + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_palettecolor_remove(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Remove palette color"; + ot->idname = "GPENCIL_OT_palettecolor_remove"; + ot->description = "Remove active Grease Pencil palette color"; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* callbacks */ + ot->exec = gp_palettecolor_remove_exec; + ot->poll = gp_active_palettecolor_poll; +} + +/* ********************** Isolate palette color **************************** */ + +static int gp_isolate_palettecolor_exec(bContext *C, wmOperator *op) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); + bGPDpalettecolor *active_color = BKE_gpencil_palettecolor_getactive(palette); + bGPDpalettecolor *palcolor; + + int flags = PC_COLOR_LOCKED; + bool isolate = false; + + if (RNA_boolean_get(op->ptr, "affect_visibility")) + flags |= PC_COLOR_HIDE; + + if (ELEM(NULL, gpd, active_color)) { + BKE_report(op->reports, RPT_ERROR, "No active color to isolate"); + return OPERATOR_CANCELLED; + } + + /* Test whether to isolate or clear all flags */ + for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { + /* Skip if this is the active one */ + if (palcolor == active_color) + continue; + + /* If the flags aren't set, that means that the color is + * not alone, so we have some colors to isolate still + */ + if ((palcolor->flag & flags) == 0) { + isolate = true; + break; + } + } + + /* Set/Clear flags as appropriate */ + if (isolate) { + /* Set flags on all "other" colors */ + for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { + if (palcolor == active_color) + continue; + else + palcolor->flag |= flags; + } + } + else { + /* Clear flags - Restore everything else */ + for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { + palcolor->flag &= ~flags; + } + } + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_palettecolor_isolate(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Isolate Palette Color"; + ot->idname = "GPENCIL_OT_palettecolor_isolate"; + ot->description = "Toggle whether the active color is the only one that is editable and/or visible"; + + /* callbacks */ + ot->exec = gp_isolate_palettecolor_exec; + ot->poll = gp_active_palettecolor_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + RNA_def_boolean(ot->srna, "affect_visibility", false, "Affect Visibility", "In addition to toggling " + "the editability, also affect the visibility"); +} + +/* *********************** Hide Palette colors ******************************** */ + +static int gp_palettecolor_hide_exec(bContext *C, wmOperator *op) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); + bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette); + + bool unselected = RNA_boolean_get(op->ptr, "unselected"); + + /* sanity checks */ + if (ELEM(NULL, gpd, palette, palcolor)) + return OPERATOR_CANCELLED; + + if (unselected) { + bGPDpalettecolor *color; + + /* hide unselected */ + for (color = palette->colors.first; color; color = color->next) { + if (color != palcolor) { + color->flag |= PC_COLOR_HIDE; + } + } + } + else { + /* hide selected/active */ + palcolor->flag |= PC_COLOR_HIDE; + } + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_palettecolor_hide(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Hide Color(s)"; + ot->idname = "GPENCIL_OT_palettecolor_hide"; + ot->description = "Hide selected/unselected Grease Pencil colors"; + + /* callbacks */ + ot->exec = gp_palettecolor_hide_exec; + ot->poll = gp_active_palettecolor_poll; /* NOTE: we need an active color to play with */ + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* props */ + RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected colors"); +} + +/* ********************** Show All Colors ***************************** */ + +/* poll callback for showing colors */ +static int gp_palettecolor_reveal_poll(bContext *C) +{ + return ED_gpencil_data_get_active(C) != NULL; +} + +static int gp_palettecolor_reveal_exec(bContext *C, wmOperator *UNUSED(op)) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); + bGPDpalettecolor *palcolor; + + /* sanity checks */ + if (ELEM(NULL, gpd, palette)) + return OPERATOR_CANCELLED; + + /* make all colors visible */ + for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { + palcolor->flag &= ~PC_COLOR_HIDE; + } + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_palettecolor_reveal(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Show All Colors"; + ot->idname = "GPENCIL_OT_palettecolor_reveal"; + ot->description = "Unhide all hidden Grease Pencil palette colors"; + + /* callbacks */ + ot->exec = gp_palettecolor_reveal_exec; + ot->poll = gp_palettecolor_reveal_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* ***************** Lock/Unlock All Palette colors ************************ */ + +static int gp_palettecolor_lock_all_exec(bContext *C, wmOperator *UNUSED(op)) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); + bGPDpalettecolor *palcolor; + + /* sanity checks */ + if (ELEM(NULL, gpd, palette)) + return OPERATOR_CANCELLED; + + /* make all layers non-editable */ + for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { + palcolor->flag |= PC_COLOR_LOCKED; + } + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_palettecolor_lock_all(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Lock All Colors"; + ot->idname = "GPENCIL_OT_palettecolor_lock_all"; + ot->description = "Lock all Grease Pencil colors to prevent them from being accidentally modified"; + + /* callbacks */ + ot->exec = gp_palettecolor_lock_all_exec; + ot->poll = gp_palettecolor_reveal_poll; /* XXX: could use dedicated poll later */ + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* -------------------------- */ + +static int gp_palettecolor_unlock_all_exec(bContext *C, wmOperator *UNUSED(op)) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); + bGPDpalettecolor *palcolor; + + /* sanity checks */ + if (ELEM(NULL, gpd, palette)) + return OPERATOR_CANCELLED; + + /* make all layers editable again*/ + for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { + palcolor->flag &= ~PC_COLOR_LOCKED; + } + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_palettecolor_unlock_all(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Unlock All Colors"; + ot->idname = "GPENCIL_OT_palettecolor_unlock_all"; + ot->description = "Unlock all Grease Pencil colors so that they can be edited"; + + /* callbacks */ + ot->exec = gp_palettecolor_unlock_all_exec; + ot->poll = gp_palettecolor_reveal_poll; /* XXX: could use dedicated poll later */ + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* ******************* Move Color Up/Down ************************** */ + +enum { + GP_COLOR_MOVE_UP = -1, + GP_COLOR_MOVE_DOWN = 1 +}; + +static int gp_palettecolor_move_exec(bContext *C, wmOperator *op) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); + bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette); + + int direction = RNA_enum_get(op->ptr, "direction"); + + /* sanity checks */ + if (ELEM(NULL, gpd, palette, palcolor)) + return OPERATOR_CANCELLED; + + /* up or down? */ + if (direction == GP_COLOR_MOVE_UP) { + /* up */ + BLI_remlink(&palette->colors, palcolor); + BLI_insertlinkbefore(&palette->colors, palcolor->prev, palcolor); + } + else if (direction == GP_COLOR_MOVE_DOWN) { + /* down */ + BLI_remlink(&palette->colors, palcolor); + BLI_insertlinkafter(&palette->colors, palcolor->next, palcolor); + } + else { + BLI_assert(0); + } + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_palettecolor_move(wmOperatorType *ot) +{ + static EnumPropertyItem slot_move[] = { + {GP_COLOR_MOVE_UP, "UP", 0, "Up", ""}, + {GP_COLOR_MOVE_DOWN, "DOWN", 0, "Down", ""}, + {0, NULL, 0, NULL, NULL} + }; + + /* identifiers */ + ot->name = "Move Palette color"; + ot->idname = "GPENCIL_OT_palettecolor_move"; + ot->description = "Move the active Grease Pencil palette color up/down in the list"; + + /* api callbacks */ + ot->exec = gp_palettecolor_move_exec; + ot->poll = gp_palettecolor_reveal_poll; /* XXX: could use dedicated poll later */ + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + ot->prop = RNA_def_enum(ot->srna, "direction", slot_move, GP_COLOR_MOVE_UP, "Direction", ""); +} + +/* ***************** Select all strokes using Palette color ************************ */ + +static int gp_palettecolor_select_exec(bContext *C, wmOperator *UNUSED(op)) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); + bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette); + + /* sanity checks */ + if (ELEM(NULL, gpd, palette, palcolor)) + return OPERATOR_CANCELLED; + + /* read all strokes and select*/ + 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)) { + /* verify something to do */ + for (bGPDstroke *gps = gpl->actframe->strokes.first; gps; gps = gps->next) { + /* 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; + + /* select */ + if (strcmp(palcolor->info, gps->colorname) == 0) { + bGPDspoint *pt; + int i; + + gps->flag |= GP_STROKE_SELECT; + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + pt->flag |= GP_SPOINT_SELECT; + } + } + } + } + } + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_palettecolor_select(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Select Color"; + ot->idname = "GPENCIL_OT_palettecolor_select"; + ot->description = "Select all Grease Pencil strokes using current color"; + + /* callbacks */ + ot->exec = gp_palettecolor_select_exec; + ot->poll = gp_palettecolor_reveal_poll; /* XXX: could use dedicated poll later */ + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* ***************** Copy Palette color ************************ */ + +static int gp_palettecolor_copy_exec(bContext *C, wmOperator *UNUSED(op)) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); + bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette); + bGPDpalettecolor *newcolor; + + /* sanity checks */ + if (ELEM(NULL, gpd, palette, palcolor)) + return OPERATOR_CANCELLED; + + /* create a new color and duplicate data */ + 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; + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_palettecolor_copy(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Copy Color"; + ot->idname = "GPENCIL_OT_palettecolor_copy"; + ot->description = "Copy current Grease Pencil palette color"; + + /* callbacks */ + ot->exec = gp_palettecolor_copy_exec; + ot->poll = gp_active_palettecolor_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index ac49a51c716..b298769c6dc 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -125,10 +125,48 @@ static int gp_stroke_edit_poll(bContext *C) return CTX_DATA_COUNT(C, editable_gpencil_strokes) != 0; } +/* ************ Stroke Hide selection Toggle ************** */ + +static int gpencil_hideselect_toggle_exec(bContext *C, wmOperator *UNUSED(op)) +{ + ToolSettings *ts = CTX_data_tool_settings(C); + + if (ts == NULL) + return OPERATOR_CANCELLED; + + /* Just toggle alpha... */ + if (ts->gp_sculpt.alpha > 0.0f) { + ts->gp_sculpt.alpha = 0.0f; + } + else { + ts->gp_sculpt.alpha = 1.0f; + } + + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | ND_GPENCIL_EDITMODE, NULL); + WM_event_add_notifier(C, NC_SCENE | ND_MODE, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_selection_opacity_toggle(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Hide Selection"; + ot->idname = "GPENCIL_OT_selection_opacity_toggle"; + ot->description = "Hide/Unhide selected points for Grease Pencil strokes setting alpha factor"; + + /* callbacks */ + ot->exec = gpencil_hideselect_toggle_exec; + ot->poll = gp_stroke_edit_poll; + + /* flags */ + ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; +} + /* ************** Duplicate Selected Strokes **************** */ /* Make copies of selected point segments in a selected stroke */ -static void gp_duplicate_points(const bGPDstroke *gps, ListBase *new_strokes) +static void gp_duplicate_points(const bGPDstroke *gps, ListBase *new_strokes, const char *layername) { bGPDspoint *pt; int i; @@ -169,6 +207,7 @@ static void gp_duplicate_points(const bGPDstroke *gps, ListBase *new_strokes) /* 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 */ /* initialize triangle memory - will be calculated on next redraw */ gpsd->triangles = NULL; @@ -216,8 +255,9 @@ static int gp_duplicate_exec(bContext *C, wmOperator *op) /* make copies of selected strokes, and deselect these once we're done */ for (gps = gpf->strokes.first; gps; gps = gps->next) { /* skip strokes that are invalid for current view */ - if (ED_gpencil_stroke_can_use(C, gps) == false) + if (ED_gpencil_stroke_can_use(C, gps) == false) { continue; + } if (gps->flag & GP_STROKE_SELECT) { if (gps->totpoints == 1) { @@ -226,8 +266,9 @@ 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); gpsd->points = MEM_dupallocN(gps->points); - + /* triangle information - will be calculated on next redraw */ gpsd->flag |= GP_STROKE_RECALC_CACHES; gpsd->triangles = NULL; @@ -238,7 +279,7 @@ static int gp_duplicate_exec(bContext *C, wmOperator *op) } else { /* delegate to a helper, as there's too much to fit in here (for copying subsets)... */ - gp_duplicate_points(gps, &new_strokes); + gp_duplicate_points(gps, &new_strokes, gpl->info); } /* deselect original stroke, or else the originals get moved too @@ -345,6 +386,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 */ gpsd->points = MEM_dupallocN(gps->points); /* triangles cache - will be recalculated on next redraw */ @@ -358,7 +400,7 @@ static int gp_strokes_copy_exec(bContext *C, wmOperator *op) } else { /* delegate to a helper, as there's too much to fit in here (for copying subsets)... */ - gp_duplicate_points(gps, &gp_strokes_copypastebuf); + gp_duplicate_points(gps, &gp_strokes_copypastebuf, gpl->info); } } } @@ -395,13 +437,20 @@ static int gp_strokes_paste_poll(bContext *C) return (CTX_data_active_gpencil_layer(C) != NULL) && (!BLI_listbase_is_empty(&gp_strokes_copypastebuf)); } +enum { + GP_COPY_ONLY = -1, + GP_COPY_MERGE = 1 +}; + static int gp_strokes_paste_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDlayer *gpl = CTX_data_active_gpencil_layer(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"); + /* check for various error conditions */ if (gpd == NULL) { BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data"); @@ -413,9 +462,9 @@ 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) { + 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"); return OPERATOR_CANCELLED; } @@ -463,26 +512,34 @@ static int gp_strokes_paste_exec(bContext *C, wmOperator *op) * we are obliged to add a new frame if one * doesn't exist already */ - gpf = gpencil_layer_getframe(gpl, CFRA, true); - if (gpf) { 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)) { - bGPDstroke *new_stroke = MEM_dupallocN(gps); - - 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); + /* 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 = 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); @@ -492,10 +549,16 @@ static int gp_strokes_paste_exec(bContext *C, wmOperator *op) void GPENCIL_OT_paste(wmOperatorType *ot) { + static EnumPropertyItem copy_type[] = { + {GP_COPY_ONLY, "COPY", 0, "Copy", ""}, + {GP_COPY_MERGE, "MERGE", 0, "Merge", ""}, + {0, NULL, 0, NULL, NULL} + }; + /* identifiers */ ot->name = "Paste Strokes"; ot->idname = "GPENCIL_OT_paste"; - ot->description = "Paste previously copied strokes into active layer"; + ot->description = "Paste previously copied strokes or copy and merge in active layer"; /* callbacks */ ot->exec = gp_strokes_paste_exec; @@ -503,6 +566,8 @@ void GPENCIL_OT_paste(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + ot->prop = RNA_def_enum(ot->srna, "type", copy_type, 0, "Type", ""); } /* ******************* Move To Layer ****************************** */ @@ -532,7 +597,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 */ @@ -577,7 +642,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)); @@ -614,7 +679,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); @@ -625,8 +690,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) { @@ -639,7 +704,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); @@ -681,13 +746,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; @@ -1069,7 +1134,7 @@ void GPENCIL_OT_delete(wmOperatorType *ot) }; /* identifiers */ - ot->name = "Delete..."; + ot->name = "Delete"; ot->idname = "GPENCIL_OT_delete"; ot->description = "Delete selected Grease Pencil strokes, vertices, or frames"; @@ -1124,25 +1189,65 @@ static int gp_snap_poll(bContext *C) static int gp_snap_to_grid(bContext *C, wmOperator *UNUSED(op)) { RegionView3D *rv3d = CTX_wm_region_data(C); - float gridf = rv3d->gridview; + const float gridf = rv3d->gridview; - CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) - { - bGPDspoint *pt; - int i; - - // TOOD: 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.. */ - if (pt->flag & GP_SPOINT_SELECT) { - pt->x = gridf * floorf(0.5f + pt->x / gridf); - pt->y = gridf * floorf(0.5f + pt->y / gridf); - pt->z = gridf * floorf(0.5f + pt->z / gridf); + 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)) { + + /* 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) { + /* 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; + } + + bGPDspoint *pt; + int i; + + // TOOD: 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.. */ + if (pt->flag & GP_SPOINT_SELECT) { + if (gpl->parent == NULL) { + pt->x = gridf * floorf(0.5f + pt->x / gridf); + pt->y = gridf * floorf(0.5f + pt->y / gridf); + pt->z = gridf * floorf(0.5f + pt->z / gridf); + } + else { + /* 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); + } + + } + } + } } } - CTX_DATA_END; WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; @@ -1169,41 +1274,68 @@ static int gp_snap_to_cursor(bContext *C, wmOperator *op) { 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); - - CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) - { - 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); + + 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)) { + + /* calculate difference matrix if parent object */ + if (gpl->parent != NULL) { + ED_gpencil_parent_location(gpl, diff_mat); } - } - else { - /* affect each selected point */ - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - if (pt->flag & GP_SPOINT_SELECT) { - copy_v3_v3(&pt->x, cursor_global); + + bGPDframe *gpf = gpl->actframe; + bGPDstroke *gps; + for (gps = gpf->strokes.first; gps; gps = gps->next) { + /* 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; + } + + 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); + } + } + else { + /* affect each selected point */ + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + if (pt->flag & GP_SPOINT_SELECT) { + copy_v3_v3(&pt->x, cursor_global); + if (gpl->parent != NULL) { + gp_apply_parent_point(gpl, pt); + } + } + } } + + } } } - CTX_DATA_END; WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; @@ -1243,24 +1375,57 @@ static int gp_snap_cursor_to_sel(bContext *C, wmOperator *UNUSED(op)) INIT_MINMAX(min, max); /* calculate midpoints from selected points */ - CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) - { - 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) { - add_v3_v3(centroid, &pt->x); - minmax_v3v3_v3(min, max, &pt->x); - count++; + 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)) { + + /* 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) { + /* 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; + } + + 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) { + add_v3_v3(centroid, &pt->x); + minmax_v3v3_v3(min, max, &pt->x); + } + else { + /* 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++; + } + } + } } } - CTX_DATA_END; if (v3d->around == V3D_AROUND_CENTER_MEAN && count) { mul_v3_fl(centroid, 1.0f / (float)count); diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h index 53fb33eeb9b..f37fba4212d 100644 --- a/source/blender/editors/gpencil/gpencil_intern.h +++ b/source/blender/editors/gpencil/gpencil_intern.h @@ -101,6 +101,23 @@ void gp_point_to_xy(GP_SpaceConversion *settings, struct bGPDstroke *gps, struct int *r_x, int *r_y); /** + * Convert point to parent space + * + * \param pt Original point + * \param diff_mat Matrix with the difference between original parent matrix + * \param[out] r_pt Pointer to new point after apply matrix + */ +void gp_point_to_parent_space(bGPDspoint *pt, float diff_mat[4][4], bGPDspoint *r_pt); +/** + * Change points position relative to parent object + */ +void gp_apply_parent(bGPDlayer *gpl, bGPDstroke *gps); +/** + * Change point position relative to parent object + */ +void gp_apply_parent_point(bGPDlayer *gpl, bGPDspoint *pt); + +/** * Convert a screenspace point to a 3D Grease Pencil coordinate. * * For use with editing tools where it is easier to perform the operations in 2D, @@ -116,6 +133,10 @@ bool gp_point_xy_to_3d(GP_SpaceConversion *gsc, struct Scene *scene, const float int gp_add_poll(struct bContext *C); int gp_active_layer_poll(struct bContext *C); +int gp_active_brush_poll(struct bContext *C); +int gp_active_palette_poll(struct bContext *C); +int gp_active_palettecolor_poll(struct bContext *C); +int gp_brush_crt_presets_poll(bContext *C); /* Copy/Paste Buffer --------------------------------- */ /* gpencil_edit.c */ @@ -137,17 +158,47 @@ void gp_stroke_delete_tagged_points(bGPDframe *gpf, bGPDstroke *gps, bGPDstroke bool gp_smooth_stroke(bGPDstroke *gps, int i, float inf, bool affect_pressure); /** +* Apply smooth for strength to stroke point +* \param gps Stroke to smooth +* \param i Point index +* \param inf Amount of smoothing to apply +*/ +bool gp_smooth_stroke_strength(bGPDstroke *gps, int i, float inf); + +/** +* Apply smooth for thickness to stroke point (use pressure) +* \param gps Stroke to smooth +* \param i Point index +* \param inf Amount of smoothing to apply +*/ +bool gp_smooth_stroke_thickness(bGPDstroke *gps, int i, float inf); + +/** * Subdivide a stroke once, by adding points at the midpoint between each pair of points * \param gps Stroke data * \param new_totpoints Total number of points (after subdividing) */ void gp_subdivide_stroke(bGPDstroke *gps, const int new_totpoints); +/** +* Add randomness to stroke +* \param gps Stroke data +* \param brsuh Brush data +*/ +void gp_randomize_stroke(bGPDstroke *gps, bGPDbrush *brush); + /* Layers Enums -------------------------------------- */ struct EnumPropertyItem *ED_gpencil_layers_enum_itemf(struct bContext *C, struct PointerRNA *ptr, struct PropertyRNA *prop, bool *r_free); struct EnumPropertyItem *ED_gpencil_layers_with_new_enum_itemf(struct bContext *C, struct PointerRNA *ptr, struct PropertyRNA *prop, bool *r_free); +/* Enums of GP Brushes */ +EnumPropertyItem *ED_gpencil_brushes_enum_itemf(bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), + bool *r_free); + +/* Enums of GP palettes */ +EnumPropertyItem *ED_gpencil_palettes_enum_itemf(bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), + bool *r_free); /* ***************************************************** */ /* Operator Defines */ @@ -155,7 +206,7 @@ struct EnumPropertyItem *ED_gpencil_layers_with_new_enum_itemf(struct bContext * 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, @@ -166,6 +217,7 @@ typedef enum eGPencil_PaintModes { /* stroke editing ----- */ void GPENCIL_OT_editmode_toggle(struct wmOperatorType *ot); +void GPENCIL_OT_selection_opacity_toggle(struct wmOperatorType *ot); void GPENCIL_OT_select(struct wmOperatorType *ot); void GPENCIL_OT_select_all(struct wmOperatorType *ot); @@ -216,12 +268,50 @@ void GPENCIL_OT_lock_all(struct wmOperatorType *ot); void GPENCIL_OT_unlock_all(struct wmOperatorType *ot); void GPENCIL_OT_layer_isolate(struct wmOperatorType *ot); +void GPENCIL_OT_layer_merge(struct wmOperatorType *ot); void GPENCIL_OT_active_frame_delete(struct wmOperatorType *ot); 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); +void GPENCIL_OT_stroke_apply_thickness(struct wmOperatorType *ot); +void GPENCIL_OT_stroke_cyclical_set(struct wmOperatorType *ot); +void GPENCIL_OT_stroke_join(struct wmOperatorType *ot); +void GPENCIL_OT_stroke_flip(struct wmOperatorType *ot); + +void GPENCIL_OT_brush_add(struct wmOperatorType *ot); +void GPENCIL_OT_brush_remove(struct wmOperatorType *ot); +void GPENCIL_OT_brush_change(struct wmOperatorType *ot); +void GPENCIL_OT_brush_move(struct wmOperatorType *ot); +void GPENCIL_OT_brush_presets_create(struct wmOperatorType *ot); +void GPENCIL_OT_brush_copy(struct wmOperatorType *ot); +void GPENCIL_OT_brush_select(struct wmOperatorType *ot); + +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); +void GPENCIL_OT_palettecolor_unlock_all(struct wmOperatorType *ot); +void GPENCIL_OT_palettecolor_move(struct wmOperatorType *ot); +void GPENCIL_OT_palettecolor_select(struct wmOperatorType *ot); +void GPENCIL_OT_palettecolor_copy(struct wmOperatorType *ot); + /* undo stack ---------- */ void gpencil_undo_init(struct bGPdata *gpd); @@ -273,4 +363,39 @@ typedef enum ACTCONT_TYPES { ACTCONT_GPENCIL } ACTCONT_TYPES; +/** +* Iterate over all editable strokes in the current context, +* stopping on each usable layer + stroke pair (i.e. gpl and gps) +* to perform some operations on the stroke. +* +* \param gpl The identifier to use for the layer of the stroke being processed. +* Choose a suitable value to avoid name clashes. +* \param gps The identifier to use for current stroke being processed. +* Choose a suitable value to avoid name clashes. +*/ +#define GP_EDITABLE_STROKES_BEGIN(C, gpl, gps) \ +{ \ + CTX_DATA_BEGIN(C, bGPDlayer*, gpl, editable_gpencil_layers) \ + { \ + if (gpl->actframe == NULL) \ + continue; \ + /* calculate difference matrix if parent object */ \ + float diff_mat[4][4]; \ + ED_gpencil_parent_location(gpl, diff_mat); \ + /* loop over strokes */ \ + for (bGPDstroke *gps = gpl->actframe->strokes.first; gps; gps = gps->next) { \ + /* 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; \ + /* ... Do Stuff With Strokes ... */ + +#define GP_EDITABLE_STROKES_END \ + } \ + } \ + 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 65ee1122b56..50f4e795d70 100644 --- a/source/blender/editors/gpencil/gpencil_ops.c +++ b/source/blender/editors/gpencil/gpencil_ops.c @@ -18,7 +18,7 @@ * The Original Code is Copyright (C) 2009, Blender Foundation, Joshua Leung * This is a new part of Blender * - * Contributor(s): Joshua Leung + * Contributor(s): Joshua Leung, Antonio Vazquez * * ***** END GPL LICENSE BLOCK ***** */ @@ -140,8 +140,8 @@ static void ed_keymap_gpencil_editing(wmKeyConfig *keyconf) * that the only data being edited is that of the Grease Pencil strokes */ - /* FKEY = Eraser Radius */ - kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", FKEY, KM_PRESS, 0, 0); + /* CTRL + FKEY = Eraser Radius */ + kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", FKEY, KM_PRESS, KM_CTRL, 0); RNA_string_set(kmi->ptr, "data_path_primary", "user_preferences.edit.grease_pencil_eraser_radius"); @@ -169,8 +169,8 @@ static void ed_keymap_gpencil_editing(wmKeyConfig *keyconf) kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", FKEY, KM_PRESS, KM_SHIFT, 0); RNA_string_set(kmi->ptr, "data_path_primary", "tool_settings.gpencil_sculpt.brush.strength"); - /* Ctrl-FKEY = Sculpt Brush Size */ - kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", FKEY, KM_PRESS, KM_CTRL, 0); + /* FKEY = Sculpt Brush Size */ + kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", FKEY, KM_PRESS, 0, 0); RNA_string_set(kmi->ptr, "data_path_primary", "tool_settings.gpencil_sculpt.brush.size"); @@ -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); @@ -266,14 +272,37 @@ static void ed_keymap_gpencil_editing(wmKeyConfig *keyconf) kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_hide", HKEY, KM_PRESS, KM_SHIFT, 0); RNA_boolean_set(kmi->ptr, "unselected", true); + + WM_keymap_add_item(keymap, "GPENCIL_OT_selection_opacity_toggle", HKEY, KM_PRESS, KM_CTRL, 0); /* Isolate Layer */ WM_keymap_add_item(keymap, "GPENCIL_OT_layer_isolate", PADASTERKEY, KM_PRESS, 0, 0); /* Move to Layer */ WM_keymap_add_item(keymap, "GPENCIL_OT_move_to_layer", MKEY, KM_PRESS, 0, 0); - - + + /* Select drawing brush using index */ + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_select", ONEKEY, KM_PRESS, 0, 0); + RNA_int_set(kmi->ptr, "index", 0); + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_select", TWOKEY, KM_PRESS, 0, 0); + RNA_int_set(kmi->ptr, "index", 1); + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_select", THREEKEY, KM_PRESS, 0, 0); + RNA_int_set(kmi->ptr, "index", 2); + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_select", FOURKEY, KM_PRESS, 0, 0); + RNA_int_set(kmi->ptr, "index", 3); + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_select", FIVEKEY, KM_PRESS, 0, 0); + RNA_int_set(kmi->ptr, "index", 4); + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_select", SIXKEY, KM_PRESS, 0, 0); + RNA_int_set(kmi->ptr, "index", 5); + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_select", SEVENKEY, KM_PRESS, 0, 0); + RNA_int_set(kmi->ptr, "index", 6); + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_select", EIGHTKEY, KM_PRESS, 0, 0); + RNA_int_set(kmi->ptr, "index", 7); + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_select", NINEKEY, KM_PRESS, 0, 0); + RNA_int_set(kmi->ptr, "index", 8); + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_select", ZEROKEY, KM_PRESS, 0, 0); + RNA_int_set(kmi->ptr, "index", 9); + /* Transform Tools */ kmi = WM_keymap_add_item(keymap, "TRANSFORM_OT_translate", GKEY, KM_PRESS, 0, 0); @@ -318,7 +347,8 @@ void ED_operatortypes_gpencil(void) /* Editing (Strokes) ------------ */ WM_operatortype_append(GPENCIL_OT_editmode_toggle); - + WM_operatortype_append(GPENCIL_OT_selection_opacity_toggle); + WM_operatortype_append(GPENCIL_OT_select); WM_operatortype_append(GPENCIL_OT_select_all); WM_operatortype_append(GPENCIL_OT_select_circle); @@ -362,12 +392,44 @@ void ED_operatortypes_gpencil(void) WM_operatortype_append(GPENCIL_OT_lock_all); WM_operatortype_append(GPENCIL_OT_unlock_all); WM_operatortype_append(GPENCIL_OT_layer_isolate); - + WM_operatortype_append(GPENCIL_OT_layer_merge); + WM_operatortype_append(GPENCIL_OT_active_frame_delete); WM_operatortype_append(GPENCIL_OT_active_frames_delete_all); WM_operatortype_append(GPENCIL_OT_convert); + WM_operatortype_append(GPENCIL_OT_stroke_arrange); + WM_operatortype_append(GPENCIL_OT_stroke_change_color); + WM_operatortype_append(GPENCIL_OT_stroke_lock_color); + WM_operatortype_append(GPENCIL_OT_stroke_apply_thickness); + WM_operatortype_append(GPENCIL_OT_stroke_cyclical_set); + WM_operatortype_append(GPENCIL_OT_stroke_join); + WM_operatortype_append(GPENCIL_OT_stroke_flip); + + WM_operatortype_append(GPENCIL_OT_palette_add); + WM_operatortype_append(GPENCIL_OT_palette_remove); + WM_operatortype_append(GPENCIL_OT_palette_change); + WM_operatortype_append(GPENCIL_OT_palette_lock_layer); + WM_operatortype_append(GPENCIL_OT_palettecolor_add); + WM_operatortype_append(GPENCIL_OT_palettecolor_remove); + WM_operatortype_append(GPENCIL_OT_palettecolor_isolate); + WM_operatortype_append(GPENCIL_OT_palettecolor_hide); + WM_operatortype_append(GPENCIL_OT_palettecolor_reveal); + WM_operatortype_append(GPENCIL_OT_palettecolor_lock_all); + WM_operatortype_append(GPENCIL_OT_palettecolor_unlock_all); + WM_operatortype_append(GPENCIL_OT_palettecolor_move); + WM_operatortype_append(GPENCIL_OT_palettecolor_select); + WM_operatortype_append(GPENCIL_OT_palettecolor_copy); + + WM_operatortype_append(GPENCIL_OT_brush_add); + WM_operatortype_append(GPENCIL_OT_brush_remove); + WM_operatortype_append(GPENCIL_OT_brush_change); + WM_operatortype_append(GPENCIL_OT_brush_move); + WM_operatortype_append(GPENCIL_OT_brush_presets_create); + WM_operatortype_append(GPENCIL_OT_brush_copy); + WM_operatortype_append(GPENCIL_OT_brush_select); + /* Editing (Time) --------------- */ } diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index a570d586f50..eefdee70304 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -18,7 +18,7 @@ * The Original Code is Copyright (C) 2008, Blender Foundation, Joshua Leung * This is a new part of Blender * - * Contributor(s): Joshua Leung + * Contributor(s): Joshua Leung, Antonio Vazquez * * ***** END GPL LICENSE BLOCK ***** */ @@ -39,21 +39,26 @@ #include "BLI_blenlib.h" #include "BLI_math.h" #include "BLI_utildefines.h" +#include "BLI_rand.h" #include "BLT_translation.h" #include "PIL_time.h" +#include "BKE_main.h" +#include "BKE_paint.h" #include "BKE_gpencil.h" #include "BKE_context.h" #include "BKE_global.h" #include "BKE_report.h" #include "BKE_screen.h" #include "BKE_tracking.h" +#include "BKE_colortools.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_gpencil_types.h" +#include "DNA_brush_types.h" #include "DNA_windowmanager_types.h" #include "UI_view2d.h" @@ -151,6 +156,10 @@ typedef struct tGPsdata { float custom_color[4]; /* custom color - hack for enforcing a particular color for track/mask editing */ void *erasercursor; /* radial cursor data for drawing eraser */ + + bGPDpalettecolor *palettecolor; /* current palette color */ + bGPDbrush *brush; /* current drawing brush */ + short straight[2]; /* 1: line horizontal, 2: line vertical, other: not defined, second element position */ } tGPsdata; /* ------ */ @@ -333,10 +342,90 @@ static void gp_stroke_convertcoords(tGPsdata *p, const int mval[2], float out[3] } } +/* apply jitter to stroke */ +static void gp_brush_jitter(bGPdata *gpd, bGPDbrush *brush, tGPspoint *pt, const int mval[2], int r_mval[2]) +{ + float pressure = pt->pressure; + float tmp_pressure = pt->pressure; + if (brush->draw_jitter > 0.0f) { + float curvef = curvemapping_evaluateF(brush->cur_jitter, 0, pressure); + tmp_pressure = curvef * brush->draw_sensitivity; + } + const float exfactor = (brush->draw_jitter + 2.0f) * (brush->draw_jitter + 2.0f); /* exponential value */ + const float fac = BLI_frand() * exfactor * tmp_pressure; + /* Jitter is applied perpendicular to the mouse movement vector (2D space) */ + float mvec[2], svec[2]; + /* mouse movement in ints -> floats */ + if (gpd->sbuffer_size > 1) { + mvec[0] = (float)(mval[0] - (pt - 1)->x); + mvec[1] = (float)(mval[1] - (pt - 1)->y); + normalize_v2(mvec); + } + else { + mvec[0] = 0.0f; + mvec[1] = 0.0f; + } + /* rotate mvec by 90 degrees... */ + svec[0] = -mvec[1]; + svec[1] = mvec[0]; + /* scale the displacement by the random, and apply */ + if (BLI_frand() > 0.5f) { + mul_v2_fl(svec, -fac); + } + else { + mul_v2_fl(svec, fac); + } + + r_mval[0] = mval[0] + svec[0]; + r_mval[1] = mval[1] + svec[1]; + +} + +/* apply pressure change depending of the angle of the stroke to simulate a pen with shape */ +static void gp_brush_angle(bGPdata *gpd, bGPDbrush *brush, tGPspoint *pt, const int mval[2]) +{ + float mvec[2]; + float sen = brush->draw_angle_factor; /* sensitivity */; + float fac; + float mpressure; + + float angle = brush->draw_angle; /* default angle of brush in radians */; + float v0[2] = { cos(angle), sin(angle) }; /* angle vector of the brush with full thickness */ + + /* Apply to first point (only if there are 2 points because before no data to do it ) */ + if (gpd->sbuffer_size == 1) { + mvec[0] = (float)(mval[0] - (pt - 1)->x); + mvec[1] = (float)(mval[1] - (pt - 1)->y); + normalize_v2(mvec); + + /* uses > 1.0f to get a smooth transition in first point */ + fac = 1.4f - fabs(dot_v2v2(v0, mvec)); /* 0.0 to 1.0 */ + (pt - 1)->pressure = (pt - 1)->pressure - (sen * fac); + + CLAMP((pt - 1)->pressure, GPENCIL_ALPHA_OPACITY_THRESH, 1.0f); + } + + /* apply from second point */ + if (gpd->sbuffer_size >= 1) { + mvec[0] = (float)(mval[0] - (pt - 1)->x); + mvec[1] = (float)(mval[1] - (pt - 1)->y); + normalize_v2(mvec); + + fac = 1.0f - fabs(dot_v2v2(v0, mvec)); /* 0.0 to 1.0 */ + /* interpolate with previous point for smoother transitions */ + mpressure = interpf(pt->pressure - (sen * fac), (pt - 1)->pressure, 0.3f); + pt->pressure = mpressure; + + CLAMP(pt->pressure, GPENCIL_ALPHA_OPACITY_THRESH, 1.0f); + } + +} + /* add current stroke-point to buffer (returns whether point was successfully added) */ static short gp_stroke_addpoint(tGPsdata *p, const int mval[2], float pressure, double curtime) { bGPdata *gpd = p->gpd; + bGPDbrush *brush = p->brush; tGPspoint *pt; /* check painting mode */ @@ -349,6 +438,7 @@ static short gp_stroke_addpoint(tGPsdata *p, const int mval[2], float pressure, /* store settings */ copy_v2_v2_int(&pt->x, mval); pt->pressure = 1.0f; /* T44932 - Pressure vals are unreliable, so ignore for now */ + pt->strength = 1.0f; pt->time = (float)(curtime - p->inittime); /* increment buffer size */ @@ -363,6 +453,7 @@ static short gp_stroke_addpoint(tGPsdata *p, const int mval[2], float pressure, /* store settings */ copy_v2_v2_int(&pt->x, mval); pt->pressure = 1.0f; /* T44932 - Pressure vals are unreliable, so ignore for now */ + pt->strength = 1.0f; pt->time = (float)(curtime - p->inittime); /* now the buffer has 2 points (and shouldn't be allowed to get any larger) */ @@ -381,8 +472,65 @@ static short gp_stroke_addpoint(tGPsdata *p, const int mval[2], float pressure, pt = ((tGPspoint *)(gpd->sbuffer) + gpd->sbuffer_size); /* store settings */ - copy_v2_v2_int(&pt->x, mval); - pt->pressure = pressure; + /* pressure */ + if (brush->flag & GP_BRUSH_USE_PRESSURE) { + float curvef = curvemapping_evaluateF(brush->cur_sensitivity, 0, pressure); + pt->pressure = curvef * brush->draw_sensitivity; + } + else { + pt->pressure = 1.0f; + } + /* Apply jitter to position */ + if (brush->draw_jitter > 0.0f) { + int r_mval[2]; + gp_brush_jitter(gpd, brush, pt, mval, r_mval); + copy_v2_v2_int(&pt->x, r_mval); + } + else { + copy_v2_v2_int(&pt->x, mval); + } + /* apply randomness to pressure */ + if ((brush->draw_random_press > 0.0f) && (brush->flag & GP_BRUSH_USE_RANDOM_PRESSURE)) { + float curvef = curvemapping_evaluateF(brush->cur_sensitivity, 0, pressure); + float tmp_pressure = curvef * brush->draw_sensitivity; + if (BLI_frand() > 0.5f) { + pt->pressure -= tmp_pressure * brush->draw_random_press * BLI_frand(); + } + else { + pt->pressure += tmp_pressure * brush->draw_random_press * BLI_frand(); + } + CLAMP(pt->pressure, GPENCIL_STRENGTH_MIN, 1.0f); + } + + /* apply angle of stroke to brush size */ + if (brush->draw_angle_factor > 0.0f) { + gp_brush_angle(gpd, brush, pt, mval); + } + + /* color strength */ + if (brush->flag & GP_BRUSH_USE_STENGTH_PRESSURE) { + float curvef = curvemapping_evaluateF(brush->cur_strength, 0, pressure); + float tmp_pressure = curvef * brush->draw_sensitivity; + + pt->strength = tmp_pressure * brush->draw_strength; + } + else { + pt->strength = brush->draw_strength; + } + CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); + + /* apply randomness to color strength */ + if ((brush->draw_random_press > 0.0f) && (brush->flag & GP_BRUSH_USE_RANDOM_STRENGTH)) { + if (BLI_frand() > 0.5f) { + pt->strength -= pt->strength * brush->draw_random_press * BLI_frand(); + } + else { + pt->strength += pt->strength * brush->draw_random_press * BLI_frand(); + } + CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); + } + + /* point time */ pt->time = (float)(curtime - p->inittime); /* increment counters */ @@ -395,12 +543,15 @@ static short gp_stroke_addpoint(tGPsdata *p, const int mval[2], float pressure, return GP_STROKEADD_NORMAL; } else if (p->paintmode == GP_PAINTMODE_DRAW_POLY) { + + bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); /* get pointer to destination point */ pt = (tGPspoint *)(gpd->sbuffer); /* store settings */ copy_v2_v2_int(&pt->x, mval); pt->pressure = 1.0f; /* T44932 - Pressure vals are unreliable, so ignore for now */ + pt->strength = 1.0f; pt->time = (float)(curtime - p->inittime); /* if there's stroke for this poly line session add (or replace last) point @@ -433,10 +584,16 @@ static short gp_stroke_addpoint(tGPsdata *p, const int mval[2], float pressure, /* convert screen-coordinates to appropriate coordinates (and store them) */ gp_stroke_convertcoords(p, &pt->x, &pts->x, NULL); - + /* if parented change position relative to parent object */ + if (gpl->parent != NULL) { + gp_apply_parent_point(gpl, pts); + } /* copy pressure and time */ pts->pressure = pt->pressure; + pts->strength = pt->strength; pts->time = pt->time; + /* force fill recalc */ + gps->flag |= GP_STROKE_RECALC_CACHES; } /* increment counters */ @@ -534,6 +691,7 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) bGPDstroke *gps; bGPDspoint *pt; tGPspoint *ptc; + bGPDbrush *brush = p->brush; int i, totelem; /* since strokes are so fine, when using their depth we need a margin otherwise they might get missed */ @@ -569,7 +727,7 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) /* copy appropriate settings for stroke */ gps->totpoints = totelem; - gps->thickness = p->gpl->thickness; + gps->thickness = brush->thickness; gps->flag = gpd->sbuffer_sflag; gps->inittime = p->inittime; @@ -577,7 +735,7 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) gps->flag |= GP_STROKE_RECALC_CACHES; /* allocate enough memory for a continuous array for storage points */ - int sublevel = gpl->sublevel; + int sublevel = brush->sublevel; int new_totpoints = gps->totpoints; for (i = 0; i < sublevel; i++) { @@ -600,9 +758,14 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) /* convert screen-coordinates to appropriate coordinates (and store them) */ gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL); - + /* if parented change position relative to parent object */ + if (gpl->parent != NULL) { + gp_apply_parent_point(gpl, pt); + } /* copy pressure and time */ pt->pressure = ptc->pressure; + pt->strength = ptc->strength; + CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); pt->time = ptc->time; pt++; @@ -614,9 +777,15 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) /* convert screen-coordinates to appropriate coordinates (and store them) */ gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL); - + /* if parented change position relative to parent object */ + if (gpl->parent != NULL) { + gp_apply_parent_point(gpl, pt); + } + /* copy pressure and time */ pt->pressure = ptc->pressure; + pt->strength = ptc->strength; + CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); pt->time = ptc->time; } } @@ -626,9 +795,14 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) /* convert screen-coordinates to appropriate coordinates (and store them) */ gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL); - + /* if parented change position relative to parent object */ + if (gpl->parent != NULL) { + gp_apply_parent_point(gpl, pt); + } /* copy pressure and time */ pt->pressure = ptc->pressure; + pt->strength = ptc->strength; + CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); pt->time = ptc->time; } else { @@ -703,6 +877,8 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) /* copy pressure and time */ pt->pressure = ptc->pressure; + pt->strength = ptc->strength; + CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); pt->time = ptc->time; } @@ -716,25 +892,38 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) gp_subdivide_stroke(gps, totpoints); } } - + /* apply randomness to stroke */ + if (brush->draw_random_sub > 0.0f) { + gp_randomize_stroke(gps, brush); + } + /* smooth stroke after subdiv - only if there's something to do * for each iteration, the factor is reduced to get a better smoothing without changing too much * the original stroke */ - if (gpl->draw_smoothfac > 0.0f) { + if (brush->draw_smoothfac > 0.0f) { float reduce = 0.0f; - for (int r = 0; r < gpl->draw_smoothlvl; ++r) { + for (int r = 0; r < brush->draw_smoothlvl; ++r) { for (i = 0; i < gps->totpoints; i++) { /* NOTE: No pressure smoothing, or else we get annoying thickness changes while drawing... */ - gp_smooth_stroke(gps, i, gpl->draw_smoothfac - reduce, false); + gp_smooth_stroke(gps, i, brush->draw_smoothfac - reduce, false); } reduce += 0.25f; // reduce the factor } } - + /* if parented change position relative to parent object */ + if (gpl->parent != NULL) { + gp_apply_parent(gpl, gps); + } + if (depth_arr) MEM_freeN(depth_arr); } + /* Save palette color */ + bGPDpalette *palette = BKE_gpencil_palette_getactive(p->gpd); + bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette); + gps->palcolor = palcolor; + strcpy(gps->colorname, palcolor->info); /* add stroke to frame */ BLI_addtail(&p->gpf->strokes, gps); @@ -761,12 +950,21 @@ static bool gp_stroke_eraser_is_occluded(tGPsdata *p, const bGPDspoint *pt, cons (p->flags & GP_PAINTFLAG_V3D_ERASER_DEPTH)) { RegionView3D *rv3d = p->ar->regiondata; + bGPDlayer *gpl = p->gpl; + const int mval[2] = {x, y}; float mval_3d[3]; - + float fpt[3]; + + float diff_mat[4][4]; + /* calculate difference matrix if parent object */ + ED_gpencil_parent_location(gpl, diff_mat); + if (ED_view3d_autodist_simple(p->ar, mval, mval_3d, 0, NULL)) { const float depth_mval = view3d_point_depth(rv3d, mval_3d); - const float depth_pt = view3d_point_depth(rv3d, &pt->x); + + mul_v3_m4v3(fpt, diff_mat, &pt->x); + const float depth_pt = view3d_point_depth(rv3d, fpt); if (depth_pt > depth_mval) { return true; @@ -804,7 +1002,13 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p, int pc1[2] = {0}; int pc2[2] = {0}; int i; - + float diff_mat[4][4]; + + /* calculate difference matrix if parent object */ + if (gpl->parent != NULL) { + ED_gpencil_parent_location(gpl, diff_mat); + } + if (gps->totpoints == 0) { /* just free stroke */ if (gps->points) @@ -816,8 +1020,14 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p, else if (gps->totpoints == 1) { /* only process if it hasn't been masked out... */ if (!(p->flags & GP_PAINTFLAG_SELECTMASK) || (gps->points->flag & GP_SPOINT_SELECT)) { - gp_point_to_xy(&p->gsc, gps, gps->points, &pc1[0], &pc1[1]); - + if (gpl->parent == NULL) { + gp_point_to_xy(&p->gsc, gps, gps->points, &pc1[0], &pc1[1]); + } + else { + bGPDspoint pt_temp; + gp_point_to_parent_space(gps->points, diff_mat, &pt_temp); + gp_point_to_xy(&p->gsc, gps, &pt_temp, &pc1[0], &pc1[1]); + } /* do boundbox check first */ if ((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) { /* only check if point is inside */ @@ -826,7 +1036,7 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p, // XXX: pressure sensitive eraser should apply here too? MEM_freeN(gps->points); if (gps->triangles) - MEM_freeN(gps->triangles); + MEM_freeN(gps->triangles); BLI_freelinkN(&gpf->strokes, gps); } } @@ -836,7 +1046,7 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p, /* Pressure threshold at which stroke should be culled: Calculated as pressure value * below which we would have invisible strokes */ - const float cull_thresh = (gpl->thickness) ? 1.0f / ((float)gpl->thickness) : 1.0f; + const float cull_thresh = (gps->thickness) ? 1.0f / ((float)gps->thickness) : 1.0f; /* Amount to decrease the pressure of each point with each stroke */ // TODO: Fetch from toolsettings, or compute based on thickness instead? @@ -865,15 +1075,24 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p, /* get points to work with */ pt1 = gps->points + i; pt2 = gps->points + i + 1; - + /* only process if it hasn't been masked out... */ if ((p->flags & GP_PAINTFLAG_SELECTMASK) && !(gps->points->flag & GP_SPOINT_SELECT)) continue; - /* get coordinates of point in screenspace */ - gp_point_to_xy(&p->gsc, gps, pt1, &pc1[0], &pc1[1]); - gp_point_to_xy(&p->gsc, gps, pt2, &pc2[0], &pc2[1]); - + if (gpl->parent == NULL) { + gp_point_to_xy(&p->gsc, gps, pt1, &pc1[0], &pc1[1]); + gp_point_to_xy(&p->gsc, gps, pt2, &pc2[0], &pc2[1]); + } + else { + bGPDspoint npt; + gp_point_to_parent_space(pt1, diff_mat, &npt); + gp_point_to_xy(&p->gsc, gps, &npt, &pc1[0], &pc1[1]); + + gp_point_to_parent_space(pt2, diff_mat, &npt); + gp_point_to_xy(&p->gsc, gps, &npt, &pc2[0], &pc2[1]); + } + /* Check that point segment of the boundbox of the eraser stroke */ if (((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) || ((!ELEM(V2D_IS_CLIPPED, pc2[0], pc2[1])) && BLI_rcti_isect_pt(rect, pc2[0], pc2[1]))) @@ -955,7 +1174,10 @@ static void gp_stroke_doeraser(tGPsdata *p) /* loop over strokes, checking segments for intersections */ for (gps = gpf->strokes.first; gps; gps = gpn) { gpn = gps->next; - + /* check if the color is editable */ + if (ED_gpencil_stroke_color_use(gpl, gps) == false) { + continue; + } /* Not all strokes in the datablock may be valid in the current editor/context * (e.g. 2D space strokes in the 3D view, if the same datablock is shared) */ @@ -994,6 +1216,78 @@ static void gp_session_validatebuffer(tGPsdata *p) p->inittime = 0.0; } +/* create a new palette color */ +static bGPDpalettecolor *gp_create_new_color(bGPDpalette *palette) +{ + bGPDpalettecolor *palcolor; + + palcolor = BKE_gpencil_palettecolor_addnew(palette, DATA_("Color"), true); + + return palcolor; +} + +/* initialize a drawing brush */ +static void gp_init_drawing_brush(ToolSettings *ts, tGPsdata *p) +{ + bGPDbrush *brush; + + /* if not exist, create a new one */ + if (BLI_listbase_is_empty(&ts->gp_brushes)) { + /* create new brushes */ + BKE_gpencil_brush_init_presets(ts); + brush = BKE_gpencil_brush_getactive(ts); + } + else { + /* Use the current */ + brush = BKE_gpencil_brush_getactive(ts); + } + /* be sure curves are initializated */ + curvemapping_initialize(brush->cur_sensitivity); + curvemapping_initialize(brush->cur_strength); + curvemapping_initialize(brush->cur_jitter); + + /* asign to temp tGPsdata */ + p->brush = brush; +} + + +/* initialize a paint palette brush and a default color if not exist */ +static void gp_init_palette(tGPsdata *p) +{ + bGPdata *gpd; + bGPDpalette *palette; + bGPDpalettecolor *palcolor; + + gpd = p->gpd; + + /* if not exist, create a new palette */ + if (BLI_listbase_is_empty(&gpd->palettes)) { + /* create new palette */ + 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 = 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 = BKE_gpencil_palettecolor_getactive(palette); + } + /* in some situations can be null, so use first */ + if (palcolor == NULL) { + BKE_gpencil_palettecolor_setactive(palette, palette->colors.first); + palcolor = palette->colors.first; + } + } + + /* asign to temp tGPsdata */ + p->palettecolor = palcolor; +} + /* (re)init new painting data */ static bool gp_session_initdata(bContext *C, tGPsdata *p) { @@ -1145,7 +1439,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; } @@ -1158,7 +1452,16 @@ static bool gp_session_initdata(bContext *C, tGPsdata *p) /* clear out buffer (stored in gp-data), in case something contaminated it */ gp_session_validatebuffer(p); - + /* set brush and create a new one if null */ + gp_init_drawing_brush(ts, p); + /* set palette info and create a new one if null */ + gp_init_palette(p); + /* set palette colors */ + bGPDpalettecolor *palcolor = p->palettecolor; + bGPdata *pdata = p->gpd; + copy_v4_v4(pdata->scolor, palcolor->color); + pdata->sflag = palcolor->flag; + return 1; } @@ -1177,7 +1480,7 @@ static tGPsdata *gp_session_initpaint(bContext *C) * erase size won't get lost */ p->radius = U.gp_eraser; - + /* return context data for running paint operator */ return p; } @@ -1211,9 +1514,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); @@ -1241,7 +1544,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 @@ -1277,7 +1580,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; @@ -1495,7 +1798,6 @@ static bool gpencil_is_tablet_eraser_active(const wmEvent *event) /* ------------------------------- */ - static void gpencil_draw_exit(bContext *C, wmOperator *op) { tGPsdata *p = op->customdata; @@ -1513,7 +1815,7 @@ static void gpencil_draw_exit(bContext *C, wmOperator *op) /* turn off radial brush cursor */ gpencil_draw_toggle_eraser_cursor(C, p, false); } - + /* always store the new eraser size to be used again next time * NOTE: Do this even when not in eraser mode, as eraser may * have been toggled at some point. @@ -1600,7 +1902,7 @@ static void gpencil_draw_status_indicators(tGPsdata *p) break; case GP_PAINTMODE_DRAW: ED_area_headerprint(p->sa, IFACE_("Grease Pencil Freehand Session: Hold and drag LMB to draw | " - "ESC/Enter to end (or click outside this area)")); + "E/ESC/Enter to end (or click outside this area)")); break; case GP_PAINTMODE_DRAW_POLY: ED_area_headerprint(p->sa, IFACE_("Grease Pencil Poly Session: LMB click to place next stroke vertex | " @@ -1691,6 +1993,31 @@ static void gpencil_draw_apply_event(wmOperator *op, const wmEvent *event) */ p->mval[0] = event->mval[0] + 1; p->mval[1] = event->mval[1] + 1; + + /* verify key status for straight lines */ + if ((event->ctrl > 0) || (event->alt > 0)) { + if (p->straight[0] == 0) { + int dx = abs(p->mval[0] - p->mvalo[0]); + int dy = abs(p->mval[1] - p->mvalo[1]); + if ((dx > 0) || (dy > 0)) { + /* check mouse direction to replace the other coordinate with previous values */ + if (dx >= dy) { + /* horizontal */ + p->straight[0] = 1; + p->straight[1] = p->mval[1]; /* save y */ + } + else { + /* vertical */ + p->straight[0] = 2; + p->straight[1] = p->mval[0]; /* save x */ + } + } + } + } + else { + p->straight[0] = 0; + } + p->curtime = PIL_check_seconds_timer(); /* handle pressure sensitivity (which is supplied by tablets) */ @@ -1725,7 +2052,9 @@ static void gpencil_draw_apply_event(wmOperator *op, const wmEvent *event) p->mvalo[1] = p->mval[1]; p->opressure = p->pressure; p->inittime = p->ocurtime = p->curtime; - + p->straight[0] = 0; + p->straight[1] = 0; + /* special exception here for too high pressure values on first touch in * windows for some tablets, then we just skip first touch... */ @@ -1733,6 +2062,18 @@ static void gpencil_draw_apply_event(wmOperator *op, const wmEvent *event) return; } + /* check if alt key is pressed and limit to straight lines */ + if (p->straight[0] != 0) { + if (p->straight[0] == 1) { + /* horizontal */ + p->mval[1] = p->straight[1]; /* replace y */ + } + else { + /* vertical */ + p->mval[0] = p->straight[1]; /* replace x */ + } + } + /* fill in stroke data (not actually used directly by gpencil_draw_apply) */ RNA_collection_add(op->ptr, "stroke", &itemptr); @@ -1855,21 +2196,21 @@ static int gpencil_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event if (p->paintmode == GP_PAINTMODE_ERASER) { gpencil_draw_toggle_eraser_cursor(C, p, true); } - /* set cursor * NOTE: This may change later (i.e. intentionally via brush toggle, * or unintentionally if the user scrolls outside the area)... */ gpencil_draw_cursor_set(p); - + /* only start drawing immediately if we're allowed to do so... */ if (RNA_boolean_get(op->ptr, "wait_for_input") == false) { /* hotkey invoked - start drawing */ /* printf("\tGP - set first spot\n"); */ p->status = GP_STATUS_PAINTING; - + /* handle the initial drawing - i.e. for just doing a simple dot */ gpencil_draw_apply_event(op, event); + op->flag |= OP_IS_MODAL_CURSOR_REGION; } else { /* toolbar invoked - don't start drawing yet... */ @@ -1976,6 +2317,10 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) * is essential for ensuring that they can quickly return to that view */ } + else if ((ELEM(event->type, DKEY)) && (event->val == KM_RELEASE)) { + /* enable continuous if release D key in mid drawing */ + p->scene->toolsettings->gpencil_flags |= GP_TOOL_FLAG_PAINTSESSIONS_ON; + } else { estate = OPERATOR_RUNNING_MODAL; } @@ -1986,7 +2331,7 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) /* exit painting mode (and/or end current stroke) * NOTE: cannot do RIGHTMOUSE (as is standard for canceling) as that would break polyline [#32647] */ - if (ELEM(event->type, RETKEY, PADENTER, ESCKEY, SPACEKEY)) { + 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"); */ p->status = GP_STATUS_DONE; @@ -2045,6 +2390,9 @@ 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; + p->status = GP_STATUS_DONE; estate = OPERATOR_FINISHED; } @@ -2074,6 +2422,9 @@ 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; @@ -2090,6 +2441,9 @@ 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; @@ -2117,6 +2471,9 @@ 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; } } @@ -2125,6 +2482,9 @@ 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; + 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 b6482786b4f..4cb966c6378 100644 --- a/source/blender/editors/gpencil/gpencil_select.c +++ b/source/blender/editors/gpencil/gpencil_select.c @@ -43,6 +43,7 @@ #include "DNA_gpencil_types.h" #include "DNA_scene_types.h" #include "DNA_screen_types.h" +#include "DNA_object_types.h" #include "BKE_context.h" #include "BKE_gpencil.h" @@ -265,7 +266,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; @@ -616,9 +617,10 @@ void GPENCIL_OT_select_less(wmOperatorType *ot) /* NOTE: Code here is adapted (i.e. copied directly) from gpencil_paint.c::gp_stroke_eraser_dostroke() * It would be great to de-duplicate the logic here sometime, but that can wait... */ -static bool gp_stroke_do_circle_sel(bGPDstroke *gps, GP_SpaceConversion *gsc, - const int mx, const int my, const int radius, - const bool select, rcti *rect) +static bool gp_stroke_do_circle_sel( + bGPDstroke *gps, GP_SpaceConversion *gsc, + const int mx, const int my, const int radius, + const bool select, rcti *rect, const bool parented, float diff_mat[4][4]) { bGPDspoint *pt1, *pt2; int x0 = 0, y0 = 0, x1 = 0, y1 = 0; @@ -626,7 +628,14 @@ static bool gp_stroke_do_circle_sel(bGPDstroke *gps, GP_SpaceConversion *gsc, bool changed = false; if (gps->totpoints == 1) { - gp_point_to_xy(gsc, gps, gps->points, &x0, &y0); + if (!parented) { + gp_point_to_xy(gsc, gps, gps->points, &x0, &y0); + } + else { + bGPDspoint pt_temp; + gp_point_to_parent_space(gps->points, diff_mat, &pt_temp); + gp_point_to_xy(gsc, gps, &pt_temp, &x0, &y0); + } /* do boundbox check first */ if ((!ELEM(V2D_IS_CLIPPED, x0, y0)) && BLI_rcti_isect_pt(rect, x0, y0)) { @@ -654,9 +663,18 @@ static bool gp_stroke_do_circle_sel(bGPDstroke *gps, GP_SpaceConversion *gsc, /* get points to work with */ pt1 = gps->points + i; pt2 = gps->points + i + 1; - - gp_point_to_xy(gsc, gps, pt1, &x0, &y0); - gp_point_to_xy(gsc, gps, pt2, &x1, &y1); + if (!parented) { + gp_point_to_xy(gsc, gps, pt1, &x0, &y0); + gp_point_to_xy(gsc, gps, pt2, &x1, &y1); + } + else { + bGPDspoint npt; + gp_point_to_parent_space(pt1, diff_mat, &npt); + gp_point_to_xy(gsc, gps, &npt, &x0, &y0); + + gp_point_to_parent_space(pt2, diff_mat, &npt); + gp_point_to_xy(gsc, gps, &npt, &x1, &y1); + } /* check that point segment of the boundbox of the selection stroke */ if (((!ELEM(V2D_IS_CLIPPED, x0, y0)) && BLI_rcti_isect_pt(rect, x0, y0)) || @@ -691,7 +709,7 @@ static bool gp_stroke_do_circle_sel(bGPDstroke *gps, GP_SpaceConversion *gsc, } /* Ensure that stroke selection is in sync with its points */ - gpencil_stroke_sync_selection(gps); + BKE_gpencil_stroke_sync_selection(gps); } return changed; @@ -733,12 +751,14 @@ static int gpencil_circle_select_exec(bContext *C, wmOperator *op) /* find visible strokes, and select if hit */ - CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) + GP_EDITABLE_STROKES_BEGIN(C, gpl, gps) { - changed |= gp_stroke_do_circle_sel(gps, &gsc, mx, my, radius, select, &rect); + changed |= gp_stroke_do_circle_sel( + gps, &gsc, mx, my, radius, select, &rect, + (gpl->parent != NULL), diff_mat); } - CTX_DATA_END; - + GP_EDITABLE_STROKES_END; + /* updates */ if (changed) { WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL); @@ -818,17 +838,25 @@ static int gpencil_border_select_exec(bContext *C, wmOperator *op) WM_operator_properties_border_to_rcti(op, &rect); /* select/deselect points */ - CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) + GP_EDITABLE_STROKES_BEGIN(C, gpl, gps) { + bGPDspoint *pt; int i; - + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { int x0, y0; - + /* convert point coords to screenspace */ - gp_point_to_xy(&gsc, gps, pt, &x0, &y0); - + if (gpl->parent == NULL) { + gp_point_to_xy(&gsc, gps, pt, &x0, &y0); + } + else { + bGPDspoint pt2; + gp_point_to_parent_space(pt, diff_mat, &pt2); + gp_point_to_xy(&gsc, gps, &pt2, &x0, &y0); + } + /* test if in selection rect */ if ((!ELEM(V2D_IS_CLIPPED, x0, y0)) && BLI_rcti_isect_pt(&rect, x0, y0)) { if (select) { @@ -837,16 +865,16 @@ static int gpencil_border_select_exec(bContext *C, wmOperator *op) else { pt->flag &= ~GP_SPOINT_SELECT; } - + changed = true; } } - + /* Ensure that stroke selection is in sync with its points */ - gpencil_stroke_sync_selection(gps); + BKE_gpencil_stroke_sync_selection(gps); } - CTX_DATA_END; - + GP_EDITABLE_STROKES_END; + /* updates */ if (changed) { WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL); @@ -920,20 +948,26 @@ static int gpencil_lasso_select_exec(bContext *C, wmOperator *op) } /* select/deselect points */ - CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) + GP_EDITABLE_STROKES_BEGIN(C, gpl, gps) { bGPDspoint *pt; int i; - + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { int x0, y0; - + /* convert point coords to screenspace */ - gp_point_to_xy(&gsc, gps, pt, &x0, &y0); - + if (gpl->parent == NULL) { + gp_point_to_xy(&gsc, gps, pt, &x0, &y0); + } + else { + bGPDspoint pt2; + gp_point_to_parent_space(pt, diff_mat, &pt2); + gp_point_to_xy(&gsc, gps, &pt2, &x0, &y0); + } /* test if in lasso boundbox + within the lasso noose */ if ((!ELEM(V2D_IS_CLIPPED, x0, y0)) && BLI_rcti_isect_pt(&rect, x0, y0) && - BLI_lasso_is_point_inside(mcords, mcords_tot, x0, y0, INT_MAX)) + BLI_lasso_is_point_inside(mcords, mcords_tot, x0, y0, INT_MAX)) { if (select) { pt->flag |= GP_SPOINT_SELECT; @@ -941,16 +975,16 @@ static int gpencil_lasso_select_exec(bContext *C, wmOperator *op) else { pt->flag &= ~GP_SPOINT_SELECT; } - + changed = true; } } - + /* Ensure that stroke selection is in sync with its points */ - gpencil_stroke_sync_selection(gps); + BKE_gpencil_stroke_sync_selection(gps); } - CTX_DATA_END; - + GP_EDITABLE_STROKES_END; + /* cleanup */ MEM_freeN((void *)mcords); @@ -1020,35 +1054,42 @@ static int gpencil_select_exec(bContext *C, wmOperator *op) /* First Pass: Find stroke point which gets hit */ /* XXX: maybe we should go from the top of the stack down instead... */ - CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) + GP_EDITABLE_STROKES_BEGIN(C, gpl, gps) { bGPDspoint *pt; int i; - + /* firstly, check for hit-point */ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { int xy[2]; - - gp_point_to_xy(&gsc, gps, pt, &xy[0], &xy[1]); - + + if (gpl->parent == NULL) { + gp_point_to_xy(&gsc, gps, pt, &xy[0], &xy[1]); + } + else { + bGPDspoint pt2; + gp_point_to_parent_space(pt, diff_mat, &pt2); + gp_point_to_xy(&gsc, gps, &pt2, &xy[0], &xy[1]); + } + /* do boundbox check first */ if (!ELEM(V2D_IS_CLIPPED, xy[0], xy[1])) { const int pt_distance = len_manhattan_v2v2_int(mval, xy); - + /* check if point is inside */ if (pt_distance <= radius_squared) { /* only use this point if it is a better match than the current hit - T44685 */ if (pt_distance < hit_distance) { hit_stroke = gps; - hit_point = pt; + hit_point = pt; hit_distance = pt_distance; } } } } } - CTX_DATA_END; - + GP_EDITABLE_STROKES_END; + /* Abort if nothing hit... */ if (ELEM(NULL, hit_stroke, hit_point)) { return OPERATOR_CANCELLED; @@ -1111,7 +1152,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 f9b479ca03d..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); } } @@ -142,7 +142,7 @@ void gpencil_undo_push(bGPdata *gpd) */ undo_node->gpd->adt = NULL; - BKE_gpencil_free(undo_node->gpd); + BKE_gpencil_free(undo_node->gpd, false); MEM_freeN(undo_node->gpd); BLI_freelinkN(&undo_nodes, undo_node); @@ -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; @@ -170,7 +170,7 @@ void gpencil_undo_finish(void) */ undo_node->gpd->adt = NULL; - BKE_gpencil_free(undo_node->gpd); + BKE_gpencil_free(undo_node->gpd, false); MEM_freeN(undo_node->gpd); undo_node = undo_node->next; diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c index d62625baaa4..ed9a591dcbe 100644 --- a/source/blender/editors/gpencil/gpencil_utils.c +++ b/source/blender/editors/gpencil/gpencil_utils.c @@ -17,7 +17,7 @@ * * The Original Code is Copyright (C) 2014, Blender Foundation * - * Contributor(s): Joshua Leung + * Contributor(s): Joshua Leung, Antonio Vazquez * * ***** END GPL LICENSE BLOCK ***** */ @@ -32,9 +32,13 @@ #include <stddef.h> #include <math.h> +#include "MEM_guardedalloc.h" + #include "BLI_math.h" #include "BLI_blenlib.h" #include "BLI_utildefines.h" +#include "BLT_translation.h" +#include "BLI_rand.h" #include "DNA_gpencil_types.h" #include "DNA_object_types.h" @@ -46,6 +50,7 @@ #include "BKE_context.h" #include "BKE_gpencil.h" #include "BKE_tracking.h" +#include "BKE_action.h" #include "WM_api.h" @@ -220,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 @@ -234,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 @@ -264,11 +269,39 @@ 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); } +/* poll callback for checking if there is an active brush */ +int gp_active_brush_poll(bContext *C) +{ + ToolSettings *ts = CTX_data_tool_settings(C); + bGPDbrush *brush = BKE_gpencil_brush_getactive(ts); + + return (brush != NULL); +} + +/* poll callback for checking if there is an active palette */ +int gp_active_palette_poll(bContext *C) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); + + return (palette != NULL); +} + +/* poll callback for checking if there is an active palette color */ +int gp_active_palettecolor_poll(bContext *C) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); + bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette); + + return (palcolor != NULL); +} + /* ******************************************************** */ /* Dynamic Enums of GP Layers */ /* NOTE: These include an option to create a new layer and use that... */ @@ -412,6 +445,60 @@ bool ED_gpencil_stroke_can_use(const bContext *C, const bGPDstroke *gps) return ED_gpencil_stroke_can_use_direct(sa, gps); } +/* Check whether given stroke can be edited for the current color */ +bool ED_gpencil_stroke_color_use(const bGPDlayer *gpl, const bGPDstroke *gps) +{ + /* check if the color is editable */ + bGPDpalettecolor *palcolor = gps->palcolor; + if (palcolor != NULL) { + if (palcolor->flag & PC_COLOR_HIDE) + return false; + if (((gpl->flag & GP_LAYER_UNLOCK_COLOR) == 0) && (palcolor->flag & PC_COLOR_LOCKED)) + return false; + } + + return true; +} + +/* Get palette color or create a new one */ +bGPDpalettecolor *ED_gpencil_stroke_getcolor(bGPdata *gpd, bGPDstroke *gps) +{ + bGPDpalette *palette; + bGPDpalettecolor *palcolor; + + if ((gps->palcolor != NULL) && ((gps->flag & GP_STROKE_RECALC_COLOR) == 0)) + return gps->palcolor; + + /* get palette */ + palette = BKE_gpencil_palette_getactive(gpd); + if (palette == NULL) { + palette = BKE_gpencil_palette_addnew(gpd, DATA_("GP_Palette"), true); + } + /* get color */ + palcolor = BKE_gpencil_palettecolor_getbyname(palette, gps->colorname); + if (palcolor == NULL) { + if (gps->palcolor == NULL) { + 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 = BKE_gpencil_palettecolor_addnew(palette, gps->colorname, true); + /* set old color and attributes */ + bGPDpalettecolor *gpscolor = gps->palcolor; + copy_v4_v4(palcolor->color, gpscolor->color); + copy_v4_v4(palcolor->fill, gpscolor->fill); + palcolor->flag = gpscolor->flag; + } + } + + /* clear flag and set pointer */ + gps->flag &= ~GP_STROKE_RECALC_COLOR; + gps->palcolor = palcolor; + + return palcolor; +} + /* ******************************************************** */ /* Space Conversion */ @@ -451,6 +538,50 @@ void gp_point_conversion_init(bContext *C, GP_SpaceConversion *r_gsc) } } +/* convert point to parent space */ +void gp_point_to_parent_space(bGPDspoint *pt, float diff_mat[4][4], bGPDspoint *r_pt) +{ + float fpt[3]; + + mul_v3_m4v3(fpt, diff_mat, &pt->x); + copy_v3_v3(&r_pt->x, fpt); +} + +/* Change position relative to parent object */ +void gp_apply_parent(bGPDlayer *gpl, bGPDstroke *gps) +{ + bGPDspoint *pt; + int i; + + /* undo matrix */ + float diff_mat[4][4]; + float inverse_diff_mat[4][4]; + float fpt[3]; + + ED_gpencil_parent_location(gpl, diff_mat); + invert_m4_m4(inverse_diff_mat, diff_mat); + + for (i = 0; i < gps->totpoints; i++) { + pt = &gps->points[i]; + mul_v3_m4v3(fpt, inverse_diff_mat, &pt->x); + copy_v3_v3(&pt->x, fpt); + } +} + +/* Change point position relative to parent object */ +void gp_apply_parent_point(bGPDlayer *gpl, bGPDspoint *pt) +{ + /* undo matrix */ + float diff_mat[4][4]; + float inverse_diff_mat[4][4]; + float fpt[3]; + + ED_gpencil_parent_location(gpl, diff_mat); + invert_m4_m4(inverse_diff_mat, diff_mat); + + mul_v3_m4v3(fpt, inverse_diff_mat, &pt->x); + copy_v3_v3(&pt->x, fpt); +} /* Convert Grease Pencil points to screen-space values * WARNING: This assumes that the caller has already checked whether the stroke in question can be drawn @@ -591,25 +722,107 @@ bool gp_smooth_stroke(bGPDstroke *gps, int i, float inf, bool affect_pressure) madd_v3_v3fl(sco, &pt1->x, average_fac); madd_v3_v3fl(sco, &pt2->x, average_fac); +#if 0 + /* XXX: Disabled because get weird result */ /* do pressure too? */ if (affect_pressure) { pressure += pt1->pressure * average_fac; pressure += pt2->pressure * average_fac; } +#endif } } /* Based on influence factor, blend between original and optimal smoothed coordinate */ interp_v3_v3v3(&pt->x, &pt->x, sco, inf); +#if 0 + /* XXX: Disabled because get weird result */ if (affect_pressure) { pt->pressure = pressure; } +#endif return true; } /** +* Apply smooth for strength to stroke point +* \param gps Stroke to smooth +* \param i Point index +* \param inf Amount of smoothing to apply +*/ +bool gp_smooth_stroke_strength(bGPDstroke *gps, int i, float inf) +{ + bGPDspoint *ptb = &gps->points[i]; + + /* Do nothing if not enough points */ + if (gps->totpoints <= 2) { + return false; + } + + /* Compute theoretical optimal value using distances */ + bGPDspoint *pta, *ptc; + int before = i - 1; + int after = i + 1; + + CLAMP_MIN(before, 0); + CLAMP_MAX(after, gps->totpoints - 1); + + pta = &gps->points[before]; + ptc = &gps->points[after]; + + /* the optimal value is the corresponding to the interpolation of the strength + * at the distance of point b + */ + const float fac = line_point_factor_v3(&ptb->x, &pta->x, &ptc->x); + const float optimal = (1.0f - fac) * pta->strength + fac * ptc->strength; + + /* Based on influence factor, blend between original and optimal */ + ptb->strength = (1.0f - inf) * ptb->strength + inf * optimal; + + return true; +} + +/** +* Apply smooth for thickness to stroke point (use pressure) +* \param gps Stroke to smooth +* \param i Point index +* \param inf Amount of smoothing to apply +*/ +bool gp_smooth_stroke_thickness(bGPDstroke *gps, int i, float inf) +{ + bGPDspoint *ptb = &gps->points[i]; + + /* Do nothing if not enough points */ + if (gps->totpoints <= 2) { + return false; + } + + /* Compute theoretical optimal value using distances */ + bGPDspoint *pta, *ptc; + int before = i - 1; + int after = i + 1; + + CLAMP_MIN(before, 0); + CLAMP_MAX(after, gps->totpoints - 1); + + pta = &gps->points[before]; + ptc = &gps->points[after]; + + /* the optimal value is the corresponding to the interpolation of the pressure + * at the distance of point b + */ + float fac = line_point_factor_v3(&ptb->x, &pta->x, &ptc->x); + float optimal = (1.0f - fac) * pta->pressure + fac * ptc->pressure; + + /* Based on influence factor, blend between original and optimal */ + ptb->pressure = (1.0f - inf) * ptb->pressure + inf * optimal; + + return true; +} + +/** * Subdivide a stroke once, by adding a point half way between each pair of existing points * \param gps Stroke data * \param new_totpoints Total number of points (after subdividing) @@ -633,16 +846,99 @@ void gp_subdivide_stroke(bGPDstroke *gps, const int new_totpoints) interp_v3_v3v3(&pt->x, &prev->x, &next->x, 0.5f); pt->pressure = interpf(prev->pressure, next->pressure, 0.5f); - pt->time = interpf(prev->time, next->time, 0.5f); + pt->strength = interpf(prev->strength, next->strength, 0.5f); + CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); + pt->time = interpf(prev->time, next->time, 0.5f); } /* Update to new total number of points */ gps->totpoints = new_totpoints; } -/* ******************************************************** */ +/** + * Add randomness to stroke + * \param gps Stroke data + * \param brsuh Brush data + */ +void gp_randomize_stroke(bGPDstroke *gps, bGPDbrush *brush) +{ + bGPDspoint *pt1, *pt2, *pt3; + float v1[3]; + float v2[3]; + if (gps->totpoints < 3) { + return; + } + + /* get two vectors using 3 points */ + pt1 = &gps->points[0]; + pt2 = &gps->points[1]; + pt3 = &gps->points[(int)(gps->totpoints * 0.75)]; + + sub_v3_v3v3(v1, &pt2->x, &pt1->x); + sub_v3_v3v3(v2, &pt3->x, &pt2->x); + normalize_v3(v1); + normalize_v3(v2); + + /* get normal vector to plane created by two vectors */ + 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]; + /* get vector with shift (apply a division because random is too sensitive */ + const float fac = BLI_frand() * (brush->draw_random_sub / 10.0f); + float svec[3]; + copy_v3_v3(svec, ortho); + if (BLI_frand() > 0.5f) { + mul_v3_fl(svec, -fac); + } + else { + mul_v3_fl(svec, fac); + } + + /* apply shift */ + add_v3_v3(&pt->x, svec); + } + +} +/* calculate difference matrix */ +void ED_gpencil_parent_location(bGPDlayer *gpl, float diff_mat[4][4]) +{ + Object *ob = gpl->parent; + if (ob == NULL) { + unit_m4(diff_mat); + return; + } + else { + if ((gpl->partype == PAROBJECT) || (gpl->partype == PARSKEL)) { + mul_m4_m4m4(diff_mat, ob->obmat, gpl->inverse); + return; + } + else if (gpl->partype == PARBONE) { + bPoseChannel *pchan = BKE_pose_channel_find_name(ob->pose, gpl->parsubstr); + if (pchan) { + float tmp_mat[4][4]; + mul_m4_m4m4(tmp_mat, ob->obmat, pchan->pose_mat); + mul_m4_m4m4(diff_mat, tmp_mat, gpl->inverse); + } + else { + mul_m4_m4m4(diff_mat, ob->obmat, gpl->inverse); /* if bone not found use object (armature) */ + } + return; + } + else { + unit_m4(diff_mat); /* not defined type */ + } + } +} +/* ******************************************************** */ bool ED_gpencil_stroke_minmax( const bGPDstroke *gps, const bool use_select, float r_min[3], float r_max[3]) @@ -659,3 +955,74 @@ bool ED_gpencil_stroke_minmax( } return changed; } +/* Dynamic Enums of GP Brushes */ + +EnumPropertyItem *ED_gpencil_brushes_enum_itemf( + bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), + bool *r_free) +{ + ToolSettings *ts = CTX_data_tool_settings(C); + bGPDbrush *brush; + EnumPropertyItem *item = NULL, item_tmp = { 0 }; + int totitem = 0; + int i = 0; + + if (ELEM(NULL, C, ts)) { + return DummyRNA_DEFAULT_items; + } + + /* Existing brushes */ + for (brush = ts->gp_brushes.first; brush; brush = brush->next, i++) { + item_tmp.identifier = brush->info; + item_tmp.name = brush->info; + item_tmp.value = i; + + if (brush->flag & GP_BRUSH_ACTIVE) + item_tmp.icon = ICON_BRUSH_DATA; + else + item_tmp.icon = ICON_NONE; + + RNA_enum_item_add(&item, &totitem, &item_tmp); + } + + RNA_enum_item_end(&item, &totitem); + *r_free = true; + + return item; +} +/* Dynamic Enums of GP Palettes */ + +EnumPropertyItem *ED_gpencil_palettes_enum_itemf( + bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), + bool *r_free) +{ + bGPdata *gpd = CTX_data_gpencil_data(C); + bGPDpalette *palette; + EnumPropertyItem *item = NULL, item_tmp = { 0 }; + int totitem = 0; + int i = 0; + + if (ELEM(NULL, C, gpd)) { + return DummyRNA_DEFAULT_items; + } + + /* Existing palettes */ + for (palette = gpd->palettes.first; palette; palette = palette->next, i++) { + item_tmp.identifier = palette->info; + item_tmp.name = palette->info; + item_tmp.value = i; + + if (palette->flag & PL_PALETTE_ACTIVE) + item_tmp.icon = ICON_COLOR; + else + item_tmp.icon = ICON_NONE; + + RNA_enum_item_add(&item, &totitem, &item_tmp); + } + + RNA_enum_item_end(&item, &totitem); + *r_free = true; + + return item; +} +/* ******************************************************** */ diff --git a/source/blender/editors/include/ED_anim_api.h b/source/blender/editors/include/ED_anim_api.h index 1095c770703..99f9287be8f 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, @@ -274,6 +275,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_MBALL_OBJD(mb) (CHECK_TYPE_INLINE(mb, MetaBall *), ((mb->flag2 & MB_DS_EXPAND))) #define FILTER_ARM_OBJD(arm) (CHECK_TYPE_INLINE(arm, bArmature *), ((arm->flag & ARM_DS_EXPAND))) diff --git a/source/blender/editors/include/ED_gpencil.h b/source/blender/editors/include/ED_gpencil.h index de5ab80a88f..d526b0841cc 100644 --- a/source/blender/editors/include/ED_gpencil.h +++ b/source/blender/editors/include/ED_gpencil.h @@ -41,6 +41,8 @@ struct bGPdata; struct bGPDlayer; struct bGPDframe; struct bGPDstroke; +struct bGPDpalette; +struct bGPDpalettecolor; struct bAnimContext; struct KeyframeEditData; struct PointerRNA; @@ -57,6 +59,7 @@ struct wmKeyConfig; typedef struct tGPspoint { int x, y; /* x and y coordinates of cursor (in relative to area) */ float pressure; /* pressure of tablet at this point */ + float strength; /* pressure of tablet at this point for alpha factor */ float time; /* Time relative to stroke start (used when converting to path) */ } tGPspoint; @@ -86,6 +89,9 @@ bool ED_gpencil_has_keyframe_v3d(struct Scene *scene, struct Object *ob, int cfr bool ED_gpencil_stroke_can_use_direct(const struct ScrArea *sa, const struct bGPDstroke *gps); bool ED_gpencil_stroke_can_use(const struct bContext *C, const struct bGPDstroke *gps); +bool ED_gpencil_stroke_color_use(const struct bGPDlayer *gpl, const struct bGPDstroke *gps); + +struct bGPDpalettecolor *ED_gpencil_stroke_getcolor(struct bGPdata *gpd, struct bGPDstroke *gps); bool ED_gpencil_stroke_minmax( const struct bGPDstroke *gps, const bool use_select, @@ -142,4 +148,10 @@ bool ED_gpencil_anim_copybuf_paste(struct bAnimContext *ac, const short copy_mod int ED_gpencil_session_active(void); int ED_undo_gpencil_step(struct bContext *C, int step, const char *name); +/* ------------ Transformation Utilities ------------ */ + +/* get difference matrix using parent */ +void ED_gpencil_parent_location(struct bGPDlayer *gpl, float diff_mat[4][4]); + + #endif /* __ED_GPENCIL_H__ */ 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_icons.h b/source/blender/editors/include/UI_icons.h index d85b60dcc43..e016e014a1a 100644 --- a/source/blender/editors/include/UI_icons.h +++ b/source/blender/editors/include/UI_icons.h @@ -320,9 +320,9 @@ DEF_ICON(OUTLINER_OB_SPEAKER) DEF_ICON(BLANK123) DEF_ICON(BLANK124) DEF_ICON(BLANK125) - DEF_ICON(BLANK126) - DEF_ICON(BLANK127) #endif +DEF_ICON(RESTRICT_COLOR_OFF) +DEF_ICON(RESTRICT_COLOR_ON) DEF_ICON(RESTRICT_VIEW_OFF) DEF_ICON(RESTRICT_VIEW_ON) DEF_ICON(RESTRICT_SELECT_OFF) diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index f77e795adca..1f053c806b0 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -945,6 +945,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_icons.c b/source/blender/editors/interface/interface_icons.c index 73a7be16176..646260b770e 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_templates.c b/source/blender/editors/interface/interface_templates.c index 3af25ba8894..b3f2192c26c 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" @@ -366,6 +367,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"); @@ -1905,6 +1907,7 @@ enum { UICURVE_FUNC_RESET_VIEW, UICURVE_FUNC_HANDLE_VECTOR, UICURVE_FUNC_HANDLE_AUTO, + UICURVE_FUNC_HANDLE_AUTO_ANIM, UICURVE_FUNC_EXTEND_HOZ, UICURVE_FUNC_EXTEND_EXP, }; @@ -1925,13 +1928,16 @@ static void curvemap_tools_dofunc(bContext *C, void *cumap_v, int event) cumap->curr = cumap->clipr; break; case UICURVE_FUNC_HANDLE_VECTOR: /* set vector */ - curvemap_sethandle(cuma, 1); + curvemap_handle_set(cuma, HD_VECT); curvemapping_changed(cumap, false); break; case UICURVE_FUNC_HANDLE_AUTO: /* set auto */ - curvemap_sethandle(cuma, 0); + curvemap_handle_set(cuma, HD_AUTO); curvemapping_changed(cumap, false); break; + case UICURVE_FUNC_HANDLE_AUTO_ANIM: /* set auto-clamped */ + curvemap_handle_set(cuma, HD_AUTO_ANIM); + curvemapping_changed(cumap, false); case UICURVE_FUNC_EXTEND_HOZ: /* extend horiz */ cuma->flag &= ~CUMA_EXTEND_EXTRAPOLATE; curvemapping_changed(cumap, false); @@ -1965,6 +1971,9 @@ static uiBlock *curvemap_tools_func( uiDefIconTextBut( block, UI_BTYPE_BUT_MENU, 1, ICON_BLANK1, IFACE_("Auto Handle"), 0, yco -= UI_UNIT_Y, menuwidth, UI_UNIT_Y, NULL, 0.0, 0.0, 0, UICURVE_FUNC_HANDLE_AUTO, ""); + uiDefIconTextBut( + block, UI_BTYPE_BUT_MENU, 1, ICON_BLANK1, IFACE_("Auto Clamped Handle"), + 0, yco -= UI_UNIT_Y, menuwidth, UI_UNIT_Y, NULL, 0.0, 0.0, 0, UICURVE_FUNC_HANDLE_AUTO_ANIM, ""); } if (show_extend) { @@ -3806,3 +3815,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/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..7a7c42e501b --- /dev/null +++ b/source/blender/editors/io/io_alembic.c @@ -0,0 +1,458 @@ +/* + * ***** 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")) { + 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); + } + + 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 = uiLayoutBox(layout); + uiLayout *row; + +#ifdef WITH_ALEMBIC_HDF5 + 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); +} + +void WM_OT_alembic_export(wmOperatorType *ot) +{ + ot->name = "Export 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; + + 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 datas 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 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..d6e2c1ae204 --- /dev/null +++ b/source/blender/editors/io/io_cache.c @@ -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. + * + * 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->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_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/object/object_add.c b/source/blender/editors/object/object_add.c index 15c1a195db5..313181abc97 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -48,6 +48,7 @@ #include "DNA_scene_types.h" #include "DNA_vfont_types.h" #include "DNA_actuator_types.h" +#include "DNA_gpencil_types.h" #include "BLI_utildefines.h" #include "BLI_ghash.h" @@ -1144,10 +1145,22 @@ static int object_delete_exec(bContext *C, wmOperator *op) } else if (is_indirectly_used && ID_REAL_USERS(base->object) <= 1) { BKE_reportf(op->reports, RPT_WARNING, - "Cannot delete object '%s' from scene '%s', indirectly used objects need at least one user", - base->object->id.name + 2, scene->id.name + 2); + "Cannot delete object '%s' from scene '%s', indirectly used objects need at least one user", + base->object->id.name + 2, scene->id.name + 2); continue; } + /* remove from Grease Pencil parent */ + for (bGPdata *gpd = bmain->gpencil.first; gpd; gpd = gpd->id.next) { + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + if (gpl->parent != NULL) { + Object *ob = gpl->parent; + Object *curob = base->object; + if (ob == curob) { + gpl->parent = NULL; + } + } + } + } /* deselect object -- it could be used in other scenes */ base->object->flag &= ~SELECT; 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_relations.c b/source/blender/editors/object/object_relations.c index d8b0767562c..b735896459a 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -47,6 +47,7 @@ #include "DNA_world_types.h" #include "DNA_object_types.h" #include "DNA_vfont_types.h" +#include "DNA_gpencil_types.h" #include "BLI_math.h" #include "BLI_listbase.h" @@ -1761,6 +1762,14 @@ static void single_object_users(Main *bmain, Scene *scene, View3D *v3d, const in else { /* 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; + } + } + base->flag = obn->flag; id_us_min(&ob->id); diff --git a/source/blender/editors/render/render_opengl.c b/source/blender/editors/render/render_opengl.c index 992b827113d..85c05ab0e5c 100644 --- a/source/blender/editors/render/render_opengl.c +++ b/source/blender/editors/render/render_opengl.c @@ -43,6 +43,7 @@ #include "DNA_scene_types.h" #include "DNA_object_types.h" +#include "DNA_gpencil_types.h" #include "BKE_camera.h" #include "BKE_context.h" @@ -414,6 +415,99 @@ static void screen_opengl_render_write(OGLRender *oglrender) else printf("OpenGL Render failed to write '%s'\n", name); } +static void addAlphaOverFloat(float dest[4], const float source[4]) +{ + /* d = s + (1-alpha_s)d*/ + float mul; + + mul = 1.0f - source[3]; + + dest[0] = (mul * dest[0]) + source[0]; + dest[1] = (mul * dest[1]) + source[1]; + dest[2] = (mul * dest[2]) + source[2]; + dest[3] = (mul * dest[3]) + source[3]; + +} + +/* add renderlayer and renderpass for each grease pencil layer for using in composition */ +static void add_gpencil_renderpass(OGLRender *oglrender, RenderResult *rr, RenderView *rv) +{ + bGPdata *gpd = oglrender->scene->gpd; + Scene *scene = oglrender->scene; + + /* sanity checks */ + if (gpd == NULL) { + return; + } + if (scene == NULL) { + return; + } + if (BLI_listbase_is_empty(&gpd->layers)) { + return; + } + + /* save old alpha mode */ + short oldalphamode = scene->r.alphamode; + /* set alpha transparent for gp */ + scene->r.alphamode = R_ALPHAPREMUL; + + /* saves layer status */ + short *oldsts = MEM_mallocN(BLI_listbase_count(&gpd->layers) * sizeof(short), "temp_gplayers_flag"); + int i = 0; + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + oldsts[i] = gpl->flag; + ++i; + } + /* loop all layers to create separate render */ + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + /* dont draw layer if hidden */ + if (gpl->flag & GP_LAYER_HIDE) + continue; + /* hide all layer except current */ + for (bGPDlayer *gph = gpd->layers.first; gph; gph = gph->next) { + if (gpl != gph) { + gph->flag |= GP_LAYER_HIDE; + } + } + + /* render this gp layer */ + screen_opengl_render_doit(oglrender, rr); + + /* add RendePass composite */ + RenderPass *rp = RE_create_gp_pass(rr, gpl->info, rv->name); + + /* copy image data from rectf */ + float *src = RE_RenderViewGetById(rr, oglrender->view_id)->rectf; + float *dest = rp->rect; + + float *pixSrc, *pixDest; + int x, y, rectx, recty; + rectx = rr->rectx; + recty = rr->recty; + for (y = 0; y < recty; y++) { + for (x = 0; x < rectx; x++) { + pixSrc = src + 4 * (rectx * y + x); + if (pixSrc[3] > 0.0) { + pixDest = dest + 4 * (rectx * y + x); + addAlphaOverFloat(pixDest, pixSrc); + } + } + } + + /* back layer status */ + i = 0; + for (bGPDlayer *gph = gpd->layers.first; gph; gph = gph->next) { + gph->flag = oldsts[i]; + ++i; + } + } + /* free memory */ + MEM_freeN(oldsts); + + /* back default alpha mode */ + scene->r.alphamode = oldalphamode; +} + static void screen_opengl_render_apply(OGLRender *oglrender) { RenderResult *rr; @@ -449,6 +543,9 @@ static void screen_opengl_render_apply(OGLRender *oglrender) BLI_assert(view_id < oglrender->views_len); RE_SetActiveRenderView(oglrender->re, rv->name); oglrender->view_id = view_id; + /* add grease pencil passes */ + add_gpencil_renderpass(oglrender, rr, rv); + /* render composite */ screen_opengl_render_doit(oglrender, rr); } diff --git a/source/blender/editors/render/render_preview.c b/source/blender/editors/render/render_preview.c index b2aefb5e523..b4f3426677a 100644 --- a/source/blender/editors/render/render_preview.c +++ b/source/blender/editors/render/render_preview.c @@ -1210,7 +1210,7 @@ void ED_preview_icon_job(const bContext *C, void *owner, ID *id, unsigned int *r /* setup job */ WM_jobs_customdata_set(wm_job, ip, icon_preview_free); - WM_jobs_timer(wm_job, 0.1, NC_WINDOW , NC_WINDOW); + WM_jobs_timer(wm_job, 0.1, NC_WINDOW, NC_WINDOW); WM_jobs_callbacks(wm_job, icon_preview_startjob_all_sizes, NULL, NULL, icon_preview_endjob); WM_jobs_start(CTX_wm_manager(C), wm_job); diff --git a/source/blender/editors/screen/screen_context.c b/source/blender/editors/screen/screen_context.c index 99c71dab9dc..b55330602e9 100644 --- a/source/blender/editors/screen/screen_context.c +++ b/source/blender/editors/screen/screen_context.c @@ -85,7 +85,8 @@ const char *screen_context_dir[] = { "sequences", "selected_sequences", "selected_editable_sequences", /* sequencer */ "gpencil_data", "gpencil_data_owner", /* grease pencil data */ "visible_gpencil_layers", "editable_gpencil_layers", "editable_gpencil_strokes", - "active_gpencil_layer", "active_gpencil_frame", + "active_gpencil_layer", "active_gpencil_frame", "active_gpencil_palette", + "active_gpencil_palettecolor", "active_gpencil_brush", "active_operator", NULL}; @@ -116,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)) { @@ -131,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) { @@ -148,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)) { @@ -163,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)) { @@ -182,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) { @@ -201,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... */ @@ -243,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... */ @@ -460,7 +461,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); @@ -468,12 +469,50 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult } } } + else if (CTX_data_equals(member, "active_gpencil_palette")) { + /* XXX: see comment for gpencil_data case... */ + bGPdata *gpd = ED_gpencil_data_get_active_direct((ID *)sc, scene, sa, obact); + + if (gpd) { + bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); + + if (palette) { + CTX_data_pointer_set(result, &gpd->id, &RNA_GPencilPalette, palette); + return 1; + } + } + } + else if (CTX_data_equals(member, "active_gpencil_palettecolor")) { + /* XXX: see comment for gpencil_data case... */ + bGPdata *gpd = ED_gpencil_data_get_active_direct((ID *)sc, scene, sa, obact); + + if (gpd) { + bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); + + if (palette) { + bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette); + if (palcolor) { + CTX_data_pointer_set(result, &gpd->id, &RNA_GPencilPaletteColor, palcolor); + return 1; + } + } + } + } + else if (CTX_data_equals(member, "active_gpencil_brush")) { + /* XXX: see comment for gpencil_data case... */ + bGPDbrush *brush = BKE_gpencil_brush_getactive(scene->toolsettings); + + if (brush) { + CTX_data_pointer_set(result, NULL, &RNA_GPencilBrush, brush); + return 1; + } + } else if (CTX_data_equals(member, "active_gpencil_frame")) { /* XXX: see comment for gpencil_data case... */ 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); @@ -527,6 +566,11 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult for (gps = gpf->strokes.first; gps; gps = gps->next) { if (ED_gpencil_stroke_can_use_direct(sa, gps)) { + /* check if the color is editable */ + if (ED_gpencil_stroke_color_use(gpl, gps) == false) { + continue; + } + CTX_data_list_add(result, &gpd->id, &RNA_GPencilStroke, gps); } } diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c index 62aeca4b9d1..677a6472c72 100644 --- a/source/blender/editors/screen/screen_edit.c +++ b/source/blender/editors/screen/screen_edit.c @@ -1943,6 +1943,7 @@ ScrArea *ED_screen_state_toggle(bContext *C, wmWindow *win, ScrArea *sa, const s sc = ED_screen_add(win, oldscreen->scene, newname); sc->state = state; sc->redraws_flag = oldscreen->redraws_flag; + sc->temp = oldscreen->temp; /* timer */ sc->animtimer = oldscreen->animtimer; diff --git a/source/blender/editors/sculpt_paint/paint_image_2d.c b/source/blender/editors/sculpt_paint/paint_image_2d.c index 080bd5b73c7..9474a46d716 100644 --- a/source/blender/editors/sculpt_paint/paint_image_2d.c +++ b/source/blender/editors/sculpt_paint/paint_image_2d.c @@ -1126,8 +1126,8 @@ static int paint_2d_op(void *state, ImBuf *ibufb, unsigned short *curveb, unsign /* blend into canvas */ for (a = 0; a < tot; a++) { ED_imapaint_dirty_region(s->image, s->canvas, - region[a].destx, region[a].desty, - region[a].width, region[a].height, true); + region[a].destx, region[a].desty, + region[a].width, region[a].height, true); if (s->do_masking) { /* masking, find original pixels tiles from undo buffer to composite over */ diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 1305b76b5ad..fe0fb3f5035 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -1239,7 +1239,7 @@ static float brush_strength( /* Return a multiplier for brush strength on a particular vertex. */ static float tex_strength(SculptSession *ss, Brush *br, - const float point[3], + const float brush_point[3], const float len, const short vno[3], const float fno[3], @@ -1251,6 +1251,9 @@ static float tex_strength(SculptSession *ss, Brush *br, MTex *mtex = &br->mtex; float avg = 1; float rgba[4]; + float point[3]; + + sub_v3_v3v3(point, brush_point, cache->plane_offset); if (!mtex->tex) { avg = 1; diff --git a/source/blender/editors/space_action/action_buttons.c b/source/blender/editors/space_action/action_buttons.c index 063a477d2b6..bfc808f6d83 100644 --- a/source/blender/editors/space_action/action_buttons.c +++ b/source/blender/editors/space_action/action_buttons.c @@ -122,7 +122,7 @@ void ACTION_OT_properties(wmOperatorType *ot) { ot->name = "Properties"; ot->idname = "ACTION_OT_properties"; - ot->description = "Toggle display properties panel"; + ot->description = "Toggle the properties region visibility"; ot->exec = action_properties_toggle_exec; ot->poll = ED_operator_action_active; 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_clip/clip_toolbar.c b/source/blender/editors/space_clip/clip_toolbar.c index d31d7f53b30..b042bbe8fea 100644 --- a/source/blender/editors/space_clip/clip_toolbar.c +++ b/source/blender/editors/space_clip/clip_toolbar.c @@ -106,7 +106,7 @@ void CLIP_OT_properties(wmOperatorType *ot) { /* identifiers */ ot->name = "Properties"; - ot->description = "Toggle clip properties panel"; + ot->description = "Toggle the properties region visibility"; ot->idname = "CLIP_OT_properties"; /* api callbacks */ diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index 5e9eb1f9207..b6e4991bf52 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -920,6 +920,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) { @@ -1952,6 +1954,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 +2009,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/filesel.c b/source/blender/editors/space_file/filesel.c index 07e1b2c4d79..d9293aa126a 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_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; diff --git a/source/blender/editors/space_graph/graph_buttons.c b/source/blender/editors/space_graph/graph_buttons.c index a9ab1502e16..516814b63b4 100644 --- a/source/blender/editors/space_graph/graph_buttons.c +++ b/source/blender/editors/space_graph/graph_buttons.c @@ -812,6 +812,11 @@ static void graph_panel_drivers(const bContext *C, Panel *pa) } col = uiLayoutColumn(pa->layout, true); + + if (driver->type == DRIVER_TYPE_PYTHON) { + uiItemR(col, &driver_ptr, "use_self", 0, NULL, ICON_NONE); + } + /* debug setting */ uiItemR(col, &driver_ptr, "show_debug_info", 0, NULL, ICON_NONE); @@ -1062,7 +1067,7 @@ void GRAPH_OT_properties(wmOperatorType *ot) { ot->name = "Properties"; ot->idname = "GRAPH_OT_properties"; - ot->description = "Toggle display properties panel"; + ot->description = "Toggle the properties region visibility"; ot->exec = graph_properties_toggle_exec; ot->poll = ED_operator_graphedit_active; diff --git a/source/blender/editors/space_image/image_buttons.c b/source/blender/editors/space_image/image_buttons.c index 38a54ade367..b9d98dfe794 100644 --- a/source/blender/editors/space_image/image_buttons.c +++ b/source/blender/editors/space_image/image_buttons.c @@ -1330,7 +1330,7 @@ void IMAGE_OT_properties(wmOperatorType *ot) { ot->name = "Properties"; ot->idname = "IMAGE_OT_properties"; - ot->description = "Toggle display properties panel"; + ot->description = "Toggle the properties region visibility"; ot->exec = image_properties_toggle_exec; ot->poll = ED_operator_image_active; diff --git a/source/blender/editors/space_logic/logic_buttons.c b/source/blender/editors/space_logic/logic_buttons.c index 12c7ef3d3ec..e5eee21ed08 100644 --- a/source/blender/editors/space_logic/logic_buttons.c +++ b/source/blender/editors/space_logic/logic_buttons.c @@ -63,7 +63,7 @@ static int logic_properties_toggle_exec(bContext *C, wmOperator *UNUSED(op)) void LOGIC_OT_properties(wmOperatorType *ot) { ot->name = "Properties"; - ot->description = "Toggle display properties panel"; + ot->description = "Toggle the properties region visibility"; ot->idname = "LOGIC_OT_properties"; ot->exec = logic_properties_toggle_exec; diff --git a/source/blender/editors/space_nla/nla_buttons.c b/source/blender/editors/space_nla/nla_buttons.c index a23987244f2..bb6cf568425 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: @@ -566,7 +567,7 @@ void NLA_OT_properties(wmOperatorType *ot) { ot->name = "Properties"; ot->idname = "NLA_OT_properties"; - ot->description = "Toggle display properties panel"; + ot->description = "Toggle the properties region visibility"; ot->exec = nla_properties_toggle_exec; ot->poll = ED_operator_nla_active; diff --git a/source/blender/editors/space_nla/nla_channels.c b/source/blender/editors/space_nla/nla_channels.c index 0c91a2eb1c6..957d5a96e1e 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_node/drawnode.c b/source/blender/editors/space_node/drawnode.c index ddbd07616bc..7b08b8368ba 100644 --- a/source/blender/editors/space_node/drawnode.c +++ b/source/blender/editors/space_node/drawnode.c @@ -1338,7 +1338,7 @@ static void node_composit_buts_renderlayers(uiLayout *layout, bContext *C, Point scn_ptr = RNA_pointer_get(ptr, "scene"); RNA_string_get(&scn_ptr, "name", scene_name); - + WM_operator_properties_create_ptr(&op_ptr, ot); RNA_string_set(&op_ptr, "layer", layer_name); RNA_string_set(&op_ptr, "scene", scene_name); diff --git a/source/blender/editors/space_node/node_buttons.c b/source/blender/editors/space_node/node_buttons.c index 52b0292b9a3..f0567924edd 100644 --- a/source/blender/editors/space_node/node_buttons.c +++ b/source/blender/editors/space_node/node_buttons.c @@ -222,7 +222,7 @@ static int node_properties_poll(bContext *C) void NODE_OT_properties(wmOperatorType *ot) { ot->name = "Properties"; - ot->description = "Toggles the properties panel display"; + ot->description = "Toggle the properties region visibility"; ot->idname = "NODE_OT_properties"; ot->exec = node_properties_toggle_exec; diff --git a/source/blender/editors/space_outliner/outliner_draw.c b/source/blender/editors/space_outliner/outliner_draw.c index 14efd4338b4..fcee64775a9 100644 --- a/source/blender/editors/space_outliner/outliner_draw.c +++ b/source/blender/editors/space_outliner/outliner_draw.c @@ -1002,7 +1002,7 @@ static void tselem_draw_icon_uibut(struct DrawIconArg *arg, int icon) } -static void tselem_draw_gp_icon_uibut(struct DrawIconArg *arg, ID *id, bGPDlayer *gpl) +static void UNUSED_FUNCTION(tselem_draw_gp_icon_uibut)(struct DrawIconArg *arg, ID *id, bGPDlayer *gpl) { /* restrict column clip - skip it for now... */ if (arg->x >= arg->xmax) { @@ -1167,6 +1167,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: @@ -1227,9 +1229,12 @@ static void tselem_draw_icon(uiBlock *block, int xmax, float x, float y, TreeSto else UI_icon_draw(x, y, RNA_struct_ui_icon(te->rnaptr.type)); break; + /* Removed the icons from outliner. Need a better structure with Layers, Palettes and Colors */ +#if 0 case TSE_GP_LAYER: tselem_draw_gp_icon_uibut(&arg, tselem->id, te->directdata); break; +#endif default: UI_icon_draw(x, y, ICON_DOT); break; } diff --git a/source/blender/editors/space_outliner/outliner_intern.h b/source/blender/editors/space_outliner/outliner_intern.h index 4ee8e55ce82..a65733ce667 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_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 5f4c5837df4..e5c2de466c2 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" @@ -737,6 +738,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_buttons.c b/source/blender/editors/space_sequencer/sequencer_buttons.c index 86d3fcbe1ac..b33a26db728 100644 --- a/source/blender/editors/space_sequencer/sequencer_buttons.c +++ b/source/blender/editors/space_sequencer/sequencer_buttons.c @@ -94,7 +94,7 @@ void SEQUENCER_OT_properties(wmOperatorType *ot) { ot->name = "Properties"; ot->idname = "SEQUENCER_OT_properties"; - ot->description = "Open sequencer properties panel"; + ot->description = "Toggle the properties region visibility"; ot->exec = sequencer_properties_toggle_exec; ot->poll = ED_operator_sequencer_active; 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_text/text_header.c b/source/blender/editors/space_text/text_header.c index 91665f1a598..e06a5b5474e 100644 --- a/source/blender/editors/space_text/text_header.c +++ b/source/blender/editors/space_text/text_header.c @@ -92,7 +92,7 @@ void TEXT_OT_properties(wmOperatorType *ot) { /* identifiers */ ot->name = "Properties"; - ot->description = "Toggle text properties panel"; + ot->description = "Toggle the properties region visibility"; ot->idname = "TEXT_OT_properties"; /* api callbacks */ diff --git a/source/blender/editors/space_time/space_time.c b/source/blender/editors/space_time/space_time.c index ff9136f3554..46bddc00f0a 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 "ED_anim_api.h" @@ -163,6 +169,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 */ @@ -187,6 +196,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) { @@ -197,7 +256,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) { @@ -423,6 +486,10 @@ static void time_main_region_listener(bScreen *UNUSED(sc), ScrArea *UNUSED(sa), break; } break; + case NC_GPENCIL: + if (wmn->data == ND_DATA) + ED_region_tag_redraw(ar); + break; } } diff --git a/source/blender/editors/space_view3d/drawmesh.c b/source/blender/editors/space_view3d/drawmesh.c index f5289a0d245..74a50497164 100644 --- a/source/blender/editors/space_view3d/drawmesh.c +++ b/source/blender/editors/space_view3d/drawmesh.c @@ -435,12 +435,16 @@ static void draw_textured_begin(Scene *scene, View3D *v3d, RegionView3D *rv3d, O else { /* draw with lights in the scene otherwise */ solidtex = false; - if (v3d->flag2 & V3D_SHADELESS_TEX) + if (v3d->flag2 & V3D_SHADELESS_TEX) { Gtexdraw.is_lit = 0; - else - Gtexdraw.is_lit = GPU_scene_object_lights(scene, ob, v3d->lay, rv3d->viewmat, !rv3d->is_persp); + } + else { + Gtexdraw.is_lit = GPU_scene_object_lights( + scene, ob, v3d->localvd ? v3d->localvd->lay : v3d->lay, + rv3d->viewmat, !rv3d->is_persp); + } } - + rgba_float_to_uchar(obcol, ob->col); if (solidtex || v3d->drawtype == OB_TEXTURE) is_tex = true; diff --git a/source/blender/editors/space_view3d/drawobject.c b/source/blender/editors/space_view3d/drawobject.c index 9699b2d05d0..bec158b767a 100644 --- a/source/blender/editors/space_view3d/drawobject.c +++ b/source/blender/editors/space_view3d/drawobject.c @@ -4657,7 +4657,7 @@ static bool drawDispList_nobackface(Scene *scene, View3D *v3d, RegionView3D *rv3 index3_nors_incr = true; } else { - if (!render_only || (render_only && BKE_displist_has_faces(lb))) { + if (!render_only || BKE_displist_has_faces(lb)) { return drawDispListwire(lb, ob->type); } } diff --git a/source/blender/editors/space_view3d/view3d_buttons.c b/source/blender/editors/space_view3d/view3d_buttons.c index c52d1238163..b9c8c98b62f 100644 --- a/source/blender/editors/space_view3d/view3d_buttons.c +++ b/source/blender/editors/space_view3d/view3d_buttons.c @@ -1206,7 +1206,7 @@ static int view3d_properties_toggle_exec(bContext *C, wmOperator *UNUSED(op)) void VIEW3D_OT_properties(wmOperatorType *ot) { ot->name = "Properties"; - ot->description = "Toggles the properties panel display"; + ot->description = "Toggle the properties region visibility"; ot->idname = "VIEW3D_OT_properties"; ot->exec = view3d_properties_toggle_exec; diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index 83fde34fcde..dde6c3949c7 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -4514,7 +4514,7 @@ void VIEW3D_OT_background_image_add(wmOperatorType *ot) /* note: having key shortcut here is bad practice, * but for now keep because this displays when dragging an image over the 3D viewport */ ot->name = "Add Background Image (Ctrl for Empty Object)"; - ot->description = "Add a new background image"; + ot->description = "Add a new background image (Ctrl for Empty Object)"; ot->idname = "VIEW3D_OT_background_image_add"; /* api callbacks */ @@ -4523,7 +4523,7 @@ void VIEW3D_OT_background_image_add(wmOperatorType *ot) ot->poll = ED_operator_view3d_active; /* flags */ - ot->flag = 0; + ot->flag = OPTYPE_UNDO; /* properties */ RNA_def_string(ot->srna, "name", "Image", MAX_ID_NAME - 2, "Name", "Image name to assign"); diff --git a/source/blender/editors/space_view3d/view3d_ruler.c b/source/blender/editors/space_view3d/view3d_ruler.c index f46608b7d5e..37b068e3e49 100644 --- a/source/blender/editors/space_view3d/view3d_ruler.c +++ b/source/blender/editors/space_view3d/view3d_ruler.c @@ -302,18 +302,18 @@ static bool view3d_ruler_to_gpencil(bContext *C, RulerInfo *ruler_info) 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); + 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; @@ -327,6 +327,7 @@ static bool view3d_ruler_to_gpencil(bContext *C, RulerInfo *ruler_info) for (j = 0; j < 3; j++) { copy_v3_v3(&pt->x, ruler_item->co[j]); pt->pressure = 1.0f; + pt->strength = 1.0f; pt++; } } @@ -336,6 +337,7 @@ static bool view3d_ruler_to_gpencil(bContext *C, RulerInfo *ruler_info) for (j = 0; j < 3; j += 2) { copy_v3_v3(&pt->x, ruler_item->co[j]); pt->pressure = 1.0f; + pt->strength = 1.0f; pt++; } } @@ -358,7 +360,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/transform/transform.c b/source/blender/editors/transform/transform.c index 6846ebc6680..7ff66c21af5 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -52,6 +52,7 @@ #include "BLI_listbase.h" #include "BLI_string.h" #include "BLI_ghash.h" +#include "BLI_stackdefines.h" #include "BLI_memarena.h" #include "BKE_nla.h" @@ -3719,19 +3720,10 @@ static void initRotation(TransInfo *t) copy_v3_v3(t->axis_orig, t->axis); } -static void ElementRotation(TransInfo *t, TransData *td, float mat[3][3], short around) +static void ElementRotation_ex(TransInfo *t, TransData *td, float mat[3][3], const float *center) { float vec[3], totmat[3][3], smat[3][3]; float eul[3], fmat[3][3], quat[4]; - const float *center; - - /* local constraint shouldn't alter center */ - if (transdata_check_local_center(t, around)) { - center = td->center; - } - else { - center = t->center; - } if (t->flag & T_POINTS) { mul_m3_m3m3(totmat, mat, td->mtx); @@ -3941,6 +3933,21 @@ static void ElementRotation(TransInfo *t, TransData *td, float mat[3][3], short } } +static void ElementRotation(TransInfo *t, TransData *td, float mat[3][3], const short around) +{ + const float *center; + + /* local constraint shouldn't alter center */ + if (transdata_check_local_center(t, around)) { + center = td->center; + } + else { + center = t->center; + } + + ElementRotation_ex(t, td, mat, center); +} + static void applyRotationValue(TransInfo *t, float angle, float axis[3]) { TransData *td = t->data; @@ -3990,10 +3997,8 @@ static void applyRotation(TransInfo *t, const int UNUSED(mval[2])) applySnapping(t, &final); - if (applyNumInput(&t->num, &final)) { - /* Clamp between -PI and PI */ - final = angle_wrap_rad(final); - } + /* Used to clamp final result in [-PI, PI[ range, no idea why, inheritance from 2.4x area, see T48998. */ + applyNumInput(&t->num, &final); t->values[0] = final; @@ -4341,12 +4346,16 @@ static void applyTranslationValue(TransInfo *t, const float vec[3]) if (td->flag & TD_SKIP) continue; + float rotate_offset[3] = {0}; + bool use_rotate_offset = false; + /* handle snapping rotation before doing the translation */ if (usingSnappingNormal(t)) { + float mat[3][3]; + if (validSnappingNormal(t)) { const float *original_normal; - float mat[3][3]; - + /* In pose mode, we want to align normals with Y axis of bones... */ if (t->flag & T_POSE) original_normal = td->axismtx[1]; @@ -4354,18 +4363,19 @@ static void applyTranslationValue(TransInfo *t, const float vec[3]) original_normal = td->axismtx[2]; rotation_between_vecs_to_mat3(mat, original_normal, t->tsnap.snapNormal); - - ElementRotation(t, td, mat, V3D_AROUND_LOCAL_ORIGINS); } else { - float mat[3][3]; - unit_m3(mat); - - ElementRotation(t, td, mat, V3D_AROUND_LOCAL_ORIGINS); + } + + ElementRotation_ex(t, td, mat, t->tsnap.snapTarget); + + if (td->loc) { + use_rotate_offset = true; + sub_v3_v3v3(rotate_offset, td->loc, td->iloc); } } - + if (t->con.applyVec) { float pvec[3]; t->con.applyVec(t, td, vec, tvec, pvec); @@ -4373,6 +4383,10 @@ static void applyTranslationValue(TransInfo *t, const float vec[3]) else { copy_v3_v3(tvec, vec); } + + if (use_rotate_offset) { + add_v3_v3(tvec, rotate_offset); + } mul_m3_v3(td->smtx, tvec); mul_v3_fl(tvec, td->factor); @@ -4381,7 +4395,7 @@ static void applyTranslationValue(TransInfo *t, const float vec[3]) if (td->loc) add_v3_v3v3(td->loc, td->iloc, tvec); - + constraintTransLim(t, td); } } @@ -5893,7 +5907,7 @@ static BMLoop *get_next_loop(BMVert *v, BMLoop *l, */ static void calcEdgeSlide_mval_range( TransInfo *t, EdgeSlideData *sld, const int *sv_table, const int loop_nr, - const float mval[2], const bool use_btree_disp, const bool use_calc_direction) + const float mval[2], const bool use_occlude_geometry, const bool use_calc_direction) { TransDataEdgeSlideVert *sv_array = sld->sv; BMEditMesh *em = BKE_editmesh_from_object(t->obedit); @@ -5902,7 +5916,7 @@ static void calcEdgeSlide_mval_range( View3D *v3d = NULL; RegionView3D *rv3d = NULL; float projectMat[4][4]; - BMBVHTree *btree; + BMBVHTree *bmbvh; /* only for use_calc_direction */ float (*loop_dir)[3] = NULL, *loop_maxdist = NULL; @@ -5926,11 +5940,11 @@ static void calcEdgeSlide_mval_range( ED_view3d_ob_project_mat_get(rv3d, t->obedit, projectMat); } - if (use_btree_disp) { - btree = BKE_bmbvh_new_from_editmesh(em, BMBVH_RESPECT_HIDDEN, NULL, false); + if (use_occlude_geometry) { + bmbvh = BKE_bmbvh_new_from_editmesh(em, BMBVH_RESPECT_HIDDEN, NULL, false); } else { - btree = NULL; + bmbvh = NULL; } /* find mouse vectors, the global one, and one per loop in case we have @@ -5965,7 +5979,7 @@ static void calcEdgeSlide_mval_range( continue; /* This test is only relevant if object is not wire-drawn! See [#32068]. */ - if (use_btree_disp && !BMBVH_EdgeVisible(btree, e_other, ar, v3d, t->obedit)) { + if (use_occlude_geometry && !BMBVH_EdgeVisible(bmbvh, e_other, ar, v3d, t->obedit)) { continue; } @@ -6046,8 +6060,8 @@ static void calcEdgeSlide_mval_range( sld->mval_end[0] = t->mval[0] + mval_end[0]; sld->mval_end[1] = t->mval[1] + mval_end[1]; - if (btree) { - BKE_bmbvh_free(btree); + if (bmbvh) { + BKE_bmbvh_free(bmbvh); } } @@ -6109,8 +6123,8 @@ static bool createEdgeSlideVerts_double_side(TransInfo *t, bool use_even, bool f int *sv_table; /* BMVert -> sv_array index */ EdgeSlideData *sld = MEM_callocN(sizeof(*sld), "sld"); float mval[2] = {(float)t->mval[0], (float)t->mval[1]}; - int numsel, i, j, loop_nr; - bool use_btree_disp = false; + int numsel, i, loop_nr; + bool use_occlude_geometry = false; View3D *v3d = NULL; RegionView3D *rv3d = NULL; @@ -6157,31 +6171,39 @@ static bool createEdgeSlideVerts_double_side(TransInfo *t, bool use_even, bool f sv_table = MEM_mallocN(sizeof(*sv_table) * bm->totvert, __func__); - j = 0; - BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) { - if (BM_elem_flag_test(v, BM_ELEM_SELECT)) { - BM_elem_flag_enable(v, BM_ELEM_TAG); - sv_table[i] = j; - j += 1; - } - else { - BM_elem_flag_disable(v, BM_ELEM_TAG); - sv_table[i] = -1; +#define INDEX_UNSET -1 +#define INDEX_INVALID -2 + + { + int j = 0; + BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) { + if (BM_elem_flag_test(v, BM_ELEM_SELECT)) { + BM_elem_flag_enable(v, BM_ELEM_TAG); + sv_table[i] = INDEX_UNSET; + j += 1; + } + else { + BM_elem_flag_disable(v, BM_ELEM_TAG); + sv_table[i] = INDEX_INVALID; + } + BM_elem_index_set(v, i); /* set_inline */ } - BM_elem_index_set(v, i); /* set_inline */ - } - bm->elem_index_dirty &= ~BM_VERT; + bm->elem_index_dirty &= ~BM_VERT; - if (!j) { - MEM_freeN(sld); - MEM_freeN(sv_table); - return false; + if (!j) { + MEM_freeN(sld); + MEM_freeN(sv_table); + return false; + } + sv_tot = j; } - sv_tot = j; sv_array = MEM_callocN(sizeof(TransDataEdgeSlideVert) * sv_tot, "sv_array"); loop_nr = 0; + STACK_DECLARE(sv_array); + STACK_INIT(sv_array, sv_tot); + while (1) { float vec_a[3], vec_b[3]; BMLoop *l_a, *l_b; @@ -6274,6 +6296,11 @@ static bool createEdgeSlideVerts_double_side(TransInfo *t, bool use_even, bool f l_a_prev = NULL; l_b_prev = NULL; +#define SV_FROM_VERT(v) ( \ + (sv_table[BM_elem_index_get(v)] == INDEX_UNSET) ? \ + ((void)(sv_table[BM_elem_index_get(v)] = STACK_SIZE(sv_array)), STACK_PUSH_RET_PTR(sv_array)) : \ + (&sv_array[sv_table[BM_elem_index_get(v)]])) + /*iterate over the loop*/ v_first = v; do { @@ -6285,8 +6312,8 @@ static bool createEdgeSlideVerts_double_side(TransInfo *t, bool use_even, bool f /* XXX, 'sv' will initialize multiple times, this is suspicious. see [#34024] */ BLI_assert(v != NULL); - BLI_assert(sv_table[BM_elem_index_get(v)] != -1); - sv = &sv_array[sv_table[BM_elem_index_get(v)]]; + BLI_assert(sv_table[BM_elem_index_get(v)] != INDEX_INVALID); + sv = SV_FROM_VERT(v); sv->v = v; copy_v3_v3(sv->v_co_orig, v->co); sv->loop_nr = loop_nr; @@ -6311,8 +6338,10 @@ static bool createEdgeSlideVerts_double_side(TransInfo *t, bool use_even, bool f if (!e) { BLI_assert(v != NULL); - BLI_assert(sv_table[BM_elem_index_get(v)] != -1); - sv = &sv_array[sv_table[BM_elem_index_get(v)]]; + + BLI_assert(sv_table[BM_elem_index_get(v)] != INDEX_INVALID); + sv = SV_FROM_VERT(v); + sv->v = v; copy_v3_v3(sv->v_co_orig, v->co); sv->loop_nr = loop_nr; @@ -6401,6 +6430,10 @@ static bool createEdgeSlideVerts_double_side(TransInfo *t, bool use_even, bool f BM_elem_flag_disable(v_prev, BM_ELEM_TAG); } while ((e != v_first->e) && (l_a || l_b)); +#undef SV_FROM_VERT +#undef INDEX_UNSET +#undef INDEX_INVALID + loop_nr++; #undef EDGESLIDE_VERT_IS_INNER @@ -6408,6 +6441,8 @@ static bool createEdgeSlideVerts_double_side(TransInfo *t, bool use_even, bool f /* EDBM_flag_disable_all(em, BM_ELEM_SELECT); */ + BLI_assert(STACK_SIZE(sv_array) == sv_tot); + sld->sv = sv_array; sld->totsv = sv_tot; @@ -6415,10 +6450,10 @@ static bool createEdgeSlideVerts_double_side(TransInfo *t, bool use_even, bool f if (t->spacetype == SPACE_VIEW3D) { v3d = t->sa ? t->sa->spacedata.first : NULL; rv3d = t->ar ? t->ar->regiondata : NULL; - use_btree_disp = (v3d && t->obedit->dt > OB_WIRE && v3d->drawtype > OB_WIRE); + use_occlude_geometry = (v3d && t->obedit->dt > OB_WIRE && v3d->drawtype > OB_WIRE); } - calcEdgeSlide_mval_range(t, sld, sv_table, loop_nr, mval, use_btree_disp, true); + calcEdgeSlide_mval_range(t, sld, sv_table, loop_nr, mval, use_occlude_geometry, true); /* create copies of faces for customdata projection */ bmesh_edit_begin(bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES); @@ -6456,7 +6491,7 @@ static bool createEdgeSlideVerts_single_side(TransInfo *t, bool use_even, bool f EdgeSlideData *sld = MEM_callocN(sizeof(*sld), "sld"); float mval[2] = {(float)t->mval[0], (float)t->mval[1]}; int loop_nr; - bool use_btree_disp = false; + bool use_occlude_geometry = false; View3D *v3d = NULL; RegionView3D *rv3d = NULL; @@ -6503,6 +6538,7 @@ static bool createEdgeSlideVerts_single_side(TransInfo *t, bool use_even, bool f bm->elem_index_dirty &= ~BM_VERT; if (!j) { + MEM_freeN(sld); return false; } @@ -6617,10 +6653,10 @@ static bool createEdgeSlideVerts_single_side(TransInfo *t, bool use_even, bool f if (t->spacetype == SPACE_VIEW3D) { v3d = t->sa ? t->sa->spacedata.first : NULL; rv3d = t->ar ? t->ar->regiondata : NULL; - use_btree_disp = (v3d && t->obedit->dt > OB_WIRE && v3d->drawtype > OB_WIRE); + use_occlude_geometry = (v3d && t->obedit->dt > OB_WIRE && v3d->drawtype > OB_WIRE); } - calcEdgeSlide_mval_range(t, sld, sv_table, loop_nr, mval, use_btree_disp, false); + calcEdgeSlide_mval_range(t, sld, sv_table, loop_nr, mval, use_occlude_geometry, false); /* create copies of faces for customdata projection */ bmesh_edit_begin(bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES); diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index da448faa16b..22d6e7af7fe 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -87,7 +87,6 @@ typedef struct TransSnap { float snapPoint[3]; /* snapping from this point */ float snapTarget[3]; /* to this point */ float snapNormal[3]; - float snapTangent[3]; char snapNodeBorder; ListBase points; TransSnapPoint *selectedPoint; diff --git a/source/blender/editors/transform/transform_conversions.c b/source/blender/editors/transform/transform_conversions.c index 0b17f7075bc..6271f80570a 100644 --- a/source/blender/editors/transform/transform_conversions.c +++ b/source/blender/editors/transform/transform_conversions.c @@ -699,7 +699,6 @@ int count_set_pose_transflags(int *out_mode, short around, Object *ob) bPoseChannel *pchan; Bone *bone; int mode = *out_mode; - int hastranslation = 0; int total = 0; for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { @@ -727,7 +726,7 @@ int count_set_pose_transflags(int *out_mode, short around, Object *ob) } } /* now count, and check if we have autoIK or have to switch from translate to rotate */ - hastranslation = 0; + bool has_translation = false, has_rotation = false; for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { bone = pchan->bone; @@ -738,20 +737,29 @@ int count_set_pose_transflags(int *out_mode, short around, Object *ob) if (has_targetless_ik(pchan) == NULL) { if (pchan->parent && (pchan->bone->flag & BONE_CONNECTED)) { if (pchan->bone->flag & BONE_HINGE_CHILD_TRANSFORM) - hastranslation = 1; + has_translation = true; } - else if ((pchan->protectflag & OB_LOCK_LOC) != OB_LOCK_LOC) - hastranslation = 1; + else { + if ((pchan->protectflag & OB_LOCK_LOC) != OB_LOCK_LOC) + has_translation = true; + } + if ((pchan->protectflag & OB_LOCK_ROT) != OB_LOCK_ROT) + has_rotation = true; } else - hastranslation = 1; + has_translation = true; } } } /* if there are no translatable bones, do rotation */ - if (mode == TFM_TRANSLATION && !hastranslation) { - *out_mode = TFM_ROTATION; + if (mode == TFM_TRANSLATION && !has_translation) { + if (has_rotation) { + *out_mode = TFM_ROTATION; + } + else { + *out_mode = TFM_RESIZE; + } } return total; @@ -3114,7 +3122,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); } } } @@ -7488,7 +7496,11 @@ static void createTransGPencil(bContext *C, TransInfo *t) 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; + } + if (is_prop_edit) { /* Proportional Editing... */ if (is_prop_edit_connected) { @@ -7536,14 +7548,27 @@ static void createTransGPencil(bContext *C, TransInfo *t) if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { bGPDframe *gpf = gpl->actframe; bGPDstroke *gps; + float diff_mat[4][4]; + float inverse_diff_mat[4][4]; + + /* calculate difference matrix if parent object */ + if (gpl->parent != NULL) { + ED_gpencil_parent_location(gpl, diff_mat); + /* undo matrix */ + invert_m4_m4(inverse_diff_mat, diff_mat); + } - /* Make a new frame to work on if the layer's frame and the current scene frame don't match up + /* Make a new frame to work on if the layer's frame and the current scene frame don't match up * - This is useful when animating as it saves that "uh-oh" moment when you realize you've * spent too much time editing the wrong frame... */ // 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; + } } /* Loop over strokes, adding TransData for points as needed... */ @@ -7556,7 +7581,10 @@ static void createTransGPencil(bContext *C, TransInfo *t) 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; + } /* What we need to include depends on proportional editing settings... */ if (is_prop_edit) { if (is_prop_edit_connected) { @@ -7625,9 +7653,18 @@ static void createTransGPencil(bContext *C, TransInfo *t) /* screenspace */ td->protectflag = OB_LOCK_LOCZ | OB_LOCK_ROTZ | OB_LOCK_SCALEZ; - copy_m3_m4(td->smtx, t->persmat); - copy_m3_m4(td->mtx, t->persinv); - unit_m3(td->axismtx); + /* apply parent transformations */ + if (gpl->parent == NULL) { + copy_m3_m4(td->smtx, t->persmat); + copy_m3_m4(td->mtx, t->persinv); + unit_m3(td->axismtx); + } + else { + /* apply matrix transformation relative to parent */ + copy_m3_m4(td->smtx, inverse_diff_mat); /* final position */ + copy_m3_m4(td->mtx, diff_mat); /* display position */ + copy_m3_m4(td->axismtx, diff_mat); /* axis orientation */ + } } else { /* configure 2D dataspace points so that they don't play up... */ @@ -7636,9 +7673,18 @@ static void createTransGPencil(bContext *C, TransInfo *t) // XXX: matrices may need to be different? } - copy_m3_m3(td->smtx, smtx); - copy_m3_m3(td->mtx, mtx); - unit_m3(td->axismtx); // XXX? + /* apply parent transformations */ + if (gpl->parent == NULL) { + copy_m3_m3(td->smtx, smtx); + copy_m3_m3(td->mtx, mtx); + unit_m3(td->axismtx); // XXX? + } + else { + /* apply matrix transformation relative to parent */ + copy_m3_m4(td->smtx, inverse_diff_mat); /* final position */ + copy_m3_m4(td->mtx, diff_mat); /* display position */ + copy_m3_m4(td->axismtx, diff_mat); /* axis orientation */ + } } /* Triangulation must be calculated again, so save the stroke for recalc function */ td->extra = gps; diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c index 96bdad9b3ae..9e9372c72ea 100644 --- a/source/blender/editors/transform/transform_generics.c +++ b/source/blender/editors/transform/transform_generics.c @@ -966,7 +966,9 @@ static void recalcData_gpencil_strokes(TransInfo *t) TransData *td = t->data; for (int i = 0; i < t->total; i++, td++) { bGPDstroke *gps = td->extra; - gps->flag |= GP_STROKE_RECALC_CACHES; + if (gps != NULL) { + gps->flag |= GP_STROKE_RECALC_CACHES; + } } } diff --git a/source/blender/editors/transform/transform_manipulator.c b/source/blender/editors/transform/transform_manipulator.c index efee0fb8c7c..82bdc6d3924 100644 --- a/source/blender/editors/transform/transform_manipulator.c +++ b/source/blender/editors/transform/transform_manipulator.c @@ -57,6 +57,7 @@ #include "BKE_global.h" #include "BKE_editmesh.h" #include "BKE_lattice.h" +#include "BKE_gpencil.h" #include "BIF_gl.h" @@ -66,6 +67,7 @@ #include "ED_armature.h" #include "ED_curve.h" #include "ED_view3d.h" +#include "ED_gpencil.h" #include "UI_resources.h" @@ -286,24 +288,49 @@ static int calc_manipulator_stats(const bContext *C) zero_v3(scene->twcent); if (is_gp_edit) { - CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) - { - /* we're only interested in selected points here... */ - if (gps->flag & GP_STROKE_SELECT) { - bGPDspoint *pt; - int i; - - /* Change selection status of all points, then make the stroke match */ - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - if (pt->flag & GP_SPOINT_SELECT) { - calc_tw_center(scene, &pt->x); - totsel++; + float diff_mat[4][4]; + float fpt[3]; + + 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)) { + + /* calculate difference matrix if parent object */ + if (gpl->parent != NULL) { + ED_gpencil_parent_location(gpl, diff_mat); + } + + for (bGPDstroke *gps = gpl->actframe->strokes.first; gps; gps = gps->next) { + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) { + continue; + } + + /* we're only interested in selected points here... */ + if (gps->flag & GP_STROKE_SELECT) { + bGPDspoint *pt; + int i; + + /* Change selection status of all points, then make the stroke match */ + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + if (pt->flag & GP_SPOINT_SELECT) { + if (gpl->parent == NULL) { + calc_tw_center(scene, &pt->x); + totsel++; + } + else { + mul_v3_m4v3(fpt, diff_mat, &pt->x); + calc_tw_center(scene, fpt); + totsel++; + } + } + } } } } } - CTX_DATA_END; - + + /* selection center */ if (totsel) { mul_v3_fl(scene->twcent, 1.0f / (float)totsel); /* centroid! */ diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c index 3caa69cb839..121a23a7027 100644 --- a/source/blender/editors/transform/transform_snap.c +++ b/source/blender/editors/transform/transform_snap.c @@ -976,15 +976,6 @@ static void CalcSnapGeometry(TransInfo *t, float *UNUSED(vec)) } if (found == true) { - float tangent[3]; - - sub_v2_v2v2(tangent, loc, t->tsnap.snapPoint); - tangent[2] = 0.0f; - - if (!is_zero_v3(tangent)) { - copy_v3_v3(t->tsnap.snapTangent, tangent); - } - copy_v3_v3(t->tsnap.snapPoint, loc); copy_v3_v3(t->tsnap.snapNormal, no); 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/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..77beb1d97d9 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 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/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index 60701e96edb..725eefe5155 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -213,42 +213,49 @@ typedef struct PreviewImage { * Written to #BHead.code (for file IO) * and the first 2 bytes of #ID.name (for runtime checks, see #GS macro). */ -#define ID_SCE MAKE_ID2('S', 'C') /* Scene */ -#define ID_LI MAKE_ID2('L', 'I') /* Library */ -#define ID_OB MAKE_ID2('O', 'B') /* Object */ -#define ID_ME MAKE_ID2('M', 'E') /* Mesh */ -#define ID_CU MAKE_ID2('C', 'U') /* Curve */ -#define ID_MB MAKE_ID2('M', 'B') /* MetaBall */ -#define ID_MA MAKE_ID2('M', 'A') /* Material */ -#define ID_TE MAKE_ID2('T', 'E') /* Tex (Texture) */ -#define ID_IM MAKE_ID2('I', 'M') /* Image */ -#define ID_LT MAKE_ID2('L', 'T') /* Lattice */ -#define ID_LA MAKE_ID2('L', 'A') /* Lamp */ -#define ID_CA MAKE_ID2('C', 'A') /* Camera */ -#define ID_IP MAKE_ID2('I', 'P') /* Ipo (depreciated, replaced by FCurves) */ -#define ID_KE MAKE_ID2('K', 'E') /* Key (shape key) */ -#define ID_WO MAKE_ID2('W', 'O') /* World */ -#define ID_SCR MAKE_ID2('S', 'R') /* Screen */ -#define ID_SCRN MAKE_ID2('S', 'N') /* (depreciated?) */ -#define ID_VF MAKE_ID2('V', 'F') /* VFont (Vector Font) */ -#define ID_TXT MAKE_ID2('T', 'X') /* Text */ -#define ID_SPK MAKE_ID2('S', 'K') /* Speaker */ -#define ID_SO MAKE_ID2('S', 'O') /* Sound */ -#define ID_GR MAKE_ID2('G', 'R') /* Group */ -#define ID_ID MAKE_ID2('I', 'D') /* (internal use only) */ -#define ID_AR MAKE_ID2('A', 'R') /* bArmature */ -#define ID_AC MAKE_ID2('A', 'C') /* bAction */ -#define ID_NT MAKE_ID2('N', 'T') /* bNodeTree */ -#define ID_BR MAKE_ID2('B', 'R') /* Brush */ -#define ID_GD MAKE_ID2('G', 'D') /* bGPdata, (Grease Pencil) */ -#define ID_WM MAKE_ID2('W', 'M') /* WindowManager */ -#define ID_MC MAKE_ID2('M', 'C') /* MovieClip */ -#define ID_MSK MAKE_ID2('M', 'S') /* Mask */ -#define ID_LS MAKE_ID2('L', 'S') /* FreestyleLineStyle */ -#define ID_PAL MAKE_ID2('P', 'L') /* Palette */ -#define ID_PC MAKE_ID2('P', 'C') /* PaintCurve */ - - /* NOTE! Fake IDs, needed for g.sipo->blocktype or outliner */ +typedef enum ID_Type { + ID_SCE = MAKE_ID2('S', 'C'), /* Scene */ + ID_LI = MAKE_ID2('L', 'I'), /* Library */ + ID_OB = MAKE_ID2('O', 'B'), /* Object */ + ID_ME = MAKE_ID2('M', 'E'), /* Mesh */ + ID_CU = MAKE_ID2('C', 'U'), /* Curve */ + ID_MB = MAKE_ID2('M', 'B'), /* MetaBall */ + ID_MA = MAKE_ID2('M', 'A'), /* Material */ + ID_TE = MAKE_ID2('T', 'E'), /* Tex (Texture) */ + ID_IM = MAKE_ID2('I', 'M'), /* Image */ + ID_LT = MAKE_ID2('L', 'T'), /* Lattice */ + ID_LA = MAKE_ID2('L', 'A'), /* Lamp */ + ID_CA = MAKE_ID2('C', 'A'), /* Camera */ + ID_IP = MAKE_ID2('I', 'P'), /* Ipo (depreciated, replaced by FCurves) */ + ID_KE = MAKE_ID2('K', 'E'), /* Key (shape key) */ + ID_WO = MAKE_ID2('W', 'O'), /* World */ + ID_SCR = MAKE_ID2('S', 'R'), /* Screen */ + ID_VF = MAKE_ID2('V', 'F'), /* VFont (Vector Font) */ + ID_TXT = MAKE_ID2('T', 'X'), /* Text */ + ID_SPK = MAKE_ID2('S', 'K'), /* Speaker */ + ID_SO = MAKE_ID2('S', 'O'), /* Sound */ + ID_GR = MAKE_ID2('G', 'R'), /* Group */ + ID_AR = MAKE_ID2('A', 'R'), /* bArmature */ + ID_AC = MAKE_ID2('A', 'C'), /* bAction */ + ID_NT = MAKE_ID2('N', 'T'), /* bNodeTree */ + ID_BR = MAKE_ID2('B', 'R'), /* Brush */ + ID_GD = MAKE_ID2('G', 'D'), /* bGPdata, (Grease Pencil) */ + ID_WM = MAKE_ID2('W', 'M'), /* WindowManager */ + ID_MC = MAKE_ID2('M', 'C'), /* MovieClip */ + ID_MSK = MAKE_ID2('M', 'S'), /* Mask */ + 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. */ +#define ID_ID MAKE_ID2('I', 'D') /* (internal use only) */ + +/* Deprecated. */ +#define ID_SCRN MAKE_ID2('S', 'N') + +/* NOTE! Fake IDs, needed for g.sipo->blocktype or outliner */ #define ID_SEQ MAKE_ID2('S', 'Q') /* constraint */ #define ID_CO MAKE_ID2('C', 'O') @@ -369,6 +376,7 @@ enum { FILTER_ID_TXT = (1 << 24), FILTER_ID_VF = (1 << 25), FILTER_ID_WO = (1 << 26), + FILTER_ID_CF = (1 << 27), }; #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_anim_types.h b/source/blender/makesdna/DNA_anim_types.h index 6bd7b3a4999..31fe8fe563e 100644 --- a/source/blender/makesdna/DNA_anim_types.h +++ b/source/blender/makesdna/DNA_anim_types.h @@ -450,7 +450,9 @@ typedef enum eDriver_Flags { /* the names are cached so they don't need have python unicode versions created each time */ DRIVER_FLAG_RENAMEVAR = (1<<4), /* intermediate values of driver should be shown in the UI for debugging purposes */ - DRIVER_FLAG_SHOWDEBUG = (1<<5) + DRIVER_FLAG_SHOWDEBUG = (1<<5), + /* include 'self' in the drivers namespace. */ + DRIVER_FLAG_USE_SELF = (1<<6), } eDriver_Flags; /* F-Curves -------------------------------------- */ diff --git a/source/blender/makesdna/DNA_cachefile_types.h b/source/blender/makesdna/DNA_cachefile_types.h new file mode 100644 index 00000000000..1cda0233aa8 --- /dev/null +++ b/source/blender/makesdna/DNA_cachefile_types.h @@ -0,0 +1,84 @@ +/* + * ***** 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; + + /* 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_cloth_types.h b/source/blender/makesdna/DNA_cloth_types.h index d385e303a7c..ee147da8dae 100644 --- a/source/blender/makesdna/DNA_cloth_types.h +++ b/source/blender/makesdna/DNA_cloth_types.h @@ -63,6 +63,7 @@ typedef struct ClothSimSettings { float max_sewing; /* max sewing force */ float avg_spring_len; /* used for normalized springs */ float timescale; /* parameter how fast cloth runs */ + float time_scale; /* multiplies cloth speed */ float maxgoal; /* see SB */ float eff_force_scale;/* Scaling of effector forces (see softbody_calc_forces).*/ float eff_wind_scale; /* Scaling of effector wind (see softbody_calc_forces). */ @@ -98,6 +99,7 @@ typedef struct ClothSimSettings { short presets; /* used for presets on GUI */ short reset; + char pad0[4]; struct EffectorWeights *effector_weights; } ClothSimSettings; diff --git a/source/blender/makesdna/DNA_color_types.h b/source/blender/makesdna/DNA_color_types.h index 1d88b01cf62..f7ee1ff3915 100644 --- a/source/blender/makesdna/DNA_color_types.h +++ b/source/blender/makesdna/DNA_color_types.h @@ -49,7 +49,8 @@ typedef struct CurveMapPoint { /* curvepoint->flag */ enum { CUMA_SELECT = 1, - CUMA_VECTOR = 2 + CUMA_HANDLE_VECTOR = 2, + CUMA_HANDLE_AUTO_ANIM = 4, }; typedef struct CurveMap { 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_gpencil_types.h b/source/blender/makesdna/DNA_gpencil_types.h index 41f53f9f51c..f1546053c5c 100644 --- a/source/blender/makesdna/DNA_gpencil_types.h +++ b/source/blender/makesdna/DNA_gpencil_types.h @@ -18,7 +18,7 @@ * The Original Code is Copyright (C) 2008, Blender Foundation. * This is a new part of Blender * - * Contributor(s): Joshua Leung + * Contributor(s): Joshua Leung, Antonio Vazquez * * ***** END GPL LICENSE BLOCK ***** */ @@ -32,9 +32,10 @@ #include "DNA_listBase.h" #include "DNA_ID.h" +#include "DNA_brush_types.h" struct AnimData; - +struct CurveMapping; /* Grease-Pencil Annotations - 'Stroke Point' * -> Coordinates may either be 2d or 3d depending on settings at the time @@ -44,6 +45,7 @@ struct AnimData; typedef struct bGPDspoint { float x, y, z; /* co-ordinates of point (usually 2d, but can be 3d as well) */ float pressure; /* pressure of input device (from 0 to 1) at this point */ + float strength; /* color strength (used for alpha factor) */ float time; /* seconds since start of stroke */ int flag; /* additional options (NOTE: can shrink this field down later if needed) */ } bGPDspoint; @@ -65,24 +67,113 @@ typedef struct bGPDtriangle { int v1, v2, v3; /* indices for tesselated triangle used for GP Fill */ } bGPDtriangle; +/* GP brush (used for new strokes) */ +typedef struct bGPDbrush { + struct bGPDbrush *next, *prev; + + char info[64]; /* Brush name. Must be unique. */ + short thickness; /* thickness to apply to strokes */ + short flag; + float draw_smoothfac; /* amount of smoothing to apply to newly created strokes */ + short draw_smoothlvl; /* number of times to apply smooth factor to new strokes */ + short sublevel; /* number of times to subdivide new strokes */ + + float draw_sensitivity; /* amount of sensivity to apply to newly created strokes */ + float draw_strength; /* amount of alpha strength to apply to newly created strokes */ + float draw_jitter; /* amount of jitter to apply to newly created strokes */ + float draw_angle; /* angle when the brush has full thickness */ + float draw_angle_factor; /* factor to apply when angle change (only 90 degrees) */ + float draw_random_press; /* factor of randomness for sensitivity and strength */ + float draw_random_sub; /* factor of randomness for subdivision */ + struct CurveMapping *cur_sensitivity; + struct CurveMapping *cur_strength; + struct CurveMapping *cur_jitter; +} bGPDbrush; + +/* bGPDbrush->flag */ +typedef enum eGPDbrush_Flag { + /* brush is active */ + GP_BRUSH_ACTIVE = (1 << 0), + /* brush use pressure */ + GP_BRUSH_USE_PRESSURE = (1 << 1), + /* brush use pressure for alpha factor */ + GP_BRUSH_USE_STENGTH_PRESSURE = (1 << 2), + /* brush use pressure for alpha factor */ + GP_BRUSH_USE_JITTER_PRESSURE = (1 << 3), + /* brush use random for pressure */ + GP_BRUSH_USE_RANDOM_PRESSURE = (1 << 4), + /* brush use random for strength */ + GP_BRUSH_USE_RANDOM_STRENGTH = (1 << 5) +} eGPDbrush_Flag; + +/* color of palettes */ +typedef struct bGPDpalettecolor { + struct bGPDpalettecolor *next, *prev; + char info[64]; /* Color name. Must be unique. */ + float color[4]; + float fill[4]; /* color that should be used for drawing "fills" for strokes */ + short flag; /* settings for palette color */ + char pad[6]; /* padding for compiler alignment error */ +} bGPDpalettecolor; + +/* bGPDpalettecolor->flag */ +typedef enum eGPDpalettecolor_Flag { + /* color is active */ + PC_COLOR_ACTIVE = (1 << 0), + /* don't display color */ + PC_COLOR_HIDE = (1 << 1), + /* protected from further editing */ + PC_COLOR_LOCKED = (1 << 2), + /* do onion skinning */ + PC_COLOR_ONIONSKIN = (1 << 3), + /* "volumetric" strokes (i.e. GLU Quadric discs in 3D) */ + PC_COLOR_VOLUMETRIC = (1 << 4), + /* Use High quality fill */ + PC_COLOR_HQ_FILL = (1 << 5) +} eGPDpalettecolor_Flag; + +/* palette of colors */ +typedef struct bGPDpalette { + struct bGPDpalette *next, *prev; + + /* pointer to individual colours */ + ListBase colors; + char info[64]; /* Palette name. Must be unique. */ + + short flag; + char pad[6]; /* padding for compiler alignment error */ +} bGPDpalette; + +/* bGPDpalette->flag */ +typedef enum eGPDpalette_Flag { + /* palette is active */ + PL_PALETTE_ACTIVE = (1 << 0) +} eGPDpalette_Flag; + /* Grease-Pencil Annotations - 'Stroke' * -> A stroke represents a (simplified version) of the curve * drawn by the user in one 'mousedown'->'mouseup' operation */ typedef struct bGPDstroke { struct bGPDstroke *next, *prev; + bGPDspoint *points; /* array of data-points for stroke */ - void *pad; /* keep 4 pointers at the beginning, padding for 'inittime' is tricky 64/32bit */ - int totpoints; /* number of data-points in array */ - - short thickness; /* thickness of stroke (currently not used) */ - short flag; /* various settings about this stroke */ - bGPDtriangle *triangles;/* tesselated triangles for GP Fill */ + int totpoints; /* number of data-points in array */ int tot_triangles; /* number of triangles in array */ - int pad1, *pad2; + + short thickness; /* thickness of stroke */ + short flag, pad[2]; /* various settings about this stroke */ double inittime; /* Init time of stroke */ + /* The pointer to color is only used during drawing, but not saved + * colorname is the join with the palette, but when draw, the pointer is update if the value is NULL + * to speed up the drawing + */ + char colorname[128]; /* color name */ + bGPDpalettecolor *palcolor; /* current palette color */ + /* temporary layer name only used during copy/paste to put the stroke in the original layer */ + char tmp_layerinfo[128]; } bGPDstroke; /* bGPDstroke->flag */ @@ -97,6 +188,10 @@ typedef enum eGPDstroke_Flag { GP_STROKE_SELECT = (1 << 3), /* Recalculate triangulation for high quality fill (when true, force a new recalc) */ GP_STROKE_RECALC_CACHES = (1 << 4), + /* Recalculate the color pointer using the name as index (true force a new recalc) */ + GP_STROKE_RECALC_COLOR = (1 << 5), + /* Flag used to indicate that stroke is closed and draw edge between last and first point */ + GP_STROKE_CYCLIC = (1 << 7), /* only for use with stroke-buffer (while drawing eraser) */ GP_STROKE_ERASER = (1 << 15) } eGPDstroke_Flag; @@ -139,16 +234,18 @@ typedef struct bGPDlayer { float gcolor_prev[3]; /* optional color for ghosts before the active frame */ float gcolor_next[3]; /* optional color for ghosts after the active frame */ - float color[4]; /* color that should be used to draw all the strokes in this layer */ - float fill[4]; /* color that should be used for drawing "fills" for strokes */ + float color[4]; /* Color for strokes in layers (replaced by palettecolor). Only used for ruler (which uses GPencil internally) */ + float fill[4]; /* Fill color for strokes in layers. Not used and replaced by palettecolor fill */ char info[128]; /* optional reference info about this layer (i.e. "director's comments, 12/3") * this is used for the name of the layer too and kept unique. */ - float draw_smoothfac; /* amount of smoothing to apply to newly created strokes */ - short draw_smoothlvl; /* number of times to apply smooth factor to new strokes */ - short sublevel; /* number of times to subdivide new strokes */ - short pad[4]; /* padding for compiler error */ + struct Object *parent; /* parent object */ + float inverse[4][4]; /* inverse matrix (only used if parented) */ + char parsubstr[64]; /* String describing subobject info, MAX_ID_NAME-2 */ + short partype, pad; + float tintcolor[4]; /* Color used to tint layer, alpha value is used as factor */ + float opacity; /* Opacity of the layer */ } bGPDlayer; /* bGPDlayer->flag */ @@ -176,7 +273,9 @@ typedef enum eGPDlayer_Flag { /* "volumetric" strokes (i.e. GLU Quadric discs in 3D) */ GP_LAYER_VOLUMETRIC = (1 << 10), /* Use high quality fill (instead of buggy legacy OpenGL Fill) */ - GP_LAYER_HQ_FILL = (1 << 11) + GP_LAYER_HQ_FILL = (1 << 11), + /* Unlock color */ + GP_LAYER_UNLOCK_COLOR = (1 << 12) } eGPDlayer_Flag; /* Grease-Pencil Annotations - 'DataBlock' */ @@ -187,7 +286,7 @@ typedef struct bGPdata { /* saved Grease-Pencil data */ ListBase layers; /* bGPDlayers */ int flag; /* settings for this datablock */ - + /* not-saved stroke buffer data (only used during paint-session) * - buffer must be initialized before use, but freed after * whole paint operation is over @@ -195,6 +294,13 @@ typedef struct bGPdata { short sbuffer_size; /* number of elements currently in cache */ short sbuffer_sflag; /* flags for stroke that cache represents */ void *sbuffer; /* stroke buffer (can hold GP_STROKE_BUFFER_MAX) */ + float scolor[4]; /* buffer color using palettes */ + char pad[6]; /* padding for compiler alignment error */ + short sflag; /* settings for palette color */ + + /* saved paletes and brushes */ + ListBase palettes; + //ListBase brushes; } bGPdata; /* bGPdata->flag */ @@ -229,7 +335,9 @@ typedef enum eGPdata_Flag { GP_DATA_STROKE_EDITMODE = (1 << 8), /* Convenience/cache flag to make it easier to quickly toggle onion skinning on/off */ - GP_DATA_SHOW_ONIONSKINS = (1 << 9) + GP_DATA_SHOW_ONIONSKINS = (1 << 9), + /* Draw a green and red point to indicate start and end of the stroke */ + GP_DATA_SHOW_DIRECTION = (1 << 10) } eGPdata_Flag; #endif /* __DNA_GPENCIL_TYPES_H__ */ diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h index a6bbb40d826..a02948ddc92 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; @@ -1503,4 +1504,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_force.h b/source/blender/makesdna/DNA_object_force.h index 94bf7e456cd..71988d10ecf 100644 --- a/source/blender/makesdna/DNA_object_force.h +++ b/source/blender/makesdna/DNA_object_force.h @@ -255,6 +255,8 @@ typedef struct SoftBody { float shearstiff; float inpush; + struct Group *collision_group; + struct EffectorWeights *effector_weights; /* reverse esimated obmatrix .. no need to store in blend file .. how ever who cares */ float lcom[3]; diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index 804c58e445c..66b79282533 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -62,6 +62,7 @@ struct AnimData; struct Editing; struct SceneStats; struct bGPdata; +struct bGPDbrush; struct MovieClip; struct ColorSpace; @@ -1084,12 +1085,12 @@ typedef enum eGP_EditBrush_Types { GP_EDITBRUSH_TYPE_SUBDIVIDE = 7, GP_EDITBRUSH_TYPE_SIMPLIFY = 8, GP_EDITBRUSH_TYPE_CLONE = 9, - + GP_EDITBRUSH_TYPE_STRENGTH = 10, + /* !!! Update GP_EditBrush_Data brush[###]; below !!! */ TOT_GP_EDITBRUSH_TYPES } eGP_EditBrush_Types; - /* Settings for a GPencil Stroke Sculpting Brush */ typedef struct GP_EditBrush_Data { short size; /* radius of brush */ @@ -1115,17 +1116,26 @@ typedef enum eGP_EditBrush_Flag { /* GPencil Stroke Sculpting Settings */ typedef struct GP_BrushEdit_Settings { - GP_EditBrush_Data brush[10]; /* TOT_GP_EDITBRUSH_TYPES */ + GP_EditBrush_Data brush[11]; /* TOT_GP_EDITBRUSH_TYPES */ void *paintcursor; /* runtime */ int brushtype; /* eGP_EditBrush_Types */ int flag; /* eGP_BrushEdit_SettingsFlag */ + char pad[4]; + float alpha; /* alpha factor for selection color */ } GP_BrushEdit_Settings; /* GP_BrushEdit_Settings.flag */ typedef enum eGP_BrushEdit_SettingsFlag { /* only affect selected points */ - GP_BRUSHEDIT_FLAG_SELECT_MASK = (1 << 0) + GP_BRUSHEDIT_FLAG_SELECT_MASK = (1 << 0), + /* apply brush to position */ + GP_BRUSHEDIT_FLAG_APPLY_POSITION = (1 << 1), + /* apply brush to strength */ + GP_BRUSHEDIT_FLAG_APPLY_STRENGTH = (1 << 2), + /* apply brush to thickness */ + GP_BRUSHEDIT_FLAG_APPLY_THICKNESS = (1 << 3) + } eGP_BrushEdit_SettingsFlag; /* *************************************************************** */ @@ -1345,6 +1355,9 @@ typedef struct ToolSettings { /* Grease Pencil Sculpt */ struct GP_BrushEdit_Settings gp_sculpt; + /* Grease Pencil Drawing Brushes (bGPDbrush) */ + ListBase gp_brushes; + /* Image Paint (8 byttse aligned please!) */ struct ImagePaintSettings imapaint; diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index 4c5cb5a473a..12c71be3830 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/intern/makesdna.c b/source/blender/makesdna/intern/makesdna.c index cb2ae0340c8..0f7ed8c0bc0 100644 --- a/source/blender/makesdna/intern/makesdna.c +++ b/source/blender/makesdna/intern/makesdna.c @@ -127,6 +127,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 */ @@ -1336,4 +1337,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 f623416f673..64cacfa3dea 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -86,6 +86,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; @@ -247,6 +249,9 @@ extern StructRNA RNA_FreestyleSettings; extern StructRNA RNA_Function; extern StructRNA RNA_GPencilFrame; extern StructRNA RNA_GPencilLayer; +extern StructRNA RNA_GPencilPalette; +extern StructRNA RNA_GPencilPaletteColor; +extern StructRNA RNA_GPencilBrush; extern StructRNA RNA_GPencilStroke; extern StructRNA RNA_GPencilStrokePoint; extern StructRNA RNA_GPencilSculptSettings; @@ -691,6 +696,11 @@ void RNA_main_pointer_create(struct Main *main, PointerRNA *r_ptr); void RNA_id_pointer_create(struct ID *id, PointerRNA *r_ptr); void RNA_pointer_create(struct ID *id, StructRNA *type, void *data, PointerRNA *r_ptr); +bool RNA_path_resolved_create( + PointerRNA *ptr, struct PropertyRNA *prop, + const int prop_index, + PathResolvedRNA *r_anim_rna); + void RNA_blender_rna_pointer_create(PointerRNA *r_ptr); void RNA_pointer_recast(PointerRNA *ptr, PointerRNA *r_ptr); diff --git a/source/blender/makesrna/RNA_enum_types.h b/source/blender/makesrna/RNA_enum_types.h index cd709a42b38..27da7392cbd 100644 --- a/source/blender/makesrna/RNA_enum_types.h +++ b/source/blender/makesrna/RNA_enum_types.h @@ -197,6 +197,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/RNA_types.h b/source/blender/makesrna/RNA_types.h index 1d5f46a1814..276531992f9 100644 --- a/source/blender/makesrna/RNA_types.h +++ b/source/blender/makesrna/RNA_types.h @@ -64,6 +64,16 @@ typedef struct PropertyPointerRNA { struct PropertyRNA *prop; } PropertyPointerRNA; +/** + * Stored result of a RNA path lookup (as used by anim-system) + */ +typedef struct PathResolvedRNA { + struct PointerRNA ptr; + struct PropertyRNA *prop; + /* -1 for non-array access */ + int prop_index; +} PathResolvedRNA; + /* Property */ typedef enum PropertyType { diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt index ef159cf5930..c67780204e1 100644 --- a/source/blender/makesrna/intern/CMakeLists.txt +++ b/source/blender/makesrna/intern/CMakeLists.txt @@ -37,6 +37,7 @@ set(DEFSRC rna_animviz.c rna_armature.c rna_brush.c + rna_cachefile.c rna_camera.c rna_cloth.c rna_color.c @@ -288,6 +289,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 diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c index dde4de759bc..94290828e46 100644 --- a/source/blender/makesrna/intern/makesrna.c +++ b/source/blender/makesrna/intern/makesrna.c @@ -3300,6 +3300,7 @@ static RNAProcessItem PROCESS_ITEMS[] = { {"rna_actuator.c", "rna_actuator_api.c", RNA_def_actuator}, {"rna_armature.c", "rna_armature_api.c", RNA_def_armature}, {"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 44ed05e5880..265eab32df8 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", ""}, @@ -138,6 +139,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; @@ -177,6 +179,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; diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index 5a93e18a7dd..047e5ea17ab 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -6966,3 +6966,22 @@ bool RNA_struct_equals(PointerRNA *a, PointerRNA *b, eRNAEqualsMode mode) return equals; } + +bool RNA_path_resolved_create( + PointerRNA *ptr, struct PropertyRNA *prop, + const int prop_index, + PathResolvedRNA *r_anim_rna) +{ + int array_len = RNA_property_array_length(ptr, prop); + + if ((array_len == 0) || (prop_index < array_len)) { + r_anim_rna->ptr = *ptr; + r_anim_rna->prop = prop; + r_anim_rna->prop_index = array_len ? prop_index : -1; + + return true; + } + else { + return false; + } +} 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 c91b6487653..781e44c9ed6 100644 --- a/source/blender/makesrna/intern/rna_cloth.c +++ b/source/blender/makesrna/intern/rna_cloth.c @@ -436,11 +436,19 @@ static void rna_def_cloth_sim_settings(BlenderRNA *brna) prop = RNA_def_property(srna, "quality", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "stepsPerFrame"); - RNA_def_property_range(prop, 1, 80); + RNA_def_property_range(prop, 1, INT_MAX); + RNA_def_property_ui_range(prop, 1, 80, 1, -1); RNA_def_property_ui_text(prop, "Quality", "Quality of the simulation in steps per frame (higher is better quality but slower)"); RNA_def_property_update(prop, 0, "rna_cloth_update"); + prop = RNA_def_property(srna, "time_scale", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "time_scale"); + RNA_def_property_range(prop, 0.0f, FLT_MAX); + RNA_def_property_ui_range(prop, 0.0f, 10.0f, 10, 3); + RNA_def_property_ui_text(prop, "Speed", "Cloth speed is multiplied by this value"); + RNA_def_property_update(prop, 0, "rna_cloth_update"); + prop = RNA_def_property(srna, "vertex_group_shrink", PROP_STRING, PROP_NONE); RNA_def_property_string_funcs(prop, "rna_ClothSettings_shrink_vgroup_get", "rna_ClothSettings_shrink_vgroup_length", "rna_ClothSettings_shrink_vgroup_set"); @@ -557,6 +565,12 @@ static void rna_def_cloth_sim_settings(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Rest Shape Key", "Shape key to use the rest spring lengths from"); RNA_def_property_update(prop, 0, "rna_cloth_update"); + prop = RNA_def_property(srna, "use_dynamic_mesh", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flags", CLOTH_SIMSETTINGS_FLAG_DYNAMIC_BASEMESH); + RNA_def_property_ui_text(prop, "Dynamic Base Mesh", "Make simulation respect deformations in the base mesh"); + RNA_def_property_update(prop, 0, "rna_cloth_update"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + /* unused */ /* unused still */ @@ -656,7 +670,8 @@ static void rna_def_cloth_collision_settings(BlenderRNA *brna) prop = RNA_def_property(srna, "collision_quality", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "loop_count"); - RNA_def_property_range(prop, 1, 20); + RNA_def_property_range(prop, 1, SHRT_MAX); + RNA_def_property_ui_range(prop, 1, 20, 1, -1); RNA_def_property_ui_text(prop, "Collision Quality", "How many collision iterations should be done. (higher is better quality but slower)"); RNA_def_property_update(prop, 0, "rna_cloth_update"); @@ -681,7 +696,8 @@ static void rna_def_cloth_collision_settings(BlenderRNA *brna) prop = RNA_def_property(srna, "self_collision_quality", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "self_loop_count"); - RNA_def_property_range(prop, 1, 10); + RNA_def_property_range(prop, 1, SHRT_MAX); + RNA_def_property_ui_range(prop, 1, 10, 1, -1); RNA_def_property_ui_text(prop, "Self Collision Quality", "How many self collision iterations should be done " "(higher is better quality but slower)"); diff --git a/source/blender/makesrna/intern/rna_color.c b/source/blender/makesrna/intern/rna_color.c index 30c966fdc3a..41aad14394b 100644 --- a/source/blender/makesrna/intern/rna_color.c +++ b/source/blender/makesrna/intern/rna_color.c @@ -682,7 +682,8 @@ static void rna_def_curvemappoint(BlenderRNA *brna) PropertyRNA *prop; static EnumPropertyItem prop_handle_type_items[] = { {0, "AUTO", 0, "Auto Handle", ""}, - {CUMA_VECTOR, "VECTOR", 0, "Vector Handle", ""}, + {CUMA_HANDLE_AUTO_ANIM, "AUTO_CLAMPED", 0, "Auto Clamped Handle", ""}, + {CUMA_HANDLE_VECTOR, "VECTOR", 0, "Vector Handle", ""}, {0, NULL, 0, NULL, NULL} }; 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_fcurve.c b/source/blender/makesrna/intern/rna_fcurve.c index 5fb581eb74a..c521e93d33e 100644 --- a/source/blender/makesrna/intern/rna_fcurve.c +++ b/source/blender/makesrna/intern/rna_fcurve.c @@ -1600,7 +1600,13 @@ static void rna_def_channeldriver(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "flag", DRIVER_FLAG_SHOWDEBUG); RNA_def_property_ui_text(prop, "Show Debug Info", "Show intermediate values for the driver calculations to allow debugging of drivers"); - + + prop = RNA_def_property(srna, "use_self", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", DRIVER_FLAG_USE_SELF); + RNA_def_property_ui_text(prop, "Use Self", + "Include a 'self' variable in the name-space, " + "so drivers can easily reference the data being modified (object, bone, etc...)"); + /* State Info (for Debugging) */ prop = RNA_def_property(srna, "is_valid", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", DRIVER_FLAG_INVALID); diff --git a/source/blender/makesrna/intern/rna_gpencil.c b/source/blender/makesrna/intern/rna_gpencil.c index 52c04bec743..80f4d5d05b6 100644 --- a/source/blender/makesrna/intern/rna_gpencil.c +++ b/source/blender/makesrna/intern/rna_gpencil.c @@ -15,7 +15,7 @@ * 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): Blender Foundation (2009), Joshua Leung + * Contributor(s): Blender Foundation (2009), Joshua Leung, Antonio Vazquez * * ***** END GPL LICENSE BLOCK ***** */ @@ -41,6 +41,17 @@ #include "rna_internal.h" #include "WM_types.h" +#include "DNA_object_types.h" +#include "ED_gpencil.h" + +/* parent type */ +static EnumPropertyItem parent_type_items[] = { + {PAROBJECT, "OBJECT", 0, "Object", "The layer is parented to an object"}, + {PARSKEL, "ARMATURE", 0, "Armature", ""}, + {PARBONE, "BONE", 0, "Bone", "The layer is parented to a bone"}, + {0, NULL, 0, NULL, NULL} +}; + #ifdef RNA_RUNTIME @@ -49,8 +60,7 @@ #include "WM_api.h" #include "BKE_gpencil.h" - -#include "DNA_object_types.h" +#include "BKE_action.h" static void rna_GPencil_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *UNUSED(ptr)) @@ -90,6 +100,16 @@ static void rna_GPencil_onion_skinning_update(Main *bmain, Scene *scene, Pointer rna_GPencil_update(bmain, scene, ptr); } +static void rna_GPencil_stroke_colorname_update(Main *bmain, Scene *scene, PointerRNA *ptr) +{ + bGPDstroke *gps = (bGPDstroke *)ptr->data; + gps->flag |= GP_STROKE_RECALC_COLOR; + gps->palcolor = NULL; + + /* Now do standard updates... */ + rna_GPencil_update(bmain, scene, ptr); +} + static char *rna_GPencilLayer_path(PointerRNA *ptr) { bGPDlayer *gpl = (bGPDlayer *)ptr->data; @@ -123,38 +143,140 @@ static void rna_GPencilLayer_line_width_range(PointerRNA *ptr, int *min, int *ma * it's relatively hard to test for that. So, for now, only volumetric strokes * get to be larger... */ + + /* From GP v2 this value is used to increase or decrease the thickness of the stroke */ if (gpl->flag & GP_LAYER_VOLUMETRIC) { - *min = 1; + *min = -300; *max = 300; - *softmin = 1; + *softmin = -100; *softmax = 100; } else { - *min = 1; + *min = -10; *max = 10; - *softmin = 1; + *softmin = -10; *softmax = 10; } } -static int rna_GPencilLayer_is_stroke_visible_get(PointerRNA *ptr) +/* set parent */ +static void set_parent(bGPDlayer *gpl, Object *par, const int type, const char *substr) +{ + if (type == PAROBJECT) { + invert_m4_m4(gpl->inverse, par->obmat); + gpl->parent = par; + gpl->partype |= PAROBJECT; + gpl->parsubstr[0] = 0; + } + else if (type == PARSKEL) { + invert_m4_m4(gpl->inverse, par->obmat); + gpl->parent = par; + gpl->partype |= PARSKEL; + gpl->parsubstr[0] = 0; + } + else if (type == PARBONE) { + bPoseChannel *pchan = BKE_pose_channel_find_name(par->pose, substr); + if (pchan) { + float tmp_mat[4][4]; + mul_m4_m4m4(tmp_mat, par->obmat, pchan->pose_mat); + + invert_m4_m4(gpl->inverse, tmp_mat); + gpl->parent = par; + gpl->partype |= PARBONE; + BLI_strncpy(gpl->parsubstr, substr, sizeof(gpl->parsubstr)); + } + } +} + +/* set parent object and inverse matrix */ +static void rna_GPencilLayer_parent_set(PointerRNA *ptr, PointerRNA value) { - /* see drawgpencil.c -> gp_draw_data_layers() for more details - * about this limit for showing/not showing - */ bGPDlayer *gpl = (bGPDlayer *)ptr->data; - return (gpl->color[3] > GPENCIL_ALPHA_OPACITY_THRESH); + Object *par = (Object *)value.data; + + if (par != NULL) { + set_parent(gpl, par, gpl->partype, gpl->parsubstr); + } + else { + /* keep strokes in the same place, so apply current transformation */ + if (gpl->parent != NULL) { + bGPDspoint *pt; + int i; + float diff_mat[4][4]; + /* calculate difference matrix */ + ED_gpencil_parent_location(gpl, diff_mat); + for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + mul_m4_v3(diff_mat, &pt->x); + } + } + } + } + /* clear parent */ + gpl->parent = NULL; + } } -static int rna_GPencilLayer_is_fill_visible_get(PointerRNA *ptr) +/* set parent type */ +static void rna_GPencilLayer_parent_type_set(PointerRNA *ptr, int value) +{ + bGPDlayer *gpl = (bGPDlayer *)ptr->data; + Object *par = gpl->parent; + gpl->partype = value; + + if (par != NULL) { + set_parent(gpl, par, value, gpl->parsubstr); + } +} + +/* set parent bone */ +static void rna_GPencilLayer_parent_bone_set(PointerRNA *ptr, const char *value) +{ + bGPDlayer *gpl = (bGPDlayer *)ptr->data; + + Object *par = gpl->parent; + gpl->partype = PARBONE; + + if (par != NULL) { + set_parent(gpl, par, gpl->partype, value); + } +} + + +/* parent types enum */ +static EnumPropertyItem *rna_Object_parent_type_itemf( + bContext *UNUSED(C), PointerRNA *ptr, + PropertyRNA *UNUSED(prop), bool *r_free) +{ + bGPDlayer *gpl = (bGPDlayer *)ptr->data; + EnumPropertyItem *item = NULL; + int totitem = 0; + + RNA_enum_items_add_value(&item, &totitem, parent_type_items, PAROBJECT); + + if (gpl->parent) { + Object *par = gpl->parent; + + if (par->type == OB_ARMATURE) { + /* special hack: prevents this being overrided */ + RNA_enum_items_add_value(&item, &totitem, &parent_type_items[1], PARSKEL); + RNA_enum_items_add_value(&item, &totitem, parent_type_items, PARBONE); + } + } + + RNA_enum_item_end(&item, &totitem); + *r_free = true; + + return item; +} + +static int rna_GPencilLayer_is_parented_get(PointerRNA *ptr) { - /* see drawgpencil.c -> gp_draw_data_layers() for more details - * about this limit for showing/not showing - */ bGPDlayer *gpl = (bGPDlayer *)ptr->data; - return (gpl->fill[3] > GPENCIL_ALPHA_OPACITY_THRESH); + return (gpl->parent != NULL); } static PointerRNA rna_GPencil_active_layer_get(PointerRNA *ptr) @@ -201,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); } @@ -211,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) @@ -244,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; } @@ -313,7 +435,7 @@ 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); } } @@ -357,10 +479,12 @@ static void rna_GPencil_stroke_point_pop(bGPDstroke *stroke, ReportList *reports WM_main_add_notifier(NC_GPENCIL | NA_EDITED, NULL); } -static bGPDstroke *rna_GPencil_stroke_new(bGPDframe *frame) +static bGPDstroke *rna_GPencil_stroke_new(bGPDframe *frame, const char *colorname) { bGPDstroke *stroke = MEM_callocN(sizeof(bGPDstroke), "gp_stroke"); - + strcpy(stroke->colorname, colorname); + stroke->palcolor = NULL; + stroke->flag |= GP_STROKE_RECALC_COLOR; BLI_addtail(&frame->strokes, stroke); return stroke; @@ -410,7 +534,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); @@ -425,7 +549,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); @@ -433,7 +557,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++; @@ -448,7 +572,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); @@ -463,7 +587,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); @@ -471,25 +595,258 @@ 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); } +/* Palettes */ +static bGPDpalette *rna_GPencil_palette_new(bGPdata *gpd, const char *name, int setactive) +{ + bGPDpalette *palette = BKE_gpencil_palette_addnew(gpd, name, setactive != 0); + + WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return palette; +} + +static void rna_GPencil_palette_remove(bGPdata *gpd, ReportList *reports, PointerRNA *palette_ptr) +{ + bGPDpalette *palette = palette_ptr->data; + if (BLI_findindex(&gpd->palettes, palette) == -1) { + BKE_report(reports, RPT_ERROR, "Palette not found in grease pencil data"); + return; + } + + BKE_gpencil_palette_delete(gpd, palette); + RNA_POINTER_INVALIDATE(palette_ptr); + + WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL); +} + +static PointerRNA rna_GPencil_active_palette_get(PointerRNA *ptr) +{ + bGPdata *gpd = ptr->id.data; + + if (GS(gpd->id.name) == ID_GD) { /* why would this ever be not GD */ + bGPDpalette *palette; + + for (palette = gpd->palettes.first; palette; palette = palette->next) { + if (palette->flag & PL_PALETTE_ACTIVE) { + break; + } + } + + if (palette) { + return rna_pointer_inherit_refine(ptr, &RNA_GPencilPalette, palette); + } + } + + return rna_pointer_inherit_refine(ptr, NULL, NULL); +} + +static void rna_GPencil_active_palette_set(PointerRNA *ptr, PointerRNA value) +{ + bGPdata *gpd = ptr->id.data; + + if (GS(gpd->id.name) == ID_GD) { /* why would this ever be not GD */ + bGPDpalette *palette; + + for (palette = gpd->palettes.first; palette; palette = palette->next) { + if (palette == value.data) { + palette->flag |= PL_PALETTE_ACTIVE; + } + else { + palette->flag &= ~PL_PALETTE_ACTIVE; + } + } + /* force color recalc */ + BKE_gpencil_palette_change_strokes(gpd); + + WM_main_add_notifier(NC_GPENCIL | NA_EDITED, NULL); + } +} + +static int rna_GPencilPalette_index_get(PointerRNA *ptr) +{ + bGPdata *gpd = (bGPdata *)ptr->id.data; + bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); + + return BLI_findindex(&gpd->palettes, palette); +} + +static void rna_GPencilPalette_index_set(PointerRNA *ptr, int value) +{ + bGPdata *gpd = (bGPdata *)ptr->id.data; + bGPDpalette *palette = BLI_findlink(&gpd->palettes, value); + + BKE_gpencil_palette_setactive(gpd, palette); + WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL); +} + +static void rna_GPencilPalette_index_range(PointerRNA *ptr, int *min, int *max, int *softmin, int *softmax) +{ + bGPdata *gpd = (bGPdata *)ptr->id.data; + + *min = 0; + *max = max_ii(0, BLI_listbase_count(&gpd->palettes) - 1); + + *softmin = *min; + *softmax = *max; +} + +/* Palette colors */ +static bGPDpalettecolor *rna_GPencilPalette_color_new(bGPDpalette *palette) +{ + bGPDpalettecolor *color = BKE_gpencil_palettecolor_addnew(palette, DATA_("Color"), true); + + return color; +} + +static void rna_GPencilPalette_color_remove(bGPDpalette *palette, ReportList *reports, PointerRNA *color_ptr) +{ + bGPDpalettecolor *color = color_ptr->data; + + if (BLI_findindex(&palette->colors, color) == -1) { + BKE_reportf(reports, RPT_ERROR, "Palette '%s' does not contain color given", palette->info + 2); + return; + } + + BKE_gpencil_palettecolor_delete(palette, color); + RNA_POINTER_INVALIDATE(color_ptr); + + WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL); +} + +static PointerRNA rna_GPencilPalette_active_color_get(PointerRNA *ptr) +{ + bGPDpalette *palette = (bGPDpalette *)ptr->data; + bGPDpalettecolor *color; + + for (color = palette->colors.first; color; color = color->next) { + if (color->flag & PC_COLOR_ACTIVE) { + break; + } + } + + if (color) { + return rna_pointer_inherit_refine(ptr, &RNA_GPencilPaletteColor, color); + } + + return rna_pointer_inherit_refine(ptr, NULL, NULL); +} + +static void rna_GPencilPalette_active_color_set(PointerRNA *ptr, PointerRNA value) +{ + bGPDpalette *palette = (bGPDpalette *)ptr->data; + bGPDpalettecolor *color = value.data; + + BKE_gpencil_palettecolor_setactive(palette, color); +} + +static void rna_GPencilPalette_info_set(PointerRNA *ptr, const char *value) +{ + bGPdata *gpd = ptr->id.data; + bGPDpalette *palette = ptr->data; + + /* copy the new name into the name slot */ + BLI_strncpy_utf8(palette->info, value, sizeof(palette->info)); + + BLI_uniquename(&gpd->palettes, palette, DATA_("GP_Palette"), '.', offsetof(bGPDpalette, info), + sizeof(palette->info)); +} + +static char *rna_GPencilPalette_color_path(PointerRNA *ptr) +{ + bGPdata *gpd = ptr->id.data; + bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); + bGPDpalettecolor *palcolor = ptr->data; + + char name_palette[sizeof(palette->info) * 2]; + char name_color[sizeof(palcolor->info) * 2]; + + BLI_strescape(name_palette, palette->info, sizeof(name_palette)); + BLI_strescape(name_color, palcolor->info, sizeof(name_color)); + + return BLI_sprintfN("palettes[\"%s\"].colors[\"%s\"]", name_palette, name_color); +} + +static void rna_GPencilPaletteColor_info_set(PointerRNA *ptr, const char *value) +{ + bGPdata *gpd = ptr->id.data; + bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); + bGPDpalettecolor *palcolor = ptr->data; + + /* rename all strokes */ + 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)); + BLI_uniquename(&palette->colors, palcolor, DATA_("Color"), '.', offsetof(bGPDpalettecolor, info), + sizeof(palcolor->info)); +} + +static void rna_GPencilStrokeColor_info_set(PointerRNA *ptr, const char *value) +{ + bGPDstroke *gps = ptr->data; + + /* copy the new name into the name slot */ + BLI_strncpy_utf8(gps->colorname, value, sizeof(gps->colorname)); +} + + +static int rna_GPencilPaletteColor_is_stroke_visible_get(PointerRNA *ptr) +{ + bGPDpalettecolor *pcolor = (bGPDpalettecolor *)ptr->data; + return (pcolor->color[3] > GPENCIL_ALPHA_OPACITY_THRESH); +} + +static int rna_GPencilPaletteColor_is_fill_visible_get(PointerRNA *ptr) +{ + bGPDpalettecolor *pcolor = (bGPDpalettecolor *)ptr->data; + return (pcolor->fill[3] > GPENCIL_ALPHA_OPACITY_THRESH); +} + +static int rna_GPencilPaletteColor_index_get(PointerRNA *ptr) +{ + bGPDpalette *palette = (bGPDpalette *)ptr->data; + bGPDpalettecolor *pcolor = BKE_gpencil_palettecolor_getactive(palette); + + return BLI_findindex(&palette->colors, pcolor); +} + +static void rna_GPencilPaletteColor_index_set(PointerRNA *ptr, int value) +{ + bGPDpalette *palette = (bGPDpalette *)ptr->data; + bGPDpalettecolor *pcolor = BLI_findlink(&palette->colors, value); + BKE_gpencil_palettecolor_setactive(palette, pcolor); +} + +static void rna_GPencilPaletteColor_index_range(PointerRNA *ptr, int *min, int *max, int *softmin, int *softmax) +{ + bGPDpalette *palette = (bGPDpalette *)ptr->data; + + *min = 0; + *max = max_ii(0, BLI_listbase_count(&palette->colors) - 1); + + *softmin = *min; + *softmax = *max; +} + #else static void rna_def_gpencil_stroke_point(BlenderRNA *brna) @@ -513,6 +870,12 @@ static void rna_def_gpencil_stroke_point(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Pressure", "Pressure of tablet at point when drawing it"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + prop = RNA_def_property(srna, "strength", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "strength"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "Strength", "Color intensity (alpha factor)"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + prop = RNA_def_property(srna, "select", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_SPOINT_SELECT); RNA_def_property_boolean_funcs(prop, NULL, "rna_GPencil_stroke_point_select_set"); @@ -542,6 +905,35 @@ static void rna_def_gpencil_stroke_points_api(BlenderRNA *brna, PropertyRNA *cpr RNA_def_int(func, "index", -1, INT_MIN, INT_MAX, "Index", "point index", INT_MIN, INT_MAX); } +/* This information is read only and it can be used by add-ons */ +static void rna_def_gpencil_triangle(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "GPencilTriangle", NULL); + RNA_def_struct_sdna(srna, "bGPDtriangle"); + RNA_def_struct_ui_text(srna, "Triangle", "Triangulation data for HQ fill"); + + /* 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_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_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_clear_flag(prop, PROP_EDITABLE); +} + static void rna_def_gpencil_stroke(BlenderRNA *brna) { StructRNA *srna; @@ -566,6 +958,19 @@ static void rna_def_gpencil_stroke(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Stroke Points", "Stroke data points"); rna_def_gpencil_stroke_points_api(brna, prop); + /* Triangles */ + prop = RNA_def_property(srna, "triangles", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_sdna(prop, NULL, "triangles", "tot_triangles"); + RNA_def_property_struct_type(prop, "GPencilTriangle"); + RNA_def_property_ui_text(prop, "Triangles", "Triangulation data for HQ fill"); + + /* Color */ + prop = RNA_def_property(srna, "color", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "GPencilPaletteColor"); + RNA_def_property_pointer_sdna(prop, NULL, "palcolor"); + RNA_def_property_ui_text(prop, "Palette Color", "Color from palette used in Stroke"); + RNA_def_property_update(prop, 0, "rna_GPencil_update"); + /* Settings */ prop = RNA_def_property(srna, "draw_mode", PROP_ENUM, PROP_NONE); RNA_def_property_enum_bitflag_sdna(prop, NULL, "flag"); @@ -578,6 +983,27 @@ static void rna_def_gpencil_stroke(BlenderRNA *brna) RNA_def_property_boolean_funcs(prop, NULL, "rna_GPencil_stroke_select_set"); RNA_def_property_ui_text(prop, "Select", "Stroke is selected for viewport editing"); RNA_def_property_update(prop, 0, "rna_GPencil_update"); + + /* Color Name */ + prop = RNA_def_property(srna, "colorname", PROP_STRING, PROP_NONE); + RNA_def_property_string_funcs(prop, NULL, NULL, "rna_GPencilStrokeColor_info_set"); + RNA_def_property_ui_text(prop, "Color Name", "Palette color name"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_stroke_colorname_update"); + + /* Cyclic: Draw a line from end to start point */ + prop = RNA_def_property(srna, "draw_cyclic", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_STROKE_CYCLIC); + RNA_def_property_ui_text(prop, "Cyclic", "Enable cyclic drawing, closing the stroke"); + RNA_def_property_update(prop, 0, "rna_GPencil_update"); + + /* Line Thickness */ + prop = RNA_def_property(srna, "line_width", PROP_INT, PROP_PIXEL); + RNA_def_property_int_sdna(prop, NULL, "thickness"); + RNA_def_property_range(prop, 1, 300); + RNA_def_property_ui_range(prop, 1, 10, 1, 0); + RNA_def_property_ui_text(prop, "Thickness", "Thickness of stroke (in pixels)"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + } static void rna_def_gpencil_strokes_api(BlenderRNA *brna, PropertyRNA *cprop) @@ -594,6 +1020,7 @@ static void rna_def_gpencil_strokes_api(BlenderRNA *brna, PropertyRNA *cprop) func = RNA_def_function(srna, "new", "rna_GPencil_stroke_new"); RNA_def_function_ui_description(func, "Add a new grease pencil stroke"); + parm = RNA_def_string(func, "colorname", 0, MAX_NAME, "Color", "Name of the color"); parm = RNA_def_pointer(func, "stroke", "GPencilStroke", "", "The newly created stroke"); RNA_def_function_return(func, parm); @@ -721,45 +1148,33 @@ static void rna_def_gpencil_layer(BlenderRNA *brna) 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"); - /* Use High Quality Fill */ - prop = RNA_def_property(srna, "use_hq_fill", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LAYER_HQ_FILL); - RNA_def_property_ui_text(prop, "High Quality Fill", "Fill strokes using high quality method to avoid glitches (slower fps during animation playback)"); - RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); - - /* Stroke Drawing Color */ - prop = RNA_def_property(srna, "color", PROP_FLOAT, PROP_COLOR_GAMMA); - RNA_def_property_array(prop, 3); - RNA_def_property_range(prop, 0.0f, 1.0f); - RNA_def_property_ui_text(prop, "Color", "Color for all strokes in this layer"); - RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); - - prop = RNA_def_property(srna, "alpha", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "color[3]"); + prop = RNA_def_property(srna, "opacity", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "opacity"); RNA_def_property_range(prop, 0.0, 1.0f); RNA_def_property_ui_text(prop, "Opacity", "Layer Opacity"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); - /* Fill Drawing Color */ - prop = RNA_def_property(srna, "fill_color", PROP_FLOAT, PROP_COLOR_GAMMA); - RNA_def_property_float_sdna(prop, NULL, "fill"); + /* Tint Color */ + prop = RNA_def_property(srna, "tint_color", PROP_FLOAT, PROP_COLOR_GAMMA); + RNA_def_property_float_sdna(prop, NULL, "tintcolor"); RNA_def_property_array(prop, 3); RNA_def_property_range(prop, 0.0f, 1.0f); - RNA_def_property_ui_text(prop, "Fill Color", "Color for filling region bounded by each stroke"); + RNA_def_property_ui_text(prop, "Tint Color", "Color for tinting stroke colors"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); - prop = RNA_def_property(srna, "fill_alpha", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "fill[3]"); + /* Tint factor */ + prop = RNA_def_property(srna, "tint_factor", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "tintcolor[3]"); RNA_def_property_range(prop, 0.0, 1.0f); - RNA_def_property_ui_text(prop, "Fill Opacity", "Opacity for filling region bounded by each stroke"); + RNA_def_property_ui_text(prop, "Tint Factor", "Factor of tinting color"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); - /* Line Thickness */ - prop = RNA_def_property(srna, "line_width", PROP_INT, PROP_PIXEL); + /* Line Thickness change */ + prop = RNA_def_property(srna, "line_change", PROP_INT, PROP_PIXEL); 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 of strokes (in pixels)"); + RNA_def_property_ui_text(prop, "Thickness", "Thickness change to apply current strokes (in pixels)"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); /* Onion-Skinning */ @@ -803,31 +1218,6 @@ static void rna_def_gpencil_layer(BlenderRNA *brna) RNA_def_property_ui_text(prop, "After Color", "Base color for ghosts after the active frame"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); - /* 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_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); - - /* 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 " - "(smoothing strength is halved on each successive round of smoothing)"); - RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); - - /* 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_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); - /* Flags */ prop = RNA_def_property(srna, "hide", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LAYER_HIDE); @@ -847,6 +1237,15 @@ static void rna_def_gpencil_layer(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Frame Locked", "Lock current frame displayed by layer"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + /* Unlock colors */ + 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_update(prop, NC_GPENCIL | ND_DATA, NULL); + + /* expose as layers.active */ #if 0 prop = RNA_def_property(srna, "active", PROP_BOOLEAN, PROP_NONE); @@ -873,18 +1272,42 @@ static void rna_def_gpencil_layer(BlenderRNA *brna) RNA_def_property_ui_text(prop, "X Ray", "Make the layer draw in front of objects"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); - - /* Read-only state props (for simpler UI code) */ - prop = RNA_def_property(srna, "is_stroke_visible", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_funcs(prop, "rna_GPencilLayer_is_stroke_visible_get", NULL); - RNA_def_property_clear_flag(prop, PROP_EDITABLE); - RNA_def_property_ui_text(prop, "Is Stroke Visible", "True when opacity of stroke is set high enough to be visible"); - - prop = RNA_def_property(srna, "is_fill_visible", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_funcs(prop, "rna_GPencilLayer_is_fill_visible_get", NULL); + /* Parent object */ + prop = RNA_def_property(srna, "parent", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_funcs(prop, NULL, "rna_GPencilLayer_parent_set", NULL, NULL); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK); + RNA_def_property_ui_text(prop, "Parent", "Parent Object"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + + /* parent type */ + prop = RNA_def_property(srna, "parent_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_bitflag_sdna(prop, NULL, "partype"); + RNA_def_property_enum_items(prop, parent_type_items); + RNA_def_property_enum_funcs(prop, NULL, "rna_GPencilLayer_parent_type_set", "rna_Object_parent_type_itemf"); + RNA_def_property_ui_text(prop, "Parent Type", "Type of parent relation"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + + /* parent bone */ + prop = RNA_def_property(srna, "parent_bone", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "parsubstr"); + RNA_def_property_string_funcs(prop, NULL, NULL, "rna_GPencilLayer_parent_bone_set"); + RNA_def_property_ui_text(prop, "Parent Bone", "Name of parent bone in case of a bone parenting relation"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + + /* matrix */ + prop = RNA_def_property(srna, "matrix_inverse", PROP_FLOAT, PROP_MATRIX); + 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_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + + /* read only parented flag */ + prop = RNA_def_property(srna, "is_parented", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_funcs(prop, "rna_GPencilLayer_is_parented_get", NULL); RNA_def_property_clear_flag(prop, PROP_EDITABLE); - RNA_def_property_ui_text(prop, "Is Fill Visible", "True when opacity of fill is set high enough to be visible"); - + RNA_def_property_ui_text(prop, "Is Parented", "True when the layer parent object is set"); + /* Layers API */ func = RNA_def_function(srna, "clear", "rna_GPencil_layer_clear"); RNA_def_function_ui_description(func, "Remove all the grease pencil layer data"); @@ -933,6 +1356,207 @@ static void rna_def_gpencil_layers_api(BlenderRNA *brna, PropertyRNA *cprop) RNA_def_property_ui_text(prop, "Active Layer Index", "Index of active grease pencil layer"); } +static void rna_def_gpencil_palettecolor(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "GPencilPaletteColor", NULL); + RNA_def_struct_sdna(srna, "bGPDpalettecolor"); + RNA_def_struct_ui_text(srna, "Grease Pencil Palette color", "Collection of related colors"); + RNA_def_struct_path_func(srna, "rna_GPencilPalette_color_path"); + + /* Stroke Drawing Color */ + prop = RNA_def_property(srna, "color", PROP_FLOAT, PROP_COLOR_GAMMA); + RNA_def_property_float_sdna(prop, NULL, "color"); + RNA_def_property_array(prop, 3); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "Color", "Color for strokes"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + + prop = RNA_def_property(srna, "alpha", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "color[3]"); + RNA_def_property_range(prop, 0.0, 1.0f); + RNA_def_property_ui_text(prop, "Opacity", "Color Opacity"); + 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"); + 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"); + + /* Fill Drawing Color */ + prop = RNA_def_property(srna, "fill_color", PROP_FLOAT, PROP_COLOR_GAMMA); + RNA_def_property_float_sdna(prop, NULL, "fill"); + RNA_def_property_array(prop, 3); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "Fill Color", "Color for filling region bounded by each stroke"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + + /* Fill alpha */ + prop = RNA_def_property(srna, "fill_alpha", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "fill[3]"); + RNA_def_property_range(prop, 0.0, 1.0f); + RNA_def_property_ui_text(prop, "Fill Opacity", "Opacity for filling region bounded by each stroke"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + + /* Flags */ + prop = RNA_def_property(srna, "hide", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", PC_COLOR_HIDE); + RNA_def_property_ui_icon(prop, ICON_RESTRICT_VIEW_OFF, 1); + RNA_def_property_ui_text(prop, "Hide", "Set color Visibility"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + + prop = RNA_def_property(srna, "lock", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", PC_COLOR_LOCKED); + RNA_def_property_ui_icon(prop, ICON_UNLOCKED, 1); + RNA_def_property_ui_text(prop, "Locked", "Protect color from further editing and/or frame changes"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + + prop = RNA_def_property(srna, "ghost", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", PC_COLOR_ONIONSKIN); + RNA_def_property_ui_icon(prop, ICON_GHOST_ENABLED, 0); + RNA_def_property_ui_text(prop, "Ghost", "Display the color in onion skinning"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + + /* Draw Style */ + prop = RNA_def_property(srna, "use_volumetric_strokes", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", PC_COLOR_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_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + + /* Use High quality fill */ + prop = RNA_def_property(srna, "use_hq_fill", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", PC_COLOR_HQ_FILL); + RNA_def_property_ui_text(prop, "High Quality Fill", "Fill strokes using high quality to avoid glitches " + "(slower fps during animation play)"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + + /* Read-only state props (for simpler UI code) */ + prop = RNA_def_property(srna, "is_stroke_visible", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_funcs(prop, "rna_GPencilPaletteColor_is_stroke_visible_get", NULL); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Is Stroke Visible", "True when opacity of stroke is set high enough to be visible"); + + prop = RNA_def_property(srna, "is_fill_visible", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_funcs(prop, "rna_GPencilPaletteColor_is_fill_visible_get", NULL); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Is Fill Visible", "True when opacity of fill is set high enough to be visible"); +} + +/* palette colors api */ +static void rna_def_gpencil_palettecolors_api(BlenderRNA *brna, PropertyRNA *cprop) +{ + StructRNA *srna; + PropertyRNA *prop; + + FunctionRNA *func; + PropertyRNA *parm; + + RNA_def_property_srna(cprop, "GPencilPaletteColors"); + srna = RNA_def_struct(brna, "GPencilPaletteColors", NULL); + RNA_def_struct_sdna(srna, "bGPDpalette"); + RNA_def_struct_ui_text(srna, "Palette colors", "Collection of palette colors"); + + func = RNA_def_function(srna, "new", "rna_GPencilPalette_color_new"); + RNA_def_function_ui_description(func, "Add a new color to the palette"); + parm = RNA_def_pointer(func, "color", "GPencilPaletteColor", "", "The newly created color"); + RNA_def_function_return(func, parm); + + func = RNA_def_function(srna, "remove", "rna_GPencilPalette_color_remove"); + RNA_def_function_ui_description(func, "Remove a color from the palette"); + RNA_def_function_flag(func, FUNC_USE_REPORTS); + parm = RNA_def_pointer(func, "color", "GPencilPaletteColor", "", "The color to remove"); + RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL | PROP_RNAPTR); + RNA_def_property_clear_flag(parm, PROP_THICK_WRAP); + + prop = RNA_def_property(srna, "active", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "GPencilPaletteColor"); + RNA_def_property_pointer_funcs(prop, "rna_GPencilPalette_active_color_get", "rna_GPencilPalette_active_color_set", NULL, NULL); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Active Palette Color", "Current active color"); + + prop = RNA_def_property(srna, "active_index", PROP_INT, PROP_UNSIGNED); + RNA_def_property_int_funcs(prop, + "rna_GPencilPaletteColor_index_get", + "rna_GPencilPaletteColor_index_set", + "rna_GPencilPaletteColor_index_range"); + RNA_def_property_ui_text(prop, "Active color Index", "Index of active palette color"); +} + +static void rna_def_gpencil_palette(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "GPencilPalette", NULL); + RNA_def_struct_sdna(srna, "bGPDpalette"); + RNA_def_struct_ui_text(srna, "Grease Pencil Palette", "Collection of related palettes"); + RNA_def_struct_ui_icon(srna, ICON_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", "Palette name"); + RNA_def_property_string_funcs(prop, NULL, NULL, "rna_GPencilPalette_info_set"); + RNA_def_struct_name_property(srna, prop); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + + /* Colors */ + prop = RNA_def_property(srna, "colors", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_sdna(prop, NULL, "colors", NULL); + RNA_def_property_struct_type(prop, "GPencilPaletteColor"); + RNA_def_property_ui_text(prop, "Colors", "Colors of the palette"); + rna_def_gpencil_palettecolors_api(brna, prop); + +} + +static void rna_def_gpencil_palettes_api(BlenderRNA *brna, PropertyRNA *cprop) +{ + StructRNA *srna; + PropertyRNA *prop; + + FunctionRNA *func; + PropertyRNA *parm; + + RNA_def_property_srna(cprop, "GreasePencilPalettes"); + srna = RNA_def_struct(brna, "GreasePencilPalettes", NULL); + RNA_def_struct_sdna(srna, "bGPdata"); + RNA_def_struct_ui_text(srna, "Grease Pencil Palettes", "Collection of grease pencil palettes"); + + func = RNA_def_function(srna, "new", "rna_GPencil_palette_new"); + RNA_def_function_ui_description(func, "Add a new grease pencil palette"); + parm = RNA_def_string(func, "name", "GPencilPalette", MAX_NAME, "Name", "Name of the palette"); + RNA_def_property_flag(parm, PROP_REQUIRED); + RNA_def_boolean(func, "set_active", 0, "Set Active", "Activate the newly created palette"); + parm = RNA_def_pointer(func, "palette", "GPencilPalette", "", "The newly created palette"); + RNA_def_function_return(func, parm); + + func = RNA_def_function(srna, "remove", "rna_GPencil_palette_remove"); + RNA_def_function_ui_description(func, "Remove a grease pencil palette"); + RNA_def_function_flag(func, FUNC_USE_REPORTS); + parm = RNA_def_pointer(func, "palette", "GPencilPalette", "", "The palette to remove"); + RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL | PROP_RNAPTR); + RNA_def_property_clear_flag(parm, PROP_THICK_WRAP); + + prop = RNA_def_property(srna, "active", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "GPencilPalette"); + RNA_def_property_pointer_funcs(prop, "rna_GPencil_active_palette_get", "rna_GPencil_active_palette_set", + NULL, NULL); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Active Palette", "Current active palette"); + + prop = RNA_def_property(srna, "active_index", PROP_INT, PROP_UNSIGNED); + RNA_def_property_int_funcs(prop, + "rna_GPencilPalette_index_get", + "rna_GPencilPalette_index_set", + "rna_GPencilPalette_index_range"); + RNA_def_property_ui_text(prop, "Active Palette Index", "Index of active palette"); +} + static void rna_def_gpencil_data(BlenderRNA *brna) { StructRNA *srna; @@ -951,6 +1575,13 @@ static void rna_def_gpencil_data(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Layers", ""); rna_def_gpencil_layers_api(brna, prop); + /* Palettes */ + prop = RNA_def_property(srna, "palettes", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_sdna(prop, NULL, "palettes", NULL); + RNA_def_property_struct_type(prop, "GPencilPalette"); + RNA_def_property_ui_text(prop, "Palettes", ""); + rna_def_gpencil_palettes_api(brna, prop); + /* Animation Data */ rna_def_animdata_common(srna); @@ -967,6 +1598,12 @@ static void rna_def_gpencil_data(BlenderRNA *brna) "Show ghosts of the frames before and after the current frame, toggle to enable on active layer or disable all"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + prop = RNA_def_property(srna, "show_stroke_direction", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_DATA_SHOW_DIRECTION); + RNA_def_property_ui_text(prop, "Show Direction", "Show stroke drawing direction with a bigger green dot (start) " + "and smaller red dot (end) points"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + /* API Functions */ func = RNA_def_function(srna, "clear", "rna_GPencil_clear"); RNA_def_function_ui_description(func, "Remove all the grease pencil data"); @@ -980,8 +1617,12 @@ void RNA_def_gpencil(BlenderRNA *brna) rna_def_gpencil_layer(brna); rna_def_gpencil_frame(brna); + rna_def_gpencil_triangle(brna); rna_def_gpencil_stroke(brna); rna_def_gpencil_stroke_point(brna); + + rna_def_gpencil_palette(brna); + rna_def_gpencil_palettecolor(brna); } #endif diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h index 5a3031d0514..dbff87c8e1b 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); @@ -330,6 +331,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_main.c b/source/blender/makesrna/intern/rna_main.c index 5d3cc75784a..9a245ff0147 100644 --- a/source/blender/makesrna/intern/rna_main.c +++ b/source/blender/makesrna/intern/rna_main.c @@ -275,6 +275,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; @@ -347,6 +353,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 fb7ce951da1..e7b33253d17 100644 --- a/source/blender/makesrna/intern/rna_main_api.c +++ b/source/blender/makesrna/intern/rna_main_api.c @@ -525,6 +525,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 @@ -1502,6 +1503,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; @@ -1518,7 +1534,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_modifier.c b/source/blender/makesrna/intern/rna_modifier.c index be783e5373a..df6c6c913ab 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", ""}, @@ -278,12 +280,17 @@ 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" #include "BKE_modifier.h" #include "BKE_object.h" +#ifdef WITH_ALEMBIC +# include "ABC_alembic.h" +#endif + static void rna_UVProject_projectors_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) { UVProjectModifierData *uvp = (UVProjectModifierData *)ptr->data; @@ -391,6 +398,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: @@ -4094,6 +4103,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; @@ -4621,6 +4666,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_object_force.c b/source/blender/makesrna/intern/rna_object_force.c index 0531cd385b6..c2dad964573 100644 --- a/source/blender/makesrna/intern/rna_object_force.c +++ b/source/blender/makesrna/intern/rna_object_force.c @@ -1433,6 +1433,11 @@ static void rna_def_softbody(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Self Collision", "Enable naive vertex ball self collision"); RNA_def_property_update(prop, 0, "rna_softbody_update"); + 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"); + prop = RNA_def_property(srna, "effector_weights", PROP_POINTER, PROP_NONE); RNA_def_property_pointer_sdna(prop, NULL, "effector_weights"); RNA_def_property_struct_type(prop, "EffectorWeights"); diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 641e8ebc155..f39e7dc10b7 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -34,6 +34,7 @@ #include "DNA_linestyle_types.h" #include "DNA_userdef_types.h" #include "DNA_world_types.h" +#include "DNA_gpencil_types.h" #include "IMB_imbuf_types.h" @@ -435,6 +436,7 @@ EnumPropertyItem rna_enum_bake_pass_filter_type_items[] = { #include "BKE_sequencer.h" #include "BKE_animsys.h" #include "BKE_freestyle.h" +#include "BKE_gpencil.h" #include "ED_info.h" #include "ED_node.h" @@ -447,6 +449,108 @@ EnumPropertyItem rna_enum_bake_pass_filter_type_items[] = { #include "FRS_freestyle.h" #endif +/* Grease pencil Drawing Brushes */ +static bGPDbrush *rna_GPencil_brush_new(ToolSettings *ts, const char *name, int setactive) +{ + bGPDbrush *brush = BKE_gpencil_brush_addnew(ts, name, setactive != 0); + + WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return brush; +} + +static void rna_GPencil_brush_remove(ToolSettings *ts, ReportList *reports, PointerRNA *brush_ptr) +{ + bGPDbrush *brush = brush_ptr->data; + if (BLI_findindex(&ts->gp_brushes, brush) == -1) { + BKE_report(reports, RPT_ERROR, "Brush not found in grease pencil data"); + return; + } + + 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_GPencilBrushes_active_get(PointerRNA *ptr) +{ + ToolSettings *ts = (ToolSettings *) ptr->data; + + bGPDbrush *brush; + + for (brush = ts->gp_brushes.first; brush; brush = brush->next) { + if (brush->flag & GP_BRUSH_ACTIVE) { + break; + } + } + + if (brush) { + return rna_pointer_inherit_refine(ptr, &RNA_GPencilBrush, brush); + } + + return rna_pointer_inherit_refine(ptr, NULL, NULL); +} + +static void rna_GPencilBrushes_active_set(PointerRNA *ptr, PointerRNA value) +{ + ToolSettings *ts = (ToolSettings *) ptr->data; + + bGPDbrush *brush; + + for (brush = ts->gp_brushes.first; brush; brush = brush->next) { + if (brush == value.data) { + brush->flag |= GP_BRUSH_ACTIVE; + } + else { + brush->flag &= ~GP_BRUSH_ACTIVE; + } + } + WM_main_add_notifier(NC_GPENCIL | NA_EDITED, NULL); +} + +static int rna_GPencilBrushes_index_get(PointerRNA *ptr) +{ + ToolSettings *ts = (ToolSettings *) ptr->data; + bGPDbrush *brush = BKE_gpencil_brush_getactive(ts); + + return BLI_findindex(&ts->gp_brushes, brush); +} + +static void rna_GPencilBrushes_index_set(PointerRNA *ptr, int value) +{ + ToolSettings *ts = (ToolSettings *) ptr->data; + + bGPDbrush *brush = BLI_findlink(&ts->gp_brushes, value); + + BKE_gpencil_brush_setactive(ts, brush); + WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL); +} + +static void rna_GPencilBrushes_index_range(PointerRNA *ptr, int *min, int *max, int *softmin, int *softmax) +{ + ToolSettings *ts = (ToolSettings *) ptr->data; + + *min = 0; + *max = max_ii(0, BLI_listbase_count(&ts->gp_brushes) - 1); + + *softmin = *min; + *softmax = *max; +} + +static void rna_GPencilBrush_name_set(PointerRNA *ptr, const char *value) +{ + ToolSettings *ts = ((Scene *) ptr->id.data)->toolsettings; + bGPDbrush *brush = ptr->data; + + /* copy the new name into the name slot */ + BLI_strncpy_utf8(brush->info, value, sizeof(brush->info)); + + BLI_uniquename(&ts->gp_brushes, brush, DATA_("GP_Brush"), '.', offsetof(bGPDbrush, info), sizeof(brush->info)); +} + +/* ----------------- end of Grease pencil drawing brushes ------------*/ + static void rna_SpaceImageEditor_uv_sculpt_update(Main *bmain, Scene *scene, PointerRNA *UNUSED(ptr)) { ED_space_image_uv_sculpt_update(bmain->wm.first, scene); @@ -1976,6 +2080,203 @@ static int rna_gpu_is_hq_supported_get(PointerRNA *UNUSED(ptr)) #else +/* Grease Pencil Drawing Brushes */ +static void rna_def_gpencil_brush(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + 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_icon(srna, ICON_BRUSH_DATA); + + /* 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", "Brush name"); + RNA_def_property_string_funcs(prop, NULL, NULL, "rna_GPencilBrush_name_set"); + RNA_def_struct_name_property(srna, prop); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + + /* Line Thickness */ + prop = RNA_def_property(srna, "line_width", PROP_INT, PROP_PIXEL); + RNA_def_property_int_sdna(prop, NULL, "thickness"); + RNA_def_property_range(prop, 1, 300); + RNA_def_property_ui_range(prop, 1, 10, 1, 0); + RNA_def_property_ui_text(prop, "Thickness", "Thickness of strokes (in pixels)"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + + /* Sensitivity factor for new strokes */ + prop = RNA_def_property(srna, "pen_sensitivity_factor", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "draw_sensitivity"); + RNA_def_property_range(prop, 0.1f, 3.0f); + RNA_def_property_ui_text(prop, "Sensitivity", "Pressure sensitivity factor for new strokes"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + + /* Strength factor for new strokes */ + prop = RNA_def_property(srna, "strength", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "draw_strength"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "Strength", "Color strength for new strokes (affect alpha factor of color)"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + + /* Jitter factor for new strokes */ + prop = RNA_def_property(srna, "jitter", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "draw_jitter"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "Jitter", "Jitter factor for new strokes"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + + /* Randomnes factor for sensitivity and strength */ + prop = RNA_def_property(srna, "random_press", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "draw_random_press"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "Randomness", "Randomness factor for pressure and strength in new strokes"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + + /* Randomnes factor for subdivision */ + prop = RNA_def_property(srna, "random_subdiv", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "draw_random_sub"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "Random Subdivision", "Randomness factor for new strokes after subdivision"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + + /* 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_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_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_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_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_update(prop, NC_GPENCIL | ND_DATA, NULL); + + /* Curves for pressure */ + prop = RNA_def_property(srna, "curve_sensitivity", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "cur_sensitivity"); + RNA_def_property_struct_type(prop, "CurveMapping"); + RNA_def_property_ui_text(prop, "Curve Sensitivity", "Curve used for the sensitivity"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + + prop = RNA_def_property(srna, "curve_strength", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "cur_strength"); + RNA_def_property_struct_type(prop, "CurveMapping"); + RNA_def_property_ui_text(prop, "Curve Strength", "Curve used for the strength"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + + prop = RNA_def_property(srna, "curve_jitter", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "cur_jitter"); + RNA_def_property_struct_type(prop, "CurveMapping"); + RNA_def_property_ui_text(prop, "Curve Jitter", "Curve used for the jitter effect"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + + /* Flags */ + prop = RNA_def_property(srna, "use_pressure", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSH_USE_PRESSURE); + RNA_def_property_ui_icon(prop, ICON_STYLUS_PRESSURE, 0); + RNA_def_property_ui_text(prop, "Use Pressure", "Use tablet pressure"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + + prop = RNA_def_property(srna, "use_strength_pressure", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSH_USE_STENGTH_PRESSURE); + RNA_def_property_ui_icon(prop, ICON_STYLUS_PRESSURE, 0); + RNA_def_property_ui_text(prop, "Use Pressure Strength", "Use tablet pressure for color strength"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + + prop = RNA_def_property(srna, "use_jitter_pressure", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSH_USE_JITTER_PRESSURE); + RNA_def_property_ui_icon(prop, ICON_STYLUS_PRESSURE, 0); + RNA_def_property_ui_text(prop, "Use Pressure Jitter", "Use tablet pressure for jitter"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + + prop = RNA_def_property(srna, "use_random_pressure", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSH_USE_RANDOM_PRESSURE); + RNA_def_property_ui_icon(prop, ICON_PARTICLES, 0); + RNA_def_property_ui_text(prop, "Random Pressure", "Use random value for pressure"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + + prop = RNA_def_property(srna, "use_random_strength", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSH_USE_RANDOM_STRENGTH); + RNA_def_property_ui_icon(prop, ICON_PARTICLES, 0); + RNA_def_property_ui_text(prop, "Random Strength", "Use random value for strength"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + +} + +/* Grease Pencil Drawing Brushes API */ +static void rna_def_gpencil_brushes(BlenderRNA *brna, PropertyRNA *cprop) +{ + StructRNA *srna; + PropertyRNA *prop; + + FunctionRNA *func; + PropertyRNA *parm; + + RNA_def_property_srna(cprop, "GreasePencilBrushes"); + srna = RNA_def_struct(brna, "GreasePencilBrushes", NULL); + RNA_def_struct_sdna(srna, "ToolSettings"); + RNA_def_struct_ui_text(srna, "Grease Pencil Brushes", "Collection of grease pencil brushes"); + + func = RNA_def_function(srna, "new", "rna_GPencil_brush_new"); + RNA_def_function_ui_description(func, "Add a new grease pencil brush"); + parm = RNA_def_string(func, "name", "GPencilBrush", MAX_NAME, "Name", "Name of the brush"); + RNA_def_property_flag(parm, PROP_REQUIRED); + RNA_def_boolean(func, "set_active", 0, "Set Active", "Set the newly created brush to the active brush"); + parm = RNA_def_pointer(func, "palette", "GPencilBrush", "", "The newly created brush"); + RNA_def_function_return(func, parm); + + func = RNA_def_function(srna, "remove", "rna_GPencil_brush_remove"); + RNA_def_function_ui_description(func, "Remove a grease pencil brush"); + RNA_def_function_flag(func, FUNC_USE_REPORTS); + parm = RNA_def_pointer(func, "brush", "GPencilBrush", "", "The brush to remove"); + RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL | PROP_RNAPTR); + RNA_def_property_clear_flag(parm, PROP_THICK_WRAP); + + prop = RNA_def_property(srna, "active", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "GPencilBrush"); + 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_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) { StructRNA *srna; @@ -2303,7 +2604,14 @@ static void rna_def_tool_settings(BlenderRNA *brna) RNA_def_property_pointer_sdna(prop, NULL, "gp_sculpt"); RNA_def_property_struct_type(prop, "GPencilSculptSettings"); RNA_def_property_ui_text(prop, "Grease Pencil Sculpt", ""); - + + /* Grease Pencil - Drawing brushes */ + prop = RNA_def_property(srna, "gpencil_brushes", PROP_COLLECTION, PROP_NONE); + 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(brna, prop); + /* Grease Pencil - 3D View Stroke Placement */ prop = RNA_def_property(srna, "gpencil_stroke_placement_view3d", PROP_ENUM, PROP_NONE); RNA_def_property_enum_bitflag_sdna(prop, NULL, "gpencil_v3d_align"); @@ -6808,6 +7116,7 @@ void RNA_def_scene(BlenderRNA *brna) /* *** Non-Animated *** */ RNA_define_animate_sdna(false); rna_def_tool_settings(brna); + rna_def_gpencil_brush(brna); rna_def_unified_paint_settings(brna); rna_def_curve_paint_settings(brna); rna_def_statvis(brna); diff --git a/source/blender/makesrna/intern/rna_scene_api.c b/source/blender/makesrna/intern/rna_scene_api.c index 1d3537dc9a0..ed51f0cffb0 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" @@ -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 68da2b6dedc..2fe9bdd17e1 100644 --- a/source/blender/makesrna/intern/rna_sculpt_paint.c +++ b/source/blender/makesrna/intern/rna_sculpt_paint.c @@ -52,7 +52,8 @@ EnumPropertyItem rna_enum_gpencil_sculpt_brush_items[] = { {GP_EDITBRUSH_TYPE_SMOOTH, "SMOOTH", 0, "Smooth", "Smooth stroke points"}, {GP_EDITBRUSH_TYPE_THICKNESS, "THICKNESS", 0, "Thickness", "Adjust thickness of strokes"}, - {GP_EDITBRUSH_TYPE_GRAB, "GRAB", 0, "Grab", "Translate the set of points initially within the brush circle"}, + { GP_EDITBRUSH_TYPE_STRENGTH, "STRENGTH", 0, "Strength", "Adjust color strength of strokes" }, + { GP_EDITBRUSH_TYPE_GRAB, "GRAB", 0, "Grab", "Translate the set of points initially within the brush circle" }, {GP_EDITBRUSH_TYPE_PUSH, "PUSH", 0, "Push", "Move points out of the way, as if combing them"}, {GP_EDITBRUSH_TYPE_TWIST, "TWIST", 0, "Twist", "Rotate points around the midpoint of the brush"}, {GP_EDITBRUSH_TYPE_PINCH, "PINCH", 0, "Pinch", "Pull points towards the midpoint of the brush"}, @@ -60,7 +61,7 @@ EnumPropertyItem rna_enum_gpencil_sculpt_brush_items[] = { //{GP_EDITBRUSH_TYPE_SUBDIVIDE, "SUBDIVIDE", 0, "Subdivide", "Increase point density for higher resolution strokes when zoomed in"}, //{GP_EDITBRUSH_TYPE_SIMPLIFY, "SIMPLIFY", 0, "Simplify", "Reduce density of stroke points"}, {GP_EDITBRUSH_TYPE_CLONE, "CLONE", 0, "Clone", "Paste copies of the strokes stored on the clipboard"}, - {0, NULL, 0, NULL, NULL} + { 0, NULL, 0, NULL, NULL } }; EnumPropertyItem rna_enum_symmetrize_direction_items[] = { @@ -85,6 +86,11 @@ EnumPropertyItem rna_enum_symmetrize_direction_items[] = { #include "GPU_buffers.h" +static void rna_GPencil_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *UNUSED(ptr)) +{ + WM_main_add_notifier(NC_GPENCIL | NA_EDITED, NULL); +} + static int rna_Brush_mode_poll(PointerRNA *ptr, PointerRNA value) { Scene *scene = (Scene *)ptr->id.data; @@ -697,6 +703,27 @@ static void rna_def_gpencil_sculpt(BlenderRNA *brna) RNA_def_property_ui_icon(prop, ICON_VERTEXSEL, 0); // FIXME: this needs a custom icon RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + 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_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_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_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + prop = RNA_def_property(srna, "selection_alpha", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "alpha"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "Alpha", "Alpha value for selected vertices"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, "rna_GPencil_update"); + /* brush */ srna = RNA_def_struct(brna, "GPencilSculptBrush", NULL); RNA_def_struct_sdna(srna, "GP_EditBrush_Data"); diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index ca5c0f2c6f0..944e350a77c 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -3345,6 +3345,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} }; @@ -3784,6 +3785,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"}, @@ -3827,7 +3829,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_PAL | FILTER_ID_PC | FILTER_ID_TXT | FILTER_ID_VF, + {FILTER_ID_BR | FILTER_ID_GD | 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_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/modifiers/CMakeLists.txt b/source/blender/modifiers/CMakeLists.txt index a82c88d64f8..d5b9303249a 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 @@ -110,6 +111,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 ee8a03fb411..93176cb012a 100644 --- a/source/blender/modifiers/MOD_modifiertypes.h +++ b/source/blender/modifiers/MOD_modifiertypes.h @@ -82,6 +82,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_fluidsim_util.c b/source/blender/modifiers/intern/MOD_fluidsim_util.c index 77fbb482f8b..ffbbb1b0745 100644 --- a/source/blender/modifiers/intern/MOD_fluidsim_util.c +++ b/source/blender/modifiers/intern/MOD_fluidsim_util.c @@ -525,7 +525,7 @@ DerivedMesh *fluidsimModifier_do(FluidsimModifierData *fluidmd, Scene *scene, return dm; /* sanity check */ - if (!fluidmd || (fluidmd && !fluidmd->fss)) + if (!fluidmd || !fluidmd->fss) return dm; fss = fluidmd->fss; 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_util.c b/source/blender/modifiers/intern/MOD_util.c index bcc28ddbcb5..a59ace130e7 100644 --- a/source/blender/modifiers/intern/MOD_util.c +++ b/source/blender/modifiers/intern/MOD_util.c @@ -284,5 +284,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/physics/intern/BPH_mass_spring.cpp b/source/blender/physics/intern/BPH_mass_spring.cpp index 648ef132655..359395b63c4 100644 --- a/source/blender/physics/intern/BPH_mass_spring.cpp +++ b/source/blender/physics/intern/BPH_mass_spring.cpp @@ -376,7 +376,8 @@ BLI_INLINE void cloth_calc_spring_force(ClothModifierData *clmd, ClothSpring *s, s->flags |= CLOTH_SPRING_FLAG_NEEDED; // current_position = xold + t * (newposition - xold) - interp_v3_v3v3(goal_x, verts[s->ij].xold, verts[s->ij].xconst, time); + /* divide by time_scale to prevent goal vertices' delta locations from being multiplied */ + interp_v3_v3v3(goal_x, verts[s->ij].xold, verts[s->ij].xconst, time / parms->time_scale); sub_v3_v3v3(goal_v, verts[s->ij].xconst, verts[s->ij].xold); // distance covered over dt==1 scaling = parms->goalspring + s->stiffness * fabsf(parms->max_struct - parms->goalspring); @@ -1004,6 +1005,8 @@ int BPH_cloth_solve(Object *ob, float frame, ClothModifierData *clmd, ListBase * float v[3]; sub_v3_v3v3(v, verts[i].xconst, verts[i].xold); // mul_v3_fl(v, clmd->sim_parms->stepsPerFrame); + /* divide by time_scale to prevent constrained velocities from being multiplied */ + mul_v3_fl(v, 1.0f / clmd->sim_parms->time_scale); BPH_mass_spring_set_velocity(id, i, v); } } @@ -1070,7 +1073,8 @@ int BPH_cloth_solve(Object *ob, float frame, ClothModifierData *clmd, ListBase * if (clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_GOAL) { if (verts[i].flags & CLOTH_VERT_FLAG_PINNED) { float x[3]; - interp_v3_v3v3(x, verts[i].xold, verts[i].xconst, step + dt); + /* divide by time_scale to prevent pinned vertices' delta locations from being multiplied */ + interp_v3_v3v3(x, verts[i].xold, verts[i].xconst, (step + dt) / clmd->sim_parms->time_scale); BPH_mass_spring_set_position(id, i, x); } } diff --git a/source/blender/python/BPY_extern.h b/source/blender/python/BPY_extern.h index 4006816e788..3148dab3c50 100644 --- a/source/blender/python/BPY_extern.h +++ b/source/blender/python/BPY_extern.h @@ -32,6 +32,7 @@ #ifndef __BPY_EXTERN_H__ #define __BPY_EXTERN_H__ +struct PathResolvedRNA; struct Text; /* defined in DNA_text_types.h */ struct ID; /* DNA_ID.h */ struct Object; /* DNA_object_types.h */ @@ -85,7 +86,7 @@ void BPY_modules_load_user(struct bContext *C); void BPY_app_handlers_reset(const short do_all); void BPY_driver_reset(void); -float BPY_driver_exec(struct ChannelDriver *driver, const float evaltime); +float BPY_driver_exec(struct PathResolvedRNA *anim_rna, struct ChannelDriver *driver, const float evaltime); void BPY_DECREF(void *pyob_ptr); /* Py_DECREF() */ void BPY_DECREF_RNA_INVALIDATE(void *pyob_ptr); diff --git a/source/blender/python/bmesh/bmesh_py_ops_call.c b/source/blender/python/bmesh/bmesh_py_ops_call.c index 551a66d1ed8..8f287918a4a 100644 --- a/source/blender/python/bmesh/bmesh_py_ops_call.c +++ b/source/blender/python/bmesh/bmesh_py_ops_call.c @@ -581,7 +581,7 @@ static PyObject *bpy_slot_to_py(BMesh *bm, BMOpSlot *slot) switch (slot->slot_subtype.map) { case BMO_OP_SLOT_SUBTYPE_MAP_ELEM: { - item = PyDict_New(); + item = _PyDict_NewPresized(slot_hash ? BLI_ghash_size(slot_hash) : 0); if (slot_hash) { GHASH_ITER (hash_iter, slot_hash) { BMHeader *ele_key = BLI_ghashIterator_getKey(&hash_iter); @@ -599,7 +599,7 @@ static PyObject *bpy_slot_to_py(BMesh *bm, BMOpSlot *slot) } case BMO_OP_SLOT_SUBTYPE_MAP_FLT: { - item = PyDict_New(); + item = _PyDict_NewPresized(slot_hash ? BLI_ghash_size(slot_hash) : 0); if (slot_hash) { GHASH_ITER (hash_iter, slot_hash) { BMHeader *ele_key = BLI_ghashIterator_getKey(&hash_iter); @@ -617,7 +617,7 @@ static PyObject *bpy_slot_to_py(BMesh *bm, BMOpSlot *slot) } case BMO_OP_SLOT_SUBTYPE_MAP_INT: { - item = PyDict_New(); + item = _PyDict_NewPresized(slot_hash ? BLI_ghash_size(slot_hash) : 0); if (slot_hash) { GHASH_ITER (hash_iter, slot_hash) { BMHeader *ele_key = BLI_ghashIterator_getKey(&hash_iter); @@ -635,7 +635,7 @@ static PyObject *bpy_slot_to_py(BMesh *bm, BMOpSlot *slot) } case BMO_OP_SLOT_SUBTYPE_MAP_BOOL: { - item = PyDict_New(); + item = _PyDict_NewPresized(slot_hash ? BLI_ghash_size(slot_hash) : 0); if (slot_hash) { GHASH_ITER (hash_iter, slot_hash) { BMHeader *ele_key = BLI_ghashIterator_getKey(&hash_iter); diff --git a/source/blender/python/generic/idprop_py_api.c b/source/blender/python/generic/idprop_py_api.c index 11646f3f3df..2e15b7b1413 100644 --- a/source/blender/python/generic/idprop_py_api.c +++ b/source/blender/python/generic/idprop_py_api.c @@ -98,7 +98,7 @@ static PyObject *idprop_py_from_idp_array(ID *id, IDProperty *prop) static PyObject *idprop_py_from_idp_idparray(ID *id, IDProperty *prop) { - PyObject *seq = PyList_New(prop->len), *wrap; + PyObject *seq = PyList_New(prop->len); IDProperty *array = IDP_IDPArray(prop); int i; @@ -110,10 +110,13 @@ static PyObject *idprop_py_from_idp_idparray(ID *id, IDProperty *prop) } for (i = 0; i < prop->len; i++) { - wrap = BPy_IDGroup_WrapData(id, array++, prop); + PyObject *wrap = BPy_IDGroup_WrapData(id, array++, prop); - if (!wrap) /* BPy_IDGroup_MapDataToPy sets the error */ + /* BPy_IDGroup_MapDataToPy sets the error */ + if (UNLIKELY(wrap == NULL)) { + Py_DECREF(seq); return NULL; + } PyList_SET_ITEM(seq, i, wrap); } @@ -378,8 +381,10 @@ bool BPy_IDProperty_Map_ValidateAndCreate(PyObject *name_obj, IDProperty *group, } else if (PyUnicode_Check(ob)) { #ifdef USE_STRING_COERCE + Py_ssize_t value_size; PyObject *value_coerce = NULL; - val.string.str = PyC_UnicodeAsByte(ob, &value_coerce); + val.string.str = PyC_UnicodeAsByteAndSize(ob, &value_size, &value_coerce); + val.string.len = (int)value_size + 1; val.string.subtype = IDP_STRING_SUB_UTF8; prop = IDP_New(IDP_STRING, &val, name); Py_XDECREF(value_coerce); @@ -657,7 +662,7 @@ static PyObject *BPy_IDGroup_MapDataToPy(IDProperty *prop) } case IDP_IDPARRAY: { - PyObject *seq = PyList_New(prop->len), *wrap; + PyObject *seq = PyList_New(prop->len); IDProperty *array = IDP_IDPArray(prop); int i; @@ -669,10 +674,13 @@ static PyObject *BPy_IDGroup_MapDataToPy(IDProperty *prop) } for (i = 0; i < prop->len; i++) { - wrap = BPy_IDGroup_MapDataToPy(array++); + PyObject *wrap = BPy_IDGroup_MapDataToPy(array++); - if (!wrap) /* BPy_IDGroup_MapDataToPy sets the error */ + /* BPy_IDGroup_MapDataToPy sets the error */ + if (UNLIKELY(wrap == NULL)) { + Py_DECREF(seq); return NULL; + } PyList_SET_ITEM(seq, i, wrap); } @@ -680,14 +688,17 @@ static PyObject *BPy_IDGroup_MapDataToPy(IDProperty *prop) } case IDP_GROUP: { - PyObject *dict = PyDict_New(), *wrap; + PyObject *dict = _PyDict_NewPresized(prop->len); IDProperty *loop; for (loop = prop->data.group.first; loop; loop = loop->next) { - wrap = BPy_IDGroup_MapDataToPy(loop); + PyObject *wrap = BPy_IDGroup_MapDataToPy(loop); - if (!wrap) /* BPy_IDGroup_MapDataToPy sets the error */ + /* BPy_IDGroup_MapDataToPy sets the error */ + if (UNLIKELY(wrap == NULL)) { + Py_DECREF(dict); return NULL; + } PyDict_SetItemString(dict, loop->name, wrap); Py_DECREF(wrap); @@ -702,7 +713,17 @@ static PyObject *BPy_IDGroup_MapDataToPy(IDProperty *prop) return NULL; } -static PyObject *BPy_IDGroup_Pop(BPy_IDProperty *self, PyObject *value) +PyDoc_STRVAR(BPy_IDGroup_pop_doc, +".. method:: pop(key)\n" +"\n" +" Remove an item from the group, returning a Python representation.\n" +"\n" +" :raises KeyError: When the item doesn't exist.\n" +"\n" +" :arg key: Name of item to remove.\n" +" :type key: string\n" +); +static PyObject *BPy_IDGroup_pop(BPy_IDProperty *self, PyObject *value) { IDProperty *idprop; PyObject *pyform; @@ -735,7 +756,12 @@ static PyObject *BPy_IDGroup_Pop(BPy_IDProperty *self, PyObject *value) return NULL; } -static PyObject *BPy_IDGroup_IterItems(BPy_IDProperty *self) +PyDoc_STRVAR(BPy_IDGroup_iter_items_doc, +".. method:: iteritems()\n" +"\n" +" Iterate through the items in the dict; behaves like dictionary method iteritems.\n" +); +static PyObject *BPy_IDGroup_iter_items(BPy_IDProperty *self) { BPy_IDGroup_Iter *iter = PyObject_New(BPy_IDGroup_Iter, &BPy_IDGroup_Iter_Type); iter->group = self; @@ -829,18 +855,32 @@ PyObject *BPy_Wrap_GetItems(ID *id, IDProperty *prop) return seq; } - -static PyObject *BPy_IDGroup_GetKeys(BPy_IDProperty *self) +PyDoc_STRVAR(BPy_IDGroup_keys_doc, +".. method:: keys()\n" +"\n" +" Return the keys associated with this group as a list of strings.\n" +); +static PyObject *BPy_IDGroup_keys(BPy_IDProperty *self) { return BPy_Wrap_GetKeys(self->prop); } -static PyObject *BPy_IDGroup_GetValues(BPy_IDProperty *self) +PyDoc_STRVAR(BPy_IDGroup_values_doc, +".. method:: values()\n" +"\n" +" Return the values associated with this group.\n" +); +static PyObject *BPy_IDGroup_values(BPy_IDProperty *self) { return BPy_Wrap_GetValues(self->id, self->prop); } -static PyObject *BPy_IDGroup_GetItems(BPy_IDProperty *self) +PyDoc_STRVAR(BPy_IDGroup_items_doc, +".. method:: items()\n" +"\n" +" Return the items associated with this group.\n" +); +static PyObject *BPy_IDGroup_items(BPy_IDProperty *self) { return BPy_Wrap_GetItems(self->id, self->prop); } @@ -859,7 +899,15 @@ static int BPy_IDGroup_Contains(BPy_IDProperty *self, PyObject *value) return IDP_GetPropertyFromGroup(self->prop, name) ? 1 : 0; } -static PyObject *BPy_IDGroup_Update(BPy_IDProperty *self, PyObject *value) +PyDoc_STRVAR(BPy_IDGroup_update_doc, +".. method:: update(other)\n" +"\n" +" Update key, values.\n" +"\n" +" :arg other: Updates the values in the group with this.\n" +" :type other: :class:`IDPropertyGroup` or dict\n" +); +static PyObject *BPy_IDGroup_update(BPy_IDProperty *self, PyObject *value) { PyObject *pkey, *pval; Py_ssize_t i = 0; @@ -890,19 +938,33 @@ static PyObject *BPy_IDGroup_Update(BPy_IDProperty *self, PyObject *value) Py_RETURN_NONE; } +PyDoc_STRVAR(BPy_IDGroup_to_dict_doc, +".. method:: to_dict()\n" +"\n" +" Return a purely python version of the group.\n" +); static PyObject *BPy_IDGroup_to_dict(BPy_IDProperty *self) { return BPy_IDGroup_MapDataToPy(self->prop); } +PyDoc_STRVAR(BPy_IDGroup_clear_doc, +".. method:: clear()\n" +"\n" +" Clear all members from this group.\n" +); static PyObject *BPy_IDGroup_clear(BPy_IDProperty *self) { IDP_ClearProperty(self->prop); Py_RETURN_NONE; } -/* Matches python dict.get(key, [default]) */ -static PyObject *BPy_IDGroup_Get(BPy_IDProperty *self, PyObject *args) +PyDoc_STRVAR(BPy_IDGroup_get_doc, +".. method:: get(key, default=None)\n" +"\n" +" Return the value for key, if it exists, else default.\n" +); +static PyObject *BPy_IDGroup_get(BPy_IDProperty *self, PyObject *args) { IDProperty *idprop; const char *key; @@ -923,24 +985,15 @@ static PyObject *BPy_IDGroup_Get(BPy_IDProperty *self, PyObject *args) } static struct PyMethodDef BPy_IDGroup_methods[] = { - {"pop", (PyCFunction)BPy_IDGroup_Pop, METH_O, - "pop an item from the group; raises KeyError if the item doesn't exist"}, - {"iteritems", (PyCFunction)BPy_IDGroup_IterItems, METH_NOARGS, - "iterate through the items in the dict; behaves like dictionary method iteritems"}, - {"keys", (PyCFunction)BPy_IDGroup_GetKeys, METH_NOARGS, - "get the keys associated with this group as a list of strings"}, - {"values", (PyCFunction)BPy_IDGroup_GetValues, METH_NOARGS, - "get the values associated with this group"}, - {"items", (PyCFunction)BPy_IDGroup_GetItems, METH_NOARGS, - "get the items associated with this group"}, - {"update", (PyCFunction)BPy_IDGroup_Update, METH_O, - "updates the values in the group with the values of another or a dict"}, - {"get", (PyCFunction)BPy_IDGroup_Get, METH_VARARGS, - "idprop.get(k[,d]) -> idprop[k] if k in idprop, else d. d defaults to None"}, - {"to_dict", (PyCFunction)BPy_IDGroup_to_dict, METH_NOARGS, - "return a purely python version of the group"}, - {"clear", (PyCFunction)BPy_IDGroup_clear, METH_NOARGS, - "clear all members from this group"}, + {"pop", (PyCFunction)BPy_IDGroup_pop, METH_O, BPy_IDGroup_pop_doc}, + {"iteritems", (PyCFunction)BPy_IDGroup_iter_items, METH_NOARGS, BPy_IDGroup_iter_items_doc}, + {"keys", (PyCFunction)BPy_IDGroup_keys, METH_NOARGS, BPy_IDGroup_keys_doc}, + {"values", (PyCFunction)BPy_IDGroup_values, METH_NOARGS, BPy_IDGroup_values_doc}, + {"items", (PyCFunction)BPy_IDGroup_items, METH_NOARGS, BPy_IDGroup_items_doc}, + {"update", (PyCFunction)BPy_IDGroup_update, METH_O, BPy_IDGroup_update_doc}, + {"get", (PyCFunction)BPy_IDGroup_get, METH_VARARGS, BPy_IDGroup_get_doc}, + {"to_dict", (PyCFunction)BPy_IDGroup_to_dict, METH_NOARGS, BPy_IDGroup_to_dict_doc}, + {"clear", (PyCFunction)BPy_IDGroup_clear, METH_NOARGS, BPy_IDGroup_clear_doc}, {NULL, NULL, 0, NULL} }; @@ -1049,7 +1102,10 @@ static PyObject *BPy_IDArray_repr(BPy_IDArray *self) return PyUnicode_FromFormat("<bpy id property array [%d]>", self->prop->len); } -static PyObject *BPy_IDArray_GetType(BPy_IDArray *self) +PyDoc_STRVAR(BPy_IDArray_get_typecode_doc, +"The type of the data in the array {'f': float, 'd': double, 'i': int}." +); +static PyObject *BPy_IDArray_get_typecode(BPy_IDArray *self) { switch (self->prop->subtype) { case IDP_FLOAT: return PyUnicode_FromString("f"); @@ -1066,18 +1122,22 @@ static PyObject *BPy_IDArray_GetType(BPy_IDArray *self) static PyGetSetDef BPy_IDArray_getseters[] = { /* matches pythons array.typecode */ - {(char *)"typecode", (getter)BPy_IDArray_GetType, (setter)NULL, (char *)"The type of the data in the array, is an int.", NULL}, + {(char *)"typecode", (getter)BPy_IDArray_get_typecode, (setter)NULL, BPy_IDArray_get_typecode_doc, NULL}, {NULL, NULL, NULL, NULL, NULL}, }; +PyDoc_STRVAR(BPy_IDArray_to_list_doc, +".. method:: to_list()\n" +"\n" +" Return the array as a list.\n" +); static PyObject *BPy_IDArray_to_list(BPy_IDArray *self) { return BPy_IDGroup_MapDataToPy(self->prop); } static PyMethodDef BPy_IDArray_methods[] = { - {"to_list", (PyCFunction)BPy_IDArray_to_list, METH_NOARGS, - "return the array as a list"}, + {"to_list", (PyCFunction)BPy_IDArray_to_list, METH_NOARGS, BPy_IDArray_to_list_doc}, {NULL, NULL, 0, NULL} }; diff --git a/source/blender/python/generic/py_capi_utils.c b/source/blender/python/generic/py_capi_utils.c index 72dec55e50b..7b2d58a1268 100644 --- a/source/blender/python/generic/py_capi_utils.c +++ b/source/blender/python/generic/py_capi_utils.c @@ -540,6 +540,35 @@ PyObject *PyC_ExceptionBuffer_Simple(void) } /* string conversion, escape non-unicode chars, coerce must be set to NULL */ +const char *PyC_UnicodeAsByteAndSize(PyObject *py_str, Py_ssize_t *size, PyObject **coerce) +{ + const char *result; + + result = _PyUnicode_AsStringAndSize(py_str, size); + + if (result) { + /* 99% of the time this is enough but we better support non unicode + * chars since blender doesnt limit this */ + return result; + } + else { + PyErr_Clear(); + + if (PyBytes_Check(py_str)) { + *size = PyBytes_GET_SIZE(py_str); + return PyBytes_AS_STRING(py_str); + } + else if ((*coerce = PyUnicode_EncodeFSDefault(py_str))) { + *size = PyBytes_GET_SIZE(*coerce); + return PyBytes_AS_STRING(*coerce); + } + else { + /* leave error raised from EncodeFS */ + return NULL; + } + } +} + const char *PyC_UnicodeAsByte(PyObject *py_str, PyObject **coerce) { const char *result; diff --git a/source/blender/python/generic/py_capi_utils.h b/source/blender/python/generic/py_capi_utils.h index a06a717ecbc..04cfc8801eb 100644 --- a/source/blender/python/generic/py_capi_utils.h +++ b/source/blender/python/generic/py_capi_utils.h @@ -53,6 +53,7 @@ void PyC_List_Fill(PyObject *list, PyObject *value); PyObject * PyC_UnicodeFromByte(const char *str); PyObject * PyC_UnicodeFromByteAndSize(const char *str, Py_ssize_t size); const char * PyC_UnicodeAsByte(PyObject *py_str, PyObject **coerce); /* coerce must be NULL */ +const char * PyC_UnicodeAsByteAndSize(PyObject *py_str, Py_ssize_t *size, PyObject **coerce); /* name namespace function for bpy & bge */ PyObject * PyC_DefaultNameSpace(const char *filename); 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_app_translations.c b/source/blender/python/intern/bpy_app_translations.c index b90deafb377..6ba858f0228 100644 --- a/source/blender/python/intern/bpy_app_translations.c +++ b/source/blender/python/intern/bpy_app_translations.c @@ -675,7 +675,7 @@ static PyObject *app_translations_new(PyTypeObject *type, PyObject *UNUSED(args) _translations->contexts = app_translations_contexts_make(); - py_ctxts = PyDict_New(); + py_ctxts = _PyDict_NewPresized(ARRAY_SIZE(_contexts)); for (ctxt = _contexts; ctxt->c_id; ctxt++) { PyObject *val = PyUnicode_FromString(ctxt->py_id); PyDict_SetItemString(py_ctxts, ctxt->c_id, val); diff --git a/source/blender/python/intern/bpy_driver.c b/source/blender/python/intern/bpy_driver.c index 65b6bd501ce..c8ce7b2f770 100644 --- a/source/blender/python/intern/bpy_driver.c +++ b/source/blender/python/intern/bpy_driver.c @@ -44,6 +44,8 @@ #include "bpy_rna_driver.h" /* for pyrna_driver_get_variable_value */ +#include "bpy_intern_string.h" + #include "bpy_driver.h" extern void BPY_update_rna_module(void); @@ -97,26 +99,48 @@ int bpy_pydriver_create_dict(void) } /* note, this function should do nothing most runs, only when changing frame */ -static PyObject *bpy_pydriver_InternStr__frame = NULL; /* not thread safe but neither is python */ -static float bpy_pydriver_evaltime_prev = FLT_MAX; - -static void bpy_pydriver_update_dict(const float evaltime) +static struct { + float evaltime; + + /* borrowed reference to the 'self' in 'bpy_pydriver_Dict' + * keep for as long as the same self is used. */ + PyObject *self; +} g_pydriver_state_prev = { + .evaltime = FLT_MAX, + .self = NULL, +}; + +static void bpy_pydriver_namespace_update_frame(const float evaltime) { - if (bpy_pydriver_evaltime_prev != evaltime) { + if (g_pydriver_state_prev.evaltime != evaltime) { + PyObject *item = PyFloat_FromDouble(evaltime); + PyDict_SetItem(bpy_pydriver_Dict, bpy_intern_str_frame, item); + Py_DECREF(item); - /* currently only update the frame */ - if (bpy_pydriver_InternStr__frame == NULL) { - bpy_pydriver_InternStr__frame = PyUnicode_FromString("frame"); - } + g_pydriver_state_prev.evaltime = evaltime; + } +} - PyObject *item = PyFloat_FromDouble(evaltime); - PyDict_SetItem(bpy_pydriver_Dict, - bpy_pydriver_InternStr__frame, - item); +static void bpy_pydriver_namespace_update_self(struct PathResolvedRNA *anim_rna) +{ + if ((g_pydriver_state_prev.self == NULL) || + (pyrna_driver_is_equal_anim_rna(anim_rna, g_pydriver_state_prev.self) == false)) + { + PyObject *item = pyrna_driver_self_from_anim_rna(anim_rna); + PyDict_SetItem(bpy_pydriver_Dict, bpy_intern_str_self, item); Py_DECREF(item); - bpy_pydriver_evaltime_prev = evaltime; + g_pydriver_state_prev.self = item; + } +} + +static void bpy_pydriver_namespace_clear_self(void) +{ + if (g_pydriver_state_prev.self) { + PyDict_DelItem(bpy_pydriver_Dict, bpy_intern_str_self); + + g_pydriver_state_prev.self = NULL; } } @@ -139,11 +163,10 @@ void BPY_driver_reset(void) bpy_pydriver_Dict = NULL; } - if (bpy_pydriver_InternStr__frame) { - Py_DECREF(bpy_pydriver_InternStr__frame); - bpy_pydriver_InternStr__frame = NULL; - bpy_pydriver_evaltime_prev = FLT_MAX; - } + g_pydriver_state_prev.evaltime = FLT_MAX; + + /* freed when clearing driver dict */ + g_pydriver_state_prev.self = NULL; if (use_gil) PyGILState_Release(gilstate); @@ -174,7 +197,7 @@ static void pydriver_error(ChannelDriver *driver) * now release the GIL on python operator execution instead, using * PyEval_SaveThread() / PyEval_RestoreThread() so we don't lock up blender. */ -float BPY_driver_exec(ChannelDriver *driver, const float evaltime) +float BPY_driver_exec(struct PathResolvedRNA *anim_rna, ChannelDriver *driver, const float evaltime) { PyObject *driver_vars = NULL; PyObject *retval = NULL; @@ -224,8 +247,14 @@ float BPY_driver_exec(ChannelDriver *driver, const float evaltime) } /* update global namespace */ - bpy_pydriver_update_dict(evaltime); + bpy_pydriver_namespace_update_frame(evaltime); + if (driver->flag & DRIVER_FLAG_USE_SELF) { + bpy_pydriver_namespace_update_self(anim_rna); + } + else { + bpy_pydriver_namespace_clear_self(); + } if (driver->expr_comp == NULL) driver->flag |= DRIVER_FLAG_RECOMPILE; @@ -264,7 +293,7 @@ float BPY_driver_exec(ChannelDriver *driver, const float evaltime) } /* add target values to a dict that will be used as '__locals__' dict */ - driver_vars = PyDict_New(); + driver_vars = _PyDict_NewPresized(PyTuple_GET_SIZE(expr_vars)); for (dvar = driver->variables.first, i = 0; dvar; dvar = dvar->next) { PyObject *driver_arg = NULL; diff --git a/source/blender/python/intern/bpy_driver.h b/source/blender/python/intern/bpy_driver.h index 1fccec7e1b2..017a6fe89c5 100644 --- a/source/blender/python/intern/bpy_driver.h +++ b/source/blender/python/intern/bpy_driver.h @@ -28,12 +28,13 @@ #define __BPY_DRIVER_H__ struct ChannelDriver; +struct PathResolvedRNA; int bpy_pydriver_create_dict(void); extern PyObject *bpy_pydriver_Dict; /* externals */ -float BPY_driver_exec(struct ChannelDriver *driver, const float evaltime); +float BPY_driver_exec(struct PathResolvedRNA *anim_rna, struct ChannelDriver *driver, const float evaltime); void BPY_driver_reset(void); #endif /* __BPY_DRIVER_H__ */ diff --git a/source/blender/python/intern/bpy_intern_string.c b/source/blender/python/intern/bpy_intern_string.c index fd32c91a480..ac0100fa75d 100644 --- a/source/blender/python/intern/bpy_intern_string.c +++ b/source/blender/python/intern/bpy_intern_string.c @@ -34,7 +34,7 @@ #include "BLI_utildefines.h" -static PyObject *bpy_intern_str_arr[11]; +static PyObject *bpy_intern_str_arr[13]; PyObject *bpy_intern_str_register; PyObject *bpy_intern_str_unregister; @@ -43,6 +43,8 @@ PyObject *bpy_intern_str_bl_property; PyObject *bpy_intern_str_bpy_types; PyObject *bpy_intern_str_order; PyObject *bpy_intern_str_attr; +PyObject *bpy_intern_str_self; +PyObject *bpy_intern_str_frame; PyObject *bpy_intern_str___slots__; PyObject *bpy_intern_str___name__; PyObject *bpy_intern_str___doc__; @@ -62,6 +64,8 @@ void bpy_intern_string_init(void) BPY_INTERN_STR(bpy_intern_str_bpy_types, "bpy.types"); BPY_INTERN_STR(bpy_intern_str_order, "order"); BPY_INTERN_STR(bpy_intern_str_attr, "attr"); + BPY_INTERN_STR(bpy_intern_str_self, "self"); + BPY_INTERN_STR(bpy_intern_str_frame, "frame"); BPY_INTERN_STR(bpy_intern_str___slots__, "__slots__"); BPY_INTERN_STR(bpy_intern_str___name__, "__name__"); BPY_INTERN_STR(bpy_intern_str___doc__, "__doc__"); diff --git a/source/blender/python/intern/bpy_intern_string.h b/source/blender/python/intern/bpy_intern_string.h index f4f4560dbfd..394e84d89bd 100644 --- a/source/blender/python/intern/bpy_intern_string.h +++ b/source/blender/python/intern/bpy_intern_string.h @@ -37,6 +37,8 @@ extern PyObject *bpy_intern_str_bl_property; extern PyObject *bpy_intern_str_bpy_types; extern PyObject *bpy_intern_str_order; extern PyObject *bpy_intern_str_attr; +extern PyObject *bpy_intern_str_self; +extern PyObject *bpy_intern_str_frame; extern PyObject *bpy_intern_str___slots__; extern PyObject *bpy_intern_str___name__; extern PyObject *bpy_intern_str___doc__; diff --git a/source/blender/python/intern/bpy_library_load.c b/source/blender/python/intern/bpy_library_load.c index 37a7e0e23dd..ec69abbb1df 100644 --- a/source/blender/python/intern/bpy_library_load.c +++ b/source/blender/python/intern/bpy_library_load.c @@ -210,7 +210,7 @@ static PyObject *bpy_lib_load(PyObject *UNUSED(self), PyObject *args, PyObject * ret->flag = ((is_link ? FILE_LINK : 0) | (is_rel ? FILE_RELPATH : 0)); - ret->dict = PyDict_New(); + ret->dict = _PyDict_NewPresized(MAX_LIBARRAY); return (PyObject *)ret; } @@ -240,7 +240,7 @@ static PyObject *bpy_lib_enter(BPy_Library *self, PyObject *UNUSED(args)) { PyObject *ret; BPy_Library *self_from; - PyObject *from_dict = PyDict_New(); + PyObject *from_dict = _PyDict_NewPresized(MAX_LIBARRAY); ReportList reports; BKE_reports_init(&reports, RPT_STORE); diff --git a/source/blender/python/intern/bpy_rna_driver.c b/source/blender/python/intern/bpy_rna_driver.c index 482508a8d85..b4c0de51c04 100644 --- a/source/blender/python/intern/bpy_rna_driver.c +++ b/source/blender/python/intern/bpy_rna_driver.c @@ -77,3 +77,24 @@ PyObject *pyrna_driver_get_variable_value( return driver_arg; } + +PyObject *pyrna_driver_self_from_anim_rna(PathResolvedRNA *anim_rna) +{ + return pyrna_struct_CreatePyObject(&anim_rna->ptr); +} + +bool pyrna_driver_is_equal_anim_rna(const PathResolvedRNA *anim_rna, const PyObject *py_anim_rna) +{ + if (BPy_StructRNA_Check(py_anim_rna)) { + const PointerRNA *ptr_a = &anim_rna->ptr; + const PointerRNA *ptr_b = &(((const BPy_StructRNA *)py_anim_rna)->ptr); + + if ((ptr_a->id.data == ptr_b->id.data) && + (ptr_a->type == ptr_b->type) && + (ptr_a->data == ptr_b->data)) + { + return true; + } + } + return false; +} diff --git a/source/blender/python/intern/bpy_rna_driver.h b/source/blender/python/intern/bpy_rna_driver.h index 8deac2e4384..3f4bf2b9df5 100644 --- a/source/blender/python/intern/bpy_rna_driver.h +++ b/source/blender/python/intern/bpy_rna_driver.h @@ -27,7 +27,11 @@ struct ChannelDriver; struct DriverTarget; +struct PathResolvedRNA; PyObject *pyrna_driver_get_variable_value(struct ChannelDriver *driver, struct DriverTarget *dtar); +PyObject *pyrna_driver_self_from_anim_rna(struct PathResolvedRNA *anim_rna); +bool pyrna_driver_is_equal_anim_rna(const struct PathResolvedRNA *anim_rna, const PyObject *py_anim_rna); + #endif /* __BPY_RNA_DRIVER_H__ */ diff --git a/source/blender/python/intern/bpy_rna_id_collection.c b/source/blender/python/intern/bpy_rna_id_collection.c index 104e3e47646..31189ba4dee 100644 --- a/source/blender/python/intern/bpy_rna_id_collection.c +++ b/source/blender/python/intern/bpy_rna_id_collection.c @@ -198,7 +198,7 @@ static PyObject *bpy_user_map(PyObject *UNUSED(self), PyObject *args, PyObject * PyObject **subset_array = PySequence_Fast_ITEMS(subset_fast); Py_ssize_t subset_len = PySequence_Fast_GET_SIZE(subset_fast); - data_cb.user_map = PyDict_New(); + data_cb.user_map = _PyDict_NewPresized(subset_len); data_cb.is_subset = true; for (; subset_len; subset_array++, subset_len--) { PyObject *set = PySet_New(NULL); diff --git a/source/blender/python/mathutils/mathutils_Vector.c b/source/blender/python/mathutils/mathutils_Vector.c index 7b6fa466fce..afc8a30a6b5 100644 --- a/source/blender/python/mathutils/mathutils_Vector.c +++ b/source/blender/python/mathutils/mathutils_Vector.c @@ -2324,6 +2324,20 @@ static int Vector_swizzle_set(VectorObject *self, PyObject *value, void *closure return 0; } +/* XYZW -> 0123 */ +#define AXIS_FROM_CHAR(a) (((a) != 'W') ? ((a) - 'X') : 3) + +#define _VA_SWIZZLE_1(a) ( \ + ((AXIS_FROM_CHAR(a) | SWIZZLE_VALID_AXIS))) +#define _VA_SWIZZLE_2(a, b) (_VA_SWIZZLE_1(a) | \ + ((AXIS_FROM_CHAR(b) | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS))) +#define _VA_SWIZZLE_3(a, b, c) (_VA_SWIZZLE_2(a, b) | \ + ((AXIS_FROM_CHAR(c) | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))) +#define _VA_SWIZZLE_4(a, b, c, d) (_VA_SWIZZLE_3(a, b, c) | \ + ((AXIS_FROM_CHAR(d) | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) + +#define SWIZZLE(...) SET_INT_IN_POINTER(VA_NARGS_CALL_OVERLOAD(_VA_SWIZZLE_, __VA_ARGS__)) + /*****************************************************************************/ /* Python attributes get/set structure: */ /*****************************************************************************/ @@ -2340,385 +2354,409 @@ static PyGetSetDef Vector_getseters[] = { {(char *)"owner", (getter)BaseMathObject_owner_get, (setter)NULL, BaseMathObject_owner_doc, NULL}, /* autogenerated swizzle attrs, see python script below */ - {(char *)"xx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS)))}, // 36 - {(char *)"xxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 292 - {(char *)"xxxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2340 - {(char *)"xxxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2852 - {(char *)"xxxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3364 - {(char *)"xxxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3876 - {(char *)"xxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 356 - {(char *)"xxyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2404 - {(char *)"xxyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2916 - {(char *)"xxyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3428 - {(char *)"xxyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3940 - {(char *)"xxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 420 - {(char *)"xxzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2468 - {(char *)"xxzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2980 - {(char *)"xxzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3492 - {(char *)"xxzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4004 - {(char *)"xxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 484 - {(char *)"xxwx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2532 - {(char *)"xxwy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3044 - {(char *)"xxwz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3556 - {(char *)"xxww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4068 - {(char *)"xy", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS)))}, // 44 - {(char *)"xyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 300 - {(char *)"xyxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2348 - {(char *)"xyxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2860 - {(char *)"xyxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3372 - {(char *)"xyxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3884 - {(char *)"xyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 364 - {(char *)"xyyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2412 - {(char *)"xyyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2924 - {(char *)"xyyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3436 - {(char *)"xyyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3948 - {(char *)"xyz", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 428 - {(char *)"xyzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2476 - {(char *)"xyzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2988 - {(char *)"xyzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3500 - {(char *)"xyzw", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4012 - {(char *)"xyw", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 492 - {(char *)"xywx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2540 - {(char *)"xywy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3052 - {(char *)"xywz", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3564 - {(char *)"xyww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4076 - {(char *)"xz", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS)))}, // 52 - {(char *)"xzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 308 - {(char *)"xzxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2356 - {(char *)"xzxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2868 - {(char *)"xzxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3380 - {(char *)"xzxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3892 - {(char *)"xzy", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 372 - {(char *)"xzyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2420 - {(char *)"xzyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2932 - {(char *)"xzyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3444 - {(char *)"xzyw", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3956 - {(char *)"xzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 436 - {(char *)"xzzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2484 - {(char *)"xzzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2996 - {(char *)"xzzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3508 - {(char *)"xzzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4020 - {(char *)"xzw", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 500 - {(char *)"xzwx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2548 - {(char *)"xzwy", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3060 - {(char *)"xzwz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3572 - {(char *)"xzww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4084 - {(char *)"xw", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS)))}, // 60 - {(char *)"xwx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 316 - {(char *)"xwxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2364 - {(char *)"xwxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2876 - {(char *)"xwxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3388 - {(char *)"xwxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3900 - {(char *)"xwy", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 380 - {(char *)"xwyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2428 - {(char *)"xwyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2940 - {(char *)"xwyz", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3452 - {(char *)"xwyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3964 - {(char *)"xwz", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 444 - {(char *)"xwzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2492 - {(char *)"xwzy", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3004 - {(char *)"xwzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3516 - {(char *)"xwzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4028 - {(char *)"xww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 508 - {(char *)"xwwx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2556 - {(char *)"xwwy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3068 - {(char *)"xwwz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3580 - {(char *)"xwww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4092 - {(char *)"yx", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS)))}, // 37 - {(char *)"yxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 293 - {(char *)"yxxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2341 - {(char *)"yxxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2853 - {(char *)"yxxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3365 - {(char *)"yxxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3877 - {(char *)"yxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 357 - {(char *)"yxyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2405 - {(char *)"yxyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2917 - {(char *)"yxyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3429 - {(char *)"yxyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3941 - {(char *)"yxz", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 421 - {(char *)"yxzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2469 - {(char *)"yxzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2981 - {(char *)"yxzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3493 - {(char *)"yxzw", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4005 - {(char *)"yxw", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 485 - {(char *)"yxwx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2533 - {(char *)"yxwy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3045 - {(char *)"yxwz", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3557 - {(char *)"yxww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4069 - {(char *)"yy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS)))}, // 45 - {(char *)"yyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 301 - {(char *)"yyxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2349 - {(char *)"yyxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2861 - {(char *)"yyxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3373 - {(char *)"yyxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3885 - {(char *)"yyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 365 - {(char *)"yyyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2413 - {(char *)"yyyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2925 - {(char *)"yyyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3437 - {(char *)"yyyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3949 - {(char *)"yyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 429 - {(char *)"yyzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2477 - {(char *)"yyzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2989 - {(char *)"yyzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3501 - {(char *)"yyzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4013 - {(char *)"yyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 493 - {(char *)"yywx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2541 - {(char *)"yywy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3053 - {(char *)"yywz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3565 - {(char *)"yyww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4077 - {(char *)"yz", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS)))}, // 53 - {(char *)"yzx", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 309 - {(char *)"yzxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2357 - {(char *)"yzxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2869 - {(char *)"yzxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3381 - {(char *)"yzxw", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3893 - {(char *)"yzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 373 - {(char *)"yzyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2421 - {(char *)"yzyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2933 - {(char *)"yzyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3445 - {(char *)"yzyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3957 - {(char *)"yzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 437 - {(char *)"yzzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2485 - {(char *)"yzzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2997 - {(char *)"yzzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3509 - {(char *)"yzzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4021 - {(char *)"yzw", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 501 - {(char *)"yzwx", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2549 - {(char *)"yzwy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3061 - {(char *)"yzwz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3573 - {(char *)"yzww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4085 - {(char *)"yw", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS)))}, // 61 - {(char *)"ywx", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 317 - {(char *)"ywxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2365 - {(char *)"ywxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2877 - {(char *)"ywxz", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3389 - {(char *)"ywxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3901 - {(char *)"ywy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 381 - {(char *)"ywyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2429 - {(char *)"ywyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2941 - {(char *)"ywyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3453 - {(char *)"ywyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3965 - {(char *)"ywz", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 445 - {(char *)"ywzx", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2493 - {(char *)"ywzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3005 - {(char *)"ywzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3517 - {(char *)"ywzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4029 - {(char *)"yww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 509 - {(char *)"ywwx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2557 - {(char *)"ywwy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3069 - {(char *)"ywwz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3581 - {(char *)"ywww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4093 - {(char *)"zx", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS)))}, // 38 - {(char *)"zxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 294 - {(char *)"zxxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2342 - {(char *)"zxxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2854 - {(char *)"zxxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3366 - {(char *)"zxxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3878 - {(char *)"zxy", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 358 - {(char *)"zxyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2406 - {(char *)"zxyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2918 - {(char *)"zxyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3430 - {(char *)"zxyw", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3942 - {(char *)"zxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 422 - {(char *)"zxzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2470 - {(char *)"zxzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2982 - {(char *)"zxzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3494 - {(char *)"zxzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4006 - {(char *)"zxw", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 486 - {(char *)"zxwx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2534 - {(char *)"zxwy", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3046 - {(char *)"zxwz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3558 - {(char *)"zxww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4070 - {(char *)"zy", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS)))}, // 46 - {(char *)"zyx", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 302 - {(char *)"zyxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2350 - {(char *)"zyxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2862 - {(char *)"zyxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3374 - {(char *)"zyxw", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3886 - {(char *)"zyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 366 - {(char *)"zyyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2414 - {(char *)"zyyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2926 - {(char *)"zyyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3438 - {(char *)"zyyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3950 - {(char *)"zyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 430 - {(char *)"zyzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2478 - {(char *)"zyzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2990 - {(char *)"zyzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3502 - {(char *)"zyzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4014 - {(char *)"zyw", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 494 - {(char *)"zywx", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2542 - {(char *)"zywy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3054 - {(char *)"zywz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3566 - {(char *)"zyww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4078 - {(char *)"zz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS)))}, // 54 - {(char *)"zzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 310 - {(char *)"zzxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2358 - {(char *)"zzxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2870 - {(char *)"zzxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3382 - {(char *)"zzxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3894 - {(char *)"zzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 374 - {(char *)"zzyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2422 - {(char *)"zzyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2934 - {(char *)"zzyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3446 - {(char *)"zzyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3958 - {(char *)"zzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 438 - {(char *)"zzzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2486 - {(char *)"zzzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2998 - {(char *)"zzzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3510 - {(char *)"zzzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4022 - {(char *)"zzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 502 - {(char *)"zzwx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2550 - {(char *)"zzwy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3062 - {(char *)"zzwz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3574 - {(char *)"zzww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4086 - {(char *)"zw", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS)))}, // 62 - {(char *)"zwx", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 318 - {(char *)"zwxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2366 - {(char *)"zwxy", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2878 - {(char *)"zwxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3390 - {(char *)"zwxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3902 - {(char *)"zwy", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 382 - {(char *)"zwyx", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2430 - {(char *)"zwyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2942 - {(char *)"zwyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3454 - {(char *)"zwyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3966 - {(char *)"zwz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 446 - {(char *)"zwzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2494 - {(char *)"zwzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3006 - {(char *)"zwzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3518 - {(char *)"zwzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4030 - {(char *)"zww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 510 - {(char *)"zwwx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2558 - {(char *)"zwwy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3070 - {(char *)"zwwz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3582 - {(char *)"zwww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4094 - {(char *)"wx", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS)))}, // 39 - {(char *)"wxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 295 - {(char *)"wxxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2343 - {(char *)"wxxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2855 - {(char *)"wxxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3367 - {(char *)"wxxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3879 - {(char *)"wxy", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 359 - {(char *)"wxyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2407 - {(char *)"wxyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2919 - {(char *)"wxyz", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3431 - {(char *)"wxyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3943 - {(char *)"wxz", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 423 - {(char *)"wxzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2471 - {(char *)"wxzy", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2983 - {(char *)"wxzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3495 - {(char *)"wxzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4007 - {(char *)"wxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 487 - {(char *)"wxwx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2535 - {(char *)"wxwy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3047 - {(char *)"wxwz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3559 - {(char *)"wxww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4071 - {(char *)"wy", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS)))}, // 47 - {(char *)"wyx", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 303 - {(char *)"wyxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2351 - {(char *)"wyxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2863 - {(char *)"wyxz", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3375 - {(char *)"wyxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3887 - {(char *)"wyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 367 - {(char *)"wyyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2415 - {(char *)"wyyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2927 - {(char *)"wyyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3439 - {(char *)"wyyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3951 - {(char *)"wyz", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 431 - {(char *)"wyzx", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2479 - {(char *)"wyzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2991 - {(char *)"wyzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3503 - {(char *)"wyzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4015 - {(char *)"wyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 495 - {(char *)"wywx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2543 - {(char *)"wywy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3055 - {(char *)"wywz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3567 - {(char *)"wyww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4079 - {(char *)"wz", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS)))}, // 55 - {(char *)"wzx", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 311 - {(char *)"wzxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2359 - {(char *)"wzxy", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2871 - {(char *)"wzxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3383 - {(char *)"wzxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3895 - {(char *)"wzy", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 375 - {(char *)"wzyx", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2423 - {(char *)"wzyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2935 - {(char *)"wzyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3447 - {(char *)"wzyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3959 - {(char *)"wzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 439 - {(char *)"wzzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2487 - {(char *)"wzzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2999 - {(char *)"wzzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3511 - {(char *)"wzzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4023 - {(char *)"wzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 503 - {(char *)"wzwx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2551 - {(char *)"wzwy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3063 - {(char *)"wzwz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3575 - {(char *)"wzww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4087 - {(char *)"ww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS)))}, // 63 - {(char *)"wwx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 319 - {(char *)"wwxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2367 - {(char *)"wwxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2879 - {(char *)"wwxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3391 - {(char *)"wwxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3903 - {(char *)"wwy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 383 - {(char *)"wwyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2431 - {(char *)"wwyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2943 - {(char *)"wwyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3455 - {(char *)"wwyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3967 - {(char *)"wwz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 447 - {(char *)"wwzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2495 - {(char *)"wwzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3007 - {(char *)"wwzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3519 - {(char *)"wwzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4031 - {(char *)"www", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 511 - {(char *)"wwwx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2559 - {(char *)"wwwy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3071 - {(char *)"wwwz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3583 - {(char *)"wwww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4095 + {(char *)"xx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'X')}, + {(char *)"xxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'X', 'X')}, + {(char *)"xxxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'X', 'X', 'X')}, + {(char *)"xxxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'X', 'X', 'Y')}, + {(char *)"xxxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'X', 'X', 'Z')}, + {(char *)"xxxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'X', 'X', 'W')}, + {(char *)"xxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'X', 'Y')}, + {(char *)"xxyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'X', 'Y', 'X')}, + {(char *)"xxyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'X', 'Y', 'Y')}, + {(char *)"xxyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'X', 'Y', 'Z')}, + {(char *)"xxyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'X', 'Y', 'W')}, + {(char *)"xxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'X', 'Z')}, + {(char *)"xxzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'X', 'Z', 'X')}, + {(char *)"xxzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'X', 'Z', 'Y')}, + {(char *)"xxzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'X', 'Z', 'Z')}, + {(char *)"xxzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'X', 'Z', 'W')}, + {(char *)"xxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'X', 'W')}, + {(char *)"xxwx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'X', 'W', 'X')}, + {(char *)"xxwy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'X', 'W', 'Y')}, + {(char *)"xxwz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'X', 'W', 'Z')}, + {(char *)"xxww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'X', 'W', 'W')}, + {(char *)"xy", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('X', 'Y')}, + {(char *)"xyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Y', 'X')}, + {(char *)"xyxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Y', 'X', 'X')}, + {(char *)"xyxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Y', 'X', 'Y')}, + {(char *)"xyxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Y', 'X', 'Z')}, + {(char *)"xyxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Y', 'X', 'W')}, + {(char *)"xyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Y', 'Y')}, + {(char *)"xyyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Y', 'Y', 'X')}, + {(char *)"xyyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Y', 'Y', 'Y')}, + {(char *)"xyyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Y', 'Y', 'Z')}, + {(char *)"xyyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Y', 'Y', 'W')}, + {(char *)"xyz", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('X', 'Y', 'Z')}, + {(char *)"xyzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Y', 'Z', 'X')}, + {(char *)"xyzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Y', 'Z', 'Y')}, + {(char *)"xyzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Y', 'Z', 'Z')}, + {(char *)"xyzw", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('X', 'Y', 'Z', 'W')}, + {(char *)"xyw", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('X', 'Y', 'W')}, + {(char *)"xywx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Y', 'W', 'X')}, + {(char *)"xywy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Y', 'W', 'Y')}, + {(char *)"xywz", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('X', 'Y', 'W', 'Z')}, + {(char *)"xyww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Y', 'W', 'W')}, + {(char *)"xz", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('X', 'Z')}, + {(char *)"xzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Z', 'X')}, + {(char *)"xzxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Z', 'X', 'X')}, + {(char *)"xzxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Z', 'X', 'Y')}, + {(char *)"xzxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Z', 'X', 'Z')}, + {(char *)"xzxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Z', 'X', 'W')}, + {(char *)"xzy", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('X', 'Z', 'Y')}, + {(char *)"xzyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Z', 'Y', 'X')}, + {(char *)"xzyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Z', 'Y', 'Y')}, + {(char *)"xzyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Z', 'Y', 'Z')}, + {(char *)"xzyw", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('X', 'Z', 'Y', 'W')}, + {(char *)"xzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Z', 'Z')}, + {(char *)"xzzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Z', 'Z', 'X')}, + {(char *)"xzzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Z', 'Z', 'Y')}, + {(char *)"xzzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Z', 'Z', 'Z')}, + {(char *)"xzzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Z', 'Z', 'W')}, + {(char *)"xzw", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('X', 'Z', 'W')}, + {(char *)"xzwx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Z', 'W', 'X')}, + {(char *)"xzwy", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('X', 'Z', 'W', 'Y')}, + {(char *)"xzwz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Z', 'W', 'Z')}, + {(char *)"xzww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Z', 'W', 'W')}, + {(char *)"xw", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('X', 'W')}, + {(char *)"xwx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'W', 'X')}, + {(char *)"xwxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'W', 'X', 'X')}, + {(char *)"xwxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'W', 'X', 'Y')}, + {(char *)"xwxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'W', 'X', 'Z')}, + {(char *)"xwxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'W', 'X', 'W')}, + {(char *)"xwy", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('X', 'W', 'Y')}, + {(char *)"xwyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'W', 'Y', 'X')}, + {(char *)"xwyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'W', 'Y', 'Y')}, + {(char *)"xwyz", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('X', 'W', 'Y', 'Z')}, + {(char *)"xwyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'W', 'Y', 'W')}, + {(char *)"xwz", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('X', 'W', 'Z')}, + {(char *)"xwzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'W', 'Z', 'X')}, + {(char *)"xwzy", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('X', 'W', 'Z', 'Y')}, + {(char *)"xwzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'W', 'Z', 'Z')}, + {(char *)"xwzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'W', 'Z', 'W')}, + {(char *)"xww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'W', 'W')}, + {(char *)"xwwx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'W', 'W', 'X')}, + {(char *)"xwwy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'W', 'W', 'Y')}, + {(char *)"xwwz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'W', 'W', 'Z')}, + {(char *)"xwww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'W', 'W', 'W')}, + {(char *)"yx", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('Y', 'X')}, + {(char *)"yxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'X', 'X')}, + {(char *)"yxxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'X', 'X', 'X')}, + {(char *)"yxxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'X', 'X', 'Y')}, + {(char *)"yxxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'X', 'X', 'Z')}, + {(char *)"yxxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'X', 'X', 'W')}, + {(char *)"yxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'X', 'Y')}, + {(char *)"yxyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'X', 'Y', 'X')}, + {(char *)"yxyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'X', 'Y', 'Y')}, + {(char *)"yxyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'X', 'Y', 'Z')}, + {(char *)"yxyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'X', 'Y', 'W')}, + {(char *)"yxz", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('Y', 'X', 'Z')}, + {(char *)"yxzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'X', 'Z', 'X')}, + {(char *)"yxzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'X', 'Z', 'Y')}, + {(char *)"yxzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'X', 'Z', 'Z')}, + {(char *)"yxzw", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('Y', 'X', 'Z', 'W')}, + {(char *)"yxw", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('Y', 'X', 'W')}, + {(char *)"yxwx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'X', 'W', 'X')}, + {(char *)"yxwy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'X', 'W', 'Y')}, + {(char *)"yxwz", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('Y', 'X', 'W', 'Z')}, + {(char *)"yxww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'X', 'W', 'W')}, + {(char *)"yy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Y')}, + {(char *)"yyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Y', 'X')}, + {(char *)"yyxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Y', 'X', 'X')}, + {(char *)"yyxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Y', 'X', 'Y')}, + {(char *)"yyxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Y', 'X', 'Z')}, + {(char *)"yyxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Y', 'X', 'W')}, + {(char *)"yyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Y', 'Y')}, + {(char *)"yyyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Y', 'Y', 'X')}, + {(char *)"yyyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Y', 'Y', 'Y')}, + {(char *)"yyyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Y', 'Y', 'Z')}, + {(char *)"yyyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Y', 'Y', 'W')}, + {(char *)"yyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Y', 'Z')}, + {(char *)"yyzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Y', 'Z', 'X')}, + {(char *)"yyzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Y', 'Z', 'Y')}, + {(char *)"yyzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Y', 'Z', 'Z')}, + {(char *)"yyzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Y', 'Z', 'W')}, + {(char *)"yyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Y', 'W')}, + {(char *)"yywx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Y', 'W', 'X')}, + {(char *)"yywy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Y', 'W', 'Y')}, + {(char *)"yywz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Y', 'W', 'Z')}, + {(char *)"yyww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Y', 'W', 'W')}, + {(char *)"yz", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('Y', 'Z')}, + {(char *)"yzx", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('Y', 'Z', 'X')}, + {(char *)"yzxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Z', 'X', 'X')}, + {(char *)"yzxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Z', 'X', 'Y')}, + {(char *)"yzxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Z', 'X', 'Z')}, + {(char *)"yzxw", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('Y', 'Z', 'X', 'W')}, + {(char *)"yzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Z', 'Y')}, + {(char *)"yzyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Z', 'Y', 'X')}, + {(char *)"yzyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Z', 'Y', 'Y')}, + {(char *)"yzyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Z', 'Y', 'Z')}, + {(char *)"yzyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Z', 'Y', 'W')}, + {(char *)"yzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Z', 'Z')}, + {(char *)"yzzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Z', 'Z', 'X')}, + {(char *)"yzzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Z', 'Z', 'Y')}, + {(char *)"yzzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Z', 'Z', 'Z')}, + {(char *)"yzzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Z', 'Z', 'W')}, + {(char *)"yzw", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('Y', 'Z', 'W')}, + {(char *)"yzwx", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('Y', 'Z', 'W', 'X')}, + {(char *)"yzwy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Z', 'W', 'Y')}, + {(char *)"yzwz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Z', 'W', 'Z')}, + {(char *)"yzww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Z', 'W', 'W')}, + {(char *)"yw", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('Y', 'W')}, + {(char *)"ywx", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('Y', 'W', 'X')}, + {(char *)"ywxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'W', 'X', 'X')}, + {(char *)"ywxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'W', 'X', 'Y')}, + {(char *)"ywxz", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('Y', 'W', 'X', 'Z')}, + {(char *)"ywxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'W', 'X', 'W')}, + {(char *)"ywy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'W', 'Y')}, + {(char *)"ywyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'W', 'Y', 'X')}, + {(char *)"ywyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'W', 'Y', 'Y')}, + {(char *)"ywyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'W', 'Y', 'Z')}, + {(char *)"ywyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'W', 'Y', 'W')}, + {(char *)"ywz", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('Y', 'W', 'Z')}, + {(char *)"ywzx", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('Y', 'W', 'Z', 'X')}, + {(char *)"ywzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'W', 'Z', 'Y')}, + {(char *)"ywzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'W', 'Z', 'Z')}, + {(char *)"ywzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'W', 'Z', 'W')}, + {(char *)"yww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'W', 'W')}, + {(char *)"ywwx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'W', 'W', 'X')}, + {(char *)"ywwy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'W', 'W', 'Y')}, + {(char *)"ywwz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'W', 'W', 'Z')}, + {(char *)"ywww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'W', 'W', 'W')}, + {(char *)"zx", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('Z', 'X')}, + {(char *)"zxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'X', 'X')}, + {(char *)"zxxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'X', 'X', 'X')}, + {(char *)"zxxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'X', 'X', 'Y')}, + {(char *)"zxxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'X', 'X', 'Z')}, + {(char *)"zxxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'X', 'X', 'W')}, + {(char *)"zxy", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('Z', 'X', 'Y')}, + {(char *)"zxyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'X', 'Y', 'X')}, + {(char *)"zxyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'X', 'Y', 'Y')}, + {(char *)"zxyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'X', 'Y', 'Z')}, + {(char *)"zxyw", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('Z', 'X', 'Y', 'W')}, + {(char *)"zxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'X', 'Z')}, + {(char *)"zxzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'X', 'Z', 'X')}, + {(char *)"zxzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'X', 'Z', 'Y')}, + {(char *)"zxzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'X', 'Z', 'Z')}, + {(char *)"zxzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'X', 'Z', 'W')}, + {(char *)"zxw", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('Z', 'X', 'W')}, + {(char *)"zxwx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'X', 'W', 'X')}, + {(char *)"zxwy", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('Z', 'X', 'W', 'Y')}, + {(char *)"zxwz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'X', 'W', 'Z')}, + {(char *)"zxww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'X', 'W', 'W')}, + {(char *)"zy", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('Z', 'Y')}, + {(char *)"zyx", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('Z', 'Y', 'X')}, + {(char *)"zyxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Y', 'X', 'X')}, + {(char *)"zyxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Y', 'X', 'Y')}, + {(char *)"zyxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Y', 'X', 'Z')}, + {(char *)"zyxw", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('Z', 'Y', 'X', 'W')}, + {(char *)"zyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Y', 'Y')}, + {(char *)"zyyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Y', 'Y', 'X')}, + {(char *)"zyyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Y', 'Y', 'Y')}, + {(char *)"zyyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Y', 'Y', 'Z')}, + {(char *)"zyyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Y', 'Y', 'W')}, + {(char *)"zyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Y', 'Z')}, + {(char *)"zyzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Y', 'Z', 'X')}, + {(char *)"zyzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Y', 'Z', 'Y')}, + {(char *)"zyzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Y', 'Z', 'Z')}, + {(char *)"zyzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Y', 'Z', 'W')}, + {(char *)"zyw", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('Z', 'Y', 'W')}, + {(char *)"zywx", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('Z', 'Y', 'W', 'X')}, + {(char *)"zywy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Y', 'W', 'Y')}, + {(char *)"zywz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Y', 'W', 'Z')}, + {(char *)"zyww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Y', 'W', 'W')}, + {(char *)"zz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Z')}, + {(char *)"zzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Z', 'X')}, + {(char *)"zzxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Z', 'X', 'X')}, + {(char *)"zzxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Z', 'X', 'Y')}, + {(char *)"zzxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Z', 'X', 'Z')}, + {(char *)"zzxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Z', 'X', 'W')}, + {(char *)"zzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Z', 'Y')}, + {(char *)"zzyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Z', 'Y', 'X')}, + {(char *)"zzyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Z', 'Y', 'Y')}, + {(char *)"zzyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Z', 'Y', 'Z')}, + {(char *)"zzyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Z', 'Y', 'W')}, + {(char *)"zzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Z', 'Z')}, + {(char *)"zzzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Z', 'Z', 'X')}, + {(char *)"zzzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Z', 'Z', 'Y')}, + {(char *)"zzzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Z', 'Z', 'Z')}, + {(char *)"zzzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Z', 'Z', 'W')}, + {(char *)"zzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Z', 'W')}, + {(char *)"zzwx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Z', 'W', 'X')}, + {(char *)"zzwy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Z', 'W', 'Y')}, + {(char *)"zzwz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Z', 'W', 'Z')}, + {(char *)"zzww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Z', 'W', 'W')}, + {(char *)"zw", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('Z', 'W')}, + {(char *)"zwx", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('Z', 'W', 'X')}, + {(char *)"zwxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'W', 'X', 'X')}, + {(char *)"zwxy", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('Z', 'W', 'X', 'Y')}, + {(char *)"zwxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'W', 'X', 'Z')}, + {(char *)"zwxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'W', 'X', 'W')}, + {(char *)"zwy", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('Z', 'W', 'Y')}, + {(char *)"zwyx", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('Z', 'W', 'Y', 'X')}, + {(char *)"zwyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'W', 'Y', 'Y')}, + {(char *)"zwyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'W', 'Y', 'Z')}, + {(char *)"zwyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'W', 'Y', 'W')}, + {(char *)"zwz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'W', 'Z')}, + {(char *)"zwzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'W', 'Z', 'X')}, + {(char *)"zwzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'W', 'Z', 'Y')}, + {(char *)"zwzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'W', 'Z', 'Z')}, + {(char *)"zwzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'W', 'Z', 'W')}, + {(char *)"zww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'W', 'W')}, + {(char *)"zwwx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'W', 'W', 'X')}, + {(char *)"zwwy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'W', 'W', 'Y')}, + {(char *)"zwwz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'W', 'W', 'Z')}, + {(char *)"zwww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'W', 'W', 'W')}, + {(char *)"wx", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('W', 'X')}, + {(char *)"wxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'X', 'X')}, + {(char *)"wxxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'X', 'X', 'X')}, + {(char *)"wxxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'X', 'X', 'Y')}, + {(char *)"wxxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'X', 'X', 'Z')}, + {(char *)"wxxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'X', 'X', 'W')}, + {(char *)"wxy", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('W', 'X', 'Y')}, + {(char *)"wxyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'X', 'Y', 'X')}, + {(char *)"wxyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'X', 'Y', 'Y')}, + {(char *)"wxyz", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('W', 'X', 'Y', 'Z')}, + {(char *)"wxyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'X', 'Y', 'W')}, + {(char *)"wxz", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('W', 'X', 'Z')}, + {(char *)"wxzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'X', 'Z', 'X')}, + {(char *)"wxzy", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('W', 'X', 'Z', 'Y')}, + {(char *)"wxzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'X', 'Z', 'Z')}, + {(char *)"wxzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'X', 'Z', 'W')}, + {(char *)"wxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'X', 'W')}, + {(char *)"wxwx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'X', 'W', 'X')}, + {(char *)"wxwy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'X', 'W', 'Y')}, + {(char *)"wxwz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'X', 'W', 'Z')}, + {(char *)"wxww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'X', 'W', 'W')}, + {(char *)"wy", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('W', 'Y')}, + {(char *)"wyx", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('W', 'Y', 'X')}, + {(char *)"wyxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Y', 'X', 'X')}, + {(char *)"wyxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Y', 'X', 'Y')}, + {(char *)"wyxz", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('W', 'Y', 'X', 'Z')}, + {(char *)"wyxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Y', 'X', 'W')}, + {(char *)"wyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Y', 'Y')}, + {(char *)"wyyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Y', 'Y', 'X')}, + {(char *)"wyyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Y', 'Y', 'Y')}, + {(char *)"wyyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Y', 'Y', 'Z')}, + {(char *)"wyyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Y', 'Y', 'W')}, + {(char *)"wyz", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('W', 'Y', 'Z')}, + {(char *)"wyzx", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('W', 'Y', 'Z', 'X')}, + {(char *)"wyzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Y', 'Z', 'Y')}, + {(char *)"wyzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Y', 'Z', 'Z')}, + {(char *)"wyzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Y', 'Z', 'W')}, + {(char *)"wyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Y', 'W')}, + {(char *)"wywx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Y', 'W', 'X')}, + {(char *)"wywy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Y', 'W', 'Y')}, + {(char *)"wywz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Y', 'W', 'Z')}, + {(char *)"wyww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Y', 'W', 'W')}, + {(char *)"wz", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('W', 'Z')}, + {(char *)"wzx", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('W', 'Z', 'X')}, + {(char *)"wzxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Z', 'X', 'X')}, + {(char *)"wzxy", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('W', 'Z', 'X', 'Y')}, + {(char *)"wzxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Z', 'X', 'Z')}, + {(char *)"wzxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Z', 'X', 'W')}, + {(char *)"wzy", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('W', 'Z', 'Y')}, + {(char *)"wzyx", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('W', 'Z', 'Y', 'X')}, + {(char *)"wzyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Z', 'Y', 'Y')}, + {(char *)"wzyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Z', 'Y', 'Z')}, + {(char *)"wzyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Z', 'Y', 'W')}, + {(char *)"wzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Z', 'Z')}, + {(char *)"wzzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Z', 'Z', 'X')}, + {(char *)"wzzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Z', 'Z', 'Y')}, + {(char *)"wzzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Z', 'Z', 'Z')}, + {(char *)"wzzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Z', 'Z', 'W')}, + {(char *)"wzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Z', 'W')}, + {(char *)"wzwx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Z', 'W', 'X')}, + {(char *)"wzwy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Z', 'W', 'Y')}, + {(char *)"wzwz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Z', 'W', 'Z')}, + {(char *)"wzww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Z', 'W', 'W')}, + {(char *)"ww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'W')}, + {(char *)"wwx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'W', 'X')}, + {(char *)"wwxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'W', 'X', 'X')}, + {(char *)"wwxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'W', 'X', 'Y')}, + {(char *)"wwxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'W', 'X', 'Z')}, + {(char *)"wwxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'W', 'X', 'W')}, + {(char *)"wwy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'W', 'Y')}, + {(char *)"wwyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'W', 'Y', 'X')}, + {(char *)"wwyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'W', 'Y', 'Y')}, + {(char *)"wwyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'W', 'Y', 'Z')}, + {(char *)"wwyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'W', 'Y', 'W')}, + {(char *)"wwz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'W', 'Z')}, + {(char *)"wwzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'W', 'Z', 'X')}, + {(char *)"wwzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'W', 'Z', 'Y')}, + {(char *)"wwzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'W', 'Z', 'Z')}, + {(char *)"wwzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'W', 'Z', 'W')}, + {(char *)"www", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'W', 'W')}, + {(char *)"wwwx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'W', 'W', 'X')}, + {(char *)"wwwy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'W', 'W', 'Y')}, + {(char *)"wwwz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'W', 'W', 'Z')}, + {(char *)"wwww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'W', 'W', 'W')}, + +#undef AXIS_FROM_CHAR +#undef SWIZZLE +#undef _VA_SWIZZLE_1 +#undef _VA_SWIZZLE_2 +#undef _VA_SWIZZLE_3 +#undef _VA_SWIZZLE_4 + {NULL, NULL, NULL, NULL, NULL} /* Sentinel */ }; -/* Python script used to make swizzle array */ -/* -SWIZZLE_BITS_PER_AXIS = 3 -SWIZZLE_VALID_AXIS = 0x4 - -axis_dict = {} -axis_pos = {'x':0, 'y':1, 'z':2, 'w':3} -axises = 'xyzw' -while len(axises) >= 2: - - for axis_0 in axises: - axis_0_pos = axis_pos[axis_0] - for axis_1 in axises: - axis_1_pos = axis_pos[axis_1] - axis_dict[axis_0 + axis_1] = '((%s|SWIZZLE_VALID_AXIS) | ((%s|SWIZZLE_VALID_AXIS)<<SWIZZLE_BITS_PER_AXIS))' % (axis_0_pos, axis_1_pos) - if len(axises)>2: - for axis_2 in axises: - axis_2_pos = axis_pos[axis_2] - axis_dict[axis_0 + axis_1 + axis_2] = '((%s|SWIZZLE_VALID_AXIS) | ((%s|SWIZZLE_VALID_AXIS)<<SWIZZLE_BITS_PER_AXIS) | ((%s|SWIZZLE_VALID_AXIS)<<(SWIZZLE_BITS_PER_AXIS * 2)))' % (axis_0_pos, axis_1_pos, axis_2_pos) - if len(axises)>3: - for axis_3 in axises: - axis_3_pos = axis_pos[axis_3] - axis_dict[axis_0 + axis_1 + axis_2 + axis_3] = '((%s|SWIZZLE_VALID_AXIS) | ((%s|SWIZZLE_VALID_AXIS)<<SWIZZLE_BITS_PER_AXIS) | ((%s|SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((%s|SWIZZLE_VALID_AXIS)<<(SWIZZLE_BITS_PER_AXIS * 3))) ' % (axis_0_pos, axis_1_pos, axis_2_pos, axis_3_pos) - - axises = axises[:-1] - - -items = axis_dict.items() -items.sort(key = lambda a: a[0].replace('x', '0').replace('y', '1').replace('z', '2').replace('w', '3')) - -unique = set() -for key, val in items: - num = eval(val) - set_str = 'Vector_setSwizzle' if (len(set(key)) == len(key)) else 'NULL' - print '\t{"%s", %s(getter)Vector_getSwizzle, (setter)%s, NULL, SET_INT_IN_POINTER(%s)}, // %s' % (key, (' '*(4 - len(key))), set_str, axis_dict[key], num) - unique.add(num) - -if len(unique) != len(items): - print "ERROR" -*/ +/** + * Python script used to make swizzle array: + * + * \code{.py} + * SWIZZLE_BITS_PER_AXIS = 3 + * SWIZZLE_VALID_AXIS = 0x4 + * + * axis_dict = {} + * axis_pos = {'x': 0, 'y': 1, 'z': 2, 'w': 3} + * axises = 'xyzw' + * while len(axises) >= 2: + * for axis_0 in axises: + * axis_0_pos = axis_pos[axis_0] + * for axis_1 in axises: + * axis_1_pos = axis_pos[axis_1] + * axis_dict[axis_0 + axis_1] = ( + * '((%s | SWIZZLE_VALID_AXIS) | ' + * '((%s | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS))' % + * (axis_0_pos, axis_1_pos)) + * if len(axises) > 2: + * for axis_2 in axises: + * axis_2_pos = axis_pos[axis_2] + * axis_dict[axis_0 + axis_1 + axis_2] = ( + * '((%s | SWIZZLE_VALID_AXIS) | ' + * '((%s | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ' + * '((%s | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)))' % + * (axis_0_pos, axis_1_pos, axis_2_pos)) + * if len(axises) > 3: + * for axis_3 in axises: + * axis_3_pos = axis_pos[axis_3] + * axis_dict[axis_0 + axis_1 + axis_2 + axis_3] = ( + * '((%s | SWIZZLE_VALID_AXIS) | ' + * '((%s | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ' + * '((%s | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ' + * '((%s | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) ' % + * (axis_0_pos, axis_1_pos, axis_2_pos, axis_3_pos)) + * + * axises = axises[:-1] + * + * + * items = list(axis_dict.items()) + * items.sort(key=lambda a: a[0].replace('x', '0').replace('y', '1').replace('z', '2').replace('w', '3')) + * + * unique = set() + * for key, val in items: + * num = eval(val) + * set_str = 'Vector_swizzle_set' if (len(set(key)) == len(key)) else 'NULL' + * key_args = ', '.join(["'%s'" % c for c in key.upper()]) + * print('\t{(char *)"%s", %s(getter)Vector_swizzle_get, (setter)%s, NULL, SWIZZLE(%s)},' % + * (key, (' ' * (4 - len(key))), set_str, key_args)) + * unique.add(num) + * + * if len(unique) != len(items): + * print("ERROR, duplicate values found") + * \endcode + */ /* ROW VECTOR Multiplication - Vector X Matrix * [x][y][z] * [1][4][7] diff --git a/source/blender/render/extern/include/RE_pipeline.h b/source/blender/render/extern/include/RE_pipeline.h index 39f62f9fc33..509ad6f3515 100644 --- a/source/blender/render/extern/include/RE_pipeline.h +++ b/source/blender/render/extern/include/RE_pipeline.h @@ -238,6 +238,9 @@ void RE_render_result_rect_from_ibuf( struct RenderLayer *RE_GetRenderLayer(struct RenderResult *rr, const char *name); float *RE_RenderLayerGetPass(volatile struct RenderLayer *rl, int passtype, const char *viewname); +/* add passes for grease pencil */ +struct RenderPass *RE_create_gp_pass(struct RenderResult *rr, const char *layername, const char *viewname); + /* obligatory initialize call, disprect is optional */ void RE_InitState(struct Render *re, struct Render *source, struct RenderData *rd, struct SceneRenderLayer *srl, diff --git a/source/blender/render/intern/include/render_result.h b/source/blender/render/intern/include/render_result.h index 2619ac76d59..0c4f4e20325 100644 --- a/source/blender/render/intern/include/render_result.h +++ b/source/blender/render/intern/include/render_result.h @@ -83,6 +83,9 @@ void render_result_save_empty_result_tiles(struct Render *re); void render_result_exr_file_begin(struct Render *re); void render_result_exr_file_end(struct Render *re); +/* render pass wrapper for gpencil */ +struct RenderPass *gp_add_pass(struct RenderResult *rr, struct RenderLayer *rl, int channels, int passtype, const char *viewname); + void render_result_exr_file_merge(struct RenderResult *rr, struct RenderResult *rrpart, const char *viewname); void render_result_exr_file_path(struct Scene *scene, const char *layname, int sample, char *filepath); diff --git a/source/blender/render/intern/source/pipeline.c b/source/blender/render/intern/source/pipeline.c index 8a2911464b1..fc99bedfa0f 100644 --- a/source/blender/render/intern/source/pipeline.c +++ b/source/blender/render/intern/source/pipeline.c @@ -3992,3 +3992,31 @@ RenderPass *RE_pass_find_by_type(volatile RenderLayer *rl, int passtype, const c } return rp; } + +/* create a renderlayer and renderpass for grease pencil layer */ +RenderPass *RE_create_gp_pass(RenderResult *rr, const char *layername, const char *viewname) +{ + RenderLayer *rl = BLI_findstring(&rr->layers, layername, offsetof(RenderLayer, name)); + /* only create render layer if not exist */ + if (!rl) { + rl = MEM_callocN(sizeof(RenderLayer), layername); + BLI_addtail(&rr->layers, rl); + BLI_strncpy(rl->name, layername, sizeof(rl->name)); + rl->lay = 0; + rl->layflag = SCE_LAY_SOLID; + rl->passflag = SCE_PASS_COMBINED; + rl->rectx = rr->rectx; + rl->recty = rr->recty; + } + + /* clear previous pass if exist or the new image will be over previous one*/ + RenderPass *rp = RE_pass_find_by_type(rl, SCE_PASS_COMBINED, viewname); + if (rp) { + if (rp->rect) { + MEM_freeN(rp->rect); + } + BLI_freelinkN(&rl->passes, rp); + } + /* create a totally new pass */ + return gp_add_pass(rr, rl, 4, SCE_PASS_COMBINED, viewname); +} diff --git a/source/blender/render/intern/source/render_result.c b/source/blender/render/intern/source/render_result.c index bddd84c45d7..6ea46af6f7e 100644 --- a/source/blender/render/intern/source/render_result.c +++ b/source/blender/render/intern/source/render_result.c @@ -540,6 +540,11 @@ static RenderPass *render_layer_add_pass(RenderResult *rr, RenderLayer *rl, int return rpass; } +/* wrapper called from render_opengl */ +RenderPass *gp_add_pass(RenderResult *rr, RenderLayer *rl, int channels, int passtype, const char *viewname) +{ + return render_layer_add_pass(rr, rl, channels, passtype, viewname); +} #ifdef WITH_CYCLES_DEBUG const char *RE_debug_pass_name_get(int debug_type) diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index 26a1c7038d8..69905fc296b 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -387,9 +387,7 @@ void WM_gestures_remove(struct bContext *C); /* fileselecting support */ void WM_event_add_fileselect(struct bContext *C, struct wmOperator *op); void WM_event_fileselect_event(struct wmWindowManager *wm, void *ophandle, int eventval); -#ifndef NDEBUG void WM_event_print(const struct wmEvent *event); -#endif void WM_operator_region_active_win_set(struct bContext *C); @@ -443,6 +441,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 */ }; diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index 4515ae92f66..01dc77127d2 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -81,9 +81,7 @@ #include "wm_event_system.h" #include "wm_event_types.h" -#ifndef NDEBUG -# include "RNA_enum_types.h" -#endif +#include "RNA_enum_types.h" static void wm_notifier_clear(wmNotifier *note); static void update_tablet_data(wmWindow *win, wmEvent *event); @@ -550,8 +548,6 @@ void WM_operator_region_active_win_set(bContext *C) } /* for debugging only, getting inspecting events manually is tedious */ -#ifndef NDEBUG - void WM_event_print(const wmEvent *event) { if (event) { @@ -593,8 +589,6 @@ void WM_event_print(const wmEvent *event) } } -#endif /* NDEBUG */ - /** * Show the report in the info header. */ @@ -1969,15 +1963,11 @@ static int wm_action_not_handled(int action) static int wm_handlers_do_intern(bContext *C, wmEvent *event, ListBase *handlers) { -#ifndef NDEBUG const bool do_debug_handler = (G.debug & G_DEBUG_HANDLERS) && /* comment this out to flood the console! (if you really want to test) */ !ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE) ; -# define PRINT if (do_debug_handler) printf -#else -# define PRINT(format, ...) -#endif +# define PRINT if (do_debug_handler) printf wmWindowManager *wm = CTX_wm_manager(C); wmEventHandler *handler, *nexthandler; @@ -2382,20 +2372,16 @@ void wm_event_do_handlers(bContext *C) while ( (event = win->queue.first) ) { int action = WM_HANDLER_CONTINUE; -#ifndef NDEBUG if (G.debug & (G_DEBUG_HANDLERS | G_DEBUG_EVENTS) && !ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) { printf("\n%s: Handling event\n", __func__); WM_event_print(event); } -#endif /* take care of pie event filter */ if (wm_event_pie_filter(win, event)) { -#ifndef NDEBUG if (G.debug & (G_DEBUG_HANDLERS | G_DEBUG_EVENTS) && !ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) { printf("\n%s: event filtered due to pie button pressed\n", __func__); } -#endif BLI_remlink(&win->queue, event); wm_event_free(event); continue; diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c index e2ccb6fbbd7..3405537a09c 100644 --- a/source/blender/windowmanager/intern/wm_files_link.c +++ b/source/blender/windowmanager/intern/wm_files_link.c @@ -577,7 +577,7 @@ static void lib_relocate_do( WMLinkAppendDataItem *item; /* We remove it from current Main, and add it to items to link... */ - /* Note that non-linkable IDs (like e.g. shapekeys) are also explicitely linked here... */ + /* Note that non-linkable IDs (like e.g. shapekeys) are also explicitly linked here... */ BLI_remlink(lbarray[lba_idx], id); item = wm_link_append_data_item_add(lapp_data, id->name + 2, idcode, id); BLI_BITMAP_SET_ALL(item->libraries, true, lapp_data->num_libraries); 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_window.c b/source/blender/windowmanager/intern/wm_window.c index 42f6585f152..425287993d9 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,14 +1196,27 @@ 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)); U.pixelsize = wm_window_pixelsize(win); 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_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; |