From: John Tsiombikas Date: Wed, 17 Jan 2018 06:37:34 +0000 (+0200) Subject: ExhibitSlots and exhibit placement (initial) X-Git-Url: http://git.mutantstargoat.com/user/nuclear/?p=laserbrain_demo;a=commitdiff_plain;h=91c5d07b779f24afec373047afe401b8811811c7 ExhibitSlots and exhibit placement (initial) --- diff --git a/src/app.cc b/src/app.cc index 59d713c..8865012 100644 --- a/src/app.cc +++ b/src/app.cc @@ -1,4 +1,5 @@ #include +#include #include #include #include "app.h" @@ -72,7 +73,11 @@ static ExhibitManager *exman; static BlobExhibit *blobs; static bool show_blobs; -ExSelection exsel_grab, exsel_hover; +ExSelection exsel_active, exsel_hover; +ExSelection exsel_grab_left, exsel_grab_right; +#define exsel_grab_mouse exsel_grab_right +static ExhibitSlot exslot_left, exslot_right; +#define exslot_mouse exslot_right static Renderer *rend; @@ -221,6 +226,9 @@ void app_cleanup() delete rend; + /* this must be destroyed before the scene graph to detach exhibit nodes + * before the scene tries to delete them recursively + */ delete exman; texman.clear(); @@ -333,7 +341,7 @@ static void update(float dt) floor_y = avatar.pos.y - user_eye_height; } - // TODO move to avatar + // TODO move to the avatar system // calculate mouselook view matrix mouse_view_matrix = Mat4::identity; mouse_view_matrix.pre_translate(0, 0, -cam_dist); @@ -343,12 +351,17 @@ static void update(float dt) mouse_view_matrix.pre_rotate_y(deg_to_rad(avatar.body_rot)); mouse_view_matrix.pre_translate(-avatar.pos.x, -avatar.pos.y, -avatar.pos.z); - // check if an exhibit is hovered-over by mouse or 6dof - // XXX note: using previous view/proj matrix lattency shouldn't be an issue but - // make sure state-creep doesn't get us - // XXX also this mouse-picking probably should only be active in non-VR mode - Ray ray = calc_pick_ray(prev_mx, prev_my); - exsel_hover = exman->select(ray); + // check if an exhibit is hovered-over by mouse or 6dof (only if we don't have one grabbed) + if(!exsel_grab_mouse) { + // XXX note: using previous view/proj matrix lattency shouldn't be an issue but + // make sure state-creep doesn't get us + // XXX also this mouse-picking probably should only be active in non-VR mode + Ray ray = calc_pick_ray(prev_mx, prev_my); + exsel_hover = exman->select(ray); + } + + if(!exslot_left.empty()) exslot_left.node.update(dt); + if(!exslot_right.empty()) exslot_right.node.update(dt); // update hand-tracking if(have_handtracking) { @@ -601,6 +614,8 @@ void app_keyboard(int key, bool pressed) void app_mouse_button(int bn, bool pressed, int x, int y) { + static int press_x, press_y; + if(debug_gui) { debug_gui_mbutton(bn, pressed, x, y); return; // ignore mouse events while GUI is visible @@ -611,13 +626,64 @@ void app_mouse_button(int bn, bool pressed, int x, int y) bnstate[bn] = pressed; if(bn == 0) { - if(!pressed) { - if(exsel_grab.ex) { - exsel_grab.ex = 0; - } else { - Ray ray = calc_pick_ray(x, y); - exsel_grab = exman->select(ray); + ExSelection sel; + Ray ray = calc_pick_ray(x, y); + sel = exman->select(ray); + + if(pressed) { + if(sel && (app_get_modifiers() & MOD_CTRL)) { + exsel_grab_mouse = sel; + Vec3 pos = sel.ex->node->get_position(); + debug_log("grabbing... (%g %g %g)\n", pos.x, pos.y, pos.z); + exslot_mouse.node.set_position(pos); + exslot_mouse.node.set_rotation(sel.ex->node->get_rotation()); + exslot_mouse.attach_exhibit(sel.ex, EXSLOT_ATTACH_TRANSIENT); + if(exsel_active) { + exsel_active = ExSelection::null; // cancel active on grab + } + } + press_x = x; + press_y = y; + + } else { + if(exsel_grab_mouse) { + // cancel grab on mouse release + debug_log("releasing...\n"); + Exhibit *ex = exsel_grab_mouse.ex; + Vec3 pos = exslot_mouse.node.get_position(); + debug_log("release location: %g %g %g\n", pos.x, pos.y, pos.z); + + ExhibitSlot *slot = exman->nearest_empty_slot(pos); + if(!slot) { + debug_log("no nearby slot\n"); + if(ex->prev_slot && ex->prev_slot->empty()) { + slot = ex->prev_slot; + debug_log("previous slot available though\n"); + } + } + + if(slot) { + slot->attach_exhibit(ex); + } else { + // nowhere to put it, so stash it for later + exslot_mouse.detach_exhibit(); + exman->stash_exhibit(ex); + debug_log("no slots available, stashing\n"); + } + + exsel_grab_mouse = ExSelection::null; } + + if(abs(press_x - x) < 5 && abs(press_y - y) < 5) { + exsel_active = sel; // select or deselect active exhibit + if(sel) { + debug_log("selecting...\n"); + } else { + debug_log("deselecting...\n"); + } + } + + press_x = press_y = INT_MIN; } } } @@ -652,11 +718,11 @@ void app_mouse_motion(int x, int y) if(!dx && !dy) return; - if(exsel_grab.ex) { - Vec3 pos = exsel_grab.ex->node->get_node_position(); + if(exsel_grab_mouse) { + Vec3 pos = exslot_mouse.node.get_node_position(); Vec3 dir = transpose(view_matrix.upper3x3()) * Vec3(dx * 1.0, dy * -1.0, 0); - exsel_grab.ex->node->set_position(pos + dir); + exslot_mouse.node.set_position(pos + dir); } if(bnstate[2]) { diff --git a/src/app.h b/src/app.h index 1eed2fe..686c7e0 100644 --- a/src/app.h +++ b/src/app.h @@ -16,7 +16,9 @@ extern SceneSet sceneman; extern unsigned int sdr_ltmap, sdr_ltmap_notex; -extern ExSelection exsel_grab, exsel_hover; +extern ExSelection exsel_active; // active (info/interact) exhibit +extern ExSelection exsel_grab_left, exsel_grab_right; // grabbed on each hand +extern ExSelection exsel_hover; // hover extern int fpexcept_enabled; // int so that C modules may fwd-delcare and use it diff --git a/src/avatar.h b/src/avatar.h index ca3ea8c..6c3aa41 100644 --- a/src/avatar.h +++ b/src/avatar.h @@ -1,6 +1,8 @@ #ifndef AVATAR_H_ #define AVATAR_H_ +// TODO incomplete + #include /* when head-tracking is available, head_tilt is ignored, and the diff --git a/src/exhibit.cc b/src/exhibit.cc index 266a583..8403550 100644 --- a/src/exhibit.cc +++ b/src/exhibit.cc @@ -4,14 +4,7 @@ #include "geomdraw.h" #include "app.h" -class ExhibitPriv { -public: - Vec3 orig_pos; - Quat orig_rot; - SceneNode *orig_node; - - ExhibitPriv(); -}; +ExSelection ExSelection::null; // selection @@ -38,28 +31,21 @@ ExData::~ExData() delete voice; } -// private data for each exhibit type -ExhibitPriv::ExhibitPriv() -{ - orig_node = 0; -} - // exhibit class Exhibit::Exhibit() { - priv = new ExhibitPriv; + orig_parent = 0; + prev_slot = 0; } Exhibit::~Exhibit() { - delete priv; } void Exhibit::set_node(SceneNode *node) { - this->node = priv->orig_node = node; - priv->orig_pos = node->get_position(); - priv->orig_rot = node->get_rotation(); + this->node = node; + orig_parent = node->get_parent(); } ExSelection Exhibit::select(const Ray &ray) const diff --git a/src/exhibit.h b/src/exhibit.h index f331d99..fd1d77b 100644 --- a/src/exhibit.h +++ b/src/exhibit.h @@ -8,7 +8,7 @@ #include "audio/stream.h" class Exhibit; -class ExhibitPriv; +class ExhibitSlot; class Scene; enum { @@ -18,6 +18,8 @@ enum { class ExSelection { public: + static ExSelection null; // null selection + Exhibit *ex; void *obj; void *data; @@ -56,9 +58,10 @@ public: */ class Exhibit : public Object { private: - ExhibitPriv *priv; + SceneNode *orig_parent; public: + ExhibitSlot *prev_slot; std::vector data; Exhibit(); diff --git a/src/exman.cc b/src/exman.cc index 21e0074..02f4bc5 100644 --- a/src/exman.cc +++ b/src/exman.cc @@ -7,6 +7,101 @@ 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() { } @@ -23,6 +118,12 @@ void ExhibitManager::clear() delete items[i]; } items.clear(); + + num = (int)exslots.size(); + for(int i=0; iset_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) { @@ -92,9 +205,6 @@ bool ExhibitManager::load(MetaScene *mscn, const char *fname) } ex->data.push_back(exd); } - - ex->set_node(snode); - items.push_back(ex); } } @@ -129,6 +239,42 @@ 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; iempty()) 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(); diff --git a/src/exman.h b/src/exman.h index 4a72993..bbd72fd 100644 --- a/src/exman.h +++ b/src/exman.h @@ -5,9 +5,38 @@ #include "exhibit.h" #include "metascene.h" +//! argument to ExhibitSlot::attach +enum ExSlotAttachMode { + EXSLOT_ATTACH_PERMANENT, + EXSLOT_ATTACH_TRANSIENT +}; + +//! slot which can hold a single exhibit +class ExhibitSlot { +private: + Exhibit *ex; + +public: + SceneNode node; + + ExhibitSlot(Exhibit *ex = 0); + ~ExhibitSlot(); + + void init(Exhibit *ex); + + bool empty() const; + Exhibit *get_exhibit() const; + + bool attach_exhibit(Exhibit *ex, ExSlotAttachMode mode = EXSLOT_ATTACH_PERMANENT); + bool detach_exhibit(); +}; + + class ExhibitManager { private: - std::vector items; + std::vector items, stashed; + std::vector exslots; + // TODO kdtree of slots for quick nearest queries public: ExhibitManager(); @@ -23,6 +52,10 @@ public: ExSelection select(const Ray &ray) const; ExSelection select(const Sphere &sph) const; + ExhibitSlot *nearest_empty_slot(const Vec3 &pos, float max_dist = 10) const; + + void stash_exhibit(Exhibit *ex); + void update(float dt = 0.0f); void draw() const; }; diff --git a/src/ui_exhibit.cc b/src/ui_exhibit.cc new file mode 100644 index 0000000..409bd94 --- /dev/null +++ b/src/ui_exhibit.cc @@ -0,0 +1,21 @@ +#include "ui_exhibit.h" +#include "app.h" + +bool exui_init() +{ + return true; +} + +void exui_shutdown() +{ +} + +void exui_update(float dt) +{ +} + +void exui_draw() +{ + if(!exsel_active) return; + +} diff --git a/src/ui_exhibit.h b/src/ui_exhibit.h new file mode 100644 index 0000000..6fdb6ca --- /dev/null +++ b/src/ui_exhibit.h @@ -0,0 +1,10 @@ +#ifndef UI_EXHIBIT_H_ +#define UI_EXHIBIT_H_ + +bool exui_init(); +void exui_shutdown(); + +void exui_update(float dt); +void exui_draw(); + +#endif // UI_EXHIBIT_H_