#include "StoneObject.h" #include "Utils.h" #include #include #include #include "Renderer.h" #include "PlanetData.h" namespace ZL { // --- КОНСТАНТЫ ПАРАМЕТРОВ (как вы просили) --- const float StoneParams::BASE_SCALE = 17.0f; // Общий размер камня //const float StoneParams::BASE_SCALE = 5000.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 = 100; // Вспомогательная функция для получения случайного числа в диапазоне [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; } StoneGroup CreateStoneGroupData(uint64_t globalSeed, const LodLevel& planetLodLevel) { StoneGroup group; // Резервируем место под все треугольники текущего LOD 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); //instance.position = Vector3f(5000.0f, 5000.0f, 5000.0f); // Генерируем случайные параметры один раз instance.scale = { getRandomFloat(engine, 0.5f, 1.5f), getRandomFloat(engine, 0.5f, 1.5f), getRandomFloat(engine, 0.5f, 1.5f) }; 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(); group.allInstances[tIdx].push_back(instance); } } return group; } void StoneGroup::inflate(const std::vector& triangleIndices) { // 1. Очищаем текущий меш перед заполнением mesh.PositionData.clear(); mesh.NormalData.clear(); mesh.TexCoordData.clear(); static VertexDataStruct baseStone = CreateBaseConvexPolyhedron(1337); // 2. Наполняем меш только для видимых треугольников for (int tIdx : triangleIndices) { if (tIdx >= allInstances.size()) continue; 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]; mesh.PositionData.push_back(MultMatrixVector(rotMat, p) + inst.position); mesh.NormalData.push_back(MultMatrixVector(rotMat, n)); mesh.TexCoordData.push_back(baseStone.TexCoordData[j]); } } } } } // namespace ZL