ArViewer  Android Version
Arvos - Augmented reality viewer open source
 All Classes Namespaces Files Functions Variables
ArvosObject.java
Go to the documentation of this file.
1 /*
2  Copyright (C) 2013, Peter Graf
3 
4  This file is part of Arvos - AR Viewer Open Source for Android.
5  Arvos is free software.
6 
7  This program is free software: you can redistribute it and/or modify
8  it under the terms of the GNU General Public License as published by
9  the Free Software Foundation, either version 3 of the License, or
10  (at your option) any later version.
11 
12  This program is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with this program. If not, see <http://www.gnu.org/licenses/>.
19 
20  For more information on the AR Viewer Open Source or Peter Graf,
21  please see: http://www.mission-base.com/.
22  */
23 
24 package com.mission_base.arviewer_android.viewer.opengl;
25 
26 import javax.microedition.khronos.opengles.GL10;
27 
28 import android.graphics.Bitmap;
29 
30 import com.mission_base.arviewer_android.Arvos;
31 import com.mission_base.arviewer_android.viewer.utilities.MatrixUtils;
32 
39 public class ArvosObject extends Square
40 {
41  public static final String BillboardHandlingNone = "none";
42  public static final String BillboardHandlingCylinder = "cylinder";
43  public static final String BillboardHandlingSphere = "sphere";
44 
45  public int mId;
46  public String mName;
47  public String mTexture;
48  public float[] mPosition;
49  public float[] mScale;
50  public float[] mRotation;
51  public String mBillboardHandling;
52 
53  public Bitmap mBitmap;
54 
55  public boolean mTextureLoaded = false;
56 
57  private Arvos mInstance;
58 
59  public ArvosObject(int id)
60  {
61  mId = id;
62  mInstance = Arvos.getInstance();
63  }
64 
68  public void draw(GL10 gl)
69  {
70  if (!mTextureLoaded)
71  {
72  if (mBitmap != null)
73  {
75  }
76 
77  mTextureLoaded = true;
78  }
79 
80  // Take the device orientation into account
81  //
82  gl.glRotatef(mInstance.getRotationDegrees(), 0f, 0f, 1f);
83 
84  // The device coordinates are flat on the table with X east, Y north and
85  // Z up.
86  // The world coordinates are X east, Y up and Z north
87  //
88  gl.glRotatef(90, 1f, 0f, 0f);
89 
90  // Apply azimut, pitch and roll of the device
91  //
92  gl.glRotatef(mInstance.mRoll, 0f, 0f, -1f);
93  gl.glRotatef(mInstance.mPitch, 1f, 0f, 0f);
94  gl.glRotatef(mInstance.mAzimuth, 0f, 1f, 0f);
95 
96  float x = 0f;
97  float y = 0f;
98  float z = 0f;
99 
100  if (mPosition != null && mPosition.length == 3)
101  {
102  x = mPosition[0];
103  y = mPosition[1];
104  z = mPosition[2];
105  }
106 
107  // Move the object
108  //
109  gl.glTranslatef(x, y, z);
110 
111  // Make it face the camera
112  //
114  {
115  l3dBillboardCylindricalBegin(gl, 0f, 0f, 0f, x, y, z);
116  }
118  {
119  l3dBillboardSphericalBegin(gl, 0f, 0f, 0f, x, y, z);
120  }
121 
122  if (mRotation != null && mRotation.length == 4)
123  {
124  gl.glRotatef(mRotation[3], mRotation[0], mRotation[1], mRotation[2]);
125  }
126 
127  if (mScale != null && mScale.length == 3)
128  {
129  gl.glScalef(mScale[0], mScale[1], mScale[2]);
130  }
131 
132  super.draw(gl);
133  }
134 
149  protected void l3dBillboardSphericalBegin(GL10 gl, float camX, float camY, float camZ, float posX, float posY, float posZ)
150  {
151  float[] lookAt = new float[] { 0, 0, 1 };
152  float[] objToCamProj = new float[3];
153  float[] objToCam = new float[3];
154  float[] upAux = new float[3];
155  float angleCosine;
156 
157  // objToCamProj is the vector in world coordinates from the local origin
158  // to the camera
159  // projected in the XZ plane
160  objToCamProj[0] = camX - posX;
161  objToCamProj[1] = 0;
162  objToCamProj[2] = camZ - posZ;
163 
164  // normalize both vectors to get the cosine directly afterwards
165  MatrixUtils.normalize(objToCamProj);
166 
167  // easy fix to determine whether the angle is negative or positive
168  // for positive angles upAux will be a vector pointing in the
169  // positive y direction, otherwise upAux will point downwards
170  // effectively reversing the rotation.
171 
172  MatrixUtils.cross(lookAt, objToCamProj, upAux);
173 
174  // compute the angle
175  angleCosine = MatrixUtils.dot(lookAt, objToCamProj);
176 
177  // perform the rotation. The if statement is used for stability reasons
178  // if the lookAt and v vectors are too close together then |aux| could
179  // be bigger than 1 due to lack of precision
180  if ((angleCosine < 0.99990) && (angleCosine > -0.9999))
181  {
182  float f = Arvos.toDegrees((float) (Math.acos(angleCosine)));
183  MatrixUtils.normalize(upAux);
184  gl.glRotatef(f, upAux[0], upAux[1], upAux[2]);
185  }
186 
187  // objToCam is the vector in world coordinates from the local origin to
188  // the camera
189  objToCam[0] = camX - posX;
190  objToCam[1] = camY - posY;
191  objToCam[2] = camZ - posZ;
192 
193  // Normalize to get the cosine afterwards
194  MatrixUtils.normalize(objToCam);
195 
196  // Compute the angle between v and v2, i.e. compute the
197  // required angle for the lookup vector
198  angleCosine = MatrixUtils.dot(objToCamProj, objToCam);
199 
200  // Tilt the object. The test is done to prevent instability when
201  // objToCam and objToCamProj have a very small
202  // angle between them
203  if ((angleCosine < 0.99990) && (angleCosine > -0.9999))
204  {
205  if (objToCam[1] < 0)
206  {
207  float f = Arvos.toDegrees((float) (Math.acos(angleCosine)));
208  gl.glRotatef(f, 1, 0, 0);
209  }
210  else
211  {
212  float f = Arvos.toDegrees((float) (Math.acos(angleCosine)));
213  gl.glRotatef(f, -1, 0, 0);
214  }
215  }
216  }
217 
232  public static float l3dBillboardCylindricalDegrees(float camX, float camY, float camZ, float posX, float posY, float posZ, float[] pUpAux)
233  {
234  float[] lookAt = new float[] { 0, 0, 1 };
235  float[] objToCamProj = new float[3];
236  float[] upAux = pUpAux;
237  if (upAux == null)
238  {
239  upAux = new float[3];
240  }
241  float angleCosine;
242 
243  // objToCamProj is the vector in world coordinates from the local origin
244  // to the camera
245  // projected in the XZ plane
246  objToCamProj[0] = camX - posX;
247  objToCamProj[1] = 0;
248  objToCamProj[2] = camZ - posZ;
249 
250  // normalize both vectors to get the cosine directly afterwards
251  MatrixUtils.normalize(objToCamProj);
252 
253  // easy fix to determine whether the angle is negative or positive
254  // for positive angles upAux will be a vector pointing in the
255  // positive y direction, otherwise upAux will point downwards
256  // effectively reversing the rotation.
257 
258  MatrixUtils.cross(lookAt, objToCamProj, upAux);
259 
260  // compute the angle
261  angleCosine = MatrixUtils.dot(lookAt, objToCamProj);
262 
263  // perform the rotation. The if statement is used for stability reasons
264  // if the lookAt and v vectors are too close together then |aux| could
265  // be bigger than 1 due to lack of precision
266  if ((angleCosine < 0.99990) && (angleCosine > -0.9999))
267  {
268  return Arvos.toDegrees((float) (Math.acos(angleCosine)));
269  }
270  return Float.NaN;
271  }
272 
284  public static void l3dBillboardCylindricalBegin(GL10 gl, float camX, float camY, float camZ, float posX, float posY, float posZ)
285  {
286  float[] upAux = new float[3];
287  float f = l3dBillboardCylindricalDegrees(camX, camY, camZ, posX, posY, posZ, upAux);
288  if (!Float.isNaN(f))
289  {
290  gl.glRotatef(f, upAux[0], upAux[1], upAux[2]);
291  }
292  }
293 }