832 lines
32 KiB
C++
Executable File
832 lines
32 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
|
||
|
||
// Вспомогательная функция для получения случайного числа в диапазоне [min, max]
|
||
float getRandomFloat(std::mt19937& gen, float min, float max) {
|
||
std::uniform_real_distribution<> distrib(min, max);
|
||
return static_cast<float>(distrib(gen));
|
||
}
|
||
|
||
// Икосаэдр (на основе золотого сечения phi)
|
||
// Координаты могут быть вычислены заранее для константного икосаэдра.
|
||
// Здесь только объявление, чтобы показать идею.
|
||
|
||
VertexDataStruct CreateConvexPolyhedron(uint64_t seed) {
|
||
|
||
// --- КОНСТАНТЫ ПАРАМЕТРОВ (как вы просили) ---
|
||
const float BASE_SCALE = 3.0f; // Общий размер камня
|
||
const float MIN_AXIS_SCALE = 0.5f; // Минимальное растяжение/сжатие по оси
|
||
const float MAX_AXIS_SCALE = 1.5f; // Максимальное растяжение/сжатие по оси
|
||
const float MIN_PERTURBATION = 0.05f; // Минимальное радиальное возмущение вершины
|
||
const float MAX_PERTURBATION = 0.25f; // Максимальное радиальное возмущение вершины
|
||
// const size_t SUBDIVISION_LEVEL = 1; // Уровень подразделения (для более круглого камня, пока опустим)
|
||
|
||
std::mt19937 engine(static_cast<unsigned int>(seed));
|
||
|
||
// Золотое сечение
|
||
const float t = (1.0f + std::sqrt(5.0f)) / 2.0f;
|
||
|
||
// 12 вершин икосаэдра
|
||
std::vector<Vector3f> initialVertices = {
|
||
{ -1, t, 0 }, { 1, t, 0 }, { -1, -t, 0 }, { 1, -t, 0 },
|
||
{ 0, -1, t }, { 0, 1, t }, { 0, -1, -t }, { 0, 1, -t },
|
||
{ t, 0, -1 }, { t, 0, 1 }, { -t, 0, -1 }, { -t, 0, 1 }
|
||
};
|
||
|
||
// 20 треугольных граней (индексы вершин)
|
||
std::vector<std::array<int, 3>> faces = {
|
||
// 5 треугольников вокруг вершины 0
|
||
{0, 11, 5}, {0, 5, 1}, {0, 1, 7}, {0, 7, 10}, {0, 10, 11},
|
||
// 5 смежных полос
|
||
{1, 5, 9}, {5, 11, 4}, {11, 10, 2}, {10, 7, 6}, {7, 1, 8},
|
||
// 5 треугольников вокруг вершины 3
|
||
{3, 9, 4}, {3, 4, 2}, {3, 2, 6}, {3, 6, 8}, {3, 8, 9},
|
||
// 5 смежных полос
|
||
{4, 9, 5}, {2, 4, 11}, {6, 2, 10}, {8, 6, 7}, {9, 8, 1}
|
||
};
|
||
|
||
// 1. Нормализация и Возмущение (Perturbation)
|
||
for (Vector3f& v : initialVertices) {
|
||
v = v.normalized() * BASE_SCALE; // Нормализация к сфере радиуса BASE_SCALE
|
||
|
||
// Радиальное возмущение:
|
||
float perturbation = getRandomFloat(engine, MIN_PERTURBATION, MAX_PERTURBATION);
|
||
v = v * (1.0f + perturbation);
|
||
}
|
||
|
||
// 2. Трансформация (Масштабирование и Поворот)
|
||
|
||
// Случайные масштабы по осям
|
||
Vector3f scaleFactors = {
|
||
getRandomFloat(engine, MIN_AXIS_SCALE, MAX_AXIS_SCALE),
|
||
getRandomFloat(engine, MIN_AXIS_SCALE, MAX_AXIS_SCALE),
|
||
getRandomFloat(engine, MIN_AXIS_SCALE, MAX_AXIS_SCALE)
|
||
};
|
||
|
||
// Применяем масштабирование
|
||
for (Vector3f& v : initialVertices) {
|
||
v.v[0] *= scaleFactors.v[0];
|
||
v.v[1] *= scaleFactors.v[1];
|
||
v.v[2] *= scaleFactors.v[2];
|
||
}
|
||
|
||
// Случайный поворот (например, вокруг трех осей)
|
||
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));
|
||
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
|
||
VertexDataStruct result;
|
||
// Карта для накопления нормалей по уникальным позициям вершин
|
||
// (Требует определенного оператора < для Vector3f в ZLMath.h, который у вас есть)
|
||
std::map<Vector3f, Vector3f> smoothNormalsMap;
|
||
|
||
// Предварительное заполнение карты нормалями
|
||
for (const auto& face : faces) {
|
||
Vector3f p1 = initialVertices[face[0]];
|
||
Vector3f p2 = initialVertices[face[1]];
|
||
Vector3f p3 = initialVertices[face[2]];
|
||
|
||
// Нормаль грани: (p2 - p1) x (p3 - p1)
|
||
Vector3f faceNormal = (p2 - p1).cross(p3 - p1).normalized();
|
||
|
||
smoothNormalsMap[p1] = smoothNormalsMap[p1] + faceNormal;
|
||
smoothNormalsMap[p2] = smoothNormalsMap[p2] + faceNormal;
|
||
smoothNormalsMap[p3] = smoothNormalsMap[p3] + faceNormal;
|
||
}
|
||
|
||
// Нормализация накопленных нормалей
|
||
for (auto& pair : smoothNormalsMap) {
|
||
pair.second = pair.second.normalized();
|
||
}
|
||
|
||
|
||
// 4. Финальное заполнение VertexDataStruct и Текстурные Координаты
|
||
for (const auto& face : faces) {
|
||
Vector3f p1 = initialVertices[face[0]];
|
||
Vector3f p2 = initialVertices[face[1]];
|
||
Vector3f p3 = initialVertices[face[2]];
|
||
|
||
// Позиции
|
||
result.PositionData.push_back(p1);
|
||
result.PositionData.push_back(p2);
|
||
result.PositionData.push_back(p3);
|
||
|
||
// Сглаженные Нормали (из карты)
|
||
result.NormalData.push_back(smoothNormalsMap[p1]);
|
||
result.NormalData.push_back(smoothNormalsMap[p2]);
|
||
result.NormalData.push_back(smoothNormalsMap[p3]);
|
||
|
||
// Текстурные Координаты (Планарная проекция на плоскость грани)
|
||
// p1 -> (0, 0), p2 -> (L_12, 0), p3 -> (L_13 * cos(angle), L_13 * sin(angle))
|
||
// Где L_xy - длина вектора, angle - угол между p2-p1 и p3-p1
|
||
|
||
Vector3f uAxis = (p2 - p1).normalized();
|
||
Vector3f vRaw = p3 - p1;
|
||
|
||
// Проекция vRaw на uAxis
|
||
float uProjLen = vRaw.dot(uAxis);
|
||
|
||
// Вектор V перпендикулярный U: vRaw - uProj
|
||
Vector3f vAxisRaw = vRaw - (uAxis * uProjLen);
|
||
|
||
// Длина оси V
|
||
float vLen = vAxisRaw.length();
|
||
|
||
// Нормализованная ось V
|
||
Vector3f vAxis = vAxisRaw.normalized();
|
||
|
||
// Координаты (u, v) для p1, p2, p3 относительно p1
|
||
Vector2f uv1 = { 0.0f, 0.0f };
|
||
Vector2f uv2 = { (p2 - p1).length(), 0.0f }; // p2-p1 вдоль оси U
|
||
Vector2f uv3 = { uProjLen, vLen }; // p3-p1: u-компонента = uProjLen, v-компонента = vLen
|
||
|
||
// Находим максимальный размер, чтобы масштабировать в [0, 1]
|
||
float maxUV = max( uv2.v[0], uv3.v[0], uv3.v[1] );
|
||
|
||
if (maxUV > 0.000001f) {
|
||
// Масштабируем:
|
||
result.TexCoordData.push_back(uv1);
|
||
result.TexCoordData.push_back(uv2 * (1.f/ maxUV));
|
||
result.TexCoordData.push_back(uv3 * (1.f/ maxUV));
|
||
}
|
||
else {
|
||
// Предотвращение деления на ноль для вырожденных граней
|
||
result.TexCoordData.push_back({ 0.0f, 0.0f });
|
||
result.TexCoordData.push_back({ 0.0f, 0.0f });
|
||
result.TexCoordData.push_back({ 0.0f, 0.0f });
|
||
}
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
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)
|
||
{
|
||
std::vector<Vector3f> emissionPoints = {
|
||
Vector3f{-2.1f, 0.9f, 5.0f},
|
||
Vector3f{2.1f, 0.9f, 5.0f}
|
||
};
|
||
sparkEmitter = SparkEmitter(emissionPoints, 100.0f);
|
||
}
|
||
|
||
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);
|
||
renderer.shaderManager.AddShaderFromFiles("defaultAtmosphere", "./shaders/defaultAtmosphere.vertex", "./shaders/defaultAtmosphere.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_fog.vertex", "./shaders/defaultColor_fog_desktop.fragment", CONST_ZIP_FILE);
|
||
renderer.shaderManager.AddShaderFromFiles("env", "./shaders/env_sky.vertex", "./shaders/env_sky_desktop.fragment", CONST_ZIP_FILE);
|
||
renderer.shaderManager.AddShaderFromFiles("defaultAtmosphere", "./shaders/defaultAtmosphere.vertex", "./shaders/defaultAtmosphere.fragment", CONST_ZIP_FILE);
|
||
renderer.shaderManager.AddShaderFromFiles("defaultColor2", "./shaders/defaultColor_fog2.vertex", "./shaders/defaultColor_fog2_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, -13, 0 });
|
||
spaceshipBase.Move(Vector3f{ -0.52998, -10, 10 });
|
||
|
||
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].data = CreateConvexPolyhedron(1999);
|
||
boxRenderArr[i].RefreshVBO();
|
||
}
|
||
|
||
sparkTexture = std::make_unique<Texture>(CreateTextureDataFromPng("./resources/spark.png", CONST_ZIP_FILE));
|
||
|
||
sparkEmitter.setTexture(sparkTexture);
|
||
|
||
|
||
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);
|
||
|
||
planetObject.init();
|
||
|
||
rockTexture = std::make_unique<Texture>(CreateTextureDataFromPng("./resources/rock.png", ""));
|
||
|
||
}
|
||
|
||
void Game::drawCubemap(float skyPercent)
|
||
{
|
||
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";
|
||
static const std::string skyPercentUniformName = "skyPercent";
|
||
|
||
renderer.shaderManager.PushShader(envShaderName);
|
||
renderer.RenderUniform1i(textureUniformName, 0);
|
||
renderer.RenderUniform1f(skyPercentUniformName, skyPercent);
|
||
renderer.EnableVertexAttribArray(vPositionName);
|
||
renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5,
|
||
static_cast<float>(Environment::width) / static_cast<float>(Environment::height),
|
||
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
|
||
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),
|
||
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
|
||
renderer.PushMatrix();
|
||
|
||
renderer.LoadIdentity();
|
||
renderer.TranslateMatrix({ 0,0, -1.0f * Environment::zoom });
|
||
|
||
|
||
glBindTexture(GL_TEXTURE_2D, spaceshipTexture->getTexID());
|
||
renderer.DrawVertexRenderStruct(spaceship);
|
||
sparkEmitter.draw(renderer, Environment::zoom, Environment::width, Environment::height);
|
||
|
||
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),
|
||
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
|
||
|
||
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({ 0.f, 0.f, 45000.f });
|
||
renderer.TranslateMatrix(boxCoordsArr[i].pos);
|
||
renderer.RotateMatrix(boxCoordsArr[i].m);
|
||
|
||
//glBindTexture(GL_TEXTURE_2D, boxTexture->getTexID());
|
||
glBindTexture(GL_TEXTURE_2D, rockTexture->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) * (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, 1.0f, 0.0f, 1.0f);
|
||
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
|
||
|
||
glViewport(0, 0, Environment::width, Environment::height);
|
||
|
||
CheckGlError();
|
||
|
||
float skyPercent = 0.0;
|
||
float distance = planetObject.distanceToPlanetSurface(Environment::shipPosition);
|
||
if (distance > 1900.f)
|
||
{
|
||
skyPercent = 0.0f;
|
||
}
|
||
else if (distance < 1000.f)
|
||
{
|
||
skyPercent = 1.0f;
|
||
}
|
||
else
|
||
{
|
||
skyPercent = (1900.f - distance) / 900.f;
|
||
}
|
||
|
||
drawCubemap(skyPercent);
|
||
planetObject.draw(renderer);
|
||
if (planetObject.distanceToPlanetSurface(Environment::shipPosition) > 100.f)
|
||
{
|
||
glClear(GL_DEPTH_BUFFER_BIT);
|
||
}
|
||
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);
|
||
sparkEmitter.update(static_cast<float>(delta));
|
||
planetObject.update(static_cast<float>(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 (abs(diffy) > 5.0 || abs(diffx) > 5.0) //threshold
|
||
{
|
||
|
||
float rotationPower = sqrtf(diffx * diffx + diffy * diffy);
|
||
|
||
//std::cout << rotationPower << std::endl;
|
||
|
||
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);
|
||
|
||
}
|
||
}
|
||
|
||
if (fabs(Environment::shipVelocity) > 0.01f)
|
||
{
|
||
Vector3f velocityDirection = { 0,0, -Environment::shipVelocity * delta / 1000.f };
|
||
Vector3f velocityDirectionAdjusted = MultMatrixVector(Environment::shipMatrix, velocityDirection);
|
||
|
||
Environment::shipPosition = Environment::shipPosition + velocityDirectionAdjusted;
|
||
}
|
||
|
||
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 (uiX >= volumeBarMinX - 40 && uiX <= volumeBarMaxX + 40 &&
|
||
uiY >= volumeBarMinY - 40 && uiY <= volumeBarMaxY + 40) {
|
||
isDraggingVolume = true;
|
||
UpdateVolumeFromMouse(mx, my);
|
||
}
|
||
else {
|
||
Environment::tapDownHold = 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;
|
||
}
|
||
else if (event.type == SDL_MOUSEMOTION) {
|
||
// 3. Îáðàáîòêà ïåðåìåùåíèÿ ìûøè
|
||
int mx = event.motion.x;
|
||
int my = event.motion.y;
|
||
|
||
if (isDraggingVolume) {
|
||
// Äâèãàåì ìûøü ïî ñëàéäåðó — ìåíÿåì ãðîìêîñòü è ïîçèöèþ êðóæêà
|
||
UpdateVolumeFromMouse(mx, my);
|
||
}
|
||
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 += 500.f;
|
||
}
|
||
if (event.key.keysym.sym == SDLK_k)
|
||
{
|
||
Environment::shipVelocity -= 500.f;
|
||
}
|
||
if (event.key.keysym.sym == SDLK_o)
|
||
{
|
||
Environment::shipVelocity += 50.f;
|
||
}
|
||
if (event.key.keysym.sym == SDLK_l)
|
||
{
|
||
Environment::shipVelocity -= 50.f;
|
||
}
|
||
}
|
||
}
|
||
render();
|
||
}
|
||
|
||
} // namespace ZL
|