+
+ // crosshair
+ OutlineModel::Buffer buf;
+ buf.vertices = std::vector<glm::vec3>({
+ { -10.0f, 0.0f, 0.0f }, { 10.0f, 0.0f, 0.0f },
+ { 0.0f, -10.0f, 0.0f }, { 0.0f, 10.0f, 0.0f },
+ });
+ buf.indices = std::vector<OutlineModel::Index>({
+ 0, 1, 2, 3
+ });
+ buf.colors.resize(4, { 10.0f, 10.0f, 10.0f });
+ crosshair.Update(buf);
+}
+
+namespace {
+
+OutlineModel::Buffer outl_buf;
+
+}
+
+void HUD::FocusBlock(const Chunk &chunk, int index) {
+ const Block &block = chunk.BlockAt(index);
+ const BlockType &type = chunk.Type(index);
+ outl_buf.Clear();
+ type.FillOutlineModel(outl_buf);
+ outline.Update(outl_buf);
+ outline_transform = chunk.Transform(player.GetEntity().ChunkCoords());
+ outline_transform *= chunk.ToTransform(Chunk::ToPos(index), index);
+ outline_transform *= glm::scale(glm::vec3(1.005f));
+ outline_visible = true;
+ {
+ std::stringstream s;
+ s << "Block: "
+ << type.label
+ << ", face: " << block.GetFace()
+ << ", turn: " << block.GetTurn();
+ block_text.Set(env.assets.small_ui_font, s.str());
+ }
+ show_block = true;
+ show_entity = false;
+}
+
+void HUD::FocusEntity(const Entity &entity) {
+ {
+ std::stringstream s;
+ s << "Entity: " << entity.Name();
+ entity_text.Set(env.assets.small_ui_font, s.str());
+ }
+ show_block = false;
+ show_entity = true;
+}
+
+void HUD::FocusNone() {
+ outline_visible = false;
+ show_block = false;
+ show_entity = false;
+}
+
+void HUD::DisplayNone() {
+ block_visible = false;
+}
+
+void HUD::Display(const BlockType &type) {
+ block_buf.Clear();
+ type.FillEntityModel(block_buf);
+ block.Update(block_buf);
+
+ block_label.Set(env.assets.small_ui_font, type.label);
+
+ block_visible = type.visible;
+}
+
+
+void HUD::UpdateDebug() {
+ UpdateCounter();
+ UpdatePosition();
+ UpdateOrientation();
+}
+
+void HUD::UpdateCounter() {
+ std::stringstream s;
+ s << std::setprecision(3) <<
+ "avg: " << env.counter.Average().running << "ms, "
+ "peak: " << env.counter.Peak().running << "ms";
+ std::string text = s.str();
+ counter_text.Set(env.assets.small_ui_font, text);
+}
+
+void HUD::UpdatePosition() {
+ std::stringstream s;
+ s << std::setprecision(3) << "pos: " << player.GetEntity().AbsolutePosition();
+ position_text.Set(env.assets.small_ui_font, s.str());
+}
+
+void HUD::UpdateOrientation() {
+ //std::stringstream s;
+ //s << std::setprecision(3) << "pitch: " << rad2deg(ctrl.Pitch())
+ // << ", yaw: " << rad2deg(ctrl.Yaw());
+ //orientation_text.Set(env.assets.small_ui_font, s.str());
+}
+
+void HUD::PostMessage(const char *msg) {
+ messages.PushLine(msg);
+ msg_timer.Reset();
+ msg_timer.Start();
+ std::cout << msg << std::endl;
+}
+
+
+void HUD::Update(int dt) {
+ msg_timer.Update(dt);
+ if (msg_timer.HitOnce()) {
+ msg_timer.Stop();
+ }
+
+ if (config.video.debug) {
+ if (env.counter.Changed()) {
+ UpdateCounter();
+ }
+ UpdatePosition();
+ UpdateOrientation();
+ }
+}
+
+void HUD::Render(Viewport &viewport) noexcept {
+ // block focus
+ if (outline_visible && config.video.world) {
+ PlainColor &outline_prog = viewport.WorldOutlineProgram();
+ outline_prog.SetM(outline_transform);
+ outline.Draw();
+ }
+
+ // clear depth buffer so everything renders above the world
+ viewport.ClearDepth();
+
+ if (config.video.hud) {
+ // "inventory"
+ if (block_visible) {
+ DirectionalLighting &world_prog = viewport.HUDProgram();
+ world_prog.SetLightDirection({ 1.0f, 3.0f, 5.0f });
+ // disable distance fog
+ world_prog.SetFogDensity(0.0f);
+
+ viewport.DisableBlending();
+ world_prog.SetM(block_transform);
+ block.Draw();
+ block_label.Render(viewport);
+ }
+
+ // message box
+ if (msg_timer.Running()) {
+ messages.Render(viewport);
+ }
+
+ // crosshair
+ PlainColor &outline_prog = viewport.HUDOutlineProgram();
+ viewport.EnableInvertBlending();
+ viewport.SetCursor(glm::vec3(0.0f), Gravity::CENTER);
+ outline_prog.SetM(viewport.Cursor());
+ crosshair.Draw();
+ }
+
+ // debug overlay
+ if (config.video.debug) {
+ counter_text.Render(viewport);
+ position_text.Render(viewport);
+ orientation_text.Render(viewport);
+ if (show_block) {
+ block_text.Render(viewport);
+ } else if (show_entity) {
+ entity_text.Render(viewport);
+ }
+ }
+}
+
+
+InteractiveManipulator::InteractiveManipulator(Environment &env, Entity &player)
+: player(player)
+, audio(env.audio)
+, place_sound(env.loader.LoadSound("thump"))
+, remove_sound(env.loader.LoadSound("plop")) {
+
+}
+
+void InteractiveManipulator::SetBlock(Chunk &chunk, int index, const Block &block) {
+ chunk.SetBlock(index, block);
+ glm::vec3 coords = chunk.ToSceneCoords(player.ChunkCoords(), Chunk::ToCoords(index));
+ // TODO: get sound effect from block type
+ if (block.type == 0) {
+ audio.Play(remove_sound, coords);
+ } else {
+ audio.Play(place_sound, coords);
+ }
+}
+
+
+Interface::Interface(
+ Config &config,
+ const Keymap &keymap,
+ PlayerController &pc,
+ ClientController &cc)
+: config(config)
+, keymap(keymap)
+, player_ctrl(pc)
+, client_ctrl(cc)
+, fwd(0)
+, rev(0)
+, slot(0)
+, num_slots(10) {
+