thank you for reading my question.
The problem I have only occurs in android 7.0 (Nougat) and what it's happening is that the card (a fragment with Image views) when it rotates in the animation, it suddenly crops part of the image, Look:
Android 7 Nougat Screenshoot
Card in rotation
Android 4.4 Kitkat Screenshoot
Card in rotation
I'm using Animation (not objectAnimator yet), Here's my code:
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
// Angle around the y-axis of the rotation at the given time
// calculated both in radians and degrees.
final double radians = Math.PI * interpolatedTime;
float degrees = (float) (180.0 * radians / Math.PI);
Log.d(LOG_TAG," "+interpolatedTime);
// Once we reach the midpoint in the animation, we need to hide the
// source view and show the destination view. We also need to change
// the angle by 180 degrees so that the destination does not come in
// flipped around
if (interpolatedTime >= 0.5f) {
degrees -= 180.f;
fromView.setVisibility(View.GONE);
toView.setVisibility(View.VISIBLE);
}
if (forward)
degrees = -degrees; //determines direction of rotation when flip begins
//computing
final Matrix matrix = t.getMatrix();
camera.save();
camera.translate(0, 0, Math.abs(degrees) * 3f);
camera.rotateY(degrees);
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(-centerX, -centerY);
matrix.postTranslate(centerX, centerY);
}
Thank you so much for sharing your ideas. :)
Read the https://developer.android.com/reference/android/view/View.html#setCameraDistance(float)
You have to set the camera distance.
I know that this question is old, but I hope that will help somebody.
Related
I have an object which moves on a terrain and a third person camera follow it, after I move it for some distance in different directions it begin to shaking or vibrating even if it is not moving and the camera rotates around it, this is the moving code of the object
double& delta = engine.getDeltaTime();
GLfloat velocity = delta * movementSpeed;
glm::vec3 t(glm::vec3(0, 0, 1) * (velocity * 3.0f));
//translate the objet atri before rendering
matrix = glm::translate(matrix, t);
//get the forward vetor of the matrix
glm::vec3 f(matrix[2][0], matrix[2][1], matrix[2][2]);
f = glm::normalize(f);
f = f * (velocity * 3.0f);
f = -f;
camera.translate(f);
and the camera rotation is
void Camera::rotate(GLfloat xoffset, GLfloat yoffset, glm::vec3& c, double& delta, GLboolean constrainpitch) {
xoffset *= (delta * this->rotSpeed);
yoffset *= (delta * this->rotSpeed);
pitch += yoffset;
yaw += xoffset;
if (constrainpitch) {
if (pitch >= maxPitch) {
pitch = maxPitch;
yoffset = 0;
}
if (pitch <= minPitch) {
pitch = minPitch;
yoffset = 0;
}
}
glm::quat Qx(glm::angleAxis(glm::radians(yoffset), glm::vec3(1.0f, 0.0f, 0.0f)));
glm::quat Qy(glm::angleAxis(glm::radians(xoffset), glm::vec3(0.0f, 1.0f, 0.0f)));
glm::mat4 rotX = glm::mat4_cast(Qx);
glm::mat4 rotY = glm::mat4_cast(Qy);
view = glm::translate(view, c);
view = rotX * view;
view = view * rotY;
view = glm::translate(view, -c);
}
float is sometimes not enough.
I use double precision matrices on CPU side to avoid such problems. But as you are on Android it might not be possible. For GPU use floats again as there are no 64bit interpolators yet.
Big numbers are usually the problem
If your world is big then you are passing big numbers into the equations multiplying any errors and only at the final stage the stuff is translated relative to camera position meaning the errors stay multiplied but the numbers got clamped so error/data ratio got big.
To lower this problem before rendering convert all vertexes to coordinate system with origin at or near your camera. You can ignore rotations just offset the positions.
This way you will got higher errors only far away from camera which is with perspective not visible anyway... For more info see:
ray and ellipsoid intersection accuracy improvement
Use cumulative transform matrix instead of Euler angles
for more info see Understanding 4x4 homogenous transform matrices and all the links at bottom of that answer.
This sounds like a numerical effect to me. Even small offsets coming from your game object will influence the rotation of the following camera with small movements / rotations and it looks like a vibrating object / camera.
So what you can do is:
Check if the movement above a threshold value before calculating a new rotation for your camera
When you are above this threshold: do a linear interpolation between the old and the new rotation using the lerp-algorithm for the quaternion ( see this unity answer to get a better understanding how your code can look like: Unity lerp discussion )
According to official documents of Android on Matrix.
XXRotate(float degrees) --
process the matrix to rotate about (0,0) by the specified number of degrees.
XXRotate(float degrees, float px, float py) --
process the matrix to rotate by the specified number of degrees, with a pivot point at (px, py).
XX set, post or pre as per needed.
for example : XXRotate(90, center_point_x_of_view, center_point_y_of_view)
Does it take some translate regarding the pivot points while specifying some pivot points ?
what are the changes in origin coordinates points on views after first rotation?
As an example, let's take a square with a width and height of 10. The upper left corner is at the origin of (0,0) which puts the lower right corner at (10,10).
If we transform that square with matrix.setRotate(180F) we would expect that the origin point — being the pivot point — won't move, while the lower right corner is moved to (-10, -10).
Now let's say we transform the square with matrix.setRotate(180F, 5F, 5F). We have put the pivot point at the center of the square, so we expect that the origin moves to (10, 10) and the lower right corner moves to (0, 0).
So after you look at all the math, it turns out that
matrix.setRotate(theta, pivotX, pivotY);
is really just a shorter version of
matrix.setRotate(theta);
matrix.preTranslate(-pivotX, -pivotY);
matrix.postTranslate(pivotX, pivotY);
If you want to know the changes of the points, for the rotation the X changes by the cosine of the angle and the y changes by the sine of the angle.
So simple rotation:
float thetaR = theta * Math.PI / 180F; // convert degrees to radians
int x2 = Math.round(x * (float) Math.cos(thetaR));
int y2 = Math.round(y * (float) Math.sin(thetaR));
And putting it all together, you have
float thetaR = theta * Math.PI / 180F; // convert degrees to radians
int x2 = Math.round((x - pivotX) * (float) Math.cos(thetaR) + pivotX);
int y2 = Math.round((y - pivotY) * (float) Math.sin(thetaR) + pivotY);
I've played with the FlipImageView project and noticed one rather curious issue. When I'm running the sample contained within the project, everything works just fine. But, when I scale the FlipImageView manually (using View.setScaleX(float) and View.setScaleY(float) methods) this sample works fine just for devices with API version 16-18, and it clips the FlipImageView during the animation to the size of the unscaled image if sample is run on the device with the 19-th version of API. Here are screenshots:
The star is clipped, API 19
The star isn't clipped, API 18
I've set the visibility of all other widgets to GONE and changed the image's width to wrap_content to make an issue even more obvious. I see this behavior both on emulators and on real devices.
The code of the custom animation looks like so:
public class FlipAnimator extends Animation {
// fields declarations omitted
public void setToDrawable(Drawable to) {
toDrawable = to;
visibilitySwapped = false;
}
public FlipAnimator() {
setFillAfter(true);
}
#Override
public void initialize(int width, int height, int parentWidth, int parentHeight) {
super.initialize(width, height, parentWidth, parentHeight);
camera = new Camera();
this.centerX = width / 2;
this.centerY = height / 2;
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
final double radians = Math.PI * interpolatedTime;
float degrees = (float) (180.0 * radians / Math.PI);
if(mIsRotationReversed){
degrees = -degrees;
}
if (interpolatedTime >= 0.5f) {
if(mIsRotationReversed){ degrees += 180.f; } else{ degrees -= 180.f; }
if (!visibilitySwapped) {
setImageDrawable(toDrawable);
visibilitySwapped = true;
}
}
final Matrix matrix = t.getMatrix();
camera.save();
camera.translate(0.0f, 0.0f, (float) (150.0 * Math.sin(radians)));
camera.rotateX(mIsRotationXEnabled ? degrees : 0);
camera.rotateY(mIsRotationYEnabled ? degrees : 0);
camera.rotateZ(mIsRotationZEnabled ? degrees : 0);
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(-centerX, -centerY);
matrix.postTranslate(centerX, centerY);
}
}
I've searched through the diff between API version 18 and 19 and didn't find any changes in the Matrix or Camera classes which are used in the Animation.applyTransformation() method. I didn't find any changes in the Animation class itself neither.
What is the source of this problem?
Upd: the clipping occurs only during the animation - when it ends, the FlipImageView is drawn in its full size.
I was trying to make a rotational cube using translations and rotations. I'm using a classe named Rotate3dAnimation.java provided in the Google samples. The rotation is working pretty well and i was working about making the illusion of depth on the cube while rotating making it go further and closer from the screen.
To do this i was modifying the method applyTransformation(float interpolatedTime, Transformation t) but the problem is that, the face of the cube (the one hiding while rotating) has to a movement of going further for the first half of the time, come close again at the second half.
Resuming:
Camera.translate(float x, float y, float z)
The z translate should be decreasing for the first half and increasing the second half.
Here is the code i'm using:
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
final float fromDegrees = mFromDegrees;
float degrees = fromDegrees + ((mToDegrees - fromDegrees) * interpolatedTime);
final float centerX = mCenterX;
final float centerY = mCenterY;
final String orientation = mOrientation;
final Camera camera = mCamera;
final Matrix matrix = t.getMatrix();
camera.save();
float midTime = getDuration()/2;
if (mReverse) {
camera.translate(0.0f, 0.0f, (interpolatedTime<midTime)?
(mDepthZ * interpolatedTime): (mDepthZ * (1.0f - interpolatedTime)));
} else {
camera.translate(0.0f, 0.0f, (interpolatedTime<midTime)?
(mDepthZ * (1.0f - interpolatedTime)): (mDepthZ * interpolatedTime));
}
if(orientation.equals("horizontal"))
camera.rotateY(degrees);
else
camera.rotateX(degrees);
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(-centerX, -centerY);
matrix.postTranslate(centerX, centerY);
}
But i dont think this is working very well... Any sugestions?
Thanks!
I actually had the same problem and after some debugging, I realized that interpolatedTime is not in milliseconds as I (and I am guessing you) expected. This is nice so you don't really have to calculate what the midTime really is, you just need to know the progress of the interpolation.
To check if you're halfway through with the animation just use
(interpolatedTime < .5)
in your ternary statement.
I am planning to port the great 3D accordion effect found on iOS (https://github.com/xyfeng/XYOrigami) on Android.
so far I have something pretty close, here is a screenshot with "pinch-to-fold" to fold the view:
http://imageshack.us/photo/my-images/822/3daccordion.png
as you can see, it remains a space between 2 opposite "panels".
The process is simple:
take the canvas of the main Layout
create a Custom surface view which will replace all the view of the layout
set as background image the taken canvas
cut this canvas into X panels (here 16)
redraw the surfaceview with all the "panels" with a calculated matrix
recalculate the matrix while pinching to give the "folding effect"
here is the transformation matrix :
private Matrix getFoldingMatrix(int angle, int startFrom,int position) {
int move = (int) (
(mWidth - (Math.cos(angle * Math.PI / 180) * mWidth)) * position
) +1*position; //overlap of 1px the panels and not get space between panels
final Camera camera = new Camera();
final Matrix matrix = new Matrix();
camera.save();
camera.rotateY(angle);
camera.getMatrix(matrix);
if(position%2==1){
move += (int) (
(mWidth - (Math.cos(angle * Math.PI / 180) * mWidth))
) ;
matrix.preTranslate(-mWidth, -mHeight/2);
matrix.postTranslate(startFrom+mWidth-move, mHeight/2);
} else {
matrix.preTranslate(0f, -mHeight/2);
matrix.postTranslate(startFrom-move, mHeight/2);
}
camera.restore();
return matrix;
}
what am I getting wrong ? why is there still these spaces between opposite "panels"?