aboutsummaryrefslogtreecommitdiff
path: root/src/raytracer/raytracer.cpp
blob: 604d717900d030f98484e020e5abb94e03f5cd39 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
#include <QList>
#include <QtConcurrent>
#include <iostream>
#include <cstdlib>
#include <iomanip>
#include "raytracer.h"
#include "raytracescene.h"
#include "settings.h"
#include "mainwindow.h"
#include <QKeyEvent>
#include <QTimerEvent>
#include "vec4ops/vec4ops.h"
#include "physics/physics.h"

// RayTracer::RayTracer(const Config &config) : m_config(config) {}
RayTracer::RayTracer(QWidget *parent) : QWidget(parent) {
    setFocusPolicy(Qt::StrongFocus);

    // map the 1 key
    m_keyMap[Qt::Key_1]       = false;
    m_keyMap[Qt::Key_2]       = false;
    m_keyMap[Qt::Key_3]       = false;
    m_keyMap[Qt::Key_4]       = false;
    m_keyMap[Qt::Key_5]       = false;
    m_keyMap[Qt::Key_6]       = false;

    m_width = 576;
    m_height = 432;
    m_depth = 500;

    m_image = QImage(m_width, m_height, QImage::Format_RGBX8888);
//    m_camera = nullptr;
}

glm::vec4 getPt(glm::vec3 n1 , glm::vec3 n2 , float perc )
{
    glm::vec3 diff = n2 - n1;

    return glm::vec4(n1 + ( diff * perc ), 0.0f);
}

// updated to use 4D
void RayTracer::render(RGBA *imageData, const RayTraceScene &scene) {
    renderParallel(imageData, scene);

    if (settings.bulkOutputFolderPath.size() > 0) { // means we are doing bulk rendering
        // save the image to the bulk directory
        std::string paddedTime = std::to_string(settings.currentTime);
        paddedTime = std::string(4 - paddedTime.length(), '0') + paddedTime;
        std::string filePath = settings.bulkOutputFolderPath + QDir::separator().toLatin1() + paddedTime + ".png";
        saveViewportImage(filePath);
        if (settings.currentTime < settings.maxTime) { // still more to render
            // render the next frame
            settings.currentTime++;
//            settings.w++;

            // update physics for moving objects
            Physics::updateShapePositions(m_metaData.shapes);
            Physics::handleCollisions(m_metaData.shapes);

        } else { // done rendering
            // assemble the video
            saveFFMPEGVideo(settings.bulkOutputFolderPath);
            settings.currentTime = 0;
            settings.bulkOutputFolderPath = "";
        }
        QTimer::singleShot(0, this, [this]() {
            settingsChanged(m_imageLabel);
        });
    }
    emit cameraPositionChanged(m_metaData.cameraData.pos); 
}


glm::vec4 RayTracer::getPixelFromRay(
    glm::vec4 pWorld,
    glm::vec4 dWorld,
    const RayTraceScene &scene,
    int depth)
{
    if (depth > m_maxRecursiveDepth)
    {
        return glm::vec4(0.f);
    }

    // variables from computing the intersection
    glm::vec4 closestIntersectionObj;
    glm::vec4 closestIntersectionWorld;
    RenderShapeData intersectedShape;

    float minDist = FINF;
    // shoot a ray at each shape
    for (const RenderShapeData &shape : scene.getShapes()) {
        glm::vec4 pObject = Vec4Ops::inverseTransformPoint4(pWorld, shape.inverseCTM, -shape.translation4d);
        glm::vec4 dObject = glm::normalize(Vec4Ops::inverseTransformDir4(dWorld, shape.inverseCTM));
        bool isHit = false;
        glm::vec4 newIntersectionObj = findIntersection(pObject, dObject, shape, isHit);
        if (!isHit) // no hit
        {
            continue;
        }

        auto newIntersectionWorld = shape.ctm * newIntersectionObj;
        float newDist = glm::distance(newIntersectionWorld, pWorld);
        if (
                newDist < minDist // closer intersection
                && !floatEquals(newDist, 0) // and not a self intersection
                )
        {
            minDist = newDist;

            intersectedShape = shape;
            closestIntersectionObj = newIntersectionObj;
            closestIntersectionWorld = newIntersectionWorld;
        }
    }

    if (minDist == FINF) // no hit
    {
        return glm::vec4(0.f);
    }

    glm::vec4 normalObject = glm::normalize(getNormal(closestIntersectionObj, intersectedShape, scene));
    // update
    glm::vec4 normalWorld = glm::inverse(glm::transpose(intersectedShape.ctm)) * glm::vec4(normalObject);

    return illuminatePixel(closestIntersectionWorld, normalWorld, -dWorld, intersectedShape, scene, depth);
}

void RayTracer::sceneChanged(QLabel* imageLabel) {
    // RenderData metaData;
    
    bool success = SceneParser::parse(settings.sceneFilePath, m_metaData);

    if (!success) {
        std::cerr << "Error loading scene: \"" << settings.sceneFilePath << "\"" << std::endl;
        m_image.fill(Qt::black);
        imageLabel->setPixmap(QPixmap::fromImage(m_image));
        m_imageData = reinterpret_cast<RGBA *>(m_image.bits());
        return;
    }

    // render the scene
    m_imageData = reinterpret_cast<RGBA *>(m_image.bits());

    RayTraceScene rtScene{ m_width, m_height, m_metaData, m_depth };
    this->render(m_imageData, rtScene);

    QImage flippedImage = m_image.mirrored(false, false);
    // make the image larger
    flippedImage = flippedImage.scaled(m_width, m_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
    imageLabel->setPixmap(QPixmap::fromImage(flippedImage));

    m_imageLabel = imageLabel;
    // m_image = image;
}

 void RayTracer::settingsChanged(QLabel* imageLabel) {
    emit timeValueChanged(settings.currentTime);

    if (settings.sceneFilePath.size() == 0) {
        // no scene loaded
        m_image.fill(Qt::black);
        imageLabel->setPixmap(QPixmap::fromImage(m_image));
        m_imageData = reinterpret_cast<RGBA *>(m_image.bits());
        return;
    }

    int width = 576;
    int height = 432;

    QImage image = QImage(width, height, QImage::Format_RGBX8888);
    image.fill(Qt::black);
    m_imageData = reinterpret_cast<RGBA *>(image.bits());

    RayTraceScene rtScene{ m_width, m_height, m_metaData, m_depth };
    // update the camera position
    rtScene.m_camera.updateViewMatrix(m_metaData.cameraData);
    this->render(m_imageData, rtScene);

    QImage flippedImage = image.mirrored(false, false);
    flippedImage = flippedImage.scaled(width, height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
    imageLabel->setPixmap(QPixmap::fromImage(flippedImage));
    // m_controlPointIndex++;


    // QTimer::singleShot(3500, this, [this, imageLabel]() {
    //     // This code will be executed after a 2-second delay
    //     emit rotationChanged(settings.rotation);
    // });
    m_image = image;
 }

//void RayTracer::settingsChanged(QLabel* imageLabel) {
//    emit timeValueChanged(settings.currentTime);
//
//    bool success = SceneParser::parse(settings.sceneFilePath, m_metaData); // FIXME: this is a hack to get the camera position
//
//    if (!success) {
//        std::cerr << "Error loading scene: \"" << settings.sceneFilePath << "\"" << std::endl;
//        // return;
//        // render a blank image
//        QImage image = QImage(576, 432, QImage::Format_RGBX8888);
//        image.fill(Qt::black);
//        RGBA *data = reinterpret_cast<RGBA *>(image.bits());
//        m_imageData = data;
//        imageLabel->setPixmap(QPixmap::fromImage(image));
//    }
//
//    // if (settings.sceneFilePath.size() == 0) {
//    //     // no scene loaded
//    //     m_image.fill(Qt::black);
//    //     imageLabel->setPixmap(QPixmap::fromImage(m_image));
//    //     m_imageData = reinterpret_cast<RGBA *>(m_image.bits());
//    //     return;
//    // }
//
//    int width = 576;
//    int height = 432;
//
//    QImage image = QImage(width, height, QImage::Format_RGBX8888);
//    image.fill(Qt::black);
//    RGBA *data = reinterpret_cast<RGBA *>(image.bits());
//
//    RayTraceScene rtScene{ m_width, m_height, m_metaData, m_depth };
////    Camera camera = rtScene.getCamera();
////    if (settings.currentTime % 3 == 0) {
////        m_controlPoints = camera.m_controlPoints;
////    }
////
////    auto P1 = m_controlPoints[settings.currentTime];
////    auto P2 = m_controlPoints[settings.currentTime];
////    auto P3 = m_controlPoints[settings.currentTime];
////    auto P4 = m_controlPoints[settings.currentTime];
////
////    // glm::vec4 xa = getPt(P1, P2, settings.currentTime);
////    // glm::vec4 xb = getPt(P2, P3, settings.currentTime);
////    // glm::vec4 xc = getPt(P3, P4, settings.currentTime);
////
////    // // Calculate points on the lines between the above points
////    // glm::vec4 xm = getPt(xa, xb, settings.currentTime);
////    // glm::vec4 xn = getPt(xb, xc, settings.currentTime);
////
////    // // Calculate the final point on the Bezier curve
////    // glm::vec4 pointOnCurve = getPt(xm, xn, settings.currentTime);
////    // m_metaData.cameraData.pos = pointOnCurve;
////
////    settings.xy += 4.f;
////    if (m_controlPointIndex % 1 == 0) {
////        settings.xz += 8.f;
////    }
////    if (m_controlPointIndex % 3 == 0){
////        settings.yz += 8.f;
////    }
//    this->render(data, rtScene);
//
//    QImage flippedImage = image.mirrored(false, false);
//    flippedImage = flippedImage.scaled(width, height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
//    imageLabel->setPixmap(QPixmap::fromImage(flippedImage));
//    // m_controlPointIndex++;
//
//
//    // QTimer::singleShot(3500, this, [this, imageLabel]() {
//    //     // This code will be executed after a 2-second delay
//    //     emit rotationChanged(settings.rotation);
//    // });
//    m_image = image;
//}

void RayTracer::keyPressEvent(QKeyEvent *event) {
    m_keyMap[Qt::Key(event->key())] = true;
    std::cout << "key pressed" << std::endl;

    // J and L for xy rotation
    if (m_keyMap[Qt::Key_J]) {
        std::cout << "key 1" << std::endl;
            settings.xy += settings.rotation;
        emit xyRotationChanged(settings.xy);
    }
    if (m_keyMap[Qt::Key_L]) {
        settings.xy -= settings.rotation;
        emit xyRotationChanged(settings.xy);
    }

    // I and M for xz rotation
    if (m_keyMap[Qt::Key_I]) {
        settings.xz += settings.rotation;
        emit xzRotationChanged(settings.xz);
    }
    if (m_keyMap[Qt::Key_M]) {
        settings.xz -= settings.rotation;
        emit xzRotationChanged(settings.xz);
    }

    // O and N for yz rotation
    if (m_keyMap[Qt::Key_O]) {
        settings.yz += settings.rotation;
        emit yzRotationChanged(settings.yz);
    }
    if (m_keyMap[Qt::Key_N]) {
        settings.yz -= settings.rotation;
        emit yzRotationChanged(settings.yz);
    }

    // W and S for x translation
    if (m_keyMap[Qt::Key_W]) {
        settings.xw += settings.translation;
        emit xwRotationChanged(settings.xw);
    }
    if (m_keyMap[Qt::Key_S]) {
        settings.xw -= settings.translation;
        emit xwRotationChanged(settings.xw);
    }

    // A and D for y translation
    if (m_keyMap[Qt::Key_A]) {
        settings.yw += settings.translation;
        emit yzRotationChanged(settings.yw);
    }
    if (m_keyMap[Qt::Key_D]) {
        settings.yw -= settings.translation;
        emit ywRotationChanged(settings.yw);
    }

    // TODO: add slider for z translation

    // R & F for w translation using zw
    if (m_keyMap[Qt::Key_R]) {
        settings.zw += settings.translation;
        emit zwRotationChanged(settings.zw);
    }
    if (m_keyMap[Qt::Key_F]) {
        settings.zw -= settings.translation;
        emit zwRotationChanged(settings.zw);
    }

    // TODO: ONLY IF HAVE TIME, NOT NEEDED
    // Space & V for vorex depth
    if (m_keyMap[Qt::Key_Space]) {
        settings.w += settings.rotation;

    }
    if (m_keyMap[Qt::Key_V]) {
        settings.w -= settings.rotation;
    }
}

void RayTracer::keyReleaseEvent(QKeyEvent *event) {
    m_keyMap[Qt::Key(event->key())] = false;
}

void RayTracer::saveViewportImage(std::string filePath) {
    QImage image = QImage((uchar *) m_imageData, 576, 432, QImage::Format_RGBX8888);
    image.save(QString::fromStdString(filePath));
}

void RayTracer::saveFFMPEGVideo(std::string filePath) {
    std::string directory = filePath + QDir::separator().toLatin1();
    std::string command = "ffmpeg -framerate 24 -pattern_type glob -i '" + directory + "*.png' -c:v libx264 -pix_fmt yuv420p -crf 0 -y '" + directory + "video.mp4'";
    int result = std::system(command.c_str());
    if (result != 0) {
        std::cerr << "Failed to assemble video." << std::endl;
    }
}