Related
I am programming a GUI framework in lwjgl (opengl for java). I've recently implemented rounded rectangles by rendering a couple of normal rectangles surrounded by circles. To render the circles I used GL11.GL_POINTS. I now reached the point, where I am trying to implement animations and for a window open animation, I decided to GL11.glScaled() it from small to normal. That works fine, but unfortunately my circles don't get resized.
I tried changing my GL_POINTS circle render method against a method that uses TRIANGLE_FANs and that worked fine. My problem there was, that the circles didn't look smooth and round at all and if I increase the rendered triangles it starts to lag very quick. Even though my computer isn't bad at all.
This is the code I've used to render circles with GL_POINTS.
GL11.glEnable(GL11.GL_POINT_SMOOTH);
GL11.glHint(GL11.GL_POINT_SMOOTH_HINT, GL11.GL_NICEST);
GL11.glPointSize(radius);
GL11.glBegin(GL11.GL_POINTS);
GL11.glVertex2d(x, y);
GL11.glEnd();
GL11.glDisable(GL11.GL_POINT_SMOOTH);
This is the code I've used to scale the circles
GL11.glPushMatrix();
GL11.glTranslated(x, y, 0);
GL11.glScaled(2.0f, 2.0f, 1);
GL11.glTranslated(-x, -y, 0);
render circles
GL11.glPopMatrix();
I expect the circles to scale accordingly to the number I've put into glScaled()
Currently they aren't rescaling at all, just rendered at their normal size.
Here's a demonstration of how to properly render a circle using triangle fans:
public void render() {
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT);
glLoadIdentity();
// Coordinate system starts out as screen space coordinates
glOrtho(0, 400, 300, 0, 1, -1);
glColor3d(1, 0.5, 0.5);
renderCircle(120, 120, 100);
glColor3d(0.5, 1, 0.5);
renderCircle(300, 200, 50);
glColor3d(0.5, 0.5, 1);
renderCircle(200, 250, 30);
}
private void renderCircle(double centerX, double centerY, double radius) {
glPushMatrix();
glTranslated(centerX, centerY, 0);
glScaled(radius, radius, 1);
// Another translation here would be wrong
renderUnitCircle();
glPopMatrix();
}
private void renderUnitCircle() {
glBegin(GL_TRIANGLE_FAN);
int numVertices = 100;
double angle = 2 * Math.PI / numVertices;
for (int i = 0; i < numVertices; ++i) {
glVertex2d(Math.cos(i*angle), Math.sin(i*angle));
}
glEnd();
}
Output image:
The GL_POINT_SIZE value is actually the size of the point in pixels onscreen, not current coordinate units. For that reason your circles were unaffected by GL_SCALE. That's one reason not to use GL_POINTS to render circles. The other (arguably more important) reason being that GL_POINT_SIZE is severely deprecated and unsupported in newer OpenGL profiles.
I've been trying to learn libgdx a little but now i'm kinda confused about world units there that i don't even know what exact question i should be asking.
Anyway, i've been reading this example game code here
and from what i understand, when there's no camera, SpriteBatch renders everything in relation to device resolution, so pixels, but when we make a camera, set it's "size", position and then use batch.setProjectionMatrix(camera.combined), batch translates pixels to units and then it knows where to render , but in this game there's collision detection between Rectangle objects, and when you're setting position of this Rectangle with rect.set(x, y, 1, 1); where x and y are world units and not pixels, how does rectangle know if it should use those x and y as units and not as pixels if there's nothing used like setProjectionMatrix to let it know that now we're working in units and not in pixels, and then there's this 1, 1); at the end, is it in units too? if so, then how does Rectangle know how big those units shoud be (Scale in game is 1 / 16 for example) renderer = new OrthogonalTiledMapRenderer(map, 1 / 16f);.
I don't even know if this question really makes sense, but that's where i'm at and i'm really lost here
EDIT: Ok, i understand how units work now, but collision still confuses me a bit, here's example, i created this
// onCreate
mapSprite = new Sprite(new Texture(Gdx.files.internal("space.png")));
planetTexture = new Texture(Gdx.files.internal("planet.png"));
shapeRenderer = new ShapeRenderer();
//Planet(X,Y,Radius)
planet = new Planet(20, 30, 7);
planet2 = new Planet(70, 50, 8);
circle = new Circle(planet.getPosition().x + planet.radius, planet.getPosition().y + planet.radius, planet.radius);
circle2 = new Circle(planet2.getPosition().x + planet2.radius, planet2.getPosition().y + planet2.radius, planet2.radius);
mapSprite.setPosition(0,0);
mapSprite.setSize(133, 100);
stateTime = 0;
camera = new OrthographicCamera();
camera.setToOrtho(false, 133, 100);
camera.position.set(133 /2, 100 /2, 0);
camera.update();
batch = new SpriteBatch();
...
// Render Method
batch.setProjectionMatrix(camera.combined);
batch.begin();
mapSprite.draw(batch);
batch.draw(planetTexture, planet.getPosition().x, planet.getPosition().y, planet.radius * 2, planet.radius * 2);
batch.draw(planetTexture, planet2.getPosition().x, planet2.getPosition().y, planet2.radius * 2, planet2.radius * 2);
batch.end();
shapeRenderer.setProjectionMatrix(camera.combined);
shapeRenderer.setColor(Color.RED);
shapeRenderer.begin(ShapeRenderer.ShapeType.Filled);
shapeRenderer.circle(circle.x, circle.y, circle.radius + 1);
shapeRenderer.end();
And everything renders fine, the ShapeRenderer circle is where it's supposed to be after setting ShapeRenderer's projection matrix shapeRenderer.setProjectionMatrix(camera.combined) :
But, i still don't understand how to check collision on this circle, when i do this in render method and press on the circle nothing is happening, like i don't get that log and i assume that's because the circle doesn't know the world scale (no nothing like setprojectionmatrix ) and render coordinates and where the actual circle "thinks" it is don't match. How do i check collision on that circle?
if(Gdx.input.justTouched()) {
if(circle.contains(Gdx.input.getX(), Gdx.input.getY())) {
Gdx.app.log("SCREEN", "TOUCHED AND CONTAINS");
}
}
EDIT2: I get it now, everything works, i just wasn't using camera.unproject on touch coordinates
Imagine you have a device 1200 x 800 pixel big.
Now you say you will have a box2d rectangle on position: 100, 100 and size: 100,100
When you now use your SpriteBatch (Box2dDebugRenderer) to render this you will see a world 1200 x 800 units big. You must try to forget to think in pixels. Important is that the SpriteBatch always draw 1200 x 800 pixels but not always 1200 x 800 units, later more.
So this is what you see:
When we now use a Camera we say we will only our world of 600 x 400 units.
We create a Camera with size: 600 x 400 units!
camera = new OrthographicCamera(600, 400);
The batch still draw 1200 x 800 units and 1200 x 800 pixels
Now with batch.setProjectionMatrix(camera.combined); you give the batch a Matrix to calculate the size of a unit. With this Matrix he can calculate how big it musst draw 1 unit.
And after that the SpriteBatch draw 600 x 400 units but still 1200 x 800 pixels.
And then you see:
For this reason you can forget to think in pixels the Matrix of camera makes the calculation for you from pixel to units and you can focus on thinking in units.
The next thing why you must think in units is that box2D calculates in meters. So the rectangle is 100 x 100 meters big and 100 meter above the bottom. So by gravity of g = 9,81m/s the rectangle falls not so fast as you might expect.
The SpriteBatch always draw 1200 x 800 pixels otherwise you only see the game on the half of your screen. But with the Matrix of camera the batch know how many pixel he must draw 1 unit and so you see a World of 600 x 400 units on a Screen of 1200 x 800 pixels.
Attention: 1 unit is not always equal 1 meter. For example in a spaceship game there are for example 5 kilometre between two ships. So please don't create a World of 10000 x 10000 units because Box2d won't calculate that.
Then you can say 1 unit = 100 meter, so you have a World of 100 x 100 units.
Now you must remember when a ship should be 200 m/s fast you must set body.setLinearVelocity(2,0) because 1 unit = 100 meter.
So always think in units!
Edit:
Sometimes we do not come around pixel for example Gdx.input
Gdx.input return the position in pixel.
Now our camera have two methods to convert from pos in pixel to pos of our units and vice versa.
float x = Gdx.input.getX();
float y = Gdx.input.getY();
camera.unproject(new Vector3(x, y, 0));//this converts a Vector3 position of pixels to a Vector3 position of units
camera.project(new Vector3(10,10,0));//this converts a Vector3 position of units to a Vector3 position of pixels
I hope I could help you
When rendering things using a camera, you should be setting the projection matrix of the sprite batch to the combined view of the camera.
The world is completely separate from the camera. The world has coordinates both positive and negative. The camera is your view of the world, and like a literal camera, it can move around in space. When you move the game camera, you are merely changing what part of the world you can see.
All physics is done on pixel level (in your case) relative to the world origin. It is not changed in any way by the camera.
I am trying to turn my screen x and y coordinates to the ones that's used to draw on screen.
So I get my screen X and Y coordinates from MotionEvent thats fired by my touch listener.
I thought it should be as easy as multiplying those by the matrix that's used to draw on canvas so I creatad Matrix instance at creation of view
matrix = new Matrix();
when void onDraw(Canvas canvas) gets called, I set the canvas' matrix to be the matrix I created on the constructor and apply all my transformations to the matrix
matrix.reset();
canvas.setMatrix(matrix);
canvas.translate(getWidth() / 2, getHeight() / 2);
canvas.scale(mScaleFactor, mScaleFactor);
canvas.translate(-getWidth() / 2, -getHeight() / 2);
canvas.translate(-x, -y);
The view looks as it should but when on my touch listener I try to turn my screen coordinates to view coordinates with that matrix using mapPoints(float[] points) the values it gives aren't right, I draw cross on 0, 0 in onDraw
canvas.drawLine(0f, -100f, 0f, 100f, viewportPaint);
canvas.drawLine(-100f, 0f, 100, 0f, viewportPaint);
and when I click where it appears to be after scaling and what not the values I receive aren't even close to 0, 0
float[] array = new float[]{e.getX(), e.getY()};
matrix.mapPoints(array);
Log.v(TAG, "points transformed are " + array[0] + ", " + array[1]);
When I took this picture where I am clearly clicking the 0, 0 mark I received the following logging:
03-09 14:08:48.803: V/XXXX(22181): points transformed are 403.43967, 628.47
Ps. I am not touching matrix anywhere else than in my onDraw code
I had the question in my mind inverted, when I thought about the question again after having a break from it(after 8 hours..) I got it, I had to invert the matrix in order to perform this operation.
Also the matrix wasn't transformed when I was transforming canvas so i had to canvas.get(matrix)
in order to get the same matrix that the canvas was using..
This might help others:
In onDraw()
canvasMatrix.postScale(mScaleFactor, mScaleFactor);
canvasMatrix.postTranslate(-getWidth() / 2, -getHeight() / 2);
canvas.setMatrix(canvasMatrix);
Somewhere else where you need the mapped coordinates:
Matrix m = new Matrix();
canvasMatrix.invert(m);
float[] touch = new float[] { X, Y };
m.mapPoints(touch);
X = touch[0];
Y = touch[1];
there you go.
this is a big problem I have been running into.
I am trying to render multiple tiles using glTranslate but when I call my draw function with the x, y coordinates the tiles are spaced weirdly(I don't want spaces).
Here is what happens.
here is my code:
Draw:
public void draw(float Xa, float Ya) {
GL11.glTranslatef(Xa, Ya, 0);
if(hasTexture) {
Texture.bind();
GL11.glBegin(GL11.GL_QUADS);
GL11.glColor3f(0.5f, 0.5f, 1);
GL11.glTexCoord2f(0, 0);
GL11.glVertex2f(0, 0);
GL11.glTexCoord2f(0, 1);
GL11.glVertex2f(0, S);
GL11.glTexCoord2f(1, 1);
GL11.glVertex2f(S, S);
GL11.glTexCoord2f(1, 0);
GL11.glVertex2f(S, 0);
GL11.glEnd();
}
and my render code:
public void a() throws IOException {
GL11.glTranslatef(0, 0, -10);
int x = 0;
while (x < World.BLOCKS_WIDTH - 1) {
int y = 0;
while (y < World.BLOCKS_HEIGHT - 1) {
blocks.b[data.blocks[x][y]].draw(x, y);
y++;
}
x++;
}
there are no errors (except the visible ones)
You do not appear to be initialising or pushing / popping the current transform. So the translations will accumulate, producing the effect you see, getting further and further apart as you translate by ever larger values.
Lets say your blocks are 10 units apart. The first is drawn with a translation of (0, 0), then next (0, 10), then (0, 20), (0, 30), etc.
However as the translations accumulate in the view matrix, what you actually get are translations of (0,0), (0,10), (0,30), (0,60), etc.
This is important, as it allows you to build a complex transform from a series of simple discrete steps. However when you want to render multiple objects, each with their own transform, you need to have some form of reset in between each object.
You could reinitialise the whole matrix, but that's a bit untidy and involves knowing what other transforms (such as the camera, etc.) have been done previously.
Instead, you can "push" the current matrix onto a stack, perform whatever local transformations you want to do, render stuff, and then "pop" the matrix back off so that you're back where you started, ready to render the next object.
I should point out that all this functionality is deprecated in the later versions of GL. With the more modern API you use shaders, and can supply whatever transforms you care to calculate.
How can we rotate a Image Clockwise using LibGDX? what i am looking is when a image is loaded,suppose a star, i need to rotate it from beginning of screen to end of the screen horizontally, with star rotating,how can i do that in libgdx?
When you draw the Texture with your SpriteBatch, you can use one of the draw functions that includes rotation. This javadoc has all the draw functions: SpriteBatch
You can keep a variable for position and rotation, and increase the rotation and x component of the position each time you render to make it rotate while moving horizontally.
Libgdx gives you more then one way to do that:
You can use Scene2D and add an Image to your Stage. Image is a subclass of Actor, so you can add Actions to it:
Image myImage = new Image(myTexture);
myImage.addAction(Actions.parallel(Actions.moveTo(endX, endY, duration), Actions.rotateBy(degrees, duration)));
myImage.setPosition(startX, startY);
myImage.setOrigin(sizeX/2, sizeY/2);
stage.add(myImage);
In render you can then call stage.act(), which updates the position, rotation, scale... of all your Actors and then call stage.draw() which will call draw() for all your Actors.
Image allready handles the draw() so you don't need to care about that anymore.
You can also do it without scene2d, by updating everything yourself:
You can store a int rotationSpeed in degrees/sec
You can store a int moveSpeed in units/sec (maybe pixel but i would suggest to use camera or viewport and use your own unit, which is equal on all devices)
Store the float angle, which is the current rotation of your Texture
and store a Vector2 position, which contains the x and y position of your Texture.
If you want to move in x and y direction you can also store a Vector2 direction, which is a normalized Vector, giving the percent of movement in x and y direction, but thats a different story.
Then in your render(float delta) you update everything:
angle+=delta*rotationSpeed;
angl%=360; // Limits the angle to be <= 360
while (angle < 0) // Unfortunally the "modulo" in java gives negative result for negativ values.
angle+=360;
position.x+=direction.x*moveSpeed*delta;
position.y+=direction.y*movSpeed*delta;
spriteBatch.draw(yourTextureRegion, position.x, position.y, sizeX/2, sizeY/2, sizeX, sizeY, scaleX, scaleY, angle);
For clockwise rotation simply use a negative rotationSpeed or replace the angle+= with angle-=.
Hope it helps.
Following is the implementation to rotate any sprite
batch.draw(sprite,(Gdx.graphics.getWidth() - sprite.getRegionWidth()) / 2.0f,(Gdx.graphics.getHeight() - sprite.getRegionHeight()) / 2.0f,sprite.getRegionWidth()/2.0f,sprite.getRegionHeight()/2.0f, sprite.getRegionWidth(), sprite.getRegionHeight(), 1f, 1f,count, false);
if(count < 0.0f)
count = 360.0f;
else
count --;
Initially set counter to
private float count =360.0f;
You can also use the Scene2D actions. I have an example here with asteroid-type thing falling down the screen and rotating.
http://www.netthreads.co.uk/2012/02/09/libgdx-scene2d-demo-with-scene-transitions/
To rotate anticlockwise and horizontally..
create a textureRegion
then
Sprite sprite = new Sprite(textureRegion, 0, 0, 128, 128);
sprite.setPosition(++mX, 0);
angle++;
sprite.setRotation(angle);
sprite.draw(batcher);
You can do it too like this:
on your create method
sprite.setOrigin(sprite.getWitdh() /2f, sprite.getHeight() /2f);
sprite.setPosition( 0, 200 ); //200 it's a example
on your render(float delta)
sprite.setX( sprite.getX() + delta ).setRotation( sprite.getRotation() + delta );
Here is a simple to rotate an actor in libgdx. First you need to set the origin:
img.setOrigin(getWidth/2,getHeight/2);
And then you can rotate clockwise and anticlockwise according to your need:
img.rotate(2f); or img.rotate(-2f);
So the following sample worked for me (infinite rotation)
Method 1: (recommended)
loadingActor.addAction(Actions.repeat(RepeatAction.FOREVER, Actions.rotateBy(360, 1)));
Method 2:
Image loadingActor = new Image(AssetsController.getInstance().getLoading());
loadingActor.setOrigin(Align.center);
final SequenceAction infiniteRotate = Actions.sequence();
infiniteRotate.addAction(Actions.rotateTo(0 , 0f) );
infiniteRotate.addAction(Actions.rotateTo(360 , 1f) );
loadingActor.addAction(Actions.forever(infiniteRotate));