274 lines
9.7 KiB
C++
274 lines
9.7 KiB
C++
/***********************************************************************************************************************
|
|
*
|
|
* 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 <A3DSDKIncludes.h>
|
|
#include <hoops_license.h>
|
|
|
|
#include "../common.hpp"
|
|
|
|
#include <iostream>
|
|
#include <map>
|
|
|
|
#include <glad/glad.h>
|
|
#include <glm/glm.hpp>
|
|
#include <glm/gtc/matrix_transform.hpp>
|
|
#include <glm/gtc/type_ptr.hpp>
|
|
|
|
#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<std::string, std::string> 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<he::structure::Part> model_leaves;
|
|
model::exchange::collect_leaves(model_file, model_leaves);
|
|
|
|
// transfer mesh data
|
|
std::vector<Mesh> meshs;
|
|
std::vector<Mesh> 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<A3DRiRepresentationItem*> 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<utils::geometry::BufferData> 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<float, Mesh> 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;
|
|
}
|