If I try to allocate any memory during onDraw in my View-derived class in my Android app, Eclipse/lint gives me warnings that I shouldn't be allocating memory during the execution of onDraw. so I'm trying to think of the best way to append a rotated rectangle to a path that may get used to define clipping bounds. I'm also going to want to figure out how to add a rotated ellipse to such a path.
I have considered using Matrix.mapPoints with the 4 corners of the rectangle (using a pre-allocated matrix), but I don't currently have a pre-allocated array of floats to use with that, and I'm not sure I want to do that if there's another way. Should I use Math.atan2 to get polar coordinates, offset the result, and then use sin and cos to calculate new coordinates, or is that going to have a lot more overhead than the matrix multiplication?
Are there other ways of adding rotated rectangles and ellipses to clipping boundaries that I should consider?
Edit: I'm also not clear if calling other functions that have local variables would be considered memory allocation. If I create a function like this:
private void drawOperation(Operation op, Canvas canvas) {
float coords[] = {0,0,0,0,0,0,0,0};
....
}
Does that array get created on the heap or the stack? Does it still constitute something that should be avoided during onDraw?
I am considering code like this, where mMatrix, mRotationPath, mPoint and mPath are pre-allocated objects:
mMatrix.setRotate(angle, mPoint.x, mPoint.y);
mRotationPath.rewind();
mRotationPath.addRect(mRect, Path.Direction.CW);
mPath.addPath(mRotationPath, mMatrix);
Related
Hello I am an inexperienced programmer and this is my first question on Stack Overflow!
I am attempting to implement 'fog of war' in my Java game. This means most of my map begins off black and then as one of my characters moves around parts of the map will be revealed. I have searched around including here and found a few suggestions and tried tweaking them myself. Each of my approaches works, however I run into significant runtime issues with each. For comparison, before any of my fog of war attempts I was getting 250-300 FPS.
Here is my basic approach:
Render my background and all objects on my JPanel
Create a black BufferedImage (fogofwarBI)
Work out which areas of my map need to be visible
Set the relevant pixels on my fogofwarBI to be fully transparent
Render my fogofwarBI, thus covering parts of the screen with black and in transparent sections allowing the background and objects to be seen.
For initialising the buffered image I have done the following in my FogOfWar() class:
private BufferedImage blackBI = loader.loadImage("/map_black_2160x1620.png");
private BufferedImage fogofwarBI = new BufferedImage(blackBI.getWidth(), blackBI.getHeight(), BufferedImage.TYPE_INT_ARGB);
public FogOfWar() {
fogofwarBI.getGraphics().drawImage(blackBI,0,0,null);
}
In each of my attempts I start the character in a middle of 'visible' terrain, ie. in a section of my map which has no fog (where my fogofwarBI will have fully transparent pixels).
Attempt 1: setRGB
First I find the 'new' coordinates in my character's field of vision if it has moved. ie. not every pixel within the character's range of sight, but just the pixels at the edge of his range of vision in the direction he is moving. This is done with a for loop, and will go through up to 400 or so pixels.
I feed each of these x and y coordinates into my FogOfWar class.
I check if these x,y coordinates are already visible (in which case I don't bother doing anything to them to save time). I do this check by maintaining a Set of Lists. Where each List contains two elements: an x and y value. And the Set is a unique set of the coordinate Lists. The Set begins empty, and I will add x,y coordinates to represent transparent pixels. I use the Set to keep the collection unique and because I understand the List.contains function is a fast way of doing this check. And I store the coordinates in a List to avoid mixing up x and y.
If a given x,y position on my fogofwarBI is not currently visible I add set the RBG to be transparent using .setRGB, and add it to my transparentPoints Set so that coordinate will not be edited again in future.
Set<List<Integer>> transparentPoints = new HashSet<List<Integer>>();
public void editFog(int x, int y) {
if (transparentPoints.contains(Arrays.asList(x,y)) == false){
fogofwarBI.setRGB(x,y,0); // 0 is transparent in ARGB
transparentPoints.add(Arrays.asList(x,y));
}
}
I then render it using
public void render(Graphics g, Camera camera) {
g.drawImage(fogofwarBI, 0, 0, Game.v_WIDTH, Game.v_HEIGHT,
camera.getX()-Game.v_WIDTH/2, camera.getY()-Game.v_HEIGHT/2,
camera.getX()+Game.v_WIDTH/2, camera.getY()+Game.v_HEIGHT/2, null);
}
Where I am basically applying the correct part of my fogofwarBI to my JPanel (800*600) based on where my game camera is.
Results:
Works correctly.
FPS of 20-30 when moving through fog, otherwise normal (250-300).
This method is slow due to the .setRGB function, being run up to 400 times each time my game 'ticks'.
Attempt 2: Raster
In this attempt I create a raster of my fogofwarBI to play with the pixels directly in an array format.
private BufferedImage blackBI = loader.loadImage("/map_black_2160x1620.png");
private BufferedImage fogofwarBI = new BufferedImage(blackBI.getWidth(), blackBI.getHeight(), BufferedImage.TYPE_INT_ARGB);
WritableRaster raster = fogofwarBI.getRaster();
DataBufferInt dataBuffer = (DataBufferInt)raster.getDataBuffer();
int[] pixels = dataBuffer.getData();
public FogOfWar() {
fogofwarBI.getGraphics().drawImage(blackBI,0,0,null);
}
My editFog method then looks like this:
public void editFog(int x, int y) {
if (transparentPoints.contains(Arrays.asList(x,y)) == false){
pixels[(x)+((y)*Game.m_WIDTH)] = 0; // 0 is transparent in ARGB
transparentPoints.add(Arrays.asList(x,y));
}
}
My understanding is that the raster is in (constant?) communication with the pixels array, and so I render the BI in the same way as in attempt 1.
Results:
Works correctly.
A constant FPS of around 15.
I believe it is constantly this slow (regardless of whether my character is moving through fog or not) because whilst manipulating the pixels array is quick, the raster is constantly working.
Attempt 3: Smaller Raster
This is a variation on attempt 2.
I read somewhere that constantly resizing a BufferedImage using the 10 input version of .drawImage is slow. I also thought that having a raster for a 2160*1620 BufferedImage might be slow.
Therefore I tried having my 'fog layer' only equal to the size of my view (800*600), and updating every pixel using a for loop, based on whether the current pixel should be black or visible from my standard transparentPoints Set and based on my camera position.
So now my editFog Class just updates the Set of invisible pixels and my render class looks like this:
public void render(Graphics g, Camera camera) {
int xOffset = camera.getX() - Game.v_WIDTH/2;
int yOffset = camera.getY() - Game.v_HEIGHT/2;
for (int i = 0; i<Game.v_WIDTH; i++) {
for (int j = 0; j<Game.v_HEIGHT; j++) {
if ( transparentPoints.contains(Arrays.asList(i+xOffset,j+yOffset)) ) {
pixels[i+j*Game.v_WIDTH] = 0;
} else {
pixels[i+j*Game.v_WIDTH] = myBlackARGB;
}
}
}
g.drawImage(fogofwarBI, 0, 0, null);
}
So I am no longer resizing my fogofwarBI on the fly, but I am updating every single pixel every time.
Result:
Works correctly.
FPS: Constantly 1 FPS - worst result yet!
I guess that any savings of not resizing my fogofwarBI and having it smaller are massively outweighed by updating 800*600 pixels in the raster rather than around 400.
I have run out of ideas and none of my internet searching is getting me any further in trying to do this in a better way. I think there must be a way to do fog of war effectively, but perhaps I am not yet familiar enough with Java or the available tools.
And pointers as to whether my current attempts could be improved or whether I should be trying something else altogether would be very much appreciated.
Thanks!
This is a good question. I am not familar with the awt/swing type rendering, so I can only try to explain a possible solution for the problem.
From a performance standpoint I think it is a better choice to chunk/raster the FOW in bigger sections of the map rather than using a pixelbased system. That will reduce the amount of checks per tick and updating it will also take less resources, as only a small portion of the window/map needs to update. The larger the grid, the less checks, but there is a visual penalty the bigger you go.
Leaving it like that would make the FOW look blocky/pixelated, but its not something you can't fix.
For the direct surrounding of a player, you can add a circle texture with the player at its center. You can than use blending (I believe the term in awt/swing is composite) to 'override' the alpha where the circle overlaps the FOW texture. This way the pixel-based updating is done by the renderingAPI which usually uses hardware enhanced methods to achieve these things. (for custom pixel-based rendering, something like 'shader scripts' are often used if supported by the rendering API)
This is enough if you only need temporary vission in the FOW (if you don't need to 'remember' the map), you don't even need a texture grid for the FOW than, but I suspect you do want to 'remember' the map. So in that case:
The blocky/pixelated look can be fixed like they do with grid-based terain. Basically add a small additional textures/shapes based on the surroundings to make things look nice. The link below provides good examples and a detailed explanation on how to do the 'terrain-transitions' as they are called.
https://www.gamedev.net/articles/programming/general-and-gameplay-programming/tilemap-based-game-techniques-handling-terrai-r934/
I hope this gives a better result. If you cannot get a better result, I would advise switching over to something like OpenGL for the render engine as it is meant for games, while the awt/swing API is primarely used for UI/application rendering.
I have a program that utilizes Path2D.Float to draw a vector object (a large fractal design). My code allows zooming and panning. I have an axis object that has methods to convert world coordinates (pairs of doubles) to display coordinates (pairs of floats) based on the current scaling settings (stored in the axis object).
Anyways, the vector graphic is large and detailed and contains many line segments in world coordinates. Each time the user zooms or pans, new Path2D objects are created and rendered to the screen.
Everything is perfectly smooth when zoomed out. The problem occurs when I zoom in to a certain depth. Apparently the Path2D lines get very long and this slows down their rendering (even though the vast majority is outside the viewing area!). It's not my conversion algorithms consuming resources. I profiled it and it's definitely the Java graphics drawing algorithm that's slowing down due to the size of the lines in comparison to the small clipping region.
I was hoping there was a way to get Java to deal with the clipping of large lines automatically. I do call setClip() from the graphics object before drawing. I don't see what's taking so much time. Is there something problematic/inefficient about the clipping algorithm when lines are long in comparison the clipping rectangle? I don't think I'm zooming so far that my conversion from world coordinates to display coordinates is causing overflow. I'll have to check for this. If that's the case I'll try using Path2D.double instead.
Anyways, any help appreciated. I'm sure I'll eventually figure this out but I hope someone that's encountered the same problem can give me a pointer so it doesn't take so long to figure out.
I've not used paths when zooming, but I have used them for drawing some very complex shapes with textures & gradients etc. Some issues I had were:
In my experience, I had to avoid creating new Path2D objects on a per frame basis because of performance issues, not just for their recreation execution, but because it caused a lot of garbage collection with generating & then dropping so many so quickly, which slowed things down. If your shape doesn't change, cache the generated path.
Avoid clipping with paths - where possible stick to rectangles - paths seem to give rough edges on curves and are more costly to use.
Even when clipping to smaller regions, simply asking to draw large regions could slow things down. Consider when the user zooms in to tessellate your shape, i.e. the shape is only as big as your viewport. Perhaps as you say maybe there is an issue with the clip function when dealing with large volume areas, so tessellation might help here.
I'm having a problem with my rendering cycle using libgdx, basically I need to fill an area with a square texture, and the last part of this area may be smaller or with a different shape than the texture, so it means that i need to render a quad of arbitrary form and slap the texture on it, cutting the parts I don't need.
I'm a bit lost on how to do this, so far I've seen that the PolygonRegion and PolygonSpriteBatch might do it for me, but I'm a bit wary of instancing a new heavy object I'll use only on one object.
Is there any alternative? Perhaps the Mesh class but i'd like to be certain.
I suggest using a Mesh to define exactly what region you want. Defining the vertex points and mapping those to the texture coordinates is a bit fiddly, but its good to know what's going on underneath some of the higher level APIs (like the *Batch bits). Additionally, the *Batch APIs are designed to share the weight of uploading a single texture across multiple objects, which sounds like it might not apply in this case. (On the other hand, even if the Batch objects are a bit "heavyweight", they may not actually be a problem in practice.)
Another approach to consider is to render the object as a square mesh, but to define your texture with transparent pixels for all the pixels outside the region. (I'm assuming the non-square shape is something you can know offline, and isn't dynamic.)
It isn't a big problem if you instantiate PolygonSpriteBatch for that purpose. The object mainly contains geometric data for buffered geometry. Of course you will need to care about correct rendering order calling flush or end when needed.
Mesh is another option but it can be a bit more work because you need to provide vertices and texture coordinates there manually.
From performance point of view rendering of one sprite is slightly faster with Mesh. I'm not sure if difference affects fps somehow in your case.
EDIT: forgot to mention, if you use SpriteBatch for rendering one object, don't use default constructor it reserves a lot of memory.
My application draws to the canvas in a continuous loop and on each loop re-evaluates the positions of the drawables and cycles them to animate. My question is which of the following 2 methods is superior and why? I'm a beginner so I have no idea how to benchmark methods and that kinda stuff, so if you can, or you already have, i'd appreciate the input.
The first method, (the one i'm using) is to assign the png resource a Handle as a Drawable. then every time I want to draw the object I call:
Drawable.setBounds(x,y,x,y);
Drawable.draw(canvas);
My question is would it be faster to (in the constructor), decode the resource as a BitMap, and then scale it it to the appropriate size. Then on each loop Draw the resource via:
canvas.drawBitmap(DrawableName, 0, 0, null);
The reason I ask is that my app draws hundreds of resources, so changing a few doesn't do enough to tell a difference, and i'd like to know whether it would be significantly faster doing it this way before I overhaul the code. Regardless, I need to increase the performance somehow so any other good ideas are also welcome.
In general, drawing bitmaps is faster than drawing as with the right preparation, drawing a bitmap is just dumping memory to the screen. If you need to draw a scaled bitmap, then draw it as one using createScaledBitmap rather than creating it then scaling it. You can achieve this by:
Bitmap myBitmap = BitmapFactory.decodeFile(myFile.getPath());
myBitmap = myBitmap.createScaledBitmap(myBitmap, width, height, true);
The Android developers documentation on the above function
Calculating and drawing primitives while running takes calculations and when drawing many of them will decrease performance, so use more bitmaps where you can - but be careful of doing premature optimisation - there's no point creating lots of bitmaps if there's no need as there will not be a significance (i.e. noticable) performance increase.
I have rendered a 3D scene in OpenGL viewed from the gluOrtho perspective. In my application I am looking at the front face of a cube of volume 100x70x60mm (which I have as 1000x700x600 pixels). Inside this cube I have rendered a simple blue sphere which sits exactly in the middle and 'fills' the cube (radius 300 pixels).
I now want to read the color value of pixels (in 3D) at specific points within the cube; i.e. I wish to know if say point (100,100,-200) is blue or blank (black).
glReadPixels only allows 2D extraction of color and I have tried it with the DEPTH_COMPONENT but am unsure what this should return in byte form? Is there a way to combine the two? Am I missing something?
I am using Eclipse with Java and JOGL.
This can't be done in the context of OpenGL--you'll need some sort of scene graph or other space partitioning scheme working in concert with your application's data structures.
The reason is simple: the frame buffer only stores the color and depth of the fragment nearest to the eye at each pixel location (assuming a normal GL_LESS depth function). The depth value stored in the Z-buffer is used to determine if each subsequent fragment is closer or farther from the eye than the existing fragment, and thus whether the new fragment should replace the old or not. The frame buffer only stores color and depth values from the most recent winner of the depth test, not the entire set of fragments that would have mapped to that pixel location. Indeed, there would be no way to bound the amount of graphics memory required if that were the case.
You're not the first to fall for this misconception, so I say it the most blunt way possible: OpenGL doesn't work that way. OpenGL never(!) deals with objects or any complex scenes. The only thing OpenGL knows about are framebuffers, shaders and single triangles. Whenever you draw an object, usually composed of triangles, OpenGL will only see each triangle at a time. And once something has been drawn to the framebuffer, whatever has been there before is lost.
There are algorithms based on the concepts of rasterizers (like OpenGL is) that decompose a rendered scene into it's parts, depth peeling would be one of them.