Asset - Extension Fluid Dynamics - GM Studio 1

Dragon47

Member


This thread is for the GM Studio 1 version of Fluid Dynamics.
GM Studio 2 version: https://forum.yoyogames.com/index.php?threads/fluid-dynamics-gm-studio-2.26605/

Download demo: https://goo.gl/weubTn

Marketplace link: https://marketplace.yoyogames.com/assets/5232/fluid-dynamics

This asset provides you with realistic fluid simulation and visualization for your games. It includes a guide, a well-commented example, and documentation for each script making it easy to implement it into your game. The physics and visualization are done with shaders, making the fluid simulation very fast. The asset is highly customizable, letting you tweak the fluids extensively. It can be used to simulate fire, smoke, gas, dye, dust, wind, water droplets, cloud formation, and much more. Development and testing has only been done with the windows VM target and the windows YYC target, you might run into problems if you use this asset with other targets.

Features:
  • Can simulate fire, smoke, gas, dye, dust, wind, water droplets, cloud formation, and much more.
  • Using new mathematics from 2014 making fluid simulation faster than ever before.
  • Instances can be pushed around by the velocity field of the simulation.
  • Custom collision masks from sprites and surfaces.
  • Can be tweaked to imitate versions of explosions and tornadoes.
  • 9 different example implementations of fluids.
  • Can be used in small, large and infinite game worlds.
  • Everything is highly customizable, built on top of a fundamental fluid simulation.
  • Custom shaders.
  • Choose between two shader languages, HLSL 9 and GLSL ES.
  • A guide for implementation.
  • Commented and organized code.
  • Fast and optimized, made with efficiency in mind.
  • Custom time step.
  • Customizable acceleration equation.
  • Optional dissipation of fluid velocity and material.
  • All in GML, HLSL 9, and GLSL ES.


Screenshots:











 
Last edited:

GM029

Member
The screenshots look awesome. I know there's a demo but do you have a video of it in action, or a showcase of games using this system?

That water effect looks amazing.
 
M

Mann_mit_Hut

Guest
Maybe the most sophisticated asset ever made! Congratulations!!

//Edit: habe your already tried mobile?
 
Last edited by a moderator:
T

TheShyestJake

Guest
This makes me actually want to purchase assets *_* This is the most beautiful thing I've ever seen be done in game maker ^_^
 

icuurd12b42

TMC Founder
GMC Elder
Code:
var fwidth = 1024;//fd_rectangle_get_material_width(FluidHandler.fd_rectangle);
var fheight = 1024;//fd_rectangle_get_material_height(FluidHandler.fd_rectangle);
var fwwidth = FluidHandler.world_width;
var fwheight = FluidHandler.world_height;
var f_x_relative = (mouse_x-x) / fwwidth * fwidth;
var f_y_relative = (mouse_y-y) / fwheight * fheight;
if(!surface_exists(FluidHandler.fuild_collision_surf))
{
    FluidHandler.fuild_collision_surf = surface_create(fwidth,fheight)
    show_debug_message( fd_rectangle_get_material_width(FluidHandler.fd_rectangle));
    show_debug_message( fd_rectangle_get_material_height(FluidHandler.fd_rectangle));
}

var surfexists = surface_exists(FluidHandler.fuild_collision_surf);
var fcolor = make_colour_rgb(128,128,0);
draw_set_color(fcolor);
draw_set_alpha(1);
if(surfexists)
{
//show_debug_message("about_to_set");
    fd_rectangle_set_collision_mask_surface(FluidHandler.fd_rectangle, FluidHandler.fuild_collision_surf);
//show_debug_message("set");
    surface_set_target(FluidHandler.fuild_collision_surf);
    draw_clear_alpha(fcolor,0);
    draw_primitive_begin(pr_trianglestrip);
    ct = 0;
 
    for(i = 0; i<global.TerrainCt; ++i)
    {
        var x1 = global.TerrainX[i] / fwwidth * fwidth;
        var y1 = room_height / fwheight * fheight;
        var x2 = global.TerrainX[i] / fwwidth * fwidth;
        var y2 = global.TerrainY[i] / fwheight * fheight;
        draw_vertex_colour(x1,y1,fcolor,1);
        draw_vertex_colour(x2,y2,fcolor,1);
        draw_point(x1,y1);
        draw_point(x2,y2);
        //draw_line(global.TerrainX[i] / fwwidth * fwidth,
        //    global.TerrainY[i]/ fwheight * fheight,
        //    global.TerrainX[i+1] / fwwidth * fwidth,
        //    global.TerrainY[i+1]/ fwheight * fheight);
        ct++;
        if(ct>500)
        {
            draw_primitive_end();
            draw_primitive_begin(pr_trianglestrip);
            draw_vertex_colour(x1,y1,fcolor,1);
            draw_vertex_colour(x2,y2,fcolor,1);
            ct = 0;
        }
    }
    draw_primitive_end();
    surface_reset_target();
    draw_surface(FluidHandler.fuild_collision_surf,0,0);
}
I'm having an issue with fd_rectangle_get_material_height/width always returning 256 though I called fd_rectangle_create with 1024,1024

it made it so my collision mask creation was 256x256, making a coarse collision. I bypassed it by hard coding 1024x1024 for my collision surface.. which does show less coarseness.

Here the 1280x768 screen with the mask 1024x1024 drawn overtop for debugging. Line on screen, trianglelist in surface. It does seem to work, there is still some coarseness. but wal less than before. so I'm not 100% sure here

upload_2017-3-31_19-34-40.png

Also is there a way to shift the content of the rectangle? I'm planning on moving the view around. I need the smoke (and the field) to shift left when I move right.


UPDATE:
I'm getting variable does not exist error with obj_fd_rectangle for sf_world_update when it executes the step function fd_rectangle_update.... after having changes the depth of my controller and the depth of my terrain generator. Not sure the exact conflict, where the controller did not perform a step before a render (surface not ready in step ievent but was ready in the draw...)

Please add all the variables your system uses in the create of obj_fd_rectangle. Safety tip, do not trust variables added on the fly, you should have them predefined in the create. without this one may have issues with YYC as well

I tried to fix the error without touching your system but no go...
create
safe = false;
// Creates a fluid dynamics rectangle.
...

step
safe = false;
if(!surface_exists(fuild_collision_surf)) exit;
safe = true;
fd_rectangle_update(fd_rectangle);
...

draw
if(!surface_exists(fuild_collision_surf)) exit;
if(safe == false) exit;
// Draws things behind the fluid.
...

fuild_collision_surf set to -1 and is created by another object... so syncing issues
so I added the variable to the create of obj_fd_rectangle myself which resolved to issue
--- hmm I typoed fluid for fuild_collision_surf...
 
Last edited:

Dragon47

Member
The screenshots look awesome. I know there's a demo but do you have a video of it in action, or a showcase of games using this system?

That water effect looks amazing.
I might add a video soon. The water in the screenshot with the bug is not part of this asset
 

Dragon47

Member
Code:
var fwidth = 1024;//fd_rectangle_get_material_width(FluidHandler.fd_rectangle);
var fheight = 1024;//fd_rectangle_get_material_height(FluidHandler.fd_rectangle);
var fwwidth = FluidHandler.world_width;
var fwheight = FluidHandler.world_height;
var f_x_relative = (mouse_x-x) / fwwidth * fwidth;
var f_y_relative = (mouse_y-y) / fwheight * fheight;
if(!surface_exists(FluidHandler.fuild_collision_surf))
{
    FluidHandler.fuild_collision_surf = surface_create(fwidth,fheight)
    show_debug_message( fd_rectangle_get_material_width(FluidHandler.fd_rectangle));
    show_debug_message( fd_rectangle_get_material_height(FluidHandler.fd_rectangle));
}

var surfexists = surface_exists(FluidHandler.fuild_collision_surf);
var fcolor = make_colour_rgb(128,128,0);
draw_set_color(fcolor);
draw_set_alpha(1);
if(surfexists)
{
//show_debug_message("about_to_set");
    fd_rectangle_set_collision_mask_surface(FluidHandler.fd_rectangle, FluidHandler.fuild_collision_surf);
//show_debug_message("set");
    surface_set_target(FluidHandler.fuild_collision_surf);
    draw_clear_alpha(fcolor,0);
    draw_primitive_begin(pr_trianglestrip);
    ct = 0;
 
    for(i = 0; i<global.TerrainCt; ++i)
    {
        var x1 = global.TerrainX[i] / fwwidth * fwidth;
        var y1 = room_height / fwheight * fheight;
        var x2 = global.TerrainX[i] / fwwidth * fwidth;
        var y2 = global.TerrainY[i] / fwheight * fheight;
        draw_vertex_colour(x1,y1,fcolor,1);
        draw_vertex_colour(x2,y2,fcolor,1);
        draw_point(x1,y1);
        draw_point(x2,y2);
        //draw_line(global.TerrainX[i] / fwwidth * fwidth,
        //    global.TerrainY[i]/ fwheight * fheight,
        //    global.TerrainX[i+1] / fwwidth * fwidth,
        //    global.TerrainY[i+1]/ fwheight * fheight);
        ct++;
        if(ct>500)
        {
            draw_primitive_end();
            draw_primitive_begin(pr_trianglestrip);
            draw_vertex_colour(x1,y1,fcolor,1);
            draw_vertex_colour(x2,y2,fcolor,1);
            ct = 0;
        }
    }
    draw_primitive_end();
    surface_reset_target();
    draw_surface(FluidHandler.fuild_collision_surf,0,0);
}
I'm having an issue with fd_rectangle_get_material_height/width always returning 256 though I called fd_rectangle_create with 1024,1024

it made it so my collision mask creation was 256x256, making a coarse collision. I bypassed it by hard coding 1024x1024 for my collision surface.. which does show less coarseness.

Here the 1280x768 screen with the mask 1024x1024 drawn overtop for debugging. Line on screen, trianglelist in surface. It does seem to work, there is still some coarseness. but wal less than before. so I'm not 100% sure here

View attachment 8335

Also is there a way to shift the content of the rectangle? I'm planning on moving the view around. I need the smoke (and the field) to shift left when I move right.


UPDATE:
I'm getting variable does not exist error with obj_fd_rectangle for sf_world_update when it executes the step function fd_rectangle_update.... after having changes the depth of my controller and the depth of my terrain generator. Not sure the exact conflict, where the controller did not perform a step before a render (surface not ready in step ievent but was ready in the draw...)

Please add all the variables your system uses in the create of obj_fd_rectangle. Safety tip, do not trust variables added on the fly, you should have them predefined in the create. without this one may have issues with YYC as well

I tried to fix the error without touching your system but no go...
create
safe = false;
// Creates a fluid dynamics rectangle.
...

step
safe = false;
if(!surface_exists(fuild_collision_surf)) exit;
safe = true;
fd_rectangle_update(fd_rectangle);
...

draw
if(!surface_exists(fuild_collision_surf)) exit;
if(safe == false) exit;
// Draws things behind the fluid.
...

fuild_collision_surf set to -1 and is created by another object... so syncing issues
so I added the variable to the create of obj_fd_rectangle myself which resolved to issue
--- hmm I typoed fluid for fuild_collision_surf...
Thank you for all the valuable information! I'll look into it when I have time (atm im on a 2 week uni trip)
 
This is a great asset and I'd love to get it working properly - however it doesn't work at all if your view needs to scroll.

I've tied the fluid rectangle to view_xview/view_hview, and I believe that's what's causing the smearing below. The other option is to set the fluid rectangle to the room_width/room_height, but that causes massive performance issues.

I've spent the last 12 hours trying to create a workaround however I've had no luck and am wondering if you can help at all.

This is what happens if the viewport scrolls



As another side effect, I'm also seeing some intense strobing or flickering on certain sprites. I can't figure out why this is, but I'm guessing it's something to do with the order in which surfaces are drawn?

 

icuurd12b42

TMC Founder
GMC Elder
This is a great asset and I'd love to get it working properly - however it doesn't work at all if your view needs to scroll.

I've tied the fluid rectangle to view_xview/view_hview, and I believe that's what's causing the smearing below. The other option is to set the fluid rectangle to the room_width/room_height, but that causes massive performance issues.

I've spent the last 12 hours trying to create a workaround however I've had no luck and am wondering if you can help at all.

As another side effect, I'm also seeing some intense strobing or flickering on certain sprites. I can't figure out why this is, but I'm guessing it's something to do with the order in which surfaces are drawn?
That why I asked for a shift rectangle feature.

I actually tried to write my own but failed miserably
Code:
/// fd_rectangle_shift(instance id, dx,dy, buffer_surf)
/*
used fd_rectangle_assure_surfaces_exist as a base;

shift the surfaces by dx,dy.
dx and dy are percentages /uyv type values (-1-0-1) since the surfaces may not all be the same size, a ratio allows shifting them all equally
example: //shift by the speed of an object pixel motion converted using the view size
fd_rectangle_shift(instance id, -vspeed/view_wview,-hspeed/view_hview)

buffer_surf is a pre-allocated surface whose size is large enough to handle all the system surfaces, this allows reusing the
    buffer surface for a more efficient method. You must create that surface and ensure it's still valid
 
*/

with (argument0) {
    var dx = argument1,
        dy = argument2,
        buffer = argument3;
    var sw,sh,surf,dx_pix,dy_pix;
    if (surface_exists(sf_pressure))
    {
        surf = sf_pressure;
        sw = surface_get_width(surf);
        sh = surface_get_height(surf);
        dx_pix = dx*sw;
        dy_pix = dy*sh;
  
        surface_set_target(buffer);
        {
            draw_clear_alpha($000000, 0);
            draw_surface(surf,0,0);
        }
        surface_reset_target();
  
        surface_set_target(surf);
        {
            //re-init the surface as when it was done initially
            //draw_clear_alpha($000000, 0);
            //shift
            draw_surface_part(buffer,dx_pix,dy_pix,sw,sh,0,0);
        }
        surface_reset_target();
        //original code in cloned script
  
        //sf_pressure = surface_create(sf_pressure_width, sf_pressure_height);
        //surface_set_target(sf_pressure);
        //draw_clear_alpha($000000, 0);
        //surface_reset_target();
    }
    if (surface_exists(sf_pressure_temporary))
    {
        surf = sf_pressure_temporary;
        sw = surface_get_width(surf);
        sh = surface_get_height(surf);
        dx_pix = dx*sw;
        dy_pix = dy*sh;
  
        surface_set_target(buffer);
        {
            draw_clear_alpha($000000, 0);
            draw_surface(surf,0,0);
        }
        surface_reset_target();
  
        surface_set_target(surf);
        {
            //re-init the surface as when it was done initially
            //draw_clear_alpha($000000, 0);
            //shift
            draw_surface_part(buffer,dx_pix,dy_pix,sw,sh,0,0);
        }
        surface_reset_target();
        //original code in cloned script
        //sf_pressure_temporary = surface_create(sf_pressure_width, sf_pressure_height);
    }
    if (surface_exists(sf_velocity))
    {
        surf = sf_velocity;
        sw = surface_get_width(surf);
        sh = surface_get_height(surf);
        dx_pix = dx*sw;
        dy_pix = dy*sh;
  
        surface_set_target(buffer);
        {
            draw_clear_alpha($000000, 0);
            draw_surface(surf,0,0);
        }
        surface_reset_target();
  
        surface_set_target(surf);
        {
            //re-init the surface as when it was done initially
            draw_clear_alpha($008080, 0);
            //shift
            draw_surface_part(buffer,dx_pix,dy_pix,sw,sh,0,0);
        }
        surface_reset_target();
        //original code in cloned script
        //sf_velocity = surface_create(sf_velocity_width, sf_velocity_height);
        //surface_set_target(sf_velocity);
        //draw_clear_alpha($008080, 0);
        //surface_reset_target();
    }
    if (surface_exists(sf_velocity_temporary))
    {
        surf = sf_velocity_temporary;
        sw = surface_get_width(surf);
        sh = surface_get_height(surf);
        dx_pix = dx*sw;
        dy_pix = dy*sh;
  
        surface_set_target(buffer);
        {
            draw_clear_alpha($000000, 0);
            draw_surface(surf,0,0);
        }
        surface_reset_target();
  
        surface_set_target(surf);
        {
            //re-init the surface as when it was done initially
            //draw_clear_alpha($000000, 0);
            //shift
            draw_surface_part(buffer,dx_pix,dy_pix,sw,sh,0,0);
        }
        surface_reset_target();
        //original code in cloned script
        //sf_velocity_temporary = surface_create(sf_velocity_width, sf_velocity_height);
    }
    if (surface_exists(sf_material_0))
    {
        surf = sf_material_0;
        sw = surface_get_width(surf);
        sh = surface_get_height(surf);
        dx_pix = dx*sw;
        dy_pix = dy*sh;
  
        surface_set_target(buffer);
        {
            draw_clear_alpha($000000, 0);
            draw_surface(surf,0,0);
        }
        surface_reset_target();
  
        surface_set_target(surf);
        {
            //re-init the surface as when it was done initially
            //draw_clear_alpha($000000, 0);
            //shift
            draw_surface_part(buffer,dx_pix,dy_pix,sw,sh,0,0);
        }
        surface_reset_target();
        //original code in cloned script
        //sf_material_0 = surface_create(sf_material_width, sf_material_height);
        //surface_set_target(sf_material_0);
        //draw_clear_alpha($000000, 0);
        //surface_reset_target();
        //material_surface_was_created = true;
    }
    if (surface_exists(sf_material_0_temporary))
    {
        surf = sf_material_0_temporary;
        sw = surface_get_width(surf);
        sh = surface_get_height(surf);
        dx_pix = dx*sw;
        dy_pix = dy*sh;
  
        surface_set_target(buffer);
        {
            draw_clear_alpha($000000, 0);
            draw_surface(surf,0,0);
        }
        surface_reset_target();
  
        surface_set_target(surf);
        {
            //re-init the surface as when it was done initially
            //draw_clear_alpha($000000, 0);
            //shift
            draw_surface_part(buffer,dx_pix,dy_pix,sw,sh,0,0);
        }
        surface_reset_target();
        //original code in cloned script
        //sf_material_0_temporary = surface_create(sf_material_width, sf_material_height);
    }
    if (surface_exists(sf_material_1))
    {
        surf = sf_material_1;
        sw = surface_get_width(surf);
        sh = surface_get_height(surf);
        dx_pix = dx*sw;
        dy_pix = dy*sh;
  
        surface_set_target(buffer);
        {
            //draw_clear_alpha($000000, 0);
            draw_surface(surf,0,0);
        }
        surface_reset_target();
  
        surface_set_target(surf);
        {
            //re-init the surface as when it was done initially
            draw_clear_alpha($000000, 0);
            //shift
            draw_surface_part(buffer,dx_pix,dy_pix,sw,sh,0,0);
        }
        surface_reset_target();
        //original code in cloned script
  
        //sf_material_1 = surface_create(sf_material_width, sf_material_height);
        //surface_set_target(sf_material_1);
        //draw_clear_alpha($000000, 0);
        //surface_reset_target();
    }
    if (surface_exists(sf_material_1_temporary))
    {
        surf = sf_material_1_temporary;
        sw = surface_get_width(surf);
        sh = surface_get_height(surf);
        dx_pix = dx*sw;
        dy_pix = dy*sh;
  
        surface_set_target(buffer);
        {
            draw_clear_alpha($000000, 0);
            draw_surface(surf,0,0);
        }
        surface_reset_target();
  
        surface_set_target(surf);
        {
            //re-init the surface as when it was done initially
            draw_clear_alpha($000000, 0);
            //shift
            draw_surface_part(buffer,dx_pix,dy_pix,sw,sh,0,0);
        }
        surface_reset_target();
        //original code in cloned script
        //sf_material_1_temporary = surface_create(sf_material_width, sf_material_height);
    }
    if (surface_exists(sf_world))
    {
        surf = sf_world;
        sw = surface_get_width(surf);
        sh = surface_get_height(surf);
        dx_pix = dx*sw;
        dy_pix = dy*sh;
  
        surface_set_target(buffer);
        {
            draw_clear_alpha($000000, 0);
            draw_surface(surf,0,0);
        }
        surface_reset_target();
  
        surface_set_target(surf);
        {
            //re-init the surface as when it was done initially
            //draw_clear_alpha($00FFFF, 0)
            //shift
            draw_surface_part(buffer,dx_pix,dy_pix,sw,sh,0,0);
        }
        surface_reset_target();
    }
    //if (!surface_exists(sf_world)) sf_world_update = true;
    //if (sf_world_update) {
    //    if (collision_mask_type == 0) {
    //        sf_world = surface_create(sprite_get_width(collision_mask_sprite_index), sprite_get_height(collision_mask_sprite_index));
    //        surface_set_target(sf_world);
    //            draw_clear_alpha($00FFFF, 0);
    //            draw_sprite(collision_mask_sprite_index, collision_mask_image_index, 0, 0);
    //        surface_reset_target();
    //    } else {
    //        var error_string = "Fluid Dynamics error. An invalid collision mask surface was provided for a fluid dynamics rectangle." + chr(13) + chr(10) + "It could be that the surface spontaneously ceased to exist (because " +
    //                    "of surfaces' volatile behavior)" + chr(13) + chr(10) + "thereby making it invalid. When setting your own collision masks with fd_rectangle_set_collision_mask_surface," + chr(13) + chr(10) +
    //                    "make sure to manually assure their existence each time before calling fd_rectangle_update." + chr(13) + chr(10);
    //        show_debug_message(error_string);
    //        show_error(error_string, false);
    //    }
    //    sf_world_update = false;
    //}
}

Help Script sais
Can I use this asset for a large or even infinite game world?

Yes, you can for example shift the content of the fluid dynamics rectangle as the view moves around, and draw it at the view's position.
This will only make fluid near the view get simulated, allowing for highly detailed fluids even in large or infinite worlds. You can
make the fluid dynamics rectangle slightly bigger than the view size so that fluid outside the view disappears in an elegant way. This
requires knowledge of how surfaces work, and can be a bit tricky, but you can message me (see bottom of guide) for help.
But I have not seen a shift function in the api...


For the moment I simply have 3 tiled rectangles that "walk" along the player, clearing themselves when they switch position. my add smoke and velocity add to all of them, so that if something was added close to be on 2 rectangles they are both set with content.... the side effect is they dont share edges so each simulate independently which may cause a visual edge.
upload_2017-4-2_6-20-11.png

UPDATE
>I'm having an issue with fd_rectangle_get_material_height/width always returning 256 though I called fd_rectangle_create with 1024,1024
I did not notice the code was calling set_XXX_size after the create...

right now I'm struggling with the mask which does not seem to work like it does in the demo... smoke seem to get absorbed by it instead of bouncing off
 
Last edited:
S

SimenGames

Guest
shift rectangle feature
Just talked to ThePhotoshop about this, and we have a solution that might be a bit slow but it works:
Code:
///fd_rectangle_move_information(word to simulation size ratio)

draw_enable_alphablend(false);
with(fd_rectangle){
    //sf_pressure
    surface_set_target(sf_pressure_temporary);
   
        draw_clear_alpha(c_black,0);
        draw_surface(sf_pressure,(other.old_view_xview-view_xview[0])*argument0,(other.old_view_yview-view_yview[0])*argument0);
       
    surface_reset_target();
   
    var t = sf_pressure;
    sf_pressure = sf_pressure_temporary;
    sf_pressure_temporary = t;
   
   
    //sf_velocity
    surface_set_target(sf_velocity_temporary);
   
        draw_clear_alpha($7f7f7f,0.5);
        draw_surface(sf_velocity,(other.old_view_xview - view_xview[0])*argument0,(other.old_view_yview-view_yview[0])*argument0);
       
    surface_reset_target();
   
    var t = sf_velocity;
    sf_velocity = sf_velocity_temporary;
    sf_velocity_temporary = t;
   
    //sf_material_0
    surface_set_target(sf_material_0_temporary);
   
        draw_clear_alpha(c_black,0);
        draw_surface(sf_material_0,(other.old_view_xview-view_xview[0])*argument0,(other.old_view_yview-view_yview[0])*argument0);
       
    surface_reset_target();
   
    var t = sf_material_0;
    sf_material_0 = sf_material_0_temporary;
    sf_material_0_temporary = t;
   
    //sf_material_1
    surface_set_target(sf_material_1_temporary);
   
        draw_clear_alpha(c_black,0);
        draw_surface(sf_material_1,(other.old_view_xview-view_xview[0])*argument0,(other.old_view_yview-view_yview[0])*argument0);
       
    surface_reset_target();
   
    var t = sf_material_1;
    sf_material_1 = sf_material_1_temporary;
    sf_material_1_temporary = t;
}  
   
old_view_xview = view_xview[0];
old_view_yview = view_yview[0];

draw_enable_alphablend(true);
The outline and strobing is caused by the fact that the asset sets texture_set_interpolation to true, so if you dont want this you're going to have to disable it after using the fd rectangle.
 
M

Mann_mit_Hut

Guest
The most efficient way to shift the field is usually modifying the advection shader with an offset.
If he has used the same shader to advect velocity, density and pressure then it's easy and without performance loss.

That's how I did it in my fluidsim.
 

Dragon47

Member
@CreativeBand124 Thank you :)
@SimenGames @icuurd12b42 @ThePhotoshop
Shifting of content can be tricky. The script posted looks correct, I can add a shift script to the asset when I have time (uni trip is going to last a couple of weeks, but I might get time during it).
To speed up the script, you can remove the section that shifts pressure, because the pressure is reset in the beginning of each update step anyway.
And when it comes to sf_material_1, you can try to remove this section as well. If you're using material type RGBA_16, you might want to keep it, but it would be almost the same without because sf_material_1 contains the least significant 8 bits of the 16 bits.
 

icuurd12b42

TMC Founder
GMC Elder
The other thing to consider is adding support for image_blend and image_alpha when drawing the rectangle. My hack:

I'm drawing with the colorize shader which I modified... Because I have multiple instances of the rectangle, I needed to fade and disable the tiles further away to have smooth transitions. I initially rendered to my own surface and faded that but.. extra swap is extra swap...

My draw event, sets the rectangle's image_blend and image alpha
Code:
fd_rectangle.image_alpha = image_alpha;
fd_rectangle.image_blend = global.hilly_color;
fd_rectangle_draw_stretched(fd_rectangle, x, y, world_width, world_height, material_interpolation_enabled);
fd_rectangle.image_alpha = 1;
fd_rectangle.image_blend = c_white;

the fd_rectangle_draw_stretched calls fd_rectangle_draw_part. I modified fd_rectangle_draw_part's case that does the colorize to use the passed image_blend and image_alpha
Code:
case FD_VISUALIZATION_SHADER.COLORIZE:
            shader_set(sh_fd_visualize_colorize);
                draw_surface_part_ext(sf_material_0, argument1, argument2, argument3, argument4, argument5, argument6, argument7, argument8, image_blend, image_alpha);
            shader_reset();
            break;

and modified the sh_fd_visualize_colorize_glsl shader to use the blend and alpha
added in_Color (which has the image_blend and image_alpha), pass through to fragment
Code:
// Passthrough vertex shader.

attribute vec4 in_Position;
attribute vec2 in_TextureCoord;
attribute vec4 in_Colour;                    // (r,g,b,a)
varying vec2 v_vTexcoord;
varying vec4 v_vColour;
void main() {
v_vColour = in_Colour;
    gl_Position = gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION] * in_Position;
    v_vTexcoord = in_TextureCoord;
}
I use a different form of rendering, but this should do the trick to match the original...
Code:
//fragment
// #define color vec4(0.76, 0.5, 0.0, 1.0)

varying vec2 v_vTexcoord;
varying vec4 v_vColour;
void main() {
    gl_FragColor = vec4(v_vColour.rgb, v_vColour.a * texture2D(gm_BaseTexture, v_vTexcoord).w);
}
 

Dragon47

Member
The other thing to consider is adding support for image_blend and image_alpha when drawing the rectangle. My hack:

I'm drawing with the colorize shader which I modified... Because I have multiple instances of the rectangle, I needed to fade and disable the tiles further away to have smooth transitions. I initially rendered to my own surface and faded that but.. extra swap is extra swap...

My draw event, sets the rectangle's image_blend and image alpha
Code:
fd_rectangle.image_alpha = image_alpha;
fd_rectangle.image_blend = global.hilly_color;
fd_rectangle_draw_stretched(fd_rectangle, x, y, world_width, world_height, material_interpolation_enabled);
fd_rectangle.image_alpha = 1;
fd_rectangle.image_blend = c_white;

the fd_rectangle_draw_stretched calls fd_rectangle_draw_part. I modified fd_rectangle_draw_part's case that does the colorize to use the passed image_blend and image_alpha
Code:
case FD_VISUALIZATION_SHADER.COLORIZE:
            shader_set(sh_fd_visualize_colorize);
                draw_surface_part_ext(sf_material_0, argument1, argument2, argument3, argument4, argument5, argument6, argument7, argument8, image_blend, image_alpha);
            shader_reset();
            break;

and modified the sh_fd_visualize_colorize_glsl shader to use the blend and alpha
added in_Color (which has the image_blend and image_alpha), pass through to fragment
Code:
// Passthrough vertex shader.

attribute vec4 in_Position;
attribute vec2 in_TextureCoord;
attribute vec4 in_Colour;                    // (r,g,b,a)
varying vec2 v_vTexcoord;
varying vec4 v_vColour;
void main() {
v_vColour = in_Colour;
    gl_Position = gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION] * in_Position;
    v_vTexcoord = in_TextureCoord;
}
I use a different form of rendering, but this should do the trick to match the original...
Code:
//fragment
// #define color vec4(0.76, 0.5, 0.0, 1.0)

varying vec2 v_vTexcoord;
varying vec4 v_vColour;
void main() {
    gl_FragColor = vec4(v_vColour.rgb, v_vColour.a * texture2D(gm_BaseTexture, v_vTexcoord).w);
}
Good point, I'll look into it
 

Dragon47

Member
A new version is out, version 1.1.0.
Changes:
Fixed HLSL 9 shader bugs, added a script for content shifting of fluid dynamics rectangles, and added color and alpha to the draw scripts.
 
I just thought I should note that I saw this error come up in the compile window. Not exactly sure what it would affect:

Code:
Process Chunk: SHDR   4408
Error in fragment shader "sh_fd_visualize_pixel_art_fiery_smoke_hlsl"

 C:\Users\....\GameMaker\Temp\gm_ttt_79109\gm_ttt_91921\memory(25,54): error X3014: incorrect number of arguments to numeric-type constructor

Error in fragment shader "sh_fd_visualize_thick_smoke_hlsl"

 C:\Users\....\GameMaker\Temp\gm_ttt_79109\gm_ttt_91921\memory(23,106): error X3004: undeclared identifier 'vec2'
 

Dragon47

Member
I just thought I should note that I saw this error come up in the compile window. Not exactly sure what it would affect:

Code:
Process Chunk: SHDR   4408
Error in fragment shader "sh_fd_visualize_pixel_art_fiery_smoke_hlsl"

 C:\Users\....\GameMaker\Temp\gm_ttt_79109\gm_ttt_91921\memory(25,54): error X3014: incorrect number of arguments to numeric-type constructor

Error in fragment shader "sh_fd_visualize_thick_smoke_hlsl"

 C:\Users\....\GameMaker\Temp\gm_ttt_79109\gm_ttt_91921\memory(23,106): error X3004: undeclared identifier 'vec2'
Yes, it makes the fiery smoke and thick smoke incompatible with the HLSL 9 shader language, but it's fixed in the newest version.
 
Hey, another question:

I'm trying to optimise a bit by only calling fd_rectangle_update when there's a fire on-screen (fires in this game are the objects that emit material).

However, sometimes those fires can go out, but the rest of the firey smoke still needs to dissipate for a few seconds.

So, is there an easy way for me to essentially say, "if there is still material on-screen, continue calling fd_rectangle_update - until there is no more material"?
 

icuurd12b42

TMC Founder
GMC Elder
you can probably determine the number of frames to wait from the last time you added stuff to it and the dissipation rate. Long story short though, it's a surface. the only way to look if it's clear is to shove it in the buffer and look at the bytes which frankly is a little intensive.
 

Dragon47

Member
Yes, whenever you call a script that adds material, set a variable to 1, then tick this variable down with the same dissipation value of the fluid (if the dissipation type is 1, subtract the dissipation value. if its 0, multiply the dissipation value). This variable will then correspond to the highest possible material opacity, and if it's 0 or close to 0 there's no visible fluid. This is possible because material opacity never increases, but only decreases (or is maintained if dissipation is disabled).
 

Dragon47

Member
I added a showcase video for the asset (which you can find in the main post). It shows some of the things you can do and what it looks like when implemented into a game.
 

Dragon47

Member
Version 1.2.0 is out! I added some scripts that SimenGames wrote for making it easier to let a fluid dynamics rectangle follow the view in large scale or infinite game worlds. The scripts can be found in the API folder "SimenGames' View Extension".
 

icuurd12b42

TMC Founder
GMC Elder
Can you post that script? or is it he one above? I made changes to the core for it to use my tmc_batch to reduce the swap when adding content... so I cant really upgrade. but I'd like to try out the script
 

Dragon47

Member
There are several scripts. Maybe you can create a separate project and import the updated version to it there?
 
Hey again, two questions:

- I just compiled my game as a native MacOS version for the first time, and am having an issue with this shader. It seems that it still creates material as normal, but it no longer adds velocity (the material just stays where it is).

- I've also compiled a native Linux build, but I'm the game doesn't run at all (The game window appears briefly but then immediately closes). Running through Terminal produces the following error:

Process Chunk: SHDR 4436
Error linking program:
ERROR: Compiled vertex shader was corrupt.
ERROR: Compiled fragment shader was corrupt.

I'm going to assume GLSL ES is OK to use on Mac and Linux - or do I need to specify GLSL?

Have you successfully tested this asset on a Mac and Linux OS before?
 

Dragon47

Member
GLSL ES should be okay on windows, mac and linux.
I haven't tested it myself on mac or linux, only on normal windows and windows YYC.
Not sure what causes the linux shader error, maybe you have to delete the HLSL 9 shaders before compiling (even if they're not in use).
As for the mac version, does it stay still in all of the example rooms? And is it not affected in any way when you move the mouse around?
 
Shortly after posting that, I got the game to compile on Linux. I've no idea how as I didn't change anything, but whatever!

The example rooms show the same behaviour, yes, and there's no change when moving the mouse. Just stays as a circular sprite (for the fire examples) and does not add any velocity.

EDIT: I did notice this when I held down '1' to view the debug info, though:

 
Last edited:

Dragon47

Member
Shortly after posting that, I got the game to compile on Linux. I've no idea how as I didn't change anything, but whatever!

The example rooms show the same behaviour, yes, and there's no change when moving the mouse. Just stays as a circular sprite (for the fire examples) and does not add any velocity.

EDIT: I did notice this when I held down '1' to view the debug info, though:

Sorry, I'm not sure what's going on there. I haven't done much development for mac or linux before.
 

Dragon47

Member
could be something to do about blend mode compatibility? I have not looked much at the core code but perhaps so parts would benefit from bypassing blend modes via a shader

On my end I'm a little concerned with the about of swaps
Dragon, have you read this thread?
https://forum.yoyogames.com/index.php?threads/multiple-render-targets-shaders.4008/
Multiple outputs... perhaps there is a way to combine processing somehow :)
Yeah, might be something like that.
I've done multiple render targets before. My Water Physics asset uses multiple render targets if HLSL 9 is chosen. But surprisingly the fps of HLSL 9 as opposed to GLSL ES was only slightly higher. I don't think it would make much of a difference if I did multiple render targets for Fluid Dynamics, and it's a bit time consuming to implement. And the bottleneck of the asset wouldn't benefit from multiple render targets either.
 

Dragon47

Member
gl_Position is the main output of the vertex shader and contains the vertex' position on screen. I am writing to gl_Position in every vertex shader, so I'm not sure why this error is showing up. If you go to a shader and look at the vertex shader tab, there should be a "gl_Position =" part there. Are you using GLSL ES or HLSL 9? If you're using HLSL 9 or have any HLSL 9 shaders existing in your project this might be what's causing the issue. Try deleting all HLSL 9 shaders and make sure all the other shaders are set to GLSL ES by going through the shader resources and looking at the shader language dropdown box in the top right corner of each window.
 
Quick question - what would I need to change in order to make the grey smoke parts of the pixel art fiery smoke shader slightly transparent?
 

icuurd12b42

TMC Founder
GMC Elder
Quick question - what would I need to change in order to make the grey smoke parts of the pixel art fiery smoke shader slightly transparent?
I use my own shader...

set the visualisation to FD_VISUALIZATION_SHADER.NO_SHADER to disable the ones canned in the product.

draw the field with
shader_set(YOURSHADER);
fd_rectangle_draw_stretched(fd_rectangle, x, y, world_width, world_height, material_interpolation_enabled);
shader_reset();

Copy/clone the pixel art shader to be your shader...
change
gl_FragColor.a = float(reaction_coordinate > 0.05);
which basically is a hard toggle between invisible to full on...
to something else... like
gl_FragColor.a = float(reaction_coordinate > 0.05) *.5;
half transparent

Firery Smoke sets
gl_FragColor.rgb to (.3,.3,.3) for smoke

gl_FragColor.rgb = vec3(1.0, 0.2, 0.0);
if (reaction_coordinate < 0.2) gl_FragColor.rgb = vec3(0.3);
if (reaction_coordinate > 0.37) gl_FragColor.rgb = vec3(1.0, 0.8, 0.0);
if (reaction_coordinate > 0.65) gl_FragColor.rgb = vec3(1.0, 1.0, 1.0);
gl_FragColor.a = float(reaction_coordinate > 0.05);

looking at the red value, looks like it's 1 for all , .3 for smoke... you could use that red value for alha...
gl_FragColor.a = float(reaction_coordinate > 0.05) * gl_FragColor.r;

.a will be 1 or .3... or 0

you can also change the code to specify the alpha for each possible states, 1 for all, .6 for smoke
gl_FragColor = vec4(1.0, 0.2, 0.0,1.0);
if (reaction_coordinate < 0.2) gl_FragColor = vec4(0.3,0.3,0.3.0.6);
if (reaction_coordinate > 0.37) gl_FragColor = vec4(1.0, 0.8, 0.0,1.0);
if (reaction_coordinate > 0.65) gl_FragColor = vec4(1.0, 1.0, 1.0,1.0);
gl_FragColor.a *= float(reaction_coordinate > 0.05); // mult .6 or 1.0 with 1 or 0... resulting in 0 or 1.0 or .6;
 

Dragon47

Member
Yes, like icuurd says. I recommend the last code snippet as it lets you set custom transparencies for all colors individually.
 
Top