Compare commits

...
7 Commits
Author SHA1 Message Date
vottozi e799b4ec79 conflict resolution 2026-02-05 01:43:45 +06:00
vottozi c8076b6018 adjust label scale: constant far, smooth growth near 2026-02-05 01:34:14 +06:00
Vlad 204b79bf06 add main menu 2026-01-31 19:25:40 +06:00
Vlad 50232c0816 fix shooting 2026-01-31 16:44:00 +06:00
Vlad e8fb14b809 added sync projectiles/boxes 2026-01-27 19:38:18 +06:00
Vladislav Khorev d425167dce Added planet physics and simple base 2026-01-25 20:55:36 +03:00
Vlad cacc18dc7e added boxes from server 2026-01-19 22:04:33 +06:00
25 changed files with 6888 additions and 47 deletions

No files matched your search

+128
View File
@@ -0,0 +1,128 @@
{
"root": {
"type": "FrameLayout",
"x": 0,
"y": 0,
"width": 1280,
"height": 720,
"children": [
{
"type": "LinearLayout",
"name": "settingsButtons",
"orientation": "vertical",
"spacing": 10,
"x": 0,
"y": 0,
"width": 300,
"height": 300,
"children": [
{
"type": "Button",
"name": "langButton",
"x": 1100,
"y": 580,
"width": 142,
"height": 96,
"textures": {
"normal": "resources/main_menu/lang.png",
"hover": "resources/main_menu/lang.png",
"pressed": "resources/main_menu/lang.png"
}
},
{
"type": "Button",
"name": "titleBtn",
"x": 473,
"y": 500,
"width": 254,
"height": 35,
"textures": {
"normal": "resources/main_menu/title.png",
"hover": "resources/main_menu/title.png",
"pressed": "resources/main_menu/title.png"
}
},
{
"type": "Button",
"name": "underlineBtn",
"x": 516,
"y": 465,
"width": 168,
"height": 44,
"textures": {
"normal": "resources/main_menu/line.png",
"hover": "resources/main_menu/line.png",
"pressed": "resources/main_menu/line.png"
}
},
{
"type": "Button",
"name": "subtitleBtn",
"x": 528,
"y": 455,
"width": 144,
"height": 11,
"textures": {
"normal": "resources/main_menu/subtitle.png",
"hover": "resources/main_menu/subtitle.png",
"pressed": "resources/main_menu/subtitle.png"
}
},
{
"type": "Button",
"name": "singleButton",
"x": 409,
"y": 360,
"width": 382,
"height": 56,
"textures": {
"normal": "resources/main_menu/single.png",
"hover": "resources/main_menu/single.png",
"pressed": "resources/main_menu/single.png"
}
},
{
"type": "Button",
"name": "multiplayerButton",
"x": 409,
"y": 289,
"width": 382,
"height": 56,
"textures": {
"normal": "resources/main_menu/multi.png",
"hover": "resources/main_menu/multi.png",
"pressed": "resources/main_menu/multi.png"
}
},
{
"type": "Button",
"name": "exitButton",
"x": 409,
"y": 218,
"width": 382,
"height": 56,
"textures": {
"normal": "resources/main_menu/exit.png",
"hover": "resources/main_menu/exit.png",
"pressed": "resources/main_menu/exit.png"
}
},
{
"type": "Button",
"name": "versionLabel",
"x": 559.5,
"y": 170,
"width": 81,
"height": 9,
"textures": {
"normal": "resources/main_menu/version.png",
"hover": "resources/main_menu/version.png",
"pressed": "resources/main_menu/version.png"
}
}
]
}
]
}
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large. Load diff
Binary file not shown.
+187 -18
View File
@@ -11,6 +11,8 @@
#define _USE_MATH_DEFINES
#include <math.h>
#include "../src/network/ClientState.h"
#include <random>
#include <algorithm>
// Вспомогательный split
std::vector<std::string> split(const std::string& s, char delimiter) {
@@ -31,6 +33,16 @@ using tcp = net::ip::tcp;
class Session;
struct ServerBox {
Eigen::Vector3f position;
Eigen::Matrix3f rotation;
float collisionRadius = 2.0f;
};
std::vector<ServerBox> g_serverBoxes;
std::mutex g_boxes_mutex;
std::vector<std::shared_ptr<Session>> g_sessions;
std::mutex g_sessions_mutex;
@@ -38,38 +50,84 @@ class Session : public std::enable_shared_from_this<Session> {
websocket::stream<beast::tcp_stream> ws_;
beast::flat_buffer buffer_;
int id_;
bool is_writing_ = false;
ClientStateInterval timedClientStates;
void process_message(const std::string& msg) {
auto now_server = std::chrono::system_clock::now();
auto parts = split(msg, ':');
if (parts.size() < 16)
{
throw std::runtime_error("Unknown message type received, too small");
if (parts.empty()) {
std::cerr << "Empty message received\n";
return;
}
std::string type = parts[0];
if (type == "UPD") {
if (parts.size() < 16) {
std::cerr << "Invalid UPD message: too few parts (" << parts.size() << ")\n";
return;
}
uint64_t clientTimestamp = std::stoull(parts[1]);
ClientState receivedState;
receivedState.id = id_;
std::chrono::system_clock::time_point uptime_timepoint{ std::chrono::duration_cast<std::chrono::system_clock::time_point::duration>(std::chrono::milliseconds(clientTimestamp)) };
std::chrono::system_clock::time_point uptime_timepoint{
std::chrono::milliseconds(clientTimestamp)
};
receivedState.lastUpdateServerTime = uptime_timepoint;
if (parts[0] == "UPD") {
receivedState.handle_full_sync(parts, 2);
retranslateMessage(msg);
timedClientStates.add_state(receivedState);
}
else
{
throw std::runtime_error("Unknown message type received: " + parts[0]);
else if (parts[0] == "FIRE") {
if (parts.size() < 8) {
std::cerr << "Invalid FIRE: too few parts\n";
return;
}
timedClientStates.add_state(receivedState);
uint64_t clientTime = std::stoull(parts[1]);
Eigen::Vector3f pos{
std::stof(parts[2]), std::stof(parts[3]), std::stof(parts[4])
};
Eigen::Vector3f dir{
std::stof(parts[5]), std::stof(parts[6]), std::stof(parts[7])
};
int shotCount = 1;
if (parts.size() >= 9) {
try {
shotCount = std::stoi(parts[8]);
}
catch (...) {
shotCount = 1;
}
}
std::string broadcast = "PROJECTILE:" +
std::to_string(id_) + ":" +
std::to_string(clientTime) + ":" +
std::to_string(pos.x()) + ":" +
std::to_string(pos.y()) + ":" +
std::to_string(pos.z()) + ":" +
std::to_string(dir.x()) + ":" +
std::to_string(dir.y()) + ":" +
std::to_string(dir.z()) + ":" +
std::to_string(shotCount);
std::lock_guard<std::mutex> lock(g_sessions_mutex);
std::cout << "Player " << id_ << " fired " << shotCount << " shots → broadcasting\n";
for (auto& session : g_sessions) {
if (session->get_id() != id_) {
session->send_message(broadcast);
}
}
}
else {
std::cerr << "Unknown message type: " << type << "\n";
}
}
void retranslateMessage(const std::string& msg)
@@ -84,6 +142,52 @@ class Session : public std::enable_shared_from_this<Session> {
}
}
void sendBoxesToClient() {
std::lock_guard<std::mutex> lock(g_boxes_mutex);
std::string boxMsg = "BOXES:";
for (const auto& box : g_serverBoxes) {
Eigen::Quaternionf q(box.rotation);
boxMsg += std::to_string(box.position.x()) + ":" +
std::to_string(box.position.y()) + ":" +
std::to_string(box.position.z()) + ":" +
std::to_string(q.w()) + ":" +
std::to_string(q.x()) + ":" +
std::to_string(q.y()) + ":" +
std::to_string(q.z()) + "|";
}
if (!boxMsg.empty() && boxMsg.back() == '|') {
boxMsg.pop_back();
}
send_message(boxMsg);
}
void send_message(std::string msg) {
auto ss = std::make_shared<std::string>(std::move(msg));
if (is_writing_) {
ws_.async_write(net::buffer(*ss),
[self = shared_from_this(), ss](beast::error_code ec, std::size_t) {
if (ec) {
std::cerr << "Write error: " << ec.message() << std::endl;
}
});
}
else {
is_writing_ = true;
ws_.async_write(net::buffer(*ss),
[self = shared_from_this(), ss](beast::error_code ec, std::size_t) {
self->is_writing_ = false;
if (ec) {
std::cerr << "Write error: " << ec.message() << std::endl;
}
});
}
}
public:
explicit Session(tcp::socket&& socket, int id)
@@ -92,6 +196,16 @@ public:
void init()
{
sendBoxesToClient();
auto timer = std::make_shared<net::steady_timer>(ws_.get_executor());
timer->expires_after(std::chrono::milliseconds(100));
timer->async_wait([self = shared_from_this(), timer](const boost::system::error_code& ec) {
if (!ec) {
self->send_message("ID:" + std::to_string(self->id_));
self->do_read();
}
});
}
void run() {
@@ -105,15 +219,15 @@ public:
if (ec) return;
std::cout << "Client " << self->id_ << " connected\n";
self->init();
self->send_message("ID:" + std::to_string(self->id_));
self->do_read();
// self->send_message("ID:" + std::to_string(self->id_));
// self->do_read();
});
}
void send_message(std::string msg) {
/*void send_message(std::string msg) {
auto ss = std::make_shared<std::string>(std::move(msg));
ws_.async_write(net::buffer(*ss), [ss](beast::error_code, std::size_t) {});
}
}*/
int get_id() const {
return id_;
@@ -151,8 +265,63 @@ void update_world(net::steady_timer& timer, net::io_context& ioc) {
});
}
std::vector<ServerBox> generateServerBoxes(int count) {
std::vector<ServerBox> boxes;
std::random_device rd;
std::mt19937 gen(rd());
const float MIN_COORD = -100.0f;
const float MAX_COORD = 100.0f;
const float MIN_DISTANCE = 3.0f;
const float MIN_DISTANCE_SQUARED = MIN_DISTANCE * MIN_DISTANCE;
const int MAX_ATTEMPTS = 1000;
std::uniform_real_distribution<> posDistrib(MIN_COORD, MAX_COORD);
std::uniform_real_distribution<> angleDistrib(0.0, M_PI * 2.0);
for (int i = 0; i < count; i++) {
bool accepted = false;
int attempts = 0;
while (!accepted && attempts < MAX_ATTEMPTS) {
ServerBox box;
box.position = Eigen::Vector3f(
(float)posDistrib(gen),
(float)posDistrib(gen),
(float)posDistrib(gen)
);
accepted = true;
for (const auto& existingBox : boxes) {
Eigen::Vector3f diff = box.position - existingBox.position;
if (diff.squaredNorm() < MIN_DISTANCE_SQUARED) {
accepted = false;
break;
}
}
if (accepted) {
float randomAngle = (float)angleDistrib(gen);
Eigen::Vector3f axis = Eigen::Vector3f::Random().normalized();
box.rotation = Eigen::AngleAxisf(randomAngle, axis).toRotationMatrix();
boxes.push_back(box);
}
attempts++;
}
}
return boxes;
}
int main() {
try {
{
std::lock_guard<std::mutex> lock(g_boxes_mutex);
g_serverBoxes = generateServerBoxes(50);
std::cout << "Generated " << g_serverBoxes.size() << " boxes on server\n";
}
net::io_context ioc;
tcp::acceptor acceptor{ ioc, {tcp::v4(), 8080} };
int next_id = 0;
+136 -7
View File
@@ -10,6 +10,7 @@
#include <random>
#include <cmath>
#include <algorithm>
#include <functional>
#ifdef __ANDROID__
#include <android/log.h>
#endif
@@ -268,6 +269,10 @@ namespace ZL
bool explosionCfgLoaded = explosionEmitter.loadFromJsonFile("resources/config/explosion_config.json", renderer, CONST_ZIP_FILE);
explosionEmitter.setEmissionPoints(std::vector<Vector3f>());
projectileEmitter.setEmissionPoints(std::vector<Vector3f>());
uiManager.loadFromFile("resources/config/main_menu.json", renderer, CONST_ZIP_FILE);
std::function<void()> loadGameplayUI;
loadGameplayUI = [this]() {
uiManager.loadFromFile("resources/config/ui.json", renderer, CONST_ZIP_FILE);
uiManager.startAnimationOnNode("backgroundNode", "bgScroll");
@@ -296,10 +301,8 @@ namespace ZL
g_exitBgAnimating = false;
});
// Set UI button callbacks
uiManager.setButtonCallback("playButton", [this](const std::string& name) {
std::cerr << "Play button pressed: " << name << std::endl;
});
uiManager.setButtonCallback("settingsButton", [this](const std::string& name) {
@@ -332,11 +335,31 @@ namespace ZL
}
}
});
uiManager.setButtonCallback("shootButton", [this](const std::string& name) {
uint64_t now = SDL_GetTicks64();
if (now - lastProjectileFireTime >= static_cast<uint64_t>(projectileCooldownMs)) {
lastProjectileFireTime = now;
fireProjectiles();
this->fireProjectiles();
Eigen::Vector3f localForward = { 0, 0, -1 };
Eigen::Vector3f worldForward = (Environment::shipState.rotation * localForward).normalized();
Eigen::Vector3f centerPos = Environment::shipState.position +
Environment::shipState.rotation * Vector3f{ 0, 0.9f, 5.0f };
std::string fireMsg = "FIRE:" +
std::to_string(now) + ":" +
std::to_string(centerPos.x()) + ":" +
std::to_string(centerPos.y()) + ":" +
std::to_string(centerPos.z()) + ":" +
std::to_string(worldForward.x()) + ":" +
std::to_string(worldForward.y()) + ":" +
std::to_string(worldForward.z()) + ":" +
"2";
networkClient->Send(fireMsg);
}
});
@@ -346,6 +369,20 @@ namespace ZL
newShipVelocity = newVel;
}
});
};
uiManager.setButtonCallback("singleButton", [loadGameplayUI](const std::string& name) {
std::cerr << "Single button pressed: " << name << " -> load gameplay UI\n";
loadGameplayUI();
});
uiManager.setButtonCallback("multiplayerButton", [loadGameplayUI](const std::string& name) {
std::cerr << "Multiplayer button pressed: " << name << " -> load gameplay UI\n";
loadGameplayUI();
});
uiManager.setButtonCallback("exitButton", [](const std::string& name) {
std::cerr << "Exit from main menu pressed: " << name << " -> exiting\n";
Environment::exitGameLoop = true;
});
cubemapTexture = std::make_shared<Texture>(
std::array<TextureDataStruct, 6>{
@@ -540,6 +577,9 @@ namespace ZL
explosionEmitter.draw(renderer, Environment::zoom, Environment::width, Environment::height);
}
//glBindTexture(GL_TEXTURE_2D, basePlatformTexture->getTexID());
//renderer.DrawVertexRenderStruct(basePlatform);
glDisable(GL_BLEND);
renderer.PopMatrix();
renderer.PopProjectionMatrix();
@@ -692,6 +732,27 @@ namespace ZL
latestRemotePlayers = networkClient->getRemotePlayers();
// Если сервер прислал коробки, применяем их однократно вместо локальной генерации
if (!serverBoxesApplied && networkClient) {
auto sboxes = networkClient->getServerBoxes();
if (!sboxes.empty()) {
boxCoordsArr.clear();
for (auto& b : sboxes) {
BoxCoords bc;
bc.pos = b.first;
bc.m = b.second;
boxCoordsArr.push_back(bc);
}
boxRenderArr.resize(boxCoordsArr.size());
for (int i = 0; i < (int)boxCoordsArr.size(); ++i) {
boxRenderArr[i].AssignFrom(boxBase);
boxRenderArr[i].RefreshVBO();
}
boxAlive.assign(boxCoordsArr.size(), true);
serverBoxesApplied = true;
}
}
// Итерируемся по актуальным данным из extrapolateRemotePlayers
for (auto const& [id, remotePlayer] : latestRemotePlayers) {
@@ -770,7 +831,16 @@ namespace ZL
continue;
float uiX = sx, uiY = sy;
float scale = std::clamp(BASE_SCALE / (dist * PERSPECTIVE_K + 1.f), MIN_SCALE, MAX_SCALE);
//float scale = std::clamp(BASE_SCALE / (dist * PERSPECTIVE_K + 1.f), MIN_SCALE, MAX_SCALE);
float scale;
if (dist > CLOSE_DIST) {
scale = 0.4f;
}
else {
float t = 1.0f - (dist / CLOSE_DIST);
scale = 0.4f + (MAX_SCALE - 0.4f) * t;
}
scale = std::clamp(scale, MIN_SCALE, MAX_SCALE);
// Дефолтный лейбл
std::string label = "Player (" + std::to_string(st.id) + ") " + std::to_string((int)dist) + "m";
@@ -1230,7 +1300,7 @@ namespace ZL
handleMotion(mx, my);
}
/*
if (event.type == SDL_MOUSEWHEEL) {
static const float zoomstep = 2.0f;
if (event.wheel.y > 0) {
@@ -1247,14 +1317,73 @@ namespace ZL
{
if (event.key.keysym.sym == SDLK_i)
{
x = x + 1;
}
if (event.key.keysym.sym == SDLK_k)
{
x = x - 1;
}
if (event.key.keysym.sym == SDLK_a)
{
Environment::shipState.position = { 9466.15820, 1046.00159, 18531.2090 };
}
}
}*/
#endif
}
render();
mainThreadHandler.processMainThreadTasks();
networkClient->Poll();
if (networkClient) {
auto pending = networkClient->getPendingProjectiles();
if (!pending.empty()) {
const float projectileSpeed = 60.0f;
const float lifeMs = 5000.0f;
const float size = 0.5f;
auto remotePlayersSnapshot = networkClient->getRemotePlayers();
for (const auto& pi : pending) {
Eigen::Vector3f dir = pi.direction;
float len = dir.norm();
if (len <= 1e-6f) continue;
dir /= len;
Eigen::Matrix3f shooterRot = Eigen::Matrix3f::Identity();
float shooterVel = 0.0f;
auto it = remotePlayersSnapshot.find(pi.shooterId);
if (it != remotePlayersSnapshot.end()) {
std::chrono::system_clock::time_point pktTime{ std::chrono::milliseconds(pi.clientTime) };
if (it->second.canFetchClientStateAtTime(pktTime)) {
ClientState shooterState = it->second.fetchClientStateAtTime(pktTime);
shooterRot = shooterState.rotation;
shooterVel = shooterState.velocity;
}
}
float speedWithOwner = projectileSpeed + shooterVel;
Eigen::Vector3f baseVel = dir * speedWithOwner;
int shotCount = 2;
std::vector<Eigen::Vector3f> localOffsets = {
{-1.5f, 0.9f, 5.0f},
{ 1.5f, 0.9f, 5.0f}
};
for (int i = 0; i < shotCount; ++i) {
Eigen::Vector3f rotatedOffset = shooterRot * localOffsets[i];
Eigen::Vector3f shotPos = pi.position + rotatedOffset;
for (auto& p : projectiles) {
if (!p->isActive()) {
p->init(shotPos, baseVel, lifeMs, size, projectileTexture, renderer);
break;
}
}
}
}
}
}
}
void Game::handleDown(int mx, int my)
+4 -1
View File
@@ -86,6 +86,7 @@ namespace ZL {
VertexDataStruct spaceshipBase;
VertexRenderStruct spaceship;
VertexRenderStruct cubemap;
std::shared_ptr<Texture> boxTexture;
@@ -121,7 +122,9 @@ namespace ZL {
static constexpr float BASE_SCALE = 140.f;
static constexpr float PERSPECTIVE_K = 0.05f; // Tune
static constexpr float MIN_SCALE = 0.4f;
static constexpr float MAX_SCALE = 1.5f;
static constexpr float MAX_SCALE = 0.8f;
static constexpr float CLOSE_DIST = 600.0f;
bool serverBoxesApplied = false;
};
+63
View File
@@ -45,6 +45,69 @@ void ClientState::simulate_physics(size_t delta) {
}
}
float distToCenter = position.norm(); // Расстояние до {0,0,0}
float landingZone = PLANET_RADIUS * PLANET_ALIGN_ZONE;
if (distToCenter <= landingZone) {
Eigen::Vector3f planetNormal = position.normalized();
// --- 1. ВЫРАВНИВАНИЕ КРЕНА (Roll - ось Z) ---
Eigen::Vector3f localX = rotation.col(0);
float rollError = localX.dot(planetNormal);
if (std::abs(rollError) > 0.001f) {
currentAngularVelocity.z() -= rollError * PLANET_ANGULAR_ACCEL * delta;
currentAngularVelocity.z() = std::max(-PLANET_MAX_ANGULAR_VELOCITY,
std::min(currentAngularVelocity.z(), PLANET_MAX_ANGULAR_VELOCITY));
}
// --- 2. ОГРАНИЧЕНИЕ ТАНГАЖА (Pitch - ось X) ---
// Нос корабля в локальных координатах — это -Z (третий столбец матрицы)
Eigen::Vector3f forwardDir = -rotation.col(2);
// В твоем случае dot < 0 означает, что нос направлен К планете
float pitchSin = forwardDir.dot(planetNormal);
float currentPitchAngle = asinf(std::clamp(pitchSin, -1.0f, 1.0f));
// Лимит у нас M_PI / 6.0 (примерно 0.523)
// По твоим данным: -0.89 < -0.523, значит мы превысили наклон вниз
if (currentPitchAngle < -PITCH_LIMIT) {
// Вычисляем ошибку (насколько мы ушли "ниже" лимита)
// -0.89 - (-0.52) = -0.37
float pitchError = currentPitchAngle + PITCH_LIMIT;
// Теперь важно: нам нужно ПОДНЯТЬ нос.
// Если pitchError отрицательный, а нам нужно уменьшить вращение,
// пробуем прибавлять или вычитать в зависимости от твоей оси X.
// Судя по стандартной логике Eigen, нам нужно ПЛЮСОВАТЬ:
currentAngularVelocity.x() -= pitchError * PLANET_ANGULAR_ACCEL * delta;
}
}
else {
// Вне зоны: тормозим Z (крен) И X (тангаж), если они были активны
float drop = ANGULAR_ACCEL * delta;
if (std::abs(currentAngularVelocity[2]) > 0.0001f) {
if (std::abs(currentAngularVelocity[2]) <= drop) {
currentAngularVelocity[2] = 0.0f;
}
else {
currentAngularVelocity[2] -= (currentAngularVelocity[2] > 0 ? 1.0f : -1.0f) * drop;
}
}
}
// Ограничение скорости
currentAngularVelocity.x() = std::max(-PLANET_MAX_ANGULAR_VELOCITY,
std::min(currentAngularVelocity.x(), PLANET_MAX_ANGULAR_VELOCITY));
// Ограничение скорости
currentAngularVelocity.y() = std::max(-PLANET_MAX_ANGULAR_VELOCITY,
std::min(currentAngularVelocity.y(), PLANET_MAX_ANGULAR_VELOCITY));
// Ограничение скорости
currentAngularVelocity.z() = std::max(-PLANET_MAX_ANGULAR_VELOCITY,
std::min(currentAngularVelocity.z(), PLANET_MAX_ANGULAR_VELOCITY));
float speedScale = currentAngularVelocity.norm();
if (speedScale > 0.0001f) {
// Коэффициент чувствительности вращения
+7 -1
View File
@@ -13,8 +13,14 @@ constexpr float ANGULAR_ACCEL = 0.005f * 1000.0f;
constexpr float SHIP_ACCEL = 1.0f * 1000.0f;
constexpr float ROTATION_SENSITIVITY = 0.002f;
constexpr float PLANET_RADIUS = 20000.f;
constexpr float PLANET_ALIGN_ZONE = 1.05f;
constexpr float PLANET_ANGULAR_ACCEL = 0.01f; // Подбери под динамику
constexpr float PLANET_MAX_ANGULAR_VELOCITY = 10.f;
constexpr float PITCH_LIMIT = static_cast<float>(M_PI) / 9.f;//18.0f;
constexpr long long SERVER_DELAY = 0; //ms
constexpr long long CLIENT_DELAY = 200; //ms
constexpr long long CLIENT_DELAY = 1000; //ms
constexpr long long CUTOFF_TIME = 5000; //ms
struct ClientState {
+3 -1
View File
@@ -11,8 +11,10 @@ namespace ZL {
}
void LocalClient::Send(const std::string& message) {
}
std::vector<ProjectileInfo> LocalClient::getPendingProjectiles() {
return {};
}
}
+5
View File
@@ -17,9 +17,14 @@ namespace ZL {
bool IsConnected() const override { return true; }
int GetClientId() const { return 1; }
std::vector<ProjectileInfo> getPendingProjectiles();
std::unordered_map<int, ClientStateInterval> getRemotePlayers() override {
return std::unordered_map<int, ClientStateInterval>();
}
std::vector<std::pair<Eigen::Vector3f, Eigen::Matrix3f>> getServerBoxes() override {
return {};
}
};
}
+11
View File
@@ -7,6 +7,13 @@
// NetworkInterface.h - »нтерфейс дл¤ разных типов соединений
namespace ZL {
struct ProjectileInfo {
int shooterId = -1;
uint64_t clientTime = 0;
Eigen::Vector3f position = Eigen::Vector3f::Zero();
Eigen::Vector3f direction = Eigen::Vector3f::Zero();
};
class INetworkClient {
public:
virtual ~INetworkClient() = default;
@@ -15,5 +22,9 @@ namespace ZL {
virtual bool IsConnected() const = 0;
virtual void Poll() = 0; // ƒл¤ обработки вход¤щих пакетов
virtual std::unordered_map<int, ClientStateInterval> getRemotePlayers() = 0;
virtual std::vector<std::pair<Eigen::Vector3f, Eigen::Matrix3f>> getServerBoxes() = 0;
virtual std::vector<ProjectileInfo> getPendingProjectiles() = 0;
};
}
+69
View File
@@ -64,6 +64,13 @@ namespace ZL {
messageQueue.push(msg);
}
std::vector<ProjectileInfo> WebSocketClient::getPendingProjectiles() {
std::lock_guard<std::mutex> lock(projMutex_);
auto copy = pendingProjectiles_;
pendingProjectiles_.clear();
return copy;
}
void WebSocketClient::Poll() {
std::lock_guard<std::mutex> lock(queueMutex);
@@ -83,6 +90,68 @@ namespace ZL {
std::string msg = messageQueue.front();
messageQueue.pop();
// Обработка списка коробок от сервера
if (msg.rfind("BOXES:", 0) == 0) {
std::string payload = msg.substr(6); // после "BOXES:"
std::vector<std::pair<Eigen::Vector3f, Eigen::Matrix3f>> parsedBoxes;
if (!payload.empty()) {
auto items = split(payload, '|');
for (auto& item : items) {
if (item.empty()) continue;
auto parts = split(item, ':');
if (parts.size() < 7) continue;
try {
float px = std::stof(parts[0]);
float py = std::stof(parts[1]);
float pz = std::stof(parts[2]);
Eigen::Quaternionf q(
std::stof(parts[3]),
std::stof(parts[4]),
std::stof(parts[5]),
std::stof(parts[6])
);
Eigen::Matrix3f rot = q.toRotationMatrix();
parsedBoxes.emplace_back(Eigen::Vector3f{ px, py, pz }, rot);
}
catch (...) {
// пропускаем некорректную запись
continue;
}
}
}
{
std::lock_guard<std::mutex> bLock(boxesMutex);
serverBoxes_ = std::move(parsedBoxes);
}
continue;
}
if (msg.rfind("PROJECTILE:", 0) == 0) {
auto parts = split(msg, ':');
if (parts.size() >= 9) {
try {
ProjectileInfo pi;
pi.shooterId = std::stoi(parts[1]);
pi.clientTime = std::stoull(parts[2]);
pi.position = Eigen::Vector3f(
std::stof(parts[3]),
std::stof(parts[4]),
std::stof(parts[5])
);
pi.direction = Eigen::Vector3f(
std::stof(parts[6]),
std::stof(parts[7]),
std::stof(parts[8])
);
std::lock_guard<std::mutex> pl(projMutex_);
pendingProjectiles_.push_back(pi);
}
catch (...) {
}
}
continue;
}
if (msg.rfind("EVENT:", 0) == 0) {
auto parts = split(msg, ':');
if (parts.size() < 5) continue; // EVENT:ID:TYPE:TIME:DATA...
+14
View File
@@ -34,6 +34,13 @@ namespace ZL {
std::unordered_map<int, ClientStateInterval> remotePlayers;
std::mutex playersMutex;
// Серверные коробки
std::vector<std::pair<Eigen::Vector3f, Eigen::Matrix3f>> serverBoxes_;
std::mutex boxesMutex;
std::vector<ProjectileInfo> pendingProjectiles_;
std::mutex projMutex_;
void startAsyncRead();
void processIncomingMessage(const std::string& msg);
@@ -54,6 +61,13 @@ namespace ZL {
std::lock_guard<std::mutex> lock(playersMutex);
return remotePlayers;
}
std::vector<std::pair<Eigen::Vector3f, Eigen::Matrix3f>> getServerBoxes() override {
std::lock_guard<std::mutex> lock(boxesMutex);
return serverBoxes_;
}
std::vector<ProjectileInfo> getPendingProjectiles() override;
};
}
#endif
+28 -7
View File
@@ -6,6 +6,7 @@
namespace ZL {
Matrix3f GetRotationForTriangle(const Triangle& tri);
const float PlanetData::PLANET_RADIUS = 20000.f;
const Vector3f PlanetData::PLANET_CENTER_OFFSET = Vector3f{ 0.f, 0.f, 0.0f };
@@ -29,9 +30,9 @@ namespace ZL {
PlanetData::PlanetData()
: perlin(77777)
, colorPerlin(123123)
, currentLod(0)
//, currentLod(0)
{
currentLod = planetMeshLods.size() - 1; // Start with max LOD
// currentLod = planetMeshLods.size() - 1; // Start with max LOD
/*
initialVertexMap = {
{{ 0.0f, 1.0f, 0.0f}, "A"},
@@ -54,10 +55,29 @@ namespace ZL {
planetAtmosphereLod = generateSphere(5, 0);
planetAtmosphereLod.Scale(PLANET_RADIUS * 1.03);
planetAtmosphereLod.Move(PLANET_CENTER_OFFSET);
const auto& lodLevel = getLodLevel();
for (size_t i = 0; i < lodLevel.triangles.size(); i++)
{
if (i % 100 == 0)
{
PlanetCampObject campObject;
campObject.position = (lodLevel.triangles[i].data[0] +
lodLevel.triangles[i].data[1] +
lodLevel.triangles[i].data[2]) / 3.0f;
auto newM = Eigen::Quaternionf(Eigen::AngleAxisf(M_PI * 0.5, Eigen::Vector3f::UnitX())).toRotationMatrix();
campObject.rotation = GetRotationForTriangle(lodLevel.triangles[i]).inverse() * newM;
campObjects.push_back(campObject);
}
}
}
const LodLevel& PlanetData::getLodLevel(int level) const {
return planetMeshLods.at(level);
const LodLevel& PlanetData::getLodLevel() const {
return planetMeshLods.at(MAX_LOD_LEVELS-1);
}
@@ -65,13 +85,14 @@ namespace ZL {
return planetAtmosphereLod;
}
/*
int PlanetData::getCurrentLodIndex() const {
return currentLod;
}
int PlanetData::getMaxLodIndex() const {
return static_cast<int>(planetMeshLods.size() - 1);
}
}*/
std::pair<float, float> PlanetData::calculateZRange(float dToPlanetSurface) {
@@ -138,7 +159,7 @@ namespace ZL {
std::vector<int> PlanetData::getBestTriangleUnderCamera(const Vector3f& viewerPosition) {
const LodLevel& finalLod = planetMeshLods[currentLod]; // Работаем с текущим активным LOD
const LodLevel& finalLod = planetMeshLods[MAX_LOD_LEVELS - 1]; // Работаем с текущим активным LOD
Vector3f targetDir = (viewerPosition - PLANET_CENTER_OFFSET).normalized();
int bestTriangle = -1;
@@ -167,7 +188,7 @@ namespace ZL {
}
std::vector<int> PlanetData::getTrianglesUnderCameraNew2(const Vector3f& viewerPosition) {
const LodLevel& finalLod = planetMeshLods[currentLod];
const LodLevel& finalLod = planetMeshLods[MAX_LOD_LEVELS - 1];
Vector3f shipLocal = viewerPosition - PLANET_CENTER_OFFSET;
float currentDist = shipLocal.norm();
Vector3f targetDir = shipLocal.normalized();
+19 -6
View File
@@ -75,6 +75,20 @@ namespace ZL {
}
};
struct PlanetCampObject
{
Vector3f position;
Matrix3f rotation;
std::array<Vector3f, 5> platformPos = {
Vector3f{ 0.f, 0.f,-38.f },
Vector3f{ 20.f, 0.f,-18.f },
Vector3f{ 20.f, 0.f,-58.f },
Vector3f{ -20.f, 0.f,-58.f },
Vector3f{ -20.f, 0.f,-18.f }
};
};
class PlanetData {
public:
static const float PLANET_RADIUS;
@@ -87,7 +101,7 @@ namespace ZL {
std::array<LodLevel, MAX_LOD_LEVELS> planetMeshLods;
LodLevel planetAtmosphereLod;
int currentLod; // Логический текущий уровень детализации
//int currentLod; // Логический текущий уровень детализации
//std::map<Vector3f, VertexID, Vector3fComparator> initialVertexMap;
@@ -100,12 +114,8 @@ namespace ZL {
PlanetData();
void init();
// Методы доступа к данным (для рендерера)
const LodLevel& getLodLevel(int level) const;
const LodLevel& getLodLevel() const;
const LodLevel& getAtmosphereLod() const;
int getCurrentLodIndex() const;
int getMaxLodIndex() const;
// Логика
std::pair<float, float> calculateZRange(float distanceToSurface);
@@ -116,6 +126,9 @@ namespace ZL {
std::vector<int> getTrianglesUnderCameraNew2(const Vector3f& viewerPosition);
void applySphericalRelaxation(LodLevel& lod, int iterations);
std::vector<PlanetCampObject> campObjects;
};
} // namespace ZL
+103 -6
View File
@@ -5,9 +5,12 @@
#include "Environment.h"
#include "StoneObject.h"
#include "utils/TaskManager.h"
#include "TextModel.h"
namespace ZL {
extern float x;
#if defined EMSCRIPTEN || defined __ANDROID__
using std::min;
using std::max;
@@ -70,8 +73,7 @@ namespace ZL {
// 2. Забираем данные для VBO
// Берем максимальный LOD для начальной отрисовки
int lodIndex = planetData.getMaxLodIndex();
planetRenderStruct.data = planetData.getLodLevel(lodIndex).vertexData;
planetRenderStruct.data = planetData.getLodLevel().vertexData;
//planetRenderStruct.data.PositionData.resize(9);
planetRenderStruct.RefreshVBO();
@@ -87,13 +89,20 @@ namespace ZL {
planetAtmosphereRenderStruct.RefreshVBO();
}
planetStones = CreateStoneGroupData(778, planetData.getLodLevel(lodIndex));
planetStones = CreateStoneGroupData(778, planetData.getLodLevel());
//stonesToRender = planetStones.inflate(planetStones.allInstances.size());
stonesToRender.resize(planetStones.allInstances.size());
planetStones.initStatuses();
stoneToBake = planetStones.inflateOne(0, 0.75);
campPlatform.data = LoadFromTextFile02("resources/platform1.txt", CONST_ZIP_FILE);
campPlatform.RefreshVBO();
campPlatformTexture = std::make_unique<Texture>(CreateTextureDataFromPng("resources/platform_base.png", CONST_ZIP_FILE));
}
@@ -187,7 +196,7 @@ namespace ZL {
renderer.EnableVertexAttribArray(vPositionName);
renderer.EnableVertexAttribArray(vTexCoordName);
Triangle tr = planetData.getLodLevel(planetData.getCurrentLodIndex()).triangles[0];
Triangle tr = planetData.getLodLevel().triangles[0];
// 1. Получаем матрицу вращения (оси в столбцах)
Matrix3f mr = GetRotationForTriangle(tr);
@@ -282,6 +291,8 @@ namespace ZL {
drawPlanet(renderer);
drawStones(renderer);
drawCamp(renderer);
glClear(GL_DEPTH_BUFFER_BIT);
drawAtmosphere(renderer);
}
@@ -328,7 +339,7 @@ namespace ZL {
renderer.RenderUniform1i("BakedTexture", 1);
Triangle tr = planetData.getLodLevel(planetData.getCurrentLodIndex()).triangles[0]; // Берем базовый треугольник
Triangle tr = planetData.getLodLevel().triangles[0]; // Берем базовый треугольник
Matrix3f mr = GetRotationForTriangle(tr); // Та же матрица, что и при запекании
// Позиция камеры (корабля) в мире
@@ -464,7 +475,7 @@ namespace ZL {
renderer.shaderManager.PopShader();
CheckGlError();
glClear(GL_DEPTH_BUFFER_BIT);
}
void PlanetObject::drawAtmosphere(Renderer& renderer) {
@@ -568,6 +579,92 @@ namespace ZL {
}
void PlanetObject::drawCamp(Renderer& renderer)
{
static const std::string defaultShaderName2 = "default";
static const std::string vPositionName = "vPosition";
static const std::string vColorName = "vColor";
static const std::string vNormalName = "vNormal";
static const std::string vTexCoordName = "vTexCoord";
static const std::string textureUniformName = "Texture";
renderer.shaderManager.PushShader(defaultShaderName2);
renderer.RenderUniform1i(textureUniformName, 0);
renderer.EnableVertexAttribArray(vPositionName);
renderer.EnableVertexAttribArray(vColorName);
renderer.EnableVertexAttribArray(vNormalName);
renderer.EnableVertexAttribArray(vTexCoordName);
float dist = planetData.distanceToPlanetSurfaceFast(Environment::shipState.position);
auto zRange = planetData.calculateZRange(dist);
const float currentZNear = zRange.first;
const float currentZFar = zRange.second;
// 2. Применяем динамическую матрицу проекции
renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5,
static_cast<float>(Environment::width) / static_cast<float>(Environment::height),
currentZNear, currentZFar);
renderer.PushMatrix();
renderer.LoadIdentity();
renderer.TranslateMatrix({ 0,0, -1.0f * Environment::zoom });
renderer.RotateMatrix(Environment::inverseShipMatrix);
renderer.TranslateMatrix(-Environment::shipState.position);
renderer.RenderUniform1f("uDistanceToPlanetSurface", dist);
renderer.RenderUniform1f("uCurrentZFar", currentZFar);
renderer.RenderUniform3fv("uViewPos", Environment::shipState.position.data());
//std::cout << "uViewPos" << Environment::shipState.position << std::endl;
// PlanetObject.cpp, метод drawStones
Vector3f sunDirWorld = Vector3f(1.0f, -1.0f, -1.0f).normalized();
renderer.RenderUniform3fv("uLightDirWorld", sunDirWorld.data());
Vector3f playerDirWorld = Environment::shipState.position.normalized();
float playerLightFactor = max(0.0f, (playerDirWorld.dot(-sunDirWorld) + 0.2f) / 1.2f);
renderer.RenderUniform1f("uPlayerLightFactor", playerLightFactor);
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
for (int i = 0; i < planetData.campObjects.size(); i++)
{
renderer.PushMatrix();
for (int j = 0; j < 5; j++)
{
renderer.PushMatrix();
renderer.TranslateMatrix(planetData.campObjects[i].position);
renderer.RotateMatrix(planetData.campObjects[i].rotation);
renderer.ScaleMatrix(Vector3f{ 2.0f, 2.0f, 2.0f });
renderer.TranslateMatrix(planetData.campObjects[i].platformPos[j]);
glBindTexture(GL_TEXTURE_2D, campPlatformTexture->getTexID());
renderer.DrawVertexRenderStruct(campPlatform);
renderer.PopMatrix();
}
renderer.PopMatrix();
}
CheckGlError();
glDisable(GL_BLEND);
glDisable(GL_CULL_FACE);
renderer.PopMatrix();
renderer.PopProjectionMatrix();
renderer.DisableVertexAttribArray(vTexCoordName);
renderer.DisableVertexAttribArray(vNormalName);
renderer.DisableVertexAttribArray(vColorName);
renderer.DisableVertexAttribArray(vPositionName);
renderer.shaderManager.PopShader();
CheckGlError();
glClear(GL_DEPTH_BUFFER_BIT);
}
float PlanetObject::distanceToPlanetSurface(const Vector3f& viewerPosition)
{
return planetData.distanceToPlanetSurfaceFast(viewerPosition);
+5
View File
@@ -41,6 +41,10 @@ namespace ZL {
std::unique_ptr<FrameBuffer> stoneMapFB;
VertexRenderStruct campPlatform;
std::shared_ptr<Texture> campPlatformTexture;
Vector3f lastUpdatePos;
// External items, set outside
@@ -57,6 +61,7 @@ namespace ZL {
void drawStones(Renderer& renderer);
void drawPlanet(Renderer& renderer);
void drawAtmosphere(Renderer& renderer);
void drawCamp(Renderer& renderer);
float distanceToPlanetSurface(const Vector3f& viewerPosition);
};