Compare commits
16 Commits
70ef730e86
...
59b5bea540
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
59b5bea540 | ||
|
|
9da3cc4401 | ||
|
|
4a542fd6c8 | ||
|
|
a06cb77a66 | ||
|
|
3c2c06ca40 | ||
|
|
928600acd4 | ||
|
|
7ccda081b0 | ||
|
|
41f370f2ee | ||
|
|
4af20c65e6 | ||
|
|
ac551122d9 | ||
|
|
8528b4dc90 | ||
|
|
c35e093cc5 | ||
|
|
a8c76dee3c | ||
|
|
e84a094ad9 | ||
|
|
b0b7391939 | ||
|
|
3e2632c5b5 |
@ -73,6 +73,8 @@ set(SOURCES
|
||||
../src/MenuManager.cpp
|
||||
../src/Space.h
|
||||
../src/Space.cpp
|
||||
../src/GameConstants.h
|
||||
../src/GameConstants.cpp
|
||||
)
|
||||
|
||||
add_executable(space-game001 ${SOURCES})
|
||||
|
||||
@ -1,9 +1,14 @@
|
||||
# how to build
|
||||
|
||||
If emsdk is not installed, you need to clone it from here: https://github.com/emscripten-core/emsdk
|
||||
|
||||
Activate the environment:
|
||||
and install:
|
||||
```
|
||||
C:\Work\Projects\emsdk\emsdk.bat install latest
|
||||
```
|
||||
|
||||
Then activate the environment:
|
||||
```
|
||||
C:\Work\Projects\emsdk\emsdk.bat activate latest
|
||||
C:\Work\Projects\emsdk\emsdk_env.bat
|
||||
```
|
||||
|
||||
@ -17,23 +17,10 @@
|
||||
width: 100vw; height: 100vh;
|
||||
border: none;
|
||||
}
|
||||
/* Кнопка Fullscreen */
|
||||
#fs-button {
|
||||
position: absolute;
|
||||
top: 10px; right: 10px;
|
||||
padding: 10px;
|
||||
z-index: 10;
|
||||
background: rgba(255,255,255,0.3);
|
||||
color: white; border: 1px solid white;
|
||||
cursor: pointer;
|
||||
font-family: sans-serif;
|
||||
border-radius: 5px;
|
||||
}
|
||||
#status { color: white; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<button id="fs-button">Fullscreen</button>
|
||||
<div id="status">Downloading...</div>
|
||||
<canvas id="canvas" oncontextmenu="event.preventDefault()" tabindex="-1"></canvas>
|
||||
|
||||
@ -49,17 +36,6 @@
|
||||
}
|
||||
};
|
||||
|
||||
// Кнопка Fullscreen
|
||||
document.getElementById('fs-button').addEventListener('click', function() {
|
||||
if (!document.fullscreenElement) {
|
||||
document.documentElement.requestFullscreen().catch(e => {
|
||||
console.error(`Error attempting to enable full-screen mode: ${e.message}`);
|
||||
});
|
||||
} else {
|
||||
document.exitFullscreen();
|
||||
}
|
||||
});
|
||||
|
||||
// Обработка ориентации
|
||||
window.addEventListener("orientationchange", function() {
|
||||
// Chrome на Android обновляет innerWidth/Height не мгновенно.
|
||||
|
||||
@ -67,6 +67,8 @@ add_executable(space-game001
|
||||
../src/MenuManager.cpp
|
||||
../src/Space.h
|
||||
../src/Space.cpp
|
||||
../src/GameConstants.h
|
||||
../src/GameConstants.cpp
|
||||
)
|
||||
|
||||
# Установка проекта по умолчанию для Visual Studio
|
||||
|
||||
BIN
resources/button_minus.png
(Stored with Git LFS)
Normal file
BIN
resources/button_minus.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
resources/button_minus_pressed.png
(Stored with Git LFS)
Normal file
BIN
resources/button_minus_pressed.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
resources/button_plus.png
(Stored with Git LFS)
Normal file
BIN
resources/button_plus.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
resources/button_plus_pressed.png
(Stored with Git LFS)
Normal file
BIN
resources/button_plus_pressed.png
(Stored with Git LFS)
Normal file
Binary file not shown.
22
resources/config/connecting.json
Normal file
22
resources/config/connecting.json
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"root": {
|
||||
"type": "FrameLayout",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"width": "match_parent",
|
||||
"height": "match_parent",
|
||||
"children": [
|
||||
{
|
||||
"type": "StaticImage",
|
||||
"name": "connecting",
|
||||
"x" : 0,
|
||||
"y" : 0,
|
||||
"width": 488,
|
||||
"height": 154,
|
||||
"horizontal_gravity": "center",
|
||||
"vertical_gravity": "center",
|
||||
"texture": "resources/connecting.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
52
resources/config/connection_failed.json
Normal file
52
resources/config/connection_failed.json
Normal file
@ -0,0 +1,52 @@
|
||||
{
|
||||
"root": {
|
||||
"type": "FrameLayout",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"width": "match_parent",
|
||||
"height": "match_parent",
|
||||
"children": [
|
||||
{
|
||||
"type": "StaticImage",
|
||||
"name": "connectionFailed",
|
||||
"x" : 0,
|
||||
"y" : 0,
|
||||
"width": 488,
|
||||
"height": 308,
|
||||
"horizontal_gravity": "center",
|
||||
"vertical_gravity": "center",
|
||||
"texture": "resources/connection_failed.png"
|
||||
},
|
||||
{
|
||||
"type": "Button",
|
||||
"name": "connectionFailedReconnectButton",
|
||||
"width": 382,
|
||||
"height": 56,
|
||||
"x" : 0,
|
||||
"y" : -20,
|
||||
"horizontal_gravity": "center",
|
||||
"vertical_gravity": "center",
|
||||
"textures": {
|
||||
"normal": "resources/game_over/Filledbuttons.png",
|
||||
"hover": "resources/game_over/Variant5.png",
|
||||
"pressed": "resources/game_over/Variant6.png"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Button",
|
||||
"name": "connectionFailedGoBack",
|
||||
"width": 382,
|
||||
"height": 56,
|
||||
"x" : 0,
|
||||
"y" : -86,
|
||||
"horizontal_gravity": "center",
|
||||
"vertical_gravity": "center",
|
||||
"textures": {
|
||||
"normal": "resources/game_over/Secondarybutton.png",
|
||||
"hover": "resources/game_over/Secondarybutton.png",
|
||||
"pressed": "resources/game_over/Secondarybutton.png"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
52
resources/config/connection_lost.json
Normal file
52
resources/config/connection_lost.json
Normal file
@ -0,0 +1,52 @@
|
||||
{
|
||||
"root": {
|
||||
"type": "FrameLayout",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"width": "match_parent",
|
||||
"height": "match_parent",
|
||||
"children": [
|
||||
{
|
||||
"type": "StaticImage",
|
||||
"name": "connectionLost",
|
||||
"x" : 0,
|
||||
"y" : 0,
|
||||
"width": 488,
|
||||
"height": 308,
|
||||
"horizontal_gravity": "center",
|
||||
"vertical_gravity": "center",
|
||||
"texture": "resources/connection_lost.png"
|
||||
},
|
||||
{
|
||||
"type": "Button",
|
||||
"name": "reconnectButton",
|
||||
"width": 382,
|
||||
"height": 56,
|
||||
"x" : 0,
|
||||
"y" : -20,
|
||||
"horizontal_gravity": "center",
|
||||
"vertical_gravity": "center",
|
||||
"textures": {
|
||||
"normal": "resources/game_over/Filledbuttons.png",
|
||||
"hover": "resources/game_over/Variant5.png",
|
||||
"pressed": "resources/game_over/Variant6.png"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Button",
|
||||
"name": "exitServerButton",
|
||||
"width": 382,
|
||||
"height": 56,
|
||||
"x" : 0,
|
||||
"y" : -86,
|
||||
"horizontal_gravity": "center",
|
||||
"vertical_gravity": "center",
|
||||
"textures": {
|
||||
"normal": "resources/game_over/Secondarybutton.png",
|
||||
"hover": "resources/game_over/Secondarybutton.png",
|
||||
"pressed": "resources/game_over/Secondarybutton.png"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -5,7 +5,7 @@
|
||||
"texture": "resources/spark_white.png",
|
||||
"speedRange": [10.0, 30.0],
|
||||
"zSpeedRange": [-1.0, 1.0],
|
||||
"scaleRange": [0.5, 1.0],
|
||||
"scaleRange": [5.0, 10.0],
|
||||
"lifeTimeRange": [200.0, 800.0],
|
||||
"emissionRate": 50.0,
|
||||
"maxParticles": 5,
|
||||
|
||||
@ -65,8 +65,8 @@
|
||||
"height": 56,
|
||||
"textures": {
|
||||
"normal": "resources/game_over/Filledbuttons.png",
|
||||
"hover": "resources/game_over/Filledbuttons.png",
|
||||
"pressed": "resources/game_over/Filledbuttons.png"
|
||||
"hover": "resources/game_over/Variant5.png",
|
||||
"pressed": "resources/game_over/Variant6.png"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@ -11,26 +11,18 @@
|
||||
"height": "match_parent",
|
||||
"children": [
|
||||
{
|
||||
"type": "Button",
|
||||
"type": "StaticImage",
|
||||
"name": "titleBtn",
|
||||
"width": 254,
|
||||
"height": 35,
|
||||
"textures": {
|
||||
"normal": "resources/main_menu/title.png",
|
||||
"hover": "resources/main_menu/title.png",
|
||||
"pressed": "resources/main_menu/title.png"
|
||||
}
|
||||
"texture": "resources/main_menu/title.png"
|
||||
},
|
||||
{
|
||||
"type": "Button",
|
||||
"type": "StaticImage",
|
||||
"name": "underlineBtn",
|
||||
"width": 168,
|
||||
"height": 44,
|
||||
"textures": {
|
||||
"normal": "resources/main_menu/line.png",
|
||||
"hover": "resources/main_menu/line.png",
|
||||
"pressed": "resources/main_menu/line.png"
|
||||
}
|
||||
"texture": "resources/main_menu/line.png"
|
||||
},
|
||||
{
|
||||
"type": "Button",
|
||||
@ -50,8 +42,8 @@
|
||||
"height": 56,
|
||||
"textures": {
|
||||
"normal": "resources/main_menu/single.png",
|
||||
"hover": "resources/main_menu/single.png",
|
||||
"pressed": "resources/main_menu/single.png"
|
||||
"hover": "resources/main_menu/Variant5.png",
|
||||
"pressed": "resources/main_menu/Variant6.png"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -61,20 +53,16 @@
|
||||
"height": 56,
|
||||
"textures": {
|
||||
"normal": "resources/main_menu/multi.png",
|
||||
"hover": "resources/main_menu/multi.png",
|
||||
"pressed": "resources/main_menu/multi.png"
|
||||
"hover": "resources/main_menu/Variant7.png",
|
||||
"pressed": "resources/main_menu/Variant8.png"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Button",
|
||||
"type": "StaticImage",
|
||||
"name": "versionLabel",
|
||||
"width": 81,
|
||||
"height": 9,
|
||||
"textures": {
|
||||
"normal": "resources/main_menu/version.png",
|
||||
"hover": "resources/main_menu/version.png",
|
||||
"pressed": "resources/main_menu/version.png"
|
||||
}
|
||||
"texture": "resources/main_menu/version.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -1,20 +1,14 @@
|
||||
{
|
||||
"emissionRate": 100.0,
|
||||
"maxParticles": 200,
|
||||
"emissionRate": 0.4,
|
||||
"maxParticles": 400,
|
||||
"particleSize": 0.3,
|
||||
"biasX": 0.3,
|
||||
"emissionPoints": [
|
||||
{
|
||||
"position": [-1.3, 0, 0.0]
|
||||
},
|
||||
{
|
||||
"position": [1.3, 0.0, 0.0]
|
||||
}
|
||||
],
|
||||
"speedRange": [0.5, 2.0],
|
||||
"zSpeedRange": [1.0, 3.0],
|
||||
"scaleRange": [0.8, 1.2],
|
||||
"lifeTimeRange": [600.0, 1400.0],
|
||||
"lifeTimeRange": [300.0, 500.0],
|
||||
"texture": "resources/spark.png",
|
||||
"shaderProgramName": "spark"
|
||||
}
|
||||
20
resources/config/spark_config_cargo.json
Normal file
20
resources/config/spark_config_cargo.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"emissionRate": 0.4,
|
||||
"maxParticles": 400,
|
||||
"particleSize": 0.3,
|
||||
"biasX": 0.3,
|
||||
"emissionPoints": [
|
||||
{
|
||||
"position": [0.0, 2.8, -3.5]
|
||||
},
|
||||
{
|
||||
"position": [0.0, 1.5, -3.5]
|
||||
}
|
||||
],
|
||||
"speedRange": [0.5, 2.0],
|
||||
"zSpeedRange": [1.0, 3.0],
|
||||
"scaleRange": [0.8, 1.2],
|
||||
"lifeTimeRange": [600.0, 1400.0],
|
||||
"texture": "resources/spark.png",
|
||||
"shaderProgramName": "spark"
|
||||
}
|
||||
@ -2,14 +2,14 @@
|
||||
"emissionPoints": [
|
||||
{ "position": [0.0, 0.0, 0.0] }
|
||||
],
|
||||
"texture": "resources/spark_white.png",
|
||||
"speedRange": [10.0, 30.0],
|
||||
"texture": "resources/spark2.png",
|
||||
"speedRange": [5.0, 10.0],
|
||||
"zSpeedRange": [-1.0, 1.0],
|
||||
"scaleRange": [0.5, 1.0],
|
||||
"scaleRange": [0.5, 2.0],
|
||||
"lifeTimeRange": [200.0, 800.0],
|
||||
"emissionRate": 50.0,
|
||||
"maxParticles": 10,
|
||||
"particleSize": 0.5,
|
||||
"emissionRate": 30.0,
|
||||
"maxParticles": 150,
|
||||
"particleSize": 1.0,
|
||||
"biasX": 0.1,
|
||||
"shaderProgramName": "default"
|
||||
}
|
||||
@ -6,6 +6,20 @@
|
||||
"width": "match_parent",
|
||||
"height": "match_parent",
|
||||
"children": [
|
||||
{
|
||||
"type": "TextView",
|
||||
"name": "velocityText",
|
||||
"x": 10,
|
||||
"y": 10,
|
||||
"width": 200,
|
||||
"height": 40,
|
||||
"horizontal_gravity": "left",
|
||||
"vertical_gravity": "top",
|
||||
"text": "Velocity: 0",
|
||||
"fontSize": 24,
|
||||
"color": [1.0, 1.0, 1.0, 1.0],
|
||||
"centered": false
|
||||
},
|
||||
{
|
||||
"type": "Button",
|
||||
"name": "shootButton",
|
||||
@ -16,9 +30,10 @@
|
||||
"horizontal_gravity": "right",
|
||||
"vertical_gravity": "bottom",
|
||||
"textures": {
|
||||
"normal": "resources/shoot_normal.png",
|
||||
"hover": "resources/shoot_hover.png",
|
||||
"pressed": "resources/shoot_pressed.png"
|
||||
"normal": "resources/fire.png",
|
||||
"hover": "resources/fire.png",
|
||||
"pressed": "resources/fire2.png",
|
||||
"disabled": "resources/fire_disabled.png"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -31,25 +46,42 @@
|
||||
"horizontal_gravity": "left",
|
||||
"vertical_gravity": "bottom",
|
||||
"textures": {
|
||||
"normal": "resources/shoot_normal.png",
|
||||
"hover": "resources/shoot_hover.png",
|
||||
"pressed": "resources/shoot_pressed.png"
|
||||
"normal": "resources/fire.png",
|
||||
"hover": "resources/fire.png",
|
||||
"pressed": "resources/fire2.png",
|
||||
"disabled": "resources/fire_disabled.png"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Slider",
|
||||
"name": "velocitySlider",
|
||||
"x": 10,
|
||||
"y": 200,
|
||||
"width": 80,
|
||||
"height": 300,
|
||||
"value": 0.0,
|
||||
"orientation": "vertical",
|
||||
"type": "Button",
|
||||
"name": "minusButton",
|
||||
"x": -20,
|
||||
"y": 110,
|
||||
"width": 150,
|
||||
"height": 150,
|
||||
"border" : 20,
|
||||
"horizontal_gravity": "right",
|
||||
"vertical_gravity": "bottom",
|
||||
"textures": {
|
||||
"track": "resources/velocitySliderTexture.png",
|
||||
"knob": "resources/velocitySliderButton.png"
|
||||
"normal": "resources/button_minus.png",
|
||||
"hover": "resources/button_minus_pressed.png",
|
||||
"pressed": "resources/button_minus_pressed.png"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Button",
|
||||
"name": "plusButton",
|
||||
"x": -20,
|
||||
"y": 220,
|
||||
"width": 150,
|
||||
"height": 150,
|
||||
"border" : 20,
|
||||
"horizontal_gravity": "right",
|
||||
"vertical_gravity": "bottom",
|
||||
"textures": {
|
||||
"normal": "resources/button_plus.png",
|
||||
"hover": "resources/button_plus_pressed.png",
|
||||
"pressed": "resources/button_plus_pressed.png"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
BIN
resources/connecting.png
(Stored with Git LFS)
Normal file
BIN
resources/connecting.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
resources/connection_failed.png
(Stored with Git LFS)
Normal file
BIN
resources/connection_failed.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
resources/connection_lost.png
(Stored with Git LFS)
Normal file
BIN
resources/connection_lost.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
resources/fire.png
(Stored with Git LFS)
Normal file
BIN
resources/fire.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
resources/fire2.png
(Stored with Git LFS)
Normal file
BIN
resources/fire2.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
resources/fire_disabled.png
(Stored with Git LFS)
Normal file
BIN
resources/fire_disabled.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
resources/game_over/Variant5.png
(Stored with Git LFS)
Normal file
BIN
resources/game_over/Variant5.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
resources/game_over/Variant6.png
(Stored with Git LFS)
Normal file
BIN
resources/game_over/Variant6.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
resources/main_menu/Variant5.png
(Stored with Git LFS)
Normal file
BIN
resources/main_menu/Variant5.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
resources/main_menu/Variant6.png
(Stored with Git LFS)
Normal file
BIN
resources/main_menu/Variant6.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
resources/main_menu/Variant7.png
(Stored with Git LFS)
Normal file
BIN
resources/main_menu/Variant7.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
resources/main_menu/Variant8.png
(Stored with Git LFS)
Normal file
BIN
resources/main_menu/Variant8.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
resources/spark.png
(Stored with Git LFS)
BIN
resources/spark.png
(Stored with Git LFS)
Binary file not shown.
BIN
resources/spark2.png
(Stored with Git LFS)
BIN
resources/spark2.png
(Stored with Git LFS)
Binary file not shown.
BIN
resources/spark3.png
(Stored with Git LFS)
Normal file
BIN
resources/spark3.png
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -26,6 +26,7 @@ add_subdirectory("${BOOST_SRC_DIR}/libs/predef" boost-predef-build EXCLUDE_FROM_
|
||||
|
||||
# Исполняемый файл сервера
|
||||
add_executable(Server
|
||||
server.h
|
||||
server.cpp
|
||||
../src/network/ClientState.h
|
||||
../src/network/ClientState.cpp
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
140
server/server.h
Normal file
140
server/server.h
Normal file
@ -0,0 +1,140 @@
|
||||
#pragma once
|
||||
#include <boost/beast/core.hpp>
|
||||
#include <boost/beast/websocket.hpp>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <unordered_set>
|
||||
#include <Eigen/Dense>
|
||||
#include "../src/network/ClientState.h"
|
||||
#define _USE_MATH_DEFINES
|
||||
#include <math.h>
|
||||
|
||||
namespace beast = boost::beast;
|
||||
namespace http = beast::http;
|
||||
namespace websocket = beast::websocket;
|
||||
namespace net = boost::asio;
|
||||
using tcp = net::ip::tcp;
|
||||
static constexpr float kWorldZOffset = 45000.0f;
|
||||
static const Eigen::Vector3f kWorldOffset(0.0f, 0.0f, kWorldZOffset);
|
||||
|
||||
static constexpr float kShipRadius = 15.0f;
|
||||
static constexpr float kSpawnShipMargin = 25.0f;
|
||||
static constexpr float kSpawnBoxMargin = 15.0f;
|
||||
static constexpr float kSpawnZJitter = 60.0f;
|
||||
|
||||
std::vector<std::string> split(const std::string& s, char delimiter);
|
||||
|
||||
struct DeathInfo {
|
||||
int targetId = -1;
|
||||
uint64_t serverTime = 0;
|
||||
Eigen::Vector3f position = Eigen::Vector3f::Zero();
|
||||
int killerId = -1;
|
||||
};
|
||||
|
||||
|
||||
struct ServerBox {
|
||||
Eigen::Vector3f position;
|
||||
Eigen::Matrix3f rotation;
|
||||
float collisionRadius = 2.0f;
|
||||
bool destroyed = false;
|
||||
};
|
||||
|
||||
struct Projectile {
|
||||
int shooterId = -1;
|
||||
uint64_t spawnMs = 0;
|
||||
Eigen::Vector3f pos;
|
||||
Eigen::Vector3f vel;
|
||||
float lifeMs = PROJECTILE_LIFE;
|
||||
};
|
||||
|
||||
struct BoxDestroyedInfo {
|
||||
int boxIndex = -1;
|
||||
uint64_t serverTime = 0;
|
||||
Eigen::Vector3f position = Eigen::Vector3f::Zero();
|
||||
int destroyedBy = -1;
|
||||
};
|
||||
|
||||
class Server;
|
||||
|
||||
class Session : public std::enable_shared_from_this<Session> {
|
||||
Server& server_;
|
||||
websocket::stream<beast::tcp_stream> ws_;
|
||||
beast::flat_buffer buffer_;
|
||||
int id_;
|
||||
bool is_writing_ = false;
|
||||
std::queue<std::shared_ptr<std::string>> writeQueue_;
|
||||
std::mutex writeMutex_;
|
||||
|
||||
|
||||
public:
|
||||
ClientStateInterval timedClientStates;
|
||||
bool joined_ = false;
|
||||
std::chrono::system_clock::time_point lastReceivedTime_;
|
||||
|
||||
bool hasReservedSpawn_ = false;
|
||||
Eigen::Vector3f reservedSpawn_ = Eigen::Vector3f(0.0f, 0.0f, kWorldZOffset);
|
||||
|
||||
std::string nickname = "Player";
|
||||
int shipType = 0;
|
||||
|
||||
Session(Server& server, tcp::socket&& socket, int id);
|
||||
int get_id() const;
|
||||
bool hasSpawnReserved() const;
|
||||
const Eigen::Vector3f& reservedSpawn() const;
|
||||
bool fetchStateAtTime(std::chrono::system_clock::time_point targetTime, ClientState& outState) const;
|
||||
void send_message(const std::string& msg);
|
||||
void run();
|
||||
bool IsMessageValid(const std::string& fullMessage);
|
||||
bool is_timed_out(std::chrono::system_clock::time_point now) const;
|
||||
void force_disconnect();
|
||||
|
||||
private:
|
||||
void sendBoxesToClient();
|
||||
public:
|
||||
void init();
|
||||
ClientState get_latest_state(std::chrono::system_clock::time_point now);
|
||||
void doWrite();
|
||||
private:
|
||||
|
||||
void do_read();
|
||||
void process_message(const std::string& msg);
|
||||
};
|
||||
|
||||
class Server
|
||||
{
|
||||
public:
|
||||
tcp::acceptor& acceptor_;
|
||||
net::io_context& ioc_;
|
||||
net::steady_timer timer;
|
||||
std::vector<BoxDestroyedInfo> g_boxDestructions;
|
||||
std::mutex g_boxDestructions_mutex;
|
||||
|
||||
std::vector<ServerBox> g_serverBoxes;
|
||||
std::mutex g_boxes_mutex;
|
||||
|
||||
std::vector<std::shared_ptr<Session>> g_sessions;
|
||||
std::mutex g_sessions_mutex;
|
||||
|
||||
std::vector<Projectile> g_projectiles;
|
||||
std::mutex g_projectiles_mutex;
|
||||
|
||||
std::unordered_set<int> g_dead_players;
|
||||
std::mutex g_dead_mutex;
|
||||
|
||||
int next_id = 1000;
|
||||
|
||||
std::vector<ServerBox> generateServerBoxes(int count);
|
||||
public:
|
||||
Server(tcp::acceptor& acceptor, net::io_context& ioc);
|
||||
|
||||
void broadcastToAll(const std::string& message);
|
||||
void broadcastToAllExceptId(const std::string& message, int id);
|
||||
void createProjectile(int id, Eigen::Vector3f pos, Eigen::Quaternionf dir, float velocity);
|
||||
void update_world();
|
||||
Eigen::Vector3f PickSafeSpawnPos(int forPlayerId);
|
||||
void init();
|
||||
void accept();
|
||||
};
|
||||
@ -1,4 +1,4 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "render/Renderer.h"
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#ifdef AUDIO
|
||||
#ifdef AUDIO
|
||||
|
||||
#include "AudioPlayerAsync.h"
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#ifdef AUDIO
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#include "BoneAnimatedModel.h"
|
||||
#include "BoneAnimatedModel.h"
|
||||
#include <regex>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
#include "render/Renderer.h"
|
||||
#include <unordered_map>
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#include "Environment.h"
|
||||
#include "Environment.h"
|
||||
|
||||
#include "utils/Utils.h"
|
||||
|
||||
@ -13,8 +13,8 @@ namespace ZL {
|
||||
|
||||
|
||||
int Environment::windowHeaderHeight = 0;
|
||||
int Environment::width = 0;
|
||||
int Environment::height = 0;
|
||||
int Environment::width = CONST_DEFAULT_WIDTH;
|
||||
int Environment::height = CONST_DEFAULT_HEIGHT;
|
||||
float Environment::zoom = DEFAULT_ZOOM;
|
||||
|
||||
|
||||
@ -49,10 +49,10 @@ void Environment::computeProjectionDimensions()
|
||||
if (width >= height) {
|
||||
// Landscape: fix height to 720, scale width to preserve aspect
|
||||
projectionHeight = refShortSide;
|
||||
projectionWidth = refShortSide * aspect;
|
||||
projectionWidth = refShortSide * aspect;
|
||||
} else {
|
||||
// Portrait: fix width to 720, scale height to preserve aspect
|
||||
projectionWidth = refShortSide;
|
||||
projectionWidth = refShortSide;
|
||||
projectionHeight = refShortSide / aspect;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
#ifdef __linux__
|
||||
#include <SDL2/SDL.h>
|
||||
#endif
|
||||
@ -14,6 +14,8 @@ namespace ZL {
|
||||
|
||||
class Environment {
|
||||
public:
|
||||
static constexpr int CONST_DEFAULT_WIDTH = 1280;
|
||||
static constexpr int CONST_DEFAULT_HEIGHT = 720;
|
||||
static int windowHeaderHeight;
|
||||
static int width;
|
||||
static int height;
|
||||
|
||||
158
src/Game.cpp
158
src/Game.cpp
@ -30,7 +30,7 @@
|
||||
|
||||
#include "network/LocalClient.h"
|
||||
#include "network/ClientState.h"
|
||||
|
||||
#include "GameConstants.h"
|
||||
|
||||
namespace ZL
|
||||
{
|
||||
@ -86,6 +86,8 @@ namespace ZL
|
||||
//glContext = SDL_GL_CreateContext(ZL::Environment::window);
|
||||
//glContext = in_glContext;
|
||||
|
||||
Environment::width = Environment::CONST_DEFAULT_WIDTH;
|
||||
Environment::height = Environment::CONST_DEFAULT_HEIGHT;
|
||||
Environment::computeProjectionDimensions();
|
||||
|
||||
ZL::BindOpenGlFunctions();
|
||||
@ -146,10 +148,28 @@ namespace ZL
|
||||
|
||||
menuManager.setupMenu();
|
||||
|
||||
menuManager.onMainMenuEntered = [this]() {
|
||||
if (networkClient) {
|
||||
networkClient->Disconnect();
|
||||
networkClient.reset();
|
||||
}
|
||||
};
|
||||
|
||||
menuManager.onSingleplayerPressed = [this](const std::string& nickname, int shipType) {
|
||||
Environment::shipState.nickname = nickname;
|
||||
Environment::shipState.shipType = shipType;
|
||||
|
||||
if (Environment::shipState.shipType == 1)
|
||||
{
|
||||
menuManager.uiManager.findButton("shootButton")->state = ButtonState::Disabled;
|
||||
menuManager.uiManager.findButton("shootButton2")->state = ButtonState::Disabled;
|
||||
}
|
||||
else
|
||||
{
|
||||
menuManager.uiManager.findButton("shootButton")->state = ButtonState::Normal;
|
||||
menuManager.uiManager.findButton("shootButton2")->state = ButtonState::Normal;
|
||||
}
|
||||
|
||||
auto localClient = new LocalClient;
|
||||
ClientState st = Environment::shipState;
|
||||
st.id = localClient->GetClientId();
|
||||
@ -158,15 +178,14 @@ namespace ZL
|
||||
networkClient = std::unique_ptr<INetworkClient>(localClient);
|
||||
networkClient->Connect("", 0);
|
||||
|
||||
space.resetPlayerState();
|
||||
lastTickCount = 0;
|
||||
spaceGameStarted = 1;
|
||||
};
|
||||
|
||||
menuManager.onMultiplayerPressed = [this](const std::string& nickname, int shipType) {
|
||||
Environment::shipState.nickname = nickname;
|
||||
Environment::shipState.shipType = shipType;
|
||||
|
||||
|
||||
#ifdef EMSCRIPTEN
|
||||
networkClient = std::make_unique<WebSocketClientEmscripten>();
|
||||
networkClient->Connect("localhost", 8081);
|
||||
@ -174,7 +193,7 @@ namespace ZL
|
||||
networkClient = std::make_unique<WebSocketClient>(taskManager.getIOContext());
|
||||
networkClient->Connect("localhost", 8081);
|
||||
#endif
|
||||
|
||||
|
||||
if (networkClient) {
|
||||
std::string joinMsg = std::string("JOIN:") + nickname + ":" + std::to_string(shipType);
|
||||
networkClient->Send(joinMsg);
|
||||
@ -183,12 +202,12 @@ namespace ZL
|
||||
|
||||
space.boxCoordsArr.clear();
|
||||
space.boxRenderArr.clear();
|
||||
//space.boxLabels.clear();
|
||||
space.boxAlive.clear();
|
||||
space.serverBoxesApplied = false;
|
||||
|
||||
space.resetPlayerState();
|
||||
connectingStartTicks = SDL_GetTicks();
|
||||
lastTickCount = 0;
|
||||
spaceGameStarted = 1;
|
||||
};
|
||||
|
||||
|
||||
@ -200,21 +219,10 @@ namespace ZL
|
||||
|
||||
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.DisableVertexAttribArray(vPositionName);
|
||||
renderer.DisableVertexAttribArray(vTexCoordName);
|
||||
glEnable(GL_BLEND);
|
||||
menuManager.uiManager.draw(renderer);
|
||||
glDisable(GL_BLEND);
|
||||
@ -230,13 +238,14 @@ namespace ZL
|
||||
}
|
||||
|
||||
void Game::drawScene() {
|
||||
glViewport(0, 0, Environment::width, Environment::height);
|
||||
if (!loadingCompleted) {
|
||||
drawLoading();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
if (spaceGameStarted) {
|
||||
if (menuManager.shouldRenderSpace()) {
|
||||
space.drawScene();
|
||||
}
|
||||
else
|
||||
@ -250,17 +259,10 @@ namespace ZL
|
||||
|
||||
void Game::drawLoading()
|
||||
{
|
||||
static const std::string defaultShaderName = "default";
|
||||
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);
|
||||
|
||||
//float width = Environment::projectionWidth;
|
||||
//float height = Environment::projectionHeight;
|
||||
@ -278,9 +280,6 @@ namespace ZL
|
||||
|
||||
renderer.PopMatrix();
|
||||
renderer.PopProjectionMatrix();
|
||||
|
||||
renderer.DisableVertexAttribArray(vPositionName);
|
||||
renderer.DisableVertexAttribArray(vTexCoordName);
|
||||
renderer.shaderManager.PopShader();
|
||||
CheckGlError();
|
||||
}
|
||||
@ -320,7 +319,7 @@ namespace ZL
|
||||
//throw std::runtime_error("Synchronization is lost");
|
||||
}
|
||||
|
||||
if (spaceGameStarted) {
|
||||
if (menuManager.shouldRenderSpace()) {
|
||||
space.processTickCount(newTickCount, delta);
|
||||
}
|
||||
menuManager.uiManager.update(static_cast<float>(delta));
|
||||
@ -335,8 +334,10 @@ namespace ZL
|
||||
glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
//processTickCount();
|
||||
drawScene();
|
||||
processTickCount();
|
||||
//std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
|
||||
SDL_GL_SwapWindow(ZL::Environment::window);
|
||||
}
|
||||
@ -370,33 +371,53 @@ namespace ZL
|
||||
if (event.type == SDL_FINGERDOWN) {
|
||||
int mx = static_cast<int>(event.tfinger.x * Environment::projectionWidth);
|
||||
int my = static_cast<int>(event.tfinger.y * Environment::projectionHeight);
|
||||
handleDown(mx, my);
|
||||
handleDown(static_cast<int64_t>(event.tfinger.fingerId), mx, my);
|
||||
}
|
||||
else if (event.type == SDL_FINGERUP) {
|
||||
int mx = static_cast<int>(event.tfinger.x * Environment::projectionWidth);
|
||||
int my = static_cast<int>(event.tfinger.y * Environment::projectionHeight);
|
||||
handleUp(mx, my);
|
||||
handleUp(static_cast<int64_t>(event.tfinger.fingerId), mx, my);
|
||||
}
|
||||
else if (event.type == SDL_FINGERMOTION) {
|
||||
int mx = static_cast<int>(event.tfinger.x * Environment::projectionWidth);
|
||||
int my = static_cast<int>(event.tfinger.y * Environment::projectionHeight);
|
||||
handleMotion(mx, my);
|
||||
handleMotion(static_cast<int64_t>(event.tfinger.fingerId), mx, my);
|
||||
}
|
||||
#else
|
||||
|
||||
// Emscripten on mobile browser: handle real touch events with per-finger IDs.
|
||||
// SDL_HINT_TOUCH_MOUSE_EVENTS="0" is set in main.cpp so these don't
|
||||
// also fire SDL_MOUSEBUTTONDOWN, preventing double-processing.
|
||||
#ifdef EMSCRIPTEN
|
||||
if (event.type == SDL_FINGERDOWN) {
|
||||
int mx = static_cast<int>(event.tfinger.x * Environment::projectionWidth);
|
||||
int my = static_cast<int>(event.tfinger.y * Environment::projectionHeight);
|
||||
handleDown(static_cast<int64_t>(event.tfinger.fingerId), mx, my);
|
||||
}
|
||||
else if (event.type == SDL_FINGERUP) {
|
||||
int mx = static_cast<int>(event.tfinger.x * Environment::projectionWidth);
|
||||
int my = static_cast<int>(event.tfinger.y * Environment::projectionHeight);
|
||||
handleUp(static_cast<int64_t>(event.tfinger.fingerId), mx, my);
|
||||
}
|
||||
else if (event.type == SDL_FINGERMOTION) {
|
||||
int mx = static_cast<int>(event.tfinger.x * Environment::projectionWidth);
|
||||
int my = static_cast<int>(event.tfinger.y * Environment::projectionHeight);
|
||||
handleMotion(static_cast<int64_t>(event.tfinger.fingerId), mx, my);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (event.type == SDL_MOUSEBUTTONDOWN || event.type == SDL_MOUSEBUTTONUP) {
|
||||
// Преобразуем экранные пиксели в проекционные единицы
|
||||
int mx = static_cast<int>((float)event.button.x / Environment::width * Environment::projectionWidth);
|
||||
int my = static_cast<int>((float)event.button.y / Environment::height * Environment::projectionHeight);
|
||||
|
||||
if (event.type == SDL_MOUSEBUTTONDOWN) handleDown(mx, my);
|
||||
else handleUp(mx, my);
|
||||
if (event.type == SDL_MOUSEBUTTONDOWN) handleDown(ZL::UiManager::MOUSE_FINGER_ID, mx, my);
|
||||
else handleUp(ZL::UiManager::MOUSE_FINGER_ID, mx, my);
|
||||
//std::cout << "Mouse button " << (event.type == SDL_MOUSEBUTTONDOWN ? "down" : "up") << ": x=" << mx << " y=" << my << std::endl;
|
||||
}
|
||||
else if (event.type == SDL_MOUSEMOTION) {
|
||||
int mx = static_cast<int>((float)event.motion.x / Environment::width * Environment::projectionWidth);
|
||||
int my = static_cast<int>((float)event.motion.y / Environment::height * Environment::projectionHeight);
|
||||
handleMotion(mx, my);
|
||||
handleMotion(ZL::UiManager::MOUSE_FINGER_ID, mx, my);
|
||||
}
|
||||
|
||||
/*if (event.type == SDL_MOUSEBUTTONDOWN) {
|
||||
@ -459,6 +480,22 @@ namespace ZL
|
||||
}
|
||||
//#endif
|
||||
networkClient->Poll();
|
||||
|
||||
if (menuManager.getState() == GameState::Connecting) {
|
||||
if (networkClient->IsConnected()) {
|
||||
menuManager.notifyConnected();
|
||||
// Enable/disable shoot buttons based on ship type
|
||||
if (Environment::shipState.shipType == 1) {
|
||||
if (auto b = menuManager.uiManager.findButton("shootButton")) b->state = ButtonState::Disabled;
|
||||
if (auto b = menuManager.uiManager.findButton("shootButton2")) b->state = ButtonState::Disabled;
|
||||
} else {
|
||||
if (auto b = menuManager.uiManager.findButton("shootButton")) b->state = ButtonState::Normal;
|
||||
if (auto b = menuManager.uiManager.findButton("shootButton2")) b->state = ButtonState::Normal;
|
||||
}
|
||||
} else if (SDL_GetTicks() - connectingStartTicks > CONNECTING_TIMEOUT_MS) {
|
||||
menuManager.notifyConnectionFailed();
|
||||
}
|
||||
}
|
||||
#ifdef NETWORK
|
||||
auto* wsBase = dynamic_cast<ZL::WebSocketClientBase*>(networkClient.get());
|
||||
if (wsBase) {
|
||||
@ -487,67 +524,58 @@ namespace ZL
|
||||
}
|
||||
mainThreadHandler.processMainThreadTasks();
|
||||
|
||||
if (spaceGameStarted) {
|
||||
if (menuManager.shouldRenderSpace()) {
|
||||
space.update();
|
||||
}
|
||||
}
|
||||
|
||||
void Game::handleDown(int mx, int my)
|
||||
void Game::handleDown(int64_t fingerId, int mx, int my)
|
||||
{
|
||||
int uiX = mx;
|
||||
int uiY = Environment::projectionHeight - my;
|
||||
|
||||
menuManager.uiManager.onMouseDown(uiX, uiY);
|
||||
menuManager.uiManager.onTouchDown(fingerId, uiX, uiY);
|
||||
|
||||
bool uiHandled = false;
|
||||
|
||||
for (const auto& button : menuManager.uiManager.findButton("") ? std::vector<std::shared_ptr<UiButton>>{} : std::vector<std::shared_ptr<UiButton>>{}) {
|
||||
(void)button;
|
||||
}
|
||||
|
||||
auto pressedSlider = [&]() -> std::shared_ptr<UiSlider> {
|
||||
for (const auto& slider : menuManager.uiManager.findSlider("") ? std::vector<std::shared_ptr<UiSlider>>{} : std::vector<std::shared_ptr<UiSlider>>{}) {
|
||||
(void)slider;
|
||||
}
|
||||
return nullptr;
|
||||
}();
|
||||
|
||||
if (!menuManager.uiManager.isUiInteraction()) {
|
||||
if (spaceGameStarted) {
|
||||
if (!menuManager.uiManager.isUiInteractionForFinger(fingerId)) {
|
||||
if (menuManager.shouldRenderSpace()) {
|
||||
space.handleDown(mx, my);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Game::handleUp(int mx, int my)
|
||||
void Game::handleUp(int64_t fingerId, int mx, int my)
|
||||
{
|
||||
int uiX = mx;
|
||||
int uiY = Environment::projectionHeight - my;
|
||||
|
||||
menuManager.uiManager.onMouseUp(uiX, uiY);
|
||||
// Check BEFORE onTouchUp erases the finger from the map.
|
||||
// If this finger started on a UI element, don't notify space —
|
||||
// otherwise space would think the ship-control finger was released.
|
||||
bool wasUiInteraction = menuManager.uiManager.isUiInteractionForFinger(fingerId);
|
||||
menuManager.uiManager.onTouchUp(fingerId, uiX, uiY);
|
||||
|
||||
if (!menuManager.uiManager.isUiInteraction()) {
|
||||
if (spaceGameStarted) {
|
||||
if (!wasUiInteraction) {
|
||||
if (menuManager.shouldRenderSpace()) {
|
||||
space.handleUp(mx, my);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Game::handleMotion(int mx, int my)
|
||||
void Game::handleMotion(int64_t fingerId, int mx, int my)
|
||||
{
|
||||
int uiX = mx;
|
||||
int uiY = Environment::projectionHeight - my;
|
||||
|
||||
menuManager.uiManager.onMouseMove(uiX, uiY);
|
||||
// Check before onTouchMove so the "started on UI" state is preserved
|
||||
// regardless of what onTouchMove does internally.
|
||||
bool wasUiInteraction = menuManager.uiManager.isUiInteractionForFinger(fingerId);
|
||||
menuManager.uiManager.onTouchMove(fingerId, uiX, uiY);
|
||||
|
||||
if (!menuManager.uiManager.isUiInteraction()) {
|
||||
if (spaceGameStarted) {
|
||||
if (!wasUiInteraction) {
|
||||
if (menuManager.shouldRenderSpace()) {
|
||||
space.handleMotion(mx, my);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
11
src/Game.h
11
src/Game.h
@ -13,6 +13,7 @@
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <cstdint>
|
||||
#include <render/TextRenderer.h>
|
||||
#include "MenuManager.h"
|
||||
#include "Space.h"
|
||||
@ -50,9 +51,9 @@ namespace ZL {
|
||||
void drawUI();
|
||||
void drawUnderMainMenu();
|
||||
void drawLoading();
|
||||
void handleDown(int mx, int my);
|
||||
void handleUp(int mx, int my);
|
||||
void handleMotion(int mx, int my);
|
||||
void handleDown(int64_t fingerId, int mx, int my);
|
||||
void handleUp(int64_t fingerId, int mx, int my);
|
||||
void handleMotion(int64_t fingerId, int mx, int my);
|
||||
|
||||
#ifdef EMSCRIPTEN
|
||||
static Game* s_instance;
|
||||
@ -65,14 +66,14 @@ namespace ZL {
|
||||
|
||||
int64_t newTickCount;
|
||||
int64_t lastTickCount;
|
||||
uint32_t connectingStartTicks = 0;
|
||||
static constexpr uint32_t CONNECTING_TIMEOUT_MS = 10000;
|
||||
|
||||
static const size_t CONST_TIMER_INTERVAL = 10;
|
||||
static const size_t CONST_MAX_TIME_INTERVAL = 1000;
|
||||
|
||||
MenuManager menuManager;
|
||||
Space space;
|
||||
|
||||
int spaceGameStarted = 0;
|
||||
};
|
||||
|
||||
|
||||
|
||||
9
src/GameConstants.cpp
Normal file
9
src/GameConstants.cpp
Normal file
@ -0,0 +1,9 @@
|
||||
#include "GameConstants.h"
|
||||
|
||||
namespace ZL
|
||||
{
|
||||
const std::string defaultShaderName = "default";
|
||||
const std::string envShaderName = "env";
|
||||
const std::string textureUniformName = "Texture";
|
||||
}
|
||||
|
||||
11
src/GameConstants.h
Normal file
11
src/GameConstants.h
Normal file
@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
#include "render/Renderer.h"
|
||||
|
||||
namespace ZL
|
||||
{
|
||||
extern const std::string defaultShaderName;
|
||||
extern const std::string envShaderName;
|
||||
|
||||
extern const std::string textureUniformName;
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
#include "MenuManager.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace ZL {
|
||||
|
||||
@ -10,278 +10,225 @@ namespace ZL {
|
||||
|
||||
void MenuManager::setupMenu()
|
||||
{
|
||||
mainMenuRoot = loadUiFromFile("resources/config/main_menu.json", renderer, CONST_ZIP_FILE);
|
||||
shipSelectionRoot = loadUiFromFile("resources/config/ship_selection_menu.json", renderer, CONST_ZIP_FILE);
|
||||
connectingRoot = loadUiFromFile("resources/config/connecting.json", renderer, CONST_ZIP_FILE);
|
||||
connectionFailedRoot= loadUiFromFile("resources/config/connection_failed.json", renderer, CONST_ZIP_FILE);
|
||||
gameplayRoot = loadUiFromFile("resources/config/ui.json", renderer, CONST_ZIP_FILE);
|
||||
gameOverRoot = loadUiFromFile("resources/config/game_over.json", renderer, CONST_ZIP_FILE);
|
||||
connectionLostRoot = loadUiFromFile("resources/config/connection_lost.json", renderer, CONST_ZIP_FILE);
|
||||
|
||||
uiManager.loadFromFile("resources/config/main_menu.json", renderer, CONST_ZIP_FILE);
|
||||
enterMainMenu();
|
||||
}
|
||||
|
||||
uiSavedRoot = loadUiFromFile("resources/config/ui.json", renderer, CONST_ZIP_FILE);
|
||||
|
||||
settingsSavedRoot = loadUiFromFile("resources/config/settings.json", renderer, CONST_ZIP_FILE);
|
||||
bool MenuManager::shouldRenderSpace() const
|
||||
{
|
||||
return state == GameState::Gameplay
|
||||
|| state == GameState::GameOver
|
||||
|| state == GameState::ConnectionLost;
|
||||
}
|
||||
|
||||
multiplayerSavedRoot = loadUiFromFile("resources/config/multiplayer_menu.json", renderer, CONST_ZIP_FILE);
|
||||
|
||||
gameOverSavedRoot = loadUiFromFile("resources/config/game_over.json", renderer, CONST_ZIP_FILE);
|
||||
|
||||
auto shipSelectionRoot = loadUiFromFile("resources/config/ship_selection_menu.json", renderer, CONST_ZIP_FILE);
|
||||
std::function<void()> loadGameplayUI;
|
||||
loadGameplayUI = [this]() {
|
||||
uiManager.replaceRoot(uiSavedRoot);
|
||||
// ── State: MainMenu ──────────────────────────────────────────────────────
|
||||
|
||||
auto velocityTv = uiManager.findTextView("velocityText");
|
||||
if (velocityTv) {
|
||||
velocityTv->rect.x = 10.0f;
|
||||
velocityTv->rect.y = static_cast<float>(Environment::height) - velocityTv->rect.h - 10.0f;
|
||||
}
|
||||
else {
|
||||
std::cerr << "Failed to find velocityText in UI" << std::endl;
|
||||
}
|
||||
void MenuManager::enterMainMenu()
|
||||
{
|
||||
state = GameState::MainMenu;
|
||||
uiManager.replaceRoot(mainMenuRoot);
|
||||
|
||||
uiManager.startAnimationOnNode("backgroundNode", "bgScroll");
|
||||
static bool isExitButtonAnimating = false;
|
||||
uiManager.setAnimationCallback("settingsButton", "buttonsExit", [this]() {
|
||||
std::cerr << "Settings button animation finished -> ??????? ? ?????????" << std::endl;
|
||||
if (uiManager.pushMenuFromSavedRoot(settingsSavedRoot)) {
|
||||
uiManager.setButtonCallback("Opt1", [this](const std::string& n) {
|
||||
std::cerr << "Opt1 pressed: " << n << std::endl;
|
||||
});
|
||||
uiManager.setButtonCallback("Opt2", [this](const std::string& n) {
|
||||
std::cerr << "Opt2 pressed: " << n << std::endl;
|
||||
});
|
||||
uiManager.setButtonCallback("backButton", [this](const std::string& n) {
|
||||
uiManager.stopAllAnimations();
|
||||
uiManager.popMenu();
|
||||
});
|
||||
}
|
||||
else {
|
||||
std::cerr << "Failed to open settings menu after animations" << std::endl;
|
||||
}
|
||||
});
|
||||
if (onMainMenuEntered) onMainMenuEntered();
|
||||
|
||||
uiManager.setAnimationCallback("exitButton", "bgScroll", [this]() {
|
||||
std::cerr << "Exit button bgScroll animation finished" << std::endl;
|
||||
g_exitBgAnimating = false;
|
||||
});
|
||||
|
||||
uiManager.setButtonCallback("playButton", [this](const std::string& name) {
|
||||
std::cerr << "Play button pressed: " << name << std::endl;
|
||||
});
|
||||
|
||||
uiManager.setButtonCallback("settingsButton", [this](const std::string& name) {
|
||||
std::cerr << "Settings button pressed: " << name << std::endl;
|
||||
uiManager.startAnimationOnNode("playButton", "buttonsExit");
|
||||
uiManager.startAnimationOnNode("settingsButton", "buttonsExit");
|
||||
uiManager.startAnimationOnNode("exitButton", "buttonsExit");
|
||||
});
|
||||
|
||||
uiManager.setButtonCallback("exitButton", [this](const std::string& name) {
|
||||
std::cerr << "Exit button pressed: " << name << std::endl;
|
||||
|
||||
if (!g_exitBgAnimating) {
|
||||
std::cerr << "start repeat anim bgScroll on exitButton" << std::endl;
|
||||
g_exitBgAnimating = true;
|
||||
uiManager.startAnimationOnNode("exitButton", "bgScroll");
|
||||
}
|
||||
else {
|
||||
std::cerr << "stop repeat anim bgScroll on exitButton" << std::endl;
|
||||
g_exitBgAnimating = false;
|
||||
uiManager.stopAnimationOnNode("exitButton", "bgScroll");
|
||||
|
||||
auto exitButton = uiManager.findButton("exitButton");
|
||||
if (exitButton) {
|
||||
exitButton->animOffsetX = 0.0f;
|
||||
exitButton->animOffsetY = 0.0f;
|
||||
exitButton->animScaleX = 1.0f;
|
||||
exitButton->animScaleY = 1.0f;
|
||||
exitButton->buildMesh();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
uiManager.setButtonCallback("shootButton", [this](const std::string& name) {
|
||||
onFirePressed();
|
||||
});
|
||||
uiManager.setButtonCallback("shootButton2", [this](const std::string& name) {
|
||||
onFirePressed();
|
||||
});
|
||||
uiManager.setSliderCallback("velocitySlider", [this](const std::string& name, float value) {
|
||||
|
||||
int newVel = roundf(value * 10);
|
||||
if (newVel > 2)
|
||||
{
|
||||
newVel = 2;
|
||||
}
|
||||
|
||||
if (newVel != Environment::shipState.selectedVelocity) {
|
||||
onVelocityChanged(newVel);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
uiManager.setButtonCallback("singleButton", [this, shipSelectionRoot, loadGameplayUI](const std::string& name) {
|
||||
std::cerr << "Single button pressed: " << name << " -> open ship selection UI\n";
|
||||
if (!shipSelectionRoot) {
|
||||
std::cerr << "Failed to load ship selection UI\n";
|
||||
return;
|
||||
}
|
||||
if (uiManager.pushMenuFromSavedRoot(shipSelectionRoot)) {
|
||||
uiManager.setButtonCallback("spaceshipButton", [this, loadGameplayUI](const std::string& btnName) {
|
||||
std::string nick = uiManager.getTextFieldValue("nicknameInput");
|
||||
if (nick.empty()) nick = "Player";
|
||||
int shipType = 0;
|
||||
uiManager.popMenu();
|
||||
loadGameplayUI();
|
||||
if (onSingleplayerPressed) onSingleplayerPressed(nick, shipType);
|
||||
});
|
||||
|
||||
uiManager.setButtonCallback("cargoshipButton", [this, loadGameplayUI](const std::string& btnName) {
|
||||
std::string nick = uiManager.getTextFieldValue("nicknameInput");
|
||||
if (nick.empty()) nick = "Player";
|
||||
int shipType = 1;
|
||||
uiManager.popMenu();
|
||||
loadGameplayUI();
|
||||
if (onSingleplayerPressed) onSingleplayerPressed(nick, shipType);
|
||||
});
|
||||
|
||||
uiManager.setButtonCallback("backButton", [this](const std::string& btnName) {
|
||||
uiManager.popMenu();
|
||||
});
|
||||
}
|
||||
else {
|
||||
std::cerr << "Failed to push ship selection menu\n";
|
||||
}
|
||||
uiManager.setButtonCallback("singleButton", [this](const std::string&) {
|
||||
enterShipSelectionSingle();
|
||||
});
|
||||
|
||||
uiManager.setButtonCallback("multiplayerButton", [this, shipSelectionRoot, loadGameplayUI](const std::string& name) {
|
||||
std::cerr << "Multiplayer button pressed: " << name << " -> open ship selection UI\n";
|
||||
if (!shipSelectionRoot) {
|
||||
std::cerr << "Failed to load ship selection UI\n";
|
||||
return;
|
||||
}
|
||||
if (uiManager.pushMenuFromSavedRoot(shipSelectionRoot)) {
|
||||
uiManager.setButtonCallback("spaceshipButton", [this, loadGameplayUI](const std::string& btnName) {
|
||||
std::string nick = uiManager.getTextFieldValue("nicknameInput");
|
||||
if (nick.empty()) nick = "Player";
|
||||
int shipType = 0;
|
||||
uiManager.popMenu();
|
||||
loadGameplayUI();
|
||||
if (onMultiplayerPressed) onMultiplayerPressed(nick, shipType);
|
||||
});
|
||||
uiManager.setButtonCallback("multiplayerButton", [this](const std::string&) {
|
||||
enterShipSelectionMulti();
|
||||
});
|
||||
}
|
||||
|
||||
uiManager.setButtonCallback("cargoshipButton", [this, loadGameplayUI](const std::string& btnName) {
|
||||
std::string nick = uiManager.getTextFieldValue("nicknameInput");
|
||||
if (nick.empty()) nick = "Player";
|
||||
int shipType = 1;
|
||||
uiManager.popMenu();
|
||||
loadGameplayUI();
|
||||
if (onMultiplayerPressed) onMultiplayerPressed(nick, shipType);
|
||||
});
|
||||
// ── State: ShipSelectionSingle ───────────────────────────────────────────
|
||||
|
||||
uiManager.setButtonCallback("backButton", [this](const std::string& btnName) {
|
||||
uiManager.popMenu();
|
||||
});
|
||||
}
|
||||
else {
|
||||
std::cerr << "Failed to push ship selection menu\n";
|
||||
}
|
||||
void MenuManager::enterShipSelectionSingle()
|
||||
{
|
||||
state = GameState::ShipSelectionSingle;
|
||||
uiManager.replaceRoot(shipSelectionRoot);
|
||||
|
||||
uiManager.setButtonCallback("spaceshipButton", [this](const std::string&) {
|
||||
std::string nick = uiManager.getTextFieldValue("nicknameInput");
|
||||
if (nick.empty()) nick = "Player";
|
||||
enterGameplay();
|
||||
if (onSingleplayerPressed) onSingleplayerPressed(nick, 0);
|
||||
});
|
||||
|
||||
/*uiManager.setButtonCallback("multiplayerButton2", [this, shipSelectionRoot, loadGameplayUI](const std::string& name) {
|
||||
std::cerr << "Multiplayer button pressed → opening multiplayer menu\n";
|
||||
uiManager.setButtonCallback("cargoshipButton", [this](const std::string&) {
|
||||
std::string nick = uiManager.getTextFieldValue("nicknameInput");
|
||||
if (nick.empty()) nick = "Player";
|
||||
enterGameplay();
|
||||
if (onSingleplayerPressed) onSingleplayerPressed(nick, 1);
|
||||
});
|
||||
|
||||
uiManager.startAnimationOnNode("playButton", "buttonsExit");
|
||||
uiManager.startAnimationOnNode("settingsButton", "buttonsExit");
|
||||
uiManager.startAnimationOnNode("multiplayerButton", "buttonsExit");
|
||||
uiManager.startAnimationOnNode("exitButton", "buttonsExit");
|
||||
uiManager.setButtonCallback("backButton", [this](const std::string&) {
|
||||
enterMainMenu();
|
||||
});
|
||||
}
|
||||
|
||||
if (uiManager.pushMenuFromSavedRoot(multiplayerSavedRoot)) {
|
||||
// ── State: ShipSelectionMulti ─────────────────────────────────────────────
|
||||
|
||||
uiManager.setButtonCallback("connectButton", [this](const std::string& buttonName) {
|
||||
std::string serverAddress = uiManager.getTextFieldValue("serverInputField");
|
||||
void MenuManager::enterShipSelectionMulti()
|
||||
{
|
||||
state = GameState::ShipSelectionMulti;
|
||||
uiManager.replaceRoot(shipSelectionRoot);
|
||||
|
||||
if (serverAddress.empty()) {
|
||||
uiManager.setText("statusText", "Please enter server address");
|
||||
return;
|
||||
}
|
||||
uiManager.setButtonCallback("spaceshipButton", [this](const std::string&) {
|
||||
std::string nick = uiManager.getTextFieldValue("nicknameInput");
|
||||
if (nick.empty()) nick = "Player";
|
||||
pendingMultiNick = nick;
|
||||
pendingMultiShipType = 0;
|
||||
enterConnecting();
|
||||
if (onMultiplayerPressed) onMultiplayerPressed(nick, 0);
|
||||
});
|
||||
|
||||
uiManager.setText("statusText", "Connecting to " + serverAddress + "...");
|
||||
std::cerr << "Connecting to server: " << serverAddress << std::endl;
|
||||
uiManager.setButtonCallback("cargoshipButton", [this](const std::string&) {
|
||||
std::string nick = uiManager.getTextFieldValue("nicknameInput");
|
||||
if (nick.empty()) nick = "Player";
|
||||
pendingMultiNick = nick;
|
||||
pendingMultiShipType = 1;
|
||||
enterConnecting();
|
||||
if (onMultiplayerPressed) onMultiplayerPressed(nick, 1);
|
||||
});
|
||||
|
||||
});
|
||||
uiManager.setButtonCallback("backButton", [this](const std::string&) {
|
||||
enterMainMenu();
|
||||
});
|
||||
}
|
||||
|
||||
uiManager.setButtonCallback("backButton", [this](const std::string& buttonName) {
|
||||
uiManager.popMenu();
|
||||
});
|
||||
// ── State: Connecting ────────────────────────────────────────────────────
|
||||
|
||||
uiManager.setTextFieldCallback("serverInputField",
|
||||
[this](const std::string& fieldName, const std::string& newText) {
|
||||
std::cout << "Server input field changed to: " << newText << std::endl;
|
||||
});
|
||||
void MenuManager::enterConnecting()
|
||||
{
|
||||
state = GameState::Connecting;
|
||||
uiManager.replaceRoot(connectingRoot);
|
||||
// No interactive elements — just a static "Connecting..." image
|
||||
}
|
||||
|
||||
std::cerr << "Multiplayer menu loaded successfully\n";
|
||||
}
|
||||
else {
|
||||
std::cerr << "Failed to load multiplayer menu\n";
|
||||
}
|
||||
std::cerr << "Single button pressed: " << name << " -> open ship selection UI\n";
|
||||
if (!shipSelectionRoot) {
|
||||
std::cerr << "Failed to load ship selection UI\n";
|
||||
return;
|
||||
}
|
||||
if (uiManager.pushMenuFromSavedRoot(shipSelectionRoot)) {
|
||||
uiManager.setButtonCallback("spaceshipButton", [this, loadGameplayUI](const std::string& btnName) {
|
||||
std::string nick = uiManager.getTextFieldValue("nicknameInput");
|
||||
if (nick.empty()) nick = "Player";
|
||||
int shipType = 0;
|
||||
uiManager.popMenu();
|
||||
loadGameplayUI();
|
||||
if (onSingleplayerPressed) onSingleplayerPressed(nick, shipType);
|
||||
});
|
||||
// ── State: ConnectionFailed ───────────────────────────────────────────────
|
||||
|
||||
uiManager.setButtonCallback("cargoshipButton", [this, loadGameplayUI](const std::string& btnName) {
|
||||
std::string nick = uiManager.getTextFieldValue("nicknameInput");
|
||||
if (nick.empty()) nick = "Player";
|
||||
int shipType = 1;
|
||||
uiManager.popMenu();
|
||||
loadGameplayUI();
|
||||
if (onSingleplayerPressed) onSingleplayerPressed(nick, shipType);
|
||||
});
|
||||
void MenuManager::enterConnectionFailed()
|
||||
{
|
||||
state = GameState::ConnectionFailed;
|
||||
uiManager.replaceRoot(connectionFailedRoot);
|
||||
|
||||
uiManager.setButtonCallback("backButton", [this](const std::string& btnName) {
|
||||
uiManager.popMenu();
|
||||
});
|
||||
}
|
||||
else {
|
||||
std::cerr << "Failed to push ship selection menu\n";
|
||||
uiManager.setButtonCallback("connectionFailedReconnectButton", [this](const std::string&) {
|
||||
enterConnecting();
|
||||
if (onMultiplayerPressed) onMultiplayerPressed(pendingMultiNick, pendingMultiShipType);
|
||||
});
|
||||
|
||||
uiManager.setButtonCallback("connectionFailedGoBack", [this](const std::string&) {
|
||||
enterMainMenu();
|
||||
});
|
||||
}
|
||||
|
||||
// ── State: Gameplay ──────────────────────────────────────────────────────
|
||||
|
||||
void MenuManager::enterGameplay()
|
||||
{
|
||||
state = GameState::Gameplay;
|
||||
uiManager.replaceRoot(gameplayRoot);
|
||||
|
||||
auto velocityTv = uiManager.findTextView("velocityText");
|
||||
if (velocityTv) {
|
||||
velocityTv->rect.x = 10.0f;
|
||||
velocityTv->rect.y = static_cast<float>(Environment::height) - velocityTv->rect.h - 10.0f;
|
||||
}
|
||||
|
||||
uiManager.startAnimationOnNode("backgroundNode", "bgScroll");
|
||||
|
||||
uiManager.setButtonPressCallback("shootButton", [this](const std::string&) {
|
||||
if (onFirePressed) onFirePressed();
|
||||
});
|
||||
uiManager.setButtonPressCallback("shootButton2", [this](const std::string&) {
|
||||
if (onFirePressed) onFirePressed();
|
||||
});
|
||||
uiManager.setButtonPressCallback("plusButton", [this](const std::string&) {
|
||||
int newVel = Environment::shipState.selectedVelocity + 1;
|
||||
if (newVel > 4) newVel = 4;
|
||||
if (onVelocityChanged) onVelocityChanged(newVel);
|
||||
});
|
||||
uiManager.setButtonPressCallback("minusButton", [this](const std::string&) {
|
||||
int newVel = Environment::shipState.selectedVelocity - 1;
|
||||
if (newVel < 0) newVel = 0;
|
||||
if (onVelocityChanged) onVelocityChanged(newVel);
|
||||
});
|
||||
uiManager.setSliderCallback("velocitySlider", [this](const std::string&, float value) {
|
||||
int newVel = static_cast<int>(roundf(value * 10));
|
||||
if (newVel > 2) newVel = 2;
|
||||
if (newVel != Environment::shipState.selectedVelocity) {
|
||||
if (onVelocityChanged) onVelocityChanged(newVel);
|
||||
}
|
||||
});
|
||||
uiManager.setButtonCallback("exitButton", [](const std::string& name) {
|
||||
std::cerr << "Exit from main menu pressed: " << name << " -> exiting\n";
|
||||
Environment::exitGameLoop = true;
|
||||
});*/
|
||||
}
|
||||
|
||||
// ── State: GameOver ──────────────────────────────────────────────────────
|
||||
|
||||
void MenuManager::enterGameOver(int score)
|
||||
{
|
||||
state = GameState::GameOver;
|
||||
uiManager.replaceRoot(gameOverRoot);
|
||||
|
||||
uiManager.setText("scoreText", "Score: " + std::to_string(score));
|
||||
|
||||
uiManager.setButtonCallback("restartButton", [this](const std::string&) {
|
||||
if (onRestartPressed) onRestartPressed();
|
||||
enterGameplay();
|
||||
});
|
||||
uiManager.setButtonCallback("gameOverExitButton", [this](const std::string&) {
|
||||
enterMainMenu();
|
||||
});
|
||||
}
|
||||
|
||||
// ── State: ConnectionLost ─────────────────────────────────────────────────
|
||||
|
||||
void MenuManager::enterConnectionLost()
|
||||
{
|
||||
state = GameState::ConnectionLost;
|
||||
uiManager.replaceRoot(connectionLostRoot);
|
||||
|
||||
uiManager.setButtonCallback("reconnectButton", [this](const std::string&) {
|
||||
// TODO: reconnect logic
|
||||
});
|
||||
uiManager.setButtonCallback("exitServerButton", [this](const std::string&) {
|
||||
enterMainMenu();
|
||||
});
|
||||
}
|
||||
|
||||
// ── Public event API ──────────────────────────────────────────────────────
|
||||
|
||||
void MenuManager::notifyConnected()
|
||||
{
|
||||
if (state == GameState::Connecting) {
|
||||
enterGameplay();
|
||||
}
|
||||
}
|
||||
|
||||
void MenuManager::notifyConnectionFailed()
|
||||
{
|
||||
if (state == GameState::Connecting) {
|
||||
enterConnectionFailed();
|
||||
}
|
||||
}
|
||||
|
||||
void MenuManager::showGameOver(int score)
|
||||
{
|
||||
if (!uiGameOverShown) {
|
||||
if (uiManager.pushMenuFromSavedRoot(gameOverSavedRoot)) {
|
||||
uiManager.setText("scoreText", std::string("Score: ") + std::to_string(score));
|
||||
|
||||
uiManager.setButtonCallback("restartButton", [this](const std::string& name) {
|
||||
uiManager.setText("scoreText", "");
|
||||
uiGameOverShown = false;
|
||||
uiManager.popMenu();
|
||||
if (onRestartPressed) onRestartPressed();
|
||||
});
|
||||
|
||||
uiManager.setButtonCallback("gameOverExitButton", [this](const std::string& name) {
|
||||
Environment::exitGameLoop = true;
|
||||
});
|
||||
|
||||
uiGameOverShown = true;
|
||||
}
|
||||
else {
|
||||
std::cerr << "Failed to load game_over.json\n";
|
||||
}
|
||||
if (state == GameState::Gameplay) {
|
||||
enterGameOver(score);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MenuManager::showConnectionLost()
|
||||
{
|
||||
if (state == GameState::Gameplay) {
|
||||
enterConnectionLost();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ZL
|
||||
@ -7,37 +7,71 @@
|
||||
namespace ZL {
|
||||
|
||||
extern const char* CONST_ZIP_FILE;
|
||||
//extern bool g_exitBgAnimating;
|
||||
|
||||
class MenuManager
|
||||
{
|
||||
enum class GameState {
|
||||
MainMenu,
|
||||
ShipSelectionSingle,
|
||||
ShipSelectionMulti,
|
||||
Connecting,
|
||||
ConnectionFailed,
|
||||
Gameplay,
|
||||
GameOver,
|
||||
ConnectionLost
|
||||
};
|
||||
|
||||
class MenuManager {
|
||||
protected:
|
||||
Renderer& renderer;
|
||||
std::shared_ptr<UiNode> uiSavedRoot;
|
||||
std::shared_ptr<UiNode> gameOverSavedRoot;
|
||||
std::shared_ptr<UiNode> settingsSavedRoot;
|
||||
std::shared_ptr<UiNode> multiplayerSavedRoot;
|
||||
|
||||
// Pre-loaded UI roots (loaded once in setupMenu)
|
||||
std::shared_ptr<UiNode> mainMenuRoot;
|
||||
std::shared_ptr<UiNode> shipSelectionRoot;
|
||||
std::shared_ptr<UiNode> connectingRoot;
|
||||
std::shared_ptr<UiNode> connectionFailedRoot;
|
||||
std::shared_ptr<UiNode> gameplayRoot;
|
||||
std::shared_ptr<UiNode> gameOverRoot;
|
||||
std::shared_ptr<UiNode> connectionLostRoot;
|
||||
|
||||
// Stored for multiplayer retry
|
||||
std::string pendingMultiNick;
|
||||
int pendingMultiShipType = 0;
|
||||
|
||||
GameState state = GameState::MainMenu;
|
||||
|
||||
// State transition methods
|
||||
void enterMainMenu();
|
||||
void enterShipSelectionSingle();
|
||||
void enterShipSelectionMulti();
|
||||
void enterConnecting();
|
||||
void enterConnectionFailed();
|
||||
void enterGameplay();
|
||||
void enterGameOver(int score);
|
||||
void enterConnectionLost();
|
||||
|
||||
public:
|
||||
bool uiGameOverShown = false;
|
||||
bool g_exitBgAnimating = false;
|
||||
|
||||
UiManager uiManager;
|
||||
|
||||
MenuManager(Renderer& iRenderer);
|
||||
|
||||
void setupMenu();
|
||||
|
||||
//void showGameOver();
|
||||
void showGameOver(int score);
|
||||
// Returns true for states where Space should render and run (Gameplay, GameOver, ConnectionLost)
|
||||
bool shouldRenderSpace() const;
|
||||
GameState getState() const { return state; }
|
||||
|
||||
// Called by game events
|
||||
void showGameOver(int score);
|
||||
void showConnectionLost();
|
||||
void notifyConnected();
|
||||
void notifyConnectionFailed();
|
||||
|
||||
// Callbacks set by Game/Space
|
||||
std::function<void()> onMainMenuEntered;
|
||||
std::function<void()> onRestartPressed;
|
||||
std::function<void(float)> onVelocityChanged;
|
||||
std::function<void()> onFirePressed;
|
||||
|
||||
std::function<void(const std::string&, int)> onSingleplayerPressed;
|
||||
std::function<void(const std::string&, int)> onMultiplayerPressed;
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace ZL
|
||||
@ -1,4 +1,4 @@
|
||||
#include "Projectile.h"
|
||||
#include "Projectile.h"
|
||||
|
||||
namespace ZL {
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include "render/Renderer.h"
|
||||
#include "render/TextureManager.h"
|
||||
|
||||
258
src/Space.cpp
258
src/Space.cpp
@ -1,4 +1,4 @@
|
||||
#include "Space.h"
|
||||
#include "Space.h"
|
||||
#include "AnimatedModel.h"
|
||||
#include "BoneAnimatedModel.h"
|
||||
#include "planet/PlanetData.h"
|
||||
@ -25,6 +25,8 @@
|
||||
#include "network/LocalClient.h"
|
||||
#endif
|
||||
|
||||
#include "GameConstants.h"
|
||||
|
||||
namespace ZL
|
||||
{
|
||||
|
||||
@ -252,20 +254,26 @@ namespace ZL
|
||||
Space::~Space() {
|
||||
}
|
||||
|
||||
void Space::resetPlayerState()
|
||||
{
|
||||
shipAlive = true;
|
||||
gameOver = false;
|
||||
showExplosion = false;
|
||||
explosionEmitter.setEmissionPoints(std::vector<Vector3f>());
|
||||
Environment::shipState.position = Vector3f{ 0, 0, 45000.f };
|
||||
Environment::shipState.velocity = 0.0f;
|
||||
Environment::shipState.rotation = Eigen::Matrix3f::Identity();
|
||||
Environment::inverseShipMatrix = Eigen::Matrix3f::Identity();
|
||||
Environment::zoom = DEFAULT_ZOOM;
|
||||
Environment::tapDownHold = false;
|
||||
playerScore = 0;
|
||||
}
|
||||
|
||||
void Space::setup() {
|
||||
|
||||
|
||||
menuManager.onRestartPressed = [this]() {
|
||||
this->shipAlive = true;
|
||||
this->gameOver = false;
|
||||
this->showExplosion = false;
|
||||
this->explosionEmitter.setEmissionPoints(std::vector<Vector3f>());
|
||||
Environment::shipState.position = Vector3f{ 0, 0, 45000.f };
|
||||
Environment::shipState.velocity = 0.0f;
|
||||
Environment::shipState.rotation = Eigen::Matrix3f::Identity();
|
||||
Environment::inverseShipMatrix = Eigen::Matrix3f::Identity();
|
||||
Environment::zoom = DEFAULT_ZOOM;
|
||||
Environment::tapDownHold = false;
|
||||
resetPlayerState();
|
||||
|
||||
if (networkClient) {
|
||||
try {
|
||||
@ -276,12 +284,25 @@ namespace ZL
|
||||
std::cerr << "Client: Failed to send RESPAWN\n";
|
||||
}
|
||||
}
|
||||
this->playerScore = 0;
|
||||
std::cerr << "Game restarted\n";
|
||||
};
|
||||
|
||||
menuManager.onVelocityChanged = [this](float newVelocity) {
|
||||
newShipVelocity = newVelocity;
|
||||
if (Environment::shipState.shipType == 0)
|
||||
{
|
||||
if (newVelocity > 2)
|
||||
{
|
||||
this->menuManager.uiManager.findButton("shootButton")->state = ButtonState::Disabled;
|
||||
this->menuManager.uiManager.findButton("shootButton2")->state = ButtonState::Disabled;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
this->menuManager.uiManager.findButton("shootButton")->state = ButtonState::Normal;
|
||||
this->menuManager.uiManager.findButton("shootButton2")->state = ButtonState::Normal;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
menuManager.onFirePressed = [this]() {
|
||||
@ -289,10 +310,16 @@ namespace ZL
|
||||
};
|
||||
|
||||
bool cfgLoaded = sparkEmitter.loadFromJsonFile("resources/config/spark_config.json", renderer, CONST_ZIP_FILE);
|
||||
bool cfgLoaded2 = sparkEmitterCargo.loadFromJsonFile("resources/config/spark_config_cargo.json", renderer, CONST_ZIP_FILE);
|
||||
sparkEmitter.setIsActive(false);
|
||||
sparkEmitterCargo.setIsActive(false);
|
||||
|
||||
bool projCfgLoaded = projectileEmitter.loadFromJsonFile("resources/config/spark_projectile_config.json", renderer, CONST_ZIP_FILE);
|
||||
bool explosionCfgLoaded = explosionEmitter.loadFromJsonFile("resources/config/explosion_config.json", renderer, CONST_ZIP_FILE);
|
||||
explosionEmitter.setEmissionPoints(std::vector<Vector3f>());
|
||||
projectileEmitter.setEmissionPoints(std::vector<Vector3f>());
|
||||
//projectileEmitter.setEmissionPoints({ Vector3f{0,0,45000} });
|
||||
//projectileEmitter.setUseWorldSpace(true);
|
||||
|
||||
|
||||
cubemapTexture = std::make_shared<Texture>(
|
||||
std::array<TextureDataStruct, 6>{
|
||||
@ -315,7 +342,9 @@ namespace ZL
|
||||
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.Move(Vector3f{ 1.2, 0, -5 });
|
||||
spaceshipBase.Scale(0.4f);
|
||||
|
||||
spaceship.AssignFrom(spaceshipBase);
|
||||
spaceship.RefreshVBO();
|
||||
@ -394,17 +423,13 @@ namespace ZL
|
||||
|
||||
void Space::drawCubemap(float skyPercent)
|
||||
{
|
||||
static const std::string defaultShaderName = "default";
|
||||
static const std::string envShaderName = "env_sky";
|
||||
static const std::string vPositionName = "vPosition";
|
||||
static const std::string vTexCoordName = "vTexCoord";
|
||||
static const std::string textureUniformName = "Texture";
|
||||
static const std::string envSkyShaderName = "env_sky";
|
||||
static const std::string skyPercentUniformName = "skyPercent";
|
||||
|
||||
renderer.shaderManager.PushShader(envShaderName);
|
||||
renderer.shaderManager.PushShader(envSkyShaderName);
|
||||
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);
|
||||
@ -452,24 +477,14 @@ namespace ZL
|
||||
|
||||
renderer.PopMatrix();
|
||||
renderer.PopProjectionMatrix();
|
||||
renderer.DisableVertexAttribArray(vPositionName);
|
||||
|
||||
renderer.shaderManager.PopShader();
|
||||
CheckGlError();
|
||||
}
|
||||
|
||||
void Space::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),
|
||||
@ -490,69 +505,59 @@ namespace ZL
|
||||
glBindTexture(GL_TEXTURE_2D, spaceshipTexture->getTexID());
|
||||
renderer.DrawVertexRenderStruct(spaceship);
|
||||
}
|
||||
|
||||
|
||||
renderer.PushMatrix();
|
||||
renderer.RotateMatrix(Environment::inverseShipMatrix);
|
||||
renderer.TranslateMatrix(- /*0.5 * */ Environment::shipState.position);
|
||||
std::cout << "Ship pos draw: " << Environment::shipState.position.transpose() << "\n";
|
||||
if (Environment::shipState.shipType == 1) {
|
||||
sparkEmitterCargo.draw(renderer, Environment::zoom, Environment::width, Environment::height);
|
||||
}
|
||||
else {
|
||||
sparkEmitter.draw(renderer, Environment::zoom, Environment::width, Environment::height);
|
||||
}
|
||||
renderer.PopMatrix();
|
||||
}
|
||||
|
||||
renderer.PopMatrix();
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
renderer.shaderManager.PushShader("default");
|
||||
renderer.shaderManager.PushShader(defaultShaderName);
|
||||
renderer.RenderUniform1i(textureUniformName, 0);
|
||||
renderer.EnableVertexAttribArray(vPositionName);
|
||||
renderer.EnableVertexAttribArray(vTexCoordName);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
|
||||
renderer.PushMatrix();
|
||||
renderer.LoadIdentity();
|
||||
renderer.TranslateMatrix({ 0,0, -1.0f * Environment::zoom });
|
||||
renderer.RotateMatrix(Environment::inverseShipMatrix);
|
||||
renderer.TranslateMatrix(-Environment::shipState.position);
|
||||
for (const auto& p : projectiles) {
|
||||
if (p && p->isActive()) {
|
||||
p->draw(renderer);
|
||||
p->projectileEmitter.draw(renderer, Environment::zoom, Environment::width, Environment::height);
|
||||
}
|
||||
}
|
||||
renderer.PopMatrix();
|
||||
glDisable(GL_BLEND);
|
||||
|
||||
renderer.DisableVertexAttribArray(vPositionName);
|
||||
renderer.DisableVertexAttribArray(vTexCoordName);
|
||||
renderer.shaderManager.PopShader();
|
||||
|
||||
//projectileEmitter.draw(renderer, Environment::zoom, Environment::width, Environment::height);
|
||||
|
||||
if (shipAlive) {
|
||||
renderer.PushMatrix();
|
||||
renderer.TranslateMatrix({ 0, 0, 16 });
|
||||
renderer.TranslateMatrix({ 0, -6.f, 0 });
|
||||
sparkEmitter.draw(renderer, Environment::zoom, Environment::width, Environment::height);
|
||||
renderer.PopMatrix();
|
||||
}
|
||||
|
||||
if (showExplosion) {
|
||||
explosionEmitter.draw(renderer, Environment::zoom, Environment::width, Environment::height);
|
||||
explosionEmitter.draw(renderer, Environment::zoom, Environment::width, Environment::height, false);
|
||||
}
|
||||
|
||||
//glBindTexture(GL_TEXTURE_2D, basePlatformTexture->getTexID());
|
||||
//renderer.DrawVertexRenderStruct(basePlatform);
|
||||
|
||||
glDisable(GL_BLEND);
|
||||
renderer.PopMatrix();
|
||||
renderer.PopProjectionMatrix();
|
||||
renderer.DisableVertexAttribArray(vPositionName);
|
||||
renderer.DisableVertexAttribArray(vTexCoordName);
|
||||
|
||||
renderer.shaderManager.PopShader();
|
||||
CheckGlError();
|
||||
}
|
||||
|
||||
void Space::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);
|
||||
@ -571,31 +576,32 @@ namespace ZL
|
||||
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 Space::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);
|
||||
|
||||
// Готовим данные всех эмиттеров (CPU + VBO upload) до начала отрисовки,
|
||||
// чтобы draw() делал только GPU-вызовы без пауз между кораблём и частицами.
|
||||
sparkEmitter.prepareForDraw(true);
|
||||
sparkEmitterCargo.prepareForDraw(true);
|
||||
explosionEmitter.prepareForDraw(false);
|
||||
for (const auto& p : projectiles) {
|
||||
if (p && p->isActive()) {
|
||||
p->projectileEmitter.prepareForDraw(true);
|
||||
}
|
||||
}
|
||||
|
||||
CheckGlError();
|
||||
|
||||
float skyPercent = 0.0;
|
||||
@ -633,17 +639,9 @@ namespace ZL
|
||||
}
|
||||
|
||||
void Space::drawRemoteShips() {
|
||||
static const std::string defaultShaderName = "default";
|
||||
static const std::string vPositionName = "vPosition";
|
||||
static const std::string vTexCoordName = "vTexCoord";
|
||||
static const std::string textureUniformName = "Texture";
|
||||
|
||||
renderer.shaderManager.PushShader(defaultShaderName);
|
||||
renderer.RenderUniform1i(textureUniformName, 0);
|
||||
|
||||
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);
|
||||
@ -684,7 +682,6 @@ namespace ZL
|
||||
renderer.LoadIdentity();
|
||||
|
||||
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);
|
||||
|
||||
@ -706,8 +703,6 @@ namespace ZL
|
||||
}
|
||||
|
||||
renderer.PopProjectionMatrix();
|
||||
renderer.DisableVertexAttribArray(vPositionName);
|
||||
renderer.DisableVertexAttribArray(vTexCoordName);
|
||||
renderer.shaderManager.PopShader();
|
||||
|
||||
CheckGlError();
|
||||
@ -961,17 +956,11 @@ namespace ZL
|
||||
renderer.PushMatrix();
|
||||
renderer.LoadIdentity();
|
||||
|
||||
renderer.EnableVertexAttribArray("vPosition");
|
||||
renderer.EnableVertexAttribArray("vColor");
|
||||
|
||||
Eigen::Vector4f uColor(crosshairCfg.color.x(), crosshairCfg.color.y(), crosshairCfg.color.z(), crosshairCfg.alpha);
|
||||
renderer.RenderUniform4fv("uColor", uColor.data());
|
||||
|
||||
renderer.DrawVertexRenderStruct(crosshairMesh);
|
||||
|
||||
renderer.DisableVertexAttribArray("vPosition");
|
||||
renderer.DisableVertexAttribArray("vColor");
|
||||
|
||||
renderer.PopMatrix();
|
||||
renderer.PopProjectionMatrix();
|
||||
renderer.shaderManager.PopShader();
|
||||
@ -1300,7 +1289,6 @@ namespace ZL
|
||||
renderer.PushMatrix();
|
||||
renderer.LoadIdentity();
|
||||
|
||||
renderer.EnableVertexAttribArray("vPosition");
|
||||
renderer.RenderUniform4fv("uColor", enemyColor.data());
|
||||
|
||||
// рамки
|
||||
@ -1323,7 +1311,6 @@ namespace ZL
|
||||
drawLeadRing2D(lx, ly);
|
||||
}
|
||||
|
||||
renderer.DisableVertexAttribArray("vPosition");
|
||||
renderer.PopMatrix();
|
||||
renderer.PopProjectionMatrix();
|
||||
renderer.shaderManager.PopShader();
|
||||
@ -1409,7 +1396,6 @@ namespace ZL
|
||||
renderer.PushMatrix();
|
||||
renderer.LoadIdentity();
|
||||
|
||||
renderer.EnableVertexAttribArray("vPosition");
|
||||
renderer.RenderUniform4fv("uColor", enemyColor.data());
|
||||
|
||||
// стрелка
|
||||
@ -1427,7 +1413,6 @@ namespace ZL
|
||||
drawLeadRing2D(lx, ly);
|
||||
}
|
||||
|
||||
renderer.DisableVertexAttribArray("vPosition");
|
||||
renderer.PopMatrix();
|
||||
renderer.PopProjectionMatrix();
|
||||
renderer.shaderManager.PopShader();
|
||||
@ -1450,9 +1435,6 @@ namespace ZL
|
||||
|
||||
auto now_ms = newTickCount;
|
||||
|
||||
sparkEmitter.update(static_cast<float>(delta));
|
||||
planetObject.update(static_cast<float>(delta));
|
||||
|
||||
if (firePressed)
|
||||
{
|
||||
firePressed = false;
|
||||
@ -1563,6 +1545,41 @@ namespace ZL
|
||||
}
|
||||
|
||||
|
||||
//--------------
|
||||
|
||||
SparkEmitter* sparkEmitterPtr;
|
||||
|
||||
if (Environment::shipState.shipType == 1) {
|
||||
sparkEmitterPtr = &sparkEmitterCargo;
|
||||
static std::vector<Vector3f> emissionPoints = { Vector3f(0, 0, 0), Vector3f(0, 0, 0) };
|
||||
emissionPoints[0] = Environment::shipState.position + Environment::shipState.rotation * Vector3f{ 0.0, 2.8, -6.5 + 16.0 };
|
||||
emissionPoints[1] = Environment::shipState.position + Environment::shipState.rotation * Vector3f{ 0.0, 1.5, -6.5 + 16.0 };
|
||||
sparkEmitterPtr->setEmissionPoints(emissionPoints);
|
||||
}
|
||||
else
|
||||
{
|
||||
sparkEmitterPtr = &sparkEmitter;
|
||||
static std::vector<Vector3f> emissionPoints = { Vector3f(0, 0, 0), Vector3f(0, 0, 0) };
|
||||
emissionPoints[0] = Environment::shipState.position + Environment::shipState.rotation * Vector3f{ -0.9, 1.4 - 1.0, -8.5 + 16.0 };
|
||||
emissionPoints[1] = Environment::shipState.position + Environment::shipState.rotation * Vector3f{ 0.9, 1.4 - 1.0, -8.5 + 16.0 };
|
||||
sparkEmitterPtr->setEmissionPoints(emissionPoints);
|
||||
//sparkEmitterPtr->setEmissionPoints({ /*0.5* */Environment::shipState.position });
|
||||
//sparkEmitterPtr->setEmissionPoints({ Vector3f(0, 0, 0) });
|
||||
std::cout << "Ship pos empo: " << Environment::shipState.position.transpose() << "\n";
|
||||
}
|
||||
|
||||
if (Environment::shipState.velocity > 0.1f)
|
||||
{
|
||||
sparkEmitterPtr->setIsActive(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
sparkEmitterPtr->setIsActive(false);
|
||||
}
|
||||
|
||||
sparkEmitterPtr->update(static_cast<float>(delta));
|
||||
|
||||
|
||||
auto latestRemotePlayers = networkClient->getRemotePlayers();
|
||||
|
||||
std::chrono::system_clock::time_point nowRoundedWithDelay{ std::chrono::milliseconds(newTickCount - CLIENT_DELAY) };
|
||||
@ -1594,33 +1611,11 @@ namespace ZL
|
||||
for (const auto& p : projectiles) {
|
||||
if (p && p->isActive()) {
|
||||
Vector3f worldPos = p->getPosition();
|
||||
Vector3f rel = worldPos - Environment::shipState.position;
|
||||
Vector3f camPos = Environment::inverseShipMatrix * rel;
|
||||
p->projectileEmitter.setEmissionPoints({ camPos });
|
||||
p->projectileEmitter.emit();
|
||||
p->projectileEmitter.resetEmissionPoints({ worldPos });
|
||||
p->projectileEmitter.update(static_cast<float>(delta));
|
||||
}
|
||||
}
|
||||
/*
|
||||
if (!projCameraPoints.empty()) {
|
||||
projectileEmitter.setEmissionPoints(projCameraPoints);
|
||||
projectileEmitter.emit();
|
||||
}
|
||||
else {
|
||||
projectileEmitter.setEmissionPoints(std::vector<Vector3f>());
|
||||
}*/
|
||||
|
||||
std::vector<Vector3f> shipCameraPoints;
|
||||
for (const auto& lp : shipLocalEmissionPoints) {
|
||||
Vector3f adjusted = lp + Vector3f{ 0.0f, -Environment::zoom * 0.03f, 0.0f };
|
||||
shipCameraPoints.push_back(adjusted);
|
||||
}
|
||||
if (!shipCameraPoints.empty()) {
|
||||
sparkEmitter.setEmissionPoints(shipCameraPoints);
|
||||
}
|
||||
|
||||
sparkEmitter.update(static_cast<float>(delta));
|
||||
//projectileEmitter.update(static_cast<float>(delta));
|
||||
|
||||
explosionEmitter.update(static_cast<float>(delta));
|
||||
if (showExplosion) {
|
||||
@ -1731,6 +1726,8 @@ namespace ZL
|
||||
}
|
||||
}
|
||||
|
||||
planetObject.update(static_cast<float>(delta));
|
||||
|
||||
// update velocity text
|
||||
|
||||
if (shipAlive && !gameOver) {
|
||||
@ -1773,6 +1770,18 @@ namespace ZL
|
||||
|
||||
void Space::update() {
|
||||
if (networkClient) {
|
||||
if (networkClient->IsConnected()) {
|
||||
wasConnectedToServer = true;
|
||||
}
|
||||
else if (wasConnectedToServer && shipAlive && !gameOver) {
|
||||
wasConnectedToServer = false;
|
||||
shipAlive = false;
|
||||
gameOver = true;
|
||||
Environment::shipState.velocity = 0.0f;
|
||||
std::cout << "Client: Lost connection to server\n";
|
||||
menuManager.showConnectionLost();
|
||||
}
|
||||
|
||||
auto pending = networkClient->getPendingProjectiles();
|
||||
if (!pending.empty()) {
|
||||
const float projectileSpeed = PROJECTILE_VELOCITY;
|
||||
@ -1862,6 +1871,17 @@ namespace ZL
|
||||
}
|
||||
}
|
||||
|
||||
auto disconnects = networkClient->getPendingDisconnects();
|
||||
for (int pid : disconnects) {
|
||||
remotePlayerStates.erase(pid);
|
||||
deadRemotePlayers.erase(pid);
|
||||
if (trackedTargetId == pid) {
|
||||
trackedTargetId = -1;
|
||||
targetAcquireAnim = 0.f;
|
||||
}
|
||||
std::cout << "Client: Remote player " << pid << " left the game, removed from scene\n";
|
||||
}
|
||||
|
||||
auto boxDestructions = networkClient->getPendingBoxDestructions();
|
||||
if (!boxDestructions.empty()) {
|
||||
std::cout << "Game: Received " << boxDestructions.size() << " box destruction events" << std::endl;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include "render/Renderer.h"
|
||||
#include "Environment.h"
|
||||
@ -88,6 +88,7 @@ namespace ZL {
|
||||
VertexDataStruct boxBase;
|
||||
|
||||
SparkEmitter sparkEmitter;
|
||||
SparkEmitter sparkEmitterCargo;
|
||||
SparkEmitter projectileEmitter;
|
||||
SparkEmitter explosionEmitter;
|
||||
PlanetObject planetObject;
|
||||
@ -97,7 +98,7 @@ namespace ZL {
|
||||
float projectileCooldownMs = 500.0f;
|
||||
int64_t lastProjectileFireTime = 0;
|
||||
int maxProjectiles = 500;
|
||||
std::vector<Vector3f> shipLocalEmissionPoints;
|
||||
//std::vector<Vector3f> shipLocalEmissionPoints;
|
||||
|
||||
|
||||
bool shipAlive = true;
|
||||
@ -123,6 +124,7 @@ namespace ZL {
|
||||
|
||||
std::unordered_set<int> deadRemotePlayers;
|
||||
int playerScore = 0;
|
||||
bool wasConnectedToServer = false;
|
||||
|
||||
static constexpr float TARGET_MAX_DIST = 50000.0f;
|
||||
static constexpr float TARGET_MAX_DIST_SQ = TARGET_MAX_DIST * TARGET_MAX_DIST;
|
||||
@ -139,6 +141,7 @@ namespace ZL {
|
||||
void drawTargetHud(); // рисует рамку или стрелку
|
||||
int pickTargetId() const; // ???????? ???? (????: ????????? ????? ????????? ?????)
|
||||
|
||||
void resetPlayerState();
|
||||
void clearTextRendererCache();
|
||||
|
||||
// Crosshair HUD
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
#include "Environment.h"
|
||||
#include <stdexcept>
|
||||
#include "utils/Utils.h"
|
||||
#include "GameConstants.h"
|
||||
|
||||
namespace ZL {
|
||||
|
||||
@ -74,7 +75,7 @@ namespace ZL {
|
||||
texture = tex;
|
||||
}
|
||||
|
||||
void SparkEmitter::prepareDrawData() {
|
||||
void SparkEmitter::prepareDrawData(bool withRotation) {
|
||||
if (!drawDataDirty) return;
|
||||
|
||||
drawPositions.clear();
|
||||
@ -91,10 +92,10 @@ namespace ZL {
|
||||
for (const auto& particle : particles) {
|
||||
if (particle.active) {
|
||||
Vector3f posCam;
|
||||
if (useWorldSpace) {
|
||||
if (withRotation) {
|
||||
Vector3f rel = particle.position - Environment::shipState.position;
|
||||
posCam = Environment::inverseShipMatrix * rel;
|
||||
}
|
||||
}
|
||||
else {
|
||||
posCam = particle.position;
|
||||
}
|
||||
@ -120,29 +121,71 @@ namespace ZL {
|
||||
|
||||
float size = particleSize * particle.scale;
|
||||
|
||||
drawPositions.push_back({ posCam(0) - size, posCam(1) - size, posCam(2) });
|
||||
drawTexCoords.push_back({ 0.0f, 0.0f });
|
||||
if (withRotation)
|
||||
{
|
||||
|
||||
drawPositions.push_back({ posCam(0) - size, posCam(1) + size, posCam(2) });
|
||||
drawTexCoords.push_back({ 0.0f, 1.0f });
|
||||
drawPositions.push_back(Environment::shipState.rotation * Vector3f{ -size, -size, 0 } + posCam);
|
||||
drawTexCoords.push_back({ 0.0f, 0.0f });
|
||||
|
||||
drawPositions.push_back({ posCam(0) + size, posCam(1) + size, posCam(2) });
|
||||
drawTexCoords.push_back({ 1.0f, 1.0f });
|
||||
drawPositions.push_back(Environment::shipState.rotation * Vector3f{ -size, size,0 } + posCam);
|
||||
drawTexCoords.push_back({ 0.0f, 1.0f });
|
||||
|
||||
drawPositions.push_back({ posCam(0) - size, posCam(1) - size, posCam(2) });
|
||||
drawTexCoords.push_back({ 0.0f, 0.0f });
|
||||
drawPositions.push_back(Environment::shipState.rotation * Vector3f{ size,size, 0 } + posCam);
|
||||
drawTexCoords.push_back({ 1.0f, 1.0f });
|
||||
|
||||
drawPositions.push_back({ posCam(0) + size, posCam(1) + size, posCam(2) });
|
||||
drawTexCoords.push_back({ 1.0f, 1.0f });
|
||||
drawPositions.push_back(Environment::shipState.rotation * Vector3f{ -size, -size, 0 } + posCam);
|
||||
drawTexCoords.push_back({ 0.0f, 0.0f });
|
||||
|
||||
drawPositions.push_back({ posCam(0) + size, posCam(1) - size, posCam(2) });
|
||||
drawTexCoords.push_back({ 1.0f, 0.0f });
|
||||
drawPositions.push_back(Environment::shipState.rotation * Vector3f{ size, size,0 } + posCam);
|
||||
drawTexCoords.push_back({ 1.0f, 1.0f });
|
||||
|
||||
drawPositions.push_back(Environment::shipState.rotation * Vector3f{ size, -size, 0 } + posCam);
|
||||
drawTexCoords.push_back({ 1.0f, 0.0f });
|
||||
}
|
||||
else
|
||||
{
|
||||
drawPositions.push_back({ posCam(0) - size, posCam(1) - size, posCam(2) });
|
||||
drawTexCoords.push_back({ 0.0f, 0.0f });
|
||||
|
||||
drawPositions.push_back({ posCam(0) - size, posCam(1) + size, posCam(2) });
|
||||
drawTexCoords.push_back({ 0.0f, 1.0f });
|
||||
|
||||
drawPositions.push_back({ posCam(0) + size, posCam(1) + size, posCam(2) });
|
||||
drawTexCoords.push_back({ 1.0f, 1.0f });
|
||||
|
||||
drawPositions.push_back({ posCam(0) - size, posCam(1) - size, posCam(2) });
|
||||
drawTexCoords.push_back({ 0.0f, 0.0f });
|
||||
|
||||
drawPositions.push_back({ posCam(0) + size, posCam(1) + size, posCam(2) });
|
||||
drawTexCoords.push_back({ 1.0f, 1.0f });
|
||||
|
||||
drawPositions.push_back({ posCam(0) + size, posCam(1) - size, posCam(2) });
|
||||
drawTexCoords.push_back({ 1.0f, 0.0f });
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
drawDataDirty = false;
|
||||
}
|
||||
|
||||
void SparkEmitter::draw(Renderer& renderer, float zoom, int screenWidth, int screenHeight) {
|
||||
void SparkEmitter::prepareForDraw(bool withRotation) {
|
||||
if (!configured) return;
|
||||
|
||||
prepareDrawData(withRotation);
|
||||
|
||||
if (!drawPositions.empty()) {
|
||||
sparkQuad.data.PositionData = drawPositions;
|
||||
sparkQuad.data.TexCoordData = drawTexCoords;
|
||||
sparkQuad.RefreshVBO();
|
||||
}
|
||||
}
|
||||
|
||||
void SparkEmitter::draw(Renderer& renderer, float zoom, int screenWidth, int screenHeight)
|
||||
{
|
||||
draw(renderer, zoom, screenWidth, screenHeight, true);
|
||||
}
|
||||
|
||||
void SparkEmitter::draw(Renderer& renderer, float zoom, int screenWidth, int screenHeight, bool withRotation) {
|
||||
if (!configured) {
|
||||
throw std::runtime_error("Failed to load spark emitter config file 1!");
|
||||
}
|
||||
@ -155,46 +198,30 @@ namespace ZL {
|
||||
throw std::runtime_error("Failed to load spark emitter config file 2!");
|
||||
}
|
||||
|
||||
prepareDrawData();
|
||||
//prepareDrawData(withRotation);
|
||||
|
||||
if (drawPositions.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
sparkQuad.data.PositionData = drawPositions;
|
||||
sparkQuad.data.TexCoordData = drawTexCoords;
|
||||
sparkQuad.RefreshVBO();
|
||||
|
||||
static const std::string vPositionName = "vPosition";
|
||||
static const std::string vTexCoordName = "vTexCoord";
|
||||
static const std::string textureUniformName = "Texture";
|
||||
|
||||
*/
|
||||
renderer.shaderManager.PushShader(shaderProgramName);
|
||||
renderer.RenderUniform1i(textureUniformName, 0);
|
||||
renderer.EnableVertexAttribArray(vPositionName);
|
||||
renderer.EnableVertexAttribArray(vTexCoordName);
|
||||
|
||||
//float aspectRatio = static_cast<float>(screenWidth) / static_cast<float>(screenHeight);
|
||||
//renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5, aspectRatio, Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
|
||||
renderer.SetMatrix();
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, texture->getTexID());
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
|
||||
|
||||
renderer.PushMatrix();
|
||||
//renderer.LoadIdentity();
|
||||
//renderer.TranslateMatrix({ 0, 0, -1.0f * zoom });
|
||||
|
||||
//renderer.PushMatrix();
|
||||
renderer.DrawVertexRenderStruct(sparkQuad);
|
||||
|
||||
renderer.PopMatrix();
|
||||
//renderer.PopProjectionMatrix();
|
||||
//renderer.PopMatrix();
|
||||
|
||||
glDisable(GL_BLEND);
|
||||
|
||||
renderer.DisableVertexAttribArray(vPositionName);
|
||||
renderer.DisableVertexAttribArray(vTexCoordName);
|
||||
renderer.shaderManager.PopShader();
|
||||
}
|
||||
|
||||
@ -207,9 +234,19 @@ namespace ZL {
|
||||
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
currentTime - lastEmissionTime).count();
|
||||
|
||||
if (isActive && elapsed >= emissionRate) {
|
||||
emit();
|
||||
lastEmissionTime = currentTime;
|
||||
if (isActive && elapsed >= static_cast<long long>(emissionRate)) {
|
||||
int emitCount = static_cast<int>(elapsed / emissionRate);
|
||||
float elapsedF = static_cast<float>(elapsed);
|
||||
for (int e = 0; e < emitCount; ++e) {
|
||||
// e=0 — самая старая эмиссия, e=emitCount-1 — самая свежая
|
||||
float ageMs = static_cast<float>(emitCount - 1 - e) * emissionRate;
|
||||
// lerpT=0 → текущая позиция (новейшая), lerpT=1 → предыдущая (самая старая)
|
||||
float lerpT = (elapsedF > 0.0f) ? min(ageMs / elapsedF, 1.0f) : 0.0f;
|
||||
emit(ageMs, lerpT);
|
||||
}
|
||||
lastEmissionTime += std::chrono::milliseconds(
|
||||
static_cast<long long>(emitCount * emissionRate));
|
||||
|
||||
drawDataDirty = true;
|
||||
}
|
||||
|
||||
@ -248,7 +285,7 @@ namespace ZL {
|
||||
}
|
||||
}
|
||||
|
||||
void SparkEmitter::emit() {
|
||||
void SparkEmitter::emit(float ageMs, float lerpT) {
|
||||
if (!configured) {
|
||||
throw std::runtime_error("Failed to load spark emitter config file 4!");
|
||||
}
|
||||
@ -256,7 +293,30 @@ namespace ZL {
|
||||
if (emissionPoints.empty()) return;
|
||||
bool emitted = false;
|
||||
|
||||
for (int i = 0; i < emissionPoints.size(); ++i) {
|
||||
auto applyAge = [](SparkParticle& particle, float age) {
|
||||
if (age <= 0.0f) return;
|
||||
particle.position(0) += particle.velocity(0) * age / 1000.0f;
|
||||
particle.position(1) += particle.velocity(1) * age / 1000.0f;
|
||||
particle.position(2) += particle.velocity(2) * age / 1000.0f;
|
||||
particle.lifeTime = age;
|
||||
if (particle.lifeTime >= particle.maxLifeTime) {
|
||||
particle.active = false;
|
||||
} else {
|
||||
float lifeRatio = particle.lifeTime / particle.maxLifeTime;
|
||||
particle.scale = 1.0f - lifeRatio * 0.8f;
|
||||
}
|
||||
};
|
||||
|
||||
// Вычисляем стартовую позицию с интерполяцией между prev и current
|
||||
// lerpT=0 → текущая позиция (новейшая эмиссия), lerpT=1 → предыдущая (самая старая)
|
||||
bool canInterp = (prevEmissionPoints.size() == emissionPoints.size());
|
||||
|
||||
for (int i = 0; i < (int)emissionPoints.size(); ++i) {
|
||||
Vector3f birthPos = emissionPoints[i];
|
||||
if (canInterp && lerpT > 0.0f) {
|
||||
birthPos = emissionPoints[i] * (1.0f - lerpT) + prevEmissionPoints[i] * lerpT;
|
||||
}
|
||||
|
||||
bool particleFound = false;
|
||||
|
||||
for (auto& particle : particles) {
|
||||
@ -264,8 +324,9 @@ namespace ZL {
|
||||
initParticle(particle, i);
|
||||
particle.active = true;
|
||||
particle.lifeTime = 0;
|
||||
particle.position = emissionPoints[i];
|
||||
particle.position = birthPos;
|
||||
particle.emitterIndex = i;
|
||||
applyAge(particle, ageMs);
|
||||
particleFound = true;
|
||||
emitted = true;
|
||||
break;
|
||||
@ -274,11 +335,11 @@ namespace ZL {
|
||||
|
||||
if (!particleFound && !particles.empty()) {
|
||||
size_t oldestIndex = 0;
|
||||
float maxLifeTime = 0;
|
||||
float maxLifeRatio = 0;
|
||||
|
||||
for (size_t j = 0; j < particles.size(); ++j) {
|
||||
if (particles[j].lifeTime > maxLifeTime) {
|
||||
maxLifeTime = particles[j].lifeTime;
|
||||
if (particles[j].lifeTime > maxLifeRatio) {
|
||||
maxLifeRatio = particles[j].lifeTime;
|
||||
oldestIndex = j;
|
||||
}
|
||||
}
|
||||
@ -286,8 +347,9 @@ namespace ZL {
|
||||
initParticle(particles[oldestIndex], i);
|
||||
particles[oldestIndex].active = true;
|
||||
particles[oldestIndex].lifeTime = 0;
|
||||
particles[oldestIndex].position = emissionPoints[i];
|
||||
particles[oldestIndex].position = birthPos;
|
||||
particles[oldestIndex].emitterIndex = i;
|
||||
applyAge(particles[oldestIndex], ageMs);
|
||||
emitted = true;
|
||||
}
|
||||
}
|
||||
@ -298,6 +360,14 @@ namespace ZL {
|
||||
}
|
||||
|
||||
void SparkEmitter::setEmissionPoints(const std::vector<Vector3f>& positions) {
|
||||
prevEmissionPoints = emissionPoints;
|
||||
emissionPoints = positions;
|
||||
drawDataDirty = true;
|
||||
}
|
||||
|
||||
void SparkEmitter::resetEmissionPoints(const std::vector<Vector3f>& positions)
|
||||
{
|
||||
prevEmissionPoints.clear();
|
||||
emissionPoints = positions;
|
||||
drawDataDirty = true;
|
||||
}
|
||||
@ -465,8 +535,9 @@ namespace ZL {
|
||||
std::cout << "Total emission points: " << emissionPoints.size() << std::endl;
|
||||
}
|
||||
else {
|
||||
std::cerr << "Emission points parsed but empty" << std::endl;
|
||||
throw std::runtime_error("Failed to load spark emitter config file 10!");
|
||||
setEmissionPoints({});
|
||||
std::cout << "Emission points parsed but empty" << std::endl;
|
||||
//throw std::runtime_error("Failed to load spark emitter config file 10!");
|
||||
}
|
||||
}
|
||||
else {
|
||||
@ -553,25 +624,6 @@ namespace ZL {
|
||||
throw std::runtime_error("Failed to load spark emitter config file 18!");
|
||||
}
|
||||
|
||||
|
||||
std::cout << "Working with shaders 2" << std::endl;
|
||||
/*
|
||||
if (j.contains("vertexShader") && j.contains("fragmentShader")
|
||||
&& j["vertexShader"].is_string() && j["fragmentShader"].is_string()) {
|
||||
std::string v = j["vertexShader"].get<std::string>();
|
||||
std::string f = j["fragmentShader"].get<std::string>();
|
||||
std::cout << "Loading shaders - vertex: " << v << ", fragment: " << f << std::endl;
|
||||
|
||||
try {
|
||||
renderer.shaderManager.AddShaderFromFiles(shaderProgramName, v, f, zipFile.c_str());
|
||||
std::cout << "Shaders loaded successfully" << std::endl;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "Shader load error: " << e.what() << std::endl;
|
||||
throw std::runtime_error("Failed to load spark emitter config file 19!");
|
||||
}
|
||||
}*/
|
||||
|
||||
drawDataDirty = true;
|
||||
configured = true;
|
||||
std::cout << "SparkEmitter configuration loaded successfully!" << std::endl;
|
||||
|
||||
@ -26,6 +26,7 @@ namespace ZL {
|
||||
private:
|
||||
std::vector<SparkParticle> particles;
|
||||
std::vector<Vector3f> emissionPoints;
|
||||
std::vector<Vector3f> prevEmissionPoints;
|
||||
std::chrono::steady_clock::time_point lastEmissionTime;
|
||||
float emissionRate;
|
||||
bool isActive;
|
||||
@ -50,7 +51,7 @@ namespace ZL {
|
||||
std::string shaderProgramName;
|
||||
|
||||
bool configured;
|
||||
void prepareDrawData();
|
||||
void prepareDrawData(bool withRotation);
|
||||
bool useWorldSpace;
|
||||
|
||||
public:
|
||||
@ -62,6 +63,7 @@ namespace ZL {
|
||||
float rate = 100.0f);
|
||||
|
||||
void setEmissionPoints(const std::vector<Vector3f>& positions);
|
||||
void resetEmissionPoints(const std::vector<Vector3f>& positions);
|
||||
void setTexture(std::shared_ptr<Texture> tex);
|
||||
void setEmissionRate(float rate) { emissionRate = rate; }
|
||||
void setShaderProgramName(const std::string& name) { shaderProgramName = name; }
|
||||
@ -73,14 +75,23 @@ namespace ZL {
|
||||
bool loadFromJsonFile(const std::string& path, Renderer& renderer, const std::string& zipFile = "");
|
||||
|
||||
void update(float deltaTimeMs);
|
||||
void emit();
|
||||
void emit(float ageMs = 0.0f, float lerpT = 0.0f);
|
||||
|
||||
// Вызывать ДО draw() в начале кадра: готовит данные и загружает в VBO.
|
||||
void prepareForDraw(bool withRotation = true);
|
||||
|
||||
void draw(Renderer& renderer, float zoom, int screenWidth, int screenHeight);
|
||||
void draw(Renderer& renderer, float zoom, int screenWidth, int screenHeight, bool withRotation);
|
||||
|
||||
const std::vector<SparkParticle>& getParticles() const;
|
||||
size_t getActiveParticleCount() const;
|
||||
std::shared_ptr<Texture> getTexture() const { return texture; }
|
||||
|
||||
void setIsActive(bool newActive)
|
||||
{
|
||||
isActive = newActive;
|
||||
}
|
||||
|
||||
private:
|
||||
void initParticle(SparkParticle& particle, int emitterIndex);
|
||||
Vector3f getRandomVelocity(int emitterIndex);
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#include "TextModel.h"
|
||||
#include "TextModel.h"
|
||||
#include <regex>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include "render/Renderer.h"
|
||||
#include <unordered_map>
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
#include "UiManager.h"
|
||||
#include "UiManager.h"
|
||||
#include "utils/Utils.h"
|
||||
#include "render/TextRenderer.h"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include "GameConstants.h"
|
||||
|
||||
namespace ZL {
|
||||
|
||||
@ -57,26 +58,17 @@ namespace ZL {
|
||||
case ButtonState::Normal: tex = &texNormal; break;
|
||||
case ButtonState::Hover: tex = &texHover; break;
|
||||
case ButtonState::Pressed: tex = &texPressed; break;
|
||||
case ButtonState::Disabled: tex = &texDisabled; break;
|
||||
}
|
||||
if (!(*tex)) return;
|
||||
|
||||
static const std::string vPositionName = "vPosition";
|
||||
static const std::string vTexCoordName = "vTexCoord";
|
||||
static const std::string textureUniformName = "Texture";
|
||||
|
||||
renderer.PushMatrix();
|
||||
renderer.TranslateMatrix({ animOffsetX, animOffsetY, 0.0f });
|
||||
renderer.ScaleMatrix({ animScaleX, animScaleY, 1.0f });
|
||||
|
||||
renderer.RenderUniform1i(textureUniformName, 0);
|
||||
renderer.EnableVertexAttribArray(vPositionName);
|
||||
renderer.EnableVertexAttribArray(vTexCoordName);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, (*tex)->getTexID());
|
||||
renderer.DrawVertexRenderStruct(mesh);
|
||||
|
||||
renderer.DisableVertexAttribArray(vPositionName);
|
||||
renderer.DisableVertexAttribArray(vTexCoordName);
|
||||
renderer.PopMatrix();
|
||||
}
|
||||
|
||||
@ -147,14 +139,7 @@ namespace ZL {
|
||||
}
|
||||
|
||||
void UiSlider::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 (texTrack) {
|
||||
glBindTexture(GL_TEXTURE_2D, texTrack->getTexID());
|
||||
renderer.DrawVertexRenderStruct(trackMesh);
|
||||
@ -163,9 +148,44 @@ namespace ZL {
|
||||
glBindTexture(GL_TEXTURE_2D, texKnob->getTexID());
|
||||
renderer.DrawVertexRenderStruct(knobMesh);
|
||||
}
|
||||
}
|
||||
|
||||
renderer.DisableVertexAttribArray(vPositionName);
|
||||
renderer.DisableVertexAttribArray(vTexCoordName);
|
||||
void UiStaticImage::buildMesh() {
|
||||
mesh.data.PositionData.clear();
|
||||
mesh.data.TexCoordData.clear();
|
||||
|
||||
float x0 = rect.x;
|
||||
float y0 = rect.y;
|
||||
float x1 = rect.x + rect.w;
|
||||
float y1 = rect.y + rect.h;
|
||||
|
||||
mesh.data.PositionData.push_back({ x0, y0, 0 });
|
||||
mesh.data.TexCoordData.push_back({ 0, 0 });
|
||||
|
||||
mesh.data.PositionData.push_back({ x0, y1, 0 });
|
||||
mesh.data.TexCoordData.push_back({ 0, 1 });
|
||||
|
||||
mesh.data.PositionData.push_back({ x1, y1, 0 });
|
||||
mesh.data.TexCoordData.push_back({ 1, 1 });
|
||||
|
||||
mesh.data.PositionData.push_back({ x0, y0, 0 });
|
||||
mesh.data.TexCoordData.push_back({ 0, 0 });
|
||||
|
||||
mesh.data.PositionData.push_back({ x1, y1, 0 });
|
||||
mesh.data.TexCoordData.push_back({ 1, 1 });
|
||||
|
||||
mesh.data.PositionData.push_back({ x1, y0, 0 });
|
||||
mesh.data.TexCoordData.push_back({ 1, 0 });
|
||||
|
||||
mesh.RefreshVBO();
|
||||
}
|
||||
|
||||
void UiStaticImage::draw(Renderer& renderer) const {
|
||||
if (!texture) return;
|
||||
|
||||
renderer.RenderUniform1i(textureUniformName, 0);
|
||||
glBindTexture(GL_TEXTURE_2D, texture->getTexID());
|
||||
renderer.DrawVertexRenderStruct(mesh);
|
||||
}
|
||||
|
||||
void UiTextField::draw(Renderer& renderer) const {
|
||||
@ -248,6 +268,7 @@ namespace ZL {
|
||||
if (j.contains("horizontal_gravity")) {
|
||||
std::string hg = j["horizontal_gravity"].get<std::string>();
|
||||
if (hg == "right") node->layoutSettings.hGravity = HorizontalGravity::Right;
|
||||
else if (hg == "center") node->layoutSettings.hGravity = HorizontalGravity::Center;
|
||||
else node->layoutSettings.hGravity = HorizontalGravity::Left;
|
||||
}
|
||||
|
||||
@ -255,6 +276,7 @@ namespace ZL {
|
||||
if (j.contains("vertical_gravity")) {
|
||||
std::string vg = j["vertical_gravity"].get<std::string>();
|
||||
if (vg == "bottom") node->layoutSettings.vGravity = VerticalGravity::Bottom;
|
||||
else if (vg == "center") node->layoutSettings.vGravity = VerticalGravity::Center;
|
||||
else node->layoutSettings.vGravity = VerticalGravity::Top;
|
||||
}
|
||||
|
||||
@ -290,6 +312,9 @@ namespace ZL {
|
||||
btn->texNormal = loadTex("normal");
|
||||
btn->texHover = loadTex("hover");
|
||||
btn->texPressed = loadTex("pressed");
|
||||
btn->texDisabled = loadTex("disabled");
|
||||
|
||||
btn->border = j.value("border", 0.0f);
|
||||
|
||||
node->button = btn;
|
||||
}
|
||||
@ -399,6 +424,32 @@ namespace ZL {
|
||||
}
|
||||
}
|
||||
|
||||
if (typeStr == "StaticImage") {
|
||||
auto img = std::make_shared<UiStaticImage>();
|
||||
img->name = node->name;
|
||||
img->rect = initialRect;
|
||||
|
||||
std::string texPath;
|
||||
if (j.contains("texture") && j["texture"].is_string()) {
|
||||
texPath = j["texture"].get<std::string>();
|
||||
}
|
||||
else if (j.contains("textures") && j["textures"].is_object() && j["textures"].contains("normal")) {
|
||||
texPath = j["textures"]["normal"].get<std::string>();
|
||||
}
|
||||
|
||||
if (!texPath.empty()) {
|
||||
try {
|
||||
auto data = CreateTextureDataFromPng(texPath.c_str(), zipFile.c_str());
|
||||
img->texture = std::make_shared<Texture>(data);
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "UiManager: failed load texture for StaticImage '" << img->name << "' : " << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
node->staticImage = img;
|
||||
}
|
||||
|
||||
if (typeStr == "TextView") {
|
||||
auto tv = std::make_shared<UiTextView>();
|
||||
|
||||
@ -488,6 +539,7 @@ namespace ZL {
|
||||
sliders.clear();
|
||||
textViews.clear();
|
||||
textFields.clear();
|
||||
staticImages.clear();
|
||||
collectButtonsAndSliders(root);
|
||||
|
||||
nodeActiveAnims.clear();
|
||||
@ -499,6 +551,9 @@ namespace ZL {
|
||||
s->buildTrackMesh();
|
||||
s->buildKnobMesh();
|
||||
}
|
||||
for (auto& img : staticImages) {
|
||||
img->buildMesh();
|
||||
}
|
||||
}
|
||||
|
||||
void UiManager::loadFromFile(const std::string& path, Renderer& renderer, const std::string& zipFile) {
|
||||
@ -614,9 +669,15 @@ namespace ZL {
|
||||
if (child->layoutSettings.hGravity == HorizontalGravity::Right) {
|
||||
fLX = currentW - childW - child->localX;
|
||||
}
|
||||
else if (child->layoutSettings.hGravity == HorizontalGravity::Center) {
|
||||
fLX = (currentW - childW) / 2.0f + child->localX;
|
||||
}
|
||||
if (child->layoutSettings.vGravity == VerticalGravity::Top) {
|
||||
fLY = currentH - childH - child->localY;
|
||||
}
|
||||
else if (child->layoutSettings.vGravity == VerticalGravity::Center) {
|
||||
fLY = (currentH - childH) / 2.0f + child->localY;
|
||||
}
|
||||
|
||||
// Передаем рассчитанные fLX, fLY в рекурсию
|
||||
layoutNode(child, node->screenRect.x, node->screenRect.y, currentW, currentH, fLX, fLY);
|
||||
@ -657,6 +718,12 @@ namespace ZL {
|
||||
node->textField->rect = node->screenRect;
|
||||
// Аналогично для курсора и фонового меша
|
||||
}
|
||||
|
||||
// 5. Обновляем статическое изображение
|
||||
if (node->staticImage) {
|
||||
node->staticImage->rect = node->screenRect;
|
||||
node->staticImage->buildMesh();
|
||||
}
|
||||
}
|
||||
|
||||
void UiManager::updateAllLayouts() {
|
||||
@ -686,6 +753,9 @@ namespace ZL {
|
||||
if (node->textField) {
|
||||
textFields.push_back(node->textField);
|
||||
}
|
||||
if (node->staticImage) {
|
||||
staticImages.push_back(node->staticImage);
|
||||
}
|
||||
for (auto& c : node->children) collectButtonsAndSliders(c);
|
||||
}
|
||||
|
||||
@ -699,6 +769,16 @@ namespace ZL {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UiManager::setButtonPressCallback(const std::string& name, std::function<void(const std::string&)> cb) {
|
||||
auto b = findButton(name);
|
||||
if (!b) {
|
||||
std::cerr << "UiManager: setButtonPressCallback failed, button not found: " << name << std::endl;
|
||||
return false;
|
||||
}
|
||||
b->onPress = std::move(cb);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UiManager::addSlider(const std::string& name, const UiRect& rect, Renderer& renderer, const std::string& zipFile,
|
||||
const std::string& trackPath, const std::string& knobPath, float initialValue, bool vertical) {
|
||||
|
||||
@ -784,8 +864,9 @@ namespace ZL {
|
||||
prev.buttons = buttons;
|
||||
prev.sliders = sliders;
|
||||
prev.textFields = textFields;
|
||||
prev.pressedButton = pressedButton;
|
||||
prev.pressedSlider = pressedSlider;
|
||||
prev.staticImages = staticImages;
|
||||
prev.pressedButtons = pressedButtons;
|
||||
prev.pressedSliders = pressedSliders;
|
||||
prev.focusedTextField = focusedTextField;
|
||||
prev.path = "";
|
||||
|
||||
@ -834,8 +915,9 @@ namespace ZL {
|
||||
buttons = s.buttons;
|
||||
sliders = s.sliders;
|
||||
textFields = s.textFields;
|
||||
pressedButton = s.pressedButton;
|
||||
pressedSlider = s.pressedSlider;
|
||||
staticImages = s.staticImages;
|
||||
pressedButtons = s.pressedButtons;
|
||||
pressedSliders = s.pressedSliders;
|
||||
focusedTextField = s.focusedTextField;
|
||||
|
||||
animCallbacks = s.animCallbacks;
|
||||
@ -868,6 +950,9 @@ namespace ZL {
|
||||
renderer.PushMatrix();
|
||||
renderer.LoadIdentity();
|
||||
|
||||
for (const auto& img : staticImages) {
|
||||
img->draw(renderer);
|
||||
}
|
||||
for (const auto& b : buttons) {
|
||||
b->draw(renderer);
|
||||
}
|
||||
@ -1047,18 +1132,25 @@ namespace ZL {
|
||||
}
|
||||
}
|
||||
|
||||
void UiManager::onMouseMove(int x, int y) {
|
||||
for (auto& b : buttons) {
|
||||
if (b->rect.contains((float)x, (float)y)) {
|
||||
if (b->state != ButtonState::Pressed) b->state = ButtonState::Hover;
|
||||
}
|
||||
else {
|
||||
if (b->state != ButtonState::Pressed) b->state = ButtonState::Normal;
|
||||
void UiManager::onTouchMove(int64_t fingerId, int x, int y) {
|
||||
// Hover state updates only make sense for mouse (single pointer)
|
||||
if (fingerId == MOUSE_FINGER_ID) {
|
||||
for (auto& b : buttons) {
|
||||
if (b->state != ButtonState::Disabled)
|
||||
{
|
||||
if (b->rect.containsConsideringBorder((float)x, (float)y, b->border)) {
|
||||
if (b->state != ButtonState::Pressed) b->state = ButtonState::Hover;
|
||||
}
|
||||
else {
|
||||
if (b->state != ButtonState::Pressed) b->state = ButtonState::Normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pressedSlider) {
|
||||
auto s = pressedSlider;
|
||||
auto it = pressedSliders.find(fingerId);
|
||||
if (it != pressedSliders.end()) {
|
||||
auto s = it->second;
|
||||
float t;
|
||||
if (s->vertical) {
|
||||
t = (y - s->rect.y) / s->rect.h;
|
||||
@ -1074,17 +1166,23 @@ namespace ZL {
|
||||
}
|
||||
}
|
||||
|
||||
void UiManager::onMouseDown(int x, int y) {
|
||||
|
||||
void UiManager::onTouchDown(int64_t fingerId, int x, int y) {
|
||||
for (auto& b : buttons) {
|
||||
if (b->rect.contains((float)x, (float)y)) {
|
||||
b->state = ButtonState::Pressed;
|
||||
pressedButton = b;
|
||||
if (b->state != ButtonState::Disabled)
|
||||
{
|
||||
if (b->rect.containsConsideringBorder((float)x, (float)y, b->border)) {
|
||||
b->state = ButtonState::Pressed;
|
||||
pressedButtons[fingerId] = b;
|
||||
if (b->onPress) b->onPress(b->name);
|
||||
break; // a single finger can only press one button
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& s : sliders) {
|
||||
if (s->rect.contains((float)x, (float)y)) {
|
||||
pressedSlider = s;
|
||||
pressedSliders[fingerId] = s;
|
||||
float t;
|
||||
if (s->vertical) {
|
||||
t = (y - s->rect.y) / s->rect.h;
|
||||
@ -1112,29 +1210,32 @@ namespace ZL {
|
||||
}
|
||||
}
|
||||
|
||||
void UiManager::onMouseUp(int x, int y) {
|
||||
void UiManager::onTouchUp(int64_t fingerId, int x, int y) {
|
||||
std::vector<std::shared_ptr<UiButton>> clicked;
|
||||
|
||||
for (auto& b : buttons) {
|
||||
if (!b) continue;
|
||||
bool contains = b->rect.contains((float)x, (float)y);
|
||||
|
||||
if (b->state == ButtonState::Pressed) {
|
||||
if (contains && pressedButton == b) {
|
||||
clicked.push_back(b);
|
||||
auto btnIt = pressedButtons.find(fingerId);
|
||||
if (btnIt != pressedButtons.end()) {
|
||||
auto b = btnIt->second;
|
||||
if (b) {
|
||||
bool contains = b->rect.contains((float)x, (float)y);
|
||||
if (b->state == ButtonState::Pressed) {
|
||||
if (contains) {
|
||||
clicked.push_back(b);
|
||||
}
|
||||
// On mouse: leave Hover if still over button. On touch: always Normal.
|
||||
b->state = (contains && fingerId == MOUSE_FINGER_ID) ? ButtonState::Hover : ButtonState::Normal;
|
||||
}
|
||||
b->state = contains ? ButtonState::Hover : ButtonState::Normal;
|
||||
}
|
||||
pressedButtons.erase(btnIt);
|
||||
}
|
||||
|
||||
pressedSliders.erase(fingerId);
|
||||
|
||||
for (auto& b : clicked) {
|
||||
if (b->onClick) {
|
||||
b->onClick(b->name);
|
||||
}
|
||||
}
|
||||
|
||||
pressedButton.reset();
|
||||
if (pressedSlider) pressedSlider.reset();
|
||||
}
|
||||
|
||||
void UiManager::onKeyPress(unsigned char key) {
|
||||
@ -1250,6 +1351,13 @@ namespace ZL {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::shared_ptr<UiStaticImage> UiManager::findStaticImage(const std::string& name) {
|
||||
for (auto& img : staticImages) {
|
||||
if (img->name == name) return img;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<UiTextView> UiManager::findTextView(const std::string& name) {
|
||||
for (auto& tv : textViews) {
|
||||
if (tv->name == name) return tv;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include "render/Renderer.h"
|
||||
#include "render/TextureManager.h"
|
||||
@ -10,6 +10,7 @@
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <cstdint>
|
||||
|
||||
namespace ZL {
|
||||
|
||||
@ -23,12 +24,17 @@ namespace ZL {
|
||||
bool contains(float px, float py) const {
|
||||
return px >= x && px <= x + w && py >= y && py <= y + h;
|
||||
}
|
||||
|
||||
bool containsConsideringBorder(float px, float py, float border) const {
|
||||
return px >= x+border && px <= x + w-border && py >= y+border && py <= y + h-border;
|
||||
}
|
||||
};
|
||||
|
||||
enum class ButtonState {
|
||||
Normal,
|
||||
Hover,
|
||||
Pressed
|
||||
Pressed,
|
||||
Disabled
|
||||
};
|
||||
|
||||
enum class LayoutType {
|
||||
@ -55,11 +61,13 @@ namespace ZL {
|
||||
|
||||
enum class HorizontalGravity {
|
||||
Left,
|
||||
Center,
|
||||
Right
|
||||
};
|
||||
|
||||
enum class VerticalGravity {
|
||||
Bottom, // Обычно в OpenGL Y растет вверх, так что низ - это 0
|
||||
Center,
|
||||
Top
|
||||
};
|
||||
|
||||
@ -76,14 +84,17 @@ namespace ZL {
|
||||
struct UiButton {
|
||||
std::string name;
|
||||
UiRect rect;
|
||||
float border = 0;
|
||||
std::shared_ptr<Texture> texNormal;
|
||||
std::shared_ptr<Texture> texHover;
|
||||
std::shared_ptr<Texture> texPressed;
|
||||
std::shared_ptr<Texture> texDisabled;
|
||||
ButtonState state = ButtonState::Normal;
|
||||
|
||||
VertexRenderStruct mesh;
|
||||
|
||||
std::function<void(const std::string&)> onClick;
|
||||
std::function<void(const std::string&)> onPress; // fires on touch/mouse down
|
||||
|
||||
// animation runtime
|
||||
float animOffsetX = 0.0f;
|
||||
@ -152,6 +163,17 @@ namespace ZL {
|
||||
void draw(Renderer& renderer) const;
|
||||
};
|
||||
|
||||
struct UiStaticImage {
|
||||
std::string name;
|
||||
UiRect rect;
|
||||
std::shared_ptr<Texture> texture;
|
||||
|
||||
VertexRenderStruct mesh;
|
||||
|
||||
void buildMesh();
|
||||
void draw(Renderer& renderer) const;
|
||||
};
|
||||
|
||||
struct UiNode {
|
||||
std::string name;
|
||||
LayoutType layoutType = LayoutType::Frame;
|
||||
@ -178,6 +200,7 @@ namespace ZL {
|
||||
std::shared_ptr<UiSlider> slider;
|
||||
std::shared_ptr<UiTextView> textView;
|
||||
std::shared_ptr<UiTextField> textField;
|
||||
std::shared_ptr<UiStaticImage> staticImage;
|
||||
|
||||
// Анимации
|
||||
struct AnimStep {
|
||||
@ -203,19 +226,35 @@ namespace ZL {
|
||||
public:
|
||||
UiManager() = default;
|
||||
|
||||
// Sentinel finger ID used for mouse events on desktop/web
|
||||
static constexpr int64_t MOUSE_FINGER_ID = -1LL;
|
||||
|
||||
void replaceRoot(std::shared_ptr<UiNode> newRoot);
|
||||
void loadFromFile(const std::string& path, Renderer& renderer, const std::string& zipFile = "");
|
||||
|
||||
void draw(Renderer& renderer);
|
||||
|
||||
void onMouseMove(int x, int y);
|
||||
void onMouseDown(int x, int y);
|
||||
void onMouseUp(int x, int y);
|
||||
// Multi-touch methods (used directly for touch events with per-finger IDs)
|
||||
void onTouchDown(int64_t fingerId, int x, int y);
|
||||
void onTouchUp(int64_t fingerId, int x, int y);
|
||||
void onTouchMove(int64_t fingerId, int x, int y);
|
||||
|
||||
// Mouse convenience wrappers (delegate to touch with MOUSE_FINGER_ID)
|
||||
void onMouseMove(int x, int y) { onTouchMove(MOUSE_FINGER_ID, x, y); }
|
||||
void onMouseDown(int x, int y) { onTouchDown(MOUSE_FINGER_ID, x, y); }
|
||||
void onMouseUp(int x, int y) { onTouchUp(MOUSE_FINGER_ID, x, y); }
|
||||
|
||||
void onKeyPress(unsigned char key);
|
||||
void onKeyBackspace();
|
||||
|
||||
// Returns true if any finger is currently interacting with UI
|
||||
bool isUiInteraction() const {
|
||||
return pressedButton != nullptr || pressedSlider != nullptr || focusedTextField != nullptr;
|
||||
return !pressedButtons.empty() || !pressedSliders.empty() || focusedTextField != nullptr;
|
||||
}
|
||||
|
||||
// Returns true if this specific finger is currently interacting with UI
|
||||
bool isUiInteractionForFinger(int64_t fingerId) const {
|
||||
return pressedButtons.count(fingerId) > 0 || pressedSliders.count(fingerId) > 0 || focusedTextField != nullptr;
|
||||
}
|
||||
|
||||
void stopAllAnimations() {
|
||||
@ -234,6 +273,7 @@ namespace ZL {
|
||||
std::shared_ptr<UiButton> findButton(const std::string& name);
|
||||
|
||||
bool setButtonCallback(const std::string& name, std::function<void(const std::string&)> cb);
|
||||
bool setButtonPressCallback(const std::string& name, std::function<void(const std::string&)> cb);
|
||||
|
||||
bool addSlider(const std::string& name, const UiRect& rect, Renderer& renderer, const std::string& zipFile,
|
||||
const std::string& trackPath, const std::string& knobPath, float initialValue = 0.0f, bool vertical = true);
|
||||
@ -249,6 +289,8 @@ namespace ZL {
|
||||
bool setTextFieldCallback(const std::string& name, std::function<void(const std::string&, const std::string&)> cb);
|
||||
std::string getTextFieldValue(const std::string& name);
|
||||
|
||||
std::shared_ptr<UiStaticImage> findStaticImage(const std::string& name);
|
||||
|
||||
bool pushMenuFromFile(const std::string& path, Renderer& renderer, const std::string& zipFile = "");
|
||||
bool pushMenuFromSavedRoot(std::shared_ptr<UiNode> newRoot);
|
||||
bool popMenu();
|
||||
@ -294,12 +336,14 @@ namespace ZL {
|
||||
std::vector<std::shared_ptr<UiSlider>> sliders;
|
||||
std::vector<std::shared_ptr<UiTextView>> textViews;
|
||||
std::vector<std::shared_ptr<UiTextField>> textFields;
|
||||
std::vector<std::shared_ptr<UiStaticImage>> staticImages;
|
||||
|
||||
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::shared_ptr<UiButton> pressedButton;
|
||||
std::shared_ptr<UiSlider> pressedSlider;
|
||||
// Per-finger tracking for multi-touch support
|
||||
std::map<int64_t, std::shared_ptr<UiButton>> pressedButtons;
|
||||
std::map<int64_t, std::shared_ptr<UiSlider>> pressedSliders;
|
||||
std::shared_ptr<UiTextField> focusedTextField;
|
||||
|
||||
struct MenuState {
|
||||
@ -307,8 +351,9 @@ namespace ZL {
|
||||
std::vector<std::shared_ptr<UiButton>> buttons;
|
||||
std::vector<std::shared_ptr<UiSlider>> sliders;
|
||||
std::vector<std::shared_ptr<UiTextField>> textFields;
|
||||
std::shared_ptr<UiButton> pressedButton;
|
||||
std::shared_ptr<UiSlider> pressedSlider;
|
||||
std::vector<std::shared_ptr<UiStaticImage>> staticImages;
|
||||
std::map<int64_t, std::shared_ptr<UiButton>> pressedButtons;
|
||||
std::map<int64_t, std::shared_ptr<UiSlider>> pressedSliders;
|
||||
std::shared_ptr<UiTextField> focusedTextField;
|
||||
std::string path;
|
||||
std::map<std::pair<std::string, std::string>, std::function<void()>> animCallbacks;
|
||||
|
||||
86
src/main.cpp
86
src/main.cpp
@ -1,4 +1,4 @@
|
||||
#include "Game.h"
|
||||
#include "Game.h"
|
||||
#include "Environment.h"
|
||||
#include <iostream>
|
||||
#ifdef __ANDROID__
|
||||
@ -26,10 +26,6 @@ ZL::Game game;
|
||||
|
||||
#ifdef EMSCRIPTEN
|
||||
void MainLoop() {
|
||||
// SDL_GL_MakeCurrent тут не нужен каждый раз
|
||||
/*glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
SDL_GL_SwapWindow(ZL::Environment::window);*/
|
||||
g_game->update();
|
||||
}
|
||||
#else
|
||||
@ -55,8 +51,8 @@ EM_BOOL onWebGLContextRestored(int /*eventType*/, const void* /*reserved*/, void
|
||||
|
||||
static void applyResize(int logicalW, int logicalH) {
|
||||
// Получаем коэффициент плотности пикселей (например, 2.625 на Pixel или 3.0 на Samsung)
|
||||
//double dpr = emscripten_get_device_pixel_ratio();
|
||||
double dpr = 1; // low quality
|
||||
double dpr = emscripten_get_device_pixel_ratio();
|
||||
//double dpr = 1; // low quality
|
||||
|
||||
// Вычисляем реальные физические пиксели
|
||||
int physicalW = static_cast<int>(logicalW * dpr);
|
||||
@ -72,10 +68,6 @@ static void applyResize(int logicalW, int logicalH) {
|
||||
SDL_SetWindowSize(ZL::Environment::window, physicalW, physicalH);
|
||||
}
|
||||
|
||||
// Обновляем ваши внутренние переменные окружения
|
||||
ZL::Environment::width = physicalW;
|
||||
ZL::Environment::height = physicalH;
|
||||
|
||||
// Пушим событие, чтобы движок пересчитал матрицы проекции
|
||||
SDL_Event e = {};
|
||||
e.type = SDL_WINDOWEVENT;
|
||||
@ -83,6 +75,10 @@ static void applyResize(int logicalW, int logicalH) {
|
||||
e.window.data1 = physicalW;
|
||||
e.window.data2 = physicalH;
|
||||
SDL_PushEvent(&e);
|
||||
|
||||
std::cout << "Resized, new size: " << logicalW << "x" << logicalH
|
||||
<< " (physical: " << physicalW << "x" << physicalH
|
||||
<< ", DPR: " << dpr << ")" << std::endl;
|
||||
}
|
||||
|
||||
EM_BOOL onWindowResized(int /*eventType*/, const EmscriptenUiEvent* e, void* /*userData*/) {
|
||||
@ -131,7 +127,11 @@ int main(int argc, char* argv[]) {
|
||||
// канваса и отправит SDL_WINDOWEVENT_RESIZED для настройки проекции.
|
||||
applyResize(canvasW, canvasH);
|
||||
|
||||
// Prevent mouse clicks from generating fake SDL_FINGERDOWN events (desktop browser)
|
||||
SDL_SetHint(SDL_HINT_MOUSE_TOUCH_EVENTS, "0");
|
||||
// Prevent touch events from generating fake SDL_MOUSEBUTTONDOWN events (mobile browser),
|
||||
// since we now handle SDL_FINGERDOWN directly for multi-touch support.
|
||||
SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0");
|
||||
|
||||
emscripten_set_main_loop(MainLoop, 0, 1);
|
||||
|
||||
@ -240,68 +240,6 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
ZL::Environment::width = CONST_WIDTH;
|
||||
ZL::Environment::height = CONST_HEIGHT;
|
||||
|
||||
|
||||
/*#ifdef EMSCRIPTEN
|
||||
if (SDL_Init(SDL_INIT_VIDEO) != 0) {
|
||||
std::cerr << "SDL_Init failed: " << SDL_GetError() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
|
||||
|
||||
SDL_Window* win = SDL_CreateWindow("Space Ship Game",
|
||||
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
|
||||
CONST_WIDTH, CONST_HEIGHT,
|
||||
SDL_WINDOW_OPENGL);
|
||||
|
||||
if (!win) {
|
||||
std::cerr << "SDL_CreateWindow failed: " << SDL_GetError() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
SDL_GLContext glContext = SDL_GL_CreateContext(win);
|
||||
if (!glContext) {
|
||||
std::cerr << "SDL_GL_CreateContext failed: " << SDL_GetError() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Привязка контекста к окну — важно!
|
||||
SDL_GL_MakeCurrent(win, glContext);
|
||||
|
||||
ZL::Environment::window = win;
|
||||
|
||||
g_game = new ZL::Game();
|
||||
g_game->setup();
|
||||
|
||||
// Re-create the game object when the WebGL context is lost and restored
|
||||
// (this happens e.g. when the user toggles fullscreen in the browser).
|
||||
emscripten_set_webglcontextlost_callback("#canvas", nullptr, EM_TRUE, onWebGLContextLost);
|
||||
emscripten_set_webglcontextrestored_callback("#canvas", nullptr, EM_TRUE, onWebGLContextRestored);
|
||||
|
||||
// Keep Environment::width/height in sync when the canvas is resized.
|
||||
emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, nullptr, EM_FALSE, onWindowResized);
|
||||
emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, nullptr, EM_FALSE, onFullscreenChanged);
|
||||
|
||||
// 2. ИНИЦИАЛИЗАЦИЯ РАЗМЕРОВ:
|
||||
// Получаем реальные размеры окна браузера на момент запуска
|
||||
int canvasW = EM_ASM_INT({ return window.innerWidth; });
|
||||
int canvasH = EM_ASM_INT({ return window.innerHeight; });
|
||||
|
||||
// Вызываем вашу функцию — она сама применит DPR, выставит физический размер
|
||||
// канваса и отправит SDL_WINDOWEVENT_RESIZED для настройки проекции.
|
||||
applyResize(canvasW, canvasH);
|
||||
|
||||
// 3. Создаем игру и вызываем setup (теперь проекция уже будет знать верный size)
|
||||
g_game = new ZL::Game();
|
||||
g_game->setup();
|
||||
|
||||
SDL_SetHint(SDL_HINT_MOUSE_TOUCH_EVENTS, "0");
|
||||
|
||||
emscripten_set_main_loop(MainLoop, 0, 1);
|
||||
#else*/
|
||||
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0) {
|
||||
SDL_Log("SDL init failed: %s", SDL_GetError());
|
||||
return 1;
|
||||
@ -327,8 +265,6 @@ int main(int argc, char *argv[]) {
|
||||
game.update();
|
||||
SDL_Delay(2);
|
||||
}
|
||||
//#endif
|
||||
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
|
||||
@ -14,7 +14,7 @@ constexpr auto NET_SECRET = "880b3713b9ff3e7a94b2712d54679e1f";
|
||||
#define ENABLE_NETWORK_CHECKSUM
|
||||
|
||||
constexpr float ANGULAR_ACCEL = 0.005f * 1000.0f;
|
||||
constexpr float SHIP_ACCEL = 1.0f * 1000.0f;
|
||||
constexpr float SHIP_ACCEL = 1.0f * 200.0f;
|
||||
constexpr float ROTATION_SENSITIVITY = 0.002f;
|
||||
|
||||
constexpr float PLANET_RADIUS = 20000.f;
|
||||
@ -26,6 +26,7 @@ constexpr float PITCH_LIMIT = static_cast<float>(M_PI) / 9.f;//18.0f;
|
||||
constexpr long long SERVER_DELAY = 0; //ms
|
||||
constexpr long long CLIENT_DELAY = 500; //ms
|
||||
constexpr long long CUTOFF_TIME = 5000; //ms
|
||||
constexpr long long PLAYER_TIMEOUT_MS = 10000; //ms — disconnect if no UPD received
|
||||
|
||||
constexpr float PROJECTILE_VELOCITY = 600.f;
|
||||
constexpr float PROJECTILE_LIFE = 15000.f; //ms
|
||||
|
||||
@ -34,6 +34,7 @@ namespace ZL {
|
||||
public:
|
||||
virtual ~INetworkClient() = default;
|
||||
virtual void Connect(const std::string& host, uint16_t port) = 0;
|
||||
virtual void Disconnect() {}
|
||||
virtual void Send(const std::string& message) = 0;
|
||||
virtual bool IsConnected() const = 0;
|
||||
virtual void Poll() = 0; // ƒл¤ обработки вход¤щих пакетов
|
||||
@ -50,6 +51,7 @@ namespace ZL {
|
||||
virtual int GetClientId() const { return -1; }
|
||||
virtual std::vector<BoxDestroyedInfo> getPendingBoxDestructions() = 0;
|
||||
virtual int64_t getTimeOffset() const { return 0; }
|
||||
virtual std::vector<int> getPendingDisconnects() { return {}; }
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@ -28,6 +28,15 @@ namespace ZL {
|
||||
}
|
||||
}
|
||||
|
||||
void WebSocketClient::Disconnect() {
|
||||
if (!ws_ || !connected) return;
|
||||
connected = false;
|
||||
try {
|
||||
boost::beast::get_lowest_layer(*ws_).cancel();
|
||||
}
|
||||
catch (...) {}
|
||||
}
|
||||
|
||||
void WebSocketClient::startAsyncRead() {
|
||||
ws_->async_read(buffer_, [this](boost::beast::error_code ec, std::size_t bytes) {
|
||||
if (!ec) {
|
||||
|
||||
@ -54,6 +54,7 @@ namespace ZL {
|
||||
{}
|
||||
|
||||
void Connect(const std::string& host, uint16_t port) override;
|
||||
void Disconnect() override;
|
||||
|
||||
void Poll() override;
|
||||
|
||||
|
||||
@ -103,6 +103,19 @@ namespace ZL {
|
||||
|
||||
return;
|
||||
}
|
||||
if (msg.rfind("PLAYER_LEFT:", 0) == 0) {
|
||||
if (parts.size() >= 2) {
|
||||
try {
|
||||
int pid = std::stoi(parts[1]);
|
||||
remotePlayers.erase(pid);
|
||||
pendingDisconnects_.push_back(pid);
|
||||
std::cout << "Client: Player " << pid << " disconnected (PLAYER_LEFT)\n";
|
||||
}
|
||||
catch (...) {}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg.rfind("RESPAWN_ACK:", 0) == 0) {
|
||||
//auto parts = split(msg, ':');
|
||||
if (parts.size() >= 2) {
|
||||
@ -356,6 +369,12 @@ namespace ZL {
|
||||
return copy;
|
||||
}
|
||||
|
||||
std::vector<int> WebSocketClientBase::getPendingDisconnects() {
|
||||
std::vector<int> copy;
|
||||
copy.swap(pendingDisconnects_);
|
||||
return copy;
|
||||
}
|
||||
|
||||
std::vector<ClientState> WebSocketClientBase::getPendingSpawns() {
|
||||
std::vector<ClientState> copy;
|
||||
copy.swap(pendingSpawns_);
|
||||
|
||||
@ -20,6 +20,7 @@ namespace ZL {
|
||||
std::vector<DeathInfo> pendingDeaths_;
|
||||
std::vector<int> pendingRespawns_;
|
||||
std::vector<BoxDestroyedInfo> pendingBoxDestructions_;
|
||||
std::vector<int> pendingDisconnects_;
|
||||
int clientId = -1;
|
||||
int64_t timeOffset = 0;
|
||||
std::vector<ClientState> pendingSpawns_;
|
||||
@ -41,7 +42,7 @@ namespace ZL {
|
||||
return serverBoxes_;
|
||||
}
|
||||
|
||||
std::vector<bool> getServerBoxDestroyedFlags() {
|
||||
std::vector<bool> getServerBoxDestroyedFlags() override {
|
||||
return serverBoxesDestroyed_;
|
||||
}
|
||||
|
||||
@ -49,6 +50,7 @@ namespace ZL {
|
||||
std::vector<DeathInfo> getPendingDeaths() override;
|
||||
std::vector<int> getPendingRespawns() override;
|
||||
std::vector<BoxDestroyedInfo> getPendingBoxDestructions() override;
|
||||
std::vector<int> getPendingDisconnects() override;
|
||||
std::vector<ClientState> getPendingSpawns();
|
||||
int getClientId() const { return clientId; }
|
||||
};
|
||||
|
||||
@ -26,6 +26,15 @@ namespace ZL {
|
||||
connected = false;
|
||||
}
|
||||
|
||||
void WebSocketClientEmscripten::Disconnect() {
|
||||
if (socket_ > 0) {
|
||||
emscripten_websocket_close(socket_, 1000, "User disconnected");
|
||||
emscripten_websocket_delete(socket_);
|
||||
socket_ = 0;
|
||||
}
|
||||
connected = false;
|
||||
}
|
||||
|
||||
void WebSocketClientEmscripten::flushOutgoingQueue() {
|
||||
std::lock_guard<std::mutex> lock(outgoingMutex);
|
||||
if (!socket_) return;
|
||||
|
||||
@ -28,6 +28,7 @@ namespace ZL {
|
||||
virtual ~WebSocketClientEmscripten() = default;
|
||||
|
||||
void Connect(const std::string& host, uint16_t port) override;
|
||||
void Disconnect() override;
|
||||
void Send(const std::string& message) override;
|
||||
void Poll() override;
|
||||
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
#include "StoneObject.h"
|
||||
#include "utils/TaskManager.h"
|
||||
#include "TextModel.h"
|
||||
#include "GameConstants.h"
|
||||
|
||||
namespace ZL {
|
||||
|
||||
@ -185,16 +186,10 @@ namespace ZL {
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
|
||||
static const std::string defaultShaderName2 = "planetBake";
|
||||
static const std::string vPositionName = "vPosition";
|
||||
static const std::string vTexCoordName = "vTexCoord";
|
||||
static const std::string textureUniformName = "Texture";
|
||||
static const std::string planetBakeShaderName = "planetBake";
|
||||
|
||||
|
||||
renderer.shaderManager.PushShader(defaultShaderName2);
|
||||
renderer.shaderManager.PushShader(planetBakeShaderName);
|
||||
renderer.RenderUniform1i(textureUniformName, 0);
|
||||
renderer.EnableVertexAttribArray(vPositionName);
|
||||
renderer.EnableVertexAttribArray(vTexCoordName);
|
||||
|
||||
Triangle tr = planetData.getLodLevel().triangles[0];
|
||||
|
||||
@ -260,8 +255,6 @@ namespace ZL {
|
||||
glDisable(GL_CULL_FACE); // Не забываем выключить, чтобы не сломать остальной рендер
|
||||
renderer.PopMatrix();
|
||||
renderer.PopProjectionMatrix();
|
||||
renderer.DisableVertexAttribArray(vTexCoordName);
|
||||
renderer.DisableVertexAttribArray(vPositionName);
|
||||
renderer.shaderManager.PopShader();
|
||||
CheckGlError();
|
||||
}
|
||||
@ -298,23 +291,12 @@ namespace ZL {
|
||||
|
||||
void PlanetObject::drawPlanet(Renderer& renderer)
|
||||
{
|
||||
static const std::string defaultShaderName = "planetLand";
|
||||
static const std::string planetLandShaderName = "planetLand";
|
||||
|
||||
static const std::string vPositionName = "vPosition";
|
||||
static const std::string vColorName = "vColor";
|
||||
static const std::string vNormalName = "vNormal";
|
||||
static const std::string vTexCoordName = "vTexCoord";
|
||||
static const std::string textureUniformName = "Texture";
|
||||
|
||||
renderer.shaderManager.PushShader(defaultShaderName);
|
||||
renderer.shaderManager.PushShader(planetLandShaderName);
|
||||
|
||||
renderer.EnableVertexAttribArray(vPositionName);
|
||||
renderer.EnableVertexAttribArray(vColorName);
|
||||
renderer.EnableVertexAttribArray(vNormalName);
|
||||
renderer.EnableVertexAttribArray("vTangent");
|
||||
renderer.EnableVertexAttribArray("vBinormal");
|
||||
renderer.EnableVertexAttribArray(vTexCoordName);
|
||||
|
||||
|
||||
float dist = planetData.distanceToPlanetSurfaceFast(Environment::shipState.position);
|
||||
auto zRange = planetData.calculateZRange(dist);
|
||||
@ -335,10 +317,9 @@ namespace ZL {
|
||||
|
||||
const Matrix4f viewMatrix = renderer.GetCurrentModelViewMatrix();
|
||||
|
||||
renderer.RenderUniform1i("Texture", 0);
|
||||
renderer.RenderUniform1i(textureUniformName, 0);
|
||||
renderer.RenderUniform1i("BakedTexture", 1);
|
||||
|
||||
|
||||
Triangle tr = planetData.getLodLevel().triangles[0]; // Берем базовый треугольник
|
||||
Matrix3f mr = GetRotationForTriangle(tr); // Та же матрица, что и при запекании
|
||||
|
||||
@ -376,12 +357,6 @@ namespace ZL {
|
||||
|
||||
renderer.PopMatrix();
|
||||
renderer.PopProjectionMatrix();
|
||||
renderer.DisableVertexAttribArray(vTexCoordName);
|
||||
renderer.DisableVertexAttribArray(vNormalName);
|
||||
renderer.DisableVertexAttribArray("vTangent");
|
||||
renderer.DisableVertexAttribArray("vBinormal");
|
||||
renderer.DisableVertexAttribArray(vColorName);
|
||||
renderer.DisableVertexAttribArray(vPositionName);
|
||||
renderer.shaderManager.PopShader();
|
||||
CheckGlError();
|
||||
|
||||
@ -390,20 +365,10 @@ namespace ZL {
|
||||
|
||||
void PlanetObject::drawStones(Renderer& renderer)
|
||||
{
|
||||
static const std::string defaultShaderName2 = "planetStone";
|
||||
static const std::string vPositionName = "vPosition";
|
||||
static const std::string vColorName = "vColor";
|
||||
static const std::string vNormalName = "vNormal";
|
||||
static const std::string vTexCoordName = "vTexCoord";
|
||||
static const std::string textureUniformName = "Texture";
|
||||
static const std::string planetStoneShaderName = "planetStone";
|
||||
|
||||
renderer.shaderManager.PushShader(defaultShaderName2);
|
||||
renderer.shaderManager.PushShader(planetStoneShaderName);
|
||||
renderer.RenderUniform1i(textureUniformName, 0);
|
||||
renderer.EnableVertexAttribArray(vPositionName);
|
||||
renderer.EnableVertexAttribArray(vColorName);
|
||||
renderer.EnableVertexAttribArray(vNormalName);
|
||||
renderer.EnableVertexAttribArray(vTexCoordName);
|
||||
|
||||
|
||||
float dist = planetData.distanceToPlanetSurfaceFast(Environment::shipState.position);
|
||||
auto zRange = planetData.calculateZRange(dist);
|
||||
@ -468,10 +433,6 @@ namespace ZL {
|
||||
|
||||
renderer.PopMatrix();
|
||||
renderer.PopProjectionMatrix();
|
||||
renderer.DisableVertexAttribArray(vTexCoordName);
|
||||
renderer.DisableVertexAttribArray(vNormalName);
|
||||
renderer.DisableVertexAttribArray(vColorName);
|
||||
renderer.DisableVertexAttribArray(vPositionName);
|
||||
renderer.shaderManager.PopShader();
|
||||
CheckGlError();
|
||||
|
||||
@ -480,16 +441,10 @@ namespace ZL {
|
||||
|
||||
void PlanetObject::drawAtmosphere(Renderer& renderer) {
|
||||
static const std::string defaultShaderName = "defaultAtmosphere";
|
||||
//static const std::string defaultShaderName = "defaultColor";
|
||||
static const std::string vPositionName = "vPosition";
|
||||
static const std::string vNormalName = "vNormal";
|
||||
//glClear(GL_DEPTH_BUFFER_BIT);
|
||||
glDepthMask(GL_FALSE);
|
||||
|
||||
renderer.shaderManager.PushShader(defaultShaderName);
|
||||
renderer.EnableVertexAttribArray(vPositionName);
|
||||
renderer.EnableVertexAttribArray(vNormalName);
|
||||
|
||||
float dist = planetData.distanceToPlanetSurfaceFast(Environment::shipState.position);
|
||||
auto zRange = planetData.calculateZRange(dist);
|
||||
float currentZNear = zRange.first;
|
||||
@ -571,9 +526,6 @@ namespace ZL {
|
||||
glDepthMask(GL_TRUE);
|
||||
renderer.PopMatrix();
|
||||
renderer.PopProjectionMatrix();
|
||||
|
||||
renderer.DisableVertexAttribArray(vNormalName);
|
||||
renderer.DisableVertexAttribArray(vPositionName);
|
||||
renderer.shaderManager.PopShader();
|
||||
CheckGlError();
|
||||
|
||||
@ -581,20 +533,8 @@ namespace ZL {
|
||||
|
||||
void PlanetObject::drawCamp(Renderer& renderer)
|
||||
{
|
||||
static const std::string defaultShaderName2 = "default";
|
||||
static const std::string vPositionName = "vPosition";
|
||||
static const std::string vColorName = "vColor";
|
||||
static const std::string vNormalName = "vNormal";
|
||||
static const std::string vTexCoordName = "vTexCoord";
|
||||
static const std::string textureUniformName = "Texture";
|
||||
|
||||
renderer.shaderManager.PushShader(defaultShaderName2);
|
||||
renderer.shaderManager.PushShader(defaultShaderName);
|
||||
renderer.RenderUniform1i(textureUniformName, 0);
|
||||
renderer.EnableVertexAttribArray(vPositionName);
|
||||
renderer.EnableVertexAttribArray(vColorName);
|
||||
renderer.EnableVertexAttribArray(vNormalName);
|
||||
renderer.EnableVertexAttribArray(vTexCoordName);
|
||||
|
||||
|
||||
float dist = planetData.distanceToPlanetSurfaceFast(Environment::shipState.position);
|
||||
auto zRange = planetData.calculateZRange(dist);
|
||||
@ -655,10 +595,6 @@ namespace ZL {
|
||||
|
||||
renderer.PopMatrix();
|
||||
renderer.PopProjectionMatrix();
|
||||
renderer.DisableVertexAttribArray(vTexCoordName);
|
||||
renderer.DisableVertexAttribArray(vNormalName);
|
||||
renderer.DisableVertexAttribArray(vColorName);
|
||||
renderer.DisableVertexAttribArray(vPositionName);
|
||||
renderer.shaderManager.PopShader();
|
||||
CheckGlError();
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#include "FrameBuffer.h"
|
||||
#include "FrameBuffer.h"
|
||||
#include <iostream>
|
||||
#include "Environment.h"
|
||||
|
||||
@ -15,10 +15,10 @@ namespace ZL {
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
|
||||
|
||||
// Настраиваем фильтрацию
|
||||
// Настраиваем фильтрацию
|
||||
if (useMipmaps) {
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
// Сразу генерируем пустые уровни, чтобы текстура считалась "полной"
|
||||
// Сразу генерируем пустые уровни, чтобы текстура считалась "полной"
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
}
|
||||
else {
|
||||
@ -49,13 +49,13 @@ namespace ZL {
|
||||
|
||||
void FrameBuffer::Bind() {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||
glViewport(0, 0, width, height); // Важно: устанавливаем вьюпорт под размер текстуры
|
||||
glViewport(0, 0, width, height); // Важно: устанавливаем вьюпорт под размер текстуры
|
||||
}
|
||||
|
||||
void FrameBuffer::Unbind() {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
// Здесь желательно возвращать вьюпорт к размерам экрана,
|
||||
// например, через Environment::width/height
|
||||
// Здесь желательно возвращать вьюпорт к размерам экрана,
|
||||
// например, через Environment::width/height
|
||||
glViewport(0, 0, Environment::width, Environment::height);
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
#include "render/OpenGlExtensions.h"
|
||||
#include <memory>
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#include "OpenGlExtensions.h"
|
||||
#include "OpenGlExtensions.h"
|
||||
|
||||
#include "utils/Utils.h"
|
||||
#include <iostream>
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "SDL.h"
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#include "render/Renderer.h"
|
||||
#include "render/Renderer.h"
|
||||
#include <cmath>
|
||||
|
||||
namespace ZL {
|
||||
@ -50,17 +50,14 @@ namespace ZL {
|
||||
|
||||
Matrix4f r;
|
||||
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
r.data()[0] = 2.f / width;
|
||||
r.data()[5] = 2.f / height;
|
||||
r.data()[10] = -1.f / depthRange;
|
||||
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
r.data()[1] = r.data()[2] = r.data()[3] = 0;
|
||||
r.data()[4] = r.data()[6] = r.data()[7] = 0;
|
||||
r.data()[8] = r.data()[9] = r.data()[11] = 0;
|
||||
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> (<28><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>)
|
||||
r.data()[12] = -(xmax + xmin) / width;
|
||||
r.data()[13] = -(ymax + ymin) / height;
|
||||
r.data()[14] = zNear / depthRange;
|
||||
@ -384,7 +381,7 @@ namespace ZL {
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, positionVBO->getBuffer());
|
||||
|
||||
glBufferData(GL_ARRAY_BUFFER, data.PositionData.size() * 12, &data.PositionData[0], GL_STATIC_DRAW);
|
||||
glBufferData(GL_ARRAY_BUFFER, data.PositionData.size() * 12, &data.PositionData[0], GL_DYNAMIC_DRAW);
|
||||
|
||||
if (data.TexCoordData.size() > 0)
|
||||
{
|
||||
@ -395,7 +392,7 @@ namespace ZL {
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, texCoordVBO->getBuffer());
|
||||
|
||||
glBufferData(GL_ARRAY_BUFFER, data.TexCoordData.size() * 8, &data.TexCoordData[0], GL_STATIC_DRAW);
|
||||
glBufferData(GL_ARRAY_BUFFER, data.TexCoordData.size() * 8, &data.TexCoordData[0], GL_DYNAMIC_DRAW);
|
||||
}
|
||||
|
||||
if (data.NormalData.size() > 0)
|
||||
@ -407,7 +404,7 @@ namespace ZL {
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, normalVBO->getBuffer());
|
||||
|
||||
glBufferData(GL_ARRAY_BUFFER, data.NormalData.size() * 12, &data.NormalData[0], GL_STATIC_DRAW);
|
||||
glBufferData(GL_ARRAY_BUFFER, data.NormalData.size() * 12, &data.NormalData[0], GL_DYNAMIC_DRAW);
|
||||
}
|
||||
|
||||
if (data.TangentData.size() > 0)
|
||||
@ -419,7 +416,7 @@ namespace ZL {
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, tangentVBO->getBuffer());
|
||||
|
||||
glBufferData(GL_ARRAY_BUFFER, data.TangentData.size() * 12, &data.TangentData[0], GL_STATIC_DRAW);
|
||||
glBufferData(GL_ARRAY_BUFFER, data.TangentData.size() * 12, &data.TangentData[0], GL_DYNAMIC_DRAW);
|
||||
}
|
||||
|
||||
if (data.BinormalData.size() > 0)
|
||||
@ -431,7 +428,7 @@ namespace ZL {
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, binormalVBO->getBuffer());
|
||||
|
||||
glBufferData(GL_ARRAY_BUFFER, data.BinormalData.size() * 12, &data.BinormalData[0], GL_STATIC_DRAW);
|
||||
glBufferData(GL_ARRAY_BUFFER, data.BinormalData.size() * 12, &data.BinormalData[0], GL_DYNAMIC_DRAW);
|
||||
}
|
||||
|
||||
if (data.ColorData.size() > 0)
|
||||
@ -443,7 +440,7 @@ namespace ZL {
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, colorVBO->getBuffer());
|
||||
|
||||
glBufferData(GL_ARRAY_BUFFER, data.ColorData.size() * 12, &data.ColorData[0], GL_STATIC_DRAW);
|
||||
glBufferData(GL_ARRAY_BUFFER, data.ColorData.size() * 12, &data.ColorData[0], GL_DYNAMIC_DRAW);
|
||||
}
|
||||
}
|
||||
|
||||
@ -703,19 +700,7 @@ namespace ZL {
|
||||
Matrix4f m = Matrix4f::Identity();
|
||||
|
||||
m.block<3, 3>(0, 0) = m3;
|
||||
/*
|
||||
m.m[0] = m3.data()[0];
|
||||
m.m[1] = m3.data()[1];
|
||||
m.m[2] = m3.data()[2];
|
||||
|
||||
m.m[4] = m3.data()[3];
|
||||
m.m[5] = m3.data()[4];
|
||||
m.m[6] = m3.data()[5];
|
||||
|
||||
m.m[8] = m3.data()[6];
|
||||
m.m[9] = m3.data()[7];
|
||||
m.m[10] = m3.data()[8];
|
||||
*/
|
||||
m = ModelviewMatrixStack.top() * m;
|
||||
|
||||
if (ModelviewMatrixStack.size() == 0)
|
||||
@ -733,17 +718,7 @@ namespace ZL {
|
||||
void Renderer::RotateMatrix(const Matrix3f& m3)
|
||||
{
|
||||
Matrix4f m = Matrix4f::Identity();
|
||||
/*m.m[0] = m3.data()[0];
|
||||
m.m[1] = m3.data()[1];
|
||||
m.m[2] = m3.data()[2];
|
||||
|
||||
m.m[4] = m3.data()[3];
|
||||
m.m[5] = m3.data()[4];
|
||||
m.m[6] = m3.data()[5];
|
||||
|
||||
m.m[8] = m3.data()[6];
|
||||
m.m[9] = m3.data()[7];
|
||||
m.m[10] = m3.data()[8];*/
|
||||
m.block<3, 3>(0, 0) = m3;
|
||||
|
||||
m = ModelviewMatrixStack.top() * m;
|
||||
@ -782,22 +757,6 @@ namespace ZL {
|
||||
SetMatrix();
|
||||
}
|
||||
|
||||
|
||||
void Renderer::EnableVertexAttribArray(const std::string& attribName)
|
||||
{
|
||||
|
||||
auto shader = shaderManager.GetCurrentShader();
|
||||
if (shader->attribList.find(attribName) != shader->attribList.end())
|
||||
glEnableVertexAttribArray(shader->attribList[attribName]);
|
||||
}
|
||||
|
||||
void Renderer::DisableVertexAttribArray(const std::string& attribName)
|
||||
{
|
||||
auto shader = shaderManager.GetCurrentShader();
|
||||
if (shader->attribList.find(attribName) != shader->attribList.end())
|
||||
glDisableVertexAttribArray(shader->attribList[attribName]);
|
||||
}
|
||||
|
||||
void Renderer::RenderUniformMatrix3fv(const std::string& uniformName, bool transpose, const float* value)
|
||||
{
|
||||
auto shader = shaderManager.GetCurrentShader();
|
||||
@ -887,8 +846,25 @@ namespace ZL {
|
||||
glVertexAttribPointer(shader->attribList[attribName], 3, GL_FLOAT, GL_FALSE, stride, pointer);
|
||||
}
|
||||
|
||||
void Renderer::DisableVertexAttribArray(const std::string& attribName)
|
||||
{
|
||||
auto shader = shaderManager.GetCurrentShader();
|
||||
auto it = shader->attribList.find(attribName);
|
||||
if (it != shader->attribList.end())
|
||||
glDisableVertexAttribArray(it->second);
|
||||
}
|
||||
|
||||
void Renderer::DrawVertexRenderStruct(const VertexRenderStruct& VertexRenderStruct)
|
||||
{
|
||||
#ifndef EMSCRIPTEN
|
||||
#ifndef __ANDROID__
|
||||
if (VertexRenderStruct.vao) {
|
||||
glBindVertexArray(VertexRenderStruct.vao->getBuffer());
|
||||
shaderManager.EnableVertexAttribArrays();
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
static const std::string vNormal("vNormal");
|
||||
static const std::string vTangent("vTangent");
|
||||
static const std::string vBinormal("vBinormal");
|
||||
@ -902,7 +878,6 @@ namespace ZL {
|
||||
{
|
||||
glBindBuffer(GL_ARRAY_BUFFER, VertexRenderStruct.normalVBO->getBuffer());
|
||||
VertexAttribPointer3fv(vNormal, 0, NULL);
|
||||
EnableVertexAttribArray(vNormal);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -912,7 +887,6 @@ namespace ZL {
|
||||
{
|
||||
glBindBuffer(GL_ARRAY_BUFFER, VertexRenderStruct.tangentVBO->getBuffer());
|
||||
VertexAttribPointer3fv(vTangent, 0, NULL);
|
||||
EnableVertexAttribArray(vTangent);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -922,7 +896,6 @@ namespace ZL {
|
||||
{
|
||||
glBindBuffer(GL_ARRAY_BUFFER, VertexRenderStruct.binormalVBO->getBuffer());
|
||||
VertexAttribPointer3fv(vBinormal, 0, NULL);
|
||||
EnableVertexAttribArray(vBinormal);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -932,7 +905,6 @@ namespace ZL {
|
||||
{
|
||||
glBindBuffer(GL_ARRAY_BUFFER, VertexRenderStruct.colorVBO->getBuffer());
|
||||
VertexAttribPointer3fv(vColor, 0, NULL);
|
||||
EnableVertexAttribArray(vColor);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -942,7 +914,6 @@ namespace ZL {
|
||||
{
|
||||
glBindBuffer(GL_ARRAY_BUFFER, VertexRenderStruct.texCoordVBO->getBuffer());
|
||||
VertexAttribPointer2fv(vTexCoord, 0, NULL);
|
||||
EnableVertexAttribArray(vTexCoord);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -951,7 +922,7 @@ namespace ZL {
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, VertexRenderStruct.positionVBO->getBuffer());
|
||||
VertexAttribPointer3fv(vPosition, 0, NULL);
|
||||
EnableVertexAttribArray(vPosition);
|
||||
//EnableVertexAttribArray(vPosition);
|
||||
|
||||
glDrawArrays(GL_TRIANGLES, 0, static_cast<GLsizei>(VertexRenderStruct.data.PositionData.size()));
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include "render/OpenGlExtensions.h"
|
||||
|
||||
@ -127,13 +127,6 @@ namespace ZL {
|
||||
|
||||
void SetMatrix();
|
||||
|
||||
|
||||
|
||||
void EnableVertexAttribArray(const std::string& attribName);
|
||||
|
||||
void DisableVertexAttribArray(const std::string& attribName);
|
||||
|
||||
|
||||
void RenderUniformMatrix3fv(const std::string& uniformName, bool transpose, const float* value);
|
||||
void RenderUniformMatrix4fv(const std::string& uniformName, bool transpose, const float* value);
|
||||
void RenderUniform1i(const std::string& uniformName, const int value);
|
||||
@ -145,6 +138,8 @@ namespace ZL {
|
||||
|
||||
void VertexAttribPointer3fv(const std::string& attribName, int stride, const char* pointer);
|
||||
|
||||
void DisableVertexAttribArray(const std::string& attribName);
|
||||
|
||||
void DrawVertexRenderStruct(const VertexRenderStruct& VertexRenderStruct);
|
||||
};
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#include "ShaderManager.h"
|
||||
#include "ShaderManager.h"
|
||||
#include <iostream>
|
||||
#include <SDL.h>
|
||||
|
||||
@ -167,7 +167,6 @@ namespace ZL {
|
||||
fragmentShader = readTextFile(fragmentShaderFileName);
|
||||
}
|
||||
|
||||
///std::cout << "Shader: "<< vertexShader << std::endl;
|
||||
shaderResourceMap[shaderName] = std::make_shared<ShaderResource>(vertexShader,
|
||||
fragmentShader);
|
||||
}
|
||||
@ -181,9 +180,26 @@ namespace ZL {
|
||||
throw std::runtime_error("Shader does not exist!");
|
||||
}
|
||||
|
||||
if (shaderStack.size() > 0)
|
||||
{
|
||||
auto& prevShaderAttribList = shaderResourceMap[shaderStack.top()]->attribList;
|
||||
for (auto& attrib : prevShaderAttribList)
|
||||
{
|
||||
glDisableVertexAttribArray(attrib.second);
|
||||
}
|
||||
}
|
||||
|
||||
shaderStack.push(shaderName);
|
||||
|
||||
glUseProgram(shaderResourceMap[shaderName]->getShaderProgram());
|
||||
auto& shaderResource = shaderResourceMap[shaderName];
|
||||
|
||||
glUseProgram(shaderResource->getShaderProgram());
|
||||
|
||||
auto& shaderAttribList = shaderResource->attribList;
|
||||
for (auto& attrib : shaderAttribList)
|
||||
{
|
||||
glEnableVertexAttribArray(attrib.second);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -192,12 +208,39 @@ namespace ZL {
|
||||
throw std::runtime_error("Shader stack underflow!");
|
||||
}
|
||||
|
||||
auto& prevShaderAttribList = shaderResourceMap[shaderStack.top()]->attribList;
|
||||
for (auto& attrib : prevShaderAttribList)
|
||||
{
|
||||
glDisableVertexAttribArray(attrib.second);
|
||||
}
|
||||
|
||||
shaderStack.pop();
|
||||
|
||||
if (shaderStack.size() == 0) {
|
||||
glUseProgram(0);
|
||||
} else {
|
||||
glUseProgram(shaderResourceMap[shaderStack.top()]->getShaderProgram());
|
||||
auto& shaderResource = shaderResourceMap[shaderStack.top()];
|
||||
|
||||
glUseProgram(shaderResource->getShaderProgram());
|
||||
|
||||
auto& shaderAttribList = shaderResource->attribList;
|
||||
for (auto& attrib : shaderAttribList)
|
||||
{
|
||||
glEnableVertexAttribArray(attrib.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderManager::EnableVertexAttribArrays()
|
||||
{
|
||||
if (shaderStack.size() != 0) {
|
||||
auto& shaderResource = shaderResourceMap[shaderStack.top()];
|
||||
|
||||
auto& shaderAttribList = shaderResource->attribList;
|
||||
for (auto& attrib : shaderAttribList)
|
||||
{
|
||||
glEnableVertexAttribArray(attrib.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -42,6 +42,7 @@ namespace ZL {
|
||||
|
||||
void PushShader(const std::string& shaderName);
|
||||
void PopShader();
|
||||
void EnableVertexAttribArrays();
|
||||
|
||||
std::shared_ptr<ShaderResource> GetCurrentShader();
|
||||
};
|
||||
|
||||
@ -380,10 +380,6 @@ void TextRenderer::drawText(const std::string& text, float x, float y, float sca
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, atlasTexture->getTexID());
|
||||
|
||||
|
||||
r->EnableVertexAttribArray("vPosition");
|
||||
r->EnableVertexAttribArray("vTexCoord");
|
||||
|
||||
//for (size_t i = 0; i < text.length(); ++i) {
|
||||
// auto it = glyphs.find(text[i]);
|
||||
// if (it == glyphs.end()) continue;
|
||||
@ -401,9 +397,6 @@ void TextRenderer::drawText(const std::string& text, float x, float y, float sca
|
||||
// glDrawArrays(GL_TRIANGLES, 0, 6);
|
||||
//}
|
||||
r->DrawVertexRenderStruct(cached.mesh);
|
||||
|
||||
r->DisableVertexAttribArray("vPosition");
|
||||
r->DisableVertexAttribArray("vTexCoord");
|
||||
r->shaderManager.PopShader();
|
||||
|
||||
// Сброс бинда текстуры не обязателен, но можно для чистоты
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#include "Perlin.h"
|
||||
#include "Perlin.h"
|
||||
#include <cmath>
|
||||
#include <numeric>
|
||||
#include <random>
|
||||
@ -9,19 +9,19 @@ namespace ZL {
|
||||
PerlinNoise::PerlinNoise() {
|
||||
p.resize(256);
|
||||
std::iota(p.begin(), p.end(), 0);
|
||||
// Перемешиваем для случайности (можно задать seed)
|
||||
// Перемешиваем для случайности (можно задать seed)
|
||||
std::default_random_engine engine(77777);
|
||||
std::shuffle(p.begin(), p.end(), engine);
|
||||
p.insert(p.end(), p.begin(), p.end()); // Дублируем для переполнения
|
||||
p.insert(p.end(), p.begin(), p.end()); // Дублируем для переполнения
|
||||
}
|
||||
|
||||
PerlinNoise::PerlinNoise(uint64_t seed) {
|
||||
p.resize(256);
|
||||
std::iota(p.begin(), p.end(), 0);
|
||||
// Перемешиваем для случайности (используем переданный seed)
|
||||
// Перемешиваем для случайности (используем переданный seed)
|
||||
std::default_random_engine engine(static_cast<unsigned int>(seed));
|
||||
std::shuffle(p.begin(), p.end(), engine);
|
||||
p.insert(p.end(), p.begin(), p.end()); // Дублируем для переполнения
|
||||
p.insert(p.end(), p.begin(), p.end()); // Дублируем для переполнения
|
||||
}
|
||||
|
||||
float PerlinNoise::fade(float t) { return t * t * t * (t * (t * 6 - 15) + 10); }
|
||||
@ -58,13 +58,13 @@ namespace ZL {
|
||||
}
|
||||
|
||||
float PerlinNoise::getSurfaceHeight(Eigen::Vector3f pos, float noiseCoeff) {
|
||||
// Частота шума (чем больше, тем больше "холмов")
|
||||
// Частота шума (чем больше, тем больше "холмов")
|
||||
float frequency = 7.0f;
|
||||
|
||||
// Получаем значение шума (обычно от -1 до 1)
|
||||
// Получаем значение шума (обычно от -1 до 1)
|
||||
float noiseValue = noise(pos(0) * frequency, pos(1) * frequency, pos(2) * frequency);
|
||||
|
||||
// Масштабируем: хотим отклонение от 1.0 до 1.1 (примерно)
|
||||
// Масштабируем: хотим отклонение от 1.0 до 1.1 (примерно)
|
||||
float height = 1.0f + (noiseValue * noiseCoeff);
|
||||
|
||||
return height;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#include "TaskManager.h"
|
||||
#include "TaskManager.h"
|
||||
|
||||
|
||||
namespace ZL
|
||||
@ -27,8 +27,8 @@ namespace ZL
|
||||
|
||||
TaskManager::~TaskManager() {
|
||||
#ifndef EMSCRIPTEN
|
||||
workGuard.reset(); // Разрешаем ioContext.run() завершиться, когда задач не останется
|
||||
ioContext.stop(); // Опционально: немедленная остановка
|
||||
workGuard.reset(); // Разрешаем ioContext.run() завершиться, когда задач не останется
|
||||
ioContext.stop(); // Опционально: немедленная остановка
|
||||
for (auto& t : workers) {
|
||||
if (t.joinable()) t.join();
|
||||
}
|
||||
@ -51,7 +51,7 @@ namespace ZL
|
||||
mainThreadTasks.pop();
|
||||
}
|
||||
#else
|
||||
// Извлекаем только одну задачу, чтобы не блокировать update надолго
|
||||
// Извлекаем только одну задачу, чтобы не блокировать update надолго
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mainThreadMutex);
|
||||
if (!mainThreadTasks.empty()) {
|
||||
@ -62,7 +62,7 @@ namespace ZL
|
||||
#endif
|
||||
|
||||
if (task) {
|
||||
task(); // Здесь выполняется RefreshVBO или загрузка текстуры
|
||||
task(); // Здесь выполняется RefreshVBO или загрузка текстуры
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#ifndef EMSCRIPTEN
|
||||
#include <boost/asio.hpp>
|
||||
@ -23,7 +23,7 @@ namespace ZL {
|
||||
public:
|
||||
TaskManager(size_t threadCount = 2);
|
||||
|
||||
// Метод для добавления фоновой задачи
|
||||
// ћетод дл¤ добавлени¤ фоновой задачи
|
||||
void EnqueueBackgroundTask(std::function<void()> task);
|
||||
|
||||
// Graceful shutdown
|
||||
@ -49,7 +49,7 @@ namespace ZL {
|
||||
public:
|
||||
void EnqueueMainThreadTask(std::function<void()> task);
|
||||
|
||||
// Выполнение задач по одной (или пачкой) за кадр
|
||||
// ¬ыполнение задач по одной (или пачкой) за кадр
|
||||
void processMainThreadTasks();
|
||||
};
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user