Java-Gaming.org    
Featured games (81)
games approved by the League of Dukes
Games in Showcase (499)
Games in Android Showcase (118)
games submitted by our members
Games in WIP (567)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  Improving performance for a blood rendering  (Read 743 times)
0 Members and 1 Guest are viewing this topic.
Offline CopyableCougar4

Senior Member


Medals: 11
Exp: 1 year



« Posted 2014-07-27 20:52:35 »

Hi

In my project I was trying to improve my blood splatter system to add pooling. It's still very simple, but I need to improve it's performance.

Code:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
package warlord.ingame.effects;

import java.util.ArrayList;
import warlord.ingame.world.Block;
import warlord.ingame.world.Tile;
import warlord.opengl.Game;
import warlord.opengl.Logger;
import warlord.opengl.MathHelper;

import static org.lwjgl.opengl.GL11.*;

public class BloodSplatter {
   
   // Each particle is 4x4
  public static float SIZE = 4.0f;
   public static int particlesPerTile = Math.round(Tile.WIDTH / SIZE);
   
   private ArrayList<float[]> particles; // Stores the x, y, alpha, and life
 
   
   public BloodSplatter(Block block){
      particles = new ArrayList<float[]>();
   }
   
   public void update(){
      for(int index = 0; index < particles.size(); index++){
         float[] particle = particles.get(index);
         if(particle.length < 4){
            Logger.print("Particle doesn't have a life. Skipping index " + index);
            continue;
         }
         if(Game.getFloat() < 0.999f){
            continue; // .1% chance of spreading
        }
         setLife(index, getLife(index) - 1);
         addParticle(particle[0] - SIZE, particle[1], getLife(index));
         addParticle(particle[0] + SIZE, particle[1], getLife(index));
         addParticle(particle[0], particle[1] - SIZE, getLife(index));
         addParticle(particle[0], particle[1] + SIZE, getLife(index));
      }
      trimLevels();
   }
   
   public void sprayDeath(float cx, float cy){
      sprayDeath(Math.round(cx), Math.round(cy), 5);
   }
   public void sprayDeath(int cx, int cy, int radius){
      for(int _radius = 0; _radius <= radius; _radius++){
         for(int degree = 0; degree <= 360; degree++){
            float dx = (MathHelper.cos((float)Math.toRadians(degree)));
            float dy = (MathHelper.sin((float)Math.toRadians(degree)));
            addParticle(cx + dx, cy + dy);
         }
      }
   }
   
   public void addParticle(float x, float y){
      particles.add(new float[]{x, y, 1.0f, 30.0f});
   }
   public void addParticle(float x, float y, float life){
      particles.add(new float[]{x, y, 1.0f, life});
   }
   
   public void setLife(int index, float life){
      float[] old = particles.get(index);
      old[3] = life;
      particles.set(index, old);
   }
   public float getLife(int index){
      return particles.get(index)[3];
   }
   
   public void trimLevels(){
      for(int index = 0; index < particles.size(); index++){
         float[] particle = particles.get(index);
         if(particle[3] <= 0.0f){
            // the particle is dead
           particles.remove(index);
         }
      }
   }
   
   public void render(){
      update();
      glBegin(GL_QUADS);
      ArrayList<Float> x = new ArrayList<Float>();
      ArrayList<Float> y = new ArrayList<Float>();
      for(int index = 0; index < particles.size(); index++){
         float[] particle = particles.get(index);
         if(x.contains(particle[0])){
            if(y.contains(particle[1])){
               continue;
            }
         }
         x.add(particle[0]);
         y.add(particle[1]);
         glColor4f(1.0f, 0.0f, 0.0f, 30.0f / particle[3]);
         glVertex2f(particle[0], particle[1]);
         glVertex2f(particle[0] + SIZE, particle[1]);
         glVertex2f(particle[0] + SIZE, particle[1] + SIZE);
         glVertex2f(particle[0], particle[1] + SIZE);
      }
      glEnd();
      glColor3f(1.0f, 1.0f, 1.0f);
   }

}


The blood seems to pool alright and looks okay so far, but when the blood starts to spread and overlap it begins to lag. I will eventually change the rendering but for now in development I'm using immediate mode. Any techniques or ideas to improve performance would be appreciated Smiley

CopyableCougar4

Either wandering the forum or programming. Most likely the latter Smiley

Github: http://github.com/CopyableCougar4
Offline basil_
« Reply #1 - Posted 2014-07-27 22:04:03 »

here is my guess ..

once you get alot particles going on ..
1  
2  
3  
4  
5  
if(x.contains(particle[0])){
  if(y.contains(particle[1])){
    continue;
  }
}
ArrayList.contains() is pretty expensive. try using a HashSet instead.
i would also create the two sets as fields and use clear() every render.

then i would go back and replace the particles array. for unique items, as you seem to be aiming for, i would create a particle class with custom equals() and hashcode() methods and store them in a hashmap. the hashmap would only accept unique particles so the check would become obsolete.

you like some code example into that direction ?

2nd, ofc it would speed up things when you replace the actual rendering part. do you run any shaders yet ? if, you could keep immediate mode and draw all particles in gl_points mode, just the center of each particle - and use point-sprites, which require a gl_pointSize line in a vertex-shader. would create exact the same output as you get now but with just 1/4 draw-calls.
Offline CopyableCougar4

Senior Member


Medals: 11
Exp: 1 year



« Reply #2 - Posted 2014-07-27 22:35:28 »

Hi

So I was digging around in old ditched projects and I found some old blood code. So I spent the last 15 minutes refactoring and testing it, and I think I am part of the way to my solution. Here is the latest code:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
123  
124  
125  
126  
127  
128  
129  
130  
131  
132  
133  
134  
135  
package warlord.ingame.effects;

import java.util.ArrayList;
import java.util.HashSet;

import warlord.ingame.world.Collisions;
import warlord.opengl.Game;
import warlord.opengl.MathHelper;

import static org.lwjgl.opengl.GL11.*;

public class Blood {
   
   private ArrayList<Particle> blood;
   private Emitter emitter;
   
   public Blood(){
      blood = new ArrayList<Particle>();
      emitter = new Emitter(10.0f);
   }
   
   public void addParticle(float x, float y, float alpha, float radius, long ticks){
      blood.add(new Particle(x, y, alpha, ticks));
   }
   
   public void render(){
      HashSet<Float> _x = new HashSet<Float>();
      HashSet<Float> _y = new HashSet<Float>();
      for(int i = 0; i < blood.size(); i++){
         Particle particle = blood.get(i);
         if(_x.contains(particle.x)){
            if(_y.contains(particle.y)){
               continue;
            }
         }
         _x.add(particle.x);
         _y.add(particle.y);
         if(Game.getFloat() > (59.0f / 60.0f)){
            particle.spread(this, particle); // Only spread 1x per second
        }
         particle.render();
      }
   }
   
   public void sprayDeath(float x, float y, int angle){
      emitter.spray(x, y, this, angle - 60, 120);
   }
   public void sprayDeath(float x, float y){
      emitter.spray(x, y, this, 0, 360);
   }
   
   /*
    * INNER UTILITY CLASSES
    */

   
   private static class Particle {

      public float x, y, alpha;
      private long ticks;
      private boolean gone;
      private static float size = 4.0f;
     
      public Particle(float x, float y, float alpha, long ticks){
         this.x = x;
         this.y = y;
         this.alpha = alpha;
         this.ticks = ticks;
         gone = false;
         if(Collisions.isCollision(x, y)){
            gone = true;
         }
      }
     
      public void spread(Blood blood, Particle old){
         if(gone || old.ticks <= 0){
            return;
         }
         if(Game.getFloat() > 0.5f) blood.addParticle(x - size, y, 1.0f, Game.getFloat() * 80.0f, old.ticks - 1);
         if(Game.getFloat() > 0.5f) blood.addParticle(x + size, y, 1.0f, Game.getFloat() * 80.0f, old.ticks - 1);
         if(Game.getFloat() > 0.5f) blood.addParticle(x, y - size, 1.0f, Game.getFloat() * 80.0f, old.ticks - 1);
         if(Game.getFloat() > 0.5f) blood.addParticle(x, y + size, 1.0f, Game.getFloat() * 80.0f, old.ticks - 1);
         if(Game.getFloat() > 0.5f) blood.addParticle(x - size, y - size, 1.0f, Game.getFloat() * 80.0f, old.ticks - 1);
         if(Game.getFloat() > 0.5f) blood.addParticle(x - size, y + size, 1.0f, Game.getFloat() * 80.0f, old.ticks - 1);
         if(Game.getFloat() > 0.5f) blood.addParticle(x + size, y - size, 1.0f, Game.getFloat() * 80.0f, old.ticks - 1);
         if(Game.getFloat() > 0.5f) blood.addParticle(x + size, y + size, 1.0f, Game.getFloat() * 80.0f, old.ticks - 1);
      }
     
      public void render(){
         if(gone){
            return;
         }
         // All of the particles should destroy themselves in a few seconds
        if(ticks > 0L){
            ticks--;
         }
         if(ticks <= 0 && Game.getFloat() > (59.9f / 60.0f)){
            gone = true;
         }
         alpha = Math.max(0.0f, alpha - 0.0001f);
         glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
         glBegin(GL_QUADS);
            glVertex2f(x, y);
            glVertex2f(x + size, y);
            glVertex2f(x + size, y + size);
            glVertex2f(x, y + size);
         glEnd();
         glColor3f(1.0f, 1.0f, 1.0f);
      }
     
   }
   
   private static class Emitter {
     
      private float radius;
     
      public Emitter(float radius){
         this.radius = radius;
      }
     
      public void spray(float cx, float cy, Blood blood, int degree_, int range){
         for(float _radius = 1.0f; _radius <= radius; _radius += 1.0f){
            for(int degree = degree_; degree <= degree_ + range; degree++){
            //   if(Game.getFloat() > (1.0f - percent)){
                 // Add a particle
                 float dx = _radius * (float)MathHelper.cos((float) Math.toRadians(degree));
                  float dy = _radius * (float)MathHelper.sin((float) Math.toRadians(degree));
                  if(Game.getFloat() > 0.75f) blood.addParticle(dx + cx, dy + cy, 1.0f, _radius, 500 - Math.round(_radius));
            //   }
           }
         }
      }
     
   }

}


CopyableCougar4

Either wandering the forum or programming. Most likely the latter Smiley

Github: http://github.com/CopyableCougar4
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline Longarmx
« Reply #3 - Posted 2014-07-27 23:11:46 »

One of the easiest ways to increase performance is to stop using immediate mode  Wink

Online BurntPizza
« Reply #4 - Posted 2014-07-27 23:27:09 »

Pretty sure you can call glBegin/End outside of the particle render loop instead of for every particle, so try that first, but yeah, no real reason to stick around with that crap.

Could you describe in more high-level terms (pseudocode perhaps, general algorithm description) what it is you're actually trying to do? I'm having trouble determining it but it looks slower than it needs to be.
Offline CopyableCougar4

Senior Member


Medals: 11
Exp: 1 year



« Reply #5 - Posted 2014-07-28 00:09:14 »

My attempt at a description of what I'm going for:

When blood needs to be added:
1  
2  
3  
4  
5  
6  
7  
8  
9  
for(distance from 1 to the given radius){
  for(angle from 0 to 360){
    dx = cos(angle in radians)
    dy = sin(angle in radians)
    if(random chance){
       add a particle at the given position
    }
  }
}


Updating the blood
1  
2  
3  
4  
for(Particle in particle arraylist){
  if the spread distance is less than 0 it "dies"
  spread the particle to the 8 surrounding locations, by cloning and reducing the life / spread distance
}


Rendering the blood
1  
2  
3  
for(Particle in particle){
  draw colored quad
}


When a particle is added there is also a check in the constructor to see if that particle collides with a wall. I hope I was descriptive enough Smiley

CopyableCougar4

Either wandering the forum or programming. Most likely the latter Smiley

Github: http://github.com/CopyableCougar4
Pages: [1]
  ignore  |  Print  
 
 

 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

Pippogeek (39 views)
2014-09-24 16:13:29

Pippogeek (30 views)
2014-09-24 16:12:22

Pippogeek (19 views)
2014-09-24 16:12:06

Grunnt (45 views)
2014-09-23 14:38:19

radar3301 (28 views)
2014-09-21 23:33:17

BurntPizza (63 views)
2014-09-21 02:42:18

BurntPizza (33 views)
2014-09-21 01:30:30

moogie (41 views)
2014-09-21 00:26:15

UprightPath (50 views)
2014-09-20 20:14:06

BurntPizza (54 views)
2014-09-19 03:14:18
List of Learning Resources
by Longor1996
2014-08-16 10:40:00

List of Learning Resources
by SilverTiger
2014-08-05 19:33:27

Resources for WIP games
by CogWheelz
2014-08-01 16:20:17

Resources for WIP games
by CogWheelz
2014-08-01 16:19:50

List of Learning Resources
by SilverTiger
2014-07-31 16:29:50

List of Learning Resources
by SilverTiger
2014-07-31 16:26:06

List of Learning Resources
by SilverTiger
2014-07-31 11:54:12

HotSpot Options
by dleskov
2014-07-08 01:59:08
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!