diff options
Diffstat (limited to 'source/blender')
165 files changed, 4220 insertions, 2449 deletions
diff --git a/source/blender/alembic/intern/abc_curves.cc b/source/blender/alembic/intern/abc_curves.cc index 0542255d84b..28e75db2862 100644 --- a/source/blender/alembic/intern/abc_curves.cc +++ b/source/blender/alembic/intern/abc_curves.cc @@ -205,6 +205,7 @@ void AbcCurveReader::readObjectData(Main *bmain, float time) cu->flag |= CU_DEFORM_FILL | CU_3D; cu->actvert = CU_ACT_NONE; + cu->resolu = 1; m_object = BKE_object_add_only_object(bmain, OB_CURVE, m_object_name.c_str()); m_object->data = cu; @@ -250,13 +251,18 @@ void read_curve_sample(Curve *cu, const ICurvesSchema &schema, const float time) nu->pntsv = 1; nu->flag |= CU_SMOOTH; - nu->orderu = num_verts; - - if (smp.getType() == Alembic::AbcGeom::kCubic) { - nu->orderu = 3; - } - else if (orders && orders->size() > i) { - nu->orderu = static_cast<short>((*orders)[i] - 1); + switch (smp.getType()) { + case Alembic::AbcGeom::kCubic: + nu->orderu = 4; + break; + case Alembic::AbcGeom::kVariableOrder: + if (orders && orders->size() > i) { + nu->orderu = static_cast<short>((*orders)[i]); + } + break; + case Alembic::AbcGeom::kLinear: + default: + nu->orderu = 2; } if (periodicity == Alembic::AbcGeom::kNonPeriodic) { diff --git a/source/blender/alembic/intern/abc_exporter.cc b/source/blender/alembic/intern/abc_exporter.cc index 61e7712150f..ef3196cb15d 100644 --- a/source/blender/alembic/intern/abc_exporter.cc +++ b/source/blender/alembic/intern/abc_exporter.cc @@ -66,6 +66,7 @@ using Alembic::Abc::OBox3dProperty; ExportSettings::ExportSettings() : scene(NULL) + , logger() , selected_only(false) , visible_layers_only(false) , renderable_only(false) @@ -123,14 +124,32 @@ static bool object_is_shape(Object *ob) } } -static bool export_object(const ExportSettings * const settings, Object *ob) + +/** + * Returns whether this object should be exported into the Alembic file. + * + * @param settings export settings, used for options like 'selected only'. + * @param ob the object in question. + * @param is_duplicated normally false; true when the object is instanced + * into the scene by a dupli-object (e.g. part of a + * dupligroup). This ignores selection and layer + * visibility, and assumes that the dupli-object itself + * (e.g. the group-instantiating empty) is exported. + */ +static bool export_object(const ExportSettings * const settings, Object *ob, + bool is_duplicated) { - if (settings->selected_only && !parent_selected(ob)) { - return false; - } + if (!is_duplicated) { + /* These two tests only make sense when the object isn't being instanced + * into the scene. When it is, its exportability is determined by + * its dupli-object and the DupliObject::no_draw property. */ + if (settings->selected_only && !parent_selected(ob)) { + return false; + } - if (settings->visible_layers_only && !(settings->scene->lay & ob->lay)) { - return false; + if (settings->visible_layers_only && !(settings->scene->lay & ob->lay)) { + return false; + } } if (settings->renderable_only && (ob->restrictflag & OB_RESTRICT_RENDER)) { @@ -153,11 +172,13 @@ AbcExporter::AbcExporter(Scene *scene, const char *filename, ExportSettings &set AbcExporter::~AbcExporter() { - std::map<std::string, AbcTransformWriter*>::iterator it, e; - for (it = m_xforms.begin(), e = m_xforms.end(); it != e; ++it) { - delete it->second; + /* Free xforms map */ + m_xforms_type::iterator it_x, e_x; + for (it_x = m_xforms.begin(), e_x = m_xforms.end(); it_x != e_x; ++it_x) { + delete it_x->second; } + /* Free shapes vector */ for (int i = 0, e = m_shapes.size(); i != e; ++i) { delete m_shapes[i]; } @@ -269,13 +290,7 @@ void AbcExporter::operator()(Main *bmain, float &progress, bool &was_canceled) OBox3dProperty archive_bounds_prop = Alembic::AbcGeom::CreateOArchiveBounds(m_writer->archive(), m_trans_sampling_index); - if (m_settings.flatten_hierarchy) { - createTransformWritersFlat(); - } - else { - createTransformWritersHierarchy(bmain->eval_ctx); - } - + createTransformWritersHierarchy(bmain->eval_ctx); createShapeWriters(bmain->eval_ctx); /* Make a list of frames to export. */ @@ -322,7 +337,7 @@ void AbcExporter::operator()(Main *bmain, float &progress, bool &was_canceled) continue; } - std::map<std::string, AbcTransformWriter *>::iterator xit, xe; + m_xforms_type::iterator xit, xe; for (xit = m_xforms.begin(), xe = m_xforms.end(); xit != xe; ++xit) { xit->second->write(); } @@ -346,34 +361,16 @@ void AbcExporter::createTransformWritersHierarchy(EvaluationContext *eval_ctx) while (base) { Object *ob = base->object; - if (export_object(&m_settings, ob)) { - switch (ob->type) { - case OB_LAMP: - case OB_LATTICE: - case OB_MBALL: - case OB_SPEAKER: - /* We do not export transforms for objects of these classes. */ - break; - - default: - exploreTransform(eval_ctx, ob, ob->parent, NULL); - } - } - - base = base->next; - } -} + switch (ob->type) { + case OB_LAMP: + case OB_LATTICE: + case OB_MBALL: + case OB_SPEAKER: + /* We do not export transforms for objects of these classes. */ + break; -void AbcExporter::createTransformWritersFlat() -{ - Base *base = static_cast<Base *>(m_scene->base.first); - - while (base) { - Object *ob = base->object; - - if (export_object(&m_settings, ob) && object_is_shape(ob)) { - std::string name = get_id_name(ob); - m_xforms[name] = new AbcTransformWriter(ob, m_writer->archive().getTop(), 0, m_trans_sampling_index, m_settings); + default: + exploreTransform(eval_ctx, ob, ob->parent); } base = base->next; @@ -382,8 +379,13 @@ void AbcExporter::createTransformWritersFlat() void AbcExporter::exploreTransform(EvaluationContext *eval_ctx, Object *ob, Object *parent, Object *dupliObParent) { + /* If an object isn't exported itself, its duplilist shouldn't be + * exported either. */ + if (!export_object(&m_settings, ob, dupliObParent != NULL)) { + return; + } - if (export_object(&m_settings, ob) && object_is_shape(ob)) { + if (object_is_shape(ob)) { createTransformWriter(ob, parent, dupliObParent); } @@ -394,68 +396,91 @@ void AbcExporter::exploreTransform(EvaluationContext *eval_ctx, Object *ob, Obje Object *dupli_ob = NULL; Object *dupli_parent = NULL; - while (link) { + for (; link; link = link->next) { + /* This skips things like custom bone shapes. */ + if (m_settings.renderable_only && link->no_draw) { + continue; + } + if (link->type == OB_DUPLIGROUP) { dupli_ob = link->ob; dupli_parent = (dupli_ob->parent) ? dupli_ob->parent : ob; exploreTransform(eval_ctx, dupli_ob, dupli_parent, ob); } - - link = link->next; } } free_object_duplilist(lb); } -void AbcExporter::createTransformWriter(Object *ob, Object *parent, Object *dupliObParent) +AbcTransformWriter * AbcExporter::createTransformWriter(Object *ob, Object *parent, Object *dupliObParent) { - const std::string name = get_object_dag_path_name(ob, dupliObParent); - /* An object should not be its own parent, or we'll get infinite loops. */ BLI_assert(ob != parent); BLI_assert(ob != dupliObParent); - /* check if we have already created a transform writer for this object */ - if (getXForm(name) != NULL){ - std::cerr << "xform " << name << " already exists\n"; - return; + std::string name; + if (m_settings.flatten_hierarchy) { + name = get_id_name(ob); + } + else { + name = get_object_dag_path_name(ob, dupliObParent); } - AbcTransformWriter *parent_xform = NULL; + /* check if we have already created a transform writer for this object */ + AbcTransformWriter *my_writer = getXForm(name); + if (my_writer != NULL){ + return my_writer; + } - if (parent) { - const std::string parentname = get_object_dag_path_name(parent, dupliObParent); - parent_xform = getXForm(parentname); + AbcTransformWriter *parent_writer = NULL; + Alembic::Abc::OObject alembic_parent; - if (!parent_xform) { - if (parent->parent) { - createTransformWriter(parent, parent->parent, dupliObParent); + if (m_settings.flatten_hierarchy || parent == NULL) { + /* Parentless objects still have the "top object" as parent + * in Alembic. */ + alembic_parent = m_writer->archive().getTop(); + } + else { + /* Since there are so many different ways to find parents (as evident + * in the number of conditions below), we can't really look up the + * parent by name. We'll just call createTransformWriter(), which will + * return the parent's AbcTransformWriter pointer. */ + if (parent->parent) { + if (parent == dupliObParent) { + parent_writer = createTransformWriter(parent, parent->parent, NULL); + } + else { + parent_writer = createTransformWriter(parent, parent->parent, dupliObParent); } - else if (parent == dupliObParent) { - if (dupliObParent->parent == NULL) { - createTransformWriter(parent, NULL, NULL); - } - else { - createTransformWriter(parent, dupliObParent->parent, dupliObParent->parent); - } + } + else if (parent == dupliObParent) { + if (dupliObParent->parent == NULL) { + parent_writer = createTransformWriter(parent, NULL, NULL); } else { - createTransformWriter(parent, dupliObParent, dupliObParent); + parent_writer = createTransformWriter(parent, dupliObParent->parent, dupliObParent->parent); } - - parent_xform = getXForm(parentname); } - } + else { + parent_writer = createTransformWriter(parent, dupliObParent, dupliObParent); + } - if (parent_xform) { - m_xforms[name] = new AbcTransformWriter(ob, parent_xform->alembicXform(), parent_xform, m_trans_sampling_index, m_settings); - m_xforms[name]->setParent(parent); + BLI_assert(parent_writer); + alembic_parent = parent_writer->alembicXform(); } - else { - m_xforms[name] = new AbcTransformWriter(ob, m_writer->archive().getTop(), NULL, m_trans_sampling_index, m_settings); + + my_writer = new AbcTransformWriter(ob, alembic_parent, parent_writer, + m_trans_sampling_index, m_settings); + + /* When flattening, the matrix of the dupliobject has to be added. */ + if (m_settings.flatten_hierarchy && dupliObParent) { + my_writer->m_proxy_from = dupliObParent; } + + m_xforms[name] = my_writer; + return my_writer; } void AbcExporter::createShapeWriters(EvaluationContext *eval_ctx) @@ -472,19 +497,28 @@ void AbcExporter::createShapeWriters(EvaluationContext *eval_ctx) void AbcExporter::exploreObject(EvaluationContext *eval_ctx, Object *ob, Object *dupliObParent) { - ListBase *lb = object_duplilist(eval_ctx, m_scene, ob); - + /* If an object isn't exported itself, its duplilist shouldn't be + * exported either. */ + if (!export_object(&m_settings, ob, dupliObParent != NULL)) { + return; + } + createShapeWriter(ob, dupliObParent); + ListBase *lb = object_duplilist(eval_ctx, m_scene, ob); + if (lb) { - DupliObject *dupliob = static_cast<DupliObject *>(lb->first); + DupliObject *link = static_cast<DupliObject *>(lb->first); - while (dupliob) { - if (dupliob->type == OB_DUPLIGROUP) { - exploreObject(eval_ctx, dupliob->ob, ob); + for (; link; link = link->next) { + /* This skips things like custom bone shapes. */ + if (m_settings.renderable_only && link->no_draw) { + continue; } - dupliob = dupliob->next; + if (link->type == OB_DUPLIGROUP) { + exploreObject(eval_ctx, link->ob, ob); + } } } @@ -497,10 +531,6 @@ void AbcExporter::createShapeWriter(Object *ob, Object *dupliObParent) return; } - if (!export_object(&m_settings, ob)) { - return; - } - std::string name; if (m_settings.flatten_hierarchy) { @@ -513,7 +543,7 @@ void AbcExporter::createShapeWriter(Object *ob, Object *dupliObParent) AbcTransformWriter *xform = getXForm(name); if (!xform) { - std::cerr << __func__ << ": xform " << name << " is NULL\n"; + ABC_LOG(m_settings.logger) << __func__ << ": xform " << name << " is NULL\n"; return; } diff --git a/source/blender/alembic/intern/abc_exporter.h b/source/blender/alembic/intern/abc_exporter.h index b0eb8e185d6..73b7af280d9 100644 --- a/source/blender/alembic/intern/abc_exporter.h +++ b/source/blender/alembic/intern/abc_exporter.h @@ -28,6 +28,8 @@ #include <set> #include <vector> +#include "abc_util.h" + class AbcObjectWriter; class AbcTransformWriter; class ArchiveWriter; @@ -41,6 +43,7 @@ struct ExportSettings { ExportSettings(); Scene *scene; + SimpleLogger logger; bool selected_only; bool visible_layers_only; @@ -86,7 +89,10 @@ class AbcExporter { ArchiveWriter *m_writer; - std::map<std::string, AbcTransformWriter *> m_xforms; + /* mapping from name to transform writer */ + typedef std::map<std::string, AbcTransformWriter *> m_xforms_type; + m_xforms_type m_xforms; + std::vector<AbcObjectWriter *> m_shapes; public: @@ -103,8 +109,7 @@ private: void getFrameSet(double step, std::set<double> &frames); void createTransformWritersHierarchy(EvaluationContext *eval_ctx); - void createTransformWritersFlat(); - void createTransformWriter(Object *ob, Object *parent, Object *dupliObParent); + AbcTransformWriter * createTransformWriter(Object *ob, Object *parent, Object *dupliObParent); void exploreTransform(EvaluationContext *eval_ctx, Object *ob, Object *parent, Object *dupliObParent = NULL); void exploreObject(EvaluationContext *eval_ctx, Object *ob, Object *dupliObParent); void createShapeWriters(EvaluationContext *eval_ctx); diff --git a/source/blender/alembic/intern/abc_mesh.cc b/source/blender/alembic/intern/abc_mesh.cc index 5a57e43326a..79b891dbcd4 100644 --- a/source/blender/alembic/intern/abc_mesh.cc +++ b/source/blender/alembic/intern/abc_mesh.cc @@ -897,19 +897,31 @@ static void *add_customdata_cb(void *user_data, const char *name, int data_type) { DerivedMesh *dm = static_cast<DerivedMesh *>(user_data); CustomDataType cd_data_type = static_cast<CustomDataType>(data_type); - void *cd_ptr = NULL; - - if (ELEM(cd_data_type, CD_MLOOPUV, CD_MLOOPCOL)) { - cd_ptr = CustomData_get_layer_named(dm->getLoopDataLayout(dm), cd_data_type, name); - - if (cd_ptr == NULL) { - cd_ptr = CustomData_add_layer_named(dm->getLoopDataLayout(dm), - cd_data_type, - CD_DEFAULT, - NULL, - dm->getNumLoops(dm), - name); - } + void *cd_ptr; + CustomData *loopdata; + int numloops; + + /* unsupported custom data type -- don't do anything. */ + if (!ELEM(cd_data_type, CD_MLOOPUV, CD_MLOOPCOL)) { + return NULL; + } + + loopdata = dm->getLoopDataLayout(dm); + cd_ptr = CustomData_get_layer_named(loopdata, cd_data_type, name); + if (cd_ptr != NULL) { + /* layer already exists, so just return it. */ + return cd_ptr; + } + + /* create a new layer, taking care to construct the hopefully-soon-to-be-removed + * CD_MTEXPOLY layer too, with the same name. */ + numloops = dm->getNumLoops(dm); + cd_ptr = CustomData_add_layer_named(loopdata, cd_data_type, CD_DEFAULT, + NULL, numloops, name); + if (cd_data_type == CD_MLOOPUV) { + CustomData_add_layer_named(dm->getPolyDataLayout(dm), + CD_MTEXPOLY, CD_DEFAULT, + NULL, numloops, name); } return cd_ptr; diff --git a/source/blender/alembic/intern/abc_object.cc b/source/blender/alembic/intern/abc_object.cc index a5b8af542fc..c8716d55218 100644 --- a/source/blender/alembic/intern/abc_object.cc +++ b/source/blender/alembic/intern/abc_object.cc @@ -91,7 +91,7 @@ Imath::Box3d AbcObjectWriter::bounds() if (!bb) { if (this->m_object->type != OB_CAMERA) { - std::cerr << "Boundbox is null!\n"; + ABC_LOG(m_settings.logger) << "Bounding box is null!\n"; } return Imath::Box3d(); @@ -127,6 +127,7 @@ AbcObjectReader::AbcObjectReader(const IObject &object, ImportSettings &settings , m_min_time(std::numeric_limits<chrono_t>::max()) , m_max_time(std::numeric_limits<chrono_t>::min()) , m_refcount(0) + , parent_reader(NULL) { m_name = object.getFullName(); std::vector<std::string> parts; @@ -214,7 +215,7 @@ Imath::M44d get_matrix(const IXformSchema &schema, const float time) return s0.getMatrix(); } -void AbcObjectReader::readObjectMatrix(const float time) +void AbcObjectReader::setupObjectTransform(const float time) { bool is_constant = false; @@ -236,49 +237,88 @@ void AbcObjectReader::readObjectMatrix(const float time) } } -void AbcObjectReader::read_matrix(float mat[4][4], const float time, const float scale, bool &is_constant) +Alembic::AbcGeom::IXform AbcObjectReader::xform() { - IXform ixform; - bool has_alembic_parent = false; - /* Check that we have an empty object (locator, bone head/tail...). */ if (IXform::matches(m_iobject.getMetaData())) { - ixform = IXform(m_iobject, Alembic::AbcGeom::kWrapExisting); - - /* See comment below. */ - has_alembic_parent = m_iobject.getParent().getParent().valid(); + return IXform(m_iobject, Alembic::AbcGeom::kWrapExisting); } - /* Check that we have an object with actual data. */ - else if (IXform::matches(m_iobject.getParent().getMetaData())) { - ixform = IXform(m_iobject.getParent(), Alembic::AbcGeom::kWrapExisting); - - /* This is a bit hackish, but we need to make sure that extra - * transformations added to the matrix (rotation/scale) are only applied - * to root objects. The way objects and their hierarchy are created will - * need to be revisited at some point but for now this seems to do the - * trick. - * - * Explanation of the trick: - * The first getParent() will return this object's transformation matrix. - * The second getParent() will get the parent of the transform, but this - * might be the archive root ('/') which is valid, so we go passed it to - * make sure that there is no parent. - */ - has_alembic_parent = m_iobject.getParent().getParent().getParent().valid(); + + /* Check that we have an object with actual data, in which case the + * parent Alembic object should contain the transform. */ + IObject abc_parent = m_iobject.getParent(); + + /* The archive's top object can be recognised by not having a parent. */ + if (abc_parent.getParent() + && IXform::matches(abc_parent.getMetaData())) { + return IXform(abc_parent, Alembic::AbcGeom::kWrapExisting); } + /* Should not happen. */ - else { + std::cerr << "AbcObjectReader::xform(): " + << "unable to find IXform for Alembic object '" + << m_iobject.getFullName() << "'\n"; + BLI_assert(false); + + return IXform(); +} + +void AbcObjectReader::read_matrix(float r_mat[4][4], const float time, + const float scale, bool &is_constant) +{ + IXform ixform = xform(); + if (!ixform) { return; } - const IXformSchema &schema(ixform.getSchema()); - + const IXformSchema & schema(ixform.getSchema()); if (!schema.valid()) { + std::cerr << "Alembic object " << ixform.getFullName() + << " has an invalid schema." << std::endl; return; } + bool has_alembic_parent; + IObject ixform_parent = ixform.getParent(); + if (!ixform_parent.getParent()) { + /* The archive top object certainly is not a transform itself, so handle + * it as "no parent". */ + has_alembic_parent = false; + } + else { + has_alembic_parent = ixform_parent && schema.getInheritsXforms(); + + if (has_alembic_parent && m_object->parent == NULL) { + /* TODO Sybren: This happened in some files. I think I solved it, + * but I'll leave this check in here anyway until we've tested it + * more thoroughly. Better than crashing on a null parent anyway. */ + std::cerr << "Alembic object " << m_iobject.getFullName() + << " with transform " << ixform.getFullName() + << " has an Alembic parent but no parent Blender object." + << std::endl; + has_alembic_parent = false; + } + } + const Imath::M44d matrix = get_matrix(schema, time); - convert_matrix(matrix, m_object, mat, scale, has_alembic_parent); + convert_matrix(matrix, m_object, r_mat); + + if (has_alembic_parent) { + /* In this case, the matrix in Alembic is in local coordinates, so + * convert to world matrix. To prevent us from reading and accumulating + * all parent matrices in the Alembic file, we assume that the Blender + * parent object is already updated for the current timekey, and use its + * world matrix. */ + BLI_assert(m_object->parent); + mul_m4_m4m4(r_mat, m_object->parent->obmat, r_mat); + } + else { + /* Only apply scaling to root objects, parenting will propagate it. */ + float scale_mat[4][4]; + scale_m4_fl(scale_mat, scale); + scale_mat[3][3] = scale; /* scale translations too */ + mul_m4_m4m4(r_mat, r_mat, scale_mat); + } is_constant = schema.isConstant(); } diff --git a/source/blender/alembic/intern/abc_object.h b/source/blender/alembic/intern/abc_object.h index 0f733e67d3f..d5344533b55 100644 --- a/source/blender/alembic/intern/abc_object.h +++ b/source/blender/alembic/intern/abc_object.h @@ -117,15 +117,7 @@ struct ImportSettings { template <typename Schema> static bool has_animations(Schema &schema, ImportSettings *settings) { - if (settings->is_sequence) { - return true; - } - - if (!schema.isConstant()) { - return true; - } - - return false; + return settings->is_sequence || !schema.isConstant(); } /* ************************************************************************** */ @@ -152,15 +144,30 @@ protected: int m_refcount; public: + AbcObjectReader *parent_reader; + +public: explicit AbcObjectReader(const Alembic::Abc::IObject &object, ImportSettings &settings); virtual ~AbcObjectReader(); const Alembic::Abc::IObject &iobject() const; + typedef std::vector<AbcObjectReader *> ptr_vector; + + /** + * Returns the transform of this object. This can be the Alembic object + * itself (in case of an Empty) or it can be the parent Alembic object. + */ + virtual Alembic::AbcGeom::IXform xform(); + Object *object() const; void object(Object *ob); + const std::string & name() const { return m_name; } + const std::string & object_name() const { return m_object_name; } + const std::string & data_name() const { return m_data_name; } + virtual bool valid() const = 0; virtual void readObjectData(Main *bmain, float time) = 0; @@ -173,7 +180,8 @@ public: return dm; } - void readObjectMatrix(const float time); + /** Reads the object matrix and sets up an object transform if animated. */ + void setupObjectTransform(const float time); void addCacheModifier(); @@ -184,7 +192,8 @@ public: void incref(); void decref(); - void read_matrix(float mat[4][4], const float time, const float scale, bool &is_constant); + void read_matrix(float r_mat[4][4], const float time, + const float scale, bool &is_constant); }; Imath::M44d get_matrix(const Alembic::AbcGeom::IXformSchema &schema, const float time); diff --git a/source/blender/alembic/intern/abc_transform.cc b/source/blender/alembic/intern/abc_transform.cc index 2c6ef09326c..6e218cac429 100644 --- a/source/blender/alembic/intern/abc_transform.cc +++ b/source/blender/alembic/intern/abc_transform.cc @@ -62,9 +62,9 @@ AbcTransformWriter::AbcTransformWriter(Object *ob, unsigned int time_sampling, ExportSettings &settings) : AbcObjectWriter(NULL, ob, time_sampling, settings, parent) + , m_proxy_from(NULL) { m_is_animated = hasAnimation(m_object); - m_parent = NULL; if (!m_is_animated) { time_sampling = 0; @@ -72,6 +72,9 @@ AbcTransformWriter::AbcTransformWriter(Object *ob, m_xform = OXform(abc_parent, get_id_name(m_object), time_sampling); m_schema = m_xform.getSchema(); + + /* Blender objects can't have a parent without inheriting the transform. */ + m_inherits_xform = parent != NULL; } void AbcTransformWriter::do_write() @@ -86,27 +89,29 @@ void AbcTransformWriter::do_write() return; } - float mat[4][4]; - create_transform_matrix(m_object, mat); + float yup_mat[4][4]; + create_transform_matrix(m_object, yup_mat, + m_inherits_xform ? ABC_MATRIX_LOCAL : ABC_MATRIX_WORLD, + m_proxy_from); /* Only apply rotation to root camera, parenting will propagate it. */ - if (m_object->type == OB_CAMERA && !has_parent_camera(m_object)) { + if (m_object->type == OB_CAMERA && (!m_inherits_xform || !has_parent_camera(m_object))) { float rot_mat[4][4]; axis_angle_to_mat4_single(rot_mat, 'X', -M_PI_2); - mul_m4_m4m4(mat, mat, rot_mat); + mul_m4_m4m4(yup_mat, yup_mat, rot_mat); } - if (!m_object->parent) { + if (!m_object->parent || !m_inherits_xform) { /* Only apply scaling to root objects, parenting will propagate it. */ float scale_mat[4][4]; scale_m4_fl(scale_mat, m_settings.global_scale); - mul_m4_m4m4(mat, mat, scale_mat); - mul_v3_fl(mat[3], m_settings.global_scale); + scale_mat[3][3] = m_settings.global_scale; /* also scale translation */ + mul_m4_m4m4(yup_mat, yup_mat, scale_mat); } - m_matrix = convert_matrix(mat); - + m_matrix = convert_matrix(yup_mat); m_sample.setMatrix(m_matrix); + m_sample.setInheritsXforms(m_inherits_xform); m_schema.set(m_sample); } @@ -133,6 +138,10 @@ bool AbcTransformWriter::hasAnimation(Object * /*ob*/) const AbcEmptyReader::AbcEmptyReader(const Alembic::Abc::IObject &object, ImportSettings &settings) : AbcObjectReader(object, settings) { + /* Empties have no data. It makes the import of Alembic files easier to + * understand when we name the empty after its name in Alembic. */ + m_object_name = object.getName(); + Alembic::AbcGeom::IXform xform(object, Alembic::AbcGeom::kWrapExisting); m_schema = xform.getSchema(); @@ -146,6 +155,7 @@ bool AbcEmptyReader::valid() const void AbcEmptyReader::readObjectData(Main *bmain, float /*time*/) { - m_object = BKE_object_add_only_object(bmain, OB_EMPTY, m_data_name.c_str()); + m_object = BKE_object_add_only_object(bmain, OB_EMPTY, + m_object_name.c_str()); m_object->data = NULL; } diff --git a/source/blender/alembic/intern/abc_transform.h b/source/blender/alembic/intern/abc_transform.h index 6a3aae216f2..59388e155dc 100644 --- a/source/blender/alembic/intern/abc_transform.h +++ b/source/blender/alembic/intern/abc_transform.h @@ -37,8 +37,11 @@ class AbcTransformWriter : public AbcObjectWriter { Alembic::Abc::M44d m_matrix; bool m_is_animated; - Object *m_parent; bool m_visible; + bool m_inherits_xform; + +public: + Object *m_proxy_from; public: AbcTransformWriter(Object *ob, @@ -49,7 +52,6 @@ public: Alembic::AbcGeom::OXform &alembicXform() { return m_xform;} virtual Imath::Box3d bounds(); - void setParent(Object *p) { m_parent = p; } private: virtual void do_write(); diff --git a/source/blender/alembic/intern/abc_util.cc b/source/blender/alembic/intern/abc_util.cc index 50fa43a3491..67d2d3b1eb2 100644 --- a/source/blender/alembic/intern/abc_util.cc +++ b/source/blender/alembic/intern/abc_util.cc @@ -41,7 +41,7 @@ extern "C" { #include "PIL_time.h" } -std::string get_id_name(Object *ob) +std::string get_id_name(const Object * const ob) { if (!ob) { return ""; @@ -50,7 +50,7 @@ std::string get_id_name(Object *ob) return get_id_name(&ob->id); } -std::string get_id_name(ID *id) +std::string get_id_name(const ID * const id) { std::string name(id->name + 2); std::replace(name.begin(), name.end(), ' ', '_'); @@ -60,7 +60,7 @@ std::string get_id_name(ID *id) return name; } -std::string get_object_dag_path_name(Object *ob, Object *dupli_parent) +std::string get_object_dag_path_name(const Object * const ob, Object *dupli_parent) { std::string name = get_id_name(ob); @@ -132,15 +132,28 @@ void split(const std::string &s, const char delim, std::vector<std::string> &tok } } -/* Create a rotation matrix for each axis from euler angles. - * Euler angles are swaped to change coordinate system. */ -static void create_rotation_matrix( +void create_swapped_rotation_matrix( float rot_x_mat[3][3], float rot_y_mat[3][3], - float rot_z_mat[3][3], const float euler[3], const bool to_yup) + float rot_z_mat[3][3], const float euler[3], + AbcAxisSwapMode mode) { const float rx = euler[0]; - const float ry = (to_yup) ? euler[2] : -euler[2]; - const float rz = (to_yup) ? -euler[1] : euler[1]; + float ry; + float rz; + + /* Apply transformation */ + switch(mode) { + case ABC_ZUP_FROM_YUP: + ry = -euler[2]; + rz = euler[1]; + break; + case ABC_YUP_FROM_ZUP: + ry = euler[2]; + rz = -euler[1]; + break; + default: + BLI_assert(false); + } unit_m3(rot_x_mat); unit_m3(rot_y_mat); @@ -162,58 +175,70 @@ static void create_rotation_matrix( rot_z_mat[1][1] = cos(rz); } -/* Recompute transform matrix of object in new coordinate system - * (from Y-Up to Z-Up). */ -void create_transform_matrix(float r_mat[4][4]) +/* Convert matrix from Z=up to Y=up or vice versa. Use yup_mat = zup_mat for in-place conversion. */ +void copy_m44_axis_swap(float dst_mat[4][4], float src_mat[4][4], AbcAxisSwapMode mode) { - float rot_mat[3][3], rot[3][3], scale_mat[4][4], invmat[4][4], transform_mat[4][4]; + float dst_rot[3][3], src_rot[3][3], dst_scale_mat[4][4]; float rot_x_mat[3][3], rot_y_mat[3][3], rot_z_mat[3][3]; - float loc[3], scale[3], euler[3]; + float src_trans[3], dst_scale[3], src_scale[3], euler[3]; - zero_v3(loc); - zero_v3(scale); + zero_v3(src_trans); + zero_v3(dst_scale); + zero_v3(src_scale); zero_v3(euler); - unit_m3(rot); - unit_m3(rot_mat); - unit_m4(scale_mat); - unit_m4(transform_mat); - unit_m4(invmat); + unit_m3(src_rot); + unit_m3(dst_rot); + unit_m4(dst_scale_mat); - /* Compute rotation matrix. */ + /* We assume there is no sheer component and no homogeneous scaling component. */ + BLI_assert(fabs(src_mat[0][3]) < 2 * FLT_EPSILON); + BLI_assert(fabs(src_mat[1][3]) < 2 * FLT_EPSILON); + BLI_assert(fabs(src_mat[2][3]) < 2 * FLT_EPSILON); + BLI_assert(fabs(src_mat[3][3] - 1.0f) < 2 * FLT_EPSILON); - /* Extract location, rotation, and scale from matrix. */ - mat4_to_loc_rot_size(loc, rot, scale, r_mat); + /* Extract translation, rotation, and scale form matrix. */ + mat4_to_loc_rot_size(src_trans, src_rot, src_scale, src_mat); /* Get euler angles from rotation matrix. */ - mat3_to_eulO(euler, ROT_MODE_XYZ, rot); + mat3_to_eulO(euler, ROT_MODE_XZY, src_rot); /* Create X, Y, Z rotation matrices from euler angles. */ - create_rotation_matrix(rot_x_mat, rot_y_mat, rot_z_mat, euler, false); + create_swapped_rotation_matrix(rot_x_mat, rot_y_mat, rot_z_mat, euler, mode); /* Concatenate rotation matrices. */ - mul_m3_m3m3(rot_mat, rot_mat, rot_y_mat); - mul_m3_m3m3(rot_mat, rot_mat, rot_z_mat); - mul_m3_m3m3(rot_mat, rot_mat, rot_x_mat); + mul_m3_m3m3(dst_rot, dst_rot, rot_z_mat); + mul_m3_m3m3(dst_rot, dst_rot, rot_y_mat); + mul_m3_m3m3(dst_rot, dst_rot, rot_x_mat); - /* Add rotation matrix to transformation matrix. */ - copy_m4_m3(transform_mat, rot_mat); + mat3_to_eulO(euler, ROT_MODE_XZY, dst_rot); - /* Add translation to transformation matrix. */ - copy_zup_from_yup(transform_mat[3], loc); + /* Start construction of dst_mat from rotation matrix */ + unit_m4(dst_mat); + copy_m4_m3(dst_mat, dst_rot); - /* Create scale matrix. */ - scale_mat[0][0] = scale[0]; - scale_mat[1][1] = scale[2]; - scale_mat[2][2] = scale[1]; + /* Apply translation */ + switch(mode) { + case ABC_ZUP_FROM_YUP: + copy_zup_from_yup(dst_mat[3], src_trans); + break; + case ABC_YUP_FROM_ZUP: + copy_yup_from_zup(dst_mat[3], src_trans); + break; + default: + BLI_assert(false); + } - /* Add scale to transformation matrix. */ - mul_m4_m4m4(transform_mat, transform_mat, scale_mat); + /* Apply scale matrix. Swaps y and z, but does not + * negate like translation does. */ + dst_scale[0] = src_scale[0]; + dst_scale[1] = src_scale[2]; + dst_scale[2] = src_scale[1]; - copy_m4_m4(r_mat, transform_mat); + size_to_mat4(dst_scale_mat, dst_scale); + mul_m4_m4m4(dst_mat, dst_mat, dst_scale_mat); } -void convert_matrix(const Imath::M44d &xform, Object *ob, - float r_mat[4][4], float scale, bool has_alembic_parent) +void convert_matrix(const Imath::M44d &xform, Object *ob, float r_mat[4][4]) { for (int i = 0; i < 4; ++i) { for (int j = 0; j < 4; ++j) { @@ -227,207 +252,32 @@ void convert_matrix(const Imath::M44d &xform, Object *ob, mul_m4_m4m4(r_mat, r_mat, cam_to_yup); } - create_transform_matrix(r_mat); - - if (ob->parent) { - mul_m4_m4m4(r_mat, ob->parent->obmat, r_mat); - } - /* TODO(kevin) */ - else if (!has_alembic_parent) { - /* Only apply scaling to root objects, parenting will propagate it. */ - float scale_mat[4][4]; - scale_m4_fl(scale_mat, scale); - mul_m4_m4m4(r_mat, r_mat, scale_mat); - mul_v3_fl(r_mat[3], scale); - } + copy_m44_axis_swap(r_mat, r_mat, ABC_ZUP_FROM_YUP); } -/* Recompute transform matrix of object in new coordinate system (from Z-Up to Y-Up). */ -void create_transform_matrix(Object *obj, float transform_mat[4][4]) +/* Recompute transform matrix of object in new coordinate system + * (from Z-Up to Y-Up). */ +void create_transform_matrix(Object *obj, float r_yup_mat[4][4], AbcMatrixMode mode, + Object *proxy_from) { - float rot_mat[3][3], rot[3][3], scale_mat[4][4], invmat[4][4], mat[4][4]; - float rot_x_mat[3][3], rot_y_mat[3][3], rot_z_mat[3][3]; - float loc[3], scale[3], euler[3]; - - zero_v3(loc); - zero_v3(scale); - zero_v3(euler); - unit_m3(rot); - unit_m3(rot_mat); - unit_m4(scale_mat); - unit_m4(transform_mat); - unit_m4(invmat); - unit_m4(mat); - - /* get local matrix. */ - if (obj->parent) { - invert_m4_m4(invmat, obj->parent->obmat); - mul_m4_m4m4(mat, invmat, obj->obmat); + float zup_mat[4][4]; + + /* get local or world matrix. */ + if (mode == ABC_MATRIX_LOCAL && obj->parent) { + /* Note that this produces another matrix than the local matrix, due to + * constraints and modifiers as well as the obj->parentinv matrix. */ + invert_m4_m4(obj->parent->imat, obj->parent->obmat); + mul_m4_m4m4(zup_mat, obj->parent->imat, obj->obmat); } else { - copy_m4_m4(mat, obj->obmat); + copy_m4_m4(zup_mat, obj->obmat); } - /* Compute rotation matrix. */ - switch (obj->rotmode) { - case ROT_MODE_AXISANGLE: - { - /* Get euler angles from axis angle rotation. */ - axis_angle_to_eulO(euler, ROT_MODE_XYZ, obj->rotAxis, obj->rotAngle); - - /* Create X, Y, Z rotation matrices from euler angles. */ - create_rotation_matrix(rot_x_mat, rot_y_mat, rot_z_mat, euler, true); - - /* Concatenate rotation matrices. */ - mul_m3_m3m3(rot_mat, rot_mat, rot_y_mat); - mul_m3_m3m3(rot_mat, rot_mat, rot_z_mat); - mul_m3_m3m3(rot_mat, rot_mat, rot_x_mat); - - /* Extract location and scale from matrix. */ - mat4_to_loc_rot_size(loc, rot, scale, mat); - - break; - } - case ROT_MODE_QUAT: - { - float q[4]; - copy_v4_v4(q, obj->quat); - - /* Swap axis. */ - q[2] = obj->quat[3]; - q[3] = -obj->quat[2]; - - /* Compute rotation matrix from quaternion. */ - quat_to_mat3(rot_mat, q); - - /* Extract location and scale from matrix. */ - mat4_to_loc_rot_size(loc, rot, scale, mat); - - break; - } - case ROT_MODE_XYZ: - { - /* Extract location, rotation, and scale form matrix. */ - mat4_to_loc_rot_size(loc, rot, scale, mat); - - /* Get euler angles from rotation matrix. */ - mat3_to_eulO(euler, ROT_MODE_XYZ, rot); - - /* Create X, Y, Z rotation matrices from euler angles. */ - create_rotation_matrix(rot_x_mat, rot_y_mat, rot_z_mat, euler, true); - - /* Concatenate rotation matrices. */ - mul_m3_m3m3(rot_mat, rot_mat, rot_y_mat); - mul_m3_m3m3(rot_mat, rot_mat, rot_z_mat); - mul_m3_m3m3(rot_mat, rot_mat, rot_x_mat); - - break; - } - case ROT_MODE_XZY: - { - /* Extract location, rotation, and scale form matrix. */ - mat4_to_loc_rot_size(loc, rot, scale, mat); - - /* Get euler angles from rotation matrix. */ - mat3_to_eulO(euler, ROT_MODE_XZY, rot); - - /* Create X, Y, Z rotation matrices from euler angles. */ - create_rotation_matrix(rot_x_mat, rot_y_mat, rot_z_mat, euler, true); - - /* Concatenate rotation matrices. */ - mul_m3_m3m3(rot_mat, rot_mat, rot_z_mat); - mul_m3_m3m3(rot_mat, rot_mat, rot_y_mat); - mul_m3_m3m3(rot_mat, rot_mat, rot_x_mat); - - break; - } - case ROT_MODE_YXZ: - { - /* Extract location, rotation, and scale form matrix. */ - mat4_to_loc_rot_size(loc, rot, scale, mat); - - /* Get euler angles from rotation matrix. */ - mat3_to_eulO(euler, ROT_MODE_YXZ, rot); - - /* Create X, Y, Z rotation matrices from euler angles. */ - create_rotation_matrix(rot_x_mat, rot_y_mat, rot_z_mat, euler, true); - - /* Concatenate rotation matrices. */ - mul_m3_m3m3(rot_mat, rot_mat, rot_y_mat); - mul_m3_m3m3(rot_mat, rot_mat, rot_x_mat); - mul_m3_m3m3(rot_mat, rot_mat, rot_z_mat); - - break; - } - case ROT_MODE_YZX: - { - /* Extract location, rotation, and scale form matrix. */ - mat4_to_loc_rot_size(loc, rot, scale, mat); - - /* Get euler angles from rotation matrix. */ - mat3_to_eulO(euler, ROT_MODE_YZX, rot); - - /* Create X, Y, Z rotation matrices from euler angles. */ - create_rotation_matrix(rot_x_mat, rot_y_mat, rot_z_mat, euler, true); - - /* Concatenate rotation matrices. */ - mul_m3_m3m3(rot_mat, rot_mat, rot_x_mat); - mul_m3_m3m3(rot_mat, rot_mat, rot_y_mat); - mul_m3_m3m3(rot_mat, rot_mat, rot_z_mat); - - break; - } - case ROT_MODE_ZXY: - { - /* Extract location, rotation, and scale form matrix. */ - mat4_to_loc_rot_size(loc, rot, scale, mat); - - /* Get euler angles from rotation matrix. */ - mat3_to_eulO(euler, ROT_MODE_ZXY, rot); - - /* Create X, Y, Z rotation matrices from euler angles. */ - create_rotation_matrix(rot_x_mat, rot_y_mat, rot_z_mat, euler, true); - - /* Concatenate rotation matrices. */ - mul_m3_m3m3(rot_mat, rot_mat, rot_z_mat); - mul_m3_m3m3(rot_mat, rot_mat, rot_x_mat); - mul_m3_m3m3(rot_mat, rot_mat, rot_y_mat); - - break; - } - case ROT_MODE_ZYX: - { - /* Extract location, rotation, and scale form matrix. */ - mat4_to_loc_rot_size(loc, rot, scale, mat); - - /* Get euler angles from rotation matrix. */ - mat3_to_eulO(euler, ROT_MODE_ZYX, rot); - - /* Create X, Y, Z rotation matrices from euler angles. */ - create_rotation_matrix(rot_x_mat, rot_y_mat, rot_z_mat, euler, true); - - /* Concatenate rotation matrices. */ - mul_m3_m3m3(rot_mat, rot_mat, rot_x_mat); - mul_m3_m3m3(rot_mat, rot_mat, rot_z_mat); - mul_m3_m3m3(rot_mat, rot_mat, rot_y_mat); - - break; - } + if (proxy_from) { + mul_m4_m4m4(zup_mat, proxy_from->obmat, zup_mat); } - /* Add rotation matrix to transformation matrix. */ - copy_m4_m3(transform_mat, rot_mat); - - /* Add translation to transformation matrix. */ - copy_yup_from_zup(transform_mat[3], loc); - - /* Create scale matrix. */ - scale_mat[0][0] = scale[0]; - scale_mat[1][1] = scale[2]; - scale_mat[2][2] = scale[1]; - - /* Add scale to transformation matrix. */ - mul_m4_m4m4(transform_mat, transform_mat, scale_mat); + copy_m44_axis_swap(r_yup_mat, zup_mat, ABC_YUP_FROM_ZUP); } bool has_property(const Alembic::Abc::ICompoundProperty &prop, const std::string &name) @@ -520,7 +370,10 @@ AbcObjectReader *create_reader(const Alembic::AbcGeom::IObject &object, ImportSe reader = new AbcCurveReader(object, settings); } else { - assert(false); + std::cerr << "Alembic: unknown how to handle objects of schema " + << md.get("schemaObjTitle") + << ", skipping object " + << object.getFullName() << std::endl; } return reader; @@ -537,3 +390,32 @@ ScopeTimer::~ScopeTimer() { fprintf(stderr, "%s: %fs\n", m_message, PIL_check_seconds_timer() - m_start); } + +/* ********************** */ + +bool SimpleLogger::empty() +{ + return ((size_t)m_stream.tellp()) == 0ul; +} + +std::string SimpleLogger::str() const +{ + return m_stream.str(); +} + +void SimpleLogger::clear() +{ + m_stream.clear(); + m_stream.str(""); +} + +std::ostringstream &SimpleLogger::stream() +{ + return m_stream; +} + +std::ostream &operator<<(std::ostream &os, const SimpleLogger &logger) +{ + os << logger.str(); + return os; +} diff --git a/source/blender/alembic/intern/abc_util.h b/source/blender/alembic/intern/abc_util.h index 85ba4d5c9c7..74d0faf97d3 100644 --- a/source/blender/alembic/intern/abc_util.h +++ b/source/blender/alembic/intern/abc_util.h @@ -32,6 +32,11 @@ # define ABC_INLINE static inline #endif +/** + * @brief The CacheReader struct is only used for anonymous pointers, + * to interface between C and C++ code. This library only creates + * pointers to AbcObjectReader (or subclasses thereof). + */ struct CacheReader { int unused; }; @@ -44,16 +49,21 @@ struct ImportSettings; struct ID; struct Object; -std::string get_id_name(ID *id); -std::string get_id_name(Object *ob); -std::string get_object_dag_path_name(Object *ob, Object *dupli_parent); +std::string get_id_name(const ID * const id); +std::string get_id_name(const Object * const ob); +std::string get_object_dag_path_name(const Object * const ob, Object *dupli_parent); bool object_selected(Object *ob); bool parent_selected(Object *ob); Imath::M44d convert_matrix(float mat[4][4]); -void create_transform_matrix(float r_mat[4][4]); -void create_transform_matrix(Object *obj, float transform_mat[4][4]); + +typedef enum { + ABC_MATRIX_WORLD = 1, + ABC_MATRIX_LOCAL = 2, +} AbcMatrixMode; +void create_transform_matrix(Object *obj, float r_transform_mat[4][4], + AbcMatrixMode mode, Object *proxy_from); void split(const std::string &s, const char delim, std::vector<std::string> &tokens); @@ -64,8 +74,7 @@ bool begins_with(const TContainer &input, const TContainer &match) && std::equal(match.begin(), match.end(), input.begin()); } -void convert_matrix(const Imath::M44d &xform, Object *ob, - float r_mat[4][4], float scale, bool has_alembic_parent = false); +void convert_matrix(const Imath::M44d &xform, Object *ob, float r_mat[4][4]); template <typename Schema> void get_min_max_time_ex(const Schema &schema, chrono_t &min, chrono_t &max) @@ -118,34 +127,54 @@ AbcObjectReader *create_reader(const Alembic::AbcGeom::IObject &object, ImportSe ABC_INLINE void copy_zup_from_yup(float zup[3], const float yup[3]) { + const float old_yup1 = yup[1]; /* in case zup == yup */ zup[0] = yup[0]; zup[1] = -yup[2]; - zup[2] = yup[1]; + zup[2] = old_yup1; } ABC_INLINE void copy_zup_from_yup(short zup[3], const short yup[3]) { + const short old_yup1 = yup[1]; /* in case zup == yup */ zup[0] = yup[0]; zup[1] = -yup[2]; - zup[2] = yup[1]; + zup[2] = old_yup1; } /* Copy from Z-up to Y-up. */ ABC_INLINE void copy_yup_from_zup(float yup[3], const float zup[3]) { + const float old_zup1 = zup[1]; /* in case yup == zup */ yup[0] = zup[0]; yup[1] = zup[2]; - yup[2] = -zup[1]; + yup[2] = -old_zup1; } ABC_INLINE void copy_yup_from_zup(short yup[3], const short zup[3]) { + const short old_zup1 = zup[1]; /* in case yup == zup */ yup[0] = zup[0]; yup[1] = zup[2]; - yup[2] = -zup[1]; + yup[2] = -old_zup1; } +/* Names are given in (dst, src) order, just like + * the parameters of copy_m44_axis_swap() */ +typedef enum { + ABC_ZUP_FROM_YUP = 1, + ABC_YUP_FROM_ZUP = 2, +} AbcAxisSwapMode; + +/* Create a rotation matrix for each axis from euler angles. + * Euler angles are swaped to change coordinate system. */ +void create_swapped_rotation_matrix( + float rot_x_mat[3][3], float rot_y_mat[3][3], + float rot_z_mat[3][3], const float euler[3], + AbcAxisSwapMode mode); + +void copy_m44_axis_swap(float dst_mat[4][4], float src_mat[4][4], AbcAxisSwapMode mode); + /* *************************** */ #undef ABC_DEBUG_TIME @@ -165,4 +194,48 @@ public: # define SCOPE_TIMER(message) #endif +/* *************************** */ + +/** + * Utility class whose purpose is to more easily log related informations. An + * instance of the SimpleLogger can be created in any context, and will hold a + * copy of all the strings passed to its output stream. + * + * Different instances of the class may be accessed from different threads, + * although accessing the same instance from different threads will lead to race + * conditions. + */ +class SimpleLogger { + std::ostringstream m_stream; + +public: + /** + * Check whether or not the SimpleLogger's stream is empty. + */ + bool empty(); + + /** + * Return a copy of the string contained in the SimpleLogger's stream. + */ + std::string str() const; + + /** + * Remove the bits set on the SimpleLogger's stream and clear its string. + */ + void clear(); + + /** + * Return a reference to the SimpleLogger's stream, in order to e.g. push + * content into it. + */ + std::ostringstream &stream(); +}; + +#define ABC_LOG(logger) logger.stream() + +/** + * Pass the content of the logger's stream to the specified std::ostream. + */ +std::ostream &operator<<(std::ostream &os, const SimpleLogger &logger); + #endif /* __ABC_UTIL_H__ */ diff --git a/source/blender/alembic/intern/alembic_capi.cc b/source/blender/alembic/intern/alembic_capi.cc index dc5146a26e0..0de0d1a1ab6 100644 --- a/source/blender/alembic/intern/alembic_capi.cc +++ b/source/blender/alembic/intern/alembic_capi.cc @@ -21,6 +21,7 @@ */ #include "../ABC_alembic.h" +#include <boost/foreach.hpp> #include <Alembic/AbcMaterial/IMaterial.h> @@ -120,91 +121,62 @@ ABC_INLINE AbcArchiveHandle *handle_from_archive(ArchiveReader *archive) /* NOTE: this function is similar to visit_objects below, need to keep them in * sync. */ -static void gather_objects_paths(const IObject &object, ListBase *object_paths) +static bool gather_objects_paths(const IObject &object, ListBase *object_paths) { if (!object.valid()) { - return; + return false; } - for (int i = 0; i < object.getNumChildren(); ++i) { - IObject child = object.getChild(i); - if (!child.valid()) { - continue; - } + size_t children_claiming_this_object = 0; + size_t num_children = object.getNumChildren(); - bool get_path = false; + for (size_t i = 0; i < num_children; ++i) { + bool child_claims_this_object = gather_objects_paths(object.getChild(i), object_paths); + children_claiming_this_object += child_claims_this_object ? 1 : 0; + } - const MetaData &md = child.getMetaData(); + const MetaData &md = object.getMetaData(); + bool get_path = false; + bool parent_is_part_of_this_object = false; - if (IXform::matches(md)) { - /* Check whether or not this object is a Maya locator, which is - * similar to empties used as parent object in Blender. */ - if (has_property(child.getProperties(), "locator")) { - get_path = true; - } - else { - /* Avoid creating an empty object if the child of this transform - * is not a transform (that is an empty). */ - if (child.getNumChildren() == 1) { - if (IXform::matches(child.getChild(0).getMetaData())) { - get_path = true; - } -#if 0 - else { - std::cerr << "Skipping " << child.getFullName() << '\n'; - } -#endif - } - else { - get_path = true; - } - } - } - else if (IPolyMesh::matches(md)) { - get_path = true; - } - else if (ISubD::matches(md)) { - get_path = true; - } - else if (INuPatch::matches(md)) { -#ifdef USE_NURBS - get_path = true; -#endif - } - else if (ICamera::matches(md)) { - get_path = true; - } - else if (IPoints::matches(md)) { - get_path = true; - } - else if (IMaterial::matches(md)) { - /* Pass for now. */ - } - else if (ILight::matches(md)) { - /* Pass for now. */ - } - else if (IFaceSet::matches(md)) { - /* Pass, those are handled in the mesh reader. */ - } - else if (ICurves::matches(md)) { + if (!object.getParent()) { + /* The root itself is not an object we should import. */ + } + else if (IXform::matches(md)) { + if (has_property(object.getProperties(), "locator")) { get_path = true; } else { - assert(false); + get_path = children_claiming_this_object == 0; } - if (get_path) { - AlembicObjectPath *abc_path = static_cast<AlembicObjectPath *>( - MEM_callocN(sizeof(AlembicObjectPath), "AlembicObjectPath")); - - BLI_strncpy(abc_path->path, child.getFullName().c_str(), PATH_MAX); + /* Transforms are never "data" for their parent. */ + parent_is_part_of_this_object = false; + } + else { + /* These types are "data" for their parent. */ + get_path = + IPolyMesh::matches(md) || + ISubD::matches(md) || +#ifdef USE_NURBS + INuPatch::matches(md) || +#endif + ICamera::matches(md) || + IPoints::matches(md) || + ICurves::matches(md); + parent_is_part_of_this_object = get_path; + } - BLI_addtail(object_paths, abc_path); - } + if (get_path) { + void *abc_path_void = MEM_callocN(sizeof(AlembicObjectPath), "AlembicObjectPath"); + AlembicObjectPath *abc_path = static_cast<AlembicObjectPath *>(abc_path_void); - gather_objects_paths(child, object_paths); + BLI_strncpy(abc_path->path, object.getFullName().c_str(), PATH_MAX); + BLI_addtail(object_paths, abc_path); } + + return parent_is_part_of_this_object; } AbcArchiveHandle *ABC_create_handle(const char *filename, ListBase *object_paths) @@ -301,10 +273,10 @@ static void export_startjob(void *customdata, short *stop, short *do_update, flo } } catch (const std::exception &e) { - std::cerr << "Abc Export error: " << e.what() << '\n'; + ABC_LOG(data->settings.logger) << "Abc Export error: " << e.what() << '\n'; } catch (...) { - std::cerr << "Abc Export error\n"; + ABC_LOG(data->settings.logger) << "Abc Export: unknown error...\n"; } } @@ -316,6 +288,11 @@ static void export_endjob(void *customdata) BLI_delete(data->filename, false, false); } + if (!data->settings.logger.empty()) { + std::cerr << data->settings.logger; + WM_report(RPT_ERROR, "Errors occured during the export, look in the console to know more..."); + } + G.is_rendering = false; BKE_spacedata_draw_locks(false); } @@ -331,6 +308,22 @@ void ABC_export( job->bmain = CTX_data_main(C); BLI_strncpy(job->filename, filepath, 1024); + /* Alright, alright, alright.... + * + * ExportJobData contains an ExportSettings containing a SimpleLogger. + * + * Since ExportJobData is a C-style struct dynamically allocated with + * MEM_mallocN (see above), its construtor is never called, therefore the + * ExportSettings constructor is not called which implies that the + * SimpleLogger one is not called either. SimpleLogger in turn does not call + * the constructor of its data members which ultimately means that its + * std::ostringstream member has a NULL pointer. To be able to properly use + * the stream's operator<<, the pointer needs to be set, therefore we have + * to properly construct everything. And this is done using the placement + * new operator as here below. It seems hackish, but I'm too lazy to + * do bigger refactor and maybe there is a better way which does not involve + * hardcore refactoring. */ + new (&job->settings) ExportSettings(); job->settings.scene = job->scene; job->settings.frame_start = params->frame_start; job->settings.frame_end = params->frame_end; @@ -376,114 +369,208 @@ void ABC_export( /* ********************** Import file ********************** */ -static void visit_object(const IObject &object, - std::vector<AbcObjectReader *> &readers, - GHash *parent_map, - ImportSettings &settings) +/** + * Generates an AbcObjectReader for this Alembic object and its children. + * + * \param object The Alembic IObject to visit. + * \param readers The created AbcObjectReader * will be appended to this vector. + * \param settings Import settings, not used directly but passed to the + * AbcObjectReader subclass constructors. + * \param r_assign_as_parent Return parameter, contains a list of reader + * pointers, whose parent pointer should still be set. + * This is filled when this call to visit_object() didn't create + * a reader that should be the parent. + * \return A pair of boolean and reader pointer. The boolean indicates whether + * this IObject claims its parent as part of the same object + * (for example an IPolyMesh object would claim its parent, as the mesh + * is interpreted as the object's data, and the parent IXform as its + * Blender object). The pointer is the AbcObjectReader that represents + * the IObject parameter. + * + * NOTE: this function is similar to gather_object_paths above, need to keep + * them in sync. */ +static std::pair<bool, AbcObjectReader *> visit_object( + const IObject &object, + AbcObjectReader::ptr_vector &readers, + ImportSettings &settings, + AbcObjectReader::ptr_vector &r_assign_as_parent) { + const std::string & full_name = object.getFullName(); + if (!object.valid()) { - return; + std::cerr << " - " + << full_name + << ": object is invalid, skipping it and all its children.\n"; + return std::make_pair(false, static_cast<AbcObjectReader *>(NULL)); } - for (int i = 0; i < object.getNumChildren(); ++i) { - IObject child = object.getChild(i); + /* The interpretation of data by the children determine the role of this + * object. This is especially important for Xform objects, as they can be + * either part of a Blender object or a Blender object (Empty) themselves. + */ + size_t children_claiming_this_object = 0; + size_t num_children = object.getNumChildren(); + AbcObjectReader::ptr_vector claiming_child_readers; + AbcObjectReader::ptr_vector nonclaiming_child_readers; + AbcObjectReader::ptr_vector assign_as_parent; + for (size_t i = 0; i < num_children; ++i) { + const IObject ichild = object.getChild(i); - if (!child.valid()) { - continue; + /* TODO: When we only support C++11, use std::tie() instead. */ + std::pair<bool, AbcObjectReader *> child_result; + child_result = visit_object(ichild, readers, settings, assign_as_parent); + + bool child_claims_this_object = child_result.first; + AbcObjectReader *child_reader = child_result.second; + + if (child_reader == NULL) { + BLI_assert(!child_claims_this_object); + } + else { + if (child_claims_this_object) { + claiming_child_readers.push_back(child_reader); + } else { + nonclaiming_child_readers.push_back(child_reader); + } } - AbcObjectReader *reader = NULL; + children_claiming_this_object += child_claims_this_object ? 1 : 0; + } + BLI_assert(children_claiming_this_object == claiming_child_readers.size()); - const MetaData &md = child.getMetaData(); + AbcObjectReader *reader = NULL; + const MetaData &md = object.getMetaData(); + bool parent_is_part_of_this_object = false; - if (IXform::matches(md)) { - bool create_xform = false; + if (!object.getParent()) { + /* The root itself is not an object we should import. */ + } + else if (IXform::matches(md)) { + bool create_empty; - /* Check whether or not this object is a Maya locator, which is - * similar to empties used as parent object in Blender. */ - if (has_property(child.getProperties(), "locator")) { - create_xform = true; - } - else { - /* Avoid creating an empty object if the child of this transform - * is not a transform (that is an empty). */ - if (child.getNumChildren() == 1) { - if (IXform::matches(child.getChild(0).getMetaData())) { - create_xform = true; - } -#if 0 - else { - std::cerr << "Skipping " << child.getFullName() << '\n'; - } -#endif - } - else { - create_xform = true; - } - } + /* An xform can either be a Blender Object (if it contains a mesh, for + * example), but it can also be an Empty. Its correct translation to + * Blender's data model depends on its children. */ - if (create_xform) { - reader = new AbcEmptyReader(child, settings); - } + /* Check whether or not this object is a Maya locator, which is + * similar to empties used as parent object in Blender. */ + if (has_property(object.getProperties(), "locator")) { + create_empty = true; } - else if (IPolyMesh::matches(md)) { - reader = new AbcMeshReader(child, settings); + else { + create_empty = claiming_child_readers.empty(); } - else if (ISubD::matches(md)) { - reader = new AbcSubDReader(child, settings); + + if (create_empty) { + reader = new AbcEmptyReader(object, settings); } - else if (INuPatch::matches(md)) { + } + else if (IPolyMesh::matches(md)) { + reader = new AbcMeshReader(object, settings); + parent_is_part_of_this_object = true; + } + else if (ISubD::matches(md)) { + reader = new AbcSubDReader(object, settings); + parent_is_part_of_this_object = true; + } + else if (INuPatch::matches(md)) { #ifdef USE_NURBS - /* TODO(kevin): importing cyclic NURBS from other software crashes - * at the moment. This is due to the fact that NURBS in other - * software have duplicated points which causes buffer overflows in - * Blender. Need to figure out exactly how these points are - * duplicated, in all cases (cyclic U, cyclic V, and cyclic UV). - * Until this is fixed, disabling NURBS reading. */ - reader = new AbcNurbsReader(child, settings); + /* TODO(kevin): importing cyclic NURBS from other software crashes + * at the moment. This is due to the fact that NURBS in other + * software have duplicated points which causes buffer overflows in + * Blender. Need to figure out exactly how these points are + * duplicated, in all cases (cyclic U, cyclic V, and cyclic UV). + * Until this is fixed, disabling NURBS reading. */ + reader = new AbcNurbsReader(object, settings); + parent_is_part_of_this_object = true; #endif + } + else if (ICamera::matches(md)) { + reader = new AbcCameraReader(object, settings); + parent_is_part_of_this_object = true; + } + else if (IPoints::matches(md)) { + reader = new AbcPointsReader(object, settings); + parent_is_part_of_this_object = true; + } + else if (IMaterial::matches(md)) { + /* Pass for now. */ + } + else if (ILight::matches(md)) { + /* Pass for now. */ + } + else if (IFaceSet::matches(md)) { + /* Pass, those are handled in the mesh reader. */ + } + else if (ICurves::matches(md)) { + reader = new AbcCurveReader(object, settings); + parent_is_part_of_this_object = true; + } + else { + std::cerr << "Alembic object " << full_name + << " is of unsupported schema type '" + << object.getMetaData().get("schemaObjTitle") << "'" + << std::endl; + } + + if (reader) { + /* We have created a reader, which should imply that this object is + * not claimed as part of any child Alembic object. */ + BLI_assert(claiming_child_readers.empty()); + + readers.push_back(reader); + reader->incref(); + + AlembicObjectPath *abc_path = static_cast<AlembicObjectPath *>( + MEM_callocN(sizeof(AlembicObjectPath), "AlembicObjectPath")); + BLI_strncpy(abc_path->path, full_name.c_str(), PATH_MAX); + BLI_addtail(&settings.cache_file->object_paths, abc_path); + + /* We can now assign this reader as parent for our children. */ + if (nonclaiming_child_readers.size() + assign_as_parent.size() > 0) { + /* TODO: When we only support C++11, use for (a: b) instead. */ + BOOST_FOREACH(AbcObjectReader *child_reader, nonclaiming_child_readers) { + child_reader->parent_reader = reader; + } + BOOST_FOREACH(AbcObjectReader *child_reader, assign_as_parent) { + child_reader->parent_reader = reader; + } } - else if (ICamera::matches(md)) { - reader = new AbcCameraReader(child, settings); - } - else if (IPoints::matches(md)) { - reader = new AbcPointsReader(child, settings); - } - else if (IMaterial::matches(md)) { - /* Pass for now. */ - } - else if (ILight::matches(md)) { - /* Pass for now. */ - } - else if (IFaceSet::matches(md)) { - /* Pass, those are handled in the mesh reader. */ - } - else if (ICurves::matches(md)) { - reader = new AbcCurveReader(child, settings); + } + else if (object.getParent()) { + if (claiming_child_readers.size() > 0) { + /* The first claiming child will serve just fine as parent to + * our non-claiming children. Since all claiming children share + * the same XForm, it doesn't really matter which one we pick. */ + AbcObjectReader *claiming_child = claiming_child_readers[0]; + BOOST_FOREACH(AbcObjectReader *child_reader, nonclaiming_child_readers) { + child_reader->parent_reader = claiming_child; + } + BOOST_FOREACH(AbcObjectReader *child_reader, assign_as_parent) { + child_reader->parent_reader = claiming_child; + } + /* Claiming children should have our parent set as their parent. */ + BOOST_FOREACH(AbcObjectReader *child_reader, claiming_child_readers) { + r_assign_as_parent.push_back(child_reader); + } } else { - assert(false); - } - - if (reader) { - readers.push_back(reader); - reader->incref(); - - AlembicObjectPath *abc_path = static_cast<AlembicObjectPath *>( - MEM_callocN(sizeof(AlembicObjectPath), "AlembicObjectPath")); - - BLI_strncpy(abc_path->path, child.getFullName().c_str(), PATH_MAX); - - BLI_addtail(&settings.cache_file->object_paths, abc_path); - - /* Cast to `void *` explicitly to avoid compiler errors because it - * is a `const char *` which the compiler cast to `const void *` - * instead of the expected `void *`. */ - BLI_ghash_insert(parent_map, (void *)child.getFullName().c_str(), reader); + /* This object isn't claimed by any child, and didn't produce + * a reader. Odd situation, could be the top Alembic object, or + * an unsupported Alembic schema. Delegate to our parent. */ + BOOST_FOREACH(AbcObjectReader *child_reader, claiming_child_readers) { + r_assign_as_parent.push_back(child_reader); + } + BOOST_FOREACH(AbcObjectReader *child_reader, nonclaiming_child_readers) { + r_assign_as_parent.push_back(child_reader); + } + BOOST_FOREACH(AbcObjectReader *child_reader, assign_as_parent) { + r_assign_as_parent.push_back(child_reader); + } } - - visit_object(child, readers, parent_map, settings); } + + return std::make_pair(parent_is_part_of_this_object, reader); } enum { @@ -498,7 +585,6 @@ struct ImportJobData { char filename[1024]; ImportSettings settings; - GHash *parent_map; std::vector<AbcObjectReader *> readers; short *stop; @@ -575,11 +661,12 @@ static void import_startjob(void *user_data, short *stop, short *do_update, floa *data->do_update = true; *data->progress = 0.05f; - data->parent_map = BLI_ghash_str_new("alembic parent ghash"); - /* Parse Alembic Archive. */ + AbcObjectReader::ptr_vector assign_as_parent; + visit_object(archive->getTop(), data->readers, data->settings, assign_as_parent); - visit_object(archive->getTop(), data->readers, data->parent_map, data->settings); + /* There shouldn't be any orphans. */ + BLI_assert(assign_as_parent.size() == 0); if (G.is_break) { data->was_cancelled = true; @@ -603,13 +690,16 @@ static void import_startjob(void *user_data, short *stop, short *do_update, floa if (reader->valid()) { reader->readObjectData(data->bmain, 0.0f); - reader->readObjectMatrix(0.0f); min_time = std::min(min_time, reader->minTime()); max_time = std::max(max_time, reader->maxTime()); } + else { + std::cerr << "Object " << reader->name() << " in Alembic file " + << data->filename << " is invalid.\n"; + } - *data->progress = 0.1f + 0.6f * (++i / size); + *data->progress = 0.1f + 0.3f * (++i / size); *data->do_update = true; if (G.is_break) { @@ -627,45 +717,31 @@ static void import_startjob(void *user_data, short *stop, short *do_update, floa CFRA = SFRA; } else if (min_time < max_time) { - SFRA = static_cast<int>(min_time * FPS); - EFRA = static_cast<int>(max_time * FPS); + SFRA = static_cast<int>(round(min_time * FPS)); + EFRA = static_cast<int>(round(max_time * FPS)); CFRA = SFRA; } } - /* Setup parentship. */ - - i = 0; + /* Setup parenthood. */ for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) { const AbcObjectReader *reader = *iter; - const AbcObjectReader *parent_reader = NULL; - const IObject &iobject = reader->iobject(); - - IObject parent = iobject.getParent(); + const AbcObjectReader *parent_reader = reader->parent_reader; + Object *ob = reader->object(); - if (!IXform::matches(iobject.getHeader())) { - /* In the case of an non XForm node, the parent is the transform - * matrix of the data itself, so we get the its grand parent. - */ - - /* Special case with object only containing a mesh and some strands, - * we want both objects to be parented to the same object. */ - if (!is_mesh_and_strands(parent)) { - parent = parent.getParent(); - } + if (parent_reader == NULL) { + ob->parent = NULL; } - - parent_reader = reinterpret_cast<AbcObjectReader *>( - BLI_ghash_lookup(data->parent_map, parent.getFullName().c_str())); - - if (parent_reader) { - Object *parent = parent_reader->object(); - - if (parent != NULL && reader->object() != parent) { - Object *ob = reader->object(); - ob->parent = parent; - } + else { + ob->parent = parent_reader->object(); } + } + + /* Setup transformations and constraints. */ + i = 0; + for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) { + AbcObjectReader *reader = *iter; + reader->setupObjectTransform(0.0f); *data->progress = 0.7f + 0.3f * (++i / size); *data->do_update = true; @@ -690,10 +766,9 @@ static void import_endjob(void *user_data) for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) { Object *ob = (*iter)->object(); - if (ob->data) { - BKE_libblock_free_us(data->bmain, ob->data); - ob->data = NULL; - } + /* It's possible that cancellation occured between the creation of + * the reader and the creation of the Blender object. */ + if (ob == NULL) continue; BKE_libblock_free_us(data->bmain, ob); } @@ -723,10 +798,6 @@ static void import_endjob(void *user_data) } } - if (data->parent_map) { - BLI_ghash_free(data->parent_map, NULL, NULL); - } - switch (data->error_code) { default: case ABC_NO_ERROR: @@ -760,7 +831,6 @@ void ABC_import(bContext *C, const char *filepath, float scale, bool is_sequence job->settings.sequence_len = sequence_len; job->settings.offset = offset; job->settings.validate_meshes = validate_meshes; - job->parent_map = NULL; job->error_code = ABC_NO_ERROR; job->was_cancelled = false; diff --git a/source/blender/blenfont/intern/blf_glyph.c b/source/blender/blenfont/intern/blf_glyph.c index aa7d539538b..bbbabfb8ba2 100644 --- a/source/blender/blenfont/intern/blf_glyph.c +++ b/source/blender/blenfont/intern/blf_glyph.c @@ -135,6 +135,7 @@ void blf_glyph_cache_clear(FontBLF *font) while ((gc = BLI_pophead(&font->cache))) { blf_glyph_cache_free(gc); } + font->glyph_cache = NULL; } void blf_glyph_cache_free(GlyphCacheBLF *gc) diff --git a/source/blender/blenkernel/BKE_appdir.h b/source/blender/blenkernel/BKE_appdir.h index 077fe2a629c..ac8f861fa56 100644 --- a/source/blender/blenkernel/BKE_appdir.h +++ b/source/blender/blenkernel/BKE_appdir.h @@ -27,11 +27,15 @@ /* note on naming: typical _get() suffix is omitted here, * since its the main purpose of the API. */ const char *BKE_appdir_folder_default(void); +const char *BKE_appdir_folder_id_ex(const int folder_id, const char *subfolder, char *path, size_t path_len); const char *BKE_appdir_folder_id(const int folder_id, const char *subfolder); const char *BKE_appdir_folder_id_create(const int folder_id, const char *subfolder); const char *BKE_appdir_folder_id_user_notest(const int folder_id, const char *subfolder); const char *BKE_appdir_folder_id_version(const int folder_id, const int ver, const bool do_check); +bool BKE_appdir_app_template_any(void); +bool BKE_appdir_app_template_id_search(const char *app_template, char *path, size_t path_len); + /* Initialize path to program executable */ void BKE_appdir_program_path_init(const char *argv0); diff --git a/source/blender/blenkernel/BKE_blender.h b/source/blender/blenkernel/BKE_blender.h index 62a15bae153..d55926ffb1e 100644 --- a/source/blender/blenkernel/BKE_blender.h +++ b/source/blender/blenkernel/BKE_blender.h @@ -52,6 +52,8 @@ void BKE_blender_userdef_set_data(struct UserDef *userdef); void BKE_blender_userdef_free_data(struct UserDef *userdef); void BKE_blender_userdef_refresh(void); +void BKE_blender_userdef_set_app_template(struct UserDef *userdef); + /* set this callback when a UI is running */ void BKE_blender_callback_test_break_set(void (*func)(void)); int BKE_blender_test_break(void); diff --git a/source/blender/blenkernel/BKE_blendfile.h b/source/blender/blenkernel/BKE_blendfile.h index 75978120051..ac58451e412 100644 --- a/source/blender/blenkernel/BKE_blendfile.h +++ b/source/blender/blenkernel/BKE_blendfile.h @@ -50,6 +50,7 @@ bool BKE_blendfile_read_from_memory( bool BKE_blendfile_read_from_memfile( struct bContext *C, struct MemFile *memfile, struct ReportList *reports, int skip_flag); +void BKE_blendfile_read_make_empty(struct bContext *C); struct UserDef *BKE_blendfile_userdef_read( const char *filepath, struct ReportList *reports); diff --git a/source/blender/blenkernel/BKE_cloth.h b/source/blender/blenkernel/BKE_cloth.h index 36330242f18..6c517bd02df 100644 --- a/source/blender/blenkernel/BKE_cloth.h +++ b/source/blender/blenkernel/BKE_cloth.h @@ -136,9 +136,6 @@ typedef struct ClothSpring { float restlen; /* The original length of the spring. */ int type; /* types defined in BKE_cloth.h ("springType") */ int flags; /* defined in BKE_cloth.h, e.g. deactivated due to tearing */ - float dfdx[3][3]; - float dfdv[3][3]; - float f[3]; float stiffness; /* stiffness factor from the vertex groups */ float editrestlen; @@ -240,9 +237,6 @@ void bvhselftree_update_from_cloth(struct ClothModifierData *clmd, bool moving); // needed for button_object.c void cloth_clear_cache (struct Object *ob, struct ClothModifierData *clmd, float framenr ); -// needed for cloth.c -int cloth_add_spring (struct ClothModifierData *clmd, unsigned int indexA, unsigned int indexB, float restlength, int spring_type); - void cloth_parallel_transport_hair_frame(float mat[3][3], const float dir_old[3], const float dir_new[3]); //////////////////////////////////////////////// diff --git a/source/blender/blenkernel/BKE_idprop.h b/source/blender/blenkernel/BKE_idprop.h index 5b10d7ebc06..ab8728faedb 100644 --- a/source/blender/blenkernel/BKE_idprop.h +++ b/source/blender/blenkernel/BKE_idprop.h @@ -60,8 +60,6 @@ typedef union IDPropertyTemplate { IDProperty *IDP_NewIDPArray(const char *name) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); IDProperty *IDP_CopyIDPArray(const IDProperty *array) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); -void IDP_FreeIDPArray(IDProperty *prop); - /* shallow copies item */ void IDP_SetIndexArray(struct IDProperty *prop, int index, struct IDProperty *item) ATTR_NONNULL(); struct IDProperty *IDP_GetIndexArray(struct IDProperty *prop, int index) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); @@ -81,8 +79,8 @@ void IDP_ConcatString(struct IDProperty *str1, struct IDProperty *append) ATTR_N void IDP_FreeString(struct IDProperty *prop) ATTR_NONNULL(); /*-------- ID Type -------*/ -void IDP_LinkID(struct IDProperty *prop, ID *id); -void IDP_UnlinkID(struct IDProperty *prop); + +typedef void(*IDPWalkFunc)(void *userData, IDProperty *idp); /*-------- Group Functions -------*/ @@ -112,11 +110,12 @@ bool IDP_EqualsProperties(struct IDProperty *prop1, struct IDProperty *prop2) AT struct IDProperty *IDP_New(const char type, const IDPropertyTemplate *val, const char *name) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +void IDP_FreeProperty_ex(struct IDProperty *prop, const bool do_id_user); void IDP_FreeProperty(struct IDProperty *prop); void IDP_ClearProperty(IDProperty *prop); -void IDP_UnlinkProperty(struct IDProperty *prop); +void IDP_RelinkProperty(struct IDProperty *prop); #define IDP_Int(prop) ((prop)->data.val) #define IDP_Array(prop) ((prop)->data.pointer) @@ -134,11 +133,15 @@ void IDP_UnlinkProperty(struct IDProperty *prop); # define IDP_IDPArray(prop) _Generic((prop), \ IDProperty *: ((IDProperty *) (prop)->data.pointer), \ const IDProperty *: ((const IDProperty *) (prop)->data.pointer)) +# define IDP_Id(prop) _Generic((prop), \ + IDProperty *: ((ID *) (prop)->data.pointer), \ + const IDProperty *: ((const ID *) (prop)->data.pointer)) #else # define IDP_Float(prop) (*(float *)&(prop)->data.val) # define IDP_Double(prop) (*(double *)&(prop)->data.val) # define IDP_String(prop) ((char *) (prop)->data.pointer) # define IDP_IDPArray(prop) ((IDProperty *) (prop)->data.pointer) +# define IDP_Id(prop) ((ID *) (prop)->data.pointer) #endif #ifndef NDEBUG diff --git a/source/blender/blenkernel/BKE_library.h b/source/blender/blenkernel/BKE_library.h index 72ae2cf4efa..6649cfbb585 100644 --- a/source/blender/blenkernel/BKE_library.h +++ b/source/blender/blenkernel/BKE_library.h @@ -66,7 +66,7 @@ struct ID *BKE_libblock_find_name(const short type, const char *name) ATTR_WARN_ void BKE_libblock_free(struct Main *bmain, void *idv) ATTR_NONNULL(); void BKE_libblock_free_ex(struct Main *bmain, void *idv, const bool do_id_user, const bool do_ui_user) ATTR_NONNULL(); void BKE_libblock_free_us(struct Main *bmain, void *idv) ATTR_NONNULL(); -void BKE_libblock_free_data(struct Main *bmain, struct ID *id) ATTR_NONNULL(); +void BKE_libblock_free_data(struct Main *bmain, struct ID *id, const bool do_id_user) ATTR_NONNULL(); void BKE_libblock_delete(struct Main *bmain, void *idv) ATTR_NONNULL(); void BKE_id_lib_local_paths(struct Main *bmain, struct Library *lib, struct ID *id); diff --git a/source/blender/blenkernel/BKE_library_query.h b/source/blender/blenkernel/BKE_library_query.h index 1258e2fa72e..68bf9f43fe1 100644 --- a/source/blender/blenkernel/BKE_library_query.h +++ b/source/blender/blenkernel/BKE_library_query.h @@ -87,7 +87,7 @@ void BKE_library_update_ID_link_user(struct ID *id_dst, struct ID *id_src, const int BKE_library_ID_use_ID(struct ID *id_user, struct ID *id_used); -bool BKE_library_idtype_can_use_idtype(const short id_type_owner, const short id_type_used); +bool BKE_library_id_can_use_idtype(struct ID *id_owner, const short id_type_used); bool BKE_library_ID_is_locally_used(struct Main *bmain, void *idv); bool BKE_library_ID_is_indirectly_used(struct Main *bmain, void *idv); diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 546f0d97c2b..193f1154c54 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -49,7 +49,7 @@ #include "RNA_types.h" /* not very important, but the stack solver likes to know a maximum */ -#define MAX_SOCKET 64 +#define MAX_SOCKET 512 struct bContext; struct bNode; diff --git a/source/blender/blenkernel/intern/appdir.c b/source/blender/blenkernel/intern/appdir.c index f2f0a92d8b3..43fd47981b1 100644 --- a/source/blender/blenkernel/intern/appdir.c +++ b/source/blender/blenkernel/intern/appdir.c @@ -114,18 +114,26 @@ static char *blender_version_decimal(const int ver) * Concatenates path_base, (optional) path_sep and (optional) folder_name into targetpath, * returning true if result points to a directory. */ -static bool test_path(char *targetpath, const char *path_base, const char *path_sep, const char *folder_name) +static bool test_path( + char *targetpath, size_t targetpath_len, + const char *path_base, const char *path_sep, const char *folder_name) { char tmppath[FILE_MAX]; - if (path_sep) BLI_join_dirfile(tmppath, sizeof(tmppath), path_base, path_sep); - else BLI_strncpy(tmppath, path_base, sizeof(tmppath)); + if (path_sep) { + BLI_join_dirfile(tmppath, sizeof(tmppath), path_base, path_sep); + } + else { + BLI_strncpy(tmppath, path_base, sizeof(tmppath)); + } /* rare cases folder_name is omitted (when looking for ~/.config/blender/2.xx dir only) */ - if (folder_name) - BLI_make_file_string("/", targetpath, tmppath, folder_name); - else - BLI_strncpy(targetpath, tmppath, sizeof(tmppath)); + if (folder_name) { + BLI_join_dirfile(targetpath, targetpath_len, tmppath, folder_name); + } + else { + BLI_strncpy(targetpath, tmppath, targetpath_len); + } /* FIXME: why is "//" on front of tmppath expanded to "/" (by BLI_join_dirfile) * if folder_name is specified but not otherwise? */ @@ -179,7 +187,9 @@ static bool test_env_path(char *path, const char *envvar) * \param ver To construct name of version-specific directory within bprogdir * \return true if such a directory exists. */ -static bool get_path_local(char *targetpath, const char *folder_name, const char *subfolder_name, const int ver) +static bool get_path_local( + char *targetpath, size_t targetpath_len, + const char *folder_name, const char *subfolder_name, const int ver) { char relfolder[FILE_MAX]; @@ -201,11 +211,12 @@ static bool get_path_local(char *targetpath, const char *folder_name, const char /* try EXECUTABLE_DIR/2.5x/folder_name - new default directory for local blender installed files */ #ifdef __APPLE__ - static char osx_resourses[FILE_MAX]; /* due new codesign situation in OSX > 10.9.5 we must move the blender_version dir with contents to Resources */ + /* due new codesign situation in OSX > 10.9.5 we must move the blender_version dir with contents to Resources */ + static char osx_resourses[FILE_MAX]; sprintf(osx_resourses, "%s../Resources", bprogdir); - return test_path(targetpath, osx_resourses, blender_version_decimal(ver), relfolder); + return test_path(targetpath, targetpath_len, osx_resourses, blender_version_decimal(ver), relfolder); #else - return test_path(targetpath, bprogdir, blender_version_decimal(ver), relfolder); + return test_path(targetpath, targetpath_len, bprogdir, blender_version_decimal(ver), relfolder); #endif } @@ -219,7 +230,7 @@ static bool is_portable_install(void) const int ver = BLENDER_VERSION; char path[FILE_MAX]; - return get_path_local(path, "config", NULL, ver); + return get_path_local(path, sizeof(path), "config", NULL, ver); } /** @@ -233,20 +244,22 @@ static bool is_portable_install(void) * \param ver Blender version, used to construct a subdirectory name * \return true if it was able to construct such a path. */ -static bool get_path_user(char *targetpath, const char *folder_name, const char *subfolder_name, const char *envvar, const int ver) +static bool get_path_user( + char *targetpath, size_t targetpath_len, const char *folder_name, const char *subfolder_name, + const char *envvar, const int ver) { char user_path[FILE_MAX]; const char *user_base_path; /* for portable install, user path is always local */ - if (is_portable_install()) - return get_path_local(targetpath, folder_name, subfolder_name, ver); - + if (is_portable_install()) { + return get_path_local(targetpath, targetpath_len, folder_name, subfolder_name, ver); + } user_path[0] = '\0'; if (test_env_path(user_path, envvar)) { if (subfolder_name) { - return test_path(targetpath, user_path, NULL, subfolder_name); + return test_path(targetpath, targetpath_len, user_path, NULL, subfolder_name); } else { BLI_strncpy(targetpath, user_path, FILE_MAX); @@ -266,10 +279,10 @@ static bool get_path_user(char *targetpath, const char *folder_name, const char #endif if (subfolder_name) { - return test_path(targetpath, user_path, folder_name, subfolder_name); + return test_path(targetpath, targetpath_len, user_path, folder_name, subfolder_name); } else { - return test_path(targetpath, user_path, NULL, folder_name); + return test_path(targetpath, targetpath_len, user_path, NULL, folder_name); } } @@ -283,7 +296,9 @@ static bool get_path_user(char *targetpath, const char *folder_name, const char * \param ver Blender version, used to construct a subdirectory name * \return true if it was able to construct such a path. */ -static bool get_path_system(char *targetpath, const char *folder_name, const char *subfolder_name, const char *envvar, const int ver) +static bool get_path_system( + char *targetpath, size_t targetpath_len, const char *folder_name, const char *subfolder_name, + const char *envvar, const int ver) { char system_path[FILE_MAX]; const char *system_base_path; @@ -307,13 +322,13 @@ static bool get_path_system(char *targetpath, const char *folder_name, const cha /* try CWD/release/folder_name */ if (BLI_current_working_dir(cwd, sizeof(cwd))) { - if (test_path(targetpath, cwd, "release", relfolder)) { + if (test_path(targetpath, targetpath_len, cwd, "release", relfolder)) { return true; } } /* try EXECUTABLE_DIR/release/folder_name */ - if (test_path(targetpath, bprogdir, "release", relfolder)) + if (test_path(targetpath, targetpath_len, bprogdir, "release", relfolder)) return true; /* end developer overrides */ @@ -324,7 +339,7 @@ static bool get_path_system(char *targetpath, const char *folder_name, const cha if (test_env_path(system_path, envvar)) { if (subfolder_name) { - return test_path(targetpath, system_path, NULL, subfolder_name); + return test_path(targetpath, targetpath_len, system_path, NULL, subfolder_name); } else { BLI_strncpy(targetpath, system_path, FILE_MAX); @@ -345,57 +360,63 @@ static bool get_path_system(char *targetpath, const char *folder_name, const cha if (subfolder_name) { /* try $BLENDERPATH/folder_name/subfolder_name */ - return test_path(targetpath, system_path, folder_name, subfolder_name); + return test_path(targetpath, targetpath_len, system_path, folder_name, subfolder_name); } else { /* try $BLENDERPATH/folder_name */ - return test_path(targetpath, system_path, NULL, folder_name); + return test_path(targetpath, targetpath_len, system_path, NULL, folder_name); } } -/* get a folder out of the 'folder_id' presets for paths */ -/* returns the path if found, NULL string if not */ -const char *BKE_appdir_folder_id(const int folder_id, const char *subfolder) +/** + * Get a folder out of the 'folder_id' presets for paths. + * returns the path if found, NULL string if not + * + * \param subfolder: The name of a directory to check for, + * this may contain path separators but must resolve to a directory, checked with #BLI_is_dir. + */ +const char *BKE_appdir_folder_id_ex( + const int folder_id, const char *subfolder, + char *path, size_t path_len) { const int ver = BLENDER_VERSION; - static char path[FILE_MAX] = ""; - + switch (folder_id) { case BLENDER_DATAFILES: /* general case */ - if (get_path_user(path, "datafiles", subfolder, "BLENDER_USER_DATAFILES", ver)) break; - if (get_path_local(path, "datafiles", subfolder, ver)) break; - if (get_path_system(path, "datafiles", subfolder, "BLENDER_SYSTEM_DATAFILES", ver)) break; + if (get_path_user(path, path_len, "datafiles", subfolder, "BLENDER_USER_DATAFILES", ver)) break; + if (get_path_local(path, path_len, "datafiles", subfolder, ver)) break; + if (get_path_system(path, path_len, "datafiles", subfolder, "BLENDER_SYSTEM_DATAFILES", ver)) break; return NULL; case BLENDER_USER_DATAFILES: - if (get_path_user(path, "datafiles", subfolder, "BLENDER_USER_DATAFILES", ver)) break; + if (get_path_user(path, path_len, "datafiles", subfolder, "BLENDER_USER_DATAFILES", ver)) break; return NULL; case BLENDER_SYSTEM_DATAFILES: - if (get_path_local(path, "datafiles", subfolder, ver)) break; - if (get_path_system(path, "datafiles", subfolder, "BLENDER_SYSTEM_DATAFILES", ver)) break; + if (get_path_local(path, path_len, "datafiles", subfolder, ver)) break; + if (get_path_system(path, path_len, "datafiles", subfolder, "BLENDER_SYSTEM_DATAFILES", ver)) break; return NULL; case BLENDER_USER_AUTOSAVE: - if (get_path_user(path, "autosave", subfolder, "BLENDER_USER_DATAFILES", ver)) break; + if (get_path_user(path, path_len, "autosave", subfolder, "BLENDER_USER_DATAFILES", ver)) break; return NULL; case BLENDER_USER_CONFIG: - if (get_path_user(path, "config", subfolder, "BLENDER_USER_CONFIG", ver)) break; + if (get_path_user(path, path_len, "config", subfolder, "BLENDER_USER_CONFIG", ver)) break; return NULL; case BLENDER_USER_SCRIPTS: - if (get_path_user(path, "scripts", subfolder, "BLENDER_USER_SCRIPTS", ver)) break; + if (get_path_user(path, path_len, "scripts", subfolder, "BLENDER_USER_SCRIPTS", ver)) break; return NULL; case BLENDER_SYSTEM_SCRIPTS: - if (get_path_local(path, "scripts", subfolder, ver)) break; - if (get_path_system(path, "scripts", subfolder, "BLENDER_SYSTEM_SCRIPTS", ver)) break; + if (get_path_local(path, path_len, "scripts", subfolder, ver)) break; + if (get_path_system(path, path_len, "scripts", subfolder, "BLENDER_SYSTEM_SCRIPTS", ver)) break; return NULL; case BLENDER_SYSTEM_PYTHON: - if (get_path_local(path, "python", subfolder, ver)) break; - if (get_path_system(path, "python", subfolder, "BLENDER_SYSTEM_PYTHON", ver)) break; + if (get_path_local(path, path_len, "python", subfolder, ver)) break; + if (get_path_system(path, path_len, "python", subfolder, "BLENDER_SYSTEM_PYTHON", ver)) break; return NULL; default: @@ -406,6 +427,13 @@ const char *BKE_appdir_folder_id(const int folder_id, const char *subfolder) return path; } +const char *BKE_appdir_folder_id( + const int folder_id, const char *subfolder) +{ + static char path[FILE_MAX] = ""; + return BKE_appdir_folder_id_ex(folder_id, subfolder, path, sizeof(path)); +} + /** * Returns the path to a folder in the user area without checking that it actually exists first. */ @@ -416,16 +444,16 @@ const char *BKE_appdir_folder_id_user_notest(const int folder_id, const char *su switch (folder_id) { case BLENDER_USER_DATAFILES: - get_path_user(path, "datafiles", subfolder, "BLENDER_USER_DATAFILES", ver); + get_path_user(path, sizeof(path), "datafiles", subfolder, "BLENDER_USER_DATAFILES", ver); break; case BLENDER_USER_CONFIG: - get_path_user(path, "config", subfolder, "BLENDER_USER_CONFIG", ver); + get_path_user(path, sizeof(path), "config", subfolder, "BLENDER_USER_CONFIG", ver); break; case BLENDER_USER_AUTOSAVE: - get_path_user(path, "autosave", subfolder, "BLENDER_USER_AUTOSAVE", ver); + get_path_user(path, sizeof(path), "autosave", subfolder, "BLENDER_USER_AUTOSAVE", ver); break; case BLENDER_USER_SCRIPTS: - get_path_user(path, "scripts", subfolder, "BLENDER_USER_SCRIPTS", ver); + get_path_user(path, sizeof(path), "scripts", subfolder, "BLENDER_USER_SCRIPTS", ver); break; default: BLI_assert(0); @@ -469,13 +497,13 @@ const char *BKE_appdir_folder_id_version(const int folder_id, const int ver, con bool ok; switch (folder_id) { case BLENDER_RESOURCE_PATH_USER: - ok = get_path_user(path, NULL, NULL, NULL, ver); + ok = get_path_user(path, sizeof(path), NULL, NULL, NULL, ver); break; case BLENDER_RESOURCE_PATH_LOCAL: - ok = get_path_local(path, NULL, NULL, ver); + ok = get_path_local(path, sizeof(path), NULL, NULL, ver); break; case BLENDER_RESOURCE_PATH_SYSTEM: - ok = get_path_system(path, NULL, NULL, NULL, ver); + ok = get_path_system(path, sizeof(path), NULL, NULL, NULL, ver); break; default: path[0] = '\0'; /* in case do_check is false */ @@ -655,6 +683,48 @@ bool BKE_appdir_program_python_search( return is_found; } +static const char *app_template_directory_search[2] = { + "startup" SEP_STR "bl_app_templates_user", + "startup" SEP_STR "bl_app_templates_system", +}; + +static const int app_template_directory_id[2] = { + BLENDER_USER_SCRIPTS, + BLENDER_SYSTEM_SCRIPTS, +}; + +/** + * Return true if templates exist + */ +bool BKE_appdir_app_template_any(void) +{ + char temp_dir[FILE_MAX]; + for (int i = 0; i < 2; i++) { + if (BKE_appdir_folder_id_ex( + app_template_directory_id[i], app_template_directory_search[i], + temp_dir, sizeof(temp_dir))) + { + return true; + } + } + return false; +} + +bool BKE_appdir_app_template_id_search(const char *app_template, char *path, size_t path_len) +{ + for (int i = 0; i < 2; i++) { + char subdir[FILE_MAX]; + BLI_join_dirfile(subdir, sizeof(subdir), app_template_directory_search[i], app_template); + if (BKE_appdir_folder_id_ex( + app_template_directory_id[i], subdir, + path, path_len)) + { + return true; + } + } + return false; +} + /** * Gets the temp directory when blender first runs. * If the default path is not found, use try $TEMP @@ -713,7 +783,8 @@ static void where_is_temp(char *fullname, char *basename, const size_t maxlen, c BLI_add_slash(fullname); #ifdef WIN32 if (userdir && userdir != fullname) { - BLI_strncpy(userdir, fullname, maxlen); /* also set user pref to show %TEMP%. /tmp/ is just plain confusing for Windows users. */ + /* also set user pref to show %TEMP%. /tmp/ is just plain confusing for Windows users. */ + BLI_strncpy(userdir, fullname, maxlen); } #endif } diff --git a/source/blender/blenkernel/intern/blender.c b/source/blender/blenkernel/intern/blender.c index f661f18fbc0..ceb641073e0 100644 --- a/source/blender/blenkernel/intern/blender.c +++ b/source/blender/blenkernel/intern/blender.c @@ -238,6 +238,44 @@ void BKE_blender_userdef_refresh(void) } +/** + * Write U from userdef. + * This function defines which settings a template will override for the user preferences. + */ +void BKE_blender_userdef_set_app_template(UserDef *userdef) +{ + /* TODO: + * - keymaps + * - various minor settings (add as needed). + */ + +#define LIST_OVERRIDE(id) { \ + BLI_freelistN(&U.id); \ + BLI_movelisttolist(&U.id, &userdef->id); \ +} ((void)0) + +#define MEMCPY_OVERRIDE(id) \ + memcpy(U.id, userdef->id, sizeof(U.id)); + + /* for some types we need custom free functions */ + userdef_free_addons(&U); + userdef_free_keymaps(&U); + + LIST_OVERRIDE(uistyles); + LIST_OVERRIDE(uifonts); + LIST_OVERRIDE(themes); + LIST_OVERRIDE(addons); + LIST_OVERRIDE(user_keymaps); + + MEMCPY_OVERRIDE(light); + + MEMCPY_OVERRIDE(font_path_ui); + MEMCPY_OVERRIDE(font_path_ui_mono); + +#undef LIST_OVERRIDE +#undef MEMCPY_OVERRIDE +} + /* ***************** testing for break ************* */ static void (*blender_test_break_cb)(void) = NULL; diff --git a/source/blender/blenkernel/intern/blendfile.c b/source/blender/blenkernel/intern/blendfile.c index 5725a12e3d4..a521d671ea4 100644 --- a/source/blender/blenkernel/intern/blendfile.c +++ b/source/blender/blenkernel/intern/blendfile.c @@ -356,8 +356,10 @@ int BKE_blendfile_read( BlendFileData *bfd; int retval = BKE_BLENDFILE_READ_OK; - if (strstr(filepath, BLENDER_STARTUP_FILE) == NULL) /* don't print user-pref loading */ - printf("read blend: %s\n", filepath); + /* don't print user-pref loading */ + if (strstr(filepath, BLENDER_STARTUP_FILE) == NULL) { + printf("Read blend: %s\n", filepath); + } bfd = BLO_read_from_file(filepath, reports, skip_flags); if (bfd) { @@ -424,6 +426,32 @@ bool BKE_blendfile_read_from_memfile( return (bfd != NULL); } +/** + * Utility to make a file 'empty' used for startup to optionally give an empty file. + * Handy for tests. + */ +void BKE_blendfile_read_make_empty(bContext *C) +{ + Main *bmain = CTX_data_main(C); + + ListBase *lbarray[MAX_LIBARRAY]; + ID *id; + int a; + + a = set_listbasepointers(bmain, lbarray); + while (a--) { + id = lbarray[a]->first; + if (id != NULL) { + if (ELEM(GS(id->name), ID_SCE, ID_SCR, ID_WM)) { + continue; + } + while ((id = lbarray[a]->first)) { + BKE_libblock_delete(bmain, id); + } + } + } +} + /* only read the userdef from a .blend */ UserDef *BKE_blendfile_userdef_read(const char *filepath, ReportList *reports) { diff --git a/source/blender/blenkernel/intern/cloth.c b/source/blender/blenkernel/intern/cloth.c index 28ef3f6f248..ee0fde1ea61 100644 --- a/source/blender/blenkernel/intern/cloth.c +++ b/source/blender/blenkernel/intern/cloth.c @@ -869,12 +869,6 @@ static int cloth_from_object(Object *ob, ClothModifierData *clmd, DerivedMesh *d return 0; } - for ( i = 0; i < dm->getNumVerts(dm); i++) { - if ((!(cloth->verts[i].flags & CLOTH_VERT_FLAG_PINNED)) && (cloth->verts[i].goal > ALMOST_ZERO)) { - cloth_add_spring (clmd, i, i, 0.0, CLOTH_SPRING_TYPE_GOAL); - } - } - // init our solver BPH_cloth_solver_init(ob, clmd); @@ -944,37 +938,6 @@ BLI_INLINE void spring_verts_ordered_set(ClothSpring *spring, int v0, int v1) } } -// be careful: implicit solver has to be resettet when using this one! -// --> only for implicit handling of this spring! -int cloth_add_spring(ClothModifierData *clmd, unsigned int indexA, unsigned int indexB, float restlength, int spring_type) -{ - Cloth *cloth = clmd->clothObject; - ClothSpring *spring = NULL; - - if (cloth) { - // TODO: look if this spring is already there - - spring = (ClothSpring *)MEM_callocN ( sizeof ( ClothSpring ), "cloth spring" ); - - if (!spring) - return 0; - - spring->ij = indexA; - spring->kl = indexB; - spring->restlen = restlength; - spring->type = spring_type; - spring->flags = 0; - spring->stiffness = 0; - - cloth->numsprings++; - - BLI_linklist_prepend ( &cloth->springs, spring ); - - return 1; - } - return 0; -} - static void cloth_free_edgelist(LinkNodePair *edgelist, unsigned int mvert_num) { if (edgelist) { diff --git a/source/blender/blenkernel/intern/customdata.c b/source/blender/blenkernel/intern/customdata.c index c9f0b8ec9ca..7c3f0ac630d 100644 --- a/source/blender/blenkernel/intern/customdata.c +++ b/source/blender/blenkernel/intern/customdata.c @@ -33,14 +33,10 @@ /** \file blender/blenkernel/intern/customdata.c * \ingroup bke */ - - -#include <math.h> -#include <string.h> -#include <assert.h> #include "MEM_guardedalloc.h" +#include "DNA_customdata_types.h" #include "DNA_meshdata_types.h" #include "DNA_ID.h" @@ -62,20 +58,16 @@ #include "BKE_mesh_remap.h" #include "BKE_multires.h" -#include "data_transfer_intern.h" - #include "bmesh.h" -#include <math.h> -#include <string.h> +/* only for customdata_data_transfer_interp_normal_normals */ +#include "data_transfer_intern.h" /* number of layers to add when growing a CustomData object */ #define CUSTOMDATA_GROW 5 /* ensure typemap size is ok */ -BLI_STATIC_ASSERT(sizeof(((CustomData *)NULL)->typemap) / - sizeof(((CustomData *)NULL)->typemap[0]) == CD_NUMTYPES, - "size mismatch"); +BLI_STATIC_ASSERT(ARRAY_SIZE(((CustomData *)NULL)->typemap) == CD_NUMTYPES, "size mismatch"); /********************* Layer type information **********************/ @@ -805,18 +797,15 @@ static void layerInterp_mloopcol( const float *sub_weights, int count, void *dest) { MLoopCol *mc = dest; - int i; - const float *sub_weight; struct { float a; float r; float g; float b; - } col; - col.a = col.r = col.g = col.b = 0; + } col = {0}; - sub_weight = sub_weights; - for (i = 0; i < count; ++i) { + const float *sub_weight = sub_weights; + for (int i = 0; i < count; ++i) { float weight = weights ? weights[i] : 1; const MLoopCol *src = sources[i]; if (sub_weights) { @@ -833,19 +822,16 @@ static void layerInterp_mloopcol( col.a += src->a * weight; } } - + + /* Subdivide smooth or fractal can cause problems without clamping * although weights should also not cause this situation */ - CLAMP(col.a, 0.0f, 255.0f); - CLAMP(col.r, 0.0f, 255.0f); - CLAMP(col.g, 0.0f, 255.0f); - CLAMP(col.b, 0.0f, 255.0f); - /* delay writing to the destination incase dest is in sources */ - mc->r = (int)col.r; - mc->g = (int)col.g; - mc->b = (int)col.b; - mc->a = (int)col.a; + /* also delay writing to the destination incase dest is in sources */ + mc->r = CLAMPIS(iroundf(col.r), 0, 255); + mc->g = CLAMPIS(iroundf(col.g), 0, 255); + mc->b = CLAMPIS(iroundf(col.b), 0, 255); + mc->a = CLAMPIS(iroundf(col.a), 0, 255); } static int layerMaxNum_mloopcol(void) @@ -1068,15 +1054,10 @@ static void layerInterp_mcol( /* Subdivide smooth or fractal can cause problems without clamping * although weights should also not cause this situation */ - CLAMP(col[j].a, 0.0f, 255.0f); - CLAMP(col[j].r, 0.0f, 255.0f); - CLAMP(col[j].g, 0.0f, 255.0f); - CLAMP(col[j].b, 0.0f, 255.0f); - - mc[j].a = (int)col[j].a; - mc[j].r = (int)col[j].r; - mc[j].g = (int)col[j].g; - mc[j].b = (int)col[j].b; + mc[j].a = CLAMPIS(iroundf(col[j].a), 0, 255); + mc[j].r = CLAMPIS(iroundf(col[j].r), 0, 255); + mc[j].g = CLAMPIS(iroundf(col[j].g), 0, 255); + mc[j].b = CLAMPIS(iroundf(col[j].b), 0, 255); } } diff --git a/source/blender/blenkernel/intern/depsgraph.c b/source/blender/blenkernel/intern/depsgraph.c index 678dc92a5f2..302e55a1f5a 100644 --- a/source/blender/blenkernel/intern/depsgraph.c +++ b/source/blender/blenkernel/intern/depsgraph.c @@ -3053,7 +3053,7 @@ void DAG_id_type_tag(Main *bmain, short idtype) DAG_id_type_tag(bmain, ID_SCE); } - bmain->id_tag_update[BKE_idcode_to_index(idtype)] = 1; + atomic_fetch_and_or_uint8((uint8_t*)&bmain->id_tag_update[BKE_idcode_to_index(idtype)], 1); } int DAG_id_type_tagged(Main *bmain, short idtype) diff --git a/source/blender/blenkernel/intern/idprop.c b/source/blender/blenkernel/intern/idprop.c index b2641b110f8..074a9a12fe0 100644 --- a/source/blender/blenkernel/intern/idprop.c +++ b/source/blender/blenkernel/intern/idprop.c @@ -117,31 +117,35 @@ IDProperty *IDP_CopyIDPArray(const IDProperty *array) return narray; } -void IDP_FreeIDPArray(IDProperty *prop) +static void IDP_FreeIDPArray(IDProperty *prop, const bool do_id_user) { int i; BLI_assert(prop->type == IDP_IDPARRAY); for (i = 0; i < prop->len; i++) - IDP_FreeProperty(GETPROP(prop, i)); + IDP_FreeProperty_ex(GETPROP(prop, i), do_id_user); if (prop->data.pointer) MEM_freeN(prop->data.pointer); } -/*shallow copies item*/ +/* shallow copies item */ void IDP_SetIndexArray(IDProperty *prop, int index, IDProperty *item) { IDProperty *old; BLI_assert(prop->type == IDP_IDPARRAY); + if (index >= prop->len || index < 0) + return; + old = GETPROP(prop, index); - if (index >= prop->len || index < 0) return; - if (item != old) IDP_FreeProperty(old); - - memcpy(GETPROP(prop, index), item, sizeof(IDProperty)); + if (item != old) { + IDP_FreeProperty(old); + + memcpy(old, item, sizeof(IDProperty)); + } } IDProperty *IDP_GetIndexArray(IDProperty *prop, int index) @@ -433,22 +437,24 @@ void IDP_FreeString(IDProperty *prop) /* -------------------------------------------------------------------- */ -/* ID Type (not in use yet) */ +/* ID Type */ -/** \name IDProperty ID API (unused) +/** \name IDProperty ID API * \{ */ -void IDP_LinkID(IDProperty *prop, ID *id) -{ - if (prop->data.pointer) - id_us_min(((ID *)prop->data.pointer)); - prop->data.pointer = id; - id_us_plus(id); -} -void IDP_UnlinkID(IDProperty *prop) +static IDProperty *IDP_CopyID(const IDProperty *prop) { - id_us_min(((ID *)prop->data.pointer)); + IDProperty *newp; + + BLI_assert(prop->type == IDP_ID); + newp = idp_generic_copy(prop); + + newp->data.pointer = prop->data.pointer; + id_us_plus(IDP_Id(newp)); + + return newp; } + /** \} */ @@ -499,14 +505,9 @@ void IDP_SyncGroupValues(IDProperty *dest, const IDProperty *src) break; default: { - IDProperty *tmp = other; - IDProperty *copy = IDP_CopyProperty(prop); - - BLI_insertlinkafter(&dest->data.group, other, copy); - BLI_remlink(&dest->data.group, tmp); - - IDP_FreeProperty(tmp); - MEM_freeN(tmp); + BLI_insertlinkreplace(&dest->data.group, other, IDP_CopyProperty(prop)); + IDP_FreeProperty(other); + MEM_freeN(other); break; } } @@ -526,11 +527,9 @@ void IDP_SyncGroupTypes(IDProperty *dst, const IDProperty *src, const bool do_ar if ((prop_dst->type != prop_src->type || prop_dst->subtype != prop_src->subtype) || (do_arraylen && ELEM(prop_dst->type, IDP_ARRAY, IDP_IDPARRAY) && (prop_src->len != prop_dst->len))) { - IDP_FreeFromGroup(dst, prop_dst); - prop_dst = IDP_CopyProperty(prop_src); - - dst->len++; - BLI_insertlinkbefore(&dst->data.group, prop_dst_next, prop_dst); + BLI_insertlinkreplace(&dst->data.group, prop_dst, IDP_CopyProperty(prop_src)); + IDP_FreeProperty(prop_dst); + MEM_freeN(prop_dst); } else if (prop_dst->type == IDP_GROUP) { IDP_SyncGroupTypes(prop_dst, prop_src, do_arraylen); @@ -555,11 +554,7 @@ void IDP_ReplaceGroupInGroup(IDProperty *dest, const IDProperty *src) for (prop = src->data.group.first; prop; prop = prop->next) { for (loop = dest->data.group.first; loop; loop = loop->next) { if (STREQ(loop->name, prop->name)) { - IDProperty *copy = IDP_CopyProperty(prop); - - BLI_insertlinkafter(&dest->data.group, loop, copy); - - BLI_remlink(&dest->data.group, loop); + BLI_insertlinkreplace(&dest->data.group, loop, IDP_CopyProperty(prop)); IDP_FreeProperty(loop); MEM_freeN(loop); break; @@ -586,9 +581,7 @@ void IDP_ReplaceInGroup_ex(IDProperty *group, IDProperty *prop, IDProperty *prop BLI_assert(prop_exist == IDP_GetPropertyFromGroup(group, prop->name)); if ((prop_exist = IDP_GetPropertyFromGroup(group, prop->name))) { - BLI_insertlinkafter(&group->data.group, prop_exist, prop); - - BLI_remlink(&group->data.group, prop_exist); + BLI_insertlinkreplace(&group->data.group, prop_exist, prop); IDP_FreeProperty(prop_exist); MEM_freeN(prop_exist); } @@ -719,13 +712,13 @@ IDProperty *IDP_GetPropertyTypeFromGroup(IDProperty *prop, const char *name, con * This is because all ID Property freeing functions free only direct data (not the ID Property * struct itself), but for Groups the child properties *are* considered * direct data. */ -static void IDP_FreeGroup(IDProperty *prop) +static void IDP_FreeGroup(IDProperty *prop, const bool do_id_user) { IDProperty *loop; BLI_assert(prop->type == IDP_GROUP); for (loop = prop->data.group.first; loop; loop = loop->next) { - IDP_FreeProperty(loop); + IDP_FreeProperty_ex(loop, do_id_user); } BLI_freelistN(&prop->data.group); } @@ -742,12 +735,51 @@ IDProperty *IDP_CopyProperty(const IDProperty *prop) switch (prop->type) { case IDP_GROUP: return IDP_CopyGroup(prop); case IDP_STRING: return IDP_CopyString(prop); + case IDP_ID: return IDP_CopyID(prop); case IDP_ARRAY: return IDP_CopyArray(prop); case IDP_IDPARRAY: return IDP_CopyIDPArray(prop); default: return idp_generic_copy(prop); } } +/* Updates ID pointers after an object has been copied */ +/* TODO Nuke this once its only user has been correctly converted to use generic ID management from BKE_library! */ +void IDP_RelinkProperty(struct IDProperty *prop) +{ + if (!prop) + return; + + switch (prop->type) { + case IDP_GROUP: + { + for (IDProperty *loop = prop->data.group.first; loop; loop = loop->next) { + IDP_RelinkProperty(loop); + } + break; + } + case IDP_IDPARRAY: + { + IDProperty *idp_array = IDP_Array(prop); + for (int i = 0; i < prop->len; i++) { + IDP_RelinkProperty(&idp_array[i]); + } + break; + } + case IDP_ID: + { + ID *id = IDP_Id(prop); + if (id && id->newid) { + id_us_min(IDP_Id(prop)); + prop->data.pointer = id->newid; + id_us_plus(IDP_Id(prop)); + } + break; + } + default: + break; /* Nothing to do for other IDProp types. */ + } +} + /** * Get the Group property that contains the id properties for ID id. Set create_if_needed * to create the Group property and attach it to id if it doesn't exist; otherwise @@ -844,6 +876,8 @@ bool IDP_EqualsProperties_ex(IDProperty *prop1, IDProperty *prop2, const bool is return false; return true; } + case IDP_ID: + return (IDP_Id(prop1) == IDP_Id(prop2)); default: /* should never get here */ BLI_assert(0); @@ -867,17 +901,19 @@ bool IDP_EqualsProperties(IDProperty *prop1, IDProperty *prop2) * The union is simple to use; see the top of this header file for its definition. * An example of using this function: * - * IDPropertyTemplate val; - * IDProperty *group, *idgroup, *color; - * group = IDP_New(IDP_GROUP, val, "group1"); //groups don't need a template. + * \code{.c} + * IDPropertyTemplate val; + * IDProperty *group, *idgroup, *color; + * group = IDP_New(IDP_GROUP, val, "group1"); //groups don't need a template. * - * val.array.len = 4 - * val.array.type = IDP_FLOAT; - * color = IDP_New(IDP_ARRAY, val, "color1"); + * val.array.len = 4 + * val.array.type = IDP_FLOAT; + * color = IDP_New(IDP_ARRAY, val, "color1"); * - * idgroup = IDP_GetProperties(some_id, 1); - * IDP_AddToGroup(idgroup, color); - * IDP_AddToGroup(idgroup, group); + * idgroup = IDP_GetProperties(some_id, 1); + * IDP_AddToGroup(idgroup, color); + * IDP_AddToGroup(idgroup, group); + * \endcode * * Note that you MUST either attach the id property to an id property group with * IDP_AddToGroup or MEM_freeN the property, doing anything else might result in @@ -897,7 +933,7 @@ IDProperty *IDP_New(const char type, const IDPropertyTemplate *val, const char * *(float *)&prop->data.val = val->f; break; case IDP_DOUBLE: - prop = MEM_callocN(sizeof(IDProperty), "IDProperty float"); + prop = MEM_callocN(sizeof(IDProperty), "IDProperty double"); *(double *)&prop->data.val = val->d; break; case IDP_ARRAY: @@ -917,6 +953,7 @@ IDProperty *IDP_New(const char type, const IDPropertyTemplate *val, const char * prop->len = prop->totallen = val->array.len; break; } + printf("%s: bad array type.\n",__func__); return NULL; } case IDP_STRING: @@ -963,6 +1000,14 @@ IDProperty *IDP_New(const char type, const IDPropertyTemplate *val, const char * /* heh I think all needed values are set properly by calloc anyway :) */ break; } + case IDP_ID: + { + prop = MEM_callocN(sizeof(IDProperty), "IDProperty datablock"); + prop->data.pointer = (void *)val->id; + prop->type = IDP_ID; + id_us_plus(IDP_Id(prop)); + break; + } default: { prop = MEM_callocN(sizeof(IDProperty), "IDProperty array"); @@ -977,11 +1022,10 @@ IDProperty *IDP_New(const char type, const IDPropertyTemplate *val, const char * } /** - * \note this will free all child properties of list arrays and groups! - * Also, note that this does NOT unlink anything! Plus it doesn't free - * the actual struct IDProperty struct either. + * \note This will free allocated data, all child properties of arrays and groups, and unlink IDs! + * But it does not free the actual IDProperty struct itself. */ -void IDP_FreeProperty(IDProperty *prop) +void IDP_FreeProperty_ex(IDProperty *prop, const bool do_id_user) { switch (prop->type) { case IDP_ARRAY: @@ -991,14 +1035,24 @@ void IDP_FreeProperty(IDProperty *prop) IDP_FreeString(prop); break; case IDP_GROUP: - IDP_FreeGroup(prop); + IDP_FreeGroup(prop, do_id_user); break; case IDP_IDPARRAY: - IDP_FreeIDPArray(prop); + IDP_FreeIDPArray(prop, do_id_user); + break; + case IDP_ID: + if (do_id_user) { + id_us_min(IDP_Id(prop)); + } break; } } +void IDP_FreeProperty(IDProperty *prop) +{ + IDP_FreeProperty_ex(prop, true); +} + void IDP_ClearProperty(IDProperty *prop) { IDP_FreeProperty(prop); @@ -1006,18 +1060,4 @@ void IDP_ClearProperty(IDProperty *prop) prop->len = prop->totallen = 0; } -/** - * Unlinks any struct IDProperty<->ID linkage that might be going on. - * - * \note currently unused - */ -void IDP_UnlinkProperty(IDProperty *prop) -{ - switch (prop->type) { - case IDP_ID: - IDP_UnlinkID(prop); - break; - } -} - /** \} */ diff --git a/source/blender/blenkernel/intern/library_query.c b/source/blender/blenkernel/intern/library_query.c index 9685f1f5af6..0616c614848 100644 --- a/source/blender/blenkernel/intern/library_query.c +++ b/source/blender/blenkernel/intern/library_query.c @@ -33,6 +33,7 @@ #include "DNA_actuator_types.h" #include "DNA_anim_types.h" +#include "DNA_armature_types.h" #include "DNA_brush_types.h" #include "DNA_camera_types.h" #include "DNA_constraint_types.h" @@ -70,10 +71,12 @@ #include "BKE_animsys.h" #include "BKE_constraint.h" #include "BKE_fcurve.h" +#include "BKE_idprop.h" #include "BKE_library.h" #include "BKE_library_query.h" #include "BKE_main.h" #include "BKE_modifier.h" +#include "BKE_node.h" #include "BKE_particle.h" #include "BKE_rigidbody.h" #include "BKE_sca.h" @@ -82,7 +85,9 @@ #define FOREACH_FINALIZE _finalize -#define FOREACH_FINALIZE_VOID FOREACH_FINALIZE: (void)0 +#define FOREACH_FINALIZE_VOID \ + if (0) { goto FOREACH_FINALIZE; } \ + FOREACH_FINALIZE: ((void)0) #define FOREACH_CALLBACK_INVOKE_ID_PP(_data, id_pp, _cb_flag) \ CHECK_TYPE(id_pp, ID **); \ @@ -140,6 +145,37 @@ typedef struct LibraryForeachIDData { BLI_LINKSTACK_DECLARE(ids_todo, ID *); } LibraryForeachIDData; +static void library_foreach_idproperty_ID_link(LibraryForeachIDData *data, IDProperty *prop, int flag) +{ + if (!prop) + return; + + switch (prop->type) { + case IDP_GROUP: + { + for (IDProperty *loop = prop->data.group.first; loop; loop = loop->next) { + library_foreach_idproperty_ID_link(data, loop, flag); + } + break; + } + case IDP_IDPARRAY: + { + IDProperty *loop = IDP_Array(prop); + for (int i = 0; i < prop->len; i++) { + library_foreach_idproperty_ID_link(data, &loop[i], flag); + } + break; + } + case IDP_ID: + FOREACH_CALLBACK_INVOKE_ID(data, prop->data.pointer, flag); + break; + default: + break; /* Nothing to do here with other types of IDProperties... */ + } + + FOREACH_FINALIZE_VOID; +} + static void library_foreach_rigidbodyworldSceneLooper( struct RigidBodyWorld *UNUSED(rbw), ID **id_pointer, void *user_data, int cb_flag) { @@ -265,6 +301,17 @@ static void library_foreach_paint(LibraryForeachIDData *data, Paint *paint) FOREACH_FINALIZE_VOID; } +static void library_foreach_bone(LibraryForeachIDData *data, Bone *bone) +{ + library_foreach_idproperty_ID_link(data, bone->prop, IDWALK_CB_USER); + + for (Bone *curbone = bone->childbase.first; curbone; curbone = curbone->next) { + library_foreach_bone(data, curbone); + } + + FOREACH_FINALIZE_VOID; +} + static void library_foreach_ID_as_subdata_link( ID **id_pp, LibraryIDLinkCallback callback, void *user_data, int flag, LibraryForeachIDData *data) { @@ -337,6 +384,8 @@ void BKE_library_foreach_ID_link(Main *bmain, ID *id, LibraryIDLinkCallback call continue; } + library_foreach_idproperty_ID_link(&data, id->properties, IDWALK_CB_USER); + AnimData *adt = BKE_animdata_from_id(id); if (adt) { library_foreach_animationData(&data, adt); @@ -402,6 +451,7 @@ void BKE_library_foreach_ID_link(Main *bmain, ID *id, LibraryIDLinkCallback call CALLBACK_INVOKE(seq->clip, IDWALK_CB_USER); CALLBACK_INVOKE(seq->mask, IDWALK_CB_USER); CALLBACK_INVOKE(seq->sound, IDWALK_CB_USER); + library_foreach_idproperty_ID_link(&data, seq->prop, IDWALK_CB_USER); for (SequenceModifierData *smd = seq->modifiers.first; smd; smd = smd->next) { CALLBACK_INVOKE(smd->mask_id, IDWALK_CB_USER); } @@ -515,6 +565,7 @@ void BKE_library_foreach_ID_link(Main *bmain, ID *id, LibraryIDLinkCallback call data.cb_flag |= proxy_cb_flag; for (pchan = object->pose->chanbase.first; pchan; pchan = pchan->next) { + library_foreach_idproperty_ID_link(&data, pchan->prop, IDWALK_CB_USER); CALLBACK_INVOKE(pchan->custom, IDWALK_CB_USER); BKE_constraints_id_loop(&pchan->constraints, library_foreach_constraintObjectLooper, &data); } @@ -554,6 +605,16 @@ void BKE_library_foreach_ID_link(Main *bmain, ID *id, LibraryIDLinkCallback call break; } + case ID_AR: + { + bArmature *arm = (bArmature *)id; + + for (Bone *bone = arm->bonebase.first; bone; bone = bone->next) { + library_foreach_bone(&data, bone); + } + break; + } + case ID_ME: { Mesh *mesh = (Mesh *) id; @@ -736,9 +797,27 @@ void BKE_library_foreach_ID_link(Main *bmain, ID *id, LibraryIDLinkCallback call { bNodeTree *ntree = (bNodeTree *) id; bNode *node; + bNodeSocket *sock; + CALLBACK_INVOKE(ntree->gpd, IDWALK_CB_USER); + for (node = ntree->nodes.first; node; node = node->next) { CALLBACK_INVOKE_ID(node->id, IDWALK_CB_USER); + + library_foreach_idproperty_ID_link(&data, node->prop, IDWALK_CB_USER); + for (sock = node->inputs.first; sock; sock = sock->next) { + library_foreach_idproperty_ID_link(&data, sock->prop, IDWALK_CB_USER); + } + for (sock = node->outputs.first; sock; sock = sock->next) { + library_foreach_idproperty_ID_link(&data, sock->prop, IDWALK_CB_USER); + } + } + + for (sock = ntree->inputs.first; sock; sock = sock->next) { + library_foreach_idproperty_ID_link(&data, sock->prop, IDWALK_CB_USER); + } + for (sock = ntree->outputs.first; sock; sock = sock->next) { + library_foreach_idproperty_ID_link(&data, sock->prop, IDWALK_CB_USER); } break; } @@ -898,7 +977,6 @@ void BKE_library_foreach_ID_link(Main *bmain, ID *id, LibraryIDLinkCallback call case ID_VF: case ID_TXT: case ID_SO: - case ID_AR: case ID_GD: case ID_WM: case ID_PAL: @@ -948,9 +1026,25 @@ void BKE_library_update_ID_link_user(ID *id_dst, ID *id_src, const int cb_flag) */ /* XXX This has to be fully rethink, basing check on ID type is not really working anymore (and even worth once * IDProps will support ID pointers), we'll have to do some quick checks on IDs themselves... */ -bool BKE_library_idtype_can_use_idtype(const short id_type_owner, const short id_type_used) +bool BKE_library_id_can_use_idtype(ID *id_owner, const short id_type_used) { - if (id_type_can_have_animdata(id_type_owner)) { + /* any type of ID can be used in custom props. */ + if (id_owner->properties) { + return true; + } + + const short id_type_owner = GS(id_owner->name); + + /* IDProps of armature bones and nodes, and bNode->id can use virtually any type of ID. */ + if (ELEM(id_type_owner, ID_NT, ID_AR)) { + return true; + } + + if (ntreeFromID(id_owner)) { + return true; + } + + if (BKE_animdata_from_id(id_owner)) { return true; /* AnimationData can use virtually any kind of datablocks, through drivers especially. */ } @@ -959,8 +1053,7 @@ bool BKE_library_idtype_can_use_idtype(const short id_type_owner, const short id return ELEM(id_type_used, ID_LI); case ID_SCE: return (ELEM(id_type_used, ID_OB, ID_WO, ID_SCE, ID_MC, ID_MA, ID_GR, ID_TXT, - ID_LS, ID_MSK, ID_SO, ID_GD, ID_BR, ID_PAL, ID_IM, ID_NT) || - BKE_library_idtype_can_use_idtype(ID_NT, id_type_used)); + ID_LS, ID_MSK, ID_SO, ID_GD, ID_BR, ID_PAL, ID_IM, ID_NT)); case ID_OB: /* Could be the following, but simpler to just always say 'yes' here. */ #if 0 @@ -977,13 +1070,13 @@ bool BKE_library_idtype_can_use_idtype(const short id_type_owner, const short id case ID_MB: return ELEM(id_type_used, ID_MA); case ID_MA: - return (ELEM(id_type_used, ID_TE, ID_GR) || BKE_library_idtype_can_use_idtype(ID_NT, id_type_used)); + return (ELEM(id_type_used, ID_TE, ID_GR)); case ID_TE: - return (ELEM(id_type_used, ID_IM, ID_OB) || BKE_library_idtype_can_use_idtype(ID_NT, id_type_used)); + return (ELEM(id_type_used, ID_IM, ID_OB)); case ID_LT: return ELEM(id_type_used, ID_KE); case ID_LA: - return (ELEM(id_type_used, ID_TE) || BKE_library_idtype_can_use_idtype(ID_NT, id_type_used)); + return (ELEM(id_type_used, ID_TE)); case ID_CA: return ELEM(id_type_used, ID_OB); case ID_KE: @@ -991,7 +1084,7 @@ bool BKE_library_idtype_can_use_idtype(const short id_type_owner, const short id case ID_SCR: return ELEM(id_type_used, ID_SCE); case ID_WO: - return (ELEM(id_type_used, ID_TE) || BKE_library_idtype_can_use_idtype(ID_NT, id_type_used)); + return (ELEM(id_type_used, ID_TE)); case ID_SPK: return ELEM(id_type_used, ID_SO); case ID_GR: @@ -1012,7 +1105,7 @@ bool BKE_library_idtype_can_use_idtype(const short id_type_owner, const short id case ID_MSK: return ELEM(id_type_used, ID_MC); /* WARNING! mask->parent.id, not typed. */ case ID_LS: - return (ELEM(id_type_used, ID_TE, ID_OB) || BKE_library_idtype_can_use_idtype(ID_NT, id_type_used)); + return (ELEM(id_type_used, ID_TE, ID_OB)); case ID_IM: case ID_VF: case ID_TXT: @@ -1118,7 +1211,7 @@ static bool library_ID_is_used(Main *bmain, void *idv, const bool check_linked) while (i-- && !is_defined) { ID *id_curr = lb_array[i]->first; - if (!id_curr || !BKE_library_idtype_can_use_idtype(GS(id_curr->name), GS(id->name))) { + if (!id_curr || !BKE_library_id_can_use_idtype(id_curr, GS(id->name))) { continue; } @@ -1170,7 +1263,7 @@ void BKE_library_ID_test_usages(Main *bmain, void *idv, bool *is_used_local, boo while (i-- && !is_defined) { ID *id_curr = lb_array[i]->first; - if (!id_curr || !BKE_library_idtype_can_use_idtype(GS(id_curr->name), GS(id->name))) { + if (!id_curr || !BKE_library_id_can_use_idtype(id_curr, GS(id->name))) { continue; } diff --git a/source/blender/blenkernel/intern/library_remap.c b/source/blender/blenkernel/intern/library_remap.c index b6f4621a0b3..d14e0cf0b65 100644 --- a/source/blender/blenkernel/intern/library_remap.c +++ b/source/blender/blenkernel/intern/library_remap.c @@ -448,20 +448,16 @@ ATTR_NONNULL(1) static void libblock_remap_data( * objects actually using given old_id... sounds rather unlikely currently, though, so this will do for now. */ while (i--) { - ID *id_curr = lb_array[i]->first; - - if (!id_curr || !BKE_library_idtype_can_use_idtype(GS(id_curr->name), GS(old_id->name))) { - continue; - } - - for (; id_curr; id_curr = id_curr->next) { - /* Note that we cannot skip indirect usages of old_id here (if requested), we still need to check it for - * the user count handling... - * XXX No more true (except for debug usage of those skipping counters). */ - r_id_remap_data->id = id_curr; - libblock_remap_data_preprocess(r_id_remap_data); - BKE_library_foreach_ID_link( - NULL, id_curr, foreach_libblock_remap_callback, (void *)r_id_remap_data, IDWALK_NOP); + for (ID *id_curr = lb_array[i]->first; id_curr; id_curr = id_curr->next) { + if (BKE_library_id_can_use_idtype(id_curr, GS(old_id->name))) { + /* Note that we cannot skip indirect usages of old_id here (if requested), we still need to check it for + * the user count handling... + * XXX No more true (except for debug usage of those skipping counters). */ + r_id_remap_data->id = id_curr; + libblock_remap_data_preprocess(r_id_remap_data); + BKE_library_foreach_ID_link( + NULL, id_curr, foreach_libblock_remap_callback, (void *)r_id_remap_data, IDWALK_NOP); + } } } } @@ -723,10 +719,10 @@ void BKE_libblock_relink_to_newid(ID *id) BKE_library_foreach_ID_link(NULL, id, id_relink_to_newid_looper, NULL, 0); } -void BKE_libblock_free_data(Main *UNUSED(bmain), ID *id) +void BKE_libblock_free_data(Main *UNUSED(bmain), ID *id, const bool do_id_user) { if (id->properties) { - IDP_FreeProperty(id->properties); + IDP_FreeProperty_ex(id->properties, do_id_user); MEM_freeN(id->properties); } } @@ -876,7 +872,7 @@ void BKE_libblock_free_ex(Main *bmain, void *idv, const bool do_id_user, const b BLI_remlink(lb, id); - BKE_libblock_free_data(bmain, id); + BKE_libblock_free_data(bmain, id, do_id_user); BKE_main_unlock(bmain); MEM_freeN(id); diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c index 3f3b4896653..f3223e31b17 100644 --- a/source/blender/blenkernel/intern/node.c +++ b/source/blender/blenkernel/intern/node.c @@ -1828,7 +1828,7 @@ void ntreeFreeTree(bNodeTree *ntree) if (tntree == ntree) break; if (tntree == NULL) { - BKE_libblock_free_data(G.main, &ntree->id); + BKE_libblock_free_data(G.main, &ntree->id, true); } } diff --git a/source/blender/blenkernel/intern/object_dupli.c b/source/blender/blenkernel/intern/object_dupli.c index e3b801b3193..eb7abc2f004 100644 --- a/source/blender/blenkernel/intern/object_dupli.c +++ b/source/blender/blenkernel/intern/object_dupli.c @@ -64,6 +64,7 @@ #include "BLI_strict_flags.h" +#include "BLI_hash.h" /* Dupli-Geometry */ @@ -180,6 +181,22 @@ static DupliObject *make_dupli(const DupliContext *ctx, if (ob->type == OB_MBALL) dob->no_draw = true; + /* random number */ + /* the logic here is designed to match Cycles */ + dob->random_id = BLI_hash_string(dob->ob->id.name + 2); + + if (dob->persistent_id[0] != INT_MAX) { + for(i = 0; i < MAX_DUPLI_RECUR*2; i++) + dob->random_id = BLI_hash_int_2d(dob->random_id, (unsigned int)dob->persistent_id[i]); + } + else { + dob->random_id = BLI_hash_int_2d(dob->random_id, 0); + } + + if (ctx->object != ob) { + dob->random_id ^= BLI_hash_int(BLI_hash_string(ctx->object->id.name + 2)); + } + return dob; } diff --git a/source/blender/blenlib/BLI_hash.h b/source/blender/blenlib/BLI_hash.h new file mode 100644 index 00000000000..551143f5d72 --- /dev/null +++ b/source/blender/blenlib/BLI_hash.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. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __BLI_HASH_H__ +#define __BLI_HASH_H__ + +/** \file BLI_hash.h + * \ingroup bli + */ + +static inline unsigned int BLI_hash_int_2d(unsigned int kx, unsigned int ky) +{ +#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k)))) + + unsigned int a, b, c; + + a = b = c = 0xdeadbeef + (2 << 2) + 13; + a += kx; + b += ky; + + c ^= b; c -= rot(b,14); + a ^= c; a -= rot(c,11); + b ^= a; b -= rot(a,25); + c ^= b; c -= rot(b,16); + a ^= c; a -= rot(c,4); + b ^= a; b -= rot(a,14); + c ^= b; c -= rot(b,24); + + return c; + +#undef rot +} + +static inline unsigned int BLI_hash_string(const char *str) +{ + unsigned int i = 0, c; + + while((c = *str++)) + i = i * 37 + c; + + return i; +} + +static inline unsigned int BLI_hash_int(unsigned int k) +{ + return BLI_hash_int_2d(k, 0); +} + +#endif // __BLI_HASH_H__ diff --git a/source/blender/blenlib/BLI_listbase.h b/source/blender/blenlib/BLI_listbase.h index 96349a7b066..00e761b81bc 100644 --- a/source/blender/blenlib/BLI_listbase.h +++ b/source/blender/blenlib/BLI_listbase.h @@ -67,6 +67,7 @@ void *BLI_poptail(ListBase *listbase) ATTR_NONNULL(1); void BLI_addhead(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1); void BLI_insertlinkbefore(struct ListBase *listbase, void *vnextlink, void *vnewlink) ATTR_NONNULL(1); void BLI_insertlinkafter(struct ListBase *listbase, void *vprevlink, void *vnewlink) ATTR_NONNULL(1); +void BLI_insertlinkreplace(ListBase *listbase, void *v_l_src, void *v_l_dst) ATTR_NONNULL(1, 2, 3); void BLI_listbase_sort(struct ListBase *listbase, int (*cmp)(const void *, const void *)) ATTR_NONNULL(1, 2); void BLI_listbase_sort_r(ListBase *listbase, int (*cmp)(void *, const void *, const void *), void *thunk) ATTR_NONNULL(1, 2); bool BLI_listbase_link_move(ListBase *listbase, void *vlink, int step) ATTR_NONNULL(); diff --git a/source/blender/blenlib/BLI_path_util.h b/source/blender/blenlib/BLI_path_util.h index baa1f792018..b6a55d34d14 100644 --- a/source/blender/blenlib/BLI_path_util.h +++ b/source/blender/blenlib/BLI_path_util.h @@ -39,14 +39,6 @@ extern "C" { struct ListBase; -#ifdef WIN32 -#define SEP '\\' -#define ALTSEP '/' -#else -#define SEP '/' -#define ALTSEP '\\' -#endif - void BLI_setenv(const char *env, const char *val) ATTR_NONNULL(1); void BLI_setenv_if_new(const char *env, const char *val) ATTR_NONNULL(1); @@ -60,7 +52,13 @@ void BLI_path_append(char *__restrict dst, const size_t maxlen, const char *__restrict file) ATTR_NONNULL(); void BLI_join_dirfile(char *__restrict string, const size_t maxlen, const char *__restrict dir, const char *__restrict file) ATTR_NONNULL(); +size_t BLI_path_join( + char *__restrict dst, const size_t dst_len, + const char *path_first, ...) ATTR_NONNULL(1, 3) ATTR_SENTINEL(0); const char *BLI_path_basename(const char *path) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT; +bool BLI_path_name_at_index( + const char *__restrict path, const int index, + int *__restrict r_offset, int *__restrict r_len) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT; #if 0 typedef enum bli_rebase_state { @@ -83,7 +81,6 @@ bool BLI_path_program_extensions_add_win32(char *name, const size_t maxlen); #endif bool BLI_path_program_search(char *fullname, const size_t maxlen, const char *name); -void BLI_getlastdir(const char *dir, char *last, const size_t maxlen); bool BLI_testextensie(const char *str, const char *ext) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT; bool BLI_testextensie_n(const char *str, ...) ATTR_NONNULL(1) ATTR_SENTINEL(0); bool BLI_testextensie_array(const char *str, const char **ext_array) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT; @@ -143,6 +140,18 @@ bool BLI_path_suffix(char *string, size_t maxlen, const char *suffix, const char # define FILE_MAX 1024 #endif +#ifdef WIN32 +# define SEP '\\' +# define ALTSEP '/' +# define SEP_STR "\\" +# define ALTSEP_STR "/" +#else +# define SEP '/' +# define ALTSEP '\\' +# define SEP_STR "/" +# define ALTSEP_STR "\\" +#endif + /* Parent and current dir helpers. */ #define FILENAME_PARENT ".." #define FILENAME_CURRENT "." diff --git a/source/blender/blenlib/intern/listbase.c b/source/blender/blenlib/intern/listbase.c index c9bf4976ae8..6cb7b7d8e3e 100644 --- a/source/blender/blenlib/intern/listbase.c +++ b/source/blender/blenlib/intern/listbase.c @@ -342,6 +342,40 @@ void BLI_insertlinkbefore(ListBase *listbase, void *vnextlink, void *vnewlink) } } + +/** + * Insert a link in place of another, without changing it's position in the list. + * + * Puts `vnewlink` in the position of `vreplacelink`, removing `vreplacelink`. + * - `vreplacelink` *must* be in the list. + * - `vnewlink` *must not* be in the list. + */ +void BLI_insertlinkreplace(ListBase *listbase, void *vreplacelink, void *vnewlink) +{ + Link *l_old = vreplacelink; + Link *l_new = vnewlink; + + /* update adjacent links */ + if (l_old->next != NULL) { + l_old->next->prev = l_new; + } + if (l_old->prev != NULL) { + l_old->prev->next = l_new; + } + + /* set direct links */ + l_new->next = l_old->next; + l_new->prev = l_old->prev; + + /* update list */ + if (listbase->first == l_old) { + listbase->first = l_new; + } + if (listbase->last == l_old) { + listbase->last = l_new; + } +} + /** * Reinsert \a vlink relative to its current position but offset by \a step. Doesn't move * item if new position would exceed list (could optionally move to head/tail). diff --git a/source/blender/blenlib/intern/path_util.c b/source/blender/blenlib/intern/path_util.c index 6644e6605a1..7b765cfa939 100644 --- a/source/blender/blenlib/intern/path_util.c +++ b/source/blender/blenlib/intern/path_util.c @@ -1167,30 +1167,6 @@ bool BLI_path_program_search( } /** - * Copies into *last the part of *dir following the second-last slash. - */ -void BLI_getlastdir(const char *dir, char *last, const size_t maxlen) -{ - const char *s = dir; - const char *lslash = NULL; - const char *prevslash = NULL; - while (*s) { - if ((*s == '\\') || (*s == '/')) { - prevslash = lslash; - lslash = s; - } - s++; - } - if (prevslash) { - BLI_strncpy(last, prevslash + 1, maxlen); - } - else { - BLI_strncpy(last, dir, maxlen); - } -} - - -/** * Sets the specified environment variable to the specified value, * and clears it if val == NULL. */ @@ -1615,6 +1591,90 @@ void BLI_join_dirfile(char *__restrict dst, const size_t maxlen, const char *__r } /** + * Join multiple strings into a path, ensuring only a single path separator between each, + * and trailing slash is kept. + * + * \note If you want a trailing slash, add ``SEP_STR`` as the last path argument, + * duplicate slashes will be cleaned up. + */ +size_t BLI_path_join(char *__restrict dst, const size_t dst_len, const char *path, ...) +{ + if (UNLIKELY(dst_len == 0)) { + return 0; + } + const size_t dst_last = dst_len - 1; + size_t ofs = BLI_strncpy_rlen(dst, path, dst_len); + + if (ofs == dst_last) { + return ofs; + } + + /* remove trailing slashes, unless there are _only_ trailing slashes + * (allow "//" as the first argument). */ + bool has_trailing_slash = false; + if (ofs != 0) { + size_t len = ofs; + while ((len != 0) && ELEM(path[len - 1], SEP, ALTSEP)) { + len -= 1; + } + if (len != 0) { + ofs = len; + } + has_trailing_slash = (path[len] != '\0'); + } + + va_list args; + va_start(args, path); + while ((path = (const char *) va_arg(args, const char *))) { + has_trailing_slash = false; + const char *path_init = path; + while (ELEM(path[0], SEP, ALTSEP)) { + path++; + } + size_t len = strlen(path); + if (len != 0) { + while ((len != 0) && ELEM(path[len - 1], SEP, ALTSEP)) { + len -= 1; + } + + if (len != 0) { + /* the very first path may have a slash at the end */ + if (ofs && !ELEM(dst[ofs - 1], SEP, ALTSEP)) { + dst[ofs++] = SEP; + if (ofs == dst_last) { + break; + } + } + has_trailing_slash = (path[len] != '\0'); + if (ofs + len >= dst_last) { + len = dst_last - ofs; + } + memcpy(&dst[ofs], path, len); + ofs += len; + if (ofs == dst_last) { + break; + } + } + } + else { + has_trailing_slash = (path_init != path); + } + } + va_end(args); + + if (has_trailing_slash) { + if ((ofs != dst_last) && (ofs != 0) && (ELEM(dst[ofs - 1], SEP, ALTSEP) == 0)) { + dst[ofs++] = SEP; + } + } + + BLI_assert(ofs <= dst_last); + dst[ofs] = '\0'; + + return ofs; +} + +/** * like pythons os.path.basename() * * \return The pointer into \a path string immediately after last slash, @@ -1626,6 +1686,71 @@ const char *BLI_path_basename(const char *path) return filename ? filename + 1 : path; } +/** + * Get an element of the path at an index, eg: + * "/some/path/file.txt" where an index of... + * - 0 or -3: "some" + * - 1 or -2: "path" + * - 2 or -1: "file.txt" + * + * Ignores multiple slashes at any point in the path (including start/end). + */ +bool BLI_path_name_at_index(const char *path, const int index, int *r_offset, int *r_len) +{ + if (index >= 0) { + int index_step = 0; + int prev = -1; + int i = 0; + while (true) { + const char c = path[i]; + if (ELEM(c, SEP, ALTSEP, '\0')) { + if (prev + 1 != i) { + prev += 1; + if (index_step == index) { + *r_offset = prev; + *r_len = i - prev; + /* printf("!!! %d %d\n", start, end); */ + return true; + } + index_step += 1; + } + if (c == '\0') { + break; + } + prev = i; + } + i += 1; + } + return false; + } + else { + /* negative number, reverse where -1 is the last element */ + int index_step = -1; + int prev = strlen(path); + int i = prev - 1; + while (true) { + const char c = i >= 0 ? path[i] : '\0'; + if (ELEM(c, SEP, ALTSEP, '\0')) { + if (prev - 1 != i) { + i += 1; + if (index_step == index) { + *r_offset = i; + *r_len = prev - i; + return true; + } + index_step -= 1; + } + if (c == '\0') { + break; + } + prev = i; + } + i -= 1; + } + return false; + } +} + /* UNUSED */ #if 0 /** diff --git a/source/blender/blenlib/intern/task.c b/source/blender/blenlib/intern/task.c index 17e20f8fa18..eb4e6e91aee 100644 --- a/source/blender/blenlib/intern/task.c +++ b/source/blender/blenlib/intern/task.c @@ -162,6 +162,16 @@ struct TaskPool { */ int thread_id; + /* For the pools which are created from non-main thread which is not a + * scheduler worker thread we can't re-use any of scheduler's threads TLS + * and have to use our own one. + */ + bool use_local_tls; + TaskThreadLocalStorage local_tls; +#ifndef NDEBUG + pthread_t creator_thread_id; +#endif + #ifdef DEBUG_STATS TaskMemPoolStats *mempool_stats; #endif @@ -202,13 +212,25 @@ BLI_INLINE void task_data_free(Task *task, const int thread_id) } } +BLI_INLINE void initialize_task_tls(TaskThreadLocalStorage *tls) +{ + memset(tls, 0, sizeof(TaskThreadLocalStorage)); +} + BLI_INLINE TaskThreadLocalStorage *get_task_tls(TaskPool *pool, const int thread_id) { TaskScheduler *scheduler = pool->scheduler; BLI_assert(thread_id >= 0); BLI_assert(thread_id <= scheduler->num_threads); + if (pool->use_local_tls && thread_id == 0) { + BLI_assert(pool->thread_id == 0); + BLI_assert(!BLI_thread_is_main()); + BLI_assert(pthread_equal(pthread_self(), pool->creator_thread_id)); + return &pool->local_tls; + } if (thread_id == 0) { + BLI_assert(BLI_thread_is_main()); return &scheduler->task_threads[pool->thread_id].tls; } return &scheduler->task_threads[thread_id].tls; @@ -252,6 +274,9 @@ static void task_free(TaskPool *pool, Task *task, const int thread_id) task_data_free(task, thread_id); BLI_assert(thread_id >= 0); BLI_assert(thread_id <= pool->scheduler->num_threads); + if (thread_id == 0) { + BLI_assert(pool->use_local_tls || BLI_thread_is_main()); + } TaskThreadLocalStorage *tls = get_task_tls(pool, thread_id); TaskMemPool *task_mempool = &tls->task_mempool; if (task_mempool->num_tasks < MEMPOOL_SIZE - 1) { @@ -424,9 +449,12 @@ TaskScheduler *BLI_task_scheduler_create(int num_threads) num_threads = 1; } - scheduler->task_threads = MEM_callocN(sizeof(TaskThread) * (num_threads + 1), + scheduler->task_threads = MEM_mallocN(sizeof(TaskThread) * (num_threads + 1), "TaskScheduler task threads"); + /* Initialize TLS for main thread. */ + initialize_task_tls(&scheduler->task_threads[0].tls); + pthread_key_create(&scheduler->tls_id_key, NULL); /* launch threads that will be waiting for work */ @@ -440,6 +468,7 @@ TaskScheduler *BLI_task_scheduler_create(int num_threads) TaskThread *thread = &scheduler->task_threads[i + 1]; thread->scheduler = scheduler; thread->id = i + 1; + initialize_task_tls(&thread->tls); if (pthread_create(&scheduler->threads[i], NULL, task_scheduler_thread_run, thread) != 0) { fprintf(stderr, "TaskScheduler failed to launch thread %d/%d\n", i, num_threads); @@ -572,6 +601,7 @@ static TaskPool *task_pool_create_ex(TaskScheduler *scheduler, pool->num_suspended = 0; pool->suspended_queue.first = pool->suspended_queue.last = NULL; pool->run_in_background = is_background; + pool->use_local_tls = false; BLI_mutex_init(&pool->num_mutex); BLI_condition_init(&pool->num_cond); @@ -584,13 +614,18 @@ static TaskPool *task_pool_create_ex(TaskScheduler *scheduler, } else { TaskThread *thread = pthread_getspecific(scheduler->tls_id_key); - /* NOTE: It is possible that pool is created from non-main thread - * which isn't a scheduler thread. In this case pthread's TLS will - * be NULL and we can safely consider thread id 0 for the main - * thread of this pool (the one which does wort_and_wait()). - */ if (thread == NULL) { + /* NOTE: Task pool is created from non-main thread which is not + * managed by the task scheduler. We identify ourselves as thread ID + * 0 but we do not use scheduler's TLS storage and use our own + * instead to avoid any possible threading conflicts. + */ pool->thread_id = 0; + pool->use_local_tls = true; +#ifndef NDEBUG + pool->creator_thread_id = pthread_self(); +#endif + initialize_task_tls(&pool->local_tls); } else { pool->thread_id = thread->id; @@ -670,6 +705,10 @@ void BLI_task_pool_free(TaskPool *pool) MEM_freeN(pool->mempool_stats); #endif + if (pool->use_local_tls) { + free_task_tls(&pool->local_tls); + } + MEM_freeN(pool); BLI_end_threaded_malloc(); @@ -743,9 +782,7 @@ void BLI_task_pool_work_and_wait(TaskPool *pool) BLI_condition_notify_all(&scheduler->queue_cond); BLI_mutex_unlock(&scheduler->queue_mutex); - } - pool->is_suspended = false; } pool->do_work = true; diff --git a/source/blender/blenloader/CMakeLists.txt b/source/blender/blenloader/CMakeLists.txt index 8cb9ef837b2..3d2e8a306de 100644 --- a/source/blender/blenloader/CMakeLists.txt +++ b/source/blender/blenloader/CMakeLists.txt @@ -69,6 +69,12 @@ if(WITH_BUILDINFO) add_definitions(-DWITH_BUILDINFO) endif() +if(WITH_PYTHON) + if(WITH_PYTHON_SECURITY) + add_definitions(-DWITH_PYTHON_SECURITY) + endif() +endif() + if(WITH_INTERNATIONAL) add_definitions(-DWITH_INTERNATIONAL) endif() diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index fb80585dc2a..2900f9e4fc4 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -129,6 +129,7 @@ #include "BKE_library_idmap.h" #include "BKE_library_query.h" #include "BKE_idcode.h" +#include "BKE_idprop.h" #include "BKE_material.h" #include "BKE_main.h" // for Main #include "BKE_mesh.h" // for ME_ defines (patching) @@ -2001,7 +2002,7 @@ static void test_pointer_array(FileData *fd, void **mat) /* ************ READ ID Properties *************** */ static void IDP_DirectLinkProperty(IDProperty *prop, int switch_endian, FileData *fd); -static void IDP_LibLinkProperty(IDProperty *prop, int switch_endian, FileData *fd); +static void IDP_LibLinkProperty(IDProperty *prop, FileData *fd); static void IDP_DirectLinkIDPArray(IDProperty *prop, int switch_endian, FileData *fd) { @@ -2132,10 +2133,39 @@ static void _IDP_DirectLinkGroup_OrFree(IDProperty **prop, int switch_endian, Fi } } -/* stub function */ -static void IDP_LibLinkProperty(IDProperty *UNUSED(prop), int UNUSED(switch_endian), FileData *UNUSED(fd)) +static void IDP_LibLinkProperty(IDProperty *prop, FileData *fd) { - /* Should we do something here, prop should be ensured to be non-NULL first... */ + if (!prop) + return; + + switch (prop->type) { + case IDP_ID: /* PointerProperty */ + { + void *newaddr = newlibadr_us(fd, NULL, IDP_Id(prop)); + if (IDP_Id(prop) && !newaddr && G.debug) { + printf("Error while loading \"%s\". Data not found in file!\n", prop->name); + } + prop->data.pointer = newaddr; + break; + } + case IDP_IDPARRAY: /* CollectionProperty */ + { + IDProperty *idp_array = IDP_IDPArray(prop); + for (int i = 0; i < prop->len; i++) { + IDP_LibLinkProperty(&(idp_array[i]), fd); + } + break; + } + case IDP_GROUP: /* PointerProperty */ + { + for (IDProperty *loop = prop->data.group.first; loop; loop = loop->next) { + IDP_LibLinkProperty(loop, fd); + } + break; + } + default: + break; /* Nothing to do for other IDProps. */ + } } /* ************ READ IMAGE PREVIEW *************** */ @@ -2192,19 +2222,19 @@ static void direct_link_curvemapping(FileData *fd, CurveMapping *cumap) /* library brush linking after fileread */ static void lib_link_brush(FileData *fd, Main *main) { - Brush *brush; - /* only link ID pointers */ - for (brush = main->brush.first; brush; brush = brush->id.next) { + for (Brush *brush = main->brush.first; brush; brush = brush->id.next) { if (brush->id.tag & LIB_TAG_NEED_LINK) { - brush->id.tag &= ~LIB_TAG_NEED_LINK; - + IDP_LibLinkProperty(brush->id.properties, fd); + /* brush->(mask_)mtex.obj is ignored on purpose? */ brush->mtex.tex = newlibadr_us(fd, brush->id.lib, brush->mtex.tex); brush->mask_mtex.tex = newlibadr_us(fd, brush->id.lib, brush->mask_mtex.tex); brush->clone.image = newlibadr(fd, brush->id.lib, brush->clone.image); brush->toggle_brush = newlibadr(fd, brush->id.lib, brush->toggle_brush); brush->paint_curve = newlibadr_us(fd, brush->id.lib, brush->paint_curve); + + brush->id.tag &= ~LIB_TAG_NEED_LINK; } } } @@ -2227,13 +2257,13 @@ static void direct_link_brush(FileData *fd, Brush *brush) } /* ************ READ Palette *************** */ -static void lib_link_palette(FileData *UNUSED(fd), Main *main) +static void lib_link_palette(FileData *fd, Main *main) { - Palette *palette; - /* only link ID pointers */ - for (palette = main->palettes.first; palette; palette = palette->id.next) { + for (Palette *palette = main->palettes.first; palette; palette = palette->id.next) { if (palette->id.tag & LIB_TAG_NEED_LINK) { + IDP_LibLinkProperty(palette->id.properties, fd); + palette->id.tag &= ~LIB_TAG_NEED_LINK; } } @@ -2245,13 +2275,13 @@ static void direct_link_palette(FileData *fd, Palette *palette) link_list(fd, &palette->colors); } -static void lib_link_paint_curve(FileData *UNUSED(fd), Main *main) +static void lib_link_paint_curve(FileData *fd, Main *main) { - PaintCurve *pc; - /* only link ID pointers */ - for (pc = main->paintcurves.first; pc; pc = pc->id.next) { + for (PaintCurve *pc = main->paintcurves.first; pc; pc = pc->id.next) { if (pc->id.tag & LIB_TAG_NEED_LINK) { + IDP_LibLinkProperty(pc->id.properties, fd); + pc->id.tag &= ~LIB_TAG_NEED_LINK; } } @@ -2503,15 +2533,12 @@ static void direct_link_fcurves(FileData *fd, ListBase *list) static void lib_link_action(FileData *fd, Main *main) { - bAction *act; - bActionChannel *chan; - - for (act = main->action.first; act; act = act->id.next) { + for (bAction *act = main->action.first; act; act = act->id.next) { if (act->id.tag & LIB_TAG_NEED_LINK) { - act->id.tag &= ~LIB_TAG_NEED_LINK; + IDP_LibLinkProperty(act->id.properties, fd); // XXX deprecated - old animation system <<< - for (chan=act->chanbase.first; chan; chan=chan->next) { + for (bActionChannel *chan = act->chanbase.first; chan; chan = chan->next) { chan->ipo = newlibadr_us(fd, act->id.lib, chan->ipo); lib_link_constraint_channels(fd, &act->id, &chan->constraintChannels); } @@ -2524,6 +2551,8 @@ static void lib_link_action(FileData *fd, Main *main) marker->camera = newlibadr(fd, act->id.lib, marker->camera); } } + + act->id.tag &= ~LIB_TAG_NEED_LINK; } } } @@ -2710,26 +2739,20 @@ static void direct_link_animdata(FileData *fd, AnimData *adt) static void lib_link_cachefiles(FileData *fd, Main *bmain) { - CacheFile *cache_file; - /* only link ID pointers */ - for (cache_file = bmain->cachefiles.first; cache_file; cache_file = cache_file->id.next) { + for (CacheFile *cache_file = bmain->cachefiles.first; cache_file; cache_file = cache_file->id.next) { if (cache_file->id.tag & LIB_TAG_NEED_LINK) { - cache_file->id.tag &= ~LIB_TAG_NEED_LINK; - } - - BLI_listbase_clear(&cache_file->object_paths); - cache_file->handle = NULL; - cache_file->handle_mutex = NULL; - - if (cache_file->adt) { + IDP_LibLinkProperty(cache_file->id.properties, fd); lib_link_animdata(fd, &cache_file->id, cache_file->adt); + + cache_file->id.tag &= ~LIB_TAG_NEED_LINK; } } } static void direct_link_cachefile(FileData *fd, CacheFile *cache_file) { + BLI_listbase_clear(&cache_file->object_paths); cache_file->handle = NULL; cache_file->handle_mutex = NULL; @@ -2753,19 +2776,13 @@ static void direct_link_motionpath(FileData *fd, bMotionPath *mpath) /* ************ READ NODE TREE *************** */ -static void lib_link_node_socket(FileData *fd, ID *UNUSED(id), bNodeSocket *sock) -{ - /* Link ID Properties -- and copy this comment EXACTLY for easy finding - * of library blocks that implement this.*/ - IDP_LibLinkProperty(sock->prop, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd); -} - /* Single node tree (also used for material/scene trees), ntree is not NULL */ static void lib_link_ntree(FileData *fd, ID *id, bNodeTree *ntree) { bNode *node; bNodeSocket *sock; + IDP_LibLinkProperty(ntree->id.properties, fd); lib_link_animdata(fd, &ntree->id, ntree->adt); ntree->gpd = newlibadr_us(fd, id->lib, ntree->gpd); @@ -2773,32 +2790,35 @@ static void lib_link_ntree(FileData *fd, ID *id, bNodeTree *ntree) for (node = ntree->nodes.first; node; node = node->next) { /* Link ID Properties -- and copy this comment EXACTLY for easy finding * of library blocks that implement this.*/ - IDP_LibLinkProperty(node->prop, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd); + IDP_LibLinkProperty(node->prop, fd); - node->id= newlibadr_us(fd, id->lib, node->id); + node->id = newlibadr_us(fd, id->lib, node->id); - for (sock = node->inputs.first; sock; sock = sock->next) - lib_link_node_socket(fd, id, sock); - for (sock = node->outputs.first; sock; sock = sock->next) - lib_link_node_socket(fd, id, sock); + for (sock = node->inputs.first; sock; sock = sock->next) { + IDP_LibLinkProperty(sock->prop, fd); + } + for (sock = node->outputs.first; sock; sock = sock->next) { + IDP_LibLinkProperty(sock->prop, fd); + } } - for (sock = ntree->inputs.first; sock; sock = sock->next) - lib_link_node_socket(fd, id, sock); - for (sock = ntree->outputs.first; sock; sock = sock->next) - lib_link_node_socket(fd, id, sock); + for (sock = ntree->inputs.first; sock; sock = sock->next) { + IDP_LibLinkProperty(sock->prop, fd); + } + for (sock = ntree->outputs.first; sock; sock = sock->next) { + IDP_LibLinkProperty(sock->prop, fd); + } } /* library ntree linking after fileread */ static void lib_link_nodetree(FileData *fd, Main *main) { - bNodeTree *ntree; - /* only link ID pointers */ - for (ntree = main->nodetree.first; ntree; ntree = ntree->id.next) { + for (bNodeTree *ntree = main->nodetree.first; ntree; ntree = ntree->id.next) { if (ntree->id.tag & LIB_TAG_NEED_LINK) { - ntree->id.tag &= ~LIB_TAG_NEED_LINK; lib_link_ntree(fd, &ntree->id, ntree); + + ntree->id.tag &= ~LIB_TAG_NEED_LINK; } } } @@ -3291,6 +3311,8 @@ static void lib_link_pose(FileData *fd, Main *bmain, Object *ob, bPose *pose) pchan->bone = BLI_ghash_lookup(bone_hash, pchan->name); + IDP_LibLinkProperty(pchan->prop, fd); + pchan->custom = newlibadr_us(fd, arm->id.lib, pchan->custom); if (UNLIKELY(pchan->bone == NULL)) { rebuild = true; @@ -3311,13 +3333,26 @@ static void lib_link_pose(FileData *fd, Main *bmain, Object *ob, bPose *pose) } } +static void lib_link_bones(FileData *fd, Bone *bone) +{ + IDP_LibLinkProperty(bone->prop, fd); + + for (Bone *curbone = bone->childbase.first; curbone; curbone = curbone->next) { + lib_link_bones(fd, curbone); + } +} + static void lib_link_armature(FileData *fd, Main *main) { - bArmature *arm; - - for (arm = main->armature.first; arm; arm = arm->id.next) { + for (bArmature *arm = main->armature.first; arm; arm = arm->id.next) { if (arm->id.tag & LIB_TAG_NEED_LINK) { + IDP_LibLinkProperty(arm->id.properties, fd); lib_link_animdata(fd, &arm->id, arm->adt); + + for (Bone *curbone = arm->bonebase.first; curbone; curbone = curbone->next) { + lib_link_bones(fd, curbone); + } + arm->id.tag &= ~LIB_TAG_NEED_LINK; } } @@ -3362,14 +3397,13 @@ static void direct_link_armature(FileData *fd, bArmature *arm) static void lib_link_camera(FileData *fd, Main *main) { - Camera *ca; - - for (ca = main->camera.first; ca; ca = ca->id.next) { + for (Camera *ca = main->camera.first; ca; ca = ca->id.next) { if (ca->id.tag & LIB_TAG_NEED_LINK) { + IDP_LibLinkProperty(ca->id.properties, fd); lib_link_animdata(fd, &ca->id, ca->adt); ca->ipo = newlibadr_us(fd, ca->id.lib, ca->ipo); // XXX deprecated - old animation system - + ca->dof_ob = newlibadr(fd, ca->id.lib, ca->dof_ob); ca->id.tag &= ~LIB_TAG_NEED_LINK; @@ -3388,16 +3422,13 @@ static void direct_link_camera(FileData *fd, Camera *ca) static void lib_link_lamp(FileData *fd, Main *main) { - Lamp *la; - MTex *mtex; - int a; - - for (la = main->lamp.first; la; la = la->id.next) { + for (Lamp *la = main->lamp.first; la; la = la->id.next) { if (la->id.tag & LIB_TAG_NEED_LINK) { + IDP_LibLinkProperty(la->id.properties, fd); lib_link_animdata(fd, &la->id, la->adt); - for (a = 0; a < MAX_MTEX; a++) { - mtex = la->mtex[a]; + for (int a = 0; a < MAX_MTEX; a++) { + MTex *mtex = la->mtex[a]; if (mtex) { mtex->tex = newlibadr_us(fd, la->id.lib, mtex->tex); mtex->object = newlibadr(fd, la->id.lib, mtex->object); @@ -3454,17 +3485,11 @@ void blo_do_versions_key_uidgen(Key *key) static void lib_link_key(FileData *fd, Main *main) { - Key *key; - - for (key = main->key.first; key; key = key->id.next) { - /*check if we need to generate unique ids for the shapekeys*/ - if (!key->uidgen) { - blo_do_versions_key_uidgen(key); - } - + for (Key *key = main->key.first; key; key = key->id.next) { BLI_assert((key->id.tag & LIB_TAG_EXTERN) == 0); if (key->id.tag & LIB_TAG_NEED_LINK) { + IDP_LibLinkProperty(key->id.properties, fd); lib_link_animdata(fd, &key->id, key->adt); key->ipo = newlibadr_us(fd, key->id.lib, key->ipo); // XXX deprecated - old animation system @@ -3527,15 +3552,14 @@ static void direct_link_key(FileData *fd, Key *key) static void lib_link_mball(FileData *fd, Main *main) { - MetaBall *mb; - int a; - - for (mb = main->mball.first; mb; mb = mb->id.next) { + for (MetaBall *mb = main->mball.first; mb; mb = mb->id.next) { if (mb->id.tag & LIB_TAG_NEED_LINK) { + IDP_LibLinkProperty(mb->id.properties, fd); lib_link_animdata(fd, &mb->id, mb->adt); - for (a = 0; a < mb->totcol; a++) + for (int a = 0; a < mb->totcol; a++) { mb->mat[a] = newlibadr_us(fd, mb->id.lib, mb->mat[a]); + } mb->ipo = newlibadr_us(fd, mb->id.lib, mb->ipo); // XXX deprecated - old animation system @@ -3564,18 +3588,15 @@ static void direct_link_mball(FileData *fd, MetaBall *mb) static void lib_link_world(FileData *fd, Main *main) { - World *wrld; - MTex *mtex; - int a; - - for (wrld = main->world.first; wrld; wrld = wrld->id.next) { + for (World *wrld = main->world.first; wrld; wrld = wrld->id.next) { if (wrld->id.tag & LIB_TAG_NEED_LINK) { + IDP_LibLinkProperty(wrld->id.properties, fd); lib_link_animdata(fd, &wrld->id, wrld->adt); wrld->ipo = newlibadr_us(fd, wrld->id.lib, wrld->ipo); // XXX deprecated - old animation system - for (a=0; a < MAX_MTEX; a++) { - mtex = wrld->mtex[a]; + for (int a = 0; a < MAX_MTEX; a++) { + MTex *mtex = wrld->mtex[a]; if (mtex) { mtex->tex = newlibadr_us(fd, wrld->id.lib, mtex->tex); mtex->object = newlibadr(fd, wrld->id.lib, mtex->object); @@ -3616,12 +3637,12 @@ static void direct_link_world(FileData *fd, World *wrld) /* ************ READ VFONT ***************** */ -static void lib_link_vfont(FileData *UNUSED(fd), Main *main) +static void lib_link_vfont(FileData *fd, Main *main) { - VFont *vf; - - for (vf = main->vfont.first; vf; vf = vf->id.next) { + for (VFont *vf = main->vfont.first; vf; vf = vf->id.next) { if (vf->id.tag & LIB_TAG_NEED_LINK) { + IDP_LibLinkProperty(vf->id.properties, fd); + vf->id.tag &= ~LIB_TAG_NEED_LINK; } } @@ -3636,12 +3657,12 @@ static void direct_link_vfont(FileData *fd, VFont *vf) /* ************ READ TEXT ****************** */ -static void lib_link_text(FileData *UNUSED(fd), Main *main) +static void lib_link_text(FileData *fd, Main *main) { - Text *text; - - for (text = main->text.first; text; text = text->id.next) { + for (Text *text = main->text.first; text; text = text->id.next) { if (text->id.tag & LIB_TAG_NEED_LINK) { + IDP_LibLinkProperty(text->id.properties, fd); + text->id.tag &= ~LIB_TAG_NEED_LINK; } } @@ -3690,11 +3711,9 @@ static void direct_link_text(FileData *fd, Text *text) static void lib_link_image(FileData *fd, Main *main) { - Image *ima; - - for (ima = main->image.first; ima; ima = ima->id.next) { + for (Image *ima = main->image.first; ima; ima = ima->id.next) { if (ima->id.tag & LIB_TAG_NEED_LINK) { - IDP_LibLinkProperty(ima->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd); + IDP_LibLinkProperty(ima->id.properties, fd); ima->id.tag &= ~LIB_TAG_NEED_LINK; } @@ -3759,15 +3778,14 @@ static void direct_link_image(FileData *fd, Image *ima) static void lib_link_curve(FileData *fd, Main *main) { - Curve *cu; - int a; - - for (cu = main->curve.first; cu; cu = cu->id.next) { + for (Curve *cu = main->curve.first; cu; cu = cu->id.next) { if (cu->id.tag & LIB_TAG_NEED_LINK) { + IDP_LibLinkProperty(cu->id.properties, fd); lib_link_animdata(fd, &cu->id, cu->adt); - for (a = 0; a < cu->totcol; a++) + for (int a = 0; a < cu->totcol; a++) { cu->mat[a] = newlibadr_us(fd, cu->id.lib, cu->mat[a]); + } cu->bevobj = newlibadr(fd, cu->id.lib, cu->bevobj); cu->taperobj = newlibadr(fd, cu->id.lib, cu->taperobj); @@ -3852,10 +3870,9 @@ static void direct_link_curve(FileData *fd, Curve *cu) static void lib_link_texture(FileData *fd, Main *main) { - Tex *tex; - - for (tex = main->tex.first; tex; tex = tex->id.next) { + for (Tex *tex = main->tex.first; tex; tex = tex->id.next) { if (tex->id.tag & LIB_TAG_NEED_LINK) { + IDP_LibLinkProperty(tex->id.properties, fd); lib_link_animdata(fd, &tex->id, tex->adt); tex->ima = newlibadr_us(fd, tex->id.lib, tex->ima); @@ -3931,23 +3948,20 @@ static void direct_link_texture(FileData *fd, Tex *tex) static void lib_link_material(FileData *fd, Main *main) { - Material *ma; - MTex *mtex; - int a; - - for (ma = main->mat.first; ma; ma = ma->id.next) { + for (Material *ma = main->mat.first; ma; ma = ma->id.next) { if (ma->id.tag & LIB_TAG_NEED_LINK) { + IDP_LibLinkProperty(ma->id.properties, fd); lib_link_animdata(fd, &ma->id, ma->adt); /* Link ID Properties -- and copy this comment EXACTLY for easy finding * of library blocks that implement this.*/ - IDP_LibLinkProperty(ma->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd); + IDP_LibLinkProperty(ma->id.properties, fd); ma->ipo = newlibadr_us(fd, ma->id.lib, ma->ipo); // XXX deprecated - old animation system ma->group = newlibadr_us(fd, ma->id.lib, ma->group); - for (a = 0; a < MAX_MTEX; a++) { - mtex = ma->mtex[a]; + for (int a = 0; a < MAX_MTEX; a++) { + MTex *mtex = ma->mtex[a]; if (mtex) { mtex->tex = newlibadr_us(fd, ma->id.lib, mtex->tex); mtex->object = newlibadr(fd, ma->id.lib, mtex->object); @@ -4078,14 +4092,11 @@ static void lib_link_partdeflect(FileData *fd, ID *id, PartDeflect *pd) static void lib_link_particlesettings(FileData *fd, Main *main) { - ParticleSettings *part; - ParticleDupliWeight *dw; - MTex *mtex; - int a; - - for (part = main->particle.first; part; part = part->id.next) { + for (ParticleSettings *part = main->particle.first; part; part = part->id.next) { if (part->id.tag & LIB_TAG_NEED_LINK) { + IDP_LibLinkProperty(part->id.properties, fd); lib_link_animdata(fd, &part->id, part->adt); + part->ipo = newlibadr_us(fd, part->id.lib, part->ipo); // XXX deprecated - old animation system part->dup_ob = newlibadr(fd, part->id.lib, part->dup_ob); @@ -4105,6 +4116,7 @@ static void lib_link_particlesettings(FileData *fd, Main *main) } if (part->dupliweights.first && part->dup_group) { + ParticleDupliWeight *dw; int index_ok = 0; /* check for old files without indices (all indexes 0) */ if (BLI_listbase_is_single(&part->dupliweights)) { @@ -4169,8 +4181,8 @@ static void lib_link_particlesettings(FileData *fd, Main *main) } } - for (a = 0; a < MAX_MTEX; a++) { - mtex= part->mtex[a]; + for (int a = 0; a < MAX_MTEX; a++) { + MTex *mtex= part->mtex[a]; if (mtex) { mtex->tex = newlibadr_us(fd, part->id.lib, mtex->tex); mtex->object = newlibadr(fd, part->id.lib, mtex->object); @@ -4404,7 +4416,7 @@ static void lib_link_mesh(FileData *fd, Main *main) /* Link ID Properties -- and copy this comment EXACTLY for easy finding * of library blocks that implement this.*/ - IDP_LibLinkProperty(me->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd); + IDP_LibLinkProperty(me->id.properties, fd); lib_link_animdata(fd, &me->id, me->adt); /* this check added for python created meshes */ @@ -4677,10 +4689,9 @@ static void direct_link_mesh(FileData *fd, Mesh *mesh) static void lib_link_latt(FileData *fd, Main *main) { - Lattice *lt; - - for (lt = main->latt.first; lt; lt = lt->id.next) { + for (Lattice *lt = main->latt.first; lt; lt = lt->id.next) { if (lt->id.tag & LIB_TAG_NEED_LINK) { + IDP_LibLinkProperty(lt->id.properties, fd); lib_link_animdata(fd, <->id, lt->adt); lt->ipo = newlibadr_us(fd, lt->id.lib, lt->ipo); // XXX deprecated - old animation system @@ -4724,17 +4735,13 @@ static void lib_link_modifiers(FileData *fd, Object *ob) static void lib_link_object(FileData *fd, Main *main) { - Object *ob; - PartEff *paf; - bSensor *sens; - bController *cont; - bActuator *act; - void *poin; - int warn=0, a; - - for (ob = main->object.first; ob; ob = ob->id.next) { + bool warn = false; + + for (Object *ob = main->object.first; ob; ob = ob->id.next) { if (ob->id.tag & LIB_TAG_NEED_LINK) { - IDP_LibLinkProperty(ob->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd); + int a; + + IDP_LibLinkProperty(ob->id.properties, fd); lib_link_animdata(fd, &ob->id, ob->adt); // XXX deprecated - old animation system <<< @@ -4766,17 +4773,17 @@ static void lib_link_object(FileData *fd, Main *main) } ob->proxy_group = newlibadr(fd, ob->id.lib, ob->proxy_group); - poin = ob->data; + void *poin = ob->data; ob->data = newlibadr_us(fd, ob->id.lib, ob->data); - if (ob->data==NULL && poin!=NULL) { + if (ob->data == NULL && poin != NULL) { if (ob->id.lib) printf("Can't find obdata of %s lib %s\n", ob->id.name + 2, ob->id.lib->name); else printf("Object %s lost data.\n", ob->id.name + 2); ob->type = OB_EMPTY; - warn = 1; + warn = true; if (ob->pose) { /* we can't call #BKE_pose_free() here because of library linking @@ -4822,13 +4829,13 @@ static void lib_link_object(FileData *fd, Main *main) lib_link_nlastrips(fd, &ob->id, &ob->nlastrips); // >>> XXX deprecated - old animation system - for (paf = ob->effect.first; paf; paf = paf->next) { + for (PartEff *paf = ob->effect.first; paf; paf = paf->next) { if (paf->type == EFF_PARTICLE) { paf->group = newlibadr_us(fd, ob->id.lib, paf->group); } } - for (sens = ob->sensors.first; sens; sens = sens->next) { + for (bSensor *sens = ob->sensors.first; sens; sens = sens->next) { for (a = 0; a < sens->totlinks; a++) sens->links[a] = newglobadr(fd, sens->links[a]); @@ -4839,7 +4846,7 @@ static void lib_link_object(FileData *fd, Main *main) } } - for (cont = ob->controllers.first; cont; cont = cont->next) { + for (bController *cont = ob->controllers.first; cont; cont = cont->next) { for (a=0; a < cont->totlinks; a++) cont->links[a] = newglobadr(fd, cont->links[a]); @@ -4851,86 +4858,117 @@ static void lib_link_object(FileData *fd, Main *main) cont->totslinks = 0; } - for (act = ob->actuators.first; act; act = act->next) { - if (act->type == ACT_SOUND) { - bSoundActuator *sa = act->data; - sa->sound= newlibadr_us(fd, ob->id.lib, sa->sound); - } - else if (act->type == ACT_GAME) { - /* bGameActuator *ga= act->data; */ - } - else if (act->type == ACT_CAMERA) { - bCameraActuator *ca = act->data; - ca->ob= newlibadr(fd, ob->id.lib, ca->ob); - } - /* leave this one, it's obsolete but necessary to read for conversion */ - else if (act->type == ACT_ADD_OBJECT) { - bAddObjectActuator *eoa = act->data; - if (eoa) eoa->ob= newlibadr(fd, ob->id.lib, eoa->ob); - } - else if (act->type == ACT_OBJECT) { - bObjectActuator *oa = act->data; - if (oa == NULL) { - init_actuator(act); + for (bActuator *act = ob->actuators.first; act; act = act->next) { + switch (act->type) { + case ACT_SOUND: + { + bSoundActuator *sa = act->data; + sa->sound = newlibadr_us(fd, ob->id.lib, sa->sound); + break; } - else { - oa->reference = newlibadr(fd, ob->id.lib, oa->reference); + case ACT_GAME: + /* bGameActuator *ga= act->data; */ + break; + case ACT_CAMERA: + { + bCameraActuator *ca = act->data; + ca->ob = newlibadr(fd, ob->id.lib, ca->ob); + break; } - } - else if (act->type == ACT_EDIT_OBJECT) { - bEditObjectActuator *eoa = act->data; - if (eoa == NULL) { - init_actuator(act); + /* leave this one, it's obsolete but necessary to read for conversion */ + case ACT_ADD_OBJECT: + { + bAddObjectActuator *eoa = act->data; + if (eoa) + eoa->ob = newlibadr(fd, ob->id.lib, eoa->ob); + break; } - else { - eoa->ob= newlibadr(fd, ob->id.lib, eoa->ob); - eoa->me= newlibadr(fd, ob->id.lib, eoa->me); + case ACT_OBJECT: + { + bObjectActuator *oa = act->data; + if (oa == NULL) { + init_actuator(act); + } + else { + oa->reference = newlibadr(fd, ob->id.lib, oa->reference); + } + break; } - } - else if (act->type == ACT_SCENE) { - bSceneActuator *sa = act->data; - sa->camera= newlibadr(fd, ob->id.lib, sa->camera); - sa->scene= newlibadr(fd, ob->id.lib, sa->scene); - } - else if (act->type == ACT_ACTION) { - bActionActuator *aa = act->data; - aa->act= newlibadr_us(fd, ob->id.lib, aa->act); - } - else if (act->type == ACT_SHAPEACTION) { - bActionActuator *aa = act->data; - aa->act= newlibadr_us(fd, ob->id.lib, aa->act); - } - else if (act->type == ACT_PROPERTY) { - bPropertyActuator *pa = act->data; - pa->ob= newlibadr(fd, ob->id.lib, pa->ob); - } - else if (act->type == ACT_MESSAGE) { - bMessageActuator *ma = act->data; - ma->toObject= newlibadr(fd, ob->id.lib, ma->toObject); - } - else if (act->type == ACT_2DFILTER) { - bTwoDFilterActuator *_2dfa = act->data; - _2dfa->text= newlibadr(fd, ob->id.lib, _2dfa->text); - } - else if (act->type == ACT_PARENT) { - bParentActuator *parenta = act->data; - parenta->ob = newlibadr(fd, ob->id.lib, parenta->ob); - } - else if (act->type == ACT_STATE) { - /* bStateActuator *statea = act->data; */ - } - else if (act->type == ACT_ARMATURE) { - bArmatureActuator *arma= act->data; - arma->target= newlibadr(fd, ob->id.lib, arma->target); - arma->subtarget= newlibadr(fd, ob->id.lib, arma->subtarget); - } - else if (act->type == ACT_STEERING) { - bSteeringActuator *steeringa = act->data; - steeringa->target = newlibadr(fd, ob->id.lib, steeringa->target); - steeringa->navmesh = newlibadr(fd, ob->id.lib, steeringa->navmesh); - } - else if (act->type == ACT_MOUSE) { - /* bMouseActuator *moa= act->data; */ + case ACT_EDIT_OBJECT: + { + bEditObjectActuator *eoa = act->data; + if (eoa == NULL) { + init_actuator(act); + } + else { + eoa->ob = newlibadr(fd, ob->id.lib, eoa->ob); + eoa->me = newlibadr(fd, ob->id.lib, eoa->me); + } + break; + } + case ACT_SCENE: + { + bSceneActuator *sa = act->data; + sa->camera = newlibadr(fd, ob->id.lib, sa->camera); + sa->scene = newlibadr(fd, ob->id.lib, sa->scene); + break; + } + case ACT_ACTION: + { + bActionActuator *aa = act->data; + aa->act = newlibadr_us(fd, ob->id.lib, aa->act); + break; + } + case ACT_SHAPEACTION: + { + bActionActuator *aa = act->data; + aa->act = newlibadr_us(fd, ob->id.lib, aa->act); + break; + } + case ACT_PROPERTY: + { + bPropertyActuator *pa = act->data; + pa->ob = newlibadr(fd, ob->id.lib, pa->ob); + break; + } + case ACT_MESSAGE: + { + bMessageActuator *ma = act->data; + ma->toObject = newlibadr(fd, ob->id.lib, ma->toObject); + break; + } + case ACT_2DFILTER: + { + bTwoDFilterActuator *_2dfa = act->data; + _2dfa->text = newlibadr(fd, ob->id.lib, _2dfa->text); + break; + } + case ACT_PARENT: + { + bParentActuator *parenta = act->data; + parenta->ob = newlibadr(fd, ob->id.lib, parenta->ob); + break; + } + case ACT_STATE: + /* bStateActuator *statea = act->data; */ + break; + case ACT_ARMATURE: + { + bArmatureActuator *arma= act->data; + arma->target = newlibadr(fd, ob->id.lib, arma->target); + arma->subtarget = newlibadr(fd, ob->id.lib, arma->subtarget); + break; + } + case ACT_STEERING: + { + bSteeringActuator *steeringa = act->data; + steeringa->target = newlibadr(fd, ob->id.lib, steeringa->target); + steeringa->navmesh = newlibadr(fd, ob->id.lib, steeringa->navmesh); + break; + } + case ACT_MOUSE: + /* bMouseActuator *moa = act->data; */ + break; } } @@ -5662,23 +5700,16 @@ static bool scene_validate_setscene__liblink(Scene *sce, const int totscene) static void lib_link_scene(FileData *fd, Main *main) { - Scene *sce; - Base *base, *next; - Sequence *seq; - SceneRenderLayer *srl; - FreestyleModuleConfig *fmc; - FreestyleLineSet *fls; - #ifdef USE_SETSCENE_CHECK bool need_check_set = false; int totscene = 0; #endif - for (sce = main->scene.first; sce; sce = sce->id.next) { + for (Scene *sce = main->scene.first; sce; sce = sce->id.next) { if (sce->id.tag & LIB_TAG_NEED_LINK) { /* Link ID Properties -- and copy this comment EXACTLY for easy finding * of library blocks that implement this.*/ - IDP_LibLinkProperty(sce->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd); + IDP_LibLinkProperty(sce->id.properties, fd); lib_link_animdata(fd, &sce->id, sce->adt); lib_link_keyingsets(fd, &sce->id, &sce->keyingsets); @@ -5714,7 +5745,7 @@ static void lib_link_scene(FileData *fd, Main *main) sce->toolsettings->particle.shape_object = newlibadr(fd, sce->id.lib, sce->toolsettings->particle.shape_object); - for (base = sce->base.first; base; base = next) { + for (Base *next, *base = sce->base.first; base; base = next) { next = base->next; base->object = newlibadr_us(fd, sce->id.lib, base->object); @@ -5728,8 +5759,11 @@ static void lib_link_scene(FileData *fd, Main *main) } } + Sequence *seq; SEQ_BEGIN (sce->ed, seq) { + IDP_LibLinkProperty(seq->prop, fd); + if (seq->ipo) seq->ipo = newlibadr_us(fd, sce->id.lib, seq->ipo); // XXX deprecated - old animation system seq->scene_sound = NULL; if (seq->scene) { @@ -5793,13 +5827,13 @@ static void lib_link_scene(FileData *fd, Main *main) composite_patch(sce->nodetree, sce); } - for (srl = sce->r.layers.first; srl; srl = srl->next) { + for (SceneRenderLayer *srl = sce->r.layers.first; srl; srl = srl->next) { srl->mat_override = newlibadr_us(fd, sce->id.lib, srl->mat_override); srl->light_override = newlibadr_us(fd, sce->id.lib, srl->light_override); - for (fmc = srl->freestyleConfig.modules.first; fmc; fmc = fmc->next) { + for (FreestyleModuleConfig *fmc = srl->freestyleConfig.modules.first; fmc; fmc = fmc->next) { fmc->script = newlibadr(fd, sce->id.lib, fmc->script); } - for (fls = srl->freestyleConfig.linesets.first; fls; fls = fls->next) { + for (FreestyleLineSet *fls = srl->freestyleConfig.linesets.first; fls; fls = fls->next) { fls->linestyle = newlibadr_us(fd, sce->id.lib, fls->linestyle); fls->group = newlibadr_us(fd, sce->id.lib, fls->group); } @@ -5833,7 +5867,7 @@ static void lib_link_scene(FileData *fd, Main *main) #ifdef USE_SETSCENE_CHECK if (need_check_set) { - for (sce = main->scene.first; sce; sce = sce->id.next) { + for (Scene *sce = main->scene.first; sce; sce = sce->id.next) { if (sce->id.tag & LIB_TAG_NEED_LINK) { sce->id.tag &= ~LIB_TAG_NEED_LINK; if (!scene_validate_setscene__liblink(sce, totscene)) { @@ -6243,8 +6277,10 @@ static void lib_link_windowmanager(FileData *fd, Main *main) for (wm = main->wm.first; wm; wm = wm->id.next) { if (wm->id.tag & LIB_TAG_NEED_LINK) { - for (win = wm->windows.first; win; win = win->next) + /* Note: WM IDProperties are never written to file, hence no need to read/link them here. */ + for (win = wm->windows.first; win; win = win->next) { win->screen = newlibadr(fd, NULL, win->screen); + } wm->id.tag &= ~LIB_TAG_NEED_LINK; } @@ -6256,13 +6292,12 @@ static void lib_link_windowmanager(FileData *fd, Main *main) /* relink's grease pencil data's refs */ static void lib_link_gpencil(FileData *fd, Main *main) { - bGPdata *gpd; - - for (gpd = main->gpencil.first; gpd; gpd = gpd->id.next) { + for (bGPdata *gpd = main->gpencil.first; gpd; gpd = gpd->id.next) { if (gpd->id.tag & LIB_TAG_NEED_LINK) { - gpd->id.tag &= ~LIB_TAG_NEED_LINK; - + IDP_LibLinkProperty(gpd->id.properties, fd); lib_link_animdata(fd, &gpd->id, gpd->adt); + + gpd->id.tag &= ~LIB_TAG_NEED_LINK; } } } @@ -6324,12 +6359,11 @@ static void direct_link_gpencil(FileData *fd, bGPdata *gpd) * check lib pointers in call below */ static void lib_link_screen(FileData *fd, Main *main) { - bScreen *sc; - ScrArea *sa; - - for (sc = main->screen.first; sc; sc = sc->id.next) { + for (bScreen *sc = main->screen.first; sc; sc = sc->id.next) { if (sc->id.tag & LIB_TAG_NEED_LINK) { + IDP_LibLinkProperty(sc->id.properties, fd); id_us_ensure_real(&sc->id); + sc->scene = newlibadr(fd, sc->id.lib, sc->scene); /* this should not happen, but apparently it does somehow. Until we figure out the cause, @@ -6340,178 +6374,205 @@ static void lib_link_screen(FileData *fd, Main *main) sc->animtimer = NULL; /* saved in rare cases */ sc->scrubbing = false; - for (sa = sc->areabase.first; sa; sa = sa->next) { - SpaceLink *sl; - + for (ScrArea *sa = sc->areabase.first; sa; sa = sa->next) { sa->full = newlibadr(fd, sc->id.lib, sa->full); - for (sl = sa->spacedata.first; sl; sl= sl->next) { - if (sl->spacetype == SPACE_VIEW3D) { - View3D *v3d = (View3D*) sl; - BGpic *bgpic = NULL; - - v3d->camera= newlibadr(fd, sc->id.lib, v3d->camera); - v3d->ob_centre= newlibadr(fd, sc->id.lib, v3d->ob_centre); - - /* should be do_versions but not easy adding into the listbase */ - if (v3d->bgpic) { - v3d->bgpic = newlibadr(fd, sc->id.lib, v3d->bgpic); - BLI_addtail(&v3d->bgpicbase, bgpic); - v3d->bgpic = NULL; + for (SpaceLink *sl = sa->spacedata.first; sl; sl= sl->next) { + switch (sl->spacetype) { + case SPACE_VIEW3D: + { + View3D *v3d = (View3D*) sl; + BGpic *bgpic = NULL; + + v3d->camera= newlibadr(fd, sc->id.lib, v3d->camera); + v3d->ob_centre= newlibadr(fd, sc->id.lib, v3d->ob_centre); + + /* should be do_versions but not easy adding into the listbase */ + if (v3d->bgpic) { + v3d->bgpic = newlibadr(fd, sc->id.lib, v3d->bgpic); + BLI_addtail(&v3d->bgpicbase, bgpic); + v3d->bgpic = NULL; + } + + for (bgpic = v3d->bgpicbase.first; bgpic; bgpic = bgpic->next) { + bgpic->ima = newlibadr_us(fd, sc->id.lib, bgpic->ima); + bgpic->clip = newlibadr_us(fd, sc->id.lib, bgpic->clip); + } + if (v3d->localvd) { + v3d->localvd->camera = newlibadr(fd, sc->id.lib, v3d->localvd->camera); + } + break; } - - for (bgpic = v3d->bgpicbase.first; bgpic; bgpic = bgpic->next) { - bgpic->ima = newlibadr_us(fd, sc->id.lib, bgpic->ima); - bgpic->clip = newlibadr_us(fd, sc->id.lib, bgpic->clip); + case SPACE_IPO: + { + SpaceIpo *sipo = (SpaceIpo *)sl; + bDopeSheet *ads = sipo->ads; + + if (ads) { + ads->source = newlibadr(fd, sc->id.lib, ads->source); + ads->filter_grp = newlibadr(fd, sc->id.lib, ads->filter_grp); + } + break; } - if (v3d->localvd) { - v3d->localvd->camera = newlibadr(fd, sc->id.lib, v3d->localvd->camera); + case SPACE_BUTS: + { + SpaceButs *sbuts = (SpaceButs *)sl; + sbuts->pinid = newlibadr(fd, sc->id.lib, sbuts->pinid); + if (sbuts->pinid == NULL) { + sbuts->flag &= ~SB_PIN_CONTEXT; + } + break; } - } - else if (sl->spacetype == SPACE_IPO) { - SpaceIpo *sipo = (SpaceIpo *)sl; - bDopeSheet *ads = sipo->ads; - - if (ads) { - ads->source = newlibadr(fd, sc->id.lib, ads->source); - ads->filter_grp = newlibadr(fd, sc->id.lib, ads->filter_grp); + case SPACE_FILE: + break; + case SPACE_ACTION: + { + SpaceAction *saction = (SpaceAction *)sl; + bDopeSheet *ads = &saction->ads; + + if (ads) { + ads->source = newlibadr(fd, sc->id.lib, ads->source); + ads->filter_grp = newlibadr(fd, sc->id.lib, ads->filter_grp); + } + + saction->action = newlibadr(fd, sc->id.lib, saction->action); + break; } - } - else if (sl->spacetype == SPACE_BUTS) { - SpaceButs *sbuts = (SpaceButs *)sl; - sbuts->pinid = newlibadr(fd, sc->id.lib, sbuts->pinid); - if (sbuts->pinid == NULL) { - sbuts->flag &= ~SB_PIN_CONTEXT; + case SPACE_IMAGE: + { + SpaceImage *sima = (SpaceImage *)sl; + + sima->image = newlibadr_real_us(fd, sc->id.lib, sima->image); + sima->mask_info.mask = newlibadr_real_us(fd, sc->id.lib, sima->mask_info.mask); + + /* NOTE: pre-2.5, this was local data not lib data, but now we need this as lib data + * so fingers crossed this works fine! + */ + sima->gpd = newlibadr_us(fd, sc->id.lib, sima->gpd); + break; } - } - else if (sl->spacetype == SPACE_FILE) { - ; - } - else if (sl->spacetype == SPACE_ACTION) { - SpaceAction *saction = (SpaceAction *)sl; - bDopeSheet *ads = &saction->ads; - - if (ads) { - ads->source = newlibadr(fd, sc->id.lib, ads->source); - ads->filter_grp = newlibadr(fd, sc->id.lib, ads->filter_grp); + case SPACE_SEQ: + { + SpaceSeq *sseq = (SpaceSeq *)sl; + + /* NOTE: pre-2.5, this was local data not lib data, but now we need this as lib data + * so fingers crossed this works fine! + */ + sseq->gpd = newlibadr_us(fd, sc->id.lib, sseq->gpd); + break; } - - saction->action = newlibadr(fd, sc->id.lib, saction->action); - } - else if (sl->spacetype == SPACE_IMAGE) { - SpaceImage *sima = (SpaceImage *)sl; - - sima->image = newlibadr_real_us(fd, sc->id.lib, sima->image); - sima->mask_info.mask = newlibadr_real_us(fd, sc->id.lib, sima->mask_info.mask); - - /* NOTE: pre-2.5, this was local data not lib data, but now we need this as lib data - * so fingers crossed this works fine! - */ - sima->gpd = newlibadr_us(fd, sc->id.lib, sima->gpd); - } - else if (sl->spacetype == SPACE_SEQ) { - SpaceSeq *sseq = (SpaceSeq *)sl; - - /* NOTE: pre-2.5, this was local data not lib data, but now we need this as lib data - * so fingers crossed this works fine! - */ - sseq->gpd = newlibadr_us(fd, sc->id.lib, sseq->gpd); + case SPACE_NLA: + { + SpaceNla *snla= (SpaceNla *)sl; + bDopeSheet *ads= snla->ads; + + if (ads) { + ads->source = newlibadr(fd, sc->id.lib, ads->source); + ads->filter_grp = newlibadr(fd, sc->id.lib, ads->filter_grp); + } + break; + } + case SPACE_TEXT: + { + SpaceText *st= (SpaceText *)sl; - } - else if (sl->spacetype == SPACE_NLA) { - SpaceNla *snla= (SpaceNla *)sl; - bDopeSheet *ads= snla->ads; - - if (ads) { - ads->source = newlibadr(fd, sc->id.lib, ads->source); - ads->filter_grp = newlibadr(fd, sc->id.lib, ads->filter_grp); + st->text= newlibadr(fd, sc->id.lib, st->text); + break; } - } - else if (sl->spacetype == SPACE_TEXT) { - SpaceText *st= (SpaceText *)sl; - - st->text= newlibadr(fd, sc->id.lib, st->text); - } - else if (sl->spacetype == SPACE_SCRIPT) { - SpaceScript *scpt = (SpaceScript *)sl; - /*scpt->script = NULL; - 2.45 set to null, better re-run the script */ - if (scpt->script) { - scpt->script = newlibadr(fd, sc->id.lib, scpt->script); + case SPACE_SCRIPT: + { + SpaceScript *scpt = (SpaceScript *)sl; + /*scpt->script = NULL; - 2.45 set to null, better re-run the script */ if (scpt->script) { - SCRIPT_SET_NULL(scpt->script); + scpt->script = newlibadr(fd, sc->id.lib, scpt->script); + if (scpt->script) { + SCRIPT_SET_NULL(scpt->script); + } } + break; } - } - else if (sl->spacetype == SPACE_OUTLINER) { - SpaceOops *so= (SpaceOops *)sl; - so->search_tse.id = newlibadr(fd, NULL, so->search_tse.id); - - if (so->treestore) { - TreeStoreElem *tselem; - BLI_mempool_iter iter; + case SPACE_OUTLINER: + { + SpaceOops *so= (SpaceOops *)sl; + so->search_tse.id = newlibadr(fd, NULL, so->search_tse.id); + + if (so->treestore) { + TreeStoreElem *tselem; + BLI_mempool_iter iter; + + BLI_mempool_iternew(so->treestore, &iter); + while ((tselem = BLI_mempool_iterstep(&iter))) { + tselem->id = newlibadr(fd, NULL, tselem->id); + } + if (so->treehash) { + /* rebuild hash table, because it depends on ids too */ + so->storeflag |= SO_TREESTORE_REBUILD; + } + } + break; + } + case SPACE_NODE: + { + SpaceNode *snode = (SpaceNode *)sl; + bNodeTreePath *path, *path_next; + bNodeTree *ntree; + + /* node tree can be stored locally in id too, link this first */ + snode->id = newlibadr(fd, sc->id.lib, snode->id); + snode->from = newlibadr(fd, sc->id.lib, snode->from); + + ntree = snode->id ? ntreeFromID(snode->id) : NULL; + snode->nodetree = ntree ? ntree : newlibadr_us(fd, sc->id.lib, snode->nodetree); + + for (path = snode->treepath.first; path; path = path->next) { + if (path == snode->treepath.first) { + /* first nodetree in path is same as snode->nodetree */ + path->nodetree = snode->nodetree; + } + else + path->nodetree = newlibadr_us(fd, sc->id.lib, path->nodetree); - BLI_mempool_iternew(so->treestore, &iter); - while ((tselem = BLI_mempool_iterstep(&iter))) { - tselem->id = newlibadr(fd, NULL, tselem->id); + if (!path->nodetree) + break; } - if (so->treehash) { - /* rebuild hash table, because it depends on ids too */ - so->storeflag |= SO_TREESTORE_REBUILD; + + /* remaining path entries are invalid, remove */ + for (; path; path = path_next) { + path_next = path->next; + + BLI_remlink(&snode->treepath, path); + MEM_freeN(path); } - } - } - else if (sl->spacetype == SPACE_NODE) { - SpaceNode *snode = (SpaceNode *)sl; - bNodeTreePath *path, *path_next; - bNodeTree *ntree; - - /* node tree can be stored locally in id too, link this first */ - snode->id = newlibadr(fd, sc->id.lib, snode->id); - snode->from = newlibadr(fd, sc->id.lib, snode->from); - - ntree = snode->id ? ntreeFromID(snode->id) : NULL; - snode->nodetree = ntree ? ntree : newlibadr_us(fd, sc->id.lib, snode->nodetree); - - for (path = snode->treepath.first; path; path = path->next) { - if (path == snode->treepath.first) { - /* first nodetree in path is same as snode->nodetree */ - path->nodetree = snode->nodetree; + + /* edittree is just the last in the path, + * set this directly since the path may have been shortened above */ + if (snode->treepath.last) { + path = snode->treepath.last; + snode->edittree = path->nodetree; } - else - path->nodetree = newlibadr_us(fd, sc->id.lib, path->nodetree); - - if (!path->nodetree) - break; + else { + snode->edittree = NULL; + } + break; } - - /* remaining path entries are invalid, remove */ - for (; path; path = path_next) { - path_next = path->next; - - BLI_remlink(&snode->treepath, path); - MEM_freeN(path); + case SPACE_CLIP: + { + SpaceClip *sclip = (SpaceClip *)sl; + + sclip->clip = newlibadr_real_us(fd, sc->id.lib, sclip->clip); + sclip->secondary_clip = newlibadr_real_us(fd, sc->id.lib, sclip->secondary_clip); + sclip->mask_info.mask = newlibadr_real_us(fd, sc->id.lib, sclip->mask_info.mask); + break; } - - /* edittree is just the last in the path, - * set this directly since the path may have been shortened above */ - if (snode->treepath.last) { - path = snode->treepath.last; - snode->edittree = path->nodetree; + case SPACE_LOGIC: + { + SpaceLogic *slogic = (SpaceLogic *)sl; + + slogic->gpd = newlibadr_us(fd, sc->id.lib, slogic->gpd); + break; } - else - snode->edittree = NULL; - } - else if (sl->spacetype == SPACE_CLIP) { - SpaceClip *sclip = (SpaceClip *)sl; - - sclip->clip = newlibadr_real_us(fd, sc->id.lib, sclip->clip); - sclip->secondary_clip = newlibadr_real_us(fd, sc->id.lib, sclip->secondary_clip); - sclip->mask_info.mask = newlibadr_real_us(fd, sc->id.lib, sclip->mask_info.mask); - } - else if (sl->spacetype == SPACE_LOGIC) { - SpaceLogic *slogic = (SpaceLogic *)sl; - - slogic->gpd = newlibadr_us(fd, sc->id.lib, slogic->gpd); + default: + break; } } } @@ -7386,13 +7447,13 @@ static void fix_relpaths_library(const char *basepath, Main *main) static void lib_link_speaker(FileData *fd, Main *main) { - Speaker *spk; - - for (spk = main->speaker.first; spk; spk = spk->id.next) { + for (Speaker *spk = main->speaker.first; spk; spk = spk->id.next) { if (spk->id.tag & LIB_TAG_NEED_LINK) { + IDP_LibLinkProperty(spk->id.properties, fd); lib_link_animdata(fd, &spk->id, spk->adt); spk->sound = newlibadr_us(fd, spk->id.lib, spk->sound); + spk->id.tag &= ~LIB_TAG_NEED_LINK; } } @@ -7442,14 +7503,15 @@ static void direct_link_sound(FileData *fd, bSound *sound) static void lib_link_sound(FileData *fd, Main *main) { - bSound *sound; - - for (sound = main->sound.first; sound; sound = sound->id.next) { + for (bSound *sound = main->sound.first; sound; sound = sound->id.next) { if (sound->id.tag & LIB_TAG_NEED_LINK) { - sound->id.tag &= ~LIB_TAG_NEED_LINK; + IDP_LibLinkProperty(sound->id.properties, fd); + sound->ipo = newlibadr_us(fd, sound->id.lib, sound->ipo); // XXX deprecated - old animation system BKE_sound_load(main, sound); + + sound->id.tag &= ~LIB_TAG_NEED_LINK; } } } @@ -7464,17 +7526,13 @@ static void direct_link_group(FileData *fd, Group *group) static void lib_link_group(FileData *fd, Main *main) { - Group *group; - GroupObject *go; - bool add_us; - - for (group = main->group.first; group; group = group->id.next) { + for (Group *group = main->group.first; group; group = group->id.next) { if (group->id.tag & LIB_TAG_NEED_LINK) { - group->id.tag &= ~LIB_TAG_NEED_LINK; + IDP_LibLinkProperty(group->id.properties, fd); - add_us = false; + bool add_us = false; - for (go = group->gobject.first; go; go = go->next) { + for (GroupObject *go = group->gobject.first; go; go = go->next) { go->ob = newlibadr_real_us(fd, group->id.lib, go->ob); if (go->ob) { go->ob->flag |= OB_FROMGROUP; @@ -7486,6 +7544,8 @@ static void lib_link_group(FileData *fd, Main *main) id_us_ensure_real(&group->id); } BKE_group_object_unlink(group, NULL, NULL, NULL); /* removes NULL entries */ + + group->id.tag &= ~LIB_TAG_NEED_LINK; } } } @@ -7608,13 +7668,11 @@ static void lib_link_movieCorrespondences(FileData *fd, static void lib_link_movieclip(FileData *fd, Main *main) { - MovieClip *clip; - - for (clip = main->movieclip.first; clip; clip = clip->id.next) { + for (MovieClip *clip = main->movieclip.first; clip; clip = clip->id.next) { if (clip->id.tag & LIB_TAG_NEED_LINK) { MovieTracking *tracking = &clip->tracking; - MovieTrackingObject *object; + IDP_LibLinkProperty(clip->id.properties, fd); lib_link_animdata(fd, &clip->id, clip->adt); clip->gpd = newlibadr_us(fd, clip->id.lib, clip->gpd); @@ -7623,7 +7681,7 @@ static void lib_link_movieclip(FileData *fd, Main *main) lib_link_moviePlaneTracks(fd, clip, &tracking->plane_tracks); lib_link_movieCorrespondences(fd, clip, &tracking->correspondences); - for (object = tracking->objects.first; object; object = object->next) { + for (MovieTrackingObject *object = tracking->objects.first; object; object = object->next) { lib_link_movieTracks(fd, clip, &object->tracks); lib_link_moviePlaneTracks(fd, clip, &object->plane_tracks); } @@ -7700,16 +7758,12 @@ static void lib_link_mask_parent(FileData *fd, Mask *mask, MaskParent *parent) static void lib_link_mask(FileData *fd, Main *main) { - Mask *mask; - - mask = main->mask.first; - while (mask) { + for (Mask *mask = main->mask.first; mask; mask = mask->id.next) { if (mask->id.tag & LIB_TAG_NEED_LINK) { - MaskLayer *masklay; - + IDP_LibLinkProperty(mask->id.properties, fd); lib_link_animdata(fd, &mask->id, mask->adt); - for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { + for (MaskLayer *masklay = mask->masklayers.first; masklay; masklay = masklay->next) { MaskSpline *spline; spline = masklay->splines.first; @@ -7730,7 +7784,6 @@ static void lib_link_mask(FileData *fd, Main *main) mask->id.tag &= ~LIB_TAG_NEED_LINK; } - mask = mask->id.next; } } @@ -7738,18 +7791,13 @@ static void lib_link_mask(FileData *fd, Main *main) static void lib_link_linestyle(FileData *fd, Main *main) { - FreestyleLineStyle *linestyle; - LineStyleModifier *m; - MTex *mtex; - int a; - - linestyle = main->linestyle.first; - while (linestyle) { + for (FreestyleLineStyle *linestyle = main->linestyle.first; linestyle; linestyle = linestyle->id.next) { if (linestyle->id.tag & LIB_TAG_NEED_LINK) { - linestyle->id.tag &= ~LIB_TAG_NEED_LINK; + LineStyleModifier *m; - IDP_LibLinkProperty(linestyle->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd); + IDP_LibLinkProperty(linestyle->id.properties, fd); lib_link_animdata(fd, &linestyle->id, linestyle->adt); + for (m = linestyle->color_modifiers.first; m; m = m->next) { switch (m->type) { case LS_MODIFIER_DISTANCE_FROM_OBJECT: @@ -7780,8 +7828,8 @@ static void lib_link_linestyle(FileData *fd, Main *main) break; } } - for (a=0; a < MAX_MTEX; a++) { - mtex = linestyle->mtex[a]; + for (int a = 0; a < MAX_MTEX; a++) { + MTex *mtex = linestyle->mtex[a]; if (mtex) { mtex->tex = newlibadr_us(fd, linestyle->id.lib, mtex->tex); mtex->object = newlibadr(fd, linestyle->id.lib, mtex->object); @@ -7791,8 +7839,9 @@ static void lib_link_linestyle(FileData *fd, Main *main) lib_link_ntree(fd, &linestyle->id, linestyle->nodetree); linestyle->nodetree->id.lib = linestyle->id.lib; } + + linestyle->id.tag &= ~LIB_TAG_NEED_LINK; } - linestyle = linestyle->id.next; } } @@ -8463,12 +8512,13 @@ static void lib_link_all(FileData *fd, Main *main) lib_link_screen(fd, main); lib_link_scene(fd, main); lib_link_object(fd, main); + lib_link_mesh(fd, main); lib_link_curve(fd, main); lib_link_mball(fd, main); lib_link_material(fd, main); lib_link_texture(fd, main); lib_link_image(fd, main); - lib_link_ipo(fd, main); // XXX deprecated... still needs to be maintained for version patches still + lib_link_ipo(fd, main); /* XXX deprecated... still needs to be maintained for version patches still */ lib_link_key(fd, main); lib_link_world(fd, main); lib_link_lamp(fd, main); @@ -8481,7 +8531,7 @@ static void lib_link_all(FileData *fd, Main *main) lib_link_armature(fd, main); lib_link_action(fd, main); lib_link_vfont(fd, main); - lib_link_nodetree(fd, main); /* has to be done after scene/materials, this will verify group nodes */ + lib_link_nodetree(fd, main); /* has to be done after scene/materials, this will verify group nodes */ lib_link_brush(fd, main); lib_link_palette(fd, main); lib_link_paint_curve(fd, main); @@ -8492,9 +8542,7 @@ static void lib_link_all(FileData *fd, Main *main) lib_link_gpencil(fd, main); lib_link_cachefiles(fd, main); - lib_link_mesh(fd, main); /* as last: tpage images with users at zero */ - - lib_link_library(fd, main); /* only init users */ + lib_link_library(fd, main); /* only init users */ } static void direct_link_keymapitem(FileData *fd, wmKeyMapItem *kmi) @@ -8914,6 +8962,31 @@ static void expand_constraint_channels(FileData *fd, Main *mainvar, ListBase *ch } } +static void expand_idprops(FileData *fd, Main *mainvar, IDProperty *prop) +{ + if (!prop) + return; + + switch (prop->type) { + case IDP_ID: + expand_doit(fd, mainvar, IDP_Id(prop)); + break; + case IDP_IDPARRAY: + { + IDProperty *idp_array = IDP_IDPArray(prop); + for (int i = 0; i < prop->len; i++) { + expand_idprops(fd, mainvar, &idp_array[i]); + } + break; + } + case IDP_GROUP: + for (IDProperty *loop = prop->data.group.first; loop; loop = loop->next) { + expand_idprops(fd, mainvar, loop); + } + break; + } +} + static void expand_fmodifiers(FileData *fd, Main *mainvar, ListBase *list) { FModifier *fcm; @@ -9099,6 +9172,7 @@ static void expand_key(FileData *fd, Main *mainvar, Key *key) static void expand_nodetree(FileData *fd, Main *mainvar, bNodeTree *ntree) { bNode *node; + bNodeSocket *sock; if (ntree->adt) expand_animdata(fd, mainvar, ntree->adt); @@ -9107,10 +9181,22 @@ static void expand_nodetree(FileData *fd, Main *mainvar, bNodeTree *ntree) expand_doit(fd, mainvar, ntree->gpd); for (node = ntree->nodes.first; node; node = node->next) { - if (node->id && node->type != CMP_NODE_R_LAYERS) + if (node->id && node->type != CMP_NODE_R_LAYERS) { expand_doit(fd, mainvar, node->id); + } + + expand_idprops(fd, mainvar, node->prop); + + for (sock = node->inputs.first; sock; sock = sock->next) + expand_doit(fd, mainvar, sock->prop); + for (sock = node->outputs.first; sock; sock = sock->next) + expand_doit(fd, mainvar, sock->prop); } + for (sock = ntree->inputs.first; sock; sock = sock->next) + expand_doit(fd, mainvar, sock->prop); + for (sock = ntree->outputs.first; sock; sock = sock->next) + expand_doit(fd, mainvar, sock->prop); } static void expand_texture(FileData *fd, Main *mainvar, Tex *tex) @@ -9328,17 +9414,6 @@ static void expand_constraints(FileData *fd, Main *mainvar, ListBase *lb) } } -#if 0 /* Disabled as it doesn't actually do anything except recurse... */ -static void expand_bones(FileData *fd, Main *mainvar, Bone *bone) -{ - Bone *curBone; - - for (curBone = bone->childbase.first; curBone; curBone=curBone->next) { - expand_bones(fd, mainvar, curBone); - } -} -#endif - static void expand_pose(FileData *fd, Main *mainvar, bPose *pose) { bPoseChannel *chan; @@ -9348,24 +9423,28 @@ static void expand_pose(FileData *fd, Main *mainvar, bPose *pose) for (chan = pose->chanbase.first; chan; chan = chan->next) { expand_constraints(fd, mainvar, &chan->constraints); + expand_idprops(fd, mainvar, chan->prop); expand_doit(fd, mainvar, chan->custom); } } +static void expand_bones(FileData *fd, Main *mainvar, Bone *bone) +{ + expand_idprops(fd, mainvar, bone->prop); + + for (Bone *curBone = bone->childbase.first; curBone; curBone = curBone->next) { + expand_bones(fd, mainvar, curBone); + } +} + static void expand_armature(FileData *fd, Main *mainvar, bArmature *arm) -{ +{ if (arm->adt) expand_animdata(fd, mainvar, arm->adt); - -#if 0 /* Disabled as this currently only recurses down the chain doing nothing */ - { - Bone *curBone; - - for (curBone = arm->bonebase.first; curBone; curBone=curBone->next) { - expand_bones(fd, mainvar, curBone); - } + + for (Bone *curBone = arm->bonebase.first; curBone; curBone = curBone->next) { + expand_bones(fd, mainvar, curBone); } -#endif } static void expand_object_expandModifiers( @@ -9593,6 +9672,8 @@ static void expand_scene(FileData *fd, Main *mainvar, Scene *sce) SEQ_BEGIN (sce->ed, seq) { + expand_idprops(fd, mainvar, seq->prop); + if (seq->scene) expand_doit(fd, mainvar, seq->scene); if (seq->scene_camera) expand_doit(fd, mainvar, seq->scene_camera); if (seq->clip) expand_doit(fd, mainvar, seq->clip); @@ -9750,6 +9831,8 @@ void BLO_expand_main(void *fdhandle, Main *mainvar) id = lbarray[a]->first; while (id) { if (id->tag & LIB_TAG_NEED_EXPAND) { + expand_idprops(fd, mainvar, id->properties); + switch (GS(id->name)) { case ID_OB: expand_object(fd, mainvar, (Object *)id); diff --git a/source/blender/blenloader/intern/versioning_defaults.c b/source/blender/blenloader/intern/versioning_defaults.c index 99d9e140481..e34f12b1cf9 100644 --- a/source/blender/blenloader/intern/versioning_defaults.c +++ b/source/blender/blenloader/intern/versioning_defaults.c @@ -68,6 +68,18 @@ void BLO_update_defaults_userpref_blend(void) * but take care since some hardware has driver bugs here (T46962). * Further hardware workarounds should be made in gpu_extensions.c */ U.glalphaclip = (1.0f / 255); + + /* default so DPI is detected automatically */ + U.dpi = 0; + U.ui_scale = 1.0f; + +#ifdef WITH_PYTHON_SECURITY + /* use alternative setting for security nuts + * otherwise we'd need to patch the binary blob - startup.blend.c */ + U.flag |= USER_SCRIPT_AUTOEXEC_DISABLE; +#else + U.flag &= ~USER_SCRIPT_AUTOEXEC_DISABLE; +#endif } /** diff --git a/source/blender/bmesh/intern/bmesh_core.c b/source/blender/bmesh/intern/bmesh_core.c index cee5450a37d..4fe14fdf5c9 100644 --- a/source/blender/bmesh/intern/bmesh_core.c +++ b/source/blender/bmesh/intern/bmesh_core.c @@ -2403,15 +2403,20 @@ static void bmesh_kernel_vert_separate__cleanup(BMesh *bm, LinkNode *edges_separ do { LinkNode *n_orig = edges_separate->link; do { - BMEdge *e_orig = n_orig->link; + LinkNode *n_prev = n_orig; LinkNode *n_step = n_orig->next; + BMEdge *e_orig = n_orig->link; do { BMEdge *e = n_step->link; BLI_assert(e != e_orig); - if ((e->v1 == e_orig->v1) && (e->v2 == e_orig->v2)) { - BM_edge_splice(bm, e_orig, e); + if ((e->v1 == e_orig->v1) && (e->v2 == e_orig->v2) && + BM_edge_splice(bm, e_orig, e)) + { + /* don't visit again */ + n_prev->next = n_step->next; } - } while ((n_step = n_step->next)); + } while ((n_prev = n_step), + (n_step = n_step->next)); } while ((n_orig = n_orig->next) && n_orig->next); } while ((edges_separate = edges_separate->next)); diff --git a/source/blender/bmesh/intern/bmesh_mesh.c b/source/blender/bmesh/intern/bmesh_mesh.c index d92d0cbb469..e9fb6a28154 100644 --- a/source/blender/bmesh/intern/bmesh_mesh.c +++ b/source/blender/bmesh/intern/bmesh_mesh.c @@ -1512,23 +1512,6 @@ int BM_mesh_elem_count(BMesh *bm, const char htype) } } -/** - * Special case: Python uses custom-data layers to hold PyObject references. - * These have to be kept in-place, else the PyObject's we point to, wont point back to us. - * - * \note ``ele_src`` Is a duplicate, so we don't need to worry about getting in a feedback loop. - * - * \note If there are other customdata layers which need this functionality, it should be generalized. - * However #BM_mesh_remap is currently the only place where this is done. - */ -static void bm_mesh_remap_cd_update( - BMHeader *ele_dst, BMHeader *ele_src, - const int cd_elem_pyptr) -{ - void **pyptr_dst_p = BM_ELEM_CD_GET_VOID_P(((BMElem *)ele_dst), cd_elem_pyptr); - void **pyptr_src_p = BM_ELEM_CD_GET_VOID_P(((BMElem *)ele_src), cd_elem_pyptr); - *pyptr_dst_p = *pyptr_src_p; -} /** * Remaps the vertices, edges and/or faces of the bmesh as indicated by vert/edge/face_idx arrays @@ -1570,6 +1553,8 @@ void BM_mesh_remap( BMVert **verts_pool, *verts_copy, **vep; int i, totvert = bm->totvert; const unsigned int *new_idx; + /* Special case: Python uses custom - data layers to hold PyObject references. + * These have to be kept in - place, else the PyObject's we point to, wont point back to us. */ const int cd_vert_pyptr = CustomData_get_offset(&bm->vdata, CD_BM_ELEM_PYPTR); /* Init the old-to-new vert pointers mapping */ @@ -1578,9 +1563,14 @@ void BM_mesh_remap( /* Make a copy of all vertices. */ verts_pool = bm->vtable; verts_copy = MEM_mallocN(sizeof(BMVert) * totvert, "BM_mesh_remap verts copy"); + void **pyptrs = (cd_vert_pyptr != -1) ? MEM_mallocN(sizeof(void *) * totvert, __func__) : NULL; for (i = totvert, ve = verts_copy + totvert - 1, vep = verts_pool + totvert - 1; i--; ve--, vep--) { *ve = **vep; /* printf("*vep: %p, verts_pool[%d]: %p\n", *vep, i, verts_pool[i]);*/ + if (cd_vert_pyptr != -1) { + void **pyptr = BM_ELEM_CD_GET_VOID_P(((BMElem *)ve), cd_vert_pyptr); + pyptrs[i] = *pyptr; + } } /* Copy back verts to their new place, and update old2new pointers mapping. */ @@ -1593,13 +1583,17 @@ void BM_mesh_remap( /* printf("mapping vert from %d to %d (%p/%p to %p)\n", i, *new_idx, *vep, verts_pool[i], new_vep);*/ BLI_ghash_insert(vptr_map, *vep, new_vep); if (cd_vert_pyptr != -1) { - bm_mesh_remap_cd_update(&(*vep)->head, &new_vep->head, cd_vert_pyptr); + void **pyptr = BM_ELEM_CD_GET_VOID_P(((BMElem *)new_vep), cd_vert_pyptr); + *pyptr = pyptrs[*new_idx]; } } bm->elem_index_dirty |= BM_VERT; bm->elem_table_dirty |= BM_VERT; MEM_freeN(verts_copy); + if (pyptrs) { + MEM_freeN(pyptrs); + } } /* Remap Edges */ @@ -1607,6 +1601,8 @@ void BM_mesh_remap( BMEdge **edges_pool, *edges_copy, **edp; int i, totedge = bm->totedge; const unsigned int *new_idx; + /* Special case: Python uses custom - data layers to hold PyObject references. + * These have to be kept in - place, else the PyObject's we point to, wont point back to us. */ const int cd_edge_pyptr = CustomData_get_offset(&bm->edata, CD_BM_ELEM_PYPTR); /* Init the old-to-new vert pointers mapping */ @@ -1615,8 +1611,13 @@ void BM_mesh_remap( /* Make a copy of all vertices. */ edges_pool = bm->etable; edges_copy = MEM_mallocN(sizeof(BMEdge) * totedge, "BM_mesh_remap edges copy"); + void **pyptrs = (cd_edge_pyptr != -1) ? MEM_mallocN(sizeof(void *) * totedge, __func__) : NULL; for (i = totedge, ed = edges_copy + totedge - 1, edp = edges_pool + totedge - 1; i--; ed--, edp--) { *ed = **edp; + if (cd_edge_pyptr != -1) { + void **pyptr = BM_ELEM_CD_GET_VOID_P(((BMElem *)ed), cd_edge_pyptr); + pyptrs[i] = *pyptr; + } } /* Copy back verts to their new place, and update old2new pointers mapping. */ @@ -1629,13 +1630,17 @@ void BM_mesh_remap( BLI_ghash_insert(eptr_map, *edp, new_edp); /* printf("mapping edge from %d to %d (%p/%p to %p)\n", i, *new_idx, *edp, edges_pool[i], new_edp);*/ if (cd_edge_pyptr != -1) { - bm_mesh_remap_cd_update(&(*edp)->head, &new_edp->head, cd_edge_pyptr); + void **pyptr = BM_ELEM_CD_GET_VOID_P(((BMElem *)new_edp), cd_edge_pyptr); + *pyptr = pyptrs[*new_idx]; } } bm->elem_index_dirty |= BM_EDGE; bm->elem_table_dirty |= BM_EDGE; MEM_freeN(edges_copy); + if (pyptrs) { + MEM_freeN(pyptrs); + } } /* Remap Faces */ @@ -1643,6 +1648,8 @@ void BM_mesh_remap( BMFace **faces_pool, *faces_copy, **fap; int i, totface = bm->totface; const unsigned int *new_idx; + /* Special case: Python uses custom - data layers to hold PyObject references. + * These have to be kept in - place, else the PyObject's we point to, wont point back to us. */ const int cd_poly_pyptr = CustomData_get_offset(&bm->pdata, CD_BM_ELEM_PYPTR); /* Init the old-to-new vert pointers mapping */ @@ -1651,8 +1658,13 @@ void BM_mesh_remap( /* Make a copy of all vertices. */ faces_pool = bm->ftable; faces_copy = MEM_mallocN(sizeof(BMFace) * totface, "BM_mesh_remap faces copy"); + void **pyptrs = (cd_poly_pyptr != -1) ? MEM_mallocN(sizeof(void *) * totface, __func__) : NULL; for (i = totface, fa = faces_copy + totface - 1, fap = faces_pool + totface - 1; i--; fa--, fap--) { *fa = **fap; + if (cd_poly_pyptr != -1) { + void **pyptr = BM_ELEM_CD_GET_VOID_P(((BMElem *)fa), cd_poly_pyptr); + pyptrs[i] = *pyptr; + } } /* Copy back verts to their new place, and update old2new pointers mapping. */ @@ -1664,7 +1676,8 @@ void BM_mesh_remap( *new_fap = *fa; BLI_ghash_insert(fptr_map, *fap, new_fap); if (cd_poly_pyptr != -1) { - bm_mesh_remap_cd_update(&(*fap)->head, &new_fap->head, cd_poly_pyptr); + void **pyptr = BM_ELEM_CD_GET_VOID_P(((BMElem *)new_fap), cd_poly_pyptr); + *pyptr = pyptrs[*new_idx]; } } @@ -1672,6 +1685,9 @@ void BM_mesh_remap( bm->elem_table_dirty |= BM_FACE; MEM_freeN(faces_copy); + if (pyptrs) { + MEM_freeN(pyptrs); + } } /* And now, fix all vertices/edges/faces/loops pointers! */ diff --git a/source/blender/bmesh/operators/bmo_primitive.c b/source/blender/bmesh/operators/bmo_primitive.c index 723e0b168e0..e5c3ff7a088 100644 --- a/source/blender/bmesh/operators/bmo_primitive.c +++ b/source/blender/bmesh/operators/bmo_primitive.c @@ -1566,7 +1566,7 @@ void BM_mesh_calc_uvs_cone( BLI_assert(cd_loop_uv_offset != -1); /* caller is responsible for ensuring the mesh has UVs */ - x = 0.0f; + x = 1.0f; y = 1.0f - uv_height; BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) { @@ -1580,7 +1580,7 @@ void BM_mesh_calc_uvs_cone( switch (loop_index) { case 0: - x += uv_width; + /* Continue in the last position */ break; case 1: y += uv_height; @@ -1598,8 +1598,6 @@ void BM_mesh_calc_uvs_cone( luv->uv[0] = x; luv->uv[1] = y; } - - x += uv_width; } else { /* top or bottom face - so unwrap it by transforming back to a circle and using the X/Y coords */ diff --git a/source/blender/collada/AnimationImporter.cpp b/source/blender/collada/AnimationImporter.cpp index 3801c9300df..bd47ee0214d 100644 --- a/source/blender/collada/AnimationImporter.cpp +++ b/source/blender/collada/AnimationImporter.cpp @@ -1934,7 +1934,7 @@ Object *AnimationImporter::get_joint_object(COLLADAFW::Node *root, COLLADAFW::No mul_m4_m4m4(mat, ipar, temp); } - TransformBase::decompose(mat, job->loc, NULL, job->quat, job->size); + bc_decompose(mat, job->loc, NULL, job->quat, job->size); if (par_job) { job->parent = par_job; diff --git a/source/blender/collada/ArmatureExporter.cpp b/source/blender/collada/ArmatureExporter.cpp index 40065956ecb..9348f3b3285 100644 --- a/source/blender/collada/ArmatureExporter.cpp +++ b/source/blender/collada/ArmatureExporter.cpp @@ -256,47 +256,59 @@ void ArmatureExporter::add_bone_transform(Object *ob_arm, Bone *bone, COLLADASW: //bPoseChannel *pchan = BKE_pose_channel_find_name(ob_arm->pose, bone->name); float mat[4][4]; + float bone_rest_mat[4][4]; /* derived from bone->arm_mat */ + float parent_rest_mat[4][4]; /* derived from bone->parent->arm_mat */ - if (bone->parent) { - // get bone-space matrix from parent pose - /*bPoseChannel *parchan = BKE_pose_channel_find_name(ob_arm->pose, bone->parent->name); - float invpar[4][4]; - invert_m4_m4(invpar, parchan->pose_mat); - mul_m4_m4m4(mat, invpar, pchan->pose_mat);*/ - - float invpar[4][4]; - invert_m4_m4(invpar, bone->parent->arm_mat); - mul_m4_m4m4(mat, invpar, bone->arm_mat); + bool has_restmat = bc_get_property_matrix(bone, "rest_mat", mat); - } - else { + if (!has_restmat) { + + /* Have no restpose matrix stored, try old style <= Blender 2.78 */ - //copy_m4_m4(mat, pchan->pose_mat); - //pose mat is object space - //New change: export bone->arm_mat - copy_m4_m4(mat, bone->arm_mat); - } + bc_create_restpose_mat(this->export_settings, bone, bone_rest_mat, bone->arm_mat, true); - // OPEN_SIM_COMPATIBILITY - if (export_settings->open_sim) { - // Remove rotations vs armature from transform - // parent_rest_rot * mat * irest_rot - float temp[4][4]; - copy_m4_m4(temp, bone->arm_mat); - temp[3][0] = temp[3][1] = temp[3][2] = 0.0f; - invert_m4(temp); + if (bone->parent) { + // get bone-space matrix from parent pose + /*bPoseChannel *parchan = BKE_pose_channel_find_name(ob_arm->pose, bone->parent->name); + float invpar[4][4]; + invert_m4_m4(invpar, parchan->pose_mat); + mul_m4_m4m4(mat, invpar, pchan->pose_mat);*/ + float invpar[4][4]; + bc_create_restpose_mat(this->export_settings, bone->parent, parent_rest_mat, bone->parent->arm_mat, true); - mul_m4_m4m4(mat, mat, temp); + invert_m4_m4(invpar, parent_rest_mat); + mul_m4_m4m4(mat, invpar, bone_rest_mat); - if (bone->parent) { - copy_m4_m4(temp, bone->parent->arm_mat); + } + else { + copy_m4_m4(mat, bone_rest_mat); + } + + // OPEN_SIM_COMPATIBILITY + if (export_settings->open_sim) { + // Remove rotations vs armature from transform + // parent_rest_rot * mat * irest_rot + float temp[4][4]; + copy_m4_m4(temp, bone_rest_mat); temp[3][0] = temp[3][1] = temp[3][2] = 0.0f; + invert_m4(temp); + + mul_m4_m4m4(mat, mat, temp); + + if (bone->parent) { + copy_m4_m4(temp, parent_rest_mat); + temp[3][0] = temp[3][1] = temp[3][2] = 0.0f; - mul_m4_m4m4(mat, temp, mat); + mul_m4_m4m4(mat, temp, mat); + } } } + if (this->export_settings->limit_precision) + bc_sanitize_mat(mat, 6); + TransformWriter::add_node_transform(node, mat, NULL); + } std::string ArmatureExporter::get_controller_id(Object *ob_arm, Object *ob) diff --git a/source/blender/collada/ArmatureImporter.cpp b/source/blender/collada/ArmatureImporter.cpp index f4ce3992771..0ea8324ed7c 100644 --- a/source/blender/collada/ArmatureImporter.cpp +++ b/source/blender/collada/ArmatureImporter.cpp @@ -32,6 +32,7 @@ #include "COLLADAFWUniqueId.h" +extern "C" { #include "BKE_action.h" #include "BKE_depsgraph.h" #include "BKE_object.h" @@ -39,7 +40,9 @@ #include "BLI_string.h" #include "BLI_listbase.h" #include "ED_armature.h" +} +#include "collada_utils.h" #include "ArmatureImporter.h" // use node name, or fall back to original id if not present (name is optional) @@ -91,6 +94,7 @@ int ArmatureImporter::create_bone(SkinInfo *skin, COLLADAFW::Node *node, EditBon { float mat[4][4]; float joint_inv_bind_mat[4][4]; + float joint_bind_mat[4][4]; int chain_length = 0; //Checking if bone is already made. @@ -114,7 +118,7 @@ int ArmatureImporter::create_bone(SkinInfo *skin, COLLADAFW::Node *node, EditBon // get original world-space matrix invert_m4_m4(mat, joint_inv_bind_mat); - + copy_m4_m4(joint_bind_mat, mat); // And make local to armature Object *ob_arm = skin->BKE_armature_from_object(); if (ob_arm) { @@ -130,18 +134,7 @@ int ArmatureImporter::create_bone(SkinInfo *skin, COLLADAFW::Node *node, EditBon // create a bone even if there's no joint data for it (i.e. it has no influence) if (!bone_is_skinned) { - float obmat[4][4]; - // bone-space - get_node_mat(obmat, node, NULL, NULL); - - // get world-space - if (parent) { - mul_m4_m4m4(mat, parent_mat, obmat); - } - else { - copy_m4_m4(mat, obmat); - } - + get_node_mat(mat, node, NULL, NULL, parent_mat); } if (parent) bone->parent = parent; @@ -157,10 +150,11 @@ int ArmatureImporter::create_bone(SkinInfo *skin, COLLADAFW::Node *node, EditBon int use_connect = be.get_use_connect(); switch (use_connect) { - case 1: bone->flag |= BONE_CONNECTED; - break; - case 0: bone->flag &= ~BONE_CONNECTED; - case -1: break; // not defined + case 1: bone->flag |= BONE_CONNECTED; + break; + case -1:/* Connect type not specified */ + case 0: bone->flag &= ~BONE_CONNECTED; + break; } if (be.has_roll()) { @@ -173,6 +167,15 @@ int ArmatureImporter::create_bone(SkinInfo *skin, COLLADAFW::Node *node, EditBon bone->roll = angle; } copy_v3_v3(bone->head, mat[3]); + + if (bone_is_skinned) + { + float rest_mat[4][4]; + get_node_mat(rest_mat, node, NULL, NULL, NULL); + bc_set_IDPropertyMatrix(bone, "bind_mat", joint_bind_mat); + bc_set_IDPropertyMatrix(bone, "rest_mat", rest_mat); + } + add_v3_v3v3(bone->tail, bone->head, tail); //tail must be non zero /* find smallest bone length in armature (used later for leaf bone length) */ @@ -274,7 +277,6 @@ void ArmatureImporter::fix_parent_connect(bArmature *armature, Bone *bone) } - void ArmatureImporter::connect_bone_chains(bArmature *armature, Bone *parentbone, int clip) { BoneExtensionMap &extended_bones = bone_extension_manager.getExtensionMap(armature); @@ -290,12 +292,13 @@ void ArmatureImporter::connect_bone_chains(bArmature *armature, Bone *parentbone for (; child; child = child->next) { BoneExtended *be = extended_bones[child->name]; if (be != NULL) { - if (be->get_chain_length() <= clip) { - if (be->get_chain_length() > maxlen) { + int chain_len = be->get_chain_length(); + if (chain_len <= clip) { + if (chain_len > maxlen) { dominant_child = be; - maxlen = be->get_chain_length(); + maxlen = chain_len; } - else if (be->get_chain_length() == maxlen) { + else if (chain_len == maxlen) { dominant_child = NULL; } } @@ -309,7 +312,6 @@ void ArmatureImporter::connect_bone_chains(bArmature *armature, Bone *parentbone EditBone *pebone = bc_get_edit_bone(armature, parentbone->name); EditBone *cebone = bc_get_edit_bone(armature, dominant_child->get_name()); if (pebone && !(cebone->flag & BONE_CONNECTED)) { - float vec[3]; sub_v3_v3v3(vec, cebone->head, pebone->head); @@ -322,14 +324,16 @@ void ArmatureImporter::connect_bone_chains(bArmature *armature, Bone *parentbone if (len_squared_v3(vec) > MINIMUM_BONE_LENGTH) { - pebone->tail[0] = cebone->head[0]; - pebone->tail[1] = cebone->head[1]; - pebone->tail[2] = cebone->head[2]; - + copy_v3_v3(pebone->tail, cebone->head); + pbe->set_tail(pebone->tail); /* to make fix_leafbone happy ...*/ if (pbe && pbe->get_chain_length() >= this->import_settings->min_chain_length) { + + BoneExtended *cbe = extended_bones[cebone->name]; + cbe->set_use_connect(true); + cebone->flag |= BONE_CONNECTED; - printf("Connecting chain: parent %s --> %s (child)\n", pebone->name, cebone->name); pbe->set_leaf_bone(false); + printf("Connect Bone chain: parent (%s --> %s) child)\n", pebone->name, cebone->name); } } } diff --git a/source/blender/collada/ControllerExporter.cpp b/source/blender/collada/ControllerExporter.cpp index a868adc1e66..1c2642e8313 100644 --- a/source/blender/collada/ControllerExporter.cpp +++ b/source/blender/collada/ControllerExporter.cpp @@ -463,81 +463,6 @@ std::string ControllerExporter::add_joints_source(Object *ob_arm, ListBase *defb return source_id; } -static float get_property(Bone *bone, const char *key, float def) -{ - float result = def; - if (bone->prop) { - IDProperty *property = IDP_GetPropertyFromGroup(bone->prop, key); - if (property) { - switch (property->type) { - case IDP_INT: - result = (float)(IDP_Int(property)); - break; - case IDP_FLOAT: - result = (float)(IDP_Float(property)); - break; - case IDP_DOUBLE: - result = (float)(IDP_Double(property)); - break; - default: - result = def; - } - } - } - return result; -} - -/** - * This function creates an arbitrary rest pose matrix from - * data provided as custom properties. This is a workaround - * for support of maya's restpose matrix which can be arbitrary - * in opposition to Blender where the Rest pose Matrix is always - * the Identity matrix. - * - * The custom properties are: - * - * restpose_scale_x - * restpose_scale_y - * restpose_scale_z - * - * restpose_rot_x - * restpose_rot_y - * restpose_rot_z - * - * restpose_loc_x - * restpose_loc_y - * restpose_loc_z - * - * The matrix is only setup if the scale AND the rot properties are defined. - * The presence of the loc properties is optional. - * - * This feature has been implemented to support Second Life "Fitted Mesh" - * TODO: Check if an arbitrary rest pose matrix makes sense within Blender. - * Eventually leverage the custom property data into an "official" - * Edit_bone Property - */ -static void create_restpose_mat(Bone *bone, float mat[4][4]) -{ - float loc[3] = { - get_property(bone, "restpose_loc_x", 0.0), - get_property(bone, "restpose_loc_y", 0.0), - get_property(bone, "restpose_loc_z", 0.0) - }; - - float rot[3] = { - DEG2RADF(get_property(bone, "restpose_rot_x", 0.0)), - DEG2RADF(get_property(bone, "restpose_rot_y", 0.0)), - DEG2RADF(get_property(bone, "restpose_rot_z", 0.0)) - }; - - float scale[3] = { - get_property(bone, "restpose_scale_x", 1.0), - get_property(bone, "restpose_scale_y", 1.0), - get_property(bone, "restpose_scale_z", 1.0) - }; - - loc_eulO_size_to_mat4(mat, loc, rot, scale, 6); -} std::string ControllerExporter::add_inv_bind_mats_source(Object *ob_arm, ListBase *defbase, const std::string& controller_id) { @@ -580,30 +505,36 @@ std::string ControllerExporter::add_inv_bind_mats_source(Object *ob_arm, ListBas float world[4][4]; float inv_bind_mat[4][4]; + float bind_mat[4][4]; /* derived from bone->arm_mat */ + + bool has_bindmat = bc_get_property_matrix(pchan->bone, "bind_mat", bind_mat); - // SL/OPEN_SIM COMPATIBILITY - if (export_settings->open_sim) { - // Only translations, no rotation vs armature - float temp[4][4]; - unit_m4(temp); - copy_v3_v3(temp[3], pchan->bone->arm_mat[3]); - mul_m4_m4m4(world, ob_arm->obmat, temp); - - // Add Maya restpose matrix (if defined as properties) - float restpose_mat[4][4]; - create_restpose_mat(pchan->bone, restpose_mat); - mul_m4_m4m4(world, world, restpose_mat); + if (!has_bindmat) { - } - else { - // make world-space matrix, arm_mat is armature-space - mul_m4_m4m4(world, ob_arm->obmat, pchan->bone->arm_mat); + /* Have no bind matrix stored, try old style <= Blender 2.78 */ + + bc_create_restpose_mat(this->export_settings, pchan->bone, bind_mat, pchan->bone->arm_mat, true); + + // SL/OPEN_SIM COMPATIBILITY + if (export_settings->open_sim) { + + float loc[3]; + float rot[3] = { 0, 0, 0 }; + float scale[3]; + bc_decompose(bind_mat, loc, NULL, NULL, scale); + + // Only translations, no rotation vs armature + loc_eulO_size_to_mat4(bind_mat, loc, rot, scale, 6); + } } + // make world-space matrix (bind_mat is armature-space) + mul_m4_m4m4(world, ob_arm->obmat, bind_mat); invert_m4_m4(mat, world); converter.mat4_to_dae(inv_bind_mat, mat); - + if (this->export_settings->limit_precision) + bc_sanitize_mat(inv_bind_mat, 6); source.appendValues(inv_bind_mat); } } diff --git a/source/blender/collada/ExportSettings.h b/source/blender/collada/ExportSettings.h index 9451cac9dae..de91f68a492 100644 --- a/source/blender/collada/ExportSettings.h +++ b/source/blender/collada/ExportSettings.h @@ -51,7 +51,10 @@ public: bool use_blender_profile; bool sort_by_name; BC_export_transformation_type export_transformation_type; + bool open_sim; + bool limit_precision; + bool keep_bind_info; char *filepath; LinkNode *export_set; diff --git a/source/blender/collada/ImportSettings.h b/source/blender/collada/ImportSettings.h index 2c52d73e756..4a2d4e8046a 100644 --- a/source/blender/collada/ImportSettings.h +++ b/source/blender/collada/ImportSettings.h @@ -37,6 +37,7 @@ public: bool fix_orientation; int min_chain_length; char *filepath; + bool keep_bind_info; }; #endif diff --git a/source/blender/collada/TransformReader.cpp b/source/blender/collada/TransformReader.cpp index f8f31304d28..7f742be7e30 100644 --- a/source/blender/collada/TransformReader.cpp +++ b/source/blender/collada/TransformReader.cpp @@ -34,7 +34,21 @@ TransformReader::TransformReader(UnitConverter *conv) : unit_converter(conv) /* pass */ } -void TransformReader::get_node_mat(float mat[4][4], COLLADAFW::Node *node, std::map<COLLADAFW::UniqueId, Animation> *animation_map, Object *ob) +void TransformReader::get_node_mat( + float mat[4][4], + COLLADAFW::Node *node, + std::map<COLLADAFW::UniqueId, Animation> *animation_map, + Object *ob) +{ + get_node_mat(mat, node, animation_map, ob, NULL); +} + +void TransformReader::get_node_mat( + float mat[4][4], + COLLADAFW::Node *node, + std::map<COLLADAFW::UniqueId, Animation> *animation_map, + Object *ob, + float parent_mat[4][4]) { float cur[4][4]; float copy[4][4]; @@ -52,6 +66,9 @@ void TransformReader::get_node_mat(float mat[4][4], COLLADAFW::Node *node, std:: // then this is considered as redundant information. // So if we find a Matrix we use that and return. dae_matrix_to_mat4(tm, mat); + if (parent_mat) { + mul_m4_m4m4(mat, parent_mat, mat); + } return; case COLLADAFW::Transformation::TRANSLATE: dae_translate_to_mat4(tm, cur); @@ -80,6 +97,10 @@ void TransformReader::get_node_mat(float mat[4][4], COLLADAFW::Node *node, std:: (*animation_map)[anim_list_id] = anim; } } + + if (parent_mat) { + mul_m4_m4m4(mat, parent_mat, mat); + } } void TransformReader::dae_rotate_to_mat4(COLLADAFW::Transformation *tm, float m[4][4]) diff --git a/source/blender/collada/TransformReader.h b/source/blender/collada/TransformReader.h index ab974b9ba85..08bb17ccac1 100644 --- a/source/blender/collada/TransformReader.h +++ b/source/blender/collada/TransformReader.h @@ -43,7 +43,7 @@ //struct Object; -class TransformReader : public TransformBase +class TransformReader { protected: @@ -59,7 +59,8 @@ public: TransformReader(UnitConverter *conv); void get_node_mat(float mat[4][4], COLLADAFW::Node *node, std::map<COLLADAFW::UniqueId, Animation> *animation_map, Object *ob); - + void get_node_mat(float mat[4][4], COLLADAFW::Node *node, std::map<COLLADAFW::UniqueId, Animation> *animation_map, Object *ob, float parent_mat[4][4]); + void dae_rotate_to_mat4(COLLADAFW::Transformation *tm, float m[4][4]); void dae_translate_to_mat4(COLLADAFW::Transformation *tm, float m[4][4]); void dae_scale_to_mat4(COLLADAFW::Transformation *tm, float m[4][4]); diff --git a/source/blender/collada/TransformWriter.cpp b/source/blender/collada/TransformWriter.cpp index 908111ebae6..b7eeff3b074 100644 --- a/source/blender/collada/TransformWriter.cpp +++ b/source/blender/collada/TransformWriter.cpp @@ -27,11 +27,10 @@ #include "BKE_object.h" +#include "BLI_math.h" #include "TransformWriter.h" -#include "BLI_math.h" - void TransformWriter::add_node_transform(COLLADASW::Node& node, float mat[4][4], float parent_mat[4][4]) { float loc[3], rot[3], scale[3]; @@ -51,7 +50,7 @@ void TransformWriter::add_node_transform(COLLADASW::Node& node, float mat[4][4], converter->mat4_to_dae_double(dmat, local); delete converter; - TransformBase::decompose(local, loc, rot, NULL, scale); + bc_decompose(local, loc, rot, NULL, scale); if (node.getType() == COLLADASW::Node::JOINT) { // XXX Why are joints handled differently ? @@ -116,7 +115,7 @@ void TransformWriter::add_node_transform_ob(COLLADASW::Node& node, Object *ob, B case BC_TRANSFORMATION_TYPE_TRANSROTLOC: { float loc[3], rot[3], scale[3]; - TransformBase::decompose(f_obmat, loc, rot, NULL, scale); + bc_decompose(f_obmat, loc, rot, NULL, scale); add_transform(node, loc, rot, scale); break; } diff --git a/source/blender/collada/TransformWriter.h b/source/blender/collada/TransformWriter.h index 7f69a4b9c95..5bb13d4aac9 100644 --- a/source/blender/collada/TransformWriter.h +++ b/source/blender/collada/TransformWriter.h @@ -33,9 +33,10 @@ #include "DNA_object_types.h" #include "collada_internal.h" +#include "collada_utils.h" #include "collada.h" -class TransformWriter : protected TransformBase +class TransformWriter { protected: void add_node_transform(COLLADASW::Node& node, float mat[4][4], float parent_mat[4][4]); diff --git a/source/blender/collada/collada.cpp b/source/blender/collada/collada.cpp index fe8b1d2320a..bfe3180909b 100644 --- a/source/blender/collada/collada.cpp +++ b/source/blender/collada/collada.cpp @@ -48,7 +48,8 @@ int collada_import(bContext *C, int find_chains, int auto_connect, int fix_orientation, - int min_chain_length) + int min_chain_length, + int keep_bind_info) { ImportSettings import_settings; @@ -58,6 +59,7 @@ int collada_import(bContext *C, import_settings.find_chains = find_chains != 0; import_settings.fix_orientation = fix_orientation != 0; import_settings.min_chain_length = min_chain_length; + import_settings.keep_bind_info = keep_bind_info !=0; DocumentImporter imp(C, &import_settings); if (imp.import()) return 1; @@ -87,7 +89,9 @@ int collada_export(Scene *sce, int use_blender_profile, int sort_by_name, BC_export_transformation_type export_transformation_type, - int open_sim) + int open_sim, + int limit_precision, + int keep_bind_info) { ExportSettings export_settings; @@ -112,7 +116,8 @@ int collada_export(Scene *sce, export_settings.sort_by_name = sort_by_name != 0; export_settings.export_transformation_type = export_transformation_type; export_settings.open_sim = open_sim != 0; - + export_settings.limit_precision = limit_precision != 0; + export_settings.keep_bind_info = keep_bind_info !=0; int includeFilter = OB_REL_NONE; if (export_settings.include_armatures) includeFilter |= OB_REL_MOD_ARMATURE; diff --git a/source/blender/collada/collada.h b/source/blender/collada/collada.h index a4416608584..8035af59c8b 100644 --- a/source/blender/collada/collada.h +++ b/source/blender/collada/collada.h @@ -58,7 +58,9 @@ int collada_import(struct bContext *C, int find_chains, int auto_connect, int fix_orientation, - int min_chain_length); + int min_chain_length, + + int keep_bind_info); int collada_export(struct Scene *sce, const char *filepath, @@ -81,9 +83,10 @@ int collada_export(struct Scene *sce, int use_blender_profile, int sort_by_name, BC_export_transformation_type export_transformation_type, - int open_sim); - + int open_sim, + int limit_precision, + int keep_bind_info); #ifdef __cplusplus } diff --git a/source/blender/collada/collada_internal.cpp b/source/blender/collada/collada_internal.cpp index 70b44ebc222..6ebde6bd773 100644 --- a/source/blender/collada/collada_internal.cpp +++ b/source/blender/collada/collada_internal.cpp @@ -162,18 +162,6 @@ void UnitConverter::calculate_scale(Scene &sce) size_to_mat4(scale_mat4, rescale); } -void TransformBase::decompose(float mat[4][4], float *loc, float eul[3], float quat[4], float *size) -{ - mat4_to_size(size, mat); - if (eul) { - mat4_to_eul(eul, mat); - } - if (quat) { - mat4_to_quat(quat, mat); - } - copy_v3_v3(loc, mat[3]); -} - /** * Translation map. * Used to translate every COLLADA id to a valid id, no matter what "wrong" letters may be diff --git a/source/blender/collada/collada_internal.h b/source/blender/collada/collada_internal.h index 482dbf9ab31..1c7aa160f57 100644 --- a/source/blender/collada/collada_internal.h +++ b/source/blender/collada/collada_internal.h @@ -85,12 +85,6 @@ public: }; -class TransformBase -{ -public: - void decompose(float mat[4][4], float *loc, float eul[3], float quat[4], float *size); -}; - extern void clear_global_id_map(); /** Look at documentation of translate_map */ extern std::string translate_id(const std::string &id); diff --git a/source/blender/collada/collada_utils.cpp b/source/blender/collada/collada_utils.cpp index 2efa8b21d81..ac4395e1430 100644 --- a/source/blender/collada/collada_utils.cpp +++ b/source/blender/collada/collada_utils.cpp @@ -32,8 +32,6 @@ #include "COLLADAFWMeshPrimitive.h" #include "COLLADAFWMeshVertexData.h" -#include "collada_utils.h" - extern "C" { #include "DNA_modifier_types.h" #include "DNA_customdata_types.h" @@ -63,6 +61,9 @@ extern "C" { #include "bmesh_tools.h" } +#include "collada_utils.h" +#include "ExportSettings.h" + float bc_get_float_value(const COLLADAFW::FloatOrDoubleArray& array, unsigned int index) { if (index >= array.getValuesCount()) @@ -352,6 +353,28 @@ void bc_match_scale(std::vector<Object *> *objects_done, } } +/* + Convenience function to get only the needed components of a matrix +*/ +void bc_decompose(float mat[4][4], float *loc, float eul[3], float quat[4], float *size) +{ + if (size) { + mat4_to_size(size, mat); + } + + if (eul) { + mat4_to_eul(eul, mat); + } + + if (quat) { + mat4_to_quat(quat, mat); + } + + if (loc) { + copy_v3_v3(loc, mat[3]); + } +} + void bc_triangulate_mesh(Mesh *me) { bool use_beauty = false; @@ -612,3 +635,200 @@ int BoneExtended::get_use_connect() { return this->use_connect; } + +/** +* Stores a 4*4 matrix as a custom bone property array of size 16 +*/ +void bc_set_IDPropertyMatrix(EditBone *ebone, const char *key, float mat[4][4]) +{ + IDProperty *idgroup = (IDProperty *)ebone->prop; + if (idgroup == NULL) + { + IDPropertyTemplate val = { 0 }; + idgroup = IDP_New(IDP_GROUP, &val, "RNA_EditBone ID properties"); + ebone->prop = idgroup; + } + + IDPropertyTemplate val = { 0 }; + val.array.len = 16; + val.array.type = IDP_FLOAT; + + IDProperty *data = IDP_New(IDP_ARRAY, &val, key); + float *array = (float *)IDP_Array(data); + for (int i = 0; i < 4; i++) + for (int j = 0; j < 4; j++) + array[4 * i + j] = mat[i][j]; + + IDP_AddToGroup(idgroup, data); +} + +#if 0 +/** +* Stores a Float value as a custom bone property +* +* Note: This function is currently not needed. Keep for future usage +*/ +static void bc_set_IDProperty(EditBone *ebone, const char *key, float value) +{ + if (ebone->prop == NULL) + { + IDPropertyTemplate val = { 0 }; + ebone->prop = IDP_New(IDP_GROUP, &val, "RNA_EditBone ID properties"); + } + + IDProperty *pgroup = (IDProperty *)ebone->prop; + IDPropertyTemplate val = { 0 }; + IDProperty *prop = IDP_New(IDP_FLOAT, &val, key); + IDP_Float(prop) = value; + IDP_AddToGroup(pgroup, prop); + +} +#endif + +/* +* Get a custom property when it exists. +* This function is also used to check if a property exists. +*/ +IDProperty *bc_get_IDProperty(Bone *bone, std::string key) +{ + return (bone->prop == NULL) ? NULL : IDP_GetPropertyFromGroup(bone->prop, key.c_str()); +} + +/** +* Read a custom bone property and convert to float +* Return def if the property does not exist. +*/ +float bc_get_property(Bone *bone, std::string key, float def) +{ + float result = def; + IDProperty *property = bc_get_IDProperty(bone, key); + if (property) { + switch (property->type) { + case IDP_INT: + result = (float)(IDP_Int(property)); + break; + case IDP_FLOAT: + result = (float)(IDP_Float(property)); + break; + case IDP_DOUBLE: + result = (float)(IDP_Double(property)); + break; + default: + result = def; + } + } + return result; +} + +/** +* Read a custom bone property and convert to matrix +* Return true if conversion was succesfull +* +* Return false if: +* - the property does not exist +* - is not an array of size 16 +*/ +bool bc_get_property_matrix(Bone *bone, std::string key, float mat[4][4]) +{ + IDProperty *property = bc_get_IDProperty(bone, key); + if (property && property->type == IDP_ARRAY && property->len == 16) { + float *array = (float *)IDP_Array(property); + for (int i = 0; i < 4; i++) + for (int j = 0; j < 4; j++) + mat[i][j] = array[4 * i + j]; + return true; + } + return false; +} + +/** +* get a vector that is stored in 3 custom properties (used in Blender <= 2.78) +*/ +void bc_get_property_vector(Bone *bone, std::string key, float val[3], const float def[3]) +{ + val[0] = bc_get_property(bone, key + "_x", def[0]); + val[1] = bc_get_property(bone, key + "_y", def[1]); + val[2] = bc_get_property(bone, key + "_z", def[2]); +} + +/** +* Check if vector exist stored in 3 custom properties (used in Blender <= 2.78) +*/ +static bool has_custom_props(Bone *bone, bool enabled, std::string key) +{ + if (!enabled) + return false; + + return (bc_get_IDProperty(bone, key + "_x") + || bc_get_IDProperty(bone, key + "_y") + || bc_get_IDProperty(bone, key + "_z")); + +} + +/** +* Check if custom information about bind matrix exists and modify the from_mat +* accordingly. +* +* Note: This is old style for Blender <= 2.78 only kept for compatibility +*/ +void bc_create_restpose_mat(const ExportSettings *export_settings, Bone *bone, float to_mat[4][4], float from_mat[4][4], bool use_local_space) +{ + float loc[3]; + float rot[3]; + float scale[3]; + static const float V0[3] = { 0, 0, 0 }; + + if (!has_custom_props(bone, export_settings->keep_bind_info, "restpose_loc") && + !has_custom_props(bone, export_settings->keep_bind_info, "restpose_rot") && + !has_custom_props(bone, export_settings->keep_bind_info, "restpose_scale")) + { + /* No need */ + copy_m4_m4(to_mat, from_mat); + return; + } + + bc_decompose(from_mat, loc, rot, NULL, scale); + loc_eulO_size_to_mat4(to_mat, loc, rot, scale, 6); + + if (export_settings->keep_bind_info) { + bc_get_property_vector(bone, "restpose_loc", loc, loc); + + if (use_local_space && bone->parent) { + Bone *b = bone; + while (b->parent) { + b = b->parent; + float ploc[3]; + bc_get_property_vector(b, "restpose_loc", ploc, V0); + loc[0] += ploc[0]; + loc[1] += ploc[1]; + loc[2] += ploc[2]; + } + } + } + + if (export_settings->keep_bind_info) { + if (bc_get_IDProperty(bone, "restpose_rot_x")) + rot[0] = DEG2RADF(bc_get_property(bone, "restpose_rot_x", 0)); + if (bc_get_IDProperty(bone, "restpose_rot_y")) + rot[1] = DEG2RADF(bc_get_property(bone, "restpose_rot_y", 0)); + if (bc_get_IDProperty(bone, "restpose_rot_z")) + rot[2] = DEG2RADF(bc_get_property(bone, "restpose_rot_z", 0)); + } + + if (export_settings->keep_bind_info) { + bc_get_property_vector(bone, "restpose_scale", scale, scale); + } + + loc_eulO_size_to_mat4(to_mat, loc, rot, scale, 6); + +} + +/* + Make 4*4 matrices better readable +*/ +void bc_sanitize_mat(float mat[4][4], int precision) +{ + for (int i = 0; i < 4; i++) + for (int j = 0; j < 4; j++) + mat[i][j] = double_round(mat[i][j], precision); +}
\ No newline at end of file diff --git a/source/blender/collada/collada_utils.h b/source/blender/collada/collada_utils.h index 7fdbef3b6cb..38c0bd5096a 100644 --- a/source/blender/collada/collada_utils.h +++ b/source/blender/collada/collada_utils.h @@ -53,8 +53,10 @@ extern "C" { #include "BKE_object.h" #include "BKE_DerivedMesh.h" #include "BKE_scene.h" +#include "BKE_idprop.h" } +#include "ImportSettings.h" #include "ExportSettings.h" #include "collada_internal.h" @@ -88,11 +90,24 @@ extern std::string bc_url_encode(std::string data); extern void bc_match_scale(Object *ob, UnitConverter &bc_unit, bool scale_to_scene); extern void bc_match_scale(std::vector<Object *> *objects_done, UnitConverter &unit_converter, bool scale_to_scene); +extern void bc_decompose(float mat[4][4], float *loc, float eul[3], float quat[4], float *size); + extern void bc_triangulate_mesh(Mesh *me); extern bool bc_is_leaf_bone(Bone *bone); extern EditBone *bc_get_edit_bone(bArmature * armature, char *name); extern int bc_set_layer(int bitfield, int layer, bool enable); extern int bc_set_layer(int bitfield, int layer); +extern void bc_sanitize_mat(float mat[4][4], int precision); + +extern IDProperty *bc_get_IDProperty(Bone *bone, std::string key); +extern void bc_set_IDProperty(EditBone *ebone, const char *key, float value); +extern void bc_set_IDPropertyMatrix(EditBone *ebone, const char *key, float mat[4][4]); + +extern float bc_get_property(Bone *bone, std::string key, float def); +extern void bc_get_property_vector(Bone *bone, std::string key, float val[3], const float def[3]); +extern bool bc_get_property_matrix(Bone *bone, std::string key, float mat[4][4]); + +extern void bc_create_restpose_mat(const ExportSettings *export_settings, Bone *bone, float to_mat[4][4], float world[4][4], bool use_local_space); class BCPolygonNormalsIndices { diff --git a/source/blender/depsgraph/intern/builder/deg_builder.cc b/source/blender/depsgraph/intern/builder/deg_builder.cc index cb2f057a090..7f62eb122db 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder.cc @@ -52,14 +52,6 @@ namespace DEG { -string deg_fcurve_id_name(const FCurve *fcu) -{ - char index_buf[32]; - // TODO(sergey): Use int-to-string utility or so. - BLI_snprintf(index_buf, sizeof(index_buf), "[%d]", fcu->array_index); - return string(fcu->rna_path) + index_buf; -} - static bool check_object_needs_evaluation(Object *object) { if (object->recalc & OB_RECALC_ALL) { @@ -77,28 +69,8 @@ static bool check_object_needs_evaluation(Object *object) return false; } -void deg_graph_build_finalize(Depsgraph *graph) +void deg_graph_build_flush_layers(Depsgraph *graph) { - /* STEP 1: Make sure new invisible dependencies are ready for use. - * - * TODO(sergey): This might do a bit of extra tagging, but it's kinda nice - * to do it ahead of a time and don't spend time on flushing updates on - * every frame change. - */ - GHASH_FOREACH_BEGIN(IDDepsNode *, id_node, graph->id_hash) - { - if (id_node->layers == 0) { - ID *id = id_node->id; - if (GS(id->name) == ID_OB) { - Object *object = (Object *)id; - if (check_object_needs_evaluation(object)) { - id_node->tag_update(graph); - } - } - } - } - GHASH_FOREACH_END(); - /* STEP 2: Flush visibility layers from children to parent. */ std::stack<OperationDepsNode *> stack; foreach (OperationDepsNode *node, graph->operations) { IDDepsNode *id_node = node->owner->owner; @@ -143,6 +115,31 @@ void deg_graph_build_finalize(Depsgraph *graph) } } } +} + +void deg_graph_build_finalize(Depsgraph *graph) +{ + /* STEP 1: Make sure new invisible dependencies are ready for use. + * + * TODO(sergey): This might do a bit of extra tagging, but it's kinda nice + * to do it ahead of a time and don't spend time on flushing updates on + * every frame change. + */ + GHASH_FOREACH_BEGIN(IDDepsNode *, id_node, graph->id_hash) + { + if (id_node->layers == 0) { + ID *id = id_node->id; + if (GS(id->name) == ID_OB) { + Object *object = (Object *)id; + if (check_object_needs_evaluation(object)) { + id_node->tag_update(graph); + } + } + } + } + GHASH_FOREACH_END(); + /* STEP 2: Flush visibility layers from children to parent. */ + deg_graph_build_flush_layers(graph); /* STEP 3: Re-tag IDs for update if it was tagged before the relations * update tag. */ @@ -154,7 +151,7 @@ void deg_graph_build_finalize(Depsgraph *graph) } GHASH_FOREACH_END(); - if ((id_node->layers & graph->layers) != 0) { + if ((id_node->layers & graph->layers) != 0 || graph->layers == 0) { ID *id = id_node->id; if ((id->tag & LIB_TAG_ID_RECALC_ALL) && (id->tag & LIB_TAG_DOIT)) diff --git a/source/blender/depsgraph/intern/builder/deg_builder.h b/source/blender/depsgraph/intern/builder/deg_builder.h index bdc030e3810..b8ea8c8e599 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder.h +++ b/source/blender/depsgraph/intern/builder/deg_builder.h @@ -38,9 +38,7 @@ namespace DEG { struct Depsgraph; -/* Get unique identifier for FCurves and Drivers */ -string deg_fcurve_id_name(const FCurve *fcu); - void deg_graph_build_finalize(struct Depsgraph *graph); +void deg_graph_build_flush_layers(struct Depsgraph *graph); } // namespace DEG diff --git a/source/blender/depsgraph/intern/depsgraph_tag.cc b/source/blender/depsgraph/intern/depsgraph_tag.cc index e8ed03666a6..cfc9005a1e3 100644 --- a/source/blender/depsgraph/intern/depsgraph_tag.cc +++ b/source/blender/depsgraph/intern/depsgraph_tag.cc @@ -56,8 +56,8 @@ extern "C" { #include "DEG_depsgraph.h" } /* extern "C" */ +#include "intern/builder/deg_builder.h" #include "intern/eval/deg_eval_flush.h" - #include "intern/nodes/deg_node.h" #include "intern/nodes/deg_node_component.h" #include "intern/nodes/deg_node_operation.h" @@ -346,6 +346,27 @@ void DEG_graph_on_visible_update(Main *bmain, Scene *scene) GHASH_FOREACH_END(); } scene->lay_updated |= graph->layers; + /* Special trick to get local view to work. */ + LINKLIST_FOREACH (Base *, base, &scene->base) { + Object *object = base->object; + DEG::IDDepsNode *id_node = graph->find_id_node(&object->id); + id_node->layers = 0; + } + LINKLIST_FOREACH (Base *, base, &scene->base) { + Object *object = base->object; + DEG::IDDepsNode *id_node = graph->find_id_node(&object->id); + id_node->layers |= base->lay; + } + DEG::deg_graph_build_flush_layers(graph); + LINKLIST_FOREACH (Base *, base, &scene->base) { + Object *object = base->object; + DEG::IDDepsNode *id_node = graph->find_id_node(&object->id); + GHASH_FOREACH_BEGIN(DEG::ComponentDepsNode *, comp, id_node->components) + { + id_node->layers |= comp->layers; + } + GHASH_FOREACH_END(); + } } void DEG_on_visible_update(Main *bmain, const bool UNUSED(do_time)) diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c index 47f42ab5321..648fe930030 100644 --- a/source/blender/editors/curve/editcurve.c +++ b/source/blender/editors/curve/editcurve.c @@ -6047,6 +6047,9 @@ int join_curve_exec(bContext *C, wmOperator *op) cu = ob->data; BLI_movelisttolist(&cu->nurb, &tempbase); + /* Account for mixed 2D/3D curves when joining */ + BKE_curve_curve_dimension_update(cu); + DAG_relations_tag_update(bmain); // because we removed object(s), call before editmode! DAG_id_tag_update(&ob->id, OB_RECALC_OB | OB_RECALC_DATA); diff --git a/source/blender/editors/gpencil/drawgpencil.c b/source/blender/editors/gpencil/drawgpencil.c index a542bf8fa11..f091609da49 100644 --- a/source/blender/editors/gpencil/drawgpencil.c +++ b/source/blender/editors/gpencil/drawgpencil.c @@ -701,24 +701,25 @@ static void gp_draw_stroke_2d(bGPDspoint *points, int totpoints, short thickness */ { bGPDspoint *pt1, *pt2; - float pm[2]; + float s0[2], s1[2]; /* segment 'center' points */ + float pm[2]; /* normal from previous segment. */ int i; float fpt[3]; glShadeModel(GL_FLAT); glBegin(GL_QUADS); - + + /* get x and y coordinates from first point */ + mul_v3_m4v3(fpt, diff_mat, &points->x); + gp_calc_2d_stroke_fxy(fpt, sflag, offsx, offsy, winx, winy, s0); + for (i = 0, pt1 = points, pt2 = points + 1; i < (totpoints - 1); i++, pt1++, pt2++) { - float s0[2], s1[2]; /* segment 'center' points */ float t0[2], t1[2]; /* tessellated coordinates */ float m1[2], m2[2]; /* gradient and normal */ float mt[2], sc[2]; /* gradient for thickness, point for end-cap */ float pthick; /* thickness at segment point */ - /* get x and y coordinates from points */ - mul_v3_m4v3(fpt, diff_mat, &pt1->x); - gp_calc_2d_stroke_fxy(fpt, sflag, offsx, offsy, winx, winy, s0); - + /* get x and y coordinates from point2 (point1 has already been computed in previous iteration). */ mul_v3_m4v3(fpt, diff_mat, &pt2->x); gp_calc_2d_stroke_fxy(fpt, sflag, offsx, offsy, winx, winy, s1); @@ -846,6 +847,8 @@ static void gp_draw_stroke_2d(bGPDspoint *points, int totpoints, short thickness glVertex2fv(t0); } + /* store computed point2 coordinates as point1 ones of next segment. */ + copy_v2_v2(s0, s1); /* store stroke's 'natural' normal for next stroke to use */ copy_v2_v2(pm, m2); } diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index fa9acc36a2b..601a86b97cb 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -2126,10 +2126,10 @@ static int gp_count_subdivision_cuts(bGPDstroke *gps) 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 (i + 1 < gps->totpoints) { if (gps->points[i + 1].flag & GP_SPOINT_SELECT) { ++totnewpoints; - }; + } } } } @@ -2184,7 +2184,7 @@ static int gp_stroke_subdivide_exec(bContext *C, wmOperator *op) /* if next point is selected add a half way point */ if (pt->flag & GP_SPOINT_SELECT) { - if (i + 1 < oldtotpoints){ + if (i + 1 < oldtotpoints) { if (temp_points[i + 1].flag & GP_SPOINT_SELECT) { pt_final = &gps->points[i2]; /* Interpolate all values */ @@ -2196,7 +2196,7 @@ static int gp_stroke_subdivide_exec(bContext *C, wmOperator *op) pt_final->time = interpf(pt->time, next->time, 0.5f); pt_final->flag |= GP_SPOINT_SELECT; ++i2; - }; + } } } } diff --git a/source/blender/editors/gpencil/gpencil_interpolate.c b/source/blender/editors/gpencil/gpencil_interpolate.c index 297058168a0..d2360fea672 100644 --- a/source/blender/editors/gpencil/gpencil_interpolate.c +++ b/source/blender/editors/gpencil/gpencil_interpolate.c @@ -86,13 +86,14 @@ /* Core/Shared Utilities */ /* Poll callback for interpolation operators */ -static int gpencil_interpolate_poll(bContext *C) +static int gpencil_view3d_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) { + ScrArea *sa = CTX_wm_area(C); + if (sa && sa->spacetype != SPACE_VIEW3D) { return 0; } @@ -673,7 +674,7 @@ void GPENCIL_OT_interpolate(wmOperatorType *ot) ot->invoke = gpencil_interpolate_invoke; ot->modal = gpencil_interpolate_modal; ot->cancel = gpencil_interpolate_cancel; - ot->poll = gpencil_interpolate_poll; + ot->poll = gpencil_view3d_poll; /* flags */ ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING; @@ -1017,7 +1018,7 @@ void GPENCIL_OT_interpolate_sequence(wmOperatorType *ot) /* api callbacks */ ot->exec = gpencil_interpolate_seq_exec; - ot->poll = gpencil_interpolate_poll; + ot->poll = gpencil_view3d_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -1025,24 +1026,14 @@ void GPENCIL_OT_interpolate_sequence(wmOperatorType *ot) /* ******************** Remove Breakdowns ************************ */ -/* Same as gpencil_interpolate_poll(), - * except we ALSO need to have an active frame that is a breakdown - */ static int gpencil_interpolate_reverse_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)) { + if (!gpencil_view3d_poll(C)) { return 0; } - + + bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); + /* need to be on a breakdown frame */ if ((gpl->actframe == NULL) || (gpl->actframe->key_type != BEZT_KEYTYPE_BREAKDOWN)) { CTX_wm_operator_poll_msg_set(C, "Expected current frame to be a breakdown"); diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 9fbce7dd203..4945406c57b 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -421,7 +421,7 @@ typedef void (*uiBlockCancelFunc)(struct bContext *C, void *arg1); void UI_popup_block_invoke(struct bContext *C, uiBlockCreateFunc func, void *arg); void UI_popup_block_invoke_ex(struct bContext *C, uiBlockCreateFunc func, void *arg, const char *opname, int opcontext); -void UI_popup_block_ex(struct bContext *C, uiBlockCreateFunc func, uiBlockHandleFunc popup_func, uiBlockCancelFunc cancel_func, void *arg); +void UI_popup_block_ex(struct bContext *C, uiBlockCreateFunc func, uiBlockHandleFunc popup_func, uiBlockCancelFunc cancel_func, void *arg, struct wmOperator *op); /* void uiPupBlockOperator(struct bContext *C, uiBlockCreateFunc func, struct wmOperator *op, int opcontext); */ /* UNUSED */ void UI_popup_block_close(struct bContext *C, struct wmWindow *win, uiBlock *block); @@ -695,7 +695,7 @@ void UI_but_func_search_set( int UI_searchbox_size_y(void); int UI_searchbox_size_x(void); /* check if a string is in an existing search box */ -int UI_search_items_find_index(uiSearchItems *items, const char *name); +int UI_search_items_find_index(uiSearchItems *items, const char *name, const size_t offset); void UI_block_func_handle_set(uiBlock *block, uiBlockHandleFunc func, void *arg); void UI_block_func_butmenu_set(uiBlock *block, uiMenuHandleFunc func, void *arg); diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index 57f3d228a17..d4b1a7e603a 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -1337,7 +1337,7 @@ static void ui_but_to_pixelrect(rcti *rect, const ARegion *ar, uiBlock *block, u rctf rectf; ui_block_to_window_rctf(ar, block, &rectf, (but) ? &but->rect : &block->rect); - BLI_rcti_rctf_copy_floor(rect, &rectf); + BLI_rcti_rctf_copy(rect, &rectf); BLI_rcti_translate(rect, -ar->winrct.xmin, -ar->winrct.ymin); } @@ -3913,7 +3913,7 @@ uiBut *uiDefIconTextButO_ptr(uiBlock *block, int type, wmOperatorType *ot, int o uiBut *uiDefIconTextButO(uiBlock *block, int type, const char *opname, int opcontext, int icon, const char *str, int x, int y, short width, short height, const char *tip) { wmOperatorType *ot = WM_operatortype_find(opname, 0); - if (str[0] == '\0') + if (str && str[0] == '\0') return uiDefIconButO_ptr(block, type, ot, opcontext, icon, x, y, width, height, tip); return uiDefIconTextButO_ptr(block, type, ot, opcontext, icon, str, x, y, width, height, tip); } @@ -4372,7 +4372,7 @@ void UI_but_func_search_set( if (0 == (but->block->flag & UI_BLOCK_LOOP)) { /* skip empty buttons, not all buttons need input, we only show invalid */ if (but->drawstr[0]) - ui_but_search_refresh(but); + ui_but_search_refresh(but, false); } } diff --git a/source/blender/editors/interface/interface_anim.c b/source/blender/editors/interface/interface_anim.c index 5da294302e9..a04360b3395 100644 --- a/source/blender/editors/interface/interface_anim.c +++ b/source/blender/editors/interface/interface_anim.c @@ -39,6 +39,7 @@ #include "BLI_utildefines.h" #include "BKE_context.h" +#include "BKE_depsgraph.h" #include "BKE_fcurve.h" #include "BKE_global.h" #include "BKE_nla.h" @@ -210,6 +211,7 @@ bool ui_but_anim_expression_create(uiBut *but, const char *str) /* updates */ driver->flag |= DRIVER_FLAG_RECOMPILE; + DAG_relations_tag_update(CTX_data_main(C)); WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME, NULL); ok = true; } diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 6e3c3c3674a..2974c2e9304 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -87,6 +87,7 @@ #include "WM_api.h" #include "WM_types.h" +#include "wm_event_system.h" #ifdef WITH_INPUT_IME # include "wm_window.h" @@ -380,6 +381,7 @@ typedef struct uiAfterFunc { void *butm_func_arg; int a2; + wmOperator *popup_op; wmOperatorType *optype; int opcontext; PointerRNA *opptr; @@ -635,13 +637,24 @@ PointerRNA *ui_handle_afterfunc_add_operator(wmOperatorType *ot, int opcontext, return ptr; } +static void popup_check(bContext *C, wmOperator *op) +{ + if (op && op->type->check && op->type->check(C, op)) { + /* check for popup and re-layout buttons */ + ARegion *ar_menu = CTX_wm_menu(C); + if (ar_menu) + ED_region_tag_refresh_ui(ar_menu); + } +} + /** * Check if a #uiAfterFunc is needed for this button. */ static bool ui_afterfunc_check(const uiBlock *block, const uiBut *but) { return (but->func || but->funcN || but->rename_func || but->optype || but->rnaprop || block->handle_func || - (but->type == UI_BTYPE_BUT_MENU && block->butm_func)); + (but->type == UI_BTYPE_BUT_MENU && block->butm_func) || + (block->handle && block->handle->popup_op)); } static void ui_apply_but_func(bContext *C, uiBut *but) @@ -682,6 +695,9 @@ static void ui_apply_but_func(bContext *C, uiBut *but) after->butm_func_arg = block->butm_func_arg; after->a2 = but->a2; } + + if (block->handle) + after->popup_op = block->handle->popup_op; after->optype = but->optype; after->opcontext = but->opcontext; @@ -766,6 +782,9 @@ static void ui_apply_but_funcs_after(bContext *C) if (after.context) CTX_store_set(C, after.context); + if (after.popup_op) + popup_check(C, after.popup_op); + if (after.opptr) { /* free in advance to avoid leak on exit */ opptr = *after.opptr; @@ -6665,7 +6684,7 @@ static void remove_shortcut_func(bContext *C, void *arg1, void *UNUSED(arg2)) static void popup_add_shortcut_func(bContext *C, void *arg1, void *UNUSED(arg2)) { uiBut *but = (uiBut *)arg1; - UI_popup_block_ex(C, menu_add_shortcut, NULL, menu_add_shortcut_cancel, but); + UI_popup_block_ex(C, menu_add_shortcut, NULL, menu_add_shortcut_cancel, but, NULL); } /** @@ -7005,20 +7024,17 @@ static bool ui_but_menu(bContext *C, uiBut *but) uiItemO(layout, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Online Manual"), ICON_URL, "WM_OT_doc_view_manual_ui_context"); - WM_operator_properties_create(&ptr_props, "WM_OT_doc_view"); + ptr_props = uiItemFullO(layout, "WM_OT_doc_view", + CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Online Python Reference"), + ICON_NONE, NULL, WM_OP_EXEC_DEFAULT, UI_ITEM_O_RETURN_PROPS); RNA_string_set(&ptr_props, "doc_id", buf); - uiItemFullO(layout, "WM_OT_doc_view", - CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Online Python Reference"), - ICON_NONE, ptr_props.data, WM_OP_EXEC_DEFAULT, 0); /* XXX inactive option, not for public! */ #if 0 - WM_operator_properties_create(&ptr_props, "WM_OT_doc_edit"); + ptr_props = uiItemFullO(layout, "WM_OT_doc_edit", + "Submit Description", ICON_NONE, NULL, WM_OP_INVOKE_DEFAULT, UI_ITEM_O_RETURN_PROPS); RNA_string_set(&ptr_props, "doc_id", buf); RNA_string_set(&ptr_props, "doc_new", RNA_property_description(but->rnaprop)); - - uiItemFullO(layout, "WM_OT_doc_edit", - "Submit Description", ICON_NONE, ptr_props.data, WM_OP_INVOKE_DEFAULT, 0); #endif } } @@ -10182,6 +10198,25 @@ void UI_popup_handlers_add(bContext *C, ListBase *handlers, uiPopupBlockHandle * void UI_popup_handlers_remove(ListBase *handlers, uiPopupBlockHandle *popup) { + wmEventHandler *handler; + + for (handler = handlers->first; handler; handler = handler->next) { + if (handler->ui_handle == ui_popup_handler && + handler->ui_remove == ui_popup_handler_remove && + handler->ui_userdata == popup) + { + /* tag refresh parent popup */ + if (handler->next && + handler->next->ui_handle == ui_popup_handler && + handler->next->ui_remove == ui_popup_handler_remove) + { + uiPopupBlockHandle *parent_popup = handler->next->ui_userdata; + ED_region_tag_refresh_ui(parent_popup->region); + } + break; + } + } + WM_event_remove_ui_handler(handlers, ui_popup_handler, ui_popup_handler_remove, popup, false); } diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index d8f9fdcbaae..6706298c3c0 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -556,6 +556,7 @@ struct uiPopupBlockHandle { struct uiKeyNavLock keynav_state; /* for operator popups */ + struct wmOperator *popup_op; struct wmOperatorType *optype; ScrArea *ctx_area; ARegion *ctx_region; @@ -604,7 +605,7 @@ int ui_searchbox_autocomplete(struct bContext *C, struct ARegion *ar, uiBut *but void ui_searchbox_event(struct bContext *C, struct ARegion *ar, uiBut *but, const struct wmEvent *event); bool ui_searchbox_apply(uiBut *but, struct ARegion *ar); void ui_searchbox_free(struct bContext *C, struct ARegion *ar); -void ui_but_search_refresh(uiBut *but); +void ui_but_search_refresh(uiBut *but, const bool is_template_ID); uiBlock *ui_popup_block_refresh( struct bContext *C, uiPopupBlockHandle *handle, diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c index c62fb4cadd6..30a2094fee7 100644 --- a/source/blender/editors/interface/interface_layout.c +++ b/source/blender/editors/interface/interface_layout.c @@ -1274,7 +1274,8 @@ static void ui_item_rna_size( if (!w) { if (type == PROP_ENUM && icon_only) { w = ui_text_icon_width(layout, "", ICON_BLANK1, 0); - w += 0.6f * UI_UNIT_X; + if (index != RNA_ENUM_VALUE) + w += 0.6f * UI_UNIT_X; } else { w = ui_text_icon_width(layout, name, icon, 0); @@ -2171,7 +2172,17 @@ static void ui_litem_layout_row(uiLayout *litem) x += neww; - if ((neww < minw || itemw == minw || item->flag & UI_ITEM_MIN) && w != 0) { + bool min_flag = item->flag & UI_ITEM_MIN; + /* ignore min flag for rows with right or center alignment */ + if (item->type != ITEM_BUTTON && + ELEM(((uiLayout *)item)->alignment, UI_LAYOUT_ALIGN_RIGHT, UI_LAYOUT_ALIGN_CENTER) && + litem->alignment == UI_LAYOUT_ALIGN_EXPAND && + ((uiItem *)litem)->flag & UI_ITEM_MIN) + { + min_flag = false; + } + + if ((neww < minw || min_flag) && w != 0) { /* fixed size */ item->flag |= UI_ITEM_FIXED; if (item->type != ITEM_BUTTON && item->flag & UI_ITEM_MIN) { @@ -2253,10 +2264,11 @@ static void ui_litem_layout_row(uiLayout *litem) } /* single-column layout */ -static void ui_litem_estimate_column(uiLayout *litem) +static void ui_litem_estimate_column(uiLayout *litem, bool is_box) { uiItem *item; int itemw, itemh; + bool min_size_flag = true; litem->w = 0; litem->h = 0; @@ -2264,15 +2276,21 @@ static void ui_litem_estimate_column(uiLayout *litem) for (item = litem->items.first; item; item = item->next) { ui_item_size(item, &itemw, &itemh); + min_size_flag = min_size_flag && (item->flag & UI_ITEM_MIN); + litem->w = MAX2(litem->w, itemw); litem->h += itemh; - if (item->next) + if (item->next && (!is_box || item != litem->items.first)) litem->h += litem->space; } + + if (min_size_flag) { + litem->item.flag |= UI_ITEM_MIN; + } } -static void ui_litem_layout_column(uiLayout *litem) +static void ui_litem_layout_column(uiLayout *litem, bool is_box) { uiItem *item; int itemh, x, y; @@ -2286,7 +2304,7 @@ static void ui_litem_layout_column(uiLayout *litem) y -= itemh; ui_item_position(item, x, y, litem->w, itemh); - if (item->next) + if (item->next && (!is_box || item != litem->items.first)) y -= litem->space; } @@ -2428,7 +2446,7 @@ static void ui_litem_layout_root(uiLayout *litem) else if (litem->root->type == UI_LAYOUT_PIEMENU) ui_litem_layout_root_radial(litem); else - ui_litem_layout_column(litem); + ui_litem_layout_column(litem, false); } /* box layout */ @@ -2436,9 +2454,10 @@ static void ui_litem_estimate_box(uiLayout *litem) { uiStyle *style = litem->root->style; - ui_litem_estimate_column(litem); + ui_litem_estimate_column(litem, true); + litem->item.flag &= ~UI_ITEM_MIN; litem->w += 2 * style->boxspace; - litem->h += style->boxspace; + litem->h += 2 * style->boxspace; } static void ui_litem_layout_box(uiLayout *litem) @@ -2452,17 +2471,18 @@ static void ui_litem_layout_box(uiLayout *litem) h = litem->h; litem->x += style->boxspace; + litem->y -= style->boxspace; if (w != 0) litem->w -= 2 * style->boxspace; if (h != 0) litem->h -= 2 * style->boxspace; - ui_litem_layout_column(litem); + ui_litem_layout_column(litem, true); litem->x -= style->boxspace; litem->y -= style->boxspace; if (w != 0) litem->w += 2 * style->boxspace; - if (h != 0) litem->h += style->boxspace; + if (h != 0) litem->h += 2 * style->boxspace; /* roundbox around the sublayout */ but = box->roundbox; @@ -3115,7 +3135,7 @@ static void ui_item_estimate(uiItem *item) switch (litem->item.type) { case ITEM_LAYOUT_COLUMN: - ui_litem_estimate_column(litem); + ui_litem_estimate_column(litem, false); break; case ITEM_LAYOUT_COLUMN_FLOW: ui_litem_estimate_column_flow(litem); @@ -3170,7 +3190,9 @@ static void ui_item_align(uiLayout *litem, short nr) } else if (item->type == ITEM_LAYOUT_BOX) { box = (uiLayoutItemBx *)item; - box->roundbox->alignnr = nr; + if (!box->roundbox->alignnr) { + box->roundbox->alignnr = nr; + } } else if (((uiLayout *)item)->align) { ui_item_align((uiLayout *)item, nr); @@ -3212,7 +3234,7 @@ static void ui_item_layout(uiItem *item) switch (litem->item.type) { case ITEM_LAYOUT_COLUMN: - ui_litem_layout_column(litem); + ui_litem_layout_column(litem, false); break; case ITEM_LAYOUT_COLUMN_FLOW: ui_litem_layout_column_flow(litem); @@ -3556,14 +3578,13 @@ void uiLayoutOperatorButs( row = uiLayoutRow(layout, true); uiItemM(row, (bContext *)C, "WM_MT_operator_presets", NULL, ICON_NONE); - WM_operator_properties_create(&op_ptr, "WM_OT_operator_preset_add"); + wmOperatorType *ot = WM_operatortype_find("WM_OT_operator_preset_add", false); + op_ptr = uiItemFullO_ptr(row, ot, "", ICON_ZOOMIN, NULL, WM_OP_INVOKE_DEFAULT, UI_ITEM_O_RETURN_PROPS); RNA_string_set(&op_ptr, "operator", op->type->idname); - uiItemFullO(row, "WM_OT_operator_preset_add", "", ICON_ZOOMIN, op_ptr.data, WM_OP_INVOKE_DEFAULT, 0); - WM_operator_properties_create(&op_ptr, "WM_OT_operator_preset_add"); + op_ptr = uiItemFullO_ptr(row, ot, "", ICON_ZOOMOUT, NULL, WM_OP_INVOKE_DEFAULT, UI_ITEM_O_RETURN_PROPS); RNA_string_set(&op_ptr, "operator", op->type->idname); RNA_boolean_set(&op_ptr, "remove_active", true); - uiItemFullO(row, "WM_OT_operator_preset_add", "", ICON_ZOOMOUT, op_ptr.data, WM_OP_INVOKE_DEFAULT, 0); } if (op->type->ui) { diff --git a/source/blender/editors/interface/interface_regions.c b/source/blender/editors/interface/interface_regions.c index 466978272bc..0d9d8c4f887 100644 --- a/source/blender/editors/interface/interface_regions.c +++ b/source/blender/editors/interface/interface_regions.c @@ -811,11 +811,11 @@ int UI_searchbox_size_x(void) return 12 * UI_UNIT_X; } -int UI_search_items_find_index(uiSearchItems *items, const char *name) +int UI_search_items_find_index(uiSearchItems *items, const char *name, const size_t offset) { int i; for (i = 0; i < items->totitem; i++) { - if (STREQ(name, items->names[i])) { + if (STREQ(name, items->names[i] + offset)) { return i; } } @@ -894,7 +894,7 @@ static void ui_searchbox_butrect(rcti *r_rect, uiSearchboxData *data, int itemnr int ui_searchbox_find_index(ARegion *ar, const char *name) { uiSearchboxData *data = ar->regiondata; - return UI_search_items_find_index(&data->items, name); + return UI_search_items_find_index(&data->items, name, 0); } /* x and y in screencoords */ @@ -1420,14 +1420,14 @@ void ui_searchbox_free(bContext *C, ARegion *ar) /* sets red alert if button holds a string it can't find */ /* XXX weak: search_func adds all partial matches... */ -void ui_but_search_refresh(uiBut *but) +void ui_but_search_refresh(uiBut *but, const bool is_template_ID) { uiSearchItems *items; int x1; - /* possibly very large lists (such as ID datablocks) only - * only validate string RNA buts (not pointers) */ - if (but->rnaprop && RNA_property_type(but->rnaprop) != PROP_STRING) { + /* possibly very large lists (such as ID datablocks), + * only validate string and pointer RNA buts */ + if (but->rnaprop && !ELEM(RNA_property_type(but->rnaprop), PROP_STRING, PROP_POINTER)) { return; } @@ -1447,7 +1447,8 @@ void ui_but_search_refresh(uiBut *but) UI_but_flag_enable(but, UI_BUT_REDALERT); } else if (items->more == 0) { - if (UI_search_items_find_index(items, but->drawstr) == -1) { + const size_t offset = is_template_ID ? 3 : 0; + if (UI_search_items_find_index(items, but->drawstr, offset) == -1) { UI_but_flag_enable(but, UI_BUT_REDALERT); } } @@ -1692,6 +1693,28 @@ static void ui_block_region_draw(const bContext *C, ARegion *ar) UI_block_draw(C, block); } +/** + * Use to refresh centered popups on screen resizing (for splash). + */ +static void ui_block_region_popup_window_listener( + bScreen *UNUSED(sc), ScrArea *UNUSED(sa), ARegion *ar, wmNotifier *wmn) +{ + switch (wmn->category) { + case NC_WINDOW: + { + switch (wmn->action) { + case NA_EDITED: + { + /* window resize */ + ED_region_tag_refresh_ui(ar); + break; + } + } + break; + } + } +} + static void ui_popup_block_clip(wmWindow *window, uiBlock *block) { uiBut *bt; @@ -2003,6 +2026,11 @@ uiPopupBlockHandle *ui_popup_block_create( block = ui_popup_block_refresh(C, handle, butregion, but); handle = block->handle; + /* keep centered on window resizing */ + if ((block->bounds_type == UI_BLOCK_BOUNDS_POPUP_CENTER) && handle->can_refresh) { + type.listener = ui_block_region_popup_window_listener; + } + return handle; } @@ -3285,7 +3313,7 @@ void UI_popup_block_invoke(bContext *C, uiBlockCreateFunc func, void *arg) UI_popup_block_invoke_ex(C, func, arg, NULL, WM_OP_INVOKE_DEFAULT); } -void UI_popup_block_ex(bContext *C, uiBlockCreateFunc func, uiBlockHandleFunc popup_func, uiBlockCancelFunc cancel_func, void *arg) +void UI_popup_block_ex(bContext *C, uiBlockCreateFunc func, uiBlockHandleFunc popup_func, uiBlockCancelFunc cancel_func, void *arg, wmOperator *op) { wmWindow *window = CTX_wm_window(C); uiPopupBlockHandle *handle; @@ -3294,6 +3322,7 @@ void UI_popup_block_ex(bContext *C, uiBlockCreateFunc func, uiBlockHandleFunc po handle->popup = true; handle->retvalue = 1; + handle->popup_op = op; handle->popup_arg = arg; handle->popup_func = popup_func; handle->cancel_func = cancel_func; diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 131584dd405..a3541e641ed 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -451,6 +451,11 @@ static void template_ID( but = uiDefButR(block, UI_BTYPE_TEXT, 0, name, 0, 0, UI_UNIT_X * 6, UI_UNIT_Y, &idptr, "name", -1, 0, 0, -1, -1, RNA_struct_ui_description(type)); UI_but_funcN_set(but, template_id_cb, MEM_dupallocN(template), SET_INT_IN_POINTER(UI_ID_RENAME)); + + but->search_func = id_search_cb; + but->search_arg = template; + ui_but_search_refresh(but, true); + if (user_alert) UI_but_flag_enable(but, UI_BUT_REDALERT); if (id->lib) { diff --git a/source/blender/editors/interface/interface_utils.c b/source/blender/editors/interface/interface_utils.c index df6f098ee81..636b7e4e9ce 100644 --- a/source/blender/editors/interface/interface_utils.c +++ b/source/blender/editors/interface/interface_utils.c @@ -127,12 +127,10 @@ uiBut *uiDefAutoButR(uiBlock *block, PointerRNA *ptr, PropertyRNA *prop, int ind break; case PROP_POINTER: { - PointerRNA pptr; - - pptr = RNA_property_pointer_get(ptr, prop); - if (!pptr.type) - pptr.type = RNA_property_pointer_type(ptr, prop); - icon = RNA_struct_ui_icon(pptr.type); + if (icon == 0) { + PointerRNA pptr = RNA_property_pointer_get(ptr, prop); + icon = RNA_struct_ui_icon(pptr.type ? pptr.type : RNA_property_pointer_type(ptr, prop)); + } if (icon == ICON_DOT) icon = 0; diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index 5ec0c49a8cd..f7f2b422724 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -874,24 +874,18 @@ static void widget_draw_icon( float ofs = 1.0f / aspect; if (but->drawflag & UI_BUT_ICON_LEFT) { - if (but->block->flag & UI_BLOCK_LOOP) { - if (but->type == UI_BTYPE_SEARCH_MENU) - xs = rect->xmin + 4.0f * ofs; - else - xs = rect->xmin + ofs; - } - else { - if (but->dt == UI_EMBOSS_NONE || but->type == UI_BTYPE_LABEL) - xs = rect->xmin + 2.0f * ofs; - else - xs = rect->xmin + 4.0f * ofs; - } - ys = (rect->ymin + rect->ymax - height) / 2.0f; + /* special case - icon_only pie buttons */ + if (ui_block_is_pie_menu(but->block) && but->type != UI_BTYPE_MENU && but->str && but->str[0] == '\0') + xs = rect->xmin + 2.0f * ofs; + else if (but->dt == UI_EMBOSS_NONE || but->type == UI_BTYPE_LABEL) + xs = rect->xmin + 2.0f * ofs; + else + xs = rect->xmin + 4.0f * ofs; } else { xs = (rect->xmin + rect->xmax - height) / 2.0f; - ys = (rect->ymin + rect->ymax - height) / 2.0f; } + ys = (rect->ymin + rect->ymax - height) / 2.0f; /* force positions to integers, for zoom levels near 1. draws icons crisp. */ if (aspect > 0.95f && aspect < 1.05f) { diff --git a/source/blender/editors/io/io_collada.c b/source/blender/editors/io/io_collada.c index 0e185cd93a7..139c9817437 100644 --- a/source/blender/editors/io/io_collada.c +++ b/source/blender/editors/io/io_collada.c @@ -97,7 +97,10 @@ static int wm_collada_export_exec(bContext *C, wmOperator *op) int use_blender_profile; int sort_by_name; int export_transformation_type; + int open_sim; + int limit_precision; + int keep_bind_info; int export_count; @@ -148,6 +151,9 @@ static int wm_collada_export_exec(bContext *C, wmOperator *op) export_transformation_type = RNA_enum_get(op->ptr, "export_transformation_type_selection"); open_sim = RNA_boolean_get(op->ptr, "open_sim"); + limit_precision = RNA_boolean_get(op->ptr, "limit_precision"); + keep_bind_info = RNA_boolean_get(op->ptr, "keep_bind_info"); + /* get editmode results */ ED_object_editmode_load(CTX_data_edit_object(C)); @@ -172,7 +178,11 @@ static int wm_collada_export_exec(bContext *C, wmOperator *op) use_blender_profile, sort_by_name, export_transformation_type, - open_sim); + + open_sim, + limit_precision, + keep_bind_info + ); if (export_count == 0) { BKE_report(op->reports, RPT_WARNING, "No objects selected -- Created empty export file"); @@ -270,6 +280,12 @@ static void uiCollada_exportSettings(uiLayout *layout, PointerRNA *imfptr) row = uiLayoutRow(box, false); uiItemR(row, imfptr, "sort_by_name", 0, NULL, ICON_NONE); + row = uiLayoutRow(box, false); + uiItemR(row, imfptr, "keep_bind_info", 0, NULL, ICON_NONE); + + row = uiLayoutRow(box, false); + uiItemR(row, imfptr, "limit_precision", 0, NULL, ICON_NONE); + } static void wm_collada_export_draw(bContext *UNUSED(C), wmOperator *op) @@ -386,6 +402,13 @@ void WM_OT_collada_export(wmOperatorType *ot) RNA_def_boolean(func, "open_sim", 0, "Export to SL/OpenSim", "Compatibility mode for SL, OpenSim and other compatible online worlds"); + + RNA_def_boolean(func, "limit_precision", 0, + "Limit Precision", "Reduce the precision of the exported data to 6 digits"); + + RNA_def_boolean(func, "keep_bind_info", 0, + "Keep Bind Info", "Store Bindpose information in custom bone properties for later use during Collada export"); + } @@ -397,7 +420,9 @@ static int wm_collada_import_exec(bContext *C, wmOperator *op) int find_chains; int auto_connect; int fix_orientation; - int min_chain_length; + int min_chain_length; + + int keep_bind_info; if (!RNA_struct_property_is_set(op->ptr, "filepath")) { BKE_report(op->reports, RPT_ERROR, "No filename given"); @@ -409,6 +434,9 @@ static int wm_collada_import_exec(bContext *C, wmOperator *op) find_chains = RNA_boolean_get(op->ptr, "find_chains"); auto_connect = RNA_boolean_get(op->ptr, "auto_connect"); fix_orientation = RNA_boolean_get(op->ptr, "fix_orientation"); + + keep_bind_info = RNA_boolean_get(op->ptr, "keep_bind_info"); + min_chain_length = RNA_int_get(op->ptr, "min_chain_length"); RNA_string_get(op->ptr, "filepath", filename); @@ -418,7 +446,8 @@ static int wm_collada_import_exec(bContext *C, wmOperator *op) find_chains, auto_connect, fix_orientation, - min_chain_length)) + min_chain_length, + keep_bind_info) ) { return OPERATOR_FINISHED; } @@ -455,6 +484,13 @@ static void uiCollada_importSettings(uiLayout *layout, PointerRNA *imfptr) row = uiLayoutRow(box, false); uiItemR(row, imfptr, "min_chain_length", 0, NULL, ICON_NONE); + + box = uiLayoutBox(layout); + row = uiLayoutRow(box, false); + + row = uiLayoutRow(box, false); + uiItemR(row, imfptr, "keep_bind_info", 0, NULL, ICON_NONE); + } static void wm_collada_import_draw(bContext *UNUSED(C), wmOperator *op) @@ -510,5 +546,9 @@ void WM_OT_collada_import(wmOperatorType *ot) 0, INT_MAX); + RNA_def_boolean(ot->srna, + "keep_bind_info", 0, "Keep Bind Info", + "Store Bindpose information in custom bone properties for later use during Collada export"); + } #endif diff --git a/source/blender/editors/mesh/meshtools.c b/source/blender/editors/mesh/meshtools.c index 743efb246ab..7faa1dd99e1 100644 --- a/source/blender/editors/mesh/meshtools.c +++ b/source/blender/editors/mesh/meshtools.c @@ -57,6 +57,7 @@ #include "BKE_main.h" #include "BKE_mesh.h" #include "BKE_material.h" +#include "BKE_object.h" #include "BKE_report.h" #include "BKE_editmesh.h" #include "BKE_multires.h" @@ -596,6 +597,9 @@ int join_mesh_exec(bContext *C, wmOperator *op) BKE_key_sort(key); } + /* Due to dependnecy cycle some other object might access old derived data. */ + BKE_object_free_derived_caches(ob); + DAG_relations_tag_update(bmain); /* removed objects, need to rebuild dag */ DAG_id_tag_update(&ob->id, OB_RECALC_OB | OB_RECALC_DATA); diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c index d601f5c3b14..1aa1407797b 100644 --- a/source/blender/editors/object/object_modifier.c +++ b/source/blender/editors/object/object_modifier.c @@ -793,7 +793,7 @@ void OBJECT_OT_modifier_add(wmOperatorType *ot) /* identifiers */ ot->name = "Add Modifier"; - ot->description = "Add a modifier to the active object"; + ot->description = "Add a procedural operation/effect to the active object"; ot->idname = "OBJECT_OT_modifier_add"; /* api callbacks */ @@ -1484,7 +1484,6 @@ static int skin_root_mark_exec(bContext *C, wmOperator *UNUSED(op)) Object *ob = CTX_data_edit_object(C); BMEditMesh *em = BKE_editmesh_from_object(ob); BMesh *bm = em->bm; - const int cd_vert_skin_offset = CustomData_get_offset(&bm->vdata, CD_MVERT_SKIN); BMVert *bm_vert; BMIter bm_iter; GSet *visited; @@ -1493,6 +1492,8 @@ static int skin_root_mark_exec(bContext *C, wmOperator *UNUSED(op)) BKE_mesh_ensure_skin_customdata(ob->data); + const int cd_vert_skin_offset = CustomData_get_offset(&bm->vdata, CD_MVERT_SKIN); + BM_ITER_MESH (bm_vert, &bm_iter, bm, BM_VERTS_OF_MESH) { if (BM_elem_flag_test(bm_vert, BM_ELEM_SELECT) && BLI_gset_add(visited, bm_vert)) diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index b5fbe4ba586..861e249b0ee 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -72,6 +72,7 @@ #include "BKE_global.h" #include "BKE_group.h" #include "BKE_fcurve.h" +#include "BKE_idprop.h" #include "BKE_lamp.h" #include "BKE_lattice.h" #include "BKE_library.h" @@ -2101,6 +2102,34 @@ void ED_object_single_users(Main *bmain, Scene *scene, const bool full, const bo single_tex_users_expand(bmain); } + /* Relink datablock pointer properties */ + { + IDP_RelinkProperty(scene->id.properties); + + for (Base *base = scene->base.first; base; base = base->next) { + Object *ob = base->object; + if (!ID_IS_LINKED_DATABLOCK(ob)) { + IDP_RelinkProperty(ob->id.properties); + } + } + + if (scene->nodetree) { + IDP_RelinkProperty(scene->nodetree->id.properties); + for (bNode *node = scene->nodetree->nodes.first; node; node = node->next) { + IDP_RelinkProperty(node->prop); + } + } + + if (scene->gpd) { + IDP_RelinkProperty(scene->gpd->id.properties); + } + + IDP_RelinkProperty(scene->world->id.properties); + + if (scene->clip) { + IDP_RelinkProperty(scene->clip->id.properties); + } + } BKE_main_id_clear_newpoins(bmain); DAG_relations_tag_update(bmain); } diff --git a/source/blender/editors/render/render_shading.c b/source/blender/editors/render/render_shading.c index 837573ad175..0878636d0fa 100644 --- a/source/blender/editors/render/render_shading.c +++ b/source/blender/editors/render/render_shading.c @@ -611,7 +611,7 @@ void WORLD_OT_new(wmOperatorType *ot) /* identifiers */ ot->name = "New World"; ot->idname = "WORLD_OT_new"; - ot->description = "Add a new world"; + ot->description = "Create a new world Data-Block"; /* api callbacks */ ot->exec = new_world_exec; diff --git a/source/blender/editors/screen/glutil.c b/source/blender/editors/screen/glutil.c index 93bac3f6660..216cbe9d7f4 100644 --- a/source/blender/editors/screen/glutil.c +++ b/source/blender/editors/screen/glutil.c @@ -566,7 +566,7 @@ void glaDrawPixelsSafe(float x, float y, int img_w, int img_h, int row_w, int fo float rast_x = x + off_x * xzoom; float rast_y = y + off_y * yzoom; - GLfloat scissor[4]; + GLfloat viewport[4]; int draw_w, draw_h; /* Determine the smallest number of pixels we need to draw @@ -581,9 +581,9 @@ void glaDrawPixelsSafe(float x, float y, int img_w, int img_h, int row_w, int fo * fails if we zoom in on one really huge pixel so that it * covers the entire screen). */ - glGetFloatv(GL_SCISSOR_BOX, scissor); - draw_w = min_ii(img_w - off_x, ceil((scissor[2] - rast_x) / xzoom)); - draw_h = min_ii(img_h - off_y, ceil((scissor[3] - rast_y) / yzoom)); + glGetFloatv(GL_VIEWPORT, viewport); + draw_w = min_ii(img_w - off_x, ceil((viewport[2] - rast_x) / xzoom)); + draw_h = min_ii(img_h - off_y, ceil((viewport[3] - rast_y) / yzoom)); if (draw_w > 0 && draw_h > 0) { diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index d88c36a8e24..c2e094fc161 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -2808,7 +2808,7 @@ static int screen_area_options_invoke(bContext *C, wmOperator *op, const wmEvent bScreen *sc = CTX_wm_screen(C); uiPopupMenu *pup; uiLayout *layout; - PointerRNA ptr1, ptr2; + PointerRNA ptr; ScrEdge *actedge; const int winsize_x = WM_window_pixels_x(win); const int winsize_y = WM_window_pixels_y(win); @@ -2820,22 +2820,17 @@ static int screen_area_options_invoke(bContext *C, wmOperator *op, const wmEvent pup = UI_popup_menu_begin(C, RNA_struct_ui_name(op->type->srna), ICON_NONE); layout = UI_popup_menu_layout(pup); - WM_operator_properties_create(&ptr1, "SCREEN_OT_area_join"); - - /* mouse cursor on edge, '4' can fail on wide edges... */ - RNA_int_set(&ptr1, "min_x", event->x + 4); - RNA_int_set(&ptr1, "min_y", event->y + 4); - RNA_int_set(&ptr1, "max_x", event->x - 4); - RNA_int_set(&ptr1, "max_y", event->y - 4); - - WM_operator_properties_create(&ptr2, "SCREEN_OT_area_split"); - + ptr = uiItemFullO(layout, "SCREEN_OT_area_split", NULL, ICON_NONE, NULL, WM_OP_INVOKE_DEFAULT, UI_ITEM_O_RETURN_PROPS); /* store initial mouse cursor position */ - RNA_int_set(&ptr2, "mouse_x", event->x); - RNA_int_set(&ptr2, "mouse_y", event->y); - - uiItemFullO(layout, "SCREEN_OT_area_split", NULL, ICON_NONE, ptr2.data, WM_OP_INVOKE_DEFAULT, 0); - uiItemFullO(layout, "SCREEN_OT_area_join", NULL, ICON_NONE, ptr1.data, WM_OP_INVOKE_DEFAULT, 0); + RNA_int_set(&ptr, "mouse_x", event->x); + RNA_int_set(&ptr, "mouse_y", event->y); + + ptr = uiItemFullO(layout, "SCREEN_OT_area_join", NULL, ICON_NONE, NULL, WM_OP_INVOKE_DEFAULT, UI_ITEM_O_RETURN_PROPS); + /* mouse cursor on edge, '4' can fail on wide edges... */ + RNA_int_set(&ptr, "min_x", event->x + 4); + RNA_int_set(&ptr, "min_y", event->y + 4); + RNA_int_set(&ptr, "max_x", event->x - 4); + RNA_int_set(&ptr, "max_y", event->y - 4); UI_popup_menu_end(C, pup); diff --git a/source/blender/editors/sound/sound_ops.c b/source/blender/editors/sound/sound_ops.c index c2c52f58181..e273d3a40f0 100644 --- a/source/blender/editors/sound/sound_ops.c +++ b/source/blender/editors/sound/sound_ops.c @@ -670,7 +670,7 @@ static void SOUND_OT_mixdown(wmOperatorType *ot) /* identifiers */ ot->name = "Mixdown"; - ot->description = "Mixes the scene's audio to a sound file"; + ot->description = "Mix the scene's audio to a sound file"; ot->idname = "SOUND_OT_mixdown"; /* api callbacks */ diff --git a/source/blender/editors/space_clip/clip_utils.c b/source/blender/editors/space_clip/clip_utils.c index b7871225306..646541c7bd9 100644 --- a/source/blender/editors/space_clip/clip_utils.c +++ b/source/blender/editors/space_clip/clip_utils.c @@ -72,7 +72,7 @@ void clip_graph_tracking_values_iterate_track( BKE_movieclip_get_size(clip, &sc->user, &width, &height); for (coord = 0; coord < 2; coord++) { - int i, prevfra = 0; + int i, prevfra = track->markers[0].framenr; bool open = false; float prevval = 0.0f; @@ -179,6 +179,7 @@ void clip_delete_track(bContext *C, MovieClip *clip, MovieTrackingTrack *track) ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking); bool has_bundle = false; char track_name_escaped[MAX_NAME], prefix[MAX_NAME * 2]; + const bool used_for_stabilization = (track->flag & (TRACK_USE_2D_STAB | TRACK_USE_2D_STAB_ROT)); if (track == act_track) tracking->act_track = NULL; @@ -200,7 +201,7 @@ void clip_delete_track(bContext *C, MovieClip *clip, MovieTrackingTrack *track) WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip); - if (track->flag & (TRACK_USE_2D_STAB | TRACK_USE_2D_STAB_ROT)) { + if (used_for_stabilization) { WM_event_add_notifier(C, NC_MOVIECLIP | ND_DISPLAY, clip); } diff --git a/source/blender/editors/space_file/fsmenu.c b/source/blender/editors/space_file/fsmenu.c index d7aa1040e0f..71d49e0dc2e 100644 --- a/source/blender/editors/space_file/fsmenu.c +++ b/source/blender/editors/space_file/fsmenu.c @@ -169,12 +169,15 @@ void ED_fsmenu_entry_set_path(struct FSMenuEntry *fsentry, const char *path) static void fsmenu_entry_generate_name(struct FSMenuEntry *fsentry, char *name, size_t name_size) { - char temp[FILE_MAX]; + int offset = 0; + int len = name_size; - BLI_strncpy(temp, fsentry->path, FILE_MAX); - BLI_add_slash(temp); - BLI_getlastdir(temp, name, name_size); - BLI_del_slash(name); + if (BLI_path_name_at_index(fsentry->path, -1, &offset, &len)) { + /* use as size */ + len += 1; + } + + BLI_strncpy(name, &fsentry->path[offset], MIN2(len, name_size)); if (!name[0]) { name[0] = '/'; name[1] = '\0'; diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c index d31a475e45e..e4f5802a395 100644 --- a/source/blender/editors/space_node/drawnode.c +++ b/source/blender/editors/space_node/drawnode.c @@ -1320,9 +1320,6 @@ static void node_composit_buts_renderlayers(uiLayout *layout, bContext *C, Point PropertyRNA *prop; const char *layer_name; char scene_name[MAX_ID_NAME - 2]; - wmOperatorType *ot = WM_operatortype_find("RENDER_OT_render", 1); - - BLI_assert(ot != 0); uiTemplateID(layout, C, ptr, "scene", NULL, NULL, NULL); @@ -1339,11 +1336,9 @@ static void node_composit_buts_renderlayers(uiLayout *layout, bContext *C, Point scn_ptr = RNA_pointer_get(ptr, "scene"); RNA_string_get(&scn_ptr, "name", scene_name); - WM_operator_properties_create_ptr(&op_ptr, ot); + op_ptr = uiItemFullO(row, "RENDER_OT_render", "", ICON_RENDER_STILL, NULL, WM_OP_INVOKE_DEFAULT, UI_ITEM_O_RETURN_PROPS); RNA_string_set(&op_ptr, "layer", layer_name); RNA_string_set(&op_ptr, "scene", scene_name); - uiItemFullO_ptr(row, ot, "", ICON_RENDER_STILL, op_ptr.data, WM_OP_INVOKE_DEFAULT, 0); - } @@ -1792,6 +1787,7 @@ static void node_composit_buts_file_output_ex(uiLayout *layout, bContext *C, Poi Scene *scene = CTX_data_scene(C); PointerRNA imfptr = RNA_pointer_get(ptr, "format"); PointerRNA active_input_ptr, op_ptr; + wmOperatorType *ot; uiLayout *row, *col; int active_index; const bool multilayer = RNA_enum_get(&imfptr, "file_format") == R_IMF_IMTYPE_MULTILAYER; @@ -1830,11 +1826,10 @@ static void node_composit_buts_file_output_ex(uiLayout *layout, bContext *C, Poi active_input_ptr.id.data = ptr->id.data; col = uiLayoutColumn(row, true); - op_ptr = uiItemFullO(col, "NODE_OT_output_file_move_active_socket", "", - ICON_TRIA_UP, NULL, WM_OP_INVOKE_DEFAULT, UI_ITEM_O_RETURN_PROPS); + ot = WM_operatortype_find("NODE_OT_output_file_move_active_socket", false); + op_ptr = uiItemFullO_ptr(col, ot, "", ICON_TRIA_UP, NULL, WM_OP_INVOKE_DEFAULT, UI_ITEM_O_RETURN_PROPS); RNA_enum_set(&op_ptr, "direction", 1); - op_ptr = uiItemFullO(col, "NODE_OT_output_file_move_active_socket", "", - ICON_TRIA_DOWN, NULL, WM_OP_INVOKE_DEFAULT, UI_ITEM_O_RETURN_PROPS); + op_ptr = uiItemFullO_ptr(col, ot, "", ICON_TRIA_DOWN, NULL, WM_OP_INVOKE_DEFAULT, UI_ITEM_O_RETURN_PROPS); RNA_enum_set(&op_ptr, "direction", 2); if (active_input_ptr.data) { @@ -2166,14 +2161,7 @@ static void node_composit_buts_switch(uiLayout *layout, bContext *UNUSED(C), Poi static void node_composit_buts_switch_view_ex(uiLayout *layout, bContext *UNUSED(C), PointerRNA *UNUSED(ptr)) { - PointerRNA op_ptr; - wmOperatorType *ot = WM_operatortype_find("NODE_OT_switch_view_update", 1); - - BLI_assert(ot != 0); - - WM_operator_properties_create_ptr(&op_ptr, ot); - - uiItemFullO_ptr(layout, ot, "Update Views", ICON_FILE_REFRESH, op_ptr.data, WM_OP_INVOKE_DEFAULT, 0); + uiItemFullO(layout, "NODE_OT_switch_view_update", "Update Views", ICON_FILE_REFRESH, NULL, WM_OP_INVOKE_DEFAULT, 0); } static void node_composit_buts_boxmask(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/editors/space_node/node_add.c b/source/blender/editors/space_node/node_add.c index d49df2afbd4..bcd0f6623e1 100644 --- a/source/blender/editors/space_node/node_add.c +++ b/source/blender/editors/space_node/node_add.c @@ -44,6 +44,8 @@ #include "BKE_main.h" #include "BKE_node.h" #include "BKE_report.h" +#include "BKE_scene.h" +#include "BKE_texture.h" #include "ED_node.h" /* own include */ #include "ED_screen.h" @@ -312,7 +314,10 @@ static int node_add_file_exec(bContext *C, wmOperator *op) switch (snode->nodetree->type) { case NTREE_SHADER: - type = SH_NODE_TEX_IMAGE; + if (BKE_scene_use_new_shading_nodes(CTX_data_scene(C))) + type = SH_NODE_TEX_IMAGE; + else + type = SH_NODE_TEXTURE; break; case NTREE_TEXTURE: type = TEX_NODE_IMAGE; @@ -333,7 +338,14 @@ static int node_add_file_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - node->id = (ID *)ima; + if (type == SH_NODE_TEXTURE) { + Tex *tex = BKE_texture_add(CTX_data_main(C), DATA_(ima->id.name)); + tex->ima = ima; + node->id = (ID *)tex; + WM_event_add_notifier(C, NC_TEXTURE | NA_ADDED, node->id); + } + else + node->id = (ID *)ima; /* When adding new image file via drag-drop we need to load imbuf in order * to get proper image source. diff --git a/source/blender/editors/space_node/node_buttons.c b/source/blender/editors/space_node/node_buttons.c index f0567924edd..925298451ce 100644 --- a/source/blender/editors/space_node/node_buttons.c +++ b/source/blender/editors/space_node/node_buttons.c @@ -133,6 +133,7 @@ static void node_tree_interface_panel(const bContext *C, Panel *pa) int in_out; uiLayout *layout = pa->layout, *row, *split, *col; PointerRNA ptr, sockptr, opptr; + wmOperatorType *ot; if (!ntree) return; @@ -146,23 +147,25 @@ static void node_tree_interface_panel(const bContext *C, Panel *pa) split = uiLayoutRow(row, true); col = uiLayoutColumn(split, true); + ot = WM_operatortype_find("NODE_OT_tree_socket_add", false); uiItemL(col, IFACE_("Inputs:"), ICON_NONE); uiTemplateList(col, (bContext *)C, "NODE_UL_interface_sockets", "inputs", &ptr, "inputs", &ptr, "active_input", NULL, 0, 0, 0, 0); - opptr = uiItemFullO(col, "NODE_OT_tree_socket_add", "", ICON_PLUS, NULL, WM_OP_EXEC_DEFAULT, UI_ITEM_O_RETURN_PROPS); + opptr = uiItemFullO_ptr(col, ot, "", ICON_PLUS, NULL, WM_OP_EXEC_DEFAULT, UI_ITEM_O_RETURN_PROPS); RNA_enum_set(&opptr, "in_out", SOCK_IN); col = uiLayoutColumn(split, true); uiItemL(col, IFACE_("Outputs:"), ICON_NONE); uiTemplateList(col, (bContext *)C, "NODE_UL_interface_sockets", "outputs", &ptr, "outputs", &ptr, "active_output", NULL, 0, 0, 0, 0); - opptr = uiItemFullO(col, "NODE_OT_tree_socket_add", "", ICON_PLUS, NULL, WM_OP_EXEC_DEFAULT, UI_ITEM_O_RETURN_PROPS); + opptr = uiItemFullO_ptr(col, ot, "", ICON_PLUS, NULL, WM_OP_EXEC_DEFAULT, UI_ITEM_O_RETURN_PROPS); RNA_enum_set(&opptr, "in_out", SOCK_OUT); + ot = WM_operatortype_find("NODE_OT_tree_socket_move", false); col = uiLayoutColumn(row, true); - opptr = uiItemFullO(col, "NODE_OT_tree_socket_move", "", ICON_TRIA_UP, NULL, WM_OP_EXEC_DEFAULT, UI_ITEM_O_RETURN_PROPS); + opptr = uiItemFullO_ptr(col, ot, "", ICON_TRIA_UP, NULL, WM_OP_EXEC_DEFAULT, UI_ITEM_O_RETURN_PROPS); RNA_enum_set(&opptr, "direction", 1); - opptr = uiItemFullO(col, "NODE_OT_tree_socket_move", "", ICON_TRIA_DOWN, NULL, WM_OP_EXEC_DEFAULT, UI_ITEM_O_RETURN_PROPS); + opptr = uiItemFullO_ptr(col, ot, "", ICON_TRIA_DOWN, NULL, WM_OP_EXEC_DEFAULT, UI_ITEM_O_RETURN_PROPS); RNA_enum_set(&opptr, "direction", 2); if (sock) { diff --git a/source/blender/editors/space_outliner/outliner_draw.c b/source/blender/editors/space_outliner/outliner_draw.c index 42d9e4356ee..121fe812016 100644 --- a/source/blender/editors/space_outliner/outliner_draw.c +++ b/source/blender/editors/space_outliner/outliner_draw.c @@ -1052,8 +1052,8 @@ static void tselem_draw_icon(uiBlock *block, int xmax, float x, float y, TreeSto aspect = (0.8f * UI_UNIT_Y) / ICON_DEFAULT_HEIGHT; x += 2.0f * aspect; y += 2.0f * aspect; - arg.x = x = x; - arg.y = y = y; + arg.x = x; + arg.y = y; if (tselem->type) { switch (tselem->type) { diff --git a/source/blender/editors/space_outliner/outliner_edit.c b/source/blender/editors/space_outliner/outliner_edit.c index 8c3b9a55089..335eb95da0e 100644 --- a/source/blender/editors/space_outliner/outliner_edit.c +++ b/source/blender/editors/space_outliner/outliner_edit.c @@ -519,7 +519,8 @@ void OUTLINER_OT_id_remap(wmOperatorType *ot) ot->flag = 0; - RNA_def_enum(ot->srna, "id_type", rna_enum_id_type_items, ID_OB, "ID Type", ""); + prop = RNA_def_enum(ot->srna, "id_type", rna_enum_id_type_items, ID_OB, "ID Type", ""); + RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_ID); prop = RNA_def_enum(ot->srna, "old_id", DummyRNA_NULL_items, 0, "Old ID", "Old ID to replace"); RNA_def_property_enum_funcs_runtime(prop, NULL, NULL, outliner_id_itemf); @@ -2071,74 +2072,62 @@ static int parent_drop_invoke(bContext *C, wmOperator *op, const wmEvent *event) wmOperatorType *ot = WM_operatortype_find("OUTLINER_OT_parent_drop", false); uiPopupMenu *pup = UI_popup_menu_begin(C, IFACE_("Set Parent To"), ICON_NONE); uiLayout *layout = UI_popup_menu_layout(pup); - PointerRNA ptr; - WM_operator_properties_create_ptr(&ptr, ot); + /* Cannot use uiItemEnumO()... have multiple properties to set. */ + ptr = uiItemFullO_ptr(layout, ot, IFACE_("Object"), 0, NULL, WM_OP_EXEC_DEFAULT, UI_ITEM_O_RETURN_PROPS); RNA_string_set(&ptr, "parent", parname); RNA_string_set(&ptr, "child", childname); RNA_enum_set(&ptr, "type", PAR_OBJECT); - /* Cannot use uiItemEnumO()... have multiple properties to set. */ - uiItemFullO_ptr(layout, ot, IFACE_("Object"), 0, ptr.data, WM_OP_EXEC_DEFAULT, 0); - + /* par becomes parent, make the associated menus */ if (par->type == OB_ARMATURE) { - WM_operator_properties_create_ptr(&ptr, ot); + ptr = uiItemFullO_ptr(layout, ot, IFACE_("Armature Deform"), 0, NULL, WM_OP_EXEC_DEFAULT, UI_ITEM_O_RETURN_PROPS); RNA_string_set(&ptr, "parent", parname); RNA_string_set(&ptr, "child", childname); RNA_enum_set(&ptr, "type", PAR_ARMATURE); - uiItemFullO_ptr(layout, ot, IFACE_("Armature Deform"), 0, ptr.data, WM_OP_EXEC_DEFAULT, 0); - - WM_operator_properties_create_ptr(&ptr, ot); + + ptr = uiItemFullO_ptr(layout, ot, IFACE_(" With Empty Groups"), 0, NULL, WM_OP_EXEC_DEFAULT, UI_ITEM_O_RETURN_PROPS); RNA_string_set(&ptr, "parent", parname); RNA_string_set(&ptr, "child", childname); RNA_enum_set(&ptr, "type", PAR_ARMATURE_NAME); - uiItemFullO_ptr(layout, ot, IFACE_(" With Empty Groups"), 0, ptr.data, WM_OP_EXEC_DEFAULT, 0); - - WM_operator_properties_create_ptr(&ptr, ot); + + ptr = uiItemFullO_ptr(layout, ot, IFACE_(" With Envelope Weights"), 0, NULL, WM_OP_EXEC_DEFAULT, UI_ITEM_O_RETURN_PROPS); RNA_string_set(&ptr, "parent", parname); RNA_string_set(&ptr, "child", childname); RNA_enum_set(&ptr, "type", PAR_ARMATURE_ENVELOPE); - uiItemFullO_ptr(layout, ot, IFACE_(" With Envelope Weights"), 0, ptr.data, WM_OP_EXEC_DEFAULT, 0); - - WM_operator_properties_create_ptr(&ptr, ot); + + ptr = uiItemFullO_ptr(layout, ot, IFACE_(" With Automatic Weights"), 0, NULL, WM_OP_EXEC_DEFAULT, UI_ITEM_O_RETURN_PROPS); RNA_string_set(&ptr, "parent", parname); RNA_string_set(&ptr, "child", childname); RNA_enum_set(&ptr, "type", PAR_ARMATURE_AUTO); - uiItemFullO_ptr(layout, ot, IFACE_(" With Automatic Weights"), 0, ptr.data, WM_OP_EXEC_DEFAULT, 0); - - WM_operator_properties_create_ptr(&ptr, ot); + + ptr = uiItemFullO_ptr(layout, ot, IFACE_("Bone"), 0, NULL, WM_OP_EXEC_DEFAULT, UI_ITEM_O_RETURN_PROPS); RNA_string_set(&ptr, "parent", parname); RNA_string_set(&ptr, "child", childname); RNA_enum_set(&ptr, "type", PAR_BONE); - uiItemFullO_ptr(layout, ot, IFACE_("Bone"), - 0, ptr.data, WM_OP_EXEC_DEFAULT, 0); } else if (par->type == OB_CURVE) { - WM_operator_properties_create_ptr(&ptr, ot); + ptr = uiItemFullO_ptr(layout, ot, IFACE_("Curve Deform"), 0, NULL, WM_OP_EXEC_DEFAULT, UI_ITEM_O_RETURN_PROPS); RNA_string_set(&ptr, "parent", parname); RNA_string_set(&ptr, "child", childname); RNA_enum_set(&ptr, "type", PAR_CURVE); - uiItemFullO_ptr(layout, ot, IFACE_("Curve Deform"), 0, ptr.data, WM_OP_EXEC_DEFAULT, 0); - - WM_operator_properties_create_ptr(&ptr, ot); + + ptr = uiItemFullO_ptr(layout, ot, IFACE_("Follow Path"), 0, NULL, WM_OP_EXEC_DEFAULT, UI_ITEM_O_RETURN_PROPS); RNA_string_set(&ptr, "parent", parname); RNA_string_set(&ptr, "child", childname); RNA_enum_set(&ptr, "type", PAR_FOLLOW); - uiItemFullO_ptr(layout, ot, IFACE_("Follow Path"), 0, ptr.data, WM_OP_EXEC_DEFAULT, 0); - - WM_operator_properties_create_ptr(&ptr, ot); + + ptr = uiItemFullO_ptr(layout, ot, IFACE_("Path Constraint"), 0, NULL, WM_OP_EXEC_DEFAULT, UI_ITEM_O_RETURN_PROPS); RNA_string_set(&ptr, "parent", parname); RNA_string_set(&ptr, "child", childname); RNA_enum_set(&ptr, "type", PAR_PATH_CONST); - uiItemFullO_ptr(layout, ot, IFACE_("Path Constraint"), 0, ptr.data, WM_OP_EXEC_DEFAULT, 0); } else if (par->type == OB_LATTICE) { - WM_operator_properties_create_ptr(&ptr, ot); + ptr = uiItemFullO_ptr(layout, ot, IFACE_("Lattice Deform"), 0, NULL, WM_OP_EXEC_DEFAULT, UI_ITEM_O_RETURN_PROPS); RNA_string_set(&ptr, "parent", parname); RNA_string_set(&ptr, "child", childname); RNA_enum_set(&ptr, "type", PAR_LATTICE); - uiItemFullO_ptr(layout, ot, IFACE_("Lattice Deform"), 0, ptr.data, WM_OP_EXEC_DEFAULT, 0); } UI_popup_menu_end(C, pup); diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c index ede6b7ce469..46f212e3679 100644 --- a/source/blender/editors/space_sequencer/sequencer_add.c +++ b/source/blender/editors/space_sequencer/sequencer_add.c @@ -38,6 +38,8 @@ #include "BLI_math.h" #include "BLI_utildefines.h" +#include "BLT_translation.h" + #include "DNA_scene_types.h" #include "DNA_mask_types.h" @@ -445,6 +447,7 @@ void SEQUENCER_OT_movieclip_strip_add(struct wmOperatorType *ot) sequencer_generic_props__internal(ot, SEQPROP_STARTFRAME); prop = RNA_def_enum(ot->srna, "clip", DummyRNA_NULL_items, 0, "Clip", ""); RNA_def_enum_funcs(prop, RNA_movieclip_itemf); + RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_MOVIECLIP); RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE); ot->prop = prop; } diff --git a/source/blender/editors/space_view3d/drawobject.c b/source/blender/editors/space_view3d/drawobject.c index be2e4ab05e0..335d0649729 100644 --- a/source/blender/editors/space_view3d/drawobject.c +++ b/source/blender/editors/space_view3d/drawobject.c @@ -1824,16 +1824,16 @@ static void drawcamera_volume(float near_plane[4][3], float far_plane[4][3], con glBegin(mode); glVertex3fv(near_plane[2]); - glVertex3fv(near_plane[1]); - glVertex3fv(far_plane[1]); glVertex3fv(far_plane[2]); + glVertex3fv(far_plane[3]); + glVertex3fv(near_plane[3]); glEnd(); glBegin(mode); - glVertex3fv(far_plane[0]); - glVertex3fv(near_plane[0]); - glVertex3fv(near_plane[3]); glVertex3fv(far_plane[3]); + glVertex3fv(near_plane[3]); + glVertex3fv(near_plane[0]); + glVertex3fv(far_plane[0]); glEnd(); } @@ -4212,7 +4212,6 @@ static void draw_mesh_fancy(Scene *scene, ARegion *ar, View3D *v3d, RegionView3D if (is_obact && BKE_paint_select_vert_test(ob)) { const bool use_depth = (v3d->flag & V3D_ZBUF_SELECT) != 0; - glColor3f(0.0f, 0.0f, 0.0f); glPointSize(UI_GetThemeValuef(TH_VERTEX_SIZE)); if (!use_depth) glDisable(GL_DEPTH_TEST); diff --git a/source/blender/editors/space_view3d/view3d_buttons.c b/source/blender/editors/space_view3d/view3d_buttons.c index 351c7ccec15..34e01405e7e 100644 --- a/source/blender/editors/space_view3d/view3d_buttons.c +++ b/source/blender/editors/space_view3d/view3d_buttons.c @@ -814,10 +814,6 @@ static void view3d_panel_vgroup(const bContext *C, Panel *pa) if (dv && dv->totweight) { ToolSettings *ts = scene->toolsettings; - wmOperatorType *ot_weight_set_active = WM_operatortype_find("OBJECT_OT_vertex_weight_set_active", true); - wmOperatorType *ot_weight_paste = WM_operatortype_find("OBJECT_OT_vertex_weight_paste", true); - wmOperatorType *ot_weight_delete = WM_operatortype_find("OBJECT_OT_vertex_weight_delete", true); - wmOperatorType *ot; PointerRNA op_ptr, tools_ptr; PointerRNA *but_ptr; @@ -856,7 +852,7 @@ static void view3d_panel_vgroup(const bContext *C, Panel *pa) /* The Weight Group Name */ - ot = ot_weight_set_active; + ot = WM_operatortype_find("OBJECT_OT_vertex_weight_set_active", true); but = uiDefButO_ptr(block, UI_BTYPE_BUT, ot, WM_OP_EXEC_DEFAULT, dg->name, xco, yco, (x = UI_UNIT_X * 5), UI_UNIT_Y, ""); but_ptr = UI_but_operator_ptr_get(but); @@ -882,23 +878,16 @@ static void view3d_panel_vgroup(const bContext *C, Panel *pa) xco += x; /* The weight group paste function */ - - ot = ot_weight_paste; - WM_operator_properties_create_ptr(&op_ptr, ot); - RNA_int_set(&op_ptr, "weight_group", i); icon = (locked) ? ICON_BLANK1 : ICON_PASTEDOWN; - uiItemFullO_ptr(row, ot, "", icon, op_ptr.data, WM_OP_INVOKE_DEFAULT, 0); + op_ptr = uiItemFullO(row, "OBJECT_OT_vertex_weight_paste", "", icon, NULL, WM_OP_INVOKE_DEFAULT, UI_ITEM_O_RETURN_PROPS); + RNA_int_set(&op_ptr, "weight_group", i); /* The weight entry delete function */ - - ot = ot_weight_delete; - WM_operator_properties_create_ptr(&op_ptr, ot); - RNA_int_set(&op_ptr, "weight_group", i); icon = (locked) ? ICON_LOCKED : ICON_X; - uiItemFullO_ptr(row, ot, "", icon, op_ptr.data, WM_OP_INVOKE_DEFAULT, 0); + op_ptr = uiItemFullO(row, "OBJECT_OT_vertex_weight_delete", "", icon, NULL, WM_OP_INVOKE_DEFAULT, UI_ITEM_O_RETURN_PROPS); + RNA_int_set(&op_ptr, "weight_group", i); yco -= UI_UNIT_Y; - } } } diff --git a/source/blender/editors/space_view3d/view3d_view.c b/source/blender/editors/space_view3d/view3d_view.c index 8230a0de6b9..fe8274064e5 100644 --- a/source/blender/editors/space_view3d/view3d_view.c +++ b/source/blender/editors/space_view3d/view3d_view.c @@ -1405,6 +1405,8 @@ static bool view3d_localview_init( } } + DAG_on_visible_update(bmain, false); + return ok; } diff --git a/source/blender/editors/transform/transform_manipulator.c b/source/blender/editors/transform/transform_manipulator.c index 0a984d90ae3..f23c913da1e 100644 --- a/source/blender/editors/transform/transform_manipulator.c +++ b/source/blender/editors/transform/transform_manipulator.c @@ -143,21 +143,17 @@ static void protectflag_to_drawflags(short protectflag, short *drawflags) } /* for pose mode */ -static void stats_pose(Scene *scene, RegionView3D *rv3d, bPoseChannel *pchan) +static void protectflag_to_drawflags_pchan(RegionView3D *rv3d, const bPoseChannel *pchan) { - Bone *bone = pchan->bone; - - if (bone) { - calc_tw_center(scene, pchan->pose_head); - protectflag_to_drawflags(pchan->protectflag, &rv3d->twdrawflag); - } + protectflag_to_drawflags(pchan->protectflag, &rv3d->twdrawflag); } /* for editmode*/ -static void stats_editbone(RegionView3D *rv3d, EditBone *ebo) +static void protectflag_to_drawflags_ebone(RegionView3D *rv3d, const EditBone *ebo) { - if (ebo->flag & BONE_EDITMODE_LOCKED) + if (ebo->flag & BONE_EDITMODE_LOCKED) { protectflag_to_drawflags(OB_LOCK_LOC | OB_LOCK_ROT | OB_LOCK_SCALE, &rv3d->twdrawflag); + } } /* could move into BLI_math however this is only useful for display/editing purposes */ @@ -192,73 +188,71 @@ static void axis_angle_to_gimbal_axis(float gmat[3][3], const float axis[3], con } -static int test_rotmode_euler(short rotmode) +static bool test_rotmode_euler(short rotmode) { return (ELEM(rotmode, ROT_MODE_AXISANGLE, ROT_MODE_QUAT)) ? 0 : 1; } bool gimbal_axis(Object *ob, float gmat[3][3]) { - if (ob) { - if (ob->mode & OB_MODE_POSE) { - bPoseChannel *pchan = BKE_pose_channel_active(ob); - - if (pchan) { - float mat[3][3], tmat[3][3], obmat[3][3]; - if (test_rotmode_euler(pchan->rotmode)) { - eulO_to_gimbal_axis(mat, pchan->eul, pchan->rotmode); - } - else if (pchan->rotmode == ROT_MODE_AXISANGLE) { - axis_angle_to_gimbal_axis(mat, pchan->rotAxis, pchan->rotAngle); - } - else { /* quat */ - return 0; - } - - - /* apply bone transformation */ - mul_m3_m3m3(tmat, pchan->bone->bone_mat, mat); + if (ob->mode & OB_MODE_POSE) { + bPoseChannel *pchan = BKE_pose_channel_active(ob); - if (pchan->parent) { - float parent_mat[3][3]; - - copy_m3_m4(parent_mat, pchan->parent->pose_mat); - mul_m3_m3m3(mat, parent_mat, tmat); - - /* needed if object transformation isn't identity */ - copy_m3_m4(obmat, ob->obmat); - mul_m3_m3m3(gmat, obmat, mat); - } - else { - /* needed if object transformation isn't identity */ - copy_m3_m4(obmat, ob->obmat); - mul_m3_m3m3(gmat, obmat, tmat); - } - - normalize_m3(gmat); - return 1; - } - } - else { - if (test_rotmode_euler(ob->rotmode)) { - eulO_to_gimbal_axis(gmat, ob->rot, ob->rotmode); + if (pchan) { + float mat[3][3], tmat[3][3], obmat[3][3]; + if (test_rotmode_euler(pchan->rotmode)) { + eulO_to_gimbal_axis(mat, pchan->eul, pchan->rotmode); } - else if (ob->rotmode == ROT_MODE_AXISANGLE) { - axis_angle_to_gimbal_axis(gmat, ob->rotAxis, ob->rotAngle); + else if (pchan->rotmode == ROT_MODE_AXISANGLE) { + axis_angle_to_gimbal_axis(mat, pchan->rotAxis, pchan->rotAngle); } else { /* quat */ return 0; } - if (ob->parent) { + + /* apply bone transformation */ + mul_m3_m3m3(tmat, pchan->bone->bone_mat, mat); + + if (pchan->parent) { float parent_mat[3][3]; - copy_m3_m4(parent_mat, ob->parent->obmat); - normalize_m3(parent_mat); - mul_m3_m3m3(gmat, parent_mat, gmat); + + copy_m3_m4(parent_mat, pchan->parent->pose_mat); + mul_m3_m3m3(mat, parent_mat, tmat); + + /* needed if object transformation isn't identity */ + copy_m3_m4(obmat, ob->obmat); + mul_m3_m3m3(gmat, obmat, mat); + } + else { + /* needed if object transformation isn't identity */ + copy_m3_m4(obmat, ob->obmat); + mul_m3_m3m3(gmat, obmat, tmat); } + + normalize_m3(gmat); return 1; } } + else { + if (test_rotmode_euler(ob->rotmode)) { + eulO_to_gimbal_axis(gmat, ob->rot, ob->rotmode); + } + else if (ob->rotmode == ROT_MODE_AXISANGLE) { + axis_angle_to_gimbal_axis(gmat, ob->rotAxis, ob->rotAngle); + } + else { /* quat */ + return 0; + } + + if (ob->parent) { + float parent_mat[3][3]; + copy_m3_m4(parent_mat, ob->parent->obmat); + normalize_m3(parent_mat); + mul_m3_m3m3(gmat, parent_mat, gmat); + } + return 1; + } return 0; } @@ -385,7 +379,7 @@ static int calc_manipulator_stats(const bContext *C) calc_tw_center(scene, ebo->head); totsel++; } - stats_editbone(rv3d, ebo); + protectflag_to_drawflags_ebone(rv3d, ebo); } else { for (ebo = arm->edbo->first; ebo; ebo = ebo->next) { @@ -405,7 +399,7 @@ static int calc_manipulator_stats(const bContext *C) totsel++; } if (ebo->flag & BONE_SELECTED) { - stats_editbone(rv3d, ebo); + protectflag_to_drawflags_ebone(rv3d, ebo); } } } @@ -530,7 +524,8 @@ static int calc_manipulator_stats(const bContext *C) /* doesn't check selection or visibility intentionally */ Bone *bone = pchan->bone; if (bone) { - stats_pose(scene, rv3d, pchan); + calc_tw_center(scene, pchan->pose_head); + protectflag_to_drawflags_pchan(rv3d, pchan); totsel = 1; ok = true; } @@ -543,7 +538,8 @@ static int calc_manipulator_stats(const bContext *C) for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { Bone *bone = pchan->bone; if (bone && (bone->flag & BONE_TRANSFORM)) { - stats_pose(scene, rv3d, pchan); + calc_tw_center(scene, pchan->pose_head); + protectflag_to_drawflags_pchan(rv3d, pchan); } } ok = true; diff --git a/source/blender/editors/transform/transform_orientations.c b/source/blender/editors/transform/transform_orientations.c index 058af768885..674d2c376bb 100644 --- a/source/blender/editors/transform/transform_orientations.c +++ b/source/blender/editors/transform/transform_orientations.c @@ -451,7 +451,7 @@ void initTransformOrientation(bContext *C, TransInfo *t) case V3D_MANIP_GIMBAL: unit_m3(t->spacemtx); - if (gimbal_axis(ob, t->spacemtx)) { + if (ob && gimbal_axis(ob, t->spacemtx)) { BLI_strncpy(t->spacename, IFACE_("gimbal"), sizeof(t->spacename)); break; } diff --git a/source/blender/editors/transform/transform_snap_object.c b/source/blender/editors/transform/transform_snap_object.c index 3c9becc60dc..b33528b4149 100644 --- a/source/blender/editors/transform/transform_snap_object.c +++ b/source/blender/editors/transform/transform_snap_object.c @@ -1122,15 +1122,14 @@ static bool snapDerivedMesh( float dist_px_sq = dist_squared_to_projected_aabb_simple( lpmat, snapdata->win_half, ray_min_dist, snapdata->mval, ray_org_local, ray_normal_local, bb->vec[0], bb->vec[6]); - if (dist_px_sq > SQUARE(*dist_px)) - { + if (dist_px_sq > SQUARE(*dist_px)) { return retval; } } else { /* was BKE_boundbox_ray_hit_check, see: cf6ca226fa58 */ if (!isect_ray_aabb_v3_simple( - ray_start_local, ray_normal_local, bb->vec[0], bb->vec[6], NULL, NULL)) + ray_start_local, ray_normal_local, bb->vec[0], bb->vec[6], NULL, NULL)) { return retval; } diff --git a/source/blender/gpu/GPU_material.h b/source/blender/gpu/GPU_material.h index 0d92d22a173..6db23686832 100644 --- a/source/blender/gpu/GPU_material.h +++ b/source/blender/gpu/GPU_material.h @@ -98,6 +98,7 @@ typedef enum GPUBuiltin { GPU_PARTICLE_ANG_VELOCITY = (1 << 12), GPU_LOC_TO_VIEW_MATRIX = (1 << 13), GPU_INVERSE_LOC_TO_VIEW_MATRIX = (1 << 14), + GPU_OBJECT_INFO = (1 << 15) } GPUBuiltin; typedef enum GPUOpenGLBuiltin { @@ -213,6 +214,7 @@ bool GPU_stack_link(GPUMaterial *mat, const char *name, GPUNodeStack *in, GPUNod void GPU_material_output_link(GPUMaterial *material, GPUNodeLink *link); void GPU_material_enable_alpha(GPUMaterial *material); +GPUBuiltin GPU_get_material_builtins(GPUMaterial *material); GPUBlendMode GPU_material_alpha_blend(GPUMaterial *material, float obcol[4]); /* High level functions to create and use GPU materials */ @@ -230,7 +232,7 @@ void GPU_material_bind( float viewmat[4][4], float viewinv[4][4], float cameraborder[4], bool scenelock); void GPU_material_bind_uniforms( GPUMaterial *material, float obmat[4][4], float viewmat[4][4], float obcol[4], - float autobumpscale, GPUParticleInfo *pi); + float autobumpscale, GPUParticleInfo *pi, float object_info[3]); void GPU_material_unbind(GPUMaterial *material); bool GPU_material_bound(GPUMaterial *material); struct Scene *GPU_material_scene(GPUMaterial *material); @@ -345,6 +347,7 @@ struct GPUParticleInfo float location[3]; float velocity[3]; float angular_velocity[3]; + int random_id; }; #ifdef WITH_OPENSUBDIV diff --git a/source/blender/gpu/intern/gpu_basic_shader.c b/source/blender/gpu/intern/gpu_basic_shader.c index a2b89239344..8505bd847a0 100644 --- a/source/blender/gpu/intern/gpu_basic_shader.c +++ b/source/blender/gpu/intern/gpu_basic_shader.c @@ -407,7 +407,7 @@ static GPUShader *gpu_basic_shader(int options) return shader; } -static void GPU_basic_shader_uniform_autoset(GPUShader *shader, int options) +static void gpu_basic_shader_uniform_autoset(GPUShader *shader, int options) { if (options & GPU_SHADER_LINE) { glGetIntegerv(GL_VIEWPORT, &GPU_MATERIAL_STATE.viewport[0]); @@ -443,7 +443,7 @@ void GPU_basic_shader_bind(int options) if (shader) { GPU_shader_bind(shader); - GPU_basic_shader_uniform_autoset(shader, options); + gpu_basic_shader_uniform_autoset(shader, options); } } else { diff --git a/source/blender/gpu/intern/gpu_buffers.c b/source/blender/gpu/intern/gpu_buffers.c index 370841327aa..3325240a2d1 100644 --- a/source/blender/gpu/intern/gpu_buffers.c +++ b/source/blender/gpu/intern/gpu_buffers.c @@ -743,7 +743,7 @@ void GPU_triangle_setup(struct DerivedMesh *dm) GLStates |= GPU_BUFFER_ELEMENT_STATE; } -static int GPU_typesize(int type) +static int gpu_typesize(int type) { switch (type) { case GL_FLOAT: @@ -766,7 +766,7 @@ int GPU_attrib_element_size(GPUAttrib data[], int numdata) int i, elementsize = 0; for (i = 0; i < numdata; i++) { - int typesize = GPU_typesize(data[i].type); + int typesize = gpu_typesize(data[i].type); if (typesize != 0) elementsize += typesize * data[i].size; } @@ -803,7 +803,7 @@ void GPU_interleaved_attrib_setup(GPUBuffer *buffer, GPUAttrib data[], int numda glVertexAttribPointer(data[i].index, data[i].size, data[i].type, GL_TRUE, elementsize, BUFFER_OFFSET(offset)); - offset += data[i].size * GPU_typesize(data[i].type); + offset += data[i].size * gpu_typesize(data[i].type); attribData[i].index = data[i].index; attribData[i].size = data[i].size; diff --git a/source/blender/gpu/intern/gpu_codegen.c b/source/blender/gpu/intern/gpu_codegen.c index 211394e7932..b5512aa108d 100644 --- a/source/blender/gpu/intern/gpu_codegen.c +++ b/source/blender/gpu/intern/gpu_codegen.c @@ -360,7 +360,7 @@ static void codegen_print_datatype(DynStr *ds, const GPUType type, float *data) BLI_dynstr_appendf(ds, "%s(", GPU_DATATYPE_STR[type]); for (i = 0; i < type; i++) { - BLI_dynstr_appendf(ds, "%f", data[i]); + BLI_dynstr_appendf(ds, "%.12f", data[i]); if (i == type - 1) BLI_dynstr_append(ds, ")"); else @@ -410,6 +410,8 @@ const char *GPU_builtin_name(GPUBuiltin builtin) return "unfparticlevel"; else if (builtin == GPU_PARTICLE_ANG_VELOCITY) return "unfparticleangvel"; + else if (builtin == GPU_OBJECT_INFO) + return "unfobjectinfo"; else return ""; } diff --git a/source/blender/gpu/intern/gpu_compositing.c b/source/blender/gpu/intern/gpu_compositing.c index 964c2b5051e..2f2a16f9e1d 100644 --- a/source/blender/gpu/intern/gpu_compositing.c +++ b/source/blender/gpu/intern/gpu_compositing.c @@ -798,7 +798,9 @@ bool GPU_fx_do_composite_pass( ssao_shader = GPU_shader_get_builtin_fx_shader(GPU_SHADER_FX_SSAO, is_persp); if (ssao_shader) { const GPUSSAOSettings *fx_ssao = fx->settings.ssao; - float ssao_params[4] = {fx_ssao->distance_max, fx_ssao->factor, fx_ssao->attenuation, 0.0f}; + /* adjust attenuation to be scale invariant */ + float attenuation = fx_ssao->attenuation / (fx_ssao->distance_max * fx_ssao->distance_max); + float ssao_params[4] = {fx_ssao->distance_max, fx_ssao->factor, attenuation, 0.0f}; float sample_params[3]; sample_params[0] = fx->ssao_sample_count_cache; diff --git a/source/blender/gpu/intern/gpu_draw.c b/source/blender/gpu/intern/gpu_draw.c index 7936811ab4d..79934190cbc 100644 --- a/source/blender/gpu/intern/gpu_draw.c +++ b/source/blender/gpu/intern/gpu_draw.c @@ -45,6 +45,7 @@ #include "BLI_math.h" #include "BLI_threads.h" #include "BLI_utildefines.h" +#include "BLI_hash.h" #include "DNA_lamp_types.h" #include "DNA_material_types.h" @@ -1203,7 +1204,7 @@ void GPU_paint_set_mipmap(bool mipmap) /* check if image has been downscaled and do scaled partial update */ -static bool GPU_check_scaled_image(ImBuf *ibuf, Image *ima, float *frect, int x, int y, int w, int h) +static bool gpu_check_scaled_image(ImBuf *ibuf, Image *ima, float *frect, int x, int y, int w, int h) { if ((!GPU_full_non_power_of_two_support() && !is_power_of_2_resolution(ibuf->x, ibuf->y)) || is_over_resolution_limit(GL_TEXTURE_2D, ibuf->x, ibuf->y)) @@ -1296,7 +1297,7 @@ void GPU_paint_update_image(Image *ima, ImageUser *iuser, int x, int y, int w, i bool is_data = (ima->tpageflag & IMA_GLBIND_IS_DATA) != 0; IMB_partial_rect_from_float(ibuf, buffer, x, y, w, h, is_data); - if (GPU_check_scaled_image(ibuf, ima, buffer, x, y, w, h)) { + if (gpu_check_scaled_image(ibuf, ima, buffer, x, y, w, h)) { MEM_freeN(buffer); BKE_image_release_ibuf(ima, ibuf, NULL); return; @@ -1320,7 +1321,7 @@ void GPU_paint_update_image(Image *ima, ImageUser *iuser, int x, int y, int w, i return; } - if (GPU_check_scaled_image(ibuf, ima, NULL, x, y, w, h)) { + if (gpu_check_scaled_image(ibuf, ima, NULL, x, y, w, h)) { BKE_image_release_ibuf(ima, ibuf, NULL); return; } @@ -1870,7 +1871,7 @@ void GPU_begin_object_materials( GPU_object_material_unbind(); } -static int GPU_get_particle_info(GPUParticleInfo *pi) +static int gpu_get_particle_info(GPUParticleInfo *pi) { DupliObject *dob = GMS.dob; if (dob->particle_system) { @@ -1899,6 +1900,21 @@ static int GPU_get_particle_info(GPUParticleInfo *pi) return 0; } +static void GPU_get_object_info(float oi[3], Material *mat) +{ + Object *ob = GMS.gob; + oi[0] = ob->index; + oi[1] = mat->index; + unsigned int random; + if (GMS.dob) { + random = GMS.dob->random_id; + } + else { + random = BLI_hash_int_2d(BLI_hash_string(GMS.gob->id.name + 2), 0); + } + oi[2] = random * (1.0f/(float)0xFFFFFFFF); +} + int GPU_object_material_bind(int nr, void *attribs) { GPUVertexAttribs *gattribs = attribs; @@ -1958,21 +1974,27 @@ int GPU_object_material_bind(int nr, void *attribs) /* bind glsl material and get attributes */ Material *mat = GMS.gmatbuf[nr]; GPUParticleInfo partile_info; + float object_info[3] = {0}; float auto_bump_scale; GPUMaterial *gpumat = GPU_material_from_blender(GMS.gscene, mat, GMS.is_opensubdiv); GPU_material_vertex_attributes(gpumat, gattribs); - if (GMS.dob) - GPU_get_particle_info(&partile_info); + if (GMS.dob) { + gpu_get_particle_info(&partile_info); + } + + if (GPU_get_material_builtins(gpumat) & GPU_OBJECT_INFO) { + GPU_get_object_info(object_info, mat); + } GPU_material_bind( gpumat, GMS.gob->lay, GMS.glay, 1.0, !(GMS.gob->mode & OB_MODE_TEXTURE_PAINT), GMS.gviewmat, GMS.gviewinv, GMS.gviewcamtexcofac, GMS.gscenelock); auto_bump_scale = GMS.gob->derivedFinal != NULL ? GMS.gob->derivedFinal->auto_bump_scale : 1.0f; - GPU_material_bind_uniforms(gpumat, GMS.gob->obmat, GMS.gviewmat, GMS.gob->col, auto_bump_scale, &partile_info); + GPU_material_bind_uniforms(gpumat, GMS.gob->obmat, GMS.gviewmat, GMS.gob->col, auto_bump_scale, &partile_info, object_info); GMS.gboundmat = mat; /* for glsl use alpha blend mode, unless it's set to solid and diff --git a/source/blender/gpu/intern/gpu_framebuffer.c b/source/blender/gpu/intern/gpu_framebuffer.c index f62ef677434..e7a8beae5cc 100644 --- a/source/blender/gpu/intern/gpu_framebuffer.c +++ b/source/blender/gpu/intern/gpu_framebuffer.c @@ -52,7 +52,7 @@ struct GPUFrameBuffer { GPUTexture *depthtex; }; -static void GPU_print_framebuffer_error(GLenum status, char err_out[256]) +static void gpu_print_framebuffer_error(GLenum status, char err_out[256]) { const char *err = "unknown"; @@ -164,7 +164,7 @@ int GPU_framebuffer_texture_attach(GPUFrameBuffer *fb, GPUTexture *tex, int slot if (error == GL_INVALID_OPERATION) { GPU_framebuffer_restore(); - GPU_print_framebuffer_error(error, err_out); + gpu_print_framebuffer_error(error, err_out); return 0; } @@ -331,7 +331,7 @@ bool GPU_framebuffer_check_valid(GPUFrameBuffer *fb, char err_out[256]) if (status != GL_FRAMEBUFFER_COMPLETE_EXT) { GPU_framebuffer_restore(); - GPU_print_framebuffer_error(status, err_out); + gpu_print_framebuffer_error(status, err_out); return false; } diff --git a/source/blender/gpu/intern/gpu_material.c b/source/blender/gpu/intern/gpu_material.c index 4e2043471b6..1f3ae7f708a 100644 --- a/source/blender/gpu/intern/gpu_material.c +++ b/source/blender/gpu/intern/gpu_material.c @@ -122,6 +122,8 @@ struct GPUMaterial { int partvel; int partangvel; + int objectinfoloc; + ListBase lamps; bool bound; @@ -225,7 +227,7 @@ static void gpu_material_set_attrib_id(GPUMaterial *material) attribs->totlayer = b; } -static int GPU_material_construct_end(GPUMaterial *material, const char *passname) +static int gpu_material_construct_end(GPUMaterial *material, const char *passname) { if (material->outlink) { GPUNodeLink *outlink = material->outlink; @@ -268,6 +270,8 @@ static int GPU_material_construct_end(GPUMaterial *material, const char *passnam material->partvel = GPU_shader_get_uniform(shader, GPU_builtin_name(GPU_PARTICLE_VELOCITY)); if (material->builtins & GPU_PARTICLE_ANG_VELOCITY) material->partangvel = GPU_shader_get_uniform(shader, GPU_builtin_name(GPU_PARTICLE_ANG_VELOCITY)); + if (material->builtins & GPU_OBJECT_INFO) + material->objectinfoloc = GPU_shader_get_uniform(shader, GPU_builtin_name(GPU_OBJECT_INFO)); return 1; } else { @@ -398,9 +402,14 @@ void GPU_material_bind( } } +GPUBuiltin GPU_get_material_builtins(GPUMaterial *material) +{ + return material->builtins; +} + void GPU_material_bind_uniforms( GPUMaterial *material, float obmat[4][4], float viewmat[4][4], float obcol[4], - float autobumpscale, GPUParticleInfo *pi) + float autobumpscale, GPUParticleInfo *pi, float object_info[3]) { if (material->pass) { GPUShader *shader = GPU_pass_shader(material->pass); @@ -449,6 +458,9 @@ void GPU_material_bind_uniforms( if (material->builtins & GPU_PARTICLE_ANG_VELOCITY) { GPU_shader_uniform_vector(shader, material->partangvel, 3, 1, pi->angular_velocity); } + if (material->builtins & GPU_OBJECT_INFO) { + GPU_shader_uniform_vector(shader, material->objectinfoloc, 3, 1, object_info); + } } } @@ -1891,7 +1903,7 @@ GPUMaterial *GPU_material_matcap(Scene *scene, Material *ma, bool use_opensubdiv GPU_material_output_link(mat, outlink); - GPU_material_construct_end(mat, "matcap_pass"); + gpu_material_construct_end(mat, "matcap_pass"); /* note that even if building the shader fails in some way, we still keep * it to avoid trying to compile again and again, and simple do not use @@ -2044,7 +2056,7 @@ static void do_world_tex(GPUShadeInput *shi, struct World *wo, GPUNodeLink **hor } } -static void GPU_material_old_world(struct GPUMaterial *mat, struct World *wo) +static void gpu_material_old_world(struct GPUMaterial *mat, struct World *wo) { GPUShadeInput shi; GPUShadeResult shr; @@ -2112,17 +2124,18 @@ GPUMaterial *GPU_material_world(struct Scene *scene, struct World *wo) mat->type = GPU_MATERIAL_TYPE_WORLD; /* create nodes */ - if (BKE_scene_use_new_shading_nodes(scene) && wo->nodetree && wo->use_nodes) + if (BKE_scene_use_new_shading_nodes(scene) && wo->nodetree && wo->use_nodes) { ntreeGPUMaterialNodes(wo->nodetree, mat, NODE_NEW_SHADING); + } else { - GPU_material_old_world(mat, wo); + gpu_material_old_world(mat, wo); } if (GPU_material_do_color_management(mat)) if (mat->outlink) GPU_link(mat, "linearrgb_to_srgb", mat->outlink, &mat->outlink); - GPU_material_construct_end(mat, wo->id.name); + gpu_material_construct_end(mat, wo->id.name); /* note that even if building the shader fails in some way, we still keep * it to avoid trying to compile again and again, and simple do not use @@ -2188,7 +2201,7 @@ GPUMaterial *GPU_material_from_blender(Scene *scene, Material *ma, bool use_open if (mat->outlink) GPU_link(mat, "linearrgb_to_srgb", mat->outlink, &mat->outlink); - GPU_material_construct_end(mat, ma->id.name); + gpu_material_construct_end(mat, ma->id.name); /* note that even if building the shader fails in some way, we still keep * it to avoid trying to compile again and again, and simple do not use diff --git a/source/blender/gpu/intern/gpu_shader.c b/source/blender/gpu/intern/gpu_shader.c index 14f2764b009..b579f87698c 100644 --- a/source/blender/gpu/intern/gpu_shader.c +++ b/source/blender/gpu/intern/gpu_shader.c @@ -39,6 +39,7 @@ #include "GPU_glew.h" #include "GPU_shader.h" #include "GPU_texture.h" +#include "GPU_material.h" /* TODO(sergey): Find better default values for this constants. */ #define MAX_DEFINE_LENGTH 1024 diff --git a/source/blender/gpu/intern/gpu_texture.c b/source/blender/gpu/intern/gpu_texture.c index 54f0003c086..1c97c2ce811 100644 --- a/source/blender/gpu/intern/gpu_texture.c +++ b/source/blender/gpu/intern/gpu_texture.c @@ -79,7 +79,7 @@ static unsigned char *GPU_texture_convert_pixels(int length, const float *fpixel return pixels; } -static void GPU_glTexSubImageEmpty(GLenum target, GLenum format, int x, int y, int w, int h) +static void gpu_glTexSubImageEmpty(GLenum target, GLenum format, int x, int y, int w, int h) { void *pixels = MEM_callocN(sizeof(char) * 4 * w * h, "GPUTextureEmptyPixels"); @@ -193,7 +193,7 @@ static GPUTexture *GPU_texture_create_nD( pixels ? pixels : fpixels); if (tex->w > w) { - GPU_glTexSubImageEmpty(tex->target, format, w, 0, tex->w - w, 1); + gpu_glTexSubImageEmpty(tex->target, format, w, 0, tex->w - w, 1); } } } @@ -210,10 +210,12 @@ static GPUTexture *GPU_texture_create_nD( glTexSubImage2D(tex->target, 0, 0, 0, w, h, format, type, pixels ? pixels : fpixels); - if (tex->w > w) - GPU_glTexSubImageEmpty(tex->target, format, w, 0, tex->w - w, tex->h); - if (tex->h > h) - GPU_glTexSubImageEmpty(tex->target, format, 0, h, w, tex->h - h); + if (tex->w > w) { + gpu_glTexSubImageEmpty(tex->target, format, w, 0, tex->w - w, tex->h); + } + if (tex->h > h) { + gpu_glTexSubImageEmpty(tex->target, format, 0, h, w, tex->h - h); + } } } diff --git a/source/blender/gpu/shaders/gpu_shader_fx_ssao_frag.glsl b/source/blender/gpu/shaders/gpu_shader_fx_ssao_frag.glsl index 50c8e255162..f19ff4ec65a 100644 --- a/source/blender/gpu/shaders/gpu_shader_fx_ssao_frag.glsl +++ b/source/blender/gpu/shaders/gpu_shader_fx_ssao_frag.glsl @@ -75,7 +75,7 @@ float calculate_ssao_factor(float depth) float f = dot(dir, normal); /* use minor bias here to avoid self shadowing */ - if (f > 0.05 * len + 0.0001) + if (f > 0.05 * len) factor += f * 1.0 / (len * (1.0 + len * len * ssao_params.z)); } } diff --git a/source/blender/gpu/shaders/gpu_shader_material.glsl b/source/blender/gpu/shaders/gpu_shader_material.glsl index 0f3ffa8244b..c354b274f00 100644 --- a/source/blender/gpu/shaders/gpu_shader_material.glsl +++ b/source/blender/gpu/shaders/gpu_shader_material.glsl @@ -3563,12 +3563,12 @@ void node_light_falloff(float strength, float tsmooth, out float quadratic, out constant = strength; } -void node_object_info(out vec3 location, out float object_index, out float material_index, out float random) +void node_object_info(mat4 obmat, vec3 info, out vec3 location, out float object_index, out float material_index, out float random) { - location = vec3(0.0); - object_index = 0.0; - material_index = 0.0; - random = 0.0; + location = obmat[3].xyz; + object_index = info.x; + material_index = info.y; + random = info.z; } void node_normal_map(vec4 tangent, vec3 normal, vec3 texnormal, out vec3 outnormal) diff --git a/source/blender/imbuf/intern/colormanagement.c b/source/blender/imbuf/intern/colormanagement.c index 48cba3e0800..0881a24422d 100644 --- a/source/blender/imbuf/intern/colormanagement.c +++ b/source/blender/imbuf/intern/colormanagement.c @@ -1406,7 +1406,7 @@ static void *do_display_buffer_apply_thread(void *handle_v) bool is_data = handle->is_data; if (cm_processor == NULL) { - if (display_buffer_byte) { + if (display_buffer_byte && display_buffer_byte != handle->byte_buffer) { IMB_buffer_byte_from_byte(display_buffer_byte, handle->byte_buffer, IB_PROFILE_SRGB, IB_PROFILE_SRGB, false, width, height, width, width); } diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index 2c6f3d2fc66..da0f505c4f3 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -82,8 +82,6 @@ enum { IDP_FLOAT = 2, IDP_ARRAY = 5, IDP_GROUP = 6, - /* the ID link property type hasn't been implemented yet, this will require - * some cleanup of blenkernel, most likely. */ IDP_ID = 7, IDP_DOUBLE = 8, IDP_IDPARRAY = 9, diff --git a/source/blender/makesdna/DNA_meshdata_types.h b/source/blender/makesdna/DNA_meshdata_types.h index 621807d111c..3676066a399 100644 --- a/source/blender/makesdna/DNA_meshdata_types.h +++ b/source/blender/makesdna/DNA_meshdata_types.h @@ -164,8 +164,8 @@ typedef struct MLoop { * MEdge *ed = &medge[mloop[lt->tri[j]].e]; * unsigned int tri_edge[2] = {mloop[lt->tri[j]].v, mloop[lt->tri[j_next]].v}; * - * if (ELEM(ed->v1, tri_edge[0], tri_edge[1]) && - * ELEM(ed->v2, tri_edge[0], tri_edge[1])) + * if (((ed->v1 == tri_edge[0]) && (ed->v1 == tri_edge[1])) || + * ((ed->v1 == tri_edge[1]) && (ed->v1 == tri_edge[0]))) * { * printf("real edge found %u %u\n", tri_edge[0], tri_edge[1]); * } diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h index 32b43c7ea55..823a7f0812f 100644 --- a/source/blender/makesdna/DNA_modifier_types.h +++ b/source/blender/makesdna/DNA_modifier_types.h @@ -277,6 +277,7 @@ typedef struct MirrorModifierData { short axis DNA_DEPRECATED; /* deprecated, use flag instead */ short flag; float tolerance; + float uv_offset[2]; struct Object *mirror_ob; } MirrorModifierData; diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h index d24c7faa9f5..6d79e6d49f8 100644 --- a/source/blender/makesdna/DNA_object_types.h +++ b/source/blender/makesdna/DNA_object_types.h @@ -335,6 +335,8 @@ typedef struct DupliObject { /* particle this dupli was generated from */ struct ParticleSystem *particle_system; + unsigned int random_id; + unsigned int pad; } DupliObject; /* **************** OBJECT ********************* */ diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index fc970c40c12..61b580c0904 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -468,13 +468,18 @@ typedef struct UserDef { int audioformat; int audiochannels; - int scrollback; /* console scrollback limit */ - int dpi; /* range 48-128? */ - char node_margin; /* node insert offset (aka auto-offset) margin, but might be useful for later stuff as well */ + int scrollback; /* console scrollback limit */ + int dpi; /* range 48-128? */ + float ui_scale; /* interface scale */ + int pad1; + char node_margin; /* node insert offset (aka auto-offset) margin, but might be useful for later stuff as well */ char pad2; short transopts; short menuthreshold1, menuthreshold2; - + + /* startup template */ + char app_template[64]; + struct ListBase themes; struct ListBase uifonts; struct ListBase uistyles; diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index f9aaec69ce7..a1af3f98274 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -767,6 +767,8 @@ void RNA_struct_blender_type_set(StructRNA *srna, void *blender_type); struct IDProperty *RNA_struct_idprops(PointerRNA *ptr, bool create); bool RNA_struct_idprops_check(StructRNA *srna); bool RNA_struct_idprops_register_check(const StructRNA *type); +bool RNA_struct_idprops_datablock_allowed(const StructRNA *type); +bool RNA_struct_idprops_contains_datablock(const StructRNA *type); bool RNA_struct_idprops_unset(PointerRNA *ptr, const char *identifier); PropertyRNA *RNA_struct_find_property(PointerRNA *ptr, const char *identifier); diff --git a/source/blender/makesrna/RNA_define.h b/source/blender/makesrna/RNA_define.h index 2a680b6eaeb..6e62313b00a 100644 --- a/source/blender/makesrna/RNA_define.h +++ b/source/blender/makesrna/RNA_define.h @@ -167,6 +167,7 @@ void RNA_def_property_editable_func(PropertyRNA *prop, const char *editable); void RNA_def_property_editable_array_func(PropertyRNA *prop, const char *editable); void RNA_def_property_update_runtime(PropertyRNA *prop, const void *func); +void RNA_def_property_poll_runtime(PropertyRNA *prop, const void *func); void RNA_def_property_dynamic_array_funcs(PropertyRNA *prop, const char *getlength); void RNA_def_property_boolean_funcs(PropertyRNA *prop, const char *get, const char *set); diff --git a/source/blender/makesrna/RNA_types.h b/source/blender/makesrna/RNA_types.h index dee8df7d933..cd04f9e8a6d 100644 --- a/source/blender/makesrna/RNA_types.h +++ b/source/blender/makesrna/RNA_types.h @@ -434,6 +434,8 @@ typedef enum StructFlag { STRUCT_GENERATED = (1 << 4), STRUCT_FREE_POINTERS = (1 << 5), STRUCT_NO_IDPROPERTIES = (1 << 6), /* Menus and Panels don't need properties */ + STRUCT_NO_DATABLOCK_IDPROPERTIES = (1 << 7), /* e.g. for Operator */ + STRUCT_CONTAINS_DATABLOCK_IDPROPERTIES = (1 << 8), /* for PropertyGroup which contains pointers to datablocks */ } StructFlag; typedef int (*StructValidateFunc)(struct PointerRNA *ptr, void *data, int *have_function); diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c index 4552c773097..9d68c05dda0 100644 --- a/source/blender/makesrna/intern/makesrna.c +++ b/source/blender/makesrna/intern/makesrna.c @@ -507,7 +507,7 @@ static void rna_float_print(FILE *f, float num) { if (num == -FLT_MAX) fprintf(f, "-FLT_MAX"); else if (num == FLT_MAX) fprintf(f, "FLT_MAX"); - else if ((int64_t)num == num) fprintf(f, "%.1ff", num); + else if ((ABS(num) < INT64_MAX) && ((int64_t)num == num)) fprintf(f, "%.1ff", num); else fprintf(f, "%.10ff", num); } diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c index 671902c5cc7..6ed2c55138c 100644 --- a/source/blender/makesrna/intern/rna_ID.c +++ b/source/blender/makesrna/intern/rna_ID.c @@ -802,7 +802,11 @@ static void rna_def_ID_properties(BlenderRNA *brna) RNA_def_struct_name_property(srna, prop); #endif - /* IDP_ID -- not implemented yet in id properties */ + /* IDP_ID */ + prop = RNA_def_property(srna, "id", PROP_POINTER, PROP_NONE); + RNA_def_property_flag(prop, PROP_EXPORT | PROP_IDPROPERTY | PROP_NEVER_UNLINK); + RNA_def_property_struct_type(prop, "ID"); + /* ID property groups > level 0, since level 0 group is merged * with native RNA properties. the builtin_properties will take diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index bb839fd77f8..c3d2d92fc5e 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -50,6 +50,7 @@ #include "BKE_idcode.h" #include "BKE_idprop.h" #include "BKE_fcurve.h" +#include "BKE_library.h" #include "BKE_main.h" #include "BKE_report.h" @@ -380,6 +381,7 @@ static bool rna_idproperty_verify_valid(PointerRNA *ptr, PropertyRNA *prop, IDPr return false; break; case IDP_GROUP: + case IDP_ID: if (prop->type != PROP_POINTER) return false; break; @@ -395,7 +397,8 @@ static PropertyRNA *typemap[IDP_NUMTYPES] = { (PropertyRNA *)&rna_PropertyGroupItem_int, (PropertyRNA *)&rna_PropertyGroupItem_float, NULL, NULL, NULL, - (PropertyRNA *)&rna_PropertyGroupItem_group, NULL, + (PropertyRNA *)&rna_PropertyGroupItem_group, + (PropertyRNA *)&rna_PropertyGroupItem_id, (PropertyRNA *)&rna_PropertyGroupItem_double, (PropertyRNA *)&rna_PropertyGroupItem_idp_array }; @@ -587,6 +590,21 @@ bool RNA_struct_idprops_register_check(const StructRNA *type) return (type->flag & STRUCT_NO_IDPROPERTIES) == 0; } +bool RNA_struct_idprops_datablock_allowed(const StructRNA *type) +{ + return (type->flag & (STRUCT_NO_DATABLOCK_IDPROPERTIES | STRUCT_NO_IDPROPERTIES)) == 0; +} + +/** + * Whether given type implies datablock usage by IDProperties. + * This is used to prevent classes allowed to have IDProperties, but not datablock ones, to indirectly use some + * (e.g. by assigning an IDP_GROUP containing some IDP_ID pointers...). + */ +bool RNA_struct_idprops_contains_datablock(const StructRNA *type) +{ + return (type->flag & (STRUCT_CONTAINS_DATABLOCK_IDPROPERTIES | STRUCT_ID)) != 0; +} + /* remove an id-property */ bool RNA_struct_idprops_unset(PointerRNA *ptr, const char *identifier) { @@ -628,8 +646,11 @@ PropertyRNA *RNA_struct_find_property(PointerRNA *ptr, const char *identifier) /* id prop lookup, not so common */ PropertyRNA *r_prop = NULL; PointerRNA r_ptr; /* only support single level props */ - if (RNA_path_resolve(ptr, identifier, &r_ptr, &r_prop) && (r_ptr.type == ptr->type) && (r_ptr.data == ptr->data)) + if (RNA_path_resolve_property(ptr, identifier, &r_ptr, &r_prop) && + (r_ptr.type == ptr->type) && (r_ptr.data == ptr->data)) + { return r_prop; + } } else { /* most common case */ @@ -1201,13 +1222,20 @@ int RNA_property_pointer_poll(PointerRNA *ptr, PropertyRNA *prop, PointerRNA *va if (prop->type == PROP_POINTER) { PointerPropertyRNA *pprop = (PointerPropertyRNA *)prop; - if (pprop->poll) - return pprop->poll(ptr, *value); + + if (pprop->poll) { + if (rna_idproperty_check(&prop, ptr)) { + return ((PropPointerPollFuncPy) pprop->poll)(ptr, *value, prop); + } + else { + return pprop->poll(ptr, *value); + } + } return 1; } - printf("%s %s: is not a pointer property.\n", __func__, prop->identifier); + printf("%s: %s is not a pointer property.\n", __func__, prop->identifier); return 0; } @@ -2967,6 +2995,10 @@ PointerRNA RNA_property_pointer_get(PointerRNA *ptr, PropertyRNA *prop) if ((idprop = rna_idproperty_check(&prop, ptr))) { pprop = (PointerPropertyRNA *)prop; + if (RNA_struct_is_ID(pprop->type)) { + return rna_pointer_inherit_refine(ptr, pprop->type, IDP_Id(idprop)); + } + /* for groups, data is idprop itself */ if (pprop->typef) return rna_pointer_inherit_refine(ptr, pprop->typef(ptr), idprop); @@ -2989,22 +3021,32 @@ PointerRNA RNA_property_pointer_get(PointerRNA *ptr, PropertyRNA *prop) void RNA_property_pointer_set(PointerRNA *ptr, PropertyRNA *prop, PointerRNA ptr_value) { - /*IDProperty *idprop;*/ - + PointerPropertyRNA *pprop = (PointerPropertyRNA *)prop; BLI_assert(RNA_property_type(prop) == PROP_POINTER); - if ((/*idprop = */ rna_idproperty_check(&prop, ptr))) { - /* not supported */ - /* rna_idproperty_touch(idprop); */ + /* Check types */ + if (ptr_value.type != NULL && !RNA_struct_is_a(ptr_value.type, pprop->type)) { + printf("%s: expected %s type, not %s.\n", __func__, pprop->type->identifier, ptr_value.type->identifier); + return; } - else { - PointerPropertyRNA *pprop = (PointerPropertyRNA *)prop; - if (pprop->set && - !((prop->flag & PROP_NEVER_NULL) && ptr_value.data == NULL) && - !((prop->flag & PROP_ID_SELF_CHECK) && ptr->id.data == ptr_value.id.data)) - { - pprop->set(ptr, ptr_value); + /* RNA */ + if (pprop->set && + !((prop->flag & PROP_NEVER_NULL) && ptr_value.data == NULL) && + !((prop->flag & PROP_ID_SELF_CHECK) && ptr->id.data == ptr_value.id.data)) + { + pprop->set(ptr, ptr_value); + } + /* IDProperty */ + else if (prop->flag & PROP_EDITABLE) { + IDPropertyTemplate val = {0}; + IDProperty *group; + + val.id = ptr_value.data; + + group = RNA_struct_idprops(ptr, true); + if (group) { + IDP_ReplaceInGroup(group, IDP_New(IDP_ID, &val, prop->identifier)); } } } @@ -5547,6 +5589,9 @@ static char *rna_pointer_as_string__bldata(PointerRNA *ptr) return BLI_strdup("None"); } else if (RNA_struct_is_ID(ptr->type)) { + if (ptr->id.data == NULL) { + return BLI_strdup("None"); + } return RNA_path_full_ID_py(ptr->id.data); } else { @@ -5556,7 +5601,10 @@ static char *rna_pointer_as_string__bldata(PointerRNA *ptr) char *RNA_pointer_as_string(bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *prop_ptr, PointerRNA *ptr_prop) { - if (RNA_property_flag(prop_ptr) & PROP_IDPROPERTY) { + if (ptr_prop->data == NULL) { + return BLI_strdup("None"); + } + else if (RNA_property_flag(prop_ptr) & PROP_IDPROPERTY) { return RNA_pointer_as_string_id(C, ptr_prop); } else { diff --git a/source/blender/makesrna/intern/rna_action.c b/source/blender/makesrna/intern/rna_action.c index 0c4c7ddac81..d398ce95a52 100644 --- a/source/blender/makesrna/intern/rna_action.c +++ b/source/blender/makesrna/intern/rna_action.c @@ -34,6 +34,8 @@ #include "BLI_utildefines.h" +#include "BLT_translation.h" + #include "BKE_action.h" #include "RNA_access.h" @@ -731,6 +733,7 @@ static void rna_def_action(BlenderRNA *brna) RNA_def_property_ui_text(prop, "ID Root Type", "Type of ID block that action can be used on - " "DO NOT CHANGE UNLESS YOU KNOW WHAT YOU ARE DOING"); + RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_ID); /* API calls */ RNA_api_action(srna); diff --git a/source/blender/makesrna/intern/rna_animation.c b/source/blender/makesrna/intern/rna_animation.c index 9adbf5f6b2e..f271bccd326 100644 --- a/source/blender/makesrna/intern/rna_animation.c +++ b/source/blender/makesrna/intern/rna_animation.c @@ -32,6 +32,8 @@ #include "BLI_utildefines.h" +#include "BLT_translation.h" + #include "MEM_guardedalloc.h" #include "RNA_access.h" @@ -742,6 +744,7 @@ static void rna_def_keyingset_path(BlenderRNA *brna) RNA_def_property_enum_default(prop, ID_OB); RNA_def_property_enum_funcs(prop, NULL, "rna_ksPath_id_type_set", NULL); RNA_def_property_ui_text(prop, "ID Type", "Type of ID-block that can be used"); + RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_ID); RNA_def_property_update(prop, NC_SCENE | ND_KEYINGSET | NA_EDITED, NULL); /* XXX: maybe a bit too noisy */ /* Group */ diff --git a/source/blender/makesrna/intern/rna_define.c b/source/blender/makesrna/intern/rna_define.c index 1d232d2df39..49983b0bc2b 100644 --- a/source/blender/makesrna/intern/rna_define.c +++ b/source/blender/makesrna/intern/rna_define.c @@ -2168,6 +2168,16 @@ void RNA_def_property_update_runtime(PropertyRNA *prop, const void *func) prop->update = (void *)func; } +void RNA_def_property_poll_runtime(PropertyRNA *prop, const void *func) +{ + if (prop->type == PROP_POINTER) { + ((PointerPropertyRNA *)prop)->poll = func; + } + else { + fprintf(stderr, "%s: %s is not a Pointer Property.\n", __func__, prop->identifier); + } +} + void RNA_def_property_dynamic_array_funcs(PropertyRNA *prop, const char *getlength) { if (!DefRNA.preprocess) { @@ -2982,6 +2992,9 @@ PropertyRNA *RNA_def_pointer_runtime(StructOrFunctionRNA *cont_, const char *ide prop = RNA_def_property(cont, identifier, PROP_POINTER, PROP_NONE); RNA_def_property_struct_runtime(prop, type); + if ((type->flag & STRUCT_ID) != 0) { + prop->flag |= PROP_EDITABLE; + } RNA_def_property_ui_text(prop, ui_name, ui_description); return prop; diff --git a/source/blender/makesrna/intern/rna_fcurve.c b/source/blender/makesrna/intern/rna_fcurve.c index 4acdee490b8..bccc47aa95d 100644 --- a/source/blender/makesrna/intern/rna_fcurve.c +++ b/source/blender/makesrna/intern/rna_fcurve.c @@ -35,6 +35,8 @@ #include "BLI_math.h" +#include "BLT_translation.h" + #include "BKE_action.h" #include "RNA_access.h" @@ -1448,6 +1450,7 @@ static void rna_def_drivertarget(BlenderRNA *brna) RNA_def_property_enum_funcs(prop, NULL, "rna_DriverTarget_id_type_set", NULL); RNA_def_property_editable_func(prop, "rna_DriverTarget_id_type_editable"); RNA_def_property_ui_text(prop, "ID Type", "Type of ID-block that can be used"); + RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_ID); RNA_def_property_update(prop, 0, "rna_DriverTarget_update_data"); /* Target Properties - Property to Drive */ diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h index 76455adbc78..dfd5af788f6 100644 --- a/source/blender/makesrna/intern/rna_internal.h +++ b/source/blender/makesrna/intern/rna_internal.h @@ -344,6 +344,7 @@ extern IntPropertyRNA rna_PropertyGroupItem_int_array; extern FloatPropertyRNA rna_PropertyGroupItem_float; extern FloatPropertyRNA rna_PropertyGroupItem_float_array; extern PointerPropertyRNA rna_PropertyGroupItem_group; +extern PointerPropertyRNA rna_PropertyGroupItem_id; extern CollectionPropertyRNA rna_PropertyGroupItem_collection; extern CollectionPropertyRNA rna_PropertyGroupItem_idp_array; extern FloatPropertyRNA rna_PropertyGroupItem_double; diff --git a/source/blender/makesrna/intern/rna_internal_types.h b/source/blender/makesrna/intern/rna_internal_types.h index fce81e6967e..df591659fdb 100644 --- a/source/blender/makesrna/intern/rna_internal_types.h +++ b/source/blender/makesrna/intern/rna_internal_types.h @@ -94,6 +94,7 @@ typedef PointerRNA (*PropPointerGetFunc)(struct PointerRNA *ptr); typedef StructRNA *(*PropPointerTypeFunc)(struct PointerRNA *ptr); typedef void (*PropPointerSetFunc)(struct PointerRNA *ptr, const PointerRNA value); typedef int (*PropPointerPollFunc)(struct PointerRNA *ptr, const PointerRNA value); +typedef int (*PropPointerPollFuncPy)(struct PointerRNA *ptr, const PointerRNA value, const PropertyRNA *prop); typedef void (*PropCollectionBeginFunc)(struct CollectionPropertyIterator *iter, struct PointerRNA *ptr); typedef void (*PropCollectionNextFunc)(struct CollectionPropertyIterator *iter); typedef void (*PropCollectionEndFunc)(struct CollectionPropertyIterator *iter); diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c index 36cf909b299..1a8dd05a7b5 100644 --- a/source/blender/makesrna/intern/rna_modifier.c +++ b/source/blender/makesrna/intern/rna_modifier.c @@ -1526,6 +1526,20 @@ static void rna_def_modifier_mirror(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Mirror V", "Mirror the V texture coordinate around the 0.5 point"); RNA_def_property_update(prop, 0, "rna_Modifier_update"); + prop = RNA_def_property(srna, "mirror_offset_u", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "uv_offset[0]"); + RNA_def_property_range(prop, -1, 1); + RNA_def_property_ui_range(prop, -1, 1, 2, 4); + RNA_def_property_ui_text(prop, "U Offset", "Amount to offset mirrored UVs from the 0.5 point on the U axis"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "mirror_offset_v", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "uv_offset[1]"); + RNA_def_property_range(prop, -1, 1); + RNA_def_property_ui_range(prop, -1, 1, 2, 4); + RNA_def_property_ui_text(prop, "V Offset", "Amount to offset mirrored UVs from the 0.5 point on the V axis"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + prop = RNA_def_property(srna, "merge_threshold", PROP_FLOAT, PROP_DISTANCE); RNA_def_property_float_sdna(prop, NULL, "tolerance"); RNA_def_property_range(prop, 0, FLT_MAX); diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 784004182dd..8d42d746e1c 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -3327,6 +3327,7 @@ static void def_frame(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); RNA_def_struct_sdna_from(srna, "NodeFrame", "storage"); + RNA_def_struct_translation_context(srna, BLT_I18NCONTEXT_ID_NODETREE); prop = RNA_def_property(srna, "shrink", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", NODE_FRAME_SHRINK); diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c index b3c166a6810..ec6a03e713d 100644 --- a/source/blender/makesrna/intern/rna_object.c +++ b/source/blender/makesrna/intern/rna_object.c @@ -2897,6 +2897,10 @@ static void rna_def_dupli_object(BlenderRNA *brna) RNA_def_property_enum_items(prop, dupli_items); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE | PROP_EDITABLE); RNA_def_property_ui_text(prop, "Dupli Type", "Duplicator type that generated this dupli object"); + + prop = RNA_def_property(srna, "random_id", PROP_INT, PROP_UNSIGNED); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE | PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Dupli random id", "Random id for this dupli object"); } static void rna_def_object_base(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_object_api.c b/source/blender/makesrna/intern/rna_object_api.c index c680abe71a4..55852139065 100644 --- a/source/blender/makesrna/intern/rna_object_api.c +++ b/source/blender/makesrna/intern/rna_object_api.c @@ -321,13 +321,30 @@ static void rna_Object_ray_cast( float origin[3], float direction[3], float distance, int *r_success, float r_location[3], float r_normal[3], int *r_index) { - BVHTreeFromMesh treeData = {NULL}; - if (ob->derivedFinal == NULL) { BKE_reportf(reports, RPT_ERROR, "Object '%s' has no mesh data to be used for ray casting", ob->id.name + 2); return; } + *r_success = false; + + /* Test BoundBox first (efficiency) */ + BoundBox *bb = BKE_object_boundbox_get(ob); + if (bb) { + float distmin, distmax; + if (isect_ray_aabb_v3_simple(origin, direction, bb->vec[0], bb->vec[6], &distmin, &distmax)) { + float dist = distmin >= 0 ? distmin : distmax; + if (dist > distance) { + goto finally; + } + } + else { + goto finally; + } + } + + BVHTreeFromMesh treeData = {NULL}; + /* no need to managing allocation or freeing of the BVH data. this is generated and freed as needed */ bvhtree_from_mesh_looptri(&treeData, ob->derivedFinal, 0.0f, 4, 6); @@ -350,20 +367,18 @@ static void rna_Object_ray_cast( copy_v3_v3(r_location, hit.co); copy_v3_v3(r_normal, hit.no); *r_index = dm_looptri_to_poly_index(ob->derivedFinal, &treeData.looptri[hit.index]); - - goto finally; } } - } - - *r_success = false; - zero_v3(r_location); - zero_v3(r_normal); - *r_index = -1; + free_bvhtree_from_mesh(&treeData); + } + if (*r_success == false) { finally: - free_bvhtree_from_mesh(&treeData); + zero_v3(r_location); + zero_v3(r_normal); + *r_index = -1; + } } static void rna_Object_closest_point_on_mesh( diff --git a/source/blender/makesrna/intern/rna_scene_api.c b/source/blender/makesrna/intern/rna_scene_api.c index d4d8e23fdf5..9b202120b82 100644 --- a/source/blender/makesrna/intern/rna_scene_api.c +++ b/source/blender/makesrna/intern/rna_scene_api.c @@ -284,32 +284,36 @@ static void rna_Scene_collada_export( int use_blender_profile, int sort_by_name, int export_transformation_type, - int open_sim) + int open_sim, + int limit_precision, + int keep_bind_info) { - collada_export(scene, - filepath, - - apply_modifiers, - export_mesh_type, - - selected, - include_children, - include_armatures, - include_shapekeys, - deform_bones_only, - - active_uv_only, - include_uv_textures, - include_material_textures, - use_texture_copies, - - triangulate, - use_object_instantiation, - use_blender_profile, - sort_by_name, - - export_transformation_type, - open_sim); + collada_export(scene, + filepath, + + apply_modifiers, + export_mesh_type, + + selected, + include_children, + include_armatures, + include_shapekeys, + deform_bones_only, + + active_uv_only, + include_uv_textures, + include_material_textures, + use_texture_copies, + + triangulate, + use_object_instantiation, + use_blender_profile, + sort_by_name, + + export_transformation_type, + open_sim, + limit_precision, + keep_bind_info); } #endif @@ -374,59 +378,60 @@ void RNA_api_scene(StructRNA *srna) RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); RNA_def_property_subtype(parm, PROP_FILEPATH); /* allow non utf8 */ - RNA_def_boolean(func, - "apply_modifiers", 0, "Apply Modifiers", - "Apply modifiers to exported mesh (non destructive))"); + RNA_def_boolean(func, "apply_modifiers", false, + "Apply Modifiers", "Apply modifiers to exported mesh (non destructive))"); RNA_def_int(func, "export_mesh_type", 0, INT_MIN, INT_MAX, - "Resolution", "Modifier resolution for export", INT_MIN, INT_MAX); + "Resolution", "Modifier resolution for export", INT_MIN, INT_MAX); - RNA_def_boolean(func, "selected", 0, "Selection Only", - "Export only selected elements"); + RNA_def_boolean(func, "selected", false, "Selection Only", "Export only selected elements"); - RNA_def_boolean(func, "include_children", 0, "Include Children", - "Export all children of selected objects (even if not selected)"); + RNA_def_boolean(func, "include_children", false, + "Include Children", "Export all children of selected objects (even if not selected)"); - RNA_def_boolean(func, "include_armatures", 0, "Include Armatures", - "Export related armatures (even if not selected)"); + RNA_def_boolean(func, "include_armatures", false, + "Include Armatures", "Export related armatures (even if not selected)"); - RNA_def_boolean(func, "include_shapekeys", 1, "Include Shape Keys", - "Export all Shape Keys from Mesh Objects"); + RNA_def_boolean(func, "include_shapekeys", true, "Include Shape Keys", "Export all Shape Keys from Mesh Objects"); - RNA_def_boolean(func, "deform_bones_only", 0, "Deform Bones only", - "Only export deforming bones with armatures"); + RNA_def_boolean(func, "deform_bones_only", false, + "Deform Bones only", "Only export deforming bones with armatures"); + RNA_def_boolean(func, "active_uv_only", false, "Only Selected UV Map", "Export only the selected UV Map"); - RNA_def_boolean(func, "active_uv_only", 0, "Only Selected UV Map", - "Export only the selected UV Map"); + RNA_def_boolean(func, "include_uv_textures", false, + "Include UV Textures", "Export textures assigned to the object UV Maps"); - RNA_def_boolean(func, "include_uv_textures", 0, "Include UV Textures", - "Export textures assigned to the object UV Maps"); + RNA_def_boolean(func, "include_material_textures", false, + "Include Material Textures", "Export textures assigned to the object Materials"); - RNA_def_boolean(func, "include_material_textures", 0, "Include Material Textures", - "Export textures assigned to the object Materials"); + RNA_def_boolean(func, "use_texture_copies", true, + "Copy", "Copy textures to same folder where the .dae file is exported"); - RNA_def_boolean(func, "use_texture_copies", 1, "Copy", - "Copy textures to same folder where the .dae file is exported"); + RNA_def_boolean(func, "triangulate", true, "Triangulate", "Export Polygons (Quads & NGons) as Triangles"); + RNA_def_boolean(func, "use_object_instantiation", true, + "Use Object Instances", "Instantiate multiple Objects from same Data"); - RNA_def_boolean(func, "triangulate", 1, "Triangulate", - "Export Polygons (Quads & NGons) as Triangles"); + RNA_def_boolean(func, "use_blender_profile", true, "Use Blender Profile", + "Export additional Blender specific information (for material, shaders, bones, etc.)"); - RNA_def_boolean(func, "use_object_instantiation", 1, "Use Object Instances", - "Instantiate multiple Objects from same Data"); + RNA_def_boolean(func, "sort_by_name", false, "Sort by Object name", "Sort exported data by Object name"); - RNA_def_boolean(func, "use_blender_profile", 1, "Use Blender Profile", - "Export additional Blender specific information (for material, shaders, bones, etc.)"); + RNA_def_int(func, "export_transformation_type", 0, INT_MIN, INT_MAX, + "Transform", "Transformation type for translation, scale and rotation", INT_MIN, INT_MAX); - RNA_def_boolean(func, "sort_by_name", 0, "Sort by Object name", - "Sort exported data by Object name"); + RNA_def_boolean(func, "open_sim", false, + "Export to SL/OpenSim", "Compatibility mode for SL, OpenSim and other compatible online worlds"); - RNA_def_int(func, "export_transformation_type", 0, INT_MIN, INT_MAX, - "Transform", "Transformation type for translation, scale and rotation", INT_MIN, INT_MAX); + RNA_def_boolean(func, "limit_precision", false, + "Limit Precision", + "Reduce the precision of the exported data to 6 digits"); + + RNA_def_boolean(func, "keep_bind_info", false, + "Keep Bind Info", + "Store bind pose information in custom bone properties for later use during Collada export"); - RNA_def_boolean(func, "open_sim", 0, "Export to SL/OpenSim", - "Compatibility mode for SL, OpenSim and other compatible online worlds"); #endif #ifdef WITH_ALEMBIC diff --git a/source/blender/makesrna/intern/rna_ui.c b/source/blender/makesrna/intern/rna_ui.c index 7a3c862f04c..54b82fc89d6 100644 --- a/source/blender/makesrna/intern/rna_ui.c +++ b/source/blender/makesrna/intern/rna_ui.c @@ -1039,6 +1039,7 @@ static void rna_def_uilist(BlenderRNA *brna) RNA_def_struct_refine_func(srna, "rna_UIList_refine"); RNA_def_struct_register_funcs(srna, "rna_UIList_register", "rna_UIList_unregister", NULL); RNA_def_struct_idprops_func(srna, "rna_UIList_idprops"); + RNA_def_struct_flag(srna, STRUCT_NO_DATABLOCK_IDPROPERTIES); /* Registration */ prop = RNA_def_property(srna, "bl_idname", PROP_STRING, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index 6927abcb4f8..629a42e92bd 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -138,23 +138,11 @@ static void rna_userdef_update(Main *UNUSED(bmain), Scene *UNUSED(scene), Pointe } /* also used by buffer swap switching */ -static void rna_userdef_dpi_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *UNUSED(ptr)) +static void rna_userdef_dpi_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *UNUSED(ptr)) { /* font's are stored at each DPI level, without this we can easy load 100's of fonts */ BLF_cache_clear(); - BKE_blender_userdef_refresh(); - WM_main_add_notifier(NC_WINDOW, NULL); /* full redraw */ - WM_main_add_notifier(NC_SCREEN | NA_EDITED, NULL); /* refresh region sizes */ -} - -static void rna_userdef_virtual_pixel_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *UNUSED(ptr)) -{ - /* font's are stored at each DPI level, without this we can easy load 100's of fonts */ - BLF_cache_clear(); - - BKE_blender_userdef_refresh(); - /* force setting drawable again */ wmWindowManager *wm = bmain->wm.first; if (wm) { @@ -3180,6 +3168,7 @@ static void rna_def_userdef_addon_pref(BlenderRNA *brna) RNA_def_struct_refine_func(srna, "rna_AddonPref_refine"); RNA_def_struct_register_funcs(srna, "rna_AddonPref_register", "rna_AddonPref_unregister", NULL); RNA_def_struct_idprops_func(srna, "rna_AddonPref_idprops"); + RNA_def_struct_flag(srna, STRUCT_NO_DATABLOCK_IDPROPERTIES); /* Mandatory! */ /* registration */ RNA_define_verify_sdna(0); @@ -3337,6 +3326,12 @@ static void rna_def_userdef_view(BlenderRNA *brna) RNA_def_struct_ui_text(srna, "View & Controls", "Preferences related to viewing data"); /* View */ + prop = RNA_def_property(srna, "ui_scale", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_ui_text(prop, "UI Scale", "Changes the size of the fonts and buttons in the interface"); + RNA_def_property_range(prop, 0.25f, 4.0f); + RNA_def_property_ui_range(prop, 0.5f, 2.0f, 1, 1); + RNA_def_property_float_default(prop, 1.0f); + RNA_def_property_update(prop, 0, "rna_userdef_dpi_update"); /* display */ prop = RNA_def_property(srna, "show_tooltips", PROP_BOOLEAN, PROP_NONE); @@ -3928,12 +3923,6 @@ static void rna_def_userdef_system(BlenderRNA *brna) {0, NULL, 0, NULL, NULL} }; - static EnumPropertyItem virtual_pixel_mode_items[] = { - {VIRTUAL_PIXEL_NATIVE, "NATIVE", 0, "Native", "Use native pixel size of the display"}, - {VIRTUAL_PIXEL_DOUBLE, "DOUBLE", 0, "Double", "Use double the native pixel size of the display"}, - {0, NULL, 0, NULL, NULL} - }; - srna = RNA_def_struct(brna, "UserPreferencesSystem", NULL); RNA_def_struct_sdna(srna, "UserDef"); RNA_def_struct_nested(brna, srna, "UserPreferences"); @@ -3948,16 +3937,8 @@ static void rna_def_userdef_system(BlenderRNA *brna) RNA_def_property_update(prop, NC_WINDOW, "rna_userdef_language_update"); prop = RNA_def_property(srna, "dpi", PROP_INT, PROP_NONE); - RNA_def_property_int_sdna(prop, NULL, "dpi"); - RNA_def_property_range(prop, 48, 144); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "DPI", "Font size and resolution for display"); - RNA_def_property_update(prop, 0, "rna_userdef_dpi_update"); - - prop = RNA_def_property(srna, "virtual_pixel_mode", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_sdna(prop, NULL, "virtual_pixel"); - RNA_def_property_enum_items(prop, virtual_pixel_mode_items); - RNA_def_property_ui_text(prop, "Virtual Pixel Mode", "Modify the pixel size for hi-res devices"); - RNA_def_property_update(prop, 0, "rna_userdef_virtual_pixel_update"); prop = RNA_def_property(srna, "pixel_size", PROP_FLOAT, PROP_NONE); RNA_def_property_clear_flag(prop, PROP_EDITABLE); @@ -4690,6 +4671,11 @@ void RNA_def_userdef(BlenderRNA *brna) "Active section of the user preferences shown in the user interface"); RNA_def_property_update(prop, 0, "rna_userdef_update"); + /* don't expose this directly via the UI, modify via an operator */ + prop = RNA_def_property(srna, "app_template", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "app_template"); + RNA_def_property_ui_text(prop, "Application Template", ""); + prop = RNA_def_property(srna, "themes", PROP_COLLECTION, PROP_NONE); RNA_def_property_collection_sdna(prop, NULL, "themes", NULL); RNA_def_property_struct_type(prop, "Theme"); diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c index 35c9c9bcc89..b458b2e69d5 100644 --- a/source/blender/makesrna/intern/rna_wm.c +++ b/source/blender/makesrna/intern/rna_wm.c @@ -1556,6 +1556,7 @@ static void rna_def_operator(BlenderRNA *brna) RNA_def_struct_ui_text(srna, "Operator Properties", "Input properties of an Operator"); RNA_def_struct_refine_func(srna, "rna_OperatorProperties_refine"); RNA_def_struct_idprops_func(srna, "rna_OperatorProperties_idprops"); + RNA_def_struct_flag(srna, STRUCT_NO_DATABLOCK_IDPROPERTIES); } static void rna_def_macro_operator(BlenderRNA *brna) diff --git a/source/blender/modifiers/intern/MOD_meshsequencecache.c b/source/blender/modifiers/intern/MOD_meshsequencecache.c index d956763fc1f..f0edcc31a10 100644 --- a/source/blender/modifiers/intern/MOD_meshsequencecache.c +++ b/source/blender/modifiers/intern/MOD_meshsequencecache.c @@ -112,7 +112,7 @@ static DerivedMesh *applyModifier(ModifierData *md, Object *ob, if (!mcmd->reader) { mcmd->reader = CacheReader_open_alembic_object(cache_file->handle, - mcmd->reader, + NULL, ob, mcmd->object_path); if (!mcmd->reader) { diff --git a/source/blender/modifiers/intern/MOD_mirror.c b/source/blender/modifiers/intern/MOD_mirror.c index dd127cc255c..9705edc580c 100644 --- a/source/blender/modifiers/intern/MOD_mirror.c +++ b/source/blender/modifiers/intern/MOD_mirror.c @@ -274,8 +274,8 @@ static DerivedMesh *doMirrorOnAxis(MirrorModifierData *mmd, int j = maxLoops; dmloopuv += j; /* second set of loops only */ for (; j-- > 0; dmloopuv++) { - if (do_mirr_u) dmloopuv->uv[0] = 1.0f - dmloopuv->uv[0]; - if (do_mirr_v) dmloopuv->uv[1] = 1.0f - dmloopuv->uv[1]; + if (do_mirr_u) dmloopuv->uv[0] = 1.0f - dmloopuv->uv[0] + mmd->uv_offset[0]; + if (do_mirr_v) dmloopuv->uv[1] = 1.0f - dmloopuv->uv[1] + mmd->uv_offset[1]; } } } diff --git a/source/blender/modifiers/intern/MOD_surfacedeform.c b/source/blender/modifiers/intern/MOD_surfacedeform.c index 776cf02754e..da9b926d1f4 100644 --- a/source/blender/modifiers/intern/MOD_surfacedeform.c +++ b/source/blender/modifiers/intern/MOD_surfacedeform.c @@ -1120,6 +1120,11 @@ static void surfacedeformModifier_do(ModifierData *md, float (*vertexCos)[3], un tdm = smd->target->derivedFinal; } + if (!tdm) { + modifier_setError(md, "No valid target mesh"); + return; + } + tnumverts = tdm->getNumVerts(tdm); tnumpoly = tdm->getNumPolys(tdm); @@ -1139,12 +1144,10 @@ static void surfacedeformModifier_do(ModifierData *md, float (*vertexCos)[3], un /* Poly count checks */ if (smd->numverts != numverts) { modifier_setError(md, "Verts changed from %u to %u", smd->numverts, numverts); - tdm->release(tdm); return; } else if (smd->numpoly != tnumpoly) { modifier_setError(md, "Target polygons changed from %u to %u", smd->numpoly, tnumpoly); - tdm->release(tdm); return; } @@ -1170,8 +1173,6 @@ static void surfacedeformModifier_do(ModifierData *md, float (*vertexCos)[3], un MEM_freeN(data.targetCos); } - - tdm->release(tdm); } static void deformVerts(ModifierData *md, Object *ob, diff --git a/source/blender/nodes/shader/nodes/node_shader_normal_map.c b/source/blender/nodes/shader/nodes/node_shader_normal_map.c index 48d1688c386..e0bf34f42e4 100644 --- a/source/blender/nodes/shader/nodes/node_shader_normal_map.c +++ b/source/blender/nodes/shader/nodes/node_shader_normal_map.c @@ -63,7 +63,7 @@ static void node_shader_exec_normal_map(void *data, int UNUSED(thread), bNode *n CLAMP_MIN(strength, 0.0f); - float *N = shi->vno; + float *N = shi->nmapnorm; int uv_index = 0; switch (nm->space) { case SHD_NORMAL_MAP_TANGENT: diff --git a/source/blender/nodes/shader/nodes/node_shader_object_info.c b/source/blender/nodes/shader/nodes/node_shader_object_info.c index d1905246fd4..165d565a5b4 100644 --- a/source/blender/nodes/shader/nodes/node_shader_object_info.c +++ b/source/blender/nodes/shader/nodes/node_shader_object_info.c @@ -39,7 +39,16 @@ static bNodeSocketTemplate sh_node_object_info_out[] = { static int node_shader_gpu_object_info(GPUMaterial *mat, bNode *UNUSED(node), bNodeExecData *UNUSED(execdata), GPUNodeStack *in, GPUNodeStack *out) { - return GPU_stack_link(mat, "node_object_info", in, out); + return GPU_stack_link(mat, "node_object_info", in, out, GPU_builtin(GPU_OBJECT_MATRIX), GPU_builtin(GPU_OBJECT_INFO)); +} + +static void node_shader_exec_object_info(void *data, int UNUSED(thread), bNode *UNUSED(node), bNodeExecData *UNUSED(execdata), bNodeStack **UNUSED(in), bNodeStack **out) +{ + ShaderCallData *scd = (ShaderCallData *)data; + copy_v4_v4(out[0]->vec, RE_object_instance_get_matrix(scd->shi->obi, RE_OBJECT_INSTANCE_MATRIX_OB)[3]); + out[1]->vec[0] = RE_object_instance_get_object_pass_index(scd->shi->obi); + out[2]->vec[0] = scd->shi->mat->index; + out[3]->vec[0] = RE_object_instance_get_random_id(scd->shi->obi) * (1.0f/(float)0xFFFFFFFF);; } /* node type definition */ @@ -53,6 +62,7 @@ void register_node_type_sh_object_info(void) node_type_init(&ntype, NULL); node_type_storage(&ntype, "", NULL, NULL); node_type_gpu(&ntype, node_shader_gpu_object_info); + node_type_exec(&ntype, NULL, NULL, node_shader_exec_object_info); nodeRegisterType(&ntype); } diff --git a/source/blender/physics/intern/BPH_mass_spring.cpp b/source/blender/physics/intern/BPH_mass_spring.cpp index 359395b63c4..b694b6e994d 100644 --- a/source/blender/physics/intern/BPH_mass_spring.cpp +++ b/source/blender/physics/intern/BPH_mass_spring.cpp @@ -333,19 +333,14 @@ static int UNUSED_FUNCTION(cloth_calc_helper_forces)(Object *UNUSED(ob), ClothMo return 1; } -BLI_INLINE void cloth_calc_spring_force(ClothModifierData *clmd, ClothSpring *s, float time) +BLI_INLINE void cloth_calc_spring_force(ClothModifierData *clmd, ClothSpring *s) { Cloth *cloth = clmd->clothObject; ClothSimSettings *parms = clmd->sim_parms; Implicit_Data *data = cloth->implicit; - ClothVertex *verts = cloth->verts; bool no_compress = parms->flags & CLOTH_SIMSETTINGS_FLAG_NO_SPRING_COMPRESS; - zero_v3(s->f); - zero_m3(s->dfdx); - zero_m3(s->dfdv); - s->flags &= ~CLOTH_SPRING_FLAG_NEEDED; // calculate force of structural + shear springs @@ -361,31 +356,13 @@ BLI_INLINE void cloth_calc_spring_force(ClothModifierData *clmd, ClothSpring *s, if (s->type & CLOTH_SPRING_TYPE_SEWING) { // TODO: verify, half verified (couldn't see error) // sewing springs usually have a large distance at first so clamp the force so we don't get tunnelling through colission objects - BPH_mass_spring_force_spring_linear(data, s->ij, s->kl, s->restlen, k, parms->Cdis, no_compress, parms->max_sewing, s->f, s->dfdx, s->dfdv); + BPH_mass_spring_force_spring_linear(data, s->ij, s->kl, s->restlen, k, parms->Cdis, no_compress, parms->max_sewing); } else { - BPH_mass_spring_force_spring_linear(data, s->ij, s->kl, s->restlen, k, parms->Cdis, no_compress, 0.0f, s->f, s->dfdx, s->dfdv); + BPH_mass_spring_force_spring_linear(data, s->ij, s->kl, s->restlen, k, parms->Cdis, no_compress, 0.0f); } #endif } - else if (s->type & CLOTH_SPRING_TYPE_GOAL) { -#ifdef CLOTH_FORCE_SPRING_GOAL - float goal_x[3], goal_v[3]; - float k, scaling; - - s->flags |= CLOTH_SPRING_FLAG_NEEDED; - - // current_position = xold + t * (newposition - xold) - /* divide by time_scale to prevent goal vertices' delta locations from being multiplied */ - interp_v3_v3v3(goal_x, verts[s->ij].xold, verts[s->ij].xconst, time / parms->time_scale); - sub_v3_v3v3(goal_v, verts[s->ij].xconst, verts[s->ij].xold); // distance covered over dt==1 - - scaling = parms->goalspring + s->stiffness * fabsf(parms->max_struct - parms->goalspring); - k = verts[s->ij].goal * scaling / (parms->avg_spring_len + FLT_EPSILON); - - BPH_mass_spring_force_spring_goal(data, s->ij, goal_x, goal_v, k, parms->goalfrict * 0.01f, s->f, s->dfdx, s->dfdv); -#endif - } else if (s->type & CLOTH_SPRING_TYPE_BENDING) { /* calculate force of bending springs */ #ifdef CLOTH_FORCE_SPRING_BEND float kb, cb, scaling; @@ -398,7 +375,7 @@ BLI_INLINE void cloth_calc_spring_force(ClothModifierData *clmd, ClothSpring *s, // Fix for [#45084] for cloth stiffness must have cb proportional to kb cb = kb * parms->bending_damping; - BPH_mass_spring_force_spring_bending(data, s->ij, s->kl, s->restlen, kb, cb, s->f, s->dfdx, s->dfdv); + BPH_mass_spring_force_spring_bending(data, s->ij, s->kl, s->restlen, kb, cb); #endif } else if (s->type & CLOTH_SPRING_TYPE_BENDING_ANG) { @@ -474,9 +451,24 @@ static void cloth_calc_force(ClothModifierData *clmd, float UNUSED(frame), ListB /* scale gravity force */ mul_v3_v3fl(gravity, clmd->scene->physics_settings.gravity, 0.001f * clmd->sim_parms->effector_weights->global_gravity); } + vert = cloth->verts; for (i = 0; i < cloth->mvert_num; i++, vert++) { BPH_mass_spring_force_gravity(data, i, vert->mass, gravity); + + /* Vertex goal springs */ + if ((!(vert->flags & CLOTH_VERT_FLAG_PINNED)) && (vert->goal > FLT_EPSILON)) { + float goal_x[3], goal_v[3]; + float k; + + /* divide by time_scale to prevent goal vertices' delta locations from being multiplied */ + interp_v3_v3v3(goal_x, vert->xold, vert->xconst, time / clmd->sim_parms->time_scale); + sub_v3_v3v3(goal_v, vert->xconst, vert->xold); /* distance covered over dt==1 */ + + k = vert->goal * clmd->sim_parms->goalspring / (clmd->sim_parms->avg_spring_len + FLT_EPSILON); + + BPH_mass_spring_force_spring_goal(data, i, goal_x, goal_v, k, clmd->sim_parms->goalfrict * 0.01f); + } } #endif @@ -544,8 +536,9 @@ static void cloth_calc_force(ClothModifierData *clmd, float UNUSED(frame), ListB for (LinkNode *link = cloth->springs; link; link = link->next) { ClothSpring *spring = (ClothSpring *)link->link; // only handle active springs - if (!(spring->flags & CLOTH_SPRING_FLAG_DEACTIVATE)) - cloth_calc_spring_force(clmd, spring, time); + if (!(spring->flags & CLOTH_SPRING_FLAG_DEACTIVATE)) { + cloth_calc_spring_force(clmd, spring); + } } } diff --git a/source/blender/physics/intern/implicit.h b/source/blender/physics/intern/implicit.h index d1a75ca5297..2f62ab98e12 100644 --- a/source/blender/physics/intern/implicit.h +++ b/source/blender/physics/intern/implicit.h @@ -114,19 +114,15 @@ void BPH_mass_spring_force_edge_wind(struct Implicit_Data *data, int v1, int v2, void BPH_mass_spring_force_vertex_wind(struct Implicit_Data *data, int v, float radius, const float (*winvec)[3]); /* Linear spring force between two points */ bool BPH_mass_spring_force_spring_linear(struct Implicit_Data *data, int i, int j, float restlen, - float stiffness, float damping, bool no_compress, float clamp_force, - float r_f[3], float r_dfdx[3][3], float r_dfdv[3][3]); + float stiffness, float damping, bool no_compress, float clamp_force); /* Bending force, forming a triangle at the base of two structural springs */ -bool BPH_mass_spring_force_spring_bending(struct Implicit_Data *data, int i, int j, float restlen, - float kb, float cb, - float r_f[3], float r_dfdx[3][3], float r_dfdv[3][3]); +bool BPH_mass_spring_force_spring_bending(struct Implicit_Data *data, int i, int j, float restlen, float kb, float cb); /* Angular bending force based on local target vectors */ bool BPH_mass_spring_force_spring_bending_angular(struct Implicit_Data *data, int i, int j, int k, const float target[3], float stiffness, float damping); /* Global goal spring */ bool BPH_mass_spring_force_spring_goal(struct Implicit_Data *data, int i, const float goal_x[3], const float goal_v[3], - float stiffness, float damping, - float r_f[3], float r_dfdx[3][3], float r_dfdv[3][3]); + float stiffness, float damping); /* ======== Hair Volumetric Forces ======== */ diff --git a/source/blender/physics/intern/implicit_blender.c b/source/blender/physics/intern/implicit_blender.c index 2ad8ee0547f..16cd335dc0c 100644 --- a/source/blender/physics/intern/implicit_blender.c +++ b/source/blender/physics/intern/implicit_blender.c @@ -1579,8 +1579,7 @@ BLI_INLINE void apply_spring(Implicit_Data *data, int i, int j, const float f[3] } bool BPH_mass_spring_force_spring_linear(Implicit_Data *data, int i, int j, float restlen, - float stiffness, float damping, bool no_compress, float clamp_force, - float r_f[3], float r_dfdx[3][3], float r_dfdv[3][3]) + float stiffness, float damping, bool no_compress, float clamp_force) { float extent[3], length, dir[3], vel[3]; @@ -1608,25 +1607,15 @@ bool BPH_mass_spring_force_spring_linear(Implicit_Data *data, int i, int j, floa apply_spring(data, i, j, f, dfdx, dfdv); - if (r_f) copy_v3_v3(r_f, f); - if (r_dfdx) copy_m3_m3(r_dfdx, dfdx); - if (r_dfdv) copy_m3_m3(r_dfdv, dfdv); - return true; } else { - if (r_f) zero_v3(r_f); - if (r_dfdx) zero_m3(r_dfdx); - if (r_dfdv) zero_m3(r_dfdv); - return false; } } /* See "Stable but Responsive Cloth" (Choi, Ko 2005) */ -bool BPH_mass_spring_force_spring_bending(Implicit_Data *data, int i, int j, float restlen, - float kb, float cb, - float r_f[3], float r_dfdx[3][3], float r_dfdv[3][3]) +bool BPH_mass_spring_force_spring_bending(Implicit_Data *data, int i, int j, float restlen, float kb, float cb) { float extent[3], length, dir[3], vel[3]; @@ -1646,17 +1635,9 @@ bool BPH_mass_spring_force_spring_bending(Implicit_Data *data, int i, int j, flo apply_spring(data, i, j, f, dfdx, dfdv); - if (r_f) copy_v3_v3(r_f, f); - if (r_dfdx) copy_m3_m3(r_dfdx, dfdx); - if (r_dfdv) copy_m3_m3(r_dfdv, dfdv); - return true; } else { - if (r_f) zero_v3(r_f); - if (r_dfdx) zero_m3(r_dfdx); - if (r_dfdv) zero_m3(r_dfdv); - return false; } } @@ -1945,8 +1926,7 @@ bool BPH_mass_spring_force_spring_bending_angular(Implicit_Data *data, int i, in } bool BPH_mass_spring_force_spring_goal(Implicit_Data *data, int i, const float goal_x[3], const float goal_v[3], - float stiffness, float damping, - float r_f[3], float r_dfdx[3][3], float r_dfdv[3][3]) + float stiffness, float damping) { float root_goal_x[3], root_goal_v[3], extent[3], length, dir[3], vel[3]; float f[3], dfdx[3][3], dfdv[3][3]; @@ -1973,17 +1953,9 @@ bool BPH_mass_spring_force_spring_goal(Implicit_Data *data, int i, const float g add_m3_m3m3(data->dFdX[i].m, data->dFdX[i].m, dfdx); add_m3_m3m3(data->dFdV[i].m, data->dFdV[i].m, dfdv); - if (r_f) copy_v3_v3(r_f, f); - if (r_dfdx) copy_m3_m3(r_dfdx, dfdx); - if (r_dfdv) copy_m3_m3(r_dfdv, dfdv); - return true; } else { - if (r_f) zero_v3(r_f); - if (r_dfdx) zero_m3(r_dfdx); - if (r_dfdv) zero_m3(r_dfdv); - return false; } } diff --git a/source/blender/python/generic/idprop_py_api.c b/source/blender/python/generic/idprop_py_api.c index 2e15b7b1413..5d6a7c578a2 100644 --- a/source/blender/python/generic/idprop_py_api.c +++ b/source/blender/python/generic/idprop_py_api.c @@ -43,6 +43,9 @@ #include "python_utildefines.h" +extern bool pyrna_id_FromPyObject(PyObject *obj, ID **id); +extern PyObject *pyrna_id_CreatePyObject(ID *id); +extern bool pyrna_id_CheckPyObject(PyObject *obj); /*********************** ID Property Main Wrapper Stuff ***************/ @@ -88,6 +91,11 @@ static PyObject *idprop_py_from_idp_group(ID *id, IDProperty *prop, IDProperty * return (PyObject *)group; } +static PyObject *idprop_py_from_idp_id(IDProperty *prop) +{ + return pyrna_id_CreatePyObject(prop->data.pointer); +} + static PyObject *idprop_py_from_idp_array(ID *id, IDProperty *prop) { BPy_IDProperty *array = PyObject_New(BPy_IDProperty, &BPy_IDArray_Type); @@ -148,6 +156,7 @@ PyObject *BPy_IDGroup_WrapData(ID *id, IDProperty *prop, IDProperty *parent) case IDP_GROUP: return idprop_py_from_idp_group(id, prop, parent); case IDP_ARRAY: return idprop_py_from_idp_array(id, prop); case IDP_IDPARRAY: return idprop_py_from_idp_idparray(id, prop); /* this could be better a internal type */ + case IDP_ID: return idprop_py_from_idp_id(prop); default: Py_RETURN_NONE; } } @@ -335,19 +344,9 @@ static char idp_sequence_type(PyObject *seq_fast) return type; } -/** - * \note group can be a pointer array or a group. - * assume we already checked key is a string. - * - * \return success. - */ -bool BPy_IDProperty_Map_ValidateAndCreate(PyObject *name_obj, IDProperty *group, PyObject *ob) +static const char *idp_try_read_name(PyObject *name_obj) { - IDProperty *prop = NULL; - IDPropertyTemplate val = {0}; - - const char *name; - + const char *name = NULL; if (name_obj) { Py_ssize_t name_size; name = _PyUnicode_AsStringAndSize(name_obj, &name_size); @@ -356,168 +355,307 @@ bool BPy_IDProperty_Map_ValidateAndCreate(PyObject *name_obj, IDProperty *group, PyErr_Format(PyExc_KeyError, "invalid id-property key, expected a string, not a %.200s", Py_TYPE(name_obj)->tp_name); - return false; + return NULL; } if (name_size > MAX_IDPROP_NAME) { PyErr_SetString(PyExc_KeyError, "the length of IDProperty names is limited to 63 characters"); - return false; + return NULL; } } else { name = ""; } + return name; +} - if (PyFloat_Check(ob)) { - val.d = PyFloat_AsDouble(ob); - prop = IDP_New(IDP_DOUBLE, &val, name); - } - else if (PyLong_Check(ob)) { - val.i = _PyLong_AsInt(ob); - if (val.i == -1 && PyErr_Occurred()) { - return false; - } - prop = IDP_New(IDP_INT, &val, name); +/* -------------------------------------------------------------------------- */ + +/** + * The 'idp_from_Py*' functions expect that the input type has been checked before + * and return NULL if the IDProperty can't be created. + */ + +static IDProperty *idp_from_PyFloat(const char *name, PyObject *ob) +{ + IDPropertyTemplate val = {0}; + val.d = PyFloat_AsDouble(ob); + return IDP_New(IDP_DOUBLE, &val, name); +} + +static IDProperty *idp_from_PyLong(const char *name, PyObject *ob) +{ + IDPropertyTemplate val = {0}; + val.i = _PyLong_AsInt(ob); + if (val.i == -1 && PyErr_Occurred()) { + return NULL; } - else if (PyUnicode_Check(ob)) { + return IDP_New(IDP_INT, &val, name); +} + +static IDProperty *idp_from_PyUnicode(const char *name, PyObject *ob) +{ + IDProperty *prop; + IDPropertyTemplate val = {0}; #ifdef USE_STRING_COERCE - Py_ssize_t value_size; - PyObject *value_coerce = NULL; - val.string.str = PyC_UnicodeAsByteAndSize(ob, &value_size, &value_coerce); - val.string.len = (int)value_size + 1; - val.string.subtype = IDP_STRING_SUB_UTF8; - prop = IDP_New(IDP_STRING, &val, name); - Py_XDECREF(value_coerce); + Py_ssize_t value_size; + PyObject *value_coerce = NULL; + val.string.str = PyC_UnicodeAsByteAndSize(ob, &value_size, &value_coerce); + val.string.len = (int)value_size + 1; + val.string.subtype = IDP_STRING_SUB_UTF8; + prop = IDP_New(IDP_STRING, &val, name); + Py_XDECREF(value_coerce); #else - val.str = _PyUnicode_AsString(ob); - prop = IDP_New(IDP_STRING, val, name); + val.str = _PyUnicode_AsString(ob); + prop = IDP_New(IDP_STRING, val, name); #endif - } - else if (PyBytes_Check(ob)) { - val.string.str = PyBytes_AS_STRING(ob); - val.string.len = PyBytes_GET_SIZE(ob); - val.string.subtype = IDP_STRING_SUB_BYTE; + return prop; +} + +static IDProperty *idp_from_PyBytes(const char *name, PyObject *ob) +{ + IDPropertyTemplate val = {0}; + val.string.str = PyBytes_AS_STRING(ob); + val.string.len = PyBytes_GET_SIZE(ob); + val.string.subtype = IDP_STRING_SUB_BYTE; + return IDP_New(IDP_STRING, &val, name); +} - prop = IDP_New(IDP_STRING, &val, name); - //prop = IDP_NewString(PyBytes_AS_STRING(ob), name, PyBytes_GET_SIZE(ob)); - //prop->subtype = IDP_STRING_SUB_BYTE; +static int idp_array_type_from_format_char(char format) +{ + if (format == 'i') return IDP_INT; + if (format == 'f') return IDP_FLOAT; + if (format == 'd') return IDP_DOUBLE; + return -1; +} + +static const char *idp_format_from_array_type(int type) +{ + if (type == IDP_INT) return "i"; + if (type == IDP_FLOAT) return "f"; + if (type == IDP_DOUBLE) return "d"; + return NULL; +} + +static IDProperty *idp_from_PySequence_Buffer(const char *name, Py_buffer *buffer) +{ + IDProperty *prop; + IDPropertyTemplate val = {0}; + + int format = idp_array_type_from_format_char(*buffer->format); + if (format == -1) { + /* should never happen as the type has been checked before */ + return NULL; } - else if (PySequence_Check(ob)) { - PyObject *ob_seq_fast; - PyObject **ob_seq_fast_items; - PyObject *item; - int i; + else { + val.array.type = format; + val.array.len = buffer->len / buffer->itemsize; + } + prop = IDP_New(IDP_ARRAY, &val, name); + memcpy(IDP_Array(prop), buffer->buf, buffer->len); + return prop; +} - if (!(ob_seq_fast = PySequence_Fast(ob, "py -> idprop"))) { - return false; - } +static IDProperty *idp_from_PySequence_Fast(const char *name, PyObject *ob) +{ + IDProperty *prop; + IDPropertyTemplate val = {0}; - ob_seq_fast_items = PySequence_Fast_ITEMS(ob_seq_fast); + PyObject **ob_seq_fast_items; + PyObject *item; + int i; - if ((val.array.type = idp_sequence_type(ob_seq_fast)) == (char)-1) { - Py_DECREF(ob_seq_fast); - PyErr_SetString(PyExc_TypeError, "only floats, ints and dicts are allowed in ID property arrays"); - return false; - } + ob_seq_fast_items = PySequence_Fast_ITEMS(ob); - /* validate sequence and derive type. - * we assume IDP_INT unless we hit a float - * number; then we assume it's */ + if ((val.array.type = idp_sequence_type(ob)) == (char)-1) { + PyErr_SetString(PyExc_TypeError, "only floats, ints and dicts are allowed in ID property arrays"); + return NULL; + } - val.array.len = PySequence_Fast_GET_SIZE(ob_seq_fast); + /* validate sequence and derive type. + * we assume IDP_INT unless we hit a float + * number; then we assume it's */ - switch (val.array.type) { - case IDP_DOUBLE: - { - double *prop_data; - - prop = IDP_New(IDP_ARRAY, &val, name); - prop_data = IDP_Array(prop); - for (i = 0; i < val.array.len; i++) { - item = ob_seq_fast_items[i]; - if (((prop_data[i] = PyFloat_AsDouble(item)) == -1.0) && PyErr_Occurred()) { - Py_DECREF(ob_seq_fast); - return false; - } + val.array.len = PySequence_Fast_GET_SIZE(ob); + + switch (val.array.type) { + case IDP_DOUBLE: + { + double *prop_data; + prop = IDP_New(IDP_ARRAY, &val, name); + prop_data = IDP_Array(prop); + for (i = 0; i < val.array.len; i++) { + item = ob_seq_fast_items[i]; + if (((prop_data[i] = PyFloat_AsDouble(item)) == -1.0) && PyErr_Occurred()) { + return NULL; } - break; } - case IDP_INT: - { - int *prop_data; - prop = IDP_New(IDP_ARRAY, &val, name); - prop_data = IDP_Array(prop); - for (i = 0; i < val.array.len; i++) { - item = ob_seq_fast_items[i]; - if (((prop_data[i] = _PyLong_AsInt(item)) == -1) && PyErr_Occurred()) { - Py_DECREF(ob_seq_fast); - return false; - } + break; + } + case IDP_INT: + { + int *prop_data; + prop = IDP_New(IDP_ARRAY, &val, name); + prop_data = IDP_Array(prop); + for (i = 0; i < val.array.len; i++) { + item = ob_seq_fast_items[i]; + if (((prop_data[i] = _PyLong_AsInt(item)) == -1) && PyErr_Occurred()) { + return NULL; } - break; } - case IDP_IDPARRAY: - { - prop = IDP_NewIDPArray(name); - for (i = 0; i < val.array.len; i++) { - item = ob_seq_fast_items[i]; - - if (BPy_IDProperty_Map_ValidateAndCreate(NULL, prop, item) == false) { - Py_DECREF(ob_seq_fast); - return false; - } + break; + } + case IDP_IDPARRAY: + { + prop = IDP_NewIDPArray(name); + for (i = 0; i < val.array.len; i++) { + item = ob_seq_fast_items[i]; + if (BPy_IDProperty_Map_ValidateAndCreate(NULL, prop, item) == false) { + return NULL; } - break; } - default: - /* should never happen */ - Py_DECREF(ob_seq_fast); - PyErr_SetString(PyExc_RuntimeError, "internal error with idp array.type"); - return false; + break; } + default: + /* should never happen */ + PyErr_SetString(PyExc_RuntimeError, "internal error with idp array.type"); + return NULL; + } + return prop; +} - Py_DECREF(ob_seq_fast); + +static IDProperty *idp_from_PySequence(const char *name, PyObject *ob) +{ + Py_buffer buffer; + bool use_buffer = false; + + if (PyObject_CheckBuffer(ob)) { + PyObject_GetBuffer(ob, &buffer, PyBUF_SIMPLE | PyBUF_FORMAT); + char format = *buffer.format; + if (ELEM(format, 'i', 'f', 'd')) { + use_buffer = true; + } + else { + PyBuffer_Release(&buffer); + } } - else if (PyMapping_Check(ob)) { - PyObject *keys, *vals, *key, *pval; - int i, len; - /*yay! we get into recursive stuff now!*/ - keys = PyMapping_Keys(ob); - vals = PyMapping_Values(ob); - - /* we allocate the group first; if we hit any invalid data, - * we can delete it easily enough.*/ - prop = IDP_New(IDP_GROUP, &val, name); - len = PyMapping_Length(ob); - for (i = 0; i < len; i++) { - key = PySequence_GetItem(keys, i); - pval = PySequence_GetItem(vals, i); - if (BPy_IDProperty_Map_ValidateAndCreate(key, prop, pval) == false) { - IDP_FreeProperty(prop); - MEM_freeN(prop); - Py_XDECREF(keys); - Py_XDECREF(vals); - Py_XDECREF(key); - Py_XDECREF(pval); - /* error is already set */ - return false; - } + + if (use_buffer) { + IDProperty *prop = idp_from_PySequence_Buffer(name, &buffer); + PyBuffer_Release(&buffer); + return prop; + } + else { + PyObject *ob_seq_fast = PySequence_Fast(ob, "py -> idprop"); + if (ob_seq_fast != NULL) { + IDProperty *prop = idp_from_PySequence_Fast(name, ob_seq_fast); + Py_DECREF(ob_seq_fast); + return prop; + } + else { + return NULL; + } + } +} + +static IDProperty *idp_from_PyMapping(const char *name, PyObject *ob) +{ + IDProperty *prop; + IDPropertyTemplate val = {0}; + + PyObject *keys, *vals, *key, *pval; + int i, len; + /* yay! we get into recursive stuff now! */ + keys = PyMapping_Keys(ob); + vals = PyMapping_Values(ob); + + /* we allocate the group first; if we hit any invalid data, + * we can delete it easily enough.*/ + prop = IDP_New(IDP_GROUP, &val, name); + len = PyMapping_Length(ob); + for (i = 0; i < len; i++) { + key = PySequence_GetItem(keys, i); + pval = PySequence_GetItem(vals, i); + if (BPy_IDProperty_Map_ValidateAndCreate(key, prop, pval) == false) { + IDP_FreeProperty(prop); + MEM_freeN(prop); + Py_XDECREF(keys); + Py_XDECREF(vals); Py_XDECREF(key); Py_XDECREF(pval); + /* error is already set */ + return NULL; } - Py_XDECREF(keys); - Py_XDECREF(vals); + Py_XDECREF(key); + Py_XDECREF(pval); + } + Py_XDECREF(keys); + Py_XDECREF(vals); + return prop; +} + +static IDProperty *idp_from_DatablockPointer(const char *name, PyObject *ob, IDPropertyTemplate *val) +{ + pyrna_id_FromPyObject(ob, &val->id); + return IDP_New(IDP_ID, val, name); +} + +static IDProperty *idp_from_PyObject(PyObject *name_obj, PyObject *ob) +{ + IDPropertyTemplate val = {0}; + const char *name = idp_try_read_name(name_obj); + if (name == NULL) { + return NULL; + } + + if (PyFloat_Check(ob)) { + return idp_from_PyFloat(name, ob); + } + else if (PyLong_Check(ob)) { + return idp_from_PyLong(name, ob); + } + else if (PyUnicode_Check(ob)) { + return idp_from_PyUnicode(name, ob); + } + else if (PyBytes_Check(ob)) { + return idp_from_PyBytes(name, ob); + } + else if (PySequence_Check(ob)) { + return idp_from_PySequence(name, ob); + } + else if (ob == Py_None || pyrna_id_CheckPyObject(ob)) { + return idp_from_DatablockPointer(name, ob, &val); + } + else if (PyMapping_Check(ob)) { + return idp_from_PyMapping(name, ob); } else { PyErr_Format(PyExc_TypeError, "invalid id-property type %.200s not supported", Py_TYPE(ob)->tp_name); + return NULL; + } +} + +/* -------------------------------------------------------------------------- */ + +/** + * \note group can be a pointer array or a group. + * assume we already checked key is a string. + * + * \return success. + */ +bool BPy_IDProperty_Map_ValidateAndCreate(PyObject *name_obj, IDProperty *group, PyObject *ob) +{ + IDProperty *prop = idp_from_PyObject(name_obj, ob); + if (prop == NULL) { return false; } if (group->type == IDP_IDPARRAY) { IDP_AppendArray(group, prop); - // IDP_FreeProperty(item); /* IDP_AppendArray does a shallow copy (memcpy), only free memory */ + /* IDP_AppendArray does a shallow copy (memcpy), only free memory */ MEM_freeN(prop); } else { @@ -613,6 +751,8 @@ static PyObject *BPy_IDGroup_MapDataToPy(IDProperty *prop) return idprop_py_from_idp_float(prop); case IDP_DOUBLE: return idprop_py_from_idp_double(prop); + case IDP_ID: + return idprop_py_from_idp_id(prop); case IDP_ARRAY: { PyObject *seq = PyList_New(prop->len); @@ -1371,6 +1511,44 @@ static PyMappingMethods BPy_IDArray_AsMapping = { (objobjargproc)BPy_IDArray_ass_subscript }; +static int itemsize_by_idarray_type(int array_type) +{ + if (array_type == IDP_INT) return sizeof(int); + if (array_type == IDP_FLOAT) return sizeof(float); + if (array_type == IDP_DOUBLE) return sizeof(double); + return -1; /* should never happen */ +} + +static int BPy_IDArray_getbuffer(BPy_IDArray *self, Py_buffer *view, int flags) +{ + IDProperty *prop = self->prop; + int itemsize = itemsize_by_idarray_type(prop->subtype); + int length = itemsize * prop->len; + + if (PyBuffer_FillInfo(view, (PyObject *)self, IDP_Array(prop), length, false, flags) == -1) { + return -1; + } + + view->itemsize = itemsize; + view->format = (char *)idp_format_from_array_type(prop->subtype); + + Py_ssize_t *shape = MEM_mallocN(sizeof(Py_ssize_t), __func__); + shape[0] = prop->len; + view->shape = shape; + + return 0; +} + +static void BPy_IDArray_releasebuffer(BPy_IDArray *UNUSED(self), Py_buffer *view) +{ + MEM_freeN(view->shape); +} + +static PyBufferProcs BPy_IDArray_Buffer = { + (getbufferproc)BPy_IDArray_getbuffer, + (releasebufferproc)BPy_IDArray_releasebuffer, +}; + PyTypeObject BPy_IDArray_Type = { PyVarObject_HEAD_INIT(NULL, 0) @@ -1403,7 +1581,7 @@ PyTypeObject BPy_IDArray_Type = { NULL, /* setattrofunc tp_setattro; */ /* Functions to access object as input/output buffer */ - NULL, /* PyBufferProcs *tp_as_buffer; */ + &BPy_IDArray_Buffer, /* PyBufferProcs *tp_as_buffer; */ /*** Flags to define presence of optional/expanded features ***/ Py_TPFLAGS_DEFAULT, /* long tp_flags; */ diff --git a/source/blender/python/intern/bpy_props.c b/source/blender/python/intern/bpy_props.c index e61018865ab..2656e612b18 100644 --- a/source/blender/python/intern/bpy_props.c +++ b/source/blender/python/intern/bpy_props.c @@ -50,11 +50,13 @@ #include "../generic/py_capi_utils.h" /* initial definition of callback slots we'll probably have more than 1 */ -#define BPY_DATA_CB_SLOT_SIZE 3 - -#define BPY_DATA_CB_SLOT_UPDATE 0 -#define BPY_DATA_CB_SLOT_GET 1 -#define BPY_DATA_CB_SLOT_SET 2 +enum { + BPY_DATA_CB_SLOT_UPDATE = 0, + BPY_DATA_CB_SLOT_GET = 1, + BPY_DATA_CB_SLOT_SET = 2, + BPY_DATA_CB_SLOT_POLL = 3, + BPY_DATA_CB_SLOT_SIZE = 4, +}; extern BPy_StructRNA *bpy_context_module; @@ -71,6 +73,9 @@ static EnumPropertyItem property_flag_items[] = { " :arg options: Enumerator in ['HIDDEN', 'SKIP_SAVE', 'ANIMATABLE', 'LIBRARY_EDITABLE', 'PROPORTIONAL'," \ "'TEXTEDIT_UPDATE'].\n" \ " :type options: set\n" \ +" :arg poll: function to be called to determine whether an item is valid for this property.\n" \ +" The function must take 2 values (self,object) and return Bool.\n" \ +" :type poll: function\n" \ static EnumPropertyItem property_flag_enum_items[] = { {PROP_HIDDEN, "HIDDEN", 0, "Hidden", ""}, @@ -230,7 +235,7 @@ static PyObject *bpy_prop_deferred_return(PyObject *func, PyObject *kw) static void bpy_prop_update_cb(struct bContext *C, struct PointerRNA *ptr, struct PropertyRNA *prop) { PyGILState_STATE gilstate; - PyObject **py_data = (PyObject **)RNA_property_py_data_get(prop); + PyObject **py_data = RNA_property_py_data_get(prop); PyObject *py_func; PyObject *args; PyObject *self; @@ -279,7 +284,7 @@ static void bpy_prop_update_cb(struct bContext *C, struct PointerRNA *ptr, struc static int bpy_prop_boolean_get_cb(struct PointerRNA *ptr, struct PropertyRNA *prop) { - PyObject **py_data = (PyObject **)RNA_property_py_data_get(prop); + PyObject **py_data = RNA_property_py_data_get(prop); PyObject *py_func; PyObject *args; PyObject *self; @@ -337,7 +342,7 @@ static int bpy_prop_boolean_get_cb(struct PointerRNA *ptr, struct PropertyRNA *p static void bpy_prop_boolean_set_cb(struct PointerRNA *ptr, struct PropertyRNA *prop, int value) { - PyObject **py_data = (PyObject **)RNA_property_py_data_get(prop); + PyObject **py_data = RNA_property_py_data_get(prop); PyObject *py_func; PyObject *args; PyObject *self; @@ -389,9 +394,54 @@ static void bpy_prop_boolean_set_cb(struct PointerRNA *ptr, struct PropertyRNA * } } +static int bpy_prop_poll_cb(struct PointerRNA *self, PointerRNA candidate, struct PropertyRNA *prop) +{ + PyObject *py_self; + PyObject *py_candidate; + PyObject *py_func; + PyObject **py_data = RNA_property_py_data_get(prop); + PyObject *args; + PyObject *ret; + bool result; + const int is_write_ok = pyrna_write_check(); + PyGILState_STATE gilstate = PyGILState_Ensure(); + + BLI_assert(self != NULL); + + py_self = pyrna_struct_as_instance(self); + py_candidate = pyrna_struct_as_instance(&candidate); + py_func = py_data[BPY_DATA_CB_SLOT_POLL]; + + if (!is_write_ok) + pyrna_write_set(true); + + args = PyTuple_New(2); + PyTuple_SET_ITEM(args, 0, py_self); + PyTuple_SET_ITEM(args, 1, py_candidate); + + ret = PyObject_CallObject(py_func, args); + + Py_DECREF(args); + + if (ret == NULL) { + printf_func_error(py_func); + result = false; + } + else { + result = PyObject_IsTrue(ret); + Py_DECREF(ret); + } + + PyGILState_Release(gilstate); + if (!is_write_ok) + pyrna_write_set(false); + + return result; +} + static void bpy_prop_boolean_array_get_cb(struct PointerRNA *ptr, struct PropertyRNA *prop, int *values) { - PyObject **py_data = (PyObject **)RNA_property_py_data_get(prop); + PyObject **py_data = RNA_property_py_data_get(prop); PyObject *py_func; PyObject *args; PyObject *self; @@ -452,7 +502,7 @@ static void bpy_prop_boolean_array_get_cb(struct PointerRNA *ptr, struct Propert static void bpy_prop_boolean_array_set_cb(struct PointerRNA *ptr, struct PropertyRNA *prop, const int *values) { - PyObject **py_data = (PyObject **)RNA_property_py_data_get(prop); + PyObject **py_data = RNA_property_py_data_get(prop); PyObject *py_func; PyObject *args; PyObject *self; @@ -513,7 +563,7 @@ static void bpy_prop_boolean_array_set_cb(struct PointerRNA *ptr, struct Propert static int bpy_prop_int_get_cb(struct PointerRNA *ptr, struct PropertyRNA *prop) { - PyObject **py_data = (PyObject **)RNA_property_py_data_get(prop); + PyObject **py_data = RNA_property_py_data_get(prop); PyObject *py_func; PyObject *args; PyObject *self; @@ -571,7 +621,7 @@ static int bpy_prop_int_get_cb(struct PointerRNA *ptr, struct PropertyRNA *prop) static void bpy_prop_int_set_cb(struct PointerRNA *ptr, struct PropertyRNA *prop, int value) { - PyObject **py_data = (PyObject **)RNA_property_py_data_get(prop); + PyObject **py_data = RNA_property_py_data_get(prop); PyObject *py_func; PyObject *args; PyObject *self; @@ -625,7 +675,7 @@ static void bpy_prop_int_set_cb(struct PointerRNA *ptr, struct PropertyRNA *prop static void bpy_prop_int_array_get_cb(struct PointerRNA *ptr, struct PropertyRNA *prop, int *values) { - PyObject **py_data = (PyObject **)RNA_property_py_data_get(prop); + PyObject **py_data = RNA_property_py_data_get(prop); PyObject *py_func; PyObject *args; PyObject *self; @@ -686,7 +736,7 @@ static void bpy_prop_int_array_get_cb(struct PointerRNA *ptr, struct PropertyRNA static void bpy_prop_int_array_set_cb(struct PointerRNA *ptr, struct PropertyRNA *prop, const int *values) { - PyObject **py_data = (PyObject **)RNA_property_py_data_get(prop); + PyObject **py_data = RNA_property_py_data_get(prop); PyObject *py_func; PyObject *args; PyObject *self; @@ -747,7 +797,7 @@ static void bpy_prop_int_array_set_cb(struct PointerRNA *ptr, struct PropertyRNA static float bpy_prop_float_get_cb(struct PointerRNA *ptr, struct PropertyRNA *prop) { - PyObject **py_data = (PyObject **)RNA_property_py_data_get(prop); + PyObject **py_data = RNA_property_py_data_get(prop); PyObject *py_func; PyObject *args; PyObject *self; @@ -805,7 +855,7 @@ static float bpy_prop_float_get_cb(struct PointerRNA *ptr, struct PropertyRNA *p static void bpy_prop_float_set_cb(struct PointerRNA *ptr, struct PropertyRNA *prop, float value) { - PyObject **py_data = (PyObject **)RNA_property_py_data_get(prop); + PyObject **py_data = RNA_property_py_data_get(prop); PyObject *py_func; PyObject *args; PyObject *self; @@ -859,7 +909,7 @@ static void bpy_prop_float_set_cb(struct PointerRNA *ptr, struct PropertyRNA *pr static void bpy_prop_float_array_get_cb(struct PointerRNA *ptr, struct PropertyRNA *prop, float *values) { - PyObject **py_data = (PyObject **)RNA_property_py_data_get(prop); + PyObject **py_data = RNA_property_py_data_get(prop); PyObject *py_func; PyObject *args; PyObject *self; @@ -920,7 +970,7 @@ static void bpy_prop_float_array_get_cb(struct PointerRNA *ptr, struct PropertyR static void bpy_prop_float_array_set_cb(struct PointerRNA *ptr, struct PropertyRNA *prop, const float *values) { - PyObject **py_data = (PyObject **)RNA_property_py_data_get(prop); + PyObject **py_data = RNA_property_py_data_get(prop); PyObject *py_func; PyObject *args; PyObject *self; @@ -981,7 +1031,7 @@ static void bpy_prop_float_array_set_cb(struct PointerRNA *ptr, struct PropertyR static void bpy_prop_string_get_cb(struct PointerRNA *ptr, struct PropertyRNA *prop, char *value) { - PyObject **py_data = (PyObject **)RNA_property_py_data_get(prop); + PyObject **py_data = RNA_property_py_data_get(prop); PyObject *py_func; PyObject *args; PyObject *self; @@ -1040,7 +1090,7 @@ static void bpy_prop_string_get_cb(struct PointerRNA *ptr, struct PropertyRNA *p static int bpy_prop_string_length_cb(struct PointerRNA *ptr, struct PropertyRNA *prop) { - PyObject **py_data = (PyObject **)RNA_property_py_data_get(prop); + PyObject **py_data = RNA_property_py_data_get(prop); PyObject *py_func; PyObject *args; PyObject *self; @@ -1102,7 +1152,7 @@ static int bpy_prop_string_length_cb(struct PointerRNA *ptr, struct PropertyRNA static void bpy_prop_string_set_cb(struct PointerRNA *ptr, struct PropertyRNA *prop, const char *value) { - PyObject **py_data = (PyObject **)RNA_property_py_data_get(prop); + PyObject **py_data = RNA_property_py_data_get(prop); PyObject *py_func; PyObject *args; PyObject *self; @@ -1163,7 +1213,7 @@ static void bpy_prop_string_set_cb(struct PointerRNA *ptr, struct PropertyRNA *p static int bpy_prop_enum_get_cb(struct PointerRNA *ptr, struct PropertyRNA *prop) { - PyObject **py_data = (PyObject **)RNA_property_py_data_get(prop); + PyObject **py_data = RNA_property_py_data_get(prop); PyObject *py_func; PyObject *args; PyObject *self; @@ -1221,7 +1271,7 @@ static int bpy_prop_enum_get_cb(struct PointerRNA *ptr, struct PropertyRNA *prop static void bpy_prop_enum_set_cb(struct PointerRNA *ptr, struct PropertyRNA *prop, int value) { - PyObject **py_data = (PyObject **)RNA_property_py_data_get(prop); + PyObject **py_data = RNA_property_py_data_get(prop); PyObject *py_func; PyObject *args; PyObject *self; @@ -1598,6 +1648,16 @@ static void bpy_prop_callback_assign_update(struct PropertyRNA *prop, PyObject * } } +static void bpy_prop_callback_assign_pointer(struct PropertyRNA *prop, PyObject *poll_cb) +{ + if (poll_cb && poll_cb != Py_None) { + PyObject **py_data = bpy_prop_py_data_get(prop); + + RNA_def_property_poll_runtime(prop, (void *) bpy_prop_poll_cb); + py_data[BPY_DATA_CB_SLOT_POLL] = poll_cb; + } +} + static void bpy_prop_callback_assign_boolean(struct PropertyRNA *prop, PyObject *get_cb, PyObject *set_cb) { BooleanPropertyGetFunc rna_get_cb = NULL; @@ -1904,7 +1964,7 @@ static void bpy_prop_callback_assign_enum(struct PropertyRNA *prop, PyObject *ge " :type set: function\n" \ #define BPY_PROPDEF_TYPE_DOC \ -" :arg type: A subclass of :class:`bpy.types.PropertyGroup`.\n" \ +" :arg type: A subclass of :class:`bpy.types.PropertyGroup` or :class:`bpy.types.ID`.\n" \ " :type type: class\n" \ #if 0 @@ -2772,7 +2832,7 @@ static PyObject *BPy_EnumProperty(PyObject *self, PyObject *args, PyObject *kw) Py_RETURN_NONE; } -static StructRNA *pointer_type_from_py(PyObject *value, const char *error_prefix) +StructRNA *pointer_type_from_py(PyObject *value, const char *error_prefix) { StructRNA *srna; @@ -2782,25 +2842,18 @@ static StructRNA *pointer_type_from_py(PyObject *value, const char *error_prefix PyObject *msg = PyC_ExceptionBuffer(); const char *msg_char = _PyUnicode_AsString(msg); PyErr_Format(PyExc_TypeError, - "%.200s expected an RNA type derived from PropertyGroup, failed with: %s", + "%.200s expected an RNA type, failed with: %s", error_prefix, msg_char); Py_DECREF(msg); } else { PyErr_Format(PyExc_TypeError, - "%.200s expected an RNA type derived from PropertyGroup, failed with type '%s'", + "%.200s expected an RNA type, failed with type '%s'", error_prefix, Py_TYPE(value)->tp_name); } return NULL; } - if (!RNA_struct_is_a(srna, &RNA_PropertyGroup)) { - PyErr_Format(PyExc_TypeError, - "%.200s expected an RNA type derived from PropertyGroup", - error_prefix); - return NULL; - } - return srna; } @@ -2809,7 +2862,8 @@ PyDoc_STRVAR(BPy_PointerProperty_doc, "name=\"\", " "description=\"\", " "options={'ANIMATABLE'}, " - "update=None)\n" + "update=None,\n" + "poll=None)\n" "\n" " Returns a new pointer property definition.\n" "\n" @@ -2819,14 +2873,14 @@ BPY_PROPDEF_DESC_DOC BPY_PROPDEF_OPTIONS_DOC BPY_PROPDEF_UPDATE_DOC ); -static PyObject *BPy_PointerProperty(PyObject *self, PyObject *args, PyObject *kw) +PyObject *BPy_PointerProperty(PyObject *self, PyObject *args, PyObject *kw) { StructRNA *srna; BPY_PROPDEF_HEAD(PointerProperty); if (srna) { - static const char *kwlist[] = {"attr", "type", "name", "description", "options", "update", NULL}; + static const char *kwlist[] = {"attr", "type", "name", "description", "options", "poll", "update", NULL}; const char *id = NULL, *name = NULL, *description = ""; int id_len; PropertyRNA *prop; @@ -2834,33 +2888,47 @@ static PyObject *BPy_PointerProperty(PyObject *self, PyObject *args, PyObject *k PyObject *type = Py_None; PyObject *pyopts = NULL; int opts = 0; - PyObject *update_cb = NULL; + PyObject *update_cb = NULL, *poll_cb = NULL; if (!PyArg_ParseTupleAndKeywords(args, kw, - "s#O|ssO!O:PointerProperty", + "s#O|ssO!OOO:PointerProperty", (char **)kwlist, &id, &id_len, &type, &name, &description, &PySet_Type, &pyopts, - &update_cb)) + &poll_cb, &update_cb)) { return NULL; } BPY_PROPDEF_CHECK(PointerProperty, property_flag_items); - ptype = pointer_type_from_py(type, "PointerProperty(...):"); + ptype = pointer_type_from_py(type, "PointerProperty(...)"); if (!ptype) return NULL; - + if (!RNA_struct_is_a(ptype, &RNA_PropertyGroup) && !RNA_struct_is_ID(ptype)) { + PyErr_Format(PyExc_TypeError, + "PointerProperty(...) expected an RNA type derived from %.200s or %.200s", + RNA_struct_ui_name(&RNA_ID), RNA_struct_ui_name(&RNA_PropertyGroup)); + return NULL; + } if (bpy_prop_callback_check(update_cb, "update", 2) == -1) { return NULL; } - + if (bpy_prop_callback_check(poll_cb, "poll", 2) == -1) { + return NULL; + } prop = RNA_def_pointer_runtime(srna, id, ptype, name ? name : id, description); if (pyopts) { bpy_prop_assign_flag(prop, opts); } + + if (RNA_struct_idprops_contains_datablock(ptype)) { + if (RNA_struct_is_a(srna, &RNA_PropertyGroup)) { + RNA_def_struct_flag(srna, STRUCT_CONTAINS_DATABLOCK_IDPROPERTIES); + } + } bpy_prop_callback_assign_update(prop, update_cb); + bpy_prop_callback_assign_pointer(prop, poll_cb); RNA_def_property_duplicate_pointers(srna, prop); } Py_RETURN_NONE; @@ -2879,7 +2947,7 @@ BPY_PROPDEF_NAME_DOC BPY_PROPDEF_DESC_DOC BPY_PROPDEF_OPTIONS_DOC ); -static PyObject *BPy_CollectionProperty(PyObject *self, PyObject *args, PyObject *kw) +PyObject *BPy_CollectionProperty(PyObject *self, PyObject *args, PyObject *kw) { StructRNA *srna; @@ -2910,10 +2978,23 @@ static PyObject *BPy_CollectionProperty(PyObject *self, PyObject *args, PyObject if (!ptype) return NULL; + if (!RNA_struct_is_a(ptype, &RNA_PropertyGroup)) { + PyErr_Format(PyExc_TypeError, + "CollectionProperty(...) expected an RNA type derived from %.200s", + RNA_struct_ui_name(&RNA_ID), RNA_struct_ui_name(&RNA_PropertyGroup)); + return NULL; + } + prop = RNA_def_collection_runtime(srna, id, ptype, name ? name : id, description); if (pyopts) { bpy_prop_assign_flag(prop, opts); } + + if (RNA_struct_idprops_contains_datablock(ptype)) { + if (RNA_struct_is_a(srna, &RNA_PropertyGroup)) { + RNA_def_struct_flag(srna, STRUCT_CONTAINS_DATABLOCK_IDPROPERTIES); + } + } RNA_def_property_duplicate_pointers(srna, prop); } Py_RETURN_NONE; diff --git a/source/blender/python/intern/bpy_props.h b/source/blender/python/intern/bpy_props.h index c9934ca0cf3..614c1b4b708 100644 --- a/source/blender/python/intern/bpy_props.h +++ b/source/blender/python/intern/bpy_props.h @@ -30,6 +30,10 @@ PyObject *BPY_rna_props(void); +PyObject *BPy_PointerProperty(PyObject *self, PyObject *args, PyObject *kw); +PyObject *BPy_CollectionProperty(PyObject *self, PyObject *args, PyObject *kw); +StructRNA *pointer_type_from_py(PyObject *value, const char *error_prefix); + #define PYRNA_STACK_ARRAY 32 #endif diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index 2fd46ab94f0..00627030db3 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -1934,16 +1934,10 @@ static int pyrna_py_to_prop( } else { /* data == NULL, assign to RNA */ - if (value == Py_None) { - PointerRNA valueptr = {{NULL}}; - RNA_property_pointer_set(ptr, prop, valueptr); - } - else if (RNA_struct_is_a(param->ptr.type, ptr_type)) { - RNA_property_pointer_set(ptr, prop, param->ptr); - } - else { + if (value == Py_None || RNA_struct_is_a(param->ptr.type, ptr_type)) + RNA_property_pointer_set(ptr, prop, value == Py_None ? PointerRNA_NULL : param->ptr); + else raise_error = true; - } } if (raise_error) { @@ -3277,6 +3271,16 @@ static int pyrna_struct_ass_subscript(BPy_StructRNA *self, PyObject *key, PyObje return -1; } + BPy_StructRNA *val = (BPy_StructRNA *)value; + if (val && self->ptr.type && val->ptr.type) { + if (!RNA_struct_idprops_datablock_allowed(self->ptr.type) && + RNA_struct_idprops_contains_datablock(val->ptr.type)) + { + PyErr_SetString(PyExc_TypeError, "bpy_struct[key] = val: datablock id properties not supported for this type"); + return -1; + } + } + return BPy_Wrap_SetMapItem(group, key, value); } @@ -6745,7 +6749,7 @@ PyObject *pyrna_id_CreatePyObject(ID *id) bool pyrna_id_FromPyObject(PyObject *obj, ID **id) { - if (BPy_StructRNA_Check(obj) && (RNA_struct_is_ID(((BPy_StructRNA *)obj)->ptr.type))) { + if (pyrna_id_CheckPyObject(obj)) { *id = ((BPy_StructRNA *)obj)->ptr.id.data; return true; } @@ -6755,6 +6759,11 @@ bool pyrna_id_FromPyObject(PyObject *obj, ID **id) } } +bool pyrna_id_CheckPyObject(PyObject *obj) +{ + return BPy_StructRNA_Check(obj) && (RNA_struct_is_ID(((BPy_StructRNA *) obj)->ptr.type)); +} + void BPY_rna_init(void) { #ifdef USE_MATHUTILS /* register mathutils callbacks, ok to run more than once. */ @@ -7089,6 +7098,21 @@ static int deferred_register_prop(StructRNA *srna, PyObject *key, PyObject *item args_fake = PyTuple_New(1); PyTuple_SET_ITEM(args_fake, 0, py_srna_cobject); + PyObject *type = PyDict_GetItemString(py_kw, "type"); + StructRNA *type_srna = srna_from_self(type, ""); + if (type_srna) { + if (!RNA_struct_idprops_datablock_allowed(srna) && + (*(PyCFunctionWithKeywords)PyCFunction_GET_FUNCTION(py_func) == BPy_PointerProperty || + *(PyCFunctionWithKeywords)PyCFunction_GET_FUNCTION(py_func) == BPy_CollectionProperty) && + RNA_struct_idprops_contains_datablock(type_srna)) + { + PyErr_Format(PyExc_ValueError, + "bpy_struct \"%.200s\" doesn't support datablock properties \n", + RNA_struct_identifier(srna)); + return -1; + } + } + py_ret = PyObject_Call(py_func, args_fake, py_kw); if (py_ret) { diff --git a/source/blender/python/intern/bpy_rna.h b/source/blender/python/intern/bpy_rna.h index e38d4f095d6..605f79b1ad8 100644 --- a/source/blender/python/intern/bpy_rna.h +++ b/source/blender/python/intern/bpy_rna.h @@ -179,6 +179,7 @@ PyObject *pyrna_prop_CreatePyObject(PointerRNA *ptr, PropertyRNA *prop); /* extern'd by other modules which don't deal closely with RNA */ PyObject *pyrna_id_CreatePyObject(struct ID *id); bool pyrna_id_FromPyObject(PyObject *obj, struct ID **id); +bool pyrna_id_CheckPyObject(PyObject *obj); /* operators also need this to set args */ int pyrna_pydict_to_props(PointerRNA *ptr, PyObject *kw, const bool all_args, const char *error_prefix); diff --git a/source/blender/python/mathutils/mathutils_Matrix.c b/source/blender/python/mathutils/mathutils_Matrix.c index 4e980e4c0e6..bd44e77e7c6 100644 --- a/source/blender/python/mathutils/mathutils_Matrix.c +++ b/source/blender/python/mathutils/mathutils_Matrix.c @@ -1301,7 +1301,7 @@ PyDoc_STRVAR(Matrix_to_scale_doc, " :return: Return the scale of a matrix.\n" " :rtype: :class:`Vector`\n" "\n" -" .. note:: This method does not return negative a scale on any axis because it is not possible to obtain this data from the matrix alone.\n" +" .. note:: This method does not return a negative scale on any axis because it is not possible to obtain this data from the matrix alone.\n" ); static PyObject *Matrix_to_scale(MatrixObject *self) { @@ -1390,11 +1390,11 @@ PyDoc_STRVAR(Matrix_invert_doc, "\n" " Set the matrix to its inverse.\n" "\n" -" :arg fallback: Set the matrix to this value when the inverse can't be calculated\n" +" :arg fallback: Set the matrix to this value when the inverse cannot be calculated\n" " (instead of raising a :exc:`ValueError` exception).\n" " :type fallback: :class:`Matrix`\n" "\n" -" .. seealso:: <https://en.wikipedia.org/wiki/Inverse_matrix>\n" +" .. seealso:: `Inverse matrix <https://en.wikipedia.org/wiki/Inverse_matrix>` on Wikipedia.\n" ); static PyObject *Matrix_invert(MatrixObject *self, PyObject *args) { @@ -1505,7 +1505,7 @@ PyDoc_STRVAR(Matrix_invert_safe_doc, " If degenerated (e.g. zero scale on an axis), add some epsilon to its diagonal, to get an invertible one.\n" " If tweaked matrix is still degenerated, set to the identity matrix instead.\n" "\n" -" .. seealso:: <https://en.wikipedia.org/wiki/Inverse_matrix>\n" +" .. seealso:: `Inverse Matrix <https://en.wikipedia.org/wiki/Inverse_matrix>` on Wikipedia.\n" ); static PyObject *Matrix_invert_safe(MatrixObject *self) { @@ -1554,9 +1554,9 @@ PyDoc_STRVAR(Matrix_adjugate_doc, "\n" " Set the matrix to its adjugate.\n" "\n" -" .. note:: When the matrix cant be adjugated a :exc:`ValueError` exception is raised.\n" +" .. note:: When the matrix cannot be adjugated a :exc:`ValueError` exception is raised.\n" "\n" -" .. seealso:: <https://en.wikipedia.org/wiki/Adjugate_matrix>\n" +" .. seealso:: `Adjugate matrix <https://en.wikipedia.org/wiki/Adjugate_matrix>` on Wikipedia.\n" ); static PyObject *Matrix_adjugate(MatrixObject *self) { @@ -1733,7 +1733,7 @@ PyDoc_STRVAR(Matrix_determinant_doc, " :return: Return the determinant of a matrix.\n" " :rtype: float\n" "\n" -" .. seealso:: <https://en.wikipedia.org/wiki/Determinant>\n" +" .. seealso:: `Determinant <https://en.wikipedia.org/wiki/Determinant>` on Wikipedia.\n" ); static PyObject *Matrix_determinant(MatrixObject *self) { @@ -1755,7 +1755,7 @@ PyDoc_STRVAR(Matrix_transpose_doc, "\n" " Set the matrix to its transpose.\n" "\n" -" .. seealso:: <https://en.wikipedia.org/wiki/Transpose>\n" +" .. seealso:: `Transpose <https://en.wikipedia.org/wiki/Transpose>` on Wikipedia.\n" ); static PyObject *Matrix_transpose(MatrixObject *self) { @@ -1887,10 +1887,10 @@ PyDoc_STRVAR(Matrix_identity_doc, "\n" " Set the matrix to the identity matrix.\n" "\n" -" .. note:: An object with zero location and rotation, a scale of one,\n" +" .. note:: An object with a location and rotation of zero, and a scale of one\n" " will have an identity matrix.\n" "\n" -" .. seealso:: <https://en.wikipedia.org/wiki/Identity_matrix>\n" +" .. seealso:: `Identity matrix <https://en.wikipedia.org/wiki/Identity_matrix>` on Wikipedia.\n" ); static PyObject *Matrix_identity(MatrixObject *self) { diff --git a/source/blender/render/extern/include/RE_pipeline.h b/source/blender/render/extern/include/RE_pipeline.h index f535aa5aa71..eaa4cf2c69c 100644 --- a/source/blender/render/extern/include/RE_pipeline.h +++ b/source/blender/render/extern/include/RE_pipeline.h @@ -381,7 +381,7 @@ bool RE_allow_render_generic_object(struct Object *ob); /* RE_updateRenderInstances flag */ enum { RE_OBJECT_INSTANCES_UPDATE_VIEW = (1 << 0), - RE_OBJECT_INSTANCES_UPDATE_OBMAT = (1 << 1), + RE_OBJECT_INSTANCES_UPDATE_OBMAT = (1 << 1) }; void RE_updateRenderInstances(Render *re, int flag); diff --git a/source/blender/render/extern/include/RE_shader_ext.h b/source/blender/render/extern/include/RE_shader_ext.h index ae389fdfd2e..b64c0c8fc52 100644 --- a/source/blender/render/extern/include/RE_shader_ext.h +++ b/source/blender/render/extern/include/RE_shader_ext.h @@ -178,6 +178,7 @@ typedef struct ShadeInput { unsigned int lay; int layflag, passflag, combinedflag; + short object_pass_index; struct Group *light_override; struct Material *mat_override; @@ -241,6 +242,9 @@ enum { const float (*RE_object_instance_get_matrix(struct ObjectInstanceRen *obi, int matrix_id))[4]; +float RE_object_instance_get_object_pass_index(struct ObjectInstanceRen *obi); +float RE_object_instance_get_random_id(struct ObjectInstanceRen *obi); + enum { RE_VIEW_MATRIX, RE_VIEWINV_MATRIX, diff --git a/source/blender/render/intern/include/render_types.h b/source/blender/render/intern/include/render_types.h index b3a5ccdae17..f0323340899 100644 --- a/source/blender/render/intern/include/render_types.h +++ b/source/blender/render/intern/include/render_types.h @@ -382,6 +382,8 @@ typedef struct ObjectInstanceRen { float part_co[3]; float part_vel[3]; float part_avel[3]; + + unsigned int random_id; } ObjectInstanceRen; /* ------------------------------------------------------------------------- */ diff --git a/source/blender/render/intern/source/render_texture.c b/source/blender/render/intern/source/render_texture.c index b4a14f5337d..42e4d4f27f6 100644 --- a/source/blender/render/intern/source/render_texture.c +++ b/source/blender/render/intern/source/render_texture.c @@ -1112,14 +1112,15 @@ static int multitex(Tex *tex, const short which_output, struct ImagePool *pool, const bool skip_load_image, - const bool texnode_preview) + const bool texnode_preview, + const bool use_nodes) { float tmpvec[3]; int retval = 0; /* return value, int:0, col:1, nor:2, everything:3 */ texres->talpha = false; /* is set when image texture returns alpha (considered premul) */ - if (tex->use_nodes && tex->nodetree) { + if (use_nodes && tex->use_nodes && tex->nodetree) { retval = ntreeTexExecTree(tex->nodetree, texres, texvec, dxt, dyt, osatex, thread, tex, which_output, R.r.cfra, texnode_preview, NULL, NULL); } @@ -1239,7 +1240,8 @@ static int multitex_nodes_intern(Tex *tex, ImagePool *pool, const bool scene_color_manage, const bool skip_load_image, - const bool texnode_preview) + const bool texnode_preview, + const bool use_nodes) { if (tex==NULL) { memset(texres, 0, sizeof(TexResult)); @@ -1264,7 +1266,8 @@ static int multitex_nodes_intern(Tex *tex, which_output, pool, skip_load_image, - texnode_preview); + texnode_preview, + use_nodes); if (mtex->mapto & (MAP_COL+MAP_COLSPEC+MAP_COLMIR)) { ImBuf *ibuf = BKE_image_pool_acquire_ibuf(tex->ima, &tex->iuser, pool); @@ -1311,7 +1314,8 @@ static int multitex_nodes_intern(Tex *tex, which_output, pool, skip_load_image, - texnode_preview); + texnode_preview, + use_nodes); { ImBuf *ibuf = BKE_image_pool_acquire_ibuf(tex->ima, &tex->iuser, pool); @@ -1341,7 +1345,8 @@ static int multitex_nodes_intern(Tex *tex, which_output, pool, skip_load_image, - texnode_preview); + texnode_preview, + use_nodes); } } @@ -1354,7 +1359,8 @@ int multitex_nodes(Tex *tex, float texvec[3], float dxt[3], float dyt[3], int os return multitex_nodes_intern(tex, texvec, dxt, dyt, osatex, texres, thread, which_output, shi, mtex, pool, R.scene_color_manage, (R.r.scemode & R_NO_IMAGE_LOAD) != 0, - (R.r.scemode & R_TEXNODE_PREVIEW) != 0); + (R.r.scemode & R_TEXNODE_PREVIEW) != 0, + true); } /* this is called for surface shading */ @@ -1378,7 +1384,8 @@ static int multitex_mtex(ShadeInput *shi, MTex *mtex, float texvec[3], float dxt mtex->which_output, pool, skip_load_image, - (R.r.scemode & R_TEXNODE_PREVIEW) != 0); + (R.r.scemode & R_TEXNODE_PREVIEW) != 0, + true); } } @@ -1408,7 +1415,8 @@ int multitex_ext(Tex *tex, pool, scene_color_manage, skip_load_image, - false); + false, + true); } /* extern-tex doesn't support nodes (ntreeBeginExec() can't be called when rendering is going on)\ @@ -1417,13 +1425,19 @@ int multitex_ext(Tex *tex, */ int multitex_ext_safe(Tex *tex, float texvec[3], TexResult *texres, struct ImagePool *pool, bool scene_color_manage, const bool skip_load_image) { - int use_nodes= tex->use_nodes, retval; - - tex->use_nodes = false; - retval= multitex_nodes_intern(tex, texvec, NULL, NULL, 0, texres, 0, 0, NULL, NULL, pool, scene_color_manage, skip_load_image, false); - tex->use_nodes= use_nodes; - - return retval; + return multitex_nodes_intern(tex, + texvec, + NULL, NULL, + 0, + texres, + 0, + 0, + NULL, NULL, + pool, + scene_color_manage, + skip_load_image, + false, + false); } @@ -2873,7 +2887,8 @@ void do_volume_tex(ShadeInput *shi, const float *xyz, int mapto_flag, float col_ mtex->which_output, re->pool, skip_load_image, - texnode_preview); /* NULL = dxt/dyt, 0 = shi->osatex - not supported */ + texnode_preview, + true); /* NULL = dxt/dyt, 0 = shi->osatex - not supported */ /* texture output */ @@ -3051,7 +3066,8 @@ void do_halo_tex(HaloRen *har, float xn, float yn, float col_r[4]) mtex->which_output, har->pool, skip_load_image, - texnode_preview); + texnode_preview, + true); /* texture output */ if (rgb && (mtex->texflag & MTEX_RGBTOINT)) { @@ -3274,7 +3290,8 @@ void do_sky_tex( mtex->which_output, R.pool, skip_load_image, - texnode_preview); + texnode_preview, + true); /* texture output */ if (rgb && (mtex->texflag & MTEX_RGBTOINT)) { @@ -3500,7 +3517,8 @@ void do_lamp_tex(LampRen *la, const float lavec[3], ShadeInput *shi, float col_r mtex->which_output, R.pool, skip_load_image, - texnode_preview); + texnode_preview, + true); /* texture output */ if (rgb && (mtex->texflag & MTEX_RGBTOINT)) { @@ -3614,7 +3632,8 @@ int externtex(MTex *mtex, mtex->which_output, pool, skip_load_image, - texnode_preview); + texnode_preview, + true); if (rgb) { texr.tin = IMB_colormanagement_get_luminance(&texr.tr); diff --git a/source/blender/render/intern/source/renderdatabase.c b/source/blender/render/intern/source/renderdatabase.c index 76e6ca8d467..199322795f3 100644 --- a/source/blender/render/intern/source/renderdatabase.c +++ b/source/blender/render/intern/source/renderdatabase.c @@ -66,6 +66,7 @@ #include "BLI_math.h" #include "BLI_blenlib.h" #include "BLI_utildefines.h" +#include "BLI_hash.h" #include "DNA_material_types.h" #include "DNA_meshdata_types.h" @@ -1458,6 +1459,14 @@ ObjectInstanceRen *RE_addRenderInstance( } } + /* Fill object info */ + if (dob) { + obi->random_id = dob->random_id; + } + else { + obi->random_id = BLI_hash_int_2d(BLI_hash_string(obi->ob->id.name + 2), 0); + } + RE_updateRenderInstance(re, obi, RE_OBJECT_INSTANCES_UPDATE_OBMAT | RE_OBJECT_INSTANCES_UPDATE_VIEW); if (mat) { diff --git a/source/blender/render/intern/source/shadeoutput.c b/source/blender/render/intern/source/shadeoutput.c index a8fb72fb7f8..8dea0930b9e 100644 --- a/source/blender/render/intern/source/shadeoutput.c +++ b/source/blender/render/intern/source/shadeoutput.c @@ -2141,6 +2141,16 @@ const float (*RE_object_instance_get_matrix(struct ObjectInstanceRen *obi, int m return NULL; } +float RE_object_instance_get_object_pass_index(struct ObjectInstanceRen *obi) +{ + return obi->ob->index; +} + +float RE_object_instance_get_random_id(struct ObjectInstanceRen *obi) +{ + return obi->random_id; +} + const float (*RE_render_current_get_matrix(int matrix_id))[4] { switch (matrix_id) { diff --git a/source/blender/windowmanager/CMakeLists.txt b/source/blender/windowmanager/CMakeLists.txt index b6245a8c0d1..925563a0777 100644 --- a/source/blender/windowmanager/CMakeLists.txt +++ b/source/blender/windowmanager/CMakeLists.txt @@ -128,10 +128,6 @@ if(WITH_PYTHON) ../python ) add_definitions(-DWITH_PYTHON) - - if(WITH_PYTHON_SECURITY) - add_definitions(-DWITH_PYTHON_SECURITY) - endif() endif() if(WITH_GAMEENGINE) diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c index 4351cd22b18..d0522fdd7d4 100644 --- a/source/blender/windowmanager/intern/wm.c +++ b/source/blender/windowmanager/intern/wm.c @@ -489,7 +489,7 @@ void wm_close_and_free_all(bContext *C, ListBase *wmlist) while ((wm = wmlist->first)) { wm_close_and_free(C, wm); BLI_remlink(wmlist, wm); - BKE_libblock_free_data(bmain, &wm->id); + BKE_libblock_free_data(bmain, &wm->id, true); MEM_freeN(wm); } } diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index 60a361122c2..028355f9af3 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -316,7 +316,7 @@ static void wm_window_match_do(bContext *C, ListBase *oldwmlist) } /* in case UserDef was read, we re-initialize all, and do versioning */ -static void wm_init_userdef(bContext *C, const bool from_memory) +static void wm_init_userdef(bContext *C, const bool use_factory_settings) { Main *bmain = CTX_data_main(C); @@ -336,7 +336,7 @@ static void wm_init_userdef(bContext *C, const bool from_memory) } /* avoid re-saving for every small change to our prefs, allow overrides */ - if (from_memory) { + if (use_factory_settings) { BLO_update_defaults_userpref_blend(); } @@ -470,6 +470,10 @@ static void wm_file_read_post(bContext *C, bool is_startup_file) if (is_startup_file) { /* possible python hasn't been initialized */ if (CTX_py_init_get(C)) { + /* Only run when we have a template path found. */ + if (BKE_appdir_app_template_any()) { + BPY_execute_string(C, "__import__('bl_app_template_utils').reset()"); + } /* sync addons, these may have changed from the defaults */ BPY_execute_string(C, "__import__('addon_utils').reset_all()"); @@ -632,22 +636,31 @@ bool WM_file_read(bContext *C, const char *filepath, ReportList *reports) * Called on startup, (context entirely filled with NULLs) * or called for 'New File' both startup.blend and userpref.blend are checked. * - * \param from_memory: Ignore on-disk startup file, use bundled ``datatoc_startup_blend`` instead. + * \param use_factory_settings: Ignore on-disk startup file, use bundled ``datatoc_startup_blend`` instead. * Used for "Restore Factory Settings". * \param filepath_startup_override: Optional path pointing to an alternative blend file (may be NULL). + * \param app_template_override: Template to use instead of the template defined in user-preferences. + * When not-null, this is written into the user preferences. */ int wm_homefile_read( bContext *C, ReportList *reports, - bool from_memory, const char *filepath_startup_override) + bool use_factory_settings, bool use_empty_data, + const char *filepath_startup_override, const char *app_template_override) { ListBase wmbase; + bool success = false; + char filepath_startup[FILE_MAX]; char filepath_userdef[FILE_MAX]; - bool success = false; + + /* When 'app_template' is set: '{BLENDER_USER_CONFIG}/{app_template}' */ + char app_template_system[FILE_MAX]; + /* When 'app_template' is set: '{BLENDER_SYSTEM_SCRIPTS}/startup/bl_app_templates_system/{app_template}' */ + char app_template_config[FILE_MAX]; /* Indicates whether user preferences were really load from memory. * - * This is used for versioning code, and for this we can not rely on from_memory + * This is used for versioning code, and for this we can not rely on use_factory_settings * passed via argument. This is because there might be configuration folder * exists but it might not have userpref.blend and in this case we fallback to * reading home file from memory. @@ -658,7 +671,7 @@ int wm_homefile_read( eBLOReadSkip skip_flags = 0; /* options exclude eachother */ - BLI_assert((from_memory && filepath_startup_override) == 0); + BLI_assert((use_factory_settings && filepath_startup_override) == 0); if ((G.f & G_SCRIPT_OVERRIDE_PREF) == 0) { BKE_BIT_TEST_SET(G.f, (U.flag & USER_SCRIPT_AUTOEXEC_DISABLE) == 0, G_SCRIPT_AUTOEXEC); @@ -669,34 +682,32 @@ int wm_homefile_read( UI_view2d_zoom_cache_reset(); G.relbase_valid = 0; - if (!from_memory) { - const char * const cfgdir = BKE_appdir_folder_id(BLENDER_USER_CONFIG, NULL); - if (filepath_startup_override) { - BLI_strncpy(filepath_startup, filepath_startup_override, FILE_MAX); - if (cfgdir) { - BLI_make_file_string(G.main->name, filepath_userdef, cfgdir, BLENDER_USERPREF_FILE); - } - else { - filepath_userdef[0] = '\0'; - } - } - else if (cfgdir) { - BLI_make_file_string(G.main->name, filepath_startup, cfgdir, BLENDER_STARTUP_FILE); - BLI_make_file_string(G.main->name, filepath_userdef, cfgdir, BLENDER_USERPREF_FILE); + /* put aside screens to match with persistent windows later */ + wm_window_match_init(C, &wmbase); + + filepath_startup[0] = '\0'; + filepath_userdef[0] = '\0'; + app_template_system[0] = '\0'; + app_template_config[0] = '\0'; + + const char * const cfgdir = BKE_appdir_folder_id(BLENDER_USER_CONFIG, NULL); + if (!use_factory_settings) { + if (cfgdir) { + BLI_path_join(filepath_startup, sizeof(filepath_startup), cfgdir, BLENDER_STARTUP_FILE, NULL); + BLI_path_join(filepath_userdef, sizeof(filepath_startup), cfgdir, BLENDER_USERPREF_FILE, NULL); } else { - filepath_startup[0] = '\0'; - filepath_userdef[0] = '\0'; - from_memory = true; + use_factory_settings = true; + } + + if (filepath_startup_override) { + BLI_strncpy(filepath_startup, filepath_startup_override, FILE_MAX); } } - - /* put aside screens to match with persistent windows later */ - wm_window_match_init(C, &wmbase); - + /* load preferences before startup.blend */ - if (!from_memory && BLI_exists(filepath_userdef)) { + if (!use_factory_settings && BLI_exists(filepath_userdef)) { UserDef *userdef = BKE_blendfile_userdef_read(filepath_userdef, NULL); if (userdef != NULL) { BKE_blender_userdef_set_data(userdef); @@ -708,7 +719,43 @@ int wm_homefile_read( } } - if (!from_memory) { + const char *app_template = NULL; + + if (filepath_startup_override != NULL) { + /* pass */ + } + else if (app_template_override) { + app_template = app_template_override; + } + else if (!use_factory_settings && U.app_template[0]) { + app_template = U.app_template; + } + + if (app_template != NULL) { + BKE_appdir_app_template_id_search(app_template, app_template_system, sizeof(app_template_system)); + BLI_path_join(app_template_config, sizeof(app_template_config), cfgdir, app_template, NULL); + } + + /* insert template name into startup file */ + if (app_template != NULL) { + /* note that the path is being set even when 'use_factory_settings == true' + * this is done so we can load a templates factory-settings */ + if (!use_factory_settings) { + BLI_path_join(filepath_startup, sizeof(filepath_startup), app_template_config, BLENDER_STARTUP_FILE, NULL); + if (BLI_access(filepath_startup, R_OK) != 0) { + filepath_startup[0] = '\0'; + } + } + else { + filepath_startup[0] = '\0'; + } + + if (filepath_startup[0] == '\0') { + BLI_path_join(filepath_startup, sizeof(filepath_startup), app_template_system, BLENDER_STARTUP_FILE, NULL); + } + } + + if (!use_factory_settings || (filepath_startup[0] != '\0')) { if (BLI_access(filepath_startup, R_OK) == 0) { success = (BKE_blendfile_read(C, filepath_startup, NULL, skip_flags) != BKE_BLENDFILE_READ_FAIL); } @@ -720,8 +767,8 @@ int wm_homefile_read( } if (success == false && filepath_startup_override && reports) { + /* We can not return from here because wm is already reset */ BKE_reportf(reports, RPT_ERROR, "Could not read '%s'", filepath_startup_override); - /*We can not return from here because wm is already reset*/ } if (success == false) { @@ -731,15 +778,50 @@ int wm_homefile_read( if (BLI_listbase_is_empty(&wmbase)) { wm_clear_default_size(C); } - BKE_tempdir_init(U.tempdir); + } -#ifdef WITH_PYTHON_SECURITY - /* use alternative setting for security nuts - * otherwise we'd need to patch the binary blob - startup.blend.c */ - U.flag |= USER_SCRIPT_AUTOEXEC_DISABLE; -#endif + if (use_empty_data) { + BKE_blendfile_read_make_empty(C); } - + + /* Load template preferences, + * unlike regular preferences we only use some of the settings, + * see: BKE_blender_userdef_set_app_template */ + if (app_template_system[0] != '\0') { + char temp_path[FILE_MAX]; + temp_path[0] = '\0'; + if (!use_factory_settings) { + BLI_path_join(temp_path, sizeof(temp_path), app_template_config, BLENDER_USERPREF_FILE, NULL); + if (BLI_access(temp_path, R_OK) != 0) { + temp_path[0] = '\0'; + } + } + + if (temp_path[0] == '\0') { + BLI_path_join(temp_path, sizeof(temp_path), app_template_system, BLENDER_USERPREF_FILE, NULL); + } + + UserDef *userdef_template = NULL; + /* just avoids missing file warning */ + if (BLI_exists(temp_path)) { + userdef_template = BKE_blendfile_userdef_read(temp_path, NULL); + } + if (userdef_template == NULL) { + /* we need to have preferences load to overwrite preferences from previous template */ + userdef_template = BKE_blendfile_userdef_read_from_memory( + datatoc_startup_blend, datatoc_startup_blend_size, NULL); + } + if (userdef_template) { + BKE_blender_userdef_set_app_template(userdef_template); + BKE_blender_userdef_free_data(userdef_template); + MEM_freeN(userdef_template); + } + } + + if (app_template_override) { + BLI_strncpy(U.app_template, app_template_override, sizeof(U.app_template)); + } + /* prevent buggy files that had G_FILE_RELATIVE_REMAP written out by mistake. Screws up autosaves otherwise * can remove this eventually, only in a 2.53 and older, now its not written */ G.fileflags &= ~G_FILE_RELATIVE_REMAP; @@ -754,11 +836,14 @@ int wm_homefile_read( G.main->name[0] = '\0'; /* When loading factory settings, the reset solid OpenGL lights need to be applied. */ - if (!G.background) GPU_default_lights(); - - /* XXX */ - G.save_over = 0; // start with save preference untitled.blend - G.fileflags &= ~G_FILE_AUTOPLAY; /* disable autoplay in startup.blend... */ + if (!G.background) { + GPU_default_lights(); + } + + /* start with save preference untitled.blend */ + G.save_over = 0; + /* disable auto-play in startup.blend... */ + G.fileflags &= ~G_FILE_AUTOPLAY; wm_file_read_post(C, true); @@ -1274,6 +1359,13 @@ static int wm_homefile_write_exec(bContext *C, wmOperator *op) char filepath[FILE_MAX]; int fileflags; + const char *app_template = U.app_template[0] ? U.app_template : NULL; + const char * const cfgdir = BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, app_template); + if (cfgdir == NULL) { + BKE_report(op->reports, RPT_ERROR, "Unable to create user config path"); + return OPERATOR_CANCELLED; + } + BLI_callback_exec(G.main, NULL, BLI_CB_EVT_SAVE_PRE); /* check current window and close it if temp */ @@ -1283,7 +1375,8 @@ static int wm_homefile_write_exec(bContext *C, wmOperator *op) /* update keymaps in user preferences */ WM_keyconfig_update(wm); - BLI_make_file_string("/", filepath, BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL), BLENDER_STARTUP_FILE); + BLI_path_join(filepath, sizeof(filepath), cfgdir, BLENDER_STARTUP_FILE, NULL); + printf("trying to save homefile at %s ", filepath); ED_editors_flush_edits(C, false); @@ -1361,21 +1454,44 @@ static int wm_userpref_write_exec(bContext *C, wmOperator *op) { wmWindowManager *wm = CTX_wm_manager(C); char filepath[FILE_MAX]; + const char *cfgdir; + bool ok = false; /* update keymaps in user preferences */ WM_keyconfig_update(wm); - BLI_make_file_string("/", filepath, BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL), BLENDER_USERPREF_FILE); - printf("trying to save userpref at %s ", filepath); - - if (BKE_blendfile_userdef_write(filepath, op->reports) == 0) { - printf("fail\n"); - return OPERATOR_CANCELLED; + if ((cfgdir = BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL))) { + BLI_path_join(filepath, sizeof(filepath), cfgdir, BLENDER_USERPREF_FILE, NULL); + printf("trying to save userpref at %s ", filepath); + if (BKE_blendfile_userdef_write(filepath, op->reports) != 0) { + printf("ok\n"); + ok = true; + } + else { + printf("fail\n"); + } + } + else { + BKE_report(op->reports, RPT_ERROR, "Unable to create userpref path"); } - printf("ok\n"); + if (U.app_template[0] && (cfgdir = BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, U.app_template))) { + /* Also save app-template prefs */ + BLI_path_join(filepath, sizeof(filepath), cfgdir, BLENDER_USERPREF_FILE, NULL); + printf("trying to save app-template userpref at %s ", filepath); + if (BKE_blendfile_userdef_write(filepath, op->reports) == 0) { + printf("fail\n"); + ok = true; + } + else { + printf("ok\n"); + } + } + else if (U.app_template[0]) { + BKE_report(op->reports, RPT_ERROR, "Unable to create app-template userpref path"); + } - return OPERATOR_FINISHED; + return ok ? OPERATOR_FINISHED : OPERATOR_CANCELLED; } void WM_OT_save_userpref(wmOperatorType *ot) @@ -1410,11 +1526,11 @@ void WM_OT_read_history(wmOperatorType *ot) static int wm_homefile_read_exec(bContext *C, wmOperator *op) { - const bool from_memory = (STREQ(op->type->idname, "WM_OT_read_factory_settings")); + const bool use_factory_settings = (STREQ(op->type->idname, "WM_OT_read_factory_settings")); char filepath_buf[FILE_MAX]; const char *filepath = NULL; - if (!from_memory) { + if (!use_factory_settings) { PropertyRNA *prop = RNA_struct_find_property(op->ptr, "filepath"); /* This can be used when loading of a start-up file should only change @@ -1436,9 +1552,27 @@ static int wm_homefile_read_exec(bContext *C, wmOperator *op) G.fileflags &= ~G_FILE_NO_UI; } - if (wm_homefile_read(C, op->reports, from_memory, filepath)) { - /* Load a file but keep the splash open */ - if (!from_memory && RNA_boolean_get(op->ptr, "use_splash")) { + char app_template_buf[sizeof(U.app_template)]; + const char *app_template; + PropertyRNA *prop_app_template = RNA_struct_find_property(op->ptr, "app_template"); + const bool use_splash = !use_factory_settings && RNA_boolean_get(op->ptr, "use_splash"); + const bool use_empty_data = RNA_boolean_get(op->ptr, "use_empty"); + + if (prop_app_template && RNA_property_is_set(op->ptr, prop_app_template)) { + RNA_property_string_get(op->ptr, prop_app_template, app_template_buf); + app_template = app_template_buf; + } + else if (!use_factory_settings) { + /* TODO: dont reset prefs on 'New File' */ + BLI_strncpy(app_template_buf, U.app_template, sizeof(app_template_buf)); + app_template = app_template_buf; + } + else { + app_template = NULL; + } + + if (wm_homefile_read(C, op->reports, use_factory_settings, use_empty_data, filepath, app_template)) { + if (use_splash) { WM_init_splash(C); } return OPERATOR_FINISHED; @@ -1468,21 +1602,36 @@ void WM_OT_read_homefile(wmOperatorType *ot) "Load user interface setup from the .blend file"); RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, "use_empty", false, "Empty", ""); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + /* So the splash can be kept open after loading a file (for templates). */ prop = RNA_def_boolean(ot->srna, "use_splash", false, "Splash", ""); RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + prop = RNA_def_string(ot->srna, "app_template", "Template", sizeof(U.app_template), "", ""); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + /* omit poll to run in background mode */ } void WM_OT_read_factory_settings(wmOperatorType *ot) { + PropertyRNA *prop; + ot->name = "Load Factory Settings"; ot->idname = "WM_OT_read_factory_settings"; ot->description = "Load default file and user preferences"; ot->invoke = WM_operator_confirm; ot->exec = wm_homefile_read_exec; + + prop = RNA_def_string(ot->srna, "app_template", "Template", sizeof(U.app_template), "", ""); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + + prop = RNA_def_boolean(ot->srna, "use_empty", false, "Empty", ""); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + /* omit poll to run in background mode */ } diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index be74a0c7362..9bafe72d805 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -192,7 +192,7 @@ void WM_init(bContext *C, int argc, const char **argv) wm_init_reports(C); /* get the default database, plus a wm */ - wm_homefile_read(C, NULL, G.factory_startup, NULL); + wm_homefile_read(C, NULL, G.factory_startup, false, NULL, NULL); BLT_lang_set(NULL); diff --git a/source/blender/windowmanager/intern/wm_keymap.c b/source/blender/windowmanager/intern/wm_keymap.c index e201fa433d4..db5fc23146f 100644 --- a/source/blender/windowmanager/intern/wm_keymap.c +++ b/source/blender/windowmanager/intern/wm_keymap.c @@ -121,6 +121,13 @@ static void wm_keymap_item_properties_update_ot(wmKeyMapItem *kmi) if (ot->srna != kmi->ptr->type) { /* matches wm_keymap_item_properties_set but doesnt alloc new ptr */ WM_operator_properties_create_ptr(kmi->ptr, ot); + /* 'kmi->ptr->data' NULL'd above, keep using existing properties. + * Note: the operators property types may have changed, + * we will need a more comprehensive sanitize function to support this properly. + */ + if (kmi->properties) { + kmi->ptr->data = kmi->properties; + } WM_operator_properties_sanitize(kmi->ptr, 1); } } diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index 9d4c3986678..d827ccafda8 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -900,7 +900,7 @@ void WM_operator_properties_create_ptr(PointerRNA *ptr, wmOperatorType *ot) void WM_operator_properties_create(PointerRNA *ptr, const char *opstring) { - wmOperatorType *ot = WM_operatortype_find(opstring, 0); + wmOperatorType *ot = WM_operatortype_find(opstring, false); if (ot) WM_operator_properties_create_ptr(ptr, ot); @@ -1403,20 +1403,6 @@ static void dialog_exec_cb(bContext *C, void *arg1, void *arg2) } } -static void popup_check_cb(bContext *C, void *op_ptr, void *UNUSED(arg)) -{ - wmOperator *op = op_ptr; - if (op->type->check) { - if (op->type->check(C, op)) { - /* check for popup and re-layout buttons */ - ARegion *ar_menu = CTX_wm_menu(C); - if (ar_menu) { - ED_region_tag_refresh_ui(ar_menu); - } - } - } -} - /* Dialogs are popups that require user verification (click OK) before exec */ static uiBlock *wm_block_dialog_create(bContext *C, ARegion *ar, void *userData) { @@ -1435,8 +1421,6 @@ static uiBlock *wm_block_dialog_create(bContext *C, ARegion *ar, void *userData) layout = UI_block_layout(block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 0, 0, data->width, data->height, 0, style); - UI_block_func_set(block, popup_check_cb, op, NULL); - uiLayoutOperatorButs(C, layout, op, NULL, 'H', UI_LAYOUT_OP_SHOW_TITLE); /* clear so the OK button is left alone */ @@ -1475,8 +1459,6 @@ static uiBlock *wm_operator_ui_create(bContext *C, ARegion *ar, void *userData) layout = UI_block_layout(block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 0, 0, data->width, data->height, 0, style); - UI_block_func_set(block, popup_check_cb, op, NULL); - /* since ui is defined the auto-layout args are not used */ uiLayoutOperatorButs(C, layout, op, NULL, 'V', 0); @@ -1523,7 +1505,7 @@ int WM_operator_ui_popup(bContext *C, wmOperator *op, int width, int height) data->width = width; data->height = height; data->free_op = true; /* if this runs and gets registered we may want not to free it */ - UI_popup_block_ex(C, wm_operator_ui_create, NULL, wm_operator_ui_popup_cancel, data); + UI_popup_block_ex(C, wm_operator_ui_create, NULL, wm_operator_ui_popup_cancel, data, op); return OPERATOR_RUNNING_MODAL; } @@ -1553,7 +1535,7 @@ static int wm_operator_props_popup_ex(bContext *C, wmOperator *op, if (!do_redo || !(U.uiflag & USER_GLOBALUNDO)) return WM_operator_props_dialog_popup(C, op, 15 * UI_UNIT_X, UI_UNIT_Y); - UI_popup_block_ex(C, wm_block_create_redo, NULL, wm_block_redo_cancel_cb, op); + UI_popup_block_ex(C, wm_block_create_redo, NULL, wm_block_redo_cancel_cb, op, op); if (do_call) wm_block_redo_cb(C, op, 0); @@ -1595,7 +1577,7 @@ int WM_operator_props_dialog_popup(bContext *C, wmOperator *op, int width, int h data->free_op = true; /* if this runs and gets registered we may want not to free it */ /* op is not executed until popup OK but is clicked */ - UI_popup_block_ex(C, wm_block_dialog_create, wm_operator_ui_popup_ok, wm_operator_ui_popup_cancel, data); + UI_popup_block_ex(C, wm_block_dialog_create, wm_operator_ui_popup_ok, wm_operator_ui_popup_cancel, data, op); return OPERATOR_RUNNING_MODAL; } @@ -1762,6 +1744,36 @@ static uiBlock *wm_block_create_splash(bContext *C, ARegion *ar, void *UNUSED(ar ibuf = IMB_ibImageFromMemory((unsigned char *)datatoc_splash_png, datatoc_splash_png_size, IB_rect, NULL, "<splash screen>"); } + + /* overwrite splash with template image */ + if (U.app_template[0] != '\0') { + ImBuf *ibuf_template = NULL; + char splash_filepath[FILE_MAX]; + char template_directory[FILE_MAX]; + + if (BKE_appdir_app_template_id_search( + U.app_template, + template_directory, sizeof(template_directory))) + { + BLI_join_dirfile( + splash_filepath, sizeof(splash_filepath), template_directory, + (U.pixelsize == 2) ? "splash_2x.png" : "splash.png"); + ibuf_template = IMB_loadiffname(splash_filepath, IB_rect, NULL); + if (ibuf_template) { + const int x_expect = ibuf->x; + const int y_expect = 230 * (int)U.pixelsize; + /* don't cover the header text */ + if (ibuf_template->x == x_expect && ibuf_template->y == y_expect) { + memcpy(ibuf->rect, ibuf_template->rect, ibuf_template->x * ibuf_template->y * sizeof(char[4])); + } + else { + printf("Splash expected %dx%d found %dx%d, ignoring: %s\n", + x_expect, y_expect, ibuf_template->x, ibuf_template->y, splash_filepath); + } + IMB_freeImBuf(ibuf_template); + } + } + } #endif block = UI_block_begin(C, ar, "_popup", UI_EMBOSS); diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index 2d43c47679d..aaf77946412 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -77,6 +77,7 @@ #include "GPU_extensions.h" #include "GPU_init_exit.h" #include "GPU_glew.h" +#include "BLF_api.h" /* for assert */ #ifndef NDEBUG @@ -311,7 +312,7 @@ void wm_window_close(bContext *C, wmWindowManager *wm, wmWindow *win) if (tmpwin == NULL) do_exit = 1; - if ((U.uiflag & USER_QUIT_PROMPT) && !wm->file_saved) { + if ((U.uiflag & USER_QUIT_PROMPT) && !wm->file_saved && !G.background) { if (do_exit) { if (!GHOST_confirmQuit(win->ghostwin)) return; @@ -374,14 +375,39 @@ void wm_window_title(wmWindowManager *wm, wmWindow *win) } } -static float wm_window_get_virtual_pixelsize(void) +static void wm_window_set_dpi(wmWindow *win) { - return ((U.virtual_pixel == VIRTUAL_PIXEL_NATIVE) ? 1.0f : 2.0f); -} + int auto_dpi = GHOST_GetDPIHint(win->ghostwin); -float wm_window_pixelsize(wmWindow *win) -{ - return (GHOST_GetNativePixelSize(win->ghostwin) * wm_window_get_virtual_pixelsize()); + /* Lazily init UI scale size, preserving backwards compatibility by + * computing UI scale from ratio of previous DPI and auto DPI */ + if (U.ui_scale == 0) { + int virtual_pixel = (U.virtual_pixel == VIRTUAL_PIXEL_NATIVE) ? 1 : 2; + + if (U.dpi == 0) { + U.ui_scale = virtual_pixel; + } + else { + U.ui_scale = (virtual_pixel * U.dpi * 96.0f) / (auto_dpi * 72.0f); + } + + CLAMP(U.ui_scale, 0.25f, 4.0f); + } + + /* Blender's UI drawing assumes DPI 72 as a good default following macOS + * while Windows and Linux use DPI 96. GHOST assumes a default 96 so we + * remap the DPI to Blender's convention. */ + int dpi = auto_dpi * U.ui_scale * (72.0 / 96.0f); + + /* Automatically set larger pixel size for high DPI. */ + int pixelsize = MAX2(1, dpi / 54); + + /* Set user preferences globals for drawing, and for forward compatibility. */ + U.pixelsize = GHOST_GetNativePixelSize(win->ghostwin) * pixelsize; + U.dpi = dpi / pixelsize; + U.virtual_pixel = (pixelsize == 1) ? VIRTUAL_PIXEL_NATIVE : VIRTUAL_PIXEL_DOUBLE; + + BKE_blender_userdef_refresh(); } /* belongs to below */ @@ -456,10 +482,8 @@ static void wm_window_ghostwindow_add(wmWindowManager *wm, const char *title, wm glClear(GL_COLOR_BUFFER_BIT); } - /* displays with larger native pixels, like Macbook. Used to scale dpi with */ /* needed here, because it's used before it reads userdef */ - U.pixelsize = wm_window_pixelsize(win); - BKE_blender_userdef_refresh(); + wm_window_set_dpi(win); wm_window_swap_buffers(win); @@ -626,7 +650,6 @@ wmWindow *WM_window_open_temp(bContext *C, const rcti *rect_init, int type) Scene *scene = CTX_data_scene(C); const char *title; rcti rect = *rect_init; - const short px_virtual = (short)wm_window_get_virtual_pixelsize(); /* changes rect to fit within desktop */ wm_window_check_position(&rect); @@ -644,9 +667,8 @@ wmWindow *WM_window_open_temp(bContext *C, const rcti *rect_init, int type) win->posy = rect.ymin; } - /* multiply with virtual pixelsize, ghost handles native one (e.g. for retina) */ - win->sizex = BLI_rcti_size_x(&rect) * px_virtual; - win->sizey = BLI_rcti_size_y(&rect) * px_virtual; + win->sizex = BLI_rcti_size_x(&rect); + win->sizey = BLI_rcti_size_y(&rect); if (win->ghostwin) { wm_window_set_size(win, win->sizex, win->sizey); @@ -835,8 +857,7 @@ void wm_window_make_drawable(wmWindowManager *wm, wmWindow *win) GHOST_ActivateWindowDrawingContext(win->ghostwin); /* this can change per window */ - U.pixelsize = wm_window_pixelsize(win); - BKE_blender_userdef_refresh(); + wm_window_set_dpi(win); } } @@ -1035,6 +1056,8 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr if (type == GHOST_kEventWindowSize) { WM_jobs_stop(wm, win->screen, NULL); } + + wm_window_set_dpi(win); /* win32: gives undefined window size when minimized */ if (state != GHOST_kWindowStateMinimized) { @@ -1118,7 +1141,19 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr } break; } - + + case GHOST_kEventWindowDPIHintChanged: + { + wm_window_set_dpi(win); + /* font's are stored at each DPI level, without this we can easy load 100's of fonts */ + BLF_cache_clear(); + + BKE_blender_userdef_refresh(); + WM_main_add_notifier(NC_WINDOW, NULL); /* full redraw */ + WM_main_add_notifier(NC_SCREEN | NA_EDITED, NULL); /* refresh region sizes */ + break; + } + case GHOST_kEventOpenMainFile: { PointerRNA props_ptr; @@ -1199,11 +1234,9 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr { // only update if the actual pixel size changes float prev_pixelsize = U.pixelsize; - U.pixelsize = wm_window_pixelsize(win); + wm_window_set_dpi(win); if (U.pixelsize != prev_pixelsize) { - BKE_blender_userdef_refresh(); - // close all popups since they are positioned with the pixel // size baked in and it's difficult to correct them wmWindow *oldWindow = CTX_wm_window(C); diff --git a/source/blender/windowmanager/wm_files.h b/source/blender/windowmanager/wm_files.h index 954d35722f3..9a1518e15b0 100644 --- a/source/blender/windowmanager/wm_files.h +++ b/source/blender/windowmanager/wm_files.h @@ -37,7 +37,8 @@ struct wmOperatorType; void wm_history_file_read(void); int wm_homefile_read( struct bContext *C, struct ReportList *reports, - bool from_memory, const char *filepath_startup_override); + bool use_factory_settings, bool use_empty_data, + const char *filepath_startup_override, const char *app_template_override); void wm_file_read_report(bContext *C); void WM_OT_save_homefile(struct wmOperatorType *ot); diff --git a/source/blender/windowmanager/wm_window.h b/source/blender/windowmanager/wm_window.h index c106f9d7851..f70ec6b47f6 100644 --- a/source/blender/windowmanager/wm_window.h +++ b/source/blender/windowmanager/wm_window.h @@ -63,8 +63,6 @@ void wm_window_swap_buffers (wmWindow *win); void wm_window_set_swap_interval(wmWindow *win, int interval); bool wm_window_get_swap_interval(wmWindow *win, int *intervalOut); -float wm_window_pixelsize(wmWindow *win); - void wm_get_cursor_position (wmWindow *win, int *x, int *y); void wm_cursor_position_from_ghost (wmWindow *win, int *x, int *y); void wm_cursor_position_to_ghost (wmWindow *win, int *x, int *y); |