DXFViewer进行中,目标造一个dxf看图工具。.
目标1:封装OpenGL,实现正交相机及平移缩放功能

Application.h
cpp
#pragma once
#include <string>
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include "../Core/TimeStamp.h"
#include "../Core/OrthoCamera.h"
class Application
{
public:
Application();
virtual ~Application();
virtual void Initialize(int width, int height, const char* title); // 1.初始化
virtual void Run();
virtual void Startup(); // 1.准备数据
virtual void Render(); // 2.渲染数据
virtual void Shutdown(); // 3.关闭
private:
// 系统回调函数
static void ErrorCallback(int error, const char* description);
static void KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods);
static void MouseButtonCallback(GLFWwindow* window, int button, int action, int mods);
static void CursorPositionCallback(GLFWwindow* window, double xpos, double ypos);
static void WindowSizeCallback(GLFWwindow* window, int width, int height);
static void ScrollCallback(GLFWwindow* window, double xoffset, double yoffset);
static Application* GetWindow(GLFWwindow* window);
protected:
int m_winWidth = 0;
int m_winHeight = 0;
GLFWwindow* m_pWindow = nullptr;
OrthoCamera m_camera; // 相机
double m_LastCursorX = 0.0;
double m_LastCursorY = 0.0;
bool m_MiddleButtonPressed = false;
};
Application.cpp
cpp
#include "Application.h"
#include <algorithm>
#include <iostream>
Application::Application() {}
Application::~Application()
{
glfwDestroyWindow(m_pWindow);
glfwTerminate();
exit(EXIT_SUCCESS);
}
void Application::ErrorCallback(int error, const char* description)
{
std::cerr << "Error: " << description << std::endl;
}
void Application::KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
// 键盘事件处理逻辑
}
void Application::MouseButtonCallback(GLFWwindow* window, int button, int action, int mods)
{
Application* app = GetWindow(window);
if (button == GLFW_MOUSE_BUTTON_MIDDLE)
{
if (action == GLFW_PRESS)
{
app->m_MiddleButtonPressed = true;
glfwGetCursorPos(window, &app->m_LastCursorX, &app->m_LastCursorY);
// 设置抓手光标
// glfwSetCursor(window, glfwCreateStandardCursor(GLFW_HAND_CURSOR));
}
else if (action == GLFW_RELEASE)
{
app->m_MiddleButtonPressed = false;
// 恢复默认箭头光标
// glfwSetCursor(window, glfwCreateStandardCursor(GLFW_ARROW_CURSOR));
}
}
}
void Application::CursorPositionCallback(GLFWwindow* window, double xpos, double ypos)
{
Application* app = GetWindow(window);
if (app->m_MiddleButtonPressed)
{
double dx = xpos - app->m_LastCursorX;
double dy = ypos - app->m_LastCursorY;
app->m_LastCursorX = xpos;
app->m_LastCursorY = ypos;
glm::vec3 pos = app->m_camera.GetPosition();
pos.x -= (dx / app->m_camera.GetZoom() * 0.5);
pos.y += (dy / app->m_camera.GetZoom() * 0.5);
//printf("pos.x = %f, pos.y = %f \r\n", pos.x, pos.y);
app->m_camera.SetPosition(pos);
}
}
void Application::WindowSizeCallback(GLFWwindow* window, int width, int height)
{
Application* app = GetWindow(window);
app->m_winWidth = width;
app->m_winHeight = height;
app->m_camera.SetView(width, height);
}
void Application::ScrollCallback(GLFWwindow* window, double xoffset, double yoffset)
{
Application* app = GetWindow(window);
// 1. 获取鼠标在窗口中的位置
double mouseX, mouseY;
glfwGetCursorPos(window, &mouseX, &mouseY);
// 2. 将鼠标屏幕坐标转换为世界坐标
float ndcX = (2.0f * static_cast<float>(mouseX)) / app->m_winWidth - 1.0f;
float ndcY = 1.0f - (2.0f * static_cast<float>(mouseY)) / app->m_winHeight;
glm::vec4 ndcPos = glm::vec4(ndcX, ndcY, 0.0f, 1.0f);
glm::mat4 invVP = glm::inverse(app->m_camera.GetViewProjectionMatrix());
glm::vec4 worldPosBeforeZoom = invVP * ndcPos;
// 3. 执行缩放
float zoom = app->m_camera.GetZoom();
if (yoffset > 0)
zoom *= 1.15f;
else
zoom *= 0.85f;
app->m_camera.SetZoom(zoom);
// 4. 重新计算世界坐标
invVP = glm::inverse(app->m_camera.GetViewProjectionMatrix());
glm::vec4 worldPosAfterZoom = invVP * ndcPos;
// 5. 保持鼠标位置不变:调整相机位置
glm::vec3 camPos = app->m_camera.GetPosition();
glm::vec3 offset = glm::vec3(worldPosBeforeZoom - worldPosAfterZoom);
app->m_camera.SetPosition(camPos + glm::vec3(offset.x*0.5,offset.y*0.5,offset.z*0.5));
}
// 获取窗口的用户数据
Application* Application::GetWindow(GLFWwindow* window)
{
void* userdata = glfwGetWindowUserPointer(window);
return reinterpret_cast<Application*>(userdata);
}
void Application::Initialize(int width, int height, const char* title)
{
if (!glfwInit())
{
exit(EXIT_FAILURE);
}
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
m_pWindow = glfwCreateWindow(width, height, title, NULL, NULL);
m_winWidth = width;
m_winHeight = height;
if (!m_pWindow)
{
glfwTerminate();
exit(EXIT_FAILURE);
}
glfwDefaultWindowHints();
GLFWmonitor* primary = glfwGetPrimaryMonitor();
const GLFWvidmode* mode = glfwGetVideoMode(primary);
// 设置窗口位置为屏幕中心
glfwSetWindowPos(m_pWindow, (mode->width- width)/2, (mode->height - height) / 2);
// 窗口的物理尺寸
int widthMM, heightMM;
glfwGetMonitorPhysicalSize(primary, &widthMM, &heightMM);
// 计算 DPI
float dpiX = static_cast<float>(mode->width) / (static_cast<float>(widthMM) );
float dpiY = static_cast<float>(mode->height) / (static_cast<float>(heightMM));
glfwShowWindow(m_pWindow);
// 设置 glfw 回调函数
glfwSetKeyCallback(m_pWindow, KeyCallback);
glfwSetMouseButtonCallback(m_pWindow, MouseButtonCallback);
glfwSetCursorPosCallback(m_pWindow, CursorPositionCallback);
glfwSetWindowSizeCallback(m_pWindow, WindowSizeCallback);
glfwSetScrollCallback(m_pWindow, ScrollCallback);
glfwMakeContextCurrent(m_pWindow);
gladLoadGL();
glfwSwapInterval(1); // 垂直同步
glEnable(GL_DEPTH_TEST); // 开启深度测试
// 设置相机
m_camera.SetView(width, height);
glfwSetWindowUserPointer(m_pWindow, this);
}
void Application::Run()
{
Startup(); // 准备工作
while (!glfwWindowShouldClose(m_pWindow))
{
glfwGetFramebufferSize(m_pWindow, &m_winWidth, &m_winHeight);
glViewport(0, 0, m_winWidth, m_winHeight);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
Render(); // 渲染数据
glfwSwapBuffers(m_pWindow);
glfwWaitEvents(); // 等待事件
}
Shutdown(); // 关闭程序
}
void Application::Startup()
{
// 初始化数据
}
void Application::Render()
{
}
void Application::Shutdown()
{
// 清理资源
}
OrthoCamera.h
cpp
#pragma once
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
/// <summary>
/// 实现一个用于 2D 渲染的正交相机类。
/// 提供相机的位置、旋转、投影矩阵和视图矩阵等基本功能。
/// </summary>
class OrthoCamera
{
public:
OrthoCamera();
~OrthoCamera();
public:
void SetView(int windowWidth, int windowHeight);
void SetView(float fixedHeight, int windowWidth, int windowHeight);
void SetPosition(const glm::vec3& pos);
void SetZoom(float zoom);
const double& GetZoom() const { return m_Zoom ;}
const glm::vec3& GetPosition() const { return m_Position; }
const glm::mat4& GetViewProjectionMatrix() const { return m_ViewProjectionMatrix; };
private:
void RecalculateMatrix();
glm::vec3 m_Position = { 0.0f, 0.0f, 0.0f };
double m_Zoom = 200.0f;
double m_FixedHeight = 1000.0f;
int m_WindowWidth = 1280;
int m_WindowHeight = 720;
glm::mat4 m_ProjectionMatrix = glm::mat4(1.0f);
glm::mat4 m_ViewMatrix = glm::mat4(1.0f);
glm::mat4 m_ViewProjectionMatrix = glm::mat4(1.0f);
};
OrthoCamera.cpp
cpp
#include "OrthoCamera.h"
OrthoCamera::OrthoCamera()
{
}
OrthoCamera::~OrthoCamera()
{
}
void OrthoCamera::SetView(int windowWidth, int windowHeight)
{
m_FixedHeight = windowHeight;
m_WindowWidth = windowWidth;
m_WindowHeight = windowHeight;
RecalculateMatrix();
}
void OrthoCamera::SetView(float fixedHeight, int windowWidth, int windowHeight)
{
m_FixedHeight = fixedHeight;
m_WindowWidth = windowWidth;
m_WindowHeight = windowHeight;
RecalculateMatrix();
}
void OrthoCamera::SetPosition(const glm::vec3& pos)
{
m_Position = pos;
RecalculateMatrix();
}
void OrthoCamera::SetZoom(float zoom)
{
m_Zoom = zoom;
RecalculateMatrix();
}
void OrthoCamera::RecalculateMatrix()
{
float aspect = static_cast<float>(m_WindowWidth) / m_WindowHeight;
float halfHeight = (m_FixedHeight * 0.5f) / m_Zoom;
float halfWidth = halfHeight * aspect;
float left = m_Position.x - halfWidth;
float right = m_Position.x + halfWidth;
float bottom = m_Position.y - halfHeight;
float top = m_Position.y + halfHeight;
m_ProjectionMatrix = glm::ortho(left, right, bottom, top, -1000.0f, 1000.0f); // 扩展 zNear/zFar 范围
// 这里是关键:
m_ViewMatrix = glm::translate(glm::mat4(1.0f), -m_Position); // x/y/z 都考虑进去
m_ViewProjectionMatrix = m_ProjectionMatrix * m_ViewMatrix;
}
Triangle.h
cpp
#pragma once
#include "Entity.h"
#include "glm/glm.hpp"
#include "../Shader/Shader_P2_C3.h"
#include "../Core/OrthoCamera.h"
class Triangle // : public Entity
{
public:
Triangle()
{
m_vao = -1;
m_vbo = -1;
};
~Triangle()
{
glDeleteVertexArrays(1, &m_vao);
glDeleteBuffers(1, &m_vbo);
};
virtual void Init() // 数据准备
{
// 1.准备Shader
m_shader.Initialize();
// 2.准备数据
typedef struct Vertex
{
float x, y;
float r, g, b;
} Vertex;
Vertex vertices[3] =
{
{ -1.0f, -1.0f , 1.f, 0.f, 0.f},
{ 1.0f, -1.0f , 0.f, 1.f, 0.f},
{ 0.f, 1.0f , 0.f, 0.f, 1.f}
};
// Shader应用绑定顶点缓冲区数据
glGenVertexArrays(1, &m_vao);
glBindVertexArray(m_vao);
// 创建显存并向顶点缓冲填充数据
glGenBuffers(1, &m_vbo);
glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(m_shader.m_position, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)0);
glEnableVertexAttribArray(m_shader.m_position);
glVertexAttribPointer(m_shader.m_color, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)(2 * sizeof(float)));
glEnableVertexAttribArray(m_shader.m_color);
}
virtual void Render(const OrthoCamera &camera) // 绘制
{
m_shader.Begin();
glBindVertexArray(m_vao);
glUniformMatrix4fv(m_shader.m_mvp, 1, GL_FALSE, (const GLfloat*)&camera.GetViewProjectionMatrix());
glDrawArrays(GL_TRIANGLES, 0, 3);
m_shader.End();
}
public:
unsigned m_vao;
unsigned m_vbo;
Shader_P2_C3 m_shader;
};
Shader_P2_C3.h
cpp
#pragma once
#include "Shader.h"
class Shader_P2_C3:public Shader
{
public:
Shader_P2_C3()
{
m_mvp = -1;
m_position = -1;
m_color = -1;
};
~Shader_P2_C3()
{
};
virtual bool Initialize()
{
const char* vs = R"(
#version 330
uniform mat4 MVP;
in vec2 vPos;
in vec3 vCol;
out vec3 color;
void main()
{
gl_Position = MVP * vec4(vPos, 0.0, 1.0);
color = vCol;
}
)";
const char* ps = R"(
#version 330
in vec3 color;
out vec4 fragment;
void main()
{
fragment = vec4(color, 1.0);
//fragment = vec4(1.0,1,0,0);
}
)";
bool res = CreateShader(vs, ps);
if (res)
{
m_mvp = glGetUniformLocation(m_shaderId, "MVP");
m_position = glGetAttribLocation(m_shaderId, "vPos");
m_color = glGetAttribLocation(m_shaderId, "vCol");;
}
return true;
}
public:
int m_mvp;
int m_position;
int m_color;
};
Shader.h
cpp
#pragma once
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <assert.h>
#include <map>
#include <glm/gtc/type_ptr.hpp>
#include <string>
class Shader
{
public:
Shader();
~Shader();
public:
bool CreateShader(const char* vertex, const char* fragment);
virtual void Begin();
virtual void End();
void SetVec3(const std::string& name, const float x, const float y, const float z)const
{
auto cit = uniform.find(name);
glUniform3f(cit->second, x, y, z);
}
void SetVec3(const std::string& name, const glm::vec3& vec) const
{
auto cit = uniform.find(name);
glUniform3f(cit->second, vec.x, vec.y, vec.z);
}
void SetMat4(const std::string& name, const glm::mat4& mat) const
{
auto cit = uniform.find(name);
glUniformMatrix4fv(cit->second, 1, GL_FALSE, glm::value_ptr(mat));
}
void SetFloat(const std::string& name, const float x) const
{
auto cit = uniform.find(name);
glUniform1f(cit->second, x);
}
void SetInt(const std::string& name, const int d)const
{
auto cit = uniform.find(name);
glUniform1i(cit->second, d);
}
public:
int m_shaderId;
std::map <std::string, unsigned> uniform;
};
Shader.cpp
cpp
#include "Shader.h"
Shader::Shader():m_shaderId(0)
{
}
Shader::~Shader()
{
// 删除shader
glDeleteProgram(m_shaderId);
}
bool Shader::CreateShader(const char* vertex, const char* fragment)
{
bool error = false;
int vertexShader = 0;
int fragmentShader = 0;
do
{
if (vertex)
{
vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertex, 0);
glCompileShader(vertexShader);
GLint compileStatus;
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &compileStatus);
error = compileStatus == GL_FALSE;
if (error)
{
GLchar messages[256];
glGetShaderInfoLog(vertexShader, sizeof(messages), 0, messages);
assert(messages && 0 != 0);
break;
}
}
if (fragment)
{
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragment, 0);
glCompileShader(fragmentShader);
GLint compileStatus;
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &compileStatus);
error = compileStatus == GL_FALSE;
if (error)
{
GLchar messages[256];
glGetShaderInfoLog(fragmentShader, sizeof(messages), 0, messages);
assert(messages && 0 != 0);
break;
}
}
m_shaderId = glCreateProgram();
if (vertexShader)
{
glAttachShader(m_shaderId, vertexShader);
}
if (fragmentShader)
{
glAttachShader(m_shaderId, fragmentShader);
}
glLinkProgram(m_shaderId);
GLint linkStatus;
glGetProgramiv(m_shaderId, GL_LINK_STATUS, &linkStatus);
if (linkStatus == GL_FALSE)
{
GLchar messages[256];
glGetProgramInfoLog(m_shaderId, sizeof(messages), 0, messages);
break;
}
glUseProgram(m_shaderId);
} while (false);
if (error)
{
if (fragmentShader)
{
glDeleteShader(fragmentShader);
fragmentShader = 0;
}
if (vertexShader)
{
glDeleteShader(vertexShader);
vertexShader = 0;
}
if (m_shaderId)
{
glDeleteProgram(m_shaderId);
m_shaderId = 0;
}
}
return true;
}
void Shader::Begin()
{
glUseProgram(m_shaderId);
}
void Shader::End()
{
glUseProgram(0);
}
目标2:先解析DXF基础图元(直线)
dxf选用格式为utf-8明文编码,一下是图层组码信息:100 AcDbLine (直线) 。

AutoCAD 2025 Developer and ObjectARX 帮助 | LINE (DXF) | Autodesk
根据官方文档解析直线->以下组码适用于直线图元。
| LINE 组码 ||
组码 | 说明 |
---|---|
100 | 子类标记 (AcDbLine) |
39 | 厚度(可选;默认值 = 0) |
10 | 起点(在 WCS 中) DXF:X 值;APP:三维点 |
20, 30 | DXF:起点的 Y 值和 Z 值(在 WCS 中) |
11 | 端点(在 WCS 中) DXF:X 值;APP:三维点 |
21, 31 | DXF:端点的 Y 值和 Z 值(在 WCS 中) |
210 | 拉伸方向(可选;默认值 = 0, 0, 1) DXF:X 值;APP:三维矢量 |
220, 230 | DXF:拉伸方向的 Y 值和 Z 值(可选) |
cpp
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <map>
#include <Windows.h>
using Entity = std::map<std::string, std::string>;
/// <summary>
/// ifstream和getline获取中文乱码解决方案
/// </summary>
std::string UTF8ToGB(const char* str)
{
std::string result;
WCHAR* strSrc;
LPSTR szRes;
//获得临时变量的大小
int i = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0);
strSrc = new WCHAR[i + 1];
MultiByteToWideChar(CP_UTF8, 0, str, -1, strSrc, i);
//获得临时变量的大小
i = WideCharToMultiByte(CP_ACP, 0, strSrc, -1, NULL, 0, NULL, NULL);
szRes = new CHAR[i + 1];
WideCharToMultiByte(CP_ACP, 0, strSrc, -1, szRes, i, NULL, NULL);
result = szRes;
delete[]strSrc;
delete[]szRes;
return result;
}
// 去除前后空格
std::string trim(const std::string& str) {
auto first = str.find_first_not_of(" \t\r\n");
auto last = str.find_last_not_of(" \t\r\n");
return (first == std::string::npos) ? "" : str.substr(first, last - first + 1);
}
// 读取组码-值对
bool ReadCodes(std::ifstream& file, std::string& code, std::string& value) {
if (!std::getline(file, code)) return false;
if (!std::getline(file, value)) return false;
std::string strCode = UTF8ToGB(code.c_str()).c_str();
std::string strValue = UTF8ToGB(value.c_str()).c_str();
code = trim(strCode);
value = trim(strValue);
return true;
}
// 解析 DXF 中所有 LINE 实体的图层和坐标
std::vector<Entity> ParseDXFLines(const std::string& filename) {
std::ifstream file(filename, std::ios::binary);
if (!file.is_open()) {
std::cerr << "无法打开文件: " << filename << std::endl;
return {};
}
std::vector<Entity> lineEntities;
Entity currentEntity;
std::string code, value;
std::string currentLayer;
while (ReadCodes(file, code, value)) {
// 处理每个 AcDbEntity 实体
if (code == "100" && value == "AcDbEntity") {
// 获取图层信息
while (ReadCodes(file, code, value)) {
if (code == "8") {
currentLayer = value; // 保存图层名
break;
}
}
}
// 处理 AcDbLine 实体
if (code == "100" && value == "AcDbLine") {
Entity lineEntity;
lineEntity["Layer"] = currentLayer; // 设置图层信息
// 获取起点 (10, 20, 30)
while (ReadCodes(file, code, value)) {
if (code == "10") lineEntity["StartX"] = value;
if (code == "20") lineEntity["StartY"] = value;
if (code == "30") lineEntity["StartZ"] = value;
// 获取终点 (11, 21, 31)
if (code == "11") lineEntity["EndX"] = value;
if (code == "21") lineEntity["EndY"] = value;
if (code == "31") lineEntity["EndZ"] = value;
// 检查是否处理完一个线段(可根据需要继续读取更多信息)
if (lineEntity.size() == 6) {
lineEntities.push_back(lineEntity);
break;
}
}
}
}
file.close();
return lineEntities;
}
int main()
{
std::string filename = R"(C:\Users\Desktop\Drawing1.dxf)";
// 解析文件
auto lines = ParseDXFLines(filename);
// 输出解析的直线信息
for (const auto& entity : lines) {
std::string layer = entity.count("Layer") ? entity.at("Layer") : "(未知图层)";
std::string x1 = entity.count("StartX") ? entity.at("StartX") : "?";
std::string y1 = entity.count("StartY") ? entity.at("StartY") : "?";
std::string x2 = entity.count("EndX") ? entity.at("EndX") : "?";
std::string y2 = entity.count("EndY") ? entity.at("EndY") : "?";
std::cout << "图层: " << layer
<< ",起点: (" << x1 << ", " << y1 << ")"
<< ",终点: (" << x2 << ", " << y2 << ")"
<< std::endl;
}
return 0;
}
