Wednesday 29 May 2013

Graph Collector

After releasing new DirectX11 pack for vvvv and beta 30 : http://vvvv.org/contribution/directx11-nodes-alpha, Time to go back into dev.

I added new Segment Node, which allows Z extrusion. It's pretty nice playing with Phase/Cycles/Resolution you can do some nice designs, here are couple of shots below (no fancy shading yet).





So now issue with designing it, i have heap lot of shader nodes, layers and groups, so it's a big patch to maintain.

Exporting it as file would be handy, so I could just design my bits and use a file loader, fast switch, clean patch, mmmmhhh :)

So first thought was collada, but it's such a crap format, way too big for what it does.

I'd have to store vertices for every bit + transform, projected file size: 10 megs at least, bleh...

So second thought was to create module for every node, and have it output relevant data as string as well as render it, so cumbersome, bleh again...

Then last thought, oh I got dx11 source code (since I wrote it), so i should be able to traverse the graph and store data instead of render (and then serialize as any format like json, no xml please ;)

Issues:

  • I don't want to store vertices/indices, takes too much space. I already know what geometry I use, so it would be better to just tell how to generate it again.
  • Most 3d formats suck for batching, in my case I can have the same geometry sent 10 times to shader, most 3d software would replicate geometry (come on, please start to do proper format one day), Instead it would be much easier to tell geometry-> transform list.
So for the moment once geometry is created I got no way to know how it was built.

So I simply added this in geometry provider

Code Snippet
  1. string PrimitiveType { get; set; }
  2.  
  3. object Tag { get; set; }

Then for each type of geometry I create a descriptor, here is the base;

Code Snippet
  1. public abstract class AbstractPrimitiveDescriptor
  2. {
  3.     public abstract string PrimitiveType { get; }
  4.     public abstract void Initialize(Dictionary<string, object> properties);
  5.     public abstract IDX11Geometry GetGeometry(DX11RenderContext context);
  6. }

Then here is sphere:

Code Snippet
  1. public class Sphere : AbstractPrimitiveDescriptor
  2. {
  3.     public float Radius { get; set; }
  4.     public int ResX { get; set; }
  5.     public int ResY { get; set; }
  6.     public float CyclesX { get; set; }
  7.     public float CyclesY { get; set; }
  8.  
  9.     public override string PrimitiveType { get { return "Sphere"; } }
  10.  
  11.     public override void Initialize(Dictionary<string, object> properties)
  12.     {
  13.         this.Radius = (float)properties["Radius"];
  14.         this.ResX = (int)properties["ResX"];
  15.         this.ResY = (int)properties["ResY"];
  16.         this.CyclesX = (float)properties["CyclesX"];
  17.         this.CyclesY = (float)properties["CyclesY"];
  18.     }
  19.  
  20.     public override IDX11Geometry GetGeometry(DX11RenderContext context)
  21.     {
  22.         return context.Primitives.Sphere(this);
  23.     }
  24. }

That's it, instead of storing 1000 vertices i can just store 5 parameters instead, how cool is that?

When I create geometry, I just assign the descriptor to the geometry, so I can know at any time how things were created.

Now that's cool, but I need to grab geometry, so here is how it works.
Layer have RenderSettings which carry some info on how to render, so I simply added collector as render hint.

Code Snippet
  1. public enum eRenderHint { Forward, MRT, Shadow, Overlay, Collector }

Then settings carry:

Code Snippet
  1.  
  2. public class DX11ObjectGroup
  3. {
  4.     public DX11ObjectGroup()
  5.     {
  6.         this.RenderObjects = new List<DX11RenderObject>();
  7.     }
  8.  
  9.     public string ShaderName { get; set; }
  10.  
  11.     public List<DX11RenderObject> RenderObjects { get; set; }
  12. }
  13.  
  14. public class DX11RenderObject
  15. {
  16.     public string ObjectType { get; set; }
  17.  
  18.     public object Descriptor { get; set; }
  19.  
  20.     public Matrix[] Transforms { get; set; }
  21. }

So renderer only has to set it's flag to collector (tells shader that we don't want to render but collect information).

Shader then can just retrieve descriptor, and push it with list of associated transforms (huhu, batch friendly ;)

Little snippet for shader node, if render hint is set to collect it just appends object/transform instead of render.

Code Snippet
  1. IDX11Geometry g = this.FGeometry[0][context];
  2. if (g.Tag != null)
  3. {
  4.     DX11RenderObject o = new DX11RenderObject();
  5.     o.ObjectType = g.PrimitiveType;
  6.     o.Descriptor = g.Tag;
  7.  
  8.     o.Transforms = new Matrix[spmax];
  9.     for (int i = 0; i < this.spmax; i++)
  10.     {
  11.         o.Transforms[i] = this.mworld[i % this.mworldcount];
  12.     }
  13.     group.RenderObjects.Add(o);
  14.  
  15.     settings.ObjectCollector.Add(group);
  16. }

Object list is then serialized as Json Object, here is one part of it:

Code Snippet
  1. {
  2.   "ObjectType": "SegmentZ",
  3.   "Descriptor": {
  4.     "Phase": 0.0,
  5.     "Cycles": 0.25,
  6.     "InnerRadius": 0.94,
  7.     "Z": 0.03,
  8.     "Resolution": 100
  9.   },
  10.   "Transforms": [
  11.     "-1.43, 1.751187E-16, 0, 0, -1.751187E-16, -1.43, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1",
  12.     "1.43, 0, 0, 0, 0, 1.43, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1"
  13.   ]
  14. },

Now I can simply reload the whole scene from the file, (for example above, whole scene fits in 33kb indented, 22kb compressed, pretty nice ;)

So next was just doing a Loader node for testing, pretty simple, retrieve descriptor, create geom, associate with transform and render. Please note that could do an importer for anything, it's not only 4v related (technically I got a use case for it, top secret ;)

The obvious great thing is it's totally transparent, I only need to enable collect, and I can retrieve whole scene, no need for any fancy stuff, and works for any part I have already done as well, no mod needed.

Here are few example of it:




That's it, now of course will need improvements, but pretty excited about possibilities. Having source code for render system is quite invaluable, without it would have done the dirty module way, brrr....scary!!



No comments:

Post a Comment