[Bf-blender-cvs] [87218899be2] geometry-nodes: Geometry Nodes: add an initial geometry class

Jacques Lucke noreply at git.blender.org
Thu Oct 22 15:06:53 CEST 2020


Commit: 87218899be2fae7622fe778380bcff10fe0fb463
Author: Jacques Lucke
Date:   Thu Oct 22 15:02:27 2020 +0200
Branches: geometry-nodes
https://developer.blender.org/rB87218899be2fae7622fe778380bcff10fe0fb463

Geometry Nodes: add an initial geometry class

===================================================================

A	source/blender/blenkernel/BKE_geometry.hh
M	source/blender/blenkernel/CMakeLists.txt
A	source/blender/blenkernel/intern/geometry.cc

===================================================================

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



More information about the Bf-blender-cvs mailing list