Featured image of post OOP 大作业 开发日记

OOP 大作业 开发日记

Upd on 2024.11.06

今天要开始写 OOP 的大作业了,ljj 给发了一个 cinolib 的库,简单扫了一眼,里面有很多Samples,先根据 Readme 试一下能不能复现一下这些 Samples

1
2
3
mkdir build
cd build
cmake ..

cmake 的过程中在几个环节卡住了,看了一下应该是 TetgenTriangle 两个库要从 github 上拉取,挂上梯子即可。

cmake 成功,编译一下试试

1
k

这里记得开一下多核编译,不然编译巨慢

编译成功,运行一个看看

OK 能跑


下面看一下整个CMake的构建链,方便我们自己的大作业也使用

使用 CLion 打开整个 examples 文件夹,把工具链设为 Visual Studio

直接进 examples/01_trimesh_viewer 看源码

进来三行头文件,不知道干嘛用的,先不管

1
2
std::string s = (argc==2) ? std::string(argv[1]) : std::string(DATA_PATH) + "/bunny.obj";
DrawableTrimesh<> m(s.c_str());

这两行显然是用来加载模型文件的,记得大作业要求里有一个就是支持模型加载,进 DrawableTrimesh 看一看具体逻辑

类型声明如下

1
2
3
4
5
template<class M = Mesh_std_attributes, // default template arguments
         class V = Vert_std_attributes,
         class E = Edge_std_attributes,
         class P = Polygon_std_attributes>
class DrawableTrimesh : public AbstractDrawablePolygonMesh<Trimesh<M,V,E,P>>

不懂里面几个名词的定义,去问了下AI:

  • Mesh 是由 VertexEdgePolygon 组成> 的3D对象。
  • Vertex 是3D空间中的点。
  • Edge 是连接两个顶点的线段。
  • Polygon 是由多个顶点和边组成的平面区域。

然后再看下四个默认模板参数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
struct Mesh_std_attributes
{
    std::string filename;
    bool        update_normals = true;
    bool        update_bbox    = true;
};

struct Vert_std_attributes
{
    vec3d          normal  = vec3d(0,0,0);
    Color          color   = Color::WHITE();
    vec3d          uvw     = vec3d(0,0,0);
    int            label   = -1;
    float          quality = 0.0;
    std::bitset<8> flags   = 0x00;
};

struct Edge_std_attributes
{
    Color          color = Color::BLACK();
    int            label = -1;
    std::bitset<8> flags = 0x00;
};

struct Polygon_std_attributes
{
    vec3d          normal  = vec3d(0,0,0);
    Color          color   = Color::WHITE();
    int            label   = -1;
    float          quality = 0.0;
    float          AO      = 1.0;
    std::bitset<8> flags   = 0x00;
};

大概理解了一下,Mesh 就是整个模型对象,Vert,Edge,Polygon 都是模型对象中的组成部分。

然后再看一下 Trimesh 的声明

1
2
3
4
5
template<class M = Mesh_std_attributes, // default template arguments
         class V = Vert_std_attributes,
         class E = Edge_std_attributes,
         class P = Polygon_std_attributes>
class Trimesh : public AbstractPolygonMesh<M,V,E,P>

猜也能猜到,这里的AbstractPolygonMesh应该就是各种多边形 Mesh 的抽象基类,看一眼继承关系

Samples 里面给了很多个 Viewer ,我对图形学一点不懂,这个坑后面再填吧。

一路展开下去,整个继承链是这样的

1
2
DrawableTrimesh -> AbstractDrawablePolygonMesh<Trimesh> -> Mesh, DrawableObject
Trimesh -> AbstractPolygonMesh -> AbstractMesh

有点面向接口的感觉了。

然后我们往下看看他是怎么从 obj 文件中加载数据的

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
explicit DrawableTrimesh(const char * filename) : Trimesh<M,V,E,P>(filename)
{
  this->init_drawable_stuff();
}

template<class M, class V, class E, class P>
Trimesh<M,V,E,P>::Trimesh(const char * filename)
{
    this->load(filename);
}

template<class M, class V, class E, class P>
void AbstractPolygonMesh<M,V,E,P>::load(const char * filename)
{
    this->clear();
    this->mesh_data().filename = std::string(filename);

    std::vector<vec3d>             pos;      // vertex xyz positions
    std::vector<vec3d>             tex;      // vertex uv(w) texture coordinates
    std::vector<vec3d>             nor;      // vertex normals
    std::vector<std::vector<uint>> poly_pos; // polygons with references to pos
    std::vector<std::vector<uint>> poly_tex; // polygons with references to tex
    std::vector<std::vector<uint>> poly_nor; // polygons with references to nor
    std::vector<Color>             poly_col; // per polygon colors
    std::vector<int>               poly_lab; // per polygon labels

    std::string str(filename);
    std::string filetype = str.substr(str.size()-4,4);

    if (filetype.compare(".off") == 0 ||
        filetype.compare(".OFF") == 0)
    {
        read_OFF(filename, pos, poly_pos, poly_col);
    }
    else if (filetype.compare(".obj") == 0 ||
             filetype.compare(".OBJ") == 0)
    {
        //read_OBJ(filename, pos, poly_pos);
        read_OBJ(filename, pos, tex, nor, poly_pos, poly_tex, poly_nor, poly_col, poly_lab);
    }
    else if (filetype.compare(".stl") == 0 ||
             filetype.compare(".STL") == 0)
    {
        std::vector<uint> tris;
        read_STL(filename, pos, tris);
        poly_pos = polys_from_serialized_vids(tris, 3);
    }
    else
    {
        std::cerr << "ERROR : " << __FILE__ << ", line " << __LINE__ << " : load() : file format not supported yet " << std::endl;
    }

    init(pos, tex, nor, poly_pos, poly_tex, poly_nor, poly_col, poly_lab);
}

这里看到他对 .off .obj .stl 三种文件格式做了支持,我们先看看 obj 是怎么写的

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
void read_OBJ(const char                     * filename,
              std::vector<vec3d>             & pos,         // vertex xyz positions
              std::vector<vec3d>             & tex,         // vertex uv(w) texture coordinates
              std::vector<vec3d>             & nor,         // vertex normals
              std::vector<std::vector<uint>> & poly_pos,    // polygons with references to pos
              std::vector<std::vector<uint>> & poly_tex,    // polygons with references to tex
              std::vector<std::vector<uint>> & poly_nor,    // polygons with references to nor
              std::vector<Color>             & poly_col,    // per polygon colors
              std::vector<int>               & poly_lab)    // per polygon labels (OBJ groups)
{
    std::string  diffuse_path;
    std::string  specular_path;
    std::string  normal_path;
    read_OBJ(filename, pos, tex, nor, poly_pos, poly_tex, poly_nor, poly_col, poly_lab, diffuse_path, specular_path, normal_path);
}

可以看到这里做了一层委托

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
void read_OBJ(const char                     * filename,
              std::vector<vec3d>             & pos,           // vertex xyz positions
              std::vector<vec3d>             & tex,           // vertex uv(w) texture coordinates
              std::vector<vec3d>             & nor,           // vertex normals
              std::vector<std::vector<uint>> & poly_pos,      // polygons with references to pos
              std::vector<std::vector<uint>> & poly_tex,      // polygons with references to tex
              std::vector<std::vector<uint>> & poly_nor,      // polygons with references to nor
              std::vector<Color>             & poly_col,      // per polygon colors
              std::vector<int>               & poly_lab,      // per polygon labels (cluster by OBJ groups "g")
              std::string                    & diffuse_path,  // path of the image encoding the diffuse  texture component
              std::string                    & specular_path, // path of the image encoding the specular texture component
              std::string                    & normal_path)   // path of the image encoding the normal   texture component
{
    setlocale(LC_NUMERIC, "en_US.UTF-8"); // makes sure "." is the decimal separator

    pos.clear();
    tex.clear();
    nor.clear();
    poly_pos.clear();
    poly_tex.clear();
    poly_nor.clear();
    poly_col.clear();
    poly_lab.clear();
    diffuse_path.clear();
    specular_path.clear();
    normal_path.clear();

    std::ifstream f(filename);
    if(!f.is_open())
    {
        std::cerr << "ERROR : " << __FILE__ << ", line " << __LINE__ << " : read_OBJ() : couldn't open input file " << filename << std::endl;
        exit(-1);
    }

    int fresh_label = 0;
    std::map<std::string,Color> color_map;
    Color curr_color = Color::WHITE();     // set WHITE as default color
    bool has_per_face_color = false;
    bool has_groups         = false;

    std::string line;
    while(std::getline(f,line))
    {
        switch(line[0])
        {
            case 'v':
            {
                // http://stackoverflow.com/questions/16839658/printf-width-specifier-to-maintain-precision-of-floating-point-value
                //
                double a, b, c;
                     if(sscanf(line.data(), "v  %lf %lf %lf", &a, &b, &c) == 3) pos.push_back(vec3d(a,b,c));
                else if(sscanf(line.data(), "vt %lf %lf %lf", &a, &b, &c) == 3) tex.push_back(vec3d(a,b,c));
                else if(sscanf(line.data(), "vt %lf %lf %lf", &a, &b, &c) == 2) tex.push_back(vec3d(a,b,0));
                else if(sscanf(line.data(), "vn %lf %lf %lf", &a, &b, &c) == 3) nor.push_back(vec3d(a,b,c));
                break;
            }

            case 'f':
            {
                line = line.substr(1,line.size()-1); // discard the 'f' letter
                std::istringstream ss(line);
                std::vector<uint> p_pos, p_tex, p_nor;
                for(std::string sub_str; ss >> sub_str;)
                {
                    int v_pos, v_tex, v_nor;
                    read_point_id(strdup(sub_str.c_str()), v_pos, v_tex, v_nor);
                    if (v_pos >= 0) p_pos.push_back(v_pos);
                    if (v_tex >= 0) p_tex.push_back(v_tex);
                    if (v_nor >= 0) p_nor.push_back(v_nor);
                }
                if (!p_tex.empty()) poly_tex.push_back(p_tex);
                if (!p_nor.empty()) poly_nor.push_back(p_nor);
                if (!p_pos.empty())
                {
                    poly_pos.push_back(p_pos);
                    poly_col.push_back(curr_color);
                }
                poly_lab.push_back(fresh_label);
                break;
            }

            case 'u':
            {
                char mat_c[1024];
                if (sscanf(line.data(), "usemtl %s", mat_c) == 1)
                {
                    auto query = color_map.find(std::string(mat_c));
                    if (query != color_map.end())
                    {
                        curr_color = query->second;
                    }
                    else std::cerr << "WARNING: could not find material: " << mat_c << std::endl;
                }
                break;
            }

            case 'm':
            {
                char mtu_c[1024];
                if(sscanf(line.data(), "mtllib %[^\n]s", mtu_c) == 1)
                {
                    std::string s0(filename);
                    std::string s1(mtu_c);
                    std::string s2 = get_file_path(s0) + get_file_name(s1);

                    // this fix shouldn't be here, but...
                    // https://stackoverflow.com/questions/1279779/what-is-the-difference-between-r-and-n
                    if(!s2.empty() && s2[s2.size()-1]=='\r')
                    {
                        s2.erase(s2.size()-1);
                    }

                    if(read_MTU(s2.c_str(), color_map, diffuse_path, specular_path, normal_path))
                    {
                        has_per_face_color = true;
                    }
                }
                break;
            }

            case 'g':
            {
                has_groups = true;
                fresh_label++;
                break;
            }
        }
    }
    f.close();

    if(!has_per_face_color) poly_col.clear();
    if(!has_groups)         poly_lab.clear();
}

在万能的 Wikipedia 上找到了 obj 格式介绍

居然是用这种方式描述的吗,我还以为是二进制文件呢

用记事本打开了一个 .obj 文件,还真是直接用文本存的,啊这

那就是说 .obj 的序列化与反序列化其实可以直接从这个库里借鉴了


OK,读到这里,对于模型的定义和加载基本上在脑子里有一个基本的思路了,下面看一下模型是怎么渲染的

1
2
3
4
5
GLcanvas gui;
SurfaceMeshControls<DrawableTrimesh<>> menu(&m, &gui);
gui.push(&m);
gui.push(&menu);
return gui.launch();

看到有一个类 GLcanvas ,看字面意思像是画布,进去看看

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class GLcanvas
{
    public:

        GLFWwindow                        *window;
        std::vector<const DrawableObject*> drawlist;
        std::vector<Marker>                markers;
        std::vector<SideBarItem*>          side_bar_items;
        bool                               show_side_bar      = false;
        bool                               lazy_polling        = true; // wait for events if true, regular polling otherwise
        Color                              color_background   = Color::WHITE();
        float                              side_bar_width     = 0.2f;
        float                              side_bar_alpha     = 1.f;
        float                              font_size          = 13.f;
        bool                               show_axis          = false;
        bool                               depth_cull_markers = true; // skip occluded 3D markers, testing their depth with the Z-buffer
        double                             DPI_factor;

        //::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

        // ImGui and GLFW do not yet handle multi windows properly.
        // This variable ensures that only one window will create
        // and handle the ImGui context. As a result, only that
        // window will benefit from the functionalities implemented
        // with ImGui, such as the side bar with visual controls and
        // the visual markers
        bool owns_ImGui = false;

        //::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

        Camera<double> camera;
        Trackball      trackball;

        //::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
}

类的方法很多,但是从成员变量就能看出来这确实是个对 GLFWwindow 的封装,我们先看一眼构造函数的逻辑

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
GLcanvas::GLcanvas(const int width, const int height)
{
    // make sure that only the first window
    // in the app creates a ImGui context
    static int window_count = 0;
    owns_ImGui = (window_count==0);
    window_count++;

    camera = Camera<double>(width,height);
    camera.toggle_persp_ortho(); // make it perspective

    glfwInit();
    window = glfwCreateWindow(width, height, "", NULL, NULL);
    if(!window) glfwTerminate();

    glfwSwapInterval(1); // enable vsync

    // pass the window handler (i.e. this whole class) to the GLFW window,
    // so that I can access the methods of GLcanvas2from within the static
    // callbakcs defined within the class itself....
    // Despite ugly, I do not foresee better ways to interact with the
    // GLFW event handling system!
    // See:
    //      https://github.com/glfw/glfw/issues/815
    //      https://stackoverflow.com/questions/55145966/what-does-glfwgetwindowuserpointer-do
    glfwSetWindowUserPointer(window, (void*)this);

    // register GLFW callbacks
    glfwSetWindowSizeCallback (window, window_size_event );
    glfwSetKeyCallback        (window, key_event         );
    glfwSetMouseButtonCallback(window, mouse_button_event);
    glfwSetCursorPosCallback  (window, cursor_event      );
    glfwSetScrollCallback     (window, scroll_event      );

    // intialize OpenGL environment
    glfwMakeContextCurrent(window);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LEQUAL);

    // to handle high DPI displays
    update_DPI_factor();

    if(owns_ImGui)
    {
        // initialize ImGui backend for visual controls...
        IMGUI_CHECKVERSION();
        ImGui::CreateContext();
        ImGui::StyleColorsDark();
        ImGui_ImplGlfw_InitForOpenGL(window, false);
        ImGui_ImplOpenGL2_Init();
        // NOTE: since ImGui does not support dynamic font sizing, I am using oversized fonts
        // (10x) to account for zoom factor (downscaling is visually better than upscaling)
        // https://github.com/ocornut/imgui/issues/797
        ImGuiIO &io = ImGui::GetIO();
        io.IniFilename = NULL;
        io.Fonts->Clear();
        io.Fonts->AddFontFromMemoryCompressedTTF(droid_sans_data, droid_sans_size, font_size*10.f);
        io.FontGlobalScale = 0.1f; // compensate for high-res fonts
    }
}

感觉就是调用了各种 gl 的功能?也可以直接借来用


画布这一块到此为止,我们看一下画布中的内容是如何渲染的,一路追踪下去:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
int GLcanvas::launch(std::initializer_list<GLcanvas*> additional_windows)
{
    while(true)
    {
        glfwMakeContextCurrent(window);
        draw();
        if(glfwWindowShouldClose(window)) return EXIT_SUCCESS;

        for(auto it=additional_windows.begin(); it!=additional_windows.end(); ++it)
        {
            glfwMakeContextCurrent((*it)->window);
            (*it)->draw();
            if(glfwWindowShouldClose((*it)->window)) return EXIT_SUCCESS;
        }

        if(lazy_polling) glfwWaitEvents();
        else             glfwPollEvents();
    }
    return EXIT_SUCCESS;
}

看一下 draw

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
void GLcanvas::draw()
{
    glfwMakeContextCurrent(window);    
    glClearColor(color_background.r,
                 color_background.g,
                 color_background.b,
                 color_background.a);
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

    // draw your 3D scene
    for(auto obj : drawlist) obj->draw();

    if(owns_ImGui)
    {
        // draw markers and visual controls
        ImGui_ImplOpenGL2_NewFrame();
        ImGui_ImplGlfw_NewFrame();
        ImGui::NewFrame();
        draw_markers();
        if(show_side_bar) draw_side_bar();
        ImGui::Render();
        ImGui_ImplOpenGL2_RenderDrawData(ImGui::GetDrawData());
    }

    if(show_axis) draw_axis();

    glfwSwapBuffers(window);
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
class DrawableObject
{
    public :

        explicit  DrawableObject(){}
        virtual  ~DrawableObject(){}

        //::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

        virtual  ObjectType  object_type()                    const = 0;
        virtual  void        draw(const float scene_size = 1) const = 0;  // do rendering
        virtual  vec3d       scene_center()                   const = 0;  // get position in space
        virtual  float       scene_radius()                   const = 0;  // get size (approx. radius of the bounding sphere)
};

核心应该就是这个 DrawableObject 的 draw 接口,看一下 AbstractDrawablePolygonMesh 是怎么实现的

1
2
3
4
5
6
7
8
9
void AbstractDrawablePolygonMesh<Mesh>::draw(const float) const
{
    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
    glMultMatrixd(this->T.transpose()._vec);
    render(drawlist_marked);
    render(drawlist);
    glPopMatrix();
}

再往下看一下 render 的逻辑

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
void render(const RenderData & data)
{
    data.material.apply();

    glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
    glEnable(GL_DEPTH_TEST);
    glDisable(GL_CULL_FACE);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glEnable(GL_BLEND);
    glPolygonOffset(1.0, 1);

    if(data.draw_mode & DRAW_TRIS)
    {
        if(data.draw_mode & DRAW_TRI_POINTS)
        {
            glDisable(GL_LIGHTING);
            render_tris(data);
        }
        else if(data.draw_mode & DRAW_TRI_SMOOTH)
        {
            glEnable(GL_LIGHTING);
            glShadeModel(GL_SMOOTH);
            render_tris(data);
        }
        else // default: FLAT shading
        {
            glEnable(GL_LIGHTING);
            glShadeModel(GL_SMOOTH); // flatness is given by input normals
            render_tris(data);
        }

        if(data.draw_mode & DRAW_SEGS)
        {
            glDisable(GL_POLYGON_OFFSET_FILL);
            glPushAttrib(GL_POLYGON_BIT);
            glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
            render_segs(data);
            glPopAttrib();
            glEnable(GL_POLYGON_OFFSET_FILL);
        }
    }
}

里面又嵌了一层 render_tris 的逻辑

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
void render_tris(const RenderData & data)
{
    if(data.draw_mode & DRAW_TRI_POINTS)
    {
        glEnableClientState(GL_COLOR_ARRAY);
        glColorPointer(4, GL_FLOAT, 0, data.tri_v_colors.data());
        glEnableClientState(GL_VERTEX_ARRAY);
        glVertexPointer(3, GL_FLOAT, 0, data.tri_coords.data());
        glPointSize(data.seg_width);
        glDrawArrays(GL_POINTS, 0, (GLsizei)(data.tri_coords.size()/3));
        glDisableClientState(GL_VERTEX_ARRAY);
        glDisableClientState(GL_COLOR_ARRAY);
    }
    else
    {
        if(data.draw_mode & DRAW_TRI_TEXTURE1D)
        {
            glBindTexture(GL_TEXTURE_1D, data.texture.id);
            glEnableClientState(GL_TEXTURE_COORD_ARRAY);
            glTexCoordPointer(1, GL_FLOAT, 0, data.tri_text.data());
            glColor3f(1,1,1);
            glEnable(GL_COLOR_MATERIAL);
            glEnable(GL_TEXTURE_1D);
        }
        else if(data.draw_mode & DRAW_TRI_TEXTURE2D)
        {
            glBindTexture(GL_TEXTURE_2D, data.texture.id);
            glEnableClientState(GL_TEXTURE_COORD_ARRAY);
            glTexCoordPointer(2, GL_FLOAT, 0, data.tri_text.data());
            glColor3f(1,1,1);
            glEnable(GL_COLOR_MATERIAL);
            glEnable(GL_TEXTURE_2D);
        }
        else
        {
            glEnable(GL_COLOR_MATERIAL);
            glEnableClientState(GL_COLOR_ARRAY);
            glColorPointer(4, GL_FLOAT, 0, data.tri_v_colors.data());
        }
        glEnableClientState(GL_VERTEX_ARRAY);
        glVertexPointer(3, GL_FLOAT, 0, data.tri_coords.data());
        glEnableClientState(GL_NORMAL_ARRAY);
        glNormalPointer(GL_FLOAT, 0, data.tri_v_norms.data());
        glDrawElements(GL_TRIANGLES, (GLsizei)data.tris.size(), GL_UNSIGNED_INT, data.tris.data());
        glDisableClientState(GL_VERTEX_ARRAY);
        glDisableClientState(GL_NORMAL_ARRAY);
        if(data.draw_mode & DRAW_TRI_TEXTURE1D)
        {
            glDisableClientState(GL_TEXTURE_COORD_ARRAY);
            glDisable(GL_TEXTURE_1D);
            glEnable(GL_COLOR_MATERIAL);
        }
        else if(data.draw_mode & DRAW_TRI_TEXTURE2D)
        {
            glDisableClientState(GL_TEXTURE_COORD_ARRAY);
            glDisable(GL_TEXTURE_2D);
            glEnable(GL_COLOR_MATERIAL);
        }
        else
        {
            glDisableClientState(GL_COLOR_ARRAY);
            glDisable(GL_COLOR_MATERIAL);
        }
    }
}

感觉这个……也可以直接拿来用?


大体上把整个图形加载/显示的逻辑走了一遍,下面尝试把整个包的类图 Dump 出来

先装个 Doxygen 和 Graphviz,然后按照网上的教程生成类图

基本上就是这样了,后面就在这个基础上改吧

今天就到这里吧,接下来该抄大物实验报告了


Upd on 2024.11.07

昨天抄大物实验报告到凌晨两点,好困……

简单思考一下自己作业的目标吧

1
2
3
4
5
6
7
8
9
实现 Load/Save 模型 (obj & stl)
实现对 Workspace 的 Load/Save (加载/读取工作状态)
实现对 三角形/四边形/四面体/六面体 模型的渲染查看
查看器 缩放/旋转 
三维等高线  -- 算法1
四面体化  -- 算法2
重网格化  -- 算法3
环境光遮蔽  -- 算法4
LSCM (最小平方共形映射)  -- 算法5

简单列一下需要实现的组件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
Canvas : 基础画布类,封装各类 ImGUI 与 OpenGL 相关的内容

GuiComponent : 界面组件基类

Mesh : 网格体基类
    PolygonMesh: 多边形网格体
        TriangleMesh:三角形网格体
        QuadrilateralMesh: 四边形网格体
    PolyhedralMesh:多面体网格体
        TetrahedronMesh: 四面体网格体
        HexahedronMesh: 六面体网格体

一些接口:
    ISerializable : 实现序列化/反序列化
    IDrawable: 可在画布上进行绘制的对象

后面想到了再 upd 吧,现在先开始配环境!

Licensed under CC BY-NC-SA 4.0