318 lines
7.6 KiB
C++
318 lines
7.6 KiB
C++
/*
|
|
* Copyright (C) 2016 Google, Inc.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include <cassert>
|
|
#include <cmath>
|
|
#include <array>
|
|
#include <glm/gtc/matrix_transform.hpp>
|
|
#include "Simulation.h"
|
|
|
|
namespace {
|
|
|
|
class MeshPicker {
|
|
public:
|
|
MeshPicker() :
|
|
pattern_({
|
|
Meshes::MESH_PYRAMID,
|
|
Meshes::MESH_ICOSPHERE,
|
|
Meshes::MESH_TEAPOT,
|
|
Meshes::MESH_PYRAMID,
|
|
Meshes::MESH_ICOSPHERE,
|
|
Meshes::MESH_PYRAMID,
|
|
Meshes::MESH_PYRAMID,
|
|
Meshes::MESH_PYRAMID,
|
|
Meshes::MESH_PYRAMID,
|
|
Meshes::MESH_PYRAMID,
|
|
}), cur_(-1)
|
|
{
|
|
}
|
|
|
|
Meshes::Type pick()
|
|
{
|
|
cur_ = (cur_ + 1) % pattern_.size();
|
|
return pattern_[cur_];
|
|
}
|
|
|
|
float scale(Meshes::Type type) const
|
|
{
|
|
float base = 0.005f;
|
|
|
|
switch (type) {
|
|
case Meshes::MESH_PYRAMID:
|
|
default:
|
|
return base * 1.0f;
|
|
case Meshes::MESH_ICOSPHERE:
|
|
return base * 3.0f;
|
|
case Meshes::MESH_TEAPOT:
|
|
return base * 10.0f;
|
|
}
|
|
}
|
|
|
|
private:
|
|
const std::array<Meshes::Type, 10> pattern_;
|
|
int cur_;
|
|
};
|
|
|
|
class ColorPicker {
|
|
public:
|
|
ColorPicker(unsigned int rng_seed) :
|
|
rng_(rng_seed),
|
|
red_(0.0f, 1.0f),
|
|
green_(0.0f, 1.0f),
|
|
blue_(0.0f, 1.0f)
|
|
{
|
|
}
|
|
|
|
glm::vec3 pick()
|
|
{
|
|
return glm::vec3{ red_(rng_),
|
|
green_(rng_),
|
|
blue_(rng_) };
|
|
}
|
|
|
|
private:
|
|
std::mt19937 rng_;
|
|
std::uniform_real_distribution<float> red_;
|
|
std::uniform_real_distribution<float> green_;
|
|
std::uniform_real_distribution<float> blue_;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
Animation::Animation(unsigned int rng_seed, float scale)
|
|
: rng_(rng_seed), dir_(-1.0f, 1.0f), speed_(0.1f, 1.0f)
|
|
{
|
|
float x = dir_(rng_);
|
|
float y = dir_(rng_);
|
|
float z = dir_(rng_);
|
|
if (std::abs(x) + std::abs(y) + std::abs(z) == 0.0f)
|
|
x = 1.0f;
|
|
|
|
current_.axis = glm::normalize(glm::vec3(x, y, z));
|
|
|
|
current_.speed = speed_(rng_);
|
|
current_.scale = scale;
|
|
|
|
current_.matrix = glm::scale(glm::mat4(1.0f), glm::vec3(current_.scale));
|
|
}
|
|
|
|
glm::mat4 Animation::transformation(float t)
|
|
{
|
|
current_.matrix = glm::rotate(current_.matrix, current_.speed * t, current_.axis);
|
|
|
|
return current_.matrix;
|
|
}
|
|
|
|
class Curve {
|
|
public:
|
|
virtual ~Curve() {}
|
|
virtual glm::vec3 evaluate(float t) = 0;
|
|
};
|
|
|
|
namespace {
|
|
|
|
enum CurveType {
|
|
CURVE_RANDOM,
|
|
CURVE_CIRCLE,
|
|
CURVE_COUNT,
|
|
};
|
|
|
|
class RandomCurve : public Curve {
|
|
public:
|
|
RandomCurve(unsigned int rng_seed)
|
|
: rng_(rng_seed), direction_(-0.3f, 0.3f), duration_(1.0f, 5.0f),
|
|
segment_start_(0.0f), segment_direction_(0.0f),
|
|
time_start_(0.0f), time_duration_(0.0f)
|
|
{
|
|
}
|
|
|
|
glm::vec3 evaluate(float t)
|
|
{
|
|
if (t >= time_start_ + time_duration_)
|
|
new_segment(t);
|
|
|
|
pos_ += unit_dir_ * (t - last_);
|
|
last_ = t;
|
|
|
|
return pos_;
|
|
}
|
|
|
|
private:
|
|
void new_segment(float time_start)
|
|
{
|
|
segment_start_ += segment_direction_;
|
|
segment_direction_ = glm::vec3(direction_(rng_),
|
|
direction_(rng_),
|
|
direction_(rng_));
|
|
|
|
time_start_ = time_start;
|
|
time_duration_ = duration_(rng_);
|
|
|
|
unit_dir_ = segment_direction_ / time_duration_;
|
|
pos_ = segment_start_;
|
|
last_ = time_start_;
|
|
}
|
|
|
|
std::mt19937 rng_;
|
|
std::uniform_real_distribution<float> direction_;
|
|
std::uniform_real_distribution<float> duration_;
|
|
|
|
glm::vec3 segment_start_;
|
|
glm::vec3 segment_direction_;
|
|
float time_start_;
|
|
float time_duration_;
|
|
|
|
glm::vec3 unit_dir_;
|
|
glm::vec3 pos_;
|
|
float last_;
|
|
};
|
|
|
|
class CircleCurve : public Curve {
|
|
public:
|
|
CircleCurve(float radius, glm::vec3 axis)
|
|
: r_(radius)
|
|
{
|
|
glm::vec3 a;
|
|
|
|
if (axis.x != 0.0f) {
|
|
a.x = -axis.z / axis.x;
|
|
a.y = 0.0f;
|
|
a.z = 1.0f;
|
|
} else if (axis.y != 0.0f) {
|
|
a.x = 1.0f;
|
|
a.y = -axis.x / axis.y;
|
|
a.z = 0.0f;
|
|
} else {
|
|
a.x = 1.0f;
|
|
a.y = 0.0f;
|
|
a.z = -axis.x / axis.z;
|
|
}
|
|
|
|
a_ = glm::normalize(a);
|
|
b_ = glm::normalize(glm::cross(a_, axis));
|
|
}
|
|
|
|
glm::vec3 evaluate(float t)
|
|
{
|
|
return (a_ * (glm::vec3(std::cos(t)) - glm::vec3(1.0f)) + b_ * glm::vec3(std::sin(t))) *
|
|
glm::vec3(r_);
|
|
}
|
|
|
|
private:
|
|
float r_;
|
|
glm::vec3 a_;
|
|
glm::vec3 b_;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
Path::Path(unsigned int rng_seed)
|
|
: rng_(rng_seed), type_(0, CURVE_COUNT - 1), duration_(5.0f, 20.0f)
|
|
{
|
|
// trigger a subpath generation
|
|
current_.end = -1.0f;
|
|
current_.now = 0.0f;
|
|
}
|
|
|
|
glm::vec3 Path::position(float t)
|
|
{
|
|
current_.now += t;
|
|
|
|
while (current_.now >= current_.end)
|
|
generate_subpath();
|
|
|
|
return current_.origin + current_.curve->evaluate(current_.now - current_.start);
|
|
}
|
|
|
|
void Path::generate_subpath()
|
|
{
|
|
float duration = duration_(rng_);
|
|
CurveType type = static_cast<CurveType>(type_(rng_));
|
|
|
|
if (current_.curve) {
|
|
current_.origin += current_.curve->evaluate(current_.end - current_.start);
|
|
current_.start = current_.end;
|
|
} else {
|
|
std::uniform_real_distribution<float> origin(0.0f, 2.0f);
|
|
current_.origin = glm::vec3(origin(rng_), origin(rng_), origin(rng_));
|
|
current_.start = current_.now;
|
|
}
|
|
|
|
current_.end = current_.start + duration;
|
|
|
|
Curve *curve;
|
|
|
|
switch (type) {
|
|
case CURVE_RANDOM:
|
|
curve = new RandomCurve(rng_());
|
|
break;
|
|
case CURVE_CIRCLE:
|
|
{
|
|
std::uniform_real_distribution<float> dir(-1.0f, 1.0f);
|
|
glm::vec3 axis(dir(rng_), dir(rng_), dir(rng_));
|
|
if (axis.x == 0.0f && axis.y == 0.0f && axis.z == 0.0f)
|
|
axis.x = 1.0f;
|
|
|
|
std::uniform_real_distribution<float> radius_(0.02f, 0.2f);
|
|
curve = new CircleCurve(radius_(rng_), axis);
|
|
}
|
|
break;
|
|
default:
|
|
assert(!"unreachable");
|
|
curve = nullptr;
|
|
break;
|
|
}
|
|
|
|
current_.curve.reset(curve);
|
|
}
|
|
|
|
Simulation::Simulation(int object_count)
|
|
: random_dev_()
|
|
{
|
|
MeshPicker mesh;
|
|
ColorPicker color(random_dev_());
|
|
|
|
objects_.reserve(object_count);
|
|
for (int i = 0; i < object_count; i++) {
|
|
Meshes::Type type = mesh.pick();
|
|
float scale = mesh.scale(type);
|
|
|
|
objects_.emplace_back(Object{
|
|
type, glm::vec3(0.5f + 0.5f * (float)i / object_count),
|
|
color.pick(), Animation(random_dev_(), scale), Path(random_dev_()),
|
|
});
|
|
}
|
|
}
|
|
|
|
void Simulation::set_frame_data_size(uint32_t size)
|
|
{
|
|
uint32_t offset = 0;
|
|
for (auto &obj : objects_) {
|
|
obj.frame_data_offset = offset;
|
|
offset += size;
|
|
}
|
|
}
|
|
|
|
void Simulation::update(float time, int begin, int end)
|
|
{
|
|
for (int i = begin; i < end; i++) {
|
|
auto &obj = objects_[i];
|
|
|
|
glm::vec3 pos = obj.path.position(time);
|
|
glm::mat4 trans = obj.animation.transformation(time);
|
|
obj.model = glm::translate(glm::mat4(1.0f), pos) * trans;
|
|
}
|
|
}
|