add ui.json and del default values from SparkEmitter
This commit is contained in:
parent
53ea1f768c
commit
068c319890
@ -440,6 +440,8 @@ add_executable(space-game001
|
||||
SparkEmitter.h
|
||||
PlanetObject.cpp
|
||||
PlanetObject.h
|
||||
UiManager.cpp
|
||||
UiManager.h
|
||||
)
|
||||
|
||||
# Установка проекта по умолчанию для Visual Studio
|
||||
|
||||
29
Game.cpp
29
Game.cpp
@ -163,6 +163,7 @@ namespace ZL
|
||||
#endif
|
||||
|
||||
bool cfgLoaded = sparkEmitter.loadFromJsonFile("../config/spark_config.json", renderer, CONST_ZIP_FILE);
|
||||
uiManager.loadFromFile("../config/ui.json", renderer, CONST_ZIP_FILE);
|
||||
|
||||
cubemapTexture = std::make_shared<Texture>(
|
||||
std::array<TextureDataStruct, 6>{
|
||||
@ -208,7 +209,7 @@ namespace ZL
|
||||
}
|
||||
|
||||
|
||||
buttonTexture = std::make_unique<Texture>(CreateTextureDataFromPng("./resources/button.png", CONST_ZIP_FILE));
|
||||
/* buttonTexture = std::make_unique<Texture>(CreateTextureDataFromPng("./resources/button.png", CONST_ZIP_FILE));
|
||||
|
||||
button.data.PositionData.push_back({ 100, 100, 0 });
|
||||
button.data.PositionData.push_back({ 100, 150, 0 });
|
||||
@ -224,7 +225,7 @@ namespace ZL
|
||||
button.data.TexCoordData.push_back({ 1,1 });
|
||||
button.data.TexCoordData.push_back({ 1,0 });
|
||||
|
||||
button.RefreshVBO();
|
||||
button.RefreshVBO();*/
|
||||
|
||||
musicVolumeBarTexture = std::make_unique<Texture>(CreateTextureDataFromPng("./resources/musicVolumeBarTexture.png", CONST_ZIP_FILE));
|
||||
|
||||
@ -436,9 +437,8 @@ namespace ZL
|
||||
|
||||
renderer.LoadIdentity();
|
||||
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, buttonTexture->getTexID());
|
||||
renderer.DrawVertexRenderStruct(button);
|
||||
//glBindTexture(GL_TEXTURE_2D, buttonTexture->getTexID());
|
||||
//renderer.DrawVertexRenderStruct(button);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, musicVolumeBarTexture->getTexID());
|
||||
renderer.DrawVertexRenderStruct(musicVolumeBar);
|
||||
@ -450,7 +450,7 @@ namespace ZL
|
||||
renderer.PopProjectionMatrix();
|
||||
renderer.DisableVertexAttribArray(vPositionName);
|
||||
renderer.DisableVertexAttribArray(vTexCoordName);
|
||||
|
||||
uiManager.draw(renderer);
|
||||
renderer.shaderManager.PopShader();
|
||||
CheckGlError();
|
||||
}
|
||||
@ -518,7 +518,6 @@ namespace ZL
|
||||
float diffx = Environment::tapDownCurrentPos.v[0] - Environment::tapDownStartPos.v[0];
|
||||
float diffy = Environment::tapDownCurrentPos.v[1] - Environment::tapDownStartPos.v[1];
|
||||
|
||||
|
||||
if (abs(diffy) > 5.0 || abs(diffx) > 5.0) //threshold
|
||||
{
|
||||
|
||||
@ -586,6 +585,10 @@ namespace ZL
|
||||
std::cout << mx << " " << my << '\n';
|
||||
int uiX = mx;
|
||||
int uiY = Environment::height - my;
|
||||
|
||||
uiManager.onMouseMove(uiX, uiY);
|
||||
uiManager.onMouseDown(uiX, uiY);
|
||||
|
||||
if (uiX >= volumeBarMinX - 40 && uiX <= volumeBarMaxX + 40 &&
|
||||
uiY >= volumeBarMinY - 40 && uiY <= volumeBarMaxY + 40) {
|
||||
isDraggingVolume = true;
|
||||
@ -604,6 +607,13 @@ namespace ZL
|
||||
}
|
||||
else if (event.type == SDL_MOUSEBUTTONUP) {
|
||||
// 2. Îáðàáîòêà îòïóñêàíèÿ êíîïêè ìûøè
|
||||
int mx = event.button.x;
|
||||
int my = event.button.y;
|
||||
int uiX = mx;
|
||||
int uiY = Environment::height - my;
|
||||
|
||||
uiManager.onMouseUp(uiX, uiY);
|
||||
|
||||
isDraggingVolume = false;
|
||||
Environment::tapDownHold = false;
|
||||
}
|
||||
@ -612,6 +622,11 @@ namespace ZL
|
||||
int mx = event.motion.x;
|
||||
int my = event.motion.y;
|
||||
|
||||
int uiX = mx;
|
||||
int uiY = Environment::height - my;
|
||||
|
||||
uiManager.onMouseMove(uiX, uiY);
|
||||
|
||||
if (isDraggingVolume) {
|
||||
// Äâèãàåì ìûøü ïî ñëàéäåðó — ìåíÿåì ãðîìêîñòü è ïîçèöèþ êðóæêà
|
||||
UpdateVolumeFromMouse(mx, my);
|
||||
|
||||
2
Game.h
2
Game.h
@ -6,6 +6,7 @@
|
||||
#include "TextureManager.h"
|
||||
#include "SparkEmitter.h"
|
||||
#include "PlanetObject.h"
|
||||
#include "UiManager.h"
|
||||
|
||||
namespace ZL {
|
||||
|
||||
@ -86,6 +87,7 @@ namespace ZL {
|
||||
|
||||
SparkEmitter sparkEmitter;
|
||||
PlanetObject planetObject;
|
||||
UiManager uiManager;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
#include "external/nlohmann/json.hpp"
|
||||
#include <iostream>
|
||||
#include "Environment.h"
|
||||
#include <stdexcept>
|
||||
|
||||
namespace ZL {
|
||||
|
||||
@ -13,7 +14,7 @@ namespace ZL {
|
||||
|
||||
SparkEmitter::SparkEmitter()
|
||||
: emissionRate(100.0f), isActive(true), drawDataDirty(true), maxParticles(200),
|
||||
shaderProgramName("default"), particleSize(0.04f), biasX(0.3f) {
|
||||
shaderProgramName("default"), particleSize(0.04f), biasX(0.3f), configured(false) {
|
||||
particles.resize(maxParticles);
|
||||
drawPositions.reserve(maxParticles * 6);
|
||||
drawTexCoords.reserve(maxParticles * 6);
|
||||
@ -25,7 +26,7 @@ namespace ZL {
|
||||
SparkEmitter::SparkEmitter(const std::vector<Vector3f>& positions, float rate)
|
||||
: emissionPoints(positions), emissionRate(rate), isActive(true),
|
||||
drawDataDirty(true), maxParticles(positions.size() * 100),
|
||||
shaderProgramName("default"), particleSize(0.04f), biasX(0.3f) {
|
||||
shaderProgramName("default"), particleSize(0.04f), biasX(0.3f), configured(false) {
|
||||
particles.resize(maxParticles);
|
||||
drawPositions.reserve(maxParticles * 6);
|
||||
drawTexCoords.reserve(maxParticles * 6);
|
||||
@ -39,7 +40,7 @@ namespace ZL {
|
||||
float rate)
|
||||
: emissionPoints(positions), texture(tex), emissionRate(rate),
|
||||
isActive(true), drawDataDirty(true), maxParticles(positions.size() * 100),
|
||||
shaderProgramName("default"), particleSize(0.04f), biasX(0.3f) {
|
||||
shaderProgramName("default"), particleSize(0.04f), biasX(0.3f), configured(false) {
|
||||
particles.resize(maxParticles);
|
||||
drawPositions.reserve(maxParticles * 6);
|
||||
drawTexCoords.reserve(maxParticles * 6);
|
||||
@ -105,12 +106,16 @@ namespace ZL {
|
||||
}
|
||||
|
||||
void SparkEmitter::draw(Renderer& renderer, float zoom, int screenWidth, int screenHeight) {
|
||||
if (!configured) {
|
||||
throw std::runtime_error("Failed to load spark emitter config file!");
|
||||
}
|
||||
|
||||
if (getActiveParticleCount() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!texture) {
|
||||
return;
|
||||
throw std::runtime_error("Failed to load spark emitter config file!");
|
||||
}
|
||||
|
||||
prepareDrawData();
|
||||
@ -157,6 +162,10 @@ namespace ZL {
|
||||
}
|
||||
|
||||
void SparkEmitter::update(float deltaTimeMs) {
|
||||
if (!configured) {
|
||||
throw std::runtime_error("Failed to load spark emitter config file!");
|
||||
}
|
||||
|
||||
auto currentTime = std::chrono::steady_clock::now();
|
||||
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
currentTime - lastEmissionTime).count();
|
||||
@ -203,6 +212,10 @@ namespace ZL {
|
||||
}
|
||||
|
||||
void SparkEmitter::emit() {
|
||||
if (!configured) {
|
||||
throw std::runtime_error("Failed to load spark emitter config file!");
|
||||
}
|
||||
|
||||
if (emissionPoints.empty()) return;
|
||||
bool emitted = false;
|
||||
|
||||
@ -253,6 +266,10 @@ namespace ZL {
|
||||
}
|
||||
|
||||
void SparkEmitter::initParticle(SparkParticle& particle, int emitterIndex) {
|
||||
if (!configured) {
|
||||
throw std::runtime_error("Failed to load spark emitter config file!");
|
||||
}
|
||||
|
||||
particle.velocity = getRandomVelocity(emitterIndex);
|
||||
static std::random_device rd;
|
||||
static std::mt19937 gen(rd());
|
||||
@ -265,6 +282,10 @@ namespace ZL {
|
||||
}
|
||||
|
||||
Vector3f SparkEmitter::getRandomVelocity(int emitterIndex) {
|
||||
if (!configured) {
|
||||
throw std::runtime_error("Failed to load spark emitter config file!");
|
||||
}
|
||||
|
||||
static std::random_device rd;
|
||||
static std::mt19937 gen(rd());
|
||||
std::uniform_real_distribution<float> angleDist(0.0f, 2.0f * static_cast<float>(M_PI));
|
||||
@ -311,7 +332,7 @@ namespace ZL {
|
||||
std::ifstream in(path);
|
||||
if (!in.is_open()) {
|
||||
std::cerr << "Failed to open JSON file: " << path << std::endl;
|
||||
return false;
|
||||
throw std::runtime_error("Failed to load spark emitter config file!");
|
||||
}
|
||||
|
||||
json j;
|
||||
@ -321,32 +342,42 @@ namespace ZL {
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "JSON parse error: " << e.what() << std::endl;
|
||||
return false;
|
||||
throw std::runtime_error("Failed to load spark emitter config file!");
|
||||
}
|
||||
std::cout << "JSON content: " << j.dump(2) << std::endl;
|
||||
|
||||
// Основные параметры
|
||||
if (j.contains("emissionRate")) {
|
||||
emissionRate = j["emissionRate"].get<float>();
|
||||
auto requireKey = [&](const std::string& key) {
|
||||
if (!j.contains(key)) {
|
||||
std::cerr << "Missing required key in spark JSON: " << key << std::endl;
|
||||
throw std::runtime_error("Failed to load spark emitter config file!");
|
||||
}
|
||||
};
|
||||
|
||||
requireKey("emissionPoints");
|
||||
requireKey("texture");
|
||||
requireKey("speedRange");
|
||||
requireKey("zSpeedRange");
|
||||
requireKey("scaleRange");
|
||||
requireKey("lifeTimeRange");
|
||||
requireKey("emissionRate");
|
||||
requireKey("maxParticles");
|
||||
requireKey("particleSize");
|
||||
requireKey("biasX");
|
||||
requireKey("shaderProgramName");
|
||||
|
||||
emissionRate = j["emissionRate"].get<float>();
|
||||
|
||||
if (j.contains("maxParticles")) {
|
||||
maxParticles = j["maxParticles"].get<int>();
|
||||
particles.resize(maxParticles);
|
||||
drawPositions.reserve(maxParticles * 6);
|
||||
drawTexCoords.reserve(maxParticles * 6);
|
||||
std::cout << "Max particles: " << maxParticles << std::endl;
|
||||
}
|
||||
|
||||
if (j.contains("particleSize")) {
|
||||
particleSize = j["particleSize"].get<float>();
|
||||
std::cout << "Particle size: " << particleSize << std::endl;
|
||||
}
|
||||
|
||||
if (j.contains("biasX")) {
|
||||
biasX = j["biasX"].get<float>();
|
||||
std::cout << "Bias X: " << biasX << std::endl;
|
||||
}
|
||||
|
||||
// emissionPoints
|
||||
std::vector<Vector3f> points;
|
||||
@ -382,6 +413,14 @@ namespace ZL {
|
||||
setEmissionPoints(points);
|
||||
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!");
|
||||
}
|
||||
}
|
||||
else {
|
||||
std::cerr << "emissionPoints is not array" << std::endl;
|
||||
throw std::runtime_error("Failed to load spark emitter config file!");
|
||||
}
|
||||
|
||||
// Ranges
|
||||
@ -391,6 +430,10 @@ namespace ZL {
|
||||
speedRange.max = a[1].get<float>();
|
||||
std::cout << "Speed range: [" << speedRange.min << ", " << speedRange.max << "]" << std::endl;
|
||||
}
|
||||
else {
|
||||
std::cerr << "speedRange missing or invalid" << std::endl;
|
||||
throw std::runtime_error("Failed to load spark emitter config file!");
|
||||
}
|
||||
|
||||
if (j.contains("zSpeedRange") && j["zSpeedRange"].is_array()) {
|
||||
auto a = j["zSpeedRange"];
|
||||
@ -398,6 +441,10 @@ namespace ZL {
|
||||
zSpeedRange.max = a[1].get<float>();
|
||||
std::cout << "Z speed range: [" << zSpeedRange.min << ", " << zSpeedRange.max << "]" << std::endl;
|
||||
}
|
||||
else {
|
||||
std::cerr << "zSpeedRange missing or invalid" << std::endl;
|
||||
throw std::runtime_error("Failed to load spark emitter config file!");
|
||||
}
|
||||
|
||||
if (j.contains("scaleRange") && j["scaleRange"].is_array()) {
|
||||
auto a = j["scaleRange"];
|
||||
@ -405,6 +452,10 @@ namespace ZL {
|
||||
scaleRange.max = a[1].get<float>();
|
||||
std::cout << "Scale range: [" << scaleRange.min << ", " << scaleRange.max << "]" << std::endl;
|
||||
}
|
||||
else {
|
||||
std::cerr << "scaleRange missing or invalid" << std::endl;
|
||||
throw std::runtime_error("Failed to load spark emitter config file!");
|
||||
}
|
||||
|
||||
if (j.contains("lifeTimeRange") && j["lifeTimeRange"].is_array()) {
|
||||
auto a = j["lifeTimeRange"];
|
||||
@ -412,6 +463,10 @@ namespace ZL {
|
||||
lifeTimeRange.max = a[1].get<float>();
|
||||
std::cout << "Life time range: [" << lifeTimeRange.min << ", " << lifeTimeRange.max << "]" << std::endl;
|
||||
}
|
||||
else {
|
||||
std::cerr << "lifeTimeRange missing or invalid" << std::endl;
|
||||
throw std::runtime_error("Failed to load spark emitter config file!");
|
||||
}
|
||||
|
||||
// texture
|
||||
if (j.contains("texture") && j["texture"].is_string()) {
|
||||
@ -425,12 +480,12 @@ namespace ZL {
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "Texture load error: " << e.what() << std::endl;
|
||||
return false;
|
||||
throw std::runtime_error("Failed to load spark emitter config file!");
|
||||
}
|
||||
}
|
||||
else {
|
||||
std::cout << "No texture specified in JSON" << std::endl;
|
||||
return false;
|
||||
std::cerr << "No texture specified or invalid type in JSON" << std::endl;
|
||||
throw std::runtime_error("Failed to load spark emitter config file!");
|
||||
}
|
||||
|
||||
// shaders
|
||||
@ -438,6 +493,10 @@ namespace ZL {
|
||||
shaderProgramName = j["shaderProgramName"].get<std::string>();
|
||||
std::cout << "Shader program name: " << shaderProgramName << std::endl;
|
||||
}
|
||||
else {
|
||||
std::cerr << "shaderProgramName missing or invalid" << std::endl;
|
||||
throw std::runtime_error("Failed to load spark emitter config file!");
|
||||
}
|
||||
|
||||
if (j.contains("vertexShader") && j.contains("fragmentShader")
|
||||
&& j["vertexShader"].is_string() && j["fragmentShader"].is_string()) {
|
||||
@ -451,12 +510,12 @@ namespace ZL {
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "Shader load error: " << e.what() << std::endl;
|
||||
std::cerr << "Using default shader" << std::endl;
|
||||
shaderProgramName = "default";
|
||||
throw std::runtime_error("Failed to load spark emitter config file!");
|
||||
}
|
||||
}
|
||||
|
||||
drawDataDirty = true;
|
||||
configured = true;
|
||||
std::cout << "SparkEmitter configuration loaded successfully!" << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -50,6 +50,7 @@ namespace ZL {
|
||||
|
||||
std::string shaderProgramName;
|
||||
|
||||
bool configured;
|
||||
void prepareDrawData();
|
||||
|
||||
public:
|
||||
|
||||
249
UiManager.cpp
Normal file
249
UiManager.cpp
Normal file
@ -0,0 +1,249 @@
|
||||
#include "UiManager.h"
|
||||
#include "Utils.h"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
namespace ZL {
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
void UiButton::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 UiButton::draw(Renderer& renderer) const {
|
||||
if (!texNormal) return;
|
||||
const std::shared_ptr<Texture>* tex = &texNormal;
|
||||
switch (state) {
|
||||
case ButtonState::Normal: tex = &texNormal; break;
|
||||
case ButtonState::Hover: tex = &texHover; break;
|
||||
case ButtonState::Pressed: tex = &texPressed; break;
|
||||
}
|
||||
if (!(*tex)) return;
|
||||
|
||||
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);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, (*tex)->getTexID());
|
||||
renderer.DrawVertexRenderStruct(mesh);
|
||||
|
||||
renderer.DisableVertexAttribArray(vPositionName);
|
||||
renderer.DisableVertexAttribArray(vTexCoordName);
|
||||
}
|
||||
|
||||
// UiManager implementation
|
||||
void UiManager::loadFromFile(const std::string& path, Renderer& renderer, const std::string& zipFile) {
|
||||
std::ifstream in(path);
|
||||
if (!in.is_open()) {
|
||||
std::cerr << "UiManager: failed to open " << path << std::endl;
|
||||
throw std::runtime_error("Failed to load UI file: " + path);
|
||||
}
|
||||
|
||||
json j;
|
||||
try {
|
||||
in >> j;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "UiManager: json parse error: " << e.what() << std::endl;
|
||||
throw std::runtime_error("Failed to load UI file: " + path);
|
||||
}
|
||||
|
||||
if (!j.contains("root") || !j["root"].is_object()) {
|
||||
std::cerr << "UiManager: root node missing or invalid" << std::endl;
|
||||
throw std::runtime_error("Failed to load UI file: " + path);
|
||||
}
|
||||
|
||||
root = parseNode(j["root"], renderer, zipFile);
|
||||
layoutNode(root);
|
||||
buttons.clear();
|
||||
collectButtons(root);
|
||||
|
||||
for (auto& b : buttons) {
|
||||
b->buildMesh();
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<UiNode> UiManager::parseNode(const json& j, Renderer& renderer, const std::string& zipFile) {
|
||||
auto node = std::make_shared<UiNode>();
|
||||
if (j.contains("type") && j["type"].is_string()) node->type = j["type"].get<std::string>();
|
||||
if (j.contains("name") && j["name"].is_string()) node->name = j["name"].get<std::string>();
|
||||
|
||||
if (j.contains("x")) node->rect.x = j["x"].get<float>();
|
||||
if (j.contains("y")) node->rect.y = j["y"].get<float>();
|
||||
if (j.contains("width")) node->rect.w = j["width"].get<float>();
|
||||
if (j.contains("height")) node->rect.h = j["height"].get<float>();
|
||||
|
||||
if (j.contains("orientation") && j["orientation"].is_string()) node->orientation = j["orientation"].get<std::string>();
|
||||
if (j.contains("spacing")) node->spacing = j["spacing"].get<float>();
|
||||
|
||||
if (node->type == "Button") {
|
||||
auto btn = std::make_shared<UiButton>();
|
||||
btn->name = node->name;
|
||||
btn->rect = node->rect;
|
||||
|
||||
if (!j.contains("textures") || !j["textures"].is_object()) {
|
||||
std::cerr << "UiManager: Button '" << btn->name << "' missing textures" << std::endl;
|
||||
throw std::runtime_error("UI button textures missing");
|
||||
}
|
||||
auto t = j["textures"];
|
||||
auto loadTex = [&](const std::string& key)->std::shared_ptr<Texture> {
|
||||
if (!t.contains(key) || !t[key].is_string()) return nullptr;
|
||||
std::string path = t[key].get<std::string>();
|
||||
try {
|
||||
auto data = CreateTextureDataFromPng(path.c_str(), zipFile.c_str());
|
||||
return std::make_shared<Texture>(data);
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "UiManager: failed load texture " << path << " : " << e.what() << std::endl;
|
||||
throw std::runtime_error("UI texture load failed: " + path);
|
||||
}
|
||||
};
|
||||
|
||||
btn->texNormal = loadTex("normal");
|
||||
btn->texHover = loadTex("hover");
|
||||
btn->texPressed = loadTex("pressed");
|
||||
|
||||
node->button = btn;
|
||||
}
|
||||
|
||||
// parse children
|
||||
if (j.contains("children") && j["children"].is_array()) {
|
||||
for (const auto& ch : j["children"]) {
|
||||
node->children.push_back(parseNode(ch, renderer, zipFile));
|
||||
}
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
void UiManager::layoutNode(const std::shared_ptr<UiNode>& node) {
|
||||
for (auto& child : node->children) {
|
||||
child->rect.x += node->rect.x;
|
||||
child->rect.y += node->rect.y;
|
||||
}
|
||||
|
||||
if (node->type == "LinearLayout") {
|
||||
std::string orient = node->orientation;
|
||||
std::transform(orient.begin(), orient.end(), orient.begin(), ::tolower);
|
||||
|
||||
float cursorX = node->rect.x;
|
||||
float cursorY = node->rect.y;
|
||||
for (auto& child : node->children) {
|
||||
if (orient == "horizontal") {
|
||||
child->rect.x = cursorX;
|
||||
child->rect.y = node->rect.y;
|
||||
cursorX += child->rect.w + node->spacing;
|
||||
}
|
||||
else {
|
||||
child->rect.x = node->rect.x;
|
||||
child->rect.y = cursorY;
|
||||
cursorY += child->rect.h + node->spacing;
|
||||
}
|
||||
layoutNode(child);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (auto& child : node->children) {
|
||||
layoutNode(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UiManager::collectButtons(const std::shared_ptr<UiNode>& node) {
|
||||
if (node->button) {
|
||||
buttons.push_back(node->button);
|
||||
}
|
||||
for (auto& c : node->children) collectButtons(c);
|
||||
}
|
||||
|
||||
void UiManager::draw(Renderer& renderer) {
|
||||
if (!root) return;
|
||||
|
||||
renderer.PushProjectionMatrix(Environment::width, Environment::height, -1, 1);
|
||||
renderer.PushMatrix();
|
||||
renderer.LoadIdentity();
|
||||
|
||||
for (const auto& b : buttons) {
|
||||
b->draw(renderer);
|
||||
}
|
||||
|
||||
renderer.PopMatrix();
|
||||
renderer.PopProjectionMatrix();
|
||||
}
|
||||
|
||||
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::onMouseDown(int x, int y) {
|
||||
for (auto& b : buttons) {
|
||||
if (b->rect.contains((float)x, (float)y)) {
|
||||
b->state = ButtonState::Pressed;
|
||||
pressedButton = b;
|
||||
}
|
||||
else {
|
||||
// leave others
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UiManager::onMouseUp(int x, int y) {
|
||||
for (auto& b : buttons) {
|
||||
bool contains = b->rect.contains((float)x, (float)y);
|
||||
if (b->state == ButtonState::Pressed) {
|
||||
if (contains && pressedButton == b) {
|
||||
if (b->onClick) {
|
||||
b->onClick(b->name);
|
||||
}
|
||||
}
|
||||
b->state = contains ? ButtonState::Hover : ButtonState::Normal;
|
||||
}
|
||||
}
|
||||
pressedButton.reset();
|
||||
}
|
||||
|
||||
std::shared_ptr<UiButton> UiManager::findButton(const std::string& name) {
|
||||
for (auto& b : buttons) if (b->name == name) return b;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace ZL
|
||||
83
UiManager.h
Normal file
83
UiManager.h
Normal file
@ -0,0 +1,83 @@
|
||||
#pragma once
|
||||
|
||||
#include "Renderer.h"
|
||||
#include "TextureManager.h"
|
||||
#include "Environment.h"
|
||||
#include "external/nlohmann/json.hpp"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
|
||||
namespace ZL {
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
struct UiRect {
|
||||
float x = 0;
|
||||
float y = 0;
|
||||
float w = 0;
|
||||
float h = 0;
|
||||
bool contains(float px, float py) const {
|
||||
return px >= x && px <= x + w && py >= y && py <= y + h;
|
||||
}
|
||||
};
|
||||
|
||||
enum class ButtonState {
|
||||
Normal,
|
||||
Hover,
|
||||
Pressed
|
||||
};
|
||||
|
||||
struct UiButton {
|
||||
std::string name;
|
||||
UiRect rect;
|
||||
std::shared_ptr<Texture> texNormal;
|
||||
std::shared_ptr<Texture> texHover;
|
||||
std::shared_ptr<Texture> texPressed;
|
||||
ButtonState state = ButtonState::Normal;
|
||||
|
||||
VertexRenderStruct mesh;
|
||||
|
||||
std::function<void(const std::string&)> onClick;
|
||||
|
||||
void buildMesh();
|
||||
void draw(Renderer& renderer) const;
|
||||
};
|
||||
|
||||
struct UiNode {
|
||||
std::string type;
|
||||
UiRect rect;
|
||||
std::string name;
|
||||
std::vector<std::shared_ptr<UiNode>> children;
|
||||
std::shared_ptr<UiButton> button;
|
||||
std::string orientation = "vertical";
|
||||
float spacing = 0.0f;
|
||||
};
|
||||
|
||||
class UiManager {
|
||||
public:
|
||||
UiManager() = default;
|
||||
|
||||
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);
|
||||
|
||||
std::shared_ptr<UiButton> findButton(const std::string& name);
|
||||
|
||||
private:
|
||||
std::shared_ptr<UiNode> parseNode(const json& j, Renderer& renderer, const std::string& zipFile);
|
||||
void layoutNode(const std::shared_ptr<UiNode>& node);
|
||||
void collectButtons(const std::shared_ptr<UiNode>& node);
|
||||
|
||||
std::shared_ptr<UiNode> root;
|
||||
std::vector<std::shared_ptr<UiButton>> buttons;
|
||||
|
||||
std::shared_ptr<UiButton> pressedButton;
|
||||
};
|
||||
|
||||
} // namespace ZL
|
||||
@ -17,6 +17,6 @@
|
||||
"lifeTimeRange": [600.0, 1400.0],
|
||||
"texture": "./resources/spark.png",
|
||||
"shaderProgramName": "default",
|
||||
"vertexShader": "./shaders/default.vertex",
|
||||
"fragmentShader": "./shaders/default.fragment"
|
||||
"vertexShader": "./shaders/spark.vertex",
|
||||
"fragmentShader": "./shaders/spark.fragment"
|
||||
}
|
||||
72
config/ui.json
Normal file
72
config/ui.json
Normal file
@ -0,0 +1,72 @@
|
||||
{
|
||||
"root": {
|
||||
"type": "FrameLayout",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"width": 1280,
|
||||
"height": 720,
|
||||
"children": [
|
||||
{
|
||||
"type": "FrameLayout",
|
||||
"name": "leftPanel",
|
||||
"x": 100,
|
||||
"y": 100,
|
||||
"width": 320,
|
||||
"height": 400,
|
||||
"children": [
|
||||
{
|
||||
"type": "LinearLayout",
|
||||
"name": "mainButtons",
|
||||
"orientation": "vertical",
|
||||
"spacing": 10,
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"width": 300,
|
||||
"height": 300,
|
||||
"children": [
|
||||
{
|
||||
"type": "Button",
|
||||
"name": "playButton",
|
||||
"x": 100,
|
||||
"y": 100,
|
||||
"width": 200,
|
||||
"height": 50,
|
||||
"textures": {
|
||||
"normal": "./resources/button.png",
|
||||
"hover": "./resources/rock.png",
|
||||
"pressed": "./resources/sand.png"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Button",
|
||||
"name": "settingsButton",
|
||||
"x": 100,
|
||||
"y": 200,
|
||||
"width": 200,
|
||||
"height": 50,
|
||||
"textures": {
|
||||
"normal": "./resources/button.png",
|
||||
"hover": "./resources/rock.png",
|
||||
"pressed": "./resources/sand.png"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Button",
|
||||
"name": "exitButton",
|
||||
"x": 150,
|
||||
"y": 300,
|
||||
"width": 200,
|
||||
"height": 50,
|
||||
"textures": {
|
||||
"normal": "./resources/button.png",
|
||||
"hover": "./resources/rock.png",
|
||||
"pressed": "./resources/sand.png"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -1,8 +1,12 @@
|
||||
#version 120
|
||||
varying vec2 fTexCoord;
|
||||
//precision mediump float;
|
||||
uniform sampler2D Texture;
|
||||
void main() {
|
||||
vec4 col = texture2D(Texture, fTexCoord);
|
||||
// простой аддитивный блёк
|
||||
gl_FragColor = col;
|
||||
varying vec2 texCoord;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 color = texture2D(Texture,texCoord).rgba;
|
||||
//gl_FragColor = vec4(color.rgb*0.9 + vec3(0.1, 0.1, 0.1), 1.0);
|
||||
|
||||
gl_FragColor = color;
|
||||
|
||||
}
|
||||
@ -1,8 +1,11 @@
|
||||
#version 120
|
||||
attribute vec3 vPosition;
|
||||
attribute vec2 vTexCoord;
|
||||
varying vec2 fTexCoord;
|
||||
void main() {
|
||||
fTexCoord = vTexCoord;
|
||||
gl_Position = gl_ModelViewProjectionMatrix * vec4(vPosition, 1.0);
|
||||
varying vec2 texCoord;
|
||||
|
||||
uniform mat4 ProjectionModelViewMatrix;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = ProjectionModelViewMatrix * vec4(vPosition.xyz, 1.0);
|
||||
texCoord = vTexCoord;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user