Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d76342f3fa | |||
| fb4a860773 | |||
| 47da35a401 | |||
| 0a72040110 | |||
| 1dcdf10d6a | |||
|
|
ed746891b7 | ||
| 3bd3202bf8 | |||
| b7f5f43777 | |||
| 2ad6ebf38d | |||
| 96deb8b393 | |||
| 47fe0c4719 | |||
| da8946e9ef |
@ -18,6 +18,8 @@ add_executable(space-game001
|
|||||||
../src/Environment.h
|
../src/Environment.h
|
||||||
../src/render/Renderer.cpp
|
../src/render/Renderer.cpp
|
||||||
../src/render/Renderer.h
|
../src/render/Renderer.h
|
||||||
|
../src/render/Camera.cpp
|
||||||
|
../src/render/Camera.h
|
||||||
../src/render/ShaderManager.cpp
|
../src/render/ShaderManager.cpp
|
||||||
../src/render/ShaderManager.h
|
../src/render/ShaderManager.h
|
||||||
../src/render/TextureManager.cpp
|
../src/render/TextureManager.cpp
|
||||||
|
|||||||
@ -154,8 +154,8 @@
|
|||||||
{
|
{
|
||||||
"type": "Button",
|
"type": "Button",
|
||||||
"name": "shootButton",
|
"name": "shootButton",
|
||||||
"x": 100,
|
"x": 1115,
|
||||||
"y": 100,
|
"y": 0,
|
||||||
"width": 100,
|
"width": 100,
|
||||||
"height": 100,
|
"height": 100,
|
||||||
"textures": {
|
"textures": {
|
||||||
|
|||||||
BIN
resources/joystick_base.png
(Stored with Git LFS)
Normal file
BIN
resources/joystick_base.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
resources/joystick_knob.png
(Stored with Git LFS)
Normal file
BIN
resources/joystick_knob.png
(Stored with Git LFS)
Normal file
Binary file not shown.
348
src/Game.cpp
348
src/Game.cpp
@ -1,4 +1,5 @@
|
|||||||
#include "Game.h"
|
#include "GameConfig.h"
|
||||||
|
#include "Game.h"
|
||||||
#include "AnimatedModel.h"
|
#include "AnimatedModel.h"
|
||||||
#include "BoneAnimatedModel.h"
|
#include "BoneAnimatedModel.h"
|
||||||
#include "planet/PlanetData.h"
|
#include "planet/PlanetData.h"
|
||||||
@ -116,8 +117,9 @@ namespace ZL
|
|||||||
, glContext(nullptr)
|
, glContext(nullptr)
|
||||||
, newTickCount(0)
|
, newTickCount(0)
|
||||||
, lastTickCount(0)
|
, lastTickCount(0)
|
||||||
, planetObject(renderer, taskManager, mainThreadHandler)
|
, planetObject(renderer, taskManager, mainThreadHandler, camera)
|
||||||
{
|
{
|
||||||
|
Environment::shipState.selectedVelocity = 0;
|
||||||
projectiles.reserve(maxProjectiles);
|
projectiles.reserve(maxProjectiles);
|
||||||
for (int i = 0; i < maxProjectiles; ++i) {
|
for (int i = 0; i < maxProjectiles; ++i) {
|
||||||
projectiles.emplace_back(std::make_unique<Projectile>());
|
projectiles.emplace_back(std::make_unique<Projectile>());
|
||||||
@ -236,12 +238,22 @@ namespace ZL
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
uiManager.setSliderCallback("velocitySlider", [this](const std::string& name, float value) {
|
//uiManager.setSliderCallback("velocitySlider", [this](const std::string& name, float value) {
|
||||||
int newVel = roundf(value * 10);
|
// int newVel = roundf(value * 10);
|
||||||
if (newVel != Environment::shipState.selectedVelocity) {
|
// if (newVel != Environment::shipState.selectedVelocity) {
|
||||||
newShipVelocity = newVel;
|
// newShipVelocity = newVel;
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
|
|
||||||
|
|
||||||
|
// Добавляем джойстик для управления кораблём
|
||||||
|
// centerX=150, centerY=150 (от левого нижнего угла в UI координатах)
|
||||||
|
// baseRadius=120, knobRadius=50
|
||||||
|
uiManager.addJoystick("shipJoystick", 150.0f, 150.0f, 120.0f, 50.0f,
|
||||||
|
renderer, CONST_ZIP_FILE,
|
||||||
|
"resources/joystick_base.png", "resources/joystick_knob.png");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
cubemapTexture = std::make_shared<Texture>(
|
cubemapTexture = std::make_shared<Texture>(
|
||||||
std::array<TextureDataStruct, 6>{
|
std::array<TextureDataStruct, 6>{
|
||||||
@ -261,19 +273,18 @@ namespace ZL
|
|||||||
|
|
||||||
//Load texture
|
//Load texture
|
||||||
|
|
||||||
//spaceshipTexture = std::make_unique<Texture>(CreateTextureDataFromPng("resources/DefaultMaterial_BaseColor_shine.png", CONST_ZIP_FILE));
|
|
||||||
//spaceshipBase = LoadFromTextFile02("resources/spaceship006.txt", CONST_ZIP_FILE);
|
|
||||||
//spaceshipBase.RotateByMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(M_PI / 2.0, Eigen::Vector3f::UnitY())).toRotationMatrix());// QuatFromRotateAroundY(M_PI / 2.0).toRotationMatrix());
|
|
||||||
|
|
||||||
//spaceshipTexture = std::make_unique<Texture>(CreateTextureDataFromPng("./resources/cap_D.png", CONST_ZIP_FILE));
|
|
||||||
//spaceshipBase = LoadFromTextFile02("./resources/spaceship006x.txt", CONST_ZIP_FILE);
|
|
||||||
//spaceshipBase.RotateByMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-M_PI / 2.0, Eigen::Vector3f::UnitY())).toRotationMatrix());// QuatFromRotateAroundY(M_PI / 2.0).toRotationMatrix());
|
|
||||||
|
|
||||||
spaceshipTexture = std::make_unique<Texture>(CreateTextureDataFromPng("resources/MainCharacter_Base_color_sRGB.png", CONST_ZIP_FILE));
|
spaceshipTexture = std::make_unique<Texture>(CreateTextureDataFromPng("resources/MainCharacter_Base_color_sRGB.png", CONST_ZIP_FILE));
|
||||||
spaceshipBase = LoadFromTextFile02("resources/spaceshipnew001.txt", CONST_ZIP_FILE);
|
spaceshipBase = LoadFromTextFile02("resources/spaceshipnew001.txt", CONST_ZIP_FILE);
|
||||||
spaceshipBase.RotateByMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(M_PI, Eigen::Vector3f::UnitY())).toRotationMatrix());// QuatFromRotateAroundY(M_PI / 2.0).toRotationMatrix());
|
spaceshipBase.RotateByMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(M_PI, Eigen::Vector3f::UnitY())).toRotationMatrix());// QuatFromRotateAroundY(M_PI / 2.0).toRotationMatrix());
|
||||||
|
|
||||||
spaceshipBase.Move(Vector3f{ 1.2, 0, -5 });
|
|
||||||
|
|
||||||
|
|
||||||
|
spaceshipBase.Move(Vector3f{ -0.52998, 0, -10 });
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
spaceship.AssignFrom(spaceshipBase);
|
spaceship.AssignFrom(spaceshipBase);
|
||||||
spaceship.RefreshVBO();
|
spaceship.RefreshVBO();
|
||||||
@ -288,7 +299,7 @@ namespace ZL
|
|||||||
for (int i = 0; i < boxCoordsArr.size(); i++)
|
for (int i = 0; i < boxCoordsArr.size(); i++)
|
||||||
{
|
{
|
||||||
boxRenderArr[i].AssignFrom(boxBase);
|
boxRenderArr[i].AssignFrom(boxBase);
|
||||||
//boxRenderArr[i].data = CreateBaseConvexPolyhedron(1999);
|
|
||||||
boxRenderArr[i].RefreshVBO();
|
boxRenderArr[i].RefreshVBO();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -331,17 +342,21 @@ namespace ZL
|
|||||||
static_cast<float>(Environment::width) / static_cast<float>(Environment::height),
|
static_cast<float>(Environment::width) / static_cast<float>(Environment::height),
|
||||||
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
|
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
|
||||||
renderer.PushMatrix();
|
renderer.PushMatrix();
|
||||||
renderer.LoadIdentity();
|
|
||||||
renderer.RotateMatrix(Environment::inverseShipMatrix);
|
|
||||||
|
|
||||||
|
renderer.LoadIdentity();
|
||||||
|
|
||||||
|
// Для скайбокса берем только вращение от камеры
|
||||||
|
Matrix4f view = camera.getViewMatrix();
|
||||||
|
view.block<3, 1>(0, 3) = Vector3f::Zero();
|
||||||
|
renderer.PushSpecialMatrix(view);
|
||||||
|
|
||||||
Vector3f worldLightDir = Vector3f(1.0f, -1.0f, -1.0f).normalized();
|
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;
|
Vector3f lightToSource = -viewLightDir;
|
||||||
|
|
||||||
renderer.RenderUniform3fv("uLightDirView", lightToSource.data());
|
renderer.RenderUniform3fv("uLightDirView", lightToSource.data());
|
||||||
|
|
||||||
// 2. Базовый цвет атмосферы (голубой)
|
// 2. Базовый цвет атмосферы (голубой)
|
||||||
@ -372,7 +387,8 @@ namespace ZL
|
|||||||
CheckGlError();
|
CheckGlError();
|
||||||
|
|
||||||
|
|
||||||
renderer.PopMatrix();
|
renderer.PopMatrix(); // Pop special matrix
|
||||||
|
renderer.PopMatrix(); // Pop original push
|
||||||
renderer.PopProjectionMatrix();
|
renderer.PopProjectionMatrix();
|
||||||
renderer.DisableVertexAttribArray(vPositionName);
|
renderer.DisableVertexAttribArray(vPositionName);
|
||||||
|
|
||||||
@ -399,13 +415,30 @@ namespace ZL
|
|||||||
renderer.PushMatrix();
|
renderer.PushMatrix();
|
||||||
|
|
||||||
renderer.LoadIdentity();
|
renderer.LoadIdentity();
|
||||||
renderer.TranslateMatrix({ 0,0, -1.0f * Environment::zoom });
|
|
||||||
renderer.TranslateMatrix({ 0, -6.f, 0 }); //Ship camera offset
|
renderer.PushSpecialMatrix(camera.getViewMatrix());
|
||||||
|
|
||||||
|
// --- ship model matrix ---
|
||||||
|
renderer.PushMatrix();
|
||||||
|
renderer.TranslateMatrix(Environment::shipState.position);
|
||||||
|
renderer.RotateMatrix(Environment::shipState.rotation);
|
||||||
|
|
||||||
if (shipAlive) {
|
if (shipAlive) {
|
||||||
glBindTexture(GL_TEXTURE_2D, spaceshipTexture->getTexID());
|
glBindTexture(GL_TEXTURE_2D, spaceshipTexture->getTexID());
|
||||||
renderer.DrawVertexRenderStruct(spaceship);
|
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);
|
glEnable(GL_BLEND);
|
||||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
|
||||||
@ -428,7 +461,8 @@ namespace ZL
|
|||||||
}
|
}
|
||||||
|
|
||||||
glDisable(GL_BLEND);
|
glDisable(GL_BLEND);
|
||||||
renderer.PopMatrix();
|
renderer.PopMatrix(); // Pop special matrix
|
||||||
|
renderer.PopMatrix(); // Pop original push
|
||||||
renderer.PopProjectionMatrix();
|
renderer.PopProjectionMatrix();
|
||||||
renderer.DisableVertexAttribArray(vPositionName);
|
renderer.DisableVertexAttribArray(vPositionName);
|
||||||
renderer.DisableVertexAttribArray(vTexCoordName);
|
renderer.DisableVertexAttribArray(vTexCoordName);
|
||||||
@ -454,15 +488,17 @@ namespace ZL
|
|||||||
static_cast<float>(Environment::width) / static_cast<float>(Environment::height),
|
static_cast<float>(Environment::width) / static_cast<float>(Environment::height),
|
||||||
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
|
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
|
||||||
|
|
||||||
|
renderer.PushMatrix();
|
||||||
|
renderer.LoadIdentity();
|
||||||
|
renderer.PushSpecialMatrix(camera.getViewMatrix());
|
||||||
|
|
||||||
for (int i = 0; i < boxCoordsArr.size(); i++)
|
for (int i = 0; i < boxCoordsArr.size(); i++)
|
||||||
{
|
{
|
||||||
if (!boxAlive[i]) continue;
|
if (!boxAlive[i]) continue;
|
||||||
renderer.PushMatrix();
|
renderer.PushMatrix();
|
||||||
|
|
||||||
renderer.LoadIdentity();
|
// Коробки рисуются в мировых координатах
|
||||||
renderer.TranslateMatrix({ 0,0, -1.0f * Environment::zoom });
|
// Но у них есть offset { 0.f, 0.f, 45000.f }
|
||||||
renderer.RotateMatrix(Environment::inverseShipMatrix);
|
|
||||||
renderer.TranslateMatrix(-Environment::shipState.position);
|
|
||||||
renderer.TranslateMatrix({ 0.f, 0.f, 45000.f });
|
renderer.TranslateMatrix({ 0.f, 0.f, 45000.f });
|
||||||
renderer.TranslateMatrix(boxCoordsArr[i].pos);
|
renderer.TranslateMatrix(boxCoordsArr[i].pos);
|
||||||
renderer.RotateMatrix(boxCoordsArr[i].m);
|
renderer.RotateMatrix(boxCoordsArr[i].m);
|
||||||
@ -473,6 +509,8 @@ namespace ZL
|
|||||||
|
|
||||||
renderer.PopMatrix();
|
renderer.PopMatrix();
|
||||||
}
|
}
|
||||||
|
renderer.PopMatrix(); // Pop special matrix
|
||||||
|
renderer.PopMatrix(); // Pop original push
|
||||||
renderer.PopProjectionMatrix();
|
renderer.PopProjectionMatrix();
|
||||||
renderer.DisableVertexAttribArray(vPositionName);
|
renderer.DisableVertexAttribArray(vPositionName);
|
||||||
renderer.DisableVertexAttribArray(vTexCoordName);
|
renderer.DisableVertexAttribArray(vTexCoordName);
|
||||||
@ -519,6 +557,11 @@ namespace ZL
|
|||||||
|
|
||||||
CheckGlError();
|
CheckGlError();
|
||||||
|
|
||||||
|
|
||||||
|
// Обновляем камеру
|
||||||
|
// camera.follow(Environment::shipState.position, shipWorldOrientation, Environment::zoom, 6.0f);
|
||||||
|
camera.followOrbit(Environment::shipState.position, camYaw, camPitch, Environment::zoom, 6.0f);
|
||||||
|
|
||||||
float skyPercent = 0.0;
|
float skyPercent = 0.0;
|
||||||
float distance = planetObject.distanceToPlanetSurface(Environment::shipState.position);
|
float distance = planetObject.distanceToPlanetSurface(Environment::shipState.position);
|
||||||
if (distance > 1500.f)
|
if (distance > 1500.f)
|
||||||
@ -542,7 +585,9 @@ namespace ZL
|
|||||||
glClear(GL_DEPTH_BUFFER_BIT);
|
glClear(GL_DEPTH_BUFFER_BIT);
|
||||||
}
|
}
|
||||||
drawShip();
|
drawShip();
|
||||||
|
#if ENABLE_REMOTE_SHIPS
|
||||||
drawRemoteShips();
|
drawRemoteShips();
|
||||||
|
#endif
|
||||||
drawBoxes();
|
drawBoxes();
|
||||||
|
|
||||||
drawUI();
|
drawUI();
|
||||||
@ -550,13 +595,11 @@ namespace ZL
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Game::drawRemoteShips() {
|
void Game::drawRemoteShips() {
|
||||||
// Используем те же константы имен для шейдеров, что и в drawShip
|
|
||||||
static const std::string defaultShaderName = "default";
|
static const std::string defaultShaderName = "default";
|
||||||
static const std::string vPositionName = "vPosition";
|
static const std::string vPositionName = "vPosition";
|
||||||
static const std::string vTexCoordName = "vTexCoord";
|
static const std::string vTexCoordName = "vTexCoord";
|
||||||
static const std::string textureUniformName = "Texture";
|
static const std::string textureUniformName = "Texture";
|
||||||
|
|
||||||
// Активируем шейдер и текстуру (предполагаем, что меш у всех одинаковый)
|
|
||||||
renderer.shaderManager.PushShader(defaultShaderName);
|
renderer.shaderManager.PushShader(defaultShaderName);
|
||||||
renderer.RenderUniform1i(textureUniformName, 0);
|
renderer.RenderUniform1i(textureUniformName, 0);
|
||||||
|
|
||||||
@ -567,46 +610,32 @@ namespace ZL
|
|||||||
static_cast<float>(Environment::width) / static_cast<float>(Environment::height),
|
static_cast<float>(Environment::width) / static_cast<float>(Environment::height),
|
||||||
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
|
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
|
||||||
|
|
||||||
// Биндим текстуру корабля один раз для всех удаленных игроков (оптимизация батчинга)
|
|
||||||
glBindTexture(GL_TEXTURE_2D, spaceshipTexture->getTexID());
|
glBindTexture(GL_TEXTURE_2D, spaceshipTexture->getTexID());
|
||||||
|
|
||||||
auto now = std::chrono::system_clock::now();
|
auto now = std::chrono::system_clock::now();
|
||||||
|
|
||||||
//Apply server delay:
|
|
||||||
now -= std::chrono::milliseconds(CLIENT_DELAY);
|
now -= std::chrono::milliseconds(CLIENT_DELAY);
|
||||||
|
|
||||||
latestRemotePlayers = networkClient->getRemotePlayers();
|
latestRemotePlayers = networkClient->getRemotePlayers();
|
||||||
|
|
||||||
// Итерируемся по актуальным данным из extrapolateRemotePlayers
|
renderer.PushMatrix();
|
||||||
for (auto const& [id, remotePlayer] : latestRemotePlayers) {
|
renderer.LoadIdentity();
|
||||||
|
renderer.PushSpecialMatrix(camera.getViewMatrix());
|
||||||
|
|
||||||
if (!remotePlayer.canFetchClientStateAtTime(now))
|
for (auto const& [id, remotePlayer] : latestRemotePlayers) {
|
||||||
{
|
if (!remotePlayer.canFetchClientStateAtTime(now)) continue;
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
ClientState playerState = remotePlayer.fetchClientStateAtTime(now);
|
ClientState playerState = remotePlayer.fetchClientStateAtTime(now);
|
||||||
|
|
||||||
|
|
||||||
renderer.PushMatrix();
|
renderer.PushMatrix();
|
||||||
renderer.LoadIdentity();
|
renderer.TranslateMatrix(playerState.position);
|
||||||
|
|
||||||
renderer.TranslateMatrix({ 0,0, -1.0f * Environment::zoom });
|
|
||||||
renderer.TranslateMatrix({ 0, -6.f, 0 }); //Ship camera offset
|
|
||||||
renderer.RotateMatrix(Environment::inverseShipMatrix);
|
|
||||||
renderer.TranslateMatrix(-Environment::shipState.position);
|
|
||||||
|
|
||||||
|
|
||||||
Eigen::Vector3f relativePos = playerState.position;// -Environment::shipPosition;
|
|
||||||
renderer.TranslateMatrix(relativePos);
|
|
||||||
|
|
||||||
// 3. Поворот врага
|
|
||||||
renderer.RotateMatrix(playerState.rotation);
|
renderer.RotateMatrix(playerState.rotation);
|
||||||
|
|
||||||
renderer.DrawVertexRenderStruct(spaceship);
|
renderer.DrawVertexRenderStruct(spaceship);
|
||||||
renderer.PopMatrix();
|
renderer.PopMatrix();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderer.PopMatrix(); // pop special matrix stack (view)
|
||||||
|
renderer.PopMatrix(); // pop identity push
|
||||||
|
|
||||||
renderer.PopProjectionMatrix();
|
renderer.PopProjectionMatrix();
|
||||||
renderer.DisableVertexAttribArray(vPositionName);
|
renderer.DisableVertexAttribArray(vPositionName);
|
||||||
renderer.DisableVertexAttribArray(vTexCoordName);
|
renderer.DisableVertexAttribArray(vTexCoordName);
|
||||||
@ -615,6 +644,7 @@ namespace ZL
|
|||||||
CheckGlError();
|
CheckGlError();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Game::processTickCount() {
|
void Game::processTickCount() {
|
||||||
|
|
||||||
if (lastTickCount == 0) {
|
if (lastTickCount == 0) {
|
||||||
@ -640,72 +670,127 @@ namespace ZL
|
|||||||
|
|
||||||
auto now_ms = newTickCount;
|
auto now_ms = newTickCount;
|
||||||
|
|
||||||
sparkEmitter.update(static_cast<float>(delta));
|
|
||||||
planetObject.update(static_cast<float>(delta));
|
planetObject.update(static_cast<float>(delta));
|
||||||
|
|
||||||
static float pingTimer = 0.0f;
|
|
||||||
pingTimer += delta;
|
|
||||||
if (pingTimer >= 1000.0f) {
|
|
||||||
std::string pingMsg = "UPD:" + std::to_string(now_ms) + ":" + Environment::shipState.formPingMessageContent();
|
|
||||||
|
|
||||||
networkClient->Send(pingMsg);
|
|
||||||
std::cout << "Sending: " << pingMsg << std::endl;
|
|
||||||
pingTimer = 0.0f;
|
|
||||||
|
// ------------------------
|
||||||
|
// Input -> ControlState (joystick)
|
||||||
|
// ------------------------
|
||||||
|
|
||||||
|
float discreteMag = 0.0f;
|
||||||
|
int discreteAngle = -1;
|
||||||
|
|
||||||
|
auto joystick = uiManager.findJoystick("shipJoystick");
|
||||||
|
bool joystickActive = isUsingJoystick && joystick && joystick->isActive;
|
||||||
|
|
||||||
|
if (joystickActive) {
|
||||||
|
float joyX = joystick->getDirectionX(); // <-- оставляем инверсию X
|
||||||
|
float joyY = joystick->getDirectionY();
|
||||||
|
float magnitude = joystick->getMagnitude();
|
||||||
|
|
||||||
|
const float deadzone = 0.1f;
|
||||||
|
|
||||||
|
Vector3f newMoveDir = Vector3f::Zero();
|
||||||
|
float newMag = 0.0f;
|
||||||
|
|
||||||
|
if (magnitude > deadzone) {
|
||||||
|
|
||||||
|
// Берём ориентацию камеры из view matrix:
|
||||||
|
// view = World -> View, значит Rcw = (Rwv)^T = View -> World
|
||||||
|
Matrix4f viewM = camera.getViewMatrix();
|
||||||
|
Matrix3f Rwv = viewM.block<3, 3>(0, 0);
|
||||||
|
Matrix3f Rcw = Rwv.transpose();
|
||||||
|
|
||||||
|
Vector3f camRight = (Rcw * Vector3f(1, 0, 0));
|
||||||
|
Vector3f camForward = (Rcw * Vector3f(0, 0, -1)); // куда смотрит камера в мире
|
||||||
|
|
||||||
|
if (camRight.squaredNorm() > 1e-6f) camRight.normalize();
|
||||||
|
if (camForward.squaredNorm() > 1e-6f) camForward.normalize();
|
||||||
|
|
||||||
|
// joystick: X = вправо/влево, Y = вверх/вниз
|
||||||
|
// joyY обычно отрицательный при "вверх", поэтому "- camForward * joyY" даёт "вперёд"
|
||||||
|
Vector3f worldMove = camRight * joyX - camForward * joyY;
|
||||||
|
|
||||||
|
if (worldMove.squaredNorm() > 1e-6f) {
|
||||||
|
newMoveDir = worldMove.normalized();
|
||||||
|
newMag = (std::min)(magnitude, 1.0f);
|
||||||
|
newMag = std::round(newMag * 10.0f) / 10.0f;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Handle input:
|
// Применяем в shipState
|
||||||
|
bool changed = false;
|
||||||
|
|
||||||
if (newShipVelocity != Environment::shipState.selectedVelocity)
|
if ((Environment::shipState.currentAngularVelocity - newMoveDir).norm() > 0.001f) {
|
||||||
{
|
Environment::shipState.currentAngularVelocity = newMoveDir; // <-- теперь это "moveDirWorld"
|
||||||
Environment::shipState.selectedVelocity = newShipVelocity;
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fabs(Environment::shipState.discreteMag - newMag) > 0.001f) {
|
||||||
|
Environment::shipState.discreteMag = newMag; // throttle
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// discreteAngle больше не нужен для движения (можешь оставить -1)
|
||||||
|
if (Environment::shipState.discreteAngle != -1) {
|
||||||
|
Environment::shipState.discreteAngle = -1;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changed) {
|
||||||
std::string msg = "UPD:" + std::to_string(now_ms) + ":" + Environment::shipState.formPingMessageContent();
|
std::string msg = "UPD:" + std::to_string(now_ms) + ":" + Environment::shipState.formPingMessageContent();
|
||||||
networkClient->Send(msg);
|
networkClient->Send(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
float discreteMag;
|
|
||||||
int discreteAngle;
|
|
||||||
|
|
||||||
if (Environment::tapDownHold) {
|
|
||||||
float diffx = Environment::tapDownCurrentPos(0) - Environment::tapDownStartPos(0);
|
|
||||||
float diffy = Environment::tapDownCurrentPos(1) - Environment::tapDownStartPos(1);
|
|
||||||
|
|
||||||
float rawMag = sqrtf(diffx * diffx + diffy * diffy);
|
|
||||||
float maxRadius = 200.0f; // Максимальный вынос джойстика
|
|
||||||
|
|
||||||
if (rawMag > 10.0f) { // Мертвая зона
|
|
||||||
// 1. Дискретизируем отклонение (0.0 - 1.0 с шагом 0.1)
|
|
||||||
float normalizedMag = min(rawMag / maxRadius, 1.0f);
|
|
||||||
discreteMag = std::round(normalizedMag * 10.0f) / 10.0f;
|
|
||||||
|
|
||||||
// 2. Дискретизируем угол (0-359 градусов)
|
|
||||||
// atan2 возвращает радианы, переводим в градусы
|
|
||||||
float radians = atan2f(diffy, diffx);
|
|
||||||
discreteAngle = static_cast<int>(radians * 180.0f / M_PI);
|
|
||||||
if (discreteAngle < 0) discreteAngle += 360;
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
discreteAngle = -1;
|
|
||||||
discreteMag = 0.0f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
discreteAngle = -1;
|
|
||||||
discreteMag = 0.0f;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (discreteAngle != Environment::shipState.discreteAngle || discreteMag != Environment::shipState.discreteMag) {
|
// Network Update
|
||||||
|
//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);
|
||||||
|
//}
|
||||||
|
// Network Update (только если НЕ используем джойстик)
|
||||||
|
if (!joystickActive) {
|
||||||
|
bool changed2 = false;
|
||||||
|
|
||||||
|
if (discreteAngle != Environment::shipState.discreteAngle) {
|
||||||
Environment::shipState.discreteAngle = discreteAngle;
|
Environment::shipState.discreteAngle = discreteAngle;
|
||||||
|
changed2 = true;
|
||||||
|
}
|
||||||
|
if (discreteMag != Environment::shipState.discreteMag) {
|
||||||
Environment::shipState.discreteMag = discreteMag;
|
Environment::shipState.discreteMag = discreteMag;
|
||||||
|
changed2 = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changed2) {
|
||||||
std::string msg = "UPD:" + std::to_string(now_ms) + ":" + Environment::shipState.formPingMessageContent();
|
std::string msg = "UPD:" + std::to_string(now_ms) + ":" + Environment::shipState.formPingMessageContent();
|
||||||
networkClient->Send(msg);
|
networkClient->Send(msg);
|
||||||
std::cout << "Sending: " << msg << std::endl;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Environment::shipState.simulate_physics(delta);
|
Environment::shipState.simulate_physics(delta);
|
||||||
Environment::inverseShipMatrix = Environment::shipState.rotation.inverse();
|
Environment::inverseShipMatrix = Environment::shipState.rotation.inverse();
|
||||||
@ -733,6 +818,7 @@ namespace ZL
|
|||||||
projectileEmitter.setEmissionPoints(std::vector<Vector3f>());
|
projectileEmitter.setEmissionPoints(std::vector<Vector3f>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::vector<Vector3f> shipCameraPoints;
|
std::vector<Vector3f> shipCameraPoints;
|
||||||
for (const auto& lp : shipLocalEmissionPoints) {
|
for (const auto& lp : shipLocalEmissionPoints) {
|
||||||
Vector3f adjusted = lp + Vector3f{ 0.0f, -Environment::zoom * 0.03f, 0.0f };
|
Vector3f adjusted = lp + Vector3f{ 0.0f, -Environment::zoom * 0.03f, 0.0f };
|
||||||
@ -1003,8 +1089,8 @@ namespace ZL
|
|||||||
glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
|
glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
|
||||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||||
|
|
||||||
drawScene();
|
|
||||||
processTickCount();
|
processTickCount();
|
||||||
|
drawScene();
|
||||||
|
|
||||||
SDL_GL_SwapWindow(ZL::Environment::window);
|
SDL_GL_SwapWindow(ZL::Environment::window);
|
||||||
}
|
}
|
||||||
@ -1093,27 +1179,20 @@ namespace ZL
|
|||||||
|
|
||||||
uiManager.onMouseDown(uiX, uiY);
|
uiManager.onMouseDown(uiX, uiY);
|
||||||
|
|
||||||
bool uiHandled = false;
|
// Check if joystick became active
|
||||||
|
auto joystick = uiManager.findJoystick("shipJoystick");
|
||||||
for (const auto& button : uiManager.findButton("") ? std::vector<std::shared_ptr<UiButton>>{} : std::vector<std::shared_ptr<UiButton>>{}) {
|
if (joystick && joystick->isActive) {
|
||||||
(void)button;
|
isUsingJoystick = true;
|
||||||
|
isDraggingCamera = false;
|
||||||
|
Environment::tapDownHold = false; // Don't trigger camera drag
|
||||||
}
|
}
|
||||||
|
else if (!uiManager.isUiInteraction()) {
|
||||||
auto pressedSlider = [&]() -> std::shared_ptr<UiSlider> {
|
// No UI interaction (and no joystick), so it's camera drag
|
||||||
for (const auto& slider : uiManager.findSlider("") ? std::vector<std::shared_ptr<UiSlider>>{} : std::vector<std::shared_ptr<UiSlider>>{}) {
|
isUsingJoystick = false;
|
||||||
(void)slider;
|
isDraggingCamera = true;
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}();
|
|
||||||
|
|
||||||
if (!uiManager.isUiInteraction()) {
|
|
||||||
Environment::tapDownHold = true;
|
Environment::tapDownHold = true;
|
||||||
|
Environment::tapDownStartPos = Vector2f(static_cast<float>(mx), static_cast<float>(my));
|
||||||
Environment::tapDownStartPos(0) = mx;
|
Environment::tapDownCurrentPos = Environment::tapDownStartPos;
|
||||||
Environment::tapDownStartPos(1) = my;
|
|
||||||
|
|
||||||
Environment::tapDownCurrentPos(0) = mx;
|
|
||||||
Environment::tapDownCurrentPos(1) = my;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1124,9 +1203,12 @@ namespace ZL
|
|||||||
|
|
||||||
uiManager.onMouseUp(uiX, uiY);
|
uiManager.onMouseUp(uiX, uiY);
|
||||||
|
|
||||||
if (!uiManager.isUiInteraction()) {
|
if (isUsingJoystick) {
|
||||||
Environment::tapDownHold = false;
|
isUsingJoystick = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isDraggingCamera = false;
|
||||||
|
Environment::tapDownHold = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Game::handleMotion(int mx, int my)
|
void Game::handleMotion(int mx, int my)
|
||||||
@ -1136,10 +1218,26 @@ namespace ZL
|
|||||||
|
|
||||||
uiManager.onMouseMove(uiX, uiY);
|
uiManager.onMouseMove(uiX, uiY);
|
||||||
|
|
||||||
if (Environment::tapDownHold && !uiManager.isUiInteraction()) {
|
if (isDraggingCamera && !uiManager.isUiInteraction()) {
|
||||||
Environment::tapDownCurrentPos(0) = mx;
|
float curX = static_cast<float>(mx);
|
||||||
Environment::tapDownCurrentPos(1) = my;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
27
src/Game.h
27
src/Game.h
@ -1,8 +1,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "render/Renderer.h"
|
#include "render/Renderer.h"
|
||||||
#include "Environment.h"
|
#include "Environment.h"
|
||||||
#include "render/TextureManager.h"
|
#include "render/TextureManager.h"
|
||||||
|
#include "render/Camera.h"
|
||||||
#include "SparkEmitter.h"
|
#include "SparkEmitter.h"
|
||||||
#include "planet/PlanetObject.h"
|
#include "planet/PlanetObject.h"
|
||||||
#include "UiManager.h"
|
#include "UiManager.h"
|
||||||
@ -33,6 +34,7 @@ namespace ZL {
|
|||||||
bool shouldExit() const { return Environment::exitGameLoop; }
|
bool shouldExit() const { return Environment::exitGameLoop; }
|
||||||
|
|
||||||
Renderer renderer;
|
Renderer renderer;
|
||||||
|
Camera camera;
|
||||||
TaskManager taskManager;
|
TaskManager taskManager;
|
||||||
MainThreadHandler mainThreadHandler;
|
MainThreadHandler mainThreadHandler;
|
||||||
|
|
||||||
@ -64,7 +66,7 @@ namespace ZL {
|
|||||||
|
|
||||||
std::unordered_map<int, ClientStateInterval> latestRemotePlayers;
|
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_TIMER_INTERVAL = 10;
|
||||||
static const size_t CONST_MAX_TIME_INTERVAL = 1000;
|
static const size_t CONST_MAX_TIME_INTERVAL = 1000;
|
||||||
@ -93,6 +95,23 @@ namespace ZL {
|
|||||||
int maxProjectiles = 32;
|
int maxProjectiles = 32;
|
||||||
std::vector<Vector3f> shipLocalEmissionPoints;
|
std::vector<Vector3f> shipLocalEmissionPoints;
|
||||||
|
|
||||||
|
bool isDraggingShip = false;
|
||||||
|
bool isDraggingCamera = false;
|
||||||
|
bool isUsingJoystick = false;
|
||||||
|
|
||||||
|
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;
|
||||||
|
Matrix3f lockedCameraMat = Matrix3f::Identity();
|
||||||
|
|
||||||
bool shipAlive = true;
|
bool shipAlive = true;
|
||||||
bool gameOver = false;
|
bool gameOver = false;
|
||||||
@ -104,6 +123,10 @@ namespace ZL {
|
|||||||
uint64_t lastExplosionTime = 0;
|
uint64_t lastExplosionTime = 0;
|
||||||
const uint64_t explosionDurationMs = 500;
|
const uint64_t explosionDurationMs = 500;
|
||||||
|
|
||||||
|
float camDistance = 0.0f; // будет равен Environment::zoom
|
||||||
|
float camHeight = 6.0f; // высота над кораблём
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
5
src/GameConfig.h
Normal file
5
src/GameConfig.h
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Feature flags to temporarily disable unstable features
|
||||||
|
#define ENABLE_STONES 0
|
||||||
|
#define ENABLE_REMOTE_SHIPS 0
|
||||||
@ -167,6 +167,91 @@ namespace ZL {
|
|||||||
renderer.DisableVertexAttribArray(vTexCoordName);
|
renderer.DisableVertexAttribArray(vTexCoordName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UiJoystick::buildBaseMesh() {
|
||||||
|
baseMesh.data.PositionData.clear();
|
||||||
|
baseMesh.data.TexCoordData.clear();
|
||||||
|
|
||||||
|
float x0 = centerX - baseRadius;
|
||||||
|
float y0 = centerY - baseRadius;
|
||||||
|
float x1 = centerX + baseRadius;
|
||||||
|
float y1 = centerY + baseRadius;
|
||||||
|
|
||||||
|
baseMesh.data.PositionData.push_back({ x0, y0, 0 });
|
||||||
|
baseMesh.data.TexCoordData.push_back({ 0, 0 });
|
||||||
|
|
||||||
|
baseMesh.data.PositionData.push_back({ x0, y1, 0 });
|
||||||
|
baseMesh.data.TexCoordData.push_back({ 0, 1 });
|
||||||
|
|
||||||
|
baseMesh.data.PositionData.push_back({ x1, y1, 0 });
|
||||||
|
baseMesh.data.TexCoordData.push_back({ 1, 1 });
|
||||||
|
|
||||||
|
baseMesh.data.PositionData.push_back({ x0, y0, 0 });
|
||||||
|
baseMesh.data.TexCoordData.push_back({ 0, 0 });
|
||||||
|
|
||||||
|
baseMesh.data.PositionData.push_back({ x1, y1, 0 });
|
||||||
|
baseMesh.data.TexCoordData.push_back({ 1, 1 });
|
||||||
|
|
||||||
|
baseMesh.data.PositionData.push_back({ x1, y0, 0 });
|
||||||
|
baseMesh.data.TexCoordData.push_back({ 1, 0 });
|
||||||
|
|
||||||
|
baseMesh.RefreshVBO();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UiJoystick::buildKnobMesh() {
|
||||||
|
knobMesh.data.PositionData.clear();
|
||||||
|
knobMesh.data.TexCoordData.clear();
|
||||||
|
|
||||||
|
float cx = centerX + knobOffsetX;
|
||||||
|
float cy = centerY + knobOffsetY;
|
||||||
|
|
||||||
|
float x0 = cx - knobRadius;
|
||||||
|
float y0 = cy - knobRadius;
|
||||||
|
float x1 = cx + knobRadius;
|
||||||
|
float y1 = cy + knobRadius;
|
||||||
|
|
||||||
|
knobMesh.data.PositionData.push_back({ x0, y0, 0 });
|
||||||
|
knobMesh.data.TexCoordData.push_back({ 0, 0 });
|
||||||
|
|
||||||
|
knobMesh.data.PositionData.push_back({ x0, y1, 0 });
|
||||||
|
knobMesh.data.TexCoordData.push_back({ 0, 1 });
|
||||||
|
|
||||||
|
knobMesh.data.PositionData.push_back({ x1, y1, 0 });
|
||||||
|
knobMesh.data.TexCoordData.push_back({ 1, 1 });
|
||||||
|
|
||||||
|
knobMesh.data.PositionData.push_back({ x0, y0, 0 });
|
||||||
|
knobMesh.data.TexCoordData.push_back({ 0, 0 });
|
||||||
|
|
||||||
|
knobMesh.data.PositionData.push_back({ x1, y1, 0 });
|
||||||
|
knobMesh.data.TexCoordData.push_back({ 1, 1 });
|
||||||
|
|
||||||
|
knobMesh.data.PositionData.push_back({ x1, y0, 0 });
|
||||||
|
knobMesh.data.TexCoordData.push_back({ 1, 0 });
|
||||||
|
|
||||||
|
knobMesh.RefreshVBO();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UiJoystick::draw(Renderer& renderer) const {
|
||||||
|
static const std::string vPositionName = "vPosition";
|
||||||
|
static const std::string vTexCoordName = "vTexCoord";
|
||||||
|
static const std::string textureUniformName = "Texture";
|
||||||
|
|
||||||
|
renderer.RenderUniform1i(textureUniformName, 0);
|
||||||
|
renderer.EnableVertexAttribArray(vPositionName);
|
||||||
|
renderer.EnableVertexAttribArray(vTexCoordName);
|
||||||
|
|
||||||
|
if (texBase) {
|
||||||
|
glBindTexture(GL_TEXTURE_2D, texBase->getTexID());
|
||||||
|
renderer.DrawVertexRenderStruct(baseMesh);
|
||||||
|
}
|
||||||
|
if (texKnob) {
|
||||||
|
glBindTexture(GL_TEXTURE_2D, texKnob->getTexID());
|
||||||
|
renderer.DrawVertexRenderStruct(knobMesh);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderer.DisableVertexAttribArray(vPositionName);
|
||||||
|
renderer.DisableVertexAttribArray(vTexCoordName);
|
||||||
|
}
|
||||||
|
|
||||||
void UiManager::loadFromFile(const std::string& path, Renderer& renderer, const std::string& zipFile) {
|
void UiManager::loadFromFile(const std::string& path, Renderer& renderer, const std::string& zipFile) {
|
||||||
std::string content;
|
std::string content;
|
||||||
try {
|
try {
|
||||||
@ -446,6 +531,57 @@ namespace ZL {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool UiManager::addJoystick(const std::string& name, float centerX, float centerY, float baseRadius, float knobRadius,
|
||||||
|
Renderer& renderer, const std::string& zipFile,
|
||||||
|
const std::string& basePath, const std::string& knobPath) {
|
||||||
|
|
||||||
|
auto j = std::make_shared<UiJoystick>();
|
||||||
|
j->name = name;
|
||||||
|
j->centerX = centerX;
|
||||||
|
j->centerY = centerY;
|
||||||
|
j->baseRadius = baseRadius;
|
||||||
|
j->knobRadius = knobRadius;
|
||||||
|
j->knobOffsetX = 0;
|
||||||
|
j->knobOffsetY = 0;
|
||||||
|
j->isActive = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!basePath.empty()) {
|
||||||
|
auto data = CreateTextureDataFromPng(basePath.c_str(), zipFile.c_str());
|
||||||
|
j->texBase = std::make_shared<Texture>(data);
|
||||||
|
}
|
||||||
|
if (!knobPath.empty()) {
|
||||||
|
auto data = CreateTextureDataFromPng(knobPath.c_str(), zipFile.c_str());
|
||||||
|
j->texKnob = std::make_shared<Texture>(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const std::exception& e) {
|
||||||
|
std::cerr << "UiManager: addJoystick failed to load textures: " << e.what() << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
j->buildBaseMesh();
|
||||||
|
j->buildKnobMesh();
|
||||||
|
|
||||||
|
joysticks.push_back(j);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<UiJoystick> UiManager::findJoystick(const std::string& name) {
|
||||||
|
for (auto& j : joysticks) if (j->name == name) return j;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UiManager::resetJoystick(const std::string& name) {
|
||||||
|
auto j = findJoystick(name);
|
||||||
|
if (j) {
|
||||||
|
j->knobOffsetX = 0;
|
||||||
|
j->knobOffsetY = 0;
|
||||||
|
j->isActive = false;
|
||||||
|
j->buildKnobMesh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool UiManager::pushMenuFromFile(const std::string& path, Renderer& renderer, const std::string& zipFile) {
|
bool UiManager::pushMenuFromFile(const std::string& path, Renderer& renderer, const std::string& zipFile) {
|
||||||
MenuState prev;
|
MenuState prev;
|
||||||
prev.root = root;
|
prev.root = root;
|
||||||
@ -526,6 +662,9 @@ namespace ZL {
|
|||||||
renderer.PushMatrix();
|
renderer.PushMatrix();
|
||||||
renderer.LoadIdentity();
|
renderer.LoadIdentity();
|
||||||
|
|
||||||
|
for (const auto& j : joysticks) {
|
||||||
|
j->draw(renderer);
|
||||||
|
}
|
||||||
for (const auto& b : buttons) {
|
for (const auto& b : buttons) {
|
||||||
b->draw(renderer);
|
b->draw(renderer);
|
||||||
}
|
}
|
||||||
@ -724,6 +863,22 @@ namespace ZL {
|
|||||||
s->buildKnobMesh();
|
s->buildKnobMesh();
|
||||||
if (s->onValueChanged) s->onValueChanged(s->name, s->value);
|
if (s->onValueChanged) s->onValueChanged(s->name, s->value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pressedJoystick) {
|
||||||
|
auto j = pressedJoystick;
|
||||||
|
float dx = (float)x - j->centerX;
|
||||||
|
float dy = (float)y - j->centerY;
|
||||||
|
float dist = std::sqrt(dx * dx + dy * dy);
|
||||||
|
|
||||||
|
if (dist > j->baseRadius) {
|
||||||
|
dx = dx / dist * j->baseRadius;
|
||||||
|
dy = dy / dist * j->baseRadius;
|
||||||
|
}
|
||||||
|
|
||||||
|
j->knobOffsetX = dx;
|
||||||
|
j->knobOffsetY = dy;
|
||||||
|
j->buildKnobMesh();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void UiManager::onMouseDown(int x, int y) {
|
void UiManager::onMouseDown(int x, int y) {
|
||||||
@ -752,6 +907,27 @@ namespace ZL {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (auto& j : joysticks) {
|
||||||
|
if (j->contains((float)x, (float)y)) {
|
||||||
|
pressedJoystick = j;
|
||||||
|
j->isActive = true;
|
||||||
|
|
||||||
|
float dx = (float)x - j->centerX;
|
||||||
|
float dy = (float)y - j->centerY;
|
||||||
|
float dist = std::sqrt(dx * dx + dy * dy);
|
||||||
|
|
||||||
|
if (dist > j->baseRadius) {
|
||||||
|
dx = dx / dist * j->baseRadius;
|
||||||
|
dy = dy / dist * j->baseRadius;
|
||||||
|
}
|
||||||
|
|
||||||
|
j->knobOffsetX = dx;
|
||||||
|
j->knobOffsetY = dy;
|
||||||
|
j->buildKnobMesh();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void UiManager::onMouseUp(int x, int y) {
|
void UiManager::onMouseUp(int x, int y) {
|
||||||
@ -771,6 +947,14 @@ namespace ZL {
|
|||||||
if (pressedSlider) {
|
if (pressedSlider) {
|
||||||
pressedSlider.reset();
|
pressedSlider.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pressedJoystick) {
|
||||||
|
pressedJoystick->knobOffsetX = 0;
|
||||||
|
pressedJoystick->knobOffsetY = 0;
|
||||||
|
pressedJoystick->isActive = false;
|
||||||
|
pressedJoystick->buildKnobMesh();
|
||||||
|
pressedJoystick.reset();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<UiButton> UiManager::findButton(const std::string& name) {
|
std::shared_ptr<UiButton> UiManager::findButton(const std::string& name) {
|
||||||
|
|||||||
@ -9,6 +9,8 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <cmath>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
namespace ZL {
|
namespace ZL {
|
||||||
|
|
||||||
@ -71,6 +73,47 @@ namespace ZL {
|
|||||||
void draw(Renderer& renderer) const;
|
void draw(Renderer& renderer) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct UiJoystick {
|
||||||
|
std::string name;
|
||||||
|
float centerX = 0; // центр джойстика (UI координаты)
|
||||||
|
float centerY = 0;
|
||||||
|
float baseRadius = 100; // радиус основания
|
||||||
|
float knobRadius = 40; // радиус ручки
|
||||||
|
|
||||||
|
float knobOffsetX = 0; // смещение ручки от центра
|
||||||
|
float knobOffsetY = 0;
|
||||||
|
|
||||||
|
bool isActive = false; // нажат ли джойстик
|
||||||
|
|
||||||
|
std::shared_ptr<Texture> texBase; // текстура основания
|
||||||
|
std::shared_ptr<Texture> texKnob; // текстура ручки
|
||||||
|
|
||||||
|
VertexRenderStruct baseMesh;
|
||||||
|
VertexRenderStruct knobMesh;
|
||||||
|
|
||||||
|
// Возвращает направление -1..1 по каждой оси
|
||||||
|
float getDirectionX() const { return baseRadius > 0 ? knobOffsetX / baseRadius : 0; }
|
||||||
|
float getDirectionY() const { return baseRadius > 0 ? -knobOffsetY / baseRadius : 0; }
|
||||||
|
|
||||||
|
// Возвращает силу 0..1
|
||||||
|
float getMagnitude() const {
|
||||||
|
if (baseRadius <= 0) return 0;
|
||||||
|
float dist = std::sqrt(knobOffsetX * knobOffsetX + knobOffsetY * knobOffsetY);
|
||||||
|
float ratio = dist / baseRadius;
|
||||||
|
return ratio < 1.0f ? ratio : 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool contains(float px, float py) const {
|
||||||
|
float dx = px - centerX;
|
||||||
|
float dy = py - centerY;
|
||||||
|
return (dx * dx + dy * dy) <= (baseRadius * baseRadius);
|
||||||
|
}
|
||||||
|
|
||||||
|
void buildBaseMesh();
|
||||||
|
void buildKnobMesh();
|
||||||
|
void draw(Renderer& renderer) const;
|
||||||
|
};
|
||||||
|
|
||||||
struct UiNode {
|
struct UiNode {
|
||||||
std::string type;
|
std::string type;
|
||||||
UiRect rect;
|
UiRect rect;
|
||||||
@ -108,7 +151,7 @@ namespace ZL {
|
|||||||
void onMouseUp(int x, int y);
|
void onMouseUp(int x, int y);
|
||||||
|
|
||||||
bool isUiInteraction() const {
|
bool isUiInteraction() const {
|
||||||
return pressedButton != nullptr || pressedSlider != nullptr;
|
return pressedButton != nullptr || pressedSlider != nullptr || pressedJoystick != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void stopAllAnimations() {
|
void stopAllAnimations() {
|
||||||
@ -135,6 +178,12 @@ namespace ZL {
|
|||||||
bool setSliderCallback(const std::string& name, std::function<void(const std::string&, float)> cb);
|
bool setSliderCallback(const std::string& name, std::function<void(const std::string&, float)> cb);
|
||||||
bool setSliderValue(const std::string& name, float value); // programmatic set (clamped 0..1)
|
bool setSliderValue(const std::string& name, float value); // programmatic set (clamped 0..1)
|
||||||
|
|
||||||
|
bool addJoystick(const std::string& name, float centerX, float centerY, float baseRadius, float knobRadius,
|
||||||
|
Renderer& renderer, const std::string& zipFile,
|
||||||
|
const std::string& basePath, const std::string& knobPath);
|
||||||
|
std::shared_ptr<UiJoystick> findJoystick(const std::string& name);
|
||||||
|
void resetJoystick(const std::string& name); // сбросить ручку в центр
|
||||||
|
|
||||||
bool pushMenuFromFile(const std::string& path, Renderer& renderer, const std::string& zipFile = "");
|
bool pushMenuFromFile(const std::string& path, Renderer& renderer, const std::string& zipFile = "");
|
||||||
bool popMenu();
|
bool popMenu();
|
||||||
void clearMenuStack();
|
void clearMenuStack();
|
||||||
@ -176,12 +225,14 @@ namespace ZL {
|
|||||||
std::shared_ptr<UiNode> root;
|
std::shared_ptr<UiNode> root;
|
||||||
std::vector<std::shared_ptr<UiButton>> buttons;
|
std::vector<std::shared_ptr<UiButton>> buttons;
|
||||||
std::vector<std::shared_ptr<UiSlider>> sliders;
|
std::vector<std::shared_ptr<UiSlider>> sliders;
|
||||||
|
std::vector<std::shared_ptr<UiJoystick>> joysticks;
|
||||||
|
|
||||||
std::map<std::shared_ptr<UiNode>, std::vector<ActiveAnim>> nodeActiveAnims;
|
std::map<std::shared_ptr<UiNode>, std::vector<ActiveAnim>> nodeActiveAnims;
|
||||||
std::map<std::pair<std::string, std::string>, std::function<void()>> animCallbacks; // key: (nodeName, animName)
|
std::map<std::pair<std::string, std::string>, std::function<void()>> animCallbacks; // key: (nodeName, animName)
|
||||||
|
|
||||||
std::shared_ptr<UiButton> pressedButton;
|
std::shared_ptr<UiButton> pressedButton;
|
||||||
std::shared_ptr<UiSlider> pressedSlider;
|
std::shared_ptr<UiSlider> pressedSlider;
|
||||||
|
std::shared_ptr<UiJoystick> pressedJoystick;
|
||||||
|
|
||||||
struct MenuState {
|
struct MenuState {
|
||||||
std::shared_ptr<UiNode> root;
|
std::shared_ptr<UiNode> root;
|
||||||
|
|||||||
@ -1,112 +1,135 @@
|
|||||||
#include "ClientState.h"
|
#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) {
|
static inline float DegToRad(float deg) {
|
||||||
if (discreteMag > 0.01f)
|
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;
|
||||||
|
|
||||||
// Целевая угловая скорость (дискретная сила определяет модуль вектора)
|
// ---------- SETTINGS (tweak these) ----------
|
||||||
// Вектор {cos, sin, 0} дает нам направление отклонения джойстика
|
// Max speed in your world units/sec.
|
||||||
Eigen::Vector3f targetAngularVelDir(sinf(rad), cosf(rad), 0.0f);
|
// Choose something that makes sense vs projectileSpeed (60 in Game.cpp).
|
||||||
Eigen::Vector3f targetAngularVelocity = targetAngularVelDir * discreteMag;
|
const float MAX_SPEED = 80.0f;
|
||||||
|
|
||||||
Eigen::Vector3f diffVel = targetAngularVelocity - currentAngularVelocity;
|
// Accel/decel in units/sec^2
|
||||||
float diffLen = diffVel.norm();
|
const float ACCEL = 120.0f;
|
||||||
|
const float DECEL = 160.0f;
|
||||||
|
|
||||||
if (diffLen > 0.0001f) {
|
// Rotation smoothing factor (0..1 per frame). Uses your constant as "per ms".
|
||||||
// Вычисляем, на сколько мы можем изменить скорость в этом кадре
|
// With ROTATION_SENSITIVITY=0.002 and delta=10ms -> 0.02 (nice).
|
||||||
float maxChange = ANGULAR_ACCEL * static_cast<float>(delta);
|
const float ROT_T = std::clamp(ROTATION_SENSITIVITY * static_cast<float>(deltaMs), 0.0f, 1.0f);
|
||||||
|
// -------------------------------------------
|
||||||
|
|
||||||
if (diffLen <= maxChange) {
|
// Input validity
|
||||||
// Если до цели осталось меньше, чем шаг ускорения — просто прыгаем в цель
|
const bool hasInput = (discreteMag > 0.01f) && (currentAngularVelocity.squaredNorm() > 1e-6f);
|
||||||
currentAngularVelocity = targetAngularVelocity;
|
|
||||||
|
// 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
else {
|
// Heading direction in world XZ plane:
|
||||||
// Линейно двигаемся в сторону целевого вектора
|
// IMPORTANT: your Game.cpp must set discreteAngle consistently with:
|
||||||
currentAngularVelocity += (diffVel / diffLen) * maxChange;
|
// ang = atan2(worldMove.x, worldMove.z)
|
||||||
}
|
// So we reconstruct as:
|
||||||
}
|
// x = sin(rad), z = cos(rad)
|
||||||
}
|
Eigen::Vector3f moveDirWorld = currentAngularVelocity.normalized();
|
||||||
else
|
|
||||||
|
|
||||||
|
Eigen::Vector3f veccc(0.0f, 0.0f, -1.0f);
|
||||||
|
|
||||||
|
Eigen::Vector3f veccc_rotated = rotation * veccc;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Move in world
|
||||||
|
//position += moveDirWorld * (velocity * dt);
|
||||||
|
position += veccc_rotated * (velocity * dt);
|
||||||
|
|
||||||
|
// 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)
|
||||||
{
|
{
|
||||||
float currentSpeed = currentAngularVelocity.norm();
|
Eigen::Vector3f worldUp(0.0f, 1.0f, 0.0f);
|
||||||
|
|
||||||
if (currentSpeed > 0.0001f) {
|
// local forward = (0,0,-1) => значит Z-ось мира должна смотреть в -moveDirWorld
|
||||||
float drop = ANGULAR_ACCEL * static_cast<float>(delta);
|
Eigen::Vector3f zAxis = -moveDirWorld;
|
||||||
if (currentSpeed <= drop) {
|
Eigen::Vector3f xAxis = worldUp.cross(zAxis);
|
||||||
currentAngularVelocity = Eigen::Vector3f::Zero();
|
|
||||||
}
|
if (xAxis.squaredNorm() < 1e-6f) xAxis = Eigen::Vector3f(1, 0, 0);
|
||||||
else {
|
xAxis.normalize();
|
||||||
// Уменьшаем модуль вектора, сохраняя направление
|
|
||||||
currentAngularVelocity -= (currentAngularVelocity / currentSpeed) * drop;
|
Eigen::Vector3f yAxis = zAxis.cross(xAxis).normalized();
|
||||||
}
|
|
||||||
}
|
Eigen::Matrix3f R;
|
||||||
|
R.col(0) = xAxis;
|
||||||
|
R.col(1) = yAxis;
|
||||||
|
R.col(2) = zAxis;
|
||||||
|
|
||||||
|
Eigen::Quaternionf qCur(rotation);
|
||||||
|
Eigen::Quaternionf qTar(R);
|
||||||
|
|
||||||
|
Eigen::Quaternionf qNew = qCur.slerp(ROT_T, qTar).normalized();
|
||||||
|
rotation = qNew.toRotationMatrix();
|
||||||
}
|
}
|
||||||
|
|
||||||
float speedScale = currentAngularVelocity.norm();
|
|
||||||
if (speedScale > 0.0001f) {
|
|
||||||
// Коэффициент чувствительности вращения
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ClientState::apply_lag_compensation(std::chrono::system_clock::time_point nowTime)
|
||||||
// 4. Линейное изменение линейной скорости
|
|
||||||
float shipDesiredVelocity = selectedVelocity * 100.f;
|
|
||||||
|
|
||||||
if (velocity < shipDesiredVelocity)
|
|
||||||
{
|
{
|
||||||
velocity += delta * SHIP_ACCEL;
|
|
||||||
if (velocity > shipDesiredVelocity)
|
|
||||||
{
|
|
||||||
velocity = shipDesiredVelocity;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (velocity > shipDesiredVelocity)
|
|
||||||
{
|
|
||||||
velocity -= delta * SHIP_ACCEL;
|
|
||||||
if (velocity < shipDesiredVelocity)
|
|
||||||
{
|
|
||||||
velocity = shipDesiredVelocity;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fabs(velocity) > 0.01f)
|
|
||||||
{
|
|
||||||
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;
|
long long deltaMs = 0;
|
||||||
if (nowTime > lastUpdateServerTime) {
|
if (nowTime > lastUpdateServerTime) {
|
||||||
deltaMs = std::chrono::duration_cast<std::chrono::milliseconds>(nowTime - lastUpdateServerTime).count();
|
deltaMs = std::chrono::duration_cast<std::chrono::milliseconds>(nowTime - lastUpdateServerTime).count();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Защита от слишком больших скачков (Clamp)
|
long long final_lag_ms = deltaMs; // можно clamp, если нужно
|
||||||
// Если лаг более 500мс, ограничиваем его, чтобы избежать резких рывков
|
|
||||||
long long final_lag_ms = deltaMs;//min(deltaMs, 500ll);
|
|
||||||
|
|
||||||
if (final_lag_ms > 0) {
|
if (final_lag_ms > 0) {
|
||||||
// Доматываем симуляцию на величину задержки
|
simulate_physics(static_cast<size_t>(final_lag_ms));
|
||||||
// Мы предполагаем, что за это время параметры управления не менялись
|
|
||||||
simulate_physics(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]) };
|
position = { std::stof(parts[startFrom]), std::stof(parts[startFrom + 1]), std::stof(parts[startFrom + 2]) };
|
||||||
|
|
||||||
Eigen::Quaternionf q(
|
Eigen::Quaternionf q(
|
||||||
@ -116,10 +139,12 @@ void ClientState::handle_full_sync(const std::vector<std::string>& parts, int st
|
|||||||
std::stof(parts[startFrom + 6]));
|
std::stof(parts[startFrom + 6]));
|
||||||
rotation = q.toRotationMatrix();
|
rotation = q.toRotationMatrix();
|
||||||
|
|
||||||
|
// For compatibility: keep reading it, but we won't use it anymore
|
||||||
currentAngularVelocity = Eigen::Vector3f{
|
currentAngularVelocity = Eigen::Vector3f{
|
||||||
std::stof(parts[startFrom + 7]),
|
std::stof(parts[startFrom + 7]),
|
||||||
std::stof(parts[startFrom + 8]),
|
std::stof(parts[startFrom + 8]),
|
||||||
std::stof(parts[startFrom + 9]) };
|
std::stof(parts[startFrom + 9]) };
|
||||||
|
|
||||||
velocity = std::stof(parts[startFrom + 10]);
|
velocity = std::stof(parts[startFrom + 10]);
|
||||||
selectedVelocity = std::stoi(parts[startFrom + 11]);
|
selectedVelocity = std::stoi(parts[startFrom + 11]);
|
||||||
discreteMag = std::stof(parts[startFrom + 12]);
|
discreteMag = std::stof(parts[startFrom + 12]);
|
||||||
@ -130,87 +155,72 @@ std::string ClientState::formPingMessageContent()
|
|||||||
{
|
{
|
||||||
Eigen::Quaternionf q(rotation);
|
Eigen::Quaternionf q(rotation);
|
||||||
|
|
||||||
std::string pingMsg = std::to_string(position.x()) + ":"
|
std::string pingMsg =
|
||||||
+ std::to_string(position.y()) + ":"
|
std::to_string(position.x()) + ":" +
|
||||||
+ std::to_string(position.z()) + ":"
|
std::to_string(position.y()) + ":" +
|
||||||
+ std::to_string(q.w()) + ":"
|
std::to_string(position.z()) + ":" +
|
||||||
+ std::to_string(q.x()) + ":"
|
std::to_string(q.w()) + ":" +
|
||||||
+ std::to_string(q.y()) + ":"
|
std::to_string(q.x()) + ":" +
|
||||||
+ std::to_string(q.z()) + ":"
|
std::to_string(q.y()) + ":" +
|
||||||
+ std::to_string(currentAngularVelocity.x()) + ":"
|
std::to_string(q.z()) + ":" +
|
||||||
+ std::to_string(currentAngularVelocity.y()) + ":"
|
std::to_string(currentAngularVelocity.x()) + ":" +
|
||||||
+ std::to_string(currentAngularVelocity.z()) + ":"
|
std::to_string(currentAngularVelocity.y()) + ":" +
|
||||||
+ std::to_string(velocity) + ":"
|
std::to_string(currentAngularVelocity.z()) + ":" +
|
||||||
+ std::to_string(selectedVelocity) + ":"
|
std::to_string(velocity) + ":" +
|
||||||
+ std::to_string(discreteMag) + ":" // Используем те же static переменные из блока ROT
|
std::to_string(selectedVelocity) + ":" +
|
||||||
+ std::to_string(discreteAngle);
|
std::to_string(discreteMag) + ":" +
|
||||||
|
std::to_string(discreteAngle);
|
||||||
|
|
||||||
return pingMsg;
|
return pingMsg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void ClientStateInterval::add_state(const ClientState& state)
|
void ClientStateInterval::add_state(const ClientState& state)
|
||||||
{
|
{
|
||||||
auto nowTime = std::chrono::system_clock::now();
|
auto nowTime = std::chrono::system_clock::now();
|
||||||
|
|
||||||
if (timedStates.size() > 0 && timedStates[timedStates.size() - 1].lastUpdateServerTime == state.lastUpdateServerTime)
|
if (!timedStates.empty() && timedStates.back().lastUpdateServerTime == state.lastUpdateServerTime) {
|
||||||
{
|
timedStates.back() = state;
|
||||||
timedStates[timedStates.size() - 1] = state;
|
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
{
|
|
||||||
timedStates.push_back(state);
|
timedStates.push_back(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto cutoff_time = nowTime - std::chrono::milliseconds(CUTOFF_TIME);
|
auto cutoff_time = nowTime - std::chrono::milliseconds(CUTOFF_TIME);
|
||||||
|
while (!timedStates.empty() && timedStates.front().lastUpdateServerTime < cutoff_time) {
|
||||||
while (timedStates.size() > 0 && timedStates[0].lastUpdateServerTime < cutoff_time)
|
|
||||||
{
|
|
||||||
timedStates.erase(timedStates.begin());
|
timedStates.erase(timedStates.begin());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ClientStateInterval::canFetchClientStateAtTime(std::chrono::system_clock::time_point targetTime) const
|
bool ClientStateInterval::canFetchClientStateAtTime(std::chrono::system_clock::time_point targetTime) const
|
||||||
{
|
{
|
||||||
if (timedStates.empty())
|
if (timedStates.empty()) return false;
|
||||||
{
|
if (timedStates.front().lastUpdateServerTime > targetTime) return false;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (timedStates[0].lastUpdateServerTime > targetTime)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
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;
|
ClientState closestState;
|
||||||
|
|
||||||
if (timedStates.empty())
|
if (timedStates.size() == 1) {
|
||||||
{
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
closestState = timedStates[0];
|
closestState = timedStates[0];
|
||||||
closestState.apply_lag_compensation(targetTime);
|
closestState.apply_lag_compensation(targetTime);
|
||||||
return closestState;
|
return closestState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i + 1 < timedStates.size(); ++i) {
|
||||||
for (size_t i = 0; i < timedStates.size() - 1; ++i)
|
|
||||||
{
|
|
||||||
const auto& earlierState = timedStates[i];
|
const auto& earlierState = timedStates[i];
|
||||||
const auto& laterState = timedStates[i + 1];
|
const auto& laterState = timedStates[i + 1];
|
||||||
if (earlierState.lastUpdateServerTime <= targetTime && laterState.lastUpdateServerTime >= targetTime)
|
|
||||||
|
if (earlierState.lastUpdateServerTime <= targetTime &&
|
||||||
|
laterState.lastUpdateServerTime >= targetTime)
|
||||||
{
|
{
|
||||||
closestState = earlierState;
|
closestState = earlierState;
|
||||||
closestState.apply_lag_compensation(targetTime);
|
closestState.apply_lag_compensation(targetTime);
|
||||||
@ -218,7 +228,7 @@ ClientState ClientStateInterval::fetchClientStateAtTime(std::chrono::system_cloc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
closestState = timedStates[timedStates.size() - 1];
|
closestState = timedStates.back();
|
||||||
closestState.apply_lag_compensation(targetTime);
|
closestState.apply_lag_compensation(targetTime);
|
||||||
return closestState;
|
return closestState;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,12 @@
|
|||||||
#include "PlanetObject.h"
|
#include "GameConfig.h"
|
||||||
|
#include "PlanetObject.h"
|
||||||
#include <random>
|
#include <random>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include "render/OpenGlExtensions.h"
|
#include "render/OpenGlExtensions.h"
|
||||||
#include "Environment.h"
|
#include "Environment.h"
|
||||||
#include "StoneObject.h"
|
#include "StoneObject.h"
|
||||||
#include "utils/TaskManager.h"
|
#include "utils/TaskManager.h"
|
||||||
|
#include <algorithm>
|
||||||
namespace ZL {
|
namespace ZL {
|
||||||
|
|
||||||
#if defined EMSCRIPTEN || defined __ANDROID__
|
#if defined EMSCRIPTEN || defined __ANDROID__
|
||||||
@ -57,10 +58,11 @@ namespace ZL {
|
|||||||
return rot;
|
return rot;
|
||||||
}
|
}
|
||||||
|
|
||||||
PlanetObject::PlanetObject(Renderer& iRenderer, TaskManager& iTaskManager, MainThreadHandler& iMainThreadHandler)
|
PlanetObject::PlanetObject(Renderer& iRenderer, TaskManager& iTaskManager, MainThreadHandler& iMainThreadHandler, Camera& iCamera)
|
||||||
: renderer(iRenderer),
|
: renderer(iRenderer),
|
||||||
taskManager(iTaskManager),
|
taskManager(iTaskManager),
|
||||||
mainThreadHandler(iMainThreadHandler)
|
mainThreadHandler(iMainThreadHandler),
|
||||||
|
camera(iCamera)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,7 +75,7 @@ namespace ZL {
|
|||||||
int lodIndex = planetData.getMaxLodIndex();
|
int lodIndex = planetData.getMaxLodIndex();
|
||||||
planetRenderStruct.data = planetData.getLodLevel(lodIndex).vertexData;
|
planetRenderStruct.data = planetData.getLodLevel(lodIndex).vertexData;
|
||||||
|
|
||||||
//planetRenderStruct.data.PositionData.resize(9);
|
|
||||||
planetRenderStruct.RefreshVBO();
|
planetRenderStruct.RefreshVBO();
|
||||||
|
|
||||||
|
|
||||||
@ -111,34 +113,30 @@ namespace ZL {
|
|||||||
// 2. Получаем список видимых треугольников
|
// 2. Получаем список видимых треугольников
|
||||||
auto newIndices = planetData.getTrianglesUnderCameraNew2(Environment::shipState.position);
|
auto newIndices = planetData.getTrianglesUnderCameraNew2(Environment::shipState.position);
|
||||||
std::sort(newIndices.begin(), newIndices.end());
|
std::sort(newIndices.begin(), newIndices.end());
|
||||||
|
// 3. Запуск генерации для новых индексов
|
||||||
|
for (int idx : newIndices) {
|
||||||
|
if (planetStones.statuses[idx] == ChunkStatus::Empty) {
|
||||||
|
planetStones.statuses[idx] = ChunkStatus::Generating;
|
||||||
|
|
||||||
// 3. Анализируем, что нужно загрузить
|
// Запускаем задачу в фоне
|
||||||
for (int triIdx : newIndices) {
|
|
||||||
if (planetStones.statuses[triIdx] == ChunkStatus::Empty) {
|
|
||||||
// Помечаем, чтобы не спамить задачами
|
|
||||||
planetStones.statuses[triIdx] = ChunkStatus::Generating;
|
|
||||||
|
|
||||||
// Отправляем тяжелую математику в TaskManager
|
taskManager.EnqueueBackgroundTask([this, idx]() {
|
||||||
taskManager.EnqueueBackgroundTask([this, triIdx]() {
|
// Имитация тяжелой работы (генерация меша)
|
||||||
// Выполняется в фоновом потоке: только генерация геометрии в RAM
|
// Тут мы просто копируем базовый камень + скейл/поворот
|
||||||
float scaleModifier = 1.0f;
|
VertexDataStruct newData = planetStones.inflateOneDataOnly(idx, 0.75f); // 0.75f - scaleModifier
|
||||||
VertexDataStruct generatedData = planetStones.inflateOneDataOnly(triIdx, scaleModifier);
|
|
||||||
|
|
||||||
// Передаем задачу на загрузку в GPU в очередь главного потока
|
// Когда готово — перекидываем в главный поток для загрузки в GPU
|
||||||
this->mainThreadHandler.EnqueueMainThreadTask([this, triIdx, data = std::move(generatedData)]() mutable {
|
mainThreadHandler.EnqueueMainThreadTask([this, idx, data = std::move(newData)]() mutable {
|
||||||
// Проверяем актуальность: если треугольник всё еще в списке видимых
|
// Проверяем, всё ли еще актуален этот чанк (вдруг игрок улетел)
|
||||||
auto it = std::find(triangleIndicesToDraw.begin(), triangleIndicesToDraw.end(), triIdx);
|
// Но пока упростим: всегда грузим, а чистильщик (пункт 4) потом удалит если что
|
||||||
if (it != triangleIndicesToDraw.end()) {
|
if (planetStones.statuses[idx] == ChunkStatus::Generating) {
|
||||||
stonesToRender[triIdx].data = std::move(data);
|
stonesToRender[idx].data = std::move(data);
|
||||||
stonesToRender[triIdx].RefreshVBO(); // OpenGL вызов
|
stonesToRender[idx].RefreshVBO();
|
||||||
planetStones.statuses[triIdx] = ChunkStatus::Live;
|
planetStones.statuses[idx] = ChunkStatus::Live;
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Если уже не нужен — просто сбрасываем статус
|
|
||||||
planetStones.statuses[triIdx] = ChunkStatus::Empty;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -212,8 +210,7 @@ namespace ZL {
|
|||||||
float centerX = (minX + maxX) * 0.5f;
|
float centerX = (minX + maxX) * 0.5f;
|
||||||
float centerY = (minY + maxY) * 0.5f;
|
float centerY = (minY + maxY) * 0.5f;
|
||||||
|
|
||||||
//width = width * 0.995;
|
|
||||||
//height = height * 0.995;
|
|
||||||
|
|
||||||
renderer.PushProjectionMatrix(
|
renderer.PushProjectionMatrix(
|
||||||
centerX - width*0.5, centerX + width * 0.5,
|
centerX - width*0.5, centerX + width * 0.5,
|
||||||
@ -263,7 +260,7 @@ namespace ZL {
|
|||||||
{
|
{
|
||||||
if (stoneMapFB == nullptr)
|
if (stoneMapFB == nullptr)
|
||||||
{
|
{
|
||||||
//stoneMapFB = std::make_unique<FrameBuffer>(512, 512, true);
|
|
||||||
stoneMapFB = std::make_unique<FrameBuffer>(512, 512);
|
stoneMapFB = std::make_unique<FrameBuffer>(512, 512);
|
||||||
}
|
}
|
||||||
stoneMapFB->Bind();
|
stoneMapFB->Bind();
|
||||||
@ -281,10 +278,11 @@ namespace ZL {
|
|||||||
|
|
||||||
|
|
||||||
drawPlanet(renderer);
|
drawPlanet(renderer);
|
||||||
drawStones(renderer);
|
#if ENABLE_STONES
|
||||||
drawAtmosphere(renderer);
|
//drawStones(renderer);
|
||||||
|
#endif
|
||||||
|
//drawAtmosphere(renderer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlanetObject::drawPlanet(Renderer& renderer)
|
void PlanetObject::drawPlanet(Renderer& renderer)
|
||||||
{
|
{
|
||||||
static const std::string defaultShaderName = "planetLand";
|
static const std::string defaultShaderName = "planetLand";
|
||||||
@ -293,7 +291,6 @@ namespace ZL {
|
|||||||
static const std::string vColorName = "vColor";
|
static const std::string vColorName = "vColor";
|
||||||
static const std::string vNormalName = "vNormal";
|
static const std::string vNormalName = "vNormal";
|
||||||
static const std::string vTexCoordName = "vTexCoord";
|
static const std::string vTexCoordName = "vTexCoord";
|
||||||
static const std::string textureUniformName = "Texture";
|
|
||||||
|
|
||||||
renderer.shaderManager.PushShader(defaultShaderName);
|
renderer.shaderManager.PushShader(defaultShaderName);
|
||||||
|
|
||||||
@ -304,79 +301,69 @@ namespace ZL {
|
|||||||
renderer.EnableVertexAttribArray("vBinormal");
|
renderer.EnableVertexAttribArray("vBinormal");
|
||||||
renderer.EnableVertexAttribArray(vTexCoordName);
|
renderer.EnableVertexAttribArray(vTexCoordName);
|
||||||
|
|
||||||
|
|
||||||
float dist = planetData.distanceToPlanetSurfaceFast(Environment::shipState.position);
|
float dist = planetData.distanceToPlanetSurfaceFast(Environment::shipState.position);
|
||||||
auto zRange = planetData.calculateZRange(dist);
|
auto zRange = planetData.calculateZRange(dist);
|
||||||
const float currentZNear = zRange.first;
|
const float currentZNear = zRange.first;
|
||||||
const float currentZFar = zRange.second;
|
const float currentZFar = zRange.second;
|
||||||
|
|
||||||
// 2. Применяем динамическую матрицу проекции
|
renderer.PushPerspectiveProjectionMatrix(
|
||||||
renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5,
|
1.0 / 1.5,
|
||||||
static_cast<float>(Environment::width) / static_cast<float>(Environment::height),
|
static_cast<float>(Environment::width) / static_cast<float>(Environment::height),
|
||||||
currentZNear, currentZFar);
|
currentZNear, currentZFar
|
||||||
|
);
|
||||||
|
|
||||||
|
// View from Camera
|
||||||
renderer.PushMatrix();
|
renderer.PushMatrix();
|
||||||
renderer.LoadIdentity();
|
renderer.LoadIdentity();
|
||||||
renderer.TranslateMatrix({ 0,0, -1.0f * Environment::zoom });
|
renderer.PushSpecialMatrix(camera.getViewMatrix());
|
||||||
renderer.RotateMatrix(Environment::inverseShipMatrix);
|
|
||||||
|
|
||||||
renderer.TranslateMatrix(-Environment::shipState.position);
|
// Planet at world origin (если у тебя есть PLANET_CENTER_OFFSET — добавь Translate туда)
|
||||||
|
// renderer.TranslateMatrix(PlanetData::PLANET_CENTER_OFFSET);
|
||||||
const Matrix4f viewMatrix = renderer.GetCurrentModelViewMatrix();
|
|
||||||
|
|
||||||
renderer.RenderUniform1i("Texture", 0);
|
renderer.RenderUniform1i("Texture", 0);
|
||||||
renderer.RenderUniform1i("BakedTexture", 1);
|
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("uHeightScale", 0.0f);
|
||||||
|
|
||||||
renderer.RenderUniform1f("uDistanceToPlanetSurface", dist);
|
renderer.RenderUniform1f("uDistanceToPlanetSurface", dist);
|
||||||
renderer.RenderUniform1f("uCurrentZFar", currentZFar);
|
renderer.RenderUniform1f("uCurrentZFar", currentZFar);
|
||||||
|
|
||||||
// Направление на солнце в мировом пространстве
|
Eigen::Vector3f sunDirWorld = Eigen::Vector3f(1.0f, -1.0f, -1.0f).normalized();
|
||||||
Vector3f sunDirWorld = Vector3f(1.0f, -1.0f, -1.0f).normalized();
|
|
||||||
renderer.RenderUniform3fv("uLightDirWorld", sunDirWorld.data());
|
renderer.RenderUniform3fv("uLightDirWorld", sunDirWorld.data());
|
||||||
|
|
||||||
// Направление от центра планеты к игроку для расчета дня/ночи
|
Eigen::Vector3f playerDirWorld = Environment::shipState.position.normalized();
|
||||||
Vector3f playerDirWorld = Environment::shipState.position.normalized();
|
|
||||||
renderer.RenderUniform3fv("uPlayerDirWorld", playerDirWorld.data());
|
renderer.RenderUniform3fv("uPlayerDirWorld", playerDirWorld.data());
|
||||||
|
|
||||||
// Тот же фактор освещенности игрока
|
float playerLightFactor = (std::max)(0.0f, (playerDirWorld.dot(-sunDirWorld) + 0.2f) / 1.2f);
|
||||||
float playerLightFactor = playerDirWorld.dot(-sunDirWorld);
|
|
||||||
playerLightFactor = max(0.0f, (playerLightFactor + 0.2f) / 1.2f);
|
|
||||||
renderer.RenderUniform1f("uPlayerLightFactor", playerLightFactor);
|
renderer.RenderUniform1f("uPlayerLightFactor", playerLightFactor);
|
||||||
|
|
||||||
|
|
||||||
glActiveTexture(GL_TEXTURE1);
|
glActiveTexture(GL_TEXTURE1);
|
||||||
glBindTexture(GL_TEXTURE_2D, stoneMapFB->getTextureID());
|
glBindTexture(GL_TEXTURE_2D, stoneMapFB->getTextureID());
|
||||||
|
|
||||||
glActiveTexture(GL_TEXTURE0);
|
glActiveTexture(GL_TEXTURE0);
|
||||||
glBindTexture(GL_TEXTURE_2D, sandTexture->getTexID());
|
glBindTexture(GL_TEXTURE_2D, sandTexture->getTexID());
|
||||||
//glBindTexture(GL_TEXTURE_2D, stoneMapFB->getTextureID());
|
|
||||||
|
|
||||||
renderer.DrawVertexRenderStruct(planetRenderStruct);
|
renderer.DrawVertexRenderStruct(planetRenderStruct);
|
||||||
CheckGlError();
|
CheckGlError();
|
||||||
|
|
||||||
renderer.PopMatrix();
|
renderer.PopMatrix(); // special matrix
|
||||||
|
renderer.PopMatrix(); // original push
|
||||||
|
|
||||||
renderer.PopProjectionMatrix();
|
renderer.PopProjectionMatrix();
|
||||||
|
|
||||||
renderer.DisableVertexAttribArray(vTexCoordName);
|
renderer.DisableVertexAttribArray(vTexCoordName);
|
||||||
renderer.DisableVertexAttribArray(vNormalName);
|
renderer.DisableVertexAttribArray(vNormalName);
|
||||||
renderer.DisableVertexAttribArray("vTangent");
|
renderer.DisableVertexAttribArray("vTangent");
|
||||||
renderer.DisableVertexAttribArray("vBinormal");
|
renderer.DisableVertexAttribArray("vBinormal");
|
||||||
renderer.DisableVertexAttribArray(vColorName);
|
renderer.DisableVertexAttribArray(vColorName);
|
||||||
renderer.DisableVertexAttribArray(vPositionName);
|
renderer.DisableVertexAttribArray(vPositionName);
|
||||||
|
|
||||||
renderer.shaderManager.PopShader();
|
renderer.shaderManager.PopShader();
|
||||||
CheckGlError();
|
CheckGlError();
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlanetObject::drawStones(Renderer& renderer)
|
void PlanetObject::drawStones(Renderer& renderer)
|
||||||
{
|
{
|
||||||
static const std::string defaultShaderName2 = "planetStone";
|
static const std::string defaultShaderName2 = "planetStone";
|
||||||
@ -384,95 +371,81 @@ namespace ZL {
|
|||||||
static const std::string vColorName = "vColor";
|
static const std::string vColorName = "vColor";
|
||||||
static const std::string vNormalName = "vNormal";
|
static const std::string vNormalName = "vNormal";
|
||||||
static const std::string vTexCoordName = "vTexCoord";
|
static const std::string vTexCoordName = "vTexCoord";
|
||||||
static const std::string textureUniformName = "Texture";
|
|
||||||
|
|
||||||
renderer.shaderManager.PushShader(defaultShaderName2);
|
renderer.shaderManager.PushShader(defaultShaderName2);
|
||||||
renderer.RenderUniform1i(textureUniformName, 0);
|
renderer.RenderUniform1i("Texture", 0);
|
||||||
|
|
||||||
renderer.EnableVertexAttribArray(vPositionName);
|
renderer.EnableVertexAttribArray(vPositionName);
|
||||||
renderer.EnableVertexAttribArray(vColorName);
|
renderer.EnableVertexAttribArray(vColorName);
|
||||||
renderer.EnableVertexAttribArray(vNormalName);
|
renderer.EnableVertexAttribArray(vNormalName);
|
||||||
renderer.EnableVertexAttribArray(vTexCoordName);
|
renderer.EnableVertexAttribArray(vTexCoordName);
|
||||||
|
|
||||||
|
|
||||||
float dist = planetData.distanceToPlanetSurfaceFast(Environment::shipState.position);
|
float dist = planetData.distanceToPlanetSurfaceFast(Environment::shipState.position);
|
||||||
auto zRange = planetData.calculateZRange(dist);
|
auto zRange = planetData.calculateZRange(dist);
|
||||||
const float currentZNear = zRange.first;
|
const float currentZNear = zRange.first;
|
||||||
const float currentZFar = zRange.second;
|
const float currentZFar = zRange.second;
|
||||||
|
|
||||||
|
renderer.PushPerspectiveProjectionMatrix(
|
||||||
// 2. Применяем динамическую матрицу проекции
|
1.0 / 1.5,
|
||||||
renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5,
|
|
||||||
static_cast<float>(Environment::width) / static_cast<float>(Environment::height),
|
static_cast<float>(Environment::width) / static_cast<float>(Environment::height),
|
||||||
currentZNear, currentZFar);
|
currentZNear, currentZFar
|
||||||
|
);
|
||||||
|
|
||||||
renderer.PushMatrix();
|
renderer.PushMatrix();
|
||||||
renderer.LoadIdentity();
|
renderer.LoadIdentity();
|
||||||
renderer.TranslateMatrix({ 0,0, -1.0f * Environment::zoom });
|
renderer.PushSpecialMatrix(camera.getViewMatrix());
|
||||||
renderer.RotateMatrix(Environment::inverseShipMatrix);
|
|
||||||
renderer.TranslateMatrix(-Environment::shipState.position);
|
|
||||||
|
|
||||||
renderer.RenderUniform1f("uDistanceToPlanetSurface", dist);
|
renderer.RenderUniform1f("uDistanceToPlanetSurface", dist);
|
||||||
renderer.RenderUniform1f("uCurrentZFar", currentZFar);
|
renderer.RenderUniform1f("uCurrentZFar", currentZFar);
|
||||||
renderer.RenderUniform3fv("uViewPos", Environment::shipState.position.data());
|
|
||||||
//std::cout << "uViewPos" << Environment::shipState.position << std::endl;
|
Eigen::Vector3f camPos = camera.getPosition();
|
||||||
// PlanetObject.cpp, метод drawStones
|
renderer.RenderUniform3fv("uViewPos", camPos.data());
|
||||||
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());
|
renderer.RenderUniform3fv("uLightDirWorld", sunDirWorld.data());
|
||||||
|
|
||||||
Vector3f playerDirWorld = Environment::shipState.position.normalized();
|
Eigen::Vector3f playerDirWorld = Environment::shipState.position.normalized();
|
||||||
float playerLightFactor = max(0.0f, (playerDirWorld.dot(-sunDirWorld) + 0.2f) / 1.2f);
|
float playerLightFactor = (std::max)(0.0f, (playerDirWorld.dot(-sunDirWorld) + 0.2f) / 1.2f);
|
||||||
renderer.RenderUniform1f("uPlayerLightFactor", playerLightFactor);
|
renderer.RenderUniform1f("uPlayerLightFactor", playerLightFactor);
|
||||||
|
|
||||||
glEnable(GL_CULL_FACE);
|
glEnable(GL_CULL_FACE);
|
||||||
glCullFace(GL_BACK);
|
glCullFace(GL_BACK);
|
||||||
glEnable(GL_BLEND);
|
glEnable(GL_BLEND);
|
||||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
|
||||||
glBindTexture(GL_TEXTURE_2D, stoneTexture->getTexID());
|
glBindTexture(GL_TEXTURE_2D, stoneTexture->getTexID());
|
||||||
|
|
||||||
/*
|
|
||||||
for (int i : triangleIndicesToDraw)
|
|
||||||
//for (int i = 0; i < stonesToRender.size(); i++)
|
|
||||||
{
|
|
||||||
if (stonesToRender[i].data.PositionData.size() > 0)
|
|
||||||
{
|
|
||||||
renderer.DrawVertexRenderStruct(stonesToRender[i]);
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
for (int i : triangleIndicesToDraw) {
|
for (int i : triangleIndicesToDraw) {
|
||||||
// КРИТИЧЕСКОЕ ИЗМЕНЕНИЕ:
|
|
||||||
// Проверяем, что данные не просто существуют, а загружены в GPU
|
|
||||||
if (planetStones.statuses[i] == ChunkStatus::Live) {
|
if (planetStones.statuses[i] == ChunkStatus::Live) {
|
||||||
// Дополнительная проверка на наличие данных (на всякий случай)
|
if (!stonesToRender[i].data.PositionData.empty()) {
|
||||||
if (stonesToRender[i].data.PositionData.size() > 0) {
|
|
||||||
renderer.DrawVertexRenderStruct(stonesToRender[i]);
|
renderer.DrawVertexRenderStruct(stonesToRender[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Если статус Generating или Empty — мы просто пропускаем этот индекс.
|
|
||||||
// Камни появятся на экране плавно, как только отработает TaskManager и RefreshVBO.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CheckGlError();
|
CheckGlError();
|
||||||
glDisable(GL_BLEND);
|
glDisable(GL_BLEND);
|
||||||
glDisable(GL_CULL_FACE);
|
glDisable(GL_CULL_FACE);
|
||||||
|
|
||||||
renderer.PopMatrix();
|
renderer.PopMatrix(); // special
|
||||||
|
renderer.PopMatrix(); // original
|
||||||
renderer.PopProjectionMatrix();
|
renderer.PopProjectionMatrix();
|
||||||
|
|
||||||
renderer.DisableVertexAttribArray(vTexCoordName);
|
renderer.DisableVertexAttribArray(vTexCoordName);
|
||||||
renderer.DisableVertexAttribArray(vNormalName);
|
renderer.DisableVertexAttribArray(vNormalName);
|
||||||
renderer.DisableVertexAttribArray(vColorName);
|
renderer.DisableVertexAttribArray(vColorName);
|
||||||
renderer.DisableVertexAttribArray(vPositionName);
|
renderer.DisableVertexAttribArray(vPositionName);
|
||||||
|
|
||||||
renderer.shaderManager.PopShader();
|
renderer.shaderManager.PopShader();
|
||||||
CheckGlError();
|
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 = "defaultAtmosphere";
|
||||||
//static const std::string defaultShaderName = "defaultColor";
|
|
||||||
static const std::string vPositionName = "vPosition";
|
static const std::string vPositionName = "vPosition";
|
||||||
static const std::string vNormalName = "vNormal";
|
static const std::string vNormalName = "vNormal";
|
||||||
//glClear(GL_DEPTH_BUFFER_BIT);
|
|
||||||
glDepthMask(GL_FALSE);
|
glDepthMask(GL_FALSE);
|
||||||
|
|
||||||
renderer.shaderManager.PushShader(defaultShaderName);
|
renderer.shaderManager.PushShader(defaultShaderName);
|
||||||
@ -484,90 +457,65 @@ namespace ZL {
|
|||||||
float currentZNear = zRange.first;
|
float currentZNear = zRange.first;
|
||||||
float currentZFar = zRange.second;
|
float currentZFar = zRange.second;
|
||||||
|
|
||||||
if (currentZNear < 200)
|
if (currentZNear < 200) {
|
||||||
{
|
|
||||||
currentZNear = 200;
|
currentZNear = 200;
|
||||||
currentZFar = currentZNear * 100;
|
currentZFar = currentZNear * 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Применяем динамическую матрицу проекции
|
renderer.PushPerspectiveProjectionMatrix(
|
||||||
renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5,
|
1.0 / 1.5,
|
||||||
static_cast<float>(Environment::width) / static_cast<float>(Environment::height),
|
static_cast<float>(Environment::width) / static_cast<float>(Environment::height),
|
||||||
currentZNear, currentZFar);
|
currentZNear, currentZFar
|
||||||
|
);
|
||||||
|
|
||||||
renderer.PushMatrix();
|
renderer.PushMatrix();
|
||||||
renderer.LoadIdentity();
|
renderer.LoadIdentity();
|
||||||
renderer.TranslateMatrix({ 0,0, -1.0f * Environment::zoom });
|
renderer.PushSpecialMatrix(camera.getViewMatrix());
|
||||||
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());
|
|
||||||
|
|
||||||
|
const Eigen::Matrix4f viewMatrix = renderer.GetCurrentModelViewMatrix();
|
||||||
renderer.RenderUniformMatrix4fv("ModelViewMatrix", false, viewMatrix.data());
|
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);
|
renderer.RenderUniform1f("uDistanceToPlanetSurface", dist);
|
||||||
|
|
||||||
// В начале drawAtmosphere или как uniform
|
Eigen::Vector3f worldLightDir = Eigen::Vector3f(1.0f, -1.0f, -1.0f).normalized();
|
||||||
//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();
|
|
||||||
renderer.RenderUniform3fv("uWorldLightDir", worldLightDir.data());
|
renderer.RenderUniform3fv("uWorldLightDir", worldLightDir.data());
|
||||||
|
|
||||||
// 1. Рассчитываем uPlayerLightFactor (как в Game.cpp)
|
// Свет в view-space: берём матрицу вида (верхний левый 3x3)
|
||||||
Vector3f playerDirWorld = Environment::shipState.position.normalized();
|
Eigen::Matrix3f viewRot = camera.getViewMatrix().block<3, 3>(0, 0);
|
||||||
Vector3f sunDirWorld = Vector3f(1.0f, -1.0f, -1.0f).normalized();
|
Eigen::Vector3f viewLightDir = (viewRot * worldLightDir).normalized();
|
||||||
|
Eigen::Vector3f lightToSource = -viewLightDir;
|
||||||
|
renderer.RenderUniform3fv("uLightDirView", lightToSource.data());
|
||||||
|
|
||||||
// Насколько игрок на свету
|
Eigen::Vector3f playerDirWorld = Environment::shipState.position.normalized();
|
||||||
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();
|
|
||||||
renderer.RenderUniform3fv("uPlayerDirWorld", playerDirWorld.data());
|
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);
|
glEnable(GL_BLEND);
|
||||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE);// Аддитивное смешивание для эффекта свечения
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
|
||||||
|
|
||||||
renderer.DrawVertexRenderStruct(planetAtmosphereRenderStruct);
|
renderer.DrawVertexRenderStruct(planetAtmosphereRenderStruct);
|
||||||
|
|
||||||
glDisable(GL_BLEND);
|
glDisable(GL_BLEND);
|
||||||
glDepthMask(GL_TRUE);
|
glDepthMask(GL_TRUE);
|
||||||
renderer.PopMatrix();
|
|
||||||
|
renderer.PopMatrix(); // special
|
||||||
|
renderer.PopMatrix(); // original
|
||||||
renderer.PopProjectionMatrix();
|
renderer.PopProjectionMatrix();
|
||||||
|
|
||||||
renderer.DisableVertexAttribArray(vNormalName);
|
renderer.DisableVertexAttribArray(vNormalName);
|
||||||
renderer.DisableVertexAttribArray(vPositionName);
|
renderer.DisableVertexAttribArray(vPositionName);
|
||||||
|
|
||||||
renderer.shaderManager.PopShader();
|
renderer.shaderManager.PopShader();
|
||||||
CheckGlError();
|
CheckGlError();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
float PlanetObject::distanceToPlanetSurface(const Vector3f& viewerPosition)
|
float PlanetObject::distanceToPlanetSurface(const Vector3f& viewerPosition)
|
||||||
{
|
{
|
||||||
return planetData.distanceToPlanetSurfaceFast(viewerPosition);
|
return planetData.distanceToPlanetSurfaceFast(viewerPosition);
|
||||||
|
|||||||
@ -18,7 +18,7 @@
|
|||||||
#include "render/FrameBuffer.h"
|
#include "render/FrameBuffer.h"
|
||||||
#include <queue>
|
#include <queue>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include "render/Camera.h"
|
||||||
namespace ZL {
|
namespace ZL {
|
||||||
class TaskManager;
|
class TaskManager;
|
||||||
class MainThreadHandler;
|
class MainThreadHandler;
|
||||||
@ -47,8 +47,9 @@ namespace ZL {
|
|||||||
Renderer& renderer;
|
Renderer& renderer;
|
||||||
TaskManager& taskManager;
|
TaskManager& taskManager;
|
||||||
MainThreadHandler& mainThreadHandler;
|
MainThreadHandler& mainThreadHandler;
|
||||||
|
Camera& camera;
|
||||||
public:
|
public:
|
||||||
PlanetObject(Renderer& iRenderer, TaskManager& iTaskManager, MainThreadHandler& iMainThreadHandler);
|
PlanetObject(Renderer& iRenderer, TaskManager& iTaskManager, MainThreadHandler& iMainThreadHandler, Camera& iCamera);
|
||||||
|
|
||||||
void init();
|
void init();
|
||||||
void update(float deltaTimeMs);
|
void update(float deltaTimeMs);
|
||||||
|
|||||||
@ -110,18 +110,7 @@ namespace ZL {
|
|||||||
v(2) *= scaleFactors(2);
|
v(2) *= scaleFactors(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
// Случайный поворот (например, вокруг трех осей)
|
|
||||||
Vector4f qx = Eigen::Quaternionf(Eigen::AngleAxisf(getRandomFloat(engine, 0.0f, 360.0f), Eigen::Vector3f::UnitX()));//QuatFromRotateAroundX(getRandomFloat(engine, 0.0f, 360.0f));
|
|
||||||
Vector4f qy = Eigen::Quaternionf(Eigen::AngleAxisf(getRandomFloat(engine, 0.0f, 360.0f), Eigen::Vector3f::UnitY()));//QuatFromRotateAroundY(getRandomFloat(engine, 0.0f, 360.0f));
|
|
||||||
Vector4f qz = Eigen::Quaternionf(Eigen::AngleAxisf(getRandomFloat(engine, 0.0f, 360.0f), Eigen::Vector3f::UnitZ()));//QuatFromRotateAroundZ(getRandomFloat(engine, 0.0f, 360.0f));
|
|
||||||
Vector4f qFinal = slerp(qx, qy, 0.5f); // Простой пример комбинирования
|
|
||||||
qFinal = slerp(qFinal, qz, 0.5f).normalized();
|
|
||||||
Matrix3f rotationMatrix = QuatToMatrix(qFinal);
|
|
||||||
|
|
||||||
for (Vector3f& v : initialVertices) {
|
|
||||||
v = MultMatrixVector(rotationMatrix, v);
|
|
||||||
}*/
|
|
||||||
|
|
||||||
// 3. Сглаженные Нормали и Формирование Mesh
|
// 3. Сглаженные Нормали и Формирование Mesh
|
||||||
VertexDataStruct result;
|
VertexDataStruct result;
|
||||||
@ -259,18 +248,7 @@ namespace ZL {
|
|||||||
getRandomFloat(engine, SCALE_MIN, SCALE_MAX)
|
getRandomFloat(engine, SCALE_MIN, SCALE_MAX)
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
|
||||||
if (tIdx == 0)
|
|
||||||
{
|
|
||||||
instance.scale = instance.scale * 0.7f;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
Vector4f qx = QuatFromRotateAroundX(getRandomFloat(engine, 0.0f, 360.0f));
|
|
||||||
Vector4f qy = QuatFromRotateAroundY(getRandomFloat(engine, 0.0f, 360.0f));
|
|
||||||
Vector4f qz = QuatFromRotateAroundZ(getRandomFloat(engine, 0.0f, 360.0f));
|
|
||||||
instance.rotation = slerp(slerp(qx, qy, 0.5f), qz, 0.5f).normalized();
|
|
||||||
*/
|
|
||||||
instance.rotation = Vector4f(0.f, 0.f, 0.f, 1.f);
|
instance.rotation = Vector4f(0.f, 0.f, 0.f, 1.f);
|
||||||
group.allInstances[tIdx].push_back(instance);
|
group.allInstances[tIdx].push_back(instance);
|
||||||
}
|
}
|
||||||
@ -291,9 +269,15 @@ namespace ZL {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper to get the singleton base stone data
|
||||||
|
const VertexDataStruct& GetBaseStone() {
|
||||||
|
static VertexDataStruct baseStone = CreateBaseConvexPolyhedron(1337);
|
||||||
|
return baseStone;
|
||||||
|
}
|
||||||
|
|
||||||
VertexRenderStruct StoneGroup::inflateOne(int index, float scaleModifier)
|
VertexRenderStruct StoneGroup::inflateOne(int index, float scaleModifier)
|
||||||
{
|
{
|
||||||
static VertexDataStruct baseStone = CreateBaseConvexPolyhedron(1337);
|
const VertexDataStruct& baseStone = GetBaseStone();
|
||||||
|
|
||||||
VertexRenderStruct result;
|
VertexRenderStruct result;
|
||||||
|
|
||||||
@ -324,10 +308,11 @@ namespace ZL {
|
|||||||
|
|
||||||
VertexDataStruct StoneGroup::inflateOneDataOnly(int index, float scaleModifier)
|
VertexDataStruct StoneGroup::inflateOneDataOnly(int index, float scaleModifier)
|
||||||
{
|
{
|
||||||
static VertexDataStruct baseStone = CreateBaseConvexPolyhedron(1337);
|
const VertexDataStruct& baseStone = GetBaseStone();
|
||||||
|
|
||||||
VertexDataStruct result;
|
VertexDataStruct result;
|
||||||
|
// Pre-reserve logic? Optional but good.
|
||||||
|
// Since we don't know exact size (depends on stone count), maybe just standard push_back.
|
||||||
|
|
||||||
for (const auto& inst : allInstances[index]) {
|
for (const auto& inst : allInstances[index]) {
|
||||||
Matrix3f rotMat = inst.rotation.toRotationMatrix();
|
Matrix3f rotMat = inst.rotation.toRotationMatrix();
|
||||||
@ -342,7 +327,13 @@ namespace ZL {
|
|||||||
|
|
||||||
result.PositionData.push_back(rotMat * p + inst.position);
|
result.PositionData.push_back(rotMat * p + inst.position);
|
||||||
result.NormalData.push_back(rotMat * n);
|
result.NormalData.push_back(rotMat * n);
|
||||||
|
|
||||||
|
if (j < baseStone.TexCoordData.size()) {
|
||||||
result.TexCoordData.push_back(baseStone.TexCoordData[j]);
|
result.TexCoordData.push_back(baseStone.TexCoordData[j]);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
result.TexCoordData.push_back({ 0.f, 0.f });
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
55
src/render/Camera.cpp
Normal file
55
src/render/Camera.cpp
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
#include "render/Camera.h"
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
namespace ZL {
|
||||||
|
|
||||||
|
using Eigen::Vector3f;
|
||||||
|
using Eigen::Matrix3f;
|
||||||
|
using Eigen::Matrix4f;
|
||||||
|
|
||||||
|
Camera::Camera()
|
||||||
|
: position(Vector3f::Zero())
|
||||||
|
, target(Vector3f::Zero())
|
||||||
|
, rotation(Matrix3f::Identity())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Camera::followOrbit(const Vector3f& targetPos, float yaw, float pitch, float distance, float height)
|
||||||
|
{
|
||||||
|
target = targetPos;
|
||||||
|
|
||||||
|
float cosP = std::cos(pitch);
|
||||||
|
float sinP = std::sin(pitch);
|
||||||
|
float cosY = std::cos(yaw);
|
||||||
|
float sinY = std::sin(yaw);
|
||||||
|
|
||||||
|
Vector3f offset;
|
||||||
|
offset.x() = distance * sinY * cosP;
|
||||||
|
offset.y() = height + distance * sinP;
|
||||||
|
offset.z() = distance * cosY * cosP;
|
||||||
|
|
||||||
|
position = target + offset;
|
||||||
|
|
||||||
|
// 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
|
||||||
|
{
|
||||||
|
Matrix3f R_inv = rotation.transpose();
|
||||||
|
|
||||||
|
Matrix4f view = Matrix4f::Identity();
|
||||||
|
view.block<3, 3>(0, 0) = R_inv;
|
||||||
|
view.block<3, 1>(0, 3) = R_inv * (-position);
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ZL
|
||||||
23
src/render/Camera.h
Normal file
23
src/render/Camera.h
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <Eigen/Dense>
|
||||||
|
|
||||||
|
namespace ZL {
|
||||||
|
|
||||||
|
class Camera {
|
||||||
|
public:
|
||||||
|
Camera();
|
||||||
|
|
||||||
|
// Орбитальная камера вокруг targetPos (yaw вокруг Y, pitch вокруг X)
|
||||||
|
void followOrbit(const Eigen::Vector3f& targetPos, float yaw, float pitch, float distance, float height);
|
||||||
|
|
||||||
|
Eigen::Matrix4f getViewMatrix() const;
|
||||||
|
|
||||||
|
Eigen::Vector3f getPosition() const { return position; }
|
||||||
|
Eigen::Vector3f getTarget() const { return target; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Eigen::Vector3f position;
|
||||||
|
Eigen::Vector3f target;
|
||||||
|
Eigen::Matrix3f rotation; // columns: right, up, -forward
|
||||||
|
};
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user