1
/****************************************************************************
3
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4
** All rights reserved.
5
** Contact: Nokia Corporation (qt-info@nokia.com)
7
** This file is part of the demonstration applications of the Qt Toolkit.
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
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.
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.
28
** If you have questions regarding the use of this file, please contact
29
** Nokia at qt-info@nokia.com.
40
****************************************************************************/
48
#define M_PI 3.14159265358979323846
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 },
63
#define TEXTURE_SIZE 64
64
#define TEXTURE_BLOCK (TEXTURE_SIZE * TEXTURE_SIZE)
66
class Raycasting: public QWidget
69
Raycasting(QWidget *parent = 0)
75
, touchDevice(false) {
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;
85
ticker.start(25, this);
86
setAttribute(Qt::WA_OpaquePaintEvent, true);
87
setMouseTracking(false);
91
int interval = qBound(20, watch.elapsed(), 250);
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);
105
static QTime frameTick;
106
static int totalFrame = 0;
107
if (!(totalFrame & 31)) {
108
int elapsed = frameTick.elapsed();
110
int fps = 32 * 1000 / (1 + elapsed);
111
setWindowTitle(QString("Raycasting (%1 FPS)").arg(fps));
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)
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;
132
// prepare the texture pointer
133
const uchar *src = textureImg.bits();
134
const QRgb *texsrc = reinterpret_cast<const QRgb*>(src);
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;
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;
151
int stepx = (u < 0) ? -1 : 1;
152
int stepy = (v < 0) ? -1 : 1;
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);
160
// the position and texture for the hit
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;
170
// loop until we hit something
171
while (texture <= 0) {
173
// shorter distance to a hit in constant y line
177
texture = world_map[mapy][mapx];
181
qreal ofs = px + u * (mapy - py) / v;
182
texofs = ofs - floor(ofs);
184
qreal ofs = px + u * (mapy + 1 - py) / v;
185
texofs = ofs - floor(ofs);
189
// shorter distance to a hit in constant x line
193
texture = world_map[mapy][mapx];
196
qreal ofs = py + v * (mapx - px) / u;
197
texofs = ofs - floor(ofs);
199
qreal ofs = py + v * (mapx + 1 - px) / u;
200
texofs = ceil(ofs) - ofs;
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);
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;
222
// start from the screen center (vertically)
223
// y1 will go up (decrease), y2 will go down (increase)
226
QRgb *pixel1 = start + y1 * stride + ray;
227
QRgb *pixel2 = pixel1 + stride;
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];
242
for (; pixel1 > start; pixel1 -= stride)
243
*pixel1 = qRgb(0, 0, 0);
244
for (; pixel2 < finish; pixel2 += stride)
245
*pixel2 = qRgb(96, 96, 96);
248
update(QRect(QPoint(0, 0), bufferSize));
253
void resizeEvent(QResizeEvent*) {
254
#if defined(Q_OS_SYMBIAN)
256
if (width() > 480 || height() > 480)
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);
267
trackPad = QRect(width() / 2, 0, width() / 2, height());
268
centerPad = QPoint(width() * 3 / 4, height() / 2);
269
bufferSize = QSize(width() / 2, height());
278
void timerEvent(QTimerEvent*) {
284
void paintEvent(QPaintEvent *event) {
286
p.setCompositionMode(QPainter::CompositionMode_Source);
288
p.drawImage(event->rect(), buffer, event->rect());
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);
297
p.setBrush(Qt::gray);
300
poly << QPoint(-30, 0);
301
poly << QPoint(0, -40);
302
poly << QPoint(30, 0);
304
p.translate(centerPad);
305
for (int i = 0; i < 4; ++i) {
307
p.translate(0, 20 - rad);
309
p.translate(0, rad - 20);
316
void keyPressEvent(QKeyEvent *event) {
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)
324
if (event->key() == Qt::Key_Down)
328
void keyReleaseEvent(QKeyEvent *event) {
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;
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();
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();
354
void mouseReleaseEvent(QMouseEvent*) {
375
int main(int argc, char **argv)
377
QApplication app(argc, argv);
380
w.setWindowTitle("Raycasting");
381
#if defined(Q_OS_SYMBIAN)