~oif-team/ubuntu/natty/qt4-x11/xi2.1

« back to all changes in this revision

Viewing changes to demos/embedded/raycasting/raycasting.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Alessandro Ghersi
  • Date: 2009-11-02 18:30:08 UTC
  • mfrom: (1.2.2 upstream)
  • mto: (15.2.5 experimental)
  • mto: This revision was merged to the branch mainline in revision 88.
  • Revision ID: james.westby@ubuntu.com-20091102183008-b6a4gcs128mvfb3m
Tags: upstream-4.6.0~beta1
ImportĀ upstreamĀ versionĀ 4.6.0~beta1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/****************************************************************************
 
2
**
 
3
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
 
4
** All rights reserved.
 
5
** Contact: Nokia Corporation (qt-info@nokia.com)
 
6
**
 
7
** This file is part of the demonstration applications of the Qt Toolkit.
 
8
**
 
9
** $QT_BEGIN_LICENSE:LGPL$
 
10
** No Commercial Usage
 
11
** This file contains pre-release code and may not be distributed.
 
12
** You may use this file in accordance with the terms and conditions
 
13
** contained in the Technology Preview License Agreement accompanying
 
14
** this package.
 
15
**
 
16
** GNU Lesser General Public License Usage
 
17
** Alternatively, this file may be used under the terms of the GNU Lesser
 
18
** General Public License version 2.1 as published by the Free Software
 
19
** Foundation and appearing in the file LICENSE.LGPL included in the
 
20
** packaging of this file.  Please review the following information to
 
21
** ensure the GNU Lesser General Public License version 2.1 requirements
 
22
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
 
23
**
 
24
** In addition, as a special exception, Nokia gives you certain additional
 
25
** rights.  These rights are described in the Nokia Qt LGPL Exception
 
26
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
 
27
**
 
28
** If you have questions regarding the use of this file, please contact
 
29
** Nokia at qt-info@nokia.com.
 
30
**
 
31
**
 
32
**
 
33
**
 
34
**
 
35
**
 
36
**
 
37
**
 
38
** $QT_END_LICENSE$
 
39
**
 
40
****************************************************************************/
 
41
 
 
42
#include <QtCore>
 
43
#include <QtGui>
 
44
 
 
45
#include <math.h>
 
46
 
 
47
#ifndef M_PI
 
48
#define M_PI 3.14159265358979323846
 
49
#endif
 
50
 
 
51
#define WORLD_SIZE 8
 
52
int world_map[WORLD_SIZE][WORLD_SIZE] = {
 
53
    { 1, 1, 1, 1, 6, 1, 1, 1 },
 
54
    { 1, 0, 0, 1, 0, 0, 0, 7 },
 
55
    { 1, 1, 0, 1, 0, 1, 1, 1 },
 
56
    { 6, 0, 0, 0, 0, 0, 0, 3 },
 
57
    { 1, 8, 8, 0, 8, 0, 8, 1 },
 
58
    { 2, 2, 0, 0, 8, 8, 7, 1 },
 
59
    { 3, 0, 0, 0, 0, 0, 0, 5 },
 
60
    { 2, 2, 2, 2, 7, 4, 4, 4 },
 
61
};
 
62
 
 
63
#define TEXTURE_SIZE 64
 
64
#define TEXTURE_BLOCK (TEXTURE_SIZE * TEXTURE_SIZE)
 
65
 
 
66
class Raycasting: public QWidget
 
67
{
 
68
public:
 
69
    Raycasting(QWidget *parent = 0)
 
70
            : QWidget(parent)
 
71
            , angle(0.5)
 
72
            , playerPos(1.5, 1.5)
 
73
            , angleDelta(0)
 
74
            , moveDelta(0)
 
75
            , touchDevice(false) {
 
76
 
 
77
        // http://www.areyep.com/RIPandMCS-TextureLibrary.html
 
78
        textureImg.load(":/textures.png");
 
79
        textureImg = textureImg.convertToFormat(QImage::Format_ARGB32);
 
80
        Q_ASSERT(textureImg.width() == TEXTURE_SIZE * 2);
 
81
        Q_ASSERT(textureImg.bytesPerLine() == 4 * TEXTURE_SIZE * 2);
 
82
        textureCount = textureImg.height() / TEXTURE_SIZE;
 
83
 
 
84
        watch.start();
 
85
        ticker.start(25, this);
 
86
        setAttribute(Qt::WA_OpaquePaintEvent, true);
 
87
        setMouseTracking(false);
 
88
    }
 
89
 
 
90
    void updatePlayer() {
 
91
        int interval = qBound(20, watch.elapsed(), 250);
 
92
        watch.start();
 
93
        angle += angleDelta * interval / 1000;
 
94
        qreal step = moveDelta * interval / 1000;
 
95
        qreal dx = cos(angle) * step;
 
96
        qreal dy = sin(angle) * step;
 
97
        QPointF pos = playerPos + 3 * QPointF(dx, dy);
 
98
        int xi = static_cast<int>(pos.x());
 
99
        int yi = static_cast<int>(pos.y());
 
100
        if (world_map[yi][xi] == 0)
 
101
            playerPos = playerPos + QPointF(dx, dy);
 
102
    }
 
103
 
 
104
    void showFps() {
 
105
        static QTime frameTick;
 
106
        static int totalFrame = 0;
 
107
        if (!(totalFrame & 31)) {
 
108
            int elapsed = frameTick.elapsed();
 
109
            frameTick.start();
 
110
            int fps = 32 * 1000 / (1 + elapsed);
 
111
            setWindowTitle(QString("Raycasting (%1 FPS)").arg(fps));
 
112
        }
 
113
        totalFrame++;
 
114
    }
 
115
 
 
116
    void render() {
 
117
 
 
118
        // setup the screen surface
 
119
        if (buffer.size() != bufferSize)
 
120
            buffer = QImage(bufferSize, QImage::Format_ARGB32);
 
121
        int bufw = buffer.width();
 
122
        int bufh = buffer.height();
 
123
        if (bufw <= 0 || bufh <= 0)
 
124
            return;
 
125
 
 
126
        // we intentionally cheat here, to avoid detach
 
127
        const uchar *ptr = buffer.bits();
 
128
        QRgb *start = (QRgb*)(ptr);
 
129
        QRgb stride = buffer.bytesPerLine() / 4;
 
130
        QRgb *finish = start + stride * bufh;
 
131
 
 
132
        // prepare the texture pointer
 
133
        const uchar *src = textureImg.bits();
 
134
        const QRgb *texsrc = reinterpret_cast<const QRgb*>(src);
 
135
 
 
136
        // cast all rays here
 
137
        qreal sina = sin(angle);
 
138
        qreal cosa = cos(angle);
 
139
        qreal u = cosa - sina;
 
140
        qreal v = sina + cosa;
 
141
        qreal du = 2 * sina / bufw;
 
142
        qreal dv = -2 * cosa / bufw;
 
143
 
 
144
        for (int ray = 0; ray < bufw; ++ray, u += du, v += dv) {
 
145
            // everytime this ray advances 'u' units in x direction,
 
146
            // it also advanced 'v' units in y direction
 
147
            qreal uu = (u < 0) ? -u : u;
 
148
            qreal vv = (v < 0) ? -v : v;
 
149
            qreal duu = 1 / uu;
 
150
            qreal dvv = 1 / vv;
 
151
            int stepx = (u < 0) ? -1 : 1;
 
152
            int stepy = (v < 0) ? -1 : 1;
 
153
 
 
154
            // the cell in the map that we need to check
 
155
            qreal px = playerPos.x();
 
156
            qreal py = playerPos.y();
 
157
            int mapx = static_cast<int>(px);
 
158
            int mapy = static_cast<int>(py);
 
159
 
 
160
            // the position and texture for the hit
 
161
            int texture = 0;
 
162
            qreal hitdist = 0.1;
 
163
            qreal texofs = 0;
 
164
            bool dark = false;
 
165
 
 
166
            // first hit at constant x and constant y lines
 
167
            qreal distx = (u > 0) ? (mapx + 1 - px) * duu : (px - mapx) * duu;
 
168
            qreal disty = (v > 0) ? (mapy + 1 - py) * dvv : (py - mapy) * dvv;
 
169
 
 
170
            // loop until we hit something
 
171
            while (texture <= 0) {
 
172
                if (distx > disty) {
 
173
                    // shorter distance to a hit in constant y line
 
174
                    hitdist = disty;
 
175
                    disty += dvv;
 
176
                    mapy += stepy;
 
177
                    texture = world_map[mapy][mapx];
 
178
                    if (texture > 0) {
 
179
                        dark = true;
 
180
                        if (stepy > 0) {
 
181
                            qreal ofs = px + u * (mapy - py) / v;
 
182
                            texofs = ofs - floor(ofs);
 
183
                        } else {
 
184
                            qreal ofs = px + u * (mapy + 1 - py) / v;
 
185
                            texofs = ofs - floor(ofs);
 
186
                        }
 
187
                    }
 
188
                } else {
 
189
                    // shorter distance to a hit in constant x line
 
190
                    hitdist = distx;
 
191
                    distx += duu;
 
192
                    mapx += stepx;
 
193
                    texture = world_map[mapy][mapx];
 
194
                    if (texture > 0) {
 
195
                        if (stepx > 0) {
 
196
                            qreal ofs = py + v * (mapx - px) / u;
 
197
                            texofs = ofs - floor(ofs);
 
198
                        } else {
 
199
                            qreal ofs = py + v * (mapx + 1 - px) / u;
 
200
                            texofs = ceil(ofs) - ofs;
 
201
                        }
 
202
                    }
 
203
                }
 
204
            }
 
205
 
 
206
            // get the texture, note that the texture image
 
207
            // has two textures horizontally, "normal" vs "dark"
 
208
            int col = static_cast<int>(texofs * TEXTURE_SIZE);
 
209
            col = qBound(0, col, TEXTURE_SIZE - 1);
 
210
            texture = (texture - 1) % textureCount;
 
211
            const QRgb *tex = texsrc + TEXTURE_BLOCK * texture * 2 +
 
212
                              (TEXTURE_SIZE * 2 * col);
 
213
            if (dark)
 
214
                tex += TEXTURE_SIZE;
 
215
 
 
216
            // start from the texture center (horizontally)
 
217
            int h = static_cast<int>(bufw / hitdist / 2);
 
218
            int dy = (TEXTURE_SIZE << 12) / h;
 
219
            int p1 = ((TEXTURE_SIZE / 2) << 12) - dy;
 
220
            int p2 = p1 + dy;
 
221
 
 
222
            // start from the screen center (vertically)
 
223
            // y1 will go up (decrease), y2 will go down (increase)
 
224
            int y1 = bufh / 2;
 
225
            int y2 = y1 + 1;
 
226
            QRgb *pixel1 = start + y1 * stride + ray;
 
227
            QRgb *pixel2 = pixel1 + stride;
 
228
 
 
229
            // map the texture to the sliver
 
230
            while (y1 >= 0 && y2 < bufh && p1 >= 0) {
 
231
                *pixel1 = tex[p1 >> 12];
 
232
                *pixel2 = tex[p2 >> 12];
 
233
                p1 -= dy;
 
234
                p2 += dy;
 
235
                --y1;
 
236
                ++y2;
 
237
                pixel1 -= stride;
 
238
                pixel2 += stride;
 
239
            }
 
240
 
 
241
            // ceiling and floor
 
242
            for (; pixel1 > start; pixel1 -= stride)
 
243
                *pixel1 = qRgb(0, 0, 0);
 
244
            for (; pixel2 < finish; pixel2 += stride)
 
245
                *pixel2 = qRgb(96, 96, 96);
 
246
        }
 
247
 
 
248
        update(QRect(QPoint(0, 0), bufferSize));
 
249
    }
 
250
 
 
251
protected:
 
252
 
 
253
    void resizeEvent(QResizeEvent*) {
 
254
#if defined(Q_OS_SYMBIAN)
 
255
        // FIXME: use HAL
 
256
        if (width() > 480 || height() > 480)
 
257
            touchDevice = true;
 
258
#else
 
259
        touchDevice = false;
 
260
#endif
 
261
        if (touchDevice) {
 
262
            if (width() < height()) {
 
263
                trackPad = QRect(0, height() / 2, width(), height() / 2);
 
264
                centerPad = QPoint(width() / 2, height() * 3 / 4);
 
265
                bufferSize = QSize(width(), height() / 2);
 
266
            } else {
 
267
                trackPad = QRect(width() / 2, 0, width() / 2, height());
 
268
                centerPad = QPoint(width() * 3 / 4, height() / 2);
 
269
                bufferSize = QSize(width() / 2, height());
 
270
            }
 
271
        } else {
 
272
            trackPad = QRect();
 
273
            bufferSize = size();
 
274
        }
 
275
        update();
 
276
   }
 
277
 
 
278
    void timerEvent(QTimerEvent*) {
 
279
        updatePlayer();
 
280
        render();
 
281
        showFps();
 
282
    }
 
283
 
 
284
    void paintEvent(QPaintEvent *event) {
 
285
        QPainter p(this);
 
286
        p.setCompositionMode(QPainter::CompositionMode_Source);
 
287
 
 
288
        p.drawImage(event->rect(), buffer, event->rect());
 
289
 
 
290
        if (touchDevice && event->rect().intersects(trackPad)) {
 
291
            p.fillRect(trackPad, Qt::white);
 
292
            p.setPen(QPen(QColor(224, 224, 224), 6));
 
293
            int rad = qMin(trackPad.width(), trackPad.height()) * 0.3;
 
294
            p.drawEllipse(centerPad, rad, rad);
 
295
 
 
296
            p.setPen(Qt::NoPen);
 
297
            p.setBrush(Qt::gray);
 
298
 
 
299
            QPolygon poly;
 
300
            poly << QPoint(-30, 0);
 
301
            poly << QPoint(0, -40);
 
302
            poly << QPoint(30, 0);
 
303
 
 
304
            p.translate(centerPad);
 
305
            for (int i = 0; i < 4; ++i) {
 
306
                p.rotate(90);
 
307
                p.translate(0, 20 - rad);
 
308
                p.drawPolygon(poly);
 
309
                p.translate(0, rad - 20);
 
310
            }
 
311
        }
 
312
 
 
313
        p.end();
 
314
    }
 
315
 
 
316
    void keyPressEvent(QKeyEvent *event) {
 
317
        event->accept();
 
318
        if (event->key() == Qt::Key_Left)
 
319
            angleDelta = 1.3 * M_PI;
 
320
        if (event->key() == Qt::Key_Right)
 
321
            angleDelta = -1.3 * M_PI;
 
322
        if (event->key() == Qt::Key_Up)
 
323
            moveDelta = 2.5;
 
324
        if (event->key() == Qt::Key_Down)
 
325
            moveDelta = -2.5;
 
326
    }
 
327
 
 
328
    void keyReleaseEvent(QKeyEvent *event) {
 
329
        event->accept();
 
330
        if (event->key() == Qt::Key_Left)
 
331
            angleDelta = (angleDelta > 0) ? 0 : angleDelta;
 
332
        if (event->key() == Qt::Key_Right)
 
333
            angleDelta = (angleDelta < 0) ? 0 : angleDelta;
 
334
        if (event->key() == Qt::Key_Up)
 
335
            moveDelta = (moveDelta > 0) ? 0 : moveDelta;
 
336
        if (event->key() == Qt::Key_Down)
 
337
            moveDelta = (moveDelta < 0) ? 0 : moveDelta;
 
338
    }
 
339
 
 
340
    void mousePressEvent(QMouseEvent *event) {
 
341
        qreal dx = centerPad.x() - event->pos().x();
 
342
        qreal dy = centerPad.y() - event->pos().y();
 
343
        angleDelta = dx * 2 * M_PI / width();
 
344
        moveDelta = dy * 10 / height();
 
345
    }
 
346
 
 
347
    void mouseMoveEvent(QMouseEvent *event) {
 
348
        qreal dx = centerPad.x() - event->pos().x();
 
349
        qreal dy = centerPad.y() - event->pos().y();
 
350
        angleDelta = dx * 2 * M_PI / width();
 
351
        moveDelta = dy * 10 / height();
 
352
    }
 
353
 
 
354
    void mouseReleaseEvent(QMouseEvent*) {
 
355
        angleDelta = 0;
 
356
        moveDelta = 0;
 
357
    }
 
358
 
 
359
private:
 
360
    QTime watch;
 
361
    QBasicTimer ticker;
 
362
    QImage buffer;
 
363
    qreal angle;
 
364
    QPointF playerPos;
 
365
    qreal angleDelta;
 
366
    qreal moveDelta;
 
367
    QImage textureImg;
 
368
    int textureCount;
 
369
    bool touchDevice;
 
370
    QRect trackPad;
 
371
    QPoint centerPad;
 
372
    QSize bufferSize;
 
373
};
 
374
 
 
375
int main(int argc, char **argv)
 
376
{
 
377
    QApplication app(argc, argv);
 
378
 
 
379
    Raycasting w;
 
380
    w.setWindowTitle("Raycasting");
 
381
#if defined(Q_OS_SYMBIAN)
 
382
    w.showMaximized();
 
383
#else
 
384
    w.resize(640, 480);
 
385
    w.show();
 
386
#endif
 
387
 
 
388
    return app.exec();
 
389
}