Browse Source

add getters/setters

remove pass by reference of heightfield image
hecris 6 years ago
parent
commit
b97eeff20b

+ 33 - 2
panda/src/collide/collisionHeightfield.I

@@ -12,11 +12,42 @@ flush_level() {
   _test_pcollector.flush_level();
   _test_pcollector.flush_level();
 }
 }
 
 
-INLINE PNMImage &CollisionHeightfield::
-heightfield() {
+INLINE PNMImage CollisionHeightfield::
+get_heightfield() {
   return _heightfield;
   return _heightfield;
 }
 }
 
 
+INLINE void CollisionHeightfield::
+set_heightfield(PNMImage heightfield) {
+  int r = _heightfield.get_read_x_size();
+  int c = _heightfield.get_read_y_size();
+  _heightfield = heightfield;
+
+  if (_heightfield.get_read_x_size() == r &&
+      _heightfield.get_read_y_size() == c) {
+    fill_quadtree_heights();
+  } else {
+    fill_quadtree_areas();
+    fill_quadtree_heights();
+  }
+}
+
+INLINE PN_stdfloat CollisionHeightfield::
+get_max_height() {
+  return _max_height;
+}
+
+INLINE void CollisionHeightfield::
+set_max_height(PN_stdfloat max_height) {
+  _max_height = max_height;
+  fill_quadtree_heights();
+}
+
+INLINE int CollisionHeightfield::
+get_subdivisions() {
+  return _subdivisions;
+}
+
 INLINE PN_stdfloat CollisionHeightfield::
 INLINE PN_stdfloat CollisionHeightfield::
 get_height(int x, int y) const {
 get_height(int x, int y) const {
   return _heightfield.get_gray(x, y) * _max_height;
   return _heightfield.get_gray(x, y) * _max_height;

+ 138 - 83
panda/src/collide/collisionHeightfield.cxx

@@ -34,11 +34,147 @@ using std::sort;
  *
  *
  */
  */
 CollisionHeightfield::
 CollisionHeightfield::
-CollisionHeightfield(PNMImage &heightfield,
+CollisionHeightfield(PNMImage heightfield,
                      PN_stdfloat max_height, int subdivisions) {
                      PN_stdfloat max_height, int subdivisions) {
   _heightfield = heightfield;
   _heightfield = heightfield;
   _max_height = max_height;
   _max_height = max_height;
-  setup_quadtree(subdivisions);
+  _nodes_count = 0;
+
+  set_subdivisions(subdivisions);
+}
+
+/**
+ * Sets the number of quadtree subdivisions and modifies
+ * the quadtree accordingly. This should be called when a
+ * user wants to modify the number of quadtree subdivisions
+ * or from a constructor to initialize the quadtree.
+ */
+void CollisionHeightfield::
+set_subdivisions(int subdivisions) {
+  // The number of subdivisions should not be negative and
+  // shouldn't exceed 16 as it would cause integer overflow.
+  nassertv(subdivisions >= 0 && subdivisions < 17);
+  _subdivisions = subdivisions;
+
+  // Determine the number of quadtree nodes needed
+  // for the corresponding number of subdivisions.
+  int nodes_count = 0;
+  for (int i = 0; i <= _subdivisions; i++) {
+    nodes_count += pow(4, i);
+  }
+
+  if (nodes_count == _nodes_count) {
+    // No changes to quadtree to be done
+    return;
+  }
+
+  int leaf_first_index = 0;
+  for (int i = 1; i <= _subdivisions - 1; i++) {
+    leaf_first_index += pow(4, i);
+  }
+
+  // Calculate the area of a leaf node in the quadtree
+  int heightfield_area = _heightfield.get_read_x_size() *
+                         _heightfield.get_read_y_size();
+  int num_leafs = nodes_count - leaf_first_index;
+  // If the area is too small (less than 1), then we
+  // retry by decrementing the number of subdivisions.
+  if (heightfield_area / num_leafs == 0) {
+    set_subdivisions(subdivisions - 1);
+    return;
+  }
+
+  if (nodes_count < _nodes_count) {
+    // If the user is decreasing the number of
+    // subdivisions, then we need only to update the
+    // index where the quadtree leaves start.
+    _leaf_first_index = leaf_first_index;
+  } else {
+    // Otherwise we need to build a new quadtree
+    if (_nodes_count != 0) {
+      // There is an existing quadtree, delete it
+      // before building a new one
+      delete[] _nodes;
+      _nodes = nullptr;
+    }
+    QuadTreeNode *nodes = new QuadTreeNode[nodes_count];
+    _nodes = nodes;
+    _nodes_count = nodes_count;
+    _leaf_first_index = leaf_first_index;
+    fill_quadtree_areas();
+    fill_quadtree_heights();
+  }
+}
+
+/**
+ *
+ */
+void CollisionHeightfield::
+fill_quadtree_areas() {
+  _nodes[0].area.min = {0, 0};
+  _nodes[0].area.max = {(float)_heightfield.get_read_x_size(),
+                        (float)_heightfield.get_read_y_size()};
+  _nodes[0].index = 0;
+  QuadTreeNode parent;
+  for (int i = 1; i < _nodes_count; i += 4) {
+    parent = _nodes[(i-1) / 4];
+    LVector2 sub_area = (parent.area.max - parent.area.min) / 2;
+    // SE Quadrant
+    _nodes[i].area.min = parent.area.min;
+    _nodes[i].area.max = parent.area.min + sub_area;
+    _nodes[i].index = i;
+    // SW Quadrant
+    _nodes[i + 1].area.min = {parent.area.min[0] + sub_area[0],
+                              parent.area.min[1]};
+    _nodes[i + 1].area.max = _nodes[i + 1].area.min + sub_area;
+    _nodes[i + 1].index = i + 1;
+    // NE Quadrant
+    _nodes[i + 2].area.min = {parent.area.min[0],
+                              parent.area.min[1] + sub_area[1]};
+    _nodes[i + 2].area.max = _nodes[i + 2].area.min + sub_area;
+    _nodes[i + 2].index = i + 2;
+    // NW Quadrant
+    _nodes[i + 3].area.min = parent.area.min + sub_area;
+    _nodes[i + 3].area.max = parent.area.max;
+    _nodes[i + 3].index = i + 3;
+  }
+}
+
+/**
+ * Processes the heightfield image, setting the height values
+ * of each quadtree node.
+ *
+ * @relates set_max_height
+ * @relates set_heightfield
+ */
+void CollisionHeightfield::
+fill_quadtree_heights() {
+  QuadTreeNode node;
+  QuadTreeNode child;
+  for (int i = _nodes_count - 1; i >= 0; i--) {
+    node = _nodes[i];
+    PN_stdfloat height_min = INT_MAX;
+    PN_stdfloat height_max = INT_MIN;
+    if (i >= _leaf_first_index) {
+      for (int x = node.area.min[0]; x < node.area.max[0]; x++) {
+        for (int y = node.area.min[1]; y < node.area.max[1]; y++) {
+          PN_stdfloat value = _heightfield.get_gray(x,
+            _heightfield.get_read_y_size() - 1 - y) * _max_height;
+
+          height_min = min(value, height_min);
+          height_max = max(value, height_max);
+        }
+      }
+    } else {
+      for (int c = (i * 4) + 1, cmax = c + 4; c < cmax; c++) {
+        child = _nodes[c];
+        height_min = min(child.height_min, height_min);
+        height_max = max(child.height_max, height_max);
+      }
+    }
+    _nodes[i].height_min = height_min;
+    _nodes[i].height_max = height_max;
+  }
 }
 }
 
 
 /**
 /**
@@ -576,87 +712,6 @@ get_triangles(int x, int y) const {
   return triangles;
   return triangles;
 }
 }
 
 
-/**
- * Processes the given heightfield image and creates a
- * quad tree where each node represents a smaller
- * sub-rectangle of the heightfield. Each quad tree node
- * has a height_min and height_max value, thus representing
- * a box in 3D space.
- */
-void CollisionHeightfield::
-setup_quadtree(int subdivisions) {
-  int nodes_count = 0;
-  for (int i = 0; i <= subdivisions; i++) {
-    nodes_count += pow(4, i);
-  }
-  QuadTreeNode *nodes = new QuadTreeNode[nodes_count];
-  nodes[0].area.min = {0, 0};
-  nodes[0].area.max = {(float)_heightfield.get_read_x_size(),
-                       (float)_heightfield.get_read_y_size()};
-  nodes[0].index = 0;
-  QuadTreeNode parent;
-  for (int i = 1; i < nodes_count; i += 4) {
-    parent = nodes[(i-1) / 4];
-    LVector2 sub_area = (parent.area.max - parent.area.min) / 2;
-    // SE Quadrant
-    nodes[i].area.min = parent.area.min;
-    nodes[i].area.max = parent.area.min + sub_area;
-    nodes[i].index = i;
-    // SW Quadrant
-    nodes[i + 1].area.min = {parent.area.min[0] + sub_area[0],
-                             parent.area.min[1]};
-    nodes[i + 1].area.max = nodes[i + 1].area.min + sub_area;
-    nodes[i + 1].index = i + 1;
-    // NE Quadrant
-    nodes[i + 2].area.min = {parent.area.min[0],
-                             parent.area.min[1] + sub_area[1]};
-    nodes[i + 2].area.max = nodes[i + 2].area.min + sub_area;
-    nodes[i + 2].index = i + 2;
-    // NW Quadrant
-    nodes[i + 3].area.min = parent.area.min + sub_area;
-    nodes[i + 3].area.max = parent.area.max;
-    nodes[i + 3].index = i + 3;
-  }
-
-  // We now process the heightfield image, setting the
-  // height_min and height_max values of each quad tree node
-  // starting from the leaves.
-  int leaf_first_index = 0;
-  for (int i = 1; i <= subdivisions - 1; i++) {
-    leaf_first_index += pow(4, i);
-  }
-
-  QuadTreeNode node;
-  QuadTreeNode child;
-  for (int i = nodes_count - 1; i >= 0; i--) {
-    node = nodes[i];
-    PN_stdfloat height_min = INT_MAX;
-    PN_stdfloat height_max = INT_MIN;
-    if (i >= leaf_first_index) {
-      for (int x = node.area.min[0]; x < node.area.max[0]; x++) {
-        for (int y = node.area.min[1]; y < node.area.max[1]; y++) {
-          PN_stdfloat value = _heightfield.get_gray(x,
-            _heightfield.get_read_y_size() - 1 - y) * _max_height;
-
-          height_min = min(value, height_min);
-          height_max = max(value, height_max);
-        }
-      }
-    } else {
-      for (int c = (i * 4) + 1, cmax = c + 4; c < cmax; c++) {
-        child = nodes[c];
-        height_min = min(child.height_min, height_min);
-        height_max = max(child.height_max, height_max);
-      }
-    }
-    nodes[i].height_min = height_min;
-    nodes[i].height_max = height_max;
-  }
-  _nodes = nodes;
-  _nodes_count = nodes_count;
-  _leaf_first_index = leaf_first_index;
-}
-
 /**
 /**
  * Generic CollisionSolid member functions
  * Generic CollisionSolid member functions
  */
  */

+ 15 - 4
panda/src/collide/collisionHeightfield.h

@@ -14,11 +14,21 @@
  * */
  * */
 class EXPCL_PANDA_COLLIDE CollisionHeightfield : public CollisionSolid {
 class EXPCL_PANDA_COLLIDE CollisionHeightfield : public CollisionSolid {
 PUBLISHED:
 PUBLISHED:
-  CollisionHeightfield(PNMImage &heightfield,
+  CollisionHeightfield(PNMImage heightfield,
                        PN_stdfloat max_height, int subdivisions);
                        PN_stdfloat max_height, int subdivisions);
   ~CollisionHeightfield() { delete[] _nodes; }
   ~CollisionHeightfield() { delete[] _nodes; }
   virtual LPoint3 get_collision_origin() const;
   virtual LPoint3 get_collision_origin() const;
-  INLINE PNMImage &heightfield();
+
+  INLINE PNMImage get_heightfield();
+  INLINE void set_heightfield(PNMImage heightfield);
+
+  INLINE PN_stdfloat get_max_height();
+  INLINE void set_max_height(PN_stdfloat max_height);
+
+  INLINE int get_subdivisions();
+  void set_subdivisions(int subdivisions);
+
+  INLINE PN_stdfloat get_height(int x, int y) const;
 
 
 protected:
 protected:
   struct Rect {
   struct Rect {
@@ -65,12 +75,13 @@ protected:
 private:
 private:
   PNMImage _heightfield;
   PNMImage _heightfield;
   PN_stdfloat _max_height;
   PN_stdfloat _max_height;
+  int _subdivisions;
   // Todo: PT(QuadTreeNode) _nodes;
   // Todo: PT(QuadTreeNode) _nodes;
   QuadTreeNode *_nodes;
   QuadTreeNode *_nodes;
   int _nodes_count;
   int _nodes_count;
   int _leaf_first_index;
   int _leaf_first_index;
-  void setup_quadtree(int subdivisions);
-  INLINE PN_stdfloat get_height(int x, int y) const;
+  void fill_quadtree_areas();
+  void fill_quadtree_heights();
   std::vector<Triangle> get_triangles(int x, int y) const;
   std::vector<Triangle> get_triangles(int x, int y) const;
 
 
   // A pointer to a function that tests for intersection between a box and a
   // A pointer to a function that tests for intersection between a box and a