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
23 namespace {
24
25 class MeshPicker {
26 public:
MeshPicker()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
pick()43 Meshes::Type pick()
44 {
45 cur_ = (cur_ + 1) % pattern_.size();
46 return pattern_[cur_];
47 }
48
scale(Meshes::Type type) const49 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
64 private:
65 const std::array<Meshes::Type, 10> pattern_;
66 int cur_;
67 };
68
69 class ColorPicker {
70 public:
ColorPicker(unsigned int rng_seed)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
pick()79 glm::vec3 pick()
80 {
81 return glm::vec3{ red_(rng_),
82 green_(rng_),
83 blue_(rng_) };
84 }
85
86 private:
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
Animation(unsigned int rng_seed,float scale)95 Animation::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
transformation(float t)112 glm::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
119 class Curve {
120 public:
~Curve()121 virtual ~Curve() {}
122 virtual glm::vec3 evaluate(float t) = 0;
123 };
124
125 namespace {
126
127 enum CurveType {
128 CURVE_RANDOM,
129 CURVE_CIRCLE,
130 CURVE_COUNT,
131 };
132
133 class RandomCurve : public Curve {
134 public:
RandomCurve(unsigned int rng_seed)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
evaluate(float t)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
153 private:
new_segment(float time_start)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
183 class CircleCurve : public Curve {
184 public:
CircleCurve(float radius,glm::vec3 axis)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
evaluate(float t)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
214 private:
215 float r_;
216 glm::vec3 a_;
217 glm::vec3 b_;
218 };
219
220 } // namespace
221
Path(unsigned int rng_seed)222 Path::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
position(float t)230 glm::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
generate_subpath()240 void 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
Simulation(int object_count)282 Simulation::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
set_frame_data_size(uint32_t size)300 void 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
update(float time,int begin,int end)309 void 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