ExhibitSlots and exhibit placement (initial)
[laserbrain_demo] / src / exman.cc
index 21e0074..02f4bc5 100644 (file)
@@ -7,6 +7,101 @@
 
 static Exhibit *create_exhibit(const char *type);
 
 
 static Exhibit *create_exhibit(const char *type);
 
+
+ExhibitSlot::ExhibitSlot(Exhibit *ex)
+{
+       this->ex = 0;
+
+       init(ex);
+}
+
+ExhibitSlot::~ExhibitSlot()
+{
+       detach_exhibit();
+
+       SceneNode *par = node.get_parent();
+       if(par) {
+               par->remove_child(&node);
+
+               while(node.get_num_children()) {
+                       par->add_child(node.get_child(0));
+               }
+       }
+}
+
+
+void ExhibitSlot::init(Exhibit *ex)
+{
+       std::string node_name = "ExhibitSlot";
+       if(ex) {
+               if(ex->get_name()) {
+                       node_name += std::string(":") + std::string(ex->get_name());
+               }
+
+               if(ex->node) {
+                       if(ex->node->get_parent()) {
+                               ex->node->get_parent()->add_child(&node);
+                       }
+                       node.set_position(ex->node->get_node_position());
+                       node.set_rotation(ex->node->get_node_rotation());
+                       ex->node->set_position(Vec3(0, 0, 0));
+                       ex->node->set_rotation(Quat::identity);
+               }
+               attach_exhibit(ex);
+       } else {
+               this->ex = 0;
+       }
+
+       node.set_name(node_name.c_str());
+}
+
+bool ExhibitSlot::empty() const
+{
+       return ex == 0;
+}
+
+Exhibit *ExhibitSlot::get_exhibit() const
+{
+       return ex;
+}
+
+/* In the process of attaching the exhibit, we also steal the exhibit's node
+ * from its previous parent, and reparent it to the slot's node. As the slot's
+ * node itself should have been made a child of the original parent of the
+ * exhibit during init(), the initial state is we're interjecting a null node in
+ * the scene graph between the exhibit and its original parent.
+ *
+ * Attaching to a slot, implicitly detaches from the previous slot.
+ */
+bool ExhibitSlot::attach_exhibit(Exhibit *ex, ExSlotAttachMode mode)
+{
+       if(!ex || this->ex) return false;
+
+       if(ex->prev_slot && ex->prev_slot->ex == ex) {
+               ex->prev_slot->detach_exhibit();
+       }
+
+       if(mode != EXSLOT_ATTACH_TRANSIENT) {
+               ex->prev_slot = this;
+       }
+
+       node.add_child(ex->node);
+       this->ex = ex;
+       return true;
+}
+
+bool ExhibitSlot::detach_exhibit()
+{
+       if(!ex) return false;
+
+       node.remove_child(ex->node);
+       ex = 0;
+       return true;
+}
+
+
+// ---- exhibit manager implementation ----
+
 ExhibitManager::ExhibitManager()
 {
 }
 ExhibitManager::ExhibitManager()
 {
 }
@@ -23,6 +118,12 @@ void ExhibitManager::clear()
                delete items[i];
        }
        items.clear();
                delete items[i];
        }
        items.clear();
+
+       num = (int)exslots.size();
+       for(int i=0; i<num; i++) {
+               delete exslots[i];
+       }
+       exslots.clear();
 }
 
 void ExhibitManager::add(Exhibit *ex)
 }
 
 void ExhibitManager::add(Exhibit *ex)
@@ -73,7 +174,19 @@ bool ExhibitManager::load(MetaScene *mscn, const char *fname)
                                error_log("failed to create exhibit of type: %s\n", atype);
                                continue;
                        }
                                error_log("failed to create exhibit of type: %s\n", atype);
                                continue;
                        }
+                       ex->set_node(snode);
+                       items.push_back(ex);
 
 
+                       const char *alabel = ts_get_attr_str(node, "label");
+                       if(alabel) {
+                               ex->set_name(alabel);
+                       }
+
+                       // create a new slot and attach the exhibit to it
+                       ExhibitSlot *slot = new ExhibitSlot(ex);
+                       exslots.push_back(slot);
+
+                       // grab any extra exhibit data
                        const char *desc = ts_get_attr_str(node, "description");
                        const char *voice = ts_get_attr_str(node, "voiceover");
                        if(desc || voice) {
                        const char *desc = ts_get_attr_str(node, "description");
                        const char *voice = ts_get_attr_str(node, "voiceover");
                        if(desc || voice) {
@@ -92,9 +205,6 @@ bool ExhibitManager::load(MetaScene *mscn, const char *fname)
                                }
                                ex->data.push_back(exd);
                        }
                                }
                                ex->data.push_back(exd);
                        }
-
-                       ex->set_node(snode);
-                       items.push_back(ex);
                }
        }
 
                }
        }
 
@@ -129,6 +239,42 @@ ExSelection ExhibitManager::select(const Sphere &sph) const
        return sel;     // TODO
 }
 
        return sel;     // TODO
 }
 
+// TODO optimize
+ExhibitSlot *ExhibitManager::nearest_empty_slot(const Vec3 &pos, float max_dist) const
+{
+       ExhibitSlot *nearest = 0;
+       float nearest_sqdist = max_dist * max_dist;
+
+       int nslots = exslots.size();
+       for(int i=0; i<nslots; i++) {
+               ExhibitSlot *slot = exslots[i];
+               if(!slot->empty()) continue;
+
+               Vec3 slotpos = slot->node.get_position();
+               float dsq = length_sq(pos - slotpos);
+               if(dsq < nearest_sqdist) {
+                       nearest = slot;
+                       nearest_sqdist = dsq;
+               }
+       }
+
+       return nearest;
+}
+
+void ExhibitManager::stash_exhibit(Exhibit *ex)
+{
+       // make sure it's not already stashed
+       if(std::find(stashed.begin(), stashed.end(), ex) != stashed.end()) {
+               return;
+       }
+       stashed.push_back(ex);
+
+       ex->prev_slot = 0;
+       if(ex->node->get_parent()) {
+               ex->node->get_parent()->remove_child(ex->node);
+       }
+}
+
 void ExhibitManager::update(float dt)
 {
        int num = items.size();
 void ExhibitManager::update(float dt)
 {
        int num = items.size();