I am developing a game for Android using LibGDX. I have to objects in the game that need to be rotated. The objects are a board, and a tube. The problem that I am having is this, the tube piece consists of three pieces, the center piece, and the end pieces. The tube and the board can be stretched. Because they can be stretched the end pieces have to be separate graphics so that they do not become distorted from being stretched. I am having a really hard time figuring out how to do this properly. The position and rotation are retrieved from a Box2D body.
This is what the object looks like once constructed:
tube piece with end caps http://weaverhastings.com/tube.png
This is the end piece:
end cap for the tube http://weaverhastings.com/tube_endpiece.png
This is the piece that goes in the middle:
middle piece for the tube http://weaverhastings.com/tube_middle.png
From looking at it, it looks like the problem is the origin. As the object is stretched out, the origin for the rotation of the end pieces needs to change. But I cannot figure out how to calculate that origin correctly.
Here is the code that I am using right now:
// Right border
batch.draw(border, // AtlasRegion for the border
position.x, // The position as reported from box2d body
position.y,
25 + 150 * scale.x, // 25 is 2 x the end piece width, 150 is the width of the middle piece, it is multiplied by the scale because it can be stretched.
height/2, // This is just the height of the tube piece
border.getRegionWidth(), // the is the width of the border
height,
0.6f, // The scale of the borders is fixed
0.8f,
rotation * MathUtils.radiansToDegrees); // the rotation as retrieved from box2d body
// Left border
batch.draw(border,
position.x,
position.y,
25 - 150 * scale.x,
height/2,
border.getRegionWidth(),
height,
0.6f,
0.8f,
rotation * MathUtils.radiansToDegrees);
A video can be seen of the tube piece rotating here: http://youtu.be/RusL4Mnitds
Any help would be greatly appreciated. Thank you for reading this far.
In Libgdx the origin of rotation is relative to the objects position, eg, where you tell it to be drawn.
So for the left border you would need to say the originX would be half the width of the center piece (middlePiece.width/2) or whatever and for the right piece the originX would need to be negative this value(-middlePiece.width/2). Their positions of course would need to be at the ends of the centre piece when not rotated. The OriginY for each of them would be half the height of the Center piece. (middlePiece.height/2)
Hope this helps
Related
I'm trying to make a small circle move in another bigger circle as a ball moving in circle relative to Android phone tilting. I'm doing this in Godot but I think the principle is the same in all game engines probably. I make a scene in Godot and add two sprites as the two circles as the following picture. I get the accelerometer 3D vector, use x and y values and calculate the angle in x and y plate (relative to y axis i.e Vector2(0, 1) ) and rotate the mentioned scene to that degree using an animation, using this code
func _process(delta: float) -> void:
var vec3 = Input.get_accelerometer()
accelVec = Vector2(-stepify(vec3.x, 0.1), -stepify(vec3.y, 0.1))
var angle = accelVec.angle_to(Vector2(0, 1))
rotateTween.interpolate_property(self, "rotation", rotation, angle, 0.2,
Tween.TRANS_LINEAR)
rotateTween.start()
return
But the problem lies in here that when the x value of accelerometer 3D vector changes from a positive to negative value i.e when the ball is at top of the circle and is going to go to the other half of the circle, it actually moves from bottom of the circle to the desired point rather than from top of the circle.
I hope I explained the problem well, though I doubt it.
I added the Gif that shows actual test on an android phone here Testing in Android.
Any idea how to solve this problem?
Thanks in advance.
This is because Tween is interpolating linear values. It doesn't know it's working with angles, or that they should wrap around.
So when you're at -179 degrees and you tween to 179--a difference of 2 degrees--Tween just sees -179 -> 179 and goes through the whole circle.
You probably don't need a Tween here at all because _process() happens every frame.
I have a question regarding transformations in OpenGL ES 2. I'm currently drawing a rectangle using triangle fans as depicted in the image below. The origin is located in its center, while its width and height are 0.6 and 2 respectively. I assume that these sizes are related to the model space. However, in order to maintain the ratio of height and width on a tablet or phone one has to do a projection that considers the proportion of the device lengths (again width and height). This is why I call orthoM(projectionMatrix, 0, -aspectRatio, aspectRatio, -1f, 1f, -1f, 1f);and the aspectRatio is given by float aspectRatio = (float) width / (float) height. This finally leads to the rectangle shown in the image below. Now, I would like to move the rectangle along the x-axis to the border of the screen. However, I was not able to come up with the correct calculation to do so, either I moved it too little or too much. So how would the calculation look like? Furtermore, I'm a little bit confused about the sizes given in the model space. What are the max and min values that can be achieved there?
Thanks a lot!
Vertex position of the rectangle are in world space. A way to do this it could be get the screen coordinates you want to move to and then transform them into world space.
For example:
If the screen is 300 x 200 and you are in the center 0,0 in world space (or 150, 100) in screen space). You want to translate to 300.
So the transformation should be screen_position to normalized device coordiantes and then multiply by inverseOf(projection matrix * view matrix) and divided by the w component.
Here it is explained for mouse that it is finally the same, just that you know the z because it is the one you used for your rectangle already (if it is on the plane x,y): OpenGL Math - Projecting Screen space to World space coords.
I'm implementing 3d card flip animation for android (api > 14) and have an issue with big screen tablets (> 2048 dpi). During problem investigation i've come to the following basic block:
Tried to just transform a view (simple ImageView) using matrix and rotateY of camera by some angle and it works ok for angle < 60 and angle > 120 (transformed and displayed) but image disappears (just not displayed) when angle is between 60 and 120. Here is the code I use:
private void applyTransform(float degree)
{
float [] values = {1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f};
float centerX = image1.getMeasuredWidth() / 2.0f;
float centerY = image1.getMeasuredHeight() / 2.0f;
Matrix m = new Matrix();
m.setValues(values);
Camera camera = new Camera();
camera.save();
camera.rotateY(degree);
camera.getMatrix(m);
camera.restore();
m.preTranslate(-centerX, -centerY); // 1 draws fine without these 2 lines
m.postTranslate(centerX, centerY); // 2
image1.setImageMatrix(m);
}
And here is my layout XML
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="#+id/ImageView01"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="#drawable/naponer"
android:clickable="true"
android:scaleType="matrix">
</ImageView>
</FrameLayout>
So I have the following cases:
works fine for any angle, any center point if running on small screens 800X480, 1024x720, etc...
works ok for angle < 60 and > 120 when running on big screen devices 2048x1536, 2560x1600...
works ok for any angle on any device if rotation not centered (matrix pre and post translations commented out )
fails (image disappears) when running on big screen device, rotation centered and angle is between 60 and 120 degrees.
Please tell what I'm doing wrong and advise some workaround... thank you!!!
This problem is caused by the camera distance used to calculate the transformation. While the Camera class itself doesn't say much about the subject, it is better explained in the documentation for the View.setCameraDistance() method (emphasis mine):
Sets the distance along the Z axis (orthogonal to the X/Y plane on
which views are drawn) from the camera to this view. The camera's
distance affects 3D transformations, for instance rotations around the
X and Y axis. (...)
The distance of the camera from the view plane can have an affect on
the perspective distortion of the view when it is rotated around the x
or y axis. For example, a large distance will result in a large
viewing angle, and there will not be much perspective distortion of
the view as it rotates. A short distance may cause much more
perspective distortion upon rotation, and can also result in some
drawing artifacts if the rotated view ends up partially behind the
camera (which is why the recommendation is to use a distance at
least as far as the size of the view, if the view is to be rotated.)
To be honest, I hadn't seen this particular effect (not drawing at all) before, but I suspected it could be related to this question related to perspective distortion I'd encountered in the past. :)
Therefore, the solution is to use the Camera.setLocation() method to ensure this doesn't happen.
An important distinction with the View.setCameraDistance() method is that the units are not the same, since setLocation() doesn't use pixels. While setCameraDistance() adjusts for density, setLocation() does not. Therefore, if you wanted to calculate an appropriate z-distance based on the view's dimensions, remember to adjust for density. For example:
float cameraDistance = Math.max(image1.getMeasuredHeight(), image1.getMeasuredWidth()) * 5;
float densityDpi = getResources().getDisplayMetrics().densityDpi;
camera.setLocation(0, 0, -cameraDistance / densityDpi);
Instead of using 12 lines to create rotation matrix, you could just implement this one in first line http://en.wikipedia.org/wiki/Rotation_matrix
Depending of effect you want, you might want to center image to axis you want to rotate around.
http://en.wikipedia.org/wiki/Transformation_matrix
Hmm for image disappearing, I would guess it has something to do with either memory (out of memory - although this would bring exception) or rounding problems. Maybe you could try increasing precision to double precision?
One thing that comes to mind is that cos(alpha) goes toward 0 when alpha goes toward PI/2. Other than that I don's see any correlation between angles and why it doesn't work for big images.
You need to adjust your Translate coordinates. When calculating the translation for your image you need to take image size into account too. When you perform matrix calculations you set android:scaleType="matrix" for your ImageView. This aligns your image at the top left corner by default. Then, when you apply your pre/post translation, your image may get off the bounds of your ImageView (especially if the ImageView is relatively large and your image is relatively small, like in case of beeg screen tablets).
The following translation results in the image being rotated around its center Y axis and keeps the image aligned to the top left corner:
m.preTranslate(-imageWidth/2, 0);
m.postTranslate(imageWidth/2, 0);
The following alternative results in the image being rotated around its center Y/X axises and aligns the image to the center of the ImageView:
m.preTranslate(-imageWidth/2, -imageHeight/2);
m.postTranslate(centerX, centerY);
If your image is a bitmap you can use intrinsic width/height:
Drawable drawable = image1.getDrawable();
imageHeight = drawable.getIntrinsicHeight();
imageWidth = drawable.getIntrinsicWidth();
I'm using OpenGL ES 2.0 on Android and I and I initialise my display like so:
float ratio = (float) width / height;
Matrix.orthoM(mProjMatrix, 0, -ratio, ratio, -1, 1, 3, 7); //Using Orthographic as developing 2d
What I'm having trouble understanding is this:
Let's say my app is a 'fixed screen' game (like Pac-Man ie, no scrolling, just the whole game visible on the screen).
Now at the moment, if I draw a quad at -1 to +1 on both x and y I get something like this:
Obviously, this is because I am setting -ratio, ratio as seen above. So this is correct.
But am I supposed to use this as my 'whole' screen? With rather massive letterboxing on the left and right?
I want a rectangular display that is the whole height of the physical display (and as much of the width as possible), but this would mean drawing at less that -1 and more than +1, is this a problem?
I realise the option may be to use clipping if this was a scrolling game, but for this particular scenario I want the whole 'game board' on the screen and to be static (And to use as much of the available screen real estate as possible without 'stretching' thus causing elongation of my sprites).
As I like to work with 0,0 as the top of the screen, basically what I do is pass my draw method something like so:
quad1.drawQuad (10,0);
When the drawQuad method get's this, it basically takes the range from left to right as expressed my openGL and divide the the screen width (so, in my case -1.7 through +1.7 so 3.4/2560 = 0.001328125). And say I specify 10 as my X (as above), it will say something like:
-1.7 + (10*0.001328125) = -1.68671875
It then plots the quad at -1.68671875.
Doing this I am able to work with normal co-ords (and I just subtract rather than add for y axis so I can have 0 at the top).
Is this a good way to do things?
Because with this method, at the moment, if I specify a 100,100 square, it isn't a square, it's rectangle. However, on the plus side, I can fill the whole physical screen by scaling the quad by width x height.
You are drawing a 1x1 quad, so that is why you see a 1x1 quad. Try translating the quad 0.25 to the right or left and you will see that you can draw in that space too.
In graphics, you create an object, like a quad, in your case you made it 1x1. Then you position it wherever you want. If you do not position it, then it will be at the origin, which is what you see.
If you draw a wider shape, you will also see you can draw outside this area on the screen.
By the way, with your ortho matrix function, you aren't just specifying the screen aspect ratio, you are also specifying the coordinate unit size you have to work with. This is why a 1x1 is filling the height the of the screen, because your upper and lower boundaries are set to 1 and -1. Your ratio is a little more than one, since your width is longer than your height, so your left and right boundaries are essentially something like -1.5 and 1.5 (whatever your ratio happens to be).
But you can also do something like this;
Matrix.orthoM(mProjMatrix, 0, -width/2, width/2, -height/2, height/2, 3, 7);
Here, your ratio is the same, but you are sending it to your ortho projection with screen coordinates. (Disclaimer: I don't use the same math library you do, but these appears to be a conventional ortho matrix function based on the arguments you are passing to it).
So lets say you have a 1000x500 pixel resolution. In OpenGL your origin of 0,0 is in the middle. So now your left edge is at (-500,y), right edge at (500,y) and your top is (x,250). So if you draw your 1x1 quad, it will be very tiny, but if you draw a 250x250 square, it will look like your 1x1 quad in your previous ortho projection.
So you can specify the coordinates you want, the ratio, the unit size, etc for how you want to work. Personally, I dont't like specifying coordinates as fractions between 0 and 1, I like to think about them in the same sense as the screen pixels.
But whether or not you choose to do this, hopefully you understand what you are actually passing to these matrix functions.
One of the best ways to learn is draw an object to the screen and just play around with different numbers you send to your modelview and projection matrices so you can see what it is they are actually doing.
I have some sprites (Well, custom classes that implement Sprite, but whatever) that I resize. AndEngine resizes the image from the center, which makes an image placed at 0,0 no longer appear at 0,0. To fix this I applied
sprite.setScaleCenterX(0);
sprite.setScaleCenterY(0);
This places the image where I want it. However, now when I rotate the image, the image moves around (If the image were a plain square, rotating it should make no visible change). To fix this I applied
sprite.setRotationCenterX((sprite.getWidth() * sprite.getScaleX()) / 2);
sprite.setRotationCenterY((sprite.getHeight() * sprite.getScaleY()) / 2);
(For some reason, resizing a Sprite doesn't change the dimensions of the sprite, just the visual image, hence multiplying it by the scale). This, however, did not correct the problem, but merely changed where the image moved to when flipped.
Is my math off here? Wouldn't this center the rotation on the image so that the image doesn't move position? Or is there something else I'm missing?
Below is full code:
Sprite sprite = new Sprite(0, 0, singleTrackTR, getVertexBufferObjectManager());
sprite.setScale(scaleX, scaleY);
sprite.setScaleCenterX(0);
sprite.setScaleCenterY(0);
sprite.setRotationCenterX((sprite.getWidth() * sprite.getScaleX()) / 2);
sprite.setRotationCenterY((sprite.getHeight() * sprite.getScaleY()) / 2);
All your code is correct. I tried it myself, both the setProperty(x, y) and the setPropertyX/Y(a) versions.
By any chance, do you have it connected to a Body? Note that the Body also doesn't scale with a Sprite's setScale. It has its own setTransform method, which takes x and y (that you both have to divide by PhysicsConstants.PIXEL_TO_METER_RATIO_DEFAULT) and a rotation value.