summaryrefslogtreecommitdiffstats
path: root/examples/embedded/raycasting
diff options
context:
space:
mode:
authorJørgen Lind <jorgen.lind@nokia.com>2011-06-27 12:27:54 +0200
committerJørgen Lind <jorgen.lind@nokia.com>2011-06-27 12:27:54 +0200
commit18a3f6169bb8a763366003d92b8013950578c79f (patch)
treee39fa00a9511410fea9be35a6130007e43719ad8 /examples/embedded/raycasting
parent371d398f70e10408e0eaba7b5f05e638a0048599 (diff)
parenta06c8405d053b16327d65415f3335eceb37abd3b (diff)
Merge remote-tracking branch 'base/master' into refactor
Conflicts: demos/demos.pro demos/embedded/digiflip/digiflip.pro examples/examples.pro examples/graphicsview/embeddeddialogs/embeddeddialogs.pro src/gui/kernel/qplatformglcontext_qpa.cpp src/plugins/platforms/wayland/gl_integration/xcomposite_egl/qwaylandxcompositeeglcontext.cpp src/plugins/platforms/wayland/gl_integration/xcomposite_glx/qwaylandxcompositeglxcontext.cpp src/plugins/platforms/wayland/qwaylanddisplay.cpp src/plugins/platforms/wayland/qwaylandwindow.cpp Change-Id: I2a4ec9e2ca9c9aa9d57b55f98985e810b77bb745
Diffstat (limited to 'examples/embedded/raycasting')
-rw-r--r--examples/embedded/raycasting/raycasting.cpp391
-rw-r--r--examples/embedded/raycasting/raycasting.pro14
-rw-r--r--examples/embedded/raycasting/raycasting.qrc5
-rw-r--r--examples/embedded/raycasting/textures.pngbin0 -> 17669 bytes
4 files changed, 410 insertions, 0 deletions
diff --git a/examples/embedded/raycasting/raycasting.cpp b/examples/embedded/raycasting/raycasting.cpp
new file mode 100644
index 0000000000..0a50fb0419
--- /dev/null
+++ b/examples/embedded/raycasting/raycasting.cpp
@@ -0,0 +1,391 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore>
+#include <QtWidgets>
+
+#include <math.h>
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+#define WORLD_SIZE 8
+int world_map[WORLD_SIZE][WORLD_SIZE] = {
+ { 1, 1, 1, 1, 6, 1, 1, 1 },
+ { 1, 0, 0, 1, 0, 0, 0, 7 },
+ { 1, 1, 0, 1, 0, 1, 1, 1 },
+ { 6, 0, 0, 0, 0, 0, 0, 3 },
+ { 1, 8, 8, 0, 8, 0, 8, 1 },
+ { 2, 2, 0, 0, 8, 8, 7, 1 },
+ { 3, 0, 0, 0, 0, 0, 0, 5 },
+ { 2, 2, 2, 2, 7, 4, 4, 4 },
+};
+
+#define TEXTURE_SIZE 64
+#define TEXTURE_BLOCK (TEXTURE_SIZE * TEXTURE_SIZE)
+
+class Raycasting: public QWidget
+{
+public:
+ Raycasting(QWidget *parent = 0)
+ : QWidget(parent)
+ , angle(0.5)
+ , playerPos(1.5, 1.5)
+ , angleDelta(0)
+ , moveDelta(0)
+ , touchDevice(false) {
+
+ // http://www.areyep.com/RIPandMCS-TextureLibrary.html
+ textureImg.load(":/textures.png");
+ textureImg = textureImg.convertToFormat(QImage::Format_ARGB32);
+ Q_ASSERT(textureImg.width() == TEXTURE_SIZE * 2);
+ Q_ASSERT(textureImg.bytesPerLine() == 4 * TEXTURE_SIZE * 2);
+ textureCount = textureImg.height() / TEXTURE_SIZE;
+
+ watch.start();
+ ticker.start(25, this);
+ setAttribute(Qt::WA_OpaquePaintEvent, true);
+ setMouseTracking(false);
+ }
+
+ void updatePlayer() {
+ int interval = qBound(20, watch.elapsed(), 250);
+ watch.start();
+ angle += angleDelta * interval / 1000;
+ qreal step = moveDelta * interval / 1000;
+ qreal dx = cos(angle) * step;
+ qreal dy = sin(angle) * step;
+ QPointF pos = playerPos + 3 * QPointF(dx, dy);
+ int xi = static_cast<int>(pos.x());
+ int yi = static_cast<int>(pos.y());
+ if (world_map[yi][xi] == 0)
+ playerPos = playerPos + QPointF(dx, dy);
+ }
+
+ void showFps() {
+ static QTime frameTick;
+ static int totalFrame = 0;
+ if (!(totalFrame & 31)) {
+ int elapsed = frameTick.elapsed();
+ frameTick.start();
+ int fps = 32 * 1000 / (1 + elapsed);
+ setWindowTitle(QString("Raycasting (%1 FPS)").arg(fps));
+ }
+ totalFrame++;
+ }
+
+ void render() {
+
+ // setup the screen surface
+ if (buffer.size() != bufferSize)
+ buffer = QImage(bufferSize, QImage::Format_ARGB32);
+ int bufw = buffer.width();
+ int bufh = buffer.height();
+ if (bufw <= 0 || bufh <= 0)
+ return;
+
+ // we intentionally cheat here, to avoid detach
+ const uchar *ptr = buffer.bits();
+ QRgb *start = (QRgb*)(ptr);
+ QRgb stride = buffer.bytesPerLine() / 4;
+ QRgb *finish = start + stride * bufh;
+
+ // prepare the texture pointer
+ const uchar *src = textureImg.bits();
+ const QRgb *texsrc = reinterpret_cast<const QRgb*>(src);
+
+ // cast all rays here
+ qreal sina = sin(angle);
+ qreal cosa = cos(angle);
+ qreal u = cosa - sina;
+ qreal v = sina + cosa;
+ qreal du = 2 * sina / bufw;
+ qreal dv = -2 * cosa / bufw;
+
+ for (int ray = 0; ray < bufw; ++ray, u += du, v += dv) {
+ // every time this ray advances 'u' units in x direction,
+ // it also advanced 'v' units in y direction
+ qreal uu = (u < 0) ? -u : u;
+ qreal vv = (v < 0) ? -v : v;
+ qreal duu = 1 / uu;
+ qreal dvv = 1 / vv;
+ int stepx = (u < 0) ? -1 : 1;
+ int stepy = (v < 0) ? -1 : 1;
+
+ // the cell in the map that we need to check
+ qreal px = playerPos.x();
+ qreal py = playerPos.y();
+ int mapx = static_cast<int>(px);
+ int mapy = static_cast<int>(py);
+
+ // the position and texture for the hit
+ int texture = 0;
+ qreal hitdist = 0.1;
+ qreal texofs = 0;
+ bool dark = false;
+
+ // first hit at constant x and constant y lines
+ qreal distx = (u > 0) ? (mapx + 1 - px) * duu : (px - mapx) * duu;
+ qreal disty = (v > 0) ? (mapy + 1 - py) * dvv : (py - mapy) * dvv;
+
+ // loop until we hit something
+ while (texture <= 0) {
+ if (distx > disty) {
+ // shorter distance to a hit in constant y line
+ hitdist = disty;
+ disty += dvv;
+ mapy += stepy;
+ texture = world_map[mapy][mapx];
+ if (texture > 0) {
+ dark = true;
+ if (stepy > 0) {
+ qreal ofs = px + u * (mapy - py) / v;
+ texofs = ofs - floor(ofs);
+ } else {
+ qreal ofs = px + u * (mapy + 1 - py) / v;
+ texofs = ofs - floor(ofs);
+ }
+ }
+ } else {
+ // shorter distance to a hit in constant x line
+ hitdist = distx;
+ distx += duu;
+ mapx += stepx;
+ texture = world_map[mapy][mapx];
+ if (texture > 0) {
+ if (stepx > 0) {
+ qreal ofs = py + v * (mapx - px) / u;
+ texofs = ofs - floor(ofs);
+ } else {
+ qreal ofs = py + v * (mapx + 1 - px) / u;
+ texofs = ceil(ofs) - ofs;
+ }
+ }
+ }
+ }
+
+ // get the texture, note that the texture image
+ // has two textures horizontally, "normal" vs "dark"
+ int col = static_cast<int>(texofs * TEXTURE_SIZE);
+ col = qBound(0, col, TEXTURE_SIZE - 1);
+ texture = (texture - 1) % textureCount;
+ const QRgb *tex = texsrc + TEXTURE_BLOCK * texture * 2 +
+ (TEXTURE_SIZE * 2 * col);
+ if (dark)
+ tex += TEXTURE_SIZE;
+
+ // start from the texture center (horizontally)
+ int h = static_cast<int>(bufw / hitdist / 2);
+ int dy = (TEXTURE_SIZE << 12) / h;
+ int p1 = ((TEXTURE_SIZE / 2) << 12) - dy;
+ int p2 = p1 + dy;
+
+ // start from the screen center (vertically)
+ // y1 will go up (decrease), y2 will go down (increase)
+ int y1 = bufh / 2;
+ int y2 = y1 + 1;
+ QRgb *pixel1 = start + y1 * stride + ray;
+ QRgb *pixel2 = pixel1 + stride;
+
+ // map the texture to the sliver
+ while (y1 >= 0 && y2 < bufh && p1 >= 0) {
+ *pixel1 = tex[p1 >> 12];
+ *pixel2 = tex[p2 >> 12];
+ p1 -= dy;
+ p2 += dy;
+ --y1;
+ ++y2;
+ pixel1 -= stride;
+ pixel2 += stride;
+ }
+
+ // ceiling and floor
+ for (; pixel1 > start; pixel1 -= stride)
+ *pixel1 = qRgb(0, 0, 0);
+ for (; pixel2 < finish; pixel2 += stride)
+ *pixel2 = qRgb(96, 96, 96);
+ }
+
+ update(QRect(QPoint(0, 0), bufferSize));
+ }
+
+protected:
+
+ void resizeEvent(QResizeEvent*) {
+#if defined(Q_OS_WINCE_WM)
+ touchDevice = true;
+#elif defined(Q_OS_SYMBIAN)
+ // FIXME: use HAL
+ if (width() > 480 || height() > 480)
+ touchDevice = true;
+#else
+ touchDevice = false;
+#endif
+ if (touchDevice) {
+ if (width() < height()) {
+ trackPad = QRect(0, height() / 2, width(), height() / 2);
+ centerPad = QPoint(width() / 2, height() * 3 / 4);
+ bufferSize = QSize(width(), height() / 2);
+ } else {
+ trackPad = QRect(width() / 2, 0, width() / 2, height());
+ centerPad = QPoint(width() * 3 / 4, height() / 2);
+ bufferSize = QSize(width() / 2, height());
+ }
+ } else {
+ trackPad = QRect();
+ bufferSize = size();
+ }
+ update();
+ }
+
+ void timerEvent(QTimerEvent*) {
+ updatePlayer();
+ render();
+ showFps();
+ }
+
+ void paintEvent(QPaintEvent *event) {
+ QPainter p(this);
+ p.setCompositionMode(QPainter::CompositionMode_Source);
+
+ p.drawImage(event->rect(), buffer, event->rect());
+
+ if (touchDevice && event->rect().intersects(trackPad)) {
+ p.fillRect(trackPad, Qt::white);
+ p.setPen(QPen(QColor(224, 224, 224), 6));
+ int rad = qMin(trackPad.width(), trackPad.height()) * 0.3;
+ p.drawEllipse(centerPad, rad, rad);
+
+ p.setPen(Qt::NoPen);
+ p.setBrush(Qt::gray);
+
+ QPolygon poly;
+ poly << QPoint(-30, 0);
+ poly << QPoint(0, -40);
+ poly << QPoint(30, 0);
+
+ p.translate(centerPad);
+ for (int i = 0; i < 4; ++i) {
+ p.rotate(90);
+ p.translate(0, 20 - rad);
+ p.drawPolygon(poly);
+ p.translate(0, rad - 20);
+ }
+ }
+
+ p.end();
+ }
+
+ void keyPressEvent(QKeyEvent *event) {
+ event->accept();
+ if (event->key() == Qt::Key_Left)
+ angleDelta = 1.3 * M_PI;
+ if (event->key() == Qt::Key_Right)
+ angleDelta = -1.3 * M_PI;
+ if (event->key() == Qt::Key_Up)
+ moveDelta = 2.5;
+ if (event->key() == Qt::Key_Down)
+ moveDelta = -2.5;
+ }
+
+ void keyReleaseEvent(QKeyEvent *event) {
+ event->accept();
+ if (event->key() == Qt::Key_Left)
+ angleDelta = (angleDelta > 0) ? 0 : angleDelta;
+ if (event->key() == Qt::Key_Right)
+ angleDelta = (angleDelta < 0) ? 0 : angleDelta;
+ if (event->key() == Qt::Key_Up)
+ moveDelta = (moveDelta > 0) ? 0 : moveDelta;
+ if (event->key() == Qt::Key_Down)
+ moveDelta = (moveDelta < 0) ? 0 : moveDelta;
+ }
+
+ void mousePressEvent(QMouseEvent *event) {
+ qreal dx = centerPad.x() - event->pos().x();
+ qreal dy = centerPad.y() - event->pos().y();
+ angleDelta = dx * 2 * M_PI / width();
+ moveDelta = dy * 10 / height();
+ }
+
+ void mouseMoveEvent(QMouseEvent *event) {
+ qreal dx = centerPad.x() - event->pos().x();
+ qreal dy = centerPad.y() - event->pos().y();
+ angleDelta = dx * 2 * M_PI / width();
+ moveDelta = dy * 10 / height();
+ }
+
+ void mouseReleaseEvent(QMouseEvent*) {
+ angleDelta = 0;
+ moveDelta = 0;
+ }
+
+private:
+ QTime watch;
+ QBasicTimer ticker;
+ QImage buffer;
+ qreal angle;
+ QPointF playerPos;
+ qreal angleDelta;
+ qreal moveDelta;
+ QImage textureImg;
+ int textureCount;
+ bool touchDevice;
+ QRect trackPad;
+ QPoint centerPad;
+ QSize bufferSize;
+};
+
+int main(int argc, char **argv)
+{
+ QApplication app(argc, argv);
+
+ Raycasting w;
+ w.setWindowTitle("Raycasting");
+#if defined(Q_OS_SYMBIAN) || defined(Q_OS_WINCE_WM)
+ w.showMaximized();
+#else
+ w.resize(640, 480);
+ w.show();
+#endif
+
+ return app.exec();
+}
diff --git a/examples/embedded/raycasting/raycasting.pro b/examples/embedded/raycasting/raycasting.pro
new file mode 100644
index 0000000000..181eadf74c
--- /dev/null
+++ b/examples/embedded/raycasting/raycasting.pro
@@ -0,0 +1,14 @@
+TEMPLATE = app
+SOURCES = raycasting.cpp
+RESOURCES += raycasting.qrc
+
+symbian {
+ TARGET.UID3 = 0xA000CF76
+ CONFIG += qt_example
+}
+
+target.path = $$[QT_INSTALL_EXAMPLES]/qtbase/embedded/raycasting
+sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS *.pro
+sources.path = $$[QT_INSTALL_EXAMPLES]/qtbase/embedded/raycasting
+INSTALLS += target sources
+QT += widgets widgets
diff --git a/examples/embedded/raycasting/raycasting.qrc b/examples/embedded/raycasting/raycasting.qrc
new file mode 100644
index 0000000000..974a06093c
--- /dev/null
+++ b/examples/embedded/raycasting/raycasting.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/" >
+ <file>textures.png</file>
+ </qresource>
+</RCC>
diff --git a/examples/embedded/raycasting/textures.png b/examples/embedded/raycasting/textures.png
new file mode 100644
index 0000000000..2eb1ba7ff6
--- /dev/null
+++ b/examples/embedded/raycasting/textures.png
Binary files differ