welcome to linkAR technical documentation

previous

Controlling the camera position

Spherical 3D polar coordinates

Here is introduced how to manage the camera position to place according to Spherical 3D polar coordinates. This coordinates make the movement in 3D spherical environments in a much simpler way (compared to cartesian coordinates).

The mathematic conversion from spherical to cartesian coordinates is ruled by the following functions:

x=r*sinθ*cosφ
y=r*cosθ
z=r*sinθ*sinφ

The Programmatic equivalence of that operations is the following:

private static float[] getCameraPosition(float radius, float theta, float phi){
		Log.i(TAG, "R:"+radius+", T:"+theta+" P:"+phi);
		float [] result=new float[3];
		result[0]=(float)(radius*Math.sin(theta)*Math.cos(phi));
		result[1]=(float) (radius*Math.cos(theta));
		result[2]=(float)(radius*Math.sin(theta)*Math.sin(phi));
 
		return result;
	}

*Note the second slot in the coordinate array controls the altitude (Z) to match the 3DRender engine axis

Implementation

In this example, the application will manage the camera position moving according to the spherical coordinates. However, as the camera function works with cartesian coordinates, we will need to apply the conversion from spherical to cartesian.

// Values arrays
private float actualScale;
private float angleMove1;
private float angleMove2;

This variables will hold the position of the camera at any time. 7 Buttons will be added into the activity to control the camera:



  1. Up and down buttons manage angleMove1;
  2. Left and right buttons manage angleMove2;
  3. Plus and minus buttons manage the scale;

This is the layout elements distribution:

To initialize the buttons, use the following code:

 /* Buttons */
        moveUpButton=(Button)findViewById(R.id.moveUpButton);
        moveDownButton=(Button)findViewById(R.id.moveDownButton);
        moveLeftButton=(Button)findViewById(R.id.moveLeftButton);
        moveRightButton=(Button)findViewById(R.id.moveRightButton);
 
        zoomInButton=(Button)findViewById(R.id.zoomInButton);
        zoomOutButton=(Button)findViewById(R.id.zoomOutButton);
 
        resetButton=(Button)findViewById(R.id.resetButton);
 
        moveUpButton.setOnClickListener(this);
        moveDownButton.setOnClickListener(this);
        moveLeftButton.setOnClickListener(this);
        moveRightButton.setOnClickListener(this);
        zoomInButton.setOnClickListener(this);
        zoomOutButton.setOnClickListener(this);
        resetButton.setOnClickListener(this);

It is necessary to implement the OnClickListener interface:

public class MainActivity extends Activity implements EngineRenderCallbacks, OnClickListener{
...
}

And necessarily code its onClick method:

@Override
	public void onClick(View arg0) {
		Log.i(TAG, "ANGLE:"+angleMove1);
                // 1.- 
		angleMove1=(float)( Math.IEEEremainder(angleMove1, 2*Math.PI)); 
               	Log.i(TAG, "ANGLE REST:"+angleMove1);
 
		// 2.- 
                if(arg0.equals(moveUpButton)){
                	// 3.- 
			if(angleMove1>=-Math.PI/2&&angleMove1<=Math.PI/2){
 
				float newAngle=(angleMove1+ANGLESTEP);
				if(newAngle>=-Math.PI/2&&newAngle<=Math.PI/2)
					angleMove1=newAngle;
 
			}else{
				float newAngle=(angleMove1-ANGLESTEP);
				if(newAngle<=-Math.PI/2||newAngle>=Math.PI/2)
					angleMove1=newAngle;
 
			}
			// 4.- 		
         	        camPosition=getCameraPosition(actualScale, angleMove1, angleMove2);
			gl3DEngine.setCameraPosition(camPosition);	
		}else if(arg0.equals(moveDownButton)){
 
			if(angleMove1>=-Math.PI/2&&angleMove1<Math.PI/2){
				float newAngle=(angleMove1-ANGLESTEP);
				if(newAngle>=-Math.PI/2&&newAngle<=Math.PI/2)
					angleMove1=newAngle;
			}else{
				float newAngle=(angleMove1+ANGLESTEP);
				if(newAngle<=-Math.PI/2||newAngle>=Math.PI/2)
					angleMove1=newAngle;
			}
			camPosition=getCameraPosition(actualScale, angleMove1, angleMove2);
			gl3DEngine.setCameraPosition(camPosition);	
		}else if(arg0.equals(moveLeftButton)){
                        // 5.- 
			angleMove2=(float) (angleMove2+ANGLESTEP);
			camPosition=getCameraPosition(actualScale, angleMove1, angleMove2);
			gl3DEngine.setCameraPosition(camPosition);	
 
		}else if(arg0.equals(moveRightButton)){
			angleMove2=(float) (angleMove2-ANGLESTEP);
			camPosition=getCameraPosition(actualScale, angleMove1, angleMove2);
			gl3DEngine.setCameraPosition(camPosition);	
		}else if(arg0.equals(zoomInButton)){
                    	// 6.- 
			actualScale=actualScale*DECREASINGSCALEFACTOR;
			camPosition=getCameraPosition(actualScale, angleMove1, angleMove2);
			gl3DEngine.setCameraPosition(camPosition);	  
 
		}else if(arg0.equals(zoomOutButton)){
			actualScale=actualScale*INCREASINGSCALEFACTOR;
			camPosition=getCameraPosition(actualScale, angleMove1, angleMove2);
			gl3DEngine.setCameraPosition(camPosition);
		}else if(arg0.equals(resetButton)){
                        // 7.- 
			angleMove1=(float)ANGLEMOVE1INITIALVALUE;
			angleMove2= (float)ANGLEMOVE2INITIALVALUE;
			actualScale=ACTUALSCALEINITIALVALUE;
			camPosition=getCameraPosition(actualScale, angleMove1, angleMove2);
			gl3DEngine.setCameraPosition(camPosition);	  
 
 
		}
	}
  1. We ensure that the angles are in smallest range [0,2π], any angle out of that range is just a repeated one.
  2. This IF series determine which button has been pressed.
  3. For the Up and Down movement, it is needed to distinguish between the case in which the angle is in the first half or in the second half.
  4. After changing the coordinates, the camera position is recalculated.
  5. Left and right moves do not need to be filtered.
  6. Modifying the coordinate radius will set the camera closer or further to the object. This will display the object greater or smaller, respectively.
  7. A reset will place the camera at the start position.

The start position is defined in constants, in this case the following values have been used:

private static final float DECREASINGSCALEFACTOR = 0.8f;
	private static final float INCREASINGSCALEFACTOR = 1.2f;
	private static final float ACTUALSCALEINITIALVALUE = 275;
	private static final double ANGLEMOVE1INITIALVALUE = Math.PI;
	private static final double ANGLEMOVE2INITIALVALUE = -Math.PI/2;
	private static final float ANGLESTEP = (float) (Math.PI/10);
 

previous