Weapons and stuff pt. 1
This commit is contained in:
parent
5b5b6e66ab
commit
5062ef5cf0
@ -12,6 +12,8 @@ set(COMMON_SOURCES
|
||||
"src/assets/cache.cpp"
|
||||
"src/assets/cmdfile.hpp"
|
||||
"src/assets/cmdfile.cpp"
|
||||
"src/assets/item.hpp"
|
||||
"src/assets/item.cpp"
|
||||
"src/assets/map.hpp"
|
||||
"src/assets/map.cpp"
|
||||
"src/assets/model.hpp"
|
||||
@ -25,6 +27,8 @@ set(COMMON_SOURCES
|
||||
"src/collision/motionstate.hpp"
|
||||
"src/collision/trianglemesh.hpp"
|
||||
"src/collision/trianglemesh.cpp"
|
||||
"src/game/camera_controller.hpp"
|
||||
"src/game/camera_controller.cpp"
|
||||
"src/game/character_anim_state.hpp"
|
||||
"src/game/character_anim_state.cpp"
|
||||
"src/game/deform_grid.hpp"
|
||||
|
||||
@ -4,6 +4,7 @@ assets::SkeletonCache assets::CacheManager::skeleton_cache_;
|
||||
assets::ModelCache assets::CacheManager::model_cache_;
|
||||
assets::MapCache assets::CacheManager::map_cache_;
|
||||
assets::VehicleCache assets::CacheManager::vehicle_cache_;
|
||||
assets::ItemCache assets::CacheManager::item_cache_;
|
||||
|
||||
CLIENT_ONLY(assets::TextureCache assets::CacheManager::texture_cache_;)
|
||||
CLIENT_ONLY(assets::SoundCache assets::CacheManager::sound_cache_;)
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
#include "model.hpp"
|
||||
#include "skeleton.hpp"
|
||||
#include "vehiclemdl.hpp"
|
||||
#include "item.hpp"
|
||||
|
||||
#include "utils/defs.hpp"
|
||||
|
||||
@ -92,6 +93,12 @@ protected:
|
||||
PtrType Load(const std::string& key) override { return VehicleModel::LoadFromFile(key); }
|
||||
};
|
||||
|
||||
class ItemCache final : public Cache<Item>
|
||||
{
|
||||
protected:
|
||||
PtrType Load(const std::string& key) override { return Item::LoadFromFile(key); }
|
||||
};
|
||||
|
||||
class CacheManager
|
||||
{
|
||||
public:
|
||||
@ -109,6 +116,11 @@ public:
|
||||
return vehicle_cache_.Get(filename);
|
||||
}
|
||||
|
||||
static std::shared_ptr<const Item> GetItem(const std::string& filename)
|
||||
{
|
||||
return item_cache_.Get(filename);
|
||||
}
|
||||
|
||||
#ifdef CLIENT
|
||||
static std::shared_ptr<const gfx::Texture> GetTexture(const std::string& filename)
|
||||
{
|
||||
@ -131,6 +143,7 @@ private:
|
||||
static ModelCache model_cache_;
|
||||
static MapCache map_cache_;
|
||||
static VehicleCache vehicle_cache_;
|
||||
static ItemCache item_cache_;
|
||||
CLIENT_ONLY(static TextureCache texture_cache_;)
|
||||
CLIENT_ONLY(static SoundCache sound_cache_;)
|
||||
CLIENT_ONLY(static FontCache font_cache_;)
|
||||
|
||||
108
src/assets/item.cpp
Normal file
108
src/assets/item.cpp
Normal file
@ -0,0 +1,108 @@
|
||||
#include "item.hpp"
|
||||
|
||||
#include "cache.hpp"
|
||||
#include "cmdfile.hpp"
|
||||
|
||||
std::shared_ptr<assets::Item> assets::Item::LoadFromFile(const std::string& path)
|
||||
{
|
||||
auto item = std::make_shared<Item>();
|
||||
|
||||
LoadCMDFile(path, [&](const std::string& command, std::istringstream& iss) {
|
||||
if (command == "type")
|
||||
{
|
||||
std::string type_str;
|
||||
iss >> type_str;
|
||||
|
||||
if (type_str == "consumable")
|
||||
item->type = ITEM_CONSUMABLE;
|
||||
else if (type_str == "weapon")
|
||||
item->type = ITEM_WEAPON;
|
||||
else
|
||||
throw std::runtime_error("Unknown item type " + type_str);
|
||||
}
|
||||
else if (command == "name")
|
||||
{
|
||||
iss >> item->name;
|
||||
}
|
||||
else if (command == "anim")
|
||||
{
|
||||
std::string anim_type, anim_name;
|
||||
iss >> anim_type >> anim_name;
|
||||
|
||||
if (anim_type == "idle")
|
||||
item->idle_anim = anim_name;
|
||||
else if (anim_type == "use" || anim_type == "fire")
|
||||
item->use_anim = anim_name;
|
||||
else if (anim_type == "aim")
|
||||
item->aim_anim = anim_name;
|
||||
else if (anim_type == "aiming")
|
||||
item->aiming_anim = anim_name;
|
||||
else
|
||||
throw std::runtime_error("Unknown item anim type " + anim_type);
|
||||
}
|
||||
else if (command == "model")
|
||||
{
|
||||
std::string model_name;
|
||||
iss >> model_name;
|
||||
item->model = CacheManager::GetModel("data/" + model_name + ".mdl");
|
||||
}
|
||||
else if (command == "attach")
|
||||
{
|
||||
iss >> item->bone;
|
||||
glm::vec3 position;
|
||||
glm::vec3 angles;
|
||||
iss >> position.x >> position.y >> position.z >> angles.x >> angles.y >> angles.z;
|
||||
|
||||
item->bone_offset.position = position;
|
||||
item->bone_offset.rotation = glm::quat(glm::radians(angles));
|
||||
|
||||
// ParseTransform(iss, item->bone_offset);
|
||||
}
|
||||
else if (command == "weapontype")
|
||||
{
|
||||
std::string type_str;
|
||||
iss >> type_str;
|
||||
|
||||
if (type_str == "manual")
|
||||
item->weapon_type = WEAPON_MANUAL;
|
||||
else if (type_str == "semiauto")
|
||||
item->weapon_type = WEAPON_SEMIAUTO;
|
||||
else if (type_str == "auto")
|
||||
item->weapon_type = WEAPON_AUTO;
|
||||
else
|
||||
throw std::runtime_error("Unknown weapon type " + type_str);
|
||||
}
|
||||
else if (command == "firetype")
|
||||
{
|
||||
std::string type_str;
|
||||
iss >> type_str;
|
||||
|
||||
if (type_str == "melee")
|
||||
item->fire_type = FIRETYPE_MELEE;
|
||||
else if (type_str == "bullet")
|
||||
item->fire_type = FIRETYPE_BULLET;
|
||||
else if (type_str == "projectile")
|
||||
item->fire_type = FIRETYPE_PROJECTILE;
|
||||
else
|
||||
throw std::runtime_error("Unknown weapon type: " + type_str);
|
||||
}
|
||||
else if (command == "ammotype")
|
||||
{
|
||||
iss >> item->ammo_type;
|
||||
}
|
||||
else if (command == "clipsize")
|
||||
{
|
||||
iss >> item->clip_size;
|
||||
}
|
||||
else if (command == "firedelay")
|
||||
{
|
||||
iss >> item->fire_delay;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("Unknown item command: " + command);
|
||||
}
|
||||
});
|
||||
|
||||
return item;
|
||||
}
|
||||
70
src/assets/item.hpp
Normal file
70
src/assets/item.hpp
Normal file
@ -0,0 +1,70 @@
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "model.hpp"
|
||||
|
||||
namespace assets
|
||||
{
|
||||
|
||||
enum ItemType
|
||||
{
|
||||
ITEM_NONE,
|
||||
ITEM_WEAPON,
|
||||
ITEM_CONSUMABLE,
|
||||
};
|
||||
|
||||
enum ItemAimType
|
||||
{
|
||||
AIMTYPE_NONE,
|
||||
AIMTYPE_AIM,
|
||||
AIMTYPE_SCOPE,
|
||||
|
||||
};
|
||||
|
||||
enum WeaponType
|
||||
{
|
||||
WEAPON_MANUAL,
|
||||
WEAPON_SEMIAUTO,
|
||||
WEAPON_AUTO,
|
||||
};
|
||||
|
||||
enum WeaponFireType
|
||||
{
|
||||
FIRETYPE_MELEE,
|
||||
FIRETYPE_BULLET,
|
||||
FIRETYPE_PROJECTILE,
|
||||
};
|
||||
|
||||
struct Item
|
||||
{
|
||||
ItemType type = ITEM_NONE;
|
||||
std::string name;
|
||||
|
||||
std::string idle_anim;
|
||||
std::string use_anim; // use or fire
|
||||
|
||||
std::shared_ptr<const assets::Model> model;
|
||||
|
||||
std::string bone;
|
||||
Transform bone_offset;
|
||||
|
||||
// consumable
|
||||
std::string action;
|
||||
|
||||
// weapon
|
||||
WeaponType weapon_type = WEAPON_MANUAL;
|
||||
WeaponFireType fire_type = FIRETYPE_MELEE;
|
||||
std::string ammo_type;
|
||||
size_t clip_size = 0;
|
||||
size_t fire_delay = 0;
|
||||
|
||||
std::string aim_anim;
|
||||
std::string aiming_anim;
|
||||
|
||||
static std::shared_ptr<Item> LoadFromFile(const std::string& path);
|
||||
};
|
||||
|
||||
|
||||
|
||||
}
|
||||
@ -100,14 +100,16 @@ void assets::Skeleton::AddAnimation(const std::string& name, const std::shared_p
|
||||
|
||||
void assets::Skeleton::AddAimBones()
|
||||
{
|
||||
AddAimBone("DEF-spine.002", 0.5f);
|
||||
AddAimBone("MCH-spine.002", 0.5f);
|
||||
AddAimBone("DEF-spine.003", 0.5f);
|
||||
AddAimBone("MCH-spine.003", 0.5f);
|
||||
AddAimBone("DEF-spine.002", 1.0f, glm::vec3(0.0f, 1.0f, 0.0f), 0.0f, glm::vec3(0.0f));
|
||||
AddAimBone("spine_fk.002", 1.0f, glm::vec3(0.0f, 0.0f, 1.0f), 0.0f, glm::vec3(0.0f));
|
||||
|
||||
AddAimBone("DEF-spine.002", 0.0f, glm::vec3(0.0f), 0.5f, glm::vec3(1.0f, 0.0f, 0.0f));
|
||||
AddAimBone("MCH-spine.002", 0.0f, glm::vec3(0.0f), 0.5f, glm::vec3(1.0f, 0.0f, 0.0f));
|
||||
AddAimBone("DEF-spine.003", 0.0f, glm::vec3(0.0f), 0.5f, glm::vec3(1.0f, 0.0f, 0.0f));
|
||||
AddAimBone("MCH-spine.003", 0.0f, glm::vec3(0.0f), 0.5f, glm::vec3(1.0f, 0.0f, 0.0f));
|
||||
}
|
||||
|
||||
void assets::Skeleton::AddAimBone(const std::string& name, float weight)
|
||||
void assets::Skeleton::AddAimBone(const std::string& name, float yaw_weight, const glm::vec3& yaw_axis, float pitch_weight, const glm::vec3& pitch_axis)
|
||||
{
|
||||
auto idx = GetBoneIndex(name);
|
||||
if (idx < 0)
|
||||
@ -115,6 +117,9 @@ void assets::Skeleton::AddAimBone(const std::string& name, float weight)
|
||||
|
||||
AimBone aimbone{};
|
||||
aimbone.idx = idx;
|
||||
aimbone.weight = weight;
|
||||
aimbone.yaw_weight = yaw_weight;
|
||||
aimbone.yaw_axis = yaw_axis;
|
||||
aimbone.pitch_weight = pitch_weight;
|
||||
aimbone.pitch_axis = pitch_axis;
|
||||
aim_bones_.emplace_back(aimbone);
|
||||
}
|
||||
|
||||
@ -25,7 +25,10 @@ constexpr AnimIdx NO_ANIM = 255;
|
||||
struct AimBone
|
||||
{
|
||||
size_t idx;
|
||||
float weight;
|
||||
float yaw_weight;
|
||||
glm::vec3 yaw_axis;
|
||||
float pitch_weight;
|
||||
glm::vec3 pitch_axis;
|
||||
};
|
||||
|
||||
class Skeleton
|
||||
@ -50,7 +53,7 @@ private:
|
||||
void AddAnimation(const std::string& name, const std::shared_ptr<const Animation>& anim);
|
||||
|
||||
void AddAimBones();
|
||||
void AddAimBone(const std::string& name, float weight);
|
||||
void AddAimBone(const std::string& name, float yaw_weight, const glm::vec3& yaw_axis, float pitch_weight, const glm::vec3& pitch_axis);
|
||||
|
||||
private:
|
||||
std::string name_;
|
||||
|
||||
@ -201,11 +201,11 @@ static void PollEvents()
|
||||
{
|
||||
if (event.button.button == SDL_BUTTON_LEFT)
|
||||
{
|
||||
s_app->Input(game::IN_ATTACK_PRIMARY, event.button.state == SDL_PRESSED, event.button.clicks > 1);
|
||||
s_app->Input(game::IN_ATTACK_PRIMARY, event.button.state == SDL_PRESSED, false);
|
||||
}
|
||||
else if (event.button.button == SDL_BUTTON_RIGHT)
|
||||
{
|
||||
s_app->Input(game::IN_ATTACK_SECONDARY, event.button.state == SDL_PRESSED, event.button.clicks > 1);
|
||||
s_app->Input(game::IN_ATTACK_SECONDARY, event.button.state == SDL_PRESSED, false);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@ -10,3 +10,30 @@ collision::DynamicsWorld::DynamicsWorld()
|
||||
|
||||
bt_broadphase_.getOverlappingPairCache()->setInternalGhostPairCallback(&bt_ghost_pair_cb_);
|
||||
}
|
||||
|
||||
glm::vec3 collision::DynamicsWorld::CameraSweep(const glm::vec3& start, const glm::vec3& end)
|
||||
{
|
||||
const auto& bt_world = GetBtWorld();
|
||||
|
||||
static const btSphereShape shape(0.1f);
|
||||
|
||||
btVector3 bt_start(start.x, start.y, start.z);
|
||||
btVector3 bt_end(end.x, end.y, end.z);
|
||||
|
||||
btTransform from, to;
|
||||
from.setIdentity();
|
||||
from.setOrigin(bt_start);
|
||||
to.setIdentity();
|
||||
to.setOrigin(bt_end);
|
||||
|
||||
btCollisionWorld::ClosestConvexResultCallback cb(bt_start, bt_end);
|
||||
cb.m_collisionFilterGroup = btBroadphaseProxy::DefaultFilter;
|
||||
cb.m_collisionFilterMask = btBroadphaseProxy::StaticFilter;
|
||||
|
||||
bt_world.convexSweepTest(&shape, from, to, cb);
|
||||
|
||||
if (!cb.hasHit())
|
||||
return end;
|
||||
|
||||
return glm::mix(start, end, cb.m_closestHitFraction);
|
||||
}
|
||||
|
||||
@ -15,6 +15,8 @@ class DynamicsWorld
|
||||
public:
|
||||
DynamicsWorld();
|
||||
|
||||
glm::vec3 CameraSweep(const glm::vec3& start, const glm::vec3& end);
|
||||
|
||||
btDynamicsWorld& GetBtWorld() { return bt_world_; }
|
||||
const btDynamicsWorld& GetBtWorld() const { return bt_world_; }
|
||||
btDbvtBroadphase& GetBtBroadphase() { return bt_broadphase_; }
|
||||
|
||||
@ -3,6 +3,11 @@
|
||||
#include <cstdint>
|
||||
#include <btBulletDynamicsCommon.h>
|
||||
|
||||
namespace game
|
||||
{
|
||||
struct BulletInfo;
|
||||
}
|
||||
|
||||
namespace collision
|
||||
{
|
||||
|
||||
@ -36,6 +41,7 @@ public:
|
||||
ObjectCallback() = default;
|
||||
|
||||
virtual void OnContact(const ContactInfo& info) {}
|
||||
virtual void OnBulletHit(const game::BulletInfo& bullet, const btCollisionObject* hit_object) {}
|
||||
|
||||
virtual ~ObjectCallback() = default;
|
||||
};
|
||||
@ -52,11 +58,27 @@ inline void AddObjectFlags(btCollisionObject* obj, ObjectFlags flags)
|
||||
obj->setUserIndex2(static_cast<int>(static_cast<ObjectFlags>(obj->getUserIndex2())) | flags);
|
||||
}
|
||||
|
||||
inline ObjectType GetObjectType(const btCollisionObject* obj)
|
||||
{
|
||||
return static_cast<ObjectType>(obj->getUserIndex());
|
||||
}
|
||||
|
||||
inline ObjectFlags GetObjectFlags(const btCollisionObject* obj)
|
||||
{
|
||||
return static_cast<ObjectFlags>(obj->getUserIndex2());
|
||||
}
|
||||
|
||||
inline ObjectCallback* GetObjectCallback(const btCollisionObject* obj)
|
||||
{
|
||||
return static_cast<ObjectCallback*>(obj->getUserPointer());
|
||||
}
|
||||
|
||||
// legacy
|
||||
inline void GetObjectInfo(const btCollisionObject* obj, ObjectType& type, ObjectFlags& flags, ObjectCallback*& callback)
|
||||
{
|
||||
type = static_cast<ObjectType>(obj->getUserIndex());
|
||||
flags = static_cast<ObjectFlags>(obj->getUserIndex2());
|
||||
callback = static_cast<ObjectCallback*>(obj->getUserPointer());
|
||||
type = GetObjectType(obj);
|
||||
flags = GetObjectFlags(obj);
|
||||
callback = GetObjectCallback(obj);
|
||||
}
|
||||
|
||||
}
|
||||
78
src/game/camera_controller.cpp
Normal file
78
src/game/camera_controller.cpp
Normal file
@ -0,0 +1,78 @@
|
||||
#include "camera_controller.hpp"
|
||||
|
||||
#include "utils/math.hpp"
|
||||
|
||||
void game::CameraController::SetViewAngles(float yaw, float pitch)
|
||||
{
|
||||
yaw_ = yaw;
|
||||
pitch_ = pitch;
|
||||
// TODO: validate
|
||||
}
|
||||
|
||||
static glm::vec3 TranslationFromMatrix(const glm::mat4& matrix)
|
||||
{
|
||||
return matrix[3];
|
||||
}
|
||||
|
||||
static glm::vec3 UpFromMatrix(const glm::mat4& matrix)
|
||||
{
|
||||
return matrix[2];
|
||||
}
|
||||
|
||||
void game::CameraController::Update(float time)
|
||||
{
|
||||
// update aim factor
|
||||
MoveToward(aim_factor_, aiming_ ? 1.0f : 0.0f, time * 3.0f);
|
||||
}
|
||||
|
||||
void game::CameraController::Recalculate(collision::DynamicsWorld* world)
|
||||
{
|
||||
float yaw_cos = glm::cos(yaw_);
|
||||
float yaw_sin = glm::sin(yaw_);
|
||||
float pitch_cos = glm::cos(pitch_);
|
||||
float pitch_sin = glm::sin(pitch_);
|
||||
forward_ = glm::vec3(-yaw_sin * pitch_cos, yaw_cos * pitch_cos, pitch_sin);
|
||||
glm::vec3 right = glm::cross(forward_, glm::vec3(0.0f, 0.0f, 1.0f));
|
||||
|
||||
glm::vec3 start_noaim(0.0f);
|
||||
glm::vec3 start_aim(0.0f);
|
||||
|
||||
float distance_noaim = 5.0f;
|
||||
float distance_aim = 1.0f;
|
||||
|
||||
auto aim_end_offset = right * 0.4f - forward_ * 1.8f;
|
||||
|
||||
if (character_transform_)
|
||||
{
|
||||
auto up = UpFromMatrix(*character_transform_);
|
||||
start_noaim = TranslationFromMatrix(*character_transform_) + up * 2.0f;
|
||||
start_aim = start_noaim - up * 0.3f;
|
||||
}
|
||||
|
||||
if (rideable_transform_)
|
||||
{
|
||||
start_noaim = TranslationFromMatrix(*rideable_transform_) + glm::vec3(0.0f, 0.0f, 2.0f);
|
||||
distance_noaim = 8.0f;
|
||||
distance_aim = 3.0f;
|
||||
}
|
||||
|
||||
glm::vec3 end_noaim = start_noaim - forward_ * distance_noaim;
|
||||
glm::vec3 end_aim = start_aim + aim_end_offset * distance_aim;
|
||||
|
||||
auto aim_factor_smooth = glm::smoothstep(0.0f, 1.0f, aim_factor_);
|
||||
auto start = glm::mix(start_noaim, start_aim, aim_factor_smooth);
|
||||
auto end = glm::mix(end_noaim, end_aim, aim_factor_smooth);
|
||||
|
||||
eye_ = end;
|
||||
|
||||
if (world)
|
||||
{
|
||||
// prevent penetration through static objects
|
||||
eye_ = world->CameraSweep(start, end);
|
||||
}
|
||||
}
|
||||
|
||||
glm::mat4 game::CameraController::GetViewMatrix() const
|
||||
{
|
||||
return glm::lookAt(eye_, eye_ + forward_, glm::vec3(0.0f, 0.0f, 1.0f));
|
||||
}
|
||||
46
src/game/camera_controller.hpp
Normal file
46
src/game/camera_controller.hpp
Normal file
@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include "collision/dynamicsworld.hpp"
|
||||
|
||||
namespace game
|
||||
{
|
||||
|
||||
class CameraController
|
||||
{
|
||||
public:
|
||||
CameraController() = default;
|
||||
|
||||
void SetCharacterTransform(const glm::mat4* transform) { character_transform_ = transform; }
|
||||
void SetRideableTransform(const glm::mat4* transform) { rideable_transform_ = transform; }
|
||||
|
||||
void SetViewAngles(float yaw, float pitch);
|
||||
float GetYaw() const { return yaw_; }
|
||||
float GetPitch() const { return pitch_; }
|
||||
|
||||
void SetAiming(bool aiming) { aiming_ = aiming; }
|
||||
|
||||
void Update(float time);
|
||||
void Recalculate(collision::DynamicsWorld* world);
|
||||
|
||||
const glm::vec3& GetEye() const { return eye_; }
|
||||
const glm::vec3& GetForward() const { return forward_; }
|
||||
glm::mat4 GetViewMatrix() const;
|
||||
float GetAimFactor() const { return aim_factor_; }
|
||||
|
||||
private:
|
||||
const glm::mat4* character_transform_ = nullptr;
|
||||
const glm::mat4* rideable_transform_ = nullptr;
|
||||
|
||||
float yaw_ = 0.0f;
|
||||
float pitch_ = 0.0f;
|
||||
|
||||
bool aiming_ = false;
|
||||
float aim_factor_ = 0.0f;
|
||||
|
||||
glm::vec3 eye_;
|
||||
glm::vec3 forward_;
|
||||
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
21
src/game/camera_info.hpp
Normal file
21
src/game/camera_info.hpp
Normal file
@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "net/defs.hpp"
|
||||
|
||||
namespace game
|
||||
{
|
||||
|
||||
using CameraFlags = uint8_t;
|
||||
enum CameraFlag : CameraFlags
|
||||
{
|
||||
CAM_AIMING = 1,
|
||||
};
|
||||
|
||||
struct CameraInfo
|
||||
{
|
||||
net::EntNum character_entnum = 0;
|
||||
net::EntNum rideable_entnum = 0;
|
||||
CameraFlags flags = 0;
|
||||
};
|
||||
|
||||
}
|
||||
@ -38,6 +38,7 @@ void game::Character::Update()
|
||||
|
||||
SyncTransformFromController();
|
||||
UpdateMovement();
|
||||
UpdateAiming();
|
||||
UpdateActionAnim();
|
||||
root_.UpdateMatrix();
|
||||
|
||||
@ -61,6 +62,9 @@ void game::Character::SendInitData(Player& player, net::OutMessage& msg) const
|
||||
net::WriteRGB(msg, clothes.color);
|
||||
}
|
||||
|
||||
// write item
|
||||
msg.Write(net::ModelName(item_));
|
||||
|
||||
// write state against default
|
||||
static const CharacterSyncState default_state;
|
||||
size_t fields_pos = msg.Reserve<CharacterSyncFieldFlags>();
|
||||
@ -157,6 +161,19 @@ void game::Character::ClearActionAnim()
|
||||
PlayActionAnim(assets::NO_ANIM, 0.0f);
|
||||
}
|
||||
|
||||
void game::Character::SetAimTarget(const glm::vec3& target)
|
||||
{
|
||||
aim_target_ = target;
|
||||
}
|
||||
|
||||
void game::Character::SetViewItem(const std::string& item_name)
|
||||
{
|
||||
item_ = item_name;
|
||||
|
||||
auto msg = BeginEntMsg(net::EMSG_EQUIP);
|
||||
msg.Write(net::ModelName(item_name));
|
||||
}
|
||||
|
||||
void game::Character::SyncControllerTransform()
|
||||
{
|
||||
if (!controller_)
|
||||
@ -250,13 +267,74 @@ void game::Character::UpdateMovement()
|
||||
// update anim
|
||||
float run_blend_target = walking ? 0.5f : 0.0f;
|
||||
MoveToward(animstate_.loco_blend, run_blend_target, dt * 2.0f);
|
||||
float anim_speed = glm::mix(0.5f, 1.5f, UnMix(0.0f, 0.5f, animstate_.loco_blend));
|
||||
float anim_speed = glm::mix(0.3f, 1.5f, UnMix(0.0f, 0.5f, animstate_.loco_blend));
|
||||
if (running)
|
||||
anim_speed *= run_speed_mult_;
|
||||
animstate_.loco_phase = glm::mod(animstate_.loco_phase + anim_speed * dt, 1.0f);
|
||||
}
|
||||
|
||||
animstate_.pitch = view_pitch_;
|
||||
void game::Character::UpdateAiming()
|
||||
{
|
||||
float delta = 10.0f;
|
||||
|
||||
if (!aiming_)
|
||||
{
|
||||
delta = 3.0f / 25.0f;
|
||||
MoveToward(animstate_.yaw, 0.0f, delta);
|
||||
MoveToward(animstate_.pitch, 0.0f, delta);
|
||||
UpdateAimDirection();
|
||||
return;
|
||||
}
|
||||
|
||||
// get yaw and pitch relative to transform
|
||||
glm::vec3 dir = aim_target_ - GetRoot().local.position;
|
||||
|
||||
if (parent_)
|
||||
{
|
||||
auto inv_parent = glm::inverse(parent_->GetRoot().matrix);
|
||||
|
||||
// glm::vec3 character_pos_in_parent = inv_parent * glm::vec4(GetRoot().local.position, 1.0f);
|
||||
glm::vec3 aim_target_in_parent = inv_parent * glm::vec4(aim_target_, 1.0f);
|
||||
dir = aim_target_in_parent - GetRoot().local.position;
|
||||
}
|
||||
|
||||
dir.z -= aim_z_offset_; // from eye
|
||||
dir = glm::normalize(dir);
|
||||
|
||||
float pitch = glm::asin(dir.z);
|
||||
float yaw = glm::atan(-dir.x, dir.y);
|
||||
|
||||
auto target_pitch = glm::clamp(pitch, glm::radians(-60.0f), glm::radians(55.0f)); // clamp to make it less weird
|
||||
MoveToward(animstate_.pitch, target_pitch, delta);
|
||||
|
||||
if (movement_ == CMT_DISABLED)
|
||||
{
|
||||
auto target_yaw = glm::mod(yaw + glm::pi<float>(), glm::two_pi<float>()) - glm::pi<float>();
|
||||
MoveToward(animstate_.yaw, target_yaw, delta);
|
||||
}
|
||||
else
|
||||
{
|
||||
Turn(yaw_, yaw, delta);
|
||||
MoveToward(animstate_.yaw, 0.0f, delta);
|
||||
}
|
||||
|
||||
UpdateAimDirection();
|
||||
}
|
||||
|
||||
void game::Character::UpdateAimDirection()
|
||||
{
|
||||
eye_pos_ = GetRoot().matrix * glm::vec4(0.0f, 0.0f, aim_z_offset_, 1.0f);
|
||||
|
||||
auto pitch = animstate_.pitch;
|
||||
auto yaw = yaw_ + animstate_.yaw;
|
||||
aim_dir_ = glm::vec3(-glm::sin(yaw) * glm::cos(pitch), glm::cos(yaw) * glm::cos(pitch), glm::sin(pitch));
|
||||
|
||||
if (parent_)
|
||||
{
|
||||
aim_dir_ = glm::normalize(parent_->GetRoot().matrix * glm::vec4(aim_dir_, 0.0f));
|
||||
}
|
||||
|
||||
// GetWorld().Beam(eye_pos_, eye_pos_ + aim_dir_ * 100.0f, 0x0000FF, 1.0f / 25.0f);
|
||||
}
|
||||
|
||||
void game::Character::UpdateSyncState()
|
||||
|
||||
@ -79,10 +79,13 @@ public:
|
||||
float GetViewYaw() const { return view_yaw_; }
|
||||
float GetViewPitch() const { return view_pitch_; }
|
||||
|
||||
const glm::vec3& GetEyePosition() const { return eye_pos_; }
|
||||
const glm::vec3& GetAimDirection() const { return aim_dir_; }
|
||||
|
||||
void SetYaw(float yaw) { yaw_ = yaw; }
|
||||
|
||||
void SetPosition(const glm::vec3& position);
|
||||
|
||||
|
||||
~Character() override = default;
|
||||
|
||||
protected:
|
||||
@ -93,12 +96,18 @@ protected:
|
||||
void PlayActionAnim(const std::string& anim_name, float speed = 1.0f);
|
||||
void ClearActionAnim();
|
||||
bool IsActionAnimDone() { return action_anim_done_; }
|
||||
void SetAiming(bool aiming) { aiming_ = aiming; }
|
||||
bool GetAiming() const { return aiming_; }
|
||||
void SetAimTarget(const glm::vec3& target);
|
||||
void SetViewItem(const std::string& item_name);
|
||||
|
||||
private:
|
||||
void SyncControllerTransform();
|
||||
void SyncTransformFromController();
|
||||
|
||||
void UpdateMovement();
|
||||
void UpdateAiming();
|
||||
void UpdateAimDirection();
|
||||
void UpdateSyncState();
|
||||
void SendUpdateMsg();
|
||||
CharacterSyncFieldFlags WriteState(net::OutMessage& msg, const CharacterSyncState& base) const;
|
||||
@ -139,6 +148,14 @@ private:
|
||||
float action_anim_playback_speed_ = 0.0f;
|
||||
float action_anim_end_ = 0.0f;
|
||||
bool action_anim_done_ = true;
|
||||
|
||||
bool aiming_ = false;
|
||||
glm::vec3 aim_target_ = glm::vec3(0.0f);
|
||||
float aim_z_offset_ = 1.6f;
|
||||
glm::vec3 eye_pos_ = glm::vec3(0.0f);
|
||||
glm::vec3 aim_dir_ = glm::vec3(0.0f);
|
||||
|
||||
std::string item_;
|
||||
};
|
||||
|
||||
} // namespace game
|
||||
@ -47,8 +47,6 @@ void game::HumanCharacter::SetRideable(Rideable* rideable, size_t seat_idx)
|
||||
|
||||
SetSignal(HSS_RIDEABLE_CHANGED);
|
||||
OnRideableChanged();
|
||||
|
||||
|
||||
}
|
||||
|
||||
void game::HumanCharacter::Ride(Rideable* rideable, size_t seat_idx)
|
||||
@ -69,6 +67,27 @@ game::HumanCharacter::~HumanCharacter()
|
||||
Ride(nullptr, 0); // exit rideable
|
||||
}
|
||||
|
||||
void game::HumanCharacter::SetAiming(bool aiming)
|
||||
{
|
||||
if (aiming == GetAiming())
|
||||
return;
|
||||
|
||||
Super::SetAiming(aiming);
|
||||
OnAimingChanged();
|
||||
}
|
||||
|
||||
void game::HumanCharacter::Fire()
|
||||
{
|
||||
PlaySound("airrifle_fire");
|
||||
|
||||
game::BulletInfo bullet{};
|
||||
bullet.start = GetEyePosition();
|
||||
bullet.end = bullet.start + GetAimDirection() * 1000.0f;
|
||||
bullet.damage = 1.0f;
|
||||
bullet.shooter = this;
|
||||
GetWorld().FireBullet(bullet);
|
||||
}
|
||||
|
||||
void game::HumanCharacter::UpdateState()
|
||||
{
|
||||
struct HumanCharacterStateTableEntry
|
||||
@ -158,6 +177,8 @@ void game::HumanCharacter::StateOnFootEnter()
|
||||
SetWalkAnim("walk");
|
||||
SetMovementType(CMT_TURN);
|
||||
EnablePhysics(true);
|
||||
|
||||
EnterActionState(ACTION_IDLE);
|
||||
}
|
||||
|
||||
game::HumanCharacterState game::HumanCharacter::StateOnFootUpdate()
|
||||
@ -168,7 +189,7 @@ game::HumanCharacterState game::HumanCharacter::StateOnFootUpdate()
|
||||
if (PopSignal(HSS_KNOCK_DOWN))
|
||||
return HS_KNOCKED_DOWN;
|
||||
|
||||
SetMovementType(aiming_ ? CMT_DIRECTIONAL : CMT_TURN);
|
||||
SetMovementType(aimheld_ ? CMT_DIRECTIONAL : CMT_TURN);
|
||||
|
||||
return HS_ON_FOOT;
|
||||
}
|
||||
@ -183,6 +204,8 @@ void game::HumanCharacter::StateRidingEnter()
|
||||
SetIdleAnim((rideable->GetRideableType() == RIDEABLE_VEHICLE && seat_idx_ == 0) ? "vehicle_drive" : "vehicle_passenger");
|
||||
SetYaw(0.0f);
|
||||
SetMovementType(CMT_DISABLED);
|
||||
|
||||
EnterActionState(ACTION_IDLE);
|
||||
}
|
||||
|
||||
game::HumanCharacterState game::HumanCharacter::StateRidingUpdate()
|
||||
@ -217,32 +240,42 @@ void game::HumanCharacter::UpdateActionState()
|
||||
if (new_state == actionstate_)
|
||||
break;
|
||||
|
||||
ExitActionState();
|
||||
actionstate_ = new_state;
|
||||
EnterActionState();
|
||||
EnterActionState(new_state);
|
||||
}
|
||||
}
|
||||
|
||||
void game::HumanCharacter::EnterActionState()
|
||||
void game::HumanCharacter::EnterActionState(ActionState state)
|
||||
{
|
||||
switch (actionstate_)
|
||||
actionstate_ = state;
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case ACTION_IDLE:
|
||||
ClearActionAnim();
|
||||
if (state_ == HS_ON_FOOT)
|
||||
SetIdleAnim("idle_relaxed");
|
||||
SetAiming(false);
|
||||
PlayActionAnim("rifle_idle");
|
||||
break;
|
||||
|
||||
case ACTION_AIM:
|
||||
SetViewItem("airsniper");
|
||||
SetAiming(true);
|
||||
PlayActionAnim("rifle_aim", 3.0f);
|
||||
break;
|
||||
|
||||
case ACTION_AIMING:
|
||||
SetAiming(true);
|
||||
PlayActionAnim("rifle_aiming");
|
||||
break;
|
||||
|
||||
case ACTION_FIRE:
|
||||
SetAiming(true);
|
||||
PlayActionAnim("rifle_fire");
|
||||
Fire();
|
||||
break;
|
||||
|
||||
case ACTION_UNAIM:
|
||||
SetAiming(false);
|
||||
PlayActionAnim("rifle_aim", -3.0f);
|
||||
break;
|
||||
|
||||
@ -256,7 +289,7 @@ game::ActionState game::HumanCharacter::CheckActionStateTransition()
|
||||
switch (actionstate_)
|
||||
{
|
||||
case ACTION_IDLE:
|
||||
if (aiming_) // want aim
|
||||
if (aimheld_) // want aim
|
||||
return ACTION_AIM;
|
||||
|
||||
return ACTION_IDLE;
|
||||
@ -265,27 +298,31 @@ game::ActionState game::HumanCharacter::CheckActionStateTransition()
|
||||
if (IsActionAnimDone())
|
||||
return ACTION_AIMING;
|
||||
|
||||
if (!aiming_) // stop aiming immediately
|
||||
if (!aimheld_) // stop aiming immediately
|
||||
return ACTION_UNAIM;
|
||||
|
||||
return ACTION_AIM;
|
||||
|
||||
case ACTION_AIMING:
|
||||
if (!aiming_)
|
||||
if (!aimheld_)
|
||||
return ACTION_UNAIM; // wants aim no more
|
||||
|
||||
// TODO: check fire
|
||||
if (fireheld_)
|
||||
return ACTION_FIRE;
|
||||
|
||||
return ACTION_AIMING;
|
||||
|
||||
case ACTION_FIRE:
|
||||
if (IsActionAnimDone())
|
||||
return ACTION_AIMING;
|
||||
|
||||
return ACTION_FIRE;
|
||||
|
||||
case ACTION_UNAIM:
|
||||
if (IsActionAnimDone())
|
||||
return ACTION_IDLE;
|
||||
|
||||
if (aiming_) // start aiming again
|
||||
if (aimheld_) // start aiming again
|
||||
return ACTION_AIM;
|
||||
|
||||
return ACTION_UNAIM;
|
||||
@ -294,12 +331,3 @@ game::ActionState game::HumanCharacter::CheckActionStateTransition()
|
||||
return actionstate_;
|
||||
}
|
||||
}
|
||||
|
||||
void game::HumanCharacter::ExitActionState()
|
||||
{
|
||||
switch (actionstate_)
|
||||
{
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -58,14 +58,19 @@ public:
|
||||
size_t GeatSeatIdx() const { return seat_idx_; }
|
||||
bool IsDriver() const { return is_driver_; }
|
||||
|
||||
void SetAiming(bool aiming) { aiming_ = aiming; }
|
||||
void SetAimHeld(bool aimheld) { aimheld_ = aimheld; }
|
||||
void SetFireHeld(bool fireheld) { fireheld_ = fireheld; }
|
||||
|
||||
virtual ~HumanCharacter() override;
|
||||
|
||||
protected:
|
||||
virtual void OnRideableChanged() {}
|
||||
virtual void OnAimingChanged() {}
|
||||
|
||||
private:
|
||||
void SetAiming(bool aiming);
|
||||
void Fire();
|
||||
|
||||
void UpdateState();
|
||||
void SetSignal(HumanCharacterStateSignal signal);
|
||||
bool PopSignal(HumanCharacterStateSignal signal);
|
||||
@ -89,9 +94,8 @@ private:
|
||||
|
||||
void UpdateActionState();
|
||||
|
||||
void EnterActionState();
|
||||
void EnterActionState(ActionState state);
|
||||
ActionState CheckActionStateTransition();
|
||||
void ExitActionState();
|
||||
|
||||
private:
|
||||
HumanCharacterTuning human_tuning_;
|
||||
@ -106,7 +110,8 @@ private:
|
||||
|
||||
glm::vec3 rideable_exit_pos_ = glm::vec3(0.0f);
|
||||
|
||||
bool aiming_ = false;
|
||||
bool aimheld_ = false;
|
||||
bool fireheld_ = false;
|
||||
|
||||
ActionState actionstate_ = ACTION_IDLE;
|
||||
|
||||
|
||||
@ -35,6 +35,7 @@ void game::Player::Update()
|
||||
{
|
||||
SyncWorld();
|
||||
SendMenuMsgs();
|
||||
UpdateCamera();
|
||||
}
|
||||
|
||||
void game::Player::SetWorld(World* world)
|
||||
@ -45,12 +46,14 @@ void game::Player::SetWorld(World* world)
|
||||
world_ = world;
|
||||
}
|
||||
|
||||
void game::Player::SetCamera(net::EntNum entnum)
|
||||
void game::Player::SetCamera(const CameraInfo& camera_info)
|
||||
{
|
||||
cam_ent_ = entnum;
|
||||
camera_info_ = camera_info;
|
||||
|
||||
auto msg = BeginMsg(net::MSG_CAM);
|
||||
msg.Write(entnum);
|
||||
msg.Write(camera_info.character_entnum);
|
||||
msg.Write(camera_info.rideable_entnum);
|
||||
msg.Write(camera_info.flags);
|
||||
}
|
||||
|
||||
void game::Player::SendChat(const std::string& text)
|
||||
@ -97,6 +100,24 @@ void game::Player::CloseMenu(const RemoteMenu& menu)
|
||||
remote_menu_.reset();
|
||||
}
|
||||
|
||||
bool game::Player::GetView(glm::vec3& eye, glm::vec3& forward)
|
||||
{
|
||||
if (!world_)
|
||||
return false;
|
||||
|
||||
auto character = world_->GetEntity(camera_info_.character_entnum);
|
||||
camera_controller_.SetCharacterTransform(character ? &character->GetRoot().matrix : nullptr);
|
||||
|
||||
auto rideable = world_->GetEntity(camera_info_.rideable_entnum);
|
||||
camera_controller_.SetRideableTransform(rideable ? &rideable->GetRoot().matrix : nullptr);
|
||||
|
||||
camera_controller_.Recalculate(world_);
|
||||
|
||||
eye = camera_controller_.GetEye();
|
||||
forward = camera_controller_.GetForward();
|
||||
return true;
|
||||
}
|
||||
|
||||
game::Player::~Player()
|
||||
{
|
||||
game_.PlayerLeft(*this);
|
||||
@ -115,12 +136,26 @@ void game::Player::SyncWorld()
|
||||
|
||||
if (world_)
|
||||
{
|
||||
UpdateCullPos();
|
||||
SendWorldUpdateMsg();
|
||||
SendEnv();
|
||||
SyncEntities();
|
||||
}
|
||||
}
|
||||
|
||||
void game::Player::UpdateCullPos()
|
||||
{
|
||||
auto cam_entnum = camera_info_.rideable_entnum ? camera_info_.rideable_entnum : camera_info_.character_entnum;
|
||||
if (cam_entnum)
|
||||
{
|
||||
auto cam_ent = world_->GetEntity(cam_entnum);
|
||||
if (cam_ent)
|
||||
{
|
||||
cull_pos_ = cam_ent->GetRoot().GetGlobalPosition();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void game::Player::SendWorldMsg()
|
||||
{
|
||||
MSGDEBUG(std::cout << "seding CHWORLD" << std::endl;)
|
||||
@ -137,6 +172,9 @@ void game::Player::SendWorldUpdateMsg()
|
||||
|
||||
auto msg = BeginMsg(); // no CMD here, included in world payload
|
||||
msg.Write(world_->GetMsg());
|
||||
|
||||
// local msgs
|
||||
world_->PickLocalMsgs(*this, cull_pos_);
|
||||
}
|
||||
|
||||
void game::Player::SendEnv()
|
||||
@ -152,16 +190,6 @@ void game::Player::SendEnv()
|
||||
|
||||
void game::Player::SyncEntities()
|
||||
{
|
||||
// update cull pos
|
||||
if (cam_ent_)
|
||||
{
|
||||
auto cam_ent = world_->GetEntity(cam_ent_);
|
||||
if (cam_ent)
|
||||
{
|
||||
cull_pos_ = cam_ent->GetRoot().GetGlobalPosition();
|
||||
}
|
||||
}
|
||||
|
||||
// list of entities to send update and messages of
|
||||
static std::vector<const Entity*> upd_ents;
|
||||
upd_ents.clear();
|
||||
@ -293,10 +321,8 @@ bool game::Player::ProcessViewAnglesMsg(net::InMessage& msg)
|
||||
if (!msg.Read(yaw_q.value) || !msg.Read(pitch_q.value))
|
||||
return false;
|
||||
|
||||
view_yaw_ = yaw_q.Decode();
|
||||
view_pitch_ = pitch_q.Decode();
|
||||
|
||||
game_.PlayerViewAnglesChanged(*this, view_yaw_, view_pitch_);
|
||||
camera_controller_.SetViewAngles(yaw_q.Decode(), pitch_q.Decode());
|
||||
game_.PlayerViewAnglesChanged(*this, camera_controller_.GetYaw(), camera_controller_.GetPitch());
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -357,3 +383,9 @@ void game::Player::SendMenuMsgs()
|
||||
remote_menu_->ResetMsg();
|
||||
|
||||
}
|
||||
|
||||
void game::Player::UpdateCamera()
|
||||
{
|
||||
camera_controller_.SetAiming(camera_info_.flags & CAM_AIMING);
|
||||
camera_controller_.Update(1.0f / 25.0f);
|
||||
}
|
||||
|
||||
@ -9,10 +9,10 @@
|
||||
#include "net/inmessage.hpp"
|
||||
#include "net/msg_producer.hpp"
|
||||
#include "utils/defs.hpp"
|
||||
|
||||
#include "player_input.hpp"
|
||||
|
||||
#include "remote_menu.hpp"
|
||||
#include "camera_info.hpp"
|
||||
#include "camera_controller.hpp"
|
||||
|
||||
namespace game
|
||||
{
|
||||
@ -32,7 +32,7 @@ public:
|
||||
|
||||
void SetWorld(World* world);
|
||||
|
||||
void SetCamera(net::EntNum entnum);
|
||||
void SetCamera(const CameraInfo& camera_info);
|
||||
void SendChat(const std::string& text);
|
||||
void SetUseTarget(const std::string& text, const std::string& error_text, float delay);
|
||||
|
||||
@ -43,8 +43,9 @@ public:
|
||||
const std::string& GetName() const { return name_; }
|
||||
|
||||
PlayerInputFlags GetInput() const { return in_; }
|
||||
float GetViewYaw() const { return view_yaw_; }
|
||||
float GetViewPitch() const { return view_pitch_; }
|
||||
float GetViewYaw() const { return camera_controller_.GetYaw(); }
|
||||
float GetViewPitch() const { return camera_controller_.GetPitch(); }
|
||||
bool GetView(glm::vec3& eye, glm::vec3& forward);
|
||||
|
||||
const glm::vec3 GetCullPos() const { return cull_pos_; }
|
||||
|
||||
@ -53,6 +54,7 @@ public:
|
||||
private:
|
||||
// world sync
|
||||
void SyncWorld();
|
||||
void UpdateCullPos();
|
||||
void SendWorldMsg();
|
||||
void SendWorldUpdateMsg();
|
||||
void SendEnv();
|
||||
@ -74,6 +76,8 @@ private:
|
||||
// menu sync
|
||||
void SendMenuMsgs();
|
||||
|
||||
void UpdateCamera();
|
||||
|
||||
private:
|
||||
Game& game_;
|
||||
std::string name_;
|
||||
@ -84,9 +88,9 @@ private:
|
||||
int64_t last_env_time_ = 0;
|
||||
|
||||
PlayerInputFlags in_ = 0;
|
||||
float view_yaw_ = 0.0f, view_pitch_ = 0.0f;
|
||||
|
||||
net::EntNum cam_ent_ = 0;
|
||||
CameraInfo camera_info_;
|
||||
CameraController camera_controller_;
|
||||
glm::vec3 cull_pos_ = glm::vec3(0.0f);
|
||||
|
||||
// menus
|
||||
|
||||
@ -13,6 +13,7 @@ game::PlayerCharacter::PlayerCharacter(World& world, Player& player, const Human
|
||||
void game::PlayerCharacter::Update()
|
||||
{
|
||||
UpdateUseTarget();
|
||||
UpdateAimTarget();
|
||||
Super::Update();
|
||||
|
||||
if (GetRideable() && IsDriver())
|
||||
@ -46,19 +47,24 @@ void game::PlayerCharacter::OnRideableChanged()
|
||||
UpdateInputs();
|
||||
}
|
||||
|
||||
void game::PlayerCharacter::OnAimingChanged()
|
||||
{
|
||||
UpdatePlayerCamera();
|
||||
}
|
||||
|
||||
void game::PlayerCharacter::UpdatePlayerCamera()
|
||||
{
|
||||
if (!player_)
|
||||
return;
|
||||
|
||||
if (auto rideable = GetRideable(); rideable)
|
||||
{
|
||||
player_->SetCamera(rideable->GetEntity().GetEntNum());
|
||||
}
|
||||
else
|
||||
{
|
||||
player_->SetCamera(GetEntNum());
|
||||
}
|
||||
CameraInfo camera_info{};
|
||||
camera_info.character_entnum = GetEntNum();
|
||||
camera_info.rideable_entnum = GetRideable() ? GetRideable()->GetEntity().GetEntNum() : 0;
|
||||
|
||||
if (GetAiming())
|
||||
camera_info.flags |= CAM_AIMING;
|
||||
|
||||
player_->SetCamera(camera_info);
|
||||
}
|
||||
|
||||
void game::PlayerCharacter::UpdateInputs()
|
||||
@ -79,7 +85,38 @@ void game::PlayerCharacter::UpdateInputs()
|
||||
SetInputs(MapPlayerInputToCharacterInput(in));
|
||||
}
|
||||
|
||||
SetAiming(in & (1 << IN_ATTACK_SECONDARY));
|
||||
SetAimHeld(in & (1 << IN_ATTACK_SECONDARY));
|
||||
SetFireHeld(in & (1 << IN_ATTACK_PRIMARY));
|
||||
}
|
||||
|
||||
void game::PlayerCharacter::UpdateAimTarget()
|
||||
{
|
||||
if (!player_)
|
||||
return;
|
||||
|
||||
glm::vec3 eye, forward;
|
||||
if (!player_->GetView(eye, forward))
|
||||
return;
|
||||
|
||||
auto target = eye + forward * 1000.0f;
|
||||
|
||||
btVector3 bt_from(eye.x, eye.y, eye.z);
|
||||
btVector3 bt_to(target.x, target.y, target.z);
|
||||
|
||||
btCollisionWorld::ClosestRayResultCallback cb(bt_from, bt_to);
|
||||
cb.m_collisionFilterGroup = btBroadphaseProxy::DefaultFilter;
|
||||
cb.m_collisionFilterMask = btBroadphaseProxy::StaticFilter;
|
||||
|
||||
GetWorld().GetBtWorld().rayTest(bt_from, bt_to, cb);
|
||||
|
||||
if (cb.hasHit())
|
||||
{
|
||||
target = glm::vec3(cb.m_hitPointWorld.x(), cb.m_hitPointWorld.y(), cb.m_hitPointWorld.z());
|
||||
}
|
||||
|
||||
SetAimTarget(target);
|
||||
|
||||
// GetWorld().Beam(eye, target, 0xFFFF00, 1.0 / 25.0f);
|
||||
}
|
||||
|
||||
void game::PlayerCharacter::UpdateUseTarget()
|
||||
|
||||
@ -24,15 +24,18 @@ public:
|
||||
|
||||
protected:
|
||||
virtual void OnRideableChanged() override;
|
||||
virtual void OnAimingChanged() override;
|
||||
|
||||
private:
|
||||
void UpdatePlayerCamera();
|
||||
void UpdateInputs();
|
||||
void UpdateAimTarget();
|
||||
|
||||
void UpdateUseTarget();
|
||||
void UseChanged(bool enabled);
|
||||
void SendUseTargetInfo();
|
||||
|
||||
|
||||
private:
|
||||
Player* player_;
|
||||
|
||||
|
||||
@ -7,6 +7,15 @@ game::SkeletonInstance::SkeletonInstance(std::shared_ptr<const assets::Skeleton>
|
||||
SetupBoneNodes();
|
||||
}
|
||||
|
||||
const game::TransformNode* game::SkeletonInstance::GetBoneNodeByName(const std::string& bone_name) const
|
||||
{
|
||||
auto idx = skeleton_->GetBoneIndex(bone_name);
|
||||
if (idx < 0)
|
||||
return nullptr;
|
||||
|
||||
return &GetBoneNode(idx);
|
||||
}
|
||||
|
||||
void game::SkeletonInstance::ApplySkelAnim(const assets::Animation& anim, float time, float weight)
|
||||
{
|
||||
float anim_frame = time * anim.GetTPS();
|
||||
@ -61,8 +70,20 @@ void game::SkeletonInstance::ApplyAim(float yaw, float pitch)
|
||||
for (const auto& aim_bone : aim_bones)
|
||||
{
|
||||
auto& bone_transform = bone_nodes_[aim_bone.idx].local;
|
||||
auto rotation = glm::angleAxis(-pitch * aim_bone.weight, glm::vec3(1.0f, 0.0f, 0.0f));
|
||||
bone_transform.rotation = rotation * bone_transform.rotation;
|
||||
|
||||
if (aim_bone.pitch_weight > 0.0f)
|
||||
{
|
||||
auto pitch_rotation = glm::angleAxis(-pitch * aim_bone.pitch_weight, aim_bone.pitch_axis);
|
||||
bone_transform.rotation = pitch_rotation * bone_transform.rotation;
|
||||
}
|
||||
|
||||
if (aim_bone.yaw_weight > 0.0f)
|
||||
{
|
||||
auto yaw_rotation = glm::angleAxis(yaw * aim_bone.yaw_weight, aim_bone.yaw_axis);
|
||||
bone_transform.rotation = yaw_rotation * bone_transform.rotation;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -15,6 +15,7 @@ public:
|
||||
|
||||
const TransformNode* GetRootNode() const { return root_node_; }
|
||||
const TransformNode& GetBoneNode(size_t index) const { return bone_nodes_[index]; }
|
||||
const TransformNode* GetBoneNodeByName(const std::string& bone_name) const;
|
||||
|
||||
void ApplySkelAnim(const assets::Animation& anim, float time, float weight);
|
||||
void ApplyAim(float yaw, float pitch);
|
||||
|
||||
@ -92,6 +92,18 @@ void game::Vehicle::OnContact(const collision::ContactInfo& info)
|
||||
}
|
||||
}
|
||||
|
||||
void game::Vehicle::OnBulletHit(const game::BulletInfo& bullet, const btCollisionObject* hit_object)
|
||||
{
|
||||
Super::OnBulletHit(bullet, hit_object);
|
||||
|
||||
if (!physics_)
|
||||
return;
|
||||
|
||||
auto impulse = glm::normalize(bullet.end - bullet.start) * 10000.0f;
|
||||
physics_->GetBtBody().activate();
|
||||
physics_->GetBtBody().applyCentralImpulse(btVector3(impulse.x, impulse.y, impulse.z));
|
||||
}
|
||||
|
||||
void game::Vehicle::SetInput(VehicleInputType type, bool enable)
|
||||
{
|
||||
if (enable)
|
||||
|
||||
@ -64,6 +64,7 @@ public:
|
||||
virtual void SendInitData(Player& player, net::OutMessage& msg) const override;
|
||||
|
||||
virtual void OnContact(const collision::ContactInfo& info) override;
|
||||
virtual void OnBulletHit(const game::BulletInfo& bullet, const btCollisionObject* hit_object);
|
||||
|
||||
void SetInput(VehicleInputType type, bool enable);
|
||||
void SetInputs(VehicleInputFlags inputs) { in_ = inputs; }
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
#include "destroyed_object.hpp"
|
||||
#include "utils/allocnum.hpp"
|
||||
#include "player_character.hpp"
|
||||
#include "net/utils.hpp"
|
||||
|
||||
game::World::World(std::string mapname) : Scheduler(time_ms_), map_(*this, std::move(mapname)) {}
|
||||
|
||||
@ -68,6 +69,7 @@ void game::World::Update(int64_t delta_time)
|
||||
void game::World::FinishFrame()
|
||||
{
|
||||
ResetMsg();
|
||||
ResetLocalMsgs();
|
||||
|
||||
// reset ent msgs
|
||||
for (auto& [entnum, ent] : ents_)
|
||||
@ -171,6 +173,112 @@ const game::UseTarget* game::World::GetBestUseTarget(game::PlayerCharacter& char
|
||||
return cb.best_target;
|
||||
}
|
||||
|
||||
static bool IsMeOrMyRideOrOtherPassengerOfMyRide(const game::HumanCharacter* me, const btCollisionObject* obj)
|
||||
{
|
||||
if (!me) // i am not
|
||||
return false;
|
||||
|
||||
// is me?
|
||||
auto obj_cb = collision::GetObjectCallback(obj);
|
||||
if (!obj_cb)
|
||||
return false; // is nothing
|
||||
|
||||
if (obj_cb == me)
|
||||
return true; // its me
|
||||
|
||||
auto my_ride = me->GetRideable();
|
||||
if (!my_ride)
|
||||
return false; // im not riding anything
|
||||
|
||||
// is my ride?
|
||||
if (&my_ride->GetEntity() == obj_cb)
|
||||
return true; // yes
|
||||
|
||||
// is other passenger?
|
||||
auto character = dynamic_cast<game::HumanCharacter*>(obj_cb);
|
||||
if (!character)
|
||||
return false; // is not even human
|
||||
|
||||
return character->GetRideable() == my_ride;
|
||||
}
|
||||
|
||||
struct NotMeNotMyRideAndNotOtherPassengersOfMyRideClosestRayResultCallback : public btCollisionWorld::ClosestRayResultCallback
|
||||
{
|
||||
using Super = ClosestRayResultCallback;
|
||||
|
||||
NotMeNotMyRideAndNotOtherPassengersOfMyRideClosestRayResultCallback(const btVector3& rayFromWorld,
|
||||
const btVector3& rayToWorld)
|
||||
: ClosestRayResultCallback(rayFromWorld, rayToWorld)
|
||||
{
|
||||
}
|
||||
|
||||
game::HumanCharacter* me = nullptr;
|
||||
|
||||
virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult, bool normalInWorldSpace) override
|
||||
{
|
||||
if (IsMeOrMyRideOrOtherPassengerOfMyRide(me, rayResult.m_collisionObject))
|
||||
return rayResult.m_hitFraction;
|
||||
|
||||
return Super::addSingleResult(rayResult, normalInWorldSpace);
|
||||
}
|
||||
};
|
||||
|
||||
void game::World::FireBullet(const BulletInfo& bullet)
|
||||
{
|
||||
btVector3 bt_start(bullet.start.x, bullet.start.y, bullet.start.z);
|
||||
btVector3 bt_end(bullet.end.x, bullet.end.y, bullet.end.z);
|
||||
|
||||
NotMeNotMyRideAndNotOtherPassengersOfMyRideClosestRayResultCallback cb(bt_start, bt_end);
|
||||
cb.me = bullet.shooter;
|
||||
GetBtWorld().rayTest(bt_start, bt_end, cb);
|
||||
|
||||
if (!cb.hasHit() || !cb.m_collisionObject)
|
||||
return;
|
||||
|
||||
auto obj_cb = collision::GetObjectCallback(cb.m_collisionObject);
|
||||
obj_cb->OnBulletHit(bullet, cb.m_collisionObject);
|
||||
|
||||
glm::vec3 hit_pos(cb.m_hitPointWorld.x(), cb.m_hitPointWorld.y(), cb.m_hitPointWorld.z());
|
||||
const float box_extent = 0.1f;
|
||||
BeamBox(hit_pos - box_extent, hit_pos + box_extent, 0x0077FF, 1.0f);
|
||||
}
|
||||
|
||||
void game::World::Beam(const glm::vec3& start, const glm::vec3& end, uint32_t color, float time)
|
||||
{
|
||||
auto msg = BeginLocalMsg(start, 500.0f, net::MSG_BEAM);
|
||||
net::WritePosition(msg, start);
|
||||
net::WritePosition(msg, end);
|
||||
net::WriteRGB(msg, color);
|
||||
msg.Write<net::BeamTimeQ>(time);
|
||||
}
|
||||
|
||||
void game::World::BeamBox(const glm::vec3& min, const glm::vec3& max, uint32_t color, float time)
|
||||
{
|
||||
const glm::vec3& p0 = min;
|
||||
const glm::vec3 p1(max.x, min.y, min.z);
|
||||
const glm::vec3 p2(max.x, max.y, min.z);
|
||||
const glm::vec3 p3(min.x, max.y, min.z);
|
||||
const glm::vec3 p4(min.x, min.y, max.z);
|
||||
const glm::vec3 p5(max.x, min.y, max.z);
|
||||
const glm::vec3& p6 = max;
|
||||
const glm::vec3 p7(min.x, max.y, max.z);
|
||||
|
||||
Beam(p0, p1, color, time);
|
||||
Beam(p1, p2, color, time);
|
||||
Beam(p2, p3, color, time);
|
||||
Beam(p3, p0, color, time);
|
||||
|
||||
Beam(p4, p5, color, time);
|
||||
Beam(p5, p6, color, time);
|
||||
Beam(p6, p7, color, time);
|
||||
Beam(p7, p4, color, time);
|
||||
|
||||
Beam(p0, p4, color, time);
|
||||
Beam(p1, p5, color, time);
|
||||
Beam(p2, p6, color, time);
|
||||
Beam(p3, p7, color, time);
|
||||
}
|
||||
|
||||
void game::World::HandleContacts()
|
||||
{
|
||||
auto& bt_world = GetBtWorld();
|
||||
|
||||
@ -13,7 +13,17 @@
|
||||
namespace game
|
||||
{
|
||||
|
||||
class World : public collision::DynamicsWorld, public net::MsgProducer, public Scheduler
|
||||
class HumanCharacter;
|
||||
|
||||
struct BulletInfo
|
||||
{
|
||||
game::HumanCharacter* shooter;
|
||||
glm::vec3 start;
|
||||
glm::vec3 end;
|
||||
float damage;
|
||||
};
|
||||
|
||||
class World : public collision::DynamicsWorld, public net::MsgProducer, public net::LocalMsgProducer, public Scheduler
|
||||
{
|
||||
public:
|
||||
World(std::string mapname);
|
||||
@ -52,6 +62,11 @@ public:
|
||||
float GetDayTime() const { return daytime_; }
|
||||
void SetDayTime(float daytime) { daytime_ = glm::mod(daytime, 24.0f); }
|
||||
|
||||
void FireBullet(const BulletInfo& bullet);
|
||||
|
||||
void Beam(const glm::vec3& start, const glm::vec3& end, uint32_t color, float time);
|
||||
void BeamBox(const glm::vec3& min, const glm::vec3& max, uint32_t color, float time);
|
||||
|
||||
virtual ~World() = default;
|
||||
|
||||
private:
|
||||
|
||||
@ -34,6 +34,13 @@ game::view::CharacterView::CharacterView(WorldView& world, net::InMessage& msg)
|
||||
|
||||
UpdateSurfaceMask();
|
||||
|
||||
// read item
|
||||
net::ModelName item_name;
|
||||
if (!msg.Read(item_name))
|
||||
throw EntityInitError();
|
||||
|
||||
SetItem(item_name);
|
||||
|
||||
// read initial state
|
||||
if (!ReadState(&msg))
|
||||
throw EntityInitError();
|
||||
@ -47,6 +54,8 @@ bool game::view::CharacterView::ProcessMsg(net::EntMsgType type, net::InMessage&
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case net::EMSG_EQUIP:
|
||||
return ProcessEquipMsg(msg);
|
||||
default:
|
||||
return Super::ProcessMsg(type, msg);
|
||||
}
|
||||
@ -80,7 +89,11 @@ void game::view::CharacterView::Update(const UpdateInfo& info)
|
||||
|
||||
// action
|
||||
animstate_.action_phase = glm::mix(states_[0].action_phase, states_[1].action_phase, t_sane);
|
||||
|
||||
// if (animstate_.action_anim_idx != assets::NO_ANIM)
|
||||
// {
|
||||
// std::cout <<"phase: " << animstate_.action_phase << std::endl;
|
||||
// }
|
||||
|
||||
// aim
|
||||
animstate_.yaw = glm::mix(states_[0].aim_yaw, states_[1].aim_yaw, t_sane);
|
||||
animstate_.pitch = glm::mix(states_[0].aim_pitch, states_[1].aim_pitch, t_sane);
|
||||
@ -90,6 +103,11 @@ void game::view::CharacterView::Update(const UpdateInfo& info)
|
||||
root_.UpdateMatrix();
|
||||
sk_.UpdateBoneMatrices();
|
||||
ubo_valid_ = false;
|
||||
|
||||
if (item_)
|
||||
{
|
||||
item_node_.UpdateMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
void game::view::CharacterView::Draw(const DrawArgs& args)
|
||||
@ -152,6 +170,8 @@ void game::view::CharacterView::Draw(const DrawArgs& args)
|
||||
cmd.skinning = &ubo_;
|
||||
args.dlist.AddSurface(cmd);
|
||||
}
|
||||
|
||||
DrawItem(args);
|
||||
}
|
||||
|
||||
void game::view::CharacterView::OnAttach()
|
||||
@ -245,6 +265,7 @@ bool game::view::CharacterView::ReadState(net::InMessage* msg)
|
||||
{
|
||||
// anim just changed, dont blend phase
|
||||
old_state.action_phase = new_state.action_phase;
|
||||
animstate_.action_phase = new_state.action_phase;
|
||||
}
|
||||
}
|
||||
|
||||
@ -257,6 +278,7 @@ bool game::view::CharacterView::ReadState(net::InMessage* msg)
|
||||
new_state.aim_yaw = sync_.aim_yaw.Decode();
|
||||
new_state.aim_pitch = sync_.aim_pitch.Decode();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -306,3 +328,48 @@ void game::view::CharacterView::AddClothes(const std::string& name, const glm::v
|
||||
|
||||
clothes_.emplace_back(std::move(c));
|
||||
}
|
||||
|
||||
bool game::view::CharacterView::ProcessEquipMsg(net::InMessage& msg)
|
||||
{
|
||||
net::ModelName item_name;
|
||||
if (!msg.Read(item_name))
|
||||
return false;
|
||||
|
||||
SetItem(item_name);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void game::view::CharacterView::SetItem(const std::string& item_name)
|
||||
{
|
||||
if (item_name == item_name_)
|
||||
return;
|
||||
|
||||
if (item_name.empty())
|
||||
{
|
||||
item_.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
item_ = assets::CacheManager::GetItem("data/" + item_name + ".item");
|
||||
|
||||
auto bone_node = sk_.GetBoneNodeByName(item_->bone);
|
||||
item_node_.parent = bone_node ? bone_node : &root_;
|
||||
item_node_.local = item_->bone_offset;
|
||||
|
||||
}
|
||||
|
||||
void game::view::CharacterView::DrawItem(const DrawArgs& args)
|
||||
{
|
||||
if (!item_ || !item_->model)
|
||||
return;
|
||||
|
||||
const auto& mesh = *item_->model->GetMesh();
|
||||
for (const auto& surface : mesh.surfaces)
|
||||
{
|
||||
gfx::DrawSurfaceCmd cmd;
|
||||
cmd.surface = &surface;
|
||||
cmd.matrices = &item_node_.matrix;
|
||||
args.dlist.AddSurface(cmd);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
#include "entityview.hpp"
|
||||
#include "assets/model.hpp"
|
||||
#include "assets/item.hpp"
|
||||
#include "game/skeletoninstance.hpp"
|
||||
#include "skinning_ubo.hpp"
|
||||
#include "game/character_anim_state.hpp"
|
||||
@ -55,6 +56,11 @@ private:
|
||||
|
||||
void AddClothes(const std::string& name, const glm::vec3& color);
|
||||
|
||||
bool ProcessEquipMsg(net::InMessage& msg);
|
||||
|
||||
void SetItem(const std::string& item_name);
|
||||
void DrawItem(const DrawArgs& args);
|
||||
|
||||
private:
|
||||
float yaw_ = 0.0f;
|
||||
|
||||
@ -72,6 +78,10 @@ private:
|
||||
CharacterSyncState sync_;
|
||||
CharacterViewState states_[2];
|
||||
float update_time_ = 0.0f;
|
||||
|
||||
std::string item_name_;
|
||||
std::shared_ptr<const assets::Item> item_;
|
||||
TransformNode item_node_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@ -7,9 +7,12 @@
|
||||
#include "utils/version.hpp"
|
||||
#include "utils.hpp"
|
||||
#include "vehicleview.hpp"
|
||||
#include "assets/cache.hpp"
|
||||
|
||||
game::view::ClientSession::ClientSession(App& app) : app_(app), use_target_hud_(app.GetTime())
|
||||
{
|
||||
crosshair_texture_ = assets::CacheManager::GetTexture("data/crosshair.png");
|
||||
|
||||
// send login
|
||||
auto msg = BeginMsg(net::MSG_ID);
|
||||
msg.Write<net::Version>(FEKAL_VERSION);
|
||||
@ -75,18 +78,14 @@ void game::view::ClientSession::Input(game::PlayerInputType in, bool pressed, bo
|
||||
|
||||
void game::view::ClientSession::ProcessMouseMove(float delta_yaw, float delta_pitch)
|
||||
{
|
||||
yaw_ = glm::mod(yaw_ + delta_yaw, glm::two_pi<float>());
|
||||
auto sens_mult = glm::mix(1.0f, 0.3f, camera_controller_.GetAimFactor());
|
||||
|
||||
pitch_ += delta_pitch;
|
||||
// Clamp pitch to avoid gimbal lock
|
||||
if (pitch_ > glm::radians(89.0f))
|
||||
{
|
||||
pitch_ = glm::radians(89.0f);
|
||||
}
|
||||
else if (pitch_ < glm::radians(-89.0f))
|
||||
{
|
||||
pitch_ = glm::radians(-89.0f);
|
||||
}
|
||||
float yaw = glm::mod(camera_controller_.GetYaw() + delta_yaw * sens_mult, glm::two_pi<float>());
|
||||
|
||||
float pitch = camera_controller_.GetPitch() + delta_pitch * sens_mult;
|
||||
pitch = glm::clamp(pitch, glm::radians(-89.0f), glm::radians(89.0f)); // Clamp pitch to avoid gimbal lock
|
||||
|
||||
camera_controller_.SetViewAngles(yaw, pitch);
|
||||
}
|
||||
|
||||
void game::view::ClientSession::Update(const UpdateInfo& info)
|
||||
@ -95,6 +94,7 @@ void game::view::ClientSession::Update(const UpdateInfo& info)
|
||||
{
|
||||
world_->Update(info);
|
||||
SendViewAngles(info.time);
|
||||
UpdateCamera(info);
|
||||
}
|
||||
}
|
||||
|
||||
@ -105,41 +105,12 @@ void game::view::ClientSession::Draw(gfx::DrawList& dlist, gfx::DrawListParams&
|
||||
DrawWorld(dlist, params, gui);
|
||||
}
|
||||
|
||||
DrawCrosshair(gui);
|
||||
use_target_hud_.Draw(gui);
|
||||
|
||||
DrawMenus(gui);
|
||||
}
|
||||
|
||||
void game::view::ClientSession::GetViewInfo(glm::vec3& eye, glm::mat4& view) const
|
||||
{
|
||||
glm::vec3 start(0.0f, 0.0f, 2.0f);
|
||||
float distance = 5.0f;
|
||||
|
||||
if (follow_ent_)
|
||||
{
|
||||
auto ent = world_->GetEntity(follow_ent_);
|
||||
if (ent)
|
||||
{
|
||||
start += ent->GetRoot().GetGlobalPosition();
|
||||
|
||||
if (dynamic_cast<const VehicleView*>(ent))
|
||||
distance = 8.0f;
|
||||
}
|
||||
}
|
||||
|
||||
float yaw_cos = glm::cos(yaw_);
|
||||
float yaw_sin = glm::sin(yaw_);
|
||||
float pitch_cos = glm::cos(pitch_);
|
||||
float pitch_sin = glm::sin(pitch_);
|
||||
glm::vec3 dir(-yaw_sin * pitch_cos, yaw_cos * pitch_cos, pitch_sin);
|
||||
|
||||
glm::vec3 end = start - dir * distance;
|
||||
|
||||
// start.z -= 0.5f; // shift this a bit to make it better when occluded
|
||||
eye = world_->CameraSweep(start, end);
|
||||
view = glm::lookAt(eye, eye + dir, glm::vec3(0, 0, 1));
|
||||
}
|
||||
|
||||
audio::Master& game::view::ClientSession::GetAudioMaster() const
|
||||
{
|
||||
return app_.GetAudioMaster();
|
||||
@ -161,7 +132,7 @@ bool game::view::ClientSession::ProcessWorldMsg(net::InMessage& msg)
|
||||
|
||||
bool game::view::ClientSession::ProcessCameraMsg(net::InMessage& msg)
|
||||
{
|
||||
if (!msg.Read(follow_ent_))
|
||||
if (!msg.Read(camera_info_.character_entnum) || !msg.Read(camera_info_.rideable_entnum) || !msg.Read(camera_info_.flags))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
@ -222,6 +193,19 @@ bool game::view::ClientSession::ProcessMenuMsg(net::InMessage& msg)
|
||||
}
|
||||
}
|
||||
|
||||
void game::view::ClientSession::UpdateCamera(const UpdateInfo& info)
|
||||
{
|
||||
auto character = world_->GetEntity(camera_info_.character_entnum);
|
||||
camera_controller_.SetCharacterTransform(character ? &character->GetRoot().matrix : nullptr);
|
||||
|
||||
auto rideable = world_->GetEntity(camera_info_.rideable_entnum);
|
||||
camera_controller_.SetRideableTransform(rideable ? &rideable->GetRoot().matrix : nullptr);
|
||||
|
||||
camera_controller_.SetAiming(camera_info_.flags & CAM_AIMING);
|
||||
camera_controller_.Update(info.delta_time);
|
||||
camera_controller_.Recalculate(world_.get());
|
||||
}
|
||||
|
||||
void game::view::ClientSession::DrawWorld(gfx::DrawList& dlist, gfx::DrawListParams& params, gui::Context& gui)
|
||||
{
|
||||
// glm::mat4 view = glm::lookAt(glm::vec3(15.0f, 0.0f, 1.0f), glm::vec3(0.0f, 0.0f, -13.0f), glm::vec3(0.0f,
|
||||
@ -231,16 +215,14 @@ void game::view::ClientSession::DrawWorld(gfx::DrawList& dlist, gfx::DrawListPar
|
||||
const float farplane = 3000.0f;
|
||||
|
||||
glm::mat4 proj = glm::perspective(glm::radians(45.0f), aspect, 0.1f, farplane);
|
||||
glm::vec3 eye;
|
||||
glm::mat4 view;
|
||||
GetViewInfo(eye, view);
|
||||
glm::mat4 view = camera_controller_.GetViewMatrix();
|
||||
|
||||
params.view_proj = proj * view;
|
||||
params.cam_pos = eye;
|
||||
params.cam_pos = camera_controller_.GetEye();
|
||||
|
||||
// glm::mat4 fake_view_proj = glm::perspective(glm::radians(30.0f), aspect, 0.1f, 3000.0f) * view;
|
||||
|
||||
game::view::DrawArgs draw_args(dlist, params.env, gui, params.view_proj, eye,
|
||||
game::view::DrawArgs draw_args(dlist, params.env, gui, params.view_proj, params.cam_pos,
|
||||
glm::ivec2(params.screen_width, params.screen_height), farplane, 500.0f);
|
||||
world_->Draw(draw_args);
|
||||
|
||||
@ -264,8 +246,8 @@ void game::view::ClientSession::SendViewAngles(float time)
|
||||
|
||||
net::ViewYawQ yaw_q;
|
||||
net::ViewPitchQ pitch_q;
|
||||
yaw_q.Encode(yaw_);
|
||||
pitch_q.Encode(pitch_);
|
||||
yaw_q.Encode(camera_controller_.GetYaw());
|
||||
pitch_q.Encode(camera_controller_.GetPitch());
|
||||
|
||||
if (yaw_q.value == view_yaw_q_.value && pitch_q.value == view_pitch_q_.value)
|
||||
return;
|
||||
@ -312,3 +294,18 @@ game::view::RemoteMenuView* game::view::ClientSession::FindMenu(net::MenuId id)
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void game::view::ClientSession::DrawCrosshair(gui::Context& gui) const
|
||||
{
|
||||
if (camera_controller_.GetAimFactor() < 0.5f)
|
||||
return; // no aiming no crosshair
|
||||
|
||||
const float crosshair_size = 32.0f;
|
||||
|
||||
auto& viewport_size = gui.GetViewportSize();
|
||||
|
||||
auto p0 = viewport_size * 0.5f - crosshair_size * 0.5f;
|
||||
auto p1 = p0 + crosshair_size;
|
||||
|
||||
gui.DrawRect(p0, p1, 0xFFFFFFFF, crosshair_texture_.get());
|
||||
}
|
||||
|
||||
@ -3,7 +3,6 @@
|
||||
#include <memory>
|
||||
|
||||
#include "worldview.hpp"
|
||||
|
||||
#include "gfx/draw_list.hpp"
|
||||
#include "gfx/renderer.hpp"
|
||||
#include "net/defs.hpp"
|
||||
@ -12,6 +11,8 @@
|
||||
#include "game/player_input.hpp"
|
||||
#include "gui/use_target_hud.hpp"
|
||||
#include "remote_menu_view.hpp"
|
||||
#include "game/camera_info.hpp"
|
||||
#include "game/camera_controller.hpp"
|
||||
|
||||
class App;
|
||||
|
||||
@ -33,7 +34,6 @@ public:
|
||||
void Draw(gfx::DrawList& dlist, gfx::DrawListParams& params, gui::Context& gui);
|
||||
|
||||
const WorldView* GetWorld() const { return world_.get(); }
|
||||
void GetViewInfo(glm::vec3& eye, glm::mat4& view) const;
|
||||
audio::Master& GetAudioMaster() const;
|
||||
|
||||
private:
|
||||
@ -44,6 +44,7 @@ private:
|
||||
bool ProcessUseTargetMsg(net::InMessage& msg);
|
||||
bool ProcessMenuMsg(net::InMessage& msg);
|
||||
|
||||
void UpdateCamera(const UpdateInfo& info);
|
||||
void DrawWorld(gfx::DrawList& dlist, gfx::DrawListParams& params, gui::Context& gui);
|
||||
|
||||
void SendInput(game::PlayerInputType type, bool enable);
|
||||
@ -53,14 +54,16 @@ private:
|
||||
bool ProcessMenuInput(game::PlayerInputType in);
|
||||
RemoteMenuView* FindMenu(net::MenuId id) const;
|
||||
|
||||
void DrawCrosshair(gui::Context& gui) const;
|
||||
|
||||
private:
|
||||
App& app_;
|
||||
|
||||
std::unique_ptr<WorldView> world_;
|
||||
|
||||
float yaw_ = 0.0f, pitch_ = 0.0f;
|
||||
net::EntNum follow_ent_ = 0;
|
||||
|
||||
CameraController camera_controller_;
|
||||
CameraInfo camera_info_;
|
||||
|
||||
net::ViewYawQ view_yaw_q_;
|
||||
net::ViewPitchQ view_pitch_q_;
|
||||
float last_send_time_ = 0.0f;
|
||||
@ -68,6 +71,8 @@ private:
|
||||
gui::UseTargetHud use_target_hud_;
|
||||
|
||||
std::vector<std::unique_ptr<RemoteMenuView>> remote_menus_;
|
||||
|
||||
std::shared_ptr<const gfx::Texture> crosshair_texture_;
|
||||
};
|
||||
|
||||
} // namespace game::view
|
||||
@ -8,6 +8,7 @@
|
||||
#include "markerview.hpp"
|
||||
#include "client_session.hpp"
|
||||
#include "draw_args.hpp"
|
||||
#include "net/utils.hpp"
|
||||
|
||||
game::view::WorldView::WorldView(ClientSession& session, net::InMessage& msg) :
|
||||
session_(session), audiomaster_(session_.GetAudioMaster())
|
||||
@ -69,6 +70,9 @@ bool game::view::WorldView::ProcessMsg(net::MessageType type, net::InMessage& ms
|
||||
case net::MSG_OBJRESPAWN:
|
||||
return ProcessObjDestroyOrRespawnMsg(msg, true);
|
||||
|
||||
case net::MSG_BEAM:
|
||||
return ProcessBeamMsg(msg);
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@ -87,6 +91,7 @@ void game::view::WorldView::Update(const UpdateInfo& info)
|
||||
}
|
||||
|
||||
UpdateEnv();
|
||||
UpdateBeams();
|
||||
}
|
||||
|
||||
void game::view::WorldView::Draw(const DrawArgs& args) const
|
||||
@ -106,31 +111,8 @@ void game::view::WorldView::Draw(const DrawArgs& args) const
|
||||
if (args.frustum.IsSphereVisible(ent->GetBoundingSphere()))
|
||||
ent->Draw(args);
|
||||
}
|
||||
}
|
||||
|
||||
glm::vec3 game::view::WorldView::CameraSweep(const glm::vec3& start, const glm::vec3& end)
|
||||
{
|
||||
const auto& bt_world = GetBtWorld();
|
||||
|
||||
static const btSphereShape shape(0.1f);
|
||||
|
||||
btVector3 bt_start(start.x, start.y, start.z);
|
||||
btVector3 bt_end(end.x, end.y, end.z);
|
||||
|
||||
btTransform from, to;
|
||||
from.setIdentity();
|
||||
from.setOrigin(bt_start);
|
||||
to.setIdentity();
|
||||
to.setOrigin(bt_end);
|
||||
|
||||
btCollisionWorld::ClosestConvexResultCallback cb(bt_start, bt_end);
|
||||
|
||||
bt_world.convexSweepTest(&shape, from, to, cb);
|
||||
|
||||
if (!cb.hasHit())
|
||||
return end;
|
||||
|
||||
return glm::mix(start, end, cb.m_closestHitFraction);
|
||||
DrawBeams(args);
|
||||
}
|
||||
|
||||
game::view::EntityView* game::view::WorldView::GetEntity(net::EntNum entnum)
|
||||
@ -333,7 +315,36 @@ bool game::view::WorldView::ProcessObjDestroyOrRespawnMsg(net::InMessage& msg, b
|
||||
return true;
|
||||
}
|
||||
|
||||
bool game::view::WorldView::ProcessBeamMsg(net::InMessage& msg)
|
||||
{
|
||||
BeamView beam;
|
||||
if (!net::ReadPosition(msg, beam.start) || !net::ReadPosition(msg, beam.end) || !net::ReadRGB(msg, beam.color) || !msg.Read<net::BeamTimeQ>(beam.expiration))
|
||||
return false;
|
||||
|
||||
beam.expiration += GetTime();
|
||||
beam.width = 0.02f;
|
||||
|
||||
beams_.emplace_back(beam);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void game::view::WorldView::Cache(std::any val)
|
||||
{
|
||||
cache_.emplace_back(std::move(val));
|
||||
}
|
||||
|
||||
void game::view::WorldView::UpdateBeams()
|
||||
{
|
||||
beams_.erase(std::remove_if(beams_.begin(), beams_.end(),
|
||||
[this](const BeamView& beam) { return beam.expiration <= GetTime(); }),
|
||||
beams_.end());
|
||||
}
|
||||
|
||||
void game::view::WorldView::DrawBeams(const DrawArgs& args) const
|
||||
{
|
||||
for (const auto& beam : beams_)
|
||||
{
|
||||
args.dlist.AddBeam(beam.start, beam.end, beam.color | 0xFF000000, beam.width);
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,6 +16,15 @@ namespace game::view
|
||||
|
||||
class ClientSession;
|
||||
|
||||
struct BeamView
|
||||
{
|
||||
float expiration;
|
||||
glm::vec3 start;
|
||||
glm::vec3 end;
|
||||
float width;
|
||||
uint32_t color;
|
||||
};
|
||||
|
||||
class WorldView : public collision::DynamicsWorld
|
||||
{
|
||||
public:
|
||||
@ -26,8 +35,6 @@ public:
|
||||
void Update(const UpdateInfo& info);
|
||||
void Draw(const DrawArgs& args) const;
|
||||
|
||||
glm::vec3 CameraSweep(const glm::vec3& start, const glm::vec3& end);
|
||||
|
||||
EntityView* GetEntity(net::EntNum entnum);
|
||||
|
||||
float GetTime() const { return time_; }
|
||||
@ -45,11 +52,14 @@ private:
|
||||
bool ProcessEntMsgMsg(net::InMessage& msg);
|
||||
bool ProcessUpdateEntsMsg(net::InMessage& msg);
|
||||
bool ProcessEntDestroyMsg(net::InMessage& msg);
|
||||
|
||||
bool ProcessObjDestroyOrRespawnMsg(net::InMessage& msg, bool enable);
|
||||
bool ProcessBeamMsg(net::InMessage& msg);
|
||||
|
||||
void Cache(std::any val);
|
||||
|
||||
void UpdateBeams();
|
||||
void DrawBeams(const DrawArgs& args) const;
|
||||
|
||||
private:
|
||||
ClientSession& session_;
|
||||
|
||||
@ -66,6 +76,8 @@ private:
|
||||
audio::Master& audiomaster_;
|
||||
|
||||
std::vector<std::any> cache_;
|
||||
|
||||
std::vector<BeamView> beams_;
|
||||
};
|
||||
|
||||
} // namespace game::view
|
||||
@ -19,9 +19,9 @@ void gui::Context::Begin(const glm::vec2& viewport_size)
|
||||
viewport_size_ = viewport_size;
|
||||
}
|
||||
|
||||
void gui::Context::DrawRect(const glm::vec2& p0, const glm::vec2& p1, uint32_t color)
|
||||
void gui::Context::DrawRect(const glm::vec2& p0, const glm::vec2& p1, uint32_t color, const gfx::Texture* texture)
|
||||
{
|
||||
BeginTexture(white_tex_.get());
|
||||
BeginTexture(texture ? texture : white_tex_.get());
|
||||
PushRect(p0, glm::vec2(0.0f), p1, glm::vec2(1.0f), color);
|
||||
}
|
||||
|
||||
|
||||
@ -34,7 +34,7 @@ public:
|
||||
|
||||
void Begin(const glm::vec2& viewport_size);
|
||||
|
||||
void DrawRect(const glm::vec2& p0, const glm::vec2& p1, uint32_t color);
|
||||
void DrawRect(const glm::vec2& p0, const glm::vec2& p1, uint32_t color, const gfx::Texture* texture = nullptr);
|
||||
|
||||
glm::vec2 MeasureText(std::string_view text);
|
||||
void DrawText(std::string_view text, const glm::vec2& pos, uint32_t color = 0xFFFFFFFF, float scale = 1.0f);
|
||||
|
||||
@ -62,6 +62,9 @@ enum MessageType : uint8_t
|
||||
// OBJRESPAWN <ObjNum>
|
||||
MSG_OBJRESPAWN,
|
||||
|
||||
/*~~~~~~~~ Effects ~~~~~~~~*/
|
||||
// BEAM <Position start> <Position end> <Color> <BeamTime>
|
||||
MSG_BEAM,
|
||||
|
||||
/*~~~~~~~~~~~~~~~~*/
|
||||
MSG_COUNT,
|
||||
@ -108,6 +111,7 @@ enum EntMsgType : uint8_t
|
||||
EMSG_PLAYSOUND,
|
||||
EMSG_DEFORM,
|
||||
EMSG_TUNING,
|
||||
EMSG_EQUIP,
|
||||
};
|
||||
|
||||
using PositionElemQ = Quantized<uint32_t, -10000, 10000, 1>;
|
||||
@ -193,4 +197,6 @@ enum MenuActionType
|
||||
|
||||
using MenuSelectDir = uint8_t; // 0=left, 1=right
|
||||
|
||||
using BeamTimeQ = Quantized<uint8_t, 0, 10>;
|
||||
|
||||
} // namespace net
|
||||
@ -20,3 +20,40 @@ void net::MsgProducer::DiscardMsg()
|
||||
{
|
||||
message_buf_.resize(msg_start_);
|
||||
}
|
||||
|
||||
net::OutMessage net::LocalMsgProducer::BeginLocalMsg(const glm::vec3& position, float radius, MessageType type)
|
||||
{
|
||||
LocalMsgInfo& local_msg = local_msgs_.emplace_back();
|
||||
local_msg.start = local_msg_buf_.size();
|
||||
local_msg.position = position;
|
||||
local_msg.radius = radius;
|
||||
|
||||
OutMessage msg(local_msg_buf_);
|
||||
if (type != net::MSG_NONE)
|
||||
msg.Write(type);
|
||||
return msg;
|
||||
}
|
||||
|
||||
void net::LocalMsgProducer::PickLocalMsgs(MsgProducer& target, const glm::vec3& target_pos)
|
||||
{
|
||||
for (size_t i = 0; i < local_msgs_.size(); ++i)
|
||||
{
|
||||
const auto& local_msg = local_msgs_[i];
|
||||
|
||||
auto d = local_msg.position - target_pos;
|
||||
if (glm::dot(d, d) > (local_msg.radius * local_msg.radius))
|
||||
continue;
|
||||
|
||||
auto start = local_msgs_[i].start;
|
||||
auto end = (i + 1) < local_msgs_.size() ? local_msgs_[i + 1].start : local_msg_buf_.size();
|
||||
|
||||
auto msg = target.BeginMsg();
|
||||
msg.Write(std::span<char>(&local_msg_buf_[start], end - start));
|
||||
}
|
||||
}
|
||||
|
||||
void net::LocalMsgProducer::ResetLocalMsgs()
|
||||
{
|
||||
local_msgs_.clear();
|
||||
local_msg_buf_.clear();
|
||||
}
|
||||
|
||||
@ -5,6 +5,8 @@
|
||||
#include "defs.hpp"
|
||||
#include "outmessage.hpp"
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
namespace net
|
||||
{
|
||||
|
||||
@ -23,4 +25,26 @@ private:
|
||||
size_t msg_start_ = 0;
|
||||
};
|
||||
|
||||
struct LocalMsgInfo
|
||||
{
|
||||
size_t start;
|
||||
glm::vec3 position;
|
||||
float radius;
|
||||
};
|
||||
|
||||
class LocalMsgProducer
|
||||
{
|
||||
public:
|
||||
LocalMsgProducer() = default;
|
||||
|
||||
OutMessage BeginLocalMsg(const glm::vec3& position, float radius, MessageType type = MSG_NONE);
|
||||
void PickLocalMsgs(MsgProducer& target, const glm::vec3& target_pos);
|
||||
void ResetLocalMsgs();
|
||||
|
||||
private:
|
||||
std::vector<LocalMsgInfo> local_msgs_;
|
||||
std::vector<char> local_msg_buf_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@ -10,12 +10,12 @@ namespace net
|
||||
|
||||
/// TRANSFORMS
|
||||
|
||||
// inline void WritePosition(OutMessage& msg, const glm::vec3& pos)
|
||||
// {
|
||||
// msg.Write<PositionQ>(pos.x);
|
||||
// msg.Write<PositionQ>(pos.y);
|
||||
// msg.Write<PositionQ>(pos.z);
|
||||
// }
|
||||
inline void WritePosition(OutMessage& msg, const glm::vec3& pos)
|
||||
{
|
||||
msg.Write<PositionElemQ>(pos.x);
|
||||
msg.Write<PositionElemQ>(pos.y);
|
||||
msg.Write<PositionElemQ>(pos.z);
|
||||
}
|
||||
|
||||
inline void EncodePosition(const glm::vec3& pos, PositionQ& out)
|
||||
{
|
||||
@ -66,10 +66,10 @@ inline void WriteRotationQ(OutMessage& msg, const QuatQ& rotq)
|
||||
// WriteRotation(msg, trans.rotation);
|
||||
// }
|
||||
|
||||
// inline bool ReadPosition(InMessage& msg, glm::vec3& pos)
|
||||
// {
|
||||
// return msg.Read<PositionQ>(pos.x) && msg.Read<PositionQ>(pos.y) && msg.Read<PositionQ>(pos.z);
|
||||
// }
|
||||
inline bool ReadPosition(InMessage& msg, glm::vec3& pos)
|
||||
{
|
||||
return msg.Read<PositionElemQ>(pos.x) && msg.Read<PositionElemQ>(pos.y) && msg.Read<PositionElemQ>(pos.z);
|
||||
}
|
||||
|
||||
inline bool ReadPositionQ(InMessage& msg, PositionQ& posq)
|
||||
{
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user