698 lines
30 KiB
C++
Executable File
698 lines
30 KiB
C++
Executable File
#include "Game.h"
|
||
#include "AnimatedModel.h"
|
||
#include "BoneAnimatedModel.h"
|
||
#include "Utils.h"
|
||
#include "OpenGlExtensions.h"
|
||
#include <iostream>
|
||
#include "TextureManager.h"
|
||
#include "TextModel.h"
|
||
#include <random>
|
||
#include <cmath>
|
||
|
||
namespace ZL
|
||
{
|
||
#ifdef EMSCRIPTEN
|
||
const char* CONST_ZIP_FILE = "space-game001.zip";
|
||
#else
|
||
const char* CONST_ZIP_FILE = "";
|
||
#endif
|
||
|
||
Matrix3f rotateShipMat = Matrix3f::Identity();
|
||
|
||
Vector4f generateRandomQuaternion(std::mt19937& gen)
|
||
{
|
||
// Распределение для генерации случайных координат кватерниона
|
||
std::normal_distribution<> distrib(0.0, 1.0);
|
||
|
||
// Генерируем четыре случайных числа из нормального распределения N(0, 1).
|
||
// Нормализация этого вектора дает равномерное распределение по 4D-сфере (т.е. кватернион единичной длины).
|
||
Vector4f randomQuat = {
|
||
(float)distrib(gen),
|
||
(float)distrib(gen),
|
||
(float)distrib(gen),
|
||
(float)distrib(gen)
|
||
};
|
||
|
||
return randomQuat.normalized();
|
||
}
|
||
|
||
|
||
// --- Основная функция генерации ---
|
||
std::vector<BoxCoords> generateRandomBoxCoords(int N)
|
||
{
|
||
// Константы
|
||
const float MIN_DISTANCE = 3.0f;
|
||
const float MIN_DISTANCE_SQUARED = MIN_DISTANCE * MIN_DISTANCE; // Работаем с квадратом расстояния
|
||
const float MIN_COORD = -100.0f;
|
||
const float MAX_COORD = 100.0f;
|
||
const int MAX_ATTEMPTS = 1000; // Ограничение на количество попыток, чтобы избежать бесконечного цикла
|
||
|
||
std::vector<BoxCoords> boxCoordsArr;
|
||
boxCoordsArr.reserve(N); // Резервируем память
|
||
|
||
// 1. Инициализация генератора псевдослучайных чисел
|
||
// Используем Mersenne Twister (mt19937) как высококачественный генератор
|
||
std::random_device rd;
|
||
std::mt19937 gen(rd());
|
||
|
||
// 2. Определение равномерного распределения для координат [MIN_COORD, MAX_COORD]
|
||
std::uniform_real_distribution<> distrib(MIN_COORD, MAX_COORD);
|
||
|
||
int generatedCount = 0;
|
||
|
||
while (generatedCount < N)
|
||
{
|
||
bool accepted = false;
|
||
int attempts = 0;
|
||
|
||
// Попытка найти подходящие координаты
|
||
while (!accepted && attempts < MAX_ATTEMPTS)
|
||
{
|
||
// Генерируем новые случайные координаты
|
||
Vector3f newPos(
|
||
(float)distrib(gen),
|
||
(float)distrib(gen),
|
||
(float)distrib(gen)
|
||
);
|
||
|
||
// Проверка расстояния до всех уже существующих объектов
|
||
accepted = true; // Предполагаем, что подходит, пока не доказано обратное
|
||
for (const auto& existingBox : boxCoordsArr)
|
||
{
|
||
// Расчет вектора разности
|
||
Vector3f diff = newPos - existingBox.pos;
|
||
|
||
// Расчет квадрата расстояния
|
||
float distanceSquared = diff.squaredNorm();
|
||
|
||
// Если квадрат расстояния меньше квадрата минимального расстояния
|
||
if (distanceSquared < MIN_DISTANCE_SQUARED)
|
||
{
|
||
accepted = false; // Отклоняем, слишком близко
|
||
break; // Нет смысла проверять дальше, если одно нарушение найдено
|
||
}
|
||
}
|
||
|
||
if (accepted)
|
||
{
|
||
// 2. Генерируем случайный кватернион
|
||
Vector4f randomQuat = generateRandomQuaternion(gen);
|
||
|
||
// 3. Преобразуем его в матрицу вращения
|
||
Matrix3f randomMatrix = QuatToMatrix(randomQuat);
|
||
|
||
// 4. Добавляем объект с новой случайной матрицей
|
||
boxCoordsArr.emplace_back(BoxCoords{ newPos, randomMatrix });
|
||
generatedCount++;
|
||
}
|
||
attempts++;
|
||
}
|
||
|
||
// Если превышено максимальное количество попыток, выходим из цикла,
|
||
// чтобы избежать зависания, если N слишком велико или диапазон слишком мал.
|
||
if (!accepted) {
|
||
std::cerr << "Предупреждение: Не удалось сгенерировать " << N << " объектов. Сгенерировано: " << generatedCount << std::endl;
|
||
break;
|
||
}
|
||
}
|
||
|
||
return boxCoordsArr;
|
||
}
|
||
|
||
Game::Game()
|
||
: window(nullptr)
|
||
, glContext(nullptr)
|
||
, newTickCount(0)
|
||
, lastTickCount(0)
|
||
{
|
||
}
|
||
|
||
Game::~Game() {
|
||
if (glContext) {
|
||
SDL_GL_DeleteContext(glContext);
|
||
}
|
||
if (window) {
|
||
SDL_DestroyWindow(window);
|
||
}
|
||
SDL_Quit();
|
||
}
|
||
|
||
void Game::setup() {
|
||
glContext = SDL_GL_CreateContext(ZL::Environment::window);
|
||
|
||
ZL::BindOpenGlFunctions();
|
||
ZL::CheckGlError();
|
||
|
||
// Initialize renderer
|
||
|
||
#ifdef EMSCRIPTEN
|
||
renderer.shaderManager.AddShaderFromFiles("default", "./shaders/default.vertex", "./shaders/default_web.fragment", CONST_ZIP_FILE);
|
||
renderer.shaderManager.AddShaderFromFiles("defaultColor", "./shaders/defaultColor.vertex", "./shaders/defaultColor_web.fragment", CONST_ZIP_FILE);
|
||
renderer.shaderManager.AddShaderFromFiles("env", "./shaders/env.vertex", "./shaders/env_web.fragment", CONST_ZIP_FILE);
|
||
|
||
#else
|
||
renderer.shaderManager.AddShaderFromFiles("default", "./shaders/default.vertex", "./shaders/default_desktop.fragment", CONST_ZIP_FILE);
|
||
renderer.shaderManager.AddShaderFromFiles("defaultColor", "./shaders/defaultColor.vertex", "./shaders/defaultColor_desktop.fragment", CONST_ZIP_FILE);
|
||
renderer.shaderManager.AddShaderFromFiles("env", "./shaders/env.vertex", "./shaders/env_desktop.fragment", CONST_ZIP_FILE);
|
||
|
||
#endif
|
||
|
||
cubemapTexture = std::make_shared<Texture>(
|
||
std::array<TextureDataStruct, 6>{
|
||
CreateTextureDataFromBmp24("./resources/sky/space_rt.bmp", CONST_ZIP_FILE),
|
||
CreateTextureDataFromBmp24("./resources/sky/space_lf.bmp", CONST_ZIP_FILE),
|
||
CreateTextureDataFromBmp24("./resources/sky/space_up.bmp", CONST_ZIP_FILE),
|
||
CreateTextureDataFromBmp24("./resources/sky/space_dn.bmp", CONST_ZIP_FILE),
|
||
CreateTextureDataFromBmp24("./resources/sky/space_bk.bmp", CONST_ZIP_FILE),
|
||
CreateTextureDataFromBmp24("./resources/sky/space_ft.bmp", CONST_ZIP_FILE)
|
||
});
|
||
|
||
|
||
cubemap.data = ZL::CreateCubemap(500);
|
||
cubemap.RefreshVBO();
|
||
|
||
//Load texture
|
||
spaceshipTexture = std::make_unique<Texture>(CreateTextureDataFromPng("./resources/DefaultMaterial_BaseColor.png", CONST_ZIP_FILE));
|
||
spaceshipBase = LoadFromTextFile02("./resources/spaceship005.txt", CONST_ZIP_FILE);
|
||
spaceshipBase.RotateByMatrix(QuatToMatrix(QuatFromRotateAroundY(M_PI / 2.0)));
|
||
spaceshipBase.Move(Vector3f{ -0.52998, -10, 10 });
|
||
spaceshipBase.RotateByMatrix(QuatToMatrix(QuatFromRotateAroundY(M_PI / 2.0)));
|
||
|
||
spaceship.AssignFrom(spaceshipBase);
|
||
spaceship.RefreshVBO();
|
||
|
||
//Boxes
|
||
boxTexture = std::make_unique<Texture>(CreateTextureDataFromPng("./resources/box/box.png", CONST_ZIP_FILE));
|
||
boxBase = LoadFromTextFile02("./resources/box/box.txt", CONST_ZIP_FILE);
|
||
|
||
boxCoordsArr = generateRandomBoxCoords(50);
|
||
|
||
boxRenderArr.resize(boxCoordsArr.size());
|
||
|
||
for (int i = 0; i < boxCoordsArr.size(); i++)
|
||
{
|
||
boxRenderArr[i].AssignFrom(boxBase);
|
||
boxRenderArr[i].RefreshVBO();
|
||
}
|
||
|
||
|
||
buttonTexture = std::make_unique<Texture>(CreateTextureDataFromPng("./resources/button.png", CONST_ZIP_FILE));
|
||
|
||
button.data.PositionData.push_back({ 100, 100, 0 });
|
||
button.data.PositionData.push_back({ 100, 150, 0 });
|
||
button.data.PositionData.push_back({ 300, 150, 0 });
|
||
button.data.PositionData.push_back({ 100, 100, 0 });
|
||
button.data.PositionData.push_back({ 300, 150, 0 });
|
||
button.data.PositionData.push_back({ 300, 100, 0 });
|
||
|
||
button.data.TexCoordData.push_back({ 0,0 });
|
||
button.data.TexCoordData.push_back({ 0,1 });
|
||
button.data.TexCoordData.push_back({ 1,1 });
|
||
button.data.TexCoordData.push_back({ 0,0 });
|
||
button.data.TexCoordData.push_back({ 1,1 });
|
||
button.data.TexCoordData.push_back({ 1,0 });
|
||
|
||
button.RefreshVBO();
|
||
|
||
musicVolumeBarTexture = std::make_unique<Texture>(CreateTextureDataFromPng("./resources/musicVolumeBarTexture.png", CONST_ZIP_FILE));
|
||
|
||
musicVolumeBar.data.PositionData.push_back({ 1190, 100, 0 });
|
||
musicVolumeBar.data.PositionData.push_back({ 1190, 600, 0 });
|
||
musicVolumeBar.data.PositionData.push_back({ 1200, 600, 0 });
|
||
musicVolumeBar.data.PositionData.push_back({ 1190, 100, 0 });
|
||
musicVolumeBar.data.PositionData.push_back({ 1200, 600, 0 });
|
||
musicVolumeBar.data.PositionData.push_back({ 1200, 100, 0 });
|
||
|
||
musicVolumeBar.data.TexCoordData.push_back({ 0,0 });
|
||
musicVolumeBar.data.TexCoordData.push_back({ 0,1 });
|
||
musicVolumeBar.data.TexCoordData.push_back({ 1,1 });
|
||
musicVolumeBar.data.TexCoordData.push_back({ 0,0 });
|
||
musicVolumeBar.data.TexCoordData.push_back({ 1,1 });
|
||
musicVolumeBar.data.TexCoordData.push_back({ 1,0 });
|
||
|
||
musicVolumeBar.RefreshVBO();
|
||
|
||
|
||
musicVolumeBarButtonTexture = std::make_unique<Texture>(CreateTextureDataFromPng("./resources/musicVolumeBarButton.png", CONST_ZIP_FILE));
|
||
|
||
float musicVolumeBarButtonButtonCenterY = 350.0f;
|
||
|
||
musicVolumeBarButton.data.PositionData.push_back({ musicVolumeBarButtonButtonCenterX - musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY - musicVolumeBarButtonButtonRadius, 0 });
|
||
musicVolumeBarButton.data.PositionData.push_back({ musicVolumeBarButtonButtonCenterX - musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY + musicVolumeBarButtonButtonRadius, 0 });
|
||
musicVolumeBarButton.data.PositionData.push_back({ musicVolumeBarButtonButtonCenterX + musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY + musicVolumeBarButtonButtonRadius, 0 });
|
||
musicVolumeBarButton.data.PositionData.push_back({ musicVolumeBarButtonButtonCenterX - musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY - musicVolumeBarButtonButtonRadius, 0 });
|
||
musicVolumeBarButton.data.PositionData.push_back({ musicVolumeBarButtonButtonCenterX + musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY + musicVolumeBarButtonButtonRadius, 0 });
|
||
musicVolumeBarButton.data.PositionData.push_back({ musicVolumeBarButtonButtonCenterX + musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY - musicVolumeBarButtonButtonRadius, 0 });
|
||
|
||
musicVolumeBarButton.data.TexCoordData.push_back({ 0,0 });
|
||
musicVolumeBarButton.data.TexCoordData.push_back({ 0,1 });
|
||
musicVolumeBarButton.data.TexCoordData.push_back({ 1,1 });
|
||
musicVolumeBarButton.data.TexCoordData.push_back({ 0,0 });
|
||
musicVolumeBarButton.data.TexCoordData.push_back({ 1,1 });
|
||
musicVolumeBarButton.data.TexCoordData.push_back({ 1,0 });
|
||
|
||
musicVolumeBarButton.RefreshVBO();
|
||
renderer.InitOpenGL();
|
||
glEnable(GL_BLEND);
|
||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||
|
||
}
|
||
|
||
void Game::drawCubemap()
|
||
{
|
||
static const std::string defaultShaderName = "default";
|
||
static const std::string envShaderName = "env";
|
||
static const std::string vPositionName = "vPosition";
|
||
static const std::string vTexCoordName = "vTexCoord";
|
||
static const std::string textureUniformName = "Texture";
|
||
|
||
renderer.shaderManager.PushShader(envShaderName);
|
||
renderer.RenderUniform1i(textureUniformName, 0);
|
||
renderer.EnableVertexAttribArray(vPositionName);
|
||
renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5,
|
||
static_cast<float>(Environment::width) / static_cast<float>(Environment::height),
|
||
1, 1000);
|
||
renderer.PushMatrix();
|
||
renderer.LoadIdentity();
|
||
renderer.RotateMatrix(Environment::inverseShipMatrix);
|
||
|
||
CheckGlError();
|
||
|
||
glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexture->getTexID());
|
||
renderer.DrawVertexRenderStruct(cubemap);
|
||
|
||
CheckGlError();
|
||
|
||
|
||
renderer.PopMatrix();
|
||
renderer.PopProjectionMatrix();
|
||
renderer.DisableVertexAttribArray(vPositionName);
|
||
|
||
renderer.shaderManager.PopShader();
|
||
CheckGlError();
|
||
}
|
||
|
||
void Game::drawShip()
|
||
{
|
||
static const std::string defaultShaderName = "default";
|
||
static const std::string envShaderName = "env";
|
||
static const std::string vPositionName = "vPosition";
|
||
static const std::string vTexCoordName = "vTexCoord";
|
||
static const std::string textureUniformName = "Texture";
|
||
|
||
renderer.shaderManager.PushShader(defaultShaderName);
|
||
renderer.RenderUniform1i(textureUniformName, 0);
|
||
renderer.EnableVertexAttribArray(vPositionName);
|
||
renderer.EnableVertexAttribArray(vTexCoordName);
|
||
|
||
renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5,
|
||
static_cast<float>(Environment::width) / static_cast<float>(Environment::height),
|
||
1, 1000);
|
||
renderer.PushMatrix();
|
||
|
||
renderer.LoadIdentity();
|
||
renderer.TranslateMatrix({ 0,0, -1.0f * Environment::zoom });
|
||
|
||
// Используем зафиксированную камеру для отрисовки, если lock активен
|
||
// Это обеспечит правильную ориентацию модели относительно начальной позиции камеры
|
||
if (shipMoveLockActive) {
|
||
///Matrix3f inverseLockedCamera = InverseMatrix(lockedCameraMat);
|
||
renderer.RotateMatrix(lockedCameraMat);
|
||
} else {
|
||
renderer.RotateMatrix(Environment::inverseShipMatrix);
|
||
}
|
||
|
||
renderer.RotateMatrix(rotateShipMat);
|
||
|
||
// Компенсируем начальный поворот модели на 90° вокруг Y, чтобы модель смотрела в направлении движения
|
||
// Модель изначально повёрнута на M_PI / 2.0 вокруг Y, поэтому добавляем компенсацию
|
||
Vector4f correctionQuat = QuatFromRotateAroundY(M_PI / 2.0f);
|
||
renderer.RotateMatrix(correctionQuat);
|
||
|
||
|
||
glBindTexture(GL_TEXTURE_2D, spaceshipTexture->getTexID());
|
||
renderer.DrawVertexRenderStruct(spaceship);
|
||
|
||
renderer.PopMatrix();
|
||
renderer.PopProjectionMatrix();
|
||
renderer.DisableVertexAttribArray(vPositionName);
|
||
renderer.DisableVertexAttribArray(vTexCoordName);
|
||
|
||
renderer.shaderManager.PopShader();
|
||
CheckGlError();
|
||
}
|
||
|
||
void Game::drawBoxes()
|
||
{
|
||
static const std::string defaultShaderName = "default";
|
||
static const std::string envShaderName = "env";
|
||
static const std::string vPositionName = "vPosition";
|
||
static const std::string vTexCoordName = "vTexCoord";
|
||
static const std::string textureUniformName = "Texture";
|
||
|
||
renderer.shaderManager.PushShader(defaultShaderName);
|
||
renderer.RenderUniform1i(textureUniformName, 0);
|
||
renderer.EnableVertexAttribArray(vPositionName);
|
||
renderer.EnableVertexAttribArray(vTexCoordName);
|
||
|
||
renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5,
|
||
static_cast<float>(Environment::width) / static_cast<float>(Environment::height),
|
||
1, 1000);
|
||
|
||
for (int i = 0; i < boxCoordsArr.size(); i++)
|
||
{
|
||
renderer.PushMatrix();
|
||
|
||
renderer.LoadIdentity();
|
||
renderer.TranslateMatrix({ 0,0, -1.0f * Environment::zoom });
|
||
renderer.RotateMatrix(Environment::inverseShipMatrix);
|
||
renderer.TranslateMatrix(-Environment::shipPosition);
|
||
renderer.TranslateMatrix(boxCoordsArr[i].pos);
|
||
renderer.RotateMatrix(boxCoordsArr[i].m);
|
||
|
||
glBindTexture(GL_TEXTURE_2D, boxTexture->getTexID());
|
||
renderer.DrawVertexRenderStruct(boxRenderArr[i]);
|
||
|
||
renderer.PopMatrix();
|
||
}
|
||
renderer.PopProjectionMatrix();
|
||
renderer.DisableVertexAttribArray(vPositionName);
|
||
renderer.DisableVertexAttribArray(vTexCoordName);
|
||
|
||
renderer.shaderManager.PopShader();
|
||
CheckGlError();
|
||
}
|
||
|
||
void Game::UpdateVolumeKnob() {
|
||
float musicVolumeBarButtonButtonCenterY = volumeBarMinY + musicVolume * (volumeBarMaxY - volumeBarMinY);
|
||
|
||
auto& pos = musicVolumeBarButton.data.PositionData;
|
||
|
||
pos[0] = { musicVolumeBarButtonButtonCenterX - musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY - musicVolumeBarButtonButtonRadius, 0 };
|
||
pos[1] = { musicVolumeBarButtonButtonCenterX - musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY + musicVolumeBarButtonButtonRadius, 0 };
|
||
pos[2] = { musicVolumeBarButtonButtonCenterX + musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY + musicVolumeBarButtonButtonRadius, 0 };
|
||
pos[3] = { musicVolumeBarButtonButtonCenterX - musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY - musicVolumeBarButtonButtonRadius, 0 };
|
||
pos[4] = { musicVolumeBarButtonButtonCenterX + musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY + musicVolumeBarButtonButtonRadius, 0 };
|
||
pos[5] = { musicVolumeBarButtonButtonCenterX + musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY - musicVolumeBarButtonButtonRadius, 0 };
|
||
|
||
musicVolumeBarButton.RefreshVBO();
|
||
|
||
}
|
||
|
||
void Game::UpdateVolumeFromMouse(int mouseX, int mouseY) {
|
||
|
||
int uiX = mouseX;
|
||
int uiY = Environment::height - mouseY;
|
||
Environment::shipVelocity = (musicVolume - 0.2) * (20.0);
|
||
if (uiY < volumeBarMinY || uiY > volumeBarMaxY)
|
||
{
|
||
return;
|
||
}
|
||
|
||
float t = (uiY - volumeBarMinY) / (volumeBarMaxY - volumeBarMinY);
|
||
if (t < 0.0f) t = 0.0f;
|
||
if (t > 1.0f) t = 1.0f;
|
||
musicVolume = t;
|
||
UpdateVolumeKnob();
|
||
}
|
||
void Game::drawUI()
|
||
{
|
||
static const std::string defaultShaderName = "default";
|
||
static const std::string envShaderName = "env";
|
||
static const std::string vPositionName = "vPosition";
|
||
static const std::string vTexCoordName = "vTexCoord";
|
||
static const std::string textureUniformName = "Texture";
|
||
|
||
|
||
glClear(GL_DEPTH_BUFFER_BIT);
|
||
|
||
renderer.shaderManager.PushShader(defaultShaderName);
|
||
renderer.RenderUniform1i(textureUniformName, 0);
|
||
renderer.EnableVertexAttribArray(vPositionName);
|
||
renderer.EnableVertexAttribArray(vTexCoordName);
|
||
|
||
renderer.PushProjectionMatrix(Environment::width, Environment::height, -1, 1);
|
||
renderer.PushMatrix();
|
||
|
||
renderer.LoadIdentity();
|
||
|
||
|
||
glBindTexture(GL_TEXTURE_2D, buttonTexture->getTexID());
|
||
renderer.DrawVertexRenderStruct(button);
|
||
|
||
glBindTexture(GL_TEXTURE_2D, musicVolumeBarTexture->getTexID());
|
||
renderer.DrawVertexRenderStruct(musicVolumeBar);
|
||
|
||
glBindTexture(GL_TEXTURE_2D, musicVolumeBarButtonTexture->getTexID());
|
||
renderer.DrawVertexRenderStruct(musicVolumeBarButton);
|
||
|
||
renderer.PopMatrix();
|
||
renderer.PopProjectionMatrix();
|
||
renderer.DisableVertexAttribArray(vPositionName);
|
||
renderer.DisableVertexAttribArray(vTexCoordName);
|
||
|
||
renderer.shaderManager.PopShader();
|
||
CheckGlError();
|
||
}
|
||
|
||
void Game::drawScene() {
|
||
static const std::string defaultShaderName = "default";
|
||
static const std::string envShaderName = "env";
|
||
static const std::string vPositionName = "vPosition";
|
||
static const std::string vTexCoordName = "vTexCoord";
|
||
static const std::string textureUniformName = "Texture";
|
||
|
||
glClearColor(0.0f, 0.5f, 1.0f, 1.0f);
|
||
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
|
||
|
||
glViewport(0, 0, Environment::width, Environment::height);
|
||
|
||
CheckGlError();
|
||
|
||
drawCubemap();
|
||
drawShip();
|
||
drawBoxes();
|
||
|
||
//drawUI();
|
||
|
||
CheckGlError();
|
||
}
|
||
|
||
void Game::processTickCount() {
|
||
|
||
if (lastTickCount == 0) {
|
||
lastTickCount = SDL_GetTicks64();
|
||
return;
|
||
}
|
||
|
||
newTickCount = SDL_GetTicks64();
|
||
if (newTickCount - lastTickCount > CONST_TIMER_INTERVAL) {
|
||
size_t delta = (newTickCount - lastTickCount > CONST_MAX_TIME_INTERVAL) ?
|
||
CONST_MAX_TIME_INTERVAL : newTickCount - lastTickCount;
|
||
|
||
//gameObjects.updateScene(delta);
|
||
|
||
if (Environment::tapDownHold) {
|
||
|
||
float diffx = Environment::tapDownCurrentPos.v[0] - Environment::tapDownStartPos.v[0];
|
||
float diffy = Environment::tapDownCurrentPos.v[1] - Environment::tapDownStartPos.v[1];
|
||
|
||
if (isDraggingShip) {
|
||
// Движение корабля
|
||
if (abs(diffy) > 5.0 || abs(diffx) > 5.0) //threshold
|
||
{
|
||
float velocity = sqrtf(diffx * diffx + diffy * diffy);
|
||
|
||
Environment::shipVelocity = velocity * delta / 100;
|
||
|
||
// SDL screen Y направлен вниз, поэтому инвертируем diffy
|
||
// Движение по -Z (вперёд в локальных координатах камеры)
|
||
// diffx: вправо = положительное, влево = отрицательное
|
||
// diffy: вниз = положительное, вверх = отрицательное (в SDL)
|
||
// Для логики: вверх на экране (отрицательный diffy) = движение вперёд (-Z)
|
||
// Вправо на экране (положительный diffx) = поворот вправо (вокруг +Y)
|
||
|
||
// Вычисляем угол поворота вокруг Y (горизонтальный поворот)
|
||
// atan2(x, z) где x - это -diffx (инвертированное для правильного направления), z - это -diffy (вверх/вниз, инвертированное)
|
||
// Вверх (diffy < 0) должно давать угол 0 (вперёд), вправо (diffx > 0) должно давать правильное направление
|
||
float angleY = atan2(-diffx, -diffy); // угол поворота вокруг Y (инвертируем diffx для правильного направления)
|
||
|
||
// Создаём кватернион для поворота вокруг Y (горизонтальный поворот)
|
||
Vector4f quat = { 0, sin(angleY * 0.5f), 0, cos(angleY * 0.5f) };
|
||
rotateShipMat = QuatToMatrix(quat);
|
||
|
||
// Фиксируем ориентацию камеры при первом реальном движении
|
||
if (!shipMoveLockActive) {
|
||
lockedCameraMat = Environment::shipMatrix;
|
||
shipMoveLockActive = true;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
Environment::shipVelocity = 0;
|
||
}
|
||
}
|
||
else if (isDraggingCamera) {
|
||
// Вращение камеры
|
||
if (abs(diffy) > 5.0 || abs(diffx) > 5.0) //threshold
|
||
{
|
||
float rotationPower = sqrtf(diffx * diffx + diffy * diffy);
|
||
|
||
float deltaAlpha = rotationPower * delta * M_PI / 500000.f;
|
||
|
||
Vector3f rotationDirection = { diffy, diffx, 0 };
|
||
|
||
rotationDirection = rotationDirection.normalized();
|
||
|
||
Vector4f rotateQuat = {
|
||
rotationDirection.v[0] * sin(deltaAlpha * 0.5f),
|
||
rotationDirection.v[1] * sin(deltaAlpha * 0.5f),
|
||
rotationDirection.v[2] * sin(deltaAlpha * 0.5f),
|
||
cos(deltaAlpha * 0.5f) };
|
||
|
||
Matrix3f rotateMat = QuatToMatrix(rotateQuat);
|
||
|
||
Environment::shipMatrix = MultMatrixMatrix(Environment::shipMatrix, rotateMat);
|
||
Environment::inverseShipMatrix = InverseMatrix(Environment::shipMatrix);
|
||
|
||
// Обновляем начальную позицию для плавного вращения
|
||
Environment::tapDownStartPos.v[0] = Environment::tapDownCurrentPos.v[0];
|
||
Environment::tapDownStartPos.v[1] = Environment::tapDownCurrentPos.v[1];
|
||
}
|
||
}
|
||
}
|
||
|
||
if (fabs(Environment::shipVelocity) > 0.01f)
|
||
{
|
||
// Получаем локальное перемещение в координатах камеры
|
||
Vector3f localMove = MultMatrixVector(rotateShipMat, Vector3f{0, 0, -Environment::shipVelocity * delta / 1000.f});
|
||
|
||
// Выбираем матрицу камеры: зафиксированную (если lock активен) или текущую
|
||
Matrix3f camMat = lockedCameraMat;//////shipMoveLockActive ? lockedCameraMat : Environment::shipMatrix;
|
||
|
||
// Переводим локальное перемещение в мировые координаты
|
||
Vector3f worldMove = MultMatrixVector(camMat, localMove);
|
||
|
||
Environment::shipPosition = Environment::shipPosition + worldMove;
|
||
}
|
||
|
||
lastTickCount = newTickCount;
|
||
}
|
||
}
|
||
|
||
void Game::render() {
|
||
SDL_GL_MakeCurrent(ZL::Environment::window, glContext);
|
||
ZL::CheckGlError();
|
||
|
||
glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
|
||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||
|
||
drawScene();
|
||
processTickCount();
|
||
|
||
SDL_GL_SwapWindow(ZL::Environment::window);
|
||
}
|
||
void Game::update() {
|
||
SDL_Event event;
|
||
while (SDL_PollEvent(&event)) {
|
||
if (event.type == SDL_QUIT) {
|
||
Environment::exitGameLoop = true;
|
||
|
||
}
|
||
else if (event.type == SDL_MOUSEBUTTONDOWN) {
|
||
// 1. Обработка нажатия кнопки мыши
|
||
|
||
int mx = event.button.x;
|
||
int my = event.button.y;
|
||
|
||
std::cout << mx << " " << my << '\n';
|
||
int uiX = mx;
|
||
int uiY = Environment::height - my;
|
||
|
||
if (mx < 400 && my > 400)
|
||
{
|
||
Environment::tapDownHold = true;
|
||
isDraggingShip = true;
|
||
isDraggingCamera = false;
|
||
// Сбрасываем lock при новом нажатии, чтобы новый drag снова фиксировал камеру при первом движении
|
||
shipMoveLockActive = false;
|
||
// Координаты начального нажатия
|
||
Environment::tapDownStartPos.v[0] = event.button.x;
|
||
Environment::tapDownStartPos.v[1] = event.button.y;
|
||
// Начальная позиция также становится текущей
|
||
Environment::tapDownCurrentPos.v[0] = event.button.x;
|
||
Environment::tapDownCurrentPos.v[1] = event.button.y;
|
||
}
|
||
else
|
||
{
|
||
Environment::tapDownHold = true;
|
||
isDraggingShip = false;
|
||
isDraggingCamera = true;
|
||
// Координаты начального нажатия
|
||
Environment::tapDownStartPos.v[0] = event.button.x;
|
||
Environment::tapDownStartPos.v[1] = event.button.y;
|
||
// Начальная позиция также становится текущей
|
||
Environment::tapDownCurrentPos.v[0] = event.button.x;
|
||
Environment::tapDownCurrentPos.v[1] = event.button.y;
|
||
}
|
||
|
||
|
||
}
|
||
else if (event.type == SDL_MOUSEBUTTONUP) {
|
||
// 2. Обработка отпускания кнопки мыши
|
||
isDraggingVolume = false;
|
||
Environment::tapDownHold = false;
|
||
Environment::shipVelocity = 0;
|
||
|
||
// Сбрасываем lock камеры при отпускании мыши
|
||
shipMoveLockActive = false;
|
||
|
||
// Если была повернута камера, синхронизируем ориентацию корабля с камерой
|
||
// Корабль должен смотреть в том же направлении, что и камера (без дополнительного вращения)
|
||
if (isDraggingCamera) {
|
||
rotateShipMat = Matrix3f::Identity();
|
||
}
|
||
|
||
isDraggingShip = false;
|
||
isDraggingCamera = false;
|
||
}
|
||
else if (event.type == SDL_MOUSEMOTION) {
|
||
// 3. Обработка перемещения мыши
|
||
int mx = event.motion.x;
|
||
int my = event.motion.y;
|
||
|
||
if (Environment::tapDownHold) {
|
||
// Обновление текущей позиции, если кнопка удерживается
|
||
Environment::tapDownCurrentPos.v[0] = event.motion.x;
|
||
Environment::tapDownCurrentPos.v[1] = event.motion.y;
|
||
}
|
||
}
|
||
else if (event.type == SDL_MOUSEWHEEL) {
|
||
static const float zoomstep = 2.0f;
|
||
if (event.wheel.y > 0) {
|
||
Environment::zoom -= zoomstep;
|
||
}
|
||
else if (event.wheel.y < 0) {
|
||
Environment::zoom += zoomstep;
|
||
}
|
||
if (Environment::zoom < zoomstep) {
|
||
Environment::zoom = zoomstep;
|
||
}
|
||
}
|
||
else if (event.type == SDL_KEYUP)
|
||
{
|
||
|
||
if (event.key.keysym.sym == SDLK_i)
|
||
{
|
||
Environment::shipVelocity += 1.f;
|
||
}
|
||
if (event.key.keysym.sym == SDLK_k)
|
||
{
|
||
Environment::shipVelocity -= 1.f;
|
||
}
|
||
}
|
||
}
|
||
render();
|
||
}
|
||
|
||
} // namespace ZL
|