1/*
2 * Copyright (C) 2016 Google, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <cassert>
18#include <cmath>
19#include <array>
20#include <glm/gtc/matrix_transform.hpp>
21#include "Simulation.h"
22
23namespace {
24
25class MeshPicker {
26public:
27    MeshPicker() :
28        pattern_({
29                Meshes::MESH_PYRAMID,
30                Meshes::MESH_ICOSPHERE,
31                Meshes::MESH_TEAPOT,
32                Meshes::MESH_PYRAMID,
33                Meshes::MESH_ICOSPHERE,
34                Meshes::MESH_PYRAMID,
35                Meshes::MESH_PYRAMID,
36                Meshes::MESH_PYRAMID,
37                Meshes::MESH_PYRAMID,
38                Meshes::MESH_PYRAMID,
39                }), cur_(-1)
40    {
41    }
42
43    Meshes::Type pick()
44    {
45        cur_ = (cur_ + 1) % pattern_.size();
46        return pattern_[cur_];
47    }
48
49    float scale(Meshes::Type type) const
50    {
51        float base = 0.005f;
52
53        switch (type) {
54        case Meshes::MESH_PYRAMID:
55        default:
56            return base * 1.0f;
57        case Meshes::MESH_ICOSPHERE:
58            return base * 3.0f;
59        case Meshes::MESH_TEAPOT:
60            return base * 10.0f;
61        }
62    }
63
64private:
65    const std::array<Meshes::Type, 10> pattern_;
66    int cur_;
67};
68
69class ColorPicker {
70public:
71    ColorPicker(unsigned int rng_seed) :
72        rng_(rng_seed),
73        red_(0.0f, 1.0f),
74        green_(0.0f, 1.0f),
75        blue_(0.0f, 1.0f)
76    {
77    }
78
79    glm::vec3 pick()
80    {
81        return glm::vec3{ red_(rng_),
82                          green_(rng_),
83                          blue_(rng_) };
84    }
85
86private:
87    std::mt19937 rng_;
88    std::uniform_real_distribution<float> red_;
89    std::uniform_real_distribution<float> green_;
90    std::uniform_real_distribution<float> blue_;
91};
92
93} // namespace
94
95Animation::Animation(unsigned int rng_seed, float scale)
96    : rng_(rng_seed), dir_(-1.0f, 1.0f), speed_(0.1f, 1.0f)
97{
98    float x = dir_(rng_);
99    float y = dir_(rng_);
100    float z = dir_(rng_);
101    if (std::abs(x) + std::abs(y) + std::abs(z) == 0.0f)
102        x = 1.0f;
103
104    current_.axis = glm::normalize(glm::vec3(x, y, z));
105
106    current_.speed = speed_(rng_);
107    current_.scale = scale;
108
109    current_.matrix = glm::scale(glm::mat4(1.0f), glm::vec3(current_.scale));
110}
111
112glm::mat4 Animation::transformation(float t)
113{
114    current_.matrix = glm::rotate(current_.matrix, current_.speed * t, current_.axis);
115
116    return current_.matrix;
117}
118
119class Curve {
120public:
121    virtual ~Curve() {}
122    virtual glm::vec3 evaluate(float t) = 0;
123};
124
125namespace {
126
127enum CurveType {
128    CURVE_RANDOM,
129    CURVE_CIRCLE,
130    CURVE_COUNT,
131};
132
133class RandomCurve : public Curve {
134public:
135    RandomCurve(unsigned int rng_seed)
136        : rng_(rng_seed), direction_(-0.3f, 0.3f), duration_(1.0f, 5.0f),
137          segment_start_(0.0f), segment_direction_(0.0f),
138          time_start_(0.0f), time_duration_(0.0f)
139    {
140    }
141
142    glm::vec3 evaluate(float t)
143    {
144        if (t >= time_start_ + time_duration_)
145            new_segment(t);
146
147        pos_ += unit_dir_ * (t - last_);
148        last_ = t;
149
150        return pos_;
151    }
152
153private:
154    void new_segment(float time_start)
155    {
156        segment_start_ += segment_direction_;
157        segment_direction_ = glm::vec3(direction_(rng_),
158                                       direction_(rng_),
159                                       direction_(rng_));
160
161        time_start_ = time_start;
162        time_duration_ = duration_(rng_);
163
164        unit_dir_ = segment_direction_ / time_duration_;
165        pos_ = segment_start_;
166        last_ = time_start_;
167    }
168
169    std::mt19937 rng_;
170    std::uniform_real_distribution<float> direction_;
171    std::uniform_real_distribution<float> duration_;
172
173    glm::vec3 segment_start_;
174    glm::vec3 segment_direction_;
175    float time_start_;
176    float time_duration_;
177
178    glm::vec3 unit_dir_;
179    glm::vec3 pos_;
180    float last_;
181};
182
183class CircleCurve : public Curve {
184public:
185    CircleCurve(float radius, glm::vec3 axis)
186        : r_(radius)
187    {
188        glm::vec3 a;
189
190        if (axis.x != 0.0f) {
191            a.x = -axis.z / axis.x;
192            a.y = 0.0f;
193            a.z = 1.0f;
194        } else if (axis.y != 0.0f) {
195            a.x = 1.0f;
196            a.y = -axis.x / axis.y;
197            a.z = 0.0f;
198        } else {
199            a.x = 1.0f;
200            a.y = 0.0f;
201            a.z = -axis.x / axis.z;
202        }
203
204        a_ = glm::normalize(a);
205        b_ = glm::normalize(glm::cross(a_, axis));
206    }
207
208    glm::vec3 evaluate(float t)
209    {
210        return (a_ * (glm::vec3(std::cos(t)) - glm::vec3(1.0f)) + b_ * glm::vec3(std::sin(t))) *
211            glm::vec3(r_);
212    }
213
214private:
215    float r_;
216    glm::vec3 a_;
217    glm::vec3 b_;
218};
219
220} // namespace
221
222Path::Path(unsigned int rng_seed)
223    : rng_(rng_seed), type_(0, CURVE_COUNT - 1), duration_(5.0f, 20.0f)
224{
225    // trigger a subpath generation
226    current_.end = -1.0f;
227    current_.now = 0.0f;
228}
229
230glm::vec3 Path::position(float t)
231{
232    current_.now += t;
233
234    while (current_.now >= current_.end)
235        generate_subpath();
236
237    return current_.origin + current_.curve->evaluate(current_.now - current_.start);
238}
239
240void Path::generate_subpath()
241{
242    float duration = duration_(rng_);
243    CurveType type = static_cast<CurveType>(type_(rng_));
244
245    if (current_.curve) {
246        current_.origin += current_.curve->evaluate(current_.end - current_.start);
247        current_.start = current_.end;
248    } else {
249        std::uniform_real_distribution<float> origin(0.0f, 2.0f);
250        current_.origin = glm::vec3(origin(rng_), origin(rng_), origin(rng_));
251        current_.start = current_.now;
252    }
253
254    current_.end = current_.start + duration;
255
256    Curve *curve;
257
258    switch (type) {
259    case CURVE_RANDOM:
260        curve = new RandomCurve(rng_());
261        break;
262    case CURVE_CIRCLE:
263        {
264            std::uniform_real_distribution<float> dir(-1.0f, 1.0f);
265            glm::vec3 axis(dir(rng_), dir(rng_), dir(rng_));
266            if (axis.x == 0.0f && axis.y == 0.0f && axis.z == 0.0f)
267                axis.x = 1.0f;
268
269            std::uniform_real_distribution<float> radius_(0.02f, 0.2f);
270            curve = new CircleCurve(radius_(rng_), axis);
271        }
272        break;
273    default:
274        assert(!"unreachable");
275        curve = nullptr;
276        break;
277    }
278
279    current_.curve.reset(curve);
280}
281
282Simulation::Simulation(int object_count)
283    : random_dev_()
284{
285    MeshPicker mesh;
286    ColorPicker color(random_dev_());
287
288    objects_.reserve(object_count);
289    for (int i = 0; i < object_count; i++) {
290        Meshes::Type type = mesh.pick();
291        float scale = mesh.scale(type);
292
293        objects_.emplace_back(Object{
294            type, glm::vec3(0.5f + 0.5f * (float)i / object_count),
295            color.pick(), Animation(random_dev_(), scale), Path(random_dev_()),
296        });
297    }
298}
299
300void Simulation::set_frame_data_size(uint32_t size)
301{
302    uint32_t offset = 0;
303    for (auto &obj : objects_) {
304        obj.frame_data_offset = offset;
305        offset += size;
306    }
307}
308
309void Simulation::update(float time, int begin, int end)
310{
311    for (int i = begin; i < end; i++) {
312        auto &obj = objects_[i];
313
314        glm::vec3 pos = obj.path.position(time);
315        glm::mat4 trans = obj.animation.transformation(time);
316        obj.model = glm::translate(glm::mat4(1.0f), pos) * trans;
317    }
318}
319