openjdk-jfx icon indicating copy to clipboard operation
openjdk-jfx copied to clipboard

Coordinate Wrap Effect

Open RoiEXLab opened this issue 5 years ago • 8 comments

I'm sorry if this is the wrong place to ask, but I figured it might be worth trying.

I'm currently creating a concept for a potentially infinitely scrolling map. The trivial approach would be to just calculate how many instances of this map would be visible on the screen and create new instances if necessary and render them each at their respective coordinates. However because all instances would show the exact same content, this would for one create an unnecessary memory and performance overhead and make managing those instances unnecessarily complicated. So I came up with a different approach (that has its own issues) that automatically deals with the wrapping. Basically you wouldn't add the node graph you want to render into the "real" scene at all, but instead you'd create a MeshView with a mesh that is the size of the screen (updated when the screen size changes of course), so that any texture this mesh will be given is streched over the whole screen. But here's the catch: Whenever the node graph we use to render the actual content of the map gets updated, we do a snapshot of the graph, and use the resulting image as texture for our mesh plane. Whenever the user now scrolls all we need to do is to update the UV coordinates of the Mesh, and the graphics card will do the automatic wrapping of the texture for us. This makes this thing far less complex and scales well with even 8k screens, but depending on the update frequency of the map, this might not be responsive enough (haven't actually tried it out so far, but currently I'm only creating the concept).

So here's the other idea I had: Before actually trying the approach above, I was hoping to find an effect that would do this for me. Unfortunately there doesn't seem to be an effect that does exactly that, even if in theory it would be so simple to implement: From my limited understanding Effects are basically shaders, so in my understanding all that would be required for this is a pixel-/fragmentshader that uses the pixel of itself modulo a user defined width/height and the whole wrapping thing would work out of the box. I'm not sure how well this works in combination with transforms, but ideally you'd still be able to apply translations and scaling to the node without having to update the effect separately.

TL;DR: I'm basically asking for an effect that copies a given area from a node and replicates it to fill the rest of the node like a texture. I know that this is a pretty niche request and I would code it myself if Effects were actually extensible public API, but they are not so that's why I'm posting here.

Of course maybe I missed something and there's actually a way to achieve this by combining multiple effects or something, but I'd really like to hear your thoughts on this one.

RoiEXLab avatar Aug 11 '19 13:08 RoiEXLab

So basically, you would like to have a 'reusable' Node, that has multiple parents or at least is visible multiple times at once. Because this is impossible (Node can only carry 1 set of Node attributes), you would like to have a virtual node, backed by real Node, so you get the benefits of scene graph, but with little overhead in case of content reuse.

Have you thought about using a Canvas? You could do a snapshot and then draw it on Canvas multiple times. Or use ImageViews with the snapshot as Image in order to retain scene graph functionality for every node. Unless there are lots of nodes, ImageViews should scale well. Perhaps the combination of the two approaches?

I'm just shooting fom hip here though.

sghpjuikit avatar Aug 12 '19 10:08 sghpjuikit

So basically, you would like to have a 'reusable' Node, that has multiple parents or at least is visible multiple times at once. Because this is impossible (Node can only carry 1 set of Node attributes), you would like to have a virtual node, backed by real Node, so you get the benefits of scene graph, but with little overhead in case of content reuse.

Yes, ideally you wouldn't have to explicitly set the positions of the individual nodes though.

Using a canvas was actually the first thought (the application is currently using swing with custom components and really needs a rewritten rendering, so it's using Graphics2D already), but would unfortunately not quite solve the problem because you'd really just manually draw some image multiple times to the screen, where you can just have the graphics card api (opengl/directx) do the job by just adjusting uv coordinates as explained earlier. ImageView is a good idea, but doesn't really has any benefits over the approach I already came up with either.

So yeah thanks for the suggestions but unfortunately they aren't a super elegant option either.

RoiEXLab avatar Aug 12 '19 12:08 RoiEXLab

I better understand what you would like now. I wanted something similar when I was making a game with 'modular' coordinates, which would allow you co go through screen edges to the other side of the screen. This required me to paint the same content up to 8 times as momentarily (and depending on the size of,) the object could be perceived at multiple locations when passing through the edge. I'm still using the ineffective solution of multiple draw calls, but you might be able to make it work using the DisplacementMap effect which is capable of something similar.

sghpjuikit avatar Aug 14 '19 20:08 sghpjuikit

Yeah that's exactly what I want to achieve. Interesting thought of using DisplacementMap, I saw that effect as well but didn't think too much of it because I only thought of the common use case. In this case the only question that remains is if the cpu can pre-calculate all the pixel mappings fast enough when the window is resized. I mean I guess I could just calculate everything once for a sufficiently large screen, but this might not actually work all the time and is potentially wasting a lot of space.

But this definitely seems like an option worth exploring. Thanks!

RoiEXLab avatar Aug 14 '19 21:08 RoiEXLab

Yeah, the performance may or may not be satisfactory. Last time I was trying to compute the roughly fullHD sized map for the effect, it took forever (seconds). However, if you would be able to simply 'move' around chunks of the map without recomputing it pixel by pixel, then you may be fine. Still it will be big, for 8k, thats 8k pixel count * Float size * reference for both key and value or something, thats big, like big big. If you need to provide 'precomputed' maps for different resolutions packed with the application, that's gonna cost a too.

In my own experiments with the effects creating the map rendered the effect unusable. I was very dissatisfied with the fact that I can not provide my own transformation function instead of a 'texture' in the form of a map, but that may be because it is then passed to the gpu. Still, seems like a poorly implemented effect, suitable for small transformations here and there.

Be sure to post your findings, I am also interested whether the effect can be used effectively.

sghpjuikit avatar Aug 15 '19 12:08 sghpjuikit

Using a canvas would unfortunately not quite solve the problem because you'd really just manually draw...

There is FXGL, which exposes OpenGL to the Canvas or has its own Canvas, not sure. It should have support for custom shaders (JLWGL surely does) and it should be possible to achieve the effect with that. Sounds like the most natural solution, albeit not the easiest to do.

sghpjuikit avatar Aug 15 '19 12:08 sghpjuikit

There is FXGL, which exposes OpenGL to the Canvas or has its own Canvas, not sure.

Any chance you mean JFXGL? Both exist, only the latter one has to do anything with OpenGL, but doesn't seem well maintained at all and incompatible with java 11.

Be sure to post your findings, I am also interested whether the effect can be used effectively.

Without actually having written any real code, here's what my plausibility experiments showed:

  1. What takes up most of the time is memory allocation so reusing a single instance would be key.
  2. The DisplacementMap wants a percentual distance, which makes sense for most applications, but because in my case I want the map to be zoomable, I'd have to recalculate all the values whenever the zoom level changes, which will be likely impractically.

With all of this discussed I might go with a variant of my initial approach: Putting the actual parent node for the map into a stackpane on top of a MeshView. By using this approach I get the benefit of being able to use all the input listeners, and the duplicated versions will still be shown.

RoiEXLab avatar Aug 15 '19 15:08 RoiEXLab

I see. I thought the two libs were one and the same.

I hope it works out for you. Good luck.

sghpjuikit avatar Aug 16 '19 18:08 sghpjuikit