Missing Canvas.Draw(Drawable) overrides

Started by noxabellus, 07 November 2017, 20:22:43

noxabellus

The overrides for Canvas.Draw that would accept any type that implements the Drawable interface are missing. Currently the options are

  • (Shape)
  • (Shape, RenderStates)
  • (Sprite)
  • (Sprite, RenderStates)
  • (Text)
  • (Text, RenderStates)
  • (VertexArray)
  • (VertexArray, RenderStates)
  • (Vertex[], PrimitiveType)
  • (Vertex[], PrimitiveType, RenderStates)
  • (Vertex[], uint start, uint count, PrimitiveType)
  • (Vertex[], uint start, uint count, PrimitiveType, RenderStates)

Ideally I would think this method group would match the standard sfml Window.Draw which instead only includes the following

  • (Drawable)
  • (Drawable, RenderStates)
  • (Vertex[], PrimitiveType)
  • (Vertex[], PrimitiveType, RenderStates)
  • (Vertex[], uint start, uint count, PrimitiveType)
  • (Vertex[], uint start, uint count, PrimitiveType, RenderStates)

I'm also curious as to why this is preferred over something like just supplying a RenderTexture to a Widget, similar to the way you would wrap a RenderTexture in a Sprite. This would avoid the confusing "Draw" - some object into Canvas vs "Draw" - canvas into some RenderTarget. Is there a way I could do this instead?

General advice on extending the Widget system in .net would be really appreciated as well, such as how I might implement a wrapper like I've described, or for example a Widget that uses shader effects.

texus

TGUI.Net doesn't have all the features that exist in TGUI because the C interface used to communicate to TGUI can't do everything. There are some things that are possible but which I just decided not to spend time on them until someone requests them though. But the things you request seems to have technical limitations.

The Canvas.Draw method seems to be difficult. I can't pass a drawable through C due to the way CSFML defines the Drawable class (it actually doesn't even have a Drawable class). I also don't think I can use the trick that SFML.Net uses in RenderWindow. The RenderWindow.Draw(Drawable) function simply calls the Drawable.Draw function which works because it hardcodes that there are only 2 different type of RenderTargets: RenderWindow and RenderTexture. Canvas is neither nor does it have a RenderTarget in the C# code.

QuoteI'm also curious as to why this is preferred over something like just supplying a RenderTexture to a Widget, similar to the way you would wrap a RenderTexture in a Sprite. This would avoid the confusing "Draw" - some object into Canvas vs "Draw" - canvas into some RenderTarget. Is there a way I could do this instead?
I didn't fully understand what you meant by this. Could you elaborate a bit on this, perhaps with some pseudocode of how you want it to work?

QuoteGeneral advice on extending the Widget system in .net would be really appreciated as well
I'm not sure if custom widgets are possible on top of TGUI.Net due to its strong relationship with the c++ code. If you inherit from an existing widget and just add some C# code on top then MAYBE you would be able to have a custom widget. But a widget written entirely in C# is probably not going to work at all.

noxabellus

#2
QuoteI didn't fully understand what you meant by this. Could you elaborate a bit on this, perhaps with some pseudocode of how you want it to work?

Well essentially I'd just like to be able to have something like Picture, but where I can use SFML RenderTexture.Texture as the texture. With sfml Sprite you can

RenderTexture intermediate = new RenderTexture(width, height);
Sprite intermediateContext = new Sprite(intermediate.Texture);
RenderWindow window = new RenderWindow(new VideoMode(width, height), "Title");

while (true) {
  intermediate.draw(someDrawableInstance);
  window.draw(intermediateContext);
}

But of course if I try to do that with "new Picture(intermediate.Texture)" it just has the initial empty Texture from when the Picture was created.

I suppose I can just use a Sprite/RenderTexture wrapper this way, and then render that Sprite via Canvas...it just adds another layer of indirection for me.

-----------------------------------------------

Well, I just checked that idea and found another problem. Canvas does not respect Sprite's Scale property.

Here you can see the results of scaling a Sprite containing a RenderTexture and drawing it to both a Canvas and another RenderTexture, with the RenderTexture on the right giving the expected result while the Canvas on the left shows only a crop of the unscaled Sprite.
https://pasteboard.co/GSCyLjF.png

And here is the code for this test if needed
https://pastebin.com/G8wENY7P

texus

That limitation is coming from the c++ API actually. In C# where every object is similar to a smart pointer in c++, ownership of objects isn't an issue. But in my c++ code it complicates a lot. All textures are owned by the widget itself, but by using an existing texture I would have to use a pointer to a texture. In order to support that I would have to start manually allocating and destroying the textures owned by widget and keep track of whether the widget can delete it or whether the texture is external and has to be kept alive.
It would also fail to work if implemented naively because C# may destroy the texture before the picture is destroyed (since C# doesn't know about the c++ pointer), but I think I just realized why the Sprite class from SFML also stores the texture in C# as well. I would have to do the same thing everywhere textures are used.
I think you will just have to call "picture.Renderer.Texture = intermediate;" every time you update the RenderTexture.

I'll look into the scaling issue later.

noxabellus

Quote from: texus on 08 November 2017, 08:26:06
I think you will just have to call "picture.Renderer.Texture = intermediate;" every time you update the RenderTexture.

Oh I didn't realize I could access that property, that's not so bad then.

texus

SFML.Net seems to implement the Transformable class entirely in C#. This is great for people using just .Net since there is no overhead from calling the C functions, but it means that the c++ code has no idea about the transformable at all. SFML.Net was clearly only written for building C# code on top of it and not for other libraries that hook into the underlying c++ code (although I already figured that out quite some time ago). I managed to fix it though by letting the Canvas in C# apply the transformation on the RenderStates before passing them to c++.

noxabellus

#6
picture.Renderer.Texture = intermediate.Texture; each frame does put the Texture into the Picture, but unfortunately it's upside down (not effected by calling Display() before this or not)

texus

#7
I haven't been able to figure the upside down issue out yet, I'll have another look tomorrow. It definitely seems to be an issue in my code somewhere. I managed to reproduce it in CTGUI (the layer between TGUI and TGUI.Net) now, so at least I will be able to print text in the terminal when debugging tomorrow.

Update: Although I still don't know what the exact issue is, it seems to happen when using the TGUI version shipped with TGUI.Net but not with the latest TGUI version. So I guess I fixed it somewhere in the last 6 weeks without even knowing there was an issue. I'll update TGUI.Net soon, but this can take a few days at least.

texus

No wonder I couldn't figure out the issue, it isn't in my code at all.
It's a bug in SFML, I reported it on their forum: https://en.sfml-dev.org/forums/index.php?topic=22787

texus

The fix has now been merged into the SFML master branch and added to the TGUI.Net download.

texus

I'm a year late, but the Canvas now works as suggested in the first post.
Experimental support for custom widgets has also been added. It's not possible to inherit from existing widgets, but a completely new widget (which could contain existing widgets as member variables) can be written in C# (the new Canvas class is an example of a widget written in C#).