Projekt

Obecné

Profil

Stáhnout (18.5 KB) Statistiky
| Větev: | Tag: | Revize:
1 c137512e Oto Šťáva
#include "scene.hpp"
2
3
const int Scene::TRAJECTORY_I = 0;
4
const int Scene::SPHERE_I = 1;
5
const int Scene::SPHERE_II = 2;
6
const int Scene::FIRST_ARROW_I = 3;
7
const float Scene::FIRST_ARROW_WIDTH_SCALING = 750.0f;
8
const float Scene::SECOND_ARROW_WIDTH_SCALING = 400.0f;
9
const QVector3D Scene::MODIFY_LIGHT_POSITION = QVector3D(0, 0, 0);
10
const int Scene::ROTATE_REDUCTION = 25;
11
12
Scene::Scene(QWidget *parent): QOpenGLWidget(parent)
13
{
14
    xAngle = 0;
15
    yAngle = 0;
16
    eyeAngle = 45;
17
    shaderProgram = NULL;
18
    
19
    // Vytvoreni grafickych prvku.
20
    elements[TRAJECTORY_I] = (Element *) new Trajectory();
21
    elements[SPHERE_I] = (Element *) new Sphere(true);
22
    elements[SPHERE_II] = (Element *) new Sphere(false);
23
    elements[FIRST_ARROW_I] = (Element *) new Arrow(true);
24
    elements[FIRST_ARROW_I + 1] = (Element *) new Arrow(false);
25
    elements[FIRST_TYPE_WALL_I] = (Element *) new Wall(Wall::TYPE::FLOOR);
26
    elements[FIRST_TYPE_WALL_I + 1] = (Element *) new Wall(Wall::TYPE::CEILING);
27
    elements[FIRST_TYPE_WALL_I + 2] = (Element *) new Wall(Wall::TYPE::SIDE);
28
    
29
    // Cesty k vyuzivanym texturam.
30
    texturePath[0] = tr(":/img/floor.png");
31
    texturePath[1] = tr(":/img/ceiling_1013.png");
32
    texturePath[2] = tr(":/img/tile.png");
33
    
34
    // Barva pozadi, zde je zbytecne nastavovat alfa kanal.
35
    backgroundColor = QVector3D(0.702f, 1, 1);
36
    // Barva tubusu predstavujici krivku.
37
    colorValues[0] = QVector4D(1, 0.737f, 0.231f, 0.7f);
38
    // Barva koule, která se pohybuje.
39
    colorValues[1] = QVector4D(0, 0.4f, 1, 0.7f);
40
    //Barva statické koule
41
    colorValues[2] = QVector4D(0, 0.8f, 0.4f, 0.0f);
42
    // Barva prvni (dulezitejsi) krivky.
43
    colorValues[3] = QVector4D(0, 1, 0, 1);
44
    // Barva druhe (mene dulezite) krivky. Zmena pruhlednosti = zmena alfa kanalu.
45
    colorValues[4] = QVector4D(1, 0, 0, 0.75f);
46
}
47
48
Scene::~Scene()
49
{
50
    clear();
51
    // Uvolnit zbyvajici alokovanou pamet (uz zbyvaji pouze graficke prvky, ktere
52
    // jsou vytvoreny na zacatku a vyuzivany po celou dobu behu programu - nejsou
53
    // nejak napojeny na jadro OpenGL, hodnoty v techto objektech se vzdy
54
    // prekopiruji do celkoveho pole, ktere se pak ulozi do bufferu).
55
    for (int i = 0; i < FIRST_TYPE_WALL_I + 3; i++)
56
    {
57
        delete elements[i];
58
    }
59
}
60
61
void Scene::setCurve(QList<QVector4D> curve)
62
{
63
    // Vyhledani minimalnich a maximalnich souradnic.
64
    QList<QVector3D> curve3d;
65
    QVector3D minimum = curve.at(0).toVector3D();
66
    QVector3D maximum = curve.at(0).toVector3D();
67
    int itemCount = curve.size();
68
#if 0
69
    for (int i = 1; i < curve.count(); i++)
70
    {
71
        if (curve.at(i).x() < minimum.x())
72
        {
73
            minimum.setX(curve.at(i).x());
74
        }
75
        else if (curve.at(i).x() > maximum.x())
76
        {
77
            maximum.setX(curve.at(i).x());
78
        }
79
        if (curve.at(i).y() < minimum.y())
80
        {
81
            minimum.setY(curve.at(i).y());
82
        }
83
        else if (curve.at(i).y() > maximum.y())
84
        {
85
            maximum.setY(curve.at(i).y());
86
        }
87
        if (curve.at(i).z() < minimum.z())
88
        {
89
            minimum.setZ(curve.at(i).z());
90
        }
91
        else if (curve.at(i).z() > maximum.z())
92
        {
93
            maximum.setZ(curve.at(i).z());
94
        }
95
    }
96
#else
97
    //max a min nepocitat podle kriky, ale mit vzdycky maximalni rozsah
98
    minimum = QVector3D(-3200,-3200,0);
99
    maximum = QVector3D(3200,3200,9000);
100
#endif
101
102
    // Posunout stred krivky do pocatku soustavy souradnic (kvuli ruznym upravam).
103
    // Uchovat stred puvodne umistene krivky kvuli prichozim bodum predstavujici
104
    // pozici koule (souradnice tohoto bodu se take musi upravit pro presne zobrazeni).
105
    center = Sphere::middlePoint(minimum, maximum);
106
    minimum -= center;
107
    maximum -= center;
108
    curve3d.clear();
109
    if (itemCount > MAX_CURVE_SIZE)
110
        itemCount = MAX_CURVE_SIZE;
111
    for (int i = 0; i < itemCount; i++)
112
    {
113
        curve3d.append(curve[i].toVector3D() - center);
114
    }
115
    
116
    // Zjisteni a nastaveni optimalni polohy kamery na ose Z.
117
    QVector3D marginForTube(Trajectory::MAX_RADIUS, Trajectory::MAX_RADIUS, Trajectory::MAX_RADIUS);
118
    QVector3D marginForSphere(Sphere::getMaxRadius(), Sphere::getMaxRadius(), Sphere::getMaxRadius());
119
    float tg = tan((eyeAngle / 2) * M_PI / 180);
120
    QVector3D heights = QVector3D(maximum + marginForTube + marginForSphere) / tg;
121
    camera.set(QVector3D(0, 0, maximum.z() + max(heights.x(), heights.y())));
122
    
123
    // Nastaveni atributu vykreslovanych objektu.
124
    ((Trajectory *) (elements[TRAJECTORY_I]))->setNewCurve(curve3d, minimum.z(), maximum.z());
125
    ((Sphere *) (elements[SPHERE_I]))->setRangeZ(minimum.z(), maximum.z());
126
#ifdef USE_STATIC_SPHERE
127
    ((Sphere *) (elements[SPHERE_II]))->setRangeZ(minimum.z(), maximum.z());
128
#else
129
    ((Sphere *) (elements[SPHERE_II]))->setRangeZ(0,0);
130
    ((Sphere *) (elements[SPHERE_II]))->setPosition(QVector3D(0,0,-3200));
131
#endif
132
    for (int i = FIRST_TYPE_WALL_I; i < ELEMENT_COUNT; i++)
133
    {
134
        ((Wall *) (elements[i]))->setData(minimum, maximum, ((Sphere *) (elements[SPHERE_I]))->getMaxRadius());
135
    }
136
    
137
    // Upravit data v bufferu.
138
    updateBufferData();
139
    
140
    // Prenastavit pozici svetla.
141
    shaderProgram->bind();
142
    shaderProgram->setUniformValue(lightLocation, camera.getPosition() + MODIFY_LIGHT_POSITION);
143
    int colorLocation = shaderProgram->uniformLocation("un_Color[0]");
144
    shaderProgram->setUniformValue(colorLocation, colorValues[0]);
145
    shaderProgram->release();
146
    
147
    // Vynulovat pripadnou rotaci.
148
    setXAngle(0); setYAngle(0);
149
    
150
    // Vykreslit.
151
    update();
152
}
153
154
void Scene::setSphereData(const QVector3D &position, const QVector3D &fArrow, const QVector3D &sArrow)
155
{
156
    // Zmenit vektory pro prvni a druhou sipku.
157
    firstArrow = fArrow;
158
    secondArrow = sArrow;
159
    // Zmenit pozici koule.
160
    ((Sphere *) (elements[SPHERE_I]))->setPosition(position - center);
161
    // Prekreslit scenu.
162
    update();
163
}
164
165
void Scene::setTestSphereData(const QVector3D &position)
166
{
167
#ifdef USE_STATIC_SPHERE
168
    // Zmenit pozici koule.
169
    ((Sphere *) (elements[SPHERE_II]))->setPosition(position - center);
170
    // Prekreslit scenu.
171
    update();
172
#endif
173
}
174
175
QVector3D Scene::getArrow(bool first)
176
{
177
    return first ? firstArrow : secondArrow;
178
}
179
180
void Scene::setArrow(bool first, QVector3D vector)
181
{
182
    first ? firstArrow = vector : secondArrow = vector;
183
}
184
185
void Scene::setTubusColor(QVector4D color)
186
{
187
    colorValues[0] = color;
188
}
189
190
void Scene::addSphereData(QVector3D position, const bool start, const bool finish)
191
{
192
    QVector3D minimum = QVector3D(-3200,-3200,0);
193
    QVector3D maximum = QVector3D(3200,3200,9000);
194
195
    // Posunout stred krivky do pocatku soustavy souradnic (kvuli ruznym upravam).
196
    // Uchovat stred puvodne umistene krivky kvuli prichozim bodum predstavujici
197
    // pozici koule (souradnice tohoto bodu se take musi upravit pro presne zobrazeni).
198
    center = Sphere::middlePoint(minimum, maximum);
199
    minimum -= center;
200
    maximum -= center;
201
    //position -= center;
202
203
    if (start) {
204
        // Zjisteni a nastaveni optimalni polohy kamery na ose Z.
205
        QVector3D marginForTube(Trajectory::MAX_RADIUS, Trajectory::MAX_RADIUS, Trajectory::MAX_RADIUS);
206
        QVector3D marginForSphere(Sphere::getMaxRadius(), Sphere::getMaxRadius(), Sphere::getMaxRadius());
207
        float tg = tan((eyeAngle / 2) * M_PI / 180);
208
        QVector3D heights = QVector3D(maximum + marginForTube + marginForSphere) / tg;
209
        camera.set(QVector3D(0, 0, maximum.z() + max(heights.x(), heights.y())));
210
211
        // Nastaveni atributu vykreslovanych objektu.
212
    }
213
    ((Trajectory *) (elements[TRAJECTORY_I]))->addNewPointToCurve(position, start, finish);
214
    if (start) {
215
        ((Sphere *) (elements[SPHERE_I]))->setRangeZ(minimum.z(), maximum.z());
216
        ((Sphere *) (elements[SPHERE_II]))->setRangeZ(minimum.z(), maximum.z());
217
        for (int i = FIRST_TYPE_WALL_I; i < ELEMENT_COUNT; i++)
218
        {
219
            ((Wall *) (elements[i]))->setData(minimum, maximum, ((Sphere *) (elements[SPHERE_I]))->getMaxRadius());
220
        }
221
    }
222
223
    // Upravit data v bufferu.
224
    updateBufferData();
225
226
    // Prenastavit pozici svetla.
227
    shaderProgram->bind();
228
    shaderProgram->setUniformValue(lightLocation, camera.getPosition() + MODIFY_LIGHT_POSITION);
229
    int colorLocation = shaderProgram->uniformLocation("un_Color[0]");
230
    shaderProgram->setUniformValue(colorLocation, colorValues[0]);
231
    shaderProgram->release();
232
233
    // Vynulovat pripadnou rotaci.
234
    setXAngle(0); setYAngle(0);
235
236
    // Vykreslit.
237
    update();
238
}
239
240
QSize Scene::minimumSizeHint() const
241
{
242
    return QSize(100, 100);
243
}
244
245
QSize Scene::sizeHint() const
246
{
247
    return QSize(500, 500);
248
}
249
250
void Scene::initializeGL()
251
{
252
    // V prubehu chodu aplikace muze dojit ke zruseni sceny a k jejimu opetovnemu
253
    // vytvoreni (opetovnemu zavolani initializeGL()). Proto je vhodne po sobe
254
    // uklidit v pripade teto nastale udalosti.
255
    QObject::connect(context(), SIGNAL(aboutToBeDestroyed()), this, SLOT(clear()));
256
    
257
    // Vytvoreni GLSL podprogramu. Pridani zdrojovych souboru a napojeni attribute
258
    // promennych na hodnoty, ktere budou vyuzivany pri mapovani hodnot.
259
    shaderProgram = new QOpenGLShaderProgram();
260
    shaderProgram->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/glsl/shader.vert");
261
    shaderProgram->addShaderFromSourceFile(QOpenGLShader::Fragment, ":/glsl/shader.frag");
262
    shaderProgram->bindAttributeLocation("vertex", 0);
263
    shaderProgram->bindAttributeLocation("normal", 1);
264
    shaderProgram->bindAttributeLocation("group", 2);
265
    shaderProgram->link();
266
    
267
    shaderProgram->bind();
268
    
269
    // Vytvoreni textur a pocatecni inicializace uniform atributu TEXTURE.
270
    for (int i = 0; i < TEXTURE_COUNT; i++)
271
    {
272
        textures[i] = new QOpenGLTexture(QImage(texturePath[i]).mirrored());
273
    }
274
    shaderProgram->setUniformValue("texture", 0);
275
    
276
    // Zjisteni lokaci ostatnich uniform atributu.
277
    projectionLocation = shaderProgram->uniformLocation("un_Projection");
278
    lightLocation = shaderProgram->uniformLocation("un_Light");
279
    for (int i = 0; i < FIRST_TYPE_WALL_I; i++)
280
    {
281
        normalLocations[i] = shaderProgram->uniformLocation(QString("un_Normal[%1]").arg(i));
282
        mvLocations[i] = shaderProgram->uniformLocation(QString("un_ModelView[%1]").arg(i));
283
        
284
        // U barev rovnou nastavit i obsah promennych.
285
        int colorLocation = shaderProgram->uniformLocation(QString("un_Color[%1]").arg(i));
286
        shaderProgram->setUniformValue(colorLocation, colorValues[i]);
287
    }
288
    
289
    // Pocatecni inicializace pozice svetla a spusteni shader programu.
290
    shaderProgram->setUniformValue(lightLocation, QVector3D(0, 0, 0));
291
    shaderProgram->release();
292
    
293
    // Pro jistotu, manualni vytvoreni pole pro vrcholy a spojeni se scenou.
294
    vao.create();
295
    QOpenGLVertexArrayObject::Binder vaoBinder(&vao);
296
    
297
    // Vytvoreni buffer pro vrcholy.
298
    buffer.create();
299
    buffer.bind();
300
    
301
    // Namapovani hodnot z bufferu na attribute promenne v shaderu.
302
    QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
303
    f->glEnableVertexAttribArray(0);
304
    f->glEnableVertexAttribArray(1);
305
    f->glEnableVertexAttribArray(2);
306
    f->glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, Element::DATA_COUNT_FOR_VERTEX * sizeof(GLfloat), reinterpret_cast<void *>(0 * sizeof(GLfloat)));
307
    f->glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, Element::DATA_COUNT_FOR_VERTEX * sizeof(GLfloat), reinterpret_cast<void *>(3 * sizeof(GLfloat)));
308
    f->glVertexAttribPointer(2, 1, GL_FLOAT, GL_FALSE, Element::DATA_COUNT_FOR_VERTEX * sizeof(GLfloat), reinterpret_cast<void *>(6 * sizeof(GLfloat)));
309
    buffer.release();
310
    
311
    // Nastaveni vykreslovacich modu.
312
    glEnable(GL_DEPTH_TEST);
313
    glEnable(GL_CULL_FACE);
314
    glEnable(GL_MULTISAMPLE); // Anti-aliasing (s QSurfaceFormat::setSamples(4)).
315
    glEnable(GL_BLEND); // Pruhlednost.
316
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
317
}
318
319
void Scene::paintGL()
320
{
321
    QOpenGLVertexArrayObject::Binder vaoBinder(&vao);
322
    shaderProgram->bind();
323
    
324
    // Nastaveni zmenenych globalnich promennych shaderum.
325
    shaderProgram->setUniformValue(projectionLocation, projection);
326
    for (int i = 0; i < FIRST_TYPE_WALL_I; i++)
327
    {
328
        shaderProgram->setUniformValue(normalLocations[i], worlds[i].normalMatrix());
329
        shaderProgram->setUniformValue(mvLocations[i], camera * worlds[i]);
330
331
        // U vsech vykreslovanych prvku nastavit globalni rotaci.
332
        worlds[i].setToIdentity();
333
        worlds[i].rotate(xAngle / (float) ROTATE_REDUCTION, QVector3D(1, 0, 0));
334
        worlds[i].rotate(yAngle / (float) ROTATE_REDUCTION, QVector3D(0, 1, 0));
335
        
336
        if (i > TRAJECTORY_I)
337
        {
338
            float sphereScaling;
339
            if (i == SPHERE_II) {
340
                worlds[i].translate(((Sphere *) (elements[SPHERE_II]))->getPosition());
341
                sphereScaling = ((Sphere *) elements[SPHERE_II])->getCurrentScaling();
342
            } else {
343
                // Jak pro kouli, tak pro sipky, provest posunuti do nastaveneho bodu.
344
                worlds[i].translate(((Sphere *) (elements[SPHERE_I]))->getPosition());
345
                sphereScaling = ((Sphere *) elements[SPHERE_I])->getCurrentScaling();
346
            }
347
            
348
            if (i == SPHERE_I)
349
            {
350
                // Pro kouli provest danou zmenu meritka.
351
                worlds[i].scale(sphereScaling);
352
            } else if (i == SPHERE_II) {
353
                worlds[i].scale(sphereScaling);
354
            } else {
355
                QVector3D top = QVector3D(0, 1, 0);
356
                
357
                QVector3D arrow;
358
                float arrowScaling;
359
                if (i == FIRST_ARROW_I)
360
                {
361
                    arrow = firstArrow;
362
                    arrowScaling = FIRST_ARROW_WIDTH_SCALING;
363
                }
364
                else
365
                {
366
                    arrow = secondArrow;
367
                    arrowScaling = SECOND_ARROW_WIDTH_SCALING;
368
                }
369
                
370
                // Pro obe sipky urcit, podle jakeho vektoru ma byt provedena
371
                // rotace, o jaky uhel maji byt orotovany a podle velikosti
372
                // vektoru nastavit i zmenu meritka.
373
                float angle = acos(QVector3D::dotProduct(arrow, top) / (arrow.length() * top.length())) * 180 / M_PI;
374
                QVector3D normal = QVector3D::crossProduct(top, arrow);
375
                worlds[i].rotate(angle, normal);
376
                worlds[i].scale(sphereScaling * arrowScaling, sphereScaling * arrow.length(), sphereScaling * arrowScaling);
377
            }
378
        }
379
    }
380
    
381
    // Nastavit kam ma byt provedeno vykreslovani a vycistit 3D scenu.
382
    glViewport(0, 0, width(), height());
383
    glClearColor(backgroundColor.x(), backgroundColor.y(), backgroundColor.z(), 1);
384
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
385
    
386
    if (((Trajectory *) elements[TRAJECTORY_I])->isSet())
387
    {
388
        // Pomocna promenna pro urceni pocatecnich indexu vykreslovacich komponent.
389
        int vertCount = 0;
390
391
        // Vykresleni vsech grafickych prvku az na zdi.
392
        int i;
393
        int auxVertexCount = 0;
394
        bool sphereIsSet = ((Sphere *) elements[SPHERE_I])->isSet();
395
        //bool staticSphereIsSet = ((Sphere *) elements[SPHERE_II])->isSet();
396
        for (i = 0; i < FIRST_TYPE_WALL_I; i++)
397
        {
398
            vertCount += elements[i]->vertexCount();
399
            if (!sphereIsSet && i > 0)
400
            {
401
                auxVertexCount += elements[i]->vertexCount();
402
            }
403
        }
404
        // Pokud neni nastavena pozice pro kouli a sipky, vykreslit pouze trajektorii.
405
        glDrawArrays(GL_TRIANGLES, 0, vertCount - auxVertexCount);
406
407
        // Vykresleni zdi, stropu a podlah.
408
        for (int j = 0; j < TEXTURE_COUNT; j++, i++)
409
        {
410
            textures[j]->bind();
411
            glDrawArrays(GL_QUADS, vertCount, elements[i]->vertexCount());
412
            vertCount += elements[i]->vertexCount();
413
        }
414
    }
415
416
    shaderProgram->release();
417
    buffer.release();
418
}
419
420
void Scene::resizeGL(int w, int h)
421
{
422
    // Nastavit matici projekce pri kazde zmene velikosti okna.
423
    projection.setToIdentity();
424
    projection.perspective(eyeAngle, w / (float) h, 1, 40000);
425
}
426
427
void Scene::mousePressEvent(QMouseEvent *event)
428
{
429
    // Pri kazdem stisknuti tlacitka ulozit, kde je kurzor (kvuli otaceni).
430
    lastPosition = event->pos();
431
}
432
433
void Scene::mouseMoveEvent(QMouseEvent *event)
434
{
435
    if (event->buttons() == Qt::LeftButton || event->buttons() == Qt::RightButton)
436
    {
437
        // Pro obe tlacitka otacet stejne, konkretne podle osy X a Y.
438
        int dx = event->x() - lastPosition.x();
439
        int dy = event->y() - lastPosition.y();
440
        lastPosition = event->pos();
441
        setXAngle(xAngle + 5 * dy);
442
        setYAngle(yAngle + 5 * dx);
443
        update();
444
    }
445
}
446
447
void Scene::wheelEvent(QWheelEvent *e)
448
{
449
    // Pri pohybu kolecka na mysi priblizovat ci oddalovat scenu (resp. kameru
450
    // k/od pocatku soustavy souradnic).
451
    if (e != NULL)
452
    {
453
        if (e->delta() > 0)
454
        {
455
            camera.zoomOut();
456
        }
457
        else
458
        {
459
            camera.zoomIn();
460
        }
461
    }
462
}
463
464
void Scene::updateBufferData()
465
{
466
    int resultDataCount = 0;
467
    for (int i = 0; i < ELEMENT_COUNT; i++)
468
    {
469
        resultDataCount += elements[i]->dataCount();
470
    }
471
    
472
    // Vytvoreni pole s hodnotami pro vsechny vykreslovane prvky.
473
    GLfloat *resultData = new GLfloat[resultDataCount];
474
    int resultDataIndex = 0;
475
    for (int i = 0; i < ELEMENT_COUNT; i++)
476
    {
477
        const GLfloat *data = elements[i]->getData();
478
        for (int j = 0; j < elements[i]->dataCount(); j++)
479
        {
480
            resultData[resultDataIndex++] = data[j];
481
        }
482
    }
483
    
484
    // Prenastavit obsah bufferu.
485
    buffer.bind();
486
    buffer.allocate(resultData, resultDataCount * sizeof(GLfloat));
487
    delete[] resultData;
488
}
489
490
void Scene::setXAngle(int angle)
491
{
492
    angle %= 360 * ROTATE_REDUCTION;
493
    
494
    if (angle != xAngle)
495
    {
496
        xAngle = angle;
497
    }
498
}
499
500
void Scene::setYAngle(int angle)
501
{
502
    angle %= 360 * ROTATE_REDUCTION;
503
    
504
    if (angle != yAngle)
505
    {
506
        yAngle = angle;
507
    }
508
}
509
510
void Scene::clear()
511
{
512
    if (shaderProgram != NULL)
513
    {
514
        makeCurrent();
515
        
516
        // Zruseni vytvoreneho bufferu pro data pro vrcholy.
517
        // Uvolneni pameti od alokovaneho pole pro vsechny hodnoty vsech vrcholu
518
        // viz updateBufferData().
519
        buffer.destroy();
520
        
521
        // Zruseni GLSL podprogramu.
522
        delete shaderProgram;
523
        shaderProgram = NULL;
524
        
525
        // Vycisteni pameti od alokovanych textur.
526
        for (int i = 0; i < TEXTURE_COUNT; i++)
527
        {
528
            textures[i]->destroy();
529
            textures[i] = NULL;
530
        }
531
        
532
        doneCurrent();
533
    }
534
}