├── 7_21_0.png ├── figures ├── 7_13_0.png ├── 7_13_1.png ├── 7_13_2.png └── 7_20_0.png ├── project-coral.png ├── CoralRugosity_Zhihao ├── res │ ├── PointsFShader.glsl │ ├── GraphFShader.glsl │ ├── GraphVShader.glsl │ ├── PointsVShader.glsl │ ├── barkFShader.glsl │ └── barkVShader.glsl ├── main.cpp ├── utility │ ├── tinyply.cpp │ ├── utils.h │ ├── kdtree.h │ └── tinyply.h ├── res.qrc ├── object_octree.cpp ├── mainwindow.h ├── CoralRugosity_Zhihao.pro ├── object.h ├── rugosityalgorithm.h ├── glwidget.h ├── rugosityalgorithm.cpp.J73680 ├── mainwindow.cpp ├── glwidget.cpp.y85456 ├── mainwindow.ui ├── object_Mesh.cpp ├── object.cpp ├── rugosityalgorithm.cpp ├── glwidget.cpp └── CoralRugosity_Zhihao.pro.user └── README.md /7_21_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RyuZhihao123/CoralRugosity3D-Zhihao/HEAD/7_21_0.png -------------------------------------------------------------------------------- /figures/7_13_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RyuZhihao123/CoralRugosity3D-Zhihao/HEAD/figures/7_13_0.png -------------------------------------------------------------------------------- /figures/7_13_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RyuZhihao123/CoralRugosity3D-Zhihao/HEAD/figures/7_13_1.png -------------------------------------------------------------------------------- /figures/7_13_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RyuZhihao123/CoralRugosity3D-Zhihao/HEAD/figures/7_13_2.png -------------------------------------------------------------------------------- /figures/7_20_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RyuZhihao123/CoralRugosity3D-Zhihao/HEAD/figures/7_20_0.png -------------------------------------------------------------------------------- /project-coral.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RyuZhihao123/CoralRugosity3D-Zhihao/HEAD/project-coral.png -------------------------------------------------------------------------------- /CoralRugosity_Zhihao/res/PointsFShader.glsl: -------------------------------------------------------------------------------- 1 | varying vec3 v_position; 2 | uniform vec3 u_color; 3 | 4 | void main() 5 | { 6 | gl_FragColor = vec4(u_color.r,u_color.g,u_color.b,1.0); 7 | } 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /CoralRugosity_Zhihao/res/GraphFShader.glsl: -------------------------------------------------------------------------------- 1 | #version 120 2 | uniform vec3 u_color; 3 | 4 | void main() 5 | { 6 | gl_FragColor = vec4(u_color.r,u_color.g,u_color.b,1.0); 7 | //gl_FragColor = vec4(1,1,0,1.0); 8 | } 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /CoralRugosity_Zhihao/main.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindow.h" 2 | #include 3 | 4 | int main(int argc, char *argv[]) 5 | { 6 | 7 | QApplication a(argc, argv); 8 | MainWindow w; 9 | w.show(); 10 | 11 | return a.exec(); 12 | } 13 | -------------------------------------------------------------------------------- /CoralRugosity_Zhihao/utility/tinyply.cpp: -------------------------------------------------------------------------------- 1 | // This file exists to create a nice static or shared library via cmake 2 | // but can otherwise be omitted if you prefer to compile tinyply 3 | // directly into your own project. 4 | #define TINYPLY_IMPLEMENTATION 5 | #include "3rdparty/tinyply.h" 6 | -------------------------------------------------------------------------------- /CoralRugosity_Zhihao/res/GraphVShader.glsl: -------------------------------------------------------------------------------- 1 | #version 120 2 | uniform mat4 mat_projection; 3 | uniform mat4 mat_view; 4 | uniform mat4 mat_model; 5 | 6 | attribute vec3 a_position; 7 | 8 | 9 | void main() 10 | { 11 | gl_Position = mat_projection* mat_view * mat_model * vec4(a_position,1.0); 12 | } 13 | -------------------------------------------------------------------------------- /CoralRugosity_Zhihao/res.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | res/barkFShader.glsl 4 | res/barkVShader.glsl 5 | res/GraphFShader.glsl 6 | res/GraphVShader.glsl 7 | res/PointsFShader.glsl 8 | res/PointsVShader.glsl 9 | 10 | 11 | -------------------------------------------------------------------------------- /CoralRugosity_Zhihao/res/PointsVShader.glsl: -------------------------------------------------------------------------------- 1 | #version 120 2 | uniform mat4 mat_projection; 3 | uniform mat4 mat_view; 4 | uniform mat4 mat_model; 5 | 6 | attribute vec3 a_position; 7 | varying vec3 v_position; 8 | 9 | 10 | void main() 11 | { 12 | gl_Position = mat_projection* mat_view * mat_model * vec4(a_position,1.0); 13 | 14 | v_position = vec3(mat_model*vec4(a_position,1.0)); 15 | } 16 | -------------------------------------------------------------------------------- /CoralRugosity_Zhihao/object_octree.cpp: -------------------------------------------------------------------------------- 1 | #include "object.h" 2 | 3 | 4 | void Object::BuildOctree() 5 | { 6 | 7 | } 8 | 9 | void Object::BuildDictionary() 10 | { 11 | for(int i=0; i(); 14 | } 15 | 16 | for(int i=0; i 5 | #include "glwidget.h" 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace Ui { 12 | class MainWindow; 13 | } 14 | 15 | class MainWindow : public QMainWindow 16 | { 17 | Q_OBJECT 18 | 19 | public: 20 | explicit MainWindow(QWidget *parent = 0); 21 | ~MainWindow(); 22 | 23 | GLWidget* m_glwidget; 24 | QChart * m_chart_crosssection; 25 | QChart * m_chart_height; 26 | protected: 27 | 28 | void resizeEvent(QResizeEvent* e); 29 | 30 | protected slots: 31 | void slot_updateChart(float minY, float maxY); 32 | void slot_updateStatus(); 33 | void slot_saveRugosity(); 34 | 35 | private: 36 | Ui::MainWindow *ui; 37 | }; 38 | 39 | #endif // MAINWINDOW_H 40 | -------------------------------------------------------------------------------- /CoralRugosity_Zhihao/CoralRugosity_Zhihao.pro: -------------------------------------------------------------------------------- 1 | #------------------------------------------------- 2 | # 3 | # Project created by QtCreator 2022-07-10T01:16:07 4 | # 5 | #------------------------------------------------- 6 | 7 | QT += core gui opengl charts 8 | 9 | LIBS += -lopengl32 10 | LIBS += -lglu32 11 | LIBS += -lglut 12 | 13 | greaterThan(QT_MAJOR_VERSION, 4): QT += widgets 14 | 15 | TARGET = CoralRugosity_Zhihao 16 | TEMPLATE = app 17 | 18 | # The following define makes your compiler emit warnings if you use 19 | # any feature of Qt which as been marked as deprecated (the exact warnings 20 | # depend on your compiler). Please consult the documentation of the 21 | # deprecated API in order to know how to port your code away from it. 22 | DEFINES += QT_DEPRECATED_WARNINGS 23 | 24 | # You can also make your code fail to compile if you use deprecated APIs. 25 | # In order to do so, uncomment the following line. 26 | # You can also select to disable deprecated APIs only up to a certain version of Qt. 27 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 28 | 29 | 30 | SOURCES += main.cpp\ 31 | mainwindow.cpp \ 32 | utility/tinyply.cpp \ 33 | glwidget.cpp \ 34 | rugosityalgorithm.cpp \ 35 | object_Mesh.cpp \ 36 | object_Octree.cpp 37 | 38 | HEADERS += mainwindow.h \ 39 | utility/happly.h \ 40 | utility/kdtree.h \ 41 | utility/tinyply.h \ 42 | utility/utils.h \ 43 | glwidget.h \ 44 | object.h \ 45 | rugosityalgorithm.h 46 | 47 | FORMS += mainwindow.ui 48 | 49 | RESOURCES += \ 50 | res.qrc 51 | -------------------------------------------------------------------------------- /CoralRugosity_Zhihao/res/barkFShader.glsl: -------------------------------------------------------------------------------- 1 | #version 120 2 | uniform vec3 viewPos; 3 | uniform int isTextured; 4 | varying vec3 v_position; 5 | varying vec3 v_normal; 6 | varying vec3 v_texCoord; 7 | 8 | uniform sampler2D texture; 9 | uniform bool isLighting; 10 | uniform bool isBark; 11 | uniform bool isShowSeg; 12 | 13 | vec3 CalLight(vec3 lightPos,vec3 lightColor,float ambient,float specular) 14 | { 15 | vec3 norm = normalize(v_normal); 16 | 17 | vec3 ambientLight = ambient*lightColor; 18 | 19 | vec3 lightDir = normalize(lightPos - v_position); 20 | 21 | float diffuse = max(dot(norm, lightDir),0.0); 22 | 23 | vec3 diffuseLight = diffuse*lightColor; 24 | 25 | vec3 viewDir = normalize(viewPos - v_position); 26 | vec3 reflectDir = reflect(-lightDir, norm); 27 | float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32); 28 | 29 | vec3 specularLight = specular * spec * lightColor; 30 | 31 | return ambientLight+diffuseLight+specularLight; 32 | } 33 | 34 | void main() 35 | { 36 | 37 | vec3 objectColor = vec3(0.564706,0.490196,0.443137); 38 | 39 | if( isTextured == 1) 40 | { 41 | gl_FragColor = texture2D(texture,vec2(v_texCoord)); 42 | return; 43 | } 44 | 45 | 46 | vec3 lColor1 = vec3(1.0,1.0,1.0); // 光照的颜色 47 | //vec3 lPos1 = vec3(0,10000.0,10000.0); // 光源的位置 48 | vec3 lPos1 = viewPos*100; // 光源的位置 49 | vec3 light = CalLight(lPos1,lColor1,0.2,0.0); 50 | 51 | vec4 color = vec4(light*objectColor,1.0); 52 | 53 | 54 | 55 | gl_FragColor = color; 56 | } 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CoralRugosity-Zhihao 2 | 3 | This is a software designed for coral rugosity computation (``HKUST VGD Group``), which is useful for the fundamental reseach on Marine Biology field or the like. 4 | 5 | 6 | I developed this program when I studied in HKUST (2022). 7 | 8 | - 📐 **Usage**: users can interact with 3D coral mesh models and freely specify the region of interest. Then, the system will automatically compute and showcase the corresponding rugosity and other relavent information. 9 | 10 | - 📁 **Source Codes**: please go to this folder [[Source codes]](https://github.com/RyuZhihao123/CoralRugosity/tree/main/CoralRugosity_Zhihao). 11 | 12 | - 📌 **Requirement**: This project is developed with ``C++ 11`` and ``OpenGL 4.0+`` (you can use ``Visual Studio 2019+`` or ``Qt5.8+`` to easily compile this project). 13 | 14 | - 🎞️ **Video Demos**: [Full Introducion Video (6min)](https://drive.google.com/file/d/1VCVGPRM6rvlVhphPzhBRmh937NgIrrGS/view?usp=sharing) and [Short Demo Video (10sec)](https://drive.google.com/file/d/1rLDS7xQg8qLJw_aDXdZU3kCRQt-JaWJl/view?usp=sharing) 15 | 16 |
17 | 18 | 19 | ## Update History: 20 | 21 | **``2022/7/20``** 3nd commit: 22 | 23 | - Improve algorithms and interactive control. 24 | - Visual charts (cross setion & height) 25 | - Export data. 26 | - UI. 27 | 28 |
29 | 30 | **``2022/7/13`` 2nd commit** 31 | 32 | - Use **BVH Tree** data structure to accelerate the query algorithm. 33 | - Obtain the rugosity in real-time (<1s). 34 | 35 |
36 | 37 | 38 | **``2022/7/10`` 1st commit** 39 | 40 | - Interactively sketch a line on the screen to specify the desired plane to compute rugosity. 41 | - A PLY loader to support arbitrary mesh attributes, and binary modes. 42 | 43 | -------------------------------------------------------------------------------- /CoralRugosity_Zhihao/object.h: -------------------------------------------------------------------------------- 1 | #ifndef OBJECT_H 2 | #define OBJECT_H 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include "utility/happly.h" 22 | #include "utility/tinyply.h" 23 | #include "utility/utils.h" 24 | 25 | struct GLBuffer 26 | { 27 | QOpenGLBuffer vbo; 28 | int count; 29 | }; 30 | 31 | 32 | class Object 33 | { 34 | public: 35 | Object(); 36 | 37 | // Attributes 38 | QVector m_vertices; 39 | QVector m_normals; 40 | QVector m_texcoords; 41 | QVector m_colors; 42 | QVector m_faceids; 43 | 44 | QMap> m_dictPtToFace; // 存储顶点对应的面片的id (vid - fid) 45 | 46 | // OpenGL Buffers 47 | GLBuffer m_meshVBO; 48 | QOpenGLTexture* m_texture = nullptr; 49 | 50 | // 八叉树及查询 51 | 52 | OctreenNode octreeWhole; 53 | QVector octreeList; 54 | void BuildOctree(); 55 | QVector3D QueryPointFromRay(QVector3D camera, QVector3D cameraDir); 56 | 57 | // 构建vid和fid的字典 58 | void BuildDictionary(); 59 | // Mesh相关 60 | void SetTextures(const QString& filepath); 61 | void UpdateMeshs(); 62 | 63 | void CreateMeshVBO(const std::string& filepath); 64 | void DrawMeshVBO(QOpenGLShaderProgram *&program, const QMatrix4x4 &modelMat); 65 | 66 | void CreateLineVBO(const Curve& curve); 67 | void DrawLineVBO(QOpenGLShaderProgram *&program, const QMatrix4x4 &modelMat); 68 | 69 | 70 | bool hasColor() { return (m_colors.size() > 0); } 71 | bool hasTexture() { 72 | //qDebug()<<(m_texture!=nullptr)<<(m_texcoords.size()>0); 73 | return (m_texture!=nullptr && m_texcoords.size()>0);} 74 | 75 | void ResetAll(); 76 | 77 | void InverseY_Axis(); // Y轴颠倒 78 | void FlipTexcoords(); // 镜像翻转纹理坐标 79 | 80 | private: 81 | 82 | }; 83 | 84 | #endif // OBJECT_H 85 | -------------------------------------------------------------------------------- /CoralRugosity_Zhihao/rugosityalgorithm.h: -------------------------------------------------------------------------------- 1 | #ifndef RUGOSITYALGORITHM_H 2 | #define RUGOSITYALGORITHM_H 3 | #include "object.h" 4 | #include "utility/kdtree.h" 5 | 6 | 7 | struct LineID 8 | { 9 | int id1, id2; 10 | 11 | LineID(){} 12 | LineID(int a, int b){ if(a m_curves; 25 | QVector m_curveObjects; 26 | 27 | 28 | Object m_inputMesh; 29 | 30 | // kd树相关 31 | std::vector cloud; // marker Points in simplePoint3 32 | KDTree index; // kd tree 33 | 34 | void BuildKDTree(); 35 | std::vector RadiusSearch(const QVector3D &pos, float radius); 36 | std::vector CubeSearch(const QVector3D &pos, float radiusSize); 37 | 38 | QVector m_octree; 39 | void BuildPartition_WithOctree(float splitCount); 40 | 41 | QVector QueryIntersectedOctreeBoxIDs(Ray mouseray); 42 | bool QueryIntersectedPointAmong(Ray ray, QVector seletedIDs, QVector3D& outIntersectionPt); 43 | 44 | QVector GetRugosityCurvePoints(QVector3D intersectPtA, QVector3D intersectPtB, QVector3D normal); 45 | QVector GetRugosityCurves(QMap& dict_points, QMap >& dict_edges); 46 | 47 | // 寻找平面算法 48 | void FindVerticalPlane(QVector2D startPt2D, QVector2D endPt2D); 49 | 50 | // 更新/绘制Curve的对象 51 | void UpdateCurrentCurveMesh(); 52 | void RenderCurrentCurveMesh(QOpenGLShaderProgram *&program, const QMatrix4x4 &modelMat); 53 | void RenderAllOctreeNode(QOpenGLShaderProgram *&program); 54 | void RenderSelectedOctreeNode(QOpenGLShaderProgram *&program, const QVector& selectedNodeId); 55 | 56 | // interfaces 57 | Object& GetInputMesh() {return this->m_inputMesh;} 58 | 59 | 60 | void TraverseCurve(LineID startID, int firstChildID, 61 | QVector &curves, 62 | QMap &dict_points, QMap > &dict_edges, 63 | QMap &isVisited); 64 | 65 | }; 66 | 67 | #endif // RUGOSITYALGORITHM_H 68 | -------------------------------------------------------------------------------- /CoralRugosity_Zhihao/res/barkVShader.glsl: -------------------------------------------------------------------------------- 1 | #version 120 2 | uniform mat4 mat_projection; 3 | uniform mat4 mat_view; 4 | uniform mat4 mat_model; 5 | 6 | attribute vec3 a_position; 7 | attribute vec3 a_normal; 8 | attribute vec3 a_texCoord; 9 | 10 | varying vec3 v_position; 11 | varying vec3 v_normal; 12 | varying vec3 v_texCoord; 13 | varying mat4 v_mat_model; 14 | 15 | mat4 inverse_mat4(mat4 m) 16 | { 17 | float Coef00 = m[2][2] * m[3][3] - m[3][2] * m[2][3]; 18 | float Coef02 = m[1][2] * m[3][3] - m[3][2] * m[1][3]; 19 | float Coef03 = m[1][2] * m[2][3] - m[2][2] * m[1][3]; 20 | 21 | float Coef04 = m[2][1] * m[3][3] - m[3][1] * m[2][3]; 22 | float Coef06 = m[1][1] * m[3][3] - m[3][1] * m[1][3]; 23 | float Coef07 = m[1][1] * m[2][3] - m[2][1] * m[1][3]; 24 | 25 | float Coef08 = m[2][1] * m[3][2] - m[3][1] * m[2][2]; 26 | float Coef10 = m[1][1] * m[3][2] - m[3][1] * m[1][2]; 27 | float Coef11 = m[1][1] * m[2][2] - m[2][1] * m[1][2]; 28 | 29 | float Coef12 = m[2][0] * m[3][3] - m[3][0] * m[2][3]; 30 | float Coef14 = m[1][0] * m[3][3] - m[3][0] * m[1][3]; 31 | float Coef15 = m[1][0] * m[2][3] - m[2][0] * m[1][3]; 32 | 33 | float Coef16 = m[2][0] * m[3][2] - m[3][0] * m[2][2]; 34 | float Coef18 = m[1][0] * m[3][2] - m[3][0] * m[1][2]; 35 | float Coef19 = m[1][0] * m[2][2] - m[2][0] * m[1][2]; 36 | 37 | float Coef20 = m[2][0] * m[3][1] - m[3][0] * m[2][1]; 38 | float Coef22 = m[1][0] * m[3][1] - m[3][0] * m[1][1]; 39 | float Coef23 = m[1][0] * m[2][1] - m[2][0] * m[1][1]; 40 | 41 | const vec4 SignA = vec4( 1.0, -1.0, 1.0, -1.0); 42 | const vec4 SignB = vec4(-1.0, 1.0, -1.0, 1.0); 43 | 44 | vec4 Fac0 = vec4(Coef00, Coef00, Coef02, Coef03); 45 | vec4 Fac1 = vec4(Coef04, Coef04, Coef06, Coef07); 46 | vec4 Fac2 = vec4(Coef08, Coef08, Coef10, Coef11); 47 | vec4 Fac3 = vec4(Coef12, Coef12, Coef14, Coef15); 48 | vec4 Fac4 = vec4(Coef16, Coef16, Coef18, Coef19); 49 | vec4 Fac5 = vec4(Coef20, Coef20, Coef22, Coef23); 50 | 51 | vec4 Vec0 = vec4(m[1][0], m[0][0], m[0][0], m[0][0]); 52 | vec4 Vec1 = vec4(m[1][1], m[0][1], m[0][1], m[0][1]); 53 | vec4 Vec2 = vec4(m[1][2], m[0][2], m[0][2], m[0][2]); 54 | vec4 Vec3 = vec4(m[1][3], m[0][3], m[0][3], m[0][3]); 55 | 56 | vec4 Inv0 = SignA * (Vec1 * Fac0 - Vec2 * Fac1 + Vec3 * Fac2); 57 | vec4 Inv1 = SignB * (Vec0 * Fac0 - Vec2 * Fac3 + Vec3 * Fac4); 58 | vec4 Inv2 = SignA * (Vec0 * Fac1 - Vec1 * Fac3 + Vec3 * Fac5); 59 | vec4 Inv3 = SignB * (Vec0 * Fac2 - Vec1 * Fac4 + Vec2 * Fac5); 60 | 61 | mat4 Inverse = mat4(Inv0, Inv1, Inv2, Inv3); 62 | 63 | vec4 Row0 = vec4(Inverse[0][0], Inverse[1][0], Inverse[2][0], Inverse[3][0]); 64 | 65 | float Determinant = dot(m[0], Row0); 66 | 67 | Inverse /= Determinant; 68 | 69 | return Inverse; 70 | } 71 | 72 | void main() 73 | { 74 | gl_Position = mat_projection* mat_view * mat_model * vec4(a_position,1.0); 75 | 76 | v_position = vec3(mat_model*vec4(a_position,1.0)); 77 | //v_normal = vec3(normalize(mat_model*vec4(gl_Normal,1.0))); 78 | //v_normal = mat3(transpose(inverse_mat4(mat_model))) * gl_Normal; 79 | v_normal = mat3(transpose(inverse_mat4(mat_model))) * a_normal; 80 | v_texCoord = a_texCoord; 81 | } 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /CoralRugosity_Zhihao/glwidget.h: -------------------------------------------------------------------------------- 1 | #ifndef GLWIDGET_H 2 | #define GLWIDGET_H 3 | #include "object.h" 4 | #include "rugosityalgorithm.h" 5 | 6 | #define PI 3.1415926 7 | 8 | class GLWidget : public QOpenGLWidget , public QOpenGLFunctions 9 | { 10 | Q_OBJECT 11 | 12 | public: 13 | explicit GLWidget(QWidget *parent = 0); 14 | ~GLWidget(); 15 | 16 | RugosityAlgorithm* m_rugosity; 17 | 18 | // original 2D rugosity (before scaling) 19 | QVector> m_2D_curves; 20 | QVector m_2D_heights; 21 | 22 | float original_y_range, original_x_range, original_x_min, original_y_min; // 用于scale的一些数值 23 | float scaled_x_max, scaled_y_max; // scale后的数值 24 | float ui_scale_value = 1.0f; 25 | 26 | // visulized 2D rugosity (after scaling) 27 | QVector> m_2D_curves_scaled; 28 | QVector m_2D_heights_scaled; 29 | enum DISPLAY_MODE 30 | { 31 | _Normal, 32 | _OnlyLine, 33 | } m_displayMode; 34 | 35 | enum PROJECT_MODE{_Perspective, _Ortho} m_projectMode; 36 | 37 | QTimer m_viewingTimer; 38 | 39 | void SetEyeDist(const QVector3D& c) { this->m_eyeDist = c;} 40 | 41 | bool m_isWireFrame = true; 42 | bool m_isShowBVH = true; 43 | bool m_isBarkTextured; 44 | bool m_isShowSeg; 45 | 46 | 47 | // 参数maxY和minY是纵坐标轴的范围 48 | QVector> Get2DVersionCurves(float &minY, float &maxY); // Get Rugosity (cross-section, multi-curves) 49 | QVector Get2DHeightMap(const QVector>& curves); // Get Rugosity (Heights) 50 | 51 | void Compute_XY_Range(QVector>& curves, QVector& heights); 52 | void Scale_2D_Rugosity(float scale=1.0f); 53 | 54 | protected: 55 | void initializeGL(); 56 | void resizeGL(int w, int h); 57 | 58 | void paintGL(); 59 | 60 | // shader programs 61 | QOpenGLShaderProgram* m_pointProgram; 62 | QOpenGLShaderProgram* m_skeletonProgram; 63 | QOpenGLShaderProgram* m_meshProgram; 64 | 65 | // Textures 66 | QOpenGLTexture* m_leafTexture; 67 | 68 | void initShaders(QOpenGLShaderProgram *&m_program, const QString &shaderName, bool v, bool c, bool n, bool t); 69 | void setupShaders(QOpenGLShaderProgram*& m_program); 70 | void setupTexture(QOpenGLTexture*& texture,const QString& filename); 71 | 72 | // rendering related 73 | QMatrix4x4 m_projectMatrix; 74 | QMatrix4x4 m_viewMatrix; 75 | QMatrix4x4 m_modelMat; 76 | QVector3D m_eyePos,m_eyeDist; 77 | 78 | QPoint m_clickpos; 79 | bool isPressLeftButton = false; 80 | 81 | bool isPressRightButton = false; 82 | QVector2D m_startPt, m_endPt; 83 | 84 | double m_horAngle,m_verAngle; 85 | 86 | void mousePressEvent(QMouseEvent* event); 87 | void mouseMoveEvent(QMouseEvent* event); 88 | void mouseReleaseEvent(QMouseEvent* event); 89 | 90 | double distance; 91 | double scale; 92 | void wheelEvent(QWheelEvent* event); 93 | 94 | void keyPressEvent(QKeyEvent* e); 95 | 96 | private: 97 | 98 | Ray GetRayFromScreenPos(QVector2D screenPoint); 99 | 100 | signals: 101 | void sig_updateChart(float minY, float maxY); 102 | 103 | private slots: 104 | void OnViewingTimer(); 105 | }; 106 | 107 | #endif // GLWIDGET_H 108 | -------------------------------------------------------------------------------- /CoralRugosity_Zhihao/rugosityalgorithm.cpp.J73680: -------------------------------------------------------------------------------- 1 | #include "rugosityalgorithm.h" 2 | 3 | RugosityAlgorithm::RugosityAlgorithm() 4 | { 5 | 6 | } 7 | 8 | 9 | ////// 10 | /// KD-Tree 相关函数 11 | ////// 12 | void RugosityAlgorithm::BuildKDTree() 13 | { 14 | for(unsigned int i=0; i RugosityAlgorithm::RadiusSearch(const QVector3D &pos, float radius) 21 | { 22 | auto closeNodes = index.getPointsWithinCube(GlobalTools::GetSimplePoint(pos), 23 | radius); 24 | std::vector res; 25 | for(unsigned int i=0; i RugosityAlgorithm::CubeSearch(const QVector3D &pos, float radiusSize) 36 | { 37 | auto closeNodes = index.getPointsWithinCube(GlobalTools::GetSimplePoint(pos),radiusSize); 38 | 39 | return closeNodes; 40 | } 41 | 42 | 43 | /// 44 | /// 构建八叉树 45 | /// 46 | /// 47 | /// 48 | /// { 49 | struct LabelID { 50 | int x, y, z; 51 | 52 | // bool operator < ( const LabelID& item) const 53 | // { 54 | // if(this->x != item.x){ 55 | // return this->xy != item.y){ 58 | // return this->yz 0) 68 | { 69 | if( blockID%2 == 0) // 偶数 70 | return blockID/2; 71 | else if( blockID%2 !=0) // 奇数 72 | return blockID/2+1; 73 | } 74 | if(blockID < 0) 75 | { 76 | if( blockID%2 == 0) // 偶数 77 | return blockID/2; 78 | else if( blockID%2 !=0) // 奇数 79 | return blockID/2-1; 80 | } 81 | 82 | return 0; 83 | 84 | } 85 | }; 86 | void RugosityAlgorithm::BuildPartition_WithOctree(float width) 87 | { 88 | QMap octNodes; 89 | 90 | 91 | for(int i=0; im_octree.append(octNodes[key]); 118 | } 119 | 120 | qDebug()<<"OctTree构建完毕:"< 6 | 7 | 8 | 9 | MainWindow::MainWindow(QWidget *parent) : 10 | QMainWindow(parent), 11 | ui(new Ui::MainWindow) 12 | { 13 | ui->setupUi(this); 14 | m_glwidget = new GLWidget(ui->centralWidget); 15 | 16 | // initialize UI 17 | ui->ckbWireFrame->setChecked(m_glwidget->m_isWireFrame); 18 | ui->ckbBVHS->setChecked(m_glwidget->m_isShowBVH); 19 | 20 | connect(ui->ckbWireFrame,SIGNAL(clicked(bool)), this, SLOT(slot_updateStatus())); 21 | connect(ui->ckbBVHS,SIGNAL(clicked(bool)), this, SLOT(slot_updateStatus())); 22 | connect(ui->spinScale,SIGNAL(valueChanged(double)), this, SLOT(slot_updateStatus())); 23 | connect(ui->btnSaveRugosity,SIGNAL(clicked(bool)),this,SLOT(slot_saveRugosity())); 24 | 25 | // Initialize Chart 26 | m_chart_crosssection = new QChart(); 27 | m_chart_crosssection->legend()->hide(); 28 | 29 | QLineSeries* series = new QLineSeries(); 30 | series->append(0,0); 31 | series->append(10,0); 32 | m_chart_crosssection->setTitle("Rugosity - CrossSection (m)"); 33 | 34 | m_chart_crosssection->setTheme(QChart::ChartThemeBlueCerulean); 35 | m_chart_crosssection->setFont(QFont(QString("微软雅黑"),15,2)); 36 | m_chart_crosssection->setTitleFont(QFont(QString("微软雅黑"),10,2)); 37 | m_chart_crosssection->addSeries(series); 38 | m_chart_crosssection->createDefaultAxes(); 39 | ui->graphicsView->setChart(m_chart_crosssection); 40 | ui->graphicsView->setRenderHint(QPainter::Antialiasing); 41 | 42 | // Initialize Chart 2 43 | m_chart_height = new QChart(); 44 | m_chart_height->legend()->hide(); 45 | 46 | QLineSeries* series_2 = new QLineSeries(); 47 | series_2->append(0,0); 48 | series_2->append(10,0); 49 | m_chart_height->setTitle("Rugosity - Height (m)"); 50 | 51 | m_chart_height->setTheme(QChart::ChartThemeBlueCerulean); 52 | m_chart_height->setFont(QFont(QString("微软雅黑"),15,2)); 53 | m_chart_height->setTitleFont(QFont(QString("微软雅黑"),10,2)); 54 | m_chart_height->addSeries(series_2); 55 | m_chart_height->createDefaultAxes(); 56 | ui->graphicsView_2->setChart(m_chart_height); 57 | ui->graphicsView_2->setRenderHint(QPainter::Antialiasing); 58 | 59 | connect(m_glwidget, SIGNAL(sig_updateChart(float, float)), this, SLOT(slot_updateChart(float, float))); 60 | } 61 | 62 | void MainWindow::slot_updateChart(float minY, float maxY) 63 | { 64 | // Update Cross-section Chart 65 | m_chart_crosssection->removeAllSeries(); 66 | QVector>& curves = m_glwidget->m_2D_curves_scaled; 67 | 68 | QVector series; 69 | series.resize(curves.size()); 70 | for(int i=0; i& curCurve = curves[i]; 74 | for(int p=0; pappend(curCurve[p].toPointF()); 77 | } 78 | 79 | m_chart_crosssection->addSeries((series[i])); 80 | QPen pen = series[i]->pen(); 81 | pen.setWidth(5); 82 | series[i]->setPen(pen); 83 | } 84 | 85 | m_chart_crosssection->createDefaultAxes(); 86 | 87 | 88 | // Update Height Chart 89 | m_chart_height->removeAllSeries(); 90 | QVector heights = m_glwidget->m_2D_heights_scaled; 91 | 92 | QLineSeries* series_height = new QLineSeries(); 93 | for(int p=0; pappend(heights[p].toPointF()); 96 | } 97 | 98 | QPen pen = series_height->pen(); 99 | pen.setWidth(5); 100 | pen.setColor(QColor(255,0,0)); 101 | series_height->setPen(pen); 102 | 103 | m_chart_height->addSeries(series_height); 104 | QPen pen_height = series_height->pen(); 105 | pen_height.setWidth(5); 106 | series_height->setPen(pen_height); 107 | 108 | 109 | m_chart_height->createDefaultAxes(); 110 | m_chart_height->axisX()->setRange(0, this->m_glwidget->scaled_x_max*1.02); 111 | m_chart_height->axisY()->setRange(0, this->m_glwidget->scaled_y_max*1.02); 112 | m_chart_crosssection->axisX()->setRange(0, this->m_glwidget->scaled_x_max*1.02); 113 | m_chart_crosssection->axisY()->setRange(0, this->m_glwidget->scaled_y_max*1.02); 114 | // qDebug()<<(m_chart_crosssection->axes().size()); 115 | 116 | } 117 | 118 | void MainWindow::resizeEvent(QResizeEvent *e) 119 | { 120 | //qDebug()<<"resize()"; 121 | ui->groupBox->move(ui->centralWidget->width()-ui->groupBox->width(),0); 122 | 123 | m_glwidget->move(0,0); 124 | m_glwidget->resize(ui->centralWidget->width()-ui->groupBox->width()-5,ui->centralWidget->height()*0.6); 125 | ui->graphicsView->move(0, m_glwidget->height()); 126 | 127 | float newchartHeight = ui->centralWidget->height() * (1.0 - m_glwidget->height()/(float)height()); 128 | float chartRatio = newchartHeight/ui->graphicsView->height(); 129 | float newchartWidth = ui->graphicsView->width() * chartRatio; 130 | ui->graphicsView->resize(newchartWidth, newchartHeight); 131 | 132 | ui->graphicsView_2->move(ui->graphicsView->width(), m_glwidget->height()); 133 | ui->graphicsView_2->resize(newchartWidth, newchartHeight); 134 | } 135 | 136 | void MainWindow::slot_updateStatus() 137 | { 138 | m_glwidget->m_isWireFrame = ui->ckbWireFrame->isChecked(); 139 | m_glwidget->m_isShowBVH = ui->ckbBVHS->isChecked(); 140 | 141 | if((QDoubleSpinBox*)sender() == ui->spinScale) 142 | { 143 | this->m_glwidget->ui_scale_value = ui->spinScale->value(); 144 | this->m_glwidget->Scale_2D_Rugosity(this->m_glwidget->ui_scale_value); 145 | this->slot_updateChart(0,0); 146 | } 147 | m_glwidget->update(); 148 | } 149 | 150 | void MainWindow::slot_saveRugosity() 151 | { 152 | QString filepath = QFileDialog::getSaveFileName(this,tr("Save Rugosity File"), ".", tr("Rugosity (*.xls)")); 153 | 154 | qDebug()<m_2D_heights.size(); ++i) 178 | { 179 | ts<m_2D_heights[i].x()<<"\t"<m_2D_heights[i].y()<<"\t\n"; 180 | } 181 | 182 | file.close(); 183 | qDebug()<<"保存height完毕!"; 184 | } 185 | 186 | { 187 | // 首先存档Rugosity的height 188 | QString filename_crosssection = fileinfo.absolutePath() + "/" + fileinfo.baseName() + "_crossSection."+fileinfo.suffix(); 189 | 190 | QFile file(filename_crosssection); 191 | if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) 192 | return; 193 | 194 | QTextStream ts(&file); 195 | 196 | int max_point_size = 0; 197 | 198 | auto& curves2D = m_glwidget->m_2D_curves; 199 | auto& curves3D = m_glwidget->m_rugosity->m_curves; 200 | 201 | for(int i=0; i max_point_size) 204 | max_point_size = curves3D[i].pts.size(); 205 | } 206 | 207 | for(int cid = 0; cid= curves2D[cid].size()) 220 | { 221 | ts<<""<<"\t"<<""<<"\t"; 222 | } 223 | else 224 | { 225 | ts<= curves3D[cid].pts.size()) 229 | { 230 | ts<<""<<"\t"<<""<<"\t"<<""<<"\t"; 231 | } 232 | else 233 | { 234 | ts< curves3D = m_glwidget->m_rugosity->m_curves ; 260 | 261 | 262 | // // 首先计算最大的点的数目 263 | // int max = 0; 264 | // for(int i=0; isetFocusPolicy(Qt::StrongFocus); 13 | 14 | m_rugosity = new RugosityAlgorithm(); 15 | 16 | m_leafTexture = NULL; 17 | m_meshProgram = NULL; 18 | m_skeletonProgram = NULL; 19 | m_pointProgram = NULL; 20 | 21 | m_polygonMode = true; 22 | m_isBarkTextured = false; 23 | m_isShowSeg = false; 24 | 25 | m_projectMode = _Perspective; 26 | this->m_displayMode = DISPLAY_MODE::_Normal; 27 | m_horAngle = 45.0f; 28 | m_verAngle = 0.0f; 29 | distance = 10.0f; 30 | scale = 1.0f; 31 | m_eyeDist = QVector3D(0.0f,0.0f,0.0f); 32 | 33 | 34 | connect(&m_viewingTimer,SIGNAL(timeout()),this,SLOT(OnViewingTimer())); 35 | } 36 | 37 | 38 | void GLWidget::initShaders(QOpenGLShaderProgram*& m_program,const QString& shaderName, 39 | bool v = false,bool c = false, bool n = false, bool t=false) 40 | { 41 | m_program = new QOpenGLShaderProgram(this); 42 | 43 | m_program->addShaderFromSourceFile(QOpenGLShader::Vertex, QString(":/shader/res/%1VShader.glsl").arg(shaderName)); 44 | m_program->addShaderFromSourceFile(QOpenGLShader::Fragment,QString(":/shader/res/%1FShader.glsl").arg(shaderName)); 45 | 46 | if(v) 47 | m_program->bindAttributeLocation("a_position",VERTEX_ATTRIBUTE); 48 | if(c) 49 | m_program->bindAttributeLocation("a_color" ,COLOUR_ATTRIBUTE); 50 | if(n) 51 | m_program->bindAttributeLocation("a_normal" ,NORMAL_ATTRIBUTE); 52 | if(t) 53 | m_program->bindAttributeLocation("a_texCoord",TEXTURE_ATTRIBUTE); 54 | 55 | m_program->link(); 56 | m_program->bind(); 57 | } 58 | 59 | 60 | void GLWidget::setupShaders(QOpenGLShaderProgram *&m_program) 61 | { 62 | m_program->bind(); 63 | 64 | m_program->setUniformValue("mat_projection",m_projectMatrix); 65 | m_program->setUniformValue("mat_view",m_viewMatrix); 66 | } 67 | 68 | void GLWidget::setupTexture(QOpenGLTexture *&texture, const QString &filename) 69 | { 70 | if(texture) 71 | delete texture; 72 | 73 | QImage img(filename); 74 | texture = new QOpenGLTexture(img); 75 | 76 | texture->setMinificationFilter(QOpenGLTexture::Nearest); 77 | texture->setMagnificationFilter(QOpenGLTexture::Linear); 78 | } 79 | 80 | 81 | void GLWidget::initializeGL() 82 | { 83 | initializeOpenGLFunctions(); 84 | 85 | glClearColor(1.0,1.0,1.0,1.0); 86 | 87 | glEnable(GL_COLOR_MATERIAL); 88 | glEnable(GL_DEPTH_TEST); 89 | glDepthFunc(GL_LESS); 90 | 91 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 92 | glShadeModel(GL_SMOOTH); 93 | glEnable(GL_POINT_SMOOTH); 94 | glEnable(GL_BLEND); 95 | glEnable(GL_ALPHA_TEST); 96 | glEnable(GL_TEXTURE_2D); 97 | 98 | 99 | initShaders(m_pointProgram,"Points",true,true,false,false); 100 | initShaders(m_skeletonProgram,"Graph",true,false,false,false); 101 | initShaders(m_meshProgram,"bark",true,false,true,true); 102 | 103 | //setupTexture(m_leafTexture,":/img/res/leaf/叶子.png"); 104 | qsrand(QTime::currentTime().msec()*QTime::currentTime().second()); 105 | 106 | // 加载默认的coral mesh 107 | m_rugosity->GetInputMesh().CreateMeshVBO("D:/Projects/HKUST_VGD/Coral/videos/video1/coral model (small image size)/scene_dense_mesh_refine_texture.ply"); 108 | m_rugosity->BuildKDTree(); // 创建Kd树 109 | 110 | // 设定最原始配置的curve 111 | QVector tmp; 112 | 113 | tmp.append(QVector3D(0.0,0.0,0.0)); 114 | tmp.append(QVector3D(0.0,0.0,1.0)); 115 | tmp.append(QVector3D(0.0,1.0,2.0)); 116 | tmp.append(QVector3D(2.0,-1.0,-2.0)); 117 | tmp.append(QVector3D(5.0,10.0,0.0)); 118 | 119 | Curve tmpCurve; 120 | tmpCurve.pts = tmp; 121 | m_rugosity->m_curves.append(tmpCurve); 122 | 123 | m_rugosity->UpdateCurrentMesh(); 124 | } 125 | 126 | void GLWidget::resizeGL(int w, int h) 127 | { 128 | glViewport(0,0,width(),height()); 129 | 130 | m_projectMatrix.setToIdentity(); 131 | 132 | if(m_projectMode == _Perspective) 133 | m_projectMatrix.perspective(45.0f,(float)w/(float)h,0.1f,100000.0f); 134 | else if(m_projectMode == _Ortho) 135 | m_projectMatrix.ortho(-400,400,-400,400,-100.0,1000); 136 | } 137 | 138 | void GLWidget::paintGL() 139 | { 140 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 141 | 142 | if(m_projectMode == _Perspective) 143 | { 144 | m_eyePos = m_eyeDist+QVector3D(scale*distance*cos(PI*m_verAngle/180.0)*cos(PI*m_horAngle/180.0), 145 | scale*distance*sin(PI*m_verAngle/180.0), 146 | scale*distance*cos(PI*m_verAngle/180.0)*sin(PI*m_horAngle/180.0)); 147 | m_viewMatrix.setToIdentity(); 148 | m_viewMatrix.lookAt(m_eyePos,m_eyeDist,QVector3D(0,1,0)); 149 | m_modelMat.setToIdentity(); 150 | } 151 | else if(m_projectMode == _Ortho) 152 | { 153 | m_viewMatrix.setToIdentity(); 154 | m_modelMat.setToIdentity(); 155 | } 156 | 157 | 158 | // if(!m_polygonMode) 159 | // glPolygonMode(GL_FRONT_AND_BACK,GL_LINE); 160 | // else 161 | // glPolygonMode(GL_FRONT_AND_BACK,GL_FILL); 162 | 163 | glPolygonMode(GL_FRONT_AND_BACK,GL_LINE); 164 | { 165 | glLineWidth(1); 166 | // 绘制mesh 167 | setupShaders(m_meshProgram); 168 | glEnable(GL_TEXTURE0); 169 | 170 | m_meshProgram->setUniformValue("texture",0); 171 | if(m_rugosity->GetInputMesh().hasTexture()) 172 | { 173 | m_rugosity->GetInputMesh().m_texture->bind(); 174 | m_meshProgram->setUniformValue("isTextured",1); 175 | } 176 | else 177 | m_meshProgram->setUniformValue("isTextured",0); 178 | 179 | m_meshProgram->setUniformValue("viewPos",m_eyePos); 180 | m_meshProgram->setUniformValue("isLighting",false); 181 | m_meshProgram->setUniformValue("isBark",false); 182 | m_meshProgram->setUniformValue("isShowSeg",false); 183 | //m_tree->DrawTreeMeshVBO(m_meshProgram,m_modelMat); 184 | m_rugosity->GetInputMesh().DrawMeshVBO(m_meshProgram,m_modelMat); 185 | 186 | } 187 | { 188 | // 绘制找到的curve 189 | glLineWidth(4); 190 | setupShaders(this->m_skeletonProgram); 191 | m_skeletonProgram->setUniformValue("u_color",QVector3D(1,0,0)); 192 | m_rugosity->RenderCurrentMesh(m_skeletonProgram,m_modelMat); 193 | } 194 | 195 | { 196 | //------------ 渲染其他 --------------------- // 197 | if(this->isPressRightButton) 198 | { 199 | // 绘制一个选取矩形 200 | glColor3f(0.0,0.0,1.0); 201 | 202 | m_skeletonProgram->bind(); 203 | 204 | m_skeletonProgram->setUniformValue("mat_projection",QMatrix4x4()); 205 | m_skeletonProgram->setUniformValue("mat_view",QMatrix4x4()); 206 | 207 | glPolygonMode(GL_FRONT_AND_BACK,GL_LINE); 208 | 209 | glLineWidth(2.0f); 210 | 211 | float x1 = 2*m_startPt.x()/(float)width()-1.0; 212 | float y1 = -2*m_startPt.y()/(float)height()+1.0; 213 | float x2 = 2*m_endPt.x()/(float)width()-1.0; 214 | float y2 = -2*m_endPt.y()/(float)height()+1.0; 215 | 216 | glBegin(GL_LINES); 217 | glVertex2f(x1, y1); 218 | glVertex2f(x2, y2); 219 | glEnd(); 220 | } 221 | 222 | // 绘制一个选取矩形 223 | 224 | m_skeletonProgram->bind(); 225 | 226 | m_skeletonProgram->setUniformValue("mat_projection",QMatrix4x4()); 227 | m_skeletonProgram->setUniformValue("mat_view",QMatrix4x4()); 228 | 229 | glPolygonMode(GL_FRONT_AND_BACK,GL_LINE); 230 | 231 | m_skeletonProgram->setUniformValue("u_color",QVector3D(0,1,0)); 232 | QVector3D ray = GlobalTools::GetRayFromScreenPos(m_endPt.toPointF(), width(), height(), 233 | m_projectMatrix,m_viewMatrix); 234 | QVector3D dist_pt = 10.0f*ray + m_eyePos; 235 | qDebug()<buttons() & Qt::RightButton) 248 | { 249 | isPressRightButton = true; 250 | m_startPt = QVector2D(event->pos()); 251 | return; 252 | } 253 | m_clickpos = event->pos(); 254 | 255 | 256 | } 257 | 258 | void GLWidget::mouseMoveEvent(QMouseEvent *event) 259 | { 260 | if(event->buttons() & Qt::RightButton) 261 | { 262 | isPressRightButton = true; 263 | 264 | m_endPt = QVector2D(event->pos()); 265 | update(); 266 | 267 | return; 268 | } 269 | if(event->buttons() == Qt::LeftButton) 270 | { 271 | QPoint cur = event->pos(); 272 | 273 | double dx = (cur.x() - m_clickpos.x())/5.0; 274 | double dy = (cur.y() - m_clickpos.y())/5.0; 275 | 276 | m_horAngle +=dx; 277 | if(m_verAngle+dy <90.0 && m_verAngle+dy>-90.0) 278 | m_verAngle +=dy; 279 | 280 | m_clickpos = cur; 281 | update(); 282 | } 283 | } 284 | 285 | void GLWidget::mouseReleaseEvent(QMouseEvent *event) 286 | { 287 | if(isPressRightButton) 288 | { 289 | isPressRightButton = false; 290 | update(); 291 | return; 292 | } 293 | } 294 | 295 | void GLWidget::keyPressEvent(QKeyEvent *e) 296 | { 297 | if(e->key() == Qt::Key_W) 298 | m_eyeDist.setY(m_eyeDist.y()+5); 299 | if(e->key() == Qt::Key_S) 300 | m_eyeDist.setY(m_eyeDist.y()-5); 301 | if(e->key() == Qt::Key_C) 302 | m_polygonMode = ! m_polygonMode; 303 | this->update(); 304 | } 305 | 306 | void GLWidget::wheelEvent(QWheelEvent *event) 307 | { 308 | double ds = 0.03; 309 | if(event->delta()>0 && scale-ds>0) 310 | scale-=ds; 311 | else if(event->delta()<0) 312 | scale+=ds; 313 | update(); 314 | } 315 | 316 | void GLWidget::OnViewingTimer() 317 | { 318 | m_horAngle +=1; 319 | update(); 320 | } 321 | 322 | GLWidget::~GLWidget(){} 323 | -------------------------------------------------------------------------------- /CoralRugosity_Zhihao/utility/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILS_H 2 | #define UTILS_H 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | 21 | struct float2 { float x, y; }; 22 | struct float3 { float x, y, z; }; 23 | struct float6 { float u1, v1, u2, v2, u3, v3;}; 24 | struct double3 { double x, y, z; }; 25 | struct uint3 { uint32_t x, y, z; }; 26 | struct uint4 { uint32_t x, y, z, w; }; 27 | 28 | struct simplePoint3 29 | { 30 | double x,y,z; 31 | const static int dimension = 3; 32 | double getDimension(int dim) const 33 | { 34 | switch(dim) 35 | { 36 | case 0: return x; 37 | case 1: return y; 38 | case 2: return z; 39 | default: return std::numeric_limits::quiet_NaN(); 40 | } 41 | } 42 | }; 43 | 44 | 45 | struct Curve 46 | { 47 | QVector pts; 48 | }; 49 | 50 | 51 | struct Ray 52 | { 53 | QVector3D center; 54 | QVector3D dir; 55 | }; 56 | 57 | class OctreenNode 58 | { 59 | public: 60 | QVector3D center; 61 | 62 | float width; // width = Radius (instead of Diameter) 63 | 64 | QVector vertexIDs; // 所属的顶点ID 65 | }; 66 | 67 | struct BoundingBox 68 | { 69 | float x,y,z; 70 | }; 71 | 72 | 73 | 74 | class GlobalTools 75 | { 76 | public: 77 | 78 | // 检查与ply文件路径的baseName相同的纹理是否存在? 79 | static QString checkDefaultTextureExists(const QString& plyFilePath) 80 | { 81 | QFileInfo l_info(plyFilePath); 82 | 83 | QString imagePath = l_info.absolutePath() + "/" + l_info.baseName() +"." + "png"; 84 | if(QFileInfo(imagePath).exists()) return imagePath; 85 | imagePath = l_info.absolutePath() + "/" + l_info.baseName() +"." + "PNG"; 86 | if(QFileInfo(imagePath).exists()) return imagePath; 87 | imagePath = l_info.absolutePath() + "/" + l_info.baseName() +"." + "jpg"; 88 | if(QFileInfo(imagePath).exists()) return imagePath; 89 | imagePath = l_info.absolutePath() + "/" + l_info.baseName() +"." + "JPG"; 90 | if(QFileInfo(imagePath).exists()) return imagePath; 91 | imagePath = l_info.absolutePath() + "/" + l_info.baseName() +"." + "bmp"; 92 | if(QFileInfo(imagePath).exists()) return imagePath; 93 | imagePath = l_info.absolutePath() + "/" + l_info.baseName() +"." + "BMP"; 94 | if(QFileInfo(imagePath).exists()) return imagePath; 95 | 96 | return ""; 97 | } 98 | 99 | // SimplePoint3和Vector3D的转换 100 | static simplePoint3 GetSimplePoint(const QVector3D &pos) 101 | { 102 | simplePoint3 query_pt; 103 | query_pt.x = pos.x(); 104 | query_pt.y = pos.y(); 105 | query_pt.z = pos.z(); 106 | 107 | return query_pt; 108 | } 109 | 110 | static QVector3D GetVector3D(const simplePoint3 &pos) 111 | { 112 | return QVector3D(pos.x,pos.y,pos.z); 113 | } 114 | 115 | 116 | static QVector3D GetRayDirection_From2DScreenPos(QPointF screen_pos, float width, float height, 117 | QMatrix4x4 projection_matrix, QMatrix4x4 view_matrix) 118 | { 119 | float mouse_x = screen_pos.x(), mouse_y = screen_pos.y(); 120 | 121 | float x = (2.0f * mouse_x) / width - 1.0f; 122 | float y = 1.0f - (2.0f * mouse_y) / height; 123 | float z = 1.0f; 124 | QVector3D ray_nds = QVector3D(x, y, z); 125 | 126 | QVector4D ray_clip = QVector4D(ray_nds.x(), ray_nds.y(), -1.0, 1.0); 127 | 128 | QVector4D ray_eye = projection_matrix.inverted() * ray_clip; 129 | 130 | ray_eye = QVector4D(ray_eye.x(), ray_eye.y(), -1.0, 0.0); 131 | 132 | QVector3D ray_wor = QVector3D(view_matrix.inverted() * ray_eye); 133 | // don't forget to normalise the vector at some point 134 | ray_wor = ray_wor.normalized(); 135 | 136 | return ray_wor; 137 | } 138 | 139 | static bool GetIntersectPoint_ray_to_triangle(Ray ray, 140 | QVector3D vertex0, QVector3D vertex1, QVector3D vertex2, 141 | QVector3D& outIntersectionPoint) 142 | { 143 | QVector3D rayOrigin = ray.center; 144 | QVector3D rayVector = ray.dir; 145 | 146 | const float EPSILON = 0.0000001; 147 | 148 | QVector3D edge1, edge2, h, s, q; 149 | float a,f,u,v; 150 | edge1 = vertex1 - vertex0; 151 | edge2 = vertex2 - vertex0; 152 | h = QVector3D::crossProduct(rayVector, edge2); 153 | a = QVector3D::dotProduct(edge1, h); 154 | if (a > -EPSILON && a < EPSILON) 155 | return false; // This ray is parallel to this triangle. 156 | f = 1.0/a; 157 | s = rayOrigin - vertex0; 158 | u = f * QVector3D::dotProduct(s, h); 159 | if (u < 0.0 || u > 1.0) 160 | return false; 161 | q = QVector3D::crossProduct(s, edge1); 162 | v = f * QVector3D::dotProduct(rayVector, q); 163 | if (v < 0.0 || u + v > 1.0) 164 | return false; 165 | // At this stage we can compute t to find out where the intersection point is on the line. 166 | float t = f * QVector3D::dotProduct(edge2, q); 167 | if (t > EPSILON) // ray intersection 168 | { 169 | outIntersectionPoint = rayOrigin + rayVector * t; 170 | return true; 171 | } 172 | else // This means that there is a line intersection but not a ray intersection. 173 | return false; 174 | } 175 | 176 | 177 | static bool GetIntersectPoint_segment_to_triangle(QVector3D n, QVector3D p, QVector3D v1, QVector3D v2, QVector3D& out) 178 | { 179 | // Rwference Link: https://math.stackexchange.com/questions/47594/plane-intersecting-line-segment 180 | 181 | // 注意n是单位化的向量 182 | float dist1 = QVector3D::dotProduct(n, v1-p); 183 | float dist2 = QVector3D::dotProduct(n, v2-p); 184 | 185 | if(dist1*dist2 >0) // 同向, 186 | return false; 187 | 188 | if(dist1==0 && dist2==0) 189 | { 190 | out = (v2+v1)/2.0f; 191 | return true; 192 | } 193 | 194 | if(dist1==0) 195 | { 196 | out = v1; 197 | return true; 198 | } 199 | if(dist2==0) 200 | { 201 | out = v2; 202 | return true; 203 | } 204 | 205 | QVector3D x = (v2-v1).normalized(); 206 | 207 | float costheta = QVector3D::dotProduct(n,x); 208 | 209 | if(costheta==0) 210 | { 211 | out = (v2+v1)/2.0f; 212 | return true; 213 | } 214 | 215 | out = v2 - (dist2/costheta)*x; 216 | 217 | return true; 218 | } 219 | 220 | // static QVector3D GetRayFromScreenPos(QPointF screenPos, QVector3D direction, QVector3D up, float angle, float w, float h) 221 | // { 222 | 223 | // float x_uv = screenPos.x(), y_uv = screenPos.y(); 224 | 225 | // x_uv = 2.0f * x_uv / (w - 1) - 1.0; 226 | // y_uv = 2.0f * y_uv / (h - 1) - 1.0; 227 | 228 | // QVector3D u = QVector3D::crossProduct(direction, up); 229 | // u = u.normalized(); 230 | // QVector3D v = QVector3D::crossProduct(u, direction); 231 | // v = v.normalized(); 232 | 233 | // QVector3D raydir = x_uv * v + y_uv * u + 1 / tan(angle) * direction; 234 | // raydir.normalize(); 235 | 236 | // return raydir; 237 | // } 238 | 239 | static float max(float a, float b) 240 | { 241 | return (a>b)? a:b; 242 | } 243 | static float min(float a, float b) 244 | { 245 | return (a boxmax[i]) { 300 | return false; 301 | } 302 | } 303 | 304 | return tmax >= tmin && tmax >= 0.0; 305 | } 306 | 307 | // static bool intersectAABB(QVector3D rayOrigin, QVector3D rayDir, OctreenNode octreeBox) 308 | // { 309 | // // TODO: 需要重新写这个函数 310 | // float a_squared = octreeBox.width*octreeBox.width + octreeBox.width*octreeBox.width; 311 | // float box_radisu = sqrt(a_squared + octreeBox.width*octreeBox.width); 312 | // float distanceToCenter = octreeBox.center.distanceToLine(rayOrigin,rayDir); 313 | 314 | // if(distanceToCenter <= box_radisu) return true; 315 | // return false; 316 | 317 | 318 | // } 319 | 320 | static BoundingBox computeBoundingBoxFromPoints(const QVector& points) 321 | { 322 | BoundingBox res; 323 | 324 | 325 | for(unsigned int i=0; isetUniformValue("u_color",QVector3D(0.5,0.5,0.0)); 339 | glBegin(GL_LINES); 340 | glVertex3f(center.x(),center.y(), center.z()); 341 | glVertex3f(center.x()+radius, center.y(), center.z()); 342 | glEnd(); 343 | shader->setUniformValue("u_color",QVector3D(0.5,0.0,0.5)); 344 | glBegin(GL_LINES); 345 | glVertex3f(center.x(),center.y(), center.z()); 346 | glVertex3f(center.x(), center.y()+radius, center.z()); 347 | glEnd(); 348 | shader->setUniformValue("u_color",QVector3D(0.0,0.5,0.5)); 349 | glBegin(GL_LINES); 350 | glVertex3f(center.x(),center.y(), center.z()); 351 | glVertex3f(center.x(), center.y(), center.z()+radius); 352 | glEnd(); 353 | } 354 | }; 355 | 356 | #endif // UTILS_H 357 | -------------------------------------------------------------------------------- /CoralRugosity_Zhihao/mainwindow.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 787 10 | 642 11 | 12 | 13 | 14 | MainWindow 15 | 16 | 17 | background-color: rgb(255, 255, 255); 18 | 19 | 20 | 21 | 22 | 23 | 480 24 | 0 25 | 301 26 | 471 27 | 28 | 29 | 30 | 31 | 微软雅黑 32 | 11 33 | 50 34 | false 35 | 36 | 37 | 38 | background-color: rgb(255, 255, 255); 39 | 40 | 41 | Control Panel 42 | 43 | 44 | 45 | 46 | 0 47 | 400 48 | 301 49 | 71 50 | 51 | 52 | 53 | 54 | Arial 55 | 11 56 | 57 | 58 | 59 | LIU Zhihao 60 | (liuzh96@outlook.com) 61 | 62 | 63 | Qt::AlignCenter 64 | 65 | 66 | 67 | 68 | 69 | 50 70 | 150 71 | 211 72 | 41 73 | 74 | 75 | 76 | 77 | 微软雅黑 78 | 10 79 | 75 80 | true 81 | 82 | 83 | 84 | QPushButton { 85 | border: 1px solid #8f8f91; 86 | border-radius: 10px; 87 | background-color: rgb(66,133,244); 88 | min-width: 80px; 89 | color:#FFFFFF; 90 | } 91 | 92 | QPushButton:hover{ 93 | border: 1px solid #8f8f91; 94 | border-radius: 3px; 95 | background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,stop: 0 #6265F4, stop: 1 #3265F4); 96 | min-width: 80px; 97 | } 98 | 99 | QPushButton :pressed{ 100 | border: 1px solid #8f8f91; 101 | border-radius: 2px; 102 | background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,stop: 0 #5295F4, stop: 1 #4285F4); 103 | min-width: 80px; 104 | color:#FFFFFF; 105 | } 106 | 107 | 108 | 109 | 110 | 111 | Load Rugosity Data 112 | 113 | 114 | 115 | 116 | 117 | 50 118 | 50 119 | 211 120 | 41 121 | 122 | 123 | 124 | 125 | 微软雅黑 126 | 10 127 | 75 128 | true 129 | 130 | 131 | 132 | QPushButton { 133 | border: 1px solid #8f8f91; 134 | border-radius: 10px; 135 | background-color: rgb(194,67,53); 136 | min-width: 80px; 137 | color:#FFFFFF; 138 | } 139 | 140 | QPushButton:hover{ 141 | border: 1px solid #8f8f91; 142 | border-radius: 3px; 143 | background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,stop: 0 rgb(180,110,110), stop: 1 rgb(110,10,10)); 144 | min-width: 80px; 145 | } 146 | 147 | QPushButton :pressed{ 148 | border: 1px solid #8f8f91; 149 | border-radius: 2px; 150 | background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,stop: 0 #4E6060, stop: 1 #1B1B1B); 151 | min-width: 80px; 152 | color:#FFFFFF; 153 | } 154 | 155 | 156 | 157 | 158 | 159 | Open PLY Mesh 160 | 161 | 162 | 163 | 164 | 165 | 50 166 | 100 167 | 211 168 | 41 169 | 170 | 171 | 172 | 173 | 微软雅黑 174 | 10 175 | 75 176 | true 177 | 178 | 179 | 180 | QPushButton { 181 | border: 1px solid #8f8f91; 182 | border-radius: 10px; 183 | background-color: rgb(66,133,244); 184 | min-width: 80px; 185 | color:#FFFFFF; 186 | } 187 | 188 | QPushButton:hover{ 189 | border: 1px solid #8f8f91; 190 | border-radius: 3px; 191 | background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,stop: 0 #6265F4, stop: 1 #3265F4); 192 | min-width: 80px; 193 | } 194 | 195 | QPushButton :pressed{ 196 | border: 1px solid #8f8f91; 197 | border-radius: 2px; 198 | background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,stop: 0 #5295F4, stop: 1 #4285F4); 199 | min-width: 80px; 200 | color:#FFFFFF; 201 | } 202 | 203 | 204 | 205 | 206 | 207 | Save Rugosity Data 208 | 209 | 210 | 211 | 212 | 213 | 40 214 | 208 215 | 251 216 | 31 217 | 218 | 219 | 220 | 221 | 微软雅黑 222 | 10 223 | 224 | 225 | 226 | Debug: BVH Structure 227 | 228 | 229 | 230 | 231 | 232 | 40 233 | 270 234 | 251 235 | 31 236 | 237 | 238 | 239 | 240 | 微软雅黑 241 | 10 242 | 243 | 244 | 245 | Inverse Y 246 | 247 | 248 | 249 | 250 | 251 | 40 252 | 240 253 | 251 254 | 31 255 | 256 | 257 | 258 | 259 | 微软雅黑 260 | 10 261 | 262 | 263 | 264 | WireFrame / Shaded 265 | 266 | 267 | 268 | 269 | 270 | 10 271 | 320 272 | 51 273 | 21 274 | 275 | 276 | 277 | 278 | 微软雅黑 279 | 11 280 | 75 281 | true 282 | 283 | 284 | 285 | Scale: 286 | 287 | 288 | 289 | 290 | 291 | 10 292 | 350 293 | 61 294 | 21 295 | 296 | 297 | 298 | 299 | 微软雅黑 300 | 11 301 | 75 302 | true 303 | 304 | 305 | 306 | Baseline: 307 | 308 | 309 | 310 | 311 | 312 | 80 313 | 350 314 | 91 315 | 22 316 | 317 | 318 | 319 | 320 | 微软雅黑 321 | 10 322 | 323 | 324 | 325 | 3 326 | 327 | 328 | 0.001000000000000 329 | 330 | 331 | 10000.000000000000000 332 | 333 | 334 | 233.000000000000000 335 | 336 | 337 | 338 | 339 | 340 | 80 341 | 320 342 | 91 343 | 22 344 | 345 | 346 | 347 | 348 | 微软雅黑 349 | 10 350 | 351 | 352 | 353 | 3 354 | 355 | 356 | 0.001000000000000 357 | 358 | 359 | 10000.000000000000000 360 | 361 | 362 | 1.000000000000000 363 | 364 | 365 | 366 | 367 | 368 | 170 369 | 319 370 | 121 371 | 20 372 | 373 | 374 | 375 | 376 | 微软雅黑 377 | 11 378 | 379 | 380 | 381 | m = 1 unit len 382 | 383 | 384 | 385 | 386 | 387 | 388 | -20 389 | 320 390 | 431 391 | 271 392 | 393 | 394 | 395 | 396 | 397 | 398 | 30 399 | 100 400 | 431 401 | 271 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 0 410 | 0 411 | 787 412 | 17 413 | 414 | 415 | 416 | 417 | 418 | TopToolBarArea 419 | 420 | 421 | false 422 | 423 | 424 | 425 | 426 | 427 | 428 | QtCharts::QChartView 429 | QGraphicsView 430 |
qchartview.h
431 |
432 |
433 | 434 | 435 |
436 | -------------------------------------------------------------------------------- /CoralRugosity_Zhihao/utility/kdtree.h: -------------------------------------------------------------------------------- 1 | #ifndef _BJ_KD_TREE_H 2 | #define _BJ_KD_TREE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | template 10 | class KDNode 11 | { 12 | public: 13 | typedef KDNode ChildType; 14 | 15 | KDNode(size_t ind) : treeIndex(ind) {} 16 | size_t treeIndex; // 顶点在list中的位置 17 | std::unique_ptr leftChild, rightChild; 18 | }; 19 | 20 | 21 | template > 22 | class KDTree 23 | { 24 | public: 25 | KDTree() {} 26 | KDTree(const PointArray& pointsIn) 27 | { 28 | buildTree(pointsIn); 29 | } 30 | 31 | void buildTree(const PointArray& pointsIn); 32 | void dumpTreeInorder(); 33 | 34 | template 35 | void inorderTraversal(F func); 36 | 37 | std::vector getPointsWithinCube(PointType testPoint, double radius); 38 | size_t findMin(int dimension); 39 | 40 | void dumpNode(size_t i) 41 | { 42 | std::cout << points[i] << std::endl; 43 | } 44 | 45 | void deletePoint(size_t nodeIndex); 46 | PointType getPoint(size_t nodeIndex) 47 | { 48 | return points[nodeIndex]; 49 | } 50 | void insertPoint(const PointType& p); 51 | 52 | private: 53 | std::unique_ptr > root; 54 | PointArray points; 55 | std::vector pointIndeces; 56 | 57 | template 58 | std::unique_ptr > buildSubtree( std::vector::iterator begin, 59 | std::vector::iterator end); 60 | 61 | 62 | 63 | 64 | template 65 | void dumpSubtree(std::unique_ptr >& node); 66 | 67 | template 68 | void getPointsWithinCubeSubtree(PointType testPoint, double queryRange[2*PointType::dimension], 69 | std::unique_ptr >& node, 70 | std::vector& ret); 71 | 72 | static bool pointInRange(PointType testPoint, double queryRange[2*PointType::dimension]) 73 | { 74 | for(int i = 0; i < PointType::dimension; ++i) 75 | { 76 | if(testPoint.getDimension(i) < queryRange[2*i] || testPoint.getDimension(i) > queryRange[2*i +1]) 77 | { 78 | return false; 79 | } 80 | } 81 | return true; 82 | } 83 | 84 | template 85 | size_t findMinSubtree(int dimension, 86 | std::unique_ptr >& node); 87 | 88 | template 89 | std::unique_ptr > 90 | deleteFromSubtree(size_t nodeIndex, 91 | std::unique_ptr >& node); 92 | 93 | template 94 | void inorderTraversalSubtree(F func, 95 | std::unique_ptr >& node); 96 | 97 | 98 | template 99 | std::unique_ptr > 100 | insertPointSubtree(std::unique_ptr >& node, 101 | size_t pointIndex); 102 | 103 | }; 104 | 105 | 106 | template 107 | void KDTree::buildTree(const PointArray& pointsIn) 108 | { 109 | points = pointsIn; 110 | pointIndeces.resize(points.size()); 111 | std::iota(begin(pointIndeces), end(pointIndeces), 0); 112 | root = buildSubtree<0>(begin(pointIndeces), end(pointIndeces)); 113 | } 114 | 115 | template 116 | template 117 | std::unique_ptr > 118 | KDTree::buildSubtree(std::vector::iterator begin, 119 | std::vector::iterator end) 120 | { 121 | 122 | auto rangeSize = std::distance(begin, end); 123 | 124 | if(rangeSize == 0) 125 | { 126 | return std::unique_ptr >(nullptr); 127 | } 128 | else 129 | { 130 | 131 | std::sort(begin, end, 132 | [this]( size_t a, size_t b) 133 | { 134 | return points[a].getDimension(SplitDimension) < points[b].getDimension(SplitDimension); 135 | }); 136 | auto median = begin + rangeSize/2; 137 | while(median != begin && 138 | points[*(median)].getDimension(SplitDimension) == 139 | points[*(median - 1)].getDimension(SplitDimension)) 140 | { 141 | --median; 142 | } 143 | auto ret = std::unique_ptr > 144 | ( new KDNode(*median)); 145 | 146 | ret->leftChild = buildSubtree<(SplitDimension +1)%PointType::dimension>(begin, median); 147 | ret->rightChild = buildSubtree<(SplitDimension +1)%PointType::dimension>(median + 1, end); 148 | 149 | return ret; 150 | 151 | } 152 | } 153 | 154 | template 155 | void KDTree::dumpTreeInorder() 156 | { 157 | dumpSubtree<0>(root); 158 | } 159 | 160 | template 161 | template 162 | void KDTree::dumpSubtree(std::unique_ptr >& node) 163 | { 164 | if(node->leftChild) 165 | { 166 | std::cout << "dumping left: " << std::endl; 167 | dumpSubtree<(SplitDimension +1)%PointType::dimension %3>(node->leftChild); 168 | } 169 | std::cout << "dumping this: " << std::endl; 170 | std::cout << node->treeIndex << ": " << points[node->treeIndex] << std::endl; 171 | if(node->rightChild) 172 | { 173 | std::cout << "dumping right: " << std::endl; 174 | dumpSubtree<(SplitDimension +1)%PointType::dimension %3>(node->rightChild); 175 | } 176 | } 177 | 178 | template 179 | std::vector KDTree::getPointsWithinCube(PointType testPoint, double radius) 180 | { 181 | 182 | double queryRange[2*PointType::dimension]; 183 | for(auto i = 0; i < PointType::dimension; ++i) 184 | { 185 | queryRange[2*i] = testPoint.getDimension(i) - radius; 186 | queryRange[2*i +1] = testPoint.getDimension(i) + radius; 187 | } 188 | 189 | 190 | std::vector ret; 191 | getPointsWithinCubeSubtree<0>(testPoint, queryRange, root, ret); 192 | 193 | return ret; 194 | } 195 | 196 | template 197 | template 198 | void KDTree::getPointsWithinCubeSubtree(PointType testPoint, 199 | double queryRange[2*PointType::dimension], 200 | std::unique_ptr >& node, 201 | std::vector& ret) 202 | { 203 | 204 | if(node == nullptr) 205 | return; 206 | 207 | auto nodePoint = points[node->treeIndex]; 208 | if(pointInRange(nodePoint, queryRange)) 209 | { 210 | ret.push_back(node->treeIndex); 211 | } 212 | if(nodePoint.getDimension(SplitDimension) >= queryRange[2*SplitDimension]) 213 | { 214 | // 查询左子树范围 215 | //std::cout << "recurse left" << std::endl; 216 | getPointsWithinCubeSubtree<(SplitDimension +1)%PointType::dimension>(testPoint, 217 | queryRange, 218 | node->leftChild, 219 | ret); 220 | } 221 | if(nodePoint.getDimension(SplitDimension) <= queryRange[2*SplitDimension + 1]) 222 | { 223 | //查询右子树范围 224 | //std::cout << "recurse right" << std::endl; 225 | getPointsWithinCubeSubtree<(SplitDimension +1)%PointType::dimension>(testPoint, 226 | queryRange, 227 | node->rightChild, 228 | ret); 229 | } 230 | } 231 | 232 | template 233 | size_t KDTree::findMin(int dimension) 234 | { 235 | return findMinSubtree<0>(dimension, root); 236 | } 237 | 238 | 239 | template 240 | template 241 | size_t KDTree::findMinSubtree(int dimension, 242 | std::unique_ptr >& node) 244 | { 245 | if(SplitDimension == dimension) 246 | { 247 | if(node->leftChild == nullptr) 248 | { 249 | return node->treeIndex; 250 | } 251 | else 252 | { 253 | return findMinSubtree<(SplitDimension+1)%PointType::dimension>(dimension, 254 | node->leftChild); 255 | } 256 | } 257 | else 258 | { 259 | size_t leftMin = 123456, rightMin= 123456; 260 | if(node->leftChild) 261 | { 262 | leftMin = findMinSubtree<(SplitDimension+1)%PointType::dimension>(dimension, 263 | node->leftChild); 264 | } 265 | if(node->rightChild) 266 | { 267 | rightMin = findMinSubtree<(SplitDimension+1)%PointType::dimension>(dimension, 268 | node->rightChild); 269 | } 270 | 271 | auto nodeValue = points[node->treeIndex].getDimension(dimension); 272 | if(node->leftChild && 273 | points[leftMin].getDimension(dimension) < 274 | nodeValue) 275 | { 276 | 277 | if(node->rightChild) 278 | { 279 | return (points[leftMin].getDimension(dimension) < 280 | points[rightMin].getDimension(dimension)) ? leftMin : rightMin; 281 | 282 | } 283 | else 284 | { 285 | return leftMin; 286 | } 287 | } 288 | else if(node->rightChild && 289 | points[rightMin].getDimension(dimension) < 290 | nodeValue) 291 | { 292 | return rightMin; 293 | } 294 | else 295 | { 296 | return node->treeIndex; 297 | } 298 | } 299 | } 300 | 301 | template 302 | void KDTree::deletePoint(size_t nodeIndex) 303 | { 304 | 305 | root = deleteFromSubtree<0>(nodeIndex, root); 306 | } 307 | 308 | template 309 | template 310 | std::unique_ptr > 311 | KDTree::deleteFromSubtree(size_t nodeIndex, 312 | std::unique_ptr >& node) 313 | { 314 | 315 | constexpr size_t nextDimension = (SplitDimension +1)%PointType::dimension; 316 | 317 | if(node->treeIndex == nodeIndex) 318 | { 319 | if(node->rightChild) 320 | { 321 | auto rightMin = findMinSubtree(SplitDimension, node->rightChild); 322 | node->treeIndex = rightMin; 323 | node->rightChild = deleteFromSubtree(rightMin, 324 | node->rightChild); 325 | } 326 | else if(node->leftChild) 327 | { 328 | auto leftMin = findMinSubtree(SplitDimension, node->leftChild); 329 | node->treeIndex = leftMin; 330 | node->rightChild = deleteFromSubtree(leftMin, 331 | node->leftChild); 332 | node->leftChild = nullptr; 333 | } 334 | else 335 | { 336 | return nullptr; 337 | } 338 | } 339 | else if(points[nodeIndex].getDimension(SplitDimension) < 340 | points[node->treeIndex].getDimension(SplitDimension)) 341 | { 342 | 343 | node->leftChild = deleteFromSubtree(nodeIndex, 344 | node->leftChild); 345 | } 346 | else 347 | { 348 | node->rightChild = deleteFromSubtree(nodeIndex, 349 | node->rightChild); 350 | } 351 | return std::move(node); 352 | 353 | } 354 | 355 | template 356 | template 357 | void KDTree::inorderTraversal(F func) 358 | { 359 | 360 | inorderTraversalSubtree<0, F>(func, root); 361 | 362 | } 363 | 364 | template 365 | template 366 | void KDTree::inorderTraversalSubtree(F func, 367 | std::unique_ptr >& node) 369 | { 370 | auto constexpr nextDimension = (SplitDimension +1)%PointType::dimension; 371 | if(node->leftChild) 372 | { 373 | inorderTraversalSubtree(func, node->leftChild); 374 | } 375 | func(points[node->treeIndex]); 376 | if(node->rightChild) 377 | { 378 | inorderTraversalSubtree(func, node->rightChild); 379 | } 380 | 381 | } 382 | 383 | template 384 | void KDTree::insertPoint(const PointType& point) 385 | { 386 | points.push_back(point); 387 | root = insertPointSubtree<0>(root, points.size() -1); 388 | } 389 | 390 | template 391 | template 392 | std::unique_ptr > 393 | KDTree::insertPointSubtree(std::unique_ptr >& node, 394 | size_t pointIndex) 395 | { 396 | 397 | auto constexpr nextDimension = (SplitDimension +1)%PointType::dimension; 398 | 399 | if(node == nullptr) 400 | { 401 | return std::unique_ptr > (new KDNode(pointIndex)); 402 | } 403 | else if (points[pointIndex].getDimension(SplitDimension) < 404 | points[node->treeIndex].getDimension(SplitDimension)) 405 | { 406 | node->leftChild = insertPointSubtree(node->leftChild, pointIndex); 407 | } 408 | else 409 | { 410 | node->rightChild = insertPointSubtree(node->rightChild, pointIndex); 411 | } 412 | return std::move(node); 413 | 414 | } 415 | #endif //_BJ_KD_TREE_H 416 | -------------------------------------------------------------------------------- /CoralRugosity_Zhihao/object_Mesh.cpp: -------------------------------------------------------------------------------- 1 | #include "object.h" 2 | #define VERTEX_ATTRIBUTE 0 3 | #define COLOUR_ATTRIBUTE 1 4 | #define NORMAL_ATTRIBUTE 2 5 | #define TEXTURE_ATTRIBUTE 3 6 | #include 7 | 8 | bool isUsePLYFormatTexcoords = false; 9 | 10 | Object::Object() 11 | { 12 | 13 | } 14 | 15 | void Object::SetTextures(const QString &filepath) 16 | { 17 | if(m_texture!=nullptr) 18 | { 19 | m_texture->release(); 20 | m_texture->destroy(); 21 | if(m_texture!=nullptr) delete m_texture; 22 | } 23 | 24 | QImage img(filepath); 25 | m_texture = new QOpenGLTexture(img); 26 | 27 | m_texture->setMinificationFilter(QOpenGLTexture::Nearest); 28 | m_texture->setMagnificationFilter(QOpenGLTexture::Linear); 29 | } 30 | 31 | void Object::InverseY_Axis() 32 | { 33 | for(int i=0; im_meshVBO.vbo.isCreated()) this->m_meshVBO.vbo.release(); 48 | 49 | this->m_vertices.clear(); 50 | this->m_normals.clear(); 51 | this->m_colors.clear(); 52 | this->m_texcoords.clear(); 53 | this->m_faceids.clear(); 54 | this->m_dictPtToFace.clear(); 55 | 56 | this->octreeList.clear(); 57 | 58 | if(m_texture!=nullptr) 59 | { 60 | delete m_texture; 61 | } 62 | m_texture = nullptr; 63 | 64 | isUsePLYFormatTexcoords = false; 65 | } 66 | 67 | 68 | void Object::DrawMeshVBO(QOpenGLShaderProgram *&program, const QMatrix4x4 &modelMat) 69 | { 70 | 71 | if(this->m_meshVBO.count <=0 ||!this->m_meshVBO.vbo.isCreated()) 72 | return; 73 | 74 | program->setUniformValue("mat_model",modelMat); 75 | 76 | m_meshVBO.vbo.bind(); 77 | 78 | program->enableAttributeArray(VERTEX_ATTRIBUTE); 79 | program->enableAttributeArray(NORMAL_ATTRIBUTE); 80 | if(this->hasTexture()) program->enableAttributeArray(TEXTURE_ATTRIBUTE); 81 | else if(this->hasColor()) program->enableAttributeArray(COLOUR_ATTRIBUTE); 82 | 83 | if(this->hasTexture() == false && this->hasColor() == false) 84 | { 85 | program->setAttributeBuffer(VERTEX_ATTRIBUTE, GL_FLOAT, 0, 3,6*sizeof(GLfloat)); 86 | program->setAttributeBuffer(NORMAL_ATTRIBUTE, GL_FLOAT, 3*sizeof(GLfloat),3,6*sizeof(GLfloat)); 87 | } 88 | else 89 | { 90 | program->setAttributeBuffer(VERTEX_ATTRIBUTE, GL_FLOAT, 0, 3,9*sizeof(GLfloat)); 91 | program->setAttributeBuffer(NORMAL_ATTRIBUTE, GL_FLOAT, 3*sizeof(GLfloat),3,9*sizeof(GLfloat)); 92 | if(this->hasTexture()) 93 | program->setAttributeBuffer(TEXTURE_ATTRIBUTE, GL_FLOAT, 6*sizeof(GLfloat),3,9*sizeof(GLfloat)); 94 | else if(this->hasColor()) 95 | program->setAttributeBuffer(COLOUR_ATTRIBUTE, GL_FLOAT, 6*sizeof(GLfloat),3,9*sizeof(GLfloat)); 96 | 97 | } 98 | 99 | glDrawArrays(GL_TRIANGLES,0,this->m_meshVBO.count); 100 | 101 | } 102 | 103 | void Object::CreateMeshVBO(const std::string& filepath) 104 | { 105 | this->ResetAll(); 106 | 107 | // 读取二进制数据 108 | std::unique_ptr file_stream; 109 | file_stream.reset(new std::ifstream(filepath, std::ios::binary)); 110 | 111 | // 文件不存在 112 | if (!file_stream || file_stream->fail()) 113 | return; 114 | 115 | file_stream->seekg(0, std::ios::end); 116 | const float size_mb = file_stream->tellg() * float(1e-6); 117 | file_stream->seekg(0, std::ios::beg); 118 | 119 | // 创建PlyFile对象 120 | tinyply::PlyFile file; 121 | file.parse_header(*file_stream); 122 | 123 | std::shared_ptr vertices_byte, normals_byte, colors_byte, texcoords_byte, faces_byte, tripstrip, plyTexcoords_byte; 124 | try { vertices_byte = file.request_properties_from_element("vertex", { "x", "y", "z" }); } 125 | catch (const std::exception & e) { std::cerr << "tinyply exception: " << e.what() << std::endl; } 126 | try { normals_byte = file.request_properties_from_element("vertex", { "nx", "ny", "nz" }); } 127 | catch (const std::exception & e) { std::cerr << "tinyply exception: " << e.what() << std::endl; } 128 | try { colors_byte = file.request_properties_from_element("vertex", { "red", "green", "blue", "alpha" }); } 129 | catch (const std::exception & e) { std::cerr << "tinyply exception: " << e.what() << std::endl; } 130 | if (colors_byte == nullptr) 131 | { 132 | try { colors_byte = file.request_properties_from_element("vertex", { "r", "g", "b", "a" }); } 133 | catch (const std::exception & e) { std::cerr << "tinyply exception: " << e.what() << std::endl; } 134 | } 135 | try { texcoords_byte = file.request_properties_from_element("vertex", { "u", "v" }); } 136 | catch (const std::exception & e) { std::cerr << "tinyply exception: " << e.what() << std::endl; } 137 | try { faces_byte = file.request_properties_from_element("face", { "vertex_indices" }, 3); } 138 | catch (const std::exception & e) { std::cerr << "tinyply exception: " << e.what() << std::endl; } 139 | try { plyTexcoords_byte = file.request_properties_from_element("face", { "texcoord" }, 6); } 140 | catch (const std::exception & e) { std::cerr << "tinyply exception: " << e.what() << std::endl; } 141 | 142 | 143 | file.read(*file_stream); 144 | 145 | if (vertices_byte) std::cout << "\tRead " << vertices_byte->count << " total vertices "<< std::endl; 146 | if (normals_byte) std::cout << "\tRead " << normals_byte->count << " total vertex normals " << std::endl; 147 | if (colors_byte) std::cout << "\tRead " << colors_byte->count << " total vertex colors " << std::endl; 148 | if (texcoords_byte) std::cout << "\tRead " << texcoords_byte->count << " total vertex texcoords " << std::endl; 149 | if (faces_byte) std::cout << "\tRead " << faces_byte->count << " total faces (triangles) " << std::endl; 150 | if (tripstrip) std::cout << "\tRead " << (tripstrip->buffer.size_bytes() / tinyply::PropertyTable[tripstrip->t].stride) << " total indicies (tristrip) " << std::endl; 151 | if (plyTexcoords_byte) std::cout << "\tRead " << (plyTexcoords_byte->buffer.size_bytes() / tinyply::PropertyTable[plyTexcoords_byte->t].stride) << " total PLY texcoords" << std::endl; 152 | 153 | std::vector _pos, _norms,_colors; 154 | std::vector _texcoords; 155 | std::vector _plyTexcoords; 156 | std::vector _tris; 157 | if (vertices_byte) _pos.resize(vertices_byte->count); 158 | if (normals_byte) _norms.resize(normals_byte->count); 159 | if (colors_byte) _colors.resize(colors_byte->count); 160 | if (faces_byte) _tris.resize(faces_byte->count); 161 | if (texcoords_byte) { 162 | _texcoords.resize(texcoords_byte->count); 163 | isUsePLYFormatTexcoords = false; 164 | } 165 | if(_texcoords.size() == 0 && plyTexcoords_byte){ 166 | _plyTexcoords.resize(plyTexcoords_byte->count); 167 | isUsePLYFormatTexcoords = true; 168 | } 169 | 170 | // _pos 171 | std::memcpy(_pos.data(), vertices_byte->buffer.get(), (size_t)( vertices_byte->buffer.size_bytes())); 172 | 173 | if(normals_byte!=nullptr) 174 | { 175 | std::memcpy(_norms.data(), normals_byte->buffer.get(), (size_t)( normals_byte->buffer.size_bytes())); 176 | } 177 | if(colors_byte!=nullptr) 178 | { 179 | std::memcpy(_colors.data(), colors_byte->buffer.get(), (size_t)( colors_byte->buffer.size_bytes())); 180 | } 181 | if(texcoords_byte!=nullptr) 182 | { 183 | std::memcpy(_texcoords.data(), texcoords_byte->buffer.get(), (size_t)( texcoords_byte->buffer.size_bytes())); 184 | } 185 | if(plyTexcoords_byte!=nullptr) 186 | { 187 | std::memcpy(_plyTexcoords.data(), plyTexcoords_byte->buffer.get(), (size_t)( plyTexcoords_byte->buffer.size_bytes())); 188 | } 189 | if(faces_byte!=nullptr) 190 | { 191 | std::memcpy(_tris.data(), faces_byte->buffer.get(), (size_t)( faces_byte->buffer.size_bytes())); 192 | } 193 | 194 | // 读取到数组中去 195 | for(int i=0; i<_pos.size(); ++i) 196 | m_vertices.push_back(QVector3D(_pos[i].x,_pos[i].y,_pos[i].z)); 197 | for(int i=0; i<_colors.size(); ++i) 198 | m_colors.push_back(QVector3D(_colors[i].x,_colors[i].y,_colors[i].z)); 199 | for(int i=0; i<_texcoords.size(); ++i) 200 | m_texcoords.push_back(QVector2D(_texcoords[i].x,_texcoords[i].y)); 201 | for(int i=0; i<_tris.size(); ++i) 202 | m_faceids.push_back(_tris[i]); 203 | 204 | if(m_texcoords.size() == 0) 205 | { 206 | for(int i=0; i<_plyTexcoords.size(); ++i) 207 | { 208 | 209 | //qDebug()<<_plyTexcoords[i].u1<<_plyTexcoords[i].v1 << _plyTexcoords[i].u2<<_plyTexcoords[i].v2<<_plyTexcoords[i].u3<<_plyTexcoords[i].v3; 210 | m_texcoords.push_back(QVector2D(_plyTexcoords[i].u1,_plyTexcoords[i].v1)); 211 | m_texcoords.push_back(QVector2D(_plyTexcoords[i].u2,_plyTexcoords[i].v2)); 212 | m_texcoords.push_back(QVector2D(_plyTexcoords[i].u3,_plyTexcoords[i].v3)); 213 | } 214 | } 215 | 216 | if(_norms.size() > 0) 217 | { 218 | for(int i=0; i<_norms.size(); ++i) 219 | m_normals.push_back(QVector3D(_norms[i].x,_norms[i].y,_norms[i].z)); 220 | } 221 | else 222 | { 223 | this->m_normals.fill(QVector3D(0,0,0), m_vertices.size()); 224 | for(int i=0; im_faceids.size(); ++i) 225 | { 226 | int id1 = m_faceids[i].x; 227 | int id2 = m_faceids[i].y; 228 | int id3 = m_faceids[i].z; 229 | 230 | QVector3D v1 = m_vertices[id1]; // v1 231 | QVector3D v2 = m_vertices[id2]; // v2 232 | QVector3D v3 = m_vertices[id3]; // v3 233 | 234 | QVector3D e1 = v2 - v1; 235 | QVector3D e2 = v2 - v3; 236 | 237 | QVector3D normal = QVector3D::crossProduct(e1, e2); 238 | if(normal.y()<0) 239 | normal = -normal; 240 | 241 | m_normals[id1] += normal; 242 | m_normals[id2] += normal; 243 | m_normals[id3] += normal; 244 | } 245 | for(int i=0; iInverseY_Axis(); 264 | 265 | // 镜像翻转纹理图片 266 | this->FlipTexcoords(); 267 | 268 | QString defaultTexturePath = GlobalTools::checkDefaultTextureExists(QString::fromStdString(filepath)); 269 | qDebug()<<"查找默认纹理文件:" << defaultTexturePath; 270 | if(defaultTexturePath != "") 271 | this->SetTextures(defaultTexturePath); 272 | 273 | // 生成VBO 274 | this->UpdateMeshs(); 275 | } 276 | 277 | void Object::UpdateMeshs() 278 | { 279 | QVector data; 280 | for(int i=0; im_faceids.size(); ++i) 281 | { 282 | if(i%(int)(m_faceids.size()/10.0f) == 0) 283 | qDebug()<<"加载进度"<<(int)(i/(m_faceids.size()/10.0f))*10 <<"%"; 284 | 285 | int id1 = m_faceids[i].x; 286 | int id2 = m_faceids[i].y; 287 | int id3 = m_faceids[i].z; 288 | 289 | QVector3D v1 = m_vertices[id1]; // v1 290 | QVector3D v2 = m_vertices[id2]; // v2 291 | QVector3D v3 = m_vertices[id3]; // v3 292 | 293 | QVector3D n1,n2,n3; 294 | n1 = m_normals[id1]; // n1 295 | n2 = m_normals[id2]; // n2 296 | n3 = m_normals[id3]; // n3 297 | 298 | 299 | QVector3D c1,c2,c3; 300 | if(this->hasColor()) 301 | { 302 | c1 = m_colors[id1]; 303 | c2 = m_colors[id2]; 304 | c3 = m_colors[id3]; 305 | } 306 | 307 | QVector2D t1,t2,t3; 308 | if(this->hasTexture() && isUsePLYFormatTexcoords == false) 309 | { 310 | t1 = m_texcoords[id1]; 311 | t2 = m_texcoords[id2]; 312 | t3 = m_texcoords[id3]; 313 | } 314 | else if(this->hasTexture() && isUsePLYFormatTexcoords == true) 315 | { 316 | 317 | t1 = m_texcoords[i*3+0]; 318 | t2 = m_texcoords[i*3+1]; 319 | t3 = m_texcoords[i*3+2]; 320 | // qDebug()<hasTexture()) { 326 | data.push_back(t1.x()); data.push_back(t1.y()); data.push_back(1.0f); 327 | } 328 | else if(this->hasColor()){ 329 | data.push_back(c1.x()); data.push_back(c1.y()); data.push_back(c1.z()); 330 | } 331 | 332 | data.push_back(v2.x()); data.push_back(v2.y()); data.push_back(v2.z()); 333 | data.push_back(n2.x()); data.push_back(n2.y()); data.push_back(n2.z()); 334 | if(this->hasTexture()) { 335 | data.push_back(t2.x()); data.push_back(t2.y()); data.push_back(1.0f); 336 | } 337 | else if(this->hasColor()){ 338 | data.push_back(c2.x()); data.push_back(c2.y()); data.push_back(c2.z()); 339 | } 340 | 341 | data.push_back(v3.x()); data.push_back(v3.y()); data.push_back(v3.z()); 342 | data.push_back(n3.x()); data.push_back(n3.y()); data.push_back(n3.z()); 343 | if(this->hasTexture()) { 344 | data.push_back(t3.x()); data.push_back(t3.y()); data.push_back(1.0f); 345 | } 346 | else if(this->hasColor()){ 347 | data.push_back(c3.x()); data.push_back(c3.y()); data.push_back(c3.z()); 348 | } 349 | } 350 | 351 | 352 | m_meshVBO.count = data.size()/6; 353 | if(this->hasTexture()) m_meshVBO.count = data.size()/9; 354 | else if(this->hasColor()) m_meshVBO.count = data.size()/9; 355 | 356 | qDebug()<<"创建"< data; 368 | 369 | for(int i=0; im_meshVBO.count <=0 ||!this->m_meshVBO.vbo.isCreated()) 385 | return; 386 | 387 | program->setUniformValue("mat_model",modelMat); 388 | 389 | m_meshVBO.vbo.bind(); 390 | 391 | program->enableAttributeArray(VERTEX_ATTRIBUTE); 392 | program->setAttributeBuffer(VERTEX_ATTRIBUTE, GL_FLOAT, 0, 3, 3*sizeof(GLfloat)); 393 | 394 | 395 | glDrawArrays(GL_LINE_STRIP,0, this->m_meshVBO.count); 396 | } 397 | 398 | 399 | -------------------------------------------------------------------------------- /CoralRugosity_Zhihao/object.cpp: -------------------------------------------------------------------------------- 1 | #include "object.h" 2 | #define VERTEX_ATTRIBUTE 0 3 | #define COLOUR_ATTRIBUTE 1 4 | #define NORMAL_ATTRIBUTE 2 5 | #define TEXTURE_ATTRIBUTE 3 6 | #include 7 | 8 | bool isUsePLYFormatTexcoords = false; 9 | 10 | Object::Object() 11 | { 12 | 13 | } 14 | 15 | void Object::SetTextures(const QString &filepath) 16 | { 17 | if(m_texture!=nullptr) 18 | { 19 | m_texture->release(); 20 | m_texture->destroy(); 21 | if(m_texture!=nullptr) delete m_texture; 22 | } 23 | 24 | QImage img(filepath); 25 | m_texture = new QOpenGLTexture(img); 26 | 27 | m_texture->setMinificationFilter(QOpenGLTexture::Nearest); 28 | m_texture->setMagnificationFilter(QOpenGLTexture::Linear); 29 | } 30 | 31 | void Object::InverseY_Axis() 32 | { 33 | for(int i=0; im_pointVBO.vbo.isCreated()) this->m_pointVBO.vbo.release(); 48 | if(this->m_lineVBO.vbo.isCreated()) this->m_lineVBO.vbo.release(); 49 | if(this->m_meshVBO.vbo.isCreated()) this->m_meshVBO.vbo.release(); 50 | 51 | this->m_vertices.clear(); 52 | this->m_normals.clear(); 53 | this->m_colors.clear(); 54 | this->m_texcoords.clear(); 55 | this->m_faceids.clear(); 56 | 57 | if(m_texture!=nullptr) 58 | { 59 | delete m_texture; 60 | } 61 | m_texture = nullptr; 62 | 63 | isUsePLYFormatTexcoords = false; 64 | } 65 | 66 | 67 | void Object::DrawMeshVBO(QOpenGLShaderProgram *&program, const QMatrix4x4 &modelMat) 68 | { 69 | 70 | if(this->m_meshVBO.count <=0 ||!this->m_meshVBO.vbo.isCreated()) 71 | return; 72 | 73 | program->setUniformValue("mat_model",modelMat); 74 | 75 | m_meshVBO.vbo.bind(); 76 | 77 | program->enableAttributeArray(VERTEX_ATTRIBUTE); 78 | program->enableAttributeArray(NORMAL_ATTRIBUTE); 79 | if(this->hasTexture()) program->enableAttributeArray(TEXTURE_ATTRIBUTE); 80 | else if(this->hasColor()) program->enableAttributeArray(COLOUR_ATTRIBUTE); 81 | 82 | if(this->hasTexture() == false && this->hasColor() == false) 83 | { 84 | program->setAttributeBuffer(VERTEX_ATTRIBUTE, GL_FLOAT, 0, 3,6*sizeof(GLfloat)); 85 | program->setAttributeBuffer(NORMAL_ATTRIBUTE, GL_FLOAT, 3*sizeof(GLfloat),3,6*sizeof(GLfloat)); 86 | } 87 | else 88 | { 89 | program->setAttributeBuffer(VERTEX_ATTRIBUTE, GL_FLOAT, 0, 3,9*sizeof(GLfloat)); 90 | program->setAttributeBuffer(NORMAL_ATTRIBUTE, GL_FLOAT, 3*sizeof(GLfloat),3,9*sizeof(GLfloat)); 91 | if(this->hasTexture()) 92 | program->setAttributeBuffer(TEXTURE_ATTRIBUTE, GL_FLOAT, 6*sizeof(GLfloat),3,9*sizeof(GLfloat)); 93 | else if(this->hasColor()) 94 | program->setAttributeBuffer(COLOUR_ATTRIBUTE, GL_FLOAT, 6*sizeof(GLfloat),3,9*sizeof(GLfloat)); 95 | 96 | } 97 | 98 | glDrawArrays(GL_TRIANGLES,0,this->m_meshVBO.count); 99 | 100 | } 101 | 102 | void Object::CreateMeshVBO(const std::string& filepath) 103 | { 104 | this->ResetAll(); 105 | 106 | // 读取二进制数据 107 | std::unique_ptr file_stream; 108 | file_stream.reset(new std::ifstream(filepath, std::ios::binary)); 109 | 110 | // 文件不存在 111 | if (!file_stream || file_stream->fail()) 112 | return; 113 | 114 | file_stream->seekg(0, std::ios::end); 115 | const float size_mb = file_stream->tellg() * float(1e-6); 116 | file_stream->seekg(0, std::ios::beg); 117 | 118 | // 创建PlyFile对象 119 | tinyply::PlyFile file; 120 | file.parse_header(*file_stream); 121 | 122 | std::shared_ptr vertices_byte, normals_byte, colors_byte, texcoords_byte, faces_byte, tripstrip, plyTexcoords_byte; 123 | try { vertices_byte = file.request_properties_from_element("vertex", { "x", "y", "z" }); } 124 | catch (const std::exception & e) { std::cerr << "tinyply exception: " << e.what() << std::endl; } 125 | try { normals_byte = file.request_properties_from_element("vertex", { "nx", "ny", "nz" }); } 126 | catch (const std::exception & e) { std::cerr << "tinyply exception: " << e.what() << std::endl; } 127 | try { colors_byte = file.request_properties_from_element("vertex", { "red", "green", "blue", "alpha" }); } 128 | catch (const std::exception & e) { std::cerr << "tinyply exception: " << e.what() << std::endl; } 129 | if (colors_byte == nullptr) 130 | { 131 | try { colors_byte = file.request_properties_from_element("vertex", { "r", "g", "b", "a" }); } 132 | catch (const std::exception & e) { std::cerr << "tinyply exception: " << e.what() << std::endl; } 133 | } 134 | try { texcoords_byte = file.request_properties_from_element("vertex", { "u", "v" }); } 135 | catch (const std::exception & e) { std::cerr << "tinyply exception: " << e.what() << std::endl; } 136 | try { faces_byte = file.request_properties_from_element("face", { "vertex_indices" }, 3); } 137 | catch (const std::exception & e) { std::cerr << "tinyply exception: " << e.what() << std::endl; } 138 | try { plyTexcoords_byte = file.request_properties_from_element("face", { "texcoord" }, 6); } 139 | catch (const std::exception & e) { std::cerr << "tinyply exception: " << e.what() << std::endl; } 140 | 141 | 142 | file.read(*file_stream); 143 | 144 | if (vertices_byte) std::cout << "\tRead " << vertices_byte->count << " total vertices "<< std::endl; 145 | if (normals_byte) std::cout << "\tRead " << normals_byte->count << " total vertex normals " << std::endl; 146 | if (colors_byte) std::cout << "\tRead " << colors_byte->count << " total vertex colors " << std::endl; 147 | if (texcoords_byte) std::cout << "\tRead " << texcoords_byte->count << " total vertex texcoords " << std::endl; 148 | if (faces_byte) std::cout << "\tRead " << faces_byte->count << " total faces (triangles) " << std::endl; 149 | if (tripstrip) std::cout << "\tRead " << (tripstrip->buffer.size_bytes() / tinyply::PropertyTable[tripstrip->t].stride) << " total indicies (tristrip) " << std::endl; 150 | if (plyTexcoords_byte) std::cout << "\tRead " << (plyTexcoords_byte->buffer.size_bytes() / tinyply::PropertyTable[plyTexcoords_byte->t].stride) << " total PLY texcoords" << std::endl; 151 | 152 | std::vector _pos, _norms,_colors; 153 | std::vector _texcoords; 154 | std::vector _plyTexcoords; 155 | std::vector _tris; 156 | if (vertices_byte) _pos.resize(vertices_byte->count); 157 | if (normals_byte) _norms.resize(normals_byte->count); 158 | if (colors_byte) _colors.resize(colors_byte->count); 159 | if (faces_byte) _tris.resize(faces_byte->count); 160 | if (texcoords_byte) { 161 | _texcoords.resize(texcoords_byte->count); 162 | isUsePLYFormatTexcoords = false; 163 | } 164 | if(_texcoords.size() == 0 && plyTexcoords_byte){ 165 | _plyTexcoords.resize(plyTexcoords_byte->count); 166 | isUsePLYFormatTexcoords = true; 167 | } 168 | 169 | // _pos 170 | std::memcpy(_pos.data(), vertices_byte->buffer.get(), (size_t)( vertices_byte->buffer.size_bytes())); 171 | 172 | if(normals_byte!=nullptr) 173 | { 174 | std::memcpy(_norms.data(), normals_byte->buffer.get(), (size_t)( normals_byte->buffer.size_bytes())); 175 | } 176 | if(colors_byte!=nullptr) 177 | { 178 | std::memcpy(_colors.data(), colors_byte->buffer.get(), (size_t)( colors_byte->buffer.size_bytes())); 179 | } 180 | if(texcoords_byte!=nullptr) 181 | { 182 | std::memcpy(_texcoords.data(), texcoords_byte->buffer.get(), (size_t)( texcoords_byte->buffer.size_bytes())); 183 | } 184 | if(plyTexcoords_byte!=nullptr) 185 | { 186 | std::memcpy(_plyTexcoords.data(), plyTexcoords_byte->buffer.get(), (size_t)( plyTexcoords_byte->buffer.size_bytes())); 187 | } 188 | if(faces_byte!=nullptr) 189 | { 190 | std::memcpy(_tris.data(), faces_byte->buffer.get(), (size_t)( faces_byte->buffer.size_bytes())); 191 | } 192 | 193 | // 读取到数组中去 194 | for(int i=0; i<_pos.size(); ++i) 195 | m_vertices.push_back(QVector3D(_pos[i].x,_pos[i].y,_pos[i].z)); 196 | for(int i=0; i<_colors.size(); ++i) 197 | m_colors.push_back(QVector3D(_colors[i].x,_colors[i].y,_colors[i].z)); 198 | for(int i=0; i<_texcoords.size(); ++i) 199 | m_texcoords.push_back(QVector2D(_texcoords[i].x,_texcoords[i].y)); 200 | for(int i=0; i<_tris.size(); ++i) 201 | m_faceids.push_back(_tris[i]); 202 | 203 | if(m_texcoords.size() == 0) 204 | { 205 | for(int i=0; i<_plyTexcoords.size(); ++i) 206 | { 207 | 208 | //qDebug()<<_plyTexcoords[i].u1<<_plyTexcoords[i].v1 << _plyTexcoords[i].u2<<_plyTexcoords[i].v2<<_plyTexcoords[i].u3<<_plyTexcoords[i].v3; 209 | m_texcoords.push_back(QVector2D(_plyTexcoords[i].u1,_plyTexcoords[i].v1)); 210 | m_texcoords.push_back(QVector2D(_plyTexcoords[i].u2,_plyTexcoords[i].v2)); 211 | m_texcoords.push_back(QVector2D(_plyTexcoords[i].u3,_plyTexcoords[i].v3)); 212 | } 213 | } 214 | 215 | if(_norms.size() > 0) 216 | { 217 | for(int i=0; i<_norms.size(); ++i) 218 | m_normals.push_back(QVector3D(_norms[i].x,_norms[i].y,_norms[i].z)); 219 | } 220 | else 221 | { 222 | this->m_normals.fill(QVector3D(0,0,0), m_vertices.size()); 223 | for(int i=0; im_faceids.size(); ++i) 224 | { 225 | int id1 = m_faceids[i].x; 226 | int id2 = m_faceids[i].y; 227 | int id3 = m_faceids[i].z; 228 | 229 | QVector3D v1 = m_vertices[id1]; // v1 230 | QVector3D v2 = m_vertices[id2]; // v2 231 | QVector3D v3 = m_vertices[id3]; // v3 232 | 233 | QVector3D e1 = v2 - v1; 234 | QVector3D e2 = v2 - v3; 235 | 236 | QVector3D normal = QVector3D::crossProduct(e1, e2); 237 | if(normal.y()<0) 238 | normal = -normal; 239 | 240 | m_normals[id1] += normal; 241 | m_normals[id2] += normal; 242 | m_normals[id3] += normal; 243 | } 244 | for(int i=0; iInverseY_Axis(); 263 | 264 | // 镜像翻转纹理图片 265 | this->FlipTexcoords(); 266 | 267 | QString defaultTexturePath = GlobalTools::checkDefaultTextureExists(QString::fromStdString(filepath)); 268 | qDebug()<<"查找默认纹理文件:" << defaultTexturePath; 269 | if(defaultTexturePath != "") 270 | this->SetTextures(defaultTexturePath); 271 | 272 | // 生成VBO 273 | this->UpdateMeshs(); 274 | } 275 | 276 | void Object::UpdateMeshs() 277 | { 278 | QVector data; 279 | for(int i=0; im_faceids.size(); ++i) 280 | { 281 | if(i%(int)(m_faceids.size()/10.0f) == 0) 282 | qDebug()<<"加载进度"<<(int)(i/(m_faceids.size()/10.0f))*10 <<"%"; 283 | 284 | int id1 = m_faceids[i].x; 285 | int id2 = m_faceids[i].y; 286 | int id3 = m_faceids[i].z; 287 | 288 | QVector3D v1 = m_vertices[id1]; // v1 289 | QVector3D v2 = m_vertices[id2]; // v2 290 | QVector3D v3 = m_vertices[id3]; // v3 291 | 292 | QVector3D n1,n2,n3; 293 | n1 = m_normals[id1]; // n1 294 | n2 = m_normals[id2]; // n2 295 | n3 = m_normals[id3]; // n3 296 | 297 | 298 | QVector3D c1,c2,c3; 299 | if(this->hasColor()) 300 | { 301 | c1 = m_colors[id1]; 302 | c2 = m_colors[id2]; 303 | c3 = m_colors[id3]; 304 | } 305 | 306 | QVector2D t1,t2,t3; 307 | if(this->hasTexture() && isUsePLYFormatTexcoords == false) 308 | { 309 | t1 = m_texcoords[id1]; 310 | t2 = m_texcoords[id2]; 311 | t3 = m_texcoords[id3]; 312 | } 313 | else if(this->hasTexture() && isUsePLYFormatTexcoords == true) 314 | { 315 | 316 | t1 = m_texcoords[i*3+0]; 317 | t2 = m_texcoords[i*3+1]; 318 | t3 = m_texcoords[i*3+2]; 319 | // qDebug()<hasTexture()) { 325 | data.push_back(t1.x()); data.push_back(t1.y()); data.push_back(1.0f); 326 | } 327 | else if(this->hasColor()){ 328 | data.push_back(c1.x()); data.push_back(c1.y()); data.push_back(c1.z()); 329 | } 330 | 331 | data.push_back(v2.x()); data.push_back(v2.y()); data.push_back(v2.z()); 332 | data.push_back(n2.x()); data.push_back(n2.y()); data.push_back(n2.z()); 333 | if(this->hasTexture()) { 334 | data.push_back(t2.x()); data.push_back(t2.y()); data.push_back(1.0f); 335 | } 336 | else if(this->hasColor()){ 337 | data.push_back(c2.x()); data.push_back(c2.y()); data.push_back(c2.z()); 338 | } 339 | 340 | data.push_back(v3.x()); data.push_back(v3.y()); data.push_back(v3.z()); 341 | data.push_back(n3.x()); data.push_back(n3.y()); data.push_back(n3.z()); 342 | if(this->hasTexture()) { 343 | data.push_back(t3.x()); data.push_back(t3.y()); data.push_back(1.0f); 344 | } 345 | else if(this->hasColor()){ 346 | data.push_back(c3.x()); data.push_back(c3.y()); data.push_back(c3.z()); 347 | } 348 | } 349 | 350 | 351 | m_meshVBO.count = data.size()/6; 352 | if(this->hasTexture()) m_meshVBO.count = data.size()/9; 353 | else if(this->hasColor()) m_meshVBO.count = data.size()/9; 354 | 355 | qDebug()<<"创建"< data; 367 | 368 | for(int i=0; im_meshVBO.count <=0 ||!this->m_meshVBO.vbo.isCreated()) 385 | return; 386 | 387 | program->setUniformValue("mat_model",modelMat); 388 | 389 | m_meshVBO.vbo.bind(); 390 | 391 | program->enableAttributeArray(VERTEX_ATTRIBUTE); 392 | program->setAttributeBuffer(VERTEX_ATTRIBUTE, GL_FLOAT, 0, 3, 3*sizeof(GLfloat)); 393 | 394 | 395 | glDrawArrays(GL_LINE_STRIP,0, this->m_meshVBO.count); 396 | } 397 | 398 | 399 | -------------------------------------------------------------------------------- /CoralRugosity_Zhihao/rugosityalgorithm.cpp: -------------------------------------------------------------------------------- 1 | #include "rugosityalgorithm.h" 2 | #include 3 | RugosityAlgorithm::RugosityAlgorithm() 4 | { 5 | 6 | } 7 | 8 | 9 | ////// 10 | /// KD-Tree 相关函数 11 | ////// 12 | void RugosityAlgorithm::BuildKDTree() 13 | { 14 | for(unsigned int i=0; i RugosityAlgorithm::RadiusSearch(const QVector3D &pos, float radius) 21 | { 22 | auto closeNodes = index.getPointsWithinCube(GlobalTools::GetSimplePoint(pos), 23 | radius); 24 | std::vector res; 25 | for(unsigned int i=0; i RugosityAlgorithm::CubeSearch(const QVector3D &pos, float radiusSize) 36 | { 37 | auto closeNodes = index.getPointsWithinCube(GlobalTools::GetSimplePoint(pos),radiusSize); 38 | 39 | return closeNodes; 40 | } 41 | 42 | 43 | /// 44 | /// 构建八叉树 45 | /// 46 | struct LabelID 47 | { 48 | int x, y, z; 49 | 50 | bool operator<( const LabelID& other) const { return std::tie(x,y,z) < std::tie(other.x,other.y,other.z);} 51 | 52 | static int GetBlockID(float coordinate, float width){ 53 | 54 | int blockID = coordinate/width; 55 | if(blockID %2 == 0) return blockID/2; 56 | if(blockID %2 != 0) 57 | { 58 | if(blockID > 0) return blockID/2+1; 59 | if(blockID < 0) return blockID/2-1; 60 | } 61 | return 0; 62 | } 63 | }; 64 | 65 | 66 | void RugosityAlgorithm::BuildPartition_WithOctree(float width) 67 | { 68 | QMap octNodes; 69 | for(int i=0; im_octree.append(octNodes[key]); 93 | } 94 | 95 | qDebug()<<"OctTree构建完毕:"< RugosityAlgorithm::QueryIntersectedOctreeBoxIDs(Ray mouseray) 99 | { 100 | QVector intersected_octreenodes; 101 | QVector3D rayOrigin = mouseray.center; 102 | QVector3D rayDirection = mouseray.dir.normalized(); 103 | 104 | for(int i=0; i seletedIDs, QVector3D &outIntersectionPt) 116 | { 117 | float minDist = 1e10; 118 | 119 | // 首先按照距离排序 120 | for(int i=0; i& faceDict =m_inputMesh.m_dictPtToFace[vid]; 171 | for(int i=0; i RugosityAlgorithm::GetRugosityCurvePoints(QVector3D intersectPtA, QVector3D intersectPtB, QVector3D normal) 211 | { 212 | normal = normal.normalized(); 213 | QVector3D p = intersectPtA; 214 | 215 | QVector res; 216 | 217 | QMap> connected_lines; // the connected lines; 218 | 219 | QMap dict_points; 220 | QMap> dict_edges; 221 | 222 | 223 | // 初始化 224 | 225 | for(int tt=0; ttm_octree.size(); ++tt) // First, visit the tt-th OctreeNode-Box 226 | { 227 | OctreenNode& box = m_octree[tt]; 228 | 229 | float aa = box.width*box.width + box.width*box.width; 230 | float boxRadius = sqrt(aa+box.width*box.width); 231 | 232 | float distBoxToPlane = box.center.distanceToPlane(p, normal); 233 | 234 | if(distBoxToPlane > boxRadius) // If the box center is too distant from the plane; 235 | continue; 236 | 237 | // Check if each edge of the triangles in current box was intersected with the plane; 238 | for(int mm=0; mm& faceDict =m_inputMesh.m_dictPtToFace[vid]; 242 | 243 | for(int i=0; i(); 265 | 266 | 267 | out0ID = true; 268 | } 269 | 270 | if(GlobalTools::GetIntersectPoint_segment_to_triangle(normal,p, v0, v2,out1)) 271 | { 272 | if(out0ID && out1.distanceToPoint(out0)<1e-9) // 如果第一个有交点out0id,并且0和1的距离比较远 273 | ; 274 | else 275 | { 276 | res.append(out1); 277 | 278 | lineID1 = LineID(triangle.x, triangle.z); 279 | dict_points[lineID1] = out1; 280 | 281 | 282 | if(dict_edges.contains(lineID1) == false) 283 | dict_edges[lineID1] = QVector(); 284 | 285 | out1ID = true; 286 | } 287 | } 288 | 289 | if(GlobalTools::GetIntersectPoint_segment_to_triangle(normal,p, v1, v2,out2)) 290 | { 291 | if(out0ID && out1ID) 292 | ; 293 | else 294 | { 295 | res.append(out2); 296 | 297 | lineID2 = LineID(triangle.y, triangle.z); 298 | dict_points[lineID2] = out2; 299 | 300 | if(dict_edges.contains(lineID2) == false) 301 | dict_edges[lineID2] = QVector(); 302 | 303 | out2ID = true; 304 | } 305 | 306 | 307 | } 308 | 309 | if(out0ID && out1ID) 310 | { 311 | if (dict_edges[lineID0].contains(lineID1) == false) dict_edges[lineID0].append(lineID1); 312 | if (dict_edges[lineID1].contains(lineID0) == false) dict_edges[lineID1].append(lineID0); 313 | } 314 | 315 | if(out0ID && out2ID) 316 | { 317 | if (dict_edges[lineID0].contains(lineID2) == false) dict_edges[lineID0].append(lineID2); 318 | if (dict_edges[lineID2].contains(lineID0) == false) dict_edges[lineID2].append(lineID0); 319 | } 320 | 321 | if(out1ID && out2ID) 322 | { 323 | if (dict_edges[lineID1].contains(lineID2) == false) dict_edges[lineID1].append(lineID2); 324 | if (dict_edges[lineID2].contains(lineID1) == false) dict_edges[lineID2].append(lineID1); 325 | } 326 | } 327 | 328 | } 329 | } 330 | 331 | 332 | this->m_curves = GetRugosityCurves(dict_points, dict_edges); 333 | qDebug()<<"找到Curve数目: "<< m_curves.count(); 334 | 335 | return res; 336 | } 337 | 338 | 339 | QVector RugosityAlgorithm::GetRugosityCurves(QMap& dict_points, QMap>& dict_edges) 340 | { 341 | // 初始化访问数组 342 | QMap isVisited; 343 | for(int i = 0; i < dict_points.keys().size(); ++i) 344 | { 345 | isVisited[dict_points.keys()[i]] = false; 346 | } 347 | 348 | // Construct the curves sequentially 349 | QVector curves; 350 | 351 | while(true) 352 | { 353 | // Find a unvisited starting point 354 | int startPtID = -1; 355 | LineID startKey; 356 | for(int i=0; i &curves, 386 | QMap &dict_points, QMap > &dict_edges, 387 | QMap& isVisited) 388 | { 389 | Curve curCurve; 390 | 391 | isVisited[startID] = true; // 首個節點已經訪問 392 | curCurve.pts.append(dict_points[startID]); // 先把第一個點放進來 393 | 394 | LineID parentKey = startID; 395 | int nextChildID = firstChildID; 396 | while(true) 397 | { 398 | LineID curKey = dict_edges[parentKey][nextChildID]; // 當前的節點 399 | 400 | // 把当前结点放进来 401 | curCurve.pts.append(dict_points[curKey]); 402 | isVisited[curKey] = true; // 设置当前已经访问 403 | 404 | bool hasfindChild = false; 405 | for(int i=0; i &selectedNodeId) 474 | { 475 | for(int i=0; isetFocusPolicy(Qt::StrongFocus); 13 | 14 | m_rugosity = new RugosityAlgorithm(); 15 | 16 | m_leafTexture = NULL; 17 | m_meshProgram = NULL; 18 | m_skeletonProgram = NULL; 19 | m_pointProgram = NULL; 20 | 21 | 22 | m_projectMode = _Perspective; 23 | this->m_displayMode = DISPLAY_MODE::_Normal; 24 | m_horAngle = 45.0f; 25 | m_verAngle = 0.0f; 26 | distance = 10.0f; 27 | scale = 1.0f; 28 | m_eyeDist = QVector3D(0.0f,0.0f,0.0f); 29 | 30 | 31 | connect(&m_viewingTimer,SIGNAL(timeout()),this,SLOT(OnViewingTimer())); 32 | } 33 | 34 | 35 | void GLWidget::initShaders(QOpenGLShaderProgram*& m_program,const QString& shaderName, 36 | bool v = false,bool c = false, bool n = false, bool t=false) 37 | { 38 | m_program = new QOpenGLShaderProgram(this); 39 | 40 | m_program->addShaderFromSourceFile(QOpenGLShader::Vertex, QString(":/shader/res/%1VShader.glsl").arg(shaderName)); 41 | m_program->addShaderFromSourceFile(QOpenGLShader::Fragment,QString(":/shader/res/%1FShader.glsl").arg(shaderName)); 42 | 43 | if(v) 44 | m_program->bindAttributeLocation("a_position",VERTEX_ATTRIBUTE); 45 | if(c) 46 | m_program->bindAttributeLocation("a_color" ,COLOUR_ATTRIBUTE); 47 | if(n) 48 | m_program->bindAttributeLocation("a_normal" ,NORMAL_ATTRIBUTE); 49 | if(t) 50 | m_program->bindAttributeLocation("a_texCoord",TEXTURE_ATTRIBUTE); 51 | 52 | m_program->link(); 53 | m_program->bind(); 54 | } 55 | 56 | 57 | void GLWidget::setupShaders(QOpenGLShaderProgram *&m_program) 58 | { 59 | m_program->bind(); 60 | 61 | m_program->setUniformValue("mat_projection",m_projectMatrix); 62 | m_program->setUniformValue("mat_view",m_viewMatrix); 63 | } 64 | 65 | void GLWidget::setupTexture(QOpenGLTexture *&texture, const QString &filename) 66 | { 67 | if(texture) 68 | delete texture; 69 | 70 | QImage img(filename); 71 | texture = new QOpenGLTexture(img); 72 | 73 | texture->setMinificationFilter(QOpenGLTexture::Nearest); 74 | texture->setMagnificationFilter(QOpenGLTexture::Linear); 75 | } 76 | 77 | 78 | void GLWidget::initializeGL() 79 | { 80 | initializeOpenGLFunctions(); 81 | 82 | glClearColor(0.96,0.94,0.94,1.0); 83 | 84 | glEnable(GL_COLOR_MATERIAL); 85 | glEnable(GL_DEPTH_TEST); 86 | glDepthFunc(GL_LESS); 87 | 88 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 89 | glShadeModel(GL_SMOOTH); 90 | glEnable(GL_POINT_SMOOTH); 91 | glEnable(GL_BLEND); 92 | glEnable(GL_ALPHA_TEST); 93 | glEnable(GL_TEXTURE_2D); 94 | 95 | 96 | initShaders(m_pointProgram,"Points",true,true,false,false); 97 | initShaders(m_skeletonProgram,"Graph",true,false,false,false); 98 | initShaders(m_meshProgram,"bark",true,false,true,true); 99 | 100 | //setupTexture(m_leafTexture,":/img/res/leaf/叶子.png"); 101 | qsrand(QTime::currentTime().msec()*QTime::currentTime().second()); 102 | 103 | // 加载默认的coral mesh 104 | m_rugosity->GetInputMesh().CreateMeshVBO("D:/Projects/HKUST_VGD/Coral/videos/video1/coral model (small image size)/scene_dense_mesh_refine_texture.ply"); 105 | m_rugosity->BuildKDTree(); // 创建Kd树 106 | m_rugosity->m_inputMesh.BuildDictionary(); // 构建映射字典 107 | m_rugosity->BuildPartition_WithOctree(0.5); // 创建OctTree分割 108 | 109 | // 测试区域 110 | QVector3D v0(+1,0,0); 111 | QVector3D v1(0,0,-1); 112 | QVector3D v2(0,0.1,+1); 113 | 114 | Ray testRay; 115 | testRay.center = QVector3D(7.07107,0.0,7.07107); 116 | testRay.dir = QVector3D(-0.722628,0.156867,-0.673202); 117 | 118 | QVector3D intPt; 119 | bool res = GlobalTools::GetIntersectPoint_ray_to_triangle(testRay, v0, v1,v2, intPt); 120 | 121 | qDebug()< curvePoints; 139 | 140 | void GLWidget::paintGL() 141 | { 142 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 143 | 144 | // perspective camera mode 145 | { 146 | m_eyePos = m_eyeDist+QVector3D(scale*distance*cos(PI*m_verAngle/180.0)*cos(PI*m_horAngle/180.0), 147 | scale*distance*sin(PI*m_verAngle/180.0), 148 | scale*distance*cos(PI*m_verAngle/180.0)*sin(PI*m_horAngle/180.0)); 149 | m_viewMatrix.setToIdentity(); 150 | m_viewMatrix.lookAt(m_eyePos,m_eyeDist,QVector3D(0,1,0)); 151 | m_modelMat.setToIdentity(); 152 | } 153 | 154 | 155 | 156 | if(m_isWireFrame) 157 | glPolygonMode(GL_FRONT_AND_BACK,GL_LINE); 158 | else 159 | glPolygonMode(GL_FRONT_AND_BACK,GL_FILL); 160 | 161 | 162 | { 163 | glLineWidth(1); 164 | // 绘制mesh 165 | setupShaders(m_meshProgram); 166 | glEnable(GL_TEXTURE0); 167 | 168 | m_meshProgram->setUniformValue("texture",0); 169 | if(m_rugosity->GetInputMesh().hasTexture()) 170 | { 171 | m_rugosity->GetInputMesh().m_texture->bind(); 172 | m_meshProgram->setUniformValue("isTextured",1); 173 | } 174 | else 175 | m_meshProgram->setUniformValue("isTextured",0); 176 | 177 | m_meshProgram->setUniformValue("viewPos",m_eyePos); 178 | m_meshProgram->setUniformValue("isLighting",false); 179 | m_meshProgram->setUniformValue("isBark",false); 180 | m_meshProgram->setUniformValue("isShowSeg",false); 181 | m_rugosity->GetInputMesh().DrawMeshVBO(m_meshProgram,m_modelMat); 182 | 183 | } 184 | 185 | 186 | { 187 | //------------ 渲染其他 --------------------- // 188 | 189 | if(this->isPressRightButton) 190 | { 191 | // setupShaders(this->m_skeletonProgram); 192 | // 绘制一个选取矩形 193 | glLineWidth(13.0f); 194 | m_skeletonProgram->bind(); 195 | m_skeletonProgram->setUniformValue("u_color",QVector3D(1,0,0)); 196 | m_skeletonProgram->setUniformValue("mat_projection",QMatrix4x4()); 197 | m_skeletonProgram->setUniformValue("mat_view",QMatrix4x4()); 198 | m_skeletonProgram->setUniformValue("mat_model",QMatrix4x4()); 199 | 200 | glPolygonMode(GL_FRONT_AND_BACK,GL_LINE); 201 | 202 | float x1 = 2*m_startPt.x()/(float)width()-1.0; 203 | float y1 = -2*m_startPt.y()/(float)height()+1.0; 204 | float x2 = 2*m_endPt.x()/(float)width()-1.0; 205 | float y2 = -2*m_endPt.y()/(float)height()+1.0; 206 | 207 | glBegin(GL_LINES); 208 | glVertex2f(x1, y1); 209 | glVertex2f(x2, y2); 210 | glEnd(); 211 | 212 | } 213 | 214 | 215 | } 216 | 217 | if(m_isShowBVH) 218 | { 219 | setupShaders(m_skeletonProgram); 220 | m_skeletonProgram->setUniformValue("mat_model",m_modelMat); 221 | m_rugosity->RenderAllOctreeNode(m_skeletonProgram); 222 | } 223 | 224 | if(isSelectedPlane) // 如果选中了一个plane,则绘制2个交点和平面 225 | { 226 | setupShaders(m_skeletonProgram); 227 | m_skeletonProgram->setUniformValue("mat_model",m_modelMat); 228 | m_skeletonProgram->setUniformValue("u_color",QVector3D(0,0,1)); 229 | glPointSize(30); 230 | glBegin(GL_POINTS); 231 | glVertex3f(intersectPointA.x(),intersectPointA.y(), intersectPointA.z()); 232 | glVertex3f(intersectPointB.x(),intersectPointB.y(), intersectPointB.z()); 233 | glEnd(); 234 | 235 | glPointSize(10); 236 | glBegin(GL_POINTS); 237 | for(int i=0; im_curves.size(); ++i) 245 | { 246 | Curve& curve = m_rugosity->m_curves[i]; 247 | m_skeletonProgram->setUniformValue("u_color",QVector3D(0.5,0,0)); 248 | 249 | glBegin(GL_LINE_STRIP); 250 | for(int p=0; pbuttons() & Qt::RightButton) 265 | { 266 | isPressRightButton = true; 267 | m_startPt = QVector2D(event->pos()); 268 | return; 269 | } 270 | m_clickpos = event->pos(); 271 | } 272 | 273 | void GLWidget::mouseMoveEvent(QMouseEvent *event) 274 | { 275 | if(event->buttons() & Qt::RightButton) 276 | { 277 | isPressRightButton = true; 278 | 279 | m_endPt = QVector2D(event->pos()); 280 | update(); 281 | 282 | return; 283 | } 284 | if(event->buttons() == Qt::LeftButton) 285 | { 286 | QPoint cur = event->pos(); 287 | 288 | double dx = (cur.x() - m_clickpos.x())/5.0; 289 | double dy = (cur.y() - m_clickpos.y())/5.0; 290 | 291 | m_horAngle +=dx; 292 | if(m_verAngle+dy <90.0 && m_verAngle+dy>-90.0) 293 | m_verAngle +=dy; 294 | 295 | m_clickpos = cur; 296 | update(); 297 | } 298 | } 299 | 300 | QVector> GLWidget::Get2DVersionCurves(float& minY, float& maxY) 301 | { 302 | QVector3D dir = (intersectPointB - intersectPointA).normalized(); 303 | dir.setY(0.0); 304 | 305 | if(dir.z()<0) 306 | dir = - dir; 307 | 308 | 309 | auto curves = m_rugosity->m_curves; 310 | QVector> res_crosssection; 311 | res_crosssection.resize(curves.size()); 312 | 313 | minY = 1e10; 314 | maxY = -1e10; 315 | for(int i=0; imaxY) maxY = y; 331 | } 332 | } 333 | 334 | float range = maxY - minY; 335 | minY -= 0.05 * range; 336 | maxY += 0.05 * range; 337 | return res_crosssection; 338 | } 339 | 340 | bool ComputeHighestY(float x, const QVector>& curves, float& maxY) 341 | { 342 | 343 | bool isLegal = false; 344 | maxY = -1e9; 345 | for(int ttt =0; ttt& curve = curves[ttt]; 348 | 349 | if(curve.size() <=1) 350 | continue; 351 | 352 | for (int i=0; i p2.x()) // 如果p1在p2的右边 361 | { 362 | // 交换哈 363 | QVector2D tmp = p1; 364 | p1 = p2; 365 | p2 = tmp; 366 | } 367 | 368 | if(p1.x() <= x && x <= p2.x()) 369 | { 370 | isLegal = true; 371 | 372 | float ratio = (x-p1.x())/(p2.x()-p1.x()); 373 | float y = p1.y() + ratio * (p2.y()-p1.y()); 374 | 375 | if (y> maxY) 376 | maxY = y; 377 | } 378 | } 379 | } 380 | 381 | return isLegal; 382 | } 383 | 384 | 385 | QVector GLWidget::Get2DHeightMap(const QVector>& curves) 386 | { 387 | // 先统计出所有的curves的x坐标,并记录到一个数组中,按照递增顺序排序好 388 | // 之后,求每个x坐标和所有的curve的edge的交点(交点可能有多个,保留y最高) 389 | // 一个加速技巧:求交点时,先判断当前x坐标是否位于edge的两个点的x坐标之内, 390 | // if so, then compute it's y coordinate (linear interpolation), otherwise, no intersection point. 391 | 392 | 393 | QVector x_coordinates; 394 | for(int ttt =0; ttt& curve = curves[ttt]; 397 | 398 | if(curve.size() <=1) 399 | continue; 400 | 401 | for (int i=0; i res; 409 | 410 | for(int i=0; i > &curves, QVector &heights) 424 | { 425 | float minX, minY, maxX, maxY; 426 | minX = minY = +10e10f; 427 | maxX = maxY = -10e10f; 428 | 429 | for(unsigned int i=0; i& curCurve = curves[i]; 432 | for(unsigned int k=0; kx)?x:minX; 439 | minY = (minY>y)?y:minY; 440 | maxX = (maxXx)?x:minX; 452 | minY = (minY>y)?y:minY; 453 | maxX = (maxXoriginal_x_range = (maxX-minX); 458 | this->original_y_range = (maxY-minY); 459 | this->original_x_min = minX; 460 | this->original_y_min = minY; 461 | } 462 | 463 | void GLWidget::Scale_2D_Rugosity(float scale) 464 | { 465 | this->m_2D_curves_scaled.clear(); 466 | this->m_2D_heights_scaled.clear(); 467 | 468 | this->scaled_x_max = -10e10f; 469 | this->scaled_y_max = -10e10f; 470 | 471 | for(unsigned int i=0; im_2D_curves.size(); ++i) 472 | { 473 | this->m_2D_curves_scaled.push_back(QVector()); 474 | 475 | QVector& curCurve = this->m_2D_curves[i]; // scale前 476 | QVector& curCurve_scaled = this->m_2D_curves_scaled[i]; // scale后 477 | 478 | for(unsigned int k=0; koriginal_x_min); 482 | float y = scale * (pt.y()-this->original_y_min); 483 | curCurve_scaled.push_back(QVector2D(x,y)); 484 | 485 | scaled_x_max = (scaled_x_maxm_2D_heights.size(); ++k) 492 | { 493 | QVector2D pt = this->m_2D_heights[k]; 494 | float x = scale * (pt.x()-this->original_x_min); 495 | float y = scale * (pt.y()-this->original_y_min); 496 | m_2D_heights_scaled.push_back(QVector2D(x,y)); 497 | 498 | scaled_x_max = (scaled_x_maxGetRayFromScreenPos(m_startPt); 512 | Ray endRay = this->GetRayFromScreenPos(m_endPt); 513 | 514 | 515 | QVector startSelectedID = m_rugosity->QueryIntersectedOctreeBoxIDs(startRay); 516 | QVector endSelectedID = m_rugosity->QueryIntersectedOctreeBoxIDs(endRay); 517 | 518 | 519 | bool isSelectedA = m_rugosity->QueryIntersectedPointAmong(startRay,startSelectedID,intersectPointA); 520 | bool isSelectedB = m_rugosity->QueryIntersectedPointAmong(endRay,endSelectedID,intersectPointB); 521 | 522 | 523 | 524 | isSelectedPlane = isSelectedA && isSelectedB; 525 | 526 | if(isSelectedPlane == true) 527 | { 528 | QVector3D planeNormal = QVector3D::crossProduct((intersectPointA-intersectPointB), QVector3D(0,1,0)); 529 | planeNormal = planeNormal.normalized(); 530 | 531 | curvePoints = m_rugosity->GetRugosityCurvePoints(intersectPointA, intersectPointB,planeNormal); 532 | 533 | m_rugosity->UpdateCurrentCurveMesh(); 534 | 535 | float minY, maxY; 536 | this->m_2D_curves = Get2DVersionCurves(minY, maxY); 537 | this->m_2D_heights = Get2DHeightMap(this->m_2D_curves); 538 | 539 | this->Compute_XY_Range(this->m_2D_curves, this->m_2D_heights); 540 | this->Scale_2D_Rugosity(this->ui_scale_value); 541 | qDebug()<key() == Qt::Key_W) 556 | m_eyeDist.setY(m_eyeDist.y()+5); 557 | if(e->key() == Qt::Key_S) 558 | m_eyeDist.setY(m_eyeDist.y()-5); 559 | 560 | this->update(); 561 | } 562 | 563 | void GLWidget::wheelEvent(QWheelEvent *event) 564 | { 565 | double ds = 0.03; 566 | if(event->delta()>0 && scale-ds>0) 567 | scale-=ds; 568 | else if(event->delta()<0) 569 | scale+=ds; 570 | update(); 571 | } 572 | 573 | void GLWidget::OnViewingTimer() 574 | { 575 | m_horAngle +=1; 576 | update(); 577 | } 578 | 579 | Ray GLWidget::GetRayFromScreenPos(QVector2D screenPoint) 580 | { 581 | QVector3D eyePos = m_eyeDist+QVector3D(scale*distance*cos(PI*m_verAngle/180.0)*cos(PI*m_horAngle/180.0), 582 | scale*distance*sin(PI*m_verAngle/180.0), 583 | scale*distance*cos(PI*m_verAngle/180.0)*sin(PI*m_horAngle/180.0)); 584 | QMatrix4x4 matV, matP; 585 | matV.setToIdentity(); 586 | matV.lookAt(eyePos,m_eyeDist,QVector3D(0,1,0)); 587 | 588 | matP.setToIdentity(); 589 | matP.perspective(45.0f,(float)width()/(float)height(),0.1f,100000.0f); 590 | 591 | Ray ray; 592 | ray.center = eyePos; 593 | ray.dir = GlobalTools::GetRayDirection_From2DScreenPos(screenPoint.toPointF(), width(), height(), 594 | matP,matV); 595 | 596 | return ray; 597 | } 598 | 599 | GLWidget::~GLWidget(){} 600 | -------------------------------------------------------------------------------- /CoralRugosity_Zhihao/CoralRugosity_Zhihao.pro.user: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | EnvironmentId 7 | {e00c9e79-2b8c-48a9-ae2e-08b68922e8da} 8 | 9 | 10 | ProjectExplorer.Project.ActiveTarget 11 | 0 12 | 13 | 14 | ProjectExplorer.Project.EditorSettings 15 | 16 | true 17 | false 18 | true 19 | 20 | Cpp 21 | 22 | CppGlobal 23 | 24 | 25 | 26 | QmlJS 27 | 28 | QmlJSGlobal 29 | 30 | 31 | 2 32 | UTF-8 33 | false 34 | 4 35 | false 36 | 80 37 | true 38 | true 39 | 1 40 | true 41 | false 42 | 0 43 | true 44 | true 45 | 0 46 | 8 47 | true 48 | 1 49 | true 50 | true 51 | true 52 | false 53 | 54 | 55 | 56 | ProjectExplorer.Project.PluginSettings 57 | 58 | 59 | 60 | ProjectExplorer.Project.Target.0 61 | 62 | Desktop Qt 5.8.0 MinGW 32bit 63 | Desktop Qt 5.8.0 MinGW 32bit 64 | qt.58.win32_mingw53_kit 65 | 1 66 | 0 67 | 0 68 | 69 | C:/Users/liuzh/Documents/build-CoralRugosity_Zhihao-Desktop_Qt_5_8_0_MinGW_32bit-Debug 70 | 71 | 72 | true 73 | qmake 74 | 75 | QtProjectManager.QMakeBuildStep 76 | true 77 | 78 | false 79 | false 80 | false 81 | 82 | 83 | true 84 | Make 85 | 86 | Qt4ProjectManager.MakeStep 87 | 88 | false 89 | 90 | 91 | 92 | 2 93 | 构建 94 | 95 | ProjectExplorer.BuildSteps.Build 96 | 97 | 98 | 99 | true 100 | Make 101 | 102 | Qt4ProjectManager.MakeStep 103 | 104 | true 105 | clean 106 | 107 | 108 | 1 109 | 清理 110 | 111 | ProjectExplorer.BuildSteps.Clean 112 | 113 | 2 114 | false 115 | 116 | Debug 117 | 118 | Qt4ProjectManager.Qt4BuildConfiguration 119 | 2 120 | true 121 | 122 | 123 | C:/Users/liuzh/Documents/build-CoralRugosity_Zhihao-Desktop_Qt_5_8_0_MinGW_32bit-Release 124 | 125 | 126 | true 127 | qmake 128 | 129 | QtProjectManager.QMakeBuildStep 130 | false 131 | 132 | false 133 | false 134 | false 135 | 136 | 137 | true 138 | Make 139 | 140 | Qt4ProjectManager.MakeStep 141 | 142 | false 143 | 144 | 145 | 146 | 2 147 | 构建 148 | 149 | ProjectExplorer.BuildSteps.Build 150 | 151 | 152 | 153 | true 154 | Make 155 | 156 | Qt4ProjectManager.MakeStep 157 | 158 | true 159 | clean 160 | 161 | 162 | 1 163 | 清理 164 | 165 | ProjectExplorer.BuildSteps.Clean 166 | 167 | 2 168 | false 169 | 170 | Release 171 | 172 | Qt4ProjectManager.Qt4BuildConfiguration 173 | 0 174 | true 175 | 176 | 177 | C:/Users/liuzh/Documents/build-CoralRugosity_Zhihao-Desktop_Qt_5_8_0_MinGW_32bit-Profile 178 | 179 | 180 | true 181 | qmake 182 | 183 | QtProjectManager.QMakeBuildStep 184 | true 185 | 186 | false 187 | true 188 | false 189 | 190 | 191 | true 192 | Make 193 | 194 | Qt4ProjectManager.MakeStep 195 | 196 | false 197 | 198 | 199 | 200 | 2 201 | 构建 202 | 203 | ProjectExplorer.BuildSteps.Build 204 | 205 | 206 | 207 | true 208 | Make 209 | 210 | Qt4ProjectManager.MakeStep 211 | 212 | true 213 | clean 214 | 215 | 216 | 1 217 | 清理 218 | 219 | ProjectExplorer.BuildSteps.Clean 220 | 221 | 2 222 | false 223 | 224 | Profile 225 | 226 | Qt4ProjectManager.Qt4BuildConfiguration 227 | 0 228 | true 229 | 230 | 3 231 | 232 | 233 | 0 234 | 部署 235 | 236 | ProjectExplorer.BuildSteps.Deploy 237 | 238 | 1 239 | 在本地部署 240 | 241 | ProjectExplorer.DefaultDeployConfiguration 242 | 243 | 1 244 | 245 | 246 | false 247 | false 248 | 1000 249 | 250 | true 251 | 252 | false 253 | false 254 | false 255 | false 256 | true 257 | 0.01 258 | 10 259 | true 260 | 1 261 | 25 262 | 263 | 1 264 | true 265 | false 266 | true 267 | valgrind 268 | 269 | 0 270 | 1 271 | 2 272 | 3 273 | 4 274 | 5 275 | 6 276 | 7 277 | 8 278 | 9 279 | 10 280 | 11 281 | 12 282 | 13 283 | 14 284 | 285 | 2 286 | 287 | CoralRugosity_Zhihao 288 | CoralRugosity_Zhihao2 289 | Qt4ProjectManager.Qt4RunConfiguration:D:/Projects/QtProjects/CoralRugosity/CoralRugosity_Zhihao/CoralRugosity_Zhihao.pro 290 | true 291 | 292 | CoralRugosity_Zhihao.pro 293 | false 294 | 295 | C:/Users/liuzh/Documents/build-CoralRugosity_Zhihao-Desktop_Qt_5_8_0_MinGW_32bit-Release 296 | 3768 297 | false 298 | true 299 | false 300 | false 301 | true 302 | 303 | 1 304 | 305 | 306 | 307 | ProjectExplorer.Project.TargetCount 308 | 1 309 | 310 | 311 | ProjectExplorer.Project.Updater.FileVersion 312 | 18 313 | 314 | 315 | Version 316 | 18 317 | 318 | 319 | -------------------------------------------------------------------------------- /CoralRugosity_Zhihao/utility/tinyply.h: -------------------------------------------------------------------------------- 1 | /* 2 | * tinyply 2.3.4 (https://github.com/ddiakopoulos/tinyply) 3 | * 4 | * A single-header, zero-dependency (except the C++ STL) public domain implementation 5 | * of the PLY mesh file format. Requires C++11; errors are handled through exceptions. 6 | * 7 | * This software is in the public domain. Where that dedication is not 8 | * recognized, you are granted a perpetual, irrevocable license to copy, 9 | * distribute, and modify this file as you see fit. 10 | * 11 | * Authored by Dimitri Diakopoulos (http://www.dimitridiakopoulos.com) 12 | * 13 | * tinyply.h may be included in many files, however in a single compiled file, 14 | * the implementation must be created with the following defined prior to header inclusion 15 | * #define TINYPLY_IMPLEMENTATION 16 | * 17 | */ 18 | 19 | //////////////////////// 20 | // tinyply header // 21 | //////////////////////// 22 | 23 | #ifndef tinyply_h 24 | #define tinyply_h 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | namespace tinyply 38 | { 39 | 40 | enum class Type : uint8_t 41 | { 42 | INVALID, 43 | INT8, 44 | UINT8, 45 | INT16, 46 | UINT16, 47 | INT32, 48 | UINT32, 49 | FLOAT32, 50 | FLOAT64 51 | }; 52 | 53 | struct PropertyInfo 54 | { 55 | PropertyInfo() {}; 56 | PropertyInfo(int stride, std::string str) 57 | : stride(stride), str(str) {} 58 | int stride {0}; 59 | std::string str; 60 | }; 61 | 62 | static std::map PropertyTable 63 | { 64 | { Type::INT8, PropertyInfo(1, std::string("char")) }, 65 | { Type::UINT8, PropertyInfo(1, std::string("uchar")) }, 66 | { Type::INT16, PropertyInfo(2, std::string("short")) }, 67 | { Type::UINT16, PropertyInfo(2, std::string("ushort")) }, 68 | { Type::INT32, PropertyInfo(4, std::string("int")) }, 69 | { Type::UINT32, PropertyInfo(4, std::string("uint")) }, 70 | { Type::FLOAT32, PropertyInfo(4, std::string("float")) }, 71 | { Type::FLOAT64, PropertyInfo(8, std::string("double")) }, 72 | { Type::INVALID, PropertyInfo(0, std::string("INVALID"))} 73 | }; 74 | 75 | class Buffer 76 | { 77 | uint8_t * alias{ nullptr }; 78 | struct delete_array { void operator()(uint8_t * p) { delete[] p; } }; 79 | std::unique_ptr data; 80 | size_t size {0}; 81 | public: 82 | Buffer() {}; 83 | Buffer(const size_t size) : data(new uint8_t[size], delete_array()), size(size) { alias = data.get(); } // allocating 84 | Buffer(const uint8_t * ptr): alias(const_cast(ptr)) { } // non-allocating, todo: set size? 85 | uint8_t * get() { return alias; } 86 | const uint8_t * get_const() {return const_cast(alias); } 87 | size_t size_bytes() const { return size; } 88 | }; 89 | 90 | struct PlyData 91 | { 92 | Type t; 93 | Buffer buffer; 94 | size_t count {0}; 95 | bool isList {false}; 96 | }; 97 | 98 | struct PlyProperty 99 | { 100 | PlyProperty(std::istream & is); 101 | PlyProperty(Type type, std::string & _name) : name(_name), propertyType(type) {} 102 | PlyProperty(Type list_type, Type prop_type, std::string & _name, size_t list_count) 103 | : name(_name), propertyType(prop_type), isList(true), listType(list_type), listCount(list_count) {} 104 | std::string name; 105 | Type propertyType{ Type::INVALID }; 106 | bool isList{ false }; 107 | Type listType{ Type::INVALID }; 108 | size_t listCount {0}; 109 | }; 110 | 111 | struct PlyElement 112 | { 113 | PlyElement(std::istream & istream); 114 | PlyElement(const std::string & _name, size_t count) : name(_name), size(count) {} 115 | std::string name; 116 | size_t size {0}; 117 | std::vector properties; 118 | }; 119 | 120 | struct PlyFile 121 | { 122 | struct PlyFileImpl; 123 | std::unique_ptr impl; 124 | 125 | PlyFile(); 126 | ~PlyFile(); 127 | 128 | /* 129 | * The ply format requires an ascii header. This can be used to determine at 130 | * runtime which properties or elements exist in the file. Limited validation of the 131 | * header is performed; it is assumed the header correctly reflects the contents of the 132 | * payload. This function may throw. Returns true on success, false on failure. 133 | */ 134 | bool parse_header(std::istream & is); 135 | 136 | /* 137 | * Execute a read operation. Data must be requested via `request_properties_from_element(...)` 138 | * prior to calling this function. 139 | */ 140 | void read(std::istream & is); 141 | 142 | /* 143 | * `write` performs no validation and assumes that the data passed into 144 | * `add_properties_to_element` is well-formed. 145 | */ 146 | void write(std::ostream & os, bool isBinary); 147 | 148 | /* 149 | * These functions are valid after a call to `parse_header(...)`. In the case of 150 | * writing, get_comments() reference may also be used to add new comments to the ply header. 151 | */ 152 | std::vector get_elements() const; 153 | std::vector get_info() const; 154 | std::vector & get_comments(); 155 | bool is_binary_file() const; 156 | 157 | /* 158 | * In the general case where |list_size_hint| is zero, `read` performs a two-pass 159 | * parse to support variable length lists. The most general use of the 160 | * ply format is storing triangle meshes. When this fact is known a-priori, we can pass 161 | * an expected list length that will apply to this element. Doing so results in an up-front 162 | * memory allocation and a single-pass import, a 2x performance optimization. 163 | */ 164 | std::shared_ptr request_properties_from_element(const std::string & elementKey, 165 | const std::vector propertyKeys, const uint32_t list_size_hint = 0); 166 | 167 | void add_properties_to_element(const std::string & elementKey, 168 | const std::vector propertyKeys, 169 | const Type type, 170 | const size_t count, 171 | const uint8_t * data, 172 | const Type listType, 173 | const size_t listCount); 174 | }; 175 | 176 | } // end namespace tinyply 177 | 178 | #endif // end tinyply_h 179 | 180 | //////////////////////////////// 181 | // tinyply implementation // 182 | //////////////////////////////// 183 | 184 | #ifdef TINYPLY_IMPLEMENTATION 185 | 186 | #include 187 | #include 188 | #include 189 | #include 190 | #include 191 | 192 | namespace tinyply 193 | { 194 | 195 | using namespace std; 196 | 197 | template inline T2 endian_swap(const T & v) noexcept { return v; } 198 | template<> inline uint16_t endian_swap(const uint16_t & v) noexcept { return (v << 8) | (v >> 8); } 199 | template<> inline uint32_t endian_swap(const uint32_t & v) noexcept { return (v << 24) | ((v << 8) & 0x00ff0000) | ((v >> 8) & 0x0000ff00) | (v >> 24); } 200 | template<> inline uint64_t endian_swap(const uint64_t & v) noexcept 201 | { 202 | return (((v & 0x00000000000000ffLL) << 56) | 203 | ((v & 0x000000000000ff00LL) << 40) | 204 | ((v & 0x0000000000ff0000LL) << 24) | 205 | ((v & 0x00000000ff000000LL) << 8) | 206 | ((v & 0x000000ff00000000LL) >> 8) | 207 | ((v & 0x0000ff0000000000LL) >> 24) | 208 | ((v & 0x00ff000000000000LL) >> 40) | 209 | ((v & 0xff00000000000000LL) >> 56)); 210 | } 211 | template<> inline int16_t endian_swap(const int16_t & v) noexcept { uint16_t r = endian_swap(*(uint16_t*)&v); return *(int16_t*)&r; } 212 | template<> inline int32_t endian_swap(const int32_t & v) noexcept { uint32_t r = endian_swap(*(uint32_t*)&v); return *(int32_t*)&r; } 213 | template<> inline int64_t endian_swap(const int64_t & v) noexcept { uint64_t r = endian_swap(*(uint64_t*)&v); return *(int64_t*)&r; } 214 | template<> inline float endian_swap(const uint32_t & v) noexcept { union { float f; uint32_t i; }; i = endian_swap(v); return f; } 215 | template<> inline double endian_swap(const uint64_t & v) noexcept { union { double d; uint64_t i; }; i = endian_swap(v); return d; } 216 | 217 | inline uint32_t hash_fnv1a(const std::string & str) noexcept 218 | { 219 | static const uint32_t fnv1aBase32 = 0x811C9DC5u; 220 | static const uint32_t fnv1aPrime32 = 0x01000193u; 221 | uint32_t result = fnv1aBase32; 222 | for (auto & c : str) { result ^= static_cast(c); result *= fnv1aPrime32; } 223 | return result; 224 | } 225 | 226 | inline Type property_type_from_string(const std::string & t) noexcept 227 | { 228 | if (t == "int8" || t == "char") return Type::INT8; 229 | else if (t == "uint8" || t == "uchar") return Type::UINT8; 230 | else if (t == "int16" || t == "short") return Type::INT16; 231 | else if (t == "uint16" || t == "ushort") return Type::UINT16; 232 | else if (t == "int32" || t == "int") return Type::INT32; 233 | else if (t == "uint32" || t == "uint") return Type::UINT32; 234 | else if (t == "float32" || t == "float") return Type::FLOAT32; 235 | else if (t == "float64" || t == "double") return Type::FLOAT64; 236 | return Type::INVALID; 237 | } 238 | 239 | struct PlyFile::PlyFileImpl 240 | { 241 | struct PlyDataCursor 242 | { 243 | size_t byteOffset{ 0 }; 244 | size_t totalSizeBytes{ 0 }; 245 | }; 246 | 247 | struct ParsingHelper 248 | { 249 | std::shared_ptr data; 250 | std::shared_ptr cursor; 251 | uint32_t list_size_hint; 252 | }; 253 | 254 | struct PropertyLookup 255 | { 256 | ParsingHelper * helper{ nullptr }; 257 | bool skip{ false }; 258 | size_t prop_stride{ 0 }; // precomputed 259 | size_t list_stride{ 0 }; // precomputed 260 | }; 261 | 262 | std::unordered_map userData; 263 | 264 | bool isBinary = false; 265 | bool isBigEndian = false; 266 | std::vector elements; 267 | std::vector comments; 268 | std::vector objInfo; 269 | uint8_t scratch[64]; // large enough for max list size 270 | 271 | void read(std::istream & is); 272 | void write(std::ostream & os, bool isBinary); 273 | 274 | std::shared_ptr request_properties_from_element(const std::string & elementKey, 275 | const std::vector propertyKeys, 276 | const uint32_t list_size_hint); 277 | 278 | void add_properties_to_element(const std::string & elementKey, 279 | const std::vector propertyKeys, 280 | const Type type, const size_t count, const uint8_t * data, const Type listType, const size_t listCount); 281 | 282 | size_t read_property_binary(const size_t & stride, void * dest, size_t & destOffset, size_t destSize, std::istream & is); 283 | size_t read_property_ascii(const Type & t, const size_t & stride, void * dest, size_t & destOffset, size_t destSize, std::istream & is); 284 | 285 | std::vector> make_property_lookup_table(); 286 | 287 | bool parse_header(std::istream & is); 288 | void parse_data(std::istream & is, bool firstPass); 289 | void read_header_format(std::istream & is); 290 | void read_header_element(std::istream & is); 291 | void read_header_property(std::istream & is); 292 | void read_header_text(std::string line, std::vector & place, int erase = 0); 293 | 294 | void write_header(std::ostream & os) noexcept; 295 | void write_ascii_internal(std::ostream & os) noexcept; 296 | void write_binary_internal(std::ostream & os) noexcept; 297 | void write_property_ascii(Type t, std::ostream & os, const uint8_t * src, size_t & srcOffset); 298 | void write_property_binary(std::ostream & os, const uint8_t * src, size_t & srcOffset, const size_t & stride) noexcept; 299 | }; 300 | 301 | PlyProperty::PlyProperty(std::istream & is) : isList(false) 302 | { 303 | std::string type; 304 | is >> type; 305 | if (type == "list") 306 | { 307 | std::string countType; 308 | is >> countType >> type; 309 | listType = property_type_from_string(countType); 310 | isList = true; 311 | } 312 | propertyType = property_type_from_string(type); 313 | is >> name; 314 | } 315 | 316 | PlyElement::PlyElement(std::istream & is) 317 | { 318 | is >> name >> size; 319 | } 320 | 321 | template inline T ply_read_ascii(std::istream & is) 322 | { 323 | T data; 324 | is >> data; 325 | return data; 326 | } 327 | 328 | template 329 | inline void endian_swap_buffer(uint8_t * data_ptr, const size_t num_bytes, const size_t stride) 330 | { 331 | for (size_t count = 0; count < num_bytes; count += stride) 332 | { 333 | *(reinterpret_cast(data_ptr)) = endian_swap(*(reinterpret_cast(data_ptr))); 334 | data_ptr += stride; 335 | } 336 | } 337 | 338 | template void ply_cast_ascii(void * dest, std::istream & is) 339 | { 340 | *(static_cast(dest)) = ply_read_ascii(is); 341 | } 342 | 343 | int64_t find_element(const std::string & key, const std::vector & list) 344 | { 345 | for (size_t i = 0; i < list.size(); i++) if (list[i].name == key) return i; 346 | return -1; 347 | } 348 | 349 | int64_t find_property(const std::string & key, const std::vector & list) 350 | { 351 | for (size_t i = 0; i < list.size(); ++i) if (list[i].name == key) return i; 352 | return -1; 353 | } 354 | 355 | // The `userData` table is an easy data structure for capturing what data the 356 | // user would like out of the ply file, but an inner-loop hash lookup is non-ideal. 357 | // The property lookup table flattens the table down into a 2D array optimized 358 | // for parsing. The first index is the element, and the second index is the property. 359 | std::vector> PlyFile::PlyFileImpl::make_property_lookup_table() 360 | { 361 | std::vector> element_property_lookup; 362 | 363 | for (auto & element : elements) 364 | { 365 | std::vector lookups; 366 | 367 | for (auto & property : element.properties) 368 | { 369 | PropertyLookup f; 370 | 371 | auto cursorIt = userData.find(hash_fnv1a(element.name + property.name)); 372 | if (cursorIt != userData.end()) f.helper = &cursorIt->second; 373 | else f.skip = true; 374 | 375 | f.prop_stride = PropertyTable[property.propertyType].stride; 376 | if (property.isList) f.list_stride = PropertyTable[property.listType].stride; 377 | 378 | lookups.push_back(f); 379 | } 380 | 381 | element_property_lookup.push_back(lookups); 382 | } 383 | 384 | return element_property_lookup; 385 | } 386 | 387 | bool PlyFile::PlyFileImpl::parse_header(std::istream & is) 388 | { 389 | std::string line; 390 | bool success = true; 391 | while (std::getline(is, line)) 392 | { 393 | std::istringstream ls(line); 394 | std::string token; 395 | ls >> token; 396 | if (token == "ply" || token == "PLY" || token == "") continue; 397 | else if (token == "comment") read_header_text(line, comments, 8); 398 | else if (token == "format") read_header_format(ls); 399 | else if (token == "element") read_header_element(ls); 400 | else if (token == "property") read_header_property(ls); 401 | else if (token == "obj_info") read_header_text(line, objInfo, 9); 402 | else if (token == "end_header") break; 403 | else success = false; // unexpected header field 404 | } 405 | return success; 406 | } 407 | 408 | void PlyFile::PlyFileImpl::read_header_text(std::string line, std::vector& place, int erase) 409 | { 410 | place.push_back((erase > 0) ? line.erase(0, erase) : line); 411 | } 412 | 413 | void PlyFile::PlyFileImpl::read_header_format(std::istream & is) 414 | { 415 | std::string s; 416 | (is >> s); 417 | if (s == "binary_little_endian") isBinary = true; 418 | else if (s == "binary_big_endian") isBinary = isBigEndian = true; 419 | } 420 | 421 | void PlyFile::PlyFileImpl::read_header_element(std::istream & is) 422 | { 423 | elements.emplace_back(is); 424 | } 425 | 426 | void PlyFile::PlyFileImpl::read_header_property(std::istream & is) 427 | { 428 | if (!elements.size()) throw std::runtime_error("no elements defined; file is malformed"); 429 | elements.back().properties.emplace_back(is); 430 | } 431 | 432 | size_t PlyFile::PlyFileImpl::read_property_binary(const size_t & stride, void * dest, size_t & destOffset, size_t destSize, std::istream & is) 433 | { 434 | if (destOffset + stride > destSize) 435 | { 436 | throw std::runtime_error("unexpected EOF. malformed file?"); 437 | } 438 | 439 | destOffset += stride; 440 | is.read((char*)dest, stride); 441 | return stride; 442 | } 443 | 444 | size_t PlyFile::PlyFileImpl::read_property_ascii(const Type & t, const size_t & stride, void * dest, size_t & destOffset, size_t destSize, std::istream & is) 445 | { 446 | if (destOffset + stride > destSize) 447 | { 448 | throw std::runtime_error("unexpected EOF. malformed file?"); 449 | } 450 | 451 | destOffset += stride; 452 | switch (t) 453 | { 454 | case Type::INT8: *((int8_t *)dest) = static_cast(ply_read_ascii(is)); break; 455 | case Type::UINT8: *((uint8_t *)dest) = static_cast(ply_read_ascii(is)); break; 456 | case Type::INT16: ply_cast_ascii(dest, is); break; 457 | case Type::UINT16: ply_cast_ascii(dest, is); break; 458 | case Type::INT32: ply_cast_ascii(dest, is); break; 459 | case Type::UINT32: ply_cast_ascii(dest, is); break; 460 | case Type::FLOAT32: ply_cast_ascii(dest, is); break; 461 | case Type::FLOAT64: ply_cast_ascii(dest, is); break; 462 | case Type::INVALID: throw std::invalid_argument("invalid ply property"); 463 | } 464 | return stride; 465 | } 466 | 467 | void PlyFile::PlyFileImpl::write_property_ascii(Type t, std::ostream & os, const uint8_t * src, size_t & srcOffset) 468 | { 469 | switch (t) 470 | { 471 | case Type::INT8: os << static_cast(*reinterpret_cast(src)); break; 472 | case Type::UINT8: os << static_cast(*reinterpret_cast(src)); break; 473 | case Type::INT16: os << *reinterpret_cast(src); break; 474 | case Type::UINT16: os << *reinterpret_cast(src); break; 475 | case Type::INT32: os << *reinterpret_cast(src); break; 476 | case Type::UINT32: os << *reinterpret_cast(src); break; 477 | case Type::FLOAT32: os << *reinterpret_cast(src); break; 478 | case Type::FLOAT64: os << *reinterpret_cast(src); break; 479 | case Type::INVALID: throw std::invalid_argument("invalid ply property"); 480 | } 481 | os << " "; 482 | srcOffset += PropertyTable[t].stride; 483 | } 484 | 485 | void PlyFile::PlyFileImpl::write_property_binary(std::ostream & os, const uint8_t * src, size_t & srcOffset, const size_t & stride) noexcept 486 | { 487 | os.write((char *)src, stride); 488 | srcOffset += stride; 489 | } 490 | 491 | void PlyFile::PlyFileImpl::read(std::istream & is) 492 | { 493 | std::vector> buffers; 494 | for (auto & entry : userData) buffers.push_back(entry.second.data); 495 | 496 | // Discover if we can allocate up front without parsing the file twice 497 | uint32_t list_hints = 0; 498 | for (auto & b : buffers) for (auto & entry : userData) {list_hints += entry.second.list_size_hint;(void)b;} 499 | 500 | // No list hints? Then we need to calculate how much memory to allocate 501 | if (list_hints == 0) 502 | { 503 | parse_data(is, true); 504 | } 505 | 506 | // Count the number of properties (required for allocation) 507 | // e.g. if we have properties x y and z requested, we ensure 508 | // that their buffer points to the same PlyData 509 | std::unordered_map unique_data_count; 510 | for (auto & ptr : buffers) unique_data_count[ptr.get()] += 1; 511 | 512 | // Since group-requested properties share the same cursor, 513 | // we need to find unique cursors so we only allocate once 514 | std::sort(buffers.begin(), buffers.end()); 515 | buffers.erase(std::unique(buffers.begin(), buffers.end()), buffers.end()); 516 | 517 | // We sorted by ptrs on PlyData, need to remap back onto its cursor in the userData table 518 | for (auto & b : buffers) 519 | { 520 | for (auto & entry : userData) 521 | { 522 | if (entry.second.data == b && b->buffer.get() == nullptr) 523 | { 524 | // If we didn't receive any list hints, it means we did two passes over the 525 | // file to compute the total length of all (potentially) variable-length lists 526 | if (list_hints == 0) 527 | { 528 | b->buffer = Buffer(entry.second.cursor->totalSizeBytes); 529 | } 530 | else 531 | { 532 | // otherwise, we can allocate up front, skipping the first pass. 533 | const size_t list_size_multiplier = (entry.second.data->isList ? entry.second.list_size_hint : 1); 534 | auto bytes_per_property = entry.second.data->count * PropertyTable[entry.second.data->t].stride * list_size_multiplier; 535 | bytes_per_property *= unique_data_count[b.get()]; 536 | b->buffer = Buffer(bytes_per_property); 537 | } 538 | 539 | } 540 | } 541 | } 542 | 543 | // Populate the data 544 | parse_data(is, false); 545 | 546 | // In-place big-endian to little-endian swapping if required 547 | if (isBigEndian) 548 | { 549 | for (auto & b : buffers) 550 | { 551 | uint8_t * data_ptr = b->buffer.get(); 552 | const size_t stride = PropertyTable[b->t].stride; 553 | const size_t buffer_size_bytes = b->buffer.size_bytes(); 554 | 555 | switch (b->t) 556 | { 557 | case Type::INT16: endian_swap_buffer(data_ptr, buffer_size_bytes, stride); break; 558 | case Type::UINT16: endian_swap_buffer(data_ptr, buffer_size_bytes, stride); break; 559 | case Type::INT32: endian_swap_buffer(data_ptr, buffer_size_bytes, stride); break; 560 | case Type::UINT32: endian_swap_buffer(data_ptr, buffer_size_bytes, stride); break; 561 | case Type::FLOAT32: endian_swap_buffer(data_ptr, buffer_size_bytes, stride); break; 562 | case Type::FLOAT64: endian_swap_buffer(data_ptr, buffer_size_bytes, stride); break; 563 | default: break; 564 | } 565 | } 566 | } 567 | } 568 | 569 | void PlyFile::PlyFileImpl::write(std::ostream & os, bool _isBinary) 570 | { 571 | for (auto & d : userData) { d.second.cursor->byteOffset = 0; } 572 | if (_isBinary) 573 | { 574 | isBinary = true; 575 | isBigEndian = false; 576 | write_binary_internal(os); 577 | } 578 | else 579 | { 580 | isBinary = false; 581 | isBigEndian = false; 582 | write_ascii_internal(os); 583 | } 584 | } 585 | 586 | void PlyFile::PlyFileImpl::write_binary_internal(std::ostream & os) noexcept 587 | { 588 | isBinary = true; 589 | 590 | write_header(os); 591 | 592 | uint8_t listSize[4] = { 0, 0, 0, 0 }; 593 | size_t dummyCount = 0; 594 | 595 | auto element_property_lookup = make_property_lookup_table(); 596 | 597 | size_t element_idx = 0; 598 | for (auto & e : elements) 599 | { 600 | for (size_t i = 0; i < e.size; ++i) 601 | { 602 | size_t property_index = 0; 603 | for (auto & p : e.properties) 604 | { 605 | auto & f = element_property_lookup[element_idx][property_index]; 606 | auto * helper = f.helper; 607 | if (f.skip || helper == nullptr) continue; 608 | 609 | if (p.isList) 610 | { 611 | std::memcpy(listSize, &p.listCount, sizeof(uint32_t)); 612 | write_property_binary(os, listSize, dummyCount, f.list_stride); 613 | write_property_binary(os, (helper->data->buffer.get_const() + helper->cursor->byteOffset), helper->cursor->byteOffset, f.prop_stride * p.listCount); 614 | } 615 | else 616 | { 617 | write_property_binary(os, (helper->data->buffer.get_const() + helper->cursor->byteOffset), helper->cursor->byteOffset, f.prop_stride); 618 | } 619 | property_index++; 620 | } 621 | } 622 | element_idx++; 623 | } 624 | } 625 | 626 | void PlyFile::PlyFileImpl::write_ascii_internal(std::ostream & os) noexcept 627 | { 628 | write_header(os); 629 | 630 | auto element_property_lookup = make_property_lookup_table(); 631 | 632 | size_t element_idx = 0; 633 | for (auto & e : elements) 634 | { 635 | for (size_t i = 0; i < e.size; ++i) 636 | { 637 | size_t property_index = 0; 638 | for (auto & p : e.properties) 639 | { 640 | auto & f = element_property_lookup[element_idx][property_index]; 641 | auto * helper = f.helper; 642 | if (f.skip || helper == nullptr) continue; 643 | 644 | if (p.isList) 645 | { 646 | os << p.listCount << " "; 647 | for (size_t j = 0; j < p.listCount; ++j) 648 | { 649 | write_property_ascii(p.propertyType, os, (helper->data->buffer.get() + helper->cursor->byteOffset), helper->cursor->byteOffset); 650 | } 651 | } 652 | else 653 | { 654 | write_property_ascii(p.propertyType, os, (helper->data->buffer.get() + helper->cursor->byteOffset), helper->cursor->byteOffset); 655 | } 656 | property_index++; 657 | } 658 | os << "\n"; 659 | } 660 | element_idx++; 661 | } 662 | } 663 | 664 | void PlyFile::PlyFileImpl::write_header(std::ostream & os) noexcept 665 | { 666 | const std::locale & fixLoc = std::locale("C"); 667 | os.imbue(fixLoc); 668 | 669 | os << "ply\n"; 670 | if (isBinary) os << ((isBigEndian) ? "format binary_big_endian 1.0" : "format binary_little_endian 1.0") << "\n"; 671 | else os << "format ascii 1.0\n"; 672 | 673 | for (const auto & comment : comments) os << "comment " << comment << "\n"; 674 | 675 | auto property_lookup = make_property_lookup_table(); 676 | 677 | size_t element_idx = 0; 678 | for (auto & e : elements) 679 | { 680 | os << "element " << e.name << " " << e.size << "\n"; 681 | size_t property_idx = 0; 682 | for (const auto & p : e.properties) 683 | { 684 | PropertyLookup & lookup = property_lookup[element_idx][property_idx]; 685 | 686 | if (!lookup.skip) 687 | { 688 | if (p.isList) 689 | { 690 | os << "property list " << PropertyTable[p.listType].str << " " 691 | << PropertyTable[p.propertyType].str << " " << p.name << "\n"; 692 | } 693 | else 694 | { 695 | os << "property " << PropertyTable[p.propertyType].str << " " << p.name << "\n"; 696 | } 697 | } 698 | property_idx++; 699 | } 700 | element_idx++; 701 | } 702 | os << "end_header\n"; 703 | } 704 | 705 | std::shared_ptr PlyFile::PlyFileImpl::request_properties_from_element(const std::string & elementKey, 706 | const std::vector propertyKeys, 707 | const uint32_t list_size_hint) 708 | { 709 | if (elements.empty()) throw std::runtime_error("header had no elements defined. malformed file?"); 710 | if (elementKey.empty()) throw std::invalid_argument("`elementKey` argument is empty"); 711 | if (propertyKeys.empty()) throw std::invalid_argument("`propertyKeys` argument is empty"); 712 | 713 | std::shared_ptr out_data = std::make_shared(); 714 | 715 | const int64_t elementIndex = find_element(elementKey, elements); 716 | 717 | std::vector keys_not_found; 718 | 719 | // Sanity check if the user requested element is in the pre-parsed header 720 | if (elementIndex >= 0) 721 | { 722 | // We found the element 723 | const PlyElement & element = elements[elementIndex]; 724 | 725 | // Each key in `propertyKey` gets an entry into the userData map (keyed by a hash of 726 | // element name and property name), but groups of properties (requested from the 727 | // public api through this function) all share the same `ParsingHelper`. When it comes 728 | // time to .read(), we check the number of unique PlyData shared pointers 729 | // and allocate a single buffer that will be used by each property key group. 730 | // That way, properties like, {"x", "y", "z"} will all be put into the same buffer. 731 | 732 | ParsingHelper helper; 733 | helper.data = out_data; 734 | helper.data->count = element.size; // how many items are in the element? 735 | helper.data->isList = false; 736 | helper.data->t = Type::INVALID; 737 | helper.cursor = std::make_shared(); 738 | helper.list_size_hint = list_size_hint; 739 | 740 | // Find each of the keys 741 | for (const auto & key : propertyKeys) 742 | { 743 | const int64_t propertyIndex = find_property(key, element.properties); 744 | if (propertyIndex < 0) keys_not_found.push_back(key); 745 | } 746 | 747 | if (keys_not_found.size()) 748 | { 749 | std::stringstream ss; 750 | for (auto & str : keys_not_found) ss << str << ", "; 751 | throw std::invalid_argument("the following property keys were not found in the header: " + ss.str()); 752 | } 753 | 754 | for (const auto & key : propertyKeys) 755 | { 756 | const int64_t propertyIndex = find_property(key, element.properties); 757 | const PlyProperty & property = element.properties[propertyIndex]; 758 | helper.data->t = property.propertyType; 759 | helper.data->isList = property.isList; 760 | auto result = userData.insert(std::pair(hash_fnv1a(element.name + property.name), helper)); 761 | if (result.second == false) 762 | { 763 | throw std::invalid_argument("element-property key has already been requested: " + element.name + " " + property.name); 764 | } 765 | } 766 | 767 | // Sanity check that all properties share the same type 768 | std::vector propertyTypes; 769 | for (const auto & key : propertyKeys) 770 | { 771 | const int64_t propertyIndex = find_property(key, element.properties); 772 | const PlyProperty & property = element.properties[propertyIndex]; 773 | propertyTypes.push_back(property.propertyType); 774 | } 775 | 776 | if (std::adjacent_find(propertyTypes.begin(), propertyTypes.end(), std::not_equal_to()) != propertyTypes.end()) 777 | { 778 | throw std::invalid_argument("all requested properties must share the same type."); 779 | } 780 | } 781 | else throw std::invalid_argument("the element key was not found in the header: " + elementKey); 782 | 783 | return out_data; 784 | } 785 | 786 | void PlyFile::PlyFileImpl::add_properties_to_element(const std::string & elementKey, 787 | const std::vector propertyKeys, 788 | const Type type, const size_t count, const uint8_t * data, const Type listType, const size_t listCount) 789 | { 790 | ParsingHelper helper; 791 | helper.data = std::make_shared(); 792 | helper.data->count = count; 793 | helper.data->t = type; 794 | helper.data->buffer = Buffer(data); // we should also set size for safety reasons 795 | helper.cursor = std::make_shared(); 796 | 797 | auto create_property_on_element = [&](PlyElement & e) 798 | { 799 | for (auto key : propertyKeys) 800 | { 801 | PlyProperty newProp = (listType == Type::INVALID) ? PlyProperty(type, key) : PlyProperty(listType, type, key, listCount); 802 | userData.insert(std::pair(hash_fnv1a(elementKey + key), helper)); 803 | e.properties.push_back(newProp); 804 | } 805 | }; 806 | 807 | const int64_t idx = find_element(elementKey, elements); 808 | if (idx >= 0) 809 | { 810 | PlyElement & e = elements[idx]; 811 | create_property_on_element(e); 812 | } 813 | else 814 | { 815 | PlyElement newElement = (listType == Type::INVALID) ? PlyElement(elementKey, count) : PlyElement(elementKey, count); 816 | create_property_on_element(newElement); 817 | elements.push_back(newElement); 818 | } 819 | } 820 | 821 | void PlyFile::PlyFileImpl::parse_data(std::istream & is, bool firstPass) 822 | { 823 | std::function read; 824 | std::function skip; 825 | 826 | const auto start = is.tellg(); 827 | 828 | uint32_t listSize = 0; 829 | size_t dummyCount = 0; 830 | std::string skip_ascii_buffer; 831 | 832 | // Special case mirroring read_property_binary but for list types; this 833 | // has an additional big endian check to flip the data in place immediately 834 | // after reading. We do this as a performance optimization; endian flipping is 835 | // done on regular properties as a post-process after reading (also for optimization) 836 | // but we need the correct little-endian list count as we read the file. 837 | auto read_list_binary = [this](const Type & t, void * dst, size_t & destOffset, const size_t & stride, std::istream & _is) noexcept 838 | { 839 | destOffset += stride; 840 | _is.read((char*)dst, stride); 841 | 842 | if (isBigEndian) 843 | { 844 | switch (t) 845 | { 846 | case Type::INT16: *(int16_t*)dst = endian_swap(*(int16_t*)dst); break; 847 | case Type::UINT16: *(uint16_t*)dst = endian_swap(*(uint16_t*)dst); break; 848 | case Type::INT32: *(int32_t*)dst = endian_swap(*(int32_t*)dst); break; 849 | case Type::UINT32: *(uint32_t*)dst = endian_swap(*(uint32_t*)dst); break; 850 | default: break; 851 | } 852 | } 853 | 854 | return stride; 855 | }; 856 | 857 | if (isBinary) 858 | { 859 | read = [this, &listSize, &dummyCount, &read_list_binary](PropertyLookup & f, const PlyProperty & p, uint8_t * dest, size_t & destOffset, size_t destSize, std::istream & _is) 860 | { 861 | if (!p.isList) 862 | { 863 | return read_property_binary(f.prop_stride, dest + destOffset, destOffset, destSize, _is); 864 | } 865 | read_list_binary(p.listType, &listSize, dummyCount, f.list_stride, _is); // the list size 866 | return read_property_binary(f.prop_stride * listSize, dest + destOffset, destOffset, destSize, _is); // properties in list 867 | }; 868 | skip = [this, &listSize, &dummyCount, &read_list_binary](PropertyLookup & f, const PlyProperty & p, std::istream & _is) noexcept 869 | { 870 | if (!p.isList) 871 | { 872 | _is.read((char*)scratch, f.prop_stride); 873 | return f.prop_stride; 874 | } 875 | read_list_binary(p.listType, &listSize, dummyCount, f.list_stride, _is); // the list size (does not count for memory alloc) 876 | auto bytes_to_skip = f.prop_stride * listSize; 877 | _is.ignore(bytes_to_skip); 878 | return bytes_to_skip; 879 | }; 880 | } 881 | else 882 | { 883 | read = [this, &listSize, &dummyCount](PropertyLookup & f, const PlyProperty & p, uint8_t * dest, size_t & destOffset, size_t destSize, std::istream & _is) 884 | { 885 | if (!p.isList) 886 | { 887 | read_property_ascii(p.propertyType, f.prop_stride, dest + destOffset, destOffset, destSize, _is); 888 | } 889 | else 890 | { 891 | read_property_ascii(p.listType, f.list_stride, &listSize, dummyCount, sizeof(listSize), _is); // the list size 892 | for (size_t i = 0; i < listSize; ++i) 893 | { 894 | read_property_ascii(p.propertyType, f.prop_stride, dest + destOffset, destOffset, destOffset, _is); 895 | } 896 | } 897 | }; 898 | skip = [this, &listSize, &dummyCount, &skip_ascii_buffer](PropertyLookup & f, const PlyProperty & p, std::istream & _is) noexcept 899 | { 900 | skip_ascii_buffer.clear(); 901 | if (p.isList) 902 | { 903 | dummyCount = 0; 904 | read_property_ascii(p.listType, f.list_stride, &listSize, dummyCount, sizeof(listSize), _is); // the list size (does not count for memory alloc) 905 | for (size_t i = 0; i < listSize; ++i) _is >> skip_ascii_buffer; // properties in list 906 | return listSize * f.prop_stride; 907 | } 908 | _is >> skip_ascii_buffer; 909 | return f.prop_stride; 910 | }; 911 | } 912 | 913 | std::vector> element_property_lookup = make_property_lookup_table(); 914 | size_t element_idx = 0; 915 | size_t property_idx = 0; 916 | ParsingHelper * helper {nullptr}; 917 | 918 | // This is the inner import loop 919 | for (auto & element : elements) 920 | { 921 | for (size_t count = 0; count < element.size; ++count) 922 | { 923 | property_idx = 0; 924 | for (auto & property : element.properties) 925 | { 926 | PropertyLookup & lookup = element_property_lookup[element_idx][property_idx]; 927 | 928 | if (!lookup.skip) 929 | { 930 | helper = lookup.helper; 931 | if (firstPass) 932 | { 933 | helper->cursor->totalSizeBytes += skip(lookup, property, is); 934 | 935 | // These lines will be changed when tinyply supports 936 | // variable length lists. We add it here so our header data structure 937 | // contains enough info to write it back out again (e.g. transcoding). 938 | if (property.listCount == 0) property.listCount = listSize; 939 | if (property.listCount != listSize) throw std::runtime_error("variable length lists are not supported yet."); 940 | } 941 | else 942 | { 943 | const size_t destSize = helper->data->buffer.size_bytes(); 944 | read(lookup, property, helper->data->buffer.get(), helper->cursor->byteOffset, destSize, is); 945 | } 946 | } 947 | else 948 | { 949 | skip(lookup, property, is); 950 | } 951 | property_idx++; 952 | } 953 | } 954 | element_idx++; 955 | } 956 | 957 | // Reset istream position to the start of the data 958 | if (firstPass) is.seekg(start, is.beg); 959 | } 960 | 961 | // Wrap the public interface: 962 | 963 | PlyFile::PlyFile() { impl.reset(new PlyFileImpl()); } 964 | PlyFile::~PlyFile() { } 965 | bool PlyFile::parse_header(std::istream & is) { return impl->parse_header(is); } 966 | void PlyFile::read(std::istream & is) { return impl->read(is); } 967 | void PlyFile::write(std::ostream & os, bool isBinary) { return impl->write(os, isBinary); } 968 | std::vector PlyFile::get_elements() const { return impl->elements; } 969 | std::vector & PlyFile::get_comments() { return impl->comments; } 970 | std::vector PlyFile::get_info() const { return impl->objInfo; } 971 | bool PlyFile::is_binary_file() const { return impl->isBinary; } 972 | std::shared_ptr PlyFile::request_properties_from_element(const std::string & elementKey, 973 | const std::vector propertyKeys, 974 | const uint32_t list_size_hint) 975 | { 976 | return impl->request_properties_from_element(elementKey, propertyKeys, list_size_hint); 977 | } 978 | void PlyFile::add_properties_to_element(const std::string & elementKey, 979 | const std::vector propertyKeys, 980 | const Type type, const size_t count, const uint8_t * data, const Type listType, const size_t listCount) 981 | { 982 | return impl->add_properties_to_element(elementKey, propertyKeys, type, count, data, listType, listCount); 983 | } 984 | 985 | } // end namespace tinyply 986 | 987 | #endif // end TINYPLY_IMPLEMENTATION 988 | --------------------------------------------------------------------------------