]> git.localhorst.tv Git - blank.git/blobdiff - src/ui/ui.cpp
fix forward axis in block placement orientation
[blank.git] / src / ui / ui.cpp
index 263245cd79bfe61ccdeb57e710bc1f5951c67fd0..2d3b4679552045ad557e1b62fa978046619bc36f 100644 (file)
@@ -13,6 +13,7 @@
 #include "../app/init.hpp"
 #include "../audio/Audio.hpp"
 #include "../audio/SoundBank.hpp"
+#include "../geometry/distance.hpp"
 #include "../graphics/Font.hpp"
 #include "../graphics/Viewport.hpp"
 #include "../io/TokenStreamReader.hpp"
@@ -28,6 +29,7 @@
 #include <map>
 #include <sstream>
 #include <glm/gtc/matrix_transform.hpp>
+#include <glm/gtx/projection.hpp>
 #include <glm/gtx/rotate_vector.hpp>
 #include <glm/gtx/io.hpp>
 
@@ -42,6 +44,7 @@ PlayerController::PlayerController(World &world, Player &player)
 , aim_world()
 , aim_entity() {
        player.GetEntity().SetController(*this);
+       player.GetEntity().GetSteering().SetAcceleration(5.0f);
 }
 
 PlayerController::~PlayerController() {
@@ -59,10 +62,6 @@ void PlayerController::SetMovement(const glm::vec3 &m) noexcept {
        Invalidate();
 }
 
-glm::vec3 PlayerController::ControlForce(const Entity &e, const EntityState &s) const {
-       return TargetVelocity(rotateY(move_dir * e.MaxVelocity(), s.yaw), s, 5.0f);
-}
-
 void PlayerController::TurnHead(float dp, float dy) noexcept {
        player.GetEntity().TurnHead(dp, dy);
 }
@@ -90,10 +89,11 @@ void PlayerController::Invalidate() noexcept {
 void PlayerController::UpdatePlayer() noexcept {
        if (dirty) {
                Ray aim = player.Aim();
-               if (!world.Intersection(aim, glm::mat4(1.0f), player.GetEntity().ChunkCoords(), aim_world)) {
+               Entity &entity = player.GetEntity();
+               if (!world.Intersection(aim, entity.ChunkCoords(), aim_world)) {
                        aim_world = WorldCollision();
                }
-               if (!world.Intersection(aim, glm::mat4(1.0f), player.GetEntity(), aim_entity)) {
+               if (!world.Intersection(aim, entity, aim_entity)) {
                        aim_entity = EntityCollision();
                }
                if (aim_world && aim_entity) {
@@ -104,6 +104,20 @@ void PlayerController::UpdatePlayer() noexcept {
                                aim_world = WorldCollision();
                        }
                }
+               Steering &steering = entity.GetSteering();
+               if (!iszero(move_dir)) {
+                       // scale input by max velocity, apply yaw, and transform to world space
+                       steering.SetTargetVelocity(glm::vec3(
+                               glm::vec4(rotateY(move_dir * entity.MaxVelocity(), entity.Yaw()), 0.0f)
+                               * transpose(entity.Transform())
+                       ));
+                       steering.Enable(Steering::TARGET_VELOCITY);
+                       steering.Disable(Steering::HALT);
+               } else {
+                       // target velocity of 0 is the same as halt
+                       steering.Enable(Steering::HALT);
+                       steering.Disable(Steering::TARGET_VELOCITY);
+               }
                dirty = false;
        }
 }
@@ -169,14 +183,59 @@ void DirectInput::PickBlock() {
 }
 
 void DirectInput::PlaceBlock() {
+       // update block focus
        UpdatePlayer();
+       // do nothing if not looking at any block
        if (!BlockFocus()) return;
 
+       // determine block adjacent to the face the player is looking at
        BlockLookup next_block(BlockFocus().chunk, BlockFocus().BlockPos(), Block::NormalFace(BlockFocus().normal));
+       // abort if it's unavailable
        if (!next_block) {
                return;
        }
-       manip.SetBlock(next_block.GetChunk(), next_block.GetBlockIndex(), Block(InventorySlot() + 1));
+
+       // "can replace" check
+       // this prevents players from replacing solid blocks e.g. by looking through slabs
+       // simple for now, should be expanded to include things like
+       // entities in the way or replacable blocks like water and stuff
+       if (next_block.GetBlock().type != 0) {
+               return;
+       }
+
+       Block new_block(InventorySlot() + 1);
+
+       // block's up vector
+       // align with player's up
+       const glm::vec3 player_up(GetPlayer().GetEntity().Up());
+       new_block.SetFace(Block::NormalFace(player_up));
+       // to align with player's local up/down look (like stairs in minecraft), just invert
+       // it if pitch is positive
+       // or, align with focus normal (like logs in minecraft)
+
+       // determine block's turn (local rotation about up axis)
+       // when aligned with player's up (first mode, and currently the only one implemented)
+       // project the player's view forward onto his entity's XZ plane and
+       // use the closest cardinal direction it's pointing in
+       const glm::vec3 view_forward(-GetPlayer().GetEntity().ViewTransform(GetPlayer().GetEntity().ChunkCoords())[2]);
+       // if view is straight up or down, this will be a null vector (NaN after normalization)
+       // in that case maybe the model forward should be used?
+       // the current implementation implicitly falls back to TURN_NONE which is -Z
+       const glm::vec3 local_forward(normalize(view_forward - proj(view_forward, player_up)));
+       // FIXME: I suspect this only works when player_up is positive Y
+       if (local_forward.x > 0.707f) {
+               new_block.SetTurn(Block::TURN_RIGHT);
+       } else if (local_forward.z > 0.707f) {
+               new_block.SetTurn(Block::TURN_AROUND);
+       } else if (local_forward.x < -0.707f) {
+               new_block.SetTurn(Block::TURN_LEFT);
+       }
+       // for mode two ("minecraft stairs") it should work the same, but I haven't properly
+       // thought that through (well, that's also true about the whole face/turn thing, but oh well)
+       // mode three I have absoloutely no clue. that placement would be appropriate for pipe-like
+       // blocks, where turn shouldn't make a difference, but what if it does?
+
+       manip.SetBlock(next_block.GetChunk(), next_block.GetBlockIndex(), new_block);
        Invalidate();
 }
 
@@ -282,7 +341,7 @@ HUD::HUD(Environment &env, Config &config, const Player &player)
        buf.indices = std::vector<PrimitiveMesh::Index>({
                0, 1, 2, 3
        });
-       buf.colors.resize(4, { 10.0f, 10.0f, 10.0f, 1.0f });
+       buf.colors.resize(4, { 255, 255, 255, 255 });
        crosshair.Update(buf);
 }
 
@@ -368,8 +427,8 @@ void HUD::UpdatePosition() {
 
 void HUD::UpdateOrientation() {
        std::stringstream s;
-       s << std::setprecision(3) << "pitch: " << rad2deg(player.GetEntity().Pitch())
-               << ", yaw: " << rad2deg(player.GetEntity().Yaw());
+       s << std::setprecision(3) << "pitch: " << glm::degrees(player.GetEntity().Pitch())
+               << ", yaw: " << glm::degrees(player.GetEntity().Yaw());
        orientation_text.Set(env.assets.small_ui_font, s.str());
 }
 
@@ -590,20 +649,19 @@ void Interface::HandlePress(const SDL_KeyboardEvent &event) {
                        break;
 
                case Keymap::TOGGLE_AUDIO:
-                       config.audio.enabled = !config.audio.enabled;
-                       client_ctrl.SetAudio(config.audio.enabled);
+                       client_ctrl.SetAudio(!config.audio.enabled);
                        break;
                case Keymap::TOGGLE_VIDEO:
-                       config.video.world = !config.video.world;
-                       client_ctrl.SetVideo(config.video.world);
+                       client_ctrl.SetVideo(!config.video.world);
                        break;
                case Keymap::TOGGLE_HUD:
-                       config.video.hud = !config.video.hud;
-                       client_ctrl.SetHUD(config.video.hud);
+                       client_ctrl.SetHUD(!config.video.hud);
                        break;
                case Keymap::TOGGLE_DEBUG:
-                       config.video.debug = !config.video.debug;
-                       client_ctrl.SetDebug(config.video.debug);
+                       client_ctrl.SetDebug(!config.video.debug);
+                       break;
+               case Keymap::CAMERA_NEXT:
+                       client_ctrl.NextCamera();
                        break;
 
                default:
@@ -773,7 +831,6 @@ void Keymap::LoadDefault() {
        Map(SDL_SCANCODE_0, INV_10);
 
        Map(SDL_SCANCODE_INSERT, SECONDARY);
-       Map(SDL_SCANCODE_RETURN, SECONDARY);
        Map(SDL_SCANCODE_MENU, TERTIARY);
        Map(SDL_SCANCODE_DELETE, PRIMARY);
        Map(SDL_SCANCODE_BACKSPACE, PRIMARY);
@@ -782,6 +839,7 @@ void Keymap::LoadDefault() {
        Map(SDL_SCANCODE_F2, TOGGLE_VIDEO);
        Map(SDL_SCANCODE_F3, TOGGLE_DEBUG);
        Map(SDL_SCANCODE_F4, TOGGLE_AUDIO);
+       Map(SDL_SCANCODE_F5, CAMERA_NEXT);
 
        Map(SDL_SCANCODE_ESCAPE, EXIT);
 }
@@ -867,6 +925,7 @@ std::map<std::string, Keymap::Action> action_map = {
        { "toggle_video", Keymap::TOGGLE_VIDEO },
        { "toggle_hud", Keymap::TOGGLE_HUD },
        { "toggle_debug", Keymap::TOGGLE_DEBUG },
+       { "camera_next", Keymap::CAMERA_NEXT },
 
        { "exit", Keymap::EXIT },
 };