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, 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