1 /*
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
29 namespace {
30
31 class MeshPicker {
32 public:
MeshPicker()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
pick()49 Meshes::Type pick()
50 {
51 cur_ = (cur_ + 1) % pattern_.size();
52 return pattern_[cur_];
53 }
54
scale(Meshes::Type type) const55 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
70 private:
71 const std::array<Meshes::Type, 10> pattern_;
72 int cur_;
73 };
74
75 class ColorPicker {
76 public:
ColorPicker(unsigned int rng_seed)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
pick()85 glm::vec3 pick()
86 {
87 return glm::vec3{ red_(rng_),
88 green_(rng_),
89 blue_(rng_) };
90 }
91
92 private:
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
Animation(unsigned int rng_seed,float scale)101 Animation::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
transformation(float t)118 glm::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
125 class Curve {
126 public:
~Curve()127 virtual ~Curve() {}
128 virtual glm::vec3 evaluate(float t) = 0;
129 };
130
131 namespace {
132
133 enum CurveType {
134 CURVE_RANDOM,
135 CURVE_CIRCLE,
136 CURVE_COUNT,
137 };
138
139 class RandomCurve : public Curve {
140 public:
RandomCurve(unsigned int rng_seed)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
evaluate(float t)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
159 private:
new_segment(float time_start)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
189 class CircleCurve : public Curve {
190 public:
CircleCurve(float radius,glm::vec3 axis)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
evaluate(float t)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
220 private:
221 float r_;
222 glm::vec3 a_;
223 glm::vec3 b_;
224 };
225
226 } // namespace
227
Path(unsigned int rng_seed)228 Path::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
position(float t)236 glm::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
generate_subpath()246 void 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
Simulation(int object_count)288 Simulation::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{
300 type,
301 glm::vec3(0.5 + 0.5 * (float) i / object_count),
302 color.pick(),
303 Animation(random_dev_(), scale),
304 Path(random_dev_()),
305 });
306 }
307 }
308
set_frame_data_size(uint32_t size)309 void Simulation::set_frame_data_size(uint32_t size)
310 {
311 uint32_t offset = 0;
312 for (auto &obj : objects_) {
313 obj.frame_data_offset = offset;
314 offset += size;
315 }
316 }
317
update(float time,int begin,int end)318 void Simulation::update(float time, int begin, int end)
319 {
320 for (int i = begin; i < end; i++) {
321 auto &obj = objects_[i];
322
323 glm::vec3 pos = obj.path.position(time);
324 glm::mat4 trans = obj.animation.transformation(time);
325 obj.model = glm::translate(glm::mat4(1.0f), pos) * trans;
326 }
327 }
328