exhibit drawing is now handled by the Renderer
[laserbrain_demo] / src / exman.cc
index e0a12bb..31ee1ae 100644 (file)
+#include <float.h>
+#include <algorithm>
 #include "exman.h"
 #include "exhibit.h"
 #include "blob_exhibit.h"
 #include "treestore.h"
+#include "app.h"
+#include "geomdraw.h"
 
 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()
 {
+       own_scn = 0;
 }
 
 ExhibitManager::~ExhibitManager()
 {
-       int num = (int)items.size();
+       clear();
+}
+
+void ExhibitManager::clear()
+{
+       // not deleting exhibit objects, as they will be deleted the own_scn destructor
+       items.clear();
+
+       int num = (int)exslots.size();
        for(int i=0; i<num; i++) {
-               delete items[i];
+               delete exslots[i];
        }
-       items.clear();
+       exslots.clear();
+
+       delete own_scn; // this must be the last thing to destroy
 }
 
-bool ExhibitManager::load(const MetaScene *mscn, const char *fname)
+void ExhibitManager::add(Exhibit *ex)
+{
+       std::vector<Exhibit*>::iterator it = std::find(items.begin(), items.end(), ex);
+       if(it == items.end()) {
+               items.push_back(ex);
+               own_scn->add_object(ex);
+               if(ex->node) own_scn->add_node(ex->node);
+       }
+}
+
+bool ExhibitManager::remove(Exhibit *ex)
+{
+       std::vector<Exhibit*>::iterator it = std::find(items.begin(), items.end(), ex);
+       if(it != items.end()) {
+               items.erase(it);
+               own_scn->remove_object(ex);
+               if(ex->node) own_scn->remove_node(ex->node);
+               return true;
+       }
+       return false;
+}
+
+bool ExhibitManager::load(MetaScene *mscn, const char *fname)
 {
        struct ts_node *root = ts_load(fname);
        if(!root || strcmp(root->name, "exhibits") != 0) {
@@ -27,17 +159,212 @@ bool ExhibitManager::load(const MetaScene *mscn, const char *fname)
                return false;
        }
 
+       /* create our own scene to manage all exhibits not already in an existing scene
+        * and add it to the metascene.
+        * Also exhibit drawing happens due to the renderer drawing the metascene
+        */
+       if(!own_scn) {
+               own_scn = new Scene;
+               own_scn->name = "ad-hoc exhibits";
+               mscn->scenes.push_back(own_scn);
+       }
+
+       struct ts_node *iter = root->child_list;
+       while(iter) {
+               struct ts_node *node = iter;
+               iter = iter->next;
+
+               SceneNode *snode = 0;
+
+               if(strcmp(node->name, "item") == 0) {
+                       Exhibit *ex;
+                       const char *atype = ts_get_attr_str(node, "type");
+                       if(!atype || !(ex = create_exhibit(atype))) {
+                               error_log("failed to create exhibit of type: %s\n", atype);
+                               continue;
+                       }
+                       const char *alabel = ts_get_attr_str(node, "label");
+                       if(alabel) {
+                               ex->set_name(alabel);
+                       }
+
+                       const char *amatch = ts_get_attr_str(node, "match_node");
+                       if(amatch) {
+                               if(!(snode = mscn->match_node(amatch))) {
+                                       error_log("ExhibitManager::load: regexp \"%s\" didn't match any nodes\n",
+                                                       amatch ? amatch : "");
+                                       continue;
+                               }
+                       }
+
+                       // add everything to our data structures
+                       // equivalent to add_exhibit(ex), but without all the searching
+                       own_scn->add_object(ex);
+                       if(!snode) {
+                               snode = new SceneNode;
+                               snode->set_name(ex->get_name());
+                               own_scn->add_node(snode);
+                       }
+                       ex->set_node(snode);
+                       items.push_back(ex);
+
+                       float *apos = ts_get_attr_vec(node, "pos");
+                       if(apos) {
+                               snode->set_position(Vec3(apos[0], apos[1], apos[2]));
+                       }
+                       float *arot_axis = ts_get_attr_vec(node, "rotaxis");
+                       if(arot_axis) {
+                               float arot_angle = ts_get_attr_num(node, "rotangle", 0.0f);
+                               Vec3 axis = Vec3(arot_axis[0], arot_axis[1], arot_axis[2]);
+                               Quat q;
+                               q.set_rotation(axis, deg_to_rad(arot_angle));
+                               snode->set_rotation(q);
+                       }
+                       struct ts_attr *ascale = ts_get_attr(node, "scale");
+                       if(ascale) {
+                               switch(ascale->val.type) {
+                               case TS_NUMBER:
+                                       snode->set_scaling(Vec3(ascale->val.fnum, ascale->val.fnum, ascale->val.fnum));
+                                       break;
+                               case TS_VECTOR:
+                                       snode->set_scaling(Vec3(ascale->val.vec[0], ascale->val.vec[1], ascale->val.vec[2]));
+                               default:
+                                       break;
+                               }
+                       }
+
+                       // 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) {
+                               ExData exd;
+
+                               if(desc) {
+                                       exd.text = std::string(desc);
+                               }
+                               if(voice) {
+                                       exd.voice = new OggVorbisStream;
+                                       if(!exd.voice->open(voice)) {
+                                               error_log("failed to open voiceover: %s\n", voice);
+                                               delete exd.voice;
+                                               exd.voice = 0;
+                                       }
+                               }
+                               ex->data.push_back(exd);
+                       }
+               }
+       }
+
        ts_free_tree(root);
        return true;
 }
 
+ExSelection ExhibitManager::select(const Ray &ray) const
+{
+       ExSelection nearest;
+       nearest.dist = FLT_MAX;
+
+       int nitems = items.size();
+       for(int i=0; i<nitems; i++) {
+               ExSelection sel = items[i]->select(ray);
+               if(sel && sel.dist < nearest.dist) {
+                       nearest = sel;
+               }
+       }
+
+       return nearest;
+}
+
+ExSelection ExhibitManager::select(const Sphere &sph) const
+{
+       ExSelection sel;
+       if(!items.empty()) {
+               sel.ex = items[0];
+               sel.selsphere = sph;
+               sel.validmask = EXSEL_SPHERE;
+       }
+       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();
+       for(int i=0; i<num; i++) {
+               // if the exhibit is not part of a scene graph, first call its
+               // node's update function (otherwise it would have been called recursively earlier)
+               if(items[i]->node && !items[i]->node->get_parent()) {
+                       items[i]->node->update(dt);
+               }
+               items[i]->update(dt);
+       }
+}
+
+void ExhibitManager::draw() const
+{
+       int num = items.size();
+       for(int i=0; i<num; i++) {
+               if(exsel_hover.ex == items[i]) {
+                       const AABox &bvol = items[i]->get_aabox();
+                       draw_geom_object(&bvol);
+               }
+       }
+}
 
 static Exhibit *create_exhibit(const char *type)
 {
        if(strcmp(type, "static") == 0) {
+               debug_log("creating static exhibit\n");
                return new Exhibit;
        } else if(strcmp(type, "blobs") == 0) {
-               return new BlobExhibit;
+               debug_log("creating blobs exhibit\n");
+               BlobExhibit *b = new BlobExhibit;
+               if(!b->init()) {
+                       delete b;
+                       error_log("failed to initialize blobs exhibit\n");
+                       return 0;
+               }
+               return b;
        }
+       error_log("unknown exhibit type: %s\n", type);
        return 0;
 }