exhibit drawing is now handled by the Renderer
[laserbrain_demo] / src / exman.cc
1 #include <float.h>
2 #include <algorithm>
3 #include "exman.h"
4 #include "exhibit.h"
5 #include "blob_exhibit.h"
6 #include "treestore.h"
7 #include "app.h"
8 #include "geomdraw.h"
9
10 static Exhibit *create_exhibit(const char *type);
11
12
13 ExhibitSlot::ExhibitSlot(Exhibit *ex)
14 {
15         this->ex = 0;
16
17         init(ex);
18 }
19
20 ExhibitSlot::~ExhibitSlot()
21 {
22         detach_exhibit();
23
24         SceneNode *par = node.get_parent();
25         if(par) {
26                 par->remove_child(&node);
27
28                 while(node.get_num_children()) {
29                         par->add_child(node.get_child(0));
30                 }
31         }
32 }
33
34
35 void ExhibitSlot::init(Exhibit *ex)
36 {
37         std::string node_name = "ExhibitSlot";
38         if(ex) {
39                 if(ex->get_name()) {
40                         node_name += std::string(":") + std::string(ex->get_name());
41                 }
42
43                 if(ex->node) {
44                         if(ex->node->get_parent()) {
45                                 ex->node->get_parent()->add_child(&node);
46                         }
47                         node.set_position(ex->node->get_node_position());
48                         node.set_rotation(ex->node->get_node_rotation());
49                         ex->node->set_position(Vec3(0, 0, 0));
50                         ex->node->set_rotation(Quat::identity);
51                 }
52                 attach_exhibit(ex);
53         } else {
54                 this->ex = 0;
55         }
56
57         node.set_name(node_name.c_str());
58 }
59
60 bool ExhibitSlot::empty() const
61 {
62         return ex == 0;
63 }
64
65 Exhibit *ExhibitSlot::get_exhibit() const
66 {
67         return ex;
68 }
69
70 /* In the process of attaching the exhibit, we also steal the exhibit's node
71  * from its previous parent, and reparent it to the slot's node. As the slot's
72  * node itself should have been made a child of the original parent of the
73  * exhibit during init(), the initial state is we're interjecting a null node in
74  * the scene graph between the exhibit and its original parent.
75  *
76  * Attaching to a slot, implicitly detaches from the previous slot.
77  */
78 bool ExhibitSlot::attach_exhibit(Exhibit *ex, ExSlotAttachMode mode)
79 {
80         if(!ex || this->ex) return false;
81
82         if(ex->prev_slot && ex->prev_slot->ex == ex) {
83                 ex->prev_slot->detach_exhibit();
84         }
85
86         if(mode != EXSLOT_ATTACH_TRANSIENT) {
87                 ex->prev_slot = this;
88         }
89
90         node.add_child(ex->node);
91         this->ex = ex;
92         return true;
93 }
94
95 bool ExhibitSlot::detach_exhibit()
96 {
97         if(!ex) return false;
98
99         node.remove_child(ex->node);
100         ex = 0;
101         return true;
102 }
103
104
105 // ---- exhibit manager implementation ----
106
107 ExhibitManager::ExhibitManager()
108 {
109         own_scn = 0;
110 }
111
112 ExhibitManager::~ExhibitManager()
113 {
114         clear();
115 }
116
117 void ExhibitManager::clear()
118 {
119         // not deleting exhibit objects, as they will be deleted the own_scn destructor
120         items.clear();
121
122         int num = (int)exslots.size();
123         for(int i=0; i<num; i++) {
124                 delete exslots[i];
125         }
126         exslots.clear();
127
128         delete own_scn; // this must be the last thing to destroy
129 }
130
131 void ExhibitManager::add(Exhibit *ex)
132 {
133         std::vector<Exhibit*>::iterator it = std::find(items.begin(), items.end(), ex);
134         if(it == items.end()) {
135                 items.push_back(ex);
136                 own_scn->add_object(ex);
137                 if(ex->node) own_scn->add_node(ex->node);
138         }
139 }
140
141 bool ExhibitManager::remove(Exhibit *ex)
142 {
143         std::vector<Exhibit*>::iterator it = std::find(items.begin(), items.end(), ex);
144         if(it != items.end()) {
145                 items.erase(it);
146                 own_scn->remove_object(ex);
147                 if(ex->node) own_scn->remove_node(ex->node);
148                 return true;
149         }
150         return false;
151 }
152
153 bool ExhibitManager::load(MetaScene *mscn, const char *fname)
154 {
155         struct ts_node *root = ts_load(fname);
156         if(!root || strcmp(root->name, "exhibits") != 0) {
157                 ts_free_tree(root);
158                 error_log("failed to load exhibits\n");
159                 return false;
160         }
161
162         /* create our own scene to manage all exhibits not already in an existing scene
163          * and add it to the metascene.
164          * Also exhibit drawing happens due to the renderer drawing the metascene
165          */
166         if(!own_scn) {
167                 own_scn = new Scene;
168                 own_scn->name = "ad-hoc exhibits";
169                 mscn->scenes.push_back(own_scn);
170         }
171
172         struct ts_node *iter = root->child_list;
173         while(iter) {
174                 struct ts_node *node = iter;
175                 iter = iter->next;
176
177                 SceneNode *snode = 0;
178
179                 if(strcmp(node->name, "item") == 0) {
180                         Exhibit *ex;
181                         const char *atype = ts_get_attr_str(node, "type");
182                         if(!atype || !(ex = create_exhibit(atype))) {
183                                 error_log("failed to create exhibit of type: %s\n", atype);
184                                 continue;
185                         }
186                         const char *alabel = ts_get_attr_str(node, "label");
187                         if(alabel) {
188                                 ex->set_name(alabel);
189                         }
190
191                         const char *amatch = ts_get_attr_str(node, "match_node");
192                         if(amatch) {
193                                 if(!(snode = mscn->match_node(amatch))) {
194                                         error_log("ExhibitManager::load: regexp \"%s\" didn't match any nodes\n",
195                                                         amatch ? amatch : "");
196                                         continue;
197                                 }
198                         }
199
200                         // add everything to our data structures
201                         // equivalent to add_exhibit(ex), but without all the searching
202                         own_scn->add_object(ex);
203                         if(!snode) {
204                                 snode = new SceneNode;
205                                 snode->set_name(ex->get_name());
206                                 own_scn->add_node(snode);
207                         }
208                         ex->set_node(snode);
209                         items.push_back(ex);
210
211                         float *apos = ts_get_attr_vec(node, "pos");
212                         if(apos) {
213                                 snode->set_position(Vec3(apos[0], apos[1], apos[2]));
214                         }
215                         float *arot_axis = ts_get_attr_vec(node, "rotaxis");
216                         if(arot_axis) {
217                                 float arot_angle = ts_get_attr_num(node, "rotangle", 0.0f);
218                                 Vec3 axis = Vec3(arot_axis[0], arot_axis[1], arot_axis[2]);
219                                 Quat q;
220                                 q.set_rotation(axis, deg_to_rad(arot_angle));
221                                 snode->set_rotation(q);
222                         }
223                         struct ts_attr *ascale = ts_get_attr(node, "scale");
224                         if(ascale) {
225                                 switch(ascale->val.type) {
226                                 case TS_NUMBER:
227                                         snode->set_scaling(Vec3(ascale->val.fnum, ascale->val.fnum, ascale->val.fnum));
228                                         break;
229                                 case TS_VECTOR:
230                                         snode->set_scaling(Vec3(ascale->val.vec[0], ascale->val.vec[1], ascale->val.vec[2]));
231                                 default:
232                                         break;
233                                 }
234                         }
235
236                         // create a new slot and attach the exhibit to it
237                         ExhibitSlot *slot = new ExhibitSlot(ex);
238                         exslots.push_back(slot);
239
240                         // grab any extra exhibit data
241                         const char *desc = ts_get_attr_str(node, "description");
242                         const char *voice = ts_get_attr_str(node, "voiceover");
243                         if(desc || voice) {
244                                 ExData exd;
245
246                                 if(desc) {
247                                         exd.text = std::string(desc);
248                                 }
249                                 if(voice) {
250                                         exd.voice = new OggVorbisStream;
251                                         if(!exd.voice->open(voice)) {
252                                                 error_log("failed to open voiceover: %s\n", voice);
253                                                 delete exd.voice;
254                                                 exd.voice = 0;
255                                         }
256                                 }
257                                 ex->data.push_back(exd);
258                         }
259                 }
260         }
261
262         ts_free_tree(root);
263         return true;
264 }
265
266 ExSelection ExhibitManager::select(const Ray &ray) const
267 {
268         ExSelection nearest;
269         nearest.dist = FLT_MAX;
270
271         int nitems = items.size();
272         for(int i=0; i<nitems; i++) {
273                 ExSelection sel = items[i]->select(ray);
274                 if(sel && sel.dist < nearest.dist) {
275                         nearest = sel;
276                 }
277         }
278
279         return nearest;
280 }
281
282 ExSelection ExhibitManager::select(const Sphere &sph) const
283 {
284         ExSelection sel;
285         if(!items.empty()) {
286                 sel.ex = items[0];
287                 sel.selsphere = sph;
288                 sel.validmask = EXSEL_SPHERE;
289         }
290         return sel;     // TODO
291 }
292
293 // TODO optimize
294 ExhibitSlot *ExhibitManager::nearest_empty_slot(const Vec3 &pos, float max_dist) const
295 {
296         ExhibitSlot *nearest = 0;
297         float nearest_sqdist = max_dist * max_dist;
298
299         int nslots = exslots.size();
300         for(int i=0; i<nslots; i++) {
301                 ExhibitSlot *slot = exslots[i];
302                 if(!slot->empty()) continue;
303
304                 Vec3 slotpos = slot->node.get_position();
305                 float dsq = length_sq(pos - slotpos);
306                 if(dsq < nearest_sqdist) {
307                         nearest = slot;
308                         nearest_sqdist = dsq;
309                 }
310         }
311
312         return nearest;
313 }
314
315 void ExhibitManager::stash_exhibit(Exhibit *ex)
316 {
317         // make sure it's not already stashed
318         if(std::find(stashed.begin(), stashed.end(), ex) != stashed.end()) {
319                 return;
320         }
321         stashed.push_back(ex);
322
323         ex->prev_slot = 0;
324         if(ex->node->get_parent()) {
325                 ex->node->get_parent()->remove_child(ex->node);
326         }
327 }
328
329 void ExhibitManager::update(float dt)
330 {
331         int num = items.size();
332         for(int i=0; i<num; i++) {
333                 // if the exhibit is not part of a scene graph, first call its
334                 // node's update function (otherwise it would have been called recursively earlier)
335                 if(items[i]->node && !items[i]->node->get_parent()) {
336                         items[i]->node->update(dt);
337                 }
338                 items[i]->update(dt);
339         }
340 }
341
342 void ExhibitManager::draw() const
343 {
344         int num = items.size();
345         for(int i=0; i<num; i++) {
346                 if(exsel_hover.ex == items[i]) {
347                         const AABox &bvol = items[i]->get_aabox();
348                         draw_geom_object(&bvol);
349                 }
350         }
351 }
352
353 static Exhibit *create_exhibit(const char *type)
354 {
355         if(strcmp(type, "static") == 0) {
356                 debug_log("creating static exhibit\n");
357                 return new Exhibit;
358         } else if(strcmp(type, "blobs") == 0) {
359                 debug_log("creating blobs exhibit\n");
360                 BlobExhibit *b = new BlobExhibit;
361                 if(!b->init()) {
362                         delete b;
363                         error_log("failed to initialize blobs exhibit\n");
364                         return 0;
365                 }
366                 return b;
367         }
368         error_log("unknown exhibit type: %s\n", type);
369         return 0;
370 }