5 #include "blob_exhibit.h"
10 static Exhibit *create_exhibit(const char *type);
11 static void clean_desc_text(char *dest, const char *src);
14 ExhibitSlot::ExhibitSlot(Exhibit *ex)
21 ExhibitSlot::~ExhibitSlot()
25 SceneNode *par = node.get_parent();
27 par->remove_child(&node);
29 while(node.get_num_children()) {
30 par->add_child(node.get_child(0));
36 void ExhibitSlot::init(Exhibit *ex)
38 std::string node_name = "ExhibitSlot";
41 node_name += std::string(":") + std::string(ex->get_name());
45 if(ex->node->get_parent()) {
46 ex->node->get_parent()->add_child(&node);
48 node.set_position(ex->node->get_node_position());
49 node.set_rotation(ex->node->get_node_rotation());
50 ex->node->set_position(Vec3(0, 0, 0));
51 ex->node->set_rotation(Quat::identity);
58 node.set_name(node_name.c_str());
61 bool ExhibitSlot::empty() const
66 Exhibit *ExhibitSlot::get_exhibit() const
71 /* In the process of attaching the exhibit, we also steal the exhibit's node
72 * from its previous parent, and reparent it to the slot's node. As the slot's
73 * node itself should have been made a child of the original parent of the
74 * exhibit during init(), the initial state is we're interjecting a null node in
75 * the scene graph between the exhibit and its original parent.
77 * Attaching to a slot, implicitly detaches from the previous slot.
79 bool ExhibitSlot::attach_exhibit(Exhibit *ex, ExSlotAttachMode mode)
81 if(!ex || this->ex) return false;
83 if(ex->prev_slot && ex->prev_slot->ex == ex) {
84 ex->prev_slot->detach_exhibit();
87 if(mode != EXSLOT_ATTACH_TRANSIENT) {
91 node.add_child(ex->node);
96 bool ExhibitSlot::detach_exhibit()
100 node.remove_child(ex->node);
106 // ---- exhibit manager implementation ----
108 ExhibitManager::ExhibitManager()
113 ExhibitManager::~ExhibitManager()
118 void ExhibitManager::clear()
120 // not deleting exhibit objects, as they will be deleted the own_scn destructor
123 int num = (int)exslots.size();
124 for(int i=0; i<num; i++) {
129 delete own_scn; // this must be the last thing to destroy
132 void ExhibitManager::add(Exhibit *ex)
134 std::vector<Exhibit*>::iterator it = std::find(items.begin(), items.end(), ex);
135 if(it == items.end()) {
137 own_scn->add_object(ex);
138 if(ex->node) own_scn->add_node(ex->node);
142 bool ExhibitManager::remove(Exhibit *ex)
144 std::vector<Exhibit*>::iterator it = std::find(items.begin(), items.end(), ex);
145 if(it != items.end()) {
147 own_scn->remove_object(ex);
148 if(ex->node) own_scn->remove_node(ex->node);
154 bool ExhibitManager::load(MetaScene *mscn, const char *fname)
156 info_log("ExhibitManager::load(%s)\n", fname);
158 struct ts_node *root = ts_load(fname);
159 if(!root || strcmp(root->name, "exhibits") != 0) {
161 error_log("failed to load exhibits\n");
165 /* create our own scene to manage all exhibits not already in an existing scene
166 * and add it to the metascene.
167 * Also exhibit drawing happens due to the renderer drawing the metascene
171 own_scn->name = "ad-hoc exhibits";
172 mscn->scenes.push_back(own_scn);
175 struct ts_node *iter = root->child_list;
177 struct ts_node *node = iter;
180 SceneNode *snode = 0;
182 if(strcmp(node->name, "item") == 0) {
184 const char *atype = ts_get_attr_str(node, "type");
185 if(!atype || !(ex = create_exhibit(atype))) {
186 error_log("failed to create exhibit of type: %s\n", atype);
189 const char *alabel = ts_get_attr_str(node, "label");
191 ex->set_name(alabel);
194 const char *amatch = ts_get_attr_str(node, "match_node");
196 if(!(snode = mscn->match_node(amatch))) {
197 error_log("ExhibitManager::load: regexp \"%s\" didn't match any nodes\n",
198 amatch ? amatch : "");
203 // add everything to our data structures
204 // equivalent to add_exhibit(ex), but without all the searching
205 own_scn->add_object(ex);
207 snode = new SceneNode;
208 snode->set_name(ex->get_name());
209 own_scn->add_node(snode);
214 float *apos = ts_get_attr_vec(node, "pos");
216 snode->set_position(Vec3(apos[0], apos[1], apos[2]));
218 float *arot_axis = ts_get_attr_vec(node, "rotaxis");
220 float arot_angle = ts_get_attr_num(node, "rotangle", 0.0f);
221 Vec3 axis = Vec3(arot_axis[0], arot_axis[1], arot_axis[2]);
223 q.set_rotation(axis, deg_to_rad(arot_angle));
224 snode->set_rotation(q);
226 struct ts_attr *ascale = ts_get_attr(node, "scale");
228 switch(ascale->val.type) {
230 snode->set_scaling(Vec3(ascale->val.fnum, ascale->val.fnum, ascale->val.fnum));
233 snode->set_scaling(Vec3(ascale->val.vec[0], ascale->val.vec[1], ascale->val.vec[2]));
239 // create a new slot and attach the exhibit to it
240 ExhibitSlot *slot = new ExhibitSlot(ex);
241 exslots.push_back(slot);
243 // grab any extra exhibit data
244 const char *desc = ts_get_attr_str(node, "description");
245 const char *voice = ts_get_attr_str(node, "voiceover");
248 exd.type = EXDATA_INFO;
251 char *fixed_desc = new char[strlen(desc) + 1];
252 clean_desc_text(fixed_desc, desc);
253 exd.text = std::string(fixed_desc);
254 delete [] fixed_desc;
257 exd.voice = new OggVorbisStream;
258 if(!exd.voice->open(voice)) {
259 error_log("failed to open voiceover: %s\n", voice);
264 ex->data.push_back(exd);
273 ExSelection ExhibitManager::select(const Ray &ray) const
276 nearest.dist = FLT_MAX;
278 int nitems = items.size();
279 for(int i=0; i<nitems; i++) {
280 ExSelection sel = items[i]->select(ray);
281 if(sel && sel.dist < nearest.dist) {
289 ExSelection ExhibitManager::select(const Sphere &sph) const
291 int nitems = items.size();
292 for(int i=0; i<nitems; i++) {
293 ExSelection sel = items[i]->select(sph);
298 return ExSelection();
302 ExhibitSlot *ExhibitManager::nearest_empty_slot(const Vec3 &pos, float max_dist) const
304 ExhibitSlot *nearest = 0;
305 float nearest_sqdist = max_dist * max_dist;
307 int nslots = exslots.size();
308 for(int i=0; i<nslots; i++) {
309 ExhibitSlot *slot = exslots[i];
310 if(!slot->empty()) continue;
312 Vec3 slotpos = slot->node.get_position();
313 float dsq = length_sq(pos - slotpos);
314 if(dsq < nearest_sqdist) {
316 nearest_sqdist = dsq;
323 void ExhibitManager::stash_exhibit(Exhibit *ex)
325 // make sure it's not already stashed
326 if(std::find(stashed.begin(), stashed.end(), ex) != stashed.end()) {
329 stashed.push_back(ex);
332 if(ex->node->get_parent()) {
333 ex->node->get_parent()->remove_child(ex->node);
337 Exhibit *ExhibitManager::unstash_exhibit()
339 if(stashed.empty()) {
343 Exhibit *ex = stashed[0];
344 stashed.erase(stashed.begin());
348 void ExhibitManager::update(float dt)
350 int num = items.size();
351 for(int i=0; i<num; i++) {
352 // if the exhibit is not part of a scene graph, first call its
353 // node's update function (otherwise it would have been called recursively earlier)
354 if(items[i]->node && !items[i]->node->get_parent()) {
355 items[i]->node->update(dt);
357 items[i]->update(dt);
361 void ExhibitManager::draw() const
363 int num = items.size();
364 for(int i=0; i<num; i++) {
365 if(exsel_hover.ex == items[i]) {
366 const AABox &bvol = items[i]->get_aabox();
367 draw_geom_object(&bvol);
372 static Exhibit *create_exhibit(const char *type)
374 if(strcmp(type, "static") == 0) {
375 debug_log("creating static exhibit\n");
377 } else if(strcmp(type, "blobs") == 0) {
378 debug_log("creating blobs exhibit\n");
379 BlobExhibit *b = new BlobExhibit;
382 error_log("failed to initialize blobs exhibit\n");
387 error_log("unknown exhibit type: %s\n", type);
391 /* clean up description text to be more easily layed out later.
393 * - remove redundant spaces
394 * - remove all newlines except as paragraph separators
395 * - remove all other whitespace chars
396 * destination buffer must be as large as the source buffer
398 static void clean_desc_text(char *dest, const char *src)
402 if(*src == '\n' && *(src + 1) == '\n') {
407 while(*src && isspace(*src)) ++src;