[{"data":1,"prerenderedAt":7547},["ShallowReactive",2],{"page-\u002Fcpp\u002Fopengel\u002F摄像机":3},{"id":4,"title":5,"body":6,"description":19,"extension":7541,"meta":7542,"navigation":542,"path":7543,"seo":7544,"stem":7545,"__hash__":7546},"content\u002Fcpp\u002Fopengel\u002F摄像机.md","摄像机",{"type":7,"value":8,"toc":7507},"minimark",[9,20,24,29,49,55,59,66,80,91,94,104,120,132,135,146,161,164,175,184,202,206,221,227,237,247,254,281,287,290,296,326,330,333,353,358,367,370,377,455,469,485,488,2447,2450,2461,2472,2475,3896,3900,3904,3908,3942,3946,4025,4029,4036,4067,4073,4093,4100,4114,4118,4122,4137,4157,4161,4167,4238,4244,4301,4305,4337,4341,4367,4371,4385,4388,5777,7503],[10,11,16],"pre",{"className":12,"code":14,"language":15},[13],"language-text","前面的教程中我们讨论了观察矩阵以及如何使用观察矩阵移动场景（我们向后移动了一点）。OpenGL本身没有**摄像机**(Camera)的概念，但我们可以通过把场景中的所有物体往相反方向移动的方式来模拟出摄像机，产生一种**我们**在移动的感觉，而不是场景在移动。\n","text",[17,18,14],"code",{"__ignoreMap":19},"",[21,22,23],"p",{},"​\t本节我们将会讨论如何在OpenGL中配置一个摄像机，并且将会讨论FPS风格的摄像机，让你能够在3D场景中自由移动。我们也会讨论键盘和鼠标输入，最终完成一个自定义的摄像机类。",[25,26,28],"h2",{"id":27},"摄像机观察空间","摄像机\u002F观察空间",[21,30,31,32,36,37,40,41,44,45,48],{},"​\t当我们",[33,34,35],"strong",{},"讨论摄像机\u002F观察空间(Camera\u002FView Space)的时候","，是在讨论",[33,38,39],{},"以摄像机的视角作为场景原点时场景中所有的顶点坐标：观察矩阵把所有的世界坐标变换为相对于摄像机位置与方向的观察坐标","。要定义一个摄像机，我们需要",[33,42,43],{},"它在世界空间中的位置、观察的方向、一个指向它右侧的向量以及一个指向它上方的向量","。细心的读者可能已经注意到我们实际上",[33,46,47],{},"创建了一个三个单位轴相互垂直的、以摄像机的位置为原点的坐标系","。",[21,50,51],{},[52,53],"img",{"alt":52,"src":54},".\u002Fassets\u002Fcamera_axes.png",[56,57,58],"h3",{"id":58},"摄像机位置",[21,60,61,62,65],{},"​\t获取摄像机位置很简单。",[33,63,64],{},"摄像机位置简单来说就是世界空间中一个指向摄像机位置的向量","。我们把摄像机位置设置为上一节中的那个相同的位置：",[10,67,71],{"className":68,"code":69,"language":70,"meta":19,"style":19},"language-c++ shiki shiki-themes github-light github-dark","glm::vec3 cameraPos = glm::vec3(0.0f, 0.0f, 3.0f);\n","c++",[17,72,73],{"__ignoreMap":19},[74,75,78],"span",{"class":76,"line":77},"line",1,[74,79,69],{},[81,82,83,88],"blockquote",{},[21,84,85],{},[74,86,87],{},"!NOTE",[21,89,90],{},"不要忘记正z轴是从屏幕指向你的，如果我们希望摄像机向后移动，我们就沿着z轴的正方向移动。",[56,92,93],{"id":93},"摄像机方向",[21,95,96,97,100,101],{},"​\t下一个需要的向量是摄像机的方向，",[33,98,99],{},"这里指的是摄像机指向哪个方向。现在我们让摄像机指向场景原点：(0, 0, 0)。还记得如果将两个矢量相减","，我们就能得到这两个矢量的差吗？用场景原点向量减去摄像机位置向量的结果就是摄像机的指向向量。由于我们知道摄像机指向z轴负方向，但我们希望方向向量(Direction Vector)指向摄像机的z轴正方向。",[33,102,103],{},"如果我们交换相减的顺序，我们就会获得一个指向摄像机正z轴方向的向量：",[10,105,107],{"className":68,"code":106,"language":70,"meta":19,"style":19},"glm::vec3 cameraTarget = glm::vec3(0.0f, 0.0f, 0.0f);\nglm::vec3 cameraDirection = glm::normalize(cameraPos - cameraTarget);\n",[17,108,109,114],{"__ignoreMap":19},[74,110,111],{"class":76,"line":77},[74,112,113],{},"glm::vec3 cameraTarget = glm::vec3(0.0f, 0.0f, 0.0f);\n",[74,115,117],{"class":76,"line":116},2,[74,118,119],{},"glm::vec3 cameraDirection = glm::normalize(cameraPos - cameraTarget);\n",[81,121,122,126],{},[21,123,124],{},[74,125,87],{},[21,127,128,131],{},[33,129,130],{},"方向","向量(Direction Vector)并不是最好的名字，因为它实际上指向从它到目标向量的相反方向（译注：注意看前面的那个图，蓝色的方向向量大概指向z轴的正方向，与摄像机实际指向的方向是正好相反的）。",[56,133,134],{"id":134},"右轴",[21,136,137,138,141,142,145],{},"​\t我们需要的另一个向量是一个",[33,139,140],{},"右向量","(Right Vector)，它代表摄像机空间的x轴的正方向。为获取右向量我们需要先使用一个小技巧：先定义一个",[33,143,144],{},"上向量","(Up Vector)。接下来把上向量和第二步得到的方向向量进行叉乘。两个向量叉乘的结果会同时垂直于两向量，因此我们会得到指向x轴正方向的那个向量（如果我们交换两个向量叉乘的顺序就会得到相反的指向x轴负方向的向量）：",[10,147,149],{"className":68,"code":148,"language":70,"meta":19,"style":19},"glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f); \nglm::vec3 cameraRight = glm::normalize(glm::cross(up, cameraDirection));\n",[17,150,151,156],{"__ignoreMap":19},[74,152,153],{"class":76,"line":77},[74,154,155],{},"glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f); \n",[74,157,158],{"class":76,"line":116},[74,159,160],{},"glm::vec3 cameraRight = glm::normalize(glm::cross(up, cameraDirection));\n",[56,162,163],{"id":163},"上轴",[21,165,166,167,170,171,174],{},"​\t现在我们已经",[33,168,169],{},"有了x轴向量和z轴向量","，获取",[33,172,173],{},"一个指向摄像机的正y轴向量就相对简单了","：我们把右向量和方向向量进行叉乘：",[10,176,178],{"className":68,"code":177,"language":70,"meta":19,"style":19},"glm::vec3 cameraUp = glm::cross(cameraDirection, cameraRight);\n",[17,179,180],{"__ignoreMap":19},[74,181,182],{"class":76,"line":77},[74,183,177],{},[21,185,186,187,190,191,198,199,48],{},"​\t",[33,188,189],{},"在叉乘和一些小技巧的帮助下，我们创建了所有构成观察\u002F摄像机空间的向量","。对于想学到更多数学原理的读者，提示一下，在线性代数中这个处理叫做",[192,193,197],"a",{"href":194,"rel":195},"http:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FGram%E2%80%93Schmidt_process",[196],"nofollow","格拉姆—施密特正交化","(Gram-Schmidt Process)。",[33,200,201],{},"使用这些摄像机向量我们就可以创建一个LookAt矩阵了，它在创建摄像机的时候非常有用",[25,203,205],{"id":204},"look-at","Look At",[21,207,208,209,212,213,216,217,220],{},"​\t使用矩阵的好处之一是",[33,210,211],{},"如果你使用3个相互垂直（或非线性）的轴定义了一个坐标空间","，你可以用",[33,214,215],{},"这3个轴外加一个平移向量来创建一个矩阵","，并且你可以用这个矩阵乘以任何向量来将其变换到那个坐标空间。这正是",[33,218,219],{},"LookAt","矩阵所做的，现在我们有了3个相互垂直的轴和一个定义摄像机空间的位置坐标，我们可以创建我们自己的LookAt矩阵了：",[21,222,223],{},[52,224],{"alt":225,"src":226},"image-20251010231546573",".\u002Fassets\u002Fimage-20251010231546573.png",[21,228,229,230,233,234,48],{},"​\t其中",[33,231,232],{},"R是右向量，U是上向量，D是方向向量P是摄像机位置向量","。注意，位置向量是相反的，因为我们最终希望把世界平移到与我们自身移动的相反方向。把这个LookAt矩阵作为观察矩阵可以很高效地把所有世界坐标变换到刚刚定义的观察空间。",[33,235,236],{},"LookAt矩阵就像它的名字表达的那样：它会创建一个看着(Look at)给定目标的观察矩阵",[81,238,239,244],{},[21,240,241],{},[74,242,243],{},"!WARNING",[21,245,246],{},"注意，这里有个错误，应该是 Ux那一行是 相机正在注视的世界坐标系点，应该是0 0 0 。然后Dx那一行才是上向量，用于告诉opengel屏幕的垂直方向对应世界Y轴。",[21,248,249,250,253],{},"​\t幸运的是，GLM已经提供了这些支持。",[33,251,252],{},"我们要做的只是定义一个摄像机位置，一个目标位置和一个表示世界空间中的上向量的向量（我们计算右向量使用的那个上向量）","。接着GLM就会创建一个LookAt矩阵，我们可以把它当作我们的观察矩阵：",[10,255,257],{"className":68,"code":256,"language":70,"meta":19,"style":19},"glm::mat4 view;\nview = glm::lookAt(glm::vec3(0.0f, 0.0f, 3.0f), \n           glm::vec3(0.0f, 0.0f, 0.0f), \n           glm::vec3(0.0f, 1.0f, 0.0f));\n",[17,258,259,264,269,275],{"__ignoreMap":19},[74,260,261],{"class":76,"line":77},[74,262,263],{},"glm::mat4 view;\n",[74,265,266],{"class":76,"line":116},[74,267,268],{},"view = glm::lookAt(glm::vec3(0.0f, 0.0f, 3.0f), \n",[74,270,272],{"class":76,"line":271},3,[74,273,274],{},"           glm::vec3(0.0f, 0.0f, 0.0f), \n",[74,276,278],{"class":76,"line":277},4,[74,279,280],{},"           glm::vec3(0.0f, 1.0f, 0.0f));\n",[21,282,283,284,48],{},"glm::LookAt函数需要一个",[33,285,286],{},"位置、目标和上向量。它会创建一个和在上一节使用的一样的观察矩阵",[21,288,289],{},"​\t在讨论用户输入之前，我们先来做些有意思的事，把我们的摄像机在场景中旋转。我们会将摄像机的注视点保持在(0, 0, 0)。",[21,291,292,293,48],{},"​\t我们需要用到一点三角学的知识来在每一帧创建一个x和z坐标，它会代表圆上的一点，我们将会使用它作为摄像机的位置。通过重新计算x和y坐标，我们会遍历圆上的所有点，这样摄像机就会绕着场景旋转了。我们预先定义这个圆的半径radius，",[33,294,295],{},"在每次渲染迭代中使用GLFW的glfwGetTime函数重新创建观察矩阵，来扩大这个圆",[10,297,299],{"className":68,"code":298,"language":70,"meta":19,"style":19},"float radius = 10.0f;\nfloat camX = sin(glfwGetTime()) * radius;\nfloat camZ = cos(glfwGetTime()) * radius;\nglm::mat4 view;\nview = glm::lookAt(glm::vec3(camX, 0.0, camZ), glm::vec3(0.0, 0.0, 0.0), glm::vec3(0.0, 1.0, 0.0)); \n",[17,300,301,306,311,316,320],{"__ignoreMap":19},[74,302,303],{"class":76,"line":77},[74,304,305],{},"float radius = 10.0f;\n",[74,307,308],{"class":76,"line":116},[74,309,310],{},"float camX = sin(glfwGetTime()) * radius;\n",[74,312,313],{"class":76,"line":271},[74,314,315],{},"float camZ = cos(glfwGetTime()) * radius;\n",[74,317,318],{"class":76,"line":277},[74,319,263],{},[74,321,323],{"class":76,"line":322},5,[74,324,325],{},"view = glm::lookAt(glm::vec3(camX, 0.0, camZ), glm::vec3(0.0, 0.0, 0.0), glm::vec3(0.0, 1.0, 0.0));\n",[327,328,329],"h1",{"id":329},"自由移动",[21,331,332],{},"​\t让摄像机绕着场景转的确很有趣，但是让我们自己移动摄像机会更有趣！首先我们必须设置一个摄像机系统，所以在我们的程序前面定义一些摄像机变量很有用：",[10,334,336],{"className":68,"code":335,"language":70,"meta":19,"style":19},"glm::vec3 cameraPos   = glm::vec3(0.0f, 0.0f,  3.0f);\nglm::vec3 cameraFront = glm::vec3(0.0f, 0.0f, -1.0f);\nglm::vec3 cameraUp    = glm::vec3(0.0f, 1.0f,  0.0f);\n",[17,337,338,343,348],{"__ignoreMap":19},[74,339,340],{"class":76,"line":77},[74,341,342],{},"glm::vec3 cameraPos   = glm::vec3(0.0f, 0.0f,  3.0f);\n",[74,344,345],{"class":76,"line":116},[74,346,347],{},"glm::vec3 cameraFront = glm::vec3(0.0f, 0.0f, -1.0f);\n",[74,349,350],{"class":76,"line":271},[74,351,352],{},"glm::vec3 cameraUp    = glm::vec3(0.0f, 1.0f,  0.0f);\n",[21,354,186,355,357],{},[17,356,219],{},"函数现在成了：",[10,359,361],{"className":68,"code":360,"language":70,"meta":19,"style":19},"view = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp);\n",[17,362,363],{"__ignoreMap":19},[74,364,365],{"class":76,"line":77},[74,366,360],{},[21,368,369],{},"​\t我们首先将摄像机位置设置为之前定义的cameraPos。方向是当前的位置加上我们刚刚定义的方向向量。这样能保证无论我们怎么移动，摄像机都会注视着目标方向。让我们摆弄一下这些向量，在按下某些按钮时更新cameraPos向量。",[21,371,372,373,376],{},"​\t我们已经为GLFW的键盘输入定义过一个processInput函数了，我们来新",[33,374,375],{},"添加几个需要检查的按键命令","：",[10,378,380],{"className":68,"code":379,"language":70,"meta":19,"style":19},"void processInput(GLFWwindow *window)\n{\n    ...\n    float cameraSpeed = 0.05f; \u002F\u002F adjust accordingly\n    if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)\n        cameraPos += cameraSpeed * cameraFront;\n    if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)\n        cameraPos -= cameraSpeed * cameraFront;\n    if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)\n        cameraPos -= glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;\n    if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)\n        cameraPos += glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;\n}\n",[17,381,382,387,392,397,402,407,413,419,425,431,437,443,449],{"__ignoreMap":19},[74,383,384],{"class":76,"line":77},[74,385,386],{},"void processInput(GLFWwindow *window)\n",[74,388,389],{"class":76,"line":116},[74,390,391],{},"{\n",[74,393,394],{"class":76,"line":271},[74,395,396],{},"    ...\n",[74,398,399],{"class":76,"line":277},[74,400,401],{},"    float cameraSpeed = 0.05f; \u002F\u002F adjust accordingly\n",[74,403,404],{"class":76,"line":322},[74,405,406],{},"    if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)\n",[74,408,410],{"class":76,"line":409},6,[74,411,412],{},"        cameraPos += cameraSpeed * cameraFront;\n",[74,414,416],{"class":76,"line":415},7,[74,417,418],{},"    if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)\n",[74,420,422],{"class":76,"line":421},8,[74,423,424],{},"        cameraPos -= cameraSpeed * cameraFront;\n",[74,426,428],{"class":76,"line":427},9,[74,429,430],{},"    if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)\n",[74,432,434],{"class":76,"line":433},10,[74,435,436],{},"        cameraPos -= glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;\n",[74,438,440],{"class":76,"line":439},11,[74,441,442],{},"    if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)\n",[74,444,446],{"class":76,"line":445},12,[74,447,448],{},"        cameraPos += glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;\n",[74,450,452],{"class":76,"line":451},13,[74,453,454],{},"}\n",[21,456,457,458,461,462,465,466,468],{},"​\t当我们按下",[33,459,460],{},"WASD","键的任意一个，摄像机的位置都会相应更新。",[33,463,464],{},"如果我们希望向前或向后移动，我们就把位置向量加上或减去方向向量","。如果我们希望向左右移动，我们使用叉乘来创建一个",[33,467,140],{},"(Right Vector)，并沿着它相应移动就可以了。这样就创建了使用摄像机时熟悉的横移(Strafe)效果。",[81,470,471,475],{},[21,472,473],{},[74,474,87],{},[21,476,477,478,480,481,484],{},"注意，我们对",[33,479,140],{},"进行了标准化。如果我们没对这个向量进行标准化，最后的叉乘结果会根据cameraFront变量返回大小不同的向量。如果我们不对向量进行标准化，",[33,482,483],{},"我们就得根据摄像机的朝向不同加速或减速移动了","，但如果进行了标准化移动就是匀速的。",[21,486,487],{},"​\t现在你就应该能够移动摄像机了，虽然移动速度和系统有关，你可能会需要调整一下cameraSpeed。",[10,489,491],{"className":68,"code":490,"language":70,"meta":19,"style":19},"#include \u003Cglad\u002Fglad.h>\n#include \u003CGLFW\u002Fglfw3.h>\n#include \u003Copencv4\u002Fopencv2\u002Fopencv.hpp>\n#include \u003Ciostream>\n#include \u003Ccmath>\n#include \u003Cfilesystem>\n#include \"utils\u002Fshader.h\"\n#include \"utils\u002FglmCompute.h\"\n#include \"utils\u002Flogger.h\"\n\nusing namespace cv;\nusing namespace glm;\n\n\u002F\u002F 窗口大小改变时的回调函数\nvoid framebuffer_size_callback(GLFWwindow* window, int width, int height) {\n    glViewport(0, 0, width, height);\n}\n\nstd::string getCurrentWorkingDirectory() {\n    namespace fs = std::filesystem;\n    try {\n        \u002F\u002F current_path() 返回当前工作目录\n        return fs::current_path().string();\n    } catch (const fs::filesystem_error& e) {\n        std::cerr \u003C\u003C \"Filesystem Error: \" \u003C\u003C e.what() \u003C\u003C std::endl;\n        return \"\";\n    }\n}\n\n\u002F\u002F 相机的位置和方向\nvec3 cameraPos = vec3(0.0f, 0.0f, 3.0f);\nvec3 cameraFront = vec3(0.0f, 0.0f, -1.0f); \u002F\u002F 相机朝向\nvec3 cameraUp = vec3(0.0f, 1.0f, 0.0f); \u002F\u002F 上向量\n\n\u002F\u002F 鼠标控制变量\nfloat yawAngle = -90.0f;   \u002F\u002F 偏航角\nfloat pitchAngle = 0.0f;   \u002F\u002F 俯仰角\nfloat lastX = 400.0f; \u002F\u002F 鼠标上次X位置\nfloat lastY = 300.0f; \u002F\u002F 鼠标上次Y位置\nbool firstMouse = true; \u002F\u002F 是否是第一次鼠标移动\nbool mouseCaptured = true; \u002F\u002F 鼠标是否被捕获\n\n\u002F\u002F 鼠标回调函数\nvoid mouse_callback(GLFWwindow* window, double xpos, double ypos) {\n    \u002F\u002F 只有在鼠标被捕获时才处理鼠标移动\n    if (!mouseCaptured) return;\n    \n    if (firstMouse) {\n        lastX = xpos;\n        lastY = ypos;\n        firstMouse = false;\n    }\n\n    float xoffset = xpos - lastX;\n    float yoffset = lastY - ypos; \u002F\u002F 注意这里是相反的，因为y坐标是从底部往顶部递增的\n    lastX = xpos;\n    lastY = ypos;\n\n    float sensitivity = 0.1f;\n    xoffset *= sensitivity;\n    yoffset *= sensitivity;\n\n    yawAngle += xoffset;\n    pitchAngle += yoffset;\n\n    \u002F\u002F 限制俯仰角，避免翻转\n    if (pitchAngle > 89.0f)\n        pitchAngle = 89.0f;\n    if (pitchAngle \u003C -89.0f)\n        pitchAngle = -89.0f;\n\n    \u002F\u002F 计算新的相机方向\n    vec3 front;\n    front.x = cos(radians(yawAngle)) * cos(radians(pitchAngle));\n    front.y = sin(radians(pitchAngle));\n    front.z = sin(radians(yawAngle)) * cos(radians(pitchAngle));\n    cameraFront = normalize(front);\n}\n\nvoid processInput(GLFWwindow *window){\n    \u002F\u002F 检查 Ctrl+Alt 组合键来切换鼠标捕获状态\n    bool ctrlPressed = glfwGetKey(window, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS || \n                      glfwGetKey(window, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS;\n    bool altPressed = glfwGetKey(window, GLFW_KEY_LEFT_ALT) == GLFW_PRESS || \n                      glfwGetKey(window, GLFW_KEY_RIGHT_ALT) == GLFW_PRESS;\n    \n    if (ctrlPressed && altPressed) {\n        if (mouseCaptured) {\n            \u002F\u002F 释放鼠标捕获\n            glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);\n            mouseCaptured = false;\n            firstMouse = true; \u002F\u002F 重置鼠标状态\n            std::cout \u003C\u003C \"鼠标捕获已释放 - 按 Ctrl+Alt 重新捕获\" \u003C\u003C std::endl;\n        } else {\n            \u002F\u002F 重新捕获鼠标\n            glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);\n            mouseCaptured = true;\n            firstMouse = true; \u002F\u002F 重置鼠标状态\n            std::cout \u003C\u003C \"鼠标捕获已启用\" \u003C\u003C std::endl;\n        }\n    }\n    \n    \u002F\u002F 只有在鼠标被捕获时才处理WASD移动\n    if (!mouseCaptured) return;\n    \n    float cameraSpeed = 0.05f;\n    if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)\n        cameraPos += cameraSpeed * cameraFront;\n    if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)\n        cameraPos -= cameraSpeed * cameraFront;\n    if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)\n        cameraPos -= normalize(cross(cameraFront, cameraUp)) * cameraSpeed;\n    if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)\n        cameraPos += normalize(cross(cameraFront, cameraUp)) * cameraSpeed;\n}\n\nint main() {\n\n    \u002F\u002F 模型矩阵\n    mat4 model = mat4(1.0f);\n    model = rotate(model, radians(-55.0f), vec3(1.0f, 0.0f, 0.0f));\n\n    \u002F\u002F lookat矩阵\n    mat4 view = mat4(1.0f);\n    view = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp);\n\n    \u002F\u002F 投影矩阵\n    mat4 projection = perspective(radians(45.0f), (float)800 \u002F (float)600, 0.1f, 100.0f);\n\n    \u002F\u002F 立方体数组\n    vec3 cubePositions[] = {\n        vec3(0.0f,  0.0f,  0.0f),\n        vec3(2.0f,  5.0f, -15.0f),\n        vec3(-1.5f, -2.2f, -2.5f),\n        vec3(-3.8f, -2.0f, -12.3f),\n        vec3(2.4f, -0.4f, -3.5f),\n    };\n\n    auto path = getCurrentWorkingDirectory().append(\"\u002FAssets\u002Fimage.png\");\n    auto path2 = getCurrentWorkingDirectory().append(\"\u002FAssets\u002Fimage2.png\");\n    cv::Mat image = cv::imread(path);\n    cv::Mat image2 = cv::imread(path2);\n    cv::flip(image2, image2, 0);\n    cv::cvtColor(image, image, cv::COLOR_BGR2RGB);\n    cv::cvtColor(image2, image2, cv::COLOR_BGR2RGB);\n\n    \u002F\u002F 转换为字节数组\n    std::vector\u003Cuchar> imageData;\n    std::vector\u003Cuchar> imageData2;\n    if (image.empty()) {\n        std::cerr \u003C\u003C \"Could not read the image: \" \u003C\u003C path \u003C\u003C std::endl;\n        return 1;\n    }\n    if (image2.empty()) {\n        std::cerr \u003C\u003C \"Could not read the image2: \" \u003C\u003C path2 \u003C\u003C std::endl;\n        return 1;\n    }\n    \n    std::cout \u003C\u003C \"Image 1 loaded: \" \u003C\u003C image.cols \u003C\u003C \"x\" \u003C\u003C image.rows \u003C\u003C std::endl;\n    std::cout \u003C\u003C \"Image 2 loaded: \" \u003C\u003C image2.cols \u003C\u003C \"x\" \u003C\u003C image2.rows \u003C\u003C std::endl;\n\n    cv::imencode(\".png\", image, imageData);\n    cv::imencode(\".png\", image2, imageData2);\n\n\n    \n    \u002F\u002F 初始化GLFW\n    glfwInit();\n    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);\n    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);\n    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);\n\n    \u002F\u002F 创建窗口\n    GLFWwindow* window = glfwCreateWindow(800, 600, \"OpenGL Window\", NULL, NULL);\n    if (window == NULL) {\n        std::cout \u003C\u003C \"Failed to create GLFW window\" \u003C\u003C std::endl;\n        glfwTerminate();\n        return -1;\n    }\n    \n    glfwMakeContextCurrent(window);\n    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);\n    glfwSetCursorPosCallback(window, mouse_callback);\n    \n    \u002F\u002F 隐藏鼠标光标并捕获鼠标\n    glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);\n\n    \u002F\u002F 初始化GLAD\n    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {\n        std::cout \u003C\u003C \"Failed to initialize GLAD\" \u003C\u003C std::endl;\n        return -1;\n    }\n\n    \u002F\u002F 启用深度测试\n    glEnable(GL_DEPTH_TEST);\n\n    float vertices[] = {\n    \u002F\u002F 位置(3) + 纹理坐标(2)\n    -0.5f, -0.5f, -0.5f,  0.0f, 0.0f,\n     0.5f, -0.5f, -0.5f,  1.0f, 0.0f,\n     0.5f,  0.5f, -0.5f,  1.0f, 1.0f,\n     0.5f,  0.5f, -0.5f,  1.0f, 1.0f,\n    -0.5f,  0.5f, -0.5f,  0.0f, 1.0f,\n    -0.5f, -0.5f, -0.5f,  0.0f, 0.0f,\n\n    -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,\n     0.5f, -0.5f,  0.5f,  1.0f, 0.0f,\n     0.5f,  0.5f,  0.5f,  1.0f, 1.0f,\n     0.5f,  0.5f,  0.5f,  1.0f, 1.0f,\n    -0.5f,  0.5f,  0.5f,  0.0f, 1.0f,\n    -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,\n\n    -0.5f,  0.5f,  0.5f,  1.0f, 0.0f,\n    -0.5f,  0.5f, -0.5f,  1.0f, 1.0f,\n    -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,\n    -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,\n    -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,\n    -0.5f,  0.5f,  0.5f,  1.0f, 0.0f,\n\n     0.5f,  0.5f,  0.5f,  1.0f, 0.0f,\n     0.5f,  0.5f, -0.5f,  1.0f, 1.0f,\n     0.5f, -0.5f, -0.5f,  0.0f, 1.0f,\n     0.5f, -0.5f, -0.5f,  0.0f, 1.0f,\n     0.5f, -0.5f,  0.5f,  0.0f, 0.0f,\n     0.5f,  0.5f,  0.5f,  1.0f, 0.0f,\n\n    -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,\n     0.5f, -0.5f, -0.5f,  1.0f, 1.0f,\n     0.5f, -0.5f,  0.5f,  1.0f, 0.0f,\n     0.5f, -0.5f,  0.5f,  1.0f, 0.0f,\n    -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,\n    -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,\n\n    -0.5f,  0.5f, -0.5f,  0.0f, 1.0f,\n     0.5f,  0.5f, -0.5f,  1.0f, 1.0f,\n     0.5f,  0.5f,  0.5f,  1.0f, 0.0f,\n     0.5f,  0.5f,  0.5f,  1.0f, 0.0f,\n    -0.5f,  0.5f,  0.5f,  0.0f, 0.0f,\n    -0.5f,  0.5f, -0.5f,  0.0f, 1.0f\n};\n    \n    \u002F\u002F indices数组未使用，已删除\n\n    \u002F\u002F 纹理\n    unsigned int texture;\n    unsigned int texture2;\n    glGenTextures(1, &texture); \u002F\u002F 生成纹理\n    glGenTextures(1, &texture2); \u002F\u002F 生成纹理\n    \n    \u002F\u002F 配置第一个纹理\n    glActiveTexture(GL_TEXTURE0);\n    glBindTexture(GL_TEXTURE_2D, texture); \u002F\u002F 绑定纹理\n    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); \u002F\u002F 设置纹理环绕方式\n    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); \u002F\u002F 设置纹理环绕方式\n    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); \u002F\u002F 设置纹理过滤方式\n    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); \u002F\u002F 设置纹理过滤方式\n    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, image.cols, image.rows, 0, GL_RGB, GL_UNSIGNED_BYTE, image.data); \u002F\u002F 设置纹理数据\n    glGenerateMipmap(GL_TEXTURE_2D); \u002F\u002F 生成mipmap\n\n    \u002F\u002F 配置第二个纹理\n    glActiveTexture(GL_TEXTURE1);\n    glBindTexture(GL_TEXTURE_2D, texture2); \u002F\u002F 绑定纹理\n    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); \u002F\u002F 设置纹理环绕方式\n    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); \u002F\u002F 设置纹理环绕方式   \n    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); \u002F\u002F 设置纹理过滤方式\n    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); \u002F\u002F 设置纹理过滤方式\n    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, image2.cols, image2.rows, 0, GL_RGB, GL_UNSIGNED_BYTE, image2.data); \u002F\u002F 设置纹理数据\n    glGenerateMipmap(GL_TEXTURE_2D); \u002F\u002F 生成mipmap\n\n\n    \u002F\u002F 创建VAO\n    unsigned int VAO;\n    glGenVertexArrays(1, &VAO);\n    glBindVertexArray(VAO); \u002F\u002F 绑定VAO\n\n    \u002F\u002F 创建VBO\n    unsigned int VBO;\n    glGenBuffers(1, &VBO);\n    glBindBuffer(GL_ARRAY_BUFFER, VBO); \u002F\u002F 绑定VBO\n    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);\n\n    int nrAttributes;\n    glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &nrAttributes);\n    std::cout \u003C\u003C \"Maximum nr of vertex attributes supported: \" \u003C\u003C nrAttributes \u003C\u003C std::endl;\n\n    \u002F\u002F 位置属性\n    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);\n    glEnableVertexAttribArray(0); \u002F\u002F 启用顶点属性\n    \n    \u002F\u002F 纹理属性\n    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));\n    glEnableVertexAttribArray(2);\n\n    \u002F\u002F 构建并编译着色器程序\n    Shader ourShader(\"shader\u002Fcustom.vert\", \"shader\u002Ffragment.vert\");\n\n    \u002F\u002F 渲染循环\n    while (!glfwWindowShouldClose(window)) {\n        \u002F\u002F 处理输入\n        processInput(window);\n        glfwPollEvents();\n\n        \u002F\u002F 更新view矩阵\n        view = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp);\n        \n        \u002F\u002F 渲染指令\n        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);\n        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);\n        \n        \u002F\u002F 使用着色器程序\n        ourShader.use();\n\n        \u002F\u002F 设置矩阵uniform\n        ourShader.setMat4(\"model\", model);\n        ourShader.setMat4(\"view\", view);\n        ourShader.setMat4(\"projection\", projection);\n        \n        ourShader.setInt(\"ourTexture\", 0);\n        ourShader.setInt(\"ourTexture2\", 1);\n        \n        glBindVertexArray(VAO);\n        \n        \u002F\u002F 绘制多个立方体\n        for(unsigned int i = 0; i \u003C 10; i++) {\n            glm::mat4 modelMatrix = glm::mat4(1.0f);\n            modelMatrix = glm::translate(modelMatrix, cubePositions[i]);\n            float angle = 20.0f * i; \n            modelMatrix = glm::rotate(modelMatrix, glm::radians(angle), glm::vec3(1.0f, 0.3f, 0.5f));\n            ourShader.setMat4(\"model\", modelMatrix);\n\n            glDrawArrays(GL_TRIANGLES, 0, 36);\n        }\n        \n        \u002F\u002F 交换缓冲区和检查IO事件\n        glfwSwapBuffers(window);\n    }\n\n    \u002F\u002F 清理资源\n    glDeleteVertexArrays(1, &VAO);\n    glDeleteBuffers(1, &VBO);\n    glDeleteTextures(1, &texture);\n    glDeleteTextures(1, &texture2);\n    glfwTerminate();\n    return 0;\n}\n\n",[17,492,493,498,503,508,513,518,523,528,533,538,544,549,554,558,564,570,576,581,586,592,598,604,610,616,622,628,634,640,645,650,656,662,668,674,679,685,691,697,703,709,715,721,726,732,738,744,750,756,762,768,774,780,785,790,796,802,808,814,819,825,831,837,842,848,854,859,865,871,877,883,889,894,900,906,912,918,924,930,935,940,946,952,958,964,970,976,981,987,993,999,1005,1011,1017,1023,1029,1035,1041,1047,1052,1058,1064,1069,1074,1080,1085,1090,1096,1101,1106,1111,1116,1121,1127,1132,1138,1143,1148,1154,1159,1165,1171,1177,1182,1188,1194,1200,1205,1211,1217,1222,1228,1234,1240,1246,1252,1258,1264,1270,1275,1281,1287,1293,1299,1305,1311,1317,1322,1328,1334,1340,1346,1352,1358,1363,1369,1375,1380,1385,1390,1396,1402,1407,1413,1419,1424,1429,1434,1440,1446,1452,1458,1464,1469,1475,1481,1487,1493,1499,1505,1510,1515,1521,1527,1533,1538,1544,1550,1555,1561,1567,1573,1578,1583,1588,1594,1600,1605,1611,1617,1623,1629,1635,1640,1646,1651,1656,1662,1668,1674,1679,1685,1690,1695,1701,1707,1713,1718,1723,1728,1733,1739,1744,1750,1755,1761,1766,1771,1776,1782,1787,1792,1797,1802,1807,1812,1817,1822,1827,1833,1839,1845,1850,1856,1861,1867,1873,1879,1885,1891,1896,1902,1908,1914,1920,1926,1932,1938,1944,1950,1955,1961,1967,1973,1978,1984,1989,1994,2000,2005,2010,2015,2021,2027,2033,2039,2044,2050,2056,2062,2068,2074,2079,2085,2091,2097,2102,2108,2114,2120,2125,2131,2137,2143,2148,2154,2160,2165,2171,2177,2183,2189,2195,2200,2206,2212,2218,2224,2230,2236,2241,2247,2253,2258,2264,2270,2276,2282,2287,2293,2299,2304,2310,2315,2321,2327,2333,2339,2345,2351,2357,2362,2368,2373,2378,2384,2390,2395,2400,2406,2412,2418,2424,2430,2436,2442],{"__ignoreMap":19},[74,494,495],{"class":76,"line":77},[74,496,497],{},"#include \u003Cglad\u002Fglad.h>\n",[74,499,500],{"class":76,"line":116},[74,501,502],{},"#include \u003CGLFW\u002Fglfw3.h>\n",[74,504,505],{"class":76,"line":271},[74,506,507],{},"#include \u003Copencv4\u002Fopencv2\u002Fopencv.hpp>\n",[74,509,510],{"class":76,"line":277},[74,511,512],{},"#include \u003Ciostream>\n",[74,514,515],{"class":76,"line":322},[74,516,517],{},"#include \u003Ccmath>\n",[74,519,520],{"class":76,"line":409},[74,521,522],{},"#include \u003Cfilesystem>\n",[74,524,525],{"class":76,"line":415},[74,526,527],{},"#include \"utils\u002Fshader.h\"\n",[74,529,530],{"class":76,"line":421},[74,531,532],{},"#include \"utils\u002FglmCompute.h\"\n",[74,534,535],{"class":76,"line":427},[74,536,537],{},"#include \"utils\u002Flogger.h\"\n",[74,539,540],{"class":76,"line":433},[74,541,543],{"emptyLinePlaceholder":542},true,"\n",[74,545,546],{"class":76,"line":439},[74,547,548],{},"using namespace cv;\n",[74,550,551],{"class":76,"line":445},[74,552,553],{},"using namespace glm;\n",[74,555,556],{"class":76,"line":451},[74,557,543],{"emptyLinePlaceholder":542},[74,559,561],{"class":76,"line":560},14,[74,562,563],{},"\u002F\u002F 窗口大小改变时的回调函数\n",[74,565,567],{"class":76,"line":566},15,[74,568,569],{},"void framebuffer_size_callback(GLFWwindow* window, int width, int height) {\n",[74,571,573],{"class":76,"line":572},16,[74,574,575],{},"    glViewport(0, 0, width, height);\n",[74,577,579],{"class":76,"line":578},17,[74,580,454],{},[74,582,584],{"class":76,"line":583},18,[74,585,543],{"emptyLinePlaceholder":542},[74,587,589],{"class":76,"line":588},19,[74,590,591],{},"std::string getCurrentWorkingDirectory() {\n",[74,593,595],{"class":76,"line":594},20,[74,596,597],{},"    namespace fs = std::filesystem;\n",[74,599,601],{"class":76,"line":600},21,[74,602,603],{},"    try {\n",[74,605,607],{"class":76,"line":606},22,[74,608,609],{},"        \u002F\u002F current_path() 返回当前工作目录\n",[74,611,613],{"class":76,"line":612},23,[74,614,615],{},"        return fs::current_path().string();\n",[74,617,619],{"class":76,"line":618},24,[74,620,621],{},"    } catch (const fs::filesystem_error& e) {\n",[74,623,625],{"class":76,"line":624},25,[74,626,627],{},"        std::cerr \u003C\u003C \"Filesystem Error: \" \u003C\u003C e.what() \u003C\u003C std::endl;\n",[74,629,631],{"class":76,"line":630},26,[74,632,633],{},"        return \"\";\n",[74,635,637],{"class":76,"line":636},27,[74,638,639],{},"    }\n",[74,641,643],{"class":76,"line":642},28,[74,644,454],{},[74,646,648],{"class":76,"line":647},29,[74,649,543],{"emptyLinePlaceholder":542},[74,651,653],{"class":76,"line":652},30,[74,654,655],{},"\u002F\u002F 相机的位置和方向\n",[74,657,659],{"class":76,"line":658},31,[74,660,661],{},"vec3 cameraPos = vec3(0.0f, 0.0f, 3.0f);\n",[74,663,665],{"class":76,"line":664},32,[74,666,667],{},"vec3 cameraFront = vec3(0.0f, 0.0f, -1.0f); \u002F\u002F 相机朝向\n",[74,669,671],{"class":76,"line":670},33,[74,672,673],{},"vec3 cameraUp = vec3(0.0f, 1.0f, 0.0f); \u002F\u002F 上向量\n",[74,675,677],{"class":76,"line":676},34,[74,678,543],{"emptyLinePlaceholder":542},[74,680,682],{"class":76,"line":681},35,[74,683,684],{},"\u002F\u002F 鼠标控制变量\n",[74,686,688],{"class":76,"line":687},36,[74,689,690],{},"float yawAngle = -90.0f;   \u002F\u002F 偏航角\n",[74,692,694],{"class":76,"line":693},37,[74,695,696],{},"float pitchAngle = 0.0f;   \u002F\u002F 俯仰角\n",[74,698,700],{"class":76,"line":699},38,[74,701,702],{},"float lastX = 400.0f; \u002F\u002F 鼠标上次X位置\n",[74,704,706],{"class":76,"line":705},39,[74,707,708],{},"float lastY = 300.0f; \u002F\u002F 鼠标上次Y位置\n",[74,710,712],{"class":76,"line":711},40,[74,713,714],{},"bool firstMouse = true; \u002F\u002F 是否是第一次鼠标移动\n",[74,716,718],{"class":76,"line":717},41,[74,719,720],{},"bool mouseCaptured = true; \u002F\u002F 鼠标是否被捕获\n",[74,722,724],{"class":76,"line":723},42,[74,725,543],{"emptyLinePlaceholder":542},[74,727,729],{"class":76,"line":728},43,[74,730,731],{},"\u002F\u002F 鼠标回调函数\n",[74,733,735],{"class":76,"line":734},44,[74,736,737],{},"void mouse_callback(GLFWwindow* window, double xpos, double ypos) {\n",[74,739,741],{"class":76,"line":740},45,[74,742,743],{},"    \u002F\u002F 只有在鼠标被捕获时才处理鼠标移动\n",[74,745,747],{"class":76,"line":746},46,[74,748,749],{},"    if (!mouseCaptured) return;\n",[74,751,753],{"class":76,"line":752},47,[74,754,755],{},"    \n",[74,757,759],{"class":76,"line":758},48,[74,760,761],{},"    if (firstMouse) {\n",[74,763,765],{"class":76,"line":764},49,[74,766,767],{},"        lastX = xpos;\n",[74,769,771],{"class":76,"line":770},50,[74,772,773],{},"        lastY = ypos;\n",[74,775,777],{"class":76,"line":776},51,[74,778,779],{},"        firstMouse = false;\n",[74,781,783],{"class":76,"line":782},52,[74,784,639],{},[74,786,788],{"class":76,"line":787},53,[74,789,543],{"emptyLinePlaceholder":542},[74,791,793],{"class":76,"line":792},54,[74,794,795],{},"    float xoffset = xpos - lastX;\n",[74,797,799],{"class":76,"line":798},55,[74,800,801],{},"    float yoffset = lastY - ypos; \u002F\u002F 注意这里是相反的，因为y坐标是从底部往顶部递增的\n",[74,803,805],{"class":76,"line":804},56,[74,806,807],{},"    lastX = xpos;\n",[74,809,811],{"class":76,"line":810},57,[74,812,813],{},"    lastY = ypos;\n",[74,815,817],{"class":76,"line":816},58,[74,818,543],{"emptyLinePlaceholder":542},[74,820,822],{"class":76,"line":821},59,[74,823,824],{},"    float sensitivity = 0.1f;\n",[74,826,828],{"class":76,"line":827},60,[74,829,830],{},"    xoffset *= sensitivity;\n",[74,832,834],{"class":76,"line":833},61,[74,835,836],{},"    yoffset *= sensitivity;\n",[74,838,840],{"class":76,"line":839},62,[74,841,543],{"emptyLinePlaceholder":542},[74,843,845],{"class":76,"line":844},63,[74,846,847],{},"    yawAngle += xoffset;\n",[74,849,851],{"class":76,"line":850},64,[74,852,853],{},"    pitchAngle += yoffset;\n",[74,855,857],{"class":76,"line":856},65,[74,858,543],{"emptyLinePlaceholder":542},[74,860,862],{"class":76,"line":861},66,[74,863,864],{},"    \u002F\u002F 限制俯仰角，避免翻转\n",[74,866,868],{"class":76,"line":867},67,[74,869,870],{},"    if (pitchAngle > 89.0f)\n",[74,872,874],{"class":76,"line":873},68,[74,875,876],{},"        pitchAngle = 89.0f;\n",[74,878,880],{"class":76,"line":879},69,[74,881,882],{},"    if (pitchAngle \u003C -89.0f)\n",[74,884,886],{"class":76,"line":885},70,[74,887,888],{},"        pitchAngle = -89.0f;\n",[74,890,892],{"class":76,"line":891},71,[74,893,543],{"emptyLinePlaceholder":542},[74,895,897],{"class":76,"line":896},72,[74,898,899],{},"    \u002F\u002F 计算新的相机方向\n",[74,901,903],{"class":76,"line":902},73,[74,904,905],{},"    vec3 front;\n",[74,907,909],{"class":76,"line":908},74,[74,910,911],{},"    front.x = cos(radians(yawAngle)) * cos(radians(pitchAngle));\n",[74,913,915],{"class":76,"line":914},75,[74,916,917],{},"    front.y = sin(radians(pitchAngle));\n",[74,919,921],{"class":76,"line":920},76,[74,922,923],{},"    front.z = sin(radians(yawAngle)) * cos(radians(pitchAngle));\n",[74,925,927],{"class":76,"line":926},77,[74,928,929],{},"    cameraFront = normalize(front);\n",[74,931,933],{"class":76,"line":932},78,[74,934,454],{},[74,936,938],{"class":76,"line":937},79,[74,939,543],{"emptyLinePlaceholder":542},[74,941,943],{"class":76,"line":942},80,[74,944,945],{},"void processInput(GLFWwindow *window){\n",[74,947,949],{"class":76,"line":948},81,[74,950,951],{},"    \u002F\u002F 检查 Ctrl+Alt 组合键来切换鼠标捕获状态\n",[74,953,955],{"class":76,"line":954},82,[74,956,957],{},"    bool ctrlPressed = glfwGetKey(window, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS || \n",[74,959,961],{"class":76,"line":960},83,[74,962,963],{},"                      glfwGetKey(window, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS;\n",[74,965,967],{"class":76,"line":966},84,[74,968,969],{},"    bool altPressed = glfwGetKey(window, GLFW_KEY_LEFT_ALT) == GLFW_PRESS || \n",[74,971,973],{"class":76,"line":972},85,[74,974,975],{},"                      glfwGetKey(window, GLFW_KEY_RIGHT_ALT) == GLFW_PRESS;\n",[74,977,979],{"class":76,"line":978},86,[74,980,755],{},[74,982,984],{"class":76,"line":983},87,[74,985,986],{},"    if (ctrlPressed && altPressed) {\n",[74,988,990],{"class":76,"line":989},88,[74,991,992],{},"        if (mouseCaptured) {\n",[74,994,996],{"class":76,"line":995},89,[74,997,998],{},"            \u002F\u002F 释放鼠标捕获\n",[74,1000,1002],{"class":76,"line":1001},90,[74,1003,1004],{},"            glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);\n",[74,1006,1008],{"class":76,"line":1007},91,[74,1009,1010],{},"            mouseCaptured = false;\n",[74,1012,1014],{"class":76,"line":1013},92,[74,1015,1016],{},"            firstMouse = true; \u002F\u002F 重置鼠标状态\n",[74,1018,1020],{"class":76,"line":1019},93,[74,1021,1022],{},"            std::cout \u003C\u003C \"鼠标捕获已释放 - 按 Ctrl+Alt 重新捕获\" \u003C\u003C std::endl;\n",[74,1024,1026],{"class":76,"line":1025},94,[74,1027,1028],{},"        } else {\n",[74,1030,1032],{"class":76,"line":1031},95,[74,1033,1034],{},"            \u002F\u002F 重新捕获鼠标\n",[74,1036,1038],{"class":76,"line":1037},96,[74,1039,1040],{},"            glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);\n",[74,1042,1044],{"class":76,"line":1043},97,[74,1045,1046],{},"            mouseCaptured = true;\n",[74,1048,1050],{"class":76,"line":1049},98,[74,1051,1016],{},[74,1053,1055],{"class":76,"line":1054},99,[74,1056,1057],{},"            std::cout \u003C\u003C \"鼠标捕获已启用\" \u003C\u003C std::endl;\n",[74,1059,1061],{"class":76,"line":1060},100,[74,1062,1063],{},"        }\n",[74,1065,1067],{"class":76,"line":1066},101,[74,1068,639],{},[74,1070,1072],{"class":76,"line":1071},102,[74,1073,755],{},[74,1075,1077],{"class":76,"line":1076},103,[74,1078,1079],{},"    \u002F\u002F 只有在鼠标被捕获时才处理WASD移动\n",[74,1081,1083],{"class":76,"line":1082},104,[74,1084,749],{},[74,1086,1088],{"class":76,"line":1087},105,[74,1089,755],{},[74,1091,1093],{"class":76,"line":1092},106,[74,1094,1095],{},"    float cameraSpeed = 0.05f;\n",[74,1097,1099],{"class":76,"line":1098},107,[74,1100,406],{},[74,1102,1104],{"class":76,"line":1103},108,[74,1105,412],{},[74,1107,1109],{"class":76,"line":1108},109,[74,1110,418],{},[74,1112,1114],{"class":76,"line":1113},110,[74,1115,424],{},[74,1117,1119],{"class":76,"line":1118},111,[74,1120,430],{},[74,1122,1124],{"class":76,"line":1123},112,[74,1125,1126],{},"        cameraPos -= normalize(cross(cameraFront, cameraUp)) * cameraSpeed;\n",[74,1128,1130],{"class":76,"line":1129},113,[74,1131,442],{},[74,1133,1135],{"class":76,"line":1134},114,[74,1136,1137],{},"        cameraPos += normalize(cross(cameraFront, cameraUp)) * cameraSpeed;\n",[74,1139,1141],{"class":76,"line":1140},115,[74,1142,454],{},[74,1144,1146],{"class":76,"line":1145},116,[74,1147,543],{"emptyLinePlaceholder":542},[74,1149,1151],{"class":76,"line":1150},117,[74,1152,1153],{},"int main() {\n",[74,1155,1157],{"class":76,"line":1156},118,[74,1158,543],{"emptyLinePlaceholder":542},[74,1160,1162],{"class":76,"line":1161},119,[74,1163,1164],{},"    \u002F\u002F 模型矩阵\n",[74,1166,1168],{"class":76,"line":1167},120,[74,1169,1170],{},"    mat4 model = mat4(1.0f);\n",[74,1172,1174],{"class":76,"line":1173},121,[74,1175,1176],{},"    model = rotate(model, radians(-55.0f), vec3(1.0f, 0.0f, 0.0f));\n",[74,1178,1180],{"class":76,"line":1179},122,[74,1181,543],{"emptyLinePlaceholder":542},[74,1183,1185],{"class":76,"line":1184},123,[74,1186,1187],{},"    \u002F\u002F lookat矩阵\n",[74,1189,1191],{"class":76,"line":1190},124,[74,1192,1193],{},"    mat4 view = mat4(1.0f);\n",[74,1195,1197],{"class":76,"line":1196},125,[74,1198,1199],{},"    view = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp);\n",[74,1201,1203],{"class":76,"line":1202},126,[74,1204,543],{"emptyLinePlaceholder":542},[74,1206,1208],{"class":76,"line":1207},127,[74,1209,1210],{},"    \u002F\u002F 投影矩阵\n",[74,1212,1214],{"class":76,"line":1213},128,[74,1215,1216],{},"    mat4 projection = perspective(radians(45.0f), (float)800 \u002F (float)600, 0.1f, 100.0f);\n",[74,1218,1220],{"class":76,"line":1219},129,[74,1221,543],{"emptyLinePlaceholder":542},[74,1223,1225],{"class":76,"line":1224},130,[74,1226,1227],{},"    \u002F\u002F 立方体数组\n",[74,1229,1231],{"class":76,"line":1230},131,[74,1232,1233],{},"    vec3 cubePositions[] = {\n",[74,1235,1237],{"class":76,"line":1236},132,[74,1238,1239],{},"        vec3(0.0f,  0.0f,  0.0f),\n",[74,1241,1243],{"class":76,"line":1242},133,[74,1244,1245],{},"        vec3(2.0f,  5.0f, -15.0f),\n",[74,1247,1249],{"class":76,"line":1248},134,[74,1250,1251],{},"        vec3(-1.5f, -2.2f, -2.5f),\n",[74,1253,1255],{"class":76,"line":1254},135,[74,1256,1257],{},"        vec3(-3.8f, -2.0f, -12.3f),\n",[74,1259,1261],{"class":76,"line":1260},136,[74,1262,1263],{},"        vec3(2.4f, -0.4f, -3.5f),\n",[74,1265,1267],{"class":76,"line":1266},137,[74,1268,1269],{},"    };\n",[74,1271,1273],{"class":76,"line":1272},138,[74,1274,543],{"emptyLinePlaceholder":542},[74,1276,1278],{"class":76,"line":1277},139,[74,1279,1280],{},"    auto path = getCurrentWorkingDirectory().append(\"\u002FAssets\u002Fimage.png\");\n",[74,1282,1284],{"class":76,"line":1283},140,[74,1285,1286],{},"    auto path2 = getCurrentWorkingDirectory().append(\"\u002FAssets\u002Fimage2.png\");\n",[74,1288,1290],{"class":76,"line":1289},141,[74,1291,1292],{},"    cv::Mat image = cv::imread(path);\n",[74,1294,1296],{"class":76,"line":1295},142,[74,1297,1298],{},"    cv::Mat image2 = cv::imread(path2);\n",[74,1300,1302],{"class":76,"line":1301},143,[74,1303,1304],{},"    cv::flip(image2, image2, 0);\n",[74,1306,1308],{"class":76,"line":1307},144,[74,1309,1310],{},"    cv::cvtColor(image, image, cv::COLOR_BGR2RGB);\n",[74,1312,1314],{"class":76,"line":1313},145,[74,1315,1316],{},"    cv::cvtColor(image2, image2, cv::COLOR_BGR2RGB);\n",[74,1318,1320],{"class":76,"line":1319},146,[74,1321,543],{"emptyLinePlaceholder":542},[74,1323,1325],{"class":76,"line":1324},147,[74,1326,1327],{},"    \u002F\u002F 转换为字节数组\n",[74,1329,1331],{"class":76,"line":1330},148,[74,1332,1333],{},"    std::vector\u003Cuchar> imageData;\n",[74,1335,1337],{"class":76,"line":1336},149,[74,1338,1339],{},"    std::vector\u003Cuchar> imageData2;\n",[74,1341,1343],{"class":76,"line":1342},150,[74,1344,1345],{},"    if (image.empty()) {\n",[74,1347,1349],{"class":76,"line":1348},151,[74,1350,1351],{},"        std::cerr \u003C\u003C \"Could not read the image: \" \u003C\u003C path \u003C\u003C std::endl;\n",[74,1353,1355],{"class":76,"line":1354},152,[74,1356,1357],{},"        return 1;\n",[74,1359,1361],{"class":76,"line":1360},153,[74,1362,639],{},[74,1364,1366],{"class":76,"line":1365},154,[74,1367,1368],{},"    if (image2.empty()) {\n",[74,1370,1372],{"class":76,"line":1371},155,[74,1373,1374],{},"        std::cerr \u003C\u003C \"Could not read the image2: \" \u003C\u003C path2 \u003C\u003C std::endl;\n",[74,1376,1378],{"class":76,"line":1377},156,[74,1379,1357],{},[74,1381,1383],{"class":76,"line":1382},157,[74,1384,639],{},[74,1386,1388],{"class":76,"line":1387},158,[74,1389,755],{},[74,1391,1393],{"class":76,"line":1392},159,[74,1394,1395],{},"    std::cout \u003C\u003C \"Image 1 loaded: \" \u003C\u003C image.cols \u003C\u003C \"x\" \u003C\u003C image.rows \u003C\u003C std::endl;\n",[74,1397,1399],{"class":76,"line":1398},160,[74,1400,1401],{},"    std::cout \u003C\u003C \"Image 2 loaded: \" \u003C\u003C image2.cols \u003C\u003C \"x\" \u003C\u003C image2.rows \u003C\u003C std::endl;\n",[74,1403,1405],{"class":76,"line":1404},161,[74,1406,543],{"emptyLinePlaceholder":542},[74,1408,1410],{"class":76,"line":1409},162,[74,1411,1412],{},"    cv::imencode(\".png\", image, imageData);\n",[74,1414,1416],{"class":76,"line":1415},163,[74,1417,1418],{},"    cv::imencode(\".png\", image2, imageData2);\n",[74,1420,1422],{"class":76,"line":1421},164,[74,1423,543],{"emptyLinePlaceholder":542},[74,1425,1427],{"class":76,"line":1426},165,[74,1428,543],{"emptyLinePlaceholder":542},[74,1430,1432],{"class":76,"line":1431},166,[74,1433,755],{},[74,1435,1437],{"class":76,"line":1436},167,[74,1438,1439],{},"    \u002F\u002F 初始化GLFW\n",[74,1441,1443],{"class":76,"line":1442},168,[74,1444,1445],{},"    glfwInit();\n",[74,1447,1449],{"class":76,"line":1448},169,[74,1450,1451],{},"    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);\n",[74,1453,1455],{"class":76,"line":1454},170,[74,1456,1457],{},"    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);\n",[74,1459,1461],{"class":76,"line":1460},171,[74,1462,1463],{},"    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);\n",[74,1465,1467],{"class":76,"line":1466},172,[74,1468,543],{"emptyLinePlaceholder":542},[74,1470,1472],{"class":76,"line":1471},173,[74,1473,1474],{},"    \u002F\u002F 创建窗口\n",[74,1476,1478],{"class":76,"line":1477},174,[74,1479,1480],{},"    GLFWwindow* window = glfwCreateWindow(800, 600, \"OpenGL Window\", NULL, NULL);\n",[74,1482,1484],{"class":76,"line":1483},175,[74,1485,1486],{},"    if (window == NULL) {\n",[74,1488,1490],{"class":76,"line":1489},176,[74,1491,1492],{},"        std::cout \u003C\u003C \"Failed to create GLFW window\" \u003C\u003C std::endl;\n",[74,1494,1496],{"class":76,"line":1495},177,[74,1497,1498],{},"        glfwTerminate();\n",[74,1500,1502],{"class":76,"line":1501},178,[74,1503,1504],{},"        return -1;\n",[74,1506,1508],{"class":76,"line":1507},179,[74,1509,639],{},[74,1511,1513],{"class":76,"line":1512},180,[74,1514,755],{},[74,1516,1518],{"class":76,"line":1517},181,[74,1519,1520],{},"    glfwMakeContextCurrent(window);\n",[74,1522,1524],{"class":76,"line":1523},182,[74,1525,1526],{},"    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);\n",[74,1528,1530],{"class":76,"line":1529},183,[74,1531,1532],{},"    glfwSetCursorPosCallback(window, mouse_callback);\n",[74,1534,1536],{"class":76,"line":1535},184,[74,1537,755],{},[74,1539,1541],{"class":76,"line":1540},185,[74,1542,1543],{},"    \u002F\u002F 隐藏鼠标光标并捕获鼠标\n",[74,1545,1547],{"class":76,"line":1546},186,[74,1548,1549],{},"    glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);\n",[74,1551,1553],{"class":76,"line":1552},187,[74,1554,543],{"emptyLinePlaceholder":542},[74,1556,1558],{"class":76,"line":1557},188,[74,1559,1560],{},"    \u002F\u002F 初始化GLAD\n",[74,1562,1564],{"class":76,"line":1563},189,[74,1565,1566],{},"    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {\n",[74,1568,1570],{"class":76,"line":1569},190,[74,1571,1572],{},"        std::cout \u003C\u003C \"Failed to initialize GLAD\" \u003C\u003C std::endl;\n",[74,1574,1576],{"class":76,"line":1575},191,[74,1577,1504],{},[74,1579,1581],{"class":76,"line":1580},192,[74,1582,639],{},[74,1584,1586],{"class":76,"line":1585},193,[74,1587,543],{"emptyLinePlaceholder":542},[74,1589,1591],{"class":76,"line":1590},194,[74,1592,1593],{},"    \u002F\u002F 启用深度测试\n",[74,1595,1597],{"class":76,"line":1596},195,[74,1598,1599],{},"    glEnable(GL_DEPTH_TEST);\n",[74,1601,1603],{"class":76,"line":1602},196,[74,1604,543],{"emptyLinePlaceholder":542},[74,1606,1608],{"class":76,"line":1607},197,[74,1609,1610],{},"    float vertices[] = {\n",[74,1612,1614],{"class":76,"line":1613},198,[74,1615,1616],{},"    \u002F\u002F 位置(3) + 纹理坐标(2)\n",[74,1618,1620],{"class":76,"line":1619},199,[74,1621,1622],{},"    -0.5f, -0.5f, -0.5f,  0.0f, 0.0f,\n",[74,1624,1626],{"class":76,"line":1625},200,[74,1627,1628],{},"     0.5f, -0.5f, -0.5f,  1.0f, 0.0f,\n",[74,1630,1632],{"class":76,"line":1631},201,[74,1633,1634],{},"     0.5f,  0.5f, -0.5f,  1.0f, 1.0f,\n",[74,1636,1638],{"class":76,"line":1637},202,[74,1639,1634],{},[74,1641,1643],{"class":76,"line":1642},203,[74,1644,1645],{},"    -0.5f,  0.5f, -0.5f,  0.0f, 1.0f,\n",[74,1647,1649],{"class":76,"line":1648},204,[74,1650,1622],{},[74,1652,1654],{"class":76,"line":1653},205,[74,1655,543],{"emptyLinePlaceholder":542},[74,1657,1659],{"class":76,"line":1658},206,[74,1660,1661],{},"    -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,\n",[74,1663,1665],{"class":76,"line":1664},207,[74,1666,1667],{},"     0.5f, -0.5f,  0.5f,  1.0f, 0.0f,\n",[74,1669,1671],{"class":76,"line":1670},208,[74,1672,1673],{},"     0.5f,  0.5f,  0.5f,  1.0f, 1.0f,\n",[74,1675,1677],{"class":76,"line":1676},209,[74,1678,1673],{},[74,1680,1682],{"class":76,"line":1681},210,[74,1683,1684],{},"    -0.5f,  0.5f,  0.5f,  0.0f, 1.0f,\n",[74,1686,1688],{"class":76,"line":1687},211,[74,1689,1661],{},[74,1691,1693],{"class":76,"line":1692},212,[74,1694,543],{"emptyLinePlaceholder":542},[74,1696,1698],{"class":76,"line":1697},213,[74,1699,1700],{},"    -0.5f,  0.5f,  0.5f,  1.0f, 0.0f,\n",[74,1702,1704],{"class":76,"line":1703},214,[74,1705,1706],{},"    -0.5f,  0.5f, -0.5f,  1.0f, 1.0f,\n",[74,1708,1710],{"class":76,"line":1709},215,[74,1711,1712],{},"    -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,\n",[74,1714,1716],{"class":76,"line":1715},216,[74,1717,1712],{},[74,1719,1721],{"class":76,"line":1720},217,[74,1722,1661],{},[74,1724,1726],{"class":76,"line":1725},218,[74,1727,1700],{},[74,1729,1731],{"class":76,"line":1730},219,[74,1732,543],{"emptyLinePlaceholder":542},[74,1734,1736],{"class":76,"line":1735},220,[74,1737,1738],{},"     0.5f,  0.5f,  0.5f,  1.0f, 0.0f,\n",[74,1740,1742],{"class":76,"line":1741},221,[74,1743,1634],{},[74,1745,1747],{"class":76,"line":1746},222,[74,1748,1749],{},"     0.5f, -0.5f, -0.5f,  0.0f, 1.0f,\n",[74,1751,1753],{"class":76,"line":1752},223,[74,1754,1749],{},[74,1756,1758],{"class":76,"line":1757},224,[74,1759,1760],{},"     0.5f, -0.5f,  0.5f,  0.0f, 0.0f,\n",[74,1762,1764],{"class":76,"line":1763},225,[74,1765,1738],{},[74,1767,1769],{"class":76,"line":1768},226,[74,1770,543],{"emptyLinePlaceholder":542},[74,1772,1774],{"class":76,"line":1773},227,[74,1775,1712],{},[74,1777,1779],{"class":76,"line":1778},228,[74,1780,1781],{},"     0.5f, -0.5f, -0.5f,  1.0f, 1.0f,\n",[74,1783,1785],{"class":76,"line":1784},229,[74,1786,1667],{},[74,1788,1790],{"class":76,"line":1789},230,[74,1791,1667],{},[74,1793,1795],{"class":76,"line":1794},231,[74,1796,1661],{},[74,1798,1800],{"class":76,"line":1799},232,[74,1801,1712],{},[74,1803,1805],{"class":76,"line":1804},233,[74,1806,543],{"emptyLinePlaceholder":542},[74,1808,1810],{"class":76,"line":1809},234,[74,1811,1645],{},[74,1813,1815],{"class":76,"line":1814},235,[74,1816,1634],{},[74,1818,1820],{"class":76,"line":1819},236,[74,1821,1738],{},[74,1823,1825],{"class":76,"line":1824},237,[74,1826,1738],{},[74,1828,1830],{"class":76,"line":1829},238,[74,1831,1832],{},"    -0.5f,  0.5f,  0.5f,  0.0f, 0.0f,\n",[74,1834,1836],{"class":76,"line":1835},239,[74,1837,1838],{},"    -0.5f,  0.5f, -0.5f,  0.0f, 1.0f\n",[74,1840,1842],{"class":76,"line":1841},240,[74,1843,1844],{},"};\n",[74,1846,1848],{"class":76,"line":1847},241,[74,1849,755],{},[74,1851,1853],{"class":76,"line":1852},242,[74,1854,1855],{},"    \u002F\u002F indices数组未使用，已删除\n",[74,1857,1859],{"class":76,"line":1858},243,[74,1860,543],{"emptyLinePlaceholder":542},[74,1862,1864],{"class":76,"line":1863},244,[74,1865,1866],{},"    \u002F\u002F 纹理\n",[74,1868,1870],{"class":76,"line":1869},245,[74,1871,1872],{},"    unsigned int texture;\n",[74,1874,1876],{"class":76,"line":1875},246,[74,1877,1878],{},"    unsigned int texture2;\n",[74,1880,1882],{"class":76,"line":1881},247,[74,1883,1884],{},"    glGenTextures(1, &texture); \u002F\u002F 生成纹理\n",[74,1886,1888],{"class":76,"line":1887},248,[74,1889,1890],{},"    glGenTextures(1, &texture2); \u002F\u002F 生成纹理\n",[74,1892,1894],{"class":76,"line":1893},249,[74,1895,755],{},[74,1897,1899],{"class":76,"line":1898},250,[74,1900,1901],{},"    \u002F\u002F 配置第一个纹理\n",[74,1903,1905],{"class":76,"line":1904},251,[74,1906,1907],{},"    glActiveTexture(GL_TEXTURE0);\n",[74,1909,1911],{"class":76,"line":1910},252,[74,1912,1913],{},"    glBindTexture(GL_TEXTURE_2D, texture); \u002F\u002F 绑定纹理\n",[74,1915,1917],{"class":76,"line":1916},253,[74,1918,1919],{},"    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); \u002F\u002F 设置纹理环绕方式\n",[74,1921,1923],{"class":76,"line":1922},254,[74,1924,1925],{},"    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); \u002F\u002F 设置纹理环绕方式\n",[74,1927,1929],{"class":76,"line":1928},255,[74,1930,1931],{},"    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); \u002F\u002F 设置纹理过滤方式\n",[74,1933,1935],{"class":76,"line":1934},256,[74,1936,1937],{},"    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); \u002F\u002F 设置纹理过滤方式\n",[74,1939,1941],{"class":76,"line":1940},257,[74,1942,1943],{},"    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, image.cols, image.rows, 0, GL_RGB, GL_UNSIGNED_BYTE, image.data); \u002F\u002F 设置纹理数据\n",[74,1945,1947],{"class":76,"line":1946},258,[74,1948,1949],{},"    glGenerateMipmap(GL_TEXTURE_2D); \u002F\u002F 生成mipmap\n",[74,1951,1953],{"class":76,"line":1952},259,[74,1954,543],{"emptyLinePlaceholder":542},[74,1956,1958],{"class":76,"line":1957},260,[74,1959,1960],{},"    \u002F\u002F 配置第二个纹理\n",[74,1962,1964],{"class":76,"line":1963},261,[74,1965,1966],{},"    glActiveTexture(GL_TEXTURE1);\n",[74,1968,1970],{"class":76,"line":1969},262,[74,1971,1972],{},"    glBindTexture(GL_TEXTURE_2D, texture2); \u002F\u002F 绑定纹理\n",[74,1974,1976],{"class":76,"line":1975},263,[74,1977,1919],{},[74,1979,1981],{"class":76,"line":1980},264,[74,1982,1983],{},"    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); \u002F\u002F 设置纹理环绕方式   \n",[74,1985,1987],{"class":76,"line":1986},265,[74,1988,1931],{},[74,1990,1992],{"class":76,"line":1991},266,[74,1993,1937],{},[74,1995,1997],{"class":76,"line":1996},267,[74,1998,1999],{},"    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, image2.cols, image2.rows, 0, GL_RGB, GL_UNSIGNED_BYTE, image2.data); \u002F\u002F 设置纹理数据\n",[74,2001,2003],{"class":76,"line":2002},268,[74,2004,1949],{},[74,2006,2008],{"class":76,"line":2007},269,[74,2009,543],{"emptyLinePlaceholder":542},[74,2011,2013],{"class":76,"line":2012},270,[74,2014,543],{"emptyLinePlaceholder":542},[74,2016,2018],{"class":76,"line":2017},271,[74,2019,2020],{},"    \u002F\u002F 创建VAO\n",[74,2022,2024],{"class":76,"line":2023},272,[74,2025,2026],{},"    unsigned int VAO;\n",[74,2028,2030],{"class":76,"line":2029},273,[74,2031,2032],{},"    glGenVertexArrays(1, &VAO);\n",[74,2034,2036],{"class":76,"line":2035},274,[74,2037,2038],{},"    glBindVertexArray(VAO); \u002F\u002F 绑定VAO\n",[74,2040,2042],{"class":76,"line":2041},275,[74,2043,543],{"emptyLinePlaceholder":542},[74,2045,2047],{"class":76,"line":2046},276,[74,2048,2049],{},"    \u002F\u002F 创建VBO\n",[74,2051,2053],{"class":76,"line":2052},277,[74,2054,2055],{},"    unsigned int VBO;\n",[74,2057,2059],{"class":76,"line":2058},278,[74,2060,2061],{},"    glGenBuffers(1, &VBO);\n",[74,2063,2065],{"class":76,"line":2064},279,[74,2066,2067],{},"    glBindBuffer(GL_ARRAY_BUFFER, VBO); \u002F\u002F 绑定VBO\n",[74,2069,2071],{"class":76,"line":2070},280,[74,2072,2073],{},"    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);\n",[74,2075,2077],{"class":76,"line":2076},281,[74,2078,543],{"emptyLinePlaceholder":542},[74,2080,2082],{"class":76,"line":2081},282,[74,2083,2084],{},"    int nrAttributes;\n",[74,2086,2088],{"class":76,"line":2087},283,[74,2089,2090],{},"    glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &nrAttributes);\n",[74,2092,2094],{"class":76,"line":2093},284,[74,2095,2096],{},"    std::cout \u003C\u003C \"Maximum nr of vertex attributes supported: \" \u003C\u003C nrAttributes \u003C\u003C std::endl;\n",[74,2098,2100],{"class":76,"line":2099},285,[74,2101,543],{"emptyLinePlaceholder":542},[74,2103,2105],{"class":76,"line":2104},286,[74,2106,2107],{},"    \u002F\u002F 位置属性\n",[74,2109,2111],{"class":76,"line":2110},287,[74,2112,2113],{},"    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);\n",[74,2115,2117],{"class":76,"line":2116},288,[74,2118,2119],{},"    glEnableVertexAttribArray(0); \u002F\u002F 启用顶点属性\n",[74,2121,2123],{"class":76,"line":2122},289,[74,2124,755],{},[74,2126,2128],{"class":76,"line":2127},290,[74,2129,2130],{},"    \u002F\u002F 纹理属性\n",[74,2132,2134],{"class":76,"line":2133},291,[74,2135,2136],{},"    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));\n",[74,2138,2140],{"class":76,"line":2139},292,[74,2141,2142],{},"    glEnableVertexAttribArray(2);\n",[74,2144,2146],{"class":76,"line":2145},293,[74,2147,543],{"emptyLinePlaceholder":542},[74,2149,2151],{"class":76,"line":2150},294,[74,2152,2153],{},"    \u002F\u002F 构建并编译着色器程序\n",[74,2155,2157],{"class":76,"line":2156},295,[74,2158,2159],{},"    Shader ourShader(\"shader\u002Fcustom.vert\", \"shader\u002Ffragment.vert\");\n",[74,2161,2163],{"class":76,"line":2162},296,[74,2164,543],{"emptyLinePlaceholder":542},[74,2166,2168],{"class":76,"line":2167},297,[74,2169,2170],{},"    \u002F\u002F 渲染循环\n",[74,2172,2174],{"class":76,"line":2173},298,[74,2175,2176],{},"    while (!glfwWindowShouldClose(window)) {\n",[74,2178,2180],{"class":76,"line":2179},299,[74,2181,2182],{},"        \u002F\u002F 处理输入\n",[74,2184,2186],{"class":76,"line":2185},300,[74,2187,2188],{},"        processInput(window);\n",[74,2190,2192],{"class":76,"line":2191},301,[74,2193,2194],{},"        glfwPollEvents();\n",[74,2196,2198],{"class":76,"line":2197},302,[74,2199,543],{"emptyLinePlaceholder":542},[74,2201,2203],{"class":76,"line":2202},303,[74,2204,2205],{},"        \u002F\u002F 更新view矩阵\n",[74,2207,2209],{"class":76,"line":2208},304,[74,2210,2211],{},"        view = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp);\n",[74,2213,2215],{"class":76,"line":2214},305,[74,2216,2217],{},"        \n",[74,2219,2221],{"class":76,"line":2220},306,[74,2222,2223],{},"        \u002F\u002F 渲染指令\n",[74,2225,2227],{"class":76,"line":2226},307,[74,2228,2229],{},"        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);\n",[74,2231,2233],{"class":76,"line":2232},308,[74,2234,2235],{},"        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);\n",[74,2237,2239],{"class":76,"line":2238},309,[74,2240,2217],{},[74,2242,2244],{"class":76,"line":2243},310,[74,2245,2246],{},"        \u002F\u002F 使用着色器程序\n",[74,2248,2250],{"class":76,"line":2249},311,[74,2251,2252],{},"        ourShader.use();\n",[74,2254,2256],{"class":76,"line":2255},312,[74,2257,543],{"emptyLinePlaceholder":542},[74,2259,2261],{"class":76,"line":2260},313,[74,2262,2263],{},"        \u002F\u002F 设置矩阵uniform\n",[74,2265,2267],{"class":76,"line":2266},314,[74,2268,2269],{},"        ourShader.setMat4(\"model\", model);\n",[74,2271,2273],{"class":76,"line":2272},315,[74,2274,2275],{},"        ourShader.setMat4(\"view\", view);\n",[74,2277,2279],{"class":76,"line":2278},316,[74,2280,2281],{},"        ourShader.setMat4(\"projection\", projection);\n",[74,2283,2285],{"class":76,"line":2284},317,[74,2286,2217],{},[74,2288,2290],{"class":76,"line":2289},318,[74,2291,2292],{},"        ourShader.setInt(\"ourTexture\", 0);\n",[74,2294,2296],{"class":76,"line":2295},319,[74,2297,2298],{},"        ourShader.setInt(\"ourTexture2\", 1);\n",[74,2300,2302],{"class":76,"line":2301},320,[74,2303,2217],{},[74,2305,2307],{"class":76,"line":2306},321,[74,2308,2309],{},"        glBindVertexArray(VAO);\n",[74,2311,2313],{"class":76,"line":2312},322,[74,2314,2217],{},[74,2316,2318],{"class":76,"line":2317},323,[74,2319,2320],{},"        \u002F\u002F 绘制多个立方体\n",[74,2322,2324],{"class":76,"line":2323},324,[74,2325,2326],{},"        for(unsigned int i = 0; i \u003C 10; i++) {\n",[74,2328,2330],{"class":76,"line":2329},325,[74,2331,2332],{},"            glm::mat4 modelMatrix = glm::mat4(1.0f);\n",[74,2334,2336],{"class":76,"line":2335},326,[74,2337,2338],{},"            modelMatrix = glm::translate(modelMatrix, cubePositions[i]);\n",[74,2340,2342],{"class":76,"line":2341},327,[74,2343,2344],{},"            float angle = 20.0f * i; \n",[74,2346,2348],{"class":76,"line":2347},328,[74,2349,2350],{},"            modelMatrix = glm::rotate(modelMatrix, glm::radians(angle), glm::vec3(1.0f, 0.3f, 0.5f));\n",[74,2352,2354],{"class":76,"line":2353},329,[74,2355,2356],{},"            ourShader.setMat4(\"model\", modelMatrix);\n",[74,2358,2360],{"class":76,"line":2359},330,[74,2361,543],{"emptyLinePlaceholder":542},[74,2363,2365],{"class":76,"line":2364},331,[74,2366,2367],{},"            glDrawArrays(GL_TRIANGLES, 0, 36);\n",[74,2369,2371],{"class":76,"line":2370},332,[74,2372,1063],{},[74,2374,2376],{"class":76,"line":2375},333,[74,2377,2217],{},[74,2379,2381],{"class":76,"line":2380},334,[74,2382,2383],{},"        \u002F\u002F 交换缓冲区和检查IO事件\n",[74,2385,2387],{"class":76,"line":2386},335,[74,2388,2389],{},"        glfwSwapBuffers(window);\n",[74,2391,2393],{"class":76,"line":2392},336,[74,2394,639],{},[74,2396,2398],{"class":76,"line":2397},337,[74,2399,543],{"emptyLinePlaceholder":542},[74,2401,2403],{"class":76,"line":2402},338,[74,2404,2405],{},"    \u002F\u002F 清理资源\n",[74,2407,2409],{"class":76,"line":2408},339,[74,2410,2411],{},"    glDeleteVertexArrays(1, &VAO);\n",[74,2413,2415],{"class":76,"line":2414},340,[74,2416,2417],{},"    glDeleteBuffers(1, &VBO);\n",[74,2419,2421],{"class":76,"line":2420},341,[74,2422,2423],{},"    glDeleteTextures(1, &texture);\n",[74,2425,2427],{"class":76,"line":2426},342,[74,2428,2429],{},"    glDeleteTextures(1, &texture2);\n",[74,2431,2433],{"class":76,"line":2432},343,[74,2434,2435],{},"    glfwTerminate();\n",[74,2437,2439],{"class":76,"line":2438},344,[74,2440,2441],{},"    return 0;\n",[74,2443,2445],{"class":76,"line":2444},345,[74,2446,454],{},[25,2448,2449],{"id":2449},"移动速度",[21,2451,2452,2453,2456,2457,2460],{},"​\t目前我们的",[33,2454,2455],{},"移动速度是个常量。理论上没什么问题，但是实际情况下根据处理器的能力不同，有些人可能会比其他人每秒绘制更多帧","，也就是",[33,2458,2459],{},"以更高的频率调用processInput函数。结果就是，根据配置的不同，有些人可能移动很快，而有些人会移动很慢","。当你发布你的程序的时候，你必须确保它在所有硬件上移动速度都一样。",[21,2462,2463,2464,2467,2468,2471],{},"​\t图形程序和游戏",[33,2465,2466],{},"通常会跟踪一个时间差(Deltatime)变量","，它",[33,2469,2470],{},"储存了渲染上一帧所用的时间。我们把所有速度都去乘以deltaTime值","。结果就是，如果我们的deltaTime很大，就意味着上一帧的渲染花费了更多时间，所以这一帧的速度需要变得更高来平衡渲染所花去的时间。使用这种方法时，无论你的电脑快还是慢，摄像机的速度都会相应平衡，这样每个用户的体验就都一样了。",[21,2473,2474],{},"​\twin11上dpi不稳定的解决方案： dpi稳定器：",[10,2476,2478],{"className":68,"code":2477,"language":70,"meta":19,"style":19},"#pragma once\n\n#include \u003CGLFW\u002Fglfw3.h>\n#include \u003Cglm\u002Fglm.hpp>\n#include \u003Cglm\u002Fgtc\u002Fmatrix_transform.hpp>\n#include \u003Ciostream>\n#include \u003Ccmath>\n#include \u003Cstring>\n#include \u003Calgorithm>\n\n\u002F**\n * @brief DPI 稳定器类 - 解决 Windows 11 等系统上的鼠标 DPI 漂移问题\n * \n * 功能特性：\n * - 异常偏移检测和过滤\n * - 动态敏感度调整\n * - DPI 感知设置\n * - 平滑的鼠标输入处理\n *\u002F\nclass DPIStabilizer {\nprivate:\n    \u002F\u002F 鼠标状态变量\n    float yawAngle;\n    float pitchAngle;\n    float lastX, lastY;\n    bool firstMouse;\n    bool mouseCaptured;\n    \n    \u002F\u002F DPI 稳定性参数\n    float mouseSensitivity;\n    float maxOffset;           \u002F\u002F 最大允许偏移量\n    int frameCount;           \u002F\u002F 帧计数器\n    float smoothingFactor;    \u002F\u002F 平滑因子\n    \n    \u002F\u002F 相机相关\n    glm::vec3 cameraPos;\n    glm::vec3 cameraFront;\n    glm::vec3 cameraUp;\n    \n    \u002F\u002F 统计信息\n    int totalMoves;\n    int filteredMoves;\n    float averageOffset;\n\npublic:\n    \u002F**\n     * @brief 构造函数\n     * @param sensitivity 初始鼠标敏感度 (默认 0.1f)\n     * @param maxOffset 最大允许偏移量 (默认 50.0f)\n     * @param smoothing 平滑因子 (默认 0.8f)\n     *\u002F\n    DPIStabilizer(float sensitivity = 0.1f, float maxOffset = 50.0f, float smoothing = 0.8f)\n        : yawAngle(-90.0f), pitchAngle(0.0f), lastX(400.0f), lastY(300.0f)\n        , firstMouse(true), mouseCaptured(true)\n        , mouseSensitivity(sensitivity), maxOffset(maxOffset), frameCount(0)\n        , smoothingFactor(smoothing), totalMoves(0), filteredMoves(0), averageOffset(0.0f)\n        , cameraPos(0.0f, 0.0f, 3.0f), cameraFront(0.0f, 0.0f, -1.0f), cameraUp(0.0f, 1.0f, 0.0f) {\n    }\n\n    \u002F**\n     * @brief 设置 DPI 感知提示（在创建窗口前调用）\n     *\u002F\n    static void setDPIHint() {\n        glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE);\n    }\n\n    \u002F**\n     * @brief 初始化 DPI 稳定器（在窗口创建后调用）\n     * @param window GLFW 窗口指针\n     *\u002F\n    void initialize(GLFWwindow* window) {\n        \u002F\u002F 设置用户指针，用于静态回调函数\n        glfwSetWindowUserPointer(window, this);\n        \n        \u002F\u002F 设置鼠标回调\n        glfwSetCursorPosCallback(window, mouseCallback);\n        \n        \u002F\u002F 捕获鼠标\n        glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);\n        \n        std::cout \u003C\u003C \"DPI 稳定器已初始化\" \u003C\u003C std::endl;\n    }\n\n    \u002F**\n     * @brief 鼠标回调函数（静态，供 GLFW 调用）\n     *\u002F\n    static void mouseCallback(GLFWwindow* window, double xpos, double ypos) {\n        \u002F\u002F 获取实例指针（通过用户指针存储）\n        DPIStabilizer* stabilizer = static_cast\u003CDPIStabilizer*>(glfwGetWindowUserPointer(window));\n        if (stabilizer) {\n            stabilizer->processMouseInput(xpos, ypos);\n        }\n    }\n\n    \u002F**\n     * @brief 处理鼠标输入\n     * @param xpos 鼠标 X 坐标\n     * @param ypos 鼠标 Y 坐标\n     *\u002F\n    void processMouseInput(double xpos, double ypos) {\n        if (!mouseCaptured) return;\n        \n        \u002F\u002F 第一次鼠标移动\n        if (firstMouse) {\n            lastX = xpos;\n            lastY = ypos;\n            firstMouse = false;\n            return;\n        }\n\n        \u002F\u002F 计算偏移量\n        float xoffset = xpos - lastX;\n        float yoffset = lastY - ypos; \u002F\u002F Y 轴反向\n        lastX = xpos;\n        lastY = ypos;\n\n        \u002F\u002F 统计信息\n        totalMoves++;\n        float currentOffset = std::sqrt(xoffset * xoffset + yoffset * yoffset);\n        averageOffset = (averageOffset * (totalMoves - 1) + currentOffset) \u002F totalMoves;\n\n        \u002F\u002F DPI 漂移检测\n        if (currentOffset > maxOffset) {\n            filteredMoves++;\n            std::cout \u003C\u003C \"检测到异常偏移: \" \u003C\u003C currentOffset \u003C\u003C \" 像素，已过滤\" \u003C\u003C std::endl;\n            return;\n        }\n\n        \u002F\u002F 应用敏感度\n        xoffset *= mouseSensitivity;\n        yoffset *= mouseSensitivity;\n\n        \u002F\u002F 平滑处理\n        xoffset *= smoothingFactor;\n        yoffset *= smoothingFactor;\n\n        \u002F\u002F 更新角度\n        yawAngle += xoffset;\n        pitchAngle += yoffset;\n\n        \u002F\u002F 限制俯仰角\n        if (pitchAngle > 89.0f) pitchAngle = 89.0f;\n        if (pitchAngle \u003C -89.0f) pitchAngle = -89.0f;\n\n        \u002F\u002F 计算新的相机方向\n        updateCameraDirection();\n    }\n\n    \u002F**\n     * @brief 更新相机方向\n     *\u002F\n    void updateCameraDirection() {\n        glm::vec3 front;\n        front.x = cos(glm::radians(yawAngle)) * cos(glm::radians(pitchAngle));\n        front.y = sin(glm::radians(pitchAngle));\n        front.z = sin(glm::radians(yawAngle)) * cos(glm::radians(pitchAngle));\n        cameraFront = glm::normalize(front);\n    }\n\n    \u002F**\n     * @brief 处理键盘输入\n     * @param window GLFW 窗口指针\n     *\u002F\n    void processKeyboardInput(GLFWwindow* window) {\n        \u002F\u002F 动态调整敏感度\n        if (glfwGetKey(window, GLFW_KEY_EQUAL) == GLFW_PRESS) {\n            mouseSensitivity += 0.01f;\n            if (mouseSensitivity > 0.5f) mouseSensitivity = 0.5f;\n            std::cout \u003C\u003C \"鼠标敏感度: \" \u003C\u003C mouseSensitivity \u003C\u003C std::endl;\n        }\n        if (glfwGetKey(window, GLFW_KEY_MINUS) == GLFW_PRESS) {\n            mouseSensitivity -= 0.01f;\n            if (mouseSensitivity \u003C 0.01f) mouseSensitivity = 0.01f;\n            std::cout \u003C\u003C \"鼠标敏感度: \" \u003C\u003C mouseSensitivity \u003C\u003C std::endl;\n        }\n\n        \u002F\u002F Ctrl+Alt 切换鼠标捕获\n        bool ctrlPressed = glfwGetKey(window, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS || \n                          glfwGetKey(window, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS;\n        bool altPressed = glfwGetKey(window, GLFW_KEY_LEFT_ALT) == GLFW_PRESS || \n                          glfwGetKey(window, GLFW_KEY_RIGHT_ALT) == GLFW_PRESS;\n        \n        if (ctrlPressed && altPressed) {\n            toggleMouseCapture(window);\n        }\n\n        \u002F\u002F WASD 移动（仅在捕获模式下）\n        if (mouseCaptured) {\n            float cameraSpeed = 0.05f;\n            if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)\n                cameraPos += cameraSpeed * cameraFront;\n            if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)\n                cameraPos -= cameraSpeed * cameraFront;\n            if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)\n                cameraPos -= glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;\n            if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)\n                cameraPos += glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;\n        }\n    }\n\n    \u002F**\n     * @brief 切换鼠标捕获状态\n     * @param window GLFW 窗口指针\n     *\u002F\n    void toggleMouseCapture(GLFWwindow* window) {\n        if (mouseCaptured) {\n            glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);\n            mouseCaptured = false;\n            firstMouse = true;\n            std::cout \u003C\u003C \"鼠标捕获已释放 - 按 Ctrl+Alt 重新捕获\" \u003C\u003C std::endl;\n        } else {\n            glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);\n            mouseCaptured = true;\n            firstMouse = true;\n            std::cout \u003C\u003C \"鼠标捕获已启用\" \u003C\u003C std::endl;\n        }\n    }\n\n    \u002F**\n     * @brief 获取 View 矩阵\n     * @return glm::mat4 View 矩阵\n     *\u002F\n    glm::mat4 getViewMatrix() const {\n        return glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp);\n    }\n\n    \u002F**\n     * @brief 获取相机位置\n     * @return glm::vec3 相机位置\n     *\u002F\n    glm::vec3 getCameraPosition() const {\n        return cameraPos;\n    }\n\n    \u002F**\n     * @brief 获取相机朝向\n     * @return glm::vec3 相机朝向\n     *\u002F\n    glm::vec3 getCameraFront() const {\n        return cameraFront;\n    }\n\n    \u002F**\n     * @brief 设置相机位置\n     * @param pos 新的相机位置\n     *\u002F\n    void setCameraPosition(const glm::vec3& pos) {\n        cameraPos = pos;\n    }\n\n    \u002F**\n     * @brief 设置鼠标敏感度\n     * @param sensitivity 新的敏感度值\n     *\u002F\n    void setSensitivity(float sensitivity) {\n        mouseSensitivity = std::max(0.01f, std::min(0.5f, sensitivity));\n    }\n\n    \u002F**\n     * @brief 获取当前敏感度\n     * @return float 当前敏感度\n     *\u002F\n    float getSensitivity() const {\n        return mouseSensitivity;\n    }\n\n    \u002F**\n     * @brief 获取统计信息\n     * @return std::string 统计信息字符串\n     *\u002F\n    std::string getStats() const {\n        std::string stats = \"DPI 稳定器统计:\\n\";\n        stats += \"总移动次数: \" + std::to_string(totalMoves) + \"\\n\";\n        stats += \"过滤次数: \" + std::to_string(filteredMoves) + \"\\n\";\n        \n        float filterRate = (totalMoves > 0) ? (float)filteredMoves \u002F totalMoves * 100.0f : 0.0f;\n        stats += \"过滤率: \" + std::to_string(filterRate) + \"%\\n\";\n        stats += \"平均偏移: \" + std::to_string(averageOffset) + \" 像素\\n\";\n        stats += \"当前敏感度: \" + std::to_string(mouseSensitivity) + \"\\n\";\n        return stats;\n    }\n\n    \u002F**\n     * @brief 重置统计信息\n     *\u002F\n    void resetStats() {\n        totalMoves = 0;\n        filteredMoves = 0;\n        averageOffset = 0.0f;\n    }\n\n    \u002F**\n     * @brief 设置最大偏移量\n     * @param maxOffset 新的最大偏移量\n     *\u002F\n    void setMaxOffset(float maxOffset) {\n        this->maxOffset = maxOffset;\n    }\n\n    \u002F**\n     * @brief 获取是否被捕获\n     * @return bool 是否被捕获\n     *\u002F\n    bool isMouseCaptured() const {\n        return mouseCaptured;\n    }\n};\n\n",[17,2479,2480,2485,2489,2493,2498,2503,2507,2511,2516,2521,2525,2530,2535,2540,2545,2550,2555,2560,2565,2570,2575,2580,2585,2590,2595,2600,2605,2610,2614,2619,2624,2629,2634,2639,2643,2648,2653,2658,2663,2667,2672,2677,2682,2687,2691,2696,2701,2706,2711,2716,2721,2726,2731,2736,2741,2746,2751,2756,2760,2764,2768,2773,2777,2782,2787,2791,2795,2799,2804,2809,2813,2818,2823,2828,2832,2837,2842,2846,2851,2856,2860,2865,2869,2873,2877,2882,2886,2891,2896,2901,2906,2911,2915,2919,2923,2927,2932,2937,2942,2946,2951,2956,2960,2965,2970,2975,2980,2985,2990,2994,2998,3003,3008,3013,3017,3021,3025,3030,3035,3040,3045,3049,3054,3059,3064,3069,3073,3077,3081,3086,3091,3096,3100,3105,3110,3115,3119,3124,3129,3134,3138,3143,3148,3153,3157,3162,3167,3171,3175,3179,3184,3188,3193,3198,3203,3208,3213,3218,3222,3226,3230,3235,3239,3243,3248,3253,3258,3263,3268,3273,3277,3282,3287,3292,3296,3300,3304,3309,3314,3319,3324,3329,3333,3338,3343,3347,3351,3356,3360,3365,3370,3375,3380,3385,3390,3395,3400,3405,3409,3413,3417,3421,3426,3430,3434,3439,3443,3447,3451,3456,3460,3464,3468,3472,3476,3480,3484,3488,3492,3496,3501,3506,3510,3515,3520,3524,3528,3532,3537,3542,3546,3551,3556,3560,3564,3568,3573,3578,3582,3587,3592,3596,3600,3604,3609,3614,3618,3623,3628,3632,3636,3640,3645,3650,3654,3659,3664,3668,3672,3676,3681,3686,3690,3695,3700,3704,3708,3712,3717,3722,3726,3731,3736,3741,3746,3750,3755,3760,3765,3770,3775,3779,3783,3787,3792,3796,3801,3806,3811,3816,3820,3824,3828,3833,3838,3842,3847,3852,3856,3860,3864,3869,3874,3878,3883,3888,3892],{"__ignoreMap":19},[74,2481,2482],{"class":76,"line":77},[74,2483,2484],{},"#pragma once\n",[74,2486,2487],{"class":76,"line":116},[74,2488,543],{"emptyLinePlaceholder":542},[74,2490,2491],{"class":76,"line":271},[74,2492,502],{},[74,2494,2495],{"class":76,"line":277},[74,2496,2497],{},"#include \u003Cglm\u002Fglm.hpp>\n",[74,2499,2500],{"class":76,"line":322},[74,2501,2502],{},"#include \u003Cglm\u002Fgtc\u002Fmatrix_transform.hpp>\n",[74,2504,2505],{"class":76,"line":409},[74,2506,512],{},[74,2508,2509],{"class":76,"line":415},[74,2510,517],{},[74,2512,2513],{"class":76,"line":421},[74,2514,2515],{},"#include \u003Cstring>\n",[74,2517,2518],{"class":76,"line":427},[74,2519,2520],{},"#include \u003Calgorithm>\n",[74,2522,2523],{"class":76,"line":433},[74,2524,543],{"emptyLinePlaceholder":542},[74,2526,2527],{"class":76,"line":439},[74,2528,2529],{},"\u002F**\n",[74,2531,2532],{"class":76,"line":445},[74,2533,2534],{}," * @brief DPI 稳定器类 - 解决 Windows 11 等系统上的鼠标 DPI 漂移问题\n",[74,2536,2537],{"class":76,"line":451},[74,2538,2539],{}," * \n",[74,2541,2542],{"class":76,"line":560},[74,2543,2544],{}," * 功能特性：\n",[74,2546,2547],{"class":76,"line":566},[74,2548,2549],{}," * - 异常偏移检测和过滤\n",[74,2551,2552],{"class":76,"line":572},[74,2553,2554],{}," * - 动态敏感度调整\n",[74,2556,2557],{"class":76,"line":578},[74,2558,2559],{}," * - DPI 感知设置\n",[74,2561,2562],{"class":76,"line":583},[74,2563,2564],{}," * - 平滑的鼠标输入处理\n",[74,2566,2567],{"class":76,"line":588},[74,2568,2569],{}," *\u002F\n",[74,2571,2572],{"class":76,"line":594},[74,2573,2574],{},"class DPIStabilizer {\n",[74,2576,2577],{"class":76,"line":600},[74,2578,2579],{},"private:\n",[74,2581,2582],{"class":76,"line":606},[74,2583,2584],{},"    \u002F\u002F 鼠标状态变量\n",[74,2586,2587],{"class":76,"line":612},[74,2588,2589],{},"    float yawAngle;\n",[74,2591,2592],{"class":76,"line":618},[74,2593,2594],{},"    float pitchAngle;\n",[74,2596,2597],{"class":76,"line":624},[74,2598,2599],{},"    float lastX, lastY;\n",[74,2601,2602],{"class":76,"line":630},[74,2603,2604],{},"    bool firstMouse;\n",[74,2606,2607],{"class":76,"line":636},[74,2608,2609],{},"    bool mouseCaptured;\n",[74,2611,2612],{"class":76,"line":642},[74,2613,755],{},[74,2615,2616],{"class":76,"line":647},[74,2617,2618],{},"    \u002F\u002F DPI 稳定性参数\n",[74,2620,2621],{"class":76,"line":652},[74,2622,2623],{},"    float mouseSensitivity;\n",[74,2625,2626],{"class":76,"line":658},[74,2627,2628],{},"    float maxOffset;           \u002F\u002F 最大允许偏移量\n",[74,2630,2631],{"class":76,"line":664},[74,2632,2633],{},"    int frameCount;           \u002F\u002F 帧计数器\n",[74,2635,2636],{"class":76,"line":670},[74,2637,2638],{},"    float smoothingFactor;    \u002F\u002F 平滑因子\n",[74,2640,2641],{"class":76,"line":676},[74,2642,755],{},[74,2644,2645],{"class":76,"line":681},[74,2646,2647],{},"    \u002F\u002F 相机相关\n",[74,2649,2650],{"class":76,"line":687},[74,2651,2652],{},"    glm::vec3 cameraPos;\n",[74,2654,2655],{"class":76,"line":693},[74,2656,2657],{},"    glm::vec3 cameraFront;\n",[74,2659,2660],{"class":76,"line":699},[74,2661,2662],{},"    glm::vec3 cameraUp;\n",[74,2664,2665],{"class":76,"line":705},[74,2666,755],{},[74,2668,2669],{"class":76,"line":711},[74,2670,2671],{},"    \u002F\u002F 统计信息\n",[74,2673,2674],{"class":76,"line":717},[74,2675,2676],{},"    int totalMoves;\n",[74,2678,2679],{"class":76,"line":723},[74,2680,2681],{},"    int filteredMoves;\n",[74,2683,2684],{"class":76,"line":728},[74,2685,2686],{},"    float averageOffset;\n",[74,2688,2689],{"class":76,"line":734},[74,2690,543],{"emptyLinePlaceholder":542},[74,2692,2693],{"class":76,"line":740},[74,2694,2695],{},"public:\n",[74,2697,2698],{"class":76,"line":746},[74,2699,2700],{},"    \u002F**\n",[74,2702,2703],{"class":76,"line":752},[74,2704,2705],{},"     * @brief 构造函数\n",[74,2707,2708],{"class":76,"line":758},[74,2709,2710],{},"     * @param sensitivity 初始鼠标敏感度 (默认 0.1f)\n",[74,2712,2713],{"class":76,"line":764},[74,2714,2715],{},"     * @param maxOffset 最大允许偏移量 (默认 50.0f)\n",[74,2717,2718],{"class":76,"line":770},[74,2719,2720],{},"     * @param smoothing 平滑因子 (默认 0.8f)\n",[74,2722,2723],{"class":76,"line":776},[74,2724,2725],{},"     *\u002F\n",[74,2727,2728],{"class":76,"line":782},[74,2729,2730],{},"    DPIStabilizer(float sensitivity = 0.1f, float maxOffset = 50.0f, float smoothing = 0.8f)\n",[74,2732,2733],{"class":76,"line":787},[74,2734,2735],{},"        : yawAngle(-90.0f), pitchAngle(0.0f), lastX(400.0f), lastY(300.0f)\n",[74,2737,2738],{"class":76,"line":792},[74,2739,2740],{},"        , firstMouse(true), mouseCaptured(true)\n",[74,2742,2743],{"class":76,"line":798},[74,2744,2745],{},"        , mouseSensitivity(sensitivity), maxOffset(maxOffset), frameCount(0)\n",[74,2747,2748],{"class":76,"line":804},[74,2749,2750],{},"        , smoothingFactor(smoothing), totalMoves(0), filteredMoves(0), averageOffset(0.0f)\n",[74,2752,2753],{"class":76,"line":810},[74,2754,2755],{},"        , cameraPos(0.0f, 0.0f, 3.0f), cameraFront(0.0f, 0.0f, -1.0f), cameraUp(0.0f, 1.0f, 0.0f) {\n",[74,2757,2758],{"class":76,"line":816},[74,2759,639],{},[74,2761,2762],{"class":76,"line":821},[74,2763,543],{"emptyLinePlaceholder":542},[74,2765,2766],{"class":76,"line":827},[74,2767,2700],{},[74,2769,2770],{"class":76,"line":833},[74,2771,2772],{},"     * @brief 设置 DPI 感知提示（在创建窗口前调用）\n",[74,2774,2775],{"class":76,"line":839},[74,2776,2725],{},[74,2778,2779],{"class":76,"line":844},[74,2780,2781],{},"    static void setDPIHint() {\n",[74,2783,2784],{"class":76,"line":850},[74,2785,2786],{},"        glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE);\n",[74,2788,2789],{"class":76,"line":856},[74,2790,639],{},[74,2792,2793],{"class":76,"line":861},[74,2794,543],{"emptyLinePlaceholder":542},[74,2796,2797],{"class":76,"line":867},[74,2798,2700],{},[74,2800,2801],{"class":76,"line":873},[74,2802,2803],{},"     * @brief 初始化 DPI 稳定器（在窗口创建后调用）\n",[74,2805,2806],{"class":76,"line":879},[74,2807,2808],{},"     * @param window GLFW 窗口指针\n",[74,2810,2811],{"class":76,"line":885},[74,2812,2725],{},[74,2814,2815],{"class":76,"line":891},[74,2816,2817],{},"    void initialize(GLFWwindow* window) {\n",[74,2819,2820],{"class":76,"line":896},[74,2821,2822],{},"        \u002F\u002F 设置用户指针，用于静态回调函数\n",[74,2824,2825],{"class":76,"line":902},[74,2826,2827],{},"        glfwSetWindowUserPointer(window, this);\n",[74,2829,2830],{"class":76,"line":908},[74,2831,2217],{},[74,2833,2834],{"class":76,"line":914},[74,2835,2836],{},"        \u002F\u002F 设置鼠标回调\n",[74,2838,2839],{"class":76,"line":920},[74,2840,2841],{},"        glfwSetCursorPosCallback(window, mouseCallback);\n",[74,2843,2844],{"class":76,"line":926},[74,2845,2217],{},[74,2847,2848],{"class":76,"line":932},[74,2849,2850],{},"        \u002F\u002F 捕获鼠标\n",[74,2852,2853],{"class":76,"line":937},[74,2854,2855],{},"        glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);\n",[74,2857,2858],{"class":76,"line":942},[74,2859,2217],{},[74,2861,2862],{"class":76,"line":948},[74,2863,2864],{},"        std::cout \u003C\u003C \"DPI 稳定器已初始化\" \u003C\u003C std::endl;\n",[74,2866,2867],{"class":76,"line":954},[74,2868,639],{},[74,2870,2871],{"class":76,"line":960},[74,2872,543],{"emptyLinePlaceholder":542},[74,2874,2875],{"class":76,"line":966},[74,2876,2700],{},[74,2878,2879],{"class":76,"line":972},[74,2880,2881],{},"     * @brief 鼠标回调函数（静态，供 GLFW 调用）\n",[74,2883,2884],{"class":76,"line":978},[74,2885,2725],{},[74,2887,2888],{"class":76,"line":983},[74,2889,2890],{},"    static void mouseCallback(GLFWwindow* window, double xpos, double ypos) {\n",[74,2892,2893],{"class":76,"line":989},[74,2894,2895],{},"        \u002F\u002F 获取实例指针（通过用户指针存储）\n",[74,2897,2898],{"class":76,"line":995},[74,2899,2900],{},"        DPIStabilizer* stabilizer = static_cast\u003CDPIStabilizer*>(glfwGetWindowUserPointer(window));\n",[74,2902,2903],{"class":76,"line":1001},[74,2904,2905],{},"        if (stabilizer) {\n",[74,2907,2908],{"class":76,"line":1007},[74,2909,2910],{},"            stabilizer->processMouseInput(xpos, ypos);\n",[74,2912,2913],{"class":76,"line":1013},[74,2914,1063],{},[74,2916,2917],{"class":76,"line":1019},[74,2918,639],{},[74,2920,2921],{"class":76,"line":1025},[74,2922,543],{"emptyLinePlaceholder":542},[74,2924,2925],{"class":76,"line":1031},[74,2926,2700],{},[74,2928,2929],{"class":76,"line":1037},[74,2930,2931],{},"     * @brief 处理鼠标输入\n",[74,2933,2934],{"class":76,"line":1043},[74,2935,2936],{},"     * @param xpos 鼠标 X 坐标\n",[74,2938,2939],{"class":76,"line":1049},[74,2940,2941],{},"     * @param ypos 鼠标 Y 坐标\n",[74,2943,2944],{"class":76,"line":1054},[74,2945,2725],{},[74,2947,2948],{"class":76,"line":1060},[74,2949,2950],{},"    void processMouseInput(double xpos, double ypos) {\n",[74,2952,2953],{"class":76,"line":1066},[74,2954,2955],{},"        if (!mouseCaptured) return;\n",[74,2957,2958],{"class":76,"line":1071},[74,2959,2217],{},[74,2961,2962],{"class":76,"line":1076},[74,2963,2964],{},"        \u002F\u002F 第一次鼠标移动\n",[74,2966,2967],{"class":76,"line":1082},[74,2968,2969],{},"        if (firstMouse) {\n",[74,2971,2972],{"class":76,"line":1087},[74,2973,2974],{},"            lastX = xpos;\n",[74,2976,2977],{"class":76,"line":1092},[74,2978,2979],{},"            lastY = ypos;\n",[74,2981,2982],{"class":76,"line":1098},[74,2983,2984],{},"            firstMouse = false;\n",[74,2986,2987],{"class":76,"line":1103},[74,2988,2989],{},"            return;\n",[74,2991,2992],{"class":76,"line":1108},[74,2993,1063],{},[74,2995,2996],{"class":76,"line":1113},[74,2997,543],{"emptyLinePlaceholder":542},[74,2999,3000],{"class":76,"line":1118},[74,3001,3002],{},"        \u002F\u002F 计算偏移量\n",[74,3004,3005],{"class":76,"line":1123},[74,3006,3007],{},"        float xoffset = xpos - lastX;\n",[74,3009,3010],{"class":76,"line":1129},[74,3011,3012],{},"        float yoffset = lastY - ypos; \u002F\u002F Y 轴反向\n",[74,3014,3015],{"class":76,"line":1134},[74,3016,767],{},[74,3018,3019],{"class":76,"line":1140},[74,3020,773],{},[74,3022,3023],{"class":76,"line":1145},[74,3024,543],{"emptyLinePlaceholder":542},[74,3026,3027],{"class":76,"line":1150},[74,3028,3029],{},"        \u002F\u002F 统计信息\n",[74,3031,3032],{"class":76,"line":1156},[74,3033,3034],{},"        totalMoves++;\n",[74,3036,3037],{"class":76,"line":1161},[74,3038,3039],{},"        float currentOffset = std::sqrt(xoffset * xoffset + yoffset * yoffset);\n",[74,3041,3042],{"class":76,"line":1167},[74,3043,3044],{},"        averageOffset = (averageOffset * (totalMoves - 1) + currentOffset) \u002F totalMoves;\n",[74,3046,3047],{"class":76,"line":1173},[74,3048,543],{"emptyLinePlaceholder":542},[74,3050,3051],{"class":76,"line":1179},[74,3052,3053],{},"        \u002F\u002F DPI 漂移检测\n",[74,3055,3056],{"class":76,"line":1184},[74,3057,3058],{},"        if (currentOffset > maxOffset) {\n",[74,3060,3061],{"class":76,"line":1190},[74,3062,3063],{},"            filteredMoves++;\n",[74,3065,3066],{"class":76,"line":1196},[74,3067,3068],{},"            std::cout \u003C\u003C \"检测到异常偏移: \" \u003C\u003C currentOffset \u003C\u003C \" 像素，已过滤\" \u003C\u003C std::endl;\n",[74,3070,3071],{"class":76,"line":1202},[74,3072,2989],{},[74,3074,3075],{"class":76,"line":1207},[74,3076,1063],{},[74,3078,3079],{"class":76,"line":1213},[74,3080,543],{"emptyLinePlaceholder":542},[74,3082,3083],{"class":76,"line":1219},[74,3084,3085],{},"        \u002F\u002F 应用敏感度\n",[74,3087,3088],{"class":76,"line":1224},[74,3089,3090],{},"        xoffset *= mouseSensitivity;\n",[74,3092,3093],{"class":76,"line":1230},[74,3094,3095],{},"        yoffset *= mouseSensitivity;\n",[74,3097,3098],{"class":76,"line":1236},[74,3099,543],{"emptyLinePlaceholder":542},[74,3101,3102],{"class":76,"line":1242},[74,3103,3104],{},"        \u002F\u002F 平滑处理\n",[74,3106,3107],{"class":76,"line":1248},[74,3108,3109],{},"        xoffset *= smoothingFactor;\n",[74,3111,3112],{"class":76,"line":1254},[74,3113,3114],{},"        yoffset *= smoothingFactor;\n",[74,3116,3117],{"class":76,"line":1260},[74,3118,543],{"emptyLinePlaceholder":542},[74,3120,3121],{"class":76,"line":1266},[74,3122,3123],{},"        \u002F\u002F 更新角度\n",[74,3125,3126],{"class":76,"line":1272},[74,3127,3128],{},"        yawAngle += xoffset;\n",[74,3130,3131],{"class":76,"line":1277},[74,3132,3133],{},"        pitchAngle += yoffset;\n",[74,3135,3136],{"class":76,"line":1283},[74,3137,543],{"emptyLinePlaceholder":542},[74,3139,3140],{"class":76,"line":1289},[74,3141,3142],{},"        \u002F\u002F 限制俯仰角\n",[74,3144,3145],{"class":76,"line":1295},[74,3146,3147],{},"        if (pitchAngle > 89.0f) pitchAngle = 89.0f;\n",[74,3149,3150],{"class":76,"line":1301},[74,3151,3152],{},"        if (pitchAngle \u003C -89.0f) pitchAngle = -89.0f;\n",[74,3154,3155],{"class":76,"line":1307},[74,3156,543],{"emptyLinePlaceholder":542},[74,3158,3159],{"class":76,"line":1313},[74,3160,3161],{},"        \u002F\u002F 计算新的相机方向\n",[74,3163,3164],{"class":76,"line":1319},[74,3165,3166],{},"        updateCameraDirection();\n",[74,3168,3169],{"class":76,"line":1324},[74,3170,639],{},[74,3172,3173],{"class":76,"line":1330},[74,3174,543],{"emptyLinePlaceholder":542},[74,3176,3177],{"class":76,"line":1336},[74,3178,2700],{},[74,3180,3181],{"class":76,"line":1342},[74,3182,3183],{},"     * @brief 更新相机方向\n",[74,3185,3186],{"class":76,"line":1348},[74,3187,2725],{},[74,3189,3190],{"class":76,"line":1354},[74,3191,3192],{},"    void updateCameraDirection() {\n",[74,3194,3195],{"class":76,"line":1360},[74,3196,3197],{},"        glm::vec3 front;\n",[74,3199,3200],{"class":76,"line":1365},[74,3201,3202],{},"        front.x = cos(glm::radians(yawAngle)) * cos(glm::radians(pitchAngle));\n",[74,3204,3205],{"class":76,"line":1371},[74,3206,3207],{},"        front.y = sin(glm::radians(pitchAngle));\n",[74,3209,3210],{"class":76,"line":1377},[74,3211,3212],{},"        front.z = sin(glm::radians(yawAngle)) * cos(glm::radians(pitchAngle));\n",[74,3214,3215],{"class":76,"line":1382},[74,3216,3217],{},"        cameraFront = glm::normalize(front);\n",[74,3219,3220],{"class":76,"line":1387},[74,3221,639],{},[74,3223,3224],{"class":76,"line":1392},[74,3225,543],{"emptyLinePlaceholder":542},[74,3227,3228],{"class":76,"line":1398},[74,3229,2700],{},[74,3231,3232],{"class":76,"line":1404},[74,3233,3234],{},"     * @brief 处理键盘输入\n",[74,3236,3237],{"class":76,"line":1409},[74,3238,2808],{},[74,3240,3241],{"class":76,"line":1415},[74,3242,2725],{},[74,3244,3245],{"class":76,"line":1421},[74,3246,3247],{},"    void processKeyboardInput(GLFWwindow* window) {\n",[74,3249,3250],{"class":76,"line":1426},[74,3251,3252],{},"        \u002F\u002F 动态调整敏感度\n",[74,3254,3255],{"class":76,"line":1431},[74,3256,3257],{},"        if (glfwGetKey(window, GLFW_KEY_EQUAL) == GLFW_PRESS) {\n",[74,3259,3260],{"class":76,"line":1436},[74,3261,3262],{},"            mouseSensitivity += 0.01f;\n",[74,3264,3265],{"class":76,"line":1442},[74,3266,3267],{},"            if (mouseSensitivity > 0.5f) mouseSensitivity = 0.5f;\n",[74,3269,3270],{"class":76,"line":1448},[74,3271,3272],{},"            std::cout \u003C\u003C \"鼠标敏感度: \" \u003C\u003C mouseSensitivity \u003C\u003C std::endl;\n",[74,3274,3275],{"class":76,"line":1454},[74,3276,1063],{},[74,3278,3279],{"class":76,"line":1460},[74,3280,3281],{},"        if (glfwGetKey(window, GLFW_KEY_MINUS) == GLFW_PRESS) {\n",[74,3283,3284],{"class":76,"line":1466},[74,3285,3286],{},"            mouseSensitivity -= 0.01f;\n",[74,3288,3289],{"class":76,"line":1471},[74,3290,3291],{},"            if (mouseSensitivity \u003C 0.01f) mouseSensitivity = 0.01f;\n",[74,3293,3294],{"class":76,"line":1477},[74,3295,3272],{},[74,3297,3298],{"class":76,"line":1483},[74,3299,1063],{},[74,3301,3302],{"class":76,"line":1489},[74,3303,543],{"emptyLinePlaceholder":542},[74,3305,3306],{"class":76,"line":1495},[74,3307,3308],{},"        \u002F\u002F Ctrl+Alt 切换鼠标捕获\n",[74,3310,3311],{"class":76,"line":1501},[74,3312,3313],{},"        bool ctrlPressed = glfwGetKey(window, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS || \n",[74,3315,3316],{"class":76,"line":1507},[74,3317,3318],{},"                          glfwGetKey(window, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS;\n",[74,3320,3321],{"class":76,"line":1512},[74,3322,3323],{},"        bool altPressed = glfwGetKey(window, GLFW_KEY_LEFT_ALT) == GLFW_PRESS || \n",[74,3325,3326],{"class":76,"line":1517},[74,3327,3328],{},"                          glfwGetKey(window, GLFW_KEY_RIGHT_ALT) == GLFW_PRESS;\n",[74,3330,3331],{"class":76,"line":1523},[74,3332,2217],{},[74,3334,3335],{"class":76,"line":1529},[74,3336,3337],{},"        if (ctrlPressed && altPressed) {\n",[74,3339,3340],{"class":76,"line":1535},[74,3341,3342],{},"            toggleMouseCapture(window);\n",[74,3344,3345],{"class":76,"line":1540},[74,3346,1063],{},[74,3348,3349],{"class":76,"line":1546},[74,3350,543],{"emptyLinePlaceholder":542},[74,3352,3353],{"class":76,"line":1552},[74,3354,3355],{},"        \u002F\u002F WASD 移动（仅在捕获模式下）\n",[74,3357,3358],{"class":76,"line":1557},[74,3359,992],{},[74,3361,3362],{"class":76,"line":1563},[74,3363,3364],{},"            float cameraSpeed = 0.05f;\n",[74,3366,3367],{"class":76,"line":1569},[74,3368,3369],{},"            if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)\n",[74,3371,3372],{"class":76,"line":1575},[74,3373,3374],{},"                cameraPos += cameraSpeed * cameraFront;\n",[74,3376,3377],{"class":76,"line":1580},[74,3378,3379],{},"            if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)\n",[74,3381,3382],{"class":76,"line":1585},[74,3383,3384],{},"                cameraPos -= cameraSpeed * cameraFront;\n",[74,3386,3387],{"class":76,"line":1590},[74,3388,3389],{},"            if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)\n",[74,3391,3392],{"class":76,"line":1596},[74,3393,3394],{},"                cameraPos -= glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;\n",[74,3396,3397],{"class":76,"line":1602},[74,3398,3399],{},"            if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)\n",[74,3401,3402],{"class":76,"line":1607},[74,3403,3404],{},"                cameraPos += glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;\n",[74,3406,3407],{"class":76,"line":1613},[74,3408,1063],{},[74,3410,3411],{"class":76,"line":1619},[74,3412,639],{},[74,3414,3415],{"class":76,"line":1625},[74,3416,543],{"emptyLinePlaceholder":542},[74,3418,3419],{"class":76,"line":1631},[74,3420,2700],{},[74,3422,3423],{"class":76,"line":1637},[74,3424,3425],{},"     * @brief 切换鼠标捕获状态\n",[74,3427,3428],{"class":76,"line":1642},[74,3429,2808],{},[74,3431,3432],{"class":76,"line":1648},[74,3433,2725],{},[74,3435,3436],{"class":76,"line":1653},[74,3437,3438],{},"    void toggleMouseCapture(GLFWwindow* window) {\n",[74,3440,3441],{"class":76,"line":1658},[74,3442,992],{},[74,3444,3445],{"class":76,"line":1664},[74,3446,1004],{},[74,3448,3449],{"class":76,"line":1670},[74,3450,1010],{},[74,3452,3453],{"class":76,"line":1676},[74,3454,3455],{},"            firstMouse = true;\n",[74,3457,3458],{"class":76,"line":1681},[74,3459,1022],{},[74,3461,3462],{"class":76,"line":1687},[74,3463,1028],{},[74,3465,3466],{"class":76,"line":1692},[74,3467,1040],{},[74,3469,3470],{"class":76,"line":1697},[74,3471,1046],{},[74,3473,3474],{"class":76,"line":1703},[74,3475,3455],{},[74,3477,3478],{"class":76,"line":1709},[74,3479,1057],{},[74,3481,3482],{"class":76,"line":1715},[74,3483,1063],{},[74,3485,3486],{"class":76,"line":1720},[74,3487,639],{},[74,3489,3490],{"class":76,"line":1725},[74,3491,543],{"emptyLinePlaceholder":542},[74,3493,3494],{"class":76,"line":1730},[74,3495,2700],{},[74,3497,3498],{"class":76,"line":1735},[74,3499,3500],{},"     * @brief 获取 View 矩阵\n",[74,3502,3503],{"class":76,"line":1741},[74,3504,3505],{},"     * @return glm::mat4 View 矩阵\n",[74,3507,3508],{"class":76,"line":1746},[74,3509,2725],{},[74,3511,3512],{"class":76,"line":1752},[74,3513,3514],{},"    glm::mat4 getViewMatrix() const {\n",[74,3516,3517],{"class":76,"line":1757},[74,3518,3519],{},"        return glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp);\n",[74,3521,3522],{"class":76,"line":1763},[74,3523,639],{},[74,3525,3526],{"class":76,"line":1768},[74,3527,543],{"emptyLinePlaceholder":542},[74,3529,3530],{"class":76,"line":1773},[74,3531,2700],{},[74,3533,3534],{"class":76,"line":1778},[74,3535,3536],{},"     * @brief 获取相机位置\n",[74,3538,3539],{"class":76,"line":1784},[74,3540,3541],{},"     * @return glm::vec3 相机位置\n",[74,3543,3544],{"class":76,"line":1789},[74,3545,2725],{},[74,3547,3548],{"class":76,"line":1794},[74,3549,3550],{},"    glm::vec3 getCameraPosition() const {\n",[74,3552,3553],{"class":76,"line":1799},[74,3554,3555],{},"        return cameraPos;\n",[74,3557,3558],{"class":76,"line":1804},[74,3559,639],{},[74,3561,3562],{"class":76,"line":1809},[74,3563,543],{"emptyLinePlaceholder":542},[74,3565,3566],{"class":76,"line":1814},[74,3567,2700],{},[74,3569,3570],{"class":76,"line":1819},[74,3571,3572],{},"     * @brief 获取相机朝向\n",[74,3574,3575],{"class":76,"line":1824},[74,3576,3577],{},"     * @return glm::vec3 相机朝向\n",[74,3579,3580],{"class":76,"line":1829},[74,3581,2725],{},[74,3583,3584],{"class":76,"line":1835},[74,3585,3586],{},"    glm::vec3 getCameraFront() const {\n",[74,3588,3589],{"class":76,"line":1841},[74,3590,3591],{},"        return cameraFront;\n",[74,3593,3594],{"class":76,"line":1847},[74,3595,639],{},[74,3597,3598],{"class":76,"line":1852},[74,3599,543],{"emptyLinePlaceholder":542},[74,3601,3602],{"class":76,"line":1858},[74,3603,2700],{},[74,3605,3606],{"class":76,"line":1863},[74,3607,3608],{},"     * @brief 设置相机位置\n",[74,3610,3611],{"class":76,"line":1869},[74,3612,3613],{},"     * @param pos 新的相机位置\n",[74,3615,3616],{"class":76,"line":1875},[74,3617,2725],{},[74,3619,3620],{"class":76,"line":1881},[74,3621,3622],{},"    void setCameraPosition(const glm::vec3& pos) {\n",[74,3624,3625],{"class":76,"line":1887},[74,3626,3627],{},"        cameraPos = pos;\n",[74,3629,3630],{"class":76,"line":1893},[74,3631,639],{},[74,3633,3634],{"class":76,"line":1898},[74,3635,543],{"emptyLinePlaceholder":542},[74,3637,3638],{"class":76,"line":1904},[74,3639,2700],{},[74,3641,3642],{"class":76,"line":1910},[74,3643,3644],{},"     * @brief 设置鼠标敏感度\n",[74,3646,3647],{"class":76,"line":1916},[74,3648,3649],{},"     * @param sensitivity 新的敏感度值\n",[74,3651,3652],{"class":76,"line":1922},[74,3653,2725],{},[74,3655,3656],{"class":76,"line":1928},[74,3657,3658],{},"    void setSensitivity(float sensitivity) {\n",[74,3660,3661],{"class":76,"line":1934},[74,3662,3663],{},"        mouseSensitivity = std::max(0.01f, std::min(0.5f, sensitivity));\n",[74,3665,3666],{"class":76,"line":1940},[74,3667,639],{},[74,3669,3670],{"class":76,"line":1946},[74,3671,543],{"emptyLinePlaceholder":542},[74,3673,3674],{"class":76,"line":1952},[74,3675,2700],{},[74,3677,3678],{"class":76,"line":1957},[74,3679,3680],{},"     * @brief 获取当前敏感度\n",[74,3682,3683],{"class":76,"line":1963},[74,3684,3685],{},"     * @return float 当前敏感度\n",[74,3687,3688],{"class":76,"line":1969},[74,3689,2725],{},[74,3691,3692],{"class":76,"line":1975},[74,3693,3694],{},"    float getSensitivity() const {\n",[74,3696,3697],{"class":76,"line":1980},[74,3698,3699],{},"        return mouseSensitivity;\n",[74,3701,3702],{"class":76,"line":1986},[74,3703,639],{},[74,3705,3706],{"class":76,"line":1991},[74,3707,543],{"emptyLinePlaceholder":542},[74,3709,3710],{"class":76,"line":1996},[74,3711,2700],{},[74,3713,3714],{"class":76,"line":2002},[74,3715,3716],{},"     * @brief 获取统计信息\n",[74,3718,3719],{"class":76,"line":2007},[74,3720,3721],{},"     * @return std::string 统计信息字符串\n",[74,3723,3724],{"class":76,"line":2012},[74,3725,2725],{},[74,3727,3728],{"class":76,"line":2017},[74,3729,3730],{},"    std::string getStats() const {\n",[74,3732,3733],{"class":76,"line":2023},[74,3734,3735],{},"        std::string stats = \"DPI 稳定器统计:\\n\";\n",[74,3737,3738],{"class":76,"line":2029},[74,3739,3740],{},"        stats += \"总移动次数: \" + std::to_string(totalMoves) + \"\\n\";\n",[74,3742,3743],{"class":76,"line":2035},[74,3744,3745],{},"        stats += \"过滤次数: \" + std::to_string(filteredMoves) + \"\\n\";\n",[74,3747,3748],{"class":76,"line":2041},[74,3749,2217],{},[74,3751,3752],{"class":76,"line":2046},[74,3753,3754],{},"        float filterRate = (totalMoves > 0) ? (float)filteredMoves \u002F totalMoves * 100.0f : 0.0f;\n",[74,3756,3757],{"class":76,"line":2052},[74,3758,3759],{},"        stats += \"过滤率: \" + std::to_string(filterRate) + \"%\\n\";\n",[74,3761,3762],{"class":76,"line":2058},[74,3763,3764],{},"        stats += \"平均偏移: \" + std::to_string(averageOffset) + \" 像素\\n\";\n",[74,3766,3767],{"class":76,"line":2064},[74,3768,3769],{},"        stats += \"当前敏感度: \" + std::to_string(mouseSensitivity) + \"\\n\";\n",[74,3771,3772],{"class":76,"line":2070},[74,3773,3774],{},"        return stats;\n",[74,3776,3777],{"class":76,"line":2076},[74,3778,639],{},[74,3780,3781],{"class":76,"line":2081},[74,3782,543],{"emptyLinePlaceholder":542},[74,3784,3785],{"class":76,"line":2087},[74,3786,2700],{},[74,3788,3789],{"class":76,"line":2093},[74,3790,3791],{},"     * @brief 重置统计信息\n",[74,3793,3794],{"class":76,"line":2099},[74,3795,2725],{},[74,3797,3798],{"class":76,"line":2104},[74,3799,3800],{},"    void resetStats() {\n",[74,3802,3803],{"class":76,"line":2110},[74,3804,3805],{},"        totalMoves = 0;\n",[74,3807,3808],{"class":76,"line":2116},[74,3809,3810],{},"        filteredMoves = 0;\n",[74,3812,3813],{"class":76,"line":2122},[74,3814,3815],{},"        averageOffset = 0.0f;\n",[74,3817,3818],{"class":76,"line":2127},[74,3819,639],{},[74,3821,3822],{"class":76,"line":2133},[74,3823,543],{"emptyLinePlaceholder":542},[74,3825,3826],{"class":76,"line":2139},[74,3827,2700],{},[74,3829,3830],{"class":76,"line":2145},[74,3831,3832],{},"     * @brief 设置最大偏移量\n",[74,3834,3835],{"class":76,"line":2150},[74,3836,3837],{},"     * @param maxOffset 新的最大偏移量\n",[74,3839,3840],{"class":76,"line":2156},[74,3841,2725],{},[74,3843,3844],{"class":76,"line":2162},[74,3845,3846],{},"    void setMaxOffset(float maxOffset) {\n",[74,3848,3849],{"class":76,"line":2167},[74,3850,3851],{},"        this->maxOffset = maxOffset;\n",[74,3853,3854],{"class":76,"line":2173},[74,3855,639],{},[74,3857,3858],{"class":76,"line":2179},[74,3859,543],{"emptyLinePlaceholder":542},[74,3861,3862],{"class":76,"line":2185},[74,3863,2700],{},[74,3865,3866],{"class":76,"line":2191},[74,3867,3868],{},"     * @brief 获取是否被捕获\n",[74,3870,3871],{"class":76,"line":2197},[74,3872,3873],{},"     * @return bool 是否被捕获\n",[74,3875,3876],{"class":76,"line":2202},[74,3877,2725],{},[74,3879,3880],{"class":76,"line":2208},[74,3881,3882],{},"    bool isMouseCaptured() const {\n",[74,3884,3885],{"class":76,"line":2214},[74,3886,3887],{},"        return mouseCaptured;\n",[74,3889,3890],{"class":76,"line":2220},[74,3891,639],{},[74,3893,3894],{"class":76,"line":2226},[74,3895,1844],{},[327,3897,3899],{"id":3898},"dpi-稳定器使用说明","DPI 稳定器使用说明",[25,3901,3903],{"id":3902},"功能特性","🎯 功能特性",[56,3905,3907],{"id":3906},"已集成的功能","✅ 已集成的功能：",[3909,3910,3911,3918,3924,3930,3936],"ul",{},[3912,3913,3914,3917],"li",{},[33,3915,3916],{},"异常偏移检测","：自动过滤 DPI 漂移",[3912,3919,3920,3923],{},[33,3921,3922],{},"动态敏感度调整","：实时调节鼠标响应",[3912,3925,3926,3929],{},[33,3927,3928],{},"平滑处理","：减少鼠标抖动",[3912,3931,3932,3935],{},[33,3933,3934],{},"统计信息","：监控过滤效果",[3912,3937,3938,3941],{},[33,3939,3940],{},"鼠标捕获切换","：Ctrl+Alt 释放\u002F捕获鼠标",[25,3943,3945],{"id":3944},"控制方式","🎮 控制方式",[3947,3948,3949,3962],"table",{},[3950,3951,3952],"thead",{},[3953,3954,3955,3959],"tr",{},[3956,3957,3958],"th",{},"按键",[3956,3960,3961],{},"功能",[3963,3964,3965,3975,3985,3995,4005,4015],"tbody",{},[3953,3966,3967,3972],{},[3968,3969,3970],"td",{},[17,3971,460],{},[3968,3973,3974],{},"相机移动（仅在捕获模式下）",[3953,3976,3977,3982],{},[3968,3978,3979],{},[17,3980,3981],{},"鼠标移动",[3968,3983,3984],{},"控制相机视角",[3953,3986,3987,3992],{},[3968,3988,3989],{},[17,3990,3991],{},"Ctrl+Alt",[3968,3993,3994],{},"切换鼠标捕获状态",[3953,3996,3997,4002],{},[3968,3998,3999],{},[17,4000,4001],{},"+",[3968,4003,4004],{},"增加鼠标敏感度",[3953,4006,4007,4012],{},[3968,4008,4009],{},[17,4010,4011],{},"-",[3968,4013,4014],{},"减少鼠标敏感度",[3953,4016,4017,4022],{},[3968,4018,4019],{},[17,4020,4021],{},"T",[3968,4023,4024],{},"显示统计信息",[25,4026,4028],{"id":4027},"技术实现","🔧 技术实现",[56,4030,4032,4033],{"id":4031},"_1-dpi-漂移检测","1. ",[33,4034,4035],{},"DPI 漂移检测",[10,4037,4041],{"className":4038,"code":4039,"language":4040,"meta":19,"style":19},"language-cpp shiki shiki-themes github-light github-dark","\u002F\u002F 检测异常大的偏移量\nif (currentOffset > maxOffset) {\n    \u002F\u002F 过滤异常输入\n    return;\n}\n","cpp",[17,4042,4043,4048,4053,4058,4063],{"__ignoreMap":19},[74,4044,4045],{"class":76,"line":77},[74,4046,4047],{},"\u002F\u002F 检测异常大的偏移量\n",[74,4049,4050],{"class":76,"line":116},[74,4051,4052],{},"if (currentOffset > maxOffset) {\n",[74,4054,4055],{"class":76,"line":271},[74,4056,4057],{},"    \u002F\u002F 过滤异常输入\n",[74,4059,4060],{"class":76,"line":277},[74,4061,4062],{},"    return;\n",[74,4064,4065],{"class":76,"line":322},[74,4066,454],{},[56,4068,4070,4071],{"id":4069},"_2-平滑处理","2. ",[33,4072,3928],{},[10,4074,4076],{"className":4038,"code":4075,"language":4040,"meta":19,"style":19},"\u002F\u002F 应用平滑因子\nxoffset *= smoothingFactor;\nyoffset *= smoothingFactor;\n",[17,4077,4078,4083,4088],{"__ignoreMap":19},[74,4079,4080],{"class":76,"line":77},[74,4081,4082],{},"\u002F\u002F 应用平滑因子\n",[74,4084,4085],{"class":76,"line":116},[74,4086,4087],{},"xoffset *= smoothingFactor;\n",[74,4089,4090],{"class":76,"line":271},[74,4091,4092],{},"yoffset *= smoothingFactor;\n",[56,4094,4096,4097],{"id":4095},"_3-统计监控","3. ",[33,4098,4099],{},"统计监控",[3909,4101,4102,4105,4108,4111],{},[3912,4103,4104],{},"总移动次数",[3912,4106,4107],{},"过滤次数和过滤率",[3912,4109,4110],{},"平均偏移量",[3912,4112,4113],{},"当前敏感度",[25,4115,4117],{"id":4116},"参数说明","📊 参数说明",[56,4119,4121],{"id":4120},"构造函数参数","构造函数参数：",[10,4123,4125],{"className":4038,"code":4124,"language":4040,"meta":19,"style":19},"DPIStabilizer(敏感度, 最大偏移量, 平滑因子)\nDPIStabilizer(0.1f, 50.0f, 0.8f)\n",[17,4126,4127,4132],{"__ignoreMap":19},[74,4128,4129],{"class":76,"line":77},[74,4130,4131],{},"DPIStabilizer(敏感度, 最大偏移量, 平滑因子)\n",[74,4133,4134],{"class":76,"line":116},[74,4135,4136],{},"DPIStabilizer(0.1f, 50.0f, 0.8f)\n",[3909,4138,4139,4145,4151],{},[3912,4140,4141,4144],{},[33,4142,4143],{},"敏感度","：0.01f - 0.5f（默认 0.1f）",[3912,4146,4147,4150],{},[33,4148,4149],{},"最大偏移量","：像素阈值（默认 50.0f）",[3912,4152,4153,4156],{},[33,4154,4155],{},"平滑因子","：0.0f - 1.0f（默认 0.8f）",[25,4158,4160],{"id":4159},"使用方法","🚀 使用方法",[56,4162,4032,4164],{"id":4163},"_1-基本使用",[33,4165,4166],{},"基本使用",[10,4168,4170],{"className":4038,"code":4169,"language":4040,"meta":19,"style":19},"\u002F\u002F 创建实例\nDPIStabilizer dpiStabilizer(0.1f, 50.0f, 0.8f);\n\n\u002F\u002F 在窗口创建前设置 DPI 提示\nDPIStabilizer::setDPIHint();\n\n\u002F\u002F 在窗口创建后初始化\ndpiStabilizer.initialize(window);\n\n\u002F\u002F 在渲染循环中处理输入\ndpiStabilizer.processKeyboardInput(window);\n\n\u002F\u002F 获取 view 矩阵\nmat4 view = dpiStabilizer.getViewMatrix();\n",[17,4171,4172,4177,4182,4186,4191,4196,4200,4205,4210,4214,4219,4224,4228,4233],{"__ignoreMap":19},[74,4173,4174],{"class":76,"line":77},[74,4175,4176],{},"\u002F\u002F 创建实例\n",[74,4178,4179],{"class":76,"line":116},[74,4180,4181],{},"DPIStabilizer dpiStabilizer(0.1f, 50.0f, 0.8f);\n",[74,4183,4184],{"class":76,"line":271},[74,4185,543],{"emptyLinePlaceholder":542},[74,4187,4188],{"class":76,"line":277},[74,4189,4190],{},"\u002F\u002F 在窗口创建前设置 DPI 提示\n",[74,4192,4193],{"class":76,"line":322},[74,4194,4195],{},"DPIStabilizer::setDPIHint();\n",[74,4197,4198],{"class":76,"line":409},[74,4199,543],{"emptyLinePlaceholder":542},[74,4201,4202],{"class":76,"line":415},[74,4203,4204],{},"\u002F\u002F 在窗口创建后初始化\n",[74,4206,4207],{"class":76,"line":421},[74,4208,4209],{},"dpiStabilizer.initialize(window);\n",[74,4211,4212],{"class":76,"line":427},[74,4213,543],{"emptyLinePlaceholder":542},[74,4215,4216],{"class":76,"line":433},[74,4217,4218],{},"\u002F\u002F 在渲染循环中处理输入\n",[74,4220,4221],{"class":76,"line":439},[74,4222,4223],{},"dpiStabilizer.processKeyboardInput(window);\n",[74,4225,4226],{"class":76,"line":445},[74,4227,543],{"emptyLinePlaceholder":542},[74,4229,4230],{"class":76,"line":451},[74,4231,4232],{},"\u002F\u002F 获取 view 矩阵\n",[74,4234,4235],{"class":76,"line":560},[74,4236,4237],{},"mat4 view = dpiStabilizer.getViewMatrix();\n",[56,4239,4070,4241],{"id":4240},"_2-高级功能",[33,4242,4243],{},"高级功能",[10,4245,4247],{"className":4038,"code":4246,"language":4040,"meta":19,"style":19},"\u002F\u002F 获取统计信息\nstd::string stats = dpiStabilizer.getStats();\n\n\u002F\u002F 设置敏感度\ndpiStabilizer.setSensitivity(0.15f);\n\n\u002F\u002F 重置统计\ndpiStabilizer.resetStats();\n\n\u002F\u002F 检查捕获状态\nbool captured = dpiStabilizer.isMouseCaptured();\n",[17,4248,4249,4254,4259,4263,4268,4273,4277,4282,4287,4291,4296],{"__ignoreMap":19},[74,4250,4251],{"class":76,"line":77},[74,4252,4253],{},"\u002F\u002F 获取统计信息\n",[74,4255,4256],{"class":76,"line":116},[74,4257,4258],{},"std::string stats = dpiStabilizer.getStats();\n",[74,4260,4261],{"class":76,"line":271},[74,4262,543],{"emptyLinePlaceholder":542},[74,4264,4265],{"class":76,"line":277},[74,4266,4267],{},"\u002F\u002F 设置敏感度\n",[74,4269,4270],{"class":76,"line":322},[74,4271,4272],{},"dpiStabilizer.setSensitivity(0.15f);\n",[74,4274,4275],{"class":76,"line":409},[74,4276,543],{"emptyLinePlaceholder":542},[74,4278,4279],{"class":76,"line":415},[74,4280,4281],{},"\u002F\u002F 重置统计\n",[74,4283,4284],{"class":76,"line":421},[74,4285,4286],{},"dpiStabilizer.resetStats();\n",[74,4288,4289],{"class":76,"line":427},[74,4290,543],{"emptyLinePlaceholder":542},[74,4292,4293],{"class":76,"line":433},[74,4294,4295],{},"\u002F\u002F 检查捕获状态\n",[74,4297,4298],{"class":76,"line":439},[74,4299,4300],{},"bool captured = dpiStabilizer.isMouseCaptured();\n",[25,4302,4304],{"id":4303},"优势","🎯 优势",[4306,4307,4308,4314,4320,4326,4331],"ol",{},[3912,4309,4310,4313],{},[33,4311,4312],{},"模块化设计","：易于集成和维护",[3912,4315,4316,4319],{},[33,4317,4318],{},"自动过滤","：无需手动处理 DPI 漂移",[3912,4321,4322,4325],{},[33,4323,4324],{},"实时调整","：运行时动态调节参数",[3912,4327,4328,4330],{},[33,4329,4099],{},"：了解过滤效果",[3912,4332,4333,4336],{},[33,4334,4335],{},"跨平台兼容","：支持不同操作系统",[25,4338,4340],{"id":4339},"调试技巧","🔍 调试技巧",[4306,4342,4343,4349,4355,4361],{},[3912,4344,4345,4348],{},[33,4346,4347],{},"按 T 键","查看统计信息",[3912,4350,4351,4354],{},[33,4352,4353],{},"调整敏感度","找到最佳设置",[3912,4356,4357,4360],{},[33,4358,4359],{},"监控过滤率","了解 DPI 漂移情况",[3912,4362,4363,4366],{},[33,4364,4365],{},"使用 Ctrl+Alt","释放鼠标进行调试",[25,4368,4370],{"id":4369},"性能优化","📈 性能优化",[3909,4372,4373,4376,4379,4382],{},[3912,4374,4375],{},"使用静态回调函数减少开销",[3912,4377,4378],{},"智能过滤减少不必要的计算",[3912,4380,4381],{},"平滑处理提升用户体验",[3912,4383,4384],{},"统计信息帮助性能调优",[21,4386,4387],{},"显示imgui窗口的完整代码：",[10,4389,4391],{"className":68,"code":4390,"language":70,"meta":19,"style":19},"#include \u003Cglad\u002Fglad.h>\n#include \u003CGLFW\u002Fglfw3.h>\n#include \u003Copencv4\u002Fopencv2\u002Fopencv.hpp>\n#include \u003Ciostream>\n#include \u003Ccmath>\n#include \u003Cfilesystem>\n#include \u003Cimgui.h>\n#include \u003Cimgui_impl_glfw.h>\n#include \u003Cimgui_impl_opengl3.h>\n#include \"utils\u002Fshader.h\"\n#include \"utils\u002FglmCompute.h\"\n#include \"utils\u002Flogger.h\"\n#include \"include\u002Fdpi_stabilizer.h\"\n#include \"include\u002Fimgui_manager.h\"\n\nusing namespace cv;\nusing namespace glm;\n\n\u002F\u002F 窗口大小改变时的回调函数\nvoid framebuffer_size_callback(GLFWwindow* window, int width, int height) {\n    glViewport(0, 0, width, height);\n}\n\nstd::string getCurrentWorkingDirectory() {\n    namespace fs = std::filesystem;\n    try {\n        \u002F\u002F current_path() 返回当前工作目录\n        return fs::current_path().string();\n    } catch (const fs::filesystem_error& e) {\n        std::cerr \u003C\u003C \"Filesystem Error: \" \u003C\u003C e.what() \u003C\u003C std::endl;\n        return \"\";\n    }\n}\n\n\u002F\u002F 全局变量\nfloat deltaTime = 0.0f; \u002F\u002F 当前帧与上一帧的时间差\nfloat lastFrame = 0.0f; \u002F\u002F 上一帧的时间\n\n\u002F\u002F DPI 稳定器实例\nDPIStabilizer dpiStabilizer(0.1f, 50.0f, 0.8f);\n\n\u002F\u002F ImGui 管理器实例\nImGuiManager imguiManager;\n\n\u002F\u002F 简化的输入处理函数\nvoid processInput(GLFWwindow *window){\n    \u002F\u002F 使用 DPI 稳定器处理所有输入\n    dpiStabilizer.processKeyboardInput(window);\n    \n    \u002F\u002F 窗口控制快捷键\n    if (glfwGetKey(window, GLFW_KEY_F1) == GLFW_PRESS) {\n        imguiManager.toggleWindow(\"Hello ImGui!\");\n    }\n    if (glfwGetKey(window, GLFW_KEY_F2) == GLFW_PRESS) {\n        imguiManager.toggleWindow(\"控制面板\");\n    }\n    if (glfwGetKey(window, GLFW_KEY_F3) == GLFW_PRESS) {\n        imguiManager.toggleWindow(\"性能监控\");\n    }\n    if (glfwGetKey(window, GLFW_KEY_F4) == GLFW_PRESS) {\n        imguiManager.toggleWindow(\"调试信息\");\n    }\n    \n    \u002F\u002F 显示统计信息 (按 T 键)\n    if (glfwGetKey(window, GLFW_KEY_T) == GLFW_PRESS) {\n        std::cout \u003C\u003C dpiStabilizer.getStats() \u003C\u003C std::endl;\n    }\n}\n\nint main() {\n\n    \u002F\u002F 模型矩阵\n    mat4 model = mat4(1.0f);\n    model = rotate(model, radians(-55.0f), vec3(1.0f, 0.0f, 0.0f));\n\n    \u002F\u002F 投影矩阵\n    mat4 projection = perspective(radians(45.0f), (float)800 \u002F (float)600, 0.1f, 100.0f);\n\n    \u002F\u002F 立方体数组\n    vec3 cubePositions[] = {\n        vec3(0.0f,  0.0f,  0.0f),\n        vec3(2.0f,  5.0f, -15.0f),\n        vec3(-1.5f, -2.2f, -2.5f),\n        vec3(-3.8f, -2.0f, -12.3f),\n        vec3(2.4f, -0.4f, -3.5f),\n    };\n\n    auto path = getCurrentWorkingDirectory().append(\"\u002FAssets\u002Fimage.png\");\n    auto path2 = getCurrentWorkingDirectory().append(\"\u002FAssets\u002Fimage2.png\");\n    cv::Mat image = cv::imread(path);\n    cv::Mat image2 = cv::imread(path2);\n    cv::flip(image2, image2, 0);\n    cv::cvtColor(image, image, cv::COLOR_BGR2RGB);\n    cv::cvtColor(image2, image2, cv::COLOR_BGR2RGB);\n\n    \u002F\u002F 转换为字节数组\n    std::vector\u003Cuchar> imageData;\n    std::vector\u003Cuchar> imageData2;\n    if (image.empty()) {\n        std::cerr \u003C\u003C \"Could not read the image: \" \u003C\u003C path \u003C\u003C std::endl;\n        return 1;\n    }\n    if (image2.empty()) {\n        std::cerr \u003C\u003C \"Could not read the image2: \" \u003C\u003C path2 \u003C\u003C std::endl;\n        return 1;\n    }\n    \n    std::cout \u003C\u003C \"Image 1 loaded: \" \u003C\u003C image.cols \u003C\u003C \"x\" \u003C\u003C image.rows \u003C\u003C std::endl;\n    std::cout \u003C\u003C \"Image 2 loaded: \" \u003C\u003C image2.cols \u003C\u003C \"x\" \u003C\u003C image2.rows \u003C\u003C std::endl;\n\n    cv::imencode(\".png\", image, imageData);\n    cv::imencode(\".png\", image2, imageData2);\n\n\n    \n    \u002F\u002F 初始化GLFW\n    glfwInit();\n    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);\n    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);\n    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);\n    \n    \u002F\u002F 设置 DPI 感知提示\n    DPIStabilizer::setDPIHint();\n\n    \u002F\u002F 创建窗口\n    GLFWwindow* window = glfwCreateWindow(800, 600, \"OpenGL Window\", NULL, NULL);\n    if (window == NULL) {\n        std::cout \u003C\u003C \"Failed to create GLFW window\" \u003C\u003C std::endl;\n        glfwTerminate();\n        return -1;\n    }\n\n\n    \n    glfwMakeContextCurrent(window);\n    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);\n    \n    \u002F\u002F 初始化 DPI 稳定器\n    dpiStabilizer.initialize(window);\n\n    \u002F\u002F 初始化GLAD\n    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {\n        std::cout \u003C\u003C \"Failed to initialize GLAD\" \u003C\u003C std::endl;\n        return -1;\n    }\n\n    \u002F\u002F 初始化 ImGui 管理器\n    imguiManager.initialize(window);\n    \n    \u002F\u002F 注册窗口\n    imguiManager.registerWindow(\"Hello ImGui!\", []() {\n        ImGui::Text(\"Hello, World!\");\n        ImGui::Text(\"DPI 稳定器状态: %s\", dpiStabilizer.isMouseCaptured() ? \"已捕获\" : \"已释放\");\n        ImGui::Text(\"鼠标敏感度: %.3f\", dpiStabilizer.getSensitivity());\n        \n        if (ImGui::Button(\"重置统计\")) {\n            dpiStabilizer.resetStats();\n        }\n        \n        float sensitivity = dpiStabilizer.getSensitivity();\n        if (ImGui::SliderFloat(\"鼠标敏感度\", &sensitivity, 0.01f, 0.5f)) {\n            dpiStabilizer.setSensitivity(sensitivity);\n        }\n    });\n    \n    imguiManager.registerWindow(\"控制面板\", []() {\n        ImGuiIO& io = ImGui::GetIO();\n        ImGui::Text(\"FPS: %.1f\", io.Framerate);\n        ImGui::Text(\"帧时间: %.3f ms\", 1000.0f \u002F io.Framerate);\n    });\n\n    \u002F\u002F 启用深度测试\n    glEnable(GL_DEPTH_TEST);\n\n    float vertices[] = {\n    \u002F\u002F 位置(3) + 纹理坐标(2)\n    -0.5f, -0.5f, -0.5f,  0.0f, 0.0f,\n     0.5f, -0.5f, -0.5f,  1.0f, 0.0f,\n     0.5f,  0.5f, -0.5f,  1.0f, 1.0f,\n     0.5f,  0.5f, -0.5f,  1.0f, 1.0f,\n    -0.5f,  0.5f, -0.5f,  0.0f, 1.0f,\n    -0.5f, -0.5f, -0.5f,  0.0f, 0.0f,\n\n    -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,\n     0.5f, -0.5f,  0.5f,  1.0f, 0.0f,\n     0.5f,  0.5f,  0.5f,  1.0f, 1.0f,\n     0.5f,  0.5f,  0.5f,  1.0f, 1.0f,\n    -0.5f,  0.5f,  0.5f,  0.0f, 1.0f,\n    -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,\n\n    -0.5f,  0.5f,  0.5f,  1.0f, 0.0f,\n    -0.5f,  0.5f, -0.5f,  1.0f, 1.0f,\n    -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,\n    -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,\n    -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,\n    -0.5f,  0.5f,  0.5f,  1.0f, 0.0f,\n\n     0.5f,  0.5f,  0.5f,  1.0f, 0.0f,\n     0.5f,  0.5f, -0.5f,  1.0f, 1.0f,\n     0.5f, -0.5f, -0.5f,  0.0f, 1.0f,\n     0.5f, -0.5f, -0.5f,  0.0f, 1.0f,\n     0.5f, -0.5f,  0.5f,  0.0f, 0.0f,\n     0.5f,  0.5f,  0.5f,  1.0f, 0.0f,\n\n    -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,\n     0.5f, -0.5f, -0.5f,  1.0f, 1.0f,\n     0.5f, -0.5f,  0.5f,  1.0f, 0.0f,\n     0.5f, -0.5f,  0.5f,  1.0f, 0.0f,\n    -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,\n    -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,\n\n    -0.5f,  0.5f, -0.5f,  0.0f, 1.0f,\n     0.5f,  0.5f, -0.5f,  1.0f, 1.0f,\n     0.5f,  0.5f,  0.5f,  1.0f, 0.0f,\n     0.5f,  0.5f,  0.5f,  1.0f, 0.0f,\n    -0.5f,  0.5f,  0.5f,  0.0f, 0.0f,\n    -0.5f,  0.5f, -0.5f,  0.0f, 1.0f\n};\n    \n    \u002F\u002F indices数组未使用，已删除\n\n    \u002F\u002F 纹理\n    unsigned int texture;\n    unsigned int texture2;\n    glGenTextures(1, &texture); \u002F\u002F 生成纹理\n    glGenTextures(1, &texture2); \u002F\u002F 生成纹理\n    \n    \u002F\u002F 配置第一个纹理\n    glActiveTexture(GL_TEXTURE0);\n    glBindTexture(GL_TEXTURE_2D, texture); \u002F\u002F 绑定纹理\n    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); \u002F\u002F 设置纹理环绕方式\n    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); \u002F\u002F 设置纹理环绕方式\n    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); \u002F\u002F 设置纹理过滤方式\n    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); \u002F\u002F 设置纹理过滤方式\n    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, image.cols, image.rows, 0, GL_RGB, GL_UNSIGNED_BYTE, image.data); \u002F\u002F 设置纹理数据\n    glGenerateMipmap(GL_TEXTURE_2D); \u002F\u002F 生成mipmap\n\n    \u002F\u002F 配置第二个纹理\n    glActiveTexture(GL_TEXTURE1);\n    glBindTexture(GL_TEXTURE_2D, texture2); \u002F\u002F 绑定纹理\n    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); \u002F\u002F 设置纹理环绕方式\n    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); \u002F\u002F 设置纹理环绕方式   \n    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); \u002F\u002F 设置纹理过滤方式\n    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); \u002F\u002F 设置纹理过滤方式\n    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, image2.cols, image2.rows, 0, GL_RGB, GL_UNSIGNED_BYTE, image2.data); \u002F\u002F 设置纹理数据\n    glGenerateMipmap(GL_TEXTURE_2D); \u002F\u002F 生成mipmap\n\n\n    \u002F\u002F 创建VAO\n    unsigned int VAO;\n    glGenVertexArrays(1, &VAO);\n    glBindVertexArray(VAO); \u002F\u002F 绑定VAO\n\n    \u002F\u002F 创建VBO\n    unsigned int VBO;\n    glGenBuffers(1, &VBO);\n    glBindBuffer(GL_ARRAY_BUFFER, VBO); \u002F\u002F 绑定VBO\n    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);\n\n    int nrAttributes;\n    glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &nrAttributes);\n    std::cout \u003C\u003C \"Maximum nr of vertex attributes supported: \" \u003C\u003C nrAttributes \u003C\u003C std::endl;\n\n    \u002F\u002F 位置属性\n    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);\n    glEnableVertexAttribArray(0); \u002F\u002F 启用顶点属性\n    \n    \u002F\u002F 纹理属性\n    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));\n    glEnableVertexAttribArray(2);\n\n    \u002F\u002F 构建并编译着色器程序\n    Shader ourShader(\"shader\u002Fcustom.vert\", \"shader\u002Ffragment.vert\");\n\n    \u002F\u002F 渲染循环\n    while (!glfwWindowShouldClose(window)) {\n        \u002F\u002F 处理输入\n        processInput(window);\n        glfwPollEvents();\n\n        \u002F\u002F 从 DPI 稳定器获取 view 矩阵\n        mat4 view = dpiStabilizer.getViewMatrix();\n        \n        float currentFrame = glfwGetTime();\n        deltaTime = currentFrame - lastFrame;\n        lastFrame = currentFrame;\n        \u002F\u002F 渲染指令\n        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);\n        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);\n        \n        \u002F\u002F 使用着色器程序\n        ourShader.use();\n\n        \u002F\u002F 设置矩阵uniform\n        ourShader.setMat4(\"model\", model);\n        ourShader.setMat4(\"view\", view);\n        ourShader.setMat4(\"projection\", projection);\n        \n        ourShader.setInt(\"ourTexture\", 0);\n        ourShader.setInt(\"ourTexture2\", 1);\n        \n        glBindVertexArray(VAO);\n        \n        \u002F\u002F 绘制多个立方体\n        for(unsigned int i = 0; i \u003C 10; i++) {\n            glm::mat4 modelMatrix = glm::mat4(1.0f);\n            modelMatrix = glm::translate(modelMatrix, cubePositions[i]);\n            float angle = 20.0f * i; \n            modelMatrix = glm::rotate(modelMatrix, glm::radians(angle), glm::vec3(1.0f, 0.3f, 0.5f));\n            ourShader.setMat4(\"model\", modelMatrix);\n\n            glDrawArrays(GL_TRIANGLES, 0, 36);\n        }\n        \n        \u002F\u002F 使用 ImGui 管理器渲染所有窗口\n        imguiManager.render();\n\n        \u002F\u002F 交换缓冲区和检查IO事件\n        glfwSwapBuffers(window);\n    }\n\n    \u002F\u002F 清理 ImGui 管理器\n    imguiManager.shutdown();\n\n    \u002F\u002F 清理资源\n    glDeleteVertexArrays(1, &VAO);\n    glDeleteBuffers(1, &VBO);\n    glDeleteTextures(1, &texture);\n    glDeleteTextures(1, &texture2);\n    glfwTerminate();\n    return 0;\n}\n\n",[17,4392,4393,4397,4401,4405,4409,4413,4417,4422,4427,4432,4436,4440,4444,4449,4454,4458,4462,4466,4470,4474,4478,4482,4486,4490,4494,4498,4502,4506,4510,4514,4518,4522,4526,4530,4534,4539,4544,4549,4553,4558,4562,4566,4571,4576,4580,4585,4589,4594,4599,4603,4608,4613,4618,4622,4627,4632,4636,4641,4646,4650,4655,4660,4664,4668,4673,4678,4683,4687,4691,4695,4699,4703,4707,4711,4715,4719,4723,4727,4731,4735,4739,4743,4747,4751,4755,4759,4763,4767,4771,4775,4779,4783,4787,4791,4795,4799,4803,4807,4811,4815,4819,4823,4827,4831,4835,4839,4843,4847,4851,4855,4859,4863,4867,4871,4875,4879,4883,4887,4891,4895,4899,4903,4908,4913,4917,4921,4925,4929,4933,4937,4941,4945,4949,4953,4957,4961,4965,4969,4974,4979,4983,4987,4991,4995,4999,5003,5007,5012,5017,5021,5026,5031,5036,5041,5046,5050,5055,5060,5064,5068,5073,5078,5083,5087,5092,5096,5101,5106,5111,5116,5120,5124,5128,5132,5136,5140,5144,5148,5152,5156,5160,5164,5168,5172,5176,5180,5184,5188,5192,5196,5200,5204,5208,5212,5216,5220,5224,5228,5232,5236,5240,5244,5248,5252,5256,5260,5264,5268,5272,5276,5280,5284,5288,5292,5296,5300,5304,5308,5312,5316,5320,5324,5328,5332,5336,5340,5344,5348,5352,5356,5360,5364,5368,5372,5376,5380,5384,5388,5392,5396,5400,5404,5408,5412,5416,5420,5424,5428,5432,5436,5440,5444,5448,5452,5456,5460,5464,5468,5472,5476,5480,5484,5488,5492,5496,5500,5504,5508,5512,5516,5520,5524,5528,5532,5536,5540,5544,5548,5552,5556,5560,5565,5570,5574,5579,5584,5589,5593,5597,5601,5605,5609,5613,5617,5621,5625,5629,5633,5637,5641,5645,5649,5653,5657,5661,5665,5669,5673,5677,5681,5685,5689,5693,5697,5701,5706,5711,5715,5719,5723,5727,5731,5736,5741,5745,5749,5753,5757,5761,5765,5769,5773],{"__ignoreMap":19},[74,4394,4395],{"class":76,"line":77},[74,4396,497],{},[74,4398,4399],{"class":76,"line":116},[74,4400,502],{},[74,4402,4403],{"class":76,"line":271},[74,4404,507],{},[74,4406,4407],{"class":76,"line":277},[74,4408,512],{},[74,4410,4411],{"class":76,"line":322},[74,4412,517],{},[74,4414,4415],{"class":76,"line":409},[74,4416,522],{},[74,4418,4419],{"class":76,"line":415},[74,4420,4421],{},"#include \u003Cimgui.h>\n",[74,4423,4424],{"class":76,"line":421},[74,4425,4426],{},"#include \u003Cimgui_impl_glfw.h>\n",[74,4428,4429],{"class":76,"line":427},[74,4430,4431],{},"#include \u003Cimgui_impl_opengl3.h>\n",[74,4433,4434],{"class":76,"line":433},[74,4435,527],{},[74,4437,4438],{"class":76,"line":439},[74,4439,532],{},[74,4441,4442],{"class":76,"line":445},[74,4443,537],{},[74,4445,4446],{"class":76,"line":451},[74,4447,4448],{},"#include \"include\u002Fdpi_stabilizer.h\"\n",[74,4450,4451],{"class":76,"line":560},[74,4452,4453],{},"#include \"include\u002Fimgui_manager.h\"\n",[74,4455,4456],{"class":76,"line":566},[74,4457,543],{"emptyLinePlaceholder":542},[74,4459,4460],{"class":76,"line":572},[74,4461,548],{},[74,4463,4464],{"class":76,"line":578},[74,4465,553],{},[74,4467,4468],{"class":76,"line":583},[74,4469,543],{"emptyLinePlaceholder":542},[74,4471,4472],{"class":76,"line":588},[74,4473,563],{},[74,4475,4476],{"class":76,"line":594},[74,4477,569],{},[74,4479,4480],{"class":76,"line":600},[74,4481,575],{},[74,4483,4484],{"class":76,"line":606},[74,4485,454],{},[74,4487,4488],{"class":76,"line":612},[74,4489,543],{"emptyLinePlaceholder":542},[74,4491,4492],{"class":76,"line":618},[74,4493,591],{},[74,4495,4496],{"class":76,"line":624},[74,4497,597],{},[74,4499,4500],{"class":76,"line":630},[74,4501,603],{},[74,4503,4504],{"class":76,"line":636},[74,4505,609],{},[74,4507,4508],{"class":76,"line":642},[74,4509,615],{},[74,4511,4512],{"class":76,"line":647},[74,4513,621],{},[74,4515,4516],{"class":76,"line":652},[74,4517,627],{},[74,4519,4520],{"class":76,"line":658},[74,4521,633],{},[74,4523,4524],{"class":76,"line":664},[74,4525,639],{},[74,4527,4528],{"class":76,"line":670},[74,4529,454],{},[74,4531,4532],{"class":76,"line":676},[74,4533,543],{"emptyLinePlaceholder":542},[74,4535,4536],{"class":76,"line":681},[74,4537,4538],{},"\u002F\u002F 全局变量\n",[74,4540,4541],{"class":76,"line":687},[74,4542,4543],{},"float deltaTime = 0.0f; \u002F\u002F 当前帧与上一帧的时间差\n",[74,4545,4546],{"class":76,"line":693},[74,4547,4548],{},"float lastFrame = 0.0f; \u002F\u002F 上一帧的时间\n",[74,4550,4551],{"class":76,"line":699},[74,4552,543],{"emptyLinePlaceholder":542},[74,4554,4555],{"class":76,"line":705},[74,4556,4557],{},"\u002F\u002F DPI 稳定器实例\n",[74,4559,4560],{"class":76,"line":711},[74,4561,4181],{},[74,4563,4564],{"class":76,"line":717},[74,4565,543],{"emptyLinePlaceholder":542},[74,4567,4568],{"class":76,"line":723},[74,4569,4570],{},"\u002F\u002F ImGui 管理器实例\n",[74,4572,4573],{"class":76,"line":728},[74,4574,4575],{},"ImGuiManager imguiManager;\n",[74,4577,4578],{"class":76,"line":734},[74,4579,543],{"emptyLinePlaceholder":542},[74,4581,4582],{"class":76,"line":740},[74,4583,4584],{},"\u002F\u002F 简化的输入处理函数\n",[74,4586,4587],{"class":76,"line":746},[74,4588,945],{},[74,4590,4591],{"class":76,"line":752},[74,4592,4593],{},"    \u002F\u002F 使用 DPI 稳定器处理所有输入\n",[74,4595,4596],{"class":76,"line":758},[74,4597,4598],{},"    dpiStabilizer.processKeyboardInput(window);\n",[74,4600,4601],{"class":76,"line":764},[74,4602,755],{},[74,4604,4605],{"class":76,"line":770},[74,4606,4607],{},"    \u002F\u002F 窗口控制快捷键\n",[74,4609,4610],{"class":76,"line":776},[74,4611,4612],{},"    if (glfwGetKey(window, GLFW_KEY_F1) == GLFW_PRESS) {\n",[74,4614,4615],{"class":76,"line":782},[74,4616,4617],{},"        imguiManager.toggleWindow(\"Hello ImGui!\");\n",[74,4619,4620],{"class":76,"line":787},[74,4621,639],{},[74,4623,4624],{"class":76,"line":792},[74,4625,4626],{},"    if (glfwGetKey(window, GLFW_KEY_F2) == GLFW_PRESS) {\n",[74,4628,4629],{"class":76,"line":798},[74,4630,4631],{},"        imguiManager.toggleWindow(\"控制面板\");\n",[74,4633,4634],{"class":76,"line":804},[74,4635,639],{},[74,4637,4638],{"class":76,"line":810},[74,4639,4640],{},"    if (glfwGetKey(window, GLFW_KEY_F3) == GLFW_PRESS) {\n",[74,4642,4643],{"class":76,"line":816},[74,4644,4645],{},"        imguiManager.toggleWindow(\"性能监控\");\n",[74,4647,4648],{"class":76,"line":821},[74,4649,639],{},[74,4651,4652],{"class":76,"line":827},[74,4653,4654],{},"    if (glfwGetKey(window, GLFW_KEY_F4) == GLFW_PRESS) {\n",[74,4656,4657],{"class":76,"line":833},[74,4658,4659],{},"        imguiManager.toggleWindow(\"调试信息\");\n",[74,4661,4662],{"class":76,"line":839},[74,4663,639],{},[74,4665,4666],{"class":76,"line":844},[74,4667,755],{},[74,4669,4670],{"class":76,"line":850},[74,4671,4672],{},"    \u002F\u002F 显示统计信息 (按 T 键)\n",[74,4674,4675],{"class":76,"line":856},[74,4676,4677],{},"    if (glfwGetKey(window, GLFW_KEY_T) == GLFW_PRESS) {\n",[74,4679,4680],{"class":76,"line":861},[74,4681,4682],{},"        std::cout \u003C\u003C dpiStabilizer.getStats() \u003C\u003C std::endl;\n",[74,4684,4685],{"class":76,"line":867},[74,4686,639],{},[74,4688,4689],{"class":76,"line":873},[74,4690,454],{},[74,4692,4693],{"class":76,"line":879},[74,4694,543],{"emptyLinePlaceholder":542},[74,4696,4697],{"class":76,"line":885},[74,4698,1153],{},[74,4700,4701],{"class":76,"line":891},[74,4702,543],{"emptyLinePlaceholder":542},[74,4704,4705],{"class":76,"line":896},[74,4706,1164],{},[74,4708,4709],{"class":76,"line":902},[74,4710,1170],{},[74,4712,4713],{"class":76,"line":908},[74,4714,1176],{},[74,4716,4717],{"class":76,"line":914},[74,4718,543],{"emptyLinePlaceholder":542},[74,4720,4721],{"class":76,"line":920},[74,4722,1210],{},[74,4724,4725],{"class":76,"line":926},[74,4726,1216],{},[74,4728,4729],{"class":76,"line":932},[74,4730,543],{"emptyLinePlaceholder":542},[74,4732,4733],{"class":76,"line":937},[74,4734,1227],{},[74,4736,4737],{"class":76,"line":942},[74,4738,1233],{},[74,4740,4741],{"class":76,"line":948},[74,4742,1239],{},[74,4744,4745],{"class":76,"line":954},[74,4746,1245],{},[74,4748,4749],{"class":76,"line":960},[74,4750,1251],{},[74,4752,4753],{"class":76,"line":966},[74,4754,1257],{},[74,4756,4757],{"class":76,"line":972},[74,4758,1263],{},[74,4760,4761],{"class":76,"line":978},[74,4762,1269],{},[74,4764,4765],{"class":76,"line":983},[74,4766,543],{"emptyLinePlaceholder":542},[74,4768,4769],{"class":76,"line":989},[74,4770,1280],{},[74,4772,4773],{"class":76,"line":995},[74,4774,1286],{},[74,4776,4777],{"class":76,"line":1001},[74,4778,1292],{},[74,4780,4781],{"class":76,"line":1007},[74,4782,1298],{},[74,4784,4785],{"class":76,"line":1013},[74,4786,1304],{},[74,4788,4789],{"class":76,"line":1019},[74,4790,1310],{},[74,4792,4793],{"class":76,"line":1025},[74,4794,1316],{},[74,4796,4797],{"class":76,"line":1031},[74,4798,543],{"emptyLinePlaceholder":542},[74,4800,4801],{"class":76,"line":1037},[74,4802,1327],{},[74,4804,4805],{"class":76,"line":1043},[74,4806,1333],{},[74,4808,4809],{"class":76,"line":1049},[74,4810,1339],{},[74,4812,4813],{"class":76,"line":1054},[74,4814,1345],{},[74,4816,4817],{"class":76,"line":1060},[74,4818,1351],{},[74,4820,4821],{"class":76,"line":1066},[74,4822,1357],{},[74,4824,4825],{"class":76,"line":1071},[74,4826,639],{},[74,4828,4829],{"class":76,"line":1076},[74,4830,1368],{},[74,4832,4833],{"class":76,"line":1082},[74,4834,1374],{},[74,4836,4837],{"class":76,"line":1087},[74,4838,1357],{},[74,4840,4841],{"class":76,"line":1092},[74,4842,639],{},[74,4844,4845],{"class":76,"line":1098},[74,4846,755],{},[74,4848,4849],{"class":76,"line":1103},[74,4850,1395],{},[74,4852,4853],{"class":76,"line":1108},[74,4854,1401],{},[74,4856,4857],{"class":76,"line":1113},[74,4858,543],{"emptyLinePlaceholder":542},[74,4860,4861],{"class":76,"line":1118},[74,4862,1412],{},[74,4864,4865],{"class":76,"line":1123},[74,4866,1418],{},[74,4868,4869],{"class":76,"line":1129},[74,4870,543],{"emptyLinePlaceholder":542},[74,4872,4873],{"class":76,"line":1134},[74,4874,543],{"emptyLinePlaceholder":542},[74,4876,4877],{"class":76,"line":1140},[74,4878,755],{},[74,4880,4881],{"class":76,"line":1145},[74,4882,1439],{},[74,4884,4885],{"class":76,"line":1150},[74,4886,1445],{},[74,4888,4889],{"class":76,"line":1156},[74,4890,1451],{},[74,4892,4893],{"class":76,"line":1161},[74,4894,1457],{},[74,4896,4897],{"class":76,"line":1167},[74,4898,1463],{},[74,4900,4901],{"class":76,"line":1173},[74,4902,755],{},[74,4904,4905],{"class":76,"line":1179},[74,4906,4907],{},"    \u002F\u002F 设置 DPI 感知提示\n",[74,4909,4910],{"class":76,"line":1184},[74,4911,4912],{},"    DPIStabilizer::setDPIHint();\n",[74,4914,4915],{"class":76,"line":1190},[74,4916,543],{"emptyLinePlaceholder":542},[74,4918,4919],{"class":76,"line":1196},[74,4920,1474],{},[74,4922,4923],{"class":76,"line":1202},[74,4924,1480],{},[74,4926,4927],{"class":76,"line":1207},[74,4928,1486],{},[74,4930,4931],{"class":76,"line":1213},[74,4932,1492],{},[74,4934,4935],{"class":76,"line":1219},[74,4936,1498],{},[74,4938,4939],{"class":76,"line":1224},[74,4940,1504],{},[74,4942,4943],{"class":76,"line":1230},[74,4944,639],{},[74,4946,4947],{"class":76,"line":1236},[74,4948,543],{"emptyLinePlaceholder":542},[74,4950,4951],{"class":76,"line":1242},[74,4952,543],{"emptyLinePlaceholder":542},[74,4954,4955],{"class":76,"line":1248},[74,4956,755],{},[74,4958,4959],{"class":76,"line":1254},[74,4960,1520],{},[74,4962,4963],{"class":76,"line":1260},[74,4964,1526],{},[74,4966,4967],{"class":76,"line":1266},[74,4968,755],{},[74,4970,4971],{"class":76,"line":1272},[74,4972,4973],{},"    \u002F\u002F 初始化 DPI 稳定器\n",[74,4975,4976],{"class":76,"line":1277},[74,4977,4978],{},"    dpiStabilizer.initialize(window);\n",[74,4980,4981],{"class":76,"line":1283},[74,4982,543],{"emptyLinePlaceholder":542},[74,4984,4985],{"class":76,"line":1289},[74,4986,1560],{},[74,4988,4989],{"class":76,"line":1295},[74,4990,1566],{},[74,4992,4993],{"class":76,"line":1301},[74,4994,1572],{},[74,4996,4997],{"class":76,"line":1307},[74,4998,1504],{},[74,5000,5001],{"class":76,"line":1313},[74,5002,639],{},[74,5004,5005],{"class":76,"line":1319},[74,5006,543],{"emptyLinePlaceholder":542},[74,5008,5009],{"class":76,"line":1324},[74,5010,5011],{},"    \u002F\u002F 初始化 ImGui 管理器\n",[74,5013,5014],{"class":76,"line":1330},[74,5015,5016],{},"    imguiManager.initialize(window);\n",[74,5018,5019],{"class":76,"line":1336},[74,5020,755],{},[74,5022,5023],{"class":76,"line":1342},[74,5024,5025],{},"    \u002F\u002F 注册窗口\n",[74,5027,5028],{"class":76,"line":1348},[74,5029,5030],{},"    imguiManager.registerWindow(\"Hello ImGui!\", []() {\n",[74,5032,5033],{"class":76,"line":1354},[74,5034,5035],{},"        ImGui::Text(\"Hello, World!\");\n",[74,5037,5038],{"class":76,"line":1360},[74,5039,5040],{},"        ImGui::Text(\"DPI 稳定器状态: %s\", dpiStabilizer.isMouseCaptured() ? \"已捕获\" : \"已释放\");\n",[74,5042,5043],{"class":76,"line":1365},[74,5044,5045],{},"        ImGui::Text(\"鼠标敏感度: %.3f\", dpiStabilizer.getSensitivity());\n",[74,5047,5048],{"class":76,"line":1371},[74,5049,2217],{},[74,5051,5052],{"class":76,"line":1377},[74,5053,5054],{},"        if (ImGui::Button(\"重置统计\")) {\n",[74,5056,5057],{"class":76,"line":1382},[74,5058,5059],{},"            dpiStabilizer.resetStats();\n",[74,5061,5062],{"class":76,"line":1387},[74,5063,1063],{},[74,5065,5066],{"class":76,"line":1392},[74,5067,2217],{},[74,5069,5070],{"class":76,"line":1398},[74,5071,5072],{},"        float sensitivity = dpiStabilizer.getSensitivity();\n",[74,5074,5075],{"class":76,"line":1404},[74,5076,5077],{},"        if (ImGui::SliderFloat(\"鼠标敏感度\", &sensitivity, 0.01f, 0.5f)) {\n",[74,5079,5080],{"class":76,"line":1409},[74,5081,5082],{},"            dpiStabilizer.setSensitivity(sensitivity);\n",[74,5084,5085],{"class":76,"line":1415},[74,5086,1063],{},[74,5088,5089],{"class":76,"line":1421},[74,5090,5091],{},"    });\n",[74,5093,5094],{"class":76,"line":1426},[74,5095,755],{},[74,5097,5098],{"class":76,"line":1431},[74,5099,5100],{},"    imguiManager.registerWindow(\"控制面板\", []() {\n",[74,5102,5103],{"class":76,"line":1436},[74,5104,5105],{},"        ImGuiIO& io = ImGui::GetIO();\n",[74,5107,5108],{"class":76,"line":1442},[74,5109,5110],{},"        ImGui::Text(\"FPS: %.1f\", io.Framerate);\n",[74,5112,5113],{"class":76,"line":1448},[74,5114,5115],{},"        ImGui::Text(\"帧时间: %.3f ms\", 1000.0f \u002F io.Framerate);\n",[74,5117,5118],{"class":76,"line":1454},[74,5119,5091],{},[74,5121,5122],{"class":76,"line":1460},[74,5123,543],{"emptyLinePlaceholder":542},[74,5125,5126],{"class":76,"line":1466},[74,5127,1593],{},[74,5129,5130],{"class":76,"line":1471},[74,5131,1599],{},[74,5133,5134],{"class":76,"line":1477},[74,5135,543],{"emptyLinePlaceholder":542},[74,5137,5138],{"class":76,"line":1483},[74,5139,1610],{},[74,5141,5142],{"class":76,"line":1489},[74,5143,1616],{},[74,5145,5146],{"class":76,"line":1495},[74,5147,1622],{},[74,5149,5150],{"class":76,"line":1501},[74,5151,1628],{},[74,5153,5154],{"class":76,"line":1507},[74,5155,1634],{},[74,5157,5158],{"class":76,"line":1512},[74,5159,1634],{},[74,5161,5162],{"class":76,"line":1517},[74,5163,1645],{},[74,5165,5166],{"class":76,"line":1523},[74,5167,1622],{},[74,5169,5170],{"class":76,"line":1529},[74,5171,543],{"emptyLinePlaceholder":542},[74,5173,5174],{"class":76,"line":1535},[74,5175,1661],{},[74,5177,5178],{"class":76,"line":1540},[74,5179,1667],{},[74,5181,5182],{"class":76,"line":1546},[74,5183,1673],{},[74,5185,5186],{"class":76,"line":1552},[74,5187,1673],{},[74,5189,5190],{"class":76,"line":1557},[74,5191,1684],{},[74,5193,5194],{"class":76,"line":1563},[74,5195,1661],{},[74,5197,5198],{"class":76,"line":1569},[74,5199,543],{"emptyLinePlaceholder":542},[74,5201,5202],{"class":76,"line":1575},[74,5203,1700],{},[74,5205,5206],{"class":76,"line":1580},[74,5207,1706],{},[74,5209,5210],{"class":76,"line":1585},[74,5211,1712],{},[74,5213,5214],{"class":76,"line":1590},[74,5215,1712],{},[74,5217,5218],{"class":76,"line":1596},[74,5219,1661],{},[74,5221,5222],{"class":76,"line":1602},[74,5223,1700],{},[74,5225,5226],{"class":76,"line":1607},[74,5227,543],{"emptyLinePlaceholder":542},[74,5229,5230],{"class":76,"line":1613},[74,5231,1738],{},[74,5233,5234],{"class":76,"line":1619},[74,5235,1634],{},[74,5237,5238],{"class":76,"line":1625},[74,5239,1749],{},[74,5241,5242],{"class":76,"line":1631},[74,5243,1749],{},[74,5245,5246],{"class":76,"line":1637},[74,5247,1760],{},[74,5249,5250],{"class":76,"line":1642},[74,5251,1738],{},[74,5253,5254],{"class":76,"line":1648},[74,5255,543],{"emptyLinePlaceholder":542},[74,5257,5258],{"class":76,"line":1653},[74,5259,1712],{},[74,5261,5262],{"class":76,"line":1658},[74,5263,1781],{},[74,5265,5266],{"class":76,"line":1664},[74,5267,1667],{},[74,5269,5270],{"class":76,"line":1670},[74,5271,1667],{},[74,5273,5274],{"class":76,"line":1676},[74,5275,1661],{},[74,5277,5278],{"class":76,"line":1681},[74,5279,1712],{},[74,5281,5282],{"class":76,"line":1687},[74,5283,543],{"emptyLinePlaceholder":542},[74,5285,5286],{"class":76,"line":1692},[74,5287,1645],{},[74,5289,5290],{"class":76,"line":1697},[74,5291,1634],{},[74,5293,5294],{"class":76,"line":1703},[74,5295,1738],{},[74,5297,5298],{"class":76,"line":1709},[74,5299,1738],{},[74,5301,5302],{"class":76,"line":1715},[74,5303,1832],{},[74,5305,5306],{"class":76,"line":1720},[74,5307,1838],{},[74,5309,5310],{"class":76,"line":1725},[74,5311,1844],{},[74,5313,5314],{"class":76,"line":1730},[74,5315,755],{},[74,5317,5318],{"class":76,"line":1735},[74,5319,1855],{},[74,5321,5322],{"class":76,"line":1741},[74,5323,543],{"emptyLinePlaceholder":542},[74,5325,5326],{"class":76,"line":1746},[74,5327,1866],{},[74,5329,5330],{"class":76,"line":1752},[74,5331,1872],{},[74,5333,5334],{"class":76,"line":1757},[74,5335,1878],{},[74,5337,5338],{"class":76,"line":1763},[74,5339,1884],{},[74,5341,5342],{"class":76,"line":1768},[74,5343,1890],{},[74,5345,5346],{"class":76,"line":1773},[74,5347,755],{},[74,5349,5350],{"class":76,"line":1778},[74,5351,1901],{},[74,5353,5354],{"class":76,"line":1784},[74,5355,1907],{},[74,5357,5358],{"class":76,"line":1789},[74,5359,1913],{},[74,5361,5362],{"class":76,"line":1794},[74,5363,1919],{},[74,5365,5366],{"class":76,"line":1799},[74,5367,1925],{},[74,5369,5370],{"class":76,"line":1804},[74,5371,1931],{},[74,5373,5374],{"class":76,"line":1809},[74,5375,1937],{},[74,5377,5378],{"class":76,"line":1814},[74,5379,1943],{},[74,5381,5382],{"class":76,"line":1819},[74,5383,1949],{},[74,5385,5386],{"class":76,"line":1824},[74,5387,543],{"emptyLinePlaceholder":542},[74,5389,5390],{"class":76,"line":1829},[74,5391,1960],{},[74,5393,5394],{"class":76,"line":1835},[74,5395,1966],{},[74,5397,5398],{"class":76,"line":1841},[74,5399,1972],{},[74,5401,5402],{"class":76,"line":1847},[74,5403,1919],{},[74,5405,5406],{"class":76,"line":1852},[74,5407,1983],{},[74,5409,5410],{"class":76,"line":1858},[74,5411,1931],{},[74,5413,5414],{"class":76,"line":1863},[74,5415,1937],{},[74,5417,5418],{"class":76,"line":1869},[74,5419,1999],{},[74,5421,5422],{"class":76,"line":1875},[74,5423,1949],{},[74,5425,5426],{"class":76,"line":1881},[74,5427,543],{"emptyLinePlaceholder":542},[74,5429,5430],{"class":76,"line":1887},[74,5431,543],{"emptyLinePlaceholder":542},[74,5433,5434],{"class":76,"line":1893},[74,5435,2020],{},[74,5437,5438],{"class":76,"line":1898},[74,5439,2026],{},[74,5441,5442],{"class":76,"line":1904},[74,5443,2032],{},[74,5445,5446],{"class":76,"line":1910},[74,5447,2038],{},[74,5449,5450],{"class":76,"line":1916},[74,5451,543],{"emptyLinePlaceholder":542},[74,5453,5454],{"class":76,"line":1922},[74,5455,2049],{},[74,5457,5458],{"class":76,"line":1928},[74,5459,2055],{},[74,5461,5462],{"class":76,"line":1934},[74,5463,2061],{},[74,5465,5466],{"class":76,"line":1940},[74,5467,2067],{},[74,5469,5470],{"class":76,"line":1946},[74,5471,2073],{},[74,5473,5474],{"class":76,"line":1952},[74,5475,543],{"emptyLinePlaceholder":542},[74,5477,5478],{"class":76,"line":1957},[74,5479,2084],{},[74,5481,5482],{"class":76,"line":1963},[74,5483,2090],{},[74,5485,5486],{"class":76,"line":1969},[74,5487,2096],{},[74,5489,5490],{"class":76,"line":1975},[74,5491,543],{"emptyLinePlaceholder":542},[74,5493,5494],{"class":76,"line":1980},[74,5495,2107],{},[74,5497,5498],{"class":76,"line":1986},[74,5499,2113],{},[74,5501,5502],{"class":76,"line":1991},[74,5503,2119],{},[74,5505,5506],{"class":76,"line":1996},[74,5507,755],{},[74,5509,5510],{"class":76,"line":2002},[74,5511,2130],{},[74,5513,5514],{"class":76,"line":2007},[74,5515,2136],{},[74,5517,5518],{"class":76,"line":2012},[74,5519,2142],{},[74,5521,5522],{"class":76,"line":2017},[74,5523,543],{"emptyLinePlaceholder":542},[74,5525,5526],{"class":76,"line":2023},[74,5527,2153],{},[74,5529,5530],{"class":76,"line":2029},[74,5531,2159],{},[74,5533,5534],{"class":76,"line":2035},[74,5535,543],{"emptyLinePlaceholder":542},[74,5537,5538],{"class":76,"line":2041},[74,5539,2170],{},[74,5541,5542],{"class":76,"line":2046},[74,5543,2176],{},[74,5545,5546],{"class":76,"line":2052},[74,5547,2182],{},[74,5549,5550],{"class":76,"line":2058},[74,5551,2188],{},[74,5553,5554],{"class":76,"line":2064},[74,5555,2194],{},[74,5557,5558],{"class":76,"line":2070},[74,5559,543],{"emptyLinePlaceholder":542},[74,5561,5562],{"class":76,"line":2076},[74,5563,5564],{},"        \u002F\u002F 从 DPI 稳定器获取 view 矩阵\n",[74,5566,5567],{"class":76,"line":2081},[74,5568,5569],{},"        mat4 view = dpiStabilizer.getViewMatrix();\n",[74,5571,5572],{"class":76,"line":2087},[74,5573,2217],{},[74,5575,5576],{"class":76,"line":2093},[74,5577,5578],{},"        float currentFrame = glfwGetTime();\n",[74,5580,5581],{"class":76,"line":2099},[74,5582,5583],{},"        deltaTime = currentFrame - lastFrame;\n",[74,5585,5586],{"class":76,"line":2104},[74,5587,5588],{},"        lastFrame = currentFrame;\n",[74,5590,5591],{"class":76,"line":2110},[74,5592,2223],{},[74,5594,5595],{"class":76,"line":2116},[74,5596,2229],{},[74,5598,5599],{"class":76,"line":2122},[74,5600,2235],{},[74,5602,5603],{"class":76,"line":2127},[74,5604,2217],{},[74,5606,5607],{"class":76,"line":2133},[74,5608,2246],{},[74,5610,5611],{"class":76,"line":2139},[74,5612,2252],{},[74,5614,5615],{"class":76,"line":2145},[74,5616,543],{"emptyLinePlaceholder":542},[74,5618,5619],{"class":76,"line":2150},[74,5620,2263],{},[74,5622,5623],{"class":76,"line":2156},[74,5624,2269],{},[74,5626,5627],{"class":76,"line":2162},[74,5628,2275],{},[74,5630,5631],{"class":76,"line":2167},[74,5632,2281],{},[74,5634,5635],{"class":76,"line":2173},[74,5636,2217],{},[74,5638,5639],{"class":76,"line":2179},[74,5640,2292],{},[74,5642,5643],{"class":76,"line":2185},[74,5644,2298],{},[74,5646,5647],{"class":76,"line":2191},[74,5648,2217],{},[74,5650,5651],{"class":76,"line":2197},[74,5652,2309],{},[74,5654,5655],{"class":76,"line":2202},[74,5656,2217],{},[74,5658,5659],{"class":76,"line":2208},[74,5660,2320],{},[74,5662,5663],{"class":76,"line":2214},[74,5664,2326],{},[74,5666,5667],{"class":76,"line":2220},[74,5668,2332],{},[74,5670,5671],{"class":76,"line":2226},[74,5672,2338],{},[74,5674,5675],{"class":76,"line":2232},[74,5676,2344],{},[74,5678,5679],{"class":76,"line":2238},[74,5680,2350],{},[74,5682,5683],{"class":76,"line":2243},[74,5684,2356],{},[74,5686,5687],{"class":76,"line":2249},[74,5688,543],{"emptyLinePlaceholder":542},[74,5690,5691],{"class":76,"line":2255},[74,5692,2367],{},[74,5694,5695],{"class":76,"line":2260},[74,5696,1063],{},[74,5698,5699],{"class":76,"line":2266},[74,5700,2217],{},[74,5702,5703],{"class":76,"line":2272},[74,5704,5705],{},"        \u002F\u002F 使用 ImGui 管理器渲染所有窗口\n",[74,5707,5708],{"class":76,"line":2278},[74,5709,5710],{},"        imguiManager.render();\n",[74,5712,5713],{"class":76,"line":2284},[74,5714,543],{"emptyLinePlaceholder":542},[74,5716,5717],{"class":76,"line":2289},[74,5718,2383],{},[74,5720,5721],{"class":76,"line":2295},[74,5722,2389],{},[74,5724,5725],{"class":76,"line":2301},[74,5726,639],{},[74,5728,5729],{"class":76,"line":2306},[74,5730,543],{"emptyLinePlaceholder":542},[74,5732,5733],{"class":76,"line":2312},[74,5734,5735],{},"    \u002F\u002F 清理 ImGui 管理器\n",[74,5737,5738],{"class":76,"line":2317},[74,5739,5740],{},"    imguiManager.shutdown();\n",[74,5742,5743],{"class":76,"line":2323},[74,5744,543],{"emptyLinePlaceholder":542},[74,5746,5747],{"class":76,"line":2329},[74,5748,2405],{},[74,5750,5751],{"class":76,"line":2335},[74,5752,2411],{},[74,5754,5755],{"class":76,"line":2341},[74,5756,2417],{},[74,5758,5759],{"class":76,"line":2347},[74,5760,2423],{},[74,5762,5763],{"class":76,"line":2353},[74,5764,2429],{},[74,5766,5767],{"class":76,"line":2359},[74,5768,2435],{},[74,5770,5771],{"class":76,"line":2364},[74,5772,2441],{},[74,5774,5775],{"class":76,"line":2370},[74,5776,454],{},[10,5778,5780],{"className":68,"code":5779,"language":70,"meta":19,"style":19},"#pragma once\n\n#include \u003Cimgui.h>\n#include \u003Cimgui_impl_glfw.h>\n#include \u003Cimgui_impl_opengl3.h>\n#include \u003CGLFW\u002Fglfw3.h>\n#include \u003Cstring>\n#include \u003Cfunctional>\n#include \u003Cunordered_map>\n#include \u003Ciostream>\n\n\u002F**\n * @brief ImGui 窗口管理器类 - 封装 ImGui 的初始化和窗口管理\n * \n * 功能特性：\n * - 自动初始化和清理 ImGui\n * - 窗口注册和管理\n * - 统一的渲染循环\n * - 可扩展的窗口系统\n *\u002F\nclass ImGuiManager {\nprivate:\n    GLFWwindow* window;\n    bool initialized;\n    \n    \u002F\u002F 窗口注册系统\n    struct WindowInfo {\n        std::string name;\n        std::function\u003Cvoid()> renderFunc;\n        bool visible;\n    };\n    \n    std::unordered_map\u003Cstd::string, WindowInfo> windows;\n    \n    \u002F\u002F 字体管理\n    std::unordered_map\u003Cstd::string, ImFont*> fonts;\n    std::string defaultFont;\n\npublic:\n    \u002F**\n     * @brief 构造函数\n     *\u002F\n    ImGuiManager() : window(nullptr), initialized(false) {}\n\n    \u002F**\n     * @brief 析构函数\n     *\u002F\n    ~ImGuiManager() {\n        shutdown();\n    }\n\n    \u002F**\n     * @brief 初始化 ImGui 管理器\n     * @param glfwWindow GLFW 窗口指针\n     * @return 是否初始化成功\n     *\u002F\n    bool initialize(GLFWwindow* glfwWindow) {\n        if (initialized) return true;\n        \n        window = glfwWindow;\n        \n        \u002F\u002F 初始化 ImGui\n        IMGUI_CHECKVERSION();\n        ImGui::CreateContext();\n        ImGuiIO& io = ImGui::GetIO(); (void)io;\n        io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;\n        io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;\n\n        \u002F\u002F 设置 ImGui 样式\n        ImGui::StyleColorsDark();\n\n        \u002F\u002F 初始化 ImGui 平台\u002F渲染器绑定\n        ImGui_ImplGlfw_InitForOpenGL(window, true);\n        ImGui_ImplOpenGL3_Init(\"#version 330\");\n        \n        \u002F\u002F 加载默认字体\n        loadDefaultFonts();\n        \n        initialized = true;\n        std::cout \u003C\u003C \"ImGui 管理器已初始化\" \u003C\u003C std::endl;\n        return true;\n    }\n\n    \u002F**\n     * @brief 注册一个窗口\n     * @param name 窗口名称\n     * @param renderFunc 渲染函数\n     * @param visible 是否默认可见\n     *\u002F\n    void registerWindow(const std::string& name, std::function\u003Cvoid()> renderFunc, bool visible = true) {\n        windows[name] = {name, renderFunc, visible};\n    }\n\n    \u002F**\n     * @brief 切换窗口可见性\n     * @param name 窗口名称\n     *\u002F\n    void toggleWindow(const std::string& name) {\n        auto it = windows.find(name);\n        if (it != windows.end()) {\n            it->second.visible = !it->second.visible;\n        }\n    }\n\n    \u002F**\n     * @brief 设置窗口可见性\n     * @param name 窗口名称\n     * @param visible 是否可见\n     *\u002F\n    void setWindowVisible(const std::string& name, bool visible) {\n        auto it = windows.find(name);\n        if (it != windows.end()) {\n            it->second.visible = visible;\n        }\n    }\n\n    \u002F**\n     * @brief 开始新帧\n     *\u002F\n    void beginFrame() {\n        if (!initialized) return;\n        \n        ImGui_ImplOpenGL3_NewFrame();\n        ImGui_ImplGlfw_NewFrame();\n        ImGui::NewFrame();\n    }\n\n    \u002F**\n     * @brief 渲染所有注册的窗口\n     *\u002F\n    void renderWindows() {\n        if (!initialized) return;\n        \n        for (const auto& [name, windowInfo] : windows) {\n            if (windowInfo.visible) {\n                if (ImGui::Begin(name.c_str())) {\n                    windowInfo.renderFunc();\n                }\n                ImGui::End();\n            }\n        }\n    }\n\n    \u002F**\n     * @brief 结束帧并渲染\n     *\u002F\n    void endFrame() {\n        if (!initialized) return;\n        \n        ImGui::Render();\n        ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());\n    }\n\n    \u002F**\n     * @brief 完整的渲染循环（推荐使用）\n     *\u002F\n    void render() {\n        beginFrame();\n        renderWindows();\n        endFrame();\n    }\n\n    \u002F**\n     * @brief 获取 ImGui IO\n     * @return ImGuiIO 引用\n     *\u002F\n    ImGuiIO& getIO() {\n        return ImGui::GetIO();\n    }\n\n\n    \u002F**\n     * @brief 检查窗口是否存在\n     * @param name 窗口名称\n     * @return 是否存在\n     *\u002F\n    bool hasWindow(const std::string& name) const {\n        return windows.find(name) != windows.end();\n    }\n\n    \u002F**\n     * @brief 加载字体\n     * @param fontPath 字体文件路径\n     * @param fontName 字体名称\n     * @param fontSize 字体大小\n     * @param glyphRanges 字符范围\n     * @return 是否加载成功\n     *\u002F\n    bool loadFont(const std::string& fontPath, const std::string& fontName, \n                  float fontSize = 16.0f, const ImWchar* glyphRanges = nullptr) {\n        if (!initialized) return false;\n        \n        ImGuiIO& io = ImGui::GetIO();\n        ImFont* font = io.Fonts->AddFontFromFileTTF(fontPath.c_str(), fontSize, nullptr, glyphRanges);\n        \n        if (font) {\n            fonts[fontName] = font;\n            if (defaultFont.empty()) {\n                defaultFont = fontName;\n            }\n            \n            \u002F\u002F 重新构建字体纹理\n            io.Fonts->Build();\n            ImGui_ImplOpenGL3_CreateFontsTexture();\n            \n            std::cout \u003C\u003C \"字体加载成功: \" \u003C\u003C fontName \u003C\u003C \" (\" \u003C\u003C fontPath \u003C\u003C \")\" \u003C\u003C std::endl;\n            return true;\n        } else {\n            std::cerr \u003C\u003C \"字体加载失败: \" \u003C\u003C fontPath \u003C\u003C std::endl;\n            return false;\n        }\n    }\n\n    \u002F**\n     * @brief 使用指定字体\n     * @param fontName 字体名称\n     * @return 是否成功使用字体\n     *\u002F\n    bool useFont(const std::string& fontName) {\n        auto it = fonts.find(fontName);\n        if (it != fonts.end() && it->second) {\n            ImGui::PushFont(it->second);\n            return true;\n        }\n        return false;\n    }\n\n    \u002F**\n     * @brief 弹出当前字体\n     * @return 是否成功弹出字体\n     *\u002F\n    bool popFont() {\n        ImGuiIO& io = ImGui::GetIO();\n        if (io.Fonts->Fonts.Size > 0) {\n            ImGui::PopFont();\n            return true;\n        }\n        return false;\n    }\n\n    \u002F**\n     * @brief 设置默认字体\n     * @param fontName 字体名称\n     *\u002F\n    void setDefaultFont(const std::string& fontName) {\n        if (fonts.find(fontName) != fonts.end()) {\n            defaultFont = fontName;\n        }\n    }\n\n    \u002F**\n     * @brief 获取字体\n     * @param fontName 字体名称\n     * @return 字体指针\n     *\u002F\n    ImFont* getFont(const std::string& fontName) {\n        auto it = fonts.find(fontName);\n        return (it != fonts.end()) ? it->second : nullptr;\n    }\n\n    \u002F**\n     * @brief 检查字体是否存在\n     * @param fontName 字体名称\n     * @return 是否存在\n     *\u002F\n    bool hasFont(const std::string& fontName) const {\n        return fonts.find(fontName) != fonts.end();\n    }\n\n    \u002F**\n     * @brief 关闭 ImGui 管理器\n     *\u002F\n    void shutdown() {\n        if (!initialized) return;\n        \n        ImGui_ImplOpenGL3_Shutdown();\n        ImGui_ImplGlfw_Shutdown();\n        ImGui::DestroyContext();\n        \n        initialized = false;\n        windows.clear();\n        fonts.clear();\n        defaultFont.clear();\n        std::cout \u003C\u003C \"ImGui 管理器已关闭\" \u003C\u003C std::endl;\n    }\n\nprivate:\n    \u002F**\n     * @brief 加载默认字体\n     *\u002F\n    void loadDefaultFonts() {\n        \u002F\u002F 加载 Inter 字体\n        loadFont(\"Assets\u002FInter.ttc\", \"Inter\", 16.0f, ImGui::GetIO().Fonts->GetGlyphRangesChineseFull());\n        loadFont(\"Assets\u002FInter.ttc\", \"Inter-Large\", 20.0f, ImGui::GetIO().Fonts->GetGlyphRangesChineseFull());\n        loadFont(\"Assets\u002FInter.ttc\", \"Inter-Small\", 14.0f, ImGui::GetIO().Fonts->GetGlyphRangesChineseFull());\n        \n        \u002F\u002F 加载 Windows 系统黑体字体\n        loadFont(\"C:\\\\Windows\\\\Fonts\\\\simhei.ttf\", \"SimHei\", 18.0f, ImGui::GetIO().Fonts->GetGlyphRangesChineseFull());\n        loadFont(\"C:\\\\Windows\\\\Fonts\\\\simhei.ttf\", \"SimHei-Large\", 22.0f, ImGui::GetIO().Fonts->GetGlyphRangesChineseFull());\n        loadFont(\"C:\\\\Windows\\\\Fonts\\\\simhei.ttf\", \"SimHei-Small\", 16.0f, ImGui::GetIO().Fonts->GetGlyphRangesChineseFull());\n        \n        \u002F\u002F 设置默认字体为黑体\n        setDefaultFont(\"SimHei\");\n    }\n};\n\n\n\u002F**\n * @brief 预定义的窗口创建器 - 提供常用的窗口模板\n *\u002F\nclass ImGuiWindowTemplates {\npublic:\n    \u002F**\n     * @brief 创建信息显示窗口\n     * @param title 窗口标题\n     * @param infoFunc 信息获取函数\n     * @return 窗口渲染函数\n     *\u002F\n    static std::function\u003Cvoid()> createInfoWindow(\n        const std::string& title, \n        std::function\u003Cstd::string()> infoFunc\n    ) {\n        return [title, infoFunc]() {\n            ImGui::Text(\"=== %s ===\", title.c_str());\n            ImGui::Separator();\n            ImGui::Text(\"%s\", infoFunc().c_str());\n        };\n    }\n\n    \u002F**\n     * @brief 创建控制面板窗口\n     * @param controls 控制函数列表\n     * @return 窗口渲染函数\n     *\u002F\n    static std::function\u003Cvoid()> createControlPanel(\n        std::vector\u003Cstd::function\u003Cvoid()>> controls\n    ) {\n        return [controls]() {\n            for (const auto& control : controls) {\n                control();\n            }\n        };\n    }\n\n    \u002F**\n     * @brief 创建性能监控窗口\n     * @return 窗口渲染函数\n     *\u002F\n    static std::function\u003Cvoid()> createPerformanceWindow() {\n        return []() {\n            ImGuiIO& io = ImGui::GetIO();\n            ImGui::Text(\"性能监控\");\n            ImGui::Separator();\n            ImGui::Text(\"FPS: %.1f\", io.Framerate);\n            ImGui::Text(\"帧时间: %.3f ms\", 1000.0f \u002F io.Framerate);\n            ImGui::Text(\"绘制调用: %d\", io.MetricsRenderVertices);\n        };\n    }\n\n    \u002F**\n     * @brief 创建调试信息窗口\n     * @param debugInfo 调试信息获取函数\n     * @return 窗口渲染函数\n     *\u002F\n    static std::function\u003Cvoid()> createDebugWindow(\n        std::function\u003Cstd::string()> debugInfo\n    ) {\n        return [debugInfo]() {\n            ImGui::Text(\"调试信息\");\n            ImGui::Separator();\n            ImGui::TextWrapped(\"%s\", debugInfo().c_str());\n        };\n    }\n};\n\n",[17,5781,5782,5786,5790,5794,5798,5802,5806,5810,5815,5820,5824,5828,5832,5837,5841,5845,5850,5855,5860,5865,5869,5874,5878,5883,5888,5892,5897,5902,5907,5912,5917,5921,5925,5930,5934,5939,5944,5949,5953,5957,5961,5965,5969,5974,5978,5982,5987,5991,5996,6001,6005,6009,6013,6018,6023,6028,6032,6037,6042,6046,6051,6055,6060,6065,6070,6075,6080,6085,6089,6094,6099,6103,6108,6113,6118,6122,6127,6132,6136,6141,6146,6151,6155,6159,6163,6168,6173,6178,6183,6187,6192,6197,6201,6205,6209,6214,6218,6222,6227,6232,6237,6242,6246,6250,6254,6258,6263,6267,6272,6276,6281,6285,6289,6294,6298,6302,6306,6310,6315,6319,6324,6329,6333,6338,6343,6348,6352,6356,6360,6365,6369,6374,6378,6382,6387,6392,6397,6402,6407,6412,6417,6421,6425,6429,6433,6438,6442,6447,6451,6455,6460,6465,6469,6473,6477,6482,6486,6491,6496,6501,6506,6510,6514,6518,6523,6528,6532,6537,6542,6546,6550,6554,6558,6563,6567,6572,6576,6581,6586,6590,6594,6598,6603,6608,6613,6618,6623,6628,6632,6637,6642,6647,6651,6655,6660,6664,6669,6674,6679,6684,6688,6693,6698,6703,6708,6712,6717,6722,6726,6731,6736,6740,6744,6748,6752,6757,6761,6766,6770,6775,6780,6785,6790,6794,6798,6803,6807,6811,6815,6820,6825,6829,6834,6838,6843,6848,6852,6856,6860,6864,6868,6872,6877,6881,6885,6890,6895,6900,6904,6908,6912,6916,6921,6925,6930,6934,6939,6943,6948,6952,6956,6960,6965,6969,6973,6977,6982,6987,6991,6995,6999,7004,7008,7013,7017,7021,7026,7031,7036,7040,7045,7050,7055,7060,7065,7069,7073,7077,7081,7086,7090,7095,7100,7105,7110,7115,7119,7124,7129,7134,7139,7143,7148,7153,7157,7161,7165,7169,7173,7178,7182,7187,7191,7195,7200,7205,7210,7215,7219,7224,7229,7234,7239,7244,7249,7254,7259,7264,7268,7272,7276,7281,7286,7290,7294,7299,7304,7308,7313,7318,7323,7327,7331,7335,7339,7343,7349,7354,7359,7365,7371,7377,7383,7388,7394,7400,7406,7411,7416,7421,7426,7432,7438,7443,7448,7454,7460,7465,7471,7477,7482,7488,7493,7498],{"__ignoreMap":19},[74,5783,5784],{"class":76,"line":77},[74,5785,2484],{},[74,5787,5788],{"class":76,"line":116},[74,5789,543],{"emptyLinePlaceholder":542},[74,5791,5792],{"class":76,"line":271},[74,5793,4421],{},[74,5795,5796],{"class":76,"line":277},[74,5797,4426],{},[74,5799,5800],{"class":76,"line":322},[74,5801,4431],{},[74,5803,5804],{"class":76,"line":409},[74,5805,502],{},[74,5807,5808],{"class":76,"line":415},[74,5809,2515],{},[74,5811,5812],{"class":76,"line":421},[74,5813,5814],{},"#include \u003Cfunctional>\n",[74,5816,5817],{"class":76,"line":427},[74,5818,5819],{},"#include \u003Cunordered_map>\n",[74,5821,5822],{"class":76,"line":433},[74,5823,512],{},[74,5825,5826],{"class":76,"line":439},[74,5827,543],{"emptyLinePlaceholder":542},[74,5829,5830],{"class":76,"line":445},[74,5831,2529],{},[74,5833,5834],{"class":76,"line":451},[74,5835,5836],{}," * @brief ImGui 窗口管理器类 - 封装 ImGui 的初始化和窗口管理\n",[74,5838,5839],{"class":76,"line":560},[74,5840,2539],{},[74,5842,5843],{"class":76,"line":566},[74,5844,2544],{},[74,5846,5847],{"class":76,"line":572},[74,5848,5849],{}," * - 自动初始化和清理 ImGui\n",[74,5851,5852],{"class":76,"line":578},[74,5853,5854],{}," * - 窗口注册和管理\n",[74,5856,5857],{"class":76,"line":583},[74,5858,5859],{}," * - 统一的渲染循环\n",[74,5861,5862],{"class":76,"line":588},[74,5863,5864],{}," * - 可扩展的窗口系统\n",[74,5866,5867],{"class":76,"line":594},[74,5868,2569],{},[74,5870,5871],{"class":76,"line":600},[74,5872,5873],{},"class ImGuiManager {\n",[74,5875,5876],{"class":76,"line":606},[74,5877,2579],{},[74,5879,5880],{"class":76,"line":612},[74,5881,5882],{},"    GLFWwindow* window;\n",[74,5884,5885],{"class":76,"line":618},[74,5886,5887],{},"    bool initialized;\n",[74,5889,5890],{"class":76,"line":624},[74,5891,755],{},[74,5893,5894],{"class":76,"line":630},[74,5895,5896],{},"    \u002F\u002F 窗口注册系统\n",[74,5898,5899],{"class":76,"line":636},[74,5900,5901],{},"    struct WindowInfo {\n",[74,5903,5904],{"class":76,"line":642},[74,5905,5906],{},"        std::string name;\n",[74,5908,5909],{"class":76,"line":647},[74,5910,5911],{},"        std::function\u003Cvoid()> renderFunc;\n",[74,5913,5914],{"class":76,"line":652},[74,5915,5916],{},"        bool visible;\n",[74,5918,5919],{"class":76,"line":658},[74,5920,1269],{},[74,5922,5923],{"class":76,"line":664},[74,5924,755],{},[74,5926,5927],{"class":76,"line":670},[74,5928,5929],{},"    std::unordered_map\u003Cstd::string, WindowInfo> windows;\n",[74,5931,5932],{"class":76,"line":676},[74,5933,755],{},[74,5935,5936],{"class":76,"line":681},[74,5937,5938],{},"    \u002F\u002F 字体管理\n",[74,5940,5941],{"class":76,"line":687},[74,5942,5943],{},"    std::unordered_map\u003Cstd::string, ImFont*> fonts;\n",[74,5945,5946],{"class":76,"line":693},[74,5947,5948],{},"    std::string defaultFont;\n",[74,5950,5951],{"class":76,"line":699},[74,5952,543],{"emptyLinePlaceholder":542},[74,5954,5955],{"class":76,"line":705},[74,5956,2695],{},[74,5958,5959],{"class":76,"line":711},[74,5960,2700],{},[74,5962,5963],{"class":76,"line":717},[74,5964,2705],{},[74,5966,5967],{"class":76,"line":723},[74,5968,2725],{},[74,5970,5971],{"class":76,"line":728},[74,5972,5973],{},"    ImGuiManager() : window(nullptr), initialized(false) {}\n",[74,5975,5976],{"class":76,"line":734},[74,5977,543],{"emptyLinePlaceholder":542},[74,5979,5980],{"class":76,"line":740},[74,5981,2700],{},[74,5983,5984],{"class":76,"line":746},[74,5985,5986],{},"     * @brief 析构函数\n",[74,5988,5989],{"class":76,"line":752},[74,5990,2725],{},[74,5992,5993],{"class":76,"line":758},[74,5994,5995],{},"    ~ImGuiManager() {\n",[74,5997,5998],{"class":76,"line":764},[74,5999,6000],{},"        shutdown();\n",[74,6002,6003],{"class":76,"line":770},[74,6004,639],{},[74,6006,6007],{"class":76,"line":776},[74,6008,543],{"emptyLinePlaceholder":542},[74,6010,6011],{"class":76,"line":782},[74,6012,2700],{},[74,6014,6015],{"class":76,"line":787},[74,6016,6017],{},"     * @brief 初始化 ImGui 管理器\n",[74,6019,6020],{"class":76,"line":792},[74,6021,6022],{},"     * @param glfwWindow GLFW 窗口指针\n",[74,6024,6025],{"class":76,"line":798},[74,6026,6027],{},"     * @return 是否初始化成功\n",[74,6029,6030],{"class":76,"line":804},[74,6031,2725],{},[74,6033,6034],{"class":76,"line":810},[74,6035,6036],{},"    bool initialize(GLFWwindow* glfwWindow) {\n",[74,6038,6039],{"class":76,"line":816},[74,6040,6041],{},"        if (initialized) return true;\n",[74,6043,6044],{"class":76,"line":821},[74,6045,2217],{},[74,6047,6048],{"class":76,"line":827},[74,6049,6050],{},"        window = glfwWindow;\n",[74,6052,6053],{"class":76,"line":833},[74,6054,2217],{},[74,6056,6057],{"class":76,"line":839},[74,6058,6059],{},"        \u002F\u002F 初始化 ImGui\n",[74,6061,6062],{"class":76,"line":844},[74,6063,6064],{},"        IMGUI_CHECKVERSION();\n",[74,6066,6067],{"class":76,"line":850},[74,6068,6069],{},"        ImGui::CreateContext();\n",[74,6071,6072],{"class":76,"line":856},[74,6073,6074],{},"        ImGuiIO& io = ImGui::GetIO(); (void)io;\n",[74,6076,6077],{"class":76,"line":861},[74,6078,6079],{},"        io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;\n",[74,6081,6082],{"class":76,"line":867},[74,6083,6084],{},"        io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;\n",[74,6086,6087],{"class":76,"line":873},[74,6088,543],{"emptyLinePlaceholder":542},[74,6090,6091],{"class":76,"line":879},[74,6092,6093],{},"        \u002F\u002F 设置 ImGui 样式\n",[74,6095,6096],{"class":76,"line":885},[74,6097,6098],{},"        ImGui::StyleColorsDark();\n",[74,6100,6101],{"class":76,"line":891},[74,6102,543],{"emptyLinePlaceholder":542},[74,6104,6105],{"class":76,"line":896},[74,6106,6107],{},"        \u002F\u002F 初始化 ImGui 平台\u002F渲染器绑定\n",[74,6109,6110],{"class":76,"line":902},[74,6111,6112],{},"        ImGui_ImplGlfw_InitForOpenGL(window, true);\n",[74,6114,6115],{"class":76,"line":908},[74,6116,6117],{},"        ImGui_ImplOpenGL3_Init(\"#version 330\");\n",[74,6119,6120],{"class":76,"line":914},[74,6121,2217],{},[74,6123,6124],{"class":76,"line":920},[74,6125,6126],{},"        \u002F\u002F 加载默认字体\n",[74,6128,6129],{"class":76,"line":926},[74,6130,6131],{},"        loadDefaultFonts();\n",[74,6133,6134],{"class":76,"line":932},[74,6135,2217],{},[74,6137,6138],{"class":76,"line":937},[74,6139,6140],{},"        initialized = true;\n",[74,6142,6143],{"class":76,"line":942},[74,6144,6145],{},"        std::cout \u003C\u003C \"ImGui 管理器已初始化\" \u003C\u003C std::endl;\n",[74,6147,6148],{"class":76,"line":948},[74,6149,6150],{},"        return true;\n",[74,6152,6153],{"class":76,"line":954},[74,6154,639],{},[74,6156,6157],{"class":76,"line":960},[74,6158,543],{"emptyLinePlaceholder":542},[74,6160,6161],{"class":76,"line":966},[74,6162,2700],{},[74,6164,6165],{"class":76,"line":972},[74,6166,6167],{},"     * @brief 注册一个窗口\n",[74,6169,6170],{"class":76,"line":978},[74,6171,6172],{},"     * @param name 窗口名称\n",[74,6174,6175],{"class":76,"line":983},[74,6176,6177],{},"     * @param renderFunc 渲染函数\n",[74,6179,6180],{"class":76,"line":989},[74,6181,6182],{},"     * @param visible 是否默认可见\n",[74,6184,6185],{"class":76,"line":995},[74,6186,2725],{},[74,6188,6189],{"class":76,"line":1001},[74,6190,6191],{},"    void registerWindow(const std::string& name, std::function\u003Cvoid()> renderFunc, bool visible = true) {\n",[74,6193,6194],{"class":76,"line":1007},[74,6195,6196],{},"        windows[name] = {name, renderFunc, visible};\n",[74,6198,6199],{"class":76,"line":1013},[74,6200,639],{},[74,6202,6203],{"class":76,"line":1019},[74,6204,543],{"emptyLinePlaceholder":542},[74,6206,6207],{"class":76,"line":1025},[74,6208,2700],{},[74,6210,6211],{"class":76,"line":1031},[74,6212,6213],{},"     * @brief 切换窗口可见性\n",[74,6215,6216],{"class":76,"line":1037},[74,6217,6172],{},[74,6219,6220],{"class":76,"line":1043},[74,6221,2725],{},[74,6223,6224],{"class":76,"line":1049},[74,6225,6226],{},"    void toggleWindow(const std::string& name) {\n",[74,6228,6229],{"class":76,"line":1054},[74,6230,6231],{},"        auto it = windows.find(name);\n",[74,6233,6234],{"class":76,"line":1060},[74,6235,6236],{},"        if (it != windows.end()) {\n",[74,6238,6239],{"class":76,"line":1066},[74,6240,6241],{},"            it->second.visible = !it->second.visible;\n",[74,6243,6244],{"class":76,"line":1071},[74,6245,1063],{},[74,6247,6248],{"class":76,"line":1076},[74,6249,639],{},[74,6251,6252],{"class":76,"line":1082},[74,6253,543],{"emptyLinePlaceholder":542},[74,6255,6256],{"class":76,"line":1087},[74,6257,2700],{},[74,6259,6260],{"class":76,"line":1092},[74,6261,6262],{},"     * @brief 设置窗口可见性\n",[74,6264,6265],{"class":76,"line":1098},[74,6266,6172],{},[74,6268,6269],{"class":76,"line":1103},[74,6270,6271],{},"     * @param visible 是否可见\n",[74,6273,6274],{"class":76,"line":1108},[74,6275,2725],{},[74,6277,6278],{"class":76,"line":1113},[74,6279,6280],{},"    void setWindowVisible(const std::string& name, bool visible) {\n",[74,6282,6283],{"class":76,"line":1118},[74,6284,6231],{},[74,6286,6287],{"class":76,"line":1123},[74,6288,6236],{},[74,6290,6291],{"class":76,"line":1129},[74,6292,6293],{},"            it->second.visible = visible;\n",[74,6295,6296],{"class":76,"line":1134},[74,6297,1063],{},[74,6299,6300],{"class":76,"line":1140},[74,6301,639],{},[74,6303,6304],{"class":76,"line":1145},[74,6305,543],{"emptyLinePlaceholder":542},[74,6307,6308],{"class":76,"line":1150},[74,6309,2700],{},[74,6311,6312],{"class":76,"line":1156},[74,6313,6314],{},"     * @brief 开始新帧\n",[74,6316,6317],{"class":76,"line":1161},[74,6318,2725],{},[74,6320,6321],{"class":76,"line":1167},[74,6322,6323],{},"    void beginFrame() {\n",[74,6325,6326],{"class":76,"line":1173},[74,6327,6328],{},"        if (!initialized) return;\n",[74,6330,6331],{"class":76,"line":1179},[74,6332,2217],{},[74,6334,6335],{"class":76,"line":1184},[74,6336,6337],{},"        ImGui_ImplOpenGL3_NewFrame();\n",[74,6339,6340],{"class":76,"line":1190},[74,6341,6342],{},"        ImGui_ImplGlfw_NewFrame();\n",[74,6344,6345],{"class":76,"line":1196},[74,6346,6347],{},"        ImGui::NewFrame();\n",[74,6349,6350],{"class":76,"line":1202},[74,6351,639],{},[74,6353,6354],{"class":76,"line":1207},[74,6355,543],{"emptyLinePlaceholder":542},[74,6357,6358],{"class":76,"line":1213},[74,6359,2700],{},[74,6361,6362],{"class":76,"line":1219},[74,6363,6364],{},"     * @brief 渲染所有注册的窗口\n",[74,6366,6367],{"class":76,"line":1224},[74,6368,2725],{},[74,6370,6371],{"class":76,"line":1230},[74,6372,6373],{},"    void renderWindows() {\n",[74,6375,6376],{"class":76,"line":1236},[74,6377,6328],{},[74,6379,6380],{"class":76,"line":1242},[74,6381,2217],{},[74,6383,6384],{"class":76,"line":1248},[74,6385,6386],{},"        for (const auto& [name, windowInfo] : windows) {\n",[74,6388,6389],{"class":76,"line":1254},[74,6390,6391],{},"            if (windowInfo.visible) {\n",[74,6393,6394],{"class":76,"line":1260},[74,6395,6396],{},"                if (ImGui::Begin(name.c_str())) {\n",[74,6398,6399],{"class":76,"line":1266},[74,6400,6401],{},"                    windowInfo.renderFunc();\n",[74,6403,6404],{"class":76,"line":1272},[74,6405,6406],{},"                }\n",[74,6408,6409],{"class":76,"line":1277},[74,6410,6411],{},"                ImGui::End();\n",[74,6413,6414],{"class":76,"line":1283},[74,6415,6416],{},"            }\n",[74,6418,6419],{"class":76,"line":1289},[74,6420,1063],{},[74,6422,6423],{"class":76,"line":1295},[74,6424,639],{},[74,6426,6427],{"class":76,"line":1301},[74,6428,543],{"emptyLinePlaceholder":542},[74,6430,6431],{"class":76,"line":1307},[74,6432,2700],{},[74,6434,6435],{"class":76,"line":1313},[74,6436,6437],{},"     * @brief 结束帧并渲染\n",[74,6439,6440],{"class":76,"line":1319},[74,6441,2725],{},[74,6443,6444],{"class":76,"line":1324},[74,6445,6446],{},"    void endFrame() {\n",[74,6448,6449],{"class":76,"line":1330},[74,6450,6328],{},[74,6452,6453],{"class":76,"line":1336},[74,6454,2217],{},[74,6456,6457],{"class":76,"line":1342},[74,6458,6459],{},"        ImGui::Render();\n",[74,6461,6462],{"class":76,"line":1348},[74,6463,6464],{},"        ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());\n",[74,6466,6467],{"class":76,"line":1354},[74,6468,639],{},[74,6470,6471],{"class":76,"line":1360},[74,6472,543],{"emptyLinePlaceholder":542},[74,6474,6475],{"class":76,"line":1365},[74,6476,2700],{},[74,6478,6479],{"class":76,"line":1371},[74,6480,6481],{},"     * @brief 完整的渲染循环（推荐使用）\n",[74,6483,6484],{"class":76,"line":1377},[74,6485,2725],{},[74,6487,6488],{"class":76,"line":1382},[74,6489,6490],{},"    void render() {\n",[74,6492,6493],{"class":76,"line":1387},[74,6494,6495],{},"        beginFrame();\n",[74,6497,6498],{"class":76,"line":1392},[74,6499,6500],{},"        renderWindows();\n",[74,6502,6503],{"class":76,"line":1398},[74,6504,6505],{},"        endFrame();\n",[74,6507,6508],{"class":76,"line":1404},[74,6509,639],{},[74,6511,6512],{"class":76,"line":1409},[74,6513,543],{"emptyLinePlaceholder":542},[74,6515,6516],{"class":76,"line":1415},[74,6517,2700],{},[74,6519,6520],{"class":76,"line":1421},[74,6521,6522],{},"     * @brief 获取 ImGui IO\n",[74,6524,6525],{"class":76,"line":1426},[74,6526,6527],{},"     * @return ImGuiIO 引用\n",[74,6529,6530],{"class":76,"line":1431},[74,6531,2725],{},[74,6533,6534],{"class":76,"line":1436},[74,6535,6536],{},"    ImGuiIO& getIO() {\n",[74,6538,6539],{"class":76,"line":1442},[74,6540,6541],{},"        return ImGui::GetIO();\n",[74,6543,6544],{"class":76,"line":1448},[74,6545,639],{},[74,6547,6548],{"class":76,"line":1454},[74,6549,543],{"emptyLinePlaceholder":542},[74,6551,6552],{"class":76,"line":1460},[74,6553,543],{"emptyLinePlaceholder":542},[74,6555,6556],{"class":76,"line":1466},[74,6557,2700],{},[74,6559,6560],{"class":76,"line":1471},[74,6561,6562],{},"     * @brief 检查窗口是否存在\n",[74,6564,6565],{"class":76,"line":1477},[74,6566,6172],{},[74,6568,6569],{"class":76,"line":1483},[74,6570,6571],{},"     * @return 是否存在\n",[74,6573,6574],{"class":76,"line":1489},[74,6575,2725],{},[74,6577,6578],{"class":76,"line":1495},[74,6579,6580],{},"    bool hasWindow(const std::string& name) const {\n",[74,6582,6583],{"class":76,"line":1501},[74,6584,6585],{},"        return windows.find(name) != windows.end();\n",[74,6587,6588],{"class":76,"line":1507},[74,6589,639],{},[74,6591,6592],{"class":76,"line":1512},[74,6593,543],{"emptyLinePlaceholder":542},[74,6595,6596],{"class":76,"line":1517},[74,6597,2700],{},[74,6599,6600],{"class":76,"line":1523},[74,6601,6602],{},"     * @brief 加载字体\n",[74,6604,6605],{"class":76,"line":1529},[74,6606,6607],{},"     * @param fontPath 字体文件路径\n",[74,6609,6610],{"class":76,"line":1535},[74,6611,6612],{},"     * @param fontName 字体名称\n",[74,6614,6615],{"class":76,"line":1540},[74,6616,6617],{},"     * @param fontSize 字体大小\n",[74,6619,6620],{"class":76,"line":1546},[74,6621,6622],{},"     * @param glyphRanges 字符范围\n",[74,6624,6625],{"class":76,"line":1552},[74,6626,6627],{},"     * @return 是否加载成功\n",[74,6629,6630],{"class":76,"line":1557},[74,6631,2725],{},[74,6633,6634],{"class":76,"line":1563},[74,6635,6636],{},"    bool loadFont(const std::string& fontPath, const std::string& fontName, \n",[74,6638,6639],{"class":76,"line":1569},[74,6640,6641],{},"                  float fontSize = 16.0f, const ImWchar* glyphRanges = nullptr) {\n",[74,6643,6644],{"class":76,"line":1575},[74,6645,6646],{},"        if (!initialized) return false;\n",[74,6648,6649],{"class":76,"line":1580},[74,6650,2217],{},[74,6652,6653],{"class":76,"line":1585},[74,6654,5105],{},[74,6656,6657],{"class":76,"line":1590},[74,6658,6659],{},"        ImFont* font = io.Fonts->AddFontFromFileTTF(fontPath.c_str(), fontSize, nullptr, glyphRanges);\n",[74,6661,6662],{"class":76,"line":1596},[74,6663,2217],{},[74,6665,6666],{"class":76,"line":1602},[74,6667,6668],{},"        if (font) {\n",[74,6670,6671],{"class":76,"line":1607},[74,6672,6673],{},"            fonts[fontName] = font;\n",[74,6675,6676],{"class":76,"line":1613},[74,6677,6678],{},"            if (defaultFont.empty()) {\n",[74,6680,6681],{"class":76,"line":1619},[74,6682,6683],{},"                defaultFont = fontName;\n",[74,6685,6686],{"class":76,"line":1625},[74,6687,6416],{},[74,6689,6690],{"class":76,"line":1631},[74,6691,6692],{},"            \n",[74,6694,6695],{"class":76,"line":1637},[74,6696,6697],{},"            \u002F\u002F 重新构建字体纹理\n",[74,6699,6700],{"class":76,"line":1642},[74,6701,6702],{},"            io.Fonts->Build();\n",[74,6704,6705],{"class":76,"line":1648},[74,6706,6707],{},"            ImGui_ImplOpenGL3_CreateFontsTexture();\n",[74,6709,6710],{"class":76,"line":1653},[74,6711,6692],{},[74,6713,6714],{"class":76,"line":1658},[74,6715,6716],{},"            std::cout \u003C\u003C \"字体加载成功: \" \u003C\u003C fontName \u003C\u003C \" (\" \u003C\u003C fontPath \u003C\u003C \")\" \u003C\u003C std::endl;\n",[74,6718,6719],{"class":76,"line":1664},[74,6720,6721],{},"            return true;\n",[74,6723,6724],{"class":76,"line":1670},[74,6725,1028],{},[74,6727,6728],{"class":76,"line":1676},[74,6729,6730],{},"            std::cerr \u003C\u003C \"字体加载失败: \" \u003C\u003C fontPath \u003C\u003C std::endl;\n",[74,6732,6733],{"class":76,"line":1681},[74,6734,6735],{},"            return false;\n",[74,6737,6738],{"class":76,"line":1687},[74,6739,1063],{},[74,6741,6742],{"class":76,"line":1692},[74,6743,639],{},[74,6745,6746],{"class":76,"line":1697},[74,6747,543],{"emptyLinePlaceholder":542},[74,6749,6750],{"class":76,"line":1703},[74,6751,2700],{},[74,6753,6754],{"class":76,"line":1709},[74,6755,6756],{},"     * @brief 使用指定字体\n",[74,6758,6759],{"class":76,"line":1715},[74,6760,6612],{},[74,6762,6763],{"class":76,"line":1720},[74,6764,6765],{},"     * @return 是否成功使用字体\n",[74,6767,6768],{"class":76,"line":1725},[74,6769,2725],{},[74,6771,6772],{"class":76,"line":1730},[74,6773,6774],{},"    bool useFont(const std::string& fontName) {\n",[74,6776,6777],{"class":76,"line":1735},[74,6778,6779],{},"        auto it = fonts.find(fontName);\n",[74,6781,6782],{"class":76,"line":1741},[74,6783,6784],{},"        if (it != fonts.end() && it->second) {\n",[74,6786,6787],{"class":76,"line":1746},[74,6788,6789],{},"            ImGui::PushFont(it->second);\n",[74,6791,6792],{"class":76,"line":1752},[74,6793,6721],{},[74,6795,6796],{"class":76,"line":1757},[74,6797,1063],{},[74,6799,6800],{"class":76,"line":1763},[74,6801,6802],{},"        return false;\n",[74,6804,6805],{"class":76,"line":1768},[74,6806,639],{},[74,6808,6809],{"class":76,"line":1773},[74,6810,543],{"emptyLinePlaceholder":542},[74,6812,6813],{"class":76,"line":1778},[74,6814,2700],{},[74,6816,6817],{"class":76,"line":1784},[74,6818,6819],{},"     * @brief 弹出当前字体\n",[74,6821,6822],{"class":76,"line":1789},[74,6823,6824],{},"     * @return 是否成功弹出字体\n",[74,6826,6827],{"class":76,"line":1794},[74,6828,2725],{},[74,6830,6831],{"class":76,"line":1799},[74,6832,6833],{},"    bool popFont() {\n",[74,6835,6836],{"class":76,"line":1804},[74,6837,5105],{},[74,6839,6840],{"class":76,"line":1809},[74,6841,6842],{},"        if (io.Fonts->Fonts.Size > 0) {\n",[74,6844,6845],{"class":76,"line":1814},[74,6846,6847],{},"            ImGui::PopFont();\n",[74,6849,6850],{"class":76,"line":1819},[74,6851,6721],{},[74,6853,6854],{"class":76,"line":1824},[74,6855,1063],{},[74,6857,6858],{"class":76,"line":1829},[74,6859,6802],{},[74,6861,6862],{"class":76,"line":1835},[74,6863,639],{},[74,6865,6866],{"class":76,"line":1841},[74,6867,543],{"emptyLinePlaceholder":542},[74,6869,6870],{"class":76,"line":1847},[74,6871,2700],{},[74,6873,6874],{"class":76,"line":1852},[74,6875,6876],{},"     * @brief 设置默认字体\n",[74,6878,6879],{"class":76,"line":1858},[74,6880,6612],{},[74,6882,6883],{"class":76,"line":1863},[74,6884,2725],{},[74,6886,6887],{"class":76,"line":1869},[74,6888,6889],{},"    void setDefaultFont(const std::string& fontName) {\n",[74,6891,6892],{"class":76,"line":1875},[74,6893,6894],{},"        if (fonts.find(fontName) != fonts.end()) {\n",[74,6896,6897],{"class":76,"line":1881},[74,6898,6899],{},"            defaultFont = fontName;\n",[74,6901,6902],{"class":76,"line":1887},[74,6903,1063],{},[74,6905,6906],{"class":76,"line":1893},[74,6907,639],{},[74,6909,6910],{"class":76,"line":1898},[74,6911,543],{"emptyLinePlaceholder":542},[74,6913,6914],{"class":76,"line":1904},[74,6915,2700],{},[74,6917,6918],{"class":76,"line":1910},[74,6919,6920],{},"     * @brief 获取字体\n",[74,6922,6923],{"class":76,"line":1916},[74,6924,6612],{},[74,6926,6927],{"class":76,"line":1922},[74,6928,6929],{},"     * @return 字体指针\n",[74,6931,6932],{"class":76,"line":1928},[74,6933,2725],{},[74,6935,6936],{"class":76,"line":1934},[74,6937,6938],{},"    ImFont* getFont(const std::string& fontName) {\n",[74,6940,6941],{"class":76,"line":1940},[74,6942,6779],{},[74,6944,6945],{"class":76,"line":1946},[74,6946,6947],{},"        return (it != fonts.end()) ? it->second : nullptr;\n",[74,6949,6950],{"class":76,"line":1952},[74,6951,639],{},[74,6953,6954],{"class":76,"line":1957},[74,6955,543],{"emptyLinePlaceholder":542},[74,6957,6958],{"class":76,"line":1963},[74,6959,2700],{},[74,6961,6962],{"class":76,"line":1969},[74,6963,6964],{},"     * @brief 检查字体是否存在\n",[74,6966,6967],{"class":76,"line":1975},[74,6968,6612],{},[74,6970,6971],{"class":76,"line":1980},[74,6972,6571],{},[74,6974,6975],{"class":76,"line":1986},[74,6976,2725],{},[74,6978,6979],{"class":76,"line":1991},[74,6980,6981],{},"    bool hasFont(const std::string& fontName) const {\n",[74,6983,6984],{"class":76,"line":1996},[74,6985,6986],{},"        return fonts.find(fontName) != fonts.end();\n",[74,6988,6989],{"class":76,"line":2002},[74,6990,639],{},[74,6992,6993],{"class":76,"line":2007},[74,6994,543],{"emptyLinePlaceholder":542},[74,6996,6997],{"class":76,"line":2012},[74,6998,2700],{},[74,7000,7001],{"class":76,"line":2017},[74,7002,7003],{},"     * @brief 关闭 ImGui 管理器\n",[74,7005,7006],{"class":76,"line":2023},[74,7007,2725],{},[74,7009,7010],{"class":76,"line":2029},[74,7011,7012],{},"    void shutdown() {\n",[74,7014,7015],{"class":76,"line":2035},[74,7016,6328],{},[74,7018,7019],{"class":76,"line":2041},[74,7020,2217],{},[74,7022,7023],{"class":76,"line":2046},[74,7024,7025],{},"        ImGui_ImplOpenGL3_Shutdown();\n",[74,7027,7028],{"class":76,"line":2052},[74,7029,7030],{},"        ImGui_ImplGlfw_Shutdown();\n",[74,7032,7033],{"class":76,"line":2058},[74,7034,7035],{},"        ImGui::DestroyContext();\n",[74,7037,7038],{"class":76,"line":2064},[74,7039,2217],{},[74,7041,7042],{"class":76,"line":2070},[74,7043,7044],{},"        initialized = false;\n",[74,7046,7047],{"class":76,"line":2076},[74,7048,7049],{},"        windows.clear();\n",[74,7051,7052],{"class":76,"line":2081},[74,7053,7054],{},"        fonts.clear();\n",[74,7056,7057],{"class":76,"line":2087},[74,7058,7059],{},"        defaultFont.clear();\n",[74,7061,7062],{"class":76,"line":2093},[74,7063,7064],{},"        std::cout \u003C\u003C \"ImGui 管理器已关闭\" \u003C\u003C std::endl;\n",[74,7066,7067],{"class":76,"line":2099},[74,7068,639],{},[74,7070,7071],{"class":76,"line":2104},[74,7072,543],{"emptyLinePlaceholder":542},[74,7074,7075],{"class":76,"line":2110},[74,7076,2579],{},[74,7078,7079],{"class":76,"line":2116},[74,7080,2700],{},[74,7082,7083],{"class":76,"line":2122},[74,7084,7085],{},"     * @brief 加载默认字体\n",[74,7087,7088],{"class":76,"line":2127},[74,7089,2725],{},[74,7091,7092],{"class":76,"line":2133},[74,7093,7094],{},"    void loadDefaultFonts() {\n",[74,7096,7097],{"class":76,"line":2139},[74,7098,7099],{},"        \u002F\u002F 加载 Inter 字体\n",[74,7101,7102],{"class":76,"line":2145},[74,7103,7104],{},"        loadFont(\"Assets\u002FInter.ttc\", \"Inter\", 16.0f, ImGui::GetIO().Fonts->GetGlyphRangesChineseFull());\n",[74,7106,7107],{"class":76,"line":2150},[74,7108,7109],{},"        loadFont(\"Assets\u002FInter.ttc\", \"Inter-Large\", 20.0f, ImGui::GetIO().Fonts->GetGlyphRangesChineseFull());\n",[74,7111,7112],{"class":76,"line":2156},[74,7113,7114],{},"        loadFont(\"Assets\u002FInter.ttc\", \"Inter-Small\", 14.0f, ImGui::GetIO().Fonts->GetGlyphRangesChineseFull());\n",[74,7116,7117],{"class":76,"line":2162},[74,7118,2217],{},[74,7120,7121],{"class":76,"line":2167},[74,7122,7123],{},"        \u002F\u002F 加载 Windows 系统黑体字体\n",[74,7125,7126],{"class":76,"line":2173},[74,7127,7128],{},"        loadFont(\"C:\\\\Windows\\\\Fonts\\\\simhei.ttf\", \"SimHei\", 18.0f, ImGui::GetIO().Fonts->GetGlyphRangesChineseFull());\n",[74,7130,7131],{"class":76,"line":2179},[74,7132,7133],{},"        loadFont(\"C:\\\\Windows\\\\Fonts\\\\simhei.ttf\", \"SimHei-Large\", 22.0f, ImGui::GetIO().Fonts->GetGlyphRangesChineseFull());\n",[74,7135,7136],{"class":76,"line":2185},[74,7137,7138],{},"        loadFont(\"C:\\\\Windows\\\\Fonts\\\\simhei.ttf\", \"SimHei-Small\", 16.0f, ImGui::GetIO().Fonts->GetGlyphRangesChineseFull());\n",[74,7140,7141],{"class":76,"line":2191},[74,7142,2217],{},[74,7144,7145],{"class":76,"line":2197},[74,7146,7147],{},"        \u002F\u002F 设置默认字体为黑体\n",[74,7149,7150],{"class":76,"line":2202},[74,7151,7152],{},"        setDefaultFont(\"SimHei\");\n",[74,7154,7155],{"class":76,"line":2208},[74,7156,639],{},[74,7158,7159],{"class":76,"line":2214},[74,7160,1844],{},[74,7162,7163],{"class":76,"line":2220},[74,7164,543],{"emptyLinePlaceholder":542},[74,7166,7167],{"class":76,"line":2226},[74,7168,543],{"emptyLinePlaceholder":542},[74,7170,7171],{"class":76,"line":2232},[74,7172,2529],{},[74,7174,7175],{"class":76,"line":2238},[74,7176,7177],{}," * @brief 预定义的窗口创建器 - 提供常用的窗口模板\n",[74,7179,7180],{"class":76,"line":2243},[74,7181,2569],{},[74,7183,7184],{"class":76,"line":2249},[74,7185,7186],{},"class ImGuiWindowTemplates {\n",[74,7188,7189],{"class":76,"line":2255},[74,7190,2695],{},[74,7192,7193],{"class":76,"line":2260},[74,7194,2700],{},[74,7196,7197],{"class":76,"line":2266},[74,7198,7199],{},"     * @brief 创建信息显示窗口\n",[74,7201,7202],{"class":76,"line":2272},[74,7203,7204],{},"     * @param title 窗口标题\n",[74,7206,7207],{"class":76,"line":2278},[74,7208,7209],{},"     * @param infoFunc 信息获取函数\n",[74,7211,7212],{"class":76,"line":2284},[74,7213,7214],{},"     * @return 窗口渲染函数\n",[74,7216,7217],{"class":76,"line":2289},[74,7218,2725],{},[74,7220,7221],{"class":76,"line":2295},[74,7222,7223],{},"    static std::function\u003Cvoid()> createInfoWindow(\n",[74,7225,7226],{"class":76,"line":2301},[74,7227,7228],{},"        const std::string& title, \n",[74,7230,7231],{"class":76,"line":2306},[74,7232,7233],{},"        std::function\u003Cstd::string()> infoFunc\n",[74,7235,7236],{"class":76,"line":2312},[74,7237,7238],{},"    ) {\n",[74,7240,7241],{"class":76,"line":2317},[74,7242,7243],{},"        return [title, infoFunc]() {\n",[74,7245,7246],{"class":76,"line":2323},[74,7247,7248],{},"            ImGui::Text(\"=== %s ===\", title.c_str());\n",[74,7250,7251],{"class":76,"line":2329},[74,7252,7253],{},"            ImGui::Separator();\n",[74,7255,7256],{"class":76,"line":2335},[74,7257,7258],{},"            ImGui::Text(\"%s\", infoFunc().c_str());\n",[74,7260,7261],{"class":76,"line":2341},[74,7262,7263],{},"        };\n",[74,7265,7266],{"class":76,"line":2347},[74,7267,639],{},[74,7269,7270],{"class":76,"line":2353},[74,7271,543],{"emptyLinePlaceholder":542},[74,7273,7274],{"class":76,"line":2359},[74,7275,2700],{},[74,7277,7278],{"class":76,"line":2364},[74,7279,7280],{},"     * @brief 创建控制面板窗口\n",[74,7282,7283],{"class":76,"line":2370},[74,7284,7285],{},"     * @param controls 控制函数列表\n",[74,7287,7288],{"class":76,"line":2375},[74,7289,7214],{},[74,7291,7292],{"class":76,"line":2380},[74,7293,2725],{},[74,7295,7296],{"class":76,"line":2386},[74,7297,7298],{},"    static std::function\u003Cvoid()> createControlPanel(\n",[74,7300,7301],{"class":76,"line":2392},[74,7302,7303],{},"        std::vector\u003Cstd::function\u003Cvoid()>> controls\n",[74,7305,7306],{"class":76,"line":2397},[74,7307,7238],{},[74,7309,7310],{"class":76,"line":2402},[74,7311,7312],{},"        return [controls]() {\n",[74,7314,7315],{"class":76,"line":2408},[74,7316,7317],{},"            for (const auto& control : controls) {\n",[74,7319,7320],{"class":76,"line":2414},[74,7321,7322],{},"                control();\n",[74,7324,7325],{"class":76,"line":2420},[74,7326,6416],{},[74,7328,7329],{"class":76,"line":2426},[74,7330,7263],{},[74,7332,7333],{"class":76,"line":2432},[74,7334,639],{},[74,7336,7337],{"class":76,"line":2438},[74,7338,543],{"emptyLinePlaceholder":542},[74,7340,7341],{"class":76,"line":2444},[74,7342,2700],{},[74,7344,7346],{"class":76,"line":7345},346,[74,7347,7348],{},"     * @brief 创建性能监控窗口\n",[74,7350,7352],{"class":76,"line":7351},347,[74,7353,7214],{},[74,7355,7357],{"class":76,"line":7356},348,[74,7358,2725],{},[74,7360,7362],{"class":76,"line":7361},349,[74,7363,7364],{},"    static std::function\u003Cvoid()> createPerformanceWindow() {\n",[74,7366,7368],{"class":76,"line":7367},350,[74,7369,7370],{},"        return []() {\n",[74,7372,7374],{"class":76,"line":7373},351,[74,7375,7376],{},"            ImGuiIO& io = ImGui::GetIO();\n",[74,7378,7380],{"class":76,"line":7379},352,[74,7381,7382],{},"            ImGui::Text(\"性能监控\");\n",[74,7384,7386],{"class":76,"line":7385},353,[74,7387,7253],{},[74,7389,7391],{"class":76,"line":7390},354,[74,7392,7393],{},"            ImGui::Text(\"FPS: %.1f\", io.Framerate);\n",[74,7395,7397],{"class":76,"line":7396},355,[74,7398,7399],{},"            ImGui::Text(\"帧时间: %.3f ms\", 1000.0f \u002F io.Framerate);\n",[74,7401,7403],{"class":76,"line":7402},356,[74,7404,7405],{},"            ImGui::Text(\"绘制调用: %d\", io.MetricsRenderVertices);\n",[74,7407,7409],{"class":76,"line":7408},357,[74,7410,7263],{},[74,7412,7414],{"class":76,"line":7413},358,[74,7415,639],{},[74,7417,7419],{"class":76,"line":7418},359,[74,7420,543],{"emptyLinePlaceholder":542},[74,7422,7424],{"class":76,"line":7423},360,[74,7425,2700],{},[74,7427,7429],{"class":76,"line":7428},361,[74,7430,7431],{},"     * @brief 创建调试信息窗口\n",[74,7433,7435],{"class":76,"line":7434},362,[74,7436,7437],{},"     * @param debugInfo 调试信息获取函数\n",[74,7439,7441],{"class":76,"line":7440},363,[74,7442,7214],{},[74,7444,7446],{"class":76,"line":7445},364,[74,7447,2725],{},[74,7449,7451],{"class":76,"line":7450},365,[74,7452,7453],{},"    static std::function\u003Cvoid()> createDebugWindow(\n",[74,7455,7457],{"class":76,"line":7456},366,[74,7458,7459],{},"        std::function\u003Cstd::string()> debugInfo\n",[74,7461,7463],{"class":76,"line":7462},367,[74,7464,7238],{},[74,7466,7468],{"class":76,"line":7467},368,[74,7469,7470],{},"        return [debugInfo]() {\n",[74,7472,7474],{"class":76,"line":7473},369,[74,7475,7476],{},"            ImGui::Text(\"调试信息\");\n",[74,7478,7480],{"class":76,"line":7479},370,[74,7481,7253],{},[74,7483,7485],{"class":76,"line":7484},371,[74,7486,7487],{},"            ImGui::TextWrapped(\"%s\", debugInfo().c_str());\n",[74,7489,7491],{"class":76,"line":7490},372,[74,7492,7263],{},[74,7494,7496],{"class":76,"line":7495},373,[74,7497,639],{},[74,7499,7501],{"class":76,"line":7500},374,[74,7502,1844],{},[7504,7505,7506],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":19,"searchDepth":116,"depth":116,"links":7508},[7509,7515,7516,7517,7520,7521,7529,7532,7538,7539,7540],{"id":27,"depth":116,"text":28,"children":7510},[7511,7512,7513,7514],{"id":58,"depth":271,"text":58},{"id":93,"depth":271,"text":93},{"id":134,"depth":271,"text":134},{"id":163,"depth":271,"text":163},{"id":204,"depth":116,"text":205},{"id":2449,"depth":116,"text":2449},{"id":3902,"depth":116,"text":3903,"children":7518},[7519],{"id":3906,"depth":271,"text":3907},{"id":3944,"depth":116,"text":3945},{"id":4027,"depth":116,"text":4028,"children":7522},[7523,7525,7527],{"id":4031,"depth":271,"text":7524},"1. DPI 漂移检测",{"id":4069,"depth":271,"text":7526},"2. 平滑处理",{"id":4095,"depth":271,"text":7528},"3. 统计监控",{"id":4116,"depth":116,"text":4117,"children":7530},[7531],{"id":4120,"depth":271,"text":4121},{"id":4159,"depth":116,"text":4160,"children":7533},[7534,7536],{"id":4163,"depth":271,"text":7535},"1. 基本使用",{"id":4240,"depth":271,"text":7537},"2. 高级功能",{"id":4303,"depth":116,"text":4304},{"id":4339,"depth":116,"text":4340},{"id":4369,"depth":116,"text":4370},"md",{},"\u002Fcpp\u002Fopengel\u002F摄像机",{"description":19},"cpp\u002Fopengel\u002F摄像机","dJwrTlB_TC9vOCObHa8UeojwFEzrI8H5COuc3EPQgtA",1791042463410]