added class Camera and fixed control of ship
This commit is contained in:
parent
47da35a401
commit
fb4a860773
184
src/Game.cpp
184
src/Game.cpp
@ -117,8 +117,9 @@ namespace ZL
|
||||
, glContext(nullptr)
|
||||
, newTickCount(0)
|
||||
, lastTickCount(0)
|
||||
, planetObject(renderer, taskManager, mainThreadHandler)
|
||||
, planetObject(renderer, taskManager, mainThreadHandler, camera)
|
||||
{
|
||||
Environment::shipState.selectedVelocity = 0;
|
||||
projectiles.reserve(maxProjectiles);
|
||||
for (int i = 0; i < maxProjectiles; ++i) {
|
||||
projectiles.emplace_back(std::make_unique<Projectile>());
|
||||
@ -237,12 +238,13 @@ namespace ZL
|
||||
}
|
||||
});
|
||||
|
||||
uiManager.setSliderCallback("velocitySlider", [this](const std::string& name, float value) {
|
||||
int newVel = roundf(value * 10);
|
||||
if (newVel != Environment::shipState.selectedVelocity) {
|
||||
newShipVelocity = newVel;
|
||||
}
|
||||
});
|
||||
//uiManager.setSliderCallback("velocitySlider", [this](const std::string& name, float value) {
|
||||
// int newVel = roundf(value * 10);
|
||||
// if (newVel != Environment::shipState.selectedVelocity) {
|
||||
// newShipVelocity = newVel;
|
||||
// }
|
||||
// });
|
||||
|
||||
|
||||
// Добавляем джойстик для управления кораблём
|
||||
// centerX=150, centerY=150 (от левого нижнего угла в UI координатах)
|
||||
@ -345,17 +347,16 @@ namespace ZL
|
||||
|
||||
// Для скайбокса берем только вращение от камеры
|
||||
Matrix4f view = camera.getViewMatrix();
|
||||
view.block<3, 1>(0, 3) = Vector3f::Zero(); // Убираем смещение
|
||||
view.block<3, 1>(0, 3) = Vector3f::Zero();
|
||||
renderer.PushSpecialMatrix(view);
|
||||
|
||||
|
||||
Vector3f worldLightDir = Vector3f(1.0f, -1.0f, -1.0f).normalized();
|
||||
Matrix3f viewMatrix = Environment::inverseShipMatrix;
|
||||
Vector3f viewLightDir = (viewMatrix * worldLightDir).normalized();
|
||||
|
||||
|
||||
// Передаем вектор НА источник света
|
||||
Matrix3f viewRot = view.block<3, 3>(0, 0); // world->view rotation
|
||||
Vector3f viewLightDir = (viewRot * worldLightDir).normalized();
|
||||
Vector3f lightToSource = -viewLightDir;
|
||||
|
||||
renderer.RenderUniform3fv("uLightDirView", lightToSource.data());
|
||||
|
||||
// 2. Базовый цвет атмосферы (голубой)
|
||||
@ -417,15 +418,27 @@ namespace ZL
|
||||
|
||||
renderer.PushSpecialMatrix(camera.getViewMatrix());
|
||||
|
||||
// --- ship model matrix ---
|
||||
renderer.PushMatrix();
|
||||
renderer.TranslateMatrix(Environment::shipState.position);
|
||||
renderer.RotateMatrix(shipWorldOrientation);
|
||||
renderer.RotateMatrix(Environment::shipState.rotation);
|
||||
|
||||
if (shipAlive) {
|
||||
glBindTexture(GL_TEXTURE_2D, spaceshipTexture->getTexID());
|
||||
renderer.DrawVertexRenderStruct(spaceship);
|
||||
}
|
||||
|
||||
// Эмиттеры рисуем в той же матрице корабля
|
||||
if (shipAlive) {
|
||||
renderer.PushMatrix();
|
||||
renderer.TranslateMatrix({ 0, 0, 16 });
|
||||
sparkEmitter.draw(renderer, Environment::zoom, Environment::width, Environment::height);
|
||||
renderer.PopMatrix();
|
||||
}
|
||||
|
||||
renderer.PopMatrix(); // --- end ship model matrix ---
|
||||
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
@ -542,8 +555,6 @@ namespace ZL
|
||||
|
||||
glViewport(0, 0, Environment::width, Environment::height);
|
||||
|
||||
glViewport(0, 0, Environment::width, Environment::height);
|
||||
|
||||
CheckGlError();
|
||||
|
||||
|
||||
@ -584,13 +595,11 @@ namespace ZL
|
||||
}
|
||||
|
||||
void Game::drawRemoteShips() {
|
||||
// Используем те же константы имен для шейдеров, что и в drawShip
|
||||
static const std::string defaultShaderName = "default";
|
||||
static const std::string vPositionName = "vPosition";
|
||||
static const std::string vTexCoordName = "vTexCoord";
|
||||
static const std::string textureUniformName = "Texture";
|
||||
|
||||
// Активируем шейдер и текстуру (предполагаем, что меш у всех одинаковый)
|
||||
renderer.shaderManager.PushShader(defaultShaderName);
|
||||
renderer.RenderUniform1i(textureUniformName, 0);
|
||||
|
||||
@ -601,45 +610,32 @@ namespace ZL
|
||||
static_cast<float>(Environment::width) / static_cast<float>(Environment::height),
|
||||
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
|
||||
|
||||
// Биндим текстуру корабля один раз для всех удаленных игроков (оптимизация батчинга)
|
||||
glBindTexture(GL_TEXTURE_2D, spaceshipTexture->getTexID());
|
||||
|
||||
auto now = std::chrono::system_clock::now();
|
||||
|
||||
//Apply server delay:
|
||||
now -= std::chrono::milliseconds(CLIENT_DELAY);
|
||||
|
||||
latestRemotePlayers = networkClient->getRemotePlayers();
|
||||
|
||||
// Итерируемся по актуальным данным из extrapolateRemotePlayers
|
||||
for (auto const& [id, remotePlayer] : latestRemotePlayers) {
|
||||
|
||||
if (!remotePlayer.canFetchClientStateAtTime(now))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ClientState playerState = remotePlayer.fetchClientStateAtTime(now);
|
||||
|
||||
|
||||
renderer.PushMatrix();
|
||||
renderer.LoadIdentity();
|
||||
renderer.PushSpecialMatrix(camera.getViewMatrix());
|
||||
|
||||
for (auto const& [id, remotePlayer] : latestRemotePlayers) {
|
||||
if (!remotePlayer.canFetchClientStateAtTime(now)) continue;
|
||||
|
||||
ClientState playerState = remotePlayer.fetchClientStateAtTime(now);
|
||||
|
||||
renderer.PushMatrix();
|
||||
// Позиция удаленного игрока в мире
|
||||
renderer.TranslateMatrix(playerState.position);
|
||||
|
||||
// 3. Поворот врага
|
||||
renderer.RotateMatrix(playerState.rotation);
|
||||
|
||||
renderer.DrawVertexRenderStruct(spaceship);
|
||||
renderer.PopMatrix();
|
||||
|
||||
renderer.PopMatrix();
|
||||
renderer.PopMatrix();
|
||||
}
|
||||
|
||||
renderer.PopMatrix(); // pop special matrix stack (view)
|
||||
renderer.PopMatrix(); // pop identity push
|
||||
|
||||
renderer.PopProjectionMatrix();
|
||||
renderer.DisableVertexAttribArray(vPositionName);
|
||||
renderer.DisableVertexAttribArray(vTexCoordName);
|
||||
@ -648,6 +644,7 @@ namespace ZL
|
||||
CheckGlError();
|
||||
}
|
||||
|
||||
|
||||
void Game::processTickCount() {
|
||||
|
||||
if (lastTickCount == 0) {
|
||||
@ -673,85 +670,80 @@ namespace ZL
|
||||
|
||||
auto now_ms = newTickCount;
|
||||
|
||||
sparkEmitter.update(static_cast<float>(delta));
|
||||
planetObject.update(static_cast<float>(delta));
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Управление: Джойстик (движение) vs Камера (орбита)
|
||||
// ------------------------
|
||||
// Input -> ControlState (joystick)
|
||||
// ------------------------
|
||||
|
||||
float discreteMag = 0.0f;
|
||||
int discreteAngle = -1;
|
||||
|
||||
// 1. Joystick Logic (Movement)
|
||||
auto joystick = uiManager.findJoystick("shipJoystick");
|
||||
if (isUsingJoystick && joystick && joystick->isActive) {
|
||||
float joyX = joystick->getDirectionX();
|
||||
float joyY = joystick->getDirectionY();
|
||||
bool joystickActive = isUsingJoystick && joystick && joystick->isActive;
|
||||
|
||||
if (joystickActive) {
|
||||
float joyX = -joystick->getDirectionX(); // -1..1
|
||||
float joyY = joystick->getDirectionY(); // -1..1 (вверх обычно отрицательный)
|
||||
float magnitude = joystick->getMagnitude();
|
||||
|
||||
if (magnitude > 0.1f) {
|
||||
// Movement logic
|
||||
Environment::shipState.velocity = 0.0f;
|
||||
const float deadzone = 0.1f;
|
||||
if (magnitude > deadzone) {
|
||||
|
||||
float moveSpeed = 0.5f;
|
||||
// forward/right из ТЕКУЩЕГО camYaw (без lock)
|
||||
Vector3f forward(-sinf(camYaw), 0.0f, -cosf(camYaw));
|
||||
Vector3f right(cosf(camYaw), 0.0f, -sinf(camYaw));
|
||||
|
||||
// Local movement vector
|
||||
Vector3f localMove(joyX, 0.0f, joyY);
|
||||
// joyY: если вверх = отрицательный, то "- forward * joyY" даёт движение вперёд при joyY<0
|
||||
Vector3f worldMove = right * joyX - forward * joyY;
|
||||
|
||||
// Transform to world space using current ship rotation
|
||||
Vector3f worldMove = Environment::shipState.rotation * localMove;
|
||||
if (worldMove.squaredNorm() > 1e-6f) {
|
||||
worldMove.normalize();
|
||||
|
||||
// Apply to position
|
||||
Environment::shipState.position += worldMove * moveSpeed * static_cast<float>(delta);
|
||||
float ang = atan2f(worldMove.x(), worldMove.z()); // 0 -> +Z
|
||||
int a = (int)std::round(ang * 180.0f / (float)M_PI);
|
||||
if (a < 0) a += 360;
|
||||
a %= 360;
|
||||
|
||||
// Calculate discrete values for network
|
||||
// Angle 0-359
|
||||
float radians = atan2f(joyY, joyX);
|
||||
discreteAngle = static_cast<int>(radians * 180.0f / M_PI);
|
||||
if (discreteAngle < 0) discreteAngle += 360;
|
||||
discreteAngle = a;
|
||||
|
||||
// Magnitude 0.0-1.0
|
||||
discreteMag = (std::min)(magnitude, 1.0f);
|
||||
discreteMag = std::round(discreteMag * 10.0f) / 10.0f;
|
||||
}
|
||||
}
|
||||
// 2. Camera Drag Logic
|
||||
else if (isDraggingCamera && Environment::tapDownHold && !uiManager.isUiInteraction()) {
|
||||
float diffx = Environment::tapDownCurrentPos(0) - Environment::tapDownStartPos(0);
|
||||
float diffy = Environment::tapDownCurrentPos(1) - Environment::tapDownStartPos(1);
|
||||
|
||||
if (std::abs(diffx) > 1.0f || std::abs(diffy) > 1.0f) {
|
||||
float sensitivity = 0.01f; // Radians per pixel
|
||||
|
||||
camYaw -= diffx * sensitivity; // Yaw: Horizontal drag
|
||||
camPitch -= diffy * sensitivity; // Pitch: Vertical drag
|
||||
|
||||
// Clamp pitch to avoid flipping
|
||||
// -80 to 80 degrees in radians
|
||||
float pitchLimit = 80.0f * static_cast<float>(M_PI) / 180.0f;
|
||||
if (camPitch > pitchLimit) camPitch = pitchLimit;
|
||||
if (camPitch < -pitchLimit) camPitch = -pitchLimit;
|
||||
|
||||
// Reset for incremental update
|
||||
Environment::tapDownStartPos = Environment::tapDownCurrentPos;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Network Update
|
||||
if (discreteAngle != Environment::shipState.discreteAngle || discreteMag != Environment::shipState.discreteMag) {
|
||||
Environment::shipState.discreteAngle = discreteAngle;
|
||||
Environment::shipState.discreteMag = discreteMag;
|
||||
bool changed = false;
|
||||
|
||||
if (discreteAngle != Environment::shipState.discreteAngle) {
|
||||
Environment::shipState.discreteAngle = discreteAngle;
|
||||
changed = true;
|
||||
}
|
||||
if (discreteMag != Environment::shipState.discreteMag) {
|
||||
Environment::shipState.discreteMag = discreteMag;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
// slider value применяем здесь (ты раньше только newShipVelocity менял)
|
||||
//int newVelInt = (int)newShipVelocity;
|
||||
//if (newVelInt != Environment::shipState.selectedVelocity) {
|
||||
// Environment::shipState.selectedVelocity = newVelInt;
|
||||
// changed = true;
|
||||
//}
|
||||
|
||||
if (changed) {
|
||||
std::string msg = "UPD:" + std::to_string(now_ms) + ":" + Environment::shipState.formPingMessageContent();
|
||||
networkClient->Send(msg);
|
||||
//std::cout << "Sending: " << msg << std::endl;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Environment::shipState.simulate_physics(delta);
|
||||
Environment::inverseShipMatrix = Environment::shipState.rotation.inverse();
|
||||
|
||||
@ -1049,8 +1041,8 @@ namespace ZL
|
||||
glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
drawScene();
|
||||
processTickCount();
|
||||
drawScene();
|
||||
|
||||
SDL_GL_SwapWindow(ZL::Environment::window);
|
||||
}
|
||||
@ -1179,9 +1171,25 @@ namespace ZL
|
||||
uiManager.onMouseMove(uiX, uiY);
|
||||
|
||||
if (isDraggingCamera && !uiManager.isUiInteraction()) {
|
||||
Environment::tapDownCurrentPos(0) = static_cast<float>(mx);
|
||||
Environment::tapDownCurrentPos(1) = static_cast<float>(my);
|
||||
float curX = static_cast<float>(mx);
|
||||
float curY = static_cast<float>(my);
|
||||
|
||||
float dx = curX - Environment::tapDownStartPos(0);
|
||||
float dy = curY - Environment::tapDownStartPos(1);
|
||||
|
||||
const float sens = 0.005f; // rad per pixel
|
||||
camYaw -= dx * sens;
|
||||
camPitch -= dy * sens;
|
||||
|
||||
float pitchLimit = 80.0f * static_cast<float>(M_PI) / 180.0f;
|
||||
if (camPitch > pitchLimit) camPitch = pitchLimit;
|
||||
if (camPitch < -pitchLimit) camPitch = -pitchLimit;
|
||||
|
||||
// обновляем стартовую точку, чтобы drag был плавный
|
||||
Environment::tapDownStartPos(0) = curX;
|
||||
Environment::tapDownStartPos(1) = curY;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
12
src/Game.h
12
src/Game.h
@ -66,7 +66,7 @@ namespace ZL {
|
||||
|
||||
std::unordered_map<int, ClientStateInterval> latestRemotePlayers;
|
||||
|
||||
float newShipVelocity = 0;
|
||||
//float newShipVelocity = 0;
|
||||
|
||||
static const size_t CONST_TIMER_INTERVAL = 10;
|
||||
static const size_t CONST_MAX_TIME_INTERVAL = 1000;
|
||||
@ -102,6 +102,12 @@ namespace ZL {
|
||||
float camYaw = 0.0f;
|
||||
float camPitch = 0.0f;
|
||||
|
||||
// Movement direction locking
|
||||
bool isMoving = false; // Is joystick movement active
|
||||
float moveYawLocked = 0.0f; // Locked camera yaw at movement start
|
||||
Vector3f moveForwardLocked = Vector3f::UnitZ(); // Locked forward direction (world space)
|
||||
Vector3f moveRightLocked = Vector3f::UnitX(); // Locked right direction (world space)
|
||||
|
||||
Matrix3f rotateShipMat = Matrix3f::Identity(); // Локальный поворот от джойстика
|
||||
Matrix3f shipWorldOrientation = Matrix3f::Identity(); // Ориентация корабля в мире (независимо от камеры)
|
||||
bool shipMoveLockActive = false;
|
||||
@ -117,6 +123,10 @@ namespace ZL {
|
||||
uint64_t lastExplosionTime = 0;
|
||||
const uint64_t explosionDurationMs = 500;
|
||||
|
||||
float camDistance = 0.0f; // будет равен Environment::zoom
|
||||
float camHeight = 6.0f; // высота над кораблём
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -1,112 +1,128 @@
|
||||
#include "ClientState.h"
|
||||
#include <algorithm> // std::min/max/clamp
|
||||
#include <cmath> // sinf/cosf/atan2f
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// New control model:
|
||||
//
|
||||
// discreteAngle = heading in WORLD XZ plane (0..359)
|
||||
// discreteMag = throttle 0..1 (joystick magnitude)
|
||||
// selectedVelocity = optional 0..10 (for compatibility)
|
||||
//
|
||||
// Movement is along heading direction.
|
||||
// Ship rotates to face heading direction (optional).
|
||||
// ------------------------------------------------------------
|
||||
|
||||
void ClientState::simulate_physics(size_t delta) {
|
||||
if (discreteMag > 0.01f)
|
||||
static inline float DegToRad(float deg) {
|
||||
return deg * static_cast<float>(M_PI) / 180.0f;
|
||||
}
|
||||
|
||||
static inline float Clamp01(float v) {
|
||||
return (std::max)(0.0f, (std::min)(1.0f, v));
|
||||
}
|
||||
|
||||
static inline float Approach(float current, float target, float maxDelta) {
|
||||
if (current < target) return (std::min)(target, current + maxDelta);
|
||||
if (current > target) return (std::max)(target, current - maxDelta);
|
||||
return current;
|
||||
}
|
||||
|
||||
void ClientState::simulate_physics(size_t deltaMs)
|
||||
{
|
||||
float rad = static_cast<float>(discreteAngle) * static_cast<float>(M_PI) / 180.0f;
|
||||
// dt in seconds
|
||||
const float dt = static_cast<float>(deltaMs) / 1000.0f;
|
||||
|
||||
// Целевая угловая скорость (дискретная сила определяет модуль вектора)
|
||||
// Вектор {cos, sin, 0} дает нам направление отклонения джойстика
|
||||
Eigen::Vector3f targetAngularVelDir(sinf(rad), cosf(rad), 0.0f);
|
||||
Eigen::Vector3f targetAngularVelocity = targetAngularVelDir * discreteMag;
|
||||
// ---------- SETTINGS (tweak these) ----------
|
||||
// Max speed in your world units/sec.
|
||||
// Choose something that makes sense vs projectileSpeed (60 in Game.cpp).
|
||||
const float MAX_SPEED = 80.0f;
|
||||
|
||||
Eigen::Vector3f diffVel = targetAngularVelocity - currentAngularVelocity;
|
||||
float diffLen = diffVel.norm();
|
||||
// Accel/decel in units/sec^2
|
||||
const float ACCEL = 120.0f;
|
||||
const float DECEL = 160.0f;
|
||||
|
||||
if (diffLen > 0.0001f) {
|
||||
// Вычисляем, на сколько мы можем изменить скорость в этом кадре
|
||||
float maxChange = ANGULAR_ACCEL * static_cast<float>(delta);
|
||||
// Rotation smoothing factor (0..1 per frame). Uses your constant as "per ms".
|
||||
// With ROTATION_SENSITIVITY=0.002 and delta=10ms -> 0.02 (nice).
|
||||
const float ROT_T = std::clamp(ROTATION_SENSITIVITY * static_cast<float>(deltaMs), 0.0f, 1.0f);
|
||||
// -------------------------------------------
|
||||
|
||||
if (diffLen <= maxChange) {
|
||||
// Если до цели осталось меньше, чем шаг ускорения — просто прыгаем в цель
|
||||
currentAngularVelocity = targetAngularVelocity;
|
||||
}
|
||||
|
||||
else {
|
||||
// Линейно двигаемся в сторону целевого вектора
|
||||
currentAngularVelocity += (diffVel / diffLen) * maxChange;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
float currentSpeed = currentAngularVelocity.norm();
|
||||
|
||||
if (currentSpeed > 0.0001f) {
|
||||
float drop = ANGULAR_ACCEL * static_cast<float>(delta);
|
||||
if (currentSpeed <= drop) {
|
||||
// We keep this for protocol compatibility, but it no longer affects anything.
|
||||
currentAngularVelocity = Eigen::Vector3f::Zero();
|
||||
|
||||
// Input validity
|
||||
const bool hasInput = (discreteAngle >= 0) && (discreteMag > 0.01f);
|
||||
|
||||
// Speed command:
|
||||
// - Prefer discreteMag (joystick magnitude)
|
||||
// - Also accept selectedVelocity (0..10) for compatibility
|
||||
float speed01_fromMag = Clamp01(discreteMag);
|
||||
float speed01_fromSel = Clamp01(static_cast<float>(selectedVelocity) / 10.0f);
|
||||
float speed01 = (std::max)(speed01_fromMag, speed01_fromSel);
|
||||
|
||||
float targetSpeed = hasInput ? (speed01 * MAX_SPEED) : 0.0f;
|
||||
|
||||
// Smooth speed (accelerate/decelerate)
|
||||
if (targetSpeed > velocity) velocity = Approach(velocity, targetSpeed, ACCEL * dt);
|
||||
else velocity = Approach(velocity, targetSpeed, DECEL * dt);
|
||||
|
||||
// If no direction input — only тормозим
|
||||
if (!hasInput) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Heading direction in world XZ plane:
|
||||
// IMPORTANT: your Game.cpp must set discreteAngle consistently with:
|
||||
// ang = atan2(worldMove.x, worldMove.z)
|
||||
// So we reconstruct as:
|
||||
// x = sin(rad), z = cos(rad)
|
||||
const float rad = DegToRad(static_cast<float>(discreteAngle));
|
||||
|
||||
Eigen::Vector3f moveDirWorld(
|
||||
sinf(rad),
|
||||
0.0f,
|
||||
cosf(rad)
|
||||
);
|
||||
|
||||
if (moveDirWorld.squaredNorm() > 1e-6f) {
|
||||
moveDirWorld.normalize();
|
||||
}
|
||||
else {
|
||||
// Уменьшаем модуль вектора, сохраняя направление
|
||||
currentAngularVelocity -= (currentAngularVelocity / currentSpeed) * drop;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
float speedScale = currentAngularVelocity.norm();
|
||||
if (speedScale > 0.0001f) {
|
||||
// Коэффициент чувствительности вращения
|
||||
// Move in world
|
||||
position += moveDirWorld * (velocity * dt);
|
||||
|
||||
float deltaAlpha = speedScale * static_cast<float>(delta) * ROTATION_SENSITIVITY;
|
||||
|
||||
Eigen::Vector3f axis = currentAngularVelocity.normalized();
|
||||
Eigen::Quaternionf rotateQuat(Eigen::AngleAxisf(deltaAlpha, axis));
|
||||
|
||||
rotation = rotation * rotateQuat.toRotationMatrix();
|
||||
}
|
||||
|
||||
|
||||
// 4. Линейное изменение линейной скорости
|
||||
float shipDesiredVelocity = selectedVelocity * 100.f;
|
||||
|
||||
if (velocity < shipDesiredVelocity)
|
||||
// Rotate ship to face movement direction (optional).
|
||||
// Ship local forward is (0,0,-1) (as in your fireProjectiles).
|
||||
// We need yaw so that rotation * (0,0,-1) == moveDirWorld.
|
||||
// That yaw is: yaw = atan2(-dir.x, -dir.z)
|
||||
{
|
||||
velocity += delta * SHIP_ACCEL;
|
||||
if (velocity > shipDesiredVelocity)
|
||||
{
|
||||
velocity = shipDesiredVelocity;
|
||||
}
|
||||
}
|
||||
else if (velocity > shipDesiredVelocity)
|
||||
{
|
||||
velocity -= delta * SHIP_ACCEL;
|
||||
if (velocity < shipDesiredVelocity)
|
||||
{
|
||||
velocity = shipDesiredVelocity;
|
||||
float desiredYaw = atan2f(-moveDirWorld.x(), -moveDirWorld.z());
|
||||
|
||||
Eigen::Quaternionf qCur(rotation);
|
||||
Eigen::Quaternionf qTar(Eigen::AngleAxisf(desiredYaw, Eigen::Vector3f::UnitY()));
|
||||
|
||||
Eigen::Quaternionf qNew = qCur.slerp(ROT_T, qTar).normalized();
|
||||
rotation = qNew.toRotationMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
if (fabs(velocity) > 0.01f)
|
||||
void ClientState::apply_lag_compensation(std::chrono::system_clock::time_point nowTime)
|
||||
{
|
||||
Eigen::Vector3f velocityDirection = { 0,0, -velocity * delta / 1000.f };
|
||||
Eigen::Vector3f velocityDirectionAdjusted = rotation * velocityDirection;
|
||||
position = position + velocityDirectionAdjusted;
|
||||
}
|
||||
}
|
||||
|
||||
void ClientState::apply_lag_compensation(std::chrono::system_clock::time_point nowTime) {
|
||||
|
||||
// 2. Вычисляем задержку
|
||||
long long deltaMs = 0;
|
||||
if (nowTime > lastUpdateServerTime) {
|
||||
deltaMs = std::chrono::duration_cast<std::chrono::milliseconds>(nowTime - lastUpdateServerTime).count();
|
||||
}
|
||||
|
||||
// 3. Защита от слишком больших скачков (Clamp)
|
||||
// Если лаг более 500мс, ограничиваем его, чтобы избежать резких рывков
|
||||
long long final_lag_ms = deltaMs;//min(deltaMs, 500ll);
|
||||
|
||||
long long final_lag_ms = deltaMs; // можно clamp, если нужно
|
||||
if (final_lag_ms > 0) {
|
||||
// Доматываем симуляцию на величину задержки
|
||||
// Мы предполагаем, что за это время параметры управления не менялись
|
||||
simulate_physics(final_lag_ms);
|
||||
simulate_physics(static_cast<size_t>(final_lag_ms));
|
||||
}
|
||||
}
|
||||
|
||||
void ClientState::handle_full_sync(const std::vector<std::string>& parts, int startFrom) {
|
||||
// Позиция
|
||||
void ClientState::handle_full_sync(const std::vector<std::string>& parts, int startFrom)
|
||||
{
|
||||
position = { std::stof(parts[startFrom]), std::stof(parts[startFrom + 1]), std::stof(parts[startFrom + 2]) };
|
||||
|
||||
Eigen::Quaternionf q(
|
||||
@ -116,10 +132,12 @@ void ClientState::handle_full_sync(const std::vector<std::string>& parts, int st
|
||||
std::stof(parts[startFrom + 6]));
|
||||
rotation = q.toRotationMatrix();
|
||||
|
||||
// For compatibility: keep reading it, but we won't use it anymore
|
||||
currentAngularVelocity = Eigen::Vector3f{
|
||||
std::stof(parts[startFrom + 7]),
|
||||
std::stof(parts[startFrom + 8]),
|
||||
std::stof(parts[startFrom + 9]) };
|
||||
|
||||
velocity = std::stof(parts[startFrom + 10]);
|
||||
selectedVelocity = std::stoi(parts[startFrom + 11]);
|
||||
discreteMag = std::stof(parts[startFrom + 12]);
|
||||
@ -130,87 +148,72 @@ std::string ClientState::formPingMessageContent()
|
||||
{
|
||||
Eigen::Quaternionf q(rotation);
|
||||
|
||||
std::string pingMsg = std::to_string(position.x()) + ":"
|
||||
+ std::to_string(position.y()) + ":"
|
||||
+ std::to_string(position.z()) + ":"
|
||||
+ std::to_string(q.w()) + ":"
|
||||
+ std::to_string(q.x()) + ":"
|
||||
+ std::to_string(q.y()) + ":"
|
||||
+ std::to_string(q.z()) + ":"
|
||||
+ std::to_string(currentAngularVelocity.x()) + ":"
|
||||
+ std::to_string(currentAngularVelocity.y()) + ":"
|
||||
+ std::to_string(currentAngularVelocity.z()) + ":"
|
||||
+ std::to_string(velocity) + ":"
|
||||
+ std::to_string(selectedVelocity) + ":"
|
||||
+ std::to_string(discreteMag) + ":" // Используем те же static переменные из блока ROT
|
||||
+ std::to_string(discreteAngle);
|
||||
std::string pingMsg =
|
||||
std::to_string(position.x()) + ":" +
|
||||
std::to_string(position.y()) + ":" +
|
||||
std::to_string(position.z()) + ":" +
|
||||
std::to_string(q.w()) + ":" +
|
||||
std::to_string(q.x()) + ":" +
|
||||
std::to_string(q.y()) + ":" +
|
||||
std::to_string(q.z()) + ":" +
|
||||
std::to_string(currentAngularVelocity.x()) + ":" +
|
||||
std::to_string(currentAngularVelocity.y()) + ":" +
|
||||
std::to_string(currentAngularVelocity.z()) + ":" +
|
||||
std::to_string(velocity) + ":" +
|
||||
std::to_string(selectedVelocity) + ":" +
|
||||
std::to_string(discreteMag) + ":" +
|
||||
std::to_string(discreteAngle);
|
||||
|
||||
return pingMsg;
|
||||
}
|
||||
|
||||
|
||||
void ClientStateInterval::add_state(const ClientState& state)
|
||||
{
|
||||
auto nowTime = std::chrono::system_clock::now();
|
||||
|
||||
if (timedStates.size() > 0 && timedStates[timedStates.size() - 1].lastUpdateServerTime == state.lastUpdateServerTime)
|
||||
{
|
||||
timedStates[timedStates.size() - 1] = state;
|
||||
if (!timedStates.empty() && timedStates.back().lastUpdateServerTime == state.lastUpdateServerTime) {
|
||||
timedStates.back() = state;
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
timedStates.push_back(state);
|
||||
}
|
||||
|
||||
auto cutoff_time = nowTime - std::chrono::milliseconds(CUTOFF_TIME);
|
||||
|
||||
while (timedStates.size() > 0 && timedStates[0].lastUpdateServerTime < cutoff_time)
|
||||
{
|
||||
while (!timedStates.empty() && timedStates.front().lastUpdateServerTime < cutoff_time) {
|
||||
timedStates.erase(timedStates.begin());
|
||||
}
|
||||
}
|
||||
|
||||
bool ClientStateInterval::canFetchClientStateAtTime(std::chrono::system_clock::time_point targetTime) const
|
||||
{
|
||||
if (timedStates.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (timedStates[0].lastUpdateServerTime > targetTime)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (timedStates.empty()) return false;
|
||||
if (timedStates.front().lastUpdateServerTime > targetTime) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
ClientState ClientStateInterval::fetchClientStateAtTime(std::chrono::system_clock::time_point targetTime) const {
|
||||
ClientState ClientStateInterval::fetchClientStateAtTime(std::chrono::system_clock::time_point targetTime) const
|
||||
{
|
||||
if (timedStates.empty()) {
|
||||
throw std::runtime_error("No timed client states available");
|
||||
}
|
||||
if (timedStates.front().lastUpdateServerTime > targetTime) {
|
||||
throw std::runtime_error("Found time but it is in future");
|
||||
}
|
||||
|
||||
ClientState closestState;
|
||||
|
||||
if (timedStates.empty())
|
||||
{
|
||||
throw std::runtime_error("No timed client states available");
|
||||
return closestState;
|
||||
}
|
||||
if (timedStates[0].lastUpdateServerTime > targetTime)
|
||||
{
|
||||
throw std::runtime_error("Found time but it is in future");
|
||||
return closestState;
|
||||
}
|
||||
if (timedStates.size() == 1)
|
||||
{
|
||||
if (timedStates.size() == 1) {
|
||||
closestState = timedStates[0];
|
||||
closestState.apply_lag_compensation(targetTime);
|
||||
return closestState;
|
||||
}
|
||||
|
||||
|
||||
for (size_t i = 0; i < timedStates.size() - 1; ++i)
|
||||
{
|
||||
for (size_t i = 0; i + 1 < timedStates.size(); ++i) {
|
||||
const auto& earlierState = timedStates[i];
|
||||
const auto& laterState = timedStates[i + 1];
|
||||
if (earlierState.lastUpdateServerTime <= targetTime && laterState.lastUpdateServerTime >= targetTime)
|
||||
|
||||
if (earlierState.lastUpdateServerTime <= targetTime &&
|
||||
laterState.lastUpdateServerTime >= targetTime)
|
||||
{
|
||||
closestState = earlierState;
|
||||
closestState.apply_lag_compensation(targetTime);
|
||||
@ -218,7 +221,7 @@ ClientState ClientStateInterval::fetchClientStateAtTime(std::chrono::system_cloc
|
||||
}
|
||||
}
|
||||
|
||||
closestState = timedStates[timedStates.size() - 1];
|
||||
closestState = timedStates.back();
|
||||
closestState.apply_lag_compensation(targetTime);
|
||||
return closestState;
|
||||
}
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
#include "Environment.h"
|
||||
#include "StoneObject.h"
|
||||
#include "utils/TaskManager.h"
|
||||
|
||||
#include <algorithm>
|
||||
namespace ZL {
|
||||
|
||||
#if defined EMSCRIPTEN || defined __ANDROID__
|
||||
@ -58,10 +58,11 @@ namespace ZL {
|
||||
return rot;
|
||||
}
|
||||
|
||||
PlanetObject::PlanetObject(Renderer& iRenderer, TaskManager& iTaskManager, MainThreadHandler& iMainThreadHandler)
|
||||
PlanetObject::PlanetObject(Renderer& iRenderer, TaskManager& iTaskManager, MainThreadHandler& iMainThreadHandler, Camera& iCamera)
|
||||
: renderer(iRenderer),
|
||||
taskManager(iTaskManager),
|
||||
mainThreadHandler(iMainThreadHandler)
|
||||
mainThreadHandler(iMainThreadHandler),
|
||||
camera(iCamera)
|
||||
{
|
||||
}
|
||||
|
||||
@ -278,11 +279,10 @@ namespace ZL {
|
||||
|
||||
drawPlanet(renderer);
|
||||
#if ENABLE_STONES
|
||||
drawStones(renderer);
|
||||
//drawStones(renderer);
|
||||
#endif
|
||||
drawAtmosphere(renderer);
|
||||
//drawAtmosphere(renderer);
|
||||
}
|
||||
|
||||
void PlanetObject::drawPlanet(Renderer& renderer)
|
||||
{
|
||||
static const std::string defaultShaderName = "planetLand";
|
||||
@ -291,7 +291,6 @@ namespace ZL {
|
||||
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(defaultShaderName);
|
||||
|
||||
@ -302,79 +301,69 @@ namespace ZL {
|
||||
renderer.EnableVertexAttribArray("vBinormal");
|
||||
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,
|
||||
renderer.PushPerspectiveProjectionMatrix(
|
||||
1.0 / 1.5,
|
||||
static_cast<float>(Environment::width) / static_cast<float>(Environment::height),
|
||||
currentZNear, currentZFar);
|
||||
currentZNear, currentZFar
|
||||
);
|
||||
|
||||
// View from Camera
|
||||
renderer.PushMatrix();
|
||||
renderer.LoadIdentity();
|
||||
renderer.TranslateMatrix({ 0,0, -1.0f * Environment::zoom });
|
||||
renderer.RotateMatrix(Environment::inverseShipMatrix);
|
||||
renderer.PushSpecialMatrix(camera.getViewMatrix());
|
||||
|
||||
renderer.TranslateMatrix(-Environment::shipState.position);
|
||||
|
||||
const Matrix4f viewMatrix = renderer.GetCurrentModelViewMatrix();
|
||||
// Planet at world origin (если у тебя есть PLANET_CENTER_OFFSET — добавь Translate туда)
|
||||
// renderer.TranslateMatrix(PlanetData::PLANET_CENTER_OFFSET);
|
||||
|
||||
renderer.RenderUniform1i("Texture", 0);
|
||||
renderer.RenderUniform1i("BakedTexture", 1);
|
||||
|
||||
// Камера (для освещения) — именно позиция камеры, а не shipState.position
|
||||
Eigen::Vector3f camPos = camera.getPosition();
|
||||
renderer.RenderUniform3fv("uViewPos", camPos.data());
|
||||
|
||||
Triangle tr = planetData.getLodLevel(planetData.getCurrentLodIndex()).triangles[0]; // Берем базовый треугольник
|
||||
Matrix3f mr = GetRotationForTriangle(tr); // Та же матрица, что и при запекании
|
||||
|
||||
// Позиция камеры (корабля) в мире
|
||||
renderer.RenderUniform3fv("uViewPos", Environment::shipState.position.data());
|
||||
|
||||
//renderer.RenderUniform1f("uHeightScale", 0.08f);
|
||||
renderer.RenderUniform1f("uHeightScale", 0.0f);
|
||||
|
||||
renderer.RenderUniform1f("uDistanceToPlanetSurface", dist);
|
||||
renderer.RenderUniform1f("uCurrentZFar", currentZFar);
|
||||
|
||||
// Направление на солнце в мировом пространстве
|
||||
Vector3f sunDirWorld = Vector3f(1.0f, -1.0f, -1.0f).normalized();
|
||||
Eigen::Vector3f sunDirWorld = Eigen::Vector3f(1.0f, -1.0f, -1.0f).normalized();
|
||||
renderer.RenderUniform3fv("uLightDirWorld", sunDirWorld.data());
|
||||
|
||||
// Направление от центра планеты к игроку для расчета дня/ночи
|
||||
Vector3f playerDirWorld = Environment::shipState.position.normalized();
|
||||
Eigen::Vector3f playerDirWorld = Environment::shipState.position.normalized();
|
||||
renderer.RenderUniform3fv("uPlayerDirWorld", playerDirWorld.data());
|
||||
|
||||
// Тот же фактор освещенности игрока
|
||||
float playerLightFactor = playerDirWorld.dot(-sunDirWorld);
|
||||
playerLightFactor = max(0.0f, (playerLightFactor + 0.2f) / 1.2f);
|
||||
float playerLightFactor = (std::max)(0.0f, (playerDirWorld.dot(-sunDirWorld) + 0.2f) / 1.2f);
|
||||
renderer.RenderUniform1f("uPlayerLightFactor", playerLightFactor);
|
||||
|
||||
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glBindTexture(GL_TEXTURE_2D, stoneMapFB->getTextureID());
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, sandTexture->getTexID());
|
||||
//glBindTexture(GL_TEXTURE_2D, stoneMapFB->getTextureID());
|
||||
|
||||
renderer.DrawVertexRenderStruct(planetRenderStruct);
|
||||
CheckGlError();
|
||||
|
||||
renderer.PopMatrix();
|
||||
renderer.PopMatrix(); // special matrix
|
||||
renderer.PopMatrix(); // original push
|
||||
|
||||
renderer.PopProjectionMatrix();
|
||||
|
||||
renderer.DisableVertexAttribArray(vTexCoordName);
|
||||
renderer.DisableVertexAttribArray(vNormalName);
|
||||
renderer.DisableVertexAttribArray("vTangent");
|
||||
renderer.DisableVertexAttribArray("vBinormal");
|
||||
renderer.DisableVertexAttribArray(vColorName);
|
||||
renderer.DisableVertexAttribArray(vPositionName);
|
||||
|
||||
renderer.shaderManager.PopShader();
|
||||
CheckGlError();
|
||||
|
||||
|
||||
}
|
||||
|
||||
void PlanetObject::drawStones(Renderer& renderer)
|
||||
{
|
||||
static const std::string defaultShaderName2 = "planetStone";
|
||||
@ -382,86 +371,81 @@ namespace ZL {
|
||||
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.RenderUniform1i("Texture", 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,
|
||||
renderer.PushPerspectiveProjectionMatrix(
|
||||
1.0 / 1.5,
|
||||
static_cast<float>(Environment::width) / static_cast<float>(Environment::height),
|
||||
currentZNear, currentZFar);
|
||||
currentZNear, currentZFar
|
||||
);
|
||||
|
||||
renderer.PushMatrix();
|
||||
renderer.LoadIdentity();
|
||||
renderer.TranslateMatrix({ 0,0, -1.0f * Environment::zoom });
|
||||
renderer.RotateMatrix(Environment::inverseShipMatrix);
|
||||
renderer.TranslateMatrix(-Environment::shipState.position);
|
||||
renderer.PushSpecialMatrix(camera.getViewMatrix());
|
||||
|
||||
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();
|
||||
|
||||
Eigen::Vector3f camPos = camera.getPosition();
|
||||
renderer.RenderUniform3fv("uViewPos", camPos.data());
|
||||
|
||||
Eigen::Vector3f sunDirWorld = Eigen::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);
|
||||
Eigen::Vector3f playerDirWorld = Environment::shipState.position.normalized();
|
||||
float playerLightFactor = (std::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);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, stoneTexture->getTexID());
|
||||
|
||||
for (int i : triangleIndicesToDraw) {
|
||||
// КРИТИЧЕСКОЕ ИЗМЕНЕНИЕ:
|
||||
// Проверяем, что данные не просто существуют, а загружены в GPU
|
||||
if (planetStones.statuses[i] == ChunkStatus::Live) {
|
||||
// Дополнительная проверка на наличие данных (на всякий случай)
|
||||
if (stonesToRender[i].data.PositionData.size() > 0) {
|
||||
if (!stonesToRender[i].data.PositionData.empty()) {
|
||||
renderer.DrawVertexRenderStruct(stonesToRender[i]);
|
||||
}
|
||||
}
|
||||
// Если статус Generating или Empty — мы просто пропускаем этот индекс.
|
||||
// Камни появятся на экране плавно, как только отработает TaskManager и RefreshVBO.
|
||||
}
|
||||
|
||||
CheckGlError();
|
||||
glDisable(GL_BLEND);
|
||||
glDisable(GL_CULL_FACE);
|
||||
|
||||
renderer.PopMatrix();
|
||||
renderer.PopMatrix(); // special
|
||||
renderer.PopMatrix(); // original
|
||||
renderer.PopProjectionMatrix();
|
||||
|
||||
renderer.DisableVertexAttribArray(vTexCoordName);
|
||||
renderer.DisableVertexAttribArray(vNormalName);
|
||||
renderer.DisableVertexAttribArray(vColorName);
|
||||
renderer.DisableVertexAttribArray(vPositionName);
|
||||
|
||||
renderer.shaderManager.PopShader();
|
||||
CheckGlError();
|
||||
|
||||
glClear(GL_DEPTH_BUFFER_BIT);
|
||||
}
|
||||
|
||||
void PlanetObject::drawAtmosphere(Renderer& renderer) {
|
||||
void PlanetObject::drawAtmosphere(Renderer& renderer)
|
||||
{
|
||||
static const std::string defaultShaderName = "defaultAtmosphere";
|
||||
//static const std::string defaultShaderName = "defaultColor";
|
||||
static const std::string vPositionName = "vPosition";
|
||||
static const std::string vNormalName = "vNormal";
|
||||
//glClear(GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
glDepthMask(GL_FALSE);
|
||||
|
||||
renderer.shaderManager.PushShader(defaultShaderName);
|
||||
@ -473,90 +457,65 @@ namespace ZL {
|
||||
float currentZNear = zRange.first;
|
||||
float currentZFar = zRange.second;
|
||||
|
||||
if (currentZNear < 200)
|
||||
{
|
||||
if (currentZNear < 200) {
|
||||
currentZNear = 200;
|
||||
currentZFar = currentZNear * 100;
|
||||
}
|
||||
|
||||
// 2. Применяем динамическую матрицу проекции
|
||||
renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5,
|
||||
renderer.PushPerspectiveProjectionMatrix(
|
||||
1.0 / 1.5,
|
||||
static_cast<float>(Environment::width) / static_cast<float>(Environment::height),
|
||||
currentZNear, currentZFar);
|
||||
currentZNear, currentZFar
|
||||
);
|
||||
|
||||
renderer.PushMatrix();
|
||||
renderer.LoadIdentity();
|
||||
renderer.TranslateMatrix({ 0,0, -1.0f * Environment::zoom });
|
||||
renderer.RotateMatrix(Environment::inverseShipMatrix);
|
||||
renderer.TranslateMatrix(-Environment::shipState.position);
|
||||
|
||||
const Matrix4f viewMatrix = renderer.GetCurrentModelViewMatrix();
|
||||
|
||||
Vector3f color = { 0.0, 0.5, 1.0 };
|
||||
|
||||
renderer.RenderUniform3fv("uColor", color.data());
|
||||
renderer.PushSpecialMatrix(camera.getViewMatrix());
|
||||
|
||||
const Eigen::Matrix4f viewMatrix = renderer.GetCurrentModelViewMatrix();
|
||||
renderer.RenderUniformMatrix4fv("ModelViewMatrix", false, viewMatrix.data());
|
||||
|
||||
Eigen::Vector3f color = { 0.0f, 0.5f, 1.0f };
|
||||
renderer.RenderUniform3fv("uColor", color.data());
|
||||
renderer.RenderUniform3fv("uSkyColor", color.data());
|
||||
renderer.RenderUniform1f("uDistanceToPlanetSurface", dist);
|
||||
|
||||
// В начале drawAtmosphere или как uniform
|
||||
//float time = SDL_GetTicks() / 1000.0f;
|
||||
//renderer.RenderUniform1f("uTime", time);
|
||||
|
||||
// В методе PlanetObject::drawAtmosphere
|
||||
Vector3f worldLightDir = Vector3f(1.0f, -1.0f, -1.0f).normalized();
|
||||
|
||||
// Получаем текущую матрицу вида (ModelView без трансляции объекта)
|
||||
// В вашем движке это Environment::inverseShipMatrix
|
||||
Matrix3f viewMatrix2 = Environment::inverseShipMatrix;
|
||||
|
||||
// Трансформируем вектор света в пространство вида
|
||||
Vector3f viewLightDir = viewMatrix2 * worldLightDir;
|
||||
|
||||
// Передаем инвертированный вектор (направление НА источник)
|
||||
Vector3f lightToSource = -viewLightDir.normalized();
|
||||
renderer.RenderUniform3fv("uLightDirView", lightToSource.data());
|
||||
|
||||
// В методе Game::drawCubemap
|
||||
//renderer.RenderUniform1f("uTime", SDL_GetTicks() / 1000.0f);
|
||||
// Направление света в мировом пространстве для освещения облаков
|
||||
//Vector3f worldLightDir = Vector3f(1.0f, -1.0f, -1.0f).normalized();
|
||||
Eigen::Vector3f worldLightDir = Eigen::Vector3f(1.0f, -1.0f, -1.0f).normalized();
|
||||
renderer.RenderUniform3fv("uWorldLightDir", worldLightDir.data());
|
||||
|
||||
// 1. Рассчитываем uPlayerLightFactor (как в Game.cpp)
|
||||
Vector3f playerDirWorld = Environment::shipState.position.normalized();
|
||||
Vector3f sunDirWorld = Vector3f(1.0f, -1.0f, -1.0f).normalized();
|
||||
// Свет в view-space: берём матрицу вида (верхний левый 3x3)
|
||||
Eigen::Matrix3f viewRot = camera.getViewMatrix().block<3, 3>(0, 0);
|
||||
Eigen::Vector3f viewLightDir = (viewRot * worldLightDir).normalized();
|
||||
Eigen::Vector3f lightToSource = -viewLightDir;
|
||||
renderer.RenderUniform3fv("uLightDirView", lightToSource.data());
|
||||
|
||||
// Насколько игрок на свету
|
||||
float playerLightFactor = playerDirWorld.dot(-sunDirWorld);
|
||||
playerLightFactor = max(0.0f, (playerLightFactor + 0.2f) / 1.2f); // Мягкий порог
|
||||
|
||||
// 2. ОБЯЗАТЕЛЬНО передаем в шейдер
|
||||
renderer.RenderUniform1f("uPlayerLightFactor", playerLightFactor);
|
||||
|
||||
// 3. Убедитесь, что uSkyColor тоже передан (в коде выше его не было)
|
||||
renderer.RenderUniform3fv("uSkyColor", color.data());
|
||||
|
||||
//Vector3f playerDirWorld = Environment::shipState.position.normalized();
|
||||
Eigen::Vector3f playerDirWorld = Environment::shipState.position.normalized();
|
||||
renderer.RenderUniform3fv("uPlayerDirWorld", playerDirWorld.data());
|
||||
|
||||
Eigen::Vector3f sunDirWorld = worldLightDir;
|
||||
float playerLightFactor = (std::max)(0.0f, (playerDirWorld.dot(-sunDirWorld) + 0.2f) / 1.2f);
|
||||
renderer.RenderUniform1f("uPlayerLightFactor", playerLightFactor);
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE);// Аддитивное смешивание для эффекта свечения
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
|
||||
|
||||
renderer.DrawVertexRenderStruct(planetAtmosphereRenderStruct);
|
||||
|
||||
glDisable(GL_BLEND);
|
||||
glDepthMask(GL_TRUE);
|
||||
renderer.PopMatrix();
|
||||
|
||||
renderer.PopMatrix(); // special
|
||||
renderer.PopMatrix(); // original
|
||||
renderer.PopProjectionMatrix();
|
||||
|
||||
renderer.DisableVertexAttribArray(vNormalName);
|
||||
renderer.DisableVertexAttribArray(vPositionName);
|
||||
|
||||
renderer.shaderManager.PopShader();
|
||||
CheckGlError();
|
||||
|
||||
}
|
||||
|
||||
|
||||
float PlanetObject::distanceToPlanetSurface(const Vector3f& viewerPosition)
|
||||
{
|
||||
return planetData.distanceToPlanetSurfaceFast(viewerPosition);
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
#include "render/FrameBuffer.h"
|
||||
#include <queue>
|
||||
#include <mutex>
|
||||
|
||||
#include "render/Camera.h"
|
||||
namespace ZL {
|
||||
class TaskManager;
|
||||
class MainThreadHandler;
|
||||
@ -47,8 +47,9 @@ namespace ZL {
|
||||
Renderer& renderer;
|
||||
TaskManager& taskManager;
|
||||
MainThreadHandler& mainThreadHandler;
|
||||
Camera& camera;
|
||||
public:
|
||||
PlanetObject(Renderer& iRenderer, TaskManager& iTaskManager, MainThreadHandler& iMainThreadHandler);
|
||||
PlanetObject(Renderer& iRenderer, TaskManager& iTaskManager, MainThreadHandler& iMainThreadHandler, Camera& iCamera);
|
||||
|
||||
void init();
|
||||
void update(float deltaTimeMs);
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
#include "render/Camera.h"
|
||||
#include <cmath>
|
||||
|
||||
namespace ZL {
|
||||
|
||||
@ -8,50 +9,47 @@ namespace ZL {
|
||||
|
||||
Camera::Camera()
|
||||
: position(Vector3f::Zero())
|
||||
, target(Vector3f::Zero())
|
||||
, rotation(Matrix3f::Identity())
|
||||
{
|
||||
}
|
||||
|
||||
void Camera::follow(const Vector3f& targetPos, const Matrix3f& targetRot, float distance, float height)
|
||||
{
|
||||
Vector3f offset(0.0f, height, distance);
|
||||
Vector3f globalOffset = targetRot * offset;
|
||||
|
||||
position = targetPos + globalOffset;
|
||||
rotation = targetRot;
|
||||
}
|
||||
|
||||
void Camera::followOrbit(const Vector3f& targetPos, float yaw, float pitch, float distance, float height)
|
||||
{
|
||||
// Convert yaw/pitch to rotation matrix
|
||||
// Yaw (Y-axis), Pitch (X-axis)
|
||||
target = targetPos;
|
||||
|
||||
Eigen::AngleAxisf yawRot(yaw, Vector3f::UnitY());
|
||||
Eigen::AngleAxisf pitchRot(pitch, Vector3f::UnitX());
|
||||
float cosP = std::cos(pitch);
|
||||
float sinP = std::sin(pitch);
|
||||
float cosY = std::cos(yaw);
|
||||
float sinY = std::sin(yaw);
|
||||
|
||||
// Combine rotations: Yaw * Pitch (order matters)
|
||||
Eigen::Quaternionf q = yawRot * pitchRot;
|
||||
rotation = q.toRotationMatrix();
|
||||
Vector3f offset;
|
||||
offset.x() = distance * sinY * cosP;
|
||||
offset.y() = height + distance * sinP;
|
||||
offset.z() = distance * cosY * cosP;
|
||||
|
||||
Vector3f offset(0.0f, height, distance);
|
||||
Vector3f globalOffset = rotation * offset;
|
||||
position = target + offset;
|
||||
|
||||
position = targetPos + globalOffset;
|
||||
// Build camera basis to look at target
|
||||
Vector3f forward = (target - position).normalized();
|
||||
Vector3f worldUp(0.0f, 1.0f, 0.0f);
|
||||
|
||||
Vector3f right = worldUp.cross(forward).normalized();
|
||||
Vector3f up = forward.cross(right);
|
||||
|
||||
rotation.col(0) = right;
|
||||
rotation.col(1) = up;
|
||||
rotation.col(2) = -forward; // OpenGL convention
|
||||
}
|
||||
|
||||
Matrix4f Camera::getViewMatrix() const
|
||||
{
|
||||
// View Matrix = (Translate * Rotate)^-1
|
||||
// = Rotate^-1 * Translate^-1
|
||||
|
||||
Matrix3f R_inv = rotation.transpose();
|
||||
Vector3f T_inv = -position;
|
||||
|
||||
Matrix4f view = Matrix4f::Identity();
|
||||
|
||||
view.block<3, 3>(0, 0) = R_inv;
|
||||
view.block<3,1>(0,3) = R_inv * T_inv;
|
||||
|
||||
view.block<3, 1>(0, 3) = R_inv * (-position);
|
||||
return view;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ZL
|
||||
|
||||
@ -7,19 +7,17 @@ namespace ZL {
|
||||
public:
|
||||
Camera();
|
||||
|
||||
// Обновление позиции камеры (например, слежение за кораблем)
|
||||
void follow(const Eigen::Vector3f& targetPos, const Eigen::Matrix3f& targetRot, float distance, float height);
|
||||
|
||||
// Follow target with orbital rotation (yaw/pitch) independent of target rotation
|
||||
// Орбитальная камера вокруг targetPos (yaw вокруг Y, pitch вокруг X)
|
||||
void followOrbit(const Eigen::Vector3f& targetPos, float yaw, float pitch, float distance, float height);
|
||||
|
||||
// Получить матрицу вида (View Matrix) для шейдера
|
||||
Eigen::Matrix4f getViewMatrix() const;
|
||||
|
||||
Eigen::Vector3f getPosition() const { return position; }
|
||||
Eigen::Vector3f getTarget() const { return target; }
|
||||
|
||||
private:
|
||||
Eigen::Vector3f position;
|
||||
Eigen::Matrix3f rotation; // Ориентация камеры
|
||||
Eigen::Vector3f target;
|
||||
Eigen::Matrix3f rotation; // columns: right, up, -forward
|
||||
};
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user