#include "StoneObject.h" #include "utils/Utils.h" #include #include #include #include "render/Renderer.h" #include "PlanetData.h" namespace ZL { const float StoneParams::BASE_SCALE = 15.0f; // Общий размер камня const float StoneParams::MIN_AXIS_SCALE = 1.0f; // Минимальное растяжение/сжатие по оси const float StoneParams::MAX_AXIS_SCALE = 1.0f; // Максимальное растяжение/сжатие по оси const float StoneParams::MIN_PERTURBATION = 0.0f; // Минимальное радиальное возмущение вершины const float StoneParams::MAX_PERTURBATION = 0.0f; // Максимальное радиальное возмущение вершины /*const float StoneParams::MIN_AXIS_SCALE = 0.5f; // Минимальное растяжение/сжатие по оси const float StoneParams::MAX_AXIS_SCALE = 1.5f; // Максимальное растяжение/сжатие по оси const float StoneParams::MIN_PERTURBATION = 0.05f; // Минимальное радиальное возмущение вершины const float StoneParams::MAX_PERTURBATION = 0.25f; // Максимальное радиальное возмущение вершины */ const int StoneParams::STONES_PER_TRIANGLE = 40; // Вспомогательная функция для получения случайного числа в диапазоне [min, max] float getRandomFloat(std::mt19937& gen, float min, float max) { std::uniform_real_distribution<> distrib(min, max); return static_cast(distrib(gen)); } // Вспомогательная функция для генерации случайной точки на треугольнике // Использует барицентрические координаты Vector3f GetRandomPointOnTriangle(const Triangle& t, std::mt19937& engine) { std::uniform_real_distribution<> distrib(0.0f, 1.0f); float r1 = getRandomFloat(engine, 0.0f, 1.0f); float r2 = getRandomFloat(engine, 0.0f, 1.0f); // Преобразование r1, r2 для получения равномерного распределения float a = 1.0f - std::sqrt(r1); float b = std::sqrt(r1) * r2; float c = 1.0f - a - b; // c = sqrt(r1) * (1 - r2) // Барицентрические координаты // P = a*p1 + b*p2 + c*p3 Vector3f p1_term = t.data[0] * a; Vector3f p2_term = t.data[1] * b; Vector3f p3_term = t.data[2] * c; return p1_term + p2_term + p3_term; } // Икосаэдр (на основе золотого сечения phi) // Координаты могут быть вычислены заранее для константного икосаэдра. // Здесь только объявление, чтобы показать идею. VertexDataStruct CreateBaseConvexPolyhedron(uint64_t seed) { // const size_t SUBDIVISION_LEVEL = 1; // Уровень подразделения (для более круглого камня, пока опустим) std::mt19937 engine(static_cast(seed)); // Золотое сечение const float t = (1.0f + std::sqrt(5.0f)) / 2.0f; // 12 вершин икосаэдра std::vector initialVertices = { { -1, t, 0 }, { 1, t, 0 }, { -1, -t, 0 }, { 1, -t, 0 }, { 0, -1, t }, { 0, 1, t }, { 0, -1, -t }, { 0, 1, -t }, { t, 0, -1 }, { t, 0, 1 }, { -t, 0, -1 }, { -t, 0, 1 } }; // 20 треугольных граней (индексы вершин) std::vector> faces = { // 5 треугольников вокруг вершины 0 {0, 11, 5}, {0, 5, 1}, {0, 1, 7}, {0, 7, 10}, {0, 10, 11}, // 5 смежных полос {1, 5, 9}, {5, 11, 4}, {11, 10, 2}, {10, 7, 6}, {7, 1, 8}, // 5 треугольников вокруг вершины 3 {3, 9, 4}, {3, 4, 2}, {3, 2, 6}, {3, 6, 8}, {3, 8, 9}, // 5 смежных полос {4, 9, 5}, {2, 4, 11}, {6, 2, 10}, {8, 6, 7}, {9, 8, 1} }; // 1. Нормализация и Возмущение (Perturbation) for (Vector3f& v : initialVertices) { v = v.normalized() * StoneParams::BASE_SCALE; // Нормализация к сфере радиуса BASE_SCALE // Радиальное возмущение: float perturbation = getRandomFloat(engine, StoneParams::MIN_PERTURBATION, StoneParams::MAX_PERTURBATION); v = v * (1.0f + perturbation); } // 2. Трансформация (Масштабирование и Поворот) // Случайные масштабы по осям Vector3f scaleFactors = { getRandomFloat(engine, StoneParams::MIN_AXIS_SCALE, StoneParams::MAX_AXIS_SCALE), getRandomFloat(engine, StoneParams::MIN_AXIS_SCALE, StoneParams::MAX_AXIS_SCALE), getRandomFloat(engine, StoneParams::MIN_AXIS_SCALE, StoneParams::MAX_AXIS_SCALE) }; // Применяем масштабирование for (Vector3f& v : initialVertices) { v.v[0] *= scaleFactors.v[0]; v.v[1] *= scaleFactors.v[1]; v.v[2] *= scaleFactors.v[2]; } /* // Случайный поворот (например, вокруг трех осей) Vector4f qx = QuatFromRotateAroundX(getRandomFloat(engine, 0.0f, 360.0f)); Vector4f qy = QuatFromRotateAroundY(getRandomFloat(engine, 0.0f, 360.0f)); Vector4f qz = QuatFromRotateAroundZ(getRandomFloat(engine, 0.0f, 360.0f)); Vector4f qFinal = slerp(qx, qy, 0.5f); // Простой пример комбинирования qFinal = slerp(qFinal, qz, 0.5f).normalized(); Matrix3f rotationMatrix = QuatToMatrix(qFinal); for (Vector3f& v : initialVertices) { v = MultMatrixVector(rotationMatrix, v); }*/ // 3. Сглаженные Нормали и Формирование Mesh VertexDataStruct result; // Карта для накопления нормалей по уникальным позициям вершин // (Требует определенного оператора < для Vector3f в ZLMath.h, который у вас есть) std::map smoothNormalsMap; // Предварительное заполнение карты нормалями for (const auto& face : faces) { Vector3f p1 = initialVertices[face[0]]; Vector3f p2 = initialVertices[face[1]]; Vector3f p3 = initialVertices[face[2]]; // Нормаль грани: (p2 - p1) x (p3 - p1) Vector3f faceNormal = (p2 - p1).cross(p3 - p1).normalized(); smoothNormalsMap[p1] = smoothNormalsMap[p1] + faceNormal; smoothNormalsMap[p2] = smoothNormalsMap[p2] + faceNormal; smoothNormalsMap[p3] = smoothNormalsMap[p3] + faceNormal; } // Нормализация накопленных нормалей for (auto& pair : smoothNormalsMap) { pair.second = pair.second.normalized(); } // 4. Финальное заполнение VertexDataStruct и Текстурные Координаты for (const auto& face : faces) { Vector3f p1 = initialVertices[face[0]]; Vector3f p2 = initialVertices[face[1]]; Vector3f p3 = initialVertices[face[2]]; // Позиции result.PositionData.push_back(p1); result.PositionData.push_back(p2); result.PositionData.push_back(p3); // Сглаженные Нормали (из карты) result.NormalData.push_back(smoothNormalsMap[p1]); result.NormalData.push_back(smoothNormalsMap[p2]); result.NormalData.push_back(smoothNormalsMap[p3]); // Текстурные Координаты (Планарная проекция на плоскость грани) // p1 -> (0, 0), p2 -> (L_12, 0), p3 -> (L_13 * cos(angle), L_13 * sin(angle)) // Где L_xy - длина вектора, angle - угол между p2-p1 и p3-p1 Vector3f uAxis = (p2 - p1).normalized(); Vector3f vRaw = p3 - p1; // Проекция vRaw на uAxis float uProjLen = vRaw.dot(uAxis); // Вектор V перпендикулярный U: vRaw - uProj Vector3f vAxisRaw = vRaw - (uAxis * uProjLen); // Длина оси V float vLen = vAxisRaw.length(); // Нормализованная ось V Vector3f vAxis = vAxisRaw.normalized(); // Координаты (u, v) для p1, p2, p3 относительно p1 Vector2f uv1 = { 0.0f, 0.0f }; Vector2f uv2 = { (p2 - p1).length(), 0.0f }; // p2-p1 вдоль оси U Vector2f uv3 = { uProjLen, vLen }; // p3-p1: u-компонента = uProjLen, v-компонента = vLen // Находим максимальный размер, чтобы масштабировать в [0, 1] float maxUV = max(uv2.v[0], max(uv3.v[0], uv3.v[1])); if (maxUV > 0.000001f) { // Масштабируем: result.TexCoordData.push_back(uv1); result.TexCoordData.push_back(uv2 * (1.f / maxUV)); result.TexCoordData.push_back(uv3 * (1.f / maxUV)); } else { // Предотвращение деления на ноль для вырожденных граней result.TexCoordData.push_back({ 0.0f, 0.0f }); result.TexCoordData.push_back({ 0.0f, 0.0f }); result.TexCoordData.push_back({ 0.0f, 0.0f }); } } return result; } Triangle createLocalTriangle(const Triangle& sampleTri) { // Находим центр в 3D Vector3f center = (sampleTri.data[0] + sampleTri.data[1] + sampleTri.data[2]) * (1.0f / 3.0f); // Строим базис самого треугольника // vY направляем на 0-ю вершину (как в вашем Special расчете) Vector3f vY = (sampleTri.data[0] - center).normalized(); // Временный X для расчета нормали Vector3f vX_temp = (sampleTri.data[1] - sampleTri.data[2]).normalized(); // Чистая нормаль Vector3f vZ = vX_temp.cross(vY).normalized(); // Чистый X, перпендикулярный Y и Z Vector3f vX = vY.cross(vZ).normalized(); // Переводим 3D точки в этот 2D базис (Z зануляется сам собой) auto toLocal = [&](const Vector3f& p) { Vector3f d = p - center; return Vector3f{ d.dot(vX), d.dot(vY), 0.0f }; }; Triangle local; local.data[0] = toLocal(sampleTri.data[0]); local.data[1] = toLocal(sampleTri.data[1]); local.data[2] = toLocal(sampleTri.data[2]); return local; } StoneGroup CreateStoneGroupData(uint64_t globalSeed, const LodLevel& planetLodLevel) { StoneGroup group; group.allInstances.resize(planetLodLevel.triangles.size()); for (size_t tIdx = 0; tIdx < planetLodLevel.triangles.size(); ++tIdx) { const Triangle& tri = planetLodLevel.triangles[tIdx]; std::mt19937 engine(static_cast(globalSeed)); for (int i = 0; i < StoneParams::STONES_PER_TRIANGLE; ++i) { StoneInstance instance; instance.seed = globalSeed;// + tIdx * 1000 + i; instance.position = GetRandomPointOnTriangle(tri, engine); float SCALE_MIN = 0.75f; float SCALE_MAX = 2.5f; instance.scale = { getRandomFloat(engine, SCALE_MIN, SCALE_MAX), getRandomFloat(engine, SCALE_MIN, SCALE_MAX), getRandomFloat(engine, SCALE_MIN, SCALE_MAX) }; /* if (tIdx == 0) { instance.scale = instance.scale * 0.7f; }*/ /* Vector4f qx = QuatFromRotateAroundX(getRandomFloat(engine, 0.0f, 360.0f)); Vector4f qy = QuatFromRotateAroundY(getRandomFloat(engine, 0.0f, 360.0f)); Vector4f qz = QuatFromRotateAroundZ(getRandomFloat(engine, 0.0f, 360.0f)); instance.rotation = slerp(slerp(qx, qy, 0.5f), qz, 0.5f).normalized(); */ instance.rotation = Vector4f(0.f, 0.f, 0.f, 1.f); group.allInstances[tIdx].push_back(instance); } } return group; } std::vector StoneGroup::inflate(int count) { //static VertexDataStruct baseStone = CreateBaseConvexPolyhedron(1337); std::vector result; result.resize(count); for (int tIdx = 0; tIdx < count; tIdx++) { result[tIdx] = inflateOne(tIdx, 1.0f); /* for (const auto& inst : allInstances[tIdx]) { Matrix3f rotMat = QuatToMatrix(inst.rotation); for (size_t j = 0; j < baseStone.PositionData.size(); ++j) { Vector3f p = baseStone.PositionData[j]; Vector3f n = baseStone.NormalData[j]; p.v[0] *= inst.scale.v[0]; p.v[1] *= inst.scale.v[1]; p.v[2] *= inst.scale.v[2]; result[tIdx].data.PositionData.push_back(MultMatrixVector(rotMat, p) + inst.position); result[tIdx].data.NormalData.push_back(MultMatrixVector(rotMat, n)); result[tIdx].data.TexCoordData.push_back(baseStone.TexCoordData[j]); } result[tIdx].RefreshVBO(); }*/ } return result; } VertexRenderStruct StoneGroup::inflateOne(int index, float scaleModifier) { static VertexDataStruct baseStone = CreateBaseConvexPolyhedron(1337); VertexRenderStruct result; for (const auto& inst : allInstances[index]) { Matrix3f rotMat = QuatToMatrix(inst.rotation); for (size_t j = 0; j < baseStone.PositionData.size(); ++j) { Vector3f p = baseStone.PositionData[j]; Vector3f n = baseStone.NormalData[j]; p.v[0] *= inst.scale.v[0] * scaleModifier; p.v[1] *= inst.scale.v[1] * scaleModifier; p.v[2] *= inst.scale.v[2] * scaleModifier; result.data.PositionData.push_back(MultMatrixVector(rotMat, p) + inst.position); result.data.NormalData.push_back(MultMatrixVector(rotMat, n)); result.data.TexCoordData.push_back(baseStone.TexCoordData[j]); } result.RefreshVBO(); } return result; } } // namespace ZL