();
+ for (int n = 0; n < children.size(); n++) {
+ newInstance.children.add(children.get(n).cloneTree());
+ }
+ return newInstance;
+ }
+}
diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/ImageComponent.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/ImageComponent.java
new file mode 100644
index 0000000..9c00a74
--- /dev/null
+++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/ImageComponent.java
@@ -0,0 +1,16 @@
+package com.google.ar.core.examples.java.common.java3d;
+
+public abstract class ImageComponent extends NodeComponent {
+ public static final int FORMAT_RGB = 1;
+ public static final int FORMAT_RGBA = 2;
+
+ protected int format = 0;
+ protected int width = 0;
+ protected int height = 0;
+
+ public ImageComponent(int format, int width, int height) {
+ this.format = format;
+ this.width = width;
+ this.height = height;
+ }
+}
diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/ImageComponent2D.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/ImageComponent2D.java
new file mode 100644
index 0000000..1235eae
--- /dev/null
+++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/ImageComponent2D.java
@@ -0,0 +1,23 @@
+package com.google.ar.core.examples.java.common.java3d;
+
+import android.graphics.Bitmap;
+
+public class ImageComponent2D extends ImageComponent {
+ private Bitmap image = null;
+
+ public ImageComponent2D(int format, Bitmap image) {
+ super(format, image.getWidth(), image.getHeight());
+ this.image = image;
+ }
+
+ public Bitmap getBitmap() {
+ return image;
+ }
+
+ @Override
+ public NodeComponent cloneNodeComponent() {
+ ImageComponent2D newOne = new ImageComponent2D(format, image);
+ return newOne;
+ }
+
+}
diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/IndexedGeometryArray.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/IndexedGeometryArray.java
new file mode 100644
index 0000000..ba9aa40
--- /dev/null
+++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/IndexedGeometryArray.java
@@ -0,0 +1,89 @@
+package com.google.ar.core.examples.java.common.java3d;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.ShortBuffer;
+
+public abstract class IndexedGeometryArray extends GeometryArray {
+
+ protected int indexCount;
+ protected ShortBuffer coordinateIndexBuffer = null;
+ protected ShortBuffer texCoordinateIndexBuffer = null;
+ protected ShortBuffer normalIndexBuffer = null;
+
+ // コンストラクタ
+ public IndexedGeometryArray(int vertexCount, int vertexFormat, int indexCount) {
+ super(vertexCount, vertexFormat);
+ this.indexCount = indexCount;
+ ByteBuffer vbb = ByteBuffer.allocateDirect(indexCount * 2);
+ vbb.order(ByteOrder.nativeOrder());
+ coordinateIndexBuffer = vbb.asShortBuffer();
+ if ((vertexFormat & TEXTURE_COORDINATE_2) != 0
+ || (vertexFormat & TEXTURE_COORDINATE_3) != 0
+ || (vertexFormat & TEXTURE_COORDINATE_4) != 0) {
+ ByteBuffer vbb2 = ByteBuffer.allocateDirect(indexCount * 2);
+ vbb2.order(ByteOrder.nativeOrder());
+ texCoordinateIndexBuffer = vbb2.asShortBuffer();
+ }
+ if ((vertexFormat & NORMALS) != 0) {
+ ByteBuffer vbb3 = ByteBuffer.allocateDirect(indexCount * 2);
+ vbb3.order(ByteOrder.nativeOrder());
+ normalIndexBuffer = vbb3.asShortBuffer();
+ }
+ }
+
+ /** オブジェクトのインデックス数を取得する */
+ public int getIndexCount() {
+ return indexCount;
+ }
+
+ public int getCoordinateIndex(int index) {
+ return coordinateIndexBuffer.get(index);
+ }
+
+ public void getCoordinateIndices(int index, int[] coordinateIndicies) {
+ for (int i = 0; i < coordinateIndicies.length && index + i < coordinateIndexBuffer.limit(); i++) {
+ coordinateIndicies[i] = coordinateIndexBuffer.get(index + i);
+ }
+ }
+
+ public void setCoordinateIndex(int index, int coordinateIndex) {
+ coordinateIndexBuffer.put(index, (short)coordinateIndex);
+ }
+
+ public void setCoordinateIndices(int index, int[] coordinateIndicies) {
+ coordinateIndexBuffer.position(index);
+ for (int i = 0; i < coordinateIndicies.length; i++) {
+ coordinateIndexBuffer.put((short)coordinateIndicies[i]);
+ }
+ coordinateIndexBuffer.position(0);
+ }
+
+ public int getTextureCoordinateIndex(int index) {
+ return texCoordinateIndexBuffer.get(index);
+ }
+
+ public void setTextureCoordinateIndex(int index, int texCoordIndex) {
+ coordinateIndexBuffer.put(index, (short)texCoordIndex);
+ }
+
+ public int getNormalIndex(int index) {
+ return normalIndexBuffer.get(index);
+ }
+
+ public void setNormalIndex(int index, int normalIndex) {
+ normalIndexBuffer.put(index, (short)normalIndex);
+ }
+
+ public ShortBuffer getCoordinateIndexBuffer() {
+ return coordinateIndexBuffer;
+ }
+
+ public ShortBuffer getTextureCoordinateIndexBuffer() {
+ return texCoordinateIndexBuffer;
+ }
+
+ public ShortBuffer getNormalIndexBuffer() {
+ return normalIndexBuffer;
+ }
+}
diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/IndexedGeometryStripArray.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/IndexedGeometryStripArray.java
new file mode 100644
index 0000000..682bfad
--- /dev/null
+++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/IndexedGeometryStripArray.java
@@ -0,0 +1,22 @@
+package com.google.ar.core.examples.java.common.java3d;
+
+public abstract class IndexedGeometryStripArray extends IndexedGeometryArray {
+ protected int[] stripIndexCounts;
+
+ public IndexedGeometryStripArray(int vertexCount, int vertexFormat, int indexCount, int[] stripIndexCounts) {
+ super(vertexCount, vertexFormat, indexCount);
+ this.stripIndexCounts = stripIndexCounts;
+ }
+
+ public int getNumStrips() {
+ return stripIndexCounts.length;
+ }
+
+ public void setStripIndexCounts(int[] stripIndexCounts) {
+ this.stripIndexCounts = stripIndexCounts;
+ }
+
+ public void getStripIndexCounts(int[] stripIndexCounts) {
+ System.arraycopy(this.stripIndexCounts, 0, stripIndexCounts, 0, stripIndexCounts.length);
+ }
+}
diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/IndexedTriangleArray.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/IndexedTriangleArray.java
new file mode 100644
index 0000000..a2d751c
--- /dev/null
+++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/IndexedTriangleArray.java
@@ -0,0 +1,21 @@
+package com.google.ar.core.examples.java.common.java3d;
+
+public class IndexedTriangleArray extends IndexedGeometryArray {
+ public IndexedTriangleArray(int vertexCount, int vertexFormat, int indexCount) {
+ super(vertexCount, vertexFormat, indexCount);
+ }
+
+ @Override
+ public NodeComponent cloneNodeComponent() {
+ IndexedTriangleArray newOne = new IndexedTriangleArray(vertexCount, vertexFormat, indexCount);
+ newOne.vertexBuffer = vertexBuffer.duplicate();
+ if (normalBuffer != null) newOne.normalBuffer = normalBuffer.duplicate();
+ if (uvBuffer != null) newOne.uvBuffer = uvBuffer.duplicate();
+ newOne.indexCount = indexCount;
+ newOne.coordinateIndexBuffer = coordinateIndexBuffer.duplicate();
+ if (texCoordinateIndexBuffer != null) newOne.texCoordinateIndexBuffer = texCoordinateIndexBuffer.duplicate();
+ if (normalIndexBuffer != null) newOne.normalIndexBuffer = normalIndexBuffer.duplicate();
+ return newOne;
+ }
+
+}
diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/IndexedTriangleFanArray.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/IndexedTriangleFanArray.java
new file mode 100644
index 0000000..52556e0
--- /dev/null
+++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/IndexedTriangleFanArray.java
@@ -0,0 +1,21 @@
+package com.google.ar.core.examples.java.common.java3d;
+
+public class IndexedTriangleFanArray extends IndexedGeometryStripArray {
+ public IndexedTriangleFanArray(int vertexCount, int vertexFormat, int indexCount, int[] stripIndexCounts) {
+ super(vertexCount, vertexFormat, indexCount, stripIndexCounts);
+ }
+
+ @Override
+ public NodeComponent cloneNodeComponent() {
+ IndexedGeometryArray newOne = new IndexedTriangleFanArray(vertexCount, vertexFormat, indexCount, (int [])stripIndexCounts.clone());
+ newOne.vertexBuffer = vertexBuffer.duplicate();
+ if (normalBuffer != null) newOne.normalBuffer = normalBuffer.duplicate();
+ if (uvBuffer != null) newOne.uvBuffer = uvBuffer.duplicate();
+ newOne.indexCount = indexCount;
+ newOne.coordinateIndexBuffer = coordinateIndexBuffer.duplicate();
+ if (texCoordinateIndexBuffer != null) newOne.texCoordinateIndexBuffer = texCoordinateIndexBuffer.duplicate();
+ if (normalIndexBuffer != null) newOne.normalIndexBuffer = normalIndexBuffer.duplicate();
+ return newOne;
+ }
+
+}
diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/IndexedTriangleStripArray.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/IndexedTriangleStripArray.java
new file mode 100644
index 0000000..b9a9739
--- /dev/null
+++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/IndexedTriangleStripArray.java
@@ -0,0 +1,20 @@
+package com.google.ar.core.examples.java.common.java3d;
+
+public class IndexedTriangleStripArray extends IndexedGeometryStripArray {
+ public IndexedTriangleStripArray(int vertexCount, int vertexFormat, int indexCount, int[] stripIndexCounts) {
+ super(vertexCount, vertexFormat, indexCount, stripIndexCounts);
+ }
+
+ @Override
+ public NodeComponent cloneNodeComponent() {
+ IndexedTriangleStripArray newOne = new IndexedTriangleStripArray(vertexCount, vertexFormat, indexCount, (int [])stripIndexCounts.clone());
+ newOne.vertexBuffer = vertexBuffer.duplicate();
+ if (normalBuffer != null) newOne.normalBuffer = normalBuffer.duplicate();
+ if (uvBuffer != null) newOne.uvBuffer = uvBuffer.duplicate();
+ newOne.indexCount = indexCount;
+ newOne.coordinateIndexBuffer = coordinateIndexBuffer.duplicate();
+ if (texCoordinateIndexBuffer != null) newOne.texCoordinateIndexBuffer = texCoordinateIndexBuffer.duplicate();
+ if (normalIndexBuffer != null) newOne.normalIndexBuffer = normalIndexBuffer.duplicate();
+ return newOne;
+ }
+}
diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Leaf.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Leaf.java
new file mode 100644
index 0000000..91950b9
--- /dev/null
+++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Leaf.java
@@ -0,0 +1,5 @@
+package com.google.ar.core.examples.java.common.java3d;
+
+public abstract class Leaf extends Node {
+
+}
diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Light.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Light.java
new file mode 100644
index 0000000..a20b4c1
--- /dev/null
+++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Light.java
@@ -0,0 +1,17 @@
+package com.google.ar.core.examples.java.common.java3d;
+
+public abstract class Light extends Leaf {
+ protected Color3f color;
+
+ public Light(Color3f c) {
+ this.color = c;
+ }
+
+ public void setColor(Color3f c) {
+ color = c;
+ }
+
+ public Color3f getColor() {
+ return color;
+ }
+}
diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Material.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Material.java
new file mode 100644
index 0000000..f8c426b
--- /dev/null
+++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Material.java
@@ -0,0 +1,73 @@
+package com.google.ar.core.examples.java.common.java3d;
+
+public class Material extends NodeComponent {
+ float diffuseR = 1.0f;
+ float diffuseG = 1.0f;
+ float diffuseB = 1.0f;
+ float ambientR = 0.2f;
+ float ambientG = 0.2f;
+ float ambientB = 0.2f;
+ float specularR = 1.0f;
+ float specularG = 1.0f;
+ float specularB = 1.0f;
+ float emissiveR = 0.0f;
+ float emissiveG = 0.0f;
+ float emissiveB = 0.0f;
+ float shininess = 64.0f;
+ float[] diffuse = null;
+ float[] ambient = null;
+ float[] specular = null;
+ float[] emissive = null;
+
+ public Material() {
+ setDiffuseColor(1.0f, 1.0f, 1.0f);
+ setAmbientColor(0.2f, 0.2f, 0.2f);
+ setSpecularColor(1.0f, 1.0f, 1.0f);
+ setEmissiveColor(0.0f, 0.0f, 0.0f);
+ setShininess(64.0f);
+ }
+
+ public void setDiffuseColor(float r, float g, float b) {
+ this.diffuseR = r;
+ this.diffuseG = g;
+ this.diffuseB = b;
+ diffuse = new float[]{r, g ,b, 1.0f};
+ }
+
+ public void setAmbientColor(float r, float g, float b) {
+ this.ambientR = r;
+ this.ambientG = g;
+ this.ambientB = b;
+ ambient = new float[]{r, g ,b, 1.0f};
+ }
+
+ public void setSpecularColor(float r, float g, float b) {
+ this.specularR = r;
+ this.specularG = g;
+ this.specularB = b;
+ specular = new float[]{r, g ,b, 1.0f};
+ }
+
+ public void setEmissiveColor(float r, float g, float b) {
+ this.emissiveR = r;
+ this.emissiveG = g;
+ this.emissiveB = b;
+ emissive = new float[]{r, g ,b, 1.0f};
+ }
+
+ public void setShininess(float shininess) {
+ this.shininess = shininess;
+ }
+
+ @Override
+ public NodeComponent cloneNodeComponent() {
+ Material m = new Material();
+ m.setDiffuseColor(diffuseR, diffuseG, diffuseB);
+ m.setAmbientColor(ambientR, ambientG, ambientB);
+ m.setSpecularColor(specularR, specularG, specularB);
+ m.setEmissiveColor(emissiveR, emissiveG, emissiveB);
+ m.setShininess(shininess);
+ return m;
+ }
+
+}
diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Matrix3d.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Matrix3d.java
new file mode 100644
index 0000000..017eaf8
--- /dev/null
+++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Matrix3d.java
@@ -0,0 +1,484 @@
+package com.google.ar.core.examples.java.common.java3d;
+
+public class Matrix3d {
+ public double m00;
+ public double m01;
+ public double m02;
+ public double m10;
+ public double m11;
+ public double m12;
+ public double m20;
+ public double m21;
+ public double m22;
+
+ // コンストラクタ
+ public Matrix3d() {
+ m00 = 0.0;
+ m01 = 0.0;
+ m02 = 0.0;
+ m10 = 0.0;
+ m11 = 0.0;
+ m12 = 0.0;
+ m20 = 0.0;
+ m21 = 0.0;
+ m22 = 0.0;
+ }
+
+ public Matrix3d(double[] v) {
+ m00 = v[0];
+ m01 = v[1];
+ m02 = v[2];
+ m10 = v[3];
+ m11 = v[4];
+ m12 = v[5];
+ m20 = v[6];
+ m21 = v[7];
+ m22 = v[8];
+ }
+
+ public Matrix3d(double m00, double m01, double m02, double m10, double m11,
+ double m12, double m20, double m21, double m22) {
+ this.m00 = m00;
+ this.m01 = m01;
+ this.m02 = m02;
+ this.m10 = m10;
+ this.m11 = m11;
+ this.m12 = m12;
+ this.m20 = m20;
+ this.m21 = m21;
+ this.m22 = m22;
+ }
+
+ public Matrix3d(Matrix3d m1) {
+ this.m00 = m1.m00;
+ this.m01 = m1.m01;
+ this.m02 = m1.m02;
+ this.m10 = m1.m10;
+ this.m11 = m1.m11;
+ this.m12 = m1.m12;
+ this.m20 = m1.m20;
+ this.m21 = m1.m21;
+ this.m22 = m1.m22;
+ }
+
+ public Matrix3d clone() {
+ return new Matrix3d(m00, m01, m02, m10, m11, m12, m20, m21, m22);
+ }
+
+ /** 各要素に足す */
+ public void add(Matrix3d m1) {
+ this.m00 += m1.m00;
+ this.m01 += m1.m01;
+ this.m02 += m1.m02;
+ this.m10 += m1.m10;
+ this.m11 += m1.m11;
+ this.m12 += m1.m12;
+ this.m20 += m1.m20;
+ this.m21 += m1.m21;
+ this.m22 += m1.m22;
+ }
+
+ /** 二つの行列の和を格納する */
+ public void add(Matrix3d m1, Matrix3d m2) {
+ this.m00 = m1.m00 + m2.m00;
+ this.m01 = m1.m01 + m2.m01;
+ this.m02 = m1.m02 + m2.m02;
+ this.m10 = m1.m10 + m2.m10;
+ this.m11 = m1.m11 + m2.m11;
+ this.m12 = m1.m12 + m2.m12;
+ this.m20 = m1.m20 + m2.m20;
+ this.m21 = m1.m21 + m2.m21;
+ this.m22 = m1.m22 + m2.m22;
+ }
+
+ /** x軸周りにangle回転 */
+ public void rotX(double angle) {
+ double d1 = Math.sin(angle);
+ double d2 = Math.cos(angle);
+ m00 = 1.0D;
+ m01 = 0.0D;
+ m02 = 0.0D;
+ m10 = 0.0D;
+ m11 = d2;
+ m12 = -d1;
+ m20 = 0.0D;
+ m21 = d1;
+ m22 = d2;
+ }
+
+ /** y軸周りにangle回転 */
+ public void rotY(double angle) {
+ double d1 = Math.sin(angle);
+ double d2 = Math.cos(angle);
+ m00 = d2;
+ m01 = 0.0D;
+ m02 = d1;
+ m10 = 0.0D;
+ m11 = 1.0D;
+ m12 = 0.0D;
+ m20 = -d1;
+ m21 = 0.0D;
+ m22 = d2;
+ }
+
+ /** z軸周りにangle1回転 */
+ public void rotZ(double angle) {
+ double d1 = Math.sin(angle);
+ double d2 = Math.cos(angle);
+ m00 = d2;
+ m01 = -d1;
+ m02 = 0.0D;
+ m10 = d1;
+ m11 = d2;
+ m12 = 0.0D;
+ m20 = 0.0D;
+ m21 = 0.0D;
+ m22 = 1.0D;
+ }
+
+ /** 自分に引数の行列を掛けたものを格納する */
+ public void mul(Matrix3d mat) {
+ double t00 = m00 * mat.m00 + m01 * mat.m10 + m02 * mat.m20;
+ double t01 = m00 * mat.m01 + m01 * mat.m11 + m02 * mat.m21;
+ double t02 = m00 * mat.m02 + m01 * mat.m12 + m02 * mat.m22;
+ double t10 = m10 * mat.m10 + m11 * mat.m10 + m12 * mat.m20;
+ double t11 = m10 * mat.m11 + m11 * mat.m11 + m12 * mat.m21;
+ double t12 = m10 * mat.m12 + m11 * mat.m12 + m12 * mat.m22;
+ double t20 = m20 * mat.m20 + m21 * mat.m10 + m22 * mat.m20;
+ double t21 = m20 * mat.m21 + m21 * mat.m11 + m22 * mat.m21;
+ double t22 = m20 * mat.m22 + m21 * mat.m12 + m22 * mat.m22;
+
+ set(t00, t01, t02, t10, t11, t12, t20, t21, t22);
+ }
+
+ public void mul(Matrix3d mat1, Matrix3d mat2) {
+ this.set(mat1.m00, mat1.m01, mat1.m02, mat1.m10, mat1.m11, mat1.m12, mat1.m20, mat1.m21, mat1.m22);
+ mul(mat2);
+ }
+
+ public void set(double t00, double t01, double t02, double t10, double t11,
+ double t12, double t20, double t21, double t22) {
+ m00 = t00;
+ m01 = t01;
+ m02 = t02;
+ m10 = t10;
+ m11 = t11;
+ m12 = t12;
+ m20 = t20;
+ m21 = t21;
+ m22 = t22;
+ }
+
+ /**
+ * Sets the value of this matrix to the matrix inverse of the passed matrix
+ * m1.
+ *
+ * @param m1
+ * the matrix to be inverted
+ */
+ public final void invert(Matrix3d m1) {
+ invertGeneral(m1);
+ }
+
+ /**
+ * Inverts this matrix in place.
+ */
+ public final void invert() {
+ invertGeneral(this);
+ }
+
+ /**
+ * General invert routine. Inverts m1 and places the result in "this". Note
+ * that this routine handles both the "this" version and the non-"this"
+ * version.
+ *
+ * Also note that since this routine is slow anyway, we won't worry about
+ * allocating a little bit of garbage.
+ */
+ private final void invertGeneral(Matrix3d m1) {
+ double result[] = new double[9];
+ int row_perm[] = new int[3];
+ int i, r, c;
+ double[] tmp = new double[9]; // scratch matrix
+
+ // Use LU decomposition and backsubstitution code specifically
+ // for floating-point 3x3 matrices.
+
+ // Copy source matrix to t1tmp
+ tmp[0] = m1.m00;
+ tmp[1] = m1.m01;
+ tmp[2] = m1.m02;
+
+ tmp[3] = m1.m10;
+ tmp[4] = m1.m11;
+ tmp[5] = m1.m12;
+
+ tmp[6] = m1.m20;
+ tmp[7] = m1.m21;
+ tmp[8] = m1.m22;
+
+ // Calculate LU decomposition: Is the matrix singular?
+ if (!luDecomposition(tmp, row_perm)) {
+ // Matrix has no inverse
+ // throw new
+ // SingularMatrixException(VecMathI18N.getString("Matrix3d12"));
+ }
+
+ // Perform back substitution on the identity matrix
+ for (i = 0; i < 9; i++)
+ result[i] = 0.0;
+ result[0] = 1.0;
+ result[4] = 1.0;
+ result[8] = 1.0;
+ luBacksubstitution(tmp, row_perm, result);
+
+ this.m00 = result[0];
+ this.m01 = result[1];
+ this.m02 = result[2];
+
+ this.m10 = result[3];
+ this.m11 = result[4];
+ this.m12 = result[5];
+
+ this.m20 = result[6];
+ this.m21 = result[7];
+ this.m22 = result[8];
+
+ }
+
+ /**
+ * Given a 3x3 array "matrix0", this function replaces it with the LU
+ * decomposition of a row-wise permutation of itself. The input parameters
+ * are "matrix0" and "dimen". The array "matrix0" is also an output
+ * parameter. The vector "row_perm[3]" is an output parameter that contains
+ * the row permutations resulting from partial pivoting. The output
+ * parameter "even_row_xchg" is 1 when the number of row exchanges is even,
+ * or -1 otherwise. Assumes data type is always double.
+ *
+ * This function is similar to luDecomposition, except that it is tuned
+ * specifically for 3x3 matrices.
+ *
+ * @return true if the matrix is nonsingular, or false otherwise.
+ */
+ //
+ // Reference: Press, Flannery, Teukolsky, Vetterling,
+ // _Numerical_Recipes_in_C_, Cambridge University Press,
+ // 1988, pp 40-45.
+ //
+ static boolean luDecomposition(double[] matrix0, int[] row_perm) {
+
+ double row_scale[] = new double[3];
+
+ // Determine implicit scaling information by looping over rows
+ {
+ int i, j;
+ int ptr, rs;
+ double big, temp;
+
+ ptr = 0;
+ rs = 0;
+
+ // For each row ...
+ i = 3;
+ while (i-- != 0) {
+ big = 0.0;
+
+ // For each column, find the largest element in the row
+ j = 3;
+ while (j-- != 0) {
+ temp = matrix0[ptr++];
+ temp = Math.abs(temp);
+ if (temp > big) {
+ big = temp;
+ }
+ }
+
+ // Is the matrix singular?
+ if (big == 0.0) {
+ return false;
+ }
+ row_scale[rs++] = 1.0 / big;
+ }
+ }
+
+ {
+ int j;
+ int mtx;
+
+ mtx = 0;
+
+ // For all columns, execute Crout's method
+ for (j = 0; j < 3; j++) {
+ int i, imax, k;
+ int target, p1, p2;
+ double sum, big, temp;
+
+ // Determine elements of upper diagonal matrix U
+ for (i = 0; i < j; i++) {
+ target = mtx + (3 * i) + j;
+ sum = matrix0[target];
+ k = i;
+ p1 = mtx + (3 * i);
+ p2 = mtx + j;
+ while (k-- != 0) {
+ sum -= matrix0[p1] * matrix0[p2];
+ p1++;
+ p2 += 3;
+ }
+ matrix0[target] = sum;
+ }
+
+ // Search for largest pivot element and calculate
+ // intermediate elements of lower diagonal matrix L.
+ big = 0.0;
+ imax = -1;
+ for (i = j; i < 3; i++) {
+ target = mtx + (3 * i) + j;
+ sum = matrix0[target];
+ k = j;
+ p1 = mtx + (3 * i);
+ p2 = mtx + j;
+ while (k-- != 0) {
+ sum -= matrix0[p1] * matrix0[p2];
+ p1++;
+ p2 += 3;
+ }
+ matrix0[target] = sum;
+
+ // Is this the best pivot so far?
+ if ((temp = row_scale[i] * Math.abs(sum)) >= big) {
+ big = temp;
+ imax = i;
+ }
+ }
+
+ if (imax < 0) {
+ // throw new
+ // RuntimeException(VecMathI18N.getString("Matrix3d13"));
+ }
+
+ // Is a row exchange necessary?
+ if (j != imax) {
+ // Yes: exchange rows
+ k = 3;
+ p1 = mtx + (3 * imax);
+ p2 = mtx + (3 * j);
+ while (k-- != 0) {
+ temp = matrix0[p1];
+ matrix0[p1++] = matrix0[p2];
+ matrix0[p2++] = temp;
+ }
+
+ // Record change in scale factor
+ row_scale[imax] = row_scale[j];
+ }
+
+ // Record row permutation
+ row_perm[j] = imax;
+
+ // Is the matrix singular
+ if (matrix0[(mtx + (3 * j) + j)] == 0.0) {
+ return false;
+ }
+
+ // Divide elements of lower diagonal matrix L by pivot
+ if (j != (3 - 1)) {
+ temp = 1.0 / (matrix0[(mtx + (3 * j) + j)]);
+ target = mtx + (3 * (j + 1)) + j;
+ i = 2 - j;
+ while (i-- != 0) {
+ matrix0[target] *= temp;
+ target += 3;
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Solves a set of linear equations. The input parameters "matrix1", and
+ * "row_perm" come from luDecompostionD3x3 and do not change here. The
+ * parameter "matrix2" is a set of column vectors assembled into a 3x3
+ * matrix of floating-point values. The procedure takes each column of
+ * "matrix2" in turn and treats it as the right-hand side of the matrix
+ * equation Ax = LUx = b. The solution vector replaces the original column
+ * of the matrix.
+ *
+ * If "matrix2" is the identity matrix, the procedure replaces its contents
+ * with the inverse of the matrix from which "matrix1" was originally
+ * derived.
+ */
+ //
+ // Reference: Press, Flannery, Teukolsky, Vetterling,
+ // _Numerical_Recipes_in_C_, Cambridge University Press,
+ // 1988, pp 44-45.
+ //
+ static void luBacksubstitution(double[] matrix1, int[] row_perm,
+ double[] matrix2) {
+
+ int i, ii, ip, j, k;
+ int rp;
+ int cv, rv;
+
+ // rp = row_perm;
+ rp = 0;
+
+ // For each column vector of matrix2 ...
+ for (k = 0; k < 3; k++) {
+ // cv = &(matrix2[0][k]);
+ cv = k;
+ ii = -1;
+
+ // Forward substitution
+ for (i = 0; i < 3; i++) {
+ double sum;
+
+ ip = row_perm[rp + i];
+ sum = matrix2[cv + 3 * ip];
+ matrix2[cv + 3 * ip] = matrix2[cv + 3 * i];
+ if (ii >= 0) {
+ // rv = &(matrix1[i][0]);
+ rv = i * 3;
+ for (j = ii; j <= i - 1; j++) {
+ sum -= matrix1[rv + j] * matrix2[cv + 3 * j];
+ }
+ } else if (sum != 0.0) {
+ ii = i;
+ }
+ matrix2[cv + 3 * i] = sum;
+ }
+
+ // Backsubstitution
+ // rv = &(matrix1[3][0]);
+ rv = 2 * 3;
+ matrix2[cv + 3 * 2] /= matrix1[rv + 2];
+
+ rv -= 3;
+ matrix2[cv + 3 * 1] = (matrix2[cv + 3 * 1] - matrix1[rv + 2]
+ * matrix2[cv + 3 * 2])
+ / matrix1[rv + 1];
+
+ rv -= 3;
+ matrix2[cv + 4 * 0] = (matrix2[cv + 3 * 0] - matrix1[rv + 1]
+ * matrix2[cv + 3 * 1] - matrix1[rv + 2]
+ * matrix2[cv + 3 * 2])
+ / matrix1[rv + 0];
+
+ }
+ }
+
+ /**
+ * Multiply this matrix by the tuple t and place the result
+ * back into the tuple (t = this*t).
+ * @param t the tuple to be multiplied by this matrix and then replaced
+ */
+ public final void transform(Tuple3d t) {
+ double x,y,z;
+ x = m00* t.x + m01*t.y + m02*t.z;
+ y = m10* t.x + m11*t.y + m12*t.z;
+ z = m20* t.x + m21*t.y + m22*t.z;
+ t.set(x,y,z);
+ }
+}
diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Matrix4d.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Matrix4d.java
new file mode 100644
index 0000000..3c9e1b5
--- /dev/null
+++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Matrix4d.java
@@ -0,0 +1,216 @@
+package com.google.ar.core.examples.java.common.java3d;
+
+public class Matrix4d {
+ public double m00;
+ public double m01;
+ public double m02;
+ public double m03;
+ public double m10;
+ public double m11;
+ public double m12;
+ public double m13;
+ public double m20;
+ public double m21;
+ public double m22;
+ public double m23;
+ public double m30;
+ public double m31;
+ public double m32;
+ public double m33;
+
+ // コンストラクタ
+ public Matrix4d() {
+ m00 = 1.0;
+ m01 = 0.0;
+ m02 = 0.0;
+ m03 = 0.0;
+ m10 = 0.0;
+ m11 = 1.0;
+ m12 = 0.0;
+ m13 = 0.0;
+ m20 = 0.0;
+ m21 = 0.0;
+ m22 = 1.0;
+ m23 = 0.0;
+ m30 = 0.0;
+ m31 = 0.0;
+ m32 = 0.0;
+ m33 = 1.0;
+ }
+
+ public Matrix4d(double[] v) {
+ m00 = v[0];
+ m01 = v[1];
+ m02 = v[2];
+ m03 = v[3];
+ m10 = v[4];
+ m11 = v[5];
+ m12 = v[6];
+ m13 = v[7];
+ m20 = v[8];
+ m21 = v[9];
+ m22 = v[10];
+ m23 = v[11];
+ m30 = v[12];
+ m31 = v[13];
+ m32 = v[14];
+ m33 = v[15];
+ }
+
+ public Matrix4d(double m00, double m01, double m02, double m03, double m10,
+ double m11, double m12, double m13, double m20, double m21,
+ double m22, double m23, double m30, double m31, double m32,
+ double m33) {
+ this.m00 = m00;
+ this.m01 = m01;
+ this.m02 = m02;
+ this.m03 = m03;
+ this.m10 = m10;
+ this.m11 = m11;
+ this.m12 = m12;
+ this.m13 = m13;
+ this.m20 = m20;
+ this.m21 = m21;
+ this.m22 = m22;
+ this.m23 = m23;
+ this.m30 = m30;
+ this.m31 = m31;
+ this.m32 = m32;
+ this.m33 = m33;
+ }
+
+ public Matrix4d clone() {
+ return new Matrix4d(m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, m30, m31, m32, m33);
+ }
+
+ public Matrix4d(Matrix4d m1) {
+ this.m00 = m1.m00;
+ this.m01 = m1.m01;
+ this.m02 = m1.m02;
+ this.m03 = m1.m03;
+ this.m10 = m1.m10;
+ this.m11 = m1.m11;
+ this.m12 = m1.m12;
+ this.m13 = m1.m13;
+ this.m20 = m1.m20;
+ this.m21 = m1.m21;
+ this.m22 = m1.m22;
+ this.m23 = m1.m23;
+ this.m30 = m1.m30;
+ this.m31 = m1.m31;
+ this.m32 = m1.m32;
+ this.m33 = m1.m33;
+ }
+
+ /** 各要素に足す */
+ public void add(Matrix4d m1) {
+ this.m00 += m1.m00;
+ this.m01 += m1.m01;
+ this.m02 += m1.m02;
+ this.m03 += m1.m03;
+ this.m10 += m1.m10;
+ this.m11 += m1.m11;
+ this.m12 += m1.m12;
+ this.m13 += m1.m13;
+ this.m20 += m1.m20;
+ this.m21 += m1.m21;
+ this.m22 += m1.m22;
+ this.m23 += m1.m23;
+ this.m30 += m1.m30;
+ this.m31 += m1.m31;
+ this.m32 += m1.m32;
+ this.m33 += m1.m33;
+ }
+
+ /** 二つの行列の和を格納する */
+ public void add(Matrix4d m1, Matrix4d m2) {
+ this.m00 = m1.m00 + m2.m00;
+ this.m01 = m1.m01 + m2.m01;
+ this.m02 = m1.m02 + m2.m02;
+ this.m03 = m1.m03 + m2.m03;
+ this.m10 = m1.m10 + m2.m10;
+ this.m11 = m1.m11 + m2.m11;
+ this.m12 = m1.m12 + m2.m12;
+ this.m13 = m1.m13 + m2.m13;
+ this.m20 = m1.m20 + m2.m20;
+ this.m21 = m1.m21 + m2.m21;
+ this.m22 = m1.m22 + m2.m22;
+ this.m23 = m1.m23 + m2.m23;
+ this.m30 = m1.m30 + m2.m30;
+ this.m31 = m1.m31 + m2.m31;
+ this.m32 = m1.m32 + m2.m32;
+ this.m33 = m1.m33 + m2.m33;
+ }
+
+ /** 自分に引数の行列を右から掛けたものを格納する */
+ public void mul(Matrix4d right) {
+ double _m00 = m00 * right.m00 + m01 * right.m10 + m02 * right.m20 + m03 * right.m30;
+ double _m01 = m00 * right.m01 + m01 * right.m11 + m02 * right.m21 + m03 * right.m31;
+ double _m02 = m00 * right.m02 + m01 * right.m12 + m02 * right.m22 + m03 * right.m32;
+ double _m03 = m00 * right.m03 + m01 * right.m13 + m02 * right.m23 + m03 * right.m33;
+
+ double _m10 = m10 * right.m00 + m11 * right.m10 + m12 * right.m20 + m13 * right.m30;
+ double _m11 = m10 * right.m01 + m11 * right.m11 + m12 * right.m21 + m13 * right.m31;
+ double _m12 = m10 * right.m02 + m11 * right.m12 + m12 * right.m22 + m13 * right.m32;
+ double _m13 = m10 * right.m03 + m11 * right.m13 + m12 * right.m23 + m13 * right.m33;
+
+ double _m20 = m20 * right.m00 + m21 * right.m10 + m22 * right.m20 + m23 * right.m30;
+ double _m21 = m20 * right.m01 + m21 * right.m11 + m22 * right.m21 + m23 * right.m31;
+ double _m22 = m20 * right.m02 + m21 * right.m12 + m22 * right.m22 + m23 * right.m32;
+ double _m23 = m20 * right.m03 + m21 * right.m13 + m22 * right.m23 + m23 * right.m33;
+
+ double _m30 = m30 * right.m00 + m31 * right.m10 + m32 * right.m20 + m33 * right.m30;
+ double _m31 = m30 * right.m01 + m31 * right.m11 + m32 * right.m21 + m33 * right.m31;
+ double _m32 = m30 * right.m02 + m31 * right.m12 + m32 * right.m22 + m33 * right.m32;
+ double _m33 = m30 * right.m03 + m31 * right.m13 + m32 * right.m23 + m33 * right.m33;
+
+ m00 = _m00; m01 = _m01; m02 = _m02; m03 = _m03;
+ m10 = _m10; m11 = _m11; m12 = _m12; m13 = _m13;
+ m20 = _m20; m21 = _m21; m22 = _m22; m23 = _m23;
+ m30 = _m30; m31 = _m31; m32 = _m32; m33 = _m33;
+ }
+
+ public void invert() {
+ double det = determinant();
+ if (det == 0) {
+ return;
+ }
+ double b00 = determinant3x3(m11,m12,m13,m21,m22,m23,m31,m32,m33) / det;
+ double b01 = -determinant3x3(m01,m02,m03,m21,m22,m23,m31,m32,m33) / det;
+ double b02 = determinant3x3(m01,m02,m03,m11,m12,m13,m31,m32,m33) / det;
+ double b03 = -determinant3x3(m01,m02,m03,m11,m12,m13,m21,m22,m23) / det;
+
+ double b10 = -determinant3x3(m10,m12,m13,m20,m22,m23,m30,m32,m33) / det;
+ double b11 = determinant3x3(m00,m02,m03,m20,m22,m23,m30,m32,m33) / det;
+ double b12 = -determinant3x3(m00,m02,m03,m10,m12,m13,m30,m32,m33) / det;
+ double b13 = determinant3x3(m00,m02,m03,m10,m12,m13,m20,m22,m23) / det;
+
+ double b20 = determinant3x3(m10,m11,m13,m20,m21,m23,m30,m31,m33) / det;
+ double b21 = -determinant3x3(m00,m01,m03,m20,m21,m23,m30,m31,m33) / det;
+ double b22 = determinant3x3(m00,m01,m03,m10,m11,m13,m30,m31,m33) / det;
+ double b23 = -determinant3x3(m00,m01,m03,m10,m11,m13,m20,m21,m23) / det;
+
+ double b30 = -determinant3x3(m10,m11,m12,m20,m21,m22,m30,m31,m32) / det;
+ double b31 = determinant3x3(m00,m01,m02,m20,m21,m22,m30,m31,m32) / det;
+ double b32 = -determinant3x3(m00,m01,m02,m10,m11,m12,m30,m31,m32) / det;
+ double b33 = determinant3x3(m00,m01,m02,m10,m11,m12,m20,m21,m22) / det;
+
+ m00 = b00; m01 = b01; m02 = b02; m03 = b03;
+ m10 = b10; m11 = b11; m12 = b12; m13 = b13;
+ m20 = b20; m21 = b21; m22 = b22; m23 = b23;
+ m30 = b30; m31 = b31; m32 = b32; m33 = b33;
+ }
+
+ private static double determinant3x3(double m11, double m12, double m13,double m21,double m22, double m23,double m31,double m32,double m33) {
+ return m11*m22*m33+m12*m23*m31+m13*m21*m32-m11*m23*m32-m12*m21*m33-m13*m22*m31;
+ }
+
+ /** 行列式を求める */
+ public double determinant() {
+ return m00 * determinant3x3(m11,m12,m13,m21,m22,m23,m31,m32,m33)-
+ m01 * determinant3x3(m10,m12,m13,m20,m22,m23,m30,m32,m33)+
+ m02 * determinant3x3(m10,m11,m13,m20,m21,m23,m30,m31,m33)-
+ m03 * determinant3x3(m10,m11,m12,m20,m21,m22,m30,m31,m32);
+ }
+
+}
diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Node.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Node.java
new file mode 100644
index 0000000..53ccdb9
--- /dev/null
+++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Node.java
@@ -0,0 +1,11 @@
+package com.google.ar.core.examples.java.common.java3d;
+
+public abstract class Node {
+
+ public Node() {
+ super();
+ }
+
+ public abstract Node cloneTree();
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/NodeComponent.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/NodeComponent.java
new file mode 100644
index 0000000..535ef0d
--- /dev/null
+++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/NodeComponent.java
@@ -0,0 +1,6 @@
+package com.google.ar.core.examples.java.common.java3d;
+
+public abstract class NodeComponent {
+ public abstract NodeComponent cloneNodeComponent();
+
+}
diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Point3d.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Point3d.java
new file mode 100644
index 0000000..798627d
--- /dev/null
+++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Point3d.java
@@ -0,0 +1,68 @@
+package com.google.ar.core.examples.java.common.java3d;
+
+public class Point3d extends Tuple3d {
+
+ // コンストラクタ
+ public Point3d() {
+ this.x = 0;
+ this.y = 0;
+ this.z = 0;
+ }
+
+ public Point3d(double[] p) {
+ this.x = p[0];
+ this.y = p[1];
+ this.z = p[2];
+ }
+
+ public Point3d(double x, double y, double z) {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ }
+
+ public Point3d(Point3d p1) {
+ this.x = p1.x;
+ this.y = p1.y;
+ this.z = p1.z;
+ }
+
+ public Point3d(Tuple3d v) {
+ this.x = v.x;
+ this.y = v.y;
+ this.z = v.z;
+ }
+
+ public void scale(double s) {
+ this.x = x * s;
+ this.y = y * s;
+ this.z = z * s;
+ }
+
+ public void add(Tuple3d p) {
+ this.x += p.x;
+ this.y += p.y;
+ this.z += p.z;
+ }
+
+ public void sub(Tuple3d p) {
+ this.x -= p.x;
+ this.y -= p.y;
+ this.z -= p.z;
+ }
+
+ public void set(Tuple3d v) {
+ this.x = v.x;
+ this.y = v.y;
+ this.z = v.z;
+ }
+
+ /** 引数の点との距離を返す */
+ public double distance(Point3d p) {
+ return Math.sqrt(distanceSquared(p));
+ }
+
+ public double distanceSquared(Point3d p) {
+ return (this.x - p.x) * (this.x - p.x) + (this.y - p.y) * (this.y - p.y) + (this.z - p.z) * (this.z - p.z);
+ }
+}
diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Point3f.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Point3f.java
new file mode 100644
index 0000000..a3336cf
--- /dev/null
+++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Point3f.java
@@ -0,0 +1,65 @@
+package com.google.ar.core.examples.java.common.java3d;
+
+public class Point3f {
+ public float x;
+ public float y;
+ public float z;
+
+ // コンストラクタ
+ public Point3f() {
+ this.x = 0;
+ this.y = 0;
+ this.z = 0;
+ }
+
+ public Point3f(float[] p) {
+ this.x = p[0];
+ this.y = p[1];
+ this.z = p[2];
+ }
+
+ public Point3f(float x, float y, float z) {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ }
+
+ public Point3f(Point3f p1) {
+ this.x = p1.x;
+ this.y = p1.y;
+ this.z = p1.z;
+ }
+
+ public void scale(float s) {
+ this.x = x * s;
+ this.y = y * s;
+ this.z = z * s;
+ }
+
+ public void add(Point3f p) {
+ this.x += p.x;
+ this.y += p.y;
+ this.z += p.z;
+ }
+
+ public void sub(Point3f p) {
+ this.x -= p.x;
+ this.y -= p.y;
+ this.z -= p.z;
+ }
+
+ public void set(Point3f v) {
+ this.x = v.x;
+ this.y = v.y;
+ this.z = v.z;
+ }
+
+ /** 引数の点との距離を返す */
+ public double distance(Point3f p) {
+ return Math.sqrt(distanceSquared(p));
+ }
+
+ public double distanceSquared(Point3f p) {
+ return (this.x - p.x) * (this.x - p.x) + (this.y - p.y) * (this.y - p.y) + (this.z - p.z) * (this.z - p.z);
+ }
+}
diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/PointLight.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/PointLight.java
new file mode 100644
index 0000000..adbd468
--- /dev/null
+++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/PointLight.java
@@ -0,0 +1,31 @@
+package com.google.ar.core.examples.java.common.java3d;
+
+public class PointLight extends Light {
+ private Point3f position;
+ private Point3f attenuation;
+
+ public PointLight() {
+ super(new Color3f(1.0f, 1.0f, 1.0f));
+ position = new Point3f(0.0f, 0.0f, 0.0f);
+ }
+
+ public PointLight(Color3f color, Point3f position, Point3f attenuation){
+ super(color);
+ this.position = position;
+ this.attenuation = attenuation;
+ }
+
+ @Override
+ public Node cloneTree() {
+ return new PointLight(color, position, attenuation);
+ }
+
+ public void setPosition(Point3f p){
+ this.position = p;
+ }
+
+ public Point3f getPosition(){
+ return position;
+ }
+
+}
diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Primitive.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Primitive.java
new file mode 100644
index 0000000..ceb8891
--- /dev/null
+++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Primitive.java
@@ -0,0 +1,15 @@
+package com.google.ar.core.examples.java.common.java3d;
+
+public abstract class Primitive extends Group {
+ private Appearance ap = null;
+
+ public Appearance getAppearance() {
+ return ap;
+ }
+
+ public void setAppearance(Appearance ap) {
+ this.ap = ap;
+ }
+
+ public abstract Shape3D getShape(int partid);
+}
diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Quat4d.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Quat4d.java
new file mode 100644
index 0000000..d977c60
--- /dev/null
+++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Quat4d.java
@@ -0,0 +1,130 @@
+package com.google.ar.core.examples.java.common.java3d;
+
+public class Quat4d implements Cloneable {
+ private static final double EPS = 1.0e-12;
+ public double x;
+ public double y;
+ public double z;
+ public double w;
+
+ public Quat4d clone() {
+ Quat4d q;
+ try {
+ q = (Quat4d) super.clone();
+ } catch (CloneNotSupportedException ce) {
+ throw new RuntimeException();
+ }
+ q.set(x, y, z, w);
+ return q;
+ }
+
+ // コンストラクタ
+ public Quat4d() {
+ x = 0.0;
+ y = 0.0;
+ z = 0.0;
+ w = 0.0;
+ }
+
+ // コンストラクタ
+ public Quat4d(double px, double py, double pz, double pw) {
+ double mag = 1.0/ Math.sqrt(px*px+py*py+pz*pz+pw*pw);
+ x = px*mag;
+ y = py*mag;
+ z = pz*mag;
+ w = pw*mag;
+ }
+
+ public Quat4d(Quat4d q) {
+ x = q.x;
+ y = q.y;
+ z = q.z;
+ w = q.w;
+ }
+
+ public void set(AxisAngle4d a) {
+ double mag,amag;
+ amag = Math.sqrt( a.x*a.x + a.y*a.y + a.z*a.z);
+ if( amag < EPS ) {
+ w = 0.0;
+ x = 0.0;
+ y = 0.0;
+ z = 0.0;
+ } else {
+ amag = 1.0/amag;
+ mag = Math.sin(a.angle/2.0);
+ w = Math.cos(a.angle/2.0);
+ x = a.x*amag*mag;
+ y = a.y*amag*mag;
+ z = a.z*amag*mag;
+ }
+ }
+
+ private void set(double x2, double y2, double z2, double w2) {
+ x = x2;
+ y = y2;
+ z = z2;
+ w = w2;
+ }
+
+ public void mul(Quat4d q1) {
+ double x, y, w;
+ w = this.w * q1.w - this.x * q1.x - this.y * q1.y - this.z * q1.z;
+ x = this.w * q1.x + q1.w * this.x + this.y * q1.z - this.z * q1.y;
+ y = this.w * q1.y + q1.w * this.y - this.x * q1.z + this.z * q1.x;
+ this.z = this.w * q1.z + q1.w * this.z + this.x * q1.y - this.y * q1.x;
+ this.w = w;
+ this.x = x;
+ this.y = y;
+ }
+
+ public void interpolate(Quat4d q1, double alpha) {
+ double dot, s1, s2, om, sinom;
+
+ dot = x * q1.x + y * q1.y + z * q1.z + w * q1.w;
+
+ if (dot < 0) {
+ // negate quaternion
+ q1.x = -q1.x;
+ q1.y = -q1.y;
+ q1.z = -q1.z;
+ q1.w = -q1.w;
+ dot = -dot;
+ }
+
+ if ((1.0 - dot) > EPS) {
+ om = Math.acos(dot);
+ sinom = Math.sin(om);
+ s1 = Math.sin((1.0 - alpha) * om) / sinom;
+ s2 = Math.sin(alpha * om) / sinom;
+ } else {
+ s1 = 1.0 - alpha;
+ s2 = alpha;
+ }
+
+ w = s1 * w + s2 * q1.w;
+ x = s1 * x + s2 * q1.x;
+ y = s1 * y + s2 * q1.y;
+ z = s1 * z + s2 * q1.z;
+ }
+
+ public void normalize() {
+ double norm;
+
+ norm = (this.x * this.x + this.y * this.y + this.z * this.z + this.w
+ * this.w);
+
+ if (norm > 0.0) {
+ norm = 1.0 / Math.sqrt(norm);
+ this.x *= norm;
+ this.y *= norm;
+ this.z *= norm;
+ this.w *= norm;
+ } else {
+ this.x = 0.0;
+ this.y = 0.0;
+ this.z = 0.0;
+ this.w = 0.0;
+ }
+ }
+}
diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Shape3D.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Shape3D.java
new file mode 100644
index 0000000..aa654de
--- /dev/null
+++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Shape3D.java
@@ -0,0 +1,33 @@
+package com.google.ar.core.examples.java.common.java3d;
+
+public class Shape3D extends Leaf {
+ private Geometry geometry;
+ private Appearance ap;
+
+ public Shape3D(Geometry g, Appearance a) {
+ this.geometry = g;
+ this.ap = a;
+ }
+
+ public Appearance getAppearance() {
+ return ap;
+ }
+
+ public void setAppearance(Appearance ap) {
+ this.ap = ap;
+ }
+
+ public Geometry getGeometry() {
+ return geometry;
+ }
+
+ public void setGeometry(Geometry geometry) {
+ this.geometry = geometry;
+ }
+
+ @Override
+ public Node cloneTree() {
+ Shape3D shape = new Shape3D(geometry, (Appearance)ap.cloneNodeComponent());
+ return shape;
+ }
+}
diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Sphere.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Sphere.java
new file mode 100644
index 0000000..fc06ed7
--- /dev/null
+++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Sphere.java
@@ -0,0 +1,22 @@
+package com.google.ar.core.examples.java.common.java3d;
+
+public class Sphere extends Primitive {
+ public static final int BODY = 0;
+ static final int MID_REZ_DIV = 16;
+
+ private float radius;
+ int divisions;
+
+ public float getRadius() {
+ return radius;
+ }
+
+ public void setRadius(float radius) {
+ this.radius = radius;
+ }
+
+ @Override
+ public Shape3D getShape(int partid) {
+ return null;
+ }
+}
diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/TexCoordGeneration.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/TexCoordGeneration.java
new file mode 100644
index 0000000..3a39355
--- /dev/null
+++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/TexCoordGeneration.java
@@ -0,0 +1,42 @@
+package com.google.ar.core.examples.java.common.java3d;
+
+public class TexCoordGeneration extends NodeComponent {
+ public static final int OBJECT_LINEAR = 0;
+ public static final int EYE_LINEAR = 1;
+ public static final int SPHERE_MAP = 2;
+ public static final int NORMAL_MAP = 3;
+ public static final int REFLECTION_MAP = 4;
+ public static final int TEXTURE_COORDINATE_2 = 0;
+ public static final int TEXTURE_COORDINATE_3 = 1;
+ public static final int TEXTURE_COORDINATE_4 = 2;
+
+ private int genMode;
+ private int format;
+
+ public TexCoordGeneration(int genMode, int format) {
+ this.genMode = genMode;
+ this.format = format;
+ }
+
+ public int getGenMode() {
+ return genMode;
+ }
+
+ public void setGenMode(int genMode) {
+ this.genMode = genMode;
+ }
+
+ public int getFormat() {
+ return format;
+ }
+
+ public void setFormat(int format) {
+ this.format = format;
+ }
+
+ @Override
+ public NodeComponent cloneNodeComponent() {
+ return new TexCoordGeneration(genMode, format);
+ }
+
+}
diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Texture.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Texture.java
new file mode 100644
index 0000000..e8217f0
--- /dev/null
+++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Texture.java
@@ -0,0 +1,36 @@
+package com.google.ar.core.examples.java.common.java3d;
+
+public abstract class Texture extends NodeComponent {
+ public static final int BASE_LEVEL = 1;
+ public static final int RGB = 5;
+ public static final int RGBA = 6;
+
+ protected ImageComponent[] imageComponents = null;
+ protected int mipmapMode;
+ protected int format;
+ protected int width;
+ protected int hight;
+
+ public Texture(int mipmapMode, int format, int width, int hight) {
+ this.mipmapMode = mipmapMode;
+ this.format = format;
+ this.width = width;
+ this.hight = hight;
+ }
+
+ public void setImage(int n, ImageComponent image) {
+ imageComponents[n] = image;
+ }
+
+ public ImageComponent getImage(int n) {
+ return imageComponents[n];
+ }
+
+ public void setImages(ImageComponent[] images) {
+ imageComponents = images;
+ }
+
+ public ImageComponent[] getImages() {
+ return imageComponents;
+ }
+}
diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Texture2D.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Texture2D.java
new file mode 100644
index 0000000..62627e8
--- /dev/null
+++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Texture2D.java
@@ -0,0 +1,16 @@
+package com.google.ar.core.examples.java.common.java3d;
+
+public class Texture2D extends Texture {
+
+ public Texture2D(int mipmapMode, int format, int width, int hight) {
+ super(mipmapMode, format, width, hight);
+ imageComponents = new ImageComponent[1];
+ }
+
+ @Override
+ public NodeComponent cloneNodeComponent() {
+ Texture2D tex2D = new Texture2D(mipmapMode, format, width, hight);
+ tex2D.imageComponents = imageComponents.clone();
+ return tex2D;
+ }
+}
diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/TextureAttributes.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/TextureAttributes.java
new file mode 100644
index 0000000..ba7679e
--- /dev/null
+++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/TextureAttributes.java
@@ -0,0 +1,70 @@
+package com.google.ar.core.examples.java.common.java3d;
+
+public class TextureAttributes extends NodeComponent {
+ public static final int FASTEST = 0;
+ public static final int NICEST = 1;
+ public static final int MODULATE = 2;
+ public static final int DECAL = 3;
+ public static final int BLEND = 4;
+ public static final int REPLACE = 5;
+// public static final int COMBINE = 6;
+// public static final int COMBINE_REPLACE = 0;
+// public static final int COMBINE_MODULATE = 1;
+// public static final int COMBINE_ADD = 2;
+// public static final int COMBINE_ADD_SIGNED = 3;
+// public static final int COMBINE_SUBTRACT = 4;
+// public static final int COMBINE_INTERPOLATE = 5;
+// public static final int COMBINE_DOT3 = 6;
+// public static final int COMBINE_OBJECT_COLOR = 0;
+// public static final int COMBINE_TEXTURE_COLOR = 1;
+// public static final int COMBINE_CONSTANT_COLOR = 2;
+// public static final int COMBINE_PREVIOUS_TEXTURE_UNIT_STATE = 3;
+// public static final int COMBINE_SRC_COLOR = 0;
+// public static final int COMBINE_ONE_MINUS_SRC_COLOR = 1;
+// public static final int COMBINE_SRC_ALPHA = 2;
+// public static final int COMBINE_ONE_MINUS_SRC_ALPHA = 3;
+
+ private int textureMode;
+ private Transform3D transform;
+ private Color4f textureBlendColor;
+ private int perspCorrectionMode;
+
+ public TextureAttributes() {
+ textureMode = TextureAttributes.REPLACE;
+ textureBlendColor = new Color4f();
+ perspCorrectionMode = TextureAttributes.NICEST;
+ }
+
+ public TextureAttributes(int textureMode, Transform3D transform, Color4f textureBlendColor, int perspCorrectionMode) {
+ this.textureMode = textureMode;
+ this.transform = transform;
+ this.textureBlendColor = textureBlendColor;
+ this.perspCorrectionMode = perspCorrectionMode;
+ }
+
+ public int getTextureMode() {
+ return textureMode;
+ }
+
+ public void setTextureMode(int textureMode) {
+ this.textureMode = textureMode;
+ }
+
+ public void setTextureTransform(Transform3D transform) {
+ this.transform = transform;
+ }
+
+ public int getPerspectiveCorrectionMode() {
+ return perspCorrectionMode;
+ }
+
+ public void setPerspectiveCorrectionMode(int perspCorrectionMode) {
+ this.perspCorrectionMode = perspCorrectionMode;
+ }
+
+ @Override
+ public NodeComponent cloneNodeComponent() {
+ return new TextureAttributes(textureMode, new Transform3D(transform), textureBlendColor.clone(), perspCorrectionMode);
+ }
+
+}
diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/TextureCubeMap.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/TextureCubeMap.java
new file mode 100644
index 0000000..3730dba
--- /dev/null
+++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/TextureCubeMap.java
@@ -0,0 +1,29 @@
+package com.google.ar.core.examples.java.common.java3d;
+
+public class TextureCubeMap extends Texture {
+ public static final int POSITIVE_X = 0;
+ public static final int NEGATIVE_X = 1;
+ public static final int POSITIVE_Y = 2;
+ public static final int NEGATIVE_Y = 3;
+ public static final int POSITIVE_Z = 4;
+ public static final int NEGATIVE_Z = 5;
+
+ public TextureCubeMap(int mipmapMode, int format, int width) {
+ super(mipmapMode, format, width, 0);
+ imageComponents = new ImageComponent[6];
+ }
+
+
+ public void setImage(int level, int face, ImageComponent2D image) {
+ imageComponents[face] = image;
+ width = image.getBitmap().getWidth();
+ }
+
+ @Override
+ public NodeComponent cloneNodeComponent() {
+ TextureCubeMap texCubeMap = new TextureCubeMap(mipmapMode, format, width);
+ texCubeMap.imageComponents = imageComponents.clone();
+ return texCubeMap;
+ }
+
+}
diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/TextureLoader.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/TextureLoader.java
new file mode 100644
index 0000000..283e5a8
--- /dev/null
+++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/TextureLoader.java
@@ -0,0 +1,43 @@
+package com.google.ar.core.examples.java.common.java3d;
+
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+
+import java.io.InputStream;
+
+public class TextureLoader {
+
+ public static final int BY_REFERENCE = 2;
+ public static final int Y_UP = 4;
+ private InputStream in = null;
+ private Resources res = null;
+ private int id = 0;
+ private Bitmap bitmap;
+
+ public TextureLoader(InputStream in, int flags) {
+ this.in = in;
+ }
+
+ public TextureLoader(Resources res, int id, int flags) {
+ this.res = res;
+ this.id = id;
+ }
+
+ public ImageComponent2D getImage() {
+ if (in != null) {
+ bitmap = BitmapFactory.decodeStream(in);
+ } else if (res != null) {
+ bitmap = BitmapFactory.decodeResource(res, id);
+ }
+ return new ImageComponent2D(ImageComponent2D.FORMAT_RGB, bitmap);
+ }
+
+ public Texture getTexture() {
+ ImageComponent2D ic2 = getImage();
+ Texture tex = new Texture2D(Texture.BASE_LEVEL, Texture.RGB, bitmap.getWidth(), bitmap.getHeight());
+ tex.setImage(0, ic2);
+ return tex;
+ }
+
+}
diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/TextureUnitState.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/TextureUnitState.java
new file mode 100644
index 0000000..4ffa3c8
--- /dev/null
+++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/TextureUnitState.java
@@ -0,0 +1,48 @@
+package com.google.ar.core.examples.java.common.java3d;
+
+public class TextureUnitState extends NodeComponent {
+ private Texture texture;
+ private TextureAttributes textureAttribute;
+ private TexCoordGeneration texCoordGeneration;
+
+ public TextureUnitState() {
+ this(null, null ,null);
+ }
+
+ public TextureUnitState(Texture texture, TextureAttributes textureAttribute, TexCoordGeneration texCoordGeneration) {
+ this.texture = texture;
+ this.textureAttribute = textureAttribute;
+ this.texCoordGeneration= texCoordGeneration;
+ }
+
+ public Texture getTexture() {
+ return texture;
+ }
+
+ public void setTexture(Texture texture) {
+ this.texture = texture;
+ }
+
+ public TextureAttributes getTextureAttributes() {
+ return textureAttribute;
+ }
+
+ public void setTextureAttributes(TextureAttributes textureAttribute) {
+ this.textureAttribute = textureAttribute;
+ }
+
+ public TexCoordGeneration getTexCoordGeneration() {
+ return texCoordGeneration;
+ }
+
+ public void setTexCoordGeneration(TexCoordGeneration texCoordGeneration) {
+ this.texCoordGeneration = texCoordGeneration;
+ }
+
+ @Override
+ public NodeComponent cloneNodeComponent() {
+ return new TextureUnitState((Texture)texture.cloneNodeComponent(),
+ (TextureAttributes)textureAttribute.cloneNodeComponent(),
+ (TexCoordGeneration)texCoordGeneration.cloneNodeComponent());
+ }
+}
diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Transform3D.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Transform3D.java
new file mode 100644
index 0000000..efeef90
--- /dev/null
+++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Transform3D.java
@@ -0,0 +1,425 @@
+package com.google.ar.core.examples.java.common.java3d;
+
+
+public class Transform3D {
+ double[][] mat;
+ double[] rot;
+ double[] scales = new double[] {1.0,1.0,1.0};
+
+ static final double EPSILON_ABSOLUTE = 1.0e-5;
+
+ public Transform3D() {
+ mat = new double[][]{{1.0,0.0,0.0,0.0},{0.0,1.0,0.0,0.0},{0.0,0.0,1.0,0.0},{0.0,0.0,0.0,1.0}};
+ }
+
+ public Transform3D(Matrix4d m1) {
+ mat = new double[][]{{m1.m00,m1.m01,m1.m02,m1.m03},{m1.m10,m1.m11,m1.m12,m1.m13},{m1.m20,m1.m21,m1.m22,m1.m23},{m1.m30,m1.m31,m1.m32,m1.m33}};
+ }
+
+ public Transform3D(Transform3D transform) {
+ mat = new double[4][4];
+ set(transform);
+ }
+
+ public void set(Transform3D transform) {
+ for (int i = 0;i < 4; i++) {
+ for (int j = 0;j < 4; j++) {
+ mat[i][j] = transform.mat[i][j];
+ }
+ }
+ scales[0] = transform.scales[0];
+ scales[1] = transform.scales[1];
+ scales[2] = transform.scales[2];
+ }
+
+ /**
+ * this = t1 * t2
+ *
+ * @param t1
+ * @param t2
+ */
+ public void mul(Transform3D t1, Transform3D t2) {
+ for (int i = 0; i < 4; i++) {
+ double[] row = row(i, t1.mat);
+ for (int j = 0; j < 4; j++) {
+ mat[i][j] = 0.0;
+ double[] col = col(j, t2.mat);
+ for (int k = 0; k < 4; k++) {
+ mat[i][j] += row[k] * col[k];
+ }
+ }
+ }
+ }
+
+ /**
+ * this = this * t1
+ *
+ * @param t1
+ */
+ public void mul(Transform3D t1) {
+ this.mul(new Transform3D(this), t1);
+ }
+
+ /** 4*4行列の行を返す */
+ private double[] row(int row, double[][] mat) {
+ return new double[] {mat[row][0], mat[row][1], mat[row][2], mat[row][3]};
+ }
+
+ /** 4*4行列の列を返す */
+ private double[] col(int col, double[][] mat) {
+ return new double[] {mat[0][col], mat[1][col], mat[2][col], mat[3][col]};
+ }
+
+ public void set(Vector3d v) {
+ mat[0][3] = v.x;
+ mat[1][3] = v.y;
+ mat[2][3] = v.z;
+ }
+
+ /** 4*4の行列を16の1次元配列で返す */
+ public float[] getMatrix() {
+ float[] result = new float[16];
+ for (int i = 0; i < 4; i++) {
+ for (int j = 0; j < 4; j++) {
+ result[j + 4 * i] = (float)(mat[j][i]);
+ }
+ }
+ return result;
+ }
+
+ /** 対角行列をスカラ倍する */
+ public void setScale(double s) {
+ for (int i = 0; i < 3; i++) {
+ mat[i][i] = s;
+ scales[i] = s;
+ }
+ }
+
+ /** 対角行列をvectorの成分倍する */
+ public void setScale(Vector3d vector3d) {
+ scales[0] = mat[0][0] = vector3d.x;
+ scales[1] = mat[1][1] = vector3d.y;
+ scales[2] = mat[2][2] = vector3d.z;
+ }
+
+ /** 任意の単位ベクトルまわりに回転する */
+ public void setRotation(AxisAngle4d t) {
+ double vx = t.x;
+ double vy = t.y;
+ double vz = t.z;
+ double sin = Math.sin(t.angle);
+ double cos = Math.cos(t.angle);
+
+ mat[0][0] = (Math.pow(vx, 2.0)) * (1 - cos) + cos;
+ mat[0][1] = ((vx * vy) * (1 - cos)) - (vz * sin);
+ mat[0][2] = ((vz * vx) * (1 - cos)) + (vy * sin);
+ mat[1][0] = ((vx * vy) * (1 - cos)) + (vz * sin);
+ mat[1][1] = (Math.pow(vy, 2.0)) * (1 - cos) + cos;
+ mat[1][2] = ((vy * vz) * (1 - cos)) - (vx * sin);
+ mat[2][0] = ((vz * vx) * (1 - cos)) - (vy * sin);
+ mat[2][1] = ((vy * vz) * (1 - cos)) + (vx * sin);
+ mat[2][2] = (Math.pow(vz, 2.0)) * (1 - cos) + cos;
+ }
+
+ /** 任意の単位ベクトルまわりに回転する */
+ public void setRotation(Quat4d q) {
+// double vx = q.x;
+// double vy = q.y;
+// double vz = q.z;
+// double sin = Math.sin(q.w);
+// double cos = Math.cos(q.w);
+// double cosm = 1 - cos;
+//
+// mat[0][0] = (vx * vx) * cosm + cos;
+// mat[0][1] = ((vx * vy) * cosm) - (vz * sin);
+// mat[0][2] = ((vz * vx) * cosm) + (vy * sin);
+// mat[1][0] = ((vx * vy) * cosm) + (vz * sin);
+// mat[1][1] = (vy * vy) * cosm + cos;
+// mat[1][2] = ((vy * vz) * cosm) - (vx * sin);
+// mat[2][0] = ((vz * vx) * cosm) - (vy * sin);
+// mat[2][1] = ((vy * vz) * cosm) + (vx * sin);
+// mat[2][2] = (vz * vz) * cosm + cos;
+
+ mat[0][0] = (1.0 - 2.0 * q.y * q.y - 2.0 * q.z * q.z) * scales[0];
+ mat[1][0] = (2.0 * (q.x * q.y + q.w * q.z)) * scales[0];
+ mat[2][0] = (2.0 * (q.x * q.z - q.w * q.y)) * scales[0];
+ mat[0][1] = (2.0 * (q.x * q.y - q.w * q.z)) * scales[1];
+ mat[1][1] = (1.0 - 2.0 * q.x * q.x - 2.0 * q.z * q.z) * scales[1];
+ mat[2][1] = (2.0 * (q.y * q.z + q.w * q.x)) * scales[1];
+ mat[0][2] = (2.0 * (q.x * q.z + q.w * q.y)) * scales[2];
+ mat[1][2] = (2.0 * (q.y * q.z - q.w * q.x)) * scales[2];
+ mat[2][2] = (1.0 - 2.0 * q.x * q.x - 2.0 * q.y * q.y) * scales[2];
+ }
+
+ public void rotX(double angle) {
+ double sinAngle = Math.sin(angle);
+ double cosAngle = Math.cos(angle);
+
+ mat[0][0] = 1.0;
+ mat[0][1] = 0.0;
+ mat[0][2] = 0.0;
+ mat[0][3] = 0.0;
+
+ mat[1][0] = 0.0;
+ mat[1][1] = cosAngle;
+ mat[1][2] = -sinAngle;
+ mat[1][3] = 0.0;
+
+ mat[2][0] = 0.0;
+ mat[2][1] = sinAngle;
+ mat[2][2] = cosAngle;
+ mat[2][3] = 0.0;
+
+ mat[3][0] = 0.0;
+ mat[3][1] = 0.0;
+ mat[3][2] = 0.0;
+ mat[3][3] = 1.0;
+ }
+
+ public void rotY(double angle) {
+ double sinAngle = Math.sin(angle);
+ double cosAngle = Math.cos(angle);
+
+ mat[0][0] = cosAngle;
+ mat[0][1] = 0.0;
+ mat[0][2] = sinAngle;
+ mat[0][3] = 0.0;
+
+ mat[1][0] = 0.0;
+ mat[1][1] = 1.0;
+ mat[1][2] = 0.0;
+ mat[1][3] = 0.0;
+
+ mat[2][0] = -sinAngle;
+ mat[2][1] = 0.0;
+ mat[2][2] = cosAngle;
+ mat[2][3] = 0.0;
+
+ mat[3][0] = 0.0;
+ mat[3][1] = 0.0;
+ mat[3][2] = 0.0;
+ mat[3][3] = 1.0;
+ }
+
+ public void rotZ(double angle) {
+ double sinAngle = Math.sin(angle);
+ double cosAngle = Math.cos(angle);
+
+ mat[0][0] = cosAngle;
+ mat[0][1] = -sinAngle;
+ mat[0][2] = 0.0;
+ mat[0][3] = 0.0;
+
+ mat[1][0] = sinAngle;
+ mat[1][1] = cosAngle;
+ mat[1][2] = 0.0;
+ mat[1][3] = 0.0;
+
+ mat[2][0] = 0.0;
+ mat[2][1] = 0.0;
+ mat[2][2] = 1.0;
+ mat[2][3] = 0.0;
+
+ mat[3][0] = 0.0;
+ mat[3][1] = 0.0;
+ mat[3][2] = 0.0;
+ mat[3][3] = 1.0;
+ }
+
+ /** 引数の成分を格納する */
+ public void get(Matrix4d mat4d) {
+ mat4d.m00 = mat[0][0];
+ mat4d.m01 = mat[0][1];
+ mat4d.m02 = mat[0][2];
+ mat4d.m03 = mat[0][3];
+ mat4d.m10 = mat[1][0];
+ mat4d.m11 = mat[1][1];
+ mat4d.m12 = mat[1][2];
+ mat4d.m13 = mat[1][3];
+ mat4d.m20 = mat[2][0];
+ mat4d.m21 = mat[2][1];
+ mat4d.m22 = mat[2][2];
+ mat4d.m23 = mat[2][3];
+ mat4d.m30 = mat[3][0];
+ mat4d.m31 = mat[3][1];
+ mat4d.m32 = mat[3][2];
+ mat4d.m33 = mat[3][3];
+ }
+
+
+ public void set(double[] matrix) {
+ mat[0][0] = matrix[0];
+ mat[0][1] = matrix[1];
+ mat[0][2] = matrix[2];
+ mat[0][3] = matrix[3];
+ mat[1][0] = matrix[4];
+ mat[1][1] = matrix[5];
+ mat[1][2] = matrix[6];
+ mat[1][3] = matrix[7];
+ mat[2][0] = matrix[8];
+ mat[2][1] = matrix[9];
+ mat[2][2] = matrix[10];
+ mat[2][3] = matrix[11];
+ mat[3][0] = matrix[12];
+ mat[3][1] = matrix[13];
+ mat[3][2] = matrix[14];
+ mat[3][3] = matrix[15];
+ scales[0] = 1.0;
+ scales[1] = 1.0;
+ scales[2] = 1.0;
+ }
+
+ public void set(AxisAngle4d a1) {
+ double mag = Math.sqrt( a1.x*a1.x + a1.y*a1.y + a1.z*a1.z);
+ if (almostZero(mag)) {
+ setIdentity();
+ } else {
+ mag = 1.0/mag;
+ double ax = a1.x*mag;
+ double ay = a1.y*mag;
+ double az = a1.z*mag;
+ double sinTheta = Math.sin((double)a1.angle);
+ double cosTheta = Math.cos((double)a1.angle);
+ double t = 1.0 - cosTheta;
+ double xz = ax * az;
+ double xy = ax * ay;
+ double yz = ay * az;
+ mat[0][0] = t * ax * ax + cosTheta;
+ mat[0][1] = t * xy - sinTheta * az;
+ mat[0][2] = t * xz + sinTheta * ay;
+ mat[0][3] = 0.0;
+ mat[1][0] = t * xy + sinTheta * az;
+ mat[1][1] = t * ay * ay + cosTheta;
+ mat[1][2] = t * yz - sinTheta * ax;
+ mat[1][3] = 0.0;
+ mat[2][0] = t * xz - sinTheta * ay;
+ mat[2][1] = t * yz + sinTheta * ax;
+ mat[2][2] = t * az * az + cosTheta;
+ mat[2][3] = 0.0;
+ mat[3][0] = 0.0;
+ mat[3][1] = 0.0;
+ mat[3][2] = 0.0;
+ mat[3][3] = 1.0;
+ }
+ }
+
+ public void set(Quat4d q1) {
+ mat[0][0] = (1.0f - 2.0f*q1.y*q1.y - 2.0f*q1.z*q1.z);
+ mat[1][0] = (2.0f*(q1.x*q1.y + q1.w*q1.z));
+ mat[2][0] = (2.0f*(q1.x*q1.z - q1.w*q1.y));
+
+ mat[0][1] = (2.0f*(q1.x*q1.y - q1.w*q1.z));
+ mat[1][1] = (1.0f - 2.0f*q1.x*q1.x - 2.0f*q1.z*q1.z);
+ mat[2][1] = (2.0f*(q1.y*q1.z + q1.w*q1.x));
+
+ mat[0][2] = (2.0f*(q1.x*q1.z + q1.w*q1.y));
+ mat[1][2] = (2.0f*(q1.y*q1.z - q1.w*q1.x));
+ mat[2][2] = (1.0f - 2.0f*q1.x*q1.x - 2.0f*q1.y*q1.y);
+
+ mat[0][3] = 0.0;
+ mat[1][3] = 0.0;
+ mat[2][3] = 0.0;
+
+ mat[3][0] = 0.0;
+ mat[3][1] = 0.0;
+ mat[3][2] = 0.0;
+ mat[3][3] = 1.0;
+
+ // Issue 253: set all dirty bits if input is infinity or NaN
+// if (isInfOrNaN(q1)) {
+// dirtyBits = ALL_DIRTY;
+// return;
+// }
+
+// dirtyBits = CLASSIFY_BIT | SCALE_BIT | ROTATION_BIT;
+// type = RIGID | CONGRUENT | AFFINE | ORTHO;
+ }
+
+ /** Vector3dをTransform3dで変換する */
+ public void transform(Vector3d v) {
+ double vx = mat[0][0]*v.x + mat[0][1]*v.y + mat[0][2]*v.z;// + mat[0][3] * 1.0;
+ double vy = mat[1][0]*v.x + mat[1][1]*v.y + mat[1][2]*v.z;// + mat[1][3] * 1.0;
+ v.z = mat[2][0]*v.x + mat[2][1]*v.y + mat[2][2]*v.z;// + mat[2][3] * 1.0;
+ v.x = vx;
+ v.y = vy;
+ }
+
+ private static final boolean almostZero(double a) {
+ return ((a < EPSILON_ABSOLUTE) && (a > -EPSILON_ABSOLUTE));
+ }
+
+ public final void setIdentity() {
+ mat[0][0] = 1.0; mat[0][1] = 0.0; mat[0][2] = 0.0; mat[0][3] = 0.0;
+ mat[1][0] = 0.0; mat[1][1] = 1.0; mat[1][2] = 0.0; mat[1][3] = 0.0;
+ mat[2][0] = 0.0; mat[2][1] = 0.0; mat[2][2] = 1.0; mat[2][3] = 0.0;
+ mat[3][0] = 0.0; mat[3][1] = 0.0; mat[3][2] = 0.0; mat[3][3] = 1.0;
+ }
+
+ public void invert() {
+ Matrix4d m = new Matrix4d();
+ m.m00 = mat[0][0]; m.m01 = mat[0][1]; m.m02 = mat[0][2]; m.m03 = mat[0][3];
+ m.m10 = mat[1][0]; m.m11 = mat[1][1]; m.m12 = mat[1][2]; m.m13 = mat[1][3];
+ m.m20 = mat[2][0]; m.m21 = mat[2][1]; m.m22 = mat[2][2]; m.m23 = mat[2][3];
+ m.m30 = mat[3][0]; m.m31 = mat[3][1]; m.m32 = mat[3][2]; m.m33 = mat[3][3];
+ m.invert();
+ mat = new double[][]{{m.m00,m.m01,m.m02,m.m03},{m.m10,m.m11,m.m12,m.m13},{m.m20,m.m21,m.m22,m.m23},{m.m30,m.m31,m.m32,m.m33}};
+ }
+
+ public void transpose() {
+ for (int i = 0; i < 3; i++) {
+ for (int j = 1 + i; j < 4; j++) {
+ double m = mat[j][i];
+ mat[j][i] = mat[i][j];
+ mat[i][j] = m;
+ }
+ }
+ }
+
+ public void transform(Point3d point) {
+ Point3d p = new Point3d();
+ p.x = mat[0][0] * point.x + mat[0][1] * point.y + mat[0][2] * point.z;
+ p.y = mat[1][0] * point.x + mat[1][1] * point.y + mat[1][2] * point.z;
+ p.z = mat[2][0] * point.x + mat[2][1] * point.y + mat[2][2] * point.z;
+ point.x = p.x;
+ point.y = p.y;
+ point.z = p.z;
+ }
+
+ /**
+ * 平面に対してしか使ってはいけない
+ * @param plane
+ */
+ public void transform(Vector4d plane) {
+// double x = mat[0][0] * plane.x + mat[0][1] * plane.y + mat[0][2] * plane.z;
+// double y = mat[1][0] * plane.x + mat[1][1] * plane.y + mat[1][2] * plane.z;
+// double z = mat[2][0] * plane.x + mat[2][1] * plane.y + mat[2][2] * plane.z;
+//
+// double vx = x * plane.w - mat[0][3];
+// double vy = y * plane.w - mat[1][3];
+// double vz = z * plane.w - mat[2][3];
+//
+// plane.x = x;
+// plane.y = y;
+// plane.z = z;
+// plane.w = x * vx + y * vy + z * vz;
+//
+ double x = (mat[0][0]*plane.x + mat[0][1]*plane.y
+ + mat[0][2]*plane.z + mat[0][3]*plane.w);
+ double y = (mat[1][0]*plane.x + mat[1][1]*plane.y
+ + mat[1][2]*plane.z + mat[1][3]*plane.w);
+ double z = (mat[2][0]*plane.x + mat[2][1]*plane.y
+ + mat[2][2]*plane.z + mat[2][3]*plane.w);
+ plane.w = (mat[3][0]*plane.x + mat[3][1]*plane.y
+ + mat[3][2]*plane.z + mat[3][3]*plane.w);
+ plane.x = x;
+ plane.y = y;
+ plane.z = z;
+ }
+
+ public void setTranslation(Vector3d v) {
+ mat[3][0] = v.x;
+ mat[3][1] = v.y;
+ mat[3][2] = v.z;
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/TransformGroup.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/TransformGroup.java
new file mode 100644
index 0000000..1b8a692
--- /dev/null
+++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/TransformGroup.java
@@ -0,0 +1,27 @@
+package com.google.ar.core.examples.java.common.java3d;
+
+public class TransformGroup extends Group {
+
+ private Transform3D transform;
+
+ public TransformGroup() {
+ this(new Transform3D());
+ }
+
+ public TransformGroup(Transform3D transform) {
+ this.transform = transform;
+ }
+
+ public void setTransform(Transform3D transform) {
+ this.transform = transform;
+ }
+
+ public void getTransform(Transform3D transform) {
+ transform.set(this.transform);
+ }
+
+ public Node cloneTree() {
+ TransformGroup newInstance = new TransformGroup(new Transform3D(transform));
+ return newInstance;
+ }
+}
diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/TriangleArray.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/TriangleArray.java
new file mode 100644
index 0000000..905def7
--- /dev/null
+++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/TriangleArray.java
@@ -0,0 +1,18 @@
+package com.google.ar.core.examples.java.common.java3d;
+
+public class TriangleArray extends GeometryArray {
+
+ public TriangleArray(int vertexCount, int vertexFormat) {
+ super(vertexCount, vertexFormat);
+ }
+
+ @Override
+ public NodeComponent cloneNodeComponent() {
+ TriangleArray newOne = new TriangleArray(vertexCount, vertexFormat);
+ newOne.vertexBuffer = vertexBuffer.duplicate();
+ if (normalBuffer != null) newOne.normalBuffer = normalBuffer.duplicate();
+ if (uvBuffer != null) newOne.uvBuffer = uvBuffer.duplicate();
+ return newOne;
+ }
+
+}
diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/TriangleFanArray.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/TriangleFanArray.java
new file mode 100644
index 0000000..9cd80b0
--- /dev/null
+++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/TriangleFanArray.java
@@ -0,0 +1,18 @@
+package com.google.ar.core.examples.java.common.java3d;
+
+public class TriangleFanArray extends GeometryStripArray {
+
+ public TriangleFanArray(int vertexCount, int vertexFormat, int[] stripIndexCounts) {
+ super(vertexCount, vertexFormat, stripIndexCounts);
+ }
+
+ @Override
+ public NodeComponent cloneNodeComponent() {
+ TriangleFanArray newOne = new TriangleFanArray(vertexCount, vertexFormat, (int [])stripIndexCounts.clone());
+ newOne.vertexBuffer = vertexBuffer.duplicate();
+ if (normalBuffer != null) newOne.normalBuffer = normalBuffer.duplicate();
+ if (uvBuffer != null) newOne.uvBuffer = uvBuffer.duplicate();
+ return newOne;
+ }
+
+}
diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/TriangleStripArray.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/TriangleStripArray.java
new file mode 100644
index 0000000..8a55630
--- /dev/null
+++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/TriangleStripArray.java
@@ -0,0 +1,18 @@
+package com.google.ar.core.examples.java.common.java3d;
+
+public class TriangleStripArray extends GeometryStripArray {
+
+ public TriangleStripArray(int vertexCount, int vertexFormat, int[] stripIndexCounts) {
+ super(vertexCount, vertexFormat, stripIndexCounts);
+ }
+
+ @Override
+ public NodeComponent cloneNodeComponent() {
+ TriangleStripArray newOne = new TriangleStripArray(vertexCount, vertexFormat, (int [])stripIndexCounts.clone());
+ newOne.vertexBuffer = vertexBuffer.duplicate();
+ if (normalBuffer != null) newOne.normalBuffer = normalBuffer.duplicate();
+ if (uvBuffer != null) newOne.uvBuffer = uvBuffer.duplicate();
+ return newOne;
+ }
+
+}
diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Tuple3d.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Tuple3d.java
new file mode 100644
index 0000000..d128dd8
--- /dev/null
+++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Tuple3d.java
@@ -0,0 +1,38 @@
+package com.google.ar.core.examples.java.common.java3d;
+
+public class Tuple3d {
+
+ public double x;
+ public double y;
+ public double z;
+
+ public void set(double x, double y, double z) {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ }
+
+ public void setX(double x) {
+ this.x = x;
+ }
+
+ public void setY(double y) {
+ this.y = y;
+ }
+
+ public void setZ(double z) {
+ this.z = z;
+ }
+
+ public double getX() {
+ return x;
+ }
+
+ public double getY() {
+ return y;
+ }
+
+ public double getZ() {
+ return z;
+ }
+}
diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Vector2f.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Vector2f.java
new file mode 100644
index 0000000..fe6fdbc
--- /dev/null
+++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Vector2f.java
@@ -0,0 +1,104 @@
+package com.google.ar.core.examples.java.common.java3d;
+
+public class Vector2f {
+ public float x;
+ public float y;
+
+ // コンストラクタ
+ public Vector2f() {
+ x = 0.0f;
+ y = 0.0f;
+ }
+
+ // コンストラクタ
+ public Vector2f(float px, float py) {
+ x = px;
+ y = py;
+ }
+
+ public Vector2f(Vector2f v) {
+ x = v.x;
+ y = v.y;
+ }
+
+ public Vector2f clone() {
+ return new Vector2f(this.x, this.y);
+ }
+
+ public double dot(Vector2f a) {
+ return x*a.x + y*a.y;
+ }
+
+ public float length() {
+ return (float) Math.sqrt(x*x + y*y);
+ }
+
+ public void scale(float s) {;
+ x = x*s;
+ y = y*s;
+ }
+
+ public void normalize() {
+ float l = length();
+ x = x / l;
+ y = y / l;
+ }
+
+ /** v1とv2の和を格納する */
+ public void add(Vector2f v1, Vector2f v2) {
+ this.x = v1.x + v2.x;
+ this.y = v1.y + v2.y;
+ }
+
+ /** v1との和を格納する */
+ public void add(Vector2f v1) {
+ this.x += v1.x;
+ this.y += v1.y;
+ }
+
+ /** v1とv2の差を格納する */
+ public void sub(Vector2f v1, Vector2f v2) {
+ this.x = v1.x - v2.x;
+ this.y = v1.y - v2.y;
+ }
+
+ /** v1との差を格納する */
+ public void sub(Vector2f v1) {
+ this.x -= v1.x;
+ this.y -= v1.y;
+ }
+
+ /** Vector3dをセットする */
+ public void set(Vector2f v) {
+ this.x = v.x;
+ this.y = v.y;
+ }
+
+ /** 符号を反転する */
+ public void negate() {
+ this.x = -x;
+ this.y = -y;
+ }
+
+ /** this = d * axis + p */
+ public void scaleAdd(float d, Vector2f axis, Vector2f p) {
+ this.x = d * axis.x + p.x;
+ this.y = d * axis.y + p.y;
+ }
+
+ public void setX(float x) {
+ this.x = x;
+ }
+
+ public void setY(float y) {
+ this.y = y;
+ }
+
+ public float getX() {
+ return x;
+ }
+
+ public float getY() {
+ return y;
+ }
+}
diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Vector3d.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Vector3d.java
new file mode 100644
index 0000000..35a31b7
--- /dev/null
+++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Vector3d.java
@@ -0,0 +1,134 @@
+package com.google.ar.core.examples.java.common.java3d;
+
+public class Vector3d extends Tuple3d {
+
+ // コンストラクタ
+ public Vector3d() {
+ x = 0.0;
+ y = 0.0;
+ z = 0.0;
+ }
+
+ // コンストラクタ
+ public Vector3d(double px, double py, double pz) {
+ x = px;
+ y = py;
+ z = pz;
+ }
+
+ public Vector3d(Vector3d v) {
+ x = v.x;
+ y = v.y;
+ z = v.z;
+ }
+
+ public Vector3d(Tuple3d p) {
+ x = p.x;
+ y = p.y;
+ z = p.z;
+ }
+
+ public Vector3d(double[] coordinate) {
+ x = coordinate[0];
+ y = coordinate[1];
+ z = coordinate[2];
+ }
+
+ public Vector3d clone() {
+ return new Vector3d(this.x, this.y, this.z);
+ }
+
+ public double dot(Vector3d a) {
+ return x*a.x + y*a.y + z*a.z;
+ }
+
+ public void cross(Vector3d a, Vector3d b) {
+ double x = a.y*b.z - a.z*b.y;
+ double y = a.z*b.x - a.x*b.z;
+ double z = a.x*b.y - a.y*b.x;
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ }
+
+ public double length() {
+ return Math.sqrt(x*x+y*y+z*z);
+ }
+
+ public void scale(double s) {
+ x = x*s;
+ y = y*s;
+ z = z*s;
+ }
+
+ public void normalize() {
+ double l = length();
+ x = x / l;
+ y = y / l;
+ z = z / l;
+ }
+
+ /** v1とv2の和を格納する */
+ public void add(Tuple3d v1, Tuple3d v2) {
+ this.x = v1.x + v2.x;
+ this.y = v1.y + v2.y;
+ this.z = v1.z + v2.z;
+ }
+
+ /** v1との和を格納する */
+ public void add(Tuple3d v1) {
+ this.x += v1.x;
+ this.y += v1.y;
+ this.z += v1.z;
+ }
+
+ /** v1とv2の差を格納する */
+ public void sub(Tuple3d v1, Tuple3d v2) {
+ this.x = v1.x - v2.x;
+ this.y = v1.y - v2.y;
+ this.z = v1.z - v2.z;
+ }
+
+ /** v1との差を格納する */
+ public void sub(Tuple3d v1) {
+ this.x -= v1.x;
+ this.y -= v1.y;
+ this.z -= v1.z;
+ }
+
+ /** Vector3dをセットする */
+ public void set(Tuple3d v) {
+ this.x = v.x;
+ this.y = v.y;
+ this.z = v.z;
+ }
+
+ /** 符号を反転する */
+ public void negate() {
+ this.x = -x;
+ this.y = -y;
+ this.z = -z;
+ }
+
+ /** this = d * axis + p */
+ public void scaleAdd(double d, Vector3d axis, Vector3d p) {
+ this.x = d * axis.x + p.x;
+ this.y = d * axis.y + p.y;
+ this.z = d * axis.z + p.z;
+ }
+
+ public void negate(Vector3d v) {
+ this.x = -v.x;
+ this.y = -v.y;
+ this.z = -v.z;
+ }
+
+ public double angle(Vector3d v) {
+ double d = dot(v) / (length() * v.length());
+ if (d < -1D)
+ d = -1D;
+ if (d > 1.0D)
+ d = 1.0D;
+ return Math.acos(d);
+ }
+}
diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Vector3f.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Vector3f.java
new file mode 100644
index 0000000..1c2eb65
--- /dev/null
+++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Vector3f.java
@@ -0,0 +1,134 @@
+package com.google.ar.core.examples.java.common.java3d;
+
+public class Vector3f {
+ public float x;
+ public float y;
+ public float z;
+
+ // コンストラクタ
+ public Vector3f() {
+ x = 0.0f;
+ y = 0.0f;
+ z = 0.0f;
+ }
+
+ // コンストラクタ
+ public Vector3f(float px, float py, float pz) {
+ x = px;
+ y = py;
+ z = pz;
+ }
+
+ public Vector3f(Vector3f v) {
+ x = v.x;
+ y = v.y;
+ z = v.z;
+ }
+
+ public Vector3f clone() {
+ return new Vector3f(this.x, this.y, this.z);
+ }
+
+ public double dot(Vector3f a) {
+ return x*a.x + y*a.y + z*a.z;
+ }
+
+ public void cross(Vector3f a, Vector3f b) {
+ float x = a.y*b.z - a.z*b.y;
+ float y = a.z*b.x - a.x*b.z;
+ float z = a.x*b.y - a.y*b.x;
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ }
+
+ public float length() {
+ return (float) Math.sqrt(x*x + y*y + z*z);
+ }
+
+ public void scale(float s) {;
+ x = x*s;
+ y = y*s;
+ z = z*s;
+ }
+
+ public void normalize() {
+ float l = length();
+ x = x / l;
+ y = y / l;
+ z = z / l;
+ }
+
+ /** v1とv2の和を格納する */
+ public void add(Vector3f v1, Vector3f v2) {
+ this.x = v1.x + v2.x;
+ this.y = v1.y + v2.y;
+ this.z = v1.z + v2.z;
+ }
+
+ /** v1との和を格納する */
+ public void add(Vector3f v1) {
+ this.x += v1.x;
+ this.y += v1.y;
+ this.z += v1.z;
+ }
+
+ /** v1とv2の差を格納する */
+ public void sub(Vector3f v1, Vector3f v2) {
+ this.x = v1.x - v2.x;
+ this.y = v1.y - v2.y;
+ this.z = v1.z - v2.z;
+ }
+
+ /** v1との差を格納する */
+ public void sub(Vector3f v1) {
+ this.x -= v1.x;
+ this.y -= v1.y;
+ this.z -= v1.z;
+ }
+
+ /** Vector3dをセットする */
+ public void set(Vector3f v) {
+ this.x = v.x;
+ this.y = v.y;
+ this.z = v.z;
+ }
+
+ /** 符号を反転する */
+ public void negate() {
+ this.x = -x;
+ this.y = -y;
+ this.z = -z;
+ }
+
+ /** this = d * axis + p */
+ public void scaleAdd(float d, Vector3f axis, Vector3f p) {
+ this.x = d * axis.x + p.x;
+ this.y = d * axis.y + p.y;
+ this.z = d * axis.z + p.z;
+ }
+
+ public void setX(float x) {
+ this.x = x;
+ }
+
+ public void setY(float y) {
+ this.y = y;
+ }
+
+ public void setZ(float z) {
+ this.z = z;
+ }
+
+ public float getX() {
+ return x;
+ }
+
+ public float getY() {
+ return y;
+ }
+
+ public float getZ() {
+ return z;
+ }
+}
diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Vector4d.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Vector4d.java
new file mode 100644
index 0000000..f639da6
--- /dev/null
+++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Vector4d.java
@@ -0,0 +1,184 @@
+package com.google.ar.core.examples.java.common.java3d;
+
+public class Vector4d {
+ public double x;
+ public double y;
+ public double z;
+ public double w;
+
+ // コンストラクタ
+ public Vector4d() {
+ x = 0.0;
+ y = 0.0;
+ z = 0.0;
+ w = 0.0;
+ }
+
+ // コンストラクタ
+ public Vector4d(double px, double py, double pz, double pw) {
+ x = px;
+ y = py;
+ z = pz;
+ w = pw;
+ }
+
+ public Vector4d(Vector4d v) {
+ x = v.x;
+ y = v.y;
+ z = v.z;
+ w = v.w;
+ }
+
+ public void set(double x2, double y2, double z2, double w2) {
+ x = x2;
+ y = y2;
+ z = z2;
+ w = w2;
+ }
+
+ private void set(double[] t) {
+ x = t[0];
+ y = t[1];
+ z = t[2];
+ w = t[3];
+ }
+
+ public Vector4d clone() {
+ return new Vector4d(x, y, z, w);
+ }
+
+ /** 絶対値を返す */
+ final void absolute() {
+ x = Math.abs(x);
+ y = Math.abs(y);
+ z = Math.abs(z);
+ w = Math.abs(w);
+ }
+
+ final double angle(Vector4d v1) {
+ double vDot = this.dot(v1) / (this.length() * v1.length());
+ if (vDot < -1.0)
+ vDot = -1.0;
+ if (vDot > 1.0)
+ vDot = 1.0;
+ return ((double) (Math.acos(vDot)));
+ }
+
+ public final double dot(Vector4d v1) {
+ return (this.x * v1.x + this.y * v1.y + this.z * v1.z + this.w * v1.w);
+ }
+
+ public final double length() {
+ return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z
+ + this.w * this.w);
+ }
+
+ final void clamp(double min, double max) {
+ if (x > max) {
+ x = max;
+ } else if (x < min) {
+ x = min;
+ }
+
+ if (y > max) {
+ y = max;
+ } else if (y < min) {
+ y = min;
+ }
+
+ if (z > max) {
+ z = max;
+ } else if (z < min) {
+ z = min;
+ }
+
+ if (w > max) {
+ w = max;
+ } else if (w < min) {
+ w = min;
+ }
+ }
+
+ final void clampMax(double max) {
+ if (x > max)
+ x = max;
+ if (y > max)
+ y = max;
+ if (z > max)
+ z = max;
+ if (w > max)
+ w = max;
+ }
+
+ final void clampMin(double min) {
+ if (x < min)
+ x = min;
+ if (y < min)
+ y = min;
+ if (z < min)
+ z = min;
+ if (w < min)
+ w = min;
+ }
+
+ final void normalize() {
+ double norm;
+
+ norm = 1.0 / Math.sqrt(this.x * this.x + this.y * this.y + this.z
+ * this.z + this.w * this.w);
+ this.x *= norm;
+ this.y *= norm;
+ this.z *= norm;
+ this.w *= norm;
+ }
+
+ final void normalize(Vector4d v1) {
+ double norm;
+
+ norm = 1.0 / Math.sqrt(v1.x * v1.x + v1.y * v1.y + v1.z * v1.z + v1.w
+ * v1.w);
+ this.x = v1.x * norm;
+ this.y = v1.y * norm;
+ this.z = v1.z * norm;
+ this.w = v1.w * norm;
+ }
+
+ final void get(double[] t) {
+ t[0] = this.x;
+ t[1] = this.y;
+ t[2] = this.z;
+ t[3] = this.w;
+ }
+
+ public double getX() {
+ return x;
+ }
+
+ public double getY() {
+ return y;
+ }
+
+ public double getZ() {
+ return z;
+ }
+
+ public double getW() {
+ return w;
+ }
+
+ public void setX(double x) {
+ this.x = x;
+ }
+
+ public void setY(double y) {
+ this.y = y;
+ }
+
+ public void setZ(double z) {
+ this.z = z;
+ }
+
+ public void setW(double w) {
+ this.w = w;
+ }
+}
diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Vector4f.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Vector4f.java
new file mode 100644
index 0000000..a00fbad
--- /dev/null
+++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Vector4f.java
@@ -0,0 +1,183 @@
+package com.google.ar.core.examples.java.common.java3d;
+
+public class Vector4f {
+ public float x;
+ public float y;
+ public float z;
+ public float w;
+
+ // コンストラクタ
+ public Vector4f() {
+ x = 0.0f;
+ y = 0.0f;
+ z = 0.0f;
+ w = 0.0f;
+ }
+
+ // コンストラクタ
+ public Vector4f(float px, float py, float pz, float pw) {
+ x = px;
+ y = py;
+ z = pz;
+ w = pw;
+ }
+
+ public Vector4f(Vector4f v) {
+ x = v.x;
+ y = v.y;
+ z = v.z;
+ w = v.w;
+ }
+
+ public void set(float x2, float y2, float z2, float w2) {
+ x = x2;
+ y = y2;
+ z = z2;
+ w = w2;
+ }
+
+ private void set(float[] t) {
+ x = t[0];
+ y = t[1];
+ z = t[2];
+ w = t[3];
+ }
+
+ public Vector4f clone() {
+ return new Vector4f(x, y, z, w);
+ }
+
+ /** 絶対値を返す */
+ final void absolute() {
+ x = Math.abs(x);
+ y = Math.abs(y);
+ z = Math.abs(z);
+ w = Math.abs(w);
+ }
+
+ final double angle(Vector4f v1) {
+ double vDot = this.dot(v1) / (this.length() * v1.length());
+ if (vDot < -1.0)
+ vDot = -1.0;
+ if (vDot > 1.0)
+ vDot = 1.0;
+ return ((double) (Math.acos(vDot)));
+ }
+
+ public final double dot(Vector4f v1) {
+ return (this.x * v1.x + this.y * v1.y + this.z * v1.z + this.w * v1.w);
+ }
+
+ public final double length() {
+ return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z
+ + this.w * this.w);
+ }
+
+ final void clamp(float min, float max) {
+ if (x > max) {
+ x = max;
+ } else if (x < min) {
+ x = min;
+ }
+
+ if (y > max) {
+ y = max;
+ } else if (y < min) {
+ y = min;
+ }
+
+ if (z > max) {
+ z = max;
+ } else if (z < min) {
+ z = min;
+ }
+
+ if (w > max) {
+ w = max;
+ } else if (w < min) {
+ w = min;
+ }
+ }
+
+ final void clampMax(float max) {
+ if (x > max)
+ x = max;
+ if (y > max)
+ y = max;
+ if (z > max)
+ z = max;
+ if (w > max)
+ w = max;
+ }
+
+ final void clampMin(float min) {
+ if (x < min)
+ x = min;
+ if (y < min)
+ y = min;
+ if (z < min)
+ z = min;
+ if (w < min)
+ w = min;
+ }
+
+ final void normalize() {
+ double norm;
+
+ norm = 1.0 / Math.sqrt(this.x * this.x + this.y * this.y + this.z
+ * this.z + this.w * this.w);
+ this.x *= norm;
+ this.y *= norm;
+ this.z *= norm;
+ this.w *= norm;
+ }
+
+ final void normalize(Vector4f v1) {
+ float norm;
+
+ norm = 1.0f / (float) Math.sqrt(v1.x * v1.x + v1.y * v1.y + v1.z * v1.z + v1.w * v1.w);
+ this.x = v1.x * norm;
+ this.y = v1.y * norm;
+ this.z = v1.z * norm;
+ this.w = v1.w * norm;
+ }
+
+ final void get(float[] t) {
+ t[0] = this.x;
+ t[1] = this.y;
+ t[2] = this.z;
+ t[3] = this.w;
+ }
+
+ public float getX() {
+ return x;
+ }
+
+ public float getY() {
+ return y;
+ }
+
+ public float getZ() {
+ return z;
+ }
+
+ public float getW() {
+ return w;
+ }
+
+ public void setX(float x) {
+ this.x = x;
+ }
+
+ public void setY(float y) {
+ this.y = y;
+ }
+
+ public void setZ(float z) {
+ this.z = z;
+ }
+
+ public void setW(float w) {
+ this.w = w;
+ }
+}
diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/View.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/View.java
new file mode 100644
index 0000000..a59a8c8
--- /dev/null
+++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/View.java
@@ -0,0 +1,8 @@
+package com.google.ar.core.examples.java.common.java3d;
+
+public class View {
+
+ public View() {
+
+ }
+}
diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/rendering/BackgroundRenderer.java b/app/src/main/java/com/google/ar/core/examples/java/common/rendering/BackgroundRenderer.java
new file mode 100644
index 0000000..d0070af
--- /dev/null
+++ b/app/src/main/java/com/google/ar/core/examples/java/common/rendering/BackgroundRenderer.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.ar.core.examples.java.common.rendering;
+
+import android.content.Context;
+import android.opengl.GLES11Ext;
+import android.opengl.GLES20;
+import android.opengl.GLSurfaceView;
+import android.support.annotation.NonNull;
+import com.google.ar.core.Coordinates2d;
+import com.google.ar.core.Frame;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+
+/**
+ * This class renders the AR background from camera feed. It creates and hosts the texture given to
+ * ARCore to be filled with the camera image.
+ */
+public class BackgroundRenderer {
+ private static final String TAG = BackgroundRenderer.class.getSimpleName();
+
+ // Shader names.
+ private static final String VERTEX_SHADER_NAME = "shaders/screenquad.vert";
+ private static final String FRAGMENT_SHADER_NAME = "shaders/screenquad.frag";
+
+ private static final int COORDS_PER_VERTEX = 2;
+ private static final int TEXCOORDS_PER_VERTEX = 2;
+ private static final int FLOAT_SIZE = 4;
+
+ private FloatBuffer quadCoords;
+ private FloatBuffer quadTexCoords;
+
+ private int quadProgram;
+
+ private int quadPositionParam;
+ private int quadTexCoordParam;
+ private int textureId = -1;
+
+ public int getTextureId() {
+ return textureId;
+ }
+
+ /**
+ * Allocates and initializes OpenGL resources needed by the background renderer. Must be called on
+ * the OpenGL thread, typically in {@link GLSurfaceView.Renderer#onSurfaceCreated(GL10,
+ * EGLConfig)}.
+ *
+ * @param context Needed to access shader source.
+ */
+ public void createOnGlThread(Context context) throws IOException {
+ // Generate the background texture.
+ int[] textures = new int[1];
+ GLES20.glGenTextures(1, textures, 0);
+ textureId = textures[0];
+ int textureTarget = GLES11Ext.GL_TEXTURE_EXTERNAL_OES;
+ GLES20.glBindTexture(textureTarget, textureId);
+ GLES20.glTexParameteri(textureTarget, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
+ GLES20.glTexParameteri(textureTarget, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
+ GLES20.glTexParameteri(textureTarget, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
+ GLES20.glTexParameteri(textureTarget, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
+
+ int numVertices = 4;
+ if (numVertices != QUAD_COORDS.length / COORDS_PER_VERTEX) {
+ throw new RuntimeException("Unexpected number of vertices in BackgroundRenderer.");
+ }
+
+ ByteBuffer bbCoords = ByteBuffer.allocateDirect(QUAD_COORDS.length * FLOAT_SIZE);
+ bbCoords.order(ByteOrder.nativeOrder());
+ quadCoords = bbCoords.asFloatBuffer();
+ quadCoords.put(QUAD_COORDS);
+ quadCoords.position(0);
+
+ ByteBuffer bbTexCoordsTransformed =
+ ByteBuffer.allocateDirect(numVertices * TEXCOORDS_PER_VERTEX * FLOAT_SIZE);
+ bbTexCoordsTransformed.order(ByteOrder.nativeOrder());
+ quadTexCoords = bbTexCoordsTransformed.asFloatBuffer();
+
+ int vertexShader =
+ ShaderUtil.loadGLShader(TAG, context, GLES20.GL_VERTEX_SHADER, VERTEX_SHADER_NAME);
+ int fragmentShader =
+ ShaderUtil.loadGLShader(TAG, context, GLES20.GL_FRAGMENT_SHADER, FRAGMENT_SHADER_NAME);
+
+ quadProgram = GLES20.glCreateProgram();
+ GLES20.glAttachShader(quadProgram, vertexShader);
+ GLES20.glAttachShader(quadProgram, fragmentShader);
+ GLES20.glLinkProgram(quadProgram);
+ GLES20.glUseProgram(quadProgram);
+
+ ShaderUtil.checkGLError(TAG, "Program creation");
+
+ quadPositionParam = GLES20.glGetAttribLocation(quadProgram, "a_Position");
+ quadTexCoordParam = GLES20.glGetAttribLocation(quadProgram, "a_TexCoord");
+
+ ShaderUtil.checkGLError(TAG, "Program parameters");
+ }
+
+ /**
+ * Draws the AR background image. The image will be drawn such that virtual content rendered with
+ * the matrices provided by {@link com.google.ar.core.Camera#getViewMatrix(float[], int)} and
+ * {@link com.google.ar.core.Camera#getProjectionMatrix(float[], int, float, float)} will
+ * accurately follow static physical objects. This must be called before drawing virtual
+ * content.
+ *
+ * @param frame The current {@code Frame} as returned by {@link Session#update()}.
+ */
+ public void draw(@NonNull Frame frame) {
+ // If display rotation changed (also includes view size change), we need to re-query the uv
+ // coordinates for the screen rect, as they may have changed as well.
+ if (frame.hasDisplayGeometryChanged()) {
+ frame.transformCoordinates2d(
+ Coordinates2d.OPENGL_NORMALIZED_DEVICE_COORDINATES,
+ quadCoords,
+ Coordinates2d.TEXTURE_NORMALIZED,
+ quadTexCoords);
+ }
+
+ if (frame.getTimestamp() == 0) {
+ // Suppress rendering if the camera did not produce the first frame yet. This is to avoid
+ // drawing possible leftover data from previous sessions if the texture is reused.
+ return;
+ }
+
+ draw();
+ }
+
+ /**
+ * Draws the camera image using the currently configured {@link BackgroundRenderer#quadTexCoords}
+ * image texture coordinates.
+ *
+ * The image will be center cropped if the camera sensor aspect ratio does not match the screen
+ * aspect ratio, which matches the cropping behavior of {@link
+ * Frame#transformCoordinates2d(Coordinates2d, float[], Coordinates2d, float[])}.
+ */
+ public void draw(
+ int imageWidth, int imageHeight, float screenAspectRatio, int cameraToDisplayRotation) {
+ // Crop the camera image to fit the screen aspect ratio.
+ float imageAspectRatio = (float) imageWidth / imageHeight;
+ float croppedWidth;
+ float croppedHeight;
+ if (screenAspectRatio < imageAspectRatio) {
+ croppedWidth = imageHeight * screenAspectRatio;
+ croppedHeight = imageHeight;
+ } else {
+ croppedWidth = imageWidth;
+ croppedHeight = imageWidth / screenAspectRatio;
+ }
+
+ float u = (imageWidth - croppedWidth) / imageWidth * 0.5f;
+ float v = (imageHeight - croppedHeight) / imageHeight * 0.5f;
+
+ float[] texCoordTransformed;
+ switch (cameraToDisplayRotation) {
+ case 90:
+ texCoordTransformed = new float[] {1 - u, 1 - v, u, 1 - v, 1 - u, v, u, v};
+ break;
+ case 180:
+ texCoordTransformed = new float[] {1 - u, v, 1 - u, 1 - v, u, v, u, 1 - v};
+ break;
+ case 270:
+ texCoordTransformed = new float[] {u, v, 1 - u, v, u, 1 - v, 1 - u, 1 - v};
+ break;
+ case 0:
+ texCoordTransformed = new float[] {u, 1 - v, u, v, 1 - u, 1 - v, 1 - u, v};
+ break;
+ default:
+ throw new IllegalArgumentException("Unhandled rotation: " + cameraToDisplayRotation);
+ }
+
+ // Write image texture coordinates.
+ quadTexCoords.position(0);
+ quadTexCoords.put(texCoordTransformed);
+
+ draw();
+ }
+
+ /**
+ * Draws the camera background image using the currently configured {@link
+ * BackgroundRenderer#quadTexCoords} image texture coordinates.
+ */
+ private void draw() {
+ // Ensure position is rewound before use.
+ quadTexCoords.position(0);
+
+ // No need to test or write depth, the screen quad has arbitrary depth, and is expected
+ // to be drawn first.
+ GLES20.glDisable(GLES20.GL_DEPTH_TEST);
+ GLES20.glDepthMask(false);
+
+ GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
+ GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureId);
+
+ GLES20.glUseProgram(quadProgram);
+
+ // Set the vertex positions.
+ GLES20.glVertexAttribPointer(
+ quadPositionParam, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, 0, quadCoords);
+
+ // Set the texture coordinates.
+ GLES20.glVertexAttribPointer(
+ quadTexCoordParam, TEXCOORDS_PER_VERTEX, GLES20.GL_FLOAT, false, 0, quadTexCoords);
+
+ // Enable vertex arrays
+ GLES20.glEnableVertexAttribArray(quadPositionParam);
+ GLES20.glEnableVertexAttribArray(quadTexCoordParam);
+
+ GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
+
+ // Disable vertex arrays
+ GLES20.glDisableVertexAttribArray(quadPositionParam);
+ GLES20.glDisableVertexAttribArray(quadTexCoordParam);
+
+ // Restore the depth state for further drawing.
+ GLES20.glDepthMask(true);
+ GLES20.glEnable(GLES20.GL_DEPTH_TEST);
+
+ ShaderUtil.checkGLError(TAG, "BackgroundRendererDraw");
+ }
+
+ private static final float[] QUAD_COORDS =
+ new float[] {
+ -1.0f, -1.0f, -1.0f, +1.0f, +1.0f, -1.0f, +1.0f, +1.0f,
+ };
+}
diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/rendering/ObjectRenderer.java b/app/src/main/java/com/google/ar/core/examples/java/common/rendering/ObjectRenderer.java
new file mode 100644
index 0000000..9dfc0df
--- /dev/null
+++ b/app/src/main/java/com/google/ar/core/examples/java/common/rendering/ObjectRenderer.java
@@ -0,0 +1,389 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.ar.core.examples.java.common.rendering;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.opengl.GLES20;
+import android.opengl.GLUtils;
+import android.opengl.Matrix;
+import de.javagl.obj.Obj;
+import de.javagl.obj.ObjData;
+import de.javagl.obj.ObjReader;
+import de.javagl.obj.ObjUtils;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+import java.nio.ShortBuffer;
+
+/** Renders an object loaded from an OBJ file in OpenGL. */
+public class ObjectRenderer {
+ private static final String TAG = ObjectRenderer.class.getSimpleName();
+
+ /**
+ * Blend mode.
+ *
+ * @see #setBlendMode(BlendMode)
+ */
+ public enum BlendMode {
+ /** Multiplies the destination color by the source alpha. */
+ Shadow,
+ /** Normal alpha blending. */
+ Grid
+ }
+
+ // Shader names.
+ private static final String VERTEX_SHADER_NAME = "shaders/object.vert";
+ private static final String FRAGMENT_SHADER_NAME = "shaders/object.frag";
+
+ private static final int COORDS_PER_VERTEX = 3;
+ private static final float[] DEFAULT_COLOR = new float[] {0f, 0f, 0f, 0f};
+
+ // Note: the last component must be zero to avoid applying the translational part of the matrix.
+ private static final float[] LIGHT_DIRECTION = new float[] {0.250f, 0.866f, 0.433f, 0.0f};
+ private final float[] viewLightDirection = new float[4];
+
+ // Object vertex buffer variables.
+ private int vertexBufferId;
+ private int verticesBaseAddress;
+ private int texCoordsBaseAddress;
+ private int normalsBaseAddress;
+ private int indexBufferId;
+ private int indexCount;
+
+ private int program;
+ private final int[] textures = new int[1];
+
+ // Shader location: model view projection matrix.
+ private int modelViewUniform;
+ private int modelViewProjectionUniform;
+
+ // Shader location: object attributes.
+ private int positionAttribute;
+ private int normalAttribute;
+ private int texCoordAttribute;
+
+ // Shader location: texture sampler.
+ private int textureUniform;
+
+ // Shader location: environment properties.
+ private int lightingParametersUniform;
+
+ // Shader location: material properties.
+ private int materialParametersUniform;
+
+ // Shader location: color correction property
+ private int colorCorrectionParameterUniform;
+
+ // Shader location: object color property (to change the primary color of the object).
+ private int colorUniform;
+
+ private BlendMode blendMode = null;
+
+ // Temporary matrices allocated here to reduce number of allocations for each frame.
+ private final float[] modelMatrix = new float[16];
+ private final float[] modelViewMatrix = new float[16];
+ private final float[] modelViewProjectionMatrix = new float[16];
+
+ // Set some default material properties to use for lighting.
+ private float ambient = 0.3f;
+ private float diffuse = 1.0f;
+ private float specular = 1.0f;
+ private float specularPower = 6.0f;
+
+ public ObjectRenderer() {}
+
+ /**
+ * Creates and initializes OpenGL resources needed for rendering the model.
+ *
+ * @param context Context for loading the shader and below-named model and texture assets.
+ * @param objAssetName Name of the OBJ file containing the model geometry.
+ * @param diffuseTextureAssetName Name of the PNG file containing the diffuse texture map.
+ */
+ public void createOnGlThread(Context context, String objAssetName, String diffuseTextureAssetName)
+ throws IOException {
+ final int vertexShader =
+ ShaderUtil.loadGLShader(TAG, context, GLES20.GL_VERTEX_SHADER, VERTEX_SHADER_NAME);
+ final int fragmentShader =
+ ShaderUtil.loadGLShader(TAG, context, GLES20.GL_FRAGMENT_SHADER, FRAGMENT_SHADER_NAME);
+
+ program = GLES20.glCreateProgram();
+ GLES20.glAttachShader(program, vertexShader);
+ GLES20.glAttachShader(program, fragmentShader);
+ GLES20.glLinkProgram(program);
+ GLES20.glUseProgram(program);
+
+ ShaderUtil.checkGLError(TAG, "Program creation");
+
+ modelViewUniform = GLES20.glGetUniformLocation(program, "u_ModelView");
+ modelViewProjectionUniform = GLES20.glGetUniformLocation(program, "u_ModelViewProjection");
+
+ positionAttribute = GLES20.glGetAttribLocation(program, "a_Position");
+ normalAttribute = GLES20.glGetAttribLocation(program, "a_Normal");
+ texCoordAttribute = GLES20.glGetAttribLocation(program, "a_TexCoord");
+
+ textureUniform = GLES20.glGetUniformLocation(program, "u_Texture");
+
+ lightingParametersUniform = GLES20.glGetUniformLocation(program, "u_LightingParameters");
+ materialParametersUniform = GLES20.glGetUniformLocation(program, "u_MaterialParameters");
+ colorCorrectionParameterUniform =
+ GLES20.glGetUniformLocation(program, "u_ColorCorrectionParameters");
+ colorUniform = GLES20.glGetUniformLocation(program, "u_ObjColor");
+
+ ShaderUtil.checkGLError(TAG, "Program parameters");
+
+ // Read the texture.
+ Bitmap textureBitmap =
+ BitmapFactory.decodeStream(context.getAssets().open(diffuseTextureAssetName));
+
+ GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
+ GLES20.glGenTextures(textures.length, textures, 0);
+ GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0]);
+
+ GLES20.glTexParameteri(
+ GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR_MIPMAP_LINEAR);
+ GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
+ GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, textureBitmap, 0);
+ GLES20.glGenerateMipmap(GLES20.GL_TEXTURE_2D);
+ GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
+
+ textureBitmap.recycle();
+
+ ShaderUtil.checkGLError(TAG, "Texture loading");
+
+ // Read the obj file.
+ InputStream objInputStream = context.getAssets().open(objAssetName);
+ Obj obj = ObjReader.read(objInputStream);
+
+ // Prepare the Obj so that its structure is suitable for
+ // rendering with OpenGL:
+ // 1. Triangulate it
+ // 2. Make sure that texture coordinates are not ambiguous
+ // 3. Make sure that normals are not ambiguous
+ // 4. Convert it to single-indexed data
+ obj = ObjUtils.convertToRenderable(obj);
+
+ // OpenGL does not use Java arrays. ByteBuffers are used instead to provide data in a format
+ // that OpenGL understands.
+
+ // Obtain the data from the OBJ, as direct buffers:
+ IntBuffer wideIndices = ObjData.getFaceVertexIndices(obj, 3);
+ FloatBuffer vertices = ObjData.getVertices(obj);
+ FloatBuffer texCoords = ObjData.getTexCoords(obj, 2);
+ FloatBuffer normals = ObjData.getNormals(obj);
+
+ // Convert int indices to shorts for GL ES 2.0 compatibility
+ ShortBuffer indices =
+ ByteBuffer.allocateDirect(2 * wideIndices.limit())
+ .order(ByteOrder.nativeOrder())
+ .asShortBuffer();
+ while (wideIndices.hasRemaining()) {
+ indices.put((short) wideIndices.get());
+ }
+ indices.rewind();
+
+ int[] buffers = new int[2];
+ GLES20.glGenBuffers(2, buffers, 0);
+ vertexBufferId = buffers[0];
+ indexBufferId = buffers[1];
+
+ // Load vertex buffer
+ verticesBaseAddress = 0;
+ texCoordsBaseAddress = verticesBaseAddress + 4 * vertices.limit();
+ normalsBaseAddress = texCoordsBaseAddress + 4 * texCoords.limit();
+ final int totalBytes = normalsBaseAddress + 4 * normals.limit();
+
+ GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vertexBufferId);
+ GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, totalBytes, null, GLES20.GL_STATIC_DRAW);
+ GLES20.glBufferSubData(
+ GLES20.GL_ARRAY_BUFFER, verticesBaseAddress, 4 * vertices.limit(), vertices);
+ GLES20.glBufferSubData(
+ GLES20.GL_ARRAY_BUFFER, texCoordsBaseAddress, 4 * texCoords.limit(), texCoords);
+ GLES20.glBufferSubData(
+ GLES20.GL_ARRAY_BUFFER, normalsBaseAddress, 4 * normals.limit(), normals);
+ GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
+
+ // Load index buffer
+ GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, indexBufferId);
+ indexCount = indices.limit();
+ GLES20.glBufferData(
+ GLES20.GL_ELEMENT_ARRAY_BUFFER, 2 * indexCount, indices, GLES20.GL_STATIC_DRAW);
+ GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);
+
+ ShaderUtil.checkGLError(TAG, "OBJ buffer load");
+
+ Matrix.setIdentityM(modelMatrix, 0);
+ }
+
+ /**
+ * Selects the blending mode for rendering.
+ *
+ * @param blendMode The blending mode. Null indicates no blending (opaque rendering).
+ */
+ public void setBlendMode(BlendMode blendMode) {
+ this.blendMode = blendMode;
+ }
+
+ /**
+ * Updates the object model matrix and applies scaling.
+ *
+ * @param modelMatrix A 4x4 model-to-world transformation matrix, stored in column-major order.
+ * @param scaleFactor A separate scaling factor to apply before the {@code modelMatrix}.
+ * @see android.opengl.Matrix
+ */
+ public void updateModelMatrix(float[] modelMatrix, float scaleFactor) {
+ float[] scaleMatrix = new float[16];
+ Matrix.setIdentityM(scaleMatrix, 0);
+ scaleMatrix[0] = scaleFactor;
+ scaleMatrix[5] = scaleFactor;
+ scaleMatrix[10] = scaleFactor;
+ Matrix.multiplyMM(this.modelMatrix, 0, modelMatrix, 0, scaleMatrix, 0);
+ }
+
+ /**
+ * Sets the surface characteristics of the rendered model.
+ *
+ * @param ambient Intensity of non-directional surface illumination.
+ * @param diffuse Diffuse (matte) surface reflectivity.
+ * @param specular Specular (shiny) surface reflectivity.
+ * @param specularPower Surface shininess. Larger values result in a smaller, sharper specular
+ * highlight.
+ */
+ public void setMaterialProperties(
+ float ambient, float diffuse, float specular, float specularPower) {
+ this.ambient = ambient;
+ this.diffuse = diffuse;
+ this.specular = specular;
+ this.specularPower = specularPower;
+ }
+
+ /**
+ * Draws the model.
+ *
+ * @param cameraView A 4x4 view matrix, in column-major order.
+ * @param cameraPerspective A 4x4 projection matrix, in column-major order.
+ * @param lightIntensity Illumination intensity. Combined with diffuse and specular material
+ * properties.
+ * @see #setBlendMode(BlendMode)
+ * @see #updateModelMatrix(float[], float)
+ * @see #setMaterialProperties(float, float, float, float)
+ * @see android.opengl.Matrix
+ */
+ public void draw(float[] cameraView, float[] cameraPerspective, float[] colorCorrectionRgba) {
+ draw(cameraView, cameraPerspective, colorCorrectionRgba, DEFAULT_COLOR);
+ }
+
+ public void draw(
+ float[] cameraView,
+ float[] cameraPerspective,
+ float[] colorCorrectionRgba,
+ float[] objColor) {
+
+ ShaderUtil.checkGLError(TAG, "Before draw");
+
+ // Build the ModelView and ModelViewProjection matrices
+ // for calculating object position and light.
+ Matrix.multiplyMM(modelViewMatrix, 0, cameraView, 0, modelMatrix, 0);
+ Matrix.multiplyMM(modelViewProjectionMatrix, 0, cameraPerspective, 0, modelViewMatrix, 0);
+
+ GLES20.glUseProgram(program);
+
+ // Set the lighting environment properties.
+ Matrix.multiplyMV(viewLightDirection, 0, modelViewMatrix, 0, LIGHT_DIRECTION, 0);
+ normalizeVec3(viewLightDirection);
+ GLES20.glUniform4f(
+ lightingParametersUniform,
+ viewLightDirection[0],
+ viewLightDirection[1],
+ viewLightDirection[2],
+ 1.f);
+ GLES20.glUniform4fv(colorCorrectionParameterUniform, 1, colorCorrectionRgba, 0);
+
+ // Set the object color property.
+ GLES20.glUniform4fv(colorUniform, 1, objColor, 0);
+
+ // Set the object material properties.
+ GLES20.glUniform4f(materialParametersUniform, ambient, diffuse, specular, specularPower);
+
+ // Attach the object texture.
+ GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
+ GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0]);
+ GLES20.glUniform1i(textureUniform, 0);
+
+ // Set the vertex attributes.
+ GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vertexBufferId);
+
+ GLES20.glVertexAttribPointer(
+ positionAttribute, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, 0, verticesBaseAddress);
+ GLES20.glVertexAttribPointer(normalAttribute, 3, GLES20.GL_FLOAT, false, 0, normalsBaseAddress);
+ GLES20.glVertexAttribPointer(
+ texCoordAttribute, 2, GLES20.GL_FLOAT, false, 0, texCoordsBaseAddress);
+
+ GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
+
+ // Set the ModelViewProjection matrix in the shader.
+ GLES20.glUniformMatrix4fv(modelViewUniform, 1, false, modelViewMatrix, 0);
+ GLES20.glUniformMatrix4fv(modelViewProjectionUniform, 1, false, modelViewProjectionMatrix, 0);
+
+ // Enable vertex arrays
+ GLES20.glEnableVertexAttribArray(positionAttribute);
+ GLES20.glEnableVertexAttribArray(normalAttribute);
+ GLES20.glEnableVertexAttribArray(texCoordAttribute);
+
+ if (blendMode != null) {
+ GLES20.glDepthMask(false);
+ GLES20.glEnable(GLES20.GL_BLEND);
+ switch (blendMode) {
+ case Shadow:
+ // Multiplicative blending function for Shadow.
+ GLES20.glBlendFunc(GLES20.GL_ZERO, GLES20.GL_ONE_MINUS_SRC_ALPHA);
+ break;
+ case Grid:
+ // Grid, additive blending function.
+ GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
+ break;
+ }
+ }
+
+ GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, indexBufferId);
+ GLES20.glDrawElements(GLES20.GL_TRIANGLES, indexCount, GLES20.GL_UNSIGNED_SHORT, 0);
+ GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);
+
+ if (blendMode != null) {
+ GLES20.glDisable(GLES20.GL_BLEND);
+ GLES20.glDepthMask(true);
+ }
+
+ // Disable vertex arrays
+ GLES20.glDisableVertexAttribArray(positionAttribute);
+ GLES20.glDisableVertexAttribArray(normalAttribute);
+ GLES20.glDisableVertexAttribArray(texCoordAttribute);
+
+ GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
+
+ ShaderUtil.checkGLError(TAG, "After draw");
+ }
+
+ private static void normalizeVec3(float[] v) {
+ float reciprocalLength = 1.0f / (float) Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
+ v[0] *= reciprocalLength;
+ v[1] *= reciprocalLength;
+ v[2] *= reciprocalLength;
+ }
+}
diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/rendering/PlaneRenderer.java b/app/src/main/java/com/google/ar/core/examples/java/common/rendering/PlaneRenderer.java
new file mode 100644
index 0000000..0d22246
--- /dev/null
+++ b/app/src/main/java/com/google/ar/core/examples/java/common/rendering/PlaneRenderer.java
@@ -0,0 +1,419 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.ar.core.examples.java.common.rendering;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.opengl.GLES20;
+import android.opengl.GLSurfaceView;
+import android.opengl.GLUtils;
+import android.opengl.Matrix;
+import com.google.ar.core.Camera;
+import com.google.ar.core.Plane;
+import com.google.ar.core.Pose;
+import com.google.ar.core.TrackingState;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+import java.nio.ShortBuffer;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/** Renders the detected AR planes. */
+public class PlaneRenderer {
+ private static final String TAG = PlaneRenderer.class.getSimpleName();
+
+ // Shader names.
+ private static final String VERTEX_SHADER_NAME = "shaders/plane.vert";
+ private static final String FRAGMENT_SHADER_NAME = "shaders/plane.frag";
+
+ private static final int BYTES_PER_FLOAT = Float.SIZE / 8;
+ private static final int BYTES_PER_SHORT = Short.SIZE / 8;
+ private static final int COORDS_PER_VERTEX = 3; // x, z, alpha
+
+ private static final int VERTS_PER_BOUNDARY_VERT = 2;
+ private static final int INDICES_PER_BOUNDARY_VERT = 3;
+ private static final int INITIAL_BUFFER_BOUNDARY_VERTS = 64;
+
+ private static final int INITIAL_VERTEX_BUFFER_SIZE_BYTES =
+ BYTES_PER_FLOAT * COORDS_PER_VERTEX * VERTS_PER_BOUNDARY_VERT * INITIAL_BUFFER_BOUNDARY_VERTS;
+
+ private static final int INITIAL_INDEX_BUFFER_SIZE_BYTES =
+ BYTES_PER_SHORT
+ * INDICES_PER_BOUNDARY_VERT
+ * INDICES_PER_BOUNDARY_VERT
+ * INITIAL_BUFFER_BOUNDARY_VERTS;
+
+ private static final float FADE_RADIUS_M = 0.25f;
+ private static final float DOTS_PER_METER = 10.0f;
+ private static final float EQUILATERAL_TRIANGLE_SCALE = (float) (1 / Math.sqrt(3));
+
+ // Using the "signed distance field" approach to render sharp lines and circles.
+ // {dotThreshold, lineThreshold, lineFadeSpeed, occlusionScale}
+ // dotThreshold/lineThreshold: red/green intensity above which dots/lines are present
+ // lineFadeShrink: lines will fade in between alpha = 1-(1/lineFadeShrink) and 1.0
+ // occlusionShrink: occluded planes will fade out between alpha = 0 and 1/occlusionShrink
+ private static final float[] GRID_CONTROL = {0.2f, 0.4f, 2.0f, 1.5f};
+
+ private int planeProgram;
+ private final int[] textures = new int[1];
+
+ private int planeXZPositionAlphaAttribute;
+
+ private int planeModelUniform;
+ private int planeNormalUniform;
+ private int planeModelViewProjectionUniform;
+ private int textureUniform;
+ private int lineColorUniform;
+ private int dotColorUniform;
+ private int gridControlUniform;
+ private int planeUvMatrixUniform;
+
+ private FloatBuffer vertexBuffer =
+ ByteBuffer.allocateDirect(INITIAL_VERTEX_BUFFER_SIZE_BYTES)
+ .order(ByteOrder.nativeOrder())
+ .asFloatBuffer();
+ private ShortBuffer indexBuffer =
+ ByteBuffer.allocateDirect(INITIAL_INDEX_BUFFER_SIZE_BYTES)
+ .order(ByteOrder.nativeOrder())
+ .asShortBuffer();
+
+ // Temporary lists/matrices allocated here to reduce number of allocations for each frame.
+ private final float[] modelMatrix = new float[16];
+ private final float[] modelViewMatrix = new float[16];
+ private final float[] modelViewProjectionMatrix = new float[16];
+ private final float[] planeColor = new float[] {1f, 1f, 1f, 1f};
+ private final float[] planeAngleUvMatrix =
+ new float[4]; // 2x2 rotation matrix applied to uv coords.
+
+ private final Map planeIndexMap = new HashMap<>();
+
+ public PlaneRenderer() {}
+
+ /**
+ * Allocates and initializes OpenGL resources needed by the plane renderer. Must be called on the
+ * OpenGL thread, typically in {@link GLSurfaceView.Renderer#onSurfaceCreated(GL10, EGLConfig)}.
+ *
+ * @param context Needed to access shader source and texture PNG.
+ * @param gridDistanceTextureName Name of the PNG file containing the grid texture.
+ */
+ public void createOnGlThread(Context context, String gridDistanceTextureName) throws IOException {
+ int vertexShader =
+ ShaderUtil.loadGLShader(TAG, context, GLES20.GL_VERTEX_SHADER, VERTEX_SHADER_NAME);
+ int passthroughShader =
+ ShaderUtil.loadGLShader(TAG, context, GLES20.GL_FRAGMENT_SHADER, FRAGMENT_SHADER_NAME);
+
+ planeProgram = GLES20.glCreateProgram();
+ GLES20.glAttachShader(planeProgram, vertexShader);
+ GLES20.glAttachShader(planeProgram, passthroughShader);
+ GLES20.glLinkProgram(planeProgram);
+ GLES20.glUseProgram(planeProgram);
+
+ ShaderUtil.checkGLError(TAG, "Program creation");
+
+ // Read the texture.
+ Bitmap textureBitmap =
+ BitmapFactory.decodeStream(context.getAssets().open(gridDistanceTextureName));
+
+ GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
+ GLES20.glGenTextures(textures.length, textures, 0);
+ GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0]);
+
+ GLES20.glTexParameteri(
+ GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR_MIPMAP_LINEAR);
+ GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
+ GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, textureBitmap, 0);
+ GLES20.glGenerateMipmap(GLES20.GL_TEXTURE_2D);
+ GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
+
+ ShaderUtil.checkGLError(TAG, "Texture loading");
+
+ planeXZPositionAlphaAttribute = GLES20.glGetAttribLocation(planeProgram, "a_XZPositionAlpha");
+
+ planeModelUniform = GLES20.glGetUniformLocation(planeProgram, "u_Model");
+ planeNormalUniform = GLES20.glGetUniformLocation(planeProgram, "u_Normal");
+ planeModelViewProjectionUniform =
+ GLES20.glGetUniformLocation(planeProgram, "u_ModelViewProjection");
+ textureUniform = GLES20.glGetUniformLocation(planeProgram, "u_Texture");
+ lineColorUniform = GLES20.glGetUniformLocation(planeProgram, "u_lineColor");
+ dotColorUniform = GLES20.glGetUniformLocation(planeProgram, "u_dotColor");
+ gridControlUniform = GLES20.glGetUniformLocation(planeProgram, "u_gridControl");
+ planeUvMatrixUniform = GLES20.glGetUniformLocation(planeProgram, "u_PlaneUvMatrix");
+
+ ShaderUtil.checkGLError(TAG, "Program parameters");
+ }
+
+ /** Updates the plane model transform matrix and extents. */
+ private void updatePlaneParameters(
+ float[] planeMatrix, float extentX, float extentZ, FloatBuffer boundary) {
+ System.arraycopy(planeMatrix, 0, modelMatrix, 0, 16);
+ if (boundary == null) {
+ vertexBuffer.limit(0);
+ indexBuffer.limit(0);
+ return;
+ }
+
+ // Generate a new set of vertices and a corresponding triangle strip index set so that
+ // the plane boundary polygon has a fading edge. This is done by making a copy of the
+ // boundary polygon vertices and scaling it down around center to push it inwards. Then
+ // the index buffer is setup accordingly.
+ boundary.rewind();
+ int boundaryVertices = boundary.limit() / 2;
+ int numVertices;
+ int numIndices;
+
+ numVertices = boundaryVertices * VERTS_PER_BOUNDARY_VERT;
+ // drawn as GL_TRIANGLE_STRIP with 3n-2 triangles (n-2 for fill, 2n for perimeter).
+ numIndices = boundaryVertices * INDICES_PER_BOUNDARY_VERT;
+
+ if (vertexBuffer.capacity() < numVertices * COORDS_PER_VERTEX) {
+ int size = vertexBuffer.capacity();
+ while (size < numVertices * COORDS_PER_VERTEX) {
+ size *= 2;
+ }
+ vertexBuffer =
+ ByteBuffer.allocateDirect(BYTES_PER_FLOAT * size)
+ .order(ByteOrder.nativeOrder())
+ .asFloatBuffer();
+ }
+ vertexBuffer.rewind();
+ vertexBuffer.limit(numVertices * COORDS_PER_VERTEX);
+
+ if (indexBuffer.capacity() < numIndices) {
+ int size = indexBuffer.capacity();
+ while (size < numIndices) {
+ size *= 2;
+ }
+ indexBuffer =
+ ByteBuffer.allocateDirect(BYTES_PER_SHORT * size)
+ .order(ByteOrder.nativeOrder())
+ .asShortBuffer();
+ }
+ indexBuffer.rewind();
+ indexBuffer.limit(numIndices);
+
+ // Note: when either dimension of the bounding box is smaller than 2*FADE_RADIUS_M we
+ // generate a bunch of 0-area triangles. These don't get rendered though so it works
+ // out ok.
+ float xScale = Math.max((extentX - 2 * FADE_RADIUS_M) / extentX, 0.0f);
+ float zScale = Math.max((extentZ - 2 * FADE_RADIUS_M) / extentZ, 0.0f);
+
+ while (boundary.hasRemaining()) {
+ float x = boundary.get();
+ float z = boundary.get();
+ vertexBuffer.put(x);
+ vertexBuffer.put(z);
+ vertexBuffer.put(0.0f);
+ vertexBuffer.put(x * xScale);
+ vertexBuffer.put(z * zScale);
+ vertexBuffer.put(1.0f);
+ }
+
+ // step 1, perimeter
+ indexBuffer.put((short) ((boundaryVertices - 1) * 2));
+ for (int i = 0; i < boundaryVertices; ++i) {
+ indexBuffer.put((short) (i * 2));
+ indexBuffer.put((short) (i * 2 + 1));
+ }
+ indexBuffer.put((short) 1);
+ // This leaves us on the interior edge of the perimeter between the inset vertices
+ // for boundary verts n-1 and 0.
+
+ // step 2, interior:
+ for (int i = 1; i < boundaryVertices / 2; ++i) {
+ indexBuffer.put((short) ((boundaryVertices - 1 - i) * 2 + 1));
+ indexBuffer.put((short) (i * 2 + 1));
+ }
+ if (boundaryVertices % 2 != 0) {
+ indexBuffer.put((short) ((boundaryVertices / 2) * 2 + 1));
+ }
+ }
+
+ private void draw(float[] cameraView, float[] cameraPerspective, float[] planeNormal) {
+ // Build the ModelView and ModelViewProjection matrices
+ // for calculating cube position and light.
+ Matrix.multiplyMM(modelViewMatrix, 0, cameraView, 0, modelMatrix, 0);
+ Matrix.multiplyMM(modelViewProjectionMatrix, 0, cameraPerspective, 0, modelViewMatrix, 0);
+
+ // Set the position of the plane
+ vertexBuffer.rewind();
+ GLES20.glVertexAttribPointer(
+ planeXZPositionAlphaAttribute,
+ COORDS_PER_VERTEX,
+ GLES20.GL_FLOAT,
+ false,
+ BYTES_PER_FLOAT * COORDS_PER_VERTEX,
+ vertexBuffer);
+
+ // Set the Model and ModelViewProjection matrices in the shader.
+ GLES20.glUniformMatrix4fv(planeModelUniform, 1, false, modelMatrix, 0);
+ GLES20.glUniform3f(planeNormalUniform, planeNormal[0], planeNormal[1], planeNormal[2]);
+ GLES20.glUniformMatrix4fv(
+ planeModelViewProjectionUniform, 1, false, modelViewProjectionMatrix, 0);
+
+ indexBuffer.rewind();
+ GLES20.glDrawElements(
+ GLES20.GL_TRIANGLE_STRIP, indexBuffer.limit(), GLES20.GL_UNSIGNED_SHORT, indexBuffer);
+ ShaderUtil.checkGLError(TAG, "Drawing plane");
+ }
+
+ static class SortablePlane {
+ final float distance;
+ final Plane plane;
+
+ SortablePlane(float distance, Plane plane) {
+ this.distance = distance;
+ this.plane = plane;
+ }
+ }
+
+ /**
+ * Draws the collection of tracked planes, with closer planes hiding more distant ones.
+ *
+ * @param allPlanes The collection of planes to draw.
+ * @param cameraPose The pose of the camera, as returned by {@link Camera#getPose()}
+ * @param cameraPerspective The projection matrix, as returned by {@link
+ * Camera#getProjectionMatrix(float[], int, float, float)}
+ */
+ public void drawPlanes(Collection allPlanes, Pose cameraPose, float[] cameraPerspective) {
+ // Planes must be sorted by distance from camera so that we draw closer planes first, and
+ // they occlude the farther planes.
+ List sortedPlanes = new ArrayList<>();
+
+ for (Plane plane : allPlanes) {
+ if (plane.getTrackingState() != TrackingState.TRACKING || plane.getSubsumedBy() != null) {
+ continue;
+ }
+
+ float distance = calculateDistanceToPlane(plane.getCenterPose(), cameraPose);
+ if (distance < 0) { // Plane is back-facing.
+ continue;
+ }
+ sortedPlanes.add(new SortablePlane(distance, plane));
+ }
+ Collections.sort(
+ sortedPlanes,
+ new Comparator() {
+ @Override
+ public int compare(SortablePlane a, SortablePlane b) {
+ return Float.compare(a.distance, b.distance);
+ }
+ });
+
+ float[] cameraView = new float[16];
+ cameraPose.inverse().toMatrix(cameraView, 0);
+
+ // Planes are drawn with additive blending, masked by the alpha channel for occlusion.
+
+ // Start by clearing the alpha channel of the color buffer to 1.0.
+ GLES20.glClearColor(1, 1, 1, 1);
+ GLES20.glColorMask(false, false, false, true);
+ GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
+ GLES20.glColorMask(true, true, true, true);
+
+ // Disable depth write.
+ GLES20.glDepthMask(false);
+
+ // Additive blending, masked by alpha channel, clearing alpha channel.
+ GLES20.glEnable(GLES20.GL_BLEND);
+ GLES20.glBlendFuncSeparate(
+ GLES20.GL_DST_ALPHA, GLES20.GL_ONE, // RGB (src, dest)
+ GLES20.GL_ZERO, GLES20.GL_ONE_MINUS_SRC_ALPHA); // ALPHA (src, dest)
+
+ // Set up the shader.
+ GLES20.glUseProgram(planeProgram);
+
+ // Attach the texture.
+ GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
+ GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0]);
+ GLES20.glUniform1i(textureUniform, 0);
+
+ // Shared fragment uniforms.
+ GLES20.glUniform4fv(gridControlUniform, 1, GRID_CONTROL, 0);
+
+ // Enable vertex arrays
+ GLES20.glEnableVertexAttribArray(planeXZPositionAlphaAttribute);
+
+ ShaderUtil.checkGLError(TAG, "Setting up to draw planes");
+
+ for (SortablePlane sortedPlane : sortedPlanes) {
+ Plane plane = sortedPlane.plane;
+ float[] planeMatrix = new float[16];
+ plane.getCenterPose().toMatrix(planeMatrix, 0);
+
+ float[] normal = new float[3];
+ // Get transformed Y axis of plane's coordinate system.
+ plane.getCenterPose().getTransformedAxis(1, 1.0f, normal, 0);
+
+ updatePlaneParameters(
+ planeMatrix, plane.getExtentX(), plane.getExtentZ(), plane.getPolygon());
+
+ // Get plane index. Keep a map to assign same indices to same planes.
+ Integer planeIndex = planeIndexMap.get(plane);
+ if (planeIndex == null) {
+ planeIndex = planeIndexMap.size();
+ planeIndexMap.put(plane, planeIndex);
+ }
+
+ // Set plane color.
+ GLES20.glUniform4fv(lineColorUniform, 1, planeColor, 0);
+ GLES20.glUniform4fv(dotColorUniform, 1, planeColor, 0);
+
+ // Each plane will have its own angle offset from others, to make them easier to
+ // distinguish. Compute a 2x2 rotation matrix from the angle.
+ float angleRadians = planeIndex * 0.144f;
+ float uScale = DOTS_PER_METER;
+ float vScale = DOTS_PER_METER * EQUILATERAL_TRIANGLE_SCALE;
+ planeAngleUvMatrix[0] = +(float) Math.cos(angleRadians) * uScale;
+ planeAngleUvMatrix[1] = -(float) Math.sin(angleRadians) * vScale;
+ planeAngleUvMatrix[2] = +(float) Math.sin(angleRadians) * uScale;
+ planeAngleUvMatrix[3] = +(float) Math.cos(angleRadians) * vScale;
+ GLES20.glUniformMatrix2fv(planeUvMatrixUniform, 1, false, planeAngleUvMatrix, 0);
+
+ draw(cameraView, cameraPerspective, normal);
+ }
+
+ // Clean up the state we set
+ GLES20.glDisableVertexAttribArray(planeXZPositionAlphaAttribute);
+ GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
+ GLES20.glDisable(GLES20.GL_BLEND);
+ GLES20.glDepthMask(true);
+ GLES20.glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
+
+ ShaderUtil.checkGLError(TAG, "Cleaning up after drawing planes");
+ }
+
+ // Calculate the normal distance to plane from cameraPose, the given planePose should have y axis
+ // parallel to plane's normal, for example plane's center pose or hit test pose.
+ public static float calculateDistanceToPlane(Pose planePose, Pose cameraPose) {
+ float[] normal = new float[3];
+ float cameraX = cameraPose.tx();
+ float cameraY = cameraPose.ty();
+ float cameraZ = cameraPose.tz();
+ // Get transformed Y axis of plane's coordinate system.
+ planePose.getTransformedAxis(1, 1.0f, normal, 0);
+ // Compute dot product of plane's normal with vector from camera to plane center.
+ return (cameraX - planePose.tx()) * normal[0]
+ + (cameraY - planePose.ty()) * normal[1]
+ + (cameraZ - planePose.tz()) * normal[2];
+ }
+}
diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/rendering/PointCloudRenderer.java b/app/src/main/java/com/google/ar/core/examples/java/common/rendering/PointCloudRenderer.java
new file mode 100644
index 0000000..fb02149
--- /dev/null
+++ b/app/src/main/java/com/google/ar/core/examples/java/common/rendering/PointCloudRenderer.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.ar.core.examples.java.common.rendering;
+
+import android.content.Context;
+import android.opengl.GLES20;
+import android.opengl.GLSurfaceView;
+import android.opengl.Matrix;
+import com.google.ar.core.PointCloud;
+import java.io.IOException;
+
+/** Renders a point cloud. */
+public class PointCloudRenderer {
+ private static final String TAG = PointCloud.class.getSimpleName();
+
+ // Shader names.
+ private static final String VERTEX_SHADER_NAME = "shaders/point_cloud.vert";
+ private static final String FRAGMENT_SHADER_NAME = "shaders/point_cloud.frag";
+
+ private static final int BYTES_PER_FLOAT = Float.SIZE / 8;
+ private static final int FLOATS_PER_POINT = 4; // X,Y,Z,confidence.
+ private static final int BYTES_PER_POINT = BYTES_PER_FLOAT * FLOATS_PER_POINT;
+ private static final int INITIAL_BUFFER_POINTS = 1000;
+
+ private int vbo;
+ private int vboSize;
+
+ private int programName;
+ private int positionAttribute;
+ private int modelViewProjectionUniform;
+ private int colorUniform;
+ private int pointSizeUniform;
+
+ private int numPoints = 0;
+
+ // Keep track of the last point cloud rendered to avoid updating the VBO if point cloud
+ // was not changed. Do this using the timestamp since we can't compare PointCloud objects.
+ private long lastTimestamp = 0;
+
+ public PointCloudRenderer() {}
+
+ /**
+ * Allocates and initializes OpenGL resources needed by the plane renderer. Must be called on the
+ * OpenGL thread, typically in {@link GLSurfaceView.Renderer#onSurfaceCreated(GL10, EGLConfig)}.
+ *
+ * @param context Needed to access shader source.
+ */
+ public void createOnGlThread(Context context) throws IOException {
+ ShaderUtil.checkGLError(TAG, "before create");
+
+ int[] buffers = new int[1];
+ GLES20.glGenBuffers(1, buffers, 0);
+ vbo = buffers[0];
+ GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vbo);
+
+ vboSize = INITIAL_BUFFER_POINTS * BYTES_PER_POINT;
+ GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, vboSize, null, GLES20.GL_DYNAMIC_DRAW);
+ GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
+
+ ShaderUtil.checkGLError(TAG, "buffer alloc");
+
+ int vertexShader =
+ ShaderUtil.loadGLShader(TAG, context, GLES20.GL_VERTEX_SHADER, VERTEX_SHADER_NAME);
+ int passthroughShader =
+ ShaderUtil.loadGLShader(TAG, context, GLES20.GL_FRAGMENT_SHADER, FRAGMENT_SHADER_NAME);
+
+ programName = GLES20.glCreateProgram();
+ GLES20.glAttachShader(programName, vertexShader);
+ GLES20.glAttachShader(programName, passthroughShader);
+ GLES20.glLinkProgram(programName);
+ GLES20.glUseProgram(programName);
+
+ ShaderUtil.checkGLError(TAG, "program");
+
+ positionAttribute = GLES20.glGetAttribLocation(programName, "a_Position");
+ colorUniform = GLES20.glGetUniformLocation(programName, "u_Color");
+ modelViewProjectionUniform = GLES20.glGetUniformLocation(programName, "u_ModelViewProjection");
+ pointSizeUniform = GLES20.glGetUniformLocation(programName, "u_PointSize");
+
+ ShaderUtil.checkGLError(TAG, "program params");
+ }
+
+ /**
+ * Updates the OpenGL buffer contents to the provided point. Repeated calls with the same point
+ * cloud will be ignored.
+ */
+ public void update(PointCloud cloud) {
+ if (cloud.getTimestamp() == lastTimestamp) {
+ // Redundant call.
+ return;
+ }
+ ShaderUtil.checkGLError(TAG, "before update");
+
+ GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vbo);
+ lastTimestamp = cloud.getTimestamp();
+
+ // If the VBO is not large enough to fit the new point cloud, resize it.
+ numPoints = cloud.getPoints().remaining() / FLOATS_PER_POINT;
+ if (numPoints * BYTES_PER_POINT > vboSize) {
+ while (numPoints * BYTES_PER_POINT > vboSize) {
+ vboSize *= 2;
+ }
+ GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, vboSize, null, GLES20.GL_DYNAMIC_DRAW);
+ }
+ GLES20.glBufferSubData(
+ GLES20.GL_ARRAY_BUFFER, 0, numPoints * BYTES_PER_POINT, cloud.getPoints());
+ GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
+
+ ShaderUtil.checkGLError(TAG, "after update");
+ }
+
+ /**
+ * Renders the point cloud. ARCore point cloud is given in world space.
+ *
+ * @param cameraView the camera view matrix for this frame, typically from {@link
+ * com.google.ar.core.Camera#getViewMatrix(float[], int)}.
+ * @param cameraPerspective the camera projection matrix for this frame, typically from {@link
+ * com.google.ar.core.Camera#getProjectionMatrix(float[], int, float, float)}.
+ */
+ public void draw(float[] cameraView, float[] cameraPerspective) {
+ float[] modelViewProjection = new float[16];
+ Matrix.multiplyMM(modelViewProjection, 0, cameraPerspective, 0, cameraView, 0);
+
+ ShaderUtil.checkGLError(TAG, "Before draw");
+
+ GLES20.glUseProgram(programName);
+ GLES20.glEnableVertexAttribArray(positionAttribute);
+ GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vbo);
+ GLES20.glVertexAttribPointer(positionAttribute, 4, GLES20.GL_FLOAT, false, BYTES_PER_POINT, 0);
+ GLES20.glUniform4f(colorUniform, 31.0f / 255.0f, 188.0f / 255.0f, 210.0f / 255.0f, 1.0f);
+ GLES20.glUniformMatrix4fv(modelViewProjectionUniform, 1, false, modelViewProjection, 0);
+ GLES20.glUniform1f(pointSizeUniform, 5.0f);
+
+ GLES20.glDrawArrays(GLES20.GL_POINTS, 0, numPoints);
+ GLES20.glDisableVertexAttribArray(positionAttribute);
+ GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
+
+ ShaderUtil.checkGLError(TAG, "Draw");
+ }
+}
diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/rendering/ShaderUtil.java b/app/src/main/java/com/google/ar/core/examples/java/common/rendering/ShaderUtil.java
new file mode 100644
index 0000000..9ea2159
--- /dev/null
+++ b/app/src/main/java/com/google/ar/core/examples/java/common/rendering/ShaderUtil.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.ar.core.examples.java.common.rendering;
+
+import android.content.Context;
+import android.opengl.GLES20;
+import android.util.Log;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+
+/** Shader helper functions. */
+public class ShaderUtil {
+ /**
+ * Converts a raw text file, saved as a resource, into an OpenGL ES shader.
+ *
+ * @param type The type of shader we will be creating.
+ * @param filename The filename of the asset file about to be turned into a shader.
+ * @return The shader object handler.
+ */
+ public static int loadGLShader(String tag, Context context, int type, String filename)
+ throws IOException {
+ String code = readShaderFileFromAssets(context, filename);
+ int shader = GLES20.glCreateShader(type);
+ GLES20.glShaderSource(shader, code);
+ GLES20.glCompileShader(shader);
+
+ // Get the compilation status.
+ final int[] compileStatus = new int[1];
+ GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compileStatus, 0);
+
+ // If the compilation failed, delete the shader.
+ if (compileStatus[0] == 0) {
+ Log.e(tag, "Error compiling shader: " + GLES20.glGetShaderInfoLog(shader));
+ GLES20.glDeleteShader(shader);
+ shader = 0;
+ }
+
+ if (shader == 0) {
+ throw new RuntimeException("Error creating shader.");
+ }
+
+ return shader;
+ }
+
+ /**
+ * Checks if we've had an error inside of OpenGL ES, and if so what that error is.
+ *
+ * @param label Label to report in case of error.
+ * @throws RuntimeException If an OpenGL error is detected.
+ */
+ public static void checkGLError(String tag, String label) {
+ int lastError = GLES20.GL_NO_ERROR;
+ // Drain the queue of all errors.
+ int error;
+ while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
+ Log.e(tag, label + ": glError " + error);
+ lastError = error;
+ }
+ if (lastError != GLES20.GL_NO_ERROR) {
+ throw new RuntimeException(label + ": glError " + lastError);
+ }
+ }
+
+ /**
+ * Converts a raw shader file into a string.
+ *
+ * @param filename The filename of the shader file about to be turned into a shader.
+ * @return The context of the text file, or null in case of error.
+ */
+ private static String readShaderFileFromAssets(Context context, String filename)
+ throws IOException {
+ try (InputStream inputStream = context.getAssets().open(filename);
+ BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
+ StringBuilder sb = new StringBuilder();
+ String line;
+ while ((line = reader.readLine()) != null) {
+ String[] tokens = line.split(" ", -1);
+ if (tokens[0].equals("#include")) {
+ String includeFilename = tokens[1];
+ includeFilename = includeFilename.replace("\"", "");
+ if (includeFilename.equals(filename)) {
+ throw new IOException("Do not include the calling file.");
+ }
+ sb.append(readShaderFileFromAssets(context, includeFilename));
+ } else {
+ sb.append(line).append("\n");
+ }
+ }
+ return sb.toString();
+ }
+ }
+}
diff --git a/app/src/main/java/com/google/ar/core/examples/java/helloar/HelloArActivity.java b/app/src/main/java/com/google/ar/core/examples/java/helloar/HelloArActivity.java
new file mode 100644
index 0000000..57fe08d
--- /dev/null
+++ b/app/src/main/java/com/google/ar/core/examples/java/helloar/HelloArActivity.java
@@ -0,0 +1,399 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.ar.core.examples.java.helloar;
+
+import android.opengl.GLES20;
+import android.opengl.GLSurfaceView;
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.widget.Toast;
+import com.google.ar.core.Anchor;
+import com.google.ar.core.ArCoreApk;
+import com.google.ar.core.Camera;
+import com.google.ar.core.Frame;
+import com.google.ar.core.HitResult;
+import com.google.ar.core.Plane;
+import com.google.ar.core.Point;
+import com.google.ar.core.Point.OrientationMode;
+import com.google.ar.core.PointCloud;
+import com.google.ar.core.Session;
+import com.google.ar.core.Trackable;
+import com.google.ar.core.TrackingState;
+import com.google.ar.core.examples.java.common.framework.RWT.RWTSurfaceView;
+import com.google.ar.core.examples.java.common.helpers.CameraPermissionHelper;
+import com.google.ar.core.examples.java.common.helpers.DisplayRotationHelper;
+import com.google.ar.core.examples.java.common.helpers.FullScreenHelper;
+import com.google.ar.core.examples.java.common.helpers.SnackbarHelper;
+import com.google.ar.core.examples.java.common.helpers.TapHelper;
+import com.google.ar.core.examples.java.common.helpers.TrackingStateHelper;
+import com.google.ar.core.examples.java.common.rendering.BackgroundRenderer;
+import com.google.ar.core.examples.java.common.rendering.ObjectRenderer;
+import com.google.ar.core.examples.java.common.rendering.ObjectRenderer.BlendMode;
+import com.google.ar.core.examples.java.common.rendering.PlaneRenderer;
+import com.google.ar.core.examples.java.common.rendering.PointCloudRenderer;
+import com.google.ar.core.exceptions.CameraNotAvailableException;
+import com.google.ar.core.exceptions.UnavailableApkTooOldException;
+import com.google.ar.core.exceptions.UnavailableArcoreNotInstalledException;
+import com.google.ar.core.exceptions.UnavailableDeviceNotCompatibleException;
+import com.google.ar.core.exceptions.UnavailableSdkTooOldException;
+import com.google.ar.core.exceptions.UnavailableUserDeclinedInstallationException;
+import java.io.IOException;
+import java.util.ArrayList;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL10;
+
+/**
+ * This is a simple example that shows how to create an augmented reality (AR) application using the
+ * ARCore API. The application will display any detected planes and will allow the user to tap on a
+ * plane to place a 3d model of the Android robot.
+ */
+public class HelloArActivity extends AppCompatActivity implements GLSurfaceView.Renderer {
+ private static final String TAG = HelloArActivity.class.getSimpleName();
+
+ // Rendering. The Renderers are created here, and initialized when the GL surface is created.
+ private GLSurfaceView surfaceView;
+
+ private boolean installRequested;
+
+ private Session session;
+ private final SnackbarHelper messageSnackbarHelper = new SnackbarHelper();
+ private DisplayRotationHelper displayRotationHelper;
+ private final TrackingStateHelper trackingStateHelper = new TrackingStateHelper(this);
+ private TapHelper tapHelper;
+
+ private final BackgroundRenderer backgroundRenderer = new BackgroundRenderer();
+ private final ObjectRenderer virtualObject = new ObjectRenderer();
+ private final ObjectRenderer virtualObjectShadow = new ObjectRenderer();
+ private final PlaneRenderer planeRenderer = new PlaneRenderer();
+ private final PointCloudRenderer pointCloudRenderer = new PointCloudRenderer();
+
+ // Temporary matrix allocated here to reduce number of allocations for each frame.
+ private final float[] anchorMatrix = new float[16];
+ private static final float[] DEFAULT_COLOR = new float[] {0f, 0f, 0f, 0f};
+
+ private static final String SEARCHING_PLANE_MESSAGE = "Searching for surfaces...";
+
+ // Anchors created from taps used for object placing with a given color.
+ private static class ColoredAnchor {
+ public final Anchor anchor;
+ public final float[] color;
+
+ public ColoredAnchor(Anchor a, float[] color4f) {
+ this.anchor = a;
+ this.color = color4f;
+ }
+ }
+
+ private final ArrayList anchors = new ArrayList<>();
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+ surfaceView = findViewById(R.id.surfaceview);
+ displayRotationHelper = new DisplayRotationHelper(/*context=*/ this);
+
+ // Set up tap listener.
+ tapHelper = new TapHelper(/*context=*/ this);
+ surfaceView.setOnTouchListener(tapHelper);
+
+ // Set up renderer.
+ surfaceView.setPreserveEGLContextOnPause(true);
+ surfaceView.setEGLContextClientVersion(2);
+ surfaceView.setEGLConfigChooser(8, 8, 8, 8, 16, 0); // Alpha used for plane blending.
+ surfaceView.setRenderer(this);
+ surfaceView.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
+ surfaceView.setWillNotDraw(false);
+
+ installRequested = false;
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ if (session == null) {
+ Exception exception = null;
+ String message = null;
+ try {
+ switch (ArCoreApk.getInstance().requestInstall(this, !installRequested)) {
+ case INSTALL_REQUESTED:
+ installRequested = true;
+ return;
+ case INSTALLED:
+ break;
+ }
+
+ // ARCore requires camera permissions to operate. If we did not yet obtain runtime
+ // permission on Android M and above, now is a good time to ask the user for it.
+ if (!CameraPermissionHelper.hasCameraPermission(this)) {
+ CameraPermissionHelper.requestCameraPermission(this);
+ return;
+ }
+
+ // Create the session.
+ session = new Session(/* context= */ this);
+
+ } catch (UnavailableArcoreNotInstalledException
+ | UnavailableUserDeclinedInstallationException e) {
+ message = "Please install ARCore";
+ exception = e;
+ } catch (UnavailableApkTooOldException e) {
+ message = "Please update ARCore";
+ exception = e;
+ } catch (UnavailableSdkTooOldException e) {
+ message = "Please update this app";
+ exception = e;
+ } catch (UnavailableDeviceNotCompatibleException e) {
+ message = "This device does not support AR";
+ exception = e;
+ } catch (Exception e) {
+ message = "Failed to create AR session";
+ exception = e;
+ }
+
+ if (message != null) {
+ messageSnackbarHelper.showError(this, message);
+ Log.e(TAG, "Exception creating session", exception);
+ return;
+ }
+ }
+
+ // Note that order matters - see the note in onPause(), the reverse applies here.
+ try {
+ session.resume();
+ } catch (CameraNotAvailableException e) {
+ messageSnackbarHelper.showError(this, "Camera not available. Try restarting the app.");
+ session = null;
+ return;
+ }
+
+ surfaceView.onResume();
+ displayRotationHelper.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ if (session != null) {
+ // Note that the order matters - GLSurfaceView is paused first so that it does not try
+ // to query the session. If Session is paused before GLSurfaceView, GLSurfaceView may
+ // still call session.update() and get a SessionPausedException.
+ displayRotationHelper.onPause();
+ surfaceView.onPause();
+ session.pause();
+ }
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] results) {
+ if (!CameraPermissionHelper.hasCameraPermission(this)) {
+ Toast.makeText(this, "Camera permission is needed to run this application", Toast.LENGTH_LONG)
+ .show();
+ if (!CameraPermissionHelper.shouldShowRequestPermissionRationale(this)) {
+ // Permission denied with checking "Do not ask again".
+ CameraPermissionHelper.launchPermissionSettings(this);
+ }
+ finish();
+ }
+ }
+
+ @Override
+ public void onWindowFocusChanged(boolean hasFocus) {
+ super.onWindowFocusChanged(hasFocus);
+ FullScreenHelper.setFullScreenOnWindowFocusChanged(this, hasFocus);
+ }
+
+ @Override
+ public void onSurfaceCreated(GL10 gl, EGLConfig config) {
+ GLES20.glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
+
+ // Prepare the rendering objects. This involves reading shaders, so may throw an IOException.
+ try {
+ // Create the texture and pass it to ARCore session to be filled during update().
+ backgroundRenderer.createOnGlThread(/*context=*/ this);
+ planeRenderer.createOnGlThread(/*context=*/ this, "models/trigrid.png");
+ pointCloudRenderer.createOnGlThread(/*context=*/ this);
+
+ virtualObject.createOnGlThread(/*context=*/ this, "models/andy.obj", "models/andy.png");
+ virtualObject.setMaterialProperties(0.0f, 2.0f, 0.5f, 6.0f);
+
+ virtualObjectShadow.createOnGlThread(
+ /*context=*/ this, "models/andy_shadow.obj", "models/andy_shadow.png");
+ virtualObjectShadow.setBlendMode(BlendMode.Shadow);
+ virtualObjectShadow.setMaterialProperties(1.0f, 0.0f, 0.0f, 1.0f);
+
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to read an asset file", e);
+ }
+ }
+
+ @Override
+ public void onSurfaceChanged(GL10 gl, int width, int height) {
+ displayRotationHelper.onSurfaceChanged(width, height);
+ GLES20.glViewport(0, 0, width, height);
+ }
+
+ @Override
+ public void onDrawFrame(GL10 gl) {
+ // Clear screen to notify driver it should not load any pixels from previous frame.
+ GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
+
+ if (session == null) {
+ return;
+ }
+ // Notify ARCore session that the view size changed so that the perspective matrix and
+ // the video background can be properly adjusted.
+ displayRotationHelper.updateSessionIfNeeded(session);
+
+ try {
+ session.setCameraTextureName(backgroundRenderer.getTextureId());
+
+ // Obtain the current frame from ARSession. When the configuration is set to
+ // UpdateMode.BLOCKING (it is by default), this will throttle the rendering to the
+ // camera framerate.
+ Frame frame = session.update();
+ Camera camera = frame.getCamera();
+
+ // Handle one tap per frame.
+ handleTap(frame, camera);
+
+ // If frame is ready, render camera preview image to the GL surface.
+ backgroundRenderer.draw(frame);
+
+ // Keep the screen unlocked while tracking, but allow it to lock when tracking stops.
+ trackingStateHelper.updateKeepScreenOnFlag(camera.getTrackingState());
+
+ // If not tracking, don't draw 3D objects, show tracking failure reason instead.
+ if (camera.getTrackingState() == TrackingState.PAUSED) {
+ messageSnackbarHelper.showMessage(
+ this, TrackingStateHelper.getTrackingFailureReasonString(camera));
+ return;
+ }
+
+ // Get projection matrix.
+ float[] projmtx = new float[16];
+ camera.getProjectionMatrix(projmtx, 0, 0.1f, 100.0f);
+
+ // Get camera matrix and draw.
+ float[] viewmtx = new float[16];
+ camera.getViewMatrix(viewmtx, 0);
+
+ // Compute lighting from average intensity of the image.
+ // The first three components are color scaling factors.
+ // The last one is the average pixel intensity in gamma space.
+ final float[] colorCorrectionRgba = new float[4];
+ frame.getLightEstimate().getColorCorrection(colorCorrectionRgba, 0);
+
+ // Visualize tracked points.
+ // Use try-with-resources to automatically release the point cloud.
+ try (PointCloud pointCloud = frame.acquirePointCloud()) {
+ pointCloudRenderer.update(pointCloud);
+ pointCloudRenderer.draw(viewmtx, projmtx);
+ }
+
+ // No tracking error at this point. If we detected any plane, then hide the
+ // message UI, otherwise show searchingPlane message.
+ if (hasTrackingPlane()) {
+ messageSnackbarHelper.hide(this);
+ } else {
+ messageSnackbarHelper.showMessage(this, SEARCHING_PLANE_MESSAGE);
+ }
+
+ // Visualize planes.
+ planeRenderer.drawPlanes(
+ session.getAllTrackables(Plane.class), camera.getDisplayOrientedPose(), projmtx);
+
+ // Visualize anchors created by touch.
+ float scaleFactor = 1.0f;
+ for (ColoredAnchor coloredAnchor : anchors) {
+ if (coloredAnchor.anchor.getTrackingState() != TrackingState.TRACKING) {
+ continue;
+ }
+ // Get the current pose of an Anchor in world space. The Anchor pose is updated
+ // during calls to session.update() as ARCore refines its estimate of the world.
+ coloredAnchor.anchor.getPose().toMatrix(anchorMatrix, 0);
+
+ // Update and draw the model and its shadow.
+ virtualObject.updateModelMatrix(anchorMatrix, scaleFactor);
+ virtualObjectShadow.updateModelMatrix(anchorMatrix, scaleFactor);
+ virtualObject.draw(viewmtx, projmtx, colorCorrectionRgba, coloredAnchor.color);
+ virtualObjectShadow.draw(viewmtx, projmtx, colorCorrectionRgba, coloredAnchor.color);
+ }
+
+ } catch (Throwable t) {
+ // Avoid crashing the application due to unhandled exceptions.
+ Log.e(TAG, "Exception on the OpenGL thread", t);
+ }
+ }
+
+ // Handle only one tap per frame, as taps are usually low frequency compared to frame rate.
+ private void handleTap(Frame frame, Camera camera) {
+ MotionEvent tap = tapHelper.poll();
+ if (tap != null && camera.getTrackingState() == TrackingState.TRACKING) {
+ for (HitResult hit : frame.hitTest(tap)) {
+ // Check if any plane was hit, and if it was hit inside the plane polygon
+ Trackable trackable = hit.getTrackable();
+ // Creates an anchor if a plane or an oriented point was hit.
+ if ((trackable instanceof Plane
+ && ((Plane) trackable).isPoseInPolygon(hit.getHitPose())
+ && (PlaneRenderer.calculateDistanceToPlane(hit.getHitPose(), camera.getPose()) > 0))
+ || (trackable instanceof Point
+ && ((Point) trackable).getOrientationMode()
+ == OrientationMode.ESTIMATED_SURFACE_NORMAL)) {
+ // Hits are sorted by depth. Consider only closest hit on a plane or oriented point.
+ // Cap the number of objects created. This avoids overloading both the
+ // rendering system and ARCore.
+ if (anchors.size() >= 20) {
+ anchors.get(0).anchor.detach();
+ anchors.remove(0);
+ }
+
+ // Assign a color to the object for rendering based on the trackable type
+ // this anchor attached to. For AR_TRACKABLE_POINT, it's blue color, and
+ // for AR_TRACKABLE_PLANE, it's green color.
+ float[] objColor;
+ if (trackable instanceof Point) {
+ objColor = new float[] {66.0f, 133.0f, 244.0f, 255.0f};
+ } else if (trackable instanceof Plane) {
+ objColor = new float[] {139.0f, 195.0f, 74.0f, 255.0f};
+ } else {
+ objColor = DEFAULT_COLOR;
+ }
+
+ // Adding an Anchor tells ARCore that it should track this position in
+ // space. This anchor is created on the Plane to place the 3D model
+ // in the correct position relative both to the world and to the plane.
+ anchors.add(new ColoredAnchor(hit.createAnchor(), objColor));
+ break;
+ }
+ }
+ }
+ }
+
+ /** Checks if we detected at least one plane. */
+ private boolean hasTrackingPlane() {
+ for (Plane plane : session.getAllTrackables(Plane.class)) {
+ if (plane.getTrackingState() == TrackingState.TRACKING) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/app/src/main/res/drawable-xxhdpi/ic_launcher.png b/app/src/main/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..3f691da
--- /dev/null
+++ b/app/src/main/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..2723fc1
--- /dev/null
+++ b/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..4555298
--- /dev/null
+++ b/app/src/main/res/values/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ HelloAR Java
+
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..68b12b6
--- /dev/null
+++ b/app/src/main/res/values/styles.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000..382bee5
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,25 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ google()
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:3.5.0'
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ jcenter()
+ mavenLocal()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 0000000..aac7c9b
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,17 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx1536m
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..87b738c
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..bcedaa3
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Mon Nov 20 10:27:45 PST 2017
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
diff --git a/gradlew b/gradlew
new file mode 100644
index 0000000..af6708f
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,172 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000..6d57edc
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,84 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 0000000..e7b4def
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1 @@
+include ':app'