summaryrefslogtreecommitdiff
path: root/themes/LeafParticleRender.cpp
blob: 2fc89ac813ae1afff647802d5cdd32c14d8a1cf0 (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
#include "LeafParticleRender.h"
#include "Renderer2d.h"
#include "mathlib.h"
#include "TreeShape.h"
#include "types.h"
#include <math.h>

const int32 verticesPerLeaf = 6; 
const float32 leafRadius = 3.f;
const int32 fallChanceMax = 100;

inline void updateLeaf(Renderer2dVertex* vertices, Vector2 position, Vector4 color, float32 scale) {
    float32 radius = scale * leafRadius;
    Vector2 bottomLeft = Vector2(-radius, -radius) + position;
    Vector2 bottomRight = Vector2(radius, -radius) + position;
    Vector2 topLeft = Vector2(-radius, radius) + position;
    Vector2 topRight = Vector2(radius, radius) + position;

    vertices[0] = { bottomLeft, color };
    vertices[1] = { bottomRight, color };
    vertices[2] = { topLeft, color };
    vertices[3] = { topLeft, color };
    vertices[4] = { topRight, color };
    vertices[5] = { bottomRight, color };
}

void LeafParticleRender::load(Renderer2d *renderer, TreeShapeLoadResult* lr) {
    LeafParticleLoadData ld;
    ld.numLeaves = 256;
    numLeaves = ld.numLeaves;
    numVertices = ld.numLeaves * verticesPerLeaf;

    updateData = new LeafParticleUpdateData[numLeaves];
    vertices = new Renderer2dVertex[numVertices];

    for (int32 leafIdx = 0; leafIdx < numLeaves; leafIdx++) {
        int32 randomBranch = randomIntBetween(0, lr->numBranches);
        int32 randomVertex = randomIntBetween(0, 6); // TODO: Manually entering num vertices per branch.
        updateData[leafIdx].vertexToFollow = &lr->updateData[randomBranch].vertices[randomVertex];
        updateData[leafIdx].fallChance = randomIntBetween(0, fallChanceMax);
        updateData[leafIdx].color = Vector4(randomFloatBetween(0.3, 0.9), randomFloatBetween(0.1, 0.6), 0, 1);
        updateData[leafIdx].vertexPtr = &vertices[leafIdx * verticesPerLeaf];
        updateData[leafIdx].resetTime = randomFloatBetween(4.f, 6.f);
    }

    useShader(renderer->shader);

    glGenVertexArrays(1, &vao);
    glBindVertexArray(vao);

    glGenBuffers(1, &vbo);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, numVertices * sizeof(Renderer2dVertex), &vertices[0], GL_DYNAMIC_DRAW);

    glEnableVertexAttribArray(renderer->attributes.position);
    glVertexAttribPointer(renderer->attributes.position, 2, GL_FLOAT, GL_FALSE, sizeof(Renderer2dVertex), (GLvoid *)0);

    glEnableVertexAttribArray(renderer->attributes.color);
    glVertexAttribPointer(renderer->attributes.color, 4, GL_FLOAT, GL_FALSE, sizeof(Renderer2dVertex), (GLvoid *)offsetof(Renderer2dVertex, color));

    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);
}

void LeafParticleRender::update(float32 dtSeconds) {
    elapsedTimeSeconds += dtSeconds;

    // Every time the fallIntervalSeconds passes, we remove one leaf
    // from the tree and send it barrelling towards the earth.
    int32 fallRoll;
    bool didGenerateFall = false;
    if (elapsedTimeSeconds >= fallIntervalSeconds) {
        fallRoll = randomIntBetween(0, fallChanceMax);
        didGenerateFall = true;
        elapsedTimeSeconds = 0;
    }

    for (int32 leafIdx = 0; leafIdx < numLeaves; leafIdx++) {
        auto updateDataItem = &updateData[leafIdx];

        if (didGenerateFall) {
            if (updateDataItem->state == LeafParticleState::OnTree && updateDataItem->fallChance == fallRoll) {
                updateDataItem->state = LeafParticleState::Falling;
                updateDataItem->fallPosition = updateDataItem->vertexToFollow->position;
                updateDataItem->fallVerticalVelocity = -randomFloatBetween(15.f, 25.f);
                updateDataItem->fallHorizontalFrequency = randomFloatBetween(3.f, 5.f);
            }
        }

        switch (updateDataItem->state) {
        case (LeafParticleState::Remerging): {
            updateDataItem->timeElapsedSeconds += dtSeconds;

            if (updateDataItem->timeElapsedSeconds >= updateDataItem->resetTime) {
                updateDataItem->timeElapsedSeconds = 0.f;
                updateDataItem->state = LeafParticleState::OnTree;
                updateDataItem->color.w = 1.f;
                updateDataItem->scale = 1.f;
            }
            else {
                updateDataItem->color.w = (updateDataItem->timeElapsedSeconds / updateDataItem->resetTime);
                updateDataItem->scale = (updateDataItem->timeElapsedSeconds / updateDataItem->resetTime);
            }

            updateLeaf(updateDataItem->vertexPtr, updateDataItem->vertexToFollow->position, updateDataItem->color, updateDataItem->scale);
            break;
        }
        case (LeafParticleState::OnGround): {
            updateDataItem->timeElapsedSeconds += dtSeconds;

            if (updateDataItem->timeElapsedSeconds >= updateDataItem->resetTime) {
                updateDataItem->timeElapsedSeconds = 0.f;
                updateDataItem->color.w = 0.f;
                updateDataItem->state = LeafParticleState::Remerging;
            }
            else {
                updateDataItem->color.w = 1.f - (updateDataItem->timeElapsedSeconds / updateDataItem->resetTime);
                updateLeaf(updateDataItem->vertexPtr, updateDataItem->fallPosition, updateDataItem->color, updateDataItem->scale);
            }
            break;
        }
        case (LeafParticleState::Falling): {
            updateDataItem->timeElapsedSeconds += dtSeconds;
            const float32 xPosUpdate = cosf(updateDataItem->fallHorizontalFrequency * updateDataItem->timeElapsedSeconds);
            updateDataItem->fallPosition.x += xPosUpdate;
            updateDataItem->fallPosition.y += updateDataItem->fallVerticalVelocity * dtSeconds;
            if (updateDataItem->fallPosition.y <= 50.f) { // TODO: Hardcoded ground for now
                updateDataItem->fallPosition.y = 50.f;
                updateDataItem->state = LeafParticleState::OnGround;
                updateDataItem->timeElapsedSeconds = 0;
                updateDataItem->resetTime = randomFloatBetween(2.f, 5.f); // TODO: Hardcoded reset interval
            }
            updateLeaf(updateDataItem->vertexPtr, updateDataItem->fallPosition, updateDataItem->color, updateDataItem->scale);
            break;
        }
        case (LeafParticleState::OnTree): {
            updateLeaf(updateDataItem->vertexPtr, updateDataItem->vertexToFollow->position, updateDataItem->color, updateDataItem->scale);
            break;
        }
        }
    }
}

void LeafParticleRender::render(Renderer2d *renderer) {
    setShaderMat4(renderer->uniforms.model, model);

    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferSubData(GL_ARRAY_BUFFER, 0, numVertices * sizeof(Renderer2dVertex), &vertices[0]);

    glBindVertexArray(vao);
    glDrawArrays(GL_TRIANGLES, 0, numVertices);
    glBindVertexArray(0);
}

void LeafParticleRender::unload() {
    glDeleteVertexArrays(1, &vao);
    glDeleteBuffers(1, &vbo);
    delete [] vertices;

    elapsedTimeSeconds = 0;
}