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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJacques Lucke <jacques@blender.org>2020-10-22 16:02:27 +0300
committerJacques Lucke <jacques@blender.org>2020-10-22 16:02:27 +0300
commit87218899be2fae7622fe778380bcff10fe0fb463 (patch)
tree18b417bb3e882416fbbe5bec222d80a66b190eac
parentffa0a6df9d94df07613bf2e34c6ea10e378d84ac (diff)
Geometry Nodes: add an initial geometry class
-rw-r--r--source/blender/blenkernel/BKE_geometry.hh204
-rw-r--r--source/blender/blenkernel/CMakeLists.txt2
-rw-r--r--source/blender/blenkernel/intern/geometry.cc152
3 files changed, 358 insertions, 0 deletions
diff --git a/source/blender/blenkernel/BKE_geometry.hh b/source/blender/blenkernel/BKE_geometry.hh
new file mode 100644
index 00000000000..e2532d639c4
--- /dev/null
+++ b/source/blender/blenkernel/BKE_geometry.hh
@@ -0,0 +1,204 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+/** \file
+ * \ingroup bke
+ */
+
+#include <atomic>
+#include <iostream>
+
+#include "BLI_hash.hh"
+
+struct Mesh;
+
+namespace blender::bke {
+
+/**
+ * Geometry can contain geometry of different types, such as meshes and curves (although currently
+ * only meshes are supported).
+ *
+ * Geometries are reference counted. This allows them to be shared without making unnecessary
+ * copies. A geometry that is shared is immutable. If some code wants to change it,
+ * #make_geometry_mutable should be called first.
+ */
+class Geometry {
+ private:
+ /* Number of users of this geometry. If this number goes to zero, the geometry is freed. If it is
+ * above 1, the geometry is immutable. */
+ std::atomic<int> users_ = 1;
+
+ /* Only contains a mesh for now. */
+ Mesh *mesh_ = nullptr;
+ /* Determines if the mesh is freed when the geometry does not want to reference it anymore. */
+ bool mesh_owned_ = false;
+
+ public:
+ Geometry() = default;
+ Geometry(const Geometry &other);
+ Geometry(Geometry &&other) = delete;
+ ~Geometry();
+
+ /* Disable copy and move assignment operators. */
+ Geometry &operator=(const Geometry &other) = delete;
+ Geometry &operator=(Geometry &&other) = delete;
+
+ void user_add();
+ void user_remove();
+ bool is_mutable() const;
+
+ void mesh_set_and_keep_ownership(Mesh *mesh);
+ void mesh_set_and_transfer_ownership(Mesh *mesh);
+ void mesh_reset();
+ Mesh *mesh_get_for_read();
+ Mesh *mesh_get_for_write();
+ Mesh *mesh_release();
+};
+
+/**
+ * A simple automatic reference counter. This should probably be moved to another file eventually.
+ * It is similar to std::shared_ptr, but expects that the reference count is inside the object.
+ */
+template<typename T> class UserCounter {
+ private:
+ T *data_ = nullptr;
+
+ public:
+ UserCounter() = default;
+
+ UserCounter(T *data) : data_(data)
+ {
+ }
+
+ UserCounter(const UserCounter &other) : data_(other.data_)
+ {
+ this->user_add(data_);
+ }
+
+ UserCounter(UserCounter &&other) : data_(other.data_)
+ {
+ other.data_ = nullptr;
+ }
+
+ ~UserCounter()
+ {
+ this->user_remove(data_);
+ }
+
+ UserCounter &operator=(const UserCounter &other)
+ {
+ if (this == &other) {
+ return *this;
+ }
+
+ this->user_remove(data_);
+ data_ = other.data_;
+ this->user_add(data_);
+ return *this;
+ }
+
+ UserCounter &operator=(UserCounter &&other)
+ {
+ if (this == &other) {
+ return *this;
+ }
+
+ this->user_remove(data_);
+ data_ = other.data_;
+ other.data_ = nullptr;
+ return *this;
+ }
+
+ T *operator->()
+ {
+ BLI_assert(data_ != nullptr);
+ return data_;
+ }
+
+ T &operator*()
+ {
+ BLI_assert(data_ != nullptr);
+ return *data_;
+ }
+
+ operator bool() const
+ {
+ return data_ != nullptr;
+ }
+
+ T *get()
+ {
+ return data_;
+ }
+
+ T *release()
+ {
+ T *data = data_;
+ data_ = nullptr;
+ return data;
+ }
+
+ void reset()
+ {
+ this->user_remove(data_);
+ data_ = nullptr;
+ }
+
+ bool has_value() const
+ {
+ return data_ != nullptr;
+ }
+
+ uint64_t hash() const
+ {
+ return DefaultHash<T *>{}(data_);
+ }
+
+ friend bool operator==(const UserCounter &a, const UserCounter &b)
+ {
+ return a.data_ == b.data_;
+ }
+
+ friend std::ostream &operator<<(std::ostream &stream, const UserCounter &value)
+ {
+ stream << value.data_;
+ return stream;
+ }
+
+ private:
+ static void user_add(T *data)
+ {
+ if (data != nullptr) {
+ data->user_add();
+ }
+ }
+
+ static void user_remove(T *data)
+ {
+ if (data != nullptr) {
+ data->user_remove();
+ }
+ }
+};
+
+/* An automatically reference counted geometry. */
+using GeometryPtr = UserCounter<class Geometry>;
+
+void make_geometry_mutable(GeometryPtr &geometry);
+
+} // namespace blender::bke
diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt
index 0fbc8c4c229..070e54653fd 100644
--- a/source/blender/blenkernel/CMakeLists.txt
+++ b/source/blender/blenkernel/CMakeLists.txt
@@ -124,6 +124,7 @@ set(SRC
intern/fmodifier.c
intern/font.c
intern/freestyle.c
+ intern/geometry.cc
intern/gpencil.c
intern/gpencil_curve.c
intern/gpencil_geom.c
@@ -310,6 +311,7 @@ set(SRC
BKE_fluid.h
BKE_font.h
BKE_freestyle.h
+ BKE_geometry.hh
BKE_global.h
BKE_gpencil.h
BKE_gpencil_curve.h
diff --git a/source/blender/blenkernel/intern/geometry.cc b/source/blender/blenkernel/intern/geometry.cc
new file mode 100644
index 00000000000..014197ed423
--- /dev/null
+++ b/source/blender/blenkernel/intern/geometry.cc
@@ -0,0 +1,152 @@
+/*
+ * 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.
+ */
+
+#include "BKE_geometry.hh"
+#include "BKE_lib_id.h"
+#include "BKE_mesh.h"
+
+#include "MEM_guardedalloc.h"
+
+namespace blender::bke {
+
+/* Make a copy of the geometry. */
+Geometry::Geometry(const Geometry &other)
+{
+ if (other.mesh_ != nullptr) {
+ mesh_ = BKE_mesh_copy_for_eval(other.mesh_, false);
+ /* Own the new mesh, regardless of whether the original mesh was owned. */
+ mesh_owned_ = true;
+ }
+}
+
+Geometry::~Geometry()
+{
+ this->mesh_reset();
+}
+
+void Geometry::user_add()
+{
+ users_.fetch_add(1);
+}
+
+void Geometry::user_remove()
+{
+ const int new_users = users_.fetch_sub(1) - 1;
+ if (new_users == 0) {
+ delete this;
+ }
+}
+
+bool Geometry::is_mutable() const
+{
+ /* If the geometry is shared, it is read-only. */
+ /* The user count can be 0, when this is called from the destructor. */
+ return users_ <= 1;
+}
+
+/**
+ * Replace the mesh in the geometry. The caller remains the owner of the given mesh and is
+ * responsible for freeing it eventually.
+ */
+void Geometry::mesh_set_and_keep_ownership(Mesh *mesh)
+{
+ BLI_assert(this->is_mutable());
+ this->mesh_reset();
+ mesh_ = mesh;
+ mesh_owned_ = false;
+}
+
+/**
+ * Replace the mesh in the geometry. The geometry becomes responsible for freeing the mesh.
+ */
+void Geometry::mesh_set_and_transfer_ownership(Mesh *mesh)
+{
+ BLI_assert(this->is_mutable());
+ this->mesh_reset();
+ mesh_ = mesh;
+ mesh_owned_ = true;
+}
+
+/**
+ * Clear any mesh data the geometry might have.
+ */
+void Geometry::mesh_reset()
+{
+ BLI_assert(this->is_mutable());
+ if (mesh_ != nullptr) {
+ if (mesh_owned_) {
+ BKE_id_free(nullptr, mesh_);
+ }
+ mesh_ = nullptr;
+ }
+}
+
+/**
+ * Get the mesh from the geometry. This mesh should only be read and not modified. This can be used
+ * on shared geometries.
+ * Might return null.
+ */
+Mesh *Geometry::mesh_get_for_read()
+{
+ return mesh_;
+}
+
+/**
+ * Get the mesh from the geometry. The caller is allowed to modify the mesh. This method can only
+ * be used on mutable geometries.
+ * Might return null.
+ */
+Mesh *Geometry::mesh_get_for_write()
+{
+ BLI_assert(this->is_mutable());
+ return mesh_;
+}
+
+/**
+ * Return the mesh in the geometry and remove it. This only works on mutable geometries.
+ * Might return null;
+ */
+Mesh *Geometry::mesh_release()
+{
+ BLI_assert(this->is_mutable());
+ Mesh *mesh = mesh_;
+ mesh_ = nullptr;
+ return mesh;
+}
+
+/**
+ * Changes the given pointer so that it points to a mutable geometry. This might do nothing, create
+ * a new empty geometry or copy the entire geometry.
+ */
+void make_geometry_mutable(GeometryPtr &geometry)
+{
+ if (!geometry) {
+ /* If the pointer is null, create a new instance. */
+ geometry = GeometryPtr{new Geometry()};
+ }
+ else if (geometry->is_mutable()) {
+ /* If the instance is mutable already, do nothing. */
+ }
+ else {
+ /* This geometry is shared, make a copy that is independent of the other users. */
+ Geometry *shared_geometry = geometry.release();
+ Geometry *new_geometry = new Geometry(*shared_geometry);
+ shared_geometry->user_remove();
+ geometry = GeometryPtr{new_geometry};
+ }
+}
+
+} // namespace blender::bke