Setting up an OpenGL ES renderer for 2D? - java

I'm trying to set up the renderer so that regardless of device, the view is a simple 2D field with the top of the screen at 1.0f and the bottom at -1.0f. I can't seem to get it quite right, I've been using the below method in the onSurfaceChanged() method and playing with the parameters in gluPerspective to achieve the desired effect, but it seems impossible to make perfect. Surely there is an alternative way to go about this to achieve what i'm after. I've also been playing with the Z values of the meshes drawn to try to get them to match.
Again i'm trying to set it up so that the screen is defined in the range -1.0f to 1.0, so that if you drew a square with sides equal to 2.0f it would fill the entire screen regardless of aspect ratio. What do I need to change to do this? (include the value I should use for the Z dimension of the mesh vertices)
(Don't be alarmed by the strange parameters in gluperspective(), I've been tinkering to see what happens.)
#Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
if(height == 0) { //Prevent A Divide By Zero By
height = 1; //Making Height Equal One
}
gl.glViewport(0, 0, width, height);
gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glLoadIdentity();
GLU.gluPerspective(gl, 90.0f, (float) width / (float) height,
0.0000001f, 100.0f);
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
}

Generate a Ortho Matrix instead:
Matrix.orthoM(projectionMatrix,0,-yourdisplayWidth/2,+yourdisplayWidth/2,-yourdisplayHeight/2,+yourdisplayHeight/2,0f,2f);
So you can place your image-quads in distance of 1f in front of your camera. You also have to size your quads as big as they are in pixels. This way you can render pixelperfect.
See also: https://github.com/Chrise55/Llama3D

You might want to try experimenting with using glOrtho or glFrustum instead of glPerspective

Related

libGDX Draw viewport only partly while cutting off the rest

This problem seemed very obvious for me to solve, but whatever I try, it doesn't work. What I'm trying to do is to incorporate a mini-version of my PlayScreen in a ScrollPane as a tutorial where you can read text and try it out immediately.
Because I didn't find any better solution to add this to the Table inside the ScrollPane, I edited the draw() method of the PlayScreen to take the ScrollPane.getScrollPercentY() and offset the camera of the PlayScreen accordingly.
What I want to do now is to only render only part of the viewport that would be normally visible in the real game. Subsequently, I want to be able to control the size and position of this "window".
I also want to be able to resize and move the content, while cutting off the edges that are not visible to the camera. This is what I tried inside the PlayScreenDraw:
public void draw(final float yOffset,
final int xTiles,
final int yTiles) {
view.getCamera().position.y = yTiles / 2f - yOffset * yTiles / HEIGHT; // HEIGHT = 800
view.getCamera().position.x = xTiles / 2f;
view.setWorldSize(xTiles, yTiles); //Do i even need to change the world size?
b.setProjectionMatrix(view.getCamera().combined);
b.begin();
...
b.end();
view.update(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
}
What this gives me, in terms of the picture above, is this
How do I need to change the viewport and/or the camera? Btw., this is how i set the two up:
cam = new OrthographicCamera();
cam.setToOrtho(false, WIDTH, HEIGHT); // WIDTH = 8, HEIGHT = 16
batch.setProjectionMatrix(cam.combined);
view = new FitViewport(WIDTH, HEIGHT, cam);
The Pixmap class can help you achieve what you want since you stated that you wanted to "cut off" the parts outside of the green selection box.
You need to render what the camera sees to an FBO and then get the pixmap from the FBO itself.
Framebuffer Objects are OpenGL Objects, which allow for the creation of user-defined Framebuffers. With them, one can render to non-Default Framebuffer locations, and thus render without disturbing the main screen.
-- OpenGL wiki
// Construct an FBO and keep a reference to it. Remember to dispose of it.
FrameBuffer fbo = new FrameBuffer(Format.RGBA8888, width, height, false);
public void render() {
//Start rendering to the fbo.
fbo.begin();
//From the camera's perspective.
batch.setProjectionMatrix(camera.combined);
batch.begin();
//Draw whatever you want to draw with the camera.
batch.end();
// Finished drawing, get pixmap.
Pixmap pixmap = ScreenUtils.getFrameBufferPixmap(0, 0, width, height);
//Stop drawing to your fbo.
fbo.end();
}
After getting the pixmap you can iterate through the pixels and set the alpha of the pixels outside your green selection window to 0 making them invisible or "cutting them off"

Libgdx Box2D pixel to meter conversion?

When trying to program a game using Box2D, I ran into a problem with Box2D. I filled in pixel numbers for the lengths of the the textures and sprites to create a box around it. Everything was at the right place, but for some reason everything went very slowly. By looking on the internet I found out that if you didn't convert pixels to meters box2d might handle shapes as very large objects. this seemed to be a logical cause of everything moving slowly.
I found similar questions on this site, but the answers didn't really seem to help out. in most of the cases the solution was to make methods to convert the pixel numbers to meters using a scaling factor. I tried this out, but everything got misplaced and had wrong sizes. this seemed logical to me since the numbers where changed but had the same meaning.
I was wondering if there is a way to make the pixels mean less meters, so everything whould be at the same place with the same (pixel) size, but mean less meters.
If you have a different way which you think might help, I whould also like to hear it..
Here is the code i use to create the camera
width = Gdx.graphics.getWidth() / 5;
height = Gdx.graphics.getHeight() / 5;
camera = new OrthographicCamera(width, height);
camera.setToOrtho(false, 1628, 440);
camera.update();
This is the method I use to create an object:
public void Create(float X, float Y, float Width, float Height, float density, float friction, float restitution, World world){
//Method to create an item
width = Width;
height = Height;
polygonDef = new BodyDef();
polygonDef.type = BodyType.DynamicBody;
polygonDef.position.set(X + (Width / 2f), Y + (Height / 2f));
polygonBody = world.createBody(polygonDef);
polygonShape = new PolygonShape();
polygonShape.setAsBox(Width / 2f, Height / 2f);
polygonFixture = new FixtureDef();
polygonFixture.shape = polygonShape;
polygonFixture.density = density;
polygonFixture.friction = friction;
polygonFixture.restitution = restitution;
polygonBody.createFixture(polygonFixture);
}
To create an item, in this case a table, I use the following:
Table = new Item();
Table.Create(372f, 60f, 152f, 96f, 1.0f, 0.2f, 0.2f, world);
The Sprites are drawn on the item by using the following method:
public void drawSprite(Sprite sprite){
polygonBody.setUserData(sprite);
Utils.batch.begin();
if(polygonBody.getUserData() instanceof Sprite){
Sprite Sprite = (Sprite) polygonBody.getUserData();
Sprite.setPosition(polygonBody.getPosition().x - Sprite.getWidth() / 2, polygonBody.getPosition().y - Sprite.getHeight() / 2);
Sprite.setRotation(polygonBody.getAngle() * MathUtils.radiansToDegrees);
Sprite.draw(Utils.batch);
}
Utils.batch.end();
}
The sprites also have pixel sizes.
Using this methods it displays the images at the right places, but everything moves slowly.
I was wondering how or if I whould have to change this to make the objects move correctly, and / or mean less. Thanks in advance.
Box2D is an entirely independent of the graphics library that you use. It doesn't have any notion of sprites and textures. What you read online is correct, you'll have to convert pixels to metres, as Box2D works with metres(the standard unit for distance).
For example, if you drew a sprite of size 100x100 pixels, that's the size of the sprite that you want the user to see on the screen. In real world the size of the object should be in metres and not in pixels - so if you say 1px = 1m, then that'll map the sprite to a gigantic 100x100 meter object. In Box2D, large world objects will slow down calculations. So what you need to do is map the 100 pixels to a smaller number of meters, say, 1 meter - thus 100x100px sprite will be represented in Box2D world by a 1x1 meter object.
Box2D doesn't work well with very small numbers and very large numbers. So keep it in between, say between 0.5 and 100, to have good performance.
EDIT:
Ok. Now I get your question.
Don't code to pixels. Its as simple as that. I know it'll take some time to understand this(it took for me). But once you get the hang of it, its straight forward.
Instead of pixels, use a unit, say, you call it meter.
So we decide our viewport should be say 6mx5m.
So initialization is
Constants.VIEWPORT_WIDTH = 6;
Constants.VIEWPORT_HEIGHT = 5;
...
void init() {
camera = new OrthographicCamera(Constants.VIEWPORT_WIDTH, Constants.VIEWPORT_HEIGHT);
camera.position.set(Constants.VIEWPORT_WIDTH/2, Constants.VIEWPORT_HEIGHT/2, 0);
camera.update();
}
Once you know the actual width and height, you call the following function in order to maintain aspect ratio:
public void resize(int width, int height) {
camera.viewportHeight = (Constants.VIEWPORT_WIDTH / width) * height;
camera.update();
}
resize() can be called anytime you change your screen size(eg: when you screen orientation changes). resize() takes the actual width and height (320x480 etc), which is the pixel value.
Now you specify you sprite sizes, their positions etc. in this new world of size 6x5. You can forget pixels. The minimum size of the sprite that'll fill the screen will be 6x5.
You can now use the same unit with Box2D. Since the new dimensions will be smaller, it won't be a problem for Box2D. If I remember correctly Box2D doesn't have any unit. We just call it meter for convenience sake.
Now you might ask where you specify the dimensions of the window. It depends on the platform. Following code shows a 320x480 windowed desktop game:
public class Main {
public static void main(String[] args) {
LwjglApplicationConfiguration cfg = new LwjglApplicationConfiguration();
cfg.title = "my-game";
cfg.useGL20 = false;
cfg.width = 480;
cfg.height = 320;
new LwjglApplication(new MyGame(), cfg);
}
}
Our camera will intelligently map the 6x5 viewport to 480x320.

LibGdx How to repeat background?

Few days ago I figured out how to do some scrolling in LibGdx. Now I'm triying to do something related. I want to repeat the background. My scrolling follows a ship (Is an s[ace ship game). In the background there is a space photo loaded as a Texture. When the ship reach the end of the backgorund, It keeps going and there's no background anymore. I have read about wrap but I don't really understand How It works. I did that:
px=new Pixmap(Gdx.files.internal("fondo.jpg"));
background=new Texture(px);
background.setWrap(TextureWrap.Repeat, TextureWrap.Repeat);
And then, in my render method
spriteBatch.begin();
spriteBatch.draw(background,0,0,500,50);
drawShip();
spriteBatch.end();
Of course It doesn't work, It only draws the background once. I don't know how make this wrap method work. Any help?
SOLUTION
I figured It out. It's not a nice code but It works.
First I declare two Textures with the same image
bck1=new Texture(Gdx.files.internal("fondo.jpg"));
bck2=new Texture(Gdx.files.internal("fondo.jpg"));
Also I declare two variables like this to specify the X value of the position of each bck
int posXBck1=0,posXBck2=0;
Then I use that in Render()
public void calculoPosicionFondos(){
posXBck2=posXBck1+ANCHODEFONDO;
if(cam.position.x>=posXBck2+cam.viewportWidth/2){
posXBck1=posXBck2;
}
}
Where:
ANCHODEFONDO is the width of my background
Cam is an OtrhoCam.
So I said that if the cam is in bck2 (wich means that you can't see bck1 anymore) It change positions, giving bck1 de position of bck2 and, in the next render loop, recalculating bck2
Then just paint both bck in your render mode.
Like Teitus said, do not load your texture multiple times, ever! Anyway, you where on the right track with the wrapper:
texture.setWrap(TextureWrap.Repeat, TextureWrap.Repeat);
Now you can just use the draw method with the source location. The source location is the area you choose to draw on the texture.
batch.draw(texture, x, y, srcX, srcY, srcWidth, srcHeight)
To scroll your texture from right to left all you have to do is increase srcX incrementally. So create a int that increments in the update/render method.
int sourceX = 0;
//render() method
//Increment the variable where to draw from on the image.
sourceX += 10;
//Simply draw it using that variable in the srcX.
batch.draw(YourTexture, 0, 0, sourceX, 0, screenWidth, screenHeight);
Because you are wrapping the texture it will wrap/loop and scroll indefinitely. There might be a issue with the sourceX int if the game runs for a very long time because a int can only hold 2147483647. It takes a while but you can fix it by subtracting the image width each time the number goes over the total image width.
Don't to this, please:
bck1=new Texture(Gdx.files.internal("fondo.jpg"));
bck2=new Texture(Gdx.files.internal("fondo.jpg"));
That will load your big background texture twice. That's a complete waste. If you want to keep your solution at least do:
bck1=new Texture(Gdx.files.internal("fondo.jpg"));
bck2=bkg1;
Regarding the texture Wrapping. If your texture is 500px wide, and you draw a 500px sprite, you won't see any repetition. If you want it repeated 2 times, draw it 1000px wide with 0-2 texture coordinates.
I'm not sure how spriteBatch handles the call you posted, you could try that one, or may be use the overload that uses a texture region and set your region manually.
I see this is a pretty old question, but I think there is an easier way to accomplish background scrolling. Just use the Sprite class. Here is a snippet I use for layered background images that scroll from right to left.
public class LevelLayer
{
public float speedScalar = 1;
private List<Sprite> backgroundSprites = new ArrayList<Sprite>();
public LevelLayer()
{
}
public void addSpriteLayer(Texture texture, float startingPointX, float y, int repeats)
{
for (int k = 0; k < repeats; k++)
{
Sprite s = new Sprite(texture);
s.setX(startingPointX + (k*texture.getWidth()));
s.setY(y);
backgroundSprites.add(s);
}
}
public void render(SpriteBatch spriteBatch, float speed)
{
for (Sprite s : backgroundSprites)
{
float delta = s.getX() - (speed * speedScalar);
s.setX(delta);
s.draw(spriteBatch);
}
}
}
Then you can use the same texture or series of textures like so:
someLayer.addSpriteLayer(sideWalkTexture1, 0, 0, 15);
someLayer.addSpriteLayer(sideWalkTexture2, 15 * sideWalkTexture1.getWidth(), 0, 7);
I change background repeating sections randomly in code and make new ones or reset existing sets when they go off screen. All the layers go to a pool and get pulled randomly when a new one is needed.
SOLUTION
I figured It out. It's not a nice code but It works.
First I declare two Textures with the same image
bck1=new Texture(Gdx.files.internal("fondo.jpg"));
bck2=new Texture(Gdx.files.internal("fondo.jpg"));
Also I declare two variables like this to specify the X value of the position of each bck
int posXBck1=0,posXBck2=0;
Then I use that in Render()
public void calculoPosicionFondos(){
posXBck2=posXBck1+ANCHODEFONDO;
if(cam.position.x>=posXBck2+cam.viewportWidth/2){
posXBck1=posXBck2;
}
}
Where:
ANCHODEFONDO is the width of my background
Cam is an OtrhoCam.
So I said that if the cam is in bck2 (wich means that you can't see bck1 anymore) It change positions, giving bck1 de position of bck2 and, in the next render loop, recalculating bck2
Then just draw both bck in your render()

How to scale text on a texture (quad object)?

I have found a good way to draw text in openGL by creating a Bitmap, drawing text on it, and making it into a texture.
I have screenCoordinates(320,~~480) and gameCoordinates(20,28), in which quads' dimensions are defined. Example constructor: GameObject(x, y, width, height, text).
To create the bitmap I create a Canvas with this size:
// get width and height in px (p is a Paint() object)
p.setTextSize(textSize * Main.getMainResources().getDisplayMetrics().density);
final int width = (int) p.measureText(text);
final int height = (int) (p.descent() + -p.ascent());
canvas.drawText(text, 0, -p.ascent(), p);
Create a bitmap of this, scaled to the next power of 2, and you have your texture-bitmap.
However the problem is that because the quad's dimensions are not adapted to the bitmap(text) dimensions, the text isn't very smooth. I have the possibility to change the quad's dimensions with quad.setDimensions(width, height), but what would be the size I'd have to give it, and also shouldn't I also have to worry about the textSize(sp) of the text i'm drawing?
This all is in 2D, and some simple math's might get involved, but how should I get the right dimensions?

Android Bitmap/Canvas offset after scale

If I have a canvas, on which I draw a Bitmap like this:
canvas.drawBitmap(bmLargeImage, srcRect, destRect, paint);
and I scale the bitmap:
canvas.scale(1.5f, 1.5f, 450, 250);
I want to get the position of the Bitmap after the scale. If the position before scale was (0, 0), after scale there is a offset and I need that offset.. how can I get it?
Thanks and sorry for the simple question, newbie here...
Ok lets try to work out the best formula for this
canvas.scale(scaleX, scaleY, pivotX, pivotY);
if (scaleX >= 1){
objectNewX = objectOldX + (objectOldX - pivotX)*(scaleX - 1);
}else{
objectNewX = objectOldX - (objectOldX - pivotX)*(1 - scaleX);
}
The same for objectNewY. The new width and height of the bitmap would of course be the multiple of the old size and scale.
I believe the cleanest Solution would be to use the underlying transformation Matrix of the Canvas you are manipulating.
In Android there is the canvas.getMatrix(Matrix cmt) method available which will yield it. The transformation matrix will transform any point in world space you throw at into screen coordinates. Just use the matrix.mapPoints(float[] points) and you will be fine.
FYI, you can easily do it the other way around too. If you want to know what screen coordinate maps to which point in world space, e.g. for tapping; the inverse matrix can be used for that. It can be obtained via the matrix.invert(Matrix out) method. Use its mapPoints() for the coordinate mapping then.
Here are the official docs:
mapPoints(), invert(), getMatrix()
If you'd like know the corners of your screen relative to your original canvas, you can use canvas.getClipBounds(). This returns a Rect with edge coordinates relative to your original canvas. For instance, if you start off with a canvas size of 320 x 480 and call
canvas.scale(2, 2, getWidth()/2, getHeight()/2);
and then
canvas.getClipBounds();
you will have a Rect (call this rect) where
rect.top == 120
rect.bottom == 360
rect.left == 80
rect.right == 240

Categories

Resources