GLFW Keyboard keys

本文全面概述了信息技术领域的核心内容,包括前端开发、后端开发、移动开发、游戏开发等细分技术领域的重要知识点,旨在为读者提供一个清晰的技术路径指南。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Macros

#define GLFW_KEY_UNKNOWN   -1
 
#define GLFW_KEY_SPACE   32
 
#define GLFW_KEY_APOSTROPHE   39 /* ' */
 
#define GLFW_KEY_COMMA   44 /* , */
 
#define GLFW_KEY_MINUS   45 /* - */
 
#define GLFW_KEY_PERIOD   46 /* . */
 
#define GLFW_KEY_SLASH   47 /* / */
 
#define GLFW_KEY_0   48
 
#define GLFW_KEY_1   49
 
#define GLFW_KEY_2   50
 
#define GLFW_KEY_3   51
 
#define GLFW_KEY_4   52
 
#define GLFW_KEY_5   53
 
#define GLFW_KEY_6   54
 
#define GLFW_KEY_7   55
 
#define GLFW_KEY_8   56
 
#define GLFW_KEY_9   57
 
#define GLFW_KEY_SEMICOLON   59 /* ; */
 
#define GLFW_KEY_EQUAL   61 /* = */
 
#define GLFW_KEY_A   65
 
#define GLFW_KEY_B   66
 
#define GLFW_KEY_C   67
 
#define GLFW_KEY_D   68
 
#define GLFW_KEY_E   69
 
#define GLFW_KEY_F   70
 
#define GLFW_KEY_G   71
 
#define GLFW_KEY_H   72
 
#define GLFW_KEY_I   73
 
#define GLFW_KEY_J   74
 
#define GLFW_KEY_K   75
 
#define GLFW_KEY_L   76
 
#define GLFW_KEY_M   77
 
#define GLFW_KEY_N   78
 
#define GLFW_KEY_O   79
 
#define GLFW_KEY_P   80
 
#define GLFW_KEY_Q   81
 
#define GLFW_KEY_R   82
 
#define GLFW_KEY_S   83
 
#define GLFW_KEY_T   84
 
#define GLFW_KEY_U   85
 
#define GLFW_KEY_V   86
 
#define GLFW_KEY_W   87
 
#define GLFW_KEY_X   88
 
#define GLFW_KEY_Y   89
 
#define GLFW_KEY_Z   90
 
#define GLFW_KEY_LEFT_BRACKET   91 /* [ */
 
#define GLFW_KEY_BACKSLASH   92 /* \ */
 
#define GLFW_KEY_RIGHT_BRACKET   93 /* ] */
 
#define GLFW_KEY_GRAVE_ACCENT   96 /* ` */
 
#define GLFW_KEY_WORLD_1   161 /* non-US #1 */
 
#define GLFW_KEY_WORLD_2   162 /* non-US #2 */
 
#define GLFW_KEY_ESCAPE   256
 
#define GLFW_KEY_ENTER   257
 
#define GLFW_KEY_TAB   258
 
#define GLFW_KEY_BACKSPACE   259
 
#define GLFW_KEY_INSERT   260
 
#define GLFW_KEY_DELETE   261
 
#define GLFW_KEY_RIGHT   262
 
#define GLFW_KEY_LEFT   263
 
#define GLFW_KEY_DOWN   264
 
#define GLFW_KEY_UP   265
 
#define GLFW_KEY_PAGE_UP   266
 
#define GLFW_KEY_PAGE_DOWN   267
 
#define GLFW_KEY_HOME   268
 
#define GLFW_KEY_END   269
 
#define GLFW_KEY_CAPS_LOCK   280
 
#define GLFW_KEY_SCROLL_LOCK   281
 
#define GLFW_KEY_NUM_LOCK   282
 
#define GLFW_KEY_PRINT_SCREEN   283
 
#define GLFW_KEY_PAUSE   284
 
#define GLFW_KEY_F1   290
 
#define GLFW_KEY_F2   291
 
#define GLFW_KEY_F3   292
 
#define GLFW_KEY_F4   293
 
#define GLFW_KEY_F5   294
 
#define GLFW_KEY_F6   295
 
#define GLFW_KEY_F7   296
 
#define GLFW_KEY_F8   297
 
#define GLFW_KEY_F9   298
 
#define GLFW_KEY_F10   299
 
#define GLFW_KEY_F11   300
 
#define GLFW_KEY_F12   301
 
#define GLFW_KEY_F13   302
 
#define GLFW_KEY_F14   303
 
#define GLFW_KEY_F15   304
 
#define GLFW_KEY_F16   305
 
#define GLFW_KEY_F17   306
 
#define GLFW_KEY_F18   307
 
#define GLFW_KEY_F19   308
 
#define GLFW_KEY_F20   309
 
#define GLFW_KEY_F21   310
 
#define GLFW_KEY_F22   311
 
#define GLFW_KEY_F23   312
 
#define GLFW_KEY_F24   313
 
#define GLFW_KEY_F25   314
 
#define GLFW_KEY_KP_0   320
 
#define GLFW_KEY_KP_1   321
 
#define GLFW_KEY_KP_2   322
 
#define GLFW_KEY_KP_3   323
 
#define GLFW_KEY_KP_4   324
 
#define GLFW_KEY_KP_5   325
 
#define GLFW_KEY_KP_6   326
 
#define GLFW_KEY_KP_7   327
 
#define GLFW_KEY_KP_8   328
 
#define GLFW_KEY_KP_9   329
 
#define GLFW_KEY_KP_DECIMAL   330
 
#define GLFW_KEY_KP_DIVIDE   331
 
#define GLFW_KEY_KP_MULTIPLY   332
 
#define GLFW_KEY_KP_SUBTRACT   333
 
#define GLFW_KEY_KP_ADD   334
 
#define GLFW_KEY_KP_ENTER   335
 
#define GLFW_KEY_KP_EQUAL   336
 
#define GLFW_KEY_LEFT_SHIFT   340
 
#define GLFW_KEY_LEFT_CONTROL   341
 
#define GLFW_KEY_LEFT_ALT   342
 
#define GLFW_KEY_LEFT_SUPER   343
 
#define GLFW_KEY_RIGHT_SHIFT   344
 
#define GLFW_KEY_RIGHT_CONTROL   345
 
#define GLFW_KEY_RIGHT_ALT   346
 
#define GLFW_KEY_RIGHT_SUPER   347
 
#define GLFW_KEY_MENU   348
 
#define GLFW_KEY_LAST   GLFW_KEY_MENU
 

Detailed Description

These key codes are inspired by the USB HID Usage Tables v1.12 (p. 53-60), but re-arranged to map to 7-bit ASCII for printable keys (function keys are put in the 256+ range).

The naming of the key codes follow these rules:

  • The US keyboard layout is used
  • Names of printable alpha-numeric characters are used (e.g. "A", "R", "3", etc.)
  • For non-alphanumeric characters, Unicode:ish names are used (e.g. "COMMA", "LEFT_SQUARE_BRACKET", etc.). Note that some names do not correspond to the Unicode standard (usually for brevity)
  • Keys that lack a clear US mapping are named "WORLD_x"
  • For non-printable keys, custom names are used (e.g. "F4", "BACKSPACE", etc.)

Macro Definition Documentation

#define GLFW_KEY_0   48
#define GLFW_KEY_1   49
#define GLFW_KEY_2   50
#define GLFW_KEY_3   51
#define GLFW_KEY_4   52
#define GLFW_KEY_5   53
#define GLFW_KEY_6   54
#define GLFW_KEY_7   55
#define GLFW_KEY_8   56
#define GLFW_KEY_9   57
#define GLFW_KEY_A   65
#define GLFW_KEY_APOSTROPHE   39 /* ' */
#define GLFW_KEY_B   66
#define GLFW_KEY_BACKSLASH   92 /* \ */
#define GLFW_KEY_BACKSPACE   259
#define GLFW_KEY_C   67
#define GLFW_KEY_CAPS_LOCK   280
#define GLFW_KEY_COMMA   44 /* , */
#define GLFW_KEY_D   68
#define GLFW_KEY_DELETE   261
#define GLFW_KEY_DOWN   264
#define GLFW_KEY_E   69
#define GLFW_KEY_END   269
#define GLFW_KEY_ENTER   257
#define GLFW_KEY_EQUAL   61 /* = */
#define GLFW_KEY_ESCAPE   256
#define GLFW_KEY_F   70
#define GLFW_KEY_F1   290
#define GLFW_KEY_F10   299
#define GLFW_KEY_F11   300
#define GLFW_KEY_F12   301
#define GLFW_KEY_F13   302
#define GLFW_KEY_F14   303
#define GLFW_KEY_F15   304
#define GLFW_KEY_F16   305
#define GLFW_KEY_F17   306
#define GLFW_KEY_F18   307
#define GLFW_KEY_F19   308
#define GLFW_KEY_F2   291
#define GLFW_KEY_F20   309
#define GLFW_KEY_F21   310
#define GLFW_KEY_F22   311
#define GLFW_KEY_F23   312
#define GLFW_KEY_F24   313
#define GLFW_KEY_F25   314
#define GLFW_KEY_F3   292
#define GLFW_KEY_F4   293
#define GLFW_KEY_F5   294
#define GLFW_KEY_F6   295
#define GLFW_KEY_F7   296
#define GLFW_KEY_F8   297
#define GLFW_KEY_F9   298
#define GLFW_KEY_G   71
#define GLFW_KEY_GRAVE_ACCENT   96 /* ` */
#define GLFW_KEY_H   72
#define GLFW_KEY_HOME   268
#define GLFW_KEY_I   73
#define GLFW_KEY_INSERT   260
#define GLFW_KEY_J   74
#define GLFW_KEY_K   75
#define GLFW_KEY_KP_0   320
#define GLFW_KEY_KP_1   321
#define GLFW_KEY_KP_2   322
#define GLFW_KEY_KP_3   323
#define GLFW_KEY_KP_4   324
#define GLFW_KEY_KP_5   325
#define GLFW_KEY_KP_6   326
#define GLFW_KEY_KP_7   327
#define GLFW_KEY_KP_8   328
#define GLFW_KEY_KP_9   329
#define GLFW_KEY_KP_ADD   334
#define GLFW_KEY_KP_DECIMAL   330
#define GLFW_KEY_KP_DIVIDE   331
#define GLFW_KEY_KP_ENTER   335
#define GLFW_KEY_KP_EQUAL   336
#define GLFW_KEY_KP_MULTIPLY   332
#define GLFW_KEY_KP_SUBTRACT   333
#define GLFW_KEY_L   76
#define GLFW_KEY_LAST   GLFW_KEY_MENU
#define GLFW_KEY_LEFT   263
#define GLFW_KEY_LEFT_ALT   342
#define GLFW_KEY_LEFT_BRACKET   91 /* [ */
#define GLFW_KEY_LEFT_CONTROL   341
#define GLFW_KEY_LEFT_SHIFT   340
#define GLFW_KEY_LEFT_SUPER   343
#define GLFW_KEY_M   77
#define GLFW_KEY_MENU   348
#define GLFW_KEY_MINUS   45 /* - */
#define GLFW_KEY_N   78
#define GLFW_KEY_NUM_LOCK   282
#define GLFW_KEY_O   79
#define GLFW_KEY_P   80
#define GLFW_KEY_PAGE_DOWN   267
#define GLFW_KEY_PAGE_UP   266
#define GLFW_KEY_PAUSE   284
#define GLFW_KEY_PERIOD   46 /* . */
#define GLFW_KEY_PRINT_SCREEN   283
#define GLFW_KEY_Q   81
#define GLFW_KEY_R   82
#define GLFW_KEY_RIGHT   262
#define GLFW_KEY_RIGHT_ALT   346
#define GLFW_KEY_RIGHT_BRACKET   93 /* ] */
#define GLFW_KEY_RIGHT_CONTROL   345
#define GLFW_KEY_RIGHT_SHIFT   344
#define GLFW_KEY_RIGHT_SUPER   347
#define GLFW_KEY_S   83
#define GLFW_KEY_SCROLL_LOCK   281
#define GLFW_KEY_SEMICOLON   59 /* ; */
#define GLFW_KEY_SLASH   47 /* / */
#define GLFW_KEY_SPACE   32
#define GLFW_KEY_T   84
#define GLFW_KEY_TAB   258
#define GLFW_KEY_U   85
#define GLFW_KEY_UNKNOWN   -1
#define GLFW_KEY_UP   265
#define GLFW_KEY_V   86
#define GLFW_KEY_W   87
#define GLFW_KEY_WORLD_1   161 /* non-US #1 */
#define GLFW_KEY_WORLD_2   162 /* non-US #2 */
#define GLFW_KEY_X   88
#define GLFW_KEY_Y   89
#define GLFW_KEY_Z   90
点击打开链接
IndexError: index 11 is out of bounds for axis 0 with size 11修正代码#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 完整的人形机器人仿真系统 - 包含 MuJoCoRenderer 定义和增强功能 """ import sys import os import time import json import math import random import numpy as np import glfw import mujoco import imageio import matplotlib.pyplot as plt from scipy.spatial.transform import Rotation as R from collections import deque from typing import Dict, List, Tuple, Optional, Callable # ======================== MuJoCo 渲染器定义 ======================== class MuJoCoRenderer: def __init__(self, model, title="Humanoid Simulation", width=1200, height=800): self.model = model self.data = mujoco.MjData(model) self.width = width self.height = height self.title = title # 初始化 GLFW if not glfw.init(): raise RuntimeError("Could not initialize GLFW") # 创建窗口 self.window = glfw.create_window(width, height, title, None, None) if not self.window: glfw.terminate() raise RuntimeError("Could not create GLFW window") glfw.make_context_current(self.window) glfw.swap_interval(1) # 开启垂直同步 # 初始化 MuJoCo 渲染组件 self.cam = mujoco.MjvCamera() self.opt = mujoco.MjvOption() self.scene = mujoco.MjvScene(model, maxgeom=1000) self.context = mujoco.MjrContext(model, mujoco.mjtFontScale.mjFONTSCALE_150) # 设置相机初始参数 self.cam.distance = 5.0 self.cam.elevation = -20 self.cam.azimuth = 90 # 注册回调函数 glfw.set_key_callback(self.window, self.keyboard_callback) glfw.set_cursor_pos_callback(self.window, self.mouse_move_callback) glfw.set_mouse_button_callback(self.window, self.mouse_button_callback) glfw.set_scroll_callback(self.window, self.scroll_callback) glfw.set_framebuffer_size_callback(self.window, self.resize_callback) # 帧缓冲区 self.framebuffer = np.zeros((height, width, 3), dtype=np.uint8) # 状态变量 self.button_left = False self.button_middle = False self.button_right = False self.last_x = 0 self.last_y = 0 self.paused = False self.camera_mode = 0 self.move_forward = False self.move_backward = False self.move_left = False self.move_right = False print("✅ MuJoCo 渲染器初始化完成") def keyboard_callback(self, window, key, scancode, action, mods): """键盘回调函数""" if action != glfw.PRESS: return # ESC 键退出 if key == glfw.KEY_ESCAPE: glfw.set_window_should_close(window, True) # 空格键暂停/继续 elif key == glfw.KEY_SPACE: self.paused = not self.paused # 方向键控制机器人 elif key == glfw.KEY_UP: self.move_forward = True elif key == glfw.KEY_DOWN: self.move_backward = True elif key == glfw.KEY_LEFT: self.move_left = True elif key == glfw.KEY_RIGHT: self.move_right = True # R 键重置 elif key == glfw.KEY_R: self.reset_simulation() # C 键切换相机模式 elif key == glfw.KEY_C: self.camera_mode = (self.camera_mode + 1) % 3 self.update_camera() def mouse_move_callback(self, window, xpos, ypos): """鼠标移动回调函数""" dx = xpos - self.last_x dy = ypos - self.last_y self.last_x = xpos self.last_y = ypos # 没有按钮按下时不操作 if not (self.button_left or self.button_middle or self.button_right): return # 获取当前窗口大小 width, height = glfw.get_window_size(window) # 鼠标左键:旋转视角 if self.button_left: self.cam.azimuth -= dx * 0.5 self.cam.elevation += dy * 0.5 self.cam.elevation = max(-90, min(90, self.cam.elevation)) # 鼠标右键:平移视角 elif self.button_right: self.cam.track(-dx * 0.01, dy * 0.01) # 鼠标中键:缩放 elif self.button_middle: self.cam.distance *= (1.0 + 0.05 * dy) self.cam.distance = max(0.1, min(50, self.cam.distance)) def mouse_button_callback(self, window, button, action, mods): """鼠标按钮回调函数""" self.button_left = (button == glfw.MOUSE_BUTTON_LEFT and action == glfw.PRESS) self.button_middle = (button == glfw.MOUSE_BUTTON_MIDDLE and action == glfw.PRESS) self.button_right = (button == glfw.MOUSE_BUTTON_RIGHT and action == glfw.PRESS) # 更新鼠标位置 self.last_x, self.last_y = glfw.get_cursor_pos(window) def scroll_callback(self, window, xoffset, yoffset): """滚轮回调函数""" self.cam.distance *= (1.0 - 0.1 * yoffset) self.cam.distance = max(0.1, min(50, self.cam.distance)) def resize_callback(self, window, width, height): """窗口大小调整回调函数""" self.width = width self.height = height self.framebuffer = np.zeros((height, width, 3), dtype=np.uint8) glfw.make_context_current(window) mujoco.mjr_resizeOffscreenBuffer(self.context, width, height) def update_camera(self): """更新相机视角""" if self.camera_mode == 0: # 默认视角 self.cam.distance = 5.0 self.cam.elevation = -20 self.cam.azimuth = 90 elif self.camera_mode == 1: # 跟随视角 self.cam.distance = 3.0 self.cam.elevation = -10 self.cam.azimuth = 45 elif self.camera_mode == 2: # 第一人称视角 self.cam.distance = 1.5 self.cam.elevation = 0 self.cam.azimuth = 0 def reset_simulation(self): """重置仿真""" mujoco.mj_resetData(self.model, self.data) self.data.qpos[0:3] = [0, 0, 1.0] # 初始位置 self.data.qpos[3:7] = [1, 0, 0, 0] # 初始朝向(四元数) mujoco.mj_forward(self.model, self.data) def render(self): """渲染一帧""" # 更新场景 mujoco.mjv_updateScene( self.model, self.data, self.opt, None, self.cam, mujoco.mjtCatBit.mjCAT_ALL, self.scene) # 设置视口 viewport = mujoco.MjrRect(0, 0, self.width, self.height) # 渲染到屏幕 mujoco.mjr_render(viewport, self.scene, self.context) # 交换缓冲区 glfw.swap_buffers(self.window) # 处理事件 glfw.poll_events() def capture_frame(self): """捕获当前帧作为图像""" # 确保帧缓冲区大小正确 if self.framebuffer.shape[0] != self.height or self.framebuffer.shape[1] != self.width: self.framebuffer = np.zeros((self.height, self.width, 3), dtype=np.uint8) # 设置视口 viewport = mujoco.MjrRect(0, 0, self.width, self.height) # 读取像素 mujoco.mjr_readPixels(self.framebuffer, None, viewport, self.context) # 图像需要垂直翻转 return np.flipud(self.framebuffer) def close(self): """关闭渲染器""" glfw.terminate() # ======================== 高级控制器定义 ======================== class Controller: """机器人控制算法基类""" def __init__(self, model, data): self.model = model self.data = data self.target_position = np.array([0.0, 0.0, 0.0]) self.target_orientation = np.array([1.0, 0.0, 0.0, 0.0]) self.gait_phase = 0.0 self.gait_speed = 1.0 self.last_update_time = time.time() def set_target(self, position, orientation=None): """设置目标位置和方向""" self.target_position = position if orientation is not None: self.target_orientation = orientation def update_gait(self, dt): """更新步态相位""" self.gait_phase = (self.gait_phase + dt * self.gait_speed) % (2 * math.pi) return self.gait_phase def compute_pd_control(self, joint_index, target_angle, kp=500, kd=50): """计算PD控制信号""" current_angle = self.data.qpos[joint_index] current_velocity = self.data.qvel[joint_index] error = target_angle - current_angle control = kp * error - kd * current_velocity return control def update(self, dt): """更新控制信号(子类实现)""" raise NotImplementedError("Controller subclass must implement update method") def reset(self): """重置控制器状态""" self.gait_phase = 0.0 class WalkingController(Controller): """双足行走控制器""" def __init__(self, model, data): super().__init__(model, data) # 步态参数 self.step_length = 0.3 self.step_height = 0.15 self.stance_width = 0.25 self.swing_percent = 0.4 self.hip_offset = 0.1 self.ankle_offset = 0.05 # 关节映射 self.joint_map = {} try: self.joint_map = { "left_hip": mujoco.mj_name2id(model, mujoco.mjtObj.mjOBJ_JOINT, "left_hip"), "left_knee": mujoco.mj_name2id(model, mujoco.mjtObj.mjOBJ_JOINT, "left_knee"), "right_hip": mujoco.mj_name2id(model, mujoco.mjtObj.mjOBJ_JOINT, "right_hip"), "right_knee": mujoco.mj_name2id(model, mujoco.mjtObj.mjOBJ_JOINT, "right_knee"), "left_ankle": mujoco.mj_name2id(model, mujoco.mjtObj.mjOBJ_JOINT, "left_ankle"), "right_ankle": mujoco.mj_name2id(model, mujoco.mjtObj.mjOBJ_JOINT, "right_ankle"), } except: # 后备方案:使用索引代替名称 self.joint_map = { "left_hip": 5, "left_knee": 6, "right_hip": 7, "right_knee": 8, "left_ankle": 9, "right_ankle": 10 } def inverse_kinematics(self, foot_pos, hip_pos, is_left=True): """计算腿部逆运动学""" # 计算腿部向量 leg_vector = foot_pos - hip_pos leg_length = np.linalg.norm(leg_vector) # 计算髋关节角度 hip_pitch = math.atan2(leg_vector[2], leg_vector[0]) hip_roll = math.atan2(leg_vector[1], math.sqrt(leg_vector[0]**2 + leg_vector[2]**2)) # 计算膝关节角度 thigh_length = 0.4 # 大腿长度 shin_length = 0.4 # 小腿长度 knee_angle = math.acos((thigh_length**2 + shin_length**2 - leg_length**2) / (2 * thigh_length * shin_length)) # 计算踝关节角度 ankle_pitch = hip_pitch ankle_roll = hip_roll # 根据左右腿调整符号 if is_left: hip_roll = -hip_roll ankle_roll = -ankle_roll return hip_pitch, hip_roll, knee_angle, ankle_pitch, ankle_roll def update(self, dt): """更新行走控制""" gait_phase = self.update_gait(dt) # 计算步态参数 swing_phase = gait_phase % (2 * math.pi) left_in_swing = (swing_phase < math.pi * self.swing_percent) or \ (swing_phase > math.pi * (1 + self.swing_percent)) right_in_swing = not left_in_swing # 获取机器人位置和方向 torso_pos = self.data.body("torso").xpos.copy() torso_quat = self.data.body("torso").xquat.copy() # 计算目标前进方向 target_direction = self.target_position - torso_pos target_direction[2] = 0 # 忽略高度差 if np.linalg.norm(target_direction) > 0.1: target_direction /= np.linalg.norm(target_direction) else: target_direction = np.array([1, 0, 0]) # 默认前进方向 # 计算髋关节位置 hip_height = 0.8 left_hip_pos = torso_pos + np.array([0, self.stance_width/2, -hip_height]) right_hip_pos = torso_pos + np.array([0, -self.stance_width/2, -hip_height]) # 计算脚部目标位置 step_progress = (gait_phase % (2 * math.pi)) / (2 * math.pi) # 左脚轨迹 left_foot_target = left_hip_pos.copy() left_foot_target[0] += self.step_length * math.sin(step_progress * math.pi) left_foot_target[2] = -0.9 # 地面高度 if left_in_swing: lift_height = self.step_height * math.sin(step_progress * math.pi / self.swing_percent) left_foot_target[2] += lift_height # 右脚轨迹 right_foot_target = right_hip_pos.copy() right_foot_target[0] += self.step_length * math.sin((step_progress + 0.5) * math.pi) right_foot_target[2] = -0.9 # 地面高度 if right_in_swing: lift_height = self.step_height * math.sin((step_progress - 0.5) * math.pi / self.swing_percent) right_foot_target[2] += lift_height # 应用逆运动学计算关节角度 try: left_hip_pitch, left_hip_roll, left_knee_angle, left_ankle_pitch, left_ankle_roll = \ self.inverse_kinematics(left_foot_target, left_hip_pos, is_left=True) right_hip_pitch, right_hip_roll, right_knee_angle, right_ankle_pitch, right_ankle_roll = \ self.inverse_kinematics(right_foot_target, right_hip_pos, is_left=False) # 应用PD控制 self.data.ctrl[self.joint_map["left_hip"]] = self.compute_pd_control( self.joint_map["left_hip"], left_hip_pitch) self.data.ctrl[self.joint_map["left_knee"]] = self.compute_pd_control( self.joint_map["left_knee"], left_knee_angle) self.data.ctrl[self.joint_map["left_ankle"]] = self.compute_pd_control( self.joint_map["left_ankle"], left_ankle_pitch) self.data.ctrl[self.joint_map["right_hip"]] = self.compute_pd_control( self.joint_map["right_hip"], right_hip_pitch) self.data.ctrl[self.joint_map["right_knee"]] = self.compute_pd_control( self.joint_map["right_knee"], right_knee_angle) self.data.ctrl[self.joint_map["right_ankle"]] = self.compute_pd_control( self.joint_map["right_ankle"], right_ankle_pitch) except KeyError: # 后备控制方案 amplitude = 0.5 freq = 2.0 self.data.ctrl[5] = amplitude * math.sin(gait_phase) # left_hip self.data.ctrl[6] = amplitude * math.sin(gait_phase + math.pi/2) # left_knee self.data.ctrl[7] = amplitude * math.sin(gait_phase + math.pi) # right_hip self.data.ctrl[8] = amplitude * math.sin(gait_phase + 3*math.pi/2) # right_knee # ======================== 环境交互管理器 ======================== class EnvironmentManager: """管理仿真环境中的物体和交互""" def __init__(self, model): self.model = model self.obstacles = [] self.targets = [] self.interactive_objects = [] self.create_environment() def create_environment(self): """创建基本环境""" # 添加一些障碍物 self.add_obstacle("box1", [3.0, 1.5, 0.5], [0.5, 0.5, 0.5]) self.add_obstacle("box2", [5.0, -1.0, 0.3], [0.8, 0.3, 0.3]) self.add_obstacle("cylinder1", [7.0, 0.0, 0.4], [0.4, 0.4, 0.4], geom_type="cylinder") # 添加目标点 self.add_target("target1", [8.0, 0.0, 0.5]) self.add_target("target2", [10.0, 2.0, 0.5]) # 添加可交互物体 self.add_interactive_object("ball1", [2.0, -2.0, 0.5], 0.3) def add_obstacle(self, name, position, size, geom_type="box", rgba=None): """添加障碍物""" if rgba is None: rgba = [0.8, 0.2, 0.2, 0.8] # 半透明红色 obstacle = { "name": name, "type": "obstacle", "geom_type": geom_type, "position": np.array(position), "size": np.array(size), "rgba": rgba } self.obstacles.append(obstacle) def add_target(self, name, position, size=0.3, rgba=None): """添加目标点""" if rgba is None: rgba = [0.2, 0.8, 0.2, 1.0] # 绿色 target = { "name": name, "type": "target", "position": np.array(position), "size": size, "rgba": rgba, "reached": False } self.targets.append(target) def add_interactive_object(self, name, position, size, mass=2.0, rgba=None): """添加可交互物体""" if rgba is None: rgba = [0.2, 0.2, 0.8, 1.0] # 蓝色 obj = { "name": name, "type": "interactive", "position": np.array(position), "size": size, "mass": mass, "rgba": rgba } self.interactive_objects.append(obj) def check_collision(self, position, radius=0.3): """检查与障碍物的碰撞""" for obstacle in self.obstacles: if obstacle["geom_type"] == "box": # 简化的AABB碰撞检测 min_corner = obstacle["position"] - obstacle["size"] max_corner = obstacle["position"] + obstacle["size"] closest_point = np.clip(position, min_corner, max_corner) distance = np.linalg.norm(position - closest_point) if distance < radius: return True, obstacle["name"] elif obstacle["geom_type"] == "cylinder": # 圆柱体碰撞检测 (忽略高度) dx = position[0] - obstacle["position"][0] dy = position[1] - obstacle["position"][1] distance = math.sqrt(dx*dx + dy*dy) if distance < obstacle["size"][0] + radius: return True, obstacle["name"] return False, None def check_target_reached(self, position, radius=0.5): """检查是否到达目标点""" for target in self.targets: if not target["reached"]: distance = np.linalg.norm(position - target["position"]) if distance < radius: target["reached"] = True return True, target["name"] return False, None # ======================== 数据分析与可视化 ======================== class DataAnalyzer: """收集、分析和可视化仿真数据""" def __init__(self): self.data = { "time": [], "position": [], "velocity": [], "energy": [], "stability": [], "collisions": [], "targets_reached": [], "joint_angles": {}, "joint_torques": {} } self.start_time = time.time() self.collision_count = 0 self.targets_reached = 0 def record_frame(self, data, robot): """记录当前帧的数据""" current_time = time.time() - self.start_time self.data["time"].append(current_time) try: # 记录位置和速度 torso_pos = robot.data.body("torso").xpos.copy() torso_vel = robot.data.body("torso").cvel[3:6].copy() # 线性速度 self.data["position"].append(torso_pos) self.data["velocity"].append(torso_vel) # 计算并记录能量消耗 energy = 0 for i in range(robot.model.nu): torque = robot.data.ctrl[i] velocity = robot.data.qvel[i] power = abs(torque * velocity) energy += power * 0.01 # 假设时间步长为0.01 self.data["energy"].append(energy) # 计算稳定性指标 (基于质心投影) com = robot.data.subtree_com[0] support_polygon = self.calculate_support_polygon(robot) stability = self.calculate_stability(com, support_polygon) self.data["stability"].append(stability) # 记录碰撞和目标达成 self.data["collisions"].append(self.collision_count) self.data["targets_reached"].append(self.targets_reached) # 记录关节角度和扭矩 for i in range(robot.model.nu): joint_name = f"joint_{i}" if joint_name not in self.data["joint_angles"]: self.data["joint_angles"][joint_name] = [] self.data["joint_torques"][joint_name] = [] self.data["joint_angles"][joint_name].append(robot.data.qpos[i]) self.data["joint_torques"][joint_name].append(robot.data.ctrl[i]) except Exception as e: print(f"数据记录出错: {e}") def calculate_support_polygon(self, robot): """计算支撑多边形""" # 简化的支撑多边形计算 (基于脚部位置) try: left_foot_pos = robot.data.body("left_foot").xpos[:2] right_foot_pos = robot.data.body("right_foot").xpos[:2] return [left_foot_pos, right_foot_pos] except: # 后备方案:使用躯干位置 torso_pos = robot.data.body("torso").xpos[:2] return [torso_pos - np.array([0.2, 0]), torso_pos + np.array([0.2, 0])] def calculate_stability(self, com, support_polygon): """计算稳定性指标 (0-1)""" try: com_2d = com[:2] min_dist = float(&#39;inf&#39;) # 计算到支撑多边形边的最小距离 for i in range(len(support_polygon)): p1 = support_polygon[i] p2 = support_polygon[(i + 1) % len(support_polygon)] # 计算点到线段的距离 line_vec = p2 - p1 point_vec = com_2d - p1 line_len = np.linalg.norm(line_vec) line_unit = line_vec / line_len proj_len = np.dot(point_vec, line_unit) proj_len = max(0, min(line_len, proj_len)) proj_point = p1 + line_unit * proj_len dist = np.linalg.norm(com_2d - proj_point) if dist < min_dist: min_dist = dist # 稳定性指标 (距离越大,稳定性越低) stability = max(0, 1.0 - min_dist * 2.0) # 假设0.5m距离为完全不稳定 return stability except: return 1.0 # 默认稳定 def report_collision(self): """记录碰撞事件""" self.collision_count += 1 def report_target_reached(self): """记录目标达成事件""" self.targets_reached += 1 def generate_report(self, filename="performance_report.pdf"): """生成性能报告""" try: fig, axs = plt.subplots(3, 2, figsize=(15, 15)) fig.suptitle("人形机器人性能分析报告", fontsize=16) # 位置轨迹 positions = np.array(self.data["position"]) axs[0, 0].plot(positions[:, 0], positions[:, 1], &#39;b-&#39;) axs[0, 0].set_title("运动轨迹") axs[0, 0].set_xlabel("X (m)") axs[0, 0].set_ylabel("Y (m)") axs[0, 0].grid(True) # 速度和能量 time = np.array(self.data["time"]) velocities = np.array([np.linalg.norm(v) for v in self.data["velocity"]]) axs[0, 1].plot(time, velocities, &#39;r-&#39;, label="速度") axs[0, 1].set_title("速度变化") axs[0, 1].set_xlabel("时间 (s)") axs[0, 1].set_ylabel("速度 (m/s)") axs[0, 1].grid(True) ax2 = axs[0, 1].twinx() energy = np.array(self.data["energy"]) cumulative_energy = np.cumsum(energy) ax2.plot(time, cumulative_energy, &#39;g-&#39;, label="累计能耗") ax2.set_ylabel("能耗 (J)") # 稳定性和事件 stability = np.array(self.data["stability"]) axs[1, 0].plot(time, stability, &#39;m-&#39;) axs[1, 0].set_title("稳定性指标") axs[1, 0].set_xlabel("时间 (s)") axs[1, 0].set_ylabel("稳定性 (0-1)") axs[1, 0].grid(True) collisions = np.array(self.data["collisions"]) targets = np.array(self.data["targets_reached"]) axs[1, 1].plot(time, collisions, &#39;r-&#39;, label="碰撞次数") axs[1, 1].plot(time, targets, &#39;g-&#39;, label="达成目标数") axs[1, 1].set_title("事件统计") axs[1, 1].set_xlabel("时间 (s)") axs[1, 1].legend() axs[1, 1].grid(True) # 关节角度和扭矩 if self.data["joint_angles"]: joint_name = list(self.data["joint_angles"].keys())[0] angles = np.array(self.data["joint_angles"][joint_name]) torques = np.array(self.data["joint_torques"][joint_name]) axs[2, 0].plot(time, angles, &#39;b-&#39;) axs[2, 0].set_title(f"关节角度: {joint_name}") axs[2, 0].set_xlabel("时间 (s)") axs[2, 0].set_ylabel("角度 (rad)") axs[2, 0].grid(True) axs[2, 1].plot(time, torques, &#39;r-&#39;) axs[2, 1].set_title(f"关节扭矩: {joint_name}") axs[2, 1].set_xlabel("时间 (s)") axs[2, 1].set_ylabel("扭矩 (Nm)") axs[2, 1].grid(True) plt.tight_layout() plt.savefig(filename) print(f"✅ 性能报告已保存到 {filename}") return True except Exception as e: print(f"⚠️ 生成报告失败: {e}") return False # ======================== 增强版人形机器人仿真系统 ======================== class EnhancedHumanoidDemo: """人形机器人仿真演示增强版""" def __init__(self): """初始化演示系统""" print("🤖 人形机器人仿真演示系统 (增强版)") print("=" * 60) # 创建人形机器人模型 self.model = self._create_robot_model() self.data = mujoco.MjData(self.model) # 创建渲染器 self.renderer = MuJoCoRenderer(self.model) self.renderer.data = self.data # 关联数据 # 创建控制器 self.controller = WalkingController(self.model, self.data) # 创建环境管理器 self.environment = EnvironmentManager(self.model) # 创建数据分析器 self.analyzer = DataAnalyzer() # 初始化状态 self.current_target_index = 0 self.navigation_path = [] # 演示配置 self.demo_config = { &#39;duration&#39;: 60.0, &#39;enable_ai&#39;: True, &#39;enable_optimization&#39;: True, &#39;enable_adaptation&#39;: True, &#39;record_data&#39;: True, &#39;save_video&#39;: True, &#39;show_analysis&#39;: True } # 视频录制 self.video_writer = None if self.demo_config[&#39;save_video&#39;]: self.init_video_recording() print("✅ 增强版演示系统初始化完成") def _create_robot_model(self): """创建人形机器人模型(包含脚部传感器)""" model_xml = """ <mujoco> <option gravity="0 0 -9.81"/> <asset> <material name="grid" rgba="0.8 0.9 0.8 1"/> <material name="body" rgba="0.5 0.7 0.9 1"/> <material name="limb" rgba="0.6 0.8 0.4 1"/> <material name="head" rgba="0.8 0.6 0.4 1"/> <material name="foot" rgba="0.3 0.3 0.3 1"/> </asset> <worldbody> <light name="light" pos="0 0 4" dir="0 0 -1"/> <geom name="ground" type="plane" size="20 20 0.1" material="grid"/> <!-- 机器人主体 --> <body name="torso" pos="0 0 1.5"> <joint name="root_z" type="slide" axis="0 0 1" pos="0 0 0.5"/> <joint name="root_x" type="slide" axis="1 0 0" pos="0 0 0.5"/> <joint name="root_y" type="hinge" axis="0 1 0" pos="0 0 0.5"/> <geom name="torso_geom" type="box" size="0.3 0.2 0.4" mass="10" material="body"/> <!-- 头部 --> <body name="head" pos="0 0 0.3"> <joint name="neck" type="ball" damping="0.01"/> <geom name="head_geom" type="sphere" size="0.2" mass="3" material="head"/> </body> <!-- 手臂 --> <body name="left_upper_arm" pos="0.3 0.2 0"> <joint name="left_shoulder" type="ball" damping="0.01"/> <geom name="left_upper_arm_geom" type="capsule" fromto="0 0 0 0.2 0 0" size="0.05" mass="1.5" material="limb"/> <body name="left_lower_arm" pos="0.2 0 0"> <joint name="left_elbow" type="hinge" axis="0 1 0" range="0 90"/> <geom name="left_lower_arm_geom" type="capsule" fromto="0 0 0 0.2 0 0" size="0.04" mass="1.0" material="limb"/> </body> </body> <body name="right_upper_arm" pos="0.3 -0.2 0"> <joint name="right_shoulder" type="ball" damping="0.01"/> <geom name="right_upper_arm_geom" type="capsule" fromto="0 0 0 0.2 0 0" size="0.05" mass="1.5" material="limb"/> <body name="right_lower_arm" pos="0.2 0 0"> <joint name="right_elbow" type="hinge" axis="0 1 0" range="0 90"/> <geom name="right_lower_arm_geom" type="capsule" fromto="0 0 0 0.2 0 0" size="0.04" mass="1.0" material="limb"/> </body> </body> <!-- 腿部 --> <body name="left_upper_leg" pos="0 -0.1 -0.4"> <joint name="left_hip" type="ball" damping="0.01"/> <geom name="left_upper_leg_geom" type="capsule" fromto="0 0 0 0 -0.1 -0.4" size="0.07" mass="4" material="limb"/> <body name="left_lower_leg" pos="0 -0.1 -0.8"> <joint name="left_knee" type="hinge" axis="0 1 0" range="0 120"/> <geom name="left_lower_leg_geom" type="capsule" fromto="0 0 0 0 0 -0.4" size="0.05" mass="3" material="limb"/> <body name="left_foot" pos="0 0 -0.4"> <joint name="left_ankle" type="ball" damping="0.01"/> <geom name="left_foot_geom" type="box" size="0.1 0.05 0.05" mass="1.5" material="foot"/> <site name="left_foot_sensor" type="sphere" size="0.01" pos="0 0 -0.05" rgba="1 0 0 1"/> </body> </body> </body> <body name="right_upper_leg" pos="0 0.1 -0.4"> <joint name="right_hip" type="ball" damping="0.01"/> <geom name="right_upper_leg_geom" type="capsule" fromto="0 0 0 0 0.1 -0.4" size="0.07" mass="4" material="limb"/> <body name="right_lower_leg" pos="0 0.1 -0.8"> <joint name="right_knee" type="hinge" axis="0 1 0" range="0 120"/> <geom name="right_lower_leg_geom" type="capsule" fromto="0 0 0 0 0 -0.4" size="0.05" mass="3" material="limb"/> <body name="right_foot" pos="0 0 -0.4"> <joint name="right_ankle" type="ball" damping="0.01"/> <geom name="right_foot_geom" type="box" size="0.1 0.05 0.05" mass="1.5" material="foot"/> <site name="right_foot_sensor" type="sphere" size="0.01" pos="0 0 -0.05" rgba="1 0 0 1"/> </body> </body> </body> </body> </worldbody> <actuator> <motor joint="neck" gear="50"/> <motor joint="left_shoulder" gear="100"/> <motor joint="left_elbow" gear="80"/> <motor joint="right_shoulder" gear="100"/> <motor joint="right_elbow" gear="80"/> <motor joint="left_hip" gear="150"/> <motor joint="left_knee" gear="120"/> <motor joint="right_hip" gear="150"/> <motor joint="right_knee" gear="120"/> <motor joint="left_ankle" gear="100"/> <motor joint="right_ankle" gear="100"/> </actuator> <sensor> <touch name="left_foot_contact" site="left_foot_sensor"/> <touch name="right_foot_contact" site="right_foot_sensor"/> </sensor> </mujoco> """ try: return mujoco.MjModel.from_xml_string(model_xml) except Exception as e: print(f"⚠️ 创建机器人模型失败: {e}") # 创建简单后备模型 return mujoco.MjModel.from_xml_string(""" <mujoco> <worldbody> <light name="light" pos="0 0 4"/> <geom name="ground" type="plane" size="10 10 0.1" rgba="0.8 0.9 0.8 1"/> <body name="torso" pos="0 0 1"> <joint type="free"/> <geom type="box" size="0.2 0.2 0.2" rgba="0.5 0.7 0.9 1"/> <body name="left_leg" pos="0 -0.2 -0.3"> <joint type="hinge" axis="0 1 0"/> <geom type="capsule" fromto="0 0 0 0 -0.2 -0.4" size="0.05" rgba="0.6 0.8 0.4 1"/> </body> <body name="right_leg" pos="0 0.2 -0.3"> <joint type="hinge" axis="0 1 0"/> <geom type="capsule" fromto="0 0 0 0 0.2 -0.4" size="0.05" rgba="0.6 0.8 0.4 1"/> </body> </body> </worldbody> <actuator> <motor joint="left_leg" gear="100"/> <motor joint="right_leg" gear="100"/> </actuator> </mujoco> """) def init_video_recording(self): """初始化视频录制""" try: self.video_writer = imageio.get_writer( f&#39;simulation_{time.strftime("%Y%m%d_%H%M%S")}.mp4&#39;, fps=30, macro_block_size=None ) print("🎥 视频录制已启动") except Exception as e: print(f"⚠️ 无法启动视频录制: {e}") self.video_writer = None def update_navigation(self): """更新导航目标""" if not self.environment.targets: return # 获取当前目标 current_target = self.environment.targets[self.current_target_index] # 如果目标已达成,移动到下一个目标 if current_target["reached"]: self.current_target_index = (self.current_target_index + 1) % len(self.environment.targets) current_target = self.environment.targets[self.current_target_index] # 设置控制器目标 self.controller.set_target(current_target["position"]) def update_robot(self, dt): """更新机器人状态""" if self.renderer.paused: return # 更新导航目标 self.update_navigation() # 更新控制器 self.controller.update(dt) # 应用物理模拟 mujoco.mj_step(self.model, self.data, nstep=1) # 检测碰撞 torso_pos = self.data.body("torso").xpos collision, obstacle_name = self.environment.check_collision(torso_pos) if collision: print(f"⚠️ 与障碍物 &#39;{obstacle_name}&#39; 发生碰撞!") self.analyzer.report_collision() # 检测目标达成 target_reached, target_name = self.environment.check_target_reached(torso_pos) if target_reached: print(f"🎯 达成目标 &#39;{target_name}&#39;!") self.analyzer.report_target_reached() def record_data(self, current_time): """记录演示数据""" self.analyzer.record_frame(self.data, self) def run_demo(self): """运行演示""" print(f"🚀 开始演示,持续时间: {self.demo_config[&#39;duration&#39;]}秒") print("=" * 60) print("控制说明:") print(" 方向键: 控制机器人移动") print(" 空格键: 暂停/继续仿真") print(" R 键: 重置仿真") print(" C 键: 切换相机视角") print(" ESC 键: 退出程序") print("=" * 60) start_time = time.time() last_render_time = time.time() last_data_record_time = time.time() frame_count = 0 try: while not glfw.window_should_close(self.renderer.window): current_time = time.time() - start_time # 更新机器人状态 dt = min(0.01, time.time() - last_render_time) self.update_robot(dt) # 记录数据 (每秒10次) if time.time() - last_data_record_time > 0.1 and self.demo_config[&#39;record_data&#39;]: self.record_data(current_time) last_data_record_time = time.time() # 渲染 self.renderer.render() frame_count += 1 # 捕获视频帧 if self.video_writer: frame = self.renderer.capture_frame() self.video_writer.append_data(frame) # 显示帧率 fps = frame_count / (current_time + 1e-5) if frame_count % 30 == 0: status = f"{self.renderer.title} - FPS: {fps:.1f}, Time: {current_time:.1f}/{self.demo_config[&#39;duration&#39;]:.1f}s" status += f", Targets: {self.analyzer.targets_reached}/{len(self.environment.targets)}" glfw.set_window_title(self.renderer.window, status) # 检查演示是否结束 if current_time >= self.demo_config[&#39;duration&#39;]: break last_render_time = time.time() print("\n✅ 演示完成!") # 生成性能报告 if self.demo_config[&#39;show_analysis&#39;]: self.analyzer.generate_report() except Exception as e: print(f"\n⛔ 演示出错: {e}") import traceback traceback.print_exc() finally: # 关闭视频录制 if self.video_writer: self.video_writer.close() print("🎥 视频录制已保存") # 关闭渲染器 self.renderer.close() def main(): """主函数""" # 创建演示实例 demo = EnhancedHumanoidDemo() # 设置演示参数 demo.demo_config[&#39;duration&#39;] = 60.0 # 60秒演示 demo.demo_config[&#39;save_video&#39;] = True # 启用视频录制 demo.demo_config[&#39;show_analysis&#39;] = True # 显示分析报告 # 运行演示 demo.run_demo() if __name__ == "__main__": # 设置环境变量(如果 MuJoCo 不在默认路径) # os.environ[&#39;MUJOCO_PY_MUJOCO_PATH&#39;] = &#39;path/to/mujoco&#39; main()
最新发布
07-29
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #include <math.h> #include <GL/glut.h> #include <string.h> #define PI 3.14159265358979323846 // 全局变量 typedef struct { float x, y, z; } Point3D; typedef struct { float r, g, b, a; } Color; typedef struct { int v1, v2, v3; } Face; // 网格数据结构 typedef struct { int numVertices; int numFaces; Point3D* vertices; Face* faces; Point3D* normals; // 用于光照计算的法线 float* texCoords; // 纹理坐标 } Mesh; // 变换控制 typedef struct { Point3D translation; Point3D rotation; float scale; int transformMode; // 0:旋转, 1:平移, 2:缩放 } Transform; // 应用程序状态 typedef struct { Mesh mesh; int drawMode; // 0:点, 1:边, 2:面, 3:真实感 Transform transform; Point3D pivotPoint; int lastMouseX; int lastMouseY; int mouseLeftDown; int mouseRightDown; int showHelp; int showTransformPoints; int textureEnabled; GLuint textureID; Point3D q1, q2; // 用户输入的变换点 Point3D originalP1, originalP2; // 原始北极和南极 } AppState; AppState appState; // 纹理数据(简单的棋盘纹理) #define TEX_WIDTH 64 #define TEX_HEIGHT 64 GLubyte texture[TEX_WIDTH][TEX_HEIGHT][3]; // 创建棋盘纹理 void createTexture() { for (int i = 0; i < TEX_WIDTH; i++) { for (int j = 0; j < TEX_HEIGHT; j++) { int c = (((i & 0x8) == 0) ^ ((j & 0x8) == 0)) * 255; texture[i][j][0] = (GLubyte)c; texture[i][j][1] = (GLubyte)(c * 0.7); texture[i][j][2] = (GLubyte)(255 - c); } } } // 初始化纹理 void initTexture() { createTexture(); glGenTextures(1, &appState.textureID); glBindTexture(GL_TEXTURE_2D, appState.textureID); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, TEX_WIDTH, TEX_HEIGHT, 0, GL_RGB, GL_UNSIGNED_BYTE, texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); appState.textureEnabled = 1; } // 读取网格文件 void ReadMesh(const char* fName) { FILE* fp = fopen(fName, "r"); if (!fp) { printf("无法打开文件: %s\n", fName); exit(1); } // 读取顶点数和面数 int nE; if (fscanf(fp, "%d %d %d", &appState.mesh.numVertices, &nE, &appState.mesh.numFaces) != 3) { printf("文件格式错误\n"); fclose(fp); exit(1); } // 分配内存 appState.mesh.vertices = (Point3D*)malloc(appState.mesh.numVertices * sizeof(Point3D)); appState.mesh.faces = (Face*)malloc(appState.mesh.numFaces * sizeof(Face)); appState.mesh.normals = (Point3D*)malloc(appState.mesh.numVertices * sizeof(Point3D)); appState.mesh.texCoords = (float*)malloc(appState.mesh.numVertices * 2 * sizeof(float)); // 初始化法线 for (int i = 0; i < appState.mesh.numVertices; i++) { appState.mesh.normals[i].x = 0.0f; appState.mesh.normals[i].y = 0.0f; appState.mesh.normals[i].z = 0.0f; } // 读取顶点 for (int i = 0; i < appState.mesh.numVertices; i++) { float x, y, z; if (fscanf(fp, "%f %f %f", &x, &y, &z) != 3) { printf("顶点数据错误\n"); fclose(fp); free(appState.mesh.vertices); free(appState.mesh.faces); exit(1); } appState.mesh.vertices[i].x = x; appState.mesh.vertices[i].y = y; appState.mesh.vertices[i].z = z; // 计算纹理坐标 (球面映射) float u = 0.5f + atan2(z, x) / (2.0f * PI); float v = 0.5f - asin(y) / PI; appState.mesh.texCoords[2 * i] = u; appState.mesh.texCoords[2 * i + 1] = v; } // 读取面(假设所有面都是三角形) for (int i = 0; i < appState.mesh.numFaces; i++) { int v1, v2, v3; if (fscanf(fp, "%d %d %d", &v1, &v2, &v3) != 3) { printf("面数据错误\n"); fclose(fp); free(appState.mesh.vertices); free(appState.mesh.faces); exit(1); } appState.mesh.faces[i].v1 = v1; appState.mesh.faces[i].v2 = v2; appState.mesh.faces[i].v3 = v3; // 计算面法线 Point3D p1 = appState.mesh.vertices[v1]; Point3D p2 = appState.mesh.vertices[v2]; Point3D p3 = appState.mesh.vertices[v3]; Point3D v = { p2.x - p1.x, p2.y - p1.y, p2.z - p1.z }; Point3D w = { p3.x - p1.x, p3.y - p1.y, p3.z - p1.z }; // 叉积 Point3D normal; normal.x = v.y * w.z - v.z * w.y; normal.y = v.z * w.x - v.x * w.z; normal.z = v.x * w.y - v.y * w.x; // 归一化 float len = sqrt(normal.x * normal.x + normal.y * normal.y + normal.z * normal.z); if (len > 0) { normal.x /= len; normal.y /= len; normal.z /= len; } // 累加到顶点法线 appState.mesh.normals[v1].x += normal.x; appState.mesh.normals[v1].y += normal.y; appState.mesh.normals[v1].z += normal.z; appState.mesh.normals[v2].x += normal.x; appState.mesh.normals[v2].y += normal.y; appState.mesh.normals[v2].z += normal.z; appState.mesh.normals[v3].x += normal.x; appState.mesh.normals[v3].y += normal.y; appState.mesh.normals[v3].z += normal.z; } // 归一化顶点法线 for (int i = 0; i < appState.mesh.numVertices; i++) { float len = sqrt(appState.mesh.normals[i].x * appState.mesh.normals[i].x + appState.mesh.normals[i].y * appState.mesh.normals[i].y + appState.mesh.normals[i].z * appState.mesh.normals[i].z); if (len > 0) { appState.mesh.normals[i].x /= len; appState.mesh.normals[i].y /= len; appState.mesh.normals[i].z /= len; } } fclose(fp); printf("成功加载网格: %d 顶点, %d 面\n", appState.mesh.numVertices, appState.mesh.numFaces); // 保存原始北极和南极 appState.originalP1 = appState.mesh.vertices[0]; // 北极 appState.originalP2 = appState.mesh.vertices[appState.mesh.numVertices - 1]; // 南极 appState.q1 = appState.originalP1; appState.q2 = appState.originalP2; } // 应用变换矩阵 void applyTransformations() { // 应用平移 glTranslatef(appState.transform.translation.x, appState.transform.translation.y, appState.transform.translation.z); // 应用旋转 glRotatef(appState.transform.rotation.x, 1.0f, 0.0f, 0.0f); glRotatef(appState.transform.rotation.y, 0.0f, 1.0f, 0.0f); glRotatef(appState.transform.rotation.z, 0.0f, 0.0f, 1.0f); // 应用缩放 glScalef(appState.transform.scale, appState.transform.scale, appState.transform.scale); } // 应用球面变换(将P1变换到Q1,P2变换到Q2) void applySphereTransformation() { // 计算原始地轴向量 Point3D originalAxis; originalAxis.x = appState.originalP2.x - appState.originalP1.x; originalAxis.y = appState.originalP2.y - appState.originalP1.y; originalAxis.z = appState.originalP2.z - appState.originalP1.z; // 计算目标地轴向量 Point3D targetAxis; targetAxis.x = appState.q2.x - appState.q1.x; targetAxis.y = appState.q2.y - appState.q1.y; targetAxis.z = appState.q2.z - appState.q1.z; // 计算原始轴长度 float originalLength = sqrt(originalAxis.x * originalAxis.x + originalAxis.y * originalAxis.y + originalAxis.z * originalAxis.z); // 计算目标轴长度 float targetLength = sqrt(targetAxis.x * targetAxis.x + targetAxis.y * targetAxis.y + targetAxis.z * targetAxis.z); // 计算缩放因子 float scale = targetLength / originalLength; // 计算旋转轴和角度 Point3D axis; float angle; if (originalLength > 0 && targetLength > 0) { // 归一化向量 originalAxis.x /= originalLength; originalAxis.y /= originalLength; originalAxis.z /= originalLength; targetAxis.x /= targetLength; targetAxis.y /= targetLength; targetAxis.z /= targetLength; // 计算旋转轴(叉积) axis.x = originalAxis.y * targetAxis.z - originalAxis.z * targetAxis.y; axis.y = originalAxis.z * targetAxis.x - originalAxis.x * targetAxis.z; axis.z = originalAxis.x * targetAxis.y - originalAxis.y * targetAxis.x; // 计算旋转角度(点积) float dot = originalAxis.x * targetAxis.x + originalAxis.y * targetAxis.y + originalAxis.z * targetAxis.z; // 确保点积在有效范围内 if (dot < -1.0f) dot = -1.0f; else if (dot > 1.0f) dot = 1.0f; angle = acos(dot) * 180.0f / PI; // 应用变换 glTranslatef(appState.q1.x, appState.q1.y, appState.q1.z); glRotatef(angle, axis.x, axis.y, axis.z); glScalef(scale, scale, scale); glTranslatef(-appState.originalP1.x, -appState.originalP1.y, -appState.originalP1.z); } } // 初始化OpenGL void initGL() { glClearColor(0.1f, 0.1f, 0.15f, 1.0f); // 深蓝色背景 glEnable(GL_DEPTH_TEST); // 启用深度测试 glShadeModel(GL_SMOOTH); // 平滑着色 glEnable(GL_NORMALIZE); // 自动归一化法线 // 设置光照 GLfloat lightAmbient[] = { 0.2f, 0.2f, 0.2f, 1.0f }; GLfloat lightDiffuse[] = { 0.8f, 0.8f, 0.8f, 1.0f }; GLfloat lightSpecular[] = { 1.0f, 1.0f, 1.0f, 1.0f }; GLfloat lightPosition[] = { 5.0f, 5.0f, 5.0f, 1.0f }; glLightfv(GL_LIGHT0, GL_AMBIENT, lightAmbient); glLightfv(GL_LIGHT0, GL_DIFFUSE, lightDiffuse); glLightfv(GL_LIGHT0, GL_SPECULAR, lightSpecular); glLightfv(GL_LIGHT0, GL_POSITION, lightPosition); glEnable(GL_LIGHT0); glEnable(GL_LIGHTING); // 设置材质 GLfloat matAmbient[] = { 0.7f, 0.7f, 0.7f, 1.0f }; GLfloat matDiffuse[] = { 0.8f, 0.8f, 0.8f, 1.0f }; GLfloat matSpecular[] = { 1.0f, 1.0f, 1.0f, 1.0f }; GLfloat matShininess[] = { 50.0f }; glMaterialfv(GL_FRONT, GL_AMBIENT, matAmbient); glMaterialfv(GL_FRONT, GL_DIFFUSE, matDiffuse); glMaterialfv(GL_FRONT, GL_SPECULAR, matSpecular); glMaterialfv(GL_FRONT, GL_SHININESS, matShininess); // 初始化纹理 initTexture(); } // 绘制坐标轴 void drawAxes() { glDisable(GL_LIGHTING); glLineWidth(2.0f); glBegin(GL_LINES); // X轴 (红色) glColor3f(1.0f, 0.0f, 0.0f); glVertex3f(0.0f, 0.0f, 0.0f); glVertex3f(1.5f, 0.0f, 0.0f); // Y轴 (绿色) glColor3f(0.0f, 1.0f, 0.0f); glVertex3f(0.0f, 0.0f, 0.0f); glVertex3f(0.0f, 1.5f, 0.0f); // Z轴 (蓝色) glColor3f(0.0f, 0.0f, 1.0f); glVertex3f(0.0f, 0.0f, 0.0f); glVertex3f(0.0f, 0.0f, 1.5f); glEnd(); glEnable(GL_LIGHTING); } // 绘制变换点 void drawTransformPoints() { if (!appState.showTransformPoints) return; glDisable(GL_LIGHTING); glPointSize(10.0f); // 绘制Q1点 (红色) glColor3f(1.0f, 0.0f, 0.0f); glBegin(GL_POINTS); glVertex3f(appState.q1.x, appState.q1.y, appState.q1.z); glEnd(); // 绘制Q2点 (蓝色) glColor3f(0.0f, 0.0f, 1.0f); glBegin(GL_POINTS); glVertex3f(appState.q2.x, appState.q2.y, appState.q2.z); glEnd(); // 绘制连接线 glLineWidth(2.0f); glBegin(GL_LINES); glColor3f(1.0f, 0.0f, 1.0f); glVertex3f(appState.q1.x, appState.q1.y, appState.q1.z); glVertex3f(appState.q2.x, appState.q2.y, appState.q2.z); glEnd(); glEnable(GL_LIGHTING); } // 显示帮助信息 void drawHelp() { if (!appState.showHelp) return; glDisable(GL_LIGHTING); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); gluOrtho2D(0, 800, 0, 600); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); glColor3f(1.0f, 1.0f, 0.8f); glRasterPos2f(10, 580); /*const char* helpText[] = { "球面网格查看器 - 帮助信息", "--------------------------------", "视图控制:", " 鼠标左键拖动: 旋转模型", " 鼠标右键拖动: 缩放模型", "", "渲染模式:", " V: 顶点模式", " E: 边模式", " F: 面模式", " R: 真实感模式", "", "变换控制:", " T: 切换变换模式(旋转/平移/缩放)", " S: 重置变换", " P: 显示/隐藏变换点", "", "其他:", " H: 显示/隐藏帮助", " L: 切换光源", " X: 切换纹理", " 1/2: 设置变换点", " 3: 重置变换点", " F1: 系统复位", " ESC: 退出程序", };*/ const char* helpText[] = { "Sphere Mesh Viewer - Help", //球面网格查看器 - 帮助信息 "--------------------------------", "View Controls:", //视图控制: " Left mouse drag: Rotate model", // 鼠标左键拖动: 旋转模型 " Right mouse drag: Zoom model", //鼠标右键拖动: 缩放模型 "", "Render Modes:", //渲染模式 " V: Vertex mode", //V: 顶点模式 " E: Edge mode", //E: 边模式 " F: Face mode", //F: 面模式 " R: Realistic mode", //R: 真实感模式 "", "Transform Controls:", //变换控制 " T: Toggle transform mode (Rotate/Translate/Scale)", //T: 切换变换模式(旋转/平移/缩放) " S: Reset transform", //S: 重置变换 " P: Show/Hide transform points", //P: 显示/隐藏变换点 "", "Other:", " H: Show/Hide help", // H: 显示/隐藏帮助 " L: Toggle lighting", // L: 切换光源 " X: Toggle texture", // X: 切换纹理 " 1/2: Set transform points", // 1/2: 设置变换点 " 3: Reset transform points", // 3: 重置变换点 " F1: System reset", //F1: 系统复位 " ESC: Quit program", // ESC: 退出程序 }; int numLines = sizeof(helpText) / sizeof(char*); for (int i = 0; i < numLines; i++) { const char* line = helpText[i]; while (*line) { glutBitmapCharacter(GLUT_BITMAP_9_BY_15, *line); line++; } glRasterPos2f(10, 580 - (i + 1) * 18); } glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glEnable(GL_LIGHTING); } // 显示状态信息 void drawStatus() { glDisable(GL_LIGHTING); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); gluOrtho2D(0, 800, 0, 600); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); glColor3f(0.8f, 0.8f, 1.0f); glRasterPos2f(10, 20); // 当前模式信息 char modeInfo[256]; const char* drawModes[] = { "Vertex", "Edge", "Face", "Realistic" }; // const char* drawModes[] = { "顶点", "边", "面", "真实感" }; const char* transformModes[] = { "Rotate", "Translate", "Scale" }; //const char* transformModes[] = { "旋转", "平移", "缩放" }; sprintf_s(modeInfo, sizeof(modeInfo), "Render Mode: %s | Transform Mode: %s | Lighting: %s | Texture: %s", //sprintf_s(modeInfo, sizeof(modeInfo), "渲染模式: %s | 变换模式: %s | 光源: %s | 纹理: %s", drawModes[appState.drawMode], transformModes[appState.transform.transformMode], glIsEnabled(GL_LIGHTING) ? "On" : "Off", // glIsEnabled(GL_LIGHTING) ? "开" : "关", appState.textureEnabled ? "On" : "Off"); //appState.textureEnabled ? "开" : "关"); const char* p = modeInfo; while (*p) { glutBitmapCharacter(GLUT_BITMAP_9_BY_15, *p); p++; } glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glEnable(GL_LIGHTING); } // 显示回调函数 void display() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); // 设置相机 gluLookAt(0.0, 0.0, 5.0, // 相机位置 0.0, 0.0, 0.0, // 观察点 0.0, 1.0, 0.0); // 上方向 // 应用交互变换 applyTransformations(); // 应用球面变换 applySphereTransformation(); // 绘制坐标轴 drawAxes(); // 绘制变换点 drawTransformPoints(); // 根据绘制模式渲染网格 if (appState.drawMode == 3) { // 真实感模式 if (appState.textureEnabled) { glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, appState.textureID); } else { glDisable(GL_TEXTURE_2D); } glBegin(GL_TRIANGLES); for (int i = 0; i < appState.mesh.numFaces; i++) { int v1 = appState.mesh.faces[i].v1; int v2 = appState.mesh.faces[i].v2; int v3 = appState.mesh.faces[i].v3; // 顶点1 glNormal3f(appState.mesh.normals[v1].x, appState.mesh.normals[v1].y, appState.mesh.normals[v1].z); glTexCoord2f(appState.mesh.texCoords[2 * v1], appState.mesh.texCoords[2 * v1 + 1]); glVertex3f(appState.mesh.vertices[v1].x, appState.mesh.vertices[v1].y, appState.mesh.vertices[v1].z); // 顶点2 glNormal3f(appState.mesh.normals[v2].x, appState.mesh.normals[v2].y, appState.mesh.normals[v2].z); glTexCoord2f(appState.mesh.texCoords[2 * v2], appState.mesh.texCoords[2 * v2 + 1]); glVertex3f(appState.mesh.vertices[v2].x, appState.mesh.vertices[v2].y, appState.mesh.vertices[v2].z); // 顶点3 glNormal3f(appState.mesh.normals[v3].x, appState.mesh.normals[v3].y, appState.mesh.normals[v3].z); glTexCoord2f(appState.mesh.texCoords[2 * v3], appState.mesh.texCoords[2 * v3 + 1]); glVertex3f(appState.mesh.vertices[v3].x, appState.mesh.vertices[v3].y, appState.mesh.vertices[v3].z); } glEnd(); glDisable(GL_TEXTURE_2D); } else { // 点、线、面模式 glDisable(GL_LIGHTING); if (appState.drawMode == 0) { // 点模式 glPointSize(3.0f); glColor3f(1.0f, 1.0f, 1.0f); glBegin(GL_POINTS); for (int i = 0; i < appState.mesh.numVertices; i++) { glVertex3f(appState.mesh.vertices[i].x, appState.mesh.vertices[i].y, appState.mesh.vertices[i].z); } glEnd(); } else if (appState.drawMode == 1) { // 边模式 glLineWidth(1.5f); glColor3f(0.4f, 0.6f, 1.0f); glBegin(GL_LINES); for (int i = 0; i < appState.mesh.numFaces; i++) { int v1 = appState.mesh.faces[i].v1; int v2 = appState.mesh.faces[i].v2; int v3 = appState.mesh.faces[i].v3; glVertex3f(appState.mesh.vertices[v1].x, appState.mesh.vertices[v1].y, appState.mesh.vertices[v1].z); glVertex3f(appState.mesh.vertices[v2].x, appState.mesh.vertices[v2].y, appState.mesh.vertices[v2].z); glVertex3f(appState.mesh.vertices[v2].x, appState.mesh.vertices[v2].y, appState.mesh.vertices[v2].z); glVertex3f(appState.mesh.vertices[v3].x, appState.mesh.vertices[v3].y, appState.mesh.vertices[v3].z); glVertex3f(appState.mesh.vertices[v3].x, appState.mesh.vertices[v3].y, appState.mesh.vertices[v3].z); glVertex3f(appState.mesh.vertices[v1].x, appState.mesh.vertices[v1].y, appState.mesh.vertices[v1].z); } glEnd(); } else if (appState.drawMode == 2) { // 面模式 glColor3f(0.2f, 0.8f, 0.3f); glBegin(GL_TRIANGLES); for (int i = 0; i < appState.mesh.numFaces; i++) { int v1 = appState.mesh.faces[i].v1; int v2 = appState.mesh.faces[i].v2; int v3 = appState.mesh.faces[i].v3; glVertex3f(appState.mesh.vertices[v1].x, appState.mesh.vertices[v1].y, appState.mesh.vertices[v1].z); glVertex3f(appState.mesh.vertices[v2].x, appState.mesh.vertices[v2].y, appState.mesh.vertices[v2].z); glVertex3f(appState.mesh.vertices[v3].x, appState.mesh.vertices[v3].y, appState.mesh.vertices[v3].z); } glEnd(); } glEnable(GL_LIGHTING); } // 绘制帮助信息 drawHelp(); // 绘制状态信息 drawStatus(); glutSwapBuffers(); } // 窗口大小调整回调 void reshape(int width, int height) { glViewport(0, 0, width, height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(45.0, (double)width / height, 0.1, 100.0); glMatrixMode(GL_MODELVIEW); } // 键盘回调 void keyboard(unsigned char key, int x, int y) { switch (key) { case &#39;v&#39;: case &#39;V&#39;: // 顶点模式 appState.drawMode = 0; break; case &#39;e&#39;: case &#39;E&#39;: // 边模式 appState.drawMode = 1; break; case &#39;f&#39;: case &#39;F&#39;: // 面模式 appState.drawMode = 2; break; case &#39;r&#39;: case &#39;R&#39;: // 真实感模式 appState.drawMode = 3; break; case &#39;t&#39;: case &#39;T&#39;: // 切换变换模式 appState.transform.transformMode = (appState.transform.transformMode + 1) % 3; break; case &#39;s&#39;: case &#39;S&#39;: // 重置变换 appState.transform.translation.x = 0.0f; appState.transform.translation.y = 0.0f; appState.transform.translation.z = 0.0f; appState.transform.rotation.x = 0.0f; appState.transform.rotation.y = 0.0f; appState.transform.rotation.z = 0.0f; appState.transform.scale = 1.0f; break; case &#39;h&#39;: case &#39;H&#39;: // 显示/隐藏帮助 appState.showHelp = !appState.showHelp; break; case &#39;p&#39;: case &#39;P&#39;: // 显示/隐藏变换点 appState.showTransformPoints = !appState.showTransformPoints; break; case &#39;l&#39;: case &#39;L&#39;: // 切换光源 if (glIsEnabled(GL_LIGHTING)) { glDisable(GL_LIGHTING); } else { glEnable(GL_LIGHTING); } break; case &#39;x&#39;: case &#39;X&#39;: // 切换纹理 appState.textureEnabled = !appState.textureEnabled; break; case &#39;1&#39;: // 设置Q1点 appState.q1.x = 0.0f; appState.q1.y = 1.5f; appState.q1.z = 0.0f; break; case &#39;2&#39;: // 设置Q2点 appState.q2.x = 0.0f; appState.q2.y = -1.5f; appState.q2.z = 0.0f; break; case &#39;3&#39;: // 重置变换点 appState.q1 = appState.originalP1; appState.q2 = appState.originalP2; break; case 27: // ESC键退出 exit(0); break; } glutPostRedisplay(); } // 特殊键盘回调(功能键) void specialKeys(int key, int x, int y) { switch (key) { case GLUT_KEY_F1: // 系统复位 appState.transform.translation.x = 0.0f; appState.transform.translation.y = 0.0f; appState.transform.translation.z = 0.0f; appState.transform.rotation.x = 0.0f; appState.transform.rotation.y = 0.0f; appState.transform.rotation.z = 0.0f; appState.transform.scale = 1.0f; appState.q1 = appState.originalP1; appState.q2 = appState.originalP2; break; case GLUT_KEY_UP: // 向上平移 if (appState.transform.transformMode == 1) { appState.transform.translation.y += 0.1f; } break; case GLUT_KEY_DOWN: // 向下平移 if (appState.transform.transformMode == 1) { appState.transform.translation.y -= 0.1f; } break; case GLUT_KEY_LEFT: // 向左平移 if (appState.transform.transformMode == 1) { appState.transform.translation.x -= 0.1f; } break; case GLUT_KEY_RIGHT: // 向右平移 if (appState.transform.transformMode == 1) { appState.transform.translation.x += 0.1f; } break; } glutPostRedisplay(); } // 鼠标按钮回调 void mouseButton(int button, int state, int x, int y) { if (button == GLUT_LEFT_BUTTON) { if (state == GLUT_DOWN) { appState.mouseLeftDown = 1; appState.lastMouseX = x; appState.lastMouseY = y; } else if (state == GLUT_UP) { appState.mouseLeftDown = 0; } } else if (button == GLUT_RIGHT_BUTTON) { if (state == GLUT_DOWN) { appState.mouseRightDown = 1; appState.lastMouseX = x; appState.lastMouseY = y; } else if (state == GLUT_UP) { appState.mouseRightDown = 0; } } } // 鼠标运动回调 void mouseMotion(int x, int y) { int dx = x - appState.lastMouseX; int dy = y - appState.lastMouseY; if (appState.mouseLeftDown) { if (appState.transform.transformMode == 0) { // 旋转模式 appState.transform.rotation.y += dx * 0.5f; appState.transform.rotation.x += dy * 0.5f; } else if (appState.transform.transformMode == 1) { // 平移模式 appState.transform.translation.x += dx * 0.01f; appState.transform.translation.y -= dy * 0.01f; } } else if (appState.mouseRightDown) { if (appState.transform.transformMode == 2) { // 缩放模式 appState.transform.scale += dy * 0.01f; if (appState.transform.scale < 0.1f) appState.transform.scale = 0.1f; } } appState.lastMouseX = x; appState.lastMouseY = y; glutPostRedisplay(); } // 初始化应用程序状态 void initAppState() { memset(&appState, 0, sizeof(AppState)); appState.drawMode = 3; // 默认真实感模式 appState.transform.scale = 1.0f; appState.showHelp = 1; // 初始显示帮助 appState.showTransformPoints = 1; // 初始显示变换点 appState.textureEnabled = 1; } // 主函数 int main(int argc, char** argv) { // 初始化GLUT glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); glutInitWindowSize(800, 600); glutCreateWindow("球面网格查看器 - 图形学与虚拟现实实验"); // 初始化应用程序状态 initAppState(); // 加载网格数据 ReadMesh("sphere30x20.msh"); // 设置回调函数 initGL(); glutDisplayFunc(display); glutReshapeFunc(reshape); glutKeyboardFunc(keyboard); glutSpecialFunc(specialKeys); glutMouseFunc(mouseButton); glutMotionFunc(mouseMotion); // 主循环 glutMainLoop(); // 清理 free(appState.mesh.vertices); free(appState.mesh.faces); free(appState.mesh.normals); free(appState.mesh.texCoords); return 0; }这段代码能改成仅仅试用glad glfw的库吗
06-22
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值