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: 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 43 Meshes::Type pick() 44 { 45 cur_ = (cur_ + 1) % pattern_.size(); 46 return pattern_[cur_]; 47 } 48 49 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: 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 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 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 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: 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: 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 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: 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: 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 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 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 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 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 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 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 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