Node Graph with Urho UI

Announcements and news updates to your projects using Urho3D.
Forum rules
If you have images or videos, you can attach them in the showcase post. Please use small thumbnail as link to original media resources.
When you post in this forum, you have to agree that Urho3D main website as well as the Facebook page have the permission to republish your post with all the related images or videos. The original images must have width larger than 890px for best viewing result in all the supported viewports by main website.

Node Graph with Urho UI

PostPosted by godan » 28 May 2016, 14:54

There's been a lot of talk about replacing the native Urho UI. However, after spending some serious time with it, I have to say, it has grown on me! I think a critical change was the recent decision to make UIBatch vertex data public. This meant I could create a little BezierCurve class that inherits from Sprite:

Image

Nothing really special yet, but it does show that possibilities! Here is my UICurve class (Disclaimer: code might be ugly - I wrote this pretty quickly). One quick request: It would be great if UIBatch::AddQuad had a version that accepts four corner vertices (that way you can draw pretty much any arbitrary shape).

Code: Select all
#pragma once
#include "AppIncludes.h"
#include <Urho3D/UI/Sprite.h>
#include "Urho3D/UI/UIBatch.h"
using namespace Urho3D;

class URHO3D_API UICurve : public Sprite
{
   URHO3D_OBJECT(UICurve, Sprite)
public:
   UICurve(Context* context);
   ~UICurve() {};
   ///segments for cuve rendering
   int segments_ = 50;
   ///thickness
   float thickness_ = 3.0f;
   ///flag for redrawing curve
   bool isDirty = true;
   ///define controls pts
   Vector<Vector3> controlPoints;
   /// Return UI rendering batches.
   virtual void GetBatches(PODVector<UIBatch>& batches, PODVector<float>& vertexData, const IntRect& currentScissor);
   void AddCurveSegment(UIBatch& batch, Vector3 a, Vector3 b, Vector3 c, Vector3 d);
   ///fills the path verts vectors from the control points
   void BezierCurve(int segments);
   ///Helper function that offsets the path verts and fills the quads vector
   void OffsetCurve(float thickness);
   ///pts defining segmented curve
   Vector<Vector3> pathVerts;
   ///quads that render the curve, by offsetting or filling it
   Vector<Vector3> pathQuads;
   ///update path and quads
   void UpdatePath();
   ///Set and end point
   void SetEndPoint(Vector3 newPos, bool moveTangent=true);
   ///Set start point
   void SetStartPoint(Vector3 newPos, bool moveTangent = true);
   ///custom update logic
   virtual void Update(float timeStep);
};


Code: Select all
#include "UICurve.h"

UICurve::UICurve(Context* context) : Sprite(context)
{
   context->RegisterFactory<UICurve>("UI");
};

void UICurve::GetBatches(PODVector<UIBatch>& batches, PODVector<float>& vertexData, const IntRect& currentScissor)
{
   if (pathQuads.Size() < 4)
      return;
   
   bool allOpaque = true;
   if (GetDerivedOpacity() < 1.0f || color_[C_TOPLEFT].a_ < 1.0f || color_[C_TOPRIGHT].a_ < 1.0f ||
      color_[C_BOTTOMLEFT].a_ < 1.0f || color_[C_BOTTOMRIGHT].a_ < 1.0f)
      allOpaque = false;

   const IntVector2& size = GetSize();
   UIBatch
      batch(this, blendMode_ == BLEND_REPLACE && !allOpaque ? BLEND_ALPHA : blendMode_, currentScissor, texture_, &vertexData);

   //iterate over pathQuads and push to batch
   for (int i = 0; i < pathQuads.Size()/4; i ++)
   {
      //adjust ordering for render
      Vector3 a = pathQuads[4 * i];
      Vector3 b = pathQuads[4 * i + 1];
      Vector3 c = pathQuads[4 * i + 2];
      Vector3 d = pathQuads[4 * i + 3];

      AddCurveSegment(batch, a, b, c, d);
   }

   UIBatch::AddOrMerge(batch, batches);

   // Reset hovering for next frame
   hovering_ = false;
}

void UICurve::Update(float timeStep)
{

}

void UICurve::UpdatePath()
{
   BezierCurve(segments_);
   OffsetCurve(thickness_);
}

void UICurve::SetStartPoint(Vector3 newPos, bool moveTangent)
{
   if (controlPoints.Size() < 4)
   {
      URHO3D_LOGERROR("Control points have not been initialize.");
      return;
   }

   //get move vec
   Vector3 vec = newPos - controlPoints[0];
   controlPoints[0] = newPos;

   if (moveTangent)
   {
      controlPoints[1] += vec;
   }

   UpdatePath();
}

void UICurve::SetEndPoint(Vector3 newPos, bool moveTangent)
{
   if (controlPoints.Size() < 4)
   {
      URHO3D_LOGERROR("Control points have not been initialize.");
      return;
   }

   //get move vec
   Vector3 vec = newPos - controlPoints[3];
   controlPoints[3] = newPos;

   if (moveTangent)
   {
      controlPoints[2] += vec;
   }

   UpdatePath();
}

void UICurve::BezierCurve(int segments)
{
   int num_segments = segments;
   segments_ = segments;
   float t_step = 1.0f / (float)num_segments;

   //check that we have the right number of control points
   if (controlPoints.Size() < 4)
   {
      URHO3D_LOGERROR("Not enough control points for Bezier curve");
      return;
   }

   //clear the path verts
   pathVerts.Clear();

   //push the first point
   pathVerts.Push(controlPoints[0]);

   for (int i_step = 1; i_step <= num_segments; i_step++)
   {
      float t = t_step * i_step;
      float u = 1.0f - t;
      float w1 = u*u*u;
      float w2 = 3 * u*u*t;
      float w3 = 3 * u*t*t;
      float w4 = t*t*t;

      Vector3 nextVert(w1*controlPoints[0].x_ + w2*controlPoints[1].x_ + w3*controlPoints[2].x_ + w4*controlPoints[3].x_,
         w1*controlPoints[0].y_ + w2*controlPoints[1].y_ + w3*controlPoints[2].y_ + w4*controlPoints[3].y_);

      //push the next pt
      pathVerts.Push(nextVert);
   }

   //mark the curve for redraw
   isDirty = true;
}

void UICurve::OffsetCurve(float thickness)
{
   //check that path verts has a least two points
   if (pathVerts.Size() < 2)
   {
      URHO3D_LOGERROR("Not enough path verts for curve offset");
      return;
   }

   thickness_ = thickness;

   //clear
   pathQuads.Clear();

   //create the perp lines
   //ordering is

   /*************************************************************
   
   0          1
   |          |
   |----------|---------- etc
   |          |
   2          3
   
   *************************************************************/

   for (int i = 0; i < pathVerts.Size()-1; i++)
   {
      if (i == 0)
      {
         Vector3 dirVec = pathVerts[i + 1] - pathVerts[i];
         Vector3 perpVec(dirVec.y_, -dirVec.x_, 0);
         perpVec.Normalize();

         Vector3 p1 = pathVerts[i] + thickness * perpVec;
         Vector3 p2 = pathVerts[i] - thickness * perpVec;
         Vector3 p3 = pathVerts[i + 1] + thickness * perpVec;
         Vector3 p4 = pathVerts[i + 1] - thickness * perpVec;

         //push
         pathQuads.Push(p1);
         pathQuads.Push(p3);
         pathQuads.Push(p2);
         pathQuads.Push(p4);

      }
      
      else if (i == pathVerts.Size() - 2)
      {
         Vector3 dirVec = pathVerts[i] - pathVerts[i - 1];
         Vector3 perpVec(dirVec.y_, -dirVec.x_, 0);
         perpVec.Normalize();


         Vector3 p1 = pathVerts[i] + thickness * perpVec;
         Vector3 p2 = pathVerts[i] - thickness * perpVec;
         Vector3 p3 = pathVerts[i + 1] + thickness * perpVec;
         Vector3 p4 = pathVerts[i + 1] - thickness * perpVec;

         //push
         pathQuads.Push(p1);
         pathQuads.Push(p3);
         pathQuads.Push(p2);
         pathQuads.Push(p4);
         

      }

      else
      {
         Vector3 dirVec = pathVerts[i + 1] - pathVerts[i];
         Vector3 perpVec(dirVec.y_, -dirVec.x_, 0);
         perpVec.Normalize();


         Vector3 p1 = pathVerts[i] + thickness * perpVec;
         Vector3 p2 = pathVerts[i] - thickness * perpVec;
         Vector3 p3 = pathVerts[i + 1] + thickness * perpVec;
         Vector3 p4 = pathVerts[i + 1] - thickness * perpVec;

         //push
         pathQuads.Push(p1);
         pathQuads.Push(p3);
         pathQuads.Push(p2);
         pathQuads.Push(p4);
      }
   }
}

//void UICurve::BezierCurve(UIBatch& batch, Vector3 a, Vector3 b, Vector3 c, Vector3 d)
//{
//   int num_segments = 50;
//   float t_step = 1.0f / (float)num_segments;
//
//   Vector3 v1, v2, v3, v4;
//   v1 = a;
//   for (int i_step = 1; i_step <= num_segments; i_step++)
//   {
//      float t = t_step * i_step;
//      float u = 1.0f - t;
//      float w1 = u*u*u;
//      float w2 = 3 * u*u*t;
//      float w3 = 3 * u*t*t;
//      float w4 = t*t*t;
//
//      v2 = Vector3(w1*a.x_ + w2*b.x_ + w3*c.x_ + w4*d.x_, w1*a.y_ + w2*b.y_ + w3*c.y_ + w4*d.y_);
//      
//      //URHO3D_LOGINFO("bezier pt: " + String(b));
//
//      v3 = v2 + Vector3(0, 20, 0);
//      v4 = v1 + Vector3(0, 20, 0);
//
//      AddCurveSegment(batch, v1, v2, v4, v3);
//      v1 = v2;
//
//
//   }
//}

void UICurve::AddCurveSegment(UIBatch& batch, Vector3 a, Vector3 b, Vector3 c, Vector3 d)
{
   //args
   int x = 0;
   int y = 0;
   const IntVector2& size = GetSize();
   int width = size.x_;
   int height = size.y_;
   Matrix3x4 transform = GetTransform();
   int texOffsetX = imageRect_.left_;
   int texOffsetY = imageRect_.top_;
   int texWidth = imageRect_.right_ - imageRect_.left_;
   int texHeight = imageRect_.bottom_ - imageRect_.top_;

   //URHO3D_LOGINFO("a: " + String(a) + ",b: " + String(b) + ",c: " + String(c) + ",d: " + String(d));
   

   //logic
   unsigned topLeftColor, topRightColor, bottomLeftColor, bottomRightColor;

   if (!batch.useGradient_)
   {
      // If alpha is 0, nothing will be rendered, so do not add the quad
      if (!(batch.color_ & 0xff000000))
         return;

      topLeftColor = batch.color_;
      topRightColor = batch.color_;
      bottomLeftColor = batch.color_;
      bottomRightColor = batch.color_;
   }
   else
   {
      topLeftColor = batch.GetInterpolatedColor(x, y);
      topRightColor = batch.GetInterpolatedColor(x + width, y);
      bottomLeftColor = batch.GetInterpolatedColor(x, y + height);
      bottomRightColor = batch.GetInterpolatedColor(x + width, y + height);
   }

/*   Vector3 v1 = (transform * Vector3((float)x, (float)y, 0.0f)) - batch.posAdjust;
   Vector3 v2 = (transform * Vector3((float)x + (float)width, (float)y, 0.0f)) - batch.posAdjust;
   Vector3 v3 = (transform * Vector3((float)x, (float)y + (float)height, 0.0f)) - batch.posAdjust;
   Vector3 v4 = (transform * Vector3((float)x + (float)width, (float)y + (float)height, 0.0f)) - batch.posAdjust*/;

   Vector3 v1 = (transform * a) - batch.posAdjust;
   Vector3 v2 = (transform * b) - batch.posAdjust;
   Vector3 v3 = (transform * c) - batch.posAdjust;
   Vector3 v4 = (transform * d) - batch.posAdjust;

   float leftUV = 0.0f; // ((float)texOffsetX) * batch.invTextureSize_.x_;
   float topUV = 0.0f;// ((float)texOffsetY) * batch.invTextureSize_.y_;
   float rightUV = 1.0f;// ((float)(texOffsetX + (texWidth ? texWidth : width))) *batch.invTextureSize_.x_;
   float bottomUV = 1.0f;// ((float)(texOffsetY + (texHeight ? texHeight : height))) * batch.invTextureSize_.y_;

   unsigned begin = batch.vertexData_->Size();
   batch.vertexData_->Resize(begin + 6 * UI_VERTEX_SIZE);
   float* dest = &(batch.vertexData_->At(begin));
   batch.vertexEnd_ = batch.vertexData_->Size();

   dest[0] = v1.x_;
   dest[1] = v1.y_;
   dest[2] = 0.0f;
   ((unsigned&)dest[3]) = topLeftColor;
   dest[4] = leftUV;
   dest[5] = topUV;

   dest[6] = v2.x_;
   dest[7] = v2.y_;
   dest[8] = 0.0f;
   ((unsigned&)dest[9]) = topRightColor;
   dest[10] = rightUV;
   dest[11] = topUV;

   dest[12] = v3.x_;
   dest[13] = v3.y_;
   dest[14] = 0.0f;
   ((unsigned&)dest[15]) = bottomLeftColor;
   dest[16] = leftUV;
   dest[17] = bottomUV;

   dest[18] = v2.x_;
   dest[19] = v2.y_;
   dest[20] = 0.0f;
   ((unsigned&)dest[21]) = topRightColor;
   dest[22] = rightUV;
   dest[23] = topUV;

   dest[24] = v4.x_;
   dest[25] = v4.y_;
   dest[26] = 0.0f;
   ((unsigned&)dest[27]) = bottomRightColor;
   dest[28] = rightUV;
   dest[29] = bottomUV;

   dest[30] = v3.x_;
   dest[31] = v3.y_;
   dest[32] = 0.0f;
   ((unsigned&)dest[33]) = bottomLeftColor;
   dest[34] = leftUV;
   dest[35] = bottomUV;
}

User avatar
godan
Active user
Active user
 
Posts: 122
Joined: 23 Sep 2014, 20:28

Re: Node Graph with Urho UI

PostPosted by dragonCASTjosh » 29 May 2016, 09:38

awesome work :) do you have example code for the whole node graph and not just the curved connectors because i was looking into making a values shader editor
User avatar
dragonCASTjosh
Moderator
Moderator
 
Posts: 205
Joined: 04 Aug 2015, 18:59

Re: Node Graph with Urho UI

PostPosted by godan » 30 May 2016, 13:24

Hey thanks! I actually do have a fully functional node graph working, but I'm not sure if I'm going to open source it (still undecided...). I will definitely post the WIP version soon, though.

Meanwhile, here are some tabbed node graphs in a docked window...Having a bit of trouble getting the UI transparency layers to work. ScrollView in particular is a bit troublesome.

Image
User avatar
godan
Active user
Active user
 
Posts: 122
Joined: 23 Sep 2014, 20:28


Return to Showcase

Who is online

Users browsing this forum: No registered users and 0 guests

cron