shadow-over-bishkek001/src/TextModel.cpp
2026-05-01 21:47:54 +03:00

275 lines
10 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "TextModel.h"
#include <regex>
#include <string>
#include <fstream>
#include <iostream>
#include <sstream>
#ifdef __ANDROID__
#include <android/log.h>
#endif
namespace ZL
{
static std::unordered_map<std::string, VertexDataStruct> s_meshCache;
static std::string CacheKey(const std::string& fileName)
{
if (fileName.size() > 4 && fileName.compare(fileName.size() - 4, 4, ".bin") == 0)
return fileName.substr(0, fileName.size() - 4);
return fileName;
}
VertexDataStruct LoadFromTextFile02(const std::string& fileName, const std::string& ZIPFileName)
{
std::string key = CacheKey(fileName);
auto it = s_meshCache.find(key);
if (it != s_meshCache.end())
return it->second;
VertexDataStruct result;
std::istringstream f;
// --- 1. Открытие потока (без изменений) ---
if (!ZIPFileName.empty())
{
std::vector<char> fileData = readFileFromZIP(fileName, ZIPFileName);
std::string fileContents(fileData.begin(), fileData.end());
f.str(fileContents);
}
else
{
#ifdef __ANDROID__
// Для Android используем SDL_RWops для чтения файлов
__android_log_print(ANDROID_LOG_INFO, "TextModel",
"LoadFromTextFile02 called for Android: %s", fileName.c_str());
#endif
// Читаем файл с помощью readTextFile
std::string fileContent = readTextFile(fileName);
if (fileContent.empty()) {
#ifdef __ANDROID__
__android_log_print(ANDROID_LOG_ERROR, "TextModel",
"Failed to read file: %s", fileName.c_str());
#endif
throw std::runtime_error("Failed to read file: " + fileName);
}
#ifdef __ANDROID__
__android_log_print(ANDROID_LOG_INFO, "TextModel",
"File read successfully, size: %zu", fileContent.size());
#endif
f.str(fileContent);
}
std::string tempLine;
std::smatch match;
// Обновленные регулярки
// pattern_float стал чуть надежнее для чисел вида "0" или "-1" без точки, если вдруг Python округлит до int
static const std::regex pattern_count(R"(\d+)");
static const std::regex pattern_float(R"([-+]?\d*\.?\d+([eE][-+]?\d+)?)");
static const std::regex pattern_int(R"([-]?\d+)");
// --- 2. Парсинг Вершин (Pos + Norm + UV) ---
// Ищем заголовок ===Vertices
while (std::getline(f, tempLine)) {
if (tempLine.find("===Vertices") != std::string::npos) break;
}
int numberVertices = 0;
if (std::regex_search(tempLine, match, pattern_count)) {
numberVertices = std::stoi(match.str());
}
else {
std::cout << "Vertices header not found or invalid: " << tempLine << std::endl;
throw std::runtime_error("Vertices header not found or invalid.");
}
// Временные буферы для хранения "уникальных" вершин перед разверткой по индексам
std::vector<Vector3f> tempPositions(numberVertices);
std::vector<Vector3f> tempNormals(numberVertices);
std::vector<Vector2f> tempUVs(numberVertices);
for (int i = 0; i < numberVertices; i++)
{
std::getline(f, tempLine);
// Строка вида: V 0: Pos(x, y, z) Norm(x, y, z) UV(u, v)
std::vector<float> floatValues;
floatValues.reserve(8); // Ожидаем ровно 8 чисел (3 pos + 3 norm + 2 uv)
auto b = tempLine.cbegin();
auto e = tempLine.cend();
while (std::regex_search(b, e, match, pattern_float)) {
floatValues.push_back(std::stof(match.str()));
b = match.suffix().first;
}
// Проверка целостности строки (ID вершины regex может поймать первым, но нас интересуют данные)
// Обычно ID идет первым (0), потом 3+3+2 float. Итого 9 чисел, если считать ID.
// Ваш Python пишет "V 0:", regex поймает 0. Потом 8 флоатов.
// Если regex ловит ID вершины как float (что вероятно), нам нужно смещение.
// ID - floatValues[0]
// Pos - [1], [2], [3]
// Norm - [4], [5], [6]
// UV - [7], [8]
if (floatValues.size() < 9) {
std::cout << "Malformed vertex line at index " << i << ": " << tempLine << std::endl;
throw std::runtime_error("Malformed vertex line at index " + std::to_string(i));
}
tempPositions[i] = Vector3f{ floatValues[1], floatValues[2], floatValues[3] };
tempNormals[i] = Vector3f{ floatValues[4], floatValues[5], floatValues[6] };
tempUVs[i] = Vector2f{ floatValues[7], floatValues[8] };
}
// --- 3. Парсинг Треугольников (Индексов) ---
// Пропускаем пустые строки до заголовка треугольников
while (std::getline(f, tempLine)) {
if (tempLine.find("===Triangles") != std::string::npos) break;
}
int numberTriangles = 0;
if (std::regex_search(tempLine, match, pattern_count)) {
numberTriangles = std::stoi(match.str());
}
else {
std::cout << "Triangles header not found or invalid: " << tempLine << std::endl;
throw std::runtime_error("Triangles header not found.");
}
// Резервируем память в result, чтобы избежать лишних аллокаций
result.PositionData.reserve(numberTriangles * 3);
result.NormalData.reserve(numberTriangles * 3);
result.TexCoordData.reserve(numberTriangles * 3);
for (int i = 0; i < numberTriangles; i++)
{
std::getline(f, tempLine);
// Строка вида: Tri: 0 1 2
std::vector<int> indices;
indices.reserve(3);
auto b = tempLine.cbegin();
auto e = tempLine.cend();
while (std::regex_search(b, e, match, pattern_int)) {
indices.push_back(std::stoi(match.str()));
b = match.suffix().first;
}
if (indices.size() != 3) {
std::cout << "Malformed triangle line at index " << i << ": " << tempLine << std::endl;
throw std::runtime_error("Malformed triangle line at index " + std::to_string(i));
}
// --- 4. Заполнение VertexDataStruct (Flattening) ---
// Берем данные из временных буферов по индексам и кладем в итоговый массив
for (int k = 0; k < 3; k++) {
int idx = indices[k];
result.PositionData.push_back(tempPositions[idx]);
result.NormalData.push_back(tempNormals[idx]);
result.TexCoordData.push_back(tempUVs[idx]);
}
}
// --- 5. Конвертация координат (Blender -> OpenGL/Engine) ---
// Сохраняем вашу логику смены осей: X->Z, Y->X, Z->Y
for (size_t i = 0; i < result.PositionData.size(); i++)
{
Vector3f originalPos = result.PositionData[i];
result.PositionData[i](0) = originalPos(1); // New X = Old Y
result.PositionData[i](1) = originalPos(2); // New Y = Old Z
result.PositionData[i](2) = originalPos(0); // New Z = Old X
Vector3f originalNorm = result.NormalData[i];
result.NormalData[i](0) = originalNorm(1);
result.NormalData[i](1) = originalNorm(2);
result.NormalData[i](2) = originalNorm(0);
}
std::cout << "Model loaded: " << numberVertices << " verts, " << numberTriangles << " tris." << std::endl;
s_meshCache[key] = result;
return result;
}
VertexDataStruct LoadModelFromBinFile(const std::string& fileName, const std::string& ZIPFileName)
{
std::string key = CacheKey(fileName);
auto it = s_meshCache.find(key);
if (it != s_meshCache.end())
return it->second;
std::vector<char> fileData = !ZIPFileName.empty()
? readFileFromZIP(fileName, ZIPFileName)
: readFile(fileName);
if (fileData.size() < 16)
throw std::runtime_error("Binary mesh file is too short: " + fileName);
const char* ptr = fileData.data();
if (ptr[0] != 'B' || ptr[1] != 'S' || ptr[2] != 'M' || ptr[3] != 'F')
throw std::runtime_error("Invalid magic bytes in binary mesh file: " + fileName);
ptr += 4;
uint32_t version = *reinterpret_cast<const uint32_t*>(ptr); ptr += 4;
if (version != 1)
throw std::runtime_error("Unsupported binary mesh version " + std::to_string(version) + ": " + fileName);
uint32_t numVertices = *reinterpret_cast<const uint32_t*>(ptr); ptr += 4;
uint32_t numTriangles = *reinterpret_cast<const uint32_t*>(ptr); ptr += 4;
const size_t expectedSize = 16
+ static_cast<size_t>(numVertices) * 8 * sizeof(float)
+ static_cast<size_t>(numTriangles) * 3 * sizeof(uint32_t);
if (fileData.size() < expectedSize)
throw std::runtime_error("Binary mesh file is truncated: " + fileName);
std::vector<Vector3f> positions(numVertices);
std::vector<Vector3f> normals(numVertices);
std::vector<Vector2f> uvs(numVertices);
for (uint32_t i = 0; i < numVertices; ++i)
{
positions[i](0) = *reinterpret_cast<const float*>(ptr); ptr += 4;
positions[i](1) = *reinterpret_cast<const float*>(ptr); ptr += 4;
positions[i](2) = *reinterpret_cast<const float*>(ptr); ptr += 4;
normals[i](0) = *reinterpret_cast<const float*>(ptr); ptr += 4;
normals[i](1) = *reinterpret_cast<const float*>(ptr); ptr += 4;
normals[i](2) = *reinterpret_cast<const float*>(ptr); ptr += 4;
uvs[i](0) = *reinterpret_cast<const float*>(ptr); ptr += 4;
uvs[i](1) = *reinterpret_cast<const float*>(ptr); ptr += 4;
}
VertexDataStruct result;
result.PositionData.reserve(numTriangles * 3);
result.NormalData.reserve(numTriangles * 3);
result.TexCoordData.reserve(numTriangles * 3);
for (uint32_t i = 0; i < numTriangles; ++i)
{
for (int k = 0; k < 3; ++k)
{
uint32_t idx = *reinterpret_cast<const uint32_t*>(ptr); ptr += 4;
if (idx >= numVertices)
throw std::runtime_error("Triangle index out of range in: " + fileName);
result.PositionData.push_back(positions[idx]);
result.NormalData.push_back(normals[idx]);
result.TexCoordData.push_back(uvs[idx]);
}
}
std::cout << "Binary model loaded: " << numVertices << " verts, " << numTriangles << " tris." << std::endl;
s_meshCache[key] = result;
return result;
}
}