bool ObjLoader::load(::QIODevice *ioDev, const QString &subMesh) { Q_CHECK_PTR(ioDev); if (!ioDev->isOpen()) { qCWarning(Render::Io) << "iodevice" << ioDev << "not open for reading"; return false; } int faceCount = 0; // Parse faces taking into account each vertex in a face can index different indices // for the positions, normals and texture coords; // Generate unique vertices (in OpenGL parlance) and output to m_points, m_texCoords, // m_normals and calculate mapping from faces to unique indices QVector<QVector3D> positions; QVector<QVector3D> normals; QVector<QVector2D> texCoords; QHash<FaceIndices, unsigned int> faceIndexMap; QVector<FaceIndices> faceIndexVector; bool skipping = false; int positionsOffset = 0; int normalsOffset = 0; int texCoordsOffset = 0; QRegExp subMeshMatch(subMesh); if (!subMeshMatch.isValid()) subMeshMatch.setPattern(QString(QStringLiteral("^(%1)$")).arg(subMesh)); Q_ASSERT(subMeshMatch.isValid()); QTextStream stream(ioDev); while (!stream.atEnd()) { QString line = stream.readLine(); line = line.simplified(); if (line.length() > 0 && line.at(0) != QChar::fromLatin1('#')) { const QVector<QStringRef> tokens = line.splitRef(QChar::fromLatin1(' ')); if (tokens.first() == QStringLiteral("v")) { if (tokens.size() < 4) { qCWarning(Render::Io) << "Unsupported number of components in vertex"; } else { if (!skipping) { float x = tokens.at(1).toFloat(); float y = tokens.at(2).toFloat(); float z = tokens.at(3).toFloat(); positions.append(QVector3D( x, y, z )); } else { positionsOffset++; } } } else if (tokens.first() == QStringLiteral("vt") && m_loadTextureCoords) { if (tokens.size() < 3) { qCWarning(Render::Io) << "Unsupported number of components in texture coordinate"; } else { if (!skipping) { // Process texture coordinate float s = tokens.at(1).toFloat(); float t = tokens.at(2).toFloat(); //FlipUVs t = 1.0f - t; texCoords.append(QVector2D( s, t )); } else { texCoordsOffset++; } } } else if (tokens.first() == QStringLiteral("vn")) { if (tokens.size() < 4) { qCWarning(Render::Io) << "Unsupported number of components in vertex normal"; } else { if (!skipping) { float x = tokens.at(1).toFloat(); float y = tokens.at(2).toFloat(); float z = tokens.at(3).toFloat(); normals.append(QVector3D( x, y, z )); } else { normalsOffset++; } } } else if (!skipping && tokens.first() == QStringLiteral("f")) { // Process face ++faceCount; int faceVertices = tokens.size() - 1; QVector<FaceIndices> face; face.reserve(faceVertices); for (int i = 0; i < faceVertices; i++) { FaceIndices faceIndices; const QVector<QStringRef> indices = tokens.at(i + 1).split(QChar::fromLatin1('/')); switch (indices.size()) { case 3: faceIndices.normalIndex = indices.at(2).toInt() - 1 - normalsOffset; // fall through case 2: faceIndices.texCoordIndex = indices.at(1).toInt() - 1 - texCoordsOffset; // fall through case 1: faceIndices.positionIndex = indices.at(0).toInt() - 1 - positionsOffset; break; default: qCWarning(Render::Io) << "Unsupported number of indices in face element"; } face.append(faceIndices); } // If number of edges in face is greater than 3, // decompose into triangles as a triangle fan. FaceIndices v0 = face[0]; FaceIndices v1 = face[1]; FaceIndices v2 = face[2]; // First face addFaceVertex(v0, faceIndexVector, faceIndexMap); addFaceVertex(v1, faceIndexVector, faceIndexMap); addFaceVertex(v2, faceIndexVector, faceIndexMap); for ( int i = 3; i < face.size(); ++i ) { v1 = v2; v2 = face[i]; addFaceVertex(v0, faceIndexVector, faceIndexMap); addFaceVertex(v1, faceIndexVector, faceIndexMap); addFaceVertex(v2, faceIndexVector, faceIndexMap); } // end of face } else if (tokens.first() == QStringLiteral("o")) { if (tokens.size() < 2) { qCWarning(Render::Io) << "Missing submesh name"; } else { if (!subMesh.isEmpty() ) { QString objName = tokens.at(1).toString(); skipping = subMeshMatch.indexIn(objName) < 0; } } } } // end of input line } // while (!stream.atEnd()) updateIndices(positions, normals, texCoords, faceIndexMap, faceIndexVector); if (m_normals.isEmpty()) generateAveragedNormals(m_points, m_normals, m_indices); if (m_generateTangents && !m_texCoords.isEmpty()) generateTangents(m_points, m_normals, m_indices, m_texCoords, m_tangents); if (m_centerMesh) center(m_points); qCDebug(Render::Io) << "Loaded mesh:"; qCDebug(Render::Io) << " " << m_points.size() << "points"; qCDebug(Render::Io) << " " << faceCount << "faces"; qCDebug(Render::Io) << " " << m_indices.size() / 3 << "triangles."; qCDebug(Render::Io) << " " << m_normals.size() << "normals"; qCDebug(Render::Io) << " " << m_tangents.size() << "tangents "; qCDebug(Render::Io) << " " << m_texCoords.size() << "texture coordinates."; return true; }
Mesh::Mesh(Object *parent, const char *fileName, Game::Surface *texture, int _flags): Object(parent), vertexCount(0), faceCount(0), currentFace(0), currentVertex(0), vertex(0), transformedVertex(0), face(0), flags(_flags) { transformation.makeIdentity(); TagFile file(fileName); int i; beginMesh(); while(1) switch(file.readTag()) { case 0: // list of vertices { vertexCount = file.getDataSize() / sizeof(SerializedVertex); SerializedVertex *v = new SerializedVertex[vertexCount]; vertex = new Vertex[vertexCount]; transformedVertex = new Vertex[vertexCount]; file.readData((unsigned char*)v, sizeof(SerializedVertex)*vertexCount); for(i=0; i<vertexCount; i++) { setTexCoord(v[i].u, v[i].v); // addVertex(Vector(v[i].x >> 1, v[i].y >> 1, v[i].z >> 1)); addVertex(Vector(v[i].x, v[i].y, v[i].z)); } delete[] v; } break; case 1: // list of faces { faceCount = file.getDataSize() / sizeof(SerializedTriangle); SerializedTriangle *t = new SerializedTriangle[faceCount]; face = new Face[faceCount]; file.readData((unsigned char*)t, sizeof(SerializedTriangle)*faceCount); for(i=0; i<faceCount; i++) { beginFace(3); setTexture(texture); addFaceVertex(t[i].a); addFaceVertex(t[i].b); addFaceVertex(t[i].c); endFace(); } delete[] t; } break; default: endMesh(); return; } }