Adapting for web
This commit is contained in:
1 parent
cf1ec1e76f
commit
a59bcc0c4b
42 files changed
+843
-294
No files matched your search
+19
-5
@@ -26,7 +26,8 @@ namespace ZL
|
||||
#ifdef EMSCRIPTEN
|
||||
const char* CONST_ZIP_FILE = "resources.zip";
|
||||
#else
|
||||
const char* CONST_ZIP_FILE = "";
|
||||
const char* CONST_ZIP_FILE = "C:\\Work\\Projects\\space-game001\\resources.zip";
|
||||
//const char* CONST_ZIP_FILE = "";
|
||||
#endif
|
||||
|
||||
static bool g_exitBgAnimating = false;
|
||||
@@ -245,8 +246,10 @@ namespace ZL
|
||||
ZL::BindOpenGlFunctions();
|
||||
ZL::CheckGlError();
|
||||
|
||||
|
||||
//#ifndef SIMPLIFIED
|
||||
|
||||
|
||||
#ifdef EMSCRIPTEN
|
||||
renderer.shaderManager.AddShaderFromFiles("defaultColor", "resources/shaders/defaultColor.vertex", "resources/shaders/defaultColor_web.fragment", CONST_ZIP_FILE);
|
||||
renderer.shaderManager.AddShaderFromFiles("default", "resources/shaders/default.vertex", "resources/shaders/default_web.fragment", CONST_ZIP_FILE);
|
||||
renderer.shaderManager.AddShaderFromFiles("env_sky", "resources/shaders/env_sky.vertex", "resources/shaders/env_sky_web.fragment", CONST_ZIP_FILE);
|
||||
@@ -254,7 +257,18 @@ namespace ZL
|
||||
renderer.shaderManager.AddShaderFromFiles("planetBake", "resources/shaders/planet_bake.vertex", "resources/shaders/planet_bake_web.fragment", CONST_ZIP_FILE);
|
||||
renderer.shaderManager.AddShaderFromFiles("planetStone", "resources/shaders/planet_stone.vertex", "resources/shaders/planet_stone_web.fragment", CONST_ZIP_FILE);
|
||||
renderer.shaderManager.AddShaderFromFiles("planetLand", "resources/shaders/planet_land.vertex", "resources/shaders/planet_land_web.fragment", CONST_ZIP_FILE);
|
||||
renderer.shaderManager.AddShaderFromFiles("spark", "resources/shaders/spark.vertex", "resources/shaders/spark_web.fragment", CONST_ZIP_FILE);
|
||||
|
||||
#else
|
||||
renderer.shaderManager.AddShaderFromFiles("defaultColor", "resources/shaders/defaultColor.vertex", "resources/shaders/defaultColor_desktop.fragment", CONST_ZIP_FILE);
|
||||
renderer.shaderManager.AddShaderFromFiles("default", "resources/shaders/default.vertex", "resources/shaders/default_desktop.fragment", CONST_ZIP_FILE);
|
||||
renderer.shaderManager.AddShaderFromFiles("env_sky", "resources/shaders/env_sky.vertex", "resources/shaders/env_sky_desktop.fragment", CONST_ZIP_FILE);
|
||||
renderer.shaderManager.AddShaderFromFiles("defaultAtmosphere", "resources/shaders/defaultAtmosphere.vertex", "resources/shaders/defaultAtmosphere_desktop.fragment", CONST_ZIP_FILE);
|
||||
renderer.shaderManager.AddShaderFromFiles("planetBake", "resources/shaders/planet_bake.vertex", "resources/shaders/planet_bake_desktop.fragment", CONST_ZIP_FILE);
|
||||
renderer.shaderManager.AddShaderFromFiles("planetStone", "resources/shaders/planet_stone.vertex", "resources/shaders/planet_stone_desktop.fragment", CONST_ZIP_FILE);
|
||||
renderer.shaderManager.AddShaderFromFiles("planetLand", "resources/shaders/planet_land.vertex", "resources/shaders/planet_land_desktop.fragment", CONST_ZIP_FILE);
|
||||
renderer.shaderManager.AddShaderFromFiles("spark", "resources/shaders/spark.vertex", "resources/shaders/spark_desktop.fragment", CONST_ZIP_FILE);
|
||||
#endif
|
||||
/*#else
|
||||
renderer.shaderManager.AddShaderFromFiles("default", "resources/shaders/default.vertex", "resources/shaders/default_web.fragment", CONST_ZIP_FILE);
|
||||
renderer.shaderManager.AddShaderFromFiles("env_sky", "resources/shaders/default_env.vertex", "resources/shaders/default_env_web.fragment", CONST_ZIP_FILE);
|
||||
@@ -450,10 +464,10 @@ namespace ZL
|
||||
}
|
||||
|
||||
boxAlive.resize(boxCoordsArr.size(), true);
|
||||
|
||||
ZL::CheckGlError();
|
||||
textRenderer = std::make_unique<ZL::TextRenderer>();
|
||||
textRenderer->init(renderer, "resources/fonts/DroidSans.ttf", 32);
|
||||
|
||||
textRenderer->init(renderer, "resources/fonts/DroidSans.ttf", 32, CONST_ZIP_FILE);
|
||||
ZL::CheckGlError();
|
||||
boxLabels.clear();
|
||||
boxLabels.reserve(boxCoordsArr.size());
|
||||
for (size_t i = 0; i < boxCoordsArr.size(); ++i) {
|
||||
|
||||
+29
-22
@@ -127,7 +127,7 @@ 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!");
|
||||
throw std::runtime_error("Failed to load spark emitter config file 1!");
|
||||
}
|
||||
|
||||
if (getActiveParticleCount() == 0) {
|
||||
@@ -135,7 +135,7 @@ namespace ZL {
|
||||
}
|
||||
|
||||
if (!texture) {
|
||||
throw std::runtime_error("Failed to load spark emitter config file!");
|
||||
throw std::runtime_error("Failed to load spark emitter config file 2!");
|
||||
}
|
||||
|
||||
prepareDrawData();
|
||||
@@ -183,7 +183,7 @@ namespace ZL {
|
||||
|
||||
void SparkEmitter::update(float deltaTimeMs) {
|
||||
if (!configured) {
|
||||
throw std::runtime_error("Failed to load spark emitter config file!");
|
||||
throw std::runtime_error("Failed to load spark emitter config file 3!");
|
||||
}
|
||||
|
||||
auto currentTime = std::chrono::steady_clock::now();
|
||||
@@ -233,7 +233,7 @@ namespace ZL {
|
||||
|
||||
void SparkEmitter::emit() {
|
||||
if (!configured) {
|
||||
throw std::runtime_error("Failed to load spark emitter config file!");
|
||||
throw std::runtime_error("Failed to load spark emitter config file 4!");
|
||||
}
|
||||
|
||||
if (emissionPoints.empty()) return;
|
||||
@@ -287,7 +287,7 @@ namespace ZL {
|
||||
|
||||
void SparkEmitter::initParticle(SparkParticle& particle, int emitterIndex) {
|
||||
if (!configured) {
|
||||
throw std::runtime_error("Failed to load spark emitter config file!");
|
||||
throw std::runtime_error("Failed to load spark emitter config file 5!");
|
||||
}
|
||||
|
||||
particle.velocity = getRandomVelocity(emitterIndex);
|
||||
@@ -303,7 +303,7 @@ namespace ZL {
|
||||
|
||||
Vector3f SparkEmitter::getRandomVelocity(int emitterIndex) {
|
||||
if (!configured) {
|
||||
throw std::runtime_error("Failed to load spark emitter config file!");
|
||||
throw std::runtime_error("Failed to load spark emitter config file 6!");
|
||||
}
|
||||
|
||||
static std::random_device rd;
|
||||
@@ -359,14 +359,14 @@ namespace ZL {
|
||||
auto buf = readFileFromZIP(path, zipFile);
|
||||
if (buf.empty()) {
|
||||
std::cerr << "Failed to read JSON from zip: " << path << " in " << zipFile << std::endl;
|
||||
throw std::runtime_error("Failed to load spark emitter config file!");
|
||||
throw std::runtime_error("Failed to load spark emitter config file 7!");
|
||||
}
|
||||
content.assign(buf.begin(), buf.end());
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "Failed to open JSON file: " << path << " : " << e.what() << std::endl;
|
||||
throw std::runtime_error("Failed to load spark emitter config file!");
|
||||
throw std::runtime_error("Failed to load spark emitter config file 8!");
|
||||
}
|
||||
|
||||
json j;
|
||||
@@ -376,14 +376,14 @@ namespace ZL {
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "JSON parse error: " << e.what() << std::endl;
|
||||
throw std::runtime_error("Failed to load spark emitter config file!");
|
||||
throw std::runtime_error("Failed to load spark emitter config file 9!");
|
||||
}
|
||||
std::cout << "JSON content: " << j.dump(2) << std::endl;
|
||||
|
||||
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!");
|
||||
throw std::runtime_error("Failed to load spark emitter config file 9!");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -449,12 +449,12 @@ namespace ZL {
|
||||
}
|
||||
else {
|
||||
std::cerr << "Emission points parsed but empty" << std::endl;
|
||||
throw std::runtime_error("Failed to load spark emitter config file!");
|
||||
throw std::runtime_error("Failed to load spark emitter config file 10!");
|
||||
}
|
||||
}
|
||||
else {
|
||||
std::cerr << "emissionPoints is not array" << std::endl;
|
||||
throw std::runtime_error("Failed to load spark emitter config file!");
|
||||
throw std::runtime_error("Failed to load spark emitter config file 11!");
|
||||
}
|
||||
|
||||
// Ranges
|
||||
@@ -466,7 +466,7 @@ namespace ZL {
|
||||
}
|
||||
else {
|
||||
std::cerr << "speedRange missing or invalid" << std::endl;
|
||||
throw std::runtime_error("Failed to load spark emitter config file!");
|
||||
throw std::runtime_error("Failed to load spark emitter config file 12!");
|
||||
}
|
||||
|
||||
if (j.contains("zSpeedRange") && j["zSpeedRange"].is_array()) {
|
||||
@@ -477,7 +477,7 @@ namespace ZL {
|
||||
}
|
||||
else {
|
||||
std::cerr << "zSpeedRange missing or invalid" << std::endl;
|
||||
throw std::runtime_error("Failed to load spark emitter config file!");
|
||||
throw std::runtime_error("Failed to load spark emitter config file 13!");
|
||||
}
|
||||
|
||||
if (j.contains("scaleRange") && j["scaleRange"].is_array()) {
|
||||
@@ -488,7 +488,7 @@ namespace ZL {
|
||||
}
|
||||
else {
|
||||
std::cerr << "scaleRange missing or invalid" << std::endl;
|
||||
throw std::runtime_error("Failed to load spark emitter config file!");
|
||||
throw std::runtime_error("Failed to load spark emitter config file 14!");
|
||||
}
|
||||
|
||||
if (j.contains("lifeTimeRange") && j["lifeTimeRange"].is_array()) {
|
||||
@@ -499,29 +499,33 @@ namespace ZL {
|
||||
}
|
||||
else {
|
||||
std::cerr << "lifeTimeRange missing or invalid" << std::endl;
|
||||
throw std::runtime_error("Failed to load spark emitter config file!");
|
||||
throw std::runtime_error("Failed to load spark emitter config file 15!");
|
||||
}
|
||||
|
||||
// texture
|
||||
if (j.contains("texture") && j["texture"].is_string()) {
|
||||
std::string texPath = j["texture"].get<std::string>();
|
||||
std::cout << "Loading texture: " << texPath << std::endl;
|
||||
std::cout << "Loading texture: " << texPath << " From zip file: " << zipFile << std::endl;
|
||||
|
||||
try {
|
||||
std::cout << "Loading texture step 1" << std::endl;
|
||||
auto texData = CreateTextureDataFromPng(texPath.c_str(), zipFile.c_str());
|
||||
std::cout << "Loading texture step 2" << std::endl;
|
||||
texture = std::make_shared<Texture>(texData);
|
||||
std::cout << "Texture loaded successfully, ID: " << texture->getTexID() << std::endl;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cout << "Texture load error: " << e.what() << std::endl;
|
||||
std::cerr << "Texture load error: " << e.what() << std::endl;
|
||||
throw std::runtime_error("Failed to load spark emitter config file!");
|
||||
throw std::runtime_error("Failed to load spark emitter config file 16!");
|
||||
}
|
||||
}
|
||||
else {
|
||||
std::cerr << "No texture specified or invalid type in JSON" << std::endl;
|
||||
throw std::runtime_error("Failed to load spark emitter config file!");
|
||||
throw std::runtime_error("Failed to load spark emitter config file 17!");
|
||||
}
|
||||
|
||||
std::cout << "Working with shaders 1" << std::endl;
|
||||
// shaders
|
||||
if (j.contains("shaderProgramName") && j["shaderProgramName"].is_string()) {
|
||||
shaderProgramName = j["shaderProgramName"].get<std::string>();
|
||||
@@ -529,9 +533,12 @@ namespace ZL {
|
||||
}
|
||||
else {
|
||||
std::cerr << "shaderProgramName missing or invalid" << std::endl;
|
||||
throw std::runtime_error("Failed to load spark emitter config file!");
|
||||
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>();
|
||||
@@ -544,9 +551,9 @@ namespace ZL {
|
||||
}
|
||||
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!");
|
||||
throw std::runtime_error("Failed to load spark emitter config file 19!");
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
drawDataDirty = true;
|
||||
configured = true;
|
||||
|
||||
+3
-1
@@ -247,6 +247,7 @@ namespace ZL {
|
||||
if (!t.contains(key) || !t[key].is_string()) return nullptr;
|
||||
std::string path = t[key].get<std::string>();
|
||||
try {
|
||||
std::cout << "UiManager: loading texture for button '" << btn->name << "' : " << path << " Zip file: " << zipFile << std::endl;
|
||||
auto data = CreateTextureDataFromPng(path.c_str(), zipFile.c_str());
|
||||
return std::make_shared<Texture>(data);
|
||||
}
|
||||
@@ -276,6 +277,7 @@ namespace ZL {
|
||||
if (!t.contains(key) || !t[key].is_string()) return nullptr;
|
||||
std::string path = t[key].get<std::string>();
|
||||
try {
|
||||
std::cout << "UiManager: --loading texture for button '" << "' : " << path << " Zip file: " << zipFile << std::endl;
|
||||
auto data = CreateTextureDataFromPng(path.c_str(), zipFile.c_str());
|
||||
return std::make_shared<Texture>(data);
|
||||
}
|
||||
@@ -345,7 +347,7 @@ namespace ZL {
|
||||
if (j.contains("centered")) tv->centered = j["centered"].get<bool>();
|
||||
|
||||
tv->textRenderer = std::make_unique<TextRenderer>();
|
||||
if (!tv->textRenderer->init(renderer, tv->fontPath, tv->fontSize)) {
|
||||
if (!tv->textRenderer->init(renderer, tv->fontPath, tv->fontSize, zipFile)) {
|
||||
std::cerr << "Failed to init TextRenderer for TextView: " << tv->name << std::endl;
|
||||
}
|
||||
|
||||
|
||||
@@ -33,5 +33,10 @@ namespace ZL {
|
||||
std::vector<int> getPendingRespawns() override {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<BoxDestroyedInfo> getPendingBoxDestructions() override
|
||||
{
|
||||
return {};
|
||||
}
|
||||
};
|
||||
}
|
||||
+136
-88
@@ -1,4 +1,4 @@
|
||||
#include "render/TextRenderer.h"
|
||||
#include "render/TextRenderer.h"
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
|
||||
@@ -11,53 +11,87 @@ namespace ZL {
|
||||
|
||||
TextRenderer::~TextRenderer()
|
||||
{
|
||||
for (auto& kv : glyphs) {
|
||||
/*for (auto& kv : glyphs) {
|
||||
if (kv.second.texID) glDeleteTextures(1, &kv.second.texID);
|
||||
}
|
||||
}*/
|
||||
glyphs.clear();
|
||||
|
||||
textMesh.positionVBO.reset();
|
||||
/*
|
||||
if (vbo) glDeleteBuffers(1, &vbo);
|
||||
// if (vao) glDeleteVertexArrays(1, &vao);
|
||||
vao = 0;
|
||||
vbo = 0;
|
||||
vbo = 0;*/
|
||||
}
|
||||
|
||||
bool TextRenderer::init(Renderer& renderer, const std::string& ttfPath, int pixelSize)
|
||||
bool TextRenderer::init(Renderer& renderer, const std::string& ttfPath, int pixelSize, const std::string& zipfilename)
|
||||
{
|
||||
r = &renderer;
|
||||
|
||||
#ifdef EMSCRIPTEN
|
||||
r->shaderManager.AddShaderFromFiles(shaderName,
|
||||
"resources/shaders/text2d.vertex",
|
||||
"resources/shaders/text2d.fragment",
|
||||
"" // ZIP пустой
|
||||
"resources/shaders/text2d_web.fragment",
|
||||
zipfilename
|
||||
);
|
||||
#else
|
||||
r->shaderManager.AddShaderFromFiles(shaderName,
|
||||
"resources/shaders/text2d.vertex",
|
||||
"resources/shaders/text2d_desktop.fragment",
|
||||
zipfilename
|
||||
);
|
||||
#endif
|
||||
ZL::CheckGlError();
|
||||
if (!loadGlyphs(ttfPath, pixelSize, zipfilename)) return false;
|
||||
ZL::CheckGlError();
|
||||
|
||||
if (!loadGlyphs(ttfPath, pixelSize)) return false;
|
||||
|
||||
textMesh.data.PositionData.resize(6, Eigen::Vector3f(0, 0, 0));
|
||||
textMesh.RefreshVBO();
|
||||
// VAO/VBO для 6 вершин (2 треугольника) * (pos.xy + uv.xy) = 4 float
|
||||
// glGenVertexArrays(1, &vao);
|
||||
glGenBuffers(1, &vbo);
|
||||
/* glGenBuffers(1, &vbo);
|
||||
|
||||
//glBindVertexArray(vao);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 6 * 4, nullptr, GL_DYNAMIC_DRAW);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
|
||||
|
||||
*/
|
||||
ZL::CheckGlError();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TextRenderer::loadGlyphs(const std::string& ttfPath, int pixelSize)
|
||||
bool TextRenderer::loadGlyphs(const std::string& ttfPath, int pixelSize, const std::string& zipfilename)
|
||||
{
|
||||
// 1. Загружаем сырые данные из ZIP
|
||||
std::vector<char> fontData;
|
||||
try {
|
||||
fontData = !zipfilename.empty()
|
||||
? readFileFromZIP(ttfPath, zipfilename)
|
||||
: readFile(ttfPath); // Предполагаем наличие функции readFile
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "TextRenderer: failed to read font data: " << e.what() << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fontData.empty()) return false;
|
||||
|
||||
FT_Library ft;
|
||||
if (FT_Init_FreeType(&ft)) {
|
||||
std::cerr << "FreeType: FT_Init_FreeType failed\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2. Инициализируем Face из памяти
|
||||
FT_Face face;
|
||||
if (FT_New_Face(ft, ttfPath.c_str(), 0, &face)) {
|
||||
std::cerr << "FreeType: failed to load font: " << ttfPath << "\n";
|
||||
// Важно: передаем указатель на буфер и его размер
|
||||
if (FT_New_Memory_Face(ft,
|
||||
reinterpret_cast<const FT_Byte*>(fontData.data()),
|
||||
static_cast<FT_Long>(fontData.size()),
|
||||
0,
|
||||
&face))
|
||||
{
|
||||
std::cerr << "FreeType: failed to load font from memory: " << ttfPath << "\n";
|
||||
FT_Done_FreeType(ft);
|
||||
return false;
|
||||
}
|
||||
@@ -67,52 +101,50 @@ bool TextRenderer::loadGlyphs(const std::string& ttfPath, int pixelSize)
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
|
||||
glyphs.clear();
|
||||
// Проходим по стандартным ASCII символам
|
||||
for (unsigned char c = 32; c < 128; ++c) {
|
||||
if (FT_Load_Char(face, c, FT_LOAD_RENDER)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
FT_Load_Char(face, c, FT_LOAD_RENDER);
|
||||
|
||||
unsigned int tex;
|
||||
glGenTextures(1, &tex);
|
||||
glBindTexture(GL_TEXTURE_2D, tex);
|
||||
glTexImage2D(
|
||||
GL_TEXTURE_2D,
|
||||
0,
|
||||
GL_RED,
|
||||
face->glyph->bitmap.width,
|
||||
face->glyph->bitmap.rows,
|
||||
0,
|
||||
GL_RED,
|
||||
GL_UNSIGNED_BYTE,
|
||||
face->glyph->bitmap.buffer
|
||||
);
|
||||
TextureDataStruct glyphData;
|
||||
glyphData.width = face->glyph->bitmap.width;
|
||||
glyphData.height = face->glyph->bitmap.rows;
|
||||
glyphData.format = TextureDataStruct::R8;
|
||||
glyphData.mipmap = TextureDataStruct::NONE;
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
// Копируем буфер FreeType в вектор данных
|
||||
size_t dataSize = glyphData.width * glyphData.height;
|
||||
glyphData.data.assign(face->glyph->bitmap.buffer, face->glyph->bitmap.buffer + dataSize);
|
||||
|
||||
GlyphInfo g;
|
||||
g.texID = tex;
|
||||
// Теперь создание текстуры — это одна строка!
|
||||
auto tex = std::make_shared<Texture>(glyphData);
|
||||
|
||||
GlyphInfo g;
|
||||
g.texture = tex;
|
||||
g.size = Eigen::Vector2f((float)face->glyph->bitmap.width, (float)face->glyph->bitmap.rows);
|
||||
g.bearing = Eigen::Vector2f((float)face->glyph->bitmap_left, (float)face->glyph->bitmap_top);
|
||||
// Advance во FreeType измеряется в 1/64 пикселя
|
||||
g.advance = (unsigned int)face->glyph->advance.x;
|
||||
|
||||
glyphs.emplace((char)c, g);
|
||||
}
|
||||
|
||||
// Очистка
|
||||
FT_Done_Face(face);
|
||||
FT_Done_FreeType(ft);
|
||||
|
||||
// После FT_Done_Face данные из fontData больше не нужны,
|
||||
// вектор сам очистится при выходе из функции.
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
void TextRenderer::drawText(const std::string& text, float x, float y, float scale, bool centered, std::array<float, 4> color)
|
||||
{
|
||||
if (!r) return;
|
||||
if (!r || text.empty()) return;
|
||||
|
||||
// Считаем ширину строки для центрирования
|
||||
// 1. Считаем ширину для центрирования
|
||||
float totalW = 0.0f;
|
||||
if (centered) {
|
||||
for (char ch : text) {
|
||||
@@ -123,35 +155,8 @@ void TextRenderer::drawText(const std::string& text, float x, float y, float sca
|
||||
x -= totalW * 0.5f;
|
||||
}
|
||||
|
||||
r->shaderManager.PushShader(shaderName);
|
||||
|
||||
// Орто-проекция в пикселях: (0..W, 0..H)
|
||||
float W = (float)Environment::width;
|
||||
float H = (float)Environment::height;
|
||||
|
||||
Eigen::Matrix4f proj = Eigen::Matrix4f::Identity();
|
||||
proj(0,0) = 2.0f / W;
|
||||
proj(1,1) = 2.0f / H;
|
||||
proj(0,3) = -1.0f;
|
||||
proj(1,3) = -1.0f;
|
||||
|
||||
// uProjection
|
||||
r->RenderUniformMatrix4fv("uProjection", false, proj.data());
|
||||
r->RenderUniform1i("uText", 0);
|
||||
|
||||
r->RenderUniform4fv("uColor", color.data());
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
// glBindVertexArray(vao);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
||||
|
||||
r->EnableVertexAttribArray("vPosition");
|
||||
r->EnableVertexAttribArray("vTexCoord");
|
||||
|
||||
r->VertexAttribPointer2fv("vPosition", 4 * sizeof(float), (const char*)0);
|
||||
r->VertexAttribPointer2fv("vTexCoord", 4 * sizeof(float), (const char*)(2 * sizeof(float)));
|
||||
|
||||
// 2. Подготовка данных (аналог CreateRect2D, но для всей строки)
|
||||
VertexDataStruct textData;
|
||||
float penX = x;
|
||||
float penY = y;
|
||||
|
||||
@@ -163,36 +168,79 @@ void TextRenderer::drawText(const std::string& text, float x, float y, float sca
|
||||
|
||||
float xpos = penX + g.bearing.x() * scale;
|
||||
float ypos = penY - (g.size.y() - g.bearing.y()) * scale;
|
||||
|
||||
float w = g.size.x() * scale;
|
||||
float h = g.size.y() * scale;
|
||||
|
||||
// 2 треугольника
|
||||
float verts[6][4] = {
|
||||
{ xpos, ypos + h, 0.0f, 0.0f },
|
||||
{ xpos, ypos, 0.0f, 1.0f },
|
||||
{ xpos + w, ypos, 1.0f, 1.0f },
|
||||
// Добавляем 2 треугольника (6 вершин) для текущего символа
|
||||
// Координаты Z ставим 0.0f, так как это 2D
|
||||
textData.PositionData.push_back({ xpos, ypos + h, 0.0f });
|
||||
textData.PositionData.push_back({ xpos, ypos, 0.0f });
|
||||
textData.PositionData.push_back({ xpos + w, ypos, 0.0f });
|
||||
textData.PositionData.push_back({ xpos, ypos + h, 0.0f });
|
||||
textData.PositionData.push_back({ xpos + w, ypos, 0.0f });
|
||||
textData.PositionData.push_back({ xpos + w, ypos + h, 0.0f });
|
||||
|
||||
{ xpos, ypos + h, 0.0f, 0.0f },
|
||||
{ xpos + w, ypos, 1.0f, 1.0f },
|
||||
{ xpos + w, ypos + h, 1.0f, 0.0f }
|
||||
};
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, g.texID);
|
||||
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(verts), verts);
|
||||
glDrawArrays(GL_TRIANGLES, 0, 6);
|
||||
// UV-координаты (здесь есть нюанс с атласом, ниже поясню)
|
||||
textData.TexCoordData.push_back({ 0.0f, 0.0f });
|
||||
textData.TexCoordData.push_back({ 0.0f, 1.0f });
|
||||
textData.TexCoordData.push_back({ 1.0f, 1.0f });
|
||||
textData.TexCoordData.push_back({ 0.0f, 0.0f });
|
||||
textData.TexCoordData.push_back({ 1.0f, 1.0f });
|
||||
textData.TexCoordData.push_back({ 1.0f, 0.0f });
|
||||
|
||||
penX += (g.advance >> 6) * scale;
|
||||
}
|
||||
|
||||
// cleanup
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
// 3. Обновляем VBO через наш стандартный механизм
|
||||
// Примечание: для текста лучше использовать GL_DYNAMIC_DRAW,
|
||||
// но RefreshVBO сейчас жестко зашит на GL_STATIC_DRAW.
|
||||
// Для UI это обычно не критично, если строк не тысячи.
|
||||
textMesh.AssignFrom(textData);
|
||||
|
||||
// 4. Рендеринг
|
||||
r->shaderManager.PushShader(shaderName);
|
||||
|
||||
// Матрица проекции (экрана)
|
||||
float W = (float)Environment::width;
|
||||
float H = (float)Environment::height;
|
||||
Eigen::Matrix4f proj = Eigen::Matrix4f::Identity();
|
||||
proj(0, 0) = 2.0f / W;
|
||||
proj(1, 1) = 2.0f / H;
|
||||
proj(0, 3) = -1.0f;
|
||||
proj(1, 3) = -1.0f;
|
||||
|
||||
r->RenderUniformMatrix4fv("uProjection", false, proj.data());
|
||||
r->RenderUniform1i("uText", 0);
|
||||
r->RenderUniform4fv("uColor", color.data());
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
|
||||
// ВНИМАНИЕ: Так как у тебя каждый символ — это отдельная текстура,
|
||||
// нам всё равно придется делать glDrawArrays в цикле, ЛИБО использовать атлас.
|
||||
// Если оставляем текущую систему с разными текстурами:
|
||||
|
||||
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;
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, it->second.texture->getTexID());
|
||||
|
||||
// Отрисовываем по 6 вершин за раз
|
||||
// Нам нужно вручную биндить VBO, так как DrawVertexRenderStruct рисует всё сразу
|
||||
glBindBuffer(GL_ARRAY_BUFFER, textMesh.positionVBO->getBuffer());
|
||||
r->VertexAttribPointer3fv("vPosition", 0, (const char*)(i * 6 * sizeof(Vector3f)));
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, textMesh.texCoordVBO->getBuffer());
|
||||
r->VertexAttribPointer2fv("vTexCoord", 0, (const char*)(i * 6 * sizeof(Vector2f)));
|
||||
|
||||
glDrawArrays(GL_TRIANGLES, 0, 6);
|
||||
}
|
||||
|
||||
r->DisableVertexAttribArray("vPosition");
|
||||
r->DisableVertexAttribArray("vTexCoord");
|
||||
|
||||
r->shaderManager.PopShader();
|
||||
}
|
||||
|
||||
} // namespace ZL
|
||||
@@ -1,17 +1,18 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <Eigen/Dense>
|
||||
#include "render/Renderer.h"
|
||||
#include "render/TextureManager.h"
|
||||
#include <array>
|
||||
|
||||
|
||||
namespace ZL {
|
||||
|
||||
struct GlyphInfo {
|
||||
unsigned int texID = 0; // GL texture for glyph
|
||||
std::shared_ptr<Texture> texture; // Texture for glyph
|
||||
Eigen::Vector2f size; // glyph size in pixels
|
||||
Eigen::Vector2f bearing; // offset from baseline
|
||||
unsigned int advance = 0; // advance.x in 1/64 pixels
|
||||
@@ -22,19 +23,21 @@ public:
|
||||
TextRenderer() = default;
|
||||
~TextRenderer();
|
||||
|
||||
bool init(Renderer& renderer, const std::string& ttfPath, int pixelSize);
|
||||
bool init(Renderer& renderer, const std::string& ttfPath, int pixelSize, const std::string& zipfilename);
|
||||
void drawText(const std::string& text, float x, float y, float scale, bool centered, std::array<float, 4> color = { 1.f,1.f,1.f,1.f });
|
||||
|
||||
private:
|
||||
bool loadGlyphs(const std::string& ttfPath, int pixelSize);
|
||||
bool loadGlyphs(const std::string& ttfPath, int pixelSize, const std::string& zipfilename);
|
||||
|
||||
Renderer* r = nullptr;
|
||||
|
||||
std::unordered_map<char, GlyphInfo> glyphs;
|
||||
|
||||
// OpenGL objects for a dynamic quad
|
||||
unsigned int vao = 0;
|
||||
unsigned int vbo = 0;
|
||||
//unsigned int vao = 0;
|
||||
//unsigned int vbo = 0;
|
||||
|
||||
VertexRenderStruct textMesh;
|
||||
|
||||
std::string shaderName = "text2d";
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "render/TextureManager.h"
|
||||
#include "render/TextureManager.h"
|
||||
#include "render/OpenGlExtensions.h"
|
||||
#ifdef PNG_ENABLED
|
||||
#include "png.h"
|
||||
@@ -11,48 +11,68 @@ namespace ZL
|
||||
Texture::Texture(const TextureDataStruct& texData) {
|
||||
width = texData.width;
|
||||
height = texData.height;
|
||||
|
||||
glGenTextures(1, &texID);
|
||||
if (texID == 0) {
|
||||
throw std::runtime_error("glGenTextures did not work");
|
||||
glBindTexture(GL_TEXTURE_2D, texID);
|
||||
|
||||
GLint internalFormat;
|
||||
GLenum externalFormat;
|
||||
|
||||
switch (texData.format) {
|
||||
case TextureDataStruct::R8:
|
||||
// Для WebGL 1.0 лучше использовать GL_LUMINANCE вместо GL_RED
|
||||
// Если используешь WebGL 2.0, можно оставить GL_RED
|
||||
#if defined(EMSCRIPTEN) || defined(__ANDROID__)
|
||||
internalFormat = GL_LUMINANCE;
|
||||
externalFormat = GL_LUMINANCE;
|
||||
#else
|
||||
internalFormat = GL_RED;
|
||||
externalFormat = GL_RED;
|
||||
#endif
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
// Исправлено: используем GL_TEXTURE_2D
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
break;
|
||||
|
||||
case TextureDataStruct::RGB:
|
||||
internalFormat = GL_RGB;
|
||||
externalFormat = GL_RGB;
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // RGB может быть не выровнен по 4 байта
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
break;
|
||||
|
||||
default: // RGBA
|
||||
internalFormat = GL_RGBA;
|
||||
externalFormat = GL_RGBA;
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
break;
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, texID);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, internalFormat,
|
||||
static_cast<GLsizei>(width), static_cast<GLsizei>(height),
|
||||
0, externalFormat, GL_UNSIGNED_BYTE, texData.data.data());
|
||||
CheckGlError();
|
||||
|
||||
// ��������� ���������� � ����������� �� ���������� ����
|
||||
if (texData.mipmap == TextureDataStruct::GENERATE) {
|
||||
// ���������� ����������� ���������� ��� ���������� ��������
|
||||
// 3. Фильтрация и Мип-мапы
|
||||
// ВНИМАНИЕ: Для шрифтов (NPOT) в WebGL glGenerateMipmap работать НЕ будет!
|
||||
if (texData.mipmap == TextureDataStruct::GENERATE && texData.format != TextureDataStruct::R8) {
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
}
|
||||
else {
|
||||
// ������� �������� ���������� (����� ������� �� ��������)
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
}
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
CheckGlError();
|
||||
|
||||
// �������� ������ �������� ������ (Level 0)
|
||||
GLint format = (texData.bitSize == TextureDataStruct::BS_24BIT) ? GL_RGB : GL_RGBA;
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, format,
|
||||
static_cast<GLsizei>(width),
|
||||
static_cast<GLsizei>(height),
|
||||
0, format, GL_UNSIGNED_BYTE, texData.data.data());
|
||||
|
||||
CheckGlError();
|
||||
|
||||
// ��������� ���-�����, ���� ��� ����������
|
||||
if (texData.mipmap == TextureDataStruct::GENERATE) {
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
CheckGlError();
|
||||
}
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
|
||||
}
|
||||
|
||||
Texture::Texture(const std::array<TextureDataStruct, 6>& texDataArray)
|
||||
{
|
||||
// ��������, ��� ��� ����� ����� ���������� �������
|
||||
width = texDataArray[0].width;
|
||||
height = texDataArray[0].height;
|
||||
|
||||
@@ -73,36 +93,28 @@ namespace ZL
|
||||
|
||||
CheckGlError();
|
||||
|
||||
// ��������� ���������� ��� Cubemap
|
||||
|
||||
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
// ���������� GL_LINEAR ��� MIN_FILTER, ��� ��� ������� ����� �� ������������
|
||||
// ���� �� �������������� ������� (e.g., GL_LINEAR_MIPMAP_LINEAR), ����� ���� �� ������� glGenerateMipmap.
|
||||
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
|
||||
// ������������ ��������� ������� ��� Cubemap
|
||||
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
|
||||
// GL_TEXTURE_WRAP_R �� �������������� � WebGL 1.0/OpenGL ES 2.0 � �������� ������.
|
||||
// ������������ ��� ����� ������ ��� ���������� ��������.
|
||||
#ifndef EMSCRIPTEN
|
||||
#ifndef __ANDROID__
|
||||
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
CheckGlError(); // �������� ����� ��������� ����������
|
||||
|
||||
// �������� ������ ��� ������ �� 6 ������
|
||||
// GL_TEXTURE_CUBE_MAP_POSITIVE_X + i ���� �����: +X (0), -X (1), +Y (2), -Y (3), +Z (4), -Z (5)
|
||||
CheckGlError();
|
||||
|
||||
for (int i = 0; i < 6; ++i)
|
||||
{
|
||||
GLint internalFormat;
|
||||
GLenum format;
|
||||
|
||||
// � WebGL 1.0/OpenGL ES 2.0 ���������� ������ (internalFormat)
|
||||
// ������ ������ ��������������� ������� ������ (format).
|
||||
if (texDataArray[i].bitSize == TextureDataStruct::BS_24BIT)
|
||||
|
||||
if (texDataArray[i].format == TextureDataStruct::RGB)
|
||||
{
|
||||
internalFormat = GL_RGB; // internalFormat
|
||||
format = GL_RGB; // format
|
||||
@@ -114,20 +126,19 @@ namespace ZL
|
||||
}
|
||||
|
||||
glTexImage2D(
|
||||
GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, // ������� �����
|
||||
0, // ������� MIP-��������
|
||||
internalFormat, // ���������� ������ (������ ��������� � ��������)
|
||||
GL_TEXTURE_CUBE_MAP_POSITIVE_X + i,
|
||||
0,
|
||||
internalFormat,
|
||||
static_cast<GLsizei>(width),
|
||||
static_cast<GLsizei>(height),
|
||||
0, // ������� (������ 0)
|
||||
format, // ������ �������� ������
|
||||
GL_UNSIGNED_BYTE, // ��� ������
|
||||
texDataArray[i].data.data() // ��������� �� ������
|
||||
0,
|
||||
format,
|
||||
GL_UNSIGNED_BYTE,
|
||||
texDataArray[i].data.data()
|
||||
);
|
||||
CheckGlError();
|
||||
}
|
||||
|
||||
// ������� �������� ��� �������
|
||||
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
|
||||
}
|
||||
|
||||
@@ -173,7 +184,7 @@ namespace ZL
|
||||
texData.width = *reinterpret_cast<uint32_t*>(&fileArr[18]);
|
||||
texData.height = *reinterpret_cast<uint32_t*>(&fileArr[22]);
|
||||
|
||||
texData.bitSize = TextureDataStruct::BS_24BIT;
|
||||
texData.format = TextureDataStruct::RGB;
|
||||
|
||||
size_t dataSize = texData.width * texData.height * 3;
|
||||
|
||||
@@ -222,7 +233,7 @@ namespace ZL
|
||||
texData.width = *reinterpret_cast<uint32_t*>(&fileArr[18]);
|
||||
texData.height = *reinterpret_cast<uint32_t*>(&fileArr[22]);
|
||||
|
||||
texData.bitSize = TextureDataStruct::BS_32BIT;
|
||||
texData.format = TextureDataStruct::RGBA;
|
||||
|
||||
size_t dataSize = texData.width * texData.height * 4;
|
||||
|
||||
@@ -255,48 +266,33 @@ namespace ZL
|
||||
|
||||
#ifdef PNG_ENABLED
|
||||
|
||||
// ��������� ��� �������� ������ � �����/������� � ������� ������� ������
|
||||
struct png_data_t {
|
||||
const char* data;
|
||||
size_t size;
|
||||
size_t offset;
|
||||
};
|
||||
|
||||
// ���������������� ������� ������ ��� libpng
|
||||
// 'png_ptr' - ��������� �� ��������� png
|
||||
// 'out_ptr' - ���� ���������� ����������� ������
|
||||
// 'bytes_to_read' - ������� ���� ����� ���������
|
||||
void user_read_data(png_structp png_ptr, png_bytep out_ptr, png_size_t bytes_to_read) {
|
||||
// �������� ��������� �� ���� ��������� png_data_t, ������� �� ���������� � ������� png_set_read_fn
|
||||
png_data_t* data = (png_data_t*)png_get_io_ptr(png_ptr);
|
||||
|
||||
if (data->offset + bytes_to_read > data->size) {
|
||||
// ������� ��������� ������, ��� ���� � �������.
|
||||
// ������ ������ ����������� ������, �� ����� ������ ��������� ������� ��� ������� ������.
|
||||
// � ���� ������ �� ������� ������ libpng.
|
||||
png_error(png_ptr, "PNG Read Error: Attempted to read past end of data buffer.");
|
||||
bytes_to_read = data->size - data->offset; // �������������, ����� ��������� ����������
|
||||
bytes_to_read = data->size - data->offset;
|
||||
}
|
||||
|
||||
// �������� ������ �� ������ ������� � ����� libpng
|
||||
std::memcpy(out_ptr, data->data + data->offset, bytes_to_read);
|
||||
|
||||
// ��������� ��������
|
||||
data->offset += bytes_to_read;
|
||||
}
|
||||
|
||||
// ���������������� ������� �������������� (�� �������, ����� ������������ nullptr)
|
||||
void user_warning_fn(png_structp png_ptr, png_const_charp warning_msg) {
|
||||
// ����� ����� ����������� ����������� ��������������
|
||||
//throw std::runtime_error();
|
||||
std::cout << "PNG Warning: " << warning_msg << std::endl;
|
||||
}
|
||||
|
||||
// ���������������� ������� ������ (����������� ��� setjmp)
|
||||
|
||||
void user_error_fn(png_structp png_ptr, png_const_charp error_msg) {
|
||||
// ����� ����� ����������� ����������� ������
|
||||
|
||||
std::cout << "PNG Error: " << error_msg << std::endl;
|
||||
// ����������� �������� longjmp ��� ������ �� �������� ������/������ PNG
|
||||
longjmp(png_jmpbuf(png_ptr), 1);
|
||||
}
|
||||
|
||||
@@ -304,7 +300,6 @@ namespace ZL
|
||||
{
|
||||
TextureDataStruct texData;
|
||||
|
||||
// ��������� ��� ���������� ������� �� �������
|
||||
png_data_t png_data = { fileArr.data(), fileArr.size(), 0 };
|
||||
|
||||
png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
|
||||
@@ -318,21 +313,15 @@ namespace ZL
|
||||
throw std::runtime_error("Could not create PNG info structure");
|
||||
}
|
||||
|
||||
// === ��������� ���������������� ������� ������ � ��������� ������ ===
|
||||
// 1. ��������� ����������� ������ � longjmp
|
||||
if (setjmp(png_jmpbuf(png))) {
|
||||
png_destroy_read_struct(&png, &info, nullptr);
|
||||
throw std::runtime_error("Error during PNG read (longjmp was executed)");
|
||||
}
|
||||
|
||||
// 2. ��������� ���������������� ������� ��� ��������� ������ � ��������������
|
||||
// ������ nullptr � error_ptr � warning_ptr ����� �������� ��������� �� ���� ��������� ������, ���� ����������
|
||||
png_set_error_fn(png, nullptr, user_error_fn, user_warning_fn);
|
||||
|
||||
// 3. ��������� ���������������� ������� ������ � �������� �� ����� ��������� png_data
|
||||
png_set_read_fn(png, &png_data, user_read_data);
|
||||
// ===================================================================
|
||||
|
||||
|
||||
png_read_info(png, info);
|
||||
|
||||
texData.width = png_get_image_width(png, info);
|
||||
@@ -340,7 +329,6 @@ namespace ZL
|
||||
png_byte color_type = png_get_color_type(png, info);
|
||||
png_byte bit_depth = png_get_bit_depth(png, info);
|
||||
|
||||
// === ���� �������������� (�������� ��� ���������) ===
|
||||
if (bit_depth == 16)
|
||||
png_set_strip_16(png);
|
||||
|
||||
@@ -363,9 +351,7 @@ namespace ZL
|
||||
png_set_gray_to_rgb(png);
|
||||
|
||||
png_read_update_info(png, info);
|
||||
// ====================================================
|
||||
|
||||
// === ������ �������� (�������� ��� ���������) ===
|
||||
|
||||
png_bytep* row_pointers = (png_bytep*)malloc(sizeof(png_bytep) * texData.height);
|
||||
for (int y = 0; y < texData.height; y++) {
|
||||
row_pointers[y] = (png_byte*)malloc(png_get_rowbytes(png, info));
|
||||
@@ -379,11 +365,11 @@ namespace ZL
|
||||
|
||||
if (has_alpha)
|
||||
{
|
||||
texData.bitSize = TextureDataStruct::BS_32BIT;
|
||||
texData.format = TextureDataStruct::RGBA;
|
||||
}
|
||||
else
|
||||
{
|
||||
texData.bitSize = TextureDataStruct::BS_24BIT;
|
||||
texData.format = TextureDataStruct::RGB;
|
||||
}
|
||||
|
||||
int channels = has_alpha ? 4 : 3;
|
||||
@@ -424,7 +410,6 @@ namespace ZL
|
||||
throw std::runtime_error("Could not read file data into memory");
|
||||
}
|
||||
|
||||
// �������� ����� �������, ������� �������� � �������� ����
|
||||
return CreateTextureDataFromPng(fileArr);
|
||||
}
|
||||
|
||||
|
||||
+10
-13
@@ -1,4 +1,4 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include "render/OpenGlExtensions.h"
|
||||
#include "utils/Utils.h"
|
||||
@@ -15,23 +15,20 @@ namespace ZL
|
||||
size_t height;
|
||||
std::vector<char> data;
|
||||
|
||||
enum BitSize {
|
||||
BS_24BIT,
|
||||
BS_32BIT
|
||||
enum Format {
|
||||
R8, // Для шрифтов (GL_RED)
|
||||
RGB, // BS_24BIT
|
||||
RGBA // BS_32BIT
|
||||
};
|
||||
|
||||
|
||||
// ����� ������������ ��� ���������� ���-���������
|
||||
enum MipmapType {
|
||||
NONE, // ��� ���-����� (GL_LINEAR)
|
||||
GENERATE // �������������� ��������� (GL_LINEAR_MIPMAP_LINEAR)
|
||||
NONE,
|
||||
GENERATE
|
||||
};
|
||||
|
||||
BitSize bitSize;
|
||||
/*#ifdef SIMPLIFIED
|
||||
MipmapType mipmap = NONE; // �� ��������� ��������
|
||||
#else*/
|
||||
MipmapType mipmap = GENERATE; // �� ��������� ��������
|
||||
//#endif
|
||||
Format format = RGBA;
|
||||
MipmapType mipmap = GENERATE;
|
||||
};
|
||||
class Texture
|
||||
{
|
||||
|
||||
+32
-31
@@ -1,4 +1,4 @@
|
||||
#include "utils/Utils.h"
|
||||
#include "utils/Utils.h"
|
||||
#include <cstring>
|
||||
#include <iterator>
|
||||
#include <vector>
|
||||
@@ -98,44 +98,45 @@ namespace ZL
|
||||
|
||||
std::vector<char> readFileFromZIP(const std::string& filename, const std::string& zipfilename) {
|
||||
#ifdef EMSCRIPTEN
|
||||
const std::string zipPath = zipfilename;
|
||||
int zipErr;
|
||||
zip_t* archive = zip_open(zipPath.c_str(), ZIP_RDONLY, &zipErr);
|
||||
if (!archive) {
|
||||
throw std::runtime_error("?????? ???????? ZIP: " + zipPath);
|
||||
}
|
||||
int zipErr = 0;
|
||||
zip_t* archive = zip_open(zipfilename.c_str(), ZIP_RDONLY, &zipErr);
|
||||
if (!archive) {
|
||||
std::cout << "Failed to open ZIP: " << zipfilename << " (error code " << zipErr << ")" << std::endl;
|
||||
throw std::runtime_error("Failed to open ZIP: " + zipfilename);
|
||||
}
|
||||
|
||||
std::string cleanFilename = filename;
|
||||
if (cleanFilename.rfind("./", 0) == 0) {
|
||||
cleanFilename = cleanFilename.substr(2);
|
||||
}
|
||||
std::string cleanFilename = filename;
|
||||
if (cleanFilename.rfind("./", 0) == 0) cleanFilename = cleanFilename.substr(2);
|
||||
|
||||
std::cout << "???? ? ZIP: " << cleanFilename << std::endl;
|
||||
zip_stat_t fileStat;
|
||||
zip_stat_init(&fileStat);
|
||||
if (zip_stat(archive, cleanFilename.c_str(), 0, &fileStat) != 0 || !(fileStat.valid & ZIP_STAT_SIZE)) {
|
||||
zip_close(archive);
|
||||
std::cout << "Failed to stat file in ZIP: " << cleanFilename << " in " << zipfilename << std::endl;
|
||||
throw std::runtime_error("Can't stat file in ZIP: " + cleanFilename);
|
||||
}
|
||||
|
||||
zip_file_t* zipFile = zip_fopen(archive, cleanFilename.c_str(), 0);
|
||||
if (!zipFile) {
|
||||
zip_close(archive);
|
||||
throw std::runtime_error("???? ?? ?????? ? ZIP: " + cleanFilename);
|
||||
}
|
||||
zip_file_t* zipFile = zip_fopen(archive, cleanFilename.c_str(), 0);
|
||||
if (!zipFile) {
|
||||
zip_close(archive);
|
||||
std::cout << "Failed to open file in ZIP: " << cleanFilename << std::endl;
|
||||
throw std::runtime_error("Can't open file in ZIP: " + cleanFilename);
|
||||
}
|
||||
|
||||
zip_stat_t fileStat;
|
||||
if (zip_stat(archive, cleanFilename.c_str(), 0, &fileStat) != 0) {
|
||||
zip_fclose(zipFile);
|
||||
zip_close(archive);
|
||||
throw std::runtime_error("?????? ?????? ZIP-??????????.");
|
||||
}
|
||||
std::vector<char> fileData(static_cast<size_t>(fileStat.size));
|
||||
zip_int64_t bytesRead = zip_fread(zipFile, fileData.data(), fileStat.size);
|
||||
|
||||
std::vector<char> fileData;
|
||||
fileData.resize(fileStat.size);
|
||||
// Обязательно закрываем всё перед проверкой результата или throw
|
||||
zip_fclose(zipFile);
|
||||
zip_close(archive);
|
||||
|
||||
zip_fread(zipFile, fileData.data(), fileData.size());
|
||||
if (bytesRead < 0 || static_cast<zip_uint64_t>(bytesRead) != fileStat.size) {
|
||||
std::cout << "Failed to read file from ZIP: " << cleanFilename << " (bytes read: " << bytesRead << ", expected: " << fileStat.size << ")" << std::endl;
|
||||
throw std::runtime_error("Error reading data from ZIP: " + cleanFilename);
|
||||
}
|
||||
|
||||
zip_fclose(zipFile);
|
||||
zip_close(archive);
|
||||
|
||||
return fileData;
|
||||
return fileData;
|
||||
#else
|
||||
// ???????
|
||||
|
||||
if (zipfilename.empty()) {
|
||||
std::cerr << "readFileFromZIP: zipfilename empty" << std::endl;
|
||||
|
||||
Reference in new issue
Block a user