Index: examples/ui/spinning_cube/spinning_cube_view.cc |
diff --git a/examples/ui/spinning_cube/spinning_cube_view.cc b/examples/ui/spinning_cube/spinning_cube_view.cc |
index 8c3ce7a3b55bdec0fbb1771efecc6dc41393bcbc..68fb4f637995d3b5873efc29fcf5ee7cc758ad4b 100644 |
--- a/examples/ui/spinning_cube/spinning_cube_view.cc |
+++ b/examples/ui/spinning_cube/spinning_cube_view.cc |
@@ -2,161 +2,191 @@ |
// Use of this source code is governed by a BSD-style license that can be |
// found in the LICENSE file. |
+#include "examples/ui/spinning_cube/spinning_cube_view.h" |
+ |
+#ifndef GL_GLEXT_PROTOTYPES |
+#define GL_GLEXT_PROTOTYPES |
+#endif |
+ |
#include <GLES2/gl2.h> |
#include <GLES2/gl2extmojo.h> |
-#include <MGL/mgl.h> |
-#include "base/message_loop/message_loop.h" |
-#include "examples/ui/spinning_cube/spinning_cube_view.h" |
+#include <cmath> |
-namespace examples { |
+#include "base/bind.h" |
+#include "mojo/services/geometry/cpp/geometry_util.h" |
-SpinningCubeView::SpinningCubeView( |
- mojo::ApplicationImpl* app, |
- const mojo::ui::ViewProvider::CreateViewCallback& callback) |
- : callback_(callback), |
- binding_(this), |
- context_owner_(app->shell()), |
- texture_cache_(context_owner_.context(), &resource_returner_), |
- surface_id_namespace_(0), |
- draw_scheduled_(false), |
- weak_ptr_factory_(this) { |
- app->ConnectToService("mojo:surfaces_service", &surfaces_); |
- app->ConnectToService("mojo:view_manager_service", &view_manager_); |
+namespace examples { |
- surfaces_->SetResourceReturner(resource_returner_.Pass()); |
- surfaces_->GetIdNamespace( |
- base::Bind(&SpinningCubeView::OnSurfaceIdNamespaceAvailable, |
- base::Unretained(this))); |
+namespace { |
+constexpr uint32_t kCubeImageResourceId = 1; |
+constexpr uint32_t kRootNodeId = mojo::gfx::composition::kSceneRootNodeId; |
- InitCube(); |
+// TODO(johngro) : investigate extending mojom with a formal flags type which it |
+// generates good bindings for, so we don't need to resort to this. |
+constexpr bool operator&(const mojo::EventFlags& f1, |
+ const mojo::EventFlags& f2) { |
+ return ((static_cast<uint32_t>(f1) & static_cast<uint32_t>(f2)) != 0); |
} |
-SpinningCubeView::~SpinningCubeView() {} |
+float CalculateDragDistance(const mojo::PointF& start, |
+ const mojo::PointF& end) { |
+ return std::hypot(start.x - end.x, start.y - end.y); |
+} |
-void SpinningCubeView::OnSurfaceIdNamespaceAvailable(uint32_t id_namespace) { |
- surface_id_namespace_ = id_namespace; |
- InitView(); |
+float GetRandomColor() { |
+ return static_cast<float>(rand()) / static_cast<float>(RAND_MAX); |
} |
-void SpinningCubeView::InitView() { |
- mojo::ui::ViewPtr view; |
- binding_.Bind(mojo::GetProxy(&view)); |
- view_manager_->RegisterView(view.Pass(), mojo::GetProxy(&view_host_), |
- callback_); |
+// Return a direction multiplier to apply to drag distances: |
+// 1 for natural (positive) motion, -1 for reverse (negative) motion |
+int GetEventDirection(const mojo::PointF& current, |
+ const mojo::PointF& initial, |
+ const mojo::PointF& last) { |
+ // Axis of motion is determined by coarse alignment of overall movement |
+ bool use_x = |
+ std::abs(current.y - initial.y) < std::abs(current.x - initial.x); |
+ // Current direction is determined by comparison with previous point |
+ float delta = use_x ? (current.x - last.x) : (current.y - last.y); |
+ return delta > 0 ? -1 : 1; |
+} |
+} // namespace |
- view_host_->GetServiceProvider(mojo::GetProxy(&view_service_provider_)); |
+SpinningCubeView::SpinningCubeView( |
+ mojo::ApplicationImpl* app_impl, |
+ const mojo::ui::ViewProvider::CreateViewCallback& create_view_callback) |
+ : GLView(app_impl, "SpinningCube", create_view_callback), |
+ choreographer_(scene(), this), |
+ input_handler_(view_service_provider(), this), |
+ weak_ptr_factory_(this) { |
+ gl_renderer()->gl_context()->MakeCurrent(); |
+ cube_.Init(); |
} |
+SpinningCubeView::~SpinningCubeView() {} |
+ |
void SpinningCubeView::OnLayout(mojo::ui::ViewLayoutParamsPtr layout_params, |
mojo::Array<uint32_t> children_needing_layout, |
const OnLayoutCallback& callback) { |
- // Create a new surface the first time or if the size has changed. |
- mojo::Size new_size; |
- new_size.width = layout_params->constraints->max_width; |
- new_size.height = layout_params->constraints->max_height; |
- if (!surface_id_ || !size_.Equals(new_size)) { |
- if (!surface_id_) { |
- surface_id_ = mojo::SurfaceId::New(); |
- surface_id_->id_namespace = surface_id_namespace_; |
- } else { |
- surfaces_->DestroySurface(surface_id_->local); |
- } |
- surface_id_->local++; |
- size_ = new_size; |
- surfaces_->CreateSurface(surface_id_->local); |
- } |
+ size_.width = layout_params->constraints->max_width; |
+ size_.height = layout_params->constraints->max_height; |
// Submit the new layout information. |
- auto info = mojo::ui::ViewLayoutInfo::New(); |
+ auto info = mojo::ui::ViewLayoutResult::New(); |
info->size = size_.Clone(); |
- info->surface_id = surface_id_->Clone(); |
callback.Run(info.Pass()); |
// Draw! |
- ScheduleDraw(); |
+ choreographer_.ScheduleDraw(); |
} |
-void SpinningCubeView::OnChildUnavailable( |
- uint32_t child_key, |
- const OnChildUnavailableCallback& callback) { |
- callback.Run(); |
-} |
- |
-void SpinningCubeView::InitCube() { |
- context_owner_.context()->MakeCurrent(); |
- cube_.Init(); |
- last_draw_ = mojo::GetTimeTicksNow(); |
-} |
- |
-void SpinningCubeView::DrawCube() { |
- draw_scheduled_ = false; |
- |
- scoped_ptr<mojo::TextureCache::TextureInfo> texture_info = |
- texture_cache_.GetTexture(size_); |
- if (!texture_info) { |
- LOG(ERROR) << "Could not allocate texture of size " << size_.width << "x" |
- << size_.height; |
+void SpinningCubeView::OnEvent(mojo::EventPtr event, |
+ const OnEventCallback& callback) { |
+ if (!event->pointer_data) { |
+ callback.Run(false); |
return; |
} |
- context_owner_.context()->MakeCurrent(); |
- scoped_ptr<mojo::GLTexture> texture = texture_info->TakeTexture(); |
- |
- GLuint fbo = 0u; |
- glGenFramebuffers(1, &fbo); |
- glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
- GLuint depth_buffer = 0u; |
- glGenRenderbuffers(1, &depth_buffer); |
- glBindRenderbuffer(GL_RENDERBUFFER, depth_buffer); |
- glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, size_.width, |
- size_.height); |
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, |
- texture->texture_id(), 0); |
- glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, |
- GL_RENDERBUFFER, depth_buffer); |
- DCHECK_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE), |
- glCheckFramebufferStatus(GL_FRAMEBUFFER)); |
- glClearColor(1, 0, 0, 0.5); |
- |
- cube_.set_size(size_.width, size_.height); |
- |
- MojoTimeTicks now = mojo::GetTimeTicksNow(); |
- MojoTimeTicks offset = now - last_draw_; |
- cube_.UpdateForTimeDelta(offset * 0.000001f); |
- last_draw_ = now; |
- |
- cube_.Draw(); |
+ switch (event->action) { |
+ case mojo::EventType::POINTER_DOWN: |
+ if (event->flags & mojo::EventFlags::RIGHT_MOUSE_BUTTON) |
+ break; |
+ capture_point_.x = event->pointer_data->x; |
+ capture_point_.y = event->pointer_data->y; |
+ last_drag_point_ = capture_point_; |
+ drag_start_time_ = mojo::GetTimeTicksNow(); |
+ cube_.SetFlingMultiplier(0.0f, 1.0f); |
+ break; |
+ |
+ case mojo::EventType::POINTER_MOVE: { |
+ if (!(event->flags & mojo::EventFlags::LEFT_MOUSE_BUTTON) && |
+ event->pointer_data->kind == mojo::PointerKind::MOUSE) { |
+ break; |
+ } |
+ mojo::PointF event_location; |
+ event_location.x = event->pointer_data->x; |
+ event_location.y = event->pointer_data->y; |
+ int direction = |
+ GetEventDirection(event_location, capture_point_, last_drag_point_); |
+ cube_.UpdateForDragDistance( |
+ direction * CalculateDragDistance(last_drag_point_, event_location)); |
+ last_drag_point_ = event_location; |
+ break; |
+ } |
- glDeleteFramebuffers(1, &fbo); |
- glDeleteRenderbuffers(1, &depth_buffer); |
+ case mojo::EventType::POINTER_UP: { |
+ if (event->flags & mojo::EventFlags::RIGHT_MOUSE_BUTTON) { |
+ cube_.set_color(GetRandomColor(), GetRandomColor(), GetRandomColor()); |
+ break; |
+ } |
+ mojo::PointF event_location; |
+ event_location.x = event->pointer_data->x; |
+ event_location.y = event->pointer_data->y; |
+ MojoTimeTicks offset = mojo::GetTimeTicksNow() - drag_start_time_; |
+ float delta = static_cast<float>(offset) / 1000000.f; |
+ // Last drag point is the same as current point here; use initial capture |
+ // point instead |
+ int direction = |
+ GetEventDirection(event_location, capture_point_, capture_point_); |
+ cube_.SetFlingMultiplier( |
+ direction * CalculateDragDistance(capture_point_, event_location), |
+ delta); |
+ capture_point_ = last_drag_point_ = mojo::PointF(); |
+ break; |
+ } |
- mojo::FramePtr frame = mojo::TextureUploader::GetUploadFrame( |
- context_owner_.context(), texture_info->resource_id(), texture); |
- surfaces_->SubmitFrame(surface_id_->local, frame.Pass(), |
- base::Bind(&SpinningCubeView::OnSurfaceSubmitted, |
- base::Unretained(this))); |
+ default: |
+ break; |
+ } |
- texture_cache_.NotifyPendingResourceReturn(texture_info->resource_id(), |
- texture.Pass()); |
+ callback.Run(true); |
} |
-void SpinningCubeView::OnSurfaceSubmitted() { |
- ScheduleDraw(); |
+void SpinningCubeView::OnDraw( |
+ const mojo::gfx::composition::FrameInfo& frame_info, |
+ const base::TimeDelta& time_delta) { |
+ // Update the state of the cube. |
+ cube_.UpdateForTimeDelta(time_delta.InSecondsF()); |
+ |
+ // Update the contents of the scene. |
+ mojo::Rect bounds; |
+ bounds.width = size_.width; |
+ bounds.height = size_.height; |
+ |
+ auto update = mojo::gfx::composition::SceneUpdate::New(); |
+ mojo::gfx::composition::ResourcePtr cube_resource = gl_renderer()->DrawGL( |
+ size_, true, |
+ base::Bind(&SpinningCubeView::DrawCubeWithGL, base::Unretained(this))); |
+ DCHECK(cube_resource); |
+ update->resources.insert(kCubeImageResourceId, cube_resource.Pass()); |
+ |
+ auto root_node = mojo::gfx::composition::Node::New(); |
+ root_node->content_transform = mojo::Transform::New(); |
+ mojo::SetIdentityTransform(root_node->content_transform.get()); |
+ // TODO(jeffbrown): Figure out why spinning cube is drawing upside down. |
+ // Other GL based programs don't seem to have this problem. |
+ root_node->content_transform->matrix[5] = -1; // flip image vertically |
+ root_node->content_transform->matrix[7] = size_.height; |
+ root_node->op = mojo::gfx::composition::NodeOp::New(); |
+ root_node->op->set_image(mojo::gfx::composition::ImageNodeOp::New()); |
+ root_node->op->get_image()->content_rect = bounds.Clone(); |
+ root_node->op->get_image()->image_resource_id = kCubeImageResourceId; |
+ update->nodes.insert(kRootNodeId, root_node.Pass()); |
+ |
+ auto metadata = mojo::gfx::composition::SceneMetadata::New(); |
+ metadata->presentation_time = frame_info.presentation_time; |
+ |
+ // Publish the scene. |
+ scene()->Update(update.Pass()); |
+ scene()->Publish(metadata.Pass()); |
+ |
+ // Loop! |
+ choreographer_.ScheduleDraw(); |
} |
-void SpinningCubeView::ScheduleDraw() { |
- if (!draw_scheduled_) { |
- draw_scheduled_ = true; |
- |
- // TODO(jeffbrown): For now, we need to throttle this down because |
- // drawing as fast as we can appears to cause starvation of the |
- // Mojo message loop and makes X11 unhappy. |
- base::MessageLoop::current()->PostDelayedTask( |
- FROM_HERE, |
- base::Bind(&SpinningCubeView::DrawCube, weak_ptr_factory_.GetWeakPtr()), |
- base::TimeDelta::FromMilliseconds(30)); |
- } |
+void SpinningCubeView::DrawCubeWithGL() { |
+ cube_.set_size(size_.width, size_.height); |
+ cube_.Draw(); |
} |
} // namespace examples |