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, Meshes::MESH_ICOSPHERE, Meshes::MESH_TEAPOT, Meshes::MESH_PYRAMID, Meshes::MESH_ICOSPHERE, 30 Meshes::MESH_PYRAMID, Meshes::MESH_PYRAMID, Meshes::MESH_PYRAMID, Meshes::MESH_PYRAMID, Meshes::MESH_PYRAMID, 31 }}), 32 cur_(-1) {} 33 34 Meshes::Type pick() { 35 cur_ = (cur_ + 1) % pattern_.size(); 36 return pattern_[cur_]; 37 } 38 39 float scale(Meshes::Type type) const { 40 float base = 0.005f; 41 42 switch (type) { 43 case Meshes::MESH_PYRAMID: 44 default: 45 return base * 1.0f; 46 case Meshes::MESH_ICOSPHERE: 47 return base * 3.0f; 48 case Meshes::MESH_TEAPOT: 49 return base * 10.0f; 50 } 51 } 52 53 private: 54 const std::array<Meshes::Type, 10> pattern_; 55 int cur_; 56 }; 57 58 class ColorPicker { 59 public: 60 ColorPicker(unsigned int rng_seed) : rng_(rng_seed), red_(0.0f, 1.0f), green_(0.0f, 1.0f), blue_(0.0f, 1.0f) {} 61 62 glm::vec3 pick() { return glm::vec3{red_(rng_), green_(rng_), blue_(rng_)}; } 63 64 private: 65 std::mt19937 rng_; 66 std::uniform_real_distribution<float> red_; 67 std::uniform_real_distribution<float> green_; 68 std::uniform_real_distribution<float> blue_; 69 }; 70 71 } // namespace 72 73 Animation::Animation(unsigned int rng_seed, float scale) : rng_(rng_seed), dir_(-1.0f, 1.0f), speed_(0.1f, 1.0f) { 74 float x = dir_(rng_); 75 float y = dir_(rng_); 76 float z = dir_(rng_); 77 if (std::abs(x) + std::abs(y) + std::abs(z) == 0.0f) x = 1.0f; 78 79 current_.axis = glm::normalize(glm::vec3(x, y, z)); 80 81 current_.speed = speed_(rng_); 82 current_.scale = scale; 83 84 current_.matrix = glm::scale(glm::mat4(1.0f), glm::vec3(current_.scale)); 85 } 86 87 glm::mat4 Animation::transformation(float t) { 88 current_.matrix = glm::rotate(current_.matrix, current_.speed * t, current_.axis); 89 90 return current_.matrix; 91 } 92 93 class Curve { 94 public: 95 virtual ~Curve() {} 96 virtual glm::vec3 evaluate(float t) = 0; 97 }; 98 99 namespace { 100 101 enum CurveType { 102 CURVE_RANDOM, 103 CURVE_CIRCLE, 104 CURVE_COUNT, 105 }; 106 107 class RandomCurve : public Curve { 108 public: 109 RandomCurve(unsigned int rng_seed) 110 : rng_(rng_seed), 111 direction_(-0.3f, 0.3f), 112 duration_(1.0f, 5.0f), 113 segment_start_(0.0f), 114 segment_direction_(0.0f), 115 time_start_(0.0f), 116 time_duration_(0.0f) {} 117 118 glm::vec3 evaluate(float t) { 119 if (t >= time_start_ + time_duration_) new_segment(t); 120 121 pos_ += unit_dir_ * (t - last_); 122 last_ = t; 123 124 return pos_; 125 } 126 127 private: 128 void new_segment(float time_start) { 129 segment_start_ += segment_direction_; 130 segment_direction_ = glm::vec3(direction_(rng_), direction_(rng_), direction_(rng_)); 131 132 time_start_ = time_start; 133 time_duration_ = duration_(rng_); 134 135 unit_dir_ = segment_direction_ / time_duration_; 136 pos_ = segment_start_; 137 last_ = time_start_; 138 } 139 140 std::mt19937 rng_; 141 std::uniform_real_distribution<float> direction_; 142 std::uniform_real_distribution<float> duration_; 143 144 glm::vec3 segment_start_; 145 glm::vec3 segment_direction_; 146 float time_start_; 147 float time_duration_; 148 149 glm::vec3 unit_dir_; 150 glm::vec3 pos_; 151 float last_; 152 }; 153 154 class CircleCurve : public Curve { 155 public: 156 CircleCurve(float radius, glm::vec3 axis) : r_(radius) { 157 glm::vec3 a; 158 159 if (axis.x != 0.0f) { 160 a.x = -axis.z / axis.x; 161 a.y = 0.0f; 162 a.z = 1.0f; 163 } else if (axis.y != 0.0f) { 164 a.x = 1.0f; 165 a.y = -axis.x / axis.y; 166 a.z = 0.0f; 167 } else { 168 a.x = 1.0f; 169 a.y = 0.0f; 170 a.z = -axis.x / axis.z; 171 } 172 173 a_ = glm::normalize(a); 174 b_ = glm::normalize(glm::cross(a_, axis)); 175 } 176 177 glm::vec3 evaluate(float t) { 178 return (a_ * (glm::vec3(std::cos(t)) - glm::vec3(1.0f)) + b_ * glm::vec3(std::sin(t))) * glm::vec3(r_); 179 } 180 181 private: 182 float r_; 183 glm::vec3 a_; 184 glm::vec3 b_; 185 }; 186 187 } // namespace 188 189 Path::Path(unsigned int rng_seed) : rng_(rng_seed), type_(0, CURVE_COUNT - 1), duration_(5.0f, 20.0f) { 190 // trigger a subpath generation 191 current_.end = -1.0f; 192 current_.now = 0.0f; 193 } 194 195 glm::vec3 Path::position(float t) { 196 current_.now += t; 197 198 while (current_.now >= current_.end) generate_subpath(); 199 200 return current_.origin + current_.curve->evaluate(current_.now - current_.start); 201 } 202 203 void Path::generate_subpath() { 204 float duration = duration_(rng_); 205 CurveType type = static_cast<CurveType>(type_(rng_)); 206 207 if (current_.curve) { 208 current_.origin += current_.curve->evaluate(current_.end - current_.start); 209 current_.origin = glm::mod(current_.origin, glm::vec3(2.0f)); 210 current_.start = current_.end; 211 } else { 212 std::uniform_real_distribution<float> origin(0.0f, 2.0f); 213 current_.origin = glm::vec3(origin(rng_), origin(rng_), origin(rng_)); 214 current_.start = current_.now; 215 } 216 217 current_.end = current_.start + duration; 218 219 Curve *curve; 220 221 switch (type) { 222 case CURVE_RANDOM: 223 curve = new RandomCurve(rng_()); 224 break; 225 case CURVE_CIRCLE: { 226 std::uniform_real_distribution<float> dir(-1.0f, 1.0f); 227 glm::vec3 axis(dir(rng_), dir(rng_), dir(rng_)); 228 if (axis.x == 0.0f && axis.y == 0.0f && axis.z == 0.0f) axis.x = 1.0f; 229 230 std::uniform_real_distribution<float> radius_(0.02f, 0.2f); 231 curve = new CircleCurve(radius_(rng_), axis); 232 } break; 233 default: 234 assert(!"unreachable"); 235 curve = nullptr; 236 break; 237 } 238 239 current_.curve.reset(curve); 240 } 241 242 Simulation::Simulation(int object_count) : random_dev_() { 243 MeshPicker mesh; 244 ColorPicker color(random_dev_()); 245 246 objects_.reserve(object_count); 247 for (int i = 0; i < object_count; i++) { 248 Meshes::Type type = mesh.pick(); 249 float scale = mesh.scale(type); 250 251 objects_.emplace_back(Object{ 252 type, glm::vec3(0.5f + 0.5f * (float)i / object_count), color.pick(), Animation(random_dev_(), scale), 253 Path(random_dev_()), 254 }); 255 } 256 } 257 258 void Simulation::set_frame_data_size(uint32_t size) { 259 uint32_t offset = 0; 260 for (auto &obj : objects_) { 261 obj.frame_data_offset = offset; 262 offset += size; 263 } 264 } 265 266 void Simulation::update(float time, int begin, int end) { 267 for (int i = begin; i < end; i++) { 268 auto &obj = objects_[i]; 269 270 glm::vec3 pos = obj.path.position(time); 271 glm::mat4 trans = obj.animation.transformation(time); 272 obj.model = glm::translate(glm::mat4(1.0f), pos) * trans; 273 } 274 } 275