blob: ff3ea57a93ea01003b55ef8ea3acbdfc273f1c75 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "garnet/examples/escher/waterfall/scenes/paper_demo_scene1.h"
#include "lib/escher/geometry/clip_planes.h"
#include "lib/escher/geometry/plane_ops.h"
#include "lib/escher/geometry/tessellation.h"
#include "lib/escher/geometry/types.h"
#include "lib/escher/material/material.h"
#include "lib/escher/math/lerp.h"
#include "lib/escher/paper/paper_scene.h"
#include "lib/escher/renderer/batch_gpu_uploader.h"
#include "lib/escher/scene/model.h"
#include "lib/escher/scene/stage.h"
#include "lib/escher/shape/modifier_wobble.h"
#include "lib/escher/util/alloca.h"
#include "lib/escher/util/stopwatch.h"
#include "lib/escher/vk/image.h"
#include "lib/escher/vk/texture.h"
#include "lib/escher/vk/vulkan_context.h"
using escher::MeshAttribute;
using escher::MeshSpec;
using escher::Object;
using escher::RoundedRectSpec;
using escher::ShapeModifier;
using escher::plane3;
using escher::vec2;
using escher::vec3;
PaperDemoScene1::PaperDemoScene1(Demo* demo) : Scene(demo) {}
void PaperDemoScene1::Init(escher::Stage* stage) {
FXL_DCHECK(false) << "Use PaperScene* version instead.";
}
escher::Model* PaperDemoScene1::Update(const escher::Stopwatch& stopwatch,
uint64_t frame_count,
escher::Stage* stage,
escher::PaperRenderer2* renderer) {
FXL_DCHECK(false) << "Use PaperScene* version instead.";
return nullptr;
}
void PaperDemoScene1::Init(escher::PaperScene* scene) {
red_ = fxl::MakeRefCounted<escher::Material>();
bg_ = fxl::MakeRefCounted<escher::Material>();
color1_ = fxl::MakeRefCounted<escher::Material>();
color2_ = fxl::MakeRefCounted<escher::Material>();
red_->set_color(vec3(0.98f, 0.15f, 0.15f));
bg_->set_color(vec3(0.8f, 0.8f, 0.8f));
color1_->set_color(vec3(63.f / 255.f, 138.f / 255.f, 153.f / 255.f));
color2_->set_color(vec3(143.f / 255.f, 143.f / 255.f, 143.f / 255.f));
// Generate animated rounded rectangles. Both their position and shape are
// animated.
for (int i = 0; i < 10; ++i) {
const float x = 20.f * i;
const float y = 400.f + 80.f * i;
const float z = 187.5 - 20.f * i;
const float big_radius = 75.f;
const float tiny_radius = 25.f;
rectangles_.push_back(RectState{
.animation = {.cycle_duration = 5.f + i,
.cycle_count_before_pause = 3,
.inter_cycle_pause_duration = 5 - 0.4f * i},
.material = (i % 2) ? color1_ : red_,
.pos1 = vec3(400 - x, y, z),
.pos2 = vec3(1800 + x, y, z),
.spec1 = {350, 250, big_radius, tiny_radius, big_radius, tiny_radius},
.spec2 = {120, 450, tiny_radius, big_radius, tiny_radius, big_radius},
});
}
// Generate animated clip-planes to clip the above rounded-rectangles.
object_space_clip_planes_.push_back(ClipPlaneState{
.animation = {.cycle_duration = 9.f,
.cycle_count_before_pause = 2,
.inter_cycle_pause_duration = 5},
.pos1 = vec2(-200, -100),
.pos2 = vec2(200, 200),
.radians1 = -M_PI / 6,
.radians2 = M_PI * 7 / 6,
});
world_space_clip_planes_.push_back(ClipPlaneState{
.animation = {.cycle_duration = 4.f,
.cycle_count_before_pause = 2,
.inter_cycle_pause_duration = 5},
.pos1 = vec2(0, 0.9f * scene->bounding_box.height()),
.pos2 = vec2(0, 0.15f * scene->bounding_box.height()),
.radians1 = M_PI * 1.5f,
.radians2 = M_PI * 1.5f,
});
}
PaperDemoScene1::~PaperDemoScene1() {}
void PaperDemoScene1::Update(const escher::Stopwatch& stopwatch,
uint64_t frame_count, escher::PaperScene* scene,
escher::PaperRenderer2* renderer) {
const float current_time_sec = stopwatch.GetElapsedSeconds();
const float screen_width = scene->bounding_box.width();
const float screen_height = scene->bounding_box.height();
escher::PaperTransformStack* transform_stack = renderer->transform_stack();
// Create our background plane. Don't waste GPU cycles casting shadows from
// it, because there is nothing beneath it.
transform_stack->PushElevation(10);
// Rounded rectangles are centered around their origin.
transform_stack->PushTranslation(0.5f * vec2(screen_width, screen_height));
constexpr float kCornerRadius = 30.f;
renderer->DrawRoundedRect(
{screen_width, screen_height, kCornerRadius, kCornerRadius, kCornerRadius,
kCornerRadius},
bg_, escher::PaperDrawableFlagBits::kDisableShadowCasting);
transform_stack->Pop(); // pop translation only
// Render clipped rounded rectangles obtained from PaperShapeCache.
{
const size_t num_world_space_planes = world_space_clip_planes_.size();
const size_t num_object_space_planes = object_space_clip_planes_.size();
// Allocate enough space for all clip-planes, including additional
// scratch-space for world-space clip-planes, which must be transformed for
// each object.
plane3* object_space_planes =
ESCHER_ALLOCA(plane3, num_object_space_planes + num_world_space_planes);
plane3* world_space_planes = object_space_planes + num_object_space_planes;
// Animate the clip-planes.
for (size_t i = 0; i < num_object_space_planes; ++i) {
object_space_planes[i] =
object_space_clip_planes_[i].Update(current_time_sec);
}
for (size_t i = 0; i < num_world_space_planes; ++i) {
world_space_planes[i] =
world_space_clip_planes_[i].Update(current_time_sec);
}
transform_stack->AddClipPlanes(world_space_planes, num_world_space_planes);
// Animate and render the clipped rounded-rectangles.
for (auto& rect : rectangles_) {
const float t = rect.animation.Update(current_time_sec);
const vec3 position = escher::Lerp(rect.pos1, rect.pos2, t);
const RoundedRectSpec rect_spec = escher::Lerp(rect.spec1, rect.spec2, t);
transform_stack->PushTranslation(position);
transform_stack->AddClipPlanes(object_space_planes,
num_object_space_planes);
renderer->DrawRoundedRect(rect_spec, rect.material);
transform_stack->Pop();
}
transform_stack->Pop();
}
// Animated stack of circles, and a clip plane.
const vec3 kInitialCenterOfStack(100, 100, 0);
transform_stack->PushTranslation(kInitialCenterOfStack);
plane3 circle_stack_clip_plane(vec3(0, 0, 0), glm::normalize(vec3(1, 1, 0)));
transform_stack->AddClipPlanes(&circle_stack_clip_plane, 1);
transform_stack->PushTranslation(
vec3(70.f + 70.f * sin(current_time_sec * 1.5), 0, 0));
transform_stack->PushElevation(35);
renderer->DrawCircle(90, red_);
transform_stack->Pop().PushElevation(45);
renderer->DrawCircle(80, color2_);
transform_stack->Pop().PushElevation(55);
renderer->DrawCircle(70, color1_);
transform_stack->Pop().PushElevation(65);
renderer->DrawCircle(60, red_);
transform_stack->Pop().PushElevation(75);
renderer->DrawCircle(50, color2_);
transform_stack->Pop().PushElevation(85);
renderer->DrawCircle(40, color1_);
transform_stack->Pop();
transform_stack->Pop();
}
float PaperDemoScene1::AnimatedState::Update(float current_time_sec) {
float t = 0.f;
const float time_in_state = current_time_sec - state_start_time;
if (paused) {
// Paused, see if it is time to resume action.
if (time_in_state > inter_cycle_pause_duration) {
paused = false;
state_start_time = current_time_sec;
}
} else if (time_in_state > cycle_duration * cycle_count_before_pause) {
// Was running, now paused.
paused = true;
state_start_time = current_time_sec;
} else {
t = 0.5f - 0.5f * cos(time_in_state / cycle_duration * 2.f * M_PI);
}
return t;
}
escher::plane3 PaperDemoScene1::ClipPlaneState::Update(float current_time_sec) {
const float t = animation.Update(current_time_sec);
const vec2 pos = escher::Lerp(pos1, pos2, t);
const float radians = escher::Lerp(radians1, radians2, t);
const vec2 dir(cos(radians), sin(radians));
return plane3(vec3(pos, 0), vec3(dir, 0));
}