1
/****************************************************************************
2
3
This file is part of the wolfenqt project on http://qt.gitorious.org.
4
5
Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).*
6
All rights reserved.
7
8
Contact:  Nokia Corporation (qt-info@nokia.com)**
9
10
You may use this file under the terms of the BSD license as follows:
11
12
"Redistribution and use in source and binary forms, with or without
13
modification, are permitted provided that the following conditions are met:
14
* Redistributions of source code must retain the above copyright notice,
15
* this list of conditions and the following disclaimer.
16
* Redistributions in binary form must reproduce the above copyright notice,
17
* this list of conditions and the following disclaimer in the documentation
18
* and/or other materials provided with the distribution.
19
* Neither the name of Nokia Corporation and its Subsidiary(-ies) nor the
20
* names of its contributors may be used to endorse or promote products
21
* derived from this software without specific prior written permission.
22
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32
POSSIBILITY OF SUCH DAMAGE."
33
34
****************************************************************************/
35
#include "model.h"
36
37
#include <QFile>
38
#include <QFileInfo>
39
#include <QTextStream>
40
#include <QVarLengthArray>
41
42
#ifndef QT_NO_OPENGL
43
#if !defined QT_OPENGL_ES_2 && !defined Q_WS_MAC
44
#include <GL/glew.h>
45
#endif
46
#include <QtOpenGL>
47
#endif
48
49
Model::Model(const QString &filePath)
50
    : m_fileName(QFileInfo(filePath).fileName())
51
{
52
    QFile file(filePath);
53
    if (!file.open(QIODevice::ReadOnly))
54
        return;
55
56
    QVector3D boundsMin( 1e9, 1e9, 1e9);
57
    QVector3D boundsMax(-1e9,-1e9,-1e9);
58
59
    QTextStream in(&file);
60
    while (!in.atEnd()) {
61
        QString input = in.readLine();
62
        if (input.isEmpty() || input[0] == '#')
63
            continue;
64
65
        QTextStream ts(&input);
66
        QString id;
67
        ts >> id;
68
        if (id == "v") {
69
            QVector3D p;
70
            for (int i = 0; i < 3; ++i) {
71
                ts >> ((float *)&p)[i];
72
                ((float *)&boundsMin)[i] = qMin(((float *)&boundsMin)[i], ((float *)&p)[i]);
73
                ((float *)&boundsMax)[i] = qMax(((float *)&boundsMax)[i], ((float *)&p)[i]);
74
            }
75
            m_points << p;
76
        } else if (id == "f" || id == "fo") {
77
            QVarLengthArray<int, 4> p;
78
79
            while (!ts.atEnd()) {
80
                QString vertex;
81
                ts >> vertex;
82
                const int vertexIndex = vertex.split('/').value(0).toInt();
83
                if (vertexIndex)
84
                    p.append(vertexIndex > 0 ? vertexIndex - 1 : m_points.size() + vertexIndex);
85
            }
86
87
            for (int i = 0; i < p.size(); ++i) {
88
                const int edgeA = p[i];
89
                const int edgeB = p[(i + 1) % p.size()];
90
91
                if (edgeA < edgeB)
92
                    m_edgeIndices << edgeA << edgeB;
93
            }
94
95
            for (int i = 0; i < 3; ++i)
96
                m_pointIndices << p[i];
97
98
            if (p.size() == 4)
99
                for (int i = 0; i < 3; ++i)
100
                    m_pointIndices << p[(i + 2) % 4];
101
        }
102
    }
103
104
    const QVector3D bounds = boundsMax - boundsMin;
105
    const qreal scale = 1 / qMax(bounds.x() / 1, qMax(bounds.y(), bounds.z() / 1));
106
    for (int i = 0; i < m_points.size(); ++i)
107
        m_points[i] = (m_points[i] - (boundsMin + bounds * 0.5)) * scale;
108
109
    m_size = bounds * scale;
110
111
    m_normals.resize(m_points.size());
112
    for (int i = 0; i < m_pointIndices.size(); i += 3) {
113
        const QVector3D a = m_points.at(m_pointIndices.at(i));
114
        const QVector3D b = m_points.at(m_pointIndices.at(i+1));
115
        const QVector3D c = m_points.at(m_pointIndices.at(i+2));
116
117
        const QVector3D normal = QVector3D::crossProduct(b - a, c - a).normalized();
118
119
        for (int j = 0; j < 3; ++j)
120
            m_normals[m_pointIndices.at(i + j)] += normal;
121
    }
122
123
    for (int i = 0; i < m_normals.size(); ++i)
124
        m_normals[i] = m_normals[i].normalized();
125
}
126
127
QVector3D Model::size() const
128
{
129
    return m_size;
130
}
131
132
void Model::render(bool wireframe, bool normals) const
133
{
134
#ifdef QT_NO_OPENGL
135
    Q_UNUSED(wireframe);
136
    Q_UNUSED(normals);
137
#else
138
    glEnable(GL_DEPTH_TEST);
139
    glDepthFunc(GL_GEQUAL);
140
    glDepthMask(true);
141
142
#ifdef QT_OPENGL_ES_2
143
    GLenum elementType = GL_UNSIGNED_SHORT;
144
#else
145
    GLenum elementType = GL_UNSIGNED_INT;
146
#endif
147
148
    glEnableVertexAttribArray(0);
149
    glEnableVertexAttribArray(1);
150
151
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (float *)m_points.data());
152
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (float *)m_normals.data());
153
154
    if (wireframe)
155
        glDrawElements(GL_LINES, m_edgeIndices.size(), elementType, m_edgeIndices.data());
156
    else
157
        glDrawElements(GL_TRIANGLES, m_pointIndices.size(), elementType, m_pointIndices.data());
158
159
    if (normals) {
160
        QVector<QVector3D> points;
161
        QVector<QVector3D> normals;
162
        for (int i = 0; i < m_normals.size(); ++i) {
163
            points << m_points.at(i) << (m_points.at(i) + m_normals.at(i) * 0.02f);
164
            normals << m_normals.at(i) << m_normals.at(i);
165
        }
166
167
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (float *)points.data());
168
        glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (float *)normals.data());
169
        glDrawArrays(GL_LINES, 0, points.size());
170
    }
171
172
    glDisableVertexAttribArray(1);
173
    glDisableVertexAttribArray(0);
174
    glDisable(GL_DEPTH_TEST);
175
#endif
176
}
177
178
void Model::render(QPainter *painter, const QMatrix4x4 &matrix, bool normals) const
179
{
180
    m_mapped.resize(m_points.size());
181
    for (int i = 0; i < m_points.size(); ++i)
182
        m_mapped[i] = matrix.map(m_points.at(i));
183
184
    m_lines.clear();
185
    for (int i = 0; i < m_edgeIndices.size(); i += 2) {
186
        const QVector3D a = m_mapped.at(m_edgeIndices.at(i));
187
        const QVector3D b = m_mapped.at(m_edgeIndices.at(i+1));
188
189
        if (a.z() > 0 && b.z() > 0)
190
            m_lines << QLineF(a.toPointF(), b.toPointF());
191
    }
192
193
    if (normals) {
194
        for (int i = 0; i < m_normals.size(); ++i) {
195
            const QVector3D a = m_mapped.at(i);
196
            const QVector3D b = matrix.map(m_points.at(i) + m_normals.at(i) * 0.02f);
197
198
            if (a.z() > 0 && b.z() > 0)
199
                m_lines << QLineF(a.toPointF(), b.toPointF());
200
        }
201
    }
202
203
    painter->setRenderHint(QPainter::Antialiasing, false);
204
    painter->drawLines(m_lines);
205
}