I'm currently trying to get a very simple program to work. It just displays a white cross on a black background. The problem is that the rendering of my cross is only working under strange conditions. These are all the conditions i figured out thus far:
The layout of the vertex shader position input has to be greater than 2
Any call to glBindVertexArray(0) is causing the cross not to render even after calling glBindVertexArray(array)
I have to call glUseProgram before every draw call
As you might see i have no idea anymore of what is acutally happening here. How do i fix this bug?
Here is the code:
int axesVBO;
int axesVAO;
int vert, frag;
int program;
#Override
public void display(GLAutoDrawable drawable) {
System.out.println("Render");
GL4 gl = drawable.getGL().getGL4();
gl.glClear(GL4.GL_COLOR_BUFFER_BIT | GL4.GL_DEPTH_BUFFER_BIT);
gl.glBindVertexArray(axesVAO);
gl.glUseProgram(program); //Doesnt work without
gl.glDrawArrays(GL4.GL_LINES, 0, 2);
gl.glDrawArrays(GL4.GL_LINES, 2, 2);
gl.glBindVertexArray(0); //After this line the cross isn't renderd anymore
}
#Override
public void dispose(GLAutoDrawable drawable) {
GL4 gl = drawable.getGL().getGL4();
gl.glDeleteBuffers(1, IntBuffer.wrap(new int[]{axesVBO}));
gl.glDeleteVertexArrays(1, IntBuffer.wrap(new int[]{axesVAO}));
gl.glDeleteProgram(program);
gl.glDeleteShader(vert);
gl.glDeleteShader(frag);
}
#Override
public void init(GLAutoDrawable drawable) {
System.out.println("Init");
GL4 gl = drawable.getGL().getGL4();
IntBuffer buffer = Buffers.newDirectIntBuffer(2);
gl.glGenBuffers(1, buffer);
axesVBO = buffer.get(0);
vert = gl.glCreateShader(GL4.GL_VERTEX_SHADER);
frag = gl.glCreateShader(GL4.GL_FRAGMENT_SHADER);
gl.glShaderSource(vert, 1, new String[]{"#version 410\n in vec2 pos;void main() {gl_Position = vec4(pos, 0, 1);}"}, null);
gl.glShaderSource(frag, 1, new String[]{"#version 410\n out vec4 FragColor;void main() {FragColor = vec4(1, 1, 1, 1);}"}, null);
gl.glCompileShader(vert);
gl.glCompileShader(frag);
if(GLUtils.getShaderiv(gl, vert, GL4.GL_COMPILE_STATUS) == GL.GL_FALSE) {
System.out.println("Vertex shader compilation failed:");
System.out.println(GLUtils.getShaderInfoLog(gl, vert));
} else {
System.out.println("Vertex shader compilation sucessfull");
}
if(GLUtils.getShaderiv(gl, frag, GL4.GL_COMPILE_STATUS) == GL.GL_FALSE) {
System.out.println("Fragment shader compilation failed:");
System.out.println(GLUtils.getShaderInfoLog(gl, frag));
} else {
System.out.println("Fragment shader compilation sucessfull");
}
program = gl.glCreateProgram();
gl.glAttachShader(program, vert);
gl.glAttachShader(program, frag);
gl.glBindAttribLocation(program, 2, "pos"); //Only works when location is > 2
gl.glLinkProgram(program);
if(GLUtils.getProgramiv(gl, program, GL4.GL_LINK_STATUS) == GL.GL_FALSE) {
System.out.println("Program linking failed:");
System.out.println(GLUtils.getProgramInfoLog(gl, program));
} else {
System.out.println("Program linking sucessfull");
}
gl.glBindBuffer(GL4.GL_ARRAY_BUFFER, axesVBO);
gl.glBufferData(GL4.GL_ARRAY_BUFFER, Float.BYTES * 8, FloatBuffer.wrap(new float[]{-1f, 0, 1f, 0, 0, 1f, 0, -1f}), GL4.GL_STATIC_DRAW);
gl.glUseProgram(program);
buffer.clear();
gl.glGenVertexArrays(1, buffer);
axesVAO = buffer.get();
gl.glBindVertexArray(axesVAO);
int pos = gl.glGetAttribLocation(program, "pos");
gl.glEnableVertexAttribArray(pos);
gl.glBindBuffer(GL4.GL_ARRAY_BUFFER, axesVBO);
gl.glVertexAttribPointer(pos, 2, GL4.GL_FLOAT, false, 0, 0);
//Commented out for testing reasons (doesnt work when active)
//gl.glBindVertexArray(0);
gl.glClearColor(0f, 0f, 0f, 1f);
}
The conditions you figured out look strange. Anyway in general, having a clean and simple code helps a lot to avoid nasty bugs. Start clean and simple and then built it up :)
Few considerations:
don't use int for vbo and vao, use directly direct buffers
don't need to declare globally vert and frag if they are gonna be used only in the init, declare them locally in the method instead
prefer generating direct buffers using the jogl utility GLBuffers.newDirect*Buffer(...)
prefer, at least at the begin, to use the jogl utility (ShaderCode.create and ShaderProgram) to compile your shaders, it offloads you from work and potential bugs and includes a deeper check on any step during the whole shader creation (sometimes even too much, but nowadays shaders are so fast to compile it doesn't matter)
if you have ARB_explicit_attrib_location, you can check with gl4.isExtensionAvailable("GL_ARB_explicit_attrib_location");, use it everywhere you can, it will avoid a lot of potential bugs and overhead with any kind of location (such as glBindAttribLocation and glGetAttribLocation)
better to pass a direct buffer to glBufferData so that jogl doesn't have to create it by itself underneath and you can keep trace of it to deallocate it
keep the init clean and readable. You are mixing a lot of stuff together. For example you generate the vbo at the begin, then you create the program, then you upload data to the vbo.
it makes no sense gl.glUseProgram(program); in the init, unless your idea is to bind it and leave it bound. Anyway, normally, program is part of the initialization phase before a rendering call, so better to move it in the display().
prefer glClearBuffer to glClear
gl.glDrawArrays(GL4.GL_LINES, 0, 2); has no utility because you are passing zero as the number of vertices
if you need inspiration, take a look of this Hello Triangle
Related
I am trying to make a simple 2D game, and I store the world in a 2D array of Block (an enum, with each value having its texture).
Since these are all simple opaque tiles, when rendering I sort them by texture and then render them by translating to their coordinate. However, I also need to specify the texture coordinates and the vertex for each tile that I draw, even though these are also the same.
Here's what I currently have:
public static void render() {
// Sorting...
for(SolidBlock block : xValues.keySet()) {
block.getTexture().bind();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
for(int coordinateIndex = 0; coordinateIndex < xValues.get(block).size(); coordinateIndex++) {
int x = xValues.get(block).get(coordinateIndex);
int y = yValues.get(block).get(coordinateIndex);
glTranslatef(x, y, Integer.MIN_VALUE);
// Here I use MIN_VALUE because I'll later have to do z sorting with other tiles
glBegin(GL_QUADS);
loadModel();
glEnd();
glLoadIdentity();
}
xValues.get(block).clear();
yValues.get(block).clear();
}
}
private static void loadModel() {
glTexCoord2f(0, 0);
glVertex2f(0, 0);
glTexCoord2f(1, 0);
glVertex2f(1, 0);
glTexCoord2f(1, 1);
glVertex2f(1, 1);
glTexCoord2f(0, 1);
glVertex2f(0, 1);
}
I'd like to know if it is possible to put loadModel() before the main loop, to avoid having to load the model thousands of times with the same Data, and also what else could be moved to make it as fast as possible!
Some quick optimizations:
glTexParameteri only needs to be called once per parameter per texture. You should put it in the part of your code where you load the textures.
You can draw multiple quads in one glBegin/glEnd pair simply by adding more vertices. However, you cannot do any coordinate changes between glBegin and glEnd (such as glTranslatef or glLoadIdentity or glPushMatrix) so you'll have to pass x and y to your loadModel function (which really should be called addQuad for accuracy). It's also not allowed to rebind textures between glBegin/glEnd, so you'll have to use one set of glBegin/glEnd per texture.
Minor, but instead of calling xValues.get(block) a whole bunch of times, just say List<Integer> blockXValues = xValues.get(block) at the beginning of your outer loop and then use blockXValues from there on.
Some more involved optimizations:
Legacy OpenGL has draw lists, which are basically macros for OpenGL. You can make OpenGL record all the OpenGL calls you're doing between glNewList and glEndList (with some exceptions), and store them somehow. The next time you want to run those exact OpenGL calls, you can use glCallList to make OpenGL do just that for you. Some optimizations will be done on the draw list in order to speed up subsequent draws.
Texture switching is relatively expensive, which you're probably already aware of since you sorted your quads by texture, but there is a better solution than sorting textures: Put all your textures into a single texture atlas. You'll want to store the subtexture coordinates of each block inside your SolidBlocks, and then pass block to addQuad as well so you can pass the appropriate subtexture coordinates to glTexCoord2f. Once you've done that, you don't need to sort by texture anymore and can just iterate over x and y coordinates.
Good practices:
Only use glLoadIdentity once per frame, at the beginning of your draw process. Then use glPushMatrix paired with glPopMatrix to save and restore the state of matrices. That way the inner parts of your code don't need to know about the matrix transformations the outer parts may or may not have done beforehand.
Don't use Integer.MIN_VALUE as a vertex coordinate. Use a constant of your own choosing, preferably one that won't make your depth range huge (the last two arguments to glOrtho which I assume you're using). Depth buffer precision is limited, you'll run into Z-fighting issues if you try to use Z coordinates of 1 or 2 or so after setting your Z range from Integer.MIN_VALUE to Integer.MAX_VALUE. Also, you're using float coordinates, so int constants don't make sense here anyway.
Here's the code after a quick pass (without the texture atlas changes):
private static final float BLOCK_Z_DEPTH = -1; // change to whatever works for you
private int blockCallList;
private boolean regenerateBlockCallList; // set to true whenever you need to update some blocks
public static void init() {
blockCallList = glGenLists(1);
regenerateBlockCallList = true;
}
public static void render() {
if (regenerateBlockCallList) {
glNewList(blockCallList, GL_COMPILE_AND_EXECUTE);
drawBlocks();
glEndList();
regenerateBlockCallList = false;
} else {
glCallList(blockCallList);
}
}
private static void drawBlocks() {
// Sorting...
glPushMatrix();
glTranslatef(0, 0, BLOCK_Z_DEPTH);
for (SolidBlock block : xValues.keySet()) {
List<Integer> blockXValues = xValues.get(block);
List<Integer> blockYValues = yValues.get(block);
block.getTexture().bind();
glBegin(GL_QUADS);
for(int coordinateIndex = 0; coordinateIndex < blockXValues.size(); coordinateIndex++) {
int x = blockXValues.get(coordinateIndex);
int y = blockYValues.get(coordinateIndex);
addQuad(x,y);
}
glEnd();
blockXValues.clear();
blockYValues.clear();
}
glPopMatrix();
}
private static void addQuad(float x, float y) {
glTexCoord2f(0, 0);
glVertex2f(x, y);
glTexCoord2f(1, 0);
glVertex2f(x+1, y);
glTexCoord2f(1, 1);
glVertex2f(x+1, y+1);
glTexCoord2f(0, 1);
glVertex2f(x, y+1);
}
With modern OpenGL (vertex buffers, shaders and instancing instead of display lists, matrix transformations and passing vertices one by one) you'd approach this problem very differently, but I'll keep that beyond the scope of my answer.
I'm trying to do a 3D collision test with Bullet, which I would have expected to work fairly simply:
#Override
public void create(){
. . .
/*
* Collision
*/
collisionConfig=new btDefaultCollisionConfiguration();
dispatcher=new btCollisionDispatcher(collisionConfig);
broadphase=new btDbvtBroadphase();
world=new btCollisionWorld(dispatcher, broadphase, collisionConfig);
contactListener=new PiscesContactListener();
contactListener.enable();
/*
* Test stuff
*/
btSphereShape sphere=new btSphereShape(2.5f);
btCollisionObject object1=new btCollisionObject();
btCollisionObject object2=new btCollisionObject();
object1.setCollisionShape(sphere);
object2.setCollisionShape(sphere);
object1.setWorldTransform(new Matrix4(new Vector3(0f, 0f, 0f), new Quaternion(0f, 0f, 0f, 0f), new Vector3(1f, 1f, 1f)));
object2.setWorldTransform(new Matrix4(new Vector3(1f, 0f, 0f), new Quaternion(0f, 0f, 0f, 0f), new Vector3(1f, 1f, 1f)));
object1.setUserValue(0);
object2.setUserValue(1);
object1.setCollisionFlags(WorldObject.COLLISION_PRIMARY); // 1<<9
object2.setCollisionFlags(WorldObject.COLLISION_PRIMARY);
world.addCollisionObject(object1, WorldObject.COLLISION_PRIMARY, WorldObject.COLLISION_EVERYTHING); // -1
world.addCollisionObject(object2, WorldObject.COLLISION_PRIMARY, WorldObject.COLLISION_EVERYTHING);
. . .
}
#Override
public void render() {
. . .
world.performDiscreteCollisionDetection();
. . .
}
/*
* In a separate file
*/
public class PiscesContactListener extends ContactListener {
public boolean onContactAdded (int userValue0, int partId0, int index0, boolean match0, int userValue1, int partId1, int index1, boolean match1) {
System.out.println("Collision detected between "+userValue0+" and "+userValue1);
return true;
}
}
(I followed this guide, though I obviously deviated from it a little to try to make it even simpler.)
A 2.5-unit sphere at (0, 0, 0) and a 2.5-unit sphere at (1, 0, 0) should cause a collision, should they not? Nothing's appearing in the console window.
I somewhat suspect there's something fundamental that I'm forgetting, since I tried to do a raycast and that isn't working either.
It's probably worth mentioning that calling world.drawDebugWorld(); draws wireframes of all the objects where they would be expected to be, so now I'm suspecting the error lies in the contact listener or maybe the flags I'm using (though I haven't had any luck with any other collision flags). Am I missing something in the contact listener?
Also, I tried using a ContactCache instead of a ContactListener but that didn't work either. Most reading I can find on the matter talks about ContactListener so I'm probably better off using that.
I'm obviously forgetting something since other people are able to do this, can anyone point out what that might be?
You are mixing collision flags (btCollisionObject#setCollisionFlags(int *flags*)) and collision filtering (btCollisionWorld#addCollisionObject(object, short *group*, short *mask*)). These are two very different things.
You should not call the btCollisionObject#setCollisionFlags(int) method with anything other than the available flags (see here). The CF_CUSTOM_MATERIAL_CALLBACK collision flag must be set for the ContactListener#onContactAdded method to be called. So that's why your code doesn't work as you expected.
Note that in my tutorial you are referring to, this is explained as well:
public void spawn() {
...
obj.body.setUserValue(instances.size);
obj.body.setCollisionFlags(obj.body.getCollisionFlags() | btCollisionObject.CollisionFlags.CF_CUSTOM_MATERIAL_CALLBACK);
...
}
In the spawn method we set this value, using the setUserValue method, to the index of the object in the instances array. And we also inform Bullet that we want to receive collision events for this object by adding the CF_CUSTOM_MATERIAL_CALLBACK flag. This flag is required for the onContactAdded method to be called.
There is no reason to use 1<<11 instead of 1<<9 for collision filtering. You probably changed something else (e.g. the collision flag) as well and wrongfully assumed that this change in the collision filter caused it to start working.
Note that besides collision flags and collision filtering, there is also the (libgdx specific) contact callback filtering (explained in the second part of that tutorial). Keep in mind that those are three totally different and unrelated things.
I want to use Alpha/Blend mode for future stuff (transactions mainly and possible image blending).
Well, I can't get it to work using LWJGL (GL1.1), I already tried other blend modes but didn't worked, nor changing the background or anything like that...
Screenshots:
http://i.imgur.com/cHU4YGS.png - GL_BLEND always enabled, everything is transparent
http://i.imgur.com/sPmPqne.png - GL_BLEND enabled on QUAD and text, I can see the line that is on disabled GL_BLEND
i imgur com/nkda41v png - GL_BLEND disabled on everything but the text -> I need some reputation to post more than 2 links, sorry about that but I belive this image is important so i'll post it anyway. Just fill with dots
The results are the same with or without alpha argument on all these tests
Code:
` private void init() {
try {
Display.setDisplayMode(new DisplayMode(DEFAULT_WIDTH, DEFAULT_HEIGHT));
Display.setResizable(true);
Display.setVSyncEnabled(true);
Display.setTitle(DEFAULT_TITLE + " v" + VERSION);
Display.create();
updateMatrix();
} catch(LWJGLException e) {
e.printStackTrace();
}
Keyboard.enableRepeatEvents(true);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
Font consolas = new Font("consolas", Font.PLAIN, 13);
font = new TrueTypeFont(consolas, antiAliasedFont);
}
private void updateMatrix() {
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, DEFAULT_WIDTH, DEFAULT_HEIGHT, 0, 1, -1);
//glScaled((double) DEFAULT_WIDTH / (double) Display.getWidth(), (double) DEFAULT_HEIGHT / (double) Display.getHeight(), 0);
glViewport(0, 0, Display.getWidth(), Display.getHeight());
glMatrixMode(GL_MODELVIEW);
}
#Override
public void run() {
init();
Main main = Main.getMain();
while(!Display.isCloseRequested()) {
currentGraphicsTick++;
{
glClear(GL_COLOR_BUFFER_BIT);
glClearColor(0f, 0f, 0f, 1f);
if(Display.wasResized()) updateMatrix();
if(vsyncMode == 1) Display.setVSyncEnabled(true);
else if(vsyncMode == 2) Display.setVSyncEnabled(false);
if(Display.isActive()) {
glPushMatrix();
try { // Draw
float alpha = (float) Math.cos(Math.toRadians(currentGraphicsTick % 90));
System.out.println("Alpha: " + alpha);
glBegin(GL_LINE_STRIP);
{
float sin = (float) Math.abs(Math.sin(Math.toRadians(currentGraphicsTick % 360)));
new Color(0.7f, 0.7f, 0.7f, alpha).bind();
glVertex2f(DEFAULT_WIDTH * 0.03f, DEFAULT_HEIGHT * 0.05f);
glVertex2f(DEFAULT_WIDTH * 0.93f * sin, DEFAULT_HEIGHT * 0.95f * sin);
}
glEnd();
glBegin(GL_QUAD_STRIP);
{
new Color(0.5f, 0.5f, 0.5f, alpha).bind();
glVertex2i(0, 0);
glVertex2i(0, DEFAULT_HEIGHT);
glVertex2i(DEFAULT_WIDTH, 0);
glVertex2i(DEFAULT_WIDTH, DEFAULT_HEIGHT);
}
glEnd();
String[] split = main.getGameLoopThread().getDebugString().split("\n");
for(int i = 0; i < split.length; i++) {
font.drawString(1, 1 + (i * font.getLineHeight()), split[i], Color.white);
}
} catch(Throwable throwable) {
throwable.printStackTrace();
}
glPopMatrix();
}
Display.update();
Display.sync(TARGET_FPS);
}
}
Display.destroy();
closeRequested = true;
}
I already tried:
Removing the 'alpha' argument from the Slick's Color constructor
Using OpenGL's glColor with and without alpha argument
Disabling/Enabling GL_BLEND in part of the code (I know some things wouldn't work, but you never know, right?)
Used constants to the alpha variable (such as 0.3f, 0.5f, 0.7f, 1f) instead of making it variable through Math.sin/cos using the tick as the degree
Using glRect(...)
Changing the background
Removing the glClearColor
Removing glClear (nice effect, never did this lol)
What I expect to see was a fading moving LINE_STRIP:
On one side it moves from the (0, 0) to (width - 7%, height - 5%)
On the other it stand still on (width + 3%, height + 5%)
the rectangle would make it fade (the original idea would use the same color as the background, but it didn't on my tests because I want to see the rectangle)
I've had a similar(in terms of what I had to tackle) when doing a 2D game using my own engine. LWJGL's blend functions always have a few issues and there aren't really that many concrete answers for them as it really boils down to your code and how you placed things.
I presume you're using SlickUtil, and to that I would say write your own (or of course search around) your own methods for this. Util's methods were always somewhat wonky with blending.
Removing alpha shouldn't have changed much in terms of getting
what you want
The only time I explicitly enable GL_BLEND is
when rendering lights(always presuming you're doing 3D) and when I would render textures
I suggest not removing glClearColor, that doesn't directly affect your situation as
long as you put it in the correct place
Like I said this bug/problem could be due to quite a few things that I can't quite pin-point from what you've posted.
My suggestions are:
Organise the whole project well to be able to point bugs out quickly
Make alot of stuff generic ASAP(Doesn't matter if you just want to quickly write something)
Avoid SlickUtil, it's great and I LOVE it but it does get it's issues
Sorry I can't help too much but tracking down your issue from the jumbled up code you posted is a bit difficult. I'll keep my eye on this question in case I may be of help in the future.
Besides the tips from #Juxhin, what fixed my problem with alpha blend was TextureImpl.bindNone(); from SlickUtils (check openGL's similar below)
This method is similar to glDisable(GL_TEXTURE_2D) before rendering the thing that needs to be blent (I know that searching and checking Slick's source)
I have an issue with drawing potentially hundreds of thousands rectangles with Java2D.
At first what I did was something along these lines:
private void render() {
BufferStrategy bs = getBufferStrategy();
if (bs == null) {
createBufferStrategy(3);
return;
}
Graphics g = bs.getDrawGraphics();
//Draw Graphics here
g.dispose();
bs.show();
}
I then realized after testing that this was horribly inefficient when having anything above 500 rectangles, so I thought that maybe drawing the graphics to a BufferedImage then displaying the BufferedImage would be more efficient, so I came up with this.
private BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
private void render() {
BufferStrategy bs = getBufferStrategy();
if (bs == null) {
createBufferStrategy(3);
return;
}
Graphics g = image.createGraphics();
//Draw Graphics here
g.dispose();
Graphics g2 = bs.getDrawGraphics();
g2.drawImage(image, 0, 0, width, height, null);
g2.dispose();
bs.show();
}
However this was still just as inefficient as the first way, I'm having trouble thinking of a better way to do this, and I'd prefer to stay away from something like OpenGL for this.
At between 500-1000 rectangles it gets really slow, and anything above that the program freezes very quickly.
So it would seem collision detection seems to be the main problem, rather than Java2D rendering. This is how I've handled the detection, it checks if two rectangles are touching on any side. If someone has a better approach to this I would appreciate it, because I'm seeing how inefficient this is.
public boolean collides(Entity e) {
Point2D upperLeftIn = new Point2D.Double(bounds.getX() + 1, bounds.getY());
Point2D upperRightIn = new Point2D.Double(bounds.getX() + 8, bounds.getY());
Point2D lowerLeftIn = new Point2D.Double(bounds.getX() + 1, bounds.getY() + 9);
Point2D lowerRightIn = new Point2D.Double(bounds.getX() + 8, bounds.getY() + 9);
Point2D upperLeftDown = new Point2D.Double(bounds.getX(), bounds.getY() + 1);
Point2D lowerLeftUp = new Point2D.Double(bounds.getX(), bounds.getY() + 8);
Point2D upperRightDown = new Point2D.Double(bounds.getX() + bounds.getWidth(), bounds.getY() + 1);
Point2D lowerRightUp = new Point2D.Double(bounds.getX() + bounds.getWidth(), bounds.getY() + 8);
Line2D top = new Line2D.Double(upperLeftIn, upperRightIn);
Line2D bottom = new Line2D.Double(lowerLeftIn, lowerRightIn);
Line2D left = new Line2D.Double(upperLeftDown, lowerLeftUp);
Line2D right = new Line2D.Double(upperRightDown, lowerRightUp);
if (e.bounds.intersectsLine(top)) {
return true;
}
if (e.bounds.intersectsLine(bottom)) {
return true;
}
if (e.bounds.intersectsLine(left)) {
return true;
}
if (e.bounds.intersectsLine(right)) {
return true;
}
return false;
}
Although I'm not sure how to handle the fact that the contents/goal of the question changed completely due to the edit...
To summarize: As it was pointed out in the comment (and you seem to have anticipated), the rendering of even thousands of rectangles should not be so much an issue in Java2D. Internally, it is using hardware support with DirectX or OpenGL, and you really have to spill lots of antialiased text and complex, textured or gradient-painted shapes onto the screen in order to really slow it down.
That being said, it's really not unlikely that the collision detection is the bottleneck here.
Presumably, the method that you posted is in the Entity class. And presumably, e.bounds is a Rectangle2D. In this case, you could simply test for intersections with
public boolean collides(Entity e) {
return this.bounds.intersects(e.bounds);
}
It is not clear what you wanted to achieve with the creation of the Line2D objects there, and you should probably explain this in words. (Maybe some sort of "threshold" around the actual bounds?). But you should keep in mind that the intersectsLine method can be computationally expensive, at least compared to the test that you actually want to do. You should try to boil this down to interval checks.
But even if you do this sort of (micro?-)optimizations to your collides method, the problem may be a more general one: From what you have written so far, one has to assume that you are testing each entity for collisions with each other entity, and this method is called in a loop like
for (int i=0; i<allEntities.size(); i++)
{
for (int j=i+1; j<allEntities.size(); j++)
{
Entity ei = allEntities.get(i);
Entity ej = allEntities.get(j);
if (ei.collides(ej)) handleCollision();
}
}
If this is the case, no optimization of the implementation will help you, because the problem lies in the asymptotic complexity: The number of intersection tests (that is, the number of calls to the collides-method) grows quadratically with the number of objects. For 500 objects, you'll have to do do ~125.000 calls to the method. That's already quite a lot, and could hardly be done 60 times per second. But for 5000 objects, you don't need 1.250.000 calls, but 12.500.000 - this is 100 times as many, and of course, impossible to do at 60 FPS.
There are sophisticated data structures for the (pairwise) collision detection of so many objects. Unfortunately, "sophisticated" here often means "horribly complicated to implement". Bounding Volume Hierarchies may be one approach that can help to accelerate the collision detection with reasonable effort. But if you have "many" and "small" objects that are in a "small" space, Spatial Hashing may be the more appropriate solution. You can quickly find tutorials/blog entries and sample code using this keyword, one example is at Spatial hashing implementation for fast 2D collisions, but there are several others.
I'm using https://github.com/markfguerra/GLWallpaperService/ to make an android live wallpaper.
I'm trying to load a png file as fullscreen background, however currently all I get is a black screen.
I've searched for a few days now but still haven't found out the problem.
I'm doing the following:
public void onSurfaceCreated(GL10 gl, EGLConfig config)
{
gl.glEnable(GL10.GL_TEXTURE_2D);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
}
and every frame:
public void onDrawFrame(GL10 gl)
{
Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.target00074);
int[] textures = new int[1];
gl.glGenTextures(1, textures, 0);
gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
}
I would really recommend to dive into the OpenGL basics as you don't seem to know what you are doing. The screen is black because you are just uploading a texture (something you should do at initialization instead of every frame) and not actually drawing anything. You will need to define some arrays defining vertex and texture positions for a start. Refer to http://blog.jayway.com/2009/12/04/opengl-es-tutorial-for-android-%E2%80%93-part-ii-building-a-polygon/
As for the fullscreen background, you would create a single polygon with your texture
I found my solution in: http://obviam.net/index.php/texture-mapping-opengl-android-displaying-images-using-opengl-and-squares/
Two basic mistakes for when textures don't work:
Texture width/height should be a power of two, unless the device supports some extension that alleviates this requirement.
Set the TEXTURE_MIN_FILTER to something like NEAREST or LINEAR, the default is mipmapped and this produces incomplete textures unless you provide mipmaps.
Also, the texture parameter state is not global, is per texture object, so set it after binding your texture.
I realize that this answer doesn't really address the question at hand. Downvote me if you must.
If all you want to do is draw a full-screen background image, you don't need OpenGL. You aren't working in 3D and a static background image doesn't need to be updated very often (especially not 30-60 times a second!) so using OpenGL will only kill the battery. A better solution is to have a look at the relevant code examples and documentation, where you will see that all you really need to do is copy-paste the CubeWallpaper example, but change drawFrame() to look like the following:
void drawFrame() {
final SurfaceHolder holder = getSurfaceHolder();
Canvas c = null;
try {
c = holder.lockCanvas();
if (c != null) {
// draw something
c.drawBitmap(myBitmap, myMatrix, myPaint);
}
} finally {
if (c != null) holder.unlockCanvasAndPost(c);
}
// Reschedule the next redraw
mHandler.removeCallbacks(mDrawCube);
if (mVisible) {
mHandler.postDelayed(mDrawCube, 1000 / 25);
}
}
where myBitmap, myMatrix, and myPaint are Bitmap, Matrix, and Paint objects that have been initialized previously.
Even if my initial assumption (your image is static) is false, this way can still be much more efficient if your image doesn't change terribly often, for example twice a second or slower.
Hope this helps