Browse Source

Merge remote-tracking branch 'remotes/friesencr/editor-viewports'

Lasse Öörni 12 years ago
parent
commit
4e1a38b0a1

+ 4 - 1
Bin/Data/Scripts/Editor/EditorGizmo.as

@@ -210,7 +210,10 @@ void UseGizmo()
     IntVector2 pos = ui.cursorPosition;
     if (ui.GetElementAt(pos) !is null)
         return;
-    Ray cameraRay = camera.GetScreenRay(float(pos.x) / graphics.width, float(pos.y) / graphics.height);
+    IntRect view = activeViewport.viewport.rect;
+    Ray cameraRay = camera.GetScreenRay(
+        float(pos.x-view.left) / view.width,
+        float(pos.y-view.top) / view.height);
     float scale = gizmoNode.scale.x;
 
     // Recalculate axes only when not left-dragging

+ 2 - 0
Bin/Data/Scripts/Editor/EditorScene.as

@@ -101,6 +101,7 @@ bool ResetScene()
     ResetCamera();
     CreateGizmo();
     CreateGrid();
+    SetActiveViewport(viewports[0]);
 
     return true;
 }
@@ -208,6 +209,7 @@ bool LoadScene(const String&in fileName)
     ResetCamera();
     CreateGizmo();
     CreateGrid();
+    SetActiveViewport(viewports[0]);
 
     return loaded;
 }

+ 36 - 3
Bin/Data/Scripts/Editor/EditorToolBar.as

@@ -64,6 +64,22 @@ void CreateToolBar()
     fillModeGroup.AddChild(CreateToolBarToggle("FillSolid"));
     FinalizeGroupHorizontal(fillModeGroup, "ToolBarToggle");
     toolBar.AddChild(fillModeGroup);
+
+    toolBar.AddChild(CreateToolBarSpacer(4));
+    DropDownList@ viewportMode = DropDownList();
+    viewportMode.style = AUTO_STYLE;
+    viewportMode.SetMaxSize(100, 18);
+    viewportMode.SetAlignment(HA_LEFT, VA_CENTER);
+    toolBar.AddChild(viewportMode);
+    viewportMode.AddItem(CreateViewPortModeText("Single", VIEWPORT_SINGLE));
+    viewportMode.AddItem(CreateViewPortModeText("Vertical Split", VIEWPORT_LEFT|VIEWPORT_RIGHT));
+    viewportMode.AddItem(CreateViewPortModeText("Horizontal Split", VIEWPORT_TOP|VIEWPORT_BOTTOM));
+    viewportMode.AddItem(CreateViewPortModeText("Quad", VIEWPORT_TOP_LEFT|VIEWPORT_TOP_RIGHT|VIEWPORT_BOTTOM_LEFT|VIEWPORT_BOTTOM_RIGHT));
+    viewportMode.AddItem(CreateViewPortModeText("1 Top / 2 Bottom", VIEWPORT_TOP|VIEWPORT_BOTTOM_LEFT|VIEWPORT_BOTTOM_RIGHT));
+    viewportMode.AddItem(CreateViewPortModeText("2 Top / 1 Bottom", VIEWPORT_TOP_LEFT|VIEWPORT_TOP_RIGHT|VIEWPORT_BOTTOM));
+    viewportMode.AddItem(CreateViewPortModeText("1 Left / 2 Right", VIEWPORT_LEFT|VIEWPORT_TOP_RIGHT|VIEWPORT_BOTTOM_RIGHT));
+    viewportMode.AddItem(CreateViewPortModeText("2 Left / 1 Right", VIEWPORT_TOP_LEFT|VIEWPORT_BOTTOM_LEFT|VIEWPORT_RIGHT));
+    SubscribeToEvent(viewportMode, "ItemSelected", "ToolBarSetViewportMode");
 }
 
 Button@ CreateToolBarButton(const String&in title)
@@ -309,7 +325,7 @@ void ToolBarFillModePoint(StringHash eventType, VariantMap& eventData)
     if (edit.checked)
     {
         fillMode = FILL_POINT;
-        camera.fillMode = fillMode;
+        SetFillMode(fillMode);
     }
     toolBarDirty = true;
 }
@@ -320,7 +336,7 @@ void ToolBarFillModeWireFrame(StringHash eventType, VariantMap& eventData)
     if (edit.checked)
     {
         fillMode = FILL_WIREFRAME;
-        camera.fillMode = fillMode;
+        SetFillMode(fillMode);
     }
     toolBarDirty = true;
 }
@@ -331,11 +347,19 @@ void ToolBarFillModeSolid(StringHash eventType, VariantMap& eventData)
     if (edit.checked)
     {
         fillMode = FILL_SOLID;
-        camera.fillMode = fillMode;
+        SetFillMode(fillMode);
     }
     toolBarDirty = true;
 }
 
+void ToolBarSetViewportMode(StringHash eventType, VariantMap& eventData)
+{
+    DropDownList@ dropDown = eventData["Element"].GetUIElement();
+    UIElement@ selected = dropDown.GetItems()[dropDown.selection];
+    uint mode = selected.vars["VIEW_MODE"].GetUInt();
+    SetViewportMode(mode);
+}
+
 void UpdateDirtyToolBar()
 {
     if (toolBar is null || !toolBarDirty)
@@ -453,3 +477,12 @@ void UpdateDirtyToolBar()
 
     toolBarDirty = false;
 }
+
+Text@ CreateViewPortModeText(String text_, uint mode)
+{
+    Text@ text = Text();
+    text.text = text_;
+    text.vars["VIEW_MODE"] = mode;
+    text.style = "EditorEnumAttributeText";
+    return text;
+}

+ 2 - 1
Bin/Data/Scripts/Editor/EditorUI.as

@@ -72,6 +72,7 @@ void CreateUI()
     CreateStatsBar();
     CreateConsole();
     CreateDebugHud();
+    CreateViewportUI();
 
     SubscribeToEvent("ScreenMode", "ResizeUI");
     SubscribeToEvent("MenuSelected", "HandleMenuSelected");
@@ -1203,7 +1204,7 @@ void HandleKeyDown(StringHash eventType, VariantMap& eventData)
                 fillMode = FILL_SOLID;
 
             // Update camera fill mode
-            camera.fillMode = fillMode;
+            SetFillMode(fillMode);
         }
         else if (key == KEY_SPACE)
         {

+ 571 - 14
Bin/Data/Scripts/Editor/EditorView.as

@@ -6,6 +6,44 @@ Camera@ camera;
 Node@ gridNode;
 CustomGeometry@ grid;
 
+UIElement@ viewportUI; // holds the viewport ui, convienent for clearing and hiding
+uint setViewportCursor = 0; // used to set cursor in post update
+uint resizingBorder = 0; // current border that is dragging
+uint viewportMode = VIEWPORT_SINGLE;
+int  viewportBorderOffset = 2; // used to center borders over viewport seams,  should be half of width
+int  viewportBorderWidth = 4; // width of a viewport resize border
+IntRect viewportArea(0, 0, graphics.width, graphics.height); // the area where the editor viewport is. if we ever want to have the viewport not take up the whole screen this abstracts that
+IntRect viewportUIClipBorder = IntRect(27, 60, 0, 0); // used to clip viewport borders, the borders are ugly when going behind the transparent toolbars
+
+const uint VIEWPORT_BORDER_H     = 0x00000001;
+const uint VIEWPORT_BORDER_H1    = 0x00000002;
+const uint VIEWPORT_BORDER_H2    = 0x00000004;
+const uint VIEWPORT_BORDER_V     = 0x00000010;
+const uint VIEWPORT_BORDER_V1    = 0x00000020;
+const uint VIEWPORT_BORDER_V2    = 0x00000040;
+
+const uint VIEWPORT_SINGLE       = 0x00000000;
+const uint VIEWPORT_TOP          = 0x00000100;
+const uint VIEWPORT_BOTTOM       = 0x00000200;
+const uint VIEWPORT_LEFT         = 0x00000400;
+const uint VIEWPORT_RIGHT        = 0x00000800;
+const uint VIEWPORT_TOP_LEFT     = 0x00001000;
+const uint VIEWPORT_TOP_RIGHT    = 0x00002000;
+const uint VIEWPORT_BOTTOM_LEFT  = 0x00004000;
+const uint VIEWPORT_BOTTOM_RIGHT = 0x00008000;
+
+// not used for setting for easier testing
+const uint VIEWPORT_BORDER_H_ANY = 0x00000007;
+const uint VIEWPORT_BORDER_V_ANY = 0x00000070;
+const uint VIEWPORT_SPLIT_H      = 0x0000f300;
+const uint VIEWPORT_SPLIT_V      = 0x0000fc00;
+const uint VIEWPORT_SPLIT_HV     = 0x0000f000;
+const uint VIEWPORT_TOP_ANY      = 0x00003300;
+const uint VIEWPORT_BOTTOM_ANY   = 0x0000c200;
+const uint VIEWPORT_LEFT_ANY     = 0x00005400;
+const uint VIEWPORT_RIGHT_ANY    = 0x0000c800;
+const uint VIEWPORT_QUAD         = 0x0000f000;
+
 enum EditMode
 {
     EDIT_MOVE = 0,
@@ -27,6 +65,49 @@ enum SnapScaleMode
     SNAP_SCALE_QUARTER
 }
 
+// holds info about a viewport such as camera settings and splits up shared resources
+class ViewportContext
+{
+    float cameraYaw = 0;
+    float cameraPitch = 0;
+    Camera@ camera;
+    Node@ cameraNode;
+    SoundListener@ soundListener;
+    Viewport@ viewport;
+    bool enabled = false;
+    uint index = 0;
+    uint viewportId = 0;
+
+    ViewportContext(IntRect viewRect, uint index_, uint viewportId_)
+    {
+        cameraNode = Node();
+        camera = cameraNode.CreateComponent("Camera");
+        camera.fillMode = fillMode;
+        soundListener = cameraNode.CreateComponent("SoundListener");
+        viewport = Viewport(editorScene, camera, viewRect);
+        index = index_;
+        viewportId = viewportId_;
+        camera.viewMask = 0x80000000 + 1 << index; // its easier to only have 1 gizmo active this viewport is shared with the gizmo
+    }
+
+    void ResetCamera()
+    {
+        cameraNode.position = Vector3(0, 5, -10);
+        // Look at the origin so user can see the scene.
+        cameraNode.rotation = Quaternion(Vector3(0, 0, 1), -cameraNode.position);
+        ReacquireCameraYawPitch();
+    }
+
+    void ReacquireCameraYawPitch()
+    {
+        cameraYaw = cameraNode.rotation.yaw;
+        cameraPitch = cameraNode.rotation.pitch;
+    }
+}
+
+Array<ViewportContext@> viewports;
+ViewportContext@ activeViewport;
+
 Text@ editorModeText;
 Text@ renderStatsText;
 Text@ cameraPosText;
@@ -36,11 +117,11 @@ AxisMode axisMode = AXIS_WORLD;
 FillMode fillMode = FILL_SOLID;
 SnapScaleMode snapScaleMode = SNAP_SCALE_FULL;
 
+float cameraYaw = 0;
+float cameraPitch = 0;
 float cameraBaseSpeed = 10;
 float cameraBaseRotationSpeed = 0.2;
 float cameraShiftSpeedMultiplier = 5;
-float cameraYaw = 0;
-float cameraPitch = 0;
 float newNodeDistance = 20;
 float moveStep = 0.5;
 float rotateStep = 5;
@@ -101,30 +182,472 @@ Array<String> fillModeText = {
 
 void CreateCamera()
 {
+    SetViewportMode(VIEWPORT_SINGLE, false);
+    SetActiveViewport(viewports[0]);
+
     // Note: the camera is not inside the scene, so that it is not listed, and does not get deleted
-    cameraNode = Node();
-    camera = cameraNode.CreateComponent("Camera");
-    audio.listener = cameraNode.CreateComponent("SoundListener");
     ResetCamera();
 
-    renderer.viewports[0] = Viewport(editorScene, camera);
-
     SubscribeToEvent("PostRenderUpdate", "HandlePostRenderUpdate");
     SubscribeToEvent("UIMouseClick", "ViewMouseClick");
+    SubscribeToEvent("MouseMove", "ViewMouseMove");
+}
+
+// create any ui associated with changing the editor viewports
+void CreateViewportUI()
+{
+    if (viewportUI is null)
+    {
+        viewportUI = UIElement();
+        ui.root.AddChild(viewportUI);
+        viewportUI.SetFixedSize(viewportArea.width, viewportArea.height);
+        viewportUI.position = IntVector2(viewportArea.top, viewportArea.left);
+        viewportUI.clipChildren = true;
+        viewportUI.clipBorder = viewportUIClipBorder;
+    }
+
+    viewportUI.RemoveAllChildren();
+    
+    Array<BorderImage@> borders;
+
+    IntRect top;
+    IntRect bottom;
+    IntRect left;
+    IntRect right;
+    IntRect topLeft;
+    IntRect topRight;
+    IntRect bottomLeft;
+    IntRect bottomRight;
+
+    for(uint i=0; i<viewports.length; i++)
+    {
+        ViewportContext@ vc = viewports[i];
+        if (vc.viewportId & VIEWPORT_TOP > 0)
+            top = vc.viewport.rect;
+        else if (vc.viewportId & VIEWPORT_BOTTOM > 0)
+            bottom = vc.viewport.rect;
+        else if (vc.viewportId & VIEWPORT_LEFT > 0)
+            left = vc.viewport.rect;
+        else if (vc.viewportId & VIEWPORT_RIGHT > 0)
+            right = vc.viewport.rect;
+        else if (vc.viewportId & VIEWPORT_TOP_LEFT > 0)
+            topLeft = vc.viewport.rect;
+        else if (vc.viewportId & VIEWPORT_TOP_RIGHT > 0)
+            topRight = vc.viewport.rect;
+        else if (vc.viewportId & VIEWPORT_BOTTOM_LEFT > 0)
+            bottomLeft = vc.viewport.rect;
+        else if (vc.viewportId & VIEWPORT_BOTTOM_RIGHT > 0)
+            bottomRight = vc.viewport.rect;
+    }
+
+    // creates resize borders based on the mode set
+    if (viewportMode == VIEWPORT_QUAD) // independent borders for quad isn't easy
+    {
+        borders.Push(CreateViewportDragBorder(VIEWPORT_BORDER_V, topLeft.right-viewportBorderOffset, topLeft.top, viewportBorderWidth, viewportArea.height));
+        borders.Push(CreateViewportDragBorder(VIEWPORT_BORDER_H, topLeft.left, topLeft.bottom-viewportBorderOffset, viewportArea.width, viewportBorderWidth));
+    }
+    else
+    {
+        // figures what borders to create based on mode
+        if (viewportMode & (VIEWPORT_LEFT|VIEWPORT_RIGHT) > 0)
+        {
+            borders.Push(
+                    viewportMode & VIEWPORT_LEFT > 0 ? 
+                        CreateViewportDragBorder(VIEWPORT_BORDER_V, left.right-viewportBorderOffset, left.top, viewportBorderWidth, left.height) : 
+                        CreateViewportDragBorder(VIEWPORT_BORDER_V, right.left-viewportBorderOffset, right.top, viewportBorderWidth, right.height)
+                    );
+        }
+        else 
+        {
+            if (viewportMode & (VIEWPORT_TOP_LEFT|VIEWPORT_TOP_RIGHT) > 0)
+            {
+                borders.Push(CreateViewportDragBorder(VIEWPORT_BORDER_V1, topLeft.right-viewportBorderOffset, topLeft.top, viewportBorderWidth, topLeft.height));
+            }
+            if (viewportMode & (VIEWPORT_BOTTOM_LEFT|VIEWPORT_BOTTOM_RIGHT) > 0)
+            {
+                borders.Push(CreateViewportDragBorder(VIEWPORT_BORDER_V2, bottomLeft.right-viewportBorderOffset, bottomLeft.top, viewportBorderWidth, bottomLeft.height));
+            }
+        }
+
+        if (viewportMode & (VIEWPORT_TOP|VIEWPORT_BOTTOM) > 0)
+        {
+            borders.Push(
+                    viewportMode & VIEWPORT_TOP > 0 ?
+                        CreateViewportDragBorder(VIEWPORT_BORDER_H, top.left, top.bottom-viewportBorderOffset, top.width, viewportBorderWidth) :
+                        CreateViewportDragBorder(VIEWPORT_BORDER_H, bottom.left, bottom.top-viewportBorderOffset, bottom.width, viewportBorderWidth)
+                    );
+        }
+        else 
+        {
+            if (viewportMode & (VIEWPORT_TOP_LEFT|VIEWPORT_BOTTOM_LEFT) > 0)
+            {
+                borders.Push(CreateViewportDragBorder(VIEWPORT_BORDER_H1, topLeft.left, topLeft.bottom-viewportBorderOffset, topLeft.width, viewportBorderWidth));
+            }
+            if (viewportMode & (VIEWPORT_TOP_RIGHT|VIEWPORT_BOTTOM_RIGHT) > 0)
+            {
+                borders.Push(CreateViewportDragBorder(VIEWPORT_BORDER_H2, topRight.left, topRight.bottom-viewportBorderOffset, topRight.width, viewportBorderWidth));
+            }
+        }
+    }
+}
+
+BorderImage@ CreateViewportDragBorder(uint value, int posX, int posY, int sizeX, int sizeY)
+{
+    BorderImage@ border = BorderImage();
+    viewportUI.AddChild(border);
+    border.name = "border";
+    border.enabled = true;
+    border.style = "ViewportBorder";
+    border.vars["VIEWMODE"] = value;
+    border.SetFixedSize(sizeX, sizeY); // relevant size gets set by viewport later
+    border.position = IntVector2(posX, posY);
+    border.opacity = uiMaxOpacity;
+    SubscribeToEvent(border, "DragMove", "HandleViewportBorderDragMove");
+    SubscribeToEvent(border, "DragEnd", "HandleViewportBorderDragEnd");
+    return border;
+}
+
+void SetFillMode(FillMode fillMode_)
+{
+    fillMode = fillMode_;
+    for (uint i = 0; i < viewports.length; i++)
+        viewports[i].camera.fillMode = fillMode_;
+}
+
+
+// Sets the viewport mode
+// the updateUI is to get around a load order issue.  cameras load before the ui
+void SetViewportMode(uint mode = VIEWPORT_SINGLE, bool updateUI = true)
+{
+    viewports.Clear();
+    viewportMode = mode;
+
+    // always have quad a
+    {
+        uint viewport = 0;
+        ViewportContext@ vc = ViewportContext(
+            IntRect(
+                0,
+                0,
+                mode & (VIEWPORT_LEFT|VIEWPORT_TOP_LEFT) > 0 ? viewportArea.width/2 : viewportArea.width,
+                mode & (VIEWPORT_TOP|VIEWPORT_TOP_LEFT) > 0 ? viewportArea.height/2 : viewportArea.height)
+            , viewports.length+1
+            , viewportMode & (VIEWPORT_TOP|VIEWPORT_LEFT|VIEWPORT_TOP_LEFT)
+        );
+        viewports.Push(vc);
+    }
+
+    uint topRight = viewportMode & (VIEWPORT_RIGHT|VIEWPORT_TOP_RIGHT);
+    if (topRight > 0)
+    {
+        ViewportContext@ vc = ViewportContext(
+            IntRect(
+                viewportArea.width/2,
+                0,
+                viewportArea.width,
+                mode & VIEWPORT_TOP_RIGHT > 0 ? viewportArea.height/2 : viewportArea.height)
+            , viewports.length+1
+            , topRight
+        );
+        viewports.Push(vc);
+    }
+
+    uint bottomLeft = viewportMode & (VIEWPORT_BOTTOM|VIEWPORT_BOTTOM_LEFT);
+    if (bottomLeft > 0)
+    {
+        ViewportContext@ vc = ViewportContext(
+            IntRect(
+                0,
+                viewportArea.height/2,
+                mode & (VIEWPORT_BOTTOM_LEFT) > 0 ? viewportArea.width/2 : viewportArea.width,
+                viewportArea.height)
+            , viewports.length+1
+            , bottomLeft
+        );
+        viewports.Push(vc);
+    }
+
+    uint bottomRight = viewportMode & (VIEWPORT_BOTTOM_RIGHT);
+    if (bottomRight > 0)
+    {
+        ViewportContext@ vc = ViewportContext(
+            IntRect(
+                viewportArea.width/2,
+                viewportArea.height/2,
+                viewportArea.width,
+                viewportArea.height
+            )
+            , viewports.length+1
+            , bottomRight
+        );
+        viewports.Push(vc);
+    }
+
+    renderer.numViewports = viewports.length;
+    for (uint i=0; i<viewports.length; i++)
+        renderer.viewports[i] = viewports[i].viewport;
+
+    if (updateUI)
+        CreateViewportUI();
+}
+
+void HandleViewportBorderDragMove(StringHash eventType, VariantMap& eventData)
+{
+
+    UIElement@ dragBorder = eventData["Element"].GetUIElement();
+    if (dragBorder is null)
+        return;
+
+    uint hPos;
+    uint vPos;
+
+    // moves border to new cursor position, restricts motion to 1 axis, and keeps borders within view area
+    if (resizingBorder & VIEWPORT_BORDER_V_ANY > 0)
+    {
+        hPos = Clamp(ui.cursorPosition.x, 150, viewportArea.width-150);
+        vPos = dragBorder.position.y;
+        dragBorder.position = IntVector2(hPos, vPos);
+    }
+    if (resizingBorder & VIEWPORT_BORDER_H_ANY > 0)
+    {
+        vPos = Clamp(ui.cursorPosition.y, 150, viewportArea.height-150);
+        hPos = dragBorder.position.x;
+        dragBorder.position = IntVector2(hPos, vPos);
+    }
+
+    // move all dependent borders
+    Array<UIElement@> borders = viewportUI.GetChildren();
+    for (uint i = 0; i < borders.length; i++)
+    {
+        BorderImage@ border = borders[i];
+        if (border is null || border is dragBorder || border.name != "border")
+            continue;
+
+        uint borderViewMode = border.vars["VIEWMODE"].GetUInt();
+        if (resizingBorder == VIEWPORT_BORDER_H)
+        {
+            if (borderViewMode == VIEWPORT_BORDER_V1)
+            {
+                border.SetFixedHeight(vPos);
+            }
+            else if (borderViewMode == VIEWPORT_BORDER_V2)
+            {
+                border.position = IntVector2(border.position.x, vPos);
+                border.SetFixedHeight(viewportArea.height - vPos);
+            }
+        }
+        else if (resizingBorder == VIEWPORT_BORDER_V)
+        {
+            if (borderViewMode == VIEWPORT_BORDER_H1)
+            {
+                border.SetFixedWidth(hPos);
+            }
+            else if (borderViewMode == VIEWPORT_BORDER_H2)
+            {
+                border.position = IntVector2(hPos, border.position.y);
+                border.SetFixedWidth(viewportArea.width - hPos);
+            }
+        }
+    }
+}
+
+void HandleViewportBorderDragEnd(StringHash eventType, VariantMap& eventData)
+{
+    // sets the new viewports by checking all the dependencies
+
+    Array<UIElement@> children = viewportUI.GetChildren();
+    Array<BorderImage@> borders;
+
+    BorderImage@ borderV;
+    BorderImage@ borderV1;
+    BorderImage@ borderV2;
+    BorderImage@ borderH;
+    BorderImage@ borderH1;
+    BorderImage@ borderH2;
+
+    for(uint i = 0; i<children.length; i++)
+    {
+        if (children[i].name == "border")
+        {
+            BorderImage@ border = children[i];
+            uint mode = border.vars["VIEWMODE"].GetUInt();
+            if (mode == VIEWPORT_BORDER_V)
+                borderV = border;
+            else if (mode == VIEWPORT_BORDER_V1)
+                borderV1 = border;
+            else if (mode == VIEWPORT_BORDER_V2)
+                borderV2 = border;
+            else if (mode == VIEWPORT_BORDER_H)
+                borderH = border;
+            else if (mode == VIEWPORT_BORDER_H1)
+                borderH1 = border;
+            else if (mode == VIEWPORT_BORDER_H2)
+                borderH2 = border;
+        }
+    }
+
+    IntRect top;
+    IntRect bottom;
+    IntRect left;
+    IntRect right;
+    IntRect topLeft;
+    IntRect topRight;
+    IntRect bottomLeft;
+    IntRect bottomRight;
+
+    for(uint i=0; i<viewports.length; i++)
+    {
+        ViewportContext@ vc = viewports[i];
+        if (vc.viewportId & VIEWPORT_TOP > 0)
+            top = vc.viewport.rect;
+        else if (vc.viewportId & VIEWPORT_BOTTOM > 0)
+            bottom = vc.viewport.rect;
+        else if (vc.viewportId & VIEWPORT_LEFT > 0)
+            left = vc.viewport.rect;
+        else if (vc.viewportId & VIEWPORT_RIGHT > 0)
+            right = vc.viewport.rect;
+        else if (vc.viewportId & VIEWPORT_TOP_LEFT > 0)
+            topLeft = vc.viewport.rect;
+        else if (vc.viewportId & VIEWPORT_TOP_RIGHT > 0)
+            topRight = vc.viewport.rect;
+        else if (vc.viewportId & VIEWPORT_BOTTOM_LEFT > 0)
+            bottomLeft = vc.viewport.rect;
+        else if (vc.viewportId & VIEWPORT_BOTTOM_RIGHT > 0)
+            bottomRight = vc.viewport.rect;
+    }
+
+    if (borderV !is null)
+    {
+        if (viewportMode & VIEWPORT_LEFT > 0)
+            left.right = borderV.position.x+viewportBorderOffset;
+        if (viewportMode & VIEWPORT_TOP_LEFT > 0)
+            topLeft.right = borderV.position.x+viewportBorderOffset;
+        if (viewportMode & VIEWPORT_TOP_RIGHT > 0)
+            topRight.left = borderV.position.x+viewportBorderOffset;
+        if (viewportMode & VIEWPORT_RIGHT > 0)
+            right.left = borderV.position.x+viewportBorderOffset;
+        if (viewportMode & VIEWPORT_BOTTOM_LEFT > 0)
+            bottomLeft.right = borderV.position.x+viewportBorderOffset;
+        if (viewportMode & VIEWPORT_BOTTOM_RIGHT > 0)
+            bottomRight.left = borderV.position.x+viewportBorderOffset;
+    }
+    else
+    {
+        if (borderV1 !is null)
+        {
+            if (viewportMode & VIEWPORT_TOP_LEFT > 0)
+                topLeft.right = borderV1.position.x+viewportBorderOffset;
+            if (viewportMode & VIEWPORT_TOP_RIGHT > 0)
+                topRight.left = borderV1.position.x+viewportBorderOffset;
+        }
+        if (borderV2 !is null)
+        {
+            if (viewportMode & VIEWPORT_BOTTOM_LEFT > 0)
+                bottomLeft.right = borderV2.position.x+viewportBorderOffset;
+            if (viewportMode & VIEWPORT_BOTTOM_RIGHT > 0)
+                bottomRight.left = borderV2.position.x+viewportBorderOffset;
+        }
+    }
+
+    if (borderH !is null)
+    {
+        if (viewportMode & VIEWPORT_TOP > 0)
+            top.bottom = borderH.position.y+viewportBorderOffset;
+        if (viewportMode & VIEWPORT_TOP_LEFT > 0)
+            topLeft.bottom = borderH.position.y+viewportBorderOffset;
+        if (viewportMode & VIEWPORT_BOTTOM_LEFT > 0)
+            bottomLeft.top = borderH.position.y+viewportBorderOffset;
+        if (viewportMode & VIEWPORT_BOTTOM > 0)
+            bottom.top = borderH.position.y+viewportBorderOffset;
+        if (viewportMode & VIEWPORT_TOP_RIGHT > 0)
+            topRight.bottom = borderH.position.y+viewportBorderOffset;
+        if (viewportMode & VIEWPORT_BOTTOM_RIGHT > 0)
+            bottomRight.top = borderH.position.y+viewportBorderOffset;
+    }
+    else
+    {
+        if (borderH1 !is null)
+        {
+            if (viewportMode & VIEWPORT_TOP_LEFT > 0)
+                topLeft.bottom = borderH1.position.y+viewportBorderOffset;
+            if (viewportMode & VIEWPORT_BOTTOM_LEFT > 0)
+                bottomLeft.top = borderH1.position.y+viewportBorderOffset;
+        }
+        if (borderH2 !is null)
+        {
+            if (viewportMode & VIEWPORT_TOP_RIGHT > 0)
+                topRight.bottom = borderH2.position.y+viewportBorderOffset;
+            if (viewportMode & VIEWPORT_BOTTOM_RIGHT > 0)
+                bottomRight.top = borderH2.position.y+viewportBorderOffset;
+        }
+    }
+
+    // applies the calculated changes
+    for(uint i=0; i<viewports.length; i++)
+    {
+        ViewportContext@ vc = viewports[i];
+        if (vc.viewportId & VIEWPORT_TOP > 0)
+            vc.viewport.rect = top;
+        else if (vc.viewportId & VIEWPORT_BOTTOM > 0)
+            vc.viewport.rect = bottom;
+        else if (vc.viewportId & VIEWPORT_LEFT > 0)
+            vc.viewport.rect = left;
+        else if (vc.viewportId & VIEWPORT_RIGHT > 0)
+            vc.viewport.rect = right;
+        else if (vc.viewportId & VIEWPORT_TOP_LEFT > 0)
+            vc.viewport.rect = topLeft;
+        else if (vc.viewportId & VIEWPORT_TOP_RIGHT > 0)
+            vc.viewport.rect = topRight;
+        else if (vc.viewportId & VIEWPORT_BOTTOM_LEFT > 0)
+            vc.viewport.rect = bottomLeft;
+        else if (vc.viewportId & VIEWPORT_BOTTOM_RIGHT > 0)
+            vc.viewport.rect = bottomRight;
+    }
+
+    // end drag state
+    resizingBorder = 0;
+    setViewportCursor = 0;
+}
+
+void SetViewportCursor()
+{
+    if (setViewportCursor & VIEWPORT_BORDER_V_ANY > 0)
+        ui.cursor.shape = CS_RESIZEHORIZONTAL;
+    else if (setViewportCursor & VIEWPORT_BORDER_H_ANY > 0)
+        ui.cursor.shape = CS_RESIZEVERTICAL;
+}
+
+void SetActiveViewport(ViewportContext@ context)
+{
+    // sets the global variables to the current context
+
+    @cameraNode = context.cameraNode;
+    @camera = context.camera;
+    @audio.listener = context.soundListener;
+    cameraYaw = context.cameraYaw;
+    cameraPitch = context.cameraPitch;
+
+    // camera is created before gizmo, this gets called again after ui is created
+    if (gizmo !is null)
+        gizmo.viewMask = camera.viewMask;
+
+    @activeViewport = context;
+
+    // if a mode is changed while in a drag or hovering over a border these can get out of sync
+    resizingBorder = 0;
+    setViewportCursor = 0;
 }
 
 void ResetCamera()
 {
-    cameraNode.position = Vector3(0, 5, -10);
-    // Look at the origin so user can see the scene.
-    cameraNode.rotation = Quaternion(Vector3(0, 0, 1), -cameraNode.position);
-    ReacquireCameraYawPitch();
+    for(uint i=0; i<viewports.length; i++)
+        viewports[i].ResetCamera();
 }
 
 void ReacquireCameraYawPitch()
 {
-    cameraYaw = cameraNode.rotation.yaw;
-    cameraPitch = cameraNode.rotation.pitch;
+    for(uint i=0; i<viewports.length; i++)
+        viewports[i].ReacquireCameraYawPitch();
 }
 
 void CreateGrid()
@@ -413,6 +936,18 @@ void UpdateView(float timeStep)
         if (moved)
             UpdateNodeAttributes();
     }
+
+    // if not dragging
+    if (resizingBorder == 0)
+    {
+        UIElement@ uiElement = ui.GetElementAt(ui.cursorPosition);
+        if (uiElement !is null && uiElement.vars.Contains("VIEWMODE"))
+        {
+            setViewportCursor = uiElement.vars["VIEWMODE"].GetUInt();
+            if (input.mouseButtonDown[MOUSEB_LEFT])
+                resizingBorder = setViewportCursor;
+        }
+    }
 }
 
 void SteppedObjectManipulation(int key)
@@ -517,9 +1052,31 @@ void HandlePostRenderUpdate()
     if (octreeDebug && editorScene.octree !is null)
         editorScene.octree.DrawDebugGeometry(true);
 
+    if (setViewportCursor | resizingBorder > 0)
+    {
+        SetViewportCursor();
+        if (resizingBorder == 0)
+            setViewportCursor = 0;
+    }
+
     ViewRaycast(false);
 }
 
+void ViewMouseMove()
+{
+    // setting mouse position based on mouse position
+    if (ui.focusElement !is null || input.mouseButtonDown[MOUSEB_LEFT|MOUSEB_MIDDLE|MOUSEB_RIGHT])
+        return;
+
+    IntVector2 pos = ui.cursor.position;
+    for(uint i=0;i<viewports.length;i++)
+    {
+        ViewportContext@ vc = viewports[i];
+        if (vc !is activeViewport && vc.viewport.rect.IsInside(pos) == INSIDE)
+            SetActiveViewport(vc);
+    }
+}
+
 void ViewMouseClick()
 {
     ViewRaycast(true);
@@ -563,7 +1120,7 @@ void ViewRaycast(bool mouseClick)
     if (elementAtPos !is null)
         return;
 
-    Ray cameraRay = camera.GetScreenRay(float(pos.x) / graphics.width, float(pos.y) / graphics.height);
+    Ray cameraRay = camera.GetScreenRay(float(pos.x) / viewportArea.width, float(pos.y) / graphics.height);
     Component@ selectedComponent;
 
     if (pickMode != PICK_RIGIDBODIES)

+ 4 - 0
Bin/Data/UI/DefaultStyle.xml

@@ -332,4 +332,8 @@
         <attribute name="Font" value="Font;Fonts/BlueHighway.ttf" />
         <attribute name="Font Size" value="12" />
     </element>
+    <element type="ViewportBorder" style="BorderImage">
+        <attribute name="Image Rect" value="50 5 51 6" />
+        <attribute name="Border" value="0 0 0 0" />
+    </element>
 </elements>