1. Hey! Guest! The 31st GMC Jam will take place between Nov 16th, 12:00 UTC (Friday noon) and Nov 26th, 12:00 UTC (Monday noon). Why not join in! Click here to find out more!
    Dismiss Notice

GML Textures tiling

Discussion in 'Programming' started by Desix, Jun 13, 2018.

  1. Desix

    Desix Member

    Joined:
    Jun 23, 2016
    Posts:
    419
    Is there an alternative way to, for example, texture a primitive with a tiled texture than giving that sprite a separate texture page?

    I'm not sure how much of an issue this can become, especially with a few at once, but I don't like the thought of it.

    So basically: is it an issue, and is there another way?
     
  2. flyingsaucerinvasion

    flyingsaucerinvasion Member

    Joined:
    Jun 20, 2016
    Posts:
    1,738
    Yes. Two ways. Edit: Three.

    1) Draw multiple primitives.

    2) Write a shader that wraps the texture coordinates within a range encoded into an extra vertex attribute.

    3) You could also just literally repeat images in the texture itself.
     
    Last edited: Jun 13, 2018
  3. Desix

    Desix Member

    Joined:
    Jun 23, 2016
    Posts:
    419
    I suspected shaders would be the answer - and way better than drawing more shapes of course.

    That's the main area I'm an extreme n00b with at the moment unfortunately, so if anyone could provide guidance in that area that would be fantastic.
     
  4. flyingsaucerinvasion

    flyingsaucerinvasion Member

    Joined:
    Jun 20, 2016
    Posts:
    1,738
    Using a shader is not necessarily the best option. But since I just had this laying around from a couple of days ago, I'll share it with you. Although, depending on exaclty what you are trying to do, there might be better solutions than this.

    I couldn't think of any way of doing this using the draw_primitive commands, becuase none of the vertex functions had enough components to fit the extra data you will need to get into the shader. Therefore I used a vertex buffer instead.

    Another important thing to note is that if you have sprites or backgrounds or whatever you are using to texture these primitives, and if they are on different texture pages, then you will have to write a different vertex buffer for each texture.

    Writing the vertex buffer:
    Code:
    //============================================================================   
        //vertex format
        vertex_format_begin();
        vertex_format_add_position_3d();  //position of vertex
        vertex_format_add_textcoord();    //texture coordinate of vertex
        vertex_format_add_custom( vertex_type_float4, vertex_usage_textcoord );  //used to wrap texture coordinates around the region defined below
        vf_wall = vertex_format_end();
    //--------------------------------------
        //in this example, spr_thing, sub-image 1 is being repeated across primitives.
        var _sprite = spr_thing;
        tex_wall = sprite_get_texture( _sprite, 0 );
        var _tex_w = texture_get_texel_width(tex_wall);
        var _tex_h = texture_get_texel_height(tex_wall);
    //--------------------------------------   
        //get the texture coordinates of this sub-image
        var _uvs = sprite_get_uvs( _sprite, 0 );
        var _left = _uvs[0];
        var _top = _uvs[1];
        var _right = _uvs[2];
        var _bottom = _uvs[3];
        //-----------------------------
        //these values will be put into the second texture vertex attribute, and will be used in the shader to wrap the texture coordinates around the region defined above
        var _w = _right - _left;  //range
        var _h = _bottom - _top;  //range
        var _ldw = _left / _w;  //left / range
        var _tdh = _top / _h;  //top / range
    //--------------------------------------   
        //create and write to the vertex buffer
        vb_wall = vertex_create_buffer();
        vertex_begin( vb_wall, vf_wall );
        var _vb = vb_wall;
        var _x0, _x1, _y0, _y1, _z0, _z1;
        with (obj_wall) {
            _x0 = x;
            _x1 = x + sprite_width;
            _y0 = y;
            _y1 = y + sprite_height;
            //just drawing a simple rectangle for each instance of obj_wall:
            //and very simply making the texture coordinates based on the position of the vertex.
            vertex_position_3d( _vb, _x0, _y0, 0 );
            vertex_texcoord( _vb, _x0 * _tex_w, _y0 * _tex_h );
            vertex_float4( _vb, _w, _h, _ldw, _tdh );
            //--------
            vertex_position_3d( _vb, _x1, _y0, 0 );
            vertex_texcoord( _vb, _x1 * _tex_w, _y0 * _tex_h );
            vertex_float4( _vb, _w, _h, _ldw, _tdh );
            //--------
            vertex_position_3d( _vb, _x1, _y1, 0 );
            vertex_texcoord( _vb, _x1 * _tex_w, _y1 * _tex_h );
            vertex_float4( _vb, _w, _h, _ldw, _tdh );  
            //--------
            vertex_position_3d( _vb, _x0, _y0, 0 );
            vertex_texcoord( _vb, _x0 * _tex_w, _y0 * _tex_h );
            vertex_float4( _vb, _w, _h, _ldw, _tdh );   
            //--------
            vertex_position_3d( _vb, _x1, _y1, 0 );
            vertex_texcoord( _vb, _x1 * _tex_w, _y1 * _tex_h );
            vertex_float4( _vb, _w, _h, _ldw, _tdh );   
            //--------
            vertex_position_3d( _vb, _x0, _y1, 0 );
            vertex_texcoord( _vb, _x0 * _tex_w, _y1 * _tex_h );
            vertex_float4( _vb, _w, _h, _ldw, _tdh );                     
        }
        vertex_end( vb_wall );  //finish writing to vertex buffer
        vertex_freeze( vb_wall );  //freezing vertex buffer basically makes it draw faster (but means it cann't be rewritten)
    //============================================================================
    

    To draw the primitives:
    Code:
        shader_set(sh_repeat_textures);
        vertex_submit( vb_wall, pr_trianglelist, tex_wall );
        shader_reset();
    

    And the shader used to repeat the textures:
    vertex shader:
    Code:
        attribute vec3 in_Position;
        attribute vec2 in_Textcoord0;
        attribute vec4 in_Textcoord1;
        varying vec2 v_vTexcoord;
        varying vec4 v_vRepeat;
        void main(){
            vec4 object_space_pos = vec4( in_Position.x, in_Position.y, in_Position.z, 1.0);
            gl_Position = gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION] * object_space_pos;
            v_vTexcoord = in_Textcoord0;
            v_vRepeat = in_Textcoord1;
        ]
    
    fragment shader:
    Code:
        varying vec2 v_vTexcoord;
        varying vec4 v_vRepeat;
        void main(){
            vec2 UV = v_vTexcoord - floor( v_vTexcoord / v_vRepeat.xy - v_vRepeat.zw ) * v_vRepeat.xy;
            gl_FragColor = texture2D( gm_BaseTexture, UV );
        }
    
    So, what is going on in that shader? The vertex shader is almost unchanged from the default shader created by gm when you insert a new shader. The only difference is, an extra varying variable called v_vRepeat is being passed from the vertex to fragment shaders. Also, I've chosen not to use a colour attribute, so there is no v_vColour varying variable. In the fragment shader, the texture coordinates are modified using a kind of clockwork or modular division.
     
  5. Desix

    Desix Member

    Joined:
    Jun 23, 2016
    Posts:
    419
    I had a test around and that does indeed work, I'll play with it before I figure out if It's faster.
    If you have concerns about it being the best method I'll explain what I'm doing:

    I basically have a 2D game where the levels are made out of polygons (which of course need to be textured), and various other objects use repeating sprites since they have customisable properties (like the length of a tree's trunk).

    So far this definitely appears to be a workable alternate method, and would let me keep a sprite set for a certain level on one tex group/sheet rather than separate sheets for every repeating sprite.
     
  6. Nocturne

    Nocturne Friendly Tyrant Forum Staff Admin

    Joined:
    Apr 13, 2016
    Posts:
    6,132
  7. Desix

    Desix Member

    Joined:
    Jun 23, 2016
    Posts:
    419
    @Nocturne Hm yeah I've heard of that being a solution but not been able to find how it's meant to be utilised for tiling like this? (It's not something I've explored before - so apologies if it's staring me in the face).

    Assuming this doesn't necessarily need the shader?
     
  8. Nocturne

    Nocturne Friendly Tyrant Forum Staff Admin

    Joined:
    Apr 13, 2016
    Posts:
    6,132
    It shouldn't need a shader, no... As for its use, I'm honestly not 100% sure myself, I just vaguely remember testing it when the function was first created... :(
     
  9. flyingsaucerinvasion

    flyingsaucerinvasion Member

    Joined:
    Jun 20, 2016
    Posts:
    1,738
    @Desix can you post a picture of what you're trying to accomplish. I have a hunch there migth be some simpler solution.

    I feel like you might actually get better results by just making extra geometry.

    And just to be clear, you were asking about how to repeat part of a texture page across a primitive, right?
     
  10. Desix

    Desix Member

    Joined:
    Jun 23, 2016
    Posts:
    419
    [​IMG]
    This is what I'm doing. The texture just repeats over the shape, textures wouldn;t move wherever the shape is.

    I have a working method of course, but the only issue is needing to give any sprite I want to tile a separate page.
     
  11. flyingsaucerinvasion

    flyingsaucerinvasion Member

    Joined:
    Jun 20, 2016
    Posts:
    1,738
    The only real difference between what you describe there and the shader I posted earlier, is that in your description, the texture coordinates are always based on the present location of the vertices.

    Are these things moving around?

    Are they created and destroyed at random? Or does their number remain unchanged?

    How many different "sprites" will you have as repeating textures?

    Could you replace the polygons with a sprite mask instead?

    I'm definitely leaning toward there being easier ways of doing this, depending on the exact details.
     
  12. Desix

    Desix Member

    Joined:
    Jun 23, 2016
    Posts:
    419
    The texture should not move even depending on where the shape is, so two shapes textures will always line up.

    They aren't moving, but the camera moves around them during gameplay, quite a small view.
    The number doesn't change, unless deactivation counts. There are many at once in a room - and many could be active on screen & overlap.

    Many sprites are able to be used and repeated.

    They can be very large, much larger than the screen in most cases. And when the user is editing these things, they can be zoomed in and out of (much larger view), and they can be changed of course. So I think a dynamic approach might be worth it? Lastly, additional elements are drawn around the edges that may also repeat. Like grass or other edging etc.

    With my current method, I split the polygon into triangles (once) and draw those that are on screen as a triangle list primitive.
     
  13. flyingsaucerinvasion

    flyingsaucerinvasion Member

    Joined:
    Jun 20, 2016
    Posts:
    1,738
    The concern I have is that if there are a large number of these objects, and any of them can change state at random (moving/deleting/creating/changing-shape), how are you going to draw them efficiently? You couldn't really just put them all into a single vertex buffer, because the whole thing will need to be re-written whenever anything changes. You might need to implement some kind of chunking system, to mitigate that problem.
     
  14. Desix

    Desix Member

    Joined:
    Jun 23, 2016
    Posts:
    419
    Ok so it's freaking me out just how much faster it is to use buffers. It's extremely fast and I'm not picking and choosing any specific on-screen triangles to draw. All I'm doing is only drawing the entire shapes that have their bounding box on screen. Currently each shape has it's own buffer. I cant be sure I'm doing this perfectly right now but -

    I didn't expect such a major difference!

    I haven't got it working with the zooming in and out yet, which would indeed require updating them when you move or zoom... not sure how yet to handle it (its fake camera movement in the editor, where the scaling and view offset is just calculated). But normal game play is stellar in comparison.
     
  15. flyingsaucerinvasion

    flyingsaucerinvasion Member

    Joined:
    Jun 20, 2016
    Posts:
    1,738
    How many shapes are there in the whole world?
     
  16. Desix

    Desix Member

    Joined:
    Jun 23, 2016
    Posts:
    419
    The amount is also custom. Could be -/+ 20.
     
  17. flyingsaucerinvasion

    flyingsaucerinvasion Member

    Joined:
    Jun 20, 2016
    Posts:
    1,738
    Do you mean around 20?
     
  18. Desix

    Desix Member

    Joined:
    Jun 23, 2016
    Posts:
    419
    I mean basically you could expect at least 20.
     
  19. flyingsaucerinvasion

    flyingsaucerinvasion Member

    Joined:
    Jun 20, 2016
    Posts:
    1,738
    but how many in the worst case? 100's? 1000's?
     
  20. Desix

    Desix Member

    Joined:
    Jun 23, 2016
    Posts:
    419
    I've yet to add a cap, but I'm sure it'd be lower or equal to 100. I haven't stress tested the current method using buffers. Because there are no caps yet. While there could be so many shapes, those shapes could be made from so many numbers of triangles (the cap for that would need to be a node cap for the shapes).

    So for sake of example lets say 100 shape cap, and 100 shape node cap. Might be lower, but that'll do for now.
     
  21. flyingsaucerinvasion

    flyingsaucerinvasion Member

    Joined:
    Jun 20, 2016
    Posts:
    1,738
    With that few number of shapes, you will probably have no problem drawing them. By the way, vertex buffers should only be rewritten if they need to be.

    And whatever isn't going to change, and which will be drawn in the same way, could be combined into a single vertex buffer. Fore example, if the number of shapes were constant, and their movement was non-existent or deterministic, you could put all of the shapes into one vertex buffer. If their motion is non-deterministic, i.e. basically random, you could actually still probably combine them into one vertex buffer, or else a small number of vertex buffers, and pass in the present position and orientation of each shape using a uniform array. That being said, with as few as 100 shapes, you could probably get away with having each and every one in their own vertex buffer, and still have reasonbly good performance.
     
  22. Desix

    Desix Member

    Joined:
    Jun 23, 2016
    Posts:
    419
    Yeah I'm writing the buffers only when the user changes the shape (so only ever one shape being changed, or all at once if needed).

    Good point about that, I could loop through all shapes for one buffer. I'll definitely mess with it and see how far I can push it. Cheers for your help!
     

Share This Page

  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice