blob: c219647e3cea41cefdb42c03d7163cb44fd84d4e [file] [log] [blame]
Tony Barbour2f18b292016-02-25 15:44:10 -07001/*
2 * Copyright (C) 2016 Google, Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included
12 * in all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 * DEALINGS IN THE SOFTWARE.
21 */
22
23#include <cassert>
24#include <cmath>
25#include <array>
26#include <glm/gtc/matrix_transform.hpp>
27#include "Simulation.h"
28
29namespace {
30
31class MeshPicker {
32public:
33 MeshPicker() :
34 pattern_({
35 Meshes::MESH_PYRAMID,
36 Meshes::MESH_ICOSPHERE,
37 Meshes::MESH_TEAPOT,
38 Meshes::MESH_PYRAMID,
39 Meshes::MESH_ICOSPHERE,
40 Meshes::MESH_PYRAMID,
41 Meshes::MESH_PYRAMID,
42 Meshes::MESH_PYRAMID,
43 Meshes::MESH_PYRAMID,
44 Meshes::MESH_PYRAMID,
45 }), cur_(-1)
46 {
47 }
48
49 Meshes::Type pick()
50 {
51 cur_ = (cur_ + 1) % pattern_.size();
52 return pattern_[cur_];
53 }
54
55 float scale(Meshes::Type type) const
56 {
57 float base = 0.005f;
58
59 switch (type) {
60 case Meshes::MESH_PYRAMID:
61 default:
62 return base * 1.0f;
63 case Meshes::MESH_ICOSPHERE:
64 return base * 3.0f;
65 case Meshes::MESH_TEAPOT:
66 return base * 10.0f;
67 }
68 }
69
70private:
71 const std::array<Meshes::Type, 10> pattern_;
72 int cur_;
73};
74
75class ColorPicker {
76public:
77 ColorPicker(unsigned int rng_seed) :
78 rng_(rng_seed),
79 red_(0.0f, 1.0f),
80 green_(0.0f, 1.0f),
81 blue_(0.0f, 1.0f)
82 {
83 }
84
85 glm::vec3 pick()
86 {
87 return glm::vec3{ red_(rng_),
88 green_(rng_),
89 blue_(rng_) };
90 }
91
92private:
93 std::mt19937 rng_;
94 std::uniform_real_distribution<float> red_;
95 std::uniform_real_distribution<float> green_;
96 std::uniform_real_distribution<float> blue_;
97};
98
99} // namespace
100
101Animation::Animation(unsigned int rng_seed, float scale)
102 : rng_(rng_seed), dir_(-1.0f, 1.0f), speed_(0.1f, 1.0f)
103{
104 float x = dir_(rng_);
105 float y = dir_(rng_);
106 float z = dir_(rng_);
107 if (std::abs(x) + std::abs(y) + std::abs(z) == 0.0f)
108 x = 1.0f;
109
110 current_.axis = glm::normalize(glm::vec3(x, y, z));
111
112 current_.speed = speed_(rng_);
113 current_.scale = scale;
114
115 current_.matrix = glm::scale(glm::mat4(1.0f), glm::vec3(current_.scale));
116}
117
118glm::mat4 Animation::transformation(float t)
119{
120 current_.matrix = glm::rotate(current_.matrix, current_.speed * t, current_.axis);
121
122 return current_.matrix;
123}
124
125class Curve {
126public:
127 virtual ~Curve() {}
128 virtual glm::vec3 evaluate(float t) = 0;
129};
130
131namespace {
132
133enum CurveType {
134 CURVE_RANDOM,
135 CURVE_CIRCLE,
136 CURVE_COUNT,
137};
138
139class RandomCurve : public Curve {
140public:
141 RandomCurve(unsigned int rng_seed)
142 : rng_(rng_seed), direction_(-0.3f, 0.3f), duration_(1.0f, 5.0f),
143 segment_start_(0.0f), segment_direction_(0.0f),
144 time_start_(0.0f), time_duration_(0.0f)
145 {
146 }
147
148 glm::vec3 evaluate(float t)
149 {
150 if (t >= time_start_ + time_duration_)
151 new_segment(t);
152
153 pos_ += unit_dir_ * (t - last_);
154 last_ = t;
155
156 return pos_;
157 }
158
159private:
160 void new_segment(float time_start)
161 {
162 segment_start_ += segment_direction_;
163 segment_direction_ = glm::vec3(direction_(rng_),
164 direction_(rng_),
165 direction_(rng_));
166
167 time_start_ = time_start;
168 time_duration_ = duration_(rng_);
169
170 unit_dir_ = segment_direction_ / time_duration_;
171 pos_ = segment_start_;
172 last_ = time_start_;
173 }
174
175 std::mt19937 rng_;
176 std::uniform_real_distribution<float> direction_;
177 std::uniform_real_distribution<float> duration_;
178
179 glm::vec3 segment_start_;
180 glm::vec3 segment_direction_;
181 float time_start_;
182 float time_duration_;
183
184 glm::vec3 unit_dir_;
185 glm::vec3 pos_;
186 float last_;
187};
188
189class CircleCurve : public Curve {
190public:
191 CircleCurve(float radius, glm::vec3 axis)
192 : r_(radius)
193 {
194 glm::vec3 a;
195
196 if (axis.x != 0.0f) {
197 a.x = -axis.z / axis.x;
198 a.y = 0.0f;
199 a.z = 1.0f;
200 } else if (axis.y != 0.0f) {
201 a.x = 1.0f;
202 a.y = -axis.x / axis.y;
203 a.z = 0.0f;
204 } else {
205 a.x = 1.0f;
206 a.y = 0.0f;
207 a.z = -axis.x / axis.z;
208 }
209
210 a_ = glm::normalize(a);
211 b_ = glm::normalize(glm::cross(a_, axis));
212 }
213
214 glm::vec3 evaluate(float t)
215 {
216 return (a_ * (glm::vec3(std::cos(t)) - glm::vec3(1.0f)) + b_ * glm::vec3(std::sin(t))) *
217 glm::vec3(r_);
218 }
219
220private:
221 float r_;
222 glm::vec3 a_;
223 glm::vec3 b_;
224};
225
226} // namespace
227
228Path::Path(unsigned int rng_seed)
229 : rng_(rng_seed), type_(0, CURVE_COUNT - 1), duration_(5.0f, 20.0f)
230{
231 // trigger a subpath generation
232 current_.end = -1.0f;
233 current_.now = 0.0f;
234}
235
236glm::vec3 Path::position(float t)
237{
238 current_.now += t;
239
240 while (current_.now >= current_.end)
241 generate_subpath();
242
243 return current_.origin + current_.curve->evaluate(current_.now - current_.start);
244}
245
246void Path::generate_subpath()
247{
248 float duration = duration_(rng_);
249 CurveType type = static_cast<CurveType>(type_(rng_));
250
251 if (current_.curve) {
252 current_.origin += current_.curve->evaluate(current_.end - current_.start);
253 current_.start = current_.end;
254 } else {
255 std::uniform_real_distribution<float> origin(0.0f, 2.0f);
256 current_.origin = glm::vec3(origin(rng_), origin(rng_), origin(rng_));
257 current_.start = current_.now;
258 }
259
260 current_.end = current_.start + duration;
261
262 Curve *curve;
263
264 switch (type) {
265 case CURVE_RANDOM:
266 curve = new RandomCurve(rng_());
267 break;
268 case CURVE_CIRCLE:
269 {
270 std::uniform_real_distribution<float> dir(-1.0f, 1.0f);
271 glm::vec3 axis(dir(rng_), dir(rng_), dir(rng_));
272 if (axis.x == 0.0f && axis.y == 0.0f && axis.z == 0.0f)
273 axis.x = 1.0f;
274
275 std::uniform_real_distribution<float> radius_(0.02f, 0.2f);
276 curve = new CircleCurve(radius_(rng_), axis);
277 }
278 break;
279 default:
280 assert(!"unreachable");
281 curve = nullptr;
282 break;
283 }
284
285 current_.curve.reset(curve);
286}
287
288Simulation::Simulation(int object_count)
289 : random_dev_()
290{
291 MeshPicker mesh;
292 ColorPicker color(random_dev_());
293
294 objects_.reserve(object_count);
295 for (int i = 0; i < object_count; i++) {
296 Meshes::Type type = mesh.pick();
297 float scale = mesh.scale(type);
298
299 objects_.emplace_back(Object{
Dustin Graves20f5fc02016-04-06 10:16:05 -0600300 type, glm::vec3(0.5f + 0.5f * (float)i / object_count),
301 color.pick(), Animation(random_dev_(), scale), Path(random_dev_()),
Tony Barbour2f18b292016-02-25 15:44:10 -0700302 });
303 }
304}
305
306void Simulation::set_frame_data_size(uint32_t size)
307{
308 uint32_t offset = 0;
309 for (auto &obj : objects_) {
310 obj.frame_data_offset = offset;
311 offset += size;
312 }
313}
314
315void Simulation::update(float time, int begin, int end)
316{
317 for (int i = begin; i < end; i++) {
318 auto &obj = objects_[i];
319
320 glm::vec3 pos = obj.path.position(time);
321 glm::mat4 trans = obj.animation.transformation(time);
322 obj.model = glm::translate(glm::mat4(1.0f), pos) * trans;
323 }
324}