diff --git a/CMakeLists.txt b/CMakeLists.txt index 2995284..3ba95e1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,8 +18,6 @@ set(COMMON_SOURCES "src/assets/model.cpp" "src/assets/skeleton.hpp" "src/assets/skeleton.cpp" - "src/assets/vehicle_tuning_list.hpp" - "src/assets/vehicle_tuning_list.cpp" "src/assets/vehiclemdl.hpp" "src/assets/vehiclemdl.cpp" "src/collision/dynamicsworld.hpp" @@ -157,6 +155,8 @@ set(SERVER_ONLY_SOURCES "src/game/tuning_world.hpp" "src/game/tuning_world.cpp" "src/game/usable.hpp" + "src/game/vehicle_tuning.hpp" + "src/game/vehicle_tuning.cpp" "src/game/vehicle.hpp" "src/game/vehicle.cpp" "src/game/world.hpp" diff --git a/src/assets/cmdfile.cpp b/src/assets/cmdfile.cpp index c6f1b03..7909e0c 100644 --- a/src/assets/cmdfile.cpp +++ b/src/assets/cmdfile.cpp @@ -8,13 +8,13 @@ void assets::LoadCMDStream(std::istream& is, CmdCallback handler) return; while (std::getline(is, line)) - { + { + // skip whitespace + line.erase(0, line.find_first_not_of(" \t\r\n")); + if (line.empty() || line[0] == '#') // Skip empty lines and comments continue; - // skip whitespace - line.erase(0, line.find_first_not_of(" \t")); - std::istringstream iss(line); iss >> command; diff --git a/src/assets/vehicle_tuning_list.cpp b/src/assets/vehicle_tuning_list.cpp deleted file mode 100644 index fc4c485..0000000 --- a/src/assets/vehicle_tuning_list.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include "vehicle_tuning_list.hpp" - -#include "cmdfile.hpp" - -std::unique_ptr assets::VehicleTuningList::LoadFromFile(const std::string& filename) -{ - auto tuninglist = std::make_unique(); - - if (!fs::FileExists(filename)) - return tuninglist; // empty - - LoadCMDFile(filename, [&](const std::string& command, std::istringstream& iss) { - if (command == "wheel") - { - VehicleWheelPreset wheel{}; - wheel.displayname = ParseString(iss); - iss >> wheel.price >> wheel.model; - - tuninglist->wheels.emplace_back(wheel); - } - }); - - return tuninglist; -} \ No newline at end of file diff --git a/src/assets/vehicle_tuning_list.hpp b/src/assets/vehicle_tuning_list.hpp deleted file mode 100644 index 0be00ca..0000000 --- a/src/assets/vehicle_tuning_list.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -namespace assets -{ - -struct VehicleWheelPreset -{ - uint32_t price; - std::string displayname; - std::string model; - -}; - -struct VehicleTuningList -{ - std::vector wheels; - - static std::unique_ptr LoadFromFile(const std::string& filename); -}; - - -} \ No newline at end of file diff --git a/src/assets/vehiclemdl.cpp b/src/assets/vehiclemdl.cpp index a9dce93..3b9e12b 100644 --- a/src/assets/vehiclemdl.cpp +++ b/src/assets/vehiclemdl.cpp @@ -48,9 +48,6 @@ std::shared_ptr assets::VehicleModel::LoadFromFile(c } }); - // tuning list - veh->tuninglist_ = VehicleTuningList::LoadFromFile(filename + ".tun"); - return veh; } diff --git a/src/assets/vehiclemdl.hpp b/src/assets/vehiclemdl.hpp index b39e93a..c402278 100644 --- a/src/assets/vehiclemdl.hpp +++ b/src/assets/vehiclemdl.hpp @@ -2,7 +2,6 @@ #include "model.hpp" #include "utils/transform.hpp" -#include "vehicle_tuning_list.hpp" namespace assets { @@ -36,13 +35,11 @@ public: const std::shared_ptr& GetModel() const { return basemodel_; } const std::vector& GetWheels() const { return wheels_; } const Transform* GetLocation(const std::string& name) const; - const VehicleTuningList& GetTuningList() const { return *tuninglist_; } private: std::shared_ptr basemodel_; std::vector wheels_; std::map locations_; - std::unique_ptr tuninglist_; }; diff --git a/src/game/drivable_vehicle.cpp b/src/game/drivable_vehicle.cpp index 9540f73..a8917b0 100644 --- a/src/game/drivable_vehicle.cpp +++ b/src/game/drivable_vehicle.cpp @@ -5,9 +5,13 @@ game::DrivableVehicle::DrivableVehicle(World& world, const VehicleTuning& tuning) : Vehicle(world, tuning), Usable(GetRoot().matrix) { InitSeats(); + OnPhysicsChanged(); +} +void game::DrivableVehicle::OnPhysicsChanged() +{ // make body usable - collision::AddObjectFlags(&GetBtBody(), collision::OF_USABLE); + collision::AddObjectFlags(&GetPhysics()->GetBtBody(), collision::OF_USABLE); } bool game::DrivableVehicle::QueryUseTarget(PlayerCharacter& character, uint32_t target_id, UseTargetQueryResult& res) @@ -90,7 +94,7 @@ static std::string GetColorTextPrefix(uint32_t color) void game::DrivableVehicle::InitSeats() { - uint32_t color = GetTuning().primary_color; + uint32_t color = GetTuningResult().colors[0]; std::string prefix = "vlízt do " + GetColorTextPrefix(color) + GetModelName() + "^r"; const auto& veh = *GetModel(); diff --git a/src/game/drivable_vehicle.hpp b/src/game/drivable_vehicle.hpp index 55775ac..3d66569 100644 --- a/src/game/drivable_vehicle.hpp +++ b/src/game/drivable_vehicle.hpp @@ -20,6 +20,8 @@ public: DrivableVehicle(World& world, const VehicleTuning& tuning); + virtual void OnPhysicsChanged() override; + virtual bool QueryUseTarget(PlayerCharacter& character, uint32_t target_id, UseTargetQueryResult& res) override; virtual void Use(PlayerCharacter& character, uint32_t target_id) override; diff --git a/src/game/npc_character.cpp b/src/game/npc_character.cpp index 795935b..7400c63 100644 --- a/src/game/npc_character.cpp +++ b/src/game/npc_character.cpp @@ -21,7 +21,7 @@ void game::NpcCharacter::VehicleChanged() { roads_ = world_.GetMap().GetGraph("roads"); - seg_start_ = GetVehicle()->GetPosition(); + seg_start_ = GetVehicle()->GetRootTransform().position; size_t start_node = 0; float min_dist = std::numeric_limits().infinity(); @@ -87,8 +87,10 @@ void game::NpcCharacter::VehicleThink() if (!IsDriver() || !GetVehicle() || !roads_) return; - glm::vec3 pos = GetVehicle()->GetPosition(); - glm::quat rot = GetVehicle()->GetRotation(); + const auto& vehicle_trans = GetVehicle()->GetRootTransform(); + + const glm::vec3& pos = vehicle_trans.position; + const glm::quat& rot = vehicle_trans.rotation; glm::vec3 forward = rot * glm::vec3{0.0f, 1.0f, 0.0f}; // glm::vec3 target = s->roads.nodes[s->node].position; diff --git a/src/game/openworld.cpp b/src/game/openworld.cpp index 61ba342..70f7bbb 100644 --- a/src/game/openworld.cpp +++ b/src/game/openworld.cpp @@ -56,9 +56,10 @@ game::OpenWorld::OpenWorld() : EnterableWorld("openworld") // initial twingo VehicleTuning twingo_tuning; twingo_tuning.model = "twingo"; - twingo_tuning.primary_color = 0x0077FF; - twingo_tuning.wheels_idx = 1; // enkei - twingo_tuning.wheel_color = 0x00FF00; + twingo_tuning.parts["primarycolor"] = "orange"; + // twingo_tuning.primary_color = 0x0077FF; + // twingo_tuning.wheels_idx = 1; // enkei + // twingo_tuning.wheel_color = 0x00FF00; auto& veh = Spawn(twingo_tuning); veh.SetPosition({110.0f, 100.0f, 5.0f}); @@ -83,11 +84,23 @@ game::DrivableVehicle& game::OpenWorld::SpawnRandomVehicle() { game::VehicleTuning tuning; tuning.model = GetRandomCarModel(); - tuning.primary_color = GetRandomColor24(); + // tuning.primary_color = GetRandomColor24(); auto& vehicle = Spawn(tuning); // vehicle.SetNametag("bot (" + std::to_string(vehicle.GetEntNum()) + ")"); + auto& tuning_list = vehicle.GetTuningList(); + auto& colors = tuning_list->groups[0].parts; + + size_t random_color = rand() % colors.size(); + + auto item = colors.begin(); + std::advance( item, random_color); + + tuning.parts["primarycolor"] = item->second.id; + + vehicle.SetTuning(tuning); + return vehicle; } diff --git a/src/game/tuning_world.cpp b/src/game/tuning_world.cpp index 796ee60..ba4a370 100644 --- a/src/game/tuning_world.cpp +++ b/src/game/tuning_world.cpp @@ -1,7 +1,6 @@ #include "tuning_world.hpp" #include "player_character.hpp" #include "utils/colors.hpp" -#include "assets/vehicle_tuning_list.hpp" #include "game.hpp" game::TuningWorld::TuningWorld(Game& game, EnterableWorld& exit_world, const glm::vec3& exit_pos, float exit_yaw, std::string mapname) : @@ -32,7 +31,7 @@ void game::TuningWorld::OnVehicleJoined(DrivableVehicle& vehicle) throw std::runtime_error("vehicle joined to tuning without ACTIVE player driver??"); vehicle_ = &vehicle; - tuning_list_ = &vehicle.GetModel()->GetTuningList(); + tuning_list_ = vehicle.GetTuningList().get(); player_ = player; Setup(); @@ -54,73 +53,133 @@ const std::string& game::TuningWorld::GetOccupantName() const void game::TuningWorld::Setup() { tuning_ = vehicle_->GetTuning(); - UpdateTuningVals(); + // UpdateTuningVals(); DisplayTuningMenu(); } -void game::TuningWorld::UpdateTuningVals() -{ - tun_primary_color_ = ColorU32ToU8Vec3(tuning_.primary_color); +// void game::TuningWorld::UpdateTuningVals() +// { +// tun_primary_color_ = ColorU32ToU8Vec3(tuning_.primary_color); - tun_wheel_idx_ = tuning_.wheels_idx; - tun_wheel_color_ = ColorU32ToU8Vec3(tuning_.wheel_color); -} +// tun_wheel_idx_ = tuning_.wheels_idx; +// tun_wheel_color_ = ColorU32ToU8Vec3(tuning_.wheel_color); +// } -void game::TuningWorld::UpdateTuning() -{ - tuning_.primary_color = ColorU8Vec3ToU32(tun_primary_color_); +// void game::TuningWorld::UpdateTuning() +// { +// tuning_.primary_color = ColorU8Vec3ToU32(tun_primary_color_); - tuning_.wheels_idx = tun_wheel_idx_; - tuning_.wheel_color = ColorU8Vec3ToU32(tun_wheel_color_); +// tuning_.wheels_idx = tun_wheel_idx_; +// tuning_.wheel_color = ColorU8Vec3ToU32(tun_wheel_color_); - vehicle_->SetTuning(tuning_); -} +// vehicle_->SetTuning(tuning_); +// } -static void AddColorChannelSlider(game::RemoteMenu& menu, uint8_t& ch, std::string name, std::function on_change) -{ - auto& slider = menu.AddItem(game::RM_SELECT, std::move(name)); +// static void AddColorChannelSlider(game::RemoteMenu& menu, uint8_t& ch, std::string name, std::function on_change) +// { +// auto& slider = menu.AddItem(game::RM_SELECT, std::move(name)); - auto on_select = [&slider, &ch, on_change = std::move(on_change)] (int dir) { - int new_val = ch + dir * 5; - if (new_val < 0) - ch = 0; - else if (new_val > 255) - ch = 255; - else - ch = new_val; +// auto on_select = [&slider, &ch, on_change = std::move(on_change)] (int dir) { +// int new_val = ch + dir * 5; +// if (new_val < 0) +// ch = 0; +// else if (new_val > 255) +// ch = 255; +// else +// ch = new_val; - slider.SetSelection(std::to_string(ch)); - on_change(); +// slider.SetSelection(std::to_string(ch)); +// on_change(); +// }; + +// on_select(0); +// slider.SetOnSelect(on_select); +// } + +// static void AddColorSliders(game::RemoteMenu& menu, glm::u8vec3& color, std::string name, std::function on_change) +// { +// AddColorChannelSlider(menu, color.r, name + " ^f00R", on_change); +// AddColorChannelSlider(menu, color.g, name + " ^0f0G", on_change); +// AddColorChannelSlider(menu, color.b, name + " ^00fB", on_change); +// } + +// static void AddWheelTypeSlider(game::RemoteMenu& menu, const assets::VehicleTuningList& tuning_list, size_t& idx, std::function on_change) +// { +// auto& slider = menu.AddItem(game::RM_SELECT, "kola"); + +// auto on_select = [&slider, &idx, &tuning_list, on_change = std::move(on_change)] (int dir) { +// auto& wheels = tuning_list.wheels; + +// if (dir < 0 && idx == 0) +// return; + +// if (dir > 0 && (idx + 1) >= wheels.size()) +// return; + +// idx += dir; + +// slider.SetSelection(tuning_list.wheels[idx].displayname); +// on_change(); +// }; + +// on_select(0); +// slider.SetOnSelect(on_select); +// } + + +void game::TuningWorld::AddTuningGroupSelect(game::RemoteMenu& menu, const VehicleTuningGroup& group) +{ + size_t num_parts = group.parts.size(); + if (num_parts == 0) + return; + + struct SliderState + { + const VehicleTuningGroup* group; + int idx = 0; + std::vector ids; }; - - on_select(0); - slider.SetOnSelect(on_select); -} -static void AddColorSliders(game::RemoteMenu& menu, glm::u8vec3& color, std::string name, std::function on_change) -{ - AddColorChannelSlider(menu, color.r, name + " ^f00R", on_change); - AddColorChannelSlider(menu, color.g, name + " ^0f0G", on_change); - AddColorChannelSlider(menu, color.b, name + " ^00fB", on_change); -} + std::string current_part; -static void AddWheelTypeSlider(game::RemoteMenu& menu, const assets::VehicleTuningList& tuning_list, size_t& idx, std::function on_change) -{ - auto& slider = menu.AddItem(game::RM_SELECT, "kola"); - - auto on_select = [&slider, &idx, &tuning_list, on_change = std::move(on_change)] (int dir) { - auto& wheels = tuning_list.wheels; + auto current_it = tuning_.parts.find(group.id); + if (current_it != tuning_.parts.end()) + { + current_part = current_it->second; + } - if (dir < 0 && idx == 0) - return; + auto state = std::make_shared(); + state->group = &group; - if (dir > 0 && (idx + 1) >= wheels.size()) - return; + state->ids.reserve(num_parts); + size_t i = 0; + for (const auto& part : group.parts) + { + state->ids.push_back(part.first); + + if (part.first == current_part) + state->idx = i; + + ++i; + } - idx += dir; + auto& slider = menu.AddItem(game::RM_SELECT, group.displayname); + + auto on_select = [&slider, this, state](int dir) mutable { + if (dir < 0 && state->idx == 0) + state->idx = state->ids.size() - 1; + else if (dir > 0 && (state->idx + 1) >= state->ids.size()) + state->idx = 0; + else + state->idx += dir; + + auto& part_id = state->ids[state->idx]; + tuning_.parts[state->group->id] = part_id; + slider.SetSelection(state->group->parts.at(part_id).displayname); + + if (dir != 0) + vehicle_->SetTuning(tuning_); - slider.SetSelection(tuning_list.wheels[idx].displayname); - on_change(); }; on_select(0); @@ -133,14 +192,19 @@ void game::TuningWorld::DisplayTuningMenu() auto& menu = player_->DisplayMenu("tuning"); menu_ = &menu; - auto on_change = [this] { UpdateTuning(); }; + // auto on_change = [this] { UpdateTuning(); }; - AddColorSliders(menu, tun_primary_color_, "primární", on_change); + // AddColorSliders(menu, tun_primary_color_, "primární", on_change); - if (!tuning_list_->wheels.empty()) + // if (!tuning_list_->wheels.empty()) + // { + // AddWheelTypeSlider(menu, *tuning_list_, tun_wheel_idx_, on_change); + // AddColorSliders(menu, tun_wheel_color_, "kola", on_change); + // } + + for (const auto& group : tuning_list_->groups) { - AddWheelTypeSlider(menu, *tuning_list_, tun_wheel_idx_, on_change); - AddColorSliders(menu, tun_wheel_color_, "kola", on_change); + AddTuningGroupSelect(menu, group); } auto& exit_btn = menu.AddItem(RM_BUTTON, "vylézt"); diff --git a/src/game/tuning_world.hpp b/src/game/tuning_world.hpp index e67cdb3..b7c1767 100644 --- a/src/game/tuning_world.hpp +++ b/src/game/tuning_world.hpp @@ -26,9 +26,7 @@ public: private: void Setup(); - void UpdateTuningVals(); - void UpdateTuning(); - + void AddTuningGroupSelect(game::RemoteMenu& menu, const VehicleTuningGroup& group); void DisplayTuningMenu(); void Reset(); @@ -42,17 +40,13 @@ private: // active player Player* player_ = nullptr; DrivableVehicle* vehicle_ = nullptr; - const assets::VehicleTuningList* tuning_list_ = nullptr; + const VehicleTuningList* tuning_list_ = nullptr; game::RemoteMenu* menu_ = nullptr; VehicleTuning tuning_; - glm::u8vec3 tun_primary_color_; - size_t tun_wheel_idx_; - glm::u8vec3 tun_wheel_color_; - }; } \ No newline at end of file diff --git a/src/game/vehicle.cpp b/src/game/vehicle.cpp index 1fe6a25..80b7f6d 100644 --- a/src/game/vehicle.cpp +++ b/src/game/vehicle.cpp @@ -15,87 +15,13 @@ static std::shared_ptr LoadVehicleModelByName(const game::Vehicle::Vehicle(World& world, const VehicleTuning& tuning) : Entity(world, net::ET_VEHICLE), tuning_(tuning), model_(LoadVehicleModelByName(tuning.model)), - motion_(root_.local) + tuninglist_(VehicleTuningList::LoadFromFile("data/" + tuning.model + ".tun")) { root_.local.position.z = 10.0f; - // setup chassis rigidbody - float mass = 1000.0f; + wheels_.resize(model_->GetWheels().size()); - btCollisionShape* shape = model_->GetModel()->GetColShape(); - if (!shape) - throw std::runtime_error("Making vehicle with no shape"); - - btVector3 local_inertia(0, 0, 0); - shape->calculateLocalInertia(mass, local_inertia); - - btRigidBody::btRigidBodyConstructionInfo rb_info(mass, &motion_, shape, local_inertia); - body_ = std::make_unique(rb_info); - // body_->setActivationState(DISABLE_DEACTIVATION); - - collision::SetObjectInfo(body_.get(), collision::OT_ENTITY, collision::OF_NOTIFY_CONTACT, this); - - // setup vehicle - btRaycastVehicle::btVehicleTuning bt_tuning; - vehicle_ = std::make_unique(bt_tuning, body_.get(), &world_.GetVehicleRaycaster()); - vehicle_->setCoordinateSystem(0, 2, 1); - - // setup wheels - // btVector3 wheelDirectionCS0(0, -1, 0); - // btVector3 wheelAxleCS(-1, 0, 0); - btVector3 wheelDirectionCS0(0, 0, -1); - btVector3 wheelAxleCS(1, 0, 0); - - wheel_z_offset_ = 0.2f; - - const auto& wheels = model_->GetWheels(); - - if (wheels.size() > MAX_WHEELS) - throw std::runtime_error("Max wheels exceeded"); - - num_wheels_ = wheels.size(); - - for (const auto& wheeldef : wheels) - { - float wheelRadius = wheeldef.radius; - - float friction = 2.2f; // 5.0f; - float suspensionStiffness = 50.0f; - // float suspensionDamping = 2.3f; - // float suspensionCompression = 4.4f; - float suspensionRestLength = 0.3f; - float rollInfluence = 0.3f; - - float maxSuspensionForce = 90000.0f; - float maxSuspensionTravelCm = 15.0f; - - float k = 0.2f; - - const bool is_front = !(wheeldef.type & assets::WHEEL_REAR); - - if (!is_front) - friction *= 1.5f; - - btVector3 wheel_pos(wheeldef.position.x, wheeldef.position.y, wheeldef.position.z + wheel_z_offset_); - auto& wi = vehicle_->addWheel(wheel_pos, wheelDirectionCS0, wheelAxleCS, suspensionRestLength, wheelRadius, - bt_tuning, is_front); - - wi.m_suspensionStiffness = suspensionStiffness; - - wi.m_wheelsDampingCompression = k * 2.0 * btSqrt(suspensionStiffness); // vehicleTuning.suspensionCompression; - wi.m_wheelsDampingRelaxation = k * 3.3 * btSqrt(suspensionStiffness); // vehicleTuning.suspensionDamping; - - wi.m_frictionSlip = friction; - // if (wi.m_bIsFrontWheel) wi.m_frictionSlip = vehicleTuning.friction * 1.4f; - - wi.m_rollInfluence = rollInfluence; - wi.m_maxSuspensionForce = maxSuspensionForce; - wi.m_maxSuspensionTravelCm = maxSuspensionTravelCm; - } - - auto& bt_world = world_.GetBtWorld(); - bt_world.addRigidBody(body_.get(), btBroadphaseProxy::DefaultFilter, btBroadphaseProxy::AllFilter); - bt_world.addAction(vehicle_.get()); + ApplyTuning(tuning); // init deform gfx::DeformGridInfo info{}; @@ -130,7 +56,7 @@ void game::Vehicle::SendInitData(Player& player, net::OutMessage& msg) const msg.Write(net::ModelName(tuning_.model)); WriteTuning(msg); - + // write state against default static const VehicleSyncState default_state; size_t fields_pos = msg.Reserve(); @@ -163,7 +89,6 @@ void game::Vehicle::OnContact(const collision::ContactInfo& info) { Deform(info.pos, -glm::normalize(info.normal) * 0.1f, 1.0f); } - } void game::Vehicle::SetInput(VehicleInputType type, bool enable) @@ -174,28 +99,18 @@ void game::Vehicle::SetInput(VehicleInputType type, bool enable) in_ &= ~(1 << type); } -glm::vec3 game::Vehicle::GetPosition() const -{ - btVector3 pos = body_->getWorldTransform().getOrigin(); - return glm::vec3(pos.x(), pos.y(), pos.z()); -} - void game::Vehicle::SetPosition(const glm::vec3& pos) { - auto t = body_->getWorldTransform(); - t.setOrigin(btVector3(pos.x, pos.y, pos.z)); - body_->setWorldTransform(t); -} + auto& body = physics_->GetBtBody(); -glm::quat game::Vehicle::GetRotation() const -{ - btQuaternion rot = body_->getWorldTransform().getRotation(); - return glm::quat(rot.w(), rot.x(), rot.y(), rot.z()); + auto t = body.getWorldTransform(); + t.setOrigin(btVector3(pos.x, pos.y, pos.z)); + body.setWorldTransform(t); } float game::Vehicle::GetSpeed() const { - return vehicle_->getCurrentSpeedKmHour(); + return physics_->GetBtVehicle().getCurrentSpeedKmHour(); } void game::Vehicle::SetSteering(bool analog, float value) @@ -206,54 +121,48 @@ void game::Vehicle::SetSteering(bool analog, float value) void game::Vehicle::SetTuning(const VehicleTuning& tuning) { - tuning_ = tuning; + ApplyTuning(tuning); + // send to clients auto msg = BeginEntMsg(net::EMSG_TUNING); WriteTuning(msg); } -game::Vehicle::~Vehicle() -{ - auto& bt_world = world_.GetBtWorld(); - bt_world.removeRigidBody(body_.get()); - bt_world.removeAction(vehicle_.get()); -} - void game::Vehicle::ProcessInput() { // TODO: totally fix - //std::string nt = ""; + // std::string nt = ""; - if (in_) { - //nt += "in "; - // body_->setActivationState(ACTIVE_TAG); - body_->activate(); + if (in_) + { + // nt += "in "; + // body_->setActivationState(ACTIVE_TAG); + physics_->GetBtBody().activate(); } else { - //nt += "no in"; + // nt += "no in"; } - //nt += std::to_string(body_->getActivationState()); - //SetNametag(nt); + // nt += std::to_string(body_->getActivationState()); + // SetNametag(nt); float t_delta = 1.0f / 25.0f; // float steeringIncrement = .04 * 60; // float steeringClamp = .5; // float maxEngineForce = 7000; - float maxEngineForce = 3200; - float maxBreakingForce = 400; + float maxEngineForce = tuning_ctx_.engine_force; + float maxBreakingForce = tuning_ctx_.braking_force; - float speed = vehicle_->getCurrentSpeedKmHour(); + float speed = GetSpeed(); if (glm::abs(speed) > 200.0f) maxEngineForce = 100.0f; float engineForce = 0; float breakingForce = 0; - float maxsc = .5f; float minsc = .08f; float sl = 130.f; @@ -263,11 +172,10 @@ void game::Vehicle::ProcessInput() float steeringSpeed = steeringClamp * 5.0f; float steeringInc = steeringSpeed * t_delta; - const bool in_forward = in_ & (1 << VIN_FORWARD); const bool in_backward = in_ & (1 << VIN_BACKWARD); const bool in_left = in_ & (1 << VIN_LEFT); - const bool in_right = in_ & (1 << VIN_RIGHT); + const bool in_right = in_ & (1 << VIN_RIGHT); if (in_forward) { @@ -287,7 +195,7 @@ void game::Vehicle::ProcessInput() // idle breaking if (!in_forward && !in_backward) { - breakingForce = maxBreakingForce * 0.05f; + breakingForce = 20.0f; } if (!steering_analog_) @@ -341,16 +249,29 @@ void game::Vehicle::ProcessInput() steering_ = -steeringClamp; } - vehicle_->applyEngineForce(engineForce, 0); - vehicle_->applyEngineForce(engineForce, 1); + auto& vehicle = physics_->GetBtVehicle(); - vehicle_->setBrake(breakingForce * 0.1f, 0); - vehicle_->setBrake(breakingForce * 0.1f, 1); - vehicle_->setBrake(breakingForce, 2); - vehicle_->setBrake(breakingForce, 3); + for (size_t i = 0; i < wheels_.size(); ++i) + { + float engine_factor = tuning_ctx_.wheels[i].engine_factor; + if (engine_factor > 0.0001f) + { + vehicle.applyEngineForce(engine_factor * engineForce, i); + } + + float braking_factor = tuning_ctx_.wheels[i].braking_factor; + if (braking_factor > 0.0001f) + { + vehicle.setBrake(braking_factor * breakingForce, i); + } + + float steering_factor = tuning_ctx_.wheels[i].steering_factor; + if (std::abs(steering_factor) > 0.0001f) + { + vehicle.setSteeringValue(steering_factor * steering_, i); + } + } - vehicle_->setSteeringValue(steering_, 0); - vehicle_->setSteeringValue(steering_, 1); if (glm::abs(engineForce) > 0) flags_ |= VF_ACCELERATING; @@ -363,7 +284,7 @@ void game::Vehicle::UpdateCrash() { if (window_health_ <= 0.0f) flags_ |= VF_BROKENWINDOWS; - + if (no_crash_frames_) { --no_crash_frames_; @@ -394,7 +315,6 @@ void game::Vehicle::UpdateCrash() PlaySound("crash", volume, pitch); no_crash_frames_ = 7 + rand() % 10; } - } crash_intensity_ = 0.0f; @@ -402,12 +322,14 @@ void game::Vehicle::UpdateCrash() void game::Vehicle::UpdateWheels() { - for (size_t i = 0; i < num_wheels_; ++i) + auto& vehicle = physics_->GetBtVehicle(); + + for (size_t i = 0; i < wheels_.size(); ++i) { - auto& bt_wheel = vehicle_->getWheelInfo(i); + auto& bt_wheel = vehicle.getWheelInfo(i); wheels_[i].speed = -(bt_wheel.m_rotation - wheels_[i].rotation) * 25.0f; wheels_[i].rotation = bt_wheel.m_rotation; - wheels_[i].z_offset = wheel_z_offset_ - bt_wheel.m_raycastInfo.m_suspensionLength; + wheels_[i].z_offset = tuning_ctx_.wheels[i].z_offset - bt_wheel.m_raycastInfo.m_suspensionLength; } } @@ -416,13 +338,13 @@ void game::Vehicle::UpdateSyncState() VehicleSyncState& state = sync_[sync_current_]; state.flags = flags_; - + net::EncodePosition(root_.local.position, state.pos); net::EncodeRotation(root_.local.rotation, state.rot); state.steering.Encode(steering_); - for (size_t i = 0; i < num_wheels_; ++i) + for (size_t i = 0; i < wheels_.size(); ++i) { auto& wheel = wheels_[i]; state.wheels[i].z_offset.Encode(wheel.z_offset); @@ -441,8 +363,7 @@ game::VehicleSyncFieldFlags game::Vehicle::WriteState(net::OutMessage& msg, cons msg.Write(curr.flags); } - if (curr.pos.x.value != base.pos.x.value || - curr.pos.y.value != base.pos.y.value || + if (curr.pos.x.value != base.pos.x.value || curr.pos.y.value != base.pos.y.value || curr.pos.z.value != base.pos.z.value) { fields |= VSF_POSITION; @@ -452,8 +373,7 @@ game::VehicleSyncFieldFlags game::Vehicle::WriteState(net::OutMessage& msg, cons net::WriteDelta(msg, curr.pos.z, base.pos.z); } - if (curr.rot.x.value != base.rot.x.value || - curr.rot.y.value != base.rot.y.value || + if (curr.rot.x.value != base.rot.x.value || curr.rot.y.value != base.rot.y.value || curr.rot.z.value != base.rot.z.value) { fields |= VSF_ROTATION; @@ -471,7 +391,7 @@ game::VehicleSyncFieldFlags game::Vehicle::WriteState(net::OutMessage& msg, cons } bool wheels_changed = false; - for (size_t i = 0; i < num_wheels_; ++i) + for (size_t i = 0; i < wheels_.size(); ++i) { if (curr.wheels[i].z_offset.value != base.wheels[i].z_offset.value || curr.wheels[i].speed.value != base.wheels[i].speed.value) @@ -485,7 +405,7 @@ game::VehicleSyncFieldFlags game::Vehicle::WriteState(net::OutMessage& msg, cons { fields |= VSF_WHEELS; - for (size_t i = 0; i < num_wheels_; ++i) + for (size_t i = 0; i < wheels_.size(); ++i) { net::WriteDelta(msg, curr.wheels[i].z_offset, base.wheels[i].z_offset); net::WriteDelta(msg, curr.wheels[i].speed, base.wheels[i].speed); @@ -545,7 +465,7 @@ void game::Vehicle::Deform(const glm::vec3& pos, const glm::vec3& deform, float net::PositionQ deform_q; net::EncodePosition(pos, pos_q); net::EncodePosition(deform, deform_q); - + SendDeformMsg(pos_q, deform_q); // defeorm locally @@ -563,11 +483,147 @@ void game::Vehicle::SendDeformMsg(const net::PositionQ& pos, const net::Position net::WritePositionQ(msg, deform); } +void game::Vehicle::ApplyTuning(const VehicleTuning& tuning) +{ + tuning_ = tuning; + + tuning_ctx_ = VehicleTuningContext{}; + + // setup wheels + const auto& model_wheels = model_->GetWheels(); + tuning_ctx_.wheels.resize(model_wheels.size()); + for (size_t i = 0; i < model_wheels.size(); ++i) + { + tuning_ctx_.wheels[i].front = !(model_wheels[i].type & assets::WHEEL_REAR); + } + + // apply tunning to ctx + for (const auto& func : tuninglist_->default_funcs) + { + func(tuning_ctx_); + } + + for (const auto& group : tuninglist_->groups) + { + auto group_it = tuning_.parts.find(group.id); + if (group_it == tuning_.parts.end()) + continue; + + const auto& part_name = group_it->second; + const auto& part = group.parts.at(part_name); + + for (const auto& func : part.funcs) + { + func(tuning_ctx_); + } + } + + // (re)create physics + physics_.reset(); + physics_ = std::make_unique(world_, root_.local, *this, *model_, tuning_ctx_); + OnPhysicsChanged(); +} + void game::Vehicle::WriteTuning(net::OutMessage& msg) const { - net::WriteRGB(msg, tuning_.primary_color); + // write colors + for (const auto& color : tuning_ctx_.colors) + { + net::WriteRGB(msg, color); + } - // wheels - msg.Write(tuning_.wheels_idx); - net::WriteRGB(msg, tuning_.wheel_color); + // write wheel models + for (const auto& wheel : tuning_ctx_.wheels) + { + msg.Write(net::ModelName(wheel.modelname)); + } +} + +// PHYSICS + +game::VehiclePhysics::VehiclePhysics(collision::DynamicsWorld& world, Transform& transform, + collision::ObjectCallback& obj_cb, const assets::VehicleModel& model, + const VehicleTuningContext& tuning) + : world_(world), motion_(transform) +{ + + // setup chassis rigidbody + btCollisionShape* shape = model.GetModel()->GetColShape(); + if (!shape) + throw std::runtime_error("Making vehicle with no shape"); + + btVector3 local_inertia(0, 0, 0); + shape->calculateLocalInertia(tuning.mass, local_inertia); + + btRigidBody::btRigidBodyConstructionInfo rb_info(tuning.mass, &motion_, shape, local_inertia); + body_ = std::make_unique(rb_info); + // body_->setActivationState(DISABLE_DEACTIVATION); + + collision::SetObjectInfo(body_.get(), collision::OT_ENTITY, collision::OF_NOTIFY_CONTACT, &obj_cb); + + // setup vehicle + btRaycastVehicle::btVehicleTuning bt_tuning; + vehicle_ = std::make_unique(bt_tuning, body_.get(), &world_.GetVehicleRaycaster()); + vehicle_->setCoordinateSystem(0, 2, 1); + + // setup wheels + // btVector3 wheelDirectionCS0(0, -1, 0); + // btVector3 wheelAxleCS(-1, 0, 0); + btVector3 wheelDirectionCS0(0, 0, -1); + btVector3 wheelAxleCS(1, 0, 0); + + const auto& model_wheels = model.GetWheels(); + + size_t num_wheels = model_wheels.size(); + + for (size_t i = 0; i < num_wheels; ++i) + { + const auto& wheel_mdl = model_wheels[i]; + const auto& wheel_tun = tuning.wheels[i]; + + // float wheelRadius = wheel_tun.radius; + + // float friction = wheel_tun.friction + // float suspensionStiffness = 50.0f; + // // float suspensionDamping = 2.3f; + // // float suspensionCompression = 4.4f; + // float suspensionRestLength = 0.3f; + // float rollInfluence = 0.3f; + + // float maxSuspensionForce = 90000.0f; + // float maxSuspensionTravelCm = 15.0f; + + float k = 0.2f; + + // if (!is_front) + // friction *= 1.5f; + + btVector3 wheel_pos(wheel_mdl.position.x, wheel_mdl.position.y, wheel_mdl.position.z + wheel_tun.z_offset); + + auto& wi = vehicle_->addWheel(wheel_pos, wheelDirectionCS0, wheelAxleCS, wheel_tun.suspension_rest_length, wheel_tun.radius, + bt_tuning, wheel_tun.front); + + wi.m_suspensionStiffness = wheel_tun.suspension_stiffness; + + wi.m_wheelsDampingCompression = k * 2.0 * btSqrt(wi.m_suspensionStiffness); // vehicleTuning.suspensionCompression; + wi.m_wheelsDampingRelaxation = k * 3.3 * btSqrt(wi.m_suspensionStiffness); // vehicleTuning.suspensionDamping; + + wi.m_frictionSlip = wheel_tun.friction; + + wi.m_rollInfluence = wheel_tun.roll_influence; + wi.m_maxSuspensionForce = wheel_tun.suspension_max_force; + wi.m_maxSuspensionTravelCm = wheel_tun.suspension_travel * 100.0f; + } + + auto& bt_world = world_.GetBtWorld(); + bt_world.addRigidBody(body_.get(), btBroadphaseProxy::DefaultFilter, btBroadphaseProxy::AllFilter); + bt_world.addAction(vehicle_.get()); + +} + +game::VehiclePhysics::~VehiclePhysics() +{ + auto& bt_world = world_.GetBtWorld(); + bt_world.removeRigidBody(body_.get()); + bt_world.removeAction(vehicle_.get()); } diff --git a/src/game/vehicle.hpp b/src/game/vehicle.hpp index c72a46b..03800ec 100644 --- a/src/game/vehicle.hpp +++ b/src/game/vehicle.hpp @@ -6,11 +6,11 @@ #include "assets/vehiclemdl.hpp" #include "collision/motionstate.hpp" #include "collision/raycastvehicle.hpp" -#include "entity.hpp" -#include "world.hpp" -#include "vehicle_sync.hpp" #include "deform_grid.hpp" +#include "entity.hpp" +#include "vehicle_sync.hpp" #include "vehicle_tuning.hpp" +#include "world.hpp" namespace game { @@ -33,6 +33,26 @@ enum VehicleInputType VIN_HANDBRAKE, }; +class VehiclePhysics +{ +public: + VehiclePhysics(collision::DynamicsWorld& world, Transform& transform, collision::ObjectCallback& obj_cb, + const assets::VehicleModel& model, const VehicleTuningContext& tuning); + + DELETE_COPY_MOVE(VehiclePhysics) + + btRigidBody& GetBtBody() { return *body_; } + collision::RaycastVehicle& GetBtVehicle() { return *vehicle_; } + + ~VehiclePhysics(); + +private: + collision::DynamicsWorld& world_; + collision::MotionState motion_; + std::unique_ptr body_; + std::unique_ptr vehicle_; +}; + class Vehicle : public Entity { public: @@ -48,10 +68,8 @@ public: void SetInput(VehicleInputType type, bool enable); void SetInputs(VehicleInputFlags inputs) { in_ = inputs; } - glm::vec3 GetPosition() const; void SetPosition(const glm::vec3& pos); - glm::quat GetRotation() const; float GetSpeed() const; void SetSteering(bool analog, float value = 0.0f); @@ -60,16 +78,17 @@ public: const std::string& GetModelName() const { return tuning_.model; } const std::shared_ptr& GetModel() const { return model_; } + const VehicleTuning& GetTuning() const { return tuning_; } - - virtual ~Vehicle(); + const std::shared_ptr& GetTuningList() const { return tuninglist_; } + const VehicleTuningContext& GetTuningResult() const { return tuning_ctx_; } private: void ProcessInput(); void UpdateCrash(); void UpdateWheels(); void UpdateSyncState(); - + VehicleSyncFieldFlags WriteState(net::OutMessage& msg, const VehicleSyncState& base) const; void SendUpdateMsg(); @@ -77,26 +96,28 @@ private: void Deform(const glm::vec3& pos, const glm::vec3& deform, float radius); void SendDeformMsg(const net::PositionQ& pos, const net::PositionQ& deform); + void ApplyTuning(const VehicleTuning& tuning); + void WriteTuning(net::OutMessage& msg) const; protected: - btRigidBody& GetBtBody() { return *body_; } + VehiclePhysics* GetPhysics() { return physics_.get(); } + virtual void OnPhysicsChanged() {} private: VehicleTuning tuning_; std::shared_ptr model_; + std::shared_ptr tuninglist_; - collision::MotionState motion_; - std::unique_ptr body_; - std::unique_ptr vehicle_; + VehicleTuningContext tuning_ctx_; + + std::unique_ptr physics_; float steering_ = 0.0f; bool steering_analog_ = false; float target_steering_ = 0.0f; - float wheel_z_offset_ = 0.0f; - size_t num_wheels_ = 0; - std::array wheels_; + std::vector wheels_; VehicleFlags flags_ = VF_NONE; VehicleSyncState sync_[2]; diff --git a/src/game/vehicle_tuning.cpp b/src/game/vehicle_tuning.cpp new file mode 100644 index 0000000..f0bd9d4 --- /dev/null +++ b/src/game/vehicle_tuning.cpp @@ -0,0 +1,218 @@ +#include "vehicle_tuning.hpp" + +#include "assets/cmdfile.hpp" + +#include + +static float game::VehicleTuningContext::* GetCtxVariablePointer(const std::string& name) +{ + if (name == "mass") + return &game::VehicleTuningContext::mass; + if (name == "engine_force") + return &game::VehicleTuningContext::engine_force; + if (name == "braking_force") + return &game::VehicleTuningContext::braking_force; + + throw std::runtime_error("tuning list: invalid variable " + name); +} + +static float game::VehicleWheelTuningContext::* GetWheelVariablePointer(const std::string& name) +{ + if (name == "radius") + return &game::VehicleWheelTuningContext::radius; + if (name == "z_offset") + return &game::VehicleWheelTuningContext::z_offset; + if (name == "friction") + return &game::VehicleWheelTuningContext::friction; + if (name == "suspension_stiffness") + return &game::VehicleWheelTuningContext::suspension_stiffness; + if (name == "suspension_max_force") + return &game::VehicleWheelTuningContext::suspension_max_force; + if (name == "suspension_rest_length") + return &game::VehicleWheelTuningContext::suspension_rest_length; + if (name == "suspension_travel") + return &game::VehicleWheelTuningContext::suspension_travel; + if (name == "roll_influence") + return &game::VehicleWheelTuningContext::roll_influence; + if (name == "steering_factor") + return &game::VehicleWheelTuningContext::steering_factor; + if (name == "braking_factor") + return &game::VehicleWheelTuningContext::braking_factor; + if (name == "engine_factor") + return &game::VehicleWheelTuningContext::engine_factor; + + throw std::runtime_error("tuning list: invalid wheel variable " + name); +} + +static void ApplyOp(float& var, const std::string& op, float value) +{ + if (op == "+=") + var += value; + else if (op == "-=") + var -= value; + else if (op == "*=") + var *= value; + else if (op == "/=") + var /= value; + else + var = value; +} + +static bool CheckWheelCond(const game::VehicleWheelTuningContext& wheel_ctx, const std::string& cond) +{ + if (cond == "front" && !wheel_ctx.front) + return false; + + if (cond == "rear" && wheel_ctx.front) + return false; + + return true; +} + +static uint32_t ParseColor(uint32_t c) +{ + auto r = (c >> 16) & 0xFF; + auto g = (c >> 8) & 0xFF; + auto b = c & 0xFF; + return 0xFF000000 | (b << 16) | (g << 8) | r; +} + +static game::VehicleTuningFunction ParseTuningFunction(std::istringstream& iss) +{ + std::string func_name; + iss >> func_name; + + if (func_name == "set") + { + std::string var_name, op; + float value; + iss >> var_name >> op >> value; + + auto var_ptr = GetCtxVariablePointer(var_name); + + return [var_ptr, op, value](game::VehicleTuningContext& ctx) { + ApplyOp(ctx.*var_ptr, op, value); + }; + } + else if (func_name == "setcolor") + { + size_t color_idx; + uint32_t color; + iss >> color_idx >> std::hex >> color >> std::dec; + + if (color_idx >= 4) + throw std::runtime_error("tuning list: invalid color index"); + + color = ParseColor(color); + + return [color_idx, color](game::VehicleTuningContext& ctx) { + ctx.colors[color_idx] = color; + }; + } + else if (func_name == "setwheel") + { + std::string wheel_cond, var_name, op; + float value; + + iss >> wheel_cond >> var_name >> op >> value; + + auto var_ptr = GetWheelVariablePointer(var_name); + + return [wheel_cond, var_ptr, op, value](game::VehicleTuningContext& ctx) { + for (auto& wheel : ctx.wheels) + { + if (CheckWheelCond(wheel, wheel_cond)) + ApplyOp(wheel.*var_ptr, op, value); + } + }; + } + else if (func_name == "setwheelmodel") + { + std::string wheel_cond, modelname; + iss >> wheel_cond >> modelname; + + return [wheel_cond, modelname](game::VehicleTuningContext& ctx) { + for (auto& wheel : ctx.wheels) + { + if (CheckWheelCond(wheel, wheel_cond)) + wheel.modelname = modelname; + } + }; + } + else + { + throw std::runtime_error("tuning list: unknown function " + func_name); + } +} + +std::unique_ptr game::VehicleTuningList::LoadFromFile(const std::string& filename) +{ + auto tuninglist = std::make_unique(); + + if (!fs::FileExists(filename)) + return tuninglist; // empty + + VehicleTuningGroup* current_group = nullptr; + VehicleTuningPart* current_part = nullptr; + + auto process_command = [&](const std::string& command, std::istringstream& iss) { + if (command == "group") + { + VehicleTuningGroup group; + iss >> group.id; + group.displayname = assets::ParseString(iss); + + tuninglist->groups.emplace_back(std::move(group)); + current_group = &tuninglist->groups.back(); + + return true; + } + else if (command == "part") + { + if (!current_group) + throw std::runtime_error("tuning list: part without active group"); + + VehicleTuningPart part; + iss >> part.id >> part.price; + part.displayname = assets::ParseString(iss); + + current_part = &(current_group->parts[part.id] = std::move(part)); + + return true; + } + else if (command == "default") + { + tuninglist->default_funcs.emplace_back(ParseTuningFunction(iss)); + return true; + } + else if (command == "mod") + { + if (!current_part) + throw std::runtime_error("tuning list: mod without active part"); + + current_part->funcs.emplace_back(ParseTuningFunction(iss)); + return true; + } + + return false; + }; + + assets::LoadCMDFile(filename, [&](const std::string& command, std::istringstream& iss) { + + if (process_command(command, iss)) + return; + + if (command == "include") + { + std::string include_name; + iss >> include_name; + + assets::LoadCMDFile("data/" + include_name + ".tun", [&](const std::string& command, std::istringstream& iss) { + process_command(command, iss); + }); + + } + }); + + return tuninglist; +} \ No newline at end of file diff --git a/src/game/vehicle_tuning.hpp b/src/game/vehicle_tuning.hpp index 28121c5..cce1520 100644 --- a/src/game/vehicle_tuning.hpp +++ b/src/game/vehicle_tuning.hpp @@ -1,18 +1,72 @@ #pragma once #include +#include +#include +#include +#include +#include +#include namespace game { +struct VehicleWheelTuningContext +{ + bool front; + std::string modelname; + float radius; + float z_offset; + float friction; + float suspension_stiffness; + float suspension_max_force; + float suspension_rest_length; + float suspension_travel; + float roll_influence; + float steering_factor; + float braking_factor; + float engine_factor; +}; + +struct VehicleTuningContext +{ + std::array colors; + float mass; + float engine_force; + float braking_force; + std::vector wheels; +}; + +using VehicleTuningFunction = std::function; + +struct VehicleTuningPart +{ + std::string id; + std::string displayname; + uint32_t price; + + std::vector funcs; +}; + +struct VehicleTuningGroup +{ + std::string id; + std::string displayname; + std::map parts; +}; + +struct VehicleTuningList +{ + std::vector default_funcs; + std::vector groups; + + static std::unique_ptr LoadFromFile(const std::string& filename); +}; + struct VehicleTuning { std::string model; - - uint32_t primary_color = 0xFFFFFFFF; - - size_t wheels_idx = 0; - uint32_t wheel_color = 0xFFFFFFFF; + std::map parts; // group : part }; diff --git a/src/gameview/vehicleview.cpp b/src/gameview/vehicleview.cpp index 039ffbb..5d9f3d3 100644 --- a/src/gameview/vehicleview.cpp +++ b/src/gameview/vehicleview.cpp @@ -128,7 +128,7 @@ void game::view::VehicleView::Draw(const DrawArgs& args) gfx::DrawSurfaceCmd cmd; cmd.surface = &surface; cmd.matrices = &root_.matrix; - cmd.color = &color_; + cmd.color = &colors_[0]; args.dlist.AddSurface(cmd); } @@ -188,28 +188,29 @@ void game::view::VehicleView::InitMesh() bool game::view::VehicleView::ReadTuning(net::InMessage& msg) { - uint32_t color, wheel_color; - net::TuningPartIdx wheel_idx; + // read colors + for (size_t i = 0; i < 4; ++i) + { + uint32_t color; - const auto& tuninglist = model_->GetTuningList(); + if (!net::ReadRGB(msg, color)) + return false; - if (!net::ReadRGB(msg, color)) - return false; - - color_ = glm::unpackUnorm4x8(color); - - // wheels - if (!msg.Read(wheel_idx) || !net::ReadRGB(msg, wheel_color)) - return false; - - auto wheelmodel = wheel_idx < tuninglist.wheels.size() ? assets::CacheManager::GetModel("data/" + tuninglist.wheels[wheel_idx].model + ".mdl") : nullptr; - glm::vec3 wheelcolor = glm::unpackUnorm4x8(wheel_color); + colors_[i] = glm::unpackUnorm4x8(color); + } + // read wheel models for (size_t i = 0; i < wheels_.size(); ++i) { - auto& wheel = wheels_[i]; - wheel.model = wheelmodel ? wheelmodel : model_->GetWheels()[i].model; - wheel.color = glm::vec4(wheelcolor, 1.0f); + net::ModelName wheelmodel_fixed; + + if (!msg.Read(wheelmodel_fixed)) + return false; + + std::string wheel_model_name = wheelmodel_fixed; + + wheels_[i].model = !wheel_model_name.empty() ? assets::CacheManager::GetModel("data/" + wheel_model_name + ".mdl") : model_->GetWheels()[i].model; + wheels_[i].color = colors_[1]; // TODO: dynamic?; } return true; diff --git a/src/gameview/vehicleview.hpp b/src/gameview/vehicleview.hpp index b0c95e8..655aca3 100644 --- a/src/gameview/vehicleview.hpp +++ b/src/gameview/vehicleview.hpp @@ -55,7 +55,7 @@ private: private: std::shared_ptr model_; assets::Mesh mesh_; - glm::vec4 color_; + glm::vec4 colors_[4]; game::VehicleSyncState sync_; std::vector wheels_; diff --git a/src/utils/version.hpp b/src/utils/version.hpp index 02a568c..0a08027 100644 --- a/src/utils/version.hpp +++ b/src/utils/version.hpp @@ -1,3 +1,3 @@ #pragma once -#define FEKAL_VERSION 2026032901 +#define FEKAL_VERSION 2026041201