#include "StoneObject.h" #include "Utils.h" #include #include #include #include "Renderer.h" #include "PlanetData.h" namespace ZL { // Вспомогательная функция для получения случайного числа в диапазоне [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 float BASE_SCALE = 3.0f; // Общий размер камня const float BASE_SCALE = 20.0f; // Общий размер камня const float MIN_AXIS_SCALE = 0.5f; // Минимальное растяжение/сжатие по оси const float MAX_AXIS_SCALE = 1.5f; // Максимальное растяжение/сжатие по оси const float MIN_PERTURBATION = 0.05f; // Минимальное радиальное возмущение вершины const float MAX_PERTURBATION = 0.25f; // Максимальное радиальное возмущение вершины // 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() * BASE_SCALE; // Нормализация к сфере радиуса BASE_SCALE // Радиальное возмущение: float perturbation = getRandomFloat(engine, MIN_PERTURBATION, MAX_PERTURBATION); v = v * (1.0f + perturbation); } // 2. Трансформация (Масштабирование и Поворот) // Случайные масштабы по осям Vector3f scaleFactors = { getRandomFloat(engine, MIN_AXIS_SCALE, MAX_AXIS_SCALE), getRandomFloat(engine, MIN_AXIS_SCALE, MAX_AXIS_SCALE), getRandomFloat(engine, MIN_AXIS_SCALE, 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; } VertexDataStruct CreateConvexPolyhedron(uint64_t seed, const LodLevel& planetLodLevel, const std::vector& triangleIndices) { VertexDataStruct finalMesh; const int STONES_PER_TRIANGLE = 100; // Константы трансформации (нужны здесь, чтобы каждый камень был уникальным) const float MIN_AXIS_SCALE = 0.5f; const float MAX_AXIS_SCALE = 1.5f; // 1. Создаем БАЗОВЫЙ МЕШ ОДИН РАЗ, чтобы не повторять сложную генерацию // Используем отдельный seed для базовой формы, чтобы она была уникальной, // но оставалась одинаковой при разных seed для размещения. uint64_t baseSeed = 1337; // Константный seed для формы камня VertexDataStruct baseStone = CreateBaseConvexPolyhedron(baseSeed); // 2. Итерируемся по заданным треугольникам for (int triangleIndex : triangleIndices) { std::mt19937 engine(static_cast(seed)); if (triangleIndex >= planetLodLevel.triangles.size()) { // Обработка ошибки continue; } const Triangle& currentTriangle = planetLodLevel.triangles[triangleIndex]; // 3. Генерируем 100 камней на каждом треугольнике for (int i = 0; i < STONES_PER_TRIANGLE; i++) { // --- 3.1. Генерируем случайное местоположение на треугольнике --- Vector3f stoneCenter = GetRandomPointOnTriangle(currentTriangle, engine); // --- 3.2. Применяем случайную трансформацию (Масштаб + Поворот) --- // Случайные масштабы Vector3f scaleFactors = { getRandomFloat(engine, MIN_AXIS_SCALE, MAX_AXIS_SCALE), getRandomFloat(engine, MIN_AXIS_SCALE, MAX_AXIS_SCALE), getRandomFloat(engine, MIN_AXIS_SCALE, MAX_AXIS_SCALE) }; // Случайный поворот (например, вокруг оси нормали к поверхности) // Для реалистичности, камень должен лежать на поверхности. // Вектор 'вверх' для камня должен быть выровнен по нормали треугольника. // 1. Создаем вращение вокруг нормали треугольника (currentTriangle.normal) float angle = getRandomFloat(engine, 0.0f, 360.0f); // (Для простоты здесь используется Matrix4f::Identity, но вам нужно реализовать // QuaternionFromAxisAngle для корректного вращения вокруг заданной оси Normal) // --- ПРОСТОЕ РЕШЕНИЕ: Случайный поворот вокруг трех осей --- 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); // --- 3.3. Трансформируем и Смещаем вершины базового меша --- // Копируем данные для текущего камня VertexDataStruct currentStone = baseStone; // Применяем масштабирование, поворот, и смещение к каждой вершине for (size_t j = 0; j < currentStone.PositionData.size(); j++) { Vector3f& pos = currentStone.PositionData[j]; Vector3f& norm = currentStone.NormalData[j]; // 1. Масштабирование pos.v[0] *= scaleFactors.v[0]; pos.v[1] *= scaleFactors.v[1]; pos.v[2] *= scaleFactors.v[2]; // 2. Поворот pos = MultMatrixVector(rotationMatrix, pos); norm = MultMatrixVector(rotationMatrix, norm); // 3. Смещение (Translation) pos = pos + stoneCenter; } // --- 3.4. Объединяем меши --- finalMesh.PositionData.insert(finalMesh.PositionData.end(), currentStone.PositionData.begin(), currentStone.PositionData.end()); finalMesh.NormalData.insert(finalMesh.NormalData.end(), currentStone.NormalData.begin(), currentStone.NormalData.end()); finalMesh.TexCoordData.insert(finalMesh.TexCoordData.end(), currentStone.TexCoordData.begin(), currentStone.TexCoordData.end()); // ... аналогично для других данных (Tangent, Binormal, Color) } } return finalMesh; } } // namespace ZL