├── examples ├── ircad11 │ ├── README │ ├── ircad11.scene │ ├── santi-liver.scene │ ├── santi-morison1.scene │ └── santi-morison2.scene └── sphere │ ├── box.scene │ ├── simple.scene │ └── sphere.scene ├── src ├── mesh.h ├── OpenGL │ └── GLInstanceGraphicsShape.h ├── inputmanager.h ├── wavefront │ ├── tiny_obj_loader.h │ └── tiny_obj_loader.cpp ├── scene.h ├── volume.h ├── ray.h ├── psf.h ├── inputmanager.cpp ├── transducer.h ├── main.cpp ├── objloader.h ├── rfimage.h ├── ray.cpp └── scene.cpp ├── utils └── vtp_to_obj.py ├── README.md ├── .gitignore └── CMakeLists.txt /examples/ircad11/README: -------------------------------------------------------------------------------- 1 | This scene is taken from this IRCAD dataset (http://test.ircad.fr/research/3d-ircadb-01/), specifically the 11th patient. -------------------------------------------------------------------------------- /src/mesh.h: -------------------------------------------------------------------------------- 1 | #ifndef MESH_H 2 | #define MESH_H 3 | 4 | #include 5 | #include 6 | 7 | struct material 8 | { 9 | float impedance, attenuation, mu0, mu1, sigma, specularity, shininess, thickness; 10 | }; 11 | 12 | struct mesh 13 | { 14 | const std::string filename; 15 | const bool is_rigid, is_vascular; 16 | const std::array deltas; 17 | const bool outside_normals; 18 | const material & material_inside; 19 | const material & material_outside; 20 | }; 21 | 22 | #endif // MESH_H 23 | -------------------------------------------------------------------------------- /utils/vtp_to_obj.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import vtk 4 | import os 5 | 6 | reader = vtk.vtkXMLPolyDataReader() 7 | path = "C:/Simeco/simeco/test/data/ircad11_large/body/body_0_triangle.vtp" 8 | reader.SetFileName(path) 9 | reader.Update() 10 | 11 | polydata = reader.GetOutput() 12 | #print(polydata) 13 | 14 | cell_array = polydata.GetPolys() 15 | idList = vtk.vtkIdList() 16 | 17 | while (cell_array.GetNextCell(idList)): 18 | for cell_id in range(idList.GetNumberOfIds()): 19 | point_id = idList.GetId(cell_id) 20 | point = polydata.GetPoint(point_id) 21 | print point[0], point[1], point[2], 22 | print 23 | 24 | 25 | #interactor.Start() -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MCRay-Tracing 2 | Monte-Carlo Ray-Tracing for Interactive Ultrasound Simulation 3 | 4 | Implementation of [Monte-Carlo Ray-Tracing for Realistic Interactive Ultrasound Simulation](https://www.researchgate.net/publication/308012126_Monte-Carlo_Ray-Tracing_for_Realistic_Interactive_Ultrasound_Simulation) in C++ (in CPU). 5 | 6 | --- 7 | 8 | ## Prerequisites 9 | - C++14 compiler (tested using MinGW-w64) 10 | - CMake 3.2 11 | 12 | ## To run 13 | 14 | ## Third Party Libraries 15 | - openCV 16 | - [Bulletphysics](https://github.com/bulletphysics/bullet3) (not included) 17 | - [nholthaus/units](https://github.com/nholthaus/units) (header-only, included) 18 | - [nlohmann/json](https://github.com/nlohmann/json) (header-only, included) 19 | -------------------------------------------------------------------------------- /src/OpenGL/GLInstanceGraphicsShape.h: -------------------------------------------------------------------------------- 1 | #ifndef GL_INSTANCE_GRAPHICS_SHAPE_H 2 | #define GL_INSTANCE_GRAPHICS_SHAPE_H 3 | 4 | #include "Bullet3Common/b3AlignedObjectArray.h" 5 | 6 | struct GLInstanceVertex 7 | { 8 | float xyzw[4]; 9 | float normal[3]; 10 | float uv[2]; 11 | }; 12 | 13 | struct GLInstanceGraphicsShape 14 | { 15 | b3AlignedObjectArray* m_vertices; 16 | int m_numvertices; 17 | b3AlignedObjectArray* m_indices; 18 | int m_numIndices; 19 | float m_scaling[4]; 20 | 21 | GLInstanceGraphicsShape() : 22 | m_vertices(0), 23 | m_indices(0) 24 | { 25 | } 26 | 27 | virtual ~GLInstanceGraphicsShape() 28 | { 29 | delete m_vertices; 30 | delete m_indices; 31 | } 32 | }; 33 | 34 | #endif //GL_INSTANCE_GRAPHICS_SHAPE_H 35 | 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # CMake 2 | CMakeCache.txt 3 | CMakeFiles 4 | CMakeScripts 5 | Makefile 6 | cmake_install.cmake 7 | install_manifest.txt 8 | CTestTestfile.cmake 9 | 10 | # QtCreator 11 | *.autosave 12 | 13 | # QtCtreator CMake 14 | CMakeLists.txt.user* 15 | 16 | # C++ 17 | # Compiled Object files 18 | *.slo 19 | *.lo 20 | *.o 21 | *.obj 22 | # Precompiled Headers 23 | *.gch 24 | *.pch 25 | # Compiled Dynamic libraries 26 | *.so 27 | *.dylib 28 | *.dll 29 | # Compiled Static libraries 30 | *.lai 31 | *.la 32 | *.a 33 | *.lib 34 | # Executables 35 | *.exe 36 | *.out 37 | *.app 38 | 39 | #Vim 40 | .vim.custom 41 | .ycm_extra_conf.* 42 | *.swp 43 | *.swo 44 | 45 | # Default build directory 46 | /build 47 | 48 | #data directory 49 | /examples/icard11 50 | /images 51 | 52 | #include directory 53 | /include/bullet3 54 | -------------------------------------------------------------------------------- /src/inputmanager.h: -------------------------------------------------------------------------------- 1 | #ifndef INPUTMANAGER_H 2 | #define INPUTMANAGER_H 3 | 4 | #include 5 | #include "transducer.h" 6 | 7 | namespace sf 8 | { 9 | class RenderWindow; 10 | } 11 | 12 | class inputManager 13 | { 14 | using transducer_ = transducer<512>; 15 | 16 | public: 17 | inputManager(); 18 | //~inputmanager(); 19 | void setTransducer(transducer_ * transducer); 20 | void update(float elapsedTimeInMillis); 21 | void updateKeyboard(float elapsedTimeInMillis); 22 | void handleKeyboardMovement(float elapsedTimeInMillis); 23 | 24 | private: 25 | transducer_ * transducer; 26 | float translationSpeedX, translationSpeedY, translationSpeedZ; 27 | bool inputEnabled = true, isQuitPressed = false; 28 | std::vector windows; 29 | 30 | 31 | }; 32 | #endif // INPUTMANAGER_H 33 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(mattausch) 2 | 3 | cmake_minimum_required(VERSION 3.2.0) 4 | 5 | # C++14 support 6 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fopenmp -std=c++14 -fpermissive") 7 | 8 | # Sources 9 | set (mattausch_SRC 10 | "src/main.cpp" 11 | "src/mesh.h" 12 | "src/scene.cpp" 13 | "src/objloader.h" 14 | "src/ray.cpp" 15 | "src/rfimage.h" 16 | "src/psf.h" 17 | "src/transducer.h" 18 | "src/volume.h" 19 | "src/wavefront/tiny_obj_loader.cpp") 20 | 21 | add_executable(mattausch ${mattausch_SRC}) 22 | 23 | # Bullet includes 24 | set(BULLET_SRC_DIR "" CACHE PATH "") 25 | set(BULLET_LIB_DIR "" CACHE PATH "") 26 | message("Bullet source directory: ${BULLET_SRC_DIR}") 27 | message("Bullet lib directory: ${BULLET_LIB_DIR}") 28 | include_directories(${BULLET_SRC_DIR}) 29 | 30 | target_link_libraries(mattausch 31 | ${BULLET_LIB_DIR}/libBullet3Common.a 32 | ${BULLET_LIB_DIR}/libBulletDynamics.a 33 | ${BULLET_LIB_DIR}/libBulletCollision.a 34 | ${BULLET_LIB_DIR}/libLinearMath.a 35 | ) 36 | 37 | # OpenCV 38 | find_package(OpenCV REQUIRED) 39 | if (OpenCV_FOUND) 40 | target_link_libraries(mattausch ${OpenCV_LIBS}) 41 | endif() 42 | 43 | # Include other stuff, like Units 44 | include_directories("${CMAKE_SOURCE_DIR}/include") 45 | -------------------------------------------------------------------------------- /src/wavefront/tiny_obj_loader.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2012-2013, Syoyo Fujita. 3 | // 4 | // Licensed under 2-clause BSD liecense. 5 | // 6 | #ifndef _TINY_OBJ_LOADER_H 7 | #define _TINY_OBJ_LOADER_H 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | namespace tinyobj { 14 | 15 | typedef struct 16 | { 17 | std::string name; 18 | 19 | float ambient[3]; 20 | float diffuse[3]; 21 | float specular[3]; 22 | float transmittance[3]; 23 | float emission[3]; 24 | float shininess; 25 | 26 | std::string ambient_texname; 27 | std::string diffuse_texname; 28 | std::string specular_texname; 29 | std::string normal_texname; 30 | std::map unknown_parameter; 31 | } material_t; 32 | 33 | typedef struct 34 | { 35 | std::vector positions; 36 | std::vector normals; 37 | std::vector texcoords; 38 | std::vector indices; 39 | } mesh_t; 40 | 41 | typedef struct 42 | { 43 | std::string name; 44 | material_t material; 45 | mesh_t mesh; 46 | } shape_t; 47 | 48 | /// Loads .obj from a file. 49 | /// 'shapes' will be filled with parsed shape data 50 | /// The function returns error string. 51 | /// Returns empty string when loading .obj success. 52 | /// 'mtl_basepath' is optional, and used for base path for .mtl file. 53 | std::string LoadObj( 54 | std::vector& shapes, // [output] 55 | const char* filename, 56 | const char* mtl_basepath = NULL); 57 | 58 | }; 59 | 60 | #endif // _TINY_OBJ_LOADER_H 61 | -------------------------------------------------------------------------------- /src/scene.h: -------------------------------------------------------------------------------- 1 | #ifndef SCENE_H 2 | #define SCENE_H 3 | 4 | #include "btBulletDynamicsCommon.h" 5 | 6 | #include "ray.h" 7 | #include "mesh.h" 8 | #include "transducer.h" 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | 19 | class scene 20 | { 21 | using transducer_ = transducer<512>; 22 | 23 | public: 24 | explicit scene(const nlohmann::json & config, transducer_ & transducer); 25 | ~scene(); 26 | 27 | void init(); 28 | 29 | template 30 | std::array,sample_count>, ray_count>cast_rays(transducer_ & transducer); 31 | 32 | void step(float delta_time); 33 | 34 | units::length::millimeter_t distance(const btVector3 & from, const btVector3 & to) const; 35 | 36 | //void setTransducer (transducer_ transducer); 37 | 38 | protected: 39 | std::string working_dir; 40 | 41 | std::unordered_map materials; 42 | std::string starting_material; 43 | 44 | std::vector meshes; 45 | 46 | transducer_ & transducer; 47 | 48 | const float intensity_epsilon { 1e-8 }; 49 | const float initial_intensity { 1.0f }; 50 | 51 | std::array spacing; 52 | std::array origin; 53 | float scaling; 54 | 55 | void parse_config(const nlohmann::json & config); 56 | 57 | void create_empty_world(); 58 | void destroy_world(); 59 | 60 | units::length::millimeter_t distance_in_mm(const btVector3 & v1, const btVector3 & v2) const; 61 | btVector3 enlarge(const btVector3 & versor, float mm) const; 62 | 63 | class btRigidBody * add_rigidbody_from_obj(const std::string & fileName, std::array deltas, float scaling); 64 | 65 | btAlignedObjectArray m_collisionShapes; 66 | std::unique_ptr m_broadphase; 67 | std::unique_ptr m_dispatcher; 68 | std::unique_ptr m_solver; 69 | std::unique_ptr m_collisionConfiguration; 70 | std::unique_ptr m_dynamicsWorld; 71 | 72 | btVector3 transducer_pos; 73 | std::array transducer_dir; 74 | 75 | clock_t frame_start; 76 | }; 77 | 78 | #endif // SCENE_H 79 | -------------------------------------------------------------------------------- /src/volume.h: -------------------------------------------------------------------------------- 1 | #ifndef VOLUME_H 2 | #define VOLUME_H 3 | 4 | #include 5 | #include 6 | 7 | 8 | /** 9 | * The volume class holds a cubic matrix with gaussian random values where 10 | * each voxel has a mm resolution. 11 | * 12 | * It is posible to query the volume for one of its values. The volume loops 13 | * over its internal matrix and returns the expected value. 14 | */ 15 | template 16 | class volume 17 | { 18 | public: 19 | volume() 20 | { 21 | std::default_random_engine generator; 22 | std::normal_distribution distribution(0.0,1.0); 23 | 24 | for (unsigned int i = 0; i < size; i++) 25 | { 26 | for (unsigned int j = 0; j < size; j++) 27 | { 28 | for (unsigned int k = 0; k < size; k++) 29 | { 30 | matrix[i][j][k].texture_noise = distribution(generator); 31 | matrix[i][j][k].scattering_probability = distribution(generator); 32 | } 33 | } 34 | } 35 | } 36 | 37 | constexpr float get_resolution_in_millis() const 38 | { 39 | return static_cast(resolution_micrometers)/1000.0f; 40 | } 41 | 42 | /** 43 | * Gets the scattering value for a tissue with given properties at a fixed point in space. 44 | * @sa Eq. 15 in Burger13 45 | */ 46 | float get_scattering(const float scattering_density, const float scattering_mu, const float scattering_sigma, 47 | const float x_millis, const float y_millis, const float z_millis) const 48 | { 49 | constexpr float resolution = resolution_micrometers / 1000.0f; // [mm] 50 | 51 | // TODO: Change static_cast to linear interpolation? 52 | const unsigned int x = static_cast(x_millis / resolution) % size; 53 | const unsigned int y = static_cast(y_millis / resolution) % size; 54 | const unsigned int z = static_cast(z_millis / resolution) % size; 55 | 56 | const auto & voxel = matrix[x][y][z]; 57 | 58 | return voxel.scattering_probability >= scattering_density ? 59 | voxel.texture_noise * scattering_sigma + scattering_mu : 60 | 0.0f; 61 | } 62 | 63 | private: 64 | struct voxel_values 65 | { 66 | float texture_noise; 67 | float scattering_probability; 68 | }; 69 | 70 | std::array, size>, size> matrix; 71 | }; 72 | 73 | #endif // VOLUME_H 74 | -------------------------------------------------------------------------------- /src/ray.h: -------------------------------------------------------------------------------- 1 | #ifndef RAY_H 2 | #define RAY_H 3 | 4 | #include 5 | 6 | #include 7 | #include "mesh.h" 8 | //class material; 9 | class mesh; 10 | 11 | namespace ray_physics { 12 | 13 | struct ray 14 | { 15 | btVector3 from, direction; 16 | size_t depth; 17 | material media; 18 | const material * media_outside = nullptr; // keep track of media outside of vascularities, so we can switch back to it when the ray gets out of them 19 | float intensity, frequency; 20 | units::length::millimeter_t distance_traveled; // [mm] 21 | unsigned short parent_collision; // position in collision vector 22 | 23 | static constexpr size_t max_depth = 10; 24 | static constexpr float intensity_epsilon = 1e-10; 25 | bool null = false; 26 | }; 27 | 28 | struct segment 29 | { 30 | btVector3 from, to, direction; 31 | float reflected_intensity; // reflected back to the transducer, at the end of the segment 32 | float initial_intensity, attenuation; 33 | 34 | units::length::millimeter_t distance_traveled; // traveled from the transducer to the beginning of the segment 35 | const material & media; 36 | }; 37 | 38 | struct collision 39 | { 40 | btVector3 position; 41 | unsigned short parent_collision; // position in collision vector 42 | }; 43 | 44 | struct hit_result { float reflected_intensity; ray returned; }; 45 | 46 | hit_result hit_boundary(const ray & r, const btVector3 &hit_point, const btVector3 & surface_normal, const mesh & collided_mesh); 47 | 48 | // Advance through homogeneous media and decrease intensity accordingly 49 | void travel(ray & r, units::length::millimeter_t mm); 50 | 51 | bool should_travel(const ray & r); 52 | 53 | float max_ray_length(const ray & r); 54 | 55 | btVector3 snells_law(const btVector3 & ray_direction, const btVector3 & surface_normal, float incidence_angle, float refraction_angle, float refr_ratio); 56 | 57 | /** 58 | * Intensity of the reflected ray. 59 | * IMPORTANT: This it NOT the intensity reflected back to the transducer. 60 | */ 61 | float reflection_intensity(const float intensity_in, const float media_1, const float incidence_angle, const float media_2, const float refracted_angle); 62 | 63 | // Intensity reflected back to the transducer when a ray passes through an interface. 64 | float reflected_intensity(const float ray_intensity, const float incidence_angle, const material & ray_media, const material & colliding_media); 65 | 66 | float reflected_intensity(const btVector3 direction, const btVector3 refraction_direction, const btVector3 reflection_direction, const material & colliding_media); 67 | 68 | btVector3 random_unit_vector(btVector3 v, float cos_theta); 69 | 70 | float power_cosine_variate(int v); 71 | 72 | } // end ray_physics 73 | 74 | #endif // RAY_H 75 | -------------------------------------------------------------------------------- /src/psf.h: -------------------------------------------------------------------------------- 1 | #ifndef PSF_H 2 | #define PSF_H 3 | 4 | #define _USE_MATH_DEFINES 5 | #include 6 | #include 7 | #include 8 | 9 | #define M_PI 3.14159 10 | 11 | /** 12 | * The PSF has values in a voxelized space. 13 | * 14 | * It is defined amongst a bounded range, since outside of it it's value is 0. 15 | * 16 | * It has three diferent ranges: axial, lateral and elevation. 17 | * The axial range varies according to frequency. 18 | * Lateral and elevation ranges vary according to distance to the transducer. 19 | * 20 | * The discretization is done around the center of the psf. Each voxel has a 21 | * constant resolution in mm. This means that the size of the matrix that holds 22 | * the discrete volume should be able to hold every value of the psf while the 23 | * psf' ranges are at a maximum. This also means that when the psf' ranges become 24 | * smaller (near the focus zone), there will be zeroed voxels. 25 | */ 26 | template 27 | class psf 28 | { 29 | static_assert(axial_size % 2, "axial_size must be an odd positive integer"); 30 | static_assert(lateral_size % 2, "lateral_size must be an odd positive integer"); 31 | static_assert(elevation_size % 2, "elevation_size must be an odd positive integer"); 32 | 33 | public: 34 | psf(const float freq, const float var_x, const float var_y, const float var_z) : 35 | freq(freq), 36 | var_x(var_x), 37 | var_y(var_y), 38 | var_z(var_z) 39 | { 40 | constexpr auto half_axial = axial_size * resolution_micrometers / 1000.0f / 2.0f; // [mm] 41 | constexpr auto half_lateral = lateral_size * resolution_micrometers / 1000.0f / 2.0f; // [mm] 42 | constexpr auto half_elevation = elevation_size * resolution_micrometers / 1000.0f / 2.0f; // [mm] 43 | constexpr auto resolution = resolution_micrometers / 1000.0f; // [mm] 44 | 45 | // Fill axial kernel 46 | for (size_t i = 0; i < axial_size; i++) 47 | { 48 | const float x = i * resolution - half_axial; // [mm] 49 | axial_kernel[i] = axial_function(x); 50 | } 51 | 52 | // Fill lateral kernel 53 | for (size_t i = 0; i < lateral_size; i++) 54 | { 55 | const float y = i * resolution - half_lateral; // [mm] 56 | lateral_kernel[i] = lateral_function(y); 57 | } 58 | } 59 | 60 | constexpr size_t get_axial_size() const 61 | { 62 | return axial_size; 63 | } 64 | 65 | constexpr size_t get_lateral_size() const 66 | { 67 | return lateral_size; 68 | } 69 | 70 | constexpr size_t get_elevation_size() const 71 | { 72 | return elevation_size; 73 | } 74 | 75 | std::array axial_kernel; 76 | std::array lateral_kernel; 77 | std::array elevation_kernel; 78 | 79 | private: 80 | float axial_function(const float x) const 81 | { 82 | using namespace std; 83 | 84 | return exp(-0.5f*(pow(x,2)/var_x))*cos(2*M_PI*freq*x); 85 | } 86 | 87 | float lateral_function(const float y) const 88 | { 89 | using namespace std; 90 | 91 | return exp(-0.5f*(pow(y,2)/var_y)); 92 | } 93 | 94 | const float var_x, var_y, var_z; 95 | const float freq; 96 | }; 97 | 98 | #endif // PSF_H 99 | -------------------------------------------------------------------------------- /examples/sphere/box.scene: -------------------------------------------------------------------------------- 1 | { 2 | "workingDirectory": "/home/santiago/Proyectos/MCRay-Tracing/examples/sphere/", 3 | "transducerPosition": [-13.5, 0.0, 0.0], 4 | "transducerAngles": [0.0, 0.0, -90.0], 5 | "materials": [ 6 | { 7 | "name": "GEL", 8 | "impedance": 1.99, 9 | "attenuation": 1e-8, 10 | "mu0": 0.0, 11 | "mu1": 0.0, 12 | "sigma": 0.0, 13 | "specularity": 1.0, 14 | "shininess":1000000, 15 | "thickness":0.0 16 | }, 17 | { 18 | "name": "AIR", 19 | "impedance": 0.0004, 20 | "attenuation": 1.64, 21 | "mu0": 0.78, 22 | "mu1": 0.56, 23 | "sigma": 0.1, 24 | "specularity": 1.0, 25 | "shininess":1000000, 26 | "thickness":0.0 27 | }, 28 | { 29 | "name": "FAT", 30 | "impedance": 1.38, 31 | "attenuation": 0.63, 32 | "mu0": 0.5, 33 | "mu1": 0.5, 34 | "sigma": 0.0, 35 | "specularity": 1.0, 36 | "shininess":1000000, 37 | "thickness":0.0 38 | }, 39 | { 40 | "name": "LIVER", 41 | "impedance": 1.65, 42 | "attenuation": 0.7, 43 | "mu0": 0.19, 44 | "mu1": 1.0, 45 | "sigma": 0.24, 46 | "specularity": 1.0, 47 | "shininess":1000000, 48 | "thickness":0.0 49 | }, 50 | { 51 | "name": "BONE", 52 | "impedance": 7.8, 53 | "attenuation": 5.0, 54 | "mu0": 0.78, 55 | "mu1": 0.56, 56 | "sigma": 0.1, 57 | "specularity": 1.0, 58 | "shininess":1000000, 59 | "thickness":0.0 60 | }, 61 | { 62 | "name": "BLOOD", 63 | "impedance": 1.61, 64 | "attenuation": 0.18, 65 | "mu0": 0.001, 66 | "mu1": 0.0, 67 | "sigma": 0.01, 68 | "specularity": 1.0, 69 | "shininess":1000000, 70 | "thickness":0.0 71 | }, 72 | { 73 | "name": "VESSEL", 74 | "impedance": 1.99, 75 | "attenuation": 1.09, 76 | "mu0": 0.2, 77 | "mu1": 0.1, 78 | "sigma": 0.2, 79 | "specularity": 1.0, 80 | "shininess":1000000, 81 | "thickness":0.0 82 | }, 83 | { 84 | "name": "KIDNEY", 85 | "impedance": 1.62, 86 | "attenuation": 1.0, 87 | "mu0": 0.4, 88 | "mu1": 0.6, 89 | "sigma": 0.3, 90 | "specularity": 1.0, 91 | "shininess":1000000, 92 | "thickness":0.0 93 | }, 94 | { 95 | "name": "SUPRARRENAL", 96 | "impedance": 1.62, 97 | "attenuation": 1.0, 98 | "mu0": 0.4, 99 | "mu1": 0.6, 100 | "sigma": 0.3, 101 | "specularity": 1.0, 102 | "shininess":1000000, 103 | "thickness":0.0 104 | }, 105 | { 106 | "name": "GALLBLADDER", 107 | "impedance": 1.62, 108 | "attenuation": 1.0, 109 | "mu0": 0.4, 110 | "mu1": 0.6, 111 | "sigma": 0.3, 112 | "specularity": 1.0, 113 | "shininess":1000000, 114 | "thickness":0.0 115 | }, 116 | { 117 | "name": "SKIN", 118 | "impedance": 1.99, 119 | "attenuation": 1.0, 120 | "mu0": 0.4, 121 | "mu1": 0.6, 122 | "sigma": 0.3, 123 | "specularity": 1.0, 124 | "shininess":1000000, 125 | "thickness":0.0 126 | } 127 | ], 128 | "meshes": [ 129 | { 130 | "file": "BOX.obj", 131 | "rigid": true, 132 | "vascular": false, 133 | "deltas": [0.0,0.0,0.0], 134 | "material": "BONE", 135 | "outsideMaterial": "FAT", 136 | "outsideNormals": true 137 | } 138 | ], 139 | "origin": [0.0, 0.0, 0.0], 140 | "spacing": [1.0,1.0,1.0], 141 | "scaling": 1.0, 142 | "startingMaterial": "FAT" 143 | } 144 | -------------------------------------------------------------------------------- /examples/sphere/simple.scene: -------------------------------------------------------------------------------- 1 | { 2 | "workingDirectory": "/home/santiago/Proyectos/MCRay-Tracing/examples/sphere/", 3 | "transducerPosition": [-13.5, 0.0, 0.0], 4 | "transducerAngles": [0.0, 0.0, -90.0], 5 | "materials": [ 6 | { 7 | "name": "GEL", 8 | "impedance": 1.99, 9 | "attenuation": 1e-8, 10 | "mu0": 0.0, 11 | "mu1": 0.0, 12 | "sigma": 0.0, 13 | "specularity": 1.0, 14 | "shininess":1000000, 15 | "thickness":0.0 16 | }, 17 | { 18 | "name": "AIR", 19 | "impedance": 0.0004, 20 | "attenuation": 1.64, 21 | "mu0": 0.78, 22 | "mu1": 0.56, 23 | "sigma": 0.1, 24 | "specularity": 1.0, 25 | "shininess":1000000, 26 | "thickness":0.0 27 | }, 28 | { 29 | "name": "FAT", 30 | "impedance": 1.38, 31 | "attenuation": 0.63, 32 | "mu0": 0.5, 33 | "mu1": 0.5, 34 | "sigma": 0.0, 35 | "specularity": 1.0, 36 | "shininess":1000000, 37 | "thickness":0.0 38 | }, 39 | { 40 | "name": "LIVER", 41 | "impedance": 1.65, 42 | "attenuation": 0.7, 43 | "mu0": 0.19, 44 | "mu1": 1.0, 45 | "sigma": 0.24, 46 | "specularity": 1.0, 47 | "shininess":1000000, 48 | "thickness":0.0 49 | }, 50 | { 51 | "name": "BONE", 52 | "impedance": 7.8, 53 | "attenuation": 5.0, 54 | "mu0": 0.78, 55 | "mu1": 0.56, 56 | "sigma": 0.1, 57 | "specularity": 1.0, 58 | "shininess":1000000, 59 | "thickness":0.0 60 | }, 61 | { 62 | "name": "BLOOD", 63 | "impedance": 1.61, 64 | "attenuation": 0.18, 65 | "mu0": 0.001, 66 | "mu1": 0.0, 67 | "sigma": 0.01, 68 | "specularity": 1.0, 69 | "shininess":1000000, 70 | "thickness":0.0 71 | }, 72 | { 73 | "name": "VESSEL", 74 | "impedance": 1.99, 75 | "attenuation": 1.09, 76 | "mu0": 0.2, 77 | "mu1": 0.1, 78 | "sigma": 0.2, 79 | "specularity": 1.0, 80 | "shininess":1000000, 81 | "thickness":0.0 82 | }, 83 | { 84 | "name": "KIDNEY", 85 | "impedance": 1.62, 86 | "attenuation": 1.0, 87 | "mu0": 0.4, 88 | "mu1": 0.6, 89 | "sigma": 0.3, 90 | "specularity": 1.0, 91 | "shininess":1000000, 92 | "thickness":0.0 93 | }, 94 | { 95 | "name": "SUPRARRENAL", 96 | "impedance": 1.62, 97 | "attenuation": 1.0, 98 | "mu0": 0.4, 99 | "mu1": 0.6, 100 | "sigma": 0.3, 101 | "specularity": 1.0, 102 | "shininess":1000000, 103 | "thickness":0.0 104 | }, 105 | { 106 | "name": "GALLBLADDER", 107 | "impedance": 1.62, 108 | "attenuation": 1.0, 109 | "mu0": 0.4, 110 | "mu1": 0.6, 111 | "sigma": 0.3, 112 | "specularity": 1.0, 113 | "shininess":1000000, 114 | "thickness":0.0 115 | }, 116 | { 117 | "name": "SKIN", 118 | "impedance": 1.99, 119 | "attenuation": 1.0, 120 | "mu0": 0.4, 121 | "mu1": 0.6, 122 | "sigma": 0.3, 123 | "specularity": 1.0, 124 | "shininess":1000000, 125 | "thickness":0.0 126 | } 127 | ], 128 | "meshes": [ 129 | { 130 | "file": "SPHERE.obj", 131 | "rigid": true, 132 | "vascular": false, 133 | "deltas": [0.0,0.0,0.0], 134 | "material": "BONE", 135 | "outsideMaterial": "FAT", 136 | "outsideNormals": true 137 | } 138 | ], 139 | "origin": [0.0, 0.0, 0.0], 140 | "spacing": [1.0,1.0,1.0], 141 | "scaling": 1.0, 142 | "startingMaterial": "FAT" 143 | } 144 | -------------------------------------------------------------------------------- /src/inputmanager.cpp: -------------------------------------------------------------------------------- 1 | #include "inputmanager.h" 2 | #include 3 | 4 | inputManager::inputManager() : 5 | translationSpeedX(0.001f), 6 | translationSpeedY(0.001f), 7 | translationSpeedZ(0.001f) 8 | { 9 | 10 | } 11 | 12 | 13 | void inputManager::setTransducer(transducer_ * transducer) 14 | { 15 | this->transducer = transducer; 16 | } 17 | 18 | void inputManager::update(float elapsedTimeInMillis) 19 | { 20 | assert(transducer != nullptr); 21 | for (sf::RenderWindow * w : windows) 22 | { 23 | if (!w->isOpen()) 24 | { 25 | //DEBUGMSG("Window not open. Input handling could not be done."); 26 | std::cout<< "error"; 27 | return; 28 | } 29 | 30 | sf::Event event; 31 | while (w->pollEvent(event)) { 32 | if (event.type == sf::Event::Closed) 33 | { 34 | isQuitPressed = true; 35 | exit(0); 36 | } 37 | else if (event.type == sf::Event::Resized) 38 | { 39 | w->setView(sf::View(sf::FloatRect(0, 0, 40 | event.size.width, 41 | event.size.height))); 42 | } 43 | } 44 | } 45 | 46 | if (inputEnabled) 47 | { 48 | updateKeyboard(elapsedTimeInMillis); 49 | 50 | transducer->update(); 51 | } 52 | } 53 | 54 | 55 | 56 | void inputManager::updateKeyboard(float elapsedTimeInMillis) 57 | { 58 | handleKeyboardMovement(elapsedTimeInMillis); 59 | } 60 | 61 | void inputManager::handleKeyboardMovement(float elapsedTimeInMillis) 62 | { 63 | float rx = 0.0f, ry = 0.0f, rz = 0.0f; 64 | float dx = 0.0f, dy = 0.0f, dz = 0.0f; 65 | if (sf::Keyboard::isKeyPressed(sf::Keyboard::R)) 66 | { 67 | // transducer->resetPosition();// si hacemos esta funcion seria que vuelva a partir de la posicion del archivo de configuracion 68 | } 69 | bool translationDetected = false; 70 | 71 | // translate Y 72 | if (sf::Keyboard::isKeyPressed(sf::Keyboard::Add) || 73 | sf::Keyboard::isKeyPressed(sf::Keyboard::Up)) 74 | { 75 | //dy = 100 * translationSpeedY * elapsedTimeInMillis; 76 | dy = 0.1; 77 | translationDetected = true; 78 | } 79 | if (sf::Keyboard::isKeyPressed(sf::Keyboard::Subtract) || 80 | sf::Keyboard::isKeyPressed(sf::Keyboard::Down)) 81 | { 82 | //dy = -100 * translationSpeedY * elapsedTimeInMillis; 83 | dy = -0.1; 84 | translationDetected = true; 85 | } 86 | 87 | // translate Z 88 | if (sf::Keyboard::isKeyPressed(sf::Keyboard::Num1)) 89 | { 90 | //dz = 100 * translationSpeedZ * elapsedTimeInMillis; 91 | dz = 0.1; 92 | translationDetected = true; 93 | } 94 | if (sf::Keyboard::isKeyPressed(sf::Keyboard::Num2)) 95 | { 96 | //dz = -100 * translationSpeedZ * elapsedTimeInMillis; 97 | dz = -0.1; 98 | translationDetected = true; 99 | } 100 | 101 | // translate X 102 | if (sf::Keyboard::isKeyPressed(sf::Keyboard::Numpad4) || 103 | sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) 104 | { 105 | //dx = 100 * translationSpeedX * elapsedTimeInMillis; 106 | dx = 0.1; 107 | translationDetected = true; 108 | } 109 | if (sf::Keyboard::isKeyPressed(sf::Keyboard::Numpad6) || 110 | sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) 111 | { 112 | //dx = -100 * translationSpeedX * elapsedTimeInMillis; 113 | dx = -0.1; 114 | translationDetected = true; 115 | } 116 | 117 | if (translationDetected) 118 | { 119 | btVector3 lastPosition = transducer->getPosition(); 120 | transducer->setPosition(btVector3(lastPosition[0] + dx, lastPosition[1] + dy, lastPosition[2] + dz)); 121 | } 122 | } 123 | 124 | 125 | 126 | -------------------------------------------------------------------------------- /examples/sphere/sphere.scene: -------------------------------------------------------------------------------- 1 | { 2 | "workingDirectory": "/home/santiago/Proyectos/MCRay-Tracing/examples/sphere/", 3 | "transducerPosition": [-13.5, 0.0, 0.0], 4 | "transducerAngles": [0.0, 0.0, -90.0], 5 | "materials": [ 6 | { 7 | "name": "GEL", 8 | "impedance": 1.99, 9 | "attenuation": 1e-8, 10 | "mu0": 0.0, 11 | "mu1": 0.0, 12 | "sigma": 0.0, 13 | "specularity": 1.0, 14 | "shininess":1000000, 15 | "thickness":0.0 16 | }, 17 | { 18 | "name": "AIR", 19 | "impedance": 0.0004, 20 | "attenuation": 1.64, 21 | "mu0": 0.78, 22 | "mu1": 0.56, 23 | "sigma": 0.1, 24 | "specularity": 1.0, 25 | "shininess":1000000, 26 | "thickness":0.0 27 | }, 28 | { 29 | "name": "FAT", 30 | "impedance": 1.38, 31 | "attenuation": 0.63, 32 | "mu0": 0.5, 33 | "mu1": 0.5, 34 | "sigma": 0.0, 35 | "specularity": 1.0, 36 | "shininess":1000000, 37 | "thickness":0.0 38 | }, 39 | { 40 | "name": "LIVER", 41 | "impedance": 1.65, 42 | "attenuation": 0.7, 43 | "mu0": 0.19, 44 | "mu1": 1.0, 45 | "sigma": 0.24, 46 | "specularity": 1.0, 47 | "shininess":1000000, 48 | "thickness":0.0 49 | }, 50 | { 51 | "name": "BONE", 52 | "impedance": 7.8, 53 | "attenuation": 5.0, 54 | "mu0": 0.78, 55 | "mu1": 0.56, 56 | "sigma": 0.1, 57 | "specularity": 1.0, 58 | "shininess":1000000, 59 | "thickness":0.0 60 | }, 61 | { 62 | "name": "BLOOD", 63 | "impedance": 1.61, 64 | "attenuation": 0.18, 65 | "mu0": 0.001, 66 | "mu1": 0.0, 67 | "sigma": 0.01, 68 | "specularity": 1.0, 69 | "shininess":1000000, 70 | "thickness":0.0 71 | }, 72 | { 73 | "name": "VESSEL", 74 | "impedance": 1.99, 75 | "attenuation": 1.09, 76 | "mu0": 0.2, 77 | "mu1": 0.1, 78 | "sigma": 0.2, 79 | "specularity": 1.0, 80 | "shininess":1000000, 81 | "thickness":0.0 82 | }, 83 | { 84 | "name": "KIDNEY", 85 | "impedance": 1.62, 86 | "attenuation": 1.0, 87 | "mu0": 0.4, 88 | "mu1": 0.6, 89 | "sigma": 0.3, 90 | "specularity": 1.0, 91 | "shininess":1000000, 92 | "thickness":0.0 93 | }, 94 | { 95 | "name": "SUPRARRENAL", 96 | "impedance": 1.62, 97 | "attenuation": 1.0, 98 | "mu0": 0.4, 99 | "mu1": 0.6, 100 | "sigma": 0.3, 101 | "specularity": 1.0, 102 | "shininess":1000000, 103 | "thickness":0.0 104 | }, 105 | { 106 | "name": "GALLBLADDER", 107 | "impedance": 1.62, 108 | "attenuation": 1.0, 109 | "mu0": 0.4, 110 | "mu1": 0.6, 111 | "sigma": 0.3, 112 | "specularity": 1.0, 113 | "shininess":1000000, 114 | "thickness":0.0 115 | }, 116 | { 117 | "name": "SKIN", 118 | "impedance": 1.99, 119 | "attenuation": 1.0, 120 | "mu0": 0.4, 121 | "mu1": 0.6, 122 | "sigma": 0.3, 123 | "specularity": 1.0, 124 | "shininess":1000000, 125 | "thickness":0.0 126 | } 127 | ], 128 | "meshes": [ 129 | { 130 | "file": "BOX.obj", 131 | "rigid": true, 132 | "vascular": false, 133 | "deltas": [0.0,0.0,0.0], 134 | "material": "LIVER", 135 | "outsideMaterial": "GEL", 136 | "outsideNormals": true 137 | }, 138 | { 139 | "file": "SPHERE.obj", 140 | "rigid": true, 141 | "vascular": false, 142 | "deltas": [0.0,0.0,0.0], 143 | "material": "BONE", 144 | "outsideMaterial": "LIVER", 145 | "outsideNormals": true 146 | } 147 | ], 148 | "origin": [0.0, 0.0, 0.0], 149 | "spacing": [1.0,1.0,1.0], 150 | "scaling": 1.0, 151 | "startingMaterial": "GEL" 152 | } 153 | -------------------------------------------------------------------------------- /examples/ircad11/ircad11.scene: -------------------------------------------------------------------------------- 1 | 2 | { 3 | "workingDirectory": "/home/santiago/Proyectos/MCRay-Tracing/examples/ircad11/", 4 | "transducerPosition": [-16,3,2], 5 | "transducerAngles": [90, 0, -90], 6 | "materials": [ 7 | { 8 | "name": "GEL", 9 | "impedance": 1.38, 10 | "attenuation": 1e-8, 11 | "mu0": 0.0, 12 | "mu1": 0.0, 13 | "sigma": 0.0, 14 | "specularity": 1.0 15 | }, 16 | { 17 | "name": "AIR", 18 | "impedance": 0.0004, 19 | "attenuation": 1.64, 20 | "mu0": 0.78, 21 | "mu1": 0.56, 22 | "sigma": 0.1, 23 | "specularity": 1.0 24 | }, 25 | { 26 | "name": "FAT", 27 | "impedance": 1.38, 28 | "attenuation": 0.63, 29 | "mu0": 0.5, 30 | "mu1": 0.5, 31 | "sigma": 0.0, 32 | "specularity": 1.0 33 | }, 34 | { 35 | "name": "LIVER", 36 | "impedance": 1.65, 37 | "attenuation": 0.7, 38 | "mu0": 0.19, 39 | "mu1": 1.0, 40 | "sigma": 0.24, 41 | "specularity": 1.0 42 | }, 43 | { 44 | "name": "BONE", 45 | "impedance": 7.8, 46 | "attenuation": 5.0, 47 | "mu0": 0.78, 48 | "mu1": 0.56, 49 | "sigma": 0.1, 50 | "specularity": 1.0 51 | }, 52 | { 53 | "name": "BLOOD", 54 | "impedance": 1.61, 55 | "attenuation": 0.18, 56 | "mu0": 0.001, 57 | "mu1": 0.0, 58 | "sigma": 0.01, 59 | "specularity": 0.001 60 | }, 61 | { 62 | "name": "VESSEL", 63 | "impedance": 1.99, 64 | "attenuation": 1.09, 65 | "mu0": 0.2, 66 | "mu1": 0.1, 67 | "sigma": 0.2, 68 | "specularity": 1.0 69 | }, 70 | { 71 | "name": "KIDNEY", 72 | "impedance": 1.62, 73 | "attenuation": 1.0, 74 | "mu0": 0.4, 75 | "mu1": 0.6, 76 | "sigma": 0.3, 77 | "specularity": 0.2 78 | }, 79 | { 80 | "name": "SUPRARRENAL", 81 | "impedance": 1.62, 82 | "attenuation": 1.0, 83 | "mu0": 0.4, 84 | "mu1": 0.6, 85 | "sigma": 0.3, 86 | "specularity": 1.0 87 | }, 88 | { 89 | "name": "GALLBLADDER", 90 | "impedance": 1.62, 91 | "attenuation": 1.0, 92 | "mu0": 0.4, 93 | "mu1": 0.6, 94 | "sigma": 0.3, 95 | "specularity": 1.0 96 | }, 97 | { 98 | "name": "SKIN", 99 | "impedance": 1.99, 100 | "attenuation": 1.0, 101 | "mu0": 0.4, 102 | "mu1": 0.6, 103 | "sigma": 0.3, 104 | "specularity": 1.0 105 | } 106 | ], 107 | "meshes": [ 108 | { 109 | "file": "aorta.obj", 110 | "rigid": true, 111 | "vascular": true, 112 | "deltas": [152.533512115, 174.472991943, 105.106495678], 113 | "material": "BLOOD", 114 | "outsideMaterial": "FAT", 115 | "outsideNormals": true 116 | }, 117 | { 118 | "file": "bones.obj", 119 | "rigid": true, 120 | "vascular": false, 121 | "deltas": [188.265544891, 202.440551758, 105.599998474], 122 | "material": "BONE", 123 | "outsideMaterial": "FAT", 124 | "outsideNormals": true 125 | }, 126 | { 127 | "file": "liver.obj", 128 | "rigid": true, 129 | "vascular": false, 130 | "deltas": [141.238292694, 176.429901123, 130.10585022], 131 | "material": "LIVER", 132 | "outsideMaterial": "FAT", 133 | "outsideNormals": true 134 | }, 135 | { 136 | "file": "cava.obj", 137 | "rigid": true, 138 | "vascular": true, 139 | "deltas": [206.332504272, 192.29649353, 104.897496045], 140 | "material": "BLOOD", 141 | "outsideMaterial": "FAT", 142 | "outsideNormals": true 143 | }, 144 | { 145 | "file": "right_kidney.obj", 146 | "rigid": true, 147 | "vascular": false, 148 | "deltas": [118.23374939, 218.907501221, 53.6022927761], 149 | "material": "KIDNEY", 150 | "outsideMaterial": "SKIN", 151 | "outsideNormals": true 152 | }, 153 | { 154 | "file": "left_kidney.obj", 155 | "rigid": true, 156 | "vascular": false, 157 | "deltas": [251.052993774, 227.63949585, 64.8468027115], 158 | "material": "KIDNEY", 159 | "outsideMaterial": "SKIN", 160 | "outsideNormals": true 161 | }, 162 | { 163 | "file": "right_suprarrenal.obj", 164 | "rigid": true, 165 | "vascular": false, 166 | "deltas": [152.25050354, 213.971496582, 115.338005066], 167 | "material": "SUPRARRENAL", 168 | "outsideMaterial": "FAT", 169 | "outsideNormals": true 170 | }, 171 | { 172 | "file": "left_suprarrenal.obj", 173 | "rigid": true, 174 | "vascular": false, 175 | "deltas": [217.128997803, 209.525497437, 102.477149963], 176 | "material": "SUPRARRENAL", 177 | "outsideMaterial": "FAT", 178 | "outsideNormals": true 179 | }, 180 | { 181 | "file": "gallbladder.obj", 182 | "rigid": true, 183 | "vascular": false, 184 | "deltas": [128.70715332, 146.592498779, 112.361503601], 185 | "material": "GALLBLADDER", 186 | "outsideMaterial": "FAT", 187 | "outsideNormals": true 188 | }, 189 | { 190 | "file": "skin.obj", 191 | "rigid": true, 192 | "vascular": false, 193 | "deltas": [188.597551346, 199.367202759, 105.622316509], 194 | "material": "FAT", 195 | "outsideMaterial": "GEL", 196 | "outsideNormals": true 197 | }, 198 | { 199 | "file": "porta.obj", 200 | "rigid": true, 201 | "vascular": true, 202 | "deltas": [182.364089966, 177.214996338, 93.0034988523], 203 | "material": "BLOOD", 204 | "outsideMaterial": "FAT", 205 | "outsideNormals": true 206 | } 207 | ], 208 | "origin": [-18.0, -22.0, -5.0], 209 | "spacing": [1.0,1.0,1.0], 210 | "scaling": 0.1, 211 | "startingMaterial": "GEL" 212 | } 213 | -------------------------------------------------------------------------------- /examples/ircad11/santi-liver.scene: -------------------------------------------------------------------------------- 1 | { 2 | "workingDirectory": "/home/santiago/Proyectos/MCRay-Tracing/examples/ircad11/", 3 | "transducerPosition": [-17.5, 1.0, 5.0], 4 | "transducerAngles": [120.0, 0.0, -90.0], 5 | "materials": [ 6 | { 7 | "name": "GEL", 8 | "impedance": 1.38, 9 | "attenuation": 1e-8, 10 | "mu0": 0.0, 11 | "mu1": 0.0, 12 | "sigma": 0.0, 13 | "specularity": 1.0, 14 | "shininess":1000000, 15 | "thickness":0.0 16 | }, 17 | { 18 | "name": "AIR", 19 | "impedance": 0.0004, 20 | "attenuation": 1.64, 21 | "mu0": 0.78, 22 | "mu1": 0.56, 23 | "sigma": 0.1, 24 | "specularity": 1.0, 25 | "shininess":1000000, 26 | "thickness":0.0 27 | }, 28 | { 29 | "name": "FAT", 30 | "impedance": 1.38, 31 | "attenuation": 0.63, 32 | "mu0": 0.5, 33 | "mu1": 0.5, 34 | "sigma": 0.0, 35 | "specularity": 1.0, 36 | "shininess":1000000, 37 | "thickness":0.0 38 | }, 39 | { 40 | "name": "LIVER", 41 | "impedance": 1.65, 42 | "attenuation": 0.7, 43 | "mu0": 0.19, 44 | "mu1": 1.0, 45 | "sigma": 0.24, 46 | "specularity": 1.0, 47 | "shininess":1000000, 48 | "thickness":0.0 49 | }, 50 | { 51 | "name": "BONE", 52 | "impedance": 7.8, 53 | "attenuation": 5.0, 54 | "mu0": 0.78, 55 | "mu1": 0.56, 56 | "sigma": 0.1, 57 | "specularity": 1.0, 58 | "shininess":1000000, 59 | "thickness":0.3 60 | }, 61 | { 62 | "name": "BLOOD", 63 | "impedance": 1.61, 64 | "attenuation": 0.18, 65 | "mu0": 0.001, 66 | "mu1": 0.0, 67 | "sigma": 0.01, 68 | "specularity": 1.0, 69 | "shininess":1000000, 70 | "thickness":0.0 71 | }, 72 | { 73 | "name": "VESSEL", 74 | "impedance": 1.99, 75 | "attenuation": 1.09, 76 | "mu0": 0.2, 77 | "mu1": 0.1, 78 | "sigma": 0.2, 79 | "specularity": 1.0, 80 | "shininess":1000000, 81 | "thickness":0.0 82 | }, 83 | { 84 | "name": "KIDNEY", 85 | "impedance": 1.62, 86 | "attenuation": 1.0, 87 | "mu0": 0.4, 88 | "mu1": 0.6, 89 | "sigma": 0.3, 90 | "specularity": 1.0, 91 | "shininess":1000000, 92 | "thickness":0.0 93 | }, 94 | { 95 | "name": "SUPRARRENAL", 96 | "impedance": 1.62, 97 | "attenuation": 1.0, 98 | "mu0": 0.4, 99 | "mu1": 0.6, 100 | "sigma": 0.3, 101 | "specularity": 1.0, 102 | "shininess":1000000, 103 | "thickness":0.0 104 | }, 105 | { 106 | "name": "GALLBLADDER", 107 | "impedance": 1.62, 108 | "attenuation": 1.0, 109 | "mu0": 0.4, 110 | "mu1": 0.6, 111 | "sigma": 0.3, 112 | "specularity": 1.0, 113 | "shininess":1000000, 114 | "thickness":0.0 115 | }, 116 | { 117 | "name": "SKIN", 118 | "impedance": 1.99, 119 | "attenuation": 1.0, 120 | "mu0": 0.4, 121 | "mu1": 0.6, 122 | "sigma": 0.3, 123 | "specularity": 1.0, 124 | "shininess":1000000, 125 | "thickness":0.0 126 | } 127 | ], 128 | "meshes": [ 129 | { 130 | "file": "aorta.obj", 131 | "rigid": true, 132 | "vascular": true, 133 | "deltas": [152.533512115, 174.472991943, 105.106495678], 134 | "material": "BLOOD", 135 | "outsideMaterial": "FAT", 136 | "outsideNormals": true 137 | }, 138 | { 139 | "file": "bones.obj", 140 | "rigid": true, 141 | "vascular": false, 142 | "deltas": [188.265544891, 202.440551758, 105.599998474], 143 | "material": "BONE", 144 | "outsideMaterial": "FAT", 145 | "outsideNormals": true 146 | }, 147 | { 148 | "file": "liver.obj", 149 | "rigid": true, 150 | "vascular": false, 151 | "deltas": [141.238292694, 176.429901123, 130.10585022], 152 | "material": "LIVER", 153 | "outsideMaterial": "FAT", 154 | "outsideNormals": true 155 | }, 156 | { 157 | "file": "cava.obj", 158 | "rigid": true, 159 | "vascular": true, 160 | "deltas": [206.332504272, 192.29649353, 104.897496045], 161 | "material": "BLOOD", 162 | "outsideMaterial": "FAT", 163 | "outsideNormals": true 164 | }, 165 | { 166 | "file": "right_kidney.obj", 167 | "rigid": true, 168 | "vascular": false, 169 | "deltas": [118.23374939, 218.907501221, 53.6022927761], 170 | "material": "KIDNEY", 171 | "outsideMaterial": "SKIN", 172 | "outsideNormals": true 173 | }, 174 | { 175 | "file": "left_kidney.obj", 176 | "rigid": true, 177 | "vascular": false, 178 | "deltas": [251.052993774, 227.63949585, 64.8468027115], 179 | "material": "KIDNEY", 180 | "outsideMaterial": "SKIN", 181 | "outsideNormals": true 182 | }, 183 | { 184 | "file": "right_suprarrenal.obj", 185 | "rigid": true, 186 | "vascular": false, 187 | "deltas": [152.25050354, 213.971496582, 115.338005066], 188 | "material": "SUPRARRENAL", 189 | "outsideMaterial": "FAT", 190 | "outsideNormals": true 191 | }, 192 | { 193 | "file": "left_suprarrenal.obj", 194 | "rigid": true, 195 | "vascular": false, 196 | "deltas": [217.128997803, 209.525497437, 102.477149963], 197 | "material": "SUPRARRENAL", 198 | "outsideMaterial": "FAT", 199 | "outsideNormals": true 200 | }, 201 | { 202 | "file": "gallbladder.obj", 203 | "rigid": true, 204 | "vascular": false, 205 | "deltas": [128.70715332, 146.592498779, 112.361503601], 206 | "material": "GALLBLADDER", 207 | "outsideMaterial": "FAT", 208 | "outsideNormals": true 209 | }, 210 | { 211 | "file": "skin.obj", 212 | "rigid": true, 213 | "vascular": false, 214 | "deltas": [188.597551346, 199.367202759, 105.622316509], 215 | "material": "FAT", 216 | "outsideMaterial": "GEL", 217 | "outsideNormals": true 218 | }, 219 | { 220 | "file": "porta.obj", 221 | "rigid": true, 222 | "vascular": true, 223 | "deltas": [182.364089966, 177.214996338, 93.0034988523], 224 | "material": "BLOOD", 225 | "outsideMaterial": "FAT", 226 | "outsideNormals": true 227 | } 228 | ], 229 | "origin": [-18.0, -22.0, -5.0], 230 | "spacing": [1.0,1.0,1.0], 231 | "scaling": 0.1, 232 | "startingMaterial": "GEL" 233 | } 234 | -------------------------------------------------------------------------------- /examples/ircad11/santi-morison1.scene: -------------------------------------------------------------------------------- 1 | { 2 | "workingDirectory": "/home/santiago/Proyectos/MCRay-Tracing/examples/ircad11/", 3 | "transducerPosition": [-16.0, 3.0, 14.0], 4 | "transducerAngles": [45.0, 45.0, -90.0], 5 | "materials": [ 6 | { 7 | "name": "GEL", 8 | "impedance": 1.38, 9 | "attenuation": 1e-8, 10 | "mu0": 0.0, 11 | "mu1": 0.0, 12 | "sigma": 0.0, 13 | "specularity": 1.0, 14 | "shininess":1000000, 15 | "thickness":0.0 16 | }, 17 | { 18 | "name": "AIR", 19 | "impedance": 0.0004, 20 | "attenuation": 1.64, 21 | "mu0": 0.78, 22 | "mu1": 0.56, 23 | "sigma": 0.1, 24 | "specularity": 1.0, 25 | "shininess":1000000, 26 | "thickness":0.0 27 | }, 28 | { 29 | "name": "FAT", 30 | "impedance": 1.38, 31 | "attenuation": 0.63, 32 | "mu0": 0.5, 33 | "mu1": 0.5, 34 | "sigma": 0.0, 35 | "specularity": 1.0, 36 | "shininess":1000000, 37 | "thickness":0.0 38 | }, 39 | { 40 | "name": "LIVER", 41 | "impedance": 1.65, 42 | "attenuation": 0.7, 43 | "mu0": 0.19, 44 | "mu1": 1.0, 45 | "sigma": 0.24, 46 | "specularity": 1.0, 47 | "shininess":1000000, 48 | "thickness":0.0 49 | }, 50 | { 51 | "name": "BONE", 52 | "impedance": 7.8, 53 | "attenuation": 5.0, 54 | "mu0": 0.78, 55 | "mu1": 0.56, 56 | "sigma": 0.1, 57 | "specularity": 1.0, 58 | "shininess":1000000, 59 | "thickness":0.1 60 | }, 61 | { 62 | "name": "BLOOD", 63 | "impedance": 1.61, 64 | "attenuation": 0.18, 65 | "mu0": 0.001, 66 | "mu1": 0.0, 67 | "sigma": 0.01, 68 | "specularity": 1.0, 69 | "shininess":1000000, 70 | "thickness":0.0 71 | }, 72 | { 73 | "name": "VESSEL", 74 | "impedance": 1.99, 75 | "attenuation": 1.09, 76 | "mu0": 0.2, 77 | "mu1": 0.1, 78 | "sigma": 0.2, 79 | "specularity": 1.0, 80 | "shininess":1000000, 81 | "thickness":0.0 82 | }, 83 | { 84 | "name": "KIDNEY", 85 | "impedance": 1.62, 86 | "attenuation": 1.0, 87 | "mu0": 0.4, 88 | "mu1": 0.6, 89 | "sigma": 0.3, 90 | "specularity": 1.0, 91 | "shininess":10000, 92 | "thickness":0.0 93 | }, 94 | { 95 | "name": "SUPRARRENAL", 96 | "impedance": 1.62, 97 | "attenuation": 1.0, 98 | "mu0": 0.4, 99 | "mu1": 0.6, 100 | "sigma": 0.3, 101 | "specularity": 1.0, 102 | "shininess":1000000, 103 | "thickness":0.0 104 | }, 105 | { 106 | "name": "GALLBLADDER", 107 | "impedance": 1.62, 108 | "attenuation": 1.0, 109 | "mu0": 0.4, 110 | "mu1": 0.6, 111 | "sigma": 0.3, 112 | "specularity": 1.0, 113 | "shininess":1000000, 114 | "thickness":0.0 115 | }, 116 | { 117 | "name": "SKIN", 118 | "impedance": 1.99, 119 | "attenuation": 1.0, 120 | "mu0": 0.4, 121 | "mu1": 0.6, 122 | "sigma": 0.3, 123 | "specularity": 1.0, 124 | "shininess":1000000, 125 | "thickness":0.0 126 | } 127 | ], 128 | "meshes": [ 129 | { 130 | "file": "aorta.obj", 131 | "rigid": true, 132 | "vascular": true, 133 | "deltas": [152.533512115, 174.472991943, 105.106495678], 134 | "material": "BLOOD", 135 | "outsideMaterial": "FAT", 136 | "outsideNormals": true 137 | }, 138 | { 139 | "file": "bones.obj", 140 | "rigid": true, 141 | "vascular": false, 142 | "deltas": [188.265544891, 202.440551758, 105.599998474], 143 | "material": "BONE", 144 | "outsideMaterial": "FAT", 145 | "outsideNormals": true 146 | }, 147 | { 148 | "file": "liver.obj", 149 | "rigid": true, 150 | "vascular": false, 151 | "deltas": [141.238292694, 176.429901123, 130.10585022], 152 | "material": "LIVER", 153 | "outsideMaterial": "FAT", 154 | "outsideNormals": true 155 | }, 156 | { 157 | "file": "cava.obj", 158 | "rigid": true, 159 | "vascular": true, 160 | "deltas": [206.332504272, 192.29649353, 104.897496045], 161 | "material": "BLOOD", 162 | "outsideMaterial": "FAT", 163 | "outsideNormals": true 164 | }, 165 | { 166 | "file": "right_kidney.obj", 167 | "rigid": true, 168 | "vascular": false, 169 | "deltas": [118.23374939, 218.907501221, 53.6022927761], 170 | "material": "KIDNEY", 171 | "outsideMaterial": "SKIN", 172 | "outsideNormals": true 173 | }, 174 | { 175 | "file": "left_kidney.obj", 176 | "rigid": true, 177 | "vascular": false, 178 | "deltas": [251.052993774, 227.63949585, 64.8468027115], 179 | "material": "KIDNEY", 180 | "outsideMaterial": "SKIN", 181 | "outsideNormals": true 182 | }, 183 | { 184 | "file": "right_suprarrenal.obj", 185 | "rigid": true, 186 | "vascular": false, 187 | "deltas": [152.25050354, 213.971496582, 115.338005066], 188 | "material": "SUPRARRENAL", 189 | "outsideMaterial": "FAT", 190 | "outsideNormals": true 191 | }, 192 | { 193 | "file": "left_suprarrenal.obj", 194 | "rigid": true, 195 | "vascular": false, 196 | "deltas": [217.128997803, 209.525497437, 102.477149963], 197 | "material": "SUPRARRENAL", 198 | "outsideMaterial": "FAT", 199 | "outsideNormals": true 200 | }, 201 | { 202 | "file": "gallbladder.obj", 203 | "rigid": true, 204 | "vascular": false, 205 | "deltas": [128.70715332, 146.592498779, 112.361503601], 206 | "material": "GALLBLADDER", 207 | "outsideMaterial": "FAT", 208 | "outsideNormals": true 209 | }, 210 | { 211 | "file": "skin.obj", 212 | "rigid": true, 213 | "vascular": false, 214 | "deltas": [188.597551346, 199.367202759, 105.622316509], 215 | "material": "FAT", 216 | "outsideMaterial": "GEL", 217 | "outsideNormals": true 218 | }, 219 | { 220 | "file": "porta.obj", 221 | "rigid": true, 222 | "vascular": true, 223 | "deltas": [182.364089966, 177.214996338, 93.0034988523], 224 | "material": "BLOOD", 225 | "outsideMaterial": "FAT", 226 | "outsideNormals": true 227 | } 228 | ], 229 | "origin": [-18.0, -22.0, -5.0], 230 | "spacing": [1.0,1.0,1.0], 231 | "scaling": 0.1, 232 | "startingMaterial": "GEL" 233 | } 234 | -------------------------------------------------------------------------------- /examples/ircad11/santi-morison2.scene: -------------------------------------------------------------------------------- 1 | { 2 | "workingDirectory": "/home/santiago/Proyectos/MCRay-Tracing/examples/ircad11/", 3 | "transducerPosition": [-16.0, 3.0, 2.0], 4 | "transducerAngles": [90.0, 0.0, -90.0], 5 | "materials": [ 6 | { 7 | "name": "GEL", 8 | "impedance": 1.38, 9 | "attenuation": 1e-8, 10 | "mu0": 0.0, 11 | "mu1": 0.0, 12 | "sigma": 0.0, 13 | "specularity": 1.0, 14 | "shininess":1000000, 15 | "thickness":0.0 16 | }, 17 | { 18 | "name": "AIR", 19 | "impedance": 0.0004, 20 | "attenuation": 1.64, 21 | "mu0": 0.78, 22 | "mu1": 0.56, 23 | "sigma": 0.1, 24 | "specularity": 1.0, 25 | "shininess":1000000, 26 | "thickness":0.0 27 | }, 28 | { 29 | "name": "FAT", 30 | "impedance": 1.38, 31 | "attenuation": 0.63, 32 | "mu0": 0.5, 33 | "mu1": 0.5, 34 | "sigma": 0.0, 35 | "specularity": 1.0, 36 | "shininess":1000000, 37 | "thickness":0.0 38 | }, 39 | { 40 | "name": "LIVER", 41 | "impedance": 1.65, 42 | "attenuation": 0.7, 43 | "mu0": 0.19, 44 | "mu1": 1.0, 45 | "sigma": 0.24, 46 | "specularity": 1.0, 47 | "shininess":1000000, 48 | "thickness":0.0 49 | }, 50 | { 51 | "name": "BONE", 52 | "impedance": 7.8, 53 | "attenuation": 5.0, 54 | "mu0": 0.78, 55 | "mu1": 0.56, 56 | "sigma": 0.1, 57 | "specularity": 1.0, 58 | "shininess":1000000, 59 | "thickness":0.3 60 | }, 61 | { 62 | "name": "BLOOD", 63 | "impedance": 1.61, 64 | "attenuation": 0.18, 65 | "mu0": 0.001, 66 | "mu1": 0.0, 67 | "sigma": 0.01, 68 | "specularity": 1.0, 69 | "shininess":1000000, 70 | "thickness":0.0 71 | }, 72 | { 73 | "name": "VESSEL", 74 | "impedance": 1.99, 75 | "attenuation": 1.09, 76 | "mu0": 0.2, 77 | "mu1": 0.1, 78 | "sigma": 0.2, 79 | "specularity": 1.0, 80 | "shininess":1000000, 81 | "thickness":0.0 82 | }, 83 | { 84 | "name": "KIDNEY", 85 | "impedance": 1.62, 86 | "attenuation": 1.0, 87 | "mu0": 0.4, 88 | "mu1": 0.6, 89 | "sigma": 0.3, 90 | "specularity": 1.0, 91 | "shininess":1000000, 92 | "thickness":0.0 93 | }, 94 | { 95 | "name": "SUPRARRENAL", 96 | "impedance": 1.62, 97 | "attenuation": 1.0, 98 | "mu0": 0.4, 99 | "mu1": 0.6, 100 | "sigma": 0.3, 101 | "specularity": 1.0, 102 | "shininess":1000000, 103 | "thickness":0.0 104 | }, 105 | { 106 | "name": "GALLBLADDER", 107 | "impedance": 1.62, 108 | "attenuation": 1.0, 109 | "mu0": 0.4, 110 | "mu1": 0.6, 111 | "sigma": 0.3, 112 | "specularity": 1.0, 113 | "shininess":1000000, 114 | "thickness":0.0 115 | }, 116 | { 117 | "name": "SKIN", 118 | "impedance": 1.99, 119 | "attenuation": 1.0, 120 | "mu0": 0.4, 121 | "mu1": 0.6, 122 | "sigma": 0.3, 123 | "specularity": 1.0, 124 | "shininess":1000000, 125 | "thickness":0.0 126 | } 127 | ], 128 | "meshes": [ 129 | { 130 | "file": "aorta.obj", 131 | "rigid": true, 132 | "vascular": true, 133 | "deltas": [152.533512115, 174.472991943, 105.106495678], 134 | "material": "BLOOD", 135 | "outsideMaterial": "FAT", 136 | "outsideNormals": true 137 | }, 138 | { 139 | "file": "bones.obj", 140 | "rigid": true, 141 | "vascular": false, 142 | "deltas": [188.265544891, 202.440551758, 105.599998474], 143 | "material": "BONE", 144 | "outsideMaterial": "FAT", 145 | "outsideNormals": true 146 | }, 147 | { 148 | "file": "liver.obj", 149 | "rigid": true, 150 | "vascular": false, 151 | "deltas": [141.238292694, 176.429901123, 130.10585022], 152 | "material": "LIVER", 153 | "outsideMaterial": "FAT", 154 | "outsideNormals": true 155 | }, 156 | { 157 | "file": "cava.obj", 158 | "rigid": true, 159 | "vascular": true, 160 | "deltas": [206.332504272, 192.29649353, 104.897496045], 161 | "material": "BLOOD", 162 | "outsideMaterial": "FAT", 163 | "outsideNormals": true 164 | }, 165 | { 166 | "file": "right_kidney.obj", 167 | "rigid": true, 168 | "vascular": false, 169 | "deltas": [118.23374939, 218.907501221, 53.6022927761], 170 | "material": "KIDNEY", 171 | "outsideMaterial": "SKIN", 172 | "outsideNormals": true 173 | }, 174 | { 175 | "file": "left_kidney.obj", 176 | "rigid": true, 177 | "vascular": false, 178 | "deltas": [251.052993774, 227.63949585, 64.8468027115], 179 | "material": "KIDNEY", 180 | "outsideMaterial": "SKIN", 181 | "outsideNormals": true 182 | }, 183 | { 184 | "file": "right_suprarrenal.obj", 185 | "rigid": true, 186 | "vascular": false, 187 | "deltas": [152.25050354, 213.971496582, 115.338005066], 188 | "material": "SUPRARRENAL", 189 | "outsideMaterial": "FAT", 190 | "outsideNormals": true 191 | }, 192 | { 193 | "file": "left_suprarrenal.obj", 194 | "rigid": true, 195 | "vascular": false, 196 | "deltas": [217.128997803, 209.525497437, 102.477149963], 197 | "material": "SUPRARRENAL", 198 | "outsideMaterial": "FAT", 199 | "outsideNormals": true 200 | }, 201 | { 202 | "file": "gallbladder.obj", 203 | "rigid": true, 204 | "vascular": false, 205 | "deltas": [128.70715332, 146.592498779, 112.361503601], 206 | "material": "GALLBLADDER", 207 | "outsideMaterial": "FAT", 208 | "outsideNormals": true 209 | }, 210 | { 211 | "file": "skin.obj", 212 | "rigid": true, 213 | "vascular": false, 214 | "deltas": [188.597551346, 199.367202759, 105.622316509], 215 | "material": "FAT", 216 | "outsideMaterial": "GEL", 217 | "outsideNormals": true 218 | }, 219 | { 220 | "file": "porta.obj", 221 | "rigid": true, 222 | "vascular": true, 223 | "deltas": [182.364089966, 177.214996338, 93.0034988523], 224 | "material": "BLOOD", 225 | "outsideMaterial": "FAT", 226 | "outsideNormals": true 227 | } 228 | ], 229 | "origin": [-18.0, -22.0, -5.0], 230 | "spacing": [1.0,1.0,1.0], 231 | "scaling": 0.1, 232 | "startingMaterial": "GEL" 233 | } 234 | -------------------------------------------------------------------------------- /src/transducer.h: -------------------------------------------------------------------------------- 1 | #ifndef TRANSDUCER_H 2 | #define TRANSDUCER_H 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #define M_PI 3.14159 13 | 14 | template 15 | class transducer 16 | { 17 | public: 18 | struct transducer_element 19 | { 20 | btVector3 position; 21 | btVector3 direction; 22 | }; 23 | 24 | transducer(const float frequency, const units::length::centimeter_t radius, units::length::millimeter_t transducer_element_separation, 25 | const btVector3 & position, const std::array & angles) : 26 | frequency(frequency), 27 | radius(radius), 28 | position(position), 29 | angles(angles), 30 | transducer_element_separation(transducer_element_separation) 31 | { 32 | using namespace units::angle; 33 | using namespace units::literals; 34 | 35 | assert(transducer_element_separation * transducer_elements < M_PI * radius); 36 | 37 | radian_t x_angle { angles[0] }; 38 | radian_t y_angle { angles[1] }; 39 | radian_t z_angle { angles[2] }; 40 | 41 | auto amp = transducer_element_separation / radius; 42 | const radian_t amplitude { amp.to() }; // angle covered by a single TE 43 | const radian_t angle_center_of_element { amplitude / 2.0f }; 44 | 45 | radian_t angle = -(amplitude * transducer_elements / 2) + angle_center_of_element; 46 | 47 | for (size_t t = 0; t < transducer_elements; t++) 48 | { 49 | elements[t] = transducer_element 50 | { 51 | position + radius.to() * btVector3 ( std::sin(angle.to()), std::cos(angle.to()), 0 ).rotate(btVector3(0,0,1), z_angle.to()) 52 | .rotate(btVector3(1,0,0), x_angle.to()) 53 | .rotate(btVector3(0,1,0), y_angle.to()), // position 54 | btVector3 ( std::sin(angle.to()), std::cos(angle.to()), 0 ).rotate(btVector3(0,0,1), z_angle.to()) 55 | .rotate(btVector3(1,0,0), x_angle.to()) 56 | .rotate(btVector3(0,1,0), y_angle.to()) // direction 57 | }; 58 | 59 | angle = angle + amplitude; 60 | } 61 | 62 | } 63 | 64 | transducer_element element(size_t i) const 65 | { 66 | return elements.at(i); 67 | } 68 | 69 | void print(bool direction) const 70 | { 71 | auto print_vec = [](const auto & v) 72 | { 73 | std::cout << v.x() << "," << v.z() << std::endl; 74 | }; 75 | 76 | for (auto & element : elements) 77 | { 78 | print_vec(direction? element.direction : element.position); 79 | } 80 | } 81 | 82 | void update() 83 | { 84 | using namespace units::angle; 85 | using namespace units::literals; 86 | 87 | radian_t x_angle { angles[0] }; 88 | radian_t y_angle { angles[1] }; 89 | radian_t z_angle { angles[2] }; 90 | 91 | auto amp = transducer_element_separation / radius; 92 | const radian_t amplitude { amp.to() }; // angle covered by a single TE 93 | const radian_t angle_center_of_element { amplitude / 2.0f }; 94 | 95 | radian_t angle = -(amplitude * transducer_elements / 2) + angle_center_of_element; 96 | 97 | for (size_t t = 0; t < transducer_elements; t++) 98 | { 99 | /*elements[t] = transducer_element 100 | { 101 | position + radius.to() * btVector3 ( std::sin(angle.to()), std::cos(angle.to()), 0 ).rotate(btVector3(0,0,1), z_angle.to()) 102 | .rotate(btVector3(1,0,0), x_angle.to()) 103 | .rotate(btVector3(0,1,0), y_angle.to()), // position 104 | btVector3 ( std::sin(angle.to()), std::cos(angle.to()), 0 ).rotate(btVector3(0,0,1), z_angle.to()) 105 | .rotate(btVector3(1,0,0), x_angle.to()) 106 | .rotate(btVector3(0,1,0), y_angle.to()) // direction 107 | };*/ 108 | elements[t].position = position + radius.to() * btVector3 ( std::sin(angle.to()), std::cos(angle.to()), 0 ).rotate(btVector3(0,0,1), z_angle.to()) 109 | .rotate(btVector3(1,0,0), x_angle.to()) 110 | .rotate(btVector3(0,1,0), y_angle.to()); 111 | 112 | elements[t].direction = btVector3 ( std::sin(angle.to()), std::cos(angle.to()), 0 ).rotate(btVector3(0,0,1), z_angle.to()) 113 | .rotate(btVector3(1,0,0), x_angle.to()) 114 | .rotate(btVector3(0,1,0), y_angle.to()); 115 | 116 | angle = angle + amplitude; 117 | } 118 | } 119 | 120 | void setPosition (const btVector3 position) 121 | { 122 | this->position = position; 123 | } 124 | 125 | void setAngles(const std::array angles) 126 | { 127 | this->angles = angles; 128 | } 129 | btVector3 getPosition () 130 | { 131 | return position; 132 | } 133 | 134 | const float frequency; 135 | 136 | btVector3 position, direction; 137 | const std::array angles; 138 | 139 | private: 140 | const units::length::centimeter_t radius; 141 | const units::length::millimeter_t transducer_element_separation; 142 | 143 | std::array elements; 144 | }; 145 | 146 | #endif // TRANSDUCER_H 147 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "scene.h" 2 | #include "volume.h" 3 | #include "psf.h" 4 | #include "rfimage.h" 5 | #include "transducer.h" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "inputmanager.h" 14 | #include 15 | #include 16 | 17 | using namespace units::literals; 18 | using namespace units::velocity; 19 | using namespace units::length; 20 | using namespace units::time; 21 | using namespace units::angle; 22 | 23 | constexpr meters_per_second_t speed_of_sound = 1500_m / 1_s; // [μm/μs], [m/s] 24 | constexpr float transducer_frequency = 4.5f; // [Mhz] 25 | constexpr millimeter_t axial_resolution = millimeter_t(1.45f / transducer_frequency); // [mm], the division can be deduced from Burger13 26 | constexpr size_t transducer_elements = 512; 27 | constexpr size_t samples_te = 5; //santi 28 | constexpr radian_t transducer_amplitude = 60_deg; 29 | constexpr centimeter_t transducer_radius = 3_cm; 30 | constexpr centimeter_t ultrasound_depth = 15_cm; // [15cm -> μm] 31 | constexpr microsecond_t max_travel_time = microsecond_t(ultrasound_depth / speed_of_sound); // [μs] 32 | 33 | constexpr unsigned int resolution = 145;//145; // [μm], from Burger13 34 | using psf_ = psf<7, 13, 7, resolution>; 35 | using volume_ = volume<256, resolution>; 36 | using rf_image_ = rf_image(), static_cast(axial_resolution.to()*1000.0f/*mm->μm*/)>; 37 | using transducer_ = transducer; 38 | 39 | 40 | 41 | 42 | int main(int argc, char** argv) 43 | { 44 | // std::unique_ptr inputManager1; 45 | 46 | if (argc != 2) 47 | { 48 | std::cout << "Incorrect argument list." << std::endl; 49 | return 0; 50 | } 51 | 52 | static const volume_ texture_volume; 53 | 54 | const psf_ psf { transducer_frequency, 0.05f, 0.2f, 0.1f }; 55 | 56 | rf_image_ rf_image { transducer_radius, transducer_amplitude }; 57 | 58 | nlohmann::json json; 59 | { 60 | std::ifstream infile { argv[1] }; 61 | 62 | json << infile; 63 | } 64 | 65 | const auto & t_pos = json.at("transducerPosition"); 66 | millimeter_t transducer_element_separation = transducer_amplitude.to() * transducer_radius / transducer_elements; 67 | 68 | const auto & t_dir = json.at("transducerAngles"); 69 | std::array transducer_angles = {degree_t((float)t_dir[0]), degree_t((float)t_dir[1]), degree_t((float)t_dir[2])}; 70 | 71 | transducer_ transducer(transducer_frequency, transducer_radius, transducer_element_separation, 72 | btVector3(t_pos[0], t_pos[1], t_pos[2]), transducer_angles); 73 | //btVector3(-17.0, 1.2, 6.45) // liver 74 | //btVector3(-19.5, 1.2, -0.45) // kidney 75 | 76 | std::cout << max_travel_time << std::endl; 77 | 78 | try 79 | { 80 | scene scene { json,transducer }; 81 | scene.step(1000.0f); 82 | 83 | // Create InputManager 84 | //inputManager1 = std::make_unique(); 85 | //inputManager1->setTransducer(& transducer); 86 | 87 | std::chrono::time_point startTime, endTime; 88 | 89 | startTime = std::chrono::high_resolution_clock::now(); 90 | endTime = std::chrono::high_resolution_clock::now(); 91 | 92 | while(true) 93 | { 94 | // Calculate time elapsed from last loop 95 | float timeElapsed = std::chrono::duration_cast 96 | (endTime-startTime).count(); 97 | startTime = std::chrono::high_resolution_clock::now(); 98 | 99 | // Update Input 100 | //inputManager1->update(timeElapsed); 101 | 102 | rf_image.clear(); 103 | 104 | auto rays = scene.cast_rays(transducer); 105 | 106 | for (unsigned int ray_i = 0; ray_i < rays.size(); ray_i++) 107 | { 108 | const auto & ray = rays[ray_i]; 109 | for (unsigned int sample_i = 0; sample_i < samples_te; sample_i++) 110 | { 111 | const auto & sample = ray[sample_i]; 112 | for (auto & segment : sample) 113 | { 114 | const auto starting_micros = rf_image.micros_traveled(segment.distance_traveled /*mm -> μm*/); 115 | const auto distance = scene.distance(segment.from, segment.to); // [mm] 116 | auto steps = (unsigned int)(distance / axial_resolution); 117 | const auto delta_step = axial_resolution.to() * segment.direction; 118 | const auto time_step = rf_image.micros_traveled(axial_resolution); // [μs] 119 | 120 | auto point = segment.from; 121 | auto time_elapsed = starting_micros; 122 | auto intensity = segment.initial_intensity; 123 | 124 | for (unsigned int step = 0; step < steps && time_elapsed < max_travel_time; step++) 125 | { 126 | float scattering = texture_volume.get_scattering(segment.media.mu1, segment.media.mu0, segment.media.sigma, point.x(), point.y(), point.z()); 127 | 128 | rf_image.add_echo(ray_i, intensity * scattering, time_elapsed); 129 | 130 | // Step forward through the segment, decreasing intensity using Beer-Lambert's law 131 | point += delta_step; 132 | time_elapsed = time_elapsed + time_step; 133 | 134 | constexpr auto k = 1.0f; 135 | intensity *= std::exp(-segment.attenuation * axial_resolution.to()*0.01f * transducer_frequency * k); 136 | } 137 | 138 | // Add reflection term, i.e. intensity directly reflected back to the transducer. See Burger13, Eq. 10. 139 | rf_image.add_echo(ray_i, (segment.reflected_intensity)/samples_te, starting_micros + time_step * (steps-1)); 140 | 141 | } 142 | } 143 | 144 | } 145 | 146 | rf_image.convolve(psf); 147 | rf_image.envelope(); 148 | rf_image.postprocess(); 149 | rf_image.show(); 150 | 151 | endTime = std::chrono::high_resolution_clock::now(); 152 | } 153 | } 154 | catch (const std::exception & ex) 155 | { 156 | std::cout << "The program found an error and will terminate.\n" 157 | << "Reason:\n" 158 | << ex.what() << std::endl; 159 | } 160 | 161 | } 162 | -------------------------------------------------------------------------------- /src/objloader.h: -------------------------------------------------------------------------------- 1 | #ifndef OBJLOADER_H 2 | #define OBJLOADER_H 3 | 4 | #include "Bullet3Common/b3MinMax.h" 5 | 6 | // load_mesh_from_obj 7 | #include "wavefront/tiny_obj_loader.h" 8 | #include "Bullet3Common/b3AlignedObjectArray.h" 9 | #include 10 | #include 11 | 12 | // btgCreateGraphicsShapeFromWavefrontObj 13 | #include "btBulletDynamicsCommon.h" 14 | #include "OpenGL/GLInstanceGraphicsShape.h" 15 | 16 | GLInstanceGraphicsShape* btgCreateGraphicsShapeFromWavefrontObj(std::vector& shapes, bool flatShading=false) 17 | { 18 | 19 | b3AlignedObjectArray* vertices = new b3AlignedObjectArray; 20 | { 21 | b3AlignedObjectArray* indicesPtr = new b3AlignedObjectArray; 22 | 23 | for (int s = 0; s(shapes.size()); s++) 24 | { 25 | tinyobj::shape_t& shape = shapes[s]; 26 | int faceCount = shape.mesh.indices.size(); 27 | 28 | for (int f=0;fsize(); 32 | 33 | GLInstanceVertex vtx0; 34 | vtx0.xyzw[0] = shape.mesh.positions[shape.mesh.indices[f]*3+0]; 35 | vtx0.xyzw[1] = shape.mesh.positions[shape.mesh.indices[f]*3+1]; 36 | vtx0.xyzw[2] = shape.mesh.positions[shape.mesh.indices[f]*3+2]; 37 | vtx0.xyzw[3] = 0.f; 38 | 39 | if (shape.mesh.texcoords.size()) 40 | { 41 | vtx0.uv[0] = shape.mesh.texcoords[shape.mesh.indices[f]*2+0]; 42 | vtx0.uv[1] = shape.mesh.texcoords[shape.mesh.indices[f]*2+1]; 43 | } 44 | else 45 | { 46 | vtx0.uv[0] = 0.5; 47 | vtx0.uv[1] = 0.5; 48 | } 49 | 50 | GLInstanceVertex vtx1; 51 | vtx1.xyzw[0] = shape.mesh.positions[shape.mesh.indices[f+1]*3+0]; 52 | vtx1.xyzw[1] = shape.mesh.positions[shape.mesh.indices[f+1]*3+1]; 53 | vtx1.xyzw[2] = shape.mesh.positions[shape.mesh.indices[f+1]*3+2]; 54 | vtx1.xyzw[3]= 0.f; 55 | 56 | if (shape.mesh.texcoords.size()) 57 | { 58 | vtx1.uv[0] = shape.mesh.texcoords[shape.mesh.indices[f+1]*2+0]; 59 | vtx1.uv[1] = shape.mesh.texcoords[shape.mesh.indices[f+1]*2+1]; 60 | } 61 | else 62 | { 63 | vtx1.uv[0] = 0.5f; 64 | vtx1.uv[1] = 0.5f; 65 | } 66 | 67 | GLInstanceVertex vtx2; 68 | vtx2.xyzw[0] = shape.mesh.positions[shape.mesh.indices[f+2]*3+0]; 69 | vtx2.xyzw[1] = shape.mesh.positions[shape.mesh.indices[f+2]*3+1]; 70 | vtx2.xyzw[2] = shape.mesh.positions[shape.mesh.indices[f+2]*3+2]; 71 | vtx2.xyzw[3] = 0.f; 72 | if (shape.mesh.texcoords.size()) 73 | { 74 | vtx2.uv[0] = shape.mesh.texcoords[shape.mesh.indices[f+2]*2+0]; 75 | vtx2.uv[1] = shape.mesh.texcoords[shape.mesh.indices[f+2]*2+1]; 76 | } else 77 | { 78 | vtx2.uv[0] = 0.5; 79 | vtx2.uv[1] = 0.5; 80 | } 81 | 82 | btVector3 v0(vtx0.xyzw[0],vtx0.xyzw[1],vtx0.xyzw[2]); 83 | btVector3 v1(vtx1.xyzw[0],vtx1.xyzw[1],vtx1.xyzw[2]); 84 | btVector3 v2(vtx2.xyzw[0],vtx2.xyzw[1],vtx2.xyzw[2]); 85 | 86 | unsigned int maxIndex = 0; 87 | maxIndex = b3Max(maxIndex,shape.mesh.indices[f]*3+0); 88 | maxIndex = b3Max(maxIndex,shape.mesh.indices[f]*3+1); 89 | maxIndex = b3Max(maxIndex,shape.mesh.indices[f]*3+2); 90 | maxIndex = b3Max(maxIndex,shape.mesh.indices[f+1]*3+0); 91 | maxIndex = b3Max(maxIndex,shape.mesh.indices[f+1]*3+1); 92 | maxIndex = b3Max(maxIndex,shape.mesh.indices[f+1]*3+2); 93 | maxIndex = b3Max(maxIndex,shape.mesh.indices[f+2]*3+0); 94 | maxIndex = b3Max(maxIndex,shape.mesh.indices[f+2]*3+1); 95 | maxIndex = b3Max(maxIndex,shape.mesh.indices[f+2]*3+2); 96 | bool hasNormals = (shape.mesh.normals.size() && maxIndex SIMD_EPSILON) 104 | { 105 | normal.normalize(); 106 | } 107 | else 108 | { 109 | normal.setValue(0,0,0); 110 | } 111 | vtx0.normal[0] = normal[0]; 112 | vtx0.normal[1] = normal[1]; 113 | vtx0.normal[2] = normal[2]; 114 | vtx1.normal[0] = normal[0]; 115 | vtx1.normal[1] = normal[1]; 116 | vtx1.normal[2] = normal[2]; 117 | vtx2.normal[0] = normal[0]; 118 | vtx2.normal[1] = normal[1]; 119 | vtx2.normal[2] = normal[2]; 120 | } 121 | else 122 | { 123 | vtx0.normal[0] = shape.mesh.normals[shape.mesh.indices[f]*3+0]; 124 | vtx0.normal[1] = shape.mesh.normals[shape.mesh.indices[f]*3+1]; 125 | vtx0.normal[2] = shape.mesh.normals[shape.mesh.indices[f]*3+2]; //shape.mesh.indices[f+1]*3+0 126 | vtx1.normal[0] = shape.mesh.normals[shape.mesh.indices[f+1]*3+0]; 127 | vtx1.normal[1] = shape.mesh.normals[shape.mesh.indices[f+1]*3+1]; 128 | vtx1.normal[2] = shape.mesh.normals[shape.mesh.indices[f+1]*3+2]; 129 | vtx2.normal[0] = shape.mesh.normals[shape.mesh.indices[f+2]*3+0]; 130 | vtx2.normal[1] = shape.mesh.normals[shape.mesh.indices[f+2]*3+1]; 131 | vtx2.normal[2] = shape.mesh.normals[shape.mesh.indices[f+2]*3+2]; 132 | } 133 | vertices->push_back(vtx0); 134 | vertices->push_back(vtx1); 135 | vertices->push_back(vtx2); 136 | indicesPtr->push_back(vtxBaseIndex); 137 | indicesPtr->push_back(vtxBaseIndex+1); 138 | indicesPtr->push_back(vtxBaseIndex+2); 139 | } 140 | } 141 | 142 | 143 | GLInstanceGraphicsShape* gfxShape = new GLInstanceGraphicsShape; 144 | gfxShape->m_vertices = vertices; 145 | gfxShape->m_numvertices = vertices->size(); 146 | gfxShape->m_indices = indicesPtr; 147 | gfxShape->m_numIndices = indicesPtr->size(); 148 | for (int i=0;i<4;i++) 149 | gfxShape->m_scaling[i] = 1;//bake the scaling into the vertices 150 | return gfxShape; 151 | } 152 | } 153 | 154 | GLInstanceGraphicsShape* load_mesh_from_obj(const std::string & relativeFileName, const std::string & materialPrefixPath) 155 | { 156 | std::vector shapes; 157 | std::string err = tinyobj::LoadObj(shapes, relativeFileName.c_str(), materialPrefixPath.c_str()); 158 | 159 | GLInstanceGraphicsShape* gfxShape = btgCreateGraphicsShapeFromWavefrontObj(shapes); 160 | return gfxShape; 161 | } 162 | 163 | #endif // OBJLOADER_H 164 | -------------------------------------------------------------------------------- /src/rfimage.h: -------------------------------------------------------------------------------- 1 | #ifndef RFIMAGE_H 2 | #define RFIMAGE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "psf.h" 10 | 11 | /** 12 | * Radio-frequency image. 13 | * 14 | * Stores the resulting echoes of the ultrasound colliding with tissues. 15 | * Each column should gather information from a single transducer element. 16 | * The number of rows is calculated automatically according to maximum travel 17 | * time of the ultrasound pulse, psf's axial resolution, and average speed of sound. 18 | */ 19 | template 20 | class rf_image 21 | { 22 | public: 23 | rf_image(units::length::millimeter_t radius, units::angle::radian_t angle) : 24 | intensities(max_rows, columns, CV_32FC1), 25 | conv_axial_buffer(max_rows, columns, CV_32FC1), 26 | scan_converted(400, 500, CV_32FC1) 27 | 28 | { 29 | std::cout << "rf_image: " << max_rows << ", " << columns << std::endl; 30 | create_mapping(radius, angle, columns, max_rows); 31 | } 32 | 33 | void add_echo(const unsigned int column, const float echo, const units::time::microsecond_t micros_from_source) 34 | { 35 | const units::dimensionless::dimensionless_t row = micros_from_source / (axial_resolution_ / speed_of_sound_); 36 | if (row < max_rows) 37 | { 38 | intensities.at(row, column) += echo; 39 | } 40 | } 41 | 42 | // get the delta time that represents a pixel (row resolution in time) 43 | constexpr units::time::microsecond_t get_dt() const 44 | { 45 | return axial_resolution / speed_of_sound_; 46 | } 47 | 48 | constexpr units::time::microsecond_t micros_traveled(units::length::micrometer_t microm_from_source) const 49 | { 50 | return microm_from_source / speed_of_sound_; 51 | } 52 | 53 | // Transforms the rf image by doing a fast approximation of the envelope function 54 | void envelope() 55 | { 56 | // Travel through each column looking for concave peaks. 57 | // Then, recalculate the points between the peaks as the linear interpolation of the absolute values of those peaks. 58 | // This should work as a fast approximation of the hilbert transform over the rf signal. 59 | 60 | for (size_t column = 0; column < columns; column++) 61 | { 62 | bool ascending = intensities.at(0, column) < intensities.at(1, column); 63 | size_t last_peak_pos = 0; 64 | float last_peak = intensities.at(last_peak_pos, column); 65 | for (size_t i = 1; i < max_rows-1; i++) 66 | { 67 | if (intensities.at(i, column) < intensities.at(i+1, column)) 68 | { 69 | ascending = true; 70 | } 71 | else if (ascending) 72 | // if it was ascending and now descended, we found a concave point at i 73 | { 74 | ascending = false; 75 | const float new_peak = std::abs(intensities.at(i, column)); 76 | 77 | // lerp last_peak -> new_peak over last_peak_pos -> i (new_peak_pos) 78 | for (size_t j = last_peak_pos; j < i; j++) 79 | { 80 | const float alpha = (static_cast(j) - static_cast(last_peak_pos)) / 81 | (static_cast(i) - static_cast(last_peak_pos)); 82 | 83 | intensities.at(j, column) = last_peak * (1-alpha) + new_peak * alpha; 84 | } 85 | 86 | last_peak_pos = i; 87 | last_peak = new_peak; 88 | } 89 | } 90 | } 91 | } 92 | 93 | template 94 | void convolve(const psf_ & p) 95 | { 96 | // Convolve using only axial kernel and store in intermediate buffer 97 | for (int col = 0; col < intensities.cols; col++) //each column is a different TE 98 | { 99 | for (int row = p.get_axial_size(); row < intensities.rows - p.get_axial_size(); row++) // each row holds information from along a ray 100 | { 101 | float convolution = 0; 102 | for (int kernel_i = 0; kernel_i < p.get_axial_size(); kernel_i++) 103 | { 104 | convolution += intensities.at(row + kernel_i, col) * p.axial_kernel[kernel_i]; 105 | } 106 | conv_axial_buffer.at(row,col) = convolution; 107 | } 108 | } 109 | 110 | // Convolve intermediate buffer using lateral kernel 111 | for (int row = p.get_axial_size(); row < conv_axial_buffer.rows - p.get_axial_size(); row++) // each row holds information from along a ray 112 | { 113 | for (int col = p.get_lateral_size() / 2; col < conv_axial_buffer.cols - p.get_lateral_size(); col++) //each column is a different TE 114 | { 115 | float convolution = 0; 116 | for (int kernel_i = 0; kernel_i < p.get_lateral_size(); kernel_i++) 117 | { 118 | convolution += conv_axial_buffer.at(row, col + kernel_i) * p.lateral_kernel[kernel_i]; 119 | } 120 | intensities.at(row,col) = convolution; 121 | } 122 | } 123 | } 124 | 125 | void postprocess() 126 | { 127 | double min, max; 128 | cv::minMaxLoc(intensities, &min, &max); 129 | 130 | save("prelog.png"); 131 | /* 132 | for (size_t i = 0; i < max_rows * columns; i++) 133 | { 134 | intensities.at(i) = std::log10(intensities.at(i)+1)/std::log10(max+1); 135 | } 136 | */ 137 | // apply scan conversion using preprocessed mapping 138 | constexpr float invalid_color = 0.0f; 139 | cv::remap(intensities, scan_converted, map_y, map_x, CV_INTER_LINEAR, cv::BORDER_CONSTANT, cv::Scalar(invalid_color)); 140 | } 141 | 142 | void save(const std::string & filename) const 143 | { 144 | cv::Mat out; 145 | //hay que convertirla porque imwrite trabaja con enteros del 0-255. scan_convert es una matriz de flotantes de 0-1 146 | scan_converted.convertTo(out, CV_8U, 255.0); 147 | cv::imwrite(filename, out); 148 | } 149 | 150 | void show() const 151 | { 152 | cv::namedWindow("Scan Converted", cv::WINDOW_AUTOSIZE ); 153 | cv::imshow("Scan Converted", scan_converted ); 154 | save("/home/santiago/Proyectos/burger/burgercpp/images/mattausch.jpg"); 155 | // cv::imshow("Scan Converted", intensities ); 156 | 157 | cv::waitKey(0); 158 | 159 | } 160 | 161 | void clear() 162 | { 163 | intensities.setTo(0.0f); 164 | } 165 | 166 | void print(size_t column) const 167 | { 168 | for (size_t i = 0; i < max_rows; i++) 169 | { 170 | std::cout << intensities.at(i, column) << ", "; 171 | } 172 | std::cout << std::endl; 173 | } 174 | 175 | private: 176 | // Create constexpr variables to use type operations later 177 | static constexpr units::velocity::meters_per_second_t speed_of_sound_ = units::velocity::meters_per_second_t(speed_of_sound); 178 | static constexpr units::length::micrometer_t axial_resolution_ = units::length::micrometer_t(axial_resolution); 179 | 180 | static constexpr unsigned int max_rows = (speed_of_sound * max_travel_time) / axial_resolution; 181 | 182 | // fills map_x and map_y 183 | void create_mapping(units::length::millimeter_t radius, units::angle::radian_t total_angle, unsigned int rf_width, unsigned int rf_height) 184 | { 185 | // ratio to convert from mm to px 186 | float ratio = (max_travel_time * speed_of_sound * 0.001f + radius.to() - radius.to() * std::cos(total_angle.to()/2.0)) / scan_converted.rows; 187 | 188 | // distance to transducer center going from the edge of the scan_converted image 189 | units::length::millimeter_t shift_y = radius * std::cos(total_angle.to() / 2.0f); 190 | 191 | // horizontal center of the image 192 | float half_width = (float)scan_converted.cols / 2.0f; 193 | 194 | map_x.create(scan_converted.size(), CV_32FC1); 195 | map_y.create(scan_converted.size(), CV_32FC1); 196 | 197 | for( int j = 0; j < scan_converted.cols; j++ ) 198 | { 199 | for( int i = 0; i < scan_converted.rows; i++ ) 200 | { 201 | float fi = static_cast(i)+shift_y.to()/ratio; 202 | float fj = static_cast(j)-half_width; 203 | 204 | // distance from the point i,j to the center of the transducer, located in -shift_y,half_width 205 | float r = std::sqrt(std::pow(fi,2.0f) + std::pow(fj,2.0f)); 206 | 207 | // angle of the vector (i+shift_y, j-half_width) against the half center line 208 | units::angle::radian_t angle = units::angle::radian_t(std::atan2(fj, fi)); 209 | 210 | // Invalid values are OK here. cv::remap checks for them and assigns them a special color. 211 | map_x.at(i,j) = (r*ratio-radius.to())/(max_travel_time*speed_of_sound*0.001f) * (float)rf_height; 212 | map_y.at(i,j) = ((angle - (-total_angle/2)) / (total_angle)) * (float)rf_width; 213 | } 214 | } 215 | } 216 | 217 | cv::Mat intensities; 218 | cv::Mat scan_converted; 219 | cv::Mat conv_axial_buffer; 220 | float * usFrame = nullptr; 221 | 222 | // Mapping used for scan conversion 223 | cv::Mat map_x, map_y; 224 | }; 225 | 226 | // http://stackoverflow.com/a/22414046 227 | template 228 | constexpr units::velocity::meters_per_second_t rf_image::speed_of_sound_; 229 | 230 | template 231 | constexpr units::length::micrometer_t rf_image::axial_resolution_; 232 | 233 | #endif // RFIMAGE_H 234 | -------------------------------------------------------------------------------- /src/ray.cpp: -------------------------------------------------------------------------------- 1 | #include "ray.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "mesh.h" 8 | 9 | using namespace ray_physics; 10 | 11 | ray_physics::hit_result ray_physics::hit_boundary(const ray & r, const btVector3 & hit_point, const btVector3 & surface_normal, const mesh & collided_mesh) 12 | { 13 | // TODO: this logic can probably be simpler 14 | const material * material_after_vascularities = nullptr; 15 | const auto & material_after_collision = [&r, &collided_mesh, &material_after_vascularities]() -> const material & 16 | { 17 | if (r.media_outside) // if we are in a vessel 18 | { 19 | if (collided_mesh.is_vascular) // and we collided against a vessel (assuming same vessel, so we're getting out of it) 20 | { 21 | material_after_vascularities = nullptr; 22 | return *r.media_outside; // we are going back to the stored media 23 | } 24 | else // we are still inside the vessel but went out of the surrounding organ 25 | { 26 | // update the surrounding tissue 27 | material_after_vascularities = r.media_outside == &collided_mesh.material_inside ? &collided_mesh.material_outside : &collided_mesh.material_inside; 28 | 29 | // but we remain in the same media, i.e. the vessel 30 | return r.media; 31 | } 32 | } 33 | else // we are not in a vessel 34 | { 35 | if (collided_mesh.is_vascular) // and we collided with a vessel 36 | { 37 | // update the surrounding tissue 38 | material_after_vascularities = &r.media; // we will come back to this tissue after getting out of the vessel 39 | return collided_mesh.material_inside; 40 | } 41 | else // and we collided with a regular organ 42 | { 43 | material_after_vascularities = nullptr; 44 | return &r.media == &collided_mesh.material_inside ? collided_mesh.material_outside : collided_mesh.material_inside; 45 | } 46 | } 47 | }(); 48 | //power-cosine distribution random normal 49 | float random_angle = power_cosine_variate(material_after_collision.shininess); // par: s shininess : ( 0 = diffuse; inf = specular) 50 | btVector3 random_normal = random_unit_vector(surface_normal, random_angle); 51 | // 52 | //santi: reemplazo la normal de la superficie por la nueva normal random 53 | btScalar incidence_angle = r.direction.dot(-random_normal); // cos theta_1 54 | if (incidence_angle < 0) 55 | { 56 | incidence_angle = r.direction.dot(random_normal); 57 | } 58 | // santi: snell law 59 | const float refr_ratio = r.media.impedance / material_after_collision.impedance; 60 | 61 | float refraction_angle = 1 - refr_ratio*refr_ratio * (1 - incidence_angle*incidence_angle); 62 | const bool total_internal_reflection = refraction_angle < 0; 63 | refraction_angle = std::sqrt(refraction_angle); 64 | 65 | auto refraction_direction = snells_law(r.direction, random_normal, incidence_angle, refraction_angle, refr_ratio); 66 | refraction_direction = refraction_direction.normalized(); 67 | 68 | btVector3 reflection_direction = r.direction + 2*incidence_angle * random_normal; 69 | reflection_direction = reflection_direction.normalized(); 70 | 71 | const auto intensity_refl = total_internal_reflection ? 72 | r.intensity : 73 | reflection_intensity(r.intensity, 74 | r.media.impedance, incidence_angle, 75 | material_after_collision.impedance, refraction_angle); 76 | const auto intensity_refr = r.intensity - intensity_refl; 77 | 78 | // Eq. 10 in Burger13 79 | //const float back_to_transducer_intensity = reflected_intensity(r.intensity, incidence_angle, r.media, material_after_collision);//aca 80 | 81 | // Eq 8 in Mattausch 82 | const float back_to_transducer_intensity = reflected_intensity(r.direction, refraction_direction, reflection_direction, material_after_collision) * random_angle; 83 | 84 | //choose one of refraction or reflection ray. // santi 85 | std::random_device rd2; //Will be used to obtain a seed for the random number engine 86 | std::mt19937 generator2(rd2()); //Standard mersenne_twister_engine seeded with rd() 87 | std::uniform_real_distribution distribution2(0.0,1.0); 88 | float x = distribution2(generator2); 89 | float reflection_probabilily = intensity_refl / r.intensity; 90 | ray returned_ray; 91 | if (reflection_probabilily > x) 92 | returned_ray = { hit_point, reflection_direction, r.depth+1, r.media, r.media_outside, intensity_refl > ray::intensity_epsilon ? intensity_refl : 0.0f, r.frequency, r.distance_traveled, 0 }; 93 | else 94 | returned_ray = { hit_point, refraction_direction, r.depth+1, material_after_collision, material_after_vascularities, intensity_refr > ray::intensity_epsilon ? intensity_refr : 0.0f, r.frequency, r.distance_traveled, 0 }; 95 | 96 | return { back_to_transducer_intensity, returned_ray}; 97 | } 98 | 99 | void ray_physics::travel(ray & r, units::length::millimeter_t mm) 100 | { 101 | r.distance_traveled = r.distance_traveled + mm; 102 | r.intensity = r.intensity * std::exp(-r.media.attenuation*(mm.to()*0.01f)*r.frequency); // TODO: that 0.01 should be 0.1 103 | } 104 | 105 | bool ray_physics::should_travel(const ray & r) 106 | { 107 | return r.depth < r.max_depth; 108 | } 109 | 110 | float ray_physics::max_ray_length(const ray & r) 111 | { 112 | return 10.f /*<- cm to mm*/ * std::log(ray::intensity_epsilon/r.intensity) / -r.media.attenuation * r.frequency; 113 | } 114 | 115 | btVector3 ray_physics::snells_law(const btVector3 & ray_direction, const btVector3 & surface_normal, float incidence_angle, float refraction_angle, float refr_ratio) 116 | { 117 | // For more details, read https://en.wikipedia.org/wiki/Snell%27s_law#Vector_form 118 | const btVector3 & l = ray_direction; 119 | const btVector3 & n = surface_normal; 120 | const float c = incidence_angle; 121 | const float r = refr_ratio; 122 | 123 | return btVector3( r * l + (r*c - refraction_angle) * n ); 124 | } 125 | 126 | float ray_physics::reflection_intensity(const float intensity_in, const float media_1, const float incidence_angle, const float media_2, const float refracted_angle) 127 | { 128 | const auto && num = media_1 * incidence_angle - media_2 * refracted_angle; 129 | const auto && denom = media_1 * incidence_angle + media_2 * refracted_angle; 130 | 131 | return intensity_in * pow(num/denom, 2); 132 | } 133 | 134 | float ray_physics::reflected_intensity(const float ray_intensity, const float incidence_angle, const material & ray_media, const material & colliding_media) 135 | { 136 | // Eq. 10 in Burger13 137 | constexpr auto small_reflections_enhancement_factor = 0.2; 138 | 139 | constexpr auto custom_reflection_enhancement_factor = 0.05; // we made this up 140 | 141 | const auto specular_factor = std::pow(incidence_angle, colliding_media.specularity); 142 | const auto impedance_factor = std::pow(( (colliding_media.impedance - ray_media.impedance) 143 | /(colliding_media.impedance + ray_media.impedance)),2); 144 | const auto intensity = std::pow(ray_intensity, small_reflections_enhancement_factor); 145 | 146 | //std::cout << media_1.impedance << " " << media_2.impedance << std::endl; 147 | //std::cout << ray_intensity << ", " << specular_factor << " * " << impedance_factor << " * " << intensity << " = " << std::abs(specular_factor * impedance_factor * intensity) << std::endl; 148 | 149 | return std::abs(specular_factor * std::pow(impedance_factor, custom_reflection_enhancement_factor) * intensity); //A 150 | //return std::pow(std::abs(specular_factor * impedance_factor * ray_intensity), small_reflections_enhancement_factor); //B blito comentado 151 | //return std::abs(specular_factor * impedance_factor * intensity); //C santi 152 | //return std::abs(specular_factor * intensity); //D santi 153 | } 154 | float ray_physics::reflected_intensity(const btVector3 direction, const btVector3 refraction_direction, const btVector3 reflection_direction, const material & colliding_media) 155 | { 156 | // Eq. 8 in Mattausch 157 | float refraction_angle = direction.dot(refraction_direction); 158 | const auto refraction_factor = std::pow(refraction_angle, colliding_media.specularity); 159 | float reflection_angle = direction.dot(reflection_direction); 160 | const auto reflection_factor = std::pow(reflection_angle, colliding_media.specularity); 161 | // std::cout << "refraction factor : " << refraction_factor << " reflection factor : " << reflection_factor << std::endl; 162 | return std::max(refraction_factor,(float)0.0) + std::max(reflection_factor,(float)0.0); 163 | 164 | } 165 | 166 | // describes the determination of a random unit vector around v with given polar angle theta. 167 | btVector3 ray_physics::random_unit_vector(btVector3 v, float cos_theta) 168 | { 169 | bool flag = false; 170 | float px, py,p; 171 | do 172 | { 173 | //Generating a random point within a circle (uniformly) 174 | //https://programming.guide/random-point-within-circle.html 175 | std::random_device rd; //Will be used to obtain a seed for the random number engine 176 | std::mt19937 generator(rd()); //Standard mersenne_twister_engine seeded with rd() 177 | std::uniform_real_distribution distribution(0.0,1.0); 178 | double a = distribution(generator) * 2 * M_PI; 179 | double r = 0.5 * sqrt(distribution(generator)); 180 | //in Cartesian coordinates 181 | px = r * cos(a); 182 | py = r * sin(a); 183 | p = px * px + py * py; 184 | } while (! (p <= 0.25) ); 185 | float vx = v.getX(); 186 | float vy = v.getY(); 187 | float vz = v.getZ(); 188 | if ( abs(vx) > abs(vy) ) 189 | { 190 | vx = vy; 191 | vy = v.getX(); 192 | flag = true; 193 | } 194 | float b = 1 - vx * vx; 195 | float radicando = 1 - cos_theta * cos_theta; 196 | radicando = radicando / (p * b); 197 | float c = sqrt(radicando); 198 | px = px * c; 199 | py = py * c; 200 | float d = cos_theta - vx * px; 201 | float wx = vx * cos_theta - b * px; 202 | float wy = vy * d + vz * py; 203 | float wz = vz * d - vy * py; 204 | if (flag) 205 | { 206 | float aux = wy; 207 | wy = wx; 208 | wx = aux; 209 | } 210 | return btVector3 (wx,wy,wz); 211 | } 212 | 213 | float ray_physics::power_cosine_variate(int v) 214 | { 215 | //std::default_random_engine generator; 216 | std::random_device rd; //Will be used to obtain a seed for the random number engine 217 | std::mt19937 generator(rd()); //Standard mersenne_twister_engine seeded with rd() 218 | std::uniform_real_distribution distribution(0.0,1.0); 219 | double number = distribution(generator); 220 | // std::cout << "aleatorio : " << number << std::endl; 221 | int indice = v + 1; 222 | float exponente = (double)1.0 / indice; 223 | return pow(number, exponente); 224 | } 225 | 226 | -------------------------------------------------------------------------------- /src/scene.cpp: -------------------------------------------------------------------------------- 1 | #include "scene.h" 2 | 3 | #include "objloader.h" 4 | #include "ray.h" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | 13 | //template std::array, 256> scene::cast_rays<256>(); 14 | template std::array,5>, 512> scene::cast_rays<5,512>(transducer_ & transducer); 15 | 16 | scene::scene(const nlohmann::json & config, transducer_ & transducer) : 17 | transducer(transducer) 18 | { 19 | try 20 | { 21 | parse_config(config); 22 | } 23 | catch (const std::exception & ex) 24 | { 25 | throw std::runtime_error{ "Error while loading scene: " + std::string{ex.what()} }; 26 | } 27 | 28 | create_empty_world(); 29 | 30 | init(); 31 | } 32 | 33 | scene::~scene() 34 | { 35 | destroy_world(); 36 | } 37 | 38 | void scene::init() 39 | { 40 | for (auto & mesh : meshes) 41 | { 42 | const auto full_path = working_dir + mesh.filename; 43 | 44 | auto object = add_rigidbody_from_obj(full_path, mesh.deltas, scaling); 45 | 46 | object->setUserPointer(&mesh); 47 | } 48 | } 49 | 50 | template 51 | std::array,sample_count>, ray_count> scene::cast_rays(transducer_ & transducer) 52 | { 53 | using namespace ray_physics; 54 | 55 | //arreglo que guarda todos los segmentos de cada sample de cada rayo. 56 | std::array,sample_count>, ray_count> segments; 57 | 58 | unsigned int tests = 0; 59 | unsigned int total_collisions = 0; 60 | 61 | ///step the simulation 62 | if (m_dynamicsWorld) 63 | { 64 | const float ray_start_step { 0.02f }; 65 | 66 | for (auto & sample_vector : segments) 67 | { 68 | for (auto & segments_vector : sample_vector ) 69 | { 70 | segments_vector.reserve(ray::max_depth); 71 | } 72 | } 73 | //para cada rayo del trasductor 74 | //#pragma omp parallel for 75 | for (size_t ray_i = 0; ray_i < ray_count; ray_i++) 76 | { 77 | auto & samples_vector = segments[ray_i];//santi ver si quitar o no 78 | // modificar xq ahora tengo un arreglo samples. y cada sample tiene un array de segments. 79 | 80 | std::array samples; 81 | 82 | // Add first ray 83 | { 84 | ray first_ray 85 | { 86 | //transducer_pos + btVector3(0,0,ray_start_step * ray_i), 87 | transducer.element(ray_i).position, // from 88 | transducer.element(ray_i).direction, // initial direction 89 | 0, //depth // depth 90 | materials.at(starting_material), 91 | nullptr, //material outside 92 | initial_intensity / sample_count,//reparto equitativamente la intensidad entre cada uno de los path 93 | transducer.frequency, 94 | units::length::millimeter_t(0), // distance traveled 95 | 0 // previous ray 96 | }; 97 | for (size_t sample_i = 0; sample_i < sample_count; sample_i++) 98 | { 99 | samples.at(sample_i) = first_ray; 100 | } 101 | } 102 | for(size_t i = 0; i < ray::max_depth; i++) 103 | { 104 | //para cada sample: calculo nuevo segmento y lo guardo. actualizo sample. 105 | //#pragma omp parallel for 106 | for (size_t sample_i = 0; sample_i < sample_count; sample_i++) 107 | { 108 | // Pop a ray from the samples array and check if it collides 109 | auto & ray_ = samples.at(sample_i); 110 | if (!ray_.null) 111 | { 112 | float r_length = ray_physics::max_ray_length(ray_); 113 | auto to = ray_.from + enlarge(ray_.direction, r_length); 114 | 115 | btCollisionWorld::ClosestRayResultCallback closestResults(ray_.from + 0.1f * ray_.direction,to); 116 | 117 | m_dynamicsWorld->rayTest(ray_.from + 0.1f * ray_.direction,to,closestResults); 118 | tests++; 119 | 120 | if (closestResults.hasHit()) 121 | { 122 | // Substract ray intensity according to distance traveled 123 | auto distance_before_hit = ray_.distance_traveled; 124 | auto intensity_before_hit = ray_.intensity; 125 | 126 | const auto organ = static_cast(closestResults.m_collisionObject->getUserPointer()); 127 | assert(organ); 128 | 129 | //Simulacion de la penetracion de la onda sobre el tejido para generar interfaces mas difusas y no tan "plasticas" 130 | //cada material tiene un parametro thickness (s) que representa la media de los mm que puede penetrar la su superficie la onda. 131 | //Por medio de una variable aleatoria calculamos la penetracion de cada rayo sobre la superficie. var: q variable que sigue una probabilidad normal de media 0 y desvio s. 132 | std::random_device rd; //Will be used to obtain a seed for the random number engine 133 | std::mt19937 generator(rd()); //Standard mersenne_twister_engine seeded with rd() 134 | std::normal_distribution distribution(0.0,organ->material_inside.thickness); 135 | float q = std::abs(distribution(generator)); 136 | 137 | //modifico el hit_point para simular la penetracion del rayo - actualizar la intensidad restante que queda al penetrar la malla. 138 | 139 | auto inside_point = q * ray_.direction + closestResults.m_hitPointWorld; 140 | ray_physics::travel(ray_, distance_in_mm(ray_.from, inside_point));//aca 141 | std::cout << "inside : " << distance_in_mm(ray_.from, inside_point) << std::endl; 142 | std::cout << "original" << distance_in_mm(ray_.from, closestResults.m_hitPointWorld) << std::endl; 143 | // Calculate refraction and reflection directions and intensities 144 | 145 | auto result = ray_physics::hit_boundary(ray_, closestResults.m_hitPointWorld, closestResults.m_hitNormalWorld, *organ);//aca 146 | 147 | // Register collision creating a segment from the beggining of the ray to the collision point 148 | samples_vector[sample_i].emplace_back(segment{ray_.from, inside_point, ray_.direction, result.reflected_intensity, intensity_before_hit, ray_.media.attenuation, distance_before_hit, ray_.media});//aca 149 | 150 | // Spawn reflection or refraction rays 151 | if (result.returned.intensity > ray::intensity_epsilon) 152 | { 153 | result.returned.parent_collision = samples_vector[sample_i].size()-1; 154 | samples.at(sample_i) = result.returned; 155 | } 156 | else 157 | samples.at(sample_i).null = true; 158 | 159 | } 160 | else 161 | { 162 | // Ray did not reach another media, add a data point at its end. 163 | samples_vector[sample_i].emplace_back(segment{ray_.from, to, ray_.direction, 0.0f, ray_.intensity, ray_.media.attenuation, ray_.distance_traveled, ray_.media}); 164 | samples.at(sample_i).null = true; 165 | } 166 | } 167 | 168 | } 169 | } 170 | } 171 | 172 | for (auto & collision_vector : segments) 173 | { 174 | total_collisions += collision_vector.size(); 175 | } 176 | } 177 | 178 | const float fps = 1.0f/(float( clock() - frame_start ) / CLOCKS_PER_SEC); 179 | std::cout << fps << " " << tests << " " << total_collisions << std::endl; 180 | frame_start = clock(); 181 | 182 | return segments; 183 | } 184 | 185 | void scene::parse_config(const nlohmann::json & config) 186 | { 187 | using namespace units::angle; 188 | 189 | working_dir = config.find("workingDirectory") != config.end() ? config.at("workingDirectory") : ""; 190 | 191 | const auto & t_pos = config.at("transducerPosition"); 192 | transducer_pos = {t_pos[0], t_pos[1], t_pos[2]}; 193 | 194 | const auto & orig = config.at("origin"); 195 | origin = {orig[0], orig[1], orig[2]}; 196 | 197 | const auto & spac = config.at("spacing"); 198 | spacing = {spac[0], spac[1], spac[2]}; 199 | 200 | starting_material = config.at("startingMaterial"); 201 | 202 | scaling = config.at("scaling"); 203 | 204 | const auto & mats = config.at("materials"); 205 | if (mats.is_array()) 206 | { 207 | for (const auto & mat : mats) 208 | { 209 | materials[mat.at("name")] = 210 | { 211 | mat.at("impedance"), 212 | mat.at("attenuation"), 213 | mat.at("mu0"), 214 | mat.at("mu1"), 215 | mat.at("sigma"), 216 | mat.at("specularity"), 217 | mat.at("shininess"), 218 | mat.at("thickness") 219 | }; 220 | } 221 | } 222 | else 223 | { 224 | throw std::runtime_error("materials must be an array"); 225 | } 226 | 227 | const auto & meshes_ = config.at("meshes"); 228 | if (meshes_.is_array()) 229 | { 230 | for (const auto & mesh_ : meshes_) 231 | { 232 | const auto & deltas { mesh_.at("deltas") }; 233 | meshes.emplace_back(mesh{ 234 | mesh_.at("file"), 235 | mesh_.at("rigid"), 236 | mesh_.at("vascular"), 237 | {deltas[0], deltas[1], deltas[2]}, 238 | mesh_.at("outsideNormals"), 239 | materials.at(mesh_.at("material")), 240 | materials.at(mesh_.at("outsideMaterial"))}); 241 | } 242 | } 243 | else 244 | { 245 | throw std::runtime_error("meshes must be an array"); 246 | } 247 | } 248 | 249 | void scene::create_empty_world() 250 | { 251 | m_collisionConfiguration = std::make_unique(); 252 | 253 | m_dispatcher = std::make_unique(m_collisionConfiguration.get()); 254 | 255 | m_broadphase = std::make_unique(); 256 | 257 | m_solver = std::make_unique(); 258 | 259 | m_dynamicsWorld = std::make_unique(m_dispatcher.get(),m_broadphase.get(),m_solver.get(),m_collisionConfiguration.get()); 260 | 261 | m_dynamicsWorld->setGravity(btVector3(0,-10,0)); 262 | } 263 | 264 | void scene::destroy_world() 265 | { 266 | //delete collision shapes 267 | for (int j = 0; j < m_collisionShapes.size(); j++) 268 | { 269 | btCollisionShape* shape = m_collisionShapes[j]; 270 | delete shape; 271 | } 272 | m_collisionShapes.clear(); 273 | 274 | m_dynamicsWorld.reset(); 275 | m_solver.reset(); 276 | m_broadphase.reset(); 277 | m_dispatcher.reset(); 278 | m_collisionConfiguration.reset(); 279 | } 280 | 281 | units::length::millimeter_t scene::distance_in_mm(const btVector3 & v1, const btVector3 & v2) const 282 | { 283 | using namespace std; 284 | 285 | auto x_dist = abs(v1.getX() - v2.getX()) * spacing[0]; 286 | auto y_dist = abs(v1.getY() - v2.getY()) * spacing[1]; 287 | auto z_dist = abs(v1.getZ() - v2.getZ()) * spacing[2]; 288 | 289 | return units::length::millimeter_t(sqrt(pow(x_dist,2) + pow(y_dist,2) + pow(z_dist,2)) * 10); 290 | } 291 | 292 | btVector3 scene::enlarge(const btVector3 & versor, float mm) const 293 | { 294 | assert(versor.length2() < 1.1f); 295 | return mm/100.0f * btVector3 ( spacing[0] * versor.getX(), 296 | spacing[1] * versor.getY(), 297 | spacing[2] * versor.getZ() ); 298 | } 299 | 300 | btRigidBody * scene::add_rigidbody_from_obj(const std::string & fileName, std::array deltas, float scaling) 301 | { 302 | GLInstanceGraphicsShape* glmesh = load_mesh_from_obj(fileName, ""); 303 | printf("[INFO] Obj loaded: Extracted %d verticed from obj file [%s]\n", glmesh->m_numvertices, fileName.c_str()); 304 | 305 | const GLInstanceVertex& v = glmesh->m_vertices->at(0); 306 | btTriangleIndexVertexArray* tiva = new btTriangleIndexVertexArray(glmesh->m_numIndices / 3, &glmesh->m_indices->at(0), 3* sizeof(int), 307 | glmesh->m_numvertices, (btScalar*)(&(v.xyzw[0])), sizeof(GLInstanceVertex)); 308 | 309 | btBvhTriangleMeshShape* shape = new btBvhTriangleMeshShape(tiva, true); 310 | 311 | m_collisionShapes.push_back(shape); 312 | 313 | float _scaling[4] = {scaling,scaling,scaling,1}; 314 | 315 | btVector3 localScaling(_scaling[0],_scaling[1],_scaling[2]); 316 | shape->setLocalScaling(localScaling); 317 | 318 | btTransform startTransform; 319 | startTransform.setIdentity(); 320 | 321 | //std::array origin { -18, -22, -5 }; // origin for organs scene 322 | float pos[4] = {deltas[0]*_scaling[0]*_scaling[0],deltas[1]*_scaling[1]*_scaling[1],deltas[2]*_scaling[2]*_scaling[2],0}; 323 | btVector3 position(pos[0] + origin[0], pos[1] + origin[1], pos[2] + origin[2]); 324 | startTransform.setOrigin(position); 325 | 326 | btScalar mass(0.f); 327 | btVector3 localInertia(0, 0, 0); 328 | auto myMotionState = new btDefaultMotionState(startTransform); 329 | btRigidBody::btRigidBodyConstructionInfo cInfo(mass, myMotionState, shape, localInertia); 330 | auto body = new btRigidBody(cInfo); 331 | body->setUserIndex(-1); 332 | m_dynamicsWorld->addRigidBody(body); 333 | return body; 334 | } 335 | 336 | void scene::step(float delta_time) 337 | { 338 | m_dynamicsWorld->stepSimulation(delta_time); 339 | } 340 | 341 | // TODO: Is this equals to distance_in_mm? 342 | units::length::millimeter_t scene::distance(const btVector3 & from, const btVector3 & to) const 343 | { 344 | // TODO: Use scaling in this calculation 345 | return units::length::millimeter_t(from.distance(to)*10.0f); 346 | } 347 | 348 | 349 | -------------------------------------------------------------------------------- /src/wavefront/tiny_obj_loader.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2012-2013, Syoyo Fujita. 3 | // 4 | // Licensed under 2-clause BSD liecense. 5 | // 6 | 7 | // Erwin Coumans: improved performance, especially in debug mode on Visual Studio (25sec -> 4sec) 8 | // 9 | // version 0.9.5: Parse multiple group name. 10 | // Add support of specifying the base path to load material file. 11 | // version 0.9.4: Initial suupport of group tag(g) 12 | // version 0.9.3: Fix parsing triple 'x/y/z' 13 | // version 0.9.2: Add more .mtl load support 14 | // version 0.9.1: Add initial .mtl load support 15 | // version 0.9.0: Initial 16 | // 17 | 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "tiny_obj_loader.h" 30 | 31 | namespace tinyobj { 32 | 33 | //See http://stackoverflow.com/questions/6089231/getting-std-ifstream-to-handle-lf-cr-and-crlf 34 | std::istream& safeGetline(std::istream& is, std::string& t) 35 | { 36 | t.clear(); 37 | 38 | // The characters in the stream are read one-by-one using a std::streambuf. 39 | // That is faster than reading them one-by-one using the std::istream. 40 | // Code that uses streambuf this way must be guarded by a sentry object. 41 | // The sentry object performs various tasks, 42 | // such as thread synchronization and updating the stream state. 43 | 44 | std::istream::sentry se(is, true); 45 | std::streambuf* sb = is.rdbuf(); 46 | 47 | for(;;) { 48 | int c = sb->sbumpc(); 49 | switch (c) { 50 | case '\n': 51 | return is; 52 | case '\r': 53 | if(sb->sgetc() == '\n') 54 | sb->sbumpc(); 55 | return is; 56 | case EOF: 57 | // Also handle the case when the last line has no line ending 58 | if(t.empty()) 59 | is.setstate(std::ios::eofbit); 60 | return is; 61 | default: 62 | t += (char)c; 63 | } 64 | } 65 | } 66 | 67 | struct vertex_index { 68 | int v_idx, vt_idx, vn_idx, dummy; 69 | }; 70 | struct MyIndices 71 | { 72 | int m_offset; 73 | int m_numIndices; 74 | }; 75 | 76 | 77 | // for std::map 78 | static inline bool operator<(const vertex_index& a, const vertex_index& b) 79 | { 80 | if (a.v_idx != b.v_idx) return (a.v_idx < b.v_idx); 81 | if (a.vn_idx != b.vn_idx) return (a.vn_idx < b.vn_idx); 82 | if (a.vt_idx != b.vt_idx) return (a.vt_idx < b.vt_idx); 83 | 84 | return false; 85 | } 86 | 87 | 88 | static inline bool isSpace(const char c) { 89 | return (c == ' ') || (c == '\t'); 90 | } 91 | 92 | static inline bool isNewLine(const char c) { 93 | return (c == '\r') || (c == '\n') || (c == '\0'); 94 | } 95 | 96 | // Make index zero-base, and also support relative index. 97 | static inline int fixIndex(int idx, int n) 98 | { 99 | int i; 100 | 101 | if (idx > 0) { 102 | i = idx - 1; 103 | } else if (idx == 0) { 104 | i = 0; 105 | } else { // negative value = relative 106 | i = n + idx; 107 | } 108 | return i; 109 | } 110 | 111 | static inline std::string parseString(const char*& token) 112 | { 113 | std::string s; 114 | int b = strspn(token, " \t"); 115 | int e = strcspn(token, " \t\r"); 116 | s = std::string(&token[b], &token[e]); 117 | 118 | token += (e - b); 119 | return s; 120 | } 121 | 122 | static inline float parseFloat(const char*& token) 123 | { 124 | token += strspn(token, " \t"); 125 | float f = (float)atof(token); 126 | token += strcspn(token, " \t\r"); 127 | return f; 128 | } 129 | 130 | static inline void parseFloat2( 131 | float& x, float& y, 132 | const char*& token) 133 | { 134 | x = parseFloat(token); 135 | y = parseFloat(token); 136 | } 137 | 138 | static inline void parseFloat3( 139 | float& x, float& y, float& z, 140 | const char*& token) 141 | { 142 | x = parseFloat(token); 143 | y = parseFloat(token); 144 | z = parseFloat(token); 145 | } 146 | 147 | 148 | // Parse triples: i, i/j/k, i//k, i/j 149 | static vertex_index parseTriple( 150 | const char* &token, 151 | int vsize, 152 | int vnsize, 153 | int vtsize) 154 | { 155 | vertex_index vi; 156 | vi.vn_idx = -1; 157 | vi.vt_idx = -1; 158 | vi.v_idx= -1; 159 | 160 | vi.v_idx = fixIndex(atoi(token), vsize); 161 | token += strcspn(token, "/ \t\r"); 162 | if (token[0] != '/') { 163 | return vi; 164 | } 165 | token++; 166 | 167 | // i//k 168 | if (token[0] == '/') { 169 | token++; 170 | vi.vn_idx = fixIndex(atoi(token), vnsize); 171 | token += strcspn(token, "/ \t\r"); 172 | return vi; 173 | } 174 | 175 | // i/j/k or i/j 176 | vi.vt_idx = fixIndex(atoi(token), vtsize); 177 | token += strcspn(token, "/ \t\r"); 178 | if (token[0] != '/') { 179 | return vi; 180 | } 181 | 182 | // i/j/k 183 | token++; // skip '/' 184 | vi.vn_idx = fixIndex(atoi(token), vnsize); 185 | token += strcspn(token, "/ \t\r"); 186 | return vi; 187 | } 188 | 189 | 190 | static unsigned int 191 | updateVertex( 192 | std::map& vertexCache, 193 | std::vector& positions, 194 | std::vector& normals, 195 | std::vector& texcoords, 196 | const std::vector& in_positions, 197 | const std::vector& in_normals, 198 | const std::vector& in_texcoords, 199 | const vertex_index& i) 200 | { 201 | const std::map::iterator it = vertexCache.find(i); 202 | 203 | if (it != vertexCache.end()) { 204 | // found cache 205 | return it->second; 206 | } 207 | 208 | assert(static_cast(in_positions.size()) > (3*i.v_idx+2)); 209 | 210 | positions.push_back(in_positions[3*i.v_idx+0]); 211 | positions.push_back(in_positions[3*i.v_idx+1]); 212 | positions.push_back(in_positions[3*i.v_idx+2]); 213 | 214 | if (i.vn_idx >= 0) { 215 | normals.push_back(in_normals[3*i.vn_idx+0]); 216 | normals.push_back(in_normals[3*i.vn_idx+1]); 217 | normals.push_back(in_normals[3*i.vn_idx+2]); 218 | } 219 | 220 | if (i.vt_idx >= 0) { 221 | texcoords.push_back(in_texcoords[2*i.vt_idx+0]); 222 | texcoords.push_back(in_texcoords[2*i.vt_idx+1]); 223 | } 224 | 225 | unsigned int idx = positions.size() / 3 - 1; 226 | vertexCache[i] = idx; 227 | 228 | return idx; 229 | } 230 | 231 | 232 | static bool 233 | exportFaceGroupToShape( 234 | shape_t& shape, 235 | const std::vector& in_positions, 236 | const std::vector& in_normals, 237 | const std::vector& in_texcoords, 238 | const std::vector& faceGroup, 239 | const material_t material, 240 | const std::string name, 241 | std::vector& allIndices 242 | ) 243 | { 244 | if (faceGroup.empty()) { 245 | return false; 246 | } 247 | 248 | // Flattened version of vertex data 249 | std::vector positions; 250 | std::vector normals; 251 | std::vector texcoords; 252 | std::map vertexCache; 253 | std::vector indices; 254 | 255 | // Flatten vertices and indices 256 | for (size_t i = 0; i < faceGroup.size(); i++) 257 | { 258 | 259 | const MyIndices& face = faceGroup[i]; 260 | 261 | vertex_index i0 = allIndices[face.m_offset]; 262 | vertex_index i1; 263 | i1.vn_idx = -1; 264 | i1.vt_idx = -1; 265 | i1.v_idx= -1; 266 | vertex_index i2 = allIndices[face.m_offset+1]; 267 | 268 | size_t npolys = face.m_numIndices;//.size(); 269 | 270 | 271 | { 272 | // Polygon -> triangle fan conversion 273 | for (size_t k = 2; k < npolys; k++) 274 | { 275 | i1 = i2; 276 | i2 = allIndices[face.m_offset+k]; 277 | 278 | unsigned int v0 = updateVertex(vertexCache, positions, normals, texcoords, in_positions, in_normals, in_texcoords, i0); 279 | unsigned int v1 = updateVertex(vertexCache, positions, normals, texcoords, in_positions, in_normals, in_texcoords, i1); 280 | unsigned int v2 = updateVertex(vertexCache, positions, normals, texcoords, in_positions, in_normals, in_texcoords, i2); 281 | 282 | indices.push_back(v0); 283 | indices.push_back(v1); 284 | indices.push_back(v2); 285 | } 286 | } 287 | 288 | } 289 | 290 | // 291 | // Construct shape. 292 | // 293 | shape.name = name; 294 | shape.mesh.positions.swap(positions); 295 | shape.mesh.normals.swap(normals); 296 | shape.mesh.texcoords.swap(texcoords); 297 | shape.mesh.indices.swap(indices); 298 | 299 | shape.material = material; 300 | 301 | return true; 302 | 303 | } 304 | 305 | 306 | 307 | void InitMaterial(material_t& material) { 308 | material.name = ""; 309 | material.ambient_texname = ""; 310 | material.diffuse_texname = ""; 311 | material.specular_texname = ""; 312 | material.normal_texname = ""; 313 | for (int i = 0; i < 3; i ++) { 314 | material.ambient[i] = 0.f; 315 | material.diffuse[i] = 0.f; 316 | material.specular[i] = 0.f; 317 | material.transmittance[i] = 0.f; 318 | material.emission[i] = 0.f; 319 | } 320 | material.shininess = 1.f; 321 | } 322 | 323 | std::string LoadMtl ( 324 | std::map& material_map, 325 | const char* filename, 326 | const char* mtl_basepath) 327 | { 328 | material_map.clear(); 329 | std::stringstream err; 330 | 331 | std::string filepath; 332 | 333 | if (mtl_basepath) { 334 | filepath = std::string(mtl_basepath) + std::string(filename); 335 | } else { 336 | filepath = std::string(filename); 337 | } 338 | 339 | std::ifstream ifs(filepath.c_str()); 340 | if (!ifs) { 341 | err << "Cannot open file [" << filepath << "]" << std::endl; 342 | return err.str(); 343 | } 344 | 345 | material_t material; 346 | 347 | int maxchars = 8192; // Alloc enough size. 348 | std::vector buf(maxchars); // Alloc enough size. 349 | while (ifs.peek() != -1) { 350 | 351 | std::string linebuf; 352 | safeGetline(ifs,linebuf); 353 | 354 | 355 | 356 | // Trim newline '\r\n' or '\r' 357 | if (linebuf.size() > 0) { 358 | if (linebuf[linebuf.size()-1] == '\n') linebuf.erase(linebuf.size()-1); 359 | } 360 | if (linebuf.size() > 0) { 361 | if (linebuf[linebuf.size()-1] == '\n') linebuf.erase(linebuf.size()-1); 362 | } 363 | 364 | // Skip if empty line. 365 | if (linebuf.empty()) { 366 | continue; 367 | } 368 | 369 | linebuf = linebuf.substr(0, linebuf.find_last_not_of(" \t") + 1); 370 | 371 | // Skip leading space. 372 | const char* token = linebuf.c_str(); 373 | token += strspn(token, " \t"); 374 | 375 | assert(token); 376 | if (token[0] == '\0') continue; // empty line 377 | 378 | if (token[0] == '#') continue; // comment line 379 | 380 | // new mtl 381 | if ((0 == strncmp(token, "newmtl", 6)) && isSpace((token[6]))) { 382 | // flush previous material. 383 | material_map.insert(std::pair(material.name, material)); 384 | 385 | // initial temporary material 386 | InitMaterial(material); 387 | 388 | // set new mtl name 389 | char namebuf[4096]; 390 | token += 7; 391 | sscanf(token, "%s", namebuf); 392 | material.name = namebuf; 393 | continue; 394 | } 395 | 396 | // ambient 397 | if (token[0] == 'K' && token[1] == 'a' && isSpace((token[2]))) { 398 | token += 2; 399 | float r, g, b; 400 | parseFloat3(r, g, b, token); 401 | material.ambient[0] = r; 402 | material.ambient[1] = g; 403 | material.ambient[2] = b; 404 | continue; 405 | } 406 | 407 | // diffuse 408 | if (token[0] == 'K' && token[1] == 'd' && isSpace((token[2]))) { 409 | token += 2; 410 | float r, g, b; 411 | parseFloat3(r, g, b, token); 412 | material.diffuse[0] = r; 413 | material.diffuse[1] = g; 414 | material.diffuse[2] = b; 415 | continue; 416 | } 417 | 418 | // specular 419 | if (token[0] == 'K' && token[1] == 's' && isSpace((token[2]))) { 420 | token += 2; 421 | float r, g, b; 422 | parseFloat3(r, g, b, token); 423 | material.specular[0] = r; 424 | material.specular[1] = g; 425 | material.specular[2] = b; 426 | continue; 427 | } 428 | 429 | // specular 430 | if (token[0] == 'K' && token[1] == 't' && isSpace((token[2]))) { 431 | token += 2; 432 | float r, g, b; 433 | parseFloat3(r, g, b, token); 434 | material.specular[0] = r; 435 | material.specular[1] = g; 436 | material.specular[2] = b; 437 | continue; 438 | } 439 | 440 | // emission 441 | if(token[0] == 'K' && token[1] == 'e' && isSpace(token[2])) { 442 | token += 2; 443 | float r, g, b; 444 | parseFloat3(r, g, b, token); 445 | material.emission[0] = r; 446 | material.emission[1] = g; 447 | material.emission[2] = b; 448 | continue; 449 | } 450 | 451 | // shininess 452 | if(token[0] == 'N' && token[1] == 's' && isSpace(token[2])) { 453 | token += 2; 454 | material.shininess = parseFloat(token); 455 | continue; 456 | } 457 | 458 | // ambient texture 459 | if ((0 == strncmp(token, "map_Ka", 6)) && isSpace(token[6])) { 460 | token += 7; 461 | material.ambient_texname = token; 462 | continue; 463 | } 464 | 465 | // diffuse texture 466 | if ((0 == strncmp(token, "map_Kd", 6)) && isSpace(token[6])) { 467 | token += 7; 468 | material.diffuse_texname = token; 469 | continue; 470 | } 471 | 472 | // specular texture 473 | if ((0 == strncmp(token, "map_Ks", 6)) && isSpace(token[6])) { 474 | token += 7; 475 | material.specular_texname = token; 476 | continue; 477 | } 478 | 479 | // normal texture 480 | if ((0 == strncmp(token, "map_Ns", 6)) && isSpace(token[6])) { 481 | token += 7; 482 | material.normal_texname = token; 483 | continue; 484 | } 485 | 486 | // unknown parameter 487 | const char* _space = strchr(token, ' '); 488 | if(!_space) { 489 | _space = strchr(token, '\t'); 490 | } 491 | if(_space) { 492 | int len = _space - token; 493 | std::string key(token, len); 494 | std::string value = _space + 1; 495 | material.unknown_parameter.insert(std::pair(key, value)); 496 | } 497 | } 498 | // flush last material. 499 | material_map.insert(std::pair(material.name, material)); 500 | 501 | return err.str(); 502 | } 503 | 504 | std::string 505 | LoadObj( 506 | std::vector& shapes, 507 | const char* filename, 508 | const char* mtl_basepath) 509 | { 510 | 511 | shapes.resize(0); 512 | std::vector allIndices; 513 | allIndices.reserve(1024*1024); 514 | 515 | MyIndices face; 516 | 517 | std::stringstream err; 518 | 519 | std::ifstream ifs(filename); 520 | if (!ifs) { 521 | err << "Cannot open file [" << filename << "]" << std::endl; 522 | return err.str(); 523 | } 524 | 525 | std::vector v; 526 | v.reserve(1024*1024); 527 | std::vector vn; 528 | vn.reserve(1024*1024); 529 | std::vector vt; 530 | vt.reserve(1024*1024); 531 | //std::vector > faceGroup; 532 | std::vector faceGroup; 533 | faceGroup.reserve(1024*1024); 534 | std::string name; 535 | 536 | // material 537 | std::map material_map; 538 | material_t material; 539 | 540 | int maxchars = 8192; // Alloc enough size. 541 | std::vector buf(maxchars); // Alloc enough size. 542 | while (ifs.peek() != -1) { 543 | 544 | std::string linebuf; 545 | safeGetline(ifs,linebuf); 546 | 547 | // Trim newline '\r\n' or '\r' 548 | if (linebuf.size() > 0) { 549 | if (linebuf[linebuf.size()-1] == '\n') linebuf.erase(linebuf.size()-1); 550 | } 551 | if (linebuf.size() > 0) { 552 | if (linebuf[linebuf.size()-1] == '\n') linebuf.erase(linebuf.size()-1); 553 | } 554 | 555 | // Skip if empty line. 556 | if (linebuf.empty()) { 557 | continue; 558 | } 559 | 560 | // Skip leading space. 561 | const char* token = linebuf.c_str(); 562 | token += strspn(token, " \t"); 563 | 564 | assert(token); 565 | if (token[0] == '\0') continue; // empty line 566 | 567 | if (token[0] == '#') continue; // comment line 568 | 569 | // vertex 570 | if (token[0] == 'v' && isSpace((token[1]))) { 571 | token += 2; 572 | float x, y, z; 573 | parseFloat3(x, y, z, token); 574 | v.push_back(x); 575 | v.push_back(y); 576 | v.push_back(z); 577 | continue; 578 | } 579 | 580 | // normal 581 | if (token[0] == 'v' && token[1] == 'n' && isSpace((token[2]))) { 582 | token += 3; 583 | float x, y, z; 584 | parseFloat3(x, y, z, token); 585 | vn.push_back(x); 586 | vn.push_back(y); 587 | vn.push_back(z); 588 | continue; 589 | } 590 | 591 | // texcoord 592 | if (token[0] == 'v' && token[1] == 't' && isSpace((token[2]))) { 593 | token += 3; 594 | float x, y; 595 | parseFloat2(x, y, token); 596 | vt.push_back(x); 597 | vt.push_back(y); 598 | continue; 599 | } 600 | 601 | // face 602 | if (token[0] == 'f' && isSpace((token[1]))) { 603 | token += 2; 604 | token += strspn(token, " \t"); 605 | 606 | face.m_offset = allIndices.size(); 607 | face.m_numIndices = 0; 608 | 609 | while (!isNewLine(token[0])) { 610 | vertex_index vi = parseTriple(token, v.size() / 3, vn.size() / 3, vt.size() / 2); 611 | allIndices.push_back(vi); 612 | face.m_numIndices++; 613 | int n = strspn(token, " \t\r"); 614 | token += n; 615 | } 616 | 617 | faceGroup.push_back(face); 618 | 619 | continue; 620 | } 621 | 622 | // use mtl 623 | if ((0 == strncmp(token, "usemtl", 6)) && isSpace((token[6]))) { 624 | 625 | char namebuf[4096]; 626 | token += 7; 627 | sscanf(token, "%s", namebuf); 628 | 629 | if (material_map.find(namebuf) != material_map.end()) { 630 | material = material_map[namebuf]; 631 | } else { 632 | // { error!! material not found } 633 | InitMaterial(material); 634 | } 635 | continue; 636 | 637 | } 638 | 639 | // load mtl 640 | if ((0 == strncmp(token, "mtllib", 6)) && isSpace((token[6]))) { 641 | char namebuf[4096]; 642 | token += 7; 643 | sscanf(token, "%s", namebuf); 644 | 645 | std::string err_mtl = LoadMtl(material_map, namebuf, mtl_basepath); 646 | if (!err_mtl.empty()) { 647 | //faceGroup.resize(0); // for safety 648 | //return err_mtl; 649 | } 650 | continue; 651 | } 652 | 653 | // group name 654 | if (token[0] == 'g' && isSpace((token[1]))) { 655 | 656 | // flush previous face group. 657 | shape_t shape; 658 | bool ret = exportFaceGroupToShape(shape, v, vn, vt, faceGroup, material, name,allIndices); 659 | if (ret) { 660 | shapes.push_back(shape); 661 | } 662 | 663 | faceGroup.resize(0); 664 | 665 | std::vector names; 666 | while (!isNewLine(token[0])) { 667 | std::string str = parseString(token); 668 | names.push_back(str); 669 | token += strspn(token, " \t\r"); // skip tag 670 | } 671 | 672 | assert(names.size() > 0); 673 | 674 | // names[0] must be 'g', so skipt 0th element. 675 | if (names.size() > 1) { 676 | name = names[1]; 677 | } else { 678 | name = ""; 679 | } 680 | 681 | continue; 682 | } 683 | 684 | // object name 685 | if (token[0] == 'o' && isSpace((token[1]))) { 686 | 687 | // flush previous face group. 688 | shape_t shape; 689 | bool ret = exportFaceGroupToShape(shape, v, vn, vt, faceGroup, material, name,allIndices); 690 | if (ret) { 691 | shapes.push_back(shape); 692 | } 693 | 694 | faceGroup.resize(0); 695 | 696 | // @todo { multiple object name? } 697 | char namebuf[4096]; 698 | token += 2; 699 | sscanf(token, "%s", namebuf); 700 | name = std::string(namebuf); 701 | 702 | 703 | continue; 704 | } 705 | 706 | // Ignore unknown command. 707 | } 708 | 709 | shape_t shape; 710 | bool ret = exportFaceGroupToShape(shape, v, vn, vt, faceGroup, material, name,allIndices); 711 | if (ret) { 712 | shapes.push_back(shape); 713 | } 714 | faceGroup.resize(0); // for safety 715 | 716 | return err.str(); 717 | } 718 | 719 | 720 | }; 721 | --------------------------------------------------------------------------------