diff options
author | Sergey Sharybin <sergey.vfx@gmail.com> | 2016-09-16 18:09:28 +0300 |
---|---|---|
committer | Sergey Sharybin <sergey.vfx@gmail.com> | 2016-09-16 18:09:28 +0300 |
commit | d96b8e168f3c809cd79f6028dbf99eeeebb71302 (patch) | |
tree | 75ee78ee75ea602c2c698f4b4ec8494093b949c7 /source | |
parent | 76c99f361f58752eff8054f5cff52cad9ce57145 (diff) | |
parent | 4b39069908c88099860c04a3b7d3b4aae0a272f5 (diff) |
Merge branch 'master' into blender2.8
Diffstat (limited to 'source')
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 |