// Copyright (C) 2025 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "lightmapmesh.h" #include #include #include LightmapMesh::LightmapMesh(QQuick3DObject *parent) : QQuick3DGeometry(parent) { } LightmapMesh::~LightmapMesh() = default; void LightmapMesh::updateData() { if (!m_source.isValid() || m_key.isEmpty()) return; if (m_currentlyLoadedSource == m_source && m_currentlyLoadedKey == m_key) return; auto loader = QSSGLightmapLoader::open(m_source.toLocalFile()); if (!loader) { qWarning("Failed to open lightmap source file"); return; } const auto keys = loader->getKeys(); const bool isMeshKey = keys.contains(std::make_pair(m_key, QSSGLightmapIODataTag::Mesh)); if (!isMeshKey) { return; } QByteArray meshData = loader->readData(m_key, QSSGLightmapIODataTag::Mesh); if (meshData.isEmpty()) return; QBuffer buffer(&meshData); buffer.open(QIODevice::ReadOnly); const QSSGMesh::Mesh mesh = QSSGMesh::Mesh::loadMesh(&buffer, 1); if (!mesh.isValid()) { qWarning("Mesh load failed"); return; } if (!mesh.hasLightmapUVChannel()) { qWarning("Mesh does not have lightmap UVs"); return; } clear(); const auto &vb = mesh.vertexBuffer(); const auto &ib = mesh.indexBuffer(); setVertexData(vb.data); setIndexData(ib.data); setStride(vb.stride); setPrimitiveType(QQuick3DGeometry::PrimitiveType::Triangles); auto findEntry = [&](const QByteArray &name) -> const QSSGMesh::Mesh::VertexBufferEntry * { for (const auto &e : vb.entries) if (e.name == name) return &e; return nullptr; }; const QByteArray posName = QSSGMesh::MeshInternal::getPositionAttrName(); const QByteArray lmuvName = QSSGMesh::MeshInternal::getLightmapUVAttrName(); const auto *pos = findEntry(posName); const auto *lmuv = findEntry(lmuvName); if (!pos) { qWarning("Position attribute not found"); return; } if (!lmuv) { qWarning("Lightmap UV attribute not found"); return; } addAttribute(QQuick3DGeometry::Attribute::PositionSemantic, pos->offset, Attribute::ComponentType::F32Type); addAttribute(QQuick3DGeometry::Attribute::TexCoord0Semantic, lmuv->offset, Attribute::ComponentType::F32Type); Attribute::ComponentType ibCompType; switch (ib.componentType) { case QSSGRenderComponentType::UnsignedInt16: ibCompType = Attribute::ComponentType::U16Type; break; case QSSGRenderComponentType::UnsignedInt32: ibCompType = Attribute::ComponentType::U32Type; break; case QSSGRenderComponentType::Int32: ibCompType = Attribute::ComponentType::I32Type; break; case QSSGRenderComponentType::Float32: ibCompType = Attribute::ComponentType::F32Type; break; default: ibCompType = Attribute::ComponentType::U32Type; } addAttribute(QQuick3DGeometry::Attribute::IndexSemantic, 0, ibCompType); m_bounds.bounds.minimum = QVector3D(std::numeric_limits::max(), std::numeric_limits::max(), std::numeric_limits::max()); m_bounds.bounds.maximum = QVector3D(-std::numeric_limits::max(), -std::numeric_limits::max(), -std::numeric_limits::max()); for (const auto &subset : mesh.subsets()) { m_bounds.bounds.minimum = QSSGUtils::vec3::minimum(m_bounds.bounds.minimum, subset.bounds.min); m_bounds.bounds.maximum = QSSGUtils::vec3::maximum(m_bounds.bounds.maximum, subset.bounds.max); } setBounds(m_bounds.bounds.minimum, m_bounds.bounds.maximum); m_currentlyLoadedSource = m_source; m_currentlyLoadedKey = m_key; update(); emit boundsChanged(); } QUrl LightmapMesh::source() const { return m_source; } void LightmapMesh::setSource(const QUrl &newSource) { if (m_source == newSource) return; m_source = newSource; updateData(); emit sourceChanged(); } QString LightmapMesh::key() const { return m_key; } void LightmapMesh::setKey(const QString &newKey) { if (m_key == newKey) return; m_key = newKey; updateData(); emit keyChanged(); } const QQuick3DBounds3 &LightmapMesh::bounds() const { return m_bounds; }