#include "model.h" #include #include #include #include #include "GL/gl.h" static Point3d readPoint(QTextStream &ts) { Point3d p; ts >> p.x >> p.y >> p.z; return p; } struct Edge { int pointA; int pointB; }; uint qHash(const Edge &edge) { return qHash(edge.pointA) ^ qHash(edge.pointB); } bool operator==(const Edge &a, const Edge &b) { return (a.pointA == b.pointA && a.pointB == b.pointB) || (a.pointA == b.pointB && a.pointB == b.pointA); } Model::Model(const QString &filename) { QFile file(filename); if (!file.open(QIODevice::ReadOnly)) return; QSet edges; Point3d min( 1e9, 1e9, 1e9); Point3d max(-1e9,-1e9,-1e9); QVector pointData; QVector normalData; QTextStream in(&file); while (!in.atEnd()) { QString input = in.readLine(); if (input.isEmpty() || input[0] == '#') continue; QTextStream ts(&input); QString id; ts >> id; if (id == "v") { Point3d p = readPoint(ts); p.y = -p.y; pointData << p; normalData << Point3d(); min.x = qMin(min.x, p.x); min.y = qMin(min.y, p.y); min.z = qMin(min.z, p.z); max.x = qMax(max.x, p.x); max.y = qMax(max.y, p.y); max.z = qMax(max.z, p.z); } else if (id == "f" || id == "fo") { QVarLengthArray p; while (!ts.atEnd()) { QString vertex; ts >> vertex; int index; QTextStream vertexStream(&vertex); vertexStream >> index; if (vertexStream.status() == QTextStream::Ok) p.append(index - 1); } for (int i = 0; i < p.size(); ++i) { Edge edge = { p[i], p[(i + 1) % p.size()] }; edges << edge; } for (int i = 0; i < 3; ++i) m_pointIndices << p[i]; if (p.size() == 4) for (int i = 0; i < 3; ++i) m_pointIndices << p[(i + 2) % 4]; } } #if 0 for (int i = 0; i < m_pointIndices.size(); ++i) { Point3d p1 = pointData.at(m_pointIndices.at(i)); for (int j = 0; j < m_pointIndices.at(i); ++j) { Point3d p2 = pointData.at(j); if (p1.x == p2.x && p1.y == p2.y && p1.z == p2.z) { m_pointIndices[i] = j; break; } } } #endif Point3d bounds = max - min; qreal scale = 1 / qMax(bounds.x, qMax(bounds.y, bounds.z)); for (int i = 0; i < pointData.size(); ++i) { Point3d &p = pointData[i]; p.x -= min.x + bounds.x * 0.5; p.y -= min.y + bounds.y * 0.5; p.z -= min.z + bounds.z * 0.5; p.x *= scale; p.y *= scale; p.z *= scale; } QVector counts(normalData.size()); for (int i = 0; i < counts.size(); ++i) counts[i] = 0; for (int i = 0; i < m_pointIndices.size(); i += 3) { const Point3d a = pointData.at(m_pointIndices.at(i)); const Point3d b = pointData.at(m_pointIndices.at(i+1)); const Point3d c = pointData.at(m_pointIndices.at(i+2)); Point3d normal = cross(c - a, b - a).normalize(); for (int j = 0; j < 3; ++j) { Point3d old = normalData.at(m_pointIndices.at(i + j)); normalData[m_pointIndices.at(i + j)] = old + normal; ++counts[m_pointIndices.at(i + j)]; } } for (int i = 0; i < normalData.size(); ++i) { float r = 1. / counts.at(i); normalData[i] = normalData.at(i) * r;//.normalize(); } #if 0 Point3d normal; for (int i = 0; i < m_pointIndices.size(); ++i) { #if 0 if ((i % 3) == 0) { Point3d a = pointData.at(m_pointIndices.at(i)); Point3d b = pointData.at(m_pointIndices.at(i+1)); Point3d c = pointData.at(m_pointIndices.at(i+2)); normal = cross(c - a, b - a).normalize(); //float len = normal.x * normal.x +normal.y * normal.y +normal.z * normal.z; } #endif m_points << pointData.at(m_pointIndices.at(i)); m_normals << normalData.at(m_pointIndices.at(i)).normalize(); #if 0 Point3d p = m_points.last(); Point3d n = m_normals.last(); printf("Index: %d, point: %f %f %f, normal: %f %f %f\n", m_pointIndices.at(i), p.x, p.y, p.z, n.x, n.y, n.z); #endif } #endif m_points = pointData; m_normals = normalData; foreach(const Edge &edge, QVector::fromList(edges.toList())) m_edges << pointData.at(edge.pointA) << pointData.at(edge.pointB); } void Model::render(bool wireframe, bool normals) const { glEnableClientState(GL_VERTEX_ARRAY); if (wireframe) { glColor3f(0, 0, 0); glVertexPointer(3, GL_FLOAT, 0, m_edges.data()); glDrawArrays(GL_LINES, 0, m_edges.size()); } else { glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glEnable(GL_COLOR_MATERIAL); glShadeModel(GL_SMOOTH); glEnable(GL_DEPTH_TEST); glEnableClientState(GL_NORMAL_ARRAY); glVertexPointer(3, GL_FLOAT, 0, (float *)m_points.data()); glNormalPointer(GL_FLOAT, 0, (float *)m_normals.data()); glDrawElements(GL_TRIANGLES, m_pointIndices.size(), GL_UNSIGNED_INT, m_pointIndices.data()); glDisableClientState(GL_NORMAL_ARRAY); glDisable(GL_DEPTH_TEST); glDisable(GL_COLOR_MATERIAL); glDisable(GL_LIGHT0); glDisable(GL_LIGHTING); } glDisableClientState(GL_VERTEX_ARRAY); if (normals) { glColor3f(1, 0, 0); glBegin(GL_LINES); for (int i = 0; i < m_normals.size(); ++i) { Point3d a = m_points.at(i); Point3d b = m_points.at(i) + m_normals.at(i) * 0.02; glVertex3f(a.x, a.y, a.z); glVertex3f(b.x, b.y, b.z); } glEnd(); } }