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