Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
Diffstat (limited to 'source')
-rw-r--r--source/blender/alembic/ABC_alembic.h4
-rw-r--r--source/blender/alembic/CMakeLists.txt3
-rw-r--r--source/blender/alembic/intern/abc_archive.cc175
-rw-r--r--source/blender/alembic/intern/abc_archive.h66
-rw-r--r--source/blender/alembic/intern/abc_customdata.h9
-rw-r--r--source/blender/alembic/intern/abc_exporter.cc96
-rw-r--r--source/blender/alembic/intern/abc_exporter.h10
-rw-r--r--source/blender/alembic/intern/abc_hair.cc2
-rw-r--r--source/blender/alembic/intern/abc_mesh.cc99
-rw-r--r--source/blender/alembic/intern/abc_points.cc2
-rw-r--r--source/blender/alembic/intern/alembic_capi.cc120
-rw-r--r--source/blender/blenkernel/BKE_DerivedMesh.h1
-rw-r--r--source/blender/blenkernel/BKE_blender_copybuffer.h1
-rw-r--r--source/blender/blenkernel/BKE_blender_version.h2
-rw-r--r--source/blender/blenkernel/BKE_library_remap.h2
-rw-r--r--source/blender/blenkernel/intern/DerivedMesh.c50
-rw-r--r--source/blender/blenkernel/intern/blender_copybuffer.c25
-rw-r--r--source/blender/blenkernel/intern/blendfile.c19
-rw-r--r--source/blender/blenkernel/intern/cdderivedmesh.c5
-rw-r--r--source/blender/blenkernel/intern/editderivedmesh.c20
-rw-r--r--source/blender/blenkernel/intern/library_remap.c3
-rw-r--r--source/blender/blenkernel/intern/mesh.c2
-rw-r--r--source/blender/blenkernel/intern/subsurf_ccg.c88
-rw-r--r--source/blender/blenlib/intern/task.c5
-rw-r--r--source/blender/bmesh/tools/bmesh_bevel.c10
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_relations.cc30
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval.cc2
-rw-r--r--source/blender/editors/animation/anim_channels_defines.c67
-rw-r--r--source/blender/editors/animation/anim_channels_edit.c6
-rw-r--r--source/blender/editors/animation/anim_filter.c10
-rw-r--r--source/blender/editors/armature/armature_edit.c1
-rw-r--r--source/blender/editors/armature/armature_intern.h1
-rw-r--r--source/blender/editors/armature/armature_ops.c1
-rw-r--r--source/blender/editors/armature/pose_lib.c96
-rw-r--r--source/blender/editors/armature/pose_transform.c111
-rw-r--r--source/blender/editors/gpencil/drawgpencil.c35
-rw-r--r--source/blender/editors/gpencil/gpencil_edit.c793
-rw-r--r--source/blender/editors/gpencil/gpencil_intern.h6
-rw-r--r--source/blender/editors/gpencil/gpencil_ops.c14
-rw-r--r--source/blender/editors/gpencil/gpencil_undo.c52
-rw-r--r--source/blender/editors/include/ED_anim_api.h3
-rw-r--r--source/blender/editors/include/ED_gpencil.h33
-rw-r--r--source/blender/editors/include/ED_numinput.h2
-rw-r--r--source/blender/editors/include/ED_util.h3
-rw-r--r--source/blender/editors/include/UI_interface.h6
-rw-r--r--source/blender/editors/interface/interface_ops.c73
-rw-r--r--source/blender/editors/interface/interface_panel.c76
-rw-r--r--source/blender/editors/interface/interface_regions.c63
-rw-r--r--source/blender/editors/io/io_alembic.c26
-rw-r--r--source/blender/editors/render/render_opengl.c239
-rw-r--r--source/blender/editors/screen/screen_ops.c8
-rw-r--r--source/blender/editors/sound/sound_ops.c69
-rw-r--r--source/blender/editors/space_file/filelist.c9
-rw-r--r--source/blender/editors/space_graph/graph_draw.c2
-rw-r--r--source/blender/editors/space_graph/space_graph.c4
-rw-r--r--source/blender/editors/space_image/image_ops.c5
-rw-r--r--source/blender/editors/space_outliner/outliner_edit.c42
-rw-r--r--source/blender/editors/space_view3d/drawobject.c157
-rw-r--r--source/blender/makesdna/DNA_anim_types.h5
-rw-r--r--source/blender/makesdna/DNA_gpencil_types.h2
-rw-r--r--source/blender/makesdna/DNA_scene_types.h6
-rw-r--r--source/blender/makesrna/RNA_access.h1
-rw-r--r--source/blender/makesrna/intern/rna_ID.c4
-rw-r--r--source/blender/makesrna/intern/rna_access.c39
-rw-r--r--source/blender/makesrna/intern/rna_internal.h1
-rw-r--r--source/blender/makesrna/intern/rna_main.c7
-rw-r--r--source/blender/makesrna/intern/rna_main_api.c16
-rw-r--r--source/blender/makesrna/intern/rna_scene_api.c11
-rw-r--r--source/blender/makesrna/intern/rna_sculpt_paint.c10
-rw-r--r--source/blender/makesrna/intern/rna_ui_api.c6
-rw-r--r--source/blender/makesrna/intern/rna_wm_api.c26
-rw-r--r--source/blender/modifiers/intern/MOD_subsurf.c3
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_fresnel.c6
-rw-r--r--source/blender/python/intern/bpy_app.c10
-rw-r--r--source/blender/python/intern/bpy_rna_id_collection.c32
-rw-r--r--source/blender/render/extern/include/RE_pipeline.h3
-rw-r--r--source/blender/render/intern/source/render_result.c81
-rw-r--r--source/blender/windowmanager/intern/wm_event_system.c8
-rw-r--r--source/blender/windowmanager/intern/wm_init_exit.c1
-rw-r--r--source/blender/windowmanager/intern/wm_operators.c10
-rw-r--r--source/creator/CMakeLists.txt3
81 files changed, 2483 insertions, 672 deletions
diff --git a/source/blender/alembic/ABC_alembic.h b/source/blender/alembic/ABC_alembic.h
index cf121f8488c..e62713f57f5 100644
--- a/source/blender/alembic/ABC_alembic.h
+++ b/source/blender/alembic/ABC_alembic.h
@@ -64,8 +64,12 @@ struct AlembicExportParams {
unsigned int face_sets : 1;
unsigned int use_subdiv_schema : 1;
unsigned int packuv : 1;
+ unsigned int triangulate : 1;
unsigned int compression_type : 1;
+
+ int quad_method;
+ int ngon_method;
float global_scale;
};
diff --git a/source/blender/alembic/CMakeLists.txt b/source/blender/alembic/CMakeLists.txt
index 0b6b2433bd0..ad85f79ef2e 100644
--- a/source/blender/alembic/CMakeLists.txt
+++ b/source/blender/alembic/CMakeLists.txt
@@ -28,6 +28,7 @@ set(INC
../blenkernel
../blenlib
../blenloader
+ ../bmesh
../editors/include
../makesdna
../makesrna
@@ -48,6 +49,7 @@ if(APPLE OR WIN32)
endif()
set(SRC
+ intern/abc_archive.cc
intern/abc_camera.cc
intern/abc_customdata.cc
intern/abc_curves.cc
@@ -62,6 +64,7 @@ set(SRC
intern/alembic_capi.cc
ABC_alembic.h
+ intern/abc_archive.h
intern/abc_camera.h
intern/abc_customdata.h
intern/abc_curves.h
diff --git a/source/blender/alembic/intern/abc_archive.cc b/source/blender/alembic/intern/abc_archive.cc
new file mode 100644
index 00000000000..0985a06d732
--- /dev/null
+++ b/source/blender/alembic/intern/abc_archive.cc
@@ -0,0 +1,175 @@
+/*
+ * ***** 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_archive.h"
+
+#ifdef WIN32
+# include "utfconv.h"
+#endif
+
+using Alembic::Abc::Exception;
+using Alembic::Abc::ErrorHandler;
+using Alembic::Abc::IArchive;
+using Alembic::Abc::kWrapExisting;
+using Alembic::Abc::OArchive;
+
+static IArchive open_archive(const std::string &filename,
+ const std::vector<std::istream *> &input_streams,
+ bool &is_hdf5)
+{
+ try {
+ is_hdf5 = false;
+ Alembic::AbcCoreOgawa::ReadArchive archive_reader(input_streams);
+
+ return IArchive(archive_reader(filename),
+ kWrapExisting,
+ ErrorHandler::kThrowPolicy);
+ }
+ catch (const Exception &e) {
+ std::cerr << e.what() << '\n';
+
+#ifdef WITH_ALEMBIC_HDF5
+ try {
+ is_hdf5 = true;
+ Alembic::AbcCoreAbstract::ReadArraySampleCachePtr cache_ptr;
+
+ return IArchive(Alembic::AbcCoreHDF5::ReadArchive(),
+ filename.c_str(), ErrorHandler::kThrowPolicy,
+ cache_ptr);
+ }
+ catch (const Exception &) {
+ std::cerr << e.what() << '\n';
+ return IArchive();
+ }
+#else
+ return IArchive();
+#endif
+ }
+
+ return IArchive();
+}
+
+ArchiveReader::ArchiveReader(const char *filename)
+{
+#ifdef WIN32
+ UTF16_ENCODE(filename);
+ std::wstring wstr(filename_16);
+ m_infile.open(wstr.c_str(), std::ios::in | std::ios::binary);
+ UTF16_UN_ENCODE(filename);
+#else
+ m_infile.open(filename, std::ios::in | std::ios::binary);
+#endif
+
+ m_streams.push_back(&m_infile);
+
+ bool is_hdf5;
+ m_archive = open_archive(filename, m_streams, is_hdf5);
+
+ /* We can't open an HDF5 file from a stream, so close it. */
+ if (is_hdf5) {
+ m_infile.close();
+ m_streams.clear();
+ }
+}
+
+bool ArchiveReader::valid() const
+{
+ return m_archive.valid();
+}
+
+Alembic::Abc::IObject ArchiveReader::getTop()
+{
+ return m_archive.getTop();
+}
+
+/* ************************************************************************** */
+
+/* This kinda duplicates CreateArchiveWithInfo, but Alembic does not seem to
+ * have a version supporting streams. */
+static OArchive create_archive(std::ostream *ostream,
+ const std::string &filename,
+ const std::string &scene_name,
+ Alembic::Abc::MetaData &md,
+ bool ogawa)
+{
+ md.set(Alembic::Abc::kApplicationNameKey, "Blender");
+ md.set(Alembic::Abc::kUserDescriptionKey, scene_name);
+
+ time_t raw_time;
+ time(&raw_time);
+ char buffer[128];
+
+#if defined _WIN32 || defined _WIN64
+ ctime_s(buffer, 128, &raw_time);
+#else
+ ctime_r(&raw_time, buffer);
+#endif
+
+ const std::size_t buffer_len = strlen(buffer);
+ if (buffer_len > 0 && buffer[buffer_len - 1] == '\n') {
+ buffer[buffer_len - 1] = '\0';
+ }
+
+ md.set(Alembic::Abc::kDateWrittenKey, buffer);
+
+ ErrorHandler::Policy policy = ErrorHandler::kThrowPolicy;
+
+#ifdef WITH_ALEMBIC_HDF5
+ if (!ogawa) {
+ return OArchive(Alembic::AbcCoreHDF5::WriteArchive(), filename, md, policy);
+ }
+#else
+ static_cast<void>(filename);
+ static_cast<void>(ogawa);
+#endif
+
+ Alembic::AbcCoreOgawa::WriteArchive archive_writer;
+ return OArchive(archive_writer(ostream, md), kWrapExisting, policy);
+}
+
+ArchiveWriter::ArchiveWriter(const char *filename, const char *scene, bool do_ogawa, Alembic::Abc::MetaData &md)
+{
+ /* Use stream to support unicode character paths on Windows. */
+ if (do_ogawa) {
+#ifdef WIN32
+ UTF16_ENCODE(filename);
+ std::wstring wstr(filename_16);
+ m_outfile.open(wstr.c_str(), std::ios::out | std::ios::binary);
+ UTF16_UN_ENCODE(filename);
+#else
+ m_outfile.open(filename, std::ios::out | std::ios::binary);
+#endif
+ }
+
+ m_archive = create_archive(&m_outfile,
+ filename,
+ scene,
+ md,
+ do_ogawa);
+}
+
+OArchive &ArchiveWriter::archive()
+{
+ return m_archive;
+}
diff --git a/source/blender/alembic/intern/abc_archive.h b/source/blender/alembic/intern/abc_archive.h
new file mode 100644
index 00000000000..d412574b736
--- /dev/null
+++ b/source/blender/alembic/intern/abc_archive.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.
+ *
+ * The Original Code is Copyright (C) 2016 Kévin Dietrich.
+ * All rights reserved.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ *
+ */
+
+#ifndef __ABC_ARCHIVE_H__
+#define __ABC_ARCHIVE_H__
+
+#include <Alembic/Abc/All.h>
+
+#ifdef WITH_ALEMBIC_HDF5
+# include <Alembic/AbcCoreHDF5/All.h>
+#endif
+
+#include <Alembic/AbcCoreOgawa/All.h>
+
+#include <fstream>
+
+/* Wrappers around input and output archives. The goal is to be able to use
+ * streams so that unicode paths work on Windows (T49112), and to make sure that
+ * the stream objects remain valid as long as the archives are open.
+ */
+
+class ArchiveReader {
+ Alembic::Abc::IArchive m_archive;
+ std::ifstream m_infile;
+ std::vector<std::istream *> m_streams;
+
+public:
+ explicit ArchiveReader(const char *filename);
+
+ bool valid() const;
+
+ Alembic::Abc::IObject getTop();
+};
+
+class ArchiveWriter {
+ std::ofstream m_outfile;
+ Alembic::Abc::OArchive m_archive;
+
+public:
+ explicit ArchiveWriter(const char *filename, const char *scene, bool do_ogawa, Alembic::Abc::MetaData &md);
+
+ Alembic::Abc::OArchive &archive();
+};
+
+#endif /* __ABC_ARCHIVE_H__ */
diff --git a/source/blender/alembic/intern/abc_customdata.h b/source/blender/alembic/intern/abc_customdata.h
index 3b16c0d9796..bc42e24eba1 100644
--- a/source/blender/alembic/intern/abc_customdata.h
+++ b/source/blender/alembic/intern/abc_customdata.h
@@ -63,6 +63,11 @@ struct CDStreamConfig {
void *user_data;
void *(*add_customdata_cb)(void *user_data, const char *name, int data_type);
+ float weight;
+ float time;
+ int index;
+ int ceil_index;
+
CDStreamConfig()
: mloop(NULL)
, totloop(0)
@@ -72,6 +77,10 @@ struct CDStreamConfig {
, pack_uvs(false)
, user_data(NULL)
, add_customdata_cb(NULL)
+ , weight(0.0f)
+ , time(0.0f)
+ , index(0)
+ , ceil_index(0)
{}
};
diff --git a/source/blender/alembic/intern/abc_exporter.cc b/source/blender/alembic/intern/abc_exporter.cc
index 57a2bdd274f..db41d5e837c 100644
--- a/source/blender/alembic/intern/abc_exporter.cc
+++ b/source/blender/alembic/intern/abc_exporter.cc
@@ -24,16 +24,7 @@
#include <cmath>
-#ifdef WITH_ALEMBIC_HDF5
-# include <Alembic/AbcCoreHDF5/All.h>
-#endif
-
-#include <Alembic/AbcCoreOgawa/All.h>
-
-#ifdef WIN32
-# include "utfconv.h"
-#endif
-
+#include "abc_archive.h"
#include "abc_camera.h"
#include "abc_curves.h"
#include "abc_hair.h"
@@ -70,54 +61,6 @@ extern "C" {
using Alembic::Abc::TimeSamplingPtr;
using Alembic::Abc::OBox3dProperty;
-
-/* ************************************************************************** */
-
-/* This kinda duplicates CreateArchiveWithInfo, but Alembic does not seem to
- * have a version supporting streams. */
-static Alembic::Abc::OArchive create_archive(std::ostream *ostream,
- const std::string &filename,
- const std::string &scene_name,
- const Alembic::Abc::Argument &arg0,
- const Alembic::Abc::Argument &arg1,
- bool ogawa)
-{
- Alembic::Abc::MetaData md = GetMetaData(arg0, arg1);
- md.set(Alembic::Abc::kApplicationNameKey, "Blender");
- md.set(Alembic::Abc::kUserDescriptionKey, scene_name);
-
- time_t raw_time;
- time(&raw_time);
- char buffer[128];
-
-#if defined _WIN32 || defined _WIN64
- ctime_s(buffer, 128, &raw_time);
-#else
- ctime_r(&raw_time, buffer);
-#endif
-
- const std::size_t buffer_len = strlen(buffer);
- if (buffer_len > 0 && buffer[buffer_len - 1] == '\n') {
- buffer[buffer_len - 1] = '\0';
- }
-
- md.set(Alembic::Abc::kDateWrittenKey, buffer);
-
- Alembic::Abc::ErrorHandler::Policy policy = GetErrorHandlerPolicyFromArgs(arg0, arg1);
-
-#ifdef WITH_ALEMBIC_HDF5
- if (!ogawa) {
- return Alembic::Abc::OArchive(Alembic::AbcCoreHDF5::WriteArchive(), filename, md, policy);
- }
-#else
- static_cast<void>(filename);
- static_cast<void>(ogawa);
-#endif
-
- Alembic::AbcCoreOgawa::WriteArchive archive_writer;
- return Alembic::Abc::OArchive(archive_writer(ostream, md), Alembic::Abc::kWrapExisting, policy);
-}
-
/* ************************************************************************** */
ExportSettings::ExportSettings()
@@ -144,6 +87,9 @@ ExportSettings::ExportSettings()
, export_ogawa(true)
, pack_uv(false)
, do_convert_axis(false)
+ , triangulate(false)
+ , quad_method(0)
+ , ngon_method(0)
{}
static bool object_is_smoke_sim(Object *ob)
@@ -201,6 +147,7 @@ AbcExporter::AbcExporter(Scene *scene, const char *filename, ExportSettings &set
, m_trans_sampling_index(0)
, m_shape_sampling_index(0)
, m_scene(scene)
+ , m_writer(NULL)
{}
AbcExporter::~AbcExporter()
@@ -213,6 +160,8 @@ AbcExporter::~AbcExporter()
for (int i = 0, e = m_shapes.size(); i != e; ++i) {
delete m_shapes[i];
}
+
+ delete m_writer;
}
void AbcExporter::getShutterSamples(double step, bool time_relative,
@@ -296,32 +245,13 @@ void AbcExporter::operator()(Main *bmain, float &progress, bool &was_canceled)
Alembic::AbcCoreAbstract::MetaData md;
md.set("FramesPerTimeUnit", str_fps);
- Alembic::Abc::Argument arg(md);
-
- /* Use stream to support unicode character paths on Windows. */
- if (m_settings.export_ogawa) {
-#ifdef WIN32
- UTF16_ENCODE(m_filename);
- std::wstring wstr(m_filename_16);
- m_out_file.open(wstr.c_str(), std::ios::out | std::ios::binary);
- UTF16_UN_ENCODE(m_filename);
-#else
- m_out_file.open(m_filename, std::ios::out | std::ios::binary);
-#endif
- }
-
- m_archive = create_archive(&m_out_file,
- m_filename,
- scene_name,
- Alembic::Abc::ErrorHandler::kThrowPolicy,
- arg,
- m_settings.export_ogawa);
+ m_writer = new ArchiveWriter(m_filename, scene_name.c_str(), m_settings.export_ogawa, md);
/* 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);
+ m_trans_sampling_index = m_writer->archive().addTimeSampling(*trans_time);
TimeSamplingPtr shape_time;
@@ -333,10 +263,10 @@ void AbcExporter::operator()(Main *bmain, float &progress, bool &was_canceled)
}
else {
shape_time = createTimeSampling(m_settings.frame_step_shape);
- m_shape_sampling_index = m_archive.addTimeSampling(*shape_time);
+ m_shape_sampling_index = m_writer->archive().addTimeSampling(*shape_time);
}
- OBox3dProperty archive_bounds_prop = Alembic::AbcGeom::CreateOArchiveBounds(m_archive, m_trans_sampling_index);
+ OBox3dProperty archive_bounds_prop = Alembic::AbcGeom::CreateOArchiveBounds(m_writer->archive(), m_trans_sampling_index);
if (m_settings.flatten_hierarchy) {
createTransformWritersFlat();
@@ -442,7 +372,7 @@ void AbcExporter::createTransformWritersFlat()
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);
+ m_xforms[name] = new AbcTransformWriter(ob, m_writer->archive().getTop(), 0, m_trans_sampling_index, m_settings);
}
base = base->next;
@@ -508,7 +438,7 @@ void AbcExporter::createTransformWriter(Object *ob, Object *parent, Object *dupl
m_xforms[name]->setParent(parent);
}
else {
- m_xforms[name] = new AbcTransformWriter(ob, m_archive.getTop(), NULL, m_trans_sampling_index, m_settings);
+ m_xforms[name] = new AbcTransformWriter(ob, m_writer->archive().getTop(), NULL, m_trans_sampling_index, m_settings);
}
}
diff --git a/source/blender/alembic/intern/abc_exporter.h b/source/blender/alembic/intern/abc_exporter.h
index 6c242f973c4..b0eb8e185d6 100644
--- a/source/blender/alembic/intern/abc_exporter.h
+++ b/source/blender/alembic/intern/abc_exporter.h
@@ -24,13 +24,13 @@
#define __ABC_EXPORTER_H__
#include <Alembic/Abc/All.h>
-#include <fstream>
#include <map>
#include <set>
#include <vector>
class AbcObjectWriter;
class AbcTransformWriter;
+class ArchiveWriter;
struct EvaluationContext;
struct Main;
@@ -66,6 +66,10 @@ struct ExportSettings {
bool export_child_hairs;
bool export_ogawa;
bool pack_uv;
+ bool triangulate;
+
+ int quad_method;
+ int ngon_method;
bool do_convert_axis;
float convert_matrix[3][3];
@@ -76,12 +80,12 @@ class AbcExporter {
const char *m_filename;
- std::ofstream m_out_file;
- Alembic::Abc::OArchive m_archive;
unsigned int m_trans_sampling_index, m_shape_sampling_index;
Scene *m_scene;
+ ArchiveWriter *m_writer;
+
std::map<std::string, AbcTransformWriter *> m_xforms;
std::vector<AbcObjectWriter *> m_shapes;
diff --git a/source/blender/alembic/intern/abc_hair.cc b/source/blender/alembic/intern/abc_hair.cc
index 967c99d81e1..5a95944d3fa 100644
--- a/source/blender/alembic/intern/abc_hair.cc
+++ b/source/blender/alembic/intern/abc_hair.cc
@@ -58,7 +58,7 @@ AbcHairWriter::AbcHairWriter(Scene *scene,
{
m_psys = NULL; // = psys;
- OCurves curves(parent->alembicXform(), m_name, m_time_sampling);
+ OCurves curves(parent->alembicXform(), psys->name, m_time_sampling);
m_schema = curves.getSchema();
}
diff --git a/source/blender/alembic/intern/abc_mesh.cc b/source/blender/alembic/intern/abc_mesh.cc
index 4db33289232..41261ffaa30 100644
--- a/source/blender/alembic/intern/abc_mesh.cc
+++ b/source/blender/alembic/intern/abc_mesh.cc
@@ -37,8 +37,8 @@ extern "C" {
#include "BLI_math_geom.h"
#include "BLI_string.h"
+#include "BKE_cdderivedmesh.h"
#include "BKE_depsgraph.h"
-#include "BKE_DerivedMesh.h"
#include "BKE_main.h"
#include "BKE_material.h"
#include "BKE_mesh.h"
@@ -49,6 +49,9 @@ extern "C" {
#include "WM_types.h"
#include "ED_mesh.h"
+
+#include "bmesh.h"
+#include "bmesh_tools.h"
}
using Alembic::Abc::FloatArraySample;
@@ -538,6 +541,23 @@ DerivedMesh *AbcMeshWriter::getFinalMesh()
m_subsurf_mod->mode &= ~eModifierMode_DisableTemporary;
}
+ if (m_settings.triangulate) {
+ const bool tag_only = false;
+ const int quad_method = m_settings.quad_method;
+ const int ngon_method = m_settings.ngon_method;
+
+ BMesh *bm = DM_to_bmesh(dm, true);
+
+ BM_mesh_triangulate(bm, quad_method, ngon_method, tag_only, NULL, NULL, NULL);
+
+ DerivedMesh *result = CDDM_from_bmesh(bm, false);
+ BM_mesh_free(bm);
+
+ freeMesh(dm);
+
+ dm = result;
+ }
+
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);
@@ -784,6 +804,7 @@ struct AbcMeshData {
Int32ArraySamplePtr face_counts;
P3fArraySamplePtr positions;
+ P3fArraySamplePtr ceil_positions;
N3fArraySamplePtr vertex_normals;
N3fArraySamplePtr face_normals;
@@ -831,12 +852,32 @@ CDStreamConfig create_config(Mesh *mesh)
return config;
}
+static void read_mverts_interp(MVert *mverts, const P3fArraySamplePtr &positions, const P3fArraySamplePtr &ceil_positions, const float weight)
+{
+ float tmp[3];
+ for (int i = 0; i < positions->size(); ++i) {
+ MVert &mvert = mverts[i];
+ const Imath::V3f &floor_pos = (*positions)[i];
+ const Imath::V3f &ceil_pos = (*ceil_positions)[i];
+
+ interp_v3_v3v3(tmp, floor_pos.getValue(), ceil_pos.getValue(), weight);
+ copy_yup_zup(mvert.co, tmp);
+
+ mvert.bweight = 0;
+ }
+}
+
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;
+ if (config.weight != 0.0f && mesh_data.ceil_positions) {
+ read_mverts_interp(mverts, positions, mesh_data.ceil_positions, config.weight);
+ return;
+ }
+
read_mverts(mverts, positions, normals);
}
@@ -1063,6 +1104,46 @@ void AbcMeshReader::readFaceSetsSample(Main *bmain, Mesh *mesh, size_t poly_star
utils::assign_materials(bmain, m_object, mat_map);
}
+typedef std::pair<Alembic::AbcCoreAbstract::index_t, float> index_time_pair_t;
+
+static void get_weight_and_index(CDStreamConfig &config,
+ Alembic::AbcCoreAbstract::TimeSamplingPtr time_sampling,
+ size_t samples_number)
+{
+ if (samples_number == 0) {
+ samples_number = 1;
+ }
+
+ index_time_pair_t floor_index = time_sampling->getFloorIndex(config.time, samples_number);
+
+ config.index = floor_index.first;
+ config.ceil_index = config.index;
+
+ if (fabs(config.time - floor_index.second) < 0.0001f) {
+ config.weight = 0.0f;
+ return;
+ }
+
+ index_time_pair_t ceil_index = time_sampling->getCeilIndex(config.time, samples_number);
+
+ if (config.index == ceil_index.first) {
+ config.weight = 0.0f;
+ return;
+ }
+
+ config.ceil_index = ceil_index.first;
+
+ float alpha = (config.time - floor_index.second) / (ceil_index.second - floor_index.second);
+
+ /* Since we so closely match the ceiling, we'll just use it. */
+ if (fabs(1.0f - alpha) < 0.0001f) {
+ config.index = config.ceil_index;
+ alpha = 0.0f;
+ }
+
+ config.weight = alpha;
+}
+
void read_mesh_sample(ImportSettings *settings,
const IPolyMeshSchema &schema,
const ISampleSelector &selector,
@@ -1080,6 +1161,14 @@ void read_mesh_sample(ImportSettings *settings,
do_normals = (abc_mesh_data.face_normals != NULL);
+ get_weight_and_index(config, schema.getTimeSampling(), schema.getNumSamples());
+
+ if (config.weight != 0.0f) {
+ Alembic::AbcGeom::IPolyMeshSchema::Sample ceil_sample;
+ schema.get(ceil_sample, Alembic::Abc::ISampleSelector(static_cast<Alembic::AbcCoreAbstract::index_t>(config.ceil_index)));
+ abc_mesh_data.ceil_positions = ceil_sample.getPositions();
+ }
+
if ((settings->read_flag & MOD_MESHSEQ_READ_UV) != 0) {
read_uvs_params(config, abc_mesh_data, schema.getUVsParam(), selector);
}
@@ -1195,6 +1284,14 @@ void read_subd_sample(ImportSettings *settings,
abc_mesh_data.face_normals = N3fArraySamplePtr();
abc_mesh_data.positions = sample.getPositions();
+ get_weight_and_index(config, schema.getTimeSampling(), schema.getNumSamples());
+
+ if (config.weight != 0.0f) {
+ Alembic::AbcGeom::ISubDSchema::Sample ceil_sample;
+ schema.get(ceil_sample, Alembic::Abc::ISampleSelector(static_cast<Alembic::AbcCoreAbstract::index_t>(config.ceil_index)));
+ abc_mesh_data.ceil_positions = ceil_sample.getPositions();
+ }
+
if ((settings->read_flag & MOD_MESHSEQ_READ_UV) != 0) {
read_uvs_params(config, abc_mesh_data, schema.getUVsParam(), selector);
}
diff --git a/source/blender/alembic/intern/abc_points.cc b/source/blender/alembic/intern/abc_points.cc
index 22a51d984ef..8b711239916 100644
--- a/source/blender/alembic/intern/abc_points.cc
+++ b/source/blender/alembic/intern/abc_points.cc
@@ -66,7 +66,7 @@ AbcPointsWriter::AbcPointsWriter(Scene *scene,
{
m_psys = NULL; // = psys;
- OPoints points(parent->alembicXform(), m_name, m_time_sampling);
+ OPoints points(parent->alembicXform(), psys->name, m_time_sampling);
m_schema = points.getSchema();
}
diff --git a/source/blender/alembic/intern/alembic_capi.cc b/source/blender/alembic/intern/alembic_capi.cc
index d057cc341f6..3cc549350ea 100644
--- a/source/blender/alembic/intern/alembic_capi.cc
+++ b/source/blender/alembic/intern/alembic_capi.cc
@@ -22,19 +22,9 @@
#include "../ABC_alembic.h"
-#ifdef WITH_ALEMBIC_HDF5
-# include <Alembic/AbcCoreHDF5/All.h>
-#endif
-
-#include <Alembic/AbcCoreOgawa/All.h>
#include <Alembic/AbcMaterial/IMaterial.h>
-#include <fstream>
-
-#ifdef WIN32
-# include "utfconv.h"
-#endif
-
+#include "abc_archive.h"
#include "abc_camera.h"
#include "abc_curves.h"
#include "abc_hair.h"
@@ -83,13 +73,10 @@ extern "C" {
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;
@@ -115,95 +102,16 @@ using Alembic::AbcGeom::V3fArraySamplePtr;
using Alembic::AbcMaterial::IMaterial;
-static IArchive open_archive(const std::string &filename,
- const std::vector<std::istream *> &input_streams,
- bool &is_hdf5)
-{
- try {
- is_hdf5 = false;
- Alembic::AbcCoreOgawa::ReadArchive archive_reader(input_streams);
-
- return IArchive(archive_reader(filename),
- kWrapExisting,
- ErrorHandler::kThrowPolicy);
- }
- catch (const Exception &e) {
- std::cerr << e.what() << '\n';
-
-#ifdef WITH_ALEMBIC_HDF5
- try {
- is_hdf5 = true;
- Alembic::AbcCoreAbstract::ReadArraySampleCachePtr cache_ptr;
-
- return IArchive(Alembic::AbcCoreHDF5::ReadArchive(),
- filename.c_str(), ErrorHandler::kThrowPolicy,
- cache_ptr);
- }
- catch (const Exception &) {
- std::cerr << e.what() << '\n';
- return IArchive();
- }
-#else
- return IArchive();
-#endif
- }
-
- return IArchive();
-}
-
-/* Wrapper around an archive to be able to use streams so that unicode paths
- * work on Windows (T49112), and to make sure the input stream remains valid as
- * long as the archive is open. */
-class ArchiveWrapper {
- IArchive m_archive;
- std::ifstream m_infile;
- std::vector<std::istream *> m_streams;
-
-public:
- explicit ArchiveWrapper(const char *filename)
- {
-#ifdef WIN32
- UTF16_ENCODE(filename);
- std::wstring wstr(filename_16);
- m_infile.open(wstr.c_str(), std::ios::in | std::ios::binary);
- UTF16_UN_ENCODE(filename);
-#else
- m_infile.open(filename, std::ios::in | std::ios::binary);
-#endif
-
- m_streams.push_back(&m_infile);
-
- bool is_hdf5;
- m_archive = open_archive(filename, m_streams, is_hdf5);
-
- /* We can't open an HDF5 file from a stream, so close it. */
- if (is_hdf5) {
- m_infile.close();
- m_streams.clear();
- }
- }
-
- bool valid() const
- {
- return m_archive.valid();
- }
-
- IObject getTop()
- {
- return m_archive.getTop();
- }
-};
-
struct AbcArchiveHandle {
int unused;
};
-ABC_INLINE ArchiveWrapper *archive_from_handle(AbcArchiveHandle *handle)
+ABC_INLINE ArchiveReader *archive_from_handle(AbcArchiveHandle *handle)
{
- return reinterpret_cast<ArchiveWrapper *>(handle);
+ return reinterpret_cast<ArchiveReader *>(handle);
}
-ABC_INLINE AbcArchiveHandle *handle_from_archive(ArchiveWrapper *archive)
+ABC_INLINE AbcArchiveHandle *handle_from_archive(ArchiveReader *archive)
{
return reinterpret_cast<AbcArchiveHandle *>(archive);
}
@@ -301,7 +209,7 @@ static void gather_objects_paths(const IObject &object, ListBase *object_paths)
AbcArchiveHandle *ABC_create_handle(const char *filename, ListBase *object_paths)
{
- ArchiveWrapper *archive = new ArchiveWrapper(filename);
+ ArchiveReader *archive = new ArchiveReader(filename);
if (!archive->valid()) {
delete archive;
@@ -443,6 +351,9 @@ void ABC_export(
job->settings.export_ogawa = (params->compression_type == ABC_ARCHIVE_OGAWA);
job->settings.pack_uv = params->packuv;
job->settings.global_scale = params->global_scale;
+ job->settings.triangulate = params->triangulate;
+ job->settings.quad_method = params->quad_method;
+ job->settings.ngon_method = params->ngon_method;
if (job->settings.frame_start > job->settings.frame_end) {
std::swap(job->settings.frame_start, job->settings.frame_end);
@@ -599,10 +510,6 @@ struct ImportJobData {
ABC_INLINE bool is_mesh_and_strands(const IObject &object)
{
- if (object.getNumChildren() != 2) {
- return false;
- }
-
bool has_mesh = false;
bool has_curve = false;
@@ -624,6 +531,9 @@ ABC_INLINE bool is_mesh_and_strands(const IObject &object)
else if (ICurves::matches(md)) {
has_curve = true;
}
+ else if (IPoints::matches(md)) {
+ has_curve = true;
+ }
}
return has_mesh && has_curve;
@@ -637,7 +547,7 @@ static void import_startjob(void *user_data, short *stop, short *do_update, floa
data->do_update = do_update;
data->progress = progress;
- ArchiveWrapper *archive = new ArchiveWrapper(data->filename);
+ ArchiveReader *archive = new ArchiveReader(data->filename);
if (!archive->valid()) {
delete archive;
@@ -865,7 +775,7 @@ void ABC_import(bContext *C, const char *filepath, float scale, bool is_sequence
void ABC_get_transform(AbcArchiveHandle *handle, Object *ob, const char *object_path, float r_mat[4][4], float time, float scale)
{
- ArchiveWrapper *archive = archive_from_handle(handle);
+ ArchiveReader *archive = archive_from_handle(handle);
if (!archive || !archive->valid()) {
return;
@@ -988,6 +898,7 @@ static DerivedMesh *read_mesh_sample(DerivedMesh *dm, const IObject &iobject, co
}
CDStreamConfig config = get_config(new_dm ? new_dm : dm);
+ config.time = time;
bool do_normals = false;
read_mesh_sample(&settings, schema, sample_sel, config, do_normals);
@@ -1042,6 +953,7 @@ static DerivedMesh *read_subd_sample(DerivedMesh *dm, const IObject &iobject, co
/* Only read point data when streaming meshes, unless we need to create new ones. */
CDStreamConfig config = get_config(new_dm ? new_dm : dm);
+ config.time = time;
read_subd_sample(&settings, schema, sample_sel, config);
if (new_dm) {
@@ -1141,7 +1053,7 @@ DerivedMesh *ABC_read_mesh(AbcArchiveHandle *handle,
const char **err_str,
int read_flag)
{
- ArchiveWrapper *archive = archive_from_handle(handle);
+ ArchiveReader *archive = archive_from_handle(handle);
if (!archive || !archive->valid()) {
*err_str = "Invalid archive!";
diff --git a/source/blender/blenkernel/BKE_DerivedMesh.h b/source/blender/blenkernel/BKE_DerivedMesh.h
index 606488f85cc..789bc8df7e5 100644
--- a/source/blender/blenkernel/BKE_DerivedMesh.h
+++ b/source/blender/blenkernel/BKE_DerivedMesh.h
@@ -783,6 +783,7 @@ void DM_vertex_attributes_from_gpu(
struct GPUVertexAttribs *gattribs, DMVertexAttribs *attribs);
void DM_draw_attrib_vertex(DMVertexAttribs *attribs, int a, int index, int vert, int loop);
+void DM_draw_attrib_vertex_uniforms(const DMVertexAttribs *attribs);
void DM_calc_tangents_names_from_gpu(
const struct GPUVertexAttribs *gattribs,
diff --git a/source/blender/blenkernel/BKE_blender_copybuffer.h b/source/blender/blenkernel/BKE_blender_copybuffer.h
index 8aaf295c33a..02bd96217f6 100644
--- a/source/blender/blenkernel/BKE_blender_copybuffer.h
+++ b/source/blender/blenkernel/BKE_blender_copybuffer.h
@@ -37,6 +37,7 @@ struct ID;
void BKE_copybuffer_begin(struct Main *bmain_src);
void BKE_copybuffer_tag_ID(struct ID *id);
bool BKE_copybuffer_save(struct Main *bmain_src, const char *filename, struct ReportList *reports);
+bool BKE_copybuffer_read(struct Main *bmain_dst, const char *libname, struct ReportList *reports);
bool BKE_copybuffer_paste(struct bContext *C, const char *libname, const short flag, struct ReportList *reports);
#ifdef __cplusplus
diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h
index 189340db618..3ae01d8148a 100644
--- a/source/blender/blenkernel/BKE_blender_version.h
+++ b/source/blender/blenkernel/BKE_blender_version.h
@@ -37,7 +37,7 @@
/* can be left blank, otherwise a,b,c... etc with no quotes */
#define BLENDER_VERSION_CHAR
/* alpha/beta/rc/release, docs use this */
-#define BLENDER_VERSION_CYCLE rc
+#define BLENDER_VERSION_CYCLE alpha
extern char versionstr[]; /* from blender.c */
diff --git a/source/blender/blenkernel/BKE_library_remap.h b/source/blender/blenkernel/BKE_library_remap.h
index e974b79ee66..89b087014b2 100644
--- a/source/blender/blenkernel/BKE_library_remap.h
+++ b/source/blender/blenkernel/BKE_library_remap.h
@@ -44,7 +44,7 @@ enum {
* (like e.g. Object->data). */
ID_REMAP_FLAG_NEVER_NULL_USAGE = 1 << 2,
/* This tells the callback func to force setting IDs using target one with a 'never NULL' pointer to NULL.
- * WARNING! Use with extreme care, this will leave database in broken state! */
+ * WARNING! Use with extreme care, this will leave database in broken state and can cause crashes very easily! */
ID_REMAP_FORCE_NEVER_NULL_USAGE = 1 << 3,
};
diff --git a/source/blender/blenkernel/intern/DerivedMesh.c b/source/blender/blenkernel/intern/DerivedMesh.c
index 714417ae29e..8168817491f 100644
--- a/source/blender/blenkernel/intern/DerivedMesh.c
+++ b/source/blender/blenkernel/intern/DerivedMesh.c
@@ -80,6 +80,7 @@ static DerivedMesh *navmesh_dm_createNavMeshForVisualization(DerivedMesh *dm);
#include "GPU_shader.h"
#ifdef WITH_OPENSUBDIV
+# include "BKE_depsgraph.h"
# include "DNA_userdef_types.h"
#endif
@@ -2566,7 +2567,7 @@ static void editbmesh_calc_modifiers(
* playback performance is kept as high as possible.
*/
static bool calc_modifiers_skip_orco(Scene *scene,
- const Object *ob,
+ Object *ob,
bool use_render_params)
{
ModifierData *last_md = ob->modifiers.last;
@@ -2575,9 +2576,18 @@ static bool calc_modifiers_skip_orco(Scene *scene,
last_md->type == eModifierType_Subsurf &&
modifier_isEnabled(scene, last_md, required_mode))
{
+ if (U.opensubdiv_compute_type == USER_OPENSUBDIV_COMPUTE_NONE) {
+ return false;
+ }
+ else if ((ob->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT | OB_MODE_TEXTURE_PAINT)) != 0) {
+ return false;
+ }
+ else if ((DAG_get_eval_flags_for_object(scene, ob) & DAG_EVAL_NEED_CPU) != 0) {
+ return false;
+ }
SubsurfModifierData *smd = (SubsurfModifierData *)last_md;
/* TODO(sergey): Deduplicate this with checks from subsurf_ccg.c. */
- return smd->use_opensubdiv && U.opensubdiv_compute_type != USER_OPENSUBDIV_COMPUTE_NONE;
+ return smd->use_opensubdiv != 0;
}
return false;
}
@@ -3464,13 +3474,17 @@ void DM_calc_loop_tangents(
/* Update active layer index */
uv_index = CustomData_get_layer_index_n(&dm->loopData, CD_MLOOPUV, act_uv_n);
- tan_index = CustomData_get_named_layer_index(&dm->loopData, CD_TANGENT, dm->loopData.layers[uv_index].name);
- CustomData_set_layer_active_index(&dm->loopData, CD_TANGENT, tan_index);
+ if (uv_index != -1) {
+ tan_index = CustomData_get_named_layer_index(&dm->loopData, CD_TANGENT, dm->loopData.layers[uv_index].name);
+ CustomData_set_layer_active_index(&dm->loopData, CD_TANGENT, tan_index);
+ }
/* Update render layer index */
uv_index = CustomData_get_layer_index_n(&dm->loopData, CD_MLOOPUV, ren_uv_n);
- tan_index = CustomData_get_named_layer_index(&dm->loopData, CD_TANGENT, dm->loopData.layers[uv_index].name);
- CustomData_set_layer_render_index(&dm->loopData, CD_TANGENT, tan_index);
+ if (uv_index != -1) {
+ tan_index = CustomData_get_named_layer_index(&dm->loopData, CD_TANGENT, dm->loopData.layers[uv_index].name);
+ CustomData_set_layer_render_index(&dm->loopData, CD_TANGENT, tan_index);
+ }
}
}
@@ -3797,7 +3811,6 @@ void DM_draw_attrib_vertex(DMVertexAttribs *attribs, int a, int index, int vert,
glTexCoord3fv(orco);
else
glVertexAttrib3fv(attribs->orco.gl_index, orco);
- glUniform1i(attribs->orco.gl_info_index, 0);
}
/* uv texture coordinates */
@@ -3816,7 +3829,6 @@ void DM_draw_attrib_vertex(DMVertexAttribs *attribs, int a, int index, int vert,
glTexCoord2fv(uv);
else
glVertexAttrib2fv(attribs->tface[b].gl_index, uv);
- glUniform1i(attribs->tface[b].gl_info_index, 0);
}
/* vertex colors */
@@ -3832,17 +3844,33 @@ void DM_draw_attrib_vertex(DMVertexAttribs *attribs, int a, int index, int vert,
}
glVertexAttrib4fv(attribs->mcol[b].gl_index, col);
- glUniform1i(attribs->mcol[b].gl_info_index, GPU_ATTR_INFO_SRGB);
}
/* tangent for normal mapping */
for (b = 0; b < attribs->tottang; b++) {
if (attribs->tang[b].array) {
/*const*/ float (*array)[4] = attribs->tang[b].array;
- const float *tang = (array) ? array[a * 4 + vert] : zero;
+ const float *tang = (array) ? array[loop] : zero;
glVertexAttrib4fv(attribs->tang[b].gl_index, tang);
}
- glUniform1i(attribs->tang[b].gl_info_index, 0);
+ }
+}
+
+void DM_draw_attrib_vertex_uniforms(const DMVertexAttribs *attribs)
+{
+ int i;
+ if (attribs->totorco) {
+ glUniform1i(attribs->orco.gl_info_index, 0);
+ }
+ for (i = 0; i < attribs->tottface; i++) {
+ glUniform1i(attribs->tface[i].gl_info_index, 0);
+ }
+ for (i = 0; i < attribs->totmcol; i++) {
+ glUniform1i(attribs->mcol[i].gl_info_index, GPU_ATTR_INFO_SRGB);
+ }
+
+ for (i = 0; i < attribs->tottang; i++) {
+ glUniform1i(attribs->tang[i].gl_info_index, 0);
}
}
diff --git a/source/blender/blenkernel/intern/blender_copybuffer.c b/source/blender/blenkernel/intern/blender_copybuffer.c
index ad01fac2144..a4c28121040 100644
--- a/source/blender/blenkernel/intern/blender_copybuffer.c
+++ b/source/blender/blenkernel/intern/blender_copybuffer.c
@@ -85,6 +85,31 @@ bool BKE_copybuffer_save(Main *bmain_src, const char *filename, ReportList *repo
return retval;
}
+bool BKE_copybuffer_read(Main *bmain_dst, const char *libname, ReportList *reports)
+{
+ BlendHandle *bh = BLO_blendhandle_from_file(libname, reports);
+ if (bh == NULL) {
+ /* Error reports will have been made by BLO_blendhandle_from_file(). */
+ return false;
+ }
+ /* Here appending/linking starts. */
+ Main *mainl = BLO_library_link_begin(bmain_dst, &bh, libname);
+ BLO_library_link_copypaste(mainl, bh);
+ BLO_library_link_end(mainl, &bh, 0, NULL, NULL);
+ /* Mark all library linked objects to be updated. */
+ BKE_main_lib_objects_recalc_all(bmain_dst);
+ IMB_colormanagement_check_file_config(bmain_dst);
+ /* Append, rather than linking. */
+ Library *lib = BLI_findstring(&bmain_dst->library, libname, offsetof(Library, filepath));
+ BKE_library_make_local(bmain_dst, lib, true, false);
+ /* Important we unset, otherwise these object wont
+ * link into other scenes from this blend file.
+ */
+ BKE_main_id_tag_all(bmain_dst, LIB_TAG_PRE_EXISTING, false);
+ BLO_blendhandle_close(bh);
+ return true;
+}
+
/**
* \return Success.
*/
diff --git a/source/blender/blenkernel/intern/blendfile.c b/source/blender/blenkernel/intern/blendfile.c
index bedf262541f..6da68470ecc 100644
--- a/source/blender/blenkernel/intern/blendfile.c
+++ b/source/blender/blenkernel/intern/blendfile.c
@@ -121,7 +121,12 @@ static void setup_app_data(
LOAD_UNDO,
} mode;
- if (BLI_listbase_is_empty(&bfd->main->screen)) {
+ /* may happen with library files - UNDO file should never have NULL cursccene... */
+ if (ELEM(NULL, bfd->curscreen, bfd->curscene)) {
+ BKE_report(reports, RPT_WARNING, "Library file, loading empty scene");
+ mode = LOAD_UI_OFF;
+ }
+ else if (BLI_listbase_is_empty(&bfd->main->screen)) {
mode = LOAD_UNDO;
}
else if (G.fileflags & G_FILE_NO_UI) {
@@ -131,14 +136,6 @@ static void setup_app_data(
mode = LOAD_UI;
}
- if (mode != LOAD_UNDO) {
- /* may happen with library files */
- if (ELEM(NULL, bfd->curscreen, bfd->curscene)) {
- BKE_report(reports, RPT_WARNING, "Library file, loading empty scene");
- mode = LOAD_UI_OFF;
- }
- }
-
/* Free all render results, without this stale data gets displayed after loading files */
if (mode != LOAD_UNDO) {
RE_FreeAllRenderResults();
@@ -511,6 +508,10 @@ bool BKE_blendfile_write_partial(
void *path_list_backup = NULL;
const int path_list_flag = (BKE_BPATH_TRAVERSE_SKIP_LIBRARY | BKE_BPATH_TRAVERSE_SKIP_MULTIFILE);
+ /* This is needed to be able to load that file as a real one later
+ * (otherwise main->name will not be set at read time). */
+ BLI_strncpy(bmain_dst->name, bmain_src->name, sizeof(bmain_dst->name));
+
if (write_flags & G_FILE_RELATIVE_REMAP) {
path_list_backup = BKE_bpath_list_backup(bmain_src, path_list_flag);
}
diff --git a/source/blender/blenkernel/intern/cdderivedmesh.c b/source/blender/blenkernel/intern/cdderivedmesh.c
index 2d06bc88aa7..f2dd2a3fcf6 100644
--- a/source/blender/blenkernel/intern/cdderivedmesh.c
+++ b/source/blender/blenkernel/intern/cdderivedmesh.c
@@ -941,8 +941,10 @@ static void cdDM_drawMappedFacesGLSL(
matnr = new_matnr;
do_draw = setMaterial(matnr + 1, &gattribs);
- if (do_draw)
+ if (do_draw) {
DM_vertex_attributes_from_gpu(dm, &gattribs, &attribs);
+ DM_draw_attrib_vertex_uniforms(&attribs);
+ }
glBegin(GL_TRIANGLES);
}
@@ -1210,6 +1212,7 @@ static void cdDM_drawMappedFacesMat(
setMaterial(userData, matnr = new_matnr, &gattribs);
DM_vertex_attributes_from_gpu(dm, &gattribs, &attribs);
+ DM_draw_attrib_vertex_uniforms(&attribs);
glBegin(GL_TRIANGLES);
}
diff --git a/source/blender/blenkernel/intern/editderivedmesh.c b/source/blender/blenkernel/intern/editderivedmesh.c
index 1aba76baa2c..e51a3c3c4b0 100644
--- a/source/blender/blenkernel/intern/editderivedmesh.c
+++ b/source/blender/blenkernel/intern/editderivedmesh.c
@@ -1410,24 +1410,6 @@ static void emDM_drawMappedFacesTex(
emDM_drawFacesTex_common(dm, NULL, setDrawOptions, compareDrawOptions, userData);
}
-static void emdm_pass_attrib_update_uniforms(const DMVertexAttribs *attribs)
-{
- int i;
- if (attribs->totorco) {
- glUniform1i(attribs->orco.gl_info_index, 0);
- }
- for (i = 0; i < attribs->tottface; i++) {
- glUniform1i(attribs->tface[i].gl_info_index, 0);
- }
- for (i = 0; i < attribs->totmcol; i++) {
- glUniform1i(attribs->mcol[i].gl_info_index, GPU_ATTR_INFO_SRGB);
- }
-
- for (i = 0; i < attribs->tottang; i++) {
- glUniform1i(attribs->tang[i].gl_info_index, 0);
- }
-}
-
/**
* \note
*
@@ -1548,7 +1530,7 @@ static void emDM_drawMappedFacesGLSL(
do_draw = setMaterial(matnr = new_matnr, &gattribs);
if (do_draw) {
DM_vertex_attributes_from_gpu(dm, &gattribs, &attribs);
- emdm_pass_attrib_update_uniforms(&attribs);
+ DM_draw_attrib_vertex_uniforms(&attribs);
if (UNLIKELY(attribs.tottang && bm->elem_index_dirty & BM_LOOP)) {
BM_mesh_elem_index_ensure(bm, BM_LOOP);
}
diff --git a/source/blender/blenkernel/intern/library_remap.c b/source/blender/blenkernel/intern/library_remap.c
index 61831d92766..bdfe951a501 100644
--- a/source/blender/blenkernel/intern/library_remap.c
+++ b/source/blender/blenkernel/intern/library_remap.c
@@ -153,7 +153,7 @@ enum {
ID_REMAP_IS_USER_ONE_SKIPPED = 1 << 1, /* There was some skipped 'user_one' usages of old_id. */
};
-static int foreach_libblock_remap_callback(void *user_data, ID *UNUSED(id_self), ID **id_p, int cb_flag)
+static int foreach_libblock_remap_callback(void *user_data, ID *id_self, ID **id_p, int cb_flag)
{
IDRemap *id_remap_data = user_data;
ID *old_id = id_remap_data->old_id;
@@ -211,6 +211,7 @@ static int foreach_libblock_remap_callback(void *user_data, ID *UNUSED(id_self),
else {
if (!is_never_null) {
*id_p = new_id;
+ DAG_id_tag_update(id_self, OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME);
}
if (cb_flag & IDWALK_USER) {
id_us_min(old_id);
diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c
index ba3aef81514..7714c71681a 100644
--- a/source/blender/blenkernel/intern/mesh.c
+++ b/source/blender/blenkernel/intern/mesh.c
@@ -2333,7 +2333,7 @@ Mesh *BKE_mesh_new_from_object(
DerivedMesh *dm;
/* CustomDataMask mask = CD_MASK_BAREMESH|CD_MASK_MTFACE|CD_MASK_MCOL; */
CustomDataMask mask = CD_MASK_MESH; /* this seems more suitable, exporter,
- * for example, needs CD_MASK_MDEFORMVERT */
+ * for example, needs CD_MASK_MDEFORMVERT */
if (calc_undeformed)
mask |= CD_MASK_ORCO;
diff --git a/source/blender/blenkernel/intern/subsurf_ccg.c b/source/blender/blenkernel/intern/subsurf_ccg.c
index 60f1eb6b98c..6d57c5f09e8 100644
--- a/source/blender/blenkernel/intern/subsurf_ccg.c
+++ b/source/blender/blenkernel/intern/subsurf_ccg.c
@@ -2820,12 +2820,13 @@ static void ccgDM_drawMappedFacesGLSL(DerivedMesh *dm,
int matnr = -1;
int do_draw = 0;
-#define PASSATTRIB(dx, dy, vert) { \
- if (attribs.totorco) \
- index = getFaceIndex(ss, f, S, x + dx, y + dy, edgeSize, gridSize); \
- else \
- index = 0; \
- DM_draw_attrib_vertex(&attribs, a, index, vert, ((a) * 4) + vert); \
+#define PASSATTRIB(dx, dy, vert) { \
+ if (attribs.totorco) \
+ index = getFaceIndex(ss, f, S, x + dx, y + dy, edgeSize, gridSize); \
+ else \
+ index = 0; \
+ DM_draw_attrib_vertex(&attribs, a, index, vert, ((a) * 4) + vert); \
+ DM_draw_attrib_vertex_uniforms(&attribs); \
} (void)0
totpoly = ccgSubSurf_getNumFaces(ss);
@@ -3194,24 +3195,62 @@ static void ccgDM_drawMappedFacesMat(DerivedMesh *dm,
#ifdef WITH_OPENSUBDIV
if (ccgdm->useGpuBackend) {
- int new_matnr;
- bool draw_smooth;
+ const int level = ccgSubSurf_getSubdivisionLevels(ss);
+ const int face_side = 1 << level;
+ const int grid_side = 1 << (level - 1);
+ const int face_patches = face_side * face_side;
+ const int grid_patches = grid_side * grid_side;
+ const int num_base_faces = ccgSubSurf_getNumGLMeshBaseFaces(ss);
+ int current_patch = 0;
+ int mat_nr = -1;
+ bool draw_smooth = false;
+ int start_draw_patch = -1, num_draw_patches = 0;
GPU_draw_update_fvar_offset(dm);
if (UNLIKELY(ccgSubSurf_prepareGLMesh(ss, true, -1) == false)) {
return;
}
- /* TODO(sergey): Single matierial currently. */
- if (faceFlags) {
- draw_smooth = (faceFlags[0].flag & ME_SMOOTH);
- new_matnr = (faceFlags[0].mat_nr + 1);
+ for (i = 0; i < num_base_faces; ++i) {
+ const int num_face_verts = ccgSubSurf_getNumGLMeshBaseFaceVerts(ss, i);
+ const int num_patches = (num_face_verts == 4) ? face_patches
+ : num_face_verts * grid_patches;
+ int new_matnr;
+ bool new_draw_smooth;
+
+ if (faceFlags) {
+ new_draw_smooth = (faceFlags[i].flag & ME_SMOOTH);
+ new_matnr = (faceFlags[i].mat_nr + 1);
+ }
+ else {
+ new_draw_smooth = true;
+ new_matnr = 1;
+ }
+ if (new_draw_smooth != draw_smooth || new_matnr != mat_nr) {
+ if (num_draw_patches != 0) {
+ setMaterial(userData, mat_nr, &gattribs);
+ glShadeModel(draw_smooth ? GL_SMOOTH : GL_FLAT);
+ ccgSubSurf_drawGLMesh(ss,
+ true,
+ start_draw_patch,
+ num_draw_patches);
+ }
+ start_draw_patch = current_patch;
+ num_draw_patches = num_patches;
+ mat_nr = new_matnr;
+ draw_smooth = new_draw_smooth;
+ }
+ else {
+ num_draw_patches += num_patches;
+ }
+ current_patch += num_patches;
}
- else {
- draw_smooth = true;
- new_matnr = 1;
+ if (num_draw_patches != 0) {
+ setMaterial(userData, mat_nr, &gattribs);
+ glShadeModel(draw_smooth ? GL_SMOOTH : GL_FLAT);
+ ccgSubSurf_drawGLMesh(ss,
+ true,
+ start_draw_patch,
+ num_draw_patches);
}
- glShadeModel(draw_smooth ? GL_SMOOTH : GL_FLAT);
- setMaterial(userData, new_matnr, &gattribs);
- ccgSubSurf_drawGLMesh(ss, true, -1, -1);
glShadeModel(GL_SMOOTH);
return;
}
@@ -3222,12 +3261,13 @@ static void ccgDM_drawMappedFacesMat(DerivedMesh *dm,
matnr = -1;
-#define PASSATTRIB(dx, dy, vert) { \
- if (attribs.totorco) \
- index = getFaceIndex(ss, f, S, x + dx, y + dy, edgeSize, gridSize); \
- else \
- index = 0; \
- DM_draw_attrib_vertex(&attribs, a, index, vert, ((a) * 4) + vert); \
+#define PASSATTRIB(dx, dy, vert) { \
+ if (attribs.totorco) \
+ index = getFaceIndex(ss, f, S, x + dx, y + dy, edgeSize, gridSize); \
+ else \
+ index = 0; \
+ DM_draw_attrib_vertex(&attribs, a, index, vert, ((a) * 4) + vert); \
+ DM_draw_attrib_vertex_uniforms(&attribs); \
} (void)0
totface = ccgSubSurf_getNumFaces(ss);
diff --git a/source/blender/blenlib/intern/task.c b/source/blender/blenlib/intern/task.c
index bd7b7f9cdbd..9d4d40e1eae 100644
--- a/source/blender/blenlib/intern/task.c
+++ b/source/blender/blenlib/intern/task.c
@@ -780,9 +780,10 @@ BLI_INLINE bool parallel_range_next_iter_get(
ParallelRangeState * __restrict state,
int * __restrict iter, int * __restrict count)
{
- uint32_t previter = atomic_fetch_and_add_uint32((uint32_t *)(&state->iter), state->chunk_size);
+ uint32_t uval = atomic_fetch_and_add_uint32((uint32_t *)(&state->iter), state->chunk_size);
+ int previter = *(int32_t*)&uval;
- *iter = (int)previter;
+ *iter = previter;
*count = max_ii(0, min_ii(state->chunk_size, state->stop - previter));
return (previter < state->stop);
diff --git a/source/blender/bmesh/tools/bmesh_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c
index 1dfb9dee8eb..45fbfdd1767 100644
--- a/source/blender/bmesh/tools/bmesh_bevel.c
+++ b/source/blender/bmesh/tools/bmesh_bevel.c
@@ -2194,10 +2194,13 @@ static void fill_vmesh_fracs(VMesh *vm, float *frac, int i)
total += len_v3v3(mesh_vert(vm, i, 0, k)->co, mesh_vert(vm, i, 0, k + 1)->co);
frac[k + 1] = total;
}
- if (total > BEVEL_EPSILON) {
+ if (total > 0.0f) {
for (k = 1; k <= ns; k++)
frac[k] /= total;
}
+ else {
+ frac[ns] = 1.0f;
+ }
}
/* Like fill_vmesh_fracs but want fractions for profile points of bndv, with ns segments */
@@ -2215,11 +2218,14 @@ static void fill_profile_fracs(BevelParams *bp, BoundVert *bndv, float *frac, in
frac[k + 1] = total;
copy_v3_v3(co, nextco);
}
- if (total > BEVEL_EPSILON) {
+ if (total > 0.0f) {
for (k = 1; k <= ns; k++) {
frac[k] /= total;
}
}
+ else {
+ frac[ns] = 1.0f;
+ }
}
/* Return i such that frac[i] <= f <= frac[i + 1], where frac[n] == 1.0
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
index f016b379327..232c801563e 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
@@ -113,6 +113,32 @@ namespace DEG {
/* ***************** */
/* Relations Builder */
+/* TODO(sergey): This is somewhat weak, but we don't want neither false-positive
+ * time dependencies nor special exceptions in the depsgraph evaluation.
+ */
+static bool python_driver_depends_on_time(ChannelDriver *driver)
+{
+ if (driver->expression[0] == '\0') {
+ /* Empty expression depends on nothing. */
+ return false;
+ }
+ if (strchr(driver->expression, '(') != NULL) {
+ /* Function calls are considered dependent on a time. */
+ return true;
+ }
+ if (strstr(driver->expression, "time") != NULL) {
+ /* Variable `time` depends on time. */
+ /* TODO(sergey): This is a bit weak, but not sure about better way of
+ * handling this.
+ */
+ return true;
+ }
+ /* Possible indirect time relation s should be handled via variable
+ * targets.
+ */
+ return false;
+}
+
/* **** General purpose functions **** */
RNAPathKey::RNAPathKey(ID *id, const char *path) :
@@ -994,7 +1020,9 @@ void DepsgraphRelationBuilder::build_driver(ID *id, FCurve *fcu)
* so for now we'll be quite conservative here about optimization and consider
* all python drivers to be depending on time.
*/
- if (driver->type == DRIVER_TYPE_PYTHON) {
+ if ((driver->type == DRIVER_TYPE_PYTHON) &&
+ python_driver_depends_on_time(driver))
+ {
TimeSourceKey time_src_key;
add_relation(time_src_key, driver_key, DEPSREL_TYPE_TIME, "[TimeSrc -> Driver]");
}
diff --git a/source/blender/depsgraph/intern/eval/deg_eval.cc b/source/blender/depsgraph/intern/eval/deg_eval.cc
index f8cca5393e2..c3fd202d832 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval.cc
+++ b/source/blender/depsgraph/intern/eval/deg_eval.cc
@@ -304,7 +304,7 @@ static void schedule_node(TaskPool *pool, Depsgraph *graph, unsigned int layers,
deg_task_run_func,
node,
false,
- TASK_PRIORITY_HIGH,
+ TASK_PRIORITY_LOW,
thread_id);
}
}
diff --git a/source/blender/editors/animation/anim_channels_defines.c b/source/blender/editors/animation/anim_channels_defines.c
index fe5c7c555f9..40dfa01ced8 100644
--- a/source/blender/editors/animation/anim_channels_defines.c
+++ b/source/blender/editors/animation/anim_channels_defines.c
@@ -528,7 +528,10 @@ static bool acf_scene_setting_valid(bAnimContext *ac, bAnimListElem *UNUSED(ale)
case ACHANNEL_SETTING_SELECT:
case ACHANNEL_SETTING_EXPAND:
return true;
-
+
+ case ACHANNEL_SETTING_ALWAYS_VISIBLE:
+ return false;
+
default:
return false;
}
@@ -554,7 +557,7 @@ static int acf_scene_setting_flag(bAnimContext *UNUSED(ac), eAnimChannel_Setting
case ACHANNEL_SETTING_VISIBLE: /* visible (only in Graph Editor) */
*neg = true;
return ADT_CURVES_NOT_VISIBLE;
-
+
default: /* unsupported */
return 0;
}
@@ -681,7 +684,10 @@ static bool acf_object_setting_valid(bAnimContext *ac, bAnimListElem *ale, eAnim
case ACHANNEL_SETTING_SELECT:
case ACHANNEL_SETTING_EXPAND:
return true;
-
+
+ case ACHANNEL_SETTING_ALWAYS_VISIBLE:
+ return ((ac) && (ac->spacetype == SPACE_IPO) && (ob->adt));
+
default:
return false;
}
@@ -707,7 +713,10 @@ static int acf_object_setting_flag(bAnimContext *UNUSED(ac), eAnimChannel_Settin
case ACHANNEL_SETTING_VISIBLE: /* visible (only in Graph Editor) */
*neg = true;
return ADT_CURVES_NOT_VISIBLE;
-
+
+ case ACHANNEL_SETTING_ALWAYS_VISIBLE:
+ return ADT_CURVES_ALWAYS_VISIBLE;
+
default: /* unsupported */
return 0;
}
@@ -731,6 +740,7 @@ static void *acf_object_setting_ptr(bAnimListElem *ale, eAnimChannel_Settings se
case ACHANNEL_SETTING_MUTE: /* mute (only in NLA) */
case ACHANNEL_SETTING_VISIBLE: /* visible (for Graph Editor only) */
+ case ACHANNEL_SETTING_ALWAYS_VISIBLE:
if (ob->adt)
return GET_ACF_FLAG_PTR(ob->adt->flag, type);
return NULL;
@@ -838,7 +848,10 @@ static bool acf_group_setting_valid(bAnimContext *ac, bAnimListElem *UNUSED(ale)
/* conditionally supported */
case ACHANNEL_SETTING_VISIBLE: /* Only available in Graph Editor */
return (ac->spacetype == SPACE_IPO);
-
+
+ case ACHANNEL_SETTING_ALWAYS_VISIBLE:
+ return (ac->spacetype == SPACE_IPO);
+
default: /* always supported */
return true;
}
@@ -878,7 +891,10 @@ static int acf_group_setting_flag(bAnimContext *ac, eAnimChannel_Settings settin
case ACHANNEL_SETTING_VISIBLE: /* visibility - graph editor */
*neg = 1;
return AGRP_NOTVISIBLE;
-
+
+ case ACHANNEL_SETTING_ALWAYS_VISIBLE:
+ return ADT_CURVES_ALWAYS_VISIBLE;
+
default:
/* this shouldn't happen */
return 0;
@@ -964,7 +980,10 @@ static bool acf_fcurve_setting_valid(bAnimContext *ac, bAnimListElem *ale, eAnim
case ACHANNEL_SETTING_VISIBLE: /* Only available in Graph Editor */
return (ac->spacetype == SPACE_IPO);
-
+
+ case ACHANNEL_SETTING_ALWAYS_VISIBLE:
+ return false;
+
/* always available */
default:
return true;
@@ -3670,7 +3689,9 @@ void ANIM_channel_draw(bAnimContext *ac, bAnimListElem *ale, float yminc, float
* - in Grease Pencil mode, color swatches for layer color
*/
if (ac->sl) {
- if ((ac->spacetype == SPACE_IPO) && acf->has_setting(ac, ale, ACHANNEL_SETTING_VISIBLE)) {
+ if ((ac->spacetype == SPACE_IPO) &&
+ (acf->has_setting(ac, ale, ACHANNEL_SETTING_VISIBLE) ||
+ acf->has_setting(ac, ale, ACHANNEL_SETTING_ALWAYS_VISIBLE))) {
/* for F-Curves, draw color-preview of curve behind checkbox */
if (ELEM(ale->type, ANIMTYPE_FCURVE, ANIMTYPE_NLACURVE)) {
FCurve *fcu = (FCurve *)ale->data;
@@ -3684,9 +3705,13 @@ void ANIM_channel_draw(bAnimContext *ac, bAnimListElem *ale, float yminc, float
*/
glRectf(offset, yminc, offset + ICON_WIDTH, ymaxc);
}
-
/* icon is drawn as widget now... */
- offset += ICON_WIDTH;
+ if (acf->has_setting(ac, ale, ACHANNEL_SETTING_VISIBLE)) {
+ offset += ICON_WIDTH;
+ }
+ if (acf->has_setting(ac, ale, ACHANNEL_SETTING_ALWAYS_VISIBLE)) {
+ offset += ICON_WIDTH;
+ }
}
else if ((ac->spacetype == SPACE_NLA) && acf->has_setting(ac, ale, ACHANNEL_SETTING_SOLO)) {
/* just skip - drawn as widget now */
@@ -4031,6 +4056,11 @@ static void draw_setting_widget(bAnimContext *ac, bAnimListElem *ale, const bAni
tooltip = TIP_("Channels are visible in Graph Editor for editing");
break;
+ case ACHANNEL_SETTING_ALWAYS_VISIBLE:
+ icon = ICON_UNPINNED;
+ tooltip = TIP_("Channels are visible in Graph Editor for editing");
+ break;
+
case ACHANNEL_SETTING_MOD_OFF: /* modifiers disabled */
icon = ICON_MODIFIER;
usetoggle = false;
@@ -4140,6 +4170,7 @@ static void draw_setting_widget(bAnimContext *ac, bAnimListElem *ale, const bAni
case ACHANNEL_SETTING_MUTE: /* General - muting flags */
case ACHANNEL_SETTING_PINNED: /* NLA Actions - 'map/nomap' */
case ACHANNEL_SETTING_MOD_OFF:
+ case ACHANNEL_SETTING_ALWAYS_VISIBLE:
UI_but_funcN_set(but, achannel_setting_flush_widget_cb, MEM_dupallocN(ale), SET_INT_IN_POINTER(setting));
break;
@@ -4203,10 +4234,20 @@ void ANIM_channel_draw_widgets(const bContext *C, bAnimContext *ac, bAnimListEle
* - in Grease Pencil mode, color swatches for layer color
*/
if (ac->sl) {
- if ((ac->spacetype == SPACE_IPO) && acf->has_setting(ac, ale, ACHANNEL_SETTING_VISIBLE)) {
+ if ((ac->spacetype == SPACE_IPO) &&
+ (acf->has_setting(ac, ale, ACHANNEL_SETTING_VISIBLE) ||
+ acf->has_setting(ac, ale, ACHANNEL_SETTING_ALWAYS_VISIBLE)))
+ {
+ /* pin toggle */
+ if (acf->has_setting(ac, ale, ACHANNEL_SETTING_ALWAYS_VISIBLE)) {
+ draw_setting_widget(ac, ale, acf, block, offset, ymid, ACHANNEL_SETTING_ALWAYS_VISIBLE);
+ offset += ICON_WIDTH;
+ }
/* visibility toggle */
- draw_setting_widget(ac, ale, acf, block, offset, ymid, ACHANNEL_SETTING_VISIBLE);
- offset += ICON_WIDTH;
+ if (acf->has_setting(ac, ale, ACHANNEL_SETTING_VISIBLE)) {
+ draw_setting_widget(ac, ale, acf, block, offset, ymid, ACHANNEL_SETTING_VISIBLE);
+ offset += ICON_WIDTH;
+ }
}
else if ((ac->spacetype == SPACE_NLA) && acf->has_setting(ac, ale, ACHANNEL_SETTING_SOLO)) {
/* 'solo' setting for NLA Tracks */
diff --git a/source/blender/editors/animation/anim_channels_edit.c b/source/blender/editors/animation/anim_channels_edit.c
index 20e5bec44a3..0a6ecd7f026 100644
--- a/source/blender/editors/animation/anim_channels_edit.c
+++ b/source/blender/editors/animation/anim_channels_edit.c
@@ -431,7 +431,11 @@ void ANIM_flush_setting_anim_channels(bAnimContext *ac, ListBase *anim_data, bAn
/* sanity check */
if (ELEM(NULL, anim_data, anim_data->first))
return;
-
+
+ if (setting == ACHANNEL_SETTING_ALWAYS_VISIBLE) {
+ return;
+ }
+
/* find the channel that got changed */
for (ale = anim_data->first; ale; ale = ale->next) {
/* compare data, and type as main way of identifying the channel */
diff --git a/source/blender/editors/animation/anim_filter.c b/source/blender/editors/animation/anim_filter.c
index df7b6c53b97..c41251578e7 100644
--- a/source/blender/editors/animation/anim_filter.c
+++ b/source/blender/editors/animation/anim_filter.c
@@ -938,6 +938,9 @@ static bAnimListElem *make_new_animlistelem(void *data, short datatype, ID *owne
*/
static bool skip_fcurve_selected_data(bDopeSheet *ads, FCurve *fcu, ID *owner_id, int filter_mode)
{
+ if (fcu->grp != NULL && fcu->grp->flag & ADT_CURVES_ALWAYS_VISIBLE) {
+ return false;
+ }
/* hidden items should be skipped if we only care about visible data, but we aren't interested in hidden stuff */
const bool skip_hidden = (filter_mode & ANIMFILTER_DATA_VISIBLE) && !(ads->filterflag & ADS_FILTER_INCL_HIDDEN);
@@ -2753,7 +2756,12 @@ static bool animdata_filter_base_is_ok(bDopeSheet *ads, Scene *scene, Base *base
if ((ob->adt) && (ob->adt->flag & ADT_CURVES_NOT_VISIBLE))
return false;
}
-
+
+ /* Pinned curves are visible regardless of selection flags. */
+ if ((ob->adt) && (ob->adt->flag & ADT_CURVES_ALWAYS_VISIBLE)) {
+ return true;
+ }
+
/* check selection and object type filters */
if ((ads->filterflag & ADS_FILTER_ONLYSEL) && !((base->flag & SELECT) /*|| (base == sce->basact)*/)) {
/* only selected should be shown */
diff --git a/source/blender/editors/armature/armature_edit.c b/source/blender/editors/armature/armature_edit.c
index 354b748e129..ece0f18e96e 100644
--- a/source/blender/editors/armature/armature_edit.c
+++ b/source/blender/editors/armature/armature_edit.c
@@ -410,6 +410,7 @@ static int armature_calc_roll_exec(bContext *C, wmOperator *op)
if (type < 3) vec[type] = 1.0f;
else vec[type - 2] = -1.0f;
mul_m3_v3(imat, vec);
+ normalize_v3(vec);
}
if (axis_flip) negate_v3(vec);
diff --git a/source/blender/editors/armature/armature_intern.h b/source/blender/editors/armature/armature_intern.h
index 02aefce3464..b39b4bd81ee 100644
--- a/source/blender/editors/armature/armature_intern.h
+++ b/source/blender/editors/armature/armature_intern.h
@@ -201,6 +201,7 @@ void POSELIB_OT_action_sanitize(struct wmOperatorType *ot);
void POSELIB_OT_pose_add(struct wmOperatorType *ot);
void POSELIB_OT_pose_remove(struct wmOperatorType *ot);
void POSELIB_OT_pose_rename(struct wmOperatorType *ot);
+void POSELIB_OT_pose_move(struct wmOperatorType *ot);
void POSELIB_OT_browse_interactive(struct wmOperatorType *ot);
void POSELIB_OT_apply_pose(struct wmOperatorType *ot);
diff --git a/source/blender/editors/armature/armature_ops.c b/source/blender/editors/armature/armature_ops.c
index ed5f96a5829..5622cd0437d 100644
--- a/source/blender/editors/armature/armature_ops.c
+++ b/source/blender/editors/armature/armature_ops.c
@@ -151,6 +151,7 @@ void ED_operatortypes_armature(void)
WM_operatortype_append(POSELIB_OT_pose_add);
WM_operatortype_append(POSELIB_OT_pose_remove);
WM_operatortype_append(POSELIB_OT_pose_rename);
+ WM_operatortype_append(POSELIB_OT_pose_move);
WM_operatortype_append(POSELIB_OT_new);
WM_operatortype_append(POSELIB_OT_unlink);
diff --git a/source/blender/editors/armature/pose_lib.c b/source/blender/editors/armature/pose_lib.c
index d9a3efa765c..2012237ed4e 100644
--- a/source/blender/editors/armature/pose_lib.c
+++ b/source/blender/editors/armature/pose_lib.c
@@ -733,6 +733,102 @@ void POSELIB_OT_pose_rename(wmOperatorType *ot)
RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE);
}
+static int poselib_move_exec(bContext *C, wmOperator *op)
+{
+ Object *ob = get_poselib_object(C);
+ bAction *act = (ob) ? ob->poselib : NULL;
+ TimeMarker *marker;
+ int marker_index;
+ int dir;
+ PropertyRNA *prop;
+
+ /* check if valid poselib */
+ if (act == NULL) {
+ BKE_report(op->reports, RPT_ERROR, "Object does not have pose lib data");
+ return OPERATOR_CANCELLED;
+ }
+
+ prop = RNA_struct_find_property(op->ptr, "pose");
+ if (RNA_property_is_set(op->ptr, prop)) {
+ marker_index = RNA_property_enum_get(op->ptr, prop);
+ }
+ else {
+ marker_index = act->active_marker - 1;
+ }
+
+ /* get index (and pointer) of pose to remove */
+ marker = BLI_findlink(&act->markers, marker_index);
+ if (marker == NULL) {
+ BKE_reportf(op->reports, RPT_ERROR, "Invalid pose specified %d", marker_index);
+ return OPERATOR_CANCELLED;
+ }
+
+ dir = RNA_enum_get(op->ptr, "direction");
+
+ /* move pose */
+ if (dir == 1) { /* up */
+ void *prev = marker->prev;
+
+ if (prev == NULL)
+ return OPERATOR_FINISHED;
+
+ BLI_remlink(&act->markers, marker);
+ BLI_insertlinkbefore(&act->markers, prev, marker);
+ }
+ else { /* down */
+ void *next = marker->next;
+
+ if (next == NULL)
+ return OPERATOR_FINISHED;
+
+ BLI_remlink(&act->markers, marker);
+ BLI_insertlinkafter(&act->markers, next, marker);
+ }
+
+ act->active_marker = marker_index - dir + 1;
+
+ /* send notifiers for this - using keyframe editing notifiers, since action
+ * may be being shown in anim editors as active action
+ */
+ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
+
+ /* done */
+ return OPERATOR_FINISHED;
+}
+
+void POSELIB_OT_pose_move(wmOperatorType *ot)
+{
+ PropertyRNA *prop;
+ static EnumPropertyItem pose_lib_pose_move[] = {
+ {1, "UP", 0, "Up", ""},
+ {-1, "DOWN", 0, "Down", ""},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ /* identifiers */
+ ot->name = "PoseLib Move Pose";
+ ot->idname = "POSELIB_OT_pose_move";
+ ot->description = "Move the pose up or down in the active Pose Library";
+
+ /* api callbacks */
+ ot->invoke = WM_menu_invoke;
+ ot->exec = poselib_move_exec;
+ ot->poll = has_poselib_pose_data_for_editing_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ prop = RNA_def_enum(ot->srna, "pose", DummyRNA_NULL_items, 0, "Pose", "The pose to move");
+ RNA_def_enum_funcs(prop, poselib_stored_pose_itemf);
+ RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE);
+ ot->prop = prop;
+
+ RNA_def_enum(ot->srna, "direction", pose_lib_pose_move, 0, "Direction", "Direction to move, UP or DOWN");
+}
+
+
+
/* ************************************************************* */
/* Pose-Lib Browsing/Previewing Operator */
diff --git a/source/blender/editors/armature/pose_transform.c b/source/blender/editors/armature/pose_transform.c
index df906a3638a..fa7850bc2f4 100644
--- a/source/blender/editors/armature/pose_transform.c
+++ b/source/blender/editors/armature/pose_transform.c
@@ -39,7 +39,9 @@
#include "BKE_animsys.h"
#include "BKE_action.h"
+#include "BKE_appdir.h"
#include "BKE_armature.h"
+#include "BKE_blender_copybuffer.h"
#include "BKE_context.h"
#include "BKE_deform.h"
#include "BKE_depsgraph.h"
@@ -248,29 +250,6 @@ void POSE_OT_visual_transform_apply(wmOperatorType *ot)
/* ********************************************** */
/* Copy/Paste */
-/* Global copy/paste buffer for pose - cleared on start/end session + before every copy operation */
-static bPose *g_posebuf = NULL;
-
-void ED_clipboard_posebuf_free(void)
-{
- if (g_posebuf) {
- bPoseChannel *pchan;
-
- for (pchan = g_posebuf->chanbase.first; pchan; pchan = pchan->next) {
- if (pchan->prop) {
- IDP_FreeProperty(pchan->prop);
- MEM_freeN(pchan->prop);
- }
- }
-
- /* was copied without constraints */
- BLI_freelistN(&g_posebuf->chanbase);
- MEM_freeN(g_posebuf);
- }
-
- g_posebuf = NULL;
-}
-
/* This function is used to indicate that a bone is selected
* and needs to be included in copy buffer (used to be for inserting keys)
*/
@@ -435,22 +414,24 @@ static bPoseChannel *pose_bone_do_paste(Object *ob, bPoseChannel *chan, const bo
static int pose_copy_exec(bContext *C, wmOperator *op)
{
+ Main *bmain = CTX_data_main(C);
Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
-
- /* sanity checking */
+ char str[FILE_MAX];
+ /* Sanity checking. */
if (ELEM(NULL, ob, ob->pose)) {
BKE_report(op->reports, RPT_ERROR, "No pose to copy");
return OPERATOR_CANCELLED;
}
-
- /* free existing pose buffer */
- ED_clipboard_posebuf_free();
-
- /* sets chan->flag to POSE_KEY if bone selected, then copy those bones to the buffer */
- set_pose_keys(ob);
- BKE_pose_copy_data(&g_posebuf, ob->pose, 0);
-
-
+ /* Sets chan->flag to POSE_KEY if bone selected. */
+ set_pose_keys(ob);
+ /* Store the whole object to the copy buffer because pose can't be
+ * existing on it's own.
+ */
+ BKE_copybuffer_begin(bmain);
+ BKE_copybuffer_tag_ID(&ob->id);
+ BLI_make_file_string("/", str, BKE_tempdir_base(), "copybuffer_pose.blend");
+ BKE_copybuffer_save(bmain, str, op->reports);
+ BKE_report(op->reports, RPT_INFO, "Copied pose to buffer");
return OPERATOR_FINISHED;
}
@@ -478,44 +459,60 @@ static int pose_paste_exec(bContext *C, wmOperator *op)
bPoseChannel *chan;
const bool flip = RNA_boolean_get(op->ptr, "flipped");
bool selOnly = RNA_boolean_get(op->ptr, "selected_mask");
-
- /* get KeyingSet to use */
+ /* Get KeyingSet to use. */
KeyingSet *ks = ANIM_get_keyingset_for_autokeying(scene, ANIM_KS_WHOLE_CHARACTER_ID);
-
- /* sanity checks */
- if (ELEM(NULL, ob, ob->pose))
+ /* Sanity checks. */
+ if (ELEM(NULL, ob, ob->pose)) {
return OPERATOR_CANCELLED;
-
- if (g_posebuf == NULL) {
+ }
+ /* Read copy buffer .blend file. */
+ char str[FILE_MAX];
+ Main *tmp_bmain = BKE_main_new();
+ BLI_make_file_string("/", str, BKE_tempdir_base(), "copybuffer_pose.blend");
+ if (!BKE_copybuffer_read(tmp_bmain, str, op->reports)) {
BKE_report(op->reports, RPT_ERROR, "Copy buffer is empty");
+ BKE_main_free(tmp_bmain);
return OPERATOR_CANCELLED;
}
-
- /* if selOnly option is enabled, if user hasn't selected any bones,
- * just go back to default behavior to be more in line with other pose tools
+ /* Make sure data from this file is usable for pose paste. */
+ if (BLI_listbase_count_ex(&tmp_bmain->object, 2) != 1) {
+ BKE_report(op->reports, RPT_ERROR, "Copy buffer is not from pose mode");
+ BKE_main_free(tmp_bmain);
+ return OPERATOR_CANCELLED;
+ }
+ Object *object_from = tmp_bmain->object.first;
+ bPose *pose_from = object_from->pose;
+ if (pose_from == NULL) {
+ BKE_report(op->reports, RPT_ERROR, "Copy buffer has no pose");
+ BKE_main_free(tmp_bmain);
+ return OPERATOR_CANCELLED;
+ }
+ /* If selOnly option is enabled, if user hasn't selected any bones,
+ * just go back to default behavior to be more in line with other
+ * pose tools.
*/
if (selOnly) {
- if (CTX_DATA_COUNT(C, selected_pose_bones) == 0)
- selOnly = 0;
+ if (CTX_DATA_COUNT(C, selected_pose_bones) == 0) {
+ selOnly = false;
+ }
}
-
- /* Safely merge all of the channels in the buffer pose into any existing pose */
- for (chan = g_posebuf->chanbase.first; chan; chan = chan->next) {
+ /* Safely merge all of the channels in the buffer pose into any
+ * existing pose.
+ */
+ for (chan = pose_from->chanbase.first; chan; chan = chan->next) {
if (chan->flag & POSE_KEY) {
- /* try to perform paste on this bone */
+ /* Try to perform paste on this bone. */
bPoseChannel *pchan = pose_bone_do_paste(ob, chan, selOnly, flip);
-
- if (pchan) {
- /* keyframing tagging for successful paste */
+ if (pchan != NULL) {
+ /* Keyframing tagging for successful paste, */
ED_autokeyframe_pchan(C, scene, ob, pchan, ks);
}
}
}
-
- /* Update event for pose and deformation children */
+ BKE_main_free(tmp_bmain);
+ /* Update event for pose and deformation children. */
DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
-
- /* notifiers for updates */
+ /* Notifiers for updates, */
WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
return OPERATOR_FINISHED;
diff --git a/source/blender/editors/gpencil/drawgpencil.c b/source/blender/editors/gpencil/drawgpencil.c
index 4ef76f5ee25..48786e08f85 100644
--- a/source/blender/editors/gpencil/drawgpencil.c
+++ b/source/blender/editors/gpencil/drawgpencil.c
@@ -66,6 +66,7 @@
#include "ED_gpencil.h"
#include "ED_screen.h"
#include "ED_view3d.h"
+#include "ED_space_api.h"
#include "UI_interface_icons.h"
#include "UI_resources.h"
@@ -74,7 +75,6 @@
/* GREASE PENCIL DRAWING */
/* ----- General Defines ------ */
-
/* flags for sflag */
typedef enum eDrawStrokeFlags {
GP_DRAWDATA_NOSTATUS = (1 << 0), /* don't draw status info */
@@ -1338,6 +1338,39 @@ static void gp_draw_onionskins(
}
+/* draw interpolate strokes (used only while operator is running) */
+void ED_gp_draw_interpolation(tGPDinterpolate *tgpi, const int type)
+{
+ tGPDinterpolate_layer *tgpil;
+ float diff_mat[4][4];
+ float color[4];
+
+ int offsx = 0;
+ int offsy = 0;
+ int winx = tgpi->ar->winx;
+ int winy = tgpi->ar->winy;
+
+ UI_GetThemeColor3fv(TH_GP_VERTEX_SELECT, color);
+ color[3] = 0.6f;
+ int dflag = 0;
+ /* if 3d stuff, enable flags */
+ if (type == REGION_DRAW_POST_VIEW) {
+ dflag |= (GP_DRAWDATA_ONLY3D | GP_DRAWDATA_NOSTATUS);
+ }
+
+ /* turn on alpha-blending */
+ glEnable(GL_BLEND);
+ for (tgpil = tgpi->ilayers.first; tgpil; tgpil = tgpil->next) {
+ /* calculate parent position */
+ ED_gpencil_parent_location(tgpil->gpl, diff_mat);
+ if (tgpil->interFrame) {
+ gp_draw_strokes(tgpi->gpd, tgpil->interFrame, offsx, offsy, winx, winy, dflag, false,
+ tgpil->gpl->thickness, 1.0f, color, true, true, diff_mat);
+ }
+ }
+ glDisable(GL_BLEND);
+}
+
/* loop over gpencil data layers, drawing them */
static void gp_draw_data_layers(
bGPDbrush *brush, float alpha, bGPdata *gpd,
diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c
index 9f700e8716c..7175ec3e83d 100644
--- a/source/blender/editors/gpencil/gpencil_edit.c
+++ b/source/blender/editors/gpencil/gpencil_edit.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 *****
*
@@ -74,6 +74,8 @@
#include "ED_object.h"
#include "ED_screen.h"
#include "ED_view3d.h"
+#include "ED_screen.h"
+#include "ED_space_api.h"
#include "gpencil_intern.h"
@@ -1968,4 +1970,791 @@ void GPENCIL_OT_reproject(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-/* ************************************************ */
+/* ******************* Stroke subdivide ************************** */
+/* helper: Count how many points need to be inserted */
+static int gp_count_subdivision_cuts(bGPDstroke *gps)
+{
+ bGPDspoint *pt;
+ int i;
+ int totnewpoints = 0;
+ for (i = 0, pt = gps->points; i < gps->totpoints && pt; i++, pt++) {
+ if (pt->flag & GP_SPOINT_SELECT) {
+ if (i + 1 < gps->totpoints){
+ if (gps->points[i + 1].flag & GP_SPOINT_SELECT) {
+ ++totnewpoints;
+ };
+ }
+ }
+ }
+
+ return totnewpoints;
+}
+static int gp_stroke_subdivide_exec(bContext *C, wmOperator *op)
+{
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ bGPDspoint *temp_points;
+ const int cuts = RNA_int_get(op->ptr, "number_cuts");
+
+ int totnewpoints, oldtotpoints;
+ int i2;
+
+ /* sanity checks */
+ if (ELEM(NULL, gpd))
+ return OPERATOR_CANCELLED;
+
+ /* Go through each editable + selected stroke */
+ GP_EDITABLE_STROKES_BEGIN(C, gpl, gps)
+ {
+ if (gps->flag & GP_STROKE_SELECT) {
+ /* loop as many times as cuts */
+ for (int s = 0; s < cuts; s++) {
+ totnewpoints = gp_count_subdivision_cuts(gps);
+ if (totnewpoints == 0) {
+ continue;
+ }
+ /* duplicate points in a temp area */
+ temp_points = MEM_dupallocN(gps->points);
+ oldtotpoints = gps->totpoints;
+
+ /* resize the points arrys */
+ gps->totpoints += totnewpoints;
+ gps->points = MEM_recallocN(gps->points, sizeof(*gps->points) * gps->totpoints);
+ gps->flag |= GP_STROKE_RECALC_CACHES;
+
+ /* loop and interpolate */
+ i2 = 0;
+ for (int i = 0; i < oldtotpoints; i++) {
+ bGPDspoint *pt = &temp_points[i];
+ bGPDspoint *pt_final = &gps->points[i2];
+
+ /* copy current point */
+ copy_v3_v3(&pt_final->x, &pt->x);
+ pt_final->pressure = pt->pressure;
+ pt_final->strength = pt->strength;
+ pt_final->time = pt->time;
+ pt_final->flag = pt->flag;
+ ++i2;
+
+ /* if next point is selected add a half way point */
+ if (pt->flag & GP_SPOINT_SELECT) {
+ if (i + 1 < oldtotpoints){
+ if (temp_points[i + 1].flag & GP_SPOINT_SELECT) {
+ pt_final = &gps->points[i2];
+ /* Interpolate all values */
+ bGPDspoint *next = &temp_points[i + 1];
+ interp_v3_v3v3(&pt_final->x, &pt->x, &next->x, 0.5f);
+ pt_final->pressure = interpf(pt->pressure, next->pressure, 0.5f);
+ pt_final->strength = interpf(pt->strength, next->strength, 0.5f);
+ CLAMP(pt_final->strength, GPENCIL_STRENGTH_MIN, 1.0f);
+ pt_final->time = interpf(pt->time, next->time, 0.5f);
+ pt_final->flag |= GP_SPOINT_SELECT;
+ ++i2;
+ };
+ }
+ }
+ }
+ /* free temp memory */
+ MEM_freeN(temp_points);
+ }
+ }
+ }
+ GP_EDITABLE_STROKES_END;
+
+ /* notifiers */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_stroke_subdivide(wmOperatorType *ot)
+{
+ PropertyRNA *prop;
+
+ /* identifiers */
+ ot->name = "Subdivide Stroke";
+ ot->idname = "GPENCIL_OT_stroke_subdivide";
+ ot->description = "Subdivide between continuous selected points of the stroke adding a point half way between them";
+
+ /* api callbacks */
+ ot->exec = gp_stroke_subdivide_exec;
+ ot->poll = gp_active_layer_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
+
+ /* properties */
+ prop = RNA_def_int(ot->srna, "number_cuts", 1, 1, 10, "Number of Cuts", "", 1, 5);
+ /* avoid re-using last var because it can cause _very_ high value and annoy users */
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+
+}
+
+/* ========= Interpolation operators ========================== */
+/* Helper: Update point with interpolation */
+static void gp_interpolate_update_points(bGPDstroke *gps_from, bGPDstroke *gps_to, bGPDstroke *new_stroke, float factor)
+{
+ bGPDspoint *prev, *pt, *next;
+
+ /* update points */
+ for (int i = 0; i < new_stroke->totpoints; i++) {
+ prev = &gps_from->points[i];
+ pt = &new_stroke->points[i];
+ next = &gps_to->points[i];
+
+ /* Interpolate all values */
+ interp_v3_v3v3(&pt->x, &prev->x, &next->x, factor);
+ pt->pressure = interpf(prev->pressure, next->pressure, factor);
+ pt->strength = interpf(prev->strength, next->strength, factor);
+ CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f);
+ }
+}
+
+/* Helper: Update all strokes interpolated */
+static void gp_interpolate_update_strokes(bContext *C, tGPDinterpolate *tgpi)
+{
+ tGPDinterpolate_layer *tgpil;
+ bGPDstroke *new_stroke, *gps_from, *gps_to;
+ int cStroke;
+ float factor;
+ float shift = tgpi->shift;
+
+ for (tgpil = tgpi->ilayers.first; tgpil; tgpil = tgpil->next) {
+ factor = tgpil->factor + shift;
+ for (new_stroke = tgpil->interFrame->strokes.first; new_stroke; new_stroke = new_stroke->next) {
+ if (new_stroke->totpoints == 0) {
+ continue;
+ }
+ /* get strokes to interpolate */
+ cStroke = BLI_findindex(&tgpil->interFrame->strokes, new_stroke);
+ gps_from = BLI_findlink(&tgpil->prevFrame->strokes, cStroke);
+ gps_to = BLI_findlink(&tgpil->nextFrame->strokes, cStroke);
+ /* update points position */
+ if ((gps_from) && (gps_to)) {
+ gp_interpolate_update_points(gps_from, gps_to, new_stroke, factor);
+ }
+ }
+ }
+
+ WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL);
+}
+
+/* Helper: Verify valid strokes for interpolation */
+static bool gp_interpolate_check_todo(bContext *C, bGPdata *gpd)
+{
+ ToolSettings *ts = CTX_data_tool_settings(C);
+ int flag = ts->gp_sculpt.flag;
+
+ bGPDlayer *gpl;
+ bGPDlayer *active_gpl = CTX_data_active_gpencil_layer(C);
+ bGPDstroke *gps_from, *gps_to;
+ int fFrame;
+
+ /* get layers */
+ for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ /* all layers or only active */
+ if (((flag & GP_BRUSHEDIT_FLAG_INTERPOLATE_ALL_LAYERS) == 0) && (gpl != active_gpl)) {
+ continue;
+ }
+ /* only editable and visible layers are considered */
+ if (!gpencil_layer_is_editable(gpl) || (gpl->actframe == NULL)) {
+ continue;
+ }
+ /* read strokes */
+ for (gps_from = gpl->actframe->strokes.first; gps_from; gps_from = gps_from->next) {
+ /* only selected */
+ if ((flag & GP_BRUSHEDIT_FLAG_INTERPOLATE_ONLY_SELECTED) && ((gps_from->flag & GP_STROKE_SELECT) == 0)) {
+ continue;
+ }
+ /* skip strokes that are invalid for current view */
+ if (ED_gpencil_stroke_can_use(C, gps_from) == false) {
+ continue;
+ }
+ /* check if the color is editable */
+ if (ED_gpencil_stroke_color_use(gpl, gps_from) == false) {
+ continue;
+ }
+ /* get final stroke to interpolate */
+ fFrame = BLI_findindex(&gpl->actframe->strokes, gps_from);
+ gps_to = BLI_findlink(&gpl->actframe->next->strokes, fFrame);
+ if (gps_to == NULL) {
+ continue;
+ }
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* Helper: Create internal strokes interpolated */
+static void gp_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi)
+{
+ bGPDlayer *gpl;
+ bGPdata *gpd = tgpi->gpd;
+ tGPDinterpolate_layer *tgpil;
+ bGPDlayer *active_gpl = CTX_data_active_gpencil_layer(C);
+ bGPDstroke *gps_from, *gps_to, *new_stroke;
+ int fFrame;
+
+ /* save initial factor for active layer to define shift limits */
+ tgpi->init_factor = (float)(tgpi->cframe - active_gpl->actframe->framenum) / (active_gpl->actframe->next->framenum - active_gpl->actframe->framenum + 1);
+ /* limits are 100% below 0 and 100% over the 100% */
+ tgpi->low_limit = -1.0f - tgpi->init_factor;
+ tgpi->high_limit = 2.0f - tgpi->init_factor;
+
+ /* set layers */
+ for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ /* all layers or only active */
+ if (((tgpi->flag & GP_BRUSHEDIT_FLAG_INTERPOLATE_ALL_LAYERS) == 0) && (gpl != active_gpl)) {
+ continue;
+ }
+ /* only editable and visible layers are considered */
+ if (!gpencil_layer_is_editable(gpl) || (gpl->actframe == NULL)) {
+ continue;
+ }
+ /* create temp data for each layer */
+ tgpil = NULL;
+ tgpil = MEM_callocN(sizeof(tGPDinterpolate_layer), "GPencil Interpolate Layer");
+
+ tgpil->gpl = gpl;
+ tgpil->prevFrame = gpl->actframe;
+ tgpil->nextFrame = gpl->actframe->next;
+
+ BLI_addtail(&tgpi->ilayers, tgpil);
+ /* create a new temporary frame */
+ tgpil->interFrame = MEM_callocN(sizeof(bGPDframe), "bGPDframe");
+ tgpil->interFrame->framenum = tgpi->cframe;
+
+ /* get interpolation factor by layer (usually must be equal for all layers, but not sure) */
+ tgpil->factor = (float)(tgpi->cframe - tgpil->prevFrame->framenum) / (tgpil->nextFrame->framenum - tgpil->prevFrame->framenum + 1);
+ /* create new strokes data with interpolated points reading original stroke */
+ for (gps_from = tgpil->prevFrame->strokes.first; gps_from; gps_from = gps_from->next) {
+ bool valid = true;
+ /* only selected */
+ if ((tgpi->flag & GP_BRUSHEDIT_FLAG_INTERPOLATE_ONLY_SELECTED) && ((gps_from->flag & GP_STROKE_SELECT) == 0)) {
+ valid = false;
+ }
+
+ /* skip strokes that are invalid for current view */
+ if (ED_gpencil_stroke_can_use(C, gps_from) == false) {
+ valid = false;
+ }
+ /* check if the color is editable */
+ if (ED_gpencil_stroke_color_use(tgpil->gpl, gps_from) == false) {
+ valid = false;
+ }
+ /* get final stroke to interpolate */
+ fFrame = BLI_findindex(&tgpil->prevFrame->strokes, gps_from);
+ gps_to = BLI_findlink(&tgpil->nextFrame->strokes, fFrame);
+ if (gps_to == NULL) {
+ valid = false;
+ }
+ /* create new stroke */
+ new_stroke = MEM_dupallocN(gps_from);
+ new_stroke->points = MEM_dupallocN(gps_from->points);
+ new_stroke->triangles = MEM_dupallocN(gps_from->triangles);
+ if (valid) {
+ /* if destination stroke is smaller, resize new_stroke to size of gps_to stroke */
+ if (gps_from->totpoints > gps_to->totpoints) {
+ new_stroke->points = MEM_recallocN(new_stroke->points, sizeof(*new_stroke->points) * gps_to->totpoints);
+ new_stroke->totpoints = gps_to->totpoints;
+ new_stroke->tot_triangles = 0;
+ new_stroke->flag |= GP_STROKE_RECALC_CACHES;
+ }
+ /* update points position */
+ gp_interpolate_update_points(gps_from, gps_to, new_stroke, tgpil->factor);
+ }
+ else {
+ /* need an empty stroke to keep index correct for lookup, but resize to smallest size */
+ new_stroke->totpoints = 0;
+ new_stroke->points = MEM_recallocN(new_stroke->points, sizeof(*new_stroke->points));
+ new_stroke->tot_triangles = 0;
+ new_stroke->triangles = MEM_recallocN(new_stroke->triangles, sizeof(*new_stroke->triangles));
+ }
+ /* add to strokes */
+ BLI_addtail(&tgpil->interFrame->strokes, new_stroke);
+ }
+ }
+}
+
+/* Helper: calculate shift based on position of mouse (we only use x-axis for now.
+* since this is more convenient for users to do), and store new shift value
+*/
+static void gpencil_mouse_update_shift(tGPDinterpolate *tgpi, wmOperator *op, const wmEvent *event)
+{
+ float mid = (float)(tgpi->ar->winx - tgpi->ar->winrct.xmin) / 2.0f;
+ float mpos = event->x - tgpi->ar->winrct.xmin;
+ if (mpos >= mid) {
+ tgpi->shift = ((mpos - mid) * tgpi->high_limit) / mid;
+ }
+ else {
+ tgpi->shift = tgpi->low_limit - ((mpos * tgpi->low_limit) / mid);
+ }
+
+ CLAMP(tgpi->shift, tgpi->low_limit, tgpi->high_limit);
+ RNA_float_set(op->ptr, "shift", tgpi->shift);
+}
+
+/* Helper: Draw status message while the user is running the operator */
+static void gpencil_interpolate_status_indicators(tGPDinterpolate *p)
+{
+ Scene *scene = p->scene;
+ char status_str[UI_MAX_DRAW_STR];
+ char msg_str[UI_MAX_DRAW_STR];
+ BLI_strncpy(msg_str, IFACE_("GPencil Interpolation: ESC/RMB to cancel, Enter/LMB to confirm, WHEEL/MOVE to adjust, Factor"), UI_MAX_DRAW_STR);
+
+ if (hasNumInput(&p->num)) {
+ char str_offs[NUM_STR_REP_LEN];
+
+ outputNumInput(&p->num, str_offs, &scene->unit);
+
+ BLI_snprintf(status_str, sizeof(status_str), "%s: %s", msg_str, str_offs);
+ }
+ else {
+ BLI_snprintf(status_str, sizeof(status_str), "%s: %d %%", msg_str, (int)((p->init_factor + p->shift) * 100.0f));
+ }
+
+ ED_area_headerprint(p->sa, status_str);
+}
+
+/* Helper: Update screen and stroke */
+static void gpencil_interpolate_update(bContext *C, wmOperator *op, tGPDinterpolate *tgpi)
+{
+ /* update shift indicator in header */
+ gpencil_interpolate_status_indicators(tgpi);
+ /* apply... */
+ tgpi->shift = RNA_float_get(op->ptr, "shift");
+ /* update points position */
+ gp_interpolate_update_strokes(C, tgpi);
+}
+
+/* init new temporary interpolation data */
+static bool gp_interpolate_set_init_values(bContext *C, wmOperator *op, tGPDinterpolate *tgpi)
+{
+ ToolSettings *ts = CTX_data_tool_settings(C);
+ bGPdata *gpd = CTX_data_gpencil_data(C);
+
+ /* set current scene and window */
+ tgpi->scene = CTX_data_scene(C);
+ tgpi->sa = CTX_wm_area(C);
+ tgpi->ar = CTX_wm_region(C);
+ tgpi->flag = ts->gp_sculpt.flag;
+
+ /* set current frame number */
+ tgpi->cframe = tgpi->scene->r.cfra;
+
+ /* set GP datablock */
+ tgpi->gpd = gpd;
+
+ /* set interpolation weight */
+ tgpi->shift = RNA_float_get(op->ptr, "shift");
+ /* set layers */
+ gp_interpolate_set_points(C, tgpi);
+
+ return 1;
+}
+
+/* Poll handler: check if context is suitable for interpolation */
+static int gpencil_interpolate_poll(bContext *C)
+{
+ bGPdata * gpd = CTX_data_gpencil_data(C);
+ bGPDlayer *gpl = CTX_data_active_gpencil_layer(C);
+ /* only 3D view */
+ if (CTX_wm_area(C)->spacetype != SPACE_VIEW3D) {
+ return 0;
+ }
+ /* need data to interpolate */
+ if (ELEM(NULL, gpd, gpl)) {
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Allocate memory and initialize values */
+static tGPDinterpolate *gp_session_init_interpolation(bContext *C, wmOperator *op)
+{
+ tGPDinterpolate *tgpi = NULL;
+
+ /* create new context data */
+ tgpi = MEM_callocN(sizeof(tGPDinterpolate), "GPencil Interpolate Data");
+
+ /* define initial values */
+ gp_interpolate_set_init_values(C, op, tgpi);
+
+ /* return context data for running operator */
+ return tgpi;
+}
+
+/* Exit and free memory */
+static void gpencil_interpolate_exit(bContext *C, wmOperator *op)
+{
+ tGPDinterpolate *tgpi = op->customdata;
+ tGPDinterpolate_layer *tgpil;
+
+ /* don't assume that operator data exists at all */
+ if (tgpi) {
+ /* remove drawing handler */
+ if (tgpi->draw_handle_screen) {
+ ED_region_draw_cb_exit(tgpi->ar->type, tgpi->draw_handle_screen);
+ }
+ if (tgpi->draw_handle_3d) {
+ ED_region_draw_cb_exit(tgpi->ar->type, tgpi->draw_handle_3d);
+ }
+ /* clear status message area */
+ ED_area_headerprint(tgpi->sa, NULL);
+ /* finally, free memory used by temp data */
+ for (tgpil = tgpi->ilayers.first; tgpil; tgpil = tgpil->next) {
+ BKE_gpencil_free_strokes(tgpil->interFrame);
+ MEM_freeN(tgpil->interFrame);
+ }
+
+ BLI_freelistN(&tgpi->ilayers);
+ MEM_freeN(tgpi);
+ }
+ WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL);
+
+ /* clear pointer */
+ op->customdata = NULL;
+}
+
+/* Cancel handler */
+static void gpencil_interpolate_cancel(bContext *C, wmOperator *op)
+{
+ /* this is just a wrapper around exit() */
+ gpencil_interpolate_exit(C, op);
+}
+
+/* Init interpolation: Allocate memory and set init values */
+static int gpencil_interpolate_init(bContext *C, wmOperator *op)
+{
+ tGPDinterpolate *tgpi;
+ /* check context */
+ tgpi = op->customdata = gp_session_init_interpolation(C, op);
+ if (tgpi == NULL) {
+ /* something wasn't set correctly in context */
+ gpencil_interpolate_exit(C, op);
+ return 0;
+ }
+
+ /* everything is now setup ok */
+ return 1;
+}
+
+/* ********************** custom drawcall api ***************** */
+/* Helper: drawing callback for modal operator in screen mode */
+static void gpencil_interpolate_draw_screen(const struct bContext *UNUSED(C), ARegion *UNUSED(ar), void *arg)
+{
+ wmOperator *op = arg;
+ struct tGPDinterpolate *tgpi = op->customdata;
+ ED_gp_draw_interpolation(tgpi, REGION_DRAW_POST_PIXEL);
+}
+
+/* Helper: drawing callback for modal operator in 3d mode */
+static void gpencil_interpolate_draw_3d(const struct bContext *UNUSED(C), ARegion *UNUSED(ar), void *arg)
+{
+ wmOperator *op = arg;
+ struct tGPDinterpolate *tgpi = op->customdata;
+ ED_gp_draw_interpolation(tgpi, REGION_DRAW_POST_VIEW);
+}
+
+/* Invoke handler: Initialize the operator */
+static int gpencil_interpolate_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+{
+ wmWindow *win = CTX_wm_window(C);
+ Scene *scene = CTX_data_scene(C);
+ bGPdata * gpd = CTX_data_gpencil_data(C);
+ bGPDlayer *gpl = CTX_data_active_gpencil_layer(C);
+ tGPDinterpolate *tgpi = NULL;
+
+ /* cannot interpolate if not between 2 frames */
+ if ((gpl->actframe == NULL) || (gpl->actframe->next == NULL)) {
+ BKE_report(op->reports, RPT_ERROR, "Interpolation requires to be between two grease pencil frames in active layer");
+ return OPERATOR_CANCELLED;
+ }
+
+ /* cannot interpolate in extremes */
+ if ((gpl->actframe->framenum == scene->r.cfra) || (gpl->actframe->next->framenum == scene->r.cfra)) {
+ BKE_report(op->reports, RPT_ERROR, "Interpolation requires to be between two grease pencil frames in active layer");
+ return OPERATOR_CANCELLED;
+ }
+
+ /* need editable strokes */
+ if (!gp_interpolate_check_todo(C, gpd)) {
+ BKE_report(op->reports, RPT_ERROR, "Interpolation requires some editable stroke");
+ return OPERATOR_CANCELLED;
+ }
+
+ /* try to initialize context data needed */
+ if (!gpencil_interpolate_init(C, op)) {
+ if (op->customdata)
+ MEM_freeN(op->customdata);
+ return OPERATOR_CANCELLED;
+ }
+ else
+ tgpi = op->customdata;
+
+ /* enable custom drawing handlers. It needs 2 handlers because can be strokes in 3d space and screen space and each handler use different
+ coord system */
+ tgpi->draw_handle_screen = ED_region_draw_cb_activate(tgpi->ar->type, gpencil_interpolate_draw_screen, op, REGION_DRAW_POST_PIXEL);
+ tgpi->draw_handle_3d = ED_region_draw_cb_activate(tgpi->ar->type, gpencil_interpolate_draw_3d, op, REGION_DRAW_POST_VIEW);
+ /* set cursor to indicate modal */
+ WM_cursor_modal_set(win, BC_EW_SCROLLCURSOR);
+ /* update shift indicator in header */
+ gpencil_interpolate_status_indicators(tgpi);
+ WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL);
+
+ /* add a modal handler for this operator */
+ WM_event_add_modal_handler(C, op);
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+/* Modal handler: Events handling during interactive part */
+static int gpencil_interpolate_modal(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ tGPDinterpolate *tgpi = op->customdata;
+ wmWindow *win = CTX_wm_window(C);
+ bGPDframe *gpf_dst;
+ bGPDstroke *gps_src, *gps_dst;
+ tGPDinterpolate_layer *tgpil;
+ const bool has_numinput = hasNumInput(&tgpi->num);
+
+ switch (event->type) {
+ case LEFTMOUSE: /* confirm */
+ case RETKEY:
+ {
+ /* return to normal cursor and header status */
+ ED_area_headerprint(tgpi->sa, NULL);
+ WM_cursor_modal_restore(win);
+
+ /* insert keyframes as required... */
+ for (tgpil = tgpi->ilayers.first; tgpil; tgpil = tgpil->next) {
+ gpf_dst = BKE_gpencil_layer_getframe(tgpil->gpl, tgpi->cframe, GP_GETFRAME_ADD_NEW);
+ gpf_dst->key_type = BEZT_KEYTYPE_BREAKDOWN;
+
+ /* copy strokes */
+ BLI_listbase_clear(&gpf_dst->strokes);
+ for (gps_src = tgpil->interFrame->strokes.first; gps_src; gps_src = gps_src->next) {
+ if (gps_src->totpoints == 0) {
+ continue;
+ }
+ /* make copy of source stroke, then adjust pointer to points too */
+ 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);
+ }
+ }
+ /* clean up temp data */
+ gpencil_interpolate_exit(C, op);
+
+ /* done! */
+ return OPERATOR_FINISHED;
+ }
+
+ case ESCKEY: /* cancel */
+ case RIGHTMOUSE:
+ {
+ /* return to normal cursor and header status */
+ ED_area_headerprint(tgpi->sa, NULL);
+ WM_cursor_modal_restore(win);
+
+ /* clean up temp data */
+ gpencil_interpolate_exit(C, op);
+
+ /* canceled! */
+ return OPERATOR_CANCELLED;
+ }
+ case WHEELUPMOUSE:
+ {
+ tgpi->shift = tgpi->shift + 0.01f;
+ CLAMP(tgpi->shift, tgpi->low_limit, tgpi->high_limit);
+ RNA_float_set(op->ptr, "shift", tgpi->shift);
+ /* update screen */
+ gpencil_interpolate_update(C, op, tgpi);
+ break;
+ }
+ case WHEELDOWNMOUSE:
+ {
+ tgpi->shift = tgpi->shift - 0.01f;
+ CLAMP(tgpi->shift, tgpi->low_limit, tgpi->high_limit);
+ RNA_float_set(op->ptr, "shift", tgpi->shift);
+ /* update screen */
+ gpencil_interpolate_update(C, op, tgpi);
+ break;
+ }
+ case MOUSEMOVE: /* calculate new position */
+ {
+ /* only handle mousemove if not doing numinput */
+ if (has_numinput == false) {
+ /* update shift based on position of mouse */
+ gpencil_mouse_update_shift(tgpi, op, event);
+ /* update screen */
+ gpencil_interpolate_update(C, op, tgpi);
+ }
+ break;
+ }
+ default:
+ if ((event->val == KM_PRESS) && handleNumInput(C, &tgpi->num, event)) {
+ float value;
+ float factor = tgpi->init_factor;
+
+ /* Grab shift from numeric input, and store this new value (the user see an int) */
+ value = (factor + tgpi->shift) * 100.0f;
+ applyNumInput(&tgpi->num, &value);
+ tgpi->shift = value / 100.0f;
+ /* recalculate the shift to get the right value in the frame scale */
+ tgpi->shift = tgpi->shift - factor;
+
+ CLAMP(tgpi->shift, tgpi->low_limit, tgpi->high_limit);
+ RNA_float_set(op->ptr, "shift", tgpi->shift);
+
+ /* update screen */
+ gpencil_interpolate_update(C, op, tgpi);
+
+ break;
+ }
+ else {
+ /* unhandled event - allow to pass through */
+ return OPERATOR_RUNNING_MODAL | OPERATOR_PASS_THROUGH;
+ }
+ }
+
+ /* still running... */
+ return OPERATOR_RUNNING_MODAL;
+}
+
+/* Define modal operator for interpolation */
+void GPENCIL_OT_interpolate(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Grease Pencil Interpolation";
+ ot->idname = "GPENCIL_OT_interpolate";
+ ot->description = "Interpolate grease pencil strokes between frames";
+
+ /* api callbacks */
+ ot->invoke = gpencil_interpolate_invoke;
+ ot->modal = gpencil_interpolate_modal;
+ ot->cancel = gpencil_interpolate_cancel;
+ ot->poll = gpencil_interpolate_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING;
+
+ RNA_def_float_percentage(ot->srna, "shift", 0.0f, -1.0f, 1.0f, "Shift", "Displacement factor for the interpolate operation", -0.9f, 0.9f);
+}
+
+/* =============== Interpolate sequence ===============*/
+/* Create Sequence Interpolation */
+static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op)
+{
+ Scene *scene = CTX_data_scene(C);
+ ToolSettings *ts = CTX_data_tool_settings(C);
+ bGPdata * gpd = CTX_data_gpencil_data(C);
+ bGPDlayer *active_gpl = CTX_data_active_gpencil_layer(C);
+ bGPDlayer *gpl;
+ bGPDframe *prevFrame, *nextFrame, *interFrame;
+ bGPDstroke *gps_from, *gps_to, *new_stroke;
+ float factor;
+ int cframe, fFrame;
+ int flag = ts->gp_sculpt.flag;
+
+ /* cannot interpolate if not between 2 frames */
+ if ((active_gpl->actframe == NULL) || (active_gpl->actframe->next == NULL)) {
+ BKE_report(op->reports, RPT_ERROR, "Interpolation requires to be between two grease pencil frames");
+ return OPERATOR_CANCELLED;
+ }
+ /* cannot interpolate in extremes */
+ if ((active_gpl->actframe->framenum == scene->r.cfra) || (active_gpl->actframe->next->framenum == scene->r.cfra)) {
+ BKE_report(op->reports, RPT_ERROR, "Interpolation requires to be between two grease pencil frames");
+ return OPERATOR_CANCELLED;
+ }
+
+ /* loop all layer to check if need interpolation */
+ for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ /* all layers or only active */
+ if (((flag & GP_BRUSHEDIT_FLAG_INTERPOLATE_ALL_LAYERS) == 0) && (gpl != active_gpl)) {
+ continue;
+ }
+ /* only editable and visible layers are considered */
+ if (!gpencil_layer_is_editable(gpl) || (gpl->actframe == NULL)) {
+ continue;
+ }
+ /* store extremes */
+ prevFrame = gpl->actframe;
+ nextFrame = gpl->actframe->next;
+ /* Loop over intermediary frames and create the interpolation */
+ for (cframe = prevFrame->framenum + 1; cframe < nextFrame->framenum; cframe++) {
+ interFrame = NULL;
+
+ /* get interpolation factor */
+ factor = (float)(cframe - prevFrame->framenum) / (nextFrame->framenum - prevFrame->framenum + 1);
+
+ /* create new strokes data with interpolated points reading original stroke */
+ for (gps_from = prevFrame->strokes.first; gps_from; gps_from = gps_from->next) {
+ /* only selected */
+ if ((flag & GP_BRUSHEDIT_FLAG_INTERPOLATE_ONLY_SELECTED) && ((gps_from->flag & GP_STROKE_SELECT) == 0)) {
+ continue;
+ }
+ /* skip strokes that are invalid for current view */
+ if (ED_gpencil_stroke_can_use(C, gps_from) == false) {
+ continue;
+ }
+ /* check if the color is editable */
+ if (ED_gpencil_stroke_color_use(gpl, gps_from) == false) {
+ continue;
+ }
+ /* get final stroke to interpolate */
+ fFrame = BLI_findindex(&prevFrame->strokes, gps_from);
+ gps_to = BLI_findlink(&nextFrame->strokes, fFrame);
+ if (gps_to == NULL) {
+ continue;
+ }
+ /* create a new frame if needed */
+ if (interFrame == NULL) {
+ interFrame = BKE_gpencil_layer_getframe(gpl, cframe, GP_GETFRAME_ADD_NEW);
+ interFrame->key_type = BEZT_KEYTYPE_BREAKDOWN;
+ }
+ /* create new stroke */
+ new_stroke = MEM_dupallocN(gps_from);
+ new_stroke->points = MEM_dupallocN(gps_from->points);
+ new_stroke->triangles = MEM_dupallocN(gps_from->triangles);
+ /* if destination stroke is smaller, resize new_stroke to size of gps_to stroke */
+ if (gps_from->totpoints > gps_to->totpoints) {
+ new_stroke->points = MEM_recallocN(new_stroke->points, sizeof(*new_stroke->points) * gps_to->totpoints);
+ new_stroke->totpoints = gps_to->totpoints;
+ new_stroke->tot_triangles = 0;
+ new_stroke->flag |= GP_STROKE_RECALC_CACHES;
+ }
+ /* update points position */
+ gp_interpolate_update_points(gps_from, gps_to, new_stroke, factor);
+
+ /* add to strokes */
+ BLI_addtail(&interFrame->strokes, new_stroke);
+ }
+ }
+ }
+
+ /* notifiers */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+/* Define sequence interpolation */
+void GPENCIL_OT_interpolate_sequence(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Grease Pencil Sequence Interpolation";
+ ot->idname = "GPENCIL_OT_interpolate_sequence";
+ ot->description = "Interpolate full grease pencil strokes sequence between frames";
+
+ /* api callbacks */
+ ot->exec = gpencil_interpolate_seq_exec;
+ ot->poll = gpencil_interpolate_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+/* ========= End Interpolation operators ========================== */
diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h
index 4178d49d652..e2e5fc28710 100644
--- a/source/blender/editors/gpencil/gpencil_intern.h
+++ b/source/blender/editors/gpencil/gpencil_intern.h
@@ -304,6 +304,7 @@ 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_stroke_subdivide(struct wmOperatorType *ot);
void GPENCIL_OT_brush_add(struct wmOperatorType *ot);
void GPENCIL_OT_brush_remove(struct wmOperatorType *ot);
@@ -335,6 +336,11 @@ void gpencil_undo_init(struct bGPdata *gpd);
void gpencil_undo_push(struct bGPdata *gpd);
void gpencil_undo_finish(void);
+/* interpolation ---------- */
+
+void GPENCIL_OT_interpolate(struct wmOperatorType *ot);
+void GPENCIL_OT_interpolate_sequence(struct wmOperatorType *ot);
+
/* ****************************************************** */
/* FILTERED ACTION DATA - TYPES ---> XXX DEPRECEATED OLD ANIM SYSTEM CODE! */
diff --git a/source/blender/editors/gpencil/gpencil_ops.c b/source/blender/editors/gpencil/gpencil_ops.c
index ae1c5554521..98cdbadb87d 100644
--- a/source/blender/editors/gpencil/gpencil_ops.c
+++ b/source/blender/editors/gpencil/gpencil_ops.c
@@ -79,7 +79,6 @@ static void ed_keymap_gpencil_general(wmKeyConfig *keyconf)
RNA_enum_set(kmi->ptr, "mode", GP_PAINTMODE_ERASER);
RNA_boolean_set(kmi->ptr, "wait_for_input", false);
-
/* Tablet Mappings for Drawing ------------------ */
/* For now, only support direct drawing using the eraser, as most users using a tablet
* may still want to use that as their primary pointing device!
@@ -144,7 +143,10 @@ static void ed_keymap_gpencil_editing(wmKeyConfig *keyconf)
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");
-
+ /* Interpolation */
+ WM_keymap_add_item(keymap, "GPENCIL_OT_interpolate", EKEY, KM_PRESS, KM_CTRL, 0);
+ WM_keymap_add_item(keymap, "GPENCIL_OT_interpolate_sequence", EKEY, KM_PRESS, KM_SHIFT | KM_CTRL, 0);
+
/* Sculpting ------------------------------------- */
/* Brush-Based Editing:
@@ -240,6 +242,9 @@ 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);
+ /* menu edit specials */
+ WM_keymap_add_menu(keymap, "GPENCIL_MT_gpencil_edit_specials", WKEY, KM_PRESS, 0, 0);
+
/* join strokes */
WM_keymap_add_item(keymap, "GPENCIL_OT_stroke_join", JKEY, KM_PRESS, KM_CTRL, 0);
@@ -408,6 +413,7 @@ void ED_operatortypes_gpencil(void)
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_stroke_subdivide);
WM_operatortype_append(GPENCIL_OT_palette_add);
WM_operatortype_append(GPENCIL_OT_palette_remove);
@@ -433,6 +439,10 @@ void ED_operatortypes_gpencil(void)
WM_operatortype_append(GPENCIL_OT_brush_select);
/* Editing (Time) --------------- */
+
+ /* Interpolation */
+ WM_operatortype_append(GPENCIL_OT_interpolate);
+ WM_operatortype_append(GPENCIL_OT_interpolate_sequence);
}
void ED_operatormacros_gpencil(void)
diff --git a/source/blender/editors/gpencil/gpencil_undo.c b/source/blender/editors/gpencil/gpencil_undo.c
index 793ed2a07d0..7a9ad2b32c0 100644
--- a/source/blender/editors/gpencil/gpencil_undo.c
+++ b/source/blender/editors/gpencil/gpencil_undo.c
@@ -124,6 +124,17 @@ void gpencil_undo_init(bGPdata *gpd)
gpencil_undo_push(gpd);
}
+static void gpencil_undo_free_node(bGPundonode *undo_node)
+{
+ /* HACK: animdata wasn't duplicated, so it shouldn't be freed here,
+ * or else the real copy will segfault when accessed
+ */
+ undo_node->gpd->adt = NULL;
+
+ BKE_gpencil_free(undo_node->gpd, false);
+ MEM_freeN(undo_node->gpd);
+}
+
void gpencil_undo_push(bGPdata *gpd)
{
bGPundonode *undo_node;
@@ -137,20 +148,36 @@ void gpencil_undo_push(bGPdata *gpd)
while (undo_node) {
bGPundonode *next_node = undo_node->next;
- /* HACK: animdata wasn't duplicated, so it shouldn't be freed here,
- * or else the real copy will segfault when accessed
- */
- undo_node->gpd->adt = NULL;
-
- BKE_gpencil_free(undo_node->gpd, false);
- MEM_freeN(undo_node->gpd);
-
+ gpencil_undo_free_node(undo_node);
BLI_freelinkN(&undo_nodes, undo_node);
undo_node = next_node;
}
}
+ /* limit number of undo steps to the maximum undo steps
+ * - to prevent running out of memory during **really**
+ * long drawing sessions (triggering swapping)
+ */
+ /* TODO: Undo-memory constraint is not respected yet, but can be added if we have any need for it */
+ if (U.undosteps && !BLI_listbase_is_empty(&undo_nodes)) {
+ /* remove anything older than n-steps before cur_node */
+ int steps = 0;
+
+ undo_node = (cur_node) ? cur_node : undo_nodes.last;
+ while (undo_node) {
+ bGPundonode *prev_node = undo_node->prev;
+
+ if (steps >= U.undosteps) {
+ gpencil_undo_free_node(undo_node);
+ BLI_freelinkN(&undo_nodes, undo_node);
+ }
+
+ steps++;
+ undo_node = prev_node;
+ }
+ }
+
/* create new undo node */
undo_node = MEM_callocN(sizeof(bGPundonode), "gpencil undo node");
undo_node->gpd = BKE_gpencil_data_duplicate(G.main, gpd, true);
@@ -165,14 +192,7 @@ void gpencil_undo_finish(void)
bGPundonode *undo_node = undo_nodes.first;
while (undo_node) {
- /* HACK: animdata wasn't duplicated, so it shouldn't be freed here,
- * or else the real copy will segfault when accessed
- */
- undo_node->gpd->adt = NULL;
-
- BKE_gpencil_free(undo_node->gpd, false);
- MEM_freeN(undo_node->gpd);
-
+ gpencil_undo_free_node(undo_node);
undo_node = undo_node->next;
}
diff --git a/source/blender/editors/include/ED_anim_api.h b/source/blender/editors/include/ED_anim_api.h
index 99f9287be8f..e05f15ce7fc 100644
--- a/source/blender/editors/include/ED_anim_api.h
+++ b/source/blender/editors/include/ED_anim_api.h
@@ -415,7 +415,8 @@ typedef enum eAnimChannel_Settings {
ACHANNEL_SETTING_VISIBLE = 4, /* only for Graph Editor */
ACHANNEL_SETTING_SOLO = 5, /* only for NLA Tracks */
ACHANNEL_SETTING_PINNED = 6, /* only for NLA Actions */
- ACHANNEL_SETTING_MOD_OFF = 7
+ ACHANNEL_SETTING_MOD_OFF = 7,
+ ACHANNEL_SETTING_ALWAYS_VISIBLE = 8, /* channel is pinned and always visible */
} eAnimChannel_Settings;
diff --git a/source/blender/editors/include/ED_gpencil.h b/source/blender/editors/include/ED_gpencil.h
index d526b0841cc..bc93b5622cb 100644
--- a/source/blender/editors/include/ED_gpencil.h
+++ b/source/blender/editors/include/ED_gpencil.h
@@ -30,6 +30,8 @@
#ifndef __ED_GPENCIL_H__
#define __ED_GPENCIL_H__
+#include "ED_numinput.h"
+
struct ID;
struct ListBase;
struct bContext;
@@ -51,6 +53,36 @@ struct wmKeyConfig;
/* ------------- Grease-Pencil Helpers ---------------- */
+typedef struct tGPDinterpolate_layer {
+ struct tGPDinterpolate_layer *next, *prev;
+
+ struct bGPDlayer *gpl; /* layer */
+ struct bGPDframe *prevFrame; /* frame before current frame (interpolate-from) */
+ struct bGPDframe *nextFrame; /* frame after current frame (interpolate-to) */
+ struct bGPDframe *interFrame; /* interpolated frame */
+ float factor; /* interpolate factor */
+
+} tGPDinterpolate_layer;
+
+/* Temporary interpolate operation data */
+typedef struct tGPDinterpolate {
+ struct Scene *scene; /* current scene from context */
+ struct ScrArea *sa; /* area where painting originated */
+ struct ARegion *ar; /* region where painting originated */
+ struct bGPdata *gpd; /* current GP datablock */
+
+ int cframe; /* current frame number */
+ ListBase ilayers; /* (tGPDinterpolate_layer) layers to be interpolated */
+ float shift; /* value for determining the displacement influence */
+ float init_factor; /* initial interpolation factor for active layer */
+ float low_limit; /* shift low limit (-100%) */
+ float high_limit; /* shift upper limit (200%) */
+ int flag; /* flag from toolsettings */
+
+ NumInput num; /* numeric input */
+ void *draw_handle_3d; /* handle for drawing strokes while operator is running 3d stuff */
+ void *draw_handle_screen; /* handle for drawing strokes while operator is running screen stuff */
+} tGPDinterpolate;
/* Temporary 'Stroke Point' data
*
@@ -118,6 +150,7 @@ void ED_gpencil_draw_view2d(const struct bContext *C, bool onlyv2d);
void ED_gpencil_draw_view3d(struct wmWindowManager *wm, struct Scene *scene, struct View3D *v3d, struct ARegion *ar, bool only3d);
void ED_gpencil_draw_ex(struct Scene *scene, struct bGPdata *gpd, int winx, int winy,
const int cfra, const char spacetype);
+void ED_gp_draw_interpolation(struct tGPDinterpolate *tgpi, const int type);
/* ----------- Grease-Pencil AnimEdit API ------------------ */
bool ED_gplayer_frames_looper(struct bGPDlayer *gpl, struct Scene *scene,
diff --git a/source/blender/editors/include/ED_numinput.h b/source/blender/editors/include/ED_numinput.h
index f9a22429fc2..00558a3a787 100644
--- a/source/blender/editors/include/ED_numinput.h
+++ b/source/blender/editors/include/ED_numinput.h
@@ -30,6 +30,8 @@
#define NUM_STR_REP_LEN 64
#define NUM_MAX_ELEMENTS 3
+struct wmEvent;
+
typedef struct NumInput {
short idx_max; /* idx_max < NUM_MAX_ELEMENTS */
int unit_sys;
diff --git a/source/blender/editors/include/ED_util.h b/source/blender/editors/include/ED_util.h
index b6b80b93e0b..f5968397f65 100644
--- a/source/blender/editors/include/ED_util.h
+++ b/source/blender/editors/include/ED_util.h
@@ -78,9 +78,6 @@ void undo_editmode_push(struct bContext *C, const char *name,
void undo_editmode_clear(void);
-/* cut-paste buffer free */
-void ED_clipboard_posebuf_free(void);
-
/* ************** XXX OLD CRUFT WARNING ************* */
void apply_keyb_grid(int shift, int ctrl, float *val, float fac1, float fac2, float fac3, int invert);
diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h
index 49e5845e3ca..26a6fdd7d1f 100644
--- a/source/blender/editors/include/UI_interface.h
+++ b/source/blender/editors/include/UI_interface.h
@@ -1021,6 +1021,12 @@ void ED_keymap_ui(struct wmKeyConfig *keyconf);
void UI_drop_color_copy(struct wmDrag *drag, struct wmDropBox *drop);
int UI_drop_color_poll(struct bContext *C, struct wmDrag *drag, const struct wmEvent *event);
+/* UI_OT_space_context_cycle direction */
+enum {
+ SPACE_CONTEXT_CYCLE_PREV,
+ SPACE_CONTEXT_CYCLE_NEXT,
+};
+
bool UI_context_copy_to_selected_list(
struct bContext *C, struct PointerRNA *ptr, struct PropertyRNA *prop,
struct ListBase *r_lb, bool *r_use_path_from_id, char **r_path);
diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c
index 1af6d902b18..cb539bb1c5d 100644
--- a/source/blender/editors/interface/interface_ops.c
+++ b/source/blender/editors/interface/interface_ops.c
@@ -1082,6 +1082,78 @@ static void UI_OT_drop_color(wmOperatorType *ot)
RNA_def_boolean(ot->srna, "gamma", 0, "Gamma Corrected", "The source color is gamma corrected ");
}
+/* ------------------------------------------------------------------------- */
+
+static EnumPropertyItem space_context_cycle_direction[] = {
+ {SPACE_CONTEXT_CYCLE_PREV, "PREV", 0, "Previous", ""},
+ {SPACE_CONTEXT_CYCLE_NEXT, "NEXT", 0, "Next", ""},
+ {0, NULL, 0, NULL, NULL}
+};
+
+static int space_context_cycle_poll(bContext *C)
+{
+ ScrArea *sa = CTX_wm_area(C);
+ return ELEM(sa->spacetype, SPACE_BUTS, SPACE_USERPREF);
+}
+
+/**
+ * Helper to get the correct RNA pointer/property pair for changing
+ * the display context of active space type in \sa.
+ */
+static void context_cycle_prop_get(
+ bScreen *screen, const ScrArea *sa,
+ PointerRNA *r_ptr, PropertyRNA **r_prop)
+{
+ const char *propname;
+
+ switch (sa->spacetype) {
+ case SPACE_BUTS:
+ RNA_pointer_create(&screen->id, &RNA_SpaceProperties, sa->spacedata.first, r_ptr);
+ propname = "context";
+ break;
+ case SPACE_USERPREF:
+ RNA_pointer_create(NULL, &RNA_UserPreferences, &U, r_ptr);
+ propname = "active_section";
+ break;
+ }
+
+ *r_prop = RNA_struct_find_property(r_ptr, propname);
+}
+
+static int space_context_cycle_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+{
+ const int direction = RNA_enum_get(op->ptr, "direction");
+
+ PointerRNA ptr;
+ PropertyRNA *prop;
+ context_cycle_prop_get(CTX_wm_screen(C), CTX_wm_area(C), &ptr, &prop);
+
+ const int old_context = RNA_property_enum_get(&ptr, prop);
+ const int new_context = RNA_property_enum_step(
+ C, &ptr, prop, old_context,
+ direction == SPACE_CONTEXT_CYCLE_PREV ? -1 : 1);
+ RNA_property_enum_set(&ptr, prop, new_context);
+ RNA_property_update(C, &ptr, prop);
+
+ return OPERATOR_FINISHED;
+}
+
+static void UI_OT_space_context_cycle(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Cycle Space Context";
+ ot->description = "Cycle through the editor context by activating the next/previous one";
+ ot->idname = "UI_OT_space_context_cycle";
+
+ /* api callbacks */
+ ot->invoke = space_context_cycle_invoke;
+ ot->poll = space_context_cycle_poll;
+
+ ot->flag = 0;
+
+ RNA_def_enum(ot->srna, "direction", space_context_cycle_direction, SPACE_CONTEXT_CYCLE_NEXT, "Direction",
+ "Direction to cycle through");
+}
/* ********************************************************* */
@@ -1102,6 +1174,7 @@ void ED_operatortypes_ui(void)
WM_operatortype_append(UI_OT_edittranslation_init);
#endif
WM_operatortype_append(UI_OT_reloadtranslation);
+ WM_operatortype_append(UI_OT_space_context_cycle);
/* external */
WM_operatortype_append(UI_OT_eyedropper_color);
diff --git a/source/blender/editors/interface/interface_panel.c b/source/blender/editors/interface/interface_panel.c
index 79961eae79d..c131bcb8e14 100644
--- a/source/blender/editors/interface/interface_panel.c
+++ b/source/blender/editors/interface/interface_panel.c
@@ -1841,6 +1841,52 @@ void UI_panel_category_draw_all(ARegion *ar, const char *category_id_active)
#undef USE_FLAT_INACTIVE
}
+static int ui_handle_panel_category_cycling(const wmEvent *event, ARegion *ar, const uiBut *active_but)
+{
+ const bool is_mousewheel = ELEM(event->type, WHEELUPMOUSE, WHEELDOWNMOUSE);
+ const bool inside_tabregion = (event->mval[0] < ((PanelCategoryDyn *)ar->panels_category.first)->rect.xmax);
+
+ /* if mouse is inside non-tab region, ctrl key is required */
+ if (is_mousewheel && !event->ctrl && !inside_tabregion)
+ return WM_UI_HANDLER_CONTINUE;
+
+
+ if (active_but && ui_but_supports_cycling(active_but)) {
+ /* skip - exception to make cycling buttons
+ * using ctrl+mousewheel work in tabbed regions */
+ }
+ else {
+ const char *category = UI_panel_category_active_get(ar, false);
+ if (LIKELY(category)) {
+ PanelCategoryDyn *pc_dyn = UI_panel_category_find(ar, category);
+ if (LIKELY(pc_dyn)) {
+ if (is_mousewheel) {
+ /* we can probably get rid of this and only allow ctrl+tabbing */
+ pc_dyn = (event->type == WHEELDOWNMOUSE) ? pc_dyn->next : pc_dyn->prev;
+ }
+ else {
+ const bool backwards = event->shift;
+ pc_dyn = backwards ? pc_dyn->prev : pc_dyn->next;
+ if (!pc_dyn) {
+ /* proper cyclic behavior, back to first/last category (only used for ctrl+tab) */
+ pc_dyn = backwards ? ar->panels_category.last : ar->panels_category.first;
+ }
+ }
+
+ if (pc_dyn) {
+ /* intentionally don't reset scroll in this case,
+ * this allows for quick browsing between tabs */
+ UI_panel_category_active_set(ar, pc_dyn->idname);
+ ED_region_tag_redraw(ar);
+ }
+ }
+ }
+ return WM_UI_HANDLER_BREAK;
+ }
+
+ return WM_UI_HANDLER_CONTINUE;
+}
+
/* XXX should become modal keymap */
/* AKey is opening/closing panels, independent of button state now */
@@ -1853,6 +1899,7 @@ int ui_handler_panel_region(bContext *C, const wmEvent *event, ARegion *ar, cons
retval = WM_UI_HANDLER_CONTINUE;
+ /* handle category tabs */
if (has_category_tabs) {
if (event->val == KM_PRESS) {
if (event->type == LEFTMOUSE) {
@@ -1867,32 +1914,9 @@ int ui_handler_panel_region(bContext *C, const wmEvent *event, ARegion *ar, cons
retval = WM_UI_HANDLER_BREAK;
}
}
- else if (ELEM(event->type, WHEELUPMOUSE, WHEELDOWNMOUSE)) {
- /* mouse wheel cycle tabs */
-
- /* first check if the mouse is in the tab region */
- if (event->ctrl || (event->mval[0] < ((PanelCategoryDyn *)ar->panels_category.first)->rect.xmax)) {
- if (active_but && ui_but_supports_cycling(active_but)) {
- /* skip - exception to make cycling buttons
- * using ctrl+mousewheel work in tabbed regions */
- }
- else {
- const char *category = UI_panel_category_active_get(ar, false);
- if (LIKELY(category)) {
- PanelCategoryDyn *pc_dyn = UI_panel_category_find(ar, category);
- if (LIKELY(pc_dyn)) {
- pc_dyn = (event->type == WHEELDOWNMOUSE) ? pc_dyn->next : pc_dyn->prev;
- if (pc_dyn) {
- /* intentionally don't reset scroll in this case,
- * this allows for quick browsing between tabs */
- UI_panel_category_active_set(ar, pc_dyn->idname);
- ED_region_tag_redraw(ar);
- }
- }
- }
- retval = WM_UI_HANDLER_BREAK;
- }
- }
+ else if ((event->type == TABKEY && event->ctrl) || ELEM(event->type, WHEELUPMOUSE, WHEELDOWNMOUSE)) {
+ /* cycle tabs */
+ retval = ui_handle_panel_category_cycling(event, ar, active_but);
}
}
}
diff --git a/source/blender/editors/interface/interface_regions.c b/source/blender/editors/interface/interface_regions.c
index c507401b9a0..7a506c5c451 100644
--- a/source/blender/editors/interface/interface_regions.c
+++ b/source/blender/editors/interface/interface_regions.c
@@ -73,38 +73,6 @@
#define MENU_PADDING (int)(0.2f * UI_UNIT_Y)
#define MENU_BORDER (int)(0.3f * U.widget_unit)
-static int rna_property_enum_step(const bContext *C, PointerRNA *ptr, PropertyRNA *prop, int direction)
-{
- EnumPropertyItem *item_array;
- int totitem;
- bool free;
- int value;
- int i, i_init;
- int step = (direction < 0) ? -1 : 1;
- int step_tot = 0;
-
- RNA_property_enum_items((bContext *)C, ptr, prop, &item_array, &totitem, &free);
- value = RNA_property_enum_get(ptr, prop);
- i = RNA_enum_from_value(item_array, value);
- i_init = i;
-
- do {
- i = mod_i(i + step, totitem);
- if (item_array[i].identifier[0]) {
- step_tot += step;
- }
- } while ((i != i_init) && (step_tot != direction));
-
- if (i != i_init) {
- value = item_array[i].value;
- }
-
- if (free) {
- MEM_freeN(item_array);
- }
-
- return value;
-}
bool ui_but_menu_step_poll(const uiBut *but)
{
@@ -122,7 +90,8 @@ int ui_but_menu_step(uiBut *but, int direction)
return but->menu_step_func(but->block->evil_C, direction, but->poin);
}
else {
- return rna_property_enum_step(but->block->evil_C, &but->rnapoin, but->rnaprop, direction);
+ const int curval = RNA_property_enum_get(&but->rnapoin, but->rnaprop);
+ return RNA_property_enum_step(but->block->evil_C, &but->rnapoin, but->rnaprop, curval, direction);
}
}
@@ -484,20 +453,30 @@ static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but)
}
MEM_freeN(str);
+ }
+
+ /* button is disabled, we may be able to tell user why */
+ if ((but->flag & UI_BUT_DISABLED) || but->lock) {
+ const char *disabled_msg = NULL;
- /* second check if we are disabled - why */
- if (but->flag & UI_BUT_DISABLED) {
- const char *poll_msg;
+ /* if operator poll check failed, it can give pretty precise info why */
+ if (but->optype) {
CTX_wm_operator_poll_msg_set(C, NULL);
WM_operator_poll_context(C, but->optype, but->opcontext);
- poll_msg = CTX_wm_operator_poll_msg_get(C);
- if (poll_msg) {
- BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Disabled: %s"), poll_msg);
- data->format[data->totline].color_id = UI_TIP_LC_ALERT;
- data->totline++;
- }
+ disabled_msg = CTX_wm_operator_poll_msg_get(C);
+ }
+ /* alternatively, buttons can store some reasoning too */
+ else if (but->lockstr) {
+ disabled_msg = but->lockstr;
+ }
+
+ if (disabled_msg && disabled_msg[0]) {
+ BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Disabled: %s"), disabled_msg);
+ data->format[data->totline].color_id = UI_TIP_LC_ALERT;
+ data->totline++;
}
}
+
if ((U.flag & USER_TOOLTIPS_PYTHON) == 0 && !but->optype && rna_struct.strinfo) {
if (rna_prop.strinfo) {
/* Struct and prop */
diff --git a/source/blender/editors/io/io_alembic.c b/source/blender/editors/io/io_alembic.c
index cd75983e0a0..2256bd7f8c5 100644
--- a/source/blender/editors/io/io_alembic.c
+++ b/source/blender/editors/io/io_alembic.c
@@ -34,6 +34,7 @@
#include "MEM_guardedalloc.h"
#include "DNA_mesh_types.h"
+#include "DNA_modifier_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "DNA_space_types.h"
@@ -121,6 +122,9 @@ static int wm_alembic_export_exec(bContext *C, wmOperator *op)
.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"),
+ .triangulate = RNA_boolean_get(op->ptr, "triangulate"),
+ .quad_method = RNA_enum_get(op->ptr, "quad_method"),
+ .ngon_method = RNA_enum_get(op->ptr, "ngon_method"),
.global_scale = RNA_float_get(op->ptr, "global_scale"),
};
@@ -212,6 +216,19 @@ static void ui_alembic_export_settings(uiLayout *layout, PointerRNA *imfptr)
row = uiLayoutRow(box, false);
uiItemR(row, imfptr, "apply_subdiv", 0, NULL, ICON_NONE);
+
+ row = uiLayoutRow(box, false);
+ uiItemR(row, imfptr, "triangulate", 0, NULL, ICON_NONE);
+
+ const bool triangulate = RNA_boolean_get(imfptr, "triangulate");
+
+ row = uiLayoutRow(box, false);
+ uiLayoutSetEnabled(row, triangulate);
+ uiItemR(row, imfptr, "quad_method", 0, NULL, ICON_NONE);
+
+ row = uiLayoutRow(box, false);
+ uiLayoutSetEnabled(row, triangulate);
+ uiItemR(row, imfptr, "ngon_method", 0, NULL, ICON_NONE);
}
static void wm_alembic_export_draw(bContext *UNUSED(C), wmOperator *op)
@@ -308,6 +325,15 @@ void WM_OT_alembic_export(wmOperatorType *ot)
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);
+
+ RNA_def_boolean(ot->srna, "triangulate", false, "Triangulate",
+ "Export Polygons (Quads & NGons) as Triangles");
+
+ RNA_def_enum(ot->srna, "quad_method", rna_enum_modifier_triangulate_quad_method_items,
+ MOD_TRIANGULATE_QUAD_SHORTEDGE, "Quad Method", "Method for splitting the quads into triangles");
+
+ RNA_def_enum(ot->srna, "ngon_method", rna_enum_modifier_triangulate_quad_method_items,
+ MOD_TRIANGULATE_NGON_BEAUTY, "Polygon Method", "Method for splitting the polygons into triangles");
}
/* ************************************************************************** */
diff --git a/source/blender/editors/render/render_opengl.c b/source/blender/editors/render/render_opengl.c
index 85c05ab0e5c..ee2772ce31a 100644
--- a/source/blender/editors/render/render_opengl.c
+++ b/source/blender/editors/render/render_opengl.c
@@ -40,6 +40,7 @@
#include "BLI_utildefines.h"
#include "BLI_jitter.h"
#include "BLI_threads.h"
+#include "BLI_task.h"
#include "DNA_scene_types.h"
#include "DNA_object_types.h"
@@ -74,9 +75,15 @@
#include "GPU_compositing.h"
#include "GPU_framebuffer.h"
-
#include "render_intern.h"
+/* Define this to get timing information. */
+// #undef DEBUG_TIME
+
+#ifdef DEBUG_TIME
+# include "PIL_time.h"
+#endif
+
typedef struct OGLRender {
Main *bmain;
Render *re;
@@ -123,6 +130,14 @@ typedef struct OGLRender {
wmTimer *timer; /* use to check if running modal or not (invoke'd or exec'd)*/
void **movie_ctx_arr;
+
+ TaskPool *task_pool;
+ bool pool_ok;
+ SpinLock reports_lock;
+
+#ifdef DEBUG_TIME
+ double time_start;
+#endif
} OGLRender;
/* added because v3d is not always valid */
@@ -182,6 +197,9 @@ static void screen_opengl_views_setup(OGLRender *oglrender)
if (rv_del->rectz)
MEM_freeN(rv_del->rectz);
+ if (rv_del->rect32)
+ MEM_freeN(rv_del->rect32);
+
MEM_freeN(rv_del);
}
}
@@ -194,8 +212,6 @@ static void screen_opengl_views_setup(OGLRender *oglrender)
while (rv) {
srv = BLI_findstring(&rd->views, rv->name, offsetof(SceneRenderView, name));
if (BKE_scene_multiview_is_render_view_active(rd, srv)) {
- if (rv->rectf == NULL)
- rv->rectf = MEM_callocN(sizeof(float) * 4 * oglrender->sizex * oglrender->sizey, "screen_opengl_render_init rect");
rv = rv->prev;
}
else {
@@ -210,6 +226,9 @@ static void screen_opengl_views_setup(OGLRender *oglrender)
if (rv_del->rectz)
MEM_freeN(rv_del->rectz);
+ if (rv_del->rect32)
+ MEM_freeN(rv_del->rect32);
+
MEM_freeN(rv_del);
}
}
@@ -229,12 +248,6 @@ static void screen_opengl_views_setup(OGLRender *oglrender)
}
}
- for (rv = rr->views.first; rv; rv = rv->next) {
- if (rv->rectf == NULL) {
- rv->rectf = MEM_callocN(sizeof(float) * 4 * oglrender->sizex * oglrender->sizey, "screen_opengl_render_init rect");
- }
- }
-
BLI_lock_thread(LOCK_DRAW_IMAGE);
if (!(is_multiview && BKE_scene_multiview_is_stereo3d(rd)))
oglrender->iuser.flag &= ~IMA_SHOW_STEREO;
@@ -266,6 +279,7 @@ static void screen_opengl_render_doit(OGLRender *oglrender, RenderResult *rr)
bool draw_sky = (scene->r.alphamode == R_ADDSKY);
unsigned char *rect = NULL;
const char *viewname = RE_GetActiveRenderView(oglrender->re);
+ ImBuf *ibuf_result = NULL;
if (oglrender->is_sequencer) {
SpaceSeq *sseq = oglrender->sseq;
@@ -349,46 +363,22 @@ static void screen_opengl_render_doit(OGLRender *oglrender, RenderResult *rr)
}
if (ibuf_view) {
- /* steal rect reference from ibuf */
+ ibuf_result = ibuf_view;
rect = (unsigned char *)ibuf_view->rect;
- ibuf_view->mall &= ~IB_rect;
-
- IMB_freeImBuf(ibuf_view);
}
else {
fprintf(stderr, "%s: failed to get buffer, %s\n", __func__, err_out);
}
}
- /* note on color management:
- *
- * OpenGL renders into sRGB colors, but render buffers are expected to be
- * linear So we convert to linear here, so the conversion back to bytes can make it
- * sRGB (or other display space) again, and so that e.g. openexr saving also saves the
- * correct linear float buffer.
- */
-
- if (rect) {
- int profile_to;
- float *rectf = RE_RenderViewGetById(rr, oglrender->view_id)->rectf;
+ if (ibuf_result != NULL) {
- if (BKE_scene_check_color_management_enabled(scene))
- profile_to = IB_PROFILE_LINEAR_RGB;
- else
- profile_to = IB_PROFILE_SRGB;
-
- /* sequencer has got trickier conversion happened above
- * also assume opengl's space matches byte buffer color space */
- IMB_buffer_float_from_byte(rectf, rect,
- profile_to, IB_PROFILE_SRGB, true,
- oglrender->sizex, oglrender->sizey, oglrender->sizex, oglrender->sizex);
-
- /* rr->rectf is now filled with image data */
+ RE_render_result_rect_from_ibuf(rr, &scene->r, ibuf_result, oglrender->view_id);
if ((scene->r.stamp & R_STAMP_ALL) && (scene->r.stamp & R_STAMP_DRAW))
- BKE_image_stamp_buf(scene, camera, NULL, rect, rectf, rr->rectx, rr->recty, 4);
+ BKE_image_stamp_buf(scene, camera, NULL, rect, NULL, rr->rectx, rr->recty, 4);
- MEM_freeN(rect);
+ IMB_freeImBuf(ibuf_result);
}
}
@@ -445,6 +435,9 @@ static void add_gpencil_renderpass(OGLRender *oglrender, RenderResult *rr, Rende
if (BLI_listbase_is_empty(&gpd->layers)) {
return;
}
+ if ((oglrender->v3d->flag2 & V3D_SHOW_GPENCIL) == 0) {
+ return;
+ }
/* save old alpha mode */
short oldalphamode = scene->r.alphamode;
@@ -477,19 +470,21 @@ static void add_gpencil_renderpass(OGLRender *oglrender, RenderResult *rr, Rende
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;
+ // XXX: Needs conversion.
+ unsigned char *src = (unsigned char *)RE_RenderViewGetById(rr, oglrender->view_id)->rect32;
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);
+ unsigned char *pixSrc = src + 4 * (rectx * y + x);
+ if (pixSrc[3] > 0) {
+ float *pixDest = dest + 4 * (rectx * y + x);
+ float float_src[4];
+ srgb_to_linearrgb_uchar4(float_src, pixSrc);
+ addAlphaOverFloat(pixDest, float_src);
}
}
}
@@ -695,6 +690,28 @@ static bool screen_opengl_render_init(bContext *C, wmOperator *op)
oglrender->mh = NULL;
oglrender->movie_ctx_arr = NULL;
+ if (is_animation) {
+ TaskScheduler *task_scheduler = BLI_task_scheduler_get();
+ if (BKE_imtype_is_movie(scene->r.im_format.imtype)) {
+ oglrender->task_pool = BLI_task_pool_create_background(task_scheduler,
+ oglrender);
+ BLI_pool_set_num_threads(oglrender->task_pool, 1);
+ }
+ else {
+ oglrender->task_pool = BLI_task_pool_create(task_scheduler,
+ oglrender);
+ }
+ }
+ else {
+ oglrender->task_pool = NULL;
+ }
+ oglrender->pool_ok = true;
+ BLI_spin_init(&oglrender->reports_lock);
+
+#ifdef DEBUG_TIME
+ oglrender->time_start = PIL_check_seconds_timer();
+#endif
+
return true;
}
@@ -704,6 +721,14 @@ static void screen_opengl_render_end(bContext *C, OGLRender *oglrender)
Scene *scene = oglrender->scene;
int i;
+ BLI_task_pool_work_and_wait(oglrender->task_pool);
+ BLI_task_pool_free(oglrender->task_pool);
+ BLI_spin_end(&oglrender->reports_lock);
+
+#ifdef DEBUG_TIME
+ printf("Total render time: %f\n", PIL_check_seconds_timer() - oglrender->time_start);
+#endif
+
if (oglrender->mh) {
if (BKE_imtype_is_movie(scene->r.im_format.imtype)) {
for (i = 0; i < oglrender->totvideos; i++) {
@@ -798,6 +823,102 @@ static bool screen_opengl_render_anim_initialize(bContext *C, wmOperator *op)
return true;
}
+typedef struct WriteTaskData {
+ RenderResult *rr;
+ int cfra;
+} WriteTaskData;
+
+static void write_result_func(TaskPool * __restrict pool,
+ void *task_data_v,
+ int UNUSED(thread_id))
+{
+ OGLRender *oglrender = (OGLRender *) BLI_task_pool_userdata(pool);
+ WriteTaskData *task_data = (WriteTaskData *) task_data_v;
+ Scene *scene = oglrender->scene;
+ RenderResult *rr = task_data->rr;
+ const bool is_movie = BKE_imtype_is_movie(scene->r.im_format.imtype);
+ const int cfra = task_data->cfra;
+ bool ok;
+ /* Construct local thread0safe copy of reports structure which we can
+ * safely pass to the underlying functions.
+ */
+ ReportList reports;
+ BKE_reports_init(&reports, oglrender->reports->flag & ~RPT_PRINT);
+ /* Do actual save logic here, depending on the file format. */
+ if (is_movie) {
+ /* We have to construct temporary scene with proper scene->r.cfra.
+ * This is because underlying calls do not use r.cfra but use scene
+ * for that.
+ */
+ Scene tmp_scene = *scene;
+ tmp_scene.r.cfra = cfra;
+ ok = RE_WriteRenderViewsMovie(&reports,
+ rr,
+ &tmp_scene,
+ &tmp_scene.r,
+ oglrender->mh,
+ oglrender->movie_ctx_arr,
+ oglrender->totvideos,
+ PRVRANGEON != 0);
+ }
+ else {
+ /* TODO(sergey): We can in theory save some CPU ticks here because we
+ * calculate file name again here.
+ */
+ char name[FILE_MAX];
+ BKE_image_path_from_imformat(name,
+ scene->r.pic,
+ oglrender->bmain->name,
+ cfra,
+ &scene->r.im_format,
+ (scene->r.scemode & R_EXTENSION) != 0,
+ true,
+ NULL);
+
+ BKE_render_result_stamp_info(scene, scene->camera, rr, false);
+ ok = RE_WriteRenderViewsImage(NULL, rr, scene, true, name);
+ if (!ok) {
+ BKE_reportf(&reports,
+ RPT_ERROR,
+ "Write error: cannot save %s",
+ name);
+ }
+ }
+ if (reports.list.first != NULL) {
+ BLI_spin_lock(&oglrender->reports_lock);
+ for (Report *report = reports.list.first;
+ report != NULL;
+ report = report->next)
+ {
+ BKE_report(oglrender->reports,
+ report->type,
+ report->message);
+ }
+ BLI_spin_unlock(&oglrender->reports_lock);
+ }
+ if (!ok) {
+ oglrender->pool_ok = false;
+ }
+ RE_FreeRenderResult(rr);
+}
+
+static bool schedule_write_result(OGLRender *oglrender, RenderResult *rr)
+{
+ if (!oglrender->pool_ok) {
+ return false;
+ }
+ Scene *scene = oglrender->scene;
+ WriteTaskData *task_data = MEM_mallocN(sizeof(WriteTaskData), "write task data");
+ task_data->rr = rr;
+ task_data->cfra = scene->r.cfra;
+ BLI_task_pool_push(oglrender->task_pool,
+ write_result_func,
+ task_data,
+ true,
+ TASK_PRIORITY_LOW);
+ return true;
+}
+
static bool screen_opengl_render_anim_step(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
@@ -830,7 +951,9 @@ static bool screen_opengl_render_anim_step(bContext *C, wmOperator *op)
&scene->r.im_format, (scene->r.scemode & R_EXTENSION) != 0, true, NULL);
if ((scene->r.mode & R_NO_OVERWRITE) && BLI_exists(name)) {
+ BLI_spin_lock(&oglrender->reports_lock);
BKE_reportf(op->reports, RPT_INFO, "Skipping existing frame \"%s\"", name);
+ BLI_spin_unlock(&oglrender->reports_lock);
ok = true;
goto finally;
}
@@ -858,34 +981,10 @@ static bool screen_opengl_render_anim_step(bContext *C, wmOperator *op)
/* save to disk */
rr = RE_AcquireResultRead(oglrender->re);
-
- if (is_movie) {
- ok = RE_WriteRenderViewsMovie(oglrender->reports, rr, scene, &scene->r, oglrender->mh,
- oglrender->movie_ctx_arr, oglrender->totvideos, PRVRANGEON != 0);
- if (ok) {
- printf("Append frame %d", scene->r.cfra);
- BKE_reportf(op->reports, RPT_INFO, "Appended frame: %d", scene->r.cfra);
- }
- }
- else {
- BKE_render_result_stamp_info(scene, scene->camera, rr, false);
- ok = RE_WriteRenderViewsImage(op->reports, rr, scene, true, name);
- if (ok) {
- printf("Saved: %s", name);
- BKE_reportf(op->reports, RPT_INFO, "Saved file: %s", name);
- }
- else {
- printf("Write error: cannot save %s\n", name);
- BKE_reportf(op->reports, RPT_ERROR, "Write error: cannot save %s", name);
- }
- }
-
+ RenderResult *new_rr = RE_DuplicateRenderResult(rr);
RE_ReleaseResult(oglrender->re);
-
- /* movie stats prints have no line break */
- printf("\n");
-
+ ok = schedule_write_result(oglrender, new_rr);
finally: /* Step the frame and bail early if needed */
diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c
index 6dbb5db53d0..e446194a1da 100644
--- a/source/blender/editors/screen/screen_ops.c
+++ b/source/blender/editors/screen/screen_ops.c
@@ -4331,7 +4331,13 @@ void ED_keymap_screen(wmKeyConfig *keyconf)
WM_keymap_add_item(keymap, "SCREEN_OT_screenshot", F3KEY, KM_PRESS, KM_CTRL, 0);
WM_keymap_add_item(keymap, "SCREEN_OT_screencast", F3KEY, KM_PRESS, KM_ALT, 0);
-
+
+ /* UI */
+ kmi = WM_keymap_add_item(keymap, "UI_OT_space_context_cycle", TABKEY, KM_PRESS, KM_CTRL, 0);
+ RNA_enum_set(kmi->ptr, "direction", SPACE_CONTEXT_CYCLE_NEXT);
+ kmi = WM_keymap_add_item(keymap, "UI_OT_space_context_cycle", TABKEY, KM_PRESS, KM_CTRL | KM_SHIFT, 0);
+ RNA_enum_set(kmi->ptr, "direction", SPACE_CONTEXT_CYCLE_PREV);
+
/* tests */
WM_keymap_add_item(keymap, "SCREEN_OT_region_quadview", QKEY, KM_PRESS, KM_CTRL | KM_ALT, 0);
WM_keymap_verify_item(keymap, "SCREEN_OT_repeat_history", F3KEY, KM_PRESS, 0, 0);
diff --git a/source/blender/editors/sound/sound_ops.c b/source/blender/editors/sound/sound_ops.c
index ac3fc769ea1..52c39e5c7a1 100644
--- a/source/blender/editors/sound/sound_ops.c
+++ b/source/blender/editors/sound/sound_ops.c
@@ -217,32 +217,56 @@ static void SOUND_OT_open_mono(wmOperatorType *ot)
/* ******************************************************* */
-static int sound_update_animation_flags_exec(bContext *C, wmOperator *UNUSED(op))
+static void sound_update_animation_flags(Scene *scene);
+
+static int sound_update_animation_flags_cb(Sequence *seq, void *user_data)
{
- Sequence *seq;
- Scene *scene = CTX_data_scene(C);
struct FCurve *fcu;
+ Scene *scene = (Scene *)user_data;
bool driven;
- SEQ_BEGIN(scene->ed, seq)
- {
- fcu = id_data_find_fcurve(&scene->id, seq, &RNA_Sequence, "volume", 0, &driven);
- if (fcu || driven)
- seq->flag |= SEQ_AUDIO_VOLUME_ANIMATED;
- else
- seq->flag &= ~SEQ_AUDIO_VOLUME_ANIMATED;
+ fcu = id_data_find_fcurve(&scene->id, seq, &RNA_Sequence, "volume", 0, &driven);
+ if (fcu || driven)
+ seq->flag |= SEQ_AUDIO_VOLUME_ANIMATED;
+ else
+ seq->flag &= ~SEQ_AUDIO_VOLUME_ANIMATED;
- fcu = id_data_find_fcurve(&scene->id, seq, &RNA_Sequence, "pitch", 0, &driven);
- if (fcu || driven)
- seq->flag |= SEQ_AUDIO_PITCH_ANIMATED;
- else
- seq->flag &= ~SEQ_AUDIO_PITCH_ANIMATED;
+ fcu = id_data_find_fcurve(&scene->id, seq, &RNA_Sequence, "pitch", 0, &driven);
+ if (fcu || driven)
+ seq->flag |= SEQ_AUDIO_PITCH_ANIMATED;
+ else
+ seq->flag &= ~SEQ_AUDIO_PITCH_ANIMATED;
- fcu = id_data_find_fcurve(&scene->id, seq, &RNA_Sequence, "pan", 0, &driven);
- if (fcu || driven)
- seq->flag |= SEQ_AUDIO_PAN_ANIMATED;
- else
- seq->flag &= ~SEQ_AUDIO_PAN_ANIMATED;
+ fcu = id_data_find_fcurve(&scene->id, seq, &RNA_Sequence, "pan", 0, &driven);
+ if (fcu || driven)
+ seq->flag |= SEQ_AUDIO_PAN_ANIMATED;
+ else
+ seq->flag &= ~SEQ_AUDIO_PAN_ANIMATED;
+
+ if (seq->type == SEQ_TYPE_SCENE) {
+ /* TODO(sergey): For now we do manual recursion into the scene strips,
+ * but perhaps it should be covered by recursive_apply?
+ */
+ sound_update_animation_flags(seq->scene);
+ }
+
+ return 0;
+}
+
+static void sound_update_animation_flags(Scene *scene)
+{
+ struct FCurve *fcu;
+ bool driven;
+ Sequence *seq;
+
+ if (scene->id.tag & LIB_TAG_DOIT) {
+ return;
+ }
+ scene->id.tag |= LIB_TAG_DOIT;
+
+ SEQ_BEGIN(scene->ed, seq)
+ {
+ BKE_sequencer_recursive_apply(seq, sound_update_animation_flags_cb, scene);
}
SEQ_END
@@ -251,7 +275,12 @@ static int sound_update_animation_flags_exec(bContext *C, wmOperator *UNUSED(op)
scene->audio.flag |= AUDIO_VOLUME_ANIMATED;
else
scene->audio.flag &= ~AUDIO_VOLUME_ANIMATED;
+}
+static int sound_update_animation_flags_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ BKE_main_id_tag_idcode(CTX_data_main(C), ID_SCE, LIB_TAG_DOIT, false);
+ sound_update_animation_flags(CTX_data_scene(C));
return OPERATOR_FINISHED;
}
diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c
index 14719322bf7..6af36ea6778 100644
--- a/source/blender/editors/space_file/filelist.c
+++ b/source/blender/editors/space_file/filelist.c
@@ -1405,7 +1405,9 @@ void filelist_setdir(struct FileList *filelist, char *r_dir)
BLI_assert(strlen(r_dir) < FILE_MAX_LIBEXTRA);
BLI_cleanup_dir(G.main->name, r_dir);
- BLI_assert(filelist->checkdirf(filelist, r_dir, true));
+ const bool is_valid_path = filelist->checkdirf(filelist, r_dir, true);
+ BLI_assert(is_valid_path);
+ UNUSED_VARS_NDEBUG(is_valid_path);
if (!STREQ(filelist->filelist.root, r_dir)) {
BLI_strncpy(filelist->filelist.root, r_dir, sizeof(filelist->filelist.root));
@@ -1651,6 +1653,7 @@ bool filelist_file_cache_block(struct FileList *filelist, const int index)
int start_index = max_ii(0, index - (cache_size / 2));
int end_index = min_ii(nbr_entries, index + (cache_size / 2));
int i;
+ const bool full_refresh = (filelist->flags & FL_IS_READY) == 0;
if ((index < 0) || (index >= nbr_entries)) {
// printf("Wrong index %d ([%d:%d])", index, 0, nbr_entries);
@@ -1673,8 +1676,8 @@ bool filelist_file_cache_block(struct FileList *filelist, const int index)
// start_index, end_index, index, cache->block_start_index, cache->block_end_index);
/* If we have something to (re)cache... */
- if ((start_index != cache->block_start_index) || (end_index != cache->block_end_index)) {
- if ((start_index >= cache->block_end_index) || (end_index <= cache->block_start_index)) {
+ if (full_refresh || (start_index != cache->block_start_index) || (end_index != cache->block_end_index)) {
+ if (full_refresh || (start_index >= cache->block_end_index) || (end_index <= cache->block_start_index)) {
int size1 = cache->block_end_index - cache->block_start_index;
int size2 = 0;
int idx1 = cache->block_cursor, idx2 = 0;
diff --git a/source/blender/editors/space_graph/graph_draw.c b/source/blender/editors/space_graph/graph_draw.c
index ae91a466495..96a078b2817 100644
--- a/source/blender/editors/space_graph/graph_draw.c
+++ b/source/blender/editors/space_graph/graph_draw.c
@@ -1131,7 +1131,7 @@ void graph_draw_channel_names(bContext *C, bAnimContext *ac, ARegion *ar)
* start of list offset, and the second is as a correction for the scrollers.
*/
height = (float)((items * ACHANNEL_STEP(ac)) + (ACHANNEL_HEIGHT(ac) * 2));
- UI_view2d_totRect_set(v2d, ar->winx, height);
+ UI_view2d_totRect_set(v2d, BLI_rcti_size_x(&ar->v2d.mask), height);
/* loop through channels, and set up drawing depending on their type */
{ /* first pass: just the standard GL-drawing for backdrop + text */
diff --git a/source/blender/editors/space_graph/space_graph.c b/source/blender/editors/space_graph/space_graph.c
index b35d1b2c777..f12db310856 100644
--- a/source/blender/editors/space_graph/space_graph.c
+++ b/source/blender/editors/space_graph/space_graph.c
@@ -335,8 +335,8 @@ static void graph_channel_region_init(wmWindowManager *wm, ARegion *ar)
wmKeyMap *keymap;
/* make sure we keep the hide flags */
- ar->v2d.scroll |= (V2D_SCROLL_RIGHT | V2D_SCROLL_BOTTOM);
- ar->v2d.scroll &= ~(V2D_SCROLL_LEFT | V2D_SCROLL_TOP); /* prevent any noise of past */
+ ar->v2d.scroll |= V2D_SCROLL_RIGHT;
+ ar->v2d.scroll &= ~(V2D_SCROLL_LEFT | V2D_SCROLL_TOP | V2D_SCROLL_BOTTOM); /* prevent any noise of past */
ar->v2d.scroll |= V2D_SCROLL_HORIZONTAL_HIDE;
ar->v2d.scroll |= V2D_SCROLL_VERTICAL_HIDE;
diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c
index 1f591b5fb35..a89d3494af0 100644
--- a/source/blender/editors/space_image/image_ops.c
+++ b/source/blender/editors/space_image/image_ops.c
@@ -1608,6 +1608,11 @@ static int save_image_options_init(SaveImageOptions *simopts, SpaceImage *sima,
/* imtype */
simopts->im_format = scene->r.im_format;
is_depth_set = true;
+ if (!BKE_image_is_multiview(ima)) {
+ /* In case multiview is disabled, render settings would be invalid for render result in this area. */
+ simopts->im_format.stereo3d_format = *ima->stereo3d_format;
+ simopts->im_format.views_format = ima->views_format;
+ }
}
else {
if (ima->source == IMA_SRC_GENERATED) {
diff --git a/source/blender/editors/space_outliner/outliner_edit.c b/source/blender/editors/space_outliner/outliner_edit.c
index 3c47f542dae..65608c8c15a 100644
--- a/source/blender/editors/space_outliner/outliner_edit.c
+++ b/source/blender/editors/space_outliner/outliner_edit.c
@@ -245,18 +245,15 @@ static void do_item_rename(ARegion *ar, TreeElement *te, TreeStoreElem *tselem,
}
void item_rename_cb(
- bContext *C, ReportList *UNUSED(reports), Scene *UNUSED(scene), TreeElement *te,
+ bContext *C, ReportList *reports, Scene *UNUSED(scene), TreeElement *te,
TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem, void *UNUSED(user_data))
{
ARegion *ar = CTX_wm_region(C);
- ReportList *reports = CTX_wm_reports(C); // XXX
do_item_rename(ar, te, tselem, reports);
}
-static int do_outliner_item_rename(bContext *C, ARegion *ar, SpaceOops *soops, TreeElement *te, const float mval[2])
-{
- ReportList *reports = CTX_wm_reports(C); // XXX
-
+static int do_outliner_item_rename(ReportList *reports, ARegion *ar, SpaceOops *soops, TreeElement *te, const float mval[2])
+{
if (mval[1] > te->ys && mval[1] < te->ys + UI_UNIT_Y) {
TreeStoreElem *tselem = TREESTORE(te);
@@ -269,12 +266,12 @@ static int do_outliner_item_rename(bContext *C, ARegion *ar, SpaceOops *soops, T
}
for (te = te->subtree.first; te; te = te->next) {
- if (do_outliner_item_rename(C, ar, soops, te, mval)) return 1;
+ if (do_outliner_item_rename(reports, ar, soops, te, mval)) return 1;
}
return 0;
}
-static int outliner_item_rename(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
+static int outliner_item_rename(bContext *C, wmOperator *op, const wmEvent *event)
{
ARegion *ar = CTX_wm_region(C);
SpaceOops *soops = CTX_wm_space_outliner(C);
@@ -285,7 +282,7 @@ static int outliner_item_rename(bContext *C, wmOperator *UNUSED(op), const wmEve
UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]);
for (te = soops->tree.first; te; te = te->next) {
- if (do_outliner_item_rename(C, ar, soops, te, fmval)) {
+ if (do_outliner_item_rename(op->reports, ar, soops, te, fmval)) {
changed = true;
break;
}
@@ -788,12 +785,17 @@ int common_restrict_check(bContext *C, Object *ob)
/* Toggle Visibility ---------------------------------------- */
void object_toggle_visibility_cb(
- bContext *C, ReportList *UNUSED(reports), Scene *scene, TreeElement *te,
+ bContext *C, ReportList *reports, Scene *scene, TreeElement *te,
TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem, void *UNUSED(user_data))
{
Base *base = (Base *)te->directdata;
Object *ob = (Object *)tselem->id;
-
+
+ if (ID_IS_LINKED_DATABLOCK(tselem->id)) {
+ BKE_report(reports, RPT_WARNING, "Cannot edit external libdata");
+ return;
+ }
+
/* add check for edit mode */
if (!common_restrict_check(C, ob)) return;
@@ -845,11 +847,16 @@ void OUTLINER_OT_visibility_toggle(wmOperatorType *ot)
/* Toggle Selectability ---------------------------------------- */
void object_toggle_selectability_cb(
- bContext *UNUSED(C), ReportList *UNUSED(reports), Scene *scene, TreeElement *te,
+ bContext *UNUSED(C), ReportList *reports, Scene *scene, TreeElement *te,
TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem, void *UNUSED(user_data))
{
Base *base = (Base *)te->directdata;
-
+
+ if (ID_IS_LINKED_DATABLOCK(tselem->id)) {
+ BKE_report(reports, RPT_WARNING, "Cannot edit external libdata");
+ return;
+ }
+
if (base == NULL) base = BKE_scene_base_find(scene, (Object *)tselem->id);
if (base) {
base->object->restrictflag ^= OB_RESTRICT_SELECT;
@@ -895,11 +902,16 @@ void OUTLINER_OT_selectability_toggle(wmOperatorType *ot)
/* Toggle Renderability ---------------------------------------- */
void object_toggle_renderability_cb(
- bContext *UNUSED(C), ReportList *UNUSED(reports), Scene *scene, TreeElement *te,
+ bContext *UNUSED(C), ReportList *reports, Scene *scene, TreeElement *te,
TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem, void *UNUSED(user_data))
{
Base *base = (Base *)te->directdata;
-
+
+ if (ID_IS_LINKED_DATABLOCK(tselem->id)) {
+ BKE_report(reports, RPT_WARNING, "Cannot edit external libdata");
+ return;
+ }
+
if (base == NULL) base = BKE_scene_base_find(scene, (Object *)tselem->id);
if (base) {
base->object->restrictflag ^= OB_RESTRICT_RENDER;
diff --git a/source/blender/editors/space_view3d/drawobject.c b/source/blender/editors/space_view3d/drawobject.c
index 7ac74fb4bc0..d308c11fe9e 100644
--- a/source/blender/editors/space_view3d/drawobject.c
+++ b/source/blender/editors/space_view3d/drawobject.c
@@ -6659,123 +6659,80 @@ void draw_object(Scene *scene, ARegion *ar, View3D *v3d, Base *base, const short
}
}
- /* draw code for smoke */
- if (smd) {
-#if 0
- /* draw collision objects */
- if ((smd->type & MOD_SMOKE_TYPE_COLL) && smd->coll) {
- SmokeCollSettings *scs = smd->coll;
- if (scs->points) {
- size_t i;
-
- glLoadMatrixf(rv3d->viewmat);
-
- if (col || (ob->flag & SELECT)) cpack(0xFFFFFF);
- glDepthMask(GL_FALSE);
- glEnable(GL_BLEND);
-
+ /* draw code for smoke, only draw domains */
+ if (smd && smd->domain) {
+ SmokeDomainSettings *sds = smd->domain;
+ const bool show_smoke = true; /* XXX was checking cached frame range before */
+ float viewnormal[3];
- // glPointSize(3.0);
- glBegin(GL_POINTS);
-
- for (i = 0; i < scs->numpoints; i++)
- {
- glVertex3fv(&scs->points[3 * i]);
- }
+ glLoadMatrixf(rv3d->viewmat);
+ glMultMatrixf(ob->obmat);
- glEnd();
+ if (!render_override) {
+ BoundBox bb;
+ float p0[3], p1[3];
- glMultMatrixf(ob->obmat);
- glDisable(GL_BLEND);
- glDepthMask(GL_TRUE);
- if (col) cpack(col);
+ /* draw max domain bounds */
+ if ((sds->flags & MOD_SMOKE_ADAPTIVE_DOMAIN)) {
+ VECSUBFAC(p0, sds->p0, sds->cell_size, sds->adapt_res);
+ VECADDFAC(p1, sds->p1, sds->cell_size, sds->adapt_res);
+ BKE_boundbox_init_from_minmax(&bb, p0, p1);
+ draw_box(bb.vec, false);
}
- }
-#endif
- /* only draw domains */
- if (smd->domain) {
- SmokeDomainSettings *sds = smd->domain;
- const bool show_smoke = true; /* XXX was checking cached frame range before */
- float viewnormal[3];
+ /* draw a single voxel to hint the user about the resolution of the fluid */
+ copy_v3_v3(p0, sds->p0);
- glLoadMatrixf(rv3d->viewmat);
- glMultMatrixf(ob->obmat);
-
- if (!render_override) {
- BoundBox bb;
- float p0[3], p1[3];
-
- /* draw adaptive domain bounds */
- if ((sds->flags & MOD_SMOKE_ADAPTIVE_DOMAIN)) {
- /* draw domain max bounds */
- VECSUBFAC(p0, sds->p0, sds->cell_size, sds->adapt_res);
- VECADDFAC(p1, sds->p1, sds->cell_size, sds->adapt_res);
- BKE_boundbox_init_from_minmax(&bb, p0, p1);
- draw_box(bb.vec, false);
- }
+ if (sds->flags & MOD_SMOKE_HIGHRES) {
+ madd_v3_v3v3fl(p1, p0, sds->cell_size, 1.0f / (sds->amplify + 1));
+ }
+ else {
+ add_v3_v3v3(p1, p0, sds->cell_size);
+ }
-#if 0
- /* draw base resolution bounds */
- BKE_boundbox_init_from_minmax(&bb, sds->p0, sds->p1);
- draw_box(bb.vec);
-#endif
+ BKE_boundbox_init_from_minmax(&bb, p0, p1);
+ draw_box(bb.vec, false);
+ }
+ /* don't show smoke before simulation starts, this could be made an option in the future */
+ if (sds->fluid && show_smoke) {
+ float p0[3], p1[3];
- /* draw a single voxel to hint the user about the resolution of the fluid */
- copy_v3_v3(p0, sds->p0);
+ /* get view vector */
+ invert_m4_m4(ob->imat, ob->obmat);
+ mul_v3_mat3_m4v3(viewnormal, ob->imat, rv3d->viewinv[2]);
+ normalize_v3(viewnormal);
- if (sds->flags & MOD_SMOKE_HIGHRES) {
- madd_v3_v3v3fl(p1, p0, sds->cell_size, 1.0f / (sds->amplify + 1));
- }
- else {
- add_v3_v3v3(p1, p0, sds->cell_size);
- }
+ /* set dynamic boundaries to draw the volume
+ * also scale cube to global space to equalize volume slicing on all axes
+ * (it's scaled back before drawing) */
+ p0[0] = (sds->p0[0] + sds->cell_size[0] * sds->res_min[0] + sds->obj_shift_f[0]) * fabsf(ob->size[0]);
+ p0[1] = (sds->p0[1] + sds->cell_size[1] * sds->res_min[1] + sds->obj_shift_f[1]) * fabsf(ob->size[1]);
+ p0[2] = (sds->p0[2] + sds->cell_size[2] * sds->res_min[2] + sds->obj_shift_f[2]) * fabsf(ob->size[2]);
+ p1[0] = (sds->p0[0] + sds->cell_size[0] * sds->res_max[0] + sds->obj_shift_f[0]) * fabsf(ob->size[0]);
+ p1[1] = (sds->p0[1] + sds->cell_size[1] * sds->res_max[1] + sds->obj_shift_f[1]) * fabsf(ob->size[1]);
+ p1[2] = (sds->p0[2] + sds->cell_size[2] * sds->res_max[2] + sds->obj_shift_f[2]) * fabsf(ob->size[2]);
- BKE_boundbox_init_from_minmax(&bb, p0, p1);
- draw_box(bb.vec, false);
+ if (!sds->wt || !(sds->viewsettings & MOD_SMOKE_VIEW_SHOWBIG)) {
+ sds->tex = NULL;
+ GPU_create_smoke(smd, 0);
+ draw_smoke_volume(sds, ob, p0, p1, viewnormal);
+ GPU_free_smoke(smd);
+ }
+ else if (sds->wt && (sds->viewsettings & MOD_SMOKE_VIEW_SHOWBIG)) {
+ sds->tex = NULL;
+ GPU_create_smoke(smd, 1);
+ draw_smoke_volume(sds, ob, p0, p1, viewnormal);
+ GPU_free_smoke(smd);
}
- /* don't show smoke before simulation starts, this could be made an option in the future */
- if (sds->fluid && show_smoke) {
- float p0[3], p1[3];
-
- /* get view vector */
- invert_m4_m4(ob->imat, ob->obmat);
- mul_v3_mat3_m4v3(viewnormal, ob->imat, rv3d->viewinv[2]);
- normalize_v3(viewnormal);
-
- /* set dynamic boundaries to draw the volume
- * also scale cube to global space to equalize volume slicing on all axes
- * (it's scaled back before drawing) */
- p0[0] = (sds->p0[0] + sds->cell_size[0] * sds->res_min[0] + sds->obj_shift_f[0]) * fabsf(ob->size[0]);
- p0[1] = (sds->p0[1] + sds->cell_size[1] * sds->res_min[1] + sds->obj_shift_f[1]) * fabsf(ob->size[1]);
- p0[2] = (sds->p0[2] + sds->cell_size[2] * sds->res_min[2] + sds->obj_shift_f[2]) * fabsf(ob->size[2]);
- p1[0] = (sds->p0[0] + sds->cell_size[0] * sds->res_max[0] + sds->obj_shift_f[0]) * fabsf(ob->size[0]);
- p1[1] = (sds->p0[1] + sds->cell_size[1] * sds->res_max[1] + sds->obj_shift_f[1]) * fabsf(ob->size[1]);
- p1[2] = (sds->p0[2] + sds->cell_size[2] * sds->res_max[2] + sds->obj_shift_f[2]) * fabsf(ob->size[2]);
-
- if (!sds->wt || !(sds->viewsettings & MOD_SMOKE_VIEW_SHOWBIG)) {
- sds->tex = NULL;
- GPU_create_smoke(smd, 0);
- draw_smoke_volume(sds, ob, p0, p1, viewnormal);
- GPU_free_smoke(smd);
- }
- else if (sds->wt && (sds->viewsettings & MOD_SMOKE_VIEW_SHOWBIG)) {
- sds->tex = NULL;
- GPU_create_smoke(smd, 1);
- draw_smoke_volume(sds, ob, p0, p1, viewnormal);
- GPU_free_smoke(smd);
- }
-
- /* smoke debug render */
+ /* smoke debug render */
#ifdef SMOKE_DEBUG_VELOCITY
- draw_smoke_velocity(smd->domain, ob);
+ draw_smoke_velocity(smd->domain, ob);
#endif
#ifdef SMOKE_DEBUG_HEAT
- draw_smoke_heat(smd->domain, ob);
+ draw_smoke_heat(smd->domain, ob);
#endif
- }
}
}
diff --git a/source/blender/makesdna/DNA_anim_types.h b/source/blender/makesdna/DNA_anim_types.h
index 31fe8fe563e..e50a2637fff 100644
--- a/source/blender/makesdna/DNA_anim_types.h
+++ b/source/blender/makesdna/DNA_anim_types.h
@@ -939,7 +939,10 @@ typedef enum eAnimData_Flag {
ADT_UI_ACTIVE = (1<<15),
/* F-Curves from this AnimData block are not visible in the Graph Editor */
- ADT_CURVES_NOT_VISIBLE = (1<<16)
+ ADT_CURVES_NOT_VISIBLE = (1<<16),
+
+ /* F-Curves from this AnimData block are always visible */
+ ADT_CURVES_ALWAYS_VISIBLE = (1<<17),
} eAnimData_Flag;
/* Animation Data recalculation settings (to be set by depsgraph) */
diff --git a/source/blender/makesdna/DNA_gpencil_types.h b/source/blender/makesdna/DNA_gpencil_types.h
index 773d203bdb3..23b73424da5 100644
--- a/source/blender/makesdna/DNA_gpencil_types.h
+++ b/source/blender/makesdna/DNA_gpencil_types.h
@@ -336,7 +336,7 @@ typedef enum eGPdata_Flag {
/* Convenience/cache flag to make it easier to quickly toggle onion skinning on/off */
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)
+ GP_DATA_SHOW_DIRECTION = (1 << 10)
} eGPdata_Flag;
#endif /* __DNA_GPENCIL_TYPES_H__ */
diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h
index d59dad5762b..33695dad163 100644
--- a/source/blender/makesdna/DNA_scene_types.h
+++ b/source/blender/makesdna/DNA_scene_types.h
@@ -1134,7 +1134,11 @@ typedef enum eGP_BrushEdit_SettingsFlag {
/* apply brush to strength */
GP_BRUSHEDIT_FLAG_APPLY_STRENGTH = (1 << 2),
/* apply brush to thickness */
- GP_BRUSHEDIT_FLAG_APPLY_THICKNESS = (1 << 3)
+ GP_BRUSHEDIT_FLAG_APPLY_THICKNESS = (1 << 3),
+ /* apply interpolation to all layers */
+ GP_BRUSHEDIT_FLAG_INTERPOLATE_ALL_LAYERS = (1 << 4),
+ /* apply interpolation to only selected */
+ GP_BRUSHEDIT_FLAG_INTERPOLATE_ONLY_SELECTED = (1 << 5)
} eGP_BrushEdit_SettingsFlag;
diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h
index a52f4548733..d430049557d 100644
--- a/source/blender/makesrna/RNA_access.h
+++ b/source/blender/makesrna/RNA_access.h
@@ -892,6 +892,7 @@ int RNA_property_enum_get(PointerRNA *ptr, PropertyRNA *prop);
void RNA_property_enum_set(PointerRNA *ptr, PropertyRNA *prop, int value);
int RNA_property_enum_get_default(PointerRNA *ptr, PropertyRNA *prop);
void *RNA_property_enum_py_data_get(PropertyRNA *prop);
+int RNA_property_enum_step(const struct bContext *C, PointerRNA *ptr, PropertyRNA *prop, int from_value, int direction);
PointerRNA RNA_property_pointer_get(PointerRNA *ptr, PropertyRNA *prop);
void RNA_property_pointer_set(PointerRNA *ptr, PropertyRNA *prop, PointerRNA ptr_value);
diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c
index 282da6c0cd4..9eb9b2ade6c 100644
--- a/source/blender/makesrna/intern/rna_ID.c
+++ b/source/blender/makesrna/intern/rna_ID.c
@@ -985,12 +985,12 @@ static void rna_def_ID(BlenderRNA *brna)
RNA_def_function_ui_description(func, "Replace all usage in the .blend file of this ID by new given one");
RNA_def_function_flag(func, FUNC_USE_MAIN);
parm = RNA_def_pointer(func, "new_id", "ID", "", "New ID to use");
- RNA_def_property_flag(parm, PROP_NEVER_NULL);
+ RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL);
func = RNA_def_function(srna, "user_of_id", "BKE_library_ID_use_ID");
RNA_def_function_ui_description(func, "Count the number of times that ID uses/references given one");
parm = RNA_def_pointer(func, "id", "ID", "", "ID to count usages");
- RNA_def_property_flag(parm, PROP_NEVER_NULL);
+ RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL);
parm = RNA_def_int(func, "count", 0, 0, INT_MAX,
"", "Number of usages/references of given id by current datablock", 0, INT_MAX);
RNA_def_function_return(func, parm);
diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c
index 047e5ea17ab..6879a0534e9 100644
--- a/source/blender/makesrna/intern/rna_access.c
+++ b/source/blender/makesrna/intern/rna_access.c
@@ -2873,6 +2873,45 @@ void *RNA_property_enum_py_data_get(PropertyRNA *prop)
return eprop->py_data;
}
+/**
+ * Get the value of the item that is \a step items away from \a from_value.
+ *
+ * \param from_value: Item value to start stepping from.
+ * \param step: Absolute value defines step size, sign defines direction.
+ * E.g to get the next item, pass 1, for the previous -1.
+ */
+int RNA_property_enum_step(const bContext *C, PointerRNA *ptr, PropertyRNA *prop, int from_value, int step)
+{
+ EnumPropertyItem *item_array;
+ int totitem;
+ bool free;
+ int result_value = from_value;
+ int i, i_init;
+ int single_step = (step < 0) ? -1 : 1;
+ int step_tot = 0;
+
+ RNA_property_enum_items((bContext *)C, ptr, prop, &item_array, &totitem, &free);
+ i = RNA_enum_from_value(item_array, from_value);
+ i_init = i;
+
+ do {
+ i = mod_i(i + single_step, totitem);
+ if (item_array[i].identifier[0]) {
+ step_tot += single_step;
+ }
+ } while ((i != i_init) && (step_tot != step));
+
+ if (i != i_init) {
+ result_value = item_array[i].value;
+ }
+
+ if (free) {
+ MEM_freeN(item_array);
+ }
+
+ return result_value;
+}
+
PointerRNA RNA_property_pointer_get(PointerRNA *ptr, PropertyRNA *prop)
{
PointerPropertyRNA *pprop = (PointerPropertyRNA *)prop;
diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h
index dbff87c8e1b..c406aa987e5 100644
--- a/source/blender/makesrna/intern/rna_internal.h
+++ b/source/blender/makesrna/intern/rna_internal.h
@@ -332,6 +332,7 @@ 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);
+void RNA_def_main_paintcurves(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 9a245ff0147..6686b23e847 100644
--- a/source/blender/makesrna/intern/rna_main.c
+++ b/source/blender/makesrna/intern/rna_main.c
@@ -281,6 +281,12 @@ static void rna_Main_cachefiles_begin(CollectionPropertyIterator *iter, PointerR
rna_iterator_listbase_begin(iter, &bmain->cachefiles, NULL);
}
+static void rna_Main_paintcurves_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
+{
+ Main *bmain = (Main *)ptr->data;
+ rna_iterator_listbase_begin(iter, &bmain->paintcurves, NULL);
+}
+
static void rna_Main_version_get(PointerRNA *ptr, int *value)
{
Main *bmain = (Main *)ptr->data;
@@ -354,6 +360,7 @@ void RNA_def_main(BlenderRNA *brna)
{"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},
+ {"paint_curves", "PaintCurve", "rna_Main_paintcurves_begin", "Paint Curves", "Paint Curves datablocks", RNA_def_main_paintcurves},
{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 e7b33253d17..e5cea63db1f 100644
--- a/source/blender/makesrna/intern/rna_main_api.c
+++ b/source/blender/makesrna/intern/rna_main_api.c
@@ -526,6 +526,7 @@ 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)
+RNA_MAIN_ID_TAG_FUNCS_DEF(paintcurves, paintcurves, ID_PC)
#undef RNA_MAIN_ID_TAG_FUNCS_DEF
@@ -1518,6 +1519,21 @@ void RNA_def_main_cachefiles(BlenderRNA *brna, PropertyRNA *cprop)
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_paintcurves(BlenderRNA *brna, PropertyRNA *cprop)
+{
+ RNA_def_property_srna(cprop, "BlendDataPaintCurves");
+ StructRNA *srna = RNA_def_struct(brna, "BlendDataPaintCurves", NULL);
+ RNA_def_struct_sdna(srna, "Main");
+ RNA_def_struct_ui_text(srna, "Main Paint Curves", "Collection of paint curves");
+
+ FunctionRNA *func = RNA_def_function(srna, "tag", "rna_Main_paintcurves_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_paintcurves_is_updated_get", NULL);
+}
void RNA_def_main_gpencil(BlenderRNA *brna, PropertyRNA *cprop)
{
StructRNA *srna;
diff --git a/source/blender/makesrna/intern/rna_scene_api.c b/source/blender/makesrna/intern/rna_scene_api.c
index 519f28bea24..85c0b01334f 100644
--- a/source/blender/makesrna/intern/rna_scene_api.c
+++ b/source/blender/makesrna/intern/rna_scene_api.c
@@ -210,7 +210,10 @@ static void rna_Scene_alembic_export(
int use_subdiv_schema,
int compression_type,
int packuv,
- float scale)
+ float scale,
+ int triangulate,
+ int quad_method,
+ int ngon_method)
{
/* We have to enable allow_threads, because we may change scene frame number
* during export. */
@@ -240,6 +243,9 @@ static void rna_Scene_alembic_export(
.use_subdiv_schema = use_subdiv_schema,
.compression_type = compression_type,
.packuv = packuv,
+ .triangulate = triangulate,
+ .quad_method = quad_method,
+ .ngon_method = ngon_method,
.global_scale = scale,
};
@@ -404,6 +410,9 @@ void RNA_api_scene(StructRNA *srna)
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_boolean(func, "triangulate", 0, "Triangulate", "Export Polygons (Quads & NGons) as Triangles");
+ RNA_def_enum(func, "quad_method", rna_enum_modifier_triangulate_quad_method_items, 0, "Quad Method", "Method for splitting the quads into triangles");
+ RNA_def_enum(func, "ngon_method", rna_enum_modifier_triangulate_quad_method_items, 0, "Polygon Method", "Method for splitting the polygons into triangles");
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 34e364981ba..305b4aa1b6e 100644
--- a/source/blender/makesrna/intern/rna_sculpt_paint.c
+++ b/source/blender/makesrna/intern/rna_sculpt_paint.c
@@ -718,6 +718,16 @@ static void rna_def_gpencil_sculpt(BlenderRNA *brna)
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, "interpolate_all_layers", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSHEDIT_FLAG_INTERPOLATE_ALL_LAYERS);
+ RNA_def_property_ui_text(prop, "Interpolate All Layers", "Interpolate all layers, not only active");
+ RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
+
+ prop = RNA_def_property(srna, "interpolate_selected_only", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSHEDIT_FLAG_INTERPOLATE_ONLY_SELECTED);
+ RNA_def_property_ui_text(prop, "Interpolate Selected Strokes", "Interpolate only selected strokes in the original frame");
+ 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);
diff --git a/source/blender/makesrna/intern/rna_ui_api.c b/source/blender/makesrna/intern/rna_ui_api.c
index a751c414d83..9d55115a14c 100644
--- a/source/blender/makesrna/intern/rna_ui_api.c
+++ b/source/blender/makesrna/intern/rna_ui_api.c
@@ -618,7 +618,7 @@ void RNA_api_ui_layout(StructRNA *srna)
#endif
func = RNA_def_function(srna, "label", "rna_uiItemL");
- RNA_def_function_ui_description(func, "Item. Display text and/or icon in the layout");
+ RNA_def_function_ui_description(func, "Item. Displays text and/or icon in the layout");
api_ui_item_common(func);
parm = RNA_def_property(func, "icon_value", PROP_INT, PROP_UNSIGNED);
RNA_def_property_ui_text(parm, "Icon Value", "Override automatic icon of the item");
@@ -644,6 +644,7 @@ void RNA_api_ui_layout(StructRNA *srna)
/* templates */
func = RNA_def_function(srna, "template_header", "uiTemplateHeader");
RNA_def_function_flag(func, FUNC_USE_CONTEXT);
+ RNA_def_function_ui_description(func, "Inserts common Space header UI (editor type selector)");
func = RNA_def_function(srna, "template_ID", "uiTemplateID");
RNA_def_function_flag(func, FUNC_USE_CONTEXT);
@@ -853,9 +854,12 @@ void RNA_api_ui_layout(StructRNA *srna)
func = RNA_def_function(srna, "template_header_3D", "uiTemplateHeader3D");
RNA_def_function_flag(func, FUNC_USE_CONTEXT);
+ RNA_def_function_ui_description(func, "Inserts common 3DView header UI (selectors for context mode, shading, etc.)");
+
func = RNA_def_function(srna, "template_edit_mode_selection", "uiTemplateEditModeSelection");
RNA_def_function_flag(func, FUNC_USE_CONTEXT);
+ RNA_def_function_ui_description(func, "Inserts common 3DView Edit modes header UI (selector for selection mode)");
func = RNA_def_function(srna, "template_reports_banner", "uiTemplateReportsBanner");
RNA_def_function_flag(func, FUNC_USE_CONTEXT);
diff --git a/source/blender/makesrna/intern/rna_wm_api.c b/source/blender/makesrna/intern/rna_wm_api.c
index 7cf0cf67269..8d0b704a402 100644
--- a/source/blender/makesrna/intern/rna_wm_api.c
+++ b/source/blender/makesrna/intern/rna_wm_api.c
@@ -398,18 +398,21 @@ void RNA_api_wm(StructRNA *srna)
rna_generic_op_invoke(func, 0);
func = RNA_def_function(srna, "modal_handler_add", "rna_event_modal_handler_add");
+ RNA_def_function_ui_description(func, "Add a modal handler to the window manager, for the given modal operator "
+ "(called by invoke() with self, just before returning {'RUNNING_MODAL'})");
RNA_def_function_flag(func, FUNC_NO_SELF | FUNC_USE_CONTEXT);
parm = RNA_def_pointer(func, "operator", "Operator", "", "Operator to call");
RNA_def_property_flag(parm, PROP_REQUIRED);
- RNA_def_function_return(func, RNA_def_boolean(func, "handle", 1, "", ""));
+ RNA_def_function_return(func, RNA_def_boolean(func, "handle", 1, "", "Whether adding the handler was successful"));
func = RNA_def_function(srna, "event_timer_add", "rna_event_timer_add");
+ RNA_def_function_ui_description(func, "Add a timer to the given window, to generate periodic 'TIMER' events");
parm = RNA_def_property(func, "time_step", PROP_FLOAT, PROP_NONE);
RNA_def_property_flag(parm, PROP_REQUIRED);
RNA_def_property_range(parm, 0.0, FLT_MAX);
RNA_def_property_ui_text(parm, "Time Step", "Interval in seconds between timer events");
- RNA_def_pointer(func, "window", "Window", "", "Window to attach the timer to or None");
+ RNA_def_pointer(func, "window", "Window", "", "Window to attach the timer to, or None");
parm = RNA_def_pointer(func, "result", "Timer", "", "");
RNA_def_function_return(func, parm);
@@ -421,44 +424,49 @@ void RNA_api_wm(StructRNA *srna)
/* Progress bar interface */
func = RNA_def_function(srna, "progress_begin", "rna_progress_begin");
RNA_def_function_ui_description(func, "Start progress report");
-
parm = RNA_def_property(func, "min", PROP_FLOAT, PROP_NONE);
RNA_def_property_ui_text(parm, "min", "any value in range [0,9999]");
RNA_def_property_flag(parm, PROP_REQUIRED);
-
parm = RNA_def_property(func, "max", PROP_FLOAT, PROP_NONE);
RNA_def_property_flag(parm, PROP_REQUIRED);
RNA_def_property_ui_text(parm, "max", "any value in range [min+1,9998]");
func = RNA_def_function(srna, "progress_update", "rna_progress_update");
+ RNA_def_function_ui_description(func, "Update the progress feedback");
parm = RNA_def_property(func, "value", PROP_FLOAT, PROP_NONE);
RNA_def_property_flag(parm, PROP_REQUIRED);
- RNA_def_property_ui_text(parm, "value", "any value between min and max as set in progress_begin()");
+ RNA_def_property_ui_text(parm, "value", "Any value between min and max as set in progress_begin()");
func = RNA_def_function(srna, "progress_end", "rna_progress_end");
RNA_def_function_ui_description(func, "Terminate progress report");
/* invoke functions, for use with python */
func = RNA_def_function(srna, "invoke_props_popup", "rna_Operator_props_popup");
- RNA_def_function_ui_description(func, "Operator popup invoke");
+ RNA_def_function_ui_description(func, "Operator popup invoke "
+ "(show operator properties and execute it automatically on changes)");
rna_generic_op_invoke(func, WM_GEN_INVOKE_EVENT | WM_GEN_INVOKE_RETURN);
/* invoked dialog opens popup with OK button, does not auto-exec operator. */
func = RNA_def_function(srna, "invoke_props_dialog", "WM_operator_props_dialog_popup");
- RNA_def_function_ui_description(func, "Operator dialog (non-autoexec popup) invoke");
+ RNA_def_function_ui_description(func, "Operator dialog (non-autoexec popup) invoke "
+ "(show operator properties and only execute it on click on OK button)");
rna_generic_op_invoke(func, WM_GEN_INVOKE_SIZE | WM_GEN_INVOKE_RETURN);
/* invoke enum */
func = RNA_def_function(srna, "invoke_search_popup", "rna_Operator_enum_search_invoke");
+ RNA_def_function_ui_description(func, "Operator search popup invoke (search in values of "
+ "operator's type 'prop' EnumProperty, and execute it on confirmation)");
rna_generic_op_invoke(func, 0);
/* invoke functions, for use with python */
func = RNA_def_function(srna, "invoke_popup", "WM_operator_ui_popup");
- RNA_def_function_ui_description(func, "Operator popup invoke");
+ RNA_def_function_ui_description(func, "Operator popup invoke "
+ "(only shows operator's properties, without executing it)");
rna_generic_op_invoke(func, WM_GEN_INVOKE_SIZE | WM_GEN_INVOKE_RETURN);
func = RNA_def_function(srna, "invoke_confirm", "rna_Operator_confirm");
- RNA_def_function_ui_description(func, "Operator confirmation");
+ RNA_def_function_ui_description(func, "Operator confirmation popup "
+ "(only to let user confirm the execution, no operator properties shown)");
rna_generic_op_invoke(func, WM_GEN_INVOKE_EVENT | WM_GEN_INVOKE_RETURN);
diff --git a/source/blender/modifiers/intern/MOD_subsurf.c b/source/blender/modifiers/intern/MOD_subsurf.c
index 23ab61821f4..8711384e1ee 100644
--- a/source/blender/modifiers/intern/MOD_subsurf.c
+++ b/source/blender/modifiers/intern/MOD_subsurf.c
@@ -130,6 +130,9 @@ static DerivedMesh *applyModifier(ModifierData *md, Object *ob,
if (U.opensubdiv_compute_type == USER_OPENSUBDIV_COMPUTE_NONE) {
modifier_setError(md, "OpenSubdiv is disabled in User Preferences");
}
+ else if ((ob->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT | OB_MODE_TEXTURE_PAINT)) != 0) {
+ modifier_setError(md, "OpenSubdiv is not supported in paint modes");
+ }
else if ((DAG_get_eval_flags_for_object(md->scene, ob) & DAG_EVAL_NEED_CPU) == 0) {
subsurf_flags |= SUBSURF_USE_GPU_BACKEND;
do_cddm_convert = false;
diff --git a/source/blender/nodes/shader/nodes/node_shader_fresnel.c b/source/blender/nodes/shader/nodes/node_shader_fresnel.c
index b50f722c71b..ef2ce99c924 100644
--- a/source/blender/nodes/shader/nodes/node_shader_fresnel.c
+++ b/source/blender/nodes/shader/nodes/node_shader_fresnel.c
@@ -41,10 +41,12 @@ static bNodeSocketTemplate sh_node_fresnel_out[] = {
static int node_shader_gpu_fresnel(GPUMaterial *mat, bNode *UNUSED(node), bNodeExecData *UNUSED(execdata), GPUNodeStack *in, GPUNodeStack *out)
{
- if (!in[1].link)
+ if (!in[1].link) {
in[1].link = GPU_builtin(GPU_VIEW_NORMAL);
- else
+ }
+ else if (GPU_material_use_world_space_shading(mat)) {
GPU_link(mat, "direction_transform_m4v3", in[1].link, GPU_builtin(GPU_VIEW_MATRIX), &in[1].link);
+ }
return GPU_stack_link(mat, "node_fresnel", in, out, GPU_builtin(GPU_VIEW_POSITION));
}
diff --git a/source/blender/python/intern/bpy_app.c b/source/blender/python/intern/bpy_app.c
index 78cb537cccd..ed7cec2f2d5 100644
--- a/source/blender/python/intern/bpy_app.c
+++ b/source/blender/python/intern/bpy_app.c
@@ -234,7 +234,7 @@ static int bpy_app_debug_set(PyObject *UNUSED(self), PyObject *value, void *clos
return 0;
}
-
+#define BROKEN_BINARY_PATH_PYTHON_HACK
PyDoc_STRVAR(bpy_app_binary_path_python_doc,
"String, the path to the python executable (read-only)"
@@ -251,14 +251,18 @@ static PyObject *bpy_app_binary_path_python_get(PyObject *self, void *UNUSED(clo
fullpath, sizeof(fullpath),
PY_MAJOR_VERSION, PY_MINOR_VERSION);
ret = PyC_UnicodeFromByte(fullpath);
- PyDict_SetItem(BlenderAppType.tp_dict, PyDescr_NAME(self), ret);
+#ifdef BROKEN_BINARY_PATH_PYTHON_HACK
+ Py_INCREF(ret);
+ UNUSED_VARS(self);
+#else
+ PyDict_SetItem(BlenderAppType.tp_dict, /* XXX BAAAADDDDDD! self is not a PyDescr at all! it's bpy.app!!! */ PyDescr_NAME(self), ret);
+#endif
}
else {
Py_INCREF(ret);
}
return ret;
-
}
PyDoc_STRVAR(bpy_app_debug_value_doc,
diff --git a/source/blender/python/intern/bpy_rna_id_collection.c b/source/blender/python/intern/bpy_rna_id_collection.c
index 31189ba4dee..1037c83815c 100644
--- a/source/blender/python/intern/bpy_rna_id_collection.c
+++ b/source/blender/python/intern/bpy_rna_id_collection.c
@@ -40,6 +40,9 @@
#include "BKE_library_query.h"
#include "DNA_ID.h"
+/* Those folowing are only to support hack of not listing some internal 'backward' pointers in generated user_map... */
+#include "DNA_object_types.h"
+#include "DNA_key_types.h"
#include "bpy_util.h"
#include "bpy_rna_id_collection.h"
@@ -81,7 +84,7 @@ static bool id_check_type(const ID *id, const BLI_bitmap *types_bitmap)
}
static int foreach_libblock_id_user_map_callback(
- void *user_data, ID *UNUSED(self_id), ID **id_p, int UNUSED(cb_flag))
+ void *user_data, ID *self_id, ID **id_p, int UNUSED(cb_flag))
{
IDUserMapData *data = user_data;
@@ -93,6 +96,15 @@ static int foreach_libblock_id_user_map_callback(
}
}
+ if ((GS(self_id->name) == ID_OB) && (id_p == (ID **)&((Object *)self_id)->proxy_from)) {
+ /* We skip proxy_from here, since it some internal pointer which is not irrelevant info for py/API level. */
+ return IDWALK_RET_NOP;
+ }
+ else if ((GS(self_id->name) == ID_KE) && (id_p == (ID **)&((Key *)self_id)->from)) {
+ /* We skip from here, since it some internal pointer which is not irrelevant info for py/API level. */
+ return IDWALK_RET_NOP;
+ }
+
/* pyrna_struct_hash() uses ptr.data only,
* but pyrna_struct_richcmp() uses also ptr.type,
* so we need to create a valid PointerRNA here...
@@ -233,8 +245,26 @@ static PyObject *bpy_user_map(PyObject *UNUSED(self), PyObject *args, PyObject *
data_cb.py_id_key_lookup_only = pyrna_id_CreatePyObject(id);
}
+ if (!data_cb.is_subset) {
+ PyObject *key = data_cb.py_id_key_lookup_only;
+ PyObject *set;
+
+ RNA_id_pointer_create(id, &((BPy_StructRNA *)key)->ptr);
+
+ /* We have to insert the key now, otherwise ID unused would be missing from final dict... */
+ if ((set = PyDict_GetItem(data_cb.user_map, key)) == NULL) {
+ /* Cannot use our placeholder key here! */
+ key = pyrna_id_CreatePyObject(id);
+ set = PySet_New(NULL);
+ PyDict_SetItem(data_cb.user_map, key, set);
+ Py_DECREF(set);
+ Py_DECREF(key);
+ }
+ }
+
data_cb.id_curr = id;
BKE_library_foreach_ID_link(id, foreach_libblock_id_user_map_callback, &data_cb, IDWALK_NOP);
+
if (data_cb.py_id_curr) {
Py_DECREF(data_cb.py_id_curr);
data_cb.py_id_curr = NULL;
diff --git a/source/blender/render/extern/include/RE_pipeline.h b/source/blender/render/extern/include/RE_pipeline.h
index 509ad6f3515..7021477a702 100644
--- a/source/blender/render/extern/include/RE_pipeline.h
+++ b/source/blender/render/extern/include/RE_pipeline.h
@@ -48,7 +48,6 @@ struct ReportList;
struct Scene;
struct SceneRenderLayer;
struct EnvMap;
-struct RenderResult;
struct StampData;
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
@@ -392,6 +391,8 @@ bool RE_RenderResult_is_stereo(RenderResult *res);
struct RenderView *RE_RenderViewGetById(struct RenderResult *res, const int view_id);
struct RenderView *RE_RenderViewGetByName(struct RenderResult *res, const char *viewname);
+RenderResult *RE_DuplicateRenderResult(RenderResult *rr);
+
/******* Debug pass helper functions *********/
#ifdef WITH_CYCLES_DEBUG
diff --git a/source/blender/render/intern/source/render_result.c b/source/blender/render/intern/source/render_result.c
index 6ea46af6f7e..e3fc8174bd6 100644
--- a/source/blender/render/intern/source/render_result.c
+++ b/source/blender/render/intern/source/render_result.c
@@ -1665,3 +1665,84 @@ RenderView *RE_RenderViewGetByName(RenderResult *res, const char *viewname)
BLI_assert(res->views.first);
return rv ? rv : res->views.first;
}
+
+static RenderPass *duplicate_render_pass(RenderPass *rpass)
+{
+ RenderPass *new_rpass = MEM_mallocN(sizeof(RenderPass), "new render pass");
+ *new_rpass = *rpass;
+ new_rpass->next = new_rpass->prev = NULL;
+ if (new_rpass->rect != NULL) {
+ new_rpass->rect = MEM_dupallocN(new_rpass->rect);
+ }
+ return new_rpass;
+}
+
+static RenderLayer *duplicate_render_layer(RenderLayer *rl)
+{
+ RenderLayer *new_rl = MEM_mallocN(sizeof(RenderLayer), "new render layer");
+ *new_rl = *rl;
+ new_rl->next = new_rl->prev = NULL;
+ new_rl->passes.first = new_rl->passes.last = NULL;
+ new_rl->exrhandle = NULL;
+ if (new_rl->acolrect != NULL) {
+ new_rl->acolrect = MEM_dupallocN(new_rl->acolrect);
+ }
+ if (new_rl->scolrect != NULL) {
+ new_rl->scolrect = MEM_dupallocN(new_rl->scolrect);
+ }
+ if (new_rl->display_buffer != NULL) {
+ new_rl->display_buffer = MEM_dupallocN(new_rl->display_buffer);
+ }
+ for (RenderPass *rpass = rl->passes.first; rpass != NULL; rpass = rpass->next) {
+ RenderPass *new_rpass = duplicate_render_pass(rpass);
+ BLI_addtail(&new_rl->passes, new_rpass);
+ }
+ return new_rl;
+}
+
+static RenderView *duplicate_render_view(RenderView *rview)
+{
+ RenderView *new_rview = MEM_mallocN(sizeof(RenderView), "new render view");
+ *new_rview = *rview;
+ if (new_rview->rectf != NULL) {
+ new_rview->rectf = MEM_dupallocN(new_rview->rectf);
+ }
+ if (new_rview->rectf != NULL) {
+ new_rview->rectf = MEM_dupallocN(new_rview->rectf);
+ }
+ if (new_rview->rectz != NULL) {
+ new_rview->rectz = MEM_dupallocN(new_rview->rectz);
+ }
+ if (new_rview->rect32 != NULL) {
+ new_rview->rect32 = MEM_dupallocN(new_rview->rect32);
+ }
+ return new_rview;
+}
+
+RenderResult *RE_DuplicateRenderResult(RenderResult *rr)
+{
+ RenderResult *new_rr = MEM_mallocN(sizeof(RenderResult), "new render result");
+ *new_rr = *rr;
+ new_rr->next = new_rr->prev = NULL;
+ new_rr->layers.first = new_rr->layers.last = NULL;
+ new_rr->views.first = new_rr->views.last = NULL;
+ for (RenderLayer *rl = rr->layers.first; rl != NULL; rl = rl->next) {
+ RenderLayer *new_rl = duplicate_render_layer(rl);
+ BLI_addtail(&new_rr->layers, new_rl);
+ }
+ for (RenderView *rview = rr->views.first; rview != NULL; rview = rview->next) {
+ RenderView *new_rview = duplicate_render_view(rview);
+ BLI_addtail(&new_rr->views, new_rview);
+ }
+ if (new_rr->rect32 != NULL) {
+ new_rr->rect32 = MEM_dupallocN(new_rr->rect32);
+ }
+ if (new_rr->rectf != NULL) {
+ new_rr->rectf = MEM_dupallocN(new_rr->rectf);
+ }
+ if (new_rr->rectz != NULL) {
+ new_rr->rectz = MEM_dupallocN(new_rr->rectz);
+ }
+ new_rr->stamp_data = MEM_dupallocN(new_rr->stamp_data);
+ return new_rr;
+}
diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c
index cf93e84d0a1..7625f55be6e 100644
--- a/source/blender/windowmanager/intern/wm_event_system.c
+++ b/source/blender/windowmanager/intern/wm_event_system.c
@@ -2030,11 +2030,13 @@ static int wm_handlers_do_intern(bContext *C, wmEvent *event, ListBase *handlers
break;
}
else {
- if (action & WM_HANDLER_HANDLED)
+ if (action & WM_HANDLER_HANDLED) {
if (G.debug & (G_DEBUG_EVENTS | G_DEBUG_HANDLERS))
printf("%s: handled - and pass on! '%s'\n", __func__, kmi->idname);
-
- PRINT("%s: un-handled '%s'...", __func__, kmi->idname);
+ }
+ else {
+ PRINT("%s: un-handled '%s'\n", __func__, kmi->idname);
+ }
}
}
}
diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c
index 73622cda483..c11c398c616 100644
--- a/source/blender/windowmanager/intern/wm_init_exit.c
+++ b/source/blender/windowmanager/intern/wm_init_exit.c
@@ -527,7 +527,6 @@ void WM_exit_ext(bContext *C, const bool do_python)
ANIM_fmodifiers_copybuf_free();
ED_gpencil_anim_copybuf_free();
ED_gpencil_strokes_copybuf_free();
- ED_clipboard_posebuf_free();
BKE_node_clipboard_clear();
BLF_exit();
diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c
index 0c137221856..7177244611e 100644
--- a/source/blender/windowmanager/intern/wm_operators.c
+++ b/source/blender/windowmanager/intern/wm_operators.c
@@ -2299,6 +2299,11 @@ int WM_border_select_modal(bContext *C, wmOperator *op, const wmEvent *event)
}
}
+#ifdef WITH_INPUT_NDOF
+ else if (event->type == NDOF_MOTION) {
+ return OPERATOR_PASS_THROUGH;
+ }
+#endif
// /* Allow view navigation??? */
// else {
// return OPERATOR_PASS_THROUGH;
@@ -2413,6 +2418,11 @@ int WM_gesture_circle_modal(bContext *C, wmOperator *op, const wmEvent *event)
return OPERATOR_FINISHED; /* use finish or we don't get an undo */
}
}
+#ifdef WITH_INPUT_NDOF
+ else if (event->type == NDOF_MOTION) {
+ return OPERATOR_PASS_THROUGH;
+ }
+#endif
/* Allow view navigation??? */
/* note, this gives issues: 1) other modal ops run on top (border select), 2) middlemouse is used now 3) tablet/trackpad? */
// else {
diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt
index fc02dfda9d1..f65688e1304 100644
--- a/source/creator/CMakeLists.txt
+++ b/source/creator/CMakeLists.txt
@@ -338,7 +338,8 @@ if(WITH_PYTHON)
# install(CODE "message(\"copying blender scripts...\")")
# exclude addons_contrib if release
- if("${BLENDER_VERSION_CYCLE}" STREQUAL "release")
+ if("${BLENDER_VERSION_CYCLE}" STREQUAL "release" OR
+ "${BLENDER_VERSION_CYCLE}" STREQUAL "rc")
set(ADDON_EXCLUDE_CONDITIONAL "addons_contrib/*")
else()
set(ADDON_EXCLUDE_CONDITIONAL "_addons_contrib/*") # dummy, wont do anything