#Override
public void onDrawFrame(GL10 gl) {
float[] scratch = new float[16];
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
// Set the camera position (View matrix)
Matrix.setLookAtM(mViewMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
// Calculate the projection and view transformation
Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);
if(move_x) x=-tiltx/100;
else{
x=0;
if(side_x) {
if (tiltx < -0.1f) {
x = -tiltx / 100;
move_x = true;
}
}
if(!side_x) {
if (tiltx > 0.1f) {
x = -tiltx / 100;
move_x = true;
}
}
}
if(move_y) y=-tilty/100;
else{
y=0;
if(side_y){
if(tilty <-0.1f) {
y=-tilty/100;
move_y=true;
}
}
if(!side_y) {
if(tilty >0.1f) {
y = -tilty / 100;
move_y = true;
}
}
}
Part above checks if it is allowed to move, if it is not it assigns 0 to translate value (x and y). Than (below) the translation is applied, despite the 0 x or y values it moves the object a little futher more and that causes to object end partly behind the view. It also causes to trigger the
if (Math.abs(scratch[12])>=2.4 && move_x)
again when coming back which means playing the sound again.
Matrix.translateM(mBall.mModelMatrix, 0, (float)x*1f, (float)y*1f, 0f);
// Combine the rotation matrix with the projection and camera view
// Note that the mMVPMatrix factor *must be first* in order
// for the matrix multiplication product to be correct.
Matrix.multiplyMM(scratch, 0, mMVPMatrix, 0, mBall.mModelMatrix, 0);
if (Math.abs(scratch[12])>=2.4 && move_x){
setMyBoolean(true);
move_x=false;
if(scratch[12]<0) {
side_x=false;
scratch[12]=-(float)2.4;
}
else {
side_x=true;
scratch[12]=(float)2.4;
}
// true - prawa strona, false - lewa
}
if (Math.abs(scratch[13])>=1.8 && move_y){
move_y=false;
setMyBoolean(true);
if(scratch[13]<0){
side_y=true;
scratch[13]=-(float)1.8;
}
else {
side_y=false;
scratch[13]=(float)1.8;
}
// true - prawa strona, false - lewa
}
mBall.draw(scratch);
The code above checks if the object did hits the wall and if it does it blocks futher movement, plays the sound via setMyBoolean, and whats most important - sets the object to exactly touch the wall by changing the scratch[12] and 13 values.
As explained above, in the next loop the object position is calculated different (despite the fact the translate variable is set to 0) and the ball ends in the wall again.
It's worth mentioning that it does not change the position futher in the next loops. It stays in the wall until I tilt the phone to the other side triggering the if that allows the object to move away from the wall.
The answer is actually simple, my method didn't or actually did work but only from one frame, since I didn't correct the wrong position by translating the object. What I did was only correcting the value of matrix that was used to draw the object once. But since the ball remembers its position, it would still draw iteself in the wall in the next frame.
What I did to fix this issue is by adjusting the position of the ball when I've detected it just hit the wall and didn't yet go back. Now the adjustments are made by actually translating the object and it is done every frame.
Heres the code that does that:
if(!move_x) {
if(side_x) {
Matrix.translateM(mBall.mModelMatrix, 0, (scratch[12] - (float) 2.4) * 1f, 0, 0f);
Matrix.multiplyMM(scratch, 0, mMVPMatrix, 0, mBall.mModelMatrix, 0);
} else {
Matrix.translateM(mBall.mModelMatrix, 0, (scratch[12] + (float) 2.4) * 1f, 0, 0f);
Matrix.multiplyMM(scratch, 0, mMVPMatrix, 0, mBall.mModelMatrix, 0);
}
}
if(!move_y) {
if(side_y){
Matrix.translateM(mBall.mModelMatrix, 0, 0, (1.8f - scratch[13]) * 0.5f, 0f);
Matrix.multiplyMM(scratch, 0, mMVPMatrix, 0, mBall.mModelMatrix, 0);
} else {
Matrix.translateM(mBall.mModelMatrix, 0, 0, -(1.8f + scratch[13]) * 0.5f, 0f);
Matrix.multiplyMM(scratch, 0, mMVPMatrix, 0, mBall.mModelMatrix, 0);
}
}
Related
I want to rotate an element around a specific point defined by me and dynamically changed.
I am orientating myself at the guidelines from the google developers site.
My first approach is this:
scratch = new float[16];
Matrix.setIdentityM(mRotationMatrix, 0);
Matrix.setRotateM(mRotationMatrix, 0, angle, 0, 0, 1f);
Matrix.multiplyMM(scratch, 0, mMVPMatrix, 0, mRotationMatrix, 0);
element.draw(scratch);
This rotates the object around the center of the screen.
What do I have to add/change to make the object rotate around some other point?
Add a translation operation.
Grafika's Sprite2d class provides an example:
/**
* Re-computes mModelViewMatrix, based on the current values for rotation, scale, and
* translation.
*/
private void recomputeMatrix() {
float[] modelView = mModelViewMatrix;
Matrix.setIdentityM(modelView, 0);
Matrix.translateM(modelView, 0, mPosX, mPosY, 0.0f);
if (mAngle != 0.0f) {
Matrix.rotateM(modelView, 0, mAngle, 0.0f, 0.0f, 1.0f);
}
Matrix.scaleM(modelView, 0, mScaleX, mScaleY, 1.0f);
mMatrixReady = true;
}
This positions the object, then rotates it around the center of the object.
You need to translate the matrix in the reverse direction of the point first, then rotate and then translate it back. Look at it as if the rotation is always rotating around the center of the world, and the translation moves the center of the world.
Something like this (untested):
scratch = new float[16];
Matrix.setIdentityM(mRotationMatrix, 0);
Matrix.translateM(mRotationMatrix, 0, -x, -y, -z);
Matrix.rotateM(mRotationMatrix, 0, angle, 0, 0, 1f);
Matrix.translateM(mRotationMatrix, 0, x, y, z);
Matrix.multiplyMM(scratch, 0, mMVPMatrix, 0, mRotationMatrix, 0);
element.draw(scratch);
The x, y and z values need to be calculated as the delta between the current position of the object in the world and the position of the point you want to rotate around. You need to do that calculation yourself, but that's pretty trivial.
I'm trying to implement color picking using GLES20.glReadPixels function in android OpenGL ES. The problem is that this function is always returning 0,0,0,0 as color and not the correct color values. Any idea why? My code looks like this:
public boolean onTouchEvent(MotionEvent event)
{
if (event != null)
{
float x = event.getX();
float y = event.getY();
if (event.getAction() == MotionEvent.ACTION_UP)
{
int newX = (int)x;
int newY = (int)y;
ByteBuffer pixel = ByteBuffer.allocate(4);
pixel.order(ByteOrder.nativeOrder());
pixel.position(0);
GLES20.glReadPixels(newX, (int)mRenderer.viewport[3] - newY, 1, 1,
GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, pixel);
}
return true;
}
else
{
return super.onTouchEvent(event);
}
So as I said before... the result of the pixel array is always 0,0,0,0. Dont know why :/ What am I doing wrong? I was using the lighthouse tutorial as a reference:
http://www.lighthouse3d.com/opengl/picking/index.php3?color2
And I really can't see the mistake at this point :/
Oh I forgot to tell that my scene contains a 3D cube which is fully BLUE so the result should be something like 0,0,1,0 when I click on it but it isn't :(
EDIT:
The code from the Renderer where the Cube is drawn (it rotates arround its y-axis)
public void onDrawFrame(GL10 unused) {
float[] scratch = new float[16];
GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
GLES20.glEnable(GLES20.GL_DEPTH_TEST);
Matrix.setLookAtM(mViewMatrix, 0, 0, 0, -3.0f, 0f, -0.3f, 0.0f, 0.0f, 1.0f, 0.0f);
Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);
float[] mModelMatrix = new float[16];
Matrix.setIdentityM(mModelMatrix, 0);
Matrix.translateM(mModelMatrix, 0, 0, 0, -0.5f);
Matrix.setIdentityM(mRotationMatrix, 0);
Matrix.rotateM(mRotationMatrix, 0, mDeltaX, 0, 1.0f, 0);
Matrix.rotateM(mRotationMatrix, 0, -mDeltaY, 1.0f, 0, 0);
mDeltaX = 0.2f;
mDeltaY = 0.2f;
float[] mTempMatrix = new float[16];
Matrix.multiplyMM(mTempMatrix, 0, mRotationMatrix, 0, mAccumulatedRotation, 0);
System.arraycopy(mTempMatrix, 0, mAccumulatedRotation, 0, 16);
float[] temp = new float[16];
Matrix.multiplyMM(temp, 0, mModelMatrix, 0 , mAccumulatedRotation, 0);
Matrix.multiplyMM(scratch, 0, mMVPMatrix, 0, temp, 0);
Matrix.setIdentityM(mModelMatrix, 0);
Matrix.translateM(mModelMatrix, 0, 0, 0, 0.5f);
float[] temp2 = new float[16];
Matrix.multiplyMM(temp2, 0, scratch, 0, mModelMatrix, 0);
mCube.drawCube(temp2);
}
Like all other OpenGL calls, glReadPixels() only works if there is a current OpenGL context.
In Android, OpenGL rendering is mostly done using a GLSurfaceView, which takes care of spawning a secondary thread for rendering, creating an OpenGL context, and making that context current in the secondary rendering thread while invoking the methods in your GLSurfaceView.Renderer implementation.
onTouchEvent() is invoked in the UI thread, so you won't have a current OpenGL context here. To use glReadPixels(), you can forward the request to your rendering thread using the GLSurfaceView.queueEvent() method, and then process it asynchronously the next time your Renderer.onDraw() method is invoked.
I have cube that rotates around the center of the coordinates system. But the problem is it rotates very slowly. So in my case how to set the rotation speed?
The following three methods update the mCurrentModelMatrix with the given model transformation. These are stateful accumulative methods.
public void trnslate(float x, float y, float z)
{
float[] tempModelMatrix = new float[16];
Matrix.setIdentityM(tempModelMatrix, 0);
Matrix.translateM(tempModelMatrix,0,x,y,z);
Matrix.multiplyMM(this.mCurrentModelMatrix, 0,
tempModelMatrix, 0, this.mCurrentModelMatrix, 0);
}
public void rotate(float angle, float x, float y, float z)
{
float[] tempModelMatrix = new float[16];
Matrix.setIdentityM(tempModelMatrix, 0);
Matrix.rotateM(tempModelMatrix,0,angle,x,y,z);
Matrix.multiplyMM(this.mCurrentModelMatrix, 0,
tempModelMatrix, 0, this.mCurrentModelMatrix, 0);
}
public void scale(float xFactor, float yFactor, float zFactor)
{
float[] tempModelMatrix = new float[16];
Matrix.setIdentityM(tempModelMatrix, 0);
Matrix.scaleM(tempModelMatrix,0,xFactor,yFactor,zFactor);
Matrix.multiplyMM(this.mCurrentModelMatrix, 0,
tempModelMatrix, 0, this.mCurrentModelMatrix, 0);
}
/*
* Calculaute the final model view matrix
* 1. Order of matrix multiplication is important
* 2. MVPmatrix = proj * view * model;
* 3. Setup the MVP matrix in the vertex shader memory
*/
protected void setupMatrices()
{
float[] tempModelMatrix = new float[16];
Matrix.setIdentityM(tempModelMatrix, 0);
//translate the model combo next
Matrix.multiplyMM(mMVPMatrix, 0, //matrix and offset
mCurrentModelMatrix, 0,
tempModelMatrix, 0);
//translate eye coordinates first
Matrix.multiplyMM(mMVPMatrix, 0,
this.mVMatrix, 0,
mMVPMatrix, 0);
//Project it: screen coordinates
Matrix.multiplyMM(mMVPMatrix, 0,
mProjMatrix, 0,
mMVPMatrix, 0);
//Set the vertex uniform handler representing the MVP matrix
GLES20.glUniformMatrix4fv(muMVPMatrixHandle, //uniform handle
1, //number of uniforms. 1 if it is not an array
false, //transpose: must be false
mMVPMatrix, //client matrix memory pointer
0); //offset
}
draw method
// Drawing operation
#Override
protected void draw(GL10 gl, int positionHandle) {
// Hide the hidden surfaces using these APIs
GLES20.glEnable(GLES20.GL_DEPTH_TEST);
GLES20.glDepthFunc(GLES20.GL_LESS);
// Transfer vertices to the shader
transferVertexPoints(positionHandle);
// Transfer texture points to the shader
transferTexturePoints(getTextureHandle());
// Implement rotation from 0 to 360 degrees
// Stop when asked and restart when the stopFlag
// is set to false.
// Decide what the current angle to apply
// for rotation is.
if (stopFlag == true) {
// stop rotation
curAngle = stoppedAtAngle;
} else {
curAngle += 1.0f;
}
if (curAngle > 360) {
curAngle = 0;
}
// Tell the base class to start their
// matrices to unit matrices.
this.initializeMatrices();
// The order of these model transformations matter
// Each model transformation is specified with
// respect to the last one, and not the very first.
// Center the cube
this.trnslate(0, 0, -1);
// Rotate it around y axis
this.rotate(curAngle, 0, -1, 0);
// Decenter it to where ever you want
this.trnslate(0, -2, 2);
// Go ahead calculate the ModelViewMatrix as
// we are done with ALL of our model transformations
this.setupMatrices();
// Call glDrawArrays to use the vertices and draw
int vertexCount = mTriangleVerticesData.length / 3;
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, // what primitives to use
0, // at what point to start
vertexCount); // Starting there how many points to use
// Check if there are errors
checkGlError("glDrawArrays");
}
Thanks in advance!
You are rotating at 1 degree per frame, so it will take 360 frames to do a complete rotation.
If you want it to rotate in 2 seconds, and you were running at 30 frames per second, you would want to rotate by 6 degrees per frame, by changing this section:
if (stopFlag == true) {
// stop rotation
curAngle = stoppedAtAngle;
} else {
curAngle += 6.0f;
}
if (curAngle > 360) {
curAngle = 0;
}
I have a class that determines if a square should be drawn or not via a boolean. The problem is, the square stays put even if the boolean is false. My question is, how do I remove the drawn square if the boolean comes up false?
public void onDrawFrame(GL10 unused) {
// Draw background color
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
// Set the camera position (View matrix)
Matrix.setLookAtM(mVMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
// Calculate the projection and view transformation
Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mVMatrix, 0);
// Draw square
Matrix.setRotateM(mRotationMatrix, 0, mAngle, 0, 0, -1.0f);
// Combine the rotation matrix with the projection and camera view
Matrix.multiplyMM(mMVPMatrix, 0, mRotationMatrix, 0, mMVPMatrix, 0);
if (drawObject == true) {
mSquare.draw(mMVPMatrix);
}
// Draw Square
}
Ok, I found the answer.
Create a method:
public void clearBuffers(boolean color, boolean depth, boolean stencil) {
int bits = 0;
if (color) {
bits = GLES20.GL_COLOR_BUFFER_BIT;
}
if (depth) {
bits |= GLES20.GL_DEPTH_BUFFER_BIT;
}
if (stencil) {
bits |= GLES20.GL_STENCIL_BUFFER_BIT;
}
if (bits != 0) {
GLES20.glClear(bits);
}
}
And then call it with:
clearBuffers(true, true, true);
to clear everything on the screen.
I am using default android sample code
http://developer.android.com/training/graphics/opengl/touch.html
In this sample we can rotate triangle by toucht events.
I want just to add movement by x,y axiss for test purposes.
The point that triangle behaviour is not as i am expecting. What i am doing wrong?
Code from tutorial with my new row hilighted:
public void onDrawFrame(GL10 unused) {
// Draw background color
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
// Set the camera position (View matrix)
Matrix.setLookAtM(mVMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
// Calculate the projection and view transformation
Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mVMatrix, 0);
// Draw square
mSquare.draw(mMVPMatrix);
**//Translating this matrix 'brakes' triangle
-> Matrix.translateM(mMVPMatrix, 0, 0, pos, -1.0f);
//NOTHING happens here: ??? Why?
-> Matrix.translateM(mRotationMatrix, 0, pos, 0, -1.0f);**
// Create a rotation for the triangle
// long time = SystemClock.uptimeMillis() % 4000L;
// float angle = 0.090f * ((int) time);
Matrix.setRotateM(mRotationMatrix, 0, mAngle, 0, 0, -1.0f);
// Combine the rotation matrix with the projection and camera view
Matrix.multiplyMM(mMVPMatrix, 0, mRotationMatrix, 0, mMVPMatrix, 0);
// Draw triangle
mTriangle.draw(mMVPMatrix);
}
Default behaviour:
With my code:
Thanks for a icrev comment:
You can not do translation / rotation / scaling on MVP matrix and get results as you expect.
you must translate / rotate your object in model matrix (or in View matrix for camera trans/rotation).
Look at this The purpose of Model View Projection Matrix to understand better what you need to do
These are the steps:
set M matrix to Identity matrix. Translate or rotate it. Be aware of gimbal lock (en.wikipedia.org/wiki/Gimbal_lock)
set V matrix Matrix.setLookAtM(mVMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f); 3. you already have projection matrix (in your case mProjMatrix)
multippy M * V * P to recieve final MVP matrix