From ca453d701be5cbe18a0c5440814a454d9b708e5f Mon Sep 17 00:00:00 2001 From: Andres Oviedo Date: Thu, 26 Dec 2024 11:38:44 +0100 Subject: [PATCH 1/2] fixes #245 - fixed submodule --- engine | 1 + engine/build.gradle | 46 - engine/src/main/AndroidManifest.xml | 3 - .../controller/TouchController.java | 251 ----- .../android_3d_model_engine/model/Camera.java | 496 --------- .../services/LoaderTask.java | 115 --- .../services/collada/entities/MeshData.java | 933 ----------------- .../the3deer/util/android/ContentUtils.java | 486 --------- .../org/the3deer/util/math/Math3DUtils.java | 969 ------------------ 9 files changed, 1 insertion(+), 3299 deletions(-) create mode 160000 engine delete mode 100644 engine/build.gradle delete mode 100644 engine/src/main/AndroidManifest.xml delete mode 100644 engine/src/main/java/org/the3deer/android_3d_model_engine/controller/TouchController.java delete mode 100644 engine/src/main/java/org/the3deer/android_3d_model_engine/model/Camera.java delete mode 100644 engine/src/main/java/org/the3deer/android_3d_model_engine/services/LoaderTask.java delete mode 100644 engine/src/main/java/org/the3deer/android_3d_model_engine/services/collada/entities/MeshData.java delete mode 100644 engine/src/main/java/org/the3deer/util/android/ContentUtils.java delete mode 100644 engine/src/main/java/org/the3deer/util/math/Math3DUtils.java diff --git a/engine b/engine new file mode 160000 index 00000000..c6eb265e --- /dev/null +++ b/engine @@ -0,0 +1 @@ +Subproject commit c6eb265e5fdcc0f1f7f4d02a7562320d9470c465 diff --git a/engine/build.gradle b/engine/build.gradle deleted file mode 100644 index 410b67e8..00000000 --- a/engine/build.gradle +++ /dev/null @@ -1,46 +0,0 @@ -plugins { - id 'com.android.library' -} - -android { - compileSdk 34 - - defaultConfig { - minSdk 21 - targetSdk 34 - - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - consumerProguardFiles "consumer-rules.pro" - - multiDexEnabled true - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - } - } - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - namespace 'org.the3deer.android_3d_model_engine' - -} - -dependencies { - implementation fileTree(dir: 'libs', include: ['*.jar']) - - implementation 'javax.inject:javax.inject:1' - implementation 'androidx.core:core:1.13.1' - implementation 'androidx.fragment:fragment:1.8.3' - implementation 'androidx.preference:preference:1.2.1' - implementation 'androidx.constraintlayout:constraintlayout:2.1.4' - implementation 'androidx.coordinatorlayout:coordinatorlayout:1.2.0' - implementation 'com.google.android.material:material:1.12.0' - implementation 'com.fasterxml.jackson.core:jackson-databind:2.17.2' // required by gltf parser - testImplementation 'junit:junit:4.13.2' - androidTestImplementation 'androidx.test.ext:junit:1.2.1' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1' -} diff --git a/engine/src/main/AndroidManifest.xml b/engine/src/main/AndroidManifest.xml deleted file mode 100644 index 69fc4129..00000000 --- a/engine/src/main/AndroidManifest.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/engine/src/main/java/org/the3deer/android_3d_model_engine/controller/TouchController.java b/engine/src/main/java/org/the3deer/android_3d_model_engine/controller/TouchController.java deleted file mode 100644 index 9212b5c5..00000000 --- a/engine/src/main/java/org/the3deer/android_3d_model_engine/controller/TouchController.java +++ /dev/null @@ -1,251 +0,0 @@ -package org.the3deer.android_3d_model_engine.controller; - -import android.opengl.Matrix; -import android.os.SystemClock; -import android.util.Log; -import android.view.MotionEvent; - -import org.the3deer.android_3d_model_engine.renderer.RenderEvent; -import org.the3deer.util.event.EventListener; -import org.the3deer.util.event.EventManager; -import org.the3deer.util.math.Math3DUtils; - -import java.util.EventObject; - -import javax.inject.Inject; - -/** - *

Android Generic Touch Screen Controller

- *

It fires events of this type @{@link TouchEvent}

- */ -public class TouchController implements EventListener { - - private static final String TAG = TouchController.class.getSimpleName(); - - private static final int TOUCH_STATUS_ZOOMING_CAMERA = 1; - private static final int TOUCH_STATUS_ROTATING_CAMERA = 4; - private static final int TOUCH_STATUS_MOVING_WORLD = 5; - - // constants - private int width; - private int height; - - // variables - @Inject - private EventManager eventManager; - private float x1 = Float.MIN_VALUE; - private float y1 = Float.MIN_VALUE; - private float x2 = Float.MIN_VALUE; - private float y2 = Float.MIN_VALUE; - private float dx1 = Float.MIN_VALUE; - private float dy1 = Float.MIN_VALUE; - private float dx2 = Float.MIN_VALUE; - private float dy2 = Float.MIN_VALUE; - - private float length = Float.MIN_VALUE; - private float previousLength = Float.MIN_VALUE; - private float currentPress1 = Float.MIN_VALUE; - private float currentPress2 = Float.MIN_VALUE; - - private float rotation = 0; - private int currentSquare = Integer.MIN_VALUE; - - private boolean isOneFixedAndOneMoving = false; - private boolean fingersAreClosing = false; - private boolean isRotating = false; - - private boolean gestureChanged = false; - private boolean move = false; - private boolean click = false; - private boolean clickLarge = false; - private long lastClickTime; - private int moveCounter = -2; - private float previousX1; - private float previousY1; - private float previousX2; - private float previousY2; - private float[] previousVector = new float[4]; - private float[] vector = new float[4]; - private float previousRotationSquare; - -/* public TouchController(Activity parent) { - super(); - try { - if (!AndroidUtils.supportsMultiTouch(parent.getPackageManager())) { - Log.w(TAG,"Multitouch not supported. Some app features may not be available"); - } else { - Log.i(TAG,"Initializing TouchController..."); - } - }catch (Exception e){ - Toast.makeText(parent, "Error loading Touch Controller:\n" +e.getMessage(), Toast.LENGTH_LONG).show(); - } - }*/ - - public TouchController() { - } - - public void setSize(int width, int height) { - this.width = width; - this.height = height; - } - - - private void fireEvent(EventObject eventObject) { - if (eventManager != null) { - eventManager.propagate(eventObject); - } - } - - @Override - public boolean onEvent(EventObject event) { - // Log.d("TouchController","Processing event... "+ event); - if (event.getSource() instanceof MotionEvent) { - return onMotionEvent((MotionEvent) event.getSource()); - } else if (event instanceof RenderEvent) { - RenderEvent renderEvent = (RenderEvent) event; - if (renderEvent.getCode() == RenderEvent.Code.SURFACE_CHANGED) { - this.setSize(renderEvent.getWidth(), renderEvent.getHeight()); - } - } - return false; - } - - public boolean onMotionEvent(MotionEvent motionEvent) { - - final int pointerCount = motionEvent.getPointerCount(); - - - switch (motionEvent.getActionMasked()) { - case MotionEvent.ACTION_DOWN: - case MotionEvent.ACTION_POINTER_DOWN: - case MotionEvent.ACTION_HOVER_ENTER: - move = false; - click = false; - clickLarge = false; - - gestureChanged = true; - moveCounter = 0; - lastClickTime = SystemClock.uptimeMillis(); - break; - case MotionEvent.ACTION_MOVE: - move = true; - click = false; - moveCounter++; - break; - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_CANCEL: - case MotionEvent.ACTION_POINTER_UP: - case MotionEvent.ACTION_HOVER_EXIT: - case MotionEvent.ACTION_OUTSIDE: - // this to handle "1 simple touch" - click = moveCounter < 20; - move = false; - clickLarge = click && lastClickTime > SystemClock.uptimeMillis() - 2000; - - gestureChanged = true; - moveCounter = 0; - lastClickTime = SystemClock.uptimeMillis(); - break; - default: - Log.w(TAG, "Unknown state: " + motionEvent.getAction()); - gestureChanged = true; - } - - // Log.v(TAG, "click: " + click + ", clickLarge: "+clickLarge+", move: " + move+", moveCounter: "+moveCounter); - - if (pointerCount == 1) { - x1 = motionEvent.getX(); - y1 = motionEvent.getY(); - //Log.v(TAG, "1 touch ! x:" + x1 + ",y:" + y1); - if (gestureChanged) { - previousX1 = x1; - previousY1 = y1; - } - dx1 = x1 - previousX1; - dy1 = y1 - previousY1; - } else if (pointerCount == 2) { - x1 = motionEvent.getX(0); - y1 = motionEvent.getY(0); - x2 = motionEvent.getX(1); - y2 = motionEvent.getY(1); - //Log.v(TAG, "2 touch ! x1:" + x1 + ",y1:" + y1 + ",x2:" + x2 + ",y2:" + y2); - - vector[0] = x2 - x1; - vector[1] = y2 - y1; - vector[2] = 0; - vector[3] = 1; - float len = Matrix.length(vector[0], vector[1], vector[2]); - vector[0] /= len; - vector[1] /= len; - - if (gestureChanged) { - previousX1 = x1; - previousY1 = y1; - previousX2 = x2; - previousY2 = y2; - System.arraycopy(vector, 0, previousVector, 0, vector.length); - } - dx1 = x1 - previousX1; - dy1 = y1 - previousY1; - dx2 = x2 - previousX2; - dy2 = y2 - previousY2; - - previousLength = (float) Math - .sqrt(Math.pow(previousX2 - previousX1, 2) + Math.pow(previousY2 - previousY1, 2)); - length = (float) Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2)); - - currentPress1 = motionEvent.getPressure(0); - currentPress2 = motionEvent.getPressure(1); - rotation = 0; - rotation = TouchScreen.getRotation360(motionEvent); - currentSquare = TouchScreen.getSquare(motionEvent); - if (currentSquare == 1 && previousRotationSquare == 4) { - rotation = 0; - } else if (currentSquare == 4 && previousRotationSquare == 1) { - rotation = 360; - } - - // gesture detection - isOneFixedAndOneMoving = ((dx1 + dy1) == 0) != (((dx2 + dy2) == 0)); - fingersAreClosing = (Math.abs(dx1 + dx2) < 10 && Math.abs(dy1 + dy2) < 10); - isRotating = !isOneFixedAndOneMoving && (dx1 != 0 && dy1 != 0 && dx2 != 0 && dy2 != 0); - } - - if (pointerCount == 1 && click) { - fireEvent(new TouchEvent(this, TouchEvent.CLICK, width, height, x1, y1)); - } else if (pointerCount == 1 && move) { - fireEvent(new TouchEvent(this, TouchEvent.MOVE, width, height, previousX1, previousY1, - x1, y1, dx1, dy1, 0, - 0f)); - } else if (pointerCount == 2) { - if (fingersAreClosing) { - fireEvent(new TouchEvent(this, TouchEvent.PINCH, width, height, previousX1, previousY1, - x1, y1, dx1, dy1, (previousLength - length), 0f)); - fingersAreClosing = false; - } else if (isRotating) { - final double angle = Math3DUtils.calculateAngleBetween(previousVector, vector); - if (Math.abs(angle) >= Math.PI / 180) { - fireEvent(new TouchEvent(this, TouchEvent.ROTATE, width, height, previousX1, previousY1 - , x1, y1, dx1, dy1, 0, (float) angle)); - } - } - } - //} - - - previousX1 = x1; - previousY1 = y1; - previousX2 = x2; - previousY2 = y2; - - previousRotationSquare = currentSquare; - - System.arraycopy(vector, 0, previousVector, 0, vector.length); - - if (gestureChanged && moveCounter > 1) { - gestureChanged = false; - } - return true; - } -} - diff --git a/engine/src/main/java/org/the3deer/android_3d_model_engine/model/Camera.java b/engine/src/main/java/org/the3deer/android_3d_model_engine/model/Camera.java deleted file mode 100644 index a7801b87..00000000 --- a/engine/src/main/java/org/the3deer/android_3d_model_engine/model/Camera.java +++ /dev/null @@ -1,496 +0,0 @@ -package org.the3deer.android_3d_model_engine.model; - -// http://stackoverflow.com/questions/14607640/rotating-a-vector-in-3d-space - -import android.opengl.Matrix; -import android.util.Log; - -import androidx.annotation.NonNull; - -import org.the3deer.util.android.AndroidUtils; -import org.the3deer.util.event.EventListener; -import org.the3deer.util.math.Math3DUtils; -import org.the3deer.util.math.Quaternion; - -import java.util.ArrayList; -import java.util.EventObject; -import java.util.List; - -import javax.inject.Inject; - -public class Camera { - - /** - * Controls the camera - */ - public interface Controller { - - default void move(float dX, float dY) { - } - - default void zoom(float direction) { - } - - default void rotate(float angle) { - } - } - - /** - * Triggers on any camera update - */ - public static class CameraUpdatedEvent extends EventObject { - - /** - * Constructs a prototypical Event. - * - * @param source the object on which the Event initially occurred - * @throws IllegalArgumentException if source is null - */ - public CameraUpdatedEvent(Object source) { - super(source); - } - } - - private final BoundingBox centerBox = new BoundingBox("scene", -Constants.ROOM_CENTER_SIZE, Constants.ROOM_CENTER_SIZE, - -Constants.ROOM_CENTER_SIZE, Constants.ROOM_CENTER_SIZE, -Constants.ROOM_CENTER_SIZE, Constants.ROOM_CENTER_SIZE); - private final BoundingBox roomBox = new BoundingBox("scene", -Constants.ROOM_SIZE, Constants.ROOM_SIZE, - -Constants.ROOM_SIZE, Constants.ROOM_SIZE, -Constants.ROOM_SIZE, Constants.ROOM_SIZE); - - // new vector model - protected float[] pos = new float[]{0, 0, 1, 1}; - protected float[] view = new float[]{0, 0, 0, 1}; - protected float[] up = new float[]{0, 1, 0, 1}; - - // transformation matrix - public float[] viewMatrix = new float[16]; - public float[] projectionMatrix = new float[16]; - //public final float[] projectionViewMatrix = new float[16]; - - // camera mode - private Projection projection = Projection.PERSPECTIVE; - - // @Inject - private List listeners = new ArrayList<>(); - /** - * The new orientation of the device. - *

- * Please check @{@link android.view.OrientationEventListener} - */ - private int deviceOrientation; - - // camera orientation - private float[] orientationMatrix = new float[16]; - private Quaternion orientation = new Quaternion(); - - @Inject - private Screen screen; - - //@Inject - private Controller controller; - - private boolean changed = false; - - private Dimensions dimensions2D = new Dimensions(); - private Dimensions dimensions3D = new Dimensions(); - - public Camera() { - this(Constants.UNIT); - } - - public Camera(float distance) { - // Initialize variables... - this(0, 0, distance, 0, 0, 0, 0, 1, 0); - } - - public Camera(Camera cam2) { - this.screen = cam2.screen; - this.pos = cam2.pos; - this.view = cam2.view; - this.up = cam2.up; - this.viewMatrix = cam2.viewMatrix; - this.projectionMatrix = cam2.projectionMatrix; - } - - public Camera(float xPos, float yPos, float zPos, float xView, float yView, float zView, float xUp, float yUp, - float zUp) { - - // Here we set the camera to the values sent in to us. This is mostly - // used to set up a - // default position. - this.pos[0] = xPos; - this.pos[1] = yPos; - this.pos[2] = zPos; - this.view[0] = xView; - this.view[1] = yView; - this.view[2] = zView; - this.up[0] = xUp; - this.up[1] = yUp; - this.up[2] = zUp; - } - - public Projection getProjection() { - return projection; - } - - public void setProjection(Projection projection) { - this.projection = projection; - } - - public Controller getController() { - return controller; - } - - public void setController(Controller controller) { - this.controller = controller; - } - - public void setUp() { - refresh(); - } - - public void addListener(EventListener eventListener) { - this.listeners.add(eventListener); - } - - public void move(float dX, float dY) { - if (controller != null) - controller.move(dX, dY); - } - - public void zoom(float direction) { - if (controller != null) - controller.zoom(direction); - } - - public void rotate(float angle) { - if (controller != null) - controller.rotate(angle); - } - - protected void refresh() { - - // check - if (screen == null) return; - - // setup projection matrix - switch (projection) { - case ORTHOGRAPHIC: - case ISOMETRIC: - Matrix.orthoM(projectionMatrix, 0, - -Constants.UNIT * screen.getRatio(), - Constants.UNIT * screen.getRatio(), - -Constants.UNIT, - Constants.UNIT, - Constants.near, Constants.far); - break; - case PERSPECTIVE: - case FREE: - Matrix.frustumM(projectionMatrix, 0, - -screen.getRatio(), screen.getRatio(), - -1f, 1f, Constants.near, Constants.far); - break; - } - - // setup view matrix - Matrix.setLookAtM(viewMatrix, 0, - getxPos(), getyPos(), getzPos(), - getxView(), getyView(), getzView(), - getxUp(), getyUp(), getzUp()); - - - // update orientation - Matrix.setLookAtM(orientationMatrix, 0, - 0, 0, 0, - -getxPos() + getxView(), -getyPos() + getyView(), -getzPos() + getzView(), - getxUp(), getyUp(), getzUp()); - this.orientation = new Quaternion(orientationMatrix); - - - // dimensions - this.dimensions2D = new Dimensions(-Constants.UNIT * screen.getRatio(), Constants.UNIT * screen.getRatio(), - Constants.UNIT, -Constants.UNIT, 0, 1); - this.dimensions3D = new Dimensions(0, screen.getWidth(), screen.getHeight(), 0, Constants.near, Constants.far); - - //Log.v("Camera","Camera refreshed: "+this.projection); - } - - public Quaternion getOrientation() { - return orientation; - } - - - - public void enable() { - } - - /** - * Test whether specified position is either outside room "walls" or in the very center of the room. - * - * @param x x position - * @param y y position - * @param z z position - * @return true if specified position is outside room "walls" or in the very center of the room - */ - public boolean isOutOfBounds(float x, float y, float z) { - if (roomBox.outOfBound(x, y, z)) { - Log.v("Camera", "Out of room walls. " + x + "," + y + "," + z); - return true; - } - if (!centerBox.outOfBound(x, y, z)) { - Log.v("Camera", "Inside absolute center"); - return true; - } - return false; - } - - /** - * Translation is the movement that makes the Earth around the Sun. - * So in this context, translating the camera means moving the camera around the Zero (0,0,0) - *

- * This implementation makes uses of 3D Vectors Algebra. - *

- * The idea behind this implementation is to translate the 2D user vectors (the line in the - * screen) with the 3D equivalents. - *

- * In order to to that, we need to calculate the Right and Arriba vectors so we have a match - * for user 2D vector. - * - * @param dX the X component of the user 2D vector, that is, a value between [-1,1] - * @param dY the Y component of the user 2D vector, that is, a value between [-1,1] - */ - public synchronized void translateCamera(float dX, float dY) { - } - - public boolean hasChanged() { - return changed; - } - - public void setChanged(boolean changed) { - this.changed = changed; - refresh(); - AndroidUtils.fireEvent(listeners, new CameraUpdatedEvent(this)); - } - - - public synchronized void Rotate(float angle) { - } - - public Camera[] toStereo(float eyeSeparation) { - - // look vector - float xLook = getxView() - pos[0]; - float yLook = getyView() - pos[1]; - float zLook = view[2] - pos[2]; - - // right vector - float[] crossRight = Math3DUtils.crossProduct(xLook, yLook, zLook, getxUp(), getyUp(), getzUp()); - Math3DUtils.normalizeVector(crossRight); - - // new left pos - float xPosLeft = pos[0] - crossRight[0] * eyeSeparation / 2; - float yPosLeft = pos[1] - crossRight[1] * eyeSeparation / 2; - float zPosLeft = pos[2] - crossRight[2] * eyeSeparation / 2; - float xViewLeft = getxView() - crossRight[0] * eyeSeparation / 2; - float yViewLeft = getyView() - crossRight[1] * eyeSeparation / 2; - float zViewLeft = view[2] - crossRight[2] * eyeSeparation / 2; - - // new right pos - float xPosRight = pos[0] + crossRight[0] * eyeSeparation / 2; - float yPosRight = pos[1] + crossRight[1] * eyeSeparation / 2; - float zPosRight = pos[2] + crossRight[2] * eyeSeparation / 2; - float xViewRight = getxView() + crossRight[0] * eyeSeparation / 2; - float yViewRight = getyView() + crossRight[1] * eyeSeparation / 2; - float zViewRight = view[2] + crossRight[2] * eyeSeparation / 2; - - //xViewLeft = getxView(); - //yViewLeft = getyView(); - //zViewLeft = view[2]; - - //xViewRight = getxView(); - //yViewRight = getyView(); - //zViewRight = view[2]; - - - Camera left = new Camera(xPosLeft, yPosLeft, zPosLeft, xViewLeft, yViewLeft, zViewLeft, getxUp(), getyUp(), getzUp()); - Camera right = new Camera(xPosRight, yPosRight, zPosRight, xViewRight, yViewRight, zViewRight, getxUp(), getyUp(), getzUp()); - - return new Camera[]{left, right}; - } - - public float getxView() { - return view[0]; - } - - public float getyView() { - return view[1]; - } - - - public float getzView() { - return view[2]; - } - - public float getxUp() { - return up[0]; - } - - public float getyUp() { - return up[1]; - } - - public float getzUp() { - return up[2]; - } - - public float getxPos() { - return pos[0]; - } - - public float getyPos() { - return pos[1]; - } - - public float getzPos() { - return pos[2]; - } - - public void set(float xPos, float yPos, float zPos, float xView, float yView, float zView, float xUp, float yUp, - float zUp) { - - // check that we have valid coordinates - - - // Here we set the camera to the values sent in to us. This is mostly - // used to set up a - // default position. - this.pos[0] = xPos; - this.pos[1] = yPos; - this.pos[2] = zPos; - this.view[0] = xView; - this.view[1] = yView; - this.view[2] = zView; - this.up[0] = xUp; - this.up[1] = yUp; - this.up[2] = zUp; - - setChanged(true); - } - - public void set(float[] pos, float[] view, float[] up) { - this.set(pos[0], pos[1], pos[2], view[0], view[1], view[2], up[0], up[1], up[2]); - } - - - public float getDistance() { - return Math3DUtils.length(this.pos); - } - - public float[] getPos() { - return this.pos; - } - - public float[] getRight() { - return Math3DUtils.normalize2(Math3DUtils.crossProduct(this.up, this.pos)); - } - - public float[] getUp() { - return this.up; - } - - public float[] getView() { - return this.view; - } - - // cellphone orientation - - /** - * Rotate using the current view vector - * - * @param angle angle in degrees - */ - public void setDeviceOrientation(int angle) { - - if (angle == this.deviceOrientation) return; - else if (Math.abs(this.deviceOrientation - angle) < 5) return; - else { - int previous = this.deviceOrientation; - this.deviceOrientation = angle; - angle = previous - angle; - } - - float dX = (float) Math.sin(Math.toRadians(angle)); - float dY = (float) Math.cos(Math.toRadians(angle)); - float[] right = Math3DUtils.to4d(getRight()); - - // screen orientation vector - /*float[] ovector1 = Math3DUtils.multiply(up, dX); - float[] ovector2 = Math3DUtils.multiply(getRight(), -dY); - float[] ovector = Math3DUtils.add(ovector1,ovector2); - Math3DUtils.normalize(ovector);*/ - - // rotation matrix - float[] matrix = new float[16]; - Matrix.setIdentityM(matrix, 0); - Matrix.setRotateM(matrix, 0, angle, getxPos(), getyPos(), getzPos()); - - float[] newUp = new float[4]; - Matrix.multiplyMV(newUp, 0, matrix, 0, up, 0); - Math3DUtils.normalizeVector(newUp); - - this.up[0] = newUp[0]; - this.up[1] = newUp[1]; - this.up[2] = newUp[2]; - - setChanged(true); - - /*float rota = -(float) Math.tan(angle-180)/5f; //-angle / 90f; - - float dot = Math.abs(Math3DUtils.dotProduct(up, newUp)); - if (Math.abs(dot) < 0.1f){ - Log.v("Camera","angle: "+angle+", rot:"+rota+", dx:"+dX+", dy:"+dY+" HIT! "+Math3DUtils.dotProduct(up,newUp)); - return; - } else { - Log.v("Camera","angle: "+angle+", rot:"+rota+", dx:"+dX+", dy:"+dY+" DOT! "+Math3DUtils.dotProduct(up,newUp)); - //return; - } - - Matrix.setIdentityM(matrix,0); - Matrix.setRotateM(matrix,0, -angle/90f, getxPos(),getyPos(),getzPos()); - Matrix.multiplyMV(newUp,0,matrix,0,up,0); - Math3DUtils.normalize(newUp); - - this.up[0] = newUp[0]; - this.up[1] = newUp[1]; - this.up[2] = newUp[2]; - setChanged(true);*/ - } - - public float[] getViewMatrix() { - /*Matrix.setLookAtM(this.viewMatrix,0,getxPos(),getyPos(),getzPos(), - getxView(),getyView(),getzView(),getxUp(),getyUp(),getzUp());*/ - return viewMatrix; - } - - public float[] getProjectionMatrix() { - return projectionMatrix; - } - -/* public float[] getProjectionViewMatrix() { - return projectionViewMatrix; - }*/ - - // from gui - - public Dimensions getDimensions2D() { - return dimensions2D; - } - - @NonNull - @Override - public String toString() { - return "Camera [projection="+projection+", xPos=" + pos[0] + ", yPos=" + pos[1] + ", zPos=" + pos[2] + - ", xView=" + getxView() + ", yView=" + getyView() + - ", zView=" + view[2] + ", xUp=" + getxUp() + - ", yUp=" + getyUp() + ", zUp=" + getzUp() + "]"; - } -} diff --git a/engine/src/main/java/org/the3deer/android_3d_model_engine/services/LoaderTask.java b/engine/src/main/java/org/the3deer/android_3d_model_engine/services/LoaderTask.java deleted file mode 100644 index d36b56cb..00000000 --- a/engine/src/main/java/org/the3deer/android_3d_model_engine/services/LoaderTask.java +++ /dev/null @@ -1,115 +0,0 @@ -package org.the3deer.android_3d_model_engine.services; - -import android.app.Activity; -import android.app.ProgressDialog; -import android.os.AsyncTask; -import android.util.Log; - -import org.the3deer.android_3d_model_engine.model.Object3DData; -import org.the3deer.util.android.ContentUtils; - -import java.net.URI; -import java.util.List; - -/** - * This component allows loading the model without blocking the UI. - * - * @author andresoviedo - */ -public abstract class LoaderTask extends AsyncTask> implements LoadListener { - - /** - * URL to the 3D model - */ - protected final URI uri; - /** - * Callback to notify of events - */ - private final LoadListener callback; - /** - * The dialog that will show the progress of the loading - */ - private final ProgressDialog dialog; - - /** - * Build a new progress dialog for loading the data model asynchronously - * @param uri the URL pointing to the 3d model - * - */ - public LoaderTask(Activity parent, URI uri, LoadListener callback) { - this.uri = uri; - this.dialog = new ProgressDialog(parent); - this.callback = callback; } - - - @Override - protected void onPreExecute() { - super.onPreExecute(); - this.dialog.setMessage("Loading..."); - this.dialog.setCancelable(false); - //this.dialog.getWindow().setGravity(Gravity.BOTTOM); - this.dialog.show(); - } - - - - @Override - protected List doInBackground(Void... params) { - try { - ContentUtils.setThreadActivity(dialog.getContext()); - callback.onStart(); - List data = build(); - callback.onLoadComplete(); - ContentUtils.setThreadActivity(null); - return data; - } catch (Exception ex) { - Log.e("LoaderTask",ex.getMessage(),ex); - callback.onLoadError(ex); - return null; - } catch (OutOfMemoryError err){ - callback.onLoadError(new RuntimeException("Out Of Memory Error",err)); - return null; - } - } - - protected abstract List build() throws Exception; - - public void onLoad(Object3DData data){ - callback.onLoad(data); - } - - @Override - protected void onProgressUpdate(String... values) { - super.onProgressUpdate(values); - this.dialog.setMessage(values[0]); - } - - @Override - protected void onPostExecute(List data) { - super.onPostExecute(data); - if (dialog.isShowing()) { - dialog.dismiss(); - } - } - - @Override - public void onStart() { - callback.onStart(); - } - - @Override - public void onProgress(String progress) { - super.publishProgress(progress); - callback.onProgress(progress); - } - - @Override - public void onLoadError(Exception ex) { - callback.onLoadError(ex); - } - - @Override - public void onLoadComplete() { - callback.onLoadComplete(); - } -} \ No newline at end of file diff --git a/engine/src/main/java/org/the3deer/android_3d_model_engine/services/collada/entities/MeshData.java b/engine/src/main/java/org/the3deer/android_3d_model_engine/services/collada/entities/MeshData.java deleted file mode 100644 index 44f28f4c..00000000 --- a/engine/src/main/java/org/the3deer/android_3d_model_engine/services/collada/entities/MeshData.java +++ /dev/null @@ -1,933 +0,0 @@ -package org.the3deer.android_3d_model_engine.services.collada.entities; - -import android.util.Log; - -import org.the3deer.android_3d_model_engine.model.Element; -import org.the3deer.util.io.IOUtils; -import org.the3deer.util.math.Math3DUtils; - -import java.nio.FloatBuffer; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * This object contains all the mesh data for an animated model that is to be loaded into the VAO. - * Notice that the buffers returned ignore any index beyond vertices length (colors or textures sometimes are greater than vertex length) - * Smoothing groups are an alternative to providing explicit vertex normals. - * If a polygon’s vertices include normal references, those supersede any smoothing group. - * Generating vertex normals usually involves calculating face normals for each face, - * then calculating a normal for each vertex as the average of the face normals of all of the faces which use that vertex. - * When smoothing groups are used, this process is performed separately for each smoothing group. - * So if a vertex is used by faces in multiple smoothing groups, there will be multiple normals associated with it. Each normal is calculated as the average of the face normals for all of the associated faces in a specific group. - * The end result is that where the faces on either side of an edge belong to different smoothing groups, - * the faces will use different normals for the same vertex, - * meaning that the edge will appear as a sharp corner (i.e. there will be a discontinuity in the lighting) - *

- * Essentially, the vertex normal for a specific vertex ID and smoothing group ID is the normalised average of the face normals - * for all faces in that smoothing group which reference that vertex ID. - * Typically, you average the un-normalised face normals, - * so that larger faces are given more weight in the calculation. - * - * @author andresoviedo - */ -public class MeshData { - - - private static final float[] WRONG_NORMAL = {0, -1, 0}; - - public static class Builder { - - private String id; - private String name; - - private List vertices; - private List normals; - private List colors; - private List textures; - - private List vertexAttributes; - private List elements; - - private String materialFile; - - private Map> smoothingGroups; - - public Builder id(String id) { - this.id = id; - return this; - } - - public Builder name(String name) { - this.name = name; - return this; - } - - public Builder vertices(List vertices) { - this.vertices = vertices; - return this; - } - - public Builder normals(List normals) { - this.normals = normals; - return this; - } - - public Builder colors(List colors) { - this.colors = colors; - return this; - } - - public Builder textures(List textures) { - this.textures = textures; - return this; - } - - public Builder vertexAttributes(List vertices) { - this.vertexAttributes = vertices; - return this; - } - - public Builder addElement(Element element) { - if (this.elements == null) { - this.elements = new ArrayList<>(); - } - this.elements.add(element); - return this; - } - - public Builder materialFile(String materialFile) { - this.materialFile = materialFile; - return this; - } - - public Builder smoothingGroups(Map> smoothingGroups) { - this.smoothingGroups = smoothingGroups; - return this; - } - - public MeshData build() { - return new MeshData(id, name, vertices, normals, colors, textures, vertexAttributes, elements, materialFile, smoothingGroups); - } - } - - private final String id; - private final String name; - - private List verticesAttributes; - - private final List vertices; - private final List textures; - private List normals; // we can build them - private final List colors; - private final List elements; - - private FloatBuffer vertexBuffer; - private FloatBuffer normalsBuffer; - private FloatBuffer colorsBuffer; - private FloatBuffer textureBuffer; - - // skinning data - private float[] bindShapeMatrix; - private int[] jointsArray; - private float[] weightsArray; - private FloatBuffer jointsBuffer; - private FloatBuffer weightsBuffer; - - // external material file - private final String materialFile; - - // smoothing - private final Map> smoothingGroups; - private List normalsOriginal; - private List verticesAttributesOriginal; - - public MeshData(String id, String name, List vertices, List normals, List colors, List textures, List verticesAttributes, - List elements, String materialFile, Map> smoothingGroups) { - this.id = id; - this.name = name; - - this.vertices = vertices; - this.normals = normals; - this.colors = colors; - this.textures = textures; - - this.verticesAttributes = verticesAttributes; - this.elements = elements; - - this.materialFile = materialFile; - - this.smoothingGroups = smoothingGroups; - - } - - public String getId() { - return id; - } - - public String getName() { - return name; - } - - public List getNormals() { - return this.normals; - } - - public List getElements() { - return elements; - } - - public void smooth() { - - // backup current data - this.normalsOriginal = new ArrayList<>(this.normals.size()); - for (int i = 0; i < this.normals.size(); i++) { - this.normalsOriginal.add(this.normals.get(i).clone()); - } - if (this.verticesAttributes != null) { - try { - this.verticesAttributesOriginal = new ArrayList<>(this.verticesAttributes.size()); - for (int i = 0; i < this.verticesAttributes.size(); i++) { - this.verticesAttributesOriginal.add(this.verticesAttributes.get(i).clone()); - } - } catch (CloneNotSupportedException e) { - // this should never happen - } - } - - // check we have normals to smooth - if (smoothingGroups == null || smoothingGroups.isEmpty()) { - smoothAuto(); - } else { - smoothGroups(); - } - - // refresh normals buffer - refreshNormalsBuffer(); - } - - public void unSmooth() { - if (this.normalsOriginal != null) { - this.normals.clear(); - this.normals = this.normalsOriginal; - } - if (this.verticesAttributesOriginal != null) { - this.verticesAttributes.clear(); - this.verticesAttributes = verticesAttributesOriginal; - } - - } - - private void smoothAuto() { - if (this.elements != null) { - smoothAutoForElements(); - } else { - smoothAutoForArrays(); - } - } - - private void smoothGroups() { - // log event - Log.i("MeshData", "Smoothing groups... Total: " + smoothingGroups.size()); - - // process all smoothing groups - for (Map.Entry> smoothingGroup : smoothingGroups.entrySet()) { - - Log.v("MeshData", "Smoothing group... Total vertices: " + smoothingGroup.getValue().size()); - - - // accumulated normal - float[] smoothNormal = new float[3]; - - for (int i = 0; i < smoothingGroup.getValue().size(); i += 3) { - Vertex va1 = smoothingGroup.getValue().get(i); - Vertex va2 = smoothingGroup.getValue().get(i + 1); - Vertex va3 = smoothingGroup.getValue().get(i + 2); - float[] v1 = this.vertices.get(va1.getVertexIndex()); - float[] v2 = this.vertices.get(va2.getVertexIndex()); - float[] v3 = this.vertices.get(va3.getVertexIndex()); - float[] normal = calculateNormalFailsafe(v1, v2, v3); - smoothNormal = Math3DUtils.add(smoothNormal, normal); - } - - // normalize smooth normals - Math3DUtils.normalizeVector(smoothNormal); - - - // add new normal - final int newSmoothNormalIdx = this.normals.size(); - this.normals.add(newSmoothNormalIdx, smoothNormal); - - // update normal index to smoothed normal - for (int i = 0; i < smoothingGroup.getValue().size(); i++) { - - // next vertex attribute linked to the smoothing group - Vertex va = smoothingGroup.getValue().get(i); - - // When vertex normals are present, they supersede smoothing groups. - if (va.getNormalIndex() != -1) { - continue; - } - - // update with smoothed normal - va.setNormalIndex(newSmoothNormalIdx); - } - } - } - - /** - * Calculate normal using high precision if needed. Otherwise return dummy normal - * - * @param v1 - * @param v2 - * @param v3 - * @return - */ - private static float[] calculateNormalFailsafe(float[] v1, float[] v2, float[] v3) { - float[] normal = Math3DUtils.calculateNormal(v1, v2, v3); - try { - Math3DUtils.normalizeVector(normal); - } catch (Exception e) { - Log.w("MeshData", "Error calculating normal. " + e.getMessage() - + "," + Math3DUtils.toString(v1) - + "," + Math3DUtils.toString(v2) - + "," + Math3DUtils.toString(v3)); - normal = WRONG_NORMAL; - } - return normal; - } - - - /** - * Fix missing or wrong normals. Only for triangulated polygons - */ - public void fixNormals() { - - Log.i("MeshData", "Fixing missing or wrong normals..."); - - // check there is normals to fix - if (this.normals == null || this.normals.isEmpty()) { - - // write new normals - generateNormals(); - - } else { - - // fix missing or wrong - if (this.elements != null) { - fixNormalsForElements(); - } else { - fixNormalsForArrays(); - } - } - } - - - /** - * Fix missing or wrong normals. Only for triangulated polygons - */ - private void generateNormals() { - - Log.i("MeshData", "Generating normals..."); - - // replaced normals - final List newNormals = new ArrayList<>(); - - int counter = 0; - for (Element element : getElements()) { - - for (int i = 0; i < element.getIndices().size(); i += 3) { - - final int idx1 = element.getIndices().get(i); - final int idx2 = element.getIndices().get(i + 1); - final int idx3 = element.getIndices().get(i + 2); - - final Vertex vertexAttribute1 = this.verticesAttributes.get(idx1); - final Vertex vertexAttribute2 = this.verticesAttributes.get(idx2); - final Vertex vertexAttribute3 = this.verticesAttributes.get(idx3); - - final int idxV1 = vertexAttribute1.getVertexIndex(); - final int idxV2 = vertexAttribute2.getVertexIndex(); - final int idxV3 = vertexAttribute3.getVertexIndex(); - - final float[] v1 = this.vertices.get(idxV1); - final float[] v2 = this.vertices.get(idxV2); - final float[] v3 = this.vertices.get(idxV3); - - // check valid triangle - if (Arrays.equals(v1, v2) || Arrays.equals(v2, v3) || Arrays.equals(v1, v3)) { - - // update normal attribute - vertexAttribute1.setNormalIndex(newNormals.size()); - vertexAttribute2.setNormalIndex(newNormals.size()); - vertexAttribute3.setNormalIndex(newNormals.size()); - - // repeated vertex - no normal - newNormals.add(newNormals.size(), WRONG_NORMAL); - - counter++; - continue; - } - - // calculate normal - final float[] calculatedNormal = calculateNormalFailsafe(v1, v2, v3); - - // update normal attribute - vertexAttribute1.setNormalIndex(newNormals.size()); - vertexAttribute2.setNormalIndex(newNormals.size()); - vertexAttribute3.setNormalIndex(newNormals.size()); - - // add normal - newNormals.add(newNormals.size(), calculatedNormal); - - } - } - - this.normals = newNormals; - - Log.i("MeshData", "Generated normals. Total: " + this.normals.size() + ", Faces/Lines: " + counter); - } - - private void fixNormalsForArrays() { - - Log.i("MeshData", "Fixing normals..."); - - // otherwise replaced with this normals - final List newNormals = new ArrayList<>(); - - int counter = 0; - for (int i = 0; i < vertices.size(); i += 3) { - - final float[] v1 = this.vertices.get(i); - final float[] v2 = this.vertices.get(i + 1); - final float[] v3 = this.vertices.get(i + 2); - - // check valid triangle - if (Arrays.equals(v1, v2) || Arrays.equals(v2, v3) || Arrays.equals(v1, v3)) { - - // repeated vertex - no normal - newNormals.add(newNormals.size(), WRONG_NORMAL); - newNormals.add(newNormals.size(), WRONG_NORMAL); - newNormals.add(newNormals.size(), WRONG_NORMAL); - - counter++; - continue; - } - - final float[] normalV1 = normals.get(i); - final float[] normalV2 = normals.get(i + 1); - final float[] normalV3 = normals.get(i + 2); - - // calculate normal - float[] calculatedNormal = calculateNormalFailsafe(v1, v2, v3); - - // check normal attribute 1 - if (Math3DUtils.length(normalV1) < 0.1f) { - - // add normal - newNormals.add(calculatedNormal); - - counter++; - - } else { - - // preserve current normal - newNormals.add(normalV1); - } - - // check normal attribute 2 - if (Math3DUtils.length(normalV2) < 0.1f) { - - // add normal - newNormals.add(calculatedNormal); - - counter++; - - } else { - - // preserve current normal - newNormals.add(normalV2); - } - - // check normal attribute 3 - if (Math3DUtils.length(normalV3) < 0.1f) { - - // add normal - newNormals.add(calculatedNormal); - - counter++; - - } else { - - // preserve current normal - newNormals.add(normalV3); - } - } - - - this.normals = newNormals; - - Log.i("MeshData", "Fixed normals. Total: " + counter); - } - - - private void fixNormalsForElements() { - - Log.i("MeshData", "Fixing normals for all elements..."); - - // otherwise replaced with this normals - final List newNormals = new ArrayList<>(); - - int counter = 0; - for (Element element : getElements()) { - - if (element.getIndices().size() % 3 != 0) continue; - - for (int i = 0; i < element.getIndices().size(); i += 3) { - - final int idx1 = element.getIndices().get(i); - final int idx2 = element.getIndices().get(i + 1); - final int idx3 = element.getIndices().get(i + 2); - - final Vertex vertexAttribute1 = this.verticesAttributes.get(idx1); - final Vertex vertexAttribute2 = this.verticesAttributes.get(idx2); - final Vertex vertexAttribute3 = this.verticesAttributes.get(idx3); - - final int idxV1 = vertexAttribute1.getVertexIndex(); - final int idxV2 = vertexAttribute2.getVertexIndex(); - final int idxV3 = vertexAttribute3.getVertexIndex(); - - final float[] v1 = this.vertices.get(idxV1); - final float[] v2 = this.vertices.get(idxV2); - final float[] v3 = this.vertices.get(idxV3); - - // check valid triangle - if (Arrays.equals(v1, v2) || Arrays.equals(v2, v3) || Arrays.equals(v1, v3)) { - - // update normal attribute - vertexAttribute1.setNormalIndex(newNormals.size()); - vertexAttribute2.setNormalIndex(newNormals.size()); - vertexAttribute3.setNormalIndex(newNormals.size()); - - // repeated vertex - no normal - newNormals.add(newNormals.size(), WRONG_NORMAL); - - counter++; - continue; - } - - final int normalIdxV1 = vertexAttribute1.getNormalIndex(); - final int normalIdxV2 = vertexAttribute2.getNormalIndex(); - final int normalIdxV3 = vertexAttribute3.getNormalIndex(); - - final float[] normalV1 = normals.get(normalIdxV1); - final float[] normalV2 = normals.get(normalIdxV2); - final float[] normalV3 = normals.get(normalIdxV3); - - // calculate normal - float[] calculatedNormal = calculateNormalFailsafe(v1, v2, v3); - - // check normal attribute 1 - if (normalIdxV1 == -1 || Math3DUtils.length(normalV1) < 0.1f) { - - // update normal attribute - vertexAttribute1.setNormalIndex(newNormals.size()); - - // add normal - newNormals.add(calculatedNormal); - - counter++; - - } else { - - // update normal attribute - vertexAttribute1.setNormalIndex(newNormals.size()); - - // preserve current normal - newNormals.add(normalV1); - } - - // check normal attribute 2 - if (normalIdxV2 == -1 || Math3DUtils.length(normalV2) < 0.1f) { - - // update normal attribute - vertexAttribute2.setNormalIndex(newNormals.size()); - - // add normal - newNormals.add(calculatedNormal); - - counter++; - - } else { - - // update normal attribute - vertexAttribute2.setNormalIndex(newNormals.size()); - - // preserve current normal - newNormals.add(normalV2); - } - - // check normal attribute 3 - if (normalIdxV3 == -1 || Math3DUtils.length(normalV3) < 0.1f) { - - // update normal attribute - vertexAttribute3.setNormalIndex(newNormals.size()); - - // add normal - newNormals.add(calculatedNormal); - - counter++; - - } else { - - // update normal attribute - vertexAttribute3.setNormalIndex(newNormals.size()); - - // preserve current normal - newNormals.add(normalV3); - } - } - } - - this.normals = newNormals; - - Log.i("MeshData", "Fixed normals. Total: " + counter); - } - - private void smoothAutoForArrays() { - - // log event - Log.i("MeshData", "Auto smoothing normals for arrays..."); - - // smoothed normal - final Map smoothNormals = new HashMap<>(); - - for (int i = 0; i < vertices.size(); i++) { - - final String idxKey = Arrays.toString(vertices.get(i)); - final float[] smoothNormal = smoothNormals.get(idxKey); - if (smoothNormal == null) { - smoothNormals.put(idxKey, this.normals.get(i)); - continue; - } - - // if same normal, do nothing - final float[] normal = this.normals.get(i); - if (normal == smoothNormal || Arrays.equals(normal, smoothNormal)) { - normals.set(i, smoothNormal); - continue; - } - - // smooth normal - final float[] newSmoothNormal = Math3DUtils.mean(smoothNormal, normal); - Math3DUtils.normalizeVector(newSmoothNormal); - - // update smoothed normal - smoothNormal[0] = newSmoothNormal[0]; - smoothNormal[1] = newSmoothNormal[1]; - smoothNormal[2] = newSmoothNormal[2]; - - // replace with smoothed normal - this.normals.set(i, smoothNormal); - } - } - - private void smoothAutoForElements() { - - // log event - Log.i("MeshData", "Auto smoothing normals for all elements..."); - - // list of normals associated to the vertex (vertexId --> set of normals) - final Map> vertexNormals = new HashMap<>(); - - for (Element element : getElements()) { - - for (int i = 0; i < element.getIndices().size(); i++) { - - // next index - final int idx = element.getIndices().get(i); - - // next vertex attributes - final int vertexIndex = this.verticesAttributes.get(idx).getVertexIndex(); - final int normalIndex = this.verticesAttributes.get(idx).getNormalIndex(); - - // current normal - final float[] normal = this.normals.get(normalIndex); - - // add normal to normals list per vertex - List normals = vertexNormals.get(vertexIndex); - if (normals == null) { - normals = new ArrayList<>(); - normals.add(normal); - vertexNormals.put(vertexIndex, normals); - } else { - normals.add(normal); - } - } - } - - // list of saved smoothed normals - final Map vertexSmooths = new HashMap<>(); - for (Map.Entry> entry : vertexNormals.entrySet()) { - // if same normal, do nothing - final List normals = entry.getValue(); - - // if only 1 normal per vertex, no need to do anything - if (normals.size() == 1) { - vertexSmooths.put(entry.getKey(), normals.get(0)); - continue; - }; - - // otherwise, calculate normal average and save - final float[] smoothNormal = Math3DUtils.mean(normals); - //final float[] smoothNormal = new float[]{1,0,0}; - Math3DUtils.normalizeVector(smoothNormal); - - vertexSmooths.put(entry.getKey(), smoothNormal); - } - - this.normals.clear(); - for (Element element : getElements()) { - - for (int i = 0; i < element.getIndices().size(); i++) { - - // next index - final int idx = element.getIndices().get(i); - - // next vertex attributes - final int vertexIndex = this.verticesAttributes.get(idx).getVertexIndex(); - final int normalIndex = this.verticesAttributes.get(idx).getNormalIndex(); - - float[] smoothNormal = vertexSmooths.get(vertexIndex); - //if (smoothNormal == null) smoothNormal = new float[]{0,1,0}; - this.verticesAttributes.get(idx).setNormalIndex(this.normals.size()); - this.normals.add(smoothNormal); - } - } - -// // last, we update smooth to all references normals -// int count = 0; -// for (Map.Entry entry : vertexSmooths.entrySet()) { -// final float[] smoothNormal = entry.getValue(); -// final List normals = vertexNormals.get(entry.getKey()); -// for (int j = 0; j < normals.size(); j++) { -// normals.get(j)[0] = smoothNormal[0]; -// normals.get(j)[1] = smoothNormal[1]; -// normals.get(j)[2] = smoothNormal[2]; -// count++; -// -//// float[] check = search(this.normals, normals.get(j)); -//// if (check == null || !Arrays.equals(check, normals.get(j))){ -//// Log.e("MeshData","search returned ko"); -//// } -// } -// -// } - - //Log.i("MeshData", "Smoothing fixed a total of " + count + " normals"); - } - - private static float[] search(List list, float[] search){ - for (int i=0; i= normals.size()) { - //throw new IllegalArgumentException("Wrong normal index: " + vertexAttribute.getNormalIndex()); - } - } - } - } - - - public FloatBuffer getVertexBuffer() { - if (this.vertexBuffer == null) { - if (this.verticesAttributes != null) { - this.vertexBuffer = IOUtils.createFloatBuffer(verticesAttributes.size() * 3); - for (int i = 0; i < verticesAttributes.size(); i++) - this.vertexBuffer.put(vertices.get(verticesAttributes.get(i).getVertexIndex())); - } else { - this.vertexBuffer = IOUtils.createFloatBuffer(vertices.size() * 3); - for (int i = 0; i < vertices.size(); i++) - this.vertexBuffer.put(vertices.get(i)); - } - } - return vertexBuffer; - } - - public FloatBuffer getNormalsBuffer() { - if (this.normalsBuffer == null && !this.normals.isEmpty()) { - if (this.verticesAttributes != null) { - this.normalsBuffer = IOUtils.createFloatBuffer(this.verticesAttributes.size() * 3); - for (int i = 0; i < verticesAttributes.size(); i++) { - float[] normal = WRONG_NORMAL; // no normal in case of error - final int index = verticesAttributes.get(i).getNormalIndex(); - if (index >= 0 && index < normals.size()) { - normal = normals.get(index); - } else { - Log.v("MeshData", "Wrong normal index: " + index); - } - this.normalsBuffer.put(normal); - } - } else { - this.normalsBuffer = IOUtils.createFloatBuffer(normals.size() * 3); - for (int i = 0; i < normals.size(); i++) - this.normalsBuffer.put(normals.get(i)); - } - } - return normalsBuffer; - } - - public void refreshNormalsBuffer() { - if (this.normalsBuffer == null || this.normals.isEmpty()) { - Log.e("MeshData", "Can't refresh normals buffer. Either normals or normalsBuffer is empty"); - return; - } else if (this.verticesAttributes != null && this.verticesAttributes.size() * 3 != this.normalsBuffer.capacity()) { - Log.e("MeshData", "Can't refresh normals buffer. Buffer size doesn't match actual data"); - } else if (this.verticesAttributes == null && this.normals.size() * 3 != this.normalsBuffer.capacity()) { - Log.e("MeshData", "Can't refresh normals buffer. Buffer size doesn't match actual data"); - } - - Log.i("MeshData", "Refreshing normals buffer..."); - if (this.verticesAttributes != null) { - for (int i = 0; i < verticesAttributes.size(); i++) { - float[] normal = WRONG_NORMAL; // no normal in case of error - final int index = verticesAttributes.get(i).getNormalIndex(); - if (index >= 0 && index < normals.size()) { - normal = this.normals.get(index); - } else { - Log.v("MeshData", "Wrong normal index: " + index); - } - this.normalsBuffer.put(i * 3, normal[0]); - this.normalsBuffer.put(i * 3 + 1, normal[1]); - this.normalsBuffer.put(i * 3 + 2, normal[2]); - } - } else { - for (int i = 0; i < this.normals.size(); i++) { - this.normalsBuffer.put(i * 3, this.normals.get(i)[0]); - this.normalsBuffer.put(i * 3 + 1, this.normals.get(i)[1]); - this.normalsBuffer.put(i * 3 + 2, this.normals.get(i)[2]); - } - } - } - - public FloatBuffer getColorsBuffer() { - if (this.colorsBuffer == null && !this.colors.isEmpty()) { - this.colorsBuffer = IOUtils.createFloatBuffer(this.verticesAttributes.size() * 4); - for (int i = 0; i < verticesAttributes.size() && i < colors.size(); i++) { - float[] color = new float[]{1, 0, 0, 1}; // red to warn about error - final int index = verticesAttributes.get(i).getColorIndex(); - if (index >= 0 && index < colors.size()) { - color = colors.get(index); - } - this.colorsBuffer.put(color); - } - } - return colorsBuffer; - } - - public FloatBuffer getTextureBuffer() { - if (this.textureBuffer == null && !this.textures.isEmpty()) { - this.textureBuffer = IOUtils.createFloatBuffer(this.verticesAttributes.size() * 2); - for (int i = 0; i < verticesAttributes.size(); i++) { - float[] texture = new float[2]; // no texture in case of error - int index = verticesAttributes.get(i).getTextureIndex(); - if (index >= 0 && index < textures.size()) { - texture = textures.get(index); - texture = new float[]{texture[0], 1 - texture[1]}; - } - this.textureBuffer.put(texture); - } - } - return textureBuffer; - } - - public String getMaterialFile() { - return materialFile; - } - - public FloatBuffer getJointsBuffer() { - if (this.jointsBuffer == null && getJointsArray() != null) { - this.jointsBuffer = IOUtils.createFloatBuffer(getJointsArray().length); - for (int i = 0; i < getJointsArray().length; i++) { - this.jointsBuffer.put(getJointsArray()[i]); - } - } - return jointsBuffer; - } - - public FloatBuffer getWeightsBuffer() { - if (this.weightsBuffer == null && getWeightsArray() != null) { - this.weightsBuffer = IOUtils.createFloatBuffer(getWeightsArray().length); - this.weightsBuffer.put(getWeightsArray()); - } - return weightsBuffer; - } - - public List getVerticesAttributes() { - return verticesAttributes; - } - - public void setBindShapeMatrix(float[] bindShapeMatrix) { - this.bindShapeMatrix = bindShapeMatrix; - } - - public float[] getBindShapeMatrix() { - return bindShapeMatrix; - } - - public void setJointsArray(int[] jointIdsArray) { - this.jointsArray = jointIdsArray; - } - - public void setWeightsArray(float[] weightsArray) { - this.weightsArray = weightsArray; - } - - public int[] getJointsArray() { - return jointsArray; - } - - public float[] getWeightsArray() { - return weightsArray; - } - - public MeshData clone() { - final MeshData ret = new MeshData(getId(), getName(), this.vertices, this.normals, this.colors, this.textures, - getVerticesAttributes(), getElements(), materialFile, smoothingGroups); - ret.setBindShapeMatrix(getBindShapeMatrix()); - ret.setJointsArray(getJointsArray()); - ret.setWeightsArray(getWeightsArray()); - return ret; - } - -} diff --git a/engine/src/main/java/org/the3deer/util/android/ContentUtils.java b/engine/src/main/java/org/the3deer/util/android/ContentUtils.java deleted file mode 100644 index 562aa57a..00000000 --- a/engine/src/main/java/org/the3deer/util/android/ContentUtils.java +++ /dev/null @@ -1,486 +0,0 @@ -package org.the3deer.util.android; - -import android.annotation.SuppressLint; -import android.annotation.TargetApi; -import android.app.Activity; -import android.app.AlertDialog; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.database.Cursor; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.net.Uri; -import android.os.Build; -import android.provider.OpenableColumns; -import android.util.Log; -import android.widget.ImageView; -import android.widget.ListAdapter; -import android.widget.SimpleAdapter; - -import org.the3deer.android_3d_model_engine.R; - -import java.io.BufferedInputStream; -import java.io.BufferedReader; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.net.MalformedURLException; -import java.net.URI; -import java.net.URL; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Scanner; -import java.util.zip.ZipEntry; -import java.util.zip.ZipInputStream; - -import dalvik.system.ZipPathValidator; - -public class ContentUtils { - - static { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { - ZipPathValidator.clearCallback(); - } - } - - public static final String MODELS_FOLDER = "models"; - /** - * Documents opened by the user. This list helps finding the relative filenames found in the model - */ - private static Map documentsProvided = new HashMap<>(); - /** - * Binary files provided by the user. - * Example: zip file names together with their binary data - */ - private static Map binariesProvided = new HashMap<>(); - - private static ThreadLocal currentActivity = new ThreadLocal<>(); - - private static File currentDir = null; - - public static void setThreadActivity(Context currentActivity) { - Log.i("ContentUtils", "Current activity thread: " + Thread.currentThread().getName()); - ContentUtils.currentActivity.set(currentActivity); - } - - private static Context getCurrentActivity() { - return ContentUtils.currentActivity.get(); - } - - public static void setCurrentDir(File file) { - ContentUtils.currentDir = file; - } - - public static void clearDocumentsProvided() { - // clear documents provided by user (kitkat only) - documentsProvided.clear(); - binariesProvided.clear(); - } - - public static void provideAssets(Activity activity) { - Log.i("ContentUtils", "Registering assets... "); - documentsProvided.clear(); - provideAssets(activity, MODELS_FOLDER); - Log.i("ContentUtils", "Assets found: "+ documentsProvided.size()/2); - } - - private static void provideAssets(Activity activity, String directory) { - try { - final String[] files = activity.getAssets().list(directory); - if (files.length > 0) { - for (String document : files) { - final String[] files2 = activity.getAssets().list(directory + "/" + document); - if (files2.length == 0) { - //documentsProvided.put(document, Uri.parse("android://"+activity().getPackageName()+"/assets/models/" + document)); - final Uri assetUri = Uri.parse("android://" + activity.getPackageName() + "/assets/" + directory + "/" + document); - addUri(directory + "/" + document, assetUri); - // FIXME: remove this - addUri("/" + directory + "/" + document, assetUri); - } else { - Log.i("ContentUtils", "Listing directory... " + directory); - provideAssets(activity, directory + "/"+document); - } - } - } - - } catch (IOException ex) { - Log.e("ContentUtils", "Error listing assets from models folder", ex); - } - } - - public static void addUri(String name, Uri uri) { - documentsProvided.put(name, uri); - Log.i("ContentUtils", "Added (" + name + ") " + uri); - } - - /** - * Gets the Uri linked to the filename. - * If the Uri is not found, then a fallback mechanism replacing back slashes - * to forward slashes (standard) is tried ('\' to '/') - * @param name the file name - * @return the linked Uri - */ - public static Uri getUri(String name) { - - // default try - Uri uri = documentsProvided.get(name); - if (uri != null) return uri; - - // try replacing back-slashes - return documentsProvided.get(name.replaceAll("\\\\","/")); - } - - public static void addData(Uri uri, byte[] data) { - binariesProvided.put(uri, data); - Log.i("ContentUtils", "Added (" + uri + ") " + data.length + " bytes"); - } - - public static byte[] getData(Uri uri) { - return binariesProvided.get(uri); - } - - /** - * Find the relative file that should be already selected by the user - * - * @param path relative file - * @return InputStream of the file - * @throws IOException if there is an error opening stream - */ - public static InputStream getInputStream(String path) throws IOException { - Uri uri = getUri(path); - if (uri == null) { - uri = getUri("models/"+path); - } - if (uri == null && currentDir != null) { - uri = Uri.parse("file://" + new File(currentDir, path).getAbsolutePath()); - } - if (uri != null) { - return getInputStream(uri); - } - - Log.e("ContentUtils", "Media not found: " + path); - Log.d("ContentUtils", "Available media: " + documentsProvided); - Log.d("ContentUtils", "Available media: " + binariesProvided); - throw new FileNotFoundException("File not found: " + path); - } - - public static InputStream getInputStream(URI uri) throws IOException { - return getInputStream(Uri.parse(uri.toURL().toString())); - } - - public static InputStream getInputStream(Uri uri) throws IOException { - if (getCurrentActivity() == null){ - throw new IllegalStateException("There is no context configured. Did you call #setContext() before?"); - } - if (getData(uri) != null){ - Log.i("ContentUtils", "Returning binary: " + uri); - return new ByteArrayInputStream(getData(uri)); - } - - Log.i("ContentUtils", "Opening stream ..." + uri); - if (uri.getScheme().equals("android")) { - if (uri.getPath().startsWith("/assets/")) { - final String path = uri.getPath().substring("/assets/".length()); - Log.i("ContentUtils", "Opening asset: " + path); - return getCurrentActivity().getAssets().open(path); - } else if (uri.getPath().startsWith("/res/drawable/")){ - final String path = uri.getPath().substring("/res/drawable/".length()).replace(".png",""); - Log.i("ContentUtils", "Opening drawable: " + path); - final int resourceId = getCurrentActivity().getResources() - .getIdentifier(path, "drawable", getCurrentActivity().getPackageName()); - return getCurrentActivity().getResources().openRawResource(resourceId); - } else { - throw new IllegalArgumentException("unknown android path: "+uri.getPath()); - } - } - if (uri.getScheme().equals("http") || uri.getScheme().equals("https")) { - return new URL(uri.toString()).openStream(); - } - if (uri.getScheme().equals("content")) { - return getCurrentActivity().getContentResolver().openInputStream(uri); - } - return getCurrentActivity().getContentResolver().openInputStream(uri); - } - - /** - * Read the Android resource id (R.raw.xxxId) - * - * @param resourceId - * @return - * @throws IOException - */ - public static InputStream getInputStream(int resourceId) throws IOException { - if (getCurrentActivity() == null) throw new IllegalStateException("No current activity"); - return getCurrentActivity().getResources().openRawResource(resourceId); - } - - - public static Intent createGetContentIntent(String mimeType) { - // check here to KITKAT or new version - final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; - if (isKitKat) { - return createGetMultipleContentIntent(mimeType); - } - return createGetSingleContentIntent(mimeType); - } - - /** - * Get the Intent for selecting content to be used in an Intent Chooser. - * - * @return The intent for opening a file with Intent.createChooser() - * @author andresoviedo - */ - @TargetApi(Build.VERSION_CODES.KITKAT) - private static Intent createGetMultipleContentIntent(String mimeType) { - // Implicitly allow the user to select a particular kind of data - final Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); - // The MIME data type filter - intent.setType(mimeType); - // EXTRA_ALLOW_MULTIPLE: added in API level 18 - intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true); - // Only return URIs that can be opened with ContentResolver - intent.addCategory(Intent.CATEGORY_OPENABLE); - return intent; - } - - /** - * Get the Intent for selecting content to be used in an Intent Chooser. - * - * @return The intent for opening a file with Intent.createChooser() - * @author andresoviedo - */ - @TargetApi(Build.VERSION_CODES.KITKAT) - private static Intent createGetSingleContentIntent(String mimeType) { - // Implicitly allow the user to select a particular kind of data - final Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); - // The MIME data type filter - intent.setType(mimeType); - // Only return URIs that can be opened with ContentResolver - intent.addCategory(Intent.CATEGORY_OPENABLE); - return intent; - } - - public static void showDialog(Activity activity, String title, CharSequence message, String positiveButtonLabel, - String negativeButtonLabel, DialogInterface.OnClickListener listener) { - AlertDialog.Builder builder = new AlertDialog.Builder(activity); - builder.setTitle(title); - builder.setMessage(message); - builder.setPositiveButton(positiveButtonLabel, listener); - builder.setNegativeButton(negativeButtonLabel, listener); - builder.create().show(); - } - - public static void showListDialog(Activity activity, String title, String[] options, DialogInterface - .OnClickListener listener) { - AlertDialog.Builder builder = new AlertDialog.Builder(activity); - builder.setTitle(title).setItems(options, listener); - builder.create().show(); - } - - @FunctionalInterface - public interface Callback { - void onClick(String asset); - } - - public static AlertDialog.Builder createChooserDialog(Context context, String title, CharSequence message, List fileListAssets, - String fileRegex, AssetUtils.Callback callback) { - final AlertDialog.Builder builder = new AlertDialog.Builder(context); - builder.setTitle(title); - builder.setMessage(message); - builder.setNegativeButton("Cancel", (DialogInterface dialog, int which) -> { - callback.onClick(null); - }); - - final List fileListModels = new ArrayList<>(); - for (String file : fileListAssets) { - if (file.matches(fileRegex)) { - fileListModels.add(file); - } - } - final String[] fileListArray = new String[fileListModels.size()]; - for (int i = 0; i < fileListModels.size(); i++) { - String model = fileListModels.get(i); - fileListArray[i] = model.substring(model.lastIndexOf("/") + 1); - if (fileListArray[i].endsWith(".index")){ - fileListArray[i] = fileListArray[i].replace(".index","..."); - } - } - builder.setItems(fileListArray, (DialogInterface dialog, int which) -> { - documentsProvided.clear(); - for (String asset : fileListAssets) { - documentsProvided.put(asset.substring(asset.lastIndexOf("/") + 1), Uri.parse(asset)); - } - callback.onClick(fileListModels.get(which)); - }); - - return builder; - } - - public static AlertDialog.Builder createChooserDialog(Context context, String title, CharSequence message, List fileListAssets, Map icons, - String fileRegex, AssetUtils.Callback callback) { - if (icons == null){ - return createChooserDialog(context, title, message, fileListAssets, fileRegex, callback); - } - final AlertDialog.Builder builder = new AlertDialog.Builder(context); - builder.setTitle(title); - builder.setMessage(message); - builder.setNegativeButton("Cancel", (DialogInterface dialog, int which) -> { - callback.onClick(null); - }); - - final List fileListModels = new ArrayList<>(); - for (String file : fileListAssets) { - if (file.matches(fileRegex)) { - fileListModels.add(file); - } - } - - final ArrayList> modelList=new ArrayList<>(); - for (int i=0;i hashMap=new HashMap<>();//create a hashmap to store the data in key value pair - hashMap.put("name", label); - if (icons != null && icons.containsKey(icon)){ - hashMap.put("image", String.valueOf(i)); - hashMap.put("bitmap",BitmapFactory.decodeStream(new ByteArrayInputStream(icons.get(icon)))); - } else if (icons != null && icons.containsKey(icon2)){ - hashMap.put("image", String.valueOf(i)); - hashMap.put("bitmap",BitmapFactory.decodeStream(new ByteArrayInputStream(icons.get(icon2)))); - } - hashMap.put("url", url); - modelList.add(hashMap);//add the hashmap into arrayList - } - - final String[] from={"name","image"};//string array - final int[] to={R.id.textView,R.id.imageView};//int array of views id's - final ListAdapter textImageAdapter = new SimpleAdapter(context, - modelList, R.layout.list_text_with_image ,from,to){ - public void setViewImage(ImageView v, String value){ - v.setImageBitmap(null); - if (value != null && value.length() > 0) { - try { - v.setImageBitmap((Bitmap) modelList.get(Integer.parseInt(value)).get("bitmap")); - } catch (Exception ex) { - Log.e("ContentUtils", ex.getMessage(), ex); - } - } - } - }; - builder.setAdapter(textImageAdapter, (DialogInterface dialog, int which) -> { - documentsProvided.clear(); - for (String asset : fileListAssets) { - documentsProvided.put(asset.substring(asset.lastIndexOf("/") + 1), Uri.parse(asset)); - } - callback.onClick((String)modelList.get(which).get("url")); - }); - - return builder; - } - - public static List readLines(String url) { - try { - return readLines(new URL(url)); - } catch (MalformedURLException ex) { - Log.e("ContentUtils", "Error", ex); - return null; - } - } - - public static String read(URL url) { - try (InputStream is = url.openStream()){ - return new Scanner(is).useDelimiter("\\Z").next(); - } catch (IOException ex) { - Log.e("ContentUtils", "Error reading from " + url, ex); - return null; - } - } - - public static List readLines(URL url) { - try { - List ret = new ArrayList<>(); - try (InputStream is = url.openStream()) { - BufferedReader br = new BufferedReader(new InputStreamReader(is)); - String line; - while ((line = br.readLine()) != null) { - ret.add(line); - } - } - return ret; - } catch (IOException ex) { - Log.e("ContentUtils", "Error reading from " + url, ex); - return null; - } - } - - /** - * Read a zip file from the specified URL and return the list of files - * @param url zip file - * @return list of files (name + bytes) - */ - public static Map readFiles(URL url) { - try { - byte[] buffer = new byte[512]; - try (InputStream is = url.openStream()) { - final Map ret = new HashMap<>(); - ZipInputStream zis = new ZipInputStream(new BufferedInputStream(is)); - ZipEntry ze; - while ((ze = zis.getNextEntry()) != null) { - if (ze.isDirectory()) continue; - final String name = ze.getName(); - final ByteArrayOutputStream bos = new ByteArrayOutputStream(); - int readed = 0; - while ((readed = zis.read(buffer)) > 0) { - bos.write(buffer, 0, readed); - } - ret.put(name, bos.toByteArray()); - } - return ret; - } - } catch (FileNotFoundException ex){ - return null; - } catch (IOException ex) { - Log.e("ContentUtils", "Error reading from " + url, ex); - return null; - } - } - - /** - * Get filename from the Android Content Provider response - * @param uri content provider returned uri - * @return the filename of the Uri - */ - @SuppressLint("Range") - public static String getFileName(Uri uri) { - String result = null; - if (uri.getScheme().equals("content") && currentActivity != null) { - try (Cursor cursor = Objects.requireNonNull(currentActivity.get()). - getContentResolver().query(uri, null, null, null, null)) { - if (cursor != null && cursor.moveToFirst()) { - result = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)); - } - } - } - if (result == null) { - result = uri.getPath(); - int cut = result.lastIndexOf('/'); - if (cut != -1) { - result = result.substring(cut + 1); - } - } - return result; - } - -} \ No newline at end of file diff --git a/engine/src/main/java/org/the3deer/util/math/Math3DUtils.java b/engine/src/main/java/org/the3deer/util/math/Math3DUtils.java deleted file mode 100644 index bce98a8d..00000000 --- a/engine/src/main/java/org/the3deer/util/math/Math3DUtils.java +++ /dev/null @@ -1,969 +0,0 @@ -package org.the3deer.util.math; - -import android.opengl.Matrix; -import android.util.Log; - -import org.the3deer.android_3d_model_engine.animation.JointTransform; -import org.the3deer.android_3d_model_engine.model.Constants; - -import java.math.BigDecimal; -import java.util.Arrays; -import java.util.List; -import java.util.Locale; - -/** - * Utility class to calculate 3D stuff - * - * @author andresoviedo - */ -public class Math3DUtils { - - public static final float IDENTITY_MATRIX[] = new float[16]; - public static final float VECTOR_UNIT_X[] = {1,0,0}; - public static final float VECTOR_UNIT_Y[] = {0,1,0}; - public static final float VECTOR_UNIT_Z[] = {0,0,1}; - - static { - Matrix.setIdentityM(IDENTITY_MATRIX, 0); - } - - public static float[] initMatrixIfNull(float[] matrix) { - if (matrix != null) return matrix; - matrix = new float[16]; - Matrix.setIdentityM(matrix, 0); - return matrix; - } - - /** - * Matrix - column major order: - * - * lhs[0] lhs[4] lhs[8] lhs[12] rhs[0] rhs[4] rhs[8] rhs[12] - * t = lhs[1] lhs[5] lhs[9] lhs[13] x rhs[1] ... - * lhs[2] lhs[6] lhs[10] lhs[14] - * lhs[3] lhs[7] lhs[11] lhs[15] - * - * t[0] t[4] t[8] t[12] - * t[1] - * - * @param lhs - * @param rhs - * @param dest - */ - public static void multiplyMM(float[] dest, float[] lhs, float[] rhs) - { - // first column - dest[0] = lhs[0] * rhs[0] + lhs[4] * rhs[1] + lhs[8] * rhs[2] + lhs[12] * rhs[3]; - dest[1] = lhs[1] * rhs[0] + lhs[5] * rhs[1] + lhs[9] * rhs[2] + lhs[13] * rhs[3]; - dest[2] = lhs[2] * rhs[0] + lhs[6] * rhs[1] + lhs[10] * rhs[2] + lhs[14] * rhs[3]; - dest[3] = lhs[3] * rhs[0] + lhs[7] * rhs[1] + lhs[11] * rhs[2] + lhs[15] * rhs[3]; - - // second column - dest[4] = lhs[0] * rhs[4] + lhs[4] * rhs[5] + lhs[8] * rhs[6] + lhs[12] * rhs[7]; - dest[5] = lhs[1] * rhs[4] + lhs[5] * rhs[5] + lhs[9] * rhs[6] + lhs[13] * rhs[7]; - dest[6] = lhs[2] * rhs[4] + lhs[6] * rhs[5] + lhs[10] * rhs[6] + lhs[14] * rhs[7]; - dest[7] = lhs[3] * rhs[4] + lhs[7] * rhs[5] + lhs[11] * rhs[6] + lhs[15] * rhs[7]; - - // third column - dest[8] = lhs[0] * rhs[8] + lhs[4] * rhs[9] + lhs[8] * rhs[10] + lhs[12] * rhs[11]; - dest[9] = lhs[1] * rhs[8] + lhs[5] * rhs[9] + lhs[9] * rhs[10] + lhs[13] * rhs[11]; - dest[10] = lhs[2] * rhs[8] + lhs[6] * rhs[9] + lhs[10] * rhs[10] + lhs[14] * rhs[11]; - dest[11] = lhs[3] * rhs[8] + lhs[7] * rhs[9] + lhs[11] * rhs[10] + lhs[15] * rhs[11]; - - // forth column - dest[12] = lhs[0] * rhs[12] + lhs[4] * rhs[13] + lhs[8] * rhs[14] + lhs[12] * rhs[15]; - dest[13] = lhs[1] * rhs[12] + lhs[5] * rhs[13] + lhs[9] * rhs[14] + lhs[13] * rhs[15]; - dest[14] = lhs[2] * rhs[12] + lhs[6] * rhs[13] + lhs[10] * rhs[14] + lhs[14] * rhs[15]; - dest[15] = lhs[3] * rhs[12] + lhs[7] * rhs[13] + lhs[11] * rhs[14] + lhs[15] * rhs[15]; - }; - - /** - * Calculate face normal - *

- * So for a triangle p1, p2, p3, if the vector U = p2 - p1 and the vector V = p3 - p1 then the normal N = U X V and can be calculated by: - *

- *
-     * Nx = UyVz - UzVy
-     * Ny = UzVx - UxVz
-     * Nz = UxVy - UyVx
-     * 
- * - * Calculating_a_Surface_Normal - * - * @param v0 - * @param v1 - * @param v2 - * @return - */ - public static float[] calculateNormal(float[] v0, float[] v1, float[] v2) { - - // calculate perpendicular vector to the face. That is to calculate the cross product of v1-v0 x v2-v0 - double[] va = new double[]{v1[0] - v0[0], v1[1] - v0[1], v1[2] - v0[2]}; - double[] vb = new double[]{v2[0] - v0[0], v2[1] - v0[1], v2[2] - v0[2]}; - float[] vn = {(float) (va[1] * vb[2] - va[2] * vb[1]), (float) (va[2] * vb[0] - va[0] * vb[2]), - (float) (va[0] * vb[1] - va[1] * vb[0])}; - - if (length(vn) != 0) { - return vn; - } else { - return calculateNormal_highPrecision(v0, v1, v2); - } - } - - /** - * Calculate face normal using high precision maths - *

- * So for a triangle p1, p2, p3, if the vector U = p2 - p1 and the vector V = p3 - p1 then the normal N = U X V and can be calculated by: - *

- *
-     * Nx = UyVz - UzVy
-     * Ny = UzVx - UxVz
-     * Nz = UxVy - UyVx
-     * 
- * - * Calculating_a_Surface_Normal - * - * @param v0 - * @param v1 - * @param v2 - * @return - */ - public static float[] calculateNormal_highPrecision(float[] v0, float[] v1, float[] v2) { - - // calculate the 2 vectors - final float[] u = substract(v1, v0); - final float[] v = substract(v2, v0); - - final BigDecimal[] u_ = new BigDecimal[]{ - new BigDecimal(Float.toString(u[0])), - new BigDecimal(Float.toString(u[1])), - new BigDecimal(Float.toString(u[2])) - }; - - final BigDecimal[] v_ = new BigDecimal[]{ - new BigDecimal(Float.toString(v[0])), - new BigDecimal(Float.toString(v[1])), - new BigDecimal(Float.toString(v[2])) - }; - - final BigDecimal[] n_ = new BigDecimal[]{ - u_[1].multiply(v_[2]).subtract(u_[2].multiply(v_[1])), - u_[2].multiply(v_[0]).subtract(u_[0].multiply(v_[2])), - u_[0].multiply(v_[1]).subtract(u_[1].multiply(v_[0])) - }; - - return new float[]{ - n_[0].floatValue(), - n_[1].floatValue(), - n_[2].floatValue() - }; - } - - /** - * Calculate the 2 vectors, that is a line (x1,y1,z1-x2,y2,z2} corresponding to the normal of the specified face. - * The calculated line will be positioned exactly in the middle of the face - * - * @param v0 the first vector of the face - * @param v1 the second vector of the face - * @param v2 the third vector of the face - * @param scale if true scale normal line according to triangle size (bigger triangle bigger line) - * @return the 2 vectors (line) corresponding to the face normal - */ - public static float[][] calculateNormalLine(float[] v0, float[] v1, float[] v2, boolean scale) { - - // calculate perpendicular vector to the face. That is to calculate the cross product of v1-v0 x v2-v0 - float[] va = new float[]{v1[0] - v0[0], v1[1] - v0[1], v1[2] - v0[2]}; - float[] vb = new float[]{v2[0] - v0[0], v2[1] - v0[1], v2[2] - v0[2]}; - float[] n = new float[]{va[1] * vb[2] - va[2] * vb[1], va[2] * vb[0] - va[0] * vb[2], - va[0] * vb[1] - va[1] * vb[0]}; - float modul = Matrix.length(n[0], n[1], n[2]); - float[] vn = new float[]{n[0] / modul, n[1] / modul, n[2] / modul}; - - return getNormalLine(v0, v1, v2, vn, scale, 1); - } - - /** - * Calculate the 2 vectors, that is a line (x1,y1,z1-x2,y2,z2} corresponding to the normal of the specified face. - * The calculated line will be positioned exactly in the middle of the face - * - * @param v0 the first vector of the face - * @param v1 the second vector of the face - * @param v2 the third vector of the face - * @param normal the normal vector - * @param scale if true scale normal line according to triangle size (bigger triangle bigger line) - * @return the 2 vectors (line) corresponding to the face normal - */ - public static float[][] getNormalLine(float[] v0, float[] v1, float[] v2, float[] normal, boolean scale, float rescale) { - - // calculate center of the face - final float[] faceCenter = calculateFaceCenter(v0, v1, v2); - - final float[] va = new float[]{v1[0] - v0[0], v1[1] - v0[1], v1[2] - v0[2]}; - final float[] vb = new float[]{v2[0] - v0[0], v2[1] - v0[1], v2[2] - v0[2]}; - final float[] vc = new float[]{v2[0] - v1[0], v2[1] - v1[1], v2[2] - v1[2]}; - - // scale normal proportional to triangle perimeter (or area) - final float scaleFactor = scale ? (length(va[0], va[1], va[2]) - + length(vb[0], vb[1], vb[2]) - + length(vc[0], vc[1], vc[2])) / 3 : 1; - - // calculate 2nd vertex position - float[] vn2 = new float[]{ - faceCenter[0] + normal[0] * scaleFactor * rescale - , faceCenter[1] + normal[1] * scaleFactor * rescale - , faceCenter[2] + normal[2] * scaleFactor * rescale}; - - return new float[][]{faceCenter, vn2}; - } - - /** - * Calculate the 3 lines, that is a line (x1,y1,z1-x2,y2,z2} corresponding to the normal of the specified face. - * - * @param v0 the first vector of the face - * @param v1 the second vector of the face - * @param v2 the third vector of the face - * @param normal the normal vector - * @param scale if true scale normal line according to triangle size (bigger triangle bigger line) - * @return the 2 vectors (line) corresponding to the face normal - */ - public static float[][] getNormalLines(float[] v0, float[] v1, float[] v2, float[] normal, boolean scale, float rescale) { - - final float[] va = new float[]{v1[0] - v0[0], v1[1] - v0[1], v1[2] - v0[2]}; - final float[] vb = new float[]{v2[0] - v0[0], v2[1] - v0[1], v2[2] - v0[2]}; - final float[] vc = new float[]{v2[0] - v1[0], v2[1] - v1[1], v2[2] - v1[2]}; - - // scale normal proportional to triangle perimeter (or area) - final float scaleFactor = scale ? (length(va[0], va[1], va[2]) - + length(vb[0], vb[1], vb[2]) - + length(vc[0], vc[1], vc[2])) / 3 : 1; - - // calculate 2nd vertex position - float[] vn0 = new float[]{ - v0[0] + normal[0] * scaleFactor * rescale - , v0[1] + normal[1] * scaleFactor * rescale - , v0[2] + normal[2] * scaleFactor * rescale}; - - float[] vn1 = new float[]{ - v1[0] + normal[0] * scaleFactor * rescale - , v1[1] + normal[1] * scaleFactor * rescale - , v1[2] + normal[2] * scaleFactor * rescale}; - - float[] vn2 = new float[]{ - v2[0] + normal[0] * scaleFactor * rescale - , v2[1] + normal[1] * scaleFactor * rescale - , v2[2] + normal[2] * scaleFactor * rescale}; - - return new float[][]{v0, vn0, v1, vn1, v2, vn2}; - } - - public static float[] calculateFaceCenter(float[] v0, float[] v1, float[] v2) { - return new float[]{(v0[0] + v1[0] + v2[0]) / 3, (v0[1] + v1[1] + v2[1]) / 3, (v0[2] + v1[2] + v2[2]) / 3}; - } - - /** - * Calculates the distance of the intersection between the specified ray and the target, or return -1 if the ray - * doesn't intersect the target - * - * @param rayPoint1 where the ray starts - * @param rayPoint2 where the ray ends - * @param target where is the object to intersect - * @param precision the radius to test for intersection - * @return the distance of intersection - * @deprecated - */ - public static float calculateDistanceOfIntersection(float[] rayPoint1, float[] rayPoint2, float[] target, - float precision) { - float raySteps = 100f; - float objHalfWidth = precision / 2; - - float length = Matrix.length(rayPoint2[0] - rayPoint1[0], rayPoint2[1] - rayPoint1[1], - rayPoint2[2] - rayPoint1[2]); - float lengthDiff = length / raySteps; - - float xDif = (rayPoint2[0] - rayPoint1[0]) / raySteps; - float yDif = (rayPoint2[1] - rayPoint1[1]) / raySteps; - float zDif = (rayPoint2[2] - rayPoint1[2]) / raySteps; - - for (int i = 0; i < raySteps; i++) { - // @formatter:off - if ((rayPoint1[0] + (xDif * i)) > target[0] - objHalfWidth - && (rayPoint1[0] + (xDif * i)) < target[0] + objHalfWidth - && (rayPoint1[1] + (yDif * i)) > target[1] - objHalfWidth - && (rayPoint1[1] + (yDif * i)) < target[1] + objHalfWidth - && (rayPoint1[2] + (zDif * i)) > target[2] - objHalfWidth - && (rayPoint1[2] + (zDif * i)) < target[2] + objHalfWidth) { - // @formatter:on - // Log.v(TouchController.TAG, "HIT: i[" + i + "] wz[" + (rayPoint1[2] + (zDif * i)) + "]"); - // return new Object[] { i * lengthDiff, new float[] { rayPoint1[0] + (xDif * i), - // rayPoint1[1] + (yDif * i), rayPoint1[2] + (zDif * i) } }; - return i * lengthDiff; - } - } - return -1; - } - - /** - * Substract 2 vertex: a-b - * - * @param a - * @param b - * @return a-b - */ - public static float[] substract(float[] a, float[] b) { - return new float[]{a[0] - b[0], a[1] - b[1], a[2] - b[2]}; - } - - /** - * Divide 2 vertex: a/b - * - * @param a - * @param b - * @return a/b - */ - public static float[] divide(float[] a, float[] b) { - return new float[]{a[0] / b[0], a[1] / b[1], a[2] / b[2]}; - } - - /** - * Divide vertex: a/b - * - * @param a - * @param b - * @return a/b - */ - public static float[] divide(float[] a, float b) { - return new float[]{a[0] / b, a[1] / b, a[2] / b}; - } - - /** - * Get the min of both vertex - * - * @param a - * @param b - * @return min of both vertex - */ - public static float[] min(float[] a, float[] b) { - return new float[]{Math.min(a[0], b[0]), Math.min(a[1], b[1]), Math.min(a[2], b[2])}; - } - - /** - * Get the max of both vertex - * - * @param a - * @param b - * @return max of both vertex - */ - public static float[] max(float[] a, float[] b) { - return new float[]{Math.max(a[0], b[0]), Math.max(a[1], b[1]), Math.max(a[2], b[2])}; - } - - /** - * Normalize the specified vector - * - * @param a - */ - public static void normalizeVector(float[] a) { - float length = length(a); - if (length == 0) { -// throw new IllegalArgumentException("vector length is zero"); - return; - } - a[0] = a[0] / length; - a[1] = a[1] / length; - a[2] = a[2] / length; - } - - public static float[] normalize2(float[] a) { - float[] copy = a.clone(); - normalizeVector(copy); - return copy; - } - - public static float[] crossProduct(float[] a, float[] b) { - // AxB = (AyBz − AzBy, AzBx − AxBz, AxBy − AyBx) - //(r)[0] = (a)[1] * (b)[2] - (b)[1] * (a)[2]; \ - //(r)[1] = (a)[2] * (b)[0] - (b)[2] * (a)[0]; \ - //(r)[2] = (a)[0] * (b)[1] - (b)[0] * (a)[1]; - float x = a[1] * b[2] - a[2] * b[1]; - float y = a[2] * b[0] - a[0] * b[2]; - float z = a[0] * b[1] - a[1] * b[0]; - return new float[]{x, y, z}; - } - - public static float[] crossProduct(float ax, float ay, float az, float bx, float by, float bz) { - // AxB = (AyBz − AzBy, AzBx − AxBz, AxBy − AyBx) - //(r)[0] = (a)[1] * (b)[2] - (b)[1] * (a)[2]; \ - //(r)[1] = (a)[2] * (b)[0] - (b)[2] * (a)[0]; \ - //(r)[2] = (a)[0] * (b)[1] - (b)[0] * (a)[1]; - float x = ay * bz - az * by; - float y = az * bx - ax * bz; - float z = ax * by - ay * bx; - return new float[]{x, y, z}; - } - - public static float dotProduct(float[] a, float[] b) { - // a1b1+a2b2+a3b3 - return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; - } - - public static void mult(float[] v, float t) { - for (int i = 0; i < v.length; i++) v[i] = v[i] * t; - } - - public static float[] multiply(float[] a, float t) { - return new float[]{a[0] * t, a[1] * t, a[2] * t}; - } - - /** - * Adds 2 vectors - * @param a vector 1 - * @param b vector 2 - * @return a new float the with result of the addition - */ - public static float[] add(float[] a, float[] b) { - return new float[]{a[0] + b[0], a[1] + b[1], a[2] + b[2]}; - } - - public static float[] mean(List normals) { - float[] normal_mean = normals.get(0); - for (int i=1; i= 0) { - ret.append("+"); - } - ret.append(String.format(Locale.getDefault(), "%+.3f", matrix[j * 4 + i])); - ret.append(" "); - } - } - return ret.toString(); - } - - public static String toString(float[] array) { - StringBuilder ret = new StringBuilder("["); - for (int i = 0; i < array.length; i++) { - if (i > 0) ret.append(" "); - ret.append(String.format(Locale.getDefault(), "%+.4f", array[i])); - } - ret.append("]"); - return ret.toString(); - } - - public static float[] parseFloat(String[] rawData) { - float[] matrixData = new float[rawData.length]; - for (int i = 0; i < rawData.length; i++) { - matrixData[i] = Float.parseFloat(rawData[i]); - } - return matrixData; - } - - /** - * {@link Matrix} - */ - public static void setRotateM(float[] rm, int rmOffset, - float a, float x, float y, float z) { - rm[rmOffset + 3] = 0; - rm[rmOffset + 7] = 0; - rm[rmOffset + 11] = 0; - rm[rmOffset + 12] = 0; - rm[rmOffset + 13] = 0; - rm[rmOffset + 14] = 0; - rm[rmOffset + 15] = 1; - a *= (float) (Math.PI / 180.0f); - float s = (float) Math.sin(a); - float c = (float) Math.cos(a); - if (1.0f == x && 0.0f == y && 0.0f == z) { - rm[rmOffset + 5] = c; - rm[rmOffset + 10] = c; - rm[rmOffset + 6] = s; - rm[rmOffset + 9] = -s; - rm[rmOffset + 1] = 0; - rm[rmOffset + 2] = 0; - rm[rmOffset + 4] = 0; - rm[rmOffset + 8] = 0; - rm[rmOffset + 0] = 1; - } else if (0.0f == x && 1.0f == y && 0.0f == z) { - rm[rmOffset + 0] = c; - rm[rmOffset + 10] = c; - rm[rmOffset + 8] = s; - rm[rmOffset + 2] = -s; - rm[rmOffset + 1] = 0; - rm[rmOffset + 4] = 0; - rm[rmOffset + 6] = 0; - rm[rmOffset + 9] = 0; - rm[rmOffset + 5] = 1; - } else if (0.0f == x && 0.0f == y && 1.0f == z) { - rm[rmOffset + 0] = c; - rm[rmOffset + 5] = c; - rm[rmOffset + 1] = s; - rm[rmOffset + 4] = -s; - rm[rmOffset + 2] = 0; - rm[rmOffset + 6] = 0; - rm[rmOffset + 8] = 0; - rm[rmOffset + 9] = 0; - rm[rmOffset + 10] = 1; - } else { - float len = length(x, y, z); - if (1.0f != len) { - float recipLen = 1.0f / len; - x *= recipLen; - y *= recipLen; - z *= recipLen; - } - float nc = 1.0f - c; - float xy = x * y; - float yz = y * z; - float zx = z * x; - float xs = x * s; - float ys = y * s; - float zs = z * s; - rm[rmOffset + 0] = x * x * nc + c; - rm[rmOffset + 4] = xy * nc - zs; - rm[rmOffset + 8] = zx * nc + ys; - rm[rmOffset + 1] = xy * nc + zs; - rm[rmOffset + 5] = y * y * nc + c; - rm[rmOffset + 9] = yz * nc - xs; - rm[rmOffset + 2] = zx * nc - ys; - rm[rmOffset + 6] = yz * nc + xs; - rm[rmOffset + 10] = z * z * nc + c; - } - } - - /** - * {@link Matrix} - */ - public static float length(float[] vector) { - return length(vector[0], vector[1], vector[2]); - } - - /** - * {@link Matrix} - */ - public static float length(float x, float y, float z) { - return (float) Math.sqrt(x * x + y * y + z * z); - } - - public static void interpolate(JointTransform result, JointTransform start, JointTransform end, float progression) { - interpolate(result.getScale(), start.getScale(), end.getScale(), progression); - interpolate(result.getLocation(), start.getLocation(), end.getLocation(), progression); - /*interpolate(result.getRotation1(), start.getRotation1(), end.getRotation1(), progression); - interpolate(result.getRotation2(), start.getRotation2(), end.getRotation2(), progression); - interpolate(result.getRotation2Location(), start.getRotation2Location(), end.getRotation2Location(), progression);*/ - Quaternion.interpolate(result.getQRotation(), start.getQRotation(), end.getQRotation(), progression); - } - - /** - * Linearly interpolates between two translations based on a "progression" - * value. - * - * @param start - the start translation. - * @param end - the end translation. - * @param progression - a value between 0 and 1 indicating how far to interpolate - * between the two translations. - * @return - */ - public static void interpolate(Float result[], Float[] start, Float[] end, float progression) { - if (start == null || end == null) return; - for (int i = 0; i < result.length; i++) { - result[i] = start[i] + (end[i] - start[i]) * progression; - } - } - - public static float[] negate(float[] vector) { - float[] ret = new float[vector.length]; - for (int i = 0; i < vector.length; i++) ret[i] = -vector[i]; - return ret; - } - - public static float[] mult(float[] vector1, float[] vector2) { - float[] ret = new float[vector1.length]; - for (int i = 0; i < vector1.length; i++) ret[i] = vector1[i] * vector2[i]; - return ret; - } - - public static Float[] scaleFromMatrix(float[] matrix) { - - // check - if (matrix == null) return null; - - // |A| = a(ei − fh) − b(di − fg) + c(dh − eg) - Float[] ret = new Float[3]; - ret[0] = (float) Math.sqrt(Math.pow(matrix[0], 2) + Math.pow(matrix[1], 2) + Math.pow(matrix[2], 2)); - ret[1] = (float) Math.sqrt(Math.pow(matrix[4], 2) + Math.pow(matrix[5], 2) + Math.pow(matrix[6], 2)); - ret[2] = (float) Math.sqrt(Math.pow(matrix[8], 2) + Math.pow(matrix[9], 2) + Math.pow(matrix[10], 2)); - if (determinant(matrix) < 0) { - ret[1] = -ret[1]; - } - return ret; - } - - public static float[] scaleFromMatrixf(float[] matrix) { - // |A| = a(ei − fh) − b(di − fg) + c(dh − eg) - float[] ret = new float[3]; - ret[0] = (float) Math.sqrt(Math.pow(matrix[0], 2) + Math.pow(matrix[1], 2) + Math.pow(matrix[2], 2)); - ret[1] = (float) Math.sqrt(Math.pow(matrix[4], 2) + Math.pow(matrix[5], 2) + Math.pow(matrix[6], 2)); - ret[2] = (float) Math.sqrt(Math.pow(matrix[8], 2) + Math.pow(matrix[9], 2) + Math.pow(matrix[10], 2)); - if (determinant(matrix) < 0) { - ret[1] = -ret[1]; - } - return ret; - } - - public static float determinant(float[] matrix) { - float ret = 0; - ret += matrix[0] * (matrix[5] * (matrix[10] * matrix[15] - matrix[11] * matrix[14])); - ret -= matrix[1] * (matrix[6] * (matrix[11] * matrix[12] - matrix[8] * matrix[15])); - ret += matrix[2] * (matrix[7] * (matrix[8] * matrix[13] - matrix[9] * matrix[12])); - ret -= matrix[3] * (matrix[4] * (matrix[9] * matrix[14] - matrix[10] * matrix[13])); - return ret; - } - - public static float[] createRotationMatrixAroundVector(float angle, float x, float y, float z) { - - final float[] matrix = new float[16]; - final int offset = 0; - - float cos = (float) Math.cos(angle); - float sin = (float) Math.sin(angle); - float cos_1 = 1 - cos; - - // @formatter:off - matrix[offset] = cos_1 * x * x + cos; - matrix[offset + 1] = cos_1 * x * y - z * sin; - matrix[offset + 2] = cos_1 * z * x + y * sin; - matrix[offset + 3] = 0; - matrix[offset + 4] = cos_1 * x * y + z * sin; - matrix[offset + 5] = cos_1 * y * y + cos; - matrix[offset + 6] = cos_1 * y * z - x * sin; - matrix[offset + 7] = 0; - matrix[offset + 8] = cos_1 * z * x - y * sin; - matrix[offset + 9] = cos_1 * y * z + x * sin; - matrix[offset + 10] = cos_1 * z * z + cos; - matrix[offset + 11] = 0; - matrix[offset + 12] = 0; - matrix[offset + 13] = 0; - matrix[offset + 14] = 0; - matrix[offset + 15] = 1; - - // @formatter:on - - return matrix; - } - - /** - * Calculate the angle between the 2 specified vectors - * - * @param v1 - * @param v2 - * @return - */ - public static double calculateAngleBetween(float[] v1, float[] v2){ - // Using acos - //float[] v1n = normalize2(v1); - //float[] v2n = normalize2(v2); - - // perp dot-product - // https://stackoverflow.com/questions/2150050/finding-signed-angle-between-vectors - //return Math.atan2( v1n[0]*v2n[1] - v1n[1]*v2n[0], v1n[0]*v2n[0] + v1n[1]*v2n[1] ); - return Math.atan2( v1[0]*v2[1] - v1[1]*v2[0], v1[0]*v2[0] + v1[1]*v2[1] ); - - } - - public static void createRotationMatrixAroundVector(float[] matrix, int offset, double angle, float[] vector) { - createRotationMatrixAroundVector(matrix, offset, angle, vector[0], vector[1], vector[2]); - } - - /** - * - * @param matrix output matrix - * @param offset output matrix offset - * @param angle in radians - * @param x rotation vector x - * @param y rotation vector y - * @param z rotation vector z - */ - public static void createRotationMatrixAroundVector(float[] matrix, int offset, double angle, float x, float y, - float z) { - float cos = (float) Math.cos(angle); - float sin = (float) Math.sin(angle); - float cos_1 = 1 - cos; - - // @formatter:off - matrix[offset] = cos_1 * x * x + cos; - matrix[offset + 1] = cos_1 * x * y - z * sin; - matrix[offset + 2] = cos_1 * z * x + y * sin; - matrix[offset + 3] = 0; - matrix[offset + 4] = cos_1 * x * y + z * sin; - matrix[offset + 5] = cos_1 * y * y + cos; - matrix[offset + 6] = cos_1 * y * z - x * sin; - matrix[offset + 7] = 0; - matrix[offset + 8] = cos_1 * z * x - y * sin; - matrix[offset + 9] = cos_1 * y * z + x * sin; - matrix[offset + 10] = cos_1 * z * z + cos; - matrix[offset + 11] = 0; - matrix[offset + 12] = 0; - matrix[offset + 13] = 0; - matrix[offset + 14] = 0; - matrix[offset + 15] = 1; - - // @formatter:on - } - - public static void multiplyMMV(float[] result, int retOffset, float[] matrix, int matOffet, float[] vector4Matrix, - int vecOffset) { - for (int i = 0; i < vector4Matrix.length / 4; i++) { - Matrix.multiplyMV(result, retOffset + (i * 4), matrix, matOffet, vector4Matrix, vecOffset + (i * 4)); - } - } - - public static void snapToGrid(float[] v) { - final float[] TEST_VALUES = { - Constants.UNIT_SIN_1, Constants.UNIT_SIN_2, Constants.UNIT_SIN_3, Constants.UNIT_SIN_5, - Constants.UNIT_SIN_1 * Constants.UNIT, Constants.UNIT_SIN_2 * Constants.UNIT, - Constants.UNIT_SIN_3 * Constants.UNIT, Constants.UNIT_SIN_5 * Constants.UNIT, - Constants.UNIT_0, Constants.UNIT_1, Constants.UNIT_2, Constants.UNIT_3, Constants.UNIT_5, - Constants.UNIT_0 * Constants.UNIT, Constants.UNIT_1 * Constants.UNIT, Constants.UNIT_2 * Constants.UNIT, - Constants.UNIT_3 * Constants.UNIT, Constants.UNIT_5 * Constants.UNIT}; - for (int i = 0; i < v.length; i++) { - for (int j = 0; j < TEST_VALUES.length; j++) { - final float testValue = TEST_VALUES[j] ; - if (v[i] >= (-testValue - Constants.SNAP_TO_GRID_THRESHOLD) - && v[i] <= (-testValue + Constants.SNAP_TO_GRID_THRESHOLD)) { - v[i] = -testValue; - break; - } else if (v[i] >= (testValue - Constants.SNAP_TO_GRID_THRESHOLD) - && v[i] <= (testValue + Constants.SNAP_TO_GRID_THRESHOLD)) { - v[i] = testValue; - break; - } - } - } - } - - public static float[] to4d(float[] v3d) { - return new float[]{v3d[0], v3d[1], v3d[2], 1}; - } - - public static boolean lineEquals(float[] v1, float[] v2, float[] v3, float[] v4) { - return equals(v1,v3) && equals(v2,v4) || equals(v1,v4) && equals(v2,v3); - } - - public static boolean equals(float[] v1, float[] v2) { - if (v1 == v2) return true; - if (v1 == null || v2 == null) return false; - - //if (v1 != null && v2 == null) return false; - //if (v1 == null) return false; - return v1[0] == v2[0] && v1[1] == v2[1] && v1[2] == v2[2]; - } - - public static void round(float[] v, int factor) { - v[0] = (float)Math.round(v[0]*factor)/factor; - v[1] = (float)Math.round(v[1]*factor)/factor; - v[2] = (float)Math.round(v[2]*factor)/factor; - } - - public static float dot(float[] a, float[] b){ - // a1b1+a2b2+a3b3 - return a[0]*b[0]+a[1]*b[1]+a[2]*b[2]; - } - - public static float[] cross(float[] a, float[] b){ - // AxB = (AyBz − AzBy, AzBx − AxBz, AxBy − AyBx) - //(r)[0] = (a)[1] * (b)[2] - (b)[1] * (a)[2]; \ - //(r)[1] = (a)[2] * (b)[0] - (b)[2] * (a)[0]; \ - //(r)[2] = (a)[0] * (b)[1] - (b)[0] * (a)[1]; - float x = a[1]*b[2] - a[2]*b[1]; - float y = a[2]*b[0] - a[0]*b[2]; - float z = a[0]*b[1] - a[1]*b[0]; - return new float[]{x,y,z}; - } - - public static float[] getRotation(float[] v1, float[] v2, float[] v3, float[] newOrientation){ - // calculate polygon normal - final float[] normal = calculateNormal(v1, v2, v3); - Math3DUtils.normalizeVector(normal); - - // check if triangle is already facing the new orientation - if (Math3DUtils.equals(normal, newOrientation)){ - return Math3DUtils.IDENTITY_MATRIX; - } - - // calculate 2D rotation - final float dot = Math3DUtils.dotProduct(newOrientation, normal); - final float angle = (float) Math.acos(dot); - final float[] cross = Math3DUtils.crossProduct(Constants.Z_NORMAL, normal); - Math3DUtils.normalizeVector(cross); - //cross[1] = 0; - //cross[2] = 0; - float[] rotationMatrix = Math3DUtils.createRotationMatrixAroundVector(angle, cross[0], cross[1], cross[2]); - - Log.i("HoleCutter", "normal: " + Arrays.toString(normal) + ", angle: " + angle + ", axis: " + Arrays.toString(cross)); - return rotationMatrix; - } - - public static float[] extractTranslation(float[] matrix, float[] ret) { - if (ret == null){ - ret = new float[3]; - } - ret[0]=matrix[12]; ret[1]=matrix[13]; ret[2]=matrix[14]; - return ret; - } - - public static Float[] extractTranslation2(float[] matrix, Float[] ret) { - - // check - if (matrix == null) return null; - - if (ret == null){ - ret = new Float[3]; - } - ret[0]=matrix[12]; ret[1]=matrix[13]; ret[2]=matrix[14]; - return ret; - } - - public static float[] extractRotationMatrix(float[] matrix) { - float[] s = new float[]{Matrix.length(matrix[0],matrix[4],matrix[8]),Matrix.length(matrix[1],matrix[5],matrix[9]),Matrix.length - (matrix[2],matrix[6],matrix[10])}; - return new float[]{ - matrix[0]/s[0], matrix[1]/s[1], matrix[2]/s[2], 0, - matrix[4]/s[0], matrix[5]/s[1], matrix[6]/s[2], 0, - matrix[8]/s[0], matrix[9]/s[1], matrix[10]/s[2], 0, - 0,0,0,1 - }; - } - - public static float[] extractAxisAngle(float[] rotationMatrix){ - float[] ret = new float[4]; - ret[0] = rotationMatrix[9]-rotationMatrix[6]; - ret[1] = rotationMatrix[2]-rotationMatrix[8]; - ret[2] = rotationMatrix[4]-rotationMatrix[1]; - normalizeVector(ret); - float trace = rotationMatrix[0]+rotationMatrix[5]+rotationMatrix[10]; - ret[3] = (float)Math.acos((trace-1)/2); - return ret; - } - - public static float[] normal(float[] v0, float[] v1, float[] v2) { - - // calculate perpendicular vector to the face. That is to calculate the cross product of v1-v0 x v2-v0 - float[] va = substract(v1,v0); - float[] vb = substract(v2,v0); - float nx = va[1] * vb[2] - va[2] * vb[1]; - float ny = va[2] * vb[0] - va[0] * vb[2]; - float nz = va[0] * vb[1] - va[1] * vb[0]; - float[] n = new float[]{nx, ny, nz}; - normalizeVector(n); - return n; - } - - public static float[] transform(float x, float y, float z, float[] matrix){ - float[] xyz1 = new float[]{x, y, z, 1}; - Matrix.multiplyMV(xyz1, 0, matrix, 0, xyz1, 0); - Math3DUtils.round(xyz1, 100000); - return new float[]{xyz1[0], xyz1[1], xyz1[2]}; - } - - public static float[] centroid(float[] v0, float[] v1, float[] v2) { - return new float[]{(v0[0] + v1[0] + v2[0]) / 3, (v0[1] + v1[1] + v2[1]) / 3, (v0[2] + v1[2] + v2[2]) / 3}; - } - - public static boolean isCoplanar(float[] v1, float[] v2, float[] v3, float[] p1, float[] p2){ - float[] n1 = Math3DUtils.normal(v1, v2, v3); - return dot(n1, substract(p2, p1)) == 0; - } - - public static boolean coplanar(float[] n1, float[] n2) { - if (Math3DUtils.equals(n1, n2)){ - return true; - } - invert(n1); - return Math3DUtils.equals(n1, n2); - } - - private static void invert(float[] v) { - v[0] = -v[0]; - v[1] = -v[1]; - v[2] = -v[2]; - } - - public static boolean hasAreaZero(float[] v1, float[] v2, float[] v3){ - if (equals(v1,v2) || equals(v1,v3) || equals(v2,v3)){ - return true; - } - - float[] v12 = substract(v2,v1); - float[] v23 = substract(v3,v2); - float[] v31 = substract(v1,v3); - float l12 = length(v12); - float l23 = length(v23); - float l31 = length(v31); - float area; - if (l12 >= l23 && l12 >= l31){ - area = l12-l23-l31; - } else if (l23 >= l12 && l23 >= l31){ - area = l23-l12-l31; - } else if (l31 >= l12 && l31 >= l23){ - area = l31-l12-l23; - } else { - // equilateral triangle have area - return false; - } - return Math.abs(area)<0.001f; - } - - public static boolean hasLine(float[] v1, float[] v2, float[] v3, float[] p1, float[] p2){ - return lineEquals(v1, v2, p1, p2) || lineEquals(v2, v3, p1, p2) || lineEquals(v3, v1, p1, p2); - } - - public static boolean isZero(float[] v){ - return v[0] == 0 && v[1] == 0 && v[2] == 0; - } - - public static boolean isCollinear(float[] v1, float[] v2, float[] v3){ - // v1-v2-p1 are collinear? - float[] v2v1 = substract(v2,v1); - float[] v3v1 = substract(v3,v1); - return isZero(cross(v2v1, v3v1)); - } - - public static boolean areCollinear(float[] v1, float[] v2, float[] v3, float[] v4){ - return isCollinear(v1,v2,v3) && isCollinear(v1,v2,v4); - } - - public static boolean equals(float[] v1, float[] v2, float factor) { - return v1 == v2 || v1[0]*factor/factor == v2[0]*factor/factor && v1[1]*factor/factor == v2[1]*factor/factor - && v1[2]*factor/factor == v2[2]*factor/factor ; - } -} - From 8a104a578ac651e553b15bc2662a218df6187b7d Mon Sep 17 00:00:00 2001 From: Andres Oviedo Date: Thu, 26 Dec 2024 11:49:17 +0100 Subject: [PATCH 2/2] fixed build doc --- README.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index df9962b6..e5efd734 100644 --- a/README.md +++ b/README.md @@ -115,7 +115,17 @@ You can install the application in either of these ways: * APK: [app-release.apk](app/build/outputs/apk/release/app-release.apk) * Source code: clone the repository, compile with gradle and install with adb + +Compilation +=========== + +Script to build an apk package and run in your device. +- Git 1.6.5 or later is required +- Android Gradle plugin requires Java 17 to run + ``` + git clone --recursive https://github.com/the3deer/android-3D-model-viewer.git + cd android-3D-model-viewer export ANDROID_HOME=/home/$USER/Android/Sdk ./gradlew assembleDebug adb install -r app/build/outputs/apk/app-debug.apk @@ -206,4 +216,4 @@ Assets * skybox sea : https://learnopengl.com/Advanced-OpenGL/Cubemaps * skybox sand : Copyright 2012 Mobialia - https://github.com/mobialia/jmini3d * models (parts) : Community contribution (Professor S) - \ No newline at end of file +