#pragma once #include <QOpenGLFunctions_4_5_Core> #include <QOpenGLShaderProgram> #include <QOpenGLTexture> #include <QVector3D> #include <QVector2D> #include <QVector4D> #include <vector> #include <memory> #include <QString>
struct CubeVertex { QVector3D pos; QVector2D texCoord; };
class ViewCube : protected QOpenGLFunctions_4_5_Core { public: explicit ViewCube(QOpenGLFunctions_4_5_Core* gl, const QString& texPath, unsigned int windowWidth, unsigned int windowHeight) : m_gl(gl), m_texPath(texPath), m_windowWidth(windowWidth), m_windowHeight(windowHeight) { init(); }
void draw(QOpenGLShaderProgram& shader, const QMatrix4x4& model, const QMatrix4x4& view, const QMatrix4x4& projection) { m_modelMatrix = model; m_viewMatrix = view; m_projectionMatrix = projection;
m_gl->glBindVertexArray(m_vao); shader.setUniformValue("model", model); m_texture->bind(0); shader.setUniformValue("u_texture", 0); m_gl->glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, nullptr); m_gl->glBindVertexArray(0); m_texture->release(); }
enum class CubeFaceType { None, Top, Bottom, Left, Right, Front, Back }; void setWindowSize(int width, int height) { m_windowWidth = width; m_windowHeight = height; }
CubeFaceType getFirstHitFace( float mouseX, float mouseY, int windowWidth, int windowHeight, const QMatrix4x4& viewMatrix, const QMatrix4x4& projectionMatrix) { Ray ray = createRayFromScreenCoord(mouseX, mouseY, windowWidth, windowHeight, viewMatrix, projectionMatrix);
std::vector<Plane> cubePlanes = createViewCubePlanes();
float minT = std::numeric_limits<float>::max(); CubeFaceType hitFace = CubeFaceType::None;
for (const auto& plane : cubePlanes) { float t = getIntersectionT(ray, plane); if (t > 0 && t < minT) { minT = t; hitFace = plane.face; } }
return hitFace; }
private: struct Ray { QVector3D origin; QVector3D direction; };
struct Plane { float a, b, c, d; CubeFaceType face; Plane(float a, float b, float c, float d, CubeFaceType face) : a(a), b(b), c(c), d(d), face(face) { } };
std::vector<Plane> createViewCubePlanes() { return { Plane(0, 0, 1, -0.5f, CubeFaceType::Front), Plane(0, 0, -1, -0.5f, CubeFaceType::Back), Plane(-1, 0, 0, -0.5f, CubeFaceType::Left), Plane(1, 0, 0, -0.5f, CubeFaceType::Right), Plane(0, 1, 0, -0.5f, CubeFaceType::Top), Plane(0, -1, 0, -0.5f, CubeFaceType::Bottom) }; }
float getIntersectionT(const Ray& ray, const Plane& plane) { const float eps = 1e-6; float denom = plane.a * ray.direction.x() + plane.b * ray.direction.y() + plane.c * ray.direction.z(); if (fabs(denom) < eps) return -1;
float numerator = -(plane.a * ray.origin.x() + plane.b * ray.origin.y() + plane.c * ray.origin.z() + plane.d); float t = numerator / denom; if (t < eps) return -1;
QVector3D hitPoint = ray.origin + ray.direction * t;
switch (plane.face) { case CubeFaceType::Front: case CubeFaceType::Back: if (hitPoint.x() < -0.5f - eps || hitPoint.x() > 0.5f + eps) return -1; if (hitPoint.y() < -0.5f - eps || hitPoint.y() > 0.5f + eps) return -1; break; case CubeFaceType::Left: case CubeFaceType::Right: if (hitPoint.y() < -0.5f - eps || hitPoint.y() > 0.5f + eps) return -1; if (hitPoint.z() < -0.5f - eps || hitPoint.z() > 0.5f + eps) return -1; break; case CubeFaceType::Top: case CubeFaceType::Bottom: if (hitPoint.x() < -0.5f - eps || hitPoint.x() > 0.5f + eps) return -1; if (hitPoint.z() < -0.5f - eps || hitPoint.z() > 0.5f + eps) return -1; break; default: return -1; }
return t; }
Ray createRayFromScreenCoord( float screenX, float screenY, int windowWidth, int windowHeight, const QMatrix4x4& viewMatrix, const QMatrix4x4& projectionMatrix) { float ndcX = (2.0f * screenX) / windowWidth - 1.0f; float ndcY = 1.0f - (2.0f * screenY) / windowHeight; QMatrix4x4 invViewProj = (projectionMatrix * viewMatrix).inverted(); QVector4D nearPt(ndcX, ndcY, -1.0f, 1.0f); QVector4D farPt(ndcX, ndcY, 1.0f, 1.0f); QVector4D worldNearHomogeneous = invViewProj * nearPt; QVector3D worldNear = worldNearHomogeneous.toVector3D() / worldNearHomogeneous.w();
QVector4D worldFarHomogeneous = invViewProj * farPt; QVector3D worldFar = worldFarHomogeneous.toVector3D() / worldFarHomogeneous.w(); return { worldNear, (worldFar - worldNear).normalized() }; }
void init() { m_gl->initializeOpenGLFunctions(); generateVertices(); createTexture(); setupBuffers(); }
void generateVertices() { const QVector3D positions[8] = { { -0.5f, -0.5f, -0.5f },{ 0.5f, -0.5f, -0.5f }, { 0.5f, 0.5f, -0.5f },{ -0.5f, 0.5f, -0.5f }, { -0.5f, -0.5f, 0.5f },{ 0.5f, -0.5f, 0.5f }, { 0.5f, 0.5f, 0.5f },{ -0.5f, 0.5f, 0.5f } };
const QVector2D texCoords[6][4] = { { { 0.0f, 1.0f },{ 0.5f, 1.0f },{ 0.5f, 2.0f / 3.0f },{ 0.0f, 2.0f / 3.0f } }, { { 1.0f, 2.0f / 3.0f },{ 0.5f, 2.0f / 3.0f },{ 0.5f, 1.0f },{ 1.0f, 1.0f } }, { { 0.0f, 1.0f / 3.0f },{ 0.0f, 2.0f / 3.0f },{ 0.5f, 2.0f / 3.0f },{ 0.5f, 1.0f / 3.0f } }, { { 1.0f, 2.0f / 3.0f },{ 1.0f, 1.0f / 3.0f },{ 0.5f, 1.0f / 3.0f },{ 0.5f, 2.0f / 3.0f } }, { { 0.0f, 0.0f },{ 0.5f, 0.0f },{ 0.5f, 1.0f / 3.0f },{ 0.0f, 1.0f / 3.0f } }, { { 1.0f, 0.0f } ,{ 0.5f, 0.0f },{ 0.5f, 1.0f / 3.0f } ,{ 1.0f, 1.0f / 3.0f } } };
const unsigned int indices[6][6] = { { 3, 2, 6, 3, 7, 6 },{ 1, 0, 4, 4, 5, 1 }, { 0, 3, 7, 7, 4, 0 },{ 2, 1, 5, 5, 6, 2 }, { 4, 5, 6, 6, 7, 4 },{ 0, 1, 2, 2, 3, 0 } };
m_vertices.clear(); m_indices.clear();
for (int face = 0; face < 6; ++face) { for (int i = 0; i < 6; ++i) { unsigned int globalIdx = indices[face][i]; int localIdx = -1; for (int j = 0; j < 4; ++j) { if (faceVertices[face][j] == globalIdx) { localIdx = j; break; } } CubeVertex v; v.pos = positions[globalIdx]; v.texCoord = texCoords[face][localIdx]; m_vertices.push_back(v); } }
for (unsigned int i = 0; i < m_vertices.size(); ++i) { m_indices.push_back(i); } }
void createTexture() { QImage img(m_texPath); if (img.isNull()) { qWarning() << "纹理加载失败:" << m_texPath; return; } m_texture = std::make_unique<QOpenGLTexture>(img.mirrored()); m_texture->setMinificationFilter(QOpenGLTexture::Linear); m_texture->setMagnificationFilter(QOpenGLTexture::Linear); }
void setupBuffers() { m_gl->glGenVertexArrays(1, &m_vao); m_gl->glGenBuffers(1, &m_vbo); m_gl->glGenBuffers(1, &m_ebo);
m_gl->glBindVertexArray(m_vao); m_gl->glBindBuffer(GL_ARRAY_BUFFER, m_vbo); m_gl->glBufferData(GL_ARRAY_BUFFER, m_vertices.size() * sizeof(CubeVertex), m_vertices.data(), GL_STATIC_DRAW); m_gl->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ebo); m_gl->glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_indices.size() * sizeof(unsigned int), m_indices.data(), GL_STATIC_DRAW);
m_gl->glEnableVertexAttribArray(0); m_gl->glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(CubeVertex), (void*)offsetof(CubeVertex, pos)); m_gl->glEnableVertexAttribArray(1); m_gl->glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(CubeVertex), (void*)offsetof(CubeVertex, texCoord));
m_gl->glBindVertexArray(0); }
private: QOpenGLFunctions_4_5_Core* m_gl; QString m_texPath; std::vector<CubeVertex> m_vertices; std::vector<unsigned int> m_indices; unsigned int m_vao, m_vbo, m_ebo; std::unique_ptr<QOpenGLTexture> m_texture; const unsigned int faceVertices[6][4] = { { 3, 2, 6, 7 },{ 1, 0, 4, 5 },{ 0, 3, 7, 4 }, { 2, 1, 5, 6 },{ 4, 5, 6, 7 },{ 0, 1, 2, 3 } };
QMatrix4x4 m_modelMatrix; QMatrix4x4 m_viewMatrix; QMatrix4x4 m_projectionMatrix; int m_windowWidth; int m_windowHeight; };
|