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