Browse Source

Restore earlier HelloGUI sample, instead create separate Hello3DUI which demonstrates 3D UI render to texture. Move getting viewport related to scene from Scene to Renderer, so that Scene doesn't have to know about Renderer unnecessarily. Refactor the internal UI render calls related to rendertarget use.

Lasse Öörni 8 years ago
parent
commit
df669fbe1b

+ 1 - 137
Source/Samples/02_HelloGUI/HelloGUI.cpp

@@ -24,11 +24,6 @@
 #include <Urho3D/Engine/Engine.h>
 #include <Urho3D/Graphics/Graphics.h>
 #include <Urho3D/Graphics/Texture2D.h>
-#include <Urho3D/Graphics/Zone.h>
-#include <Urho3D/Graphics/StaticModel.h>
-#include <Urho3D/Graphics/Model.h>
-#include <Urho3D/Graphics/Technique.h>
-#include <Urho3D/Graphics/Octree.h>
 #include <Urho3D/Input/Input.h>
 #include <Urho3D/Resource/ResourceCache.h>
 #include <Urho3D/UI/Button.h>
@@ -39,8 +34,6 @@
 #include <Urho3D/UI/UI.h>
 #include <Urho3D/UI/UIEvents.h>
 #include <Urho3D/UI/Window.h>
-#include <Urho3D/UI/ListView.h>
-#include <Urho3D/UI/UIComponent.h>
 
 #include "HelloGUI.h"
 
@@ -51,10 +44,7 @@ URHO3D_DEFINE_APPLICATION_MAIN(HelloGUI)
 HelloGUI::HelloGUI(Context* context) :
     Sample(context),
     uiRoot_(GetSubsystem<UI>()->GetRoot()),
-    dragBeginPosition_(IntVector2::ZERO),
-    animateCube_(true),
-    renderOnCube_(false),
-    drawDebug_(false)
+    dragBeginPosition_(IntVector2::ZERO)
 {
 }
 
@@ -73,9 +63,6 @@ void HelloGUI::Start()
     // Set the loaded style as default style
     uiRoot_->SetDefaultStyle(style);
 
-    // Initialize Scene
-    InitScene();
-
     // Initialize Window
     InitWindow();
 
@@ -85,9 +72,6 @@ void HelloGUI::Start()
     // Create a draggable Fish
     CreateDraggableFish();
 
-    // Create 3D UI rendered on a cube.
-    Init3DUI();
-
     // Set the mouse mode to use in the sample
     Sample::InitMouseMode(MM_FREE);
 }
@@ -117,12 +101,6 @@ void HelloGUI::InitControls()
     checkBox->SetStyleAuto();
     button->SetStyleAuto();
     lineEdit->SetStyleAuto();
-
-    instructions_ = new Text(context_);
-    instructions_->SetStyleAuto();
-    instructions_->SetText("[TAB]   - toggle between rendering on screen or cube.\n"
-                           "[Space] - toggle cube rotation.");
-    uiRoot_->AddChild(instructions_);
 }
 
 void HelloGUI::InitWindow()
@@ -159,25 +137,8 @@ void HelloGUI::InitWindow()
     // Add the title bar to the Window
     window_->AddChild(titleBar);
 
-    // Create a list.
-    ListView* list = window_->CreateChild<ListView>();
-    list->SetSelectOnClickEnd(true);
-    list->SetHighlightMode(HM_ALWAYS);
-    list->SetMinHeight(200);
-
-    for (int i = 0; i < 32; i++)
-    {
-        Text* text = new Text(context_);
-        text->SetStyleAuto();
-        text->SetText(ToString("List item %d", i));
-        text->SetName(ToString("Item %d", i));
-        list->AddItem(text);
-    }
-    window_->AddChild(list);
-
     // Apply styles
     window_->SetStyleAuto();
-    list->SetStyleAuto();
     windowTitle->SetStyleAuto();
     buttonClose->SetStyle("CloseButton");
 
@@ -188,44 +149,6 @@ void HelloGUI::InitWindow()
     SubscribeToEvent(E_UIMOUSECLICK, URHO3D_HANDLER(HelloGUI, HandleControlClicked));
 }
 
-void HelloGUI::InitScene()
-{
-    ResourceCache* cache = GetSubsystem<ResourceCache>();
-
-    scene_ = new Scene(context_);
-    scene_->CreateComponent<Octree>();
-    Zone* zone = scene_->CreateComponent<Zone>();
-    zone->SetBoundingBox(BoundingBox(-1000.0f, 1000.0f));
-    zone->SetFogColor(Color::GRAY);
-    zone->SetFogStart(100.0f);
-    zone->SetFogEnd(300.0f);
-
-    // Create a child scene node (at world origin) and a StaticModel component into it.
-    Node* boxNode = scene_->CreateChild("Box");
-    boxNode->SetScale(Vector3(5.0f, 5.0f, 5.0f));
-    boxNode->SetRotation(Quaternion(90, Vector3::LEFT));
-
-    // Create a box model and hide it initially.
-    StaticModel* boxModel = boxNode->CreateComponent<StaticModel>();
-    boxModel->SetModel(cache->GetResource<Model>("Models/Box.mdl"));
-    boxNode->SetEnabled(false);
-
-    // Create a camera.
-    cameraNode_ = scene_->CreateChild("Camera");
-    cameraNode_->CreateComponent<Camera>();
-
-    // Set an initial position for the camera scene node.
-    cameraNode_->SetPosition(Vector3(0.0f, 0.0f, -10.0f));
-
-    // Set up a viewport so 3D scene can be visible.
-    Renderer* renderer = GetSubsystem<Renderer>();
-    SharedPtr<Viewport> viewport(new Viewport(context_, scene_, cameraNode_->GetComponent<Camera>()));
-    renderer->SetViewport(0, viewport);
-
-    // Subscribe to update event and animate cube and handle input.
-    SubscribeToEvent(E_UPDATE, URHO3D_HANDLER(HelloGUI, HandleUpdate));
-}
-
 void HelloGUI::CreateDraggableFish()
 {
     ResourceCache* cache = GetSubsystem<ResourceCache>();
@@ -300,62 +223,3 @@ void HelloGUI::HandleControlClicked(StringHash eventType, VariantMap& eventData)
     // Update the Window's title text
     windowTitle->SetText("Hello " + name + "!");
 }
-
-void HelloGUI::Init3DUI()
-{
-    ResourceCache* cache = GetSubsystem<ResourceCache>();
-
-    // Node that will get UI rendered on it.
-    Node* boxNode = scene_->GetChild("Box");
-    // Create a component that sets up UI rendering. It sets material to StaticModel of the node.
-    UIComponent* component = boxNode->CreateComponent<UIComponent>();
-    // Optionally modify material. Technique is changed so object is visible without any lights.
-    component->GetMaterial()->SetTechnique(0, cache->GetResource<Technique>("Techniques/DiffUnlit.xml"));
-    // Save root element of texture UI for later use.
-    textureRoot_ = component->GetRoot();
-    // Set size of root element. This is size of texture as well.
-    textureRoot_->SetSize(512, 512);
-}
-
-void HelloGUI::HandleUpdate(StringHash, VariantMap& eventData)
-{
-    using namespace Update;
-    float timeStep = eventData[P_TIMESTEP].GetFloat();
-    Input* input = GetSubsystem<Input>();
-    Node* node = scene_->GetChild("Box");
-
-    if (current_.NotNull() && drawDebug_)
-        GetSubsystem<UI>()->DebugDraw(current_);
-
-    if (input->GetMouseButtonPress(MOUSEB_LEFT))
-        current_ = GetSubsystem<UI>()->GetElementAt(input->GetMousePosition());
-
-    if (input->GetKeyPress(KEY_TAB))
-    {
-        renderOnCube_ = !renderOnCube_;
-        // Toggle between rendering on screen or to texture.
-        if (renderOnCube_)
-        {
-            node->SetEnabled(true);
-            textureRoot_->AddChild(window_);
-        }
-        else
-        {
-            node->SetEnabled(false);
-            uiRoot_->AddChild(window_);
-        }
-    }
-
-    if (input->GetKeyPress(KEY_SPACE))
-        animateCube_ = !animateCube_;
-
-    if (input->GetKeyPress(KEY_F2))
-        drawDebug_ = !drawDebug_;
-
-    if (animateCube_)
-    {
-        node->Yaw(6.0f * timeStep * 1.5f);
-        node->Roll(-6.0f * timeStep * 1.5f);
-        node->Pitch(-6.0f * timeStep * 1.5f);
-    }
-}

+ 0 - 20
Source/Samples/02_HelloGUI/HelloGUI.h

@@ -62,8 +62,6 @@ protected:
     }
 
 private:
-    /// Create and initialize a Scene.
-    void InitScene();
     /// Create and initialize a Window control.
     void InitWindow();
     /// Create and add various common controls for demonstration purposes.
@@ -80,31 +78,13 @@ private:
     void HandleControlClicked(StringHash eventType, VariantMap& eventData);
     /// Handle close button pressed and released.
     void HandleClosePressed(StringHash eventType, VariantMap& eventData);
-    /// Animate cube.
-    void HandleUpdate(StringHash, VariantMap& eventData);
-    /// Create 3D UI.
-    void Init3DUI();
 
-    /// The Scene.
-    SharedPtr<Scene> scene_;
     /// The Window.
     SharedPtr<Window> window_;
     /// The UI's root UIElement.
     SharedPtr<UIElement> uiRoot_;
     /// Remembered drag begin position.
     IntVector2 dragBeginPosition_;
-    /// Root UI element of texture.
-    SharedPtr<UIElement> textureRoot_;
-    /// UI element with instructions.
-    SharedPtr<Text> instructions_;
-    /// Enable or disable cube rotation.
-    bool animateCube_;
-    /// Enable or disable rendering to texture.
-    bool renderOnCube_;
-    /// Draw debug information of last clicked element.
-    bool drawDebug_;
-    /// Last clicked UI element.
-    WeakPtr<UIElement> current_;
 };
 
 

+ 33 - 0
Source/Samples/48_Hello3DUI/CMakeLists.txt

@@ -0,0 +1,33 @@
+#
+# Copyright (c) 2008-2017 the Urho3D project.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+
+# Define target name
+set (TARGET_NAME 48_Hello3DUI)
+
+# Define source files
+define_source_files (EXTRA_H_FILES ${COMMON_SAMPLE_H_FILES})
+
+# Setup target with resource copying
+setup_main_executable ()
+
+# Setup test cases
+setup_test ()

+ 361 - 0
Source/Samples/48_Hello3DUI/Hello3DUI.cpp

@@ -0,0 +1,361 @@
+//
+// Copyright (c) 2008-2017 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#include <Urho3D/Core/CoreEvents.h>
+#include <Urho3D/Engine/Engine.h>
+#include <Urho3D/Graphics/Graphics.h>
+#include <Urho3D/Graphics/Texture2D.h>
+#include <Urho3D/Graphics/Zone.h>
+#include <Urho3D/Graphics/StaticModel.h>
+#include <Urho3D/Graphics/Model.h>
+#include <Urho3D/Graphics/Technique.h>
+#include <Urho3D/Graphics/Octree.h>
+#include <Urho3D/Input/Input.h>
+#include <Urho3D/Resource/ResourceCache.h>
+#include <Urho3D/UI/Button.h>
+#include <Urho3D/UI/CheckBox.h>
+#include <Urho3D/UI/LineEdit.h>
+#include <Urho3D/UI/Text.h>
+#include <Urho3D/UI/ToolTip.h>
+#include <Urho3D/UI/UI.h>
+#include <Urho3D/UI/UIEvents.h>
+#include <Urho3D/UI/Window.h>
+#include <Urho3D/UI/ListView.h>
+#include <Urho3D/UI/UIComponent.h>
+
+#include "Hello3DUI.h"
+
+#include <Urho3D/DebugNew.h>
+
+URHO3D_DEFINE_APPLICATION_MAIN(Hello3DUI)
+
+Hello3DUI::Hello3DUI(Context* context) :
+    Sample(context),
+    uiRoot_(GetSubsystem<UI>()->GetRoot()),
+    dragBeginPosition_(IntVector2::ZERO),
+    animateCube_(true),
+    renderOnCube_(false),
+    drawDebug_(false)
+{
+}
+
+void Hello3DUI::Start()
+{
+    // Execute base class startup
+    Sample::Start();
+
+    // Enable OS cursor
+    GetSubsystem<Input>()->SetMouseVisible(true);
+
+    // Load XML file containing default UI style sheet
+    ResourceCache* cache = GetSubsystem<ResourceCache>();
+    XMLFile* style = cache->GetResource<XMLFile>("UI/DefaultStyle.xml");
+
+    // Set the loaded style as default style
+    uiRoot_->SetDefaultStyle(style);
+
+    // Initialize Scene
+    InitScene();
+
+    // Initialize Window
+    InitWindow();
+
+    // Create and add some controls to the Window
+    InitControls();
+
+    // Create a draggable Fish
+    CreateDraggableFish();
+
+    // Create 3D UI rendered on a cube.
+    Init3DUI();
+
+    // Set the mouse mode to use in the sample
+    Sample::InitMouseMode(MM_FREE);
+}
+
+void Hello3DUI::InitControls()
+{
+    // Create a CheckBox
+    CheckBox* checkBox = new CheckBox(context_);
+    checkBox->SetName("CheckBox");
+
+    // Create a Button
+    Button* button = new Button(context_);
+    button->SetName("Button");
+    button->SetMinHeight(24);
+
+    // Create a LineEdit
+    LineEdit* lineEdit = new LineEdit(context_);
+    lineEdit->SetName("LineEdit");
+    lineEdit->SetMinHeight(24);
+
+    // Add controls to Window
+    window_->AddChild(checkBox);
+    window_->AddChild(button);
+    window_->AddChild(lineEdit);
+
+    // Apply previously set default style
+    checkBox->SetStyleAuto();
+    button->SetStyleAuto();
+    lineEdit->SetStyleAuto();
+
+    instructions_ = new Text(context_);
+    instructions_->SetStyleAuto();
+    instructions_->SetText("[TAB]   - toggle between rendering on screen or cube.\n"
+                           "[Space] - toggle cube rotation.");
+    uiRoot_->AddChild(instructions_);
+}
+
+void Hello3DUI::InitWindow()
+{
+    // Create the Window and add it to the UI's root node
+    window_ = new Window(context_);
+    uiRoot_->AddChild(window_);
+
+    // Set Window size and layout settings
+    window_->SetMinWidth(384);
+    window_->SetLayout(LM_VERTICAL, 6, IntRect(6, 6, 6, 6));
+    window_->SetAlignment(HA_CENTER, VA_CENTER);
+    window_->SetName("Window");
+
+    // Create Window 'titlebar' container
+    UIElement* titleBar = new UIElement(context_);
+    titleBar->SetMinSize(0, 24);
+    titleBar->SetVerticalAlignment(VA_TOP);
+    titleBar->SetLayoutMode(LM_HORIZONTAL);
+
+    // Create the Window title Text
+    Text* windowTitle = new Text(context_);
+    windowTitle->SetName("WindowTitle");
+    windowTitle->SetText("Hello GUI!");
+
+    // Create the Window's close button
+    Button* buttonClose = new Button(context_);
+    buttonClose->SetName("CloseButton");
+
+    // Add the controls to the title bar
+    titleBar->AddChild(windowTitle);
+    titleBar->AddChild(buttonClose);
+
+    // Add the title bar to the Window
+    window_->AddChild(titleBar);
+
+    // Create a list.
+    ListView* list = window_->CreateChild<ListView>();
+    list->SetSelectOnClickEnd(true);
+    list->SetHighlightMode(HM_ALWAYS);
+    list->SetMinHeight(200);
+
+    for (int i = 0; i < 32; i++)
+    {
+        Text* text = new Text(context_);
+        text->SetStyleAuto();
+        text->SetText(ToString("List item %d", i));
+        text->SetName(ToString("Item %d", i));
+        list->AddItem(text);
+    }
+    window_->AddChild(list);
+
+    // Apply styles
+    window_->SetStyleAuto();
+    list->SetStyleAuto();
+    windowTitle->SetStyleAuto();
+    buttonClose->SetStyle("CloseButton");
+
+    // Subscribe to buttonClose release (following a 'press') events
+    SubscribeToEvent(buttonClose, E_RELEASED, URHO3D_HANDLER(Hello3DUI, HandleClosePressed));
+
+    // Subscribe also to all UI mouse clicks just to see where we have clicked
+    SubscribeToEvent(E_UIMOUSECLICK, URHO3D_HANDLER(Hello3DUI, HandleControlClicked));
+}
+
+void Hello3DUI::InitScene()
+{
+    ResourceCache* cache = GetSubsystem<ResourceCache>();
+
+    scene_ = new Scene(context_);
+    scene_->CreateComponent<Octree>();
+    Zone* zone = scene_->CreateComponent<Zone>();
+    zone->SetBoundingBox(BoundingBox(-1000.0f, 1000.0f));
+    zone->SetFogColor(Color::GRAY);
+    zone->SetFogStart(100.0f);
+    zone->SetFogEnd(300.0f);
+
+    // Create a child scene node (at world origin) and a StaticModel component into it.
+    Node* boxNode = scene_->CreateChild("Box");
+    boxNode->SetScale(Vector3(5.0f, 5.0f, 5.0f));
+    boxNode->SetRotation(Quaternion(90, Vector3::LEFT));
+
+    // Create a box model and hide it initially.
+    StaticModel* boxModel = boxNode->CreateComponent<StaticModel>();
+    boxModel->SetModel(cache->GetResource<Model>("Models/Box.mdl"));
+    boxNode->SetEnabled(false);
+
+    // Create a camera.
+    cameraNode_ = scene_->CreateChild("Camera");
+    cameraNode_->CreateComponent<Camera>();
+
+    // Set an initial position for the camera scene node.
+    cameraNode_->SetPosition(Vector3(0.0f, 0.0f, -10.0f));
+
+    // Set up a viewport so 3D scene can be visible.
+    Renderer* renderer = GetSubsystem<Renderer>();
+    SharedPtr<Viewport> viewport(new Viewport(context_, scene_, cameraNode_->GetComponent<Camera>()));
+    renderer->SetViewport(0, viewport);
+
+    // Subscribe to update event and animate cube and handle input.
+    SubscribeToEvent(E_UPDATE, URHO3D_HANDLER(Hello3DUI, HandleUpdate));
+}
+
+void Hello3DUI::CreateDraggableFish()
+{
+    ResourceCache* cache = GetSubsystem<ResourceCache>();
+    Graphics* graphics = GetSubsystem<Graphics>();
+
+    // Create a draggable Fish button
+    Button* draggableFish = new Button(context_);
+    draggableFish->SetTexture(cache->GetResource<Texture2D>("Textures/UrhoDecal.dds")); // Set texture
+    draggableFish->SetBlendMode(BLEND_ADD);
+    draggableFish->SetSize(128, 128);
+    draggableFish->SetPosition((graphics->GetWidth() - draggableFish->GetWidth()) / 2, 200);
+    draggableFish->SetName("Fish");
+    uiRoot_->AddChild(draggableFish);
+
+    // Add a tooltip to Fish button
+    ToolTip* toolTip = new ToolTip(context_);
+    draggableFish->AddChild(toolTip);
+    toolTip->SetPosition(IntVector2(draggableFish->GetWidth() + 5, draggableFish->GetWidth() / 2)); // slightly offset from close button
+    BorderImage* textHolder = new BorderImage(context_);
+    toolTip->AddChild(textHolder);
+    textHolder->SetStyle("ToolTipBorderImage");
+    Text* toolTipText = new Text(context_);
+    textHolder->AddChild(toolTipText);
+    toolTipText->SetStyle("ToolTipText");
+    toolTipText->SetText("Please drag me!");
+
+    // Subscribe draggableFish to Drag Events (in order to make it draggable)
+    // See "Event list" in documentation's Main Page for reference on available Events and their eventData
+    SubscribeToEvent(draggableFish, E_DRAGBEGIN, URHO3D_HANDLER(Hello3DUI, HandleDragBegin));
+    SubscribeToEvent(draggableFish, E_DRAGMOVE, URHO3D_HANDLER(Hello3DUI, HandleDragMove));
+    SubscribeToEvent(draggableFish, E_DRAGEND, URHO3D_HANDLER(Hello3DUI, HandleDragEnd));
+}
+
+void Hello3DUI::HandleDragBegin(StringHash eventType, VariantMap& eventData)
+{
+    // Get UIElement relative position where input (touch or click) occurred (top-left = IntVector2(0,0))
+    dragBeginPosition_ = IntVector2(eventData["ElementX"].GetInt(), eventData["ElementY"].GetInt());
+}
+
+void Hello3DUI::HandleDragMove(StringHash eventType, VariantMap& eventData)
+{
+    IntVector2 dragCurrentPosition = IntVector2(eventData["X"].GetInt(), eventData["Y"].GetInt());
+    UIElement* draggedElement = static_cast<UIElement*>(eventData["Element"].GetPtr());
+    draggedElement->SetPosition(dragCurrentPosition - dragBeginPosition_);
+}
+
+void Hello3DUI::HandleDragEnd(StringHash eventType, VariantMap& eventData) // For reference (not used here)
+{
+}
+
+void Hello3DUI::HandleClosePressed(StringHash eventType, VariantMap& eventData)
+{
+    if (GetPlatform() != "Web")
+        engine_->Exit();
+}
+
+void Hello3DUI::HandleControlClicked(StringHash eventType, VariantMap& eventData)
+{
+    // Get the Text control acting as the Window's title
+    Text* windowTitle = window_->GetChildStaticCast<Text>("WindowTitle", true);
+
+    // Get control that was clicked
+    UIElement* clicked = static_cast<UIElement*>(eventData[UIMouseClick::P_ELEMENT].GetPtr());
+
+    String name = "...?";
+    if (clicked)
+    {
+        // Get the name of the control that was clicked
+        name = clicked->GetName();
+    }
+
+    // Update the Window's title text
+    windowTitle->SetText("Hello " + name + "!");
+}
+
+void Hello3DUI::Init3DUI()
+{
+    ResourceCache* cache = GetSubsystem<ResourceCache>();
+
+    // Node that will get UI rendered on it.
+    Node* boxNode = scene_->GetChild("Box");
+    // Create a component that sets up UI rendering. It sets material to StaticModel of the node.
+    UIComponent* component = boxNode->CreateComponent<UIComponent>();
+    // Optionally modify material. Technique is changed so object is visible without any lights.
+    component->GetMaterial()->SetTechnique(0, cache->GetResource<Technique>("Techniques/DiffUnlit.xml"));
+    // Save root element of texture UI for later use.
+    textureRoot_ = component->GetRoot();
+    // Set size of root element. This is size of texture as well.
+    textureRoot_->SetSize(512, 512);
+}
+
+void Hello3DUI::HandleUpdate(StringHash, VariantMap& eventData)
+{
+    using namespace Update;
+    float timeStep = eventData[P_TIMESTEP].GetFloat();
+    Input* input = GetSubsystem<Input>();
+    Node* node = scene_->GetChild("Box");
+
+    if (current_.NotNull() && drawDebug_)
+        GetSubsystem<UI>()->DebugDraw(current_);
+
+    if (input->GetMouseButtonPress(MOUSEB_LEFT))
+        current_ = GetSubsystem<UI>()->GetElementAt(input->GetMousePosition());
+
+    if (input->GetKeyPress(KEY_TAB))
+    {
+        renderOnCube_ = !renderOnCube_;
+        // Toggle between rendering on screen or to texture.
+        if (renderOnCube_)
+        {
+            node->SetEnabled(true);
+            textureRoot_->AddChild(window_);
+        }
+        else
+        {
+            node->SetEnabled(false);
+            uiRoot_->AddChild(window_);
+        }
+    }
+
+    if (input->GetKeyPress(KEY_SPACE))
+        animateCube_ = !animateCube_;
+
+    if (input->GetKeyPress(KEY_F2))
+        drawDebug_ = !drawDebug_;
+
+    if (animateCube_)
+    {
+        node->Yaw(6.0f * timeStep * 1.5f);
+        node->Roll(-6.0f * timeStep * 1.5f);
+        node->Pitch(-6.0f * timeStep * 1.5f);
+    }
+}

+ 103 - 0
Source/Samples/48_Hello3DUI/Hello3DUI.h

@@ -0,0 +1,103 @@
+//
+// Copyright (c) 2008-2017 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#include "Sample.h"
+
+namespace Urho3D
+{
+
+class Window;
+
+}
+
+/// A 3D UI demonstration based on the HelloGUI sample. Renders UI alternatively
+/// either to a 3D scene object, or directly to the backbuffer.
+class Hello3DUI : public Sample
+{
+    URHO3D_OBJECT(Hello3DUI, Sample);
+
+public:
+    /// Construct.
+    Hello3DUI(Context* context);
+
+    /// Setup after engine initialization and before running the main loop.
+    virtual void Start();
+
+protected:
+    /// Return XML patch instructions for screen joystick layout for a specific sample app, if any.
+    virtual String GetScreenJoystickPatchString() const { return
+        "<patch>"
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Hat0']]\">"
+        "        <attribute name=\"Is Visible\" value=\"false\" />"
+        "    </add>"
+        "</patch>";
+    }
+
+private:
+    /// Create and initialize a Scene.
+    void InitScene();
+    /// Create and initialize a Window control.
+    void InitWindow();
+    /// Create and add various common controls for demonstration purposes.
+    void InitControls();
+    /// Create a draggable fish button.
+    void CreateDraggableFish();
+    /// Handle drag begin for the fish button.
+    void HandleDragBegin(StringHash eventType, VariantMap& eventData);
+    /// Handle drag move for the fish button.
+    void HandleDragMove(StringHash eventType, VariantMap& eventData);
+    /// Handle drag end for the fish button.
+    void HandleDragEnd(StringHash eventType, VariantMap& eventData);
+    /// Handle any UI control being clicked.
+    void HandleControlClicked(StringHash eventType, VariantMap& eventData);
+    /// Handle close button pressed and released.
+    void HandleClosePressed(StringHash eventType, VariantMap& eventData);
+    /// Animate cube.
+    void HandleUpdate(StringHash, VariantMap& eventData);
+    /// Create 3D UI.
+    void Init3DUI();
+
+    /// The Scene.
+    SharedPtr<Scene> scene_;
+    /// The Window.
+    SharedPtr<Window> window_;
+    /// The UI's root UIElement.
+    SharedPtr<UIElement> uiRoot_;
+    /// Remembered drag begin position.
+    IntVector2 dragBeginPosition_;
+    /// Root UI element of texture.
+    SharedPtr<UIElement> textureRoot_;
+    /// UI element with instructions.
+    SharedPtr<Text> instructions_;
+    /// Enable or disable cube rotation.
+    bool animateCube_;
+    /// Enable or disable rendering to texture.
+    bool renderOnCube_;
+    /// Draw debug information of last clicked element.
+    bool drawDebug_;
+    /// Last clicked UI element.
+    WeakPtr<UIElement> current_;
+};
+
+

+ 1 - 0
Source/Urho3D/AngelScript/GraphicsAPI.cpp

@@ -1963,6 +1963,7 @@ static void RegisterRenderer(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Renderer", "Viewport@+ get_viewports(uint) const", asMETHOD(Renderer, GetViewport), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "void SetDefaultRenderPath(XMLFile@+)", asMETHODPR(Renderer, SetDefaultRenderPath, (XMLFile*), void), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "void SetVSMShadowParameters(float, float)", asMETHOD(Renderer, SetVSMShadowParameters), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Renderer", "Viewport@+ GetViewportForScene(Scene@+, uint)", asMETHOD(Renderer, GetViewportForScene), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "void set_defaultRenderPath(RenderPath@+)", asMETHODPR(Renderer, SetDefaultRenderPath, (RenderPath*), void), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "RenderPath@+ get_defaultRenderPath() const", asMETHOD(Renderer, GetDefaultRenderPath), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "void set_defaultTechnique(Technique@+)", asMETHOD(Renderer, SetDefaultTechnique), asCALL_THISCALL);

+ 17 - 0
Source/Urho3D/Graphics/Renderer.cpp

@@ -574,6 +574,23 @@ Viewport* Renderer::GetViewport(unsigned index) const
     return index < viewports_.Size() ? viewports_[index] : (Viewport*)0;
 }
 
+Viewport* Renderer::GetViewportForScene(Scene* scene, unsigned index) const
+{
+    for (unsigned i = 0; i < viewports_.Size(); ++i)
+    {
+        Viewport* viewport = viewports_[i];
+        if (viewport && viewport->GetScene() == scene)
+        {
+            if (index == 0)
+                return viewport;
+            else
+                --index;
+        }
+    }
+    return 0;
+}
+
+
 RenderPath* Renderer::GetDefaultRenderPath() const
 {
     return defaultRenderPath_;

+ 3 - 0
Source/Urho3D/Graphics/Renderer.h

@@ -43,6 +43,7 @@ class Graphics;
 class RenderPath;
 class RenderSurface;
 class ResourceCache;
+class Scene;
 class Skeleton;
 class OcclusionBuffer;
 class Technique;
@@ -254,6 +255,8 @@ public:
 
     /// Return backbuffer viewport by index.
     Viewport* GetViewport(unsigned index) const;
+    /// Return nth backbuffer viewport associated to a scene. Index 0 returns the first.
+    Viewport* GetViewportForScene(Scene* scene, unsigned index) const;
     /// Return default renderpath.
     RenderPath* GetDefaultRenderPath() const;
     /// Return default non-textured material technique.

+ 1 - 1
Source/Urho3D/Graphics/View.cpp

@@ -1718,7 +1718,7 @@ void View::ExecuteRenderPathCommands()
             case CMD_RENDERUI:
                 {
                     SetRenderTargets(command);
-                    GetSubsystem<UI>()->Render(false);
+                    GetSubsystem<UI>()->Render(true);
                 }
                 break;
 

+ 1 - 0
Source/Urho3D/LuaScript/pkgs/Graphics/Renderer.pkg

@@ -36,6 +36,7 @@ class Renderer
 
     unsigned GetNumViewports() const;
     Viewport* GetViewport(unsigned index) const;
+    Viewport* GetViewportForScene(Scene* scene, unsigned index) const;
     RenderPath* GetDefaultRenderPath() const;
     Technique* GetDefaultTechnique() const;
     bool GetHDRRendering() const;

+ 0 - 17
Source/Urho3D/Scene/Scene.cpp

@@ -1529,23 +1529,6 @@ void Scene::PreloadResourcesJSON(const JSONValue& value)
 #endif
 }
 
-Viewport* Scene::GetViewport(int index)
-{
-    Renderer* renderer = GetSubsystem<Renderer>();
-    for (unsigned i = 0; i < renderer->GetNumViewports(); ++i)
-    {
-        Viewport* viewport = renderer->GetViewport(i);
-        if (viewport && viewport->GetScene() == GetScene())
-        {
-            if (index == 0)
-                return viewport;
-            else
-                --index;
-        }
-    }
-    return 0;
-}
-
 void RegisterSceneLibrary(Context* context)
 {
     ValueAnimation::RegisterObject(context);

+ 0 - 2
Source/Urho3D/Scene/Scene.h

@@ -256,8 +256,6 @@ public:
     void MarkNetworkUpdate(Component* component);
     /// Mark a node dirty in scene replication states. The node does not need to have own replication state yet.
     void MarkReplicationDirty(Node* node);
-    /// Returns viewport assigned to this scene. By default returns first viewport assigned to a scene.
-    Viewport* GetViewport(int index=0);
 
 private:
     /// Handle the logic update event to update the scene, if active.

+ 64 - 55
Source/Urho3D/UI/UI.cpp

@@ -470,43 +470,59 @@ void UI::RenderUpdate()
     }
 }
 
-void UI::Render(bool resetRenderTargets)
+void UI::Render(bool renderUICommand)
 {
-    // Perform the default render only if not rendered yet
-    if (resetRenderTargets && uiRendered_)
-        return;
-
     URHO3D_PROFILE(RenderUI);
 
     // If the OS cursor is visible, apply its shape now if changed
-    bool osCursorVisible = GetSubsystem<Input>()->IsMouseVisible();
-    if (cursor_ && osCursorVisible)
-        cursor_->ApplyOSCursorShape();
-
-    SetVertexData(vertexBuffer_, vertexData_);
-    SetVertexData(debugVertexBuffer_, debugVertexData_);
-
-    // Render non-modal batches
-    Render(resetRenderTargets, vertexBuffer_, batches_, 0, nonModalBatchSize_);
-    // Render debug draw
-    Render(resetRenderTargets, debugVertexBuffer_, debugDrawBatches_, 0, debugDrawBatches_.Size());
-    // Render modal batches
-    Render(resetRenderTargets, vertexBuffer_, batches_, nonModalBatchSize_, batches_.Size());
-    // Render to textures
-    for (Vector<WeakPtr<UIComponent> >::ConstIterator it = renderToTexture_.Begin(); it != renderToTexture_.End(); it++)
+    if (!renderUICommand)
     {
-        WeakPtr<UIComponent> component = *it;
-        if (component->IsEnabled())
+        bool osCursorVisible = GetSubsystem<Input>()->IsMouseVisible();
+        if (cursor_ && osCursorVisible)
+            cursor_->ApplyOSCursorShape();
+    }
+
+    // Perform the default backbuffer render only if not rendered yet, or additional renders through RenderUI command
+    if (renderUICommand || !uiRendered_)
+    {
+        SetVertexData(vertexBuffer_, vertexData_);
+        SetVertexData(debugVertexBuffer_, debugVertexData_);
+
+        if (!renderUICommand)
+            graphics_->ResetRenderTargets();
+        // Render non-modal batches
+        Render(vertexBuffer_, batches_, 0, nonModalBatchSize_);
+        // Render debug draw
+        Render(debugVertexBuffer_, debugDrawBatches_, 0, debugDrawBatches_.Size());
+        // Render modal batches
+        Render(vertexBuffer_, batches_, nonModalBatchSize_, batches_.Size());
+    }
+    
+    // Render to UIComponent textures. This is skipped when called from the RENDERUI command
+    if (!renderUICommand)
+    {
+        for (Vector<WeakPtr<UIComponent> >::ConstIterator it = renderToTexture_.Begin(); it != renderToTexture_.End(); it++)
         {
-            SetVertexData(component->vertexBuffer_, component->vertexData_);
-            SetVertexData(component->debugVertexBuffer_, component->debugVertexData_);
-            Render(resetRenderTargets, component->vertexBuffer_, component->batches_, 0, component->batches_.Size(),
-                   component->GetTexture()->GetRenderSurface());
-            Render(resetRenderTargets, component->debugVertexBuffer_, component->debugDrawBatches_, 0,
-                   component->debugDrawBatches_.Size(), component->GetTexture()->GetRenderSurface());
-            component->debugDrawBatches_.Clear();
-            component->debugVertexData_.Clear();
+            WeakPtr<UIComponent> component = *it;
+            if (component->IsEnabled())
+            {
+                SetVertexData(component->vertexBuffer_, component->vertexData_);
+                SetVertexData(component->debugVertexBuffer_, component->debugVertexData_);
+                
+                RenderSurface* surface = component->GetTexture()->GetRenderSurface();
+                graphics_->SetRenderTarget(0, surface);
+                graphics_->SetViewport(IntRect(0, 0, surface->GetWidth(), surface->GetHeight()));
+                graphics_->Clear(Urho3D::CLEAR_COLOR);
+
+                Render(component->vertexBuffer_, component->batches_, 0, component->batches_.Size());
+                Render(component->debugVertexBuffer_, component->debugDrawBatches_, 0, component->debugDrawBatches_.Size());
+                component->debugDrawBatches_.Clear();
+                component->debugVertexData_.Clear();
+            }
         }
+
+        if (renderToTexture_.Size())
+            graphics_->ResetRenderTargets();
     }
 
     // Clear the debug draw batches and data
@@ -946,8 +962,7 @@ void UI::SetVertexData(VertexBuffer* dest, const PODVector<float>& vertexData)
     dest->SetData(&vertexData[0]);
 }
 
-void UI::Render(bool resetRenderTargets, VertexBuffer* buffer, const PODVector<UIBatch>& batches, unsigned batchStart,
-                unsigned batchEnd, RenderSurface* surface)
+void UI::Render(VertexBuffer* buffer, const PODVector<UIBatch>& batches, unsigned batchStart, unsigned batchEnd)
 {
     // Engine does not render when window is closed or device is lost
     assert(graphics_ && graphics_->IsInitialized() && !graphics_->IsDeviceLost());
@@ -955,17 +970,9 @@ void UI::Render(bool resetRenderTargets, VertexBuffer* buffer, const PODVector<U
     if (batches.Empty())
         return;
 
-    if (resetRenderTargets)
-        graphics_->ResetRenderTargets();
-
-    bool scissorTest = true;
     unsigned alphaFormat = Graphics::GetAlphaFormat();
-    RenderSurface* previousSurface = graphics_->GetRenderTarget(0);
-    IntVector2 viewSize;
-    if (surface)
-        viewSize = IntVector2(surface->GetWidth(), surface->GetHeight());
-    else
-        viewSize = graphics_->GetViewport().Size();
+    RenderSurface* surface = graphics_->GetRenderTarget(0);
+    IntVector2 viewSize = graphics_->GetViewport().Size();
     Vector2 invScreenSize(1.0f / (float)viewSize.x_, 1.0f / (float)viewSize.y_);
     Vector2 scale(2.0f * invScreenSize.x_, -2.0f * invScreenSize.y_);
     Vector2 offset(-1.0f, 1.0f);
@@ -974,16 +981,10 @@ void UI::Render(bool resetRenderTargets, VertexBuffer* buffer, const PODVector<U
     {
 #ifdef URHO3D_OPENGL
         // On OpenGL, flip the projection if rendering to a texture so that the texture can be addressed in the
-        // same way as a render texture produced on Direct3D. Requires disabling scissor as well. This needs a
-        // proper fix.
+        // same way as a render texture produced on Direct3D.
         offset.y_ = -offset.y_;
         scale.y_ = -scale.y_;
-        scissorTest = false;
 #endif
-        graphics_->SetRenderTarget(0, surface);
-        graphics_->SetViewport(IntRect(0, 0, viewSize.x_, viewSize.y_));
-        if (resetRenderTargets)
-            graphics_->Clear(Urho3D::CLEAR_COLOR);
     }
 
     Matrix4 projection(Matrix4::IDENTITY);
@@ -998,9 +999,9 @@ void UI::Render(bool resetRenderTargets, VertexBuffer* buffer, const PODVector<U
     graphics_->ClearParameterSources();
     graphics_->SetColorWrite(true);
 #ifdef URHO3D_OPENGL
-    // Required due to OpenGL workaround above.
+    // Reverse winding if rendering to texture on OpenGL
     if (surface)
-        graphics_->SetCullMode(CULL_NONE);
+        graphics_->SetCullMode(CULL_CW);
     else
 #endif
         graphics_->SetCullMode(CULL_CCW);
@@ -1063,15 +1064,23 @@ void UI::Render(bool resetRenderTargets, VertexBuffer* buffer, const PODVector<U
         scissor.right_ = (int)(scissor.right_ * uiScale_);
         scissor.bottom_ = (int)(scissor.bottom_ * uiScale_);
 
+        // Flip scissor vertically if using OpenGL texture rendering
+#ifdef URHO3D_OPENGL
+        if (surface)
+        {
+            int top = scissor.top_;
+            int bottom = scissor.bottom_;
+            scissor.top_ = viewSize.y_ - bottom;
+            scissor.bottom_ = viewSize.y_ - top;
+        }
+#endif
+
         graphics_->SetBlendMode(batch.blendMode_);
-        graphics_->SetScissorTest(scissorTest, scissor);
+        graphics_->SetScissorTest(true, scissor);
         graphics_->SetTexture(0, batch.texture_);
         graphics_->Draw(TRIANGLE_LIST, batch.vertexStart_ / UI_VERTEX_SIZE,
             (batch.vertexEnd_ - batch.vertexStart_) / UI_VERTEX_SIZE);
     }
-
-    if (surface && !resetRenderTargets)
-        graphics_->SetRenderTarget(0, previousSurface);
 }
 
 void UI::GetBatches(PODVector<UIBatch>& batches, PODVector<float>& vertexData, UIElement* element, IntRect currentScissor)

+ 4 - 5
Source/Urho3D/UI/UI.h

@@ -78,8 +78,8 @@ public:
     void Update(float timeStep);
     /// Update the UI for rendering. Called by HandleRenderUpdate().
     void RenderUpdate();
-    /// Render the UI. If resetRenderTargets is true, is assumed to be the default UI render to backbuffer called by Engine, and will be performed only once. Additional UI renders to a different rendertarget may be triggered from the renderpath.
-    void Render(bool resetRenderTargets = true);
+    /// Render the UI. If renderUICommand is false (default), is assumed to be the default UI render to backbuffer called by Engine, and will be performed only once. Additional UI renders to a different rendertarget may be triggered from the renderpath.
+    void Render(bool renderUICommand = false);
     /// Debug draw a UI element.
     void DebugDraw(UIElement* element);
     /// Load a UI layout from an XML file. Optionally specify another XML file for element style. Return the root element.
@@ -239,9 +239,8 @@ private:
     void Update(float timeStep, UIElement* element);
     /// Upload UI geometry into a vertex buffer.
     void SetVertexData(VertexBuffer* dest, const PODVector<float>& vertexData);
-    /// Render UI batches. Geometry must have been uploaded first.
-    void Render(bool resetRenderTargets, VertexBuffer* buffer, const PODVector<UIBatch>& batches, unsigned batchStart,
-                unsigned batchEnd, RenderSurface* surface=0);
+    /// Render UI batches to the current rendertarget. Geometry must have been uploaded first.
+    void Render(VertexBuffer* buffer, const PODVector<UIBatch>& batches, unsigned batchStart, unsigned batchEnd);
     /// Generate batches from an UI element recursively. Skip the cursor element.
     void GetBatches(PODVector<UIBatch>& batches, PODVector<float>& vertexData, UIElement* element, IntRect currentScissor);
     /// Return UI element at global screen coordinates. Return position converted to element's screen coordinates.

+ 9 - 4
Source/Urho3D/UI/UIComponent.cpp

@@ -45,9 +45,9 @@ static int const UICOMPONENT_DEFAULT_TEXTURE_SIZE = 512;
 static int const UICOMPONENT_MIN_TEXTURE_SIZE = 64;
 static int const UICOMPONENT_MAX_TEXTURE_SIZE = 4096;
 
-UIComponent::UIComponent(Context* context)
-    : Component(context)
-    , isStaticModelOwned_(false)
+UIComponent::UIComponent(Context* context) : 
+    Component(context),
+    isStaticModelOwned_(false)
 {
     vertexBuffer_ = new VertexBuffer(context_);
     debugVertexBuffer_ = new VertexBuffer(context_);
@@ -128,7 +128,12 @@ bool UIComponent::ScreenToUIPosition(IntVector2 screenPos, IntVector2& result)
     if (!scene)
         return false;
 
-    Viewport* viewport = scene->GetViewport();
+    Renderer* renderer = GetSubsystem<Renderer>();
+    if (!renderer)
+        return false;
+
+    // \todo Always uses the first viewport, in case there are multiple
+    Viewport* viewport = renderer->GetViewportForScene(scene, 0);
     Octree* octree = scene->GetComponent<Octree>();
 
     if (!viewport || !octree)