Binding Video Texture on OpenGL Sphere on Android - java

I'm trying to create a 360 video sphere (like the ones for cardboard) on Android. I have done this with a photo by rendering a sphere in OpenGL ES1.0 and than attaching a texture to it. Afterwards I can use the sensor values to rotate the sphere.
However, I can't figure out how to change the picture to a video. I've tried frame by frame rendering using texSubImage2D() but it's SUPER SLOW. My video is probably going to be about 4k density as I need a good quality even when only small portion of it is shown.
I've read some theoretical stuff about how this should be done (i.e. Frame Buffers, External Texture, Synchronization, etc.) but I couldn't find any example for these things, so some code would be EXTREMELY appreciated...
Here is how I render the Sphere, draw it and attach a texture to it (i.e. my Sphere class)...
import rapid.decoder.BitmapDecoder;
public class Sphere {
/** Buffer holding the vertices. */
private final List<FloatBuffer> mVertexBuffer = new ArrayList<FloatBuffer>();
/** The vertices for the sphere. */
private final List<float[]> mVertices = new ArrayList<float[]>();
/** Buffer holding the texture coordinates. */
private final List<FloatBuffer> mTextureBuffer = new ArrayList<FloatBuffer>();
/** Mapping texture coordinates for the vertices. */
private final List<float[]> mTexture = new ArrayList<float[]>();
/** The texture pointer. */
private final int[] mTextures = new int[1];
/** Total number of strips for the given depth. */
private final int mTotalNumStrips;
public Sphere(final int depth, final float radius) {
// Calculate basic values for the sphere.
this.mTotalNumStrips = Maths.power(2, depth - 1) * 5; //last 5 is related to properties of a icosahedron
final int numVerticesPerStrip = Maths.power(2, depth) * 3;
final double altitudeStepAngle = Maths.rad120 / Maths.power(2, depth);
final double azimuthStepAngle = Maths.rad360 / this.mTotalNumStrips;
double x, y, z, h, altitude, azimuth;
Log.e("mTotalNumStrips", ""+mTotalNumStrips);
Log.e("numVerticesPerStrip", ""+numVerticesPerStrip);
for (int stripNum = 0; stripNum < this.mTotalNumStrips; stripNum++) {
// Setup arrays to hold the points for this strip.
final float[] vertices = new float[numVerticesPerStrip * 3]; // x,y,z
final float[] texturePoints = new float[numVerticesPerStrip * 2]; // 2d texture
int vertexPos = 0;
int texturePos = 0;
// Calculate position of the first vertex in this strip.
altitude = Maths.rad90;
azimuth = stripNum * azimuthStepAngle;
// Draw the rest of this strip.
for (int vertexNum = 0; vertexNum < numVerticesPerStrip; vertexNum += 2) {
// First point - Vertex.
y = radius * Math.sin(altitude);
h = radius * Math.cos(altitude);
z = h * Math.sin(azimuth);
x = h * Math.cos(azimuth);
vertices[vertexPos++] = (float) x;
vertices[vertexPos++] = (float) y;
vertices[vertexPos++] = (float) z;
// First point - Texture.
texturePoints[texturePos++] = (float) (1 + azimuth / Maths.rad360);
texturePoints[texturePos++] = (float) (1 - (altitude + Maths.rad90) / Maths.rad180);
// Second point - Vertex.
altitude -= altitudeStepAngle;
azimuth -= azimuthStepAngle / 2.0;
y = radius * Math.sin(altitude);
h = radius * Math.cos(altitude);
z = h * Math.sin(azimuth);
x = h * Math.cos(azimuth);
vertices[vertexPos++] = (float) x;
vertices[vertexPos++] = (float) y;
vertices[vertexPos++] = (float) z;
// Second point - Texture.
texturePoints[texturePos++] = (float) (1 + azimuth / Maths.rad360);
texturePoints[texturePos++] = (float) (1 - (altitude + Maths.rad90) / Maths.rad180);
azimuth += azimuthStepAngle;
}
this.mVertices.add(vertices);
this.mTexture.add(texturePoints);
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(numVerticesPerStrip * 3 * Float.SIZE);
byteBuffer.order(ByteOrder.nativeOrder());
FloatBuffer fb = byteBuffer.asFloatBuffer();
fb.put(this.mVertices.get(stripNum));
fb.position(0);
this.mVertexBuffer.add(fb);
// Setup texture.
byteBuffer = ByteBuffer.allocateDirect(numVerticesPerStrip * 2 * Float.SIZE);
byteBuffer.order(ByteOrder.nativeOrder());
fb = byteBuffer.asFloatBuffer();
fb.put(this.mTexture.get(stripNum));
fb.position(0);
this.mTextureBuffer.add(fb);
}
}
public void loadGLTexture(final GL10 gl, final Context context, final int texture) {
Bitmap bitmap = BitmapDecoder.from(context.getResources(), texture)
.scale(4048, 2024)
.decode();
// Generate one texture pointer, and bind it to the texture array.
gl.glGenTextures(1, this.mTextures, 0);
gl.glBindTexture(GL10.GL_TEXTURE_2D, this.mTextures[0]);
// Create nearest filtered texture.
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
// Use Android GLUtils to specify a two-dimensional texture image from our bitmap.
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
// Tide up.
bitmap.recycle();
}
/**
* The draw method for the square with the GL context.
*
* #param gl Graphics handle.
*/
public void draw(final GL10 gl) {
// bind the previously generated texture.
gl.glBindTexture(GL10.GL_TEXTURE_2D, this.mTextures[0]);
// Point to our buffers.
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
// Set the face rotation, clockwise in this case.
gl.glFrontFace(GL10.GL_CW);
// Point to our vertex buffer.
for (int i = 0; i < this.mTotalNumStrips; i++) {
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, this.mVertexBuffer.get(i));
gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, this.mTextureBuffer.get(i));
// Draw the vertices as triangle strip.
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, this.mVertices.get(i).length / 3);
}
// Disable the client state before leaving.
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
}
}
And this is my renderer...
#Override
public void onDrawFrame(final GL10 gl) {
zvector = new float[] {0,0,1,0};
resultvector = new float[] {0,0,1,0};
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
gl.glLoadIdentity();
float radiansX = (float) Math.toRadians(gyro_angle[1]);
float radiansY = (float) Math.toRadians(-gyro_angle[0]);
float radiansZ = (float) Math.toRadians(-gyro_angle[2]);
// Finds the Sin and Cosin for the half angle.
float sinX =(float) Math.sin(radiansX * 0.5);
float cosX =(float) Math.cos(radiansX * 0.5);
float sinY =(float) Math.sin(radiansY * 0.5);
float cosY =(float) Math.cos(radiansY * 0.5);
float sinZ =(float) Math.sin(radiansZ * 0.5);
float cosZ =(float) Math.cos(radiansZ * 0.5);
// Formula to construct a new Quaternion based on direction and angle.
quatX[0] = cosX;
quatX[1] = 1 * sinX;
quatX[2] = 0 * sinX;
quatX[3] = 0 * sinX;
quatY[0] = cosY;
quatY[1] = 0 * sinY;
quatY[2] = 1 * sinY;
quatY[3] = 0 * sinY;
quatZ[0] = cosZ;
quatZ[1] = 0 * sinZ;
quatZ[2] = 0 * sinZ;
quatZ[3] = 1 * sinZ;
quat1 = multiplyQuat(quatX, quatY);
quat2 = multiplyQuat(quat1, quatZ);
mMatrix = getMatrixfromQuat(quat1);
gl.glLoadMatrixf(mMatrix, 0);
this.mSphere.draw(gl);
}
#Override
public void onSurfaceChanged(final GL10 gl, final int width, final int height) {
final float aspectRatio = (float) width / (float) (height == 0 ? 1 : height);
gl.glViewport(0, 0, width, height);
gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glLoadIdentity();
GLU.gluPerspective(gl, 45.0f, aspectRatio, 0.1f, 100.0f);
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
}
#Override
public void onSurfaceCreated(final GL10 gl, final EGLConfig config) {
this.mSphere.loadGLTexture(gl, this.mContext, R.drawable.pic360);
gl.glEnable(GL10.GL_TEXTURE_2D);
gl.glShadeModel(GL10.GL_SMOOTH);
gl.glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
gl.glClearDepthf(1.0f);
gl.glEnable(GL10.GL_DEPTH_TEST);
gl.glDepthFunc(GL10.GL_LEQUAL);
gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST);
}
//CONSTRUCTER
public GlRenderer(final Context context) {
this.mContext = context;
this.mSphere = new Sphere(5, 2);
sensorManager = (SensorManager) this.mContext.getSystemService(this.mContext.SENSOR_SERVICE);
sensorGyroscope = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
sensorAccelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
sensorMagneticField = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
valuesAccelerometer = new float[3];
valuesMagneticField = new float[3];
matrixR = new float[9];
matrixI = new float[9];
matrixValues = new float[3];
sensorManager.registerListener(this, sensorGyroscope, SensorManager.SENSOR_DELAY_FASTEST);
sensorManager.registerListener(this, sensorAccelerometer, SensorManager.SENSOR_DELAY_FASTEST);
sensorManager.registerListener(this, sensorMagneticField, SensorManager.SENSOR_DELAY_FASTEST);
}
//HERE GOES SOME CURRENTLY IRRELEVANT STUFF ABOUT THE SENSORS AND QUATERNIONS

I had some this type of video texturing problem. First time I used ffmpeg for video decoding but the performance was so poor (just like you- Extracting frame by frame). For improving performance I used android default mediaplayer. You can use surface texture to create an opengl surface (sphere, cylinder, cube etc...) and then set the surface in the media player
Surface surface = new Surface(mSurface);//mSurface is your surface texture
mMediaPlayer.setSurface(surface);
mMediaPlayer.setScreenOnWhilePlaying(true);
This is just a technique. I did this for some commercial enclosed project, so I cant share the code. I hope I'll published a free code in github soon.

Related

How do I change my OpenGL object color only when I touch it?

I am trying to change my OpenGL square's color only when it is touched. I looked around online at some good sources to see how I could find the coordinates to change its color, Converting pixel co-ordinates to normalized co-ordinates at draw time in OpenGL 3.0. However, I am still confused about how to get my square's or onTouchEvent inputs coordinates to be translated in OpenGL code(vertexShaderCode). I have tried to directly track my square coordinates in the onTouchEvent activity, but it wrongly tracks the position since I am working with two different coordinate systems(OpenGl, Android Studios).
//THIS IS NOT MY FULL CODE
public boolean onTouchEvent(MotionEvent e) {
// MotionEvent reports input details from the touch screen
// and other input controls. In this case, you are only
// interested in events where the touch position changed.
float x = e.getX();
float y = e.getY();
colorHolder = renderer.getmSquare().getColor();
switch (e.getAction()) {
case MotionEvent.ACTION_DOWN:
//THIS IS MY PROBLEM. I DON'T KNOW A GOOD WAY OF TRACKING THE SQUARE'S POSITION BESIDES
//ADDING VARIBLE TO IT'S MAIN CLASS THEN REFERENCING THEM HERE
if(renderer.mSquareY > (y / getHeight()) && renderer.mSquareX > (x / getWidth()))
renderer.getmSquare().color = tempColor;
case MotionEvent.ACTION_MOVE:
float dx = x - previousX;
float dy = y - previousY;
float tempHeight = y / getHeight();
float tempWidth = x / getWidth();
//THIS IS MY PROBLEM. I DON'T KNOW A GOOD WAY OF TRACKING THE SQUARE'S POSITION BESIDES ADDING VARIBLE TO IT'S MAIN CLASS THEN REFERENCING THEM HERE
if(renderer.mSquareY < (y / getHeight()) && renderer.mSquareX < (x / getWidth()))
renderer.getmSquare().color = tempColor;
renderer.mSquareX = (x / getWidth());
renderer.mSquareY = (y / getHeight());
...
I have three classes that handle creating the square, handles rendering, and the main activity in the corresponding order: Square.java, MyGLRenderer.java, MyGLSurfaceView.java.
public class Square {
private final String vertexShaderCode =
// This matrix member variable provides a hook to manipulate
// the coordinates of the objects that use this vertex shader
"uniform mat4 uMVPMatrix;" +
"attribute vec4 vPosition;" +
"void main() {" +
// The matrix must be included as a modifier of gl_Position.
// Note that the uMVPMatrix factor *must be first* in order
// for the matrix multiplication product to be correct.
" gl_Position = uMVPMatrix * vPosition;" +
"}";
private final String fragmentShaderCode =
"precision mediump float;" +
"uniform vec4 vColor;" +
"void main() {" +
" gl_FragColor = vColor;" +
"}";
private FloatBuffer vertexBuffer;
private ShortBuffer drawListBuffer;
private final int mProgram;
private int mPositionHandle;
private int mColorHandle;
private int mMVPMatrixHandle;
// number of coordinates per vertex in this array
static final int COORDS_PER_VERTEX = 3;
static float squareCoords[] = {
0.5f, 0.5f, 0.0f, // top left
0.5f, -0.5f, 0.0f, // bottom left
-0.5f, -0.5f, 0.0f, // bottom right
-0.5f, 0.5f, 0.0f }; // top right
private short drawOrder[] = { 0, 1, 2, 0, 2, 3 }; // order to draw vertices
private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex
public float[] getColor() {
return color;
}
public void setColor(float[] color) {
this.color = color;
}
float color[] = { 0.2f, 0.709803922f, 0.898039216f, 1.0f };
public Square() {
// initialize vertex byte buffer for shape coordinates
ByteBuffer bb = ByteBuffer.allocateDirect(
// (# of coordinate values * 4 bytes per float)
squareCoords.length * 4);
bb.order(ByteOrder.nativeOrder());
vertexBuffer = bb.asFloatBuffer();
vertexBuffer.put(squareCoords);
vertexBuffer.position(0);
// initialize byte buffer for the draw list
ByteBuffer dlb = ByteBuffer.allocateDirect(
// (# of coordinate values * 2 bytes per short)
drawOrder.length * 2);
dlb.order(ByteOrder.nativeOrder());
drawListBuffer = dlb.asShortBuffer();
drawListBuffer.put(drawOrder);
drawListBuffer.position(0);
// prepare shaders and OpenGL program
int vertexShader = MyGLRenderer.loadShader(
GLES20.GL_VERTEX_SHADER,
vertexShaderCode);
int fragmentShader = MyGLRenderer.loadShader(
GLES20.GL_FRAGMENT_SHADER,
fragmentShaderCode);
mProgram = GLES20.glCreateProgram(); // create empty OpenGL Program
GLES20.glAttachShader(mProgram, vertexShader); // add the vertex shader to program
GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment shader to program
GLES20.glLinkProgram(mProgram); // create OpenGL program executables
}
public void draw(float[] mvpMatrix) {
// Add program to OpenGL environment
GLES20.glUseProgram(mProgram);
// get handle to vertex shader's vPosition member
mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
// Enable a handle to the triangle vertices
GLES20.glEnableVertexAttribArray(mPositionHandle);
// Prepare the triangle coordinate data
GLES20.glVertexAttribPointer(
mPositionHandle, COORDS_PER_VERTEX,
GLES20.GL_FLOAT, false,
vertexStride, vertexBuffer);
// get handle to fragment shader's vColor member
mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
// Set color for drawing the triangle
GLES20.glUniform4fv(mColorHandle, 1, color, 0);
// get handle to shape's transformation matrix
mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
//MyGLRenderer.checkGlError("glGetUniformLocation");
// Apply the projection and view transformation
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);
//MyGLRenderer.checkGlError("glUniformMatrix4fv");
// Draw the square
GLES20.glDrawElements(
GLES20.GL_TRIANGLES, drawOrder.length,
GLES20.GL_UNSIGNED_SHORT, drawListBuffer);
// Disable vertex array
GLES20.glDisableVertexAttribArray(mPositionHandle);
}
}
public class MyGLRenderer implements GLSurfaceView.Renderer {
private Triangle mTriangle;
public Square getmSquare() {
return mSquare;
}
public void setmSquare(Square mSquare) {
this.mSquare = mSquare;
}
private Square mSquare;
private Circle mCircle;
// vPMatrix is an abbreviation for "Model View Projection Matrix"
private final float[] vPMatrix = new float[16];
private final float[] projectionMatrix = new float[16];
private final float[] viewMatrix = new float[16];
private float[] rotationMatrix = new float[16];
private float[] translationMatrix = new float[16];
private float[] scaleMatrix = new float[16];
public volatile float mAngle;
public float mSquareX = 1.5f;
public float mSquareY = 0.0f;
public float mRadius = 1.0f;
public float getAngle() {
return mAngle;
}
public void setAngle(float angle) {
mAngle = angle;
}
public void onSurfaceCreated(GL10 unused, EGLConfig eglconfig) {
// Set the background frame color
GLES20.glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
// initialize a triangle
mTriangle = new Triangle();
// initialize a square
mSquare = new Square();
// initialize a square
mCircle = new Circle();
}
#Override
public void onDrawFrame(GL10 unused) {
float[] scratch = new float[16];
float[] movementSquare = new float[16];
float[] scaleCircle = new float[16];
float tempscaleFactor = 1.0f * mRadius;
// Redraw background color
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
// Set the camera position (View matrix)
Matrix.setLookAtM(viewMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
// Calculate the projection and view transformation
Matrix.multiplyMM(vPMatrix, 0, projectionMatrix, 0, viewMatrix, 0);
// Create a rotation transformation for the triangle
//long time = SystemClock.uptimeMillis() % 4000L;
//float angle = 0.090f * ((int) time);
Matrix.setRotateM(rotationMatrix, 0, mAngle, 0, 0, -1.0f);
Matrix.setIdentityM(translationMatrix,0);
Matrix.translateM(translationMatrix, 0, mSquareX, mSquareY,0);
//THIS PROBLEM HERE IS THAT MY CIRCLE TRANSLATE'S ON THE X-AXIS WHEN SCALING. MY GOAL IS TO TRY AND KEEP IT IN PLACE WHILE IT'S BEING SCALED. Y-AXIS HAS NOT ISSUES
Matrix.setIdentityM(scaleMatrix, 0);
Matrix.scaleM(scaleMatrix, 0, mRadius, mRadius, 0);
if(mRadius != 1f)
Matrix.translateM(scaleMatrix, 0, -(1 + (mRadius / 2)),0,0);
// Combine the rotation matrix with the projection and camera view
// Note that the vPMatrix factor *must be first* in order
// for the matrix multiplication product to be correct.
Matrix.multiplyMM(movementSquare, 0, vPMatrix, 0, translationMatrix, 0);
Matrix.multiplyMM(scratch, 0, vPMatrix, 0, rotationMatrix, 0);
Matrix.multiplyMM(scaleCircle, 0, vPMatrix, 0, scaleMatrix, 0);
// Draw shape
mTriangle.draw(scratch);
mSquare.draw(movementSquare);
mCircle.draw(scaleCircle);
}
#Override
public void onSurfaceChanged(GL10 unused, int width, int height) {
GLES20.glViewport(0, 0, width, height);
float ratio = (float) width / height;
// this projection matrix is applied to object coordinates
// in the onDrawFrame() method
Matrix.frustumM(projectionMatrix, 0, -ratio, ratio, -1, 1, 2, 7);
}
public static int loadShader(int type, String shaderCode){
// create a vertex shader type (GLES20.GL_VERTEX_SHADER)
// or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
int shader = GLES20.glCreateShader(type);
// add the source code to the shader and compile it
GLES20.glShaderSource(shader, shaderCode);
GLES20.glCompileShader(shader);
return shader;
}
}

Floating Bitmaps, Coding Conflicts?

Good day, I am beginning to learn Java and one of the first projects that I thought might help me to begin learning is by designing a watch face for WearOS, I am an artist/illustrator (currently going back to school to learn computer programming) by birth and school, so I am currently attempting work that I have some idea about.
My question is this. I have designed and implemented bitmaps that are rotating for the watch's hands, and have tried to center them with "mCenterX and Y" and have even manually tried to center them with canvas.translate, and entering in coordinates. Sorry if this is an absolute newbie question, but I have been Googling and attempting for at least a week. (Would include image of floating hands, but can't embed.)
The "Hour Hand" is the only one that I have currently working, and I only was able to do that with manually entering the coordinates with canvas.translate. The other hands will come into frame every-once-in-a-while then float out of frame, rotating around some "unknown" center point.
Code is below: (I have included all code pertaining to the bitmaps for the hour/minute/second hands, to see if I have something that is conflicting. I am using bits of code from various projects, which probably explains the off-centeredness, but I don't have the knowledge yet to grasp why, which is my ultimate goal here.)
Thank you for looking and replying! If you need additional information, let me know.
private class Engine extends CanvasWatchFaceService.Engine {
private static final float HOUR_STROKE_WIDTH = 5f;
private static final float MINUTE_STROKE_WIDTH = 3f;
private static final float SECOND_TICK_STROKE_WIDTH = 2f;
private static final float CENTER_GAP_AND_CIRCLE_RADIUS = 4f;
private static final int SHADOW_RADIUS = 6;
/* Handler to update the time once a second in interactive mode. */
private final Handler mUpdateTimeHandler = new EngineHandler(this);
private Calendar mCalendar;
private final BroadcastReceiver mTimeZoneReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
mCalendar.setTimeZone(TimeZone.getDefault());
invalidate();
}
};
private boolean mRegisteredTimeZoneReceiver = false;
private boolean mMuteMode;
private float mCenterX;
private float mCenterY;
private int mWatchHandColor;
private int mWatchHandHighlightColor;
private int mWatchHandShadowColor;
private Paint mHourPaint;
private Paint mMinutePaint;
private Paint mSecondPaint;
private Paint mTickAndCirclePaint;
private Paint mBackgroundPaint;
private Bitmap mHourBitmap;
private Bitmap mMinuteBitmap;
private Bitmap mSecondBitmap;
private Bitmap mBackgroundBitmap;
private Bitmap mGrayBackgroundBitmap;
private void initializeWatchFace() {
/* Set defaults for colors */
mWatchHandColor = Color.WHITE;
mWatchHandHighlightColor = Color.RED;
mWatchHandShadowColor = Color.BLACK;
mHourPaint = new Paint();
mHourPaint.setColor(mWatchHandColor);
mHourPaint.setStrokeWidth(HOUR_STROKE_WIDTH);
mHourPaint.setAntiAlias(true);
mHourPaint.setStrokeCap(Paint.Cap.ROUND);
mHourPaint.setShadowLayer(SHADOW_RADIUS, 0, 0, mWatchHandShadowColor);
mMinutePaint = new Paint();
mMinutePaint.setColor(mWatchHandColor);
mMinutePaint.setStrokeWidth(MINUTE_STROKE_WIDTH);
mMinutePaint.setAntiAlias(true);
mMinutePaint.setStrokeCap(Paint.Cap.ROUND);
mMinutePaint.setShadowLayer(SHADOW_RADIUS, 0, 0, mWatchHandShadowColor);
mSecondPaint = new Paint();
mSecondPaint.setColor(mWatchHandHighlightColor);
mSecondPaint.setStrokeWidth(SECOND_TICK_STROKE_WIDTH);
mSecondPaint.setAntiAlias(true);
mSecondPaint.setStrokeCap(Paint.Cap.ROUND);
mSecondPaint.setShadowLayer(SHADOW_RADIUS, 0, 0, mWatchHandShadowColor);
mTickAndCirclePaint = new Paint();
mTickAndCirclePaint.setColor(mWatchHandColor);
mTickAndCirclePaint.setStrokeWidth(SECOND_TICK_STROKE_WIDTH);
mTickAndCirclePaint.setAntiAlias(true);
mTickAndCirclePaint.setStyle(Paint.Style.STROKE);
mTickAndCirclePaint.setShadowLayer(SHADOW_RADIUS, 0, 0, mWatchHandShadowColor);
}
private void updateWatchHandStyle() {
if (mAmbient) {
mHourPaint.setColor(Color.WHITE);
mMinutePaint.setColor(Color.WHITE);
mSecondPaint.setColor(Color.WHITE);
mTickAndCirclePaint.setColor(Color.WHITE);
mHourPaint.setAntiAlias(false);
mMinutePaint.setAntiAlias(false);
mSecondPaint.setAntiAlias(false);
mTickAndCirclePaint.setAntiAlias(false);
mHourPaint.clearShadowLayer();
mMinutePaint.clearShadowLayer();
mSecondPaint.clearShadowLayer();
mTickAndCirclePaint.clearShadowLayer();
} else {
mHourPaint.setColor(mWatchHandColor);
mMinutePaint.setColor(mWatchHandColor);
mSecondPaint.setColor(mWatchHandHighlightColor);
mTickAndCirclePaint.setColor(mWatchHandColor);
mHourPaint.setAntiAlias(true);
mMinutePaint.setAntiAlias(true);
mSecondPaint.setAntiAlias(true);
mTickAndCirclePaint.setAntiAlias(true);
mHourPaint.setShadowLayer(SHADOW_RADIUS, 0, 0, mWatchHandShadowColor);
mMinutePaint.setShadowLayer(SHADOW_RADIUS, 0, 0, mWatchHandShadowColor);
mSecondPaint.setShadowLayer(SHADOW_RADIUS, 0, 0, mWatchHandShadowColor);
mTickAndCirclePaint.setShadowLayer(SHADOW_RADIUS, 0, 0, mWatchHandShadowColor);
}
#Override
public void onInterruptionFilterChanged(int interruptionFilter) {
super.onInterruptionFilterChanged(interruptionFilter);
boolean inMuteMode = (interruptionFilter == WatchFaceService.INTERRUPTION_FILTER_NONE);
/* Dim display in mute mode. */
if (mMuteMode != inMuteMode) {
mMuteMode = inMuteMode;
mHourPaint.setAlpha(inMuteMode ? 100 : 255);
mMinutePaint.setAlpha(inMuteMode ? 100 : 255);
mSecondPaint.setAlpha(inMuteMode ? 80 : 255);
invalidate();
}
}
#Override
public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
super.onSurfaceChanged(holder, format, width, height);
/*
* Find the coordinates of the center point on the screen, and ignore the window
* insets, so that, on round watches with a "chin", the watch face is centered on the
* entire screen, not just the usable portion.
*/
mCenterX = width / 2f;
mCenterY = height / 2f;
/*
* Calculate lengths of different hands based on watch screen size.
*/
mSecondHandLength = (float) (mCenterX * 0.875);
sMinuteHandLength = (float) (mCenterX * 0.75);
sHourHandLength = (float) (mCenterX * 0.5);
/* Scale loaded background image (more efficient) if surface dimensions change. */
float scale = ((float) width) / (float) mBackgroundBitmap.getWidth();
mBackgroundBitmap = Bitmap.createScaledBitmap(mBackgroundBitmap,
(int) (mBackgroundBitmap.getWidth() * scale),
(int) (mBackgroundBitmap.getHeight() * scale), false);
float scale2 = ((float) width) / (float) mSecondBitmap.getWidth();
mSecondBitmap = Bitmap.createScaledBitmap(mSecondBitmap,
12,
222, false);
float scale3 = ((float) width) / (float) mHourBitmap.getWidth();
mHourBitmap = Bitmap.createScaledBitmap(mHourBitmap,
12,139,false);
float scale4 = ((float) width) / (float) mMinuteBitmap.getWidth();
mMinuteBitmap = Bitmap.createScaledBitmap(mMinuteBitmap,
12,
162, false);
private void drawWatchFace(Canvas canvas) {
/*
* Draw ticks. Usually you will want to bake this directly into the photo, but in
* cases where you want to allow users to select their own photos, this dynamically
* creates them on top of the photo.
*/
float innerTickRadius = mCenterX - 10;
float outerTickRadius = mCenterX;
for (int tickIndex = 0; tickIndex < 12; tickIndex++) {
float tickRot = (float) (tickIndex * Math.PI * 2 / 12);
float innerX = (float) Math.sin(tickRot) * innerTickRadius;
float innerY = (float) -Math.cos(tickRot) * innerTickRadius;
float outerX = (float) Math.sin(tickRot) * outerTickRadius;
float outerY = (float) -Math.cos(tickRot) * outerTickRadius;
canvas.drawLine(mCenterX + innerX, mCenterY + innerY,
mCenterX + outerX, mCenterY + outerY, mTickAndCirclePaint);
}
/*
* These calculations reflect the rotation in degrees per unit of time, e.g.,
* 360 / 60 = 6 and 360 / 12 = 30.
*/
final float seconds =
(mCalendar.get(Calendar.SECOND) + mCalendar.get(Calendar.MILLISECOND) / 1000f);
final float secondsRotation = seconds * 6f;
final float minutesRotation = mCalendar.get(Calendar.MINUTE) * 6f;
final float hourHandOffset = mCalendar.get(Calendar.MINUTE) / 2f;
final float hoursRotation = (mCalendar.get(Calendar.HOUR) * 30) + hourHandOffset;
/*
* Save the canvas state before we can begin to rotate it.
*/
canvas.save();
canvas.rotate(hoursRotation, mCenterX, mCenterY);
Matrix matrixHour = new Matrix();
matrixHour.setRotate(0, mCenterX, mCenterY);
canvas.translate(175, 68);
canvas.drawBitmap(mHourBitmap, matrixHour, mHourPaint);
canvas.rotate(minutesRotation - hoursRotation, mCenterX, mCenterY);
Matrix matrixMinute = new Matrix();
matrixMinute.setRotate(0, mCenterX, mCenterY);
canvas.translate(175, -60);
canvas.drawBitmap(mMinuteBitmap, matrixMinute, mMinutePaint);
/*
* Ensure the "seconds" hand is drawn only when we are in interactive mode.
* Otherwise, we only update the watch face once a minute.
*/
if (!mAmbient) {
canvas.rotate(secondsRotation - minutesRotation, mCenterX, mCenterY);
Matrix matrix = new Matrix();
matrixHour.setRotate(0, mCenterX, mCenterY);
canvas.translate(-175,35);
canvas.drawBitmap(mSecondBitmap, matrix, mSecondPaint);
}
/* Restore the canvas' original orientation. */
canvas.restore();
}

Scaling of a quad is incorrect after resizing the window. How to fix?

If I try to render a square with the same scaling for x and y without resizing the window everything is fine. But after resizing the window there is no longer a square rendered. Instead, you can see a rectangle, even though I am recalculating the projection matrix and passing the matrix to the shader every time the window's width or height changes.
With a different width and height, the scaling of a square is incorrect. Changing the size of the window in my code without resizing the window does not change the scaling.
I have no idea where the error is. Maybe I missed something.
Coordinates:
float[] positions = new float[] {0, 1, 0, 0, 1, 1, 1, 0};
Calculating the projection matrix:
public static void createProjectionMatrix() {
Vector2f windowSize = DisplayManager.getWindowSize();
float aspectRatio = windowSize.x / windowSize.y;
float halfWidth = 1.0f;
float halfHeight = halfWidth / aspectRatio;
float left = -halfWidth;
float right = halfWidth;
float bottom = -halfHeight;
float top = halfHeight;
float far = -1f;
float near = 1f;
Matrix4f matrix = new Matrix4f();
matrix.setIdentity();
matrix.m00 = 2f / (right - left);
matrix.m11 = 2f / (top - bottom);
matrix.m22 = -2f / (far - near);
matrix.m32 = (far + near) / (far - near);
matrix.m30 = (right + left) / (right - left);
matrix.m31 = (top + bottom) / (top - bottom);
projectionMatrix = matrix;
}
Calculation the transformation (Vector2f worldScale is not used):
private Vector2f getDisplayCoords(Vector2f percentage) {
Vector2f v = DisplayManager.getWindowSize();
return new Vector2f(v.x * percentage.x, v.y * percentage.y);
}
public void loadTransformationMatricies() {
Vector2f displaySize = getDisplayCoords(size);
Vector2f displayPos = getDisplayCoords(position);
Vector2f display = DisplayManager.getWindowSize();
Vector2f worldPos = Mouse.getWorldPos(displayPos);
Vector2f worldScale = new Vector2f(displaySize.x / (display.x / 2.0f), displaySize.y / (display.y / 2));
float x, y;
x = worldPos.x;
y = worldPos.y - CORNER_SCALE;
//y is calculated correct. Moving the mouse to the same y value as calculated shows that everything is correctly calculated
System.out.println(y + " | " + Mouse.getWorldPos().y);
transforms[0] = Maths.getTransformationMatrix(new Vector2f(x, y), new Vector2f(CORNER_SCALE, CORNER_SCALE));
}
Check size:
GLFW.glfwSetWindowSizeCallback(WINDOW, new GLFWWindowSizeCallback() {
#Override
public void invoke(long arg0, int arg1, int arg2) {
resized = true;
}
});
Rendering (would normally render more objects):
#Override
protected void render() {
shader.start();
if(DisplayManager.isResized()) {
shader.loadProjectionMatrix(MasterRenderer.getProjectionMatrix());
}
bindModel(Loader.getQuad(), new int[] {0});
for(GUI gui : guis) {
if(DisplayManager.isResized()) {
gui.loadTransformationMatricies();
}
bindTexture(gui.getTexture().getTopLeftCorner(), GL13.GL_TEXTURE0);
Matrix4f[] transforms = gui.getTransformations();
for(int i = 0; i < 1; i++) {
shader.loadTransformationMatrix(transforms[i]);
drawSTRIP(Loader.getQuad());
}
}
unbind(new int[] {0});
shader.stop();
}
Vertexshader:
#version 400 core
in vec2 position;
uniform mat4 transformationMatrix;
uniform mat4 projectionMatrix;
out vec2 textureCoords;
void main (void){
gl_Position = projectionMatrix * transformationMatrix * vec4(position, 0, 1.0);
textureCoords = vec2((position.x+1.0)/2.0, 1 - (position.y+1.0)/2.0);
}
You can find the whole code on Github if you need more information but I only want to know how to fix this specific problem.
https://github.com/StackOverflowEx/GameEngine2D
After the size of the window has changed, the viewport rectangle has to be adjusted to the new size:
Use glViewport to set the viewport rectangle.
Add a method updateViewPort to the class DisplayManager
public static void updateViewPort() {
IntBuffer pWidth = stack.mallocInt(1);
IntBuffer pHeight = stack.mallocInt(1);
GLFW.glfwGetFramebufferSize(WINDOW, pWidth, pHeight);
GL11.glViewport(0, 0, pWidth.get(0), pHeight.get(0));
}
Call the DisplayManager.updateViewPort in the methodprepare in the class MasterRenderer:
private void prepare() {
if(DisplayManager.isResized()) {
DisplayManager.updateViewPort();
createProjectionMatrix();
}
Camera.calcViewMatrix();
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);
GL11.glClearColor(1, 0, 0, 1);
}
See also Java Code Examples for org.lwjgl.glfw.GLFW.glfwGetFramebufferSize().

Applying map of the earth texture a Sphere

i been trying to implement a 3D animation in openGL (using JOGL) of a solar system so far i have 5 planets of different sizes but the problem i seem to be having is i cant add a map of the earth texture on a Sphere can anybody help me on how its done?
This is the code i have so far in my Display method:
#Override
public void display(GLAutoDrawable drawable) {
GL2 gl = drawable.getGL().getGL2();
GLU glu = new GLU();
gl.glClear(GL.GL_COLOR_BUFFER_BIT);
//make sure we are in model_view mode
gl.glMatrixMode(GL2.GL_MODELVIEW);
gl.glLoadIdentity();
glu.gluLookAt(10,20,20,0,3,0,0, 20, 0);
//gl.glMatrixMode(GL2.GL_PROJECTION);
//glu.gluPerspective(45,1,1,25);
//render ground plane
gl.glPushMatrix();
gl.glTranslatef(-10.75f, 3.0f, -1.0f);
gl.glColor3f(0.3f, 0.5f, 1f);
GLUquadric earth = glu.gluNewQuadric();
glu.gluQuadricDrawStyle(earth, GLU.GLU_FILL);
glu.gluQuadricNormals(earth, GLU.GLU_FLAT);
glu.gluQuadricOrientation(earth, GLU.GLU_OUTSIDE);
final float radius = 3.378f;
final int slices = 89;
final int stacks = 16;
glu.gluSphere(earth, radius, slices, stacks);
glu.gluDeleteQuadric(earth);
Texture earths;
try {
earths = TextureIO.newTexture(new File("earth.png"), true);
}
catch (IOException e) {
javax.swing.JOptionPane.showMessageDialog(null, e);
}
gl.glPopMatrix();
//gl.glEnd();
gl.glPushMatrix();
gl.glTranslatef(2.75f, 3.0f, -0.0f);
gl.glColor3f(0.3f, 0.5f, 1f);
GLUquadric earth1 = glu.gluNewQuadric();
glu.gluQuadricDrawStyle(earth1, GLU.GLU_FILL);
glu.gluQuadricNormals(earth1, GLU.GLU_FLAT);
glu.gluQuadricOrientation(earth1, GLU.GLU_OUTSIDE);
final float radius1 = 3.378f;
final int slices1 = 90;
final int stacks1 = 63;
glu.gluSphere(earth1, radius1, slices1, stacks1);
glu.gluDeleteQuadric(earth1);
gl.glPopMatrix();
gl.glPushMatrix();
gl.glTranslatef(3.75f, 6.0f, -7.20f);
gl.glColor3f(0.3f, 0.5f, 1f);
GLUquadric earth3 = glu.gluNewQuadric();
glu.gluQuadricDrawStyle(earth3, GLU.GLU_FILL);
glu.gluQuadricNormals(earth3, GLU.GLU_FLAT);
glu.gluQuadricOrientation(earth1, GLU.GLU_OUTSIDE);
final float radius3 = 1.878f;
final int slices3 = 89;
final int stacks3 = 16;
glu.gluSphere(earth3, radius3, slices3, stacks3);
glu.gluDeleteQuadric(earth3);
gl.glPopMatrix();
gl.glPushMatrix();
gl.glTranslatef(12.75f, 2.0f, -7.20f);
gl.glColor3f(0.3f, 0.5f, 1f);
GLUquadric earth4 = glu.gluNewQuadric();
glu.gluQuadricDrawStyle(earth4, GLU.GLU_FILL);
glu.gluQuadricNormals(earth4, GLU.GLU_FLAT);
glu.gluQuadricOrientation(earth4, GLU.GLU_OUTSIDE);
final float radius4 = 1.078f;
final int slices4 = 89;
final int stacks4 = 16;
glu.gluSphere(earth4, radius4, slices4, stacks4);
glu.gluDeleteQuadric(earth4);
gl.glPopMatrix();
gl.glPushMatrix();
gl.glTranslatef(2.75f, -6.0f, -0.0f);
gl.glColor3f(0.3f, 0.5f, 1f);
GLUquadric earth5 = glu.gluNewQuadric();
glu.gluQuadricDrawStyle(earth5, GLU.GLU_FILL);
glu.gluQuadricNormals(earth5, GLU.GLU_FLAT);
glu.gluQuadricOrientation(earth5, GLU.GLU_OUTSIDE);
final float radius5 = 3.778f;
final int slices5 = 90;
final int stacks5 = 63;
glu.gluSphere(earth5, radius5, slices5, stacks5);
glu.gluDeleteQuadric(earth5);
gl.glPopMatrix();
}
create your own sphere mesh
simple 2D loop through 2 angles (spherical coordinate system 2 Cartesian). You can easily add ellipsoid properties (earth is not a sphere) if you want more precision. If not then you can use single sphere mesh for all planets and just scale it before use ...
let a be the longitude and b the latitude so loop a from 0 to 2*PI [rad] and b from -0.5*PI to +0.5*PI [rad] where PI=3.1415... is the Pi (in C++ math.h it is called M_PI). If your math api uses degrees then convert to degrees PI [rad] = 180.0 [deg]
add necessary info per vertex
normals for lighting
// just unit sphere
nx=cos(b)*cos(a);
ny=cos(b)*sin(a);
nz=sin(b);
texture coordinate (assuming rectangle non distorted image)
// just convert a,b to <0,1> range
tx=a/(2.0*PI)
ty=(b/PI)+0.5;
vertex position
// just sphere(rx=ry=rz=r) or ellipsoid (rx=ry=equatorial and rz=polar radius)
// can also use rx*nx,ry*ny,rz*nz instead ...
x=rx*cos(b)*cos(a);
y=ry*cos(b)*sin(a);
z=rz*sin(b);
send all of this to OpenGL
so all above store in some memory space (CPU or GPU) and then send to rendering. You can use legacy glBegin(QUAD_STRIP); ... glEnd(); or displaylist/VBO/VAO. Bind the right texture before each planet/body and do not forget to update ModelView matrix too. This is how mine coordinate systems looks like:
Also have a look at these related Q/As:
realistic n-body solar system
sphere mesh by subdivision
[edit1] C++ example
//---------------------------------------------------------------------------
const int nb=15; // slices
const int na=nb<<1; // points per equator
class planet
{
public:
bool _init; // has been initiated ?
GLfloat x0,y0,z0; // center of planet [GCS]
GLfloat pos[na][nb][3]; // vertex
GLfloat nor[na][nb][3]; // normal
GLfloat txr[na][nb][2]; // texcoord
GLuint txrid; // texture id
GLfloat t; // dayly rotation angle [deg]
planet() { _init=false; txrid=0; x0=0.0; y0=0.0; z0=0.0; t=0.0; }
~planet() { if (_init) glDeleteTextures(1,&txrid); }
void init(GLfloat r,AnsiString texture); // call after OpenGL is already working !!!
void draw();
};
void planet::init(GLfloat r,AnsiString texture)
{
if (!_init) { _init=true; glGenTextures(1,&txrid); }
GLfloat x,y,z,a,b,da,db;
GLfloat tx0,tdx,ty0,tdy;// just correction if CLAMP_TO_EDGE is not available
int ia,ib;
// a,b to texture coordinate system
tx0=0.0;
ty0=0.5;
tdx=0.5/M_PI;
tdy=1.0/M_PI;
// load texture to GPU memory
if (texture!="")
{
Byte q;
unsigned int *pp;
int xs,ys,x,y,adr,*txr;
union { unsigned int c32; Byte db[4]; } c;
Graphics::TBitmap *bmp=new Graphics::TBitmap; // new bmp
bmp->LoadFromFile(texture); // load from file
bmp->HandleType=bmDIB; // allow direct access to pixels
bmp->PixelFormat=pf32bit; // set pixel to 32bit so int is the same size as pixel
xs=bmp->Width; // resolution should be power of 2
ys=bmp->Height;
txr=new int[xs*ys];
for(adr=0,y=0;y<ys;y++)
{
pp=(unsigned int*)bmp->ScanLine[y];
for(x=0;x<xs;x++,adr++)
{
// rgb2bgr and copy bmp -> txr[]
c.c32=pp[x];
q =c.db[2];
c.db[2]=c.db[0];
c.db[0]=q;
txr[adr]=c.c32;
}
}
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D,txrid);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,GL_MODULATE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, xs, ys, 0, GL_RGBA, GL_UNSIGNED_BYTE, txr);
glDisable(GL_TEXTURE_2D);
delete bmp;
delete[] txr;
// texture coordinates by 1 pixel from each edge (GL_CLAMP_TO_EDGE)
tx0+=1.0/GLfloat(xs);
ty0+=1.0/GLfloat(ys);
tdx*=GLfloat(xs-2)/GLfloat(xs);
tdy*=GLfloat(ys-2)/GLfloat(ys);
}
// correct texture coordinate system (invert x)
tx0=1.0-tx0; tdx=-tdx;
da=(2.0*M_PI)/GLfloat(na-1);
db= M_PI /GLfloat(nb-1);
for (ib=0,b=-0.5*M_PI;ib<nb;ib++,b+=db)
for (ia=0,a= 0.0 ;ia<na;ia++,a+=da)
{
x=cos(b)*cos(a);
y=cos(b)*sin(a);
z=sin(b);
nor[ia][ib][0]=x;
nor[ia][ib][1]=y;
nor[ia][ib][2]=z;
pos[ia][ib][0]=r*x;
pos[ia][ib][1]=r*y;
pos[ia][ib][2]=r*z;
txr[ia][ib][0]=tx0+(a*tdx);
txr[ia][ib][1]=ty0+(b*tdy);
}
}
void planet::draw()
{
if (!_init) return;
int ia,ib0,ib1;
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glTranslatef(x0,y0,z0);
glRotatef(90.0,1.0,0.0,0.0); // rotate planets z axis (North) to OpenGL y axis (Up)
glRotatef(-t,0.0,0.0,1.0); // rotate planets z axis (North) to OpenGL y axis (Up)
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D,txrid);
glColor3f(1.0,1.0,1.0);
for (ib0=0,ib1=1;ib1<nb;ib0=ib1,ib1++)
{
glBegin(GL_QUAD_STRIP);
for (ia=0;ia<na;ia++)
{
glNormal3fv (nor[ia][ib0]);
glTexCoord2fv(txr[ia][ib0]);
glVertex3fv (pos[ia][ib0]);
glNormal3fv (nor[ia][ib1]);
glTexCoord2fv(txr[ia][ib1]);
glVertex3fv (pos[ia][ib1]);
}
glEnd();
}
glDisable(GL_TEXTURE_2D);
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
}
//---------------------------------------------------------------------------
usage:
// variable to store planet (global)
planet earth;
// init after OpenGL initialisation
earth.init(1.0,"earth.bmp");
// position update
earth.x0= 0.0;
earth.y0= 0.0;
earth.z0=-20.0;
// add this to render loop
earth.draw(); // draws the planet
earth.t+=2.5; // just rotate planet by 2.5 deg each frame...
I know its ugly but it does not use any funny stuff just legacy OpenGL and Math.h (cos(),sin(),M_PI) and VCL for bitmap loading. So rewrite to your environment and you will be fine. Do not forget that each planet has its own texture so you need to have one txrid per planet so either have each planet as separate planet variable or rewrite ...

Trouble rotating image in Android aplication

I am currently writing an android application that should rotate an image towards a set location based on the users current location. I can tell that the set location is setting correctly, and the current location seems to be updating, but instead of rotating the image, the app just zooms in on the image and then does nothing. Any help would really be appreciated!
public double bearing(double lat1, double lon1, double lat2, double lon2) {
double longitude1 = lon1;
double longitude2 = lon2;
double latitude1 = Math.toRadians(lat1);
double latitude2 = Math.toRadians(lat2);
double longDiff = Math.toRadians(longitude2 - longitude1);
double y = Math.sin(longDiff) * Math.cos(latitude2);
double x = Math.cos(latitude1) * Math.sin(latitude2) - Math.sin(latitude1) * Math.cos(latitude2) * Math.cos(longDiff);
return (Math.toDegrees(Math.atan2(y, x)) + 360) % 360;
}
private void rotateImageView(ImageView imageView, int drawable, float rotate) {
// Decode the drawable into a bitmap
Bitmap bitmapOrg = BitmapFactory.decodeResource(getResources(),
drawable);
// Get the width/height of the drawable
DisplayMetrics dm = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(dm);
int width = bitmapOrg.getWidth(), height = bitmapOrg.getHeight();
// Initialize a new Matrix
Matrix matrix = new Matrix();
// Decide on how much to rotate
rotate = rotate % 360;
// Actually rotate the image
matrix.postRotate(rotate, width, height);
// recreate the new Bitmap via a couple conditions
Bitmap rotatedBitmap = Bitmap.createBitmap(bitmapOrg, 0, 0, width, height, matrix, true);
//BitmapDrawable bmd = new BitmapDrawable( rotatedBitmap );
//imageView.setImageBitmap( rotatedBitmap );
imageView.setImageDrawable(new BitmapDrawable(getResources(), rotatedBitmap));
imageView.setScaleType(ImageView.ScaleType.CENTER);
}
public void onLocationChange() {
// If we don't have a Location, we break out
if (currentLocation == null) return;
double azimuth = bearing(currentLocation.getLatitude(), currentLocation.getLongitude(), baseLocation.getLatitude(), baseLocation.getLongitude());
double baseAzimuth = azimuth;
GeomagneticField geoField = new GeomagneticField(Double
.valueOf(currentLocation.getLatitude()).floatValue(), Double
.valueOf(currentLocation.getLongitude()).floatValue(),
Double.valueOf(currentLocation.getAltitude()).floatValue(),
System.currentTimeMillis()
);
azimuth -= geoField.getDeclination(); // converts magnetic north into true north
// Store the bearingTo in the bearTo variable
float bearTo = currentLocation.bearingTo(baseLocation);
// If the bearTo is smaller than 0, add 360 to get the rotation clockwise.
if (bearTo < 0) {
bearTo = bearTo + 360;
}
//This is where we choose to point it
double direction = bearTo - azimuth;
// If the direction is smaller than 0, add 360 to get the rotation clockwise.
if (direction < 0) {
direction = direction + 360;
}
float fDir = (float) direction;
rotateImageView((ImageView) findViewById(R.id.arrow), R.drawable.ic_launcher, fDir);
}
private Runnable updateTimeChange = new Runnable() {
public void run() {
onLocationChange();
customHandler.postDelayed(this, 500);
}
};
If your target is greater than 11 then you can try view.setRotation() method:
image.setRotation(angle); // this is for your imageview where image is the imageview object
From the developers page. getRotation()
Sets the degrees that the view is rotated around the pivot point. Increasing values result in clockwise rotation.
If you want to setRotation in xml the xml tag is :
android:rotation

Categories

Resources