|
@@ -17,13 +17,15 @@ class Simple3DWidget(QOpenGLWidget):
|
|
|
self.colors = np.array([]) # 颜色数据
|
|
|
self.triangles = np.array([], dtype=np.int32) # 三角面数据 ← 新增
|
|
|
self.normals = np.array([]) # 法线数据 ← 新增
|
|
|
+ self.edges = np.array([]) #轮廓边
|
|
|
+ self.face_normals = np.array([])
|
|
|
|
|
|
self.rotation = [0.0, 0.0] # 旋转角度 [俯仰, 偏航]
|
|
|
self.zoom = -5.0 # 视距(负值表示拉远)
|
|
|
self.pan = [0.0, 0.0] # 平移偏移 [x, y]
|
|
|
self.last_mouse_pos = None # 鼠标位置
|
|
|
self.setMouseTracking(True) # 启用鼠标跟踪
|
|
|
- self.display_mode = 'points' # 默认显示模式: 点云
|
|
|
+ self.display_mode = 'surface' # 默认显示模式: 点云
|
|
|
self.axes_display_mode = False
|
|
|
self.axes_world_display_mode = True
|
|
|
|
|
@@ -42,6 +44,10 @@ class Simple3DWidget(QOpenGLWidget):
|
|
|
self.picking = False # 是否处于拾取模式
|
|
|
self.picking_color_map = {} # 顶点索引 → 唯一颜色(用于反查)
|
|
|
|
|
|
+ #选色模式
|
|
|
+ self.set_color = False
|
|
|
+ self.highlighted_face_indices = [] # 存储所有要高亮的面索引
|
|
|
+
|
|
|
self.setFont(QFont("SimHei", 10)) # 让 renderText 使用黑体
|
|
|
|
|
|
|
|
@@ -76,45 +82,37 @@ class Simple3DWidget(QOpenGLWidget):
|
|
|
glMatrixMode(GL_MODELVIEW)
|
|
|
glLoadIdentity()
|
|
|
|
|
|
- # ✅ 使用 gluLookAt 构建轨道摄像机
|
|
|
- # 视角参数
|
|
|
- yaw = self.view_rotation[1] # 偏航角(左右)
|
|
|
- pitch = self.view_rotation[0] # 俯仰角(上下)
|
|
|
+ # --- 设置摄像机 ---
|
|
|
+ yaw = self.view_rotation[1]
|
|
|
+ pitch = self.view_rotation[0]
|
|
|
distance = self.view_distance
|
|
|
|
|
|
- # 1. 计算摄像机位置(绕原点轨道)
|
|
|
- radius = distance
|
|
|
- cam_x = radius * np.cos(np.radians(yaw)) * np.cos(np.radians(pitch))
|
|
|
- cam_y = radius * np.sin(np.radians(pitch))
|
|
|
- cam_z = radius * np.sin(np.radians(yaw)) * np.cos(np.radians(pitch))
|
|
|
-
|
|
|
- # 2. 目标点 = 世界原点 (0,0,0)
|
|
|
- target_x, target_y, target_z = 0.0, 0.0, 0.0
|
|
|
-
|
|
|
- # 3. 上方向(防止翻滚)
|
|
|
- up_x, up_y, up_z = 0.0, 1.0, 0.0
|
|
|
+ cam_x = distance * np.cos(np.radians(yaw)) * np.cos(np.radians(pitch))
|
|
|
+ cam_y = distance * np.sin(np.radians(pitch))
|
|
|
+ cam_z = distance * np.sin(np.radians(yaw)) * np.cos(np.radians(pitch))
|
|
|
|
|
|
- # ✅ 设置摄像机(绕原点轨道旋转)
|
|
|
gluLookAt(
|
|
|
- cam_x, cam_y, cam_z, # 摄像机位置
|
|
|
- target_x, target_y, target_z, # 看向目标(世界原点)
|
|
|
- up_x, up_y, up_z # 上方向
|
|
|
+ cam_x, cam_y, cam_z,
|
|
|
+ 0.0, 0.0, 0.0,
|
|
|
+ 0.0, 1.0, 0.0
|
|
|
)
|
|
|
|
|
|
- # ✅ 应用视点平移(Ctrl+右键平移)
|
|
|
glTranslatef(self.view_pan[0], self.view_pan[1], 0)
|
|
|
|
|
|
- # ✅ 绘制世界坐标系(在原点)
|
|
|
+ # 绘制世界坐标轴
|
|
|
if self.axes_world_display_mode:
|
|
|
self.drawWorldAxes()
|
|
|
|
|
|
- # ✅ 模型变换
|
|
|
+ # --- 模型变换 ---
|
|
|
glPushMatrix()
|
|
|
glTranslatef(self.model_pan[0], self.model_pan[1], 0)
|
|
|
glRotatef(self.model_rotation[0], 1, 0, 0)
|
|
|
glRotatef(self.model_rotation[1], 0, 1, 0)
|
|
|
glScalef(self.model_scale, self.model_scale, self.model_scale)
|
|
|
|
|
|
+ # 所有轮廓线已在 load_data_from_file 中提取为 self.edges
|
|
|
+
|
|
|
+ # 绘制模型坐标轴
|
|
|
if self.axes_display_mode:
|
|
|
self.drawModelAxes()
|
|
|
|
|
@@ -123,67 +121,100 @@ class Simple3DWidget(QOpenGLWidget):
|
|
|
glPopMatrix()
|
|
|
|
|
|
def _draw_model(self):
|
|
|
- """私有方法:绘制模型(含高亮)"""
|
|
|
+ """私有方法:绘制模型(含高亮 + 静态轮廓线)"""
|
|
|
if len(self.vertices) == 0:
|
|
|
return
|
|
|
|
|
|
if self.display_mode == 'points':
|
|
|
- glPointSize(8.0)
|
|
|
+ # ✅ 点云模式:只显示顶点
|
|
|
+ glDisable(GL_LIGHTING)
|
|
|
+ glPointSize(5.0)
|
|
|
+
|
|
|
glBegin(GL_POINTS)
|
|
|
- try:
|
|
|
- for i, v in enumerate(self.vertices):
|
|
|
- # 判断是否是选中点
|
|
|
- if self.selected_point is not None and np.allclose(v, self.selected_point, atol=1e-6):
|
|
|
- glColor3f(1.0, 1.0, 0.0) # 黄色高亮
|
|
|
+ for i, v in enumerate(self.vertices):
|
|
|
+ if self.selected_point is not None and np.allclose(v, self.selected_point, atol=1e-6):
|
|
|
+ glColor3f(1.0, 1.0, 0.0) # 黄色高亮
|
|
|
+ else:
|
|
|
+ if i < len(self.colors):
|
|
|
+ color = self.colors[i]
|
|
|
+ glColor3f(color[0], color[1], color[2])
|
|
|
else:
|
|
|
- glColor3f(*self.colors[i]) # 原始颜色
|
|
|
- glVertex3f(v[0], v[1], v[2])
|
|
|
- finally:
|
|
|
- glEnd() # ✅ 保证执行
|
|
|
+ glColor3f(0.8, 0.8, 0.8)
|
|
|
+ glVertex3f(v[0], v[1], v[2])
|
|
|
+ glEnd()
|
|
|
|
|
|
- # ✅ 在 glEnd() 之后再绘制文字!
|
|
|
+ # 额外高亮选中点
|
|
|
if self.selected_point is not None:
|
|
|
- x, y, z = self.selected_point
|
|
|
- offset = 0.05
|
|
|
- # 设置文字颜色
|
|
|
- glColor3f(1.0, 1.0, 0.0) # 黄色
|
|
|
- self.renderText(x + offset, y + offset, z + offset, "Selected")
|
|
|
-
|
|
|
+ glPointSize(12.0)
|
|
|
+ glBegin(GL_POINTS)
|
|
|
+ glColor3f(1.0, 1.0, 0.0)
|
|
|
+ glVertex3f(self.selected_point[0], self.selected_point[1], self.selected_point[2])
|
|
|
+ glEnd()
|
|
|
|
|
|
elif self.display_mode == 'surface' and len(self.triangles) > 0:
|
|
|
# ✅ 开启光照
|
|
|
glEnable(GL_LIGHTING)
|
|
|
try:
|
|
|
- # ✅ 预计算选中点的索引(避免在 glBegin 内部调用 np.allclose)
|
|
|
highlight_indices = set()
|
|
|
if self.selected_point is not None:
|
|
|
for idx, v in enumerate(self.vertices):
|
|
|
if np.allclose(v, self.selected_point, atol=1e-6):
|
|
|
highlight_indices.add(idx)
|
|
|
|
|
|
+ # === 绘制表面 ===
|
|
|
glBegin(GL_TRIANGLES)
|
|
|
try:
|
|
|
- for tri in self.triangles:
|
|
|
+ # 将 triangles 转为 list of tuples 便于索引查找(只做一次)
|
|
|
+ # 如果 self.triangles 是 numpy 数组,转换为列表
|
|
|
+ triangles_list = [tuple(tri) for tri in self.triangles] if isinstance(self.triangles,
|
|
|
+ np.ndarray) else self.triangles
|
|
|
+
|
|
|
+ for tri_idx, tri in enumerate(self.triangles):
|
|
|
+ # 判断当前三角形是否在高亮区域
|
|
|
+ is_face_highlighted = (
|
|
|
+ hasattr(self, 'highlighted_face_indices') and
|
|
|
+ isinstance(self.highlighted_face_indices, (list, set)) and
|
|
|
+ tri_idx in self.highlighted_face_indices
|
|
|
+ )
|
|
|
+
|
|
|
for idx in tri:
|
|
|
- # 高亮三角形中包含选中点的顶点
|
|
|
- if idx in highlight_indices:
|
|
|
- glColor3f(1.0, 0.0, 0.0)
|
|
|
+ # 优先级:共面高亮 > 选中点高亮 > 正常颜色
|
|
|
+ if is_face_highlighted:
|
|
|
+ glColor3f(1.0, 1.0, 1.0) # 白色高亮(共面区域)
|
|
|
+ elif idx in highlight_indices:
|
|
|
+ glColor3f(1.0, 0.0, 0.0) # 红色高亮(选中点)
|
|
|
else:
|
|
|
glColor3f(*self.colors[idx])
|
|
|
|
|
|
- # 设置法线(如果存在且有效)
|
|
|
if len(self.normals) > idx:
|
|
|
n = self.normals[idx]
|
|
|
if not np.any(np.isnan(n)) and not np.any(np.isinf(n)):
|
|
|
glNormal3f(*n)
|
|
|
|
|
|
glVertex3f(*self.vertices[idx])
|
|
|
+ except Exception as e:
|
|
|
+ print(f"Error in drawing triangles: {e}")
|
|
|
finally:
|
|
|
- glEnd() # ✅ 保证结束绘制
|
|
|
+ glEnd()
|
|
|
+ # === 绘制轮廓线(静态,来自 self.edges)===
|
|
|
+ glDisable(GL_LIGHTING)
|
|
|
+ if len(self.edges) > 0:
|
|
|
+ glLineWidth(2.5)
|
|
|
+ glColor3f(0.0, 0.0, 0.0) # 黑色轮廓线
|
|
|
+ glBegin(GL_LINES)
|
|
|
+ try:
|
|
|
+ for edge in self.edges:
|
|
|
+ v0 = self.vertices[edge[0]]
|
|
|
+ v1 = self.vertices[edge[1]]
|
|
|
+ glVertex3f(v0[0], v0[1], v0[2])
|
|
|
+ glVertex3f(v1[0], v1[1], v1[2])
|
|
|
+ finally:
|
|
|
+ glEnd()
|
|
|
+
|
|
|
+ glEnable(GL_LIGHTING)
|
|
|
|
|
|
finally:
|
|
|
- glDisable(GL_LIGHTING) # ✅ 保证关闭光照
|
|
|
-
|
|
|
+ glDisable(GL_LIGHTING)
|
|
|
def renderText(self, x, y, z, text):
|
|
|
"""
|
|
|
在指定的三维坐标位置渲染文本。
|
|
@@ -270,9 +301,8 @@ class Simple3DWidget(QOpenGLWidget):
|
|
|
|
|
|
glPopMatrix()
|
|
|
|
|
|
-
|
|
|
- def set_data(self, vertices, colors, triangles=None, normals=None):
|
|
|
- """设置 3D 数据(支持 mesh)"""
|
|
|
+ def set_data(self, vertices, colors, triangles=None, normals=None, silhouette_edges=None):
|
|
|
+ """设置 3D 数据(支持 mesh 和轮廓线)"""
|
|
|
self.vertices = np.array(vertices, dtype=np.float32)
|
|
|
self.colors = np.array(colors, dtype=np.float32)
|
|
|
|
|
@@ -286,22 +316,211 @@ class Simple3DWidget(QOpenGLWidget):
|
|
|
else:
|
|
|
self.normals = np.array([])
|
|
|
|
|
|
+ if silhouette_edges is not None:
|
|
|
+ self.edges = np.array(silhouette_edges, dtype=np.int32) # ← 存为 self.edges
|
|
|
+ else:
|
|
|
+ self.edges = np.array([])
|
|
|
+
|
|
|
+ # ✅ 修正:打印 self.edges,不是 self.silhouette_edges
|
|
|
print(f"✅ 设置数据: {len(self.vertices)} 个顶点, {len(self.triangles)} 个三角面")
|
|
|
+ print(f" 轮廓线数量: {len(self.edges)} 条") # ✅ 正确字段名
|
|
|
+
|
|
|
self.update()
|
|
|
|
|
|
def mousePressEvent(self, event):
|
|
|
self.last_mouse_pos = event.pos() #二维坐标,仅用来计算旋转/移动的量
|
|
|
if event.button() == Qt.LeftButton:
|
|
|
+ if self.set_color == True:
|
|
|
+ self._do_color_pick(event.pos())
|
|
|
if self.picking == True:
|
|
|
self._do_picking(event.pos())
|
|
|
super().mousePressEvent(event) #调用父类中默认的方法,获得预设的一些功能
|
|
|
|
|
|
+ def _do_color_pick(self, pos):
|
|
|
+ """
|
|
|
+ 点击屏幕某点,找到对应的三角形,
|
|
|
+ 然后通过“法线连通性”扩展出整个平面区域,
|
|
|
+ 将该区域所有三角形的顶点颜色设为白色(或加深)
|
|
|
+ """
|
|
|
+ # 1. 射线拾取:获取被点击的三角形索引
|
|
|
+ picked_tri_idx = self._ray_cast_triangle_index(pos)
|
|
|
+ if picked_tri_idx is None:
|
|
|
+ return # 没点中任何面
|
|
|
+
|
|
|
+ print(f"选中三角形: {picked_tri_idx}")
|
|
|
+
|
|
|
+ # 2. 获取该三角形的法线(作为基准)
|
|
|
+ base_normal = self.face_normals[picked_tri_idx]
|
|
|
+ tolerance = 0.05 # 法线夹角余弦容忍度(越小越严格)
|
|
|
+
|
|
|
+ # 3. 使用广度优先搜索(BFS)扩展所有“共面”三角形
|
|
|
+ from collections import deque
|
|
|
+ queue = deque([picked_tri_idx])
|
|
|
+ visited = set()
|
|
|
+ visited.add(picked_tri_idx)
|
|
|
+ region_triangles = [] # 存储属于这个“面区域”的所有三角面索引
|
|
|
+
|
|
|
+ while queue:
|
|
|
+ tri_idx = queue.popleft()
|
|
|
+ region_triangles.append(tri_idx)
|
|
|
+
|
|
|
+ # 获取当前三角形的三个顶点
|
|
|
+ v0, v1, v2 = self.triangles[tri_idx]
|
|
|
+ all_vertices = [v0, v1, v2]
|
|
|
+
|
|
|
+ # 遍历所有邻接三角形(共享边的三角形)
|
|
|
+ for i in range(3):
|
|
|
+ v_a = all_vertices[i]
|
|
|
+ v_b = all_vertices[(i + 1) % 3]
|
|
|
+ edge = tuple(sorted([v_a, v_b])) # 边(无序)
|
|
|
+
|
|
|
+ # 查找共享这条边的其他三角形(需预建边到面的映射,或遍历)
|
|
|
+ # 这里简化:遍历所有三角形找邻接
|
|
|
+ for neighbor_idx in range(len(self.triangles)):
|
|
|
+ if neighbor_idx in visited:
|
|
|
+ continue
|
|
|
+
|
|
|
+ tri = self.triangles[neighbor_idx]
|
|
|
+ tri_edges = [
|
|
|
+ tuple(sorted([tri[0], tri[1]])),
|
|
|
+ tuple(sorted([tri[1], tri[2]])),
|
|
|
+ tuple(sorted([tri[2], tri[0]])),
|
|
|
+ ]
|
|
|
+
|
|
|
+ if edge in tri_edges:
|
|
|
+ # 是邻接三角形,检查法线是否接近
|
|
|
+ neighbor_normal = self.face_normals[neighbor_idx]
|
|
|
+ dot = np.dot(base_normal, neighbor_normal)
|
|
|
+ if abs(dot) > (1 - tolerance): # 法线夹角小
|
|
|
+ visited.add(neighbor_idx)
|
|
|
+ queue.append(neighbor_idx)
|
|
|
+
|
|
|
+ # ===================================
|
|
|
+ # ✅ 核心修改:不再修改顶点颜色
|
|
|
+ # 而是将共面区域的三角形索引保存到 self.highlighted_face_indices
|
|
|
+ # 由 paintGL 在渲染时决定是否高亮
|
|
|
+ # ===================================
|
|
|
+ self.highlighted_face_indices = region_triangles
|
|
|
+
|
|
|
+ # 可选:打印调试信息
|
|
|
+ print(f"✅ 共面区域包含 {len(self.highlighted_face_indices)} 个三角形")
|
|
|
+
|
|
|
+ # 触发重绘
|
|
|
+ self.update() # 调用 paintGL 重新渲染
|
|
|
+
|
|
|
+ def _is_adjacent(self, tri_idx1, tri_idx2):
|
|
|
+ """判断两个三角形是否共享一条边(即邻接)"""
|
|
|
+ v1 = set(self.triangles[tri_idx1])
|
|
|
+ v2 = set(self.triangles[tri_idx2])
|
|
|
+ return len(v1 & v2) == 2 # 共享两个顶点 → 共享一条边
|
|
|
+
|
|
|
+ def _ray_cast_triangle_index(self, pos):
|
|
|
+ """
|
|
|
+ 从屏幕点击位置发射一条射线,检测与哪个三角形相交(最近的)
|
|
|
+ 返回相交的三角形索引,无则返回 None
|
|
|
+ """
|
|
|
+ width, height = self.width(), self.height()
|
|
|
+ x, y = pos.x(), pos.y()
|
|
|
+
|
|
|
+ # 1. 获取当前模型视图和投影矩阵
|
|
|
+ modelview = np.array(self.get_current_matrix()[0], dtype=np.float64)
|
|
|
+ projection = np.array(self.get_current_matrix()[1], dtype=np.float64)
|
|
|
+ viewport = (0, 0, width, height)
|
|
|
+
|
|
|
+ # 2. 将屏幕坐标转换为世界空间中的两个点(近平面和远平面)
|
|
|
+ try:
|
|
|
+ # 注意:OpenGL 的 Y 轴向下,所以要翻转 y
|
|
|
+ world_near = gluUnProject(x, height - y, 0.0, modelview, projection, viewport)
|
|
|
+ world_far = gluUnProject(x, height - y, 1.0, modelview, projection, viewport)
|
|
|
+ except Exception as e:
|
|
|
+ print(f"gluUnProject failed: {e}")
|
|
|
+ return None
|
|
|
+
|
|
|
+ # 3. 构造射线方向
|
|
|
+ ray_origin = np.array(world_near)
|
|
|
+ ray_direction = np.array(world_far) - np.array(world_near)
|
|
|
+ ray_direction = ray_direction / (np.linalg.norm(ray_direction) + 1e-8)
|
|
|
+
|
|
|
+ # 4. 遍历所有三角形,做射线相交检测
|
|
|
+ best_t = float('inf')
|
|
|
+ picked_idx = None
|
|
|
+
|
|
|
+ for idx, triangle in enumerate(self.triangles):
|
|
|
+ v0_idx, v1_idx, v2_idx = triangle
|
|
|
+ v0 = np.array(self.vertices[v0_idx])
|
|
|
+ v1 = np.array(self.vertices[v1_idx])
|
|
|
+ v2 = np.array(self.vertices[v2_idx])
|
|
|
+
|
|
|
+ result = self._ray_triangle_intersect(
|
|
|
+ ray_origin, ray_direction,
|
|
|
+ v0, v1, v2
|
|
|
+ )
|
|
|
+ if result is not None:
|
|
|
+ t, u, v = result
|
|
|
+ if t < best_t and t > 0:
|
|
|
+ best_t = t
|
|
|
+ picked_idx = idx
|
|
|
+
|
|
|
+ return picked_idx
|
|
|
+
|
|
|
+ def _ray_triangle_intersect(self, ray_origin, ray_direction, v0, v1, v2, eps=1e-6):
|
|
|
+ """
|
|
|
+ Möller–Trumbore 射线-三角形相交算法
|
|
|
+ 返回: (t, u, v) 或 None(无交点)
|
|
|
+ t: 射线上交点距离
|
|
|
+ u,v: 重心坐标
|
|
|
+ """
|
|
|
+ # 三角形边向量
|
|
|
+ edge1 = v1 - v0
|
|
|
+ edge2 = v2 - v0
|
|
|
+
|
|
|
+ # P = ray_direction × edge2
|
|
|
+ P = np.cross(ray_direction, edge2)
|
|
|
+ det = np.dot(edge1, P)
|
|
|
+
|
|
|
+ if abs(det) < eps:
|
|
|
+ return None # 射线与三角形平行
|
|
|
+
|
|
|
+ inv_det = 1.0 / det
|
|
|
+ T = ray_origin - v0
|
|
|
+ u = np.dot(T, P) * inv_det
|
|
|
+ if u < 0.0 or u > 1.0:
|
|
|
+ return None
|
|
|
+
|
|
|
+ Q = np.cross(T, edge1)
|
|
|
+ v = np.dot(ray_direction, Q) * inv_det
|
|
|
+ if v < 0.0 or u + v > 1.0:
|
|
|
+ return None
|
|
|
+
|
|
|
+ t = np.dot(edge2, Q) * inv_det
|
|
|
+ if t > eps:
|
|
|
+ return t, u, v # 相交
|
|
|
+
|
|
|
+ return None
|
|
|
+
|
|
|
+ from OpenGL.GL import glGetDoublev, GL_MODELVIEW_MATRIX, GL_PROJECTION
|
|
|
+
|
|
|
+ def get_current_matrix(self):
|
|
|
+ """
|
|
|
+ 安全获取当前 Modelview 和 Projection 矩阵
|
|
|
+ 必须在 OpenGL 上下文中调用(比如在 paintGL 之后)
|
|
|
+ """
|
|
|
+ try:
|
|
|
+ modelview = glGetDoublev(GL_MODELVIEW_MATRIX)
|
|
|
+ projection = glGetDoublev(GL_PROJECTION_MATRIX) # 注意:是 GL_PROJECTION_MATRIX,不是 GL_PROJECTION
|
|
|
+ return modelview, projection
|
|
|
+ except Exception as e:
|
|
|
+ print(f"获取矩阵失败: {e}")
|
|
|
+ return None, None
|
|
|
+
|
|
|
def _do_picking(self, pos):
|
|
|
x, y = pos.x(), pos.y()
|
|
|
self.makeCurrent()
|
|
|
- self._render_for_picking()
|
|
|
+ self._render_for_picking() #再渲染一个当前的模型到gpu
|
|
|
|
|
|
+ #提取pos坐标像素点在窗口中的实际颜色piexel
|
|
|
pixel = glReadPixels(x, self.height() - y - 1, 1, 1, GL_RGB, GL_FLOAT)
|
|
|
+ #将rgb分量的值提取出来
|
|
|
r, g, b = pixel[0][0]
|
|
|
print(f"Read color: ({r:.5f}, {g:.5f}, {b:.5f})")
|
|
|
|
|
@@ -396,20 +615,32 @@ class Simple3DWidget(QOpenGLWidget):
|
|
|
if len(self.vertices) == 0:
|
|
|
return
|
|
|
|
|
|
+ #设置断点::参数入栈函数,GL_ALL_ATTRIB_BITS:当前OPENGL的所有参数
|
|
|
glPushAttrib(GL_ALL_ATTRIB_BITS)
|
|
|
+ #关闭光照计算,避免编码颜色改变
|
|
|
glDisable(GL_LIGHTING)
|
|
|
+ #关闭纹理映射
|
|
|
glDisable(GL_TEXTURE_2D)
|
|
|
+ #设置平面着色:一个三角面中的颜色保持一致
|
|
|
glShadeModel(GL_FLAT)
|
|
|
+ #设置清屏颜色为黑色
|
|
|
glClearColor(0, 0, 0, 0)
|
|
|
+ #清理颜色缓存和深度缓存
|
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
|
|
|
+ #开启深度测试
|
|
|
glEnable(GL_DEPTH_TEST)
|
|
|
|
|
|
# --- 复制 paintGL 的投影和视图变换 ---
|
|
|
+ #设置矩阵模式:投影矩阵
|
|
|
glMatrixMode(GL_PROJECTION)
|
|
|
+ #重置投影矩阵为单位矩阵
|
|
|
glLoadIdentity()
|
|
|
+ #设置透视投影
|
|
|
gluPerspective(45, self.width() / max(self.height(), 1), 0.1, 100.0)
|
|
|
|
|
|
+ #切换矩阵模式:模型视图矩阵
|
|
|
glMatrixMode(GL_MODELVIEW)
|
|
|
+ #重置当前矩阵为单位矩阵
|
|
|
glLoadIdentity()
|
|
|
|
|
|
yaw = self.view_rotation[1]
|
|
@@ -447,12 +678,23 @@ class Simple3DWidget(QOpenGLWidget):
|
|
|
"""切换选点模式"""
|
|
|
if self.picking == False:
|
|
|
self.picking = True
|
|
|
+ self.set_color = False
|
|
|
print(f"已进入选点模式:{self.picking}")
|
|
|
else:
|
|
|
self.picking = False
|
|
|
print(f"已退出选点模式:{self.picking}")
|
|
|
# self.update() # 切换模式后重新绘制
|
|
|
|
|
|
+ def color_mode(self):
|
|
|
+ """切换选色模式"""
|
|
|
+ if self.set_color == False:
|
|
|
+ self.set_color = True
|
|
|
+ self.picking = False #打开选色则关闭选点
|
|
|
+ print(f"已进入选色模式:{self.set_color}")
|
|
|
+ else:
|
|
|
+ self.set_color = False
|
|
|
+ print(f"已退出选色模式:{self.set_color}")
|
|
|
+
|
|
|
#统一颜色显示方式
|
|
|
def _get_picking_color(self, index):
|
|
|
idx = index + 1
|