GML Draw particle images from texture page as sprites

gnysek

Member
GM Version: GMS 2.x
Target Platform: Windows and others
Download: http://gmclan.org/uploader/23/particle_texture.yyz
Links: na

Summary:
Draw particle images from texture page as sprites

Tutorial:

Hi. I've noticed recently, that particle shapes are exported together with texture page, on same texture (in GMS 1.4 it was a different one).
upload_2017-9-20_23-59-23.png

So, with little of guessing, I found a way to get texture ids of those particle images :) There's 14 images in total, and pointer of every is 0x16 (hex, 22 as decimal) further in memory after last one.

However, since this address can be different each time (Windows and GFX drivers allocation), we first need to find address of first sprite, then we need to count number of all sprites to get particle address.
Particles are next image after last one from what I found, and there is total of 14 possible images. They are saved on texture in little different order that pointed in manual, but that's not a big problem - an array with proper position, where index == pt_shape_xxx is enough to solve this problem.
We gonna buffer pointers of all those sprites, so we don't need to calculate them each time.

So first, a script to get texture id of particle (you can pass any part shape variable from pt_shape_pixel ... to ... pt_shape_snow)

particle_get_texture(pt_shape);
Code:
/// @desc returns pointer to particle textures, so they can be used with draw vertex functions
//
/// @param pt_shape_type a particle shape from pt_shape_pixel to pt_shape_snow - see GMS manual for more info
//
/// (c) 2017 Piotr 'gnysek' Gnys

   if (!variable_global_exists("__particle_tex_buffer")) {
 
       // ensure particles are on texture pages
       var _pt = part_type_create();
       part_type_destroy(_pt); // free it asap :)
     
       // first get number of existing sprites
       var _frames = 0;
       while(sprite_exists(_frames)) {
           _frames++;
       }
     
       global.__particle_tex_buffer = []; // create array in which we gonna buffer tex pointers
     
       var _pointer_to_first_sprite = real(base_convert(sprite_get_texture(0, 0), 16, 10)); // convert pointer of first sprite to integer number
       var _shape_to_tex = [6, 2, 12, 5, 13, 0, 7, 11, 4, 10, 8, 1, 3, 9]; // a helper array with order of particle sprites
     
       for(var i = 0; i < 14; i++) {
           global.__particle_tex_buffer[i] = ptr(base_convert(_pointer_to_first_sprite + $16 * (_shape_to_tex[i] + _frames), 10, 16)); // convert pointer to first sprite + 0x16 * number of sprites to pointer again
       }
   }
 
   return global.__particle_tex_buffer[clamp(argument0, pt_shape_pixel, pt_shape_snow)]; // return bufferred address

Then, we need one helper function, to convert string containing our pointer to real, and vice-versa. I've used script from GMLscripts.com called:

base_convert:
Code:
/// @desc base_convert(number,oldbase,newbase)
//
//  Returns a string of digits representing the
//  given number converted form one base to another.
//  Base36 is the largest base supported.
//
///      @param number      integer value to be converted, string
///      @param oldbase     base of the given number, integer
///      @param newbase     base of the returned value, integer
//
/// GMLscripts.com/license
{
    var number, oldbase, newbase, out;
    number = string_upper(string(argument0));
    oldbase = argument1;
    newbase = argument2;
    out = "";

    var len, tab;
    len = string_length(number);
    tab = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";

    var i, num;
    for (i=0; i<len; i+=1) {
        num[i] = string_pos(string_char_at(number, i+1), tab) - 1;
    }

    do {
        var divide, newlen;
        divide = 0;
        newlen = 0;
        for (i=0; i<len; i+=1) {
            divide = divide * oldbase + num[i];
            if (divide >= newbase) {
                num[newlen] = divide div newbase;
                newlen += 1;
                divide = divide mod newbase;
            } else if (newlen  > 0) {
                num[newlen] = 0;
                newlen += 1;
            }
        }
        len = newlen;
        out = string_char_at(tab, divide+1) + out;
    } until (len == 0);

    return out;
}

at last, as we cannot use draw_sprite in this case, we need to mimic it a little. I've created script called
draw_texture(x,y,width,height,textureid (will work for sprite_get_texture() too).
Code:
/// @desc draws texture using two vertexes
//
/// @param x1
/// @param x2
/// @param width
/// @param height
/// @param textureid
//
/// (c) 2017 Piotr 'gnysek' Gnys


   var x1 = argument0;
   var y1 = argument1;
 
   var x2 = argument0 + argument2;
   var y2 = argument1 + argument3;
 
   draw_primitive_begin_texture(pr_trianglelist, argument4);
 
       draw_vertex_texture(x1,y1, 0, 0);
       draw_vertex_texture(x2,y1, 1, 0);
       draw_vertex_texture(x1,y2, 0, 1);
     
       draw_vertex_texture(x1,y2, 0, 1);
       draw_vertex_texture(x2,y2, 1, 1);
       draw_vertex_texture(x2,y1, 1, 0);
     
   draw_primitive_end();


So now, we just need to create an object, then write:

Code:
draw_texture(0, 0, 100, 100, particle_get_texture(pt_star));
and we should get:
upload_2017-9-20_23-46-48.png

All particles looks like this:

upload_2017-9-20_23-55-2.png

---
I've tested this example with 3 sprites in game, one of them with two frames - so it may be, that if there's more than one texture page, it won't work.

Also notice, that pt_shape_pixel and pt_shape_line are wider than the others, so they are drawn stretched to square above.

It's a hacky way, so may not always work. If game crashes, try cleaning cache.

I've already reported suggestion to give a proper way to access particle sprites, and use them in draw_sprite() functions too: https://forum.yoyogames.com/index.php?threads/using-particle-sprite.34688/
 
Last edited:
Is it any solution instead of part_type_sprite() function.
I use the custom-sprite-framework extension in my game.so my game have no sprite which is create in the IDE.
But i can get the texture of sprite(adding in the game).
 
All the sprites is adding in the game.and the custom-sprite-framework extension combined all the sprites in only one texture page.
In my game i can draw the sprite(use the extension function),but there is no sprite_index for each sprite.
So i can't use part_type_sprite().
 

gnysek

Member
You can, if you never draw this 1x1 image, this texture page won't be even loaded to memory. GMS2 loads textures on demand. Check docs about texture flushing :)
 

icuurd12b42

TMC Founder
GMC Elder
Interesting in the way of investigating this stuff, quite the wizardry there!!!

...though on a practical basis I don't see where this would be useful. I mean in this particular case the extent to go through to use a hidden sprite which you could technically add it yourself in your sprite resource would be more efficient in so many ways.


One part that intrigues me is you figuring out the texture. a while back I was trying to figure out a way to make a tool that would figure out what texture pages the sprites being drawn came from and try to order the draw calls so everything from the same texture would draw sequentially, ie, draw calls ordered by depth and texture page, I forget why I needed it but this reminded me of that
 

gnysek

Member
but it will swap between custom texture page and particle texture page.
Then create a new project, save above particle sprites using save_sprite function and add it to your game as you wish. No problem anymore, however if you're using built-in particle system - it still swaps to it's texture, you can see all textures in debugger (in GMS2) :)
 
Then create a new project, save above particle sprites using save_sprite function and add it to your game as you wish. No problem anymore, however if you're using built-in particle system - it still swaps to it's texture, you can see all textures in debugger (in GMS2) :)
you did not get what i mean.Thanks anyway.
 
Top