/** * Copyright (c) 2012-2013 Nokia Corporation. All rights reserved. * Nokia and Nokia Connecting People are registered trademarks of Nokia Corporation. * Oracle and Java are trademarks or registered trademarks of Oracle and/or its * affiliates. Other product and company names mentioned herein may be trademarks * or trade names of their respective owners. * See LICENSE.TXT for license information. */ package com.nokia.example.amaze.ui; import javax.microedition.m3g.Camera; import javax.microedition.m3g.Transform; import javax.microedition.m3g.World; /** * Class for pause and transition camera animations. */ public class CameraAnimator { // Constants private static final float NUM_OF_TRANSITION_STEPS = 30; private static final float MIN_TRANSITION_STEP = 0.01f; private static final float PAUSE_ANIMATION_ROTATION_SPEED = 0.2f; // Animation types public static final int PAUSE_ANIMATION = 0; public static final int TRANSITION_ANIMATION_TO_POV = 1; public static final int TRANSITION_ANIMATION_TO_TOP = 2; public static final int LEVEL_RESET_ANIMATION_STEP = 3; // Members private Listener _listener = null; private World _world = null; private Camera _camera = null; private Transform _currentTransform = null; private Transform _targetTransform = null; private float[] _currentMatrix = null; private float[] _targetMatrix = null; private float[] _transitionMatrix = null; private float[] _currentOrientation = null; private float[] _targetOrientation = null; private int _animationType = -1; private int _stepCount = 0; private boolean _running = false; /** * Constructor. * @param listener The listener which is the maze canvas. */ public CameraAnimator(Listener listener, World world, Camera camera) { if (listener == null || world == null || camera == null) { throw new IllegalArgumentException("None of the arguments can be null!"); } _listener = listener; _world = world; _camera = camera; } /** * Starts the animation of the given type. * * Note that after calling this method only "_currentTransform" of the * members is guaranteed not to be null. If the given target transform is * not null, the following memebers are guaranteed not be null: * - _currentTransform, * - _targetTransform, * - _targetMatrix and * - _currentMatrix. * * @param type The animation type. * @param targetTransform The target transform. Can be null. * @param targetOrientation The target orientation. Can be null. */ public void startAnimation(int type, Transform targetTransform, float[] targetOrientation) { System.out.println("CameraAnimator::startAnimation(): " + type); if (_running) { releaseResources(); _running = false; } _animationType = type; _stepCount = 0; if (targetTransform != null) { _targetTransform = targetTransform; _targetMatrix = new float[16]; _targetTransform.get(_targetMatrix); } _currentTransform = new Transform(); Camera activeCamera = _world.getActiveCamera(); if (activeCamera == null) { // No active camera set, do set it now System.out.println("CameraAnimator::startAnimation(): No active camera set."); _world.setActiveCamera(_camera); _camera.getTransform(_currentTransform); } else { activeCamera.getTransform(_currentTransform); if (_camera != activeCamera) { System.out.println("CameraAnimator::startAnimation(): Switching the camera."); _camera.setTransform(_currentTransform); _world.setActiveCamera(_camera); } } if (targetOrientation != null) { _targetOrientation = targetOrientation; _currentOrientation = new float[4]; _camera.getOrientation(_currentOrientation); } if (targetTransform != null) { _currentMatrix = new float[16]; _currentTransform.get(_currentMatrix); _transitionMatrix = calculateTransitionMatrix(_currentMatrix, _targetMatrix); } _running = true; } /** * For convenience. * @param type * @param targetCamera */ public void startAnimation(int type, Camera targetCamera) { if (targetCamera == null) { return; } Transform transform = new Transform(); targetCamera.getTransform(transform); float[] orientation = new float[4]; targetCamera.getOrientation(orientation); startAnimation(type, transform, orientation); } /** * For convenience. * @param type */ public void startAnimation(int type) { startAnimation(type, null, null); } /** * Stops the animation. */ public void stopAnimation() { System.out.println("CameraAnimator::stopAnimation()"); releaseResources(); _running = false; _listener.onAnimationFinished(_animationType); } /** * @return True if the animator running, false otherwise. */ public final boolean running() { return _running; } /** * @return The type of the current animation. */ public final int animationType() { return _animationType; } /** * Takes one update step in the animation. * @param ticks The milliseconds since last time the method was called. */ public final void update(final int ticks) { final float coefficient = (float)ticks / MazeCanvas.TICKS_COEFFICIENT; if (_targetTransform != null) { if (!takeTransitionStepTowardsTarget(coefficient)) { System.out.println("CameraAnimator::update(): No more transition steps required."); if (_animationType == TRANSITION_ANIMATION_TO_POV || _animationType == TRANSITION_ANIMATION_TO_TOP) { // We're done here _running = false; stopAnimation(); } else { releaseResources(); } } if (_currentTransform != null) { _camera.setTransform(_currentTransform); } if (_targetOrientation != null) { _camera.setOrientation(_currentOrientation[0], _currentOrientation[1], _currentOrientation[2], _currentOrientation[3]); } } else if (_animationType == PAUSE_ANIMATION) { _camera.postRotate(PAUSE_ANIMATION_ROTATION_SPEED * coefficient, 0f, 1f, 0f); } else if (_animationType == LEVEL_RESET_ANIMATION_STEP) { _camera.translate(0f, 12.0f * coefficient, 12.0f * coefficient); _stepCount++; if (_stepCount > 60) { stopAnimation(); } } else { stopAnimation(); } } /** * Prints the matrix of the given transform. For debugging purposes. * @param transform The transform to print. */ public static final void printTransform(Transform transform) { if (transform == null) { return; } final float[] matrix = new float[16]; transform.get(matrix); for (int i = 0; i < 16; ++i) { if ((i + 1) % 4 == 0) { System.out.println(matrix[i]); } else { System.out.print(matrix[i] + "\t"); } } } /** * Prints the values of the properties of the given camera. * @param camera The camera of which values to print. */ public static final void printCameraValues(Camera camera) { if (camera != null) { Transform transform = new Transform(); camera.getTransform(transform); printTransform(transform); // Print orientation final float[] orientation = new float[4]; camera.getOrientation(orientation); System.out.print("Orientation: ["); System.out.println(orientation[0] + ", " + orientation[1] + ", " + orientation[2] + ", " + orientation[3] + "]"); } } /** * Modifies the value of "current" by the value of "diff" towards "target". * @param current The initial value. * @param target The target value. * @param diff The difference. * @return The modified value. */ private final float newValue(float current, float target, float diff) { if (diff < 0) { diff = Math.abs(diff); } if (Math.abs(current - target) < diff) { return target; } if (target > current) { return current + diff; } return current - diff; } /** * Takes a step towards the target camera transition. * @param coefficient Defines the relative size of the step. * @return True if the matrix was modified, false otherwise. */ private final boolean takeTransitionStepTowardsTarget(final float coefficient) { if (_currentMatrix == null || _targetMatrix == null) { System.out.println("CameraAnimator::takeTransitionStepTowardsTarget(): Null matrices!"); return false; } final int matrixLength = _currentMatrix.length; final int maxChangesCount = (_targetOrientation == null) ? matrixLength : _transitionMatrix.length; int noChangesCount = 0; for (int i = 0; i < matrixLength; ++i) { if (_currentMatrix[i] == _targetMatrix[i]) { noChangesCount++; continue; } _currentMatrix[i] = newValue(_currentMatrix[i], _targetMatrix[i], _transitionMatrix[i] * coefficient); } if (_currentOrientation != null && _targetOrientation != null) { for (int i = 0; i < 4; ++i) { if (_currentOrientation[i] == _targetOrientation[i]) { noChangesCount++; continue; } _currentOrientation[i] = newValue(_currentOrientation[i], _targetOrientation[i], _transitionMatrix[i + matrixLength] * coefficient); } } _currentTransform.set(_currentMatrix); return (noChangesCount != maxChangesCount); } /** * Creates the transition matrix whose values represent a single transition * step (delta) from the initial matrix to the target matrix. * @param from The initial matrix. * @param to The target matrix. * @return The transition (step delta) matrix. */ private float[] calculateTransitionMatrix(float[] from, float[] to) { if (from == null || to == null || from.length != to.length){ return null; } final int length = from.length; float[] transitionMatrix = new float[length + 4]; for (int i = 0; i < length; ++i) { if (to[i] == from[i]) { continue; } transitionMatrix[i] = (to[i] - from[i]) / NUM_OF_TRANSITION_STEPS; if (transitionMatrix[i] == 0) { transitionMatrix[i] = MIN_TRANSITION_STEP; } } if (_targetOrientation != null) { // Calculate the transition steps for the orientation for (int i = 0; i < 4; ++i) { transitionMatrix[i + length] = (_targetOrientation[i] - _currentOrientation[i]) / NUM_OF_TRANSITION_STEPS; if (transitionMatrix[i + length] == 0) { transitionMatrix[i + length] = MIN_TRANSITION_STEP; } } } return transitionMatrix; } /** * Releases resources. */ private void releaseResources() { _currentTransform = null; _targetTransform = null; _targetMatrix = null; _currentMatrix = null; _currentOrientation = null; _targetOrientation = null; _transitionMatrix = null; } /** * Listener interface for events of this class. */ public interface Listener { /** * Called when an animation is finished. * @param animationType The type of the animation which was finished. */ void onAnimationFinished(int animationType); } }