/*********************************************************************************************************************** * * Copyright (c) 2010 - 2025 by Tech Soft 3D, Inc. * The information contained herein is confidential and proprietary to Tech Soft 3D, Inc., and considered a trade secret * as defined under civil and criminal statutes. Tech Soft 3D shall pursue its civil and criminal remedies in the event * of unauthorized use or misappropriation of its trade secrets. Use of this information by anyone other than authorized * employees of Tech Soft 3D, Inc. is granted only under a written non-disclosure agreement, expressly prescribing the * scope and manner of such use. * ***********************************************************************************************************************/ #define INITIALIZE_A3D_API #include #include #include "../common.hpp" #include #include #include #include #include #include #include "application/application.h" #include "application/camera.h" #include "application/render/mesh.h" #include "application/render/shader.h" #include "application/render/vao.h" #include "model/exchange.h" #include "utils/properties.h" #include "utils/bounding_box.h" #include "utils/geometry.h" static auto const exchange_bin_dir = _T(HOOPS_BINARY_DIRECTORY); /*! Check if the file is valid. * \param [in] file_path The path to the model file. * \return true if the file is valid, else false. */ bool is_file_valid(const std::string& file_path, bool read = true) { if (FILE* file = fopen(file_path.c_str(), read ? "r" : "w")) { fclose(file); return true; } else { return false; } } /*! Manage the input command line arguments. * If no model file provided, open the default one (_micro engine.prc). * If `--dump_model_tree` option used, dump the model tree. * Finally create the resulting tree dump in the given output file. * \param argc The number of arguments. * \param argv The argument values. * \return the file to open and the xml file (tree dump) to create. */ std::pair manage_input_arguments(int argc, char** argv) { std::string filename = std::string(SAMPLES_DATA_DIRECTORY) + "/prc/_micro engine.prc"; std::string xmlfile; if (argc < 2) { std::cout << "Using file: " << filename << std::endl; } else { bool dump_tree = false; for (auto i = 1; i < argc; ++i) { if(strcmp(argv[i], "--dump_model_tree") == 0) { dump_tree = true; continue; } if (!is_file_valid(argv[i], !dump_tree)) { std::cout << "Invalid file provided. "; dump_tree = false; } else { if (dump_tree) { xmlfile = argv[i]; dump_tree = false; } else { filename = argv[i]; std::cout << "Valid file provided. "; } } } } std::cout << "Using file: " << filename << std::endl; return { filename, xmlfile }; } int main(int argc, char** argv) { //-------------------------------------------------------------------------- // Command line arguments //-------------------------------------------------------------------------- // CAD file can be configured as a parameter, // if none is provided or the one provided does not exist, // we go back to the Micro Engine PRC file available in the package. // `--dump_model_tree` option and the output xml file are optional if (argc > 4) { std::cout << "Usage:" << std::endl << argv[0] << " [input CAD file] --dump_model_tree [output XML file]" << std::endl; return -1; } auto inputs = manage_input_arguments(argc, argv); std::string input_file = inputs.first; std::string model_tree_file = inputs.second; //-------------------------------------------------------------------------- // Initialize HOOPS EXCHANGE //-------------------------------------------------------------------------- A3DSDKHOOPSExchangeLoader loader(exchange_bin_dir, HOOPS_LICENSE); CHECK_RET(loader.m_eSDKStatus); //-------------------------------------------------------------------------- // Initialize OpenGL window //-------------------------------------------------------------------------- Application application; std::string displayTitle = "TS3D Mesh Viewer : "; displayTitle.append(input_file); application.launch(utils::properties::screen_width, utils::properties::screen_height, displayTitle); application.display(); //-------------------------------------------------------------------------- // Load the input file //-------------------------------------------------------------------------- A3DAsmModelFile* model_file = nullptr; auto const load_status = model::exchange::load_model(input_file.c_str(), model_file); if (A3D_SUCCESS != load_status && A3D_LOAD_MISSING_COMPONENTS != load_status) { std::cout << "input_file = " << input_file << std::endl; std::cout << "ERROR::MODELFILE::FILE_NOT_SUCCESSFULLY_READ: " << load_status << std::endl; return -1; } std::cout << input_file << " loaded \n"; //-------------------------------------------------------------------------- // Dump Tree if asked //-------------------------------------------------------------------------- if (!model_tree_file.empty()) model::exchange::tree::print(model_file, input_file, model_tree_file); //-------------------------------------------------------------------------- // Retrieve meshes from the model //-------------------------------------------------------------------------- // Collect the leaves of the model: the parts with their associated graphical contexts (A3DMiscCascadedAttributes) and transformations std::vector model_leaves; model::exchange::collect_leaves(model_file, model_leaves); // transfer mesh data std::vector meshs; std::vector transparent_meshs; utils::geometry::BoundingBox bounding_box; for (const auto& leaf : model_leaves) { // For each leaf, retrieve the representation items that contain the geometry (brep and polybrep only) std::vector ri_list; leaf.ri_list(ri_list); for (const auto& context : leaf.get_contexts()) { // Process the different contexts of the leaf (same geometry, with different graphic style and transformation) const auto& graphical_context_data = std::get<1>(context); if (graphical_context_data.m_bShow == A3D_FALSE || graphical_context_data.m_bRemoved == A3D_TRUE) continue; const auto& graphical_context = std::get<0>(context); const auto& transformations = std::get<2>(context); for (const auto ri : ri_list) { // For each representation item containing the geometry in the leaf, // fill one buffer if the ri is unicolor, // or multiple buffers, in case it contains multiple graphic styles (color, material, texture). // These buffers contain the vertices, the indices, the bounding box and the graphic style (color, material, texture). std::vector buffer_data_array = model::exchange::geometry::prepare_render_buffer(ri, graphical_context); if (buffer_data_array.empty()) continue; for (const auto& buffer_data : buffer_data_array) { // Add the bounding box for the camera bounding_box.add_boxes(buffer_data.mesh_box, transformations); // Add the computed information in the list of meshs utils::properties::style::Graphics graphics = buffer_data.graphics; auto& container = graphics.has_transparency() ? transparent_meshs : meshs; for (const auto& placement : transformations) { container.emplace_back(buffer_data, placement, graphics); } } } } } if (meshs.empty()) { std::cout << "ERROR::MODELFILE::NO_MESH_FOUND: " << load_status << std::endl; return 1; } //-------------------------------------------------------------------------- // Sort meshes //-------------------------------------------------------------------------- Mesh::sort_by_style(meshs); //-------------------------------------------------------------------------- // Camera and shader setting //-------------------------------------------------------------------------- application.compute_camera(bounding_box); Shader shader_program("default.vert", "default.frag"); //-------------------------------------------------------------------------- // Drawing loop //-------------------------------------------------------------------------- bool first_style = true; while (!application.should_close()) { glm::vec3 translation = (application.camera.up + application.camera.right - application.camera.front) * (application.camera.max_bbx_length * 0.5f); glm::vec3 light_pos = application.camera.position + translation; shader_program.add_light_properties(light_pos); shader_program.update_projection_matrix(application.compute_projection_matrix()); shader_program.update_view_matrix(application.compute_view_matrix()); shader_program.set_vec3("view_position", application.camera.position); application.clear(); first_style = true; application.set_blending(false); for (auto& model : meshs) { shader_program.update_mesh_style(model.style, first_style); shader_program.update_model_matrix(model.placement); model.draw(); } if (transparent_meshs.size()) { std::map sorted; application.set_blending(true); for (const auto& model : transparent_meshs) { float d1 = glm::length(application.camera.position - model.box.min); float d2 = glm::length(application.camera.position - model.box.max); sorted[d1 < d2 ? d1 : d2] = model; } for (auto it = sorted.rbegin(); it != sorted.rend(); ++it) { shader_program.update_mesh_style(it->second.style, first_style); shader_program.update_model_matrix(it->second.placement); it->second.draw(); } } application.display(); } return 0; }