From b33591fe0f85bfb2cca492c0ed14589a77f8372c Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Fri, 19 May 2023 01:30:05 -0600 Subject: [PATCH] lightpreview: wip textured rendering --- lightpreview/glview.cpp | 143 ++++++++++++++++++++++++++---------- lightpreview/glview.h | 14 +++- lightpreview/mainwindow.cpp | 2 +- 3 files changed, 119 insertions(+), 40 deletions(-) diff --git a/lightpreview/glview.cpp b/lightpreview/glview.cpp index c67747b6..db4ec03f 100644 --- a/lightpreview/glview.cpp +++ b/lightpreview/glview.cpp @@ -22,6 +22,7 @@ See file, 'COPYING', for details. // #include #include +#include #include #include #include @@ -30,6 +31,8 @@ See file, 'COPYING', for details. #include #include +#include +#include GLView::GLView(QWidget *parent) : QOpenGLWidget(parent), @@ -41,9 +44,9 @@ GLView::GLView(QWidget *parent) m_cameraFwd(0, 1, 0), m_vao(), m_indexBuffer(QOpenGLBuffer::IndexBuffer), - m_indexCount(0), m_program(nullptr), - m_program_mvp_location(0) + m_program_mvp_location(0), + m_program_texture_sampler_location(0) { setFocusPolicy(Qt::StrongFocus); // allow keyboard focus } @@ -64,10 +67,14 @@ GLView::~GLView() static const char *s_fragShader = R"( #version 330 core +in vec2 uv; + out vec4 color; +uniform sampler2D texture_sampler; + void main() { - color = vec4(1.0, 0.5, 0.2, 0.2); + color = vec4(texture(texture_sampler, uv).rgb, 1.0); } )"; @@ -75,11 +82,16 @@ static const char *s_vertShader = R"( #version 330 core layout (location = 0) in vec3 position; +layout (location = 1) in vec2 vertex_uv; + +out vec2 uv; uniform mat4 MVP; void main() { gl_Position = MVP * vec4(position.x, position.y, position.z, 1.0); + + uv = vertex_uv; } )"; @@ -92,12 +104,11 @@ void GLView::initializeGL() m_program = new QOpenGLShaderProgram(); m_program->addShaderFromSourceCode(QOpenGLShader::Vertex, s_vertShader); m_program->addShaderFromSourceCode(QOpenGLShader::Fragment, s_fragShader); - m_program->bindAttributeLocation("vertex", 0); assert(m_program->link()); m_program->bind(); m_program_mvp_location = m_program->uniformLocation("MVP"); - + m_program_texture_sampler_location = m_program->uniformLocation("texture_sampler"); m_vao.create(); } @@ -118,24 +129,20 @@ void GLView::paintGL() QMatrix4x4 MVP = projectionMatrix * viewMatrix * modelMatrix; m_program->setUniformValue(m_program_mvp_location, MVP); + m_program->setUniformValue(m_program_texture_sampler_location, 0 /* texture unit */); + + for (auto &draw : m_drawcalls) { + draw.texture->bind(0 /* texture unit */); - if (m_indexCount > 0) { QOpenGLVertexArrayObject::Binder vaoBinder(&m_vao); - glEnableVertexAttribArray(0); - assert(m_vbo.bind()); - glVertexAttribPointer(0 /* attrib */, 3, GL_FLOAT, GL_FALSE, 0, (void *)0); + // glEnable(GL_BLEND); + // glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - assert(m_indexBuffer.bind()); + glDrawElements(GL_TRIANGLES, draw.index_count, GL_UNSIGNED_INT, + reinterpret_cast(draw.first_index * sizeof(uint32_t))); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - glDrawElements(GL_TRIANGLES, m_indexCount, GL_UNSIGNED_INT, (void *)0); - - glDisable(GL_BLEND); - - glDisableVertexAttribArray(0); + // glDisable(GL_BLEND); } m_program->release(); @@ -143,40 +150,104 @@ void GLView::paintGL() void GLView::renderBSP(const mbsp_t &bsp) { + // FIXME: move to a lightpreview_settings + settings::common_settings settings; + + bsp.loadversion->game->init_filesystem("placeholder.map", settings); + img::load_textures(&bsp, settings); + // NOTE: according to https://doc.qt.io/qt-6/qopenglwidget.html#resource-initialization-and-cleanup // we can only do this after `initializeGL()` has run once. makeCurrent(); - QOpenGLVertexArrayObject::Binder vaoBinder(&m_vao); - - // just upload the bsp.dvertexes data as-is - m_vbo.create(); - m_vbo.bind(); - m_vbo.allocate(bsp.dvertexes.data(), bsp.dvertexes.size() * sizeof(bsp.dvertexes[0])); - - // populate index buffer - std::vector indexBuffer; auto &m = bsp.dmodels[0]; + // collect faces grouped by texture name + std::map> faces_by_texname; + for (int i = m.firstface; i < m.firstface + m.numfaces; ++i) { auto &f = bsp.dfaces[i]; - + std::string t = Face_TextureName(&bsp, &f); + // FIXME: keep empty texture names? + if (t.empty()) + continue; if (f.numedges < 3) continue; - - for (int j = 2; j < f.numedges; ++j) { - indexBuffer.push_back(Face_VertexAtIndex(&bsp, &f, 0)); - indexBuffer.push_back(Face_VertexAtIndex(&bsp, &f, j - 1)); - indexBuffer.push_back(Face_VertexAtIndex(&bsp, &f, j)); - } + faces_by_texname[t].push_back(&f); } + // populate the vertex/index buffers + struct vertex_t + { + qvec3f pos; + qvec2f uv; + }; + std::vector verts; + std::vector indexBuffer; + + for (const auto &[texname, faces] : faces_by_texname) { + // upload texture + // FIXME: we should have a separate lightpreview_options + auto *texture = img::find(texname); + + if (!texture) { + logging::print("warning, couldn't locate {}", texname); + continue; + } + + std::unique_ptr qtexture = + std::make_unique(QImage(reinterpret_cast(texture->pixels.data()), + texture->width, texture->height, QImage::Format_RGBA8888)); + + const size_t dc_first_index = indexBuffer.size(); + + for (const mface_t *f : faces) { + const size_t first_vertex_of_face = verts.size(); + + // output a vertex for each vertex of the face + for (int j = 0; j < f->numedges; ++j) { + qvec3f pos = Face_PointAtIndex(&bsp, f, j); + qvec2f uv = Face_WorldToTexCoord(&bsp, f, pos); + + uv[0] *= (1.0 / texture->width); + uv[1] *= (1.0 / texture->height); + + verts.push_back({.pos = pos, .uv = uv}); + } + + // output the vertex indices for this face + for (int j = 2; j < f->numedges; ++j) { + indexBuffer.push_back(first_vertex_of_face); + indexBuffer.push_back(first_vertex_of_face + j - 1); + indexBuffer.push_back(first_vertex_of_face + j); + } + } + + const size_t dc_index_count = indexBuffer.size() - dc_first_index; + + drawcall_t dc = {.texture = std::move(qtexture), .first_index = dc_first_index, .index_count = dc_index_count}; + m_drawcalls.push_back(std::move(dc)); + } + + QOpenGLVertexArrayObject::Binder vaoBinder(&m_vao); + // upload index buffer m_indexBuffer.create(); m_indexBuffer.bind(); m_indexBuffer.allocate(indexBuffer.data(), indexBuffer.size() * sizeof(indexBuffer[0])); - m_indexCount = indexBuffer.size(); + // upload vertex buffer + m_vbo.create(); + m_vbo.bind(); + m_vbo.allocate(verts.data(), verts.size() * sizeof(verts[0])); + + // positions + glEnableVertexAttribArray(0 /* attrib */); + glVertexAttribPointer(0 /* attrib */, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void *)0); + + // normals + glEnableVertexAttribArray(1 /* attrib */); + glVertexAttribPointer(1 /* attrib */, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void *)(3 * sizeof(float))); doneCurrent(); @@ -268,8 +339,6 @@ void GLView::wheelEvent(QWheelEvent *event) void GLView::timerEvent(QTimerEvent *event) { - fmt::print("key movement {}\n", QTime::currentTime().toString().toStdString()); - const float speed = 10; if (m_keysPressed & static_cast(keys_t::up)) diff --git a/lightpreview/glview.h b/lightpreview/glview.h index 78d4da41..dedf7774 100644 --- a/lightpreview/glview.h +++ b/lightpreview/glview.h @@ -24,11 +24,13 @@ See file, 'COPYING', for details. #include #include #include - +#include #include #include #include +#include + enum class keys_t : uint32_t { none = 0, @@ -61,12 +63,20 @@ private: QOpenGLVertexArrayObject m_vao; QOpenGLBuffer m_vbo; QOpenGLBuffer m_indexBuffer; - int m_indexCount; + + struct drawcall_t + { + std::unique_ptr texture; + size_t first_index = 0; + size_t index_count = 0; + }; + std::vector m_drawcalls; QOpenGLShaderProgram *m_program; // uniform locations int m_program_mvp_location; + int m_program_texture_sampler_location; public: GLView(QWidget *parent = nullptr); diff --git a/lightpreview/mainwindow.cpp b/lightpreview/mainwindow.cpp index 3b745390..c6db5214 100644 --- a/lightpreview/mainwindow.cpp +++ b/lightpreview/mainwindow.cpp @@ -139,7 +139,7 @@ void MainWindow::loadFileInternal(const QString &file) { qDebug() << "loadFileInternal " << file; - auto d = QbspVisLight_Common(MakeFSPath(file), {}, {}, true); + auto d = QbspVisLight_Common(MakeFSPath(file), {}, {}, false); const auto &bsp = std::get(d.bsp);