430 lines
13 KiB
C++
Executable File
430 lines
13 KiB
C++
Executable File
#include "TextureManager.h"
|
|
#ifdef PNG_ENABLED
|
|
#include "png.h"
|
|
#endif
|
|
#include <iostream>
|
|
|
|
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);
|
|
|
|
CheckGlError();
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
|
|
CheckGlError();
|
|
|
|
//This should be only for Windows
|
|
//glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
|
|
|
|
CheckGlError();
|
|
|
|
if (texData.bitSize == TextureDataStruct::BS_24BIT)
|
|
{
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, static_cast<GLsizei>(texData.width), static_cast<GLsizei>(texData.height), 0, GL_RGB, GL_UNSIGNED_BYTE, &texData.data[0]);
|
|
}
|
|
else
|
|
{
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, static_cast<GLsizei>(texData.width), static_cast<GLsizei>(texData.height), 0, GL_RGBA, GL_UNSIGNED_BYTE, &texData.data[0]);
|
|
}
|
|
|
|
CheckGlError();
|
|
|
|
}
|
|
|
|
Texture::Texture(const std::array<TextureDataStruct, 6>& texDataArray)
|
|
{
|
|
// Ïðîâåðêà, ÷òî âñå ãðàíè èìåþò îäèíàêîâûå ðàçìåðû
|
|
width = texDataArray[0].width;
|
|
height = texDataArray[0].height;
|
|
|
|
for (size_t i = 1; i < 6; ++i) {
|
|
if (texDataArray[i].width != width || texDataArray[i].height != height) {
|
|
throw std::runtime_error("Cubemap faces must have the same dimensions");
|
|
}
|
|
}
|
|
|
|
glGenTextures(1, &texID);
|
|
|
|
if (texID == 0)
|
|
{
|
|
throw std::runtime_error("glGenTextures did not work for cubemap");
|
|
}
|
|
|
|
glBindTexture(GL_TEXTURE_CUBE_MAP, texID);
|
|
|
|
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
|
|
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
|
|
#endif
|
|
|
|
CheckGlError(); // Ïðîâåðêà ïîñëå óñòàíîâêè ïàðàìåòðîâ
|
|
|
|
// Çàãðóçêà äàííûõ äëÿ êàæäîé èç 6 ãðàíåé
|
|
// GL_TEXTURE_CUBE_MAP_POSITIVE_X + i äàåò ãðàíè: +X (0), -X (1), +Y (2), -Y (3), +Z (4), -Z (5)
|
|
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)
|
|
{
|
|
internalFormat = GL_RGB; // internalFormat
|
|
format = GL_RGB; // format
|
|
}
|
|
else // BS_32BIT
|
|
{
|
|
internalFormat = GL_RGBA; // internalFormat
|
|
format = GL_RGBA; // format
|
|
}
|
|
|
|
glTexImage2D(
|
|
GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, // Öåëåâàÿ ãðàíü
|
|
0, // Óðîâåíü MIP-òåêñòóðû
|
|
internalFormat, // Âíóòðåííèé ôîðìàò (äîëæåí ñîâïàäàòü ñ ôîðìàòîì)
|
|
static_cast<GLsizei>(width),
|
|
static_cast<GLsizei>(height),
|
|
0, // Ãðàíèöà (âñåãäà 0)
|
|
format, // Ôîðìàò èñõîäíûõ äàííûõ
|
|
GL_UNSIGNED_BYTE, // Òèï äàííûõ
|
|
texDataArray[i].data.data() // Óêàçàòåëü íà äàííûå
|
|
);
|
|
CheckGlError();
|
|
}
|
|
|
|
// Ñíèìàåì ïðèâÿçêó äëÿ ÷èñòîòû
|
|
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
|
|
}
|
|
|
|
Texture::~Texture()
|
|
{
|
|
glDeleteTextures(1, &texID);
|
|
texID = 0;
|
|
}
|
|
|
|
GLuint Texture::getTexID()
|
|
{
|
|
return texID;
|
|
}
|
|
|
|
size_t Texture::getWidth()
|
|
{
|
|
return width;
|
|
}
|
|
|
|
size_t Texture::getHeight()
|
|
{
|
|
return height;
|
|
}
|
|
|
|
|
|
|
|
TextureDataStruct CreateTextureDataFromBmp24(const std::string& fullFileName, const std::string& ZIPFileName)
|
|
{
|
|
|
|
TextureDataStruct texData;
|
|
std::vector<char> fileArr;
|
|
|
|
fileArr = !ZIPFileName.empty() ? readFileFromZIP(fullFileName, ZIPFileName) : readFile(fullFileName);
|
|
|
|
size_t fileSize = fileArr.size();
|
|
|
|
if (fileSize < 22)
|
|
{
|
|
throw std::runtime_error("File is too short or not correct!");
|
|
}
|
|
|
|
//This refers to BITMAPV5HEADER
|
|
texData.width = *reinterpret_cast<uint32_t*>(&fileArr[18]);
|
|
texData.height = *reinterpret_cast<uint32_t*>(&fileArr[22]);
|
|
|
|
texData.bitSize = TextureDataStruct::BS_24BIT;
|
|
|
|
size_t dataSize = texData.width * texData.height * 3;
|
|
|
|
texData.data.resize(dataSize);
|
|
|
|
size_t pos = *reinterpret_cast<uint32_t*>(&fileArr[10]);
|
|
size_t x = 0;
|
|
|
|
for (size_t i = 0; i < texData.width; i++)
|
|
for (size_t j = 0; j < texData.height; j++)
|
|
{
|
|
|
|
if (pos + 3 > fileSize)
|
|
{
|
|
throw std::runtime_error("File is too short!");
|
|
}
|
|
|
|
|
|
x = (i * texData.height + j) + (i * texData.height + j) + (i * texData.height + j);
|
|
|
|
texData.data[x + 2] = fileArr[pos++];
|
|
texData.data[x + 1] = fileArr[pos++];
|
|
texData.data[x + 0] = fileArr[pos++];
|
|
|
|
}
|
|
|
|
return texData;
|
|
}
|
|
|
|
TextureDataStruct CreateTextureDataFromBmp32(const std::string& fullFileName, const std::string& ZIPFileName)
|
|
{
|
|
|
|
TextureDataStruct texData;
|
|
std::vector<char> fileArr;
|
|
|
|
fileArr = !ZIPFileName.empty() ? readFileFromZIP(fullFileName, ZIPFileName) : readFile(fullFileName);
|
|
|
|
size_t fileSize = fileArr.size();
|
|
|
|
if (fileSize < 22)
|
|
{
|
|
throw std::runtime_error("File is too short or not correct!");
|
|
}
|
|
|
|
//This refers to BITMAPV5HEADER
|
|
texData.width = *reinterpret_cast<uint32_t*>(&fileArr[18]);
|
|
texData.height = *reinterpret_cast<uint32_t*>(&fileArr[22]);
|
|
|
|
texData.bitSize = TextureDataStruct::BS_32BIT;
|
|
|
|
size_t dataSize = texData.width * texData.height * 4;
|
|
|
|
texData.data.resize(dataSize);
|
|
|
|
size_t pos = *reinterpret_cast<uint32_t*>(&fileArr[10]);
|
|
size_t x = 0;
|
|
|
|
for (size_t i = 0; i < texData.width; i++)
|
|
for (size_t j = 0; j < texData.height; j++)
|
|
{
|
|
|
|
if (pos + 4 > fileSize)
|
|
{
|
|
throw std::runtime_error("File is too short!");
|
|
}
|
|
|
|
|
|
x = (i * texData.height + j) + (i * texData.height + j) + (i * texData.height + j) + (i * texData.height + j);
|
|
|
|
texData.data[x + 2] = fileArr[pos++];
|
|
texData.data[x + 1] = fileArr[pos++];
|
|
texData.data[x + 0] = fileArr[pos++];
|
|
texData.data[x + 3] = fileArr[pos++];
|
|
|
|
}
|
|
|
|
return texData;
|
|
}
|
|
|
|
#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; // Óñòàíàâëèâàåì, ÷òîáû ïðî÷èòàòü îñòàâøååñÿ
|
|
}
|
|
|
|
// Êîïèðóåì äàííûå èç íàøåãî ìàññèâà â áóôåð 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);
|
|
}
|
|
|
|
TextureDataStruct CreateTextureDataFromPng(const std::vector<char>& fileArr)
|
|
{
|
|
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);
|
|
if (!png) {
|
|
throw std::runtime_error("Could not create PNG read structure");
|
|
}
|
|
|
|
png_infop info = png_create_info_struct(png);
|
|
if (!info) {
|
|
png_destroy_read_struct(&png, nullptr, nullptr);
|
|
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);
|
|
texData.height = png_get_image_height(png, info);
|
|
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);
|
|
|
|
if (color_type == PNG_COLOR_TYPE_PALETTE)
|
|
png_set_palette_to_rgb(png);
|
|
|
|
if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
|
|
png_set_expand_gray_1_2_4_to_8(png);
|
|
|
|
if (png_get_valid(png, info, PNG_INFO_tRNS))
|
|
png_set_tRNS_to_alpha(png);
|
|
|
|
if (color_type == PNG_COLOR_TYPE_RGB ||
|
|
color_type == PNG_COLOR_TYPE_GRAY ||
|
|
color_type == PNG_COLOR_TYPE_PALETTE)
|
|
png_set_filler(png, 0xFF, PNG_FILLER_AFTER);
|
|
|
|
if (color_type == PNG_COLOR_TYPE_GRAY ||
|
|
color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
|
|
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));
|
|
}
|
|
|
|
png_read_image(png, row_pointers);
|
|
|
|
bool has_alpha = (color_type & PNG_COLOR_MASK_ALPHA) || (png_get_valid(png, info, PNG_INFO_tRNS));
|
|
|
|
size_t dataSize;
|
|
|
|
if (has_alpha)
|
|
{
|
|
texData.bitSize = TextureDataStruct::BS_32BIT;
|
|
}
|
|
else
|
|
{
|
|
texData.bitSize = TextureDataStruct::BS_24BIT;
|
|
}
|
|
|
|
int channels = has_alpha ? 4 : 3;
|
|
|
|
dataSize = texData.width * texData.height * channels;
|
|
texData.data.resize(dataSize);
|
|
|
|
|
|
for (int y = texData.height - 1; y >= 0; y--) {
|
|
png_bytep row = row_pointers[texData.height - 1 - y];
|
|
for (int x = 0; x < texData.width; x++) {
|
|
png_bytep px = &(row[x * 4]);
|
|
texData.data[(y * texData.width + x) * channels + 0] = px[0]; // R
|
|
texData.data[(y * texData.width + x) * channels + 1] = px[1]; // G
|
|
texData.data[(y * texData.width + x) * channels + 2] = px[2]; // B
|
|
if (has_alpha) {
|
|
texData.data[(y * texData.width + x) * channels + 3] = px[3]; // A
|
|
}
|
|
}
|
|
free(row_pointers[texData.height - 1 - y]);
|
|
}
|
|
free(row_pointers);
|
|
// ==================================================
|
|
|
|
png_destroy_read_struct(&png, &info, nullptr);
|
|
|
|
return texData;
|
|
}
|
|
|
|
|
|
TextureDataStruct CreateTextureDataFromPng(const std::string& fullFileName, const std::string& ZIPFileName)
|
|
{
|
|
std::vector<char> fileArr;
|
|
|
|
fileArr = !ZIPFileName.empty() ? readFileFromZIP(fullFileName, ZIPFileName) : readFile(fullFileName);
|
|
|
|
if (fileArr.empty()) {
|
|
throw std::runtime_error("Could not read file data into memory");
|
|
}
|
|
|
|
// Âûçûâàåì íîâóþ ôóíêöèþ, êîòîðàÿ ðàáîòàåò ñ ìàññèâîì áàéò
|
|
return CreateTextureDataFromPng(fileArr);
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
|
|
} |