Java-Gaming.org    
Featured games (91)
games approved by the League of Dukes
Games in Showcase (579)
games submitted by our members
Games in WIP (500)
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  
  Multiple AlphaComposite Sources?  (Read 1026 times)
0 Members and 1 Guest are viewing this topic.
Offline packetpirate

Senior Member


Medals: 1
Projects: 2



« Posted 2013-09-06 06:04:12 »

I'm having a little trouble with the AlphaComposite class in Java's AWT package. I'm trying to use it as a "lighting" system, where I use radial gradients as sources of light, but I can't figure out how to make multiple light sources, or, in other words, composite several light sources with the same overlaid translucent black rectangle. If you don't quite understand what I mean, see the attached picture... it's of the effect as it is right now in my game.



Is there a way to use this class to composite the same rectangle with multiple "light sources", or do I have to use another method? If so, what method could I use for lighting of a similar effect? I'm using Java2D for my graphics.
Offline Several Kilo-Bytes

Senior Member


Medals: 11



« Reply #1 - Posted 2013-09-06 06:31:40 »

Let one composite object know every light source, then determine the brightness at each point based on some function like the sum of the brightness of each individual source. This is the simplest way and each pixel only needs to be multiplied once. (Instead of once per light source.)
Offline philfrei
« Reply #2 - Posted 2013-09-06 06:35:32 »

Beautiful! That looks really great. I've only seen work of this quality done with OpenGL up to now.

I'm curious how you are doing this. I've only used AlphaComposites on a graphics object, effecting whatever I draw on that graphics object. How do you get the radial gradient?

"Greetings my friends! We are all interested in the future, for that is where you and I are going to spend the rest of our lives!" -- The Amazing Criswell
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline packetpirate

Senior Member


Medals: 1
Projects: 2



« Reply #3 - Posted 2013-09-06 06:52:37 »

Beautiful! That looks really great. I've only seen work of this quality done with OpenGL up to now.

I'm curious how you are doing this. I've only used AlphaComposites on a graphics object, effecting whatever I draw on that graphics object. How do you get the radial gradient?


Here's the snippet of code that draws it.

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
{ // Draw circle of light around player.
   float radius = 200.0f;
    float [] dist = {0.0f, 1.0f};
    Color [] colors = {new Color(0.0f, 0.0f, 0.0f, 0.0f), Color.BLACK};
    RadialGradientPaint p = new RadialGradientPaint(new Point2D.Double(player.getCenterX(), player.getCenterY()),
                                                                    radius, dist, colors);
    g2d.setPaint(p);
    g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.95f));
    g2d.fillRect(0, 0, Globals.W_WIDTH, Globals.W_HEIGHT);
} // End drawing circle of light.
Offline packetpirate

Senior Member


Medals: 1
Projects: 2



« Reply #4 - Posted 2013-09-06 06:53:45 »

Let one composite object know every light source, then determine the brightness at each point based on some function like the sum of the brightness of each individual source. This is the simplest way and each pixel only needs to be multiplied once. (Instead of once per light source.)

Not quite sure how to do that. Do you have an example? So far as I know, only one composite can be set on a shape.
Offline philfrei
« Reply #5 - Posted 2013-09-06 13:53:16 »

Very cool! Now, it seems to me you could draw the very same graphic without using the setComposite(), if the opaque black in the radial paint were set to (0, 0, 0, 0.95), yes?

Similarly, you could also build a BufferedImage with the same colors, by computing the alpha component as am inverse function of distance to the center of the image. Then, the image could be moved around like a spotlight, and black rectangles drawn as fill in the area outside the image.

But I can't think of how to combine two of these images and have the lit areas "add together". It seems like by this method the opaque aspects would add, instead. I made some smoke in a similar fashion, by creating a bunch of graphics that grew more transparent towards the edges, and overlaid them, but of course it was the opaque pixels that accumulated visually.

I think what Several Kilo-Bytes is suggesting, is also to make a BufferedImage. As you go through the image setting each pixels' color, compute the distance to the center of each light, and then add those alphas. Except, since the "added light" is actually a reduction in the alpha value, the adding would have to be in reverse somehow.

And computing all those distances seems like it would be really slow. Maybe this: construct a one-time reference array with the alphas all computed for a single spotlight. Then, to make an image to draw over the scene with two spotlights, offset your indexes into the reference array to simulate the spotlight locations and use the values you get from the two lookups.

I would also consider reversing the alpha values in the reference array. That way, they could be added together, then subtracted from 1 when you assign the alpha for the pixel in the new graphic.
 
Make sense?

This is going to chew up some cpus. OpenGL could do this a LOT faster.

"Greetings my friends! We are all interested in the future, for that is where you and I are going to spend the rest of our lives!" -- The Amazing Criswell
Offline Abuse

JGO Coder


Medals: 10


falling into the abyss of reality


« Reply #6 - Posted 2013-09-06 14:30:44 »

Construct your shadow mask in a buffer (using DST_IN), and then draw it over the top of everything else.

Quick hacked together example (using my 4k template as a basis).

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  
136  
137  
138  
139  
140  
141  
142  
143  
144  
145  
146  
147  
148  
149  
150  
151  
152  
153  
154  
155  
156  
157  
158  
159  
160  
161  
162  
163  
164  
165  
166  
167  
168  
169  
170  
171  
172  
173  
174  
175  
176  
177  
178  
179  
180  
181  
182  
183  
184  
185  
186  
187  
188  
189  
190  
191  
192  
193  
194  
195  
196  
197  
198  
199  
200  
201  
202  
203  
204  
205  
206  
207  
208  
209  
210  
211  
212  
213  
214  
215  
import java.applet.Applet;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Event;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RadialGradientPaint;
import java.awt.image.BufferedImage;
import java.util.Random;

public class G extends Applet implements Runnable {

   static final boolean DEBUG = true;
   static final int WIDTH = 800, HEIGHT = 600;

   static final long NANO_SECOND = 1000000000;
   static final long NANO_FRAMERATE_SAMPLE_LENGTH = NANO_SECOND/4;
   static final long NANO_FRAME_LENGTH = (NANO_SECOND / 60);

   public void start() {
      new Thread(this).start();
   }
   
   public void run() {
     
      Random r = new Random();

      BufferedImage buffer = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
      Graphics2D bg = (Graphics2D)buffer.getGraphics();

      BufferedImage shadowBuffer = new BufferedImage(WIDTH,HEIGHT,BufferedImage.TYPE_INT_ARGB);
      Graphics2D sg = (Graphics2D)shadowBuffer.getGraphics();
     
      long previousSampleStartTime;
      int frameCount;
      float fps;

      if (DEBUG) {
         previousSampleStartTime = System.nanoTime();
         frameCount = 0;
         fps = 0f;
      }

      long nextFrameStartTime, frameStartTime;
      nextFrameStartTime = System.nanoTime();

      //offsets in the ctrls[] that the mouse left/right/middle button states are stored.
     final int LMB = 0/Event.META_MASK;
      final int RMB = Event.META_MASK/Event.META_MASK;
      final int MMB = Event.ALT_MASK/Event.META_MASK;
      final int KEY_START = MMB+1;
     
      final int CONTROLS_LENGTH = 65536; // this needs to be large enough for no (remotely common) keys to throw out of bounds.
     
      // traditional key state array
     // For gui-type interfaces the processing should be done on a per event basis, so no state changes are missed.
     int [] ctrls = new int[CONTROLS_LENGTH];
      int mouseX=-1, mouseY=-1;

      final int LIGHT_COUNT = 10;
     
      int [] lightsX = new int[LIGHT_COUNT];
      int [] lightsY = new int[LIGHT_COUNT];
      int [] lightsDx = new int[LIGHT_COUNT];
      int [] lightsDy = new int[LIGHT_COUNT];
     
      for(int i = 1;i < LIGHT_COUNT;i++) {
         lightsX[i] = r.nextInt(WIDTH);
         lightsY[i] = r.nextInt(HEIGHT);
         lightsDx[i] = (r.nextInt(2)+1)*(r.nextInt(2)*2-1);
         lightsDy[i] = (r.nextInt(2)+1)*(r.nextInt(2)*2-1);
      }
           
      // enter game loop.
     do {

         while (nextFrameStartTime > (frameStartTime = System.nanoTime())) {
//               try{Thread.sleep(1);}catch(Exception e){} //ruins framerate accuracy, not necessary in Windows.
        }
         
         // Render
        bg.setColor(Color.black);
         bg.fillRect(0, 0, WIDTH, HEIGHT);        

         // note. Do processing of the queued key events as late as possible in the game loop so as to maximize responsiveness
        synchronized(this) {
            Event e;
            while((e = inputHead)!=null) {
               final int eventType = e.id;
               
               final int eventClass = eventType/100;
               
               final int KEY_EVENT = (Event.KEY_PRESS-1)/100;
               final int MOUSE_EVENT = (Event.MOUSE_DOWN-1)/100;
               
               if(eventClass==MOUSE_EVENT) {
                  bg.setColor(Color.white);
                  bg.drawLine(mouseX, mouseY, e.x, e.y);
                  mouseX = e.x;
                  mouseY = e.y;
                 
                  if(eventType<Event.MOUSE_MOVE) { // event must be MOUSE_DOWN or MOUSE_UP
                    ctrls[(e.modifiers & (Event.META_MASK|Event.ALT_MASK))/Event.META_MASK] = Event.MOUSE_UP-eventType;
                  }
               }
               
               if(eventClass==KEY_EVENT) {
                  ctrls[e.key] = eventType &1;
               }
               inputHead = e.evt;
            }
         }
         // light 0 tracks the mouse position.
        lightsX[0] = mouseX;
         lightsY[0] = mouseY;
         // other lights just fly around a bit.
        for(int i = 1;i < LIGHT_COUNT;i++ ) {
            lightsX[i]+=WIDTH+lightsDx[i];
            lightsY[i]+=HEIGHT+lightsDy[i];
           
            lightsX[i]%=WIDTH;
            lightsY[i]%=HEIGHT;
         
         }
         
         // end of logic
       
         
         /// draw background patternation
        for(int i = 0;i < WIDTH;i+=40) {
            bg.setColor(Color.BLUE);
            bg.drawLine(i,0,i,HEIGHT);
         }
         for(int i = 0;i < HEIGHT;i+=40) {
            bg.setColor(Color.RED);
            bg.drawLine(0,i,WIDTH,i);
         }

         // clear the shadow buffer
        sg.setComposite(AlphaComposite.Src);
         sg.setColor(Color.BLACK);
         sg.fillRect(0, 0, WIDTH,HEIGHT);
         // and ready it for this frame's shadows
        sg.setComposite(AlphaComposite.DstIn);
         
         
         for(int i = 0;i < LIGHT_COUNT;i++) {
             int radius = 100;
             float [] dist = {0.0f, 1.0f};
             Color [] colors = {new Color(0.0f, 0.0f, 0.0f, 0.0f), Color.BLACK};
             RadialGradientPaint p = new RadialGradientPaint(new Point(lightsX[i], lightsY[i]),
                                                                             radius, dist, colors);
             sg.setPaint(p);
             // there might be an 'off by one' bug here. Haven't spent the time thinking whether fillRect bounds match circle+/-radius.
            sg.fillRect(lightsX[i]-radius, lightsY[i]-radius, radius*2, radius*2);
             
         }
         
         if (DEBUG) {
            //framerate counter & key/mouse state reporting
           frameCount++;
            long now = System.nanoTime();
            if (now - previousSampleStartTime >= NANO_FRAMERATE_SAMPLE_LENGTH) {
               fps = frameCount * (float)NANO_FRAMERATE_SAMPLE_LENGTH/(now - previousSampleStartTime) * (float)NANO_SECOND/NANO_FRAMERATE_SAMPLE_LENGTH;
               previousSampleStartTime = now;
               frameCount = 0;
            }

            bg.setColor(Color.white);
            bg.drawString(String.format("FPS: %.2f", fps), 20, 30);
            bg.drawString(String.format("Mouse[left,middle,right]=[%d,%d,%d]", ctrls[LMB], ctrls[MMB], ctrls[RMB]), 20, 45);

            int keyDownCount = 0;
            for (int i = KEY_START; i < CONTROLS_LENGTH; i++) {
               if (ctrls[i] != 0) {
                  keyDownCount++;
                  bg.drawString(new String(new char[] { (char) i }), 20, 60 + 15 * keyDownCount);
               }
            }            
           
         }
         
         bg.drawImage(shadowBuffer,0,0,null);

         // Draw the entire results on the screen.
        final Graphics g = getGraphics();

         if (g != null)
            g.drawImage(buffer, 0, 0, null);

         nextFrameStartTime = frameStartTime + NANO_FRAME_LENGTH;
      }
      while(isActive());
     
      // shutdown here
  }

   Event inputHead;

   public synchronized boolean handleEvent(Event e) {
      // we abuse input Events by constructing a linkedlist from them.
     if(inputHead==null) {
         inputHead = e;
      }
      else {
         // note we have to iterate to the end of the list every time we add an event
        // not a performance concern as the list will never grow very large (it's reset upto 60 times/second)
        Event ll = inputHead;
         while(ll.evt!=null) ll = ll.evt;
         ll.evt = e;
      }
      return false;
   }
}

Make Elite IV:Dangerous happen! Pledge your backing at KICKSTARTER here! https://dl.dropbox.com/u/54785909/EliteIVsmaller.png
Offline Abuse

JGO Coder


Medals: 10


falling into the abyss of reality


« Reply #7 - Posted 2013-09-06 15:13:42 »

Please note the above example is not written for optimal performance.

Ideally the backBuffer would be a Transparency.OPAQUE VolatileImage (or a BufferStrategy).

As to whether the shadowBuffer should be an Transparency.TRANSLUCENT VolatileImage, or an ARGB BufferedImage... that's a little more complicated.

If draw operations with a RadialGradientPaint are accelerated by the DirectX (or OpenGL) pipeline, then the shadowBuffer should be a TRANSLUCENT VolatileImage.
On the other hand, if RadialGradientPaint is not accelerated, then the pixel readback from a VolatileImage required for the DST_IN composition will absolutely destroy performance, so a BufferedImage should be used.

If you're serious about using Java2D, and want your code to run optimally (though not necessarily well) on lots of configurations then you have to do live profiling at runtime to determine which combinations of operations & surfaces perform acceptably on the user's hardware/driver configuration.

Make Elite IV:Dangerous happen! Pledge your backing at KICKSTARTER here! https://dl.dropbox.com/u/54785909/EliteIVsmaller.png
Offline packetpirate

Senior Member


Medals: 1
Projects: 2



« Reply #8 - Posted 2013-09-06 16:16:52 »

I am so confused... there's no simpler way to do this? That's an awful lot of code for what seems like it should be a simple effect. Are there other methods of achieving this that don't involve composites? I was hoping for a method similar to that used by Notch in the game Left 4k Dead.

https://mojang.com/notch/j4k/l4kd/
Offline Abuse

JGO Coder


Medals: 10


falling into the abyss of reality


« Reply #9 - Posted 2013-09-06 16:31:52 »

I am so confused... there's no simpler way to do this? That's an awful lot of code for what seems like it should be a simple effect. Are there other methods of achieving this that don't involve composites?

That's an entire application, so you can see it running in situ.


If you want only the relevant code, here it is:
Create the shadow mask; do this once, and keep it around:

1  
2  
      BufferedImage shadowBuffer = new BufferedImage(WIDTH,HEIGHT,BufferedImage.TYPE_INT_ARGB);
      Graphics2D sg = (Graphics2D)shadowBuffer.getGraphics();


Then, each frame....
Clear the shadow mask:

1  
2  
3  
4  
5  
6  
// clear the shadow buffer
        sg.setComposite(AlphaComposite.Src);
         sg.setColor(Color.BLACK);
         sg.fillRect(0, 0, WIDTH,HEIGHT);
         // and ready it for this frame's shadows
        sg.setComposite(AlphaComposite.DstIn);


Then draw each light source onto the shadow mask:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
         for(int i = 0;i < LIGHT_COUNT;i++) {
             int radius = 100;
             float [] dist = {0.0f, 1.0f};
             Color [] colors = {new Color(0.0f, 0.0f, 0.0f, 0.0f), Color.BLACK};
             RadialGradientPaint p = new RadialGradientPaint(new Point(lightsX[i], lightsY[i]),
                                                                             radius, dist, colors);
             sg.setPaint(p);
             // there might be an 'off by one' bug here. Haven't spent the time thinking whether fillRect bounds match circle+/-radius.
            sg.fillRect(lightsX[i]-radius, lightsY[i]-radius, radius*2, radius*2);
             
         }


Finally, once you've rendered everything else in your world, draw the shadow mask over the top:
1  
bg.drawImage(shadowBuffer,0,0,null);


It's basically the code you posted, wrapped in a for loop, with a few extra lines of initialisation. Simples.

Make Elite IV:Dangerous happen! Pledge your backing at KICKSTARTER here! https://dl.dropbox.com/u/54785909/EliteIVsmaller.png
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline Abuse

JGO Coder


Medals: 10


falling into the abyss of reality


« Reply #10 - Posted 2013-09-06 16:39:01 »

Are there other methods of achieving this that don't involve composites? I was hoping for a method similar to that used by Notch in the game Left 4k Dead.

https://mojang.com/notch/j4k/l4kd/

Yes, you can do the lighting yourself at a per-pixel level.
That's probably how notch did it in L4KD; you can see for yourself, he did make the code available.

Doing it this way is far more flexible (you can do your own shadow casting calculations and such), but obviously it's a lot more complicated too.

Make Elite IV:Dangerous happen! Pledge your backing at KICKSTARTER here! https://dl.dropbox.com/u/54785909/EliteIVsmaller.png
Offline packetpirate

Senior Member


Medals: 1
Projects: 2



« Reply #11 - Posted 2013-09-06 16:39:50 »

I am so confused... there's no simpler way to do this? That's an awful lot of code for what seems like it should be a simple effect. Are there other methods of achieving this that don't involve composites?

That's an entire application, so you can see it running in situ.


If you want only the relevant code, here it is:
Create the shadow mask; do this once, and keep it around:

1  
2  
      BufferedImage shadowBuffer = new BufferedImage(WIDTH,HEIGHT,BufferedImage.TYPE_INT_ARGB);
      Graphics2D sg = (Graphics2D)shadowBuffer.getGraphics();


Then, each frame....
Clear the shadow mask:

1  
2  
3  
4  
5  
6  
// clear the shadow buffer
        sg.setComposite(AlphaComposite.Src);
         sg.setColor(Color.BLACK);
         sg.fillRect(0, 0, WIDTH,HEIGHT);
         // and ready it for this frame's shadows
        sg.setComposite(AlphaComposite.DstIn);


Then draw each light source onto the shadow mask:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
         for(int i = 0;i < LIGHT_COUNT;i++) {
             int radius = 100;
             float [] dist = {0.0f, 1.0f};
             Color [] colors = {new Color(0.0f, 0.0f, 0.0f, 0.0f), Color.BLACK};
             RadialGradientPaint p = new RadialGradientPaint(new Point(lightsX[i], lightsY[i]),
                                                                             radius, dist, colors);
             sg.setPaint(p);
             // there might be an 'off by one' bug here. Haven't spent the time thinking whether fillRect bounds match circle+/-radius.
            sg.fillRect(lightsX[i]-radius, lightsY[i]-radius, radius*2, radius*2);
             
         }


Finally, once you've rendered everything else in your world, draw the shadow mask over the top:
1  
bg.drawImage(shadowBuffer,0,0,null);


It's basically the code you posted, wrapped in a for loop, with a few extra lines of initialisation. Simples.

Alright... you had me a little worried with all the other code in your example.
In the link I posted to Left 4k Dead, how did Notch do his lighting? He used Java2D for the game, but seeing as how the code he released for the game is obfuscated, I couldn't figure out how he did it. I think he used a raycasting system, but I haven't been able to make a proper implementation of one of those yet, nor would I know what to do with the rays to achieve the "lighting".
Offline packetpirate

Senior Member


Medals: 1
Projects: 2



« Reply #12 - Posted 2013-09-06 16:40:47 »

Are there other methods of achieving this that don't involve composites? I was hoping for a method similar to that used by Notch in the game Left 4k Dead.

https://mojang.com/notch/j4k/l4kd/

Yes, you can do the lighting yourself at a per-pixel level.
That's probably how notch did it in L4KD; you can see for yourself, he did make the code available.

Doing it this way is far more flexible (you can do your own shadow casting calculations and such), but obviously it's a lot more complicated too.

Yeah, I have the code downloaded, but it's obfuscated and not commented, so I don't know what to do with it. I think it's a raycasting system, but like I said above, I'm not sure how to do that.
Offline Phased
« Reply #13 - Posted 2013-09-06 16:46:52 »

If you see obfuscated code, you must have the wrong file, the code isn't obfuscated but the variable names are not the best as he had to fit it into 4 kb to fit the competition.

https://mojang.com/notch/j4k/l4kd/G.java

if it was obfuscated, you would literally have no idea whats going on, some things can give you hints, and you could probably be able to rename the variables to better ones, or comment sections of code to what is happening.

put the code into your IDE and just start changing stuff if needed to figure out what it does.
Offline packetpirate

Senior Member


Medals: 1
Projects: 2



« Reply #14 - Posted 2013-09-06 16:59:22 »

I am so confused... there's no simpler way to do this? That's an awful lot of code for what seems like it should be a simple effect. Are there other methods of achieving this that don't involve composites?

That's an entire application, so you can see it running in situ.


If you want only the relevant code, here it is:
Create the shadow mask; do this once, and keep it around:

1  
2  
      BufferedImage shadowBuffer = new BufferedImage(WIDTH,HEIGHT,BufferedImage.TYPE_INT_ARGB);
      Graphics2D sg = (Graphics2D)shadowBuffer.getGraphics();


Then, each frame....
Clear the shadow mask:

1  
2  
3  
4  
5  
6  
// clear the shadow buffer
        sg.setComposite(AlphaComposite.Src);
         sg.setColor(Color.BLACK);
         sg.fillRect(0, 0, WIDTH,HEIGHT);
         // and ready it for this frame's shadows
        sg.setComposite(AlphaComposite.DstIn);


Then draw each light source onto the shadow mask:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
         for(int i = 0;i < LIGHT_COUNT;i++) {
             int radius = 100;
             float [] dist = {0.0f, 1.0f};
             Color [] colors = {new Color(0.0f, 0.0f, 0.0f, 0.0f), Color.BLACK};
             RadialGradientPaint p = new RadialGradientPaint(new Point(lightsX[i], lightsY[i]),
                                                                             radius, dist, colors);
             sg.setPaint(p);
             // there might be an 'off by one' bug here. Haven't spent the time thinking whether fillRect bounds match circle+/-radius.
            sg.fillRect(lightsX[i]-radius, lightsY[i]-radius, radius*2, radius*2);
             
         }


Finally, once you've rendered everything else in your world, draw the shadow mask over the top:
1  
bg.drawImage(shadowBuffer,0,0,null);


It's basically the code you posted, wrapped in a for loop, with a few extra lines of initialisation. Simples.

Got it! It wasn't so hard after all. Now I'm going to create a LightSource class to store the light's information in and add a new item to the game... Flares!

Offline packetpirate

Senior Member


Medals: 1
Projects: 2



« Reply #15 - Posted 2013-09-06 17:00:19 »

If you see obfuscated code, you must have the wrong file, the code isn't obfuscated but the variable names are not the best as he had to fit it into 4 kb to fit the competition.

https://mojang.com/notch/j4k/l4kd/G.java

if it was obfuscated, you would literally have no idea whats going on, some things can give you hints, and you could probably be able to rename the variables to better ones, or comment sections of code to what is happening.

put the code into your IDE and just start changing stuff if needed to figure out what it does.

You don't call that obfuscated? And yes, that's the file I have. None of his variable names are descriptive, there's no commenting, and everything is just one big mess of code.
Offline Abuse

JGO Coder


Medals: 10


falling into the abyss of reality


« Reply #16 - Posted 2013-09-06 17:29:38 »

Beautiful isn't it Wink

Just wait until you get asked to port code like that... but with comments and naming in another language... that uses a different character set Smiley

It gets to the point where you work from the binary, and use reflection and instrumentation to make changes. Horrifying.

Make Elite IV:Dangerous happen! Pledge your backing at KICKSTARTER here! https://dl.dropbox.com/u/54785909/EliteIVsmaller.png
Offline Abuse

JGO Coder


Medals: 10


falling into the abyss of reality


« Reply #17 - Posted 2013-09-06 17:41:44 »

It should be relatively simple to add coloured lighting, and by altering the geometry of what you draw to the shadow mask you can do simple directional light volumes too.  (CAG with the Area class would be a quick and dirty way of doing it)

Make Elite IV:Dangerous happen! Pledge your backing at KICKSTARTER here! https://dl.dropbox.com/u/54785909/EliteIVsmaller.png
Offline packetpirate

Senior Member


Medals: 1
Projects: 2



« Reply #18 - Posted 2013-09-06 18:16:33 »

It should be relatively simple to add coloured lighting, and by altering the geometry of what you draw to the shadow mask you can do simple directional light volumes too.  (CAG with the Area class would be a quick and dirty way of doing it)

I could just make a polygon in the shape of a front-facing cone and use a regular gradient paint on it, right?
Also, for colored lighting, could I just change the color values passed to the gradient?
Offline StumpyStrust
« Reply #19 - Posted 2013-09-06 19:46:52 »

Quick tips on this stuff. First it is faster to draw images then gradients in java2D and you can use images as lights and color them on the fly.  Second, the way you have it right now, simply drawing multiple radial gradiant paints on top of your scene should be fine but don't use a bufferedImage back buffer because it is very very slow. Second, stay away from java's Area class.  Super slow.

What I recommend. Keep using the gradients as you probably do not need hundreds of lights. Set the rgba of the color of a light to what ever you want. Then instead of clearing to black, clear with a color of (0,0,0,0), empty. Instead of using a BufferedImage to draw with, create a compatible volatileiamge and draw all the lights to that with the SCR_OVER rule. Once done, composite that over the scene like you are doing.  Drawing with/clipping with polygons is rather fast. Go with that.

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  
   public void render(Graphics g)
   {
      if(!LIGHT_ON) // mapg2d is the Graphics a a VolatileImage
        return;
      clear();
      mapg2d.setColor(ambientColor);
      mapg2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, ambientLight));
      mapg2d.fillRect(offX,offY,mapWidth,mapHeight);
       for(int i = 0; i <= lights.size()-1;i++)
      {
         lights.get(i).render(mapg2d);
      }

      Graphics2D g2d = (Graphics2D) g.create();
      g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, colorBlend)); // the amount of color we want from the light map to be composited into the scene
     g2d.drawImage(lightmap,0,0,scrWidth,scrHeight,null);
      g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_ATOP, 1));
      g2d.drawImage(lightmap,0,0,scrWidth,scrHeight,null);
      g2d.dispose();
   }
   
   private void clear()
   {
      mapg2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC)); // this line may not be necessary.
     mapg2d.setColor(BLANK); // a static blank Color of (0,0,0,0);
     mapg2d.fillRect(0, 0, mapWidth, mapHeight);
   }

Offline packetpirate

Senior Member


Medals: 1
Projects: 2



« Reply #20 - Posted 2013-09-06 19:55:02 »

Quick tips on this stuff. First it is faster to draw images then gradients in java2D and you can use images as lights and color them on the fly.  Second, the way you have it right now, simply drawing multiple radial gradiant paints on top of your scene should be fine but don't use a bufferedImage back buffer because it is very very slow. Second, stay away from java's Area class.  Super slow.

What I recommend. Keep using the gradients as you probably do not need hundreds of lights. Set the rgba of the color of a light to what ever you want. Then instead of clearing to black, clear with a color of (0,0,0,0), empty. Instead of using a BufferedImage to draw with, create a compatible volatileiamge and draw all the lights to that with the SCR_OVER rule. Once done, composite that over the scene like you are doing.  Drawing with/clipping with polygons is rather fast. Go with that.

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  
   public void render(Graphics g)
   {
      if(!LIGHT_ON) // mapg2d is the Graphics a a VolatileImage
        return;
      clear();
      mapg2d.setColor(ambientColor);
      mapg2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, ambientLight));
      mapg2d.fillRect(offX,offY,mapWidth,mapHeight);
       for(int i = 0; i <= lights.size()-1;i++)
      {
         lights.get(i).render(mapg2d);
      }

      Graphics2D g2d = (Graphics2D) g.create();
      g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, colorBlend)); // the amount of color we want from the light map to be composited into the scene
     g2d.drawImage(lightmap,0,0,scrWidth,scrHeight,null);
      g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_ATOP, 1));
      g2d.drawImage(lightmap,0,0,scrWidth,scrHeight,null);
      g2d.dispose();
   }
   
   private void clear()
   {
      mapg2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC)); // this line may not be necessary.
     mapg2d.setColor(BLANK); // a static blank Color of (0,0,0,0);
     mapg2d.fillRect(0, 0, mapWidth, mapHeight);
   }


At the most, there will probably only be 4-5 lights at a time... the one over the player, and the ones created by Flares. I'm not going to do anything crazy like create a light for every Flamethrower fire particle... that would be nonsense. But how can I give the effect of the fire particles lighting up? It seems strange for fire not to light up the surrounding area.
Offline Several Kilo-Bytes

Senior Member


Medals: 11



« Reply #21 - Posted 2013-09-06 20:50:11 »

Let one composite object know every light source, then determine the brightness at each point based on some function like the sum of the brightness of each individual source. This is the simplest way and each pixel only needs to be multiplied once. (Instead of once per light source.)

Not quite sure how to do that. Do you have an example? So far as I know, only one composite can be set on a shape.

Sorry I misread your question. I thought you implemented a custom compositer. Compositers just do math on pixel values in a raster image. It might not be particularly bad on the CPU. The distance function can be done entirely with integer math and no square root. If the scene doesn't change much you could "cache" a light map as an image. Maybe you could even reduce the resolution. Or use a brushfire based algorithm.

I thought you had more done already and weren't asking for general advice. Do look at other people's advice. Ray casting might be better because you can create shadows. Just using a Composite class would be suitable for using illumination sources as a game mechanic (not necessarily a photorealistic aesthetic effect) or as a fog of war effect.
Offline StumpyStrust
« Reply #22 - Posted 2013-09-06 21:00:36 »

Hehe, if you want to create a light effect for each fire particle that is very easy. They key to getting java2d to perform is to JUST draw images. That is it. Particles are images. Draw your fire particles over your lightmap. That will give a nice effect. Or, have a light attached to each particle. The big slow down in the lighting for java2d is not drawing the light but finding the shadows. Right now I have a system that can do 200+ lights that are fully dynamic casting shadows. However, this is assuming everything blocking light is a square or circle. It works with arbitrary shapes but that is slow.

The moment you start doing any custom composites or grabbing pixels from a BI, everything comes to a halt. If your game is only 300x300 or so resolution then you can do some fancy stuff like left 4k dead.

Offline Abuse

JGO Coder


Medals: 10


falling into the abyss of reality


« Reply #23 - Posted 2013-09-06 21:40:40 »

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  
   public void render(Graphics g)
   {
      if(!LIGHT_ON) // mapg2d is the Graphics a a VolatileImage
        return;
      clear();
      mapg2d.setColor(ambientColor);
      mapg2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, ambientLight));
      mapg2d.fillRect(offX,offY,mapWidth,mapHeight);
       for(int i = 0; i <= lights.size()-1;i++)
      {
         lights.get(i).render(mapg2d);
      }

      Graphics2D g2d = (Graphics2D) g.create();
      g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, colorBlend)); // the amount of color we want from the light map to be composited into the scene
     g2d.drawImage(lightmap,0,0,scrWidth,scrHeight,null);
      g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_ATOP, 1));
      g2d.drawImage(lightmap,0,0,scrWidth,scrHeight,null);
      g2d.dispose();
   }
   
   private void clear()
   {
      mapg2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC)); // this line may not be necessary.
     mapg2d.setColor(BLANK); // a static blank Color of (0,0,0,0);
     mapg2d.fillRect(0, 0, mapWidth, mapHeight);
   }


I've tried integrating this technique into my little sample applet above, and it isn't doing anything meaningful.

Could you better explain what precisely SRC_OVER, followed by a DST_ATOP achieves?
I can't visualize the port-duff composition rules well enough to see how it'd do what the OP wants.

In your example what are typical values for ambientColor, ambientLight, and colorBlend?
Also, what alpha values do the light gradients in your example go from? d0[0,0,0,0f] -> d1[0,0,0,1f] like the example I posted above? or the reverse?

:edit:




AHHHHHHH.

Nevermind, I just read this explanation which made what you were doing click in my mind.
Quote
This is accomplished by rendering our lights with the "SRC_OVER" option which allows the image to be drawn over the Lightmap image then the Lightmap is draw over our tiles / background etc... (anything effected by light) using the "DST_ATOP" method which replaces our lights with the tiles / background (objects under our lights).

Make Elite IV:Dangerous happen! Pledge your backing at KICKSTARTER here! https://dl.dropbox.com/u/54785909/EliteIVsmaller.png
Offline StumpyStrust
« Reply #24 - Posted 2013-09-07 00:09:16 »

Wow people really are looking at that?

Basically, SCR_OVER is the color and DIST_ATP is the alpha. This is just one way that I know works. There may be other better ones. But right now, with my current lighting code, I can have over 200 lights that are rather large and stay at 60 fps. You could easily attach a light to all projectiles. Maybe not to every particle in the game but flame thrower doesn't really shoot particles it shoots flames which are projectiles so that could still have a light on each projectile.

Pages: [1]
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 

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

The first screenshot will be displayed as a thumbnail.

xsi3rr4x (36 views)
2014-04-15 18:08:23

BurntPizza (32 views)
2014-04-15 03:46:01

UprightPath (47 views)
2014-04-14 17:39:50

UprightPath (30 views)
2014-04-14 17:35:47

Porlus (47 views)
2014-04-14 15:48:38

tom_mai78101 (68 views)
2014-04-10 04:04:31

BurntPizza (128 views)
2014-04-08 23:06:04

tom_mai78101 (228 views)
2014-04-05 13:34:39

trollwarrior1 (193 views)
2014-04-04 12:06:45

CJLetsGame (200 views)
2014-04-01 02:16:10
List of Learning Resources
by SHC
2014-04-18 03:17:39

List of Learning Resources
by Longarmx
2014-04-08 03:14:44

Good Examples
by matheus23
2014-04-05 13:51:37

Good Examples
by Grunnt
2014-04-03 15:48:46

Good Examples
by Grunnt
2014-04-03 15:48:37

Good Examples
by matheus23
2014-04-01 18:40:51

Good Examples
by matheus23
2014-04-01 18:40:34

Anonymous/Local/Inner class gotchas
by Roquen
2014-03-11 15:22:30
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!