Adapting web version
This commit is contained in:
1 parent
7e5aa22fee
commit
2728ca9646
16 files changed
+234
-207
No files matched your search
@@ -1,100 +1,76 @@
|
|||||||
<!doctypehtml>
|
<!DOCTYPE html>
|
||||||
<html lang=en-us>
|
<html lang="en-us">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
|
||||||
|
<title>Space Game</title>
|
||||||
|
<style>
|
||||||
|
body, html {
|
||||||
|
margin: 0; padding: 0; width: 100%; height: 100%;
|
||||||
|
overflow: hidden; background-color: #000;
|
||||||
|
position: fixed; /* Предотвращает pull-to-refresh на Android */
|
||||||
|
}
|
||||||
|
#canvas {
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
top: 0; left: 0;
|
||||||
|
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>
|
||||||
|
|
||||||
<head>
|
<script>
|
||||||
<meta charset=utf-8>
|
var statusElement = document.getElementById("status");
|
||||||
<meta content="text/html; charset=utf-8" http-equiv=Content-Type>
|
var canvas = document.getElementById("canvas");
|
||||||
<title>Space Game</title>
|
|
||||||
<style>
|
var Module = {
|
||||||
body {
|
canvas: canvas,
|
||||||
font-family: arial;
|
setStatus: function(text) {
|
||||||
margin: 0;
|
statusElement.innerHTML = text;
|
||||||
overflow: hidden;
|
statusElement.style.display = text ? 'block' : 'none';
|
||||||
padding: 0;
|
|
||||||
min-height: 100vh;
|
|
||||||
background-color: #000;
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
.emscripten {
|
// Кнопка Fullscreen
|
||||||
padding-right: 0;
|
document.getElementById('fs-button').addEventListener('click', function() {
|
||||||
margin-left: auto;
|
if (!document.fullscreenElement) {
|
||||||
margin-right: auto;
|
document.documentElement.requestFullscreen().catch(e => {
|
||||||
display: block
|
console.error(`Error attempting to enable full-screen mode: ${e.message}`);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
document.exitFullscreen();
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
div.emscripten {
|
// Обработка ориентации
|
||||||
text-align: center
|
window.addEventListener("orientationchange", function() {
|
||||||
}
|
// Chrome на Android обновляет innerWidth/Height не мгновенно.
|
||||||
|
// Ждем завершения анимации поворота.
|
||||||
|
setTimeout(() => {
|
||||||
|
// В Emscripten это вызовет ваш onWindowResized в C++
|
||||||
|
window.dispatchEvent(new Event('resize'));
|
||||||
|
}, 200);
|
||||||
|
});
|
||||||
|
|
||||||
div.emscripten_border {
|
</script>
|
||||||
border: 1px solid #000
|
<script async src="space-game001.js"></script>
|
||||||
}
|
</body>
|
||||||
|
</html>
|
||||||
canvas.emscripten {
|
|
||||||
border: 0 none;
|
|
||||||
background-color: #000;
|
|
||||||
width: 90%;
|
|
||||||
height: 100vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
@-webkit-keyframes rotation {
|
|
||||||
from {
|
|
||||||
-webkit-transform: rotate(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
to {
|
|
||||||
-webkit-transform: rotate(360deg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@-moz-keyframes rotation {
|
|
||||||
from {
|
|
||||||
-moz-transform: rotate(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
to {
|
|
||||||
-moz-transform: rotate(360deg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@-o-keyframes rotation {
|
|
||||||
from {
|
|
||||||
-o-transform: rotate(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
to {
|
|
||||||
-o-transform: rotate(360deg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes rotation {
|
|
||||||
from {
|
|
||||||
transform: rotate(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
to {
|
|
||||||
transform: rotate(360deg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#status {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#progress {
|
|
||||||
height: 20px;
|
|
||||||
width: 300px
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<div class=emscripten id=status></div>
|
|
||||||
<div class=emscripten><progress hidden id=progress max=100 value=0></progress></div>
|
|
||||||
<div class=emscripten_border><canvas class=emscripten id=canvas oncontextmenu=event.preventDefault()
|
|
||||||
tabindex=-1></canvas></div>
|
|
||||||
<script>var statusElement = document.getElementById("status"), progressElement = document.getElementById("progress"), spinnerElement = document.getElementById("spinner"), Module = { print: function () { var e = document.getElementById("output"); return e && (e.value = ""), function (t) { arguments.length > 1 && (t = Array.prototype.slice.call(arguments).join(" ")), console.log(t), e && (e.value += t + "\n", e.scrollTop = e.scrollHeight) } }(), canvas: (() => { var e = document.getElementById("canvas"); return e.addEventListener("webglcontextlost", (e => { alert("WebGL context lost. You will need to reload the page."), e.preventDefault() }), !1), e })(), setStatus: e => { if (Module.setStatus.last || (Module.setStatus.last = { time: Date.now(), text: "" }), e !== Module.setStatus.last.text) { var t = e.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/), n = Date.now(); t && n - Module.setStatus.last.time < 30 || (Module.setStatus.last.time = n, Module.setStatus.last.text = e, t ? (e = t[1], progressElement.value = 100 * parseInt(t[2]), progressElement.max = 100 * parseInt(t[4]), progressElement.hidden = !1, spinnerElement.hidden = !1) : (progressElement.value = null, progressElement.max = null, progressElement.hidden = !0, e || (spinnerElement.style.display = "none")), statusElement.innerHTML = e) } }, totalDependencies: 0, monitorRunDependencies: e => { this.totalDependencies = Math.max(this.totalDependencies, e), Module.setStatus(e ? "Preparing... (" + (this.totalDependencies - e) + "/" + this.totalDependencies + ")" : "All downloads complete.") } }; Module.setStatus("Downloading..."), window.onerror = e => { Module.setStatus("Exception thrown, see JavaScript console"), spinnerElement.style.display = "none", Module.setStatus = e => { e && console.error("[post-exception status] " + e) } }</script>
|
|
||||||
<script async src=space-game001.js></script>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -36,5 +36,25 @@ ClientState Environment::shipState;
|
|||||||
const float Environment::CONST_Z_NEAR = 5.f;
|
const float Environment::CONST_Z_NEAR = 5.f;
|
||||||
const float Environment::CONST_Z_FAR = 5000.f;
|
const float Environment::CONST_Z_FAR = 5000.f;
|
||||||
|
|
||||||
|
float Environment::projectionWidth = 1280.0f;
|
||||||
|
float Environment::projectionHeight = 720.0f;
|
||||||
|
|
||||||
|
void Environment::computeProjectionDimensions()
|
||||||
|
{
|
||||||
|
if (width <= 0 || height <= 0) return;
|
||||||
|
|
||||||
|
const float refShortSide = 720.0f;
|
||||||
|
float aspect = (float)width / (float)height;
|
||||||
|
|
||||||
|
if (width >= height) {
|
||||||
|
// Landscape: fix height to 720, scale width to preserve aspect
|
||||||
|
projectionHeight = refShortSide;
|
||||||
|
projectionWidth = refShortSide * aspect;
|
||||||
|
} else {
|
||||||
|
// Portrait: fix width to 720, scale height to preserve aspect
|
||||||
|
projectionWidth = refShortSide;
|
||||||
|
projectionHeight = refShortSide / aspect;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace ZL
|
} // namespace ZL
|
||||||
+8
-1
@@ -35,8 +35,15 @@ public:
|
|||||||
static const float CONST_Z_NEAR;
|
static const float CONST_Z_NEAR;
|
||||||
static const float CONST_Z_FAR;
|
static const float CONST_Z_FAR;
|
||||||
|
|
||||||
|
// Virtual projection dimensions used for all 2D/UI rendering.
|
||||||
|
// These maintain the screen's actual aspect ratio but normalize the
|
||||||
|
// height to 720 (landscape) or width to 720 (portrait), giving a
|
||||||
|
// consistent coordinate space regardless of physical screen resolution.
|
||||||
|
static float projectionWidth;
|
||||||
|
static float projectionHeight;
|
||||||
|
|
||||||
|
// Call this once at startup and whenever the window is resized.
|
||||||
|
static void computeProjectionDimensions();
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ZL
|
} // namespace ZL
|
||||||
+45
-42
@@ -83,6 +83,8 @@ namespace ZL
|
|||||||
void Game::setup() {
|
void Game::setup() {
|
||||||
glContext = SDL_GL_CreateContext(ZL::Environment::window);
|
glContext = SDL_GL_CreateContext(ZL::Environment::window);
|
||||||
|
|
||||||
|
Environment::computeProjectionDimensions();
|
||||||
|
|
||||||
ZL::BindOpenGlFunctions();
|
ZL::BindOpenGlFunctions();
|
||||||
ZL::CheckGlError();
|
ZL::CheckGlError();
|
||||||
renderer.InitOpenGL();
|
renderer.InitOpenGL();
|
||||||
@@ -99,7 +101,7 @@ namespace ZL
|
|||||||
loadingTexture = std::make_unique<Texture>(CreateTextureDataFromPng("resources/loading.png", CONST_ZIP_FILE));
|
loadingTexture = std::make_unique<Texture>(CreateTextureDataFromPng("resources/loading.png", CONST_ZIP_FILE));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
loadingMesh.data = CreateRect2D({ Environment::width * 0.5, Environment::height * 0.5 }, { Environment::width * 0.5, Environment::height*0.5 }, 3);
|
loadingMesh.data = CreateRect2D({ Environment::projectionWidth * 0.5f, Environment::projectionHeight * 0.5f }, { Environment::projectionWidth * 0.5f, Environment::projectionHeight * 0.5f }, 3);
|
||||||
loadingMesh.RefreshVBO();
|
loadingMesh.RefreshVBO();
|
||||||
|
|
||||||
#ifdef EMSCRIPTEN
|
#ifdef EMSCRIPTEN
|
||||||
@@ -141,17 +143,14 @@ namespace ZL
|
|||||||
Environment::shipState.nickname = nickname;
|
Environment::shipState.nickname = nickname;
|
||||||
Environment::shipState.shipType = shipType;
|
Environment::shipState.shipType = shipType;
|
||||||
|
|
||||||
networkClient = std::make_unique<LocalClient>();
|
auto localClient = new LocalClient;
|
||||||
|
ClientState st = Environment::shipState;
|
||||||
|
st.id = localClient->GetClientId();
|
||||||
|
localClient->setLocalPlayerState(st);
|
||||||
|
|
||||||
|
networkClient = std::unique_ptr<INetworkClient>(localClient);
|
||||||
networkClient->Connect("", 0);
|
networkClient->Connect("", 0);
|
||||||
|
|
||||||
#ifndef NETWORK
|
|
||||||
auto localClient = dynamic_cast<ZL::LocalClient*>(networkClient.get());
|
|
||||||
if (localClient) {
|
|
||||||
ZL::ClientState st = Environment::shipState;
|
|
||||||
st.id = localClient->GetClientId();
|
|
||||||
localClient->setLocalPlayerState(st);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
lastTickCount = 0;
|
lastTickCount = 0;
|
||||||
spaceGameStarted = 1;
|
spaceGameStarted = 1;
|
||||||
};
|
};
|
||||||
@@ -160,8 +159,7 @@ namespace ZL
|
|||||||
Environment::shipState.nickname = nickname;
|
Environment::shipState.nickname = nickname;
|
||||||
Environment::shipState.shipType = shipType;
|
Environment::shipState.shipType = shipType;
|
||||||
|
|
||||||
networkClient = std::make_unique<LocalClient>();
|
|
||||||
#ifdef NETWORK
|
|
||||||
#ifdef EMSCRIPTEN
|
#ifdef EMSCRIPTEN
|
||||||
networkClient = std::make_unique<WebSocketClientEmscripten>();
|
networkClient = std::make_unique<WebSocketClientEmscripten>();
|
||||||
networkClient->Connect("localhost", 8081);
|
networkClient->Connect("localhost", 8081);
|
||||||
@@ -169,19 +167,6 @@ namespace ZL
|
|||||||
networkClient = std::make_unique<WebSocketClient>(taskManager.getIOContext());
|
networkClient = std::make_unique<WebSocketClient>(taskManager.getIOContext());
|
||||||
networkClient->Connect("localhost", 8081);
|
networkClient->Connect("localhost", 8081);
|
||||||
#endif
|
#endif
|
||||||
#else
|
|
||||||
networkClient->Connect("", 0);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef NETWORK
|
|
||||||
auto localClient = dynamic_cast<ZL::LocalClient*>(networkClient.get());
|
|
||||||
if (localClient) {
|
|
||||||
ZL::ClientState st = Environment::shipState;
|
|
||||||
st.id = localClient->GetClientId();
|
|
||||||
localClient->setLocalPlayerState(st);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
if (networkClient) {
|
if (networkClient) {
|
||||||
std::string joinMsg = std::string("JOIN:") + nickname + ":" + std::to_string(shipType);
|
std::string joinMsg = std::string("JOIN:") + nickname + ":" + std::to_string(shipType);
|
||||||
@@ -264,8 +249,8 @@ namespace ZL
|
|||||||
renderer.EnableVertexAttribArray(vPositionName);
|
renderer.EnableVertexAttribArray(vPositionName);
|
||||||
renderer.EnableVertexAttribArray(vTexCoordName);
|
renderer.EnableVertexAttribArray(vTexCoordName);
|
||||||
|
|
||||||
float width = Environment::width;
|
float width = Environment::projectionWidth;
|
||||||
float height = Environment::height;
|
float height = Environment::projectionHeight;
|
||||||
|
|
||||||
renderer.PushProjectionMatrix(
|
renderer.PushProjectionMatrix(
|
||||||
0, width,
|
0, width,
|
||||||
@@ -350,16 +335,18 @@ namespace ZL
|
|||||||
if (event.type == SDL_QUIT) {
|
if (event.type == SDL_QUIT) {
|
||||||
Environment::exitGameLoop = true;
|
Environment::exitGameLoop = true;
|
||||||
}
|
}
|
||||||
#if SDL_VERSION_ATLEAST(2,0,5)
|
|
||||||
else if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_RESIZED) {
|
|
||||||
|
if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_RESIZED) {
|
||||||
// Обновляем размеры и сбрасываем кеш текстов, т.к. меши хранятся в пикселях
|
// Обновляем размеры и сбрасываем кеш текстов, т.к. меши хранятся в пикселях
|
||||||
Environment::width = event.window.data1;
|
Environment::width = event.window.data1;
|
||||||
Environment::height = event.window.data2;
|
Environment::height = event.window.data2;
|
||||||
std::cout << "Window resized: " << Environment::width << "x" << Environment::height << std::endl;
|
Environment::computeProjectionDimensions();
|
||||||
|
std::cout << "Window resized: " << Environment::width << "x" << Environment::height << std::endl;
|
||||||
|
|
||||||
space.clearTextRendererCache();
|
space.clearTextRendererCache();
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
#ifdef __ANDROID__
|
#ifdef __ANDROID__
|
||||||
if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_AC_BACK) {
|
if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_AC_BACK) {
|
||||||
Environment::exitGameLoop = true;
|
Environment::exitGameLoop = true;
|
||||||
@@ -368,22 +355,38 @@ namespace ZL
|
|||||||
|
|
||||||
#ifdef __ANDROID__
|
#ifdef __ANDROID__
|
||||||
if (event.type == SDL_FINGERDOWN) {
|
if (event.type == SDL_FINGERDOWN) {
|
||||||
int mx = static_cast<int>(event.tfinger.x * Environment::width);
|
int mx = static_cast<int>(event.tfinger.x * Environment::projectionWidth);
|
||||||
int my = static_cast<int>(event.tfinger.y * Environment::height);
|
int my = static_cast<int>(event.tfinger.y * Environment::projectionHeight);
|
||||||
handleDown(mx, my);
|
handleDown(mx, my);
|
||||||
}
|
}
|
||||||
else if (event.type == SDL_FINGERUP) {
|
else if (event.type == SDL_FINGERUP) {
|
||||||
int mx = static_cast<int>(event.tfinger.x * Environment::width);
|
int mx = static_cast<int>(event.tfinger.x * Environment::projectionWidth);
|
||||||
int my = static_cast<int>(event.tfinger.y * Environment::height);
|
int my = static_cast<int>(event.tfinger.y * Environment::projectionHeight);
|
||||||
handleUp(mx, my);
|
handleUp(mx, my);
|
||||||
}
|
}
|
||||||
else if (event.type == SDL_FINGERMOTION) {
|
else if (event.type == SDL_FINGERMOTION) {
|
||||||
int mx = static_cast<int>(event.tfinger.x * Environment::width);
|
int mx = static_cast<int>(event.tfinger.x * Environment::projectionWidth);
|
||||||
int my = static_cast<int>(event.tfinger.y * Environment::height);
|
int my = static_cast<int>(event.tfinger.y * Environment::projectionHeight);
|
||||||
handleMotion(mx, my);
|
handleMotion(mx, my);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
if (event.type == SDL_MOUSEBUTTONDOWN) {
|
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*if (event.type == SDL_MOUSEBUTTONDOWN) {
|
||||||
int mx = event.button.x;
|
int mx = event.button.x;
|
||||||
int my = event.button.y;
|
int my = event.button.y;
|
||||||
handleDown(mx, my);
|
handleDown(mx, my);
|
||||||
@@ -397,7 +400,7 @@ namespace ZL
|
|||||||
int mx = event.motion.x;
|
int mx = event.motion.x;
|
||||||
int my = event.motion.y;
|
int my = event.motion.y;
|
||||||
handleMotion(mx, my);
|
handleMotion(mx, my);
|
||||||
}
|
}*/
|
||||||
|
|
||||||
if (event.type == SDL_MOUSEWHEEL) {
|
if (event.type == SDL_MOUSEWHEEL) {
|
||||||
static const float zoomstep = 2.0f;
|
static const float zoomstep = 2.0f;
|
||||||
@@ -454,7 +457,7 @@ namespace ZL
|
|||||||
void Game::handleDown(int mx, int my)
|
void Game::handleDown(int mx, int my)
|
||||||
{
|
{
|
||||||
int uiX = mx;
|
int uiX = mx;
|
||||||
int uiY = Environment::height - my;
|
int uiY = Environment::projectionHeight - my;
|
||||||
|
|
||||||
menuManager.uiManager.onMouseDown(uiX, uiY);
|
menuManager.uiManager.onMouseDown(uiX, uiY);
|
||||||
|
|
||||||
@@ -482,7 +485,7 @@ namespace ZL
|
|||||||
void Game::handleUp(int mx, int my)
|
void Game::handleUp(int mx, int my)
|
||||||
{
|
{
|
||||||
int uiX = mx;
|
int uiX = mx;
|
||||||
int uiY = Environment::height - my;
|
int uiY = Environment::projectionHeight - my;
|
||||||
|
|
||||||
menuManager.uiManager.onMouseUp(uiX, uiY);
|
menuManager.uiManager.onMouseUp(uiX, uiY);
|
||||||
|
|
||||||
@@ -497,7 +500,7 @@ namespace ZL
|
|||||||
void Game::handleMotion(int mx, int my)
|
void Game::handleMotion(int mx, int my)
|
||||||
{
|
{
|
||||||
int uiX = mx;
|
int uiX = mx;
|
||||||
int uiY = Environment::height - my;
|
int uiY = Environment::projectionHeight - my;
|
||||||
|
|
||||||
menuManager.uiManager.onMouseMove(uiX, uiY);
|
menuManager.uiManager.onMouseMove(uiX, uiY);
|
||||||
|
|
||||||
|
|||||||
+27
-27
@@ -159,15 +159,15 @@ namespace ZL
|
|||||||
|
|
||||||
// В пределах экрана?
|
// В пределах экрана?
|
||||||
// (можно оставить, можно клампить)
|
// (можно оставить, можно клампить)
|
||||||
float sx = (ndc.x() * 0.5f + 0.5f) * Environment::width;
|
float sx = (ndc.x() * 0.5f + 0.5f) * Environment::projectionWidth;
|
||||||
float sy = (ndc.y() * 0.5f + 0.5f) * Environment::height;
|
float sy = (ndc.y() * 0.5f + 0.5f) * Environment::projectionHeight;
|
||||||
|
|
||||||
outX = sx;
|
outX = sx;
|
||||||
outY = sy;
|
outY = sy;
|
||||||
|
|
||||||
// Можно отсеять те, что вне:
|
// Можно отсеять те, что вне:
|
||||||
if (sx < -200 || sx > Environment::width + 200) return false;
|
if (sx < -200 || sx > Environment::projectionWidth + 200) return false;
|
||||||
if (sy < -200 || sy > Environment::height + 200) return false;
|
if (sy < -200 || sy > Environment::projectionHeight + 200) return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -296,12 +296,12 @@ namespace ZL
|
|||||||
|
|
||||||
cubemapTexture = std::make_shared<Texture>(
|
cubemapTexture = std::make_shared<Texture>(
|
||||||
std::array<TextureDataStruct, 6>{
|
std::array<TextureDataStruct, 6>{
|
||||||
CreateTextureDataFromPng("resources/sky/space_red.png", CONST_ZIP_FILE),
|
CreateTextureDataFromPng("resources/sky/space1.png", CONST_ZIP_FILE),
|
||||||
CreateTextureDataFromPng("resources/sky/space_red.png", CONST_ZIP_FILE),
|
CreateTextureDataFromPng("resources/sky/space1.png", CONST_ZIP_FILE),
|
||||||
CreateTextureDataFromPng("resources/sky/space_red.png", CONST_ZIP_FILE),
|
CreateTextureDataFromPng("resources/sky/space1.png", CONST_ZIP_FILE),
|
||||||
CreateTextureDataFromPng("resources/sky/space_red.png", CONST_ZIP_FILE),
|
CreateTextureDataFromPng("resources/sky/space1.png", CONST_ZIP_FILE),
|
||||||
CreateTextureDataFromPng("resources/sky/space_red.png", CONST_ZIP_FILE),
|
CreateTextureDataFromPng("resources/sky/space1.png", CONST_ZIP_FILE),
|
||||||
CreateTextureDataFromPng("resources/sky/space_red.png", CONST_ZIP_FILE)
|
CreateTextureDataFromPng("resources/sky/space1.png", CONST_ZIP_FILE)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@@ -859,8 +859,8 @@ namespace ZL
|
|||||||
|
|
||||||
// если ничего не изменилось — не трогаем VBO
|
// если ничего не изменилось — не трогаем VBO
|
||||||
if (crosshairMeshValid &&
|
if (crosshairMeshValid &&
|
||||||
crosshairLastW == Environment::width &&
|
crosshairLastW == Environment::projectionWidth &&
|
||||||
crosshairLastH == Environment::height &&
|
crosshairLastH == Environment::projectionHeight &&
|
||||||
std::abs(crosshairLastAlpha - crosshairCfg.alpha) < 1e-6f &&
|
std::abs(crosshairLastAlpha - crosshairCfg.alpha) < 1e-6f &&
|
||||||
std::abs(crosshairLastThickness - crosshairCfg.thicknessPx) < 1e-6f &&
|
std::abs(crosshairLastThickness - crosshairCfg.thicknessPx) < 1e-6f &&
|
||||||
std::abs(crosshairLastGap - crosshairCfg.gapPx) < 1e-6f &&
|
std::abs(crosshairLastGap - crosshairCfg.gapPx) < 1e-6f &&
|
||||||
@@ -869,18 +869,18 @@ namespace ZL
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
crosshairLastW = Environment::width;
|
crosshairLastW = Environment::projectionWidth;
|
||||||
crosshairLastH = Environment::height;
|
crosshairLastH = Environment::projectionHeight;
|
||||||
crosshairLastAlpha = crosshairCfg.alpha;
|
crosshairLastAlpha = crosshairCfg.alpha;
|
||||||
crosshairLastThickness = crosshairCfg.thicknessPx;
|
crosshairLastThickness = crosshairCfg.thicknessPx;
|
||||||
crosshairLastGap = crosshairCfg.gapPx;
|
crosshairLastGap = crosshairCfg.gapPx;
|
||||||
crosshairLastScaleMul = crosshairCfg.scaleMul;
|
crosshairLastScaleMul = crosshairCfg.scaleMul;
|
||||||
|
|
||||||
float cx = Environment::width * 0.5f;
|
float cx = Environment::projectionWidth * 0.5f;
|
||||||
float cy = Environment::height * 0.5f;
|
float cy = Environment::projectionHeight * 0.5f;
|
||||||
|
|
||||||
// масштаб от reference (стандартно: по высоте)
|
// масштаб от reference (стандартно: по высоте)
|
||||||
float scale = (crosshairCfg.refH > 0) ? (Environment::height / (float)crosshairCfg.refH) : 1.0f;
|
float scale = (crosshairCfg.refH > 0) ? (Environment::projectionHeight / (float)crosshairCfg.refH) : 1.0f;
|
||||||
scale *= crosshairCfg.scaleMul;
|
scale *= crosshairCfg.scaleMul;
|
||||||
|
|
||||||
float thickness = crosshairCfg.thicknessPx * scale;
|
float thickness = crosshairCfg.thicknessPx * scale;
|
||||||
@@ -940,7 +940,7 @@ namespace ZL
|
|||||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
|
||||||
renderer.shaderManager.PushShader("defaultColor");
|
renderer.shaderManager.PushShader("defaultColor");
|
||||||
renderer.PushProjectionMatrix((float)Environment::width, (float)Environment::height, 0.f, 1.f);
|
renderer.PushProjectionMatrix(Environment::projectionWidth, Environment::projectionHeight, 0.f, 1.f);
|
||||||
renderer.PushMatrix();
|
renderer.PushMatrix();
|
||||||
renderer.LoadIdentity();
|
renderer.LoadIdentity();
|
||||||
|
|
||||||
@@ -1187,15 +1187,15 @@ namespace ZL
|
|||||||
|
|
||||||
// 4) Настройки стиля
|
// 4) Настройки стиля
|
||||||
Eigen::Vector4f enemyColor(1.f, 0.f, 0.f, 1.f); // красный
|
Eigen::Vector4f enemyColor(1.f, 0.f, 0.f, 1.f); // красный
|
||||||
float thickness = 10.0f; // толщина линий (px)
|
float thickness = 2.0f; // толщина линий (px)
|
||||||
float z = 0.0f; // 2D слой
|
float z = 0.0f; // 2D слой
|
||||||
|
|
||||||
// 5) Если цель в кадре: рисуем скобки
|
// 5) Если цель в кадре: рисуем скобки
|
||||||
if (onScreen)
|
if (onScreen)
|
||||||
{
|
{
|
||||||
// перевод NDC -> экран (в пикселях)
|
// перевод NDC -> экран (в пикселях)
|
||||||
float sx = (ndcX * 0.5f + 0.5f) * Environment::width;
|
float sx = (ndcX * 0.5f + 0.5f) * Environment::projectionWidth;
|
||||||
float sy = (ndcY * 0.5f + 0.5f) * Environment::height;
|
float sy = (ndcY * 0.5f + 0.5f) * Environment::projectionHeight;
|
||||||
|
|
||||||
// анимация “снаружи внутрь”
|
// анимация “снаружи внутрь”
|
||||||
// targetAcquireAnim растёт к 1, быстро (похоже на захват)
|
// targetAcquireAnim растёт к 1, быстро (похоже на захват)
|
||||||
@@ -1234,7 +1234,7 @@ namespace ZL
|
|||||||
glClear(GL_DEPTH_BUFFER_BIT);
|
glClear(GL_DEPTH_BUFFER_BIT);
|
||||||
|
|
||||||
renderer.shaderManager.PushShader("defaultColor");
|
renderer.shaderManager.PushShader("defaultColor");
|
||||||
renderer.PushProjectionMatrix((float)Environment::width, (float)Environment::height, 0.f, 1.f);
|
renderer.PushProjectionMatrix(Environment::projectionWidth, Environment::projectionHeight, 0.f, 1.f);
|
||||||
renderer.PushMatrix();
|
renderer.PushMatrix();
|
||||||
renderer.LoadIdentity();
|
renderer.LoadIdentity();
|
||||||
|
|
||||||
@@ -1248,8 +1248,8 @@ namespace ZL
|
|||||||
float leadNdcX, leadNdcY, leadNdcZ, leadClipW;
|
float leadNdcX, leadNdcY, leadNdcZ, leadClipW;
|
||||||
if (projectToNDC(leadWorld, leadNdcX, leadNdcY, leadNdcZ, leadClipW) && leadClipW > 0.0f) {
|
if (projectToNDC(leadWorld, leadNdcX, leadNdcY, leadNdcZ, leadClipW) && leadClipW > 0.0f) {
|
||||||
if (leadNdcX >= -1 && leadNdcX <= 1 && leadNdcY >= -1 && leadNdcY <= 1) {
|
if (leadNdcX >= -1 && leadNdcX <= 1 && leadNdcY >= -1 && leadNdcY <= 1) {
|
||||||
float lx = (leadNdcX * 0.5f + 0.5f) * Environment::width;
|
float lx = (leadNdcX * 0.5f + 0.5f) * Environment::projectionWidth;
|
||||||
float ly = (leadNdcY * 0.5f + 0.5f) * Environment::height;
|
float ly = (leadNdcY * 0.5f + 0.5f) * Environment::projectionHeight;
|
||||||
|
|
||||||
float distLead = (Environment::shipState.position - leadWorld).norm();
|
float distLead = (Environment::shipState.position - leadWorld).norm();
|
||||||
float r = 30.0f / (distLead * 0.01f + 1.0f);
|
float r = 30.0f / (distLead * 0.01f + 1.0f);
|
||||||
@@ -1323,8 +1323,8 @@ namespace ZL
|
|||||||
float edgeNdcX = dirX * k;
|
float edgeNdcX = dirX * k;
|
||||||
float edgeNdcY = dirY * k;
|
float edgeNdcY = dirY * k;
|
||||||
|
|
||||||
float edgeX = (edgeNdcX * 0.5f + 0.5f) * Environment::width;
|
float edgeX = (edgeNdcX * 0.5f + 0.5f) * Environment::projectionWidth;
|
||||||
float edgeY = (edgeNdcY * 0.5f + 0.5f) * Environment::height;
|
float edgeY = (edgeNdcY * 0.5f + 0.5f) * Environment::projectionHeight;
|
||||||
|
|
||||||
float bob = std::sin(t * 6.0f) * 6.0f;
|
float bob = std::sin(t * 6.0f) * 6.0f;
|
||||||
edgeX += dirX * bob;
|
edgeX += dirX * bob;
|
||||||
@@ -1367,7 +1367,7 @@ namespace ZL
|
|||||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
|
||||||
renderer.shaderManager.PushShader("defaultColor");
|
renderer.shaderManager.PushShader("defaultColor");
|
||||||
renderer.PushProjectionMatrix((float)Environment::width, (float)Environment::height, 0.f, 1.f);
|
renderer.PushProjectionMatrix(Environment::projectionWidth, Environment::projectionHeight, 0.f, 1.f);
|
||||||
renderer.PushMatrix();
|
renderer.PushMatrix();
|
||||||
renderer.LoadIdentity();
|
renderer.LoadIdentity();
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -657,7 +657,7 @@ namespace ZL {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void UiManager::draw(Renderer& renderer) {
|
void UiManager::draw(Renderer& renderer) {
|
||||||
renderer.PushProjectionMatrix(Environment::width, Environment::height, -1, 1);
|
renderer.PushProjectionMatrix(Environment::projectionWidth, Environment::projectionHeight, -1, 1);
|
||||||
renderer.PushMatrix();
|
renderer.PushMatrix();
|
||||||
renderer.LoadIdentity();
|
renderer.LoadIdentity();
|
||||||
|
|
||||||
|
|||||||
+45
-22
@@ -44,20 +44,34 @@ EM_BOOL onWebGLContextRestored(int /*eventType*/, const void* /*reserved*/, void
|
|||||||
return EM_TRUE;
|
return EM_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resize the canvas, notify SDL, and push a synthetic SDL_WINDOWEVENT_RESIZED
|
static void applyResize(int logicalW, int logicalH) {
|
||||||
// so Game::update()'s existing handler updates Environment::width/height and clears caches.
|
// Получаем коэффициент плотности пикселей (например, 2.625 на Pixel или 3.0 на Samsung)
|
||||||
static void applyResize(int w, int h) {
|
double dpr = emscripten_get_device_pixel_ratio();
|
||||||
if (w <= 0 || h <= 0) return;
|
|
||||||
// Resize the actual WebGL canvas — without this the rendered pixels stay at
|
// Вычисляем реальные физические пиксели
|
||||||
// the original size no matter what Environment::width/height say.
|
int physicalW = static_cast<int>(logicalW * dpr);
|
||||||
emscripten_set_canvas_element_size("#canvas", w, h);
|
int physicalH = static_cast<int>(logicalH * dpr);
|
||||||
if (ZL::Environment::window)
|
|
||||||
SDL_SetWindowSize(ZL::Environment::window, w, h);
|
// Устанавливаем размер внутреннего буфера канваса
|
||||||
|
emscripten_set_canvas_element_size("#canvas", physicalW, physicalH);
|
||||||
|
|
||||||
|
// Сообщаем SDL о новом размере.
|
||||||
|
// ВАЖНО: SDL2 в Emscripten ожидает здесь именно физические пиксели
|
||||||
|
// для корректной работы последующих вызовов glViewport.
|
||||||
|
if (ZL::Environment::window) {
|
||||||
|
SDL_SetWindowSize(ZL::Environment::window, physicalW, physicalH);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Обновляем ваши внутренние переменные окружения
|
||||||
|
ZL::Environment::width = physicalW;
|
||||||
|
ZL::Environment::height = physicalH;
|
||||||
|
|
||||||
|
// Пушим событие, чтобы движок пересчитал матрицы проекции
|
||||||
SDL_Event e = {};
|
SDL_Event e = {};
|
||||||
e.type = SDL_WINDOWEVENT;
|
e.type = SDL_WINDOWEVENT;
|
||||||
e.window.event = SDL_WINDOWEVENT_RESIZED;
|
e.window.event = SDL_WINDOWEVENT_RESIZED;
|
||||||
e.window.data1 = w;
|
e.window.data1 = physicalW;
|
||||||
e.window.data2 = h;
|
e.window.data2 = physicalH;
|
||||||
SDL_PushEvent(&e);
|
SDL_PushEvent(&e);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,17 +83,11 @@ EM_BOOL onWindowResized(int /*eventType*/, const EmscriptenUiEvent* e, void* /*u
|
|||||||
}
|
}
|
||||||
|
|
||||||
EM_BOOL onFullscreenChanged(int /*eventType*/, const EmscriptenFullscreenChangeEvent* e, void* /*userData*/) {
|
EM_BOOL onFullscreenChanged(int /*eventType*/, const EmscriptenFullscreenChangeEvent* e, void* /*userData*/) {
|
||||||
if (e->isFullscreen) {
|
// Вместо window.innerWidth, попробуйте запросить размер целевого элемента
|
||||||
// e->screenWidth/screenHeight comes from screen.width/screen.height in JS,
|
// так как после перехода в FS именно он растягивается на весь экран.
|
||||||
// which on mobile browsers returns physical pixels (e.g. 2340x1080),
|
double clientW, clientH;
|
||||||
// causing the canvas to extend far off-screen. window.innerWidth/innerHeight
|
emscripten_get_element_css_size("#canvas", &clientW, &clientH);
|
||||||
// always gives CSS logical pixels and is correct on both desktop and mobile.
|
applyResize(clientW, clientH);
|
||||||
int w = EM_ASM_INT({ return window.innerWidth; });
|
|
||||||
int h = EM_ASM_INT({ return window.innerHeight; });
|
|
||||||
applyResize(w, h);
|
|
||||||
}
|
|
||||||
// Exiting fullscreen: the browser fires a window resize event next,
|
|
||||||
// which onWindowResized handles automatically.
|
|
||||||
return EM_FALSE;
|
return EM_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -226,6 +234,21 @@ int main(int argc, char *argv[]) {
|
|||||||
// Keep Environment::width/height in sync when the canvas is resized.
|
// 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_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, nullptr, EM_FALSE, onWindowResized);
|
||||||
emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, nullptr, EM_FALSE, onFullscreenChanged);
|
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);
|
emscripten_set_main_loop(MainLoop, 0, 1);
|
||||||
#else
|
#else
|
||||||
|
|||||||
@@ -362,9 +362,10 @@ void TextRenderer::drawText(const std::string& text, float x, float y, float sca
|
|||||||
// 4. Рендеринг
|
// 4. Рендеринг
|
||||||
r->shaderManager.PushShader(shaderName);
|
r->shaderManager.PushShader(shaderName);
|
||||||
|
|
||||||
// Матрица проекции (экрана)
|
// Матрица проекции — используем виртуальные проекционные размеры,
|
||||||
float W = (float)Environment::width;
|
// чтобы координаты текста были независимы от физического разрешения экрана.
|
||||||
float H = (float)Environment::height;
|
float W = Environment::projectionWidth;
|
||||||
|
float H = Environment::projectionHeight;
|
||||||
Eigen::Matrix4f proj = Eigen::Matrix4f::Identity();
|
Eigen::Matrix4f proj = Eigen::Matrix4f::Identity();
|
||||||
proj(0, 0) = 2.0f / W;
|
proj(0, 0) = 2.0f / W;
|
||||||
proj(1, 1) = 2.0f / H;
|
proj(1, 1) = 2.0f / H;
|
||||||
|
|||||||
Reference in new issue
Block a user