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: 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 70 private: 71 const std::array<Meshes::Type, 10> pattern_; 72 int cur_; 73 }; 74 75 class ColorPicker { 76 public: 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 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 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 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: 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: 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 159 private: 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: 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 220 private: 221 float r_; 222 glm::vec3 a_; 223 glm::vec3 b_; 224 }; 225 226 } // namespace 227 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 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 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 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 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 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