ExhibitSlots and exhibit placement (initial)
[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 }
108
109 ExhibitManager::~ExhibitManager()
110 {
111         clear();
112 }
113
114 void ExhibitManager::clear()
115 {
116         int num = (int)items.size();
117         for(int i=0; i<num; i++) {
118                 delete items[i];
119         }
120         items.clear();
121
122         num = (int)exslots.size();
123         for(int i=0; i<num; i++) {
124                 delete exslots[i];
125         }
126         exslots.clear();
127 }
128
129 void ExhibitManager::add(Exhibit *ex)
130 {
131         std::vector<Exhibit*>::iterator it = std::find(items.begin(), items.end(), ex);
132         if(it == items.end()) {
133                 items.push_back(ex);
134         }
135 }
136
137 bool ExhibitManager::remove(Exhibit *ex)
138 {
139         std::vector<Exhibit*>::iterator it = std::find(items.begin(), items.end(), ex);
140         if(it != items.end()) {
141                 items.erase(it);
142                 return true;
143         }
144         return false;
145 }
146
147 bool ExhibitManager::load(MetaScene *mscn, const char *fname)
148 {
149         struct ts_node *root = ts_load(fname);
150         if(!root || strcmp(root->name, "exhibits") != 0) {
151                 ts_free_tree(root);
152                 error_log("failed to load exhibits\n");
153                 return false;
154         }
155
156         struct ts_node *iter = root->child_list;
157         while(iter) {
158                 struct ts_node *node = iter;
159                 iter = iter->next;
160
161                 if(strcmp(node->name, "item") == 0) {
162                         SceneNode *snode;
163
164                         const char *amatch = ts_get_attr_str(node, "match_node");
165                         if(!amatch || !(snode = mscn->match_node(amatch))) {
166                                 error_log("ExhibitManager::load: regexp \"%s\" didn't match any nodes\n",
167                                                 amatch ? amatch : "");
168                                 continue;
169                         }
170
171                         Exhibit *ex;
172                         const char *atype = ts_get_attr_str(node, "type");
173                         if(!atype || !(ex = create_exhibit(atype))) {
174                                 error_log("failed to create exhibit of type: %s\n", atype);
175                                 continue;
176                         }
177                         ex->set_node(snode);
178                         items.push_back(ex);
179
180                         const char *alabel = ts_get_attr_str(node, "label");
181                         if(alabel) {
182                                 ex->set_name(alabel);
183                         }
184
185                         // create a new slot and attach the exhibit to it
186                         ExhibitSlot *slot = new ExhibitSlot(ex);
187                         exslots.push_back(slot);
188
189                         // grab any extra exhibit data
190                         const char *desc = ts_get_attr_str(node, "description");
191                         const char *voice = ts_get_attr_str(node, "voiceover");
192                         if(desc || voice) {
193                                 ExData exd;
194
195                                 if(desc) {
196                                         exd.text = std::string(desc);
197                                 }
198                                 if(voice) {
199                                         exd.voice = new OggVorbisStream;
200                                         if(!exd.voice->open(voice)) {
201                                                 error_log("failed to open voiceover: %s\n", voice);
202                                                 delete exd.voice;
203                                                 exd.voice = 0;
204                                         }
205                                 }
206                                 ex->data.push_back(exd);
207                         }
208                 }
209         }
210
211         ts_free_tree(root);
212         return true;
213 }
214
215 ExSelection ExhibitManager::select(const Ray &ray) const
216 {
217         ExSelection nearest;
218         nearest.dist = FLT_MAX;
219
220         int nitems = items.size();
221         for(int i=0; i<nitems; i++) {
222                 ExSelection sel = items[i]->select(ray);
223                 if(sel && sel.dist < nearest.dist) {
224                         nearest = sel;
225                 }
226         }
227
228         return nearest;
229 }
230
231 ExSelection ExhibitManager::select(const Sphere &sph) const
232 {
233         ExSelection sel;
234         if(!items.empty()) {
235                 sel.ex = items[0];
236                 sel.selsphere = sph;
237                 sel.validmask = EXSEL_SPHERE;
238         }
239         return sel;     // TODO
240 }
241
242 // TODO optimize
243 ExhibitSlot *ExhibitManager::nearest_empty_slot(const Vec3 &pos, float max_dist) const
244 {
245         ExhibitSlot *nearest = 0;
246         float nearest_sqdist = max_dist * max_dist;
247
248         int nslots = exslots.size();
249         for(int i=0; i<nslots; i++) {
250                 ExhibitSlot *slot = exslots[i];
251                 if(!slot->empty()) continue;
252
253                 Vec3 slotpos = slot->node.get_position();
254                 float dsq = length_sq(pos - slotpos);
255                 if(dsq < nearest_sqdist) {
256                         nearest = slot;
257                         nearest_sqdist = dsq;
258                 }
259         }
260
261         return nearest;
262 }
263
264 void ExhibitManager::stash_exhibit(Exhibit *ex)
265 {
266         // make sure it's not already stashed
267         if(std::find(stashed.begin(), stashed.end(), ex) != stashed.end()) {
268                 return;
269         }
270         stashed.push_back(ex);
271
272         ex->prev_slot = 0;
273         if(ex->node->get_parent()) {
274                 ex->node->get_parent()->remove_child(ex->node);
275         }
276 }
277
278 void ExhibitManager::update(float dt)
279 {
280         int num = items.size();
281         for(int i=0; i<num; i++) {
282                 // if the exhibit is not part of a scene graph, first call its
283                 // node's update function (otherwise it'll have been called recursively earlier)
284                 if(!items[i]->node->get_parent()) {
285                         items[i]->node->update(dt);
286                 }
287                 items[i]->update(dt);
288         }
289 }
290
291 void ExhibitManager::draw() const
292 {
293         int num = items.size();
294         for(int i=0; i<num; i++) {
295                 items[i]->pre_draw();
296                 items[i]->draw();
297                 items[i]->post_draw();
298         }
299 }
300
301 static Exhibit *create_exhibit(const char *type)
302 {
303         if(strcmp(type, "static") == 0) {
304                 debug_log("creating static exhibit\n");
305                 return new Exhibit;
306         } else if(strcmp(type, "blobs") == 0) {
307                 debug_log("creating blobs exhibit\n");
308                 return new BlobExhibit;
309         }
310         error_log("unknown exhibit type: %s\n", type);
311         return 0;
312 }