From a36ecb7065130aaf91aedc8a9959eb3b1e22b1e2 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 19 Mar 2017 14:28:24 -0700 Subject: [PATCH] UI: Track translate/scale/fade transform by screen. This translates drawing, scissor, and touch to the transform. --- UI/NativeApp.cpp | 7 +++-- ext/native/base/colorutil.cpp | 6 ++-- ext/native/gfx_es2/draw_buffer.cpp | 2 +- ext/native/gfx_es2/draw_buffer.h | 28 ++++++++++++++++- ext/native/ui/screen.cpp | 3 +- ext/native/ui/screen.h | 3 ++ ext/native/ui/ui_context.cpp | 49 +++++++++++++++++++++++++++--- ext/native/ui/ui_context.h | 13 ++++++++ ext/native/ui/ui_screen.cpp | 33 +++++++++++++++----- ext/native/ui/ui_screen.h | 5 +++ ext/native/ui/viewgroup.cpp | 16 +++++----- 11 files changed, 136 insertions(+), 29 deletions(-) diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 8aaa834fd..1d205a9fd 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -772,8 +772,8 @@ void NativeRender(GraphicsContext *graphicsContext) { ortho = ortho * g_display_rot_matrix; } - ui_draw2d.SetDrawMatrix(ortho); - ui_draw2d_front.SetDrawMatrix(ortho); + ui_draw2d.PushDrawMatrix(ortho); + ui_draw2d_front.PushDrawMatrix(ortho); screenManager->render(); if (screenManager->getUIContext()->Text()) { @@ -820,6 +820,9 @@ void NativeRender(GraphicsContext *graphicsContext) { } #endif } + + ui_draw2d.PopDrawMatrix(); + ui_draw2d_front.PopDrawMatrix(); } void HandleGlobalMessage(const std::string &msg, const std::string &value) { diff --git a/ext/native/base/colorutil.cpp b/ext/native/base/colorutil.cpp index ec9766296..927a87117 100644 --- a/ext/native/base/colorutil.cpp +++ b/ext/native/base/colorutil.cpp @@ -24,9 +24,9 @@ uint32_t alphaMul(uint32_t color, float alphaMul) { uint32_t rgb = color & 0xFFFFFF; uint32_t alpha = color >> 24; alpha *= alphaMul; - if (alpha < 0.0f) alpha = 0.0f; - if (alpha > 255.0f) alpha = 255.0f; - return ((int)(alpha)<<24) | (rgb & 0xFFFFFF); + if (alpha < 0) alpha = 0.0f; + if (alpha > 255) alpha = 255.0f; + return (alpha << 24) | (rgb & 0xFFFFFF); } uint32_t rgba(float r, float g, float b, float alpha) { diff --git a/ext/native/gfx_es2/draw_buffer.cpp b/ext/native/gfx_es2/draw_buffer.cpp index 594cf7eb5..f6de138cd 100644 --- a/ext/native/gfx_es2/draw_buffer.cpp +++ b/ext/native/gfx_es2/draw_buffer.cpp @@ -115,7 +115,7 @@ void DrawBuffer::V(float x, float y, float z, uint32_t color, float u, float v) vert->x = x; vert->y = y; vert->z = z; - vert->rgba = color; + vert->rgba = alpha_ == 1.0f ? color : alphaMul(color, alpha_); vert->u = u; vert->v = v; } diff --git a/ext/native/gfx_es2/draw_buffer.h b/ext/native/gfx_es2/draw_buffer.h index f55b0cf84..9232bf15e 100644 --- a/ext/native/gfx_es2/draw_buffer.h +++ b/ext/native/gfx_es2/draw_buffer.h @@ -2,6 +2,8 @@ // "Immediate mode"-lookalike buffered drawing. Very fast way to draw 2D. +#include + #include "base/basictypes.h" #include "base/colorutil.h" #include "gfx/gl_lost_manager.h" @@ -147,10 +149,30 @@ public: static void DoAlign(int flags, float *x, float *y, float *w, float *h); - void SetDrawMatrix(const Matrix4x4 &m) { + void PushDrawMatrix(const Matrix4x4 &m) { + drawMatrixStack_.push_back(drawMatrix_); drawMatrix_ = m; } + void PopDrawMatrix() { + drawMatrix_ = drawMatrixStack_.back(); + drawMatrixStack_.pop_back(); + } + + Matrix4x4 GetDrawMatrix() { + return drawMatrix_; + } + + void PushAlpha(float a) { + alphaStack_.push_back(alpha_); + alpha_ *= a; + } + + void PopAlpha() { + alpha_ = alphaStack_.back(); + alphaStack_.pop_back(); + } + private: struct Vertex { float x, y, z; @@ -159,6 +181,10 @@ private: }; Matrix4x4 drawMatrix_; + std::vector drawMatrixStack_; + + float alpha_ = 1.0f; + std::vector alphaStack_; Draw::DrawContext *draw_; Draw::Buffer *vbuf_; diff --git a/ext/native/ui/screen.cpp b/ext/native/ui/screen.cpp index e39e0214c..0f1622008 100644 --- a/ext/native/ui/screen.cpp +++ b/ext/native/ui/screen.cpp @@ -71,7 +71,8 @@ void ScreenManager::switchToNext() { bool ScreenManager::touch(const TouchInput &touch) { std::lock_guard guard(inputLock_); if (!stack_.empty()) { - return stack_.back().screen->touch(touch); + Screen *screen = stack_.back().screen; + return screen->touch(screen->transformTouch(touch)); } else { return false; } diff --git a/ext/native/ui/screen.h b/ext/native/ui/screen.h index 2ade58e17..df47be6d3 100644 --- a/ext/native/ui/screen.h +++ b/ext/native/ui/screen.h @@ -19,6 +19,7 @@ #include "base/basictypes.h" #include "base/logging.h" #include "base/NativeApp.h" +#include "input/input_state.h" namespace UI { class View; @@ -74,6 +75,8 @@ public: virtual bool isTransparent() const { return false; } virtual bool isTopLevel() const { return false; } + virtual TouchInput transformTouch(const TouchInput &touch) { return touch; } + private: ScreenManager *screenManager_; DISALLOW_COPY_AND_ASSIGN(Screen); diff --git a/ext/native/ui/ui_context.cpp b/ext/native/ui/ui_context.cpp index 861f5d780..f287c63f5 100644 --- a/ext/native/ui/ui_context.cpp +++ b/ext/native/ui/ui_context.cpp @@ -70,10 +70,10 @@ void UIContext::End() { Flush(); } -// TODO: Support transformed bounds using stencil +// TODO: Support transformed bounds using stencil instead. void UIContext::PushScissor(const Bounds &bounds) { Flush(); - Bounds clipped = bounds; + Bounds clipped = TransformBounds(bounds); if (scissorStack_.size()) clipped.Clip(scissorStack_.back()); scissorStack_.push_back(clipped); @@ -103,9 +103,7 @@ void UIContext::ActivateTopScissor() { int w = ceilf(scale * bounds.w); int h = ceilf(scale * bounds.h); draw_->SetScissorRect(x, y, w, h); - } - else { - bounds = bounds_; + } else { // Avoid rounding errors draw_->SetScissorRect(0, 0, pixel_xres, pixel_yres); } @@ -203,3 +201,44 @@ void UIContext::FillRect(const UI::Drawable &drawable, const Bounds &bounds) { break; } } + +void UIContext::PushTransform(const UITransform &transform) { + Flush(); + + Matrix4x4 m = Draw()->GetDrawMatrix(); + const Vec3 &t = transform.translate; + Vec3 scaledTranslate = Vec3( + t.x * m.xx + t.y * m.xy + t.z * m.xz + m.xw, + t.x * m.yx + t.y * m.yy + t.z * m.yz + m.yw, + t.x * m.zx + t.y * m.zy + t.z * m.zz + m.zw); + + m.translateAndScale(scaledTranslate, transform.scale); + Draw()->PushDrawMatrix(m); + Draw()->PushAlpha(transform.alpha); + + transformStack_.push_back(transform); +} + +void UIContext::PopTransform() { + Flush(); + + transformStack_.pop_back(); + + Draw()->PopDrawMatrix(); + Draw()->PopAlpha(); +} + +Bounds UIContext::TransformBounds(const Bounds &bounds) { + if (!transformStack_.empty()) { + const UITransform t = transformStack_.back(); + Bounds translated = bounds.Offset(t.translate.x, t.translate.y); + + // Scale around the center as the origin. + float scaledX = (translated.x - dp_xres * 0.5f) * t.scale.x + dp_xres * 0.5f; + float scaledY = (translated.y - dp_yres * 0.5f) * t.scale.y + dp_yres * 0.5f; + + return Bounds(scaledX, scaledY, translated.w * t.scale.x, translated.h * t.scale.y); + } + + return bounds; +} diff --git a/ext/native/ui/ui_context.h b/ext/native/ui/ui_context.h index 25a57ef63..5a248c3ba 100644 --- a/ext/native/ui/ui_context.h +++ b/ext/native/ui/ui_context.h @@ -4,6 +4,7 @@ #include "base/basictypes.h" #include "math/geom2d.h" +#include "math/lin/vec3.h" #include "gfx/texture_atlas.h" // Everything you need to draw a UI collected into a single unit that can be passed around. @@ -31,6 +32,13 @@ namespace UI { class DrawBuffer; +struct UITransform { + // TODO: Or just use a matrix? + Vec3 translate; + Vec3 scale; + float alpha; +}; + class UIContext { public: UIContext(); @@ -78,6 +86,10 @@ public: const Bounds &GetBounds() const { return bounds_; } Draw::DrawContext *GetDrawContext() { return draw_; } + void PushTransform(const UITransform &transform); + void PopTransform(); + Bounds TransformBounds(const Bounds &bounds); + private: Draw::DrawContext *draw_; Bounds bounds_; @@ -96,4 +108,5 @@ private: DrawBuffer *uidrawbufferTop_; std::vector scissorStack_; + std::vector transformStack_; }; diff --git a/ext/native/ui/ui_screen.cpp b/ext/native/ui/ui_screen.cpp index 3cc903a96..c473be1b6 100644 --- a/ext/native/ui/ui_screen.cpp +++ b/ext/native/ui/ui_screen.cpp @@ -11,7 +11,9 @@ static const bool ClickDebug = false; UIScreen::UIScreen() - : Screen(), root_(0), recreateViews_(true), hatDown_(0) { + : Screen(), root_(nullptr), recreateViews_(true), hatDown_(0) { + translation_ = Vec3(0.0f); + scale_ = Vec3(1.0f); } UIScreen::~UIScreen() { @@ -88,16 +90,33 @@ void UIScreen::render() { DoRecreateViews(); if (root_) { - UI::LayoutViewHierarchy(*screenManager()->getUIContext(), root_); + UIContext *uiContext = screenManager()->getUIContext(); + UI::LayoutViewHierarchy(*uiContext, root_); - screenManager()->getUIContext()->Begin(); - DrawBackground(*screenManager()->getUIContext()); - root_->Draw(*screenManager()->getUIContext()); - screenManager()->getUIContext()->End(); - screenManager()->getUIContext()->Flush(); + uiContext->PushTransform({translation_, scale_, alpha_}); + + uiContext->Begin(); + DrawBackground(*uiContext); + root_->Draw(*uiContext); + uiContext->End(); + uiContext->Flush(); + + uiContext->PopTransform(); } } +TouchInput UIScreen::transformTouch(const TouchInput &touch) { + TouchInput updated = touch; + + float x = touch.x - translation_.x; + float y = touch.y - translation_.y; + // Scale around the center as the origin. + updated.x = (x - dp_xres * 0.5f) / scale_.x + dp_xres * 0.5f; + updated.y = (y - dp_yres * 0.5f) / scale_.y + dp_yres * 0.5f; + + return updated; +} + bool UIScreen::touch(const TouchInput &touch) { if (root_) { if (ClickDebug && (touch.flags & TOUCH_DOWN)) { diff --git a/ext/native/ui/ui_screen.h b/ext/native/ui/ui_screen.h index f6ca4f210..60e747715 100644 --- a/ext/native/ui/ui_screen.h +++ b/ext/native/ui/ui_screen.h @@ -24,6 +24,8 @@ public: virtual bool key(const KeyInput &touch) override; virtual bool axis(const AxisInput &touch) override; + virtual TouchInput transformTouch(const TouchInput &touch) override; + // Some useful default event handlers UI::EventReturn OnOK(UI::EventParams &e); UI::EventReturn OnCancel(UI::EventParams &e); @@ -36,6 +38,9 @@ protected: virtual void RecreateViews() override { recreateViews_ = true; } UI::ViewGroup *root_; + Vec3 translation_; + Vec3 scale_; + float alpha_ = 1.0f; private: void DoRecreateViews(); diff --git a/ext/native/ui/viewgroup.cpp b/ext/native/ui/viewgroup.cpp index 583a4fbad..c58cd8b65 100644 --- a/ext/native/ui/viewgroup.cpp +++ b/ext/native/ui/viewgroup.cpp @@ -130,12 +130,11 @@ void ViewGroup::Draw(UIContext &dc) { } dc.FillRect(bg_, bounds_); - for (auto iter = views_.begin(); iter != views_.end(); ++iter) { - // TODO: If there is a transformation active, transform input coordinates accordingly. - if ((*iter)->GetVisibility() == V_VISIBLE) { + for (View *view : views_) { + if (view->GetVisibility() == V_VISIBLE) { // Check if bounds are in current scissor rectangle. - if (dc.GetScissorBounds().Intersects((*iter)->GetBounds())) - (*iter)->Draw(dc); + if (dc.GetScissorBounds().Intersects(dc.TransformBounds(view->GetBounds()))) + view->Draw(dc); } } if (clip_) { @@ -144,10 +143,9 @@ void ViewGroup::Draw(UIContext &dc) { } void ViewGroup::Update() { - for (auto iter = views_.begin(); iter != views_.end(); ++iter) { - // TODO: If there is a transformation active, transform input coordinates accordingly. - if ((*iter)->GetVisibility() != V_GONE) - (*iter)->Update(); + for (View *view : views_) { + if (view->GetVisibility() != V_GONE) + view->Update(); } }