added delayed init call after scenegraph/meshes are done loading
[laserbrain_demo] / src / exman.cc
index 02f4bc5..00f7e39 100644 (file)
@@ -4,8 +4,11 @@
 #include "exhibit.h"
 #include "blob_exhibit.h"
 #include "treestore.h"
+#include "app.h"
+#include "geomdraw.h"
 
 static Exhibit *create_exhibit(const char *type);
+static void clean_desc_text(char *dest, const char *src);
 
 
 ExhibitSlot::ExhibitSlot(Exhibit *ex)
@@ -104,6 +107,7 @@ bool ExhibitSlot::detach_exhibit()
 
 ExhibitManager::ExhibitManager()
 {
+       own_scn = 0;
 }
 
 ExhibitManager::~ExhibitManager()
@@ -113,17 +117,16 @@ ExhibitManager::~ExhibitManager()
 
 void ExhibitManager::clear()
 {
-       int num = (int)items.size();
-       for(int i=0; i<num; i++) {
-               delete items[i];
-       }
+       // not deleting exhibit objects, as they will be deleted the own_scn destructor
        items.clear();
 
-       num = (int)exslots.size();
+       int num = (int)exslots.size();
        for(int i=0; i<num; i++) {
                delete exslots[i];
        }
        exslots.clear();
+
+       delete own_scn; // this must be the last thing to destroy
 }
 
 void ExhibitManager::add(Exhibit *ex)
@@ -131,6 +134,8 @@ 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);
        }
 }
 
@@ -139,6 +144,8 @@ 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;
@@ -146,6 +153,8 @@ bool ExhibitManager::remove(Exhibit *ex)
 
 bool ExhibitManager::load(MetaScene *mscn, const char *fname)
 {
+       info_log("ExhibitManager::load(%s)\n", fname);
+
        struct ts_node *root = ts_load(fname);
        if(!root || strcmp(root->name, "exhibits") != 0) {
                ts_free_tree(root);
@@ -153,35 +162,80 @@ bool ExhibitManager::load(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;
 
-               if(strcmp(node->name, "item") == 0) {
-                       SceneNode *snode;
-
-                       const char *amatch = ts_get_attr_str(node, "match_node");
-                       if(!amatch || !(snode = mscn->match_node(amatch))) {
-                               error_log("ExhibitManager::load: regexp \"%s\" didn't match any nodes\n",
-                                               amatch ? amatch : "");
-                               continue;
-                       }
+               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;
                        }
-                       ex->set_node(snode);
-                       items.push_back(ex);
-
                        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);
@@ -191,9 +245,13 @@ bool ExhibitManager::load(MetaScene *mscn, const char *fname)
                        const char *voice = ts_get_attr_str(node, "voiceover");
                        if(desc || voice) {
                                ExData exd;
+                               exd.type = EXDATA_INFO;
 
                                if(desc) {
-                                       exd.text = std::string(desc);
+                                       char *fixed_desc = new char[strlen(desc) + 1];
+                                       clean_desc_text(fixed_desc, desc);
+                                       exd.text = std::string(fixed_desc);
+                                       delete [] fixed_desc;
                                }
                                if(voice) {
                                        exd.voice = new OggVorbisStream;
@@ -230,13 +288,14 @@ ExSelection ExhibitManager::select(const Ray &ray) const
 
 ExSelection ExhibitManager::select(const Sphere &sph) const
 {
-       ExSelection sel;
-       if(!items.empty()) {
-               sel.ex = items[0];
-               sel.selsphere = sph;
-               sel.validmask = EXSEL_SPHERE;
+       int nitems = items.size();
+       for(int i=0; i<nitems; i++) {
+               ExSelection sel = items[i]->select(sph);
+               if(sel) {
+                       return sel;
+               }
        }
-       return sel;     // TODO
+       return ExSelection();
 }
 
 // TODO optimize
@@ -280,8 +339,8 @@ 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'll have been called recursively earlier)
-               if(!items[i]->node->get_parent()) {
+               // 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);
@@ -292,9 +351,10 @@ void ExhibitManager::draw() const
 {
        int num = items.size();
        for(int i=0; i<num; i++) {
-               items[i]->pre_draw();
-               items[i]->draw();
-               items[i]->post_draw();
+               if(exsel_hover.ex == items[i]) {
+                       const AABox &bvol = items[i]->get_aabox();
+                       draw_geom_object(&bvol);
+               }
        }
 }
 
@@ -305,8 +365,38 @@ static Exhibit *create_exhibit(const char *type)
                return new Exhibit;
        } else if(strcmp(type, "blobs") == 0) {
                debug_log("creating blobs exhibit\n");
-               return new BlobExhibit;
+               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;
 }
+
+/* clean up description text to be more easily layed out later.
+ * more specifically:
+ *  - remove redundant spaces
+ *  - remove all newlines except as paragraph separators
+ *  - remove all other whitespace chars
+ * destination buffer must be as large as the source buffer
+ */
+static void clean_desc_text(char *dest, const char *src)
+{
+       while(*src) {
+               if(isspace(*src)) {
+                       if(*src == '\n' && *(src + 1) == '\n') {
+                               *dest++ = '\n';
+                       } else {
+                               *dest++ = ' ';
+                       }
+                       while(*src && isspace(*src)) ++src;
+               } else {
+                       *dest++ = *src++;
+               }
+       }
+       *dest = 0;
+}