+#include <float.h>
#include <algorithm>
#include "exman.h"
#include "exhibit.h"
static Exhibit *create_exhibit(const char *type);
+
+ExhibitSlot::ExhibitSlot(Exhibit *ex)
+{
+ this->ex = 0;
+
+ init(ex);
+}
+
+ExhibitSlot::~ExhibitSlot()
+{
+ detach_exhibit();
+
+ SceneNode *par = node.get_parent();
+ if(par) {
+ par->remove_child(&node);
+
+ while(node.get_num_children()) {
+ par->add_child(node.get_child(0));
+ }
+ }
+}
+
+
+void ExhibitSlot::init(Exhibit *ex)
+{
+ std::string node_name = "ExhibitSlot";
+ if(ex) {
+ if(ex->get_name()) {
+ node_name += std::string(":") + std::string(ex->get_name());
+ }
+
+ if(ex->node) {
+ if(ex->node->get_parent()) {
+ ex->node->get_parent()->add_child(&node);
+ }
+ node.set_position(ex->node->get_node_position());
+ node.set_rotation(ex->node->get_node_rotation());
+ ex->node->set_position(Vec3(0, 0, 0));
+ ex->node->set_rotation(Quat::identity);
+ }
+ attach_exhibit(ex);
+ } else {
+ this->ex = 0;
+ }
+
+ node.set_name(node_name.c_str());
+}
+
+bool ExhibitSlot::empty() const
+{
+ return ex == 0;
+}
+
+Exhibit *ExhibitSlot::get_exhibit() const
+{
+ return ex;
+}
+
+/* In the process of attaching the exhibit, we also steal the exhibit's node
+ * from its previous parent, and reparent it to the slot's node. As the slot's
+ * node itself should have been made a child of the original parent of the
+ * exhibit during init(), the initial state is we're interjecting a null node in
+ * the scene graph between the exhibit and its original parent.
+ *
+ * Attaching to a slot, implicitly detaches from the previous slot.
+ */
+bool ExhibitSlot::attach_exhibit(Exhibit *ex, ExSlotAttachMode mode)
+{
+ if(!ex || this->ex) return false;
+
+ if(ex->prev_slot && ex->prev_slot->ex == ex) {
+ ex->prev_slot->detach_exhibit();
+ }
+
+ if(mode != EXSLOT_ATTACH_TRANSIENT) {
+ ex->prev_slot = this;
+ }
+
+ node.add_child(ex->node);
+ this->ex = ex;
+ return true;
+}
+
+bool ExhibitSlot::detach_exhibit()
+{
+ if(!ex) return false;
+
+ node.remove_child(ex->node);
+ ex = 0;
+ return true;
+}
+
+
+// ---- exhibit manager implementation ----
+
ExhibitManager::ExhibitManager()
{
}
delete items[i];
}
items.clear();
+
+ num = (int)exslots.size();
+ for(int i=0; i<num; i++) {
+ delete exslots[i];
+ }
+ exslots.clear();
}
void ExhibitManager::add(Exhibit *ex)
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);
+ }
+ // create a new slot and attach the exhibit to it
+ ExhibitSlot *slot = new ExhibitSlot(ex);
+ exslots.push_back(slot);
+
+ // grab any extra exhibit data
const char *desc = ts_get_attr_str(node, "description");
const char *voice = ts_get_attr_str(node, "voiceover");
if(desc || voice) {
}
ex->data.push_back(exd);
}
-
- ex->set_node(snode);
- items.push_back(ex);
}
}
ExSelection ExhibitManager::select(const Ray &ray) const
{
- ExSelection sel;
- if(!items.empty()) {
- sel.ex = items[0];
- sel.selray = ray;
- sel.validmask = EXSEL_RAY;
+ ExSelection nearest;
+ nearest.dist = FLT_MAX;
+
+ int nitems = items.size();
+ for(int i=0; i<nitems; i++) {
+ ExSelection sel = items[i]->select(ray);
+ if(sel && sel.dist < nearest.dist) {
+ nearest = sel;
+ }
}
- return sel; // TODO
+
+ return nearest;
}
ExSelection ExhibitManager::select(const Sphere &sph) const
return sel; // TODO
}
+// TODO optimize
+ExhibitSlot *ExhibitManager::nearest_empty_slot(const Vec3 &pos, float max_dist) const
+{
+ ExhibitSlot *nearest = 0;
+ float nearest_sqdist = max_dist * max_dist;
+
+ int nslots = exslots.size();
+ for(int i=0; i<nslots; i++) {
+ ExhibitSlot *slot = exslots[i];
+ if(!slot->empty()) continue;
+
+ Vec3 slotpos = slot->node.get_position();
+ float dsq = length_sq(pos - slotpos);
+ if(dsq < nearest_sqdist) {
+ nearest = slot;
+ nearest_sqdist = dsq;
+ }
+ }
+
+ return nearest;
+}
+
+void ExhibitManager::stash_exhibit(Exhibit *ex)
+{
+ // make sure it's not already stashed
+ if(std::find(stashed.begin(), stashed.end(), ex) != stashed.end()) {
+ return;
+ }
+ stashed.push_back(ex);
+
+ ex->prev_slot = 0;
+ if(ex->node->get_parent()) {
+ ex->node->get_parent()->remove_child(ex->node);
+ }
+}
+
void ExhibitManager::update(float dt)
{
int num = items.size();