#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)
ExhibitManager::ExhibitManager()
{
+ own_scn = 0;
}
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)
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);
}
}
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)
{
+ 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);
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);
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;
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
}
}
+Exhibit *ExhibitManager::unstash_exhibit()
+{
+ if(stashed.empty()) {
+ return 0;
+ }
+
+ Exhibit *ex = stashed[0];
+ stashed.erase(stashed.begin());
+ return ex;
+}
+
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);
{
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);
+ }
}
}
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;
+}