Dandelion 1.1.2
A light-weight 3D builder for educational usage
载入中...
搜索中...
未找到
controller.h
浏览该文件的文档.
1#ifndef DANDELION_UI_CONTROLLER_H
2#define DANDELION_UI_CONTROLLER_H
3
4/*!
5 * \file ui/controller.h
6 */
7
8#include <memory>
9#include <variant>
10
11#include <spdlog/spdlog.h>
12
13#include "menubar.h"
14#include "toolbar.h"
15#include "selection_helper.h"
16#include "../platform/gl.hpp"
18#include "../scene/camera.h"
19#include "../scene/scene.h"
20
21/*!
22 * \ingroup ui
23 * \~chinese
24 * \brief 控制器管理所有的界面组件,并处理和预览视角操作(例如旋转、缩放或者平移)相关的输入。
25 *
26 * 控制器的生命周期就是整个程序 GUI 的生命周期,因此这是一个单例的类。其唯一一个实例可以通过
27 * `controller()` 这个静态方法访问,访问操作是线程安全的(但访问后的修改不是)。
28 * 控制器创建并持有所有与图形界面相关的资源:
29 *
30 * - 界面元素(菜单栏和工具栏)
31 * - 场景(包含所有物体、坐标轴等)
32 *
33 * 界面元素本身不存储场景,也不保存指向场景的引用等。控制器在每一帧将场景的引用传递给
34 * `Toolbar::render` 或 `Menubar::render` 函数,从而允许这些界面元素访问场景。
35 * 与 `Menubar` 或 `Toolbar` 保存引用相比,这种设计当前并没有额外有点,它只是参考 Scotty3D
36 * 思路的结果,将来有可能为引入全局撤销 (Undo) 和重做 (Redo) 操作提供方便。
37 */
39{
40public:
41
42 /*!
43 * \~chinese
44 * \brief 获取 `Controller` 类的唯一实例。
45 *
46 * 唯一的实例被定义成函数内的静态变量,在 C++ 11 及之后的标准中,这种定义方式是线程安全的。
47 */
48 static Controller& controller();
49 ///@{
50 /*! \~chinese 禁止复制构造。*/
51 Controller(Controller& other) = delete;
52 Controller& operator=(Controller& other) = delete;
53 ///@}
55 /*!
56 * \~chinese
57 * \brief 将鼠标拖动转换为旋转视角或平移视角操作。
58 *
59 * 当按下鼠标中键(或 Alt+鼠标左键)拖动时旋转视角,按下 Ctrl+鼠标左键拖动时平移视角。
60 * 同时按下 Alt 和 Ctrl 的效果与只按下 Alt 相同(只旋转不平移)。
61 *
62 * \param initial 为真表示开始拖动时调用,为假表示拖动过程中调用。
63 */
64 void on_mouse_dragged(bool initial);
65 /*!
66 * \~chinese
67 * \brief 根据鼠标点击选取场景中的物体。
68 *
69 * 这个函数将鼠标点击的屏幕坐标 (screen space) 转换为观察坐标 (view space),
70 * 然后将观察坐标转换回世界坐标 (world space),从而构造一条从观察相机(主相机)
71 * 发出的射线,按这条射线与场景中物体的相交结果拾取(选中)场景中的可选中对象。
72 * 工作于布局模式和物理模拟模式下时,它调用 `pick_object` 函数,工作于建模模式下时,
73 * 它调用 `pick_element` 函数。
74 */
75 void on_picking();
76 /*!
77 * \~chinese
78 * \brief 将拨动鼠标滚轮的操作转换为视角拉进或远离。
79 *
80 * 缩放视角的操作按指数规律执行:鼠标滚轮每滚动一个单位,观察相机到目标点的距离就乘上一个固定值
81 * `wheel_scroll_factor`,即
82 *
83 * \f[
84 * \mathbf{d}_\text{new}=\text{wheel_scroll_factor}^\text{wheel_delta}\cdot\mathbf{d}
85 * \f]
86 */
87 void on_wheel_scrolled();
88 /*!
89 * \~chinese
90 * \brief 在缩放窗口时调整轨迹球半径、预览视角(相机)Y 方向的 FoV,并更新自身记录的窗口尺寸。
91 *
92 * 虽然名字相同,但这个函数并非 GLFW 的窗口缩放回调函数。在缩放时它会被真正的回调函数
93 * `Platform::on_framebuffer_resized` 调用。
94 * \param width 缩放后的窗口宽度
95 * \param height 缩放后的窗口高度
96 */
97 void on_framebuffer_resized(float width, float height);
98 /*!
99 * \~chinese
100 * \brief 处理关于场景操作的输入。
101 *
102 * 这个函数判别鼠标操作是否对应于某种场景操作(调整视角、拾取物体等),并调用相应的处理函数
103 * (例如 `on_mouse_clicked` 或 `on_picking`)。与场景操作相对的是控件操作(例如点击按钮),
104 * 控件操作由相应的 ImGui 控件直接处理。
105 */
106 void process_input();
107 /*!
108 * \~chinese
109 * 渲染场景和各个 UI 组件。控制器本身不直接渲染任何内容,而是调用相应类的 `render()` 方法。
110 * \param shader 当前渲染使用的 shader,用于设置 shader 中的全局变量。
111 */
112 void render(const Shader& shader);
113 /*!
114 * \~english All variables used for layout are float because ImVec2 expects float.
115 * \~chinese 由于 ImGui 使用浮点数表示尺寸,所有和布局相关的变量都是浮点型。
116 */
117 ///@{
120 ///@}
121
122private:
123
124 /*!
125 * \~chinese
126 * \brief 选择一个元素。
127 *
128 * 这个函数修改控制器记录的选择状态。它包括两步动作:
129 * 1. 调用 `unselect` 清除原先的选择状态及其副作用
130 * 2. 修改被选中元素 `selected_element`,调用 `select_[type]` 函数执行具体操作
131 *
132 * \param element 要选择的新元素
133 */
134 void select(SelectableType element);
135 /*!
136 * \~chinese
137 * \brief 取消选择。
138 *
139 * 这个函数清楚当前的选择状态,将`selected_element` 重置为 `std::monostate`
140 * 并清空 `highlighted_element` 或 `highlighted_halfedge` 这些高亮元素。
141 * 如果之前的被选中元素设置过 `Scene` 等对象的属性,它也会一并将其重置。
142 */
143 void unselect();
144 /*!
145 * \~chinese
146 * \brief 渲染当前选中的元素。
147 *
148 * 渲染选中元素时会禁用深度检测 `GL_DEPTH_TEST` ,直接在原先的绘制结果上叠加。
149 * 因此,即使将视角旋转到背面也会看到高亮出来的被选中元素。
150 */
151 void render_selected_element(const Shader& shader);
152 /*!
153 * \~chinese
154 * \brief 渲染帮助调试的元素。
155 *
156 * 根据 `debug_options` 中的各项设置,渲染帮助调试的结构,如 BVH
157 * 的所有包围盒等。
158 */
159 void render_debug_helpers(const Shader& shader);
160 /*!
161 * \~chinese
162 * \brief 拾取物体。
163 *
164 * 在布局或物理模拟模式下,根据射线求交的结果选择物体。
165 * \param ray 根据点击位置生成的射线(世界坐标系下)
166 */
167 void pick_object(Ray& ray);
168 /*!
169 * \~chinese
170 * \brief 拾取半边、顶点、边或面片。
171 *
172 * 在建模模式下,根据射线求交的结果选择半边网格上的基本元素。
173 * \param ray 根据点击位置生成的射线(世界坐标系下)
174 */
175 void pick_element(Ray& ray);
176 /*! \~chinese 选择物体并更新 `Scene::selected_object` 。 */
177 void select_object(Object* object);
178 /*! \~chinese 选择半边并更新 `highlighted_halfedge` 。 */
179 void select_halfedge(const Halfedge* halfedge);
180 /*! \~chinese 选择顶点并更新 `highlighted_element` 。 */
181 void select_vertex(Vertex* vertex);
182 /*! \~chinese 选择边并更新 `highlighted_element` 。 */
183 void select_edge(Edge* edge);
184 /*! \~chinese 选择面片并更新 `highlighted_element` 。 */
185 void select_face(Face* face);
186 /*! \~chinese 选择光源并更新 `highlighted_element` 。 */
187 void select_light(Light* light);
188 /*!
189 * \~chinese
190 * \brief 将鼠标拖动转换为轨迹球 (Trackball) 操作,用于旋转预览视角。
191 *
192 * 轨迹球曲面的推导参考 [Object Mouse
193 * Trackball](https://www.khronos.org/opengl/wiki/Object_Mouse_Trackball)。
194 * \param initial 为真表示开始拖动时调用,为假表示拖动过程中调用。
195 */
196 void on_rotating(bool initial);
197 /*!
198 * \~chinese
199 * \brief 将鼠标拖动转换为视角平移。
200 *
201 * 将鼠标拖动时屏幕坐标的变化量换算为观察空间 (view space) 中的坐标变化,
202 * 光标向右移动对应相机向左移动,光标向上移动对应相机向下移动。
203 * 相应的变化量会累加到主相机的 `position` 和 `target` 属性上,因此平移过程中视线方向不会改变。
204 *
205 * 视点到观察点的距离增大(减小)时,相同屏幕坐标变化量对应的观察坐标变化量也增大(减小),
206 * 平移量正比于视角远近,比例系数为 `mouse_translation_factor` 。
207 * \param initial 为真表示开始拖动时调用,为假表示拖动过程中调用。
208 */
209 void on_translating(bool initial);
210 /*! \~chinese 缩放视角的控制系数,详见 `on_wheel_scrolled` 方法。 */
211 static constexpr float wheel_scroll_factor = 0.8f;
212 /*! \~chinese 平移视角时屏幕坐标到观察坐标换算系数的固定部分。 */
213 static constexpr float mouse_translation_factor = 0.001f;
214 /*! \~chinese 构造函数是私有的。 */
215 Controller();
216 /*!
217 * \~chinese
218 * \brief 全局工作模式。
219 *
220 * 控制器维护着当前的工作模式,决定各种功能可用或禁用。其他的 GUI 组件持有对该变量的引用。
221 */
223 /*! \~chinese 一些帮助调试的选项,详见 `UI::DebugOptions` 类型说明。 */
225 /*! \~chinese 菜单栏。 */
226 std::unique_ptr<UI::Menubar> menubar;
227 /*! \~chinese 工具栏。 */
228 std::unique_ptr<UI::Toolbar> toolbar;
229 /*! \~chinese 包含所有三维数据的场景实例。 */
230 std::unique_ptr<Scene> scene;
231 /*!
232 * \~chinese
233 * \brief 当前被选中的元素。
234 *
235 * 根据当前所处的模式,物体、各类几何基本元素、光源都可能被选中,详见 `SelectableType`
236 * 的类型说明。当 `selected_element` 持有 `std::monostate` 类型时,
237 * 当前的选择状态为空(没有任何元素被选中)。
238 */
240 /*! \~chinese 用于预览场景的观察相机(主相机)。 */
241 std::unique_ptr<Camera> main_camera;
242 /*! \~chinese 日志记录器。 */
243 std::shared_ptr<spdlog::logger> logger;
244 /*! \~chinese 当前的轨迹球半径,决定轨迹球控制曲面上球面和双曲面部分的相切位置。 */
246 /*! \~chinese 被选中元素类型为顶点、边、面片或光源时使用的绘制对象。 */
248 /*! \~chinese 被选中元素类型为半边时使用的绘制对象。 */
250 /*! \~chinese 显示拾取射线用的绘制对象,对应 `UI::DebugOptions::show_picking_ray` 。 */
252};
253
254#endif // DANDELION_UI_CONTROLLER_H
包含相机的类,用来表示场景中的相机。
void select_object(Object *object)
定义 controller.cpp:488
void select_light(Light *light)
定义 controller.cpp:539
void select_halfedge(const Halfedge *halfedge)
定义 controller.cpp:494
void on_rotating(bool initial)
将鼠标拖动转换为轨迹球 (Trackball) 操作,用于旋转预览视角。
定义 controller.cpp:552
void render_debug_helpers(const Shader &shader)
渲染帮助调试的元素。
定义 controller.cpp:380
std::unique_ptr< UI::Toolbar > toolbar
定义 controller.h:228
void on_framebuffer_resized(float width, float height)
在缩放窗口时调整轨迹球半径、预览视角(相机)Y 方向的 FoV,并更新自身记录的窗口尺寸。
定义 controller.cpp:151
void on_wheel_scrolled()
将拨动鼠标滚轮的操作转换为视角拉进或远离。
定义 controller.cpp:141
void select_vertex(Vertex *vertex)
定义 controller.cpp:502
std::unique_ptr< Camera > main_camera
定义 controller.h:241
void on_translating(bool initial)
将鼠标拖动转换为视角平移。
定义 controller.cpp:608
WorkingMode mode
全局工作模式。
定义 controller.h:222
void render_selected_element(const Shader &shader)
渲染当前选中的元素。
定义 controller.cpp:316
std::shared_ptr< spdlog::logger > logger
定义 controller.h:243
void select_face(Face *face)
定义 controller.cpp:522
void pick_element(Ray &ray)
拾取半边、顶点、边或面片。
定义 controller.cpp:423
static constexpr float mouse_translation_factor
定义 controller.h:213
Controller & operator=(Controller &other)=delete
static Controller & controller()
获取 Controller 类的唯一实例。
定义 controller.cpp:35
void unselect()
取消选择。
定义 controller.cpp:277
SelectableType selected_element
当前被选中的元素。
定义 controller.h:239
static constexpr float wheel_scroll_factor
定义 controller.h:211
void render(const Shader &shader)
定义 controller.cpp:242
Controller()
定义 controller.cpp:41
GL::LineSet picking_ray
定义 controller.h:251
UI::DebugOptions debug_options
定义 controller.h:224
float window_height
定义 controller.h:118
void on_mouse_dragged(bool initial)
将鼠标拖动转换为旋转视角或平移视角操作。
定义 controller.cpp:99
GL::Mesh highlighted_element
定义 controller.h:247
void on_picking()
根据鼠标点击选取场景中的物体。
定义 controller.cpp:113
std::unique_ptr< Scene > scene
定义 controller.h:230
float window_width
定义 controller.h:118
float toolbar_width
定义 controller.h:119
Controller(Controller &other)=delete
GL::LineSet highlighted_halfedge
定义 controller.h:249
void select(SelectableType element)
选择一个元素。
定义 controller.cpp:260
float trackball_radius
定义 controller.h:245
void pick_object(Ray &ray)
拾取物体。
定义 controller.cpp:396
void select_edge(Edge *edge)
定义 controller.cpp:510
std::unique_ptr< UI::Menubar > menubar
定义 controller.h:226
void process_input()
处理关于场景操作的输入。
定义 controller.cpp:159
表示物体的类。
定义 object.h:42
对 GLSL Shader 的简单封装。
定义 shader.hpp:24
std::variant< std::monostate, Object *, const Halfedge *, Vertex *, Edge *, Face *, Light * > SelectableType
场景中可被选择的元素类型。
定义 selection_helper.h:38
WorkingMode
定义 rendering.hpp:46
包含场景的类。
半边网格中的边。
定义 halfedge.h:118
半边网格中的面片。
定义 halfedge.h:151
在预览场景时绘制若干线条。
定义 gl.hpp:334
用于场景预览渲染的 Mesh 类。
定义 gl.hpp:268
半边网格中最关键的几何元素。
定义 halfedge.h:46
表示一个点光源的类。
定义 light.h:19
定义 ray.h:24
辅助调试的 GUI 选项。
定义 menubar.h:21
半边网格中的顶点。
定义 halfedge.h:86