Java to Python (PixelFlow library)

Hi,

I’m having difficulting porting the “Fluid_CustomParticles” example sketch from the PixelFlow library to Python.

In Java I have somehting like this:

static public class MyParticleSystem{
  
  public DwGLSLProgram shader_particleSpawn;
  public DwGLSLProgram shader_particleUpdate;
  public DwGLSLProgram shader_particleRender;

...


    // create shader
    shader_particleSpawn  = context.createShader("data/particleSpawn.frag");
    shader_particleUpdate = context.createShader("data/particleUpdate.frag");
    shader_particleRender = context.createShader("data/particleRender.glsl", "data/particleRender.glsl");
    shader_particleRender.vert.setDefine("SHADER_VERT", 1);
    shader_particleRender.frag.setDefine("SHADER_FRAG", 1);

But when running the following in Python mode:

def setup():
    size(1000, 800, P2D)

    
    shader_particleSpawn  = context.createShader("data/particleSpawn.frag")
    shader_particleUpdate = context.createShader("data/particleUpdate.frag")
    shader_particleRender = context.createShader("data/particleRender.glsl",  "data/particleRender.glsl")
    shader_particleRender.vert.setDefine("SHADER_VERT", 1)
    shader_particleRender.frag.setDefine("SHADER_FRAG", 1)

I have this error

DwUtils ERROR: could not create inputstream for data/particleSpawn.frag

Am i missing something ?

1 Like

That program is written in actual pure Java syntax, not Processing! No “.pde” file there! :dizzy_face:

Before converting it to Python Mode, you should 1st convert “Fluid_CustomParticles.java” to “Fluid_CustomParticles.pde”, so it runs under Java Mode.

The other “MyParticleSystem.java” file doesn’t need to be converted to a “.java” though. Just the 1 w/ setup() & draw() in it. :relieved:

Hi @GoToLoop, thanks for the reply.

I just realized the link I posted isn’t correct. Here’s the original Fluid_CustomParticles.pde file:

/**
 * 
 * PixelFlow | Copyright (C) 2016 Thomas Diewald - http://thomasdiewald.com
 * 
 * A Processing/Java library for high performance GPU-Computing (GLSL).
 * MIT License: https://opensource.org/licenses/MIT
 * 
 */



import com.thomasdiewald.pixelflow.java.DwPixelFlow;
import com.thomasdiewald.pixelflow.java.fluid.DwFluid2D;

import controlP5.Accordion;
import controlP5.ControlP5;
import controlP5.Group;
import controlP5.RadioButton;
import controlP5.Toggle;
import processing.core.*;
import processing.opengl.PGraphics2D;
import processing.opengl.PJOGL;


  // Fluid_CustomParticles show how to setup a completely customized particle
  // system that is interacting with the fluid simulation.
  // The particle data (positions) is stored in an OpenGL texture (GL_RGBA32F) 
  // and gets updated each frame using GLSL shaders.
  // No Data transfer (application <-> device), is required.
  //
  //
  // controls:
  //
  // LMB: add Particles + Velocity
  // MMB: add Particles
  // RMB: add Particles
  
  
  
  private class MyFluidData implements DwFluid2D.FluidData{
    
    // update() is called during the fluid-simulation update step.
    @Override
    public void update(DwFluid2D fluid) {
    
      float px, py, vx, vy, radius, vscale, temperature;
 
      radius = 15;
      vscale = 10;
      px     = width/2;
      py     = 50;
      vx     = 1 * +vscale;
      vy     = 1 *  vscale;
      radius = 40;
      temperature = 1f;
      fluid.addDensity(px, py, radius, 0.2f, 0.3f, 0.5f, 1.0f);
      fluid.addTemperature(px, py, radius, temperature);
      particles.spawn(fluid, px, py, radius, 100);
      

      boolean mouse_input = !cp5.isMouseOver() && mousePressed;
      
      // add impulse: density + velocity, particles
      if(mouse_input && mouseButton == LEFT){
        radius = 15;
        vscale = 15;
        px     = mouseX;
        py     = height-mouseY;
        vx     = (mouseX - pmouseX) * +vscale;
        vy     = (mouseY - pmouseY) * -vscale;
        fluid.addDensity (px, py, radius, 0.25f, 0.0f, 0.1f, 1.0f);
        fluid.addVelocity(px, py, radius, vx, vy);
        particles.spawn(fluid, px, py, radius*2, 300);
      }
      
      // add impulse: density + temperature, particles
      if(mouse_input && mouseButton == CENTER){
        radius = 15;
        vscale = 15;
        px     = mouseX;
        py     = height-mouseY;
        temperature = 2f;
        fluid.addDensity(px, py, radius, 0.25f, 0.0f, 0.1f, 1.0f);
        fluid.addTemperature(px, py, radius, temperature);
        particles.spawn(fluid, px, py, radius, 100);
      }
      
      // particles
      if(mouse_input && mouseButton == RIGHT){
        px     = mouseX;
        py     = height - 1 - mouseY; // invert
        radius = 50;
        particles.spawn(fluid, px, py, radius, 300);
      }
       
    }
  }
  
  
  int viewport_w = 1280;
  int viewport_h = 720;
  int viewport_x = 230;
  int viewport_y = 0;
  
  int gui_w = 200;
  int gui_x = 20;
  int gui_y = 20;
  
  int fluidgrid_scale = 3;
  
  DwFluid2D fluid;

  // render targets
  PGraphics2D pg_fluid;
  //texture-buffer, for adding obstacles
  PGraphics2D pg_obstacles;
  
  // custom particle system
  MyParticleSystem particles;
  
  // some state variables for the GUI/display
  int     BACKGROUND_COLOR           = 0;
  boolean UPDATE_FLUID               = true;
  boolean DISPLAY_FLUID_TEXTURES     = false;
  boolean DISPLAY_FLUID_VECTORS      = false;
  int     DISPLAY_fluid_texture_mode = 0;
  boolean DISPLAY_PARTICLES          = true;
  
  
  public void settings() {
    size(viewport_w, viewport_h, P2D);
    smooth(4);
    PJOGL.profile = 3;
  }
  

  
  public void setup() {
    surface.setLocation(viewport_x, viewport_y);
    
    // main library context
    DwPixelFlow context = new DwPixelFlow(this);
    context.print();
    context.printGL();

    // fluid simulation
    fluid = new DwFluid2D(context, viewport_w, viewport_h, fluidgrid_scale);
  
    // set some simulation parameters
    fluid.param.dissipation_density     = 0.999f;
    fluid.param.dissipation_velocity    = 0.99f;
    fluid.param.dissipation_temperature = 0.80f;
    fluid.param.vorticity               = 0.10f;
    
    // interface for adding data to the fluid simulation
    MyFluidData cb_fluid_data = new MyFluidData();
    fluid.addCallback_FluiData(cb_fluid_data);
   
    // pgraphics for fluid
    pg_fluid = (PGraphics2D) createGraphics(viewport_w, viewport_h, P2D);
    pg_fluid.smooth(4);
    pg_fluid.beginDraw();
    pg_fluid.background(BACKGROUND_COLOR);
    pg_fluid.endDraw();
    
        // pgraphics for obstacles
    pg_obstacles = (PGraphics2D) createGraphics(viewport_w, viewport_h, P2D);
    pg_obstacles.smooth(4);
    pg_obstacles.beginDraw();
    pg_obstacles.clear();
    float radius;
    radius = 200;
    pg_obstacles.stroke(64);
    pg_obstacles.strokeWeight(10);
    pg_obstacles.noFill();
    pg_obstacles.rect(1*width/2f,  1*height/4f, radius, radius, 20);
    // border-obstacle
    pg_obstacles.strokeWeight(20);
    pg_obstacles.stroke(64);
    pg_obstacles.noFill();
    pg_obstacles.rect(0, 0, pg_obstacles.width, pg_obstacles.height);
    pg_obstacles.endDraw();
    
    fluid.addObstacles(pg_obstacles);
    
    // custom particle object
    particles = new MyParticleSystem(context, 1000 * 1000);

    createGUI();
    
    background(0);
    frameRate(60);
  }
  



  public void draw() {    
    
 
    // update simulation
    if(UPDATE_FLUID){
      fluid.addObstacles(pg_obstacles);
      fluid.update();
      particles.update(fluid);
    }
    
    // clear render target
    pg_fluid.beginDraw();
    pg_fluid.background(BACKGROUND_COLOR);
    pg_fluid.endDraw();
    
    
    // render fluid stuff
    if(DISPLAY_FLUID_TEXTURES){
      // render: density (0), temperature (1), pressure (2), velocity (3)
      fluid.renderFluidTextures(pg_fluid, DISPLAY_fluid_texture_mode);
    }
    
    if(DISPLAY_FLUID_VECTORS){
      // render: velocity vector field
      fluid.renderFluidVectors(pg_fluid, 10);
    }
    
    if( DISPLAY_PARTICLES){
      // render: particles; 0 ... points, 1 ...sprite texture, 2 ... dynamic points
      particles.render(pg_fluid, BACKGROUND_COLOR);
    }
    

    // display
    image(pg_fluid    , 0, 0);
    image(pg_obstacles, 0, 0);

    
    // display number of particles as text
    String txt_num_particles = String.format("Particles  %,d", particles.ALIVE_PARTICLES);
    fill(0, 0, 0, 220);
    noStroke();
    rect(10, height-10, 160, -30);
    fill(255,128,0);
    text(txt_num_particles, 20, height-20);
 
    // info
    String txt_fps = String.format(getClass().getName()+ "   [size %d/%d]   [frame %d]   [fps %6.2f]", fluid.fluid_w, fluid.fluid_h, fluid.simulation_step, frameRate);
    surface.setTitle(txt_fps);
  }
  



  
  public void fluid_resizeUp(){
    fluid.resize(width, height, fluidgrid_scale = max(1, --fluidgrid_scale));
  }
  public void fluid_resizeDown(){
    fluid.resize(width, height, ++fluidgrid_scale);
  }
  public void fluid_reset(){
    fluid.reset();
  }
  public void fluid_togglePause(){
    UPDATE_FLUID = !UPDATE_FLUID;
  }
  public void fluid_displayMode(int val){
    DISPLAY_fluid_texture_mode = val;
    DISPLAY_FLUID_TEXTURES = DISPLAY_fluid_texture_mode != -1;
  }
  public void fluid_displayVelocityVectors(int val){
    DISPLAY_FLUID_VECTORS = val != -1;
  }

  public void fluid_displayParticles(int val){
    DISPLAY_PARTICLES = val != -1;
  }

  public void keyReleased(){
    if(key == 'p') fluid_togglePause(); // pause / unpause simulation
    if(key == '+') fluid_resizeUp();    // increase fluid-grid resolution
    if(key == '-') fluid_resizeDown();  // decrease fluid-grid resolution
    if(key == 'r') fluid_reset();       // restart simulation
    
    if(key == '1') DISPLAY_fluid_texture_mode = 0; // density
    if(key == '2') DISPLAY_fluid_texture_mode = 1; // temperature
    if(key == '3') DISPLAY_fluid_texture_mode = 2; // pressure
    if(key == '4') DISPLAY_fluid_texture_mode = 3; // velocity
    
    if(key == 'q') DISPLAY_FLUID_TEXTURES = !DISPLAY_FLUID_TEXTURES;
    if(key == 'w') DISPLAY_FLUID_VECTORS  = !DISPLAY_FLUID_VECTORS;
  }
 
  
  
  ControlP5 cp5;
  
  public void createGUI(){
    cp5 = new ControlP5(this);
    
    int sx, sy, px, py, oy;
    
    sx = 100; sy = 14; oy = (int)(sy*1.5f);
    

    ////////////////////////////////////////////////////////////////////////////
    // GUI - FLUID
    ////////////////////////////////////////////////////////////////////////////
    Group group_fluid = cp5.addGroup("fluid");
    {
      group_fluid.setHeight(20).setSize(gui_w, 300)
      .setBackgroundColor(color(16, 180)).setColorBackground(color(16, 180));
      group_fluid.getCaptionLabel().align(CENTER, CENTER);
      
      px = 10; py = 15;
      
      cp5.addButton("reset").setGroup(group_fluid).plugTo(this, "fluid_reset"     ).setSize(80, 18).setPosition(px    , py);
      cp5.addButton("+"    ).setGroup(group_fluid).plugTo(this, "fluid_resizeUp"  ).setSize(39, 18).setPosition(px+=82, py);
      cp5.addButton("-"    ).setGroup(group_fluid).plugTo(this, "fluid_resizeDown").setSize(39, 18).setPosition(px+=41, py);
      
      px = 10;
     
      cp5.addSlider("velocity").setGroup(group_fluid).setSize(sx, sy).setPosition(px, py+=(int)(oy*1.5f))
          .setRange(0, 1).setValue(fluid.param.dissipation_velocity).plugTo(fluid.param, "dissipation_velocity");
      
      cp5.addSlider("density").setGroup(group_fluid).setSize(sx, sy).setPosition(px, py+=oy)
          .setRange(0, 1).setValue(fluid.param.dissipation_density).plugTo(fluid.param, "dissipation_density");
      
      cp5.addSlider("temperature").setGroup(group_fluid).setSize(sx, sy).setPosition(px, py+=oy)
          .setRange(0, 1).setValue(fluid.param.dissipation_temperature).plugTo(fluid.param, "dissipation_temperature");
      
      cp5.addSlider("vorticity").setGroup(group_fluid).setSize(sx, sy).setPosition(px, py+=oy)
          .setRange(0, 1).setValue(fluid.param.vorticity).plugTo(fluid.param, "vorticity");
          
      cp5.addSlider("iterations").setGroup(group_fluid).setSize(sx, sy).setPosition(px, py+=oy)
          .setRange(0, 80).setValue(fluid.param.num_jacobi_projection).plugTo(fluid.param, "num_jacobi_projection");
            
      cp5.addSlider("timestep").setGroup(group_fluid).setSize(sx, sy).setPosition(px, py+=oy)
          .setRange(0, 1).setValue(fluid.param.timestep).plugTo(fluid.param, "timestep");
          
      cp5.addSlider("gridscale").setGroup(group_fluid).setSize(sx, sy).setPosition(px, py+=oy)
          .setRange(0, 50).setValue(fluid.param.gridscale).plugTo(fluid.param, "gridscale");
      
      RadioButton rb_setFluid_DisplayMode = cp5.addRadio("fluid_displayMode").setGroup(group_fluid).setSize(80,18).setPosition(px, py+=(int)(oy*1.5f))
          .setSpacingColumn(2).setSpacingRow(2).setItemsPerRow(2)
          .addItem("Density"    ,0)
          .addItem("Temperature",1)
          .addItem("Pressure"   ,2)
          .addItem("Velocity"   ,3)
          .activate(DISPLAY_fluid_texture_mode);
      for(Toggle toggle : rb_setFluid_DisplayMode.getItems()) toggle.getCaptionLabel().alignX(CENTER);
      
      cp5.addRadio("fluid_displayVelocityVectors").setGroup(group_fluid).setSize(18,18).setPosition(px, py+=(int)(oy*2.5f))
          .setSpacingColumn(2).setSpacingRow(2).setItemsPerRow(1)
          .addItem("Velocity Vectors", 0)
          .activate(DISPLAY_FLUID_VECTORS ? 0 : 2);
    }
    
    
    ////////////////////////////////////////////////////////////////////////////
    // GUI - DISPLAY
    ////////////////////////////////////////////////////////////////////////////
    Group group_display = cp5.addGroup("display");
    {
      group_display.setHeight(20).setSize(gui_w, 50)
      .setBackgroundColor(color(16, 180)).setColorBackground(color(16, 180));
      group_display.getCaptionLabel().align(CENTER, CENTER);
      
      px = 10; py = 15;
      
      cp5.addSlider("BACKGROUND").setGroup(group_display).setSize(sx,sy).setPosition(px, py)
          .setRange(0, 255).setValue(BACKGROUND_COLOR).plugTo(this, "BACKGROUND_COLOR");
      
      cp5.addRadio("fluid_displayParticles").setGroup(group_display).setSize(18,18).setPosition(px, py+=(int)(oy*1.5f))
          .setSpacingColumn(2).setSpacingRow(2).setItemsPerRow(1)
          .addItem("display particles", 0)
          .activate(DISPLAY_PARTICLES ? 0 : 2);
    }
    
    
    ////////////////////////////////////////////////////////////////////////////
    // GUI - ACCORDION
    ////////////////////////////////////////////////////////////////////////////
    cp5.addAccordion("acc").setPosition(gui_x, gui_y).setWidth(gui_w).setSize(gui_w, height)
      .setCollapseMode(Accordion.MULTI)
      .addItem(group_fluid)
      .addItem(group_display)
      .open(4);
  }

But I don’t see how it is related to my question on shader creation…

It isn’t. But at least now I could run it to check whether it works under Java Mode or not. :crazy_face:

It’d be better if you posted your full attempt under Python Mode, so I can compare it w/ Java’s. :snake:

You may be heartened to know or otherwise that I have successfully got several sketches working with JRubyArt.

1 Like

@monkstone – Thank you for your concern. Unfortunately I don’t know Ruby and it seems the problem I’m facing has to do with the DwGLSLProgram variable declaration. A variable that doesn’t seem to be used in your example sketches. Great contribution anyway.

@GoToLoop – I see. I deliberately reduced the Python code to the problematic snippet for clarity (that snippet should be working) but here’s my attempt to implement the MyParticleSystem class in full sketch:

add_library('PixelFlow')
from com.jogamp.opengl import GL2ES2


def setup():
    global fluid, pg_fluid, context, particles, tex_particles
    size(1000, 800, P2D)
    frameRate(1000)
    smooth(8)


    context = DwPixelFlow(this)
    fluid = DwFluid2D(context, width, height, 0)
    tex_particles = DwGLTexture.TexturePingPong()
    pg_fluid = createGraphics(width, height, P2D)
    particles = Particles(context, 1000 * 1000)
    
    fluid.param.dissipation_velocity = 0.99
    fluid.param.dissipation_density  = 0.99
    fluid.param.dissipation_temperature = 0.1
    fluid.param.vorticity = 0.00
    fluid.param.timestep = 0.1
    fluid.addCallback_FluiData(MyFluidData())
    
    
def draw():
    image(pg_fluid, 0, 0)
    
    pg_fluid.beginDraw()
    pg_fluid.background(0)
    pg_fluid.endDraw()
    
    fluid.update()
    fluid.renderFluidTextures(pg_fluid, 0)

    #particles.update(fluid)
    #particles.render(pg_fluid, 0)

    
class MyFluidData(DwFluid2D.FluidData):
    def update(_, fluid):
        if mousePressed:
            radius = 15
            px = mouseX
            py = height-mouseY
            vx = (mouseX-pmouseX) * 15
            vy = (mouseY-pmouseY) * -15
            fluid.addVelocity(px, py, 20, vx, vy)
            fluid.addDensity(px, py, 20, 0.0, 0.4, .7, .5)
            #particles.spawn(fluid, px, py, radius, 100)
            
            
class Particles(object):
    def __init__(self, context, n_particles):
        self.context = context
        self.npart = n_particles
        self.ALIVE_LO = 0
        self.ALIVE_HI = 0
        self.ALIVE_PARTICLES = 0
        self.dissipation = 0.90
        self.intertia = 0.20
        self.particles_x = 0
        self.particles_y = 0
        self.resiz(self.context, self.npart)
        
    def dispose(self):
        release()
        
    def release(self):
        tex_particles.release()
        
    def resiz(self, context, n_particles):
        self.particles_x = ceil(sqrt(n_particles))
        self.particles_y = self.particles_x

        context.begin()
        self.release()
        mxpart = self.particles_x * self.particles_y
        
        shader_particleSpawn  = context.createShader("data/particleSpawn.frag")
        shader_particleUpdate = context.createShader("data/particleUpdate.frag")
        shader_particleRender = context.createShader("data/particleRender.glsl",  "data/particleRender.glsl")
        shader_particleRender.vert.setDefine("SHADER_VERT", 1)
        shader_particleRender.frag.setDefine("SHADER_FRAG", 1)
    
        tex_particles.resize(context, GL2ES2.GL_RGBA32F, self.particles_x, self.particles_y, GL2ES2.GL_RGBA, GL2ES2.GL_FLOAT, GL2ES2.GL_NEAREST, 4, 4);
    
        context.end("ParticleSystem.resize");
    
        reset();
        
    def spawn(self, fluid, px, py, radius, count):
        count = round(count * 1.0)
        
        spawn_lo = self.ALIVE_HI
        spawn_hi = min(self.spawn.lo + count, n_particles)
        no = float(random()) * PI
        
        context.begin()
        context.beginDraw(self.tex_articles.dst)
        shader_particleSpawn.begin()
        shader_particleSpawn.uniform1i("spawn_lo", self.spawn_lo)
        shader_particleSpawn.uniform1i("spawn_hi", self.spawn_hi)
        shader_particleSpawn.uniform2f("spawn_origin", px, py)
        shader_particleSpawn.uniform1f("spawn_radius", radius)
        shader_particleSpawn.uniform1f("noise", no)
        shader_particleSpawn.uniform2f("wh_particles" , self.particles_x, self.particles_y)
        shader_particleSpawn.uniformTexture("tex_particles" , tex_particles.src)
        shader_particleSpawn.drawFullScreenQuad()
        shader_particleSpawn.end()
        context.endDraw()
        context.end("ParticleSystem.spawn")
        self.tex_particles.swap()
        
        self.ALIVE_HI = spawn_hi;
        self.ALIVE_PARTICLES = max(self.ALIVE_PARTICLES, self.ALIVE_HI - self.ALIVE_LO)
    
    def update(self, fluid):
        self.context.begin();
        self.context.beginDraw(tex_particles.dst);
        shader_particleUpdate.begin();
        shader_particleUpdate.uniform2f("wh_fluid", fluid.fluid_w, fluid.fluid_h);
        shader_particleUpdate.uniform2f("wh_particles", self.particles_x, self.particles_y);
        shader_particleUpdate.uniform1f("timestep", fluid.param.timestep);
        shader_particleUpdate.uniform1f("rdx", 1.0 / fluid.param.gridscale);
        shader_particleUpdate.uniform1f("dissipation", self.dissipation);
        shader_particleUpdate.uniform1f("inertia", self.inertia);
        shader_particleUpdate.uniformTexture("tex_particles", tex_particles.src);
        shader_particleUpdate.uniformTexture("tex_velocity" , fluid.tex_velocity.src);
        shader_particleUpdate.uniformTexture("tex_obstacles", fluid.tex_obstacleC.src);
        shader_particleUpdate.drawFullScreenQuad();
        shader_particleUpdate.end();
        self.context.endDraw();
        self.context.end("ParticleSystem.update");
        tex_particles.swap()
        
        
    def render(self, dst, bg):
        num_points_to_render = self.ALIVE_PARTICLES
        w = dst.width
        h = dst.height
    
        dst.beginDraw();
        dst.blendMode(PConstants.BLEND)
        if bg == 0: dst.bendMode(PConstants.ADD)
        context.begin();
        shader_particleRender.begin()
        shader_particleRender.uniform2f("wh_viewport", w, h)
        shader_particleRender.uniform2i("num_particles", self.particles_x, self.particles_y)
        shader_particleRender.uniform1f("point_size", 1.5)
        shader_particleRender.uniformTexture("tex_particles", tex_particles.src)
        shader_particleRender.drawFullScreenPoints(num_points_to_render)
        shader_particleRender.end();
        context.end("ParticleSystem.render");
    
        dst.endDraw();
1 Like

The reaction_diffusion.rb is an example where I create instances of DwGLSLProgram, but ruby is not strongly typed so it isn’t obvious

@shader_grayscott = context.create_shader(data_path('grayscott.frag'))
  @shader_render = context.create_shader(data_path('render.frag'))

Just a thought but have you tried using the absolute data path for particleRender.glsl etc., instead of data/particleRender.glsl. That is what my data_path wrapper above returns…

2 Likes

Following @monkstone suggestion I put all 3 shaders’ files in the main sketch file and it worked.
Also I apologize as I realize my first draft was full of typos. Please find below a fully working and cleaned-up version.

Script
add_library('PixelFlow')
from com.jogamp.opengl import GL2ES2


def setup():
    global fluid, pg_fluid, context, particles, tex_particles, shader_particleSpawn, shader_particleUpdate, shader_particleRender
    size(1000, 800, P2D)
    frameRate(1000)
    smooth(8)

    context = DwPixelFlow(this)
    fluid = DwFluid2D(context, width, height, 0)
    tex_particles = DwGLTexture.TexturePingPong()

    particles = Particles(context, 1000 * 1000)
    pg_fluid = createGraphics(width, height, P2D)

    shader_particleSpawn  = context.createShader("particleSpawn.frag")
    shader_particleUpdate = context.createShader("particleUpdate.frag")
    shader_particleRender = context.createShader("particleRender.glsl",  "particleRender.glsl")
    shader_particleRender.vert.setDefine("SHADER_VERT", 1)
    shader_particleRender.frag.setDefine("SHADER_FRAG", 1)
    
    fluid.param.dissipation_velocity = 0.99
    fluid.param.dissipation_density  = 0.99
    fluid.param.dissipation_temperature = 0.1
    fluid.param.vorticity = 0.00
    fluid.param.timestep = 0.1
    fluid.addCallback_FluiData(MyFluidData())

    
def draw():
    image(pg_fluid, 0, 0)
    
    pg_fluid.beginDraw()
    pg_fluid.background(0)
    pg_fluid.endDraw()
    
    fluid.update()
    #fluid.renderFluidTextures(pg_fluid, 0)

    particles.update(fluid)
    particles.render(pg_fluid, 0)

    
class MyFluidData(DwFluid2D.FluidData):
    def update(_, fluid):
        if mousePressed:
            radius = 15
            px = mouseX
            py = height-mouseY
            vx = (mouseX-pmouseX) * radius
            vy = (mouseY-pmouseY) * - radius
            fluid.addVelocity(px, py, 20, vx, vy)
            fluid.addDensity(px, py, 20, 0.0, 0.4, .7, .5)
            particles.spawn(fluid, px, py, radius, 100)
            
            
class Particles(object):
    def __init__(self, context, n_particles):
        self.context = context
        self.npart = n_particles
        self.ALIVE_LO = 0
        self.ALIVE_HI = 0
        self.ALIVE_PARTICLES = 0
        self.dissipation = 0.90
        self.inertia = 0.20
        self.particles_x = 0
        self.particles_y = 0
        self.resize(context, n_particles)

        
    def resize(self, context, n_particles):
        self.particles_x = ceil(sqrt(n_particles))
        self.particles_y = self.particles_x

        context.begin()
        tex_particles.release()
        tex_particles.resize(context, GL2ES2.GL_RGBA32F, self.particles_x, self.particles_y, GL2ES2.GL_RGBA, GL2ES2.GL_FLOAT, GL2ES2.GL_NEAREST, 4, 4)
        context.end("ParticleSystem.resize")
        
    def spawn(self, fluid, px, py, radius, count):
        count = round(count * 1.0)
        spawn_lo = int(self.ALIVE_HI)
        spawn_hi = int(min(spawn_lo + count, self.npart))
        n = float(random(1)) * PI
        
        context.begin()
        context.beginDraw(tex_particles.dst)
        shader_particleSpawn.begin()
        if fluid is not None: shader_particleSpawn.uniform2f("wh_viewport", fluid.viewp_w, fluid.viewp_h)
        shader_particleSpawn.uniform1i("spawn_lo", spawn_lo)
        shader_particleSpawn.uniform1i("spawn_hi", spawn_hi)
        shader_particleSpawn.uniform2f("spawn_origin", px, py)
        shader_particleSpawn.uniform1f("spawn_radius", radius)
        shader_particleSpawn.uniform1f("noise", n)
        shader_particleSpawn.uniform2f("wh_particles" , self.particles_x, self.particles_y)
        shader_particleSpawn.uniformTexture("tex_particles" , tex_particles.src)
        shader_particleSpawn.drawFullScreenQuad()
        shader_particleSpawn.end()
        context.endDraw()
        context.end("ParticleSystem.spawn")
        tex_particles.swap()
        
        self.ALIVE_HI = spawn_hi
        self.ALIVE_PARTICLES = max(self.ALIVE_PARTICLES, self.ALIVE_HI - self.ALIVE_LO)
    
    def update(self, fluid):
        self.context.begin()
        self.context.beginDraw(tex_particles.dst)
        shader_particleUpdate.begin()
        shader_particleUpdate.uniform2f("wh_fluid", fluid.fluid_w, fluid.fluid_h)
        shader_particleUpdate.uniform2f("wh_particles", self.particles_x, self.particles_y)
        shader_particleUpdate.uniform1f("timestep", fluid.param.timestep)
        shader_particleUpdate.uniform1f("rdx", 1.0 / fluid.param.gridscale)
        shader_particleUpdate.uniform1f("dissipation", self.dissipation)
        shader_particleUpdate.uniform1f("inertia", self.inertia)
        shader_particleUpdate.uniformTexture("tex_particles", tex_particles.src)
        shader_particleUpdate.uniformTexture("tex_velocity" , fluid.tex_velocity.src)
        shader_particleUpdate.uniformTexture("tex_obstacles", fluid.tex_obstacleC.src)
        shader_particleUpdate.drawFullScreenQuad()
        shader_particleUpdate.end()
        self.context.endDraw()
        self.context.end("ParticleSystem.update")
        tex_particles.swap()
        
        
    def render(self, dst, bg):
        num_points_to_render = self.ALIVE_PARTICLES
        w = dst.width
        h = dst.height
    
        dst.beginDraw();
        dst.blendMode(PConstants.BLEND)
        if bg == 0: dst.blendMode(PConstants.ADD)
        context.begin();
        shader_particleRender.begin()
        shader_particleRender.uniform2f("wh_viewport", w, h)
        shader_particleRender.uniform2i("num_particles", self.particles_x, self.particles_y)
        shader_particleRender.uniform1f("point_size", 1.5)
        shader_particleRender.uniformTexture("tex_particles", tex_particles.src)
        shader_particleRender.drawFullScreenPoints(num_points_to_render)
        shader_particleRender.end()
        context.end("ParticleSystem.render")
        dst.endDraw()

Thank you

1 Like