Home | History | Annotate | Download | only in smoke
      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