Java-Gaming.org    
Featured games (91)
games approved by the League of Dukes
Games in Showcase (578)
games submitted by our members
Games in WIP (499)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: 1 [2]
  ignore  |  Print  
  JevaEngine - Underground  (Read 4789 times)
0 Members and 1 Guest are viewing this topic.
Offline Jeremy
« Reply #30 - Posted 2013-08-08 23:26:50 »

I have achieved a (highly unpolished) effect for casting shadows, depth sorting the light map, and applying the lighting without experiencing a drop in frame-rate. Here is how it looks:



You'll see in this screen-shot a diffuse light (not shaded in but that step I'd say is relatively easy in contrast to the steps to get there) casting shadows - the areas behind the entities that obstructs the light's passage _BUT_ also in the bounds of the light's maximum fall-out is shaded in as the ambient lighting but in reality this area is passed to the lights rendering routine to factor in the amount of obstruction that has occurred so that the light can apply a prettier gradient (or fading effect of some sort.)

The current implementation of the diffuse lighting is to just simply render obstructed regions the ambient colour and render non-obstructed regions the light's colour - obviously though you'd apply a fading effect as you got further from the light (that part of the implementation I haven't done yet - but as I said it is relatively easy.)

How I did it:
I don't guarantee this is the most optimal method, but for those interested:

Essentially, this effect is achieved by using a lot of Java shape operations - the rendering of the light map is accelerated by using a VolatileImage.

When the light map is being generated, the render routine does the following:

 1. Creates an Area object that contains a Rectangle that covers the
    entirety of the screen. This area will contain your ambient
    lighting.
    
 2. It then iterates through the lights asking them what their
    light-casting Area would be if there were no obstructions in the way.

 3. It takes this area object and searches the world for Actors\Tiles
    that are contained within that area that the light would be cast in.

 4. For every tile that it finds that obstructs view in the light's casting area, it will calculate the difference in the light source's position and the obstruction's
    position (essentially creating a vector that points _AT_ the
    obstruction from the light source - this is the direction you want to cast your shadow) This pointing vector (in world
    space) needs to be translated to screen space.

 5. Once that has been done, a perpendicular to that vector is taken and
    normalized. This essentially gives you a line you can travel up or
    down on by multiplying it by any given length to travel the given direction in. This vector is
    perpendicular to the direction you want to cast your shadow over.

 6. Almost done, you consturct a polygon that consists of four points.
    The first two points are at the the base of the screen coordinate of
    your obstruction's center point. To get the first point, you want to
    travel up your perpendicular vector (calculated in 5) a quantity of
    half your tile's height [ this is a relatively accurate
    approximation though I think this part of the algorithm is slightly
    incorrect - but it has no noticable decay on the visual effect] -
    then ofcourse add to that the obstructions origin. To get the
    second, you do the same but instead travel down.

 7. The remainder of the two points are calculated exactly the same way -
    only these points need to be projected outward in the direction of
    your shadow's projection vector calculated in 4. - You can choose any large amount to project it outwards by - just as long as it reaches at least outside of you light's casting area (so if you just want to do it stupidly multiply your shadow projection vector by a factor of 10 and you should be safe)

 8. From this polygon you just constructed, construct an area, and then
    invoke the "intersect" method with your light's area as the first
    argument - this will assure that your shadows area doesn't reach
    outside of the bounds of the area that your light casts over.

 9. Subtract from your light's casting the shadow area you constructed
    above. At this point you now have two areas - the area where the
    light casts unobstructed, and the area the light casts over
    obstructed - if your Actors have a visibility obstruction factor
    that you used to determine that a particular actor was obstructing
    view - you also have the grade at which it obstructs the view that
    you can apply later when you are drawing in the light effect (this will allow you to chose between a darker\brighter shade depending on how much light is being obstructed

 10. Subtract from your ambient light area you constructed in (1) both
     the light area, and the obstructed light area so you don't apply
     the ambient light to areas where the lighting effect will take over
     and render into


Now you need to merge your light map with your depth-buffered world's render routine


Now that you've rendered you're light map and it is contained inside of a volatile image, you need to throw it into your world's render routine and depth-sorting algorithm. Since the back-buffer and the light map are both volatileimages, rendering the light map over the world is relatively optimal.

You need to construct a polygon that is essentially a strip that contains what a vertical strip of your world tiles would be rendered into (look at my screen shot, you'll see an array of thin diagonal lines seperating these strips. These strips are what I am referring). You can than render parts of this light map strip by strip (render it over the strip after you've rendered the last tile in that strip since - obviously - the light map has to be applied _over_ the map). You can use the same image-map just use that strip as a clip for Graphics - you will need to translate that strip polygon down per render of a strip.


Anyway, like I said I don't guarantee this is the most optimal way - but so far it is working fine for me.
The light map is applied p

JevaEngine, Latest Playthrough (This demo is networked with a centralized server model)

http://www.youtube.com/watch?v=rWA8bajpVXg
Offline StumpyStrust
« Reply #31 - Posted 2013-08-09 08:22:40 »

How well do multiple lights work? What you are doing is almost the exact same thing I do. I am going to post the important part here it may be a bit long sorry.

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  
   private void createShadows()
   {
      if(baked)
         return;
      clear();
      for(int i = 0;i<= blocks.size()-1;i++)
      {
         Block blocker = blocks.get(i);
         if(blocker.collision(this))
         {
            Point p1 = null,p2 = null,p3,p4,p5 = null,p6 = null;
            Polygon poly = new Polygon();
            if(loc.x < blocker.loc.x + blocker.size.x/2 && loc.x> blocker.loc.x - blocker.size.x/2 )
            {
               if(blocker.loc.y >= loc.y)
               {
                  p1 = new Point((int)(blocker.loc.x - blocker.size.x/2),(int) (blocker.loc.y-blocker.size.y/2));
                  p2 = new Point((int)(blocker.loc.x + blocker.size.x/2),(int) (blocker.loc.y-blocker.size.y/2));
                  p5 = new Point((int)(blocker.loc.x + blocker.size.x/2),(int) (blocker.loc.y+blocker.size.y/2));
                  p6 = new Point((int)(blocker.loc.x - blocker.size.x/2),(int) (blocker.loc.y+blocker.size.y/2));
               }
               else
               {
                  p1 = new Point((int)(blocker.loc.x - blocker.size.x/2),(int) (blocker.loc.y+blocker.size.y/2));
                  p2 = new Point((int)(blocker.loc.x + blocker.size.x/2),(int) (blocker.loc.y+blocker.size.y/2));
                  p5 = new Point((int)(blocker.loc.x + blocker.size.x/2),(int) (blocker.loc.y-blocker.size.y/2));
                  p6 = new Point((int)(blocker.loc.x - blocker.size.x/2),(int) (blocker.loc.y-blocker.size.y/2));
               }
            }
            else if (loc.y < blocker.loc.y + blocker.size.y/2 && loc.y > blocker.loc.y - blocker.size.y/2 )
            {
               if(loc.x <= blocker.loc.x)
               {
                  p1 = new Point((int)(blocker.loc.x - blocker.size.x/2),(int) (blocker.loc.y-blocker.size.y/2));
                  p2 = new Point((int)(blocker.loc.x - blocker.size.x/2),(int) (blocker.loc.y+blocker.size.y/2));
                  p5 = new Point((int)(blocker.loc.x + blocker.size.x/2),(int) (blocker.loc.y+blocker.size.y/2));
                  p6 = new Point((int)(blocker.loc.x + blocker.size.x/2),(int) (blocker.loc.y-blocker.size.y/2));
               }
               else
               {
                  p1 = new Point((int)(blocker.loc.x + blocker.size.x/2),(int) (blocker.loc.y-blocker.size.y/2));
                  p2 = new Point((int)(blocker.loc.x + blocker.size.x/2),(int) (blocker.loc.y+blocker.size.y/2));
                  p5 = new Point((int)(blocker.loc.x - blocker.size.x/2),(int) (blocker.loc.y+blocker.size.y/2));
                  p6 = new Point((int)(blocker.loc.x - blocker.size.x/2),(int) (blocker.loc.y-blocker.size.y/2));
               }
            }
            else
            {
               if(blocker.loc.x < this.loc.x)
               {
                  if(blocker.loc.y-blocker.size.y/2 < loc.y)
                  {
                     p1 = new Point((int)(blocker.loc.x - blocker.size.x/2),(int) (blocker.loc.y+blocker.size.y/2));
                     p2 = new Point((int)(blocker.loc.x + blocker.size.x/2),(int) (blocker.loc.y-blocker.size.y/2));
                     p5 = new Point((int)(blocker.loc.x - blocker.size.x/2),(int) (blocker.loc.y-blocker.size.y/2));
                  }
                  else
                  {
                     p1 = new Point((int)(blocker.loc.x - blocker.size.x/2),(int) (blocker.loc.y-blocker.size.y/2));
                     p2 = new Point((int)(blocker.loc.x + blocker.size.x/2),(int) (blocker.loc.y+blocker.size.y/2));
                     p5 = new Point((int)(blocker.loc.x - blocker.size.x/2),(int) (blocker.loc.y+blocker.size.y/2));
                  }
               }
               else
               {
                  if(blocker.loc.y-blocker.size.y/2 < loc.y)
                  {
                     p1 = new Point((int)(blocker.loc.x - blocker.size.x/2),(int) (blocker.loc.y-blocker.size.y/2));
                     p2 = new Point((int)(blocker.loc.x + blocker.size.x/2),(int) (blocker.loc.y+blocker.size.y/2));
                     p5 = new Point((int)(blocker.loc.x + blocker.size.x/2),(int) (blocker.loc.y-blocker.size.y/2));
                  }
                  else
                  {
                     p1 = new Point((int)(blocker.loc.x + blocker.size.x/2),(int) (blocker.loc.y-blocker.size.y/2));
                     p2 = new Point((int)(blocker.loc.x - blocker.size.x/2),(int) (blocker.loc.y+blocker.size.y/2));
                     p5 = new Point((int)(blocker.loc.x + blocker.size.x/2),(int) (blocker.loc.y+blocker.size.y/2));
                  }
               }
            }
            Point p7 = null;
            if(blocker.size.x != blocker.size.y)
            {  
               double angle1 = Math.atan2(blocker.loc.y-loc.y, blocker.loc.x - loc.x)* 180/Math.PI;
               p7 = new Point((int)(loc.x + size.x *19* Math.cos(angle1 * Math.PI / 180)),
                        (int)(loc.y + size.y*19 * Math.sin(angle1 * Math.PI / 180)));
            }
            double angle1 = Math.atan2(p1.y-loc.y, p1.x - loc.x)* 180/Math.PI;
            double angle2 = Math.atan2(p2.y-loc.y, p2.x - loc.x)* 180/Math.PI;
            p3 = new Point((int)(loc.x + size.x *19* Math.cos(angle1 * Math.PI / 180)),
                     (int)(loc.y + size.y*19 * Math.sin(angle1 * Math.PI / 180)));
            p4 = new Point((int)(loc.x + size.x * 19 * Math.cos(angle2 * Math.PI / 180)),
                     (int)(loc.y + size.y*19 * Math.sin(angle2 * Math.PI / 180)));
           
            poly.addPoint(p1.x,p1.y);
            poly.addPoint(p3.x,p3.y);
            if(p7 != null)
               poly.addPoint(p7.x,p7.y);
            poly.addPoint(p4.x,p4.y);
            poly.addPoint(p2.x,p2.y);
            poly.addPoint(p5.x,p5.y);
            if(p6 != null)
               poly.addPoint(p6.x,p6.y);

            drawClip(poly);
         }
      }
   }
   
   private void drawClip(Polygon p)
   {
      p.translate(-(int)loc.x,-(int)loc.y);
      p.translate(light.getWidth()/2, light.getHeight()/2);
      if(father.getAA())
         lightg2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
      lightg2d.setColor(BLANK);
      lightg2d.fill(p);
   }


Basically, find the points of the shape that is going to be casting a shadow, find the angle to the light, stretch out and create another point, add to a polygon, and block out the shadowed part from the light. I have not done a whole lot of tinkering with shapes. Could be fun.

Here is a tip on how to clear a BufferedImage without recreating one or decelerating it. (I think at least)

1  
2  
3  
4  
5  
6  
7  
8  
9  
//take graphics of image
Graphics2D g2d = image.getGraphics();
//Create a cached version of a blank color
static final Color BLANK = new Color(0,0,0,0); // no alpha
//set cached blank color
g2d.setColor(BLANK);
//fill rect with the color
g2d.fillRect(0,0,imageWidth,imageHeight);
//now you have a completely blank image.

Offline Jeremy
« Reply #32 - Posted 2013-08-09 08:56:08 »

Thanks StumpyStrust you've been an awesome help - dynamic lighting in Java2D is a bit awkward and thankfully you've already done it and I appreciate your help.

With multiple lights it has been working as I expected but I can't say for sure until I add the light shading effect since the blending of the two will likely require some toying with.

I noticed that our algorithms are virtually the same - the only two minor differences with mine is that my lights are cast as an ellipse rather than a circle (due to the perspective) and that I need to depth buffer my light map (which is a relatively easy task) - so looking at your method of using areas\shapes was definitely a huge help.

I'm on a bit of a break right now - but I'll get back to it in a couple of hours and I'll be sure to report progress.

Thanks again.

JevaEngine, Latest Playthrough (This demo is networked with a centralized server model)

http://www.youtube.com/watch?v=rWA8bajpVXg
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline StumpyStrust
« Reply #33 - Posted 2013-08-09 09:53:51 »

Mine will work as an ellipse as well or should. Heck, you really could use any source image as long as you change the center point used to find the angles.

Scratch that clearing method for bufferedimages from before. It will work but is not necessary.

1  
lightg2d.drawImage(originalLight,0,0,light.getWidth(),light.getHeight(),null);


Well work as well. About the same speed. That is the slow down that I have found because I am drawing with a bufferedimage. So to make this really fast I need a way to restore the image to its original state without drawing to a bufferedimage. VolatileImages work but these lines KILLS their performance gain.

1  
2  
lightg2d.setColor(BLANK);
lightg2d.fill(p);


Put simply, VI do not like changing transparency or more so, the polygon fill method as that probably is done in software. So, restore it with a VI, draw the shadow shape to a BI then draw that BI to the VI. hmmm....may get a nice performance boost if this works. Just talking about it some more and goofing around has given me more ideas on how to get this faster and better. By the end I could make a tutorial on fast dynamic java2D lighting. But then that would just be leading people astray because you really should not be using java2D. Tongue

How are you drawing the actually lightmap to the scene? The way I do it might be different and I would love to know. Anyways, bedtime for me. Good luck.

Offline Jeremy
« Reply #34 - Posted 2013-08-10 12:32:25 »

Mine will work as an ellipse as well or should. Heck, you really could use any source image as long as you change the center point used to find the angles.

Scratch that clearing method for bufferedimages from before. It will work but is not necessary.

1  
lightg2d.drawImage(originalLight,0,0,light.getWidth(),light.getHeight(),null);


Well work as well. About the same speed. That is the slow down that I have found because I am drawing with a bufferedimage. So to make this really fast I need a way to restore the image to its original state without drawing to a bufferedimage. VolatileImages work but these lines KILLS their performance gain.

1  
2  
lightg2d.setColor(BLANK);
lightg2d.fill(p);


Put simply, VI do not like changing transparency or more so, the polygon fill method as that probably is done in software. So, restore it with a VI, draw the shadow shape to a BI then draw that BI to the VI. hmmm....may get a nice performance boost if this works. Just talking about it some more and goofing around has given me more ideas on how to get this faster and better. By the end I could make a tutorial on fast dynamic java2D lighting. But then that would just be leading people astray because you really should not be using java2D. Tongue

How are you drawing the actually lightmap to the scene? The way I do it might be different and I would love to know. Anyways, bedtime for me. Good luck.

Thanks for the interest.

My dynamic lighting system doesn't load light texture from a file - instead I achieve hardware accelerated (at least in my environment I believe it is hardware accelerated) approach of the shading by using a variation of RadialGradientPaint mixed with a GadientPaint to achieve the lighting effects. I would avoid using a light texture because it seems like it would quite expensive to draw that over the light map (plus for a simple diffuse\directional light you don't need anything fancy.)

I have profiled the code, and I can post some details on that:
(You need to follow the image URL in your browser - the forum seems to be clipping the CPU time column)


You'll notice that the Lightmap.enqueueRender is an incredibly cheap operation - I spend virtually zero CPU time producing the light map - this is likely because the operations I am using are hardware accelerated with GradientPaint and RadialGradientPain. However, I do spend a decent amount of CPU time calculating the areas in which to render (Area.Subtract\Area.Add) however I believe there are a lot of places I can minimize the calling to those routines so we'll see how it works out. Either way I don't believe this is too big of a bottle-neck.

The bottle-neck seems to be on the Graphics2D.setClip routine which seems to be much more expensive than I thought it would be (I did some research and it is just as slow with primitive shapes.) It is literally taking about 2/3rds the cpu time I spend on drawing the depth sorted strip of the produced lightmap just to set the clip - which is _way_ to slow IMO. I'll need to find a more effective way of depth-sorting the strip. Furhter I

The Area object seems to not behave properly (I think I've found a bug in the java.awt library) if you pass it a specific set of data (I haven't logged what that data might be) - but essentially it causes the Area.intersect\Area.subtract routine to hang the invoking thread endlessly - no exception etc it just gets stuck in an internal loop. The bug is easily reproducible by positioning the light at a very specific location in my world and than translating it diagonally (world coordinates) - so until I get a response form where I report the bug I'll just avoid that location on the map lol.

On the bright side, I nearly have the lighting effects I am looking for, here is how it looks:


For now though, everything seems to be performing alright. I think I am going to finish polishing up my DiffuseLight, resolve that hanging issue with Area.Subtract\Intersect (probably issue a bug report - since even if I am giving it awkward data by mistake (and I will trace over my algorithm a bit to assure I'm not) it should have done the proper checking and thrown an exception - it's never really acceptable for it to get stuck in an endless iteration and lock up the main thread) Polish up my lighting a little bit and leave this. If it starts causing performance issues I'll do some more profiling and try and optimize the lighting a bit.

I don't think writing a tutorial on Java2D lighting is a bad idea - since most people doing are (like me) trying to avoid the need to elevated permissions on dependencies that would come along with needing to upload a shader to the GPU. I think it offers an interesting new method for lighting in a 2D rendering environment that can't optimally perform per-pixel operations in 2D games and I don't think that it is well documented.

JevaEngine, Latest Playthrough (This demo is networked with a centralized server model)

http://www.youtube.com/watch?v=rWA8bajpVXg
Offline Jeremy
« Reply #35 - Posted 2013-08-10 21:07:31 »

I have drawn the conclusion that depth sorting my light-map is pointless - and unneeded.

Depth buffering my light map meant that the applied effect was much like the light being situated very close to the ground - i.e, it wouldn't cast lighting effects sufficient over entities.

That said, having an excuse to throw away depth buffering for light-map reduces about 90% of my dynamic lighting systems overhead. Though I still need to implement JTS since java's geometry API performs very poorly in a few rare instances which causes the entire calling thread to lock up until it runs out of memory (I submitted an error report to Oracle on this) - you can take a look at this bug that I found here: http://www.java-gaming.org/topics/does-this-snippet-of-code-hang-your-thread/30393/msg/280400/view/topicseen.html#msg280400 I would link to the bug report but I believe it is remained private until it is confirmed.

Current state of the lighting system:




All that's left to do now is refactor over to JTS and create some cool torch effect with the particle engine & the new dynamic lighting system.

I'm pretty excited to start implementing a weather system and day\night cycle with the new lighting and particle subsystems.

JevaEngine, Latest Playthrough (This demo is networked with a centralized server model)

http://www.youtube.com/watch?v=rWA8bajpVXg
Offline StumpyStrust
« Reply #36 - Posted 2013-08-11 01:35:02 »

Looking good. Using an image as the source light is fine. Java is very fast at drawing images. Most g.draw calls are fast. The cool thing with using an image is that you could create all sorts of cool lighting. Also, how dark can you make the game? The trick I use to make it so only the alpha of the main lightmap is used is through alpha composites.

The idea is to take the alpha from the light/main lightmap and make it the destination alpha. That way you can have some much more realistic lighting. Here is the code.

1  
2  
3  
4  
5  
6  
      Graphics2D g2d = (Graphics2D) g.create();
      g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, colorBlend));
      g2d.drawImage(lightmap,offX,offY,scrWidth,scrHeight,null);
      g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_ATOP, 1));
      g2d.drawImage(lightmap,offX,offY,scrWidth,scrHeight,null);
      g2d.dispose();


The first draw will draw the amount of color from the lights we are blending. The second will draw the amount of alpha we will blend which in this case it all. To get ambient lighting, just draw a rectangle of some color (ambient color) over the lightmap first with a low alpha or max alpha. Alpha composites are hardware accelerated. They are rather cheap.

How many lights can you have before a slow down? How big can the lights be? In my game Retro I have a light attached to each projectile and that does not seem to slow things down much.

I have found never to use setClip. It has always been slow for me. For the day night or you just drawing a colored, translucent rectangle?

Offline Jeremy
« Reply #37 - Posted 2013-08-12 18:30:24 »

Looking good. Using an image as the source light is fine. Java is very fast at drawing images. Most g.draw calls are fast. The cool thing with using an image is that you could create all sorts of cool lighting. Also, how dark can you make the game? The trick I use to make it so only the alpha of the main lightmap is used is through alpha composites.

Ofcourse! I don't know what I was thinking when I said it would be slower (I mean, it probably is - but by an insignificant bit. I wonder though how that scales) I was tired and had just finished profiling and saw that my greatest bottle-neck was drawing the light-map - but that was due to its size I think. Ugh - lighting without pixel-shaders has been complete hell. Anyway, that said, the lighting implementation really leaves the specifics of how the light is rendered into the actual light class implementing the ILight interface. So I am setting up the system to go either way on that (using Textures or Shapes.)

I'm not sure how you've set it up, but I'm not sure how I would do things like set the light to a specific colour using a texture - i.e I don't want to have a texture for every light colour - I am sure there is a way around that (well, I know you can use a software filter and cache - but that's not practical for a lot of situations like a colour transitioning light etc.) There might be a way to do it through a AlphaComposite? I'm not sure. So I set it up to go either way.

Quote
The idea is to take the alpha from the light/main lightmap and make it the destination alpha. That way you can have some much more realistic lighting. Here is the code.
1  
2  
3  
4  
5  
6  
      Graphics2D g2d = (Graphics2D) g.create();
      g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, colorBlend));
      g2d.drawImage(lightmap,offX,offY,scrWidth,scrHeight,null);
      g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_ATOP, 1));
      g2d.drawImage(lightmap,offX,offY,scrWidth,scrHeight,null);
      g2d.dispose();


The first draw will draw the amount of color from the lights we are blending. The second will draw the amount of alpha we will blend which in this case it all. To get ambient lighting, just draw a rectangle of some color (ambient color) over the lightmap first with a low alpha or max alpha. Alpha composites are hardware accelerated. They are rather cheap.

Yep, thats what I am doing (or something like that)

Quote
How many lights can you have before a slow down? How big can the lights be? In my game Retro I have a light attached to each projectile and that does not seem to slow things down much.

I have found never to use setClip. It has always been slow for me. For the day night or you just drawing a colored, translucent rectangle?

I can't answer those questions now (well, give me an hour or two, I need to do some profiling & setup a few tests), but what I can say is that the most expensive operation is drawing the light-map to the screen - i.e I don't believe the number of lights has a linear relationship with the initial performance impact so I have to do some profiling there. I'm going to do a light-attached-to-particles test and see how well it performs.

The way it works is:
 - Clear the lightmap with a transparent colour
 - Draw over my lights with an alpha composite shape
 - Draw over my ambient light with a rectangle using source-out alpha-composite
 - Then draw over the lights again with default composite (add the colour)

Hmm, well I've just finished fixing the issue regarding overlapping lights, here is how it looks now (I chose a black background so I can be sure colours aren't being washed out):


here is how it looks with some fine-tuning:


Ugh, all this work on lighting - I forget that the lighting subsystem is such a small component of the engine. Well, I learned a lot about lighting without pixel-shaders which is great - but virtually nothing from my knowledge of prior lighting had any relevance. I really think it would be worth writing a tutorial on this - lighting is complete hell without a shader.

JevaEngine, Latest Playthrough (This demo is networked with a centralized server model)

http://www.youtube.com/watch?v=rWA8bajpVXg
Offline Jeremy
« Reply #38 - Posted 2013-08-12 21:29:32 »

Alright, I have 75 lights on there - a radius of 3 tiles. There is some performance difference between the 2 lights and the 75 lights but not much - I don't notice any frame drops (albeit I haven't implemented an FPS counter) but I'd say its running at at least 40 - not being much of a gamer I don't have a keen eye for it really. It ends up consuming about 48% of CPU time (recorded via task manager - which is a jump from ~10%-15%) All the performance impact comes from filling the shape.

Here is how it looks (really stupid because the lights have their colour randomized)



You'll notice they aren't casting shadows - I got rid of that because when you actually apply it - it looks cool for a while but the shadows don't really make sense. With an isometric world where entities are viewed as 3D - Some objects have completely different normal data and it really doesn't make any sense until I have time to encode that normal data & height into the objects - so until then I have commented out the shadow-casting code. I am not sure how much performance impact it would have - but it's really only a few geometry manipulations so I can't imagine much (especially considering how cheap they've been so far)


JevaEngine, Latest Playthrough (This demo is networked with a centralized server model)

http://www.youtube.com/watch?v=rWA8bajpVXg
Offline StumpyStrust
« Reply #39 - Posted 2013-08-13 05:40:32 »

If you do not want shadows then you can make the system scary fast. At least mine. It will be as fast as java can draw images or in your case shapes. Take your lightmap, clear it, draw all lights to it like you would sprites, and then draw the lightmap to the scene with the method I posted. Should be very very fast. The shadows are what will eat performance. Just tested my current code with turning off shadows and could do a 1500 lights at 256x256 pixels. You can even bake in shadows for dungeons and still get really cool lighting with only a memory hit.

My method is memory intensive but it still is very manageable. Each light has its own BI for the original light and the lights that we will be drawing over. Orig and thrash for thort. The shadow map is drawn onto the (thrash) BI each frame and then that BI is clear by drawing the Orig BI over it. Giving each image their own BI to manage the shadow mapping allows their shadows to not conflict with other lights. Giving them each their own Orig BI also allows them to have their own color. I also re-size each Orig texture to the lights size as to keep the memory down and give larger lights better quality. The down side is having each light have two BI. You can drop the Orig BI and just make them keep a reference to the Orig and clear with it. One thing which I have added is reducing the lights BI size. Sort of like reducing the scale of the lightmap. Reduction the size by 2, (100/scale = 50) doubles performance as the bottle neck is clearing each lights BI and you can barely tell a difference.

The advantage of using your gradient pain is that you can pick what ever color you want on the fly. Very nice and not something I thought of. I would mix my current method with yours by dropping the extra BI and instead just draw the Gradient to the thrash BI for clearing. Something I may look into. I am not sure if drawing the gradient is slow or faster then drawing an Image. There may be some software stuff going on to do it but maybe not.

If you post the code you use to draw you lightmap I can probably help as I have spent many-an-hour tinkering with java2D and finding out what is fast and not.

PS: I have no idea how to do lighting with shaders. You can help me when I venture into that.  Grin

Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline Jeremy
« Reply #40 - Posted 2013-08-13 07:15:27 »

If you do not want shadows then you can make the system scary fast. At least mine. It will be as fast as java can draw images or in your case shapes. Take your lightmap, clear it, draw all lights to it like you would sprites, and then draw the lightmap to the scene with the method I posted. Should be very very fast. The shadows are what will eat performance. Just tested my current code with turning off shadows and could do a 1500 lights at 256x256 pixels. You can even bake in shadows for dungeons and still get really cool lighting with only a memory hit.
Hmm, but casting a shadow with an Area object is a lot less involved than casting a shadow with a BufferedImage - all I have to do is subtract from the Area and reassign that area to a different render routine - which is why I say it might not really help with performance at all (in my case) by dropping shadows. Idk though, I'd actually need to test it.

Quote
The advantage of using your gradient pain is that you can pick what ever color you want on the fly. Very nice and not something I thought of. I would mix my current method with yours by dropping the extra BI and instead just draw the Gradient to the thrash BI for clearing. Something I may look into. I am not sure if drawing the gradient is slow or faster then drawing an Image. There may be some software stuff going on to do it but maybe not.
I'm pretty sure the RadialGradientPaint is hardware accelerated - i.e in the backend it is probably using a pixel shader of some sort. I know this because I tried creating my own radial gradient paint implementing Paint - and is really simple in nature (something like RadialGradientPaint) and I lost the hardware acceleration and it got really slow. Plus a pixel-shader for radial-gradient would be really easy to implement I am sure the JRE accelerates it.

Quote
If you post the code you use to draw you lightmap I can probably help as I have spent many-an-hour tinkering with java2D and finding out what is fast and not.
I'll take a look at my code and try to extract the lighting sub-system - shouldn't be too hard to do. I'll post it here when I do.

JevaEngine, Latest Playthrough (This demo is networked with a centralized server model)

http://www.youtube.com/watch?v=rWA8bajpVXg
Offline Jeremy
« Reply #41 - Posted 2013-08-13 23:49:26 »

Just added a command console that gives the user access to the scripting of the engine in real-time. Scripting has been a feature for a long while but now there is a command console to execute on-the-fly scripts as well. It's pretty cool and it only took about 45 minutes to implement (since the infrastructure has always been there). I show it off along with the new world time system (I am going to add weather, it just takes a bit more work to setup the particle emitter etc)):

Camstasia (the program I use to record the game to youtube) lags the bloody hell out of this computer, but IMO it performed alright... You have to watch this in 720p - otherwise you can't read the scripts...

<a href="http://www.youtube.com/v/pmf7vrsz0TY?version=3&amp;hl=en_US&amp;start=" target="_blank">http://www.youtube.com/v/pmf7vrsz0TY?version=3&amp;hl=en_US&amp;start=</a>


The lighting code is essentially this there is a SceneLighting object that manages the different lights on the scene - still though I haven't found time to unmangle it from the engine because I was busy fixing a few bugs that came to my attention thanks to the addition of the console.

JevaEngine, Latest Playthrough (This demo is networked with a centralized server model)

http://www.youtube.com/watch?v=rWA8bajpVXg
Offline Jeremy
« Reply #42 - Posted 2013-08-17 06:09:57 »

You can play run the applet here:
http://gamejolt.com/games/rpg/jevaengine-project-d0/16225/

It's a pre-release (I.e, this is hidden content) so don't rate it etc I don't really want a lot of people catching wind of it's accessibility.

I mainly do this because its at a stage where it is worth playing with. Here is the guide for the command line system and some notes:

- Only map/almostatcontrol.jmp has a script which relates world time with an ambient lighting. So if you want to see the effects of world time, you must be on that map to do it.

- Precaching isn't fully implemented since it hasn't been an issue until now - essentially this means that you might get temporary pauses in the game as it prepares and caches resources that haven't yet been accessed but are just then being accessed via a script etc.

- Any textarea can be scrolled through with the middle mouse scroll _AFTER_ selecting the textarea


Command Terminal Menu:

- Visibility can be toggled by using the ~ key - except when an editable text-area is selected

- The command line uses JavaScript, there is a text area adjacent to the red pointer, select it and write your scripts. After you entered your script, there are two commands you can use:

- This doesn't disclose all available commands because it would take a while to write that up...

CTRL + E (probably the only one you need) executes the script you've entered. If you're script declares functions or variables, they will be added to the global scope so they can be used later.

CTRL + L - Loads the script so that any scripts there on out are executed in the context of what you've just typed in.

- The green text of the text-area will spit out notes when executing your script - and if it evaluates to something, it will print out the evaluation. If there is an error in your script, it will display a cryptic error message (the output textarea does not scroll automatically, you must select it and scroll via middle mouse wheel)

There are three global variables accessible by the command like:
game
    - Used to acquire world script bridge, to load a world and to get the player entity
core
    - Used for nothing other than debugging purposes.
me
    - Used to access commands that are relevant to the console (or executing object) kind of like the 'this' keyword. In the command console you can use it to echo and clear

Functions exposed by 'me' in command console
clear(); - clears the console's output textarea
echo(message); - Echos message to output console.

i.e:
me.clear();
me.echo('hello');


Functions exposed by 'game' in command console
getPlayer() returns an instance of player's script bridge. Refer to 'Commands Exposed by Player ScriptBridge'
getWorld() returns an instnace of world's script bridge. Refer to 'Commands Exposed by World ScriptBridge'
loadWorld(url) loads the world located at the given URL.

There are three maps that exist in the game's File-System:
map/almostatcontrol.jmp
map/firstencounter.jmp
map/enterance.jmp

Functions exposed by Player ScriptBridge in command console & All NPCs

Just For Player:
getQuest(questName) returns an instance of the given quest tailored to that player. Provides ability to check and set quest states.

For All NPCs & Player:

getLocation() returns a Vector of integer components X, Y describing the player's location.

Example:
1  
me.echo('Players X location is: ' + game.getPlayer().getLocation().x);


setLocation(x, y) Sets the world location to (x, y)

Example:
1  
2  
3  
4  
5  
6  
7  
8  
game.getPlayer().setLocation(0, 0);

var eric = game.getWorld().getEntity('eric');

if(eric != null)
    game.getPlayer().setLocation(eric.getLocation().x, eric.getLocation().y + 1);
else
    me.echo('The NPC Eric could not be found!');


moveTo(x, y, tolerance) - Entity en-queues asynchronous task to walk to location (x, y) with an acceptable 'tolerance' i.e radius they are allowed to be in before declaring failure to accomplish task.

Example:

1  
game.getPlayer().moveTo(0, 0, 1);


attack(target) - en-queues asynchronous task to attack the given target, or fails if equip weapon allows for such range

Example:

1  
2  
3  
4  
5  
var player = game.getPlayer();
var spider = game.getWorld().getEntity('spider');
spider.moveTo(player.getLocation().x, player.getLocation().y, 1);
spider.attack(player);
player.attack(spider);


leave() - En-queues asynchronous task to have the character leave the scene

Example:
1  
2  
3  
4  
5  
var eric = game.getWorld().getEntity('eric');
//Tell eric, first move to the coordinate 0,0
eric.moveTo(0,0,1);
//Then once you arrive (or if the move tasks is otherwise 'completed') leave the world
eric.leave();


isConflictingAllegiance(target) - Returns true if is in conflicting allegiance with target (I.E target is enemy)
Example:
1  
2  
3  
4  
5  
6  
var spider = game.getWorld().getEntity('spider');
if(spider.isConflictingAllegiance(game.getPlayer())
{
    spider.moveTo(game.getPlayer().getLocation().x, game.getPlayer().getLocation().y, 1);
    spider.attack(game.getPlayer());
}


getHealth() Returns an integer value which is the current health of the character, all characters have a health between 0 and 100 as of now - though that can change depending on the character's configuration file.

setHealth(health) Sets the health of the character.

example:
1  
2  
if(game.getPlayer().getHealth() < 20);
    game.getPlayer().setHealth(100);


addItem(itemDescriptor, quantity) - Adds an item to character's inventory. Returns the number of that item added which can differ from quantity if the inventory is full before quantity is reached.

hasItem(itemDescriptor, quantity) - returns true of the player is in possession of quantity number of itemDescriptor

removeItem(itemDescriptor, quantity) - Behaves exactly has addItem only removes item. Returned value can differ from quantity if less than quantity of itemDescriptor is contained in inventory

Example
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
var player = game.getPlayer();
var eric = game.getWorld().getEntity('eric');

if(player.hasItem('item/healthpack.jitm', 1))
{
    me.echo('Fair trade!');
    if(eric.hasItem('item/rifle.jitm', 1)
    {
        player.removeItem('item/healthpack.jitm', 1);
        player.addItem('item/rifle.jitm', 1);
        eric.addItem('item/healthpack.jitm');
    }else
        me.echo('Looks like eric doesnt have anything left to trade!');
}


wonder(radius) En-queues asynchronous wonder task to randomly move around.

distance(target) Returns the distance (in tiles) from the given target

look() Has the character look around it's given area (using is sight properties described in its configuration file). Any found objects cause the object to invoke a onFound() routine - you can't really use this via the command console only via entity scripts.

loadState(path) Restores the state of the entity as described by the state located at the given path. The current release uses memory states so this is sort of like a memory virtual filesystem.

saveState(path) Saves the state of the entity to the given path.


Functions exposed by World ScriptBridge in command console

setAmbientLight(r, g, b, a) - Configure's world's ambient light.

setHour(hour) - Sets world's time
setMinute(minute)
setSecond(second)
setHour(hour) - 24 hour time

getHour, minute etc...

setTimeMultiplier(multiplier)

Example
game.getWorld().setTimeMultiplier(10000);

createEntity(name, className, npcFile)
 - className has to be jevarpg.RpgCharacter
 - npcFile can be:
    npcs/innocentspider.jnpc
    npcs/player.jnpc
    npcs/spider_almostatcontrol.jnpc

getEntity(entityName)

JevaEngine, Latest Playthrough (This demo is networked with a centralized server model)

http://www.youtube.com/watch?v=rWA8bajpVXg
Offline StumpyStrust
« Reply #43 - Posted 2013-08-17 18:49:44 »

That is a long read. Will try it out later. Keep it up. Check out my lighting post.  Roll Eyes


Offline Jeremy
« Reply #44 - Posted 2013-08-18 16:15:48 »

That is a long read. Will try it out later. Keep it up. Check out my lighting post.  Roll Eyes


Lol, yeah sorry for the long read. I'll definitely take a look at your lighting code.

Today I'm going to start planning and hopefully get started on working on a Server Module Extention to the core engine, likewise write a Client module for the Rpg extention. I think adding a bit of networking into the engine would be really cool.

Anyway for those curious how I am partitioning things:

This thread is essentially my development log, when I develop a major tech demo etc I will post in the releases, but this thread is primarily just for news etc regarding the engine.

JevaEngine, Latest Playthrough (This demo is networked with a centralized server model)

http://www.youtube.com/watch?v=rWA8bajpVXg
Offline Jeremy
« Reply #45 - Posted 2013-08-24 10:10:04 »






Network Subsystem is nearly completed, I believe I will have some basic networking to show off soon.

It looks like server-side scripted entities are actually going to be quite easy to implement, and all features in the game (minus dialog which will require some tweaking since dialogs pause the server-side world state) will seamlessly be handled on the server-side and fed to the clients.

The network-subsystem took a bit of work, but with the current implementation I am hoping for minimum bandwidth consumption.

The networking is going to be implemented outside of the engine's core. This makes most sense to me since the actual model used for networking can be most optimally chosen outside of the core of the engine. I.e, if movement is strictly tile-based (as it is for the RPG Extension) then movement can be abstracted to a very low-bandwidth per-tile level. If it is more complex than that (i.e, not strictly tile based) then obviously it may require some more bandwidth intensive operations etc to portray velocity, acceleration location etc and a more complex client-side prediction.

The current networking model communicates to clients using snapshots broadcasted at a fixed frequency. Using snapshots allows data network data to be queued for a fixed time period and then dispatched to the client(s) (and vise-versa) This is an avantage as compression ratios are best with larger chunks of data and it avoids redudant data being dispatched over at a very high frequency (wasting bandwidth.)

Anyway, updates to follow.

JevaEngine, Latest Playthrough (This demo is networked with a centralized server model)

http://www.youtube.com/watch?v=rWA8bajpVXg
Offline Jeremy
« Reply #46 - Posted 2013-08-28 16:10:07 »

<a href="http://www.youtube.com/v/4aJhpaTUDro?version=3&amp;hl=en_US&amp;start=" target="_blank">http://www.youtube.com/v/4aJhpaTUDro?version=3&amp;hl=en_US&amp;start=</a>

Networking is fully operational:

Here is a demo of the game server and client. I am still polishing up a few things then I intend on creating a simple game out of it to do some server load testing. The server is on the left, the client on the right. This demo illustrates only one client - though the server is capable of managing many clients.

The Screen Recorder I use eats up CPU Cycles like a monster so I can't record with a decent frame-rate.

Load Times:

The load times for the spider sprite in particular is quite slow. This is due to the Spider's texture file being absurdly large and inefficeint. You will see the client Freeze and the server Freeze as the spider is loaded into the world for about 5s, usually the assets would go through a precache process to avoid the load times while in-game, but precaching is not an immdediate concearn and has thus been thrown on-to the 'do later' task-list.

The load times for worlds are slow, this is because the worlds are currently stored in a plain-text (sort of like XML but with less overhead) format. This means that with large worlds and many layers it takes a large amount of CPU time to parse them. Optimally (and in later stages of development) these worlds will be stored in a binary format. Once the map files have actually been parsed, my profiler informs me that the actual LOADING of the map using the intermediate data structures fromed via the parsing process is very quick.

Security from client-side tampering:

In theory (if I'm not missing any bugs here) it is impossible for the clients to alter the state explicitly of any server entity without first asking the server so the game should be safe from game tampering. Additionally I have employed data encryption and code obfuscation\optimizations to ecourage would-be hackers to give up. The worst someone could do is to throw the client out of sync with the server, but it would have no effect on other clients or the server.

Please post any feedback, it is appreciated.

What I am doing now:

- I will be resolving a very obscure and difficult to reproduce bug in the server
- Polishing & finalizing the server & networking code.
- Creating an actual multiplayer game out of the recent advancements in the engine to do some load-testing on the server.

I'm not sure where I am going to deploy the server... I can't deploy it on this piece of crap because it barely does two things at once and I need access to my IDE. I have another computer I could deploy it on but it isn't 'mine' per se.

JevaEngine, Latest Playthrough (This demo is networked with a centralized server model)

http://www.youtube.com/watch?v=rWA8bajpVXg
Offline Jeremy
« Reply #47 - Posted 2013-08-31 04:45:34 »

JevaEngine now has a very solid networking implementation, that includes entities such as AreaTriggers and NPC Characters and various other basic entities (doors etc...)

The way it works is something like:
 - Every networked entity has a script associated with it. There is a client script (stored and executed on the client) and a server script (stored and executed on the server) Both work exactly the same as entity scripts did before (i.e, with single player.) Server scripts work on the entity for i.e movements etc. Client scripts do the same for strictly client-side tasks (like playing the 'die sound' when a character dies)

I am going to be releasing a basic game to demo this, the game works something like this (and will be published on game jolt) I will deploy the server on my computer which should be good for ~45-50 players with 175 complex entities (that is, entities with very active AI constantly mutating states that need to be shared accross clients):

1. Players spawn as soldiers on the map - except for the two top-ranking players of the previous map. Soliders play a sort of Rpg Perspective - they can loot for better weapons and level up per round (i.e, after the round is reset - their character is reset) If I can, for the first couple of releases, I want to avoid having to keep track of players beyond individual rounds etc because that requires a backend database implementation that detracts for the purpose of the first couple of releases requires more serious security considerations.

2. The first of the two top-ranking players will be the master of the World's spider hive. I.e, they play a sort of RTS perspective of the game and control the spider NPC army of the multiplayer game.

3. The second of the two top-ranking players will be the same except for Zombies

4. Spiders and Zombies work together to kill the soldiers and vise-versa. Trophies are earned for being a top-ranked player for a given period of time, killing x spiders etc.


JevaEngine, Latest Playthrough (This demo is networked with a centralized server model)

http://www.youtube.com/watch?v=rWA8bajpVXg
Offline Jeremy
« Reply #48 - Posted 2013-09-05 10:19:59 »

Latest Networking Demonstration:
http://www.youtube.com/watch?v=v-52aKJ4Smo
<a href="http://www.youtube.com/v/v-52aKJ4Smo?version=3&amp;hl=en_US&amp;start=" target="_blank">http://www.youtube.com/v/v-52aKJ4Smo?version=3&amp;hl=en_US&amp;start=</a>

JevaEngine, Latest Playthrough (This demo is networked with a centralized server model)

http://www.youtube.com/watch?v=rWA8bajpVXg
Pages: 1 [2]
  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 (28 views)
2014-04-15 18:08:23

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

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

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

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

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

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

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

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

CJLetsGame (195 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!