Browse Source

pgraphnodes: Support hardware instancing for LODNode

LODNode will now correctly calculate LOD for individual instances.

It turns out implementing it for FadeLODNode is hard (wrt. tracking state for individual instances) so I have not bothered with supporting it - FadeLODNode instead calculates a single switch level for all instances based on the centroid of the visible instances.
rdb 5 years ago
parent
commit
615502aef6
1 changed files with 74 additions and 19 deletions
  1. 74 19
      panda/src/pgraphnodes/lodNode.cxx

+ 74 - 19
panda/src/pgraphnodes/lodNode.cxx

@@ -143,26 +143,64 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
   CDReader cdata(_cycler);
 
   CPT(TransformState) rel_transform = get_rel_transform(trav, data);
-  LPoint3 center = cdata->_center * rel_transform->get_mat();
-  PN_stdfloat dist2 = center.dot(center);
+  PN_stdfloat lod_scale = cdata->_lod_scale *
+    trav->get_scene()->get_camera_node()->get_lod_scale();
 
   int num_children = std::min(get_num_children(), (int)cdata->_switch_vector.size());
-  for (int index = 0; index < num_children; ++index) {
-    const Switch &sw = cdata->_switch_vector[index];
-    bool in_range;
-    if (cdata->_got_force_switch) {
-      in_range = (cdata->_force_switch == index);
-    } else {
-      in_range = sw.in_range_2(dist2 * cdata->_lod_scale
-                   * trav->get_scene()->get_camera_node()->get_lod_scale());
+
+  if (data._instances == nullptr || cdata->_got_force_switch) {
+    LPoint3 center = cdata->_center * rel_transform->get_mat();
+    PN_stdfloat dist2 = center.dot(center);
+
+    for (int index = 0; index < num_children; ++index) {
+      const Switch &sw = cdata->_switch_vector[index];
+      bool in_range;
+      if (cdata->_got_force_switch) {
+        in_range = (cdata->_force_switch == index);
+      } else {
+        in_range = sw.in_range_2(dist2 * lod_scale);
+      }
+
+      if (in_range) {
+        // This switch level is in range.  Draw its children.
+        PandaNode *child = get_child(index);
+        if (child != nullptr) {
+          CullTraverserData next_data(data, child);
+          trav->traverse(next_data);
+        }
+      }
     }
+  }
+  else {
+    // Figure out which instances in which switch levels should be visible.
+    size_t num_instances = data._instances->size();
+    std::unique_ptr<BitArray[]> in_range(new BitArray[num_children]);
 
-    if (in_range) {
-      // This switch level is in range.  Draw its children.
-      PandaNode *child = get_child(index);
-      if (child != nullptr) {
-        CullTraverserData next_data(data, child);
-        trav->traverse(next_data);
+    for (size_t ii = 0; ii < num_instances; ++ii) {
+      LPoint3 inst_center = cdata->_center *
+        rel_transform->compose((*data._instances)[ii].get_transform())->get_mat();
+      PN_stdfloat dist2 = inst_center.dot(inst_center);
+
+      for (int index = 0; index < num_children; ++index) {
+        const Switch &sw = cdata->_switch_vector[index];
+        if (!sw.in_range_2(dist2 * lod_scale)) {
+          in_range[index].set_bit(ii);
+        }
+      }
+    }
+
+    for (int index = 0; index < num_children; ++index) {
+      const Switch &sw = cdata->_switch_vector[index];
+
+      CPT(InstanceList) instances = data._instances->without(in_range[index]);
+      if (!instances->empty()) {
+        // At least one instance is visible in this switch level.
+        PandaNode *child = get_child(index);
+        if (child != nullptr) {
+          CullTraverserData next_data(data, child);
+          next_data._instances = instances;
+          trav->traverse(next_data);
+        }
       }
     }
   }
@@ -321,13 +359,30 @@ compute_child(CullTraverser *trav, CullTraverserData &data) {
     return cdata->_force_switch;
   }
 
+  PN_stdfloat lod_scale = cdata->_lod_scale *
+    trav->get_scene()->get_camera_node()->get_lod_scale();
+
   CPT(TransformState) rel_transform = get_rel_transform(trav, data);
-  LPoint3 center = cdata->_center * rel_transform->get_mat();
+  LPoint3 center;
+
+  if (data._instances == nullptr) {
+    center = cdata->_center * rel_transform->get_mat();
+  }
+  else {
+    // Can't really do much with instancing in FadeLODNode; let's instead
+    // just calculate the centroid of the visible instances.
+    center = LPoint3(0);
+    for (const InstanceList::Instance &instance : *data._instances) {
+      center += cdata->_center *
+        rel_transform->compose(instance.get_transform())->get_mat();
+    }
+    center *= 1.0 / data._instances->size();
+  }
+
   PN_stdfloat dist2 = center.dot(center);
 
   for (int index = 0; index < (int)cdata->_switch_vector.size(); ++index) {
-    if (cdata->_switch_vector[index].in_range_2(dist2 * cdata->_lod_scale
-         * trav->get_scene()->get_camera_node()->get_lod_scale())) {
+    if (cdata->_switch_vector[index].in_range_2(dist2 * lod_scale)) {
       if (pgraph_cat.is_debug()) {
         pgraph_cat.debug()
           << data.get_node_path() << " at distance " << sqrt(dist2)