/******************************************************************************* * Breakout Cave Survey Visualizer * * Copyright (C) 2014 James Edwards * * jedwards8 at fastmail dot fm * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 51 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *******************************************************************************/ package org.andork.jogl.awt.anim; import static org.andork.math3d.Vecmath.cross; import static org.andork.math3d.Vecmath.distance3; import static org.andork.math3d.Vecmath.dot3; import static org.andork.math3d.Vecmath.getColumn3; import static org.andork.math3d.Vecmath.interp3; import static org.andork.math3d.Vecmath.invAffine; import static org.andork.math3d.Vecmath.mvmulAffine; import static org.andork.math3d.Vecmath.negate3; import static org.andork.math3d.Vecmath.newMat4f; import static org.andork.math3d.Vecmath.normalize3; import static org.andork.math3d.Vecmath.partDist; import static org.andork.math3d.Vecmath.rotation; import static org.andork.math3d.Vecmath.scaleAdd3; import static org.andork.math3d.Vecmath.setd; import static org.andork.math3d.Vecmath.setf; import static org.andork.math3d.Vecmath.subDot3; import org.andork.awt.anim.Animation; import org.andork.jogl.JoglViewSettings; import org.andork.math3d.LineLineIntersection2d; import org.andork.math3d.LinePlaneIntersection3d; import com.jogamp.opengl.GLAutoDrawable; public class GeneralViewXformOrbitAnimation implements Animation { final float[] startXform = newMat4f(); final float[] endXform = newMat4f(); JoglViewSettings viewSettings; GLAutoDrawable drawable; long elapsedTime; long totalTime; long period; final double[] startLocation = new double[3]; final double[] startRight = new double[3]; final double[] startForward = new double[3]; final double[] startUp = new double[3]; final double[] startHorizontal = new double[3]; final double[] endLocation = new double[3]; final double[] endRight = new double[3]; final double[] endForward = new double[3]; final double[] endUp = new double[3]; final double[] endHorizontal = new double[3]; final double[] alignedStartLocation = new double[3]; final double[] alignedStartForward = new double[3]; final double[] alignedEndLocation = new double[3]; final double[] alignedEndForward = new double[3]; final double[] startOrigin = new double[3]; final double[] alignedStartTiltOrigin = new double[3]; double startPan; double startTilt; double startOffset; final double[] endOrigin = new double[3]; final double[] alignedEndTiltOrigin = new double[3]; double endPan; double endTilt; double endOffset; boolean linear; double totalPan; double totalTilt; final double[] origin = new double[3]; final double[] alignedTiltOrigin = new double[3]; final double[] alignedLocation = new double[3]; final double[] alignedForward = new double[3]; final double[] horizontal = new double[3]; final float[] viewXform = newMat4f(); final LineLineIntersection2d llx = new LineLineIntersection2d(); final LinePlaneIntersection3d lpx = new LinePlaneIntersection3d(); public GeneralViewXformOrbitAnimation(GLAutoDrawable drawable, JoglViewSettings viewSettings, long totalTime, long period) { this.viewSettings = viewSettings; this.drawable = drawable; this.totalTime = totalTime; this.period = period; } @Override public long animate(long animTime) { elapsedTime += animTime; calcViewXform(Math.min(1f, (double) elapsedTime / totalTime), viewXform); viewSettings.setViewXform(viewXform); drawable.display(); return Math.min(Math.max(0, totalTime - elapsedTime), period); } public void calcViewXform(double progress, float[] outXform) { if (linear) { interp3(startLocation, endLocation, progress, outXform, 12); interp3(startRight, endRight, progress, outXform, 0); interp3(startForward, endForward, progress, outXform, 8); negate3(outXform, 8); } else { double pan = startPan + totalPan * progress; double tilt = startTilt + totalTilt * progress; double offset = startOffset * (1 - progress) + endOffset * progress; interp3(alignedStartTiltOrigin, alignedEndTiltOrigin, progress, alignedTiltOrigin); alignedForward[0] = Math.cos(tilt); alignedForward[1] = Math.sin(tilt); scaleAdd3(offset, alignedForward, alignedTiltOrigin, alignedLocation); horizontal[0] = Math.cos(pan); horizontal[2] = Math.sin(pan); interp3(startOrigin, endOrigin, progress, origin); // new location outXform[12] = (float) (origin[0] + horizontal[0] * alignedLocation[0]); outXform[13] = (float) alignedLocation[1]; outXform[14] = (float) (origin[2] + horizontal[2] * alignedLocation[0]); // new backward outXform[8] = (float) (horizontal[0] * -alignedForward[0]); outXform[9] = (float) -alignedForward[1]; outXform[10] = (float) (horizontal[2] * -alignedForward[0]); // new right outXform[0] = (float) -horizontal[2]; outXform[1] = 0; outXform[2] = (float) horizontal[0]; } // new up = backward X right cross(outXform, 8, outXform, 0, outXform, 4); invAffine(outXform); } private void restOfSetUp() { if (Math.abs(dot3(startRight, endRight) - 1f) < 1e-6f || Math.abs(dot3(startForward, endForward) - 1f) < 1e-6f) { linear = true; return; } linear = false; cross(0, 1, 0, startRight, startHorizontal); cross(0, 1, 0, endRight, endHorizontal); startPan = Math.atan2(startHorizontal[2], startHorizontal[0]); endPan = Math.atan2(endHorizontal[2], endHorizontal[0]); totalPan = rotation(startPan, endPan); llx.setUp(startLocation, startHorizontal, endLocation, endHorizontal, 0, 2); double minRotation = 1e-6; if (llx.findIntersection() && Math.abs(totalPan) > minRotation && Math.PI - Math.abs(totalPan) > minRotation) { alignedStartLocation[0] = -llx.t0; alignedEndLocation[0] = -llx.t1; startOrigin[0] = endOrigin[0] = llx.x[0]; startOrigin[2] = endOrigin[2] = llx.x[1]; // note that llx.x[1] is // the z coordinate // llx.x[2] would be out // of bounds } else if (Math.abs(totalPan) > Math.PI / 2) { lpx.lineFromRay(startLocation, startForward); lpx.planeFromUV(endLocation, endForward, endRight); lpx.findIntersection(); if (dot3(startForward, endForward) > -0.95 || lpx.isPointIntersection()) { alignedStartLocation[0] = -lpx.t * startForward[0]; alignedEndLocation[0] = -lpx.u * endForward[0]; startOrigin[0] = endOrigin[0] = lpx.result[0]; startOrigin[2] = endOrigin[2] = lpx.result[2]; } else { alignedStartLocation[0] = alignedEndLocation[0] = partDist(startLocation, endLocation, 0, 2) * 0.5f; if (subDot3(endLocation, startLocation, startHorizontal) > 0) { alignedStartLocation[0] = alignedEndLocation[0] = -alignedStartLocation[0]; } startOrigin[0] = endOrigin[0] = (startLocation[0] + endLocation[0]) * 0.5f; startOrigin[2] = endOrigin[2] = (startLocation[2] + endLocation[2]) * 0.5f; } } else { alignedStartLocation[0] = 0; alignedEndLocation[0] = subDot3(endLocation, startLocation, startHorizontal); startOrigin[0] = startLocation[0]; startOrigin[2] = startLocation[2]; double lateral = subDot3(endLocation, startLocation, startRight); endOrigin[0] = startOrigin[0] + lateral * startRight[0]; endOrigin[2] = startOrigin[2] + lateral * startRight[2]; } alignedStartLocation[1] = startLocation[1]; alignedEndLocation[1] = endLocation[1]; alignedStartForward[0] = dot3(startForward, startHorizontal); alignedEndForward[0] = dot3(endForward, endHorizontal); alignedStartForward[1] = startForward[1]; alignedEndForward[1] = endForward[1]; startTilt = Math.atan2(alignedStartForward[1], alignedStartForward[0]); endTilt = Math.atan2(alignedEndForward[1], alignedEndForward[0]); totalTilt = rotation(startTilt, endTilt); llx.setUp(alignedStartLocation, alignedStartForward, alignedEndLocation, alignedEndForward, 0, 1); if (llx.findIntersection() && Math.abs(totalTilt) > minRotation && Math.PI - Math.abs(totalTilt) > minRotation) { alignedStartTiltOrigin[0] = alignedEndTiltOrigin[0] = llx.x[0]; alignedStartTiltOrigin[1] = alignedEndTiltOrigin[1] = llx.x[1]; startOffset = -llx.t0; endOffset = -llx.t1; } else if (Math.abs(totalTilt) > Math.PI / 2) { interp3(alignedStartLocation, alignedEndLocation, 0.5f, alignedStartTiltOrigin); setd(alignedEndTiltOrigin, alignedStartTiltOrigin); startOffset = endOffset = distance3(alignedStartLocation, alignedStartTiltOrigin); } else { setd(alignedStartTiltOrigin, alignedStartLocation); setd(alignedEndTiltOrigin, alignedEndLocation); startOffset = endOffset = 0; } } public GeneralViewXformOrbitAnimation setUp(float[] startViewXform, float[] endViewXform) { invAffine(startViewXform, startXform); invAffine(endViewXform, endXform); getColumn3(startXform, 3, startLocation); getColumn3(endXform, 3, endLocation); mvmulAffine(startXform, 0, 0, -1, startForward); mvmulAffine(endXform, 0, 0, -1, endForward); normalize3(startForward); normalize3(endForward); mvmulAffine(startXform, 1, 0, 0, startRight); mvmulAffine(endXform, 1, 0, 0, endRight); normalize3(startRight); normalize3(endRight); mvmulAffine(startXform, 0, 1, 0, startUp); mvmulAffine(endXform, 0, 1, 0, endUp); normalize3(startUp); normalize3(endUp); restOfSetUp(); return this; } public GeneralViewXformOrbitAnimation setUp(float[] origin, float[] startViewXform, float[] endForward, float[] endRight) { invAffine(startViewXform, startXform); getColumn3(startXform, 3, startLocation); mvmulAffine(startXform, 0, 0, -1, startForward); mvmulAffine(startXform, 1, 0, 0, startRight); mvmulAffine(startXform, 0, 1, 0, startUp); normalize3(startForward); normalize3(startRight); normalize3(startUp); double forwardAmt = subDot3(startLocation, origin, startForward); double rightAmt = subDot3(startLocation, origin, startRight); double upAmt = subDot3(startLocation, origin, startUp); normalize3(endForward, this.endForward); normalize3(endRight, this.endRight); cross(endRight, endForward, endUp); scaleAdd3(forwardAmt, this.endForward, origin, endLocation); scaleAdd3(rightAmt, this.endRight, endLocation, endLocation); scaleAdd3(upAmt, endUp, endLocation, endLocation); restOfSetUp(); return this; } public GeneralViewXformOrbitAnimation setUpWithEndLocation(float[] startViewXform, float[] endLocation, float[] endForward, float[] endRight) { invAffine(startViewXform, startXform); getColumn3(startXform, 3, startLocation); mvmulAffine(startXform, 0, 0, -1, startForward); mvmulAffine(startXform, 1, 0, 0, startRight); mvmulAffine(startXform, 0, 1, 0, startUp); normalize3(startForward); normalize3(startRight); normalize3(startUp); normalize3(endForward, this.endForward); normalize3(endRight, this.endRight); cross(endRight, endForward, endUp); setf(this.endLocation, endLocation); restOfSetUp(); return this; } }