• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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