/
spivag
/
command_occ
Обзор
Документация
Войти
/
spivag
/
command_occ
Код
Запросы
1
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
initial
glfw_app/glfw_app.cpp
1 451 строка
49 KB
Степанов Никита
Запрос на слияние 'topology_iterators' (
#3
) из topology_iterators в initial
27 апр 2026, 21:55
Верифицирован
27 апр 2026, 21:55
c047a95
Код
Авторство
О чём код?
#include <iostream> // #define GLAD_GL_IMPLEMENTATION #include <glad/glad.h> // #define GLFW_INCLUDE_NONE #include <GLFW/glfw3.h> #include <nlohmann-json/document_deserialize.h> #include <nlohmann-json/document_serialize.h> #include <opengl_mesh_factory.h> #include <part_mating.h> #include <part_structure.h> #include <shape_command.h> #include <shape_factory.h> #include <demo_brep_elementary.hxx> #include <demo_conductor_key.hxx> #include <demo_conjugate_3_parts.hxx> #include <demo_faces_conjugate.hxx> #include <glm/glm.hpp> #include <glm/gtc/matrix_transform.hpp> #include <glm/gtc/type_ptr.hpp> #include <optional> #include <render/gl1_raw_cache.hxx> #include <sstream> #include "brep_sections.h" #include "face_manipulate.h" #include "geometry_types.h" #include <gp_Pnt.hxx> #include <gp_Vec.hxx> #include <gp_Trsf.hxx> std::unique_ptr< Document > CreateDocumentForDisplay() { // return {}; // std::stringstream ssW; // auto shape = demo::BlockBrep::CreateWithFilletedEdges(); auto doc1 = std::make_unique< Document >(); doc1->AddShape( shape ); auto part = doc1->AddPart( "ruled", { shape } ).lock(); doc1->SetRootPart( part ); return doc1; } static GLfloat alpha = 210.f, beta = -70.f; static GLfloat zoom = .1f; static double cursorX; static double cursorY; static float sceneSize = 1.0f; struct GL1RenderContext { float glLineWidthValue; float glIsolineWidthValue; std::array< float, 3 > glDefaultFaceColourValue; std::array< float, 3 > glDefaultEdgeColourValue; std::array< float, 3 > glDefaultIsolineColourValue; }; static GL1RenderContext DefaultGL1RenderContext; enum class RenderStyle { colouredShader, smoothnessShader, isolines, attributeVisualization // Визуализация атрибутов (кривизна и т.д.) }; static RenderStyle appRenderStyle{ RenderStyle::colouredShader }; static bool useOrthographicProjection = false; // Текущий тип атрибута для визуализации static AttributeType currentAttributeType = AttributeType::None; // Глобальный кэш геометрии и параметры запуска для перезагрузки static GL1RawCache glCache{}; static int g_argc = 0; static const char** g_argv = nullptr; // Настраиваемый диапазон отображения атрибутов static float displayMinAttribute = -FLT_MAX; // Нижняя граница диапазона static float displayMaxAttribute = FLT_MAX; // Верхняя граница диапазона static bool useCustomRange = false; // Использовать ли настраиваемый диапазон static GLuint shaderProgramDisplay; static GLuint shaderProgramSmoothness; static GLuint shaderProgramAttribute; // Шейдер для визуализации атрибутов static GLuint colorMapTexture = 0; // 1D текстура для jet color map static glm::mat4 projection, view, model; // Vertex Shader for displayGLCache const char* vertexShaderDisplaySource = R"( #version 330 core layout (location = 0) in vec3 aPos; uniform mat4 model; uniform mat4 view; uniform mat4 projection; void main() { gl_Position = projection * view * model * vec4(aPos, 1.0); } )"; // Fragment Shader for displayGLCache const char* fragmentShaderDisplaySource = R"( #version 330 core out vec4 FragColor; uniform vec3 color; void main() { FragColor = vec4(color, 1.0); } )"; // Vertex Shader для визуализации атрибутов const char* vertexShaderAttributeSource = R"( #version 330 core layout(location = 0) in vec3 aPos; layout(location = 1) in float aAttribute; uniform mat4 model; uniform mat4 view; uniform mat4 projection; out float vAttribute; void main() { gl_Position = projection * view * model * vec4(aPos, 1.0); vAttribute = aAttribute; } )"; // Fragment Shader для визуализации атрибутов с 1D текстурой const char* fragmentShaderAttributeSource = R"( #version 330 core in float vAttribute; out vec4 FragColor; uniform float minAttribute; uniform float maxAttribute; uniform sampler1D colorMap; void main() { // Защита от деления на ноль float range = maxAttribute - minAttribute; if (abs(range) < 0.0001) { // Если диапазон очень маленький, используем среднее значение vec3 color = texture(colorMap, 0.5).rgb; FragColor = vec4(color, 1.0); } else { float normalized = (vAttribute - minAttribute) / range; // Проверяем выход за границы диапазона if (normalized < 0.0) { // Значения ниже минимума - magenta (пурпурный) FragColor = vec4(1.0, 0.0, 1.0, 1.0); } else if (normalized > 1.0) { // Значения выше максимума - белый FragColor = vec4(1.0, 1.0, 1.0, 1.0); } else { // Значения в диапазоне - используем color map vec3 color = texture(colorMap, normalized).rgb; FragColor = vec4(color, 1.0); } } } )"; // Vertex Shader for smoothness check // Vertex Shader for smoothness check const char* vertexShaderSmoothnessSource = R"( #version 330 core layout(location = 0) in vec3 aPos; layout(location = 1) in vec3 aNormal; uniform mat4 model; uniform mat4 view; uniform mat4 projection; out vec3 FragPos; out vec3 Normal; void main() { FragPos = vec3(view * model * vec4(aPos, 1.0)); Normal = mat3(transpose(inverse(view * model))) * aNormal; gl_Position = projection * vec4(FragPos, 1.0); } )"; // Zebra pattern shaders for smoothness checking class ZebraPatterns { public: enum class PatternType { CYLINDER_HORIZONTAL, // Цилиндр с осью слева направо (X) CYLINDER_DEPTH, // Цилиндр с осью вглубь сцены (Z) PLANE_BEHIND // Плоскость за спиной у наблюдателя }; static const char* GetShader( PatternType type = PatternType::CYLINDER_HORIZONTAL ) { switch ( type ) { case PatternType::CYLINDER_HORIZONTAL: return CYLINDER_HORIZONTAL_SHADER; case PatternType::CYLINDER_DEPTH: return CYLINDER_DEPTH_SHADER; case PatternType::PLANE_BEHIND: return PLANE_BEHIND_SHADER; default: return CYLINDER_HORIZONTAL_SHADER; } } private: // Цилиндр с горизонтальной осью (X) - полосы вдоль оси static constexpr const char* CYLINDER_HORIZONTAL_SHADER = R"( #version 330 core in vec3 FragPos; in vec3 Normal; uniform float sceneSize; out vec4 FragColor; void main() { vec3 viewDir = normalize(-FragPos); vec3 norm = normalize(Normal); float facing = dot(norm, viewDir); if (facing < 0.0) { FragColor = vec4(0.5, 0.5, 0.5, 1.0); return; } vec3 reflectDir = reflect(-viewDir, norm); // Проецируем на плоскость YZ (перпендикулярную оси X) vec2 reflectDirYZ = vec2(reflectDir.y, reflectDir.z); if (length(reflectDirYZ) < 0.001) { FragColor = vec4(0.5, 0.5, 0.5, 1.0); return; } float angle = atan(reflectDir.z, reflectDir.y); float normalizedAngle = (angle + 3.14159265359) / (2.0 * 3.14159265359); float stripeCoord = normalizedAngle * 16.0; float stripe = fract(stripeCoord); vec3 color = (stripe < 0.5) ? vec3(0.0) : vec3(1.0); FragColor = vec4(color, 1.0); } )"; // Цилиндр с осью вглубь сцены (Z) - полосы вдоль оси static constexpr const char* CYLINDER_DEPTH_SHADER = R"( #version 330 core in vec3 FragPos; in vec3 Normal; uniform float sceneSize; out vec4 FragColor; void main() { vec3 viewDir = normalize(-FragPos); vec3 norm = normalize(Normal); float facing = dot(norm, viewDir); if (facing < 0.0) { FragColor = vec4(0.5, 0.5, 0.5, 1.0); return; } vec3 reflectDir = reflect(-viewDir, norm); // Проецируем на плоскость XY (перпендикулярную оси Z) vec2 reflectDirXY = vec2(reflectDir.x, reflectDir.y); if (length(reflectDirXY) < 0.001) { FragColor = vec4(0.5, 0.5, 0.5, 1.0); return; } float angle = atan(reflectDir.y, reflectDir.x); float normalizedAngle = (angle + 3.14159265359) / (2.0 * 3.14159265359); float stripeCoord = normalizedAngle * 16.0; float stripe = fract(stripeCoord); vec3 color = (stripe < 0.5) ? vec3(0.0) : vec3(1.0); FragColor = vec4(color, 1.0); } )"; // Плоскость за спиной наблюдателя (перпендикулярная Z) static constexpr const char* PLANE_BEHIND_SHADER = R"( #version 330 core in vec3 FragPos; in vec3 Normal; uniform float sceneSize; out vec4 FragColor; void main() { vec3 viewDir = normalize(-FragPos); vec3 norm = normalize(Normal); float facing = dot(norm, viewDir); if (facing < 0.0) { FragColor = vec4(0.5, 0.5, 0.5, 1.0); return; } vec3 reflectDir = reflect(-viewDir, norm); // Плоскость за наблюдателем: z = sceneSize * 3.0 float planeZ = sceneSize * 3.0; if (abs(reflectDir.z) < 0.001) { FragColor = vec4(0.5, 0.5, 0.5, 1.0); return; } float t = (planeZ - FragPos.z) / reflectDir.z; if (t < 0.0) { FragColor = vec4(0.5, 0.5, 0.5, 1.0); return; } vec3 intersectionPoint = FragPos + t * reflectDir; float stripeWidth = sceneSize * 0.5; float stripeCoord = intersectionPoint.x / stripeWidth; float stripe = fract(stripeCoord * 0.5); vec3 color = (stripe < 0.5) ? vec3(0.0) : vec3(1.0); FragColor = vec4(color, 1.0); } )"; }; static bool CheckShaderCompileStatus( GLuint shader, const std::string& name ) { GLint success = 0; glGetShaderiv( shader, GL_COMPILE_STATUS, &success ); if ( success == GL_TRUE ) return true; GLint logLength = 0; glGetShaderiv( shader, GL_INFO_LOG_LENGTH, &logLength ); std::vector< GLchar > log( logLength ); glGetShaderInfoLog( shader, logLength, nullptr, log.data() ); std::cerr << "Shader compilation FAILED for: " << name << "\n" << log.data() << std::endl; return false; } static bool CheckProgramLinkStatus( GLuint program, const std::string& name ) { GLint success = 0; glGetProgramiv( program, GL_LINK_STATUS, &success ); if ( success == GL_TRUE ) return true; GLint logLength = 0; glGetProgramiv( program, GL_INFO_LOG_LENGTH, &logLength ); std::vector< GLchar > log( logLength ); glGetProgramInfoLog( program, logLength, nullptr, log.data() ); std::cerr << "Program linking FAILED for: " << name << "\n" << log.data() << std::endl; return false; } const char* fragmentShaderSmoothnessSource = ZebraPatterns::GetShader(); static void init_display_shaders() { // === Шейдер 1: Display (цветные грани) === GLuint vertexShaderDisplay = glCreateShader( GL_VERTEX_SHADER ); glShaderSource( vertexShaderDisplay, 1, &vertexShaderDisplaySource, NULL ); glCompileShader( vertexShaderDisplay ); if ( !CheckShaderCompileStatus( vertexShaderDisplay, "VertexShaderDisplay" ) ) return; GLuint fragmentShaderDisplay = glCreateShader( GL_FRAGMENT_SHADER ); glShaderSource( fragmentShaderDisplay, 1, &fragmentShaderDisplaySource, NULL ); glCompileShader( fragmentShaderDisplay ); if ( !CheckShaderCompileStatus( fragmentShaderDisplay, "FragmentShaderDisplay" ) ) return; shaderProgramDisplay = glCreateProgram(); glAttachShader( shaderProgramDisplay, vertexShaderDisplay ); glAttachShader( shaderProgramDisplay, fragmentShaderDisplay ); glLinkProgram( shaderProgramDisplay ); if ( !CheckProgramLinkStatus( shaderProgramDisplay, "ProgramDisplay" ) ) return; glDeleteShader( vertexShaderDisplay ); glDeleteShader( fragmentShaderDisplay ); // === Шейдер 2: Smoothness (zebra) === GLuint vertexShaderSmoothness = glCreateShader( GL_VERTEX_SHADER ); glShaderSource( vertexShaderSmoothness, 1, &vertexShaderSmoothnessSource, NULL ); glCompileShader( vertexShaderSmoothness ); if ( !CheckShaderCompileStatus( vertexShaderSmoothness, "VertexShaderSmoothness" ) ) return; GLuint fragmentShaderSmoothness = glCreateShader( GL_FRAGMENT_SHADER ); glShaderSource( fragmentShaderSmoothness, 1, &fragmentShaderSmoothnessSource, NULL ); glCompileShader( fragmentShaderSmoothness ); if ( !CheckShaderCompileStatus( fragmentShaderSmoothness, "FragmentShaderSmoothness" ) ) return; shaderProgramSmoothness = glCreateProgram(); glAttachShader( shaderProgramSmoothness, vertexShaderSmoothness ); glAttachShader( shaderProgramSmoothness, fragmentShaderSmoothness ); glLinkProgram( shaderProgramSmoothness ); if ( !CheckProgramLinkStatus( shaderProgramSmoothness, "ProgramSmoothness" ) ) return; glDeleteShader( vertexShaderSmoothness ); glDeleteShader( fragmentShaderSmoothness ); // === Шейдер 3: Attribute Visualization (кривизна + colormap) === GLuint vertexShaderAttribute = glCreateShader( GL_VERTEX_SHADER ); glShaderSource( vertexShaderAttribute, 1, &vertexShaderAttributeSource, NULL ); glCompileShader( vertexShaderAttribute ); if ( !CheckShaderCompileStatus( vertexShaderAttribute, "VertexShaderAttribute" ) ) return; GLuint fragmentShaderAttribute = glCreateShader( GL_FRAGMENT_SHADER ); glShaderSource( fragmentShaderAttribute, 1, &fragmentShaderAttributeSource, NULL ); glCompileShader( fragmentShaderAttribute ); if ( !CheckShaderCompileStatus( fragmentShaderAttribute, "FragmentShaderAttribute" ) ) return; shaderProgramAttribute = glCreateProgram(); glAttachShader( shaderProgramAttribute, vertexShaderAttribute ); glAttachShader( shaderProgramAttribute, fragmentShaderAttribute ); glLinkProgram( shaderProgramAttribute ); if ( !CheckProgramLinkStatus( shaderProgramAttribute, "ProgramAttribute" ) ) return; glDeleteShader( vertexShaderAttribute ); glDeleteShader( fragmentShaderAttribute ); // === Создание 1D текстуры colormap === (остаётся без изменений) glGenTextures( 1, &colorMapTexture ); glBindTexture( GL_TEXTURE_1D, colorMapTexture ); const int texSize = 256; float colorData[texSize * 3]; for ( int i = 0; i < texSize; ++i ) { float t = static_cast< float >( i ) / ( texSize - 1 ); float r, g, b; if ( t < 0.25f ) { r = 0.0f; g = t / 0.25f; b = 1.0f; } else if ( t < 0.5f ) { r = 0.0f; g = 1.0f; b = 1.0f - ( t - 0.25f ) / 0.25f; } else if ( t < 0.75f ) { r = ( t - 0.5f ) / 0.25f; g = 1.0f; b = 0.0f; } else { r = 1.0f; g = 1.0f - ( t - 0.75f ) / 0.25f; b = 0.0f; } colorData[i * 3 + 0] = r; colorData[i * 3 + 1] = g; colorData[i * 3 + 2] = b; } glTexImage1D( GL_TEXTURE_1D, 0, GL_RGB, texSize, 0, GL_RGB, GL_FLOAT, colorData ); glTexParameteri( GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); glTexParameteri( GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); glTexParameteri( GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); glBindTexture( GL_TEXTURE_1D, 0 ); } static void cleanup_opengl_resources() { // Delete shader programs if ( shaderProgramDisplay != 0 ) { glDeleteProgram( shaderProgramDisplay ); shaderProgramDisplay = 0; } if ( shaderProgramSmoothness != 0 ) { glDeleteProgram( shaderProgramSmoothness ); shaderProgramSmoothness = 0; } if ( shaderProgramAttribute != 0 ) { glDeleteProgram( shaderProgramAttribute ); shaderProgramAttribute = 0; } if ( colorMapTexture != 0 ) { glDeleteTextures( 1, &colorMapTexture ); colorMapTexture = 0; } } static void InitApp( int, const char** ); static void init_opengl( void ); static void InitWindowUIcallbacks( GLFWwindow* window ); static void framebuffer_size_callback( GLFWwindow* window, int width, int height ); static R3::Gabarit computeBoundingBox( const GL1RawCache& glCache ) { R3::Gabarit gab; for ( const auto& grid : glCache.grids ) { for ( const auto& triangle : grid.triangles ) { for ( const auto& point : triangle ) { gab.Append( R3::Point( point.xyz[0], point.xyz[1], point.xyz[2] ) ); } } } for ( const auto& polygon : glCache.polygons ) { for ( const auto& point : polygon.points ) { gab.Append( R3::Point( point.xyz[0], point.xyz[1], point.xyz[2] ) ); } } return gab; } static bool FillGLCache( std::shared_ptr< IShapeBuilder > const& shape, GL1RawCache& glCache, AttributeType attributeType = AttributeType::None ); static bool FillGLCache( std::shared_ptr< PartInstance > const& shape, GL1RawCache& glCache, AttributeType attributeType = AttributeType::None ); static bool FillGLCache( std::unique_ptr< Document > const&, GL1RawCache& glCache, AttributeType attributeType = AttributeType::None ); static void displayGLCache( GL1RawCache const& mesh, GL1RenderContext const& renderContext = DefaultGL1RenderContext ); // Перезагрузить кэш с текущим типом атрибута static void ReloadCache() { glCache = GL1RawCache{}; // Очистить кэш if ( g_argc > 1 ) { std::cout << "Reloading file: " << g_argv[1] << " with attribute type " << static_cast< int >( currentAttributeType ) << std::endl; std::ifstream file( g_argv[1], std::ios::in ); Document document = ::Restore( file ); ::FillGLCache( std::make_unique< Document >( document ), glCache, currentAttributeType ); } else { auto doc = CreateDocumentForDisplay(); if ( doc != nullptr ) { ::FillGLCache( doc, glCache, currentAttributeType ); } } // Отладочная информация std::cout << "Loaded " << glCache.grids.size() << " grids" << std::endl; int gridsWithAttributes = 0; int totalTriangles = 0; for ( const auto& grid : glCache.grids ) { totalTriangles += grid.triangles.size(); if ( grid.attributeInfo.isValid && grid.attributeInfo.type != AttributeType::None ) { gridsWithAttributes++; std::cout << " Grid with " << grid.triangles.size() << " triangles, attribute: " << grid.attributeInfo.name << ", range: [" << grid.attributeInfo.minValue << ", " << grid.attributeInfo.maxValue << "]" << std::endl; } else { std::cout << " Grid with " << grid.triangles.size() << " triangles, no valid attributes." << std::endl; } } std::cout << "Total triangles: " << totalTriangles << ", grids with attributes: " << gridsWithAttributes << std::endl; } // Main function that initializes window and draws scene int main( int argc, const char** argv ) { g_argc = argc; g_argv = argv; InitApp( argc, argv ); // Initialize GLFW if ( !glfwInit() ) return -1; int windowWidth{ 800 }, windowHeight{ 600 }; glfwWindowHint( GLFW_CONTEXT_VERSION_MAJOR, 3 ); glfwWindowHint( GLFW_CONTEXT_VERSION_MINOR, 3 ); glfwWindowHint( GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE ); // Window creation GLFWwindow* window = glfwCreateWindow( windowWidth, windowHeight, "Visualization Example", nullptr, nullptr ); if ( !window ) { glfwTerminate(); return -1; } InitWindowUIcallbacks( window ); // gladLoadGL( glfwGetProcAddress ); gladLoadGL(); std::cout << "OpenGL version: " << glGetString( GL_VERSION ) << std::endl; std::cout << "GLSL version: " << glGetString( GL_SHADING_LANGUAGE_VERSION ) << std::endl; glfwGetFramebufferSize( window, &windowWidth, &windowHeight ); framebuffer_size_callback( window, windowWidth, windowHeight ); init_opengl(); // Загружаем геометрию в глобальный кэш if ( argc > 1 ) { std::cout << "Reading file: " << argv[1] << " into a document" << std::endl; { std::ifstream file( argv[1], std::ios::in ); Document document = ::Restore( file ); ::FillGLCache( std::make_unique< Document >( document ), glCache, currentAttributeType ); } } else { auto doc = CreateDocumentForDisplay(); if ( doc != nullptr ) { ::FillGLCache( doc, glCache, currentAttributeType ); } } // Compute bounding box and adjust initial zoom auto gab = computeBoundingBox( glCache ); float sizeX = gab.SizeX(); float sizeY = gab.SizeY(); float sizeZ = gab.SizeZ(); float maxSize = std::max( { sizeX, sizeY, sizeZ } ); if ( maxSize > 0.0f ) { sceneSize = maxSize; float fovRadians = glm::radians( 60.0f ); float tanFov = tan( fovRadians / 2.0f ); // Adjust zoom to fit the entire figure with 10% margin zoom = ( maxSize / 2.0f ) / tanFov * 1.1f; std::cout << "Bounding box size: " << maxSize << ", Initial zoom: " << zoom << std::endl; } while ( !glfwWindowShouldClose( window ) ) { glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); // Update view matrix view = glm::mat4( 1.0f ); view = glm::translate( view, glm::vec3( 0.0f, 0.0f, -zoom ) ); view = glm::rotate( view, glm::radians( beta ), glm::vec3( 1.0f, 0.0f, 0.0f ) ); view = glm::rotate( view, glm::radians( alpha ), glm::vec3( 0.0f, 0.0f, 1.0f ) ); glMatrixMode( GL_MODELVIEW ); glLoadIdentity(); // We don't want to modify the projection matrix // Move back glTranslatef( 0.0, 0.0, -zoom ); // выставлять в зависимости от габаритов? // Rotate the view glRotatef( beta, 1.0, 0.0, 0.0 ); glRotatef( alpha, 0.0, 0.0, 1.0 ); // drawModel( model ); displayGLCache( glCache ); glfwSwapBuffers( window ); glfwPollEvents(); } // Cleanup OpenGL resources before destroying window cleanup_opengl_resources(); glfwDestroyWindow( window ); glfwTerminate(); return 0; } static void InitApp( int agrc, const char** argv ) { try { std::cout << "CLI test application run with " << agrc - 1 << " arguments" << std::endl; for ( int i = 1; i < agrc; i++ ) std::cout << "[" << i << "] = " << argv[i] << std::endl; } catch ( std::exception const& e ) { std::cerr << "Kernel activation throw exception: " << e.what() << std::endl; exit( 1 ); } catch ( ... ) { std::cerr << "Kernel activation throw unknown exception." << std::endl; exit( 2 ); } DefaultGL1RenderContext.glLineWidthValue = 24.0; DefaultGL1RenderContext.glIsolineWidthValue = 10.; DefaultGL1RenderContext.glDefaultFaceColourValue = { 0.6, 0.6, 0. }; DefaultGL1RenderContext.glDefaultEdgeColourValue = { 0., 0.6, 0. }; DefaultGL1RenderContext.glDefaultIsolineColourValue = { 0.6, 0.6, 0.6 }; } static bool FillGLCache( std::shared_ptr< IShapeBuilder > const& shape, GL1RawCache& glCache, gp_Trsf const& toGlobal, AttributeType attributeType = AttributeType::None ) { if ( shape == nullptr ) return false; // Выполняем построение фигуры shape->Execute(); std::cerr << shape->ExecutionMessage() << std::endl; // Получаем фабрику OpenGL сеток IOpenGLMeshFactory& meshFactory = Factory::OpenGLMesh(); // Создаём временный кэш для данных без трансформации GL1RawCache tempCache; // Генерируем треугольники const bool hasTriangles{ meshFactory.Create( shape, IOpenGLMeshFactory::GridRenderStyle::triangles, tempCache, attributeType ) }; const bool hasIsolines{ meshFactory.Create( shape, IOpenGLMeshFactory::GridRenderStyle::isolines, tempCache ) }; if ( !( hasTriangles || hasIsolines ) ) { return false; } // Генерируем рёбра (edgePolygons) meshFactory.Create( shape, IOpenGLMeshFactory::GridRenderStyle::edgePolygons, tempCache ); // Применяем трансформацию toGlobal к полученным данным // Трансформируем grids (треугольники) for ( auto& grid : tempCache.grids ) { // Трансформируем треугольники for ( auto& triangle : grid.triangles ) { for ( int ipp = 0; ipp < 3; ++ipp ) { // Трансформируем точку gp_Pnt pt( triangle[ipp].xyz[0], triangle[ipp].xyz[1], triangle[ipp].xyz[2] ); pt.Transform( toGlobal ); triangle[ipp].xyz[0] = static_cast< float >( pt.X() ); triangle[ipp].xyz[1] = static_cast< float >( pt.Y() ); triangle[ipp].xyz[2] = static_cast< float >( pt.Z() ); // Трансформируем нормаль (только поворот, без переноса) gp_Vec curNormal( triangle[ipp].normal[0], triangle[ipp].normal[1], triangle[ipp].normal[2] ); curNormal.Transform( toGlobal ); triangle[ipp].normal[0] = static_cast< float >( curNormal.X() ); triangle[ipp].normal[1] = static_cast< float >( curNormal.Y() ); triangle[ipp].normal[2] = static_cast< float >( curNormal.Z() ); } } // Трансформируем изолинии грани for ( auto& isoline : grid.gridIsolines ) { for ( auto& point : isoline.points ) { gp_Pnt pt( point.xyz[0], point.xyz[1], point.xyz[2] ); pt.Transform( toGlobal ); point.xyz[0] = static_cast< float >( pt.X() ); point.xyz[1] = static_cast< float >( pt.Y() ); point.xyz[2] = static_cast< float >( pt.Z() ); } } // Добавляем трансформированную сетку в результат glCache.grids.push_back( std::move( grid ) ); } // Трансформируем polygons (рёбра) for ( auto& polygon : tempCache.polygons ) { for ( auto& point : polygon.points ) { gp_Pnt pt( point.xyz[0], point.xyz[1], point.xyz[2] ); pt.Transform( toGlobal ); point.xyz[0] = static_cast< float >( pt.X() ); point.xyz[1] = static_cast< float >( pt.Y() ); point.xyz[2] = static_cast< float >( pt.Z() ); } // Добавляем трансформированный полигон в результат glCache.polygons.push_back( std::move( polygon ) ); } return true; } static bool FillGLCache( std::shared_ptr< IShapeBuilder > const& shape, GL1RawCache& glCache, AttributeType attributeType ) { return FillGLCache( shape, glCache, gp_Trsf{}, attributeType ); } static bool FillGLCache( std::shared_ptr< PartInstance > const& shape, GL1RawCache& glCache, gp_Trsf const& toGlobal, AttributeType attributeType = AttributeType::None ) { if ( shape == nullptr ) return false; std::shared_ptr< IPartInstancesIterator > refItr{}; try { refItr = shape->GetInstancesIterator(); } catch ( std::exception& ) { } while ( refItr != nullptr && refItr->HasNext() ) { auto curRef = refItr->PartReference().lock(); if ( curRef != nullptr ) { gp_Trsf toRef = toGlobal; toRef.Multiply( curRef->Location() ); auto subInstance = curRef->ReferencedInstance().lock(); if ( subInstance != nullptr ) { FillGLCache( subInstance, glCache, toRef, attributeType ); } } refItr->Next(); } std::shared_ptr< IPartShapesIterator > shapesItr{}; try { shapesItr = shape->GetShapesIterator(); } catch ( std::exception& ) { } while ( shapesItr != nullptr && shapesItr->HasNext() ) { auto curShape = shapesItr->Shape().lock(); if ( curShape != nullptr ) { FillGLCache( curShape, glCache, toGlobal, attributeType ); } shapesItr->Next(); } return false; } static bool FillGLCache( std::shared_ptr< PartInstance > const& shape, GL1RawCache& glCache, AttributeType attributeType ) { return FillGLCache( shape, glCache, gp_Trsf{}, attributeType ); } static void RenderGl1Polygon( GLuint VAO_temp, GLuint VBO_temp, GL1Polygon const& polygon, GL1RenderContext const& renderContext, RenderStyle renderStyle ) { std::vector< float > vertices; for ( auto const& point : polygon.points ) { vertices.push_back( point.xyz[0] ); vertices.push_back( point.xyz[1] ); vertices.push_back( point.xyz[2] ); } if ( vertices.size() < 6 ) return; ; // At least 2 points glUseProgram( shaderProgramDisplay ); glUniformMatrix4fv( glGetUniformLocation( shaderProgramDisplay, "model" ), 1, GL_FALSE, glm::value_ptr( model ) ); glUniformMatrix4fv( glGetUniformLocation( shaderProgramDisplay, "view" ), 1, GL_FALSE, glm::value_ptr( view ) ); glUniformMatrix4fv( glGetUniformLocation( shaderProgramDisplay, "projection" ), 1, GL_FALSE, glm::value_ptr( projection ) ); glBindVertexArray( VAO_temp ); glBindBuffer( GL_ARRAY_BUFFER, VBO_temp ); glBufferData( GL_ARRAY_BUFFER, vertices.size() * sizeof( float ), vertices.data(), GL_DYNAMIC_DRAW ); glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof( float ), (void*)0 ); glEnableVertexAttribArray( 0 ); glm::vec3 color; if ( polygon.colour.has_value() ) { auto const& colorVal = polygon.colour.value(); color = glm::vec3( colorVal[0], colorVal[1], colorVal[2] ); } else { const auto& lineColour = ( renderStyle == RenderStyle::isolines ) ? renderContext.glDefaultIsolineColourValue : renderContext.glDefaultEdgeColourValue; color = glm::vec3( lineColour[0], lineColour[1], lineColour[2] ); } glUniform3fv( glGetUniformLocation( shaderProgramDisplay, "color" ), 1, glm::value_ptr( color ) ); glLineWidth( polygon.lineWidth.value_or( ( renderStyle == RenderStyle::isolines ) ? renderContext.glIsolineWidthValue : renderContext.glLineWidthValue ) ); glDrawArrays( GL_LINE_STRIP, 0, vertices.size() / 3 ); } static void displayGLCache( GL1RawCache const& mesh, GL1RenderContext const& renderContext ) { // Note: VAO and VBO are created and destroyed each frame. // For better performance, consider creating them once and reusing. GLuint VAO_temp, VBO_temp; glGenVertexArrays( 1, &VAO_temp ); glGenBuffers( 1, &VBO_temp ); // Вычисляем глобальные min/max для атрибутов по всему телу float globalMinAttribute = FLT_MAX; float globalMaxAttribute = -FLT_MAX; bool hasAnyAttributes = false; if ( appRenderStyle == RenderStyle::attributeVisualization ) { for ( auto const& grid : mesh.grids ) { if ( grid.attributeInfo.isValid && grid.attributeInfo.type != AttributeType::None ) { globalMinAttribute = std::min( globalMinAttribute, grid.attributeInfo.minValue ); globalMaxAttribute = std::max( globalMaxAttribute, grid.attributeInfo.maxValue ); hasAnyAttributes = true; } } // Если нет атрибутов, используем дефолтные значения if ( !hasAnyAttributes ) { globalMinAttribute = -1.0f; globalMaxAttribute = 1.0f; } } // Draw grids (triangles) for ( auto const& grid : mesh.grids ) { if ( appRenderStyle == RenderStyle::attributeVisualization ) { // Визуализация атрибутов (кривизна) // Отрисовываем ВСЕ грани, даже без атрибутов std::vector< float > vertices; for ( auto const& triangle : grid.triangles ) { for ( auto const& point : triangle ) { vertices.push_back( point.xyz[0] ); vertices.push_back( point.xyz[1] ); vertices.push_back( point.xyz[2] ); // Используем среднее значение для вершин без атрибутов float attrValue = point.attribute.value_or( ( globalMinAttribute + globalMaxAttribute ) * 0.5f ); vertices.push_back( attrValue ); } } if ( !vertices.empty() ) { glUseProgram( shaderProgramAttribute ); glUniformMatrix4fv( glGetUniformLocation( shaderProgramAttribute, "model" ), 1, GL_FALSE, glm::value_ptr( model ) ); glUniformMatrix4fv( glGetUniformLocation( shaderProgramAttribute, "view" ), 1, GL_FALSE, glm::value_ptr( view ) ); glUniformMatrix4fv( glGetUniformLocation( shaderProgramAttribute, "projection" ), 1, GL_FALSE, glm::value_ptr( projection ) ); // Выбираем диапазон отображения: настраиваемый или глобальный float minAttr = useCustomRange ? displayMinAttribute : globalMinAttribute; float maxAttr = useCustomRange ? displayMaxAttribute : globalMaxAttribute; glUniform1f( glGetUniformLocation( shaderProgramAttribute, "minAttribute" ), minAttr ); glUniform1f( glGetUniformLocation( shaderProgramAttribute, "maxAttribute" ), maxAttr ); // Привязываем 1D текстуру color map glActiveTexture( GL_TEXTURE0 ); glBindTexture( GL_TEXTURE_1D, colorMapTexture ); glUniform1i( glGetUniformLocation( shaderProgramAttribute, "colorMap" ), 0 ); glBindVertexArray( VAO_temp ); glBindBuffer( GL_ARRAY_BUFFER, VBO_temp ); glBufferData( GL_ARRAY_BUFFER, vertices.size() * sizeof( float ), vertices.data(), GL_STATIC_DRAW ); // Position attribute glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE, 4 * sizeof( float ), (void*)0 ); glEnableVertexAttribArray( 0 ); // Attribute value glVertexAttribPointer( 1, 1, GL_FLOAT, GL_FALSE, 4 * sizeof( float ), (void*)( 3 * sizeof( float ) ) ); glEnableVertexAttribArray( 1 ); glDrawArrays( GL_TRIANGLES, 0, vertices.size() / 4 ); } } else if ( ( appRenderStyle == RenderStyle::smoothnessShader ) || ( appRenderStyle == RenderStyle::colouredShader ) ) { std::vector< float > vertices; for ( auto const& triangle : grid.triangles ) { for ( auto const& point : triangle ) { vertices.push_back( point.xyz[0] ); vertices.push_back( point.xyz[1] ); vertices.push_back( point.xyz[2] ); if ( appRenderStyle == RenderStyle::smoothnessShader ) { vertices.push_back( point.normal[0] ); vertices.push_back( point.normal[1] ); vertices.push_back( point.normal[2] ); } } } if ( !vertices.empty() ) { if ( appRenderStyle == RenderStyle::smoothnessShader ) { glUseProgram( shaderProgramSmoothness ); glUniformMatrix4fv( glGetUniformLocation( shaderProgramSmoothness, "model" ), 1, GL_FALSE, glm::value_ptr( model ) ); glUniformMatrix4fv( glGetUniformLocation( shaderProgramSmoothness, "view" ), 1, GL_FALSE, glm::value_ptr( view ) ); glUniformMatrix4fv( glGetUniformLocation( shaderProgramSmoothness, "projection" ), 1, GL_FALSE, glm::value_ptr( projection ) ); glBindVertexArray( VAO_temp ); glBindBuffer( GL_ARRAY_BUFFER, VBO_temp ); glBufferData( GL_ARRAY_BUFFER, vertices.size() * sizeof( float ), vertices.data(), GL_STATIC_DRAW ); glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof( float ), (void*)0 ); glEnableVertexAttribArray( 0 ); glVertexAttribPointer( 1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof( float ), (void*)( 3 * sizeof( float ) ) ); glEnableVertexAttribArray( 1 ); glDrawArrays( GL_TRIANGLES, 0, vertices.size() / 6 ); } else { glUseProgram( shaderProgramDisplay ); glUniformMatrix4fv( glGetUniformLocation( shaderProgramDisplay, "model" ), 1, GL_FALSE, glm::value_ptr( model ) ); glUniformMatrix4fv( glGetUniformLocation( shaderProgramDisplay, "view" ), 1, GL_FALSE, glm::value_ptr( view ) ); glUniformMatrix4fv( glGetUniformLocation( shaderProgramDisplay, "projection" ), 1, GL_FALSE, glm::value_ptr( projection ) ); glBindVertexArray( VAO_temp ); glBindBuffer( GL_ARRAY_BUFFER, VBO_temp ); glBufferData( GL_ARRAY_BUFFER, vertices.size() * sizeof( float ), vertices.data(), GL_STATIC_DRAW ); glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof( float ), (void*)0 ); glEnableVertexAttribArray( 0 ); glm::vec3 color; if ( grid.colour.has_value() ) { auto const& colorVal = grid.colour.value(); color = glm::vec3( colorVal[0], colorVal[1], colorVal[2] ); } else { color = glm::vec3( renderContext.glDefaultFaceColourValue[0], renderContext.glDefaultFaceColourValue[1], renderContext.glDefaultFaceColourValue[2] ); } glUniform3fv( glGetUniformLocation( shaderProgramDisplay, "color" ), 1, glm::value_ptr( color ) ); glDrawArrays( GL_TRIANGLES, 0, vertices.size() / 3 ); } } } if ( appRenderStyle == RenderStyle::isolines ) { for ( auto const& curIsoline : grid.gridIsolines ) { RenderGl1Polygon( VAO_temp, VBO_temp, curIsoline, renderContext, appRenderStyle ); } } } // Draw polygons (lines) if ( appRenderStyle == RenderStyle::colouredShader ) { for ( auto const& polygon : mesh.polygons ) { RenderGl1Polygon( VAO_temp, VBO_temp, polygon, renderContext, appRenderStyle ); } } glBindVertexArray( 0 ); glDeleteBuffers( 1, &VBO_temp ); glDeleteVertexArrays( 1, &VAO_temp ); glUseProgram( 0 ); } static bool FillGLCache( std::unique_ptr< Document > const& doc, GL1RawCache& glCache, AttributeType attributeType ) { if ( doc == nullptr ) return false; auto rootPart = doc->GetRootPart().lock(); return ::FillGLCache( rootPart, glCache, attributeType ); } //======================================================================== // Handle key strokes //======================================================================== void key_callback( GLFWwindow* window, int key, int scancode, int action, int mods ) { if ( action != GLFW_PRESS ) return; switch ( key ) { case GLFW_KEY_ESCAPE: glfwSetWindowShouldClose( window, GLFW_TRUE ); break; case GLFW_KEY_SPACE: // init_grid(); break; case GLFW_KEY_LEFT: alpha += 5; break; case GLFW_KEY_RIGHT: alpha -= 5; break; case GLFW_KEY_UP: beta -= 5; break; case GLFW_KEY_DOWN: beta += 5; break; case GLFW_KEY_PAGE_UP: zoom -= 0.25f; if ( zoom < 0.f ) zoom = 0.f; break; case GLFW_KEY_PAGE_DOWN: zoom += 0.25f; break; case GLFW_KEY_T: // Переключение режимов отображения и атрибутов if ( appRenderStyle == RenderStyle::colouredShader ) { appRenderStyle = RenderStyle::smoothnessShader; } else if ( appRenderStyle == RenderStyle::smoothnessShader ) { appRenderStyle = RenderStyle::isolines; } else if ( appRenderStyle == RenderStyle::isolines ) { // Переключаемся на режим отображения кривизны if ( currentAttributeType == AttributeType::None ) { currentAttributeType = AttributeType::GaussCurvature; appRenderStyle = RenderStyle::attributeVisualization; std::cout << "Switching to Gauss Curvature visualization mode" << std::endl; ReloadCache(); } else { appRenderStyle = RenderStyle::colouredShader; } } else if ( appRenderStyle == RenderStyle::attributeVisualization ) { // Возвращаемся к обычному режиму currentAttributeType = AttributeType::None; appRenderStyle = RenderStyle::colouredShader; std::cout << "Switching to normal visualization mode" << std::endl; ReloadCache(); } break; case GLFW_KEY_O: useOrthographicProjection = !useOrthographicProjection; { int width, height; glfwGetFramebufferSize( window, &width, &height ); framebuffer_size_callback( window, width, height ); } break; case GLFW_KEY_R: // Сброс настраиваемого диапазона if ( appRenderStyle == RenderStyle::attributeVisualization ) { useCustomRange = false; std::cout << "Reset to full range" << std::endl; } break; case GLFW_KEY_LEFT_BRACKET: // [ // Уменьшить нижнюю границу диапазона if ( appRenderStyle == RenderStyle::attributeVisualization ) { if ( !useCustomRange ) { // Первое нажатие - инициализируем настраиваемый диапазон useCustomRange = true; // Вычислим глобальные min/max из кэша float globalMin = FLT_MAX, globalMax = -FLT_MAX; for ( auto const& grid : glCache.grids ) { if ( grid.attributeInfo.isValid && grid.attributeInfo.type != AttributeType::None ) { globalMin = std::min( globalMin, grid.attributeInfo.minValue ); globalMax = std::max( globalMax, grid.attributeInfo.maxValue ); } } displayMinAttribute = globalMin; displayMaxAttribute = globalMax; } float range = displayMaxAttribute - displayMinAttribute; displayMinAttribute -= range * 0.1f; std::cout << "Display range: [" << displayMinAttribute << ", " << displayMaxAttribute << "]" << std::endl; } break; case GLFW_KEY_RIGHT_BRACKET: // ] // Увеличить нижнюю границу диапазона if ( appRenderStyle == RenderStyle::attributeVisualization ) { if ( !useCustomRange ) { useCustomRange = true; float globalMin = FLT_MAX, globalMax = -FLT_MAX; for ( auto const& grid : glCache.grids ) { if ( grid.attributeInfo.isValid && grid.attributeInfo.type != AttributeType::None ) { globalMin = std::min( globalMin, grid.attributeInfo.minValue ); globalMax = std::max( globalMax, grid.attributeInfo.maxValue ); } } displayMinAttribute = globalMin; displayMaxAttribute = globalMax; } float range = displayMaxAttribute - displayMinAttribute; displayMinAttribute += range * 0.1f; if ( displayMinAttribute >= displayMaxAttribute ) displayMinAttribute = displayMaxAttribute - 0.001f; std::cout << "Display range: [" << displayMinAttribute << ", " << displayMaxAttribute << "]" << std::endl; } break; case GLFW_KEY_SEMICOLON: // ; (shift+; = :) // Уменьшить верхнюю границу диапазона if ( appRenderStyle == RenderStyle::attributeVisualization ) { if ( !useCustomRange ) { useCustomRange = true; float globalMin = FLT_MAX, globalMax = -FLT_MAX; for ( auto const& grid : glCache.grids ) { if ( grid.attributeInfo.isValid && grid.attributeInfo.type != AttributeType::None ) { globalMin = std::min( globalMin, grid.attributeInfo.minValue ); globalMax = std::max( globalMax, grid.attributeInfo.maxValue ); } } displayMinAttribute = globalMin; displayMaxAttribute = globalMax; } float range = displayMaxAttribute - displayMinAttribute; displayMaxAttribute -= range * 0.1f; if ( displayMaxAttribute <= displayMinAttribute ) displayMaxAttribute = displayMinAttribute + 0.001f; std::cout << "Display range: [" << displayMinAttribute << ", " << displayMaxAttribute << "]" << std::endl; } break; case GLFW_KEY_APOSTROPHE: // ' // Увеличить верхнюю границу диапазона if ( appRenderStyle == RenderStyle::attributeVisualization ) { if ( !useCustomRange ) { useCustomRange = true; float globalMin = FLT_MAX, globalMax = -FLT_MAX; for ( auto const& grid : glCache.grids ) { if ( grid.attributeInfo.isValid && grid.attributeInfo.type != AttributeType::None ) { globalMin = std::min( globalMin, grid.attributeInfo.minValue ); globalMax = std::max( globalMax, grid.attributeInfo.maxValue ); } } displayMinAttribute = globalMin; displayMaxAttribute = globalMax; } float range = displayMaxAttribute - displayMinAttribute; displayMaxAttribute += range * 0.1f; std::cout << "Display range: [" << displayMinAttribute << ", " << displayMaxAttribute << "]" << std::endl; } break; default: break; } } //======================================================================== // Callback function for mouse button events //======================================================================== void mouse_button_callback( GLFWwindow* window, int button, int action, int mods ) { if ( button != GLFW_MOUSE_BUTTON_LEFT ) return; if ( action == GLFW_PRESS ) { glfwSetInputMode( window, GLFW_CURSOR, GLFW_CURSOR_DISABLED ); glfwGetCursorPos( window, &cursorX, &cursorY ); } else glfwSetInputMode( window, GLFW_CURSOR, GLFW_CURSOR_NORMAL ); } //======================================================================== // Callback function for cursor motion events //======================================================================== void cursor_position_callback( GLFWwindow* window, double x, double y ) { if ( glfwGetInputMode( window, GLFW_CURSOR ) == GLFW_CURSOR_DISABLED ) { alpha += static_cast< GLfloat >( x - cursorX ) / 10.f; beta += static_cast< GLfloat >( y - cursorY ) / 10.f; cursorX = x; cursorY = y; } } //======================================================================== // Callback function for scroll events //======================================================================== void scroll_callback( GLFWwindow* window, double x, double y ) { zoom += static_cast< float >( y ) / 4.f; if ( zoom < 0 ) zoom = 0; } //======================================================================== // Callback function for framebuffer resize events //======================================================================== static void framebuffer_size_callback( GLFWwindow* window, int width, int height ) { float ratio = 1.f; if ( height > 0 ) ratio = static_cast< float >( width ) / static_cast< float >( height ); // Setup viewport glViewport( 0, 0, width, height ); // Update projection matrix if ( useOrthographicProjection ) { // Ортогональная проекция: используем размер зависящий от zoom float orthoSize = zoom * 0.5f; projection = glm::ortho( -orthoSize * ratio, orthoSize * ratio, -orthoSize, orthoSize, 0.001f, 10000.0f ); } else { // Перспективная проекция с очень близкой near plane для предотвращения отсечения projection = glm::perspective( glm::radians( 60.0f ), ratio, 0.001f, 10000.0f ); } // For compatibility with old code, still set fixed pipeline if needed glMatrixMode( GL_PROJECTION ); glLoadMatrixf( glm::value_ptr( projection ) ); } //======================================================================== // Initialize Miscellaneous OpenGL state //======================================================================== static void init_opengl( void ) { // Use Gouraud (smooth) shading glShadeModel( GL_SMOOTH ); // Switch on the z-buffer glEnable( GL_DEPTH_TEST ); // Enable face culling - render only front faces glEnable( GL_CULL_FACE ); glCullFace( GL_BACK ); // Cull back-facing triangles glFrontFace( GL_CCW ); // Counter-clockwise winding order for front faces // glEnableClientState(GL_VERTEX_ARRAY); // glEnableClientState(GL_COLOR_ARRAY); // glVertexPointer(3, GL_FLOAT, sizeof(struct Vertex), vertex); // glColorPointer(3, GL_FLOAT, sizeof(struct Vertex), &vertex[0].r); // Pointer to the first color glPointSize( 2.0 ); // Background color is black // glClearColor(0, 0, 0, 0); model = glm::mat4( 1.0f ); init_display_shaders(); } static void InitWindowUIcallbacks( GLFWwindow* window ) { glfwSetKeyCallback( window, key_callback ); glfwSetFramebufferSizeCallback( window, framebuffer_size_callback ); glfwSetMouseButtonCallback( window, mouse_button_callback ); glfwSetCursorPosCallback( window, cursor_position_callback ); glfwSetScrollCallback( window, scroll_callback ); glfwMakeContextCurrent( window ); }