Nodragem

Discussing how to implement Ninepatch Texture (i.e. resizable texture for UI)

Recommended Posts

Hello,

@Deltakosh and I opened an issue on Github to discuss how we could implement NinePatch support for the GUI: https://github.com/BabylonJS/Babylon.js/issues/5315#event-1888275521

This will allow us to have resizeable 2D textures for containers, rectangle, button as demonstrable here (using http://romannurik.github.io/AndroidAssetStudio/nine-patches.html):

ninepatchdemo.gif.7e6da0073c4276b52974c64f66c63ce1.gif

However we could not agree on how the NinePatch texture creation should be handled by BabylonJS; hence we decided to bring the discussion here.

@Deltakosh wants to provide the data to set-up where the image is stretchable/fixed through code, while I promote the idea of using Android and libGDX methods where the data is stored directly in the image.

Set the stretchable parts of the NinePatch in code

The code would look like that:

 BABYLON.GUI.createNinePatch("./img/mytexture.png", SliceLeft, SliceRight, SliceTop, SliceBottom)

While the image look like that:

panel_blue@2x.png.427b69817d2162cd2231d08f18eb5fe1.png

Set the stretchable parts of the NinePatch directly in the texture (as in Android's NinePatch):

The code would look like that:

BABYLON.GUI.createNinePatch("./img/mytexture.9.png")

While the image looks like that (note the black pixels which encode where the image is stretchable):

panel_blue2x.9.png.f25d6974ddb386b80ba269ede4705b5f.png

The left column and the top row of pixels encode where the image is stretchable while the right column and the bottom row of pixels encode the padding for the content (e.g. text). 

Disagreement

I believe that the last solution is elegant as it keeps data separated from code logic, and allows easy editing in a simple image editing app (MS Paint!), however @Deltakosh believes that this is a hacky solution and does not like it very much :D .

 

My suggested solution

I start to think that both methods should be implemented as well as a third method :D Let me explain.

My thinking is that libGDX is a great game engine and they faced the same issues we will face when they implemented NinePatch. And I believe that to copy their solution will be a good place to start.

See: https://github.com/libgdx/libgdx/wiki/Ninepatches 

As I undertstand it from the link above, LibGDX provides three ways to create a NinePatch:

  • Method 1: Set the stretchable part of the NinePatch in code
  • Method 2: Set the stretchable parts of the NinePatch directly in the texture
  • Method 3: Set the stretchable parts of the NinePatch from a TexturePack

As we also would like to implement a TexturePack (Texture Atlas + JSON) support (see https://github.com/BabylonJS/Babylon.js/issues/5314), it is worth checking how libGDX dealt with NinePatch support in their Texture Packer.

Basically, if I understand well, the NinePatch support in TexturePack is dealt with this way in libGDX:

  • the user defines NinePatch directly in the texture using Method 2
  • when the NinePatch texture is packed in a TexturePack, the rows/columns that encode stretching/padding information are removed and the information is placed in the JSON file of the TexturePack
  • when the NinePatch texture is imported from the TexturePack, Method 1 is used to apply the stretching/padding information contained in the JSON at runtime 

Hence, I think that both methods (Method 1 and Method 2) could be supported by BabylonJS for now.

What do you think?

Share this post


Link to post
Share on other sites

hi my friend @Deltakosh

 

vec4 add_popUp(vec3 color,vec4 set ,vec2 pos,float alpha,vec4 oldMat, vec2 vuv){
                      vuv.y = 1.-vuv.y;
                set.x = max(set.x,set.w);
                set.y = max(set.y,set.w);
                set.z = max(set.z,set.w); 

                vec2 pixel_uv = vuv;
                vec2 pixel_pos = pos;

                pixel_pos.x = min(1.-(set.x+set.z+set.w), max(set.x+set.z+set.w,pixel_pos.x));
                pixel_pos.y = min(1.-(set.y+set.z+set.w), max(set.y+set.z+set.w,pixel_pos.y));
                  

                float setcal = set.w;

                float ux  = set.x;
                if(pixel_uv.x > pixel_pos.x) ux = -1.*set.x;
                float uy  = set.y;
                if(pixel_uv.y > pixel_pos.y) uy = -1.*set.y;

                float i = sqrt(pow(pixel_uv.x-1.*pixel_pos.x+ux,2.)
                +pow(pixel_uv.y-1.*pixel_pos.y+uy,2.));

                if(i<set.z +set.w && i>set.z -1.*set.w ){
                    i = (i-set.z+set.w )*(0.5/set.w ) ;
                } else if(i > set.z) i = 1.;else i = 0.;
                i =1.- i;

                 float j = 0. ;

                  if((abs(pixel_uv.x-1.*pixel_pos.x) < -ux  ||
                   abs(pixel_uv.x-1.*pixel_pos.x) <  ux  ||
                   abs(pixel_uv.y-1.*pixel_pos.y) < -uy  ||
                   abs(pixel_uv.y-1.*pixel_pos.y) <  uy  ) 
                   && (  
                   (abs(pixel_uv.y-1.*pixel_pos.y -1.*set.w) <  uy+ set.z  ||    
                   abs(pixel_uv.y-1.*pixel_pos.y+1.*set.w ) <  -uy+ set.z) &&
                   (abs(pixel_uv.x-1.*pixel_pos.x -1.*set.w) <  ux+ set.z  ||    
                   abs(pixel_uv.x-1.*pixel_pos.x+1.*set.w ) <  -ux+ set.z )      
                   )) j = 1.0;
                    else if((abs(pixel_uv.x-1.*pixel_pos.x) < -ux  ||
                   abs(pixel_uv.x-1.*pixel_pos.x) <  ux  ||
                   abs(pixel_uv.y-1.*pixel_pos.y) < -uy  ||
                   abs(pixel_uv.y-1.*pixel_pos.y) <  uy  ) 
                   && (abs(pixel_uv.y-1.*pixel_pos.y +1.*set.w) <  uy+ set.z  ||    
                   abs(pixel_uv.y-1.*pixel_pos.y-1.*set.w ) <  -uy+ set.z) &&
                   (abs(pixel_uv.x-1.*pixel_pos.x +1.*set.w) <  ux+ set.z  ||    
                   abs(pixel_uv.x-1.*pixel_pos.x-1.*set.w ) <  -ux+ set.z )){
                        j=  1.- max(
                     (abs(pixel_uv.x-1.*pixel_pos.x  )-(abs(ux)+set.z -set.w ))*(0.5/set.w) ,
                      (abs(pixel_uv.y-1.*pixel_pos.y  )-(abs(uy)+set.z -set.w ))*(0.5/set.w) );
                   } 
                
                return vec4(max(i,j)*alpha*color+(1.-max(i,j)*alpha)*oldMat.xyz ,1.);  ;
                 }

add_popUp(vec3 color , vec4 set , vec2 pos , float alpha , vec4 oldMat , vec2 vuv)

    color : inner Content Color or texture

    set : vec4( width , height , radius , blur border )

    pos : vec2 (0..1,0..1) 

    alpha : opacity

    oldMat : the last  FragmentColor 

   vuv : you uv system (you can manage that if you wanna scale or make perfect square )

  so that if fully by code  just you can append content by texture

*** notice that

  optimization  - blur - support shadow  - live change in size and border radius and no any texture 

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Recently Browsing   0 members

    No registered users viewing this page.